[
  {
    "path": ".cargo/config.toml",
    "content": "# TODO: track https://github.com/rust-lang/rust/issues/141626 for a resolution\n[target.x86_64-pc-windows-msvc]\nrustflags = ['-Csymbol-mangling-version=v0']\n\n[env]\nMIRIFLAGS = \"-Zmiri-tree-borrows\"\n"
  },
  {
    "path": ".config/nextest.toml",
    "content": "[profile.ci]\n# Don't fail fast in CI to run the full test suite.\nfail-fast = false\n"
  },
  {
    "path": ".editorconfig",
    "content": "root = true\n\n[*]\nend_of_line = lf\ncharset = utf-8\ntrim_trailing_whitespace = true\ninsert_final_newline = true\nindent_size = 4\nindent_style = space\n\n[{Makefile,**.mk}]\n# Use tabs for indentation (Makefiles require tabs)\nindent_style = tab\n\n[{*.js,*.json,*.mjs}]\nindent_size = 2\n\n[*.md]\nindent_size = 2\n\n[flake.lock]\n#\n"
  },
  {
    "path": ".gitattributes",
    "content": "# Handle line endings automatically for files detected as text\n# and leave all files detected as binary untouched.\n* text=auto\n\n#\n# The above will handle all files NOT found below\n#\n# These files are text and should be normalized (Convert crlf => lf)\n*.css           eol=lf\n*.htm           eol=lf\n*.html          eol=lf\n*.js            eol=lf\n*.json          eol=lf\n*.sh            eol=lf\n*.txt           eol=lf\n*.yml           eol=lf\n*.rs            eol=lf\n*.toml          eol=lf\n*.lock          eol=lf\n*.md            eol=lf\n*.svg           eol=lf\n\n# These files are binary and should be left untouched\n# (binary is a macro for -text -diff)\n*.gif           binary\n*.ico           binary\n*.jar           binary\n*.jpg           binary\n*.jpeg          binary\n*.png           binary\n"
  },
  {
    "path": ".github/FUNDING.yml",
    "content": "open_collective: boa\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/bug_report.md",
    "content": "---\nname: \"\\U0001F41B Bug report\"\nabout: Create a report to help us improve\ntitle: \"\"\ntype: \"Bug\"\nassignees: \"\"\n---\n\n<!--\nThank you for reporting a bug in Boa! This will make us improve the engine. But first, fill the following template so that we better understand what's happening. Feel free to add or remove sections as you feel appropriate.\n-->\n\n**Describe the bug**\nA clear and concise description of what the bug is.\n\n<!-- E.g.:\nThe variable statement is not working as expected, it always adds 10 when assigning a number to a variable\"\n-->\n\n**To Reproduce**\nSteps to reproduce the issue, or JavaScript code that causes this failure.\n\n<!-- E.g.:\nThis JavaScript code reproduces the issue:\n```javascript\nvar a = 10;\na;\n```\n-->\n\n**Expected behavior**\nExplain what you expected to happen, and what is happening instead.\n\n<!-- E.g.:\nRunning this code, `a` should be set to `10` and printed, but `a` is instead set to `20`. The expected behaviour can be found in the [ECMAScript specification][spec].\n\n[spec]: https://tc39.es/ecma262/#sec-variable-statement-runtime-semantics-evaluation\n-->\n\n**Build environment (please complete the following information):**\n\n- OS: [e.g. Fedora Linux]\n- Version: [e.g. 32]\n- Target triple: [e.g. x86_64-unknown-linux-gnu]\n- Rustc version: [e.g. rustc 1.43.0 (4fb7144ed 2020-04-20), running `rustc -V`]\n\n**Additional context**\nAdd any other context about the problem here.\n\n<!-- E.g.:\nYou can find more information in [MDN](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/var).\n-->\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/config.yml",
    "content": "blank_issues_enabled: false\ncontact_links:\n  - name: Matrix space\n    url: https://matrix.to/#/#boa:matrix.org\n    about: Please ask and answer questions here.\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/custom.md",
    "content": "---\nname: Custom\nabout: Open an issue in the repo that is neither a bug or a feature.\ntitle: \"\"\nlabels: \"\"\ntype: \"\"\nassignees: \"\"\n---\n\n<!--\nThank you for contributing to Boa! Please, let us know how can we help you.\n-->\n\nE.g.: I think we should improve the way the JavaScript interpreter works by...\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/feature_request.md",
    "content": "---\nname: \"\\U0001F680 Feature request\"\nabout: Suggest a new ECMAScript feature to be implemented, or a new capability of the engine.\ntitle: \"\"\ntype: \"Feature\"\nlabels: \"\"\nassignees: \"\"\n---\n\n<!--\nThank you for adding a feature request to Boa! As this is an experimental JavaScript engine, there will probably be many ECMAScript features left to implement. In order to understand the feature request as best as possible, please fill the following template. Feel free to add or remove sections as needed.\n-->\n\n**ECMASCript feature**\nExplain the ECMAScript feature that you'd like to see implemented.\n\n<!-- E.g.:\nI would like to see `switch` statement parsing and execution implemented. [ECMAScript specification][spec].\n\n[spec]: https://tc39.es/ecma262/#sec-switch-statement\n-->\n\n**Example code**\nGive a code example that should work after the implementation of this feature.\n\n<!-- E.g.:\nThis code should now work and give the expected result:\n```javascript\nlet a = \"hello\";\nlet b;\nswitch (a) {\n    case 'hello':\n        b = 'world';\n        break;\n    case 'world':\n        b = 'hello';\n        break;\n    default:\n        b = 'hello world';\n}\nb;\n```\nThe expected output is `world`.\n-->\n"
  },
  {
    "path": ".github/PULL_REQUEST_TEMPLATE.md",
    "content": "<!---\nThank you for contributing to Boa! Please fill out the template below, and remove or add any\ninformation as you feel necessary.\n--->\n\nThis Pull Request fixes/closes #{issue_num}.\n\nIt changes the following:\n\n-\n-\n-\n"
  },
  {
    "path": ".github/codecov.yml",
    "content": "github_checks:\n  annotations: false\n\ncoverage:\n  status:\n    project:\n      default:\n        threshold: 5% # allow 5% coverage variance\n\n    patch: off\n"
  },
  {
    "path": ".github/dependabot.yml",
    "content": "version: 2\nupdates:\n  - package-ecosystem: github-actions\n    directory: /\n    schedule:\n      interval: weekly\n    labels:\n      - \"C-Dependencies\"\n      - \"C-Actions\"\n    groups:\n      ci-dependencies:\n        applies-to: version-updates\n        patterns: [\"*\"]\n        update-types:\n        - \"minor\"\n        - \"patch\"\n  - package-ecosystem: cargo\n    directory: /\n    schedule:\n      interval: weekly\n    labels:\n      - \"C-Dependencies\"\n    groups:\n      rust-dependencies:\n        applies-to: version-updates\n        patterns: [\"*\"]\n        update-types:\n        - \"minor\"\n        - \"patch\"\n  - package-ecosystem: cargo\n    directory: /tests/fuzz/\n    schedule:\n      interval: weekly\n    labels:\n      - \"C-Dependencies\"\n    groups:\n      fuzz-dependencies:\n        applies-to: version-updates\n        patterns: [\"*\"]\n        update-types:\n        - \"minor\"\n        - \"patch\"\n"
  },
  {
    "path": ".github/labeler.yml",
    "content": "C-Actions:\n- changed-files:\n  - any-glob-to-any-file:\n    - '.github/**'\n\nC-AST:\n- changed-files:\n  - any-glob-to-any-file: 'core/ast/**'\n\nC-Benchmark:\n- changed-files:\n  - any-glob-to-any-file:\n    - 'benches/**'\n    - 'core/engine/benches/**'\n\nC-Builtins:\n- all:\n  - changed-files:\n    - any-glob-to-any-file:\n        - 'core/engine/src/builtins/**'\n        - 'core/engine/src/object/builtins/**'\n    - all-globs-to-all-files:\n        - '!core/engine/src/object/builtins/intl/**'\n\nC-CLI:\n- changed-files:\n  - any-glob-to-any-file:\n    - 'cli/**'\n\nC-Dependencies:\n- changed-files:\n  - any-glob-to-any-file:\n    - '**/Cargo.lock'\n    - '**/Cargo.toml'\n\nC-Documentation:\n- changed-files:\n  - any-glob-to-any-file:\n    - '**/*.md'\n\nC-FFI:\n- changed-files:\n  - any-glob-to-any-file:\n    - 'ffi/**'\n\nC-GC:\n- changed-files:\n  - any-glob-to-any-file:\n    - 'core/gc/**'\n\nC-Intl:\n- changed-files:\n  - any-glob-to-any-file:\n    - 'core/engine/src/builtins/intl/**'\n\nC-Javascript:\n- changed-files:\n  - any-glob-to-any-file:\n    - '**/*.js'\n\nC-Parser:\n- changed-files:\n  - any-glob-to-any-file:\n    - 'core/parser/**'\n\nC-Runtime:\n- changed-files:\n  - any-glob-to-any-file:\n    - 'core/runtime/**'\n\nC-Tests:\n- changed-files:\n  - any-glob-to-any-file:\n    - '**/tests/**'\n    - '**/test*'\n\nC-VM:\n- changed-files:\n  - any-glob-to-any-file:\n    - 'core/engine/src/bytecompiler/**'\n    - 'core/engine/src/vm/**'\n\nC-WebAssembly:\n- changed-files:\n  - any-glob-to-any-file:\n    - 'ffi/wasm/**'\n"
  },
  {
    "path": ".github/release.yml",
    "content": "# .github/release.yml\n\nchangelog:\n  exclude:\n    authors:\n      - dependabot\n  categories:\n    - title: Feature Enhancements\n      labels:\n        - A-Enhancement\n    - title: Bug Fixes\n      labels:\n        - A-Bug\n    - title: Performance Improvements\n      labels:\n        - A-Performance\n        - A-Memory\n    - title: Internal Improvements\n      labels:\n        - A-Internal\n        - A-Technical Debt\n    - title: Other Changes\n      labels:\n        - \"*\"\n"
  },
  {
    "path": ".github/workflows/codeql.yml",
    "content": "name: \"CodeQL SAST Scanning\"\n\non:\n  workflow_dispatch:\n  schedule:\n    - cron: '0 0 * * 0' # Run weekly on Sundays\n\npermissions:\n  contents: read\n\njobs:\n  analyze:\n    name: Analyze\n    runs-on: ubuntu-latest\n    timeout-minutes: 60\n    permissions:\n      actions: read\n      contents: read\n      security-events: write\n\n    strategy:\n      fail-fast: false\n      matrix:\n        language: [ 'rust' ]\n\n    steps:\n    - name: Checkout repository\n      uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6\n      with:\n        persist-credentials: false\n\n    - name: Initialize CodeQL\n      uses: github/codeql-action/init@38697555549f1db7851b81482ff19f1fa5c4fedc # v4.34.1\n      with:\n        languages: ${{ matrix.language }}\n        build-mode: none\n\n    - name: Perform CodeQL Analysis\n      uses: github/codeql-action/analyze@38697555549f1db7851b81482ff19f1fa5c4fedc # v4.34.1\n      with:\n        category: \"/language:${{matrix.language}}\"\n"
  },
  {
    "path": ".github/workflows/labeler.yml",
    "content": "name: \"Pull Request Labeler\"\non:\n- pull_request_target\n\npermissions:\n  contents: read\n\njobs:\n  labeler:\n    permissions:\n      contents: read\n      pull-requests: write\n    runs-on: ubuntu-latest\n    timeout-minutes: 10\n    steps:\n    - uses: actions/labeler@634933edcd8ababfe52f92936142cc22ac488b1b # v6\n"
  },
  {
    "path": ".github/workflows/nightly_build.yml",
    "content": "name: Nightly Build\npermissions:\n  contents: read\n\n# Schedule this workflow to run at midnight every day\non:\n  schedule:\n    - cron: \"0 0 * * *\"\n  workflow_dispatch:\n\njobs:\n  build:\n    permissions:\n      contents: write\n    strategy:\n      matrix:\n        include:\n          - target: x86_64-unknown-linux-gnu\n            os: ubuntu-latest\n            binary_extension: \"\"\n          - target: aarch64-apple-darwin\n            os: macos-14\n          - target: aarch64-unknown-linux-gnu\n            os: ubuntu-24.04-arm\n            binary_extension: \"\"\n          - target: x86_64-pc-windows-msvc\n            os: windows-latest\n            binary_extension: \".exe\"\n    runs-on: ${{ matrix.os }}\n    timeout-minutes: 60\n    steps:\n      - name: Checkout repository\n        uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6\n        with:\n          persist-credentials: false\n\n      - name: Install Rust toolchain\n        uses: dtolnay/rust-toolchain@631a55b12751854ce901bb631d5902ceb48146f7 # stable\n        with:\n          toolchain: stable\n          targets: ${{ matrix.target }}\n\n      - name: Cache Cargo\n        uses: Swatinem/rust-cache@c19371144df3bb44fab255c43d04cbc2ab54d1c4 # v2.9.1\n\n      - name: Build\n        run: cargo build --target ${{ matrix.target }} --release --locked --bin boa\n\n      - name: Upload binaries to release\n        uses: svenstaro/upload-release-action@29e53e917877a24fad85510ded594ab3c9ca12de # v2\n        with:\n          repo_token: ${{ secrets.GITHUB_TOKEN }}\n          file: target/${{ matrix.target }}/release/boa${{ matrix.binary_extension }}\n          asset_name: boa-${{ matrix.target }}${{ matrix.binary_extension }}\n          tag: refs/tags/nightly\n          overwrite: true\n          prerelease: true\n"
  },
  {
    "path": ".github/workflows/pr_management.yml",
    "content": "name: PR Management\n\non:\n  pull_request_target:\n    types: [opened, reopened, synchronize, closed]\n\npermissions:\n  contents: read\n\njobs:\n  manage_pr:\n    runs-on: ubuntu-latest\n    timeout-minutes: 10\n    permissions:\n      pull-requests: write\n      issues: write\n    steps:\n      - name: Auto Add Label\n        if: github.event.action == 'opened' || github.event.action == 'reopened' || github.event.action == 'synchronize'\n        uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8\n        with:\n          script: |\n            const labels = await github.rest.issues.listLabelsOnIssue({\n              owner: context.repo.owner,\n              repo: context.repo.repo,\n              issue_number: context.issue.number\n            });\n\n            if (labels.data.every(label => label.name != \"Waiting On Author\")) {\n              github.rest.issues.addLabels({\n                issue_number: context.issue.number,\n                owner: context.repo.owner,\n                repo: context.repo.repo,\n                labels: ['Waiting On Review']\n              })\n            }\n\n\n      - name: Auto Remove Label\n        if: github.event.action == 'closed'\n        uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8\n        continue-on-error: true\n        with:\n          script: |\n            try {\n              await github.rest.issues.removeLabel({\n                issue_number: context.issue.number,\n                owner: context.repo.owner,\n                repo: context.repo.repo,\n                name: 'Waiting On Review'\n              });\n            } catch (error) {\n              console.log('Label \"Waiting On Review\" not found or could not be removed.');\n            }\n\n      - name: Auto Assign Milestone\n        if: github.event.action == 'opened'\n        uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8\n        with:\n          script: |\n            // Fetch open milestones and assign the closest one\n            const { data: milestones } = await github.rest.issues.listMilestones({\n              owner: context.repo.owner,\n              repo: context.repo.repo,\n              state: 'open',\n              sort: 'due_on',\n              direction: 'asc'\n            });\n\n            if (milestones.length > 0) {\n              const latestMilestone = milestones[0];\n              await github.rest.issues.update({\n                issue_number: context.issue.number,\n                owner: context.repo.owner,\n                repo: context.repo.repo,\n                milestone: latestMilestone.number\n              });\n            }\n"
  },
  {
    "path": ".github/workflows/pull_request.yml",
    "content": "name: Benchmarks\n\non:\n  pull_request:\n    branches:\n      - main\n      - releases/**\n\npermissions:\n  contents: read\n\nconcurrency:\n  group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}\n  cancel-in-progress: true\n\njobs:\n  runBenchmark:\n    if: contains(github.event.pull_request.labels.*.name, 'run-benchmark')\n    name: run benchmark\n    runs-on: ubuntu-latest\n    timeout-minutes: 120\n    steps:\n      - name: Checkout repository\n        uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6\n        with:\n          fetch-depth: 0\n          persist-credentials: false\n\n      - name: Install Rust toolchain\n        uses: dtolnay/rust-toolchain@631a55b12751854ce901bb631d5902ceb48146f7 # stable\n        with:\n          toolchain: stable\n\n      - name: Cache Cargo\n        uses: Swatinem/rust-cache@c19371144df3bb44fab255c43d04cbc2ab54d1c4 # v2.9.1\n\n      - uses: boa-dev/criterion-compare-action@adfd3a94634fe2041ce5613eb7df09d247555b87 # v3.2.4\n        with:\n          token: ${{ secrets.GITHUB_TOKEN }}\n          branchName: ${{ github.base_ref }}\n          cwd: ./core/engine\n"
  },
  {
    "path": ".github/workflows/release.yml",
    "content": "name: Publish Release\non:\n  release:\n    types: [published]\n\npermissions:\n  contents: read\n\njobs:\n  publish:\n    name: Publish crates\n    runs-on: ubuntu-latest\n    timeout-minutes: 60\n    steps:\n      - name: Set environment\n        env:\n          W_FLAGS: ${{ (github.ref == 'refs/heads/main' || github.base_ref == 'main') && '-Dwarnings' || '' }}\n        # Setting `RUSTFLAGS` overrides any flags set on .cargo/config.toml, so we need to\n        # set the target flags instead which are cumulative.\n        # Track https://github.com/rust-lang/cargo/issues/5376\n        run: |\n          target=$(rustc -vV | awk '/^host/ { print $2 }' | tr [:lower:] [:upper:] | tr '-' '_')\n          echo \"CARGO_TARGET_${target}_RUSTFLAGS=$W_FLAGS\" >> $GITHUB_ENV\n      - name: Checkout repository\n        uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6\n\n      - name: Install Rust toolchain\n        uses: dtolnay/rust-toolchain@631a55b12751854ce901bb631d5902ceb48146f7 # stable\n        with:\n          toolchain: stable\n\n      - name: Install cargo-workspaces\n        uses: baptiste0928/cargo-install@f204293d9709061b7bc1756fec3ec4e2cd57dec0 # v3.4.0\n        with:\n          crate: cargo-workspaces\n\n      - name: Release\n        env:\n          CARGO_REGISTRY_TOKEN: ${{ secrets.CARGO_REGISTRY_TOKEN }}\n          PATCH: ${{ github.run_number }}\n        shell: bash\n        run: |\n          git config --global user.email \"runner@gha.local\"\n          git config --global user.name \"Github Action\"\n          cargo workspaces publish \\\n            --from-git \\\n            --yes \\\n            --no-git-commit \\\n            skip\n\n  npm_publish:\n    name: Publish NPM package (wasm)\n    runs-on: ubuntu-latest\n    timeout-minutes: 60\n    steps:\n      - name: Checkout repository\n        uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6\n\n      - name: Install Rust toolchain\n        uses: dtolnay/rust-toolchain@631a55b12751854ce901bb631d5902ceb48146f7 # stable\n        with:\n          toolchain: stable\n          targets: wasm32-unknown-unknown\n\n      - name: Install wasm-pack\n        uses: baptiste0928/cargo-install@f204293d9709061b7bc1756fec3ec4e2cd57dec0 # v3.4.0\n        with:\n          crate: wasm-pack\n\n      - name: Build boa_wasm\n        run: wasm-pack build --scope boa-dev ./ffi/wasm\n\n      - name: Set-up Node.js\n        uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6\n        with:\n          node-version: \"20\"\n\n      - name: Set-up npm config for publishing\n        run: npm config set -- '//registry.npmjs.org/:_authToken' \"${{ secrets.NPM_TOKEN }}\"\n\n      - name: Check if the npm version already exists\n        run: |\n          VERSION=$(jq -r '.version' ./ffi/wasm/pkg/package.json)\n\n          if npm view @boa-dev/boa_wasm@$VERSION version > dev/null 2>&1; then\n            echo \"Version $VERSION already published. Skipping\"\n            echo \"SKIP_PUBLISH=true\" >> $GITHUB_ENV\n          fi\n\n      - name: Publish to npm\n        if: env.SKIP_PUBLISH != 'true'\n        run: npm publish ./ffi/wasm/pkg --access=public\n\n  release-binaries:\n    name: Publish binaries\n    permissions:\n      contents: write\n    needs: publish\n    strategy:\n      fail-fast: false\n      matrix:\n        build: [linux, macos-arm64, win-msvc]\n        include:\n        - build: linux\n          os: ubuntu-latest\n          target: x86_64-unknown-linux-gnu\n          binary_extension: \"\"\n        - build: macos-arm64\n          os: macos-14\n          target: aarch64-apple-darwin\n          binary_extension: \"\"\n        - build: win-msvc\n          os: windows-latest\n          target: x86_64-pc-windows-msvc\n          binary_extension: \".exe\"\n    runs-on: ${{ matrix.os }}\n    timeout-minutes: 60\n    steps:\n      - name: Checkout repository\n        uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6\n\n      - name: Install Rust toolchain\n        uses: dtolnay/rust-toolchain@631a55b12751854ce901bb631d5902ceb48146f7 # stable\n        with:\n          toolchain: stable\n          targets: ${{ matrix.target }}\n\n      - name: Cache Cargo\n        uses: Swatinem/rust-cache@c19371144df3bb44fab255c43d04cbc2ab54d1c4 # v2.9.1\n\n      - name: Build\n        run: cargo build --target ${{ matrix.target }} --verbose --release --locked --bin boa\n\n      - name: Upload binaries to release\n        uses: svenstaro/upload-release-action@29e53e917877a24fad85510ded594ab3c9ca12de # v2\n        with:\n          repo_token: ${{ secrets.GITHUB_TOKEN }}\n          file: target/${{ matrix.target }}/release/boa${{ matrix.binary_extension }}\n          asset_name: boa-${{ matrix.target }}${{ matrix.binary_extension }}\n          tag: ${{ github.ref }}\n"
  },
  {
    "path": ".github/workflows/rust.yml",
    "content": "name: Continuous integration\n\non:\n  pull_request:\n    branches:\n      - main\n      - releases/**\n  push:\n    branches:\n      - main\n      - releases/**\n  merge_group:\n    types: [checks_requested]\n  workflow_dispatch:\n\npermissions:\n  contents: read\n\nconcurrency:\n  group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}\n  cancel-in-progress: true\n\nenv:\n  W_FLAGS: ${{ (github.ref == 'refs/heads/main' || github.base_ref == 'main') && '-D warnings' || '' }}\n\njobs:\n  fmt:\n    name: Formatting\n    runs-on: ubuntu-latest\n    timeout-minutes: 60\n    steps:\n      - name: Set environment\n        run: |\n          target=$(rustc -vV | awk '/^host/ { print $2 }' | tr [:lower:] [:upper:] | tr '-' '_')\n          echo \"CARGO_TARGET_${target}_RUSTFLAGS=$W_FLAGS\" >> $GITHUB_ENV\n      - name: Checkout repository\n        uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6\n        with:\n          persist-credentials: false\n\n      - name: Install Rust toolchain\n        uses: dtolnay/rust-toolchain@631a55b12751854ce901bb631d5902ceb48146f7 # stable\n        with:\n          toolchain: stable\n          components: rustfmt\n\n      - name: Format (rustfmt)\n        run: cargo fmt --all --check\n\n  typos:\n    name: Typos\n    runs-on: ubuntu-latest\n    timeout-minutes: 60\n    steps:\n      - name: Checkout repository\n        uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6\n        with:\n          persist-credentials: false\n\n      - name: Check for typos\n        uses: crate-ci/typos@631208b7aac2daa8b707f55e7331f9112b0e062d # v1.44.0\n\n  clippy:\n    name: Lint\n    runs-on: ubuntu-latest\n    timeout-minutes: 60\n    steps:\n      - name: Set environment\n        run: |\n          target=$(rustc -vV | awk '/^host/ { print $2 }' | tr [:lower:] [:upper:] | tr '-' '_')\n          echo \"CARGO_TARGET_${target}_RUSTFLAGS=$W_FLAGS\" >> $GITHUB_ENV\n      - name: Checkout repository\n        uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6\n        with:\n          persist-credentials: false\n\n      - name: Install Rust toolchain\n        uses: dtolnay/rust-toolchain@631a55b12751854ce901bb631d5902ceb48146f7 # stable\n        with:\n          toolchain: stable\n          components: clippy\n\n      - name: Cache Cargo\n        uses: Swatinem/rust-cache@c19371144df3bb44fab255c43d04cbc2ab54d1c4 # v2.9.1\n\n      - name: Install cargo-workspaces\n        uses: baptiste0928/cargo-install@f204293d9709061b7bc1756fec3ec4e2cd57dec0 # v3.4.0\n        with:\n          crate: cargo-workspaces\n\n      - name: Clippy (All features)\n        run: cargo workspaces exec cargo clippy --all-features --all-targets\n      - name: Clippy (No features)\n        run: cargo workspaces exec cargo clippy --no-default-features --all-targets\n      - name: Clippy (Intl)\n        run: cargo clippy -p boa_engine --features intl\n      - name: Clippy (Annex-B)\n        run: cargo clippy -p boa_engine --features annex-b\n      - name: Clippy (Experimental)\n        run: cargo clippy -p boa_engine --features experimental\n\n  docs:\n    name: Documentation\n    runs-on: ubuntu-latest\n    timeout-minutes: 60\n    env:\n      RUSTDOCFLAGS: ${{ (github.ref == 'refs/heads/main' || github.base_ref == 'main') && '-D warnings' || '' }}\n    steps:\n      - name: Set environment\n        run: |\n          target=$(rustc -vV | awk '/^host/ { print $2 }' | tr [:lower:] [:upper:] | tr '-' '_')\n          echo \"CARGO_TARGET_${target}_RUSTFLAGS=$W_FLAGS\" >> $GITHUB_ENV\n      - name: Checkout repository\n        uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6\n        with:\n          persist-credentials: false\n\n      - name: Install Rust toolchain\n        uses: dtolnay/rust-toolchain@631a55b12751854ce901bb631d5902ceb48146f7 # stable\n        with:\n          toolchain: stable\n\n      - name: Cache Cargo\n        uses: Swatinem/rust-cache@c19371144df3bb44fab255c43d04cbc2ab54d1c4 # v2.9.1\n\n      - name: Generate documentation\n        run: cargo doc -v --document-private-items --all-features\n\n  msrv:\n    name: MSRV\n    runs-on: ubuntu-latest\n    timeout-minutes: 60\n    steps:\n      - name: Set environment\n        run: |\n          target=$(rustc -vV | awk '/^host/ { print $2 }' | tr [:lower:] [:upper:] | tr '-' '_')\n          echo \"CARGO_TARGET_${target}_RUSTFLAGS=$W_FLAGS\" >> $GITHUB_ENV\n      - name: Checkout repository\n        uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6\n        with:\n          persist-credentials: false\n\n      # Get the rust_version from the Cargo.toml\n      - name: Get rust_version\n        id: rust_version\n        run: echo \"rust_version=$(grep '^rust-version' Cargo.toml | cut -d' ' -f3 | tr -d '\"')\" >> $GITHUB_OUTPUT\n\n      - name: Install Rust toolchain\n        uses: dtolnay/rust-toolchain@631a55b12751854ce901bb631d5902ceb48146f7 # stable\n        with:\n          toolchain: ${{ steps.rust_version.outputs.rust_version }}\n\n      - name: Check compilation\n        run: cargo check --all-features --all-targets\n\n  coverage:\n    name: Coverage\n    runs-on: ubuntu-latest\n    timeout-minutes: 60\n    if: ${{ github.ref == 'refs/heads/main' || github.base_ref == 'main' }}\n    needs:\n      - fmt\n      - typos\n      - clippy\n      - docs\n      - msrv\n    steps:\n      - name: Set environment\n        # Setting `RUSTFLAGS` overrides any flags set on .cargo/config.toml, so we need to\n        # set the target flags instead which are cumulative.\n        # Track https://github.com/rust-lang/cargo/issues/5376\n        run: |\n          target=$(rustc -vV | awk '/^host/ { print $2 }' | tr [:lower:] [:upper:] | tr '-' '_')\n          echo \"CARGO_TARGET_${target}_RUSTFLAGS=$W_FLAGS\" >> $GITHUB_ENV\n      - name: Checkout repository\n        uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6\n        with:\n          persist-credentials: false\n\n      - name: Install Rust toolchain\n        uses: dtolnay/rust-toolchain@631a55b12751854ce901bb631d5902ceb48146f7 # stable\n\n      - name: Cache Cargo\n        uses: Swatinem/rust-cache@c19371144df3bb44fab255c43d04cbc2ab54d1c4 # v2.9.1\n\n      - name: Install cargo-tarpaulin\n        uses: baptiste0928/cargo-install@f204293d9709061b7bc1756fec3ec4e2cd57dec0 # v3.4.0\n        with:\n          crate: cargo-tarpaulin\n\n      - name: Run tarpaulin\n        run: cargo tarpaulin --workspace --features annex-b,intl_bundled,experimental --ignore-tests --engine llvm --out xml\n\n      - name: Upload to codecov.io\n        uses: codecov/codecov-action@1af58845a975a7985b0beb0cbe6fbbb71a41dbad # v5\n\n  tests:\n    name: Test\n    runs-on: ${{ matrix.os }}\n    timeout-minutes: 60\n    needs:\n      - fmt\n      - typos\n      - clippy\n      - docs\n      - msrv\n    env:\n      RUSTUP_WINDOWS_PATH_ADD_BIN: 1\n    strategy:\n      fail-fast: false\n      matrix:\n        include:\n          - os: macos-14\n          - os: windows-latest\n          - os: ubuntu-24.04-arm\n          - os: ubuntu-latest\n    steps:\n      - name: Set environment\n        if: ${{ matrix.os != 'windows-latest' }}\n        # Setting `RUSTFLAGS` overrides any flags set on .cargo/config.toml, so we need to\n        # set the target flags instead which are cumulative.\n        # Track https://github.com/rust-lang/cargo/issues/5376\n        run: |\n          target=$(rustc -vV | awk '/^host/ { print $2 }' | tr '[:lower:]' '[:upper:]' | tr '-' '_')\n          echo \"CARGO_TARGET_${target}_RUSTFLAGS=$W_FLAGS\" >> $GITHUB_ENV\n      - name: Set environment\n        if: ${{ matrix.os == 'windows-latest' }}\n        run: |\n          $target = (rustc -vV | Select-String '^host') -replace '^host:\\s+', '' | ForEach-Object { $_.ToUpper().Replace('-', '_') }\n          \"CARGO_TARGET_${target}_RUSTFLAGS=$env:W_FLAGS\" | Out-File -FilePath $env:GITHUB_ENV -Append -Encoding utf8\n\n      - name: Checkout repository\n        uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6\n        with:\n          persist-credentials: false\n\n      - name: Install Rust toolchain\n        uses: dtolnay/rust-toolchain@631a55b12751854ce901bb631d5902ceb48146f7 # stable\n        with:\n          toolchain: stable\n\n      - name: Install Cargo insta\n        uses: baptiste0928/cargo-install@f204293d9709061b7bc1756fec3ec4e2cd57dec0 # v3.4.0\n        with:\n          crate: cargo-insta\n          locked: true\n\n      - name: Cache Cargo\n        uses: Swatinem/rust-cache@c19371144df3bb44fab255c43d04cbc2ab54d1c4 # v2.9.1\n\n      - name: Build tests\n        run: cargo test --no-run --profile ci\n      # this order is faster according to rust-analyzer\n      - name: Build\n        run: cargo build --all-targets --quiet --profile ci --features annex-b,intl_bundled,experimental,embedded_lz4\n      - name: Install latest nextest\n        uses: taiki-e/install-action@7cb3ba7bc31801346db00d4a6d7008aabab5e986 # nextest\n      - name: Test with nextest\n        run: cargo nextest run --profile ci --cargo-profile ci --features annex-b,intl_bundled,experimental,embedded_lz4\n      - name: Test docs\n        run: cargo test --doc --profile ci --features annex-b,intl_bundled,experimental\n      - name: Test bytecode output\n        run: cargo insta test -p insta-bytecode\n\n  cross-tests:\n    name: Test\n    runs-on: ubuntu-latest\n    timeout-minutes: 60\n    needs:\n      - fmt\n      - typos\n      - clippy\n      - docs\n      - msrv\n    strategy:\n      matrix:\n        include:\n          - target: i686-unknown-linux-gnu\n    steps:\n      - name: Set environment\n        # Setting `RUSTFLAGS` overrides any flags set on .cargo/config.toml, so we need to\n        # set the target flags instead which are cumulative.\n        # Track https://github.com/rust-lang/cargo/issues/5376\n        run: |\n          target=$(echo ${{ matrix.target }} | tr [:lower:] [:upper:] | tr '-' '_')\n          echo \"CARGO_TARGET_${target}_RUSTFLAGS=$W_FLAGS\" >> $GITHUB_ENV\n\n      - name: Checkout repository\n        uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6\n        with:\n          persist-credentials: false\n\n      - name: Install Rust toolchain\n        uses: dtolnay/rust-toolchain@631a55b12751854ce901bb631d5902ceb48146f7 # stable\n        with:\n          toolchain: stable\n          target: ${{ matrix.target }}\n\n      - name: Install Cross\n        uses: baptiste0928/cargo-install@f204293d9709061b7bc1756fec3ec4e2cd57dec0 # v3.4.0\n        with:\n          crate: cross\n          git: https://github.com/cross-rs/cross\n\n      - name: Cache Cargo\n        uses: Swatinem/rust-cache@c19371144df3bb44fab255c43d04cbc2ab54d1c4 # v2.9.1\n\n      - name: Run tests\n        run: |\n          cross test --workspace --target ${{ matrix.target }} \\\n            --profile ci \\\n            --features annex-b,intl_bundled,experimental \\\n            --exclude boa_macros \\\n            --exclude boa_macros_tests\n\n  miri:\n    name: Miri\n    runs-on: ubuntu-latest\n    timeout-minutes: 120\n    needs:\n      - fmt\n      - typos\n      - clippy\n      - docs\n      - msrv\n    steps:\n      - name: Set environment\n        # Setting `RUSTFLAGS` overrides any flags set on .cargo/config.toml, so we need to\n        # set the target flags instead which are cumulative.\n        # Track https://github.com/rust-lang/cargo/issues/5376\n        run: |\n          target=$(rustc -vV | awk '/^host/ { print $2 }' | tr [:lower:] [:upper:] | tr '-' '_')\n          echo \"CARGO_TARGET_${target}_RUSTFLAGS=$W_FLAGS\" >> $GITHUB_ENV\n      - name: Checkout repository\n        uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6\n        with:\n          persist-credentials: false\n\n      - name: Install Rust nightly with miri\n        uses: dtolnay/rust-toolchain@631a55b12751854ce901bb631d5902ceb48146f7 # nightly\n        with:\n          toolchain: nightly\n          components: miri\n\n      - name: Setup miri\n        run: cargo miri setup\n\n      - name: Run miri tests\n        run: cargo miri test --workspace --exclude boa_cli --exclude boa_examples miri\n\n  build-fuzz:\n    name: Fuzzing\n    runs-on: ubuntu-latest\n    timeout-minutes: 60\n    needs:\n      - fmt\n      - typos\n      - clippy\n      - docs\n      - msrv\n    steps:\n      - name: Set environment\n        run: |\n          target=$(rustc -vV | awk '/^host/ { print $2 }' | tr [:lower:] [:upper:] | tr '-' '_')\n          echo \"CARGO_TARGET_${target}_RUSTFLAGS=$W_FLAGS\" >> $GITHUB_ENV\n      - name: Checkout repository\n        uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6\n        with:\n          persist-credentials: false\n\n      - name: Install Rust toolchain\n        uses: dtolnay/rust-toolchain@631a55b12751854ce901bb631d5902ceb48146f7 # stable\n        with:\n          toolchain: stable\n\n      - name: Cache Cargo\n        uses: Swatinem/rust-cache@c19371144df3bb44fab255c43d04cbc2ab54d1c4 # v2.9.1\n\n      - name: Install cargo-fuzz\n        uses: baptiste0928/cargo-install@f204293d9709061b7bc1756fec3ec4e2cd57dec0 # v3.4.0\n        with:\n          crate: cargo-fuzz\n\n      - name: Build fuzz\n        run: cd tests/fuzz && cargo fuzz build -s none --dev\n\n  build-run-examples:\n    name: Build & run examples\n    runs-on: ubuntu-latest\n    timeout-minutes: 60\n    needs:\n      - fmt\n      - typos\n      - clippy\n      - docs\n      - msrv\n    steps:\n      - name: Set environment\n        run: |\n          target=$(rustc -vV | awk '/^host/ { print $2 }' | tr [:lower:] [:upper:] | tr '-' '_')\n          echo \"CARGO_TARGET_${target}_RUSTFLAGS=$W_FLAGS\" >> $GITHUB_ENV\n      - name: Checkout repository\n        uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6\n        with:\n          persist-credentials: false\n\n      - name: Install Rust toolchain\n        uses: dtolnay/rust-toolchain@631a55b12751854ce901bb631d5902ceb48146f7 # stable\n        with:\n          toolchain: stable\n\n      - name: Cache Cargo\n        uses: Swatinem/rust-cache@c19371144df3bb44fab255c43d04cbc2ab54d1c4 # v2.9.1\n\n      - name: Install cargo-workspaces\n        uses: baptiste0928/cargo-install@f204293d9709061b7bc1756fec3ec4e2cd57dec0 # v3.4.0\n        with:\n          crate: cargo-workspaces\n\n      - name: Build (All features)\n        run: cargo workspaces exec cargo build --all-features --all-targets --profile ci\n      - name: Build (No features)\n        run: cargo workspaces exec cargo build --no-default-features --all-targets --profile ci\n\n      - name: Run examples\n        run: |\n          cd examples\n          cargo run -p boa_examples --bin 2>&1 \\\n            | grep -E '^ ' \\\n            | xargs -n1 sh -c 'cargo run -p boa_examples --profile ci --bin $0 || exit 255'\n\n  run-semver-check:\n    name: Check SemVer compatibility\n    runs-on: ubuntu-latest\n    timeout-minutes: 60\n    needs:\n      - fmt\n      - typos\n      - clippy\n      - docs\n      - msrv\n    steps:\n      - name: Set environment\n        run: |\n          target=$(rustc -vV | awk '/^host/ { print $2 }' | tr [:lower:] [:upper:] | tr '-' '_')\n          echo \"CARGO_TARGET_${target}_RUSTFLAGS=$W_FLAGS\" >> $GITHUB_ENV\n      - name: Checkout repository\n        uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6\n        with:\n          persist-credentials: false\n\n      - name: Cache Cargo\n        uses: Swatinem/rust-cache@c19371144df3bb44fab255c43d04cbc2ab54d1c4 # v2.9.1\n\n      - name: Check Semver\n        uses: obi1kenobi/cargo-semver-checks-action@5b298c9520f7096a4683c0bd981a7ac5a7e249ae # v2\n        with:\n          exclude: boa_wintertc\n"
  },
  {
    "path": ".github/workflows/security_audit.yml",
    "content": "name: Security audit\non:\n  schedule:\n    - cron: \"0 0 * * *\"\n\npermissions:\n  contents: read\njobs:\n  audit:\n    runs-on: ubuntu-latest\n    timeout-minutes: 60\n    steps:\n      - name: Checkout repository\n        uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6\n        with:\n          persist-credentials: false\n      - uses: rustsec/audit-check@69366f33c96575abad1ee0dba8212993eecbe998 # v2.0.0\n        with:\n          token: ${{ secrets.GITHUB_TOKEN }}\n"
  },
  {
    "path": ".github/workflows/test262.yml",
    "content": "name: test262\n\non:\n  pull_request:\n    branches:\n      - main\n      - releases/**\n\npermissions:\n  contents: read\n\nconcurrency:\n  group: ${{ github.workflow }}-${{ github.event.pull_request.number }}\n  cancel-in-progress: true\n\njobs:\n  run_test262:\n    name: Run the test262 test suite\n    runs-on: ubuntu-latest\n    timeout-minutes: 60\n    steps:\n      - name: Checkout repository\n        uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6\n        with:\n          path: boa\n          persist-credentials: false\n\n      - name: Install Rust toolchain\n        uses: dtolnay/rust-toolchain@631a55b12751854ce901bb631d5902ceb48146f7 # stable\n        with:\n          toolchain: stable\n\n      - name: Cache Cargo\n        uses: Swatinem/rust-cache@c19371144df3bb44fab255c43d04cbc2ab54d1c4 # v2.9.1\n\n      - name: Checkout the data repo\n        uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6\n        with:\n          repository: boa-dev/data\n          path: data\n          persist-credentials: false\n\n      - name: Run the test262 test suite\n        run: |\n          cd boa\n          mkdir -p ../results/test262\n          cargo run --release --bin boa_tester -- run -v -o ../results/test262\n          cd ..\n\n      - name: Compare results\n        shell: bash\n        run: |\n          cd boa\n\n          base_results=\"../data/test262/refs/heads/main/latest.json\"\n          pr_results=\"../results/test262/pull/latest.json\"\n          output_dir=\"../results/outputs\"\n\n          test -f \"$base_results\"\n          test -f \"$pr_results\"\n\n          comment=\"$(./target/release/boa_tester compare \"$base_results\" \"$pr_results\" -m)\"\n          maincommit=\"$(jq -r '.c' \"$base_results\")\"\n\n          mkdir -p \"$output_dir\"\n          {\n            echo \"<!-- test262-compliance-report -->\"\n            echo \"### Test262 conformance changes\"\n            echo\n            echo \"$comment\"\n            echo\n            echo \"Tested main commit: [\\`${maincommit}\\`](${{ github.event.pull_request.base.repo.html_url }}/commit/${maincommit})\"\n            echo \"Tested PR commit: [\\`${{ github.event.pull_request.head.sha }}\\`](${{ github.event.pull_request.head.repo.html_url }}/commit/${{ github.event.pull_request.head.sha }})\"\n            echo \"Compare commits: ${{ github.event.pull_request.base.repo.html_url }}/compare/${maincommit}...${{ github.event.pull_request.head.sha }}\"\n          } > \"$output_dir/comment.md\"\n\n          echo \"${{ github.event.pull_request.number }}\" > \"$output_dir/pr_number.txt\"\n\n      - name: Upload results\n        uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0\n        with:\n          name: test262-results\n          path: results/outputs\n          retention-days: 1\n"
  },
  {
    "path": ".github/workflows/test262_comment.yml",
    "content": "name: test262_comment\n\non:\n  workflow_run:\n    workflows: [\"test262\"]\n    types:\n      - completed\n\npermissions:\n  contents: read\n  pull-requests: write\n\njobs:\n  comment:\n    name: Post results to PR\n    runs-on: ubuntu-latest\n    if: ${{ github.event.workflow_run.event == 'pull_request' && github.event.workflow_run.conclusion == 'success' }}\n    steps:\n      - name: Download results\n        uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1\n        with:\n          name: test262-results\n          github-token: ${{ secrets.GITHUB_TOKEN }}\n          run-id: ${{ github.event.workflow_run.id }}\n          path: downloaded-results\n\n      - name: Read results\n        id: results\n        shell: bash\n        run: |\n          echo \"pr_number=$(cat downloaded-results/pr_number.txt)\" >> $GITHUB_OUTPUT\n\n      - name: Find Previous Comment\n        uses: peter-evans/find-comment@b30e6a3c0ed37e7c023ccd3f1db5c6c0b0c23aad # v4\n        id: previous-comment\n        with:\n          issue-number: ${{ steps.results.outputs.pr_number }}\n          body-includes: \"<!-- test262-compliance-report -->\"\n\n      - name: Update or create comment\n        uses: peter-evans/create-or-update-comment@e8674b075228eee787fea43ef493e45ece1004c9 # v5\n        with:\n          comment-id: ${{ steps.previous-comment.outputs.comment-id }}\n          issue-number: ${{ steps.results.outputs.pr_number }}\n          body-path: downloaded-results/comment.md\n          edit-mode: replace\n"
  },
  {
    "path": ".github/workflows/test262_release.yml",
    "content": "name: Update Test262 Results\n\non:\n  release:\n    types:\n      - published\n  push:\n    branches:\n      - main\n\npermissions:\n  contents: read\n\nconcurrency:\n  group: ${{ github.workflow }}-${{ github.ref }}\n  cancel-in-progress: true\n\njobs:\n  update_test262_results:\n    name: Update Test262 Results\n    runs-on: ubuntu-latest\n    timeout-minutes: 60\n    steps:\n      # Checkout the main repository\n      - name: Checkout repository\n        uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6\n        with:\n          path: boa\n\n      # Install Rust toolchain\n      - name: Install Rust toolchain\n        uses: dtolnay/rust-toolchain@631a55b12751854ce901bb631d5902ceb48146f7 # stable\n        with:\n          toolchain: stable\n\n      # Cache cargo dependencies\n      - name: Cache Cargo\n        uses: Swatinem/rust-cache@c19371144df3bb44fab255c43d04cbc2ab54d1c4 # v2.9.1\n\n      # Checkout the `data` repository\n      - name: Checkout the data repo\n        uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6\n        with:\n          repository: boa-dev/data\n          token: ${{ secrets.DATA_PAT }}\n          path: data\n\n      # Run the Test262 test suite\n      - name: Run the test262 test suite\n        run: |\n          cd boa\n          cargo run --release --bin boa_tester -- run -v -o ../data/test262\n\n      # Commit and push results back to the `data` repo\n      - name: Commit results\n        run: |\n          cd data\n          git config user.name \"GitHub Actions\"\n          git config user.email \"actions@github.com\"\n          git add test262\n          git commit -m \"Update Test262 results ( ${{ github.ref_name }} )\"\n      - name: Push changes\n        uses: ad-m/github-push-action@4cc74773234f74829a8c21bc4d69dd4be9cfa599 # master\n        with:\n            # cannot use secrets.GITHUB_TOKEN since it only gives you\n            # write permissions to the current repository.\n            github_token: ${{ secrets.DATA_PAT }}\n            repository: boa-dev/data\n            directory: data\n"
  },
  {
    "path": ".github/workflows/webassembly.yml",
    "content": "name: Webassembly demo\n\non:\n  pull_request:\n    branches:\n      - main\n      - releases/**\n  push:\n    branches:\n      - main\n      - releases/**\n  merge_group:\n    types: [checks_requested]\n\npermissions:\n  contents: read\n\nconcurrency:\n  group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}\n  cancel-in-progress: true\n\njobs:\n  check_style:\n    name: Check webassembly demo style\n    runs-on: ubuntu-latest\n    timeout-minutes: 60\n    steps:\n      - name: Checkout\n        uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6\n        with:\n          persist-credentials: false\n      - name: Check code formatting\n        run: npx prettier --check .\n\n  build:\n    name: Build webassembly demo\n    runs-on: ubuntu-latest\n    timeout-minutes: 60\n    env:\n      WASM_PACK_PATH: ~/.cargo/bin/wasm-pack\n    steps:\n      - name: Set environment\n        env:\n          W_FLAGS: ${{ (github.ref == 'refs/heads/main' || github.base_ref == 'main') && '-Dwarnings' || '' }}\n        # Setting `RUSTFLAGS` overrides any flags set on .cargo/config.toml, so we need to\n        # set the target flags instead which are cumulative.\n        # Track https://github.com/rust-lang/cargo/issues/5376\n        run: echo \"CARGO_TARGET_WASM32_UNKNOWN_UNKNOWN_RUSTFLAGS=$W_FLAGS\" >> $GITHUB_ENV\n      - name: Checkout repository\n        uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6\n        with:\n          persist-credentials: false\n\n      - name: Install Rust toolchain\n        uses: dtolnay/rust-toolchain@631a55b12751854ce901bb631d5902ceb48146f7 # stable\n        with:\n          toolchain: stable\n\n\n      - name: Cache Cargo\n        uses: Swatinem/rust-cache@c19371144df3bb44fab255c43d04cbc2ab54d1c4 # v2.9.1\n\n      - name: Install wasm-pack\n        uses: baptiste0928/cargo-install@f204293d9709061b7bc1756fec3ec4e2cd57dec0 # v3.4.0\n        with:\n          crate: wasm-pack\n\n      - name: Build Playground\n        run: wasm-pack build ./ffi/wasm --verbose\n      - name: Test (Chrome)\n        run: wasm-pack test --headless --chrome ./ffi/wasm --verbose\n      - name: Test (Firefox)\n        run: wasm-pack test --headless --firefox ./ffi/wasm --verbose\n"
  },
  {
    "path": ".gitignore",
    "content": "# IDE\n.idea/\n*.iml\n\n# Vim\n*.*.swp\n*.*.swo\n\n# Build\ntarget\ndist\n**/*.rs.bk\nnode_modules\n.DS_Store\nyarn-error.log\n.vscode/settings.json\n.zed/settings.json\n\n# debug is used for testing changes locally\n/debug\n.boa_history\n\n# test262 testing suite\ntest262\n\n# wpt testing suite\ntests_wpt\n\n# Profiling\n*.string_data\n*.string_index\n*.events\nchrome_profiler.json\n*.mm_profdata\nprofile.json.gz\n\n# Logs\n*.log\n\n# Yarn\n.yarn\n.yarnrc.yml\n\n# e2e test\nplaywright-report\ntest-results\n\n# dhat\ndhat-*.json\nperf.data*\n\n# Nix\n/.envrc\n/.direnv\n"
  },
  {
    "path": ".husky/pre-push",
    "content": "#!/bin/sh\n\ntarget=$(rustc -vV | awk '/^host/ { print $2 }' | tr '[:lower:]' '[:upper:]' | tr '-' '_')\nexport CARGO_TARGET_${target}_RUSTFLAGS='-D warnings'\n\nif ! command -v cargo-make >/dev/null 2>&1; then\n    echo \"cargo-make is not installed. Install it with:\"\n    echo \"  cargo install cargo-make\"\n    exit 1\nfi\n\ncargo make run-ci\n"
  },
  {
    "path": ".prettierignore",
    "content": "# Ignore artifacts:\n*.rs\n*.yml\ntarget\nnode_modules\ncore/engine/benches/bench_scripts/mini_js.js\ncore/engine/benches/bench_scripts/clean_js.js\nffi/wasm/pkg\ndist\ntest262\nplaywright-report\ntest-results\n\n# For some reason Prettier likes to reformat JSON lock files.\nflake.lock\n\n# Assets that should not be checked.\nbenches/scripts/v8-benches\n"
  },
  {
    "path": ".vscode/launch.json",
    "content": "{\n  // Use IntelliSense to learn about possible attributes.\n  // Hover to view descriptions of existing attributes.\n  // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387\n  \"version\": \"0.2.0\",\n  \"configurations\": [\n    {\n      \"type\": \"lldb\",\n      \"request\": \"launch\",\n      \"name\": \"Debug Boa (Script mode)\",\n      \"windows\": {\n        \"program\": \"${workspaceFolder}/target/debug/boa.exe\"\n      },\n      \"program\": \"${workspaceFolder}/target/debug/boa\",\n      \"args\": [\"${workspaceFolder}/${input:filePath}\", \"--debug-object\"],\n      \"sourceLanguages\": [\"rust\"],\n      \"preLaunchTask\": \"Cargo Build boa_cli\"\n    },\n    {\n      \"type\": \"lldb\",\n      \"request\": \"launch\",\n      \"name\": \"Debug Boa (Module mode)\",\n      \"windows\": {\n        \"program\": \"${workspaceFolder}/target/debug/boa.exe\"\n      },\n      \"program\": \"${workspaceFolder}/target/debug/boa\",\n      \"args\": [\n        \"${workspaceFolder}/${input:filePath}\",\n        \"--debug-object\",\n        \"-m\",\n        \"-r\",\n        \"${workspaceFolder}/${input:modulePath}\"\n      ],\n      \"sourceLanguages\": [\"rust\"],\n      \"preLaunchTask\": \"Cargo Build boa_cli\"\n    },\n    {\n      \"type\": \"lldb\",\n      \"request\": \"launch\",\n      \"name\": \"Debug Boa (Tester)\",\n      \"windows\": {\n        \"program\": \"${workspaceFolder}/target/debug/boa_tester.exe\"\n      },\n      \"program\": \"${workspaceFolder}/target/debug/boa_tester\",\n      \"args\": [\"run\", \"-s\", \"${input:testPath}\", \"-vvv\", \"-d\"],\n      \"sourceLanguages\": [\"rust\"],\n      \"preLaunchTask\": \"Cargo Build boa_tester\"\n    }\n  ],\n  \"inputs\": [\n    {\n      \"id\": \"filePath\",\n      \"description\": \"Relative path to the file to run\",\n      \"type\": \"promptString\"\n    },\n    {\n      \"id\": \"modulePath\",\n      \"description\": \"Relative path to the module root directory\",\n      \"type\": \"promptString\"\n    },\n    {\n      \"id\": \"testPath\",\n      \"description\": \"Relative path to the test from the test262 directory\",\n      \"type\": \"promptString\"\n    }\n  ]\n}\n"
  },
  {
    "path": ".vscode/tasks.json",
    "content": "{\n  // See https://go.microsoft.com/fwlink/?LinkId=733558\n  // for the documentation about the tasks.json format\n  \"version\": \"2.0.0\",\n  \"tasks\": [\n    {\n      \"type\": \"process\",\n      \"label\": \"Cargo Build\",\n      \"command\": \"cargo\",\n      \"args\": [\"build\"],\n      \"group\": \"build\",\n      \"presentation\": {\n        \"clear\": true\n      }\n    },\n    {\n      \"type\": \"process\",\n      \"label\": \"Cargo Build boa_cli\",\n      \"command\": \"cargo\",\n      \"args\": [\"build\", \"-p\", \"boa_cli\"],\n      \"group\": \"build\",\n      \"presentation\": {\n        \"clear\": true\n      }\n    },\n    {\n      \"type\": \"process\",\n      \"label\": \"Cargo Build boa_tester\",\n      \"command\": \"cargo\",\n      \"args\": [\"build\", \"-p\", \"boa_tester\"],\n      \"group\": \"build\",\n      \"presentation\": {\n        \"clear\": true\n      }\n    },\n    {\n      \"type\": \"process\",\n      \"label\": \"Run JS file\",\n      \"command\": \"cargo\",\n      \"args\": [\"run\", \"--bin\", \"boa\", \"${file}\"],\n      \"group\": {\n        \"kind\": \"build\",\n        \"isDefault\": true\n      },\n      \"presentation\": {\n        \"clear\": true\n      },\n      \"options\": {\n        \"env\": {\n          \"RUST_BACKTRACE\": \"1\"\n        }\n      },\n      \"problemMatcher\": []\n    },\n    {\n      \"type\": \"process\",\n      \"label\": \"Run JS file (Profiler)\",\n      \"command\": \"cargo\",\n      \"args\": [\"run\", \"--features\", \"profiler\", \"${file}\"],\n      \"group\": \"build\",\n      \"options\": {\n        \"env\": {\n          \"RUST_BACKTRACE\": \"full\"\n        },\n        \"cwd\": \"${workspaceFolder}/cli\"\n      },\n      \"presentation\": {\n        \"clear\": true\n      },\n      \"problemMatcher\": []\n    },\n    {\n      \"type\": \"process\",\n      \"label\": \"Run JS file with VM trace\",\n      \"command\": \"cargo\",\n      \"args\": [\"run\", \"--bin\", \"boa\", \"--\", \"-t\", \"${file}\"],\n      \"group\": \"build\",\n      \"presentation\": {\n        \"clear\": true\n      },\n      \"problemMatcher\": []\n    },\n    {\n      \"type\": \"process\",\n      \"label\": \"Get AST for JS file\",\n      \"command\": \"cargo\",\n      \"args\": [\"run\", \"--bin\", \"boa\", \"--\", \"-a=Debug\", \"${file}\"],\n      \"group\": \"build\",\n      \"presentation\": {\n        \"clear\": true\n      },\n      \"problemMatcher\": []\n    },\n    {\n      \"type\": \"process\",\n      \"label\": \"Cargo Test\",\n      \"command\": \"cargo\",\n      \"args\": [\"test\"],\n      \"group\": {\n        \"kind\": \"test\",\n        \"isDefault\": true\n      },\n      \"presentation\": {\n        \"clear\": true\n      }\n    },\n    {\n      \"type\": \"process\",\n      \"label\": \"Cargo Test Build\",\n      \"command\": \"cargo\",\n      \"args\": [\"test\", \"--no-run\"],\n      \"group\": \"build\"\n    }\n  ]\n}\n"
  },
  {
    "path": "ABOUT.md",
    "content": "# About Boa\n\nBoa is an open-source, experimental ECMAScript Engine written in Rust for\nlexing, parsing and executing ECMAScript/JavaScript. Currently, Boa supports some\nof the [language][boa-conformance]. More information can be viewed at [Boa's\nwebsite][boa-web].\n\nTry out the most recent release with Boa's live demo\n[playground][boa-playground].\n\n## Boa Crates\n\n- [**`boa_cli`**][cli] - Boa's CLI && REPL implementation\n- [**`boa_ast`**][ast] - Boa's ECMAScript Abstract Syntax Tree.\n- [**`boa_engine`**][engine] - Boa's implementation of ECMAScript builtin objects and execution.\n- [**`boa_gc`**][gc] - Boa's garbage collector.\n- [**`boa_icu_provider`**][icu] - Boa's ICU4X data provider.\n- [**`boa_interner`**][interner] - Boa's string interner.\n- [**`boa_macros`**][macros] - Boa's macros.\n- [**`boa_parser`**][parser] - Boa's lexer and parser.\n- [**`boa_runtime`**][runtime] - Boa's `WebAPI` features.\n- [**`boa_string`**][string] - Boa's ECMAScript string implementation.\n- [**`boa_wintertc`**][wintertc] - Boa's `WinterTC` (TC55) Minimum Common Web API implementation.\n- [**`tag_ptr`**][tag_ptr] - Utility library that enables a pointer to be associated with a tag of type `usize`.\n- [**`small_btree`**][small_btree] - Utility library that adds the `SmallBTreeMap` data structure.\n\n[boa-conformance]: https://boajs.dev/conformance\n[boa-web]: https://boajs.dev/\n[boa-playground]: https://boajs.dev/playground\n[ast]: https://docs.rs/boa_ast/latest/boa_ast/index.html\n[engine]: https://docs.rs/boa_engine/latest/boa_engine/index.html\n[gc]: https://docs.rs/boa_gc/latest/boa_gc/index.html\n[interner]: https://docs.rs/boa_interner/latest/boa_interner/index.html\n[parser]: https://docs.rs/boa_parser/latest/boa_parser/index.html\n[icu]: https://docs.rs/boa_icu_provider/latest/boa_icu_provider/index.html\n[runtime]: https://docs.rs/boa_runtime/latest/boa_runtime/index.html\n[string]: https://docs.rs/boa_string/latest/boa_string/index.html\n[wintertc]: https://docs.rs/boa_wintertc/latest/boa_wintertc/index.html\n[tag_ptr]: https://docs.rs/tag_ptr/latest/tag_ptr/index.html\n[small_btree]: https://docs.rs/small_btree/latest/small_btree/index.html\n[macros]: https://docs.rs/boa_macros/latest/boa_macros/index.html\n[cli]: https://crates.io/crates/boa_cli\n"
  },
  {
    "path": "CHANGELOG.md",
    "content": "# CHANGELOG\n\n## [v0.21.0 (2025-10-21)](https://github.com/boa-dev/boa/compare/v0.20...v0.21)\n\n### Feature Enhancements\n\n- Build out Temporal's `ZonedDateTime` and `Now` by @nekevss in https://github.com/boa-dev/boa/pull/4068\n- Add valueOf methods to the Temporal builtins by @nekevss in https://github.com/boa-dev/boa/pull/4079\n- Add more `ZonedDateTime` method implementations by @nekevss in https://github.com/boa-dev/boa/pull/4095\n- Cleanup CLI to use eyre + refactor patterns by @jedel1043 in https://github.com/boa-dev/boa/pull/4108\n- Move methods of `JsString` to `JsStr` by @jedel1043 in https://github.com/boa-dev/boa/pull/4106\n- Implement `Error.isError` by @jedel1043 in https://github.com/boa-dev/boa/pull/4114\n- Improve implementation of example `JobQueue`s by @jedel1043 in https://github.com/boa-dev/boa/pull/4111\n- Implement `PlainDate` string methods by @nekevss in https://github.com/boa-dev/boa/pull/4119\n- Revamp `JobQueue` into `JobExecutor` and introduce `NativeAsyncJob` by @jedel1043 in https://github.com/boa-dev/boa/pull/4118\n- Add `From<Cow<'a, str>>` for `JsString` by @CrazyboyQCD in https://github.com/boa-dev/boa/pull/4134\n- Implement more `toString` and `toJSON` methods on Temporal builtins by @nekevss in https://github.com/boa-dev/boa/pull/4126\n- Implement `Array.fromAsync` by @jedel1043 in https://github.com/boa-dev/boa/pull/4115\n- Implement `toString` and `toJSON` methods for the remaining builtins `Duration`, `PlainMonthDay`, and `PlainYearMonth` by @nekevss in https://github.com/boa-dev/boa/pull/4135\n- Bump temporal_rs and fix instant return by @nekevss in https://github.com/boa-dev/boa/pull/4142\n- Implement toLocaleString and some general cleanup by @nekevss in https://github.com/boa-dev/boa/pull/4156\n- add macos arm64 by @ahaoboy in https://github.com/boa-dev/boa/pull/4160\n- Use NaN-boxing on value::InnerValue by @hansl in https://github.com/boa-dev/boa/pull/4091\n- Split `Tagged<T>` into a utility crate by @HalidOdat in https://github.com/boa-dev/boa/pull/3849\n- Update JsValue::to_json to support undefined by @jamesthurley in https://github.com/boa-dev/boa/pull/4212\n- Change signature of `from_async_fn` to allow capturing the context by @jedel1043 in https://github.com/boa-dev/boa/pull/4215\n- Implement Set methods from ECMAScript Specification Features/#4128 by @Hemenguelbindi in https://github.com/boa-dev/boa/pull/4145\n- Add a `#[boa_module]` macro to automatically implement a Module by @hansl in https://github.com/boa-dev/boa/pull/4277\n- Implement small changes from Intl's 2026 spec by @jedel1043 in https://github.com/boa-dev/boa/pull/4290\n- Bump rustc edition to 2024 and version to 1.88 by @jedel1043 in https://github.com/boa-dev/boa/pull/4315\n- Implement backtrace information for errors by @HalidOdat in https://github.com/boa-dev/boa/pull/4292\n- Add WPT as optional tests for boa_runtime by @hansl in https://github.com/boa-dev/boa/pull/4008\n- Simplify SourcePositionGuard creation by @hansl in https://github.com/boa-dev/boa/pull/4327\n- Make `JobExecutor::run_jobs_async` a plain async method by @jedel1043 in https://github.com/boa-dev/boa/pull/4331\n- Introduce async `ModuleLoader`s by @jedel1043 in https://github.com/boa-dev/boa/pull/4328\n- Use `AsyncFnOnce` in constructors of `NativeAsyncJob` by @jedel1043 in https://github.com/boa-dev/boa/pull/4333\n- Add (optionally) Float16Array and f16round() support and add JsUint8ClampedArray by @hansl in https://github.com/boa-dev/boa/pull/4364\n- Implement `Atomics.waitAsync` by @jedel1043 in https://github.com/boa-dev/boa/pull/4339\n- Implement `Math.sumPrecise` by @nekevss in https://github.com/boa-dev/boa/pull/4383\n- Add `Date.prototype.toTemporalInstant` from the Temporal proposal by @nekevss in https://github.com/boa-dev/boa/pull/4382\n- Implement upsert methods for Map by @jasonmilad in https://github.com/boa-dev/boa/pull/4436\n- Change JsObject default method to take `Instrinsics` parameter by @mdrokz in https://github.com/boa-dev/boa/pull/4466\n- Cleanup `BuiltInConstructor` constants to ensure no additional allocations by @jedel1043 in https://github.com/boa-dev/boa/pull/4464\n- Implement Upsert methods for weakMap: getOrInsert and getOrInsertComputed by @rrogerc in https://github.com/boa-dev/boa/pull/4459\n\n### Bug Fixes\n\n- Fix #4051, parse Arguments should expect `)` not `}` by @zzzdong in https://github.com/boa-dev/boa/pull/4058\n- bug fix: ops that stay strictly at the EOF after assigns ops are ignored by @Nikita-str in https://github.com/boa-dev/boa/pull/4047\n- Patch Temporal.PlainTime and Temporal.Duration constructors by @nekevss in https://github.com/boa-dev/boa/pull/4078\n- Fix bugs on ephemeron and TypedArray.prototype.slice by @jedel1043 in https://github.com/boa-dev/boa/pull/4107\n- Allow bool and null literals in export aliases by @jedel1043 in https://github.com/boa-dev/boa/pull/4113\n- Allow referencing `super` within initializer of static private property by @jedel1043 in https://github.com/boa-dev/boa/pull/4121\n- Fix truncation on max microseconds and nanoseconds by @nekevss in https://github.com/boa-dev/boa/pull/4139\n- Fix issues with `to_temporal_time` and `ZonedDateTime.prototype.withPlainTime` by @nekevss in https://github.com/boa-dev/boa/pull/4154\n- Some cleanup + order of operations fixes by @nekevss in https://github.com/boa-dev/boa/pull/4190\n- Fix JsValue::to_json with cyclic values by @changhc in https://github.com/boa-dev/boa/pull/4176\n- Fixed logo in documentation by @Razican in https://github.com/boa-dev/boa/pull/4208\n- Enable `wasm_js` feature of getrandom in boa_engine crate by @HalidOdat in https://github.com/boa-dev/boa/pull/4241\n- Fix panics on staging TypedArray.slice tests by @jedel1043 in https://github.com/boa-dev/boa/pull/4289\n- Add the legacy enum-based JsValue implementation behind a flag by @hansl in https://github.com/boa-dev/boa/pull/4281\n- Allow non-reserved keywords to be used as identifiers by @cijiugechu in https://github.com/boa-dev/boa/pull/4307\n- Avoid fully awaiting futures in async event loops by @jedel1043 in https://github.com/boa-dev/boa/pull/4332\n- Prevent evalutation of code with `--dump-ast` flag by @HalidOdat in https://github.com/boa-dev/boa/pull/4337\n- Some general bug fixes for Temporal implementation by @nekevss in https://github.com/boa-dev/boa/pull/4349\n- Fix UB in implementation of `NanBoxedValue` by @jedel1043 in https://github.com/boa-dev/boa/pull/4346\n- fix(regexp): fix the capture group count assert to have the correct upper limit by @BDeuDev in https://github.com/boa-dev/boa/pull/4419\n- Fix `contains_direct_eval` for ordinary function by @hpp2334 in https://github.com/boa-dev/boa/pull/4453\n- Fix test262 comments on new PR by @jedel1043 in https://github.com/boa-dev/boa/pull/4465\n\n### Internal Improvements\n\n- Remove `try_break` macro in favour of question mark operator by @jedel1043 in https://github.com/boa-dev/boa/pull/4112\n- Use cow-utils instead by @heygsc in https://github.com/boa-dev/boa/pull/4133\n- Register VM by @HalidOdat in https://github.com/boa-dev/boa/pull/3798\n- Bump MSRV to 1.84 by @jedel1043 in https://github.com/boa-dev/boa/pull/4165\n- Avoid unnecessary calls of `to_string` in `cow_*` by @CrazyboyQCD in https://github.com/boa-dev/boa/pull/4166\n- Apply rustc 1.85 lints by @jedel1043 in https://github.com/boa-dev/boa/pull/4170\n- Some string cleanups by @CrazyboyQCD in https://github.com/boa-dev/boa/pull/4090\n- Refactor bytecode representation by @raskad in https://github.com/boa-dev/boa/pull/4220\n- Refactor registers to use the stack by @raskad in https://github.com/boa-dev/boa/pull/4263\n- Bump Test262 hash commit and cleanup test features by @jedel1043 in https://github.com/boa-dev/boa/pull/4288\n- Replace inner `Gc<T>` with `Rc<T>` for `SourceText` by @HalidOdat in https://github.com/boa-dev/boa/pull/4293\n- Fix more Intl tests for latest ECMA402 spec by @jedel1043 in https://github.com/boa-dev/boa/pull/4304\n- Fix lints for rustc 1.88 by @jedel1043 in https://github.com/boa-dev/boa/pull/4309\n- Migrate `temporal_rs` from `0.0.9` to `0.0.10` by @HalidOdat in https://github.com/boa-dev/boa/pull/4318\n- Mark the error path in `Call::operation` as cold. by @cijiugechu in https://github.com/boa-dev/boa/pull/4319\n- Add arm64 linux nightly build by @nekevss in https://github.com/boa-dev/boa/pull/4321\n- extract small_map as separate utility crate by @countradooku in https://github.com/boa-dev/boa/pull/4214\n- Remove `Box<T>` from `JsValue` for `JsString` by @HalidOdat in https://github.com/boa-dev/boa/pull/4329\n- Add `repr(C)` on `Object` to prevent field reordering by @HalidOdat in https://github.com/boa-dev/boa/pull/4343\n- Fix clippy lints for Rust 1.89 by @nekevss in https://github.com/boa-dev/boa/pull/4368\n- Add Nix flake by @xubaiwang in https://github.com/boa-dev/boa/pull/4381\n- Apply clippy fixes for Rust 1.90 by @hansl in https://github.com/boa-dev/boa/pull/4423\n- Fix UB on unaligned ArrayBuffers by @jedel1043 in https://github.com/boa-dev/boa/pull/4427\n- Use same cache key for builds and tests by @jasonwilliams in https://github.com/boa-dev/boa/pull/4426\n\n### Other Changes\n\n- Bug fix: regex started with `/=` parsed as `AssignDiv` by @Nikita-str in https://github.com/boa-dev/boa/pull/4048\n- Add fast path for number to `JsString` conversion by @CrazyboyQCD in https://github.com/boa-dev/boa/pull/4054\n- Bump the rust-dependencies group with 3 updates by @dependabot[bot] in https://github.com/boa-dev/boa/pull/4069\n- Add `fjcvtzs` instruction for `ARMv8.3` target by @CrazyboyQCD in https://github.com/boa-dev/boa/pull/4084\n- Add a stress test to the parser to parser multi-millions tokens by @hansl in https://github.com/boa-dev/boa/pull/4086\n- Bump the rust-dependencies group with 4 updates by @dependabot[bot] in https://github.com/boa-dev/boa/pull/4088\n- Privatize `JsValue`'s internals and expose it through a JsVariant (with immutable reference) by @hansl in https://github.com/boa-dev/boa/pull/4080\n- Skip creation of arguments object if possible by @raskad in https://github.com/boa-dev/boa/pull/4087\n- Allow resizing of underlying ArrayBuffer from Rust by @hansl in https://github.com/boa-dev/boa/pull/4082\n- Add inline cache for getting bindings from the global object by @raskad in https://github.com/boa-dev/boa/pull/4067\n- Update Temporal ToIntegerIfIntegral, ToIntegerWithTruncation, and ToPositiveIntegerWithTruncation implementation by @nekevss in https://github.com/boa-dev/boa/pull/4081\n- Adjust call to correct method for `PlainDateTime.prototype.since` by @nekevss in https://github.com/boa-dev/boa/pull/4096\n- fix very minor typo shift -> unshift by @albertleigh in https://github.com/boa-dev/boa/pull/4097\n- Bump the `temporal_rs` version and related changes by @nekevss in https://github.com/boa-dev/boa/pull/4098\n- Bump test262 commit and changes to `boa_tester` to support sm changes by @nekevss in https://github.com/boa-dev/boa/pull/4099\n- Bump the rust-dependencies group across 1 directory with 6 updates by @dependabot[bot] in https://github.com/boa-dev/boa/pull/4103\n- Remove some clones and branches in hot `PropertyDescriptor` functions by @raskad in https://github.com/boa-dev/boa/pull/4104\n- Bump syn from 2.0.93 to 2.0.95 in the rust-dependencies group by @dependabot[bot] in https://github.com/boa-dev/boa/pull/4109\n- Fix CI with the latest linter errors introduced in 1.84 by @hansl in https://github.com/boa-dev/boa/pull/4117\n- Use `cow_to_ascii_uppercase` instead by @heygsc in https://github.com/boa-dev/boa/pull/4124\n- Bump the rust-dependencies group across 1 directory with 7 updates by @dependabot[bot] in https://github.com/boa-dev/boa/pull/4131\n- Set the array \"length\" property in `[[DefineOwnProperty]]` based on the array shape by @raskad in https://github.com/boa-dev/boa/pull/4101\n- Bump the rust-dependencies group with 5 updates by @dependabot[bot] in https://github.com/boa-dev/boa/pull/4137\n- Implement since and until methods for ZonedDateTime by @nekevss in https://github.com/boa-dev/boa/pull/4136\n- Bump baptiste0928/cargo-install from 3.1.1 to 3.3.0 in the ci-dependencies group by @dependabot[bot] in https://github.com/boa-dev/boa/pull/4138\n- Make the HostHooks shareable between app and context by @hansl in https://github.com/boa-dev/boa/pull/4141\n- Bump the rust-dependencies group across 1 directory with 3 updates by @dependabot[bot] in https://github.com/boa-dev/boa/pull/4148\n- Implement an internal time type and Clock trait by @hansl in https://github.com/boa-dev/boa/pull/4149\n- `SourceText` collection & `toString()` for fns and methods by @Nikita-str in https://github.com/boa-dev/boa/pull/4038\n- `setTimeout`, `setInterval` and `clearInterval` (and the same `clearTimeout`) implementations by @hansl in https://github.com/boa-dev/boa/pull/4130\n- Bump the rust-dependencies group with 3 updates by @dependabot[bot] in https://github.com/boa-dev/boa/pull/4151\n- Bump the rust-dependencies group with 5 updates by @dependabot[bot] in https://github.com/boa-dev/boa/pull/4157\n- Add trace function to Logger trait by @jamesthurley in https://github.com/boa-dev/boa/pull/4155\n- Allow local parameters if mapped arguments object is not used by @raskad in https://github.com/boa-dev/boa/pull/4092\n- Bump temporal_rs to Feb. 15 version + adjustments by @nekevss in https://github.com/boa-dev/boa/pull/4162\n- Bump the rust-dependencies group with 3 updates by @dependabot[bot] in https://github.com/boa-dev/boa/pull/4163\n- Add `PlainDateTime.prototype.round` implementation from `temporal_rs` by @nekevss in https://github.com/boa-dev/boa/pull/4164\n- Fix some engine specific bugs and bump version by @nekevss in https://github.com/boa-dev/boa/pull/4167\n- Simplify date parser by @CrazyboyQCD in https://github.com/boa-dev/boa/pull/4143\n- Build out remaining method stubs for temporal by @nekevss in https://github.com/boa-dev/boa/pull/4172\n- Bump the rust-dependencies group with 4 updates by @dependabot[bot] in https://github.com/boa-dev/boa/pull/4174\n- Prioritize drop of common types for nan-boxed `JsValue` by @HalidOdat in https://github.com/boa-dev/boa/pull/4178\n- Add changes from YearMonth parsing update and MonthCode addition by @nekevss in https://github.com/boa-dev/boa/pull/4173\n- implementation of static method compare for duration from temporal.rs by @lockels in https://github.com/boa-dev/boa/pull/4189\n- Unify release workflows by @HalidOdat in https://github.com/boa-dev/boa/pull/4192\n- Temporal bump and fixes by @nekevss in https://github.com/boa-dev/boa/pull/4193\n- Bump ring from 0.17.9 to 0.17.13 by @dependabot[bot] in https://github.com/boa-dev/boa/pull/4201\n- Bump the rust-dependencies group across 1 directory with 20 updates by @dependabot[bot] in https://github.com/boa-dev/boa/pull/4204\n- feat(examples): Add comprehensive JsPromise example by @created-by-varun in https://github.com/boa-dev/boa/pull/4198\n- Temporal bump and implementation of toPlainYearMonth and toPlainMonthDay by @lockels in https://github.com/boa-dev/boa/pull/4207\n- Bump the rust-dependencies group with 5 updates by @dependabot[bot] in https://github.com/boa-dev/boa/pull/4209\n- Bump the rust-dependencies group with 4 updates by @dependabot[bot] in https://github.com/boa-dev/boa/pull/4216\n- Fix Rust 1.86.0 lints and update dependencies by @raskad in https://github.com/boa-dev/boa/pull/4228\n- Move interop and module utilities from boa_interop into boa_engine by @hansl in https://github.com/boa-dev/boa/pull/4218\n- Bump the rust-dependencies group with 3 updates by @dependabot[bot] in https://github.com/boa-dev/boa/pull/4229\n- Temporal bump by @lockels in https://github.com/boa-dev/boa/pull/4231\n- Bump the rust-dependencies group with 2 updates by @dependabot[bot] in https://github.com/boa-dev/boa/pull/4232\n- Enable `PlainDate.prototype.toZonedDateTime` method by @nekevss in https://github.com/boa-dev/boa/pull/4233\n- `PlainDateTime::toZonedDateTime` & `PlainDateTime::toPlainDate` implementations by @nekevss in https://github.com/boa-dev/boa/pull/4234\n- Bump temporal_rs and add ZonedDateTime.prototype.round impl by @nekevss in https://github.com/boa-dev/boa/pull/4236\n- Bump the rust-dependencies group across 1 directory with 6 updates by @dependabot[bot] in https://github.com/boa-dev/boa/pull/4239\n- Bump the rust-dependencies group with 2 updates by @dependabot[bot] in https://github.com/boa-dev/boa/pull/4242\n- Bump temporal_rs and remove dead code by @nekevss in https://github.com/boa-dev/boa/pull/4248\n- Fix Rust 1.87.0 lints by @raskad in https://github.com/boa-dev/boa/pull/4249\n- Avoid range checks in nan-boxing by @raskad in https://github.com/boa-dev/boa/pull/4251\n- Update README.md to new version. by @tomoverlund in https://github.com/boa-dev/boa/pull/4254\n- Fix the Set methods to pass the 262 tests by @hansl in https://github.com/boa-dev/boa/pull/4260\n- Add support for LZ4 compression in embedded module loader by @hansl in https://github.com/boa-dev/boa/pull/4261\n- Bump the rust-dependencies group across 1 directory with 3 updates by @dependabot[bot] in https://github.com/boa-dev/boa/pull/4264\n- Fix AST `contains` operation by @HalidOdat in https://github.com/boa-dev/boa/pull/4267\n- Bump temporal_rs and fix some order of operation issues by @nekevss in https://github.com/boa-dev/boa/pull/4268\n- Escape analyze function scopes on non-arrow functions by @HalidOdat in https://github.com/boa-dev/boa/pull/4266\n- Remove local binding's initialized state in `CallFrame` by @HalidOdat in https://github.com/boa-dev/boa/pull/4269\n- Improve `README.md` by @HalidOdat in https://github.com/boa-dev/boa/pull/4270\n- Add a #[boa_class] proc macro attribute by @hansl in https://github.com/boa-dev/boa/pull/4271\n- Bump ICU4X to 2.0 by @jedel1043 in https://github.com/boa-dev/boa/pull/4274\n- Bump the rust-dependencies group with 2 updates by @dependabot[bot] in https://github.com/boa-dev/boa/pull/4278\n- Add `Span`s to expression nodes by @HalidOdat in https://github.com/boa-dev/boa/pull/4273\n- Bump the rust-dependencies group with 4 updates by @dependabot[bot] in https://github.com/boa-dev/boa/pull/4283\n- Bump temporal_rs to v0.0.9 by @nekevss in https://github.com/boa-dev/boa/pull/4285\n- remove boa_profiler and profiler calls. update docs. (#4272) by @Timkarx in https://github.com/boa-dev/boa/pull/4276\n- Add a #[boa(rename = ...)] attribute to TryFromJs and TryIntoJs derive macros by @hansl in https://github.com/boa-dev/boa/pull/4286\n- Add a js_value! macro to allow creation of JsValue from JSON-like DSL by @hansl in https://github.com/boa-dev/boa/pull/4282\n- Bump baptiste0928/cargo-install from 3.3.0 to 3.3.1 in the ci-dependencies group by @dependabot[bot] in https://github.com/boa-dev/boa/pull/4295\n- Bump the rust-dependencies group with 4 updates by @dependabot[bot] in https://github.com/boa-dev/boa/pull/4296\n- Update phf requirement from 0.11.2 to 0.12.1 in /tests/fuzz in the fuzz-dependencies group by @dependabot[bot] in https://github.com/boa-dev/boa/pull/4297\n- Fix `JsValue::mul` to handle zero times a negative integer by @Rafferty97 in https://github.com/boa-dev/boa/pull/4303\n- Move the URL class to using the boa_class macro by @hansl in https://github.com/boa-dev/boa/pull/4294\n- Hint branch predictor that surrogate pairs are rare by @cijiugechu in https://github.com/boa-dev/boa/pull/4312\n- cli: use `mimalloc` as global allocator on windows by @cijiugechu in https://github.com/boa-dev/boa/pull/4314\n- Refactor the `Binding` modifiers to use bitflags by @cijiugechu in https://github.com/boa-dev/boa/pull/4316\n- Implement type erased `Gc<T>` by @HalidOdat in https://github.com/boa-dev/boa/pull/4291\n- inline `Gc::inner_ptr` by @cijiugechu in https://github.com/boa-dev/boa/pull/4317\n- Shrink `JsStr` from 24 to 16 bytes by @HalidOdat in https://github.com/boa-dev/boa/pull/4322\n- Bump tokio from 1.45.1 to 1.46.1 in the rust-dependencies group by @dependabot[bot] in https://github.com/boa-dev/boa/pull/4324\n- Refactor `JsObject` to always be size 8 by @raskad in https://github.com/boa-dev/boa/pull/4287\n- Add a new `Nullable<T>` type that deserialize null to `Nullable::<T>::Null` by @hansl in https://github.com/boa-dev/boa/pull/4325\n- Refactor `JsString`'s static string tagging by @HalidOdat in https://github.com/boa-dev/boa/pull/4334\n- Bump the rust-dependencies group with 6 updates by @dependabot[bot] in https://github.com/boa-dev/boa/pull/4335\n- Implement native backtrace positions by @HalidOdat in https://github.com/boa-dev/boa/pull/4306\n- Fix documentation for NativeFunction::from_async_fn by @jedel1043 in https://github.com/boa-dev/boa/pull/4344\n- Bump the rust-dependencies group with 2 updates by @dependabot[bot] in https://github.com/boa-dev/boa/pull/4347\n- Bump temporal_rs version to v0.0.11 by @nekevss in https://github.com/boa-dev/boa/pull/4348\n- Add an empty EmbeddedModuleLoader default by @hansl in https://github.com/boa-dev/boa/pull/4351\n- Fix a camel case bug by @hansl in https://github.com/boa-dev/boa/pull/4352\n- Temporal documentation update, part 1 by @nekevss in https://github.com/boa-dev/boa/pull/4353\n- Temporal documentation update, part 2 by @nekevss in https://github.com/boa-dev/boa/pull/4354\n- Temporal documentation, part 3 by @nekevss in https://github.com/boa-dev/boa/pull/4355\n- Temporal documentation update, part 4 by @nekevss in https://github.com/boa-dev/boa/pull/4356\n- Bump tokio from 1.46.1 to 1.47.0 in the rust-dependencies group by @dependabot[bot] in https://github.com/boa-dev/boa/pull/4357\n- `Fetch` API (beta) by @hansl in https://github.com/boa-dev/boa/pull/4338\n- Remove `Box` from `Object`'s data member by @HalidOdat in https://github.com/boa-dev/boa/pull/4342\n- Bump the rust-dependencies group with 6 updates by @dependabot[bot] in https://github.com/boa-dev/boa/pull/4361\n- Add support for utf16(-le|be) and use the class API by @hansl in https://github.com/boa-dev/boa/pull/4358\n- Bump temporal_rs version to v0.0.12 by @nekevss in https://github.com/boa-dev/boa/pull/4367\n- Allow public access to the full position of a call frame by @hansl in https://github.com/boa-dev/boa/pull/4365\n- Bump the rust-dependencies group with 5 updates by @dependabot[bot] in https://github.com/boa-dev/boa/pull/4374\n- Bump slab from 0.4.10 to 0.4.11 by @dependabot[bot] in https://github.com/boa-dev/boa/pull/4376\n- Implement queueMicrotask() and a test for it by @hansl in https://github.com/boa-dev/boa/pull/4359\n- Bump actions/checkout from 4 to 5 by @dependabot[bot] in https://github.com/boa-dev/boa/pull/4386\n- Bump the rust-dependencies group with 4 updates by @dependabot[bot] in https://github.com/boa-dev/boa/pull/4387\n- Improve object display by @xubaiwang in https://github.com/boa-dev/boa/pull/4377\n- Fix broken PlainMonthDay.from tests by @nekevss in https://github.com/boa-dev/boa/pull/4389\n- Fix final Temporal.Instant test in built-ins test suite by @nekevss in https://github.com/boa-dev/boa/pull/4388\n- Remove alignment of property keys with identation by @hansl in https://github.com/boa-dev/boa/pull/4390\n- Bump temporal_rs version to v0.0.14 by @nekevss in https://github.com/boa-dev/boa/pull/4391\n- Update `Math.sumPrecise` to remove `SummationState` by @nekevss in https://github.com/boa-dev/boa/pull/4392\n- Update phf requirement from 0.12.1 to 0.13.1 in /tests/fuzz in the fuzz-dependencies group by @dependabot[bot] in https://github.com/boa-dev/boa/pull/4384\n- Fix index access in `JumpTable` instruction by @HalidOdat in https://github.com/boa-dev/boa/pull/4372\n- Bump the rust-dependencies group with 5 updates by @dependabot[bot] in https://github.com/boa-dev/boa/pull/4395\n- Fix: prevent OOB panic in decodeURI by tightening percent-decoding bounds; add test for incomplete escape %E7%9A%8 (#4404) by @hamflx in https://github.com/boa-dev/boa/pull/4405\n- Implement a `JsValueStore` as well as structuredClone and tests by @hansl in https://github.com/boa-dev/boa/pull/4366\n- Bump baptiste0928/cargo-install from 3.3.1 to 3.3.2 in the ci-dependencies group by @dependabot[bot] in https://github.com/boa-dev/boa/pull/4385\n- Update hashbrown requirement from 0.15.5 to 0.16.0 in /tests/fuzz in the fuzz-dependencies group by @dependabot[bot] in https://github.com/boa-dev/boa/pull/4396\n- Replace the String producing Display logic to using a Formatter by @hansl in https://github.com/boa-dev/boa/pull/4393\n- Propagate AST errors to the user by @hansl in https://github.com/boa-dev/boa/pull/4408\n- Remove the boa_interop module by @hansl in https://github.com/boa-dev/boa/pull/4407\n- Fix final `Temporal.PlainTime.from` tests by @nekevss in https://github.com/boa-dev/boa/pull/4411\n- Bump the rust-dependencies group with 3 updates by @dependabot[bot] in https://github.com/boa-dev/boa/pull/4413\n- Bump actions/setup-node from 4 to 5 by @dependabot[bot] in https://github.com/boa-dev/boa/pull/4412\n- Various quality of life improvements to the CLI by @hansl in https://github.com/boa-dev/boa/pull/4414\n- Implement `Date` and `RegExp` `JsValueStore` from/to by @hansl in https://github.com/boa-dev/boa/pull/4415\n- Use async channels for Atomics.waitAsync implementation by @jedel1043 in https://github.com/boa-dev/boa/pull/4418\n- Bump the rust-dependencies group across 1 directory with 7 updates by @dependabot[bot] in https://github.com/boa-dev/boa/pull/4421\n- Unblock Context::run_jobs when no timeouts need to be run by @hansl in https://github.com/boa-dev/boa/pull/4416\n- Fix 2 UBs, and cleanup `GcRefCell` by @hansl in https://github.com/boa-dev/boa/pull/4422\n- Bump temporal_rs version to v0.0.16 by @nekevss in https://github.com/boa-dev/boa/pull/4425\n- Fix the CLI when it is not attached to a TTY by @hansl in https://github.com/boa-dev/boa/pull/4424\n- Bump the rust-dependencies group with 6 updates by @dependabot[bot] in https://github.com/boa-dev/boa/pull/4429\n- Implement `JsValueStore` for `SharedArrayBuffer` and `postMessage` by @hansl in https://github.com/boa-dev/boa/pull/4417\n- fix: comment typo by @jasonmilad in https://github.com/boa-dev/boa/pull/4430\n- Editorconfig: specify indentation and dont overrule Makefile by @hansl in https://github.com/boa-dev/boa/pull/4446\n- Add a simple cargo-make to the project by @hansl in https://github.com/boa-dev/boa/pull/4437\n- Update to temporal_rs to 0.1 release by @nekevss in https://github.com/boa-dev/boa/pull/4433\n- Add minimal CI task to cargo-make by @nekevss in https://github.com/boa-dev/boa/pull/4448\n- Improve regular expression flags parsing by @hansl in https://github.com/boa-dev/boa/pull/4434\n- Bump the rust-dependencies group with 4 updates by @dependabot[bot] in https://github.com/boa-dev/boa/pull/4449\n- Only validate the RegExp, do not optimize/compile it by @hansl in https://github.com/boa-dev/boa/pull/4451\n- Bump peter-evans/create-or-update-comment from 4 to 5 by @dependabot[bot] in https://github.com/boa-dev/boa/pull/4456\n- Bump peter-evans/find-comment from 3 to 4 by @dependabot[bot] in https://github.com/boa-dev/boa/pull/4457\n- Bump bytemuck from 1.23.2 to 1.24.0 in the rust-dependencies group by @dependabot[bot] in https://github.com/boa-dev/boa/pull/4458\n- Allow printing test262 comment on PRs from forks by @jedel1043 in https://github.com/boa-dev/boa/pull/4428\n- perf: Improve `Math.sumPrecise` Performance by @Gumichocopengin8 in https://github.com/boa-dev/boa/pull/4462\n- Bump the rust-dependencies group with 3 updates by @dependabot[bot] in https://github.com/boa-dev/boa/pull/4467\n\n### New Contributors\n\n- @zzzdong made their first contribution in https://github.com/boa-dev/boa/pull/4058\n- @albertleigh made their first contribution in https://github.com/boa-dev/boa/pull/4097\n- @heygsc made their first contribution in https://github.com/boa-dev/boa/pull/4124\n- @jamesthurley made their first contribution in https://github.com/boa-dev/boa/pull/4155\n- @lockels made their first contribution in https://github.com/boa-dev/boa/pull/4189\n- @changhc made their first contribution in https://github.com/boa-dev/boa/pull/4176\n- @created-by-varun made their first contribution in https://github.com/boa-dev/boa/pull/4198\n- @tomoverlund made their first contribution in https://github.com/boa-dev/boa/pull/4254\n- @Hemenguelbindi made their first contribution in https://github.com/boa-dev/boa/pull/4145\n- @Timkarx made their first contribution in https://github.com/boa-dev/boa/pull/4276\n- @Rafferty97 made their first contribution in https://github.com/boa-dev/boa/pull/4303\n- @cijiugechu made their first contribution in https://github.com/boa-dev/boa/pull/4307\n- @countradooku made their first contribution in https://github.com/boa-dev/boa/pull/4214\n- @xubaiwang made their first contribution in https://github.com/boa-dev/boa/pull/4381\n- @hamflx made their first contribution in https://github.com/boa-dev/boa/pull/4405\n- @BDeuDev made their first contribution in https://github.com/boa-dev/boa/pull/4419\n- @jasonmilad made their first contribution in https://github.com/boa-dev/boa/pull/4430\n- @hpp2334 made their first contribution in https://github.com/boa-dev/boa/pull/4453\n- @Gumichocopengin8 made their first contribution in https://github.com/boa-dev/boa/pull/4462\n- @mdrokz made their first contribution in https://github.com/boa-dev/boa/pull/4466\n- @rrogerc made their first contribution in https://github.com/boa-dev/boa/pull/4459\n\n**Full Changelog**: https://github.com/boa-dev/boa/compare/v0.20...v0.21\n\n## [0.20.0 (2024-12-05)](https://github.com/boa-dev/boa/compare/v0.19.1...v0.20.0)\n\n### Feature Enhancements\n\n- Add a js_error! macro to create opaque errors by @hansl in https://github.com/boa-dev/boa/pull/3920\n- Update `Instant` for new Temporal functionality by @nekevss in https://github.com/boa-dev/boa/pull/3928\n- Add a way to add setters/getters in js_class! by @hansl in https://github.com/boa-dev/boa/pull/3911\n- Fix lints from rustc 1.80.0 by @jedel1043 in https://github.com/boa-dev/boa/pull/3936\n- Add a JsError::from_rust constructor to create native errors from Rust by @hansl in https://github.com/boa-dev/boa/pull/3921\n- add some temporal methods by @jasonwilliams in https://github.com/boa-dev/boa/pull/3856\n- Allow a custom Logger to be used as the backend for boa_runtime::Console by @hansl in https://github.com/boa-dev/boa/pull/3943\n- Add more utility functions around modules and exports by @hansl in https://github.com/boa-dev/boa/pull/3937\n- Allow trailing commas in js_class functions by @hansl in https://github.com/boa-dev/boa/pull/3964\n- Implement `Atomics.pause` by @jedel1043 in https://github.com/boa-dev/boa/pull/3956\n- Add a clone_inner method to allow cloning of inner data by @hansl in https://github.com/boa-dev/boa/pull/3968\n- fix: ignore `debugger` statement by @shurizzle in https://github.com/boa-dev/boa/pull/3976\n- Add support for boa(rename = \"\") in TryFromJs derive by @hansl in https://github.com/boa-dev/boa/pull/3980\n- Add an \"iter()\" method to Js\\*Array for convenience by @hansl in https://github.com/boa-dev/boa/pull/3986\n- A simple module loader from a function by @hansl in https://github.com/boa-dev/boa/pull/3932\n- Add a way for js_error! macro to create native errors with message by @hansl in https://github.com/boa-dev/boa/pull/3971\n- Limit actions runs to 1 per branch and fix macos release by @jedel1043 in https://github.com/boa-dev/boa/pull/3996\n- Add TextEncoder, TextDecoder implementations to boa_runtime by @hansl in https://github.com/boa-dev/boa/pull/3994\n- Add TryFromJs for TypedJsFunction and more tests by @hansl in https://github.com/boa-dev/boa/pull/3981\n- Add context to the console `Logger` trait by @hansl in https://github.com/boa-dev/boa/pull/4005\n- Add a URL class to boa_runtime by @hansl in https://github.com/boa-dev/boa/pull/4004\n- Add a display_lossy() to write a JsString lossily by @hansl in https://github.com/boa-dev/boa/pull/4023\n- `TryIntoJs` trait and derive macro for it by @Nikita-str in https://github.com/boa-dev/boa/pull/3999\n- console.debug() should use a debug Logger method by @hansl in https://github.com/boa-dev/boa/pull/4019\n- `TryFromJs` from `JsMap` for `HashMap` & `BtreeMap` by @Nikita-str in https://github.com/boa-dev/boa/pull/3998\n- Add string builder to build `JsString` by @CrazyboyQCD in https://github.com/boa-dev/boa/pull/3915\n\n### Bug Fixes\n\n- Implement `Math.pow` function according to ECMAScript specification by @magic-akari in https://github.com/boa-dev/boa/pull/3916\n- Fix temporal builtin properties by @nekevss in https://github.com/boa-dev/boa/pull/3930\n- Fix wrong `neg` operation by @CrazyboyQCD in https://github.com/boa-dev/boa/pull/3926\n- Fix destructuring assignment evaluation order by @raskad in https://github.com/boa-dev/boa/pull/3934\n- Fix various parser idempotency issues and parsing errors by @raskad in https://github.com/boa-dev/boa/pull/3917\n- Implement new spec changes for `AsyncGenerator` by @jedel1043 in https://github.com/boa-dev/boa/pull/3950\n- Refactor ast function types by @raskad in https://github.com/boa-dev/boa/pull/3931\n- Fix `js_str` macro to correctly handle latin1 strings by @jedel1043 in https://github.com/boa-dev/boa/pull/3959\n- Allow dead code for code that is newly detected as unused by @hansl in https://github.com/boa-dev/boa/pull/3984\n- Allow warnings when running CI on release branches by @jedel1043 in https://github.com/boa-dev/boa/pull/3990\n- docs: Fix link to examples by @it-a-me in https://github.com/boa-dev/boa/pull/4007\n- `IntegerOrInfinity` `eq` bug fix by @Nikita-str in https://github.com/boa-dev/boa/pull/4010\n\n### Internal Improvements\n\n- Refactor `RawJsString`'s representation to make `JsString`s construction from string literal heap-allocation free by @CrazyboyQCD in https://github.com/boa-dev/boa/pull/3935\n- Split default icu data into lazily deserialized parts by @jedel1043 in https://github.com/boa-dev/boa/pull/3948\n- Add clippy for denying print and eprints by @hansl in https://github.com/boa-dev/boa/pull/3967\n- Refactor iterator APIs to be on parity with the latest spec by @jedel1043 in https://github.com/boa-dev/boa/pull/3962\n- Add support for Trace, Finalize and JsData for Convert<> by @hansl in https://github.com/boa-dev/boa/pull/3970\n- use with_capacity to reduce re-allocations fixes #3896 by @jasonwilliams in https://github.com/boa-dev/boa/pull/3961\n- add nightly build by @jasonwilliams in https://github.com/boa-dev/boa/pull/4026\n- Patch the indentation in nightly_build.yml by @nekevss in https://github.com/boa-dev/boa/pull/4028\n- Update night build's rename binary step by @nekevss in https://github.com/boa-dev/boa/pull/4032\n- Use upload-rust-binary-action for nightly release by @nekevss in https://github.com/boa-dev/boa/pull/4040\n- Fix `ref` value in nightly and add target to nightly release by @nekevss in https://github.com/boa-dev/boa/pull/4042\n- Reduce environment allocations by @raskad in https://github.com/boa-dev/boa/pull/4002\n\n### Other Changes\n\n- Implement more Temporal functionality by @nekevss in https://github.com/boa-dev/boa/pull/3924\n- Add a Source::with_path method to set the path on a Source by @hansl in https://github.com/boa-dev/boa/pull/3941\n- Add spec edition 15 to the tester by @jedel1043 in https://github.com/boa-dev/boa/pull/3957\n- Rename as_promise to as_promise_object and add as_promise -> JsPromise by @hansl in https://github.com/boa-dev/boa/pull/3965\n- Build out partial record functionality, property bag construction, and `with` methods by @nekevss in https://github.com/boa-dev/boa/pull/3955\n- Enable CI for release branches by @jedel1043 in https://github.com/boa-dev/boa/pull/3987\n- Add a display type for JsString to allow formatting without allocations by @hansl in https://github.com/boa-dev/boa/pull/3951\n- Add TryIntoJsResult for vectors by @hansl in https://github.com/boa-dev/boa/pull/3993\n- Add tests from WPT and fix them in the Console by @hansl in https://github.com/boa-dev/boa/pull/3979\n- Update changelog for v0.19.1 by @jedel1043 in https://github.com/boa-dev/boa/pull/3995\n- Implement register allocation by @HalidOdat in https://github.com/boa-dev/boa/pull/3942\n- Implement scope analysis and local variables by @raskad in https://github.com/boa-dev/boa/pull/3988\n- `JsValue::to_json` fix integer property keys by @Nikita-str in https://github.com/boa-dev/boa/pull/4011\n- Some optimizations on `Error` by @CrazyboyQCD in https://github.com/boa-dev/boa/pull/4020\n- Option::None should try into Undefined, not Null by @hansl in https://github.com/boa-dev/boa/pull/4029\n- Some string optimizations by @CrazyboyQCD in https://github.com/boa-dev/boa/pull/4030\n- Add a JsPromise::from_result for convenience by @hansl in https://github.com/boa-dev/boa/pull/4039\n- Fix misspelled permissions in nightly build action by @nekevss in https://github.com/boa-dev/boa/pull/4041\n- Remove dockerfile from documentation by @4yman-0 in https://github.com/boa-dev/boa/pull/4046\n- Bump dependencies with breaking changes by @jedel1043 in https://github.com/boa-dev/boa/pull/4050\n- Migrate to fast-float2 by @jedel1043 in https://github.com/boa-dev/boa/pull/4052\n\n### New Contributors\n\n- @magic-akari made their first contribution in https://github.com/boa-dev/boa/pull/3916\n- @shurizzle made their first contribution in https://github.com/boa-dev/boa/pull/3976\n- @it-a-me made their first contribution in https://github.com/boa-dev/boa/pull/4007\n- @Nikita-str made their first contribution in https://github.com/boa-dev/boa/pull/4010\n- @4yman-0 made their first contribution in https://github.com/boa-dev/boa/pull/4046\n\n**Full Changelog**: https://github.com/boa-dev/boa/compare/v0.19...v0.20.0\n\n## [0.19.1 (2024-09-11)](https://github.com/boa-dev/boa/compare/v0.19...v0.19.1)\n\n### Bug Fixes\n\n- Implement new spec changes for `AsyncGenerator` by @jedel1043 in https://github.com/boa-dev/boa/pull/3950\n- Allow dead code for code that is newly detected as unused by @hansl in https://github.com/boa-dev/boa/pull/3984\n- Allow warnings when running CI on release branches by @jedel1043 in https://github.com/boa-dev/boa/pull/3990\n\n### Internal Improvements\n\n- Add spec edition 15 to the tester by @jedel1043 in https://github.com/boa-dev/boa/pull/3957\n- Enable CI for release branches by @jedel1043 in https://github.com/boa-dev/boa/pull/3987\n\n**Full Changelog**: https://github.com/boa-dev/boa/compare/v0.19...v0.19.1\n\n## [0.19.0 (2024-07-08)](https://github.com/boa-dev/boa/compare/v0.18...v0.19)\n\n### Feature Enhancements\n\n- Add release binary striping by @Razican in https://github.com/boa-dev/boa/pull/3727\n- Added NPM publish workflow by @Razican in https://github.com/boa-dev/boa/pull/3725\n- Remove references to dev docs and npm dependencies by @jedel1043 in https://github.com/boa-dev/boa/pull/3787\n- Cleanup tester deps and patterns by @jedel1043 in https://github.com/boa-dev/boa/pull/3792\n- Build docs.rs docs with all features enabled by @jedel1043 in https://github.com/boa-dev/boa/pull/3794\n- Add a new type Convert<> to convert values by @hansl in https://github.com/boa-dev/boa/pull/3786\n- Add functions to create modules from a JSON value by @hansl in https://github.com/boa-dev/boa/pull/3804\n- Add an embed_module!() macro to boa_interop by @hansl in https://github.com/boa-dev/boa/pull/3784\n- Add a ContextData struct to inject host defined types from the context by @hansl in https://github.com/boa-dev/boa/pull/3802\n- Implement object keys access by @HalidOdat in https://github.com/boa-dev/boa/pull/3832\n- Group dependabot updates by @jedel1043 in https://github.com/boa-dev/boa/pull/3863\n- Adding TryFromJs implementations for BTreeMap and HashMap by @hansl in https://github.com/boa-dev/boa/pull/3844\n- Adding TryFromJs implementations for tuples by @hansl in https://github.com/boa-dev/boa/pull/3843\n- Add a js_class to implement the Class trait without boilerplate by @hansl in https://github.com/boa-dev/boa/pull/3872\n- Implement lossless TryFromJs for integers from f64 by @HalidOdat in https://github.com/boa-dev/boa/pull/3907\n\n### Bug Fixes\n\n- Close for-of iterator when the loop body throws by @raskad in https://github.com/boa-dev/boa/pull/3734\n- Add default value handling for destructuring property access arrays by @raskad in https://github.com/boa-dev/boa/pull/3738\n- Fix invalid syntax errors for allowed `let` as variable names by @raskad in https://github.com/boa-dev/boa/pull/3743\n- Fix parsing of `async` in for-of loops by @raskad in https://github.com/boa-dev/boa/pull/3745\n- Fix parsing of binding identifier in try catch parameters by @raskad in https://github.com/boa-dev/boa/pull/3752\n- Add missing environment creation in initial iteration of for loop by @raskad in https://github.com/boa-dev/boa/pull/3751\n- chore: Update README link to reflect new site paths by @NickTomlin in https://github.com/boa-dev/boa/pull/3793\n- Fix order of `ToString` call in `Function` constructor by @HalidOdat in https://github.com/boa-dev/boa/pull/3820\n- Fix CI for nextest step by @jedel1043 in https://github.com/boa-dev/boa/pull/3862\n- Fix base objects in `with` statements by @raskad in https://github.com/boa-dev/boa/pull/3870\n- Fix boa cli history by @raskad in https://github.com/boa-dev/boa/pull/3875\n- Fix hashbang comments by using proper goal symbols by @raskad in https://github.com/boa-dev/boa/pull/3876\n- Fix AsyncGenerator to correctly handle `return` inside `then` by @jedel1043 in https://github.com/boa-dev/boa/pull/3879\n- Fix HomeObject for private class methods by @raskad in https://github.com/boa-dev/boa/pull/3897\n- Fix evaluation order in destructive property assignments by @raskad in https://github.com/boa-dev/boa/pull/3895\n\n### Internal Improvements\n\n- Apply new clippy lints for rustc 1.77 by @jedel1043 in https://github.com/boa-dev/boa/pull/3759\n- Change dependabot interval to weekly by @jedel1043 in https://github.com/boa-dev/boa/pull/3758\n- Dense array storage variants for `i32` and `f64` by @HalidOdat in https://github.com/boa-dev/boa/pull/3760\n- Optimize number to `PropertyKey` conversion by @HalidOdat in https://github.com/boa-dev/boa/pull/3769\n- don't run test262 on push by @jasonwilliams in https://github.com/boa-dev/boa/pull/3774\n- Check that `min <= max` in `clamp_finite` by @jedel1043 in https://github.com/boa-dev/boa/pull/3699\n- Decouple `Context` from `ByteCompiler` by @HalidOdat in https://github.com/boa-dev/boa/pull/3829\n- Implement latin1 encoded `JsString`s by @HalidOdat in https://github.com/boa-dev/boa/pull/3450\n- Replace `js_str` with `js_string` in examples by @getong in https://github.com/boa-dev/boa/pull/3836\n- Separate `JsString` into its own crate by @HalidOdat in https://github.com/boa-dev/boa/pull/3837\n- Bump temporal_rs to latest commit by @jedel1043 in https://github.com/boa-dev/boa/pull/3880\n- Remove `FormalParameterList` from `CodeBlock` by @HalidOdat in https://github.com/boa-dev/boa/pull/3882\n\n### Other Changes\n\n- Fix a few Duration code typos by @robot-head in https://github.com/boa-dev/boa/pull/3730\n- Add a try_from_js implementation for Vec<T> (accept any Array-like) by @hansl in https://github.com/boa-dev/boa/pull/3755\n- Swap to Duration::round from temporal_rs by @robot-head in https://github.com/boa-dev/boa/pull/3731\n- Cache `this` value by @HalidOdat in https://github.com/boa-dev/boa/pull/3771\n- Allow deserialization of missing objects properties into Option<> by @hansl in https://github.com/boa-dev/boa/pull/3767\n- Optimize Regex match check by @HalidOdat in https://github.com/boa-dev/boa/pull/3779\n- Add a boa_interop crate by @hansl in https://github.com/boa-dev/boa/pull/3772\n- Add a path to Module (and expose it in Referrer) by @hansl in https://github.com/boa-dev/boa/pull/3783\n- Properly resolve paths in SimpleModuleLoader and add path to Referrer::Script by @hansl in https://github.com/boa-dev/boa/pull/3791\n- Fix SimpleModuleLoader on Windows by @hansl in https://github.com/boa-dev/boa/pull/3795\n- Add more utility traits and funtions to boa_interop by @hansl in https://github.com/boa-dev/boa/pull/3773\n- Implement Promise.try() by @linusg in https://github.com/boa-dev/boa/pull/3800\n- Implement TryFromJs for Either<L, R> by @hansl in https://github.com/boa-dev/boa/pull/3822\n- Fix Rust 1.78.0 Clippy lints by @HalidOdat in https://github.com/boa-dev/boa/pull/3838\n- Switch from actions-rs/toolchain to dtolnay/rust-toolchain by @raskad in https://github.com/boa-dev/boa/pull/3845\n- Replace archived github actions from actions-rs by @raskad in https://github.com/boa-dev/boa/pull/3848\n- Add matrix badge and update communication to include matrix by @nekevss in https://github.com/boa-dev/boa/pull/3865\n- Add groupCollapsed by @leoflalv in https://github.com/boa-dev/boa/pull/3867\n- Bump ICU4X to 1.5 and cleanup Intl by @jedel1043 in https://github.com/boa-dev/boa/pull/3868\n- Update regress to v0.10.0 by @raskad in https://github.com/boa-dev/boa/pull/3869\n- Combine `HasProperty` and `Get` operations when possible by @raskad in https://github.com/boa-dev/boa/pull/3883\n- Remove some environment clones by @raskad in https://github.com/boa-dev/boa/pull/3884\n- Refactor call frame access to avoid panic checks by @raskad in https://github.com/boa-dev/boa/pull/3888\n- Remove `Temporal.Calendar` and `Temporal.TimeZone` by @jedel1043 in https://github.com/boa-dev/boa/pull/3890\n- Update Temporal rounding and implement additional methods by @nekevss in https://github.com/boa-dev/boa/pull/3892\n- format code in comments by @jasonwilliams in https://github.com/boa-dev/boa/pull/3902\n- Updates to temporal_rs version and temporal methods by @nekevss in https://github.com/boa-dev/boa/pull/3900\n- Patch regression from change to to-relative-to-object by @nekevss in https://github.com/boa-dev/boa/pull/3906\n- Add `get_unchecked` method to `JsString` and `JsStr` by @CrazyboyQCD in https://github.com/boa-dev/boa/pull/3898\n- bump gc threshold by @jasonwilliams in https://github.com/boa-dev/boa/pull/3908\n- update versions and ABOUT files by @jasonwilliams in https://github.com/boa-dev/boa/pull/3903\n- Cleanup README.md and contributor documentation by @jedel1043 in https://github.com/boa-dev/boa/pull/3909\n- Refactor environment stack to remove some panics by @raskad in https://github.com/boa-dev/boa/pull/3893\n\n### New Contributors\n\n- @robot-head made their first contribution in https://github.com/boa-dev/boa/pull/3730\n- @hansl made their first contribution in https://github.com/boa-dev/boa/pull/3755\n- @NickTomlin made their first contribution in https://github.com/boa-dev/boa/pull/3793\n- @linusg made their first contribution in https://github.com/boa-dev/boa/pull/3800\n- @getong made their first contribution in https://github.com/boa-dev/boa/pull/3836\n- @leoflalv made their first contribution in https://github.com/boa-dev/boa/pull/3867\n- @CrazyboyQCD made their first contribution in https://github.com/boa-dev/boa/pull/3898\n\n**Full Changelog**: https://github.com/boa-dev/boa/compare/v0.18...v0.19\n\n## [0.18.0 (2024-03-04)](https://github.com/boa-dev/boa/compare/v0.17...v0.18)\n\n### Feature Enhancements\n\n- Format let-else expressions by @jedel1043 in https://github.com/boa-dev/boa/pull/3102\n- Add regexp indices (`d` flag) support by @dirkdev98 in https://github.com/boa-dev/boa/pull/3094\n- Add missing 'unscopables' to `Array.prototype[@@unscopables]` by @dirkdev98 in https://github.com/boa-dev/boa/pull/3111\n- Updated Fuzzer dependencies and added them to Dependabot by @Razican in https://github.com/boa-dev/boa/pull/3124\n- Implement `findLast` and `findLastIndex` on TypedArray by @dirkdev98 in https://github.com/boa-dev/boa/pull/3135\n- Implement i128/u128 to JsBigInt conversions by @AlvinKuruvilla in https://github.com/boa-dev/boa/pull/3129\n- Implement `String.prototype.isWellFormed` and `String.prototype.toWellFormed` by @raskad in https://github.com/boa-dev/boa/pull/3187\n- Clarify usage section in `README.md` by @postmeback in https://github.com/boa-dev/boa/pull/3092\n- Log traces even without message (boa_runtime) by @kelbazz in https://github.com/boa-dev/boa/pull/3193\n- Implement ephemeron-based weak map by @jedel1043 in https://github.com/boa-dev/boa/pull/3052\n- Improve bytecompiler bytecode generation. by @HalidOdat in https://github.com/boa-dev/boa/pull/3188\n- Add `Instruction` and `InstructionIterator` by @HalidOdat in https://github.com/boa-dev/boa/pull/3201\n- Add ECMAScript 14 to `boa_tester` by @jedel1043 in https://github.com/boa-dev/boa/pull/3273\n- Bump `rust-version` to 1.71 by @jedel1043 in https://github.com/boa-dev/boa/pull/3290\n- Lazily download `test262` repository by @HalidOdat in https://github.com/boa-dev/boa/pull/3214\n- Implement `Gc::new_cyclic` by @jedel1043 in https://github.com/boa-dev/boa/pull/3292\n- Implement `Intl.PluralRules` by @jedel1043 in https://github.com/boa-dev/boa/pull/3298\n- Implement step 5 in `RegExp` constructor by @HalidOdat in https://github.com/boa-dev/boa/pull/3305\n- Replace #[deny] with #[warn] by @jedel1043 in https://github.com/boa-dev/boa/pull/3309\n- Bump ICU4X to 1.3 by @jedel1043 in https://github.com/boa-dev/boa/pull/3306\n- Migrate to workspace deps by @jedel1043 in https://github.com/boa-dev/boa/pull/3313\n- Implement `[[HostDefined]]` field on `Realm`s by @HalidOdat in https://github.com/boa-dev/boa/pull/2952\n- Introduce experimental features by @jedel1043 in https://github.com/boa-dev/boa/pull/3318\n- Introduce a `Class` map by @jedel1043 in https://github.com/boa-dev/boa/pull/3315\n- Fix `Function.prototype.toString()` by @HalidOdat in https://github.com/boa-dev/boa/pull/3374\n- First portion of the Temporal implementation by @nekevss in https://github.com/boa-dev/boa/pull/3277\n- Update feature flags to specific feature flag by @nekevss in https://github.com/boa-dev/boa/pull/3376\n- Implement `[[HostDefined]]` for `Module` and `Script` by @arexon in https://github.com/boa-dev/boa/pull/3381\n- Implement synthetic modules by @jedel1043 in https://github.com/boa-dev/boa/pull/3294\n- Prevent `test262` repository update if not needed by @HalidOdat in https://github.com/boa-dev/boa/pull/3386\n- Implement `SharedArrayBuffer` by @jedel1043 in https://github.com/boa-dev/boa/pull/3384\n- Add `Context::create_realm` by @johnyob in https://github.com/boa-dev/boa/pull/3369\n- Introduce a thread safe version of `JsError` by @jedel1043 in https://github.com/boa-dev/boa/pull/3398\n- Implement asynchronous evaluation of scripts by @jedel1043 in https://github.com/boa-dev/boa/pull/3044\n- Feature `get/set $boa.limits.stack` by @HalidOdat in https://github.com/boa-dev/boa/pull/3385\n- Implement `change-array-by-copy` methods by @jedel1043 in https://github.com/boa-dev/boa/pull/3412\n- Implement the `array-grouping` proposal by @jedel1043 in https://github.com/boa-dev/boa/pull/3420\n- Implement `Atomics` builtin by @jedel1043 in https://github.com/boa-dev/boa/pull/3394\n- Migrate to workspace lints by @jedel1043 in https://github.com/boa-dev/boa/pull/3334\n- Bump ICU4X to 1.4 and finish Intl impls with new APIs by @jedel1043 in https://github.com/boa-dev/boa/pull/3469\n- Class: Switch `make_data` parameter from `this` to `new_target` by @johnyob in https://github.com/boa-dev/boa/pull/3478\n- Add utility methods to the `Class` trait by @jedel1043 in https://github.com/boa-dev/boa/pull/3488\n- Simplify `Icu` API by @jedel1043 in https://github.com/boa-dev/boa/pull/3503\n- Add UTF-16 input parsing by @raskad in https://github.com/boa-dev/boa/pull/3538\n- Remove allocations from `HostDefined::get_many_mut` by @jedel1043 in https://github.com/boa-dev/boa/pull/3606\n- Implement getter for `ArrayBuffer` data by @HalidOdat in https://github.com/boa-dev/boa/pull/3610\n- Implement non-erased `JsObject`s by @jedel1043 in https://github.com/boa-dev/boa/pull/3618\n- Update regress to v0.8.0 and use UTF16 / UCS2 matching by @raskad in https://github.com/boa-dev/boa/pull/3627\n- Cleanup 262 tester and stabilize some experimental features by @jedel1043 in https://github.com/boa-dev/boa/pull/3632\n- Improve typing of `DataView` and related objects by @jedel1043 in https://github.com/boa-dev/boa/pull/3626\n- Close sync iterator when async wrapper yields rejection by @jedel1043 in https://github.com/boa-dev/boa/pull/3633\n- Implement resizable buffers by @jedel1043 in https://github.com/boa-dev/boa/pull/3634\n- Implement stage 3 feature \"arraybuffer-transfer\" by @jedel1043 in https://github.com/boa-dev/boa/pull/3649\n- Implement prototype of `NumberFormat` by @jedel1043 in https://github.com/boa-dev/boa/pull/3669\n- Add example for async module fetches by @jedel1043 in https://github.com/boa-dev/boa/pull/3012\n- Js typed array methods by @AngeloChecked in https://github.com/boa-dev/boa/pull/3481\n- Create tool to regenerate `ABOUT.md` by @jedel1043 in https://github.com/boa-dev/boa/pull/3692\n- Implement RegExp `v` flag by @raskad in https://github.com/boa-dev/boa/pull/3695\n\n### Bug Fixes\n\n- Allow escaped yield and await in labelled statement by @raskad in https://github.com/boa-dev/boa/pull/3117\n- `TypedArray.prototype.values()` and `TypedArray.prototype[@@iterator]` should be equal by @HalidOdat in https://github.com/boa-dev/boa/pull/3096\n- Fix TypedArrayConstructors tests by @raskad in https://github.com/boa-dev/boa/pull/3171\n- Close iterator after generator return call while array destructuring assignment by @HalidOdat in https://github.com/boa-dev/boa/pull/3164\n- Fix remaining TypedArray bugs by @raskad in https://github.com/boa-dev/boa/pull/3186\n- Add early errors for `LexicalDeclaration` by @raskad in https://github.com/boa-dev/boa/pull/3207\n- Fix switch statement `break` and `continue` return values by @raskad in https://github.com/boa-dev/boa/pull/3205\n- Fix GitHub coverage workflow by @HalidOdat in https://github.com/boa-dev/boa/pull/3288\n- Fix tagged template `this` in strict mode by @HalidOdat in https://github.com/boa-dev/boa/pull/3307\n- fix: add 'static lifetime by @mattsse in https://github.com/boa-dev/boa/pull/3297\n- Fix class inherit from `null` by @HalidOdat in https://github.com/boa-dev/boa/pull/3312\n- Fix anonymous function name in cover assignment by @raskad in https://github.com/boa-dev/boa/pull/3325\n- Add `NonMaxU32` as integer variant for `PropertyKey` by @raskad in https://github.com/boa-dev/boa/pull/3321\n- Add missing class name binding by @raskad in https://github.com/boa-dev/boa/pull/3328\n- Truncate environment stack on non-caught native error by @HalidOdat in https://github.com/boa-dev/boa/pull/3331\n- Fix regular expression construction by @HalidOdat in https://github.com/boa-dev/boa/pull/3338\n- Fix `super()` construction with default parameters by @HalidOdat in https://github.com/boa-dev/boa/pull/3339\n- Fix static class element evaluation order by @raskad in https://github.com/boa-dev/boa/pull/3327\n- Fix detection of runtime limits for accessors by @jedel1043 in https://github.com/boa-dev/boa/pull/3335\n- Fix `Number.prototype.toFixed()` by @HalidOdat in https://github.com/boa-dev/boa/pull/2898\n- Check `eval` realm before call by @HalidOdat in https://github.com/boa-dev/boa/pull/3375\n- Evaluate all parts of `class` in strict mode by @HalidOdat in https://github.com/boa-dev/boa/pull/3383\n- Fix var declaration for deleted binding locator by @raskad in https://github.com/boa-dev/boa/pull/3387\n- Fix await flag in class constructor by @raskad in https://github.com/boa-dev/boa/pull/3388\n- Fix compilation for targets without `AtomicU64` by @jedel1043 in https://github.com/boa-dev/boa/pull/3399\n- Update `regex.match` spec and code by @raskad in https://github.com/boa-dev/boa/pull/3462\n- `Context` independent `CodeBlock`s by @HalidOdat in https://github.com/boa-dev/boa/pull/3424\n- Fix a Parser Idempotency Issue by @veera-sivarajan in https://github.com/boa-dev/boa/pull/3172\n- Non recursive gc trace by @HalidOdat in https://github.com/boa-dev/boa/pull/3508\n- Fix invalid return value when closing an iterator by @raskad in https://github.com/boa-dev/boa/pull/3567\n- Implement Date parsing according to the spec by @raskad in https://github.com/boa-dev/boa/pull/3564\n- `Date` refactor by @raskad in https://github.com/boa-dev/boa/pull/3595\n- Fix regexp `toString` method by @raskad in https://github.com/boa-dev/boa/pull/3608\n- Fix escaping in `RegExp.prototype.source` by @raskad in https://github.com/boa-dev/boa/pull/3619\n- Fix line terminators in template strings by @raskad in https://github.com/boa-dev/boa/pull/3641\n- Consider strict + no-strict tests as a single test by @jedel1043 in https://github.com/boa-dev/boa/pull/3675\n- Preserve `.exe` suffix for Windows releases by @HalidOdat in https://github.com/boa-dev/boa/pull/3680\n\n### Internal Improvements\n\n- Move `RefCell` of `CompileTimeEnvironment`s to field `bindings` by @HalidOdat in https://github.com/boa-dev/boa/pull/3108\n- Change `name` field type in `CodeBlock` to `JsString` by @HalidOdat in https://github.com/boa-dev/boa/pull/3107\n- Refactor `Array.prototype.find*` and TypedArray variants to use `FindViaPredicate` by @dirkdev98 in https://github.com/boa-dev/boa/pull/3134\n- Fix 1.71.0 lints by @RageKnify in https://github.com/boa-dev/boa/pull/3140\n- Clippy updates: add panics and etc. by @nekevss in https://github.com/boa-dev/boa/pull/3235\n- Remove unused class environments by @raskad in https://github.com/boa-dev/boa/pull/3332\n- Improve highlighter performance by @jedel1043 in https://github.com/boa-dev/boa/pull/3341\n- Cleanup `get_option` and calls to the function by @jedel1043 in https://github.com/boa-dev/boa/pull/3355\n- Fix new lints for Rust 1.73 by @jedel1043 in https://github.com/boa-dev/boa/pull/3361\n- Refactor compile time environment handling by @raskad in https://github.com/boa-dev/boa/pull/3365\n- Update all dependencies by @jedel1043 in https://github.com/boa-dev/boa/pull/3400\n- Optimize `shift` for dense arrays by @jedel1043 in https://github.com/boa-dev/boa/pull/3405\n- Disallow changing type of already created objects by @jedel1043 in https://github.com/boa-dev/boa/pull/3410\n- Merge `CodeBlock` constant pools by @HalidOdat in https://github.com/boa-dev/boa/pull/3413\n- Move ordinary function `[[ConstructorKind]]` to `CodeBlock` by @HalidOdat in https://github.com/boa-dev/boa/pull/3439\n- Move `FunctionKind` to `CodeBlock` by @HalidOdat in https://github.com/boa-dev/boa/pull/3440\n- Unify generator and ordinary function creation by @HalidOdat in https://github.com/boa-dev/boa/pull/3441\n- Move `arguments` object creation to bytecode by @HalidOdat in https://github.com/boa-dev/boa/pull/3432\n- Move parameter environment creation to bytecode by @HalidOdat in https://github.com/boa-dev/boa/pull/3433\n- Prevent `DefVar` opcode emit for global binding by @HalidOdat in https://github.com/boa-dev/boa/pull/3453\n- Transition `Intl` types to `NativeObject` API by @jedel1043 in https://github.com/boa-dev/boa/pull/3491\n- Reduce `WeakGc<T>` memory usage by @HalidOdat in https://github.com/boa-dev/boa/pull/3492\n- Migrate `Temporal` to its own crate. by @nekevss in https://github.com/boa-dev/boa/pull/3461\n- Reestructure repo and CI improvements by @jedel1043 in https://github.com/boa-dev/boa/pull/3505\n- Move `PromiseCapability` to stack by @HalidOdat in https://github.com/boa-dev/boa/pull/3528\n- Fix rust 1.75 lints by @raskad in https://github.com/boa-dev/boa/pull/3540\n- Remove double indirection in module types by @jedel1043 in https://github.com/boa-dev/boa/pull/3640\n- Fix clippy warnings for rustc 1.76 by @jedel1043 in https://github.com/boa-dev/boa/pull/3668\n- Migrate to `temporal_rs` crate by @nekevss in https://github.com/boa-dev/boa/pull/3694\n\n### Other Changes\n\n- Removed time 0.1 dependency, updated dependencies by @Razican in https://github.com/boa-dev/boa/pull/3122\n- Add new CLI options to usage in README by @Razican in https://github.com/boa-dev/boa/pull/3123\n- Find roots when running GC rather than runtime by @tunz in https://github.com/boa-dev/boa/pull/3109\n- Re-enable must_use clippy rule by @tunz in https://github.com/boa-dev/boa/pull/3180\n- Refactor environment, exception handling and jumping in VM by @HalidOdat in https://github.com/boa-dev/boa/pull/3059\n- Refactor `Context::run()` method by @HalidOdat in https://github.com/boa-dev/boa/pull/3179\n- Added examples by @postmeback in https://github.com/boa-dev/boa/pull/3141\n- Use main stack for calling ordinary functions by @HalidOdat in https://github.com/boa-dev/boa/pull/3185\n- Update license field following SPDX 2.1 license expression standard by @frisoft in https://github.com/boa-dev/boa/pull/3209\n- Store active runnable and active function in `CallFrame` by @HalidOdat in https://github.com/boa-dev/boa/pull/3197\n- Added MSRV check by @Razican in https://github.com/boa-dev/boa/pull/3291\n- Reintroduce publish CI job by @jedel1043 in https://github.com/boa-dev/boa/pull/3308\n- Format code snippets in docs by @jedel1043 in https://github.com/boa-dev/boa/pull/3317\n- Remove direct conversion from `&str` to `JsValue`/`PropertyKey`. by @jedel1043 in https://github.com/boa-dev/boa/pull/3319\n- `icu_properties` default features to true by @nekevss in https://github.com/boa-dev/boa/pull/3326\n- Varying length instruction operands by @HalidOdat in https://github.com/boa-dev/boa/pull/3253\n- Improve CI testing by @jedel1043 in https://github.com/boa-dev/boa/pull/3333\n- Refactor function internal methods by @HalidOdat in https://github.com/boa-dev/boa/pull/3322\n- Make environments opcodes use varying operands by @HalidOdat in https://github.com/boa-dev/boa/pull/3340\n- Bump test262 by @jedel1043 in https://github.com/boa-dev/boa/pull/3349\n- Refactor ordinary VM calling by @HalidOdat in https://github.com/boa-dev/boa/pull/3295\n- Fix Array.join when the array contains itself by @ahaoboy in https://github.com/boa-dev/boa/pull/3406\n- Rename master workflow to main by @Razican in https://github.com/boa-dev/boa/pull/3409\n- Cleaned up a couple of Github action warnings by @Razican in https://github.com/boa-dev/boa/pull/3417\n- Temporal duration update and cleanup by @nekevss in https://github.com/boa-dev/boa/pull/3443\n- Progress on Duration's round/total method updates by @nekevss in https://github.com/boa-dev/boa/pull/3451\n- Simplify all extensions APIs of `Context` by @jedel1043 in https://github.com/boa-dev/boa/pull/3456\n- `[[HostDefined]]` Improvements by @johnyob in https://github.com/boa-dev/boa/pull/3460\n- Make well_known_symbols functions pub by @tj825 in https://github.com/boa-dev/boa/pull/3465\n- Use `Vec<T>` for keeping track of gc objects by @HalidOdat in https://github.com/boa-dev/boa/pull/3493\n- Implement `Inline Caching` by @HalidOdat in https://github.com/boa-dev/boa/pull/2767\n- Migrate `ISO8601` parsing to `boa_temporal` by @nekevss in https://github.com/boa-dev/boa/pull/3500\n- Implement erased objects by @jedel1043 in https://github.com/boa-dev/boa/pull/3494\n- Build out ZonedDateTime, TimeZone, and Instant by @nekevss in https://github.com/boa-dev/boa/pull/3497\n- `boa_temporal` structure changes and docs update by @nekevss in https://github.com/boa-dev/boa/pull/3504\n- Refactor vm calling convention to allow locals by @HalidOdat in https://github.com/boa-dev/boa/pull/3496\n- Temporal Parser Cleanup/Fixes by @nekevss in https://github.com/boa-dev/boa/pull/3521\n- Refactor Temporal Calendar API for `AnyCalendar` and fields by @nekevss in https://github.com/boa-dev/boa/pull/3522\n- Update `boa_temporal` Time Zone design by @nekevss in https://github.com/boa-dev/boa/pull/3543\n- Implement `DifferenceInstant` and related refactor by @nekevss in https://github.com/boa-dev/boa/pull/3568\n- Run `cargo update` on fuzz crate by @jedel1043 in https://github.com/boa-dev/boa/pull/3607\n- Temporal `Instant` migration cont. and related changes by @nekevss in https://github.com/boa-dev/boa/pull/3601\n- Temporal: Update `Date` builtin with `boa_temporal` and fixes by @nekevss in https://github.com/boa-dev/boa/pull/3614\n- Temporal: Build out `Time` and its methods by @nekevss in https://github.com/boa-dev/boa/pull/3613\n- Temporal: Enable temporal tests by @nekevss in https://github.com/boa-dev/boa/pull/3620\n- Fix tests results upload by @raskad in https://github.com/boa-dev/boa/pull/3635\n- Temporal: `DateTime` and `PlainDateTime` functionality by @nekevss in https://github.com/boa-dev/boa/pull/3628\n- Temporal: Initial `PlainTime` build out by @nekevss in https://github.com/boa-dev/boa/pull/3621\n- Ignore `Cargo.lock` in fuzzer by @jedel1043 in https://github.com/boa-dev/boa/pull/3636\n- Temporal: attribute/property and custom calendar fixes by @nekevss in https://github.com/boa-dev/boa/pull/3639\n- Docs: Update boa's main README.md by @nekevss in https://github.com/boa-dev/boa/pull/3650\n- Bump time from 0.3.31 to 0.3.33 by @jedel1043 in https://github.com/boa-dev/boa/pull/3652\n- Temporal: Refactor Calendar protocol for `JsObject`s by @nekevss in https://github.com/boa-dev/boa/pull/3651\n- Simplify Temporal APIs by @jedel1043 in https://github.com/boa-dev/boa/pull/3653\n- Implement inline caching tests and cleanup by @HalidOdat in https://github.com/boa-dev/boa/pull/3513\n- Docs: Update README.md and add `boa_cli`'s README.md by @nekevss in https://github.com/boa-dev/boa/pull/3659\n- Change `HostEnsureCanCompileStrings` to the new spec by @jedel1043 in https://github.com/boa-dev/boa/pull/3690\n- Split ICU4X data generation from `boa_icu_provider` by @jedel1043 in https://github.com/boa-dev/boa/pull/3682\n- Add a catch all for other categories not labelled by @jasonwilliams in https://github.com/boa-dev/boa/pull/3703\n- Fix `temporal_rs` in Cargo.toml by @nekevss in https://github.com/boa-dev/boa/pull/3702\n\n## New Contributors\n\n- @AlvinKuruvilla made their first contribution in https://github.com/boa-dev/boa/pull/3129\n- @tunz made their first contribution in https://github.com/boa-dev/boa/pull/3109\n- @postmeback made their first contribution in https://github.com/boa-dev/boa/pull/3092\n- @kelbazz made their first contribution in https://github.com/boa-dev/boa/pull/3193\n- @frisoft made their first contribution in https://github.com/boa-dev/boa/pull/3209\n- @mattsse made their first contribution in https://github.com/boa-dev/boa/pull/3297\n- @arexon made their first contribution in https://github.com/boa-dev/boa/pull/3381\n- @johnyob made their first contribution in https://github.com/boa-dev/boa/pull/3369\n- @ahaoboy made their first contribution in https://github.com/boa-dev/boa/pull/3406\n- @tj825 made their first contribution in https://github.com/boa-dev/boa/pull/3465\n- @AngeloChecked made their first contribution in https://github.com/boa-dev/boa/pull/3481\n\n**Full Changelog**: https://github.com/boa-dev/boa/compare/v0.17...v0.18\n\n## [0.17.0 (2023-07-05)](https://github.com/boa-dev/boa/compare/v0.16...v0.17)\n\n### Feature Enhancements\n\n- Implement `new.target` expression by @raskad in [#2299](https://github.com/boa-dev/boa/pull/2299)\n- Parse static async private methods in classes by @raskad in [#2315](https://github.com/boa-dev/boa/pull/2315)\n- Implement `JsDataView` by @nekevss in [#2308](https://github.com/boa-dev/boa/pull/2308)\n- Upgrade clap to 4.0, add value hints for zsh and fish by @Razican in [#2336](https://github.com/boa-dev/boa/pull/2336)\n- Implement `JsRegExp` by @nekevss in [#2326](https://github.com/boa-dev/boa/pull/2326)\n- Create new lazy Error type by @jedel1043 in [#2283](https://github.com/boa-dev/boa/pull/2283)\n- Fixed some documentation and clippy warnings in tests by @Razican in [#2362](https://github.com/boa-dev/boa/pull/2362)\n- Removed the \"VM Implementation\" headline for Test262 by @Razican in [#2364](https://github.com/boa-dev/boa/pull/2364)\n- Modified the `loadfile` example to show how Boa can read bytes by @Razican in [#2363](https://github.com/boa-dev/boa/pull/2363)\n- Implement `LabelledStatement` by @jedel1043 in [#2349](https://github.com/boa-dev/boa/pull/2349)\n- Split vm/opcode into modules by @nekevss in [#2343](https://github.com/boa-dev/boa/pull/2343)\n- Removed some duplicate code, added `ToIndentedString` by @Razican in [#2367](https://github.com/boa-dev/boa/pull/2367)\n- Document the AST by @jedel1043 in [#2377](https://github.com/boa-dev/boa/pull/2377)\n- Implement member accessors in initializer of for loops by @jedel1043 in [#2381](https://github.com/boa-dev/boa/pull/2381)\n- Implement `JsGenerator` and wrapper docs clean up by @nekevss in [#2380](https://github.com/boa-dev/boa/pull/2380)\n- Add named evaluation of logical assignments by @raskad in [#2389](https://github.com/boa-dev/boa/pull/2389)\n- Implement optional chains by @jedel1043 in [#2390](https://github.com/boa-dev/boa/pull/2390)\n- Implement delete for references by @jedel1043 in [#2395](https://github.com/boa-dev/boa/pull/2395)\n- Implement AST Visitor pattern (attempt #3) by @addisoncrump in [#2392](https://github.com/boa-dev/boa/pull/2392)\n- Implement async arrow functions by @raskad in [#2393](https://github.com/boa-dev/boa/pull/2393)\n- Pretty print promise objects by @jedel1043 in [#2407](https://github.com/boa-dev/boa/pull/2407)\n- Parser Idempotency Fuzzer by @addisoncrump in [#2400](https://github.com/boa-dev/boa/pull/2400)\n- Safe wrapper for `JsDate` by @anuvratsingh in [#2181](https://github.com/boa-dev/boa/pull/2181)\n- Fix some Date tests by @jedel1043 in [#2431](https://github.com/boa-dev/boa/pull/2431)\n- Boa Gc implementation draft by @nekevss in [#2394](https://github.com/boa-dev/boa/pull/2394)\n- VM Fuzzer by @addisoncrump in [#2401](https://github.com/boa-dev/boa/pull/2401)\n- Implement the `WeakRef` builtin by @jedel1043 in [#2438](https://github.com/boa-dev/boa/pull/2438)\n- Refactor the `Date` builtin by @jedel1043 in [#2449](https://github.com/boa-dev/boa/pull/2449)\n- Implement instruction flowgraph generator by @HalidOdat in [#2422](https://github.com/boa-dev/boa/pull/2422)\n- `JsArrayBuffer` take method and docs by @nekevss in [#2454](https://github.com/boa-dev/boa/pull/2454)\n- Set function names in object literal methods by @raskad in [#2460](https://github.com/boa-dev/boa/pull/2460)\n- Redesign Intl API and implement some services by @jedel1043 in [#2478](https://github.com/boa-dev/boa/pull/2478)\n- Cleanup `Context` APIs by @jedel1043 in [#2504](https://github.com/boa-dev/boa/pull/2504)\n- Prepare `Promises` for new host hooks and job queue API by @jedel1043 in [#2528](https://github.com/boa-dev/boa/pull/2528)\n- Implement host hooks and job queues APIs by @jedel1043 in [#2529](https://github.com/boa-dev/boa/pull/2529)\n- First batch of `no_std` support for some sub-crates by @jedel1043 in [#2544](https://github.com/boa-dev/boa/pull/2544)\n- Create `Source` to abstract JS code sources by @jedel1043 in [#2579](https://github.com/boa-dev/boa/pull/2579)\n- Move increment and decrement operations to `Update` expression by @raskad in [#2565](https://github.com/boa-dev/boa/pull/2565)\n- Implement binary `in` operation with private names by @raskad in [#2582](https://github.com/boa-dev/boa/pull/2582)\n- Module parsing by @Razican in [#2411](https://github.com/boa-dev/boa/pull/2411)\n- Implement `WeakSet` by @lupd in [#2586](https://github.com/boa-dev/boa/pull/2586)\n- Implement `WeakMap` by @raskad in [#2597](https://github.com/boa-dev/boa/pull/2597)\n- API to construct a `NativeFunction` from a native async function by @jedel1043 in [#2542](https://github.com/boa-dev/boa/pull/2542)\n- Add `--strict` flag to cli by @HalidOdat in [#2689](https://github.com/boa-dev/boa/pull/2689)\n- Add timeout to CI by @HalidOdat in [#2691](https://github.com/boa-dev/boa/pull/2691)\n- Add ES5 and ES6 Conformance calculation to boa_tester by @ZackMitkin in [#2690](https://github.com/boa-dev/boa/pull/2690)\n- Improve tester display for multiple editions by @jedel1043 in [#2720](https://github.com/boa-dev/boa/pull/2720)\n- Implement `JsPromise` wrapper by @jedel1043 in [#2758](https://github.com/boa-dev/boa/pull/2758)\n- Initial version of a JS -> Rust conversion trait. by @Razican in [#2276](https://github.com/boa-dev/boa/pull/2276)\n- Implement `escape` and `unescape` by @jedel1043 in [#2768](https://github.com/boa-dev/boa/pull/2768)\n- Implement debug object for CLI by @HalidOdat in [#2772](https://github.com/boa-dev/boa/pull/2772)\n- Make `Realm` shareable between functions by @jedel1043 in [#2801](https://github.com/boa-dev/boa/pull/2801)\n- Implement Annex-B string html methods by @HalidOdat in [#2798](https://github.com/boa-dev/boa/pull/2798)\n- Implement annex-b `trimLeft` and `trimRight` string methods by @HalidOdat in [#2806](https://github.com/boa-dev/boa/pull/2806)\n- Implement HTML comments and gate behind the `annex-b` feature by @jedel1043 in [#2817](https://github.com/boa-dev/boa/pull/2817)\n- Implement `Intl.Segmenter` by @jedel1043 in [#2840](https://github.com/boa-dev/boa/pull/2840)\n- Implement var initializers in for-in loops by @jedel1043 in [#2842](https://github.com/boa-dev/boa/pull/2842)\n- Improve debug output of `JsNativeError` and `Realm` by @jedel1043 in [#2894](https://github.com/boa-dev/boa/pull/2894)\n- Implement runtime limits for loops by @HalidOdat in [#2857](https://github.com/boa-dev/boa/pull/2857)\n- Implement runtime limits for recursion by @HalidOdat in [#2904](https://github.com/boa-dev/boa/pull/2904)\n- Implement annexB Block-Level Function Declarations by @raskad in [#2910](https://github.com/boa-dev/boa/pull/2910)\n- Implement module execution by @jedel1043 in [#2922](https://github.com/boa-dev/boa/pull/2922)\n- Type safe root shape by @HalidOdat in [#2940](https://github.com/boa-dev/boa/pull/2940)\n- Implement dynamic imports by @jedel1043 in [#2932](https://github.com/boa-dev/boa/pull/2932)\n- Implement pseudo-property `import.meta` by @jedel1043 in [#2956](https://github.com/boa-dev/boa/pull/2956)\n- Implement `with` and object environments by @raskad in [#2692](https://github.com/boa-dev/boa/pull/2692)\n- Add hooks to get the current time and timezone by @jedel1043 in [#2824](https://github.com/boa-dev/boa/pull/2824)\n- Make `JsSymbol` thread-safe by @jedel1043 in [#2539](https://github.com/boa-dev/boa/pull/2539)\n- Added a Boa runtime by @Razican in [#2743](https://github.com/boa-dev/boa/pull/2743)\n- Implement `TryFromJs` for `JsObject` wrappers by @Razican in [#2809](https://github.com/boa-dev/boa/pull/2809)\n- Allow passing owned `HostHooks` and `JobQueues` to `Context` by @jedel1043 in [#2811](https://github.com/boa-dev/boa/pull/2811)\n- Implement `String.prototype.toLocaleUpper/LowerCase` by @jedel1043 in [#2822](https://github.com/boa-dev/boa/pull/2822)\n- Implement constant folding optimization by @HalidOdat in [#2679](https://github.com/boa-dev/boa/pull/2679)\n- Implement `Hidden classes` by @HalidOdat in [#2723](https://github.com/boa-dev/boa/pull/2723)\n- show object kind, name and address when using dbg! by @jasonwilliams in [#2960](https://github.com/boa-dev/boa/pull/2960)\n- Add convenience methods to `ModuleLoader` by @jedel1043 in [#3007](https://github.com/boa-dev/boa/pull/3007)\n- Allow `JobQueue` to concurrently run jobs by @jedel1043 in [#3036](https://github.com/boa-dev/boa/pull/3036)\n- Make `IntegerIndexed::byte_offset` public by @CryZe in [#3017](https://github.com/boa-dev/boa/pull/3017)\n- Allow awaiting `JsPromise` from Rust code by @jedel1043 in [#3011](https://github.com/boa-dev/boa/pull/3011)\n- Cache `cargo-tarpaulin` binary by @jedel1043 in [#3071](https://github.com/boa-dev/boa/pull/3071)\n- add link to the main logo by @jasonwilliams in [#3082](https://github.com/boa-dev/boa/pull/3082)\n\n### Bug Fixes\n\n- Add unicode terminator to line comment by @creampnx-x in [#2301](https://github.com/boa-dev/boa/pull/2301)\n- Fix function property order by @raskad in [#2305](https://github.com/boa-dev/boa/pull/2305)\n- Fix some Array spec deviations by @raskad in [#2306](https://github.com/boa-dev/boa/pull/2306)\n- Fix double conversion to primitive in `ToNumeric` by @raskad in [#2310](https://github.com/boa-dev/boa/pull/2310)\n- Fixing the output for the test diffs in PRs by @Razican in [#2320](https://github.com/boa-dev/boa/pull/2320)\n- Fix Regex literal parsing in MemberExpression by @tunz in [#2328](https://github.com/boa-dev/boa/pull/2328)\n- Fix error in `Proxy` set implementation by @raskad in [#2369](https://github.com/boa-dev/boa/pull/2369)\n- Allow LineTerminator before Semicolon in `continue` by @raskad in [#2371](https://github.com/boa-dev/boa/pull/2371)\n- Fix var collisions in strict eval calls by @jedel1043 in [#2382](https://github.com/boa-dev/boa/pull/2382)\n- Set `in` to `true` when parsing AssignmentExpression in ConditionalExpression by @raskad in [#2386](https://github.com/boa-dev/boa/pull/2386)\n- Skip prototype field definition for arrow function by @raskad in [#2388](https://github.com/boa-dev/boa/pull/2388)\n- Remove invalid optimization in addition by @raskad in [#2387](https://github.com/boa-dev/boa/pull/2387)\n- Fix order dependent execution in assignment. by @HalidOdat in [#2378](https://github.com/boa-dev/boa/pull/2378)\n- Add early error for `yield` in `GeneratorExpression` parameters by @raskad in [#2413](https://github.com/boa-dev/boa/pull/2413)\n- Handle `__proto__` fields in object literals by @raskad in [#2423](https://github.com/boa-dev/boa/pull/2423)\n- Fix built-ins/Array/prototype/toString/non-callable-join-string-tag.js test case by @akavi in [#2458](https://github.com/boa-dev/boa/pull/2458)\n- Fix `PartialEq` for `JsBigInt` and `f64` by @raskad in [#2461](https://github.com/boa-dev/boa/pull/2461)\n- Allow class expressions without identifier by @raskad in [#2464](https://github.com/boa-dev/boa/pull/2464)\n- Fix to weak_trace for `boa_tester` by @nekevss in [#2470](https://github.com/boa-dev/boa/pull/2470)\n- Fix unary operations on `this` by @veera-sivarajan in [#2507](https://github.com/boa-dev/boa/pull/2507)\n- Fix postfix operator line terminator parsing by @raskad in [#2520](https://github.com/boa-dev/boa/pull/2520)\n- Remove `Literal::Undefined` by @veera-sivarajan in [#2518](https://github.com/boa-dev/boa/pull/2518)\n- Add early errors for 'eval' or 'arguments' in parameters by @raskad in [#2515](https://github.com/boa-dev/boa/pull/2515)\n- Pass a receiver value in property getter opcodes by @raskad in [#2516](https://github.com/boa-dev/boa/pull/2516)\n- Add regex literal early errors by @raskad in [#2517](https://github.com/boa-dev/boa/pull/2517)\n- `Break` Opcode and `ByteCompiler` changes by @nekevss in [#2523](https://github.com/boa-dev/boa/pull/2523)\n- Refactor some class features by @raskad in [#2513](https://github.com/boa-dev/boa/pull/2513)\n- Recognize Directive Prologues correctly by @raskad in [#2521](https://github.com/boa-dev/boa/pull/2521)\n- Correctly parse consecutive semicolons by @raskad in [#2533](https://github.com/boa-dev/boa/pull/2533)\n- Fix some HoistableDeclaration parsing errors by @raskad in [#2532](https://github.com/boa-dev/boa/pull/2532)\n- Return the correct value from a statement list by @raskad in [#2554](https://github.com/boa-dev/boa/pull/2554)\n- Fix error for static class methods named `prototype` by @raskad in [#2552](https://github.com/boa-dev/boa/pull/2552)\n- Avoid creating `prototype` property on methods by @raskad in [#2553](https://github.com/boa-dev/boa/pull/2553)\n- Fix double property access on assignment ops by @raskad in [#2551](https://github.com/boa-dev/boa/pull/2551)\n- Add early errors for escaped identifiers by @raskad in [#2546](https://github.com/boa-dev/boa/pull/2546)\n- Fix failing collator tests by @jedel1043 in [#2575](https://github.com/boa-dev/boa/pull/2575)\n- fuzzer: bubble up NoInstructionsRemain error instead of trying to handle as exception by @Mrmaxmeier in [#2566](https://github.com/boa-dev/boa/pull/2566)\n- Try-catch-block control flow fix/refactor by @nekevss in [#2568](https://github.com/boa-dev/boa/pull/2568)\n- Fix doc tests and add CI check by @jedel1043 in [#2606](https://github.com/boa-dev/boa/pull/2606)\n- Fix string to number conversion for `infinity` by @raskad in [#2607](https://github.com/boa-dev/boa/pull/2607)\n- Fix exponent operator by @HalidOdat in [#2681](https://github.com/boa-dev/boa/pull/2681)\n- Update `README.md` cli options by @HalidOdat in [#2678](https://github.com/boa-dev/boa/pull/2678)\n- Fix incorrect `Number.MIN_VALUE` value by @HalidOdat in [#2682](https://github.com/boa-dev/boa/pull/2682)\n- Correctly run async tests by @jedel1043 in [#2683](https://github.com/boa-dev/boa/pull/2683)\n- Fix value to bigint conversion by @HalidOdat in [#2688](https://github.com/boa-dev/boa/pull/2688)\n- Fix Object constructor by @raskad in [#2694](https://github.com/boa-dev/boa/pull/2694)\n- Fix get function opcode traces by @HalidOdat in [#2708](https://github.com/boa-dev/boa/pull/2708)\n- Add early errors to dynamic function constructors by @raskad in [#2716](https://github.com/boa-dev/boa/pull/2716)\n- Add negative zero handling for `Map.delete` by @raskad in [#2726](https://github.com/boa-dev/boa/pull/2726)\n- Fix remaining `Set` tests by @raskad in [#2725](https://github.com/boa-dev/boa/pull/2725)\n- Fix update expressions getting values multiple times by @raskad in [#2733](https://github.com/boa-dev/boa/pull/2733)\n- Make if statements return their completion values by @raskad in [#2739](https://github.com/boa-dev/boa/pull/2739)\n- Fix super call execution order by @raskad in [#2724](https://github.com/boa-dev/boa/pull/2724)\n- Fix deserialization of `SpecEdition` by @jedel1043 in [#2762](https://github.com/boa-dev/boa/pull/2762)\n- Add `json-parse-with-source` feature to `boa_tester` by @HalidOdat in [#2778](https://github.com/boa-dev/boa/pull/2778)\n- Fix `Symbol.prototype[@@iterator]` by @HalidOdat in [#2800](https://github.com/boa-dev/boa/pull/2800)\n- Fix `String.prototype.replace()` order of `ToString` execution by @HalidOdat in [#2799](https://github.com/boa-dev/boa/pull/2799)\n- Fix `ThrowTypeError` intrinsic by @HalidOdat in [#2797](https://github.com/boa-dev/boa/pull/2797)\n- Fix `String.prototype.substr()` by @HalidOdat in [#2805](https://github.com/boa-dev/boa/pull/2805)\n- Fix destructive for-of loop assignments by @raskad in [#2803](https://github.com/boa-dev/boa/pull/2803)\n- Fix `TypedArray`s minus zero key by @HalidOdat in [#2808](https://github.com/boa-dev/boa/pull/2808)\n- Fix sync generator yield expressions by @raskad in [#2838](https://github.com/boa-dev/boa/pull/2838)\n- Fix async generators by @raskad in [#2853](https://github.com/boa-dev/boa/pull/2853)\n- Catch 'eval' and 'arguments' in setter method parameter by @raskad in [#2858](https://github.com/boa-dev/boa/pull/2858)\n- Fix `PropertyKey` index parse by @HalidOdat in [#2843](https://github.com/boa-dev/boa/pull/2843)\n- Fix `Date.prototype[Symbol.primitive]` incorrect attributes by @HalidOdat in [#2862](https://github.com/boa-dev/boa/pull/2862)\n- Allow `Date` object to store invalid `NativeDateTime` by @HalidOdat in [#2861](https://github.com/boa-dev/boa/pull/2861)\n- Fix panic when calling toString with radix by @HalidOdat in [#2863](https://github.com/boa-dev/boa/pull/2863)\n- Fix incorrect `LoopContinue` instruction in while-do loops by @HalidOdat in [#2866](https://github.com/boa-dev/boa/pull/2866)\n- Initialize `var` bindings in runtime environments with `undefined` by @raskad in [#2860](https://github.com/boa-dev/boa/pull/2860)\n- Bugfix/new.target is not understood by the parser as an expression #2793 by @projectnoa in [#2878](https://github.com/boa-dev/boa/pull/2878)\n- Fix `RegExp` constructor return value when pattern is a regexp by @HalidOdat in [#2880](https://github.com/boa-dev/boa/pull/2880)\n- `RegExp` constructor should call `IsRegExp()` by @HalidOdat in [#2881](https://github.com/boa-dev/boa/pull/2881)\n- Fix `for-of` expression parsing by @HalidOdat in [#2882](https://github.com/boa-dev/boa/pull/2882)\n- Disallow strict directives with escaped sequences by @jedel1043 in [#2892](https://github.com/boa-dev/boa/pull/2892)\n- Make `typeof` throw when accessing uninitialized variables by @raskad in [#2902](https://github.com/boa-dev/boa/pull/2902)\n- Fix wrong name of `Function.prototype[Symbol.hasInstance]` by @raskad in [#2905](https://github.com/boa-dev/boa/pull/2905)\n- Fix remaining object literal tests by @raskad in [#2906](https://github.com/boa-dev/boa/pull/2906)\n- Add SyntaxErrors in GlobalDeclarationInstantiation by @raskad in [#2908](https://github.com/boa-dev/boa/pull/2908)\n- Fix switch `default` execution by @HalidOdat in [#2907](https://github.com/boa-dev/boa/pull/2907)\n- Add loop and switch return values by @raskad in [#2828](https://github.com/boa-dev/boa/pull/2828)\n- Allow escaped `let` as expression by @HalidOdat in [#2916](https://github.com/boa-dev/boa/pull/2916)\n- Allow `let` name in for-in loop in non-strict mode by @HalidOdat in [#2915](https://github.com/boa-dev/boa/pull/2915)\n- Fix lexical environments in for loops by @raskad in [#2917](https://github.com/boa-dev/boa/pull/2917)\n- Fix `GetSubstitution` by @HalidOdat in [#2933](https://github.com/boa-dev/boa/pull/2933)\n- Allow escaped `async` as binding name by @jedel1043 in [#2936](https://github.com/boa-dev/boa/pull/2936)\n- Fix tagged template creation by @raskad in [#2925](https://github.com/boa-dev/boa/pull/2925)\n- Implement Private Runtime Environments by @raskad in [#2929](https://github.com/boa-dev/boa/pull/2929)\n- Fix remaining ES5 `built-ins/RegExp` tests by @HalidOdat in [#2957](https://github.com/boa-dev/boa/pull/2957)\n- Fix remaining static module bugs by @jedel1043 in [#2955](https://github.com/boa-dev/boa/pull/2955)\n- Deny Unicode Escapes in boolean and null expressions by @veera-sivarajan in [#2931](https://github.com/boa-dev/boa/pull/2931)\n- Fix `Date` for dynamic timezones by @jedel1043 in [#2877](https://github.com/boa-dev/boa/pull/2877)\n- Fix ES5 selector by @veera-sivaraja in [#2924](https://github.com/boa-dev/boa/pull/2924)\n- Labelled ByteCompiler Fix by @nekevss in [#2534](https://github.com/boa-dev/boa/pull/2534)\n- Fix verbose test display by @jedel1043 in [#2731](https://github.com/boa-dev/boa/pull/2731)\n- Fix WASM playground by @jedel1043 in [#2992](https://github.com/boa-dev/boa/pull/2992)\n- Correctly initialize functions inside modules by @jedel1043 in [#2993](https://github.com/boa-dev/boa/pull/2993)\n- Allow `true`, `false` and `null` in object patterns by @jedel1043 in [#2994](https://github.com/boa-dev/boa/pull/2994)\n- Fix panic in optional expressions with private identifiers by @raskad in [#2995](https://github.com/boa-dev/boa/pull/2995)\n- Fix prompt on windows by @ShaneEverittM in [#2986](https://github.com/boa-dev/boa/pull/2986)\n- Fix panic in constructor call by @raskad in [#3001](https://github.com/boa-dev/boa/pull/3001)\n- Unify async iterators and iterators compilation by @jedel1043 in [#2976](https://github.com/boa-dev/boa/pull/2976)\n- Correctly parse `yield import(..)` expressions by @jedel1043 in [#3006](https://github.com/boa-dev/boa/pull/3006)\n- Return the correct value during a labelled break by @raskad in [#2996](https://github.com/boa-dev/boa/pull/2996)\n- Fix panics on empty return values by @raskad in [#3018](https://github.com/boa-dev/boa/pull/3018)\n- Add early error for `await` in class static blocks by @raskad in [#3019](https://github.com/boa-dev/boa/pull/3019)\n- Fix class constructor return value by @raskad in [#3028](https://github.com/boa-dev/boa/pull/3028)\n- Fix super property access by @raskad in [#3026](https://github.com/boa-dev/boa/pull/3026)\n- Skip reversing arguments in SuperCallDerived by @dirkdev98 in [#3062](https://github.com/boa-dev/boa/pull/3062)\n- Mark header of rooted ephemerons when tracing by @jedel1043 in [#3049](https://github.com/boa-dev/boa/pull/3049)\n- Copy `ABOUT.md` file to all published crates by @jedel1043 in [#3074](https://github.com/boa-dev/boa/pull/3074)\n- Correctly handle finally..loop..break by @dirkdev98 in [#3073](https://github.com/boa-dev/boa/pull/3073)\n\n### Internal Improvements\n\n- Direct conversion from `u8` to `Opcode` by @HalidOdat [#2951](https://github.com/boa-dev/boa/pull/2951)\n- Fix links in readme by @raskad in [#2304](https://github.com/boa-dev/boa/pull/2304)\n- Switch to workspace inherited properties by @jedel1043 in [#2297](https://github.com/boa-dev/boa/pull/2297)\n- Separate JsObjectType implementors to their own module by @CalliEve in [#2324](https://github.com/boa-dev/boa/pull/2324)\n- First prototype for new `JsString` using UTF-16 by @jedel1043 in [#1659](https://github.com/boa-dev/boa/pull/1659)\n- Split `Node` into `Statement`, `Expression` and `Declaration` by @jedel1043 in [#2319](https://github.com/boa-dev/boa/pull/2319)\n- Changes neccesary -> necessary by @nekevss in [#2370](https://github.com/boa-dev/boa/pull/2370)\n- Cleanup and speed-up CI by @RageKnify in [#2376](https://github.com/boa-dev/boa/pull/2376)\n- Reduce documentation size in blog by @jedel1043 in [#2383](https://github.com/boa-dev/boa/pull/2383)\n- Generate `Opcode` impl using macro by @jedel1043 in [#2391](https://github.com/boa-dev/boa/pull/2391)\n- Extract the ast to a crate by @jedel1043 in [#2402](https://github.com/boa-dev/boa/pull/2402)\n- Replace `contains` and friends with visitors by @jedel1043 in [#2403](https://github.com/boa-dev/boa/pull/2403)\n- Rewrite some patterns with let-else and ok_or_else by @jedel1043 in [#2404](https://github.com/boa-dev/boa/pull/2404)\n- Fix async tests result values by @jedel1043 in [#2406](https://github.com/boa-dev/boa/pull/2406)\n- Rewrite scope analysis operations using visitors by @jedel1043 in [#2408](https://github.com/boa-dev/boa/pull/2408)\n- Make `JsString` conform to miri tests by @jedel1043 in [#2412](https://github.com/boa-dev/boa/pull/2412)\n- Reduced boilerplate code in the parser by @Razican in [#2410](https://github.com/boa-dev/boa/pull/2410)\n- Extract the parser into a crate by @jedel1043 in [#2409](https://github.com/boa-dev/boa/pull/2409)\n- Switch tarpaulin to llvm engine by @RageKnify in [#2432](https://github.com/boa-dev/boa/pull/2432)\n- Cleanup `boa_tester` by @jedel1043 in [#2440](https://github.com/boa-dev/boa/pull/2440)\n- Restructure lint lists in `boa_ast` by @raskad in [#2433](https://github.com/boa-dev/boa/pull/2433)\n- Restructure lints in multiple crates by @raskad in [#2447](https://github.com/boa-dev/boa/pull/2447)\n- Restructure lint lists in `boa_engine` by @raskad in [#2455](https://github.com/boa-dev/boa/pull/2455)\n- Fix rust 1.66.0 lints by @raskad in [#2486](https://github.com/boa-dev/boa/pull/2486)\n- Divide byte compiler by @e-codes-stuff in [#2425](https://github.com/boa-dev/boa/pull/2425)\n- Cleanup inline annotations by @jedel1043 in [#2493](https://github.com/boa-dev/boa/pull/2493)\n- [profiler] Cache StringId by @tunz in [#2495](https://github.com/boa-dev/boa/pull/2495)\n- Improve identifier parsing by @jedel1043 in [#2581](https://github.com/boa-dev/boa/pull/2581)\n- Remove Syntax Errors from Bytecompiler by @raskad in [#2598](https://github.com/boa-dev/boa/pull/2598)\n- fix: RUSTSEC-2020-0071 in boa_engine by @hanabi1224 in [#2627](https://github.com/boa-dev/boa/pull/2627)\n- Migrate tests to new test API by @jedel1043 in [#2619](https://github.com/boa-dev/boa/pull/2619)\n- [regexp] new tests for unicode flag by @selfisekai in [#2656](https://github.com/boa-dev/boa/pull/2656)\n- Handle surrogates in `String.fromCodePoint` by @jedel1043 in [#2659](https://github.com/boa-dev/boa/pull/2659)\n- Bump Test262 and add new features by @jedel1043 in [#2729](https://github.com/boa-dev/boa/pull/2729)\n- Fix cross-realm construction bugs by @jedel1043 in [#2786](https://github.com/boa-dev/boa/pull/2786)\n- Lift `InternalObjectMethods` from `Object` by @jedel1043 in [#2790](https://github.com/boa-dev/boa/pull/2790)\n- Implement async functions using generators by @jedel1043 in [#2821](https://github.com/boa-dev/boa/pull/2821)\n- Improve strictness of `GeneratorState` by @jedel1043 in [#2837](https://github.com/boa-dev/boa/pull/2837)\n- Upgraded to ICU 1.2 by @Razican in [#2826](https://github.com/boa-dev/boa/pull/2826)\n- Fix setting properties inside `with` blocks by @jedel1043 in [#2847](https://github.com/boa-dev/boa/pull/2847)\n- Create a unique `PromiseCapability` on each async function call by @jedel1043 in [#2846](https://github.com/boa-dev/boa/pull/2846)\n- Refactor binding handling APIs by @jedel1043 in [#2870](https://github.com/boa-dev/boa/pull/2870)\n- Refactor guards into a `ContextCleanupGuard` abstraction by @jedel1043 in [#2890](https://github.com/boa-dev/boa/pull/2890)\n- Refactor binding declarations by @raskad in [#2887](https://github.com/boa-dev/boa/pull/2887)\n- Cleanup some bytecompiler code by @raskad in [#2918](https://github.com/boa-dev/boa/pull/2918)\n- Fix `use_self` lints by @raskad in [#2946](https://github.com/boa-dev/boa/pull/2946)\n- Remove unused lint allows by @raskad in [#2968](https://github.com/boa-dev/boa/pull/2968)\n- Decouple bytecompiler from CodeBlock by @HalidOdat in [#2669](https://github.com/boa-dev/boa/pull/2669)\n- Clarity changes for the VM by @nekevss in [#2531](https://github.com/boa-dev/boa/pull/2531)\n- Bump bitflags to 2.0.0 by @Razican in [#2666](https://github.com/boa-dev/boa/pull/2666)\n- Replace deprecated set-output command by @karol-jani in [#2500](https://github.com/boa-dev/boa/pull/2500)\n- Documentation Updates by @nekevss in [#2463](https://github.com/boa-dev/boa/pull/2463)\n- Fixed typo in the docs by @Razican in [#2450](https://github.com/boa-dev/boa/pull/2450)\n- update tasks.json by @jasonwilliams in [#2313](https://github.com/boa-dev/boa/pull/2313)\n- Updated the Code of Conduct by @Razican in [#2365](https://github.com/boa-dev/boa/pull/2365)\n- Bump serde_json from 1.0.85 to 1.0.86 by @jedel1043 in [#2341](https://github.com/boa-dev/boa/pull/2341)\n- Add test case for issue #2719 by @jedel1043 in [#2980](https://github.com/boa-dev/boa/pull/2980)\n- Remove unneded `num_bindings` in `Opcode`s and `CodeBlock` by @HalidOdat in [#2967](https://github.com/boa-dev/boa/pull/2967)\n- Added period to sentence by @nekevss in [#2939](https://github.com/boa-dev/boa/pull/2939)\n- Prune collected shared shapes by @HalidOdat in [#2941](https://github.com/boa-dev/boa/pull/2941)\n- Separate declarative environment kinds by @jedel1043 in [#2921](https://github.com/boa-dev/boa/pull/2921)\n- Shrink environment binding locators by @HalidOdat in [#2950](https://github.com/boa-dev/boa/pull/2950)\n- Extract \"About Boa\" section into a separate file by @jedel1043 in [#2938](https://github.com/boa-dev/boa/pull/2938)\n- Remove `arguments_binding` field from `CodeBlock` by @HalidOdat in [#2969](https://github.com/boa-dev/boa/pull/2969)\n- Remove redundant `param_count` field from `CallFrame` by @HalidOdat in [#2962](https://github.com/boa-dev/boa/pull/2962)\n- Direct length access on arrays by @HalidOdat in [#2796](https://github.com/boa-dev/boa/pull/2796)\n- Prevent allocation of field names by @HalidOdat in [#2901](https://github.com/boa-dev/boa/pull/2901)\n- Added unit tests for `boa_ast::Punctuator` by @Razican in [#2884](https://github.com/boa-dev/boa/pull/2884)\n- Added unit tests for `boa_ast::Keyword` by @Razican in [#2883](https://github.com/boa-dev/boa/pull/2883)\n- Make update operations reuse the last found binding locator by @jedel1043 in [#2876](https://github.com/boa-dev/boa/pull/2876)\n- Docs update for boa_runtime and console documentation by @nekevss in [#2891](https://github.com/boa-dev/boa/pull/2891)\n- Direct array element access on `ByValue` instructions by @HalidOdat in [#2827](https://github.com/boa-dev/boa/pull/2827)\n- Optimize `String.prototype.normalize` by @jedel1043 in [#2848](https://github.com/boa-dev/boa/pull/2848)\n- Fix more Annex B tests by @jedel1043 in [#2841](https://github.com/boa-dev/boa/pull/2841)\n- Enable github queues and remove bors.toml by @jedel1043 in [#2899](https://github.com/boa-dev/boa/pull/2899)\n- Shrink size of `IndexedProperties` by @HalidOdat in [#2757](https://github.com/boa-dev/boa/pull/2757)\n- Added an example usage to documentation by @Razican in [#2742](https://github.com/boa-dev/boa/pull/2742)\n- Don't construct prototype if not needed by @HalidOdat in [#2751](https://github.com/boa-dev/boa/pull/2751)\n- Implement `is_identifier_(start/part)` using `icu_properties` by @jedel1043 in [#2865](https://github.com/boa-dev/boa/pull/2865)\n- Add boa logo to remaining hosted docs by @nekevss in [#2740](https://github.com/boa-dev/boa/pull/2740)\n- Added a bunch more tests by @Razican in [#2885](https://github.com/boa-dev/boa/pull/2885)\n- Updated dependencies, removes `remove_dir_all`, which is vulnerable by @Razican in [#2685](https://github.com/boa-dev/boa/pull/2685)\n- Updated README by @Razican in [#2825](https://github.com/boa-dev/boa/pull/2825)\n- Remove panics on module compilation by @jedel1043 in [#2730](https://github.com/boa-dev/boa/pull/2730)\n- Update icu dependencies by @raskad in [#2574](https://github.com/boa-dev/boa/pull/2574)\n- Pin tarpaulin version to 0.22 by @jedel1043 in [#2562](https://github.com/boa-dev/boa/pull/2562)\n- Improve the design of ephemerons in our GC by @jedel1043 in [#2530](https://github.com/boa-dev/boa/pull/2530)\n- Pass locale data provider by ref instead of boxing by @jedel1043 in [#2508](https://github.com/boa-dev/boa/pull/2508)\n- Fast path for static property keys by @tunz in [#2604](https://github.com/boa-dev/boa/pull/2604)\n- Replace `criterion::black_box` with `std::hint::black_box` by @jedel1043 in [#2494](https://github.com/boa-dev/boa/pull/2494)\n- Shrink objects by using `ThinVec`s by @HalidOdat in [#2752](https://github.com/boa-dev/boa/pull/2752)\n- Redesign native functions and closures API by @jedel1043 in [#2499](https://github.com/boa-dev/boa/pull/2499)\n- Make the `wasmbind` feature of the `chrono` crate optional by @Razican in [#2810](https://github.com/boa-dev/boa/pull/2810)\n- Use opcode table rather than match by @tunz in [#2501](https://github.com/boa-dev/boa/pull/2501)\n- Align iterator loops to the spec by @jedel1043 in [#2686](https://github.com/boa-dev/boa/pull/2686)\n- Add AST node for parenthesized expressions by @raskad in [#2738](https://github.com/boa-dev/boa/pull/2738)\n- Optimize Get/SetPropertyByName by @tunz in [#2608](https://github.com/boa-dev/boa/pull/2608)\n- Keep Integer type for inc/dec of an integer by @tunz in [#2615](https://github.com/boa-dev/boa/pull/2615)\n- Implement `CompletionRecords` for the Vm by @nekevss in [#2618](https://github.com/boa-dev/boa/pull/2618)\n- Feature flag on builtins console import by @nekevss in [#2584](https://github.com/boa-dev/boa/pull/2584)\n- Fix documentation links by @Razican in [#2741](https://github.com/boa-dev/boa/pull/2741)\n- Updated syn to 2.0.3 by @Razican in [#2702](https://github.com/boa-dev/boa/pull/2702)\n- Cleanup intrinsics and move to realm by @jedel1043 in [#2555](https://github.com/boa-dev/boa/pull/2555)\n- Rename `check_parser` and `Identifier` by @jedel1043 in [#2576](https://github.com/boa-dev/boa/pull/2576)\n- Fix rust 1.67 lints by @raskad in [#2567](https://github.com/boa-dev/boa/pull/2567)\n- Avoid unneeded bounds checks in bytecode address patching by @HalidOdat in [#2680](https://github.com/boa-dev/boa/pull/2680)\n- Rust 1.68 clippy fixes by @nekevss in [#2646](https://github.com/boa-dev/boa/pull/2646)\n- Fix rust 1.70 lints by @raskad in [#2990](https://github.com/boa-dev/boa/pull/2990)\n- Simplify/Refactor exception handling and last statement value by @HalidOdat in [#3053](https://github.com/boa-dev/boa/pull/3053)\n\n## [0.16.0 (2022-09-25)](https://github.com/boa-dev/boa/compare/v0.15...v0.16)\n\n### Feature Enhancements\n\n- Implement getter and setter of `Object.prototype.__proto__` by @CYBAI in [#2110](https://github.com/boa-dev/boa/pull/2110)\n- Execution stack & promises by @Razican in [#2107](https://github.com/boa-dev/boa/pull/2107)\n- Add the `[[Done]]` field to iterators by @Razican in [#2125](https://github.com/boa-dev/boa/pull/2125)\n- Fix for in/of loop initializer environment by @raskad in [#2135](https://github.com/boa-dev/boa/pull/2135)\n- Implement `Promise.all` by @raskad in [#2140](https://github.com/boa-dev/boa/pull/2140)\n- Implement `Promise.any` by @raskad in [#2145](https://github.com/boa-dev/boa/pull/2145)\n- Implement `Promise.allSettled` by @raskad in [#2146](https://github.com/boa-dev/boa/pull/2146)\n- Implement `super` expressions by @raskad in [#2116](https://github.com/boa-dev/boa/pull/2116)\n- Implement `async function` and `await` by @raskad in [#2158](https://github.com/boa-dev/boa/pull/2158)\n- Implementation of `JsMap` Wrapper by @nekevss in [#2115](https://github.com/boa-dev/boa/pull/2115)\n- Safe wrapper for `JsSet` by @anuvratsingh in [#2162](https://github.com/boa-dev/boa/pull/2162)\n- Implement `JsArrayBuffer` by @HalidOdat in [#2170](https://github.com/boa-dev/boa/pull/2170)\n- Implement arrow function parsing based on `CoverParenthesizedExpressionAndArrowParameterList` by @raskad in [#2171](https://github.com/boa-dev/boa/pull/2171)\n- Implement Generator Function Constructor by @raskad in [#2174](https://github.com/boa-dev/boa/pull/2174)\n- Parse class private async generator methods by @raskad in [#2220](https://github.com/boa-dev/boa/pull/2220)\n- Implement Async Generators by @raskad in [#2200](https://github.com/boa-dev/boa/pull/2200)\n- Add field accessors to destructing assignment by @raskad in [#2213](https://github.com/boa-dev/boa/pull/2213)\n- Added a bit more integer operation consistency to ByteDataBlock creation by @Razican in [#2272](https://github.com/boa-dev/boa/pull/2272)\n- Implement Async-from-Sync Iterator Objects by @raskad in [#2234](https://github.com/boa-dev/boa/pull/2234)\n- Add URI encoding and decoding functions by @Razican in [#2267](https://github.com/boa-dev/boa/pull/2267)\n- Implement `for await...of` loops by @raskad in [#2286](https://github.com/boa-dev/boa/pull/2286)\n\n### Bug Fixes\n\n- Fix `eval` attributes by @raskad in [#2130](https://github.com/boa-dev/boa/pull/2130)\n- Fix `this` in function calls by @raskad in [#2153](https://github.com/boa-dev/boa/pull/2153)\n- Fix remaining `Promise` bugs by @raskad in [#2156](https://github.com/boa-dev/boa/pull/2156)\n- Fix length/index in `32bit` architectures by @HalidOdat in [#2196](https://github.com/boa-dev/boa/pull/2196)\n- Fix `yield` expression to end on line terminator by @raskad in [#2232](https://github.com/boa-dev/boa/pull/2232)\n- Fix spread arguments in function calls by @raskad in [#2216](https://github.com/boa-dev/boa/pull/2216)\n- Fix `arguments` object iterator function by @raskad in [#2231](https://github.com/boa-dev/boa/pull/2231)\n- check history file exist if not create it by @udhaykumarbala in [#2245](https://github.com/boa-dev/boa/pull/2245)\n- Do not auto-insert semicolon in `VariableDeclarationList` by @tunz in [#2266](https://github.com/boa-dev/boa/pull/2266)\n- Fix property access of call expression by @tunz in [#2273](https://github.com/boa-dev/boa/pull/2273)\n- fix computed property methods can call super methods by @creampnx-x in [#2274](https://github.com/boa-dev/boa/pull/2274)\n- Fix regex literal `/[/]/` by @tunz in [#2277](https://github.com/boa-dev/boa/pull/2277)\n- Fixed assignment expression parsing by @Razican in [#2268](https://github.com/boa-dev/boa/pull/2268)\n- Fix labelled block statement by @creampnx-x in [#2285](https://github.com/boa-dev/boa/pull/2285)\n- Implement missing global object internal methods by @raskad in [#2287](https://github.com/boa-dev/boa/pull/2287)\n\n### Internal Improvements\n\n- Fix spec links for some object operation methods by @CYBAI in [#2111](https://github.com/boa-dev/boa/pull/2111)\n- Only run benchmarks on PRs when a label is set by @raskad in [#2114](https://github.com/boa-dev/boa/pull/2114)\n- Refactor `construct` and `PromiseCapability` to preserve `JsObject` invariants by @jedel1043 in [#2136](https://github.com/boa-dev/boa/pull/2136)\n- Remove `string-interner` dependency and implement custom string `Interner` by @jedel1043 in [#2147](https://github.com/boa-dev/boa/pull/2147)\n- Fix clippy 1.62.0 lints by @raskad in [#2154](https://github.com/boa-dev/boa/pull/2154)\n- Store call frames in `Vec` instead of singly-linked list by @HalidOdat in [#2164](https://github.com/boa-dev/boa/pull/2164)\n- Dense/Packed JavaScript arrays by @HalidOdat in [#2167](https://github.com/boa-dev/boa/pull/2167)\n- Fix Rust 1.63 clippy lints by @raskad in [#2230](https://github.com/boa-dev/boa/pull/2230)\n- Removed some `unsafe_empty_trace!()` calls to improve performance by @Razican in [#2233](https://github.com/boa-dev/boa/pull/2233)\n- Add integer type to fast path of `to_property_key` by @tunz in [#2261](https://github.com/boa-dev/boa/pull/2261)\n\n**Full Changelog**: https://github.com/boa-dev/boa/compare/v0.14...v0.15\n\n## [0.15.0 (2022-06-10)](https://github.com/boa-dev/boa/compare/v0.14...v0.15)\n\n### Feature Enhancements\n\n- Deploy playground to custom destination dir by @jedel1043 in [#1943](https://github.com/boa-dev/boa/pull/1943)\n- add README for crates.io publish by @superhawk610 in [#1952](https://github.com/boa-dev/boa/pull/1952)\n- migrated to clap 3 by @manthanabc in [#1957](https://github.com/boa-dev/boa/pull/1957)\n- Implement unscopables for Array.prototype by @NorbertGarfield in [#1963](https://github.com/boa-dev/boa/pull/1963)\n- Retrieve feature-based results for Test262 runs by @NorbertGarfield in [#1980](https://github.com/boa-dev/boa/pull/1980)\n- Added better error handling for the Boa tester by @Razican in [#1984](https://github.com/boa-dev/boa/pull/1984)\n- Add From<f32> for JsValue by @lastmjs in [#1990](https://github.com/boa-dev/boa/pull/1990)\n- Implement Classes by @raskad in [#1976](https://github.com/boa-dev/boa/pull/1976)\n- Allow `PropertyName`s in `BindingProperty`in `ObjectBindingPattern` by @raskad in [#2022](https://github.com/boa-dev/boa/pull/2022)\n- Allow `Initializer` after `ArrayBindingPattern` in `FormalParameter` by @raskad in [#2002](https://github.com/boa-dev/boa/pull/2002)\n- Allow unicode escaped characters in identifiers that are keywords by @raskad in [#2021](https://github.com/boa-dev/boa/pull/2021)\n- Feature `JsTypedArray`s by @HalidOdat in [#2003](https://github.com/boa-dev/boa/pull/2003)\n- Allow creating object with true/false property names by @lupd in [#2028](https://github.com/boa-dev/boa/pull/2028)\n- Implement `get RegExp.prototype.hasIndices` by @HalidOdat in [#2031](https://github.com/boa-dev/boa/pull/2031)\n- Partial implementation for Intl.DateTimeFormat by @NorbertGarfield in [#2025](https://github.com/boa-dev/boa/pull/2025)\n- Allow `let` as variable declaration name by @raskad in [#2044](https://github.com/boa-dev/boa/pull/2044)\n- cargo workspaces fixes #2001 by @jasonwilliams in [#2026](https://github.com/boa-dev/boa/pull/2026)\n- Move redeclaration errors to parser by @raskad in [#2027](https://github.com/boa-dev/boa/pull/2027)\n- Feature `JsFunction` by @HalidOdat in [#2015](https://github.com/boa-dev/boa/pull/2015)\n- Improve `JsString` performance by @YXL76 in [#2042](https://github.com/boa-dev/boa/pull/2042)\n- Implement ResolveLocale helper by @NorbertGarfield in [#2036](https://github.com/boa-dev/boa/pull/2036)\n- Refactor `IdentifierReference` parsing by @raskad in [#2055](https://github.com/boa-dev/boa/pull/2055)\n- Implement the global `eval()` function by @raskad in [#2041](https://github.com/boa-dev/boa/pull/2041)\n- DateTimeFormat helpers by @NorbertGarfield in [#2064](https://github.com/boa-dev/boa/pull/2064)\n- Create `Date` standard constructor by @jedel1043 in [#2079](https://github.com/boa-dev/boa/pull/2079)\n- Implement `ProxyBuilder` by @jedel1043 in [#2076](https://github.com/boa-dev/boa/pull/2076)\n- Remove `strict` flag from `Context` by @raskad in [#2069](https://github.com/boa-dev/boa/pull/2069)\n- Integrate ICU4X into `Intl` module by @jedel1043 in [#2083](https://github.com/boa-dev/boa/pull/2083)\n- Implement `Function` constructor by @raskad in [#2090](https://github.com/boa-dev/boa/pull/2090)\n- Parse private generator methods in classes by @raskad in [#2092](https://github.com/boa-dev/boa/pull/2092)\n\n### Bug Fixes\n\n- Fix link to the playground by @raskad in [#1947](https://github.com/boa-dev/boa/pull/1947)\n- convert inner datetime to local in `to_date_string` by @superhawk610 in [#1953](https://github.com/boa-dev/boa/pull/1953)\n- Fix panic on AST dump in JSON format by @kilotaras in [#1959](https://github.com/boa-dev/boa/pull/1959)\n- Fix panic in do while by @pdogr in [#1968](https://github.com/boa-dev/boa/pull/1968)\n- Support numbers with multiple leading zeroes by @lupd in [#1979](https://github.com/boa-dev/boa/pull/1979)\n- Fix length properties on array methods by @lupd in [#1983](https://github.com/boa-dev/boa/pull/1983)\n- Allow boolean/null as property identifier by dot operator assignment by @lupd in [#1985](https://github.com/boa-dev/boa/pull/1985)\n- fix(vm): off-by-one in code block stringification. by @tsutton in [#1999](https://github.com/boa-dev/boa/pull/1999)\n- Indicate bigint has constructor by @lupd in [#2008](https://github.com/boa-dev/boa/pull/2008)\n- Change `ArrayBuffer` `byteLength` to accessor property by @lupd in [#2010](https://github.com/boa-dev/boa/pull/2010)\n- Fix `ArrayBuffer.isView()` by @HalidOdat in [#2019](https://github.com/boa-dev/boa/pull/2019)\n- Fix casting negative number to usize in `Array.splice` by @lupd in [#2030](https://github.com/boa-dev/boa/pull/2030)\n- Fix `Symbol` and `BigInt` constructors by @HalidOdat in [#2032](https://github.com/boa-dev/boa/pull/2032)\n- Make `Array.prototype` an array object by @HalidOdat in [#2033](https://github.com/boa-dev/boa/pull/2033)\n- Fix early return in `for in loop` head by @raskad in [#2043](https://github.com/boa-dev/boa/pull/2043)\n\n### Internal Improvements\n\n- docs: update README by structuring the topics by @ftonato in [#1958](https://github.com/boa-dev/boa/pull/1958)\n- Migrate to NPM and cleanup Playground by @jedel1043 in [#1951](https://github.com/boa-dev/boa/pull/1951)\n- Fix performance bottleneck in VM by @pdogr in [#1973](https://github.com/boa-dev/boa/pull/1973)\n- Remove `git2` and `hex` dependencies by @raskad in [#1992](https://github.com/boa-dev/boa/pull/1992)\n- Fix rust 1.60 clippy lints by @raskad in [#2014](https://github.com/boa-dev/boa/pull/2014)\n- Refactor `RegExp` constructor methods by @raskad in [#2049](https://github.com/boa-dev/boa/pull/2049)\n- Fixing build for changes in clippy for Rust 1.61 by @Razican in [#2082](https://github.com/boa-dev/boa/pull/2082)\n\n**Full Changelog**: https://github.com/boa-dev/boa/compare/v0.14...v0.15\n\n## [0.14.0 (2022-03-15) - Virtual Machine](https://github.com/boa-dev/boa/compare/v0.13...v0.14)\n\n### Feature Enhancements\n\n- Implement functions for vm by @HalidOdat in [#1433](https://github.com/boa-dev/boa/pull/1433)\n- Implement Object.getOwnPropertyNames and Object.getOwnPropertySymbols by @kevinputera in [#1606](https://github.com/boa-dev/boa/pull/1606)\n- Implement `Symbol.prototype.valueOf` by @hle0 in [#1618](https://github.com/boa-dev/boa/pull/1618)\n- Implement Array.prototype.at() by @nekevss in [#1613](https://github.com/boa-dev/boa/pull/1613)\n- Implement Array.from by @nrabulinski [#1831](https://github.com/boa-dev/boa/pull/1831)\n- Implement String.fromCharCode by @hle0 in [#1619](https://github.com/boa-dev/boa/pull/1619)\n- Implement `Typed Array` built-in by @Razican in [#1552](https://github.com/boa-dev/boa/pull/1552)\n- Implement arguments exotic objects by @jedel1043 in [#1522](https://github.com/boa-dev/boa/pull/1522)\n- Allow `BindingPattern`s as `CatchParameter` by @lowr in [#1628](https://github.com/boa-dev/boa/pull/1628)\n- Implement `Symbol.prototype[ @@toPrimitive ]` by @Nimpruda in [#1634](https://github.com/boa-dev/boa/pull/1634)\n- Implement Generator parsing by @raskad in [#1575](https://github.com/boa-dev/boa/pull/1575)\n- Implement Object.hasOwn and improve Object.prototype.hasOwnProperty by @kevinputera in [#1639](https://github.com/boa-dev/boa/pull/1639)\n- Hashbang lexer support by @nekevss in [#1631](https://github.com/boa-dev/boa/pull/1631)\n- Implement `delete` operator in the vm by @raskad in [#1649](https://github.com/boa-dev/boa/pull/1649)\n- Implement Object.fromEntries by @kevinputera in [#1660](https://github.com/boa-dev/boa/pull/1660)\n- Initial implementation for increment/decrement in VM by @abhishekc-sharma in [#1621](https://github.com/boa-dev/boa/pull/1621)\n- Implement `Proxy` object by @raskad in [#1664](https://github.com/boa-dev/boa/pull/1664)\n- Implement object literals for vm by @raskad in [#1668](https://github.com/boa-dev/boa/pull/1668)\n- Implement Array findLast and findLastIndex by @bsinky in [#1665](https://github.com/boa-dev/boa/pull/1665)\n- Implement `DataView` built-in object by @Nimpruda in [#1662](https://github.com/boa-dev/boa/pull/1662)\n- Clean-up contribution guidelines, dependencies, Test262, MSRV by @Razican in [#1683](https://github.com/boa-dev/boa/pull/1683)\n- Implement Async Generator Parsing by @nekevss in [#1669](https://github.com/boa-dev/boa/pull/1669)\n- Implement prototype of `Intl` built-in by @hle0 in [#1622](https://github.com/boa-dev/boa/pull/1622)\n- Add limited console.trace implementation by @osman-turan in [#1623](https://github.com/boa-dev/boa/pull/1623)\n- Allow `BindingPattern` in function parameters by @am-a-man in [#1666](https://github.com/boa-dev/boa/pull/1666)\n- Small test ux improvements by @orndorffgrant in [#1704](https://github.com/boa-dev/boa/pull/1704)\n- Implement missing vm operations by @raskad in [#1697](https://github.com/boa-dev/boa/pull/1697)\n- Added fallible allocation to data blocks by @Razican in [#1728](https://github.com/boa-dev/boa/pull/1728)\n- Document CodeBlock by @TheDoctor314 in [#1691](https://github.com/boa-dev/boa/pull/1691)\n- Generic `JsResult<R>` in `context.throw_` methods by @HalidOdat in [#1734](https://github.com/boa-dev/boa/pull/1734)\n- Implement `String.raw( template, ...substitutions )` by @HalidOdat in [#1741](https://github.com/boa-dev/boa/pull/1741)\n- Updated test262 suite and dependencies by @Razican in [#1755](https://github.com/boa-dev/boa/pull/1755)\n- Lexer string interning by @Razican in [#1758](https://github.com/boa-dev/boa/pull/1758)\n- Adjust `compile` and `execute` to avoid clones by @Razican in [#1778](https://github.com/boa-dev/boa/pull/1778)\n- Interner support in the parser by @Razican in [#1765](https://github.com/boa-dev/boa/pull/1765)\n- Convert `Codeblock` variables to `Sym` by @raskad in [#1798](https://github.com/boa-dev/boa/pull/1798)\n- Using production builds for WebAssembly by @Razican in [#1825](https://github.com/boa-dev/boa/pull/1825)\n- Give the arrow function its proper name by @rumpl in [#1832](https://github.com/boa-dev/boa/pull/1832)\n- Unwrap removal by @Razican in [#1842](https://github.com/boa-dev/boa/pull/1842)\n- Feature `JsArray` by @HalidOdat in [#1746](https://github.com/boa-dev/boa/pull/1746)\n- Rename \"Boa\" to boa_engine, moved GC and profiler to their crates by @Razican in [#1844](https://github.com/boa-dev/boa/pull/1844)\n- Added conversions from and to serde_json's Value type by @Razican in [#1851](https://github.com/boa-dev/boa/pull/1851)\n- Toggleable `JsValue` internals displaying by @HalidOdat in [#1865](https://github.com/boa-dev/boa/pull/1865)\n- Implement generator execution by @raskad in [#1790](https://github.com/boa-dev/boa/pull/1790)\n- Feature arrays with empty elements by @HalidOdat in [#1870](https://github.com/boa-dev/boa/pull/1870)\n- Removed reference counted pointers from `JsValue` variants by @Razican in [#1866](https://github.com/boa-dev/boa/pull/1866)\n- Implement `Object.prototype.toLocaleString()` by @HalidOdat in [#1875](https://github.com/boa-dev/boa/pull/1875)\n- Implement `AggregateError` by @HalidOdat in [#1888](https://github.com/boa-dev/boa/pull/1888)\n- Implement destructing assignments for assignment expressions by @raskad in [#1895](https://github.com/boa-dev/boa/pull/1895)\n- Added boa examples by @elasmojs in [#1161](https://github.com/boa-dev/boa/pull/1161)\n\n### Bug Fixes\n\n- Fix BigInt and Number comparison by @HalidOdat [#1887](https://github.com/boa-dev/boa/pull/1887)\n- Fix broken structure links in the documentation by @abhishekc-sharma in [#1612](https://github.com/boa-dev/boa/pull/1612)\n- Use function name from identifiers in assignment expressions by @raskad [#1908](https://github.com/boa-dev/boa/pull/1908)\n- Fix integer parsing by @nrabulinski in [#1614](https://github.com/boa-dev/boa/pull/1614)\n- Fix `Number.toExponential` and `Number.toFixed` by @nrabulinski in [#1620](https://github.com/boa-dev/boa/pull/1620)\n- Badge updates by @atouchet in [#1638](https://github.com/boa-dev/boa/pull/1638)\n- refactor: fix construct_error functions by @RageKnify in [#1703](https://github.com/boa-dev/boa/pull/1703)\n- Fix internal vm tests by @raskad in [#1718](https://github.com/boa-dev/boa/pull/1718)\n- Removed a bunch of warnings and clippy errors by @Razican in [#1754](https://github.com/boa-dev/boa/pull/1754)\n- Fix some broken links in the profiler documentation by @Razican in [#1762](https://github.com/boa-dev/boa/pull/1762)\n- Add proxy handling in `isArray` method by @raskad in [#1777](https://github.com/boa-dev/boa/pull/1777)\n- Copy/paste fix in Proxy error message by @icecream17 in [#1787](https://github.com/boa-dev/boa/pull/1787)\n- Fixed #1768 by @Razican in [#1820](https://github.com/boa-dev/boa/pull/1820)\n- Fix string.prototype methods and add static string methods by @jevancc in [#1123](https://github.com/boa-dev/boa/pull/1123)\n- Handle allocation errors by @y21 in [#1850](https://github.com/boa-dev/boa/pull/1850)\n- Fix wasm use outside browsers by @Razican in [#1846](https://github.com/boa-dev/boa/pull/1846)\n- Add assertion to check that a break label is identified at compile-time by @VTCAKAVSMoACE in [#1852](https://github.com/boa-dev/boa/pull/1852)\n- Correct reference error message by @aaronmunsters in [#1855](https://github.com/boa-dev/boa/pull/1855)\n- Fixing main branch workflows by @Razican in [#1858](https://github.com/boa-dev/boa/pull/1858)\n- Correct pop_on_return behaviour by @VTCAKAVSMoACE in [#1853](https://github.com/boa-dev/boa/pull/1853)\n- Fix equality between objects and `undefined` or `null` by @HalidOdat in [#1872](https://github.com/boa-dev/boa/pull/1872)\n- Removing the panic in favour of an error result by @Razican in [#1874](https://github.com/boa-dev/boa/pull/1874)\n- Make `Object.getOwnPropertyDescriptors` spec compliant by @HalidOdat in [#1876](https://github.com/boa-dev/boa/pull/1876)\n- Make `Error` and `%NativeError%` spec compliant by @HalidOdat in [#1879](https://github.com/boa-dev/boa/pull/1879)\n- Fix `Number.prototype.toString` when passing `undefined` as radix by @HalidOdat in [#1877](https://github.com/boa-dev/boa/pull/1877)\n- Cleanup vm stack on function return by @raskad in [#1880](https://github.com/boa-dev/boa/pull/1880)\n- `%NativeError%.[[prototype]]` should be `Error` constructor by @HalidOdat in [#1883](https://github.com/boa-dev/boa/pull/1883)\n- Make `StringToNumber` spec compliant by @HalidOdat in [#1881](https://github.com/boa-dev/boa/pull/1881)\n- Fix `PropertyKey` to `JsValue` conversion by @HalidOdat in [#1886](https://github.com/boa-dev/boa/pull/1886)\n- Make iterator spec complaint by @HalidOdat in [#1889](https://github.com/boa-dev/boa/pull/1889)\n- Implement `Number.parseInt` and `Number.parseFloat` by @HalidOdat in [#1894](https://github.com/boa-dev/boa/pull/1894)\n- Fix unreachable panics in compile_access by @VTCAKAVSMoACE in [#1861](https://github.com/boa-dev/boa/pull/1861)\n- Continue panic fixes by @VTCAKAVSMoACE in [#1896](https://github.com/boa-dev/boa/pull/1896)\n- Deny const declarations without initializer inside for loops by @jedel1043 in [#1903](https://github.com/boa-dev/boa/pull/1903)\n- Fix try/catch/finally related bugs and add tests by @jedel1043 in [#1901](https://github.com/boa-dev/boa/pull/1901)\n- Compile StatementList after parse passes on negative tests by @raskad in [#1906](https://github.com/boa-dev/boa/pull/1906)\n- Prevent breaks without loop or switch from causing panics by @VTCAKAVSMoACE in [#1860](https://github.com/boa-dev/boa/pull/1860)\n- Fix postfix increment and decrement return values by @raskad in [#1913](https://github.com/boa-dev/boa/pull/1913)\n\n### Internal Improvements\n\n- Rewrite initialization of builtins to use the `BuiltIn` trait by @jedel1043 in [#1586](https://github.com/boa-dev/boa/pull/1586)\n- Unify object creation with `empty` and `from_proto_and_data` methods by @jedel1043 in [#1567](https://github.com/boa-dev/boa/pull/1567)\n- VM Tidy Up by @jasonwilliams in [#1610](https://github.com/boa-dev/boa/pull/1610)\n- Fix master refs to main by @jasonwilliams in [#1637](https://github.com/boa-dev/boa/pull/1637)\n- Refresh vm docs and fix bytecode trace output by @raskad [#1921](https://github.com/boa-dev/boa/pull/1921)\n- Change type of object prototypes to `Option<JsObject>` by @jedel1043 in [#1640](https://github.com/boa-dev/boa/pull/1640)\n- Refactor `Function` internal methods and implement `BoundFunction` objects by @jedel1043 in [#1583](https://github.com/boa-dev/boa/pull/1583)\n- change that verbosity comparison to > 2 by @praveenbakkal in [#1680](https://github.com/boa-dev/boa/pull/1680)\n- Respect rust 1.56 by @RageKnify in [#1681](https://github.com/boa-dev/boa/pull/1681)\n- Add bors to CI by @RageKnify in [#1684](https://github.com/boa-dev/boa/pull/1684)\n- Adding VM conformance output to PR checks by @Razican in [#1685](https://github.com/boa-dev/boa/pull/1685)\n- Start removing non-VM path by @jasonwilliams in [#1747](https://github.com/boa-dev/boa/pull/1747)\n- Using upstream benchmark action by @Razican in [#1753](https://github.com/boa-dev/boa/pull/1753)\n- Fix bors hanging by @RageKnify in [#1767](https://github.com/boa-dev/boa/pull/1767)\n- add more timers on object functions by @jasonwilliams in [#1775](https://github.com/boa-dev/boa/pull/1775)\n- Update the PR benchmarks action by @Razican in [#1774](https://github.com/boa-dev/boa/pull/1774)\n- General code clean-up and new lint addition by @Razican in [#1809](https://github.com/boa-dev/boa/pull/1809)\n- Reduced the size of AST nodes by @Razican in [#1821](https://github.com/boa-dev/boa/pull/1821)\n- Using the new formatting arguments from Rust 1.58 by @Razican in [#1834](https://github.com/boa-dev/boa/pull/1834)\n- Rework RegExp struct to include bitflags field by @aaronmunsters in [#1837](https://github.com/boa-dev/boa/pull/1837)\n- Ignore wastefull `RegExp` tests by @raskad in [#1840](https://github.com/boa-dev/boa/pull/1840)\n- Refactor the environment for runtime performance by @raskad in [#1829](https://github.com/boa-dev/boa/pull/1829)\n- Refactor mapped `Arguments` object by @raskad in [#1849](https://github.com/boa-dev/boa/pull/1849)\n- Fixed dependabot for submodule by @Razican in [#1856](https://github.com/boa-dev/boa/pull/1856)\n- Refactorings for Rust 1.59 by @RageKnify in [#1867](https://github.com/boa-dev/boa/pull/1867)\n- Removing internal deprecated functions by @HalidOdat in [#1854](https://github.com/boa-dev/boa/pull/1854)\n- Remove `toInteger` and document the `string` builtin by @jedel1043 in [#1884](https://github.com/boa-dev/boa/pull/1884)\n- Extract `Intrinsics` struct from `Context` and cleanup names by @jedel1043 in [#1890](https://github.com/boa-dev/boa/pull/1890)\n\n**Full Changelog**: https://github.com/boa-dev/boa/compare/v0.13...v0.14\n\n## [0.13.0 (2021-09-30) - Many new features and refactors](https://github.com/boa-dev/boa/compare/v0.12.0...v0.13.0)\n\nFeature Enhancements:\n\n- [FEATURE #1526](https://github.com/boa-dev/boa/pull/1526): Implement ComputedPropertyName for accessor properties in ObjectLiteral (@raskad)\n- [FEATURE #1365](https://github.com/boa-dev/boa/pull/1365): Implement splice method (@neeldug)\n- [FEATURE #1364](https://github.com/boa-dev/boa/pull/1364): Implement spread for objects (@FrancisMurillo)\n- [FEATURE #1525](https://github.com/boa-dev/boa/pull/1525): Implement Object.preventExtensions() and Object.isExtensible() (@HalidOdat)\n- [FEATURE #1508](https://github.com/boa-dev/boa/pull/1508): Implement Object.values() (@HalidOdat)\n- [FEATURE #1332](https://github.com/boa-dev/boa/pull/1332): Implement Array.prototype.sort (@jedel1043)\n- [FEATURE #1417](https://github.com/boa-dev/boa/pull/1471): Implement Object.keys and Object.entries (@skyne98)\n- [FEATURE #1406](https://github.com/boa-dev/boa/pull/1406): Implement destructuring assignments (@raskad)\n- [FEATURE #1469](https://github.com/boa-dev/boa/pull/1469): Implement String.prototype.replaceAll (@raskad)\n- [FEATURE #1442](https://github.com/boa-dev/boa/pull/1442): Implement closure functions (@HalidOdat)\n- [FEATURE #1390](https://github.com/boa-dev/boa/pull/1390): Implement RegExp named capture groups (@raskad)\n- [FEATURE #1424](https://github.com/boa-dev/boa/pull/1424): Implement Symbol.for and Symbol.keyFor (@HalidOdat)\n- [FEATURE #1375](https://github.com/boa-dev/boa/pull/1375): Implement `at` method for string (@neeldug)\n- [FEATURE #1369](https://github.com/boa-dev/boa/pull/1369): Implement normalize method (@neeldug)\n- [FEATURE #1334](https://github.com/boa-dev/boa/pull/1334): Implement Array.prototype.copyWithin (@jedel1043)\n- [FEATURE #1326](https://github.com/boa-dev/boa/pull/1326): Implement get RegExp[@@species] (@raskad)\n- [FEATURE #1314](https://github.com/boa-dev/boa/pull/1314): Implement RegExp.prototype [ @@search ] ( string ) (@raskad)\n- [FEATURE #1451](https://github.com/boa-dev/boa/pull/1451): Feature prelude module (@HalidOdat)\n- [FEATURE #1523](https://github.com/boa-dev/boa/pull/1523): Allow moving NativeObject variables into closures as external captures (@jedel1043)\n\nBug Fixes:\n\n- [BUG #1521](https://github.com/boa-dev/boa/pull/1521): Added \"js\" feature for getrandom for WebAssembly builds (@Razican)\n- [BUG #1528](https://github.com/boa-dev/boa/pull/1528): Always return undefined from functions that do not return (@raskad)\n- [BUG #1518](https://github.com/boa-dev/boa/pull/1518): Moving a JsObject inside a closure caused a panic (@jedel1043)\n- [BUG #1502](https://github.com/boa-dev/boa/pull/1502): Adjust EnumerableOwnPropertyNames to use all String type property keys (@raskad)\n- [BUG #1415](https://github.com/boa-dev/boa/pull/1415): Fix panic on bigint size (@neeldug)\n- [BUG #1477](https://github.com/boa-dev/boa/pull/1477): Properly handle NaN in new Date() (@raskad)\n- [BUG #1449](https://github.com/boa-dev/boa/pull/1449): Make Array.prototype methods spec compliant (@HalidOdat)\n- [BUG #1353](https://github.com/boa-dev/boa/pull/1353): Make Array.prototype.concat spec compliant (@neeldug)\n- [BUG #1384](https://github.com/boa-dev/boa/pull/1384): bitwise not operation (spec improvements) (@neeldug)\n- [BUG #1374](https://github.com/boa-dev/boa/pull/1374): Match and regexp construct fixes (@neeldug)\n- [BUG #1366](https://github.com/boa-dev/boa/pull/1366): Use lock for map iteration (@joshwd36)\n- [BUG #1360](https://github.com/boa-dev/boa/pull/1360): Adjust a comment to be next to the correct module (@teymour-aldridge)\n- [BUG #1349](https://github.com/boa-dev/boa/pull/1349): Fixes Array.protoype.includes (@neeldug)\n- [BUG #1348](https://github.com/boa-dev/boa/pull/1348): Fixes unshift maximum size (@neeldug)\n- [BUG #1339](https://github.com/boa-dev/boa/pull/1339): Scripts should not be considered in a block (@macmv)\n- [BUG #1312](https://github.com/boa-dev/boa/pull/1312): Fix display for nodes (@macmv)\n- [BUG #1347](https://github.com/boa-dev/boa/pull/1347): Fix stringpad abstract operation (@neeldug)\n- [BUG #1584](https://github.com/boa-dev/boa/pull/1584): Refactor the Math builtin object (spec compliant) (@jedel1043)\n- [BUG #1535](https://github.com/boa-dev/boa/pull/1535): Refactor JSON.parse (@raskad)\n- [BUG #1572](https://github.com/boa-dev/boa/pull/1572): Refactor builtin Map intrinsics to follow more closely the spec (@jedel1043)\n- [BUG #1445](https://github.com/boa-dev/boa/pull/1445): improve map conformance without losing perf (@neeldug)\n- [BUG #1488](https://github.com/boa-dev/boa/pull/1488): Date refactor (@raskad)\n- [BUG #1463](https://github.com/boa-dev/boa/pull/1463): Return function execution result from constructor if the function returned (@raskad)\n- [BUG #1434](https://github.com/boa-dev/boa/pull/1434): Refactor regexp costructor (@raskad)\n- [BUG #1350](https://github.com/boa-dev/boa/pull/1350): Refactor / Implement RegExp functions (@RageKnify) (@raskad)\n- [BUG #1331](https://github.com/boa-dev/boa/pull/1331): Implement missing species getters (@raskad)\n\nInternal Improvements:\n\n- [INTERNAL #1569](https://github.com/boa-dev/boa/pull/1569): Refactor EnvironmentRecordTrait functions (@raskad)\n- [INTERNAL #1464](https://github.com/boa-dev/boa/pull/1464): Optimize integer negation (@HalidOdat)\n- [INTERNAL #1550](https://github.com/boa-dev/boa/pull/1550): Add strict mode flag to Context (@raskad)\n- [INTERNAL #1561](https://github.com/boa-dev/boa/pull/1561): Implement abstract operation GetPrototypeFromConstructor (@jedel1043)\n- [INTERNAL #1309](https://github.com/boa-dev/boa/pull/1309): Implement Display for function objects(@kvnvelasco)\n- [INTERNAL #1492](https://github.com/boa-dev/boa/pull/1492): Implement new get_or_undefined method for `JsValue` (@jedel1043)\n- [INTERNAL #1553](https://github.com/boa-dev/boa/pull/1553): Fix benchmark action in CI (@jedel1043)\n- [INTERNAL #1547](https://github.com/boa-dev/boa/pull/1547): Replace FxHashMap with IndexMap in object properties (@raskad)\n- [INTERNAL #1435](https://github.com/boa-dev/boa/pull/1435): Constant JsStrings (@HalidOdat)\n- [INTERNAL #1499](https://github.com/boa-dev/boa/pull/1499): Updated the Test262 submodule (@Razican)\n- [INTERNAL #1458](https://github.com/boa-dev/boa/pull/1458): Refactor the JS testing system (@bartlomieju)\n- [INTERNAL #1485](https://github.com/boa-dev/boa/pull/1485): Implement abstract operation CreateArrayFromList (@jedel1043)\n- [INTERNAL #1465](https://github.com/boa-dev/boa/pull/1465): Feature throw Error object (@HalidOdat)\n- [INTERNAL #1493](https://github.com/boa-dev/boa/pull/1493): Rename boa::Result to JsResult (@bartlomieju)\n- [INTERNAL #1457](https://github.com/boa-dev/boa/pull/1457): Rename Value to JsValue (@HalidOdat)\n- [INTERNAL #1460](https://github.com/boa-dev/boa/pull/1460): Change StringGetOwnProperty to produce the same strings that the lexer produces (@raskad)\n- [INTERNAL #1425](https://github.com/boa-dev/boa/pull/1425): Extract PropertyMap struct from Object (@jedel1043)\n- [INTERNAL #1432](https://github.com/boa-dev/boa/pull/1432): Proposal of new PropertyDescriptor design (@jedel1043)\n- [INTERNAL #1383](https://github.com/boa-dev/boa/pull/1383): clippy lints and cleanup of old todos (@neeldug)\n- [INTERNAL #1346](https://github.com/boa-dev/boa/pull/1346): Implement gh-page workflow on release (@FrancisMurillo)\n- [INTERNAL #1422](https://github.com/boa-dev/boa/pull/1422): Refactor internal methods and make some builtins spec compliant (@HalidOdat)\n- [INTERNAL #1419](https://github.com/boa-dev/boa/pull/1419): Fix DataDescriptor Value to possibly be empty (@raskad)\n- [INTERNAL #1357](https://github.com/boa-dev/boa/pull/1357): Add Example to Execute a Function of a Script File (@schrieveslaach)\n- [INTERNAL #1408](https://github.com/boa-dev/boa/pull/1408): Refactor JavaScript bigint rust type (@HalidOdat)\n- [INTERNAL #1380](https://github.com/boa-dev/boa/pull/1380): Custom JavaScript string rust type (@HalidOdat)\n- [INTERNAL #1382](https://github.com/boa-dev/boa/pull/1382): Refactor JavaScript symbol rust type (@HalidOdat)\n- [INTERNAL #1361](https://github.com/boa-dev/boa/pull/1361): Redesign bytecode virtual machine (@HalidOdat)\n- [INTERNAL #1381](https://github.com/boa-dev/boa/pull/1381): Fixed documentation warnings (@Razican)\n- [INTERNAL #1352](https://github.com/boa-dev/boa/pull/1352): Respect Rust 1.53 (@RageKnify)\n- [INTERNAL #1356](https://github.com/boa-dev/boa/pull/1356): Respect Rust fmt updates (@RageKnify)\n- [INTERNAL #1338](https://github.com/boa-dev/boa/pull/1338): Fix cargo check errors (@neeldug)\n- [INTERNAL #1329](https://github.com/boa-dev/boa/pull/1329): Allow Value.set_field to throw (@raskad)\n- [INTERNAL #1333](https://github.com/boa-dev/boa/pull/1333): adds condition to avoid triggers from dependabot (@neeldug)\n- [INTERNAL #1337](https://github.com/boa-dev/boa/pull/1337): Fix github actions (@neeldug)\n\n## [0.12.0 (2021-06-07) - `Set`, accessors, `@@toStringTag` and no more panics](https://github.com/boa-dev/boa/compare/v0.11.0...v0.12.0)\n\nFeature Enhancements:\n\n- [FEATURE #1085](https://github.com/boa-dev/boa/pull/1085): Add primitive promotion for method calls on `GetField` (@RageKnify)\n- [FEATURE #1033](https://github.com/boa-dev/boa/pull/1033): Implement `Reflect` built-in object (@tofpie)\n- [FEATURE #1151](https://github.com/boa-dev/boa/pull/1151): Fully implement `EmptyStatement` (@SamuelQZQ)\n- [FEATURE #1158](https://github.com/boa-dev/boa/pull/1158): Include name in verbose results output of `boa-tester` (@0x7D2B)\n- [FEATURE #1225](https://github.com/boa-dev/boa/pull/1225): Implement `Math[ @@toStringTag ]` (@HalidOdat)\n- [FEATURE #1224](https://github.com/boa-dev/boa/pull/1224): Implement `JSON[ @@toStringTag ]` (@HalidOdat)\n- [FEATURE #1222](https://github.com/boa-dev/boa/pull/1222): Implement `Symbol.prototype.description` accessor (@HalidOdat)\n- [FEATURE #1221](https://github.com/boa-dev/boa/pull/1221): Implement `RegExp` flag accessors (@HalidOdat)\n- [FEATURE #1240](https://github.com/boa-dev/boa/pull/1240): Stop ignoring a bunch of tests (@Razican)\n- [FEATURE #1132](https://github.com/boa-dev/boa/pull/1132): Implement `Array.prototype.flat`/`flatMap` (@davimiku)\n- [FEATURE #1235](https://github.com/boa-dev/boa/pull/1235): Implement `Object.assign( target, ...sources )` (@HalidOdat)\n- [FEATURE #1243](https://github.com/boa-dev/boa/pull/1243): Cross realm symbols (@HalidOdat)\n- [FEATURE #1249](https://github.com/boa-dev/boa/pull/1249): Implement `Map.prototype[ @@toStringTag ]` (@wylie39)\n- [FEATURE #1111](https://github.com/boa-dev/boa/pull/1111): Implement `Set` builtin object (@RageKnify)\n- [FEATURE #1265](https://github.com/boa-dev/boa/pull/1265): Implement `BigInt.prototype[ @@toStringTag ]` (@n14littl)\n- [FEATURE #1102](https://github.com/boa-dev/boa/pull/1102): Support Unicode escape in identifier names (@jevancc)\n- [FEATURE #1273](https://github.com/boa-dev/boa/pull/1273): Add default parameter support (@0x7D2B)\n- [FEATURE #1292](https://github.com/boa-dev/boa/pull/1292): Implement `symbol.prototype[ @@ToStringTag ]` (@moadmmh)\n- [FEATURE #1291](https://github.com/boa-dev/boa/pull/1291): Support `GetOwnProperty` for `string` exotic object (@jarkonik)\n- [FEATURE #1296](https://github.com/boa-dev/boa/pull/1296): Added the `$262` object to the Test262 test runner (@Razican)\n- [FEATURE #1127](https://github.com/boa-dev/boa/pull/1127): Implement `Array.of` (@camc)\n\nBug Fixes:\n\n- [BUG #1071](https://github.com/boa-dev/boa/pull/1071): Fix attribute configurable of the length property of arguments (@tofpie)\n- [BUG #1073](https://github.com/boa-dev/boa/pull/1073): Fixed spelling (@vishalsodani)\n- [BUG #1072](https://github.com/boa-dev/boa/pull/1072): Fix `get`/`set` as short method name in `object` (@tofpie)\n- [BUG #1077](https://github.com/boa-dev/boa/pull/1077): Fix panics from multiple borrows of `Map` (@joshwd36)\n- [BUG #1079](https://github.com/boa-dev/boa/pull/1079): Fix lexing escapes in string literal (@jevancc)\n- [BUG #1075](https://github.com/boa-dev/boa/pull/1075): Fix out-of-range panics of `Date` (@jevancc)\n- [BUG #1084](https://github.com/boa-dev/boa/pull/1084): Fix line terminator in string literal (@jevancc)\n- [BUG #1110](https://github.com/boa-dev/boa/pull/1110): Fix parsing floats panics and bugs (@jevancc)\n- [BUG #1202](https://github.com/boa-dev/boa/pull/1202): Fix a typo in `gc.rs` (@teymour-aldridge)\n- [BUG #1201](https://github.com/boa-dev/boa/pull/1201): Return optional value in `to_json` functions (@fermian)\n- [BUG #1223](https://github.com/boa-dev/boa/pull/1223): Update cli name in Readme (@sphinxc0re)\n- [BUG #1175](https://github.com/boa-dev/boa/pull/1175): Handle early errors for declarations in `StatementList` (@0x7D2B)\n- [BUG #1270](https://github.com/boa-dev/boa/pull/1270): Fix `Context::register_global_function()` (@HalidOdat)\n- [BUG #1135](https://github.com/boa-dev/boa/pull/1135): Fix of instructions.rs comment, to_precision impl and rfc changes (@NathanRoyer)\n- [BUG #1272](https://github.com/boa-dev/boa/pull/1272): Fix `Array.prototype.filter` (@tofpie & @Razican)\n- [BUG #1280](https://github.com/boa-dev/boa/pull/1280): Fix slice index panic in `add_rest_param` (@0x7D2B)\n- [BUG #1284](https://github.com/boa-dev/boa/pull/1284): Fix `GcObject` `to_json` mutable borrow panic (@0x7D2B)\n- [BUG #1283](https://github.com/boa-dev/boa/pull/1283): Fix panic in regex execution (@0x7D2B)\n- [BUG #1286](https://github.com/boa-dev/boa/pull/1286): Fix construct usage (@0x7D2B)\n- [BUG #1288](https://github.com/boa-dev/boa/pull/1288): Fixed `Math.hypot.length` bug (@moadmmh)\n- [BUG #1285](https://github.com/boa-dev/boa/pull/1285): Fix environment record panics (@0x7D2B)\n- [BUG #1302](https://github.com/boa-dev/boa/pull/1302): Fix VM branch (@jasonwilliams)\n\nInternal Improvements:\n\n- [INTERNAL #1067](https://github.com/boa-dev/boa/pull/1067): Change `Realm::global_object` field from `Value` to `GcObject` (@RageKnify)\n- [INTERNAL #1048](https://github.com/boa-dev/boa/pull/1048): VM Trace output fixes (@jasonwilliams)\n- [INTERNAL #1109](https://github.com/boa-dev/boa/pull/1109): Define all property methods of constructors (@RageKnify)\n- [INTERNAL #1126](https://github.com/boa-dev/boa/pull/1126): Remove unnecessary wraps for non built-in functions (@RageKnify)\n- [INTERNAL #1044](https://github.com/boa-dev/boa/pull/1044): Removed duplicated code in `vm.run` using macros (@stephanemagnenat)\n- [INTERNAL #1103](https://github.com/boa-dev/boa/pull/1103): Lazy evaluation for cooked template string (@jevancc)\n- [INTERNAL #1156](https://github.com/boa-dev/boa/pull/1156): Rework environment records (@0x7D2B)\n- [INTERNAL #1181](https://github.com/boa-dev/boa/pull/1181): Merge `Const`/`Let`/`Var` `DeclList` into `DeclarationList` (@0x7D2B)\n- [INTERNAL #1234](https://github.com/boa-dev/boa/pull/1234): Separate `Symbol` builtin (@HalidOdat)\n- [INTERNAL #1131](https://github.com/boa-dev/boa/pull/1131): Make environment methods take `&mut Context` (@HalidOdat)\n- [INTERNAL #1271](https://github.com/boa-dev/boa/pull/1271): Make `same_value` and `same_value_zero` static methods (@HalidOdat)\n- [INTERNAL #1276](https://github.com/boa-dev/boa/pull/1276): Cleanup (@Razican)\n- [INTERNAL #1279](https://github.com/boa-dev/boa/pull/1279): Add test comparison to Test262 result compare (@Razican)\n- [INTERNAL #1293](https://github.com/boa-dev/boa/pull/1293): Fix test262 comment formatting (@0x7D2B)\n- [INTERNAL #1294](https://github.com/boa-dev/boa/pull/1294): Don't consider panic fixes as \"new failures\" (@Razican)\n\n## [0.11.0 (2021-01-14) - Faster Parsing & Better compliance](https://github.com/boa-dev/boa/compare/v0.10.0...v0.11.0)\n\nFeature Enhancements:\n\n- [FEATURE #836](https://github.com/boa-dev/boa/pull/836):\n  Async/Await parse (@Lan2u)\n- [FEATURE #704](https://github.com/boa-dev/boa/pull/704):\n  Implement for...of loops (@joshwd36)\n- [FEATURE #770](https://github.com/boa-dev/boa/pull/770):\n  Support for symbols as property keys for `Object.defineProperty` (@georgeroman)\n- [FEATURE #717](https://github.com/boa-dev/boa/pull/717):\n  Strict Mode Lex/Parse (@Lan2u)\n- [FEATURE #800](https://github.com/boa-dev/boa/pull/800):\n  Implement `console` crate feature - Put `console` object behind a feature flag (@HalidOdat)\n- [FEATURE #804](https://github.com/boa-dev/boa/pull/804):\n  Implement `EvalError` (@HalidOdat)\n- [FEATURE #805](https://github.com/boa-dev/boa/pull/805):\n  Implement `Function.prototype.call` (@RageKnify)\n- [FEATURE #806](https://github.com/boa-dev/boa/pull/806):\n  Implement `URIError` (@HalidOdat)\n- [FEATURE #811](https://github.com/boa-dev/boa/pull/811):\n  Implement spread operator using iterator (@croraf)\n- [FEATURE #844](https://github.com/boa-dev/boa/pull/844):\n  Allow UnaryExpression with prefix increment/decrement (@croraf)\n- [FEATURE #798](https://github.com/boa-dev/boa/pull/798):\n  Implement Object.getOwnPropertyDescriptor() and Object.getOwnPropertyDescriptors() (@JohnDoneth)\n- [FEATURE #847](https://github.com/boa-dev/boa/pull/847):\n  Implement Map.prototype.entries() (@croraf)\n- [FEATURE #859](https://github.com/boa-dev/boa/pull/859):\n  Implement spec compliant Array constructor (@georgeroman)\n- [FEATURE #874](https://github.com/boa-dev/boa/pull/874):\n  Implement Map.prototype.values and Map.prototype.keys (@croraf)\n- [FEATURE #877](https://github.com/boa-dev/boa/pull/877):\n  Implement Function.prototype.apply (@georgeroman)\n- [FEATURE #908](https://github.com/boa-dev/boa/pull/908):\n  Implementation of `instanceof` operator (@morrien)\n- [FEATURE #935](https://github.com/boa-dev/boa/pull/935):\n  Implement String.prototype.codePointAt (@devinus)\n- [FEATURE #961](https://github.com/boa-dev/boa/pull/961):\n  Implement the optional `space` parameter in `JSON.stringify` (@tofpie)\n- [FEATURE #962](https://github.com/boa-dev/boa/pull/962):\n  Implement Number.prototype.toPrecision (@NathanRoyer)\n- [FEATURE #983](https://github.com/boa-dev/boa/pull/983):\n  Implement Object.prototype.isPrototypeOf (@tofpie)\n- [FEATURE #995](https://github.com/boa-dev/boa/pull/995):\n  Support Numeric separators (@tofpie)\n- [FEATURE #1013](https://github.com/boa-dev/boa/pull/1013):\n  Implement nullish coalescing (?? and ??=) (@tofpie)\n- [FEATURE #987](https://github.com/boa-dev/boa/pull/987):\n  Implement property accessors (@tofpie)\n- [FEATURE #1018](https://github.com/boa-dev/boa/pull/1018):\n  Implement logical assignment operators (&&= and ||=) (@tofpie)\n- [FEATURE #1019](https://github.com/boa-dev/boa/pull/1019):\n  Implement early errors for non-assignable nodes in assignment (@tofpie)\n- [FEATURE #1020](https://github.com/boa-dev/boa/pull/1020):\n  Implement Symbol.toPrimitive (@tofpie)\n- [FEATURE #976](https://github.com/boa-dev/boa/pull/976):\n  Implement for..in (@tofpie)\n- [FEATURE #1026](https://github.com/boa-dev/boa/pull/1026):\n  Implement String.prototype.split (@jevancc)\n- [FEATURE #1047](https://github.com/boa-dev/boa/pull/1047):\n  Added syntax highlighting for numbers, identifiers and template literals (@Razican)\n- [FEATURE #1003](https://github.com/boa-dev/boa/pull/1003):\n  Improve Unicode support for identifier names (@jevancc)\n\nBug Fixes:\n\n- [BUG #782](https://github.com/boa-dev/boa/pull/782):\n  Throw TypeError if regexp is passed to startsWith, endsWith, includes (@pt2121)\n- [BUG #788](https://github.com/boa-dev/boa/pull/788):\n  Fixing a duplicated attribute in test262 results (@Razican)\n- [BUG #790](https://github.com/boa-dev/boa/pull/790):\n  Throw RangeError when BigInt division by zero occurs (@JohnDoneth)\n- [BUG #785](https://github.com/boa-dev/boa/pull/785):\n  Fix zero argument panic in JSON.parse() (@JohnDoneth)\n- [BUG #749](https://github.com/boa-dev/boa/pull/749):\n  Fix Error constructors to return rather than throw (@RageKnify)\n- [BUG #777](https://github.com/boa-dev/boa/pull/777):\n  Fix cyclic JSON.stringify / primitive conversion stack overflows (@vgel)\n- [BUG #799](https://github.com/boa-dev/boa/pull/799):\n  Fix lexer span panic with carriage return (@vgel)\n- [BUG #812](https://github.com/boa-dev/boa/pull/812):\n  Fix 2 bugs that caused Test262 to fail (@RageKnify)\n- [BUG #826](https://github.com/boa-dev/boa/pull/826):\n  Fix tokenizing Unicode escape sequence in string literal (@HalidOdat)\n- [BUG #825](https://github.com/boa-dev/boa/pull/825):\n  calling \"new\" on a primitive value throw a type error (@dlemel8)\n- [BUG #853](https://github.com/boa-dev/boa/pull/853)\n  Handle invalid Unicode code point in the string literals (@jevancc)\n- [BUG #870](https://github.com/boa-dev/boa/pull/870)\n  Fix JSON stringification for fractional numbers (@georgeroman)\n- [BUG #807](https://github.com/boa-dev/boa/pull/807):\n  Make boa::parse emit error on invalid input, not panic (@georgeroman)\n- [BUG #880](https://github.com/boa-dev/boa/pull/880):\n  Support more number literals in BigInt's from string constructor (@georgeroman)\n- [BUG #885](https://github.com/boa-dev/boa/pull/885):\n  Fix `BigInt.prototype.toString()` radix checks (@georgeroman)\n- [BUG #882](https://github.com/boa-dev/boa/pull/882):\n  Fix (panic) remainder by zero (@georgeroman)\n- [BUG #884](https://github.com/boa-dev/boa/pull/884):\n  Fix some panics related to BigInt operations (@georgeroman)\n- [BUG #888](https://github.com/boa-dev/boa/pull/888):\n  Fix some panics in String.prototype properties (@georgeroman)\n- [BUG #902](https://github.com/boa-dev/boa/pull/902):\n  Fix Accessors panics (@HalidOdat)\n- [BUG #959](https://github.com/boa-dev/boa/pull/959):\n  Fix Unicode character escape sequence parsing (@tofpie)\n- [BUG #964](https://github.com/boa-dev/boa/pull/964):\n  Fix single line comment lexing with CRLF line ending (@tofpie)\n- [BUG #919](https://github.com/boa-dev/boa/pull/919):\n  Reduce the number of `Array`-related panics (@jakubfijalkowski)\n- [BUG #968](https://github.com/boa-dev/boa/pull/968):\n  Fix unit tests that can be failed due to daylight saving time (@tofpie)\n- [BUG #972](https://github.com/boa-dev/boa/pull/972):\n  Fix enumerable attribute on array length property (@tofpie)\n- [BUG #974](https://github.com/boa-dev/boa/pull/974):\n  Fix enumerable attribute on string length property (@tofpie)\n- [BUG #981](https://github.com/boa-dev/boa/pull/981):\n  Fix prototypes for Number, String and Boolean (@tofpie)\n- [BUG #999](https://github.com/boa-dev/boa/pull/999):\n  Fix logical expressions evaluation (@tofpie)\n- [BUG #1001](https://github.com/boa-dev/boa/pull/1001):\n  Fix comparison with infinity (@tofpie)\n- [BUG #1004](https://github.com/boa-dev/boa/pull/1004):\n  Fix panics surrounding `Object.prototype.hasOwnProperty()` (@HalidOdat)\n- [BUG #1005](https://github.com/boa-dev/boa/pull/1005):\n  Fix panics surrounding `Object.defineProperty()` (@HalidOdat)\n- [BUG #1021](https://github.com/boa-dev/boa/pull/1021):\n  Fix spread in new and call expressions (@tofpie)\n- [BUG #1023](https://github.com/boa-dev/boa/pull/1023):\n  Fix attributes on properties of functions and constructors (@tofpie)\n- [BUG #1017](https://github.com/boa-dev/boa/pull/1017):\n  Don't panic when function parameters share names (@AnnikaCodes)\n- [BUG #1024](https://github.com/boa-dev/boa/pull/1024):\n  Fix delete when the property is not configurable (@tofpie)\n- [BUG #1027](https://github.com/boa-dev/boa/pull/1027):\n  Supress regress errors on invalid escapes for regex (@jasonwilliams\n- [BUG #1031](https://github.com/boa-dev/boa/pull/1031):\n  Fixed some extra regex panics (@Razican)\n- [BUG #1049](https://github.com/boa-dev/boa/pull/1049):\n  Support overriding the `arguments` variable (@AnnikaCodes)\n- [BUG #1050](https://github.com/boa-dev/boa/pull/1050):\n  Remove panic on named capture groups (@Razican)\n- [BUG #1046](https://github.com/boa-dev/boa/pull/1046):\n  Remove a few different panics (@Razican)\n- [BUG #1051](https://github.com/boa-dev/boa/pull/1051):\n  Fix parsing of arrow functions with 1 argument (@Lan2u)\n- [BUG #1045](https://github.com/boa-dev/boa/pull/1045):\n  Add newTarget to construct (@tofpie)\n- [BUG #659](https://github.com/boa-dev/boa/pull/659):\n  Error handling in environment (@54k1)\n\nInternal Improvements:\n\n- [INTERNAL #735](https://github.com/boa-dev/boa/pull/735):\n  Move exec implementations together with AST node structs (@georgeroman)\n- [INTERNAL #724](https://github.com/boa-dev/boa/pull/724):\n  Ignore tests for code coverage count (@HalidOdat)\n- [INTERNAL #768](https://github.com/boa-dev/boa/pull/768)\n  Update the benchmark Github action (@Razican)\n- [INTERNAL #722](https://github.com/boa-dev/boa/pull/722):\n  `ConstructorBuilder`, `ObjectInitializer`, cache standard objects and fix global object attributes (@HalidOdat)\n- [INTERNAL #783](https://github.com/boa-dev/boa/pull/783):\n  New test262 results format (This also reduces the payload size for the website) (@Razican)\n- [INTERNAL #787](https://github.com/boa-dev/boa/pull/787):\n  Refactor ast/node/expression into ast/node/call and ast/node/new (@croraf)\n- [INTERNAL #802](https://github.com/boa-dev/boa/pull/802):\n  Make `Function.prototype` a function (@HalidOdat)\n- [INTERNAL #746](https://github.com/boa-dev/boa/pull/746):\n  Add Object.defineProperties and handle props argument in Object.create (@dvtkrlbs)\n- [INTERNAL #774](https://github.com/boa-dev/boa/pull/774):\n  Switch from `regex` to `regress` for ECMA spec-compliant regex implementation (@neeldug)\n- [INTERNAL #794](https://github.com/boa-dev/boa/pull/794):\n  Refactor `PropertyDescriptor` (Improved performance) (@HalidOdat)\n- [INTERNAL #824](https://github.com/boa-dev/boa/pull/824):\n  [parser Expression] minor expression macro simplification (@croraf)\n- [INTERNAL #833](https://github.com/boa-dev/boa/pull/833):\n  Using unstable sort for sorting keys on `to_json()` for GC objects (@Razican)\n- [INTERNAL #837](https://github.com/boa-dev/boa/pull/837):\n  Set default-run to `boa` removing need for `--bin` (@RageKnify)\n- [INTERNAL #841](https://github.com/boa-dev/boa/pull/841):\n  Minor refactor and rename in eval() method (@croraf)\n- [INTERNAL #840](https://github.com/boa-dev/boa/pull/840):\n  fix(profiler): update profiler to match current measureme api (@neeldug)\n- [INTERNAL #838](https://github.com/boa-dev/boa/pull/838):\n  style(boa): minor cleanup (@neeldug)\n- [INTERNAL #869](https://github.com/boa-dev/boa/pull/869):\n  Updated cache in workflows (@Razican)\n- [INTERNAL #873](https://github.com/boa-dev/boa/pull/873)\n  Removed cache from MacOS builds (@Razican)\n- [INTERNAL #835](https://github.com/boa-dev/boa/pull/835):\n  Move `Object` internal object methods to `GcObject` (@HalidOdat)\n- [INTERNAL #886](https://github.com/boa-dev/boa/pull/886):\n  Support running a specific test/suite in boa_tester (@georgeroman)\n- [INTERNAL #901](https://github.com/boa-dev/boa/pull/901):\n  Added \"unimplemented\" syntax errors (@Razican)\n- [INTERNAL #911](https://github.com/boa-dev/boa/pull/911):\n  Change Symbol hash to `u64` (@HalidOdat)\n- [INTERNAL #912](https://github.com/boa-dev/boa/pull/912):\n  Feature `Context::register_global_property()` (@HalidOdat)\n- [INTERNAL #913](https://github.com/boa-dev/boa/pull/913):\n  Added check to ignore semicolon in parser (@AngelOnFira)\n- [INTERNAL #915](https://github.com/boa-dev/boa/pull/915):\n  Improve lexer by make cursor iterate over bytes (@jevancc)\n- [INTERNAL #952](https://github.com/boa-dev/boa/pull/952):\n  Upgraded rustyline and test262 (@Razican)\n- [INTERNAL #960](https://github.com/boa-dev/boa/pull/960):\n  Fix unresolved links in documentation (@tofpie)\n- [INTERNAL #979](https://github.com/boa-dev/boa/pull/979):\n  Read file input in bytes instead of string (@tofpie)\n- [INTERNAL #1014](https://github.com/boa-dev/boa/pull/1014):\n  StatementList: Rename `statements` to `items` (@AnnikaCodes)\n- [INTERNAL #860](https://github.com/boa-dev/boa/pull/860):\n  Investigation into ByteCode Interpreter (@jasonwilliams)\n- [INTERNAL #1042](https://github.com/boa-dev/boa/pull/1042):\n  Add receiver parameter to object internal methods (@tofpie)\n- [INTERNAL #1030](https://github.com/boa-dev/boa/pull/1030):\n  VM: Implement variable declaration (var, const, and let) (@AnnikaCodes)\n- [INTERNAL #1010](https://github.com/boa-dev/boa/pull/1010):\n  Modify environment binding behaviour of function (@54k1)\n\n## [0.10.0 (2020-09-29) - New Lexer & Test 262 Harness](https://github.com/boa-dev/boa/compare/v0.9.0...v0.10.0)\n\nFeature Enhancements:\n\n- [FEATURE #524](https://github.com/boa-dev/boa/pull/525):\n  Implement remaining `Math` methods (@mr-rodgers)\n- [FEATURE #562](https://github.com/boa-dev/boa/pull/562):\n  Implement remaining `Number` methods (@joshwd36)\n- [FEATURE #536](https://github.com/boa-dev/boa/pull/536):\n  Implement `SyntaxError` (@HalidOdat)\n- [FEATURE #543](https://github.com/boa-dev/boa/pull/543):\n  Implements `Object.create` builtin method (@croraf)\n- [FEATURE #492](https://github.com/boa-dev/boa/pull/492):\n  Switch to [rustyline](https://github.com/kkawakam/rustyline) for the CLI (@IovoslavIovchev & @Razican)\n- [FEATURE #595](https://github.com/boa-dev/boa/pull/595):\n  Added syntax highlighting for strings in REPL (@HalidOdat)\n- [FEATURE #586](https://github.com/boa-dev/boa/pull/586):\n  Better error formatting and cli color (@HalidOdat)\n- [FEATURE #590](https://github.com/boa-dev/boa/pull/590):\n  Added keyword and operator colors and matching bracket validator to REPL (@HalidOdat)\n- [FEATURE #555](https://github.com/boa-dev/boa/pull/555):\n  Implement Array.prototype.reduce (@benjaminflin)\n- [FEATURE #550](https://github.com/boa-dev/boa/pull/550):\n  Initial implementation of Map() (@joshwd36 & @HalidOdat)\n- [FEATURE #579](https://github.com/boa-dev/boa/pull/579):\n  Implement Array.prototype.reduceRight (@benjaminflin)\n- [FEATURE #585](https://github.com/boa-dev/boa/pull/587):\n  Implement Well-Known Symbols (@joshwd36)\n- [FEATURE #589](https://github.com/boa-dev/boa/pull/589):\n  Implement the comma operator (@KashParty)\n- [FEATURE #341](https://github.com/boa-dev/boa/pull/590):\n  Ability to create multiline blocks in boa shell (@HalidOdat)\n- [FEATURE #252](https://github.com/boa-dev/boa/pull/596):\n  Implement `Date` (@jcdickinson)\n- [FEATURE #711](https://github.com/boa-dev/boa/pull/711):\n  Add support for >>>= (@arpit-saxena)\n- [FEATURE #549](https://github.com/boa-dev/boa/pull/549):\n  Implement label statements (@jasonwilliams)\n- [FEATURE #373](https://github.com/boa-dev/boa/pull/373):\n  Introduce PropertyKey for field acces (@RageKnify)\n- [FEATURE #627](https://github.com/boa-dev/boa/pull/627):\n  Feature native class objects (`NativeObject` and `Class` traits) (@HalidOdat)\n- [FEATURE #694](https://github.com/boa-dev/boa/pull/694):\n  Feature `gc` module (@HalidOdat)\n- [FEATURE #656](https://github.com/boa-dev/boa/pull/656):\n  Feature `Context` (@HalidOdat)\n- [FEATURE #673](https://github.com/boa-dev/boa/pull/673):\n  Add `#[track_caller]` to `GcObject` methods that can panic (@HalidOdat)\n- [FEATURE #661](https://github.com/boa-dev/boa/pull/661):\n  Add documentation to `GcObject` methods (@HalidOdat)\n- [FEATURE #662](https://github.com/boa-dev/boa/pull/662):\n  Implement `std::error::Error` for `GcObject` borrow errors (@HalidOdat)\n- [FEATURE #660](https://github.com/boa-dev/boa/pull/660):\n  Make `GcObject::contruct` not take 'this' (@HalidOdat)\n- [FEATURE #654](https://github.com/boa-dev/boa/pull/654):\n  Move `require_object_coercible` to `Value` (@HalidOdat)\n- [FEATURE #603](https://github.com/boa-dev/boa/pull/603):\n  Index `PropertyKey`, `Object` iterators and symbol support (@HalidOdat)\n- [FEATURE #637](https://github.com/boa-dev/boa/pull/637):\n  Feature `boa::Result<T>` (@HalidOdat)\n- [FEATURE #625](https://github.com/boa-dev/boa/pull/625):\n  Moved value operations from `Interpreter` to `Value` (@HalidOdat)\n- [FEATURE #638](https://github.com/boa-dev/boa/pull/638):\n  Changed to `Value::to_*int32` => `Value::to_*32` (@HalidOdat)\n\nBug Fixes:\n\n- [BUG #405](https://github.com/boa-dev/boa/issues/405):\n  Fix json.stringify symbol handling (@n14little)\n- [BUG #520](https://github.com/boa-dev/boa/pull/520):\n  Fix all `Value` operations and add unsigned shift right (@HalidOdat)\n- [BUG #529](https://github.com/boa-dev/boa/pull/529):\n  Refactor exec/expression into exec/call and exec/new (@croraf)\n- [BUG #510](https://github.com/boa-dev/boa/issues/510):\n  [[Call]] calling an undefined method does not throw (@joshwd36)\n- [BUG #493](https://github.com/boa-dev/boa/pull/493):\n  Use correct exponential representation for rational values (@Tropid)\n- [BUG #572](https://github.com/boa-dev/boa/pull/572):\n  Spec Compliant `Number.prototype.toString()`, better `Number` object formating and `-0` (@HalidOdat)\n- [BUG #599](https://github.com/boa-dev/boa/pull/599):\n  Fixed `String.prototype.indexOf()` bug, when the search string is empty (@HalidOdat)\n- [BUG #615](https://github.com/boa-dev/boa/issues/615):\n  Fix abstract relational comparison operators (@HalidOdat)\n- [BUG #608](https://github.com/boa-dev/boa/issues/608):\n  `Debug::fmt` Causes Causes a Stack Overflow (@jcdickinson)\n- [BUG #532](https://github.com/boa-dev/boa/issues/532)\n  [builtins - Object] Object.getPrototypeOf returning incorrectly (@54k1)\n- [BUG #533](https://github.com/boa-dev/boa/issues/533)\n  [exec - function] function.prototype doesn't have own constructor property pointing to this function (@54k1)\n- [BUG #641](https://github.com/boa-dev/boa/issues/641)\n  Test new_instance_should_point_to_prototype is not checked correctly (@54k1)\n- [BUG #644](https://github.com/boa-dev/boa/pull/645)\n  `undefined` constants panic on execution (@jcdickinson)\n- [BUG #631](https://github.com/boa-dev/boa/pull/645):\n  Unexpected result when applying typeof to undefined value (@jcdickinson)\n- [BUG #667](https://github.com/boa-dev/boa/pull/667):\n  Fix panic when calling function that mutates itself (@dvtkrlbs)\n- [BUG #668](https://github.com/boa-dev/boa/pull/668):\n  Fix clippy on Nightly (@dvtkrlbs)\n- [BUG #582](https://github.com/boa-dev/boa/pull/582):\n  Make `String.prototype.repeat()` ECMAScript specification compliant (@HalidOdat)\n- [BUG #541](https://github.com/boa-dev/boa/pull/541):\n  Made all `Math` methods spec compliant (@HalidOdat)\n- [BUG #597](https://github.com/boa-dev/boa/pull/597):\n  Made `String.prototype.indexOf` spec compliant. (@HalidOdat)\n- [BUG #598](https://github.com/boa-dev/boa/pull/598):\n  Made `String.prototype.lastIndexOf()` spec compliant (@HalidOdat)\n- [BUG #583](https://github.com/boa-dev/boa/pull/583):\n  Fix string prototype `trim` methods (@HalidOdat)\n- [BUG #728](https://github.com/boa-dev/boa/pull/728):\n  Fix bug when setting the length on String objects (@jasonwilliams)\n- [BUG #710](https://github.com/boa-dev/boa/pull/710):\n  Fix panic when a self mutating function is constructing an object (@HalidOdat)\n- [BUG #699](https://github.com/boa-dev/boa/pull/699):\n  Fix `Value::to_json` order of items in array (@sele9)\n- [BUG #610](https://github.com/boa-dev/boa/pull/610):\n  Fix: `String.prototype.replace` substitutions (@RageKnify)\n- [BUG #645](https://github.com/boa-dev/boa/pull/645):\n  Fix undefined constant expression evaluation (@jcdickinson)\n- [BUG #643](https://github.com/boa-dev/boa/pull/643):\n  Change default return type from null to undefined (@54k1)\n- [BUG #642](https://github.com/boa-dev/boa/pull/642):\n  Missing `constructor` field in ordinary functions (@54k1)\n- [BUG #604](https://github.com/boa-dev/boa/pull/604):\n  Missing `__proto__` field in functions instances (@54k1)\n- [BUG #561](https://github.com/boa-dev/boa/pull/561):\n  Throw a `TypeError` when a non-object is called (@joshwd36)\n- [BUG #748](https://github.com/boa-dev/boa/pull/748):\n  Fix parse error throwing a `TypeError`, instead of `SyntaxError` (@iamsaquib8)\n- [BUG #737](https://github.com/boa-dev/boa/pull/737):\n  Make `Object.toString()` spec compliant (@RageKnify)\n\nInternal Improvements:\n\n- [INTERNAL #567](https://github.com/boa-dev/boa/pull/567):\n  Add ECMAScript test suite (test262) (@Razican)\n- [INTERNAL #559](https://github.com/boa-dev/boa/pull/559):\n  New Lexer (@Lan2u @HalidOdat @Razican)\n- [INTERNAL #712](https://github.com/boa-dev/boa/pull/712):\n  Refactor: `Value::to_object` to return `GcObject` (@RageKnify)\n- [INTERNAL #544](https://github.com/boa-dev/boa/pull/544):\n  Removed `console`s dependency of `InternalState` (@HalidOdat)\n- [INTERNAL #556](https://github.com/boa-dev/boa/pull/556):\n  Added benchmark for goal symbol switching (@Razican)\n- [INTERNAL #578](https://github.com/boa-dev/boa/pull/580):\n  Extract `prototype` from internal slots (@HalidOdat)\n- [INTERNAL #553](https://github.com/boa-dev/boa/pull/553):\n  Refactor Property Descriptor flags (@HalidOdat)\n- [INTERNAL #592](https://github.com/boa-dev/boa/pull/592):\n  `RegExp` specialization (@HalidOdat)\n- [INTERNAL #626](https://github.com/boa-dev/boa/pull/626):\n  Refactor `Function` (@HalidOdat @Razican)\n- [INTERNAL #564](https://github.com/boa-dev/boa/pull/581):\n  Add benchmarks for \"uglified\" JS (@neeldug)\n- [INTERNAL #706](https://github.com/boa-dev/boa/pull/706):\n  Cache well known symbols (@HalidOdat)\n- [INTERNAL #723](https://github.com/boa-dev/boa/pull/723):\n  Add fast path for string concatenation (@RageKnify)\n- [INTERNAL #689](https://github.com/boa-dev/boa/pull/689):\n  Move `object` module to root (@HalidOdat)\n- [INTERNAL #684](https://github.com/boa-dev/boa/pull/684):\n  Move `property` module to root (@HalidOdat)\n- [INTERNAL #674](https://github.com/boa-dev/boa/pull/674):\n  Move `value` module to root (@HalidOdat)\n- [INTERNAL #693](https://github.com/boa-dev/boa/pull/693):\n  Rename `Object::prototype()` and `Object::set_prototype()` (@RageKnify)\n- [INTERNAL #665](https://github.com/boa-dev/boa/pull/665):\n  `approx_eq!` macro for `expm1` tests. (@neeldung)\n- [INTERNAL #581](https://github.com/boa-dev/boa/pull/581):\n  Added CLEAN_JS and MINI_JS benches (@neeldung)\n- [INTERNAL #640](https://github.com/boa-dev/boa/pull/640):\n  Benchmark refactor (@neeldung)\n- [INTERNAL #635](https://github.com/boa-dev/boa/pull/635):\n  Add missing ops to exec module (@jarredholman)\n- [INTERNAL #616](https://github.com/boa-dev/boa/pull/616):\n  Remove `Value::as_num_to_power()` (@HalidOdat)\n- [INTERNAL #601](https://github.com/boa-dev/boa/pull/601):\n  Removed internal_slots from object (@HalidOdat)\n- [INTERNAL #560](https://github.com/boa-dev/boa/pull/560):\n  Added benchmarks for full program execution (@Razican)\n- [INTERNAL #547](https://github.com/boa-dev/boa/pull/547):\n  Merged `create` into `init` for builtins (@HalidOdat)\n- [INTERNAL #538](https://github.com/boa-dev/boa/pull/538):\n  Cleanup and added test for `String.prototype.concat` (@HalidOdat)\n- [INTERNAL #739](https://github.com/boa-dev/boa/pull/739):\n  Add release action (@jasonwilliams)\n- [INTERNAL #744](https://github.com/boa-dev/boa/pull/744):\n  Add MacOS check and test to CI (@neeldug)\n\n## [0.9.0 (2020-07-03) - Move to Organisation, 78% faster execution time](https://github.com/boa-dev/boa/compare/v0.8.0...v0.9.0)\n\nFeature Enhancements:\n\n- [FEATURE #414](https://github.com/boa-dev/boa/issues/414):\n  Implement `Number` object constants (@Lan2u) (@HalidOdat)\n- [FEATURE #345](https://github.com/boa-dev/boa/issues/345):\n  Implement the optional `replacer` parameter in `JSON.stringify( value[, replacer [, space] ] )` (@n14little)\n- [FEATURE #480](https://github.com/boa-dev/boa/issues/480):\n  Implement global `Infinity` property (@AnirudhKonduru)\n- [FEATURE #410](https://github.com/boa-dev/boa/pull/410):\n  Add support for the reviver function to JSON.parse (@abhijeetbhagat)\n- [FEATURE #425](https://github.com/boa-dev/boa/pull/425):\n  Specification compliant `ToString` (`to_string`) (@HalidOdat)\n- [FEATURE #442](https://github.com/boa-dev/boa/pull/442):\n  Added `TypeError` implementation (@HalidOdat)\n- [FEATURE #450](https://github.com/boa-dev/boa/pull/450):\n  Specification compliant `ToBigInt` (`to_bigint`) (@HalidOdat)\n- [FEATURE #455](https://github.com/boa-dev/boa/pull/455):\n  TemplateLiteral Basic lexer implementation (@croraf)\n- [FEATURE #447](https://github.com/boa-dev/boa/issues/447):\n  parseInt, parseFloat implementation (@Lan2u)\n- [FEATURE #468](https://github.com/boa-dev/boa/pull/468):\n  Add BigInt.asIntN() and BigInt.asUintN() functions (@Tropid)\n- [FEATURE #428](https://github.com/boa-dev/boa/issues/428):\n  [Feature Request] - Create benchmark for Array manipulation (@abhijeetbhagat)\n- [FEATURE #439](https://github.com/boa-dev/boa/issues/439):\n  Implement break handling in switch statements (@Lan2u)\n- [FEATURE #301](https://github.com/boa-dev/boa/issues/301):\n  Implementing the switch statement in the new parser (@Lan2u)\n- [FEATURE #120](https://github.com/boa-dev/boa/issues/120):\n  Implement `globalThis` (@zanayr)\n- [FEATURE #513](https://github.com/boa-dev/boa/issues/513):\n  Implement `Object.is()` method (@tylermorten)\n- [FEATURE #481](https://github.com/boa-dev/boa/issues/481):\n  Implement global `undefined` property (@croraf)\n\nBug Fixes:\n\n- [BUG #412](https://github.com/boa-dev/boa/pull/412):\n  Fixed parsing if statement without else block preceded by a newline (@HalidOdat)\n- [BUG #409](https://github.com/boa-dev/boa/pull/409):\n  Fix function object constructable/callable (@HalidOdat)\n- [BUG #403](https://github.com/boa-dev/boa/issues/403)\n  `Value::to_json()` does not handle `undefined` correctly (@n14little)\n- [BUG #443](https://github.com/boa-dev/boa/issues/443):\n  HasOwnProperty should call GetOwnProperty and not GetProperty (@n14little)\n- [BUG #210](https://github.com/boa-dev/boa/issues/210):\n  builtinfun.length undefined (@Croraf)\n- [BUG #466](https://github.com/boa-dev/boa/issues/466):\n  Change `ToPrimitive()` (`to_primitive()`) hint to be an enum, instead of string (@HalidOdat)\n- [BUG #421](https://github.com/boa-dev/boa/issues/421):\n  `NaN` is lexed as a number, not as an identifier (@croraf)\n- [BUG #454](https://github.com/boa-dev/boa/issues/454):\n  Function declaration returns the function, it should return `undefined` (@croraf)\n- [BUG #482](https://github.com/boa-dev/boa/issues/482):\n  Field access should propagate the exception (`Err(_)`) (@neeldug)\n- [BUG #463](https://github.com/boa-dev/boa/issues/463):\n  Use of undefined variable should throw an error (@croraf)\n- [BUG #502](https://github.com/boa-dev/boa/pull/502):\n  Fixed global objects initialization order (@HalidOdat)\n- [BUG #509](https://github.com/boa-dev/boa/issues/509):\n  JSON.stringify(undefined) panics (@n14little)\n- [BUG #514](https://github.com/boa-dev/boa/issues/514):\n  Clean up `Math` Methods (@n14little)\n- [BUG #511](https://github.com/boa-dev/boa/issues/511):\n  [Call] Usage of \"this\" in methods is not supported (@jasonwilliams)\n\nInternal Improvements\n\n- [INTERNAL #435](https://github.com/boa-dev/boa/issues/435):\n  Optimize type comparisons (@Lan2u)\n- [INTERNAL #296](https://github.com/boa-dev/boa/issues/296):\n  using measureme for profiling the interpreter (@jasonwilliams)\n- [INTERNAL #419](https://github.com/boa-dev/boa/pull/419):\n  Object specialization (fast paths for many objects) (@HalidOdat)\n- [INTERNAL #392](https://github.com/boa-dev/boa/pull/392):\n  Execution and Node modulization (@Razican)\n- [INTERNAL #465](https://github.com/boa-dev/boa/issues/465):\n  Refactoring Value (decouple `Gc` from `Value`) (@HalidOdat)\n- [INTERNAL #416](https://github.com/boa-dev/boa/pull/416) & [INTERNAL #423](https://github.com/boa-dev/boa/commit/c8218dd91ef3181e048e7a2659a4fbf8d53c7174):\n  Update links to boa-dev (@pedropaulosuzuki)\n- [INTERNAL #378](https://github.com/boa-dev/boa/issues/378):\n  Code Coverage! (@Lan2u)\n- [INTERNAL #431](https://github.com/boa-dev/boa/pull/431):\n  Updates to PR Benchmarks (@Razican)\n- [INTERNAL #427 #429 #430](https://github.com/boa-dev/boa/commit/64dbf13afd15f12f958daa87a3d236dc9af1a9aa):\n  Added new benchmarks (@Razican)\n\n## [0.8.0 (2020-05-23) - BigInt, Modularized Parser, Faster Hashing](https://github.com/boa-dev/boa/compare/v0.7.0...v0.8.0)\n\n`v0.8.0` brings more language implementations, such as do..while, function objects and also more recent EcmaScript additions, like BigInt.\nWe have now moved the Web Assembly build into the `wasm` package, plus added a code of conduct for those contributing.\n\nThe parser has been even more modularized in this release making it easier to add new parsing rules.\n\nBoa has migrated it's object implemention to FXHash which brings much improved results over the built-in Rust hashmaps (at the cost of less DOS Protection).\n\nFeature Enhancements:\n\n- [FEATURE #121](https://github.com/boa-dev/boa/issues/121):\n  `BigInt` Implemented (@HalidOdat)\n- [FEATURE #293](https://github.com/boa-dev/boa/pull/293):\n  Improved documentation of all modules (@HalidOdat)\n- [FEATURE #302](https://github.com/boa-dev/boa/issues/302):\n  Implement do..while loop (@ptasz3k)\n- [FEATURE #318](https://github.com/boa-dev/boa/pull/318):\n  Added continous integration for windows (@HalidOdat)\n- [FEATURE #290](https://github.com/boa-dev/boa/pull/290):\n  Added more build profiles (@Razican)\n- [FEATURE #323](https://github.com/boa-dev/boa/pull/323):\n  Aded more benchmarks (@Razican)\n- [FEATURE #326](https://github.com/boa-dev/boa/pull/326):\n  Rename Boa CLI (@sphinxc0re)\n- [FEATURE #312](https://github.com/boa-dev/boa/pull/312):\n  Added jemallocator for linux targets (@Razican)\n- [FEATURE #339](https://github.com/boa-dev/boa/pull/339):\n  Improved Method parsing (@muskuloes)\n- [FEATURE #352](https://github.com/boa-dev/boa/pull/352):\n  create boa-wasm package (@muskuloes)\n- [FEATURE #304](https://github.com/boa-dev/boa/pull/304):\n  Modularized parser\n- [FEATURE #141](https://github.com/boa-dev/boa/issues/141):\n  Implement function objects (@jasonwilliams)\n- [FEATURE #365](https://github.com/boa-dev/boa/issues/365):\n  Implement for loop execution (@Razican)\n- [FEATURE #356](https://github.com/boa-dev/boa/issues/356):\n  Use Fx Hash to speed up hash maps in the compiler (@Razican)\n- [FEATURE #321](https://github.com/boa-dev/boa/issues/321):\n  Implement unary operator execution (@akryvomaz)\n- [FEATURE #379](https://github.com/boa-dev/boa/issues/379):\n  Automatic auditing of Boa (@n14little)\n- [FEATURE #264](https://github.com/boa-dev/boa/issues/264):\n  Implement `this` (@jasonwilliams)\n- [FEATURE #395](https://github.com/boa-dev/boa/pull/395):\n  impl abstract-equality-comparison (@hello2dj)\n- [FEATURE #359](https://github.com/boa-dev/boa/issues/359):\n  impl typeof (@RestitutorOrbis)\n- [FEATURE #390](https://github.com/boa-dev/boa/pull/390):\n  Modularize try statement parsing (@abhijeetbhagat)\n\nBug fixes:\n\n- [BUG #308](https://github.com/boa-dev/boa/issues/308):\n  Assignment operator not working in tests (a = a +1) (@ptasz3k)\n- [BUG #322](https://github.com/boa-dev/boa/issues/322):\n  Benchmarks are failing in master (@Razican)\n- [BUG #325](https://github.com/boa-dev/boa/pull/325):\n  Put JSON functions on the object, not the prototype (@coolreader18)\n- [BUG #331](https://github.com/boa-dev/boa/issues/331):\n  We only get `Const::Num`, never `Const::Int` (@HalidOdat)\n- [BUG #209](https://github.com/boa-dev/boa/issues/209):\n  Calling `new Array` with 1 argument doesn't work properly (@HalidOdat)\n- [BUG #266](https://github.com/boa-dev/boa/issues/266):\n  Panic assigning named function to variable (@Razican)\n- [BUG #397](https://github.com/boa-dev/boa/pull/397):\n  fix `NaN` is lexed as identifier, not as a number (@attliaLin)\n- [BUG #362](https://github.com/boa-dev/boa/pull/362):\n  Remove Monaco Editor Webpack Plugin and Manually Vendor Editor Workers (@subhankar-panda)\n- [BUG #406](https://github.com/boa-dev/boa/pull/406):\n  Dependency Upgrade (@Razican)\n- [BUG #407](https://github.com/boa-dev/boa/pull/407):\n  `String()` wasn't defaulting to empty string on call (@jasonwilliams)\n- [BUG #404](https://github.com/boa-dev/boa/pull/404):\n  Fix for 0 length new String(@tylermorten)\n\nCode Of Conduct:\n\n- [COC #384](https://github.com/boa-dev/boa/pull/384):\n  Code of conduct added (@Razican)\n\nSecurity:\n\n- [SEC #391](https://github.com/boa-dev/boa/pull/391):\n  run security audit daily at midnight. (@n14little)\n\n## [# 0.7.0 (2020-04-13) - New Parser is 67% faster](https://github.com/boa-dev/boa/compare/v0.6.0...v0.7.0)\n\n`v0.7.0` brings a REPL, Improved parser messages and a new parser!\nThis is now the default behaviour of Boa, so running Boa without a file argument will bring you into a javascript shell.\nTests have also been moved to their own files, we had a lot of tests in some modules so it was time to separate.\n\n### New Parser\n\nMost of the work in this release has been on rewriting the parser. A big task taken on by [HalidOdat](https://github.com/HalidOdat), [Razican](https://github.com/Razican) and [myself](https://github.com/jasonwilliams).\n\nThe majority of the old parser was 1 big function (called [`parse`](https://github.com/boa-dev/boa/blob/019033eff066e8c6ba9456139690eb214a0bf61d/boa/src/syntax/parser.rs#L353)) which had some pattern matching on each token coming in.\nThe easy branches could generate expressions (which were basically AST Nodes), the more involved branches would recursively call into the same function, until eventually you had an expression generated.\n\nThis only worked so far, eventually debugging parsing problems were difficult, also more bugs were being raised against the parser which couldn't be fixed.\n\nWe decided to break the parser into more of a state-machine. The initial decision for this was inspired by [Fedor Indutny](https://github.com/indutny) who did a talk at (the last) JSConf EU about how he broke up the old node-parser to make it more maintanable. He goes into more detail here https://www.youtube.com/watch?v=x3k_5Mi66sY&feature=youtu.be&t=530\n\nThe new parser has functions to match the states of parsing in the spec. For example https://tc39.es/ecma262/#prod-VariableDeclaration has a matching function `read_variable_declaration`. This not only makes it better to maintain but easier for new contributors to get involed, as following the parsing logic of the spec is easier than before.\n\nOnce finished some optimisations were added by [HalidOdat](https://github.com/HalidOdat) to use references to the tokens instead of cloning them each time we take them from the lexer.\nThis works because the tokens live just as long as the parser operations do, so we don't need to copy the tokens.\nWhat this brings is a huge performance boost, the parser is 67% faster than before!\n\n![Parser Improvement](./docs/img/parser-graph.png)\n\nFeature enhancements:\n\n- [FEATURE #281](https://github.com/boa-dev/boa/pull/281):\n  Rebuild the parser (@jasonwilliams, @Razican, @HalidOdat)\n- [FEATURE #278](https://github.com/boa-dev/boa/pull/278):\n  Added the ability to dump the token stream or ast in bin. (@HalidOdat)\n- [FEATURE #253](https://github.com/boa-dev/boa/pull/253):\n  Implement Array.isArray (@cisen)\n- [FEATURE](https://github.com/boa-dev/boa/commit/edab5ca6cc10d13265f82fa4bc05d6b432a362fc)\n  Switch to normal output instead of debugged output (stdout/stdout) (@jasonwilliams)\n- [FEATURE #258](https://github.com/boa-dev/boa/pull/258):\n  Moved test modules to their own files (@Razican)\n- [FEATURE #267](https://github.com/boa-dev/boa/pull/267):\n  Add print & REPL functionality to CLI (@JohnDoneth)\n- [FEATURE #268](https://github.com/boa-dev/boa/pull/268):\n  Addition of forEach() (@jasonwilliams) (@xSke)\n- [FEATURE #262](https://github.com/boa-dev/boa/pull/262):\n  Implement Array.prototype.filter (@Nickforall)\n- [FEATURE #261](https://github.com/boa-dev/boa/pull/261):\n  Improved parser error messages (@Razican)\n- [FEATURE #277](https://github.com/boa-dev/boa/pull/277):\n  Add a logo to the project (@HalidOdat)\n- [FEATURE #260](https://github.com/boa-dev/boa/pull/260):\n  Add methods with f64 std equivelant to Math object (@Nickforall)\n\nBug fixes:\n\n- [BUG #249](https://github.com/boa-dev/boa/pull/249):\n  fix(parser): handle trailing comma in object literals (@gomesalexandre)\n- [BUG #244](https://github.com/boa-dev/boa/pull/244):\n  Fixed more Lexer Panics (@adumbidiot)\n- [BUG #256](https://github.com/boa-dev/boa/pull/256):\n  Fixed comments lexing (@Razican)\n- [BUG #251](https://github.com/boa-dev/boa/issues/251):\n  Fixed empty returns (@Razican)\n- [BUG #272](https://github.com/boa-dev/boa/pull/272):\n  Fix parsing of floats that start with a zero (@Nickforall)\n- [BUG #240](https://github.com/boa-dev/boa/issues/240):\n  Fix parser panic\n- [BUG #273](https://github.com/boa-dev/boa/issues/273):\n  new Class().method() has incorrect precedence\n\nDocumentation Updates:\n\n- [DOC #297](https://github.com/boa-dev/boa/pull/297):\n  Better user contributed documentation\n\n## [0.6.0 (2020-02-14) - Migration to Workspace Architecture + lexer/parser improvements](https://github.com/boa-dev/boa/compare/v0.5.1...v0.6.0)\n\nThe lexer has had several fixes in this release, including how it parses numbers, scientific notation should be improved.\nOn top of that the lexer no longer panics on errors including Syntax Errors (thanks @adumbidiot), instead you get some output on where the error happened.\n\n### Moving to a workspace architecture\n\nBoa offers both a CLI and a library, initially these were all in the same binary. The downside is\nthose who want to embed boa as-is end up with all of the command-line dependencies.\nSo the time has come to separate out the two, this is normal procedure, this should be analogous to ripgrep\nand the regex crate.\nCargo has great support for workspaces, so this shouldn't be an issue.\n\n### Benchmarks\n\nWe now have [benchmarks which run against master](https://boajs.dev/boa/dev/bench/)!\nThanks to Github Actions these will run automatically a commit is merged.\n\nFeature enhancements:\n\n- [FEATURE #218](https://github.com/boa-dev/boa/pull/218):\n  Implement Array.prototype.toString (@cisen)\n- [FEATURE #216](https://github.com/boa-dev/boa/commit/85e9a3526105a600358bd53811e2b022987c6fc8):\n  Keep accepting new array elements after spread.\n- [FEATURE #220](https://github.com/boa-dev/boa/pull/220):\n  Documentation updates. (@croraf)\n- [FEATURE #226](https://github.com/boa-dev/boa/pull/226):\n  add parser benchmark for expressions. (@jasonwilliams)\n- [FEATURE #217](https://github.com/boa-dev/boa/pull/217):\n  String.prototype.replace() implemented\n- [FEATURE #247](https://github.com/boa-dev/boa/pull/247):\n  Moved to a workspace architecture (@Razican)\n\nBug fixes:\n\n- [BUG #222](https://github.com/boa-dev/boa/pull/222):\n  Fixed clippy errors (@IovoslavIovchev)\n- [BUG #228](https://github.com/boa-dev/boa/pull/228):\n  [lexer: single-line-comment] Fix bug when single line comment is last line of file (@croraf)\n- [BUG #229](https://github.com/boa-dev/boa/pull/229):\n  Replace error throwing with panic in \"Lexer::next()\" (@croraf)\n- [BUG #232/BUG #238](https://github.com/boa-dev/boa/pull/232):\n  Clippy checking has been scaled right back to just Perf and Style (@jasonwilliams)\n- [BUG #227](https://github.com/boa-dev/boa/pull/227):\n  Array.prototype.toString should be called by ES value (@cisen)\n- [BUG #242](https://github.com/boa-dev/boa/pull/242):\n  Fixed some panics in the lexer (@adumbidiot)\n- [BUG #235](https://github.com/boa-dev/boa/pull/235):\n  Fixed arithmetic operations with no space (@gomesalexandre)\n- [BUG #245](https://github.com/boa-dev/boa/pull/245):\n  Fixed parsing of floats with scientific notation (@adumbidiot)\n\n## [# 0.5.1 (2019-12-02) - Rest / Spread (almost)](https://github.com/boa-dev/boa/compare/v0.5.0...v0.5.1)\n\nFeature enhancements:\n\n- [FEATURE #151](https://github.com/boa-dev/boa/issues/151):\n  Implement the Rest/Spread operator (functions and arrays).\n- [FEATURE #193](https://github.com/boa-dev/boa/issues/193):\n  Implement macro for setting builtin functions\n- [FEATURE #211](https://github.com/boa-dev/boa/pull/211):\n  Better Display support for all Objects (pretty printing)\n\n## [# 0.5.0 (2019-11-06) - Hacktoberfest Release](https://github.com/boa-dev/boa/compare/v0.4.0...v0.5.1)\n\nFeature enhancements:\n\n- [FEATURE #119](https://github.com/boa-dev/boa/issues/119):\n  Introduce realm struct to hold realm context and global object.\n- [FEATURE #89](https://github.com/boa-dev/boa/issues/89):\n  Implement exponentiation operator. Thanks @arbroween\n- [FEATURE #47](https://github.com/boa-dev/boa/issues/47):\n  Add tests for comments in source code. Thanks @Emanon42\n- [FEATURE #137](https://github.com/boa-dev/boa/issues/137):\n  Use Monaco theme for the demo page\n- [FEATURE #114](https://github.com/boa-dev/boa/issues/114):\n  String.match(regExp) is implemented (@muskuloes)\n- [FEATURE #115](https://github.com/boa-dev/boa/issues/115):\n  String.matchAll(regExp) is implemented (@bojan88)\n- [FEATURE #163](https://github.com/boa-dev/boa/issues/163):\n  Implement Array.prototype.every() (@letmutx)\n- [FEATURE #165](https://github.com/boa-dev/boa/issues/165):\n  Implement Array.prototype.find() (@letmutx)\n- [FEATURE #166](https://github.com/boa-dev/boa/issues/166):\n  Implement Array.prototype.findIndex() (@felipe-fg)\n- [FEATURE #39](https://github.com/boa-dev/boa/issues/39):\n  Implement block scoped variable declarations (@barskern)\n- [FEATURE #161](https://github.com/boa-dev/boa/pull/161):\n  Enable obj[key] = value syntax.\n- [FEATURE #179](https://github.com/boa-dev/boa/issues/179):\n  Implement the Tilde operator (@letmutx)\n- [FEATURE #189](https://github.com/boa-dev/boa/pull/189):\n  Implement Array.prototype.includes (incl tests) (@simonbrahan)\n- [FEATURE #180](https://github.com/boa-dev/boa/pull/180):\n  Implement Array.prototype.slice (@muskuloes @letmutx)\n- [FEATURE #152](https://github.com/boa-dev/boa/issues/152):\n  Short Function syntax (no arguments)\n- [FEATURE #164](https://github.com/boa-dev/boa/issues/164):\n  Implement Array.prototype.fill() (@bojan88)\n- Array tests: Tests implemented for shift, unshift and reverse, pop and push (@muskuloes)\n- Demo page has been improved, new font plus change on input. Thanks @WofWca\n- [FEATURE #182](https://github.com/boa-dev/boa/pull/182):\n  Implement some Number prototype methods (incl tests) (@pop)\n- [FEATURE #34](https://github.com/boa-dev/boa/issues/34):\n  Number object and Constructore are implemented (including methods) (@pop)\n- [FEATURE #194](https://github.com/boa-dev/boa/pull/194):\n  Array.prototype.map (@IovoslavIovchev)\n- [FEATURE #90](https://github.com/boa-dev/boa/issues/90):\n  Symbol Implementation (@jasonwilliams)\n\nBug fixes:\n\n- [BUG #113](https://github.com/boa-dev/boa/issues/113):\n  Unassigned variables have default of undefined (@pop)\n- [BUG #61](https://github.com/boa-dev/boa/issues/61):\n  Clippy warnings/errors fixed (@korpen)\n- [BUG #147](https://github.com/boa-dev/boa/pull/147):\n  Updated object global\n- [BUG #154](https://github.com/boa-dev/boa/issues/154):\n  Correctly handle all whitespaces within the lexer\n- Tidy up Globals being added to Global Object. Thanks @DomParfitt\n\n## 0.4.0 (2019-09-25)\n\nv0.4.0 brings quite a big release. The biggest feature to land is the support of regular expressions.\nFunctions now have the arguments object supported and we have a [`debugging`](docs/debugging.md) section in the docs.\n\nFeature enhancements:\n\n- [FEATURE #6](https://github.com/boa-dev/boa/issues/6):\n  Support for regex literals. (Big thanks @999eagle)\n- [FEATURE #13](https://github.com/boa-dev/boa/issues/13):\n  toLowerCase, toUpperCase, substring, substr and valueOf implemented (thanks @arbroween)\n- Support for `arguments` object within functions\n- `StringData` instead of `PrimitieData` to match spec\n- Native function signatures changed, operations added to match spec\n- Primitives can now be boxed/unboxed when methods are ran on them\n- Spelling edits (thanks @someguynamedmatt)\n- Ability to set global values before interpreter starts (thanks @999eagle)\n- Assign operators implemented (thanks @oll3)\n-\n\nBug fixes:\n\n- [BUG #57](https://github.com/boa-dev/boa/issues/57):\n  Fixed issue with stackoverflow by implementing early returns.\n- Allow to re-assign value to an existing binding. (Thanks @oll3)\n\n## 0.3.0 (2019-07-26)\n\n- UnexpectedKeyword(Else) bug fixed https://github.com/boa-dev/boa/issues/38\n- Contributing guide added\n- Ability to specify file - Thanks @callumquick\n- Travis fixes\n- Parser Tests - Thanks @Razican\n- Migrate to dyn traits - Thanks @Atul9\n- Added implementations for Array.prototype: concat(), push(), pop() and join() - Thanks @callumquick\n- Some clippy Issues fixed - Thanks @Razican\n- Objects have been refactored to use structs which are more closely aligned with the specification\n- Benchmarks have been added\n- String and Array specific console.log formats - Thanks @callumquick\n- isPropertyKey implementation added - Thanks @KrisChambers\n- Unit Tests for Array and Strings - Thanks @GalAster\n- typo fix - Thanks @palerdot\n- dist cleanup, thanks @zgotsch\n\n## 0.2.1 (2019-06-30)\n\nSome String prototype methods are implemented.\nThanks to @lennartbuit we have\ntrim/trimStart/trimEnd added to the string prototype\n\nFeature enhancements:\n\n- [String.prototype.concat ( ...args )](https://tc39.es/ecma262/#sec-string.prototype.slice)\n- [String.prototype.endsWith ( searchString [ , endPosition ] )](https://tc39.es/ecma262/#sec-string.prototype.endswith)\n- [String.prototype.includes ( searchString [ , position ] )](https://tc39.es/ecma262/#sec-string.prototype.includes)\n- [String.prototype.indexOf ( searchString [ , position ] )](https://tc39.es/ecma262/#sec-string.prototype.indexof)\n- [String.prototype.lastIndexOf ( searchString [ , position ] )](https://tc39.es/ecma262/#sec-string.prototype.lastindexof)\n- [String.prototype.repeat ( count )](https://tc39.es/ecma262/#sec-string.prototype.repeat)\n- [String.prototype.slice ( start, end )](https://tc39.es/ecma262/#sec-string.prototype.slice)\n- [String.prototype.startsWith ( searchString [ , position ] )](https://tc39.es/ecma262/#sec-string.prototype.startswith)\n\nBug fixes:\n\n- Plenty\n\n## 0.2.0 (2019-06-10)\n\nWorking state reached\n\n- Tests on the lexer, conforms with puncturators and keywords from TC39 specification\n- wasm-bindgen added with working demo in Web Assembly\n- snapshot of boa in a working state for the first time\n"
  },
  {
    "path": "CODEOWNERS",
    "content": "# Fallback to all maintainers for common directories.\n\n* @boa-dev/maintainers\n\n# Owned components\n\ncore/engine/src/builtins/intl @jedel1043 @nekevss\ncore/engine/src/builtins/temporal @jasonwilliams @jedel1043 @nekevss\n"
  },
  {
    "path": "CODE_OF_CONDUCT.md",
    "content": "# Contributor Covenant Code of Conduct\n\n## Our Pledge\n\nWe as members, contributors, and leaders pledge to make participation in our\ncommunity a harassment-free experience for everyone, regardless of age, body\nsize, visible or invisible disability, ethnicity, sex characteristics, gender\nidentity and expression, level of experience, education, socio-economic status,\nnationality, personal appearance, race, caste, color, religion, or sexual\nidentity and orientation.\n\nWe pledge to act and interact in ways that contribute to an open, welcoming,\ndiverse, inclusive, and healthy community.\n\n## Our Standards\n\nExamples of behavior that contributes to a positive environment for our\ncommunity include:\n\n- Demonstrating empathy and kindness toward other people\n- Being respectful of differing opinions, viewpoints, and experiences\n- Giving and gracefully accepting constructive feedback\n- Accepting responsibility and apologizing to those affected by our mistakes,\n  and learning from the experience\n- Focusing on what is best not just for us as individuals, but for the overall\n  community\n\nExamples of unacceptable behavior include:\n\n- The use of sexualized language or imagery, and sexual attention or advances of\n  any kind\n- Trolling, insulting or derogatory comments, and personal or political attacks\n- Public or private harassment\n- Publishing others' private information, such as a physical or email address,\n  without their explicit permission\n- Other conduct which could reasonably be considered inappropriate in a\n  professional setting\n\n## Enforcement Responsibilities\n\nCommunity leaders are responsible for clarifying and enforcing our standards of\nacceptable behavior and will take appropriate and fair corrective action in\nresponse to any behavior that they deem inappropriate, threatening, offensive,\nor harmful.\n\nCommunity leaders have the right and responsibility to remove, edit, or reject\ncomments, commits, code, wiki edits, issues, and other contributions that are\nnot aligned to this Code of Conduct, and will communicate reasons for moderation\ndecisions when appropriate.\n\n## Scope\n\nThis Code of Conduct applies within all community spaces, and also applies when\nan individual is officially representing the community in public spaces.\nExamples of representing our community include using an official e-mail address,\nposting via an official social media account, or acting as an appointed\nrepresentative at an online or offline event.\n\n## Enforcement\n\nInstances of abusive, harassing, or otherwise unacceptable behavior may be\nreported to the community leaders responsible for enforcement at\n[Matrix](https://matrix.to/#/#boa:matrix.org) by contacting any of the admins in the space.\nAll complaints will be reviewed and investigated promptly and fairly.\n\nAll community leaders are obligated to respect the privacy and security of the\nreporter of any incident.\n\n## Enforcement Guidelines\n\nCommunity leaders will follow these Community Impact Guidelines in determining\nthe consequences for any action they deem in violation of this Code of Conduct:\n\n### 1. Correction\n\n**Community Impact**: Use of inappropriate language or other behavior deemed\nunprofessional or unwelcome in the community.\n\n**Consequence**: A private, written warning from community leaders, providing\nclarity around the nature of the violation and an explanation of why the\nbehavior was inappropriate. A public apology may be requested.\n\n### 2. Warning\n\n**Community Impact**: A violation through a single incident or series of\nactions.\n\n**Consequence**: A warning with consequences for continued behavior. No\ninteraction with the people involved, including unsolicited interaction with\nthose enforcing the Code of Conduct, for a specified period of time. This\nincludes avoiding interactions in community spaces as well as external channels\nlike social media. Violating these terms may lead to a temporary or permanent\nban.\n\n### 3. Temporary Ban\n\n**Community Impact**: A serious violation of community standards, including\nsustained inappropriate behavior.\n\n**Consequence**: A temporary ban from any sort of interaction or public\ncommunication with the community for a specified period of time. No public or\nprivate interaction with the people involved, including unsolicited interaction\nwith those enforcing the Code of Conduct, is allowed during this period.\nViolating these terms may lead to a permanent ban.\n\n### 4. Permanent Ban\n\n**Community Impact**: Demonstrating a pattern of violation of community\nstandards, including sustained inappropriate behavior, harassment of an\nindividual, or aggression toward or disparagement of classes of individuals.\n\n**Consequence**: A permanent ban from any sort of public interaction within the\ncommunity.\n\n## Attribution\n\nThis Code of Conduct is adapted from the [Contributor Covenant][homepage],\nversion 2.1, available at\n[https://www.contributor-covenant.org/version/2/1/code_of_conduct.html][v2.1].\n\nCommunity Impact Guidelines were inspired by\n[Mozilla's code of conduct enforcement ladder][mozilla coc].\n\nFor answers to common questions about this code of conduct, see the FAQ at\n[https://www.contributor-covenant.org/faq][faq]. Translations are available at\n[https://www.contributor-covenant.org/translations][translations].\n\n[homepage]: https://www.contributor-covenant.org\n[v2.1]: https://www.contributor-covenant.org/version/2/1/code_of_conduct.html\n[mozilla coc]: https://github.com/mozilla/diversity\n[faq]: https://www.contributor-covenant.org/faq\n[translations]: https://www.contributor-covenant.org/translations\n"
  },
  {
    "path": "CONTRIBUTING.md",
    "content": "# Contributing to Boa\n\nBoa welcomes contribution from everyone. Here are the guidelines if you are\nthinking of helping out:\n\n## Contributions\n\nContributions to Boa or its dependencies should be made in the form of GitHub\npull requests. Each pull request will be reviewed by a core contributor\n(someone with permission to land patches) and either landed in the main tree or\ngiven feedback for changes that would be required. All contributions should\nfollow this format.\n\nShould you wish to work on an issue, please claim it first by commenting on\nthe GitHub issue that you want to work on it. This is to prevent duplicated\nefforts from contributors on the same issue.\n\nHead over to [issues][issues] and check for \"good first issue\" labels to find\ngood tasks to start with. If you come across words or jargon that do not make\nsense, please ask!\n\nIf you don't already have Rust installed [_rustup_][rustup] is the recommended\ntool to use. It will install Rust and allow you to switch between _nightly_,\n_stable_ and _beta_. You can also install additional components. In Linux, you\ncan run:\n\n```shell\ncurl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh\n```\n\nThen simply clone this project and `cargo build`.\n\n### Running the compiler\n\nYou can execute a Boa console by running `cargo run`, and you can compile a list\nof JavaScript files by running `cargo run -- file1.js file2.js` and so on.\n\n### Debugging\n\nKnowing how to debug the interpreter should help you resolve problems quite quickly.\nSee [Debugging](./docs/debugging.md).\n\n### Web Assembly\n\nIf you want to develop on the web assembly side you can run `yarn serve` and then go\nto <http://localhost:8080>.\n\n### Setup\n\n#### VSCode Plugins\n\nEither the [Rust (RLS)][rls_vscode] or the [Rust Analyzer][rust-analyzer_vscode]\nextensions are preferred. RLS is easier to set up but some of the development is\nmoving towards Rust Analyzer. Both of these plugins will help you with your Rust\nDevelopment\n\n#### Tasks\n\nThere are some pre-defined tasks in [tasks.json](.vscode/tasks.json)\n\n- Build - shift+cmd/ctrl+b should build and run cargo. You should be able to make changes and run this task.\n- Test - (there is no shortcut, you'll need to make one) - Runs `Cargo Test`.\n  I personally set a shortcut of shift+cmd+option+T (or shift+ctrl+alt+T)\n\n## Testing\n\nBoa provides its own test suite, and can also run the official ECMAScript test suite. To run the Boa test\nsuite, you can just run the normal `cargo test`, and to run the full ECMAScript test suite, you can run it\nwith this command:\n\n```shell\ncargo run --release --bin boa_tester -- run -v 2> error.log\n```\n\nThis will run the test suite in verbose mode (you can remove the `-v` part to run it in non-verbose mode),\nand output nice colorings in the terminal. It will also output any panic information into the `error.log` file.\n\nYou can get some more verbose information that tells you the exact name of each test that is being run, useful\nfor debugging purposes by setting up the verbose flag twice, for example `-vv`. If you want to know the output of\neach test that is executed, you can use the triple verbose (`-vvv`) flag.\n\nIf you want to only run one sub-suite or even one test (to just check if you fixed/broke something specific),\nyou can do it with the `-s` parameter, and then passing the path to the sub-suite or test that you want to run. Note\nthat the `-s` parameter value should be a path relative to the `test262` directory. For example, to run the number\ntype tests, use `-s test/language/types/number`.\n\nFinally, if you're using the verbose flag and running a sub suite with a small number of tests, then the output will\nbe more readable if you disable parallelism with the `-d` flag. All together it might look something like:\n\n```shell\ncargo run --release --bin boa_tester -- run -vv -d -s test/language/types/number 2> error.log\n```\n\nTo save test results for later comparison, use the `-o` flag to specify an output directory:\n\n```shell\ncargo run --release --bin boa_tester -- run -o ./test-results\n```\n\n### Comparing Test Results\n\nYou can compare two test suite runs to see what changed:\n\n```shell\ncargo run --release --bin boa_tester -- compare <base-results> <new-results>\n```\n\nBoth arguments can be either result files (e.g., `latest.json`) or directories containing test results.\nWhen directories are provided, the tester automatically uses the `latest.json` file from each directory.\n\nFor example:\n\n```shell\n# Compare using directories\ncargo run --release --bin boa_tester -- compare ./test-results-main ./test-results-feature\n\n# Compare using explicit files\ncargo run --release --bin boa_tester -- compare ./test-results-main/latest.json ./test-results-feature/latest.json\n```\n\n## Documentation\n\nTo build the development documentation, run:\n\n```shell\ncargo doc --all-features --document-private-items --workspace\n```\n\nThis will also document all the dependencies on the workspace, which could be heavier in size.\nTo only generate documentation for the workspace members, just add the `--no-deps` flag:\n\n```shell\ncargo doc --all-features --document-private-items --workspace --no-deps\n```\n\n## Reading and Understanding the ECMAScript Specification\n\nMany contributions to Boa involve implementing parts of the [ECMAScript\nLanguage Specification](https://tc39.es/ecma262/), which defines how JavaScript\nbehaves. At first, the spec can seem intimidating, but it quickly becomes\neasier to follow once you get familiar with its structure and notation.\n\nThe specification is written in a pseudo-language designed to describe behavior\nwithout being tied to any particular programming language. It introduces some\nimportant concepts:\n\n- **Abstract operations** – general algorithms (i.e. [`IsCallable`](https://tc39.es/ecma262/#sec-iscallable)),\n  which usually map to Rust functions or methods.\n- **Internal slots** – hidden object fields like\n  [`[[Prototype]]`](https://tc39.es/ecma262/#sec-ordinary-object-internal-methods-and-internal-slots)\n  that correspond to private struct or enum fields in Rust, not accessible to\n  JavaScript.\n- **Completion records** – describe how values or exceptions are returned\n  ([link](https://tc39.es/ecma262/#sec-completion-record-specification-type)),\n  and typically map to `JsResult` types in Rust.\n- **Symbols `?` and `!`** – `? Foo(...)` propagates exceptions mapped to\n  propagate `?` operator in rust, while `! Foo(...)` are infallible operations\n  and are usually mapped to\n  [`Result::expect()`](https://doc.rust-lang.org/std/result/enum.Result.html#method.expect)\n  call.\n\nFor an in-depth introduction to these concepts and more, check out [V8’s “Understanding\nthe ECMAScript spec”\nseries](https://v8.dev/blog/tags/understanding-ecmascript), starting with [Part\n1](https://v8.dev/blog/understanding-ecmascript-part-1).\n\nWhen implementing the spec in Boa, try to map your code to the corresponding\nspec steps whenever possible, and indicate in comments which steps are implemented.\nThis makes the code easier to understand, ensures it aligns with the\nspecification, and helps reviewers and future contributors follow the logic.\n\nIf a spec step does not map directly because of Rust limitations or performance\nreasons, just add a note in the code explaining the difference. Being clear\nabout these cases helps others understand your implementation while still\nfollowing the spec as closely as possible.\n\nFor examples of how to implement the specification, check out the built-in\nimplementations in Boa\n[here](https://github.com/boa-dev/boa/tree/main/core/engine/src/builtins).\n\nIf anything in the specification is confusing, don’t hesitate to ask in the\n[Boa Matrix](https://matrix.to/#/#boa:matrix.org) channel.\n\n## Learning Resources\n\nFor contributors looking to learn JavaScript and how it works, check out the [Mozilla Developer Guided Tours](https://www.youtube.com/playlist?list=PLo3w8EB99pqJVPhmYbYdInBvAGarDavh-).\n\n## Communication\n\nWe have a Matrix space, feel free to ask questions here:\n<https://matrix.to/#/#boa:matrix.org>\n\n[issues]: https://github.com/boa-dev/boa/issues\n[rustup]: https://rustup.rs/\n[rls_vscode]: https://marketplace.visualstudio.com/items?itemName=rust-lang.rust\n[rust-analyzer_vscode]: https://marketplace.visualstudio.com/items?itemName=matklad.rust-analyzer\n"
  },
  {
    "path": "Cargo.toml",
    "content": "[workspace]\nresolver = \"2\"\nmembers = [\n    # CORE\n    \"core/*\",\n    # FFI\n    \"ffi/*\",\n    # TESTS\n    \"tests/*\",\n    # TOOLS\n    \"tools/*\",\n    # OTHERS\n    \"examples\",\n    \"cli\",\n    # UTILS\n    \"utils/*\",\n    # BENCHES\n    \"benches\",\n]\n\nexclude = [\n    \"tests/fuzz\", # Does weird things on Windows tests\n    \"tests/src\", # Just a hack to have fuzz inside tests\n    \"tests/wpt\", # Should not run WPT by default.\n]\n\n[workspace.package]\nedition = \"2024\"\nversion = \"1.0.0-dev\"\nrust-version = \"1.91.0\"\nauthors = [\"boa-dev\"]\nrepository = \"https://github.com/boa-dev/boa\"\nlicense = \"Unlicense OR MIT\"\ndescription = \"Boa is a Javascript lexer, parser and compiler written in Rust. Currently, it has support for some of the language.\"\n\n[workspace.dependencies]\n\n# Repo Crates\nboa_ast = { version = \"~1.0.0-dev\", path = \"core/ast\" }\nboa_engine = { version = \"~1.0.0-dev\", path = \"core/engine\", default-features = false }\nboa_gc = { version = \"~1.0.0-dev\", path = \"core/gc\" }\nboa_icu_provider = { version = \"~1.0.0-dev\", path = \"core/icu_provider\" }\nboa_interner = { version = \"~1.0.0-dev\", path = \"core/interner\" }\nboa_macros = { version = \"~1.0.0-dev\", path = \"core/macros\" }\nboa_parser = { version = \"~1.0.0-dev\", path = \"core/parser\" }\nboa_runtime = { version = \"~1.0.0-dev\", path = \"core/runtime\" }\nboa_string = { version = \"~1.0.0-dev\", path = \"core/string\" }\n\n# Utility Repo Crates\ntag_ptr = { version = \"~0.1.0\", path = \"utils/tag_ptr\" }\nsmall_btree = { version = \"~0.1.0\", path = \"utils/small_btree\" }\n\n# Shared deps\narbitrary = \"1\"\nbase64 = \"0.22.1\"\nbitflags = \"2.11.0\"\nclap = \"4.5.59\"\ncolored = \"3.1.1\"\ncow-utils = \"0.1.3\"\nfast-float2 = \"0.2.3\"\nhashbrown = \"0.16.1\"\nhttp = { version = \"1.4.0\" }\niana-time-zone = \"0.1.65\"\nindexmap = { version = \"2.13.0\", default-features = false }\nindoc = \"2.0.7\"\nitoa = \"1.0.18\"\njemallocator = \"0.5.4\"\nmimalloc-safe = \"0.1.56\"\nlz4_flex = \"0.13.0\"\nnum-bigint = \"0.4.6\"\nnum-traits = \"0.2.19\"\nonce_cell = { version = \"1.21.3\", default-features = false }\noneshot = \"0.2.1\"\nphf = { version = \"0.13.1\", default-features = false }\npollster = \"0.4.0\"\nregex = \"1.12.3\"\nregress = { version = \"0.11.0\", features = [\"utf16\"] }\nreqwest = { version = \"0.13.2\", default-features = false, features = [\"rustls-no-provider\"] }\nrustls = { version = \"0.23.37\", default-features = false, features = [\"ring\"] }\nrustc-hash = { version = \"2.1.1\", default-features = false }\nserde_json = \"1.0.149\"\nserde = \"1.0.219\"\nstatic_assertions = \"1.1.0\"\ntextwrap = \"0.16.2\"\nthin-vec = \"0.2.14\"\ntime = { version = \"0.3.47\", default-features = false, features = [\n    \"local-offset\",\n    \"large-dates\",\n    \"parsing\",\n    \"formatting\",\n    \"macros\",\n] }\nlog = \"0.4.29\"\nsimple_logger = \"5.1.0\"\ncargo_metadata = \"0.23.1\"\ntrybuild = \"1.0.116\"\nrayon = \"1.10.0\"\ntoml = \"1.0.7\"\ncolor-eyre = \"0.6.3\"\ncomfy-table = \"7.2.2\"\nserde_repr = \"0.1.20\"\nbus = \"2.4.1\"\nwasm-bindgen = { version = \"0.2.97\", default-features = false }\ngetrandom = { version = \"0.4.2\", default-features = false }\nconsole_error_panic_hook = \"0.1.7\"\nwasm-bindgen-test = \"0.3.64\"\nsmol = \"2.0.2\"\nrustyline = { version = \"17.0.2\", default-features = false }\ndhat = \"0.3.3\"\nquote = \"1.0.45\"\nsyn = { version = \"2.0.116\", default-features = false }\nproc-macro2 = \"1.0\"\nsynstructure = \"0.13\"\nmeasureme = \"12.0.3\"\npaste = \"1.0\"\nrand = \"0.10.0\"\nnum-integer = \"0.1.46\"\nryu-js = \"1.0.2\"\ntap = \"1.0.1\"\nthiserror = { version = \"2.0.18\", default-features = false }\ndashmap = \"6.1.0\"\nnum_enum = \"0.7.6\"\nitertools = { version = \"0.14.0\", default-features = false }\nportable-atomic = \"1.13.1\"\nbytemuck = { version = \"1.25.0\", default-features = false }\narrayvec = \"0.7.6\"\nintrusive-collections = \"0.10.0\"\ncfg-if = \"1.0.4\"\neither = \"1.15.0\"\nsys-locale = \"0.3.2\"\ntimezone_provider = { version = \"0.2.0\" }\ntemporal_rs = { version = \"0.2.0\", default-features = false, features = [\"float64_representable_durations\"] }\nweb-time = \"1.1.0\"\ncriterion = \"0.8.1\"\nfloat-cmp = \"0.10.0\"\nfutures-lite = \"2.6.1\"\ntest-case = \"3.3.1\"\nrstest = \"0.26.1\"\nurl = \"2.5.8\"\ntokio = { version = \"1.50.0\", default-features = false }\nfutures-concurrency = \"7.7.1\"\ndynify = \"0.1.2\"\nfutures-channel = \"0.3.32\"\naligned-vec = \"0.6.4\"\ntemp-env = \"0.3.6\"\nstrum = { version = \"0.28\", features = [\"derive\"] }\nhusky-rs = \"0.3.2\"\nasync-channel = \"2.5.0\"\n\n# ICU4X\n\nicu_provider = { version = \"~2.1.1\", default-features = false }\nicu_locale = { version = \"~2.1.1\", default-features = false }\nicu_locale_core = { version = \"~2.1.1\", default-features = false }\nicu_datetime = { version = \"~2.1.1\", default-features = false }\nicu_time = { version = \"~2.1.1\", default-features = false }\nicu_calendar = { version = \"~2.1.1\", default-features = false }\nicu_collator = { version = \"~2.1.1\", default-features = false }\nicu_plurals = { version = \"~2.1.1\", default-features = false }\nicu_list = { version = \"~2.1.1\", default-features = false }\nicu_casemap = { version = \"~2.1.1\", default-features = false }\nicu_segmenter = { version = \"~2.1.2\", default-features = false }\nicu_provider_export = { version = \"~2.1.1\", default-features = false }\nicu_provider_source = { version = \"~2.1.2\", default-features = false }\nicu_provider_adapters = { version = \"~2.1.1\", default-features = false }\nicu_provider_blob = { version = \"~2.1.1\", default-features = false }\nicu_properties = { version = \"~2.1.2\", default-features = true }\nicu_normalizer = { version = \"~2.1.1\", default-features = false }\nicu_decimal = { version = \"~2.1.1\", default-features = false }\nwriteable = \"~0.6.2\"\ntinystr = \"~0.8.2\"\nyoke = \"~0.8.1\"\nzerofrom = \"~0.1.6\"\nfixed_decimal = \"~0.7.1\"\n\n[workspace.metadata.workspaces]\nallow_branch = \"main\"\n\n# The ci profile, designed to reduce size of target directory\n[profile.ci]\ninherits = \"dev\"\ndebug = false\nincremental = false\n\n# The release profile, used for `cargo build --release`.\n[profile.release]\n# Enables \"fat\" LTO, for faster release builds\nlto = \"fat\"\n# Makes sure that all code is compiled together, for LTO\ncodegen-units = 1\n# Strips debug information and symbols from the binary, reducing its size\nstrip = \"symbols\"\n\n[profile.release-dbg]\ninherits = \"release\"\ndebug = true\nstrip = \"none\"\n\n# The test profile, used for `cargo test`.\n[profile.test]\n# Enables thin local LTO and some optimizations.\nopt-level = 1\n\n# The benchmark profile, used for `cargo bench`.\n[profile.bench]\n# Enables \"fat\" LTO, for faster benchmark builds\nlto = \"fat\"\n# Makes sure that all code is compiled together, for LTO\ncodegen-units = 1\n\n[workspace.lints.rust]\n# rustc lint groups https://doc.rust-lang.org/rustc/lints/groups.html\nwarnings = \"warn\"\nfuture_incompatible = { level = \"warn\", priority = -1 }\nlet_underscore = { level = \"warn\", priority = -1 }\nnonstandard_style = { level = \"warn\", priority = -1 }\nrust_2018_compatibility = { level = \"warn\", priority = -1 }\nrust_2018_idioms = { level = \"warn\", priority = -1 }\nrust_2021_compatibility = { level = \"warn\", priority = -1 }\nunused = { level = \"warn\", priority = -1 }\n\n# rustc allowed-by-default lints https://doc.rust-lang.org/rustc/lints/listing/allowed-by-default.html\nmissing_docs = \"warn\"\nmacro_use_extern_crate = \"warn\"\nmeta_variable_misuse = \"warn\"\nmissing_abi = \"warn\"\nmissing_copy_implementations = \"warn\"\nmissing_debug_implementations = \"warn\"\nnon_ascii_idents = \"warn\"\nnoop_method_call = \"warn\"\nsingle_use_lifetimes = \"warn\"\ntrivial_casts = \"warn\"\ntrivial_numeric_casts = \"warn\"\nunreachable_pub = \"warn\"\nunsafe_op_in_unsafe_fn = \"warn\"\nunused_crate_dependencies = \"warn\"\nunused_import_braces = \"warn\"\nunused_lifetimes = \"warn\"\nunused_qualifications = \"warn\"\nvariant_size_differences = \"warn\"\n\n[workspace.lints.rustdoc]\n# rustdoc lints https://doc.rust-lang.org/rustdoc/lints.html\nbroken_intra_doc_links = \"warn\"\nprivate_intra_doc_links = \"warn\"\nmissing_crate_level_docs = \"warn\"\nprivate_doc_tests = \"warn\"\ninvalid_codeblock_attributes = \"warn\"\ninvalid_rust_codeblocks = \"warn\"\nbare_urls = \"warn\"\n\n[workspace.lints.clippy]\n# clippy allowed by default\ndbg_macro = \"warn\"\nprint_stdout = \"warn\"\nprint_stderr = \"warn\"\n\n# clippy categories https://doc.rust-lang.org/clippy/\nall = { level = \"warn\", priority = -1 }\ncorrectness = { level = \"warn\", priority = -1 }\nsuspicious = { level = \"warn\", priority = -1 }\nstyle = { level = \"warn\", priority = -1 }\ncomplexity = { level = \"warn\", priority = -1 }\nperf = { level = \"warn\", priority = -1 }\npedantic = { level = \"warn\", priority = -1 }\n"
  },
  {
    "path": "LICENSE-MIT",
    "content": "MIT License\n\nCopyright (c) 2019 Jason Williams\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n"
  },
  {
    "path": "LICENSE-UNLICENSE",
    "content": "This is free and unencumbered software released into the public domain.\n\nAnyone is free to copy, modify, publish, use, compile, sell, or\ndistribute this software, either in source code form or as a compiled\nbinary, for any purpose, commercial or non-commercial, and by any\nmeans.\n\nIn jurisdictions that recognize copyright laws, the author or authors\nof this software dedicate any and all copyright interest in the\nsoftware to the public domain. We make this dedication for the benefit\nof the public at large and to the detriment of our heirs and\nsuccessors. We intend this dedication to be an overt act of\nrelinquishment in perpetuity of all present and future rights to this\nsoftware under copyright law.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\nEXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\nMERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.\nIN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR\nOTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,\nARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR\nOTHER DEALINGS IN THE SOFTWARE.\n\nFor more information, please refer to <http://unlicense.org>\n"
  },
  {
    "path": "Makefile.toml",
    "content": "extend = [\n    { path = \"./make/ci.toml\"}\n]\n\n[config]\ndefault_to_workspace = false\n\n[tasks.run-ci]\ndescription = \"Run Boa CI locally\"\nrun_task.name = [\n    \"ci-fmt-check\",\n    \"ci-lint-all-features\",\n    \"ci-lint-no-features\"\n]\n\n[tasks.format]\ninstall_crate = \"rustfmt\"\ncommand = \"cargo\"\nargs = [\"fmt\", \"--all\", \"--\", \"--emit=files\"]\n\n[tasks.test]\ncommand = \"cargo\"\nargs = [\"test\", \"${@}\"]\n\n[tasks.test262]\ncommand = \"cargo\"\nargs = [\"run\", \"@@remove-empty(RELEASE_ARG)\", \"--bin\", \"boa_tester\", \"--\", \"run\", \"${@}\"]\n\n[tasks.bench-js]\ncondition = { env_set = [ \"BOA_DATA_ROOT\" ] }\ncommand = \"cargo\"\nargs = [\"run\", \"--bin\", \"boa\", \"--release\", \"--\", \"@@remove-empty(BOA_DATA_ROOT)bench/bench-v8/combined.js\"]\n\n[tasks.insta-test]\ninstall_crate = \"cargo-insta\"\ncommand = \"cargo\"\nargs = [\"insta\", \"test\", \"-p\", \"insta-bytecode\", \"--review\"]\n\n[tasks.insta-review]\ninstall_crate = \"cargo-insta\"\ncommand = \"cargo\"\nargs = [\"insta\", \"review\", \"--manifest-path\", \"./tests/insta-bytecode/Cargo.toml\"]\n\n[env.development]\nRELEASE_ARG=\"\"\n\n[env.profiling]\nRELEASE_ARG=\"--profile=release-dbg\"\n\n[env.production]\nRELEASE_ARG=\"--release\"\n"
  },
  {
    "path": "PR_MESSAGE_EXPECT_EXPRESSION.md",
    "content": "# fix(parser): replace expect_expression panic with try_into_expression error handling\n\n## Summary\n\nReplaces all uses of `expect_expression()` with `try_into_expression()` in the parser, and removes the panicking `expect_expression()` method. When the parser encounters arrow-function parameter list syntax in a context that requires an expression (e.g. conditional operator `a ? b : c`, call expression `foo()`, optional chaining `a?.b`), it now returns a recoverable parse error instead of panicking.\n\n## Motivation\n\nThe `FormalParameterListOrExpression` type represents the grammar ambiguity between `(a, b)` as a comma expression vs `(a, b)` as arrow-function parameters. In contexts like the conditional operator (`x ? y : z`), call expressions (`f()`), and optional chaining (`a?.b`), the left-hand side must be an expression—arrow params are invalid. Previously, call sites used `expect_expression()`, which panics with \"Unexpected arrow-function arguments\" when the parser produced a `FormalParameterList` instead of an `Expression`. This can occur with edge-case input such as `(a) ? 1 : 2` when `(a)` is parsed as a single arrow param. For Boa as an embedded engine or in tooling, panics are unacceptable; the host expects a parse error. The `try_into_expression()` method already existed and returns `Err(Error::General { ... })` with a helpful message (\"invalid arrow-function arguments (parentheses around the arrow-function may help)\"). Replacing `expect_expression()` with `try_into_expression()?` propagates that error instead of panicking, aligning with Boa's stability goals.\n\n## Changes\n\n| Category     | Description                                                                                           |\n| ------------ | ----------------------------------------------------------------------------------------------------- |\n| **Replaced** | `lhs.expect_expression()` with `lhs.try_into_expression()?` in conditional expression parser          |\n| **Replaced** | `member.expect_expression()` with `member.try_into_expression()?` in left-hand side (call expression) |\n| **Replaced** | `lhs.expect_expression()` with `lhs.try_into_expression()?` in left-hand side (optional expression)   |\n| **Removed**  | `expect_expression()` method from `FormalParameterListOrExpression` to prevent future misuse          |\n\n## Technical Details\n\n- **Files modified:** `core/parser/src/parser/expression/assignment/conditional.rs`, `core/parser/src/parser/expression/left_hand_side/mod.rs`, `core/parser/src/parser/expression/fpl_or_exp.rs`\n- **Lines changed:** ~15 lines across 3 files\n- **Behavioral impact:** Invalid input that previously caused a panic now produces `Err(Error::General { message: \"invalid arrow-function arguments (parentheses around the arrow-function may help)\", position })`. Valid input is unchanged.\n\n## Testing\n\n- [x] `cargo test -p boa_parser` — all 296 tests pass\n- [x] `cargo test -p boa_engine --lib` — all 921 tests pass\n- [x] `cargo clippy` — no warnings\n"
  },
  {
    "path": "README.md",
    "content": "# Boa\n\n<p align=\"center\">\n  <a href=\"https://boajs.dev/\">\n    <picture>\n      <source\n        media=\"(prefers-color-scheme: dark)\"\n        srcset=\"https://raw.githubusercontent.com/boa-dev/boa/refs/tags/v0.21/assets/logo_yellow.svg\">\n      <source\n        media=\"(prefers-color-scheme: light)\"\n        srcset=\"https://raw.githubusercontent.com/boa-dev/boa/refs/tags/v0.21/assets/logo_black.svg\">\n      <img\n        alt=\"Boa logo\"\n        style=\"width:auto;\"\n        src=\"https://raw.githubusercontent.com/boa-dev/boa/refs/tags/v0.21/assets/logo.png\">\n    </picture>\n    </a>\n</p>\n\nBoa is an experimental JavaScript lexer, parser and interpreter written in Rust 🦀, it has support for **more** than 90% of the latest ECMAScript specification. We continuously improve the conformance to keep up with the ever-evolving standard.\n\n[![Build Status][build_badge]][build_link]\n[![codecov](https://codecov.io/gh/boa-dev/boa/branch/main/graph/badge.svg)](https://codecov.io/gh/boa-dev/boa)\n[![Crates.io](https://img.shields.io/crates/v/boa_engine.svg)](https://crates.io/crates/boa_engine)\n[![Docs.rs](https://docs.rs/boa_engine/badge.svg)](https://docs.rs/boa_engine)\n[![Discord](https://img.shields.io/discord/595323158140158003?logo=discord)](https://discord.gg/tUFFk9Y)\n[![Matrix](https://img.shields.io/matrix/boa:matrix.org?logo=matrix)](https://matrix.to/#/#boa:matrix.org)\n\n[build_badge]: https://github.com/boa-dev/boa/actions/workflows/rust.yml/badge.svg?event=push&branch=main\n[build_link]: https://github.com/boa-dev/boa/actions/workflows/rust.yml?query=event%3Apush+branch%3Amain\n\n## ⚡️ Live Demo (Wasm)\n\nTry out the engine now at the live Wasm playground [here](https://boajs.dev/playground)!\n\nPrefer a CLI? Feel free to try out `boa_cli`!\n\n## 📦 Crates\n\nBoa currently publishes and actively maintains the following crates:\n\n- **`boa_ast`** - Boa's ECMAScript Abstract Syntax Tree\n- **`boa_cli`** - Boa's CLI && REPL implementation\n- **`boa_engine`** - Boa's implementation of ECMAScript builtin objects and\n  execution\n- **`boa_gc`** - Boa's garbage collector\n- **`boa_interner`** - Boa's string interner\n- **`boa_parser`** - Boa's lexer and parser\n- **`boa_icu_provider`** - Boa's ICU4X data provider\n- **`boa_runtime`** - Boa's WebAPI features\n- **`boa_string`** - Boa's ECMAScript string implementation.\n- **`tag_ptr`** - Utility library that enables a pointer to be associated with a tag of type `usize`.\n\n> [!NOTE]\n>\n> The `Boa` and `boa_unicode` crates are deprecated.\n\n## 🚀 Example\n\nTo start using Boa simply add the `boa_engine` crate to your `Cargo.toml`:\n\n```toml\n[dependencies]\nboa_engine = \"0.21.0\"\n```\n\nThen in `main.rs`, copy the below:\n\n```rust\nuse boa_engine::{Context, Source, JsResult};\n\nfn main() -> JsResult<()> {\n  let js_code = r#\"\n      let two = 1 + 1;\n      let definitely_not_four = two + \"2\";\n\n      definitely_not_four\n  \"#;\n\n  // Instantiate the execution context\n  let mut context = Context::default();\n\n  // Parse the source code\n  let result = context.eval(Source::from_bytes(js_code))?;\n\n  println!(\"{}\", result.display());\n\n  Ok(())\n}\n\n```\n\nNow, all that's left to do is `cargo run`.\n\nCongrats! You've executed your first JavaScript code using Boa!\n\n## 🔎 Documentation\n\nFor more information on Boa's API, feel free to check out our documentation.\n\n[**API Documentation**](https://docs.rs/boa_engine/latest/boa_engine/)\n\n## 🏅 Conformance\n\nTo know more details about Boa's conformance surrounding the _ECMAScript_ specification,\nyou can check out our _ECMAScript Test262_ test suite results [here](https://boajs.dev/conformance).\n\n## 🪚 Contributing\n\nPlease, check the [CONTRIBUTING.md](CONTRIBUTING.md) file to know how to\ncontribute in the project. You will need Rust installed and an editor. We have\nsome configurations ready for VSCode.\n\n### 🐛 Debugging\n\nCheck [debugging.md](./docs/debugging.md) for more info on debugging.\n\n### 🕸 Web Assembly\n\n> [!IMPORTANT]\n>\n> This only applies to `wasm32-unknown-unknown` target,\n> `WASI` and `Emscripten` target variants are handled automatically.\n\n- Enable the `js` feature flag.\n- Set `RUSTFLAGS='--cfg getrandom_backend=\"wasm_js\"'`\n\nThe `rustflags` can also be set by adding a `.cargo/config.toml` file in the project root directory:\n\n```toml\n[target.wasm32-unknown-unknown]\nrustflags = '--cfg getrandom_backend=\"wasm_js\"'\n```\n\nFor more information see: [`getrandom` WebAssembly Support][getrandom-webassembly-support]\n\n[getrandom-webassembly-support]: https://docs.rs/getrandom/latest/getrandom/index.html#webassembly-support\n\n## ⚙️ Usage\n\n- Clone this repo.\n- Run with `cargo run -- test.js` in the project root directory where `test.js` is a path to an existing JS file with any valid JS code.\n- If any JS doesn't work then it's a bug. Please raise an [issue](https://github.com/boa-dev/boa/issues/)!\n\n### Example\n\n![Example](docs/img/latestDemo.gif)\n\n### Command-line Options\n\n```txt\nUsage: boa [OPTIONS] [FILE]...\n\nArguments:\n  [FILE]...  The JavaScript file(s) to be evaluated\n\nOptions:\n      --strict                        Run in strict mode\n  -a, --dump-ast [<FORMAT>]           Dump the AST to stdout with the given format [possible values: debug, json, json-pretty]\n  -t, --trace                         Dump the AST to stdout with the given format\n      --vi                            Use vi mode in the REPL\n  -O, --optimize\n      --optimizer-statistics\n      --flowgraph [<FORMAT>]          Generate instruction flowgraph. Default is Graphviz [possible values: graphviz, mermaid]\n      --flowgraph-direction <FORMAT>  Specifies the direction of the flowgraph. Default is top-top-bottom [possible values: top-to-bottom, bottom-to-top, left-to-right, right-to-left]\n      --debug-object                  Inject debugging object `$boa`\n  -m, --module                        Treats the input files as modules\n  -r, --root <ROOT>                   Root path from where the module resolver will try to load the modules [default: .]\n  -h, --help                          Print help (see more with '--help')\n  -V, --version                       Print version\n```\n\n## 🧭 Roadmap\n\nSee [Milestones](https://github.com/boa-dev/boa/milestones).\n\n## 📊 Benchmarks\n\nThe current benchmarks are taken from v8's benchmark that you can find [here][boa-benchmarks]. You can also view the results of nightly benchmark runs comparing Boa with other JavaScript engines [here](https://boajs.dev/benchmarks).\n\nIf you wish to run the benchmarks locally, then run Boa in release using the `combined.js` script which contains all the sub-benchmarks in the `bench-v8` directory.\n\n```bash\ncargo run --release -p boa_cli -- bench-v8/combined.js\n```\n\n> [!TIP]\n>\n> If you'd like to run only a subset of the benchmarks, you can modify the `Makefile` located in the [`bench-v8` directory][boa-benchmarks].\n> Comment out the benchmarks you don't want to include, then run `make`. After that, you can run Boa using the same command as above.\n\n[boa-benchmarks]: https://github.com/boa-dev/data/tree/benchmarks/bench\n\n## 🧠 Profiling\n\nSee [Profiling](./docs/profiling.md).\n\n## 📆 Changelog\n\nSee [CHANGELOG.md](./CHANGELOG.md).\n\n## 💬 Communication\n\nFeel free to contact us on [Matrix](https://matrix.to/#/#boa:matrix.org) if you have any questions.\nContributor discussions take place on the same Matrix Space if you're interested in contributing.\nWe also have a [Discord](https://discord.gg/tUFFk9Y) for any questions or issues.\n\n## ⚖️ License\n\nThis project is licensed under the [Unlicense](./LICENSE-UNLICENSE) or [MIT](./LICENSE-MIT) licenses, at your option.\n"
  },
  {
    "path": "SECURITY.md",
    "content": "# Security Policy\n\n## Supported Versions\n\nUntil we reach the 1.0 version, we only support the latest 0.x release and the current `main`\nbranch. You can find the latest release of Boa in the [GitHub releases][gh_releases] page or\non [crates.io][crate].\n\n[gh_releases]: https://github.com/boa-dev/boa/releases\n[crate]: https://crates.io/crates/boa_engine/versions\n\n## Reporting a Vulnerability\n\nIf you find any potential vulnerability, join our [Matrix](https://matrix.to/#/#boa:matrix.org) space\nand contact anyone who is an admin. Explain how to trigger the vulnerability, where can it be found\nand any recommendation you might have to fix it.\n"
  },
  {
    "path": "benches/Cargo.toml",
    "content": "[package]\nname = \"boa_benches\"\nversion = \"0.1.0\"\nedition = \"2024\"\npublish = false\n\n[dependencies]\nboa_engine = { workspace = true, features = [\"intl_bundled\"] }\nboa_runtime.workspace = true\n\n[dev-dependencies]\ncriterion.workspace = true\nwalkdir = \"2\"\n\n[target.x86_64-unknown-linux-gnu.dev-dependencies]\njemallocator.workspace = true\n\n[[bench]]\nname = \"scripts\"\nharness = false\n"
  },
  {
    "path": "benches/benches/scripts.rs",
    "content": "#![allow(unused_crate_dependencies, missing_docs)]\nuse boa_engine::{\n    Context, JsValue, Source, js_string, optimizer::OptimizerOptions, script::Script,\n};\nuse criterion::{Criterion, criterion_group, criterion_main};\nuse std::{path::Path, time::Duration};\n\n#[cfg(all(target_arch = \"x86_64\", target_os = \"linux\", target_env = \"gnu\"))]\n#[global_allocator]\nstatic ALLOC: jemallocator::Jemalloc = jemallocator::Jemalloc;\n\nfn bench_scripts(c: &mut Criterion) {\n    let scripts_dir = Path::new(env!(\"CARGO_MANIFEST_DIR\")).join(\"scripts\");\n\n    let scripts: Vec<_> = walkdir::WalkDir::new(&scripts_dir)\n        .into_iter()\n        .filter_map(|e| e.ok())\n        .filter(|e| {\n            let path = e.path();\n            path.extension().is_some_and(|ext| ext == \"js\")\n                && path\n                    .file_name()\n                    .is_some_and(|base| !base.display().to_string().starts_with(\"_\"))\n        })\n        .collect();\n\n    for entry in scripts {\n        let path = entry.path();\n        let code = std::fs::read_to_string(path).unwrap();\n\n        // Create a nice benchmark name from the relative path\n        let rel_path = path.strip_prefix(&scripts_dir).unwrap().with_extension(\"\");\n        let name = rel_path.display().to_string();\n\n        let mut group = c.benchmark_group(&name);\n        // Use reduced sample size for slow benchmarks (e.g., v8-benches)\n        if rel_path.starts_with(\"v8-benches\") {\n            group.sample_size(10);\n            group.measurement_time(Duration::from_secs(5));\n        }\n\n        let context = &mut Context::default();\n\n        // Disable optimizations\n        context.set_optimizer_options(OptimizerOptions::empty());\n\n        // Register runtime for console.log support\n        boa_runtime::register(\n            boa_runtime::extensions::ConsoleExtension(boa_runtime::NullLogger),\n            None,\n            context,\n        )\n        .expect(\"Runtime registration failed\");\n\n        // Parse and compile once, outside the benchmark loop\n        let script = Script::parse(Source::from_bytes(&code), None, context).unwrap();\n        script.codeblock(context).unwrap();\n\n        // Evaluate once to define the main function\n        script.evaluate(context).unwrap();\n\n        // Get the main function\n        let function = context\n            .global_object()\n            .get(js_string!(\"main\"), context)\n            .unwrap_or_else(|_| panic!(\"No main function defined in script: {}\", path.display()))\n            .as_callable()\n            .unwrap_or_else(|| panic!(\"'main' is not a function in script: {}\", path.display()))\n            .clone();\n\n        group.bench_function(\"Execution\", |b| {\n            b.iter(|| function.call(&JsValue::undefined(), &[], context));\n        });\n        group.finish();\n    }\n}\n\ncriterion_group!(benches, bench_scripts);\ncriterion_main!(benches);\n"
  },
  {
    "path": "benches/scripts/basic/call-loop.js",
    "content": "function f() {}\n\nfunction main() {\n  for (let i = 0; i < 100_000; i++) {\n    f();\n  }\n}\n"
  },
  {
    "path": "benches/scripts/basic/closure.js",
    "content": "function outer() {\n  let x = 1;\n  function middle() {\n    let y = 2;\n    function inner() {\n      return x + y;\n    }\n    return inner;\n  }\n  return middle;\n}\nlet f = outer()();\n\nfunction main() {\n  for (let n = 0; n < 10_000_000; n++) {\n    f();\n  }\n}\n"
  },
  {
    "path": "benches/scripts/basic/nested-loop.js",
    "content": "const n = 1_000;\n\nfunction main() {\n  for (let i = 0; i < n; i++) {\n    for (let j = 0; j < n; j++) {}\n  }\n}\n"
  },
  {
    "path": "benches/scripts/closures/create.js",
    "content": "// Measures closure creation overhead: allocation of function objects\n// and captured variable environments inside a hot loop.\nconst kIterationCount = 100_000;\n\nfunction main() {\n  let sum = 0;\n  for (let i = 0; i < kIterationCount; i++) {\n    const x = i;\n    const y = i * 2;\n    const closure = (z) => x + y + z;\n    sum += closure(i + 1);\n  }\n  return sum;\n}\n"
  },
  {
    "path": "benches/scripts/closures/invoke.js",
    "content": "// Measures closure invocation overhead: closures are created once,\n// then called repeatedly to isolate dispatch and variable lookup cost.\nconst kIterationCount = 100_000;\n\nfunction makeCounter(start) {\n  let count = start;\n  return {\n    increment: (n) => {\n      count += n;\n      return count;\n    },\n    decrement: (n) => {\n      count -= n;\n      return count;\n    },\n    value: () => count,\n  };\n}\n\nconst counterA = makeCounter(0);\nconst counterB = makeCounter(1000);\nconst counterC = makeCounter(-500);\n\nfunction main() {\n  let sum = 0;\n  for (let i = 0; i < kIterationCount; i++) {\n    sum += counterA.increment(1);\n    sum += counterB.decrement(1);\n    sum += counterC.value();\n  }\n  return sum;\n}\n"
  },
  {
    "path": "benches/scripts/intl/collator-compare.js",
    "content": "const kIterationCount = 1000;\nconst collator = new Intl.Collator(\"en\");\n\nfunction main() {\n  for (let i = 0; i < kIterationCount; i++) {\n    collator.compare(\"apple\", \"banana\");\n  }\n}\n"
  },
  {
    "path": "benches/scripts/intl/collator-construction.js",
    "content": "const kIterationCount = 100;\nconst locales = [\"en\", \"de\", \"ja\", \"ar\", \"fr\", \"es\", \"zh\"];\n\nfunction main() {\n  for (let i = 0; i < kIterationCount; i++) {\n    for (const locale of locales) {\n      new Intl.Collator(locale);\n    }\n  }\n}\n"
  },
  {
    "path": "benches/scripts/intl/datetimeformat-construction.js",
    "content": "const kIterationCount = 100;\nconst locales = [\"en\", \"de\", \"ja\", \"ar\", \"fr\", \"es\", \"zh\"];\n\nfunction main() {\n  for (let i = 0; i < kIterationCount; i++) {\n    for (const locale of locales) {\n      new Intl.DateTimeFormat(locale);\n    }\n  }\n}\n"
  },
  {
    "path": "benches/scripts/intl/datetimeformat-format.js",
    "content": "const kIterationCount = 1000;\nconst dtf = new Intl.DateTimeFormat(\"en\");\nconst date = new Date(2024, 0, 1);\n\nfunction main() {\n  for (let i = 0; i < kIterationCount; i++) {\n    dtf.format(date);\n  }\n}\n"
  },
  {
    "path": "benches/scripts/intl/datetimeformat-with-options.js",
    "content": "const kIterationCount = 100;\n\nfunction main() {\n  for (let i = 0; i < kIterationCount; i++) {\n    new Intl.DateTimeFormat(\"en\", {\n      year: \"numeric\",\n      month: \"long\",\n      day: \"numeric\",\n    });\n    new Intl.DateTimeFormat(\"en\", {\n      hour: \"2-digit\",\n      minute: \"2-digit\",\n      second: \"2-digit\",\n    });\n  }\n}\n"
  },
  {
    "path": "benches/scripts/intl/datetimeformat_resolved_options.js",
    "content": "const kIterationCount = 1000;\nconst fmt = new Intl.DateTimeFormat(\"en-US\", {\n  dateStyle: \"full\",\n  timeStyle: \"short\",\n  timeZone: \"UTC\",\n});\n\nfunction main() {\n  for (let i = 0; i < kIterationCount; i++) {\n    fmt.resolvedOptions();\n  }\n}\n"
  },
  {
    "path": "benches/scripts/intl/listformat-construction.js",
    "content": "const kIterationCount = 100;\nconst locales = [\"en\", \"de\", \"ja\", \"ar\", \"fr\", \"es\", \"zh\"];\n\nfunction main() {\n  for (let i = 0; i < kIterationCount; i++) {\n    for (const locale of locales) {\n      new Intl.ListFormat(locale);\n    }\n  }\n}\n"
  },
  {
    "path": "benches/scripts/intl/listformat-format.js",
    "content": "const kIterationCount = 1000;\nconst lf = new Intl.ListFormat(\"en\");\nconst list = [\"apple\", \"banana\", \"cherry\"];\n\nfunction main() {\n  for (let i = 0; i < kIterationCount; i++) {\n    lf.format(list);\n  }\n}\n"
  },
  {
    "path": "benches/scripts/intl/numberformat-construction.js",
    "content": "const kIterationCount = 100;\nconst locales = [\"en\", \"de\", \"ja\", \"ar\", \"fr\", \"es\", \"zh\"];\n\nfunction main() {\n  for (let i = 0; i < kIterationCount; i++) {\n    for (const locale of locales) {\n      new Intl.NumberFormat(locale);\n    }\n  }\n}\n"
  },
  {
    "path": "benches/scripts/intl/numberformat-different-options.js",
    "content": "const kIterationCount = 100;\n\nfunction main() {\n  for (let i = 0; i < kIterationCount; i++) {\n    new Intl.NumberFormat(\"en\", { style: \"currency\", currency: \"USD\" });\n    new Intl.NumberFormat(\"en\", { style: \"percent\" });\n    new Intl.NumberFormat(\"en\", { notation: \"scientific\" });\n  }\n}\n"
  },
  {
    "path": "benches/scripts/intl/pluralrules-construction.js",
    "content": "const kIterationCount = 100;\nconst locales = [\"en\", \"de\", \"ja\", \"ar\", \"fr\", \"es\", \"zh\"];\n\nfunction main() {\n  for (let i = 0; i < kIterationCount; i++) {\n    for (const locale of locales) {\n      new Intl.PluralRules(locale);\n    }\n  }\n}\n"
  },
  {
    "path": "benches/scripts/intl/pluralrules-select.js",
    "content": "const kIterationCount = 1000;\nconst pr = new Intl.PluralRules(\"en\");\n\nfunction main() {\n  for (let i = 0; i < kIterationCount; i++) {\n    pr.select(i);\n  }\n}\n"
  },
  {
    "path": "benches/scripts/intl/segmenter-construction.js",
    "content": "const kIterationCount = 100;\nconst locales = [\"en\", \"de\", \"ja\", \"ar\", \"fr\", \"es\", \"zh\"];\n\nfunction main() {\n  for (let i = 0; i < kIterationCount; i++) {\n    for (const locale of locales) {\n      new Intl.Segmenter(locale);\n    }\n  }\n}\n"
  },
  {
    "path": "benches/scripts/intl/segmenter-segment.js",
    "content": "const kIterationCount = 100;\nconst segmenter = new Intl.Segmenter(\"en\");\nconst text = \"The quick brown fox jumps over the lazy dog.\";\n\nfunction main() {\n  for (let i = 0; i < kIterationCount; i++) {\n    [...segmenter.segment(text)];\n  }\n}\n"
  },
  {
    "path": "benches/scripts/properties/access.js",
    "content": "// Measures property access performance across varying object shapes:\n// monomorphic (single shape), polymorphic (few shapes), and\n// megamorphic (many shapes) lookup patterns.\nconst kIterationCount = 50_000;\n\n// Monomorphic: always the same object shape.\nfunction monoAccess(obj) {\n  return obj.x + obj.y + obj.z;\n}\n\n// Polymorphic: receives objects with a few different shapes.\nfunction polyAccess(obj) {\n  return obj.value + obj.id;\n}\n\n// Test data: objects with distinct shapes.\nconst mono = { x: 1, y: 2, z: 3 };\nconst polyA = { value: 10, id: 1, a: \"extra\" };\nconst polyB = { id: 2, b: true, value: 20 };\nconst polyC = { c: null, d: 4, value: 30, id: 3 };\nconst polyD = { value: 40, id: 4, e: [1, 2] };\n\n// Megamorphic: 20 objects, each with a unique shape.\nconst megaObjects = [];\nfor (let i = 0; i < 20; i++) {\n  const obj = { value: i * 100, id: i };\n  for (let j = 0; j < i; j++) {\n    obj[\"prop\" + j] = j;\n  }\n  megaObjects.push(obj);\n}\n\nfunction main() {\n  let sum = 0;\n\n  // Monomorphic access (single shape, hot path).\n  for (let i = 0; i < kIterationCount; i++) {\n    sum += monoAccess(mono);\n  }\n\n  // Polymorphic access (4 shapes rotating).\n  const polyObjs = [polyA, polyB, polyC, polyD];\n  for (let i = 0; i < kIterationCount; i++) {\n    sum += polyAccess(polyObjs[i % 4]);\n  }\n\n  // Megamorphic access (20 unique shapes).\n  for (let i = 0; i < kIterationCount; i++) {\n    sum += polyAccess(megaObjects[i % 20]);\n  }\n\n  return sum;\n}\n"
  },
  {
    "path": "benches/scripts/prototypes/chain.js",
    "content": "// Measures property resolution through prototype chains of varying\n// depth (1, 5, and 10 levels) to quantify chain-walking overhead.\nconst kIterationCount = 100_000;\n\n// Build a prototype chain of the given depth with a base property.\nfunction buildChain(depth) {\n  let proto = { baseValue: 42, type: \"root\" };\n  for (let i = 0; i < depth; i++) {\n    const child = Object.create(proto);\n    child[\"level\" + i] = i;\n    child[\"name\" + i] = \"node_\" + i;\n    proto = child;\n  }\n  return proto;\n}\n\nconst shallow = buildChain(1);\nconst medium = buildChain(5);\nconst deep = buildChain(10);\n\nfunction main() {\n  let sum = 0;\n\n  // Shallow chain: 1 prototype hop to reach baseValue.\n  for (let i = 0; i < kIterationCount; i++) {\n    sum += shallow.baseValue;\n  }\n\n  // Medium chain: 5 prototype hops.\n  for (let i = 0; i < kIterationCount; i++) {\n    sum += medium.baseValue;\n  }\n\n  // Deep chain: 10 prototype hops.\n  for (let i = 0; i < kIterationCount; i++) {\n    sum += deep.baseValue;\n  }\n\n  return sum;\n}\n"
  },
  {
    "path": "benches/scripts/strings/concat.js",
    "content": "// Measures repeated string concatenation via the += operator,\n// stressing string allocation and garbage collection throughput.\nconst kIterationCount = 10_000;\nconst fragments = [\n  \"hello\",\n  \" \",\n  \"world\",\n  \"! \",\n  \"foo\",\n  \"bar\",\n  \"baz\",\n  \" \",\n  \"qux\",\n  \"\\n\",\n];\n\nfunction main() {\n  let result = \"\";\n  for (let i = 0; i < kIterationCount; i++) {\n    result += fragments[i % fragments.length];\n  }\n  return result.length;\n}\n"
  },
  {
    "path": "benches/scripts/strings/replace.js",
    "content": "// Measures String.prototype.replace performance: substring search\n// and per-replacement allocation cost on short strings.\nconst kIterationCount = 10_000;\nconst templates = [\n  \"the quick brown fox jumps over the lazy dog\",\n  \"a fast red fox leaps across the slow cat\",\n  \"one small step for man one giant leap for mankind\",\n];\nconst replacements = [\n  [\"fox\", \"cat\"],\n  [\"quick\", \"slow\"],\n  [\"the\", \"a\"],\n];\n\nfunction main() {\n  let result = \"\";\n  for (let i = 0; i < kIterationCount; i++) {\n    let s = templates[i % templates.length];\n    const pair = replacements[i % replacements.length];\n    s = s.replace(pair[0], pair[1]);\n    result = s;\n  }\n  return result.length;\n}\n"
  },
  {
    "path": "benches/scripts/strings/slice.js",
    "content": "// This script should take a few seconds to run.\nconst kIterationCount = 10_000;\nconst base = \"abcdefghijklmnopqrstuvwxyz\".repeat(10_000_000);\n\nfunction main() {\n  for (let i = 0; i < kIterationCount; i++) {\n    base.slice(i * 100, i * 100 + 20000);\n  }\n}\n"
  },
  {
    "path": "benches/scripts/strings/split.js",
    "content": "// This script should take a few seconds to run.\nconst kIterationCount = 10_000;\nconst base = \"abcdefghijklmnopqrstuvwxyz\".repeat(1_000);\n\nfunction main() {\n  const k = base.split(\"a\").length;\n  console.log(k);\n}\n"
  },
  {
    "path": "benches/scripts/v8-benches/README.md",
    "content": "# V8 Benchmarks\n\nThese benchmarks were copied from https://chromium.googlesource.com/v8/v8/+/52ab610bd13/benchmarks/\n"
  },
  {
    "path": "benches/scripts/v8-benches/crypto.js",
    "content": "\"use strict\";\n\"use strip\";\n// Copyright 2012 the V8 project authors. All rights reserved.\n// Redistribution and use in source and binary forms, with or without\n// modification, are permitted provided that the following conditions are\n// met:\n//\n//     * Redistributions of source code must retain the above copyright\n//       notice, this list of conditions and the following disclaimer.\n//     * Redistributions in binary form must reproduce the above\n//       copyright notice, this list of conditions and the following\n//       disclaimer in the documentation and/or other materials provided\n//       with the distribution.\n//     * Neither the name of Google Inc. nor the names of its\n//       contributors may be used to endorse or promote products derived\n//       from this software without specific prior written permission.\n//\n// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS\n// \"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT\n// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR\n// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT\n// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,\n// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT\n// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,\n// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY\n// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n\n// Simple framework for running the benchmark suites and\n// computing a score based on the timing measurements.\n\n\n// A benchmark has a name (string) and a function that will be run to\n// do the performance measurement. The optional setup and tearDown\n// arguments are functions that will be invoked before and after\n// running the benchmark, but the running time of these functions will\n// not be accounted for in the benchmark score.\nfunction Benchmark(name, run, setup, tearDown) {\n  this.name = name;\n  this.run = run;\n  this.Setup = setup ? setup : function () {\n  };\n  this.TearDown = tearDown ? tearDown : function () {\n  };\n}\n\n\n// Benchmark results hold the benchmark and the measured time used to\n// run the benchmark. The benchmark score is computed later once a\n// full benchmark suite has run to completion.\nfunction BenchmarkResult(benchmark, time) {\n  this.benchmark = benchmark;\n  this.time = time;\n}\n\n\n// Automatically convert results to numbers. Used by the geometric\n// mean computation.\nBenchmarkResult.prototype.valueOf = function () {\n  return this.time;\n};\n\n\n// Suites of benchmarks consist of a name and the set of benchmarks in\n// addition to the reference timing that the final score will be based\n// on. This way, all scores are relative to a reference run and higher\n// scores implies better performance.\nfunction BenchmarkSuite(name, reference, benchmarks) {\n  this.name = name;\n  this.reference = reference;\n  this.benchmarks = benchmarks;\n  BenchmarkSuite.suites.push(this);\n}\n\n\n// Keep track of all declared benchmark suites.\nBenchmarkSuite.suites = [];\n\n\n// Scores are not comparable across versions. Bump the version if\n// you're making changes that will affect that scores, e.g. if you add\n// a new benchmark or change an existing one.\nBenchmarkSuite.version = '7';\n\n\n// To make the benchmark results predictable, we replace Math.random\n// with a 100% deterministic alternative.\nMath.random = (function () {\n  var seed = 49734321;\n  return function () {\n    // Robert Jenkins' 32 bit integer hash function.\n    seed = ((seed + 0x7ed55d16) + (seed << 12)) & 0xffffffff;\n    seed = ((seed ^ 0xc761c23c) ^ (seed >>> 19)) & 0xffffffff;\n    seed = ((seed + 0x165667b1) + (seed << 5)) & 0xffffffff;\n    seed = ((seed + 0xd3a2646c) ^ (seed << 9)) & 0xffffffff;\n    seed = ((seed + 0xfd7046c5) + (seed << 3)) & 0xffffffff;\n    seed = ((seed ^ 0xb55a4f09) ^ (seed >>> 16)) & 0xffffffff;\n    return (seed & 0xfffffff) / 0x10000000;\n  };\n})();\n\n\n// Runs all registered benchmark suites and optionally yields between\n// each individual benchmark to avoid running for too long in the\n// context of browsers. Once done, the final score is reported to the\n// runner.\nBenchmarkSuite.RunSuites = function (runner) {\n  var continuation = null;\n  var suites = BenchmarkSuite.suites;\n  var length = suites.length;\n  BenchmarkSuite.scores = [];\n  var index = 0;\n\n  function RunStep() {\n    while (continuation || index < length) {\n      if (continuation) {\n        continuation = continuation();\n      } else {\n        var suite = suites[index++];\n        if (runner.NotifyStart) runner.NotifyStart(suite.name);\n        continuation = suite.RunStep(runner);\n      }\n      if (continuation && typeof window != 'undefined' && window.setTimeout) {\n        window.setTimeout(RunStep, 25);\n        return;\n      }\n    }\n    if (runner.NotifyScore) {\n      var score = BenchmarkSuite.GeometricMean(BenchmarkSuite.scores);\n      var formatted = BenchmarkSuite.FormatScore(100 * score);\n      runner.NotifyScore(formatted);\n    }\n  }\n\n  RunStep();\n};\n\n\n// Counts the total number of registered benchmarks. Useful for\n// showing progress as a percentage.\nBenchmarkSuite.CountBenchmarks = function () {\n  var result = 0;\n  var suites = BenchmarkSuite.suites;\n  for (var i = 0; i < suites.length; i++) {\n    result += suites[i].benchmarks.length;\n  }\n  return result;\n};\n\n\n// Computes the geometric mean of a set of numbers.\nBenchmarkSuite.GeometricMean = function (numbers) {\n  var log = 0;\n  for (var i = 0; i < numbers.length; i++) {\n    log += Math.log(numbers[i]);\n  }\n  return Math.pow(Math.E, log / numbers.length);\n};\n\n\n// Converts a score value to a string with at least three significant\n// digits.\nBenchmarkSuite.FormatScore = function (value) {\n  if (value > 100) {\n    return value.toFixed(0);\n  } else {\n    return value.toPrecision(3);\n  }\n};\n\n// Notifies the runner that we're done running a single benchmark in\n// the benchmark suite. This can be useful to report progress.\nBenchmarkSuite.prototype.NotifyStep = function (result) {\n  this.results.push(result);\n  if (this.runner.NotifyStep) this.runner.NotifyStep(result.benchmark.name);\n};\n\n\n// Notifies the runner that we're done with running a suite and that\n// we have a result which can be reported to the user if needed.\nBenchmarkSuite.prototype.NotifyResult = function () {\n  var mean = BenchmarkSuite.GeometricMean(this.results);\n  var score = this.reference / mean;\n  BenchmarkSuite.scores.push(score);\n  if (this.runner.NotifyResult) {\n    var formatted = BenchmarkSuite.FormatScore(100 * score);\n    this.runner.NotifyResult(this.name, formatted);\n  }\n};\n\n\n// Notifies the runner that running a benchmark resulted in an error.\nBenchmarkSuite.prototype.NotifyError = function (error) {\n  if (this.runner.NotifyError) {\n    this.runner.NotifyError(this.name, error);\n  }\n  if (this.runner.NotifyStep) {\n    this.runner.NotifyStep(this.name);\n  }\n};\n\n\n// Runs a single benchmark for at least a second and computes the\n// average time it takes to run a single iteration.\nBenchmarkSuite.prototype.RunSingleBenchmark = function (benchmark, data) {\n  function Measure(data) {\n    var elapsed = 0;\n    var start = new Date();\n    for (var n = 0; elapsed < 1000; n++) {\n      benchmark.run();\n      elapsed = new Date() - start;\n    }\n    if (data != null) {\n      data.runs += n;\n      data.elapsed += elapsed;\n    }\n  }\n\n  if (data == null) {\n    // Measure the benchmark once for warm up and throw the result\n    // away. Return a fresh data object.\n    Measure(null);\n    return {runs: 0, elapsed: 0};\n  } else {\n    Measure(data);\n    // If we've run too few iterations, we continue for another second.\n    if (data.runs < 32) return data;\n    var usec = (data.elapsed * 1000) / data.runs;\n    this.NotifyStep(new BenchmarkResult(benchmark, usec));\n    return null;\n  }\n};\n\n\n// This function starts running a suite, but stops between each\n// individual benchmark in the suite and returns a continuation\n// function which can be invoked to run the next benchmark. Once the\n// last benchmark has been executed, null is returned.\nBenchmarkSuite.prototype.RunStep = function (runner) {\n  this.results = [];\n  this.runner = runner;\n  var length = this.benchmarks.length;\n  var index = 0;\n  var suite = this;\n  var data;\n\n  // Run the setup, the actual benchmark, and the tear down in three\n  // separate steps to allow the framework to yield between any of the\n  // steps.\n\n  function RunNextSetup() {\n    if (index < length) {\n      try {\n        suite.benchmarks[index].Setup();\n      } catch (e) {\n        suite.NotifyError(e);\n        return null;\n      }\n      return RunNextBenchmark;\n    }\n    suite.NotifyResult();\n    return null;\n  }\n\n  function RunNextBenchmark() {\n    try {\n      data = suite.RunSingleBenchmark(suite.benchmarks[index], data);\n    } catch (e) {\n      suite.NotifyError(e);\n      return null;\n    }\n    // If data is null, we're done with this benchmark.\n    return (data == null) ? RunNextTearDown : RunNextBenchmark();\n  }\n\n  function RunNextTearDown() {\n    try {\n      suite.benchmarks[index++].TearDown();\n    } catch (e) {\n      suite.NotifyError(e);\n      return null;\n    }\n    return RunNextSetup;\n  }\n\n  // Start out running the setup.\n  return RunNextSetup();\n};\n\n\n/*\n * Copyright (c) 2003-2005  Tom Wu\n * All Rights Reserved.\n *\n * Permission is hereby granted, free of charge, to any person obtaining\n * a copy of this software and associated documentation files (the\n * \"Software\"), to deal in the Software without restriction, including\n * without limitation the rights to use, copy, modify, merge, publish,\n * distribute, sublicense, and/or sell copies of the Software, and to\n * permit persons to whom the Software is furnished to do so, subject to\n * the following conditions:\n *\n * The above copyright notice and this permission notice shall be\n * included in all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS-IS\" AND WITHOUT WARRANTY OF ANY KIND,\n * EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY\n * WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.\n *\n * IN NO EVENT SHALL TOM WU BE LIABLE FOR ANY SPECIAL, INCIDENTAL,\n * INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY KIND, OR ANY DAMAGES WHATSOEVER\n * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER OR NOT ADVISED OF\n * THE POSSIBILITY OF DAMAGE, AND ON ANY THEORY OF LIABILITY, ARISING OUT\n * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.\n *\n * In addition, the following condition applies:\n *\n * All redistributions must retain an intact copy of this copyright notice\n * and disclaimer.\n */\n\n\n// Basic JavaScript BN library - subset useful for RSA encryption.\n\n// Bits per digit\nvar dbits;\nvar BI_DB;\nvar BI_DM;\nvar BI_DV;\n\nvar BI_FP;\nvar BI_FV;\nvar BI_F1;\nvar BI_F2;\n\n// JavaScript engine analysis\nvar canary = 0xdeadbeefcafe;\nvar j_lm = ((canary & 0xffffff) == 0xefcafe);\n\n// (public) Constructor\nfunction BigInteger(a, b, c) {\n  this.array = new Array();\n  if (a != null)\n    if (\"number\" == typeof a) this.fromNumber(a, b, c);\n    else if (b == null && \"string\" != typeof a) this.fromString(a, 256);\n    else this.fromString(a, b);\n}\n\n// return new, unset BigInteger\nfunction nbi() {\n  return new BigInteger(null);\n}\n\n// am: Compute w_j += (x*this_i), propagate carries,\n// c is initial carry, returns final carry.\n// c < 3*dvalue, x < 2*dvalue, this_i < dvalue\n// We need to select the fastest one that works in this environment.\n\n// am1: use a single mult and divide to get the high bits,\n// max digit bits should be 26 because\n// max internal value = 2*dvalue^2-2*dvalue (< 2^53)\nfunction am1(i, x, w, j, c, n) {\n  var this_array = this.array;\n  var w_array = w.array;\n  while (--n >= 0) {\n    var v = x * this_array[i++] + w_array[j] + c;\n    c = Math.floor(v / 0x4000000);\n    w_array[j++] = v & 0x3ffffff;\n  }\n  return c;\n}\n\n// am2 avoids a big mult-and-extract completely.\n// Max digit bits should be <= 30 because we do bitwise ops\n// on values up to 2*hdvalue^2-hdvalue-1 (< 2^31)\nfunction am2(i, x, w, j, c, n) {\n  var this_array = this.array;\n  var w_array = w.array;\n  var xl = x & 0x7fff, xh = x >> 15;\n  while (--n >= 0) {\n    var l = this_array[i] & 0x7fff;\n    var h = this_array[i++] >> 15;\n    var m = xh * l + h * xl;\n    l = xl * l + ((m & 0x7fff) << 15) + w_array[j] + (c & 0x3fffffff);\n    c = (l >>> 30) + (m >>> 15) + xh * h + (c >>> 30);\n    w_array[j++] = l & 0x3fffffff;\n  }\n  return c;\n}\n\n// Alternately, set max digit bits to 28 since some\n// browsers slow down when dealing with 32-bit numbers.\nfunction am3(i, x, w, j, c, n) {\n  var this_array = this.array;\n  var w_array = w.array;\n\n  var xl = x & 0x3fff, xh = x >> 14;\n  while (--n >= 0) {\n    var l = this_array[i] & 0x3fff;\n    var h = this_array[i++] >> 14;\n    var m = xh * l + h * xl;\n    l = xl * l + ((m & 0x3fff) << 14) + w_array[j] + c;\n    c = (l >> 28) + (m >> 14) + xh * h;\n    w_array[j++] = l & 0xfffffff;\n  }\n  return c;\n}\n\n// This is tailored to VMs with 2-bit tagging. It makes sure\n// that all the computations stay within the 29 bits available.\nfunction am4(i, x, w, j, c, n) {\n  var this_array = this.array;\n  var w_array = w.array;\n\n  var xl = x & 0x1fff, xh = x >> 13;\n  while (--n >= 0) {\n    var l = this_array[i] & 0x1fff;\n    var h = this_array[i++] >> 13;\n    var m = xh * l + h * xl;\n    l = xl * l + ((m & 0x1fff) << 13) + w_array[j] + c;\n    c = (l >> 26) + (m >> 13) + xh * h;\n    w_array[j++] = l & 0x3ffffff;\n  }\n  return c;\n}\n\n// am3/28 is best for SM, Rhino, but am4/26 is best for v8.\n// Kestrel (Opera 9.5) gets its best result with am4/26.\n// IE7 does 9% better with am3/28 than with am4/26.\n// Firefox (SM) gets 10% faster with am3/28 than with am4/26.\n\nvar setupEngine = function (fn, bits) {\n  BigInteger.prototype.am = fn;\n  dbits = bits;\n\n  BI_DB = dbits;\n  BI_DM = ((1 << dbits) - 1);\n  BI_DV = (1 << dbits);\n\n  BI_FP = 52;\n  BI_FV = Math.pow(2, BI_FP);\n  BI_F1 = BI_FP - dbits;\n  BI_F2 = 2 * dbits - BI_FP;\n}\n\n\n// Digit conversions\nvar BI_RM = \"0123456789abcdefghijklmnopqrstuvwxyz\";\nvar BI_RC = new Array();\nvar rr, vv;\nrr = \"0\".charCodeAt(0);\nfor (vv = 0; vv <= 9; ++vv) BI_RC[rr++] = vv;\nrr = \"a\".charCodeAt(0);\nfor (vv = 10; vv < 36; ++vv) BI_RC[rr++] = vv;\nrr = \"A\".charCodeAt(0);\nfor (vv = 10; vv < 36; ++vv) BI_RC[rr++] = vv;\n\nfunction int2char(n) {\n  return BI_RM.charAt(n);\n}\n\nfunction intAt(s, i) {\n  var c = BI_RC[s.charCodeAt(i)];\n  return (c == null) ? -1 : c;\n}\n\n// (protected) copy this to r\nfunction bnpCopyTo(r) {\n  var this_array = this.array;\n  var r_array = r.array;\n\n  for (var i = this.t - 1; i >= 0; --i) r_array[i] = this_array[i];\n  r.t = this.t;\n  r.s = this.s;\n}\n\n// (protected) set from integer value x, -DV <= x < DV\nfunction bnpFromInt(x) {\n  var this_array = this.array;\n  this.t = 1;\n  this.s = (x < 0) ? -1 : 0;\n  if (x > 0) this_array[0] = x;\n  else if (x < -1) this_array[0] = x + DV;\n  else this.t = 0;\n}\n\n// return bigint initialized to value\nfunction nbv(i) {\n  var r = nbi();\n  r.fromInt(i);\n  return r;\n}\n\n// (protected) set from string and radix\nfunction bnpFromString(s, b) {\n  var this_array = this.array;\n  var k;\n  if (b == 16) k = 4;\n  else if (b == 8) k = 3;\n  else if (b == 256) k = 8; // byte array\n  else if (b == 2) k = 1;\n  else if (b == 32) k = 5;\n  else if (b == 4) k = 2;\n  else {\n    this.fromRadix(s, b);\n    return;\n  }\n  this.t = 0;\n  this.s = 0;\n  var i = s.length, mi = false, sh = 0;\n  while (--i >= 0) {\n    var x = (k == 8) ? s[i] & 0xff : intAt(s, i);\n    if (x < 0) {\n      if (s.charAt(i) == \"-\") mi = true;\n      continue;\n    }\n    mi = false;\n    if (sh == 0)\n      this_array[this.t++] = x;\n    else if (sh + k > BI_DB) {\n      this_array[this.t - 1] |= (x & ((1 << (BI_DB - sh)) - 1)) << sh;\n      this_array[this.t++] = (x >> (BI_DB - sh));\n    } else\n      this_array[this.t - 1] |= x << sh;\n    sh += k;\n    if (sh >= BI_DB) sh -= BI_DB;\n  }\n  if (k == 8 && (s[0] & 0x80) != 0) {\n    this.s = -1;\n    if (sh > 0) this_array[this.t - 1] |= ((1 << (BI_DB - sh)) - 1) << sh;\n  }\n  this.clamp();\n  if (mi) BigInteger.ZERO.subTo(this, this);\n}\n\n// (protected) clamp off excess high words\nfunction bnpClamp() {\n  var this_array = this.array;\n  var c = this.s & BI_DM;\n  while (this.t > 0 && this_array[this.t - 1] == c) --this.t;\n}\n\n// (public) return string representation in given radix\nfunction bnToString(b) {\n  var this_array = this.array;\n  if (this.s < 0) return \"-\" + this.negate().toString(b);\n  var k;\n  if (b == 16) k = 4;\n  else if (b == 8) k = 3;\n  else if (b == 2) k = 1;\n  else if (b == 32) k = 5;\n  else if (b == 4) k = 2;\n  else return this.toRadix(b);\n  var km = (1 << k) - 1, d, m = false, r = \"\", i = this.t;\n  var p = BI_DB - (i * BI_DB) % k;\n  if (i-- > 0) {\n    if (p < BI_DB && (d = this_array[i] >> p) > 0) {\n      m = true;\n      r = int2char(d);\n    }\n    while (i >= 0) {\n      if (p < k) {\n        d = (this_array[i] & ((1 << p) - 1)) << (k - p);\n        d |= this_array[--i] >> (p += BI_DB - k);\n      } else {\n        d = (this_array[i] >> (p -= k)) & km;\n        if (p <= 0) {\n          p += BI_DB;\n          --i;\n        }\n      }\n      if (d > 0) m = true;\n      if (m) r += int2char(d);\n    }\n  }\n  return m ? r : \"0\";\n}\n\n// (public) -this\nfunction bnNegate() {\n  var r = nbi();\n  BigInteger.ZERO.subTo(this, r);\n  return r;\n}\n\n// (public) |this|\nfunction bnAbs() {\n  return (this.s < 0) ? this.negate() : this;\n}\n\n// (public) return + if this > a, - if this < a, 0 if equal\nfunction bnCompareTo(a) {\n  var this_array = this.array;\n  var a_array = a.array;\n\n  var r = this.s - a.s;\n  if (r != 0) return r;\n  var i = this.t;\n  r = i - a.t;\n  if (r != 0) return r;\n  while (--i >= 0) if ((r = this_array[i] - a_array[i]) != 0) return r;\n  return 0;\n}\n\n// returns bit length of the integer x\nfunction nbits(x) {\n  var r = 1, t;\n  if ((t = x >>> 16) != 0) {\n    x = t;\n    r += 16;\n  }\n  if ((t = x >> 8) != 0) {\n    x = t;\n    r += 8;\n  }\n  if ((t = x >> 4) != 0) {\n    x = t;\n    r += 4;\n  }\n  if ((t = x >> 2) != 0) {\n    x = t;\n    r += 2;\n  }\n  if ((t = x >> 1) != 0) {\n    x = t;\n    r += 1;\n  }\n  return r;\n}\n\n// (public) return the number of bits in \"this\"\nfunction bnBitLength() {\n  var this_array = this.array;\n  if (this.t <= 0) return 0;\n  return BI_DB * (this.t - 1) + nbits(this_array[this.t - 1] ^ (this.s & BI_DM));\n}\n\n// (protected) r = this << n*DB\nfunction bnpDLShiftTo(n, r) {\n  var this_array = this.array;\n  var r_array = r.array;\n  var i;\n  for (i = this.t - 1; i >= 0; --i) r_array[i + n] = this_array[i];\n  for (i = n - 1; i >= 0; --i) r_array[i] = 0;\n  r.t = this.t + n;\n  r.s = this.s;\n}\n\n// (protected) r = this >> n*DB\nfunction bnpDRShiftTo(n, r) {\n  var this_array = this.array;\n  var r_array = r.array;\n  for (var i = n; i < this.t; ++i) r_array[i - n] = this_array[i];\n  r.t = Math.max(this.t - n, 0);\n  r.s = this.s;\n}\n\n// (protected) r = this << n\nfunction bnpLShiftTo(n, r) {\n  var this_array = this.array;\n  var r_array = r.array;\n  var bs = n % BI_DB;\n  var cbs = BI_DB - bs;\n  var bm = (1 << cbs) - 1;\n  var ds = Math.floor(n / BI_DB), c = (this.s << bs) & BI_DM, i;\n  for (i = this.t - 1; i >= 0; --i) {\n    r_array[i + ds + 1] = (this_array[i] >> cbs) | c;\n    c = (this_array[i] & bm) << bs;\n  }\n  for (i = ds - 1; i >= 0; --i) r_array[i] = 0;\n  r_array[ds] = c;\n  r.t = this.t + ds + 1;\n  r.s = this.s;\n  r.clamp();\n}\n\n// (protected) r = this >> n\nfunction bnpRShiftTo(n, r) {\n  var this_array = this.array;\n  var r_array = r.array;\n  r.s = this.s;\n  var ds = Math.floor(n / BI_DB);\n  if (ds >= this.t) {\n    r.t = 0;\n    return;\n  }\n  var bs = n % BI_DB;\n  var cbs = BI_DB - bs;\n  var bm = (1 << bs) - 1;\n  r_array[0] = this_array[ds] >> bs;\n  for (var i = ds + 1; i < this.t; ++i) {\n    r_array[i - ds - 1] |= (this_array[i] & bm) << cbs;\n    r_array[i - ds] = this_array[i] >> bs;\n  }\n  if (bs > 0) r_array[this.t - ds - 1] |= (this.s & bm) << cbs;\n  r.t = this.t - ds;\n  r.clamp();\n}\n\n// (protected) r = this - a\nfunction bnpSubTo(a, r) {\n  var this_array = this.array;\n  var r_array = r.array;\n  var a_array = a.array;\n  var i = 0, c = 0, m = Math.min(a.t, this.t);\n  while (i < m) {\n    c += this_array[i] - a_array[i];\n    r_array[i++] = c & BI_DM;\n    c >>= BI_DB;\n  }\n  if (a.t < this.t) {\n    c -= a.s;\n    while (i < this.t) {\n      c += this_array[i];\n      r_array[i++] = c & BI_DM;\n      c >>= BI_DB;\n    }\n    c += this.s;\n  } else {\n    c += this.s;\n    while (i < a.t) {\n      c -= a_array[i];\n      r_array[i++] = c & BI_DM;\n      c >>= BI_DB;\n    }\n    c -= a.s;\n  }\n  r.s = (c < 0) ? -1 : 0;\n  if (c < -1) r_array[i++] = BI_DV + c;\n  else if (c > 0) r_array[i++] = c;\n  r.t = i;\n  r.clamp();\n}\n\n// (protected) r = this * a, r != this,a (HAC 14.12)\n// \"this\" should be the larger one if appropriate.\nfunction bnpMultiplyTo(a, r) {\n  var this_array = this.array;\n  var r_array = r.array;\n  var x = this.abs(), y = a.abs();\n  var y_array = y.array;\n\n  var i = x.t;\n  r.t = i + y.t;\n  while (--i >= 0) r_array[i] = 0;\n  for (i = 0; i < y.t; ++i) r_array[i + x.t] = x.am(0, y_array[i], r, i, 0, x.t);\n  r.s = 0;\n  r.clamp();\n  if (this.s != a.s) BigInteger.ZERO.subTo(r, r);\n}\n\n// (protected) r = this^2, r != this (HAC 14.16)\nfunction bnpSquareTo(r) {\n  var x = this.abs();\n  var x_array = x.array;\n  var r_array = r.array;\n\n  var i = r.t = 2 * x.t;\n  while (--i >= 0) r_array[i] = 0;\n  for (i = 0; i < x.t - 1; ++i) {\n    var c = x.am(i, x_array[i], r, 2 * i, 0, 1);\n    if ((r_array[i + x.t] += x.am(i + 1, 2 * x_array[i], r, 2 * i + 1, c, x.t - i - 1)) >= BI_DV) {\n      r_array[i + x.t] -= BI_DV;\n      r_array[i + x.t + 1] = 1;\n    }\n  }\n  if (r.t > 0) r_array[r.t - 1] += x.am(i, x_array[i], r, 2 * i, 0, 1);\n  r.s = 0;\n  r.clamp();\n}\n\n// (protected) divide this by m, quotient and remainder to q, r (HAC 14.20)\n// r != q, this != m.  q or r may be null.\nfunction bnpDivRemTo(m, q, r) {\n  var pm = m.abs();\n  if (pm.t <= 0) return;\n  var pt = this.abs();\n  if (pt.t < pm.t) {\n    if (q != null) q.fromInt(0);\n    if (r != null) this.copyTo(r);\n    return;\n  }\n  if (r == null) r = nbi();\n  var y = nbi(), ts = this.s, ms = m.s;\n  var pm_array = pm.array;\n  var nsh = BI_DB - nbits(pm_array[pm.t - 1]);\t// normalize modulus\n  if (nsh > 0) {\n    pm.lShiftTo(nsh, y);\n    pt.lShiftTo(nsh, r);\n  } else {\n    pm.copyTo(y);\n    pt.copyTo(r);\n  }\n  var ys = y.t;\n\n  var y_array = y.array;\n  var y0 = y_array[ys - 1];\n  if (y0 == 0) return;\n  var yt = y0 * (1 << BI_F1) + ((ys > 1) ? y_array[ys - 2] >> BI_F2 : 0);\n  var d1 = BI_FV / yt, d2 = (1 << BI_F1) / yt, e = 1 << BI_F2;\n  var i = r.t, j = i - ys, t = (q == null) ? nbi() : q;\n  y.dlShiftTo(j, t);\n\n  var r_array = r.array;\n  if (r.compareTo(t) >= 0) {\n    r_array[r.t++] = 1;\n    r.subTo(t, r);\n  }\n  BigInteger.ONE.dlShiftTo(ys, t);\n  t.subTo(y, y);\t// \"negative\" y so we can replace sub with am later\n  while (y.t < ys) y_array[y.t++] = 0;\n  while (--j >= 0) {\n    // Estimate quotient digit\n    var qd = (r_array[--i] == y0) ? BI_DM : Math.floor(r_array[i] * d1 + (r_array[i - 1] + e) * d2);\n    if ((r_array[i] += y.am(0, qd, r, j, 0, ys)) < qd) {\t// Try it out\n      y.dlShiftTo(j, t);\n      r.subTo(t, r);\n      while (r_array[i] < --qd) r.subTo(t, r);\n    }\n  }\n  if (q != null) {\n    r.drShiftTo(ys, q);\n    if (ts != ms) BigInteger.ZERO.subTo(q, q);\n  }\n  r.t = ys;\n  r.clamp();\n  if (nsh > 0) r.rShiftTo(nsh, r);\t// Denormalize remainder\n  if (ts < 0) BigInteger.ZERO.subTo(r, r);\n}\n\n// (public) this mod a\nfunction bnMod(a) {\n  var r = nbi();\n  this.abs().divRemTo(a, null, r);\n  if (this.s < 0 && r.compareTo(BigInteger.ZERO) > 0) a.subTo(r, r);\n  return r;\n}\n\n// Modular reduction using \"classic\" algorithm\nfunction Classic(m) {\n  this.m = m;\n}\n\nfunction cConvert(x) {\n  if (x.s < 0 || x.compareTo(this.m) >= 0) return x.mod(this.m);\n  else return x;\n}\n\nfunction cRevert(x) {\n  return x;\n}\n\nfunction cReduce(x) {\n  x.divRemTo(this.m, null, x);\n}\n\nfunction cMulTo(x, y, r) {\n  x.multiplyTo(y, r);\n  this.reduce(r);\n}\n\nfunction cSqrTo(x, r) {\n  x.squareTo(r);\n  this.reduce(r);\n}\n\nClassic.prototype.convert = cConvert;\nClassic.prototype.revert = cRevert;\nClassic.prototype.reduce = cReduce;\nClassic.prototype.mulTo = cMulTo;\nClassic.prototype.sqrTo = cSqrTo;\n\n// (protected) return \"-1/this % 2^DB\"; useful for Mont. reduction\n// justification:\n//         xy == 1 (mod m)\n//         xy =  1+km\n//   xy(2-xy) = (1+km)(1-km)\n// x[y(2-xy)] = 1-k^2m^2\n// x[y(2-xy)] == 1 (mod m^2)\n// if y is 1/x mod m, then y(2-xy) is 1/x mod m^2\n// should reduce x and y(2-xy) by m^2 at each step to keep size bounded.\n// JS multiply \"overflows\" differently from C/C++, so care is needed here.\nfunction bnpInvDigit() {\n  var this_array = this.array;\n  if (this.t < 1) return 0;\n  var x = this_array[0];\n  if ((x & 1) == 0) return 0;\n  var y = x & 3;\t\t// y == 1/x mod 2^2\n  y = (y * (2 - (x & 0xf) * y)) & 0xf;\t// y == 1/x mod 2^4\n  y = (y * (2 - (x & 0xff) * y)) & 0xff;\t// y == 1/x mod 2^8\n  y = (y * (2 - (((x & 0xffff) * y) & 0xffff))) & 0xffff;\t// y == 1/x mod 2^16\n  // last step - calculate inverse mod DV directly;\n  // assumes 16 < DB <= 32 and assumes ability to handle 48-bit ints\n  y = (y * (2 - x * y % BI_DV)) % BI_DV;\t\t// y == 1/x mod 2^dbits\n  // we really want the negative inverse, and -DV < y < DV\n  return (y > 0) ? BI_DV - y : -y;\n}\n\n// Montgomery reduction\nfunction Montgomery(m) {\n  this.m = m;\n  this.mp = m.invDigit();\n  this.mpl = this.mp & 0x7fff;\n  this.mph = this.mp >> 15;\n  this.um = (1 << (BI_DB - 15)) - 1;\n  this.mt2 = 2 * m.t;\n}\n\n// xR mod m\nfunction montConvert(x) {\n  var r = nbi();\n  x.abs().dlShiftTo(this.m.t, r);\n  r.divRemTo(this.m, null, r);\n  if (x.s < 0 && r.compareTo(BigInteger.ZERO) > 0) this.m.subTo(r, r);\n  return r;\n}\n\n// x/R mod m\nfunction montRevert(x) {\n  var r = nbi();\n  x.copyTo(r);\n  this.reduce(r);\n  return r;\n}\n\n// x = x/R mod m (HAC 14.32)\nfunction montReduce(x) {\n  var x_array = x.array;\n  while (x.t <= this.mt2)\t// pad x so am has enough room later\n    x_array[x.t++] = 0;\n  for (var i = 0; i < this.m.t; ++i) {\n    // faster way of calculating u0 = x[i]*mp mod DV\n    var j = x_array[i] & 0x7fff;\n    var u0 = (j * this.mpl + (((j * this.mph + (x_array[i] >> 15) * this.mpl) & this.um) << 15)) & BI_DM;\n    // use am to combine the multiply-shift-add into one call\n    j = i + this.m.t;\n    x_array[j] += this.m.am(0, u0, x, i, 0, this.m.t);\n    // propagate carry\n    while (x_array[j] >= BI_DV) {\n      x_array[j] -= BI_DV;\n      x_array[++j]++;\n    }\n  }\n  x.clamp();\n  x.drShiftTo(this.m.t, x);\n  if (x.compareTo(this.m) >= 0) x.subTo(this.m, x);\n}\n\n// r = \"x^2/R mod m\"; x != r\nfunction montSqrTo(x, r) {\n  x.squareTo(r);\n  this.reduce(r);\n}\n\n// r = \"xy/R mod m\"; x,y != r\nfunction montMulTo(x, y, r) {\n  x.multiplyTo(y, r);\n  this.reduce(r);\n}\n\nMontgomery.prototype.convert = montConvert;\nMontgomery.prototype.revert = montRevert;\nMontgomery.prototype.reduce = montReduce;\nMontgomery.prototype.mulTo = montMulTo;\nMontgomery.prototype.sqrTo = montSqrTo;\n\n// (protected) true iff this is even\nfunction bnpIsEven() {\n  var this_array = this.array;\n  return ((this.t > 0) ? (this_array[0] & 1) : this.s) == 0;\n}\n\n// (protected) this^e, e < 2^32, doing sqr and mul with \"r\" (HAC 14.79)\nfunction bnpExp(e, z) {\n  if (e > 0xffffffff || e < 1) return BigInteger.ONE;\n  var r = nbi(), r2 = nbi(), g = z.convert(this), i = nbits(e) - 1;\n  g.copyTo(r);\n  while (--i >= 0) {\n    z.sqrTo(r, r2);\n    if ((e & (1 << i)) > 0) z.mulTo(r2, g, r);\n    else {\n      var t = r;\n      r = r2;\n      r2 = t;\n    }\n  }\n  return z.revert(r);\n}\n\n// (public) this^e % m, 0 <= e < 2^32\nfunction bnModPowInt(e, m) {\n  var z;\n  if (e < 256 || m.isEven()) z = new Classic(m); else z = new Montgomery(m);\n  return this.exp(e, z);\n}\n\n// protected\nBigInteger.prototype.copyTo = bnpCopyTo;\nBigInteger.prototype.fromInt = bnpFromInt;\nBigInteger.prototype.fromString = bnpFromString;\nBigInteger.prototype.clamp = bnpClamp;\nBigInteger.prototype.dlShiftTo = bnpDLShiftTo;\nBigInteger.prototype.drShiftTo = bnpDRShiftTo;\nBigInteger.prototype.lShiftTo = bnpLShiftTo;\nBigInteger.prototype.rShiftTo = bnpRShiftTo;\nBigInteger.prototype.subTo = bnpSubTo;\nBigInteger.prototype.multiplyTo = bnpMultiplyTo;\nBigInteger.prototype.squareTo = bnpSquareTo;\nBigInteger.prototype.divRemTo = bnpDivRemTo;\nBigInteger.prototype.invDigit = bnpInvDigit;\nBigInteger.prototype.isEven = bnpIsEven;\nBigInteger.prototype.exp = bnpExp;\n\n// public\nBigInteger.prototype.toString = bnToString;\nBigInteger.prototype.negate = bnNegate;\nBigInteger.prototype.abs = bnAbs;\nBigInteger.prototype.compareTo = bnCompareTo;\nBigInteger.prototype.bitLength = bnBitLength;\nBigInteger.prototype.mod = bnMod;\nBigInteger.prototype.modPowInt = bnModPowInt;\n\n// \"constants\"\nBigInteger.ZERO = nbv(0);\nBigInteger.ONE = nbv(1);\n// Copyright (c) 2005  Tom Wu\n// All Rights Reserved.\n// See \"LICENSE\" for details.\n\n// Extended JavaScript BN functions, required for RSA private ops.\n\n// (public)\nfunction bnClone() {\n  var r = nbi();\n  this.copyTo(r);\n  return r;\n}\n\n// (public) return value as integer\nfunction bnIntValue() {\n  var this_array = this.array;\n  if (this.s < 0) {\n    if (this.t == 1) return this_array[0] - BI_DV;\n    else if (this.t == 0) return -1;\n  } else if (this.t == 1) return this_array[0];\n  else if (this.t == 0) return 0;\n  // assumes 16 < DB < 32\n  return ((this_array[1] & ((1 << (32 - BI_DB)) - 1)) << BI_DB) | this_array[0];\n}\n\n// (public) return value as byte\nfunction bnByteValue() {\n  var this_array = this.array;\n  return (this.t == 0) ? this.s : (this_array[0] << 24) >> 24;\n}\n\n// (public) return value as short (assumes DB>=16)\nfunction bnShortValue() {\n  var this_array = this.array;\n  return (this.t == 0) ? this.s : (this_array[0] << 16) >> 16;\n}\n\n// (protected) return x s.t. r^x < DV\nfunction bnpChunkSize(r) {\n  return Math.floor(Math.LN2 * BI_DB / Math.log(r));\n}\n\n// (public) 0 if this == 0, 1 if this > 0\nfunction bnSigNum() {\n  var this_array = this.array;\n  if (this.s < 0) return -1;\n  else if (this.t <= 0 || (this.t == 1 && this_array[0] <= 0)) return 0;\n  else return 1;\n}\n\n// (protected) convert to radix string\nfunction bnpToRadix(b) {\n  if (b == null) b = 10;\n  if (this.signum() == 0 || b < 2 || b > 36) return \"0\";\n  var cs = this.chunkSize(b);\n  var a = Math.pow(b, cs);\n  var d = nbv(a), y = nbi(), z = nbi(), r = \"\";\n  this.divRemTo(d, y, z);\n  while (y.signum() > 0) {\n    r = (a + z.intValue()).toString(b).substr(1) + r;\n    y.divRemTo(d, y, z);\n  }\n  return z.intValue().toString(b) + r;\n}\n\n// (protected) convert from radix string\nfunction bnpFromRadix(s, b) {\n  this.fromInt(0);\n  if (b == null) b = 10;\n  var cs = this.chunkSize(b);\n  var d = Math.pow(b, cs), mi = false, j = 0, w = 0;\n  for (var i = 0; i < s.length; ++i) {\n    var x = intAt(s, i);\n    if (x < 0) {\n      if (s.charAt(i) == \"-\" && this.signum() == 0) mi = true;\n      continue;\n    }\n    w = b * w + x;\n    if (++j >= cs) {\n      this.dMultiply(d);\n      this.dAddOffset(w, 0);\n      j = 0;\n      w = 0;\n    }\n  }\n  if (j > 0) {\n    this.dMultiply(Math.pow(b, j));\n    this.dAddOffset(w, 0);\n  }\n  if (mi) BigInteger.ZERO.subTo(this, this);\n}\n\n// (protected) alternate constructor\nfunction bnpFromNumber(a, b, c) {\n  if (\"number\" == typeof b) {\n    // new BigInteger(int,int,RNG)\n    if (a < 2) this.fromInt(1);\n    else {\n      this.fromNumber(a, c);\n      if (!this.testBit(a - 1))\t// force MSB set\n        this.bitwiseTo(BigInteger.ONE.shiftLeft(a - 1), op_or, this);\n      if (this.isEven()) this.dAddOffset(1, 0); // force odd\n      while (!this.isProbablePrime(b)) {\n        this.dAddOffset(2, 0);\n        if (this.bitLength() > a) this.subTo(BigInteger.ONE.shiftLeft(a - 1), this);\n      }\n    }\n  } else {\n    // new BigInteger(int,RNG)\n    var x = new Array(), t = a & 7;\n    x.length = (a >> 3) + 1;\n    b.nextBytes(x);\n    if (t > 0) x[0] &= ((1 << t) - 1); else x[0] = 0;\n    this.fromString(x, 256);\n  }\n}\n\n// (public) convert to bigendian byte array\nfunction bnToByteArray() {\n  var this_array = this.array;\n  var i = this.t, r = new Array();\n  r[0] = this.s;\n  var p = BI_DB - (i * BI_DB) % 8, d, k = 0;\n  if (i-- > 0) {\n    if (p < BI_DB && (d = this_array[i] >> p) != (this.s & BI_DM) >> p)\n      r[k++] = d | (this.s << (BI_DB - p));\n    while (i >= 0) {\n      if (p < 8) {\n        d = (this_array[i] & ((1 << p) - 1)) << (8 - p);\n        d |= this_array[--i] >> (p += BI_DB - 8);\n      } else {\n        d = (this_array[i] >> (p -= 8)) & 0xff;\n        if (p <= 0) {\n          p += BI_DB;\n          --i;\n        }\n      }\n      if ((d & 0x80) != 0) d |= -256;\n      if (k == 0 && (this.s & 0x80) != (d & 0x80)) ++k;\n      if (k > 0 || d != this.s) r[k++] = d;\n    }\n  }\n  return r;\n}\n\nfunction bnEquals(a) {\n  return (this.compareTo(a) == 0);\n}\n\nfunction bnMin(a) {\n  return (this.compareTo(a) < 0) ? this : a;\n}\n\nfunction bnMax(a) {\n  return (this.compareTo(a) > 0) ? this : a;\n}\n\n// (protected) r = this op a (bitwise)\nfunction bnpBitwiseTo(a, op, r) {\n  var this_array = this.array;\n  var a_array = a.array;\n  var r_array = r.array;\n  var i, f, m = Math.min(a.t, this.t);\n  for (i = 0; i < m; ++i) r_array[i] = op(this_array[i], a_array[i]);\n  if (a.t < this.t) {\n    f = a.s & BI_DM;\n    for (i = m; i < this.t; ++i) r_array[i] = op(this_array[i], f);\n    r.t = this.t;\n  } else {\n    f = this.s & BI_DM;\n    for (i = m; i < a.t; ++i) r_array[i] = op(f, a_array[i]);\n    r.t = a.t;\n  }\n  r.s = op(this.s, a.s);\n  r.clamp();\n}\n\n// (public) this & a\nfunction op_and(x, y) {\n  return x & y;\n}\n\nfunction bnAnd(a) {\n  var r = nbi();\n  this.bitwiseTo(a, op_and, r);\n  return r;\n}\n\n// (public) this | a\nfunction op_or(x, y) {\n  return x | y;\n}\n\nfunction bnOr(a) {\n  var r = nbi();\n  this.bitwiseTo(a, op_or, r);\n  return r;\n}\n\n// (public) this ^ a\nfunction op_xor(x, y) {\n  return x ^ y;\n}\n\nfunction bnXor(a) {\n  var r = nbi();\n  this.bitwiseTo(a, op_xor, r);\n  return r;\n}\n\n// (public) this & ~a\nfunction op_andnot(x, y) {\n  return x & ~y;\n}\n\nfunction bnAndNot(a) {\n  var r = nbi();\n  this.bitwiseTo(a, op_andnot, r);\n  return r;\n}\n\n// (public) ~this\nfunction bnNot() {\n  var this_array = this.array;\n  var r = nbi();\n  var r_array = r.array;\n\n  for (var i = 0; i < this.t; ++i) r_array[i] = BI_DM & ~this_array[i];\n  r.t = this.t;\n  r.s = ~this.s;\n  return r;\n}\n\n// (public) this << n\nfunction bnShiftLeft(n) {\n  var r = nbi();\n  if (n < 0) this.rShiftTo(-n, r); else this.lShiftTo(n, r);\n  return r;\n}\n\n// (public) this >> n\nfunction bnShiftRight(n) {\n  var r = nbi();\n  if (n < 0) this.lShiftTo(-n, r); else this.rShiftTo(n, r);\n  return r;\n}\n\n// return index of lowest 1-bit in x, x < 2^31\nfunction lbit(x) {\n  if (x == 0) return -1;\n  var r = 0;\n  if ((x & 0xffff) == 0) {\n    x >>= 16;\n    r += 16;\n  }\n  if ((x & 0xff) == 0) {\n    x >>= 8;\n    r += 8;\n  }\n  if ((x & 0xf) == 0) {\n    x >>= 4;\n    r += 4;\n  }\n  if ((x & 3) == 0) {\n    x >>= 2;\n    r += 2;\n  }\n  if ((x & 1) == 0) ++r;\n  return r;\n}\n\n// (public) returns index of lowest 1-bit (or -1 if none)\nfunction bnGetLowestSetBit() {\n  var this_array = this.array;\n  for (var i = 0; i < this.t; ++i)\n    if (this_array[i] != 0) return i * BI_DB + lbit(this_array[i]);\n  if (this.s < 0) return this.t * BI_DB;\n  return -1;\n}\n\n// return number of 1 bits in x\nfunction cbit(x) {\n  var r = 0;\n  while (x != 0) {\n    x &= x - 1;\n    ++r;\n  }\n  return r;\n}\n\n// (public) return number of set bits\nfunction bnBitCount() {\n  var r = 0, x = this.s & BI_DM;\n  for (var i = 0; i < this.t; ++i) r += cbit(this_array[i] ^ x);\n  return r;\n}\n\n// (public) true iff nth bit is set\nfunction bnTestBit(n) {\n  var this_array = this.array;\n  var j = Math.floor(n / BI_DB);\n  if (j >= this.t) return (this.s != 0);\n  return ((this_array[j] & (1 << (n % BI_DB))) != 0);\n}\n\n// (protected) this op (1<<n)\nfunction bnpChangeBit(n, op) {\n  var r = BigInteger.ONE.shiftLeft(n);\n  this.bitwiseTo(r, op, r);\n  return r;\n}\n\n// (public) this | (1<<n)\nfunction bnSetBit(n) {\n  return this.changeBit(n, op_or);\n}\n\n// (public) this & ~(1<<n)\nfunction bnClearBit(n) {\n  return this.changeBit(n, op_andnot);\n}\n\n// (public) this ^ (1<<n)\nfunction bnFlipBit(n) {\n  return this.changeBit(n, op_xor);\n}\n\n// (protected) r = this + a\nfunction bnpAddTo(a, r) {\n  var this_array = this.array;\n  var a_array = a.array;\n  var r_array = r.array;\n  var i = 0, c = 0, m = Math.min(a.t, this.t);\n  while (i < m) {\n    c += this_array[i] + a_array[i];\n    r_array[i++] = c & BI_DM;\n    c >>= BI_DB;\n  }\n  if (a.t < this.t) {\n    c += a.s;\n    while (i < this.t) {\n      c += this_array[i];\n      r_array[i++] = c & BI_DM;\n      c >>= BI_DB;\n    }\n    c += this.s;\n  } else {\n    c += this.s;\n    while (i < a.t) {\n      c += a_array[i];\n      r_array[i++] = c & BI_DM;\n      c >>= BI_DB;\n    }\n    c += a.s;\n  }\n  r.s = (c < 0) ? -1 : 0;\n  if (c > 0) r_array[i++] = c;\n  else if (c < -1) r_array[i++] = BI_DV + c;\n  r.t = i;\n  r.clamp();\n}\n\n// (public) this + a\nfunction bnAdd(a) {\n  var r = nbi();\n  this.addTo(a, r);\n  return r;\n}\n\n// (public) this - a\nfunction bnSubtract(a) {\n  var r = nbi();\n  this.subTo(a, r);\n  return r;\n}\n\n// (public) this * a\nfunction bnMultiply(a) {\n  var r = nbi();\n  this.multiplyTo(a, r);\n  return r;\n}\n\n// (public) this / a\nfunction bnDivide(a) {\n  var r = nbi();\n  this.divRemTo(a, r, null);\n  return r;\n}\n\n// (public) this % a\nfunction bnRemainder(a) {\n  var r = nbi();\n  this.divRemTo(a, null, r);\n  return r;\n}\n\n// (public) [this/a,this%a]\nfunction bnDivideAndRemainder(a) {\n  var q = nbi(), r = nbi();\n  this.divRemTo(a, q, r);\n  return new Array(q, r);\n}\n\n// (protected) this *= n, this >= 0, 1 < n < DV\nfunction bnpDMultiply(n) {\n  var this_array = this.array;\n  this_array[this.t] = this.am(0, n - 1, this, 0, 0, this.t);\n  ++this.t;\n  this.clamp();\n}\n\n// (protected) this += n << w words, this >= 0\nfunction bnpDAddOffset(n, w) {\n  var this_array = this.array;\n  while (this.t <= w) this_array[this.t++] = 0;\n  this_array[w] += n;\n  while (this_array[w] >= BI_DV) {\n    this_array[w] -= BI_DV;\n    if (++w >= this.t) this_array[this.t++] = 0;\n    ++this_array[w];\n  }\n}\n\n// A \"null\" reducer\nfunction NullExp() {\n}\n\nfunction nNop(x) {\n  return x;\n}\n\nfunction nMulTo(x, y, r) {\n  x.multiplyTo(y, r);\n}\n\nfunction nSqrTo(x, r) {\n  x.squareTo(r);\n}\n\nNullExp.prototype.convert = nNop;\nNullExp.prototype.revert = nNop;\nNullExp.prototype.mulTo = nMulTo;\nNullExp.prototype.sqrTo = nSqrTo;\n\n// (public) this^e\nfunction bnPow(e) {\n  return this.exp(e, new NullExp());\n}\n\n// (protected) r = lower n words of \"this * a\", a.t <= n\n// \"this\" should be the larger one if appropriate.\nfunction bnpMultiplyLowerTo(a, n, r) {\n  var r_array = r.array;\n  var a_array = a.array;\n  var i = Math.min(this.t + a.t, n);\n  r.s = 0; // assumes a,this >= 0\n  r.t = i;\n  while (i > 0) r_array[--i] = 0;\n  var j;\n  for (j = r.t - this.t; i < j; ++i) r_array[i + this.t] = this.am(0, a_array[i], r, i, 0, this.t);\n  for (j = Math.min(a.t, n); i < j; ++i) this.am(0, a_array[i], r, i, 0, n - i);\n  r.clamp();\n}\n\n// (protected) r = \"this * a\" without lower n words, n > 0\n// \"this\" should be the larger one if appropriate.\nfunction bnpMultiplyUpperTo(a, n, r) {\n  var r_array = r.array;\n  var a_array = a.array;\n  --n;\n  var i = r.t = this.t + a.t - n;\n  r.s = 0; // assumes a,this >= 0\n  while (--i >= 0) r_array[i] = 0;\n  for (i = Math.max(n - this.t, 0); i < a.t; ++i)\n    r_array[this.t + i - n] = this.am(n - i, a_array[i], r, 0, 0, this.t + i - n);\n  r.clamp();\n  r.drShiftTo(1, r);\n}\n\n// Barrett modular reduction\nfunction Barrett(m) {\n  // setup Barrett\n  this.r2 = nbi();\n  this.q3 = nbi();\n  BigInteger.ONE.dlShiftTo(2 * m.t, this.r2);\n  this.mu = this.r2.divide(m);\n  this.m = m;\n}\n\nfunction barrettConvert(x) {\n  if (x.s < 0 || x.t > 2 * this.m.t) return x.mod(this.m);\n  else if (x.compareTo(this.m) < 0) return x;\n  else {\n    var r = nbi();\n    x.copyTo(r);\n    this.reduce(r);\n    return r;\n  }\n}\n\nfunction barrettRevert(x) {\n  return x;\n}\n\n// x = x mod m (HAC 14.42)\nfunction barrettReduce(x) {\n  x.drShiftTo(this.m.t - 1, this.r2);\n  if (x.t > this.m.t + 1) {\n    x.t = this.m.t + 1;\n    x.clamp();\n  }\n  this.mu.multiplyUpperTo(this.r2, this.m.t + 1, this.q3);\n  this.m.multiplyLowerTo(this.q3, this.m.t + 1, this.r2);\n  while (x.compareTo(this.r2) < 0) x.dAddOffset(1, this.m.t + 1);\n  x.subTo(this.r2, x);\n  while (x.compareTo(this.m) >= 0) x.subTo(this.m, x);\n}\n\n// r = x^2 mod m; x != r\nfunction barrettSqrTo(x, r) {\n  x.squareTo(r);\n  this.reduce(r);\n}\n\n// r = x*y mod m; x,y != r\nfunction barrettMulTo(x, y, r) {\n  x.multiplyTo(y, r);\n  this.reduce(r);\n}\n\nBarrett.prototype.convert = barrettConvert;\nBarrett.prototype.revert = barrettRevert;\nBarrett.prototype.reduce = barrettReduce;\nBarrett.prototype.mulTo = barrettMulTo;\nBarrett.prototype.sqrTo = barrettSqrTo;\n\n// (public) this^e % m (HAC 14.85)\nfunction bnModPow(e, m) {\n  var e_array = e.array;\n  var i = e.bitLength(), k, r = nbv(1), z;\n  if (i <= 0) return r;\n  else if (i < 18) k = 1;\n  else if (i < 48) k = 3;\n  else if (i < 144) k = 4;\n  else if (i < 768) k = 5;\n  else k = 6;\n  if (i < 8)\n    z = new Classic(m);\n  else if (m.isEven())\n    z = new Barrett(m);\n  else\n    z = new Montgomery(m);\n\n  // precomputation\n  var g = new Array(), n = 3, k1 = k - 1, km = (1 << k) - 1;\n  g[1] = z.convert(this);\n  if (k > 1) {\n    var g2 = nbi();\n    z.sqrTo(g[1], g2);\n    while (n <= km) {\n      g[n] = nbi();\n      z.mulTo(g2, g[n - 2], g[n]);\n      n += 2;\n    }\n  }\n\n  var j = e.t - 1, w, is1 = true, r2 = nbi(), t;\n  i = nbits(e_array[j]) - 1;\n  while (j >= 0) {\n    if (i >= k1) w = (e_array[j] >> (i - k1)) & km;\n    else {\n      w = (e_array[j] & ((1 << (i + 1)) - 1)) << (k1 - i);\n      if (j > 0) w |= e_array[j - 1] >> (BI_DB + i - k1);\n    }\n\n    n = k;\n    while ((w & 1) == 0) {\n      w >>= 1;\n      --n;\n    }\n    if ((i -= n) < 0) {\n      i += BI_DB;\n      --j;\n    }\n    if (is1) {\t// ret == 1, don't bother squaring or multiplying it\n      g[w].copyTo(r);\n      is1 = false;\n    } else {\n      while (n > 1) {\n        z.sqrTo(r, r2);\n        z.sqrTo(r2, r);\n        n -= 2;\n      }\n      if (n > 0) z.sqrTo(r, r2); else {\n        t = r;\n        r = r2;\n        r2 = t;\n      }\n      z.mulTo(r2, g[w], r);\n    }\n\n    while (j >= 0 && (e_array[j] & (1 << i)) == 0) {\n      z.sqrTo(r, r2);\n      t = r;\n      r = r2;\n      r2 = t;\n      if (--i < 0) {\n        i = BI_DB - 1;\n        --j;\n      }\n    }\n  }\n  return z.revert(r);\n}\n\n// (public) gcd(this,a) (HAC 14.54)\nfunction bnGCD(a) {\n  var x = (this.s < 0) ? this.negate() : this.clone();\n  var y = (a.s < 0) ? a.negate() : a.clone();\n  if (x.compareTo(y) < 0) {\n    var t = x;\n    x = y;\n    y = t;\n  }\n  var i = x.getLowestSetBit(), g = y.getLowestSetBit();\n  if (g < 0) return x;\n  if (i < g) g = i;\n  if (g > 0) {\n    x.rShiftTo(g, x);\n    y.rShiftTo(g, y);\n  }\n  while (x.signum() > 0) {\n    if ((i = x.getLowestSetBit()) > 0) x.rShiftTo(i, x);\n    if ((i = y.getLowestSetBit()) > 0) y.rShiftTo(i, y);\n    if (x.compareTo(y) >= 0) {\n      x.subTo(y, x);\n      x.rShiftTo(1, x);\n    } else {\n      y.subTo(x, y);\n      y.rShiftTo(1, y);\n    }\n  }\n  if (g > 0) y.lShiftTo(g, y);\n  return y;\n}\n\n// (protected) this % n, n < 2^26\nfunction bnpModInt(n) {\n  var this_array = this.array;\n  if (n <= 0) return 0;\n  var d = BI_DV % n, r = (this.s < 0) ? n - 1 : 0;\n  if (this.t > 0)\n    if (d == 0) r = this_array[0] % n;\n    else for (var i = this.t - 1; i >= 0; --i) r = (d * r + this_array[i]) % n;\n  return r;\n}\n\n// (public) 1/this % m (HAC 14.61)\nfunction bnModInverse(m) {\n  var ac = m.isEven();\n  if ((this.isEven() && ac) || m.signum() == 0) return BigInteger.ZERO;\n  var u = m.clone(), v = this.clone();\n  var a = nbv(1), b = nbv(0), c = nbv(0), d = nbv(1);\n  while (u.signum() != 0) {\n    while (u.isEven()) {\n      u.rShiftTo(1, u);\n      if (ac) {\n        if (!a.isEven() || !b.isEven()) {\n          a.addTo(this, a);\n          b.subTo(m, b);\n        }\n        a.rShiftTo(1, a);\n      } else if (!b.isEven()) b.subTo(m, b);\n      b.rShiftTo(1, b);\n    }\n    while (v.isEven()) {\n      v.rShiftTo(1, v);\n      if (ac) {\n        if (!c.isEven() || !d.isEven()) {\n          c.addTo(this, c);\n          d.subTo(m, d);\n        }\n        c.rShiftTo(1, c);\n      } else if (!d.isEven()) d.subTo(m, d);\n      d.rShiftTo(1, d);\n    }\n    if (u.compareTo(v) >= 0) {\n      u.subTo(v, u);\n      if (ac) a.subTo(c, a);\n      b.subTo(d, b);\n    } else {\n      v.subTo(u, v);\n      if (ac) c.subTo(a, c);\n      d.subTo(b, d);\n    }\n  }\n  if (v.compareTo(BigInteger.ONE) != 0) return BigInteger.ZERO;\n  if (d.compareTo(m) >= 0) return d.subtract(m);\n  if (d.signum() < 0) d.addTo(m, d); else return d;\n  if (d.signum() < 0) return d.add(m); else return d;\n}\n\nvar lowprimes = [2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97, 101, 103, 107, 109, 113, 127, 131, 137, 139, 149, 151, 157, 163, 167, 173, 179, 181, 191, 193, 197, 199, 211, 223, 227, 229, 233, 239, 241, 251, 257, 263, 269, 271, 277, 281, 283, 293, 307, 311, 313, 317, 331, 337, 347, 349, 353, 359, 367, 373, 379, 383, 389, 397, 401, 409, 419, 421, 431, 433, 439, 443, 449, 457, 461, 463, 467, 479, 487, 491, 499, 503, 509];\nvar lplim = (1 << 26) / lowprimes[lowprimes.length - 1];\n\n// (public) test primality with certainty >= 1-.5^t\nfunction bnIsProbablePrime(t) {\n  var i, x = this.abs();\n  var x_array = x.array;\n  if (x.t == 1 && x_array[0] <= lowprimes[lowprimes.length - 1]) {\n    for (i = 0; i < lowprimes.length; ++i)\n      if (x_array[0] == lowprimes[i]) return true;\n    return false;\n  }\n  if (x.isEven()) return false;\n  i = 1;\n  while (i < lowprimes.length) {\n    var m = lowprimes[i], j = i + 1;\n    while (j < lowprimes.length && m < lplim) m *= lowprimes[j++];\n    m = x.modInt(m);\n    while (i < j) if (m % lowprimes[i++] == 0) return false;\n  }\n  return x.millerRabin(t);\n}\n\n// (protected) true if probably prime (HAC 4.24, Miller-Rabin)\nfunction bnpMillerRabin(t) {\n  var n1 = this.subtract(BigInteger.ONE);\n  var k = n1.getLowestSetBit();\n  if (k <= 0) return false;\n  var r = n1.shiftRight(k);\n  t = (t + 1) >> 1;\n  if (t > lowprimes.length) t = lowprimes.length;\n  var a = nbi();\n  for (var i = 0; i < t; ++i) {\n    a.fromInt(lowprimes[i]);\n    var y = a.modPow(r, this);\n    if (y.compareTo(BigInteger.ONE) != 0 && y.compareTo(n1) != 0) {\n      var j = 1;\n      while (j++ < k && y.compareTo(n1) != 0) {\n        y = y.modPowInt(2, this);\n        if (y.compareTo(BigInteger.ONE) == 0) return false;\n      }\n      if (y.compareTo(n1) != 0) return false;\n    }\n  }\n  return true;\n}\n\n// protected\nBigInteger.prototype.chunkSize = bnpChunkSize;\nBigInteger.prototype.toRadix = bnpToRadix;\nBigInteger.prototype.fromRadix = bnpFromRadix;\nBigInteger.prototype.fromNumber = bnpFromNumber;\nBigInteger.prototype.bitwiseTo = bnpBitwiseTo;\nBigInteger.prototype.changeBit = bnpChangeBit;\nBigInteger.prototype.addTo = bnpAddTo;\nBigInteger.prototype.dMultiply = bnpDMultiply;\nBigInteger.prototype.dAddOffset = bnpDAddOffset;\nBigInteger.prototype.multiplyLowerTo = bnpMultiplyLowerTo;\nBigInteger.prototype.multiplyUpperTo = bnpMultiplyUpperTo;\nBigInteger.prototype.modInt = bnpModInt;\nBigInteger.prototype.millerRabin = bnpMillerRabin;\n\n// public\nBigInteger.prototype.clone = bnClone;\nBigInteger.prototype.intValue = bnIntValue;\nBigInteger.prototype.byteValue = bnByteValue;\nBigInteger.prototype.shortValue = bnShortValue;\nBigInteger.prototype.signum = bnSigNum;\nBigInteger.prototype.toByteArray = bnToByteArray;\nBigInteger.prototype.equals = bnEquals;\nBigInteger.prototype.min = bnMin;\nBigInteger.prototype.max = bnMax;\nBigInteger.prototype.and = bnAnd;\nBigInteger.prototype.or = bnOr;\nBigInteger.prototype.xor = bnXor;\nBigInteger.prototype.andNot = bnAndNot;\nBigInteger.prototype.not = bnNot;\nBigInteger.prototype.shiftLeft = bnShiftLeft;\nBigInteger.prototype.shiftRight = bnShiftRight;\nBigInteger.prototype.getLowestSetBit = bnGetLowestSetBit;\nBigInteger.prototype.bitCount = bnBitCount;\nBigInteger.prototype.testBit = bnTestBit;\nBigInteger.prototype.setBit = bnSetBit;\nBigInteger.prototype.clearBit = bnClearBit;\nBigInteger.prototype.flipBit = bnFlipBit;\nBigInteger.prototype.add = bnAdd;\nBigInteger.prototype.subtract = bnSubtract;\nBigInteger.prototype.multiply = bnMultiply;\nBigInteger.prototype.divide = bnDivide;\nBigInteger.prototype.remainder = bnRemainder;\nBigInteger.prototype.divideAndRemainder = bnDivideAndRemainder;\nBigInteger.prototype.modPow = bnModPow;\nBigInteger.prototype.modInverse = bnModInverse;\nBigInteger.prototype.pow = bnPow;\nBigInteger.prototype.gcd = bnGCD;\nBigInteger.prototype.isProbablePrime = bnIsProbablePrime;\n\n// BigInteger interfaces not implemented in jsbn:\n\n// BigInteger(int signum, byte[] magnitude)\n// double doubleValue()\n// float floatValue()\n// int hashCode()\n// long longValue()\n// static BigInteger valueOf(long val)\n// prng4.js - uses Arcfour as a PRNG\n\nfunction Arcfour() {\n  this.i = 0;\n  this.j = 0;\n  this.S = new Array();\n}\n\n// Initialize arcfour context from key, an array of ints, each from [0..255]\nfunction ARC4init(key) {\n  var i, j, t;\n  for (i = 0; i < 256; ++i)\n    this.S[i] = i;\n  j = 0;\n  for (i = 0; i < 256; ++i) {\n    j = (j + this.S[i] + key[i % key.length]) & 255;\n    t = this.S[i];\n    this.S[i] = this.S[j];\n    this.S[j] = t;\n  }\n  this.i = 0;\n  this.j = 0;\n}\n\nfunction ARC4next() {\n  var t;\n  this.i = (this.i + 1) & 255;\n  this.j = (this.j + this.S[this.i]) & 255;\n  t = this.S[this.i];\n  this.S[this.i] = this.S[this.j];\n  this.S[this.j] = t;\n  return this.S[(t + this.S[this.i]) & 255];\n}\n\nArcfour.prototype.init = ARC4init;\nArcfour.prototype.next = ARC4next;\n\n// Plug in your RNG constructor here\nfunction prng_newstate() {\n  return new Arcfour();\n}\n\n// Pool size must be a multiple of 4 and greater than 32.\n// An array of bytes the size of the pool will be passed to init()\nvar rng_psize = 256;\n// Random number generator - requires a PRNG backend, e.g. prng4.js\n\n// For best results, put code like\n// <body onClick='rng_seed_time();' onKeyPress='rng_seed_time();'>\n// in your main HTML document.\n\nvar rng_state;\nvar rng_pool;\nvar rng_pptr;\n\n// Mix in a 32-bit integer into the pool\nfunction rng_seed_int(x) {\n  rng_pool[rng_pptr++] ^= x & 255;\n  rng_pool[rng_pptr++] ^= (x >> 8) & 255;\n  rng_pool[rng_pptr++] ^= (x >> 16) & 255;\n  rng_pool[rng_pptr++] ^= (x >> 24) & 255;\n  if (rng_pptr >= rng_psize) rng_pptr -= rng_psize;\n}\n\n// Mix in the current time (w/milliseconds) into the pool\nfunction rng_seed_time() {\n  // Use pre-computed date to avoid making the benchmark\n  // results dependent on the current date.\n  rng_seed_int(1122926989487);\n}\n\n// Initialize the pool with junk if needed.\nif (rng_pool == null) {\n  rng_pool = new Array();\n  rng_pptr = 0;\n  var t;\n  while (rng_pptr < rng_psize) {  // extract some randomness from Math.random()\n    t = Math.floor(65536 * Math.random());\n    rng_pool[rng_pptr++] = t >>> 8;\n    rng_pool[rng_pptr++] = t & 255;\n  }\n  rng_pptr = 0;\n  rng_seed_time();\n  //rng_seed_int(window.screenX);\n  //rng_seed_int(window.screenY);\n}\n\nfunction rng_get_byte() {\n  if (rng_state == null) {\n    rng_seed_time();\n    rng_state = prng_newstate();\n    rng_state.init(rng_pool);\n    for (rng_pptr = 0; rng_pptr < rng_pool.length; ++rng_pptr)\n      rng_pool[rng_pptr] = 0;\n    rng_pptr = 0;\n    //rng_pool = null;\n  }\n  // TODO: allow reseeding after first request\n  return rng_state.next();\n}\n\nfunction rng_get_bytes(ba) {\n  var i;\n  for (i = 0; i < ba.length; ++i) ba[i] = rng_get_byte();\n}\n\nfunction SecureRandom() {\n}\n\nSecureRandom.prototype.nextBytes = rng_get_bytes;\n// Depends on jsbn.js and rng.js\n\n// convert a (hex) string to a bignum object\nfunction parseBigInt(str, r) {\n  return new BigInteger(str, r);\n}\n\nfunction linebrk(s, n) {\n  var ret = \"\";\n  var i = 0;\n  while (i + n < s.length) {\n    ret += s.substring(i, i + n) + \"\\n\";\n    i += n;\n  }\n  return ret + s.substring(i, s.length);\n}\n\nfunction byte2Hex(b) {\n  if (b < 0x10)\n    return \"0\" + b.toString(16);\n  else\n    return b.toString(16);\n}\n\n// PKCS#1 (type 2, random) pad input string s to n bytes, and return a bigint\nfunction pkcs1pad2(s, n) {\n  if (n < s.length + 11) {\n    alert(\"Message too long for RSA\");\n    return null;\n  }\n  var ba = new Array();\n  var i = s.length - 1;\n  while (i >= 0 && n > 0) ba[--n] = s.charCodeAt(i--);\n  ba[--n] = 0;\n  var rng = new SecureRandom();\n  var x = new Array();\n  while (n > 2) { // random non-zero pad\n    x[0] = 0;\n    while (x[0] == 0) rng.nextBytes(x);\n    ba[--n] = x[0];\n  }\n  ba[--n] = 2;\n  ba[--n] = 0;\n  return new BigInteger(ba);\n}\n\n// \"empty\" RSA key constructor\nfunction RSAKey() {\n  this.n = null;\n  this.e = 0;\n  this.d = null;\n  this.p = null;\n  this.q = null;\n  this.dmp1 = null;\n  this.dmq1 = null;\n  this.coeff = null;\n}\n\n// Set the public key fields N and e from hex strings\nfunction RSASetPublic(N, E) {\n  if (N != null && E != null && N.length > 0 && E.length > 0) {\n    this.n = parseBigInt(N, 16);\n    this.e = parseInt(E, 16);\n  } else\n    alert(\"Invalid RSA public key\");\n}\n\n// Perform raw public operation on \"x\": return x^e (mod n)\nfunction RSADoPublic(x) {\n  return x.modPowInt(this.e, this.n);\n}\n\n// Return the PKCS#1 RSA encryption of \"text\" as an even-length hex string\nfunction RSAEncrypt(text) {\n  var m = pkcs1pad2(text, (this.n.bitLength() + 7) >> 3);\n  if (m == null) return null;\n  var c = this.doPublic(m);\n  if (c == null) return null;\n  var h = c.toString(16);\n  if ((h.length & 1) == 0) return h; else return \"0\" + h;\n}\n\n// Return the PKCS#1 RSA encryption of \"text\" as a Base64-encoded string\n//function RSAEncryptB64(text) {\n//  var h = this.encrypt(text);\n//  if(h) return hex2b64(h); else return null;\n//}\n\n// protected\nRSAKey.prototype.doPublic = RSADoPublic;\n\n// public\nRSAKey.prototype.setPublic = RSASetPublic;\nRSAKey.prototype.encrypt = RSAEncrypt;\n//RSAKey.prototype.encrypt_b64 = RSAEncryptB64;\n// Depends on rsa.js and jsbn2.js\n\n// Undo PKCS#1 (type 2, random) padding and, if valid, return the plaintext\nfunction pkcs1unpad2(d, n) {\n  var b = d.toByteArray();\n  var i = 0;\n  while (i < b.length && b[i] == 0) ++i;\n  if (b.length - i != n - 1 || b[i] != 2)\n    return null;\n  ++i;\n  while (b[i] != 0)\n    if (++i >= b.length) return null;\n  var ret = \"\";\n  while (++i < b.length)\n    ret += String.fromCharCode(b[i]);\n  return ret;\n}\n\n// Set the private key fields N, e, and d from hex strings\nfunction RSASetPrivate(N, E, D) {\n  if (N != null && E != null && N.length > 0 && E.length > 0) {\n    this.n = parseBigInt(N, 16);\n    this.e = parseInt(E, 16);\n    this.d = parseBigInt(D, 16);\n  } else\n    alert(\"Invalid RSA private key\");\n}\n\n// Set the private key fields N, e, d and CRT params from hex strings\nfunction RSASetPrivateEx(N, E, D, P, Q, DP, DQ, C) {\n  if (N != null && E != null && N.length > 0 && E.length > 0) {\n    this.n = parseBigInt(N, 16);\n    this.e = parseInt(E, 16);\n    this.d = parseBigInt(D, 16);\n    this.p = parseBigInt(P, 16);\n    this.q = parseBigInt(Q, 16);\n    this.dmp1 = parseBigInt(DP, 16);\n    this.dmq1 = parseBigInt(DQ, 16);\n    this.coeff = parseBigInt(C, 16);\n  } else\n    alert(\"Invalid RSA private key\");\n}\n\n// Generate a new random private key B bits long, using public expt E\nfunction RSAGenerate(B, E) {\n  var rng = new SecureRandom();\n  var qs = B >> 1;\n  this.e = parseInt(E, 16);\n  var ee = new BigInteger(E, 16);\n  for (; ;) {\n    for (; ;) {\n      this.p = new BigInteger(B - qs, 1, rng);\n      if (this.p.subtract(BigInteger.ONE).gcd(ee).compareTo(BigInteger.ONE) == 0 && this.p.isProbablePrime(10)) break;\n    }\n    for (; ;) {\n      this.q = new BigInteger(qs, 1, rng);\n      if (this.q.subtract(BigInteger.ONE).gcd(ee).compareTo(BigInteger.ONE) == 0 && this.q.isProbablePrime(10)) break;\n    }\n    if (this.p.compareTo(this.q) <= 0) {\n      var t = this.p;\n      this.p = this.q;\n      this.q = t;\n    }\n    var p1 = this.p.subtract(BigInteger.ONE);\n    var q1 = this.q.subtract(BigInteger.ONE);\n    var phi = p1.multiply(q1);\n    if (phi.gcd(ee).compareTo(BigInteger.ONE) == 0) {\n      this.n = this.p.multiply(this.q);\n      this.d = ee.modInverse(phi);\n      this.dmp1 = this.d.mod(p1);\n      this.dmq1 = this.d.mod(q1);\n      this.coeff = this.q.modInverse(this.p);\n      break;\n    }\n  }\n}\n\n// Perform raw private operation on \"x\": return x^d (mod n)\nfunction RSADoPrivate(x) {\n  if (this.p == null || this.q == null)\n    return x.modPow(this.d, this.n);\n\n  // TODO: re-calculate any missing CRT params\n  var xp = x.mod(this.p).modPow(this.dmp1, this.p);\n  var xq = x.mod(this.q).modPow(this.dmq1, this.q);\n\n  while (xp.compareTo(xq) < 0)\n    xp = xp.add(this.p);\n  return xp.subtract(xq).multiply(this.coeff).mod(this.p).multiply(this.q).add(xq);\n}\n\n// Return the PKCS#1 RSA decryption of \"ctext\".\n// \"ctext\" is an even-length hex string and the output is a plain string.\nfunction RSADecrypt(ctext) {\n  var c = parseBigInt(ctext, 16);\n  var m = this.doPrivate(c);\n  if (m == null) return null;\n  return pkcs1unpad2(m, (this.n.bitLength() + 7) >> 3);\n}\n\n// Return the PKCS#1 RSA decryption of \"ctext\".\n// \"ctext\" is a Base64-encoded string and the output is a plain string.\n//function RSAB64Decrypt(ctext) {\n//  var h = b64tohex(ctext);\n//  if(h) return this.decrypt(h); else return null;\n//}\n\n// protected\nRSAKey.prototype.doPrivate = RSADoPrivate;\n\n// public\nRSAKey.prototype.setPrivate = RSASetPrivate;\nRSAKey.prototype.setPrivateEx = RSASetPrivateEx;\nRSAKey.prototype.generate = RSAGenerate;\nRSAKey.prototype.decrypt = RSADecrypt;\n//RSAKey.prototype.b64_decrypt = RSAB64Decrypt;\n\n\nvar nValue = \"a5261939975948bb7a58dffe5ff54e65f0498f9175f5a09288810b8975871e99af3b5dd94057b0fc07535f5f97444504fa35169d461d0d30cf0192e307727c065168c788771c561a9400fb49175e9e6aa4e23fe11af69e9412dd23b0cb6684c4c2429bce139e848ab26d0829073351f4acd36074eafd036a5eb83359d2a698d3\";\nvar eValue = \"10001\";\nvar dValue = \"8e9912f6d3645894e8d38cb58c0db81ff516cf4c7e5a14c7f1eddb1459d2cded4d8d293fc97aee6aefb861859c8b6a3d1dfe710463e1f9ddc72048c09751971c4a580aa51eb523357a3cc48d31cfad1d4a165066ed92d4748fb6571211da5cb14bc11b6e2df7c1a559e6d5ac1cd5c94703a22891464fba23d0d965086277a161\";\nvar pValue = \"d090ce58a92c75233a6486cb0a9209bf3583b64f540c76f5294bb97d285eed33aec220bde14b2417951178ac152ceab6da7090905b478195498b352048f15e7d\";\nvar qValue = \"cab575dc652bb66df15a0359609d51d1db184750c00c6698b90ef3465c99655103edbf0d54c56aec0ce3c4d22592338092a126a0cc49f65a4a30d222b411e58f\";\nvar dmp1Value = \"1a24bca8e273df2f0e47c199bbf678604e7df7215480c77c8db39f49b000ce2cf7500038acfff5433b7d582a01f1826e6f4d42e1c57f5e1fef7b12aabc59fd25\";\nvar dmq1Value = \"3d06982efbbe47339e1f6d36b1216b8a741d410b0c662f54f7118b27b9a4ec9d914337eb39841d8666f3034408cf94f5b62f11c402fc994fe15a05493150d9fd\";\nvar coeffValue = \"3a3e731acd8960b7ff9eb81a7ff93bd1cfa74cbd56987db58b4594fb09c09084db1734c8143f98b602b981aaa9243ca28deb69b5b280ee8dcee0fd2625e53250\";\n\nsetupEngine(am3, 28);\n\nvar TEXT = \"The quick brown fox jumped over the extremely lazy frog! \" +\n  \"Now is the time for all good men to come to the party.\";\nvar encrypted;\n\nfunction encrypt() {\n  var RSA = new RSAKey();\n  RSA.setPublic(nValue, eValue);\n  RSA.setPrivateEx(nValue, eValue, dValue, pValue, qValue, dmp1Value, dmq1Value, coeffValue);\n  encrypted = RSA.encrypt(TEXT);\n}\n\nfunction decrypt() {\n  var RSA = new RSAKey();\n  RSA.setPublic(nValue, eValue);\n  RSA.setPrivateEx(nValue, eValue, dValue, pValue, qValue, dmp1Value, dmq1Value, coeffValue);\n  var decrypted = RSA.decrypt(encrypted);\n  if (decrypted != TEXT) {\n    throw new Error(\"Crypto operation failed\");\n  }\n}\n\n// The code has been adapted for use as a benchmark by Google.\nvar Crypto = new BenchmarkSuite('Crypto', 266181, [\n  new Benchmark(\"Encrypt\", encrypt),\n  new Benchmark(\"Decrypt\", decrypt)\n]);\n\n/* run_harness.js */\nvar print = console.log;\n\nfunction Run() {\n  BenchmarkSuite.RunSuites({\n    NotifyStep: ShowProgress,\n    NotifyError: AddError,\n    NotifyResult: AddResult,\n    NotifyScore: AddScore,\n  });\n}\n\nvar harnessErrorCount = 0;\n\nfunction ShowProgress(name) {\n  print(\"PROGRESS\", name);\n}\n\nfunction AddError(name, error) {\n  print(\"ERROR\", name, error);\n  print(error.stack);\n  harnessErrorCount++;\n}\n\nfunction AddResult(name, result) {\n  print(\"RESULT\", name, result);\n}\n\nfunction AddScore(score) {\n  print(\"SCORE\", score);\n}\n\nfunction main() {\n  Run();\n}\n"
  },
  {
    "path": "benches/scripts/v8-benches/deltablue.js",
    "content": "\"use strict\";\n\"use strip\";\n// Copyright 2012 the V8 project authors. All rights reserved.\n// Redistribution and use in source and binary forms, with or without\n// modification, are permitted provided that the following conditions are\n// met:\n//\n//     * Redistributions of source code must retain the above copyright\n//       notice, this list of conditions and the following disclaimer.\n//     * Redistributions in binary form must reproduce the above\n//       copyright notice, this list of conditions and the following\n//       disclaimer in the documentation and/or other materials provided\n//       with the distribution.\n//     * Neither the name of Google Inc. nor the names of its\n//       contributors may be used to endorse or promote products derived\n//       from this software without specific prior written permission.\n//\n// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS\n// \"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT\n// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR\n// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT\n// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,\n// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT\n// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,\n// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY\n// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n\n// Simple framework for running the benchmark suites and\n// computing a score based on the timing measurements.\n\n\n// A benchmark has a name (string) and a function that will be run to\n// do the performance measurement. The optional setup and tearDown\n// arguments are functions that will be invoked before and after\n// running the benchmark, but the running time of these functions will\n// not be accounted for in the benchmark score.\nfunction Benchmark(name, run, setup, tearDown) {\n  this.name = name;\n  this.run = run;\n  this.Setup = setup ? setup : function () {\n  };\n  this.TearDown = tearDown ? tearDown : function () {\n  };\n}\n\n\n// Benchmark results hold the benchmark and the measured time used to\n// run the benchmark. The benchmark score is computed later once a\n// full benchmark suite has run to completion.\nfunction BenchmarkResult(benchmark, time) {\n  this.benchmark = benchmark;\n  this.time = time;\n}\n\n\n// Automatically convert results to numbers. Used by the geometric\n// mean computation.\nBenchmarkResult.prototype.valueOf = function () {\n  return this.time;\n};\n\n\n// Suites of benchmarks consist of a name and the set of benchmarks in\n// addition to the reference timing that the final score will be based\n// on. This way, all scores are relative to a reference run and higher\n// scores implies better performance.\nfunction BenchmarkSuite(name, reference, benchmarks) {\n  this.name = name;\n  this.reference = reference;\n  this.benchmarks = benchmarks;\n  BenchmarkSuite.suites.push(this);\n}\n\n\n// Keep track of all declared benchmark suites.\nBenchmarkSuite.suites = [];\n\n\n// Scores are not comparable across versions. Bump the version if\n// you're making changes that will affect that scores, e.g. if you add\n// a new benchmark or change an existing one.\nBenchmarkSuite.version = '7';\n\n\n// To make the benchmark results predictable, we replace Math.random\n// with a 100% deterministic alternative.\nMath.random = (function () {\n  var seed = 49734321;\n  return function () {\n    // Robert Jenkins' 32 bit integer hash function.\n    seed = ((seed + 0x7ed55d16) + (seed << 12)) & 0xffffffff;\n    seed = ((seed ^ 0xc761c23c) ^ (seed >>> 19)) & 0xffffffff;\n    seed = ((seed + 0x165667b1) + (seed << 5)) & 0xffffffff;\n    seed = ((seed + 0xd3a2646c) ^ (seed << 9)) & 0xffffffff;\n    seed = ((seed + 0xfd7046c5) + (seed << 3)) & 0xffffffff;\n    seed = ((seed ^ 0xb55a4f09) ^ (seed >>> 16)) & 0xffffffff;\n    return (seed & 0xfffffff) / 0x10000000;\n  };\n})();\n\n\n// Runs all registered benchmark suites and optionally yields between\n// each individual benchmark to avoid running for too long in the\n// context of browsers. Once done, the final score is reported to the\n// runner.\nBenchmarkSuite.RunSuites = function (runner) {\n  var continuation = null;\n  var suites = BenchmarkSuite.suites;\n  var length = suites.length;\n  BenchmarkSuite.scores = [];\n  var index = 0;\n\n  function RunStep() {\n    while (continuation || index < length) {\n      if (continuation) {\n        continuation = continuation();\n      } else {\n        var suite = suites[index++];\n        if (runner.NotifyStart) runner.NotifyStart(suite.name);\n        continuation = suite.RunStep(runner);\n      }\n      if (continuation && typeof window != 'undefined' && window.setTimeout) {\n        window.setTimeout(RunStep, 25);\n        return;\n      }\n    }\n    if (runner.NotifyScore) {\n      var score = BenchmarkSuite.GeometricMean(BenchmarkSuite.scores);\n      var formatted = BenchmarkSuite.FormatScore(100 * score);\n      runner.NotifyScore(formatted);\n    }\n  }\n\n  RunStep();\n};\n\n\n// Counts the total number of registered benchmarks. Useful for\n// showing progress as a percentage.\nBenchmarkSuite.CountBenchmarks = function () {\n  var result = 0;\n  var suites = BenchmarkSuite.suites;\n  for (var i = 0; i < suites.length; i++) {\n    result += suites[i].benchmarks.length;\n  }\n  return result;\n};\n\n\n// Computes the geometric mean of a set of numbers.\nBenchmarkSuite.GeometricMean = function (numbers) {\n  var log = 0;\n  for (var i = 0; i < numbers.length; i++) {\n    log += Math.log(numbers[i]);\n  }\n  return Math.pow(Math.E, log / numbers.length);\n};\n\n\n// Converts a score value to a string with at least three significant\n// digits.\nBenchmarkSuite.FormatScore = function (value) {\n  if (value > 100) {\n    return value.toFixed(0);\n  } else {\n    return value.toPrecision(3);\n  }\n};\n\n// Notifies the runner that we're done running a single benchmark in\n// the benchmark suite. This can be useful to report progress.\nBenchmarkSuite.prototype.NotifyStep = function (result) {\n  this.results.push(result);\n  if (this.runner.NotifyStep) this.runner.NotifyStep(result.benchmark.name);\n};\n\n\n// Notifies the runner that we're done with running a suite and that\n// we have a result which can be reported to the user if needed.\nBenchmarkSuite.prototype.NotifyResult = function () {\n  var mean = BenchmarkSuite.GeometricMean(this.results);\n  var score = this.reference / mean;\n  BenchmarkSuite.scores.push(score);\n  if (this.runner.NotifyResult) {\n    var formatted = BenchmarkSuite.FormatScore(100 * score);\n    this.runner.NotifyResult(this.name, formatted);\n  }\n};\n\n\n// Notifies the runner that running a benchmark resulted in an error.\nBenchmarkSuite.prototype.NotifyError = function (error) {\n  if (this.runner.NotifyError) {\n    this.runner.NotifyError(this.name, error);\n  }\n  if (this.runner.NotifyStep) {\n    this.runner.NotifyStep(this.name);\n  }\n};\n\n\n// Runs a single benchmark for at least a second and computes the\n// average time it takes to run a single iteration.\nBenchmarkSuite.prototype.RunSingleBenchmark = function (benchmark, data) {\n  function Measure(data) {\n    var elapsed = 0;\n    var start = new Date();\n    for (var n = 0; elapsed < 1000; n++) {\n      benchmark.run();\n      elapsed = new Date() - start;\n    }\n    if (data != null) {\n      data.runs += n;\n      data.elapsed += elapsed;\n    }\n  }\n\n  if (data == null) {\n    // Measure the benchmark once for warm up and throw the result\n    // away. Return a fresh data object.\n    Measure(null);\n    return {runs: 0, elapsed: 0};\n  } else {\n    Measure(data);\n    // If we've run too few iterations, we continue for another second.\n    if (data.runs < 32) return data;\n    var usec = (data.elapsed * 1000) / data.runs;\n    this.NotifyStep(new BenchmarkResult(benchmark, usec));\n    return null;\n  }\n};\n\n\n// This function starts running a suite, but stops between each\n// individual benchmark in the suite and returns a continuation\n// function which can be invoked to run the next benchmark. Once the\n// last benchmark has been executed, null is returned.\nBenchmarkSuite.prototype.RunStep = function (runner) {\n  this.results = [];\n  this.runner = runner;\n  var length = this.benchmarks.length;\n  var index = 0;\n  var suite = this;\n  var data;\n\n  // Run the setup, the actual benchmark, and the tear down in three\n  // separate steps to allow the framework to yield between any of the\n  // steps.\n\n  function RunNextSetup() {\n    if (index < length) {\n      try {\n        suite.benchmarks[index].Setup();\n      } catch (e) {\n        suite.NotifyError(e);\n        return null;\n      }\n      return RunNextBenchmark;\n    }\n    suite.NotifyResult();\n    return null;\n  }\n\n  function RunNextBenchmark() {\n    try {\n      data = suite.RunSingleBenchmark(suite.benchmarks[index], data);\n    } catch (e) {\n      suite.NotifyError(e);\n      return null;\n    }\n    // If data is null, we're done with this benchmark.\n    return (data == null) ? RunNextTearDown : RunNextBenchmark();\n  }\n\n  function RunNextTearDown() {\n    try {\n      suite.benchmarks[index++].TearDown();\n    } catch (e) {\n      suite.NotifyError(e);\n      return null;\n    }\n    return RunNextSetup;\n  }\n\n  // Start out running the setup.\n  return RunNextSetup();\n};\n\n// Copyright 2008 the V8 project authors. All rights reserved.\n// Copyright 1996 John Maloney and Mario Wolczko.\n\n// This program is free software; you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation; either version 2 of the License, or\n// (at your option) any later version.\n//\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with this program; if not, write to the Free Software\n// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA\n\n\n// This implementation of the DeltaBlue benchmark is derived\n// from the Smalltalk implementation by John Maloney and Mario\n// Wolczko. Some parts have been translated directly, whereas\n// others have been modified more aggresively to make it feel\n// more like a JavaScript program.\n\n\n/**\n * A JavaScript implementation of the DeltaBlue constraint-solving\n * algorithm, as described in:\n *\n * \"The DeltaBlue Algorithm: An Incremental Constraint Hierarchy Solver\"\n *   Bjorn N. Freeman-Benson and John Maloney\n *   January 1990 Communications of the ACM,\n *   also available as University of Washington TR 89-08-06.\n *\n * Beware: this benchmark is written in a grotesque style where\n * the constraint model is built by side-effects from constructors.\n * I've kept it this way to avoid deviating too much from the original\n * implementation.\n */\n\n\n/* --- O b j e c t   M o d e l --- */\n\nObject.prototype.inheritsFrom = function (shuper) {\n  function Inheriter() {\n  }\n\n  Inheriter.prototype = shuper.prototype;\n  this.prototype = new Inheriter();\n  this.superConstructor = shuper;\n};\n\nfunction OrderedCollection() {\n  this.elms = new Array();\n}\n\nOrderedCollection.prototype.add = function (elm) {\n  this.elms.push(elm);\n};\n\nOrderedCollection.prototype.at = function (index) {\n  return this.elms[index];\n};\n\nOrderedCollection.prototype.size = function () {\n  return this.elms.length;\n};\n\nOrderedCollection.prototype.removeFirst = function () {\n  return this.elms.pop();\n};\n\nOrderedCollection.prototype.remove = function (elm) {\n  var index = 0, skipped = 0;\n  for (var i = 0; i < this.elms.length; i++) {\n    var value = this.elms[i];\n    if (value != elm) {\n      this.elms[index] = value;\n      index++;\n    } else {\n      skipped++;\n    }\n  }\n  for (var i = 0; i < skipped; i++)\n    this.elms.pop();\n};\n\n/* --- *\n * S t r e n g t h\n * --- */\n\n/**\n * Strengths are used to measure the relative importance of constraints.\n * New strengths may be inserted in the strength hierarchy without\n * disrupting current constraints.  Strengths cannot be created outside\n * this class, so pointer comparison can be used for value comparison.\n */\nfunction Strength(strengthValue, name) {\n  this.strengthValue = strengthValue;\n  this.name = name;\n}\n\nStrength.stronger = function (s1, s2) {\n  return s1.strengthValue < s2.strengthValue;\n};\n\nStrength.weaker = function (s1, s2) {\n  return s1.strengthValue > s2.strengthValue;\n};\n\nStrength.weakestOf = function (s1, s2) {\n  return this.weaker(s1, s2) ? s1 : s2;\n};\n\nStrength.strongest = function (s1, s2) {\n  return this.stronger(s1, s2) ? s1 : s2;\n};\n\nStrength.prototype.nextWeaker = function () {\n  switch (this.strengthValue) {\n    case 0:\n      return Strength.STRONG_PREFERRED;\n    case 1:\n      return Strength.PREFERRED;\n    case 2:\n      return Strength.STRONG_DEFAULT;\n    case 3:\n      return Strength.NORMAL;\n    case 4:\n      return Strength.WEAK_DEFAULT;\n    case 5:\n      return Strength.WEAKEST;\n  }\n};\n\n// Strength constants.\nStrength.REQUIRED = new Strength(0, \"required\");\nStrength.STRONG_PREFERRED = new Strength(1, \"strongPreferred\");\nStrength.PREFERRED = new Strength(2, \"preferred\");\nStrength.STRONG_DEFAULT = new Strength(3, \"strongDefault\");\nStrength.NORMAL = new Strength(4, \"normal\");\nStrength.WEAK_DEFAULT = new Strength(5, \"weakDefault\");\nStrength.WEAKEST = new Strength(6, \"weakest\");\n\n/* --- *\n * C o n s t r a i n t\n * --- */\n\n/**\n * An abstract class representing a system-maintainable relationship\n * (or \"constraint\") between a set of variables. A constraint supplies\n * a strength instance variable; concrete subclasses provide a means\n * of storing the constrained variables and other information required\n * to represent a constraint.\n */\nfunction Constraint(strength) {\n  this.strength = strength;\n}\n\n/**\n * Activate this constraint and attempt to satisfy it.\n */\nConstraint.prototype.addConstraint = function () {\n  this.addToGraph();\n  planner.incrementalAdd(this);\n};\n\n/**\n * Attempt to find a way to enforce this constraint. If successful,\n * record the solution, perhaps modifying the current dataflow\n * graph. Answer the constraint that this constraint overrides, if\n * there is one, or nil, if there isn't.\n * Assume: I am not already satisfied.\n */\nConstraint.prototype.satisfy = function (mark) {\n  this.chooseMethod(mark);\n  if (!this.isSatisfied()) {\n    if (this.strength == Strength.REQUIRED)\n      alert(\"Could not satisfy a required constraint!\");\n    return null;\n  }\n  this.markInputs(mark);\n  var out = this.output();\n  var overridden = out.determinedBy;\n  if (overridden != null) overridden.markUnsatisfied();\n  out.determinedBy = this;\n  if (!planner.addPropagate(this, mark))\n    alert(\"Cycle encountered\");\n  out.mark = mark;\n  return overridden;\n};\n\nConstraint.prototype.destroyConstraint = function () {\n  if (this.isSatisfied()) planner.incrementalRemove(this);\n  else this.removeFromGraph();\n};\n\n/**\n * Normal constraints are not input constraints.  An input constraint\n * is one that depends on external state, such as the mouse, the\n * keybord, a clock, or some arbitraty piece of imperative code.\n */\nConstraint.prototype.isInput = function () {\n  return false;\n};\n\n/* --- *\n * U n a r y   C o n s t r a i n t\n * --- */\n\n/**\n * Abstract superclass for constraints having a single possible output\n * variable.\n */\nfunction UnaryConstraint(v, strength) {\n  UnaryConstraint.superConstructor.call(this, strength);\n  this.myOutput = v;\n  this.satisfied = false;\n  this.addConstraint();\n}\n\nUnaryConstraint.inheritsFrom(Constraint);\n\n/**\n * Adds this constraint to the constraint graph\n */\nUnaryConstraint.prototype.addToGraph = function () {\n  this.myOutput.addConstraint(this);\n  this.satisfied = false;\n};\n\n/**\n * Decides if this constraint can be satisfied and records that\n * decision.\n */\nUnaryConstraint.prototype.chooseMethod = function (mark) {\n  this.satisfied = (this.myOutput.mark != mark)\n    && Strength.stronger(this.strength, this.myOutput.walkStrength);\n};\n\n/**\n * Returns true if this constraint is satisfied in the current solution.\n */\nUnaryConstraint.prototype.isSatisfied = function () {\n  return this.satisfied;\n};\n\nUnaryConstraint.prototype.markInputs = function (mark) {\n  // has no inputs\n};\n\n/**\n * Returns the current output variable.\n */\nUnaryConstraint.prototype.output = function () {\n  return this.myOutput;\n};\n\n/**\n * Calculate the walkabout strength, the stay flag, and, if it is\n * 'stay', the value for the current output of this constraint. Assume\n * this constraint is satisfied.\n */\nUnaryConstraint.prototype.recalculate = function () {\n  this.myOutput.walkStrength = this.strength;\n  this.myOutput.stay = !this.isInput();\n  if (this.myOutput.stay) this.execute(); // Stay optimization\n};\n\n/**\n * Records that this constraint is unsatisfied\n */\nUnaryConstraint.prototype.markUnsatisfied = function () {\n  this.satisfied = false;\n};\n\nUnaryConstraint.prototype.inputsKnown = function () {\n  return true;\n};\n\nUnaryConstraint.prototype.removeFromGraph = function () {\n  if (this.myOutput != null) this.myOutput.removeConstraint(this);\n  this.satisfied = false;\n};\n\n/* --- *\n * S t a y   C o n s t r a i n t\n * --- */\n\n/**\n * Variables that should, with some level of preference, stay the same.\n * Planners may exploit the fact that instances, if satisfied, will not\n * change their output during plan execution.  This is called \"stay\n * optimization\".\n */\nfunction StayConstraint(v, str) {\n  StayConstraint.superConstructor.call(this, v, str);\n}\n\nStayConstraint.inheritsFrom(UnaryConstraint);\n\nStayConstraint.prototype.execute = function () {\n  // Stay constraints do nothing\n};\n\n/* --- *\n * E d i t   C o n s t r a i n t\n * --- */\n\n/**\n * A unary input constraint used to mark a variable that the client\n * wishes to change.\n */\nfunction EditConstraint(v, str) {\n  EditConstraint.superConstructor.call(this, v, str);\n}\n\nEditConstraint.inheritsFrom(UnaryConstraint);\n\n/**\n * Edits indicate that a variable is to be changed by imperative code.\n */\nEditConstraint.prototype.isInput = function () {\n  return true;\n};\n\nEditConstraint.prototype.execute = function () {\n  // Edit constraints do nothing\n};\n\n/* --- *\n * B i n a r y   C o n s t r a i n t\n * --- */\n\nvar Direction = new Object();\nDirection.NONE = 0;\nDirection.FORWARD = 1;\nDirection.BACKWARD = -1;\n\n/**\n * Abstract superclass for constraints having two possible output\n * variables.\n */\nfunction BinaryConstraint(var1, var2, strength) {\n  BinaryConstraint.superConstructor.call(this, strength);\n  this.v1 = var1;\n  this.v2 = var2;\n  this.direction = Direction.NONE;\n  this.addConstraint();\n}\n\nBinaryConstraint.inheritsFrom(Constraint);\n\n/**\n * Decides if this constraint can be satisfied and which way it\n * should flow based on the relative strength of the variables related,\n * and record that decision.\n */\nBinaryConstraint.prototype.chooseMethod = function (mark) {\n  if (this.v1.mark == mark) {\n    this.direction = (this.v2.mark != mark && Strength.stronger(this.strength, this.v2.walkStrength))\n      ? Direction.FORWARD\n      : Direction.NONE;\n  }\n  if (this.v2.mark == mark) {\n    this.direction = (this.v1.mark != mark && Strength.stronger(this.strength, this.v1.walkStrength))\n      ? Direction.BACKWARD\n      : Direction.NONE;\n  }\n  if (Strength.weaker(this.v1.walkStrength, this.v2.walkStrength)) {\n    this.direction = Strength.stronger(this.strength, this.v1.walkStrength)\n      ? Direction.BACKWARD\n      : Direction.NONE;\n  } else {\n    this.direction = Strength.stronger(this.strength, this.v2.walkStrength)\n      ? Direction.FORWARD\n      : Direction.BACKWARD\n  }\n};\n\n/**\n * Add this constraint to the constraint graph\n */\nBinaryConstraint.prototype.addToGraph = function () {\n  this.v1.addConstraint(this);\n  this.v2.addConstraint(this);\n  this.direction = Direction.NONE;\n};\n\n/**\n * Answer true if this constraint is satisfied in the current solution.\n */\nBinaryConstraint.prototype.isSatisfied = function () {\n  return this.direction != Direction.NONE;\n};\n\n/**\n * Mark the input variable with the given mark.\n */\nBinaryConstraint.prototype.markInputs = function (mark) {\n  this.input().mark = mark;\n};\n\n/**\n * Returns the current input variable\n */\nBinaryConstraint.prototype.input = function () {\n  return (this.direction == Direction.FORWARD) ? this.v1 : this.v2;\n};\n\n/**\n * Returns the current output variable\n */\nBinaryConstraint.prototype.output = function () {\n  return (this.direction == Direction.FORWARD) ? this.v2 : this.v1;\n};\n\n/**\n * Calculate the walkabout strength, the stay flag, and, if it is\n * 'stay', the value for the current output of this\n * constraint. Assume this constraint is satisfied.\n */\nBinaryConstraint.prototype.recalculate = function () {\n  var ihn = this.input(), out = this.output();\n  out.walkStrength = Strength.weakestOf(this.strength, ihn.walkStrength);\n  out.stay = ihn.stay;\n  if (out.stay) this.execute();\n};\n\n/**\n * Record the fact that this constraint is unsatisfied.\n */\nBinaryConstraint.prototype.markUnsatisfied = function () {\n  this.direction = Direction.NONE;\n};\n\nBinaryConstraint.prototype.inputsKnown = function (mark) {\n  var i = this.input();\n  return i.mark == mark || i.stay || i.determinedBy == null;\n};\n\nBinaryConstraint.prototype.removeFromGraph = function () {\n  if (this.v1 != null) this.v1.removeConstraint(this);\n  if (this.v2 != null) this.v2.removeConstraint(this);\n  this.direction = Direction.NONE;\n};\n\n/* --- *\n * S c a l e   C o n s t r a i n t\n * --- */\n\n/**\n * Relates two variables by the linear scaling relationship: \"v2 =\n * (v1 * scale) + offset\". Either v1 or v2 may be changed to maintain\n * this relationship but the scale factor and offset are considered\n * read-only.\n */\nfunction ScaleConstraint(src, scale, offset, dest, strength) {\n  this.direction = Direction.NONE;\n  this.scale = scale;\n  this.offset = offset;\n  ScaleConstraint.superConstructor.call(this, src, dest, strength);\n}\n\nScaleConstraint.inheritsFrom(BinaryConstraint);\n\n/**\n * Adds this constraint to the constraint graph.\n */\nScaleConstraint.prototype.addToGraph = function () {\n  ScaleConstraint.superConstructor.prototype.addToGraph.call(this);\n  this.scale.addConstraint(this);\n  this.offset.addConstraint(this);\n};\n\nScaleConstraint.prototype.removeFromGraph = function () {\n  ScaleConstraint.superConstructor.prototype.removeFromGraph.call(this);\n  if (this.scale != null) this.scale.removeConstraint(this);\n  if (this.offset != null) this.offset.removeConstraint(this);\n};\n\nScaleConstraint.prototype.markInputs = function (mark) {\n  ScaleConstraint.superConstructor.prototype.markInputs.call(this, mark);\n  this.scale.mark = this.offset.mark = mark;\n};\n\n/**\n * Enforce this constraint. Assume that it is satisfied.\n */\nScaleConstraint.prototype.execute = function () {\n  if (this.direction == Direction.FORWARD) {\n    this.v2.value = this.v1.value * this.scale.value + this.offset.value;\n  } else {\n    this.v1.value = (this.v2.value - this.offset.value) / this.scale.value;\n  }\n}\n\n/**\n * Calculate the walkabout strength, the stay flag, and, if it is\n * 'stay', the value for the current output of this constraint. Assume\n * this constraint is satisfied.\n */\nScaleConstraint.prototype.recalculate = function () {\n  var ihn = this.input(), out = this.output();\n  out.walkStrength = Strength.weakestOf(this.strength, ihn.walkStrength);\n  out.stay = ihn.stay && this.scale.stay && this.offset.stay;\n  if (out.stay) this.execute();\n}\n\n/* --- *\n * E q u a l i t  y   C o n s t r a i n t\n * --- */\n\n/**\n * Constrains two variables to have the same value.\n */\nfunction EqualityConstraint(var1, var2, strength) {\n  EqualityConstraint.superConstructor.call(this, var1, var2, strength);\n}\n\nEqualityConstraint.inheritsFrom(BinaryConstraint);\n\n/**\n * Enforce this constraint. Assume that it is satisfied.\n */\nEqualityConstraint.prototype.execute = function () {\n  this.output().value = this.input().value;\n}\n\n/* --- *\n * V a r i a b l e\n * --- */\n\n/**\n * A constrained variable. In addition to its value, it maintain the\n * structure of the constraint graph, the current dataflow graph, and\n * various parameters of interest to the DeltaBlue incremental\n * constraint solver.\n **/\nfunction Variable(name, initialValue) {\n  this.value = initialValue || 0;\n  this.constraints = new OrderedCollection();\n  this.determinedBy = null;\n  this.mark = 0;\n  this.walkStrength = Strength.WEAKEST;\n  this.stay = true;\n  this.name = name;\n}\n\n/**\n * Add the given constraint to the set of all constraints that refer\n * this variable.\n */\nVariable.prototype.addConstraint = function (c) {\n  this.constraints.add(c);\n}\n\n/**\n * Removes all traces of c from this variable.\n */\nVariable.prototype.removeConstraint = function (c) {\n  this.constraints.remove(c);\n  if (this.determinedBy == c) this.determinedBy = null;\n}\n\n/* --- *\n * P l a n n e r\n * --- */\n\n/**\n * The DeltaBlue planner\n */\nfunction Planner() {\n  this.currentMark = 0;\n}\n\n/**\n * Attempt to satisfy the given constraint and, if successful,\n * incrementally update the dataflow graph.  Details: If satifying\n * the constraint is successful, it may override a weaker constraint\n * on its output. The algorithm attempts to resatisfy that\n * constraint using some other method. This process is repeated\n * until either a) it reaches a variable that was not previously\n * determined by any constraint or b) it reaches a constraint that\n * is too weak to be satisfied using any of its methods. The\n * variables of constraints that have been processed are marked with\n * a unique mark value so that we know where we've been. This allows\n * the algorithm to avoid getting into an infinite loop even if the\n * constraint graph has an inadvertent cycle.\n */\nPlanner.prototype.incrementalAdd = function (c) {\n  var mark = this.newMark();\n  var overridden = c.satisfy(mark);\n  while (overridden != null)\n    overridden = overridden.satisfy(mark);\n}\n\n/**\n * Entry point for retracting a constraint. Remove the given\n * constraint and incrementally update the dataflow graph.\n * Details: Retracting the given constraint may allow some currently\n * unsatisfiable downstream constraint to be satisfied. We therefore collect\n * a list of unsatisfied downstream constraints and attempt to\n * satisfy each one in turn. This list is traversed by constraint\n * strength, strongest first, as a heuristic for avoiding\n * unnecessarily adding and then overriding weak constraints.\n * Assume: c is satisfied.\n */\nPlanner.prototype.incrementalRemove = function (c) {\n  var out = c.output();\n  c.markUnsatisfied();\n  c.removeFromGraph();\n  var unsatisfied = this.removePropagateFrom(out);\n  var strength = Strength.REQUIRED;\n  do {\n    for (var i = 0; i < unsatisfied.size(); i++) {\n      var u = unsatisfied.at(i);\n      if (u.strength == strength)\n        this.incrementalAdd(u);\n    }\n    strength = strength.nextWeaker();\n  } while (strength != Strength.WEAKEST);\n}\n\n/**\n * Select a previously unused mark value.\n */\nPlanner.prototype.newMark = function () {\n  return ++this.currentMark;\n}\n\n/**\n * Extract a plan for resatisfaction starting from the given source\n * constraints, usually a set of input constraints. This method\n * assumes that stay optimization is desired; the plan will contain\n * only constraints whose output variables are not stay. Constraints\n * that do no computation, such as stay and edit constraints, are\n * not included in the plan.\n * Details: The outputs of a constraint are marked when it is added\n * to the plan under construction. A constraint may be appended to\n * the plan when all its input variables are known. A variable is\n * known if either a) the variable is marked (indicating that has\n * been computed by a constraint appearing earlier in the plan), b)\n * the variable is 'stay' (i.e. it is a constant at plan execution\n * time), or c) the variable is not determined by any\n * constraint. The last provision is for past states of history\n * variables, which are not stay but which are also not computed by\n * any constraint.\n * Assume: sources are all satisfied.\n */\nPlanner.prototype.makePlan = function (sources) {\n  var mark = this.newMark();\n  var plan = new Plan();\n  var todo = sources;\n  while (todo.size() > 0) {\n    var c = todo.removeFirst();\n    if (c.output().mark != mark && c.inputsKnown(mark)) {\n      plan.addConstraint(c);\n      c.output().mark = mark;\n      this.addConstraintsConsumingTo(c.output(), todo);\n    }\n  }\n  return plan;\n}\n\n/**\n * Extract a plan for resatisfying starting from the output of the\n * given constraints, usually a set of input constraints.\n */\nPlanner.prototype.extractPlanFromConstraints = function (constraints) {\n  var sources = new OrderedCollection();\n  for (var i = 0; i < constraints.size(); i++) {\n    var c = constraints.at(i);\n    if (c.isInput() && c.isSatisfied())\n      // not in plan already and eligible for inclusion\n      sources.add(c);\n  }\n  return this.makePlan(sources);\n}\n\n/**\n * Recompute the walkabout strengths and stay flags of all variables\n * downstream of the given constraint and recompute the actual\n * values of all variables whose stay flag is true. If a cycle is\n * detected, remove the given constraint and answer\n * false. Otherwise, answer true.\n * Details: Cycles are detected when a marked variable is\n * encountered downstream of the given constraint. The sender is\n * assumed to have marked the inputs of the given constraint with\n * the given mark. Thus, encountering a marked node downstream of\n * the output constraint means that there is a path from the\n * constraint's output to one of its inputs.\n */\nPlanner.prototype.addPropagate = function (c, mark) {\n  var todo = new OrderedCollection();\n  todo.add(c);\n  while (todo.size() > 0) {\n    var d = todo.removeFirst();\n    if (d.output().mark == mark) {\n      this.incrementalRemove(c);\n      return false;\n    }\n    d.recalculate();\n    this.addConstraintsConsumingTo(d.output(), todo);\n  }\n  return true;\n}\n\n\n/**\n * Update the walkabout strengths and stay flags of all variables\n * downstream of the given constraint. Answer a collection of\n * unsatisfied constraints sorted in order of decreasing strength.\n */\nPlanner.prototype.removePropagateFrom = function (out) {\n  out.determinedBy = null;\n  out.walkStrength = Strength.WEAKEST;\n  out.stay = true;\n  var unsatisfied = new OrderedCollection();\n  var todo = new OrderedCollection();\n  todo.add(out);\n  while (todo.size() > 0) {\n    var v = todo.removeFirst();\n    for (var i = 0; i < v.constraints.size(); i++) {\n      var c = v.constraints.at(i);\n      if (!c.isSatisfied())\n        unsatisfied.add(c);\n    }\n    var determining = v.determinedBy;\n    for (var i = 0; i < v.constraints.size(); i++) {\n      var next = v.constraints.at(i);\n      if (next != determining && next.isSatisfied()) {\n        next.recalculate();\n        todo.add(next.output());\n      }\n    }\n  }\n  return unsatisfied;\n}\n\nPlanner.prototype.addConstraintsConsumingTo = function (v, coll) {\n  var determining = v.determinedBy;\n  var cc = v.constraints;\n  for (var i = 0; i < cc.size(); i++) {\n    var c = cc.at(i);\n    if (c != determining && c.isSatisfied())\n      coll.add(c);\n  }\n}\n\n/* --- *\n * P l a n\n * --- */\n\n/**\n * A Plan is an ordered list of constraints to be executed in sequence\n * to resatisfy all currently satisfiable constraints in the face of\n * one or more changing inputs.\n */\nfunction Plan() {\n  this.v = new OrderedCollection();\n}\n\nPlan.prototype.addConstraint = function (c) {\n  this.v.add(c);\n}\n\nPlan.prototype.size = function () {\n  return this.v.size();\n}\n\nPlan.prototype.constraintAt = function (index) {\n  return this.v.at(index);\n}\n\nPlan.prototype.execute = function () {\n  for (var i = 0; i < this.size(); i++) {\n    var c = this.constraintAt(i);\n    c.execute();\n  }\n}\n\n/* --- *\n * M a i n\n * --- */\n\n/**\n * This is the standard DeltaBlue benchmark. A long chain of equality\n * constraints is constructed with a stay constraint on one end. An\n * edit constraint is then added to the opposite end and the time is\n * measured for adding and removing this constraint, and extracting\n * and executing a constraint satisfaction plan. There are two cases.\n * In case 1, the added constraint is stronger than the stay\n * constraint and values must propagate down the entire length of the\n * chain. In case 2, the added constraint is weaker than the stay\n * constraint so it cannot be accomodated. The cost in this case is,\n * of course, very low. Typical situations lie somewhere between these\n * two extremes.\n */\nfunction chainTest(n) {\n  planner = new Planner();\n  var prev = null, first = null, last = null;\n\n  // Build chain of n equality constraints\n  for (var i = 0; i <= n; i++) {\n    var name = \"v\" + i;\n    var v = new Variable(name);\n    if (prev != null)\n      new EqualityConstraint(prev, v, Strength.REQUIRED);\n    if (i == 0) first = v;\n    if (i == n) last = v;\n    prev = v;\n  }\n\n  new StayConstraint(last, Strength.STRONG_DEFAULT);\n  var edit = new EditConstraint(first, Strength.PREFERRED);\n  var edits = new OrderedCollection();\n  edits.add(edit);\n  var plan = planner.extractPlanFromConstraints(edits);\n  for (var i = 0; i < 100; i++) {\n    first.value = i;\n    plan.execute();\n    if (last.value != i)\n      alert(\"Chain test failed.\");\n  }\n}\n\n/**\n * This test constructs a two sets of variables related to each\n * other by a simple linear transformation (scale and offset). The\n * time is measured to change a variable on either side of the\n * mapping and to change the scale and offset factors.\n */\nfunction projectionTest(n) {\n  planner = new Planner();\n  var scale = new Variable(\"scale\", 10);\n  var offset = new Variable(\"offset\", 1000);\n  var src = null, dst = null;\n\n  var dests = new OrderedCollection();\n  for (var i = 0; i < n; i++) {\n    src = new Variable(\"src\" + i, i);\n    dst = new Variable(\"dst\" + i, i);\n    dests.add(dst);\n    new StayConstraint(src, Strength.NORMAL);\n    new ScaleConstraint(src, scale, offset, dst, Strength.REQUIRED);\n  }\n\n  change(src, 17);\n  if (dst.value != 1170) alert(\"Projection 1 failed\");\n  change(dst, 1050);\n  if (src.value != 5) alert(\"Projection 2 failed\");\n  change(scale, 5);\n  for (var i = 0; i < n - 1; i++) {\n    if (dests.at(i).value != i * 5 + 1000)\n      alert(\"Projection 3 failed\");\n  }\n  change(offset, 2000);\n  for (var i = 0; i < n - 1; i++) {\n    if (dests.at(i).value != i * 5 + 2000)\n      alert(\"Projection 4 failed\");\n  }\n}\n\nfunction change(v, newValue) {\n  var edit = new EditConstraint(v, Strength.PREFERRED);\n  var edits = new OrderedCollection();\n  edits.add(edit);\n  var plan = planner.extractPlanFromConstraints(edits);\n  for (var i = 0; i < 10; i++) {\n    v.value = newValue;\n    plan.execute();\n  }\n  edit.destroyConstraint();\n}\n\n// Global variable holding the current planner.\nvar planner = null;\n\nfunction deltaBlue() {\n  chainTest(100);\n  projectionTest(100);\n}\n\nvar DeltaBlue = new BenchmarkSuite('DeltaBlue', 66118, [\n  new Benchmark('DeltaBlue', deltaBlue)\n]);\n\n/* run_harness.js */\nvar print = console.log;\n\nfunction Run() {\n  BenchmarkSuite.RunSuites({\n    NotifyStep: ShowProgress,\n    NotifyError: AddError,\n    NotifyResult: AddResult,\n    NotifyScore: AddScore,\n  });\n}\n\nvar harnessErrorCount = 0;\n\nfunction ShowProgress(name) {\n  print(\"PROGRESS\", name);\n}\n\nfunction AddError(name, error) {\n  print(\"ERROR\", name, error);\n  print(error.stack);\n  harnessErrorCount++;\n}\n\nfunction AddResult(name, result) {\n  print(\"RESULT\", name, result);\n}\n\nfunction AddScore(score) {\n  print(\"SCORE\", score);\n}\n\nfunction main() {\n  Run();\n}\n"
  },
  {
    "path": "benches/scripts/v8-benches/earley-boyer.js",
    "content": "\"use strict\";\n\"use strip\";\n// Copyright 2012 the V8 project authors. All rights reserved.\n// Redistribution and use in source and binary forms, with or without\n// modification, are permitted provided that the following conditions are\n// met:\n//\n//     * Redistributions of source code must retain the above copyright\n//       notice, this list of conditions and the following disclaimer.\n//     * Redistributions in binary form must reproduce the above\n//       copyright notice, this list of conditions and the following\n//       disclaimer in the documentation and/or other materials provided\n//       with the distribution.\n//     * Neither the name of Google Inc. nor the names of its\n//       contributors may be used to endorse or promote products derived\n//       from this software without specific prior written permission.\n//\n// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS\n// \"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT\n// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR\n// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT\n// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,\n// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT\n// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,\n// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY\n// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n\n// Simple framework for running the benchmark suites and\n// computing a score based on the timing measurements.\n\n\n// A benchmark has a name (string) and a function that will be run to\n// do the performance measurement. The optional setup and tearDown\n// arguments are functions that will be invoked before and after\n// running the benchmark, but the running time of these functions will\n// not be accounted for in the benchmark score.\nfunction Benchmark(name, run, setup, tearDown) {\n  this.name = name;\n  this.run = run;\n  this.Setup = setup ? setup : function () {\n  };\n  this.TearDown = tearDown ? tearDown : function () {\n  };\n}\n\n\n// Benchmark results hold the benchmark and the measured time used to\n// run the benchmark. The benchmark score is computed later once a\n// full benchmark suite has run to completion.\nfunction BenchmarkResult(benchmark, time) {\n  this.benchmark = benchmark;\n  this.time = time;\n}\n\n\n// Automatically convert results to numbers. Used by the geometric\n// mean computation.\nBenchmarkResult.prototype.valueOf = function () {\n  return this.time;\n};\n\n\n// Suites of benchmarks consist of a name and the set of benchmarks in\n// addition to the reference timing that the final score will be based\n// on. This way, all scores are relative to a reference run and higher\n// scores implies better performance.\nfunction BenchmarkSuite(name, reference, benchmarks) {\n  this.name = name;\n  this.reference = reference;\n  this.benchmarks = benchmarks;\n  BenchmarkSuite.suites.push(this);\n}\n\n\n// Keep track of all declared benchmark suites.\nBenchmarkSuite.suites = [];\n\n\n// Scores are not comparable across versions. Bump the version if\n// you're making changes that will affect that scores, e.g. if you add\n// a new benchmark or change an existing one.\nBenchmarkSuite.version = '7';\n\n\n// To make the benchmark results predictable, we replace Math.random\n// with a 100% deterministic alternative.\nMath.random = (function () {\n  var seed = 49734321;\n  return function () {\n    // Robert Jenkins' 32 bit integer hash function.\n    seed = ((seed + 0x7ed55d16) + (seed << 12)) & 0xffffffff;\n    seed = ((seed ^ 0xc761c23c) ^ (seed >>> 19)) & 0xffffffff;\n    seed = ((seed + 0x165667b1) + (seed << 5)) & 0xffffffff;\n    seed = ((seed + 0xd3a2646c) ^ (seed << 9)) & 0xffffffff;\n    seed = ((seed + 0xfd7046c5) + (seed << 3)) & 0xffffffff;\n    seed = ((seed ^ 0xb55a4f09) ^ (seed >>> 16)) & 0xffffffff;\n    return (seed & 0xfffffff) / 0x10000000;\n  };\n})();\n\n\n// Runs all registered benchmark suites and optionally yields between\n// each individual benchmark to avoid running for too long in the\n// context of browsers. Once done, the final score is reported to the\n// runner.\nBenchmarkSuite.RunSuites = function (runner) {\n  var continuation = null;\n  var suites = BenchmarkSuite.suites;\n  var length = suites.length;\n  BenchmarkSuite.scores = [];\n  var index = 0;\n\n  function RunStep() {\n    while (continuation || index < length) {\n      if (continuation) {\n        continuation = continuation();\n      } else {\n        var suite = suites[index++];\n        if (runner.NotifyStart) runner.NotifyStart(suite.name);\n        continuation = suite.RunStep(runner);\n      }\n      if (continuation && typeof window != 'undefined' && window.setTimeout) {\n        window.setTimeout(RunStep, 25);\n        return;\n      }\n    }\n    if (runner.NotifyScore) {\n      var score = BenchmarkSuite.GeometricMean(BenchmarkSuite.scores);\n      var formatted = BenchmarkSuite.FormatScore(100 * score);\n      runner.NotifyScore(formatted);\n    }\n  }\n\n  RunStep();\n};\n\n\n// Counts the total number of registered benchmarks. Useful for\n// showing progress as a percentage.\nBenchmarkSuite.CountBenchmarks = function () {\n  var result = 0;\n  var suites = BenchmarkSuite.suites;\n  for (var i = 0; i < suites.length; i++) {\n    result += suites[i].benchmarks.length;\n  }\n  return result;\n};\n\n\n// Computes the geometric mean of a set of numbers.\nBenchmarkSuite.GeometricMean = function (numbers) {\n  var log = 0;\n  for (var i = 0; i < numbers.length; i++) {\n    log += Math.log(numbers[i]);\n  }\n  return Math.pow(Math.E, log / numbers.length);\n};\n\n\n// Converts a score value to a string with at least three significant\n// digits.\nBenchmarkSuite.FormatScore = function (value) {\n  if (value > 100) {\n    return value.toFixed(0);\n  } else {\n    return value.toPrecision(3);\n  }\n};\n\n// Notifies the runner that we're done running a single benchmark in\n// the benchmark suite. This can be useful to report progress.\nBenchmarkSuite.prototype.NotifyStep = function (result) {\n  this.results.push(result);\n  if (this.runner.NotifyStep) this.runner.NotifyStep(result.benchmark.name);\n};\n\n\n// Notifies the runner that we're done with running a suite and that\n// we have a result which can be reported to the user if needed.\nBenchmarkSuite.prototype.NotifyResult = function () {\n  var mean = BenchmarkSuite.GeometricMean(this.results);\n  var score = this.reference / mean;\n  BenchmarkSuite.scores.push(score);\n  if (this.runner.NotifyResult) {\n    var formatted = BenchmarkSuite.FormatScore(100 * score);\n    this.runner.NotifyResult(this.name, formatted);\n  }\n};\n\n\n// Notifies the runner that running a benchmark resulted in an error.\nBenchmarkSuite.prototype.NotifyError = function (error) {\n  if (this.runner.NotifyError) {\n    this.runner.NotifyError(this.name, error);\n  }\n  if (this.runner.NotifyStep) {\n    this.runner.NotifyStep(this.name);\n  }\n};\n\n\n// Runs a single benchmark for at least a second and computes the\n// average time it takes to run a single iteration.\nBenchmarkSuite.prototype.RunSingleBenchmark = function (benchmark, data) {\n  function Measure(data) {\n    var elapsed = 0;\n    var start = new Date();\n    for (var n = 0; elapsed < 1000; n++) {\n      benchmark.run();\n      elapsed = new Date() - start;\n    }\n    if (data != null) {\n      data.runs += n;\n      data.elapsed += elapsed;\n    }\n  }\n\n  if (data == null) {\n    // Measure the benchmark once for warm up and throw the result\n    // away. Return a fresh data object.\n    Measure(null);\n    return {runs: 0, elapsed: 0};\n  } else {\n    Measure(data);\n    // If we've run too few iterations, we continue for another second.\n    if (data.runs < 32) return data;\n    var usec = (data.elapsed * 1000) / data.runs;\n    this.NotifyStep(new BenchmarkResult(benchmark, usec));\n    return null;\n  }\n};\n\n\n// This function starts running a suite, but stops between each\n// individual benchmark in the suite and returns a continuation\n// function which can be invoked to run the next benchmark. Once the\n// last benchmark has been executed, null is returned.\nBenchmarkSuite.prototype.RunStep = function (runner) {\n  this.results = [];\n  this.runner = runner;\n  var length = this.benchmarks.length;\n  var index = 0;\n  var suite = this;\n  var data;\n\n  // Run the setup, the actual benchmark, and the tear down in three\n  // separate steps to allow the framework to yield between any of the\n  // steps.\n\n  function RunNextSetup() {\n    if (index < length) {\n      try {\n        suite.benchmarks[index].Setup();\n      } catch (e) {\n        suite.NotifyError(e);\n        return null;\n      }\n      return RunNextBenchmark;\n    }\n    suite.NotifyResult();\n    return null;\n  }\n\n  function RunNextBenchmark() {\n    try {\n      data = suite.RunSingleBenchmark(suite.benchmarks[index], data);\n    } catch (e) {\n      suite.NotifyError(e);\n      return null;\n    }\n    // If data is null, we're done with this benchmark.\n    return (data == null) ? RunNextTearDown : RunNextBenchmark();\n  }\n\n  function RunNextTearDown() {\n    try {\n      suite.benchmarks[index++].TearDown();\n    } catch (e) {\n      suite.NotifyError(e);\n      return null;\n    }\n    return RunNextSetup;\n  }\n\n  // Start out running the setup.\n  return RunNextSetup();\n};\n// This file is automatically generated by scheme2js, except for the\n// benchmark harness code at the beginning and end of the file.\n\n/************* GENERATED FILE - DO NOT EDIT *************/\n/************* GENERATED FILE - DO NOT EDIT *************/\n/************* GENERATED FILE - DO NOT EDIT *************/\n/************* GENERATED FILE - DO NOT EDIT *************/\n/************* GENERATED FILE - DO NOT EDIT *************/\n/************* GENERATED FILE - DO NOT EDIT *************/\n/************* GENERATED FILE - DO NOT EDIT *************/\n/************* GENERATED FILE - DO NOT EDIT *************/\n\n/*\n * To use write/prints/... the default-output port has to be set first.\n * Simply setting SC_DEFAULT_OUT and SC_ERROR_OUT to the desired values\n * should do the trick.\n * In the following example the std-out and error-port are redirected to\n * a DIV.\nfunction initRuntime() {\n    function escapeHTML(s) {\n\tvar tmp = s;\n\ttmp = tmp.replace(/&/g, \"&amp;\");\n\ttmp = tmp.replace(/</g, \"&lt;\");\n\ttmp = tmp.replace(/>/g, \"&gt;\");\n\ttmp = tmp.replace(/ /g, \"&nbsp;\");\n\ttmp = tmp.replace(/\\n/g, \"<br />\");\n\ttmp = tmp.replace(/\\t/g, \"&nbsp;&nbsp;&nbsp;&nbsp\");\n\treturn tmp;\n\n    }\n\n    document.write(\"<div id='stdout'></div>\");\n    SC_DEFAULT_OUT = new sc_GenericOutputPort(\n\tfunction(s) {\n\t    var stdout = document.getElementById('stdout');\n\t    stdout.innerHTML = stdout.innerHTML + escapeHTML(s);\n\t});\n    SC_ERROR_OUT = SC_DEFAULT_OUT;\n}\n*/\n\n\nfunction sc_print_debug() {\n  sc_print.apply(null, arguments);\n}\n\n/*** META ((export *js*)) */\nvar sc_JS_GLOBALS = this;\n\nvar __sc_LINE = -1;\nvar __sc_FILE = \"\";\n\n/*** META ((export #t)) */\nfunction sc_alert() {\n  var len = arguments.length;\n  var s = \"\";\n  var i;\n\n  for (i = 0; i < len; i++) {\n    s += sc_toDisplayString(arguments[i]);\n  }\n\n  return alert(s);\n}\n\n/*** META ((export #t)) */\nfunction sc_typeof(x) {\n  return typeof x;\n}\n\n/*** META ((export #t)) */\nfunction sc_error() {\n  var a = [sc_jsstring2symbol(\"*error*\")];\n  for (var i = 0; i < arguments.length; i++) {\n    a[i + 1] = arguments[i];\n  }\n  throw a;\n}\n\n/*** META ((export #t)\n (peephole (prefix \"throw \")))\n */\nfunction sc_raise(obj) {\n  throw obj;\n}\n\n/*** META ((export with-handler-lambda)) */\nfunction sc_withHandlerLambda(handler, body) {\n  try {\n    return body();\n  } catch (e) {\n    if (!e._internalException)\n      return handler(e);\n    else\n      throw e;\n  }\n}\n\nvar sc_properties = new Object();\n\n/*** META ((export #t)) */\nfunction sc_putpropBang(sym, key, val) {\n  var ht = sc_properties[sym];\n  if (!ht) {\n    ht = new Object();\n    sc_properties[sym] = ht;\n  }\n  ht[key] = val;\n}\n\n/*** META ((export #t)) */\nfunction sc_getprop(sym, key) {\n  var ht = sc_properties[sym];\n  if (ht) {\n    if (key in ht)\n      return ht[key];\n    else\n      return false;\n  } else\n    return false;\n}\n\n/*** META ((export #t)) */\nfunction sc_rempropBang(sym, key) {\n  var ht = sc_properties[sym];\n  if (ht)\n    delete ht[key];\n}\n\n/*** META ((export #t)) */\nfunction sc_any2String(o) {\n  return jsstring2string(sc_toDisplayString(o));\n}\n\n/*** META ((export #t)\n (peephole (infix 2 2 \"===\"))\n (type bool))\n */\nfunction sc_isEqv(o1, o2) {\n  return (o1 === o2);\n}\n\n/*** META ((export #t)\n (peephole (infix 2 2 \"===\"))\n (type bool))\n */\nfunction sc_isEq(o1, o2) {\n  return (o1 === o2);\n}\n\n/*** META ((export #t)\n (type bool))\n */\nfunction sc_isNumber(n) {\n  return (typeof n === \"number\");\n}\n\n/*** META ((export #t)\n (type bool))\n */\nfunction sc_isComplex(n) {\n  return sc_isNumber(n);\n}\n\n/*** META ((export #t)\n (type bool))\n */\nfunction sc_isReal(n) {\n  return sc_isNumber(n);\n}\n\n/*** META ((export #t)\n (type bool))\n */\nfunction sc_isRational(n) {\n  return sc_isReal(n);\n}\n\n/*** META ((export #t)\n (type bool))\n */\nfunction sc_isInteger(n) {\n  return (parseInt(n) === n);\n}\n\n/*** META ((export #t)\n (type bool)\n (peephole (postfix \", false\")))\n */\n// we don't have exact numbers...\nfunction sc_isExact(n) {\n  return false;\n}\n\n/*** META ((export #t)\n (peephole (postfix \", true\"))\n (type bool))\n */\nfunction sc_isInexact(n) {\n  return true;\n}\n\n/*** META ((export = =fx =fl)\n (type bool)\n (peephole (infix 2 2 \"===\")))\n */\nfunction sc_equal(x) {\n  for (var i = 1; i < arguments.length; i++)\n    if (x !== arguments[i])\n      return false;\n  return true;\n}\n\n/*** META ((export < <fx <fl)\n (type bool)\n (peephole (infix 2 2 \"<\")))\n */\nfunction sc_less(x) {\n  for (var i = 1; i < arguments.length; i++) {\n    if (x >= arguments[i])\n      return false;\n    x = arguments[i];\n  }\n  return true;\n}\n\n/*** META ((export > >fx >fl)\n (type bool)\n (peephole (infix 2 2 \">\")))\n */\nfunction sc_greater(x, y) {\n  for (var i = 1; i < arguments.length; i++) {\n    if (x <= arguments[i])\n      return false;\n    x = arguments[i];\n  }\n  return true;\n}\n\n/*** META ((export <= <=fx <=fl)\n (type bool)\n (peephole (infix 2 2 \"<=\")))\n */\nfunction sc_lessEqual(x, y) {\n  for (var i = 1; i < arguments.length; i++) {\n    if (x > arguments[i])\n      return false;\n    x = arguments[i];\n  }\n  return true;\n}\n\n/*** META ((export >= >=fl >=fx)\n (type bool)\n (peephole (infix 2 2 \">=\")))\n */\nfunction sc_greaterEqual(x, y) {\n  for (var i = 1; i < arguments.length; i++) {\n    if (x < arguments[i])\n      return false;\n    x = arguments[i];\n  }\n  return true;\n}\n\n/*** META ((export #t)\n (type bool)\n (peephole (postfix \"=== 0\")))\n */\nfunction sc_isZero(x) {\n  return (x === 0);\n}\n\n/*** META ((export #t)\n (type bool)\n (peephole (postfix \"> 0\")))\n */\nfunction sc_isPositive(x) {\n  return (x > 0);\n}\n\n/*** META ((export #t)\n (type bool)\n (peephole (postfix \"< 0\")))\n */\nfunction sc_isNegative(x) {\n  return (x < 0);\n}\n\n/*** META ((export #t)\n (type bool)\n (peephole (postfix \"%2===1\")))\n */\nfunction sc_isOdd(x) {\n  return (x % 2 === 1);\n}\n\n/*** META ((export #t)\n (type bool)\n (peephole (postfix \"%2===0\")))\n */\nfunction sc_isEven(x) {\n  return (x % 2 === 0);\n}\n\n/*** META ((export #t)) */\nvar sc_max = Math.max;\n/*** META ((export #t)) */\nvar sc_min = Math.min;\n\n/*** META ((export + +fx +fl)\n (peephole (infix 0 #f \"+\" \"0\")))\n */\nfunction sc_plus() {\n  var sum = 0;\n  for (var i = 0; i < arguments.length; i++)\n    sum += arguments[i];\n  return sum;\n}\n\n/*** META ((export * *fx *fl)\n (peephole (infix 0 #f \"*\" \"1\")))\n */\nfunction sc_multi() {\n  var product = 1;\n  for (var i = 0; i < arguments.length; i++)\n    product *= arguments[i];\n  return product;\n}\n\n/*** META ((export - -fx -fl)\n (peephole (minus)))\n */\nfunction sc_minus(x) {\n  if (arguments.length === 1)\n    return -x;\n  else {\n    var res = x;\n    for (var i = 1; i < arguments.length; i++)\n      res -= arguments[i];\n    return res;\n  }\n}\n\n/*** META ((export / /fl)\n (peephole (div)))\n */\nfunction sc_div(x) {\n  if (arguments.length === 1)\n    return 1 / x;\n  else {\n    var res = x;\n    for (var i = 1; i < arguments.length; i++)\n      res /= arguments[i];\n    return res;\n  }\n}\n\n/*** META ((export #t)) */\nvar sc_abs = Math.abs;\n\n/*** META ((export quotient /fx)\n (peephole (hole 2 \"parseInt(\" x \"/\" y \")\")))\n */\nfunction sc_quotient(x, y) {\n  return parseInt(x / y);\n}\n\n/*** META ((export #t)\n (peephole (infix 2 2 \"%\")))\n */\nfunction sc_remainder(x, y) {\n  return x % y;\n}\n\n/*** META ((export #t)\n (peephole (modulo)))\n */\nfunction sc_modulo(x, y) {\n  var remainder = x % y;\n  // if they don't have the same sign\n  if ((remainder * y) < 0)\n    return remainder + y;\n  else\n    return remainder;\n}\n\nfunction sc_euclid_gcd(a, b) {\n  var temp;\n  if (a === 0) return b;\n  if (b === 0) return a;\n  if (a < 0) {\n    a = -a;\n  }\n\n  if (b < 0) {\n    b = -b;\n  }\n\n  if (b > a) {\n    temp = a;\n    a = b;\n    b = temp;\n  }\n\n  while (true) {\n    a %= b;\n    if (a === 0) {\n      return b;\n    }\n\n    b %= a;\n    if (b === 0) {\n      return a;\n    }\n\n  }\n\n  return b;\n}\n\n/*** META ((export #t)) */\nfunction sc_gcd() {\n  var gcd = 0;\n  for (var i = 0; i < arguments.length; i++)\n    gcd = sc_euclid_gcd(gcd, arguments[i]);\n  return gcd;\n}\n\n/*** META ((export #t)) */\nfunction sc_lcm() {\n  var lcm = 1;\n  for (var i = 0; i < arguments.length; i++) {\n    var f = Math.round(arguments[i] / sc_euclid_gcd(arguments[i], lcm));\n    lcm *= Math.abs(f);\n  }\n  return lcm;\n}\n\n// LIMITATION: numerator and denominator don't make sense in floating point world.\n//var SC_MAX_DECIMALS = 1000000\n//\n// function sc_numerator(x) {\n//     var rounded = Math.round(x * SC_MAX_DECIMALS);\n//     return Math.round(rounded / sc_euclid_gcd(rounded, SC_MAX_DECIMALS));\n// }\n\n// function sc_denominator(x) {\n//     var rounded = Math.round(x * SC_MAX_DECIMALS);\n//     return Math.round(SC_MAX_DECIMALS / sc_euclid_gcd(rounded, SC_MAX_DECIMALS));\n// }\n\n/*** META ((export #t)) */\nvar sc_floor = Math.floor;\n/*** META ((export #t)) */\nvar sc_ceiling = Math.ceil;\n/*** META ((export #t)) */\nvar sc_truncate = parseInt;\n/*** META ((export #t)) */\nvar sc_round = Math.round;\n\n// LIMITATION: sc_rationalize doesn't make sense in a floating point world.\n\n/*** META ((export #t)) */\nvar sc_exp = Math.exp;\n/*** META ((export #t)) */\nvar sc_log = Math.log;\n/*** META ((export #t)) */\nvar sc_sin = Math.sin;\n/*** META ((export #t)) */\nvar sc_cos = Math.cos;\n/*** META ((export #t)) */\nvar sc_tan = Math.tan;\n/*** META ((export #t)) */\nvar sc_asin = Math.asin;\n/*** META ((export #t)) */\nvar sc_acos = Math.acos;\n/*** META ((export #t)) */\nvar sc_atan = Math.atan;\n\n/*** META ((export #t)) */\nvar sc_sqrt = Math.sqrt;\n/*** META ((export #t)) */\nvar sc_expt = Math.pow;\n\n// LIMITATION: we don't have complex numbers.\n// LIMITATION: the following functions are hence not implemented.\n// LIMITATION: make-rectangular, make-polar, real-part, imag-part, magnitude, angle\n// LIMITATION: 2 argument atan\n\n/*** META ((export #t)\n (peephole (id)))\n */\nfunction sc_exact2inexact(x) {\n  return x;\n}\n\n/*** META ((export #t)\n (peephole (id)))\n */\nfunction sc_inexact2exact(x) {\n  return x;\n}\n\nfunction sc_number2jsstring(x, radix) {\n  if (radix)\n    return x.toString(radix);\n  else\n    return x.toString();\n}\n\nfunction sc_jsstring2number(s, radix) {\n  if (s === \"\") return false;\n\n  if (radix) {\n    var t = parseInt(s, radix);\n    if (!t && t !== 0) return false;\n    // verify that each char is in range. (parseInt ignores leading\n    // white and trailing chars)\n    var allowedChars = \"01234567890abcdefghijklmnopqrstuvwxyz\".substring(0, radix + 1);\n    if ((new RegExp(\"^[\" + allowedChars + \"]*$\", \"i\")).test(s))\n      return t;\n    else return false;\n  } else {\n    var t = +s; // does not ignore trailing chars.\n    if (!t && t !== 0) return false;\n    // simply verify that first char is not whitespace.\n    var c = s.charAt(0);\n    // if +c is 0, but the char is not \"0\", then we have a whitespace.\n    if (+c === 0 && c !== \"0\") return false;\n    return t;\n  }\n}\n\n/*** META ((export #t)\n (type bool)\n (peephole (not)))\n */\nfunction sc_not(b) {\n  return b === false;\n}\n\n/*** META ((export #t)\n (type bool))\n */\nfunction sc_isBoolean(b) {\n  return (b === true) || (b === false);\n}\n\nfunction sc_Pair(car, cdr) {\n  this.car = car;\n  this.cdr = cdr;\n}\n\nsc_Pair.prototype.toString = function () {\n  return sc_toDisplayString(this);\n};\nsc_Pair.prototype.sc_toWriteOrDisplayString = function (writeOrDisplay) {\n  var current = this;\n\n  var res = \"(\";\n\n  while (true) {\n    res += writeOrDisplay(current.car);\n    if (sc_isPair(current.cdr)) {\n      res += \" \";\n      current = current.cdr;\n    } else if (current.cdr !== null) {\n      res += \" . \" + writeOrDisplay(current.cdr);\n      break;\n    } else // current.cdr == null\n      break;\n  }\n\n  res += \")\";\n\n  return res;\n};\nsc_Pair.prototype.sc_toDisplayString = function () {\n  return this.sc_toWriteOrDisplayString(sc_toDisplayString);\n};\nsc_Pair.prototype.sc_toWriteString = function () {\n  return this.sc_toWriteOrDisplayString(sc_toWriteString);\n};\n\n// sc_Pair.prototype.sc_toWriteCircleString in IO.js\n\n/*** META ((export #t)\n (type bool)\n (peephole (postfix \" instanceof sc_Pair\")))\n */\nfunction sc_isPair(p) {\n  return (p instanceof sc_Pair);\n}\n\nfunction sc_isPairEqual(p1, p2, comp) {\n  return (comp(p1.car, p2.car) && comp(p1.cdr, p2.cdr));\n}\n\n/*** META ((export #t)\n (peephole (hole 2 \"new sc_Pair(\" car \", \" cdr \")\")))\n */\nfunction sc_cons(car, cdr) {\n  return new sc_Pair(car, cdr);\n}\n\n/*** META ((export cons*)) */\nfunction sc_consStar() {\n  var res = arguments[arguments.length - 1];\n  for (var i = arguments.length - 2; i >= 0; i--)\n    res = new sc_Pair(arguments[i], res);\n  return res;\n}\n\n/*** META ((export #t)\n (peephole (postfix \".car\")))\n */\nfunction sc_car(p) {\n  return p.car;\n}\n\n/*** META ((export #t)\n (peephole (postfix \".cdr\")))\n */\nfunction sc_cdr(p) {\n  return p.cdr;\n}\n\n/*** META ((export #t)\n (peephole (hole 2 p \".car = \" val)))\n */\nfunction sc_setCarBang(p, val) {\n  p.car = val;\n}\n\n/*** META ((export #t)\n (peephole (hole 2 p \".cdr = \" val)))\n */\nfunction sc_setCdrBang(p, val) {\n  p.cdr = val;\n}\n\n/*** META ((export #t)\n (peephole (postfix \".car.car\")))\n */\nfunction sc_caar(p) {\n  return p.car.car;\n}\n\n/*** META ((export #t)\n (peephole (postfix \".cdr.car\")))\n */\nfunction sc_cadr(p) {\n  return p.cdr.car;\n}\n\n/*** META ((export #t)\n (peephole (postfix \".car.cdr\")))\n */\nfunction sc_cdar(p) {\n  return p.car.cdr;\n}\n\n/*** META ((export #t)\n (peephole (postfix \".cdr.cdr\")))\n */\nfunction sc_cddr(p) {\n  return p.cdr.cdr;\n}\n\n/*** META ((export #t)\n (peephole (postfix \".car.car.car\")))\n */\nfunction sc_caaar(p) {\n  return p.car.car.car;\n}\n\n/*** META ((export #t)\n (peephole (postfix \".car.cdr.car\")))\n */\nfunction sc_cadar(p) {\n  return p.car.cdr.car;\n}\n\n/*** META ((export #t)\n (peephole (postfix \".cdr.car.car\")))\n */\nfunction sc_caadr(p) {\n  return p.cdr.car.car;\n}\n\n/*** META ((export #t)\n (peephole (postfix \".cdr.cdr.car\")))\n */\nfunction sc_caddr(p) {\n  return p.cdr.cdr.car;\n}\n\n/*** META ((export #t)\n (peephole (postfix \".car.car.cdr\")))\n */\nfunction sc_cdaar(p) {\n  return p.car.car.cdr;\n}\n\n/*** META ((export #t)\n (peephole (postfix \".cdr.car.cdr\")))\n */\nfunction sc_cdadr(p) {\n  return p.cdr.car.cdr;\n}\n\n/*** META ((export #t)\n (peephole (postfix \".car.cdr.cdr\")))\n */\nfunction sc_cddar(p) {\n  return p.car.cdr.cdr;\n}\n\n/*** META ((export #t)\n (peephole (postfix \".cdr.cdr.cdr\")))\n */\nfunction sc_cdddr(p) {\n  return p.cdr.cdr.cdr;\n}\n\n/*** META ((export #t)\n (peephole (postfix \".car.car.car.car\")))\n */\nfunction sc_caaaar(p) {\n  return p.car.car.car.car;\n}\n\n/*** META ((export #t)\n (peephole (postfix \".car.cdr.car.car\")))\n */\nfunction sc_caadar(p) {\n  return p.car.cdr.car.car;\n}\n\n/*** META ((export #t)\n (peephole (postfix \".cdr.car.car.car\")))\n */\nfunction sc_caaadr(p) {\n  return p.cdr.car.car.car;\n}\n\n/*** META ((export #t)\n (peephole (postfix \".cdr.cdr.car.car\")))\n */\nfunction sc_caaddr(p) {\n  return p.cdr.cdr.car.car;\n}\n\n/*** META ((export #t)\n (peephole (postfix \".car.car.car.cdr\")))\n */\nfunction sc_cdaaar(p) {\n  return p.car.car.car.cdr;\n}\n\n/*** META ((export #t)\n (peephole (postfix \".car.cdr.car.cdr\")))\n */\nfunction sc_cdadar(p) {\n  return p.car.cdr.car.cdr;\n}\n\n/*** META ((export #t)\n (peephole (postfix \".cdr.car.car.cdr\")))\n */\nfunction sc_cdaadr(p) {\n  return p.cdr.car.car.cdr;\n}\n\n/*** META ((export #t)\n (peephole (postfix \".cdr.cdr.car.cdr\")))\n */\nfunction sc_cdaddr(p) {\n  return p.cdr.cdr.car.cdr;\n}\n\n/*** META ((export #t)\n (peephole (postfix \".car.car.cdr.car\")))\n */\nfunction sc_cadaar(p) {\n  return p.car.car.cdr.car;\n}\n\n/*** META ((export #t)\n (peephole (postfix \".car.cdr.cdr.car\")))\n */\nfunction sc_caddar(p) {\n  return p.car.cdr.cdr.car;\n}\n\n/*** META ((export #t)\n (peephole (postfix \".cdr.car.cdr.car\")))\n */\nfunction sc_cadadr(p) {\n  return p.cdr.car.cdr.car;\n}\n\n/*** META ((export #t)\n (peephole (postfix \".cdr.cdr.cdr.car\")))\n */\nfunction sc_cadddr(p) {\n  return p.cdr.cdr.cdr.car;\n}\n\n/*** META ((export #t)\n (peephole (postfix \".car.car.cdr.cdr\")))\n */\nfunction sc_cddaar(p) {\n  return p.car.car.cdr.cdr;\n}\n\n/*** META ((export #t)\n (peephole (postfix \".car.cdr.cdr.cdr\")))\n */\nfunction sc_cdddar(p) {\n  return p.car.cdr.cdr.cdr;\n}\n\n/*** META ((export #t)\n (peephole (postfix \".cdr.car.cdr.cdr\")))\n */\nfunction sc_cddadr(p) {\n  return p.cdr.car.cdr.cdr;\n}\n\n/*** META ((export #t)\n (peephole (postfix \".cdr.cdr.cdr.cdr\")))\n */\nfunction sc_cddddr(p) {\n  return p.cdr.cdr.cdr.cdr;\n}\n\n/*** META ((export #t)) */\nfunction sc_lastPair(l) {\n  if (!sc_isPair(l)) sc_error(\"sc_lastPair: pair expected\");\n  var res = l;\n  var cdr = l.cdr;\n  while (sc_isPair(cdr)) {\n    res = cdr;\n    cdr = res.cdr;\n  }\n  return res;\n}\n\n/*** META ((export #t)\n (type bool)\n (peephole (postfix \" === null\")))\n */\nfunction sc_isNull(o) {\n  return (o === null);\n}\n\n/*** META ((export #t)\n (type bool))\n */\nfunction sc_isList(o) {\n  var rabbit;\n  var turtle;\n\n  var rabbit = o;\n  var turtle = o;\n  while (true) {\n    if (rabbit === null ||\n      (rabbit instanceof sc_Pair && rabbit.cdr === null))\n      return true;  // end of list\n    else if ((rabbit instanceof sc_Pair) &&\n      (rabbit.cdr instanceof sc_Pair)) {\n      rabbit = rabbit.cdr.cdr;\n      turtle = turtle.cdr;\n      if (rabbit === turtle) return false; // cycle\n    } else\n      return false; // not pair\n  }\n}\n\n/*** META ((export #t)) */\nfunction sc_list() {\n  var res = null;\n  var a = arguments;\n  for (var i = a.length - 1; i >= 0; i--)\n    res = new sc_Pair(a[i], res);\n  return res;\n}\n\n/*** META ((export #t)) */\nfunction sc_iota(num, init) {\n  var res = null;\n  if (!init) init = 0;\n  for (var i = num - 1; i >= 0; i--)\n    res = new sc_Pair(i + init, res);\n  return res;\n}\n\n/*** META ((export #t)) */\nfunction sc_makeList(nbEls, fill) {\n  var res = null;\n  for (var i = 0; i < nbEls; i++)\n    res = new sc_Pair(fill, res);\n  return res;\n}\n\n/*** META ((export #t)) */\nfunction sc_length(l) {\n  var res = 0;\n  while (l !== null) {\n    res++;\n    l = l.cdr;\n  }\n  return res;\n}\n\n/*** META ((export #t)) */\nfunction sc_remq(o, l) {\n  var dummy = {cdr: null};\n  var tail = dummy;\n  while (l !== null) {\n    if (l.car !== o) {\n      tail.cdr = sc_cons(l.car, null);\n      tail = tail.cdr;\n    }\n    l = l.cdr;\n  }\n  return dummy.cdr;\n}\n\n/*** META ((export #t)) */\nfunction sc_remqBang(o, l) {\n  var dummy = {cdr: null};\n  var tail = dummy;\n  var needsAssig = true;\n  while (l !== null) {\n    if (l.car === o) {\n      needsAssig = true;\n    } else {\n      if (needsAssig) {\n        tail.cdr = l;\n        needsAssig = false;\n      }\n      tail = l;\n    }\n    l = l.cdr;\n  }\n  tail.cdr = null;\n  return dummy.cdr;\n}\n\n/*** META ((export #t)) */\nfunction sc_delete(o, l) {\n  var dummy = {cdr: null};\n  var tail = dummy;\n  while (l !== null) {\n    if (!sc_isEqual(l.car, o)) {\n      tail.cdr = sc_cons(l.car, null);\n      tail = tail.cdr;\n    }\n    l = l.cdr;\n  }\n  return dummy.cdr;\n}\n\n/*** META ((export #t)) */\nfunction sc_deleteBang(o, l) {\n  var dummy = {cdr: null};\n  var tail = dummy;\n  var needsAssig = true;\n  while (l !== null) {\n    if (sc_isEqual(l.car, o)) {\n      needsAssig = true;\n    } else {\n      if (needsAssig) {\n        tail.cdr = l;\n        needsAssig = false;\n      }\n      tail = l;\n    }\n    l = l.cdr;\n  }\n  tail.cdr = null;\n  return dummy.cdr;\n}\n\nfunction sc_reverseAppendBang(l1, l2) {\n  var res = l2;\n  while (l1 !== null) {\n    var tmp = res;\n    res = l1;\n    l1 = l1.cdr;\n    res.cdr = tmp;\n  }\n  return res;\n}\n\nfunction sc_dualAppend(l1, l2) {\n  if (l1 === null) return l2;\n  if (l2 === null) return l1;\n  var rev = sc_reverse(l1);\n  return sc_reverseAppendBang(rev, l2);\n}\n\n/*** META ((export #t)) */\nfunction sc_append() {\n  if (arguments.length === 0)\n    return null;\n  var res = arguments[arguments.length - 1];\n  for (var i = arguments.length - 2; i >= 0; i--)\n    res = sc_dualAppend(arguments[i], res);\n  return res;\n}\n\nfunction sc_dualAppendBang(l1, l2) {\n  if (l1 === null) return l2;\n  if (l2 === null) return l1;\n  var tmp = l1;\n  while (tmp.cdr !== null) tmp = tmp.cdr;\n  tmp.cdr = l2;\n  return l1;\n}\n\n/*** META ((export #t)) */\nfunction sc_appendBang() {\n  var res = null;\n  for (var i = 0; i < arguments.length; i++)\n    res = sc_dualAppendBang(res, arguments[i]);\n  return res;\n}\n\n/*** META ((export #t)) */\nfunction sc_reverse(l1) {\n  var res = null;\n  while (l1 !== null) {\n    res = sc_cons(l1.car, res);\n    l1 = l1.cdr;\n  }\n  return res;\n}\n\n/*** META ((export #t)) */\nfunction sc_reverseBang(l) {\n  return sc_reverseAppendBang(l, null);\n}\n\n/*** META ((export #t)) */\nfunction sc_listTail(l, k) {\n  var res = l;\n  for (var i = 0; i < k; i++) {\n    res = res.cdr;\n  }\n  return res;\n}\n\n/*** META ((export #t)) */\nfunction sc_listRef(l, k) {\n  return sc_listTail(l, k).car;\n}\n\n/* // unoptimized generic versions\nfunction sc_memX(o, l, comp) {\n    while (l != null) {\n\tif (comp(l.car, o))\n\t    return l;\n\tl = l.cdr;\n    }\n    return false;\n}\nfunction sc_memq(o, l) { return sc_memX(o, l, sc_isEq); }\nfunction sc_memv(o, l) { return sc_memX(o, l, sc_isEqv); }\nfunction sc_member(o, l) { return sc_memX(o, l, sc_isEqual); }\n*/\n\n/* optimized versions */\n/*** META ((export #t)) */\nfunction sc_memq(o, l) {\n  while (l !== null) {\n    if (l.car === o)\n      return l;\n    l = l.cdr;\n  }\n  return false;\n}\n\n/*** META ((export #t)) */\nfunction sc_memv(o, l) {\n  while (l !== null) {\n    if (l.car === o)\n      return l;\n    l = l.cdr;\n  }\n  return false;\n}\n\n/*** META ((export #t)) */\nfunction sc_member(o, l) {\n  while (l !== null) {\n    if (sc_isEqual(l.car, o))\n      return l;\n    l = l.cdr;\n  }\n  return false;\n}\n\n/* // generic unoptimized versions\nfunction sc_assX(o, al, comp) {\n    while (al != null) {\n\tif (comp(al.car.car, o))\n\t    return al.car;\n\tal = al.cdr;\n    }\n    return false;\n}\nfunction sc_assq(o, al) { return sc_assX(o, al, sc_isEq); }\nfunction sc_assv(o, al) { return sc_assX(o, al, sc_isEqv); }\nfunction sc_assoc(o, al) { return sc_assX(o, al, sc_isEqual); }\n*/\n\n// optimized versions\n/*** META ((export #t)) */\nfunction sc_assq(o, al) {\n  while (al !== null) {\n    if (al.car.car === o)\n      return al.car;\n    al = al.cdr;\n  }\n  return false;\n}\n\n/*** META ((export #t)) */\nfunction sc_assv(o, al) {\n  while (al !== null) {\n    if (al.car.car === o)\n      return al.car;\n    al = al.cdr;\n  }\n  return false;\n}\n\n/*** META ((export #t)) */\nfunction sc_assoc(o, al) {\n  while (al !== null) {\n    if (sc_isEqual(al.car.car, o))\n      return al.car;\n    al = al.cdr;\n  }\n  return false;\n}\n\n/* can be used for mutable strings and characters */\nfunction sc_isCharStringEqual(cs1, cs2) {\n  return cs1.val === cs2.val;\n}\n\nfunction sc_isCharStringLess(cs1, cs2) {\n  return cs1.val < cs2.val;\n}\n\nfunction sc_isCharStringGreater(cs1, cs2) {\n  return cs1.val > cs2.val;\n}\n\nfunction sc_isCharStringLessEqual(cs1, cs2) {\n  return cs1.val <= cs2.val;\n}\n\nfunction sc_isCharStringGreaterEqual(cs1, cs2) {\n  return cs1.val >= cs2.val;\n}\n\nfunction sc_isCharStringCIEqual(cs1, cs2) {\n  return cs1.val.toLowerCase() === cs2.val.toLowerCase();\n}\n\nfunction sc_isCharStringCILess(cs1, cs2) {\n  return cs1.val.toLowerCase() < cs2.val.toLowerCase();\n}\n\nfunction sc_isCharStringCIGreater(cs1, cs2) {\n  return cs1.val.toLowerCase() > cs2.val.toLowerCase();\n}\n\nfunction sc_isCharStringCILessEqual(cs1, cs2) {\n  return cs1.val.toLowerCase() <= cs2.val.toLowerCase();\n}\n\nfunction sc_isCharStringCIGreaterEqual(cs1, cs2) {\n  return cs1.val.toLowerCase() >= cs2.val.toLowerCase();\n}\n\n\nfunction sc_Char(c) {\n  var cached = sc_Char.lazy[c];\n  if (cached)\n    return cached;\n  this.val = c;\n  sc_Char.lazy[c] = this;\n  // add return, so FF does not complain.\n  return undefined;\n}\n\nsc_Char.lazy = new Object();\n// thanks to Eric\nsc_Char.char2readable = {\n  \"\\u0000\": \"#\\\\null\",\n  \"\\u0007\": \"#\\\\bell\",\n  \"\\b\": \"#\\\\backspace\",\n  \"\\t\": \"#\\\\tab\",\n  \"\\n\": \"#\\\\newline\",\n  \"\\f\": \"#\\\\page\",\n  \"\\r\": \"#\\\\return\",\n  \"\\u001b\": \"#\\\\esc\",\n  \" \": \"#\\\\space\",\n  \"\\u007f\": \"#\\\\delete\",\n  /* poeticless names */\n  \"\\u0001\": \"#\\\\soh\",\n  \"\\u0002\": \"#\\\\stx\",\n  \"\\u0003\": \"#\\\\etx\",\n  \"\\u0004\": \"#\\\\eot\",\n  \"\\u0005\": \"#\\\\enq\",\n  \"\\u0006\": \"#\\\\ack\",\n  \"\\u000b\": \"#\\\\vt\",\n  \"\\u000e\": \"#\\\\so\",\n  \"\\u000f\": \"#\\\\si\",\n  \"\\u0010\": \"#\\\\dle\",\n  \"\\u0011\": \"#\\\\dc1\",\n  \"\\u0012\": \"#\\\\dc2\",\n  \"\\u0013\": \"#\\\\dc3\",\n  \"\\u0014\": \"#\\\\dc4\",\n  \"\\u0015\": \"#\\\\nak\",\n  \"\\u0016\": \"#\\\\syn\",\n  \"\\u0017\": \"#\\\\etb\",\n  \"\\u0018\": \"#\\\\can\",\n  \"\\u0019\": \"#\\\\em\",\n  \"\\u001a\": \"#\\\\sub\",\n  \"\\u001c\": \"#\\\\fs\",\n  \"\\u001d\": \"#\\\\gs\",\n  \"\\u001e\": \"#\\\\rs\",\n  \"\\u001f\": \"#\\\\us\",\n};\n\nsc_Char.readable2char = {\n  \"null\": \"\\u0000\",\n  \"bell\": \"\\u0007\",\n  \"backspace\": \"\\b\",\n  \"tab\": \"\\t\",\n  \"newline\": \"\\n\",\n  \"page\": \"\\f\",\n  \"return\": \"\\r\",\n  \"escape\": \"\\u001b\",\n  \"space\": \" \",\n  \"delete\": \"\\u0000\",\n  \"soh\": \"\\u0001\",\n  \"stx\": \"\\u0002\",\n  \"etx\": \"\\u0003\",\n  \"eot\": \"\\u0004\",\n  \"enq\": \"\\u0005\",\n  \"ack\": \"\\u0006\",\n  \"bel\": \"\\u0007\",\n  \"bs\": \"\\b\",\n  \"ht\": \"\\t\",\n  \"nl\": \"\\n\",\n  \"vt\": \"\\u000b\",\n  \"np\": \"\\f\",\n  \"cr\": \"\\r\",\n  \"so\": \"\\u000e\",\n  \"si\": \"\\u000f\",\n  \"dle\": \"\\u0010\",\n  \"dc1\": \"\\u0011\",\n  \"dc2\": \"\\u0012\",\n  \"dc3\": \"\\u0013\",\n  \"dc4\": \"\\u0014\",\n  \"nak\": \"\\u0015\",\n  \"syn\": \"\\u0016\",\n  \"etb\": \"\\u0017\",\n  \"can\": \"\\u0018\",\n  \"em\": \"\\u0019\",\n  \"sub\": \"\\u001a\",\n  \"esc\": \"\\u001b\",\n  \"fs\": \"\\u001c\",\n  \"gs\": \"\\u001d\",\n  \"rs\": \"\\u001e\",\n  \"us\": \"\\u001f\",\n  \"sp\": \" \",\n  \"del\": \"\\u007f\",\n};\n\nsc_Char.prototype.toString = function () {\n  return this.val;\n};\n// sc_toDisplayString == toString\nsc_Char.prototype.sc_toWriteString = function () {\n  var entry = sc_Char.char2readable[this.val];\n  if (entry)\n    return entry;\n  else\n    return \"#\\\\\" + this.val;\n};\n\n/*** META ((export #t)\n (type bool)\n (peephole (postfix \"instanceof sc_Char\")))\n */\nfunction sc_isChar(c) {\n  return (c instanceof sc_Char);\n}\n\n/*** META ((export char=?)\n (type bool)\n (peephole (hole 2 c1 \".val === \" c2 \".val\")))\n */\nvar sc_isCharEqual = sc_isCharStringEqual;\n/*** META ((export char<?)\n (type bool)\n (peephole (hole 2 c1 \".val < \" c2 \".val\")))\n */\nvar sc_isCharLess = sc_isCharStringLess;\n/*** META ((export char>?)\n (type bool)\n (peephole (hole 2 c1 \".val > \" c2 \".val\")))\n */\nvar sc_isCharGreater = sc_isCharStringGreater;\n/*** META ((export char<=?)\n (type bool)\n (peephole (hole 2 c1 \".val <= \" c2 \".val\")))\n */\nvar sc_isCharLessEqual = sc_isCharStringLessEqual;\n/*** META ((export char>=?)\n (type bool)\n (peephole (hole 2 c1 \".val >= \" c2 \".val\")))\n */\nvar sc_isCharGreaterEqual = sc_isCharStringGreaterEqual;\n/*** META ((export char-ci=?)\n (type bool)\n (peephole (hole 2 c1 \".val.toLowerCase() === \" c2 \".val.toLowerCase()\")))\n */\nvar sc_isCharCIEqual = sc_isCharStringCIEqual;\n/*** META ((export char-ci<?)\n (type bool)\n (peephole (hole 2 c1 \".val.toLowerCase() < \" c2 \".val.toLowerCase()\")))\n */\nvar sc_isCharCILess = sc_isCharStringCILess;\n/*** META ((export char-ci>?)\n (type bool)\n (peephole (hole 2 c1 \".val.toLowerCase() > \" c2 \".val.toLowerCase()\")))\n */\nvar sc_isCharCIGreater = sc_isCharStringCIGreater;\n/*** META ((export char-ci<=?)\n (type bool)\n (peephole (hole 2 c1 \".val.toLowerCase() <= \" c2 \".val.toLowerCase()\")))\n */\nvar sc_isCharCILessEqual = sc_isCharStringCILessEqual;\n/*** META ((export char-ci>=?)\n (type bool)\n (peephole (hole 2 c1 \".val.toLowerCase() >= \" c2 \".val.toLowerCase()\")))\n */\nvar sc_isCharCIGreaterEqual = sc_isCharStringCIGreaterEqual;\n\nvar SC_NUMBER_CLASS = \"0123456789\";\nvar SC_WHITESPACE_CLASS = ' \\r\\n\\t\\f';\nvar SC_LOWER_CLASS = 'abcdefghijklmnopqrstuvwxyz';\nvar SC_UPPER_CLASS = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ';\n\nfunction sc_isCharOfClass(c, cl) {\n  return (cl.indexOf(c) != -1);\n}\n\n/*** META ((export #t)\n (type bool))\n */\nfunction sc_isCharAlphabetic(c) {\n  return sc_isCharOfClass(c.val, SC_LOWER_CLASS) ||\n    sc_isCharOfClass(c.val, SC_UPPER_CLASS);\n}\n\n/*** META ((export #t)\n (type bool)\n (peephole (hole 1 \"SC_NUMBER_CLASS.indexOf(\" c \".val) != -1\")))\n */\nfunction sc_isCharNumeric(c) {\n  return sc_isCharOfClass(c.val, SC_NUMBER_CLASS);\n}\n\n/*** META ((export #t)\n (type bool))\n */\nfunction sc_isCharWhitespace(c) {\n  var tmp = c.val;\n  return tmp === \" \" || tmp === \"\\r\" || tmp === \"\\n\" || tmp === \"\\t\" || tmp === \"\\f\";\n}\n\n/*** META ((export #t)\n (type bool)\n (peephole (hole 1 \"SC_UPPER_CLASS.indexOf(\" c \".val) != -1\")))\n */\nfunction sc_isCharUpperCase(c) {\n  return sc_isCharOfClass(c.val, SC_UPPER_CLASS);\n}\n\n/*** META ((export #t)\n (type bool)\n (peephole (hole 1 \"SC_LOWER_CLASS.indexOf(\" c \".val) != -1\")))\n */\nfunction sc_isCharLowerCase(c) {\n  return sc_isCharOfClass(c.val, SC_LOWER_CLASS);\n}\n\n/*** META ((export #t)\n (peephole (postfix \".val.charCodeAt(0)\")))\n */\nfunction sc_char2integer(c) {\n  return c.val.charCodeAt(0);\n}\n\n/*** META ((export #t)\n (peephole (hole 1 \"new sc_Char(String.fromCharCode(\" n \"))\")))\n */\nfunction sc_integer2char(n) {\n  return new sc_Char(String.fromCharCode(n));\n}\n\n/*** META ((export #t)\n (peephole (hole 1 \"new sc_Char(\" c \".val.toUpperCase())\")))\n */\nfunction sc_charUpcase(c) {\n  return new sc_Char(c.val.toUpperCase());\n}\n\n/*** META ((export #t)\n (peephole (hole 1 \"new sc_Char(\" c \".val.toLowerCase())\")))\n */\nfunction sc_charDowncase(c) {\n  return new sc_Char(c.val.toLowerCase());\n}\n\nfunction sc_makeJSStringOfLength(k, c) {\n  var fill;\n  if (c === undefined)\n    fill = \" \";\n  else\n    fill = c;\n  var res = \"\";\n  var len = 1;\n  // every round doubles the size of fill.\n  while (k >= len) {\n    if (k & len)\n      res = res.concat(fill);\n    fill = fill.concat(fill);\n    len *= 2;\n  }\n  return res;\n}\n\nfunction sc_makejsString(k, c) {\n  var fill;\n  if (c)\n    fill = c.val;\n  else\n    fill = \" \";\n  return sc_makeJSStringOfLength(k, fill);\n}\n\nfunction sc_jsstring2list(s) {\n  var res = null;\n  for (var i = s.length - 1; i >= 0; i--)\n    res = sc_cons(new sc_Char(s.charAt(i)), res);\n  return res;\n}\n\nfunction sc_list2jsstring(l) {\n  var a = new Array();\n  while (l !== null) {\n    a.push(l.car.val);\n    l = l.cdr;\n  }\n  return \"\".concat.apply(\"\", a);\n}\n\nvar sc_Vector = Array;\n\nsc_Vector.prototype.sc_toWriteOrDisplayString = function (writeOrDisplay) {\n  if (this.length === 0) return \"#()\";\n\n  var res = \"#(\" + writeOrDisplay(this[0]);\n  for (var i = 1; i < this.length; i++)\n    res += \" \" + writeOrDisplay(this[i]);\n  res += \")\";\n  return res;\n};\nsc_Vector.prototype.sc_toDisplayString = function () {\n  return this.sc_toWriteOrDisplayString(sc_toDisplayString);\n};\nsc_Vector.prototype.sc_toWriteString = function () {\n  return this.sc_toWriteOrDisplayString(sc_toWriteString);\n};\n\n/*** META ((export vector? array?)\n (type bool)\n (peephole (postfix \" instanceof sc_Vector\")))\n */\nfunction sc_isVector(v) {\n  return (v instanceof sc_Vector);\n}\n\n// only applies to vectors\nfunction sc_isVectorEqual(v1, v2, comp) {\n  if (v1.length !== v2.length) return false;\n  for (var i = 0; i < v1.length; i++)\n    if (!comp(v1[i], v2[i])) return false;\n  return true;\n}\n\n/*** META ((export make-vector make-array)) */\nfunction sc_makeVector(size, fill) {\n  var a = new sc_Vector(size);\n  if (fill !== undefined)\n    sc_vectorFillBang(a, fill);\n  return a;\n}\n\n/*** META ((export vector array)\n (peephole (vector)))\n */\nfunction sc_vector() {\n  var a = new sc_Vector();\n  for (var i = 0; i < arguments.length; i++)\n    a.push(arguments[i]);\n  return a;\n}\n\n/*** META ((export vector-length array-length)\n (peephole (postfix \".length\")))\n */\nfunction sc_vectorLength(v) {\n  return v.length;\n}\n\n/*** META ((export vector-ref array-ref)\n (peephole (hole 2 v \"[\" pos \"]\")))\n */\nfunction sc_vectorRef(v, pos) {\n  return v[pos];\n}\n\n/*** META ((export vector-set! array-set!)\n (peephole (hole 3 v \"[\" pos \"] = \" val)))\n */\nfunction sc_vectorSetBang(v, pos, val) {\n  v[pos] = val;\n}\n\n/*** META ((export vector->list array->list)) */\nfunction sc_vector2list(a) {\n  var res = null;\n  for (var i = a.length - 1; i >= 0; i--)\n    res = sc_cons(a[i], res);\n  return res;\n}\n\n/*** META ((export list->vector list->array)) */\nfunction sc_list2vector(l) {\n  var a = new sc_Vector();\n  while (l !== null) {\n    a.push(l.car);\n    l = l.cdr;\n  }\n  return a;\n}\n\n/*** META ((export vector-fill! array-fill!)) */\nfunction sc_vectorFillBang(a, fill) {\n  for (var i = 0; i < a.length; i++)\n    a[i] = fill;\n}\n\n\n/*** META ((export #t)) */\nfunction sc_copyVector(a, len) {\n  if (len <= a.length)\n    return a.slice(0, len);\n  else {\n    var tmp = a.concat();\n    tmp.length = len;\n    return tmp;\n  }\n}\n\n/*** META ((export #t)\n (peephole (hole 3 a \".slice(\" start \",\" end \")\")))\n */\nfunction sc_vectorCopy(a, start, end) {\n  return a.slice(start, end);\n}\n\n/*** META ((export #t)) */\nfunction sc_vectorCopyBang(target, tstart, source, sstart, send) {\n  if (!sstart) sstart = 0;\n  if (!send) send = source.length;\n\n  // if target == source we don't want to overwrite not yet copied elements.\n  if (tstart <= sstart) {\n    for (var i = tstart, j = sstart; j < send; i++, j++) {\n      target[i] = source[j];\n    }\n  } else {\n    var diff = send - sstart;\n    for (var i = tstart + diff - 1, j = send - 1;\n         j >= sstart;\n         i--, j--) {\n      target[i] = source[j];\n    }\n  }\n  return target;\n}\n\n/*** META ((export #t)\n (type bool)\n (peephole (hole 1 \"typeof \" o \" === 'function'\")))\n */\nfunction sc_isProcedure(o) {\n  return (typeof o === \"function\");\n}\n\n/*** META ((export #t)) */\nfunction sc_apply(proc) {\n  var args = new Array();\n  // first part of arguments are not in list-form.\n  for (var i = 1; i < arguments.length - 1; i++)\n    args.push(arguments[i]);\n  var l = arguments[arguments.length - 1];\n  while (l !== null) {\n    args.push(l.car);\n    l = l.cdr;\n  }\n  return proc.apply(null, args);\n}\n\n/*** META ((export #t)) */\nfunction sc_map(proc, l1) {\n  if (l1 === undefined)\n    return null;\n  // else\n  var nbApplyArgs = arguments.length - 1;\n  var applyArgs = new Array(nbApplyArgs);\n  var revres = null;\n  while (l1 !== null) {\n    for (var i = 0; i < nbApplyArgs; i++) {\n      applyArgs[i] = arguments[i + 1].car;\n      arguments[i + 1] = arguments[i + 1].cdr;\n    }\n    revres = sc_cons(proc.apply(null, applyArgs), revres);\n  }\n  return sc_reverseAppendBang(revres, null);\n}\n\n/*** META ((export #t)) */\nfunction sc_mapBang(proc, l1) {\n  if (l1 === undefined)\n    return null;\n  // else\n  var l1_orig = l1;\n  var nbApplyArgs = arguments.length - 1;\n  var applyArgs = new Array(nbApplyArgs);\n  while (l1 !== null) {\n    var tmp = l1;\n    for (var i = 0; i < nbApplyArgs; i++) {\n      applyArgs[i] = arguments[i + 1].car;\n      arguments[i + 1] = arguments[i + 1].cdr;\n    }\n    tmp.car = proc.apply(null, applyArgs);\n  }\n  return l1_orig;\n}\n\n/*** META ((export #t)) */\nfunction sc_forEach(proc, l1) {\n  if (l1 === undefined)\n    return undefined;\n  // else\n  var nbApplyArgs = arguments.length - 1;\n  var applyArgs = new Array(nbApplyArgs);\n  while (l1 !== null) {\n    for (var i = 0; i < nbApplyArgs; i++) {\n      applyArgs[i] = arguments[i + 1].car;\n      arguments[i + 1] = arguments[i + 1].cdr;\n    }\n    proc.apply(null, applyArgs);\n  }\n  // add return so FF does not complain.\n  return undefined;\n}\n\n/*** META ((export #t)) */\nfunction sc_filter(proc, l1) {\n  var dummy = {cdr: null};\n  var tail = dummy;\n  while (l1 !== null) {\n    if (proc(l1.car) !== false) {\n      tail.cdr = sc_cons(l1.car, null);\n      tail = tail.cdr;\n    }\n    l1 = l1.cdr;\n  }\n  return dummy.cdr;\n}\n\n/*** META ((export #t)) */\nfunction sc_filterBang(proc, l1) {\n  var head = sc_cons(\"dummy\", l1);\n  var it = head;\n  var next = l1;\n  while (next !== null) {\n    if (proc(next.car) !== false) {\n      it.cdr = next\n      it = next;\n    }\n    next = next.cdr;\n  }\n  it.cdr = null;\n  return head.cdr;\n}\n\nfunction sc_filterMap1(proc, l1) {\n  var revres = null;\n  while (l1 !== null) {\n    var tmp = proc(l1.car)\n    if (tmp !== false) revres = sc_cons(tmp, revres);\n    l1 = l1.cdr;\n  }\n  return sc_reverseAppendBang(revres, null);\n}\n\nfunction sc_filterMap2(proc, l1, l2) {\n  var revres = null;\n  while (l1 !== null) {\n    var tmp = proc(l1.car, l2.car);\n    if (tmp !== false) revres = sc_cons(tmp, revres);\n    l1 = l1.cdr;\n    l2 = l2.cdr\n  }\n  return sc_reverseAppendBang(revres, null);\n}\n\n/*** META ((export #t)) */\nfunction sc_filterMap(proc, l1, l2, l3) {\n  if (l2 === undefined)\n    return sc_filterMap1(proc, l1);\n  else if (l3 === undefined)\n    return sc_filterMap2(proc, l1, l2);\n  // else\n  var nbApplyArgs = arguments.length - 1;\n  var applyArgs = new Array(nbApplyArgs);\n  var revres = null;\n  while (l1 !== null) {\n    for (var i = 0; i < nbApplyArgs; i++) {\n      applyArgs[i] = arguments[i + 1].car;\n      arguments[i + 1] = arguments[i + 1].cdr;\n    }\n    var tmp = proc.apply(null, applyArgs);\n    if (tmp !== false) revres = sc_cons(tmp, revres);\n  }\n  return sc_reverseAppendBang(revres, null);\n}\n\n/*** META ((export #t)) */\nfunction sc_any(proc, l) {\n  var revres = null;\n  while (l !== null) {\n    var tmp = proc(l.car);\n    if (tmp !== false) return tmp;\n    l = l.cdr;\n  }\n  return false;\n}\n\n/*** META ((export any?)\n (peephole (hole 2 \"sc_any(\" proc \",\" l \") !== false\")))\n */\nfunction sc_anyPred(proc, l) {\n  return sc_any(proc, l) !== false;\n}\n\n/*** META ((export #t)) */\nfunction sc_every(proc, l) {\n  var revres = null;\n  var tmp = true;\n  while (l !== null) {\n    tmp = proc(l.car);\n    if (tmp === false) return false;\n    l = l.cdr;\n  }\n  return tmp;\n}\n\n/*** META ((export every?)\n (peephole (hole 2 \"sc_every(\" proc \",\" l \") !== false\")))\n */\nfunction sc_everyPred(proc, l) {\n  var tmp = sc_every(proc, l);\n  if (tmp !== false) return true;\n  return false;\n}\n\n/*** META ((export #t)\n (peephole (postfix \"()\")))\n */\nfunction sc_force(o) {\n  return o();\n}\n\n/*** META ((export #t)) */\nfunction sc_makePromise(proc) {\n  var isResultReady = false;\n  var result = undefined;\n  return function () {\n    if (!isResultReady) {\n      var tmp = proc();\n      if (!isResultReady) {\n        isResultReady = true;\n        result = tmp;\n      }\n    }\n    return result;\n  };\n}\n\nfunction sc_Values(values) {\n  this.values = values;\n}\n\n/*** META ((export #t)\n (peephole (values)))\n */\nfunction sc_values() {\n  if (arguments.length === 1)\n    return arguments[0];\n  else\n    return new sc_Values(arguments);\n}\n\n/*** META ((export #t)) */\nfunction sc_callWithValues(producer, consumer) {\n  var produced = producer();\n  if (produced instanceof sc_Values)\n    return consumer.apply(null, produced.values);\n  else\n    return consumer(produced);\n}\n\n/*** META ((export #t)) */\nfunction sc_dynamicWind(before, thunk, after) {\n  before();\n  try {\n    var res = thunk();\n    return res;\n  } finally {\n    after();\n  }\n}\n\n\n// TODO: eval/scheme-report-environment/null-environment/interaction-environment\n\n// LIMITATION: 'load' doesn't exist without files.\n// LIMITATION: transcript-on/transcript-off doesn't exist without files.\n\n\nfunction sc_Struct(name) {\n  this.name = name;\n}\n\nsc_Struct.prototype.sc_toDisplayString = function () {\n  return \"#<struct\" + sc_hash(this) + \">\";\n};\nsc_Struct.prototype.sc_toWriteString = sc_Struct.prototype.sc_toDisplayString;\n\n/*** META ((export #t)\n (peephole (hole 1 \"new sc_Struct(\" name \")\")))\n */\nfunction sc_makeStruct(name) {\n  return new sc_Struct(name);\n}\n\n/*** META ((export #t)\n (type bool)\n (peephole (postfix \" instanceof sc_Struct\")))\n */\nfunction sc_isStruct(o) {\n  return (o instanceof sc_Struct);\n}\n\n/*** META ((export #t)\n (type bool)\n (peephole (hole 2 \"(\" 1 \" instanceof sc_Struct) && ( \" 1 \".name === \" 0 \")\")))\n */\nfunction sc_isStructNamed(name, s) {\n  return ((s instanceof sc_Struct) && (s.name === name));\n}\n\n/*** META ((export struct-field)\n (peephole (hole 3 0 \"[\" 2 \"]\")))\n */\nfunction sc_getStructField(s, name, field) {\n  return s[field];\n}\n\n/*** META ((export struct-field-set!)\n (peephole (hole 4 0 \"[\" 2 \"] = \" 3)))\n */\nfunction sc_setStructFieldBang(s, name, field, val) {\n  s[field] = val;\n}\n\n/*** META ((export #t)\n (peephole (prefix \"~\")))\n */\nfunction sc_bitNot(x) {\n  return ~x;\n}\n\n/*** META ((export #t)\n (peephole (infix 2 2 \"&\")))\n */\nfunction sc_bitAnd(x, y) {\n  return x & y;\n}\n\n/*** META ((export #t)\n (peephole (infix 2 2 \"|\")))\n */\nfunction sc_bitOr(x, y) {\n  return x | y;\n}\n\n/*** META ((export #t)\n (peephole (infix 2 2 \"^\")))\n */\nfunction sc_bitXor(x, y) {\n  return x ^ y;\n}\n\n/*** META ((export #t)\n (peephole (infix 2 2 \"<<\")))\n */\nfunction sc_bitLsh(x, y) {\n  return x << y;\n}\n\n/*** META ((export #t)\n (peephole (infix 2 2 \">>\")))\n */\nfunction sc_bitRsh(x, y) {\n  return x >> y;\n}\n\n/*** META ((export #t)\n (peephole (infix 2 2 \">>>\")))\n */\nfunction sc_bitUrsh(x, y) {\n  return x >>> y;\n}\n\n/*** META ((export js-field js-property)\n (peephole (hole 2 o \"[\" field \"]\")))\n */\nfunction sc_jsField(o, field) {\n  return o[field];\n}\n\n/*** META ((export js-field-set! js-property-set!)\n (peephole (hole 3 o \"[\" field \"] = \" val)))\n */\nfunction sc_setJsFieldBang(o, field, val) {\n  return o[field] = val;\n}\n\n/*** META ((export js-field-delete! js-property-delete!)\n (peephole (hole 2 \"delete\" o \"[\" field \"]\")))\n */\nfunction sc_deleteJsFieldBang(o, field) {\n  delete o[field];\n}\n\n/*** META ((export #t)\n (peephole (jsCall)))\n */\nfunction sc_jsCall(o, fun) {\n  var args = new Array();\n  for (var i = 2; i < arguments.length; i++)\n    args[i - 2] = arguments[i];\n  return fun.apply(o, args);\n}\n\n/*** META ((export #t)\n (peephole (jsMethodCall)))\n */\nfunction sc_jsMethodCall(o, field) {\n  var args = new Array();\n  for (var i = 2; i < arguments.length; i++)\n    args[i - 2] = arguments[i];\n  return o[field].apply(o, args);\n}\n\n/*** META ((export new js-new)\n (peephole (jsNew)))\n */\nfunction sc_jsNew(c) {\n  var evalStr = \"new c(\";\n  evalStr += arguments.length > 1 ? \"arguments[1]\" : \"\";\n  for (var i = 2; i < arguments.length; i++)\n    evalStr += \", arguments[\" + i + \"]\";\n  evalStr += \")\";\n  return eval(evalStr);\n}\n\n// ======================== RegExp ====================\n/*** META ((export #t)) */\nfunction sc_pregexp(re) {\n  return new RegExp(sc_string2jsstring(re));\n}\n\n/*** META ((export #t)) */\nfunction sc_pregexpMatch(re, s) {\n  var reg = (re instanceof RegExp) ? re : sc_pregexp(re);\n  var tmp = reg.exec(sc_string2jsstring(s));\n\n  if (tmp == null) return false;\n\n  var res = null;\n  for (var i = tmp.length - 1; i >= 0; i--) {\n    if (tmp[i] !== null) {\n      res = sc_cons(sc_jsstring2string(tmp[i]), res);\n    } else {\n      res = sc_cons(false, res);\n    }\n  }\n  return res;\n}\n\n/*** META ((export #t)) */\nfunction sc_pregexpReplace(re, s1, s2) {\n  var reg;\n  var jss1 = sc_string2jsstring(s1);\n  var jss2 = sc_string2jsstring(s2);\n\n  if (re instanceof RegExp) {\n    if (re.global)\n      reg = re;\n    else\n      reg = new RegExp(re.source);\n  } else {\n    reg = new RegExp(sc_string2jsstring(re));\n  }\n\n  return jss1.replace(reg, jss2);\n}\n\n/*** META ((export pregexp-replace*)) */\nfunction sc_pregexpReplaceAll(re, s1, s2) {\n  var reg;\n  var jss1 = sc_string2jsstring(s1);\n  var jss2 = sc_string2jsstring(s2);\n\n  if (re instanceof RegExp) {\n    if (re.global)\n      reg = re;\n    else\n      reg = new RegExp(re.source, \"g\");\n  } else {\n    reg = new RegExp(sc_string2jsstring(re), \"g\");\n  }\n\n  return jss1.replace(reg, jss2);\n}\n\n/*** META ((export #t)) */\nfunction sc_pregexpSplit(re, s) {\n  var reg = ((re instanceof RegExp) ?\n    re :\n    new RegExp(sc_string2jsstring(re)));\n  var jss = sc_string2jsstring(s);\n  var tmp = jss.split(reg);\n\n  if (tmp == null) return false;\n\n  return sc_vector2list(tmp);\n}\n\n\n/* =========================================================================== */\n/* Other library stuff */\n\n/* =========================================================================== */\n\n/*** META ((export #t)\n (peephole (hole 1 \"Math.floor(Math.random()*\" 'n \")\")))\n */\nfunction sc_random(n) {\n  return Math.floor(Math.random() * n);\n}\n\n/*** META ((export current-date)\n (peephole (hole 0 \"new Date()\")))\n */\nfunction sc_currentDate() {\n  return new Date();\n}\n\nfunction sc_Hashtable() {\n}\n\nsc_Hashtable.prototype.toString = function () {\n  return \"#{%hashtable}\";\n};\n\n// sc_toWriteString == sc_toDisplayString == toString\n\nfunction sc_HashtableElement(key, val) {\n  this.key = key;\n  this.val = val;\n}\n\n/*** META ((export #t)\n (peephole (hole 0 \"new sc_Hashtable()\")))\n */\nfunction sc_makeHashtable() {\n  return new sc_Hashtable();\n}\n\n/*** META ((export #t)) */\nfunction sc_hashtablePutBang(ht, key, val) {\n  var hash = sc_hash(key);\n  ht[hash] = new sc_HashtableElement(key, val);\n}\n\n/*** META ((export #t)) */\nfunction sc_hashtableGet(ht, key) {\n  var hash = sc_hash(key);\n  if (hash in ht)\n    return ht[hash].val;\n  else\n    return false;\n}\n\n/*** META ((export #t)) */\nfunction sc_hashtableForEach(ht, f) {\n  for (var v in ht) {\n    if (ht[v] instanceof sc_HashtableElement)\n      f(ht[v].key, ht[v].val);\n  }\n}\n\n/*** META ((export hashtable-contains?)\n (peephole (hole 2 \"sc_hash(\" 1 \") in \" 0)))\n */\nfunction sc_hashtableContains(ht, key) {\n  var hash = sc_hash(key);\n  if (hash in ht)\n    return true;\n  else\n    return false;\n}\n\nvar SC_HASH_COUNTER = 0;\n\nfunction sc_hash(o) {\n  if (o === null)\n    return \"null\";\n  else if (o === undefined)\n    return \"undefined\";\n  else if (o === true)\n    return \"true\";\n  else if (o === false)\n    return \"false\";\n  else if (typeof o === \"number\")\n    return \"num-\" + o;\n  else if (typeof o === \"string\")\n    return \"jsstr-\" + o;\n  else if (o.sc_getHash)\n    return o.sc_getHash();\n  else\n    return sc_counterHash.call(o);\n}\n\nfunction sc_counterHash() {\n  if (!this.sc_hash) {\n    this.sc_hash = \"hash-\" + SC_HASH_COUNTER;\n    SC_HASH_COUNTER++;\n  }\n  return this.sc_hash;\n}\n\nfunction sc_Trampoline(args, maxTailCalls) {\n  this['__trampoline return__'] = true;\n  this.args = args;\n  this.MAX_TAIL_CALLs = maxTailCalls;\n}\n\n// TODO: call/cc stuff\nsc_Trampoline.prototype.restart = function () {\n  var o = this;\n  while (true) {\n    // set both globals.\n    SC_TAIL_OBJECT.calls = o.MAX_TAIL_CALLs - 1;\n    var fun = o.args.callee;\n    var res = fun.apply(SC_TAIL_OBJECT, o.args);\n    if (res instanceof sc_Trampoline)\n      o = res;\n    else\n      return res;\n  }\n}\n\n/*** META ((export bind-exit-lambda)) */\nfunction sc_bindExitLambda(proc) {\n  var escape_obj = new sc_BindExitException();\n  var escape = function (res) {\n    escape_obj.res = res;\n    throw escape_obj;\n  };\n  try {\n    return proc(escape);\n  } catch (e) {\n    if (e === escape_obj) {\n      return e.res;\n    }\n    throw e;\n  }\n}\n\nfunction sc_BindExitException() {\n  this._internalException = true;\n}\n\nvar SC_SCM2JS_GLOBALS = new Object();\n\n// default tail-call depth.\n// normally the program should set it again. but just in case...\nvar SC_TAIL_OBJECT = new Object();\nSC_SCM2JS_GLOBALS.TAIL_OBJECT = SC_TAIL_OBJECT;\n// ======================== I/O =======================\n\n/*------------------------------------------------------------------*/\n\nfunction sc_EOF() {\n}\n\nvar SC_EOF_OBJECT = new sc_EOF();\n\nfunction sc_Port() {\n}\n\n/* --------------- Input ports -------------------------------------*/\n\nfunction sc_InputPort() {\n}\n\nsc_InputPort.prototype = new sc_Port();\n\nsc_InputPort.prototype.peekChar = function () {\n  if (!(\"peeked\" in this))\n    this.peeked = this.getNextChar();\n  return this.peeked;\n}\nsc_InputPort.prototype.readChar = function () {\n  var tmp = this.peekChar();\n  delete this.peeked;\n  return tmp;\n}\nsc_InputPort.prototype.isCharReady = function () {\n  return true;\n}\nsc_InputPort.prototype.close = function () {\n  // do nothing\n}\n\n/* .............. String port ..........................*/\nfunction sc_ErrorInputPort() {\n}\n\nsc_ErrorInputPort.prototype = new sc_InputPort();\nsc_ErrorInputPort.prototype.getNextChar = function () {\n  throw \"can't read from error-port.\";\n};\nsc_ErrorInputPort.prototype.isCharReady = function () {\n  return false;\n};\n\n\n/* .............. String port ..........................*/\n\nfunction sc_StringInputPort(jsStr) {\n  // we are going to do some charAts on the str.\n  // instead of recreating all the time a String-object, we\n  // create one in the beginning. (not sure, if this is really an optim)\n  this.str = new String(jsStr);\n  this.pos = 0;\n}\n\nsc_StringInputPort.prototype = new sc_InputPort();\nsc_StringInputPort.prototype.getNextChar = function () {\n  if (this.pos >= this.str.length)\n    return SC_EOF_OBJECT;\n  return this.str.charAt(this.pos++);\n};\n\n/* ------------- Read and other lib-funs  -------------------------------*/\nfunction sc_Token(type, val, pos) {\n  this.type = type;\n  this.val = val;\n  this.pos = pos;\n}\n\nsc_Token.EOF = 0/*EOF*/;\nsc_Token.OPEN_PAR = 1/*OPEN_PAR*/;\nsc_Token.CLOSE_PAR = 2/*CLOSE_PAR*/;\nsc_Token.OPEN_BRACE = 3/*OPEN_BRACE*/;\nsc_Token.CLOSE_BRACE = 4/*CLOSE_BRACE*/;\nsc_Token.OPEN_BRACKET = 5/*OPEN_BRACKET*/;\nsc_Token.CLOSE_BRACKET = 6/*CLOSE_BRACKET*/;\nsc_Token.WHITESPACE = 7/*WHITESPACE*/;\nsc_Token.QUOTE = 8/*QUOTE*/;\nsc_Token.ID = 9/*ID*/;\nsc_Token.DOT = 10/*DOT*/;\nsc_Token.STRING = 11/*STRING*/;\nsc_Token.NUMBER = 12/*NUMBER*/;\nsc_Token.ERROR = 13/*ERROR*/;\nsc_Token.VECTOR_BEGIN = 14/*VECTOR_BEGIN*/;\nsc_Token.TRUE = 15/*TRUE*/;\nsc_Token.FALSE = 16/*FALSE*/;\nsc_Token.UNSPECIFIED = 17/*UNSPECIFIED*/;\nsc_Token.REFERENCE = 18/*REFERENCE*/;\nsc_Token.STORE = 19/*STORE*/;\nsc_Token.CHAR = 20/*CHAR*/;\n\nvar SC_ID_CLASS = SC_LOWER_CLASS + SC_UPPER_CLASS + \"!$%*+-./:<=>?@^_~\";\n\nfunction sc_Tokenizer(port) {\n  this.port = port;\n}\n\nsc_Tokenizer.prototype.peekToken = function () {\n  if (this.peeked)\n    return this.peeked;\n  var newToken = this.nextToken();\n  this.peeked = newToken;\n  return newToken;\n};\nsc_Tokenizer.prototype.readToken = function () {\n  var tmp = this.peekToken();\n  delete this.peeked;\n  return tmp;\n};\nsc_Tokenizer.prototype.nextToken = function () {\n  var port = this.port;\n\n  function isNumberChar(c) {\n    return (c >= \"0\" && c <= \"9\");\n  }\n\n  function isIdOrNumberChar(c) {\n    return SC_ID_CLASS.indexOf(c) != -1 || // ID-char\n      (c >= \"0\" && c <= \"9\");\n  }\n\n  function isWhitespace(c) {\n    return c === \" \" || c === \"\\r\" || c === \"\\n\" || c === \"\\t\" || c === \"\\f\";\n  }\n\n  function isWhitespaceOrEOF(c) {\n    return isWhitespace(c) || c === SC_EOF_OBJECT;\n  }\n\n  function readString() {\n    res = \"\";\n    while (true) {\n      var c = port.readChar();\n      switch (c) {\n        case '\"':\n          return new sc_Token(11/*STRING*/, res);\n        case \"\\\\\":\n          var tmp = port.readChar();\n          switch (tmp) {\n            case '0':\n              res += \"\\0\";\n              break;\n            case 'a':\n              res += \"\\a\";\n              break;\n            case 'b':\n              res += \"\\b\";\n              break;\n            case 'f':\n              res += \"\\f\";\n              break;\n            case 'n':\n              res += \"\\n\";\n              break;\n            case 'r':\n              res += \"\\r\";\n              break;\n            case 't':\n              res += \"\\t\";\n              break;\n            case 'v':\n              res += \"\\v\";\n              break;\n            case '\"':\n              res += '\"';\n              break;\n            case '\\\\':\n              res += '\\\\';\n              break;\n            case 'x':\n              /* hexa-number */\n              var nb = 0;\n              while (true) {\n                var hexC = port.peekChar();\n                if (hexC >= '0' && hexC <= '9') {\n                  port.readChar();\n                  nb = nb * 16 + hexC.charCodeAt(0) - '0'.charCodeAt(0);\n                } else if (hexC >= 'a' && hexC <= 'f') {\n                  port.readChar();\n                  nb = nb * 16 + hexC.charCodeAt(0) - 'a'.charCodeAt(0);\n                } else if (hexC >= 'A' && hexC <= 'F') {\n                  port.readChar();\n                  nb = nb * 16 + hexC.charCodeAt(0) - 'A'.charCodeAt(0);\n                } else {\n                  // next char isn't part of hex.\n                  res += String.fromCharCode(nb);\n                  break;\n                }\n              }\n              break;\n            default:\n              if (tmp === SC_EOF_OBJECT) {\n                return new sc_Token(13/*ERROR*/, \"unclosed string-literal\" + res);\n              }\n              res += tmp;\n          }\n          break;\n        default:\n          if (c === SC_EOF_OBJECT) {\n            return new sc_Token(13/*ERROR*/, \"unclosed string-literal\" + res);\n          }\n          res += c;\n      }\n    }\n  }\n\n  function readIdOrNumber(firstChar) {\n    var res = firstChar;\n    while (isIdOrNumberChar(port.peekChar()))\n      res += port.readChar();\n    if (isNaN(res))\n      return new sc_Token(9/*ID*/, res);\n    else\n      return new sc_Token(12/*NUMBER*/, res - 0);\n  }\n\n  function skipWhitespaceAndComments() {\n    var done = false;\n    while (!done) {\n      done = true;\n      while (isWhitespace(port.peekChar()))\n        port.readChar();\n      if (port.peekChar() === ';') {\n        port.readChar();\n        done = false;\n        while (true) {\n          curChar = port.readChar();\n          if (curChar === SC_EOF_OBJECT ||\n            curChar === '\\n')\n            break;\n        }\n      }\n    }\n  }\n\n  function readDot() {\n    if (isWhitespace(port.peekChar()))\n      return new sc_Token(10/*DOT*/);\n    else\n      return readIdOrNumber(\".\");\n  }\n\n  function readSharp() {\n    var c = port.readChar();\n    if (isWhitespace(c))\n      return new sc_Token(13/*ERROR*/, \"bad #-pattern0.\");\n\n    // reference\n    if (isNumberChar(c)) {\n      var nb = c - 0;\n      while (isNumberChar(port.peekChar()))\n        nb = nb * 10 + (port.readChar() - 0);\n      switch (port.readChar()) {\n        case '#':\n          return new sc_Token(18/*REFERENCE*/, nb);\n        case '=':\n          return new sc_Token(19/*STORE*/, nb);\n        default:\n          return new sc_Token(13/*ERROR*/, \"bad #-pattern1.\" + nb);\n      }\n    }\n\n    if (c === \"(\")\n      return new sc_Token(14/*VECTOR_BEGIN*/);\n\n    if (c === \"\\\\\") { // character\n      var tmp = \"\"\n      while (!isWhitespaceOrEOF(port.peekChar()))\n        tmp += port.readChar();\n      switch (tmp.length) {\n        case 0: // it's escaping a whitespace char:\n          if (sc_isEOFObject(port.peekChar))\n            return new sc_Token(13/*ERROR*/, \"bad #-pattern2.\");\n          else\n            return new sc_Token(20/*CHAR*/, port.readChar());\n        case 1:\n          return new sc_Token(20/*CHAR*/, tmp);\n        default:\n          var entry = sc_Char.readable2char[tmp.toLowerCase()];\n          if (entry)\n            return new sc_Token(20/*CHAR*/, entry);\n          else\n            return new sc_Token(13/*ERROR*/, \"unknown character description: #\\\\\" + tmp);\n      }\n    }\n\n    // some constants (#t, #f, #unspecified)\n    var res;\n    var needing;\n    switch (c) {\n      case 't':\n        res = new sc_Token(15/*TRUE*/, true);\n        needing = \"\";\n        break;\n      case 'f':\n        res = new sc_Token(16/*FALSE*/, false);\n        needing = \"\";\n        break;\n      case 'u':\n        res = new sc_Token(17/*UNSPECIFIED*/, undefined);\n        needing = \"nspecified\";\n        break;\n      default:\n        return new sc_Token(13/*ERROR*/, \"bad #-pattern3: \" + c);\n    }\n    while (true) {\n      c = port.peekChar();\n      if ((isWhitespaceOrEOF(c) || c === ')') &&\n        needing == \"\")\n        return res;\n      else if (isWhitespace(c) || needing == \"\")\n        return new sc_Token(13/*ERROR*/, \"bad #-pattern4 \" + c + \" \" + needing);\n      else if (needing.charAt(0) == c) {\n        port.readChar(); // consume\n        needing = needing.slice(1);\n      } else\n        return new sc_Token(13/*ERROR*/, \"bad #-pattern5\");\n    }\n\n  }\n\n  skipWhitespaceAndComments();\n  var curChar = port.readChar();\n  if (curChar === SC_EOF_OBJECT)\n    return new sc_Token(0/*EOF*/, curChar);\n  switch (curChar) {\n    case \" \":\n    case \"\\n\":\n    case \"\\t\":\n      return readWhitespace();\n    case \"(\":\n      return new sc_Token(1/*OPEN_PAR*/);\n    case \")\":\n      return new sc_Token(2/*CLOSE_PAR*/);\n    case \"{\":\n      return new sc_Token(3/*OPEN_BRACE*/);\n    case \"}\":\n      return new sc_Token(4/*CLOSE_BRACE*/);\n    case \"[\":\n      return new sc_Token(5/*OPEN_BRACKET*/);\n    case \"]\":\n      return new sc_Token(6/*CLOSE_BRACKET*/);\n    case \"'\":\n      return new sc_Token(8/*QUOTE*/);\n    case \"#\":\n      return readSharp();\n    case \".\":\n      return readDot();\n    case '\"':\n      return readString();\n    default:\n      if (isIdOrNumberChar(curChar))\n        return readIdOrNumber(curChar);\n      throw \"unexpected character: \" + curChar;\n  }\n};\n\nfunction sc_Reader(tokenizer) {\n  this.tokenizer = tokenizer;\n  this.backref = new Array();\n}\n\nsc_Reader.prototype.read = function () {\n  function readList(listBeginType) {\n    function matchesPeer(open, close) {\n      return open === 1/*OPEN_PAR*/ && close === 2/*CLOSE_PAR*/\n        || open === 3/*OPEN_BRACE*/ && close === 4/*CLOSE_BRACE*/\n        || open === 5/*OPEN_BRACKET*/ && close === 6/*CLOSE_BRACKET*/;\n    }\n\n    var res = null;\n\n    while (true) {\n      var token = tokenizer.peekToken();\n\n      switch (token.type) {\n        case 2/*CLOSE_PAR*/\n        :\n        case 4/*CLOSE_BRACE*/\n        :\n        case 6/*CLOSE_BRACKET*/\n        :\n          if (matchesPeer(listBeginType, token.type)) {\n            tokenizer.readToken(); // consume token\n            return sc_reverseBang(res);\n          } else\n            throw \"closing par doesn't match: \" + listBeginType\n            + \" \" + listEndType;\n\n        case 0/*EOF*/\n        :\n          throw \"unexpected end of file\";\n\n        case 10/*DOT*/\n        :\n          tokenizer.readToken(); // consume token\n          var cdr = this.read();\n          var par = tokenizer.readToken();\n          if (!matchesPeer(listBeginType, par.type))\n            throw \"closing par doesn't match: \" + listBeginType\n            + \" \" + par.type;\n          else\n            return sc_reverseAppendBang(res, cdr);\n\n\n        default:\n          res = sc_cons(this.read(), res);\n      }\n    }\n  }\n\n  function readQuote() {\n    return sc_cons(\"quote\", sc_cons(this.read(), null));\n  }\n\n  function readVector() {\n    // opening-parenthesis is already consumed\n    var a = new Array();\n    while (true) {\n      var token = tokenizer.peekToken();\n      switch (token.type) {\n        case 2/*CLOSE_PAR*/\n        :\n          tokenizer.readToken();\n          return a;\n\n        default:\n          a.push(this.read());\n      }\n    }\n  }\n\n  function storeRefence(nb) {\n    var tmp = this.read();\n    this.backref[nb] = tmp;\n    return tmp;\n  }\n\n  function readReference(nb) {\n    if (nb in this.backref)\n      return this.backref[nb];\n    else\n      throw \"bad reference: \" + nb;\n  }\n\n  var tokenizer = this.tokenizer;\n\n  var token = tokenizer.readToken();\n\n  // handle error\n  if (token.type === 13/*ERROR*/)\n    throw token.val;\n\n  switch (token.type) {\n    case 1/*OPEN_PAR*/\n    :\n    case 3/*OPEN_BRACE*/\n    :\n    case 5/*OPEN_BRACKET*/\n    :\n      return readList.call(this, token.type);\n    case 8/*QUOTE*/\n    :\n      return readQuote.call(this);\n    case 11/*STRING*/\n    :\n      return sc_jsstring2string(token.val);\n    case 20/*CHAR*/\n    :\n      return new sc_Char(token.val);\n    case 14/*VECTOR_BEGIN*/\n    :\n      return readVector.call(this);\n    case 18/*REFERENCE*/\n    :\n      return readReference.call(this, token.val);\n    case 19/*STORE*/\n    :\n      return storeRefence.call(this, token.val);\n    case 9/*ID*/\n    :\n      return sc_jsstring2symbol(token.val);\n    case 0/*EOF*/\n    :\n    case 12/*NUMBER*/\n    :\n    case 15/*TRUE*/\n    :\n    case 16/*FALSE*/\n    :\n    case 17/*UNSPECIFIED*/\n    :\n      return token.val;\n    default:\n      throw \"unexpected token \" + token.type + \" \" + token.val;\n  }\n};\n\n/*** META ((export #t)) */\nfunction sc_read(port) {\n  if (port === undefined) // we assume the port hasn't been given.\n    port = SC_DEFAULT_IN; // THREAD: shared var...\n  var reader = new sc_Reader(new sc_Tokenizer(port));\n  return reader.read();\n}\n\n/*** META ((export #t)) */\nfunction sc_readChar(port) {\n  if (port === undefined) // we assume the port hasn't been given.\n    port = SC_DEFAULT_IN; // THREAD: shared var...\n  var t = port.readChar();\n  return t === SC_EOF_OBJECT ? t : new sc_Char(t);\n}\n\n/*** META ((export #t)) */\nfunction sc_peekChar(port) {\n  if (port === undefined) // we assume the port hasn't been given.\n    port = SC_DEFAULT_IN; // THREAD: shared var...\n  var t = port.peekChar();\n  return t === SC_EOF_OBJECT ? t : new sc_Char(t);\n}\n\n/*** META ((export #t)\n (type bool))\n */\nfunction sc_isCharReady(port) {\n  if (port === undefined) // we assume the port hasn't been given.\n    port = SC_DEFAULT_IN; // THREAD: shared var...\n  return port.isCharReady();\n}\n\n/*** META ((export #t)\n (peephole (postfix \".close()\")))\n */\nfunction sc_closeInputPort(p) {\n  return p.close();\n}\n\n/*** META ((export #t)\n (type bool)\n (peephole (postfix \" instanceof sc_InputPort\")))\n */\nfunction sc_isInputPort(o) {\n  return (o instanceof sc_InputPort);\n}\n\n/*** META ((export eof-object?)\n (type bool)\n (peephole (postfix \" === SC_EOF_OBJECT\")))\n */\nfunction sc_isEOFObject(o) {\n  return o === SC_EOF_OBJECT;\n}\n\n/*** META ((export #t)\n (peephole (hole 0 \"SC_DEFAULT_IN\")))\n */\nfunction sc_currentInputPort() {\n  return SC_DEFAULT_IN;\n}\n\n/* ------------ file operations are not supported -----------*/\n/*** META ((export #t)) */\nfunction sc_callWithInputFile(s, proc) {\n  throw \"can't open \" + s;\n}\n\n/*** META ((export #t)) */\nfunction sc_callWithOutputFile(s, proc) {\n  throw \"can't open \" + s;\n}\n\n/*** META ((export #t)) */\nfunction sc_withInputFromFile(s, thunk) {\n  throw \"can't open \" + s;\n}\n\n/*** META ((export #t)) */\nfunction sc_withOutputToFile(s, thunk) {\n  throw \"can't open \" + s;\n}\n\n/*** META ((export #t)) */\nfunction sc_openInputFile(s) {\n  throw \"can't open \" + s;\n}\n\n/*** META ((export #t)) */\nfunction sc_openOutputFile(s) {\n  throw \"can't open \" + s;\n}\n\n/* ----------------------------------------------------------------------------*/\n/*** META ((export #t)) */\nfunction sc_basename(p) {\n  var i = p.lastIndexOf('/');\n\n  if (i >= 0)\n    return p.substring(i + 1, p.length);\n  else\n    return '';\n}\n\n/*** META ((export #t)) */\nfunction sc_dirname(p) {\n  var i = p.lastIndexOf('/');\n\n  if (i >= 0)\n    return p.substring(0, i);\n  else\n    return '';\n}\n\n/* ----------------------------------------------------------------------------*/\n\n/*** META ((export #t)) */\nfunction sc_withInputFromPort(p, thunk) {\n  try {\n    var tmp = SC_DEFAULT_IN; // THREAD: shared var.\n    SC_DEFAULT_IN = p;\n    return thunk();\n  } finally {\n    SC_DEFAULT_IN = tmp;\n  }\n}\n\n/*** META ((export #t)) */\nfunction sc_withInputFromString(s, thunk) {\n  return sc_withInputFromPort(new sc_StringInputPort(sc_string2jsstring(s)), thunk);\n}\n\n/*** META ((export #t)) */\nfunction sc_withOutputToPort(p, thunk) {\n  try {\n    var tmp = SC_DEFAULT_OUT; // THREAD: shared var.\n    SC_DEFAULT_OUT = p;\n    return thunk();\n  } finally {\n    SC_DEFAULT_OUT = tmp;\n  }\n}\n\n/*** META ((export #t)) */\nfunction sc_withOutputToString(thunk) {\n  var p = new sc_StringOutputPort();\n  sc_withOutputToPort(p, thunk);\n  return p.close();\n}\n\n/*** META ((export #t)) */\nfunction sc_withOutputToProcedure(proc, thunk) {\n  var t = function (s) {\n    proc(sc_jsstring2string(s));\n  };\n  return sc_withOutputToPort(new sc_GenericOutputPort(t), thunk);\n}\n\n/*** META ((export #t)\n (peephole (hole 0 \"new sc_StringOutputPort()\")))\n */\nfunction sc_openOutputString() {\n  return new sc_StringOutputPort();\n}\n\n/*** META ((export #t)) */\nfunction sc_openInputString(str) {\n  return new sc_StringInputPort(sc_string2jsstring(str));\n}\n\n/* ----------------------------------------------------------------------------*/\n\nfunction sc_OutputPort() {\n}\n\nsc_OutputPort.prototype = new sc_Port();\nsc_OutputPort.prototype.appendJSString = function (obj) {\n  /* do nothing */\n}\nsc_OutputPort.prototype.close = function () {\n  /* do nothing */\n}\n\nfunction sc_StringOutputPort() {\n  this.res = \"\";\n}\n\nsc_StringOutputPort.prototype = new sc_OutputPort();\nsc_StringOutputPort.prototype.appendJSString = function (s) {\n  this.res += s;\n}\nsc_StringOutputPort.prototype.close = function () {\n  return sc_jsstring2string(this.res);\n}\n\n/*** META ((export #t)) */\nfunction sc_getOutputString(sp) {\n  return sc_jsstring2string(sp.res);\n}\n\n\nfunction sc_ErrorOutputPort() {\n}\n\nsc_ErrorOutputPort.prototype = new sc_OutputPort();\nsc_ErrorOutputPort.prototype.appendJSString = function (s) {\n  throw \"don't write on ErrorPort!\";\n}\nsc_ErrorOutputPort.prototype.close = function () {\n  /* do nothing */\n}\n\nfunction sc_GenericOutputPort(appendJSString, close) {\n  this.appendJSString = appendJSString;\n  if (close)\n    this.close = close;\n}\n\nsc_GenericOutputPort.prototype = new sc_OutputPort();\n\n/*** META ((export #t)\n (type bool)\n (peephole (postfix \" instanceof sc_OutputPort\")))\n */\nfunction sc_isOutputPort(o) {\n  return (o instanceof sc_OutputPort);\n}\n\n/*** META ((export #t)\n (peephole (postfix \".close()\")))\n */\nfunction sc_closeOutputPort(p) {\n  return p.close();\n}\n\n/* ------------------ write ---------------------------------------------------*/\n\n/*** META ((export #t)) */\nfunction sc_write(o, p) {\n  if (p === undefined) // we assume not given\n    p = SC_DEFAULT_OUT;\n  p.appendJSString(sc_toWriteString(o));\n}\n\nfunction sc_toWriteString(o) {\n  if (o === null)\n    return \"()\";\n  else if (o === true)\n    return \"#t\";\n  else if (o === false)\n    return \"#f\";\n  else if (o === undefined)\n    return \"#unspecified\";\n  else if (typeof o === 'function')\n    return \"#<procedure \" + sc_hash(o) + \">\";\n  else if (o.sc_toWriteString)\n    return o.sc_toWriteString();\n  else\n    return o.toString();\n}\n\nfunction sc_escapeWriteString(s) {\n  var res = \"\";\n  var j = 0;\n  for (i = 0; i < s.length; i++) {\n    switch (s.charAt(i)) {\n      case \"\\0\":\n        res += s.substring(j, i) + \"\\\\0\";\n        j = i + 1;\n        break;\n      case \"\\b\":\n        res += s.substring(j, i) + \"\\\\b\";\n        j = i + 1;\n        break;\n      case \"\\f\":\n        res += s.substring(j, i) + \"\\\\f\";\n        j = i + 1;\n        break;\n      case \"\\n\":\n        res += s.substring(j, i) + \"\\\\n\";\n        j = i + 1;\n        break;\n      case \"\\r\":\n        res += s.substring(j, i) + \"\\\\r\";\n        j = i + 1;\n        break;\n      case \"\\t\":\n        res += s.substring(j, i) + \"\\\\t\";\n        j = i + 1;\n        break;\n      case \"\\v\":\n        res += s.substring(j, i) + \"\\\\v\";\n        j = i + 1;\n        break;\n      case '\"':\n        res += s.substring(j, i) + '\\\\\"';\n        j = i + 1;\n        break;\n      case \"\\\\\":\n        res += s.substring(j, i) + \"\\\\\\\\\";\n        j = i + 1;\n        break;\n      default:\n        var c = s.charAt(i);\n        if (\"\\a\" !== \"a\" && c == \"\\a\") {\n          res += s.substring(j, i) + \"\\\\a\";\n          j = i + 1;\n          continue;\n        }\n        if (\"\\v\" !== \"v\" && c == \"\\v\") {\n          res += s.substring(j, i) + \"\\\\v\";\n          j = i + 1;\n          continue;\n        }\n        //if (s.charAt(i) < ' ' || s.charCodeAt(i) > 127) {\n        // CARE: Manuel is this OK with HOP?\n        if (s.charAt(i) < ' ') {\n          /* non printable character and special chars */\n          res += s.substring(j, i) + \"\\\\x\" + s.charCodeAt(i).toString(16);\n          j = i + 1;\n        }\n      // else just let i increase...\n    }\n  }\n  res += s.substring(j, i);\n  return res;\n}\n\n/* ------------------ display ---------------------------------------------------*/\n\n/*** META ((export #t)) */\nfunction sc_display(o, p) {\n  if (p === undefined) // we assume not given\n    p = SC_DEFAULT_OUT;\n  p.appendJSString(sc_toDisplayString(o));\n}\n\nfunction sc_toDisplayString(o) {\n  if (o === null)\n    return \"()\";\n  else if (o === true)\n    return \"#t\";\n  else if (o === false)\n    return \"#f\";\n  else if (o === undefined)\n    return \"#unspecified\";\n  else if (typeof o === 'function')\n    return \"#<procedure \" + sc_hash(o) + \">\";\n  else if (o.sc_toDisplayString)\n    return o.sc_toDisplayString();\n  else\n    return o.toString();\n}\n\n/* ------------------ newline ---------------------------------------------------*/\n\n/*** META ((export #t)) */\nfunction sc_newline(p) {\n  if (p === undefined) // we assume not given\n    p = SC_DEFAULT_OUT;\n  p.appendJSString(\"\\n\");\n}\n\n/* ------------------ write-char ---------------------------------------------------*/\n\n/*** META ((export #t)) */\nfunction sc_writeChar(c, p) {\n  if (p === undefined) // we assume not given\n    p = SC_DEFAULT_OUT;\n  p.appendJSString(c.val);\n}\n\n/* ------------------ write-circle ---------------------------------------------------*/\n\n/*** META ((export #t)) */\nfunction sc_writeCircle(o, p) {\n  if (p === undefined) // we assume not given\n    p = SC_DEFAULT_OUT;\n  p.appendJSString(sc_toWriteCircleString(o));\n}\n\nfunction sc_toWriteCircleString(o) {\n  var symb = sc_gensym(\"writeCircle\");\n  var nbPointer = new Object();\n  nbPointer.nb = 0;\n  sc_prepWriteCircle(o, symb, nbPointer);\n  return sc_genToWriteCircleString(o, symb);\n}\n\nfunction sc_prepWriteCircle(o, symb, nbPointer) {\n  // TODO sc_Struct\n  if (o instanceof sc_Pair ||\n    o instanceof sc_Vector) {\n    if (o[symb] !== undefined) {\n      // not the first visit.\n      o[symb]++;\n      // unless there is already a number, assign one.\n      if (!o[symb + \"nb\"]) o[symb + \"nb\"] = nbPointer.nb++;\n      return;\n    }\n    o[symb] = 0;\n    if (o instanceof sc_Pair) {\n      sc_prepWriteCircle(o.car, symb, nbPointer);\n      sc_prepWriteCircle(o.cdr, symb, nbPointer);\n    } else {\n      for (var i = 0; i < o.length; i++)\n        sc_prepWriteCircle(o[i], symb, nbPointer);\n    }\n  }\n}\n\nfunction sc_genToWriteCircleString(o, symb) {\n  if (!(o instanceof sc_Pair ||\n    o instanceof sc_Vector))\n    return sc_toWriteString(o);\n  return o.sc_toWriteCircleString(symb);\n}\n\nsc_Pair.prototype.sc_toWriteCircleString = function (symb, inList) {\n  if (this[symb + \"use\"]) { // use-flag is set. Just use it.\n    var nb = this[symb + \"nb\"];\n    if (this[symb]-- === 0) { // if we are the last use. remove all fields.\n      delete this[symb];\n      delete this[symb + \"nb\"];\n      delete this[symb + \"use\"];\n    }\n    if (inList)\n      return '. #' + nb + '#';\n    else\n      return '#' + nb + '#';\n  }\n  if (this[symb]-- === 0) { // if we are the last use. remove all fields.\n    delete this[symb];\n    delete this[symb + \"nb\"];\n    delete this[symb + \"use\"];\n  }\n\n  var res = \"\";\n\n  if (this[symb] !== undefined) { // implies > 0\n    this[symb + \"use\"] = true;\n    if (inList)\n      res += '. #' + this[symb + \"nb\"] + '=';\n    else\n      res += '#' + this[symb + \"nb\"] + '=';\n    inList = false;\n  }\n\n  if (!inList)\n    res += \"(\";\n\n  // print car\n  res += sc_genToWriteCircleString(this.car, symb);\n\n  if (sc_isPair(this.cdr)) {\n    res += \" \" + this.cdr.sc_toWriteCircleString(symb, true);\n  } else if (this.cdr !== null) {\n    res += \" . \" + sc_genToWriteCircleString(this.cdr, symb);\n  }\n  if (!inList)\n    res += \")\";\n  return res;\n};\nsc_Vector.prototype.sc_toWriteCircleString = function (symb) {\n  if (this[symb + \"use\"]) { // use-flag is set. Just use it.\n    var nb = this[symb + \"nb\"];\n    if (this[symb]-- === 0) { // if we are the last use. remove all fields.\n      delete this[symb];\n      delete this[symb + \"nb\"];\n      delete this[symb + \"use\"];\n    }\n    return '#' + nb + '#';\n  }\n  if (this[symb]-- === 0) { // if we are the last use. remove all fields.\n    delete this[symb];\n    delete this[symb + \"nb\"];\n    delete this[symb + \"use\"];\n  }\n\n  var res = \"\";\n  if (this[symb] !== undefined) { // implies > 0\n    this[symb + \"use\"] = true;\n    res += '#' + this[symb + \"nb\"] + '=';\n  }\n  res += \"#(\";\n  for (var i = 0; i < this.length; i++) {\n    res += sc_genToWriteCircleString(this[i], symb);\n    if (i < this.length - 1) res += \" \";\n  }\n  res += \")\";\n  return res;\n};\n\n\n/* ------------------ print ---------------------------------------------------*/\n\n/*** META ((export #t)) */\nfunction sc_print(s) {\n  if (arguments.length === 1) {\n    sc_display(s);\n    sc_newline();\n  } else {\n    for (var i = 0; i < arguments.length; i++)\n      sc_display(arguments[i]);\n    sc_newline();\n  }\n}\n\n/* ------------------ format ---------------------------------------------------*/\n/*** META ((export #t)) */\nfunction sc_format(s, args) {\n  var len = s.length;\n  var p = new sc_StringOutputPort();\n  var i = 0, j = 1;\n\n  while (i < len) {\n    var i2 = s.indexOf(\"~\", i);\n\n    if (i2 == -1) {\n      p.appendJSString(s.substring(i, len));\n      return p.close();\n    } else {\n      if (i2 > i) {\n        if (i2 == (len - 1)) {\n          p.appendJSString(s.substring(i, len));\n          return p.close();\n        } else {\n          p.appendJSString(s.substring(i, i2));\n          i = i2;\n        }\n      }\n\n      switch (s.charCodeAt(i2 + 1)) {\n        case 65:\n        case 97:\n          // a\n          sc_display(arguments[j], p);\n          i += 2;\n          j++;\n          break;\n\n        case 83:\n        case 115:\n          // s\n          sc_write(arguments[j], p);\n          i += 2;\n          j++;\n          break;\n\n        case 86:\n        case 118:\n          // v\n          sc_display(arguments[j], p);\n          p.appendJSString(\"\\n\");\n          i += 2;\n          j++;\n          break;\n\n        case 67:\n        case 99:\n          // c\n          p.appendJSString(String.fromCharCode(arguments[j]));\n          i += 2;\n          j++;\n          break;\n\n        case 88:\n        case 120:\n          // x\n          p.appendJSString(arguments[j].toString(6));\n          i += 2;\n          j++;\n          break;\n\n        case 79:\n        case 111:\n          // o\n          p.appendJSString(arguments[j].toString(8));\n          i += 2;\n          j++;\n          break;\n\n        case 66:\n        case 98:\n          // b\n          p.appendJSString(arguments[j].toString(2));\n          i += 2;\n          j++;\n          break;\n\n        case 37:\n        case 110:\n          // %, n\n          p.appendJSString(\"\\n\");\n          i += 2;\n          break;\n\n        case 114:\n          // r\n          p.appendJSString(\"\\r\");\n          i += 2;\n          break;\n\n        case 126:\n          // ~\n          p.appendJSString(\"~\");\n          i += 2;\n          break;\n\n        default:\n          sc_error(\"format: illegal ~\"\n            + String.fromCharCode(s.charCodeAt(i2 + 1))\n            + \" sequence\");\n          return \"\";\n      }\n    }\n  }\n\n  return p.close();\n}\n\n/* ------------------ global ports ---------------------------------------------------*/\n\nvar SC_DEFAULT_IN = new sc_ErrorInputPort();\nvar SC_DEFAULT_OUT = new sc_ErrorOutputPort();\nvar SC_ERROR_OUT = new sc_ErrorOutputPort();\n\nvar sc_SYMBOL_PREFIX = \"\\u1E9C\";\nvar sc_KEYWORD_PREFIX = \"\\u1E9D\";\n\n/*** META ((export #t)\n (peephole (id))) */\nfunction sc_jsstring2string(s) {\n  return s;\n}\n\n/*** META ((export #t)\n (peephole (prefix \"'\\\\u1E9C' +\")))\n */\nfunction sc_jsstring2symbol(s) {\n  return sc_SYMBOL_PREFIX + s;\n}\n\n/*** META ((export #t)\n (peephole (id)))\n */\nfunction sc_string2jsstring(s) {\n  return s;\n}\n\n/*** META ((export #t)\n (peephole (symbol2jsstring_immutable)))\n */\nfunction sc_symbol2jsstring(s) {\n  return s.slice(1);\n}\n\n/*** META ((export #t)\n (peephole (postfix \".slice(1)\")))\n */\nfunction sc_keyword2jsstring(k) {\n  return k.slice(1);\n}\n\n/*** META ((export #t)\n (peephole (prefix \"'\\\\u1E9D' +\")))\n */\nfunction sc_jsstring2keyword(s) {\n  return sc_KEYWORD_PREFIX + s;\n}\n\n/*** META ((export #t)\n (type bool))\n */\nfunction sc_isKeyword(s) {\n  return (typeof s === \"string\") &&\n    (s.charAt(0) === sc_KEYWORD_PREFIX);\n}\n\n\n/*** META ((export #t)) */\nvar sc_gensym = function () {\n  var counter = 1000;\n  return function (sym) {\n    counter++;\n    if (!sym) sym = sc_SYMBOL_PREFIX;\n    return sym + \"s\" + counter + \"~\" + \"^sC-GeNsYm \";\n  };\n}();\n\n\n/*** META ((export #t)\n (type bool))\n */\nfunction sc_isEqual(o1, o2) {\n  return ((o1 === o2) ||\n    (sc_isPair(o1) && sc_isPair(o2)\n      && sc_isPairEqual(o1, o2, sc_isEqual)) ||\n    (sc_isVector(o1) && sc_isVector(o2)\n      && sc_isVectorEqual(o1, o2, sc_isEqual)));\n}\n\n/*** META ((export number->symbol integer->symbol)) */\nfunction sc_number2symbol(x, radix) {\n  return sc_SYMBOL_PREFIX + sc_number2jsstring(x, radix);\n}\n\n/*** META ((export number->string integer->string)) */\nvar sc_number2string = sc_number2jsstring;\n\n/*** META ((export #t)) */\nfunction sc_symbol2number(s, radix) {\n  return sc_jsstring2number(s.slice(1), radix);\n}\n\n/*** META ((export #t)) */\nvar sc_string2number = sc_jsstring2number;\n\n/*** META ((export #t)\n (peephole (prefix \"+\" s)))\n ;; peephole will only apply if no radix is given.\n */\nfunction sc_string2integer(s, radix) {\n  if (!radix) return +s;\n  return parseInt(s, radix);\n}\n\n/*** META ((export #t)\n (peephole (prefix \"+\")))\n */\nfunction sc_string2real(s) {\n  return +s;\n}\n\n\n/*** META ((export #t)\n (type bool))\n */\nfunction sc_isSymbol(s) {\n  return (typeof s === \"string\") &&\n    (s.charAt(0) === sc_SYMBOL_PREFIX);\n}\n\n/*** META ((export #t)\n (peephole (symbol2string_immutable)))\n */\nfunction sc_symbol2string(s) {\n  return s.slice(1);\n}\n\n/*** META ((export #t)\n (peephole (prefix \"'\\\\u1E9C' +\")))\n */\nfunction sc_string2symbol(s) {\n  return sc_SYMBOL_PREFIX + s;\n}\n\n/*** META ((export symbol-append)\n (peephole (symbolAppend_immutable)))\n */\nfunction sc_symbolAppend() {\n  var res = sc_SYMBOL_PREFIX;\n  for (var i = 0; i < arguments.length; i++)\n    res += arguments[i].slice(1);\n  return res;\n}\n\n/*** META ((export #t)\n (peephole (postfix \".val\")))\n */\nfunction sc_char2string(c) {\n  return c.val;\n}\n\n/*** META ((export #t)\n (peephole (hole 1 \"'\\\\u1E9C' + \" c \".val\")))\n */\nfunction sc_char2symbol(c) {\n  return sc_SYMBOL_PREFIX + c.val;\n}\n\n/*** META ((export #t)\n (type bool))\n */\nfunction sc_isString(s) {\n  return (typeof s === \"string\") &&\n    (s.charAt(0) !== sc_SYMBOL_PREFIX);\n}\n\n/*** META ((export #t)) */\nvar sc_makeString = sc_makejsString;\n\n\n/*** META ((export #t)) */\nfunction sc_string() {\n  for (var i = 0; i < arguments.length; i++)\n    arguments[i] = arguments[i].val;\n  return \"\".concat.apply(\"\", arguments);\n}\n\n/*** META ((export #t)\n (peephole (postfix \".length\")))\n */\nfunction sc_stringLength(s) {\n  return s.length;\n}\n\n/*** META ((export #t)) */\nfunction sc_stringRef(s, k) {\n  return new sc_Char(s.charAt(k));\n}\n\n/* there's no stringSet in the immutable version\nfunction sc_stringSet(s, k, c)\n*/\n\n\n/*** META ((export string=?)\n (type bool)\n (peephole (hole 2 str1 \" === \" str2)))\n */\nfunction sc_isStringEqual(s1, s2) {\n  return s1 === s2;\n}\n\n/*** META ((export string<?)\n (type bool)\n (peephole (hole 2 str1 \" < \" str2)))\n */\nfunction sc_isStringLess(s1, s2) {\n  return s1 < s2;\n}\n\n/*** META ((export string>?)\n (type bool)\n (peephole (hole 2 str1 \" > \" str2)))\n */\nfunction sc_isStringGreater(s1, s2) {\n  return s1 > s2;\n}\n\n/*** META ((export string<=?)\n (type bool)\n (peephole (hole 2 str1 \" <= \" str2)))\n */\nfunction sc_isStringLessEqual(s1, s2) {\n  return s1 <= s2;\n}\n\n/*** META ((export string>=?)\n (type bool)\n (peephole (hole 2 str1 \" >= \" str2)))\n */\nfunction sc_isStringGreaterEqual(s1, s2) {\n  return s1 >= s2;\n}\n\n/*** META ((export string-ci=?)\n (type bool)\n (peephole (hole 2 str1 \".toLowerCase() === \" str2 \".toLowerCase()\")))\n */\nfunction sc_isStringCIEqual(s1, s2) {\n  return s1.toLowerCase() === s2.toLowerCase();\n}\n\n/*** META ((export string-ci<?)\n (type bool)\n (peephole (hole 2 str1 \".toLowerCase() < \" str2 \".toLowerCase()\")))\n */\nfunction sc_isStringCILess(s1, s2) {\n  return s1.toLowerCase() < s2.toLowerCase();\n}\n\n/*** META ((export string-ci>?)\n (type bool)\n (peephole (hole 2 str1 \".toLowerCase() > \" str2 \".toLowerCase()\")))\n */\nfunction sc_isStringCIGreater(s1, s2) {\n  return s1.toLowerCase() > s2.toLowerCase();\n}\n\n/*** META ((export string-ci<=?)\n (type bool)\n (peephole (hole 2 str1 \".toLowerCase() <= \" str2 \".toLowerCase()\")))\n */\nfunction sc_isStringCILessEqual(s1, s2) {\n  return s1.toLowerCase() <= s2.toLowerCase();\n}\n\n/*** META ((export string-ci>=?)\n (type bool)\n (peephole (hole 2 str1 \".toLowerCase() >= \" str2 \".toLowerCase()\")))\n */\nfunction sc_isStringCIGreaterEqual(s1, s2) {\n  return s1.toLowerCase() >= s2.toLowerCase();\n}\n\n/*** META ((export #t)\n (peephole (hole 3 s \".substring(\" start \", \" end \")\")))\n */\nfunction sc_substring(s, start, end) {\n  return s.substring(start, end);\n}\n\n/*** META ((export #t))\n */\nfunction sc_isSubstring_at(s1, s2, i) {\n  return s2 == s1.substring(i, i + s2.length);\n}\n\n/*** META ((export #t)\n (peephole (infix 0 #f \"+\" \"''\")))\n */\nfunction sc_stringAppend() {\n  return \"\".concat.apply(\"\", arguments);\n}\n\n/*** META ((export #t)) */\nvar sc_string2list = sc_jsstring2list;\n\n/*** META ((export #t)) */\nvar sc_list2string = sc_list2jsstring;\n\n/*** META ((export #t)\n (peephole (id)))\n */\nfunction sc_stringCopy(s) {\n  return s;\n}\n\n/* there's no string-fill in the immutable version\nfunction sc_stringFill(s, c)\n*/\n\n/*** META ((export #t)\n (peephole (postfix \".slice(1)\")))\n */\nfunction sc_keyword2string(o) {\n  return o.slice(1);\n}\n\n/*** META ((export #t)\n (peephole (prefix \"'\\\\u1E9D' +\")))\n */\nfunction sc_string2keyword(o) {\n  return sc_KEYWORD_PREFIX + o;\n}\n\nString.prototype.sc_toDisplayString = function () {\n  if (this.charAt(0) === sc_SYMBOL_PREFIX)\n    // TODO: care for symbols with spaces (escape-chars symbols).\n    return this.slice(1);\n  else if (this.charAt(0) === sc_KEYWORD_PREFIX)\n    return \":\" + this.slice(1);\n  else\n    return this.toString();\n};\n\nString.prototype.sc_toWriteString = function () {\n  if (this.charAt(0) === sc_SYMBOL_PREFIX)\n    // TODO: care for symbols with spaces (escape-chars symbols).\n    return this.slice(1);\n  else if (this.charAt(0) === sc_KEYWORD_PREFIX)\n    return \":\" + this.slice(1);\n  else\n    return '\"' + sc_escapeWriteString(this) + '\"';\n};\n/* Exported Variables */\nvar BgL_testzd2boyerzd2;\nvar BgL_nboyerzd2benchmarkzd2;\nvar BgL_setupzd2boyerzd2;\n/* End Exports */\n\nvar translate_term_nboyer;\nvar translate_args_nboyer;\nvar untranslate_term_nboyer;\nvar BgL_sc_symbolzd2ze3symbolzd2record_1ze3_nboyer;\nvar BgL_sc_za2symbolzd2recordszd2alistza2_2z00_nboyer;\nvar translate_alist_nboyer;\nvar apply_subst_nboyer;\nvar apply_subst_lst_nboyer;\nvar tautologyp_nboyer;\nvar if_constructor_nboyer;\nvar rewrite_count_nboyer;\nvar rewrite_nboyer;\nvar rewrite_args_nboyer;\nvar unify_subst_nboyer;\nvar one_way_unify1_nboyer;\nvar false_term_nboyer;\nvar true_term_nboyer;\nvar trans_of_implies1_nboyer;\nvar is_term_equal_nboyer;\nvar is_term_member_nboyer;\nvar const_nboyer;\nvar sc_const_3_nboyer;\nvar sc_const_4_nboyer;\n{\n  (sc_const_4_nboyer = (new sc_Pair(\"\\u1E9Cimplies\", (new sc_Pair((new sc_Pair(\"\\u1E9Cand\", (new sc_Pair((new sc_Pair(\"\\u1E9Cimplies\", (new sc_Pair(\"\\u1E9Cx\", (new sc_Pair(\"\\u1E9Cy\", null)))))), (new sc_Pair((new sc_Pair(\"\\u1E9Cand\", (new sc_Pair((new sc_Pair(\"\\u1E9Cimplies\", (new sc_Pair(\"\\u1E9Cy\", (new sc_Pair(\"\\u1E9Cz\", null)))))), (new sc_Pair((new sc_Pair(\"\\u1E9Cand\", (new sc_Pair((new sc_Pair(\"\\u1E9Cimplies\", (new sc_Pair(\"\\u1E9Cz\", (new sc_Pair(\"\\u1E9Cu\", null)))))), (new sc_Pair((new sc_Pair(\"\\u1E9Cimplies\", (new sc_Pair(\"\\u1E9Cu\", (new sc_Pair(\"\\u1E9Cw\", null)))))), null)))))), null)))))), null)))))), (new sc_Pair((new sc_Pair(\"\\u1E9Cimplies\", (new sc_Pair(\"\\u1E9Cx\", (new sc_Pair(\"\\u1E9Cw\", null)))))), null)))))));\n  (sc_const_3_nboyer = sc_list((new sc_Pair(\"\\u1E9Cequal\", (new sc_Pair((new sc_Pair(\"\\u1E9Ccompile\", (new sc_Pair(\"\\u1E9Cform\", null)))), (new sc_Pair((new sc_Pair(\"\\u1E9Creverse\", (new sc_Pair((new sc_Pair(\"\\u1E9Ccodegen\", (new sc_Pair((new sc_Pair(\"\\u1E9Coptimize\", (new sc_Pair(\"\\u1E9Cform\", null)))), (new sc_Pair((new sc_Pair(\"\\u1E9Cnil\", null)), null)))))), null)))), null)))))), (new sc_Pair(\"\\u1E9Cequal\", (new sc_Pair((new sc_Pair(\"\\u1E9Ceqp\", (new sc_Pair(\"\\u1E9Cx\", (new sc_Pair(\"\\u1E9Cy\", null)))))), (new sc_Pair((new sc_Pair(\"\\u1E9Cequal\", (new sc_Pair((new sc_Pair(\"\\u1E9Cfix\", (new sc_Pair(\"\\u1E9Cx\", null)))), (new sc_Pair((new sc_Pair(\"\\u1E9Cfix\", (new sc_Pair(\"\\u1E9Cy\", null)))), null)))))), null)))))), (new sc_Pair(\"\\u1E9Cequal\", (new sc_Pair((new sc_Pair(\"\\u1E9Cgreaterp\", (new sc_Pair(\"\\u1E9Cx\", (new sc_Pair(\"\\u1E9Cy\", null)))))), (new sc_Pair((new sc_Pair(\"\\u1E9Clessp\", (new sc_Pair(\"\\u1E9Cy\", (new sc_Pair(\"\\u1E9Cx\", null)))))), null)))))), (new sc_Pair(\"\\u1E9Cequal\", (new sc_Pair((new sc_Pair(\"\\u1E9Clesseqp\", (new sc_Pair(\"\\u1E9Cx\", (new sc_Pair(\"\\u1E9Cy\", null)))))), (new sc_Pair((new sc_Pair(\"\\u1E9Cnot\", (new sc_Pair((new sc_Pair(\"\\u1E9Clessp\", (new sc_Pair(\"\\u1E9Cy\", (new sc_Pair(\"\\u1E9Cx\", null)))))), null)))), null)))))), (new sc_Pair(\"\\u1E9Cequal\", (new sc_Pair((new sc_Pair(\"\\u1E9Cgreatereqp\", (new sc_Pair(\"\\u1E9Cx\", (new sc_Pair(\"\\u1E9Cy\", null)))))), (new sc_Pair((new sc_Pair(\"\\u1E9Cnot\", (new sc_Pair((new sc_Pair(\"\\u1E9Clessp\", (new sc_Pair(\"\\u1E9Cx\", (new sc_Pair(\"\\u1E9Cy\", null)))))), null)))), null)))))), (new sc_Pair(\"\\u1E9Cequal\", (new sc_Pair((new sc_Pair(\"\\u1E9Cboolean\", (new sc_Pair(\"\\u1E9Cx\", null)))), (new sc_Pair((new sc_Pair(\"\\u1E9Cor\", (new sc_Pair((new sc_Pair(\"\\u1E9Cequal\", (new sc_Pair(\"\\u1E9Cx\", (new sc_Pair((new sc_Pair(\"\\u1E9Ct\", null)), null)))))), (new sc_Pair((new sc_Pair(\"\\u1E9Cequal\", (new sc_Pair(\"\\u1E9Cx\", (new sc_Pair((new sc_Pair(\"\\u1E9Cf\", null)), null)))))), null)))))), null)))))), (new sc_Pair(\"\\u1E9Cequal\", (new sc_Pair((new sc_Pair(\"\\u1E9Ciff\", (new sc_Pair(\"\\u1E9Cx\", (new sc_Pair(\"\\u1E9Cy\", null)))))), (new sc_Pair((new sc_Pair(\"\\u1E9Cand\", (new sc_Pair((new sc_Pair(\"\\u1E9Cimplies\", (new sc_Pair(\"\\u1E9Cx\", (new sc_Pair(\"\\u1E9Cy\", null)))))), (new sc_Pair((new sc_Pair(\"\\u1E9Cimplies\", (new sc_Pair(\"\\u1E9Cy\", (new sc_Pair(\"\\u1E9Cx\", null)))))), null)))))), null)))))), (new sc_Pair(\"\\u1E9Cequal\", (new sc_Pair((new sc_Pair(\"\\u1E9Ceven1\", (new sc_Pair(\"\\u1E9Cx\", null)))), (new sc_Pair((new sc_Pair(\"\\u1E9Cif\", (new sc_Pair((new sc_Pair(\"\\u1E9Czerop\", (new sc_Pair(\"\\u1E9Cx\", null)))), (new sc_Pair((new sc_Pair(\"\\u1E9Ct\", null)), (new sc_Pair((new sc_Pair(\"\\u1E9Codd\", (new sc_Pair((new sc_Pair(\"\\u1E9Csub1\", (new sc_Pair(\"\\u1E9Cx\", null)))), null)))), null)))))))), null)))))), (new sc_Pair(\"\\u1E9Cequal\", (new sc_Pair((new sc_Pair(\"\\u1E9Ccountps-\", (new sc_Pair(\"\\u1E9Cl\", (new sc_Pair(\"\\u1E9Cpred\", null)))))), (new sc_Pair((new sc_Pair(\"\\u1E9Ccountps-loop\", (new sc_Pair(\"\\u1E9Cl\", (new sc_Pair(\"\\u1E9Cpred\", (new sc_Pair((new sc_Pair(\"\\u1E9Czero\", null)), null)))))))), null)))))), (new sc_Pair(\"\\u1E9Cequal\", (new sc_Pair((new sc_Pair(\"\\u1E9Cfact-\", (new sc_Pair(\"\\u1E9Ci\", null)))), (new sc_Pair((new sc_Pair(\"\\u1E9Cfact-loop\", (new sc_Pair(\"\\u1E9Ci\", (new sc_Pair((1), null)))))), null)))))), (new sc_Pair(\"\\u1E9Cequal\", (new sc_Pair((new sc_Pair(\"\\u1E9Creverse-\", (new sc_Pair(\"\\u1E9Cx\", null)))), (new sc_Pair((new sc_Pair(\"\\u1E9Creverse-loop\", (new sc_Pair(\"\\u1E9Cx\", (new sc_Pair((new sc_Pair(\"\\u1E9Cnil\", null)), null)))))), null)))))), (new sc_Pair(\"\\u1E9Cequal\", (new sc_Pair((new sc_Pair(\"\\u1E9Cdivides\", (new sc_Pair(\"\\u1E9Cx\", (new sc_Pair(\"\\u1E9Cy\", null)))))), (new sc_Pair((new sc_Pair(\"\\u1E9Czerop\", (new sc_Pair((new sc_Pair(\"\\u1E9Cremainder\", (new sc_Pair(\"\\u1E9Cy\", (new sc_Pair(\"\\u1E9Cx\", null)))))), null)))), null)))))), (new sc_Pair(\"\\u1E9Cequal\", (new sc_Pair((new sc_Pair(\"\\u1E9Cassume-true\", (new sc_Pair(\"\\u1E9Cvar\", (new sc_Pair(\"\\u1E9Calist\", null)))))), (new sc_Pair((new sc_Pair(\"\\u1E9Ccons\", (new sc_Pair((new sc_Pair(\"\\u1E9Ccons\", (new sc_Pair(\"\\u1E9Cvar\", (new sc_Pair((new sc_Pair(\"\\u1E9Ct\", null)), null)))))), (new sc_Pair(\"\\u1E9Calist\", null)))))), null)))))), (new sc_Pair(\"\\u1E9Cequal\", (new sc_Pair((new sc_Pair(\"\\u1E9Cassume-false\", (new sc_Pair(\"\\u1E9Cvar\", (new sc_Pair(\"\\u1E9Calist\", null)))))), (new sc_Pair((new sc_Pair(\"\\u1E9Ccons\", (new sc_Pair((new sc_Pair(\"\\u1E9Ccons\", (new sc_Pair(\"\\u1E9Cvar\", (new sc_Pair((new sc_Pair(\"\\u1E9Cf\", null)), null)))))), (new sc_Pair(\"\\u1E9Calist\", null)))))), null)))))), (new sc_Pair(\"\\u1E9Cequal\", (new sc_Pair((new sc_Pair(\"\\u1E9Ctautology-checker\", (new sc_Pair(\"\\u1E9Cx\", null)))), (new sc_Pair((new sc_Pair(\"\\u1E9Ctautologyp\", (new sc_Pair((new sc_Pair(\"\\u1E9Cnormalize\", (new sc_Pair(\"\\u1E9Cx\", null)))), (new sc_Pair((new sc_Pair(\"\\u1E9Cnil\", null)), null)))))), null)))))), (new sc_Pair(\"\\u1E9Cequal\", (new sc_Pair((new sc_Pair(\"\\u1E9Cfalsify\", (new sc_Pair(\"\\u1E9Cx\", null)))), (new sc_Pair((new sc_Pair(\"\\u1E9Cfalsify1\", (new sc_Pair((new sc_Pair(\"\\u1E9Cnormalize\", (new sc_Pair(\"\\u1E9Cx\", null)))), (new sc_Pair((new sc_Pair(\"\\u1E9Cnil\", null)), null)))))), null)))))), (new sc_Pair(\"\\u1E9Cequal\", (new sc_Pair((new sc_Pair(\"\\u1E9Cprime\", (new sc_Pair(\"\\u1E9Cx\", null)))), (new sc_Pair((new sc_Pair(\"\\u1E9Cand\", (new sc_Pair((new sc_Pair(\"\\u1E9Cnot\", (new sc_Pair((new sc_Pair(\"\\u1E9Czerop\", (new sc_Pair(\"\\u1E9Cx\", null)))), null)))), (new sc_Pair((new sc_Pair(\"\\u1E9Cnot\", (new sc_Pair((new sc_Pair(\"\\u1E9Cequal\", (new sc_Pair(\"\\u1E9Cx\", (new sc_Pair((new sc_Pair(\"\\u1E9Cadd1\", (new sc_Pair((new sc_Pair(\"\\u1E9Czero\", null)), null)))), null)))))), null)))), (new sc_Pair((new sc_Pair(\"\\u1E9Cprime1\", (new sc_Pair(\"\\u1E9Cx\", (new sc_Pair((new sc_Pair(\"\\u1E9Csub1\", (new sc_Pair(\"\\u1E9Cx\", null)))), null)))))), null)))))))), null)))))), (new sc_Pair(\"\\u1E9Cequal\", (new sc_Pair((new sc_Pair(\"\\u1E9Cand\", (new sc_Pair(\"\\u1E9Cp\", (new sc_Pair(\"\\u1E9Cq\", null)))))), (new sc_Pair((new sc_Pair(\"\\u1E9Cif\", (new sc_Pair(\"\\u1E9Cp\", (new sc_Pair((new sc_Pair(\"\\u1E9Cif\", (new sc_Pair(\"\\u1E9Cq\", (new sc_Pair((new sc_Pair(\"\\u1E9Ct\", null)), (new sc_Pair((new sc_Pair(\"\\u1E9Cf\", null)), null)))))))), (new sc_Pair((new sc_Pair(\"\\u1E9Cf\", null)), null)))))))), null)))))), (new sc_Pair(\"\\u1E9Cequal\", (new sc_Pair((new sc_Pair(\"\\u1E9Cor\", (new sc_Pair(\"\\u1E9Cp\", (new sc_Pair(\"\\u1E9Cq\", null)))))), (new sc_Pair((new sc_Pair(\"\\u1E9Cif\", (new sc_Pair(\"\\u1E9Cp\", (new sc_Pair((new sc_Pair(\"\\u1E9Ct\", null)), (new sc_Pair((new sc_Pair(\"\\u1E9Cif\", (new sc_Pair(\"\\u1E9Cq\", (new sc_Pair((new sc_Pair(\"\\u1E9Ct\", null)), (new sc_Pair((new sc_Pair(\"\\u1E9Cf\", null)), null)))))))), null)))))))), null)))))), (new sc_Pair(\"\\u1E9Cequal\", (new sc_Pair((new sc_Pair(\"\\u1E9Cnot\", (new sc_Pair(\"\\u1E9Cp\", null)))), (new sc_Pair((new sc_Pair(\"\\u1E9Cif\", (new sc_Pair(\"\\u1E9Cp\", (new sc_Pair((new sc_Pair(\"\\u1E9Cf\", null)), (new sc_Pair((new sc_Pair(\"\\u1E9Ct\", null)), null)))))))), null)))))), (new sc_Pair(\"\\u1E9Cequal\", (new sc_Pair((new sc_Pair(\"\\u1E9Cimplies\", (new sc_Pair(\"\\u1E9Cp\", (new sc_Pair(\"\\u1E9Cq\", null)))))), (new sc_Pair((new sc_Pair(\"\\u1E9Cif\", (new sc_Pair(\"\\u1E9Cp\", (new sc_Pair((new sc_Pair(\"\\u1E9Cif\", (new sc_Pair(\"\\u1E9Cq\", (new sc_Pair((new sc_Pair(\"\\u1E9Ct\", null)), (new sc_Pair((new sc_Pair(\"\\u1E9Cf\", null)), null)))))))), (new sc_Pair((new sc_Pair(\"\\u1E9Ct\", null)), null)))))))), null)))))), (new sc_Pair(\"\\u1E9Cequal\", (new sc_Pair((new sc_Pair(\"\\u1E9Cfix\", (new sc_Pair(\"\\u1E9Cx\", null)))), (new sc_Pair((new sc_Pair(\"\\u1E9Cif\", (new sc_Pair((new sc_Pair(\"\\u1E9Cnumberp\", (new sc_Pair(\"\\u1E9Cx\", null)))), (new sc_Pair(\"\\u1E9Cx\", (new sc_Pair((new sc_Pair(\"\\u1E9Czero\", null)), null)))))))), null)))))), (new sc_Pair(\"\\u1E9Cequal\", (new sc_Pair((new sc_Pair(\"\\u1E9Cif\", (new sc_Pair((new sc_Pair(\"\\u1E9Cif\", (new sc_Pair(\"\\u1E9Ca\", (new sc_Pair(\"\\u1E9Cb\", (new sc_Pair(\"\\u1E9Cc\", null)))))))), (new sc_Pair(\"\\u1E9Cd\", (new sc_Pair(\"\\u1E9Ce\", null)))))))), (new sc_Pair((new sc_Pair(\"\\u1E9Cif\", (new sc_Pair(\"\\u1E9Ca\", (new sc_Pair((new sc_Pair(\"\\u1E9Cif\", (new sc_Pair(\"\\u1E9Cb\", (new sc_Pair(\"\\u1E9Cd\", (new sc_Pair(\"\\u1E9Ce\", null)))))))), (new sc_Pair((new sc_Pair(\"\\u1E9Cif\", (new sc_Pair(\"\\u1E9Cc\", (new sc_Pair(\"\\u1E9Cd\", (new sc_Pair(\"\\u1E9Ce\", null)))))))), null)))))))), null)))))), (new sc_Pair(\"\\u1E9Cequal\", (new sc_Pair((new sc_Pair(\"\\u1E9Czerop\", (new sc_Pair(\"\\u1E9Cx\", null)))), (new sc_Pair((new sc_Pair(\"\\u1E9Cor\", (new sc_Pair((new sc_Pair(\"\\u1E9Cequal\", (new sc_Pair(\"\\u1E9Cx\", (new sc_Pair((new sc_Pair(\"\\u1E9Czero\", null)), null)))))), (new sc_Pair((new sc_Pair(\"\\u1E9Cnot\", (new sc_Pair((new sc_Pair(\"\\u1E9Cnumberp\", (new sc_Pair(\"\\u1E9Cx\", null)))), null)))), null)))))), null)))))), (new sc_Pair(\"\\u1E9Cequal\", (new sc_Pair((new sc_Pair(\"\\u1E9Cplus\", (new sc_Pair((new sc_Pair(\"\\u1E9Cplus\", (new sc_Pair(\"\\u1E9Cx\", (new sc_Pair(\"\\u1E9Cy\", null)))))), (new sc_Pair(\"\\u1E9Cz\", null)))))), (new sc_Pair((new sc_Pair(\"\\u1E9Cplus\", (new sc_Pair(\"\\u1E9Cx\", (new sc_Pair((new sc_Pair(\"\\u1E9Cplus\", (new sc_Pair(\"\\u1E9Cy\", (new sc_Pair(\"\\u1E9Cz\", null)))))), null)))))), null)))))), (new sc_Pair(\"\\u1E9Cequal\", (new sc_Pair((new sc_Pair(\"\\u1E9Cequal\", (new sc_Pair((new sc_Pair(\"\\u1E9Cplus\", (new sc_Pair(\"\\u1E9Ca\", (new sc_Pair(\"\\u1E9Cb\", null)))))), (new sc_Pair((new sc_Pair(\"\\u1E9Czero\", null)), null)))))), (new sc_Pair((new sc_Pair(\"\\u1E9Cand\", (new sc_Pair((new sc_Pair(\"\\u1E9Czerop\", (new sc_Pair(\"\\u1E9Ca\", null)))), (new sc_Pair((new sc_Pair(\"\\u1E9Czerop\", (new sc_Pair(\"\\u1E9Cb\", null)))), null)))))), null)))))), (new sc_Pair(\"\\u1E9Cequal\", (new sc_Pair((new sc_Pair(\"\\u1E9Cdifference\", (new sc_Pair(\"\\u1E9Cx\", (new sc_Pair(\"\\u1E9Cx\", null)))))), (new sc_Pair((new sc_Pair(\"\\u1E9Czero\", null)), null)))))), (new sc_Pair(\"\\u1E9Cequal\", (new sc_Pair((new sc_Pair(\"\\u1E9Cequal\", (new sc_Pair((new sc_Pair(\"\\u1E9Cplus\", (new sc_Pair(\"\\u1E9Ca\", (new sc_Pair(\"\\u1E9Cb\", null)))))), (new sc_Pair((new sc_Pair(\"\\u1E9Cplus\", (new sc_Pair(\"\\u1E9Ca\", (new sc_Pair(\"\\u1E9Cc\", null)))))), null)))))), (new sc_Pair((new sc_Pair(\"\\u1E9Cequal\", (new sc_Pair((new sc_Pair(\"\\u1E9Cfix\", (new sc_Pair(\"\\u1E9Cb\", null)))), (new sc_Pair((new sc_Pair(\"\\u1E9Cfix\", (new sc_Pair(\"\\u1E9Cc\", null)))), null)))))), null)))))), (new sc_Pair(\"\\u1E9Cequal\", (new sc_Pair((new sc_Pair(\"\\u1E9Cequal\", (new sc_Pair((new sc_Pair(\"\\u1E9Czero\", null)), (new sc_Pair((new sc_Pair(\"\\u1E9Cdifference\", (new sc_Pair(\"\\u1E9Cx\", (new sc_Pair(\"\\u1E9Cy\", null)))))), null)))))), (new sc_Pair((new sc_Pair(\"\\u1E9Cnot\", (new sc_Pair((new sc_Pair(\"\\u1E9Clessp\", (new sc_Pair(\"\\u1E9Cy\", (new sc_Pair(\"\\u1E9Cx\", null)))))), null)))), null)))))), (new sc_Pair(\"\\u1E9Cequal\", (new sc_Pair((new sc_Pair(\"\\u1E9Cequal\", (new sc_Pair(\"\\u1E9Cx\", (new sc_Pair((new sc_Pair(\"\\u1E9Cdifference\", (new sc_Pair(\"\\u1E9Cx\", (new sc_Pair(\"\\u1E9Cy\", null)))))), null)))))), (new sc_Pair((new sc_Pair(\"\\u1E9Cand\", (new sc_Pair((new sc_Pair(\"\\u1E9Cnumberp\", (new sc_Pair(\"\\u1E9Cx\", null)))), (new sc_Pair((new sc_Pair(\"\\u1E9Cor\", (new sc_Pair((new sc_Pair(\"\\u1E9Cequal\", (new sc_Pair(\"\\u1E9Cx\", (new sc_Pair((new sc_Pair(\"\\u1E9Czero\", null)), null)))))), (new sc_Pair((new sc_Pair(\"\\u1E9Czerop\", (new sc_Pair(\"\\u1E9Cy\", null)))), null)))))), null)))))), null)))))), (new sc_Pair(\"\\u1E9Cequal\", (new sc_Pair((new sc_Pair(\"\\u1E9Cmeaning\", (new sc_Pair((new sc_Pair(\"\\u1E9Cplus-tree\", (new sc_Pair((new sc_Pair(\"\\u1E9Cappend\", (new sc_Pair(\"\\u1E9Cx\", (new sc_Pair(\"\\u1E9Cy\", null)))))), null)))), (new sc_Pair(\"\\u1E9Ca\", null)))))), (new sc_Pair((new sc_Pair(\"\\u1E9Cplus\", (new sc_Pair((new sc_Pair(\"\\u1E9Cmeaning\", (new sc_Pair((new sc_Pair(\"\\u1E9Cplus-tree\", (new sc_Pair(\"\\u1E9Cx\", null)))), (new sc_Pair(\"\\u1E9Ca\", null)))))), (new sc_Pair((new sc_Pair(\"\\u1E9Cmeaning\", (new sc_Pair((new sc_Pair(\"\\u1E9Cplus-tree\", (new sc_Pair(\"\\u1E9Cy\", null)))), (new sc_Pair(\"\\u1E9Ca\", null)))))), null)))))), null)))))), (new sc_Pair(\"\\u1E9Cequal\", (new sc_Pair((new sc_Pair(\"\\u1E9Cmeaning\", (new sc_Pair((new sc_Pair(\"\\u1E9Cplus-tree\", (new sc_Pair((new sc_Pair(\"\\u1E9Cplus-fringe\", (new sc_Pair(\"\\u1E9Cx\", null)))), null)))), (new sc_Pair(\"\\u1E9Ca\", null)))))), (new sc_Pair((new sc_Pair(\"\\u1E9Cfix\", (new sc_Pair((new sc_Pair(\"\\u1E9Cmeaning\", (new sc_Pair(\"\\u1E9Cx\", (new sc_Pair(\"\\u1E9Ca\", null)))))), null)))), null)))))), (new sc_Pair(\"\\u1E9Cequal\", (new sc_Pair((new sc_Pair(\"\\u1E9Cappend\", (new sc_Pair((new sc_Pair(\"\\u1E9Cappend\", (new sc_Pair(\"\\u1E9Cx\", (new sc_Pair(\"\\u1E9Cy\", null)))))), (new sc_Pair(\"\\u1E9Cz\", null)))))), (new sc_Pair((new sc_Pair(\"\\u1E9Cappend\", (new sc_Pair(\"\\u1E9Cx\", (new sc_Pair((new sc_Pair(\"\\u1E9Cappend\", (new sc_Pair(\"\\u1E9Cy\", (new sc_Pair(\"\\u1E9Cz\", null)))))), null)))))), null)))))), (new sc_Pair(\"\\u1E9Cequal\", (new sc_Pair((new sc_Pair(\"\\u1E9Creverse\", (new sc_Pair((new sc_Pair(\"\\u1E9Cappend\", (new sc_Pair(\"\\u1E9Ca\", (new sc_Pair(\"\\u1E9Cb\", null)))))), null)))), (new sc_Pair((new sc_Pair(\"\\u1E9Cappend\", (new sc_Pair((new sc_Pair(\"\\u1E9Creverse\", (new sc_Pair(\"\\u1E9Cb\", null)))), (new sc_Pair((new sc_Pair(\"\\u1E9Creverse\", (new sc_Pair(\"\\u1E9Ca\", null)))), null)))))), null)))))), (new sc_Pair(\"\\u1E9Cequal\", (new sc_Pair((new sc_Pair(\"\\u1E9Ctimes\", (new sc_Pair(\"\\u1E9Cx\", (new sc_Pair((new sc_Pair(\"\\u1E9Cplus\", (new sc_Pair(\"\\u1E9Cy\", (new sc_Pair(\"\\u1E9Cz\", null)))))), null)))))), (new sc_Pair((new sc_Pair(\"\\u1E9Cplus\", (new sc_Pair((new sc_Pair(\"\\u1E9Ctimes\", (new sc_Pair(\"\\u1E9Cx\", (new sc_Pair(\"\\u1E9Cy\", null)))))), (new sc_Pair((new sc_Pair(\"\\u1E9Ctimes\", (new sc_Pair(\"\\u1E9Cx\", (new sc_Pair(\"\\u1E9Cz\", null)))))), null)))))), null)))))), (new sc_Pair(\"\\u1E9Cequal\", (new sc_Pair((new sc_Pair(\"\\u1E9Ctimes\", (new sc_Pair((new sc_Pair(\"\\u1E9Ctimes\", (new sc_Pair(\"\\u1E9Cx\", (new sc_Pair(\"\\u1E9Cy\", null)))))), (new sc_Pair(\"\\u1E9Cz\", null)))))), (new sc_Pair((new sc_Pair(\"\\u1E9Ctimes\", (new sc_Pair(\"\\u1E9Cx\", (new sc_Pair((new sc_Pair(\"\\u1E9Ctimes\", (new sc_Pair(\"\\u1E9Cy\", (new sc_Pair(\"\\u1E9Cz\", null)))))), null)))))), null)))))), (new sc_Pair(\"\\u1E9Cequal\", (new sc_Pair((new sc_Pair(\"\\u1E9Cequal\", (new sc_Pair((new sc_Pair(\"\\u1E9Ctimes\", (new sc_Pair(\"\\u1E9Cx\", (new sc_Pair(\"\\u1E9Cy\", null)))))), (new sc_Pair((new sc_Pair(\"\\u1E9Czero\", null)), null)))))), (new sc_Pair((new sc_Pair(\"\\u1E9Cor\", (new sc_Pair((new sc_Pair(\"\\u1E9Czerop\", (new sc_Pair(\"\\u1E9Cx\", null)))), (new sc_Pair((new sc_Pair(\"\\u1E9Czerop\", (new sc_Pair(\"\\u1E9Cy\", null)))), null)))))), null)))))), (new sc_Pair(\"\\u1E9Cequal\", (new sc_Pair((new sc_Pair(\"\\u1E9Cexec\", (new sc_Pair((new sc_Pair(\"\\u1E9Cappend\", (new sc_Pair(\"\\u1E9Cx\", (new sc_Pair(\"\\u1E9Cy\", null)))))), (new sc_Pair(\"\\u1E9Cpds\", (new sc_Pair(\"\\u1E9Cenvrn\", null)))))))), (new sc_Pair((new sc_Pair(\"\\u1E9Cexec\", (new sc_Pair(\"\\u1E9Cy\", (new sc_Pair((new sc_Pair(\"\\u1E9Cexec\", (new sc_Pair(\"\\u1E9Cx\", (new sc_Pair(\"\\u1E9Cpds\", (new sc_Pair(\"\\u1E9Cenvrn\", null)))))))), (new sc_Pair(\"\\u1E9Cenvrn\", null)))))))), null)))))), (new sc_Pair(\"\\u1E9Cequal\", (new sc_Pair((new sc_Pair(\"\\u1E9Cmc-flatten\", (new sc_Pair(\"\\u1E9Cx\", (new sc_Pair(\"\\u1E9Cy\", null)))))), (new sc_Pair((new sc_Pair(\"\\u1E9Cappend\", (new sc_Pair((new sc_Pair(\"\\u1E9Cflatten\", (new sc_Pair(\"\\u1E9Cx\", null)))), (new sc_Pair(\"\\u1E9Cy\", null)))))), null)))))), (new sc_Pair(\"\\u1E9Cequal\", (new sc_Pair((new sc_Pair(\"\\u1E9Cmember\", (new sc_Pair(\"\\u1E9Cx\", (new sc_Pair((new sc_Pair(\"\\u1E9Cappend\", (new sc_Pair(\"\\u1E9Ca\", (new sc_Pair(\"\\u1E9Cb\", null)))))), null)))))), (new sc_Pair((new sc_Pair(\"\\u1E9Cor\", (new sc_Pair((new sc_Pair(\"\\u1E9Cmember\", (new sc_Pair(\"\\u1E9Cx\", (new sc_Pair(\"\\u1E9Ca\", null)))))), (new sc_Pair((new sc_Pair(\"\\u1E9Cmember\", (new sc_Pair(\"\\u1E9Cx\", (new sc_Pair(\"\\u1E9Cb\", null)))))), null)))))), null)))))), (new sc_Pair(\"\\u1E9Cequal\", (new sc_Pair((new sc_Pair(\"\\u1E9Cmember\", (new sc_Pair(\"\\u1E9Cx\", (new sc_Pair((new sc_Pair(\"\\u1E9Creverse\", (new sc_Pair(\"\\u1E9Cy\", null)))), null)))))), (new sc_Pair((new sc_Pair(\"\\u1E9Cmember\", (new sc_Pair(\"\\u1E9Cx\", (new sc_Pair(\"\\u1E9Cy\", null)))))), null)))))), (new sc_Pair(\"\\u1E9Cequal\", (new sc_Pair((new sc_Pair(\"\\u1E9Clength\", (new sc_Pair((new sc_Pair(\"\\u1E9Creverse\", (new sc_Pair(\"\\u1E9Cx\", null)))), null)))), (new sc_Pair((new sc_Pair(\"\\u1E9Clength\", (new sc_Pair(\"\\u1E9Cx\", null)))), null)))))), (new sc_Pair(\"\\u1E9Cequal\", (new sc_Pair((new sc_Pair(\"\\u1E9Cmember\", (new sc_Pair(\"\\u1E9Ca\", (new sc_Pair((new sc_Pair(\"\\u1E9Cintersect\", (new sc_Pair(\"\\u1E9Cb\", (new sc_Pair(\"\\u1E9Cc\", null)))))), null)))))), (new sc_Pair((new sc_Pair(\"\\u1E9Cand\", (new sc_Pair((new sc_Pair(\"\\u1E9Cmember\", (new sc_Pair(\"\\u1E9Ca\", (new sc_Pair(\"\\u1E9Cb\", null)))))), (new sc_Pair((new sc_Pair(\"\\u1E9Cmember\", (new sc_Pair(\"\\u1E9Ca\", (new sc_Pair(\"\\u1E9Cc\", null)))))), null)))))), null)))))), (new sc_Pair(\"\\u1E9Cequal\", (new sc_Pair((new sc_Pair(\"\\u1E9Cnth\", (new sc_Pair((new sc_Pair(\"\\u1E9Czero\", null)), (new sc_Pair(\"\\u1E9Ci\", null)))))), (new sc_Pair((new sc_Pair(\"\\u1E9Czero\", null)), null)))))), (new sc_Pair(\"\\u1E9Cequal\", (new sc_Pair((new sc_Pair(\"\\u1E9Cexp\", (new sc_Pair(\"\\u1E9Ci\", (new sc_Pair((new sc_Pair(\"\\u1E9Cplus\", (new sc_Pair(\"\\u1E9Cj\", (new sc_Pair(\"\\u1E9Ck\", null)))))), null)))))), (new sc_Pair((new sc_Pair(\"\\u1E9Ctimes\", (new sc_Pair((new sc_Pair(\"\\u1E9Cexp\", (new sc_Pair(\"\\u1E9Ci\", (new sc_Pair(\"\\u1E9Cj\", null)))))), (new sc_Pair((new sc_Pair(\"\\u1E9Cexp\", (new sc_Pair(\"\\u1E9Ci\", (new sc_Pair(\"\\u1E9Ck\", null)))))), null)))))), null)))))), (new sc_Pair(\"\\u1E9Cequal\", (new sc_Pair((new sc_Pair(\"\\u1E9Cexp\", (new sc_Pair(\"\\u1E9Ci\", (new sc_Pair((new sc_Pair(\"\\u1E9Ctimes\", (new sc_Pair(\"\\u1E9Cj\", (new sc_Pair(\"\\u1E9Ck\", null)))))), null)))))), (new sc_Pair((new sc_Pair(\"\\u1E9Cexp\", (new sc_Pair((new sc_Pair(\"\\u1E9Cexp\", (new sc_Pair(\"\\u1E9Ci\", (new sc_Pair(\"\\u1E9Cj\", null)))))), (new sc_Pair(\"\\u1E9Ck\", null)))))), null)))))), (new sc_Pair(\"\\u1E9Cequal\", (new sc_Pair((new sc_Pair(\"\\u1E9Creverse-loop\", (new sc_Pair(\"\\u1E9Cx\", (new sc_Pair(\"\\u1E9Cy\", null)))))), (new sc_Pair((new sc_Pair(\"\\u1E9Cappend\", (new sc_Pair((new sc_Pair(\"\\u1E9Creverse\", (new sc_Pair(\"\\u1E9Cx\", null)))), (new sc_Pair(\"\\u1E9Cy\", null)))))), null)))))), (new sc_Pair(\"\\u1E9Cequal\", (new sc_Pair((new sc_Pair(\"\\u1E9Creverse-loop\", (new sc_Pair(\"\\u1E9Cx\", (new sc_Pair((new sc_Pair(\"\\u1E9Cnil\", null)), null)))))), (new sc_Pair((new sc_Pair(\"\\u1E9Creverse\", (new sc_Pair(\"\\u1E9Cx\", null)))), null)))))), (new sc_Pair(\"\\u1E9Cequal\", (new sc_Pair((new sc_Pair(\"\\u1E9Ccount-list\", (new sc_Pair(\"\\u1E9Cz\", (new sc_Pair((new sc_Pair(\"\\u1E9Csort-lp\", (new sc_Pair(\"\\u1E9Cx\", (new sc_Pair(\"\\u1E9Cy\", null)))))), null)))))), (new sc_Pair((new sc_Pair(\"\\u1E9Cplus\", (new sc_Pair((new sc_Pair(\"\\u1E9Ccount-list\", (new sc_Pair(\"\\u1E9Cz\", (new sc_Pair(\"\\u1E9Cx\", null)))))), (new sc_Pair((new sc_Pair(\"\\u1E9Ccount-list\", (new sc_Pair(\"\\u1E9Cz\", (new sc_Pair(\"\\u1E9Cy\", null)))))), null)))))), null)))))), (new sc_Pair(\"\\u1E9Cequal\", (new sc_Pair((new sc_Pair(\"\\u1E9Cequal\", (new sc_Pair((new sc_Pair(\"\\u1E9Cappend\", (new sc_Pair(\"\\u1E9Ca\", (new sc_Pair(\"\\u1E9Cb\", null)))))), (new sc_Pair((new sc_Pair(\"\\u1E9Cappend\", (new sc_Pair(\"\\u1E9Ca\", (new sc_Pair(\"\\u1E9Cc\", null)))))), null)))))), (new sc_Pair((new sc_Pair(\"\\u1E9Cequal\", (new sc_Pair(\"\\u1E9Cb\", (new sc_Pair(\"\\u1E9Cc\", null)))))), null)))))), (new sc_Pair(\"\\u1E9Cequal\", (new sc_Pair((new sc_Pair(\"\\u1E9Cplus\", (new sc_Pair((new sc_Pair(\"\\u1E9Cremainder\", (new sc_Pair(\"\\u1E9Cx\", (new sc_Pair(\"\\u1E9Cy\", null)))))), (new sc_Pair((new sc_Pair(\"\\u1E9Ctimes\", (new sc_Pair(\"\\u1E9Cy\", (new sc_Pair((new sc_Pair(\"\\u1E9Cquotient\", (new sc_Pair(\"\\u1E9Cx\", (new sc_Pair(\"\\u1E9Cy\", null)))))), null)))))), null)))))), (new sc_Pair((new sc_Pair(\"\\u1E9Cfix\", (new sc_Pair(\"\\u1E9Cx\", null)))), null)))))), (new sc_Pair(\"\\u1E9Cequal\", (new sc_Pair((new sc_Pair(\"\\u1E9Cpower-eval\", (new sc_Pair((new sc_Pair(\"\\u1E9Cbig-plus1\", (new sc_Pair(\"\\u1E9Cl\", (new sc_Pair(\"\\u1E9Ci\", (new sc_Pair(\"\\u1E9Cbase\", null)))))))), (new sc_Pair(\"\\u1E9Cbase\", null)))))), (new sc_Pair((new sc_Pair(\"\\u1E9Cplus\", (new sc_Pair((new sc_Pair(\"\\u1E9Cpower-eval\", (new sc_Pair(\"\\u1E9Cl\", (new sc_Pair(\"\\u1E9Cbase\", null)))))), (new sc_Pair(\"\\u1E9Ci\", null)))))), null)))))), (new sc_Pair(\"\\u1E9Cequal\", (new sc_Pair((new sc_Pair(\"\\u1E9Cpower-eval\", (new sc_Pair((new sc_Pair(\"\\u1E9Cbig-plus\", (new sc_Pair(\"\\u1E9Cx\", (new sc_Pair(\"\\u1E9Cy\", (new sc_Pair(\"\\u1E9Ci\", (new sc_Pair(\"\\u1E9Cbase\", null)))))))))), (new sc_Pair(\"\\u1E9Cbase\", null)))))), (new sc_Pair((new sc_Pair(\"\\u1E9Cplus\", (new sc_Pair(\"\\u1E9Ci\", (new sc_Pair((new sc_Pair(\"\\u1E9Cplus\", (new sc_Pair((new sc_Pair(\"\\u1E9Cpower-eval\", (new sc_Pair(\"\\u1E9Cx\", (new sc_Pair(\"\\u1E9Cbase\", null)))))), (new sc_Pair((new sc_Pair(\"\\u1E9Cpower-eval\", (new sc_Pair(\"\\u1E9Cy\", (new sc_Pair(\"\\u1E9Cbase\", null)))))), null)))))), null)))))), null)))))), (new sc_Pair(\"\\u1E9Cequal\", (new sc_Pair((new sc_Pair(\"\\u1E9Cremainder\", (new sc_Pair(\"\\u1E9Cy\", (new sc_Pair((1), null)))))), (new sc_Pair((new sc_Pair(\"\\u1E9Czero\", null)), null)))))), (new sc_Pair(\"\\u1E9Cequal\", (new sc_Pair((new sc_Pair(\"\\u1E9Clessp\", (new sc_Pair((new sc_Pair(\"\\u1E9Cremainder\", (new sc_Pair(\"\\u1E9Cx\", (new sc_Pair(\"\\u1E9Cy\", null)))))), (new sc_Pair(\"\\u1E9Cy\", null)))))), (new sc_Pair((new sc_Pair(\"\\u1E9Cnot\", (new sc_Pair((new sc_Pair(\"\\u1E9Czerop\", (new sc_Pair(\"\\u1E9Cy\", null)))), null)))), null)))))), (new sc_Pair(\"\\u1E9Cequal\", (new sc_Pair((new sc_Pair(\"\\u1E9Cremainder\", (new sc_Pair(\"\\u1E9Cx\", (new sc_Pair(\"\\u1E9Cx\", null)))))), (new sc_Pair((new sc_Pair(\"\\u1E9Czero\", null)), null)))))), (new sc_Pair(\"\\u1E9Cequal\", (new sc_Pair((new sc_Pair(\"\\u1E9Clessp\", (new sc_Pair((new sc_Pair(\"\\u1E9Cquotient\", (new sc_Pair(\"\\u1E9Ci\", (new sc_Pair(\"\\u1E9Cj\", null)))))), (new sc_Pair(\"\\u1E9Ci\", null)))))), (new sc_Pair((new sc_Pair(\"\\u1E9Cand\", (new sc_Pair((new sc_Pair(\"\\u1E9Cnot\", (new sc_Pair((new sc_Pair(\"\\u1E9Czerop\", (new sc_Pair(\"\\u1E9Ci\", null)))), null)))), (new sc_Pair((new sc_Pair(\"\\u1E9Cor\", (new sc_Pair((new sc_Pair(\"\\u1E9Czerop\", (new sc_Pair(\"\\u1E9Cj\", null)))), (new sc_Pair((new sc_Pair(\"\\u1E9Cnot\", (new sc_Pair((new sc_Pair(\"\\u1E9Cequal\", (new sc_Pair(\"\\u1E9Cj\", (new sc_Pair((1), null)))))), null)))), null)))))), null)))))), null)))))), (new sc_Pair(\"\\u1E9Cequal\", (new sc_Pair((new sc_Pair(\"\\u1E9Clessp\", (new sc_Pair((new sc_Pair(\"\\u1E9Cremainder\", (new sc_Pair(\"\\u1E9Cx\", (new sc_Pair(\"\\u1E9Cy\", null)))))), (new sc_Pair(\"\\u1E9Cx\", null)))))), (new sc_Pair((new sc_Pair(\"\\u1E9Cand\", (new sc_Pair((new sc_Pair(\"\\u1E9Cnot\", (new sc_Pair((new sc_Pair(\"\\u1E9Czerop\", (new sc_Pair(\"\\u1E9Cy\", null)))), null)))), (new sc_Pair((new sc_Pair(\"\\u1E9Cnot\", (new sc_Pair((new sc_Pair(\"\\u1E9Czerop\", (new sc_Pair(\"\\u1E9Cx\", null)))), null)))), (new sc_Pair((new sc_Pair(\"\\u1E9Cnot\", (new sc_Pair((new sc_Pair(\"\\u1E9Clessp\", (new sc_Pair(\"\\u1E9Cx\", (new sc_Pair(\"\\u1E9Cy\", null)))))), null)))), null)))))))), null)))))), (new sc_Pair(\"\\u1E9Cequal\", (new sc_Pair((new sc_Pair(\"\\u1E9Cpower-eval\", (new sc_Pair((new sc_Pair(\"\\u1E9Cpower-rep\", (new sc_Pair(\"\\u1E9Ci\", (new sc_Pair(\"\\u1E9Cbase\", null)))))), (new sc_Pair(\"\\u1E9Cbase\", null)))))), (new sc_Pair((new sc_Pair(\"\\u1E9Cfix\", (new sc_Pair(\"\\u1E9Ci\", null)))), null)))))), (new sc_Pair(\"\\u1E9Cequal\", (new sc_Pair((new sc_Pair(\"\\u1E9Cpower-eval\", (new sc_Pair((new sc_Pair(\"\\u1E9Cbig-plus\", (new sc_Pair((new sc_Pair(\"\\u1E9Cpower-rep\", (new sc_Pair(\"\\u1E9Ci\", (new sc_Pair(\"\\u1E9Cbase\", null)))))), (new sc_Pair((new sc_Pair(\"\\u1E9Cpower-rep\", (new sc_Pair(\"\\u1E9Cj\", (new sc_Pair(\"\\u1E9Cbase\", null)))))), (new sc_Pair((new sc_Pair(\"\\u1E9Czero\", null)), (new sc_Pair(\"\\u1E9Cbase\", null)))))))))), (new sc_Pair(\"\\u1E9Cbase\", null)))))), (new sc_Pair((new sc_Pair(\"\\u1E9Cplus\", (new sc_Pair(\"\\u1E9Ci\", (new sc_Pair(\"\\u1E9Cj\", null)))))), null)))))), (new sc_Pair(\"\\u1E9Cequal\", (new sc_Pair((new sc_Pair(\"\\u1E9Cgcd\", (new sc_Pair(\"\\u1E9Cx\", (new sc_Pair(\"\\u1E9Cy\", null)))))), (new sc_Pair((new sc_Pair(\"\\u1E9Cgcd\", (new sc_Pair(\"\\u1E9Cy\", (new sc_Pair(\"\\u1E9Cx\", null)))))), null)))))), (new sc_Pair(\"\\u1E9Cequal\", (new sc_Pair((new sc_Pair(\"\\u1E9Cnth\", (new sc_Pair((new sc_Pair(\"\\u1E9Cappend\", (new sc_Pair(\"\\u1E9Ca\", (new sc_Pair(\"\\u1E9Cb\", null)))))), (new sc_Pair(\"\\u1E9Ci\", null)))))), (new sc_Pair((new sc_Pair(\"\\u1E9Cappend\", (new sc_Pair((new sc_Pair(\"\\u1E9Cnth\", (new sc_Pair(\"\\u1E9Ca\", (new sc_Pair(\"\\u1E9Ci\", null)))))), (new sc_Pair((new sc_Pair(\"\\u1E9Cnth\", (new sc_Pair(\"\\u1E9Cb\", (new sc_Pair((new sc_Pair(\"\\u1E9Cdifference\", (new sc_Pair(\"\\u1E9Ci\", (new sc_Pair((new sc_Pair(\"\\u1E9Clength\", (new sc_Pair(\"\\u1E9Ca\", null)))), null)))))), null)))))), null)))))), null)))))), (new sc_Pair(\"\\u1E9Cequal\", (new sc_Pair((new sc_Pair(\"\\u1E9Cdifference\", (new sc_Pair((new sc_Pair(\"\\u1E9Cplus\", (new sc_Pair(\"\\u1E9Cx\", (new sc_Pair(\"\\u1E9Cy\", null)))))), (new sc_Pair(\"\\u1E9Cx\", null)))))), (new sc_Pair((new sc_Pair(\"\\u1E9Cfix\", (new sc_Pair(\"\\u1E9Cy\", null)))), null)))))), (new sc_Pair(\"\\u1E9Cequal\", (new sc_Pair((new sc_Pair(\"\\u1E9Cdifference\", (new sc_Pair((new sc_Pair(\"\\u1E9Cplus\", (new sc_Pair(\"\\u1E9Cy\", (new sc_Pair(\"\\u1E9Cx\", null)))))), (new sc_Pair(\"\\u1E9Cx\", null)))))), (new sc_Pair((new sc_Pair(\"\\u1E9Cfix\", (new sc_Pair(\"\\u1E9Cy\", null)))), null)))))), (new sc_Pair(\"\\u1E9Cequal\", (new sc_Pair((new sc_Pair(\"\\u1E9Cdifference\", (new sc_Pair((new sc_Pair(\"\\u1E9Cplus\", (new sc_Pair(\"\\u1E9Cx\", (new sc_Pair(\"\\u1E9Cy\", null)))))), (new sc_Pair((new sc_Pair(\"\\u1E9Cplus\", (new sc_Pair(\"\\u1E9Cx\", (new sc_Pair(\"\\u1E9Cz\", null)))))), null)))))), (new sc_Pair((new sc_Pair(\"\\u1E9Cdifference\", (new sc_Pair(\"\\u1E9Cy\", (new sc_Pair(\"\\u1E9Cz\", null)))))), null)))))), (new sc_Pair(\"\\u1E9Cequal\", (new sc_Pair((new sc_Pair(\"\\u1E9Ctimes\", (new sc_Pair(\"\\u1E9Cx\", (new sc_Pair((new sc_Pair(\"\\u1E9Cdifference\", (new sc_Pair(\"\\u1E9Cc\", (new sc_Pair(\"\\u1E9Cw\", null)))))), null)))))), (new sc_Pair((new sc_Pair(\"\\u1E9Cdifference\", (new sc_Pair((new sc_Pair(\"\\u1E9Ctimes\", (new sc_Pair(\"\\u1E9Cc\", (new sc_Pair(\"\\u1E9Cx\", null)))))), (new sc_Pair((new sc_Pair(\"\\u1E9Ctimes\", (new sc_Pair(\"\\u1E9Cw\", (new sc_Pair(\"\\u1E9Cx\", null)))))), null)))))), null)))))), (new sc_Pair(\"\\u1E9Cequal\", (new sc_Pair((new sc_Pair(\"\\u1E9Cremainder\", (new sc_Pair((new sc_Pair(\"\\u1E9Ctimes\", (new sc_Pair(\"\\u1E9Cx\", (new sc_Pair(\"\\u1E9Cz\", null)))))), (new sc_Pair(\"\\u1E9Cz\", null)))))), (new sc_Pair((new sc_Pair(\"\\u1E9Czero\", null)), null)))))), (new sc_Pair(\"\\u1E9Cequal\", (new sc_Pair((new sc_Pair(\"\\u1E9Cdifference\", (new sc_Pair((new sc_Pair(\"\\u1E9Cplus\", (new sc_Pair(\"\\u1E9Cb\", (new sc_Pair((new sc_Pair(\"\\u1E9Cplus\", (new sc_Pair(\"\\u1E9Ca\", (new sc_Pair(\"\\u1E9Cc\", null)))))), null)))))), (new sc_Pair(\"\\u1E9Ca\", null)))))), (new sc_Pair((new sc_Pair(\"\\u1E9Cplus\", (new sc_Pair(\"\\u1E9Cb\", (new sc_Pair(\"\\u1E9Cc\", null)))))), null)))))), (new sc_Pair(\"\\u1E9Cequal\", (new sc_Pair((new sc_Pair(\"\\u1E9Cdifference\", (new sc_Pair((new sc_Pair(\"\\u1E9Cadd1\", (new sc_Pair((new sc_Pair(\"\\u1E9Cplus\", (new sc_Pair(\"\\u1E9Cy\", (new sc_Pair(\"\\u1E9Cz\", null)))))), null)))), (new sc_Pair(\"\\u1E9Cz\", null)))))), (new sc_Pair((new sc_Pair(\"\\u1E9Cadd1\", (new sc_Pair(\"\\u1E9Cy\", null)))), null)))))), (new sc_Pair(\"\\u1E9Cequal\", (new sc_Pair((new sc_Pair(\"\\u1E9Clessp\", (new sc_Pair((new sc_Pair(\"\\u1E9Cplus\", (new sc_Pair(\"\\u1E9Cx\", (new sc_Pair(\"\\u1E9Cy\", null)))))), (new sc_Pair((new sc_Pair(\"\\u1E9Cplus\", (new sc_Pair(\"\\u1E9Cx\", (new sc_Pair(\"\\u1E9Cz\", null)))))), null)))))), (new sc_Pair((new sc_Pair(\"\\u1E9Clessp\", (new sc_Pair(\"\\u1E9Cy\", (new sc_Pair(\"\\u1E9Cz\", null)))))), null)))))), (new sc_Pair(\"\\u1E9Cequal\", (new sc_Pair((new sc_Pair(\"\\u1E9Clessp\", (new sc_Pair((new sc_Pair(\"\\u1E9Ctimes\", (new sc_Pair(\"\\u1E9Cx\", (new sc_Pair(\"\\u1E9Cz\", null)))))), (new sc_Pair((new sc_Pair(\"\\u1E9Ctimes\", (new sc_Pair(\"\\u1E9Cy\", (new sc_Pair(\"\\u1E9Cz\", null)))))), null)))))), (new sc_Pair((new sc_Pair(\"\\u1E9Cand\", (new sc_Pair((new sc_Pair(\"\\u1E9Cnot\", (new sc_Pair((new sc_Pair(\"\\u1E9Czerop\", (new sc_Pair(\"\\u1E9Cz\", null)))), null)))), (new sc_Pair((new sc_Pair(\"\\u1E9Clessp\", (new sc_Pair(\"\\u1E9Cx\", (new sc_Pair(\"\\u1E9Cy\", null)))))), null)))))), null)))))), (new sc_Pair(\"\\u1E9Cequal\", (new sc_Pair((new sc_Pair(\"\\u1E9Clessp\", (new sc_Pair(\"\\u1E9Cy\", (new sc_Pair((new sc_Pair(\"\\u1E9Cplus\", (new sc_Pair(\"\\u1E9Cx\", (new sc_Pair(\"\\u1E9Cy\", null)))))), null)))))), (new sc_Pair((new sc_Pair(\"\\u1E9Cnot\", (new sc_Pair((new sc_Pair(\"\\u1E9Czerop\", (new sc_Pair(\"\\u1E9Cx\", null)))), null)))), null)))))), (new sc_Pair(\"\\u1E9Cequal\", (new sc_Pair((new sc_Pair(\"\\u1E9Cgcd\", (new sc_Pair((new sc_Pair(\"\\u1E9Ctimes\", (new sc_Pair(\"\\u1E9Cx\", (new sc_Pair(\"\\u1E9Cz\", null)))))), (new sc_Pair((new sc_Pair(\"\\u1E9Ctimes\", (new sc_Pair(\"\\u1E9Cy\", (new sc_Pair(\"\\u1E9Cz\", null)))))), null)))))), (new sc_Pair((new sc_Pair(\"\\u1E9Ctimes\", (new sc_Pair(\"\\u1E9Cz\", (new sc_Pair((new sc_Pair(\"\\u1E9Cgcd\", (new sc_Pair(\"\\u1E9Cx\", (new sc_Pair(\"\\u1E9Cy\", null)))))), null)))))), null)))))), (new sc_Pair(\"\\u1E9Cequal\", (new sc_Pair((new sc_Pair(\"\\u1E9Cvalue\", (new sc_Pair((new sc_Pair(\"\\u1E9Cnormalize\", (new sc_Pair(\"\\u1E9Cx\", null)))), (new sc_Pair(\"\\u1E9Ca\", null)))))), (new sc_Pair((new sc_Pair(\"\\u1E9Cvalue\", (new sc_Pair(\"\\u1E9Cx\", (new sc_Pair(\"\\u1E9Ca\", null)))))), null)))))), (new sc_Pair(\"\\u1E9Cequal\", (new sc_Pair((new sc_Pair(\"\\u1E9Cequal\", (new sc_Pair((new sc_Pair(\"\\u1E9Cflatten\", (new sc_Pair(\"\\u1E9Cx\", null)))), (new sc_Pair((new sc_Pair(\"\\u1E9Ccons\", (new sc_Pair(\"\\u1E9Cy\", (new sc_Pair((new sc_Pair(\"\\u1E9Cnil\", null)), null)))))), null)))))), (new sc_Pair((new sc_Pair(\"\\u1E9Cand\", (new sc_Pair((new sc_Pair(\"\\u1E9Cnlistp\", (new sc_Pair(\"\\u1E9Cx\", null)))), (new sc_Pair((new sc_Pair(\"\\u1E9Cequal\", (new sc_Pair(\"\\u1E9Cx\", (new sc_Pair(\"\\u1E9Cy\", null)))))), null)))))), null)))))), (new sc_Pair(\"\\u1E9Cequal\", (new sc_Pair((new sc_Pair(\"\\u1E9Clistp\", (new sc_Pair((new sc_Pair(\"\\u1E9Cgopher\", (new sc_Pair(\"\\u1E9Cx\", null)))), null)))), (new sc_Pair((new sc_Pair(\"\\u1E9Clistp\", (new sc_Pair(\"\\u1E9Cx\", null)))), null)))))), (new sc_Pair(\"\\u1E9Cequal\", (new sc_Pair((new sc_Pair(\"\\u1E9Csamefringe\", (new sc_Pair(\"\\u1E9Cx\", (new sc_Pair(\"\\u1E9Cy\", null)))))), (new sc_Pair((new sc_Pair(\"\\u1E9Cequal\", (new sc_Pair((new sc_Pair(\"\\u1E9Cflatten\", (new sc_Pair(\"\\u1E9Cx\", null)))), (new sc_Pair((new sc_Pair(\"\\u1E9Cflatten\", (new sc_Pair(\"\\u1E9Cy\", null)))), null)))))), null)))))), (new sc_Pair(\"\\u1E9Cequal\", (new sc_Pair((new sc_Pair(\"\\u1E9Cequal\", (new sc_Pair((new sc_Pair(\"\\u1E9Cgreatest-factor\", (new sc_Pair(\"\\u1E9Cx\", (new sc_Pair(\"\\u1E9Cy\", null)))))), (new sc_Pair((new sc_Pair(\"\\u1E9Czero\", null)), null)))))), (new sc_Pair((new sc_Pair(\"\\u1E9Cand\", (new sc_Pair((new sc_Pair(\"\\u1E9Cor\", (new sc_Pair((new sc_Pair(\"\\u1E9Czerop\", (new sc_Pair(\"\\u1E9Cy\", null)))), (new sc_Pair((new sc_Pair(\"\\u1E9Cequal\", (new sc_Pair(\"\\u1E9Cy\", (new sc_Pair((1), null)))))), null)))))), (new sc_Pair((new sc_Pair(\"\\u1E9Cequal\", (new sc_Pair(\"\\u1E9Cx\", (new sc_Pair((new sc_Pair(\"\\u1E9Czero\", null)), null)))))), null)))))), null)))))), (new sc_Pair(\"\\u1E9Cequal\", (new sc_Pair((new sc_Pair(\"\\u1E9Cequal\", (new sc_Pair((new sc_Pair(\"\\u1E9Cgreatest-factor\", (new sc_Pair(\"\\u1E9Cx\", (new sc_Pair(\"\\u1E9Cy\", null)))))), (new sc_Pair((1), null)))))), (new sc_Pair((new sc_Pair(\"\\u1E9Cequal\", (new sc_Pair(\"\\u1E9Cx\", (new sc_Pair((1), null)))))), null)))))), (new sc_Pair(\"\\u1E9Cequal\", (new sc_Pair((new sc_Pair(\"\\u1E9Cnumberp\", (new sc_Pair((new sc_Pair(\"\\u1E9Cgreatest-factor\", (new sc_Pair(\"\\u1E9Cx\", (new sc_Pair(\"\\u1E9Cy\", null)))))), null)))), (new sc_Pair((new sc_Pair(\"\\u1E9Cnot\", (new sc_Pair((new sc_Pair(\"\\u1E9Cand\", (new sc_Pair((new sc_Pair(\"\\u1E9Cor\", (new sc_Pair((new sc_Pair(\"\\u1E9Czerop\", (new sc_Pair(\"\\u1E9Cy\", null)))), (new sc_Pair((new sc_Pair(\"\\u1E9Cequal\", (new sc_Pair(\"\\u1E9Cy\", (new sc_Pair((1), null)))))), null)))))), (new sc_Pair((new sc_Pair(\"\\u1E9Cnot\", (new sc_Pair((new sc_Pair(\"\\u1E9Cnumberp\", (new sc_Pair(\"\\u1E9Cx\", null)))), null)))), null)))))), null)))), null)))))), (new sc_Pair(\"\\u1E9Cequal\", (new sc_Pair((new sc_Pair(\"\\u1E9Ctimes-list\", (new sc_Pair((new sc_Pair(\"\\u1E9Cappend\", (new sc_Pair(\"\\u1E9Cx\", (new sc_Pair(\"\\u1E9Cy\", null)))))), null)))), (new sc_Pair((new sc_Pair(\"\\u1E9Ctimes\", (new sc_Pair((new sc_Pair(\"\\u1E9Ctimes-list\", (new sc_Pair(\"\\u1E9Cx\", null)))), (new sc_Pair((new sc_Pair(\"\\u1E9Ctimes-list\", (new sc_Pair(\"\\u1E9Cy\", null)))), null)))))), null)))))), (new sc_Pair(\"\\u1E9Cequal\", (new sc_Pair((new sc_Pair(\"\\u1E9Cprime-list\", (new sc_Pair((new sc_Pair(\"\\u1E9Cappend\", (new sc_Pair(\"\\u1E9Cx\", (new sc_Pair(\"\\u1E9Cy\", null)))))), null)))), (new sc_Pair((new sc_Pair(\"\\u1E9Cand\", (new sc_Pair((new sc_Pair(\"\\u1E9Cprime-list\", (new sc_Pair(\"\\u1E9Cx\", null)))), (new sc_Pair((new sc_Pair(\"\\u1E9Cprime-list\", (new sc_Pair(\"\\u1E9Cy\", null)))), null)))))), null)))))), (new sc_Pair(\"\\u1E9Cequal\", (new sc_Pair((new sc_Pair(\"\\u1E9Cequal\", (new sc_Pair(\"\\u1E9Cz\", (new sc_Pair((new sc_Pair(\"\\u1E9Ctimes\", (new sc_Pair(\"\\u1E9Cw\", (new sc_Pair(\"\\u1E9Cz\", null)))))), null)))))), (new sc_Pair((new sc_Pair(\"\\u1E9Cand\", (new sc_Pair((new sc_Pair(\"\\u1E9Cnumberp\", (new sc_Pair(\"\\u1E9Cz\", null)))), (new sc_Pair((new sc_Pair(\"\\u1E9Cor\", (new sc_Pair((new sc_Pair(\"\\u1E9Cequal\", (new sc_Pair(\"\\u1E9Cz\", (new sc_Pair((new sc_Pair(\"\\u1E9Czero\", null)), null)))))), (new sc_Pair((new sc_Pair(\"\\u1E9Cequal\", (new sc_Pair(\"\\u1E9Cw\", (new sc_Pair((1), null)))))), null)))))), null)))))), null)))))), (new sc_Pair(\"\\u1E9Cequal\", (new sc_Pair((new sc_Pair(\"\\u1E9Cgreatereqp\", (new sc_Pair(\"\\u1E9Cx\", (new sc_Pair(\"\\u1E9Cy\", null)))))), (new sc_Pair((new sc_Pair(\"\\u1E9Cnot\", (new sc_Pair((new sc_Pair(\"\\u1E9Clessp\", (new sc_Pair(\"\\u1E9Cx\", (new sc_Pair(\"\\u1E9Cy\", null)))))), null)))), null)))))), (new sc_Pair(\"\\u1E9Cequal\", (new sc_Pair((new sc_Pair(\"\\u1E9Cequal\", (new sc_Pair(\"\\u1E9Cx\", (new sc_Pair((new sc_Pair(\"\\u1E9Ctimes\", (new sc_Pair(\"\\u1E9Cx\", (new sc_Pair(\"\\u1E9Cy\", null)))))), null)))))), (new sc_Pair((new sc_Pair(\"\\u1E9Cor\", (new sc_Pair((new sc_Pair(\"\\u1E9Cequal\", (new sc_Pair(\"\\u1E9Cx\", (new sc_Pair((new sc_Pair(\"\\u1E9Czero\", null)), null)))))), (new sc_Pair((new sc_Pair(\"\\u1E9Cand\", (new sc_Pair((new sc_Pair(\"\\u1E9Cnumberp\", (new sc_Pair(\"\\u1E9Cx\", null)))), (new sc_Pair((new sc_Pair(\"\\u1E9Cequal\", (new sc_Pair(\"\\u1E9Cy\", (new sc_Pair((1), null)))))), null)))))), null)))))), null)))))), (new sc_Pair(\"\\u1E9Cequal\", (new sc_Pair((new sc_Pair(\"\\u1E9Cremainder\", (new sc_Pair((new sc_Pair(\"\\u1E9Ctimes\", (new sc_Pair(\"\\u1E9Cy\", (new sc_Pair(\"\\u1E9Cx\", null)))))), (new sc_Pair(\"\\u1E9Cy\", null)))))), (new sc_Pair((new sc_Pair(\"\\u1E9Czero\", null)), null)))))), (new sc_Pair(\"\\u1E9Cequal\", (new sc_Pair((new sc_Pair(\"\\u1E9Cequal\", (new sc_Pair((new sc_Pair(\"\\u1E9Ctimes\", (new sc_Pair(\"\\u1E9Ca\", (new sc_Pair(\"\\u1E9Cb\", null)))))), (new sc_Pair((1), null)))))), (new sc_Pair(sc_list(\"\\u1E9Cand\", (new sc_Pair(\"\\u1E9Cnot\", (new sc_Pair((new sc_Pair(\"\\u1E9Cequal\", (new sc_Pair(\"\\u1E9Ca\", (new sc_Pair((new sc_Pair(\"\\u1E9Czero\", null)), null)))))), null)))), (new sc_Pair(\"\\u1E9Cnot\", (new sc_Pair((new sc_Pair(\"\\u1E9Cequal\", (new sc_Pair(\"\\u1E9Cb\", (new sc_Pair((new sc_Pair(\"\\u1E9Czero\", null)), null)))))), null)))), (new sc_Pair(\"\\u1E9Cnumberp\", (new sc_Pair(\"\\u1E9Ca\", null)))), (new sc_Pair(\"\\u1E9Cnumberp\", (new sc_Pair(\"\\u1E9Cb\", null)))), (new sc_Pair(\"\\u1E9Cequal\", (new sc_Pair((new sc_Pair(\"\\u1E9Csub1\", (new sc_Pair(\"\\u1E9Ca\", null)))), (new sc_Pair((new sc_Pair(\"\\u1E9Czero\", null)), null)))))), (new sc_Pair(\"\\u1E9Cequal\", (new sc_Pair((new sc_Pair(\"\\u1E9Csub1\", (new sc_Pair(\"\\u1E9Cb\", null)))), (new sc_Pair((new sc_Pair(\"\\u1E9Czero\", null)), null))))))), null)))))), (new sc_Pair(\"\\u1E9Cequal\", (new sc_Pair((new sc_Pair(\"\\u1E9Clessp\", (new sc_Pair((new sc_Pair(\"\\u1E9Clength\", (new sc_Pair((new sc_Pair(\"\\u1E9Cdelete\", (new sc_Pair(\"\\u1E9Cx\", (new sc_Pair(\"\\u1E9Cl\", null)))))), null)))), (new sc_Pair((new sc_Pair(\"\\u1E9Clength\", (new sc_Pair(\"\\u1E9Cl\", null)))), null)))))), (new sc_Pair((new sc_Pair(\"\\u1E9Cmember\", (new sc_Pair(\"\\u1E9Cx\", (new sc_Pair(\"\\u1E9Cl\", null)))))), null)))))), (new sc_Pair(\"\\u1E9Cequal\", (new sc_Pair((new sc_Pair(\"\\u1E9Csort2\", (new sc_Pair((new sc_Pair(\"\\u1E9Cdelete\", (new sc_Pair(\"\\u1E9Cx\", (new sc_Pair(\"\\u1E9Cl\", null)))))), null)))), (new sc_Pair((new sc_Pair(\"\\u1E9Cdelete\", (new sc_Pair(\"\\u1E9Cx\", (new sc_Pair((new sc_Pair(\"\\u1E9Csort2\", (new sc_Pair(\"\\u1E9Cl\", null)))), null)))))), null)))))), (new sc_Pair(\"\\u1E9Cequal\", (new sc_Pair((new sc_Pair(\"\\u1E9Cdsort\", (new sc_Pair(\"\\u1E9Cx\", null)))), (new sc_Pair((new sc_Pair(\"\\u1E9Csort2\", (new sc_Pair(\"\\u1E9Cx\", null)))), null)))))), (new sc_Pair(\"\\u1E9Cequal\", (new sc_Pair((new sc_Pair(\"\\u1E9Clength\", (new sc_Pair((new sc_Pair(\"\\u1E9Ccons\", (new sc_Pair(\"\\u1E9Cx1\", (new sc_Pair((new sc_Pair(\"\\u1E9Ccons\", (new sc_Pair(\"\\u1E9Cx2\", (new sc_Pair((new sc_Pair(\"\\u1E9Ccons\", (new sc_Pair(\"\\u1E9Cx3\", (new sc_Pair((new sc_Pair(\"\\u1E9Ccons\", (new sc_Pair(\"\\u1E9Cx4\", (new sc_Pair((new sc_Pair(\"\\u1E9Ccons\", (new sc_Pair(\"\\u1E9Cx5\", (new sc_Pair((new sc_Pair(\"\\u1E9Ccons\", (new sc_Pair(\"\\u1E9Cx6\", (new sc_Pair(\"\\u1E9Cx7\", null)))))), null)))))), null)))))), null)))))), null)))))), null)))))), null)))), (new sc_Pair((new sc_Pair(\"\\u1E9Cplus\", (new sc_Pair((6), (new sc_Pair((new sc_Pair(\"\\u1E9Clength\", (new sc_Pair(\"\\u1E9Cx7\", null)))), null)))))), null)))))), (new sc_Pair(\"\\u1E9Cequal\", (new sc_Pair((new sc_Pair(\"\\u1E9Cdifference\", (new sc_Pair((new sc_Pair(\"\\u1E9Cadd1\", (new sc_Pair((new sc_Pair(\"\\u1E9Cadd1\", (new sc_Pair(\"\\u1E9Cx\", null)))), null)))), (new sc_Pair((2), null)))))), (new sc_Pair((new sc_Pair(\"\\u1E9Cfix\", (new sc_Pair(\"\\u1E9Cx\", null)))), null)))))), (new sc_Pair(\"\\u1E9Cequal\", (new sc_Pair((new sc_Pair(\"\\u1E9Cquotient\", (new sc_Pair((new sc_Pair(\"\\u1E9Cplus\", (new sc_Pair(\"\\u1E9Cx\", (new sc_Pair((new sc_Pair(\"\\u1E9Cplus\", (new sc_Pair(\"\\u1E9Cx\", (new sc_Pair(\"\\u1E9Cy\", null)))))), null)))))), (new sc_Pair((2), null)))))), (new sc_Pair((new sc_Pair(\"\\u1E9Cplus\", (new sc_Pair(\"\\u1E9Cx\", (new sc_Pair((new sc_Pair(\"\\u1E9Cquotient\", (new sc_Pair(\"\\u1E9Cy\", (new sc_Pair((2), null)))))), null)))))), null)))))), (new sc_Pair(\"\\u1E9Cequal\", (new sc_Pair((new sc_Pair(\"\\u1E9Csigma\", (new sc_Pair((new sc_Pair(\"\\u1E9Czero\", null)), (new sc_Pair(\"\\u1E9Ci\", null)))))), (new sc_Pair((new sc_Pair(\"\\u1E9Cquotient\", (new sc_Pair((new sc_Pair(\"\\u1E9Ctimes\", (new sc_Pair(\"\\u1E9Ci\", (new sc_Pair((new sc_Pair(\"\\u1E9Cadd1\", (new sc_Pair(\"\\u1E9Ci\", null)))), null)))))), (new sc_Pair((2), null)))))), null)))))), (new sc_Pair(\"\\u1E9Cequal\", (new sc_Pair((new sc_Pair(\"\\u1E9Cplus\", (new sc_Pair(\"\\u1E9Cx\", (new sc_Pair((new sc_Pair(\"\\u1E9Cadd1\", (new sc_Pair(\"\\u1E9Cy\", null)))), null)))))), (new sc_Pair((new sc_Pair(\"\\u1E9Cif\", (new sc_Pair((new sc_Pair(\"\\u1E9Cnumberp\", (new sc_Pair(\"\\u1E9Cy\", null)))), (new sc_Pair((new sc_Pair(\"\\u1E9Cadd1\", (new sc_Pair((new sc_Pair(\"\\u1E9Cplus\", (new sc_Pair(\"\\u1E9Cx\", (new sc_Pair(\"\\u1E9Cy\", null)))))), null)))), (new sc_Pair((new sc_Pair(\"\\u1E9Cadd1\", (new sc_Pair(\"\\u1E9Cx\", null)))), null)))))))), null)))))), (new sc_Pair(\"\\u1E9Cequal\", (new sc_Pair((new sc_Pair(\"\\u1E9Cequal\", (new sc_Pair((new sc_Pair(\"\\u1E9Cdifference\", (new sc_Pair(\"\\u1E9Cx\", (new sc_Pair(\"\\u1E9Cy\", null)))))), (new sc_Pair((new sc_Pair(\"\\u1E9Cdifference\", (new sc_Pair(\"\\u1E9Cz\", (new sc_Pair(\"\\u1E9Cy\", null)))))), null)))))), (new sc_Pair((new sc_Pair(\"\\u1E9Cif\", (new sc_Pair((new sc_Pair(\"\\u1E9Clessp\", (new sc_Pair(\"\\u1E9Cx\", (new sc_Pair(\"\\u1E9Cy\", null)))))), (new sc_Pair((new sc_Pair(\"\\u1E9Cnot\", (new sc_Pair((new sc_Pair(\"\\u1E9Clessp\", (new sc_Pair(\"\\u1E9Cy\", (new sc_Pair(\"\\u1E9Cz\", null)))))), null)))), (new sc_Pair((new sc_Pair(\"\\u1E9Cif\", (new sc_Pair((new sc_Pair(\"\\u1E9Clessp\", (new sc_Pair(\"\\u1E9Cz\", (new sc_Pair(\"\\u1E9Cy\", null)))))), (new sc_Pair((new sc_Pair(\"\\u1E9Cnot\", (new sc_Pair((new sc_Pair(\"\\u1E9Clessp\", (new sc_Pair(\"\\u1E9Cy\", (new sc_Pair(\"\\u1E9Cx\", null)))))), null)))), (new sc_Pair((new sc_Pair(\"\\u1E9Cequal\", (new sc_Pair((new sc_Pair(\"\\u1E9Cfix\", (new sc_Pair(\"\\u1E9Cx\", null)))), (new sc_Pair((new sc_Pair(\"\\u1E9Cfix\", (new sc_Pair(\"\\u1E9Cz\", null)))), null)))))), null)))))))), null)))))))), null)))))), (new sc_Pair(\"\\u1E9Cequal\", (new sc_Pair((new sc_Pair(\"\\u1E9Cmeaning\", (new sc_Pair((new sc_Pair(\"\\u1E9Cplus-tree\", (new sc_Pair((new sc_Pair(\"\\u1E9Cdelete\", (new sc_Pair(\"\\u1E9Cx\", (new sc_Pair(\"\\u1E9Cy\", null)))))), null)))), (new sc_Pair(\"\\u1E9Ca\", null)))))), (new sc_Pair((new sc_Pair(\"\\u1E9Cif\", (new sc_Pair((new sc_Pair(\"\\u1E9Cmember\", (new sc_Pair(\"\\u1E9Cx\", (new sc_Pair(\"\\u1E9Cy\", null)))))), (new sc_Pair((new sc_Pair(\"\\u1E9Cdifference\", (new sc_Pair((new sc_Pair(\"\\u1E9Cmeaning\", (new sc_Pair((new sc_Pair(\"\\u1E9Cplus-tree\", (new sc_Pair(\"\\u1E9Cy\", null)))), (new sc_Pair(\"\\u1E9Ca\", null)))))), (new sc_Pair((new sc_Pair(\"\\u1E9Cmeaning\", (new sc_Pair(\"\\u1E9Cx\", (new sc_Pair(\"\\u1E9Ca\", null)))))), null)))))), (new sc_Pair((new sc_Pair(\"\\u1E9Cmeaning\", (new sc_Pair((new sc_Pair(\"\\u1E9Cplus-tree\", (new sc_Pair(\"\\u1E9Cy\", null)))), (new sc_Pair(\"\\u1E9Ca\", null)))))), null)))))))), null)))))), (new sc_Pair(\"\\u1E9Cequal\", (new sc_Pair((new sc_Pair(\"\\u1E9Ctimes\", (new sc_Pair(\"\\u1E9Cx\", (new sc_Pair((new sc_Pair(\"\\u1E9Cadd1\", (new sc_Pair(\"\\u1E9Cy\", null)))), null)))))), (new sc_Pair((new sc_Pair(\"\\u1E9Cif\", (new sc_Pair((new sc_Pair(\"\\u1E9Cnumberp\", (new sc_Pair(\"\\u1E9Cy\", null)))), (new sc_Pair((new sc_Pair(\"\\u1E9Cplus\", (new sc_Pair(\"\\u1E9Cx\", (new sc_Pair((new sc_Pair(\"\\u1E9Ctimes\", (new sc_Pair(\"\\u1E9Cx\", (new sc_Pair(\"\\u1E9Cy\", null)))))), null)))))), (new sc_Pair((new sc_Pair(\"\\u1E9Cfix\", (new sc_Pair(\"\\u1E9Cx\", null)))), null)))))))), null)))))), (new sc_Pair(\"\\u1E9Cequal\", (new sc_Pair((new sc_Pair(\"\\u1E9Cnth\", (new sc_Pair((new sc_Pair(\"\\u1E9Cnil\", null)), (new sc_Pair(\"\\u1E9Ci\", null)))))), (new sc_Pair((new sc_Pair(\"\\u1E9Cif\", (new sc_Pair((new sc_Pair(\"\\u1E9Czerop\", (new sc_Pair(\"\\u1E9Ci\", null)))), (new sc_Pair((new sc_Pair(\"\\u1E9Cnil\", null)), (new sc_Pair((new sc_Pair(\"\\u1E9Czero\", null)), null)))))))), null)))))), (new sc_Pair(\"\\u1E9Cequal\", (new sc_Pair((new sc_Pair(\"\\u1E9Clast\", (new sc_Pair((new sc_Pair(\"\\u1E9Cappend\", (new sc_Pair(\"\\u1E9Ca\", (new sc_Pair(\"\\u1E9Cb\", null)))))), null)))), (new sc_Pair((new sc_Pair(\"\\u1E9Cif\", (new sc_Pair((new sc_Pair(\"\\u1E9Clistp\", (new sc_Pair(\"\\u1E9Cb\", null)))), (new sc_Pair((new sc_Pair(\"\\u1E9Clast\", (new sc_Pair(\"\\u1E9Cb\", null)))), (new sc_Pair((new sc_Pair(\"\\u1E9Cif\", (new sc_Pair((new sc_Pair(\"\\u1E9Clistp\", (new sc_Pair(\"\\u1E9Ca\", null)))), (new sc_Pair((new sc_Pair(\"\\u1E9Ccons\", (new sc_Pair((new sc_Pair(\"\\u1E9Ccar\", (new sc_Pair((new sc_Pair(\"\\u1E9Clast\", (new sc_Pair(\"\\u1E9Ca\", null)))), null)))), (new sc_Pair(\"\\u1E9Cb\", null)))))), (new sc_Pair(\"\\u1E9Cb\", null)))))))), null)))))))), null)))))), (new sc_Pair(\"\\u1E9Cequal\", (new sc_Pair((new sc_Pair(\"\\u1E9Cequal\", (new sc_Pair((new sc_Pair(\"\\u1E9Clessp\", (new sc_Pair(\"\\u1E9Cx\", (new sc_Pair(\"\\u1E9Cy\", null)))))), (new sc_Pair(\"\\u1E9Cz\", null)))))), (new sc_Pair((new sc_Pair(\"\\u1E9Cif\", (new sc_Pair((new sc_Pair(\"\\u1E9Clessp\", (new sc_Pair(\"\\u1E9Cx\", (new sc_Pair(\"\\u1E9Cy\", null)))))), (new sc_Pair((new sc_Pair(\"\\u1E9Cequal\", (new sc_Pair((new sc_Pair(\"\\u1E9Ct\", null)), (new sc_Pair(\"\\u1E9Cz\", null)))))), (new sc_Pair((new sc_Pair(\"\\u1E9Cequal\", (new sc_Pair((new sc_Pair(\"\\u1E9Cf\", null)), (new sc_Pair(\"\\u1E9Cz\", null)))))), null)))))))), null)))))), (new sc_Pair(\"\\u1E9Cequal\", (new sc_Pair((new sc_Pair(\"\\u1E9Cassignment\", (new sc_Pair(\"\\u1E9Cx\", (new sc_Pair((new sc_Pair(\"\\u1E9Cappend\", (new sc_Pair(\"\\u1E9Ca\", (new sc_Pair(\"\\u1E9Cb\", null)))))), null)))))), (new sc_Pair((new sc_Pair(\"\\u1E9Cif\", (new sc_Pair((new sc_Pair(\"\\u1E9Cassignedp\", (new sc_Pair(\"\\u1E9Cx\", (new sc_Pair(\"\\u1E9Ca\", null)))))), (new sc_Pair((new sc_Pair(\"\\u1E9Cassignment\", (new sc_Pair(\"\\u1E9Cx\", (new sc_Pair(\"\\u1E9Ca\", null)))))), (new sc_Pair((new sc_Pair(\"\\u1E9Cassignment\", (new sc_Pair(\"\\u1E9Cx\", (new sc_Pair(\"\\u1E9Cb\", null)))))), null)))))))), null)))))), (new sc_Pair(\"\\u1E9Cequal\", (new sc_Pair((new sc_Pair(\"\\u1E9Ccar\", (new sc_Pair((new sc_Pair(\"\\u1E9Cgopher\", (new sc_Pair(\"\\u1E9Cx\", null)))), null)))), (new sc_Pair((new sc_Pair(\"\\u1E9Cif\", (new sc_Pair((new sc_Pair(\"\\u1E9Clistp\", (new sc_Pair(\"\\u1E9Cx\", null)))), (new sc_Pair((new sc_Pair(\"\\u1E9Ccar\", (new sc_Pair((new sc_Pair(\"\\u1E9Cflatten\", (new sc_Pair(\"\\u1E9Cx\", null)))), null)))), (new sc_Pair((new sc_Pair(\"\\u1E9Czero\", null)), null)))))))), null)))))), (new sc_Pair(\"\\u1E9Cequal\", (new sc_Pair((new sc_Pair(\"\\u1E9Cflatten\", (new sc_Pair((new sc_Pair(\"\\u1E9Ccdr\", (new sc_Pair((new sc_Pair(\"\\u1E9Cgopher\", (new sc_Pair(\"\\u1E9Cx\", null)))), null)))), null)))), (new sc_Pair((new sc_Pair(\"\\u1E9Cif\", (new sc_Pair((new sc_Pair(\"\\u1E9Clistp\", (new sc_Pair(\"\\u1E9Cx\", null)))), (new sc_Pair((new sc_Pair(\"\\u1E9Ccdr\", (new sc_Pair((new sc_Pair(\"\\u1E9Cflatten\", (new sc_Pair(\"\\u1E9Cx\", null)))), null)))), (new sc_Pair((new sc_Pair(\"\\u1E9Ccons\", (new sc_Pair((new sc_Pair(\"\\u1E9Czero\", null)), (new sc_Pair((new sc_Pair(\"\\u1E9Cnil\", null)), null)))))), null)))))))), null)))))), (new sc_Pair(\"\\u1E9Cequal\", (new sc_Pair((new sc_Pair(\"\\u1E9Cquotient\", (new sc_Pair((new sc_Pair(\"\\u1E9Ctimes\", (new sc_Pair(\"\\u1E9Cy\", (new sc_Pair(\"\\u1E9Cx\", null)))))), (new sc_Pair(\"\\u1E9Cy\", null)))))), (new sc_Pair((new sc_Pair(\"\\u1E9Cif\", (new sc_Pair((new sc_Pair(\"\\u1E9Czerop\", (new sc_Pair(\"\\u1E9Cy\", null)))), (new sc_Pair((new sc_Pair(\"\\u1E9Czero\", null)), (new sc_Pair((new sc_Pair(\"\\u1E9Cfix\", (new sc_Pair(\"\\u1E9Cx\", null)))), null)))))))), null)))))), (new sc_Pair(\"\\u1E9Cequal\", (new sc_Pair((new sc_Pair(\"\\u1E9Cget\", (new sc_Pair(\"\\u1E9Cj\", (new sc_Pair((new sc_Pair(\"\\u1E9Cset\", (new sc_Pair(\"\\u1E9Ci\", (new sc_Pair(\"\\u1E9Cval\", (new sc_Pair(\"\\u1E9Cmem\", null)))))))), null)))))), (new sc_Pair((new sc_Pair(\"\\u1E9Cif\", (new sc_Pair((new sc_Pair(\"\\u1E9Ceqp\", (new sc_Pair(\"\\u1E9Cj\", (new sc_Pair(\"\\u1E9Ci\", null)))))), (new sc_Pair(\"\\u1E9Cval\", (new sc_Pair((new sc_Pair(\"\\u1E9Cget\", (new sc_Pair(\"\\u1E9Cj\", (new sc_Pair(\"\\u1E9Cmem\", null)))))), null)))))))), null))))))));\n  (const_nboyer = (new sc_Pair((new sc_Pair(\"\\u1E9Cx\", (new sc_Pair(\"\\u1E9Cf\", (new sc_Pair((new sc_Pair(\"\\u1E9Cplus\", (new sc_Pair((new sc_Pair(\"\\u1E9Cplus\", (new sc_Pair(\"\\u1E9Ca\", (new sc_Pair(\"\\u1E9Cb\", null)))))), (new sc_Pair((new sc_Pair(\"\\u1E9Cplus\", (new sc_Pair(\"\\u1E9Cc\", (new sc_Pair((new sc_Pair(\"\\u1E9Czero\", null)), null)))))), null)))))), null)))))), (new sc_Pair((new sc_Pair(\"\\u1E9Cy\", (new sc_Pair(\"\\u1E9Cf\", (new sc_Pair((new sc_Pair(\"\\u1E9Ctimes\", (new sc_Pair((new sc_Pair(\"\\u1E9Ctimes\", (new sc_Pair(\"\\u1E9Ca\", (new sc_Pair(\"\\u1E9Cb\", null)))))), (new sc_Pair((new sc_Pair(\"\\u1E9Cplus\", (new sc_Pair(\"\\u1E9Cc\", (new sc_Pair(\"\\u1E9Cd\", null)))))), null)))))), null)))))), (new sc_Pair((new sc_Pair(\"\\u1E9Cz\", (new sc_Pair(\"\\u1E9Cf\", (new sc_Pair((new sc_Pair(\"\\u1E9Creverse\", (new sc_Pair((new sc_Pair(\"\\u1E9Cappend\", (new sc_Pair((new sc_Pair(\"\\u1E9Cappend\", (new sc_Pair(\"\\u1E9Ca\", (new sc_Pair(\"\\u1E9Cb\", null)))))), (new sc_Pair((new sc_Pair(\"\\u1E9Cnil\", null)), null)))))), null)))), null)))))), (new sc_Pair((new sc_Pair(\"\\u1E9Cu\", (new sc_Pair(\"\\u1E9Cequal\", (new sc_Pair((new sc_Pair(\"\\u1E9Cplus\", (new sc_Pair(\"\\u1E9Ca\", (new sc_Pair(\"\\u1E9Cb\", null)))))), (new sc_Pair((new sc_Pair(\"\\u1E9Cdifference\", (new sc_Pair(\"\\u1E9Cx\", (new sc_Pair(\"\\u1E9Cy\", null)))))), null)))))))), (new sc_Pair((new sc_Pair(\"\\u1E9Cw\", (new sc_Pair(\"\\u1E9Clessp\", (new sc_Pair((new sc_Pair(\"\\u1E9Cremainder\", (new sc_Pair(\"\\u1E9Ca\", (new sc_Pair(\"\\u1E9Cb\", null)))))), (new sc_Pair((new sc_Pair(\"\\u1E9Cmember\", (new sc_Pair(\"\\u1E9Ca\", (new sc_Pair((new sc_Pair(\"\\u1E9Clength\", (new sc_Pair(\"\\u1E9Cb\", null)))), null)))))), null)))))))), null)))))))))));\n  BgL_nboyerzd2benchmarkzd2 = function () {\n    var args = null;\n    for (var sc_tmp = arguments.length - 1; sc_tmp >= 0; sc_tmp--) {\n      args = sc_cons(arguments[sc_tmp], args);\n    }\n    var n;\n    return ((n = ((args === null) ? (0) : (args.car))), (BgL_setupzd2boyerzd2()), (BgL_runzd2benchmarkzd2((\"nboyer\" + (sc_number2string(n))), (1), function () {\n      return (BgL_testzd2boyerzd2(n));\n    }, function (rewrites) {\n      if ((sc_isNumber(rewrites)))\n        switch (n) {\n          case (0):\n            return (rewrites === (95024));\n            break;\n          case (1):\n            return (rewrites === (591777));\n            break;\n          case (2):\n            return (rewrites === (1813975));\n            break;\n          case (3):\n            return (rewrites === (5375678));\n            break;\n          case (4):\n            return (rewrites === (16445406));\n            break;\n          case (5):\n            return (rewrites === (51507739));\n            break;\n          default:\n            return true;\n            break;\n        }\n      else\n        return false;\n    })));\n  };\n  BgL_setupzd2boyerzd2 = function () {\n    return true;\n  };\n  BgL_testzd2boyerzd2 = function () {\n    return true;\n  };\n  translate_term_nboyer = function (term) {\n    var lst;\n    return (!(term instanceof sc_Pair) ? term : (new sc_Pair((BgL_sc_symbolzd2ze3symbolzd2record_1ze3_nboyer((term.car))), ((lst = (term.cdr)), ((lst === null) ? null : (new sc_Pair((translate_term_nboyer((lst.car))), (translate_args_nboyer((lst.cdr))))))))));\n  };\n  translate_args_nboyer = function (lst) {\n    var sc_lst_5;\n    var term;\n    return ((lst === null) ? null : (new sc_Pair(((term = (lst.car)), (!(term instanceof sc_Pair) ? term : (new sc_Pair((BgL_sc_symbolzd2ze3symbolzd2record_1ze3_nboyer((term.car))), (translate_args_nboyer((term.cdr))))))), ((sc_lst_5 = (lst.cdr)), ((sc_lst_5 === null) ? null : (new sc_Pair((translate_term_nboyer((sc_lst_5.car))), (translate_args_nboyer((sc_lst_5.cdr))))))))));\n  };\n  untranslate_term_nboyer = function (term) {\n    var optrOpnd;\n    var tail1131;\n    var L1127;\n    var falseHead1130;\n    var symbol_record;\n    if (!(term instanceof sc_Pair))\n      return term;\n    else {\n      (falseHead1130 = (new sc_Pair(null, null)));\n      (L1127 = (term.cdr));\n      (tail1131 = falseHead1130);\n      while (!(L1127 === null)) {\n        {\n          (tail1131.cdr = (new sc_Pair((untranslate_term_nboyer((L1127.car))), null)));\n          (tail1131 = (tail1131.cdr));\n          (L1127 = (L1127.cdr));\n        }\n      }\n      (optrOpnd = (falseHead1130.cdr));\n      return (new sc_Pair(((symbol_record = (term.car)), (symbol_record[(0)])), optrOpnd));\n    }\n  };\n  BgL_sc_symbolzd2ze3symbolzd2record_1ze3_nboyer = function (sym) {\n    var r;\n    var x;\n    return ((x = (sc_assq(sym, BgL_sc_za2symbolzd2recordszd2alistza2_2z00_nboyer))), ((x !== false) ? (x.cdr) : ((r = [sym, null]), (BgL_sc_za2symbolzd2recordszd2alistza2_2z00_nboyer = (new sc_Pair((new sc_Pair(sym, r)), BgL_sc_za2symbolzd2recordszd2alistza2_2z00_nboyer))), r)));\n  };\n  (BgL_sc_za2symbolzd2recordszd2alistza2_2z00_nboyer = null);\n  translate_alist_nboyer = function (alist) {\n    var sc_alist_6;\n    var term;\n    return ((alist === null) ? null : (new sc_Pair((new sc_Pair((alist.car.car), ((term = (alist.car.cdr)), (!(term instanceof sc_Pair) ? term : (new sc_Pair((BgL_sc_symbolzd2ze3symbolzd2record_1ze3_nboyer((term.car))), (translate_args_nboyer((term.cdr))))))))), ((sc_alist_6 = (alist.cdr)), ((sc_alist_6 === null) ? null : (new sc_Pair((new sc_Pair((sc_alist_6.car.car), (translate_term_nboyer((sc_alist_6.car.cdr))))), (translate_alist_nboyer((sc_alist_6.cdr))))))))));\n  };\n  apply_subst_nboyer = function (alist, term) {\n    var lst;\n    var temp_temp;\n    return (!(term instanceof sc_Pair) ? ((temp_temp = (sc_assq(term, alist))), ((temp_temp !== false) ? (temp_temp.cdr) : term)) : (new sc_Pair((term.car), ((lst = (term.cdr)), ((lst === null) ? null : (new sc_Pair((apply_subst_nboyer(alist, (lst.car))), (apply_subst_lst_nboyer(alist, (lst.cdr))))))))));\n  };\n  apply_subst_lst_nboyer = function (alist, lst) {\n    var sc_lst_7;\n    return ((lst === null) ? null : (new sc_Pair((apply_subst_nboyer(alist, (lst.car))), ((sc_lst_7 = (lst.cdr)), ((sc_lst_7 === null) ? null : (new sc_Pair((apply_subst_nboyer(alist, (sc_lst_7.car))), (apply_subst_lst_nboyer(alist, (sc_lst_7.cdr))))))))));\n  };\n  tautologyp_nboyer = function (sc_x_11, true_lst, false_lst) {\n    var tmp1125;\n    var x;\n    var tmp1126;\n    var sc_x_8;\n    var sc_tmp1125_9;\n    var sc_tmp1126_10;\n    var sc_x_11;\n    var true_lst;\n    var false_lst;\n    while (true) {\n      if ((((sc_tmp1126_10 = (is_term_equal_nboyer(sc_x_11, true_term_nboyer))), ((sc_tmp1126_10 !== false) ? sc_tmp1126_10 : (is_term_member_nboyer(sc_x_11, true_lst)))) !== false))\n        return true;\n      else if ((((sc_tmp1125_9 = (is_term_equal_nboyer(sc_x_11, false_term_nboyer))), ((sc_tmp1125_9 !== false) ? sc_tmp1125_9 : (is_term_member_nboyer(sc_x_11, false_lst)))) !== false))\n        return false;\n      else if (!(sc_x_11 instanceof sc_Pair))\n        return false;\n      else if (((sc_x_11.car) === if_constructor_nboyer))\n        if ((((sc_x_8 = (sc_x_11.cdr.car)), (tmp1126 = (is_term_equal_nboyer(sc_x_8, true_term_nboyer))), ((tmp1126 !== false) ? tmp1126 : (is_term_member_nboyer(sc_x_8, true_lst)))) !== false))\n          (sc_x_11 = (sc_x_11.cdr.cdr.car));\n        else if ((((x = (sc_x_11.cdr.car)), (tmp1125 = (is_term_equal_nboyer(x, false_term_nboyer))), ((tmp1125 !== false) ? tmp1125 : (is_term_member_nboyer(x, false_lst)))) !== false))\n          (sc_x_11 = (sc_x_11.cdr.cdr.cdr.car));\n        else if (((tautologyp_nboyer((sc_x_11.cdr.cdr.car), (new sc_Pair((sc_x_11.cdr.car), true_lst)), false_lst)) !== false)) {\n          (false_lst = (new sc_Pair((sc_x_11.cdr.car), false_lst)));\n          (sc_x_11 = (sc_x_11.cdr.cdr.cdr.car));\n        } else\n          return false;\n      else\n        return false;\n    }\n  };\n  (if_constructor_nboyer = \"\\u1E9C*\");\n  (rewrite_count_nboyer = (0));\n  rewrite_nboyer = function (term) {\n    var term2;\n    var sc_term_12;\n    var lst;\n    var symbol_record;\n    var sc_lst_13;\n    {\n      (++rewrite_count_nboyer);\n      if (!(term instanceof sc_Pair))\n        return term;\n      else {\n        (sc_term_12 = (new sc_Pair((term.car), ((sc_lst_13 = (term.cdr)), ((sc_lst_13 === null) ? null : (new sc_Pair((rewrite_nboyer((sc_lst_13.car))), (rewrite_args_nboyer((sc_lst_13.cdr))))))))));\n        (lst = ((symbol_record = (term.car)), (symbol_record[(1)])));\n        while (true) {\n          if ((lst === null))\n            return sc_term_12;\n          else if ((((term2 = ((lst.car).cdr.car)), (unify_subst_nboyer = null), (one_way_unify1_nboyer(sc_term_12, term2))) !== false))\n            return (rewrite_nboyer((apply_subst_nboyer(unify_subst_nboyer, ((lst.car).cdr.cdr.car)))));\n          else\n            (lst = (lst.cdr));\n        }\n      }\n    }\n  };\n  rewrite_args_nboyer = function (lst) {\n    var sc_lst_14;\n    return ((lst === null) ? null : (new sc_Pair((rewrite_nboyer((lst.car))), ((sc_lst_14 = (lst.cdr)), ((sc_lst_14 === null) ? null : (new sc_Pair((rewrite_nboyer((sc_lst_14.car))), (rewrite_args_nboyer((sc_lst_14.cdr))))))))));\n  };\n  (unify_subst_nboyer = \"\\u1E9C*\");\n  one_way_unify1_nboyer = function (term1, term2) {\n    var lst1;\n    var lst2;\n    var temp_temp;\n    if (!(term2 instanceof sc_Pair)) {\n      (temp_temp = (sc_assq(term2, unify_subst_nboyer)));\n      if ((temp_temp !== false))\n        return (is_term_equal_nboyer(term1, (temp_temp.cdr)));\n      else if ((sc_isNumber(term2)))\n        return (sc_isEqual(term1, term2));\n      else {\n        (unify_subst_nboyer = (new sc_Pair((new sc_Pair(term2, term1)), unify_subst_nboyer)));\n        return true;\n      }\n    } else if (!(term1 instanceof sc_Pair))\n      return false;\n    else if (((term1.car) === (term2.car))) {\n      (lst1 = (term1.cdr));\n      (lst2 = (term2.cdr));\n      while (true) {\n        if ((lst1 === null))\n          return (lst2 === null);\n        else if ((lst2 === null))\n          return false;\n        else if (((one_way_unify1_nboyer((lst1.car), (lst2.car))) !== false)) {\n          (lst1 = (lst1.cdr));\n          (lst2 = (lst2.cdr));\n        } else\n          return false;\n      }\n    } else\n      return false;\n  };\n  (false_term_nboyer = \"\\u1E9C*\");\n  (true_term_nboyer = \"\\u1E9C*\");\n  trans_of_implies1_nboyer = function (n) {\n    var sc_n_15;\n    return ((sc_isEqual(n, (1))) ? (sc_list(\"\\u1E9Cimplies\", (0), (1))) : (sc_list(\"\\u1E9Cand\", (sc_list(\"\\u1E9Cimplies\", (n - (1)), n)), ((sc_n_15 = (n - (1))), ((sc_isEqual(sc_n_15, (1))) ? (sc_list(\"\\u1E9Cimplies\", (0), (1))) : (sc_list(\"\\u1E9Cand\", (sc_list(\"\\u1E9Cimplies\", (sc_n_15 - (1)), sc_n_15)), (trans_of_implies1_nboyer((sc_n_15 - (1)))))))))));\n  };\n  is_term_equal_nboyer = function (x, y) {\n    var lst1;\n    var lst2;\n    var r2;\n    var r1;\n    if ((x instanceof sc_Pair))\n      if ((y instanceof sc_Pair))\n        if ((((r1 = (x.car)), (r2 = (y.car)), (r1 === r2)) !== false)) {\n          (lst1 = (x.cdr));\n          (lst2 = (y.cdr));\n          while (true) {\n            if ((lst1 === null))\n              return (lst2 === null);\n            else if ((lst2 === null))\n              return false;\n            else if (((is_term_equal_nboyer((lst1.car), (lst2.car))) !== false)) {\n              (lst1 = (lst1.cdr));\n              (lst2 = (lst2.cdr));\n            } else\n              return false;\n          }\n        } else\n          return false;\n      else\n        return false;\n    else\n      return (sc_isEqual(x, y));\n  };\n  is_term_member_nboyer = function (x, lst) {\n    var x;\n    var lst;\n    while (true) {\n      if ((lst === null))\n        return false;\n      else if (((is_term_equal_nboyer(x, (lst.car))) !== false))\n        return true;\n      else\n        (lst = (lst.cdr));\n    }\n  };\n  BgL_setupzd2boyerzd2 = function () {\n    var symbol_record;\n    var value;\n    var BgL_sc_symbolzd2record_16zd2;\n    var sym;\n    var sc_sym_17;\n    var term;\n    var lst;\n    var sc_term_18;\n    var sc_term_19;\n    {\n      (BgL_sc_za2symbolzd2recordszd2alistza2_2z00_nboyer = null);\n      (if_constructor_nboyer = (BgL_sc_symbolzd2ze3symbolzd2record_1ze3_nboyer(\"\\u1E9Cif\")));\n      (false_term_nboyer = ((sc_term_19 = (new sc_Pair(\"\\u1E9Cf\", null))), (!(sc_term_19 instanceof sc_Pair) ? sc_term_19 : (new sc_Pair((BgL_sc_symbolzd2ze3symbolzd2record_1ze3_nboyer((sc_term_19.car))), (translate_args_nboyer((sc_term_19.cdr))))))));\n      (true_term_nboyer = ((sc_term_18 = (new sc_Pair(\"\\u1E9Ct\", null))), (!(sc_term_18 instanceof sc_Pair) ? sc_term_18 : (new sc_Pair((BgL_sc_symbolzd2ze3symbolzd2record_1ze3_nboyer((sc_term_18.car))), (translate_args_nboyer((sc_term_18.cdr))))))));\n      (lst = sc_const_3_nboyer);\n      while (!(lst === null)) {\n        {\n          (term = (lst.car));\n          if (((term instanceof sc_Pair) && (((term.car) === \"\\u1E9Cequal\") && ((term.cdr.car) instanceof sc_Pair)))) {\n            (sc_sym_17 = ((term.cdr.car).car));\n            (value = (new sc_Pair((!(term instanceof sc_Pair) ? term : (new sc_Pair((BgL_sc_symbolzd2ze3symbolzd2record_1ze3_nboyer((term.car))), (translate_args_nboyer((term.cdr)))))), ((sym = ((term.cdr.car).car)), (BgL_sc_symbolzd2record_16zd2 = (BgL_sc_symbolzd2ze3symbolzd2record_1ze3_nboyer(sym))), (BgL_sc_symbolzd2record_16zd2[(1)])))));\n            (symbol_record = (BgL_sc_symbolzd2ze3symbolzd2record_1ze3_nboyer(sc_sym_17)));\n            (symbol_record[(1)] = value);\n          } else\n            (sc_error(\"ADD-LEMMA did not like term:  \", term));\n          (lst = (lst.cdr));\n        }\n      }\n      return true;\n    }\n  };\n  BgL_testzd2boyerzd2 = function (n) {\n    var optrOpnd;\n    var term;\n    var sc_n_20;\n    var answer;\n    var sc_term_21;\n    var sc_term_22;\n    {\n      (rewrite_count_nboyer = (0));\n      (term = sc_const_4_nboyer);\n      (sc_n_20 = n);\n      while (!(sc_n_20 === 0)) {\n        {\n          (term = (sc_list(\"\\u1E9Cor\", term, (new sc_Pair(\"\\u1E9Cf\", null)))));\n          (--sc_n_20);\n        }\n      }\n      (sc_term_22 = term);\n      if (!(sc_term_22 instanceof sc_Pair))\n        (optrOpnd = sc_term_22);\n      else\n        (optrOpnd = (new sc_Pair((BgL_sc_symbolzd2ze3symbolzd2record_1ze3_nboyer((sc_term_22.car))), (translate_args_nboyer((sc_term_22.cdr))))));\n      (sc_term_21 = (apply_subst_nboyer(((const_nboyer === null) ? null : (new sc_Pair((new sc_Pair((const_nboyer.car.car), (translate_term_nboyer((const_nboyer.car.cdr))))), (translate_alist_nboyer((const_nboyer.cdr)))))), optrOpnd)));\n      (answer = (tautologyp_nboyer((rewrite_nboyer(sc_term_21)), null, null)));\n      (sc_write(rewrite_count_nboyer));\n      (sc_display(\" rewrites\"));\n      (sc_newline());\n      if ((answer !== false))\n        return rewrite_count_nboyer;\n      else\n        return false;\n    }\n  };\n}\n/* Exported Variables */\nvar BgL_parsezd2ze3nbzd2treesze3;\nvar BgL_earleyzd2benchmarkzd2;\nvar BgL_parsezd2ze3parsedzf3zc2;\nvar test;\nvar BgL_parsezd2ze3treesz31;\nvar BgL_makezd2parserzd2;\n/* End Exports */\n\nvar const_earley;\n{\n  (const_earley = (new sc_Pair((new sc_Pair(\"\\u1E9Cs\", (new sc_Pair((new sc_Pair(\"\\u1E9Ca\", null)), (new sc_Pair((new sc_Pair(\"\\u1E9Cs\", (new sc_Pair(\"\\u1E9Cs\", null)))), null)))))), null)));\n  BgL_makezd2parserzd2 = function (grammar, lexer) {\n    var i;\n    var parser_descr;\n    var def_loop;\n    var nb_nts;\n    var names;\n    var steps;\n    var predictors;\n    var enders;\n    var starters;\n    var nts;\n    var sc_names_1;\n    var sc_steps_2;\n    var sc_predictors_3;\n    var sc_enders_4;\n    var sc_starters_5;\n    var nb_confs;\n    var BgL_sc_defzd2loop_6zd2;\n    var BgL_sc_nbzd2nts_7zd2;\n    var sc_nts_8;\n    var BgL_sc_defzd2loop_9zd2;\n    var ind;\n    {\n      ind = function (nt, sc_nts_10) {\n        var i;\n        {\n          (i = ((sc_nts_10.length) - (1)));\n          while (true) {\n            if ((i >= (0)))\n              if ((sc_isEqual((sc_nts_10[i]), nt)))\n                return i;\n              else\n                (--i);\n            else\n              return false;\n          }\n        }\n      };\n      (sc_nts_8 = ((BgL_sc_defzd2loop_9zd2 = function (defs, sc_nts_11) {\n        var rule_loop;\n        var head;\n        var def;\n        return ((defs instanceof sc_Pair) ? ((def = (defs.car)), (head = (def.car)), (rule_loop = function (rules, sc_nts_12) {\n          var nt;\n          var l;\n          var sc_nts_13;\n          var rule;\n          if ((rules instanceof sc_Pair)) {\n            (rule = (rules.car));\n            (l = rule);\n            (sc_nts_13 = sc_nts_12);\n            while ((l instanceof sc_Pair)) {\n              {\n                (nt = (l.car));\n                (l = (l.cdr));\n                (sc_nts_13 = (((sc_member(nt, sc_nts_13)) !== false) ? sc_nts_13 : (new sc_Pair(nt, sc_nts_13))));\n              }\n            }\n            return (rule_loop((rules.cdr), sc_nts_13));\n          } else\n            return (BgL_sc_defzd2loop_9zd2((defs.cdr), sc_nts_12));\n        }), (rule_loop((def.cdr), (((sc_member(head, sc_nts_11)) !== false) ? sc_nts_11 : (new sc_Pair(head, sc_nts_11)))))) : (sc_list2vector((sc_reverse(sc_nts_11)))));\n      }), (BgL_sc_defzd2loop_9zd2(grammar, null))));\n      (BgL_sc_nbzd2nts_7zd2 = (sc_nts_8.length));\n      (nb_confs = (((BgL_sc_defzd2loop_6zd2 = function (defs, BgL_sc_nbzd2confs_14zd2) {\n        var rule_loop;\n        var def;\n        return ((defs instanceof sc_Pair) ? ((def = (defs.car)), (rule_loop = function (rules, BgL_sc_nbzd2confs_15zd2) {\n          var l;\n          var BgL_sc_nbzd2confs_16zd2;\n          var rule;\n          if ((rules instanceof sc_Pair)) {\n            (rule = (rules.car));\n            (l = rule);\n            (BgL_sc_nbzd2confs_16zd2 = BgL_sc_nbzd2confs_15zd2);\n            while ((l instanceof sc_Pair)) {\n              {\n                (l = (l.cdr));\n                (++BgL_sc_nbzd2confs_16zd2);\n              }\n            }\n            return (rule_loop((rules.cdr), (BgL_sc_nbzd2confs_16zd2 + (1))));\n          } else\n            return (BgL_sc_defzd2loop_6zd2((defs.cdr), BgL_sc_nbzd2confs_15zd2));\n        }), (rule_loop((def.cdr), BgL_sc_nbzd2confs_14zd2))) : BgL_sc_nbzd2confs_14zd2);\n      }), (BgL_sc_defzd2loop_6zd2(grammar, (0)))) + BgL_sc_nbzd2nts_7zd2));\n      (sc_starters_5 = (sc_makeVector(BgL_sc_nbzd2nts_7zd2, null)));\n      (sc_enders_4 = (sc_makeVector(BgL_sc_nbzd2nts_7zd2, null)));\n      (sc_predictors_3 = (sc_makeVector(BgL_sc_nbzd2nts_7zd2, null)));\n      (sc_steps_2 = (sc_makeVector(nb_confs, false)));\n      (sc_names_1 = (sc_makeVector(nb_confs, false)));\n      (nts = sc_nts_8);\n      (starters = sc_starters_5);\n      (enders = sc_enders_4);\n      (predictors = sc_predictors_3);\n      (steps = sc_steps_2);\n      (names = sc_names_1);\n      (nb_nts = (sc_nts_8.length));\n      (i = (nb_nts - (1)));\n      while ((i >= (0))) {\n        {\n          (sc_steps_2[i] = (i - nb_nts));\n          (sc_names_1[i] = (sc_list((sc_nts_8[i]), (0))));\n          (sc_enders_4[i] = (sc_list(i)));\n          (--i);\n        }\n      }\n      def_loop = function (defs, conf) {\n        var rule_loop;\n        var head;\n        var def;\n        return ((defs instanceof sc_Pair) ? ((def = (defs.car)), (head = (def.car)), (rule_loop = function (rules, conf, rule_num) {\n          var i;\n          var sc_i_17;\n          var nt;\n          var l;\n          var sc_conf_18;\n          var sc_i_19;\n          var rule;\n          if ((rules instanceof sc_Pair)) {\n            (rule = (rules.car));\n            (names[conf] = (sc_list(head, rule_num)));\n            (sc_i_19 = (ind(head, nts)));\n            (starters[sc_i_19] = (new sc_Pair(conf, (starters[sc_i_19]))));\n            (l = rule);\n            (sc_conf_18 = conf);\n            while ((l instanceof sc_Pair)) {\n              {\n                (nt = (l.car));\n                (steps[sc_conf_18] = (ind(nt, nts)));\n                (sc_i_17 = (ind(nt, nts)));\n                (predictors[sc_i_17] = (new sc_Pair(sc_conf_18, (predictors[sc_i_17]))));\n                (l = (l.cdr));\n                (++sc_conf_18);\n              }\n            }\n            (steps[sc_conf_18] = ((ind(head, nts)) - nb_nts));\n            (i = (ind(head, nts)));\n            (enders[i] = (new sc_Pair(sc_conf_18, (enders[i]))));\n            return (rule_loop((rules.cdr), (sc_conf_18 + (1)), (rule_num + (1))));\n          } else\n            return (def_loop((defs.cdr), conf));\n        }), (rule_loop((def.cdr), conf, (1)))) : undefined);\n      };\n      (def_loop(grammar, (sc_nts_8.length)));\n      (parser_descr = [lexer, sc_nts_8, sc_starters_5, sc_enders_4, sc_predictors_3, sc_steps_2, sc_names_1]);\n      return function (input) {\n        var optrOpnd;\n        var sc_optrOpnd_20;\n        var sc_optrOpnd_21;\n        var sc_optrOpnd_22;\n        var loop1;\n        var BgL_sc_stateza2_23za2;\n        var toks;\n        var BgL_sc_nbzd2nts_24zd2;\n        var sc_steps_25;\n        var sc_enders_26;\n        var state_num;\n        var BgL_sc_statesza2_27za2;\n        var states;\n        var i;\n        var conf;\n        var l;\n        var tok_nts;\n        var sc_i_28;\n        var sc_i_29;\n        var l1;\n        var l2;\n        var tok;\n        var tail1129;\n        var L1125;\n        var goal_enders;\n        var BgL_sc_statesza2_30za2;\n        var BgL_sc_nbzd2nts_31zd2;\n        var BgL_sc_nbzd2confs_32zd2;\n        var nb_toks;\n        var goal_starters;\n        var sc_states_33;\n        var BgL_sc_nbzd2confs_34zd2;\n        var BgL_sc_nbzd2toks_35zd2;\n        var sc_toks_36;\n        var falseHead1128;\n        var sc_names_37;\n        var sc_steps_38;\n        var sc_predictors_39;\n        var sc_enders_40;\n        var sc_starters_41;\n        var sc_nts_42;\n        var lexer;\n        var sc_ind_43;\n        var make_states;\n        var BgL_sc_confzd2setzd2getza2_44za2;\n        var conf_set_merge_new_bang;\n        var conf_set_adjoin;\n        var BgL_sc_confzd2setzd2adjoinza2_45za2;\n        var BgL_sc_confzd2setzd2adjoinza2za2_46z00;\n        var conf_set_union;\n        var forw;\n        var is_parsed;\n        var deriv_trees;\n        var BgL_sc_derivzd2treesza2_47z70;\n        var nb_deriv_trees;\n        var BgL_sc_nbzd2derivzd2treesza2_48za2;\n        {\n          sc_ind_43 = function (nt, sc_nts_49) {\n            var i;\n            {\n              (i = ((sc_nts_49.length) - (1)));\n              while (true) {\n                if ((i >= (0)))\n                  if ((sc_isEqual((sc_nts_49[i]), nt)))\n                    return i;\n                  else\n                    (--i);\n                else\n                  return false;\n              }\n            }\n          };\n          make_states = function (BgL_sc_nbzd2toks_50zd2, BgL_sc_nbzd2confs_51zd2) {\n            var v;\n            var i;\n            var sc_states_52;\n            {\n              (sc_states_52 = (sc_makeVector((BgL_sc_nbzd2toks_50zd2 + (1)), false)));\n              (i = BgL_sc_nbzd2toks_50zd2);\n              while ((i >= (0))) {\n                {\n                  (v = (sc_makeVector((BgL_sc_nbzd2confs_51zd2 + (1)), false)));\n                  (v[(0)] = (-1));\n                  (sc_states_52[i] = v);\n                  (--i);\n                }\n              }\n              return sc_states_52;\n            }\n          };\n          BgL_sc_confzd2setzd2getza2_44za2 = function (state, BgL_sc_statezd2num_53zd2, sc_conf_54) {\n            var conf_set;\n            var BgL_sc_confzd2set_55zd2;\n            return ((BgL_sc_confzd2set_55zd2 = (state[(sc_conf_54 + (1))])), ((BgL_sc_confzd2set_55zd2 !== false) ? BgL_sc_confzd2set_55zd2 : ((conf_set = (sc_makeVector((BgL_sc_statezd2num_53zd2 + (6)), false))), (conf_set[(1)] = (-3)), (conf_set[(2)] = (-1)), (conf_set[(3)] = (-1)), (conf_set[(4)] = (-1)), (state[(sc_conf_54 + (1))] = conf_set), conf_set)));\n          };\n          conf_set_merge_new_bang = function (conf_set) {\n            return ((conf_set[((conf_set[(1)]) + (5))] = (conf_set[(4)])), (conf_set[(1)] = (conf_set[(3)])), (conf_set[(3)] = (-1)), (conf_set[(4)] = (-1)));\n          };\n          conf_set_adjoin = function (state, conf_set, sc_conf_56, i) {\n            var tail;\n            return ((tail = (conf_set[(3)])), (conf_set[(i + (5))] = (-1)), (conf_set[(tail + (5))] = i), (conf_set[(3)] = i), ((tail < (0)) ? ((conf_set[(0)] = (state[(0)])), (state[(0)] = sc_conf_56)) : undefined));\n          };\n          BgL_sc_confzd2setzd2adjoinza2_45za2 = function (sc_states_57, BgL_sc_statezd2num_58zd2, l, i) {\n            var conf_set;\n            var sc_conf_59;\n            var l1;\n            var state;\n            {\n              (state = (sc_states_57[BgL_sc_statezd2num_58zd2]));\n              (l1 = l);\n              while ((l1 instanceof sc_Pair)) {\n                {\n                  (sc_conf_59 = (l1.car));\n                  (conf_set = (BgL_sc_confzd2setzd2getza2_44za2(state, BgL_sc_statezd2num_58zd2, sc_conf_59)));\n                  if (((conf_set[(i + (5))]) === false)) {\n                    (conf_set_adjoin(state, conf_set, sc_conf_59, i));\n                    (l1 = (l1.cdr));\n                  } else\n                    (l1 = (l1.cdr));\n                }\n              }\n              return undefined;\n            }\n          };\n          BgL_sc_confzd2setzd2adjoinza2za2_46z00 = function (sc_states_60, BgL_sc_statesza2_61za2, BgL_sc_statezd2num_62zd2, sc_conf_63, i) {\n            var BgL_sc_confzd2setza2_64z70;\n            var BgL_sc_stateza2_65za2;\n            var conf_set;\n            var state;\n            return ((state = (sc_states_60[BgL_sc_statezd2num_62zd2])), ((((conf_set = (state[(sc_conf_63 + (1))])), ((conf_set !== false) ? (conf_set[(i + (5))]) : false)) !== false) ? ((BgL_sc_stateza2_65za2 = (BgL_sc_statesza2_61za2[BgL_sc_statezd2num_62zd2])), (BgL_sc_confzd2setza2_64z70 = (BgL_sc_confzd2setzd2getza2_44za2(BgL_sc_stateza2_65za2, BgL_sc_statezd2num_62zd2, sc_conf_63))), (((BgL_sc_confzd2setza2_64z70[(i + (5))]) === false) ? (conf_set_adjoin(BgL_sc_stateza2_65za2, BgL_sc_confzd2setza2_64z70, sc_conf_63, i)) : undefined), true) : false));\n          };\n          conf_set_union = function (state, conf_set, sc_conf_66, other_set) {\n            var i;\n            {\n              (i = (other_set[(2)]));\n              while ((i >= (0))) {\n                if (((conf_set[(i + (5))]) === false)) {\n                  (conf_set_adjoin(state, conf_set, sc_conf_66, i));\n                  (i = (other_set[(i + (5))]));\n                } else\n                  (i = (other_set[(i + (5))]));\n              }\n              return undefined;\n            }\n          };\n          forw = function (sc_states_67, BgL_sc_statezd2num_68zd2, sc_starters_69, sc_enders_70, sc_predictors_71, sc_steps_72, sc_nts_73) {\n            var next_set;\n            var next;\n            var conf_set;\n            var ender;\n            var l;\n            var starter_set;\n            var starter;\n            var sc_l_74;\n            var sc_loop1_75;\n            var head;\n            var BgL_sc_confzd2set_76zd2;\n            var BgL_sc_statezd2num_77zd2;\n            var state;\n            var sc_states_78;\n            var preds;\n            var BgL_sc_confzd2set_79zd2;\n            var step;\n            var sc_conf_80;\n            var BgL_sc_nbzd2nts_81zd2;\n            var sc_state_82;\n            {\n              (sc_state_82 = (sc_states_67[BgL_sc_statezd2num_68zd2]));\n              (BgL_sc_nbzd2nts_81zd2 = (sc_nts_73.length));\n              while (true) {\n                {\n                  (sc_conf_80 = (sc_state_82[(0)]));\n                  if ((sc_conf_80 >= (0))) {\n                    (step = (sc_steps_72[sc_conf_80]));\n                    (BgL_sc_confzd2set_79zd2 = (sc_state_82[(sc_conf_80 + (1))]));\n                    (head = (BgL_sc_confzd2set_79zd2[(4)]));\n                    (sc_state_82[(0)] = (BgL_sc_confzd2set_79zd2[(0)]));\n                    (conf_set_merge_new_bang(BgL_sc_confzd2set_79zd2));\n                    if ((step >= (0))) {\n                      (sc_l_74 = (sc_starters_69[step]));\n                      while ((sc_l_74 instanceof sc_Pair)) {\n                        {\n                          (starter = (sc_l_74.car));\n                          (starter_set = (BgL_sc_confzd2setzd2getza2_44za2(sc_state_82, BgL_sc_statezd2num_68zd2, starter)));\n                          if (((starter_set[(BgL_sc_statezd2num_68zd2 + (5))]) === false)) {\n                            (conf_set_adjoin(sc_state_82, starter_set, starter, BgL_sc_statezd2num_68zd2));\n                            (sc_l_74 = (sc_l_74.cdr));\n                          } else\n                            (sc_l_74 = (sc_l_74.cdr));\n                        }\n                      }\n                      (l = (sc_enders_70[step]));\n                      while ((l instanceof sc_Pair)) {\n                        {\n                          (ender = (l.car));\n                          if ((((conf_set = (sc_state_82[(ender + (1))])), ((conf_set !== false) ? (conf_set[(BgL_sc_statezd2num_68zd2 + (5))]) : false)) !== false)) {\n                            (next = (sc_conf_80 + (1)));\n                            (next_set = (BgL_sc_confzd2setzd2getza2_44za2(sc_state_82, BgL_sc_statezd2num_68zd2, next)));\n                            (conf_set_union(sc_state_82, next_set, next, BgL_sc_confzd2set_79zd2));\n                            (l = (l.cdr));\n                          } else\n                            (l = (l.cdr));\n                        }\n                      }\n                    } else {\n                      (preds = (sc_predictors_71[(step + BgL_sc_nbzd2nts_81zd2)]));\n                      (sc_states_78 = sc_states_67);\n                      (state = sc_state_82);\n                      (BgL_sc_statezd2num_77zd2 = BgL_sc_statezd2num_68zd2);\n                      (BgL_sc_confzd2set_76zd2 = BgL_sc_confzd2set_79zd2);\n                      sc_loop1_75 = function (l) {\n                        var sc_state_83;\n                        var BgL_sc_nextzd2set_84zd2;\n                        var sc_next_85;\n                        var pred_set;\n                        var i;\n                        var pred;\n                        if ((l instanceof sc_Pair)) {\n                          (pred = (l.car));\n                          (i = head);\n                          while ((i >= (0))) {\n                            {\n                              (pred_set = ((sc_state_83 = (sc_states_78[i])), (sc_state_83[(pred + (1))])));\n                              if ((pred_set !== false)) {\n                                (sc_next_85 = (pred + (1)));\n                                (BgL_sc_nextzd2set_84zd2 = (BgL_sc_confzd2setzd2getza2_44za2(state, BgL_sc_statezd2num_77zd2, sc_next_85)));\n                                (conf_set_union(state, BgL_sc_nextzd2set_84zd2, sc_next_85, pred_set));\n                              }\n                              (i = (BgL_sc_confzd2set_76zd2[(i + (5))]));\n                            }\n                          }\n                          return (sc_loop1_75((l.cdr)));\n                        } else\n                          return undefined;\n                      };\n                      (sc_loop1_75(preds));\n                    }\n                  } else\n                    return undefined;\n                }\n              }\n            }\n          };\n          is_parsed = function (nt, i, j, sc_nts_86, sc_enders_87, sc_states_88) {\n            var conf_set;\n            var state;\n            var sc_conf_89;\n            var l;\n            var BgL_sc_ntza2_90za2;\n            {\n              (BgL_sc_ntza2_90za2 = (sc_ind_43(nt, sc_nts_86)));\n              if ((BgL_sc_ntza2_90za2 !== false)) {\n                (sc_nts_86.length);\n                (l = (sc_enders_87[BgL_sc_ntza2_90za2]));\n                while (true) {\n                  if ((l instanceof sc_Pair)) {\n                    (sc_conf_89 = (l.car));\n                    if ((((state = (sc_states_88[j])), (conf_set = (state[(sc_conf_89 + (1))])), ((conf_set !== false) ? (conf_set[(i + (5))]) : false)) !== false))\n                      return true;\n                    else\n                      (l = (l.cdr));\n                  } else\n                    return false;\n                }\n              } else\n                return false;\n            }\n          };\n          deriv_trees = function (sc_conf_91, i, j, sc_enders_92, sc_steps_93, sc_names_94, sc_toks_95, sc_states_96, BgL_sc_nbzd2nts_97zd2) {\n            var sc_loop1_98;\n            var prev;\n            var name;\n            return ((name = (sc_names_94[sc_conf_91])), ((name !== false) ? ((sc_conf_91 < BgL_sc_nbzd2nts_97zd2) ? (sc_list((sc_list(name, ((sc_toks_95[i]).car))))) : (sc_list((sc_list(name))))) : ((prev = (sc_conf_91 - (1))), (sc_loop1_98 = function (l1, l2) {\n              var loop2;\n              var ender_set;\n              var state;\n              var ender;\n              var l1;\n              var l2;\n              while (true) {\n                if ((l1 instanceof sc_Pair)) {\n                  (ender = (l1.car));\n                  (ender_set = ((state = (sc_states_96[j])), (state[(ender + (1))])));\n                  if ((ender_set !== false)) {\n                    loop2 = function (k, l2) {\n                      var loop3;\n                      var ender_trees;\n                      var prev_trees;\n                      var conf_set;\n                      var sc_state_99;\n                      var k;\n                      var l2;\n                      while (true) {\n                        if ((k >= (0)))\n                          if (((k >= i) && (((sc_state_99 = (sc_states_96[k])), (conf_set = (sc_state_99[(prev + (1))])), ((conf_set !== false) ? (conf_set[(i + (5))]) : false)) !== false))) {\n                            (prev_trees = (deriv_trees(prev, i, k, sc_enders_92, sc_steps_93, sc_names_94, sc_toks_95, sc_states_96, BgL_sc_nbzd2nts_97zd2)));\n                            (ender_trees = (deriv_trees(ender, k, j, sc_enders_92, sc_steps_93, sc_names_94, sc_toks_95, sc_states_96, BgL_sc_nbzd2nts_97zd2)));\n                            loop3 = function (l3, l2) {\n                              var l4;\n                              var sc_l2_100;\n                              var ender_tree;\n                              if ((l3 instanceof sc_Pair)) {\n                                (ender_tree = (sc_list((l3.car))));\n                                (l4 = prev_trees);\n                                (sc_l2_100 = l2);\n                                while ((l4 instanceof sc_Pair)) {\n                                  {\n                                    (sc_l2_100 = (new sc_Pair((sc_append((l4.car), ender_tree)), sc_l2_100)));\n                                    (l4 = (l4.cdr));\n                                  }\n                                }\n                                return (loop3((l3.cdr), sc_l2_100));\n                              } else\n                                return (loop2((ender_set[(k + (5))]), l2));\n                            };\n                            return (loop3(ender_trees, l2));\n                          } else\n                            (k = (ender_set[(k + (5))]));\n                        else\n                          return (sc_loop1_98((l1.cdr), l2));\n                      }\n                    };\n                    return (loop2((ender_set[(2)]), l2));\n                  } else\n                    (l1 = (l1.cdr));\n                } else\n                  return l2;\n              }\n            }), (sc_loop1_98((sc_enders_92[(sc_steps_93[prev])]), null)))));\n          };\n          BgL_sc_derivzd2treesza2_47z70 = function (nt, i, j, sc_nts_101, sc_enders_102, sc_steps_103, sc_names_104, sc_toks_105, sc_states_106) {\n            var conf_set;\n            var state;\n            var sc_conf_107;\n            var l;\n            var trees;\n            var BgL_sc_nbzd2nts_108zd2;\n            var BgL_sc_ntza2_109za2;\n            {\n              (BgL_sc_ntza2_109za2 = (sc_ind_43(nt, sc_nts_101)));\n              if ((BgL_sc_ntza2_109za2 !== false)) {\n                (BgL_sc_nbzd2nts_108zd2 = (sc_nts_101.length));\n                (l = (sc_enders_102[BgL_sc_ntza2_109za2]));\n                (trees = null);\n                while ((l instanceof sc_Pair)) {\n                  {\n                    (sc_conf_107 = (l.car));\n                    if ((((state = (sc_states_106[j])), (conf_set = (state[(sc_conf_107 + (1))])), ((conf_set !== false) ? (conf_set[(i + (5))]) : false)) !== false)) {\n                      (l = (l.cdr));\n                      (trees = (sc_append((deriv_trees(sc_conf_107, i, j, sc_enders_102, sc_steps_103, sc_names_104, sc_toks_105, sc_states_106, BgL_sc_nbzd2nts_108zd2)), trees)));\n                    } else\n                      (l = (l.cdr));\n                  }\n                }\n                return trees;\n              } else\n                return false;\n            }\n          };\n          nb_deriv_trees = function (sc_conf_110, i, j, sc_enders_111, sc_steps_112, sc_toks_113, sc_states_114, BgL_sc_nbzd2nts_115zd2) {\n            var sc_loop1_116;\n            var tmp1124;\n            var prev;\n            return ((prev = (sc_conf_110 - (1))), ((((tmp1124 = (sc_conf_110 < BgL_sc_nbzd2nts_115zd2)), ((tmp1124 !== false) ? tmp1124 : ((sc_steps_112[prev]) < (0)))) !== false) ? (1) : ((sc_loop1_116 = function (l, sc_n_118) {\n              var nb_ender_trees;\n              var nb_prev_trees;\n              var conf_set;\n              var state;\n              var k;\n              var n;\n              var ender_set;\n              var sc_state_117;\n              var ender;\n              var l;\n              var sc_n_118;\n              while (true) {\n                if ((l instanceof sc_Pair)) {\n                  (ender = (l.car));\n                  (ender_set = ((sc_state_117 = (sc_states_114[j])), (sc_state_117[(ender + (1))])));\n                  if ((ender_set !== false)) {\n                    (k = (ender_set[(2)]));\n                    (n = sc_n_118);\n                    while ((k >= (0))) {\n                      if (((k >= i) && (((state = (sc_states_114[k])), (conf_set = (state[(prev + (1))])), ((conf_set !== false) ? (conf_set[(i + (5))]) : false)) !== false))) {\n                        (nb_prev_trees = (nb_deriv_trees(prev, i, k, sc_enders_111, sc_steps_112, sc_toks_113, sc_states_114, BgL_sc_nbzd2nts_115zd2)));\n                        (nb_ender_trees = (nb_deriv_trees(ender, k, j, sc_enders_111, sc_steps_112, sc_toks_113, sc_states_114, BgL_sc_nbzd2nts_115zd2)));\n                        (k = (ender_set[(k + (5))]));\n                        (n += (nb_prev_trees * nb_ender_trees));\n                      } else\n                        (k = (ender_set[(k + (5))]));\n                    }\n                    return (sc_loop1_116((l.cdr), n));\n                  } else\n                    (l = (l.cdr));\n                } else\n                  return sc_n_118;\n              }\n            }), (sc_loop1_116((sc_enders_111[(sc_steps_112[prev])]), (0))))));\n          };\n          BgL_sc_nbzd2derivzd2treesza2_48za2 = function (nt, i, j, sc_nts_119, sc_enders_120, sc_steps_121, sc_toks_122, sc_states_123) {\n            var conf_set;\n            var state;\n            var sc_conf_124;\n            var l;\n            var nb_trees;\n            var BgL_sc_nbzd2nts_125zd2;\n            var BgL_sc_ntza2_126za2;\n            {\n              (BgL_sc_ntza2_126za2 = (sc_ind_43(nt, sc_nts_119)));\n              if ((BgL_sc_ntza2_126za2 !== false)) {\n                (BgL_sc_nbzd2nts_125zd2 = (sc_nts_119.length));\n                (l = (sc_enders_120[BgL_sc_ntza2_126za2]));\n                (nb_trees = (0));\n                while ((l instanceof sc_Pair)) {\n                  {\n                    (sc_conf_124 = (l.car));\n                    if ((((state = (sc_states_123[j])), (conf_set = (state[(sc_conf_124 + (1))])), ((conf_set !== false) ? (conf_set[(i + (5))]) : false)) !== false)) {\n                      (l = (l.cdr));\n                      (nb_trees = ((nb_deriv_trees(sc_conf_124, i, j, sc_enders_120, sc_steps_121, sc_toks_122, sc_states_123, BgL_sc_nbzd2nts_125zd2)) + nb_trees));\n                    } else\n                      (l = (l.cdr));\n                  }\n                }\n                return nb_trees;\n              } else\n                return false;\n            }\n          };\n          (lexer = (parser_descr[(0)]));\n          (sc_nts_42 = (parser_descr[(1)]));\n          (sc_starters_41 = (parser_descr[(2)]));\n          (sc_enders_40 = (parser_descr[(3)]));\n          (sc_predictors_39 = (parser_descr[(4)]));\n          (sc_steps_38 = (parser_descr[(5)]));\n          (sc_names_37 = (parser_descr[(6)]));\n          (falseHead1128 = (new sc_Pair(null, null)));\n          (L1125 = (lexer(input)));\n          (tail1129 = falseHead1128);\n          while (!(L1125 === null)) {\n            {\n              (tok = (L1125.car));\n              (l1 = (tok.cdr));\n              (l2 = null);\n              while ((l1 instanceof sc_Pair)) {\n                {\n                  (sc_i_29 = (sc_ind_43((l1.car), sc_nts_42)));\n                  if ((sc_i_29 !== false)) {\n                    (l1 = (l1.cdr));\n                    (l2 = (new sc_Pair(sc_i_29, l2)));\n                  } else\n                    (l1 = (l1.cdr));\n                }\n              }\n              (sc_optrOpnd_22 = (new sc_Pair((tok.car), (sc_reverse(l2)))));\n              (sc_optrOpnd_21 = (new sc_Pair(sc_optrOpnd_22, null)));\n              (tail1129.cdr = sc_optrOpnd_21);\n              (tail1129 = (tail1129.cdr));\n              (L1125 = (L1125.cdr));\n            }\n          }\n          (sc_optrOpnd_20 = (falseHead1128.cdr));\n          (sc_toks_36 = (sc_list2vector(sc_optrOpnd_20)));\n          (BgL_sc_nbzd2toks_35zd2 = (sc_toks_36.length));\n          (BgL_sc_nbzd2confs_34zd2 = (sc_steps_38.length));\n          (sc_states_33 = (make_states(BgL_sc_nbzd2toks_35zd2, BgL_sc_nbzd2confs_34zd2)));\n          (goal_starters = (sc_starters_41[(0)]));\n          (BgL_sc_confzd2setzd2adjoinza2_45za2(sc_states_33, (0), goal_starters, (0)));\n          (forw(sc_states_33, (0), sc_starters_41, sc_enders_40, sc_predictors_39, sc_steps_38, sc_nts_42));\n          (sc_i_28 = (0));\n          while ((sc_i_28 < BgL_sc_nbzd2toks_35zd2)) {\n            {\n              (tok_nts = ((sc_toks_36[sc_i_28]).cdr));\n              (BgL_sc_confzd2setzd2adjoinza2_45za2(sc_states_33, (sc_i_28 + (1)), tok_nts, sc_i_28));\n              (forw(sc_states_33, (sc_i_28 + (1)), sc_starters_41, sc_enders_40, sc_predictors_39, sc_steps_38, sc_nts_42));\n              (++sc_i_28);\n            }\n          }\n          (nb_toks = (sc_toks_36.length));\n          (BgL_sc_nbzd2confs_32zd2 = (sc_steps_38.length));\n          (BgL_sc_nbzd2nts_31zd2 = (sc_nts_42.length));\n          (BgL_sc_statesza2_30za2 = (make_states(nb_toks, BgL_sc_nbzd2confs_32zd2)));\n          (goal_enders = (sc_enders_40[(0)]));\n          (l = goal_enders);\n          while ((l instanceof sc_Pair)) {\n            {\n              (conf = (l.car));\n              (BgL_sc_confzd2setzd2adjoinza2za2_46z00(sc_states_33, BgL_sc_statesza2_30za2, nb_toks, conf, (0)));\n              (l = (l.cdr));\n            }\n          }\n          (i = nb_toks);\n          while ((i >= (0))) {\n            {\n              (states = sc_states_33);\n              (BgL_sc_statesza2_27za2 = BgL_sc_statesza2_30za2);\n              (state_num = i);\n              (sc_enders_26 = sc_enders_40);\n              (sc_steps_25 = sc_steps_38);\n              (BgL_sc_nbzd2nts_24zd2 = BgL_sc_nbzd2nts_31zd2);\n              (toks = sc_toks_36);\n              (BgL_sc_stateza2_23za2 = (BgL_sc_statesza2_30za2[i]));\n              loop1 = function () {\n                var sc_loop1_127;\n                var prev;\n                var BgL_sc_statesza2_128za2;\n                var sc_states_129;\n                var j;\n                var i;\n                var sc_i_130;\n                var head;\n                var conf_set;\n                var sc_conf_131;\n                {\n                  (sc_conf_131 = (BgL_sc_stateza2_23za2[(0)]));\n                  if ((sc_conf_131 >= (0))) {\n                    (conf_set = (BgL_sc_stateza2_23za2[(sc_conf_131 + (1))]));\n                    (head = (conf_set[(4)]));\n                    (BgL_sc_stateza2_23za2[(0)] = (conf_set[(0)]));\n                    (conf_set_merge_new_bang(conf_set));\n                    (sc_i_130 = head);\n                    while ((sc_i_130 >= (0))) {\n                      {\n                        (i = sc_i_130);\n                        (j = state_num);\n                        (sc_states_129 = states);\n                        (BgL_sc_statesza2_128za2 = BgL_sc_statesza2_27za2);\n                        (prev = (sc_conf_131 - (1)));\n                        if (((sc_conf_131 >= BgL_sc_nbzd2nts_24zd2) && ((sc_steps_25[prev]) >= (0)))) {\n                          sc_loop1_127 = function (l) {\n                            var k;\n                            var ender_set;\n                            var state;\n                            var ender;\n                            var l;\n                            while (true) {\n                              if ((l instanceof sc_Pair)) {\n                                (ender = (l.car));\n                                (ender_set = ((state = (sc_states_129[j])), (state[(ender + (1))])));\n                                if ((ender_set !== false)) {\n                                  (k = (ender_set[(2)]));\n                                  while ((k >= (0))) {\n                                    {\n                                      if ((k >= i))\n                                        if (((BgL_sc_confzd2setzd2adjoinza2za2_46z00(sc_states_129, BgL_sc_statesza2_128za2, k, prev, i)) !== false))\n                                          (BgL_sc_confzd2setzd2adjoinza2za2_46z00(sc_states_129, BgL_sc_statesza2_128za2, j, ender, k));\n                                      (k = (ender_set[(k + (5))]));\n                                    }\n                                  }\n                                  return (sc_loop1_127((l.cdr)));\n                                } else\n                                  (l = (l.cdr));\n                              } else\n                                return undefined;\n                            }\n                          };\n                          (sc_loop1_127((sc_enders_26[(sc_steps_25[prev])])));\n                        }\n                        (sc_i_130 = (conf_set[(sc_i_130 + (5))]));\n                      }\n                    }\n                    return (loop1());\n                  } else\n                    return undefined;\n                }\n              };\n              (loop1());\n              (--i);\n            }\n          }\n          (optrOpnd = BgL_sc_statesza2_30za2);\n          return [sc_nts_42, sc_starters_41, sc_enders_40, sc_predictors_39, sc_steps_38, sc_names_37, sc_toks_36, optrOpnd, is_parsed, BgL_sc_derivzd2treesza2_47z70, BgL_sc_nbzd2derivzd2treesza2_48za2];\n        }\n      };\n    }\n  };\n  BgL_parsezd2ze3parsedzf3zc2 = function (parse, nt, i, j) {\n    var is_parsed;\n    var states;\n    var enders;\n    var nts;\n    return ((nts = (parse[(0)])), (enders = (parse[(2)])), (states = (parse[(7)])), (is_parsed = (parse[(8)])), (is_parsed(nt, i, j, nts, enders, states)));\n  };\n  BgL_parsezd2ze3treesz31 = function (parse, nt, i, j) {\n    var BgL_sc_derivzd2treesza2_132z70;\n    var states;\n    var toks;\n    var names;\n    var steps;\n    var enders;\n    var nts;\n    return ((nts = (parse[(0)])), (enders = (parse[(2)])), (steps = (parse[(4)])), (names = (parse[(5)])), (toks = (parse[(6)])), (states = (parse[(7)])), (BgL_sc_derivzd2treesza2_132z70 = (parse[(9)])), (BgL_sc_derivzd2treesza2_132z70(nt, i, j, nts, enders, steps, names, toks, states)));\n  };\n  BgL_parsezd2ze3nbzd2treesze3 = function (parse, nt, i, j) {\n    var BgL_sc_nbzd2derivzd2treesza2_133za2;\n    var states;\n    var toks;\n    var steps;\n    var enders;\n    var nts;\n    return ((nts = (parse[(0)])), (enders = (parse[(2)])), (steps = (parse[(4)])), (toks = (parse[(6)])), (states = (parse[(7)])), (BgL_sc_nbzd2derivzd2treesza2_133za2 = (parse[(10)])), (BgL_sc_nbzd2derivzd2treesza2_133za2(nt, i, j, nts, enders, steps, toks, states)));\n  };\n  test = function (k) {\n    var x;\n    var p;\n    return ((p = (BgL_makezd2parserzd2(const_earley, function (l) {\n      var sc_x_134;\n      var tail1134;\n      var L1130;\n      var falseHead1133;\n      {\n        (falseHead1133 = (new sc_Pair(null, null)));\n        (tail1134 = falseHead1133);\n        (L1130 = l);\n        while (!(L1130 === null)) {\n          {\n            (tail1134.cdr = (new sc_Pair(((sc_x_134 = (L1130.car)), (sc_list(sc_x_134, sc_x_134))), null)));\n            (tail1134 = (tail1134.cdr));\n            (L1130 = (L1130.cdr));\n          }\n        }\n        return (falseHead1133.cdr);\n      }\n    }))), (x = (p((sc_vector2list((sc_makeVector(k, \"\\u1E9Ca\"))))))), (sc_length((BgL_parsezd2ze3treesz31(x, \"\\u1E9Cs\", (0), k)))));\n  };\n  BgL_earleyzd2benchmarkzd2 = function () {\n    var args = null;\n    for (var sc_tmp = arguments.length - 1; sc_tmp >= 0; sc_tmp--) {\n      args = sc_cons(arguments[sc_tmp], args);\n    }\n    var k;\n    return ((k = ((args === null) ? (7) : (args.car))), (BgL_runzd2benchmarkzd2(\"earley\", (1), function () {\n      return (test(k));\n    }, function (result) {\n      return ((sc_display(result)), (sc_newline()), result == 132);\n    })));\n  };\n}\n\n\n/************* END OF GENERATED CODE *************/\n// Invoke this function to run a benchmark.\n// The first argument is a string identifying the benchmark.\n// The second argument is the number of times to run the benchmark.\n// The third argument is a function that runs the benchmark.\n// The fourth argument is a unary function that warns if the result\n// returned by the benchmark is incorrect.\n//\n// Example:\n// RunBenchmark(\"new Array()\",\n//              1,\n//              function () { new Array(1000000); }\n//              function (v) {\n//                return (v instanceof Array) && (v.length == 1000000);\n//              });\n\nSC_DEFAULT_OUT = new sc_GenericOutputPort(function (s) {\n});\nSC_ERROR_OUT = SC_DEFAULT_OUT;\n\nfunction RunBenchmark(name, count, run, warn) {\n  for (var n = 0; n < count; ++n) {\n    var result = run();\n    if (!warn(result)) {\n      throw new Error(\"Earley or Boyer did incorrect number of rewrites\");\n    }\n  }\n}\n\nvar BgL_runzd2benchmarkzd2 = RunBenchmark;\n\nvar EarleyBoyer = new BenchmarkSuite('EarleyBoyer', 666463, [\n  new Benchmark(\"Earley\", function () {\n    BgL_earleyzd2benchmarkzd2();\n  }),\n  new Benchmark(\"Boyer\", function () {\n    BgL_nboyerzd2benchmarkzd2();\n  })\n]);\n\n/* run_harness.js */\nvar print = console.log;\n\nfunction Run() {\n  BenchmarkSuite.RunSuites({\n    NotifyStep: ShowProgress,\n    NotifyError: AddError,\n    NotifyResult: AddResult,\n    NotifyScore: AddScore,\n  });\n}\n\nvar harnessErrorCount = 0;\n\nfunction ShowProgress(name) {\n  print(\"PROGRESS\", name);\n}\n\nfunction AddError(name, error) {\n  print(\"ERROR\", name, error);\n  print(error.stack);\n  harnessErrorCount++;\n}\n\nfunction AddResult(name, result) {\n  print(\"RESULT\", name, result);\n}\n\nfunction AddScore(score) {\n  print(\"SCORE\", score);\n}\n\nfunction main() {\n  Run();\n}\n"
  },
  {
    "path": "benches/scripts/v8-benches/navier-stokes.js",
    "content": "\"use strict\";\n\"use strip\";\n// Copyright 2012 the V8 project authors. All rights reserved.\n// Redistribution and use in source and binary forms, with or without\n// modification, are permitted provided that the following conditions are\n// met:\n//\n//     * Redistributions of source code must retain the above copyright\n//       notice, this list of conditions and the following disclaimer.\n//     * Redistributions in binary form must reproduce the above\n//       copyright notice, this list of conditions and the following\n//       disclaimer in the documentation and/or other materials provided\n//       with the distribution.\n//     * Neither the name of Google Inc. nor the names of its\n//       contributors may be used to endorse or promote products derived\n//       from this software without specific prior written permission.\n//\n// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS\n// \"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT\n// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR\n// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT\n// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,\n// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT\n// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,\n// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY\n// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n\n// Simple framework for running the benchmark suites and\n// computing a score based on the timing measurements.\n\n\n// A benchmark has a name (string) and a function that will be run to\n// do the performance measurement. The optional setup and tearDown\n// arguments are functions that will be invoked before and after\n// running the benchmark, but the running time of these functions will\n// not be accounted for in the benchmark score.\nfunction Benchmark(name, run, setup, tearDown) {\n  this.name = name;\n  this.run = run;\n  this.Setup = setup ? setup : function () {\n  };\n  this.TearDown = tearDown ? tearDown : function () {\n  };\n}\n\n\n// Benchmark results hold the benchmark and the measured time used to\n// run the benchmark. The benchmark score is computed later once a\n// full benchmark suite has run to completion.\nfunction BenchmarkResult(benchmark, time) {\n  this.benchmark = benchmark;\n  this.time = time;\n}\n\n\n// Automatically convert results to numbers. Used by the geometric\n// mean computation.\nBenchmarkResult.prototype.valueOf = function () {\n  return this.time;\n};\n\n\n// Suites of benchmarks consist of a name and the set of benchmarks in\n// addition to the reference timing that the final score will be based\n// on. This way, all scores are relative to a reference run and higher\n// scores implies better performance.\nfunction BenchmarkSuite(name, reference, benchmarks) {\n  this.name = name;\n  this.reference = reference;\n  this.benchmarks = benchmarks;\n  BenchmarkSuite.suites.push(this);\n}\n\n\n// Keep track of all declared benchmark suites.\nBenchmarkSuite.suites = [];\n\n\n// Scores are not comparable across versions. Bump the version if\n// you're making changes that will affect that scores, e.g. if you add\n// a new benchmark or change an existing one.\nBenchmarkSuite.version = '7';\n\n\n// To make the benchmark results predictable, we replace Math.random\n// with a 100% deterministic alternative.\nMath.random = (function () {\n  var seed = 49734321;\n  return function () {\n    // Robert Jenkins' 32 bit integer hash function.\n    seed = ((seed + 0x7ed55d16) + (seed << 12)) & 0xffffffff;\n    seed = ((seed ^ 0xc761c23c) ^ (seed >>> 19)) & 0xffffffff;\n    seed = ((seed + 0x165667b1) + (seed << 5)) & 0xffffffff;\n    seed = ((seed + 0xd3a2646c) ^ (seed << 9)) & 0xffffffff;\n    seed = ((seed + 0xfd7046c5) + (seed << 3)) & 0xffffffff;\n    seed = ((seed ^ 0xb55a4f09) ^ (seed >>> 16)) & 0xffffffff;\n    return (seed & 0xfffffff) / 0x10000000;\n  };\n})();\n\n\n// Runs all registered benchmark suites and optionally yields between\n// each individual benchmark to avoid running for too long in the\n// context of browsers. Once done, the final score is reported to the\n// runner.\nBenchmarkSuite.RunSuites = function (runner) {\n  var continuation = null;\n  var suites = BenchmarkSuite.suites;\n  var length = suites.length;\n  BenchmarkSuite.scores = [];\n  var index = 0;\n\n  function RunStep() {\n    while (continuation || index < length) {\n      if (continuation) {\n        continuation = continuation();\n      } else {\n        var suite = suites[index++];\n        if (runner.NotifyStart) runner.NotifyStart(suite.name);\n        continuation = suite.RunStep(runner);\n      }\n      if (continuation && typeof window != 'undefined' && window.setTimeout) {\n        window.setTimeout(RunStep, 25);\n        return;\n      }\n    }\n    if (runner.NotifyScore) {\n      var score = BenchmarkSuite.GeometricMean(BenchmarkSuite.scores);\n      var formatted = BenchmarkSuite.FormatScore(100 * score);\n      runner.NotifyScore(formatted);\n    }\n  }\n\n  RunStep();\n};\n\n\n// Counts the total number of registered benchmarks. Useful for\n// showing progress as a percentage.\nBenchmarkSuite.CountBenchmarks = function () {\n  var result = 0;\n  var suites = BenchmarkSuite.suites;\n  for (var i = 0; i < suites.length; i++) {\n    result += suites[i].benchmarks.length;\n  }\n  return result;\n};\n\n\n// Computes the geometric mean of a set of numbers.\nBenchmarkSuite.GeometricMean = function (numbers) {\n  var log = 0;\n  for (var i = 0; i < numbers.length; i++) {\n    log += Math.log(numbers[i]);\n  }\n  return Math.pow(Math.E, log / numbers.length);\n};\n\n\n// Converts a score value to a string with at least three significant\n// digits.\nBenchmarkSuite.FormatScore = function (value) {\n  if (value > 100) {\n    return value.toFixed(0);\n  } else {\n    return value.toPrecision(3);\n  }\n};\n\n// Notifies the runner that we're done running a single benchmark in\n// the benchmark suite. This can be useful to report progress.\nBenchmarkSuite.prototype.NotifyStep = function (result) {\n  this.results.push(result);\n  if (this.runner.NotifyStep) this.runner.NotifyStep(result.benchmark.name);\n};\n\n\n// Notifies the runner that we're done with running a suite and that\n// we have a result which can be reported to the user if needed.\nBenchmarkSuite.prototype.NotifyResult = function () {\n  var mean = BenchmarkSuite.GeometricMean(this.results);\n  var score = this.reference / mean;\n  BenchmarkSuite.scores.push(score);\n  if (this.runner.NotifyResult) {\n    var formatted = BenchmarkSuite.FormatScore(100 * score);\n    this.runner.NotifyResult(this.name, formatted);\n  }\n};\n\n\n// Notifies the runner that running a benchmark resulted in an error.\nBenchmarkSuite.prototype.NotifyError = function (error) {\n  if (this.runner.NotifyError) {\n    this.runner.NotifyError(this.name, error);\n  }\n  if (this.runner.NotifyStep) {\n    this.runner.NotifyStep(this.name);\n  }\n};\n\n\n// Runs a single benchmark for at least a second and computes the\n// average time it takes to run a single iteration.\nBenchmarkSuite.prototype.RunSingleBenchmark = function (benchmark, data) {\n  function Measure(data) {\n    var elapsed = 0;\n    var start = new Date();\n    for (var n = 0; elapsed < 1000; n++) {\n      benchmark.run();\n      elapsed = new Date() - start;\n    }\n    if (data != null) {\n      data.runs += n;\n      data.elapsed += elapsed;\n    }\n  }\n\n  if (data == null) {\n    // Measure the benchmark once for warm up and throw the result\n    // away. Return a fresh data object.\n    Measure(null);\n    return {runs: 0, elapsed: 0};\n  } else {\n    Measure(data);\n    // If we've run too few iterations, we continue for another second.\n    if (data.runs < 32) return data;\n    var usec = (data.elapsed * 1000) / data.runs;\n    this.NotifyStep(new BenchmarkResult(benchmark, usec));\n    return null;\n  }\n};\n\n\n// This function starts running a suite, but stops between each\n// individual benchmark in the suite and returns a continuation\n// function which can be invoked to run the next benchmark. Once the\n// last benchmark has been executed, null is returned.\nBenchmarkSuite.prototype.RunStep = function (runner) {\n  this.results = [];\n  this.runner = runner;\n  var length = this.benchmarks.length;\n  var index = 0;\n  var suite = this;\n  var data;\n\n  // Run the setup, the actual benchmark, and the tear down in three\n  // separate steps to allow the framework to yield between any of the\n  // steps.\n\n  function RunNextSetup() {\n    if (index < length) {\n      try {\n        suite.benchmarks[index].Setup();\n      } catch (e) {\n        suite.NotifyError(e);\n        return null;\n      }\n      return RunNextBenchmark;\n    }\n    suite.NotifyResult();\n    return null;\n  }\n\n  function RunNextBenchmark() {\n    try {\n      data = suite.RunSingleBenchmark(suite.benchmarks[index], data);\n    } catch (e) {\n      suite.NotifyError(e);\n      return null;\n    }\n    // If data is null, we're done with this benchmark.\n    return (data == null) ? RunNextTearDown : RunNextBenchmark();\n  }\n\n  function RunNextTearDown() {\n    try {\n      suite.benchmarks[index++].TearDown();\n    } catch (e) {\n      suite.NotifyError(e);\n      return null;\n    }\n    return RunNextSetup;\n  }\n\n  // Start out running the setup.\n  return RunNextSetup();\n};\n\n/**\n * Copyright 2012 the V8 project authors. All rights reserved.\n * Copyright 2009 Oliver Hunt <http://nerget.com>\n *\n * Permission is hereby granted, free of charge, to any person\n * obtaining a copy of this software and associated documentation\n * files (the \"Software\"), to deal in the Software without\n * restriction, including without limitation the rights to use,\n * copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the\n * Software is furnished to do so, subject to the following\n * conditions:\n *\n * The above copyright notice and this permission notice shall be\n * included in all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES\n * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\n * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT\n * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,\n * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR\n * OTHER DEALINGS IN THE SOFTWARE.\n */\n\nvar solver = null;\n\nfunction runNavierStokes() {\n  solver.update();\n}\n\nfunction setupNavierStokes() {\n  solver = new FluidField(null);\n  solver.setResolution(128, 128);\n  solver.setIterations(20);\n  solver.setDisplayFunction(function () {\n  });\n  solver.setUICallback(prepareFrame);\n  solver.reset();\n}\n\nfunction tearDownNavierStokes() {\n  solver = null;\n}\n\nfunction addPoints(field) {\n  var n = 64;\n  for (var i = 1; i <= n; i++) {\n    field.setVelocity(i, i, n, n);\n    field.setDensity(i, i, 5);\n    field.setVelocity(i, n - i, -n, -n);\n    field.setDensity(i, n - i, 20);\n    field.setVelocity(128 - i, n + i, -n, -n);\n    field.setDensity(128 - i, n + i, 30);\n  }\n}\n\nvar framesTillAddingPoints = 0;\nvar framesBetweenAddingPoints = 5;\n\nfunction prepareFrame(field) {\n  if (framesTillAddingPoints == 0) {\n    addPoints(field);\n    framesTillAddingPoints = framesBetweenAddingPoints;\n    framesBetweenAddingPoints++;\n  } else {\n    framesTillAddingPoints--;\n  }\n}\n\n// Code from Oliver Hunt (http://nerget.com/fluidSim/pressure.js) starts here.\nfunction FluidField(canvas) {\n  function addFields(x, s, dt) {\n    for (var i = 0; i < size; i++) x[i] += dt * s[i];\n  }\n\n  function set_bnd(b, x) {\n    if (b === 1) {\n      for (var i = 1; i <= width; i++) {\n        x[i] = x[i + rowSize];\n        x[i + (height + 1) * rowSize] = x[i + height * rowSize];\n      }\n\n      for (var j = 1; i <= height; i++) {\n        x[j * rowSize] = -x[1 + j * rowSize];\n        x[(width + 1) + j * rowSize] = -x[width + j * rowSize];\n      }\n    } else if (b === 2) {\n      for (var i = 1; i <= width; i++) {\n        x[i] = -x[i + rowSize];\n        x[i + (height + 1) * rowSize] = -x[i + height * rowSize];\n      }\n\n      for (var j = 1; j <= height; j++) {\n        x[j * rowSize] = x[1 + j * rowSize];\n        x[(width + 1) + j * rowSize] = x[width + j * rowSize];\n      }\n    } else {\n      for (var i = 1; i <= width; i++) {\n        x[i] = x[i + rowSize];\n        x[i + (height + 1) * rowSize] = x[i + height * rowSize];\n      }\n\n      for (var j = 1; j <= height; j++) {\n        x[j * rowSize] = x[1 + j * rowSize];\n        x[(width + 1) + j * rowSize] = x[width + j * rowSize];\n      }\n    }\n    var maxEdge = (height + 1) * rowSize;\n    x[0] = 0.5 * (x[1] + x[rowSize]);\n    x[maxEdge] = 0.5 * (x[1 + maxEdge] + x[height * rowSize]);\n    x[(width + 1)] = 0.5 * (x[width] + x[(width + 1) + rowSize]);\n    x[(width + 1) + maxEdge] = 0.5 * (x[width + maxEdge] + x[(width + 1) + height * rowSize]);\n  }\n\n  function lin_solve(b, x, x0, a, c) {\n    if (a === 0 && c === 1) {\n      for (var j = 1; j <= height; j++) {\n        var currentRow = j * rowSize;\n        ++currentRow;\n        for (var i = 0; i < width; i++) {\n          x[currentRow] = x0[currentRow];\n          ++currentRow;\n        }\n      }\n      set_bnd(b, x);\n    } else {\n      var invC = 1 / c;\n      for (var k = 0; k < iterations; k++) {\n        for (var j = 1; j <= height; j++) {\n          var lastRow = (j - 1) * rowSize;\n          var currentRow = j * rowSize;\n          var nextRow = (j + 1) * rowSize;\n          var lastX = x[currentRow];\n          ++currentRow;\n          for (var i = 1; i <= width; i++)\n            lastX = x[currentRow] = (x0[currentRow] + a * (lastX + x[++currentRow] + x[++lastRow] + x[++nextRow])) * invC;\n        }\n        set_bnd(b, x);\n      }\n    }\n  }\n\n  function diffuse(b, x, x0, dt) {\n    var a = 0;\n    lin_solve(b, x, x0, a, 1 + 4 * a);\n  }\n\n  function lin_solve2(x, x0, y, y0, a, c) {\n    if (a === 0 && c === 1) {\n      for (var j = 1; j <= height; j++) {\n        var currentRow = j * rowSize;\n        ++currentRow;\n        for (var i = 0; i < width; i++) {\n          x[currentRow] = x0[currentRow];\n          y[currentRow] = y0[currentRow];\n          ++currentRow;\n        }\n      }\n      set_bnd(1, x);\n      set_bnd(2, y);\n    } else {\n      var invC = 1 / c;\n      for (var k = 0; k < iterations; k++) {\n        for (var j = 1; j <= height; j++) {\n          var lastRow = (j - 1) * rowSize;\n          var currentRow = j * rowSize;\n          var nextRow = (j + 1) * rowSize;\n          var lastX = x[currentRow];\n          var lastY = y[currentRow];\n          ++currentRow;\n          for (var i = 1; i <= width; i++) {\n            lastX = x[currentRow] = (x0[currentRow] + a * (lastX + x[currentRow] + x[lastRow] + x[nextRow])) * invC;\n            lastY = y[currentRow] = (y0[currentRow] + a * (lastY + y[++currentRow] + y[++lastRow] + y[++nextRow])) * invC;\n          }\n        }\n        set_bnd(1, x);\n        set_bnd(2, y);\n      }\n    }\n  }\n\n  function diffuse2(x, x0, y, y0, dt) {\n    var a = 0;\n    lin_solve2(x, x0, y, y0, a, 1 + 4 * a);\n  }\n\n  function advect(b, d, d0, u, v, dt) {\n    var Wdt0 = dt * width;\n    var Hdt0 = dt * height;\n    var Wp5 = width + 0.5;\n    var Hp5 = height + 0.5;\n    for (var j = 1; j <= height; j++) {\n      var pos = j * rowSize;\n      for (var i = 1; i <= width; i++) {\n        var x = i - Wdt0 * u[++pos];\n        var y = j - Hdt0 * v[pos];\n        if (x < 0.5)\n          x = 0.5;\n        else if (x > Wp5)\n          x = Wp5;\n        var i0 = x | 0;\n        var i1 = i0 + 1;\n        if (y < 0.5)\n          y = 0.5;\n        else if (y > Hp5)\n          y = Hp5;\n        var j0 = y | 0;\n        var j1 = j0 + 1;\n        var s1 = x - i0;\n        var s0 = 1 - s1;\n        var t1 = y - j0;\n        var t0 = 1 - t1;\n        var row1 = j0 * rowSize;\n        var row2 = j1 * rowSize;\n        d[pos] = s0 * (t0 * d0[i0 + row1] + t1 * d0[i0 + row2]) + s1 * (t0 * d0[i1 + row1] + t1 * d0[i1 + row2]);\n      }\n    }\n    set_bnd(b, d);\n  }\n\n  function project(u, v, p, div) {\n    var h = -0.5 / Math.sqrt(width * height);\n    for (var j = 1; j <= height; j++) {\n      var row = j * rowSize;\n      var previousRow = (j - 1) * rowSize;\n      var prevValue = row - 1;\n      var currentRow = row;\n      var nextValue = row + 1;\n      var nextRow = (j + 1) * rowSize;\n      for (var i = 1; i <= width; i++) {\n        div[++currentRow] = h * (u[++nextValue] - u[++prevValue] + v[++nextRow] - v[++previousRow]);\n        p[currentRow] = 0;\n      }\n    }\n    set_bnd(0, div);\n    set_bnd(0, p);\n\n    lin_solve(0, p, div, 1, 4);\n    var wScale = 0.5 * width;\n    var hScale = 0.5 * height;\n    for (var j = 1; j <= height; j++) {\n      var prevPos = j * rowSize - 1;\n      var currentPos = j * rowSize;\n      var nextPos = j * rowSize + 1;\n      var prevRow = (j - 1) * rowSize;\n      var currentRow = j * rowSize;\n      var nextRow = (j + 1) * rowSize;\n\n      for (var i = 1; i <= width; i++) {\n        u[++currentPos] -= wScale * (p[++nextPos] - p[++prevPos]);\n        v[currentPos] -= hScale * (p[++nextRow] - p[++prevRow]);\n      }\n    }\n    set_bnd(1, u);\n    set_bnd(2, v);\n  }\n\n  function dens_step(x, x0, u, v, dt) {\n    addFields(x, x0, dt);\n    diffuse(0, x0, x, dt);\n    advect(0, x, x0, u, v, dt);\n  }\n\n  function vel_step(u, v, u0, v0, dt) {\n    addFields(u, u0, dt);\n    addFields(v, v0, dt);\n    var temp = u0;\n    u0 = u;\n    u = temp;\n    var temp = v0;\n    v0 = v;\n    v = temp;\n    diffuse2(u, u0, v, v0, dt);\n    project(u, v, u0, v0);\n    var temp = u0;\n    u0 = u;\n    u = temp;\n    var temp = v0;\n    v0 = v;\n    v = temp;\n    advect(1, u, u0, u0, v0, dt);\n    advect(2, v, v0, u0, v0, dt);\n    project(u, v, u0, v0);\n  }\n\n  var uiCallback = function (d, u, v) {\n  };\n\n  function Field(dens, u, v) {\n    // Just exposing the fields here rather than using accessors is a measurable win during display (maybe 5%)\n    // but makes the code ugly.\n    this.setDensity = function (x, y, d) {\n      dens[(x + 1) + (y + 1) * rowSize] = d;\n    }\n    this.getDensity = function (x, y) {\n      return dens[(x + 1) + (y + 1) * rowSize];\n    }\n    this.setVelocity = function (x, y, xv, yv) {\n      u[(x + 1) + (y + 1) * rowSize] = xv;\n      v[(x + 1) + (y + 1) * rowSize] = yv;\n    }\n    this.getXVelocity = function (x, y) {\n      return u[(x + 1) + (y + 1) * rowSize];\n    }\n    this.getYVelocity = function (x, y) {\n      return v[(x + 1) + (y + 1) * rowSize];\n    }\n    this.width = function () {\n      return width;\n    }\n    this.height = function () {\n      return height;\n    }\n  }\n\n  function queryUI(d, u, v) {\n    for (var i = 0; i < size; i++)\n      u[i] = v[i] = d[i] = 0.0;\n    uiCallback(new Field(d, u, v));\n  }\n\n  this.update = function () {\n    queryUI(dens_prev, u_prev, v_prev);\n    vel_step(u, v, u_prev, v_prev, dt);\n    dens_step(dens, dens_prev, u, v, dt);\n    displayFunc(new Field(dens, u, v));\n  }\n  this.setDisplayFunction = function (func) {\n    displayFunc = func;\n  }\n\n  this.iterations = function () {\n    return iterations;\n  }\n  this.setIterations = function (iters) {\n    if (iters > 0 && iters <= 100)\n      iterations = iters;\n  }\n  this.setUICallback = function (callback) {\n    uiCallback = callback;\n  }\n  var iterations = 10;\n  var visc = 0.5;\n  var dt = 0.1;\n  var dens;\n  var dens_prev;\n  var u;\n  var u_prev;\n  var v;\n  var v_prev;\n  var width;\n  var height;\n  var rowSize;\n  var size;\n  var displayFunc;\n\n  function reset() {\n    rowSize = width + 2;\n    size = (width + 2) * (height + 2);\n    dens = new Array(size);\n    dens_prev = new Array(size);\n    u = new Array(size);\n    u_prev = new Array(size);\n    v = new Array(size);\n    v_prev = new Array(size);\n    for (var i = 0; i < size; i++)\n      dens_prev[i] = u_prev[i] = v_prev[i] = dens[i] = u[i] = v[i] = 0;\n  }\n\n  this.reset = reset;\n  this.setResolution = function (hRes, wRes) {\n    var res = wRes * hRes;\n    if (res > 0 && res < 1000000 && (wRes != width || hRes != height)) {\n      width = wRes;\n      height = hRes;\n      reset();\n      return true;\n    }\n    return false;\n  }\n  this.setResolution(64, 64);\n}\n\nvar NavierStokes = new BenchmarkSuite('NavierStokes', 1484000,\n  [new Benchmark('NavierStokes',\n    runNavierStokes,\n    setupNavierStokes,\n    tearDownNavierStokes)]);\n\n\n/* run_harness.js */\nvar print = console.log;\n\nfunction Run() {\n  BenchmarkSuite.RunSuites({\n    NotifyStep: ShowProgress,\n    NotifyError: AddError,\n    NotifyResult: AddResult,\n    NotifyScore: AddScore,\n  });\n}\n\nvar harnessErrorCount = 0;\n\nfunction ShowProgress(name) {\n  print(\"PROGRESS\", name);\n}\n\nfunction AddError(name, error) {\n  print(\"ERROR\", name, error);\n  print(error.stack);\n  harnessErrorCount++;\n}\n\nfunction AddResult(name, result) {\n  print(\"RESULT\", name, result);\n}\n\nfunction AddScore(score) {\n  print(\"SCORE\", score);\n}\n\nfunction main() {\n  Run();\n}\n"
  },
  {
    "path": "benches/scripts/v8-benches/raytrace.js",
    "content": "\"use strict\";\n\"use strip\";\n// Copyright 2012 the V8 project authors. All rights reserved.\n// Redistribution and use in source and binary forms, with or without\n// modification, are permitted provided that the following conditions are\n// met:\n//\n//     * Redistributions of source code must retain the above copyright\n//       notice, this list of conditions and the following disclaimer.\n//     * Redistributions in binary form must reproduce the above\n//       copyright notice, this list of conditions and the following\n//       disclaimer in the documentation and/or other materials provided\n//       with the distribution.\n//     * Neither the name of Google Inc. nor the names of its\n//       contributors may be used to endorse or promote products derived\n//       from this software without specific prior written permission.\n//\n// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS\n// \"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT\n// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR\n// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT\n// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,\n// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT\n// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,\n// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY\n// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n\n// Simple framework for running the benchmark suites and\n// computing a score based on the timing measurements.\n\n\n// A benchmark has a name (string) and a function that will be run to\n// do the performance measurement. The optional setup and tearDown\n// arguments are functions that will be invoked before and after\n// running the benchmark, but the running time of these functions will\n// not be accounted for in the benchmark score.\nfunction Benchmark(name, run, setup, tearDown) {\n  this.name = name;\n  this.run = run;\n  this.Setup = setup ? setup : function () {\n  };\n  this.TearDown = tearDown ? tearDown : function () {\n  };\n}\n\n\n// Benchmark results hold the benchmark and the measured time used to\n// run the benchmark. The benchmark score is computed later once a\n// full benchmark suite has run to completion.\nfunction BenchmarkResult(benchmark, time) {\n  this.benchmark = benchmark;\n  this.time = time;\n}\n\n\n// Automatically convert results to numbers. Used by the geometric\n// mean computation.\nBenchmarkResult.prototype.valueOf = function () {\n  return this.time;\n};\n\n\n// Suites of benchmarks consist of a name and the set of benchmarks in\n// addition to the reference timing that the final score will be based\n// on. This way, all scores are relative to a reference run and higher\n// scores implies better performance.\nfunction BenchmarkSuite(name, reference, benchmarks) {\n  this.name = name;\n  this.reference = reference;\n  this.benchmarks = benchmarks;\n  BenchmarkSuite.suites.push(this);\n}\n\n\n// Keep track of all declared benchmark suites.\nBenchmarkSuite.suites = [];\n\n\n// Scores are not comparable across versions. Bump the version if\n// you're making changes that will affect that scores, e.g. if you add\n// a new benchmark or change an existing one.\nBenchmarkSuite.version = '7';\n\n\n// To make the benchmark results predictable, we replace Math.random\n// with a 100% deterministic alternative.\nMath.random = (function () {\n  var seed = 49734321;\n  return function () {\n    // Robert Jenkins' 32 bit integer hash function.\n    seed = ((seed + 0x7ed55d16) + (seed << 12)) & 0xffffffff;\n    seed = ((seed ^ 0xc761c23c) ^ (seed >>> 19)) & 0xffffffff;\n    seed = ((seed + 0x165667b1) + (seed << 5)) & 0xffffffff;\n    seed = ((seed + 0xd3a2646c) ^ (seed << 9)) & 0xffffffff;\n    seed = ((seed + 0xfd7046c5) + (seed << 3)) & 0xffffffff;\n    seed = ((seed ^ 0xb55a4f09) ^ (seed >>> 16)) & 0xffffffff;\n    return (seed & 0xfffffff) / 0x10000000;\n  };\n})();\n\n\n// Runs all registered benchmark suites and optionally yields between\n// each individual benchmark to avoid running for too long in the\n// context of browsers. Once done, the final score is reported to the\n// runner.\nBenchmarkSuite.RunSuites = function (runner) {\n  var continuation = null;\n  var suites = BenchmarkSuite.suites;\n  var length = suites.length;\n  BenchmarkSuite.scores = [];\n  var index = 0;\n\n  function RunStep() {\n    while (continuation || index < length) {\n      if (continuation) {\n        continuation = continuation();\n      } else {\n        var suite = suites[index++];\n        if (runner.NotifyStart) runner.NotifyStart(suite.name);\n        continuation = suite.RunStep(runner);\n      }\n      if (continuation && typeof window != 'undefined' && window.setTimeout) {\n        window.setTimeout(RunStep, 25);\n        return;\n      }\n    }\n    if (runner.NotifyScore) {\n      var score = BenchmarkSuite.GeometricMean(BenchmarkSuite.scores);\n      var formatted = BenchmarkSuite.FormatScore(100 * score);\n      runner.NotifyScore(formatted);\n    }\n  }\n\n  RunStep();\n};\n\n\n// Counts the total number of registered benchmarks. Useful for\n// showing progress as a percentage.\nBenchmarkSuite.CountBenchmarks = function () {\n  var result = 0;\n  var suites = BenchmarkSuite.suites;\n  for (var i = 0; i < suites.length; i++) {\n    result += suites[i].benchmarks.length;\n  }\n  return result;\n};\n\n\n// Computes the geometric mean of a set of numbers.\nBenchmarkSuite.GeometricMean = function (numbers) {\n  var log = 0;\n  for (var i = 0; i < numbers.length; i++) {\n    log += Math.log(numbers[i]);\n  }\n  return Math.pow(Math.E, log / numbers.length);\n};\n\n\n// Converts a score value to a string with at least three significant\n// digits.\nBenchmarkSuite.FormatScore = function (value) {\n  if (value > 100) {\n    return value.toFixed(0);\n  } else {\n    return value.toPrecision(3);\n  }\n};\n\n// Notifies the runner that we're done running a single benchmark in\n// the benchmark suite. This can be useful to report progress.\nBenchmarkSuite.prototype.NotifyStep = function (result) {\n  this.results.push(result);\n  if (this.runner.NotifyStep) this.runner.NotifyStep(result.benchmark.name);\n};\n\n\n// Notifies the runner that we're done with running a suite and that\n// we have a result which can be reported to the user if needed.\nBenchmarkSuite.prototype.NotifyResult = function () {\n  var mean = BenchmarkSuite.GeometricMean(this.results);\n  var score = this.reference / mean;\n  BenchmarkSuite.scores.push(score);\n  if (this.runner.NotifyResult) {\n    var formatted = BenchmarkSuite.FormatScore(100 * score);\n    this.runner.NotifyResult(this.name, formatted);\n  }\n};\n\n\n// Notifies the runner that running a benchmark resulted in an error.\nBenchmarkSuite.prototype.NotifyError = function (error) {\n  if (this.runner.NotifyError) {\n    this.runner.NotifyError(this.name, error);\n  }\n  if (this.runner.NotifyStep) {\n    this.runner.NotifyStep(this.name);\n  }\n};\n\n\n// Runs a single benchmark for at least a second and computes the\n// average time it takes to run a single iteration.\nBenchmarkSuite.prototype.RunSingleBenchmark = function (benchmark, data) {\n  function Measure(data) {\n    var elapsed = 0;\n    var start = new Date();\n    for (var n = 0; elapsed < 1000; n++) {\n      benchmark.run();\n      elapsed = new Date() - start;\n    }\n    if (data != null) {\n      data.runs += n;\n      data.elapsed += elapsed;\n    }\n  }\n\n  if (data == null) {\n    // Measure the benchmark once for warm up and throw the result\n    // away. Return a fresh data object.\n    Measure(null);\n    return {runs: 0, elapsed: 0};\n  } else {\n    Measure(data);\n    // If we've run too few iterations, we continue for another second.\n    if (data.runs < 32) return data;\n    var usec = (data.elapsed * 1000) / data.runs;\n    this.NotifyStep(new BenchmarkResult(benchmark, usec));\n    return null;\n  }\n};\n\n\n// This function starts running a suite, but stops between each\n// individual benchmark in the suite and returns a continuation\n// function which can be invoked to run the next benchmark. Once the\n// last benchmark has been executed, null is returned.\nBenchmarkSuite.prototype.RunStep = function (runner) {\n  this.results = [];\n  this.runner = runner;\n  var length = this.benchmarks.length;\n  var index = 0;\n  var suite = this;\n  var data;\n\n  // Run the setup, the actual benchmark, and the tear down in three\n  // separate steps to allow the framework to yield between any of the\n  // steps.\n\n  function RunNextSetup() {\n    if (index < length) {\n      try {\n        suite.benchmarks[index].Setup();\n      } catch (e) {\n        suite.NotifyError(e);\n        return null;\n      }\n      return RunNextBenchmark;\n    }\n    suite.NotifyResult();\n    return null;\n  }\n\n  function RunNextBenchmark() {\n    try {\n      data = suite.RunSingleBenchmark(suite.benchmarks[index], data);\n    } catch (e) {\n      suite.NotifyError(e);\n      return null;\n    }\n    // If data is null, we're done with this benchmark.\n    return (data == null) ? RunNextTearDown : RunNextBenchmark();\n  }\n\n  function RunNextTearDown() {\n    try {\n      suite.benchmarks[index++].TearDown();\n    } catch (e) {\n      suite.NotifyError(e);\n      return null;\n    }\n    return RunNextSetup;\n  }\n\n  // Start out running the setup.\n  return RunNextSetup();\n};\n\n// The ray tracer code in this file is written by Adam Burmister. It\n// is available in its original form from:\n//\n//   http://labs.flog.nz.co/raytracer/\n//\n// It has been modified slightly by Google to work as a standalone\n// benchmark, but the all the computational code remains\n// untouched. This file also contains a copy of parts of the Prototype\n// JavaScript framework which is used by the ray tracer.\n\n// Variable used to hold a number that can be used to verify that\n// the scene was ray traced correctly.\nvar checkNumber;\n\n\n// ------------------------------------------------------------------------\n// ------------------------------------------------------------------------\n\n// The following is a copy of parts of the Prototype JavaScript library:\n\n// Prototype JavaScript framework, version 1.5.0\n// (c) 2005-2007 Sam Stephenson\n//\n// Prototype is freely distributable under the terms of an MIT-style license.\n// For details, see the Prototype web site: http://prototype.conio.net/\n\nvar Class = {\n  create: function () {\n    return function () {\n      this.initialize.apply(this, arguments);\n    }\n  }\n};\n\n\nObject.extend = function (destination, source) {\n  for (var property in source) {\n    destination[property] = source[property];\n  }\n  return destination;\n};\n\n\n// ------------------------------------------------------------------------\n// ------------------------------------------------------------------------\n\n// The rest of this file is the actual ray tracer written by Adam\n// Burmister. It's a concatenation of the following files:\n//\n//   flog/color.js\n//   flog/light.js\n//   flog/vector.js\n//   flog/ray.js\n//   flog/scene.js\n//   flog/material/basematerial.js\n//   flog/material/solid.js\n//   flog/material/chessboard.js\n//   flog/shape/baseshape.js\n//   flog/shape/sphere.js\n//   flog/shape/plane.js\n//   flog/intersectioninfo.js\n//   flog/camera.js\n//   flog/background.js\n//   flog/engine.js\n\n\n/* Fake a Flog.* namespace */\nif (typeof (Flog) == 'undefined') var Flog = {};\nif (typeof (Flog.RayTracer) == 'undefined') Flog.RayTracer = {};\n\nFlog.RayTracer.Color = Class.create();\n\nFlog.RayTracer.Color.prototype = {\n  red: 0.0,\n  green: 0.0,\n  blue: 0.0,\n\n  initialize: function (r, g, b) {\n    if (!r) r = 0.0;\n    if (!g) g = 0.0;\n    if (!b) b = 0.0;\n\n    this.red = r;\n    this.green = g;\n    this.blue = b;\n  },\n\n  add: function (c1, c2) {\n    var result = new Flog.RayTracer.Color(0, 0, 0);\n\n    result.red = c1.red + c2.red;\n    result.green = c1.green + c2.green;\n    result.blue = c1.blue + c2.blue;\n\n    return result;\n  },\n\n  addScalar: function (c1, s) {\n    var result = new Flog.RayTracer.Color(0, 0, 0);\n\n    result.red = c1.red + s;\n    result.green = c1.green + s;\n    result.blue = c1.blue + s;\n\n    result.limit();\n\n    return result;\n  },\n\n  subtract: function (c1, c2) {\n    var result = new Flog.RayTracer.Color(0, 0, 0);\n\n    result.red = c1.red - c2.red;\n    result.green = c1.green - c2.green;\n    result.blue = c1.blue - c2.blue;\n\n    return result;\n  },\n\n  multiply: function (c1, c2) {\n    var result = new Flog.RayTracer.Color(0, 0, 0);\n\n    result.red = c1.red * c2.red;\n    result.green = c1.green * c2.green;\n    result.blue = c1.blue * c2.blue;\n\n    return result;\n  },\n\n  multiplyScalar: function (c1, f) {\n    var result = new Flog.RayTracer.Color(0, 0, 0);\n\n    result.red = c1.red * f;\n    result.green = c1.green * f;\n    result.blue = c1.blue * f;\n\n    return result;\n  },\n\n  divideFactor: function (c1, f) {\n    var result = new Flog.RayTracer.Color(0, 0, 0);\n\n    result.red = c1.red / f;\n    result.green = c1.green / f;\n    result.blue = c1.blue / f;\n\n    return result;\n  },\n\n  limit: function () {\n    this.red = (this.red > 0.0) ? ((this.red > 1.0) ? 1.0 : this.red) : 0.0;\n    this.green = (this.green > 0.0) ? ((this.green > 1.0) ? 1.0 : this.green) : 0.0;\n    this.blue = (this.blue > 0.0) ? ((this.blue > 1.0) ? 1.0 : this.blue) : 0.0;\n  },\n\n  distance: function (color) {\n    var d = Math.abs(this.red - color.red) + Math.abs(this.green - color.green) + Math.abs(this.blue - color.blue);\n    return d;\n  },\n\n  blend: function (c1, c2, w) {\n    var result = new Flog.RayTracer.Color(0, 0, 0);\n    result = Flog.RayTracer.Color.prototype.add(\n      Flog.RayTracer.Color.prototype.multiplyScalar(c1, 1 - w),\n      Flog.RayTracer.Color.prototype.multiplyScalar(c2, w)\n    );\n    return result;\n  },\n\n  brightness: function () {\n    var r = Math.floor(this.red * 255);\n    var g = Math.floor(this.green * 255);\n    var b = Math.floor(this.blue * 255);\n    return (r * 77 + g * 150 + b * 29) >> 8;\n  },\n\n  toString: function () {\n    var r = Math.floor(this.red * 255);\n    var g = Math.floor(this.green * 255);\n    var b = Math.floor(this.blue * 255);\n\n    return \"rgb(\" + r + \",\" + g + \",\" + b + \")\";\n  }\n}\n/* Fake a Flog.* namespace */\nif (typeof (Flog) == 'undefined') var Flog = {};\nif (typeof (Flog.RayTracer) == 'undefined') Flog.RayTracer = {};\n\nFlog.RayTracer.Light = Class.create();\n\nFlog.RayTracer.Light.prototype = {\n  position: null,\n  color: null,\n  intensity: 10.0,\n\n  initialize: function (pos, color, intensity) {\n    this.position = pos;\n    this.color = color;\n    this.intensity = (intensity ? intensity : 10.0);\n  },\n\n  toString: function () {\n    return 'Light [' + this.position.x + ',' + this.position.y + ',' + this.position.z + ']';\n  }\n}\n/* Fake a Flog.* namespace */\nif (typeof (Flog) == 'undefined') var Flog = {};\nif (typeof (Flog.RayTracer) == 'undefined') Flog.RayTracer = {};\n\nFlog.RayTracer.Vector = Class.create();\n\nFlog.RayTracer.Vector.prototype = {\n  x: 0.0,\n  y: 0.0,\n  z: 0.0,\n\n  initialize: function (x, y, z) {\n    this.x = (x ? x : 0);\n    this.y = (y ? y : 0);\n    this.z = (z ? z : 0);\n  },\n\n  copy: function (vector) {\n    this.x = vector.x;\n    this.y = vector.y;\n    this.z = vector.z;\n  },\n\n  normalize: function () {\n    var m = this.magnitude();\n    return new Flog.RayTracer.Vector(this.x / m, this.y / m, this.z / m);\n  },\n\n  magnitude: function () {\n    return Math.sqrt((this.x * this.x) + (this.y * this.y) + (this.z * this.z));\n  },\n\n  cross: function (w) {\n    return new Flog.RayTracer.Vector(\n      -this.z * w.y + this.y * w.z,\n      this.z * w.x - this.x * w.z,\n      -this.y * w.x + this.x * w.y);\n  },\n\n  dot: function (w) {\n    return this.x * w.x + this.y * w.y + this.z * w.z;\n  },\n\n  add: function (v, w) {\n    return new Flog.RayTracer.Vector(w.x + v.x, w.y + v.y, w.z + v.z);\n  },\n\n  subtract: function (v, w) {\n    if (!w || !v) throw 'Vectors must be defined [' + v + ',' + w + ']';\n    return new Flog.RayTracer.Vector(v.x - w.x, v.y - w.y, v.z - w.z);\n  },\n\n  multiplyVector: function (v, w) {\n    return new Flog.RayTracer.Vector(v.x * w.x, v.y * w.y, v.z * w.z);\n  },\n\n  multiplyScalar: function (v, w) {\n    return new Flog.RayTracer.Vector(v.x * w, v.y * w, v.z * w);\n  },\n\n  toString: function () {\n    return 'Vector [' + this.x + ',' + this.y + ',' + this.z + ']';\n  }\n}\n/* Fake a Flog.* namespace */\nif (typeof (Flog) == 'undefined') var Flog = {};\nif (typeof (Flog.RayTracer) == 'undefined') Flog.RayTracer = {};\n\nFlog.RayTracer.Ray = Class.create();\n\nFlog.RayTracer.Ray.prototype = {\n  position: null,\n  direction: null,\n  initialize: function (pos, dir) {\n    this.position = pos;\n    this.direction = dir;\n  },\n\n  toString: function () {\n    return 'Ray [' + this.position + ',' + this.direction + ']';\n  }\n}\n/* Fake a Flog.* namespace */\nif (typeof (Flog) == 'undefined') var Flog = {};\nif (typeof (Flog.RayTracer) == 'undefined') Flog.RayTracer = {};\n\nFlog.RayTracer.Scene = Class.create();\n\nFlog.RayTracer.Scene.prototype = {\n  camera: null,\n  shapes: [],\n  lights: [],\n  background: null,\n\n  initialize: function () {\n    this.camera = new Flog.RayTracer.Camera(\n      new Flog.RayTracer.Vector(0, 0, -5),\n      new Flog.RayTracer.Vector(0, 0, 1),\n      new Flog.RayTracer.Vector(0, 1, 0)\n    );\n    this.shapes = new Array();\n    this.lights = new Array();\n    this.background = new Flog.RayTracer.Background(new Flog.RayTracer.Color(0, 0, 0.5), 0.2);\n  }\n}\n/* Fake a Flog.* namespace */\nif (typeof (Flog) == 'undefined') var Flog = {};\nif (typeof (Flog.RayTracer) == 'undefined') Flog.RayTracer = {};\nif (typeof (Flog.RayTracer.Material) == 'undefined') Flog.RayTracer.Material = {};\n\nFlog.RayTracer.Material.BaseMaterial = Class.create();\n\nFlog.RayTracer.Material.BaseMaterial.prototype = {\n\n  gloss: 2.0,             // [0...infinity] 0 = matt\n  transparency: 0.0,      // 0=opaque\n  reflection: 0.0,        // [0...infinity] 0 = no reflection\n  refraction: 0.50,\n  hasTexture: false,\n\n  initialize: function () {\n\n  },\n\n  getColor: function (u, v) {\n\n  },\n\n  wrapUp: function (t) {\n    t = t % 2.0;\n    if (t < -1) t += 2.0;\n    if (t >= 1) t -= 2.0;\n    return t;\n  },\n\n  toString: function () {\n    return 'Material [gloss=' + this.gloss + ', transparency=' + this.transparency + ', hasTexture=' + this.hasTexture + ']';\n  }\n}\n/* Fake a Flog.* namespace */\nif (typeof (Flog) == 'undefined') var Flog = {};\nif (typeof (Flog.RayTracer) == 'undefined') Flog.RayTracer = {};\n\nFlog.RayTracer.Material.Solid = Class.create();\n\nFlog.RayTracer.Material.Solid.prototype = Object.extend(\n  new Flog.RayTracer.Material.BaseMaterial(), {\n    initialize: function (color, reflection, refraction, transparency, gloss) {\n      this.color = color;\n      this.reflection = reflection;\n      this.transparency = transparency;\n      this.gloss = gloss;\n      this.hasTexture = false;\n    },\n\n    getColor: function (u, v) {\n      return this.color;\n    },\n\n    toString: function () {\n      return 'SolidMaterial [gloss=' + this.gloss + ', transparency=' + this.transparency + ', hasTexture=' + this.hasTexture + ']';\n    }\n  }\n);\n/* Fake a Flog.* namespace */\nif (typeof (Flog) == 'undefined') var Flog = {};\nif (typeof (Flog.RayTracer) == 'undefined') Flog.RayTracer = {};\n\nFlog.RayTracer.Material.Chessboard = Class.create();\n\nFlog.RayTracer.Material.Chessboard.prototype = Object.extend(\n  new Flog.RayTracer.Material.BaseMaterial(), {\n    colorEven: null,\n    colorOdd: null,\n    density: 0.5,\n\n    initialize: function (colorEven, colorOdd, reflection, transparency, gloss, density) {\n      this.colorEven = colorEven;\n      this.colorOdd = colorOdd;\n      this.reflection = reflection;\n      this.transparency = transparency;\n      this.gloss = gloss;\n      this.density = density;\n      this.hasTexture = true;\n    },\n\n    getColor: function (u, v) {\n      var t = this.wrapUp(u * this.density) * this.wrapUp(v * this.density);\n\n      if (t < 0.0)\n        return this.colorEven;\n      else\n        return this.colorOdd;\n    },\n\n    toString: function () {\n      return 'ChessMaterial [gloss=' + this.gloss + ', transparency=' + this.transparency + ', hasTexture=' + this.hasTexture + ']';\n    }\n  }\n);\n/* Fake a Flog.* namespace */\nif (typeof (Flog) == 'undefined') var Flog = {};\nif (typeof (Flog.RayTracer) == 'undefined') Flog.RayTracer = {};\nif (typeof (Flog.RayTracer.Shape) == 'undefined') Flog.RayTracer.Shape = {};\n\nFlog.RayTracer.Shape.Sphere = Class.create();\n\nFlog.RayTracer.Shape.Sphere.prototype = {\n  initialize: function (pos, radius, material) {\n    this.radius = radius;\n    this.position = pos;\n    this.material = material;\n  },\n\n  intersect: function (ray) {\n    var info = new Flog.RayTracer.IntersectionInfo();\n    info.shape = this;\n\n    var dst = Flog.RayTracer.Vector.prototype.subtract(ray.position, this.position);\n\n    var B = dst.dot(ray.direction);\n    var C = dst.dot(dst) - (this.radius * this.radius);\n    var D = (B * B) - C;\n\n    if (D > 0) { // intersection!\n      info.isHit = true;\n      info.distance = (-B) - Math.sqrt(D);\n      info.position = Flog.RayTracer.Vector.prototype.add(\n        ray.position,\n        Flog.RayTracer.Vector.prototype.multiplyScalar(\n          ray.direction,\n          info.distance\n        )\n      );\n      info.normal = Flog.RayTracer.Vector.prototype.subtract(\n        info.position,\n        this.position\n      ).normalize();\n\n      info.color = this.material.getColor(0, 0);\n    } else {\n      info.isHit = false;\n    }\n    return info;\n  },\n\n  toString: function () {\n    return 'Sphere [position=' + this.position + ', radius=' + this.radius + ']';\n  }\n}\n/* Fake a Flog.* namespace */\nif (typeof (Flog) == 'undefined') var Flog = {};\nif (typeof (Flog.RayTracer) == 'undefined') Flog.RayTracer = {};\nif (typeof (Flog.RayTracer.Shape) == 'undefined') Flog.RayTracer.Shape = {};\n\nFlog.RayTracer.Shape.Plane = Class.create();\n\nFlog.RayTracer.Shape.Plane.prototype = {\n  d: 0.0,\n\n  initialize: function (pos, d, material) {\n    this.position = pos;\n    this.d = d;\n    this.material = material;\n  },\n\n  intersect: function (ray) {\n    var info = new Flog.RayTracer.IntersectionInfo();\n\n    var Vd = this.position.dot(ray.direction);\n    if (Vd == 0) return info; // no intersection\n\n    var t = -(this.position.dot(ray.position) + this.d) / Vd;\n    if (t <= 0) return info;\n\n    info.shape = this;\n    info.isHit = true;\n    info.position = Flog.RayTracer.Vector.prototype.add(\n      ray.position,\n      Flog.RayTracer.Vector.prototype.multiplyScalar(\n        ray.direction,\n        t\n      )\n    );\n    info.normal = this.position;\n    info.distance = t;\n\n    if (this.material.hasTexture) {\n      var vU = new Flog.RayTracer.Vector(this.position.y, this.position.z, -this.position.x);\n      var vV = vU.cross(this.position);\n      var u = info.position.dot(vU);\n      var v = info.position.dot(vV);\n      info.color = this.material.getColor(u, v);\n    } else {\n      info.color = this.material.getColor(0, 0);\n    }\n\n    return info;\n  },\n\n  toString: function () {\n    return 'Plane [' + this.position + ', d=' + this.d + ']';\n  }\n}\n/* Fake a Flog.* namespace */\nif (typeof (Flog) == 'undefined') var Flog = {};\nif (typeof (Flog.RayTracer) == 'undefined') Flog.RayTracer = {};\n\nFlog.RayTracer.IntersectionInfo = Class.create();\n\nFlog.RayTracer.IntersectionInfo.prototype = {\n  isHit: false,\n  hitCount: 0,\n  shape: null,\n  position: null,\n  normal: null,\n  color: null,\n  distance: null,\n\n  initialize: function () {\n    this.color = new Flog.RayTracer.Color(0, 0, 0);\n  },\n\n  toString: function () {\n    return 'Intersection [' + this.position + ']';\n  }\n}\n/* Fake a Flog.* namespace */\nif (typeof (Flog) == 'undefined') var Flog = {};\nif (typeof (Flog.RayTracer) == 'undefined') Flog.RayTracer = {};\n\nFlog.RayTracer.Camera = Class.create();\n\nFlog.RayTracer.Camera.prototype = {\n  position: null,\n  lookAt: null,\n  equator: null,\n  up: null,\n  screen: null,\n\n  initialize: function (pos, lookAt, up) {\n    this.position = pos;\n    this.lookAt = lookAt;\n    this.up = up;\n    this.equator = lookAt.normalize().cross(this.up);\n    this.screen = Flog.RayTracer.Vector.prototype.add(this.position, this.lookAt);\n  },\n\n  getRay: function (vx, vy) {\n    var pos = Flog.RayTracer.Vector.prototype.subtract(\n      this.screen,\n      Flog.RayTracer.Vector.prototype.subtract(\n        Flog.RayTracer.Vector.prototype.multiplyScalar(this.equator, vx),\n        Flog.RayTracer.Vector.prototype.multiplyScalar(this.up, vy)\n      )\n    );\n    pos.y = pos.y * -1;\n    var dir = Flog.RayTracer.Vector.prototype.subtract(\n      pos,\n      this.position\n    );\n\n    var ray = new Flog.RayTracer.Ray(pos, dir.normalize());\n\n    return ray;\n  },\n\n  toString: function () {\n    return 'Ray []';\n  }\n}\n/* Fake a Flog.* namespace */\nif (typeof (Flog) == 'undefined') var Flog = {};\nif (typeof (Flog.RayTracer) == 'undefined') Flog.RayTracer = {};\n\nFlog.RayTracer.Background = Class.create();\n\nFlog.RayTracer.Background.prototype = {\n  color: null,\n  ambience: 0.0,\n\n  initialize: function (color, ambience) {\n    this.color = color;\n    this.ambience = ambience;\n  }\n}\n/* Fake a Flog.* namespace */\nif (typeof (Flog) == 'undefined') var Flog = {};\nif (typeof (Flog.RayTracer) == 'undefined') Flog.RayTracer = {};\n\nFlog.RayTracer.Engine = Class.create();\n\nFlog.RayTracer.Engine.prototype = {\n  canvas: null, /* 2d context we can render to */\n\n  initialize: function (options) {\n    this.options = Object.extend({\n      canvasHeight: 100,\n      canvasWidth: 100,\n      pixelWidth: 2,\n      pixelHeight: 2,\n      renderDiffuse: false,\n      renderShadows: false,\n      renderHighlights: false,\n      renderReflections: false,\n      rayDepth: 2\n    }, options || {});\n\n    this.options.canvasHeight /= this.options.pixelHeight;\n    this.options.canvasWidth /= this.options.pixelWidth;\n\n    /* TODO: dynamically include other scripts */\n  },\n\n  setPixel: function (x, y, color) {\n    var pxW, pxH;\n    pxW = this.options.pixelWidth;\n    pxH = this.options.pixelHeight;\n\n    if (this.canvas) {\n      this.canvas.fillStyle = color.toString();\n      this.canvas.fillRect(x * pxW, y * pxH, pxW, pxH);\n    } else {\n      if (x === y) {\n        checkNumber += color.brightness();\n      }\n      // print(x * pxW, y * pxH, pxW, pxH);\n    }\n  },\n\n  renderScene: function (scene, canvas) {\n    checkNumber = 0;\n    /* Get canvas */\n    if (canvas) {\n      this.canvas = canvas.getContext(\"2d\");\n    } else {\n      this.canvas = null;\n    }\n\n    var canvasHeight = this.options.canvasHeight;\n    var canvasWidth = this.options.canvasWidth;\n\n    for (var y = 0; y < canvasHeight; y++) {\n      for (var x = 0; x < canvasWidth; x++) {\n        var yp = y * 1.0 / canvasHeight * 2 - 1;\n        var xp = x * 1.0 / canvasWidth * 2 - 1;\n\n        var ray = scene.camera.getRay(xp, yp);\n\n        var color = this.getPixelColor(ray, scene);\n\n        this.setPixel(x, y, color);\n      }\n    }\n    if (checkNumber !== 2321) {\n      throw new Error(\"Scene rendered incorrectly\");\n    }\n  },\n\n  getPixelColor: function (ray, scene) {\n    var info = this.testIntersection(ray, scene, null);\n    if (info.isHit) {\n      var color = this.rayTrace(info, ray, scene, 0);\n      return color;\n    }\n    return scene.background.color;\n  },\n\n  testIntersection: function (ray, scene, exclude) {\n    var hits = 0;\n    var best = new Flog.RayTracer.IntersectionInfo();\n    best.distance = 2000;\n\n    for (var i = 0; i < scene.shapes.length; i++) {\n      var shape = scene.shapes[i];\n\n      if (shape != exclude) {\n        var info = shape.intersect(ray);\n        if (info.isHit && info.distance >= 0 && info.distance < best.distance) {\n          best = info;\n          hits++;\n        }\n      }\n    }\n    best.hitCount = hits;\n    return best;\n  },\n\n  getReflectionRay: function (P, N, V) {\n    var c1 = -N.dot(V);\n    var R1 = Flog.RayTracer.Vector.prototype.add(\n      Flog.RayTracer.Vector.prototype.multiplyScalar(N, 2 * c1),\n      V\n    );\n    return new Flog.RayTracer.Ray(P, R1);\n  },\n\n  rayTrace: function (info, ray, scene, depth) {\n    // Calc ambient\n    var color = Flog.RayTracer.Color.prototype.multiplyScalar(info.color, scene.background.ambience);\n    var oldColor = color;\n    var shininess = Math.pow(10, info.shape.material.gloss + 1);\n\n    for (var i = 0; i < scene.lights.length; i++) {\n      var light = scene.lights[i];\n\n      // Calc diffuse lighting\n      var v = Flog.RayTracer.Vector.prototype.subtract(\n        light.position,\n        info.position\n      ).normalize();\n\n      if (this.options.renderDiffuse) {\n        var L = v.dot(info.normal);\n        if (L > 0.0) {\n          color = Flog.RayTracer.Color.prototype.add(\n            color,\n            Flog.RayTracer.Color.prototype.multiply(\n              info.color,\n              Flog.RayTracer.Color.prototype.multiplyScalar(\n                light.color,\n                L\n              )\n            )\n          );\n        }\n      }\n\n      // The greater the depth the more accurate the colours, but\n      // this is exponentially (!) expensive\n      if (depth <= this.options.rayDepth) {\n        // calculate reflection ray\n        if (this.options.renderReflections && info.shape.material.reflection > 0) {\n          var reflectionRay = this.getReflectionRay(info.position, info.normal, ray.direction);\n          var refl = this.testIntersection(reflectionRay, scene, info.shape);\n\n          if (refl.isHit && refl.distance > 0) {\n            refl.color = this.rayTrace(refl, reflectionRay, scene, depth + 1);\n          } else {\n            refl.color = scene.background.color;\n          }\n\n          color = Flog.RayTracer.Color.prototype.blend(\n            color,\n            refl.color,\n            info.shape.material.reflection\n          );\n        }\n\n        // Refraction\n        /* TODO */\n      }\n\n      /* Render shadows and highlights */\n\n      var shadowInfo = new Flog.RayTracer.IntersectionInfo();\n\n      if (this.options.renderShadows) {\n        var shadowRay = new Flog.RayTracer.Ray(info.position, v);\n\n        shadowInfo = this.testIntersection(shadowRay, scene, info.shape);\n        if (shadowInfo.isHit && shadowInfo.shape != info.shape /*&& shadowInfo.shape.type != 'PLANE'*/) {\n          var vA = Flog.RayTracer.Color.prototype.multiplyScalar(color, 0.5);\n          var dB = (0.5 * Math.pow(shadowInfo.shape.material.transparency, 0.5));\n          color = Flog.RayTracer.Color.prototype.addScalar(vA, dB);\n        }\n      }\n\n      // Phong specular highlights\n      if (this.options.renderHighlights && !shadowInfo.isHit && info.shape.material.gloss > 0) {\n        var Lv = Flog.RayTracer.Vector.prototype.subtract(\n          info.shape.position,\n          light.position\n        ).normalize();\n\n        var E = Flog.RayTracer.Vector.prototype.subtract(\n          scene.camera.position,\n          info.shape.position\n        ).normalize();\n\n        var H = Flog.RayTracer.Vector.prototype.subtract(\n          E,\n          Lv\n        ).normalize();\n\n        var glossWeight = Math.pow(Math.max(info.normal.dot(H), 0), shininess);\n        color = Flog.RayTracer.Color.prototype.add(\n          Flog.RayTracer.Color.prototype.multiplyScalar(light.color, glossWeight),\n          color\n        );\n      }\n    }\n    color.limit();\n    return color;\n  }\n};\n\n\nfunction renderScene() {\n  var scene = new Flog.RayTracer.Scene();\n\n  scene.camera = new Flog.RayTracer.Camera(\n    new Flog.RayTracer.Vector(0, 0, -15),\n    new Flog.RayTracer.Vector(-0.2, 0, 5),\n    new Flog.RayTracer.Vector(0, 1, 0)\n  );\n\n  scene.background = new Flog.RayTracer.Background(\n    new Flog.RayTracer.Color(0.5, 0.5, 0.5),\n    0.4\n  );\n\n  var sphere = new Flog.RayTracer.Shape.Sphere(\n    new Flog.RayTracer.Vector(-1.5, 1.5, 2),\n    1.5,\n    new Flog.RayTracer.Material.Solid(\n      new Flog.RayTracer.Color(0, 0.5, 0.5),\n      0.3,\n      0.0,\n      0.0,\n      2.0\n    )\n  );\n\n  var sphere1 = new Flog.RayTracer.Shape.Sphere(\n    new Flog.RayTracer.Vector(1, 0.25, 1),\n    0.5,\n    new Flog.RayTracer.Material.Solid(\n      new Flog.RayTracer.Color(0.9, 0.9, 0.9),\n      0.1,\n      0.0,\n      0.0,\n      1.5\n    )\n  );\n\n  var plane = new Flog.RayTracer.Shape.Plane(\n    new Flog.RayTracer.Vector(0.1, 0.9, -0.5).normalize(),\n    1.2,\n    new Flog.RayTracer.Material.Chessboard(\n      new Flog.RayTracer.Color(1, 1, 1),\n      new Flog.RayTracer.Color(0, 0, 0),\n      0.2,\n      0.0,\n      1.0,\n      0.7\n    )\n  );\n\n  scene.shapes.push(plane);\n  scene.shapes.push(sphere);\n  scene.shapes.push(sphere1);\n\n  var light = new Flog.RayTracer.Light(\n    new Flog.RayTracer.Vector(5, 10, -1),\n    new Flog.RayTracer.Color(0.8, 0.8, 0.8)\n  );\n\n  var light1 = new Flog.RayTracer.Light(\n    new Flog.RayTracer.Vector(-3, 5, -15),\n    new Flog.RayTracer.Color(0.8, 0.8, 0.8),\n    100\n  );\n\n  scene.lights.push(light);\n  scene.lights.push(light1);\n\n  var imageWidth = 100; // $F('imageWidth');\n  var imageHeight = 100; // $F('imageHeight');\n  var pixelSize = \"5,5\".split(','); //  $F('pixelSize').split(',');\n  var renderDiffuse = true; // $F('renderDiffuse');\n  var renderShadows = true; // $F('renderShadows');\n  var renderHighlights = true; // $F('renderHighlights');\n  var renderReflections = true; // $F('renderReflections');\n  var rayDepth = 2;//$F('rayDepth');\n\n  var raytracer = new Flog.RayTracer.Engine(\n    {\n      canvasWidth: imageWidth,\n      canvasHeight: imageHeight,\n      pixelWidth: pixelSize[0],\n      pixelHeight: pixelSize[1],\n      \"renderDiffuse\": renderDiffuse,\n      \"renderHighlights\": renderHighlights,\n      \"renderShadows\": renderShadows,\n      \"renderReflections\": renderReflections,\n      \"rayDepth\": rayDepth\n    }\n  );\n\n  raytracer.renderScene(scene, null, 0);\n}\n\nvar RayTrace = new BenchmarkSuite('RayTrace', 739989, [\n  new Benchmark('RayTrace', renderScene)\n]);\n\n\n/* run_harness.js */\nvar print = console.log;\n\nfunction Run() {\n  BenchmarkSuite.RunSuites({\n    NotifyStep: ShowProgress,\n    NotifyError: AddError,\n    NotifyResult: AddResult,\n    NotifyScore: AddScore,\n  });\n}\n\nvar harnessErrorCount = 0;\n\nfunction ShowProgress(name) {\n  print(\"PROGRESS\", name);\n}\n\nfunction AddError(name, error) {\n  print(\"ERROR\", name, error);\n  print(error.stack);\n  harnessErrorCount++;\n}\n\nfunction AddResult(name, result) {\n  print(\"RESULT\", name, result);\n}\n\nfunction AddScore(score) {\n  print(\"SCORE\", score);\n}\n\nfunction main() {\n  Run();\n}\n"
  },
  {
    "path": "benches/scripts/v8-benches/regexp.js",
    "content": "\"use strict\";\n\"use strip\";\n// Copyright 2012 the V8 project authors. All rights reserved.\n// Redistribution and use in source and binary forms, with or without\n// modification, are permitted provided that the following conditions are\n// met:\n//\n//     * Redistributions of source code must retain the above copyright\n//       notice, this list of conditions and the following disclaimer.\n//     * Redistributions in binary form must reproduce the above\n//       copyright notice, this list of conditions and the following\n//       disclaimer in the documentation and/or other materials provided\n//       with the distribution.\n//     * Neither the name of Google Inc. nor the names of its\n//       contributors may be used to endorse or promote products derived\n//       from this software without specific prior written permission.\n//\n// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS\n// \"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT\n// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR\n// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT\n// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,\n// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT\n// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,\n// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY\n// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n\n// Simple framework for running the benchmark suites and\n// computing a score based on the timing measurements.\n\n\n// A benchmark has a name (string) and a function that will be run to\n// do the performance measurement. The optional setup and tearDown\n// arguments are functions that will be invoked before and after\n// running the benchmark, but the running time of these functions will\n// not be accounted for in the benchmark score.\nfunction Benchmark(name, run, setup, tearDown) {\n  this.name = name;\n  this.run = run;\n  this.Setup = setup ? setup : function () {\n  };\n  this.TearDown = tearDown ? tearDown : function () {\n  };\n}\n\n\n// Benchmark results hold the benchmark and the measured time used to\n// run the benchmark. The benchmark score is computed later once a\n// full benchmark suite has run to completion.\nfunction BenchmarkResult(benchmark, time) {\n  this.benchmark = benchmark;\n  this.time = time;\n}\n\n\n// Automatically convert results to numbers. Used by the geometric\n// mean computation.\nBenchmarkResult.prototype.valueOf = function () {\n  return this.time;\n};\n\n\n// Suites of benchmarks consist of a name and the set of benchmarks in\n// addition to the reference timing that the final score will be based\n// on. This way, all scores are relative to a reference run and higher\n// scores implies better performance.\nfunction BenchmarkSuite(name, reference, benchmarks) {\n  this.name = name;\n  this.reference = reference;\n  this.benchmarks = benchmarks;\n  BenchmarkSuite.suites.push(this);\n}\n\n\n// Keep track of all declared benchmark suites.\nBenchmarkSuite.suites = [];\n\n\n// Scores are not comparable across versions. Bump the version if\n// you're making changes that will affect that scores, e.g. if you add\n// a new benchmark or change an existing one.\nBenchmarkSuite.version = '7';\n\n\n// To make the benchmark results predictable, we replace Math.random\n// with a 100% deterministic alternative.\nMath.random = (function () {\n  var seed = 49734321;\n  return function () {\n    // Robert Jenkins' 32 bit integer hash function.\n    seed = ((seed + 0x7ed55d16) + (seed << 12)) & 0xffffffff;\n    seed = ((seed ^ 0xc761c23c) ^ (seed >>> 19)) & 0xffffffff;\n    seed = ((seed + 0x165667b1) + (seed << 5)) & 0xffffffff;\n    seed = ((seed + 0xd3a2646c) ^ (seed << 9)) & 0xffffffff;\n    seed = ((seed + 0xfd7046c5) + (seed << 3)) & 0xffffffff;\n    seed = ((seed ^ 0xb55a4f09) ^ (seed >>> 16)) & 0xffffffff;\n    return (seed & 0xfffffff) / 0x10000000;\n  };\n})();\n\n\n// Runs all registered benchmark suites and optionally yields between\n// each individual benchmark to avoid running for too long in the\n// context of browsers. Once done, the final score is reported to the\n// runner.\nBenchmarkSuite.RunSuites = function (runner) {\n  var continuation = null;\n  var suites = BenchmarkSuite.suites;\n  var length = suites.length;\n  BenchmarkSuite.scores = [];\n  var index = 0;\n\n  function RunStep() {\n    while (continuation || index < length) {\n      if (continuation) {\n        continuation = continuation();\n      } else {\n        var suite = suites[index++];\n        if (runner.NotifyStart) runner.NotifyStart(suite.name);\n        continuation = suite.RunStep(runner);\n      }\n      if (continuation && typeof window != 'undefined' && window.setTimeout) {\n        window.setTimeout(RunStep, 25);\n        return;\n      }\n    }\n    if (runner.NotifyScore) {\n      var score = BenchmarkSuite.GeometricMean(BenchmarkSuite.scores);\n      var formatted = BenchmarkSuite.FormatScore(100 * score);\n      runner.NotifyScore(formatted);\n    }\n  }\n\n  RunStep();\n};\n\n\n// Counts the total number of registered benchmarks. Useful for\n// showing progress as a percentage.\nBenchmarkSuite.CountBenchmarks = function () {\n  var result = 0;\n  var suites = BenchmarkSuite.suites;\n  for (var i = 0; i < suites.length; i++) {\n    result += suites[i].benchmarks.length;\n  }\n  return result;\n};\n\n\n// Computes the geometric mean of a set of numbers.\nBenchmarkSuite.GeometricMean = function (numbers) {\n  var log = 0;\n  for (var i = 0; i < numbers.length; i++) {\n    log += Math.log(numbers[i]);\n  }\n  return Math.pow(Math.E, log / numbers.length);\n};\n\n\n// Converts a score value to a string with at least three significant\n// digits.\nBenchmarkSuite.FormatScore = function (value) {\n  if (value > 100) {\n    return value.toFixed(0);\n  } else {\n    return value.toPrecision(3);\n  }\n};\n\n// Notifies the runner that we're done running a single benchmark in\n// the benchmark suite. This can be useful to report progress.\nBenchmarkSuite.prototype.NotifyStep = function (result) {\n  this.results.push(result);\n  if (this.runner.NotifyStep) this.runner.NotifyStep(result.benchmark.name);\n};\n\n\n// Notifies the runner that we're done with running a suite and that\n// we have a result which can be reported to the user if needed.\nBenchmarkSuite.prototype.NotifyResult = function () {\n  var mean = BenchmarkSuite.GeometricMean(this.results);\n  var score = this.reference / mean;\n  BenchmarkSuite.scores.push(score);\n  if (this.runner.NotifyResult) {\n    var formatted = BenchmarkSuite.FormatScore(100 * score);\n    this.runner.NotifyResult(this.name, formatted);\n  }\n};\n\n\n// Notifies the runner that running a benchmark resulted in an error.\nBenchmarkSuite.prototype.NotifyError = function (error) {\n  if (this.runner.NotifyError) {\n    this.runner.NotifyError(this.name, error);\n  }\n  if (this.runner.NotifyStep) {\n    this.runner.NotifyStep(this.name);\n  }\n};\n\n\n// Runs a single benchmark for at least a second and computes the\n// average time it takes to run a single iteration.\nBenchmarkSuite.prototype.RunSingleBenchmark = function (benchmark, data) {\n  function Measure(data) {\n    var elapsed = 0;\n    var start = new Date();\n    for (var n = 0; elapsed < 1000; n++) {\n      benchmark.run();\n      elapsed = new Date() - start;\n    }\n    if (data != null) {\n      data.runs += n;\n      data.elapsed += elapsed;\n    }\n  }\n\n  if (data == null) {\n    // Measure the benchmark once for warm up and throw the result\n    // away. Return a fresh data object.\n    Measure(null);\n    return {runs: 0, elapsed: 0};\n  } else {\n    Measure(data);\n    // If we've run too few iterations, we continue for another second.\n    if (data.runs < 32) return data;\n    var usec = (data.elapsed * 1000) / data.runs;\n    this.NotifyStep(new BenchmarkResult(benchmark, usec));\n    return null;\n  }\n};\n\n\n// This function starts running a suite, but stops between each\n// individual benchmark in the suite and returns a continuation\n// function which can be invoked to run the next benchmark. Once the\n// last benchmark has been executed, null is returned.\nBenchmarkSuite.prototype.RunStep = function (runner) {\n  this.results = [];\n  this.runner = runner;\n  var length = this.benchmarks.length;\n  var index = 0;\n  var suite = this;\n  var data;\n\n  // Run the setup, the actual benchmark, and the tear down in three\n  // separate steps to allow the framework to yield between any of the\n  // steps.\n\n  function RunNextSetup() {\n    if (index < length) {\n      try {\n        suite.benchmarks[index].Setup();\n      } catch (e) {\n        suite.NotifyError(e);\n        return null;\n      }\n      return RunNextBenchmark;\n    }\n    suite.NotifyResult();\n    return null;\n  }\n\n  function RunNextBenchmark() {\n    try {\n      data = suite.RunSingleBenchmark(suite.benchmarks[index], data);\n    } catch (e) {\n      suite.NotifyError(e);\n      return null;\n    }\n    // If data is null, we're done with this benchmark.\n    return (data == null) ? RunNextTearDown : RunNextBenchmark();\n  }\n\n  function RunNextTearDown() {\n    try {\n      suite.benchmarks[index++].TearDown();\n    } catch (e) {\n      suite.NotifyError(e);\n      return null;\n    }\n    return RunNextSetup;\n  }\n\n  // Start out running the setup.\n  return RunNextSetup();\n};\n\n// Copyright 2010 the V8 project authors. All rights reserved.\n// Redistribution and use in source and binary forms, with or without\n// modification, are permitted provided that the following conditions are\n// met:\n//\n//     * Redistributions of source code must retain the above copyright\n//       notice, this list of conditions and the following disclaimer.\n//     * Redistributions in binary form must reproduce the above\n//       copyright notice, this list of conditions and the following\n//       disclaimer in the documentation and/or other materials provided\n//       with the distribution.\n//     * Neither the name of Google Inc. nor the names of its\n//       contributors may be used to endorse or promote products derived\n//       from this software without specific prior written permission.\n//\n// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS\n// \"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT\n// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR\n// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT\n// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,\n// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT\n// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,\n// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY\n// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n\n// Automatically generated on 2009-01-30. Manually updated on 2010-09-17.\n\n// This benchmark is generated by loading 50 of the most popular pages\n// on the web and logging all regexp operations performed.  Each\n// operation is given a weight that is calculated from an estimate of\n// the popularity of the pages where it occurs and the number of times\n// it is executed while loading each page.  Furthermore the literal\n// letters in the data are encoded using ROT13 in a way that does not\n// affect how the regexps match their input.  Finally the strings are\n// scrambled to exercise the regexp engine on different input strings.\n\n\nvar RegExp = new BenchmarkSuite('RegExp', 910985, [\n  new Benchmark(\"RegExp\", RegExpRun, RegExpSetup, RegExpTearDown)\n]);\n\nvar regExpBenchmark = null;\n\nfunction RegExpSetup() {\n  regExpBenchmark = new RegExpBenchmark();\n  RegExpRun(); // run once to get system initialized\n}\n\nfunction RegExpRun() {\n  regExpBenchmark.run();\n}\n\nfunction RegExpTearDown() {\n  regExpBenchmark = null;\n}\n\n// Returns an array of n different variants of the input string str.\n// The variants are computed by randomly rotating one random\n// character.\nfunction computeInputVariants(str, n) {\n  var variants = [str];\n  for (var i = 1; i < n; i++) {\n    var pos = Math.floor(Math.random() * str.length);\n    var chr = String.fromCharCode((str.charCodeAt(pos) + Math.floor(Math.random() * 128)) % 128);\n    variants[i] = str.substring(0, pos) + chr + str.substring(pos + 1, str.length);\n  }\n  return variants;\n}\n\nfunction RegExpBenchmark() {\n  var re0 = /^ba/;\n  var re1 = /(((\\w+):\\/\\/)([^\\/:]*)(:(\\d+))?)?([^#?]*)(\\?([^#]*))?(#(.*))?/;\n  var re2 = /^\\s*|\\s*$/g;\n  var re3 = /\\bQBZPbageby_cynprubyqre\\b/;\n  var re4 = /,/;\n  var re5 = /\\bQBZPbageby_cynprubyqre\\b/g;\n  var re6 = /^[\\s\\xa0]+|[\\s\\xa0]+$/g;\n  var re7 = /(\\d*)(\\D*)/g;\n  var re8 = /=/;\n  var re9 = /(^|\\s)lhv\\-h(\\s|$)/;\n  var str0 = 'Zbmvyyn/5.0 (Jvaqbjf; H; Jvaqbjf AG 5.1; ra-HF) NccyrJroXvg/528.9 (XUGZY, yvxr Trpxb) Puebzr/2.0.157.0 Fnsnev/528.9';\n  var re10 = /\\#/g;\n  var re11 = /\\./g;\n  var re12 = /'/g;\n  var re13 = /\\?[\\w\\W]*(sevraqvq|punaaryvq|tebhcvq)=([^\\&\\?#]*)/i;\n  var str1 = 'Fubpxjnir Synfu 9.0  e115';\n  var re14 = /\\s+/g;\n  var re15 = /^\\s*(\\S*(\\s+\\S+)*)\\s*$/;\n  var re16 = /(-[a-z])/i;\n\n  var s0 = computeInputVariants('pyvpx', 6511);\n  var s1 = computeInputVariants('uggc://jjj.snprobbx.pbz/ybtva.cuc', 1844);\n  var s2 = computeInputVariants('QBZPbageby_cynprubyqre', 739);\n  var s3 = computeInputVariants('uggc://jjj.snprobbx.pbz/', 598);\n  var s4 = computeInputVariants('uggc://jjj.snprobbx.pbz/fepu.cuc', 454);\n  var s5 = computeInputVariants('qqqq, ZZZ q, llll', 352);\n  var s6 = computeInputVariants('vachggrkg QBZPbageby_cynprubyqre', 312);\n  var s7 = computeInputVariants('/ZlFcnprUbzrcntr/Vaqrk-FvgrUbzr,10000000', 282);\n  var s8 = computeInputVariants('vachggrkg', 177);\n  var s9 = computeInputVariants('528.9', 170);\n  var s10 = computeInputVariants('528', 170);\n  var s11 = computeInputVariants('VCPhygher=ra-HF', 156);\n  var s12 = computeInputVariants('CersreerqPhygher=ra-HF', 156);\n  var s13 = computeInputVariants('xrlcerff', 144);\n  var s14 = computeInputVariants('521', 139);\n  var s15 = computeInputVariants(str0, 139);\n  var s16 = computeInputVariants('qvi .so_zrah', 137);\n  var s17 = computeInputVariants('qvi.so_zrah', 137);\n  var s18 = computeInputVariants('uvqqra_ryrz', 117);\n  var s19 = computeInputVariants('sevraqfgre_naba=nvq%3Qn6ss9p85n868ro9s059pn854735956o3%26ers%3Q%26df%3Q%26vpgl%3QHF', 95);\n  var s20 = computeInputVariants('uggc://ubzr.zlfcnpr.pbz/vaqrk.psz', 93);\n  var s21 = computeInputVariants(str1, 92);\n  var s22 = computeInputVariants('svefg', 85);\n  var s23 = computeInputVariants('uggc://cebsvyr.zlfcnpr.pbz/vaqrk.psz', 85);\n  var s24 = computeInputVariants('ynfg', 85);\n  var s25 = computeInputVariants('qvfcynl', 85);\n\n  function runBlock0() {\n    for (var i = 0; i < 6511; i++) {\n      re0.exec(s0[i]);\n    }\n    for (var i = 0; i < 1844; i++) {\n      re1.exec(s1[i]);\n    }\n    for (var i = 0; i < 739; i++) {\n      s2[i].replace(re2, '');\n    }\n    for (var i = 0; i < 598; i++) {\n      re1.exec(s3[i]);\n    }\n    for (var i = 0; i < 454; i++) {\n      re1.exec(s4[i]);\n    }\n    for (var i = 0; i < 352; i++) {\n      /qqqq|qqq|qq|q|ZZZZ|ZZZ|ZZ|Z|llll|ll|l|uu|u|UU|U|zz|z|ff|f|gg|g|sss|ss|s|mmm|mm|m/g.exec(s5[i]);\n    }\n    for (var i = 0; i < 312; i++) {\n      re3.exec(s6[i]);\n    }\n    for (var i = 0; i < 282; i++) {\n      re4.exec(s7[i]);\n    }\n    for (var i = 0; i < 177; i++) {\n      s8[i].replace(re5, '');\n    }\n    for (var i = 0; i < 170; i++) {\n      s9[i].replace(re6, '');\n      re7.exec(s10[i]);\n    }\n    for (var i = 0; i < 156; i++) {\n      re8.exec(s11[i]);\n      re8.exec(s12[i]);\n    }\n    for (var i = 0; i < 144; i++) {\n      re0.exec(s13[i]);\n    }\n    for (var i = 0; i < 139; i++) {\n      s14[i].replace(re6, '');\n      re7.exec(s14[i]);\n      re9.exec('');\n      /JroXvg\\/(\\S+)/.exec(s15[i]);\n    }\n    for (var i = 0; i < 137; i++) {\n      s16[i].replace(re10, '');\n      s16[i].replace(/\\[/g, '');\n      s17[i].replace(re11, '');\n    }\n    for (var i = 0; i < 117; i++) {\n      s18[i].replace(re2, '');\n    }\n    for (var i = 0; i < 95; i++) {\n      /(?:^|;)\\s*sevraqfgre_ynat=([^;]*)/.exec(s19[i]);\n    }\n    for (var i = 0; i < 93; i++) {\n      s20[i].replace(re12, '');\n      re13.exec(s20[i]);\n    }\n    for (var i = 0; i < 92; i++) {\n      s21[i].replace(/([a-zA-Z]|\\s)+/, '');\n    }\n    for (var i = 0; i < 85; i++) {\n      s22[i].replace(re14, '');\n      s22[i].replace(re15, '');\n      s23[i].replace(re12, '');\n      s24[i].replace(re14, '');\n      s24[i].replace(re15, '');\n      re16.exec(s25[i]);\n      re13.exec(s23[i]);\n    }\n  }\n\n  var re17 = /(^|[^\\\\])\\\"\\\\\\/Qngr\\((-?[0-9]+)\\)\\\\\\/\\\"/g;\n  var str2 = '{\"anzr\":\"\",\"ahzoreSbezng\":{\"PheeraplQrpvznyQvtvgf\":2,\"PheeraplQrpvznyFrcnengbe\":\".\",\"VfErnqBayl\":gehr,\"PheeraplTebhcFvmrf\":[3],\"AhzoreTebhcFvmrf\":[3],\"CrepragTebhcFvmrf\":[3],\"PheeraplTebhcFrcnengbe\":\",\",\"PheeraplFlzoby\":\"\\xa4\",\"AnAFlzoby\":\"AnA\",\"PheeraplArtngvirCnggrea\":0,\"AhzoreArtngvirCnggrea\":1,\"CrepragCbfvgvirCnggrea\":0,\"CrepragArtngvirCnggrea\":0,\"ArtngvirVasvavglFlzoby\":\"-Vasvavgl\",\"ArtngvirFvta\":\"-\",\"AhzoreQrpvznyQvtvgf\":2,\"AhzoreQrpvznyFrcnengbe\":\".\",\"AhzoreTebhcFrcnengbe\":\",\",\"PheeraplCbfvgvirCnggrea\":0,\"CbfvgvirVasvavglFlzoby\":\"Vasvavgl\",\"CbfvgvirFvta\":\"+\",\"CrepragQrpvznyQvtvgf\":2,\"CrepragQrpvznyFrcnengbe\":\".\",\"CrepragTebhcFrcnengbe\":\",\",\"CrepragFlzoby\":\"%\",\"CreZvyyrFlzoby\":\"\\u2030\",\"AngvirQvtvgf\":[\"0\",\"1\",\"2\",\"3\",\"4\",\"5\",\"6\",\"7\",\"8\",\"9\"],\"QvtvgFhofgvghgvba\":1},\"qngrGvzrSbezng\":{\"NZQrfvtangbe\":\"NZ\",\"Pnyraqne\":{\"ZvaFhccbegrqQngrGvzr\":\"@-62135568000000@\",\"ZnkFhccbegrqQngrGvzr\":\"@253402300799999@\",\"NytbevguzGlcr\":1,\"PnyraqneGlcr\":1,\"Renf\":[1],\"GjbQvtvgLrneZnk\":2029,\"VfErnqBayl\":gehr},\"QngrFrcnengbe\":\"/\",\"SvefgQnlBsJrrx\":0,\"PnyraqneJrrxEhyr\":0,\"ShyyQngrGvzrCnggrea\":\"qqqq, qq ZZZZ llll UU:zz:ff\",\"YbatQngrCnggrea\":\"qqqq, qq ZZZZ llll\",\"YbatGvzrCnggrea\":\"UU:zz:ff\",\"ZbaguQnlCnggrea\":\"ZZZZ qq\",\"CZQrfvtangbe\":\"CZ\",\"ESP1123Cnggrea\":\"qqq, qq ZZZ llll UU\\':\\'zz\\':\\'ff \\'TZG\\'\",\"FubegQngrCnggrea\":\"ZZ/qq/llll\",\"FubegGvzrCnggrea\":\"UU:zz\",\"FbegnoyrQngrGvzrCnggrea\":\"llll\\'-\\'ZZ\\'-\\'qq\\'G\\'UU\\':\\'zz\\':\\'ff\",\"GvzrFrcnengbe\":\":\",\"HavirefnyFbegnoyrQngrGvzrCnggrea\":\"llll\\'-\\'ZZ\\'-\\'qq UU\\':\\'zz\\':\\'ff\\'M\\'\",\"LrneZbaguCnggrea\":\"llll ZZZZ\",\"NooerivngrqQnlAnzrf\":[\"Fha\",\"Zba\",\"Ghr\",\"Jrq\",\"Guh\",\"Sev\",\"Fng\"],\"FubegrfgQnlAnzrf\":[\"Fh\",\"Zb\",\"Gh\",\"Jr\",\"Gu\",\"Se\",\"Fn\"],\"QnlAnzrf\":[\"Fhaqnl\",\"Zbaqnl\",\"Ghrfqnl\",\"Jrqarfqnl\",\"Guhefqnl\",\"Sevqnl\",\"Fngheqnl\"],\"NooerivngrqZbaguAnzrf\":[\"Wna\",\"Sro\",\"Zne\",\"Nce\",\"Znl\",\"Wha\",\"Why\",\"Nht\",\"Frc\",\"Bpg\",\"Abi\",\"Qrp\",\"\"],\"ZbaguAnzrf\":[\"Wnahnel\",\"Sroehnel\",\"Znepu\",\"Ncevy\",\"Znl\",\"Whar\",\"Whyl\",\"Nhthfg\",\"Frcgrzore\",\"Bpgbore\",\"Abirzore\",\"Qrprzore\",\"\"],\"VfErnqBayl\":gehr,\"AngvirPnyraqneAnzr\":\"Tertbevna Pnyraqne\",\"NooerivngrqZbaguTravgvirAnzrf\":[\"Wna\",\"Sro\",\"Zne\",\"Nce\",\"Znl\",\"Wha\",\"Why\",\"Nht\",\"Frc\",\"Bpg\",\"Abi\",\"Qrp\",\"\"],\"ZbaguTravgvirAnzrf\":[\"Wnahnel\",\"Sroehnel\",\"Znepu\",\"Ncevy\",\"Znl\",\"Whar\",\"Whyl\",\"Nhthfg\",\"Frcgrzore\",\"Bpgbore\",\"Abirzore\",\"Qrprzore\",\"\"]}}';\n  var str3 = '{\"anzr\":\"ra-HF\",\"ahzoreSbezng\":{\"PheeraplQrpvznyQvtvgf\":2,\"PheeraplQrpvznyFrcnengbe\":\".\",\"VfErnqBayl\":snyfr,\"PheeraplTebhcFvmrf\":[3],\"AhzoreTebhcFvmrf\":[3],\"CrepragTebhcFvmrf\":[3],\"PheeraplTebhcFrcnengbe\":\",\",\"PheeraplFlzoby\":\"$\",\"AnAFlzoby\":\"AnA\",\"PheeraplArtngvirCnggrea\":0,\"AhzoreArtngvirCnggrea\":1,\"CrepragCbfvgvirCnggrea\":0,\"CrepragArtngvirCnggrea\":0,\"ArtngvirVasvavglFlzoby\":\"-Vasvavgl\",\"ArtngvirFvta\":\"-\",\"AhzoreQrpvznyQvtvgf\":2,\"AhzoreQrpvznyFrcnengbe\":\".\",\"AhzoreTebhcFrcnengbe\":\",\",\"PheeraplCbfvgvirCnggrea\":0,\"CbfvgvirVasvavglFlzoby\":\"Vasvavgl\",\"CbfvgvirFvta\":\"+\",\"CrepragQrpvznyQvtvgf\":2,\"CrepragQrpvznyFrcnengbe\":\".\",\"CrepragTebhcFrcnengbe\":\",\",\"CrepragFlzoby\":\"%\",\"CreZvyyrFlzoby\":\"\\u2030\",\"AngvirQvtvgf\":[\"0\",\"1\",\"2\",\"3\",\"4\",\"5\",\"6\",\"7\",\"8\",\"9\"],\"QvtvgFhofgvghgvba\":1},\"qngrGvzrSbezng\":{\"NZQrfvtangbe\":\"NZ\",\"Pnyraqne\":{\"ZvaFhccbegrqQngrGvzr\":\"@-62135568000000@\",\"ZnkFhccbegrqQngrGvzr\":\"@253402300799999@\",\"NytbevguzGlcr\":1,\"PnyraqneGlcr\":1,\"Renf\":[1],\"GjbQvtvgLrneZnk\":2029,\"VfErnqBayl\":snyfr},\"QngrFrcnengbe\":\"/\",\"SvefgQnlBsJrrx\":0,\"PnyraqneJrrxEhyr\":0,\"ShyyQngrGvzrCnggrea\":\"qqqq, ZZZZ qq, llll u:zz:ff gg\",\"YbatQngrCnggrea\":\"qqqq, ZZZZ qq, llll\",\"YbatGvzrCnggrea\":\"u:zz:ff gg\",\"ZbaguQnlCnggrea\":\"ZZZZ qq\",\"CZQrfvtangbe\":\"CZ\",\"ESP1123Cnggrea\":\"qqq, qq ZZZ llll UU\\':\\'zz\\':\\'ff \\'TZG\\'\",\"FubegQngrCnggrea\":\"Z/q/llll\",\"FubegGvzrCnggrea\":\"u:zz gg\",\"FbegnoyrQngrGvzrCnggrea\":\"llll\\'-\\'ZZ\\'-\\'qq\\'G\\'UU\\':\\'zz\\':\\'ff\",\"GvzrFrcnengbe\":\":\",\"HavirefnyFbegnoyrQngrGvzrCnggrea\":\"llll\\'-\\'ZZ\\'-\\'qq UU\\':\\'zz\\':\\'ff\\'M\\'\",\"LrneZbaguCnggrea\":\"ZZZZ, llll\",\"NooerivngrqQnlAnzrf\":[\"Fha\",\"Zba\",\"Ghr\",\"Jrq\",\"Guh\",\"Sev\",\"Fng\"],\"FubegrfgQnlAnzrf\":[\"Fh\",\"Zb\",\"Gh\",\"Jr\",\"Gu\",\"Se\",\"Fn\"],\"QnlAnzrf\":[\"Fhaqnl\",\"Zbaqnl\",\"Ghrfqnl\",\"Jrqarfqnl\",\"Guhefqnl\",\"Sevqnl\",\"Fngheqnl\"],\"NooerivngrqZbaguAnzrf\":[\"Wna\",\"Sro\",\"Zne\",\"Nce\",\"Znl\",\"Wha\",\"Why\",\"Nht\",\"Frc\",\"Bpg\",\"Abi\",\"Qrp\",\"\"],\"ZbaguAnzrf\":[\"Wnahnel\",\"Sroehnel\",\"Znepu\",\"Ncevy\",\"Znl\",\"Whar\",\"Whyl\",\"Nhthfg\",\"Frcgrzore\",\"Bpgbore\",\"Abirzore\",\"Qrprzore\",\"\"],\"VfErnqBayl\":snyfr,\"AngvirPnyraqneAnzr\":\"Tertbevna Pnyraqne\",\"NooerivngrqZbaguTravgvirAnzrf\":[\"Wna\",\"Sro\",\"Zne\",\"Nce\",\"Znl\",\"Wha\",\"Why\",\"Nht\",\"Frc\",\"Bpg\",\"Abi\",\"Qrp\",\"\"],\"ZbaguTravgvirAnzrf\":[\"Wnahnel\",\"Sroehnel\",\"Znepu\",\"Ncevy\",\"Znl\",\"Whar\",\"Whyl\",\"Nhthfg\",\"Frcgrzore\",\"Bpgbore\",\"Abirzore\",\"Qrprzore\",\"\"]}}';\n  var str4 = 'HFEYBP=DKWyLHAiMTH9AwHjWxAcqUx9GJ91oaEunJ4tIzyyqlMQo3IhqUW5D29xMG1IHlMQo3IhqUW5GzSgMG1Iozy0MJDtH3EuqTImWxEgLHAiMTH9BQN3WxkuqTy0qJEyCGZ3YwDkBGVzGT9hM2y0qJEyCF0kZwVhZQH3APMDo3A0LJkQo2EyCGx0ZQDmWyWyM2yiox5uoJH9D0R%3Q';\n  var str5 = 'HFEYBP=DKWyLHAiMTH9AwHjWxAcqUx9GJ91oaEunJ4tIzyyqlMQo3IhqUW5D29xMG1IHlMQo3IhqUW5GzSgMG1Iozy0MJDtH3EuqTImWxEgLHAiMTH9BQN3WxkuqTy0qJEyCGZ3YwDkBGVzGT9hM2y0qJEyCF0kZwVhZQH3APMDo3A0LJkQo2EyCGx0ZQDmWyWyM2yiox5uoJH9D0R=';\n  var re18 = /^\\s+|\\s+$/g;\n  var str6 = 'uggc://jjj.snprobbx.pbz/vaqrk.cuc';\n  var re19 = /(?:^|\\s+)ba(?:\\s+|$)/;\n  var re20 = /[+, ]/;\n  var re21 = /ybnqrq|pbzcyrgr/;\n  var str7 = ';;jvaqbj.IjPurpxZbhfrCbfvgvbaNQ_VQ=shapgvba(r){vs(!r)ine r=jvaqbj.rirag;ine c=-1;vs(d1)c=d1.EbyybssCnary;ine bo=IjTrgBow(\"IjCnayNQ_VQ_\"+c);vs(bo&&bo.fglyr.ivfvovyvgl==\"ivfvoyr\"){ine fns=IjFns?8:0;ine pheK=r.pyvragK+IjBOFpe(\"U\")+fns,pheL=r.pyvragL+IjBOFpe(\"I\")+fns;ine y=IjBOEC(NQ_VQ,bo,\"Y\"),g=IjBOEC(NQ_VQ,bo,\"G\");ine e=y+d1.Cnaryf[c].Jvqgu,o=g+d1.Cnaryf[c].Urvtug;vs((pheK<y)||(pheK>e)||(pheL<g)||(pheL>o)){vs(jvaqbj.IjBaEbyybssNQ_VQ)IjBaEbyybssNQ_VQ(c);ryfr IjPybfrNq(NQ_VQ,c,gehr,\"\");}ryfr erghea;}IjPnapryZbhfrYvfgrareNQ_VQ();};;jvaqbj.IjFrgEbyybssCnaryNQ_VQ=shapgvba(c){ine z=\"zbhfrzbir\",q=qbphzrag,s=IjPurpxZbhfrCbfvgvbaNQ_VQ;c=IjTc(NQ_VQ,c);vs(d1&&d1.EbyybssCnary>-1)IjPnapryZbhfrYvfgrareNQ_VQ();vs(d1)d1.EbyybssCnary=c;gel{vs(q.nqqRiragYvfgrare)q.nqqRiragYvfgrare(z,s,snyfr);ryfr vs(q.nggnpuRirag)q.nggnpuRirag(\"ba\"+z,s);}pngpu(r){}};;jvaqbj.IjPnapryZbhfrYvfgrareNQ_VQ=shapgvba(){ine z=\"zbhfrzbir\",q=qbphzrag,s=IjPurpxZbhfrCbfvgvbaNQ_VQ;vs(d1)d1.EbyybssCnary=-1;gel{vs(q.erzbirRiragYvfgrare)q.erzbirRiragYvfgrare(z,s,snyfr);ryfr vs(q.qrgnpuRirag)q.qrgnpuRirag(\"ba\"+z,s);}pngpu(r){}};;d1.IjTc=d2(n,c){ine nq=d1;vs(vfAnA(c)){sbe(ine v=0;v<nq.Cnaryf.yratgu;v++)vs(nq.Cnaryf[v].Anzr==c)erghea v;erghea 0;}erghea c;};;d1.IjTpy=d2(n,c,p){ine cn=d1.Cnaryf[IjTc(n,c)];vs(!cn)erghea 0;vs(vfAnA(p)){sbe(ine v=0;v<cn.Pyvpxguehf.yratgu;v++)vs(cn.Pyvpxguehf[v].Anzr==p)erghea v;erghea 0;}erghea p;};;d1.IjGenpr=d2(n,f){gel{vs(jvaqbj[\"Ij\"+\"QtQ\"])jvaqbj[\"Ij\"+\"QtQ\"](n,1,f);}pngpu(r){}};;d1.IjYvzvg1=d2(n,f){ine nq=d1,vh=f.fcyvg(\"/\");sbe(ine v=0,p=0;v<vh.yratgu;v++){vs(vh[v].yratgu>0){vs(nq.FzV.yratgu>0)nq.FzV+=\"/\";nq.FzV+=vh[v];nq.FtZ[nq.FtZ.yratgu]=snyfr;}}};;d1.IjYvzvg0=d2(n,f){ine nq=d1,vh=f.fcyvg(\"/\");sbe(ine v=0;v<vh.yratgu;v++){vs(vh[v].yratgu>0){vs(nq.OvC.yratgu>0)nq.OvC+=\"/\";nq.OvC+=vh[v];}}};;d1.IjRVST=d2(n,c){jvaqbj[\"IjCnayNQ_VQ_\"+c+\"_Bow\"]=IjTrgBow(\"IjCnayNQ_VQ_\"+c+\"_Bow\");vs(jvaqbj[\"IjCnayNQ_VQ_\"+c+\"_Bow\"]==ahyy)frgGvzrbhg(\"IjRVST(NQ_VQ,\"+c+\")\",d1.rvsg);};;d1.IjNavzSHC=d2(n,c){ine nq=d1;vs(c>nq.Cnaryf.yratgu)erghea;ine cna=nq.Cnaryf[c],nn=gehr,on=gehr,yn=gehr,en=gehr,cn=nq.Cnaryf[0],sf=nq.ShF,j=cn.Jvqgu,u=cn.Urvtug;vs(j==\"100%\"){j=sf;en=snyfr;yn=snyfr;}vs(u==\"100%\"){u=sf;nn=snyfr;on=snyfr;}vs(cn.YnY==\"Y\")yn=snyfr;vs(cn.YnY==\"E\")en=snyfr;vs(cn.GnY==\"G\")nn=snyfr;vs(cn.GnY==\"O\")on=snyfr;ine k=0,l=0;fjvgpu(nq.NshP%8){pnfr 0:oernx;pnfr 1:vs(nn)l=-sf;oernx;pnfr 2:k=j-sf;oernx;pnfr 3:vs(en)k=j;oernx;pnfr 4:k=j-sf;l=u-sf;oernx;pnfr 5:k=j-sf;vs(on)l=u;oernx;pnfr 6:l=u-sf;oernx;pnfr 7:vs(yn)k=-sf;l=u-sf;oernx;}vs(nq.NshP++ <nq.NshG)frgGvzrbhg((\"IjNavzSHC(NQ_VQ,\"+c+\")\"),nq.NshC);ryfr{k=-1000;l=k;}cna.YrsgBssfrg=k;cna.GbcBssfrg=l;IjNhErcb(n,c);};;d1.IjTrgErnyCbfvgvba=d2(n,b,j){erghea IjBOEC.nccyl(guvf,nethzragf);};;d1.IjPnapryGvzrbhg=d2(n,c){c=IjTc(n,c);ine cay=d1.Cnaryf[c];vs(cay&&cay.UgU!=\"\"){pyrneGvzrbhg(cay.UgU);}};;d1.IjPnapryNyyGvzrbhgf=d2(n){vs(d1.YbpxGvzrbhgPunatrf)erghea;sbe(ine c=0;c<d1.bac;c++)IjPnapryGvzrbhg(n,c);};;d1.IjFgnegGvzrbhg=d2(n,c,bG){c=IjTc(n,c);ine cay=d1.Cnaryf[c];vs(cay&&((cay.UvqrGvzrbhgInyhr>0)||(nethzragf.yratgu==3&&bG>0))){pyrneGvzrbhg(cay.UgU);cay.UgU=frgGvzrbhg(cay.UvqrNpgvba,(nethzragf.yratgu==3?bG:cay.UvqrGvzrbhgInyhr));}};;d1.IjErfrgGvzrbhg=d2(n,c,bG){c=IjTc(n,c);IjPnapryGvzrbhg(n,c);riny(\"IjFgnegGvzrbhg(NQ_VQ,c\"+(nethzragf.yratgu==3?\",bG\":\"\")+\")\");};;d1.IjErfrgNyyGvzrbhgf=d2(n){sbe(ine c=0;c<d1.bac;c++)IjErfrgGvzrbhg(n,c);};;d1.IjQrgnpure=d2(n,rig,sap){gel{vs(IjQVR5)riny(\"jvaqbj.qrgnpuRirag(\\'ba\"+rig+\"\\',\"+sap+\"NQ_VQ)\");ryfr vs(!IjQVRZnp)riny(\"jvaqbj.erzbirRiragYvfgrare(\\'\"+rig+\"\\',\"+sap+\"NQ_VQ,snyfr)\");}pngpu(r){}};;d1.IjPyrnaHc=d2(n){IjCvat(n,\"G\");ine nq=d1;sbe(ine v=0;v<nq.Cnaryf.yratgu;v++){IjUvqrCnary(n,v,gehr);}gel{IjTrgBow(nq.gya).vaareUGZY=\"\";}pngpu(r){}vs(nq.gya!=nq.gya2)gel{IjTrgBow(nq.gya2).vaareUGZY=\"\";}pngpu(r){}gel{d1=ahyy;}pngpu(r){}gel{IjQrgnpure(n,\"haybnq\",\"IjHayNQ_VQ\");}pngpu(r){}gel{jvaqbj.IjHayNQ_VQ=ahyy;}pngpu(r){}gel{IjQrgnpure(n,\"fpebyy\",\"IjFeNQ_VQ\");}pngpu(r){}gel{jvaqbj.IjFeNQ_VQ=ahyy;}pngpu(r){}gel{IjQrgnpure(n,\"erfvmr\",\"IjEmNQ_VQ\");}pngpu(r){}gel{jvaqbj.IjEmNQ_VQ=ahyy;}pngpu(r){}gel{IjQrgnpure(n';\n  var str8 = ';;jvaqbj.IjPurpxZbhfrCbfvgvbaNQ_VQ=shapgvba(r){vs(!r)ine r=jvaqbj.rirag;ine c=-1;vs(jvaqbj.IjNqNQ_VQ)c=jvaqbj.IjNqNQ_VQ.EbyybssCnary;ine bo=IjTrgBow(\"IjCnayNQ_VQ_\"+c);vs(bo&&bo.fglyr.ivfvovyvgl==\"ivfvoyr\"){ine fns=IjFns?8:0;ine pheK=r.pyvragK+IjBOFpe(\"U\")+fns,pheL=r.pyvragL+IjBOFpe(\"I\")+fns;ine y=IjBOEC(NQ_VQ,bo,\"Y\"),g=IjBOEC(NQ_VQ,bo,\"G\");ine e=y+jvaqbj.IjNqNQ_VQ.Cnaryf[c].Jvqgu,o=g+jvaqbj.IjNqNQ_VQ.Cnaryf[c].Urvtug;vs((pheK<y)||(pheK>e)||(pheL<g)||(pheL>o)){vs(jvaqbj.IjBaEbyybssNQ_VQ)IjBaEbyybssNQ_VQ(c);ryfr IjPybfrNq(NQ_VQ,c,gehr,\"\");}ryfr erghea;}IjPnapryZbhfrYvfgrareNQ_VQ();};;jvaqbj.IjFrgEbyybssCnaryNQ_VQ=shapgvba(c){ine z=\"zbhfrzbir\",q=qbphzrag,s=IjPurpxZbhfrCbfvgvbaNQ_VQ;c=IjTc(NQ_VQ,c);vs(jvaqbj.IjNqNQ_VQ&&jvaqbj.IjNqNQ_VQ.EbyybssCnary>-1)IjPnapryZbhfrYvfgrareNQ_VQ();vs(jvaqbj.IjNqNQ_VQ)jvaqbj.IjNqNQ_VQ.EbyybssCnary=c;gel{vs(q.nqqRiragYvfgrare)q.nqqRiragYvfgrare(z,s,snyfr);ryfr vs(q.nggnpuRirag)q.nggnpuRirag(\"ba\"+z,s);}pngpu(r){}};;jvaqbj.IjPnapryZbhfrYvfgrareNQ_VQ=shapgvba(){ine z=\"zbhfrzbir\",q=qbphzrag,s=IjPurpxZbhfrCbfvgvbaNQ_VQ;vs(jvaqbj.IjNqNQ_VQ)jvaqbj.IjNqNQ_VQ.EbyybssCnary=-1;gel{vs(q.erzbirRiragYvfgrare)q.erzbirRiragYvfgrare(z,s,snyfr);ryfr vs(q.qrgnpuRirag)q.qrgnpuRirag(\"ba\"+z,s);}pngpu(r){}};;jvaqbj.IjNqNQ_VQ.IjTc=shapgvba(n,c){ine nq=jvaqbj.IjNqNQ_VQ;vs(vfAnA(c)){sbe(ine v=0;v<nq.Cnaryf.yratgu;v++)vs(nq.Cnaryf[v].Anzr==c)erghea v;erghea 0;}erghea c;};;jvaqbj.IjNqNQ_VQ.IjTpy=shapgvba(n,c,p){ine cn=jvaqbj.IjNqNQ_VQ.Cnaryf[IjTc(n,c)];vs(!cn)erghea 0;vs(vfAnA(p)){sbe(ine v=0;v<cn.Pyvpxguehf.yratgu;v++)vs(cn.Pyvpxguehf[v].Anzr==p)erghea v;erghea 0;}erghea p;};;jvaqbj.IjNqNQ_VQ.IjGenpr=shapgvba(n,f){gel{vs(jvaqbj[\"Ij\"+\"QtQ\"])jvaqbj[\"Ij\"+\"QtQ\"](n,1,f);}pngpu(r){}};;jvaqbj.IjNqNQ_VQ.IjYvzvg1=shapgvba(n,f){ine nq=jvaqbj.IjNqNQ_VQ,vh=f.fcyvg(\"/\");sbe(ine v=0,p=0;v<vh.yratgu;v++){vs(vh[v].yratgu>0){vs(nq.FzV.yratgu>0)nq.FzV+=\"/\";nq.FzV+=vh[v];nq.FtZ[nq.FtZ.yratgu]=snyfr;}}};;jvaqbj.IjNqNQ_VQ.IjYvzvg0=shapgvba(n,f){ine nq=jvaqbj.IjNqNQ_VQ,vh=f.fcyvg(\"/\");sbe(ine v=0;v<vh.yratgu;v++){vs(vh[v].yratgu>0){vs(nq.OvC.yratgu>0)nq.OvC+=\"/\";nq.OvC+=vh[v];}}};;jvaqbj.IjNqNQ_VQ.IjRVST=shapgvba(n,c){jvaqbj[\"IjCnayNQ_VQ_\"+c+\"_Bow\"]=IjTrgBow(\"IjCnayNQ_VQ_\"+c+\"_Bow\");vs(jvaqbj[\"IjCnayNQ_VQ_\"+c+\"_Bow\"]==ahyy)frgGvzrbhg(\"IjRVST(NQ_VQ,\"+c+\")\",jvaqbj.IjNqNQ_VQ.rvsg);};;jvaqbj.IjNqNQ_VQ.IjNavzSHC=shapgvba(n,c){ine nq=jvaqbj.IjNqNQ_VQ;vs(c>nq.Cnaryf.yratgu)erghea;ine cna=nq.Cnaryf[c],nn=gehr,on=gehr,yn=gehr,en=gehr,cn=nq.Cnaryf[0],sf=nq.ShF,j=cn.Jvqgu,u=cn.Urvtug;vs(j==\"100%\"){j=sf;en=snyfr;yn=snyfr;}vs(u==\"100%\"){u=sf;nn=snyfr;on=snyfr;}vs(cn.YnY==\"Y\")yn=snyfr;vs(cn.YnY==\"E\")en=snyfr;vs(cn.GnY==\"G\")nn=snyfr;vs(cn.GnY==\"O\")on=snyfr;ine k=0,l=0;fjvgpu(nq.NshP%8){pnfr 0:oernx;pnfr 1:vs(nn)l=-sf;oernx;pnfr 2:k=j-sf;oernx;pnfr 3:vs(en)k=j;oernx;pnfr 4:k=j-sf;l=u-sf;oernx;pnfr 5:k=j-sf;vs(on)l=u;oernx;pnfr 6:l=u-sf;oernx;pnfr 7:vs(yn)k=-sf;l=u-sf;oernx;}vs(nq.NshP++ <nq.NshG)frgGvzrbhg((\"IjNavzSHC(NQ_VQ,\"+c+\")\"),nq.NshC);ryfr{k=-1000;l=k;}cna.YrsgBssfrg=k;cna.GbcBssfrg=l;IjNhErcb(n,c);};;jvaqbj.IjNqNQ_VQ.IjTrgErnyCbfvgvba=shapgvba(n,b,j){erghea IjBOEC.nccyl(guvf,nethzragf);};;jvaqbj.IjNqNQ_VQ.IjPnapryGvzrbhg=shapgvba(n,c){c=IjTc(n,c);ine cay=jvaqbj.IjNqNQ_VQ.Cnaryf[c];vs(cay&&cay.UgU!=\"\"){pyrneGvzrbhg(cay.UgU);}};;jvaqbj.IjNqNQ_VQ.IjPnapryNyyGvzrbhgf=shapgvba(n){vs(jvaqbj.IjNqNQ_VQ.YbpxGvzrbhgPunatrf)erghea;sbe(ine c=0;c<jvaqbj.IjNqNQ_VQ.bac;c++)IjPnapryGvzrbhg(n,c);};;jvaqbj.IjNqNQ_VQ.IjFgnegGvzrbhg=shapgvba(n,c,bG){c=IjTc(n,c);ine cay=jvaqbj.IjNqNQ_VQ.Cnaryf[c];vs(cay&&((cay.UvqrGvzrbhgInyhr>0)||(nethzragf.yratgu==3&&bG>0))){pyrneGvzrbhg(cay.UgU);cay.UgU=frgGvzrbhg(cay.UvqrNpgvba,(nethzragf.yratgu==3?bG:cay.UvqrGvzrbhgInyhr));}};;jvaqbj.IjNqNQ_VQ.IjErfrgGvzrbhg=shapgvba(n,c,bG){c=IjTc(n,c);IjPnapryGvzrbhg(n,c);riny(\"IjFgnegGvzrbhg(NQ_VQ,c\"+(nethzragf.yratgu==3?\",bG\":\"\")+\")\");};;jvaqbj.IjNqNQ_VQ.IjErfrgNyyGvzrbhgf=shapgvba(n){sbe(ine c=0;c<jvaqbj.IjNqNQ_VQ.bac;c++)IjErfrgGvzrbhg(n,c);};;jvaqbj.IjNqNQ_VQ.IjQrgnpure=shapgvba(n,rig,sap){gel{vs(IjQVR5)riny(\"jvaqbj.qrgnpuRirag(\\'ba\"+rig+\"\\',\"+sap+\"NQ_VQ)\");ryfr vs(!IjQVRZnp)riny(\"jvaqbj.erzbir';\n  var str9 = ';;jvaqbj.IjPurpxZbhfrCbfvgvbaNQ_VQ=shapgvba(r){vs(!r)ine r=jvaqbj.rirag;ine c=-1;vs(jvaqbj.IjNqNQ_VQ)c=jvaqbj.IjNqNQ_VQ.EbyybssCnary;ine bo=IjTrgBow(\"IjCnayNQ_VQ_\"+c);vs(bo&&bo.fglyr.ivfvovyvgl==\"ivfvoyr\"){ine fns=IjFns?8:0;ine pheK=r.pyvragK+IjBOFpe(\"U\")+fns,pheL=r.pyvragL+IjBOFpe(\"I\")+fns;ine y=IjBOEC(NQ_VQ,bo,\"Y\"),g=IjBOEC(NQ_VQ,bo,\"G\");ine e=y+jvaqbj.IjNqNQ_VQ.Cnaryf[c].Jvqgu,o=g+jvaqbj.IjNqNQ_VQ.Cnaryf[c].Urvtug;vs((pheK<y)||(pheK>e)||(pheL<g)||(pheL>o)){vs(jvaqbj.IjBaEbyybssNQ_VQ)IjBaEbyybssNQ_VQ(c);ryfr IjPybfrNq(NQ_VQ,c,gehr,\"\");}ryfr erghea;}IjPnapryZbhfrYvfgrareNQ_VQ();};;jvaqbj.IjFrgEbyybssCnaryNQ_VQ=shapgvba(c){ine z=\"zbhfrzbir\",q=qbphzrag,s=IjPurpxZbhfrCbfvgvbaNQ_VQ;c=IjTc(NQ_VQ,c);vs(jvaqbj.IjNqNQ_VQ&&jvaqbj.IjNqNQ_VQ.EbyybssCnary>-1)IjPnapryZbhfrYvfgrareNQ_VQ();vs(jvaqbj.IjNqNQ_VQ)jvaqbj.IjNqNQ_VQ.EbyybssCnary=c;gel{vs(q.nqqRiragYvfgrare)q.nqqRiragYvfgrare(z,s,snyfr);ryfr vs(q.nggnpuRirag)q.nggnpuRirag(\"ba\"+z,s);}pngpu(r){}};;jvaqbj.IjPnapryZbhfrYvfgrareNQ_VQ=shapgvba(){ine z=\"zbhfrzbir\",q=qbphzrag,s=IjPurpxZbhfrCbfvgvbaNQ_VQ;vs(jvaqbj.IjNqNQ_VQ)jvaqbj.IjNqNQ_VQ.EbyybssCnary=-1;gel{vs(q.erzbirRiragYvfgrare)q.erzbirRiragYvfgrare(z,s,snyfr);ryfr vs(q.qrgnpuRirag)q.qrgnpuRirag(\"ba\"+z,s);}pngpu(r){}};;jvaqbj.IjNqNQ_VQ.IjTc=d2(n,c){ine nq=jvaqbj.IjNqNQ_VQ;vs(vfAnA(c)){sbe(ine v=0;v<nq.Cnaryf.yratgu;v++)vs(nq.Cnaryf[v].Anzr==c)erghea v;erghea 0;}erghea c;};;jvaqbj.IjNqNQ_VQ.IjTpy=d2(n,c,p){ine cn=jvaqbj.IjNqNQ_VQ.Cnaryf[IjTc(n,c)];vs(!cn)erghea 0;vs(vfAnA(p)){sbe(ine v=0;v<cn.Pyvpxguehf.yratgu;v++)vs(cn.Pyvpxguehf[v].Anzr==p)erghea v;erghea 0;}erghea p;};;jvaqbj.IjNqNQ_VQ.IjGenpr=d2(n,f){gel{vs(jvaqbj[\"Ij\"+\"QtQ\"])jvaqbj[\"Ij\"+\"QtQ\"](n,1,f);}pngpu(r){}};;jvaqbj.IjNqNQ_VQ.IjYvzvg1=d2(n,f){ine nq=jvaqbj.IjNqNQ_VQ,vh=f.fcyvg(\"/\");sbe(ine v=0,p=0;v<vh.yratgu;v++){vs(vh[v].yratgu>0){vs(nq.FzV.yratgu>0)nq.FzV+=\"/\";nq.FzV+=vh[v];nq.FtZ[nq.FtZ.yratgu]=snyfr;}}};;jvaqbj.IjNqNQ_VQ.IjYvzvg0=d2(n,f){ine nq=jvaqbj.IjNqNQ_VQ,vh=f.fcyvg(\"/\");sbe(ine v=0;v<vh.yratgu;v++){vs(vh[v].yratgu>0){vs(nq.OvC.yratgu>0)nq.OvC+=\"/\";nq.OvC+=vh[v];}}};;jvaqbj.IjNqNQ_VQ.IjRVST=d2(n,c){jvaqbj[\"IjCnayNQ_VQ_\"+c+\"_Bow\"]=IjTrgBow(\"IjCnayNQ_VQ_\"+c+\"_Bow\");vs(jvaqbj[\"IjCnayNQ_VQ_\"+c+\"_Bow\"]==ahyy)frgGvzrbhg(\"IjRVST(NQ_VQ,\"+c+\")\",jvaqbj.IjNqNQ_VQ.rvsg);};;jvaqbj.IjNqNQ_VQ.IjNavzSHC=d2(n,c){ine nq=jvaqbj.IjNqNQ_VQ;vs(c>nq.Cnaryf.yratgu)erghea;ine cna=nq.Cnaryf[c],nn=gehr,on=gehr,yn=gehr,en=gehr,cn=nq.Cnaryf[0],sf=nq.ShF,j=cn.Jvqgu,u=cn.Urvtug;vs(j==\"100%\"){j=sf;en=snyfr;yn=snyfr;}vs(u==\"100%\"){u=sf;nn=snyfr;on=snyfr;}vs(cn.YnY==\"Y\")yn=snyfr;vs(cn.YnY==\"E\")en=snyfr;vs(cn.GnY==\"G\")nn=snyfr;vs(cn.GnY==\"O\")on=snyfr;ine k=0,l=0;fjvgpu(nq.NshP%8){pnfr 0:oernx;pnfr 1:vs(nn)l=-sf;oernx;pnfr 2:k=j-sf;oernx;pnfr 3:vs(en)k=j;oernx;pnfr 4:k=j-sf;l=u-sf;oernx;pnfr 5:k=j-sf;vs(on)l=u;oernx;pnfr 6:l=u-sf;oernx;pnfr 7:vs(yn)k=-sf;l=u-sf;oernx;}vs(nq.NshP++ <nq.NshG)frgGvzrbhg((\"IjNavzSHC(NQ_VQ,\"+c+\")\"),nq.NshC);ryfr{k=-1000;l=k;}cna.YrsgBssfrg=k;cna.GbcBssfrg=l;IjNhErcb(n,c);};;jvaqbj.IjNqNQ_VQ.IjTrgErnyCbfvgvba=d2(n,b,j){erghea IjBOEC.nccyl(guvf,nethzragf);};;jvaqbj.IjNqNQ_VQ.IjPnapryGvzrbhg=d2(n,c){c=IjTc(n,c);ine cay=jvaqbj.IjNqNQ_VQ.Cnaryf[c];vs(cay&&cay.UgU!=\"\"){pyrneGvzrbhg(cay.UgU);}};;jvaqbj.IjNqNQ_VQ.IjPnapryNyyGvzrbhgf=d2(n){vs(jvaqbj.IjNqNQ_VQ.YbpxGvzrbhgPunatrf)erghea;sbe(ine c=0;c<jvaqbj.IjNqNQ_VQ.bac;c++)IjPnapryGvzrbhg(n,c);};;jvaqbj.IjNqNQ_VQ.IjFgnegGvzrbhg=d2(n,c,bG){c=IjTc(n,c);ine cay=jvaqbj.IjNqNQ_VQ.Cnaryf[c];vs(cay&&((cay.UvqrGvzrbhgInyhr>0)||(nethzragf.yratgu==3&&bG>0))){pyrneGvzrbhg(cay.UgU);cay.UgU=frgGvzrbhg(cay.UvqrNpgvba,(nethzragf.yratgu==3?bG:cay.UvqrGvzrbhgInyhr));}};;jvaqbj.IjNqNQ_VQ.IjErfrgGvzrbhg=d2(n,c,bG){c=IjTc(n,c);IjPnapryGvzrbhg(n,c);riny(\"IjFgnegGvzrbhg(NQ_VQ,c\"+(nethzragf.yratgu==3?\",bG\":\"\")+\")\");};;jvaqbj.IjNqNQ_VQ.IjErfrgNyyGvzrbhgf=d2(n){sbe(ine c=0;c<jvaqbj.IjNqNQ_VQ.bac;c++)IjErfrgGvzrbhg(n,c);};;jvaqbj.IjNqNQ_VQ.IjQrgnpure=d2(n,rig,sap){gel{vs(IjQVR5)riny(\"jvaqbj.qrgnpuRirag(\\'ba\"+rig+\"\\',\"+sap+\"NQ_VQ)\");ryfr vs(!IjQVRZnp)riny(\"jvaqbj.erzbirRiragYvfgrare(\\'\"+rig+\"\\',\"+sap+\"NQ_VQ,snyfr)\");}pngpu(r){}};;jvaqbj.IjNqNQ_VQ.IjPyrna';\n\n  var s26 = computeInputVariants('VC=74.125.75.1', 81);\n  var s27 = computeInputVariants('9.0  e115', 78);\n  var s28 = computeInputVariants('k', 78);\n  var s29 = computeInputVariants(str2, 81);\n  var s30 = computeInputVariants(str3, 81);\n  var s31 = computeInputVariants('144631658', 78);\n  var s32 = computeInputVariants('Pbhagel=IIZ%3Q', 78);\n  var s33 = computeInputVariants('Pbhagel=IIZ=', 78);\n  var s34 = computeInputVariants('CersreerqPhygherCraqvat=', 78);\n  var s35 = computeInputVariants(str4, 78);\n  var s36 = computeInputVariants(str5, 78);\n  var s37 = computeInputVariants('__hgzp=144631658', 78);\n  var s38 = computeInputVariants('gvzrMbar=-8', 78);\n  var s39 = computeInputVariants('gvzrMbar=0', 78);\n  // var s40 = computeInputVariants(s15[i], 78);\n  var s41 = computeInputVariants('vachggrkg  QBZPbageby_cynprubyqre', 78);\n  var s42 = computeInputVariants('xrlqbja', 78);\n  var s43 = computeInputVariants('xrlhc', 78);\n  var s44 = computeInputVariants('uggc://zrffntvat.zlfcnpr.pbz/vaqrk.psz', 77);\n  var s45 = computeInputVariants('FrffvbaFgbentr=%7O%22GnoThvq%22%3N%7O%22thvq%22%3N1231367125017%7Q%7Q', 73);\n  var s46 = computeInputVariants(str6, 72);\n  var s47 = computeInputVariants('3.5.0.0', 70);\n  var s48 = computeInputVariants(str7, 70);\n  var s49 = computeInputVariants(str8, 70);\n  var s50 = computeInputVariants(str9, 70);\n  var s51 = computeInputVariants('NI%3Q1_CI%3Q1_PI%3Q1_EI%3Q1_HI%3Q1_HP%3Q1_IC%3Q0.0.0.0_IH%3Q0', 70);\n  var s52 = computeInputVariants('svz_zlfcnpr_ubzrcntr_abgybttrqva,svz_zlfcnpr_aba_HTP,svz_zlfcnpr_havgrq-fgngrf', 70);\n  var s53 = computeInputVariants('ybnqvat', 70);\n  var s54 = computeInputVariants('#', 68);\n  var s55 = computeInputVariants('ybnqrq', 68);\n  var s56 = computeInputVariants('pbybe', 49);\n  var s57 = computeInputVariants('uggc://sevraqf.zlfcnpr.pbz/vaqrk.psz', 44);\n\n  function runBlock1() {\n    for (var i = 0; i < 81; i++) {\n      re8.exec(s26[i]);\n    }\n    for (var i = 0; i < 78; i++) {\n      s27[i].replace(/(\\s)+e/, '');\n      s28[i].replace(/./, '');\n      s29[i].replace(re17, '');\n      s30[i].replace(re17, '');\n      re8.exec(s31[i]);\n      re8.exec(s32[i]);\n      re8.exec(s33[i]);\n      re8.exec(s34[i]);\n      re8.exec(s35[i]);\n      re8.exec(s36[i]);\n      re8.exec(s37[i]);\n      re8.exec(s38[i]);\n      re8.exec(s39[i]);\n      /Fnsnev\\/(\\d+\\.\\d+)/.exec(s15[i]);\n      re3.exec(s41[i]);\n      re0.exec(s42[i]);\n      re0.exec(s43[i]);\n    }\n    for (var i = 0; i < 77; i++) {\n      s44[i].replace(re12, '');\n      re13.exec(s44[i]);\n    }\n    for (var i = 0; i < 73; i++) {\n      s45[i].replace(re18, '');\n    }\n    for (var i = 0; i < 72; i++) {\n      re1.exec(s46[i]);\n    }\n    for (var i = 0; i < 71; i++) {\n      re19.exec('');\n    }\n    for (var i = 0; i < 70; i++) {\n      s47[i].replace(re11, '');\n      s48[i].replace(/d1/g, '');\n      s49[i].replace(/NQ_VQ/g, '');\n      s50[i].replace(/d2/g, '');\n      s51[i].replace(/_/g, '');\n      s52[i].split(re20);\n      re21.exec(s53[i]);\n    }\n    for (var i = 0; i < 68; i++) {\n      re1.exec(s54[i]);\n      /(?:ZFVR.(\\d+\\.\\d+))|(?:(?:Sversbk|TenaCnenqvfb|Vprjrnfry).(\\d+\\.\\d+))|(?:Bcren.(\\d+\\.\\d+))|(?:NccyrJroXvg.(\\d+(?:\\.\\d+)?))/.exec(s15[i]);\n      /(Znp BF K)|(Jvaqbjf;)/.exec(s15[i]);\n      /Trpxb\\/([0-9]+)/.exec(s15[i]);\n      re21.exec(s55[i]);\n    }\n    for (var i = 0; i < 49; i++) {\n      re16.exec(s56[i]);\n    }\n    for (var i = 0; i < 44; i++) {\n      s57[i].replace(re12, '');\n      re13.exec(s57[i]);\n    }\n  }\n\n  var re22 = /\\bso_zrah\\b/;\n  var re23 = /^(?:(?:[^:\\/?#]+):)?(?:\\/\\/(?:[^\\/?#]*))?([^?#]*)(?:\\?([^#]*))?(?:#(.*))?/;\n  var re24 = /uggcf?:\\/\\/([^\\/]+\\.)?snprobbx\\.pbz\\//;\n  var re25 = /\"/g;\n  var re26 = /^([^?#]+)(?:\\?([^#]*))?(#.*)?/;\n  var s57a = computeInputVariants('fryrpgrq', 40);\n  var s58 = computeInputVariants('vachggrkg uvqqra_ryrz', 40);\n  var s59 = computeInputVariants('vachggrkg ', 40);\n  var s60 = computeInputVariants('vachggrkg', 40);\n  var s61 = computeInputVariants('uggc://jjj.snprobbx.pbz/', 40);\n  var s62 = computeInputVariants('uggc://jjj.snprobbx.pbz/ybtva.cuc', 40);\n  var s63 = computeInputVariants('Funer guvf tnqtrg', 40);\n  var s64 = computeInputVariants('uggc://jjj.tbbtyr.pbz/vt/qverpgbel', 40);\n  var s65 = computeInputVariants('419', 40);\n  var s66 = computeInputVariants('gvzrfgnzc', 40);\n\n  function runBlock2() {\n    for (var i = 0; i < 40; i++) {\n      s57a[i].replace(re14, '');\n      s57a[i].replace(re15, '');\n    }\n    for (var i = 0; i < 39; i++) {\n      s58[i].replace(/\\buvqqra_ryrz\\b/g, '');\n      re3.exec(s59[i]);\n      re3.exec(s60[i]);\n      re22.exec('HVYvaxOhggba');\n      re22.exec('HVYvaxOhggba_E');\n      re22.exec('HVYvaxOhggba_EJ');\n      re22.exec('zrah_ybtva_pbagnvare');\n      /\\buvqqra_ryrz\\b/.exec('vachgcnffjbeq');\n    }\n    for (var i = 0; i < 37; i++) {\n      re8.exec('111soqs57qo8o8480qo18sor2011r3n591q7s6s37r120904');\n      re8.exec('SbeprqRkcvengvba=633669315660164980');\n      re8.exec('FrffvbaQQS2=111soqs57qo8o8480qo18sor2011r3n591q7s6s37r120904');\n    }\n    for (var i = 0; i < 35; i++) {\n      'puvyq p1 svefg'.replace(re14, '');\n      'puvyq p1 svefg'.replace(re15, '');\n      'sylbhg pybfrq'.replace(re14, '');\n      'sylbhg pybfrq'.replace(re15, '');\n    }\n    for (var i = 0; i < 34; i++) {\n      re19.exec('gno2');\n      re19.exec('gno3');\n      re8.exec('44132r503660');\n      re8.exec('SbeprqRkcvengvba=633669316860113296');\n      re8.exec('AFP_zp_dfctwzs-aowb_80=44132r503660');\n      re8.exec('FrffvbaQQS2=s6r4579npn4rn2135s904r0s75pp1o5334p6s6pospo12696');\n      re8.exec('s6r4579npn4rn2135s904r0s75pp1o5334p6s6pospo12696');\n    }\n    for (var i = 0; i < 32; i++) {\n      /puebzr/i.exec(s15[i]);\n    }\n    for (var i = 0; i < 31; i++) {\n      s61[i].replace(re23, '');\n      re8.exec('SbeprqRkcvengvba=633669358527244818');\n      re8.exec('VC=66.249.85.130');\n      re8.exec('FrffvbaQQS2=s15q53p9n372sn76npr13o271n4s3p5r29p235746p908p58');\n      re8.exec('s15q53p9n372sn76npr13o271n4s3p5r29p235746p908p58');\n      re24.exec(s61[i]);\n    }\n    for (var i = 0; i < 30; i++) {\n      s65[i].replace(re6, '');\n      /(?:^|\\s+)gvzrfgnzc(?:\\s+|$)/.exec(s66[i]);\n      re7.exec(s65[i]);\n    }\n    for (var i = 0; i < 29; i++) {\n      s62[i].replace(re23, '');\n    }\n    for (var i = 0; i < 28; i++) {\n      s63[i].replace(re25, '');\n      s63[i].replace(re12, '');\n      re26.exec(s64[i]);\n    }\n  }\n\n  var re27 = /-\\D/g;\n  var re28 = /\\bnpgvingr\\b/;\n  var re29 = /%2R/gi;\n  var re30 = /%2S/gi;\n  var re31 = /^(mu-(PA|GJ)|wn|xb)$/;\n  var re32 = /\\s?;\\s?/;\n  var re33 = /%\\w?$/;\n  var re34 = /TNQP=([^;]*)/i;\n  var str10 = 'FrffvbaQQS2=111soqs57qo8o8480qo18sor2011r3n591q7s6s37r120904; ZFPhygher=VC=74.125.75.1&VCPhygher=ra-HF&CersreerqPhygher=ra-HF&CersreerqPhygherCraqvat=&Pbhagel=IIZ=&SbeprqRkcvengvba=633669315660164980&gvzrMbar=0&HFEYBP=DKWyLHAiMTH9AwHjWxAcqUx9GJ91oaEunJ4tIzyyqlMQo3IhqUW5D29xMG1IHlMQo3IhqUW5GzSgMG1Iozy0MJDtH3EuqTImWxEgLHAiMTH9BQN3WxkuqTy0qJEyCGZ3YwDkBGVzGT9hM2y0qJEyCF0kZwVhZQH3APMDo3A0LJkQo2EyCGx0ZQDmWyWyM2yiox5uoJH9D0R=';\n  var str11 = 'FrffvbaQQS2=111soqs57qo8o8480qo18sor2011r3n591q7s6s37r120904; __hgzm=144631658.1231363570.1.1.hgzpfe=(qverpg)|hgzppa=(qverpg)|hgzpzq=(abar); __hgzn=144631658.3426875219718084000.1231363570.1231363570.1231363570.1; __hgzo=144631658.0.10.1231363570; __hgzp=144631658; ZFPhygher=VC=74.125.75.1&VCPhygher=ra-HF&CersreerqPhygher=ra-HF&Pbhagel=IIZ%3Q&SbeprqRkcvengvba=633669315660164980&gvzrMbar=-8&HFEYBP=DKWyLHAiMTH9AwHjWxAcqUx9GJ91oaEunJ4tIzyyqlMQo3IhqUW5D29xMG1IHlMQo3IhqUW5GzSgMG1Iozy0MJDtH3EuqTImWxEgLHAiMTH9BQN3WxkuqTy0qJEyCGZ3YwDkBGVzGT9hM2y0qJEyCF0kZwVhZQH3APMDo3A0LJkQo2EyCGx0ZQDmWyWyM2yiox5uoJH9D0R%3Q';\n  var str12 = 'uggc://tbbtyrnqf.t.qbhoyrpyvpx.arg/cntrnq/nqf?pyvrag=pn-svz_zlfcnpr_zlfcnpr-ubzrcntr_wf&qg=1231363514065&uy=ra&nqfnsr=uvtu&br=hgs8&ahz_nqf=4&bhgchg=wf&nqgrfg=bss&pbeeryngbe=1231363514065&punaary=svz_zlfcnpr_ubzrcntr_abgybttrqva%2Psvz_zlfcnpr_aba_HTP%2Psvz_zlfcnpr_havgrq-fgngrf&hey=uggc%3N%2S%2Subzr.zlfcnpr.pbz%2Svaqrk.psz&nq_glcr=grkg&rvq=6083027&rn=0&sez=0&tn_ivq=1326469221.1231363557&tn_fvq=1231363557&tn_uvq=1114636509&synfu=9.0.115&h_u=768&h_j=1024&h_nu=738&h_nj=1024&h_pq=24&h_gm=-480&h_uvf=2&h_wnin=gehr&h_acyht=7&h_azvzr=22';\n  var str13 = 'ZFPhygher=VC=74.125.75.1&VCPhygher=ra-HF&CersreerqPhygher=ra-HF&Pbhagel=IIZ%3Q&SbeprqRkcvengvba=633669315660164980&gvzrMbar=-8&HFEYBP=DKWyLHAiMTH9AwHjWxAcqUx9GJ91oaEunJ4tIzyyqlMQo3IhqUW5D29xMG1IHlMQo3IhqUW5GzSgMG1Iozy0MJDtH3EuqTImWxEgLHAiMTH9BQN3WxkuqTy0qJEyCGZ3YwDkBGVzGT9hM2y0qJEyCF0kZwVhZQH3APMDo3A0LJkQo2EyCGx0ZQDmWyWyM2yiox5uoJH9D0R%3Q';\n  var str14 = 'ZFPhygher=VC=74.125.75.1&VCPhygher=ra-HF&CersreerqPhygher=ra-HF&CersreerqPhygherCraqvat=&Pbhagel=IIZ=&SbeprqRkcvengvba=633669315660164980&gvzrMbar=0&HFEYBP=DKWyLHAiMTH9AwHjWxAcqUx9GJ91oaEunJ4tIzyyqlMQo3IhqUW5D29xMG1IHlMQo3IhqUW5GzSgMG1Iozy0MJDtH3EuqTImWxEgLHAiMTH9BQN3WxkuqTy0qJEyCGZ3YwDkBGVzGT9hM2y0qJEyCF0kZwVhZQH3APMDo3A0LJkQo2EyCGx0ZQDmWyWyM2yiox5uoJH9D0R=';\n  var re35 = /[<>]/g;\n  var str15 = 'FrffvbaQQS2=s6r4579npn4rn2135s904r0s75pp1o5334p6s6pospo12696; ZFPhygher=VC=74.125.75.1&VCPhygher=ra-HF&CersreerqPhygher=ra-HF&CersreerqPhygherCraqvat=&Pbhagel=IIZ=&SbeprqRkcvengvba=633669316860113296&gvzrMbar=0&HFEYBP=DKWyLHAiMTH9AwHjWxAcqUx9GJ91oaEunJ4tIzyyqlMQo3IhqUW5D29xMG1IHlMQo3IhqUW5GzSgMG1Iozy0MJDtH3EuqTImWxEgLHAiMTH9BQN3WxkuqTy0qJEyCGZ3YwDkBGVzGT9hM2y0qJEyCF0kZwVhZQH3APMDo3A0LJkQo2EyCGx0ZQDmWyWyM2yiox5uoJH9D0R=; AFP_zp_dfctwzs-aowb_80=44132r503660';\n  var str16 = 'FrffvbaQQS2=s6r4579npn4rn2135s904r0s75pp1o5334p6s6pospo12696; AFP_zp_dfctwzs-aowb_80=44132r503660; __hgzm=144631658.1231363638.1.1.hgzpfe=(qverpg)|hgzppa=(qverpg)|hgzpzq=(abar); __hgzn=144631658.965867047679498800.1231363638.1231363638.1231363638.1; __hgzo=144631658.0.10.1231363638; __hgzp=144631658; ZFPhygher=VC=74.125.75.1&VCPhygher=ra-HF&CersreerqPhygher=ra-HF&Pbhagel=IIZ%3Q&SbeprqRkcvengvba=633669316860113296&gvzrMbar=-8&HFEYBP=DKWyLHAiMTH9AwHjWxAcqUx9GJ91oaEunJ4tIzyyqlMQo3IhqUW5D29xMG1IHlMQo3IhqUW5GzSgMG1Iozy0MJDtH3EuqTImWxEgLHAiMTH9BQN3WxkuqTy0qJEyCGZ3YwDkBGVzGT9hM2y0qJEyCF0kZwVhZQH3APMDo3A0LJkQo2EyCGx0ZQDmWyWyM2yiox5uoJH9D0R%3Q';\n  var str17 = 'uggc://tbbtyrnqf.t.qbhoyrpyvpx.arg/cntrnq/nqf?pyvrag=pn-svz_zlfcnpr_zlfcnpr-ubzrcntr_wf&qg=1231363621014&uy=ra&nqfnsr=uvtu&br=hgs8&ahz_nqf=4&bhgchg=wf&nqgrfg=bss&pbeeryngbe=1231363621014&punaary=svz_zlfcnpr_ubzrcntr_abgybttrqva%2Psvz_zlfcnpr_aba_HTP%2Psvz_zlfcnpr_havgrq-fgngrf&hey=uggc%3N%2S%2Scebsvyr.zlfcnpr.pbz%2Svaqrk.psz&nq_glcr=grkg&rvq=6083027&rn=0&sez=0&tn_ivq=348699119.1231363624&tn_fvq=1231363624&tn_uvq=895511034&synfu=9.0.115&h_u=768&h_j=1024&h_nu=738&h_nj=1024&h_pq=24&h_gm=-480&h_uvf=2&h_wnin=gehr&h_acyht=7&h_azvzr=22';\n  var str18 = 'uggc://jjj.yrobapbva.se/yv';\n  var str19 = 'ZFPhygher=VC=74.125.75.1&VCPhygher=ra-HF&CersreerqPhygher=ra-HF&Pbhagel=IIZ%3Q&SbeprqRkcvengvba=633669316860113296&gvzrMbar=-8&HFEYBP=DKWyLHAiMTH9AwHjWxAcqUx9GJ91oaEunJ4tIzyyqlMQo3IhqUW5D29xMG1IHlMQo3IhqUW5GzSgMG1Iozy0MJDtH3EuqTImWxEgLHAiMTH9BQN3WxkuqTy0qJEyCGZ3YwDkBGVzGT9hM2y0qJEyCF0kZwVhZQH3APMDo3A0LJkQo2EyCGx0ZQDmWyWyM2yiox5uoJH9D0R%3Q';\n  var str20 = 'ZFPhygher=VC=74.125.75.1&VCPhygher=ra-HF&CersreerqPhygher=ra-HF&CersreerqPhygherCraqvat=&Pbhagel=IIZ=&SbeprqRkcvengvba=633669316860113296&gvzrMbar=0&HFEYBP=DKWyLHAiMTH9AwHjWxAcqUx9GJ91oaEunJ4tIzyyqlMQo3IhqUW5D29xMG1IHlMQo3IhqUW5GzSgMG1Iozy0MJDtH3EuqTImWxEgLHAiMTH9BQN3WxkuqTy0qJEyCGZ3YwDkBGVzGT9hM2y0qJEyCF0kZwVhZQH3APMDo3A0LJkQo2EyCGx0ZQDmWyWyM2yiox5uoJH9D0R=';\n\n  var s67 = computeInputVariants('e115', 27);\n  var s68 = computeInputVariants('qvfcynl', 27);\n  var s69 = computeInputVariants('cbfvgvba', 27);\n  var s70 = computeInputVariants('uggc://jjj.zlfcnpr.pbz/', 27);\n  var s71 = computeInputVariants('cntrivrj', 27);\n  var s72 = computeInputVariants('VC=74.125.75.3', 27);\n  var s73 = computeInputVariants('ra', 27);\n  var s74 = computeInputVariants(str10, 27);\n  var s75 = computeInputVariants(str11, 27);\n  var s76 = computeInputVariants(str12, 27);\n  var s77 = computeInputVariants(str17, 27);\n  var s78 = computeInputVariants(str18, 27);\n\n  function runBlock3() {\n    for (var i = 0; i < 27; i++) {\n      s67[i].replace(/[A-Za-z]/g, '');\n    }\n    for (var i = 0; i < 23; i++) {\n      s68[i].replace(re27, '');\n      s69[i].replace(re27, '');\n    }\n    for (var i = 0; i < 22; i++) {\n      'unaqyr'.replace(re14, '');\n      'unaqyr'.replace(re15, '');\n      'yvar'.replace(re14, '');\n      'yvar'.replace(re15, '');\n      'cnerag puebzr6 fvatyr1 gno'.replace(re14, '');\n      'cnerag puebzr6 fvatyr1 gno'.replace(re15, '');\n      'fyvqre'.replace(re14, '');\n      'fyvqre'.replace(re15, '');\n      re28.exec('');\n    }\n    for (var i = 0; i < 21; i++) {\n      s70[i].replace(re12, '');\n      re13.exec(s70[i]);\n    }\n    for (var i = 0; i < 20; i++) {\n      s71[i].replace(re29, '');\n      s71[i].replace(re30, '');\n      re19.exec('ynfg');\n      re19.exec('ba svefg');\n      re8.exec(s72[i]);\n    }\n    for (var i = 0; i < 19; i++) {\n      re31.exec(s73[i]);\n    }\n    for (var i = 0; i < 18; i++) {\n      s74[i].split(re32);\n      s75[i].split(re32);\n      s76[i].replace(re33, '');\n      re8.exec('144631658.0.10.1231363570');\n      re8.exec('144631658.1231363570.1.1.hgzpfe=(qverpg)|hgzppa=(qverpg)|hgzpzq=(abar)');\n      re8.exec('144631658.3426875219718084000.1231363570.1231363570.1231363570.1');\n      re8.exec(str13);\n      re8.exec(str14);\n      re8.exec('__hgzn=144631658.3426875219718084000.1231363570.1231363570.1231363570.1');\n      re8.exec('__hgzo=144631658.0.10.1231363570');\n      re8.exec('__hgzm=144631658.1231363570.1.1.hgzpfe=(qverpg)|hgzppa=(qverpg)|hgzpzq=(abar)');\n      re34.exec(s74[i]);\n      re34.exec(s75[i]);\n    }\n    for (var i = 0; i < 17; i++) {\n      s15[i].match(/zfvr/gi);\n      s15[i].match(/bcren/gi);\n      str15.split(re32);\n      str16.split(re32);\n      'ohggba'.replace(re14, '');\n      'ohggba'.replace(re15, '');\n      'puvyq p1 svefg sylbhg pybfrq'.replace(re14, '');\n      'puvyq p1 svefg sylbhg pybfrq'.replace(re15, '');\n      'pvgvrf'.replace(re14, '');\n      'pvgvrf'.replace(re15, '');\n      'pybfrq'.replace(re14, '');\n      'pybfrq'.replace(re15, '');\n      'qry'.replace(re14, '');\n      'qry'.replace(re15, '');\n      'uqy_zba'.replace(re14, '');\n      'uqy_zba'.replace(re15, '');\n      s77[i].replace(re33, '');\n      s78[i].replace(/%3P/g, '');\n      s78[i].replace(/%3R/g, '');\n      s78[i].replace(/%3q/g, '');\n      s78[i].replace(re35, '');\n      'yvaxyvfg16'.replace(re14, '');\n      'yvaxyvfg16'.replace(re15, '');\n      'zvahf'.replace(re14, '');\n      'zvahf'.replace(re15, '');\n      'bcra'.replace(re14, '');\n      'bcra'.replace(re15, '');\n      'cnerag puebzr5 fvatyr1 ps NU'.replace(re14, '');\n      'cnerag puebzr5 fvatyr1 ps NU'.replace(re15, '');\n      'cynlre'.replace(re14, '');\n      'cynlre'.replace(re15, '');\n      'cyhf'.replace(re14, '');\n      'cyhf'.replace(re15, '');\n      'cb_uqy'.replace(re14, '');\n      'cb_uqy'.replace(re15, '');\n      'hyJVzt'.replace(re14, '');\n      'hyJVzt'.replace(re15, '');\n      re8.exec('144631658.0.10.1231363638');\n      re8.exec('144631658.1231363638.1.1.hgzpfe=(qverpg)|hgzppa=(qverpg)|hgzpzq=(abar)');\n      re8.exec('144631658.965867047679498800.1231363638.1231363638.1231363638.1');\n      re8.exec('4413268q3660');\n      re8.exec('4ss747o77904333q374or84qrr1s9r0nprp8r5q81534o94n');\n      re8.exec('SbeprqRkcvengvba=633669321699093060');\n      re8.exec('VC=74.125.75.20');\n      re8.exec(str19);\n      re8.exec(str20);\n      re8.exec('AFP_zp_tfwsbrg-aowb_80=4413268q3660');\n      re8.exec('FrffvbaQQS2=4ss747o77904333q374or84qrr1s9r0nprp8r5q81534o94n');\n      re8.exec('__hgzn=144631658.965867047679498800.1231363638.1231363638.1231363638.1');\n      re8.exec('__hgzo=144631658.0.10.1231363638');\n      re8.exec('__hgzm=144631658.1231363638.1.1.hgzpfe=(qverpg)|hgzppa=(qverpg)|hgzpzq=(abar)');\n      re34.exec(str15);\n      re34.exec(str16);\n    }\n  }\n\n  var re36 = /uers|fep|fryrpgrq/;\n  var re37 = /\\s*([+>~\\s])\\s*([a-zA-Z#.*:\\[])/g;\n  var re38 = /^(\\w+|\\*)$/;\n  var str21 = 'FrffvbaQQS2=s15q53p9n372sn76npr13o271n4s3p5r29p235746p908p58; ZFPhygher=VC=66.249.85.130&VCPhygher=ra-HF&CersreerqPhygher=ra-HF&CersreerqPhygherCraqvat=&Pbhagel=IIZ=&SbeprqRkcvengvba=633669358527244818&gvzrMbar=0&HFEYBP=DKWyLHAiMTH9AwHjWxAcqUx9GJ91oaEunJ4tIzyyqlMQo3IhqUW5D29xMG1IHlMQo3IhqUW5GzSgMG1Iozy0MJDtH3EuqTImWxEgLHAiMTH9BQN3WxkuqTy0qJEyCGZ3YwDkBGVzGT9hM2y0qJEyCF0kZwVhZQH3APMDo3A0LJkQo2EyCGx0ZQDmWyWyM2yiox5uoJH9D0R=';\n  var str22 = 'FrffvbaQQS2=s15q53p9n372sn76npr13o271n4s3p5r29p235746p908p58; __hgzm=144631658.1231367822.1.1.hgzpfe=(qverpg)|hgzppa=(qverpg)|hgzpzq=(abar); __hgzn=144631658.4127520630321984500.1231367822.1231367822.1231367822.1; __hgzo=144631658.0.10.1231367822; __hgzp=144631658; ZFPhygher=VC=66.249.85.130&VCPhygher=ra-HF&CersreerqPhygher=ra-HF&Pbhagel=IIZ%3Q&SbeprqRkcvengvba=633669358527244818&gvzrMbar=-8&HFEYBP=DKWyLHAiMTH9AwHjWxAcqUx9GJ91oaEunJ4tIzyyqlMQo3IhqUW5D29xMG1IHlMQo3IhqUW5GzSgMG1Iozy0MJDtH3EuqTImWxEgLHAiMTH9BQN3WxkuqTy0qJEyCGZ3YwDkBGVzGT9hM2y0qJEyCF0kZwVhZQH3APMDo3A0LJkQo2EyCGx0ZQDmWyWyM2yiox5uoJH9D0R%3Q';\n  var str23 = 'uggc://tbbtyrnqf.t.qbhoyrpyvpx.arg/cntrnq/nqf?pyvrag=pn-svz_zlfcnpr_zlfcnpr-ubzrcntr_wf&qg=1231367803797&uy=ra&nqfnsr=uvtu&br=hgs8&ahz_nqf=4&bhgchg=wf&nqgrfg=bss&pbeeryngbe=1231367803797&punaary=svz_zlfcnpr_ubzrcntr_abgybttrqva%2Psvz_zlfcnpr_aba_HTP%2Psvz_zlfcnpr_havgrq-fgngrf&hey=uggc%3N%2S%2Szrffntvat.zlfcnpr.pbz%2Svaqrk.psz&nq_glcr=grkg&rvq=6083027&rn=0&sez=0&tn_ivq=1192552091.1231367807&tn_fvq=1231367807&tn_uvq=1155446857&synfu=9.0.115&h_u=768&h_j=1024&h_nu=738&h_nj=1024&h_pq=24&h_gm=-480&h_uvf=2&h_wnin=gehr&h_acyht=7&h_azvzr=22';\n  var str24 = 'ZFPhygher=VC=66.249.85.130&VCPhygher=ra-HF&CersreerqPhygher=ra-HF&Pbhagel=IIZ%3Q&SbeprqRkcvengvba=633669358527244818&gvzrMbar=-8&HFEYBP=DKWyLHAiMTH9AwHjWxAcqUx9GJ91oaEunJ4tIzyyqlMQo3IhqUW5D29xMG1IHlMQo3IhqUW5GzSgMG1Iozy0MJDtH3EuqTImWxEgLHAiMTH9BQN3WxkuqTy0qJEyCGZ3YwDkBGVzGT9hM2y0qJEyCF0kZwVhZQH3APMDo3A0LJkQo2EyCGx0ZQDmWyWyM2yiox5uoJH9D0R%3Q';\n  var str25 = 'ZFPhygher=VC=66.249.85.130&VCPhygher=ra-HF&CersreerqPhygher=ra-HF&CersreerqPhygherCraqvat=&Pbhagel=IIZ=&SbeprqRkcvengvba=633669358527244818&gvzrMbar=0&HFEYBP=DKWyLHAiMTH9AwHjWxAcqUx9GJ91oaEunJ4tIzyyqlMQo3IhqUW5D29xMG1IHlMQo3IhqUW5GzSgMG1Iozy0MJDtH3EuqTImWxEgLHAiMTH9BQN3WxkuqTy0qJEyCGZ3YwDkBGVzGT9hM2y0qJEyCF0kZwVhZQH3APMDo3A0LJkQo2EyCGx0ZQDmWyWyM2yiox5uoJH9D0R=';\n  var str26 = 'hy.ynat-fryrpgbe';\n  var re39 = /\\\\/g;\n  var re40 = / /g;\n  var re41 = /\\/\\xc4\\/t/;\n  var re42 = /\\/\\xd6\\/t/;\n  var re43 = /\\/\\xdc\\/t/;\n  var re44 = /\\/\\xdf\\/t/;\n  var re45 = /\\/\\xe4\\/t/;\n  var re46 = /\\/\\xf6\\/t/;\n  var re47 = /\\/\\xfc\\/t/;\n  var re48 = /\\W/g;\n  var re49 = /uers|fep|fglyr/;\n  var s79 = computeInputVariants(str21, 16);\n  var s80 = computeInputVariants(str22, 16);\n  var s81 = computeInputVariants(str23, 16);\n  var s82 = computeInputVariants(str26, 16);\n\n  function runBlock4() {\n    for (var i = 0; i < 16; i++) {\n      ''.replace(/\\*/g, '');\n      /\\bnpgvir\\b/.exec('npgvir');\n      /sversbk/i.exec(s15[i]);\n      re36.exec('glcr');\n      /zfvr/i.exec(s15[i]);\n      /bcren/i.exec(s15[i]);\n    }\n    for (var i = 0; i < 15; i++) {\n      s79[i].split(re32);\n      s80[i].split(re32);\n      'uggc://ohyyrgvaf.zlfcnpr.pbz/vaqrk.psz'.replace(re12, '');\n      s81[i].replace(re33, '');\n      'yv'.replace(re37, '');\n      'yv'.replace(re18, '');\n      re8.exec('144631658.0.10.1231367822');\n      re8.exec('144631658.1231367822.1.1.hgzpfe=(qverpg)|hgzppa=(qverpg)|hgzpzq=(abar)');\n      re8.exec('144631658.4127520630321984500.1231367822.1231367822.1231367822.1');\n      re8.exec(str24);\n      re8.exec(str25);\n      re8.exec('__hgzn=144631658.4127520630321984500.1231367822.1231367822.1231367822.1');\n      re8.exec('__hgzo=144631658.0.10.1231367822');\n      re8.exec('__hgzm=144631658.1231367822.1.1.hgzpfe=(qverpg)|hgzppa=(qverpg)|hgzpzq=(abar)');\n      re34.exec(s79[i]);\n      re34.exec(s80[i]);\n      /\\.([\\w-]+)|\\[(\\w+)(?:([!*^$~|]?=)[\"']?(.*?)[\"']?)?\\]|:([\\w-]+)(?:\\([\"']?(.*?)?[\"']?\\)|$)/g.exec(s82[i]);\n      re13.exec('uggc://ohyyrgvaf.zlfcnpr.pbz/vaqrk.psz');\n      re38.exec('yv');\n    }\n    for (var i = 0; i < 14; i++) {\n      ''.replace(re18, '');\n      '9.0  e115'.replace(/(\\s+e|\\s+o[0-9]+)/, '');\n      'Funer guvf tnqtrg'.replace(/</g, '');\n      'Funer guvf tnqtrg'.replace(/>/g, '');\n      'Funer guvf tnqtrg'.replace(re39, '');\n      'uggc://cebsvyrrqvg.zlfcnpr.pbz/vaqrk.psz'.replace(re12, '');\n      'grnfre'.replace(re40, '');\n      'grnfre'.replace(re41, '');\n      'grnfre'.replace(re42, '');\n      'grnfre'.replace(re43, '');\n      'grnfre'.replace(re44, '');\n      'grnfre'.replace(re45, '');\n      'grnfre'.replace(re46, '');\n      'grnfre'.replace(re47, '');\n      'grnfre'.replace(re48, '');\n      re16.exec('znetva-gbc');\n      re16.exec('cbfvgvba');\n      re19.exec('gno1');\n      re9.exec('qz');\n      re9.exec('qg');\n      re9.exec('zbqobk');\n      re9.exec('zbqobkva');\n      re9.exec('zbqgvgyr');\n      re13.exec('uggc://cebsvyrrqvg.zlfcnpr.pbz/vaqrk.psz');\n      re26.exec('/vt/znvytnqtrg');\n      re49.exec('glcr');\n    }\n  }\n\n  var re50 = /(?:^|\\s+)fryrpgrq(?:\\s+|$)/;\n  var re51 = /\\&/g;\n  var re52 = /\\+/g;\n  var re53 = /\\?/g;\n  var re54 = /\\t/g;\n  var re55 = /(\\$\\{nqiHey\\})|(\\$nqiHey\\b)/g;\n  var re56 = /(\\$\\{cngu\\})|(\\$cngu\\b)/g;\n\n  function runBlock5() {\n    for (var i = 0; i < 13; i++) {\n      'purpx'.replace(re14, '');\n      'purpx'.replace(re15, '');\n      'pvgl'.replace(re14, '');\n      'pvgl'.replace(re15, '');\n      'qrpe fyvqrgrkg'.replace(re14, '');\n      'qrpe fyvqrgrkg'.replace(re15, '');\n      'svefg fryrpgrq'.replace(re14, '');\n      'svefg fryrpgrq'.replace(re15, '');\n      'uqy_rag'.replace(re14, '');\n      'uqy_rag'.replace(re15, '');\n      'vape fyvqrgrkg'.replace(re14, '');\n      'vape fyvqrgrkg'.replace(re15, '');\n      'vachggrkg QBZPbageby_cynprubyqre'.replace(re5, '');\n      'cnerag puebzr6 fvatyr1 gno fryrpgrq'.replace(re14, '');\n      'cnerag puebzr6 fvatyr1 gno fryrpgrq'.replace(re15, '');\n      'cb_guz'.replace(re14, '');\n      'cb_guz'.replace(re15, '');\n      'fhozvg'.replace(re14, '');\n      'fhozvg'.replace(re15, '');\n      re50.exec('');\n      /NccyrJroXvg\\/([^\\s]*)/.exec(s15[i]);\n      /XUGZY/.exec(s15[i]);\n    }\n    for (var i = 0; i < 12; i++) {\n      '${cebg}://${ubfg}${cngu}/${dz}'.replace(/(\\$\\{cebg\\})|(\\$cebg\\b)/g, '');\n      '1'.replace(re40, '');\n      '1'.replace(re10, '');\n      '1'.replace(re51, '');\n      '1'.replace(re52, '');\n      '1'.replace(re53, '');\n      '1'.replace(re39, '');\n      '1'.replace(re54, '');\n      '9.0  e115'.replace(/^(.*)\\..*$/, '');\n      '9.0  e115'.replace(/^.*e(.*)$/, '');\n      '<!-- ${nqiHey} -->'.replace(re55, '');\n      '<fpevcg glcr=\"grkg/wninfpevcg\" fep=\"${nqiHey}\"></fpevcg>'.replace(re55, '');\n      s21[i].replace(/^.*\\s+(\\S+\\s+\\S+$)/, '');\n      'tzk%2Subzrcntr%2Sfgneg%2Sqr%2S'.replace(re30, '');\n      'tzk'.replace(re30, '');\n      'uggc://${ubfg}${cngu}/${dz}'.replace(/(\\$\\{ubfg\\})|(\\$ubfg\\b)/g, '');\n      'uggc://nqpyvrag.hvzfrei.arg${cngu}/${dz}'.replace(re56, '');\n      'uggc://nqpyvrag.hvzfrei.arg/wf.at/${dz}'.replace(/(\\$\\{dz\\})|(\\$dz\\b)/g, '');\n      'frpgvba'.replace(re29, '');\n      'frpgvba'.replace(re30, '');\n      'fvgr'.replace(re29, '');\n      'fvgr'.replace(re30, '');\n      'fcrpvny'.replace(re29, '');\n      'fcrpvny'.replace(re30, '');\n      re36.exec('anzr');\n      /e/.exec('9.0  e115');\n    }\n  }\n\n  var re57 = /##yv4##/gi;\n  var re58 = /##yv16##/gi;\n  var re59 = /##yv19##/gi;\n  var str27 = '<hy pynff=\"nqi\">##yv4##Cbjreshy Zvpebfbsg grpuabybtl urycf svtug fcnz naq vzcebir frphevgl.##yv19##Trg zber qbar gunaxf gb terngre rnfr naq fcrrq.##yv16##Ybgf bs fgbentr &#40;5 TO&#41; - zber pbby fghss ba gur jnl.##OE## ##OE## ##N##Yrnea zber##/N##</hy>';\n  var str28 = '<hy pynff=\"nqi\"><yv vq=\"YvOYG4\" fglyr=\"onpxtebhaq-vzntr:hey(uggc://vzt.jykef.pbz/~Yvir.FvgrPbagrag.VQ/~14.2.1230/~/~/~/oyg4.cat)\">Cbjreshy Zvpebfbsg grpuabybtl urycf svtug fcnz naq vzcebir frphevgl.##yv19##Trg zber qbar gunaxf gb terngre rnfr naq fcrrq.##yv16##Ybgf bs fgbentr &#40;5 TO&#41; - zber pbby fghss ba gur jnl.##OE## ##OE## ##N##Yrnea zber##/N##</hy>';\n  var str29 = '<hy pynff=\"nqi\"><yv vq=\"YvOYG4\" fglyr=\"onpxtebhaq-vzntr:hey(uggc://vzt.jykef.pbz/~Yvir.FvgrPbagrag.VQ/~14.2.1230/~/~/~/oyg4.cat)\">Cbjreshy Zvpebfbsg grpuabybtl urycf svtug fcnz naq vzcebir frphevgl.##yv19##Trg zber qbar gunaxf gb terngre rnfr naq fcrrq.<yv vq=\"YvOYG16\" fglyr=\"onpxtebhaq-vzntr:hey(uggc://vzt.jykef.pbz/~Yvir.FvgrPbagrag.VQ/~14.2.1230/~/~/~/oyg16.cat)\">Ybgf bs fgbentr &#40;5 TO&#41; - zber pbby fghss ba gur jnl.##OE## ##OE## ##N##Yrnea zber##/N##</hy>';\n  var str30 = '<hy pynff=\"nqi\"><yv vq=\"YvOYG4\" fglyr=\"onpxtebhaq-vzntr:hey(uggc://vzt.jykef.pbz/~Yvir.FvgrPbagrag.VQ/~14.2.1230/~/~/~/oyg4.cat)\">Cbjreshy Zvpebfbsg grpuabybtl urycf svtug fcnz naq vzcebir frphevgl.<yv vq=\"YvOYG19\" fglyr=\"onpxtebhaq-vzntr:hey(uggc://vzt.jykef.pbz/~Yvir.FvgrPbagrag.VQ/~14.2.1230/~/~/~/oyg19.cat)\">Trg zber qbar gunaxf gb terngre rnfr naq fcrrq.<yv vq=\"YvOYG16\" fglyr=\"onpxtebhaq-vzntr:hey(uggc://vzt.jykef.pbz/~Yvir.FvgrPbagrag.VQ/~14.2.1230/~/~/~/oyg16.cat)\">Ybgf bs fgbentr &#40;5 TO&#41; - zber pbby fghss ba gur jnl.##OE## ##OE## ##N##Yrnea zber##/N##</hy>';\n  var str31 = '<hy pynff=\"nqi\"><yv vq=\"YvOYG4\" fglyr=\"onpxtebhaq-vzntr:hey(uggc://vzt.jykef.pbz/~Yvir.FvgrPbagrag.VQ/~14.2.1230/~/~/~/oyg4.cat)\">Cbjreshy Zvpebfbsg grpuabybtl urycf svtug fcnz naq vzcebir frphevgl.<yv vq=\"YvOYG19\" fglyr=\"onpxtebhaq-vzntr:hey(uggc://vzt.jykef.pbz/~Yvir.FvgrPbagrag.VQ/~14.2.1230/~/~/~/oyg19.cat)\">Trg zber qbar gunaxf gb terngre rnfr naq fcrrq.<yv vq=\"YvOYG16\" fglyr=\"onpxtebhaq-vzntr:hey(uggc://vzt.jykef.pbz/~Yvir.FvgrPbagrag.VQ/~14.2.1230/~/~/~/oyg16.cat)\">Ybgf bs fgbentr &#40;5 TO&#41; - zber pbby fghss ba gur jnl.<oe> <oe> ##N##Yrnea zber##/N##</hy>';\n  var str32 = '<hy pynff=\"nqi\"><yv vq=\"YvOYG4\" fglyr=\"onpxtebhaq-vzntr:hey(uggc://vzt.jykef.pbz/~Yvir.FvgrPbagrag.VQ/~14.2.1230/~/~/~/oyg4.cat)\">Cbjreshy Zvpebfbsg grpuabybtl urycf svtug fcnz naq vzcebir frphevgl.<yv vq=\"YvOYG19\" fglyr=\"onpxtebhaq-vzntr:hey(uggc://vzt.jykef.pbz/~Yvir.FvgrPbagrag.VQ/~14.2.1230/~/~/~/oyg19.cat)\">Trg zber qbar gunaxf gb terngre rnfr naq fcrrq.<yv vq=\"YvOYG16\" fglyr=\"onpxtebhaq-vzntr:hey(uggc://vzt.jykef.pbz/~Yvir.FvgrPbagrag.VQ/~14.2.1230/~/~/~/oyg16.cat)\">Ybgf bs fgbentr &#40;5 TO&#41; - zber pbby fghss ba gur jnl.<oe> <oe> <n uers=\"uggc://znvy.yvir.pbz/znvy/nobhg.nfck\" gnetrg=\"_oynax\">Yrnea zber##/N##</hy>';\n  var str33 = 'Bar Jvaqbjf Yvir VQ trgf lbh vagb <o>Ubgznvy</o>, <o>Zrffratre</o>, <o>Kobk YVIR</o> \\u2014 naq bgure cynprf lbh frr #~#argjbexybtb#~#';\n  var re60 = /(?:^|\\s+)bss(?:\\s+|$)/;\n  var re61 = /^(([^:\\/?#]+):)?(\\/\\/([^\\/?#]*))?([^?#]*)(\\?([^#]*))?(#(.*))?$/;\n  var re62 = /^[^<]*(<(.|\\s)+>)[^>]*$|^#(\\w+)$/;\n  var str34 = '${1}://${2}${3}${4}${5}';\n  var str35 = ' O=6gnyg0g4znrrn&o=3&f=gc; Q=_lyu=K3bQZGSxnT4lZzD3OS9GNmV3ZGLkAQxRpTyxNmRlZmRmAmNkAQLRqTImqNZjOUEgpTjQnJ5xMKtgoN--; SCF=qy';\n  var s83 = computeInputVariants(str27, 11);\n  var s84 = computeInputVariants(str28, 11);\n  var s85 = computeInputVariants(str29, 11);\n  var s86 = computeInputVariants(str30, 11);\n  var s87 = computeInputVariants(str31, 11);\n  var s88 = computeInputVariants(str32, 11);\n  var s89 = computeInputVariants(str33, 11);\n  var s90 = computeInputVariants(str34, 11);\n\n  function runBlock6() {\n    for (var i = 0; i < 11; i++) {\n      s83[i].replace(/##yv0##/gi, '');\n      s83[i].replace(re57, '');\n      s84[i].replace(re58, '');\n      s85[i].replace(re59, '');\n      s86[i].replace(/##\\/o##/gi, '');\n      s86[i].replace(/##\\/v##/gi, '');\n      s86[i].replace(/##\\/h##/gi, '');\n      s86[i].replace(/##o##/gi, '');\n      s86[i].replace(/##oe##/gi, '');\n      s86[i].replace(/##v##/gi, '');\n      s86[i].replace(/##h##/gi, '');\n      s87[i].replace(/##n##/gi, '');\n      s88[i].replace(/##\\/n##/gi, '');\n      s89[i].replace(/#~#argjbexybtb#~#/g, '');\n      / Zbovyr\\//.exec(s15[i]);\n      /##yv1##/gi.exec(s83[i]);\n      /##yv10##/gi.exec(s84[i]);\n      /##yv11##/gi.exec(s84[i]);\n      /##yv12##/gi.exec(s84[i]);\n      /##yv13##/gi.exec(s84[i]);\n      /##yv14##/gi.exec(s84[i]);\n      /##yv15##/gi.exec(s84[i]);\n      re58.exec(s84[i]);\n      /##yv17##/gi.exec(s85[i]);\n      /##yv18##/gi.exec(s85[i]);\n      re59.exec(s85[i]);\n      /##yv2##/gi.exec(s83[i]);\n      /##yv20##/gi.exec(s86[i]);\n      /##yv21##/gi.exec(s86[i]);\n      /##yv22##/gi.exec(s86[i]);\n      /##yv23##/gi.exec(s86[i]);\n      /##yv3##/gi.exec(s83[i]);\n      re57.exec(s83[i]);\n      /##yv5##/gi.exec(s84[i]);\n      /##yv6##/gi.exec(s84[i]);\n      /##yv7##/gi.exec(s84[i]);\n      /##yv8##/gi.exec(s84[i]);\n      /##yv9##/gi.exec(s84[i]);\n      re8.exec('473qq1rs0n2r70q9qo1pq48n021s9468ron90nps048p4p29');\n      re8.exec('SbeprqRkcvengvba=633669325184628362');\n      re8.exec('FrffvbaQQS2=473qq1rs0n2r70q9qo1pq48n021s9468ron90nps048p4p29');\n      /AbxvnA[^\\/]*/.exec(s15[i]);\n    }\n    for (var i = 0; i < 10; i++) {\n      ' bss'.replace(/(?:^|\\s+)bss(?:\\s+|$)/g, '');\n      s90[i].replace(/(\\$\\{0\\})|(\\$0\\b)/g, '');\n      s90[i].replace(/(\\$\\{1\\})|(\\$1\\b)/g, '');\n      s90[i].replace(/(\\$\\{pbzcyrgr\\})|(\\$pbzcyrgr\\b)/g, '');\n      s90[i].replace(/(\\$\\{sentzrag\\})|(\\$sentzrag\\b)/g, '');\n      s90[i].replace(/(\\$\\{ubfgcbeg\\})|(\\$ubfgcbeg\\b)/g, '');\n      s90[i].replace(re56, '');\n      s90[i].replace(/(\\$\\{cebgbpby\\})|(\\$cebgbpby\\b)/g, '');\n      s90[i].replace(/(\\$\\{dhrel\\})|(\\$dhrel\\b)/g, '');\n      'nqfvmr'.replace(re29, '');\n      'nqfvmr'.replace(re30, '');\n      'uggc://${2}${3}${4}${5}'.replace(/(\\$\\{2\\})|(\\$2\\b)/g, '');\n      'uggc://wf.hv-cbegny.qr${3}${4}${5}'.replace(/(\\$\\{3\\})|(\\$3\\b)/g, '');\n      'arjf'.replace(re40, '');\n      'arjf'.replace(re41, '');\n      'arjf'.replace(re42, '');\n      'arjf'.replace(re43, '');\n      'arjf'.replace(re44, '');\n      'arjf'.replace(re45, '');\n      'arjf'.replace(re46, '');\n      'arjf'.replace(re47, '');\n      'arjf'.replace(re48, '');\n      / PC=i=(\\d+)&oe=(.)/.exec(str35);\n      re60.exec(' ');\n      re60.exec(' bss');\n      re60.exec('');\n      re19.exec(' ');\n      re19.exec('svefg ba');\n      re19.exec('ynfg vtaber');\n      re19.exec('ba');\n      re9.exec('scnq so ');\n      re9.exec('zrqvgobk');\n      re9.exec('hsgy');\n      re9.exec('lhv-h');\n      /Fnsnev|Xbadhrebe|XUGZY/gi.exec(s15[i]);\n      re61.exec('uggc://wf.hv-cbegny.qr/tzk/ubzr/wf/20080602/onfr.wf');\n      re62.exec('#Ybtva_rznvy');\n    }\n  }\n\n  var re63 = /\\{0\\}/g;\n  var str36 = 'FrffvbaQQS2=4ss747o77904333q374or84qrr1s9r0nprp8r5q81534o94n; ZFPhygher=VC=74.125.75.20&VCPhygher=ra-HF&CersreerqPhygher=ra-HF&CersreerqPhygherCraqvat=&Pbhagel=IIZ=&SbeprqRkcvengvba=633669321699093060&gvzrMbar=0&HFEYBP=DKWyLHAiMTH9AwHjWxAcqUx9GJ91oaEunJ4tIzyyqlMQo3IhqUW5D29xMG1IHlMQo3IhqUW5GzSgMG1Iozy0MJDtH3EuqTImWxEgLHAiMTH9BQN3WxkuqTy0qJEyCGZ3YwDkBGVzGT9hM2y0qJEyCF0kZwVhZQH3APMDo3A0LJkQo2EyCGx0ZQDmWyWyM2yiox5uoJH9D0R=; AFP_zp_tfwsbrg-aowb_80=4413268q3660';\n  var str37 = 'FrffvbaQQS2=4ss747o77904333q374or84qrr1s9r0nprp8r5q81534o94n; AFP_zp_tfwsbrg-aowb_80=4413268q3660; __hgzm=144631658.1231364074.1.1.hgzpfe=(qverpg)|hgzppa=(qverpg)|hgzpzq=(abar); __hgzn=144631658.2294274870215848400.1231364074.1231364074.1231364074.1; __hgzo=144631658.0.10.1231364074; __hgzp=144631658; ZFPhygher=VC=74.125.75.20&VCPhygher=ra-HF&CersreerqPhygher=ra-HF&Pbhagel=IIZ%3Q&SbeprqRkcvengvba=633669321699093060&gvzrMbar=-8&HFEYBP=DKWyLHAiMTH9AwHjWxAcqUx9GJ91oaEunJ4tIzyyqlMQo3IhqUW5D29xMG1IHlMQo3IhqUW5GzSgMG1Iozy0MJDtH3EuqTImWxEgLHAiMTH9BQN3WxkuqTy0qJEyCGZ3YwDkBGVzGT9hM2y0qJEyCF0kZwVhZQH3APMDo3A0LJkQo2EyCGx0ZQDmWyWyM2yiox5uoJH9D0R%3Q';\n  var str38 = 'uggc://tbbtyrnqf.t.qbhoyrpyvpx.arg/cntrnq/nqf?pyvrag=pn-svz_zlfcnpr_zlfcnpr-ubzrcntr_wf&qg=1231364057761&uy=ra&nqfnsr=uvtu&br=hgs8&ahz_nqf=4&bhgchg=wf&nqgrfg=bss&pbeeryngbe=1231364057761&punaary=svz_zlfcnpr_ubzrcntr_abgybttrqva%2Psvz_zlfcnpr_aba_HTP%2Psvz_zlfcnpr_havgrq-fgngrf&hey=uggc%3N%2S%2Ssevraqf.zlfcnpr.pbz%2Svaqrk.psz&nq_glcr=grkg&rvq=6083027&rn=0&sez=0&tn_ivq=1667363813.1231364061&tn_fvq=1231364061&tn_uvq=1917563877&synfu=9.0.115&h_u=768&h_j=1024&h_nu=738&h_nj=1024&h_pq=24&h_gm=-480&h_uvf=2&h_wnin=gehr&h_acyht=7&h_azvzr=22';\n  var str39 = 'ZFPhygher=VC=74.125.75.20&VCPhygher=ra-HF&CersreerqPhygher=ra-HF&Pbhagel=IIZ%3Q&SbeprqRkcvengvba=633669321699093060&gvzrMbar=-8&HFEYBP=DKWyLHAiMTH9AwHjWxAcqUx9GJ91oaEunJ4tIzyyqlMQo3IhqUW5D29xMG1IHlMQo3IhqUW5GzSgMG1Iozy0MJDtH3EuqTImWxEgLHAiMTH9BQN3WxkuqTy0qJEyCGZ3YwDkBGVzGT9hM2y0qJEyCF0kZwVhZQH3APMDo3A0LJkQo2EyCGx0ZQDmWyWyM2yiox5uoJH9D0R%3Q';\n  var str40 = 'ZFPhygher=VC=74.125.75.20&VCPhygher=ra-HF&CersreerqPhygher=ra-HF&CersreerqPhygherCraqvat=&Pbhagel=IIZ=&SbeprqRkcvengvba=633669321699093060&gvzrMbar=0&HFEYBP=DKWyLHAiMTH9AwHjWxAcqUx9GJ91oaEunJ4tIzyyqlMQo3IhqUW5D29xMG1IHlMQo3IhqUW5GzSgMG1Iozy0MJDtH3EuqTImWxEgLHAiMTH9BQN3WxkuqTy0qJEyCGZ3YwDkBGVzGT9hM2y0qJEyCF0kZwVhZQH3APMDo3A0LJkQo2EyCGx0ZQDmWyWyM2yiox5uoJH9D0R=';\n  var s91 = computeInputVariants(str36, 9);\n  var s92 = computeInputVariants(str37, 9);\n  var s93 = computeInputVariants(str38, 9);\n\n  function runBlock7() {\n    for (var i = 0; i < 9; i++) {\n      '0'.replace(re40, '');\n      '0'.replace(re10, '');\n      '0'.replace(re51, '');\n      '0'.replace(re52, '');\n      '0'.replace(re53, '');\n      '0'.replace(re39, '');\n      '0'.replace(re54, '');\n      'Lrf'.replace(re40, '');\n      'Lrf'.replace(re10, '');\n      'Lrf'.replace(re51, '');\n      'Lrf'.replace(re52, '');\n      'Lrf'.replace(re53, '');\n      'Lrf'.replace(re39, '');\n      'Lrf'.replace(re54, '');\n    }\n    for (var i = 0; i < 8; i++) {\n      'Pybfr {0}'.replace(re63, '');\n      'Bcra {0}'.replace(re63, '');\n      s91[i].split(re32);\n      s92[i].split(re32);\n      'puvyq p1 svefg gnournqref'.replace(re14, '');\n      'puvyq p1 svefg gnournqref'.replace(re15, '');\n      'uqy_fcb'.replace(re14, '');\n      'uqy_fcb'.replace(re15, '');\n      'uvag'.replace(re14, '');\n      'uvag'.replace(re15, '');\n      s93[i].replace(re33, '');\n      'yvfg'.replace(re14, '');\n      'yvfg'.replace(re15, '');\n      'at_bhgre'.replace(re30, '');\n      'cnerag puebzr5 qbhoyr2 NU'.replace(re14, '');\n      'cnerag puebzr5 qbhoyr2 NU'.replace(re15, '');\n      'cnerag puebzr5 dhnq5 ps NU osyvax zbarl'.replace(re14, '');\n      'cnerag puebzr5 dhnq5 ps NU osyvax zbarl'.replace(re15, '');\n      'cnerag puebzr6 fvatyr1'.replace(re14, '');\n      'cnerag puebzr6 fvatyr1'.replace(re15, '');\n      'cb_qrs'.replace(re14, '');\n      'cb_qrs'.replace(re15, '');\n      'gnopbagrag'.replace(re14, '');\n      'gnopbagrag'.replace(re15, '');\n      'iv_svefg_gvzr'.replace(re30, '');\n      /(^|.)(ronl|qri-ehf3.wbg)(|fgberf|zbgbef|yvirnhpgvbaf|jvxv|rkcerff|punggre).(pbz(|.nh|.pa|.ux|.zl|.ft|.oe|.zk)|pb(.hx|.xe|.am)|pn|qr|se|vg|ay|or|ng|pu|vr|va|rf|cy|cu|fr)$/i.exec('cntrf.ronl.pbz');\n      re8.exec('144631658.0.10.1231364074');\n      re8.exec('144631658.1231364074.1.1.hgzpfe=(qverpg)|hgzppa=(qverpg)|hgzpzq=(abar)');\n      re8.exec('144631658.2294274870215848400.1231364074.1231364074.1231364074.1');\n      re8.exec('4413241q3660');\n      re8.exec('SbeprqRkcvengvba=633669357391353591');\n      re8.exec(str39);\n      re8.exec(str40);\n      re8.exec('AFP_zp_kkk-gdzogv_80=4413241q3660');\n      re8.exec('FrffvbaQQS2=p98s8o9q42nr21or1r61pqorn1n002nsss569635984s6qp7');\n      re8.exec('__hgzn=144631658.2294274870215848400.1231364074.1231364074.1231364074.1');\n      re8.exec('__hgzo=144631658.0.10.1231364074');\n      re8.exec('__hgzm=144631658.1231364074.1.1.hgzpfe=(qverpg)|hgzppa=(qverpg)|hgzpzq=(abar)');\n      re8.exec('p98s8o9q42nr21or1r61pqorn1n002nsss569635984s6qp7');\n      re34.exec(s91[i]);\n      re34.exec(s92[i]);\n    }\n  }\n\n  var re64 = /\\b[a-z]/g;\n  var re65 = /^uggc:\\/\\//;\n  var re66 = /(?:^|\\s+)qvfnoyrq(?:\\s+|$)/;\n  var str41 = 'uggc://cebsvyr.zlfcnpr.pbz/Zbqhyrf/Nccyvpngvbaf/Cntrf/Pnainf.nfck';\n\n  function runBlock8() {\n    for (var i = 0; i < 7; i++) {\n      s21[i].match(/\\d+/g);\n      'nsgre'.replace(re64, '');\n      'orsber'.replace(re64, '');\n      'obggbz'.replace(re64, '');\n      'ohvygva_jrngure.kzy'.replace(re65, '');\n      'ohggba'.replace(re37, '');\n      'ohggba'.replace(re18, '');\n      'qngrgvzr.kzy'.replace(re65, '');\n      'uggc://eff.paa.pbz/eff/paa_gbcfgbevrf.eff'.replace(re65, '');\n      'vachg'.replace(re37, '');\n      'vachg'.replace(re18, '');\n      'vafvqr'.replace(re64, '');\n      'cbvagre'.replace(re27, '');\n      'cbfvgvba'.replace(/[A-Z]/g, '');\n      'gbc'.replace(re27, '');\n      'gbc'.replace(re64, '');\n      'hy'.replace(re37, '');\n      'hy'.replace(re18, '');\n      str26.replace(re37, '');\n      str26.replace(re18, '');\n      'lbhghor_vtbbtyr/i2/lbhghor.kzy'.replace(re65, '');\n      'm-vaqrk'.replace(re27, '');\n      /#([\\w-]+)/.exec(str26);\n      re16.exec('urvtug');\n      re16.exec('znetvaGbc');\n      re16.exec('jvqgu');\n      re19.exec('gno0 svefg ba');\n      re19.exec('gno0 ba');\n      re19.exec('gno4 ynfg');\n      re19.exec('gno4');\n      re19.exec('gno5');\n      re19.exec('gno6');\n      re19.exec('gno7');\n      re19.exec('gno8');\n      /NqborNVE\\/([^\\s]*)/.exec(s15[i]);\n      /NccyrJroXvg\\/([^ ]*)/.exec(s15[i]);\n      /XUGZY/gi.exec(s15[i]);\n      /^(?:obql|ugzy)$/i.exec('YV');\n      re38.exec('ohggba');\n      re38.exec('vachg');\n      re38.exec('hy');\n      re38.exec(str26);\n      /^(\\w+|\\*)/.exec(str26);\n      /znp|jva|yvahk/i.exec('Jva32');\n      /eton?\\([\\d\\s,]+\\)/.exec('fgngvp');\n    }\n    for (var i = 0; i < 6; i++) {\n      ''.replace(/\\r/g, '');\n      '/'.replace(re40, '');\n      '/'.replace(re10, '');\n      '/'.replace(re51, '');\n      '/'.replace(re52, '');\n      '/'.replace(re53, '');\n      '/'.replace(re39, '');\n      '/'.replace(re54, '');\n      'uggc://zfacbegny.112.2b7.arg/o/ff/zfacbegnyubzr/1/U.7-cqi-2/{0}?[NDO]&{1}&{2}&[NDR]'.replace(re63, '');\n      str41.replace(re12, '');\n      'uggc://jjj.snprobbx.pbz/fepu.cuc'.replace(re23, '');\n      'freivpr'.replace(re40, '');\n      'freivpr'.replace(re41, '');\n      'freivpr'.replace(re42, '');\n      'freivpr'.replace(re43, '');\n      'freivpr'.replace(re44, '');\n      'freivpr'.replace(re45, '');\n      'freivpr'.replace(re46, '');\n      'freivpr'.replace(re47, '');\n      'freivpr'.replace(re48, '');\n      /((ZFVR\\s+([6-9]|\\d\\d)\\.))/.exec(s15[i]);\n      re66.exec('');\n      re50.exec('fryrpgrq');\n      re8.exec('8sqq78r9n442851q565599o401385sp3s04r92rnn7o19ssn');\n      re8.exec('SbeprqRkcvengvba=633669340386893867');\n      re8.exec('VC=74.125.75.17');\n      re8.exec('FrffvbaQQS2=8sqq78r9n442851q565599o401385sp3s04r92rnn7o19ssn');\n      /Xbadhrebe|Fnsnev|XUGZY/.exec(s15[i]);\n      re13.exec(str41);\n      re49.exec('unfsbphf');\n    }\n  }\n\n  var re67 = /zrah_byq/g;\n  var str42 = 'FrffvbaQQS2=473qq1rs0n2r70q9qo1pq48n021s9468ron90nps048p4p29; ZFPhygher=VC=74.125.75.3&VCPhygher=ra-HF&CersreerqPhygher=ra-HF&CersreerqPhygherCraqvat=&Pbhagel=IIZ=&SbeprqRkcvengvba=633669325184628362&gvzrMbar=0&HFEYBP=DKWyLHAiMTH9AwHjWxAcqUx9GJ91oaEunJ4tIzyyqlMQo3IhqUW5D29xMG1IHlMQo3IhqUW5GzSgMG1Iozy0MJDtH3EuqTImWxEgLHAiMTH9BQN3WxkuqTy0qJEyCGZ3YwDkBGVzGT9hM2y0qJEyCF0kZwVhZQH3APMDo3A0LJkQo2EyCGx0ZQDmWyWyM2yiox5uoJH9D0R=';\n  var str43 = 'FrffvbaQQS2=473qq1rs0n2r70q9qo1pq48n021s9468ron90nps048p4p29; __hgzm=144631658.1231364380.1.1.hgzpfe=(qverpg)|hgzppa=(qverpg)|hgzpzq=(abar); __hgzn=144631658.3931862196947939300.1231364380.1231364380.1231364380.1; __hgzo=144631658.0.10.1231364380; __hgzp=144631658; ZFPhygher=VC=74.125.75.3&VCPhygher=ra-HF&CersreerqPhygher=ra-HF&Pbhagel=IIZ%3Q&SbeprqRkcvengvba=633669325184628362&gvzrMbar=-8&HFEYBP=DKWyLHAiMTH9AwHjWxAcqUx9GJ91oaEunJ4tIzyyqlMQo3IhqUW5D29xMG1IHlMQo3IhqUW5GzSgMG1Iozy0MJDtH3EuqTImWxEgLHAiMTH9BQN3WxkuqTy0qJEyCGZ3YwDkBGVzGT9hM2y0qJEyCF0kZwVhZQH3APMDo3A0LJkQo2EyCGx0ZQDmWyWyM2yiox5uoJH9D0R%3Q';\n  var str44 = 'uggc://tbbtyrnqf.t.qbhoyrpyvpx.arg/cntrnq/nqf?pyvrag=pn-svz_zlfcnpr_vzntrf_wf&qg=1231364373088&uy=ra&nqfnsr=uvtu&br=hgs8&ahz_nqf=4&bhgchg=wf&nqgrfg=bss&pbeeryngbe=1231364373088&punaary=svz_zlfcnpr_hfre-ivrj-pbzzragf%2Psvz_zlfcnpr_havgrq-fgngrf&hey=uggc%3N%2S%2Spbzzrag.zlfcnpr.pbz%2Svaqrk.psz&nq_glcr=grkg&rvq=6083027&rn=0&sez=0&tn_ivq=1158737789.1231364375&tn_fvq=1231364375&tn_uvq=415520832&synfu=9.0.115&h_u=768&h_j=1024&h_nu=738&h_nj=1024&h_pq=24&h_gm=-480&h_uvf=2&h_wnin=gehr&h_acyht=7&h_azvzr=22';\n  var str45 = 'ZFPhygher=VC=74.125.75.3&VCPhygher=ra-HF&CersreerqPhygher=ra-HF&Pbhagel=IIZ%3Q&SbeprqRkcvengvba=633669325184628362&gvzrMbar=-8&HFEYBP=DKWyLHAiMTH9AwHjWxAcqUx9GJ91oaEunJ4tIzyyqlMQo3IhqUW5D29xMG1IHlMQo3IhqUW5GzSgMG1Iozy0MJDtH3EuqTImWxEgLHAiMTH9BQN3WxkuqTy0qJEyCGZ3YwDkBGVzGT9hM2y0qJEyCF0kZwVhZQH3APMDo3A0LJkQo2EyCGx0ZQDmWyWyM2yiox5uoJH9D0R%3Q';\n  var str46 = 'ZFPhygher=VC=74.125.75.3&VCPhygher=ra-HF&CersreerqPhygher=ra-HF&CersreerqPhygherCraqvat=&Pbhagel=IIZ=&SbeprqRkcvengvba=633669325184628362&gvzrMbar=0&HFEYBP=DKWyLHAiMTH9AwHjWxAcqUx9GJ91oaEunJ4tIzyyqlMQo3IhqUW5D29xMG1IHlMQo3IhqUW5GzSgMG1Iozy0MJDtH3EuqTImWxEgLHAiMTH9BQN3WxkuqTy0qJEyCGZ3YwDkBGVzGT9hM2y0qJEyCF0kZwVhZQH3APMDo3A0LJkQo2EyCGx0ZQDmWyWyM2yiox5uoJH9D0R=';\n  var re68 = /^([#.]?)((?:[\\w\\u0128-\\uffff*_-]|\\\\.)*)/;\n  var re69 = /\\{1\\}/g;\n  var re70 = /\\s+/;\n  var re71 = /(\\$\\{4\\})|(\\$4\\b)/g;\n  var re72 = /(\\$\\{5\\})|(\\$5\\b)/g;\n  var re73 = /\\{2\\}/g;\n  var re74 = /[^+>] [^+>]/;\n  var re75 = /\\bucpyv\\s*=\\s*([^;]*)/i;\n  var re76 = /\\bucuvqr\\s*=\\s*([^;]*)/i;\n  var re77 = /\\bucfie\\s*=\\s*([^;]*)/i;\n  var re78 = /\\bhfucjrn\\s*=\\s*([^;]*)/i;\n  var re79 = /\\bmvc\\s*=\\s*([^;]*)/i;\n  var re80 = /^((?:[\\w\\u0128-\\uffff*_-]|\\\\.)+)(#)((?:[\\w\\u0128-\\uffff*_-]|\\\\.)+)/;\n  var re81 = /^([>+~])\\s*(\\w*)/i;\n  var re82 = /^>\\s*((?:[\\w\\u0128-\\uffff*_-]|\\\\.)+)/;\n  var re83 = /^[\\s[]?shapgvba/;\n  var re84 = /v\\/g.tvs#(.*)/i;\n  var str47 = '#Zbq-Vasb-Vasb-WninFpevcgUvag';\n  var str48 = ',n.svryqOgaPnapry';\n  var str49 = 'FrffvbaQQS2=p98s8o9q42nr21or1r61pqorn1n002nsss569635984s6qp7; ZFPhygher=VC=74.125.75.3&VCPhygher=ra-HF&CersreerqPhygher=ra-HF&CersreerqPhygherCraqvat=&Pbhagel=IIZ=&SbeprqRkcvengvba=633669357391353591&gvzrMbar=0&HFEYBP=DKWyLHAiMTH9AwHjWxAcqUx9GJ91oaEunJ4tIzyyqlMQo3IhqUW5D29xMG1IHlMQo3IhqUW5GzSgMG1Iozy0MJDtH3EuqTImWxEgLHAiMTH9BQN3WxkuqTy0qJEyCGZ3YwDkBGVzGT9hM2y0qJEyCF0kZwVhZQH3APMDo3A0LJkQo2EyCGx0ZQDmWyWyM2yiox5uoJH9D0R=; AFP_zp_kkk-gdzogv_80=4413241q3660';\n  var str50 = 'FrffvbaQQS2=p98s8o9q42nr21or1r61pqorn1n002nsss569635984s6qp7; AFP_zp_kkk-gdzogv_80=4413241q3660; AFP_zp_kkk-aowb_80=4413235p3660; __hgzm=144631658.1231367708.1.1.hgzpfe=(qverpg)|hgzppa=(qverpg)|hgzpzq=(abar); __hgzn=144631658.2770915348920628700.1231367708.1231367708.1231367708.1; __hgzo=144631658.0.10.1231367708; __hgzp=144631658; ZFPhygher=VC=74.125.75.3&VCPhygher=ra-HF&CersreerqPhygher=ra-HF&Pbhagel=IIZ%3Q&SbeprqRkcvengvba=633669357391353591&gvzrMbar=-8&HFEYBP=DKWyLHAiMTH9AwHjWxAcqUx9GJ91oaEunJ4tIzyyqlMQo3IhqUW5D29xMG1IHlMQo3IhqUW5GzSgMG1Iozy0MJDtH3EuqTImWxEgLHAiMTH9BQN3WxkuqTy0qJEyCGZ3YwDkBGVzGT9hM2y0qJEyCF0kZwVhZQH3APMDo3A0LJkQo2EyCGx0ZQDmWyWyM2yiox5uoJH9D0R%3Q';\n  var str51 = 'uggc://tbbtyrnqf.t.qbhoyrpyvpx.arg/cntrnq/nqf?pyvrag=pn-svz_zlfcnpr_zlfcnpr-ubzrcntr_wf&qg=1231367691141&uy=ra&nqfnsr=uvtu&br=hgs8&ahz_nqf=4&bhgchg=wf&nqgrfg=bss&pbeeryngbe=1231367691141&punaary=svz_zlfcnpr_ubzrcntr_abgybttrqva%2Psvz_zlfcnpr_aba_HTP%2Psvz_zlfcnpr_havgrq-fgngrf&hey=uggc%3N%2S%2Sjjj.zlfcnpr.pbz%2S&nq_glcr=grkg&rvq=6083027&rn=0&sez=0&tn_ivq=320757904.1231367694&tn_fvq=1231367694&tn_uvq=1758792003&synfu=9.0.115&h_u=768&h_j=1024&h_nu=738&h_nj=1024&h_pq=24&h_gm=-480&h_uvf=2&h_wnin=gehr&h_acyht=7&h_azvzr=22';\n  var str52 = 'uggc://zfacbegny.112.2b7.arg/o/ff/zfacbegnyubzr/1/U.7-cqi-2/f55332979829981?[NDO]&aqu=1&g=7%2S0%2S2009%2014%3N38%3N42%203%20480&af=zfacbegny&cntrAnzr=HF%20UCZFSGJ&t=uggc%3N%2S%2Sjjj.zfa.pbz%2S&f=1024k768&p=24&x=L&oj=994&ou=634&uc=A&{2}&[NDR]';\n  var str53 = 'cnerag puebzr6 fvatyr1 gno fryrpgrq ovaq qbhoyr2 ps';\n  var str54 = 'ZFPhygher=VC=74.125.75.3&VCPhygher=ra-HF&CersreerqPhygher=ra-HF&Pbhagel=IIZ%3Q&SbeprqRkcvengvba=633669357391353591&gvzrMbar=-8&HFEYBP=DKWyLHAiMTH9AwHjWxAcqUx9GJ91oaEunJ4tIzyyqlMQo3IhqUW5D29xMG1IHlMQo3IhqUW5GzSgMG1Iozy0MJDtH3EuqTImWxEgLHAiMTH9BQN3WxkuqTy0qJEyCGZ3YwDkBGVzGT9hM2y0qJEyCF0kZwVhZQH3APMDo3A0LJkQo2EyCGx0ZQDmWyWyM2yiox5uoJH9D0R%3Q';\n  var str55 = 'ZFPhygher=VC=74.125.75.3&VCPhygher=ra-HF&CersreerqPhygher=ra-HF&CersreerqPhygherCraqvat=&Pbhagel=IIZ=&SbeprqRkcvengvba=633669357391353591&gvzrMbar=0&HFEYBP=DKWyLHAiMTH9AwHjWxAcqUx9GJ91oaEunJ4tIzyyqlMQo3IhqUW5D29xMG1IHlMQo3IhqUW5GzSgMG1Iozy0MJDtH3EuqTImWxEgLHAiMTH9BQN3WxkuqTy0qJEyCGZ3YwDkBGVzGT9hM2y0qJEyCF0kZwVhZQH3APMDo3A0LJkQo2EyCGx0ZQDmWyWyM2yiox5uoJH9D0R=';\n  var str56 = 'ne;ng;nh;or;oe;pn;pu;py;pa;qr;qx;rf;sv;se;to;ux;vq;vr;va;vg;wc;xe;zk;zl;ay;ab;am;cu;cy;cg;eh;fr;ft;gu;ge;gj;mn;';\n  var str57 = 'ZP1=I=3&THVQ=6nnpr9q661804s33nnop45nosqp17q85; zu=ZFSG; PHYGHER=RA-HF; SyvtugTebhcVq=97; SyvtugVq=OnfrCntr; ucfie=Z:5|S:5|G:5|R:5|Q:oyh|J:S; ucpyv=J.U|Y.|F.|E.|H.Y|P.|U.; hfucjrn=jp:HFPN0746; ZHVQ=Q783SN9O14054831N4869R51P0SO8886&GHVQ=1';\n  var str58 = 'ZP1=I=3&THVQ=6nnpr9q661804s33nnop45nosqp17q85; zu=ZFSG; PHYGHER=RA-HF; SyvtugTebhcVq=97; SyvtugVq=OnfrCntr; ucfie=Z:5|S:5|G:5|R:5|Q:oyh|J:S; ucpyv=J.U|Y.|F.|E.|H.Y|P.|U.; hfucjrn=jp:HFPN0746; ZHVQ=Q783SN9O14054831N4869R51P0SO8886';\n  var str59 = 'ZP1=I=3&THVQ=6nnpr9q661804s33nnop45nosqp17q85; zu=ZFSG; PHYGHER=RA-HF; SyvtugTebhcVq=97; SyvtugVq=OnfrCntr; ucfie=Z:5|S:5|G:5|R:5|Q:oyh|J:S; ucpyv=J.U|Y.|F.|E.|H.Y|P.|U.; hfucjrn=jp:HFPN0746; ZHVQ=Q783SN9O14054831N4869R51P0SO8886; mvc=m:94043|yn:37.4154|yb:-122.0585|p:HF|ue:1';\n  var str60 = 'ZP1=I=3&THVQ=6nnpr9q661804s33nnop45nosqp17q85; zu=ZFSG; PHYGHER=RA-HF; SyvtugTebhcVq=97; SyvtugVq=OnfrCntr; ucfie=Z:5|S:5|G:5|R:5|Q:oyh|J:S; ucpyv=J.U|Y.|F.|E.|H.Y|P.|U.; hfucjrn=jp:HFPN0746; ZHVQ=Q783SN9O14054831N4869R51P0SO8886; mvc=m:94043|yn:37.4154|yb:-122.0585|p:HF';\n  var str61 = 'uggc://gx2.fgp.f-zfa.pbz/oe/uc/11/ra-hf/pff/v/g.tvs#uggc://gx2.fgo.f-zfa.pbz/v/29/4RQP4969777N048NPS4RRR3PO2S7S.wct';\n  var str62 = 'uggc://gx2.fgp.f-zfa.pbz/oe/uc/11/ra-hf/pff/v/g.tvs#uggc://gx2.fgo.f-zfa.pbz/v/OQ/63NP9O94NS5OQP1249Q9S1ROP7NS3.wct';\n  var str63 = 'zbmvyyn/5.0 (jvaqbjf; h; jvaqbjf ag 5.1; ra-hf) nccyrjroxvg/528.9 (xugzy, yvxr trpxb) puebzr/2.0.157.0 fnsnev/528.9';\n  var s94 = computeInputVariants(str42, 5);\n  var s95 = computeInputVariants(str43, 5);\n  var s96 = computeInputVariants(str44, 5);\n  var s97 = computeInputVariants(str47, 5);\n  var s98 = computeInputVariants(str48, 5);\n  var s99 = computeInputVariants(str49, 5);\n  var s100 = computeInputVariants(str50, 5);\n  var s101 = computeInputVariants(str51, 5);\n  var s102 = computeInputVariants(str52, 5);\n  var s103 = computeInputVariants(str53, 5);\n\n  function runBlock9() {\n    for (var i = 0; i < 5; i++) {\n      s94[i].split(re32);\n      s95[i].split(re32);\n      'svz_zlfcnpr_hfre-ivrj-pbzzragf,svz_zlfcnpr_havgrq-fgngrf'.split(re20);\n      s96[i].replace(re33, '');\n      'zrah_arj zrah_arj_gbttyr zrah_gbttyr'.replace(re67, '');\n      'zrah_byq zrah_byq_gbttyr zrah_gbttyr'.replace(re67, '');\n      re8.exec('102n9o0o9pq60132qn0337rr867p75953502q2s27s2s5r98');\n      re8.exec('144631658.0.10.1231364380');\n      re8.exec('144631658.1231364380.1.1.hgzpfe=(qverpg)|hgzppa=(qverpg)|hgzpzq=(abar)');\n      re8.exec('144631658.3931862196947939300.1231364380.1231364380.1231364380.1');\n      re8.exec('441326q33660');\n      re8.exec('SbeprqRkcvengvba=633669341278771470');\n      re8.exec(str45);\n      re8.exec(str46);\n      re8.exec('AFP_zp_dfctwzssrwh-aowb_80=441326q33660');\n      re8.exec('FrffvbaQQS2=102n9o0o9pq60132qn0337rr867p75953502q2s27s2s5r98');\n      re8.exec('__hgzn=144631658.3931862196947939300.1231364380.1231364380.1231364380.1');\n      re8.exec('__hgzo=144631658.0.10.1231364380');\n      re8.exec('__hgzm=144631658.1231364380.1.1.hgzpfe=(qverpg)|hgzppa=(qverpg)|hgzpzq=(abar)');\n    }\n    for (var i = 0; i < 4; i++) {\n      ' yvfg1'.replace(re14, '');\n      ' yvfg1'.replace(re15, '');\n      ' yvfg2'.replace(re14, '');\n      ' yvfg2'.replace(re15, '');\n      ' frneputebhc1'.replace(re14, '');\n      ' frneputebhc1'.replace(re15, '');\n      s97[i].replace(re68, '');\n      s97[i].replace(re18, '');\n      ''.replace(/&/g, '');\n      ''.replace(re35, '');\n      '(..-{0})(\\|(\\d+)|)'.replace(re63, '');\n      s98[i].replace(re18, '');\n      '//vzt.jro.qr/vij/FC/${cngu}/${anzr}/${inyhr}?gf=${abj}'.replace(re56, '');\n      '//vzt.jro.qr/vij/FC/tzk_uc/${anzr}/${inyhr}?gf=${abj}'.replace(/(\\$\\{anzr\\})|(\\$anzr\\b)/g, '');\n      '<fcna pynff=\"urnq\"><o>Jvaqbjf Yvir Ubgznvy</o></fcna><fcna pynff=\"zft\">{1}</fcna>'.replace(re69, '');\n      '<fcna pynff=\"urnq\"><o>{0}</o></fcna><fcna pynff=\"zft\">{1}</fcna>'.replace(re63, '');\n      '<fcna pynff=\"fvtahc\"><n uers=uggc://jjj.ubgznvy.pbz><o>{1}</o></n></fcna>'.replace(re69, '');\n      '<fcna pynff=\"fvtahc\"><n uers={0}><o>{1}</o></n></fcna>'.replace(re63, '');\n      'Vzntrf'.replace(re15, '');\n      'ZFA'.replace(re15, '');\n      'Zncf'.replace(re15, '');\n      'Zbq-Vasb-Vasb-WninFpevcgUvag'.replace(re39, '');\n      'Arjf'.replace(re15, '');\n      s99[i].split(re32);\n      s100[i].split(re32);\n      'Ivqrb'.replace(re15, '');\n      'Jro'.replace(re15, '');\n      'n'.replace(re39, '');\n      'nwnkFgneg'.split(re70);\n      'nwnkFgbc'.split(re70);\n      'ovaq'.replace(re14, '');\n      'ovaq'.replace(re15, '');\n      'oevatf lbh zber. Zber fcnpr (5TO), zber frphevgl, fgvyy serr.'.replace(re63, '');\n      'puvyq p1 svefg qrpx'.replace(re14, '');\n      'puvyq p1 svefg qrpx'.replace(re15, '');\n      'puvyq p1 svefg qbhoyr2'.replace(re14, '');\n      'puvyq p1 svefg qbhoyr2'.replace(re15, '');\n      'puvyq p2 ynfg'.replace(re14, '');\n      'puvyq p2 ynfg'.replace(re15, '');\n      'puvyq p2'.replace(re14, '');\n      'puvyq p2'.replace(re15, '');\n      'puvyq p3'.replace(re14, '');\n      'puvyq p3'.replace(re15, '');\n      'puvyq p4 ynfg'.replace(re14, '');\n      'puvyq p4 ynfg'.replace(re15, '');\n      'pbclevtug'.replace(re14, '');\n      'pbclevtug'.replace(re15, '');\n      'qZFAZR_1'.replace(re14, '');\n      'qZFAZR_1'.replace(re15, '');\n      'qbhoyr2 ps'.replace(re14, '');\n      'qbhoyr2 ps'.replace(re15, '');\n      'qbhoyr2'.replace(re14, '');\n      'qbhoyr2'.replace(re15, '');\n      'uqy_arj'.replace(re14, '');\n      'uqy_arj'.replace(re15, '');\n      'uc_fubccvatobk'.replace(re30, '');\n      'ugzy%2Rvq'.replace(re29, '');\n      'ugzy%2Rvq'.replace(re30, '');\n      s101[i].replace(re33, '');\n      'uggc://wf.hv-cbegny.qr/tzk/ubzr/wf/20080602/cebgbglcr.wf${4}${5}'.replace(re71, '');\n      'uggc://wf.hv-cbegny.qr/tzk/ubzr/wf/20080602/cebgbglcr.wf${5}'.replace(re72, '');\n      s102[i].replace(re73, '');\n      'uggc://zfacbegny.112.2b7.arg/o/ff/zfacbegnyubzr/1/U.7-cqi-2/f55332979829981?[NDO]&{1}&{2}&[NDR]'.replace(re69, '');\n      'vztZFSG'.replace(re14, '');\n      'vztZFSG'.replace(re15, '');\n      'zfasbbg1 ps'.replace(re14, '');\n      'zfasbbg1 ps'.replace(re15, '');\n      s103[i].replace(re14, '');\n      s103[i].replace(re15, '');\n      'cnerag puebzr6 fvatyr1 gno fryrpgrq ovaq'.replace(re14, '');\n      'cnerag puebzr6 fvatyr1 gno fryrpgrq ovaq'.replace(re15, '');\n      'cevznel'.replace(re14, '');\n      'cevznel'.replace(re15, '');\n      'erpgnatyr'.replace(re30, '');\n      'frpbaqnel'.replace(re14, '');\n      'frpbaqnel'.replace(re15, '');\n      'haybnq'.split(re70);\n      '{0}{1}1'.replace(re63, '');\n      '|{1}1'.replace(re69, '');\n      /(..-HF)(\\|(\\d+)|)/i.exec('xb-xe,ra-va,gu-gu');\n      re4.exec('/ZlFcnprNccf/NccPnainf,45000012');\n      re8.exec('144631658.0.10.1231367708');\n      re8.exec('144631658.1231367708.1.1.hgzpfe=(qverpg)|hgzppa=(qverpg)|hgzpzq=(abar)');\n      re8.exec('144631658.2770915348920628700.1231367708.1231367708.1231367708.1');\n      re8.exec('4413235p3660');\n      re8.exec('441327q73660');\n      re8.exec('9995p6rp12rrnr893334ro7nq70o7p64p69rqn844prs1473');\n      re8.exec('SbeprqRkcvengvba=633669350559478880');\n      re8.exec(str54);\n      re8.exec(str55);\n      re8.exec('AFP_zp_dfctwzs-aowb_80=441327q73660');\n      re8.exec('AFP_zp_kkk-aowb_80=4413235p3660');\n      re8.exec('FrffvbaQQS2=9995p6rp12rrnr893334ro7nq70o7p64p69rqn844prs1473');\n      re8.exec('__hgzn=144631658.2770915348920628700.1231367708.1231367708.1231367708.1');\n      re8.exec('__hgzo=144631658.0.10.1231367708');\n      re8.exec('__hgzm=144631658.1231367708.1.1.hgzpfe=(qverpg)|hgzppa=(qverpg)|hgzpzq=(abar)');\n      re34.exec(s99[i]);\n      re34.exec(s100[i]);\n      /ZFVR\\s+5[.]01/.exec(s15[i]);\n      /HF(?=;)/i.exec(str56);\n      re74.exec(s97[i]);\n      re28.exec('svefg npgvir svefgNpgvir');\n      re28.exec('ynfg');\n      /\\bp:(..)/i.exec('m:94043|yn:37.4154|yb:-122.0585|p:HF');\n      re75.exec(str57);\n      re75.exec(str58);\n      re76.exec(str57);\n      re76.exec(str58);\n      re77.exec(str57);\n      re77.exec(str58);\n      /\\bhfucce\\s*=\\s*([^;]*)/i.exec(str59);\n      re78.exec(str57);\n      re78.exec(str58);\n      /\\bjci\\s*=\\s*([^;]*)/i.exec(str59);\n      re79.exec(str58);\n      re79.exec(str60);\n      re79.exec(str59);\n      /\\|p:([a-z]{2})/i.exec('m:94043|yn:37.4154|yb:-122.0585|p:HF|ue:1');\n      re80.exec(s97[i]);\n      re61.exec('cebgbglcr.wf');\n      re68.exec(s97[i]);\n      re81.exec(s97[i]);\n      re82.exec(s97[i]);\n      /^Fubpxjnir Synfu (\\d)/.exec(s21[i]);\n      /^Fubpxjnir Synfu (\\d+)/.exec(s21[i]);\n      re83.exec('[bowrpg tybony]');\n      re62.exec(s97[i]);\n      re84.exec(str61);\n      re84.exec(str62);\n      /jroxvg/.exec(str63);\n    }\n  }\n\n  var re85 = /eaq_zbqobkva/;\n  var str64 = '1231365729213';\n  var str65 = '74.125.75.3-1057165600.29978900';\n  var str66 = '74.125.75.3-1057165600.29978900.1231365730214';\n  var str67 = 'Frnepu%20Zvpebfbsg.pbz';\n  var str68 = 'FrffvbaQQS2=8sqq78r9n442851q565599o401385sp3s04r92rnn7o19ssn; ZFPhygher=VC=74.125.75.17&VCPhygher=ra-HF&CersreerqPhygher=ra-HF&CersreerqPhygherCraqvat=&Pbhagel=IIZ=&SbeprqRkcvengvba=633669340386893867&gvzrMbar=0&HFEYBP=DKWyLHAiMTH9AwHjWxAcqUx9GJ91oaEunJ4tIzyyqlMQo3IhqUW5D29xMG1IHlMQo3IhqUW5GzSgMG1Iozy0MJDtH3EuqTImWxEgLHAiMTH9BQN3WxkuqTy0qJEyCGZ3YwDkBGVzGT9hM2y0qJEyCF0kZwVhZQH3APMDo3A0LJkQo2EyCGx0ZQDmWyWyM2yiox5uoJH9D0R=';\n  var str69 = 'FrffvbaQQS2=8sqq78r9n442851q565599o401385sp3s04r92rnn7o19ssn; __hgzm=144631658.1231365779.1.1.hgzpfe=(qverpg)|hgzppa=(qverpg)|hgzpzq=(abar); __hgzn=144631658.1877536177953918500.1231365779.1231365779.1231365779.1; __hgzo=144631658.0.10.1231365779; __hgzp=144631658; ZFPhygher=VC=74.125.75.17&VCPhygher=ra-HF&CersreerqPhygher=ra-HF&Pbhagel=IIZ%3Q&SbeprqRkcvengvba=633669340386893867&gvzrMbar=-8&HFEYBP=DKWyLHAiMTH9AwHjWxAcqUx9GJ91oaEunJ4tIzyyqlMQo3IhqUW5D29xMG1IHlMQo3IhqUW5GzSgMG1Iozy0MJDtH3EuqTImWxEgLHAiMTH9BQN3WxkuqTy0qJEyCGZ3YwDkBGVzGT9hM2y0qJEyCF0kZwVhZQH3APMDo3A0LJkQo2EyCGx0ZQDmWyWyM2yiox5uoJH9D0R%3Q';\n  var str70 = 'I=3%26THVQ=757q3ss871q44o7o805n8113n5p72q52';\n  var str71 = 'I=3&THVQ=757q3ss871q44o7o805n8113n5p72q52';\n  var str72 = 'uggc://tbbtyrnqf.t.qbhoyrpyvpx.arg/cntrnq/nqf?pyvrag=pn-svz_zlfcnpr_zlfcnpr-ubzrcntr_wf&qg=1231365765292&uy=ra&nqfnsr=uvtu&br=hgs8&ahz_nqf=4&bhgchg=wf&nqgrfg=bss&pbeeryngbe=1231365765292&punaary=svz_zlfcnpr_ubzrcntr_abgybttrqva%2Psvz_zlfcnpr_aba_HTP%2Psvz_zlfcnpr_havgrq-fgngrf&hey=uggc%3N%2S%2Sohyyrgvaf.zlfcnpr.pbz%2Svaqrk.psz&nq_glcr=grkg&rvq=6083027&rn=0&sez=0&tn_ivq=1579793869.1231365768&tn_fvq=1231365768&tn_uvq=2056210897&synfu=9.0.115&h_u=768&h_j=1024&h_nu=738&h_nj=1024&h_pq=24&h_gm=-480&h_uvf=2&h_wnin=gehr&h_acyht=7&h_azvzr=22';\n  var str73 = 'frnepu.zvpebfbsg.pbz';\n  var str74 = 'frnepu.zvpebfbsg.pbz/';\n  var str75 = 'ZFPhygher=VC=74.125.75.17&VCPhygher=ra-HF&CersreerqPhygher=ra-HF&Pbhagel=IIZ%3Q&SbeprqRkcvengvba=633669340386893867&gvzrMbar=-8&HFEYBP=DKWyLHAiMTH9AwHjWxAcqUx9GJ91oaEunJ4tIzyyqlMQo3IhqUW5D29xMG1IHlMQo3IhqUW5GzSgMG1Iozy0MJDtH3EuqTImWxEgLHAiMTH9BQN3WxkuqTy0qJEyCGZ3YwDkBGVzGT9hM2y0qJEyCF0kZwVhZQH3APMDo3A0LJkQo2EyCGx0ZQDmWyWyM2yiox5uoJH9D0R%3Q';\n  var str76 = 'ZFPhygher=VC=74.125.75.17&VCPhygher=ra-HF&CersreerqPhygher=ra-HF&CersreerqPhygherCraqvat=&Pbhagel=IIZ=&SbeprqRkcvengvba=633669340386893867&gvzrMbar=0&HFEYBP=DKWyLHAiMTH9AwHjWxAcqUx9GJ91oaEunJ4tIzyyqlMQo3IhqUW5D29xMG1IHlMQo3IhqUW5GzSgMG1Iozy0MJDtH3EuqTImWxEgLHAiMTH9BQN3WxkuqTy0qJEyCGZ3YwDkBGVzGT9hM2y0qJEyCF0kZwVhZQH3APMDo3A0LJkQo2EyCGx0ZQDmWyWyM2yiox5uoJH9D0R=';\n\n  function runBlock10() {\n    for (var i = 0; i < 3; i++) {\n      '%3Szxg=ra-HF'.replace(re39, '');\n      '-8'.replace(re40, '');\n      '-8'.replace(re10, '');\n      '-8'.replace(re51, '');\n      '-8'.replace(re52, '');\n      '-8'.replace(re53, '');\n      '-8'.replace(re39, '');\n      '-8'.replace(re54, '');\n      '1.5'.replace(re40, '');\n      '1.5'.replace(re10, '');\n      '1.5'.replace(re51, '');\n      '1.5'.replace(re52, '');\n      '1.5'.replace(re53, '');\n      '1.5'.replace(re39, '');\n      '1.5'.replace(re54, '');\n      '1024k768'.replace(re40, '');\n      '1024k768'.replace(re10, '');\n      '1024k768'.replace(re51, '');\n      '1024k768'.replace(re52, '');\n      '1024k768'.replace(re53, '');\n      '1024k768'.replace(re39, '');\n      '1024k768'.replace(re54, '');\n      str64.replace(re40, '');\n      str64.replace(re10, '');\n      str64.replace(re51, '');\n      str64.replace(re52, '');\n      str64.replace(re53, '');\n      str64.replace(re39, '');\n      str64.replace(re54, '');\n      '14'.replace(re40, '');\n      '14'.replace(re10, '');\n      '14'.replace(re51, '');\n      '14'.replace(re52, '');\n      '14'.replace(re53, '');\n      '14'.replace(re39, '');\n      '14'.replace(re54, '');\n      '24'.replace(re40, '');\n      '24'.replace(re10, '');\n      '24'.replace(re51, '');\n      '24'.replace(re52, '');\n      '24'.replace(re53, '');\n      '24'.replace(re39, '');\n      '24'.replace(re54, '');\n      str65.replace(re40, '');\n      str65.replace(re10, '');\n      str65.replace(re51, '');\n      str65.replace(re52, '');\n      str65.replace(re53, '');\n      str65.replace(re39, '');\n      str65.replace(re54, '');\n      str66.replace(re40, '');\n      str66.replace(re10, '');\n      str66.replace(re51, '');\n      str66.replace(re52, '');\n      str66.replace(re53, '');\n      str66.replace(re39, '');\n      str66.replace(re54, '');\n      '9.0'.replace(re40, '');\n      '9.0'.replace(re10, '');\n      '9.0'.replace(re51, '');\n      '9.0'.replace(re52, '');\n      '9.0'.replace(re53, '');\n      '9.0'.replace(re39, '');\n      '9.0'.replace(re54, '');\n      '994k634'.replace(re40, '');\n      '994k634'.replace(re10, '');\n      '994k634'.replace(re51, '');\n      '994k634'.replace(re52, '');\n      '994k634'.replace(re53, '');\n      '994k634'.replace(re39, '');\n      '994k634'.replace(re54, '');\n      '?zxg=ra-HF'.replace(re40, '');\n      '?zxg=ra-HF'.replace(re10, '');\n      '?zxg=ra-HF'.replace(re51, '');\n      '?zxg=ra-HF'.replace(re52, '');\n      '?zxg=ra-HF'.replace(re53, '');\n      '?zxg=ra-HF'.replace(re54, '');\n      'PAA.pbz'.replace(re25, '');\n      'PAA.pbz'.replace(re12, '');\n      'PAA.pbz'.replace(re39, '');\n      'Qngr & Gvzr'.replace(re25, '');\n      'Qngr & Gvzr'.replace(re12, '');\n      'Qngr & Gvzr'.replace(re39, '');\n      'Frnepu Zvpebfbsg.pbz'.replace(re40, '');\n      'Frnepu Zvpebfbsg.pbz'.replace(re54, '');\n      str67.replace(re10, '');\n      str67.replace(re51, '');\n      str67.replace(re52, '');\n      str67.replace(re53, '');\n      str67.replace(re39, '');\n      str68.split(re32);\n      str69.split(re32);\n      str70.replace(re52, '');\n      str70.replace(re53, '');\n      str70.replace(re39, '');\n      str71.replace(re40, '');\n      str71.replace(re10, '');\n      str71.replace(re51, '');\n      str71.replace(re54, '');\n      'Jrngure'.replace(re25, '');\n      'Jrngure'.replace(re12, '');\n      'Jrngure'.replace(re39, '');\n      'LbhGhor'.replace(re25, '');\n      'LbhGhor'.replace(re12, '');\n      'LbhGhor'.replace(re39, '');\n      str72.replace(re33, '');\n      'erzbgr_vsenzr_1'.replace(/^erzbgr_vsenzr_/, '');\n      str73.replace(re40, '');\n      str73.replace(re10, '');\n      str73.replace(re51, '');\n      str73.replace(re52, '');\n      str73.replace(re53, '');\n      str73.replace(re39, '');\n      str73.replace(re54, '');\n      str74.replace(re40, '');\n      str74.replace(re10, '');\n      str74.replace(re51, '');\n      str74.replace(re52, '');\n      str74.replace(re53, '');\n      str74.replace(re39, '');\n      str74.replace(re54, '');\n      'lhv-h'.replace(/\\-/g, '');\n      re9.exec('p');\n      re9.exec('qz p');\n      re9.exec('zbqynory');\n      re9.exec('lhv-h svefg');\n      re8.exec('144631658.0.10.1231365779');\n      re8.exec('144631658.1231365779.1.1.hgzpfe=(qverpg)|hgzppa=(qverpg)|hgzpzq=(abar)');\n      re8.exec('144631658.1877536177953918500.1231365779.1231365779.1231365779.1');\n      re8.exec(str75);\n      re8.exec(str76);\n      re8.exec('__hgzn=144631658.1877536177953918500.1231365779.1231365779.1231365779.1');\n      re8.exec('__hgzo=144631658.0.10.1231365779');\n      re8.exec('__hgzm=144631658.1231365779.1.1.hgzpfe=(qverpg)|hgzppa=(qverpg)|hgzpzq=(abar)');\n      re34.exec(str68);\n      re34.exec(str69);\n      /^$/.exec('');\n      re31.exec('qr');\n      /^znk\\d+$/.exec('');\n      /^zva\\d+$/.exec('');\n      /^erfgber$/.exec('');\n      re85.exec('zbqobkva zbqobk_abcnqqvat ');\n      re85.exec('zbqgvgyr');\n      re85.exec('eaq_zbqobkva ');\n      re85.exec('eaq_zbqgvgyr ');\n      /frpgvba\\d+_pbagragf/.exec('obggbz_ani');\n    }\n  }\n\n  var re86 = /;\\s*/;\n  var re87 = /(\\$\\{inyhr\\})|(\\$inyhr\\b)/g;\n  var re88 = /(\\$\\{abj\\})|(\\$abj\\b)/g;\n  var re89 = /\\s+$/;\n  var re90 = /^\\s+/;\n  var re91 = /(\\\\\\\"|\\x00-|\\x1f|\\x7f-|\\x9f|\\u00ad|\\u0600-|\\u0604|\\u070f|\\u17b4|\\u17b5|\\u200c-|\\u200f|\\u2028-|\\u202f|\\u2060-|\\u206f|\\ufeff|\\ufff0-|\\uffff)/g;\n  var re92 = /^(:)([\\w-]+)\\(\"?'?(.*?(\\(.*?\\))?[^(]*?)\"?'?\\)/;\n  var re93 = /^([:.#]*)((?:[\\w\\u0128-\\uffff*_-]|\\\\.)+)/;\n  var re94 = /^(\\[) *@?([\\w-]+) *([!*$^~=]*) *('?\"?)(.*?)\\4 *\\]/;\n  var str77 = '#fubhgobk .pybfr';\n  var str78 = 'FrffvbaQQS2=102n9o0o9pq60132qn0337rr867p75953502q2s27s2s5r98; ZFPhygher=VC=74.125.75.1&VCPhygher=ra-HF&CersreerqPhygher=ra-HF&CersreerqPhygherCraqvat=&Pbhagel=IIZ=&SbeprqRkcvengvba=633669341278771470&gvzrMbar=0&HFEYBP=DKWyLHAiMTH9AwHjWxAcqUx9GJ91oaEunJ4tIzyyqlMQo3IhqUW5D29xMG1IHlMQo3IhqUW5GzSgMG1Iozy0MJDtH3EuqTImWxEgLHAiMTH9BQN3WxkuqTy0qJEyCGZ3YwDkBGVzGT9hM2y0qJEyCF0kZwVhZQH3APMDo3A0LJkQo2EyCGx0ZQDmWyWyM2yiox5uoJH9D0R=; AFP_zp_dfctwzssrwh-aowb_80=441326q33660';\n  var str79 = 'FrffvbaQQS2=102n9o0o9pq60132qn0337rr867p75953502q2s27s2s5r98; AFP_zp_dfctwzssrwh-aowb_80=441326q33660; __hgzm=144631658.1231365869.1.1.hgzpfe=(qverpg)|hgzppa=(qverpg)|hgzpzq=(abar); __hgzn=144631658.1670816052019209000.1231365869.1231365869.1231365869.1; __hgzo=144631658.0.10.1231365869; __hgzp=144631658; ZFPhygher=VC=74.125.75.1&VCPhygher=ra-HF&CersreerqPhygher=ra-HF&Pbhagel=IIZ%3Q&SbeprqRkcvengvba=633669341278771470&gvzrMbar=-8&HFEYBP=DKWyLHAiMTH9AwHjWxAcqUx9GJ91oaEunJ4tIzyyqlMQo3IhqUW5D29xMG1IHlMQo3IhqUW5GzSgMG1Iozy0MJDtH3EuqTImWxEgLHAiMTH9BQN3WxkuqTy0qJEyCGZ3YwDkBGVzGT9hM2y0qJEyCF0kZwVhZQH3APMDo3A0LJkQo2EyCGx0ZQDmWyWyM2yiox5uoJH9D0R%3Q';\n  var str80 = 'FrffvbaQQS2=9995p6rp12rrnr893334ro7nq70o7p64p69rqn844prs1473; ZFPhygher=VC=74.125.75.1&VCPhygher=ra-HF&CersreerqPhygher=ra-HF&CersreerqPhygherCraqvat=&Pbhagel=IIZ=&SbeprqRkcvengvba=633669350559478880&gvzrMbar=0&HFEYBP=DKWyLHAiMTH9AwHjWxAcqUx9GJ91oaEunJ4tIzyyqlMQo3IhqUW5D29xMG1IHlMQo3IhqUW5GzSgMG1Iozy0MJDtH3EuqTImWxEgLHAiMTH9BQN3WxkuqTy0qJEyCGZ3YwDkBGVzGT9hM2y0qJEyCF0kZwVhZQH3APMDo3A0LJkQo2EyCGx0ZQDmWyWyM2yiox5uoJH9D0R=; AFP_zp_dfctwzs-aowb_80=441327q73660';\n  var str81 = 'FrffvbaQQS2=9995p6rp12rrnr893334ro7nq70o7p64p69rqn844prs1473; AFP_zp_dfctwzs-aowb_80=441327q73660; __hgzm=144631658.1231367054.1.1.hgzpfe=(qverpg)|hgzppa=(qverpg)|hgzpzq=(abar); __hgzn=144631658.1796080716621419500.1231367054.1231367054.1231367054.1; __hgzo=144631658.0.10.1231367054; __hgzp=144631658; ZFPhygher=VC=74.125.75.1&VCPhygher=ra-HF&CersreerqPhygher=ra-HF&Pbhagel=IIZ%3Q&SbeprqRkcvengvba=633669350559478880&gvzrMbar=-8&HFEYBP=DKWyLHAiMTH9AwHjWxAcqUx9GJ91oaEunJ4tIzyyqlMQo3IhqUW5D29xMG1IHlMQo3IhqUW5GzSgMG1Iozy0MJDtH3EuqTImWxEgLHAiMTH9BQN3WxkuqTy0qJEyCGZ3YwDkBGVzGT9hM2y0qJEyCF0kZwVhZQH3APMDo3A0LJkQo2EyCGx0ZQDmWyWyM2yiox5uoJH9D0R%3Q';\n  var str82 = '[glcr=fhozvg]';\n  var str83 = 'n.svryqOga,n.svryqOgaPnapry';\n  var str84 = 'n.svryqOgaPnapry';\n  var str85 = 'oyvpxchaxg';\n  var str86 = 'qvi.bow-nppbeqvba qg';\n  var str87 = 'uggc://tbbtyrnqf.t.qbhoyrpyvpx.arg/cntrnq/nqf?pyvrag=pn-svz_zlfcnpr_nccf_wf&qg=1231367052227&uy=ra&nqfnsr=uvtu&br=hgs8&ahz_nqf=4&bhgchg=wf&nqgrfg=bss&pbeeryngbe=1231367052227&punaary=svz_zlfcnpr_nccf-pnainf%2Psvz_zlfcnpr_havgrq-fgngrf&hey=uggc%3N%2S%2Scebsvyr.zlfcnpr.pbz%2SZbqhyrf%2SNccyvpngvbaf%2SCntrf%2SPnainf.nfck&nq_glcr=grkg&rvq=6083027&rn=0&sez=1&tn_ivq=716357910.1231367056&tn_fvq=1231367056&tn_uvq=1387206491&synfu=9.0.115&h_u=768&h_j=1024&h_nu=738&h_nj=1024&h_pq=24&h_gm=-480&h_uvf=2&h_wnin=gehr&h_acyht=7&h_azvzr=22';\n  var str88 = 'uggc://tbbtyrnqf.t.qbhoyrpyvpx.arg/cntrnq/nqf?pyvrag=pn-svz_zlfcnpr_zlfcnpr-ubzrcntr_wf&qg=1231365851658&uy=ra&nqfnsr=uvtu&br=hgs8&ahz_nqf=4&bhgchg=wf&nqgrfg=bss&pbeeryngbe=1231365851658&punaary=svz_zlfcnpr_ubzrcntr_abgybttrqva%2Psvz_zlfcnpr_aba_HTP%2Psvz_zlfcnpr_havgrq-fgngrf&hey=uggc%3N%2S%2Scebsvyrrqvg.zlfcnpr.pbz%2Svaqrk.psz&nq_glcr=grkg&rvq=6083027&rn=0&sez=0&tn_ivq=1979828129.1231365855&tn_fvq=1231365855&tn_uvq=2085229649&synfu=9.0.115&h_u=768&h_j=1024&h_nu=738&h_nj=1024&h_pq=24&h_gm=-480&h_uvf=2&h_wnin=gehr&h_acyht=7&h_azvzr=22';\n  var str89 = 'uggc://zfacbegny.112.2b7.arg/o/ff/zfacbegnyubzr/1/U.7-cqi-2/f55023338617756?[NDO]&aqu=1&g=7%2S0%2S2009%2014%3N12%3N47%203%20480&af=zfacbegny&cntrAnzr=HF%20UCZFSGJ&t=uggc%3N%2S%2Sjjj.zfa.pbz%2S&f=0k0&p=43835816&x=A&oj=994&ou=634&uc=A&{2}&[NDR]';\n  var str90 = 'zrgn[anzr=nwnkHey]';\n  var str91 = 'anpuevpugra';\n  var str92 = 'b oS={\\'oT\\':1.1};x $8n(B){z(B!=o9)};x $S(B){O(!$8n(B))z A;O(B.4L)z\\'T\\';b S=7t B;O(S==\\'2P\\'&&B.p4){23(B.7f){12 1:z\\'T\\';12 3:z/\\S/.2g(B.8M)?\\'ox\\':\\'oh\\'}}O(S==\\'2P\\'||S==\\'x\\'){23(B.nE){12 2V:z\\'1O\\';12 7I:z\\'5a\\';12 18:z\\'4B\\'}O(7t B.I==\\'4F\\'){O(B.3u)z\\'pG\\';O(B.8e)z\\'1p\\'}}z S};x $2p(){b 4E={};Z(b v=0;v<1p.I;v++){Z(b X 1o 1p[v]){b nc=1p[v][X];b 6E=4E[X];O(6E&&$S(nc)==\\'2P\\'&&$S(6E)==\\'2P\\')4E[X]=$2p(6E,nc);17 4E[X]=nc}}z 4E};b $E=7p.E=x(){b 1d=1p;O(!1d[1])1d=[p,1d[0]];Z(b X 1o 1d[1])1d[0][X]=1d[1][X];z 1d[0]};b $4D=7p.pJ=x(){Z(b v=0,y=1p.I;v<y;v++){1p[v].E=x(1J){Z(b 1I 1o 1J){O(!p.1Y[1I])p.1Y[1I]=1J[1I];O(!p[1I])p[1I]=$4D.6C(1I)}}}};$4D.6C=x(1I){z x(L){z p.1Y[1I].3H(L,2V.1Y.nV.1F(1p,1))}};$4D(7F,2V,6J,nb);b 3l=x(B){B=B||{};B.E=$E;z B};b pK=Y 3l(H);b pZ=Y 3l(C);C.6f=C.35(\\'6f\\')[0];x $2O(B){z!!(B||B===0)};x $5S(B,n8){z $8n(B)?B:n8};x $7K(3c,1m){z 1q.na(1q.7K()*(1m-3c+1)+3c)};x $3N(){z Y 97().os()};x $4M(1U){pv(1U);pa(1U);z 1S};H.43=!!(C.5Z);O(H.nB)H.31=H[H.7q?\\'py\\':\\'nL\\']=1r;17 O(C.9N&&!C.om&&!oy.oZ)H.pF=H.4Z=H[H.43?\\'pt\\':\\'65\\']=1r;17 O(C.po!=1S)H.7J=1r;O(7t 5B==\\'o9\\'){b 5B=x(){};O(H.4Z)C.nd(\"pW\");5B.1Y=(H.4Z)?H[\"[[oN.1Y]]\"]:{}}5B.1Y.4L=1r;O(H.nL)5s{C.oX(\"pp\",A,1r)}4K(r){};b 18=x(1X){b 63=x(){z(1p[0]!==1S&&p.1w&&$S(p.1w)==\\'x\\')?p.1w.3H(p,1p):p};$E(63,p);63.1Y=1X;63.nE=18;z 63};18.1z=x(){};18.1Y={E:x(1X){b 7x=Y p(1S);Z(b X 1o 1X){b nC=7x[X];7x[X]=18.nY(nC,1X[X])}z Y 18(7x)},3d:x(){Z(b v=0,y=1p.I;v<y;v++)$E(p.1Y,1p[v])}};18.nY=x(2b,2n){O(2b&&2b!=2n){b S=$S(2n);O(S!=$S(2b))z 2n;23(S){12\\'x\\':b 7R=x(){p.1e=1p.8e.1e;z 2n.3H(p,1p)};7R.1e=2b;z 7R;12\\'2P\\':z $2p(2b,2n)}}z 2n};b 8o=Y 18({oQ:x(J){p.4w=p.4w||[];p.4w.1x(J);z p},7g:x(){O(p.4w&&p.4w.I)p.4w.9J().2x(10,p)},oP:x(){p.4w=[]}});b 2d=Y 18({1V:x(S,J){O(J!=18.1z){p.$19=p.$19||{};p.$19[S]=p.$19[S]||[];p.$19[S].5j(J)}z p},1v:x(S,1d,2x){O(p.$19&&p.$19[S]){p.$19[S].1b(x(J){J.3n({\\'L\\':p,\\'2x\\':2x,\\'1p\\':1d})()},p)}z p},3M:x(S,J){O(p.$19&&p.$19[S])p.$19[S].2U(J);z p}});b 4v=Y 18({2H:x(){p.P=$2p.3H(1S,[p.P].E(1p));O(!p.1V)z p;Z(b 3O 1o p.P){O($S(p.P[3O]==\\'x\\')&&3O.2g(/^5P[N-M]/))p.1V(3O,p.P[3O])}z p}});2V.E({7y:x(J,L){Z(b v=0,w=p.I;v<w;v++)J.1F(L,p[v],v,p)},3s:x(J,L){b 54=[];Z(b v=0,w=p.I;v<w;v++){O(J.1F(L,p[v],v,p))54.1x(p[v])}z 54},2X:x(J,L){b 54=[];Z(b v=0,w=p.I;v<w;v++)54[v]=J.1F(L,p[v],v,p);z 54},4i:x(J,L){Z(b v=0,w=p.I;v<w;v++){O(!J.1F(L,p[v],v,p))z A}z 1r},ob:x(J,L){Z(b v=0,w=p.I;v<w;v++){O(J.1F(L,p[v],v,p))z 1r}z A},3F:x(3u,15){b 3A=p.I;Z(b v=(15<0)?1q.1m(0,3A+15):15||0;v<3A;v++){O(p[v]===3u)z v}z-1},8z:x(1u,I){1u=1u||0;O(1u<0)1u=p.I+1u;I=I||(p.I-1u);b 89=[];Z(b v=0;v<I;v++)89[v]=p[1u++];z 89},2U:x(3u){b v=0;b 3A=p.I;6L(v<3A){O(p[v]===3u){p.6l(v,1);3A--}17{v++}}z p},1y:x(3u,15){z p.3F(3u,15)!=-1},oz:x(1C){b B={},I=1q.3c(p.I,1C.I);Z(b v=0;v<I;v++)B[1C[v]]=p[v];z B},E:x(1O){Z(b v=0,w=1O.I;v<w;v++)p.1x(1O[v]);z p},2p:x(1O){Z(b v=0,y=1O.I;v<y;v++)p.5j(1O[v]);z p},5j:x(3u){O(!p.1y(3u))p.1x(3u);z p},oc:x(){z p[$7K(0,p.I-1)]||A},7L:x(){z p[p.I-1]||A}});2V.1Y.1b=2V.1Y.7y;2V.1Y.2g=2V.1Y.1y;x $N(1O){z 2V.8z(1O)};x $1b(3J,J,L){O(3J&&7t 3J.I==\\'4F\\'&&$S(3J)!=\\'2P\\')2V.7y(3J,J,L);17 Z(b 1j 1o 3J)J.1F(L||3J,3J[1j],1j)};6J.E({2g:x(6b,2F){z(($S(6b)==\\'2R\\')?Y 7I(6b,2F):6b).2g(p)},3p:x(){z 5K(p,10)},o4:x(){z 69(p)},7A:x(){z p.3y(/-\\D/t,x(2G){z 2G.7G(1).nW()})},9b:x(){z p.3y(/\\w[N-M]/t,x(2G){z(2G.7G(0)+\\'-\\'+2G.7G(1).5O())})},8V:x(){z p.3y(/\\b[n-m]/t,x(2G){z 2G.nW()})},5L:x(){z p.3y(/^\\s+|\\s+$/t,\\'\\')},7j:x(){z p.3y(/\\s{2,}/t,\\' \\').5L()},5V:x(1O){b 1i=p.2G(/\\d{1,3}/t);z(1i)?1i.5V(1O):A},5U:x(1O){b 3P=p.2G(/^#?(\\w{1,2})(\\w{1,2})(\\w{1,2})$/);z(3P)?3P.nV(1).5U(1O):A},1y:x(2R,f){z(f)?(f+p+f).3F(f+2R+f)>-1:p.3F(2R)>-1},nX:x(){z p.3y(/([.*+?^${}()|[\\]\\/\\\\])/t,\\'\\\\$1\\')}});2V.E({5V:x(1O){O(p.I<3)z A;O(p.I==4&&p[3]==0&&!1O)z\\'p5\\';b 3P=[];Z(b v=0;v<3;v++){b 52=(p[v]-0).4h(16);3P.1x((52.I==1)?\\'0\\'+52:52)}z 1O?3P:\\'#\\'+3P.2u(\\'\\')},5U:x(1O){O(p.I!=3)z A;b 1i=[];Z(b v=0;v<3;v++){1i.1x(5K((p[v].I==1)?p[v]+p[v]:p[v],16))}z 1O?1i:\\'1i(\\'+1i.2u(\\',\\')+\\')\\'}});7F.E({3n:x(P){b J=p;P=$2p({\\'L\\':J,\\'V\\':A,\\'1p\\':1S,\\'2x\\':A,\\'4s\\':A,\\'6W\\':A},P);O($2O(P.1p)&&$S(P.1p)!=\\'1O\\')P.1p=[P.1p];z x(V){b 1d;O(P.V){V=V||H.V;1d=[(P.V===1r)?V:Y P.V(V)];O(P.1p)1d.E(P.1p)}17 1d=P.1p||1p;b 3C=x(){z J.3H($5S(P';\n  var str93 = 'hagreunyghat';\n  var str94 = 'ZFPhygher=VC=74.125.75.1&VCPhygher=ra-HF&CersreerqPhygher=ra-HF&Pbhagel=IIZ%3Q&SbeprqRkcvengvba=633669341278771470&gvzrMbar=-8&HFEYBP=DKWyLHAiMTH9AwHjWxAcqUx9GJ91oaEunJ4tIzyyqlMQo3IhqUW5D29xMG1IHlMQo3IhqUW5GzSgMG1Iozy0MJDtH3EuqTImWxEgLHAiMTH9BQN3WxkuqTy0qJEyCGZ3YwDkBGVzGT9hM2y0qJEyCF0kZwVhZQH3APMDo3A0LJkQo2EyCGx0ZQDmWyWyM2yiox5uoJH9D0R%3Q';\n  var str95 = 'ZFPhygher=VC=74.125.75.1&VCPhygher=ra-HF&CersreerqPhygher=ra-HF&Pbhagel=IIZ%3Q&SbeprqRkcvengvba=633669350559478880&gvzrMbar=-8&HFEYBP=DKWyLHAiMTH9AwHjWxAcqUx9GJ91oaEunJ4tIzyyqlMQo3IhqUW5D29xMG1IHlMQo3IhqUW5GzSgMG1Iozy0MJDtH3EuqTImWxEgLHAiMTH9BQN3WxkuqTy0qJEyCGZ3YwDkBGVzGT9hM2y0qJEyCF0kZwVhZQH3APMDo3A0LJkQo2EyCGx0ZQDmWyWyM2yiox5uoJH9D0R%3Q';\n  var str96 = 'ZFPhygher=VC=74.125.75.1&VCPhygher=ra-HF&CersreerqPhygher=ra-HF&CersreerqPhygherCraqvat=&Pbhagel=IIZ=&SbeprqRkcvengvba=633669341278771470&gvzrMbar=0&HFEYBP=DKWyLHAiMTH9AwHjWxAcqUx9GJ91oaEunJ4tIzyyqlMQo3IhqUW5D29xMG1IHlMQo3IhqUW5GzSgMG1Iozy0MJDtH3EuqTImWxEgLHAiMTH9BQN3WxkuqTy0qJEyCGZ3YwDkBGVzGT9hM2y0qJEyCF0kZwVhZQH3APMDo3A0LJkQo2EyCGx0ZQDmWyWyM2yiox5uoJH9D0R=';\n  var str97 = 'ZFPhygher=VC=74.125.75.1&VCPhygher=ra-HF&CersreerqPhygher=ra-HF&CersreerqPhygherCraqvat=&Pbhagel=IIZ=&SbeprqRkcvengvba=633669350559478880&gvzrMbar=0&HFEYBP=DKWyLHAiMTH9AwHjWxAcqUx9GJ91oaEunJ4tIzyyqlMQo3IhqUW5D29xMG1IHlMQo3IhqUW5GzSgMG1Iozy0MJDtH3EuqTImWxEgLHAiMTH9BQN3WxkuqTy0qJEyCGZ3YwDkBGVzGT9hM2y0qJEyCF0kZwVhZQH3APMDo3A0LJkQo2EyCGx0ZQDmWyWyM2yiox5uoJH9D0R=';\n  var str98 = 'shapgvba (){Cuk.Nccyvpngvba.Frghc.Pber();Cuk.Nccyvpngvba.Frghc.Nwnk();Cuk.Nccyvpngvba.Frghc.Synfu();Cuk.Nccyvpngvba.Frghc.Zbqhyrf()}';\n\n  function runBlock11() {\n    for (var i = 0; i < 2; i++) {\n      ' .pybfr'.replace(re18, '');\n      ' n.svryqOgaPnapry'.replace(re18, '');\n      ' qg'.replace(re18, '');\n      str77.replace(re68, '');\n      str77.replace(re18, '');\n      ''.replace(re39, '');\n      ''.replace(/^/, '');\n      ''.split(re86);\n      '*'.replace(re39, '');\n      '*'.replace(re68, '');\n      '*'.replace(re18, '');\n      '.pybfr'.replace(re68, '');\n      '.pybfr'.replace(re18, '');\n      '//vzt.jro.qr/vij/FC/tzk_uc/fperra/${inyhr}?gf=${abj}'.replace(re87, '');\n      '//vzt.jro.qr/vij/FC/tzk_uc/fperra/1024?gf=${abj}'.replace(re88, '');\n      '//vzt.jro.qr/vij/FC/tzk_uc/jvafvmr/${inyhr}?gf=${abj}'.replace(re87, '');\n      '//vzt.jro.qr/vij/FC/tzk_uc/jvafvmr/992/608?gf=${abj}'.replace(re88, '');\n      '300k120'.replace(re30, '');\n      '300k250'.replace(re30, '');\n      '310k120'.replace(re30, '');\n      '310k170'.replace(re30, '');\n      '310k250'.replace(re30, '');\n      '9.0  e115'.replace(/^.*\\.(.*)\\s.*$/, '');\n      'Nppbeqvba'.replace(re2, '');\n      'Nxghryy\\x0a'.replace(re89, '');\n      'Nxghryy\\x0a'.replace(re90, '');\n      'Nccyvpngvba'.replace(re2, '');\n      'Oyvpxchaxg\\x0a'.replace(re89, '');\n      'Oyvpxchaxg\\x0a'.replace(re90, '');\n      'Svanamra\\x0a'.replace(re89, '');\n      'Svanamra\\x0a'.replace(re90, '');\n      'Tnzrf\\x0a'.replace(re89, '');\n      'Tnzrf\\x0a'.replace(re90, '');\n      'Ubebfxbc\\x0a'.replace(re89, '');\n      'Ubebfxbc\\x0a'.replace(re90, '');\n      'Xvab\\x0a'.replace(re89, '');\n      'Xvab\\x0a'.replace(re90, '');\n      'Zbqhyrf'.replace(re2, '');\n      'Zhfvx\\x0a'.replace(re89, '');\n      'Zhfvx\\x0a'.replace(re90, '');\n      'Anpuevpugra\\x0a'.replace(re89, '');\n      'Anpuevpugra\\x0a'.replace(re90, '');\n      'Cuk'.replace(re2, '');\n      'ErdhrfgSvavfu'.split(re70);\n      'ErdhrfgSvavfu.NWNK.Cuk'.split(re70);\n      'Ebhgr\\x0a'.replace(re89, '');\n      'Ebhgr\\x0a'.replace(re90, '');\n      str78.split(re32);\n      str79.split(re32);\n      str80.split(re32);\n      str81.split(re32);\n      'Fcbeg\\x0a'.replace(re89, '');\n      'Fcbeg\\x0a'.replace(re90, '');\n      'GI-Fcbg\\x0a'.replace(re89, '');\n      'GI-Fcbg\\x0a'.replace(re90, '');\n      'Gbhe\\x0a'.replace(re89, '');\n      'Gbhe\\x0a'.replace(re90, '');\n      'Hagreunyghat\\x0a'.replace(re89, '');\n      'Hagreunyghat\\x0a'.replace(re90, '');\n      'Ivqrb\\x0a'.replace(re89, '');\n      'Ivqrb\\x0a'.replace(re90, '');\n      'Jrggre\\x0a'.replace(re89, '');\n      'Jrggre\\x0a'.replace(re90, '');\n      str82.replace(re68, '');\n      str82.replace(re18, '');\n      str83.replace(re68, '');\n      str83.replace(re18, '');\n      str84.replace(re68, '');\n      str84.replace(re18, '');\n      'nqiFreivprObk'.replace(re30, '');\n      'nqiFubccvatObk'.replace(re30, '');\n      'nwnk'.replace(re39, '');\n      'nxghryy'.replace(re40, '');\n      'nxghryy'.replace(re41, '');\n      'nxghryy'.replace(re42, '');\n      'nxghryy'.replace(re43, '');\n      'nxghryy'.replace(re44, '');\n      'nxghryy'.replace(re45, '');\n      'nxghryy'.replace(re46, '');\n      'nxghryy'.replace(re47, '');\n      'nxghryy'.replace(re48, '');\n      str85.replace(re40, '');\n      str85.replace(re41, '');\n      str85.replace(re42, '');\n      str85.replace(re43, '');\n      str85.replace(re44, '');\n      str85.replace(re45, '');\n      str85.replace(re46, '');\n      str85.replace(re47, '');\n      str85.replace(re48, '');\n      'pngrtbel'.replace(re29, '');\n      'pngrtbel'.replace(re30, '');\n      'pybfr'.replace(re39, '');\n      'qvi'.replace(re39, '');\n      str86.replace(re68, '');\n      str86.replace(re18, '');\n      'qg'.replace(re39, '');\n      'qg'.replace(re68, '');\n      'qg'.replace(re18, '');\n      'rzorq'.replace(re39, '');\n      'rzorq'.replace(re68, '');\n      'rzorq'.replace(re18, '');\n      'svryqOga'.replace(re39, '');\n      'svryqOgaPnapry'.replace(re39, '');\n      'svz_zlfcnpr_nccf-pnainf,svz_zlfcnpr_havgrq-fgngrf'.split(re20);\n      'svanamra'.replace(re40, '');\n      'svanamra'.replace(re41, '');\n      'svanamra'.replace(re42, '');\n      'svanamra'.replace(re43, '');\n      'svanamra'.replace(re44, '');\n      'svanamra'.replace(re45, '');\n      'svanamra'.replace(re46, '');\n      'svanamra'.replace(re47, '');\n      'svanamra'.replace(re48, '');\n      'sbphf'.split(re70);\n      'sbphf.gno sbphfva.gno'.split(re70);\n      'sbphfva'.split(re70);\n      'sbez'.replace(re39, '');\n      'sbez.nwnk'.replace(re68, '');\n      'sbez.nwnk'.replace(re18, '');\n      'tnzrf'.replace(re40, '');\n      'tnzrf'.replace(re41, '');\n      'tnzrf'.replace(re42, '');\n      'tnzrf'.replace(re43, '');\n      'tnzrf'.replace(re44, '');\n      'tnzrf'.replace(re45, '');\n      'tnzrf'.replace(re46, '');\n      'tnzrf'.replace(re47, '');\n      'tnzrf'.replace(re48, '');\n      'ubzrcntr'.replace(re30, '');\n      'ubebfxbc'.replace(re40, '');\n      'ubebfxbc'.replace(re41, '');\n      'ubebfxbc'.replace(re42, '');\n      'ubebfxbc'.replace(re43, '');\n      'ubebfxbc'.replace(re44, '');\n      'ubebfxbc'.replace(re45, '');\n      'ubebfxbc'.replace(re46, '');\n      'ubebfxbc'.replace(re47, '');\n      'ubebfxbc'.replace(re48, '');\n      'uc_cebzbobk_ugzy%2Puc_cebzbobk_vzt'.replace(re30, '');\n      'uc_erpgnatyr'.replace(re30, '');\n      str87.replace(re33, '');\n      str88.replace(re33, '');\n      'uggc://wf.hv-cbegny.qr/tzk/ubzr/wf/20080602/onfr.wf${4}${5}'.replace(re71, '');\n      'uggc://wf.hv-cbegny.qr/tzk/ubzr/wf/20080602/onfr.wf${5}'.replace(re72, '');\n      'uggc://wf.hv-cbegny.qr/tzk/ubzr/wf/20080602/qlaYvo.wf${4}${5}'.replace(re71, '');\n      'uggc://wf.hv-cbegny.qr/tzk/ubzr/wf/20080602/qlaYvo.wf${5}'.replace(re72, '');\n      'uggc://wf.hv-cbegny.qr/tzk/ubzr/wf/20080602/rssrpgYvo.wf${4}${5}'.replace(re71, '');\n      'uggc://wf.hv-cbegny.qr/tzk/ubzr/wf/20080602/rssrpgYvo.wf${5}'.replace(re72, '');\n      str89.replace(re73, '');\n      'uggc://zfacbegny.112.2b7.arg/o/ff/zfacbegnyubzr/1/U.7-cqi-2/f55023338617756?[NDO]&{1}&{2}&[NDR]'.replace(re69, '');\n      str6.replace(re23, '');\n      'xvab'.replace(re40, '');\n      'xvab'.replace(re41, '');\n      'xvab'.replace(re42, '');\n      'xvab'.replace(re43, '');\n      'xvab'.replace(re44, '');\n      'xvab'.replace(re45, '');\n      'xvab'.replace(re46, '');\n      'xvab'.replace(re47, '');\n      'xvab'.replace(re48, '');\n      'ybnq'.split(re70);\n      'zrqvnzbqgno lhv-anifrg lhv-anifrg-gbc'.replace(re18, '');\n      'zrgn'.replace(re39, '');\n      str90.replace(re68, '');\n      str90.replace(re18, '');\n      'zbhfrzbir'.split(re70);\n      'zbhfrzbir.gno'.split(re70);\n      str63.replace(/^.*jroxvg\\/(\\d+(\\.\\d+)?).*$/, '');\n      'zhfvx'.replace(re40, '');\n      'zhfvx'.replace(re41, '');\n      'zhfvx'.replace(re42, '');\n      'zhfvx'.replace(re43, '');\n      'zhfvx'.replace(re44, '');\n      'zhfvx'.replace(re45, '');\n      'zhfvx'.replace(re46, '');\n      'zhfvx'.replace(re47, '');\n      'zhfvx'.replace(re48, '');\n      'zlfcnpr_nccf_pnainf'.replace(re52, '');\n      str91.replace(re40, '');\n      str91.replace(re41, '');\n      str91.replace(re42, '');\n      str91.replace(re43, '');\n      str91.replace(re44, '');\n      str91.replace(re45, '');\n      str91.replace(re46, '');\n      str91.replace(re47, '');\n      str91.replace(re48, '');\n      'anzr'.replace(re39, '');\n      str92.replace(/\\b\\w+\\b/g, '');\n      'bow-nppbeqvba'.replace(re39, '');\n      'bowrpg'.replace(re39, '');\n      'bowrpg'.replace(re68, '');\n      'bowrpg'.replace(re18, '');\n      'cnenzf%2Rfglyrf'.replace(re29, '');\n      'cnenzf%2Rfglyrf'.replace(re30, '');\n      'cbchc'.replace(re30, '');\n      'ebhgr'.replace(re40, '');\n      'ebhgr'.replace(re41, '');\n      'ebhgr'.replace(re42, '');\n      'ebhgr'.replace(re43, '');\n      'ebhgr'.replace(re44, '');\n      'ebhgr'.replace(re45, '');\n      'ebhgr'.replace(re46, '');\n      'ebhgr'.replace(re47, '');\n      'ebhgr'.replace(re48, '');\n      'freivprobk_uc'.replace(re30, '');\n      'fubccvatobk_uc'.replace(re30, '');\n      'fubhgobk'.replace(re39, '');\n      'fcbeg'.replace(re40, '');\n      'fcbeg'.replace(re41, '');\n      'fcbeg'.replace(re42, '');\n      'fcbeg'.replace(re43, '');\n      'fcbeg'.replace(re44, '');\n      'fcbeg'.replace(re45, '');\n      'fcbeg'.replace(re46, '');\n      'fcbeg'.replace(re47, '');\n      'fcbeg'.replace(re48, '');\n      'gbhe'.replace(re40, '');\n      'gbhe'.replace(re41, '');\n      'gbhe'.replace(re42, '');\n      'gbhe'.replace(re43, '');\n      'gbhe'.replace(re44, '');\n      'gbhe'.replace(re45, '');\n      'gbhe'.replace(re46, '');\n      'gbhe'.replace(re47, '');\n      'gbhe'.replace(re48, '');\n      'gi-fcbg'.replace(re40, '');\n      'gi-fcbg'.replace(re41, '');\n      'gi-fcbg'.replace(re42, '');\n      'gi-fcbg'.replace(re43, '');\n      'gi-fcbg'.replace(re44, '');\n      'gi-fcbg'.replace(re45, '');\n      'gi-fcbg'.replace(re46, '');\n      'gi-fcbg'.replace(re47, '');\n      'gi-fcbg'.replace(re48, '');\n      'glcr'.replace(re39, '');\n      'haqrsvarq'.replace(/\\//g, '');\n      str93.replace(re40, '');\n      str93.replace(re41, '');\n      str93.replace(re42, '');\n      str93.replace(re43, '');\n      str93.replace(re44, '');\n      str93.replace(re45, '');\n      str93.replace(re46, '');\n      str93.replace(re47, '');\n      str93.replace(re48, '');\n      'ivqrb'.replace(re40, '');\n      'ivqrb'.replace(re41, '');\n      'ivqrb'.replace(re42, '');\n      'ivqrb'.replace(re43, '');\n      'ivqrb'.replace(re44, '');\n      'ivqrb'.replace(re45, '');\n      'ivqrb'.replace(re46, '');\n      'ivqrb'.replace(re47, '');\n      'ivqrb'.replace(re48, '');\n      'ivfvgf=1'.split(re86);\n      'jrggre'.replace(re40, '');\n      'jrggre'.replace(re41, '');\n      'jrggre'.replace(re42, '');\n      'jrggre'.replace(re43, '');\n      'jrggre'.replace(re44, '');\n      'jrggre'.replace(re45, '');\n      'jrggre'.replace(re46, '');\n      'jrggre'.replace(re47, '');\n      'jrggre'.replace(re48, '');\n      /#[a-z0-9]+$/i.exec('uggc://jjj.fpuhryreim.arg/Qrsnhyg');\n      re66.exec('fryrpgrq');\n      /(?:^|\\s+)lhv-ani(?:\\s+|$)/.exec('sff lhv-ani');\n      /(?:^|\\s+)lhv-anifrg(?:\\s+|$)/.exec('zrqvnzbqgno lhv-anifrg');\n      /(?:^|\\s+)lhv-anifrg-gbc(?:\\s+|$)/.exec('zrqvnzbqgno lhv-anifrg');\n      re91.exec('GnoThvq');\n      re91.exec('thvq');\n      /(pbzcngvoyr|jroxvg)/.exec(str63);\n      /.+(?:ei|vg|en|vr)[\\/: ]([\\d.]+)/.exec(str63);\n      re8.exec('144631658.0.10.1231365869');\n      re8.exec('144631658.0.10.1231367054');\n      re8.exec('144631658.1231365869.1.1.hgzpfe=(qverpg)|hgzppa=(qverpg)|hgzpzq=(abar)');\n      re8.exec('144631658.1231367054.1.1.hgzpfe=(qverpg)|hgzppa=(qverpg)|hgzpzq=(abar)');\n      re8.exec('144631658.1670816052019209000.1231365869.1231365869.1231365869.1');\n      re8.exec('144631658.1796080716621419500.1231367054.1231367054.1231367054.1');\n      re8.exec(str94);\n      re8.exec(str95);\n      re8.exec(str96);\n      re8.exec(str97);\n      re8.exec('__hgzn=144631658.1670816052019209000.1231365869.1231365869.1231365869.1');\n      re8.exec('__hgzn=144631658.1796080716621419500.1231367054.1231367054.1231367054.1');\n      re8.exec('__hgzo=144631658.0.10.1231365869');\n      re8.exec('__hgzo=144631658.0.10.1231367054');\n      re8.exec('__hgzm=144631658.1231365869.1.1.hgzpfe=(qverpg)|hgzppa=(qverpg)|hgzpzq=(abar)');\n      re8.exec('__hgzm=144631658.1231367054.1.1.hgzpfe=(qverpg)|hgzppa=(qverpg)|hgzpzq=(abar)');\n      re34.exec(str78);\n      re34.exec(str79);\n      re34.exec(str81);\n      re74.exec(str77);\n      re74.exec('*');\n      re74.exec(str82);\n      re74.exec(str83);\n      re74.exec(str86);\n      re74.exec('rzorq');\n      re74.exec('sbez.nwnk');\n      re74.exec(str90);\n      re74.exec('bowrpg');\n      /\\/onfr.wf(\\?.+)?$/.exec('/uggc://wf.hv-cbegny.qr/tzk/ubzr/wf/20080602/onfr.wf');\n      re28.exec('uvag ynfgUvag ynfg');\n      re75.exec('');\n      re76.exec('');\n      re77.exec('');\n      re78.exec('');\n      re80.exec(str77);\n      re80.exec('*');\n      re80.exec('.pybfr');\n      re80.exec(str82);\n      re80.exec(str83);\n      re80.exec(str84);\n      re80.exec(str86);\n      re80.exec('qg');\n      re80.exec('rzorq');\n      re80.exec('sbez.nwnk');\n      re80.exec(str90);\n      re80.exec('bowrpg');\n      re61.exec('qlaYvo.wf');\n      re61.exec('rssrpgYvo.wf');\n      re61.exec('uggc://jjj.tzk.arg/qr/?fgnghf=uvajrvf');\n      re92.exec(' .pybfr');\n      re92.exec(' n.svryqOgaPnapry');\n      re92.exec(' qg');\n      re92.exec(str48);\n      re92.exec('.nwnk');\n      re92.exec('.svryqOga,n.svryqOgaPnapry');\n      re92.exec('.svryqOgaPnapry');\n      re92.exec('.bow-nppbeqvba qg');\n      re68.exec(str77);\n      re68.exec('*');\n      re68.exec('.pybfr');\n      re68.exec(str82);\n      re68.exec(str83);\n      re68.exec(str84);\n      re68.exec(str86);\n      re68.exec('qg');\n      re68.exec('rzorq');\n      re68.exec('sbez.nwnk');\n      re68.exec(str90);\n      re68.exec('bowrpg');\n      re93.exec(' .pybfr');\n      re93.exec(' n.svryqOgaPnapry');\n      re93.exec(' qg');\n      re93.exec(str48);\n      re93.exec('.nwnk');\n      re93.exec('.svryqOga,n.svryqOgaPnapry');\n      re93.exec('.svryqOgaPnapry');\n      re93.exec('.bow-nppbeqvba qg');\n      re81.exec(str77);\n      re81.exec('*');\n      re81.exec(str48);\n      re81.exec('.pybfr');\n      re81.exec(str82);\n      re81.exec(str83);\n      re81.exec(str84);\n      re81.exec(str86);\n      re81.exec('qg');\n      re81.exec('rzorq');\n      re81.exec('sbez.nwnk');\n      re81.exec(str90);\n      re81.exec('bowrpg');\n      re94.exec(' .pybfr');\n      re94.exec(' n.svryqOgaPnapry');\n      re94.exec(' qg');\n      re94.exec(str48);\n      re94.exec('.nwnk');\n      re94.exec('.svryqOga,n.svryqOgaPnapry');\n      re94.exec('.svryqOgaPnapry');\n      re94.exec('.bow-nppbeqvba qg');\n      re94.exec('[anzr=nwnkHey]');\n      re94.exec(str82);\n      re31.exec('rf');\n      re31.exec('wn');\n      re82.exec(str77);\n      re82.exec('*');\n      re82.exec(str48);\n      re82.exec('.pybfr');\n      re82.exec(str82);\n      re82.exec(str83);\n      re82.exec(str84);\n      re82.exec(str86);\n      re82.exec('qg');\n      re82.exec('rzorq');\n      re82.exec('sbez.nwnk');\n      re82.exec(str90);\n      re82.exec('bowrpg');\n      re83.exec(str98);\n      re83.exec('shapgvba sbphf() { [angvir pbqr] }');\n      re62.exec('#Ybtva');\n      re62.exec('#Ybtva_cnffjbeq');\n      re62.exec(str77);\n      re62.exec('#fubhgobkWf');\n      re62.exec('#fubhgobkWfReebe');\n      re62.exec('#fubhgobkWfFhpprff');\n      re62.exec('*');\n      re62.exec(str82);\n      re62.exec(str83);\n      re62.exec(str86);\n      re62.exec('rzorq');\n      re62.exec('sbez.nwnk');\n      re62.exec(str90);\n      re62.exec('bowrpg');\n      re49.exec('pbagrag');\n      re24.exec(str6);\n      /xbadhrebe/.exec(str63);\n      /znp/.exec('jva32');\n      /zbmvyyn/.exec(str63);\n      /zfvr/.exec(str63);\n      /ag\\s5\\.1/.exec(str63);\n      /bcren/.exec(str63);\n      /fnsnev/.exec(str63);\n      /jva/.exec('jva32');\n      /jvaqbjf/.exec(str63);\n    }\n  }\n\n  function run() {\n    for (var i = 0; i < 5; i++) {\n      runBlock0();\n      runBlock1();\n      runBlock2();\n      runBlock3();\n      runBlock4();\n      runBlock5();\n      runBlock6();\n      runBlock7();\n      runBlock8();\n      runBlock9();\n      runBlock10();\n      runBlock11();\n    }\n  }\n\n  this.run = run;\n}\n\n/* run_harness.js */\nvar print = console.log;\n\nfunction Run() {\n  BenchmarkSuite.RunSuites({\n    NotifyStep: ShowProgress,\n    NotifyError: AddError,\n    NotifyResult: AddResult,\n    NotifyScore: AddScore,\n  });\n}\n\nvar harnessErrorCount = 0;\n\nfunction ShowProgress(name) {\n  print(\"PROGRESS\", name);\n}\n\nfunction AddError(name, error) {\n  print(\"ERROR\", name, error);\n  print(error.stack);\n  harnessErrorCount++;\n}\n\nfunction AddResult(name, result) {\n  print(\"RESULT\", name, result);\n}\n\nfunction AddScore(score) {\n  print(\"SCORE\", score);\n}\n\nfunction main() {\n  Run();\n}\n"
  },
  {
    "path": "benches/scripts/v8-benches/richards.js",
    "content": "\"use strict\";\n\"use strip\";\n// Copyright 2012 the V8 project authors. All rights reserved.\n// Redistribution and use in source and binary forms, with or without\n// modification, are permitted provided that the following conditions are\n// met:\n//\n//     * Redistributions of source code must retain the above copyright\n//       notice, this list of conditions and the following disclaimer.\n//     * Redistributions in binary form must reproduce the above\n//       copyright notice, this list of conditions and the following\n//       disclaimer in the documentation and/or other materials provided\n//       with the distribution.\n//     * Neither the name of Google Inc. nor the names of its\n//       contributors may be used to endorse or promote products derived\n//       from this software without specific prior written permission.\n//\n// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS\n// \"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT\n// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR\n// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT\n// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,\n// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT\n// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,\n// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY\n// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n\n// Simple framework for running the benchmark suites and\n// computing a score based on the timing measurements.\n\n\n// A benchmark has a name (string) and a function that will be run to\n// do the performance measurement. The optional setup and tearDown\n// arguments are functions that will be invoked before and after\n// running the benchmark, but the running time of these functions will\n// not be accounted for in the benchmark score.\nfunction Benchmark(name, run, setup, tearDown) {\n  this.name = name;\n  this.run = run;\n  this.Setup = setup ? setup : function () {\n  };\n  this.TearDown = tearDown ? tearDown : function () {\n  };\n}\n\n\n// Benchmark results hold the benchmark and the measured time used to\n// run the benchmark. The benchmark score is computed later once a\n// full benchmark suite has run to completion.\nfunction BenchmarkResult(benchmark, time) {\n  this.benchmark = benchmark;\n  this.time = time;\n}\n\n\n// Automatically convert results to numbers. Used by the geometric\n// mean computation.\nBenchmarkResult.prototype.valueOf = function () {\n  return this.time;\n};\n\n\n// Suites of benchmarks consist of a name and the set of benchmarks in\n// addition to the reference timing that the final score will be based\n// on. This way, all scores are relative to a reference run and higher\n// scores implies better performance.\nfunction BenchmarkSuite(name, reference, benchmarks) {\n  this.name = name;\n  this.reference = reference;\n  this.benchmarks = benchmarks;\n  BenchmarkSuite.suites.push(this);\n}\n\n\n// Keep track of all declared benchmark suites.\nBenchmarkSuite.suites = [];\n\n\n// Scores are not comparable across versions. Bump the version if\n// you're making changes that will affect that scores, e.g. if you add\n// a new benchmark or change an existing one.\nBenchmarkSuite.version = '7';\n\n\n// To make the benchmark results predictable, we replace Math.random\n// with a 100% deterministic alternative.\nMath.random = (function () {\n  var seed = 49734321;\n  return function () {\n    // Robert Jenkins' 32 bit integer hash function.\n    seed = ((seed + 0x7ed55d16) + (seed << 12)) & 0xffffffff;\n    seed = ((seed ^ 0xc761c23c) ^ (seed >>> 19)) & 0xffffffff;\n    seed = ((seed + 0x165667b1) + (seed << 5)) & 0xffffffff;\n    seed = ((seed + 0xd3a2646c) ^ (seed << 9)) & 0xffffffff;\n    seed = ((seed + 0xfd7046c5) + (seed << 3)) & 0xffffffff;\n    seed = ((seed ^ 0xb55a4f09) ^ (seed >>> 16)) & 0xffffffff;\n    return (seed & 0xfffffff) / 0x10000000;\n  };\n})();\n\n\n// Runs all registered benchmark suites and optionally yields between\n// each individual benchmark to avoid running for too long in the\n// context of browsers. Once done, the final score is reported to the\n// runner.\nBenchmarkSuite.RunSuites = function (runner) {\n  var continuation = null;\n  var suites = BenchmarkSuite.suites;\n  var length = suites.length;\n  BenchmarkSuite.scores = [];\n  var index = 0;\n\n  function RunStep() {\n    while (continuation || index < length) {\n      if (continuation) {\n        continuation = continuation();\n      } else {\n        var suite = suites[index++];\n        if (runner.NotifyStart) runner.NotifyStart(suite.name);\n        continuation = suite.RunStep(runner);\n      }\n      if (continuation && typeof window != 'undefined' && window.setTimeout) {\n        window.setTimeout(RunStep, 25);\n        return;\n      }\n    }\n    if (runner.NotifyScore) {\n      var score = BenchmarkSuite.GeometricMean(BenchmarkSuite.scores);\n      var formatted = BenchmarkSuite.FormatScore(100 * score);\n      runner.NotifyScore(formatted);\n    }\n  }\n\n  RunStep();\n};\n\n\n// Counts the total number of registered benchmarks. Useful for\n// showing progress as a percentage.\nBenchmarkSuite.CountBenchmarks = function () {\n  var result = 0;\n  var suites = BenchmarkSuite.suites;\n  for (var i = 0; i < suites.length; i++) {\n    result += suites[i].benchmarks.length;\n  }\n  return result;\n};\n\n\n// Computes the geometric mean of a set of numbers.\nBenchmarkSuite.GeometricMean = function (numbers) {\n  var log = 0;\n  for (var i = 0; i < numbers.length; i++) {\n    log += Math.log(numbers[i]);\n  }\n  return Math.pow(Math.E, log / numbers.length);\n};\n\n\n// Converts a score value to a string with at least three significant\n// digits.\nBenchmarkSuite.FormatScore = function (value) {\n  if (value > 100) {\n    return value.toFixed(0);\n  } else {\n    return value.toPrecision(3);\n  }\n};\n\n// Notifies the runner that we're done running a single benchmark in\n// the benchmark suite. This can be useful to report progress.\nBenchmarkSuite.prototype.NotifyStep = function (result) {\n  this.results.push(result);\n  if (this.runner.NotifyStep) this.runner.NotifyStep(result.benchmark.name);\n};\n\n\n// Notifies the runner that we're done with running a suite and that\n// we have a result which can be reported to the user if needed.\nBenchmarkSuite.prototype.NotifyResult = function () {\n  var mean = BenchmarkSuite.GeometricMean(this.results);\n  var score = this.reference / mean;\n  BenchmarkSuite.scores.push(score);\n  if (this.runner.NotifyResult) {\n    var formatted = BenchmarkSuite.FormatScore(100 * score);\n    this.runner.NotifyResult(this.name, formatted);\n  }\n};\n\n\n// Notifies the runner that running a benchmark resulted in an error.\nBenchmarkSuite.prototype.NotifyError = function (error) {\n  if (this.runner.NotifyError) {\n    this.runner.NotifyError(this.name, error);\n  }\n  if (this.runner.NotifyStep) {\n    this.runner.NotifyStep(this.name);\n  }\n};\n\n\n// Runs a single benchmark for at least a second and computes the\n// average time it takes to run a single iteration.\nBenchmarkSuite.prototype.RunSingleBenchmark = function (benchmark, data) {\n  function Measure(data) {\n    var elapsed = 0;\n    var start = new Date();\n    for (var n = 0; elapsed < 1000; n++) {\n      benchmark.run();\n      elapsed = new Date() - start;\n    }\n    if (data != null) {\n      data.runs += n;\n      data.elapsed += elapsed;\n    }\n  }\n\n  if (data == null) {\n    // Measure the benchmark once for warm up and throw the result\n    // away. Return a fresh data object.\n    Measure(null);\n    return {runs: 0, elapsed: 0};\n  } else {\n    Measure(data);\n    // If we've run too few iterations, we continue for another second.\n    if (data.runs < 32) return data;\n    var usec = (data.elapsed * 1000) / data.runs;\n    this.NotifyStep(new BenchmarkResult(benchmark, usec));\n    return null;\n  }\n};\n\n\n// This function starts running a suite, but stops between each\n// individual benchmark in the suite and returns a continuation\n// function which can be invoked to run the next benchmark. Once the\n// last benchmark has been executed, null is returned.\nBenchmarkSuite.prototype.RunStep = function (runner) {\n  this.results = [];\n  this.runner = runner;\n  var length = this.benchmarks.length;\n  var index = 0;\n  var suite = this;\n  var data;\n\n  // Run the setup, the actual benchmark, and the tear down in three\n  // separate steps to allow the framework to yield between any of the\n  // steps.\n\n  function RunNextSetup() {\n    if (index < length) {\n      try {\n        suite.benchmarks[index].Setup();\n      } catch (e) {\n        suite.NotifyError(e);\n        return null;\n      }\n      return RunNextBenchmark;\n    }\n    suite.NotifyResult();\n    return null;\n  }\n\n  function RunNextBenchmark() {\n    try {\n      data = suite.RunSingleBenchmark(suite.benchmarks[index], data);\n    } catch (e) {\n      suite.NotifyError(e);\n      return null;\n    }\n    // If data is null, we're done with this benchmark.\n    return (data == null) ? RunNextTearDown : RunNextBenchmark();\n  }\n\n  function RunNextTearDown() {\n    try {\n      suite.benchmarks[index++].TearDown();\n    } catch (e) {\n      suite.NotifyError(e);\n      return null;\n    }\n    return RunNextSetup;\n  }\n\n  // Start out running the setup.\n  return RunNextSetup();\n};\n\n\n// Copyright 2006-2008 the V8 project authors. All rights reserved.\n// Redistribution and use in source and binary forms, with or without\n// modification, are permitted provided that the following conditions are\n// met:\n//\n//     * Redistributions of source code must retain the above copyright\n//       notice, this list of conditions and the following disclaimer.\n//     * Redistributions in binary form must reproduce the above\n//       copyright notice, this list of conditions and the following\n//       disclaimer in the documentation and/or other materials provided\n//       with the distribution.\n//     * Neither the name of Google Inc. nor the names of its\n//       contributors may be used to endorse or promote products derived\n//       from this software without specific prior written permission.\n//\n// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS\n// \"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT\n// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR\n// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT\n// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,\n// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT\n// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,\n// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY\n// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n\n\n// This is a JavaScript implementation of the Richards\n// benchmark from:\n//\n//    http://www.cl.cam.ac.uk/~mr10/Bench.html\n//\n// The benchmark was originally implemented in BCPL by\n// Martin Richards.\n\n\n/**\n * The Richards benchmark simulates the task dispatcher of an\n * operating system.\n **/\nfunction runRichards() {\n  var scheduler = new Scheduler();\n  scheduler.addIdleTask(ID_IDLE, 0, null, COUNT);\n\n  var queue = new Packet(null, ID_WORKER, KIND_WORK);\n  queue = new Packet(queue, ID_WORKER, KIND_WORK);\n  scheduler.addWorkerTask(ID_WORKER, 1000, queue);\n\n  queue = new Packet(null, ID_DEVICE_A, KIND_DEVICE);\n  queue = new Packet(queue, ID_DEVICE_A, KIND_DEVICE);\n  queue = new Packet(queue, ID_DEVICE_A, KIND_DEVICE);\n  scheduler.addHandlerTask(ID_HANDLER_A, 2000, queue);\n\n  queue = new Packet(null, ID_DEVICE_B, KIND_DEVICE);\n  queue = new Packet(queue, ID_DEVICE_B, KIND_DEVICE);\n  queue = new Packet(queue, ID_DEVICE_B, KIND_DEVICE);\n  scheduler.addHandlerTask(ID_HANDLER_B, 3000, queue);\n\n  scheduler.addDeviceTask(ID_DEVICE_A, 4000, null);\n\n  scheduler.addDeviceTask(ID_DEVICE_B, 5000, null);\n\n  scheduler.schedule();\n\n  if (scheduler.queueCount != EXPECTED_QUEUE_COUNT ||\n    scheduler.holdCount != EXPECTED_HOLD_COUNT) {\n    var msg =\n      \"Error during execution: queueCount = \" + scheduler.queueCount +\n      \", holdCount = \" + scheduler.holdCount + \".\";\n    throw new Error(msg);\n  }\n}\n\nvar COUNT = 1000;\n\n/**\n * These two constants specify how many times a packet is queued and\n * how many times a task is put on hold in a correct run of richards.\n * They don't have any meaning a such but are characteristic of a\n * correct run so if the actual queue or hold count is different from\n * the expected there must be a bug in the implementation.\n **/\nvar EXPECTED_QUEUE_COUNT = 2322;\nvar EXPECTED_HOLD_COUNT = 928;\n\n\n/**\n * A scheduler can be used to schedule a set of tasks based on their relative\n * priorities.  Scheduling is done by maintaining a list of task control blocks\n * which holds tasks and the data queue they are processing.\n * @constructor\n */\nfunction Scheduler() {\n  this.queueCount = 0;\n  this.holdCount = 0;\n  this.blocks = new Array(NUMBER_OF_IDS);\n  this.list = null;\n  this.currentTcb = null;\n  this.currentId = null;\n}\n\nvar ID_IDLE = 0;\nvar ID_WORKER = 1;\nvar ID_HANDLER_A = 2;\nvar ID_HANDLER_B = 3;\nvar ID_DEVICE_A = 4;\nvar ID_DEVICE_B = 5;\nvar NUMBER_OF_IDS = 6;\n\nvar KIND_DEVICE = 0;\nvar KIND_WORK = 1;\n\n/**\n * Add an idle task to this scheduler.\n * @param {int} id the identity of the task\n * @param {int} priority the task's priority\n * @param {Packet} queue the queue of work to be processed by the task\n * @param {int} count the number of times to schedule the task\n */\nScheduler.prototype.addIdleTask = function (id, priority, queue, count) {\n  this.addRunningTask(id, priority, queue, new IdleTask(this, 1, count));\n};\n\n/**\n * Add a work task to this scheduler.\n * @param {int} id the identity of the task\n * @param {int} priority the task's priority\n * @param {Packet} queue the queue of work to be processed by the task\n */\nScheduler.prototype.addWorkerTask = function (id, priority, queue) {\n  this.addTask(id, priority, queue, new WorkerTask(this, ID_HANDLER_A, 0));\n};\n\n/**\n * Add a handler task to this scheduler.\n * @param {int} id the identity of the task\n * @param {int} priority the task's priority\n * @param {Packet} queue the queue of work to be processed by the task\n */\nScheduler.prototype.addHandlerTask = function (id, priority, queue) {\n  this.addTask(id, priority, queue, new HandlerTask(this));\n};\n\n/**\n * Add a handler task to this scheduler.\n * @param {int} id the identity of the task\n * @param {int} priority the task's priority\n * @param {Packet} queue the queue of work to be processed by the task\n */\nScheduler.prototype.addDeviceTask = function (id, priority, queue) {\n  this.addTask(id, priority, queue, new DeviceTask(this));\n};\n\n/**\n * Add the specified task and mark it as running.\n * @param {int} id the identity of the task\n * @param {int} priority the task's priority\n * @param {Packet} queue the queue of work to be processed by the task\n * @param {Task} task the task to add\n */\nScheduler.prototype.addRunningTask = function (id, priority, queue, task) {\n  this.addTask(id, priority, queue, task);\n  this.currentTcb.setRunning();\n};\n\n/**\n * Add the specified task to this scheduler.\n * @param {int} id the identity of the task\n * @param {int} priority the task's priority\n * @param {Packet} queue the queue of work to be processed by the task\n * @param {Task} task the task to add\n */\nScheduler.prototype.addTask = function (id, priority, queue, task) {\n  this.currentTcb = new TaskControlBlock(this.list, id, priority, queue, task);\n  this.list = this.currentTcb;\n  this.blocks[id] = this.currentTcb;\n};\n\n/**\n * Execute the tasks managed by this scheduler.\n */\nScheduler.prototype.schedule = function () {\n  this.currentTcb = this.list;\n  while (this.currentTcb != null) {\n    if (this.currentTcb.isHeldOrSuspended()) {\n      this.currentTcb = this.currentTcb.link;\n    } else {\n      this.currentId = this.currentTcb.id;\n      this.currentTcb = this.currentTcb.run();\n    }\n  }\n};\n\n/**\n * Release a task that is currently blocked and return the next block to run.\n * @param {int} id the id of the task to suspend\n */\nScheduler.prototype.release = function (id) {\n  var tcb = this.blocks[id];\n  if (tcb == null) return tcb;\n  tcb.markAsNotHeld();\n  if (tcb.priority > this.currentTcb.priority) {\n    return tcb;\n  } else {\n    return this.currentTcb;\n  }\n};\n\n/**\n * Block the currently executing task and return the next task control block\n * to run.  The blocked task will not be made runnable until it is explicitly\n * released, even if new work is added to it.\n */\nScheduler.prototype.holdCurrent = function () {\n  this.holdCount++;\n  this.currentTcb.markAsHeld();\n  return this.currentTcb.link;\n};\n\n/**\n * Suspend the currently executing task and return the next task control block\n * to run.  If new work is added to the suspended task it will be made runnable.\n */\nScheduler.prototype.suspendCurrent = function () {\n  this.currentTcb.markAsSuspended();\n  return this.currentTcb;\n};\n\n/**\n * Add the specified packet to the end of the worklist used by the task\n * associated with the packet and make the task runnable if it is currently\n * suspended.\n * @param {Packet} packet the packet to add\n */\nScheduler.prototype.queue = function (packet) {\n  var t = this.blocks[packet.id];\n  if (t == null) return t;\n  this.queueCount++;\n  packet.link = null;\n  packet.id = this.currentId;\n  return t.checkPriorityAdd(this.currentTcb, packet);\n};\n\n/**\n * A task control block manages a task and the queue of work packages associated\n * with it.\n * @param {TaskControlBlock} link the preceding block in the linked block list\n * @param {int} id the id of this block\n * @param {int} priority the priority of this block\n * @param {Packet} queue the queue of packages to be processed by the task\n * @param {Task} task the task\n * @constructor\n */\nfunction TaskControlBlock(link, id, priority, queue, task) {\n  this.link = link;\n  this.id = id;\n  this.priority = priority;\n  this.queue = queue;\n  this.task = task;\n  if (queue == null) {\n    this.state = STATE_SUSPENDED;\n  } else {\n    this.state = STATE_SUSPENDED_RUNNABLE;\n  }\n}\n\n/**\n * The task is running and is currently scheduled.\n */\nvar STATE_RUNNING = 0;\n\n/**\n * The task has packets left to process.\n */\nvar STATE_RUNNABLE = 1;\n\n/**\n * The task is not currently running.  The task is not blocked as such and may\n * be started by the scheduler.\n */\nvar STATE_SUSPENDED = 2;\n\n/**\n * The task is blocked and cannot be run until it is explicitly released.\n */\nvar STATE_HELD = 4;\n\nvar STATE_SUSPENDED_RUNNABLE = STATE_SUSPENDED | STATE_RUNNABLE;\nvar STATE_NOT_HELD = ~STATE_HELD;\n\nTaskControlBlock.prototype.setRunning = function () {\n  this.state = STATE_RUNNING;\n};\n\nTaskControlBlock.prototype.markAsNotHeld = function () {\n  this.state = this.state & STATE_NOT_HELD;\n};\n\nTaskControlBlock.prototype.markAsHeld = function () {\n  this.state = this.state | STATE_HELD;\n};\n\nTaskControlBlock.prototype.isHeldOrSuspended = function () {\n  return (this.state & STATE_HELD) != 0 || (this.state == STATE_SUSPENDED);\n};\n\nTaskControlBlock.prototype.markAsSuspended = function () {\n  this.state = this.state | STATE_SUSPENDED;\n};\n\nTaskControlBlock.prototype.markAsRunnable = function () {\n  this.state = this.state | STATE_RUNNABLE;\n};\n\n/**\n * Runs this task, if it is ready to be run, and returns the next task to run.\n */\nTaskControlBlock.prototype.run = function () {\n  var packet;\n  if (this.state == STATE_SUSPENDED_RUNNABLE) {\n    packet = this.queue;\n    this.queue = packet.link;\n    if (this.queue == null) {\n      this.state = STATE_RUNNING;\n    } else {\n      this.state = STATE_RUNNABLE;\n    }\n  } else {\n    packet = null;\n  }\n  return this.task.run(packet);\n};\n\n/**\n * Adds a packet to the worklist of this block's task, marks this as runnable if\n * necessary, and returns the next runnable object to run (the one\n * with the highest priority).\n */\nTaskControlBlock.prototype.checkPriorityAdd = function (task, packet) {\n  if (this.queue == null) {\n    this.queue = packet;\n    this.markAsRunnable();\n    if (this.priority > task.priority) return this;\n  } else {\n    this.queue = packet.addTo(this.queue);\n  }\n  return task;\n};\n\nTaskControlBlock.prototype.toString = function () {\n  return \"tcb { \" + this.task + \"@\" + this.state + \" }\";\n};\n\n/**\n * An idle task doesn't do any work itself but cycles control between the two\n * device tasks.\n * @param {Scheduler} scheduler the scheduler that manages this task\n * @param {int} v1 a seed value that controls how the device tasks are scheduled\n * @param {int} count the number of times this task should be scheduled\n * @constructor\n */\nfunction IdleTask(scheduler, v1, count) {\n  this.scheduler = scheduler;\n  this.v1 = v1;\n  this.count = count;\n}\n\nIdleTask.prototype.run = function (packet) {\n  this.count--;\n  if (this.count == 0) return this.scheduler.holdCurrent();\n  if ((this.v1 & 1) == 0) {\n    this.v1 = this.v1 >> 1;\n    return this.scheduler.release(ID_DEVICE_A);\n  } else {\n    this.v1 = (this.v1 >> 1) ^ 0xD008;\n    return this.scheduler.release(ID_DEVICE_B);\n  }\n};\n\nIdleTask.prototype.toString = function () {\n  return \"IdleTask\";\n};\n\n/**\n * A task that suspends itself after each time it has been run to simulate\n * waiting for data from an external device.\n * @param {Scheduler} scheduler the scheduler that manages this task\n * @constructor\n */\nfunction DeviceTask(scheduler) {\n  this.scheduler = scheduler;\n  this.v1 = null;\n}\n\nDeviceTask.prototype.run = function (packet) {\n  if (packet == null) {\n    if (this.v1 == null) return this.scheduler.suspendCurrent();\n    var v = this.v1;\n    this.v1 = null;\n    return this.scheduler.queue(v);\n  } else {\n    this.v1 = packet;\n    return this.scheduler.holdCurrent();\n  }\n};\n\nDeviceTask.prototype.toString = function () {\n  return \"DeviceTask\";\n};\n\n/**\n * A task that manipulates work packets.\n * @param {Scheduler} scheduler the scheduler that manages this task\n * @param {int} v1 a seed used to specify how work packets are manipulated\n * @param {int} v2 another seed used to specify how work packets are manipulated\n * @constructor\n */\nfunction WorkerTask(scheduler, v1, v2) {\n  this.scheduler = scheduler;\n  this.v1 = v1;\n  this.v2 = v2;\n}\n\nWorkerTask.prototype.run = function (packet) {\n  if (packet == null) {\n    return this.scheduler.suspendCurrent();\n  } else {\n    if (this.v1 == ID_HANDLER_A) {\n      this.v1 = ID_HANDLER_B;\n    } else {\n      this.v1 = ID_HANDLER_A;\n    }\n    packet.id = this.v1;\n    packet.a1 = 0;\n    for (var i = 0; i < DATA_SIZE; i++) {\n      this.v2++;\n      if (this.v2 > 26) this.v2 = 1;\n      packet.a2[i] = this.v2;\n    }\n    return this.scheduler.queue(packet);\n  }\n};\n\nWorkerTask.prototype.toString = function () {\n  return \"WorkerTask\";\n};\n\n/**\n * A task that manipulates work packets and then suspends itself.\n * @param {Scheduler} scheduler the scheduler that manages this task\n * @constructor\n */\nfunction HandlerTask(scheduler) {\n  this.scheduler = scheduler;\n  this.v1 = null;\n  this.v2 = null;\n}\n\nHandlerTask.prototype.run = function (packet) {\n  if (packet != null) {\n    if (packet.kind == KIND_WORK) {\n      this.v1 = packet.addTo(this.v1);\n    } else {\n      this.v2 = packet.addTo(this.v2);\n    }\n  }\n  if (this.v1 != null) {\n    var count = this.v1.a1;\n    var v;\n    if (count < DATA_SIZE) {\n      if (this.v2 != null) {\n        v = this.v2;\n        this.v2 = this.v2.link;\n        v.a1 = this.v1.a2[count];\n        this.v1.a1 = count + 1;\n        return this.scheduler.queue(v);\n      }\n    } else {\n      v = this.v1;\n      this.v1 = this.v1.link;\n      return this.scheduler.queue(v);\n    }\n  }\n  return this.scheduler.suspendCurrent();\n};\n\nHandlerTask.prototype.toString = function () {\n  return \"HandlerTask\";\n};\n\n/* --- *\n * P a c k e t\n * --- */\n\nvar DATA_SIZE = 4;\n\n/**\n * A simple package of data that is manipulated by the tasks.  The exact layout\n * of the payload data carried by a packet is not importaint, and neither is the\n * nature of the work performed on packets by the tasks.\n *\n * Besides carrying data, packets form linked lists and are hence used both as\n * data and worklists.\n * @param {Packet} link the tail of the linked list of packets\n * @param {int} id an ID for this packet\n * @param {int} kind the type of this packet\n * @constructor\n */\nfunction Packet(link, id, kind) {\n  this.link = link;\n  this.id = id;\n  this.kind = kind;\n  this.a1 = 0;\n  this.a2 = new Array(DATA_SIZE);\n}\n\n/**\n * Add this packet to the end of a worklist, and return the worklist.\n * @param {Packet} queue the worklist to add this packet to\n */\nPacket.prototype.addTo = function (queue) {\n  this.link = null;\n  if (queue == null) return this;\n  var peek, next = queue;\n  while ((peek = next.link) != null)\n    next = peek;\n  next.link = this;\n  return queue;\n};\n\nPacket.prototype.toString = function () {\n  return \"Packet\";\n};\n\nvar Richards = new BenchmarkSuite('Richards', 35302, [\n  new Benchmark(\"Richards\", runRichards)\n]);\n\n/* run_harness.js */\nvar print = console.log;\n\nfunction Run() {\n  BenchmarkSuite.RunSuites({\n    NotifyStep: ShowProgress,\n    NotifyError: AddError,\n    NotifyResult: AddResult,\n    NotifyScore: AddScore,\n  });\n}\n\nvar harnessErrorCount = 0;\n\nfunction ShowProgress(name) {\n  print(\"PROGRESS\", name);\n}\n\nfunction AddError(name, error) {\n  print(\"ERROR\", name, error);\n  print(error.stack);\n  harnessErrorCount++;\n}\n\nfunction AddResult(name, result) {\n  print(\"RESULT\", name, result);\n}\n\nfunction AddScore(score) {\n  print(\"SCORE\", score);\n}\n\nfunction main() {\n  Run();\n}\n"
  },
  {
    "path": "benches/scripts/v8-benches/splay.js",
    "content": "\"use strict\";\n\"use strip\";\n// Copyright 2012 the V8 project authors. All rights reserved.\n// Redistribution and use in source and binary forms, with or without\n// modification, are permitted provided that the following conditions are\n// met:\n//\n//     * Redistributions of source code must retain the above copyright\n//       notice, this list of conditions and the following disclaimer.\n//     * Redistributions in binary form must reproduce the above\n//       copyright notice, this list of conditions and the following\n//       disclaimer in the documentation and/or other materials provided\n//       with the distribution.\n//     * Neither the name of Google Inc. nor the names of its\n//       contributors may be used to endorse or promote products derived\n//       from this software without specific prior written permission.\n//\n// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS\n// \"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT\n// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR\n// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT\n// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,\n// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT\n// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,\n// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY\n// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n\n// Simple framework for running the benchmark suites and\n// computing a score based on the timing measurements.\n\n\n// A benchmark has a name (string) and a function that will be run to\n// do the performance measurement. The optional setup and tearDown\n// arguments are functions that will be invoked before and after\n// running the benchmark, but the running time of these functions will\n// not be accounted for in the benchmark score.\nfunction Benchmark(name, run, setup, tearDown) {\n  this.name = name;\n  this.run = run;\n  this.Setup = setup ? setup : function () {\n  };\n  this.TearDown = tearDown ? tearDown : function () {\n  };\n}\n\n\n// Benchmark results hold the benchmark and the measured time used to\n// run the benchmark. The benchmark score is computed later once a\n// full benchmark suite has run to completion.\nfunction BenchmarkResult(benchmark, time) {\n  this.benchmark = benchmark;\n  this.time = time;\n}\n\n\n// Automatically convert results to numbers. Used by the geometric\n// mean computation.\nBenchmarkResult.prototype.valueOf = function () {\n  return this.time;\n};\n\n\n// Suites of benchmarks consist of a name and the set of benchmarks in\n// addition to the reference timing that the final score will be based\n// on. This way, all scores are relative to a reference run and higher\n// scores implies better performance.\nfunction BenchmarkSuite(name, reference, benchmarks) {\n  this.name = name;\n  this.reference = reference;\n  this.benchmarks = benchmarks;\n  BenchmarkSuite.suites.push(this);\n}\n\n\n// Keep track of all declared benchmark suites.\nBenchmarkSuite.suites = [];\n\n\n// Scores are not comparable across versions. Bump the version if\n// you're making changes that will affect that scores, e.g. if you add\n// a new benchmark or change an existing one.\nBenchmarkSuite.version = '7';\n\n\n// To make the benchmark results predictable, we replace Math.random\n// with a 100% deterministic alternative.\nMath.random = (function () {\n  var seed = 49734321;\n  return function () {\n    // Robert Jenkins' 32 bit integer hash function.\n    seed = ((seed + 0x7ed55d16) + (seed << 12)) & 0xffffffff;\n    seed = ((seed ^ 0xc761c23c) ^ (seed >>> 19)) & 0xffffffff;\n    seed = ((seed + 0x165667b1) + (seed << 5)) & 0xffffffff;\n    seed = ((seed + 0xd3a2646c) ^ (seed << 9)) & 0xffffffff;\n    seed = ((seed + 0xfd7046c5) + (seed << 3)) & 0xffffffff;\n    seed = ((seed ^ 0xb55a4f09) ^ (seed >>> 16)) & 0xffffffff;\n    return (seed & 0xfffffff) / 0x10000000;\n  };\n})();\n\n\n// Runs all registered benchmark suites and optionally yields between\n// each individual benchmark to avoid running for too long in the\n// context of browsers. Once done, the final score is reported to the\n// runner.\nBenchmarkSuite.RunSuites = function (runner) {\n  var continuation = null;\n  var suites = BenchmarkSuite.suites;\n  var length = suites.length;\n  BenchmarkSuite.scores = [];\n  var index = 0;\n\n  function RunStep() {\n    while (continuation || index < length) {\n      if (continuation) {\n        continuation = continuation();\n      } else {\n        var suite = suites[index++];\n        if (runner.NotifyStart) runner.NotifyStart(suite.name);\n        continuation = suite.RunStep(runner);\n      }\n      if (continuation && typeof window != 'undefined' && window.setTimeout) {\n        window.setTimeout(RunStep, 25);\n        return;\n      }\n    }\n    if (runner.NotifyScore) {\n      var score = BenchmarkSuite.GeometricMean(BenchmarkSuite.scores);\n      var formatted = BenchmarkSuite.FormatScore(100 * score);\n      runner.NotifyScore(formatted);\n    }\n  }\n\n  RunStep();\n};\n\n\n// Counts the total number of registered benchmarks. Useful for\n// showing progress as a percentage.\nBenchmarkSuite.CountBenchmarks = function () {\n  var result = 0;\n  var suites = BenchmarkSuite.suites;\n  for (var i = 0; i < suites.length; i++) {\n    result += suites[i].benchmarks.length;\n  }\n  return result;\n};\n\n\n// Computes the geometric mean of a set of numbers.\nBenchmarkSuite.GeometricMean = function (numbers) {\n  var log = 0;\n  for (var i = 0; i < numbers.length; i++) {\n    log += Math.log(numbers[i]);\n  }\n  return Math.pow(Math.E, log / numbers.length);\n};\n\n\n// Converts a score value to a string with at least three significant\n// digits.\nBenchmarkSuite.FormatScore = function (value) {\n  if (value > 100) {\n    return value.toFixed(0);\n  } else {\n    return value.toPrecision(3);\n  }\n};\n\n// Notifies the runner that we're done running a single benchmark in\n// the benchmark suite. This can be useful to report progress.\nBenchmarkSuite.prototype.NotifyStep = function (result) {\n  this.results.push(result);\n  if (this.runner.NotifyStep) this.runner.NotifyStep(result.benchmark.name);\n};\n\n\n// Notifies the runner that we're done with running a suite and that\n// we have a result which can be reported to the user if needed.\nBenchmarkSuite.prototype.NotifyResult = function () {\n  var mean = BenchmarkSuite.GeometricMean(this.results);\n  var score = this.reference / mean;\n  BenchmarkSuite.scores.push(score);\n  if (this.runner.NotifyResult) {\n    var formatted = BenchmarkSuite.FormatScore(100 * score);\n    this.runner.NotifyResult(this.name, formatted);\n  }\n};\n\n\n// Notifies the runner that running a benchmark resulted in an error.\nBenchmarkSuite.prototype.NotifyError = function (error) {\n  if (this.runner.NotifyError) {\n    this.runner.NotifyError(this.name, error);\n  }\n  if (this.runner.NotifyStep) {\n    this.runner.NotifyStep(this.name);\n  }\n};\n\n\n// Runs a single benchmark for at least a second and computes the\n// average time it takes to run a single iteration.\nBenchmarkSuite.prototype.RunSingleBenchmark = function (benchmark, data) {\n  function Measure(data) {\n    var elapsed = 0;\n    var start = new Date();\n    for (var n = 0; elapsed < 1000; n++) {\n      benchmark.run();\n      elapsed = new Date() - start;\n    }\n    if (data != null) {\n      data.runs += n;\n      data.elapsed += elapsed;\n    }\n  }\n\n  if (data == null) {\n    // Measure the benchmark once for warm up and throw the result\n    // away. Return a fresh data object.\n    Measure(null);\n    return {runs: 0, elapsed: 0};\n  } else {\n    Measure(data);\n    // If we've run too few iterations, we continue for another second.\n    if (data.runs < 32) return data;\n    var usec = (data.elapsed * 1000) / data.runs;\n    this.NotifyStep(new BenchmarkResult(benchmark, usec));\n    return null;\n  }\n};\n\n\n// This function starts running a suite, but stops between each\n// individual benchmark in the suite and returns a continuation\n// function which can be invoked to run the next benchmark. Once the\n// last benchmark has been executed, null is returned.\nBenchmarkSuite.prototype.RunStep = function (runner) {\n  this.results = [];\n  this.runner = runner;\n  var length = this.benchmarks.length;\n  var index = 0;\n  var suite = this;\n  var data;\n\n  // Run the setup, the actual benchmark, and the tear down in three\n  // separate steps to allow the framework to yield between any of the\n  // steps.\n\n  function RunNextSetup() {\n    if (index < length) {\n      try {\n        suite.benchmarks[index].Setup();\n      } catch (e) {\n        suite.NotifyError(e);\n        return null;\n      }\n      return RunNextBenchmark;\n    }\n    suite.NotifyResult();\n    return null;\n  }\n\n  function RunNextBenchmark() {\n    try {\n      data = suite.RunSingleBenchmark(suite.benchmarks[index], data);\n    } catch (e) {\n      suite.NotifyError(e);\n      return null;\n    }\n    // If data is null, we're done with this benchmark.\n    return (data == null) ? RunNextTearDown : RunNextBenchmark();\n  }\n\n  function RunNextTearDown() {\n    try {\n      suite.benchmarks[index++].TearDown();\n    } catch (e) {\n      suite.NotifyError(e);\n      return null;\n    }\n    return RunNextSetup;\n  }\n\n  // Start out running the setup.\n  return RunNextSetup();\n};\n\n// Copyright 2009 the V8 project authors. All rights reserved.\n// Redistribution and use in source and binary forms, with or without\n// modification, are permitted provided that the following conditions are\n// met:\n//\n//     * Redistributions of source code must retain the above copyright\n//       notice, this list of conditions and the following disclaimer.\n//     * Redistributions in binary form must reproduce the above\n//       copyright notice, this list of conditions and the following\n//       disclaimer in the documentation and/or other materials provided\n//       with the distribution.\n//     * Neither the name of Google Inc. nor the names of its\n//       contributors may be used to endorse or promote products derived\n//       from this software without specific prior written permission.\n//\n// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS\n// \"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT\n// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR\n// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT\n// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,\n// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT\n// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,\n// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY\n// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n\n// This benchmark is based on a JavaScript log processing module used\n// by the V8 profiler to generate execution time profiles for runs of\n// JavaScript applications, and it effectively measures how fast the\n// JavaScript engine is at allocating nodes and reclaiming the memory\n// used for old nodes. Because of the way splay trees work, the engine\n// also has to deal with a lot of changes to the large tree object\n// graph.\n\n// Configuration.\nvar kSplayTreeSize = 8000;\nvar kSplayTreeModifications = 80;\nvar kSplayTreePayloadDepth = 5;\n\nvar splayTree = null;\n\n\nfunction GeneratePayloadTree(depth, tag) {\n  if (depth == 0) {\n    return {\n      array: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9],\n      string: 'String for key ' + tag + ' in leaf node'\n    };\n  } else {\n    return {\n      left: GeneratePayloadTree(depth - 1, tag),\n      right: GeneratePayloadTree(depth - 1, tag)\n    };\n  }\n}\n\n\nfunction GenerateKey() {\n  // The benchmark framework guarantees that Math.random is\n  // deterministic; see base.js.\n  return Math.random();\n}\n\n\nfunction InsertNewNode() {\n  // Insert new node with a unique key.\n  var key;\n  do {\n    key = GenerateKey();\n  } while (splayTree.find(key) != null);\n  var payload = GeneratePayloadTree(kSplayTreePayloadDepth, String(key));\n  splayTree.insert(key, payload);\n  return key;\n}\n\n\nfunction SplaySetup() {\n  splayTree = new SplayTree();\n  for (var i = 0; i < kSplayTreeSize; i++) InsertNewNode();\n}\n\n\nfunction SplayTearDown() {\n  // Allow the garbage collector to reclaim the memory\n  // used by the splay tree no matter how we exit the\n  // tear down function.\n  var keys = splayTree.exportKeys();\n  splayTree = null;\n\n  // Verify that the splay tree has the right size.\n  var length = keys.length;\n  if (length != kSplayTreeSize) {\n    throw new Error(\"Splay tree has wrong size\");\n  }\n\n  // Verify that the splay tree has sorted, unique keys.\n  for (var i = 0; i < length - 1; i++) {\n    if (keys[i] >= keys[i + 1]) {\n      throw new Error(\"Splay tree not sorted\");\n    }\n  }\n}\n\n\nfunction SplayRun() {\n  // Replace a few nodes in the splay tree.\n  for (var i = 0; i < kSplayTreeModifications; i++) {\n    var key = InsertNewNode();\n    var greatest = splayTree.findGreatestLessThan(key);\n    if (greatest == null) splayTree.remove(key);\n    else splayTree.remove(greatest.key);\n  }\n}\n\n\n/**\n * Constructs a Splay tree.  A splay tree is a self-balancing binary\n * search tree with the additional property that recently accessed\n * elements are quick to access again. It performs basic operations\n * such as insertion, look-up and removal in O(log(n)) amortized time.\n *\n * @constructor\n */\nfunction SplayTree() {\n}\n\n\n/**\n * Pointer to the root node of the tree.\n *\n * @type {SplayTree.Node}\n * @private\n */\nSplayTree.prototype.root_ = null;\n\n\n/**\n * @return {boolean} Whether the tree is empty.\n */\nSplayTree.prototype.isEmpty = function () {\n  return !this.root_;\n};\n\n\n/**\n * Inserts a node into the tree with the specified key and value if\n * the tree does not already contain a node with the specified key. If\n * the value is inserted, it becomes the root of the tree.\n *\n * @param {number} key Key to insert into the tree.\n * @param {*} value Value to insert into the tree.\n */\nSplayTree.prototype.insert = function (key, value) {\n  if (this.isEmpty()) {\n    this.root_ = new SplayTree.Node(key, value);\n    return;\n  }\n  // Splay on the key to move the last node on the search path for\n  // the key to the root of the tree.\n  this.splay_(key);\n  if (this.root_.key == key) {\n    return;\n  }\n  var node = new SplayTree.Node(key, value);\n  if (key > this.root_.key) {\n    node.left = this.root_;\n    node.right = this.root_.right;\n    this.root_.right = null;\n  } else {\n    node.right = this.root_;\n    node.left = this.root_.left;\n    this.root_.left = null;\n  }\n  this.root_ = node;\n};\n\n\n/**\n * Removes a node with the specified key from the tree if the tree\n * contains a node with this key. The removed node is returned. If the\n * key is not found, an exception is thrown.\n *\n * @param {number} key Key to find and remove from the tree.\n * @return {SplayTree.Node} The removed node.\n */\nSplayTree.prototype.remove = function (key) {\n  if (this.isEmpty()) {\n    throw Error('Key not found: ' + key);\n  }\n  this.splay_(key);\n  if (this.root_.key != key) {\n    throw Error('Key not found: ' + key);\n  }\n  var removed = this.root_;\n  if (!this.root_.left) {\n    this.root_ = this.root_.right;\n  } else {\n    var right = this.root_.right;\n    this.root_ = this.root_.left;\n    // Splay to make sure that the new root has an empty right child.\n    this.splay_(key);\n    // Insert the original right child as the right child of the new\n    // root.\n    this.root_.right = right;\n  }\n  return removed;\n};\n\n\n/**\n * Returns the node having the specified key or null if the tree doesn't contain\n * a node with the specified key.\n *\n * @param {number} key Key to find in the tree.\n * @return {SplayTree.Node} Node having the specified key.\n */\nSplayTree.prototype.find = function (key) {\n  if (this.isEmpty()) {\n    return null;\n  }\n  this.splay_(key);\n  return this.root_.key == key ? this.root_ : null;\n};\n\n\n/**\n * @return {SplayTree.Node} Node having the maximum key value.\n */\nSplayTree.prototype.findMax = function (opt_startNode) {\n  if (this.isEmpty()) {\n    return null;\n  }\n  var current = opt_startNode || this.root_;\n  while (current.right) {\n    current = current.right;\n  }\n  return current;\n};\n\n\n/**\n * @return {SplayTree.Node} Node having the maximum key value that\n *     is less than the specified key value.\n */\nSplayTree.prototype.findGreatestLessThan = function (key) {\n  if (this.isEmpty()) {\n    return null;\n  }\n  // Splay on the key to move the node with the given key or the last\n  // node on the search path to the top of the tree.\n  this.splay_(key);\n  // Now the result is either the root node or the greatest node in\n  // the left subtree.\n  if (this.root_.key < key) {\n    return this.root_;\n  } else if (this.root_.left) {\n    return this.findMax(this.root_.left);\n  } else {\n    return null;\n  }\n};\n\n\n/**\n * @return {Array<*>} An array containing all the keys of tree's nodes.\n */\nSplayTree.prototype.exportKeys = function () {\n  var result = [];\n  if (!this.isEmpty()) {\n    this.root_.traverse_(function (node) {\n      result.push(node.key);\n    });\n  }\n  return result;\n};\n\n\n/**\n * Perform the splay operation for the given key. Moves the node with\n * the given key to the top of the tree.  If no node has the given\n * key, the last node on the search path is moved to the top of the\n * tree. This is the simplified top-down splaying algorithm from:\n * \"Self-adjusting Binary Search Trees\" by Sleator and Tarjan\n *\n * @param {number} key Key to splay the tree on.\n * @private\n */\nSplayTree.prototype.splay_ = function (key) {\n  if (this.isEmpty()) {\n    return;\n  }\n  // Create a dummy node.  The use of the dummy node is a bit\n  // counter-intuitive: The right child of the dummy node will hold\n  // the L tree of the algorithm.  The left child of the dummy node\n  // will hold the R tree of the algorithm.  Using a dummy node, left\n  // and right will always be nodes and we avoid special cases.\n  var dummy, left, right;\n  dummy = left = right = new SplayTree.Node(null, null);\n  var current = this.root_;\n  while (true) {\n    if (key < current.key) {\n      if (!current.left) {\n        break;\n      }\n      if (key < current.left.key) {\n        // Rotate right.\n        var tmp = current.left;\n        current.left = tmp.right;\n        tmp.right = current;\n        current = tmp;\n        if (!current.left) {\n          break;\n        }\n      }\n      // Link right.\n      right.left = current;\n      right = current;\n      current = current.left;\n    } else if (key > current.key) {\n      if (!current.right) {\n        break;\n      }\n      if (key > current.right.key) {\n        // Rotate left.\n        var tmp = current.right;\n        current.right = tmp.left;\n        tmp.left = current;\n        current = tmp;\n        if (!current.right) {\n          break;\n        }\n      }\n      // Link left.\n      left.right = current;\n      left = current;\n      current = current.right;\n    } else {\n      break;\n    }\n  }\n  // Assemble.\n  left.right = current.left;\n  right.left = current.right;\n  current.left = dummy.right;\n  current.right = dummy.left;\n  this.root_ = current;\n};\n\n\n/**\n * Constructs a Splay tree node.\n *\n * @param {number} key Key.\n * @param {*} value Value.\n */\nSplayTree.Node = function (key, value) {\n  this.key = key;\n  this.value = value;\n};\n\n\n/**\n * @type {SplayTree.Node}\n */\nSplayTree.Node.prototype.left = null;\n\n\n/**\n * @type {SplayTree.Node}\n */\nSplayTree.Node.prototype.right = null;\n\n\n/**\n * Performs an ordered traversal of the subtree starting at\n * this SplayTree.Node.\n *\n * @param {function(SplayTree.Node)} f Visitor function.\n * @private\n */\nSplayTree.Node.prototype.traverse_ = function (f) {\n  var current = this;\n  while (current) {\n    var left = current.left;\n    if (left) left.traverse_(f);\n    f(current);\n    current = current.right;\n  }\n};\n\nvar Splay = new BenchmarkSuite('Splay', 81491, [\n  new Benchmark(\"Splay\", SplayRun, SplaySetup, SplayTearDown)\n]);\n\n/* run_harness.js */\nvar print = console.log;\n\nfunction Run() {\n  BenchmarkSuite.RunSuites({\n    NotifyStep: ShowProgress,\n    NotifyError: AddError,\n    NotifyResult: AddResult,\n    NotifyScore: AddScore,\n  });\n}\n\nvar harnessErrorCount = 0;\n\nfunction ShowProgress(name) {\n  print(\"PROGRESS\", name);\n}\n\nfunction AddError(name, error) {\n  print(\"ERROR\", name, error);\n  print(error.stack);\n  harnessErrorCount++;\n}\n\nfunction AddResult(name, result) {\n  print(\"RESULT\", name, result);\n}\n\nfunction AddScore(score) {\n  print(\"SCORE\", score);\n}\n\nfunction main() {\n  Run();\n}\n"
  },
  {
    "path": "benches/src/lib.rs",
    "content": "#![allow(unused_crate_dependencies)]\n"
  },
  {
    "path": "cli/ABOUT.md",
    "content": "# About Boa\n\nBoa is an open-source, experimental ECMAScript Engine written in Rust for\nlexing, parsing and executing ECMAScript/JavaScript. Currently, Boa supports some\nof the [language][boa-conformance]. More information can be viewed at [Boa's\nwebsite][boa-web].\n\nTry out the most recent release with Boa's live demo\n[playground][boa-playground].\n\n## Boa Crates\n\n- [**`boa_cli`**][cli] - Boa's CLI && REPL implementation\n- [**`boa_ast`**][ast] - Boa's ECMAScript Abstract Syntax Tree.\n- [**`boa_engine`**][engine] - Boa's implementation of ECMAScript builtin objects and execution.\n- [**`boa_gc`**][gc] - Boa's garbage collector.\n- [**`boa_icu_provider`**][icu] - Boa's ICU4X data provider.\n- [**`boa_interner`**][interner] - Boa's string interner.\n- [**`boa_macros`**][macros] - Boa's macros.\n- [**`boa_parser`**][parser] - Boa's lexer and parser.\n- [**`boa_runtime`**][runtime] - Boa's `WebAPI` features.\n- [**`boa_string`**][string] - Boa's ECMAScript string implementation.\n- [**`boa_wintertc`**][wintertc] - Boa's `WinterTC` (TC55) Minimum Common Web API implementation.\n- [**`tag_ptr`**][tag_ptr] - Utility library that enables a pointer to be associated with a tag of type `usize`.\n- [**`small_btree`**][small_btree] - Utility library that adds the `SmallBTreeMap` data structure.\n\n[boa-conformance]: https://boajs.dev/conformance\n[boa-web]: https://boajs.dev/\n[boa-playground]: https://boajs.dev/playground\n[ast]: https://docs.rs/boa_ast/latest/boa_ast/index.html\n[engine]: https://docs.rs/boa_engine/latest/boa_engine/index.html\n[gc]: https://docs.rs/boa_gc/latest/boa_gc/index.html\n[interner]: https://docs.rs/boa_interner/latest/boa_interner/index.html\n[parser]: https://docs.rs/boa_parser/latest/boa_parser/index.html\n[icu]: https://docs.rs/boa_icu_provider/latest/boa_icu_provider/index.html\n[runtime]: https://docs.rs/boa_runtime/latest/boa_runtime/index.html\n[string]: https://docs.rs/boa_string/latest/boa_string/index.html\n[wintertc]: https://docs.rs/boa_wintertc/latest/boa_wintertc/index.html\n[tag_ptr]: https://docs.rs/tag_ptr/latest/tag_ptr/index.html\n[small_btree]: https://docs.rs/small_btree/latest/small_btree/index.html\n[macros]: https://docs.rs/boa_macros/latest/boa_macros/index.html\n[cli]: https://crates.io/crates/boa_cli\n"
  },
  {
    "path": "cli/Cargo.toml",
    "content": "[package]\nname = \"boa_cli\"\nkeywords = [\"javascript\", \"compiler\", \"js\", \"cli\"]\ncategories = [\"command-line-utilities\"]\ndefault-run = \"boa\"\ndescription.workspace = true\nversion.workspace = true\nedition.workspace = true\nauthors.workspace = true\nlicense.workspace = true\nrepository.workspace = true\nrust-version.workspace = true\n\n[dependencies]\nboa_engine = { workspace = true, features = [\"deser\", \"float16\", \"flowgraph\", \"temporal\", \"trace\", \"xsum\"] }\nboa_parser.workspace = true\nboa_gc.workspace = true\nboa_runtime.workspace = true\nrustyline = { workspace = true, features = [\"derive\", \"with-file-history\"] }\nclap = { workspace = true, features = [\"derive\"] }\nserde_json.workspace = true\ncolored.workspace = true\nregex.workspace = true\nphf = { workspace = true, features = [\"macros\"] }\ndhat = { workspace = true, optional = true }\ncolor-eyre.workspace = true\ncow-utils.workspace = true\nfutures-concurrency.workspace = true\nfutures-lite.workspace = true\nasync-channel.workspace = true\nrustls.workspace = true\n\n[features]\ndefault = [\n    \"boa_engine/annex-b\",\n    \"boa_engine/experimental\",\n    \"boa_engine/intl_bundled\",\n    \"boa_engine/native-backtrace\",\n    \"fast-allocator\",\n    \"fetch\",\n]\ndhat = [\"dep:dhat\"]\nfast-allocator = [\"dep:mimalloc-safe\", \"dep:jemallocator\"]\nfetch = [\"boa_runtime/fetch\", \"boa_runtime/reqwest-blocking\"]\n\n[target.x86_64-unknown-linux-gnu.dependencies]\njemallocator = { workspace = true, optional = true }\n\n[target.'cfg(target_os = \"windows\")'.dependencies]\nmimalloc-safe = { workspace = true, optional = true, features = [\n    \"skip_collect_on_exit\",\n] }\n\n[[bin]]\nname = \"boa\"\ndoc = false\npath = \"src/main.rs\"\n\n[lints]\nworkspace = true\n\n[package.metadata.docs.rs]\nall-features = true\n"
  },
  {
    "path": "cli/README.md",
    "content": "# Boa CLI\n\nBoa CLI is `Boa`'s REPL implementation to execute `JavaScript` directly from\nyour CLI.\n\n## Installation\n\n`boa_cli` can be installed directly via `Cargo`.\n\n```shell\n    cargo install boa_cli\n```\n\n<!-- TODO (nekevss): Add a non cargo-based installation options / build out further -->\n\n## Usage\n\n<!-- TODO (nekevss): Potentially add CI driven gifs with https://github.com/charmbracelet/vhs -->\n<!-- NOTE (nekevss): VHS is currently bugged and non-functional on Windows. -->\n\nOnce installed, your good to go!\n\nTo execute some JavaScript source code, navigate to the directory of your choice and type:\n\n```shell\n    boa test.js\n```\n\nOr if you'd like to use Boa's REPL, simply type:\n\n```shell\n    boa\n```\n\nYou can also pipe JavaScript into Boa:\n\n```shell\n    echo 'console.log(1 + 2)' | boa\n    cat script.js | boa\n    boa < script.js\n```\n\n## CLI Options\n\n```txt\nUsage: boa [OPTIONS] [FILE]...\n\nArguments:\n  [FILE]...  The JavaScript file(s) to be evaluated\n\nOptions:\n      --strict                        Run in strict mode\n  -a, --dump-ast [<FORMAT>]           Dump the AST to stdout with the given format [possible values: debug, json, json-pretty]\n  -t, --trace                         Dump the AST to stdout with the given format\n      --vi                            Use vi mode in the REPL\n  -O, --optimize                      Enable bytecode compiler optimizations\n      --optimizer-statistics           Print optimizer statistics (requires -O)\n      --flowgraph [<FORMAT>]          Generate instruction flowgraph. Default is Graphviz [possible values: graphviz, mermaid]\n      --flowgraph-direction <FORMAT>  Specifies the direction of the flowgraph. Default is top-top-bottom [possible values: top-to-bottom, bottom-to-top, left-to-right, right-to-left]\n      --debug-object                  Inject debugging object `$boa`\n  -m, --module                        Treats the input files as modules\n  -r, --root <ROOT>                   Root path from where the module resolver will try to load the modules [default: .]\n  -e, --expression <EXPR>             Execute a JavaScript expression then exit\n  -q, --quiet                         Suppress the welcome banner when starting the REPL\n  -h, --help                          Print help (see more with '--help')\n  -V, --version                       Print version\n```\n\n## REPL Commands\n\nWhen running the interactive REPL (`boa` with no file arguments), the following\ndot-commands are available:\n\n| Command        | Description                         |\n| -------------- | ----------------------------------- |\n| `.help`        | Show available REPL commands        |\n| `.exit`        | Exit the REPL                       |\n| `.clear`       | Clear the terminal screen           |\n| `.load <file>` | Load and evaluate a JavaScript file |\n\nYou can also press `Ctrl+C` to abort the current expression, or `Ctrl+D` to exit.\n\n## Features\n\nBoa's CLI currently has a variety of features (as listed in `Options`).\n\nFeatures include:\n\n- Implemented runtime features (please note that only `Console` is currently implemented)\n- AST Visibility: View the compiled Boa AST (--dump-ast)\n- Tracing: Enabling a vm tracing when executing any JavaScript\n- Flowgraphs: View a generated (with various provided options)\n- Debugging: Boa's CLI comes with an implemented `$boa` debug object with various functionality (see documentation).\n\nHave an idea for a feature? Feel free to submit an issue and/or contribute!\n"
  },
  {
    "path": "cli/src/debug/function.rs",
    "content": "use boa_engine::{\n    Context, JsArgs, JsNativeError, JsObject, JsResult, JsValue, NativeFunction,\n    builtins::function::OrdinaryFunction,\n    js_string,\n    object::ObjectInitializer,\n    vm::flowgraph::{Direction, Graph},\n};\nuse cow_utils::CowUtils;\n\nuse crate::FlowgraphFormat;\n\nfn flowgraph_parse_format_option(value: &JsValue) -> JsResult<FlowgraphFormat> {\n    if value.is_undefined() {\n        return Ok(FlowgraphFormat::Mermaid);\n    }\n    if let Some(string) = value.as_string() {\n        return match string.to_std_string_escaped().cow_to_lowercase().as_ref() {\n            \"mermaid\" => Ok(FlowgraphFormat::Mermaid),\n            \"graphviz\" => Ok(FlowgraphFormat::Graphviz),\n            format => Err(JsNativeError::typ()\n                .with_message(format!(\"Unknown format type '{format}'\"))\n                .into()),\n        };\n    }\n    Err(JsNativeError::typ()\n        .with_message(\"format type must be a string\")\n        .into())\n}\n\nfn flowgraph_parse_direction_option(value: &JsValue) -> JsResult<Direction> {\n    if value.is_undefined() {\n        return Ok(Direction::LeftToRight);\n    }\n    if let Some(string) = value.as_string() {\n        return match string.to_std_string_escaped().cow_to_lowercase().as_ref() {\n            \"leftright\" | \"lr\" => Ok(Direction::LeftToRight),\n            \"rightleft\" | \"rl\" => Ok(Direction::RightToLeft),\n            \"topbottom\" | \"tb\" => Ok(Direction::TopToBottom),\n            \"bottomtop\" | \"bt \" => Ok(Direction::BottomToTop),\n            direction => Err(JsNativeError::typ()\n                .with_message(format!(\"Unknown direction type '{direction}'\"))\n                .into()),\n        };\n    }\n    Err(JsNativeError::typ()\n        .with_message(\"direction type must be a string\")\n        .into())\n}\n\n/// Get functions instruction flowgraph\nfn flowgraph(_this: &JsValue, args: &[JsValue], context: &mut Context) -> JsResult<JsValue> {\n    let Some(value) = args.first() else {\n        return Err(JsNativeError::typ()\n            .with_message(\"expected function argument\")\n            .into());\n    };\n    let Some(object) = value.as_object() else {\n        return Err(JsNativeError::typ()\n            .with_message(format!(\"expected object, got {}\", value.type_of()))\n            .into());\n    };\n    let mut format = FlowgraphFormat::Mermaid;\n    let mut direction = Direction::LeftToRight;\n    if let Some(arguments) = args.get(1) {\n        if let Some(arguments) = arguments.as_object() {\n            format = flowgraph_parse_format_option(&arguments.get(js_string!(\"format\"), context)?)?;\n            direction = flowgraph_parse_direction_option(\n                &arguments.get(js_string!(\"direction\"), context)?,\n            )?;\n        } else {\n            format = flowgraph_parse_format_option(arguments)?;\n        }\n    }\n    let Some(function) = object.downcast_ref::<OrdinaryFunction>() else {\n        return Err(JsNativeError::typ()\n            .with_message(\"expected an ordinary function object\")\n            .into());\n    };\n    let code = function.codeblock();\n    let mut graph = Graph::new(direction);\n    code.to_graph(graph.subgraph(String::default()));\n    let result = match format {\n        FlowgraphFormat::Graphviz => graph.to_graphviz_format(),\n        FlowgraphFormat::Mermaid => graph.to_mermaid_format(),\n    };\n    Ok(JsValue::new(js_string!(result)))\n}\n\nfn bytecode(_: &JsValue, args: &[JsValue], _: &mut Context) -> JsResult<JsValue> {\n    let Some(value) = args.first() else {\n        return Err(JsNativeError::typ()\n            .with_message(\"expected function argument\")\n            .into());\n    };\n\n    let Some(object) = value.as_object() else {\n        return Err(JsNativeError::typ()\n            .with_message(format!(\"expected object, got {}\", value.type_of()))\n            .into());\n    };\n    let Some(function) = object.downcast_ref::<OrdinaryFunction>() else {\n        return Err(JsNativeError::typ()\n            .with_message(\"expected an ordinary function object\")\n            .into());\n    };\n    let code = function.codeblock();\n\n    println!(\"{code}\");\n\n    Ok(JsValue::undefined())\n}\n\nfn set_trace_flag_in_function_object(object: &JsObject, value: bool) -> JsResult<()> {\n    let Some(function) = object.downcast_ref::<OrdinaryFunction>() else {\n        return Err(JsNativeError::typ()\n            .with_message(\"expected an ordinary function object\")\n            .into());\n    };\n    let code = function.codeblock();\n    code.set_traceable(value);\n    Ok(())\n}\n\n/// Trace function.\nfn trace(_: &JsValue, args: &[JsValue], context: &mut Context) -> JsResult<JsValue> {\n    let value = args.get_or_undefined(0);\n    let this = args.get_or_undefined(1);\n\n    let Some(callable) = value.as_callable() else {\n        return Err(JsNativeError::typ()\n            .with_message(\"expected callable object\")\n            .into());\n    };\n\n    let arguments = args.get(2..).unwrap_or(&[]);\n\n    set_trace_flag_in_function_object(&callable, true)?;\n    let result = callable.call(this, arguments, context);\n    set_trace_flag_in_function_object(&callable, false)?;\n\n    result\n}\n\nfn traceable(_: &JsValue, args: &[JsValue], _: &mut Context) -> JsResult<JsValue> {\n    let value = args.get_or_undefined(0);\n    let traceable = args.get_or_undefined(1).to_boolean();\n\n    let Some(callable) = value.as_callable() else {\n        return Err(JsNativeError::typ()\n            .with_message(\"expected callable object\")\n            .into());\n    };\n\n    set_trace_flag_in_function_object(&callable, traceable)?;\n\n    Ok(value.clone())\n}\n\npub(super) fn create_object(context: &mut Context) -> JsObject {\n    ObjectInitializer::new(context)\n        .function(\n            NativeFunction::from_fn_ptr(flowgraph),\n            js_string!(\"flowgraph\"),\n            1,\n        )\n        .function(\n            NativeFunction::from_fn_ptr(bytecode),\n            js_string!(\"bytecode\"),\n            1,\n        )\n        .function(NativeFunction::from_fn_ptr(trace), js_string!(\"trace\"), 1)\n        .function(\n            NativeFunction::from_fn_ptr(traceable),\n            js_string!(\"traceable\"),\n            2,\n        )\n        .build()\n}\n"
  },
  {
    "path": "cli/src/debug/gc.rs",
    "content": "use boa_engine::{\n    Context, JsObject, JsResult, JsValue, NativeFunction, js_string, object::ObjectInitializer,\n};\n\n/// Trigger garbage collection.\nfn collect(_: &JsValue, _: &[JsValue], _: &mut Context) -> JsResult<JsValue> {\n    boa_gc::force_collect();\n    Ok(JsValue::undefined())\n}\n\npub(super) fn create_object(context: &mut Context) -> JsObject {\n    ObjectInitializer::new(context)\n        .function(\n            NativeFunction::from_fn_ptr(collect),\n            js_string!(\"collect\"),\n            0,\n        )\n        .build()\n}\n"
  },
  {
    "path": "cli/src/debug/limits.rs",
    "content": "use boa_engine::{\n    Context, JsArgs, JsNativeError, JsObject, JsResult, JsValue, NativeFunction, js_string,\n    object::{FunctionObjectBuilder, ObjectInitializer},\n    property::Attribute,\n};\n\nfn get_loop(_: &JsValue, _: &[JsValue], context: &mut Context) -> JsResult<JsValue> {\n    let max = context.runtime_limits().loop_iteration_limit();\n    Ok(JsValue::from(max))\n}\n\nfn set_loop(_: &JsValue, args: &[JsValue], context: &mut Context) -> JsResult<JsValue> {\n    let value = args.get_or_undefined(0).to_length(context)?;\n    context.runtime_limits_mut().set_loop_iteration_limit(value);\n    Ok(JsValue::undefined())\n}\n\nfn get_stack(_: &JsValue, _: &[JsValue], context: &mut Context) -> JsResult<JsValue> {\n    let max = context.runtime_limits().stack_size_limit();\n    Ok(JsValue::from(max))\n}\n\nfn set_stack(_: &JsValue, args: &[JsValue], context: &mut Context) -> JsResult<JsValue> {\n    let value = args.get_or_undefined(0).to_length(context)?;\n    let Ok(value) = value.try_into() else {\n        return Err(JsNativeError::range()\n            .with_message(format!(\"Argument {value} greater than usize::MAX\"))\n            .into());\n    };\n    context.runtime_limits_mut().set_stack_size_limit(value);\n    Ok(JsValue::undefined())\n}\n\nfn get_recursion(_: &JsValue, _: &[JsValue], context: &mut Context) -> JsResult<JsValue> {\n    let max = context.runtime_limits().recursion_limit();\n    Ok(JsValue::from(max))\n}\n\nfn set_recursion(_: &JsValue, args: &[JsValue], context: &mut Context) -> JsResult<JsValue> {\n    let value = args.get_or_undefined(0).to_length(context)?;\n    let Ok(value) = value.try_into() else {\n        return Err(JsNativeError::range()\n            .with_message(format!(\"Argument {value} greater than usize::MAX\"))\n            .into());\n    };\n    context.runtime_limits_mut().set_recursion_limit(value);\n    Ok(JsValue::undefined())\n}\n\nfn get_backtrace(_: &JsValue, _: &[JsValue], context: &mut Context) -> JsResult<JsValue> {\n    let max = context.runtime_limits().backtrace_limit();\n    Ok(JsValue::from(max))\n}\n\nfn set_backtrace(_: &JsValue, args: &[JsValue], context: &mut Context) -> JsResult<JsValue> {\n    let value = args.get_or_undefined(0).to_length(context)?;\n    let Ok(value) = value.try_into() else {\n        return Err(JsNativeError::range()\n            .with_message(format!(\"Argument {value} greater than usize::MAX\"))\n            .into());\n    };\n    context.runtime_limits_mut().set_backtrace_limit(value);\n    Ok(JsValue::undefined())\n}\n\npub(super) fn create_object(context: &mut Context) -> JsObject {\n    let get_loop =\n        FunctionObjectBuilder::new(context.realm(), NativeFunction::from_fn_ptr(get_loop))\n            .name(js_string!(\"get loop\"))\n            .length(0)\n            .build();\n    let set_loop =\n        FunctionObjectBuilder::new(context.realm(), NativeFunction::from_fn_ptr(set_loop))\n            .name(js_string!(\"set loop\"))\n            .length(1)\n            .build();\n\n    let get_stack =\n        FunctionObjectBuilder::new(context.realm(), NativeFunction::from_fn_ptr(get_stack))\n            .name(js_string!(\"get stack\"))\n            .length(0)\n            .build();\n    let set_stack =\n        FunctionObjectBuilder::new(context.realm(), NativeFunction::from_fn_ptr(set_stack))\n            .name(js_string!(\"set stack\"))\n            .length(1)\n            .build();\n\n    let get_recursion =\n        FunctionObjectBuilder::new(context.realm(), NativeFunction::from_fn_ptr(get_recursion))\n            .name(js_string!(\"get recursion\"))\n            .length(0)\n            .build();\n    let set_recursion =\n        FunctionObjectBuilder::new(context.realm(), NativeFunction::from_fn_ptr(set_recursion))\n            .name(js_string!(\"set recursion\"))\n            .length(1)\n            .build();\n    let get_backtrace =\n        FunctionObjectBuilder::new(context.realm(), NativeFunction::from_fn_ptr(get_backtrace))\n            .name(js_string!(\"get backtrace\"))\n            .length(0)\n            .build();\n    let set_backtrace =\n        FunctionObjectBuilder::new(context.realm(), NativeFunction::from_fn_ptr(set_backtrace))\n            .name(js_string!(\"set backtrace\"))\n            .length(1)\n            .build();\n\n    ObjectInitializer::new(context)\n        .accessor(\n            js_string!(\"loop\"),\n            Some(get_loop),\n            Some(set_loop),\n            Attribute::WRITABLE | Attribute::CONFIGURABLE | Attribute::NON_ENUMERABLE,\n        )\n        .accessor(\n            js_string!(\"stack\"),\n            Some(get_stack),\n            Some(set_stack),\n            Attribute::WRITABLE | Attribute::CONFIGURABLE | Attribute::NON_ENUMERABLE,\n        )\n        .accessor(\n            js_string!(\"recursion\"),\n            Some(get_recursion),\n            Some(set_recursion),\n            Attribute::WRITABLE | Attribute::CONFIGURABLE | Attribute::NON_ENUMERABLE,\n        )\n        .accessor(\n            js_string!(\"backtrace\"),\n            Some(get_backtrace),\n            Some(set_backtrace),\n            Attribute::WRITABLE | Attribute::CONFIGURABLE | Attribute::NON_ENUMERABLE,\n        )\n        .build()\n}\n"
  },
  {
    "path": "cli/src/debug/mod.rs",
    "content": "// Allow lint so it, doesn't warn about `JsResult<>` unneeded return on functions.\n#![allow(clippy::unnecessary_wraps)]\n\nuse boa_engine::{Context, JsObject, js_string, object::ObjectInitializer, property::Attribute};\n\nmod function;\nmod gc;\nmod limits;\nmod object;\nmod optimizer;\nmod realm;\nmod shape;\nmod string;\n\nfn create_boa_object(context: &mut Context) -> JsObject {\n    let function_module = function::create_object(context);\n    let object_module = object::create_object(context);\n    let shape_module = shape::create_object(context);\n    let optimizer_module = optimizer::create_object(context);\n    let gc_module = gc::create_object(context);\n    let realm_module = realm::create_object(context);\n    let limits_module = limits::create_object(context);\n    let string_module = string::create_string(context);\n\n    ObjectInitializer::new(context)\n        .property(\n            js_string!(\"function\"),\n            function_module,\n            Attribute::WRITABLE | Attribute::NON_ENUMERABLE | Attribute::CONFIGURABLE,\n        )\n        .property(\n            js_string!(\"object\"),\n            object_module,\n            Attribute::WRITABLE | Attribute::NON_ENUMERABLE | Attribute::CONFIGURABLE,\n        )\n        .property(\n            js_string!(\"shape\"),\n            shape_module,\n            Attribute::WRITABLE | Attribute::NON_ENUMERABLE | Attribute::CONFIGURABLE,\n        )\n        .property(\n            js_string!(\"optimizer\"),\n            optimizer_module,\n            Attribute::WRITABLE | Attribute::NON_ENUMERABLE | Attribute::CONFIGURABLE,\n        )\n        .property(\n            js_string!(\"gc\"),\n            gc_module,\n            Attribute::WRITABLE | Attribute::NON_ENUMERABLE | Attribute::CONFIGURABLE,\n        )\n        .property(\n            js_string!(\"realm\"),\n            realm_module,\n            Attribute::WRITABLE | Attribute::NON_ENUMERABLE | Attribute::CONFIGURABLE,\n        )\n        .property(\n            js_string!(\"limits\"),\n            limits_module,\n            Attribute::WRITABLE | Attribute::NON_ENUMERABLE | Attribute::CONFIGURABLE,\n        )\n        .property(\n            js_string!(\"string\"),\n            string_module,\n            Attribute::WRITABLE | Attribute::NON_ENUMERABLE | Attribute::CONFIGURABLE,\n        )\n        .build()\n}\n\n#[allow(clippy::redundant_pub_crate)]\npub(crate) fn init_boa_debug_object(context: &mut Context) {\n    let boa_object = create_boa_object(context);\n    context\n        .register_global_property(\n            js_string!(\"$boa\"),\n            boa_object,\n            Attribute::WRITABLE | Attribute::NON_ENUMERABLE | Attribute::CONFIGURABLE,\n        )\n        .expect(\"cannot fail with the default object\");\n}\n"
  },
  {
    "path": "cli/src/debug/object.rs",
    "content": "use boa_engine::{\n    Context, JsNativeError, JsObject, JsResult, JsValue, NativeFunction, js_string,\n    object::{IndexProperties, ObjectInitializer},\n};\n\n/// Returns objects pointer in memory.\nfn id(_: &JsValue, args: &[JsValue], _: &mut Context) -> JsResult<JsValue> {\n    let Some(value) = args.first() else {\n        return Err(JsNativeError::typ()\n            .with_message(\"expected object argument\")\n            .into());\n    };\n\n    let Some(object) = value.as_object() else {\n        return Err(JsNativeError::typ()\n            .with_message(format!(\"expected object, got {}\", value.type_of()))\n            .into());\n    };\n\n    let ptr: *const _ = object.as_ref();\n    Ok(js_string!(format!(\"0x{:X}\", ptr.cast::<()>() as usize)).into())\n}\n\n/// Returns objects pointer in memory.\nfn indexed_storage_type(_: &JsValue, args: &[JsValue], _: &mut Context) -> JsResult<JsValue> {\n    let Some(value) = args.first() else {\n        return Err(JsNativeError::typ()\n            .with_message(\"expected object argument\")\n            .into());\n    };\n\n    let Some(object) = value.as_object() else {\n        return Err(JsNativeError::typ()\n            .with_message(format!(\"expected object, got {}\", value.type_of()))\n            .into());\n    };\n\n    let typ = match object.borrow().properties().index_properties() {\n        IndexProperties::DenseI32(_) => \"DenseI32\",\n        IndexProperties::DenseF64(_) => \"DenseF64\",\n        IndexProperties::DenseElement(_) => \"DenseElement\",\n        IndexProperties::SparseElement(_) => \"SparseElement\",\n        IndexProperties::SparseProperty(_) => \"SparseProperty\",\n    };\n    Ok(js_string!(typ).into())\n}\n\npub(super) fn create_object(context: &mut Context) -> JsObject {\n    ObjectInitializer::new(context)\n        .function(NativeFunction::from_fn_ptr(id), js_string!(\"id\"), 1)\n        .function(\n            NativeFunction::from_fn_ptr(indexed_storage_type),\n            js_string!(\"indexedStorageType\"),\n            1,\n        )\n        .build()\n}\n"
  },
  {
    "path": "cli/src/debug/optimizer.rs",
    "content": "use boa_engine::{\n    Context, JsArgs, JsObject, JsResult, JsValue, NativeFunction, js_string,\n    object::{FunctionObjectBuilder, ObjectInitializer},\n    optimizer::OptimizerOptions,\n    property::Attribute,\n};\n\nfn get_constant_folding(_: &JsValue, _: &[JsValue], context: &mut Context) -> JsResult<JsValue> {\n    Ok(context\n        .optimizer_options()\n        .contains(OptimizerOptions::CONSTANT_FOLDING)\n        .into())\n}\n\nfn set_constant_folding(_: &JsValue, args: &[JsValue], context: &mut Context) -> JsResult<JsValue> {\n    let value = args.get_or_undefined(0).to_boolean();\n    let mut options = context.optimizer_options();\n    options.set(OptimizerOptions::CONSTANT_FOLDING, value);\n    context.set_optimizer_options(options);\n    Ok(JsValue::undefined())\n}\n\nfn get_statistics(_: &JsValue, _: &[JsValue], context: &mut Context) -> JsResult<JsValue> {\n    Ok(context\n        .optimizer_options()\n        .contains(OptimizerOptions::STATISTICS)\n        .into())\n}\n\nfn set_statistics(_: &JsValue, args: &[JsValue], context: &mut Context) -> JsResult<JsValue> {\n    let value = args.get_or_undefined(0).to_boolean();\n    let mut options = context.optimizer_options();\n    options.set(OptimizerOptions::STATISTICS, value);\n    context.set_optimizer_options(options);\n    Ok(JsValue::undefined())\n}\n\npub(super) fn create_object(context: &mut Context) -> JsObject {\n    let get_constant_folding = FunctionObjectBuilder::new(\n        context.realm(),\n        NativeFunction::from_fn_ptr(get_constant_folding),\n    )\n    .name(\"get constantFolding\")\n    .length(0)\n    .build();\n    let set_constant_folding = FunctionObjectBuilder::new(\n        context.realm(),\n        NativeFunction::from_fn_ptr(set_constant_folding),\n    )\n    .name(\"set constantFolding\")\n    .length(1)\n    .build();\n\n    let get_statistics =\n        FunctionObjectBuilder::new(context.realm(), NativeFunction::from_fn_ptr(get_statistics))\n            .name(\"get statistics\")\n            .length(0)\n            .build();\n    let set_statistics =\n        FunctionObjectBuilder::new(context.realm(), NativeFunction::from_fn_ptr(set_statistics))\n            .name(\"set statistics\")\n            .length(1)\n            .build();\n    ObjectInitializer::new(context)\n        .accessor(\n            js_string!(\"constantFolding\"),\n            Some(get_constant_folding),\n            Some(set_constant_folding),\n            Attribute::WRITABLE | Attribute::CONFIGURABLE | Attribute::NON_ENUMERABLE,\n        )\n        .accessor(\n            js_string!(\"statistics\"),\n            Some(get_statistics),\n            Some(set_statistics),\n            Attribute::WRITABLE | Attribute::CONFIGURABLE | Attribute::NON_ENUMERABLE,\n        )\n        .build()\n}\n"
  },
  {
    "path": "cli/src/debug/realm.rs",
    "content": "use boa_engine::{\n    Context, JsObject, JsResult, JsValue, NativeFunction, js_string, object::ObjectInitializer,\n};\n\n/// Creates a new ECMAScript Realm and returns the global object of the realm.\nfn create(_: &JsValue, _: &[JsValue], _: &mut Context) -> JsResult<JsValue> {\n    let context = &mut Context::default();\n\n    Ok(context.global_object().into())\n}\n\npub(super) fn create_object(context: &mut Context) -> JsObject {\n    ObjectInitializer::new(context)\n        .function(NativeFunction::from_fn_ptr(create), js_string!(\"create\"), 0)\n        .build()\n}\n"
  },
  {
    "path": "cli/src/debug/shape.rs",
    "content": "use boa_engine::{\n    Context, JsArgs, JsNativeError, JsObject, JsResult, JsValue, NativeFunction, js_string,\n    object::ObjectInitializer,\n};\n\nfn get_object(args: &[JsValue], position: usize) -> JsResult<JsObject> {\n    let value = args.get_or_undefined(position);\n\n    let Some(object) = value.as_object() else {\n        return Err(JsNativeError::typ()\n            .with_message(format!(\n                \"expected object in argument position {position}, got {}\",\n                value.type_of()\n            ))\n            .into());\n    };\n\n    Ok(object)\n}\n\n/// Returns object's shape pointer in memory.\nfn id(_: &JsValue, args: &[JsValue], _: &mut Context) -> JsResult<JsValue> {\n    let object = get_object(args, 0)?;\n    let object = object.borrow();\n    let shape = object.shape();\n    Ok(js_string!(format!(\"0x{:X}\", shape.to_addr_usize())).into())\n}\n\n/// Returns object's shape type.\nfn r#type(_: &JsValue, args: &[JsValue], _: &mut Context) -> JsResult<JsValue> {\n    let object = get_object(args, 0)?;\n    let object = object.borrow();\n    let shape = object.shape();\n\n    Ok(if shape.is_shared() {\n        js_string!(\"shared\")\n    } else {\n        js_string!(\"unique\")\n    }\n    .into())\n}\n\n/// Returns object's shape type.\nfn same(_: &JsValue, args: &[JsValue], _: &mut Context) -> JsResult<JsValue> {\n    let lhs = get_object(args, 0)?;\n    let rhs = get_object(args, 1)?;\n\n    let lhs_shape_ptr = {\n        let object = lhs.borrow();\n        let shape = object.shape();\n        shape.to_addr_usize()\n    };\n\n    let rhs_shape_ptr = {\n        let object = rhs.borrow();\n        let shape = object.shape();\n        shape.to_addr_usize()\n    };\n\n    Ok(JsValue::new(lhs_shape_ptr == rhs_shape_ptr))\n}\n\npub(super) fn create_object(context: &mut Context) -> JsObject {\n    ObjectInitializer::new(context)\n        .function(NativeFunction::from_fn_ptr(id), js_string!(\"id\"), 1)\n        .function(NativeFunction::from_fn_ptr(r#type), js_string!(\"type\"), 1)\n        .function(NativeFunction::from_fn_ptr(same), js_string!(\"same\"), 2)\n        .build()\n}\n"
  },
  {
    "path": "cli/src/debug/string.rs",
    "content": "use boa_engine::{\n    Context, JsNativeError, JsObject, JsResult, JsValue, NativeFunction, js_string,\n    object::ObjectInitializer, property::Attribute, string::JsStrVariant,\n};\n\nfn storage(_: &JsValue, args: &[JsValue], _: &mut Context) -> JsResult<JsValue> {\n    let Some(value) = args.first() else {\n        return Err(JsNativeError::typ()\n            .with_message(\"expected string argument\")\n            .into());\n    };\n\n    let Some(string) = value.as_string() else {\n        return Err(JsNativeError::typ()\n            .with_message(format!(\"expected string, got {}\", value.type_of()))\n            .into());\n    };\n\n    let storage = if string.is_static() { \"static\" } else { \"heap\" };\n    Ok(js_string!(storage).into())\n}\n\nfn encoding(_: &JsValue, args: &[JsValue], _: &mut Context) -> JsResult<JsValue> {\n    let Some(value) = args.first() else {\n        return Err(JsNativeError::typ()\n            .with_message(\"expected string argument\")\n            .into());\n    };\n\n    let Some(string) = value.as_string() else {\n        return Err(JsNativeError::typ()\n            .with_message(format!(\"expected string, got {}\", value.type_of()))\n            .into());\n    };\n\n    let str = string.as_str();\n    let encoding = match str.variant() {\n        JsStrVariant::Latin1(_) => \"latin1\",\n        JsStrVariant::Utf16(_) => \"utf16\",\n    };\n    Ok(js_string!(encoding).into())\n}\n\nfn summary(_: &JsValue, args: &[JsValue], context: &mut Context) -> JsResult<JsValue> {\n    let Some(value) = args.first() else {\n        return Err(JsNativeError::typ()\n            .with_message(\"expected string argument\")\n            .into());\n    };\n\n    let Some(string) = value.as_string() else {\n        return Err(JsNativeError::typ()\n            .with_message(format!(\"expected string, got {}\", value.type_of()))\n            .into());\n    };\n\n    let storage = if string.is_static() { \"static\" } else { \"heap\" };\n    let encoding = match string.as_str().variant() {\n        JsStrVariant::Latin1(_) => \"latin1\",\n        JsStrVariant::Utf16(_) => \"utf16\",\n    };\n\n    let summary = ObjectInitializer::new(context)\n        .property(js_string!(\"storage\"), js_string!(storage), Attribute::all())\n        .property(\n            js_string!(\"encoding\"),\n            js_string!(encoding),\n            Attribute::all(),\n        )\n        .build();\n\n    Ok(summary.into())\n}\n\npub(super) fn create_string(context: &mut Context) -> JsObject {\n    ObjectInitializer::new(context)\n        .function(\n            NativeFunction::from_fn_ptr(storage),\n            js_string!(\"storage\"),\n            1,\n        )\n        .function(\n            NativeFunction::from_fn_ptr(encoding),\n            js_string!(\"encoding\"),\n            1,\n        )\n        .function(\n            NativeFunction::from_fn_ptr(summary),\n            js_string!(\"summary\"),\n            1,\n        )\n        .build()\n}\n"
  },
  {
    "path": "cli/src/executor.rs",
    "content": "use std::{\n    cell::RefCell,\n    collections::{BTreeMap, VecDeque},\n    mem,\n    rc::Rc,\n};\n\nuse boa_engine::{\n    Context, JsResult,\n    context::time::JsInstant,\n    job::{GenericJob, Job, JobExecutor, NativeAsyncJob, PromiseJob, TimeoutJob},\n};\nuse futures_concurrency::future::FutureGroup;\nuse futures_lite::{StreamExt, future};\n\nuse crate::{logger::SharedExternalPrinterLogger, uncaught_job_error};\n\npub(crate) struct Executor {\n    promise_jobs: RefCell<VecDeque<PromiseJob>>,\n    async_jobs: RefCell<VecDeque<NativeAsyncJob>>,\n    timeout_jobs: RefCell<BTreeMap<JsInstant, Vec<TimeoutJob>>>,\n    generic_jobs: RefCell<VecDeque<GenericJob>>,\n    finalization_registry_jobs: RefCell<VecDeque<NativeAsyncJob>>,\n\n    printer: SharedExternalPrinterLogger,\n}\n\nimpl Executor {\n    pub(crate) fn new(printer: SharedExternalPrinterLogger) -> Self {\n        Self {\n            promise_jobs: RefCell::default(),\n            async_jobs: RefCell::default(),\n            timeout_jobs: RefCell::default(),\n            generic_jobs: RefCell::default(),\n            finalization_registry_jobs: RefCell::default(),\n            printer,\n        }\n    }\n\n    pub(crate) fn clear(&self) {\n        self.promise_jobs.borrow_mut().clear();\n        self.async_jobs.borrow_mut().clear();\n        self.timeout_jobs.borrow_mut().clear();\n        self.generic_jobs.borrow_mut().clear();\n    }\n\n    fn is_empty(&self) -> bool {\n        self.promise_jobs.borrow().is_empty()\n            && self.async_jobs.borrow().is_empty()\n            && self.timeout_jobs.borrow().is_empty()\n            && self.generic_jobs.borrow().is_empty()\n    }\n\n    fn drain_timeout_jobs(&self, context: &mut Context) {\n        let now = context.clock().now();\n\n        let mut timeouts_borrow = self.timeout_jobs.borrow_mut();\n        let mut jobs_to_keep = timeouts_borrow.split_off(&now);\n        jobs_to_keep.retain(|_, jobs| {\n            jobs.retain(|job| !job.is_cancelled());\n            !jobs.is_empty()\n        });\n        let jobs_to_run = mem::replace(&mut *timeouts_borrow, jobs_to_keep);\n        drop(timeouts_borrow);\n\n        for jobs in jobs_to_run.into_values() {\n            for job in jobs {\n                if !job.is_cancelled()\n                    && let Err(e) = job.call(context)\n                {\n                    self.printer.print(uncaught_job_error(&e));\n                }\n            }\n        }\n    }\n\n    fn drain_generic_jobs(&self, context: &mut Context) {\n        let job = self.generic_jobs.borrow_mut().pop_front();\n        if let Some(generic) = job\n            && let Err(err) = generic.call(context)\n        {\n            self.printer.print(uncaught_job_error(&err));\n        }\n    }\n}\n\nimpl JobExecutor for Executor {\n    fn enqueue_job(self: Rc<Self>, job: Job, context: &mut Context) {\n        match job {\n            Job::PromiseJob(job) => self.promise_jobs.borrow_mut().push_back(job),\n            Job::AsyncJob(job) => self.async_jobs.borrow_mut().push_back(job),\n            Job::TimeoutJob(job) => {\n                let now = context.clock().now();\n                self.timeout_jobs\n                    .borrow_mut()\n                    .entry(now + job.timeout())\n                    .or_default()\n                    .push(job);\n            }\n            Job::GenericJob(job) => self.generic_jobs.borrow_mut().push_back(job),\n            Job::FinalizationRegistryCleanupJob(job) => {\n                self.finalization_registry_jobs.borrow_mut().push_back(job);\n            }\n            job => self.printer.print(format!(\"unsupported job type {job:?}\")),\n        }\n    }\n\n    fn run_jobs(self: Rc<Self>, context: &mut Context) -> JsResult<()> {\n        future::block_on(self.run_jobs_async(&RefCell::new(context)))\n    }\n\n    async fn run_jobs_async(self: Rc<Self>, context: &RefCell<&mut Context>) -> JsResult<()> {\n        let mut group = FutureGroup::new();\n        let mut fr_group = FutureGroup::new();\n\n        loop {\n            for job in mem::take(&mut *self.async_jobs.borrow_mut()) {\n                group.insert(job.call(context));\n            }\n\n            for job in mem::take(&mut *self.finalization_registry_jobs.borrow_mut()) {\n                fr_group.insert(job.call(context));\n            }\n\n            if let Some(Err(e)) = future::poll_once(group.next()).await.flatten() {\n                self.printer.print(uncaught_job_error(&e));\n            }\n\n            // We particularly want to make this check here such that the\n            // event loop is cancelled almost immediately after the channel with\n            // the reader gets closed.\n            if self.is_empty() && group.is_empty() {\n                // Run finalizers with a lower priority than every other type of\n                // job.\n                if let Some(Err(e)) = future::poll_once(fr_group.next()).await.flatten() {\n                    self.printer.print(uncaught_job_error(&e));\n                }\n\n                // Finalizers could enqueue new jobs, so we cannot just exit.\n                if self.is_empty() {\n                    return Ok(());\n                }\n            }\n\n            {\n                let context = &mut context.borrow_mut();\n                self.drain_timeout_jobs(context);\n                self.drain_generic_jobs(context);\n\n                let jobs = mem::take(&mut *self.promise_jobs.borrow_mut());\n                for job in jobs {\n                    if let Err(e) = job.call(context) {\n                        self.printer.print(uncaught_job_error(&e));\n                    }\n                }\n            }\n            context.borrow_mut().clear_kept_objects();\n\n            future::yield_now().await;\n        }\n    }\n}\n"
  },
  {
    "path": "cli/src/helper.rs",
    "content": "use boa_engine::{ast::scope::Scope, interner::Interner};\nuse boa_parser::Source;\nuse colored::{Color, Colorize};\nuse phf::{Set, phf_set};\nuse regex::{Captures, Regex, Replacer};\nuse rustyline::{\n    Completer, Helper, Hinter,\n    error::ReadlineError,\n    highlight::{CmdKind, Highlighter},\n    validate::{ValidationContext, ValidationResult, Validator},\n};\nuse std::borrow::Cow::{self, Borrowed};\n\nconst STRING_COLOR: Color = Color::Green;\nconst KEYWORD_COLOR: Color = Color::Yellow;\nconst PROPERTY_COLOR: Color = Color::Magenta;\nconst OPERATOR_COLOR: Color = Color::TrueColor {\n    r: 214,\n    g: 95,\n    b: 26,\n};\nconst UNDEFINED_COLOR: Color = Color::TrueColor {\n    r: 100,\n    g: 100,\n    b: 100,\n};\nconst NUMBER_COLOR: Color = Color::TrueColor {\n    r: 26,\n    g: 214,\n    b: 175,\n};\nconst IDENTIFIER_COLOR: Color = Color::TrueColor {\n    r: 26,\n    g: 160,\n    b: 214,\n};\n\nconst READLINE_COLOR: Color = Color::Cyan;\n\n#[allow(clippy::upper_case_acronyms, clippy::redundant_pub_crate)]\n#[derive(Completer, Helper, Hinter)]\npub(crate) struct RLHelper {\n    strict: bool,\n    highlighter: LineHighlighter,\n    colored_prompt: String,\n}\n\nimpl RLHelper {\n    pub(crate) fn new(prompt: &str, strict: bool) -> Self {\n        Self {\n            highlighter: LineHighlighter::new(),\n            strict,\n            colored_prompt: prompt.color(READLINE_COLOR).bold().to_string(),\n        }\n    }\n}\n\nimpl Validator for RLHelper {\n    fn validate(\n        &self,\n        context: &mut ValidationContext<'_>,\n    ) -> Result<ValidationResult, ReadlineError> {\n        let mut parser = boa_parser::Parser::new(Source::from_bytes(context.input()));\n        if self.strict {\n            parser.set_strict();\n        }\n\n        match parser.parse_script(&Scope::new_global(), &mut Interner::new()) {\n            // Skip scope analysis errors, those can be caused by not having the\n            // context's Scope to resolve variables.\n            Ok(_) | Err(boa_parser::Error::ScopeAnalysis { .. }) => {\n                Ok(ValidationResult::Valid(None))\n            }\n            Err(boa_parser::Error::AbruptEnd) => Ok(ValidationResult::Incomplete),\n            Err(err) => Ok(ValidationResult::Invalid(Some(format!(\n                \"\\n{} {}\",\n                \"Uncaught SyntaxError\".red(),\n                err.to_string().red()\n            )))),\n        }\n    }\n\n    fn validate_while_typing(&self) -> bool {\n        false\n    }\n}\n\nimpl Highlighter for RLHelper {\n    fn highlight<'l>(&self, line: &'l str, pos: usize) -> Cow<'l, str> {\n        self.highlighter.highlight(line, pos)\n    }\n\n    // Must match signature of Highlighter::highlight_prompt, can't elide lifetimes.\n    #[allow(single_use_lifetimes)]\n    fn highlight_prompt<'b, 's: 'b, 'p: 'b>(\n        &'s self,\n        prompt: &'p str,\n        default: bool,\n    ) -> Cow<'b, str> {\n        if default {\n            Borrowed(&self.colored_prompt)\n        } else {\n            Borrowed(prompt)\n        }\n    }\n\n    fn highlight_hint<'h>(&self, hint: &'h str) -> Cow<'h, str> {\n        hint.into()\n    }\n\n    fn highlight_candidate<'c>(\n        &self,\n        candidate: &'c str,\n        _completion: rustyline::CompletionType,\n    ) -> Cow<'c, str> {\n        self.highlighter.highlight(candidate, 0)\n    }\n\n    fn highlight_char(&self, line: &str, _: usize, _: CmdKind) -> bool {\n        !line.is_empty()\n    }\n}\n\nstatic KEYWORDS: Set<&'static str> = phf_set! {\n    \"break\",\n    \"case\",\n    \"catch\",\n    \"class\",\n    \"const\",\n    \"continue\",\n    \"default\",\n    \"delete\",\n    \"do\",\n    \"else\",\n    \"export\",\n    \"extends\",\n    \"finally\",\n    \"for\",\n    \"function\",\n    \"if\",\n    \"import\",\n    \"instanceof\",\n    \"new\",\n    \"return\",\n    \"super\",\n    \"switch\",\n    \"this\",\n    \"throw\",\n    \"try\",\n    \"typeof\",\n    \"var\",\n    \"void\",\n    \"while\",\n    \"with\",\n    \"yield\",\n    \"await\",\n    \"enum\",\n    \"let\",\n};\n\nstruct LineHighlighter {\n    regex: Regex,\n}\n\nimpl LineHighlighter {\n    fn new() -> Self {\n        // Precompiles the regex to avoid creating it again after every highlight\n        let regex = Regex::new(\n            r#\"(?x)\n            (?P<identifier>\\b[$_\\p{ID_Start}][$_\\p{ID_Continue}\\u{200C}\\u{200D}]*\\b) |\n            (?P<string_double_quote>\"([^\"\\\\]|\\\\.)*\") |\n            (?P<string_single_quote>'([^'\\\\]|\\\\.)*') |\n            (?P<template_literal>`([^`\\\\]|\\\\.)*`) |\n            (?P<op>[+\\-/*%~^!&|=<>;:]) |\n            (?P<number>0[bB][01](_?[01])*n?|0[oO][0-7](_?[0-7])*n?|0[xX][0-9a-fA-F](_?[0-9a-fA-F])*n?|(([0-9](_?[0-9])*\\.([0-9](_?[0-9])*)?)|(([0-9](_?[0-9])*)?\\.[0-9](_?[0-9])*)|([0-9](_?[0-9])*))([eE][+-]?[0-9](_?[0-9])*)?n?)\"#,\n        ).expect(\"could not compile regular expression\");\n\n        Self { regex }\n    }\n}\n\nimpl Highlighter for LineHighlighter {\n    fn highlight<'l>(&self, line: &'l str, _: usize) -> Cow<'l, str> {\n        use std::fmt::Write;\n\n        struct Colorizer;\n\n        impl Replacer for Colorizer {\n            // Changing to map_or_else moves the handling of \"identifier\" after all other kinds,\n            // which reads worse than this version.\n            #[allow(clippy::option_if_let_else)]\n            fn replace_append(&mut self, caps: &Captures<'_>, dst: &mut String) {\n                let colored = if let Some(cap) = caps.name(\"identifier\") {\n                    let cap = cap.as_str();\n\n                    let colored = match cap {\n                        \"true\" | \"false\" | \"null\" | \"Infinity\" | \"globalThis\" => {\n                            cap.color(PROPERTY_COLOR)\n                        }\n                        \"undefined\" => cap.color(UNDEFINED_COLOR),\n                        identifier if KEYWORDS.contains(identifier) => {\n                            cap.color(KEYWORD_COLOR).bold()\n                        }\n                        _ => cap.color(IDENTIFIER_COLOR),\n                    };\n\n                    Some(colored)\n                } else if let Some(cap) = caps\n                    .name(\"string_double_quote\")\n                    .or_else(|| caps.name(\"string_single_quote\"))\n                    .or_else(|| caps.name(\"template_literal\"))\n                {\n                    Some(cap.as_str().color(STRING_COLOR))\n                } else if let Some(cap) = caps.name(\"op\") {\n                    Some(cap.as_str().color(OPERATOR_COLOR))\n                } else {\n                    caps.name(\"number\")\n                        .map(|cap| cap.as_str().color(NUMBER_COLOR))\n                };\n\n                if let Some(colored) = colored {\n                    write!(dst, \"{colored}\").expect(\"could not append data to dst\");\n                } else {\n                    dst.push_str(&caps[0]);\n                }\n            }\n        }\n        self.regex.replace_all(line, Colorizer)\n    }\n}\n"
  },
  {
    "path": "cli/src/logger.rs",
    "content": "use boa_engine::{Context, Finalize, JsResult, Trace};\nuse boa_runtime::{ConsoleState, Logger};\nuse rustyline::ExternalPrinter;\nuse std::fmt::Debug;\nuse std::io::Write;\nuse std::sync::{Arc, Mutex};\n\n#[derive(Clone, Trace, Finalize)]\npub(crate) struct SharedExternalPrinterLogger {\n    #[unsafe_ignore_trace]\n    inner: Arc<Mutex<Option<Box<dyn ExternalPrinter + Send>>>>,\n}\n\nimpl SharedExternalPrinterLogger {\n    pub(crate) fn new() -> Self {\n        Self {\n            inner: Arc::new(Mutex::new(None)),\n        }\n    }\n\n    pub(crate) fn set<T: ExternalPrinter + Send + 'static>(&self, inner: T) {\n        self.inner\n            .lock()\n            .expect(\"printer lock failed\")\n            .replace(Box::new(inner));\n    }\n\n    pub(crate) fn print(&self, message: String) {\n        if let Some(l) = &mut *self.inner.lock().expect(\"printer lock failed\") {\n            // Ignore errors, there's nothing we can do at this point.\n            drop(l.print(message));\n        } else {\n            drop(std::io::stdout().write_all(message.as_bytes()));\n        }\n    }\n}\n\nimpl Debug for SharedExternalPrinterLogger {\n    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {\n        f.debug_struct(\"SharedExternalPrinterLogger\").finish()\n    }\n}\n\nimpl Logger for SharedExternalPrinterLogger {\n    #[inline]\n    fn log(&self, msg: String, state: &ConsoleState, _context: &mut Context) -> JsResult<()> {\n        let indent = state.indent();\n        self.print(format!(\"{msg:>indent$}\\n\"));\n        Ok(())\n    }\n\n    #[inline]\n    fn info(&self, msg: String, state: &ConsoleState, context: &mut Context) -> JsResult<()> {\n        self.log(msg, state, context)\n    }\n\n    #[inline]\n    fn warn(&self, msg: String, state: &ConsoleState, context: &mut Context) -> JsResult<()> {\n        self.log(msg, state, context)\n    }\n\n    #[inline]\n    fn error(&self, msg: String, state: &ConsoleState, _context: &mut Context) -> JsResult<()> {\n        let indent = state.indent();\n        self.print(format!(\"{msg:>indent$}\\n\"));\n        Ok(())\n    }\n}\n"
  },
  {
    "path": "cli/src/main.rs",
    "content": "//! A CLI implementation for `boa_engine` that comes complete with file execution and a REPL.\n#![doc = include_str!(\"../ABOUT.md\")]\n#![doc(\n    html_logo_url = \"https://raw.githubusercontent.com/boa-dev/boa/main/assets/logo_black.svg\",\n    html_favicon_url = \"https://raw.githubusercontent.com/boa-dev/boa/main/assets/logo_black.svg\"\n)]\n#![cfg_attr(not(test), deny(clippy::unwrap_used))]\n#![allow(clippy::print_stdout, clippy::print_stderr)]\n\nmod debug;\nmod executor;\nmod helper;\nmod logger;\n\nuse crate::executor::Executor;\nuse crate::logger::SharedExternalPrinterLogger;\nuse async_channel::Sender;\nuse boa_engine::JsValue;\nuse boa_engine::error::JsErasedError;\nuse boa_engine::job::NativeAsyncJob;\nuse boa_engine::{\n    Context, JsError, Source,\n    builtins::promise::PromiseState,\n    context::ContextBuilder,\n    module::{Module, SimpleModuleLoader},\n    optimizer::OptimizerOptions,\n    script::Script,\n    vm::flowgraph::{Direction, Graph},\n};\nuse boa_parser::source::ReadChar;\nuse clap::{Parser, ValueEnum, ValueHint};\nuse color_eyre::{\n    Result, Section,\n    eyre::{WrapErr, eyre},\n};\nuse colored::Colorize;\nuse debug::init_boa_debug_object;\nuse rustyline::{EditMode, Editor, config::Config, error::ReadlineError};\nuse std::time::{Duration, Instant};\nuse std::{\n    fs::OpenOptions,\n    io::{self, IsTerminal, Read, Write},\n    path::{Path, PathBuf},\n    rc::Rc,\n    thread,\n};\n\n// ----\n\n#[cfg(all(\n    target_arch = \"x86_64\",\n    target_os = \"linux\",\n    target_env = \"gnu\",\n    feature = \"dhat\"\n))]\nuse jemallocator as _;\n\n#[cfg(all(\n    target_arch = \"x86_64\",\n    target_os = \"linux\",\n    target_env = \"gnu\",\n    feature = \"fast-allocator\",\n    not(feature = \"dhat\")\n))]\n#[global_allocator]\nstatic ALLOC: jemallocator::Jemalloc = jemallocator::Jemalloc;\n\n#[cfg(all(target_os = \"windows\", feature = \"dhat\"))]\nuse mimalloc_safe as _;\n\n#[cfg(all(\n    target_os = \"windows\",\n    feature = \"fast-allocator\",\n    not(feature = \"dhat\")\n))]\n#[global_allocator]\nstatic ALLOC: mimalloc_safe::MiMalloc = mimalloc_safe::MiMalloc;\n\n#[cfg(feature = \"dhat\")]\n#[global_allocator]\nstatic ALLOC: dhat::Alloc = dhat::Alloc;\n\n/// CLI configuration for Boa.\nstatic CLI_HISTORY: &str = \".boa_history\";\n\n// Added #[allow(clippy::option_option)] because to StructOpt an Option<Option<T>>\n// is an optional argument that optionally takes a value ([--opt=[val]]).\n// https://docs.rs/structopt/0.3.11/structopt/#type-magic\n#[derive(Debug, Parser)]\n#[command(author, version, about, name = \"boa\")]\n#[allow(clippy::struct_excessive_bools)] // NOTE: Allow having more than 3 bools in struct\nstruct Opt {\n    /// The JavaScript file(s) to be evaluated.\n    #[arg(name = \"FILE\", value_hint = ValueHint::FilePath)]\n    files: Vec<PathBuf>,\n\n    /// Run in strict mode.\n    #[arg(long)]\n    strict: bool,\n\n    /// Dump the AST to stdout with the given format.\n    #[arg(\n        long,\n        short = 'a',\n        value_name = \"FORMAT\",\n        ignore_case = true,\n        value_enum,\n        conflicts_with = \"graph\"\n    )]\n    #[allow(clippy::option_option)]\n    dump_ast: Option<Option<DumpFormat>>,\n\n    /// Dump the AST to stdout with the given format.\n    #[arg(long, short, conflicts_with = \"graph\")]\n    trace: bool,\n\n    /// Use vi mode in the REPL\n    #[arg(long = \"vi\")]\n    vi_mode: bool,\n\n    /// Report parsing and execution timings.\n    #[arg(long)]\n    time: bool,\n\n    #[arg(long, short = 'O', group = \"optimizer\")]\n    optimize: bool,\n\n    #[arg(long, requires = \"optimizer\")]\n    optimizer_statistics: bool,\n\n    /// Generate instruction flowgraph. Default is Graphviz.\n    #[arg(\n        long,\n        value_name = \"FORMAT\",\n        ignore_case = true,\n        value_enum,\n        group = \"graph\"\n    )]\n    #[allow(clippy::option_option)]\n    flowgraph: Option<Option<FlowgraphFormat>>,\n\n    /// Specifies the direction of the flowgraph. Default is top-top-bottom.\n    #[arg(\n        long,\n        value_name = \"FORMAT\",\n        ignore_case = true,\n        value_enum,\n        requires = \"graph\"\n    )]\n    flowgraph_direction: Option<FlowgraphDirection>,\n\n    /// Inject debugging object `$boa`.\n    #[arg(long)]\n    debug_object: bool,\n\n    /// Treats the input files as modules.\n    #[arg(long, short = 'm', group = \"mod\")]\n    module: bool,\n\n    /// Root path from where the module resolver will try to load the modules.\n    #[arg(long, short = 'r', default_value_os_t = PathBuf::from(\".\"), requires = \"mod\")]\n    root: PathBuf,\n\n    /// Execute a JavaScript expression then exit. Files (see above) will be\n    /// executed prior to the expression.\n    #[arg(long, short = 'e')]\n    expression: Option<String>,\n\n    /// Suppress the welcome banner when starting the REPL.\n    #[arg(long, short = 'q')]\n    quiet: bool,\n}\n\nimpl Opt {\n    /// Returns whether a dump flag has been used.\n    const fn has_dump_flag(&self) -> bool {\n        self.dump_ast.is_some()\n    }\n}\n\n/// The different types of format available for dumping.\n#[derive(Debug, Copy, Clone, Default, ValueEnum)]\nenum DumpFormat {\n    // NOTE: This can easily support other formats just by\n    // adding a field to this enum and adding the necessary\n    // implementation. Example: Toml, Html, etc.\n    //\n    // NOTE: The fields of this enum are not doc comments because\n    // arg_enum! macro does not support it.\n    /// This is the default format that you get from `std::fmt::Debug`.\n    #[default]\n    Debug,\n\n    /// This is a minified json format.\n    Json,\n\n    /// This is a pretty printed json format.\n    JsonPretty,\n}\n\n/// Represents the format of the instruction flowgraph.\n#[derive(Debug, Clone, Copy, ValueEnum)]\nenum FlowgraphFormat {\n    /// Generates in graphviz format: <https://graphviz.org/>.\n    Graphviz,\n    /// Generates in mermaid format: <https://mermaid-js.github.io/mermaid/>.\n    Mermaid,\n}\n\n/// Represents the direction of the instruction flowgraph.\n#[derive(Debug, Clone, Copy, ValueEnum)]\nenum FlowgraphDirection {\n    TopToBottom,\n    BottomToTop,\n    LeftToRight,\n    RightToLeft,\n}\n\nstruct Timer<'a> {\n    name: &'static str,\n    start: Instant,\n    counters: &'a mut Vec<(&'static str, Duration)>,\n}\n\nimpl Drop for Timer<'_> {\n    fn drop(&mut self) {\n        self.counters.push((self.name, self.start.elapsed()));\n    }\n}\n\nstruct Counters {\n    counters: Option<Vec<(&'static str, Duration)>>,\n}\n\nimpl Counters {\n    fn new(enabled: bool) -> Self {\n        Self {\n            counters: enabled.then_some(Vec::new()),\n        }\n    }\n\n    fn new_timer(&mut self, name: &'static str) -> Option<Timer<'_>> {\n        self.counters.as_mut().map(|counters| Timer {\n            name,\n            start: Instant::now(),\n            counters,\n        })\n    }\n}\n\nimpl Drop for Counters {\n    fn drop(&mut self) {\n        let Some(counters) = self.counters.take() else {\n            return;\n        };\n        if counters.is_empty() {\n            return;\n        }\n\n        let max_width = counters\n            .iter()\n            .map(|(name, _)| name.len())\n            .max()\n            .unwrap_or(0)\n            .max(\"Total\".len())\n            + 1; // +1 for the colon\n\n        let mut total = Duration::ZERO;\n        eprintln!();\n        for (name, elapsed) in &counters {\n            eprintln!(\n                \"{:<width$} {elapsed:.2?}\",\n                format!(\"{name}:\"),\n                width = max_width\n            );\n            total += *elapsed;\n        }\n        if counters.len() > 1 {\n            eprintln!(\"{:<width$} {total:.2?}\", \"Total:\", width = max_width);\n        }\n    }\n}\n\n/// Dumps the AST to stdout with format controlled by the given arguments.\n///\n/// Returns a error of type String with a error message,\n/// if the source has a syntax or parsing error.\nfn dump<R: ReadChar>(src: Source<'_, R>, args: &Opt, context: &mut Context) -> Result<()> {\n    if let Some(arg) = args.dump_ast {\n        let mut counters = Counters::new(args.time);\n        let arg = arg.unwrap_or_default();\n        let mut parser = boa_parser::Parser::new(src);\n        let dump =\n            if args.module {\n                let scope = context.realm().scope().clone();\n                let module = {\n                    let _timer = counters.new_timer(\"Parsing\");\n                    parser\n                        .parse_module(&scope, context.interner_mut())\n                        .map_err(|e| eyre!(\"Uncaught SyntaxError: {e}\"))?\n                };\n                let _timer = counters.new_timer(\"AST generation\");\n                match arg {\n                    DumpFormat::Json => serde_json::to_string(&module)\n                        .expect(\"could not convert AST to a JSON string\"),\n                    DumpFormat::JsonPretty => serde_json::to_string_pretty(&module)\n                        .expect(\"could not convert AST to a pretty JSON string\"),\n                    DumpFormat::Debug => format!(\"{module:#?}\"),\n                }\n            } else {\n                let scope = context.realm().scope().clone();\n                let mut script = {\n                    let _timer = counters.new_timer(\"Parsing\");\n                    parser\n                        .parse_script(&scope, context.interner_mut())\n                        .map_err(|e| eyre!(\"Uncaught SyntaxError: {e}\"))?\n                };\n\n                if args.optimize {\n                    context.optimize_statement_list(script.statements_mut());\n                }\n\n                let _timer = counters.new_timer(\"AST generation\");\n                match arg {\n                    DumpFormat::Json => serde_json::to_string(&script)\n                        .expect(\"could not convert AST to a JSON string\"),\n                    DumpFormat::JsonPretty => serde_json::to_string_pretty(&script)\n                        .expect(\"could not convert AST to a pretty JSON string\"),\n                    DumpFormat::Debug => format!(\"{script:#?}\"),\n                }\n            };\n        drop(counters);\n        println!(\"{dump}\");\n    }\n\n    Ok(())\n}\n\nfn generate_flowgraph<R: ReadChar>(\n    context: &mut Context,\n    src: Source<'_, R>,\n    format: FlowgraphFormat,\n    direction: Option<FlowgraphDirection>,\n) -> Result<String> {\n    let script = Script::parse(src, None, context).map_err(|e| e.into_erased(context))?;\n    let code = script\n        .codeblock(context)\n        .map_err(|e| e.into_erased(context))?;\n    let direction = match direction {\n        Some(FlowgraphDirection::TopToBottom) | None => Direction::TopToBottom,\n        Some(FlowgraphDirection::BottomToTop) => Direction::BottomToTop,\n        Some(FlowgraphDirection::LeftToRight) => Direction::LeftToRight,\n        Some(FlowgraphDirection::RightToLeft) => Direction::RightToLeft,\n    };\n    let mut graph = Graph::new(direction);\n    code.to_graph(graph.subgraph(String::default()));\n    let result = match format {\n        FlowgraphFormat::Graphviz => graph.to_graphviz_format(),\n        FlowgraphFormat::Mermaid => graph.to_mermaid_format(),\n    };\n    Ok(result)\n}\n\n#[must_use]\nfn uncaught_error(error: &JsError) -> String {\n    format!(\n        \"{} {}\\n\",\n        \"Uncaught Error:\".red().bold(),\n        error.to_string().red()\n    )\n}\n\n#[must_use]\nfn uncaught_job_error(error: &JsError) -> String {\n    format!(\n        \"{} {}\\n\",\n        \"Uncaught Error (during job evaluation):\".red().bold(),\n        error.to_string().red()\n    )\n}\n\nfn evaluate_expr(\n    line: &str,\n    args: &Opt,\n    context: &mut Context,\n    printer: &SharedExternalPrinterLogger,\n) -> Result<()> {\n    if args.has_dump_flag() {\n        dump(Source::from_bytes(line), args, context)?;\n    } else if let Some(flowgraph) = args.flowgraph {\n        match generate_flowgraph(\n            context,\n            Source::from_bytes(line),\n            flowgraph.unwrap_or(FlowgraphFormat::Graphviz),\n            args.flowgraph_direction,\n        ) {\n            Ok(v) => println!(\"{v}\"),\n            Err(v) => eprintln!(\"{v:?}\"),\n        }\n    } else {\n        let mut counters = Counters::new(args.time);\n        let script = {\n            let _timer = counters.new_timer(\"Parsing\");\n            Script::parse(Source::from_bytes(line), None, context)\n        };\n\n        match script {\n            Ok(script) => {\n                let result = {\n                    let _timer = counters.new_timer(\"Execution\");\n                    let result = script.evaluate(context);\n                    if let Err(err) = context.run_jobs() {\n                        printer.print(uncaught_job_error(&err));\n                    }\n                    result\n                };\n                match result {\n                    Ok(v) => printer.print(format!(\"{}\\n\", v.display())),\n                    Err(ref v) => printer.print(uncaught_error(v)),\n                }\n            }\n            Err(ref v) => printer.print(uncaught_error(v)),\n        }\n    }\n\n    Ok(())\n}\n\nfn evaluate_file(\n    file: &Path,\n    args: &Opt,\n    context: &mut Context,\n    loader: &SimpleModuleLoader,\n    printer: &SharedExternalPrinterLogger,\n) -> Result<()> {\n    if args.has_dump_flag() {\n        return dump(Source::from_filepath(file)?, args, context);\n    }\n\n    if let Some(flowgraph) = args.flowgraph {\n        let flowgraph = generate_flowgraph(\n            context,\n            Source::from_filepath(file)?,\n            flowgraph.unwrap_or(FlowgraphFormat::Graphviz),\n            args.flowgraph_direction,\n        )?;\n\n        println!(\"{flowgraph}\");\n\n        return Ok(());\n    }\n\n    if args.module {\n        let source = Source::from_filepath(file)?;\n        let mut counters = Counters::new(args.time);\n        let module = {\n            let _timer = counters.new_timer(\"Parsing\");\n            Module::parse(source, None, context)\n        };\n        let module = module.map_err(|e| e.into_erased(context))?;\n\n        loader.insert(\n            file.canonicalize()\n                .wrap_err(\"could not canonicalize input file path\")?,\n            module.clone(),\n        );\n\n        let promise = {\n            let _timer = counters.new_timer(\"Execution\");\n            let promise = module.load_link_evaluate(context);\n            context.run_jobs().map_err(|err| err.into_erased(context))?;\n            Ok::<_, JsErasedError>(promise)\n        }?;\n        let result = promise.state();\n\n        return match result {\n            PromiseState::Pending => Err(eyre!(\"module didn't execute\")),\n            PromiseState::Fulfilled(_) => Ok(()),\n            PromiseState::Rejected(err) => {\n                Err(JsError::from_opaque(err).into_erased(context).into())\n            }\n        };\n    }\n\n    let source = Source::from_filepath(file)?;\n    let mut counters = Counters::new(args.time);\n    let script = {\n        let _timer = counters.new_timer(\"Parsing\");\n        Script::parse(source, None, context)\n    };\n    let script = script.map_err(|e| e.into_erased(context))?;\n\n    let result = {\n        let _timer = counters.new_timer(\"Execution\");\n        let result = script.evaluate(context);\n        context.run_jobs().map_err(|err| err.into_erased(context))?;\n        result\n    };\n\n    match result {\n        Ok(v) => {\n            if !v.is_undefined() {\n                println!(\"{}\", v.display());\n            }\n        }\n        Err(v) => printer.print(uncaught_error(&v)),\n    }\n\n    Ok(())\n}\n\nfn evaluate_files(\n    args: &Opt,\n    context: &mut Context,\n    loader: &SimpleModuleLoader,\n    printer: &SharedExternalPrinterLogger,\n) -> Result<()> {\n    for file in &args.files {\n        evaluate_file(file, args, context, loader, printer)?;\n    }\n    Ok(())\n}\n\n#[expect(clippy::too_many_lines)]\nfn main() -> Result<()> {\n    color_eyre::config::HookBuilder::default()\n        .display_location_section(false)\n        .display_env_section(false)\n        .install()?;\n\n    rustls::crypto::ring::default_provider()\n        .install_default()\n        .map_err(|_| eyre!(\"could not install ring as the default crypto provider\"))?;\n\n    #[cfg(feature = \"dhat\")]\n    let _profiler = dhat::Profiler::new_heap();\n\n    let args = Opt::parse();\n\n    // A channel of expressions to run.\n    let (sender, receiver) = async_channel::unbounded();\n    let printer = SharedExternalPrinterLogger::new();\n\n    let executor = Rc::new(Executor::new(printer.clone()));\n    let loader = Rc::new(SimpleModuleLoader::new(&args.root).map_err(|e| eyre!(e.to_string()))?);\n    let context = &mut ContextBuilder::new()\n        .job_executor(executor.clone())\n        .module_loader(loader.clone())\n        .build()\n        .map_err(|e| eyre!(e.to_string()))?;\n\n    // Strict mode\n    context.strict(args.strict);\n\n    // Add `console`.\n    add_runtime(printer.clone(), context);\n\n    // Trace Output\n    context.set_trace(args.trace);\n\n    if args.debug_object {\n        init_boa_debug_object(context);\n    }\n\n    // Configure optimizer options\n    let mut optimizer_options = OptimizerOptions::empty();\n    optimizer_options.set(OptimizerOptions::STATISTICS, args.optimizer_statistics);\n    optimizer_options.set(OptimizerOptions::OPTIMIZE_ALL, args.optimize);\n    context.set_optimizer_options(optimizer_options);\n\n    if !args.files.is_empty() {\n        evaluate_files(&args, context, &loader, &printer)?;\n\n        if let Some(ref expr) = args.expression {\n            evaluate_expr(expr, &args, context, &printer)?;\n        }\n\n        return Ok(());\n    } else if let Some(ref expr) = args.expression {\n        evaluate_expr(expr, &args, context, &printer)?;\n        return Ok(());\n    } else if !io::stdin().is_terminal() {\n        let mut input = String::new();\n        io::stdin()\n            .read_to_string(&mut input)\n            .wrap_err(\"failed to read stdin\")?;\n        return if input.is_empty() {\n            Ok(())\n        } else {\n            evaluate_expr(&input, &args, context, &printer)\n        };\n    }\n\n    // Print the welcome banner unless --quiet is passed.\n    if !args.quiet {\n        let version = env!(\"CARGO_PKG_VERSION\");\n        println!(\"{}\", format!(\"Welcome to Boa v{version}\").bold());\n        println!(\n            \"Type {} for more information, {} to exit.\",\n            \"\\\".help\\\"\".green(),\n            \"Ctrl+D\".green()\n        );\n        println!();\n    }\n\n    let handle = start_readline_thread(sender, printer.clone(), args.vi_mode, args.strict);\n\n    // TODO: Replace the `__BOA_LOAD_FILE__` string sentinel with a `CliCommand` enum\n    // (e.g. `Exec(String)` / `LoadFile(PathBuf)`) for type-safe cross-thread communication.\n    let exec = executor.clone();\n    let eval_loop = NativeAsyncJob::new(async move |context| {\n        while let Ok(line) = receiver.recv().await {\n            let printer_clone = printer.clone();\n\n            if let Some(file_path) = line.strip_prefix(\"__BOA_LOAD_FILE__:\") {\n                let path = Path::new(file_path);\n                if path.exists() {\n                    let mut context = context.borrow_mut();\n                    if let Err(e) =\n                        evaluate_file(path, &args, &mut context, &loader, &printer_clone)\n                    {\n                        printer_clone.print(format!(\"{e}\\n\"));\n                    }\n                } else {\n                    printer_clone.print(format!(\n                        \"{} file '{}' not found\\n\",\n                        \"Error:\".red().bold(),\n                        file_path\n                    ));\n                }\n                continue;\n            }\n\n            // schedule a new evaluation job that can run asynchronously\n            // with the other evaluations.\n            let eval_script = NativeAsyncJob::new(async move |context| {\n                let script =\n                    match Script::parse(Source::from_bytes(&line), None, &mut context.borrow_mut())\n                    {\n                        Ok(script) => script,\n                        Err(err) => {\n                            printer_clone.print(uncaught_error(&err));\n                            return Ok(JsValue::undefined());\n                        }\n                    };\n\n                // TODO: would be better to avoid blocking until the\n                // script finishes executing, but need to think about how\n                // to change the API of `evaluate_async` to enable that.\n                // (or I guess we could also implement web workers)\n                let value = match script.evaluate(&mut context.borrow_mut()) {\n                    Ok(value) => value,\n                    Err(err) => {\n                        printer_clone.print(uncaught_job_error(&err));\n                        return Ok(JsValue::undefined());\n                    }\n                };\n\n                printer_clone.print(format!(\"{}\\n\", value.display()));\n\n                Ok(JsValue::undefined())\n            });\n            context.borrow_mut().enqueue_job(eval_script.into());\n        }\n        // channel was closed, so clear the executor queue to abort all\n        // pending jobs and exit.\n        exec.clear();\n        Ok(JsValue::undefined())\n    });\n    context.enqueue_job(eval_loop.into());\n\n    let result = context.run_jobs().map_err(|e| e.into_erased(context));\n\n    handle.join().expect(\"failed to join thread\");\n\n    Ok(result?)\n}\n\nfn readline_thread_main(\n    sender: &Sender<String>,\n    printer_out: &SharedExternalPrinterLogger,\n    vi_mode: bool,\n    strict: bool,\n) -> Result<()> {\n    let config = Config::builder()\n        .keyseq_timeout(Some(1))\n        .edit_mode(if vi_mode {\n            EditMode::Vi\n        } else {\n            EditMode::Emacs\n        })\n        .build();\n\n    let mut editor =\n        Editor::with_config(config).wrap_err(\"failed to set the editor configuration\")?;\n    if let Ok(printer) = editor.create_external_printer() {\n        printer_out.set(printer);\n    }\n\n    // Check if the history file exists. If it doesn't, create it.\n    OpenOptions::new()\n        .read(true)\n        .write(true)\n        .create(true)\n        .truncate(false)\n        .open(CLI_HISTORY)?;\n    editor\n        .load_history(CLI_HISTORY)\n        .wrap_err(\"failed to read history file `.boa_history`\")?;\n    let readline = \">> \";\n    editor.set_helper(Some(helper::RLHelper::new(readline, strict)));\n\n    loop {\n        match editor.readline(readline).map(|l| l.trim().to_string()) {\n            Ok(line) if line == \".exit\" => break,\n            Err(ReadlineError::Eof) => break,\n            Err(ReadlineError::Interrupted) => {\n                println!(\"(To exit, press Ctrl+D or type .exit)\");\n            }\n\n            Ok(ref line) if line == \".help\" => {\n                println!(\"REPL Commands:\");\n                println!(\"  {}       Show this help message\", \".help\".green());\n                println!(\"  {}       Exit the REPL\", \".exit\".green());\n                println!(\"  {}      Clear the terminal screen\", \".clear\".green());\n                println!(\n                    \"  {} Load and evaluate a JavaScript file\",\n                    \".load <file>\".green()\n                );\n                println!();\n                println!(\"Press {} to abort the current expression.\", \"Ctrl+C\".bold());\n                println!(\"Press {} to exit the REPL.\", \"Ctrl+D\".bold());\n            }\n\n            Ok(ref line) if line == \".clear\" => {\n                print!(\"\\x1B[2J\\x1B[3J\\x1B[1;1H\");\n                io::stdout().flush().ok();\n            }\n\n            Ok(ref line) if line == \".load\" || line.starts_with(\".load \") => {\n                let file = line.strip_prefix(\".load\").unwrap_or(\"\").trim();\n                if file.is_empty() {\n                    eprintln!(\"{}\", \"Usage: .load <filename>\".yellow());\n                } else {\n                    sender.send_blocking(format!(\"__BOA_LOAD_FILE__:{file}\"))?;\n                    thread::sleep(Duration::from_millis(10));\n                }\n            }\n\n            Ok(line) => {\n                editor.add_history_entry(&line).map_err(io::Error::other)?;\n                sender.send_blocking(line)?;\n                thread::sleep(Duration::from_millis(10));\n            }\n\n            Err(err) => {\n                let final_error = eyre!(\"could not read the next line of the input\");\n                let final_error = if let Err(e) = editor.save_history(CLI_HISTORY) {\n                    final_error.error(e)\n                } else {\n                    final_error\n                };\n                return Err(final_error.error(err));\n            }\n        }\n    }\n\n    editor.save_history(CLI_HISTORY)?;\n\n    Ok(())\n}\n\n/// Create the readline thread which sends lines from stdin back to the main thread.\nfn start_readline_thread(\n    sender: Sender<String>,\n    printer_out: SharedExternalPrinterLogger,\n    vi_mode: bool,\n    strict: bool,\n) -> thread::JoinHandle<()> {\n    thread::spawn(\n        move || match readline_thread_main(&sender, &printer_out, vi_mode, strict) {\n            Ok(()) => {}\n            Err(e) => eprintln!(\"readline thread failed: {e}\"),\n        },\n    )\n}\n\n/// Adds the CLI runtime to the context with default options.\nfn add_runtime(printer: SharedExternalPrinterLogger, context: &mut Context) {\n    boa_runtime::register(\n        (\n            boa_runtime::extensions::ConsoleExtension(printer),\n            #[cfg(feature = \"fetch\")]\n            boa_runtime::extensions::FetchExtension(\n                boa_runtime::fetch::BlockingReqwestFetcher::default(),\n            ),\n        ),\n        None,\n        context,\n    )\n    .expect(\"should not fail while registering the runtime\");\n}\n"
  },
  {
    "path": "clippy.toml",
    "content": "doc-valid-idents = ['ECMAScript', 'JavaScript', 'SpiderMonkey', 'GitHub']\nallow-print-in-tests = true\ndisallowed-methods = [\n  { path = \"str::to_ascii_lowercase\", reason = \"To avoid memory allocation, use `cow_utils::CowUtils::cow_to_ascii_lowercase` instead.\" },\n  { path = \"str::to_ascii_uppercase\", reason = \"To avoid memory allocation, use `cow_utils::CowUtils::cow_to_ascii_uppercase` instead.\" },\n  { path = \"str::to_lowercase\", reason = \"To avoid memory allocation, use `cow_utils::CowUtils::cow_to_lowercase` instead.\" },\n  { path = \"str::to_uppercase\", reason = \"To avoid memory allocation, use `cow_utils::CowUtils::cow_to_uppercase` instead.\" },\n  { path = \"str::replace\", reason = \"To avoid memory allocation, use `cow_utils::CowUtils::cow_replace` instead.\" },\n  { path = \"str::replacen\", reason = \"To avoid memory allocation, use `cow_utils::CowUtils::cow_replacen` instead.\" },\n]\n"
  },
  {
    "path": "core/ast/ABOUT.md",
    "content": "# About Boa\n\nBoa is an open-source, experimental ECMAScript Engine written in Rust for\nlexing, parsing and executing ECMAScript/JavaScript. Currently, Boa supports some\nof the [language][boa-conformance]. More information can be viewed at [Boa's\nwebsite][boa-web].\n\nTry out the most recent release with Boa's live demo\n[playground][boa-playground].\n\n## Boa Crates\n\n- [**`boa_cli`**][cli] - Boa's CLI && REPL implementation\n- [**`boa_ast`**][ast] - Boa's ECMAScript Abstract Syntax Tree.\n- [**`boa_engine`**][engine] - Boa's implementation of ECMAScript builtin objects and execution.\n- [**`boa_gc`**][gc] - Boa's garbage collector.\n- [**`boa_icu_provider`**][icu] - Boa's ICU4X data provider.\n- [**`boa_interner`**][interner] - Boa's string interner.\n- [**`boa_macros`**][macros] - Boa's macros.\n- [**`boa_parser`**][parser] - Boa's lexer and parser.\n- [**`boa_runtime`**][runtime] - Boa's `WebAPI` features.\n- [**`boa_string`**][string] - Boa's ECMAScript string implementation.\n- [**`boa_wintertc`**][wintertc] - Boa's `WinterTC` (TC55) Minimum Common Web API implementation.\n- [**`tag_ptr`**][tag_ptr] - Utility library that enables a pointer to be associated with a tag of type `usize`.\n- [**`small_btree`**][small_btree] - Utility library that adds the `SmallBTreeMap` data structure.\n\n[boa-conformance]: https://boajs.dev/conformance\n[boa-web]: https://boajs.dev/\n[boa-playground]: https://boajs.dev/playground\n[ast]: https://docs.rs/boa_ast/latest/boa_ast/index.html\n[engine]: https://docs.rs/boa_engine/latest/boa_engine/index.html\n[gc]: https://docs.rs/boa_gc/latest/boa_gc/index.html\n[interner]: https://docs.rs/boa_interner/latest/boa_interner/index.html\n[parser]: https://docs.rs/boa_parser/latest/boa_parser/index.html\n[icu]: https://docs.rs/boa_icu_provider/latest/boa_icu_provider/index.html\n[runtime]: https://docs.rs/boa_runtime/latest/boa_runtime/index.html\n[string]: https://docs.rs/boa_string/latest/boa_string/index.html\n[wintertc]: https://docs.rs/boa_wintertc/latest/boa_wintertc/index.html\n[tag_ptr]: https://docs.rs/tag_ptr/latest/tag_ptr/index.html\n[small_btree]: https://docs.rs/small_btree/latest/small_btree/index.html\n[macros]: https://docs.rs/boa_macros/latest/boa_macros/index.html\n[cli]: https://crates.io/crates/boa_cli\n"
  },
  {
    "path": "core/ast/Cargo.toml",
    "content": "[package]\nname = \"boa_ast\"\ndescription = \"Abstract Syntax Tree definition for the Boa JavaScript engine.\"\nkeywords = [\"javascript\", \"js\", \"syntax\", \"ast\"]\ncategories = [\"parser-implementations\", \"compilers\"]\nversion.workspace = true\nedition.workspace = true\nauthors.workspace = true\nlicense.workspace = true\nrepository.workspace = true\nrust-version.workspace = true\n\n[features]\nannex-b = []\nserde = [\"dep:serde\", \"boa_interner/serde\", \"bitflags/serde\", \"num-bigint/serde\"]\narbitrary = [\"dep:arbitrary\", \"boa_interner/arbitrary\", \"num-bigint/arbitrary\"]\n\n[dependencies]\nboa_interner.workspace = true\nboa_macros.workspace = true\nboa_string.workspace = true\nrustc-hash = { workspace = true, features = [\"std\"] }\nbitflags.workspace = true\nnum-bigint.workspace = true\nserde = { workspace = true, features = [\"derive\"], optional = true }\narbitrary = { workspace = true, features = [\"derive\"], optional = true }\nindexmap.workspace = true\nstrum.workspace = true\n\n[lints]\nworkspace = true\n\n[package.metadata.docs.rs]\nall-features = true\n"
  },
  {
    "path": "core/ast/src/declaration/export.rs",
    "content": "//! Export declaration AST nodes.\n//!\n//! This module contains `export` declaration AST nodes.\n//!\n//! More information:\n//!  - [MDN documentation][mdn]\n//!  - [ECMAScript specification][spec]\n//!\n//! [spec]: https://tc39.es/ecma262/#sec-exports\n//! [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/export\n\nuse super::{ImportAttribute, ModuleSpecifier, VarDeclaration};\nuse crate::{\n    Declaration, Expression,\n    function::{\n        AsyncFunctionDeclaration, AsyncGeneratorDeclaration, ClassDeclaration, FunctionDeclaration,\n        GeneratorDeclaration,\n    },\n    visitor::{VisitWith, Visitor, VisitorMut},\n};\nuse boa_interner::Sym;\nuse std::ops::ControlFlow;\n\n/// The kind of re-export in an [`ExportDeclaration`].\n#[cfg_attr(feature = \"serde\", derive(serde::Serialize, serde::Deserialize))]\n#[cfg_attr(feature = \"arbitrary\", derive(arbitrary::Arbitrary))]\n#[derive(Clone, Debug, PartialEq, Eq)]\npub enum ReExportKind {\n    /// Namespaced Re-export (`export * as name from \"module-name\"`).\n    Namespaced {\n        /// Reexported name for the imported module.\n        name: Option<Sym>,\n    },\n    /// Re-export list (`export { export1, export2 as alias2 } from \"module-name\"`).\n    Named {\n        /// List of the required re-exports of the re-exported module.\n        names: Box<[ExportSpecifier]>,\n    },\n}\n\nimpl VisitWith for ReExportKind {\n    fn visit_with<'a, V>(&'a self, visitor: &mut V) -> ControlFlow<V::BreakTy>\n    where\n        V: Visitor<'a>,\n    {\n        match self {\n            Self::Namespaced { name: Some(name) } => visitor.visit_sym(name),\n            Self::Namespaced { name: None } => ControlFlow::Continue(()),\n            Self::Named { names } => {\n                for name in &**names {\n                    visitor.visit_export_specifier(name)?;\n                }\n                ControlFlow::Continue(())\n            }\n        }\n    }\n\n    fn visit_with_mut<'a, V>(&'a mut self, visitor: &mut V) -> ControlFlow<V::BreakTy>\n    where\n        V: VisitorMut<'a>,\n    {\n        match self {\n            Self::Namespaced { name: Some(name) } => visitor.visit_sym_mut(name),\n            Self::Namespaced { name: None } => ControlFlow::Continue(()),\n            Self::Named { names } => {\n                for name in &mut **names {\n                    visitor.visit_export_specifier_mut(name)?;\n                }\n                ControlFlow::Continue(())\n            }\n        }\n    }\n}\n\n/// An export declaration AST node.\n///\n/// More information:\n///  - [ECMAScript specification][spec]\n///\n/// [spec]: https://tc39.es/ecma262/#prod-ExportDeclaration\n#[cfg_attr(feature = \"serde\", derive(serde::Serialize, serde::Deserialize))]\n#[derive(Clone, Debug, PartialEq)]\npub enum ExportDeclaration {\n    /// Re-export.\n    ReExport {\n        /// The kind of reexport declared.\n        kind: ReExportKind,\n        /// Reexported module specifier.\n        specifier: ModuleSpecifier,\n        /// Re-export attributes.\n        attributes: Box<[ImportAttribute]>,\n    },\n    /// List of exports.\n    List(Box<[ExportSpecifier]>),\n    /// Variable statement export.\n    VarStatement(VarDeclaration),\n    /// Declaration export.\n    Declaration(Declaration),\n    /// Default function export.\n    DefaultFunctionDeclaration(FunctionDeclaration),\n    /// Default generator export.\n    DefaultGeneratorDeclaration(GeneratorDeclaration),\n    /// Default async function export.\n    DefaultAsyncFunctionDeclaration(AsyncFunctionDeclaration),\n    /// Default async generator export.\n    DefaultAsyncGeneratorDeclaration(AsyncGeneratorDeclaration),\n    /// Default class declaration export.\n    DefaultClassDeclaration(Box<ClassDeclaration>),\n    /// Default assignment expression export.\n    DefaultAssignmentExpression(Expression),\n}\n\nimpl VisitWith for ExportDeclaration {\n    fn visit_with<'a, V>(&'a self, visitor: &mut V) -> ControlFlow<V::BreakTy>\n    where\n        V: Visitor<'a>,\n    {\n        match self {\n            Self::ReExport {\n                specifier,\n                kind,\n                attributes,\n            } => {\n                visitor.visit_module_specifier(specifier)?;\n                visitor.visit_re_export_kind(kind)?;\n                for attribute in &**attributes {\n                    visitor.visit_import_attribute(attribute)?;\n                }\n                ControlFlow::Continue(())\n            }\n            Self::List(list) => {\n                for item in &**list {\n                    visitor.visit_export_specifier(item)?;\n                }\n                ControlFlow::Continue(())\n            }\n            Self::VarStatement(var) => visitor.visit_var_declaration(var),\n            Self::Declaration(decl) => visitor.visit_declaration(decl),\n            Self::DefaultFunctionDeclaration(f) => visitor.visit_function_declaration(f),\n            Self::DefaultGeneratorDeclaration(g) => visitor.visit_generator_declaration(g),\n            Self::DefaultAsyncFunctionDeclaration(af) => {\n                visitor.visit_async_function_declaration(af)\n            }\n            Self::DefaultAsyncGeneratorDeclaration(ag) => {\n                visitor.visit_async_generator_declaration(ag)\n            }\n            Self::DefaultClassDeclaration(c) => visitor.visit_class_declaration(c),\n            Self::DefaultAssignmentExpression(expr) => visitor.visit_expression(expr),\n        }\n    }\n\n    fn visit_with_mut<'a, V>(&'a mut self, visitor: &mut V) -> ControlFlow<V::BreakTy>\n    where\n        V: VisitorMut<'a>,\n    {\n        match self {\n            Self::ReExport {\n                specifier,\n                kind,\n                attributes,\n            } => {\n                visitor.visit_module_specifier_mut(specifier)?;\n                visitor.visit_re_export_kind_mut(kind)?;\n                for attribute in &mut **attributes {\n                    visitor.visit_import_attribute_mut(attribute)?;\n                }\n                ControlFlow::Continue(())\n            }\n            Self::List(list) => {\n                for item in &mut **list {\n                    visitor.visit_export_specifier_mut(item)?;\n                }\n                ControlFlow::Continue(())\n            }\n            Self::VarStatement(var) => visitor.visit_var_declaration_mut(var),\n            Self::Declaration(decl) => visitor.visit_declaration_mut(decl),\n            Self::DefaultFunctionDeclaration(f) => visitor.visit_function_declaration_mut(f),\n            Self::DefaultGeneratorDeclaration(g) => visitor.visit_generator_declaration_mut(g),\n            Self::DefaultAsyncFunctionDeclaration(af) => {\n                visitor.visit_async_function_declaration_mut(af)\n            }\n            Self::DefaultAsyncGeneratorDeclaration(ag) => {\n                visitor.visit_async_generator_declaration_mut(ag)\n            }\n            Self::DefaultClassDeclaration(c) => visitor.visit_class_declaration_mut(c),\n            Self::DefaultAssignmentExpression(expr) => visitor.visit_expression_mut(expr),\n        }\n    }\n}\n\n/// Export specifier\n///\n/// More information:\n///  - [ECMAScript specification][spec]\n///\n/// [spec]: https://tc39.es/ecma262/#prod-ExportSpecifier\n#[cfg_attr(feature = \"serde\", derive(serde::Serialize, serde::Deserialize))]\n#[cfg_attr(feature = \"arbitrary\", derive(arbitrary::Arbitrary))]\n#[derive(Clone, Debug, Copy, PartialEq, Eq)]\npub struct ExportSpecifier {\n    alias: Sym,\n    private_name: Sym,\n    string_literal: bool,\n}\n\nimpl ExportSpecifier {\n    /// Creates a new [`ExportSpecifier`].\n    #[inline]\n    #[must_use]\n    pub const fn new(alias: Sym, private_name: Sym, string_literal: bool) -> Self {\n        Self {\n            alias,\n            private_name,\n            string_literal,\n        }\n    }\n\n    /// Gets the original alias.\n    #[inline]\n    #[must_use]\n    pub const fn alias(self) -> Sym {\n        self.alias\n    }\n\n    /// Gets the private name of the export inside the module.\n    #[inline]\n    #[must_use]\n    pub const fn private_name(self) -> Sym {\n        self.private_name\n    }\n\n    /// Returns `true` if the private name of the specifier was a `StringLiteral`.\n    #[inline]\n    #[must_use]\n    pub const fn string_literal(&self) -> bool {\n        self.string_literal\n    }\n}\n\nimpl VisitWith for ExportSpecifier {\n    fn visit_with<'a, V>(&'a self, visitor: &mut V) -> ControlFlow<V::BreakTy>\n    where\n        V: Visitor<'a>,\n    {\n        visitor.visit_sym(&self.alias)?;\n        visitor.visit_sym(&self.private_name)\n    }\n\n    fn visit_with_mut<'a, V>(&'a mut self, visitor: &mut V) -> ControlFlow<V::BreakTy>\n    where\n        V: VisitorMut<'a>,\n    {\n        visitor.visit_sym_mut(&mut self.alias)?;\n        visitor.visit_sym_mut(&mut self.private_name)\n    }\n}\n\n/// The name under which a reexported binding is exported by a module.\n///\n/// This differs slightly from the spec, since `[[ImportName]]` can be either a name, `all-but-default`\n/// or `all`, but the last two exports can be identified with the `export_name` field from\n/// [`ExportEntry`], which joins both variants into a single `Star` variant.\n#[derive(Debug, Clone, Copy)]\npub enum ReExportImportName {\n    /// A binding of the imported module.\n    Name(Sym),\n    /// All exports of the module.\n    Star,\n}\n\n/// [`ExportEntry`][spec] record.\n///\n/// [spec]: https://tc39.es/ecma262/#table-exportentry-records\n#[derive(Debug, Clone)]\npub enum ExportEntry {\n    /// An ordinary export entry\n    Ordinary(LocalExportEntry),\n    /// A star reexport entry.\n    StarReExport {\n        /// The module from where this reexport will import.\n        module_request: Sym,\n        /// The import attributes for this reexport.\n        attributes: Box<[ImportAttribute]>,\n    },\n    /// A reexport entry with an export name.\n    ReExport(IndirectExportEntry),\n}\n\nimpl From<IndirectExportEntry> for ExportEntry {\n    fn from(v: IndirectExportEntry) -> Self {\n        Self::ReExport(v)\n    }\n}\n\nimpl From<LocalExportEntry> for ExportEntry {\n    fn from(v: LocalExportEntry) -> Self {\n        Self::Ordinary(v)\n    }\n}\n\n/// A local export entry\n#[derive(Debug, Clone, Copy)]\npub struct LocalExportEntry {\n    local_name: Sym,\n    export_name: Sym,\n}\n\nimpl LocalExportEntry {\n    /// Creates a new `LocalExportEntry`.\n    #[must_use]\n    pub const fn new(local_name: Sym, export_name: Sym) -> Self {\n        Self {\n            local_name,\n            export_name,\n        }\n    }\n\n    /// Gets the local name of this export entry.\n    #[must_use]\n    pub const fn local_name(&self) -> Sym {\n        self.local_name\n    }\n\n    /// Gets the export name of this export entry.\n    #[must_use]\n    pub const fn export_name(&self) -> Sym {\n        self.export_name\n    }\n}\n\n/// A reexported export entry.\n#[derive(Debug, Clone)]\npub struct IndirectExportEntry {\n    module_request: Sym,\n    import_name: ReExportImportName,\n    export_name: Sym,\n    attributes: Box<[ImportAttribute]>,\n}\n\nimpl IndirectExportEntry {\n    /// Creates a new `IndirectExportEntry`.\n    #[must_use]\n    pub fn new(\n        module_request: Sym,\n        import_name: ReExportImportName,\n        export_name: Sym,\n        attributes: Box<[ImportAttribute]>,\n    ) -> Self {\n        Self {\n            module_request,\n            import_name,\n            export_name,\n            attributes,\n        }\n    }\n\n    /// Gets the module from where this entry reexports.\n    #[must_use]\n    pub const fn module_request(&self) -> Sym {\n        self.module_request\n    }\n\n    /// Gets the import name of the reexport.\n    #[must_use]\n    pub const fn import_name(&self) -> ReExportImportName {\n        self.import_name\n    }\n\n    /// Gets the public alias of the reexport.\n    #[must_use]\n    pub const fn export_name(&self) -> Sym {\n        self.export_name\n    }\n\n    /// Gets the import attributes.\n    #[must_use]\n    pub fn attributes(&self) -> &[ImportAttribute] {\n        &self.attributes\n    }\n}\n"
  },
  {
    "path": "core/ast/src/declaration/import.rs",
    "content": "//! Import declaration AST nodes.\n//!\n//! This module contains `import` declaration AST nodes.\n//!\n//! More information:\n//! - [MDN documentation][mdn]\n//!  - [ECMAScript specification][spec]\n//!\n//! [spec]: https://tc39.es/ecma262/#sec-imports\n//! [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/import\n\nuse std::ops::ControlFlow;\n\nuse crate::{\n    expression::Identifier,\n    visitor::{VisitWith, Visitor, VisitorMut},\n};\nuse boa_interner::Sym;\n\nuse super::{ImportAttribute, ModuleSpecifier};\n\n/// The kind of import in an [`ImportDeclaration`].\n#[cfg_attr(feature = \"serde\", derive(serde::Serialize, serde::Deserialize))]\n#[cfg_attr(feature = \"arbitrary\", derive(arbitrary::Arbitrary))]\n#[derive(Clone, Debug, PartialEq, Eq)]\npub enum ImportKind {\n    /// Default (`import defaultName from \"module-name\"`) or unnamed (`import \"module-name\"`).\n    DefaultOrUnnamed,\n    /// Namespaced import (`import * as name from \"module-name\"`).\n    Namespaced {\n        /// Binding for the namespace created from the exports of the imported module.\n        binding: Identifier,\n    },\n    /// Import list (`import { export1, export2 as alias2 } from \"module-name\"`).\n    Named {\n        /// List of the required exports of the imported module.\n        names: Box<[ImportSpecifier]>,\n    },\n}\n\nimpl VisitWith for ImportKind {\n    fn visit_with<'a, V>(&'a self, visitor: &mut V) -> ControlFlow<V::BreakTy>\n    where\n        V: Visitor<'a>,\n    {\n        match self {\n            Self::DefaultOrUnnamed => ControlFlow::Continue(()),\n            Self::Namespaced { binding } => visitor.visit_identifier(binding),\n            Self::Named { names } => {\n                for name in &**names {\n                    visitor.visit_import_specifier(name)?;\n                }\n                ControlFlow::Continue(())\n            }\n        }\n    }\n\n    fn visit_with_mut<'a, V>(&'a mut self, visitor: &mut V) -> ControlFlow<V::BreakTy>\n    where\n        V: VisitorMut<'a>,\n    {\n        match self {\n            Self::DefaultOrUnnamed => ControlFlow::Continue(()),\n            Self::Namespaced { binding } => visitor.visit_identifier_mut(binding),\n            Self::Named { names } => {\n                for name in &mut **names {\n                    visitor.visit_import_specifier_mut(name)?;\n                }\n                ControlFlow::Continue(())\n            }\n        }\n    }\n}\n\n/// An import declaration AST node.\n///\n/// More information:\n///  - [ECMAScript specification][spec]\n///\n/// [spec]: https://tc39.es/ecma262/#prod-ImportDeclaration\n#[cfg_attr(feature = \"serde\", derive(serde::Serialize, serde::Deserialize))]\n#[derive(Clone, Debug, PartialEq, Eq)]\npub struct ImportDeclaration {\n    /// Binding for the default export of `specifier`.\n    default: Option<Identifier>,\n    /// See [`ImportKind`].\n    kind: ImportKind,\n    /// Module specifier.\n    specifier: ModuleSpecifier,\n    /// Import attributes.\n    attributes: Box<[ImportAttribute]>,\n}\n\nimpl ImportDeclaration {\n    /// Creates a new import declaration.\n    #[inline]\n    #[must_use]\n    pub fn new(\n        default: Option<Identifier>,\n        kind: ImportKind,\n        specifier: ModuleSpecifier,\n        attributes: Box<[ImportAttribute]>,\n    ) -> Self {\n        Self {\n            default,\n            kind,\n            specifier,\n            attributes,\n        }\n    }\n\n    /// Gets the binding for the default export of the module.\n    #[inline]\n    #[must_use]\n    pub const fn default(&self) -> Option<Identifier> {\n        self.default\n    }\n\n    /// Gets the module specifier of the import declaration.\n    #[inline]\n    #[must_use]\n    pub const fn specifier(&self) -> ModuleSpecifier {\n        self.specifier\n    }\n\n    /// Gets the import kind of the import declaration.\n    #[inline]\n    #[must_use]\n    pub const fn kind(&self) -> &ImportKind {\n        &self.kind\n    }\n\n    /// Gets the import attributes of the import declaration.\n    #[inline]\n    #[must_use]\n    pub const fn attributes(&self) -> &[ImportAttribute] {\n        &self.attributes\n    }\n}\n\nimpl VisitWith for ImportDeclaration {\n    fn visit_with<'a, V>(&'a self, visitor: &mut V) -> ControlFlow<V::BreakTy>\n    where\n        V: Visitor<'a>,\n    {\n        if let Some(default) = &self.default {\n            visitor.visit_identifier(default)?;\n        }\n        visitor.visit_import_kind(&self.kind)?;\n        visitor.visit_module_specifier(&self.specifier)?;\n        for attribute in &*self.attributes {\n            visitor.visit_import_attribute(attribute)?;\n        }\n        ControlFlow::Continue(())\n    }\n\n    fn visit_with_mut<'a, V>(&'a mut self, visitor: &mut V) -> ControlFlow<V::BreakTy>\n    where\n        V: VisitorMut<'a>,\n    {\n        if let Some(default) = &mut self.default {\n            visitor.visit_identifier_mut(default)?;\n        }\n        visitor.visit_import_kind_mut(&mut self.kind)?;\n        visitor.visit_module_specifier_mut(&mut self.specifier)?;\n        for attribute in &mut *self.attributes {\n            visitor.visit_import_attribute_mut(attribute)?;\n        }\n        ControlFlow::Continue(())\n    }\n}\n\n/// Import specifier\n///\n/// More information:\n///  - [ECMAScript specification][spec]\n///\n/// [spec]: https://tc39.es/ecma262/#prod-ImportSpecifier\n#[cfg_attr(feature = \"serde\", derive(serde::Serialize, serde::Deserialize))]\n#[cfg_attr(feature = \"arbitrary\", derive(arbitrary::Arbitrary))]\n#[derive(Debug, Clone, Copy, PartialEq, Eq)]\npub struct ImportSpecifier {\n    binding: Identifier,\n    export_name: Sym,\n}\n\nimpl ImportSpecifier {\n    /// Creates a new [`ImportSpecifier`].\n    #[inline]\n    #[must_use]\n    pub const fn new(binding: Identifier, export_name: Sym) -> Self {\n        Self {\n            binding,\n            export_name,\n        }\n    }\n\n    /// Gets the binding of the import specifier.\n    #[inline]\n    #[must_use]\n    pub const fn binding(self) -> Identifier {\n        self.binding\n    }\n\n    /// Gets the optional export name of the import.\n    #[inline]\n    #[must_use]\n    pub const fn export_name(self) -> Sym {\n        self.export_name\n    }\n}\n\nimpl VisitWith for ImportSpecifier {\n    fn visit_with<'a, V>(&'a self, visitor: &mut V) -> ControlFlow<V::BreakTy>\n    where\n        V: Visitor<'a>,\n    {\n        visitor.visit_identifier(&self.binding)?;\n        visitor.visit_sym(&self.export_name)\n    }\n\n    fn visit_with_mut<'a, V>(&'a mut self, visitor: &mut V) -> ControlFlow<V::BreakTy>\n    where\n        V: VisitorMut<'a>,\n    {\n        visitor.visit_identifier_mut(&mut self.binding)?;\n        visitor.visit_sym_mut(&mut self.export_name)\n    }\n}\n\n/// The name under which the imported binding is exported by a module.\n#[derive(Debug, Clone, Copy)]\npub enum ImportName {\n    /// The namespace object of the imported module.\n    Namespace,\n    /// A binding of the imported module.\n    Name(Sym),\n}\n\n/// [`ImportEntry`][spec] record.\n///\n/// [spec]: https://tc39.es/ecma262/#table-importentry-record-fields\n#[derive(Debug, Clone)]\npub struct ImportEntry {\n    module_request: Sym,\n    import_name: ImportName,\n    local_name: Identifier,\n    attributes: Box<[ImportAttribute]>,\n}\n\nimpl ImportEntry {\n    /// Creates a new `ImportEntry`.\n    #[must_use]\n    pub fn new(\n        module_request: Sym,\n        import_name: ImportName,\n        local_name: Identifier,\n        attributes: Box<[ImportAttribute]>,\n    ) -> Self {\n        Self {\n            module_request,\n            import_name,\n            local_name,\n            attributes,\n        }\n    }\n\n    /// Gets the module from where the binding must be imported.\n    #[must_use]\n    pub const fn module_request(&self) -> Sym {\n        self.module_request\n    }\n\n    /// Gets the import name of the imported binding.\n    #[must_use]\n    pub const fn import_name(&self) -> ImportName {\n        self.import_name\n    }\n\n    /// Gets the local name of the imported binding.\n    #[must_use]\n    pub const fn local_name(&self) -> Identifier {\n        self.local_name\n    }\n\n    /// Gets the import attributes.\n    #[must_use]\n    pub fn attributes(&self) -> &[ImportAttribute] {\n        &self.attributes\n    }\n}\n"
  },
  {
    "path": "core/ast/src/declaration/mod.rs",
    "content": "//! The [`Declaration`] Parse Node, as defined by the [spec].\n//!\n//! ECMAScript declarations include:\n//! - [Lexical][lex] declarations (`let`, `const`).\n//! - [Function][fun] declarations (`function`, `async function`).\n//! - [Class][class] declarations.\n//!\n//! See [*Difference between statements and declarations*][diff] for an explanation on why `Declaration`s\n//! and `Statement`s are distinct nodes.\n//!\n//! [spec]: https://tc39.es/ecma262/#prod-Declaration\n//! [lex]: https://tc39.es/ecma262/#prod-LexicalDeclaration\n//! [fun]: https://tc39.es/ecma262/#prod-HoistableDeclaration\n//! [class]: https://tc39.es/ecma262/#prod-ClassDeclaration\n//! [diff]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements#difference_between_statements_and_declarations\n\nuse super::function::{\n    AsyncFunctionDeclaration, AsyncGeneratorDeclaration, FunctionDeclaration, GeneratorDeclaration,\n};\nuse crate::{\n    function::ClassDeclaration,\n    visitor::{VisitWith, Visitor, VisitorMut},\n};\nuse boa_interner::{Interner, Sym, ToIndentedString, ToInternedString};\nuse core::ops::ControlFlow;\n\nmod export;\nmod import;\nmod variable;\n\npub use export::*;\npub use import::*;\npub use variable::*;\n\n/// The `Declaration` Parse Node.\n///\n/// See the [module level documentation][self] for more information.\n#[cfg_attr(feature = \"serde\", derive(serde::Serialize, serde::Deserialize))]\n#[cfg_attr(feature = \"arbitrary\", derive(arbitrary::Arbitrary))]\n#[derive(Clone, Debug, PartialEq)]\npub enum Declaration {\n    /// See [`FunctionDeclaration`]\n    FunctionDeclaration(FunctionDeclaration),\n\n    /// See [`GeneratorDeclaration`]\n    GeneratorDeclaration(GeneratorDeclaration),\n\n    /// See [`AsyncFunctionDeclaration`]\n    AsyncFunctionDeclaration(AsyncFunctionDeclaration),\n\n    /// See [`AsyncGeneratorDeclaration`]\n    AsyncGeneratorDeclaration(AsyncGeneratorDeclaration),\n\n    /// See [`ClassDeclaration`]\n    ClassDeclaration(Box<ClassDeclaration>),\n\n    /// See [`LexicalDeclaration`]\n    Lexical(LexicalDeclaration),\n}\n\nimpl ToIndentedString for Declaration {\n    fn to_indented_string(&self, interner: &Interner, indentation: usize) -> String {\n        match self {\n            Self::FunctionDeclaration(f) => f.to_indented_string(interner, indentation),\n            Self::GeneratorDeclaration(g) => g.to_indented_string(interner, indentation),\n            Self::AsyncFunctionDeclaration(af) => af.to_indented_string(interner, indentation),\n            Self::AsyncGeneratorDeclaration(ag) => ag.to_indented_string(interner, indentation),\n            Self::ClassDeclaration(c) => c.to_indented_string(interner, indentation),\n            Self::Lexical(l) => {\n                let mut s = l.to_interned_string(interner);\n                s.push(';');\n                s\n            }\n        }\n    }\n}\n\nimpl VisitWith for Declaration {\n    fn visit_with<'a, V>(&'a self, visitor: &mut V) -> ControlFlow<V::BreakTy>\n    where\n        V: Visitor<'a>,\n    {\n        match self {\n            Self::FunctionDeclaration(f) => visitor.visit_function_declaration(f),\n            Self::GeneratorDeclaration(g) => visitor.visit_generator_declaration(g),\n            Self::AsyncFunctionDeclaration(af) => visitor.visit_async_function_declaration(af),\n            Self::AsyncGeneratorDeclaration(ag) => visitor.visit_async_generator_declaration(ag),\n            Self::ClassDeclaration(c) => visitor.visit_class_declaration(c),\n            Self::Lexical(ld) => visitor.visit_lexical_declaration(ld),\n        }\n    }\n\n    fn visit_with_mut<'a, V>(&'a mut self, visitor: &mut V) -> ControlFlow<V::BreakTy>\n    where\n        V: VisitorMut<'a>,\n    {\n        match self {\n            Self::FunctionDeclaration(f) => visitor.visit_function_declaration_mut(f),\n            Self::GeneratorDeclaration(g) => visitor.visit_generator_declaration_mut(g),\n            Self::AsyncFunctionDeclaration(af) => visitor.visit_async_function_declaration_mut(af),\n            Self::AsyncGeneratorDeclaration(ag) => {\n                visitor.visit_async_generator_declaration_mut(ag)\n            }\n            Self::ClassDeclaration(c) => visitor.visit_class_declaration_mut(c),\n            Self::Lexical(ld) => visitor.visit_lexical_declaration_mut(ld),\n        }\n    }\n}\n\n/// Module specifier.\n///\n/// This is equivalent to the [`ModuleSpecifier`] production.\n///\n/// [`FromClause`]: https://tc39.es/ecma262/#prod-ModuleSpecifier\n#[cfg_attr(feature = \"serde\", derive(serde::Serialize, serde::Deserialize))]\n#[cfg_attr(feature = \"arbitrary\", derive(arbitrary::Arbitrary))]\n#[derive(Clone, Debug, Copy, PartialEq, Eq)]\npub struct ModuleSpecifier {\n    module: Sym,\n}\n\nimpl ModuleSpecifier {\n    /// Creates a `ModuleSpecifier` from a `Sym`.\n    #[must_use]\n    pub const fn new(module: Sym) -> Self {\n        Self { module }\n    }\n\n    /// Gets the inner `Sym` of the module specifier.\n    #[inline]\n    #[must_use]\n    pub const fn sym(self) -> Sym {\n        self.module\n    }\n}\n\nimpl From<Sym> for ModuleSpecifier {\n    #[inline]\n    fn from(module: Sym) -> Self {\n        Self::new(module)\n    }\n}\n\nimpl VisitWith for ModuleSpecifier {\n    fn visit_with<'a, V>(&'a self, visitor: &mut V) -> ControlFlow<V::BreakTy>\n    where\n        V: Visitor<'a>,\n    {\n        visitor.visit_sym(&self.module)\n    }\n\n    fn visit_with_mut<'a, V>(&'a mut self, visitor: &mut V) -> ControlFlow<V::BreakTy>\n    where\n        V: VisitorMut<'a>,\n    {\n        visitor.visit_sym_mut(&mut self.module)\n    }\n}\n\n/// An import attribute entry in an [`ImportDeclaration`].\n///\n/// This is a key-value pair, where the key is an identifier or string literal,\n/// and the value is a string literal.\n///\n/// More information:\n///  - [ECMAScript specification][spec]\n///\n/// [spec]: https://tc39.es/ecma262/#sec-imports\n#[cfg_attr(feature = \"serde\", derive(serde::Serialize, serde::Deserialize))]\n#[cfg_attr(feature = \"arbitrary\", derive(arbitrary::Arbitrary))]\n#[derive(Clone, Debug, Copy, PartialEq, Eq)]\npub struct ImportAttribute {\n    key: Sym,\n    value: Sym,\n}\n\nimpl ImportAttribute {\n    /// Creates a new `ImportAttribute`.\n    #[inline]\n    #[must_use]\n    pub const fn new(key: Sym, value: Sym) -> Self {\n        Self { key, value }\n    }\n\n    /// Gets the attribute key.\n    #[inline]\n    #[must_use]\n    pub const fn key(self) -> Sym {\n        self.key\n    }\n\n    /// Gets the attribute value.\n    #[inline]\n    #[must_use]\n    pub const fn value(self) -> Sym {\n        self.value\n    }\n}\n\nimpl VisitWith for ImportAttribute {\n    fn visit_with<'a, V>(&'a self, visitor: &mut V) -> ControlFlow<V::BreakTy>\n    where\n        V: Visitor<'a>,\n    {\n        visitor.visit_sym(&self.key)?;\n        visitor.visit_sym(&self.value)\n    }\n\n    fn visit_with_mut<'a, V>(&'a mut self, visitor: &mut V) -> ControlFlow<V::BreakTy>\n    where\n        V: VisitorMut<'a>,\n    {\n        visitor.visit_sym_mut(&mut self.key)?;\n        visitor.visit_sym_mut(&mut self.value)\n    }\n}\n"
  },
  {
    "path": "core/ast/src/declaration/variable.rs",
    "content": "//! Variable related declarations.\n\nuse super::Declaration;\nuse crate::{\n    Statement,\n    expression::{Expression, Identifier},\n    join_nodes,\n    pattern::Pattern,\n    visitor::{VisitWith, Visitor, VisitorMut},\n};\nuse boa_interner::{Interner, ToInternedString};\nuse core::{convert::TryFrom, fmt::Write as _, ops::ControlFlow};\n\n/// A [`var`][var] statement, also called [`VariableStatement`][varstmt] in the spec.\n///\n/// The scope of a variable declared with `var` is its current execution context, which is either\n/// the enclosing function or, for variables declared outside any function, global. If you\n/// re-declare a ECMAScript variable, it will not lose its value.\n///\n/// Although a bit confusing, `VarDeclaration`s are not considered [`Declaration`]s by the spec.\n/// This is partly because it has very different semantics from `let` and `const` declarations, but\n/// also because a `var` statement can be labelled just like any other [`Statement`]:\n///\n/// ```javascript\n/// label: var a = 5;\n/// a;\n/// ```\n///\n/// returns `5` as the value of the statement list, while:\n///\n/// ```javascript\n/// label: let a = 5;\n/// a;\n/// ```\n/// throws a `SyntaxError`.\n///\n/// `var` declarations, wherever they occur, are processed before any code is executed. This is\n/// called <code>[hoisting]</code>.\n///\n/// [var]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/var\n/// [varstmt]: https://tc39.es/ecma262/#prod-VariableStatement\n/// [hoisting]: https://developer.mozilla.org/en-US/docs/Glossary/Hoisting\n#[cfg_attr(feature = \"serde\", derive(serde::Serialize, serde::Deserialize))]\n#[cfg_attr(feature = \"arbitrary\", derive(arbitrary::Arbitrary))]\n#[derive(Clone, Debug, PartialEq)]\npub struct VarDeclaration(pub VariableList);\n\nimpl From<VarDeclaration> for Statement {\n    fn from(var: VarDeclaration) -> Self {\n        Self::Var(var)\n    }\n}\n\nimpl ToInternedString for VarDeclaration {\n    fn to_interned_string(&self, interner: &Interner) -> String {\n        format!(\"var {}\", self.0.to_interned_string(interner))\n    }\n}\n\nimpl VisitWith for VarDeclaration {\n    fn visit_with<'a, V>(&'a self, visitor: &mut V) -> ControlFlow<V::BreakTy>\n    where\n        V: Visitor<'a>,\n    {\n        visitor.visit_variable_list(&self.0)\n    }\n\n    fn visit_with_mut<'a, V>(&'a mut self, visitor: &mut V) -> ControlFlow<V::BreakTy>\n    where\n        V: VisitorMut<'a>,\n    {\n        visitor.visit_variable_list_mut(&mut self.0)\n    }\n}\n\n/// A **[lexical declaration]** defines variables that are scoped to the lexical environment of\n/// the variable declaration.\n///\n/// [lexical declaration]: https://tc39.es/ecma262/#sec-let-and-const-declarations\n#[cfg_attr(feature = \"serde\", derive(serde::Serialize, serde::Deserialize))]\n#[cfg_attr(feature = \"arbitrary\", derive(arbitrary::Arbitrary))]\n#[derive(Clone, Debug, PartialEq)]\npub enum LexicalDeclaration {\n    /// A <code>[const]</code> variable creates a constant whose scope can be either global or local\n    /// to the block in which it is declared.\n    ///\n    /// An initializer for a constant is required. You must specify its value in the same statement\n    /// in which it's declared. (This makes sense, given that it can't be changed later)\n    ///\n    /// [const]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/const\n    Const(VariableList),\n\n    /// A <code>[let]</code> variable is limited to a scope of a block statement, or expression on\n    /// which it is used, unlike the `var` keyword, which defines a variable globally, or locally to\n    /// an entire function regardless of block scope.\n    ///\n    /// Just like const, `let` does not create properties of the window object when declared\n    /// globally (in the top-most scope).\n    ///\n    /// If a let declaration does not have an initializer, the variable is assigned the value `undefined`.\n    ///\n    /// [let]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/let\n    Let(VariableList),\n\n    /// A <code>[using]</code> declaration creates a block-scoped resource that is automatically\n    /// disposed when control exits the block.\n    ///\n    /// [using]: https://tc39.es/proposal-explicit-resource-management/\n    Using(VariableList),\n\n    /// An <code>[await using]</code> declaration creates a block-scoped resource that is automatically\n    /// disposed asynchronously when control exits the block.\n    ///\n    /// [await using]: https://tc39.es/proposal-explicit-resource-management/\n    AwaitUsing(VariableList),\n}\n\nimpl LexicalDeclaration {\n    /// Gets the inner variable list of the `LexicalDeclaration`\n    #[must_use]\n    pub const fn variable_list(&self) -> &VariableList {\n        match self {\n            Self::Const(list) | Self::Let(list) | Self::Using(list) | Self::AwaitUsing(list) => {\n                list\n            }\n        }\n    }\n\n    /// Returns `true` if the declaration is a `const` declaration.\n    #[must_use]\n    pub const fn is_const(&self) -> bool {\n        matches!(self, Self::Const(_))\n    }\n}\n\nimpl From<LexicalDeclaration> for Declaration {\n    fn from(lex: LexicalDeclaration) -> Self {\n        Self::Lexical(lex)\n    }\n}\n\nimpl ToInternedString for LexicalDeclaration {\n    fn to_interned_string(&self, interner: &Interner) -> String {\n        format!(\n            \"{} {}\",\n            match &self {\n                Self::Let(_) => \"let\",\n                Self::Const(_) => \"const\",\n                Self::Using(_) => \"using\",\n                Self::AwaitUsing(_) => \"await using\",\n            },\n            self.variable_list().to_interned_string(interner)\n        )\n    }\n}\n\nimpl VisitWith for LexicalDeclaration {\n    fn visit_with<'a, V>(&'a self, visitor: &mut V) -> ControlFlow<V::BreakTy>\n    where\n        V: Visitor<'a>,\n    {\n        match self {\n            Self::Const(vars) | Self::Let(vars) | Self::Using(vars) | Self::AwaitUsing(vars) => {\n                visitor.visit_variable_list(vars)\n            }\n        }\n    }\n\n    fn visit_with_mut<'a, V>(&'a mut self, visitor: &mut V) -> ControlFlow<V::BreakTy>\n    where\n        V: VisitorMut<'a>,\n    {\n        match self {\n            Self::Const(vars) | Self::Let(vars) | Self::Using(vars) | Self::AwaitUsing(vars) => {\n                visitor.visit_variable_list_mut(vars)\n            }\n        }\n    }\n}\n\n/// List of variables in a variable declaration.\n#[cfg_attr(feature = \"serde\", derive(serde::Serialize, serde::Deserialize))]\n#[cfg_attr(feature = \"arbitrary\", derive(arbitrary::Arbitrary))]\n#[derive(Clone, Debug, PartialEq)]\npub struct VariableList {\n    list: Box<[Variable]>,\n}\n\nimpl VariableList {\n    /// Creates a variable list if the provided list of [`Variable`] is not empty.\n    #[must_use]\n    pub fn new(list: Box<[Variable]>) -> Option<Self> {\n        if list.is_empty() {\n            return None;\n        }\n\n        Some(Self { list })\n    }\n}\n\nimpl AsRef<[Variable]> for VariableList {\n    fn as_ref(&self) -> &[Variable] {\n        &self.list\n    }\n}\n\nimpl ToInternedString for VariableList {\n    fn to_interned_string(&self, interner: &Interner) -> String {\n        join_nodes(interner, self.list.as_ref())\n    }\n}\n\nimpl VisitWith for VariableList {\n    fn visit_with<'a, V>(&'a self, visitor: &mut V) -> ControlFlow<V::BreakTy>\n    where\n        V: Visitor<'a>,\n    {\n        for variable in &*self.list {\n            visitor.visit_variable(variable)?;\n        }\n        ControlFlow::Continue(())\n    }\n\n    fn visit_with_mut<'a, V>(&'a mut self, visitor: &mut V) -> ControlFlow<V::BreakTy>\n    where\n        V: VisitorMut<'a>,\n    {\n        for variable in &mut *self.list {\n            visitor.visit_variable_mut(variable)?;\n        }\n        ControlFlow::Continue(())\n    }\n}\n\n/// The error returned by the [`VariableList::try_from`] function.\n#[derive(Debug, Copy, Clone, PartialEq, Eq)]\npub struct TryFromVariableListError(());\n\nimpl std::fmt::Display for TryFromVariableListError {\n    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {\n        \"provided list of variables cannot be empty\".fmt(f)\n    }\n}\n\nimpl TryFrom<Box<[Variable]>> for VariableList {\n    type Error = TryFromVariableListError;\n\n    fn try_from(value: Box<[Variable]>) -> Result<Self, Self::Error> {\n        Self::new(value).ok_or(TryFromVariableListError(()))\n    }\n}\n\nimpl TryFrom<Vec<Variable>> for VariableList {\n    type Error = TryFromVariableListError;\n\n    fn try_from(value: Vec<Variable>) -> Result<Self, Self::Error> {\n        Self::try_from(value.into_boxed_slice())\n    }\n}\n\n/// Variable represents a variable declaration of some kind.\n///\n/// For `let` and `const` declarations this type represents a [`LexicalBinding`][spec1]\n///\n/// For `var` declarations this type represents a [`VariableDeclaration`][spec2]\n///\n/// More information:\n///  - [ECMAScript reference: 14.3 Declarations and the Variable Statement][spec3]\n///\n/// [spec1]: https://tc39.es/ecma262/#prod-LexicalBinding\n/// [spec2]: https://tc39.es/ecma262/#prod-VariableDeclaration\n/// [spec3]:  https://tc39.es/ecma262/#sec-declarations-and-the-variable-statement\n#[cfg_attr(feature = \"serde\", derive(serde::Serialize, serde::Deserialize))]\n#[cfg_attr(feature = \"arbitrary\", derive(arbitrary::Arbitrary))]\n#[derive(Clone, Debug, PartialEq)]\npub struct Variable {\n    binding: Binding,\n    init: Option<Expression>,\n}\n\nimpl ToInternedString for Variable {\n    fn to_interned_string(&self, interner: &Interner) -> String {\n        let mut buf = self.binding.to_interned_string(interner);\n\n        if let Some(ref init) = self.init {\n            let _ = write!(buf, \" = {}\", init.to_interned_string(interner));\n        }\n        buf\n    }\n}\n\nimpl Variable {\n    /// Creates a new variable declaration from a `BindingIdentifier`.\n    #[inline]\n    #[must_use]\n    pub const fn from_identifier(ident: Identifier, init: Option<Expression>) -> Self {\n        Self {\n            binding: Binding::Identifier(ident),\n            init,\n        }\n    }\n\n    /// Creates a new variable declaration from a `Pattern`.\n    #[inline]\n    #[must_use]\n    pub const fn from_pattern(pattern: Pattern, init: Option<Expression>) -> Self {\n        Self {\n            binding: Binding::Pattern(pattern),\n            init,\n        }\n    }\n    /// Gets the variable declaration binding.\n    #[must_use]\n    pub const fn binding(&self) -> &Binding {\n        &self.binding\n    }\n\n    /// Gets the initialization expression for the variable declaration, if any.\n    #[inline]\n    #[must_use]\n    pub const fn init(&self) -> Option<&Expression> {\n        self.init.as_ref()\n    }\n}\n\nimpl VisitWith for Variable {\n    fn visit_with<'a, V>(&'a self, visitor: &mut V) -> ControlFlow<V::BreakTy>\n    where\n        V: Visitor<'a>,\n    {\n        visitor.visit_binding(&self.binding)?;\n        if let Some(init) = &self.init {\n            visitor.visit_expression(init)?;\n        }\n        ControlFlow::Continue(())\n    }\n\n    fn visit_with_mut<'a, V>(&'a mut self, visitor: &mut V) -> ControlFlow<V::BreakTy>\n    where\n        V: VisitorMut<'a>,\n    {\n        visitor.visit_binding_mut(&mut self.binding)?;\n        if let Some(init) = &mut self.init {\n            visitor.visit_expression_mut(init)?;\n        }\n        ControlFlow::Continue(())\n    }\n}\n\n/// Binding represents either an individual binding or a binding pattern.\n///\n/// More information:\n///  - [ECMAScript reference: 14.3 Declarations and the Variable Statement][spec]\n///\n/// [spec]:  https://tc39.es/ecma262/#sec-declarations-and-the-variable-statement\n#[cfg_attr(feature = \"serde\", derive(serde::Serialize, serde::Deserialize))]\n#[cfg_attr(feature = \"arbitrary\", derive(arbitrary::Arbitrary))]\n#[derive(Clone, Debug, PartialEq)]\npub enum Binding {\n    /// A single identifier binding.\n    Identifier(Identifier),\n    /// A pattern binding.\n    Pattern(Pattern),\n}\n\nimpl From<Identifier> for Binding {\n    fn from(id: Identifier) -> Self {\n        Self::Identifier(id)\n    }\n}\n\nimpl From<Pattern> for Binding {\n    fn from(pat: Pattern) -> Self {\n        Self::Pattern(pat)\n    }\n}\n\nimpl ToInternedString for Binding {\n    fn to_interned_string(&self, interner: &Interner) -> String {\n        match self {\n            Self::Identifier(id) => id.to_interned_string(interner),\n            Self::Pattern(pattern) => pattern.to_interned_string(interner),\n        }\n    }\n}\n\nimpl VisitWith for Binding {\n    fn visit_with<'a, V>(&'a self, visitor: &mut V) -> ControlFlow<V::BreakTy>\n    where\n        V: Visitor<'a>,\n    {\n        match self {\n            Self::Identifier(id) => visitor.visit_identifier(id),\n            Self::Pattern(pattern) => visitor.visit_pattern(pattern),\n        }\n    }\n\n    fn visit_with_mut<'a, V>(&'a mut self, visitor: &mut V) -> ControlFlow<V::BreakTy>\n    where\n        V: VisitorMut<'a>,\n    {\n        match self {\n            Self::Identifier(id) => visitor.visit_identifier_mut(id),\n            Self::Pattern(pattern) => visitor.visit_pattern_mut(pattern),\n        }\n    }\n}\n"
  },
  {
    "path": "core/ast/src/expression/access.rs",
    "content": "//! Property access expressions, as defined by the [spec].\n//!\n//! [Property access expressions][access] provide two ways to access properties of an object: *dot notation*\n//! and *bracket notation*.\n//! - *Dot notation* is mostly used when the name of the property is static, and a valid Javascript\n//!   identifier e.g. `obj.prop`, `arr.$val`.\n//! - *Bracket notation* is used when the name of the property is either variable, not a valid\n//!   identifier or a symbol e.g. `arr[var]`, `arr[5]`, `arr[Symbol.iterator]`.\n//!\n//! A property access expression can be represented by a [`SimplePropertyAccess`] (`x.y`), a\n//! [`PrivatePropertyAccess`] (`x.#y`) or a [`SuperPropertyAccess`] (`super[\"y\"]`), each of them with\n//! slightly different semantics overall.\n//!\n//! [spec]: https://tc39.es/ecma262/multipage/ecmascript-language-expressions.html#sec-property-accessors\n//! [access]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Property_Accessors\n\nuse crate::expression::Expression;\nuse crate::function::PrivateName;\nuse crate::visitor::{VisitWith, Visitor, VisitorMut};\nuse crate::{Span, Spanned};\nuse boa_interner::{Interner, ToInternedString};\nuse core::ops::ControlFlow;\n\nuse super::Identifier;\n\n/// A property access field.\n///\n/// See the [module level documentation][self] for more information.\n#[cfg_attr(feature = \"serde\", derive(serde::Serialize, serde::Deserialize))]\n#[cfg_attr(feature = \"arbitrary\", derive(arbitrary::Arbitrary))]\n#[derive(Clone, Debug, PartialEq)]\npub enum PropertyAccessField {\n    /// A constant property field, such as `x.prop`.\n    Const(Identifier),\n    /// An expression property field, such as `x[\"val\"]`.\n    Expr(Box<Expression>),\n}\n\nimpl Spanned for PropertyAccessField {\n    #[inline]\n    fn span(&self) -> Span {\n        match self {\n            Self::Const(identifier) => identifier.span(),\n            Self::Expr(expression) => expression.span(),\n        }\n    }\n}\n\nimpl From<Identifier> for PropertyAccessField {\n    #[inline]\n    fn from(id: Identifier) -> Self {\n        Self::Const(id)\n    }\n}\n\nimpl From<Expression> for PropertyAccessField {\n    #[inline]\n    fn from(expr: Expression) -> Self {\n        Self::Expr(Box::new(expr))\n    }\n}\n\nimpl VisitWith for PropertyAccessField {\n    fn visit_with<'a, V>(&'a self, visitor: &mut V) -> ControlFlow<V::BreakTy>\n    where\n        V: Visitor<'a>,\n    {\n        match self {\n            Self::Const(sym) => visitor.visit_sym(sym.sym_ref()),\n            Self::Expr(expr) => visitor.visit_expression(expr),\n        }\n    }\n\n    fn visit_with_mut<'a, V>(&'a mut self, visitor: &mut V) -> ControlFlow<V::BreakTy>\n    where\n        V: VisitorMut<'a>,\n    {\n        match self {\n            Self::Const(sym) => visitor.visit_sym_mut(sym.sym_mut()),\n            Self::Expr(expr) => visitor.visit_expression_mut(&mut *expr),\n        }\n    }\n}\n\n/// A property access expression.\n///\n/// See the [module level documentation][self] for more information.\n#[cfg_attr(feature = \"serde\", derive(serde::Serialize, serde::Deserialize))]\n#[cfg_attr(feature = \"arbitrary\", derive(arbitrary::Arbitrary))]\n#[derive(Clone, Debug, PartialEq)]\npub enum PropertyAccess {\n    /// A simple property access (`x.prop`).\n    Simple(SimplePropertyAccess),\n    /// A property access of a private property (`x.#priv`).\n    Private(PrivatePropertyAccess),\n    /// A property access of a `super` reference. (`super[\"prop\"]`).\n    Super(SuperPropertyAccess),\n}\n\nimpl Spanned for PropertyAccess {\n    #[inline]\n    fn span(&self) -> Span {\n        match self {\n            Self::Simple(access) => access.span(),\n            Self::Private(access) => access.span(),\n            Self::Super(access) => access.span(),\n        }\n    }\n}\n\nimpl ToInternedString for PropertyAccess {\n    #[inline]\n    fn to_interned_string(&self, interner: &Interner) -> String {\n        match self {\n            Self::Simple(s) => s.to_interned_string(interner),\n            Self::Private(p) => p.to_interned_string(interner),\n            Self::Super(s) => s.to_interned_string(interner),\n        }\n    }\n}\n\nimpl From<PropertyAccess> for Expression {\n    #[inline]\n    fn from(access: PropertyAccess) -> Self {\n        Self::PropertyAccess(access)\n    }\n}\n\nimpl VisitWith for PropertyAccess {\n    fn visit_with<'a, V>(&'a self, visitor: &mut V) -> ControlFlow<V::BreakTy>\n    where\n        V: Visitor<'a>,\n    {\n        match self {\n            Self::Simple(spa) => visitor.visit_simple_property_access(spa),\n            Self::Private(ppa) => visitor.visit_private_property_access(ppa),\n            Self::Super(supa) => visitor.visit_super_property_access(supa),\n        }\n    }\n\n    fn visit_with_mut<'a, V>(&'a mut self, visitor: &mut V) -> ControlFlow<V::BreakTy>\n    where\n        V: VisitorMut<'a>,\n    {\n        match self {\n            Self::Simple(spa) => visitor.visit_simple_property_access_mut(spa),\n            Self::Private(ppa) => visitor.visit_private_property_access_mut(ppa),\n            Self::Super(supa) => visitor.visit_super_property_access_mut(supa),\n        }\n    }\n}\n\n/// A simple property access, where the target object is an [`Expression`].\n#[cfg_attr(feature = \"serde\", derive(serde::Serialize, serde::Deserialize))]\n#[cfg_attr(feature = \"arbitrary\", derive(arbitrary::Arbitrary))]\n#[derive(Clone, Debug, PartialEq)]\npub struct SimplePropertyAccess {\n    target: Box<Expression>,\n    field: PropertyAccessField,\n}\n\nimpl SimplePropertyAccess {\n    /// Gets the target object of the property access.\n    #[inline]\n    #[must_use]\n    pub const fn target(&self) -> &Expression {\n        &self.target\n    }\n\n    /// Gets the accessed field of the target object.\n    #[inline]\n    #[must_use]\n    pub const fn field(&self) -> &PropertyAccessField {\n        &self.field\n    }\n\n    /// Creates a `PropertyAccess` AST Expression.\n    pub fn new<F>(target: Expression, field: F) -> Self\n    where\n        F: Into<PropertyAccessField>,\n    {\n        Self {\n            target: target.into(),\n            field: field.into(),\n        }\n    }\n}\n\nimpl Spanned for SimplePropertyAccess {\n    #[inline]\n    fn span(&self) -> Span {\n        Span::new(self.target.span().start(), self.field.span().end())\n    }\n}\n\nimpl ToInternedString for SimplePropertyAccess {\n    #[inline]\n    fn to_interned_string(&self, interner: &Interner) -> String {\n        let target = self.target.to_interned_string(interner);\n        match self.field {\n            PropertyAccessField::Const(ident) => {\n                format!(\"{target}.{}\", interner.resolve_expect(ident.sym()))\n            }\n            PropertyAccessField::Expr(ref expr) => {\n                format!(\"{target}[{}]\", expr.to_interned_string(interner))\n            }\n        }\n    }\n}\n\nimpl From<SimplePropertyAccess> for PropertyAccess {\n    #[inline]\n    fn from(access: SimplePropertyAccess) -> Self {\n        Self::Simple(access)\n    }\n}\n\nimpl VisitWith for SimplePropertyAccess {\n    fn visit_with<'a, V>(&'a self, visitor: &mut V) -> ControlFlow<V::BreakTy>\n    where\n        V: Visitor<'a>,\n    {\n        visitor.visit_expression(&self.target)?;\n        visitor.visit_property_access_field(&self.field)\n    }\n\n    fn visit_with_mut<'a, V>(&'a mut self, visitor: &mut V) -> ControlFlow<V::BreakTy>\n    where\n        V: VisitorMut<'a>,\n    {\n        visitor.visit_expression_mut(&mut self.target)?;\n        visitor.visit_property_access_field_mut(&mut self.field)\n    }\n}\n\n/// An access expression to a class object's [private fields][mdn].\n///\n/// Private property accesses differ slightly from plain property accesses, since the accessed\n/// property must be prefixed by `#`, and the bracket notation is not allowed. For example,\n/// `this.#a` is a valid private property access.\n///\n/// This expression corresponds to the [`MemberExpression.PrivateIdentifier`][spec] production.\n///\n/// [spec]: https://tc39.es/ecma262/#prod-MemberExpression\n/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Classes/Private_class_fields\n#[cfg_attr(feature = \"serde\", derive(serde::Serialize, serde::Deserialize))]\n#[cfg_attr(feature = \"arbitrary\", derive(arbitrary::Arbitrary))]\n#[derive(Clone, Debug, PartialEq)]\npub struct PrivatePropertyAccess {\n    target: Box<Expression>,\n    field: PrivateName,\n    span: Span,\n}\n\nimpl PrivatePropertyAccess {\n    /// Creates a `GetPrivateField` AST Expression.\n    #[inline]\n    #[must_use]\n    pub fn new(value: Expression, field: PrivateName, span: Span) -> Self {\n        Self {\n            target: value.into(),\n            field,\n            span,\n        }\n    }\n\n    /// Gets the original object from where to get the field from.\n    #[inline]\n    #[must_use]\n    pub const fn target(&self) -> &Expression {\n        &self.target\n    }\n\n    /// Gets the name of the field to retrieve.\n    #[inline]\n    #[must_use]\n    pub const fn field(&self) -> PrivateName {\n        self.field\n    }\n}\n\nimpl Spanned for PrivatePropertyAccess {\n    #[inline]\n    fn span(&self) -> Span {\n        self.span\n    }\n}\n\nimpl ToInternedString for PrivatePropertyAccess {\n    #[inline]\n    fn to_interned_string(&self, interner: &Interner) -> String {\n        format!(\n            \"{}.#{}\",\n            self.target.to_interned_string(interner),\n            interner.resolve_expect(self.field.description())\n        )\n    }\n}\n\nimpl From<PrivatePropertyAccess> for PropertyAccess {\n    #[inline]\n    fn from(access: PrivatePropertyAccess) -> Self {\n        Self::Private(access)\n    }\n}\n\nimpl VisitWith for PrivatePropertyAccess {\n    fn visit_with<'a, V>(&'a self, visitor: &mut V) -> ControlFlow<V::BreakTy>\n    where\n        V: Visitor<'a>,\n    {\n        visitor.visit_expression(&self.target)?;\n        visitor.visit_private_name(&self.field)\n    }\n\n    fn visit_with_mut<'a, V>(&'a mut self, visitor: &mut V) -> ControlFlow<V::BreakTy>\n    where\n        V: VisitorMut<'a>,\n    {\n        visitor.visit_expression_mut(&mut self.target)?;\n        visitor.visit_private_name_mut(&mut self.field)\n    }\n}\n\n/// A property access of an object's parent, as defined by the [spec].\n///\n/// A `SuperPropertyAccess` is much like a regular [`PropertyAccess`], but where its `target` object\n/// is not a regular object, but a reference to the parent object of the current object ([`super`][mdn]).\n///\n/// [spec]: https://tc39.es/ecma262/#prod-SuperProperty\n/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/super\n#[cfg_attr(feature = \"serde\", derive(serde::Serialize, serde::Deserialize))]\n#[cfg_attr(feature = \"arbitrary\", derive(arbitrary::Arbitrary))]\n#[derive(Clone, Debug, PartialEq)]\npub struct SuperPropertyAccess {\n    field: PropertyAccessField,\n    span: Span,\n}\n\nimpl SuperPropertyAccess {\n    /// Creates a new property access field node.\n    #[must_use]\n    pub const fn new(field: PropertyAccessField, span: Span) -> Self {\n        Self { field, span }\n    }\n\n    /// Gets the name of the field to retrieve.\n    #[inline]\n    #[must_use]\n    pub const fn field(&self) -> &PropertyAccessField {\n        &self.field\n    }\n}\n\nimpl Spanned for SuperPropertyAccess {\n    #[inline]\n    fn span(&self) -> Span {\n        self.span\n    }\n}\n\nimpl ToInternedString for SuperPropertyAccess {\n    #[inline]\n    fn to_interned_string(&self, interner: &Interner) -> String {\n        match &self.field {\n            PropertyAccessField::Const(field) => {\n                format!(\"super.{}\", interner.resolve_expect(field.sym()))\n            }\n            PropertyAccessField::Expr(field) => {\n                format!(\"super[{}]\", field.to_interned_string(interner))\n            }\n        }\n    }\n}\n\nimpl From<SuperPropertyAccess> for PropertyAccess {\n    #[inline]\n    fn from(access: SuperPropertyAccess) -> Self {\n        Self::Super(access)\n    }\n}\n\nimpl VisitWith for SuperPropertyAccess {\n    fn visit_with<'a, V>(&'a self, visitor: &mut V) -> ControlFlow<V::BreakTy>\n    where\n        V: Visitor<'a>,\n    {\n        visitor.visit_property_access_field(&self.field)\n    }\n\n    fn visit_with_mut<'a, V>(&'a mut self, visitor: &mut V) -> ControlFlow<V::BreakTy>\n    where\n        V: VisitorMut<'a>,\n    {\n        visitor.visit_property_access_field_mut(&mut self.field)\n    }\n}\n"
  },
  {
    "path": "core/ast/src/expression/await.rs",
    "content": "//! Await expression Expression.\n\nuse core::ops::ControlFlow;\n\nuse super::Expression;\nuse crate::{\n    Span, Spanned,\n    visitor::{VisitWith, Visitor, VisitorMut},\n};\nuse boa_interner::{Interner, ToIndentedString, ToInternedString};\n\n/// An await expression is used within an async function to pause execution and wait for a\n/// promise to resolve.\n///\n/// More information:\n///  - [ECMAScript reference][spec]\n///  - [MDN documentation][mdn]\n///\n/// [spec]: https://tc39.es/ecma262/#prod-AwaitExpression\n/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/await\n#[cfg_attr(feature = \"serde\", derive(serde::Serialize, serde::Deserialize))]\n#[cfg_attr(feature = \"arbitrary\", derive(arbitrary::Arbitrary))]\n#[derive(Clone, Debug, PartialEq)]\npub struct Await {\n    target: Box<Expression>,\n    span: Span,\n}\n\nimpl Await {\n    /// Create a new [`Await`] node.\n    #[must_use]\n    pub const fn new(target: Box<Expression>, span: Span) -> Self {\n        Self { target, span }\n    }\n\n    /// Return the target expression that should be awaited.\n    #[inline]\n    #[must_use]\n    pub const fn target(&self) -> &Expression {\n        &self.target\n    }\n}\n\nimpl Spanned for Await {\n    #[inline]\n    fn span(&self) -> Span {\n        self.span\n    }\n}\n\nimpl ToInternedString for Await {\n    #[inline]\n    fn to_interned_string(&self, interner: &Interner) -> String {\n        format!(\"await {}\", self.target.to_indented_string(interner, 0))\n    }\n}\n\nimpl From<Await> for Expression {\n    #[inline]\n    fn from(awaitexpr: Await) -> Self {\n        Self::Await(awaitexpr)\n    }\n}\n\nimpl VisitWith for Await {\n    fn visit_with<'a, V>(&'a self, visitor: &mut V) -> ControlFlow<V::BreakTy>\n    where\n        V: Visitor<'a>,\n    {\n        visitor.visit_expression(&self.target)\n    }\n\n    fn visit_with_mut<'a, V>(&'a mut self, visitor: &mut V) -> ControlFlow<V::BreakTy>\n    where\n        V: VisitorMut<'a>,\n    {\n        visitor.visit_expression_mut(&mut self.target)\n    }\n}\n"
  },
  {
    "path": "core/ast/src/expression/call.rs",
    "content": "use crate::visitor::{VisitWith, Visitor, VisitorMut};\nuse crate::{Span, Spanned, join_nodes};\nuse boa_interner::{Interner, ToInternedString};\nuse core::ops::ControlFlow;\n\nuse super::Expression;\n\n/// Calling the function actually performs the specified actions with the indicated parameters.\n///\n/// Defining a function does not execute it. Defining it simply names the function and\n/// specifies what to do when the function is called. Functions must be in scope when they are\n/// called, but the function declaration can be hoisted. The scope of a function is the\n/// function in which it is declared (or the entire program, if it is declared at the top\n/// level).\n///\n/// More information:\n///  - [ECMAScript reference][spec]\n///  - [MDN documentation][mdn]\n///\n/// [spec]: https://tc39.es/ecma262/#prod-CallExpression\n/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Functions#Calling_functions\n#[cfg_attr(feature = \"serde\", derive(serde::Serialize, serde::Deserialize))]\n#[cfg_attr(feature = \"arbitrary\", derive(arbitrary::Arbitrary))]\n#[derive(Clone, Debug, PartialEq)]\npub struct Call {\n    function: Box<Expression>,\n    args: Box<[Expression]>,\n    span: Span,\n}\n\nimpl Call {\n    /// Creates a new `Call` AST Expression.\n    #[inline]\n    #[must_use]\n    pub fn new(function: Expression, args: Box<[Expression]>, span: Span) -> Self {\n        Self {\n            function: Box::new(function),\n            args,\n            span,\n        }\n    }\n\n    /// Gets the target function of this call expression.\n    #[inline]\n    #[must_use]\n    pub const fn function(&self) -> &Expression {\n        &self.function\n    }\n\n    /// Retrieves the arguments passed to the function.\n    #[inline]\n    #[must_use]\n    pub const fn args(&self) -> &[Expression] {\n        &self.args\n    }\n}\n\nimpl Spanned for Call {\n    #[inline]\n    fn span(&self) -> Span {\n        self.span\n    }\n}\n\nimpl ToInternedString for Call {\n    #[inline]\n    fn to_interned_string(&self, interner: &Interner) -> String {\n        format!(\n            \"{}({})\",\n            self.function.to_interned_string(interner),\n            join_nodes(interner, &self.args)\n        )\n    }\n}\n\nimpl From<Call> for Expression {\n    #[inline]\n    fn from(call: Call) -> Self {\n        Self::Call(call)\n    }\n}\n\nimpl VisitWith for Call {\n    fn visit_with<'a, V>(&'a self, visitor: &mut V) -> ControlFlow<V::BreakTy>\n    where\n        V: Visitor<'a>,\n    {\n        visitor.visit_expression(&self.function)?;\n        for expr in &*self.args {\n            visitor.visit_expression(expr)?;\n        }\n        ControlFlow::Continue(())\n    }\n\n    fn visit_with_mut<'a, V>(&'a mut self, visitor: &mut V) -> ControlFlow<V::BreakTy>\n    where\n        V: VisitorMut<'a>,\n    {\n        visitor.visit_expression_mut(&mut self.function)?;\n        for expr in &mut *self.args {\n            visitor.visit_expression_mut(expr)?;\n        }\n        ControlFlow::Continue(())\n    }\n}\n\n/// The `super` keyword is used to access and call functions on an object's parent.\n///\n/// More information:\n///  - [ECMAScript reference][spec]\n///  - [MDN documentation][mdn]\n///\n/// [spec]: https://tc39.es/ecma262/#prod-SuperCall\n/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/super\n#[cfg_attr(feature = \"serde\", derive(serde::Serialize, serde::Deserialize))]\n#[cfg_attr(feature = \"arbitrary\", derive(arbitrary::Arbitrary))]\n#[derive(Clone, Debug, PartialEq)]\npub struct SuperCall {\n    args: Box<[Expression]>,\n    span: Span,\n}\n\nimpl SuperCall {\n    /// Creates a new `SuperCall` AST node.\n    pub fn new<A>(args: A, span: Span) -> Self\n    where\n        A: Into<Box<[Expression]>>,\n    {\n        Self {\n            args: args.into(),\n            span,\n        }\n    }\n\n    /// Retrieves the arguments of the super call.\n    #[must_use]\n    pub const fn arguments(&self) -> &[Expression] {\n        &self.args\n    }\n}\n\nimpl Spanned for SuperCall {\n    #[inline]\n    fn span(&self) -> Span {\n        self.span\n    }\n}\n\nimpl ToInternedString for SuperCall {\n    #[inline]\n    fn to_interned_string(&self, interner: &Interner) -> String {\n        format!(\"super({})\", join_nodes(interner, &self.args))\n    }\n}\n\nimpl From<SuperCall> for Expression {\n    #[inline]\n    fn from(call: SuperCall) -> Self {\n        Self::SuperCall(call)\n    }\n}\n\nimpl VisitWith for SuperCall {\n    fn visit_with<'a, V>(&'a self, visitor: &mut V) -> ControlFlow<V::BreakTy>\n    where\n        V: Visitor<'a>,\n    {\n        for expr in &*self.args {\n            visitor.visit_expression(expr)?;\n        }\n        ControlFlow::Continue(())\n    }\n\n    fn visit_with_mut<'a, V>(&'a mut self, visitor: &mut V) -> ControlFlow<V::BreakTy>\n    where\n        V: VisitorMut<'a>,\n    {\n        for expr in &mut *self.args {\n            visitor.visit_expression_mut(expr)?;\n        }\n        ControlFlow::Continue(())\n    }\n}\n\n/// The phase of a dynamic import call.\n///\n/// Determines how the imported module is handled:\n/// - `Evaluation` (default): `import(specifier)` — loads, links, and evaluates the module.\n/// - `Defer`: `import.defer(specifier)` — deferred evaluation of the module.\n/// - `Source`: `import.source(specifier)` — source phase import.\n///\n/// More information:\n///  - [import-defer proposal][defer]\n///  - [source-phase-imports proposal][source]\n///\n/// [defer]: https://github.com/tc39/proposal-defer-import-eval\n/// [source]: https://github.com/tc39/proposal-source-phase-imports\n#[cfg_attr(feature = \"serde\", derive(serde::Serialize, serde::Deserialize))]\n#[cfg_attr(feature = \"arbitrary\", derive(arbitrary::Arbitrary))]\n#[derive(Clone, Copy, Debug, PartialEq, Eq, Default)]\npub enum ImportPhase {\n    /// `import(specifier)` — standard dynamic import.\n    #[default]\n    Evaluation,\n    /// `import.defer(specifier)` — deferred import evaluation.\n    Defer,\n    /// `import.source(specifier)` — source phase import.\n    Source,\n}\n\n/// The `import()` syntax, commonly called dynamic import, is a function-like expression that allows\n/// loading an ECMAScript module asynchronously and dynamically into a potentially non-module\n/// environment.\n///\n/// More information:\n///  - [ECMAScript reference][spec]\n///  - [MDN documentation][mdn]\n///\n/// [spec]: https://tc39.es/ecma262/#prod-ImportCall\n/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/import\n#[cfg_attr(feature = \"serde\", derive(serde::Serialize, serde::Deserialize))]\n#[cfg_attr(feature = \"arbitrary\", derive(arbitrary::Arbitrary))]\n#[derive(Clone, Debug, PartialEq)]\npub struct ImportCall {\n    specifier: Box<Expression>,\n    options: Option<Box<Expression>>,\n    phase: ImportPhase,\n    span: Span,\n}\n\nimpl ImportCall {\n    /// Creates a new `ImportCall` AST node.\n    #[inline]\n    #[must_use]\n    pub fn new<S>(specifier: S, options: Option<Expression>, phase: ImportPhase, span: Span) -> Self\n    where\n        S: Into<Expression>,\n    {\n        Self {\n            specifier: Box::new(specifier.into()),\n            options: options.map(Box::new),\n            phase,\n            span,\n        }\n    }\n\n    /// Retrieves the specifier (first argument) of the import call.\n    #[inline]\n    #[must_use]\n    pub const fn specifier(&self) -> &Expression {\n        &self.specifier\n    }\n\n    /// Retrieves the options (second argument) of the import call, if present.\n    ///\n    /// This is used for import attributes:\n    /// ```js\n    /// import(\"foo.json\", { with: { type: \"json\" } })\n    /// ```\n    #[inline]\n    #[must_use]\n    pub fn options(&self) -> Option<&Expression> {\n        self.options.as_deref()\n    }\n\n    /// Returns the phase of this import call.\n    #[inline]\n    #[must_use]\n    pub const fn phase(&self) -> ImportPhase {\n        self.phase\n    }\n\n    /// Gets the module specifier of the import call.\n    ///\n    /// This is an alias for [`Self::specifier`] for backwards compatibility.\n    #[inline]\n    #[must_use]\n    #[deprecated(since = \"0.21.0\", note = \"use `specifier` instead\")]\n    pub const fn argument(&self) -> &Expression {\n        &self.specifier\n    }\n}\n\nimpl Spanned for ImportCall {\n    #[inline]\n    fn span(&self) -> Span {\n        self.span\n    }\n}\n\nimpl ToInternedString for ImportCall {\n    #[inline]\n    fn to_interned_string(&self, interner: &Interner) -> String {\n        let phase_str = match self.phase {\n            ImportPhase::Evaluation => \"\",\n            ImportPhase::Defer => \".defer\",\n            ImportPhase::Source => \".source\",\n        };\n        if let Some(options) = &self.options {\n            format!(\n                \"import{}({}, {})\",\n                phase_str,\n                self.specifier.to_interned_string(interner),\n                options.to_interned_string(interner)\n            )\n        } else {\n            format!(\n                \"import{}({})\",\n                phase_str,\n                self.specifier.to_interned_string(interner)\n            )\n        }\n    }\n}\n\nimpl From<ImportCall> for Expression {\n    #[inline]\n    fn from(call: ImportCall) -> Self {\n        Self::ImportCall(call)\n    }\n}\n\nimpl VisitWith for ImportCall {\n    fn visit_with<'a, V>(&'a self, visitor: &mut V) -> ControlFlow<V::BreakTy>\n    where\n        V: Visitor<'a>,\n    {\n        visitor.visit_expression(&self.specifier)?;\n        if let Some(options) = &self.options {\n            visitor.visit_expression(options)?;\n        }\n        ControlFlow::Continue(())\n    }\n\n    fn visit_with_mut<'a, V>(&'a mut self, visitor: &mut V) -> ControlFlow<V::BreakTy>\n    where\n        V: VisitorMut<'a>,\n    {\n        visitor.visit_expression_mut(&mut self.specifier)?;\n        if let Some(options) = &mut self.options {\n            visitor.visit_expression_mut(options)?;\n        }\n        ControlFlow::Continue(())\n    }\n}\n"
  },
  {
    "path": "core/ast/src/expression/identifier.rs",
    "content": "//! Local identifier Expression.\n\nuse crate::{\n    Span, Spanned, ToStringEscaped,\n    visitor::{VisitWith, Visitor, VisitorMut},\n};\nuse boa_interner::{Interner, Sym, ToInternedString};\nuse core::ops::ControlFlow;\n\nuse super::Expression;\n\n/// List of reserved keywords exclusive to strict mode.\npub const RESERVED_IDENTIFIERS_STRICT: [Sym; 9] = [\n    Sym::IMPLEMENTS,\n    Sym::INTERFACE,\n    Sym::LET,\n    Sym::PACKAGE,\n    Sym::PRIVATE,\n    Sym::PROTECTED,\n    Sym::PUBLIC,\n    Sym::STATIC,\n    Sym::YIELD,\n];\n\n/// An `identifier` is a sequence of characters in the code that identifies a variable,\n/// function, or property.\n///\n/// In ECMAScript, identifiers are case-sensitive and can contain Unicode letters, $, _, and\n/// digits (0-9), but may not start with a digit.\n///\n/// An identifier differs from a string in that a string is data, while an identifier is part\n/// of the code. In JavaScript, there is no way to convert identifiers to strings, but\n/// sometimes it is possible to parse strings into identifiers.\n///\n/// More information:\n///  - [ECMAScript reference][spec]\n///  - [MDN documentation][mdn]\n///\n/// [spec]: https://tc39.es/ecma262/#prod-Identifier\n/// [mdn]: https://developer.mozilla.org/en-US/docs/Glossary/Identifier\n#[cfg_attr(feature = \"serde\", derive(serde::Serialize, serde::Deserialize))]\n#[cfg_attr(feature = \"arbitrary\", derive(arbitrary::Arbitrary))]\n#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]\npub struct Identifier {\n    ident: Sym,\n    span: Span,\n}\n\nimpl PartialEq<Sym> for Identifier {\n    #[inline]\n    fn eq(&self, other: &Sym) -> bool {\n        self.ident == *other\n    }\n}\n\nimpl PartialEq<Identifier> for Sym {\n    #[inline]\n    fn eq(&self, other: &Identifier) -> bool {\n        *self == other.ident\n    }\n}\n\nimpl Identifier {\n    /// Creates a new identifier AST Expression.\n    #[inline]\n    #[must_use]\n    pub const fn new(ident: Sym, span: Span) -> Self {\n        Self { ident, span }\n    }\n\n    /// Retrieves the identifier's string symbol in the interner.\n    #[inline]\n    #[must_use]\n    pub const fn sym(self) -> Sym {\n        self.ident\n    }\n\n    /// Retrieves the identifier's string symbol in the interner.\n    #[inline]\n    #[must_use]\n    pub const fn sym_ref(&self) -> &Sym {\n        &self.ident\n    }\n\n    /// Retrieves the identifier's string symbol in the interner.\n    #[inline]\n    #[must_use]\n    pub const fn sym_mut(&mut self) -> &mut Sym {\n        &mut self.ident\n    }\n}\n\nimpl Spanned for Identifier {\n    #[inline]\n    fn span(&self) -> Span {\n        self.span\n    }\n}\n\nimpl ToInternedString for Identifier {\n    #[inline]\n    fn to_interned_string(&self, interner: &Interner) -> String {\n        interner.resolve_expect(self.ident).join(\n            String::from,\n            ToStringEscaped::to_string_escaped,\n            true,\n        )\n    }\n}\n\nimpl From<Identifier> for Expression {\n    #[inline]\n    fn from(local: Identifier) -> Self {\n        Self::Identifier(local)\n    }\n}\n\nimpl VisitWith for Identifier {\n    fn visit_with<'a, V>(&'a self, visitor: &mut V) -> ControlFlow<V::BreakTy>\n    where\n        V: Visitor<'a>,\n    {\n        visitor.visit_sym(&self.ident)\n    }\n\n    fn visit_with_mut<'a, V>(&'a mut self, visitor: &mut V) -> ControlFlow<V::BreakTy>\n    where\n        V: VisitorMut<'a>,\n    {\n        visitor.visit_sym_mut(&mut self.ident)\n    }\n}\n"
  },
  {
    "path": "core/ast/src/expression/import_meta.rs",
    "content": "//! `import.meta` ECMAScript expression.\n\nuse crate::{\n    Span, Spanned,\n    visitor::{VisitWith, Visitor, VisitorMut},\n};\nuse boa_interner::{Interner, ToInternedString};\nuse core::ops::ControlFlow;\n\nuse super::Expression;\n\n/// ECMAScript's `ImportMeta` expression AST node.\n#[cfg_attr(feature = \"serde\", derive(serde::Serialize, serde::Deserialize))]\n#[cfg_attr(feature = \"arbitrary\", derive(arbitrary::Arbitrary))]\n#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]\npub struct ImportMeta {\n    span: Span,\n}\n\nimpl ImportMeta {\n    /// Creates a new [`ImportMeta`] AST Expression.\n    #[inline]\n    #[must_use]\n    pub const fn new(span: Span) -> Self {\n        Self { span }\n    }\n}\n\nimpl Spanned for ImportMeta {\n    #[inline]\n    fn span(&self) -> Span {\n        self.span\n    }\n}\n\nimpl From<ImportMeta> for Expression {\n    #[inline]\n    fn from(value: ImportMeta) -> Self {\n        Expression::ImportMeta(value)\n    }\n}\n\nimpl ToInternedString for ImportMeta {\n    #[inline]\n    fn to_interned_string(&self, _interner: &Interner) -> String {\n        String::from(\"import.meta\")\n    }\n}\n\nimpl VisitWith for ImportMeta {\n    fn visit_with<'a, V>(&'a self, _visitor: &mut V) -> ControlFlow<V::BreakTy>\n    where\n        V: Visitor<'a>,\n    {\n        ControlFlow::Continue(())\n    }\n\n    fn visit_with_mut<'a, V>(&'a mut self, _visitor: &mut V) -> ControlFlow<V::BreakTy>\n    where\n        V: VisitorMut<'a>,\n    {\n        ControlFlow::Continue(())\n    }\n}\n"
  },
  {
    "path": "core/ast/src/expression/literal/array.rs",
    "content": "//! Array declaration Expression.\n\nuse crate::expression::Expression;\nuse crate::expression::operator::assign::{AssignOp, AssignTarget};\nuse crate::pattern::{ArrayPattern, ArrayPatternElement, Pattern};\nuse crate::visitor::{VisitWith, Visitor, VisitorMut};\nuse crate::{Span, Spanned};\nuse boa_interner::{Interner, Sym, ToInternedString};\nuse core::ops::ControlFlow;\n\n/// An array is an ordered collection of data (either primitive or object depending upon the\n/// language).\n///\n/// Arrays are used to store multiple values in a single variable.\n/// This is compared to a variable that can store only one value.\n///\n/// Each item in an array has a number attached to it, called a numeric index, that allows you\n/// to access it. In JavaScript, arrays start at index zero and can be manipulated with various\n/// methods.\n///\n/// More information:\n///  - [ECMAScript reference][spec]\n///  - [MDN documentation][mdn]\n///\n/// [spec]: https://tc39.es/ecma262/#prod-ArrayLiteral\n/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array\n#[cfg_attr(feature = \"serde\", derive(serde::Serialize, serde::Deserialize))]\n#[cfg_attr(feature = \"arbitrary\", derive(arbitrary::Arbitrary))]\n#[derive(Clone, Debug, PartialEq)]\npub struct ArrayLiteral {\n    arr: Box<[Option<Expression>]>,\n    has_trailing_comma_spread: bool,\n    span: Span,\n}\n\nimpl ArrayLiteral {\n    /// Creates a new array literal.\n    pub fn new<A>(array: A, has_trailing_comma_spread: bool, span: Span) -> Self\n    where\n        A: Into<Box<[Option<Expression>]>>,\n    {\n        Self {\n            arr: array.into(),\n            has_trailing_comma_spread,\n            span,\n        }\n    }\n\n    /// Indicates if a spread operator in the array literal has a trailing comma.\n    /// This is a syntax error in some cases.\n    #[must_use]\n    pub const fn has_trailing_comma_spread(&self) -> bool {\n        self.has_trailing_comma_spread\n    }\n\n    /// Converts this `ArrayLiteral` into an [`ArrayPattern`].\n    #[must_use]\n    pub fn to_pattern(&self, strict: bool) -> Option<ArrayPattern> {\n        if self.has_trailing_comma_spread() {\n            return None;\n        }\n\n        let mut bindings = Vec::new();\n        for (i, expr) in self.arr.iter().enumerate() {\n            let Some(expr) = expr else {\n                bindings.push(ArrayPatternElement::Elision);\n                continue;\n            };\n            match expr {\n                Expression::Identifier(ident) => {\n                    if strict && *ident == Sym::ARGUMENTS {\n                        return None;\n                    }\n\n                    bindings.push(ArrayPatternElement::SingleName {\n                        ident: *ident,\n                        default_init: None,\n                    });\n                }\n                Expression::Spread(spread) => {\n                    match spread.target() {\n                        Expression::Identifier(ident) => {\n                            bindings.push(ArrayPatternElement::SingleNameRest { ident: *ident });\n                        }\n                        Expression::PropertyAccess(access) => {\n                            bindings.push(ArrayPatternElement::PropertyAccessRest {\n                                access: access.clone(),\n                            });\n                        }\n                        Expression::ArrayLiteral(array) => {\n                            let pattern = array.to_pattern(strict)?.into();\n                            bindings.push(ArrayPatternElement::PatternRest { pattern });\n                        }\n                        Expression::ObjectLiteral(object) => {\n                            let pattern = object.to_pattern(strict)?.into();\n                            bindings.push(ArrayPatternElement::PatternRest { pattern });\n                        }\n                        _ => return None,\n                    }\n                    if i + 1 != self.arr.len() {\n                        return None;\n                    }\n                }\n                Expression::Assign(assign) => {\n                    if assign.op() != AssignOp::Assign {\n                        return None;\n                    }\n                    match assign.lhs() {\n                        AssignTarget::Identifier(ident) => {\n                            let mut init = assign.rhs().clone();\n                            init.set_anonymous_function_definition_name(ident);\n                            bindings.push(ArrayPatternElement::SingleName {\n                                ident: *ident,\n                                default_init: Some(init),\n                            });\n                        }\n                        AssignTarget::Access(access) => {\n                            bindings.push(ArrayPatternElement::PropertyAccess {\n                                access: access.clone(),\n                                default_init: Some(assign.rhs().clone()),\n                            });\n                        }\n                        AssignTarget::Pattern(pattern) => match pattern {\n                            Pattern::Object(pattern) => {\n                                bindings.push(ArrayPatternElement::Pattern {\n                                    pattern: Pattern::Object(pattern.clone()),\n                                    default_init: Some(assign.rhs().clone()),\n                                });\n                            }\n                            Pattern::Array(pattern) => {\n                                bindings.push(ArrayPatternElement::Pattern {\n                                    pattern: Pattern::Array(pattern.clone()),\n                                    default_init: Some(assign.rhs().clone()),\n                                });\n                            }\n                        },\n                    }\n                }\n                Expression::ArrayLiteral(array) => {\n                    let pattern = array.to_pattern(strict)?.into();\n                    bindings.push(ArrayPatternElement::Pattern {\n                        pattern,\n                        default_init: None,\n                    });\n                }\n                Expression::ObjectLiteral(object) => {\n                    let pattern = object.to_pattern(strict)?.into();\n                    bindings.push(ArrayPatternElement::Pattern {\n                        pattern,\n                        default_init: None,\n                    });\n                }\n                Expression::PropertyAccess(access) => {\n                    bindings.push(ArrayPatternElement::PropertyAccess {\n                        access: access.clone(),\n                        default_init: None,\n                    });\n                }\n                _ => return None,\n            }\n        }\n        Some(ArrayPattern::new(bindings.into(), self.span))\n    }\n}\n\nimpl Spanned for ArrayLiteral {\n    #[inline]\n    fn span(&self) -> Span {\n        self.span\n    }\n}\n\nimpl AsRef<[Option<Expression>]> for ArrayLiteral {\n    #[inline]\n    fn as_ref(&self) -> &[Option<Expression>] {\n        &self.arr\n    }\n}\n\nimpl AsMut<[Option<Expression>]> for ArrayLiteral {\n    #[inline]\n    fn as_mut(&mut self) -> &mut [Option<Expression>] {\n        &mut self.arr\n    }\n}\n\nimpl ToInternedString for ArrayLiteral {\n    #[inline]\n    fn to_interned_string(&self, interner: &Interner) -> String {\n        let mut buf = String::from(\"[\");\n        let mut elements = self.arr.iter().peekable();\n\n        while let Some(element) = elements.next() {\n            if let Some(e) = element {\n                buf.push_str(&e.to_interned_string(interner));\n                if elements.peek().is_some() {\n                    buf.push_str(\", \");\n                }\n            } else if elements.peek().is_some() {\n                buf.push_str(\", \");\n            } else {\n                buf.push(',');\n            }\n        }\n        buf.push(']');\n        buf\n    }\n}\n\nimpl From<ArrayLiteral> for Expression {\n    #[inline]\n    fn from(arr: ArrayLiteral) -> Self {\n        Self::ArrayLiteral(arr)\n    }\n}\n\nimpl VisitWith for ArrayLiteral {\n    fn visit_with<'a, V>(&'a self, visitor: &mut V) -> ControlFlow<V::BreakTy>\n    where\n        V: Visitor<'a>,\n    {\n        for expr in self.arr.iter().flatten() {\n            visitor.visit_expression(expr)?;\n        }\n        ControlFlow::Continue(())\n    }\n\n    fn visit_with_mut<'a, V>(&'a mut self, visitor: &mut V) -> ControlFlow<V::BreakTy>\n    where\n        V: VisitorMut<'a>,\n    {\n        for expr in self.arr.iter_mut().flatten() {\n            visitor.visit_expression_mut(expr)?;\n        }\n        ControlFlow::Continue(())\n    }\n}\n"
  },
  {
    "path": "core/ast/src/expression/literal/mod.rs",
    "content": "//! This module contains all literal expressions, which represents the primitive values in ECMAScript.\n//!\n//! More information:\n//!  - [ECMAScript reference][spec]\n//!  - [MDN documentation][mdn]\n//!\n//! [spec]: https://tc39.es/ecma262/#sec-primary-expression-literals\n//! [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Grammar_and_types#Literals\n\nmod array;\nmod object;\nmod template;\n\npub use array::ArrayLiteral;\nuse core::ops::ControlFlow;\npub use object::{ObjectLiteral, ObjectMethodDefinition, PropertyDefinition};\npub use template::{TemplateElement, TemplateLiteral};\n\nuse crate::{\n    LinearSpan, LinearSpanIgnoreEq, Span, Spanned,\n    visitor::{VisitWith, Visitor, VisitorMut},\n};\nuse boa_interner::{Interner, Sym, ToInternedString};\nuse num_bigint::BigInt;\n\nuse super::Expression;\n\n/// Literals represent values in ECMAScript.\n///\n/// These are fixed values **not variables** that you literally provide in your script.\n///\n/// More information:\n///  - [ECMAScript reference][spec]\n///  - [MDN documentation][mdn]\n///\n/// [spec]: https://tc39.es/ecma262/#sec-primary-expression-literals\n/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Grammar_and_types#Literals\n#[cfg_attr(feature = \"serde\", derive(serde::Serialize, serde::Deserialize))]\n#[cfg_attr(feature = \"arbitrary\", derive(arbitrary::Arbitrary))]\n#[derive(Debug, Clone, PartialEq)]\npub struct Literal {\n    kind: LiteralKind,\n    span: Span,\n    linear_span: LinearSpanIgnoreEq,\n}\n\nimpl Literal {\n    /// Create a new [`Literal`].\n    #[inline]\n    #[must_use]\n    pub fn new<T: Into<LiteralKind>>(kind: T, span: Span) -> Self {\n        Self {\n            kind: kind.into(),\n            span,\n            linear_span: LinearSpanIgnoreEq(LinearSpan::default()),\n        }\n    }\n\n    /// Create a new [`Literal`] with a [`LinearSpan`] for source text tracking.\n    #[inline]\n    #[must_use]\n    pub fn with_linear_span<T: Into<LiteralKind>>(\n        kind: T,\n        span: Span,\n        linear_span: LinearSpan,\n    ) -> Self {\n        Self {\n            kind: kind.into(),\n            span,\n            linear_span: LinearSpanIgnoreEq(linear_span),\n        }\n    }\n\n    /// Get reference to the [`LiteralKind`] of [`Literal`].\n    #[inline]\n    #[must_use]\n    pub const fn kind(&self) -> &LiteralKind {\n        &self.kind\n    }\n\n    /// Get mutable reference to the [`LiteralKind`] of [`Literal`].\n    #[inline]\n    #[must_use]\n    pub const fn kind_mut(&mut self) -> &mut LiteralKind {\n        &mut self.kind\n    }\n\n    /// Get the [`LinearSpan`] of this literal in the source text.\n    #[inline]\n    #[must_use]\n    pub const fn linear_span(&self) -> LinearSpan {\n        self.linear_span.0\n    }\n\n    /// Get position of the node.\n    #[inline]\n    #[must_use]\n    pub const fn as_string(&self) -> Option<Sym> {\n        if let LiteralKind::String(sym) = self.kind() {\n            return Some(*sym);\n        }\n        None\n    }\n\n    /// Check if [`Literal`] is a [`LiteralKind::Undefined`].\n    #[inline]\n    #[must_use]\n    pub const fn is_undefined(&self) -> bool {\n        matches!(self.kind(), LiteralKind::Undefined)\n    }\n}\n\nimpl Spanned for Literal {\n    #[inline]\n    fn span(&self) -> Span {\n        self.span\n    }\n}\n\nimpl From<Literal> for Expression {\n    #[inline]\n    fn from(lit: Literal) -> Self {\n        Self::Literal(lit)\n    }\n}\n\nimpl ToInternedString for Literal {\n    #[inline]\n    fn to_interned_string(&self, interner: &Interner) -> String {\n        self.kind().to_interned_string(interner)\n    }\n}\n\nimpl VisitWith for Literal {\n    fn visit_with<'a, V>(&'a self, visitor: &mut V) -> ControlFlow<V::BreakTy>\n    where\n        V: Visitor<'a>,\n    {\n        if let LiteralKind::String(sym) = &self.kind {\n            visitor.visit_sym(sym)\n        } else {\n            ControlFlow::Continue(())\n        }\n    }\n\n    fn visit_with_mut<'a, V>(&'a mut self, visitor: &mut V) -> ControlFlow<V::BreakTy>\n    where\n        V: VisitorMut<'a>,\n    {\n        if let LiteralKind::String(sym) = &mut self.kind {\n            visitor.visit_sym_mut(sym)\n        } else {\n            ControlFlow::Continue(())\n        }\n    }\n}\n\n/// Literals represent values in ECMAScript.\n///\n/// These are fixed values **not variables** that you literally provide in your script.\n///\n/// More information:\n///  - [ECMAScript reference][spec]\n///  - [MDN documentation][mdn]\n///\n/// [spec]: https://tc39.es/ecma262/#sec-primary-expression-literals\n/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Grammar_and_types#Literals\n#[cfg_attr(feature = \"serde\", derive(serde::Serialize, serde::Deserialize))]\n#[derive(Clone, Debug, PartialEq)]\npub enum LiteralKind {\n    /// A string literal is zero or more characters enclosed in double (`\"`) or single (`'`) quotation marks.\n    ///\n    /// A string must be delimited by quotation marks of the same type (that is, either both single quotation marks, or both double quotation marks).\n    /// You can call any of the String object's methods on a string literal value.\n    /// ECMAScript automatically converts the string literal to a temporary String object,\n    /// calls the method, then discards the temporary String object.\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///  - [MDN documentation][mdn]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-terms-and-definitions-string-value\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Grammar_and_types#String_literals\n    String(Sym),\n\n    /// A floating-point number literal.\n    ///\n    /// The exponent part is an \"`e`\" or \"`E`\" followed by an integer, which can be signed (preceded by \"`+`\" or \"`-`\").\n    /// A floating-point literal must have at least one digit, and either a decimal point or \"`e`\" (or \"`E`\").\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///  - [MDN documentation][mdn]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-terms-and-definitions-number-value\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Grammar_and_types#Floating-point_literals\n    Num(f64),\n\n    /// Integer types can be expressed in decimal (base 10), hexadecimal (base 16), octal (base 8) and binary (base 2).\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///  - [MDN documentation][mdn]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-terms-and-definitions-number-value\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Grammar_and_types#Numeric_literals\n    Int(i32),\n\n    /// `BigInt` provides a way to represent whole numbers larger than the largest number ECMAScript\n    /// can reliably represent with the `Number` primitive.\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///  - [MDN documentation][mdn]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-terms-and-definitions-bigint-value\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Grammar_and_types#Numeric_literals\n    BigInt(Box<BigInt>),\n\n    /// The Boolean type has two literal values: `true` and `false`.\n    ///\n    /// The Boolean object is a wrapper around the primitive Boolean data type.\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///  - [MDN documentation][mdn]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-terms-and-definitions-boolean-value\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Grammar_and_types#Boolean_literals\n    Bool(bool),\n\n    /// In JavaScript, `null` is marked as one of the primitive values, cause it's behaviour is seemingly primitive.\n    ///\n    /// In computer science, a null value represents a reference that points,\n    /// generally intentionally, to a nonexistent or invalid object or address.\n    /// The meaning of a null reference varies among language implementations.\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///  - [MDN documentation][mdn]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-null-value\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Glossary/null\n    Null,\n\n    /// This represents the JavaScript `undefined` value, it does not reference the `undefined` global variable,\n    /// it will directly evaluate to `undefined`.\n    ///\n    /// NOTE: This is used for optimizations.\n    Undefined,\n}\n\n/// Manual implementation, because `Undefined` is never constructed during parsing.\n#[cfg(feature = \"arbitrary\")]\nimpl<'a> arbitrary::Arbitrary<'a> for LiteralKind {\n    fn arbitrary(u: &mut arbitrary::Unstructured<'a>) -> arbitrary::Result<Self> {\n        let c = <u8 as arbitrary::Arbitrary<'a>>::arbitrary(u)? % 6;\n        match c {\n            0 => Ok(Self::String(<Sym as arbitrary::Arbitrary>::arbitrary(u)?)),\n            1 => Ok(Self::Num(<f64 as arbitrary::Arbitrary>::arbitrary(u)?)),\n            2 => Ok(Self::Int(<i32 as arbitrary::Arbitrary>::arbitrary(u)?)),\n            3 => Ok(Self::BigInt(Box::new(\n                <BigInt as arbitrary::Arbitrary>::arbitrary(u)?,\n            ))),\n            4 => Ok(Self::Bool(<bool as arbitrary::Arbitrary>::arbitrary(u)?)),\n            5 => Ok(Self::Null),\n            _ => unreachable!(),\n        }\n    }\n}\n\nimpl From<Sym> for LiteralKind {\n    #[inline]\n    fn from(string: Sym) -> Self {\n        Self::String(string)\n    }\n}\n\nimpl From<f64> for LiteralKind {\n    #[inline]\n    fn from(num: f64) -> Self {\n        Self::Num(num)\n    }\n}\n\nimpl From<i32> for LiteralKind {\n    #[inline]\n    fn from(i: i32) -> Self {\n        Self::Int(i)\n    }\n}\n\nimpl From<BigInt> for LiteralKind {\n    #[inline]\n    fn from(i: BigInt) -> Self {\n        Self::BigInt(Box::new(i))\n    }\n}\n\nimpl From<Box<BigInt>> for LiteralKind {\n    #[inline]\n    fn from(i: Box<BigInt>) -> Self {\n        Self::BigInt(i)\n    }\n}\n\nimpl From<bool> for LiteralKind {\n    #[inline]\n    fn from(b: bool) -> Self {\n        Self::Bool(b)\n    }\n}\n\nimpl ToInternedString for LiteralKind {\n    #[inline]\n    fn to_interned_string(&self, interner: &Interner) -> String {\n        match *self {\n            Self::String(st) => {\n                format!(\"\\\"{}\\\"\", interner.resolve_expect(st))\n            }\n            Self::Num(num) => num.to_string(),\n            Self::Int(num) => num.to_string(),\n            Self::BigInt(ref num) => format!(\"{num}n\"),\n            Self::Bool(v) => v.to_string(),\n            Self::Null => \"null\".to_owned(),\n            Self::Undefined => \"undefined\".to_owned(),\n        }\n    }\n}\n"
  },
  {
    "path": "core/ast/src/expression/literal/object.rs",
    "content": "//! Object Expression.\n\nuse crate::{\n    LinearPosition, LinearSpan, LinearSpanIgnoreEq, Span, Spanned, block_to_string,\n    expression::{\n        Expression, Identifier, RESERVED_IDENTIFIERS_STRICT,\n        operator::assign::{AssignOp, AssignTarget},\n    },\n    function::{FormalParameterList, FunctionBody},\n    join_nodes,\n    operations::{ContainsSymbol, contains},\n    pattern::{ObjectPattern, ObjectPatternElement},\n    property::{MethodDefinitionKind, PropertyName},\n    scope::FunctionScopes,\n    visitor::{VisitWith, Visitor, VisitorMut},\n};\nuse boa_interner::{Interner, Sym, ToIndentedString, ToInternedString};\nuse core::{fmt::Write as _, ops::ControlFlow};\n\n/// Objects in ECMAScript may be defined as an unordered collection of related data, of\n/// primitive or reference types, in the form of “key: value” pairs.\n///\n/// Objects can be initialized using `new Object()`, `Object.create()`, or using the literal\n/// notation.\n///\n/// An object initializer is an expression that describes the initialization of an\n/// [`Object`][object]. Objects consist of properties, which are used to describe an object.\n/// Values of object properties can either contain [`primitive`][primitive] data types or other\n/// objects.\n///\n/// More information:\n///  - [ECMAScript reference][spec]\n///  - [MDN documentation][mdn]\n///\n/// [spec]: https://tc39.es/ecma262/#prod-ObjectLiteral\n/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Object_initializer\n/// [object]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object\n/// [primitive]: https://developer.mozilla.org/en-US/docs/Glossary/primitive\n#[cfg_attr(feature = \"serde\", derive(serde::Serialize, serde::Deserialize))]\n#[cfg_attr(feature = \"arbitrary\", derive(arbitrary::Arbitrary))]\n#[derive(Clone, Debug, PartialEq)]\npub struct ObjectLiteral {\n    properties: Box<[PropertyDefinition]>,\n    span: Span,\n}\n\nimpl ObjectLiteral {\n    /// Create a new [`ObjectLiteral`].\n    #[inline]\n    #[must_use]\n    pub fn new<T>(properties: T, span: Span) -> Self\n    where\n        T: Into<Box<[PropertyDefinition]>>,\n    {\n        Self {\n            properties: properties.into(),\n            span,\n        }\n    }\n\n    /// Gets the object literal properties\n    #[inline]\n    #[must_use]\n    pub const fn properties(&self) -> &[PropertyDefinition] {\n        &self.properties\n    }\n\n    /// Converts the object literal into an [`ObjectPattern`].\n    #[must_use]\n    pub fn to_pattern(&self, strict: bool) -> Option<ObjectPattern> {\n        let mut bindings = Vec::new();\n        for (i, property) in self.properties.iter().enumerate() {\n            match property {\n                PropertyDefinition::IdentifierReference(ident) if strict && *ident == Sym::EVAL => {\n                    return None;\n                }\n                PropertyDefinition::IdentifierReference(ident) => {\n                    if strict && RESERVED_IDENTIFIERS_STRICT.contains(&ident.sym()) {\n                        return None;\n                    }\n\n                    bindings.push(ObjectPatternElement::SingleName {\n                        ident: *ident,\n                        name: PropertyName::Literal(*ident),\n                        default_init: None,\n                    });\n                }\n                PropertyDefinition::Property(name, expr) => match (name, expr) {\n                    (PropertyName::Literal(name), Expression::Identifier(ident))\n                        if name.sym() == ident.sym() =>\n                    {\n                        if strict && *name == Sym::EVAL {\n                            return None;\n                        }\n                        if strict && RESERVED_IDENTIFIERS_STRICT.contains(&name.sym()) {\n                            return None;\n                        }\n\n                        bindings.push(ObjectPatternElement::SingleName {\n                            ident: *ident,\n                            name: PropertyName::Literal(*name),\n                            default_init: None,\n                        });\n                    }\n                    (PropertyName::Literal(name), Expression::Identifier(ident)) => {\n                        bindings.push(ObjectPatternElement::SingleName {\n                            ident: *ident,\n                            name: PropertyName::Literal(*name),\n                            default_init: None,\n                        });\n                    }\n                    (PropertyName::Literal(name), Expression::ObjectLiteral(object)) => {\n                        let pattern = object.to_pattern(strict)?.into();\n                        bindings.push(ObjectPatternElement::Pattern {\n                            name: PropertyName::Literal(*name),\n                            pattern,\n                            default_init: None,\n                        });\n                    }\n                    (PropertyName::Literal(name), Expression::ArrayLiteral(array)) => {\n                        let pattern = array.to_pattern(strict)?.into();\n                        bindings.push(ObjectPatternElement::Pattern {\n                            name: PropertyName::Literal(*name),\n                            pattern,\n                            default_init: None,\n                        });\n                    }\n                    (_, Expression::Assign(assign)) => {\n                        if assign.op() != AssignOp::Assign {\n                            return None;\n                        }\n                        match assign.lhs() {\n                            AssignTarget::Identifier(ident) => {\n                                if let Some(name) = name.literal() {\n                                    if name.sym() == ident.sym() {\n                                        if strict && name == Sym::EVAL {\n                                            return None;\n                                        }\n                                        if strict\n                                            && RESERVED_IDENTIFIERS_STRICT.contains(&name.sym())\n                                        {\n                                            return None;\n                                        }\n                                    }\n                                    let mut init = assign.rhs().clone();\n                                    init.set_anonymous_function_definition_name(ident);\n                                    bindings.push(ObjectPatternElement::SingleName {\n                                        ident: *ident,\n                                        name: PropertyName::Literal(name),\n                                        default_init: Some(init),\n                                    });\n                                } else {\n                                    return None;\n                                }\n                            }\n                            AssignTarget::Pattern(pattern) => {\n                                bindings.push(ObjectPatternElement::Pattern {\n                                    name: name.clone(),\n                                    pattern: pattern.clone(),\n                                    default_init: Some(assign.rhs().clone()),\n                                });\n                            }\n                            AssignTarget::Access(access) => {\n                                bindings.push(ObjectPatternElement::AssignmentPropertyAccess {\n                                    name: name.clone(),\n                                    access: access.clone(),\n                                    default_init: Some(assign.rhs().clone()),\n                                });\n                            }\n                        }\n                    }\n                    (_, Expression::PropertyAccess(access)) => {\n                        bindings.push(ObjectPatternElement::AssignmentPropertyAccess {\n                            name: name.clone(),\n                            access: access.clone(),\n                            default_init: None,\n                        });\n                    }\n                    (PropertyName::Computed(name), Expression::Identifier(ident)) => {\n                        bindings.push(ObjectPatternElement::SingleName {\n                            ident: *ident,\n                            name: PropertyName::Computed(name.clone()),\n                            default_init: None,\n                        });\n                    }\n                    _ => return None,\n                },\n                PropertyDefinition::SpreadObject(spread) => {\n                    match spread {\n                        Expression::Identifier(ident) => {\n                            bindings.push(ObjectPatternElement::RestProperty { ident: *ident });\n                        }\n                        Expression::PropertyAccess(access) => {\n                            bindings.push(ObjectPatternElement::AssignmentRestPropertyAccess {\n                                access: access.clone(),\n                            });\n                        }\n                        _ => return None,\n                    }\n                    if i + 1 != self.properties.len() {\n                        return None;\n                    }\n                }\n                PropertyDefinition::MethodDefinition(_) => return None,\n                PropertyDefinition::CoverInitializedName(ident, expr) => {\n                    if strict && [Sym::EVAL, Sym::ARGUMENTS].contains(&ident.sym()) {\n                        return None;\n                    }\n                    let mut expr = expr.clone();\n                    expr.set_anonymous_function_definition_name(ident);\n                    bindings.push(ObjectPatternElement::SingleName {\n                        ident: *ident,\n                        name: PropertyName::Literal(*ident),\n                        default_init: Some(expr),\n                    });\n                }\n            }\n        }\n\n        Some(ObjectPattern::new(bindings.into(), self.span))\n    }\n}\n\nimpl Spanned for ObjectLiteral {\n    #[inline]\n    fn span(&self) -> Span {\n        self.span\n    }\n}\n\nimpl ToIndentedString for ObjectLiteral {\n    fn to_indented_string(&self, interner: &Interner, indent_n: usize) -> String {\n        let mut buf = \"{\\n\".to_owned();\n        let indentation = \"    \".repeat(indent_n + 1);\n        for property in &*self.properties {\n            match property {\n                PropertyDefinition::IdentifierReference(ident) => {\n                    let _ = writeln!(\n                        buf,\n                        \"{indentation}{},\",\n                        interner.resolve_expect(ident.sym())\n                    );\n                }\n                PropertyDefinition::Property(key, value) => {\n                    let _ = writeln!(\n                        buf,\n                        \"{indentation}{}: {},\",\n                        key.to_interned_string(interner),\n                        value.to_no_indent_string(interner, indent_n + 1)\n                    );\n                }\n                PropertyDefinition::SpreadObject(key) => {\n                    let _ = writeln!(buf, \"{indentation}...{},\", key.to_interned_string(interner));\n                }\n                PropertyDefinition::MethodDefinition(m) => {\n                    buf.push_str(&m.to_indented_string(interner, indent_n));\n                }\n                PropertyDefinition::CoverInitializedName(ident, expr) => {\n                    let _ = writeln!(\n                        buf,\n                        \"{indentation}{} = {},\",\n                        interner.resolve_expect(ident.sym()),\n                        expr.to_no_indent_string(interner, indent_n + 1)\n                    );\n                }\n            }\n        }\n        let _ = write!(buf, \"{}}}\", \"    \".repeat(indent_n));\n\n        buf\n    }\n}\n\nimpl From<ObjectLiteral> for Expression {\n    #[inline]\n    fn from(obj: ObjectLiteral) -> Self {\n        Self::ObjectLiteral(obj)\n    }\n}\n\nimpl VisitWith for ObjectLiteral {\n    fn visit_with<'a, V>(&'a self, visitor: &mut V) -> ControlFlow<V::BreakTy>\n    where\n        V: Visitor<'a>,\n    {\n        for pd in &*self.properties {\n            visitor.visit_property_definition(pd)?;\n        }\n        ControlFlow::Continue(())\n    }\n\n    fn visit_with_mut<'a, V>(&'a mut self, visitor: &mut V) -> ControlFlow<V::BreakTy>\n    where\n        V: VisitorMut<'a>,\n    {\n        for pd in &mut *self.properties {\n            visitor.visit_property_definition_mut(pd)?;\n        }\n        ControlFlow::Continue(())\n    }\n}\n\n/// Describes the definition of a property within an object literal.\n///\n/// A property has a name (a string) and a value (primitive, method, or object reference).\n/// Note that when we say that \"a property holds an object\", that is shorthand for \"a property holds an object reference\".\n/// This distinction matters because the original referenced object remains unchanged when you change the property's value.\n///\n/// More information:\n///  - [ECMAScript reference][spec]\n///  - [MDN documentation][mdn]\n///\n/// [spec]: https://tc39.es/ecma262/#prod-PropertyDefinition\n/// [mdn]: https://developer.mozilla.org/en-US/docs/Glossary/property/JavaScript\n// TODO: Support all features: https://tc39.es/ecma262/#prod-PropertyDefinition\n#[cfg_attr(feature = \"serde\", derive(serde::Serialize, serde::Deserialize))]\n#[cfg_attr(feature = \"arbitrary\", derive(arbitrary::Arbitrary))]\n#[derive(Clone, Debug, PartialEq)]\npub enum PropertyDefinition {\n    /// Puts a variable into an object.\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///  - [MDN documentation][mdn]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#prod-IdentifierReference\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Object_initializer#Property_definitions\n    IdentifierReference(Identifier),\n\n    /// Binds a property name to a JavaScript value.\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///  - [MDN documentation][mdn]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#prod-PropertyDefinition\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Object_initializer#Property_definitions\n    Property(PropertyName, Expression),\n\n    /// A property of an object can also refer to a function or a getter or setter method.\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///  - [MDN documentation][mdn]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#prod-MethodDefinition\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Object_initializer#Method_definitions\n    MethodDefinition(ObjectMethodDefinition),\n\n    /// The Rest/Spread Properties for ECMAScript proposal (stage 4) adds spread properties to object literals.\n    /// It copies own enumerable properties from a provided object onto a new object.\n    ///\n    /// Shallow-cloning (excluding `prototype`) or merging objects is now possible using a shorter syntax than `Object.assign()`.\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///  - [MDN documentation][mdn]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#prod-PropertyDefinition\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Object_initializer#Spread_properties\n    SpreadObject(Expression),\n\n    /// Cover grammar for when an object literal is used as an object binding pattern.\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#prod-CoverInitializedName\n    CoverInitializedName(Identifier, Expression),\n}\n\nimpl VisitWith for PropertyDefinition {\n    fn visit_with<'a, V>(&'a self, visitor: &mut V) -> ControlFlow<V::BreakTy>\n    where\n        V: Visitor<'a>,\n    {\n        match self {\n            Self::IdentifierReference(id) => visitor.visit_identifier(id),\n            Self::Property(pn, expr) => {\n                visitor.visit_property_name(pn)?;\n                visitor.visit_expression(expr)\n            }\n            Self::MethodDefinition(m) => visitor.visit_object_method_definition(m),\n            Self::SpreadObject(expr) => visitor.visit_expression(expr),\n            Self::CoverInitializedName(id, expr) => {\n                visitor.visit_identifier(id)?;\n                visitor.visit_expression(expr)\n            }\n        }\n    }\n\n    fn visit_with_mut<'a, V>(&'a mut self, visitor: &mut V) -> ControlFlow<V::BreakTy>\n    where\n        V: VisitorMut<'a>,\n    {\n        match self {\n            Self::IdentifierReference(id) => visitor.visit_identifier_mut(id),\n            Self::Property(pn, expr) => {\n                visitor.visit_property_name_mut(pn)?;\n                visitor.visit_expression_mut(expr)\n            }\n            Self::MethodDefinition(m) => visitor.visit_object_method_definition_mut(m),\n            Self::SpreadObject(expr) => visitor.visit_expression_mut(expr),\n            Self::CoverInitializedName(id, expr) => {\n                visitor.visit_identifier_mut(id)?;\n                visitor.visit_expression_mut(expr)\n            }\n        }\n    }\n}\n\n/// A method definition.\n///\n/// This type is specific to object method definitions.\n///\n/// More information:\n///  - [ECMAScript reference][spec]\n///\n/// [spec]: https://tc39.es/ecma262/#prod-MethodDefinition\n#[cfg_attr(feature = \"serde\", derive(serde::Serialize, serde::Deserialize))]\n#[cfg_attr(feature = \"arbitrary\", derive(arbitrary::Arbitrary))]\n#[derive(Clone, Debug, PartialEq)]\npub struct ObjectMethodDefinition {\n    pub(crate) name: PropertyName,\n    pub(crate) parameters: FormalParameterList,\n    pub(crate) body: FunctionBody,\n    pub(crate) contains_direct_eval: bool,\n    kind: MethodDefinitionKind,\n\n    #[cfg_attr(feature = \"serde\", serde(skip))]\n    pub(crate) scopes: FunctionScopes,\n    linear_span: LinearSpanIgnoreEq,\n}\n\nimpl ObjectMethodDefinition {\n    /// Creates a new object method definition.\n    #[inline]\n    #[must_use]\n    pub fn new(\n        name: PropertyName,\n        parameters: FormalParameterList,\n        body: FunctionBody,\n        kind: MethodDefinitionKind,\n        start_linear_pos: LinearPosition,\n    ) -> Self {\n        let contains_direct_eval = contains(&parameters, ContainsSymbol::DirectEval)\n            || contains(&body, ContainsSymbol::DirectEval);\n        let linear_span = LinearSpan::new(start_linear_pos, body.linear_pos_end()).into();\n\n        Self {\n            name,\n            parameters,\n            body,\n            contains_direct_eval,\n            kind,\n            scopes: FunctionScopes::default(),\n            linear_span,\n        }\n    }\n\n    /// Returns the name of the object method definition.\n    #[inline]\n    #[must_use]\n    pub const fn name(&self) -> &PropertyName {\n        &self.name\n    }\n\n    /// Returns the parameters of the object method definition.\n    #[inline]\n    #[must_use]\n    pub const fn parameters(&self) -> &FormalParameterList {\n        &self.parameters\n    }\n\n    /// Returns the body of the object method definition.\n    #[inline]\n    #[must_use]\n    pub const fn body(&self) -> &FunctionBody {\n        &self.body\n    }\n\n    /// Returns the kind of the object method definition.\n    #[inline]\n    #[must_use]\n    pub const fn kind(&self) -> MethodDefinitionKind {\n        self.kind\n    }\n\n    /// Gets the scopes of the object method definition.\n    #[inline]\n    #[must_use]\n    pub const fn scopes(&self) -> &FunctionScopes {\n        &self.scopes\n    }\n\n    /// Gets linear span of the function declaration.\n    #[inline]\n    #[must_use]\n    pub const fn linear_span(&self) -> LinearSpan {\n        self.linear_span.0\n    }\n\n    /// Returns `true` if the object method definition contains a direct call to `eval`.\n    #[inline]\n    #[must_use]\n    pub const fn contains_direct_eval(&self) -> bool {\n        self.contains_direct_eval\n    }\n}\n\nimpl ToIndentedString for ObjectMethodDefinition {\n    fn to_indented_string(&self, interner: &Interner, indent_n: usize) -> String {\n        let indentation = \"    \".repeat(indent_n + 1);\n        let prefix = match &self.kind {\n            MethodDefinitionKind::Get => \"get \",\n            MethodDefinitionKind::Set => \"set \",\n            MethodDefinitionKind::Ordinary => \"\",\n            MethodDefinitionKind::Generator => \"*\",\n            MethodDefinitionKind::AsyncGenerator => \"async *\",\n            MethodDefinitionKind::Async => \"async \",\n        };\n        let name = self.name.to_interned_string(interner);\n        let parameters = join_nodes(interner, self.parameters.as_ref());\n        let body = block_to_string(&self.body.statements, interner, indent_n + 1);\n        format!(\"{indentation}{prefix}{name}({parameters}) {body},\\n\")\n    }\n}\n\nimpl VisitWith for ObjectMethodDefinition {\n    fn visit_with<'a, V>(&'a self, visitor: &mut V) -> ControlFlow<V::BreakTy>\n    where\n        V: Visitor<'a>,\n    {\n        visitor.visit_property_name(&self.name)?;\n        visitor.visit_formal_parameter_list(&self.parameters)?;\n        visitor.visit_function_body(&self.body)\n    }\n\n    fn visit_with_mut<'a, V>(&'a mut self, visitor: &mut V) -> ControlFlow<V::BreakTy>\n    where\n        V: VisitorMut<'a>,\n    {\n        visitor.visit_property_name_mut(&mut self.name)?;\n        visitor.visit_formal_parameter_list_mut(&mut self.parameters)?;\n        visitor.visit_function_body_mut(&mut self.body)\n    }\n}\n"
  },
  {
    "path": "core/ast/src/expression/literal/template.rs",
    "content": "//! Template literal Expression.\n\nuse crate::{\n    Span, Spanned,\n    expression::Expression,\n    visitor::{VisitWith, Visitor, VisitorMut},\n};\nuse boa_interner::{Interner, Sym, ToInternedString};\nuse core::{fmt::Write as _, ops::ControlFlow};\n\n/// Template literals are string literals allowing embedded expressions.\n///\n/// More information:\n///  - [ECMAScript reference][spec]\n///  - [MDN documentation][mdn]\n///\n/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Template_literals\n/// [spec]: https://tc39.es/ecma262/#sec-template-literals\n#[cfg_attr(feature = \"serde\", derive(serde::Serialize, serde::Deserialize))]\n#[derive(Clone, Debug, PartialEq)]\npub struct TemplateLiteral {\n    elements: Box<[TemplateElement]>,\n    span: Span,\n}\n\n/// Manual implementation, because string and expression in the element list must always appear in order.\n#[cfg(feature = \"arbitrary\")]\nimpl<'a> arbitrary::Arbitrary<'a> for TemplateLiteral {\n    fn arbitrary(u: &mut arbitrary::Unstructured<'a>) -> arbitrary::Result<Self> {\n        let len = u.arbitrary_len::<Box<[TemplateElement]>>()?;\n\n        let mut elements = Vec::with_capacity(len);\n        for i in 0..len {\n            if i & 1 == 0 {\n                elements.push(TemplateElement::String(\n                    <Sym as arbitrary::Arbitrary>::arbitrary(u)?,\n                ));\n            } else {\n                elements.push(TemplateElement::Expr(Expression::arbitrary(u)?));\n            }\n        }\n\n        Ok(Self::new(elements.into_boxed_slice(), Span::arbitrary(u)?))\n    }\n}\n\nimpl From<TemplateLiteral> for Expression {\n    #[inline]\n    fn from(tem: TemplateLiteral) -> Self {\n        Self::TemplateLiteral(tem)\n    }\n}\n\nimpl TemplateLiteral {\n    /// Creates a new `TemplateLiteral` from a list of [`TemplateElement`]s.\n    #[inline]\n    #[must_use]\n    pub fn new(elements: Box<[TemplateElement]>, span: Span) -> Self {\n        Self { elements, span }\n    }\n\n    /// Gets the element list of this `TemplateLiteral`.\n    #[must_use]\n    pub const fn elements(&self) -> &[TemplateElement] {\n        &self.elements\n    }\n}\n\nimpl Spanned for TemplateLiteral {\n    #[inline]\n    fn span(&self) -> Span {\n        self.span\n    }\n}\n\nimpl ToInternedString for TemplateLiteral {\n    #[inline]\n    fn to_interned_string(&self, interner: &Interner) -> String {\n        let mut buf = \"`\".to_owned();\n\n        for elt in &self.elements {\n            match elt {\n                TemplateElement::String(s) => {\n                    let _ = write!(buf, \"{}\", interner.resolve_expect(*s));\n                }\n                TemplateElement::Expr(n) => {\n                    let _ = write!(buf, \"${{{}}}\", n.to_interned_string(interner));\n                }\n            }\n        }\n        buf.push('`');\n\n        buf\n    }\n}\n\nimpl VisitWith for TemplateLiteral {\n    fn visit_with<'a, V>(&'a self, visitor: &mut V) -> ControlFlow<V::BreakTy>\n    where\n        V: Visitor<'a>,\n    {\n        for element in &*self.elements {\n            visitor.visit_template_element(element)?;\n        }\n        ControlFlow::Continue(())\n    }\n\n    fn visit_with_mut<'a, V>(&'a mut self, visitor: &mut V) -> ControlFlow<V::BreakTy>\n    where\n        V: VisitorMut<'a>,\n    {\n        for element in &mut *self.elements {\n            visitor.visit_template_element_mut(element)?;\n        }\n        ControlFlow::Continue(())\n    }\n}\n\n/// An element found within a [`TemplateLiteral`].\n///\n/// The [spec] doesn't define an element akin to `TemplateElement`. However, the AST defines this\n/// node as the equivalent of the components found in a template literal.\n///\n/// [spec]: https://tc39.es/ecma262/#sec-template-literals\n#[cfg_attr(feature = \"serde\", derive(serde::Serialize, serde::Deserialize))]\n#[cfg_attr(feature = \"arbitrary\", derive(arbitrary::Arbitrary))]\n#[derive(Clone, Debug, PartialEq)]\npub enum TemplateElement {\n    /// A simple string.\n    String(Sym),\n    /// An expression that is evaluated and replaced by its string representation.\n    Expr(Expression),\n}\n\nimpl VisitWith for TemplateElement {\n    fn visit_with<'a, V>(&'a self, visitor: &mut V) -> ControlFlow<V::BreakTy>\n    where\n        V: Visitor<'a>,\n    {\n        match self {\n            Self::String(sym) => visitor.visit_sym(sym),\n            Self::Expr(expr) => visitor.visit_expression(expr),\n        }\n    }\n\n    fn visit_with_mut<'a, V>(&'a mut self, visitor: &mut V) -> ControlFlow<V::BreakTy>\n    where\n        V: VisitorMut<'a>,\n    {\n        match self {\n            Self::String(sym) => visitor.visit_sym_mut(sym),\n            Self::Expr(expr) => visitor.visit_expression_mut(expr),\n        }\n    }\n}\n"
  },
  {
    "path": "core/ast/src/expression/mod.rs",
    "content": "//! The [`Expression`] Parse Node, as defined by the [spec].\n//!\n//! ECMAScript expressions include:\n//! - [Primary][primary] expressions (`this`, function expressions, literals).\n//! - [Left hand side][lhs] expressions (accessors, `new` operator, `super`).\n//! - [operator] expressions.\n//!\n//! [spec]: https://tc39.es/ecma262/#prod-Expression\n//! [primary]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators#primary_expressions\n//! [lhs]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators#left-hand-side_expressions\n\nuse self::{\n    access::PropertyAccess,\n    literal::{ArrayLiteral, ObjectLiteral, TemplateLiteral},\n    operator::{Assign, Binary, BinaryInPrivate, Conditional, Unary, Update},\n};\nuse super::{\n    Spanned, Statement,\n    function::AsyncArrowFunction,\n    function::{\n        ArrowFunction, AsyncFunctionExpression, AsyncGeneratorExpression, ClassExpression,\n        FunctionExpression, GeneratorExpression,\n    },\n};\nuse boa_interner::{Interner, ToIndentedString, ToInternedString};\nuse core::ops::ControlFlow;\nuse literal::Literal;\n\nmod r#await;\nmod call;\nmod identifier;\nmod import_meta;\nmod new;\nmod new_target;\nmod optional;\nmod parenthesized;\nmod regexp;\nmod spread;\nmod tagged_template;\nmod this;\nmod r#yield;\n\nuse crate::{\n    Span,\n    visitor::{VisitWith, Visitor, VisitorMut},\n};\npub use r#await::Await;\npub use call::{Call, ImportCall, ImportPhase, SuperCall};\npub use identifier::{Identifier, RESERVED_IDENTIFIERS_STRICT};\npub use import_meta::ImportMeta;\npub use new::New;\npub use new_target::NewTarget;\npub use optional::{Optional, OptionalOperation, OptionalOperationKind};\npub use parenthesized::Parenthesized;\npub use regexp::RegExpLiteral;\npub use spread::Spread;\npub use tagged_template::TaggedTemplate;\npub use this::This;\npub use r#yield::Yield;\n\npub mod access;\npub mod literal;\npub mod operator;\n\n/// The `Expression` Parse Node.\n///\n/// See the [module level documentation][self] for more information.\n#[cfg_attr(feature = \"serde\", derive(serde::Serialize, serde::Deserialize))]\n#[cfg_attr(feature = \"arbitrary\", derive(arbitrary::Arbitrary))]\n#[derive(Debug, Clone, PartialEq)]\npub enum Expression {\n    /// The ECMAScript `this` keyword refers to the object it belongs to.\n    ///\n    /// A property of an execution context (global, function or eval) that,\n    /// in non–strict mode, is always a reference to an object and in strict\n    /// mode can be any value.\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///  - [MDN documentation][mdn]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-this-keyword\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/this\n    This(This),\n\n    /// See [`Identifier`].\n    Identifier(Identifier),\n\n    /// See [`Literal`].\n    Literal(Literal),\n\n    /// See [`RegExpLiteral`].\n    RegExpLiteral(RegExpLiteral),\n\n    /// See [`ArrayLiteral`].\n    ArrayLiteral(ArrayLiteral),\n\n    /// See [`ObjectLiteral`].\n    ObjectLiteral(ObjectLiteral),\n\n    /// See [`Spread`],\n    Spread(Spread),\n\n    /// See [`FunctionExpression`].\n    FunctionExpression(FunctionExpression),\n\n    /// See [`ArrowFunction`].\n    ArrowFunction(ArrowFunction),\n\n    /// See [`AsyncArrowFunction`].\n    AsyncArrowFunction(AsyncArrowFunction),\n\n    /// See [`GeneratorExpression`].\n    GeneratorExpression(GeneratorExpression),\n\n    /// See [`AsyncFunctionExpression`].\n    AsyncFunctionExpression(AsyncFunctionExpression),\n\n    /// See [`AsyncGeneratorExpression`].\n    AsyncGeneratorExpression(AsyncGeneratorExpression),\n\n    /// See [`ClassExpression`].\n    ClassExpression(Box<ClassExpression>),\n\n    /// See [`TemplateLiteral`].\n    TemplateLiteral(TemplateLiteral),\n\n    /// See [`PropertyAccess`].\n    PropertyAccess(PropertyAccess),\n\n    /// See [`New`].\n    New(New),\n\n    /// See [`Call`].\n    Call(Call),\n\n    /// See [`SuperCall`].\n    SuperCall(SuperCall),\n\n    /// See [`ImportCall`].\n    ImportCall(ImportCall),\n\n    /// See [`Optional`].\n    Optional(Optional),\n\n    /// See [`TaggedTemplate`].\n    TaggedTemplate(TaggedTemplate),\n\n    /// The `new.target` pseudo-property expression.\n    NewTarget(NewTarget),\n\n    /// The `import.meta` pseudo-property expression.\n    ImportMeta(ImportMeta),\n\n    /// See [`Assign`].\n    Assign(Assign),\n\n    /// See [`Unary`].\n    Unary(Unary),\n\n    /// See [`Unary`].\n    Update(Update),\n\n    /// See [`Binary`].\n    Binary(Binary),\n\n    /// See [`BinaryInPrivate`].\n    BinaryInPrivate(BinaryInPrivate),\n\n    /// See [`Conditional`].\n    Conditional(Conditional),\n\n    /// See [`Await`].\n    Await(Await),\n\n    /// See [`Yield`].\n    Yield(Yield),\n\n    /// See [`Parenthesized`].\n    Parenthesized(Parenthesized),\n}\n\nimpl Expression {\n    /// Implements the display formatting with indentation.\n    ///\n    /// This will not prefix the value with any indentation. If you want to prefix this with proper\n    /// indents, use [`to_indented_string()`](Self::to_indented_string).\n    pub(crate) fn to_no_indent_string(&self, interner: &Interner, indentation: usize) -> String {\n        match self {\n            Self::This(this) => this.to_interned_string(interner),\n            Self::Identifier(id) => id.to_interned_string(interner),\n            Self::Literal(lit) => lit.to_interned_string(interner),\n            Self::ArrayLiteral(arr) => arr.to_interned_string(interner),\n            Self::ObjectLiteral(o) => o.to_indented_string(interner, indentation),\n            Self::Spread(sp) => sp.to_interned_string(interner),\n            Self::FunctionExpression(f) => f.to_indented_string(interner, indentation),\n            Self::AsyncArrowFunction(f) => f.to_indented_string(interner, indentation),\n            Self::ArrowFunction(arrf) => arrf.to_indented_string(interner, indentation),\n            Self::ClassExpression(cl) => cl.to_indented_string(interner, indentation),\n            Self::GeneratorExpression(r#gen) => r#gen.to_indented_string(interner, indentation),\n            Self::AsyncFunctionExpression(asf) => asf.to_indented_string(interner, indentation),\n            Self::AsyncGeneratorExpression(asgen) => {\n                asgen.to_indented_string(interner, indentation)\n            }\n            Self::TemplateLiteral(tem) => tem.to_interned_string(interner),\n            Self::PropertyAccess(prop) => prop.to_interned_string(interner),\n            Self::New(new) => new.to_interned_string(interner),\n            Self::Call(call) => call.to_interned_string(interner),\n            Self::SuperCall(supc) => supc.to_interned_string(interner),\n            Self::ImportCall(impc) => impc.to_interned_string(interner),\n            Self::Optional(opt) => opt.to_interned_string(interner),\n            Self::NewTarget(new_target) => new_target.to_interned_string(interner),\n            Self::ImportMeta(import_meta) => import_meta.to_interned_string(interner),\n            Self::TaggedTemplate(tag) => tag.to_interned_string(interner),\n            Self::Assign(assign) => assign.to_interned_string(interner),\n            Self::Unary(unary) => unary.to_interned_string(interner),\n            Self::Update(update) => update.to_interned_string(interner),\n            Self::Binary(bin) => bin.to_interned_string(interner),\n            Self::BinaryInPrivate(bin) => bin.to_interned_string(interner),\n            Self::Conditional(cond) => cond.to_interned_string(interner),\n            Self::Await(aw) => aw.to_interned_string(interner),\n            Self::Yield(yi) => yi.to_interned_string(interner),\n            Self::Parenthesized(expr) => expr.to_interned_string(interner),\n            Self::RegExpLiteral(regexp) => regexp.to_interned_string(interner),\n        }\n    }\n\n    /// Returns if the expression is a function definition without a name.\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-isanonymousfunctiondefinition\n    #[must_use]\n    #[inline]\n    pub const fn is_anonymous_function_definition(&self) -> bool {\n        match self {\n            Self::ArrowFunction(f) => f.name().is_none(),\n            Self::AsyncArrowFunction(f) => f.name().is_none(),\n            Self::FunctionExpression(f) => f.name().is_none(),\n            Self::GeneratorExpression(f) => f.name().is_none(),\n            Self::AsyncGeneratorExpression(f) => f.name().is_none(),\n            Self::AsyncFunctionExpression(f) => f.name().is_none(),\n            Self::ClassExpression(f) => f.name().is_none(),\n            Self::Parenthesized(p) => p.expression().is_anonymous_function_definition(),\n            _ => false,\n        }\n    }\n\n    /// Sets the name of an anonymous function definition.\n    ///\n    /// This is used to set the name of a function expression when it is assigned to a variable.\n    /// If the function already has a name, this does nothing.\n    pub fn set_anonymous_function_definition_name(&mut self, name: &Identifier) {\n        match self {\n            Self::ArrowFunction(f) if f.name().is_none() => f.name = Some(*name),\n            Self::AsyncArrowFunction(f) if f.name().is_none() => f.name = Some(*name),\n            Self::FunctionExpression(f) if f.name().is_none() => f.name = Some(*name),\n            Self::GeneratorExpression(f) if f.name().is_none() => f.name = Some(*name),\n            Self::AsyncGeneratorExpression(f) if f.name().is_none() => f.name = Some(*name),\n            Self::AsyncFunctionExpression(f) if f.name().is_none() => f.name = Some(*name),\n            Self::ClassExpression(f) if f.name().is_none() => f.name = Some(*name),\n            Self::Parenthesized(p) => p.expression.set_anonymous_function_definition_name(name),\n            _ => {}\n        }\n    }\n\n    /// Returns the expression without any outer parenthesized expressions.\n    #[must_use]\n    #[inline]\n    pub const fn flatten(&self) -> &Self {\n        let mut expression = self;\n        while let Self::Parenthesized(p) = expression {\n            expression = p.expression();\n        }\n        expression\n    }\n}\n\nimpl Spanned for Expression {\n    #[inline]\n    fn span(&self) -> Span {\n        match self {\n            Self::This(this) => this.span(),\n            Self::Identifier(id) => id.span(),\n            Self::Literal(lit) => lit.span(),\n            Self::ArrayLiteral(arr) => arr.span(),\n            Self::ObjectLiteral(o) => o.span(),\n            Self::Spread(sp) => sp.span(),\n            Self::FunctionExpression(f) => f.span(),\n            Self::AsyncArrowFunction(f) => f.span(),\n            Self::ArrowFunction(arrf) => arrf.span(),\n            Self::ClassExpression(cl) => cl.span(),\n            Self::GeneratorExpression(r#gen) => r#gen.span(),\n            Self::AsyncFunctionExpression(asf) => asf.span(),\n            Self::AsyncGeneratorExpression(asgen) => asgen.span(),\n            Self::TemplateLiteral(tem) => tem.span(),\n            Self::PropertyAccess(prop) => prop.span(),\n            Self::New(new) => new.span(),\n            Self::Call(call) => call.span(),\n            Self::SuperCall(supc) => supc.span(),\n            Self::ImportCall(impc) => impc.span(),\n            Self::Optional(opt) => opt.span(),\n            Self::NewTarget(new_target) => new_target.span(),\n            Self::ImportMeta(import_meta) => import_meta.span(),\n            Self::TaggedTemplate(tag) => tag.span(),\n            Self::Assign(assign) => assign.span(),\n            Self::Unary(unary) => unary.span(),\n            Self::Update(update) => update.span(),\n            Self::Binary(bin) => bin.span(),\n            Self::BinaryInPrivate(bin) => bin.span(),\n            Self::Conditional(cond) => cond.span(),\n            Self::Await(aw) => aw.span(),\n            Self::Yield(yi) => yi.span(),\n            Self::Parenthesized(expr) => expr.span(),\n            Self::RegExpLiteral(regexp) => regexp.span(),\n        }\n    }\n}\n\nimpl From<Expression> for Statement {\n    #[inline]\n    fn from(expr: Expression) -> Self {\n        Self::Expression(expr)\n    }\n}\n\nimpl ToIndentedString for Expression {\n    #[inline]\n    fn to_indented_string(&self, interner: &Interner, indentation: usize) -> String {\n        self.to_no_indent_string(interner, indentation)\n    }\n}\n\nimpl VisitWith for Expression {\n    fn visit_with<'a, V>(&'a self, visitor: &mut V) -> ControlFlow<V::BreakTy>\n    where\n        V: Visitor<'a>,\n    {\n        match self {\n            Self::This(this) => visitor.visit_this(this),\n            Self::Identifier(id) => visitor.visit_identifier(id),\n            Self::Literal(lit) => visitor.visit_literal(lit),\n            Self::RegExpLiteral(regexp) => visitor.visit_reg_exp_literal(regexp),\n            Self::ArrayLiteral(arlit) => visitor.visit_array_literal(arlit),\n            Self::ObjectLiteral(olit) => visitor.visit_object_literal(olit),\n            Self::Spread(sp) => visitor.visit_spread(sp),\n            Self::FunctionExpression(f) => visitor.visit_function_expression(f),\n            Self::ArrowFunction(af) => visitor.visit_arrow_function(af),\n            Self::AsyncArrowFunction(af) => visitor.visit_async_arrow_function(af),\n            Self::GeneratorExpression(g) => visitor.visit_generator_expression(g),\n            Self::AsyncFunctionExpression(af) => visitor.visit_async_function_expression(af),\n            Self::AsyncGeneratorExpression(ag) => visitor.visit_async_generator_expression(ag),\n            Self::ClassExpression(c) => visitor.visit_class_expression(c),\n            Self::TemplateLiteral(tlit) => visitor.visit_template_literal(tlit),\n            Self::PropertyAccess(pa) => visitor.visit_property_access(pa),\n            Self::New(n) => visitor.visit_new(n),\n            Self::Call(c) => visitor.visit_call(c),\n            Self::SuperCall(sc) => visitor.visit_super_call(sc),\n            Self::ImportCall(ic) => visitor.visit_import_call(ic),\n            Self::Optional(opt) => visitor.visit_optional(opt),\n            Self::TaggedTemplate(tt) => visitor.visit_tagged_template(tt),\n            Self::Assign(a) => visitor.visit_assign(a),\n            Self::Unary(u) => visitor.visit_unary(u),\n            Self::Update(u) => visitor.visit_update(u),\n            Self::Binary(b) => visitor.visit_binary(b),\n            Self::BinaryInPrivate(b) => visitor.visit_binary_in_private(b),\n            Self::Conditional(c) => visitor.visit_conditional(c),\n            Self::Await(a) => visitor.visit_await(a),\n            Self::Yield(y) => visitor.visit_yield(y),\n            Self::Parenthesized(e) => visitor.visit_parenthesized(e),\n            Self::NewTarget(new_target) => visitor.visit_new_target(new_target),\n            Self::ImportMeta(import_meta) => visitor.visit_import_meta(import_meta),\n        }\n    }\n\n    fn visit_with_mut<'a, V>(&'a mut self, visitor: &mut V) -> ControlFlow<V::BreakTy>\n    where\n        V: VisitorMut<'a>,\n    {\n        match self {\n            Self::This(this) => visitor.visit_this_mut(this),\n            Self::Identifier(id) => visitor.visit_identifier_mut(id),\n            Self::Literal(lit) => visitor.visit_literal_mut(lit),\n            Self::RegExpLiteral(regexp) => visitor.visit_reg_exp_literal_mut(regexp),\n            Self::ArrayLiteral(arlit) => visitor.visit_array_literal_mut(arlit),\n            Self::ObjectLiteral(olit) => visitor.visit_object_literal_mut(olit),\n            Self::Spread(sp) => visitor.visit_spread_mut(sp),\n            Self::FunctionExpression(f) => visitor.visit_function_expression_mut(f),\n            Self::ArrowFunction(af) => visitor.visit_arrow_function_mut(af),\n            Self::AsyncArrowFunction(af) => visitor.visit_async_arrow_function_mut(af),\n            Self::GeneratorExpression(g) => visitor.visit_generator_expression_mut(g),\n            Self::AsyncFunctionExpression(af) => visitor.visit_async_function_expression_mut(af),\n            Self::AsyncGeneratorExpression(ag) => visitor.visit_async_generator_expression_mut(ag),\n            Self::ClassExpression(c) => visitor.visit_class_expression_mut(c),\n            Self::TemplateLiteral(tlit) => visitor.visit_template_literal_mut(tlit),\n            Self::PropertyAccess(pa) => visitor.visit_property_access_mut(pa),\n            Self::New(n) => visitor.visit_new_mut(n),\n            Self::Call(c) => visitor.visit_call_mut(c),\n            Self::SuperCall(sc) => visitor.visit_super_call_mut(sc),\n            Self::ImportCall(ic) => visitor.visit_import_call_mut(ic),\n            Self::Optional(opt) => visitor.visit_optional_mut(opt),\n            Self::TaggedTemplate(tt) => visitor.visit_tagged_template_mut(tt),\n            Self::Assign(a) => visitor.visit_assign_mut(a),\n            Self::Unary(u) => visitor.visit_unary_mut(u),\n            Self::Update(u) => visitor.visit_update_mut(u),\n            Self::Binary(b) => visitor.visit_binary_mut(b),\n            Self::BinaryInPrivate(b) => visitor.visit_binary_in_private_mut(b),\n            Self::Conditional(c) => visitor.visit_conditional_mut(c),\n            Self::Await(a) => visitor.visit_await_mut(a),\n            Self::Yield(y) => visitor.visit_yield_mut(y),\n            Self::Parenthesized(e) => visitor.visit_parenthesized_mut(e),\n            Self::NewTarget(new_target) => visitor.visit_new_target_mut(new_target),\n            Self::ImportMeta(import_meta) => visitor.visit_import_meta_mut(import_meta),\n        }\n    }\n}\n"
  },
  {
    "path": "core/ast/src/expression/new.rs",
    "content": "use crate::expression::Call;\nuse crate::visitor::{VisitWith, Visitor, VisitorMut};\nuse crate::{Span, Spanned};\nuse boa_interner::{Interner, ToInternedString};\nuse core::ops::ControlFlow;\n\nuse super::Expression;\n\n/// The `new` operator lets developers create an instance of a user-defined object type or of\n/// one of the built-in object types that has a constructor function.\n///\n/// The new keyword does the following things:\n///  - Creates a blank, plain JavaScript object;\n///  - Links (sets the constructor of) this object to another object;\n///  - Passes the newly created object from Step 1 as the this context;\n///  - Returns this if the function doesn't return its own object.\n///\n/// More information:\n///  - [ECMAScript reference][spec]\n///  - [MDN documentation][mdn]\n///\n/// [spec]: https://tc39.es/ecma262/#prod-NewExpression\n/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/new\n#[cfg_attr(feature = \"serde\", derive(serde::Serialize, serde::Deserialize))]\n#[cfg_attr(feature = \"arbitrary\", derive(arbitrary::Arbitrary))]\n#[derive(Clone, Debug, PartialEq)]\npub struct New {\n    call: Call,\n}\n\nimpl New {\n    /// Gets the constructor of the new expression.\n    #[inline]\n    #[must_use]\n    pub const fn constructor(&self) -> &Expression {\n        self.call.function()\n    }\n\n    /// Retrieves the arguments passed to the constructor.\n    #[inline]\n    #[must_use]\n    pub const fn arguments(&self) -> &[Expression] {\n        self.call.args()\n    }\n\n    /// Returns the inner call expression.\n    #[must_use]\n    pub const fn call(&self) -> &Call {\n        &self.call\n    }\n}\n\nimpl From<Call> for New {\n    #[inline]\n    fn from(call: Call) -> Self {\n        Self { call }\n    }\n}\n\nimpl Spanned for New {\n    #[inline]\n    fn span(&self) -> Span {\n        self.call.span()\n    }\n}\n\nimpl ToInternedString for New {\n    #[inline]\n    fn to_interned_string(&self, interner: &Interner) -> String {\n        format!(\"new {}\", self.call.to_interned_string(interner))\n    }\n}\n\nimpl From<New> for Expression {\n    #[inline]\n    fn from(new: New) -> Self {\n        Self::New(new)\n    }\n}\n\nimpl VisitWith for New {\n    fn visit_with<'a, V>(&'a self, visitor: &mut V) -> ControlFlow<V::BreakTy>\n    where\n        V: Visitor<'a>,\n    {\n        visitor.visit_call(&self.call)\n    }\n\n    fn visit_with_mut<'a, V>(&'a mut self, visitor: &mut V) -> ControlFlow<V::BreakTy>\n    where\n        V: VisitorMut<'a>,\n    {\n        visitor.visit_call_mut(&mut self.call)\n    }\n}\n"
  },
  {
    "path": "core/ast/src/expression/new_target.rs",
    "content": "//! `target.new` ECMAScript expression.\n\nuse crate::{\n    Span, Spanned,\n    visitor::{VisitWith, Visitor, VisitorMut},\n};\nuse boa_interner::{Interner, ToInternedString};\nuse core::ops::ControlFlow;\n\nuse super::Expression;\n\n/// ECMAScript's `NewTarget` expression AST node.\n#[cfg_attr(feature = \"serde\", derive(serde::Serialize, serde::Deserialize))]\n#[cfg_attr(feature = \"arbitrary\", derive(arbitrary::Arbitrary))]\n#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]\npub struct NewTarget {\n    span: Span,\n}\n\nimpl NewTarget {\n    /// Creates a new [`NewTarget`] AST Expression.\n    #[inline]\n    #[must_use]\n    pub const fn new(span: Span) -> Self {\n        Self { span }\n    }\n}\n\nimpl Spanned for NewTarget {\n    #[inline]\n    fn span(&self) -> Span {\n        self.span\n    }\n}\n\nimpl From<NewTarget> for Expression {\n    #[inline]\n    fn from(value: NewTarget) -> Self {\n        Expression::NewTarget(value)\n    }\n}\n\nimpl ToInternedString for NewTarget {\n    #[inline]\n    fn to_interned_string(&self, _interner: &Interner) -> String {\n        String::from(\"new.target\")\n    }\n}\n\nimpl VisitWith for NewTarget {\n    fn visit_with<'a, V>(&'a self, _visitor: &mut V) -> ControlFlow<V::BreakTy>\n    where\n        V: Visitor<'a>,\n    {\n        ControlFlow::Continue(())\n    }\n\n    fn visit_with_mut<'a, V>(&'a mut self, _visitor: &mut V) -> ControlFlow<V::BreakTy>\n    where\n        V: VisitorMut<'a>,\n    {\n        ControlFlow::Continue(())\n    }\n}\n"
  },
  {
    "path": "core/ast/src/expression/operator/assign/mod.rs",
    "content": "#![allow(clippy::doc_link_with_quotes)]\n\n//! Assignment expression nodes, as defined by the [spec].\n//!\n//! An [assignment operator][mdn] assigns a value to its left operand based on the value of its right\n//! operand. Almost any [`LeftHandSideExpression`][lhs] Parse Node can be the target of a simple\n//! assignment expression (`=`). However, the compound assignment operations such as `%=` or `??=`\n//! only allow [\"simple\"][simple] left hand side expressions as an assignment target.\n//!\n//! [spec]: https://tc39.es/ecma262/#prod-AssignmentExpression\n//! [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Assignment_Operators\n//! [lhs]: https://tc39.es/ecma262/#prod-LeftHandSideExpression\n//! [simple]: https://tc39.es/ecma262/#sec-static-semantics-assignmenttargettype\n\nmod op;\n\nuse core::ops::ControlFlow;\npub use op::*;\n\nuse boa_interner::{Interner, Sym, ToInternedString};\n\nuse crate::{\n    Span, Spanned,\n    expression::{Expression, access::PropertyAccess, identifier::Identifier},\n    pattern::Pattern,\n    visitor::{VisitWith, Visitor, VisitorMut},\n};\n\n/// An assignment operator expression.\n///\n/// See the [module level documentation][self] for more information.\n#[cfg_attr(feature = \"serde\", derive(serde::Serialize, serde::Deserialize))]\n#[cfg_attr(feature = \"arbitrary\", derive(arbitrary::Arbitrary))]\n#[derive(Clone, Debug, PartialEq)]\npub struct Assign {\n    op: AssignOp,\n    lhs: Box<AssignTarget>,\n    rhs: Box<Expression>,\n}\n\nimpl Assign {\n    /// Creates an `Assign` AST Expression.\n    #[inline]\n    #[must_use]\n    pub fn new(op: AssignOp, lhs: AssignTarget, rhs: Expression) -> Self {\n        Self {\n            op,\n            lhs: Box::new(lhs),\n            rhs: Box::new(rhs),\n        }\n    }\n\n    /// Gets the operator of the assignment operation.\n    #[inline]\n    #[must_use]\n    pub const fn op(&self) -> AssignOp {\n        self.op\n    }\n\n    /// Gets the left hand side of the assignment operation.\n    #[inline]\n    #[must_use]\n    pub const fn lhs(&self) -> &AssignTarget {\n        &self.lhs\n    }\n\n    /// Gets the right hand side of the assignment operation.\n    #[inline]\n    #[must_use]\n    pub const fn rhs(&self) -> &Expression {\n        &self.rhs\n    }\n}\n\nimpl Spanned for Assign {\n    #[inline]\n    fn span(&self) -> Span {\n        Span::new(self.lhs.span().start(), self.rhs.span().end())\n    }\n}\n\nimpl ToInternedString for Assign {\n    #[inline]\n    fn to_interned_string(&self, interner: &Interner) -> String {\n        format!(\n            \"{} {} {}\",\n            self.lhs.to_interned_string(interner),\n            self.op,\n            self.rhs.to_interned_string(interner)\n        )\n    }\n}\n\nimpl From<Assign> for Expression {\n    #[inline]\n    fn from(op: Assign) -> Self {\n        Self::Assign(op)\n    }\n}\n\nimpl VisitWith for Assign {\n    fn visit_with<'a, V>(&'a self, visitor: &mut V) -> ControlFlow<V::BreakTy>\n    where\n        V: Visitor<'a>,\n    {\n        visitor.visit_assign_target(&self.lhs)?;\n        visitor.visit_expression(&self.rhs)\n    }\n\n    fn visit_with_mut<'a, V>(&'a mut self, visitor: &mut V) -> ControlFlow<V::BreakTy>\n    where\n        V: VisitorMut<'a>,\n    {\n        visitor.visit_assign_target_mut(&mut self.lhs)?;\n        visitor.visit_expression_mut(&mut self.rhs)\n    }\n}\n\n/// The valid left-hand-side expressions of an assignment operator. Also called\n/// [`LeftHandSideExpression`][spec] in the spec.\n///\n/// [spec]: hhttps://tc39.es/ecma262/#prod-LeftHandSideExpression\n#[cfg_attr(feature = \"serde\", derive(serde::Serialize, serde::Deserialize))]\n#[cfg_attr(feature = \"arbitrary\", derive(arbitrary::Arbitrary))]\n#[derive(Clone, Debug, PartialEq)]\npub enum AssignTarget {\n    /// A simple identifier, such as `a`.\n    Identifier(Identifier),\n    /// A property access, such as `a.prop`.\n    Access(PropertyAccess),\n    /// A pattern assignment, such as `{a, b, ...c}`.\n    Pattern(Pattern),\n}\n\nimpl AssignTarget {\n    /// Converts the left-hand-side Expression of an assignment expression into an [`AssignTarget`].\n    /// Returns `None` if the given Expression is an invalid left-hand-side for a assignment expression.\n    #[must_use]\n    pub fn from_expression(expression: &Expression, strict: bool) -> Option<Self> {\n        match expression {\n            Expression::ObjectLiteral(object) => {\n                let pattern = object.to_pattern(strict)?;\n                Some(Self::Pattern(pattern.into()))\n            }\n            Expression::ArrayLiteral(array) => {\n                let pattern = array.to_pattern(strict)?;\n                Some(Self::Pattern(pattern.into()))\n            }\n            e => Self::from_expression_simple(e, strict),\n        }\n    }\n\n    /// Converts the left-hand-side Expression of an assignment expression into an [`AssignTarget`].\n    /// Returns `None` if the given Expression is an invalid left-hand-side for a assignment expression.\n    ///\n    /// The `AssignmentTargetType` of the expression must be `simple`.\n    #[must_use]\n    pub fn from_expression_simple(expression: &Expression, strict: bool) -> Option<Self> {\n        match expression {\n            Expression::Identifier(id)\n                if strict && (id.sym() == Sym::EVAL || id.sym() == Sym::ARGUMENTS) =>\n            {\n                None\n            }\n            Expression::Identifier(id) => Some(Self::Identifier(*id)),\n            Expression::PropertyAccess(access) => Some(Self::Access(access.clone())),\n            Expression::Parenthesized(p) => Self::from_expression_simple(p.expression(), strict),\n            _ => None,\n        }\n    }\n}\n\nimpl Spanned for AssignTarget {\n    #[inline]\n    fn span(&self) -> Span {\n        match self {\n            AssignTarget::Identifier(identifier) => identifier.span(),\n            AssignTarget::Access(property_access) => property_access.span(),\n            AssignTarget::Pattern(pattern) => pattern.span(),\n        }\n    }\n}\n\nimpl ToInternedString for AssignTarget {\n    #[inline]\n    fn to_interned_string(&self, interner: &Interner) -> String {\n        match self {\n            Self::Identifier(id) => id.to_interned_string(interner),\n            Self::Access(access) => access.to_interned_string(interner),\n            Self::Pattern(pattern) => pattern.to_interned_string(interner),\n        }\n    }\n}\n\nimpl From<Identifier> for AssignTarget {\n    #[inline]\n    fn from(target: Identifier) -> Self {\n        Self::Identifier(target)\n    }\n}\n\nimpl VisitWith for AssignTarget {\n    fn visit_with<'a, V>(&'a self, visitor: &mut V) -> ControlFlow<V::BreakTy>\n    where\n        V: Visitor<'a>,\n    {\n        match self {\n            Self::Identifier(id) => visitor.visit_identifier(id),\n            Self::Access(pa) => visitor.visit_property_access(pa),\n            Self::Pattern(pat) => visitor.visit_pattern(pat),\n        }\n    }\n\n    fn visit_with_mut<'a, V>(&'a mut self, visitor: &mut V) -> ControlFlow<V::BreakTy>\n    where\n        V: VisitorMut<'a>,\n    {\n        match self {\n            Self::Identifier(id) => visitor.visit_identifier_mut(id),\n            Self::Access(pa) => visitor.visit_property_access_mut(pa),\n            Self::Pattern(pat) => visitor.visit_pattern_mut(pat),\n        }\n    }\n}\n"
  },
  {
    "path": "core/ast/src/expression/operator/assign/op.rs",
    "content": "/// An assignment operator assigns a value to its left operand based on the value of its right operand.\n///\n/// The simple assignment operator is equal (`=`), which assigns the value of its right operand to its\n/// left operand. That is, `x = y` assigns the value of `y to x`.\n///\n/// There are also compound assignment operators that are shorthand for the operations\n///\n/// More information:\n///  - [ECMAScript reference][spec]\n///  - [MDN documentation][mdn]\n///\n/// [spec]: https://tc39.es/ecma262/#prod-AssignmentOperator\n/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Expressions_and_Operators#Assignment\n#[cfg_attr(feature = \"serde\", derive(serde::Serialize, serde::Deserialize))]\n#[cfg_attr(feature = \"arbitrary\", derive(arbitrary::Arbitrary))]\n#[derive(Clone, Copy, Debug, PartialEq, Eq)]\npub enum AssignOp {\n    /// The assignment operator assigns the value of the right operand to the left operand.\n    ///\n    /// Syntax: `x = y`\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///  - [MDN documentation][mdn]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#prod-AssignmentOperator\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Assignment\n    Assign,\n\n    /// The addition assignment operator adds the value of the right operand to a variable and assigns the result to the variable.\n    ///\n    /// Syntax: `x += y`\n    ///\n    /// The types of the two operands determine the behavior of the addition assignment operator. Addition or concatenation is possible.\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///  - [MDN documentation][mdn]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#prod-AssignmentOperator\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Assignment_Operators#Addition_assignment\n    Add,\n\n    /// The subtraction assignment operator subtracts the value of the right operand from a variable and assigns the result to the variable.\n    ///\n    /// Syntax: `x -= y`\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///  - [MDN documentation][mdn]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#prod-AssignmentOperator\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Assignment_Operators#Subtraction_assignment\n    Sub,\n\n    /// The multiplication assignment operator multiplies a variable by the value of the right operand and assigns the result to the variable.\n    ///\n    /// Syntax: `x *= y`\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///  - [MDN documentation][mdn]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#prod-AssignmentOperator\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Assignment_Operators#Multiplication_assignment\n    Mul,\n\n    /// The division assignment operator divides a variable by the value of the right operand and assigns the result to the variable.\n    ///\n    /// Syntax: `x /= y`\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///  - [MDN documentation][mdn]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#prod-AssignmentOperator\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Assignment_Operators#Division_assignment\n    Div,\n\n    /// The remainder assignment operator divides a variable by the value of the right operand and assigns the remainder to the variable.\n    ///\n    /// Syntax: `x %= y`\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///  - [MDN documentation][mdn]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#prod-AssignmentOperator\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Assignment_Operators#Remainder_assignment\n    Mod,\n\n    /// The exponentiation assignment operator raises the value of a variable to the power of the right operand.\n    ///\n    /// Syntax: `x ** y`\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///  - [MDN documentation][mdn]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#prod-AssignmentOperator\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Assignment_Operators#Exponentiation_assignment\n    Exp,\n\n    /// The bitwise AND assignment operator uses the binary representation of both operands, does a bitwise AND operation on\n    /// them and assigns the result to the variable.\n    ///\n    /// Syntax: `x &= y`\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///  - [MDN documentation][mdn]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#prod-AssignmentOperator\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Assignment_Operators#Bitwise_AND_assignment\n    And,\n\n    /// The bitwise OR assignment operator uses the binary representation of both operands, does a bitwise OR operation on\n    /// them and assigns the result to the variable.\n    ///\n    /// Syntax: `x |= y`\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///  - [MDN documentation][mdn]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#prod-AssignmentOperator\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Assignment_Operators#Bitwise_OR_assignment\n    Or,\n\n    /// The bitwise XOR assignment operator uses the binary representation of both operands, does a bitwise XOR operation on\n    /// them and assigns the result to the variable.\n    ///\n    /// Syntax: `x ^= y`\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///  - [MDN documentation][mdn]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#prod-AssignmentOperator\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Assignment_Operators#Bitwise_XOR_assignment\n    Xor,\n\n    /// The left shift assignment operator moves the specified amount of bits to the left and assigns the result to the variable.\n    ///\n    /// Syntax: `x <<= y`\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///  - [MDN documentation][mdn]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#prod-AssignmentOperator\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Assignment_Operators#Left_shift_assignment\n    Shl,\n\n    /// The right shift assignment operator moves the specified amount of bits to the right and assigns the result to the variable.\n    ///\n    /// Syntax: `x >>= y`\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///  - [MDN documentation][mdn]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#prod-AssignmentOperator\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Assignment_Operators#Right_shift_assignment\n    Shr,\n\n    /// The unsigned right shift assignment operator moves the specified amount of bits to the right and assigns the result to the variable.\n    ///\n    /// Syntax: `x >>>= y`\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///  - [MDN documentation][mdn]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#prod-AssignmentOperator\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Unsigned_right_shift_assignment\n    Ushr,\n\n    /// The logical and assignment operator only assigns if the target variable is truthy.\n    ///\n    /// Syntax: `x &&= y`\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///  - [MDN documentation][mdn]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#prod-AssignmentExpression\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Logical_AND_assignment\n    BoolAnd,\n\n    /// The logical or assignment operator only assigns if the target variable is falsy.\n    ///\n    /// Syntax: `x ||= y`\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///  - [MDN documentation][mdn]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#prod-AssignmentExpression\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Logical_OR_assignment\n    BoolOr,\n\n    /// The logical nullish assignment operator only assigns if the target variable is nullish (null or undefined).\n    ///\n    /// Syntax: `x ??= y`\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///  - [MDN documentation][mdn]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#prod-AssignmentExpression\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Logical_nullish_assignment\n    Coalesce,\n}\n\nimpl AssignOp {\n    /// Retrieves the operation as a static string.\n    const fn as_str(self) -> &'static str {\n        match self {\n            Self::Assign => \"=\",\n            Self::Add => \"+=\",\n            Self::Sub => \"-=\",\n            Self::Mul => \"*=\",\n            Self::Exp => \"**=\",\n            Self::Div => \"/=\",\n            Self::Mod => \"%=\",\n            Self::And => \"&=\",\n            Self::Or => \"|=\",\n            Self::Xor => \"^=\",\n            Self::Shl => \"<<=\",\n            Self::Shr => \">>=\",\n            Self::Ushr => \">>>=\",\n            Self::BoolAnd => \"&&=\",\n            Self::BoolOr => \"||=\",\n            Self::Coalesce => \"??=\",\n        }\n    }\n}\n\nimpl std::fmt::Display for AssignOp {\n    #[inline]\n    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {\n        write!(f, \"{}\", self.as_str())\n    }\n}\n"
  },
  {
    "path": "core/ast/src/expression/operator/binary/mod.rs",
    "content": "//! Binary expression nodes.\n//!\n//! A Binary expression comprises any operation between two expressions (excluding assignments),\n//! such as:\n//! - [Logic operations][logic] (`||`, `&&`).\n//! - [Relational math][relat] (`==`, `<`).\n//! - [Bit manipulation][bit] (`^`, `|`).\n//! - [Arithmetic][arith] (`+`, `%`).\n//! - The [comma operator][comma] (`,`)\n//!\n//! [logic]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators#binary_logical_operators\n//! [relat]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators#relational_operators\n//! [bit]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators#binary_bitwise_operators\n//! [arith]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators#arithmetic_operators\n//! [comma]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Comma_Operator\n\nmod op;\n\nuse crate::{\n    Span, Spanned,\n    expression::Expression,\n    function::PrivateName,\n    visitor::{VisitWith, Visitor, VisitorMut},\n};\nuse boa_interner::{Interner, ToInternedString};\nuse core::ops::ControlFlow;\n\npub use op::*;\n\n/// Binary operations require two operands, one before the operator and one after the operator.\n///\n/// See the [module level documentation][self] for more information.\n#[cfg_attr(feature = \"serde\", derive(serde::Serialize, serde::Deserialize))]\n#[cfg_attr(feature = \"arbitrary\", derive(arbitrary::Arbitrary))]\n#[derive(Clone, Debug, PartialEq)]\npub struct Binary {\n    op: BinaryOp,\n    lhs: Box<Expression>,\n    rhs: Box<Expression>,\n}\n\nimpl Binary {\n    /// Creates a `BinOp` AST Expression.\n    #[inline]\n    #[must_use]\n    pub fn new(op: BinaryOp, lhs: Expression, rhs: Expression) -> Self {\n        Self {\n            op,\n            lhs: Box::new(lhs),\n            rhs: Box::new(rhs),\n        }\n    }\n\n    /// Gets the binary operation of the Expression.\n    #[inline]\n    #[must_use]\n    pub const fn op(&self) -> BinaryOp {\n        self.op\n    }\n\n    /// Gets the left hand side of the binary operation.\n    #[inline]\n    #[must_use]\n    pub const fn lhs(&self) -> &Expression {\n        &self.lhs\n    }\n\n    /// Gets the right hand side of the binary operation.\n    #[inline]\n    #[must_use]\n    pub const fn rhs(&self) -> &Expression {\n        &self.rhs\n    }\n\n    /// Gets the left hand side of the binary operation.\n    #[inline]\n    #[must_use]\n    pub fn lhs_mut(&mut self) -> &mut Expression {\n        &mut self.lhs\n    }\n\n    /// Gets the right hand side of the binary operation.\n    #[inline]\n    #[must_use]\n    pub fn rhs_mut(&mut self) -> &mut Expression {\n        &mut self.rhs\n    }\n}\n\nimpl Spanned for Binary {\n    #[inline]\n    fn span(&self) -> Span {\n        Span::new(self.lhs.span().start(), self.rhs.span().end())\n    }\n}\n\nimpl ToInternedString for Binary {\n    #[inline]\n    fn to_interned_string(&self, interner: &Interner) -> String {\n        format!(\n            \"{} {} {}\",\n            self.lhs.to_interned_string(interner),\n            self.op,\n            self.rhs.to_interned_string(interner)\n        )\n    }\n}\n\nimpl From<Binary> for Expression {\n    #[inline]\n    fn from(op: Binary) -> Self {\n        Self::Binary(op)\n    }\n}\n\nimpl VisitWith for Binary {\n    fn visit_with<'a, V>(&'a self, visitor: &mut V) -> ControlFlow<V::BreakTy>\n    where\n        V: Visitor<'a>,\n    {\n        visitor.visit_expression(&self.lhs)?;\n        visitor.visit_expression(&self.rhs)\n    }\n\n    fn visit_with_mut<'a, V>(&'a mut self, visitor: &mut V) -> ControlFlow<V::BreakTy>\n    where\n        V: VisitorMut<'a>,\n    {\n        visitor.visit_expression_mut(&mut self.lhs)?;\n        visitor.visit_expression_mut(&mut self.rhs)\n    }\n}\n\n/// Binary [relational][relat] `In` expression with a private name on the left hand side.\n///\n/// Because the left hand side must be a private name, this is a separate type from [`Binary`].\n///\n/// [relat]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators#relational_operators\n#[cfg_attr(feature = \"serde\", derive(serde::Serialize, serde::Deserialize))]\n#[cfg_attr(feature = \"arbitrary\", derive(arbitrary::Arbitrary))]\n#[derive(Clone, Debug, PartialEq)]\npub struct BinaryInPrivate {\n    lhs: PrivateName,\n    rhs: Box<Expression>,\n}\n\nimpl BinaryInPrivate {\n    /// Creates a `BinaryInPrivate` AST Expression.\n    #[inline]\n    #[must_use]\n    pub fn new(lhs: PrivateName, rhs: Expression) -> Self {\n        Self {\n            lhs,\n            rhs: Box::new(rhs),\n        }\n    }\n\n    /// Gets the left hand side of the binary operation.\n    #[inline]\n    #[must_use]\n    pub const fn lhs(&self) -> &PrivateName {\n        &self.lhs\n    }\n\n    /// Gets the right hand side of the binary operation.\n    #[inline]\n    #[must_use]\n    pub const fn rhs(&self) -> &Expression {\n        &self.rhs\n    }\n}\n\nimpl Spanned for BinaryInPrivate {\n    #[inline]\n    fn span(&self) -> Span {\n        Span::new(self.lhs.span().start(), self.rhs.span().end())\n    }\n}\n\nimpl ToInternedString for BinaryInPrivate {\n    #[inline]\n    fn to_interned_string(&self, interner: &Interner) -> String {\n        format!(\n            \"#{} in {}\",\n            interner.resolve_expect(self.lhs.description()),\n            self.rhs.to_interned_string(interner)\n        )\n    }\n}\n\nimpl From<BinaryInPrivate> for Expression {\n    #[inline]\n    fn from(op: BinaryInPrivate) -> Self {\n        Self::BinaryInPrivate(op)\n    }\n}\n\nimpl VisitWith for BinaryInPrivate {\n    fn visit_with<'a, V>(&'a self, visitor: &mut V) -> ControlFlow<V::BreakTy>\n    where\n        V: Visitor<'a>,\n    {\n        visitor.visit_private_name(&self.lhs)?;\n        visitor.visit_expression(&self.rhs)\n    }\n\n    fn visit_with_mut<'a, V>(&'a mut self, visitor: &mut V) -> ControlFlow<V::BreakTy>\n    where\n        V: VisitorMut<'a>,\n    {\n        visitor.visit_private_name_mut(&mut self.lhs)?;\n        visitor.visit_expression_mut(&mut self.rhs)\n    }\n}\n"
  },
  {
    "path": "core/ast/src/expression/operator/binary/op.rs",
    "content": "//! This module implements various structure for logic handling.\n\nuse std::fmt::{Display, Formatter, Result};\n\n/// This represents a binary operation between two values.\n#[cfg_attr(feature = \"serde\", derive(serde::Serialize, serde::Deserialize))]\n#[cfg_attr(feature = \"arbitrary\", derive(arbitrary::Arbitrary))]\n#[derive(Clone, Copy, Debug, PartialEq, Eq)]\npub enum BinaryOp {\n    /// Numeric operation.\n    ///\n    /// see: [`NumOp`](enum.NumOp.html)\n    Arithmetic(ArithmeticOp),\n\n    /// Bitwise operation.\n    ///\n    /// see: [`BitOp`](enum.BitOp.html).\n    Bitwise(BitwiseOp),\n\n    /// Comparative operation.\n    ///\n    /// see: [`CompOp`](enum.CompOp.html).\n    Relational(RelationalOp),\n\n    /// Logical operation.\n    ///\n    /// see: [`LogOp`](enum.LogOp.html).\n    Logical(LogicalOp),\n\n    /// Comma operation.\n    Comma,\n}\n\nimpl From<ArithmeticOp> for BinaryOp {\n    #[inline]\n    fn from(op: ArithmeticOp) -> Self {\n        Self::Arithmetic(op)\n    }\n}\n\nimpl From<BitwiseOp> for BinaryOp {\n    #[inline]\n    fn from(op: BitwiseOp) -> Self {\n        Self::Bitwise(op)\n    }\n}\n\nimpl From<RelationalOp> for BinaryOp {\n    #[inline]\n    fn from(op: RelationalOp) -> Self {\n        Self::Relational(op)\n    }\n}\n\nimpl From<LogicalOp> for BinaryOp {\n    #[inline]\n    fn from(op: LogicalOp) -> Self {\n        Self::Logical(op)\n    }\n}\n\nimpl BinaryOp {\n    /// Retrieves the operation as a static string.\n    const fn as_str(self) -> &'static str {\n        match self {\n            Self::Arithmetic(ref op) => op.as_str(),\n            Self::Bitwise(ref op) => op.as_str(),\n            Self::Relational(ref op) => op.as_str(),\n            Self::Logical(ref op) => op.as_str(),\n            Self::Comma => \",\",\n        }\n    }\n}\n\nimpl Display for BinaryOp {\n    #[inline]\n    fn fmt(&self, f: &mut Formatter<'_>) -> Result {\n        write!(f, \"{}\", self.as_str())\n    }\n}\n\n/// Arithmetic operators take numerical values (either literals or variables)\n/// as their operands and return a single numerical value.\n///\n/// More information:\n///  - [MDN documentation][mdn]\n///\n/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Expressions_and_Operators#Arithmetic\n#[cfg_attr(feature = \"serde\", derive(serde::Serialize, serde::Deserialize))]\n#[cfg_attr(feature = \"arbitrary\", derive(arbitrary::Arbitrary))]\n#[derive(Clone, Copy, Debug, PartialEq, Eq)]\npub enum ArithmeticOp {\n    /// The addition operator produces the sum of numeric operands or string concatenation.\n    ///\n    /// Syntax: `x + y`\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec].\n    ///  - [MDN documentation][mdn]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-addition-operator-plus\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Arithmetic_Operators#Addition\n    Add,\n\n    /// The subtraction operator subtracts the two operands, producing their difference.\n    ///\n    /// Syntax: `x - y`\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec].\n    ///  - [MDN documentation][mdn]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-subtraction-operator-minus\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Arithmetic_Operators#Subtraction\n    Sub,\n\n    /// The division operator produces the quotient of its operands where the left operand\n    /// is the dividend and the right operand is the divisor.\n    ///\n    /// Syntax: `x / y`\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///  - [MDN documentation][mdn]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#prod-MultiplicativeOperator\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Arithmetic_Operators#Division\n    Div,\n\n    /// The multiplication operator produces the product of the operands.\n    ///\n    /// Syntax: `x * y`\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///  - [MDN documentation][mdn]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#prod-MultiplicativeExpression\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Arithmetic_Operators#Multiplication\n    Mul,\n\n    /// The exponentiation operator returns the result of raising the first operand to\n    /// the power of the second operand.\n    ///\n    /// Syntax: `x ** y`\n    ///\n    /// The exponentiation operator is right-associative. a ** b ** c is equal to a ** (b ** c).\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///  - [MDN documentation][mdn]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-exp-operator\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Arithmetic_Operators#Exponentiation\n    Exp,\n\n    /// The remainder operator returns the remainder left over when one operand is divided by a second operand.\n    ///\n    /// Syntax: `x % y`\n    ///\n    /// The remainder operator always takes the sign of the dividend.\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///  - [MDN documentation][mdn]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#prod-MultiplicativeOperator\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Arithmetic_Operators#Remainder\n    Mod,\n}\n\nimpl ArithmeticOp {\n    /// Retrieves the operation as a static string.\n    const fn as_str(self) -> &'static str {\n        match self {\n            Self::Add => \"+\",\n            Self::Sub => \"-\",\n            Self::Div => \"/\",\n            Self::Mul => \"*\",\n            Self::Exp => \"**\",\n            Self::Mod => \"%\",\n        }\n    }\n}\n\nimpl Display for ArithmeticOp {\n    #[inline]\n    fn fmt(&self, f: &mut Formatter<'_>) -> Result {\n        write!(f, \"{}\", self.as_str())\n    }\n}\n\n/// A bitwise operator is an operator used to perform bitwise operations\n/// on bit patterns or binary numerals that involve the manipulation of individual bits.\n///\n/// More information:\n///  - [MDN documentation][mdn]\n///\n/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Expressions_and_Operators#Bitwise\n#[cfg_attr(feature = \"serde\", derive(serde::Serialize, serde::Deserialize))]\n#[cfg_attr(feature = \"arbitrary\", derive(arbitrary::Arbitrary))]\n#[derive(Clone, Copy, Debug, PartialEq, Eq)]\npub enum BitwiseOp {\n    /// Performs the AND operation on each pair of bits. a AND b yields 1 only if both a and b are 1.\n    ///\n    /// Syntax: `x & y`\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///  - [MDN documentation][mdn]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#prod-BitwiseANDExpression\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Bitwise_Operators#Bitwise_AND\n    And,\n\n    /// Performs the OR operation on each pair of bits. a OR b yields 1 if either a or b is 1.\n    ///\n    /// Syntax: `x | y`\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///  - [MDN documentation][mdn]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#prod-BitwiseORExpression\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Bitwise_Operators#Bitwise_OR\n    Or,\n\n    /// Performs the XOR operation on each pair of bits. a XOR b yields 1 if a and b are different.\n    ///\n    /// Syntax: `x ^ y`\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///  - [MDN documentation][mdn]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#prod-BitwiseXORExpression\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Bitwise_Operators#Bitwise_XOR\n    Xor,\n\n    /// This operator shifts the first operand the specified number of bits to the left.\n    ///\n    /// Syntax: `x << y`\n    ///\n    /// Excess bits shifted off to the left are discarded. Zero bits are shifted in from the right.\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///  - [MDN documentation][mdn]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-left-shift-operator\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Bitwise_Operators#Left_shift\n    Shl,\n\n    /// This operator shifts the first operand the specified number of bits to the right.\n    ///\n    /// Syntax: `x >> y`\n    ///\n    /// Excess bits shifted off to the right are discarded. Copies of the leftmost bit\n    /// are shifted in from the left. Since the new leftmost bit has the same value as\n    /// the previous leftmost bit, the sign bit (the leftmost bit) does not change.\n    /// Hence the name \"sign-propagating\".\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///  - [MDN documentation][mdn]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-signed-right-shift-operator\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Bitwise_Operators#Right_shift\n    Shr,\n\n    /// This operator shifts the first operand the specified number of bits to the right.\n    ///\n    /// Syntax: `x >>> y`\n    ///\n    /// Excess bits shifted off to the right are discarded. Zero bits are shifted in\n    /// from the left. The sign bit becomes 0, so the result is always non-negative.\n    /// Unlike the other bitwise operators, zero-fill right shift returns an unsigned 32-bit integer.\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///  - [MDN documentation][mdn]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-unsigned-right-shift-operator\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Bitwise_Operators#Unsigned_right_shift\n    UShr,\n}\n\nimpl BitwiseOp {\n    /// Retrieves the operation as a static string.\n    const fn as_str(self) -> &'static str {\n        match self {\n            Self::And => \"&\",\n            Self::Or => \"|\",\n            Self::Xor => \"^\",\n            Self::Shl => \"<<\",\n            Self::Shr => \">>\",\n            Self::UShr => \">>>\",\n        }\n    }\n}\n\nimpl Display for BitwiseOp {\n    #[inline]\n    fn fmt(&self, f: &mut Formatter<'_>) -> Result {\n        write!(f, \"{}\", self.as_str())\n    }\n}\n\n/// A relational operator compares its operands and returns a logical value based on whether the relation is true.\n///\n/// The operands can be numerical, string, logical, or object values. Strings are compared based on standard\n/// lexicographical ordering, using Unicode values. In most cases, if the two operands are not of the same type,\n/// JavaScript attempts to convert them to an appropriate type for the comparison. This behavior generally results in\n/// comparing the operands numerically. The sole exceptions to type conversion within comparisons involve the `===` and `!==`\n/// operators, which perform strict equality and inequality comparisons. These operators do not attempt to convert the operands\n/// to compatible types before checking equality.\n///\n/// More information:\n///  - [ECMAScript reference][spec]\n///  - [MDN documentation][mdn]\n///\n/// [spec]: tc39.es/ecma262/#sec-testing-and-comparison-operations\n/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Expressions_and_Operators#Comparison\n#[cfg_attr(feature = \"serde\", derive(serde::Serialize, serde::Deserialize))]\n#[cfg_attr(feature = \"arbitrary\", derive(arbitrary::Arbitrary))]\n#[derive(Clone, Copy, Debug, PartialEq, Eq)]\npub enum RelationalOp {\n    /// The equality operator converts the operands if they are not of the same type, then applies\n    /// strict comparison.\n    ///\n    /// Syntax: `y == y`\n    ///\n    /// If both operands are objects, then JavaScript compares internal references which are equal\n    /// when operands refer to the same object in memory.\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///  - [MDN documentation][mdn]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-abstract-equality-comparison\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Comparison_Operators#Equality\n    Equal,\n\n    /// The inequality operator returns `true` if the operands are not equal.\n    ///\n    /// Syntax: `x != y`\n    ///\n    /// If the two operands are not of the same type, JavaScript attempts to convert the operands\n    /// to an appropriate type for the comparison. If both operands are objects, then JavaScript\n    /// compares internal references which are not equal when operands refer to different objects\n    /// in memory.\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///  - [MDN documentation][mdn]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#prod-EqualityExpression\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Comparison_Operators#Inequality\n    NotEqual,\n\n    /// The identity operator returns `true` if the operands are strictly equal **with no type\n    /// conversion**.\n    ///\n    /// Syntax: `x === y`\n    ///\n    /// Returns `true` if the operands are equal and of the same type.\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///  - [MDN documentation][mdn]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-strict-equality-comparison\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Comparison_Operators#Identity\n    StrictEqual,\n\n    /// The non-identity operator returns `true` if the operands **are not equal and/or not of the\n    /// same type**.\n    ///\n    /// Syntax: `x !== y`\n    ///\n    /// Returns `true` if the operands are of the same type but not equal, or are of different type.\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///  - [MDN documentation][mdn]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#prod-EqualityExpression\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Comparison_Operators#Nonidentity>\n    StrictNotEqual,\n\n    /// The greater than operator returns `true` if the left operand is greater than the right\n    /// operand.\n    ///\n    /// Syntax: `x > y`\n    ///\n    /// Returns `true` if the left operand is greater than the right operand.\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///  - [MDN documentation][mdn]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#prod-RelationalExpression\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Comparison_Operators#Greater_than_operator\n    GreaterThan,\n\n    /// The greater than or equal operator returns `true` if the left operand is greater than or\n    /// equal to the right operand.\n    ///\n    /// Syntax: `x >= y`\n    ///\n    /// Returns `true` if the left operand is greater than the right operand.\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///  - [MDN documentation][mdn]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#prod-RelationalExpression\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Comparison_Operators#Greater_than_operator\n    GreaterThanOrEqual,\n\n    /// The less than operator returns `true` if the left operand is less than the right operand.\n    ///\n    /// Syntax: `x < y`\n    ///\n    /// Returns `true` if the left operand is less than the right operand.\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///  - [MDN documentation][mdn]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#prod-RelationalExpression\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Comparison_Operators#Less_than_operator\n    LessThan,\n\n    /// The less than or equal operator returns `true` if the left operand is less than or equal to\n    /// the right operand.\n    ///\n    /// Syntax: `x <= y`\n    ///\n    /// Returns `true` if the left operand is less than or equal to the right operand.\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///  - [MDN documentation][mdn]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#prod-RelationalExpression\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Comparison_Operators#Less_than_or_equal_operator\n    LessThanOrEqual,\n\n    /// The `in` operator returns `true` if the specified property is in the specified object or\n    /// its prototype chain.\n    ///\n    /// Syntax: `prop in object`\n    ///\n    /// Returns `true` the specified property is in the specified object or its prototype chain.\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///  - [MDN documentation][mdn]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#prod-RelationalExpression\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/in\n    In,\n\n    /// The `instanceof` operator returns `true` if the specified object is an instance of the\n    /// right hand side object.\n    ///\n    /// Syntax: `obj instanceof Object`\n    ///\n    /// Returns `true` the `prototype` property of the right hand side constructor appears anywhere\n    /// in the prototype chain of the object.\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///  - [MDN documentation][mdn]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#prod-RelationalExpression\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/instanceof\n    InstanceOf,\n}\n\nimpl RelationalOp {\n    /// Retrieves the operation as a static string.\n    const fn as_str(self) -> &'static str {\n        match self {\n            Self::Equal => \"==\",\n            Self::NotEqual => \"!=\",\n            Self::StrictEqual => \"===\",\n            Self::StrictNotEqual => \"!==\",\n            Self::GreaterThan => \">\",\n            Self::GreaterThanOrEqual => \">=\",\n            Self::LessThan => \"<\",\n            Self::LessThanOrEqual => \"<=\",\n            Self::In => \"in\",\n            Self::InstanceOf => \"instanceof\",\n        }\n    }\n}\n\nimpl Display for RelationalOp {\n    #[inline]\n    fn fmt(&self, f: &mut Formatter<'_>) -> Result {\n        write!(f, \"{}\", self.as_str())\n    }\n}\n\n/// Logical operators are typically used with Boolean (logical) values; when they are, they return a Boolean value.\n///\n/// However, the `&&` and `||` operators actually return the value of one of the specified operands,\n/// so if these operators are used with non-Boolean values, they may return a non-Boolean value.\n///\n/// More information:\n///  - [ECMAScript reference][spec]\n///  - [MDN documentation][mdn]\n///\n/// [spec]: https://tc39.es/ecma262/#sec-binary-logical-operators\n/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Expressions_and_Operators#Logical\n#[cfg_attr(feature = \"serde\", derive(serde::Serialize, serde::Deserialize))]\n#[cfg_attr(feature = \"arbitrary\", derive(arbitrary::Arbitrary))]\n#[derive(Clone, Copy, Debug, PartialEq, Eq)]\npub enum LogicalOp {\n    /// The logical AND operator returns the value of the first operand if it can be coerced into `false`;\n    /// otherwise, it returns the second operand.\n    ///\n    /// Syntax: `x && y`\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///  - [MDN documentation][mdn]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#prod-LogicalANDExpression\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Logical_Operators#Logical_AND\n    And,\n\n    /// The logical OR operator returns the value the first operand if it can be coerced into `true`;\n    /// otherwise, it returns the second operand.\n    ///\n    /// Syntax: `x || y`\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///  - [MDN documentation][mdn]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#prod-LogicalORExpression\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Logical_Operators#Logical_OR\n    Or,\n\n    /// The nullish coalescing operator is a logical operator that returns the second operand\n    /// when its first operand is null or undefined, and otherwise returns its first operand.\n    ///\n    /// Syntax: `x ?? y`\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///  - [MDN documentation][mdn]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#prod-CoalesceExpression\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Nullish_coalescing_operator\n    Coalesce,\n}\n\nimpl LogicalOp {\n    /// Retrieves the operation as a static string.\n    const fn as_str(self) -> &'static str {\n        match self {\n            Self::And => \"&&\",\n            Self::Or => \"||\",\n            Self::Coalesce => \"??\",\n        }\n    }\n}\n\nimpl Display for LogicalOp {\n    #[inline]\n    fn fmt(&self, f: &mut Formatter<'_>) -> Result {\n        write!(f, \"{}\", self.as_str())\n    }\n}\n"
  },
  {
    "path": "core/ast/src/expression/operator/conditional.rs",
    "content": "use crate::{\n    Span, Spanned,\n    expression::Expression,\n    visitor::{VisitWith, Visitor, VisitorMut},\n};\nuse boa_interner::{Interner, ToInternedString};\nuse core::ops::ControlFlow;\n\n/// The `conditional` (ternary) operation is the only ECMAScript operation that takes three\n/// operands.\n///\n/// This operation takes three operands: a condition followed by a question mark (`?`),\n/// then an expression to execute `if` the condition is truthy followed by a colon (`:`),\n/// and finally the expression to execute if the condition is `false`.\n/// This operator is frequently used as a shortcut for the `if` statement.\n///\n/// More information:\n///  - [ECMAScript reference][spec]\n///  - [MDN documentation][mdn]\n///\n/// [spec]: https://tc39.es/ecma262/#prod-ConditionalExpression\n/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Grammar_and_types#Literals\n#[cfg_attr(feature = \"serde\", derive(serde::Serialize, serde::Deserialize))]\n#[cfg_attr(feature = \"arbitrary\", derive(arbitrary::Arbitrary))]\n#[derive(Clone, Debug, PartialEq)]\npub struct Conditional {\n    condition: Box<Expression>,\n    if_true: Box<Expression>,\n    if_false: Box<Expression>,\n}\n\nimpl Conditional {\n    /// Gets the condition of the `Conditional` expression.\n    #[inline]\n    #[must_use]\n    pub const fn condition(&self) -> &Expression {\n        &self.condition\n    }\n\n    /// Gets the expression returned if `condition` is truthy.\n    #[inline]\n    #[must_use]\n    pub const fn if_true(&self) -> &Expression {\n        &self.if_true\n    }\n\n    /// Gets the expression returned if `condition` is falsy.\n    #[inline]\n    #[must_use]\n    pub const fn if_false(&self) -> &Expression {\n        &self.if_false\n    }\n\n    /// Creates a `Conditional` AST Expression.\n    #[inline]\n    #[must_use]\n    pub fn new(condition: Expression, if_true: Expression, if_false: Expression) -> Self {\n        Self {\n            condition: Box::new(condition),\n            if_true: Box::new(if_true),\n            if_false: Box::new(if_false),\n        }\n    }\n}\n\nimpl Spanned for Conditional {\n    #[inline]\n    fn span(&self) -> Span {\n        Span::new(self.condition.span().start(), self.if_false.span().end())\n    }\n}\n\nimpl ToInternedString for Conditional {\n    #[inline]\n    fn to_interned_string(&self, interner: &Interner) -> String {\n        format!(\n            \"{} ? {} : {}\",\n            self.condition().to_interned_string(interner),\n            self.if_true().to_interned_string(interner),\n            self.if_false().to_interned_string(interner)\n        )\n    }\n}\n\nimpl From<Conditional> for Expression {\n    #[inline]\n    fn from(cond_op: Conditional) -> Self {\n        Self::Conditional(cond_op)\n    }\n}\n\nimpl VisitWith for Conditional {\n    fn visit_with<'a, V>(&'a self, visitor: &mut V) -> ControlFlow<V::BreakTy>\n    where\n        V: Visitor<'a>,\n    {\n        visitor.visit_expression(&self.condition)?;\n        visitor.visit_expression(&self.if_true)?;\n        visitor.visit_expression(&self.if_false)\n    }\n\n    fn visit_with_mut<'a, V>(&'a mut self, visitor: &mut V) -> ControlFlow<V::BreakTy>\n    where\n        V: VisitorMut<'a>,\n    {\n        visitor.visit_expression_mut(&mut self.condition)?;\n        visitor.visit_expression_mut(&mut self.if_true)?;\n        visitor.visit_expression_mut(&mut self.if_false)\n    }\n}\n"
  },
  {
    "path": "core/ast/src/expression/operator/mod.rs",
    "content": "//! Operator expression nodes.\n//!\n//! An [operator][op] expression is an expression that takes an operator (such as `+`, `typeof`, `+=`)\n//! and one or more expressions and returns an expression as a result.\n//! An operator expression can be any of the following:\n//!\n//! - A [`Unary`] expression.\n//! - A [`Update`] expression.\n//! - A [`Assign`] expression.\n//! - A [`Binary`] expression.\n//! - A [`Conditional`] expression.\n//!\n//! [op]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators\n\nmod conditional;\n\npub mod assign;\npub mod binary;\npub mod unary;\npub mod update;\n\npub use self::{\n    assign::Assign,\n    binary::{Binary, BinaryInPrivate},\n    conditional::Conditional,\n    unary::Unary,\n    update::Update,\n};\n"
  },
  {
    "path": "core/ast/src/expression/operator/unary/mod.rs",
    "content": "//! Unary expression nodes.\n//!\n//! A unary expression comprises any operation applied to a single expression. Some examples include:\n//!\n//! - The [`delete`][del] operator.\n//! - The [bitwise NOT][not] operator (`~`).\n//!\n//! The full list of valid unary operators is defined in [`UnaryOp`].\n//!\n//! [del]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/delete\n//! [not]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Bitwise_NOT\nmod op;\n\nuse crate::{\n    Span, Spanned,\n    expression::Expression,\n    visitor::{VisitWith, Visitor, VisitorMut},\n};\nuse boa_interner::{Interner, ToInternedString};\nuse core::ops::ControlFlow;\n\npub use op::*;\n\n/// A unary expression is an operation with only one operand.\n///\n/// More information:\n///  - [ECMAScript reference][spec]\n///  - [MDN documentation][mdn]\n///\n/// [spec]: https://tc39.es/ecma262/#prod-UnaryExpression\n/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Expressions_and_Operators#Unary_operators\n#[cfg_attr(feature = \"serde\", derive(serde::Serialize, serde::Deserialize))]\n#[cfg_attr(feature = \"arbitrary\", derive(arbitrary::Arbitrary))]\n#[derive(Clone, Debug, PartialEq)]\npub struct Unary {\n    op: UnaryOp,\n    target: Box<Expression>,\n    span: Span,\n}\n\nimpl Unary {\n    /// Creates a new `UnaryOp` AST Expression.\n    #[inline]\n    #[must_use]\n    pub fn new(op: UnaryOp, target: Expression, span: Span) -> Self {\n        Self {\n            op,\n            target: Box::new(target),\n            span,\n        }\n    }\n\n    /// Gets the unary operation of the Expression.\n    #[inline]\n    #[must_use]\n    pub const fn op(&self) -> UnaryOp {\n        self.op\n    }\n\n    /// Gets the target of this unary operator.\n    #[inline]\n    #[must_use]\n    pub fn target(&self) -> &Expression {\n        self.target.as_ref()\n    }\n\n    /// Gets the target of this unary operator.\n    #[inline]\n    #[must_use]\n    pub fn target_mut(&mut self) -> &mut Expression {\n        self.target.as_mut()\n    }\n}\n\nimpl Spanned for Unary {\n    #[inline]\n    fn span(&self) -> Span {\n        self.span\n    }\n}\n\nimpl ToInternedString for Unary {\n    #[inline]\n    fn to_interned_string(&self, interner: &Interner) -> String {\n        format!(\"{} {}\", self.op, self.target.to_interned_string(interner))\n    }\n}\n\nimpl From<Unary> for Expression {\n    #[inline]\n    fn from(op: Unary) -> Self {\n        Self::Unary(op)\n    }\n}\n\nimpl VisitWith for Unary {\n    fn visit_with<'a, V>(&'a self, visitor: &mut V) -> ControlFlow<V::BreakTy>\n    where\n        V: Visitor<'a>,\n    {\n        visitor.visit_expression(&self.target)\n    }\n\n    fn visit_with_mut<'a, V>(&'a mut self, visitor: &mut V) -> ControlFlow<V::BreakTy>\n    where\n        V: VisitorMut<'a>,\n    {\n        visitor.visit_expression_mut(&mut self.target)\n    }\n}\n"
  },
  {
    "path": "core/ast/src/expression/operator/unary/op.rs",
    "content": "/// A unary operator is one that takes a single operand/argument and performs an operation.\n///\n/// A unary operation is an operation with only one operand. This operand comes either\n/// before or after the operator. Unary operators are more efficient than standard JavaScript\n/// function calls.\n///\n/// More information:\n///  - [ECMAScript reference][spec]\n///  - [MDN documentation][mdn]\n///\n/// [spec]: https://tc39.es/ecma262/#prod-UnaryExpression\n/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Expressions_and_Operators#Unary\n#[cfg_attr(feature = \"serde\", derive(serde::Serialize, serde::Deserialize))]\n#[cfg_attr(feature = \"arbitrary\", derive(arbitrary::Arbitrary))]\n#[derive(Clone, Copy, Debug, PartialEq, Eq)]\npub enum UnaryOp {\n    /// The unary negation operator precedes its operand and negates it.\n    ///\n    /// Syntax: `-x`\n    ///\n    /// Converts non-numbers data types to numbers like unary plus,\n    /// however, it performs an additional operation, negation.\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///  - [MDN documentation][mdn]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-unary-minus-operator\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Arithmetic_Operators#Unary_negation\n    Minus,\n\n    /// The unary plus operator attempts to convert the operand into a number, if it isn't already.\n    ///\n    /// Syntax: `+x`\n    ///\n    /// Although unary negation (`-`) also can convert non-numbers, unary plus is the fastest and preferred\n    /// way of converting something into a number, because it does not perform any other operations on the number.\n    /// It can convert `string` representations of integers and floats, as well as the non-string values `true`, `false`, and `null`.\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///  - [MDN documentation][mdn]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-unary-plus-operator\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Arithmetic_Operators#Unary_plus\n    Plus,\n\n    /// Returns `false` if its single operand can be converted to `true`; otherwise, returns `true`.\n    ///\n    /// Syntax: `!x`\n    ///\n    /// Boolean values simply get inverted: `!true === false` and `!false === true`.\n    /// Non-boolean values get converted to boolean values first, then are negated.\n    /// This means that it is possible to use a couple of NOT operators in series to explicitly\n    /// force the conversion of any value to the corresponding boolean primitive.\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///  - [MDN documentation][mdn]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-logical-not-operator\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Logical_Operators#Logical_NOT\n    Not,\n\n    /// Performs the NOT operator on each bit.\n    ///\n    /// Syntax: `~x`\n    ///\n    /// NOT `a` yields the inverted value (or one's complement) of `a`.\n    /// Bitwise `NOT`ing any number x yields -(x + 1). For example, ~-5 yields 4.\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///  - [MDN documentation][mdn]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-bitwise-not-operator\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Bitwise_Operators#Bitwise_NOT\n    Tilde,\n\n    /// The `typeof` operator returns a string indicating the type of the unevaluated operand.\n    ///\n    /// Syntax: `typeof x` or `typeof(x)`\n    ///\n    /// The `typeof` is a JavaScript keyword that will return the type of a variable when you call it.\n    /// You can use this to validate function parameters or check if variables are defined.\n    /// There are other uses as well.\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///  - [MDN documentation][mdn]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-typeof-operator\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/typeof\n    TypeOf,\n\n    /// The JavaScript `delete` operator removes a property from an object.\n    ///\n    /// Syntax: `delete x`\n    ///\n    /// Unlike what common belief suggests, the delete operator has nothing to do with\n    /// directly freeing memory. Memory management is done indirectly via breaking references.\n    /// If no more references to the same property are held, it is eventually released automatically.\n    ///\n    /// The `delete` operator returns `true` for all cases except when the property is an\n    /// [own](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/hasOwnProperty)\n    /// [non-configurable](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Errors/Cant_delete)\n    /// property, in which case, `false` is returned in non-strict mode.\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///  - [MDN documentation][mdn]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-delete-operator\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/delete\n    Delete,\n\n    /// The `void` operator evaluates the given `expression` and then returns `undefined`.\n    ///\n    /// Syntax: `void x`\n    ///\n    /// This operator allows evaluating expressions that produce a value into places where an\n    /// expression that evaluates to `undefined` is desired.\n    /// The `void` operator is often used merely to obtain the `undefined` primitive value, usually using `void(0)`\n    /// (which is equivalent to `void 0`). In these cases, the global variable undefined can be used.\n    ///\n    /// When using an [immediately-invoked function expression](https://developer.mozilla.org/en-US/docs/Glossary/IIFE),\n    /// `void` can be used to force the function keyword to be treated as an expression instead of a declaration.\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///  - [MDN documentation][mdn]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-void-operator\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/void\n    Void,\n}\n\nimpl UnaryOp {\n    /// Retrieves the operation as a static string.\n    const fn as_str(self) -> &'static str {\n        match self {\n            Self::Plus => \"+\",\n            Self::Minus => \"-\",\n            Self::Not => \"!\",\n            Self::Tilde => \"~\",\n            Self::Delete => \"delete\",\n            Self::TypeOf => \"typeof\",\n            Self::Void => \"void\",\n        }\n    }\n}\n\nimpl std::fmt::Display for UnaryOp {\n    #[inline]\n    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {\n        write!(f, \"{}\", self.as_str())\n    }\n}\n"
  },
  {
    "path": "core/ast/src/expression/operator/update/mod.rs",
    "content": "//! Update expression nodes.\n//!\n//! A update expression increments or decrements it's operand and returns a value\n//!\n//! - [Increment and decrement operations][mdn] (`++`, `--`).\n//!\n//! The full list of valid update operators is defined in [`UpdateOp`].\n//!\n//! [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators#increment_and_decrement\nmod op;\n\nuse crate::{\n    Expression, Span, Spanned,\n    expression::{Identifier, access::PropertyAccess},\n    visitor::{VisitWith, Visitor, VisitorMut},\n};\nuse boa_interner::{Interner, ToInternedString};\nuse core::ops::ControlFlow;\n\npub use op::*;\n\n/// A update expression is an operation with only one operand.\n///\n/// More information:\n///  - [ECMAScript reference][spec]\n///  - [MDN documentation][mdn]\n///\n/// [spec]: https://tc39.es/ecma262/#prod-UpdateExpression\n/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators#increment_and_decrement\n#[cfg_attr(feature = \"serde\", derive(serde::Serialize, serde::Deserialize))]\n#[cfg_attr(feature = \"arbitrary\", derive(arbitrary::Arbitrary))]\n#[derive(Clone, Debug, PartialEq)]\npub struct Update {\n    op: UpdateOp,\n    target: Box<UpdateTarget>,\n    span: Span,\n}\n\nimpl Update {\n    /// Creates a new `Update` AST expression.\n    #[inline]\n    #[must_use]\n    pub fn new(op: UpdateOp, target: UpdateTarget, span: Span) -> Self {\n        Self {\n            op,\n            target: Box::new(target),\n            span,\n        }\n    }\n\n    /// Gets the update operation of the expression.\n    #[inline]\n    #[must_use]\n    pub const fn op(&self) -> UpdateOp {\n        self.op\n    }\n\n    /// Gets the target of this update operator.\n    #[inline]\n    #[must_use]\n    pub fn target(&self) -> &UpdateTarget {\n        self.target.as_ref()\n    }\n}\n\nimpl Spanned for Update {\n    #[inline]\n    fn span(&self) -> Span {\n        self.span\n    }\n}\n\nimpl ToInternedString for Update {\n    #[inline]\n    fn to_interned_string(&self, interner: &Interner) -> String {\n        match self.op {\n            UpdateOp::IncrementPost | UpdateOp::DecrementPost => {\n                format!(\"{}{}\", self.target.to_interned_string(interner), self.op)\n            }\n            UpdateOp::IncrementPre | UpdateOp::DecrementPre => {\n                format!(\"{}{}\", self.op, self.target.to_interned_string(interner))\n            }\n        }\n    }\n}\n\nimpl From<Update> for Expression {\n    #[inline]\n    fn from(op: Update) -> Self {\n        Self::Update(op)\n    }\n}\n\nimpl VisitWith for Update {\n    fn visit_with<'a, V>(&'a self, visitor: &mut V) -> ControlFlow<V::BreakTy>\n    where\n        V: Visitor<'a>,\n    {\n        match self.target.as_ref() {\n            UpdateTarget::Identifier(ident) => visitor.visit_identifier(ident),\n            UpdateTarget::PropertyAccess(access) => visitor.visit_property_access(access),\n        }\n    }\n\n    fn visit_with_mut<'a, V>(&'a mut self, visitor: &mut V) -> ControlFlow<V::BreakTy>\n    where\n        V: VisitorMut<'a>,\n    {\n        match &mut *self.target {\n            UpdateTarget::Identifier(ident) => visitor.visit_identifier_mut(ident),\n            UpdateTarget::PropertyAccess(access) => visitor.visit_property_access_mut(access),\n        }\n    }\n}\n\n/// A update expression can only be performed on identifier expressions or property access expressions.\n///\n/// More information:\n///  - [ECMAScript reference][spec]\n///  - [MDN documentation][mdn]\n///\n/// [spec]: https://tc39.es/ecma262/#prod-UpdateExpression\n/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators#increment_and_decrement\n#[cfg_attr(feature = \"serde\", derive(serde::Serialize, serde::Deserialize))]\n#[cfg_attr(feature = \"arbitrary\", derive(arbitrary::Arbitrary))]\n#[derive(Clone, Debug, PartialEq)]\npub enum UpdateTarget {\n    /// An [`Identifier`] expression.\n    Identifier(Identifier),\n\n    /// An [`PropertyAccess`] expression.\n    PropertyAccess(PropertyAccess),\n}\n\nimpl ToInternedString for UpdateTarget {\n    #[inline]\n    fn to_interned_string(&self, interner: &Interner) -> String {\n        match self {\n            Self::Identifier(identifier) => identifier.to_interned_string(interner),\n            Self::PropertyAccess(access) => access.to_interned_string(interner),\n        }\n    }\n}\n"
  },
  {
    "path": "core/ast/src/expression/operator/update/op.rs",
    "content": "/// A update operator is one that takes a single operand/argument and performs an operation.\n///\n/// More information:\n///  - [ECMAScript reference][spec]\n///  - [MDN documentation][mdn]\n///\n/// [spec]: https://tc39.es/ecma262/#prod-UpdateExpression\n/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators#increment_and_decrement\n#[cfg_attr(feature = \"serde\", derive(serde::Serialize, serde::Deserialize))]\n#[cfg_attr(feature = \"arbitrary\", derive(arbitrary::Arbitrary))]\n#[derive(Clone, Copy, Debug, PartialEq, Eq)]\npub enum UpdateOp {\n    /// The increment operator increments (adds one to) its operand and returns a value.\n    ///\n    /// Syntax: `x++`\n    ///\n    /// This operator increments and returns the value after incrementing.\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///  - [MDN documentation][mdn]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-postfix-increment-operator\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Arithmetic_Operators#Increment\n    IncrementPost,\n\n    /// The increment operator increments (adds one to) its operand and returns a value.\n    ///\n    /// Syntax: `++x`\n    ///\n    /// This operator increments and returns the value before incrementing.\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///  - [MDN documentation][mdn]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-prefix-increment-operator\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Arithmetic_Operators#Increment\n    IncrementPre,\n\n    /// The decrement operator decrements (subtracts one from) its operand and returns a value.\n    ///\n    /// Syntax: `x--`\n    ///\n    /// This operator decrements and returns the value before decrementing.\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///  - [MDN documentation][mdn]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-postfix-decrement-operator\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Arithmetic_Operators#Decrement\n    DecrementPost,\n\n    /// The decrement operator decrements (subtracts one from) its operand and returns a value.\n    ///\n    /// Syntax: `--x`\n    ///\n    /// This operator decrements the operand and returns the value after decrementing.\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///  - [MDN documentation][mdn]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-prefix-decrement-operator\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Arithmetic_Operators#Decrement\n    DecrementPre,\n}\n\nimpl UpdateOp {\n    /// Retrieves the operation as a static string.\n    const fn as_str(self) -> &'static str {\n        match self {\n            Self::IncrementPost | Self::IncrementPre => \"++\",\n            Self::DecrementPost | Self::DecrementPre => \"--\",\n        }\n    }\n}\n\nimpl std::fmt::Display for UpdateOp {\n    #[inline]\n    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {\n        write!(f, \"{}\", self.as_str())\n    }\n}\n"
  },
  {
    "path": "core/ast/src/expression/optional.rs",
    "content": "use super::{Expression, access::PropertyAccessField};\nuse crate::{\n    Span, Spanned,\n    function::PrivateName,\n    join_nodes,\n    visitor::{VisitWith, Visitor, VisitorMut},\n};\nuse boa_interner::{Interner, ToInternedString};\nuse core::{fmt::Write as _, ops::ControlFlow};\n\n/// List of valid operations in an [`Optional`] chain.\n#[cfg_attr(feature = \"serde\", derive(serde::Serialize, serde::Deserialize))]\n#[cfg_attr(feature = \"arbitrary\", derive(arbitrary::Arbitrary))]\n#[derive(Clone, Debug, PartialEq)]\npub enum OptionalOperationKind {\n    /// A property access (`a?.prop`).\n    SimplePropertyAccess {\n        /// The field accessed.\n        field: PropertyAccessField,\n    },\n    /// A private property access (`a?.#prop`).\n    PrivatePropertyAccess {\n        /// The private property accessed.\n        field: PrivateName,\n    },\n    /// A function call (`a?.(arg)`).\n    Call {\n        /// The args passed to the function call.\n        args: Box<[Expression]>,\n    },\n}\n\nimpl VisitWith for OptionalOperationKind {\n    fn visit_with<'a, V>(&'a self, visitor: &mut V) -> ControlFlow<V::BreakTy>\n    where\n        V: Visitor<'a>,\n    {\n        match self {\n            Self::SimplePropertyAccess { field } => visitor.visit_property_access_field(field),\n            Self::PrivatePropertyAccess { field } => visitor.visit_private_name(field),\n            Self::Call { args } => {\n                for arg in args {\n                    visitor.visit_expression(arg)?;\n                }\n                ControlFlow::Continue(())\n            }\n        }\n    }\n\n    fn visit_with_mut<'a, V>(&'a mut self, visitor: &mut V) -> ControlFlow<V::BreakTy>\n    where\n        V: VisitorMut<'a>,\n    {\n        match self {\n            Self::SimplePropertyAccess { field } => visitor.visit_property_access_field_mut(field),\n            Self::PrivatePropertyAccess { field } => visitor.visit_private_name_mut(field),\n            Self::Call { args } => {\n                for arg in args.iter_mut() {\n                    visitor.visit_expression_mut(arg)?;\n                }\n                ControlFlow::Continue(())\n            }\n        }\n    }\n}\n\n/// Operation within an [`Optional`] chain.\n///\n/// An operation within an `Optional` chain can be either shorted or non-shorted. A shorted operation\n/// (`?.item`) will force the expression to return `undefined` if the target is `undefined` or `null`.\n/// In contrast, a non-shorted operation (`.prop`) will try to access the property, even if the target\n/// is `undefined` or `null`.\n#[cfg_attr(feature = \"serde\", derive(serde::Serialize, serde::Deserialize))]\n#[cfg_attr(feature = \"arbitrary\", derive(arbitrary::Arbitrary))]\n#[derive(Clone, Debug, PartialEq)]\npub struct OptionalOperation {\n    kind: OptionalOperationKind,\n    shorted: bool,\n    span: Span,\n}\n\nimpl OptionalOperation {\n    /// Creates a new `OptionalOperation`.\n    #[inline]\n    #[must_use]\n    pub const fn new(kind: OptionalOperationKind, shorted: bool, span: Span) -> Self {\n        Self {\n            kind,\n            shorted,\n            span,\n        }\n    }\n    /// Gets the kind of operation.\n    #[inline]\n    #[must_use]\n    pub const fn kind(&self) -> &OptionalOperationKind {\n        &self.kind\n    }\n\n    /// Returns `true` if the operation short-circuits the [`Optional`] chain when the target is\n    /// `undefined` or `null`.\n    #[inline]\n    #[must_use]\n    pub const fn shorted(&self) -> bool {\n        self.shorted\n    }\n}\n\nimpl Spanned for OptionalOperation {\n    #[inline]\n    fn span(&self) -> Span {\n        self.span\n    }\n}\n\nimpl ToInternedString for OptionalOperation {\n    fn to_interned_string(&self, interner: &Interner) -> String {\n        let mut buf = if self.shorted {\n            String::from(\"?.\")\n        } else {\n            if let OptionalOperationKind::SimplePropertyAccess {\n                field: PropertyAccessField::Const(name),\n            } = &self.kind\n            {\n                return format!(\".{}\", interner.resolve_expect(name.sym()));\n            }\n\n            if let OptionalOperationKind::PrivatePropertyAccess { field } = &self.kind {\n                return format!(\".#{}\", interner.resolve_expect(field.description()));\n            }\n\n            String::new()\n        };\n        match &self.kind {\n            OptionalOperationKind::SimplePropertyAccess { field } => match field {\n                PropertyAccessField::Const(name) => {\n                    buf.push_str(&interner.resolve_expect(name.sym()).to_string());\n                }\n                PropertyAccessField::Expr(expr) => {\n                    let _ = write!(buf, \"[{}]\", expr.to_interned_string(interner));\n                }\n            },\n            OptionalOperationKind::PrivatePropertyAccess { field } => {\n                let _ = write!(buf, \"#{}\", interner.resolve_expect(field.description()));\n            }\n            OptionalOperationKind::Call { args } => {\n                let _ = write!(buf, \"({})\", join_nodes(interner, args));\n            }\n        }\n        buf\n    }\n}\n\nimpl VisitWith for OptionalOperation {\n    fn visit_with<'a, V>(&'a self, visitor: &mut V) -> ControlFlow<V::BreakTy>\n    where\n        V: Visitor<'a>,\n    {\n        visitor.visit_optional_operation_kind(&self.kind)\n    }\n\n    fn visit_with_mut<'a, V>(&'a mut self, visitor: &mut V) -> ControlFlow<V::BreakTy>\n    where\n        V: VisitorMut<'a>,\n    {\n        visitor.visit_optional_operation_kind_mut(&mut self.kind)\n    }\n}\n\n/// An optional chain expression, as defined by the [spec].\n///\n/// [Optional chaining][mdn] allows for short-circuiting property accesses and function calls, which\n/// will return `undefined` instead of returning an error if the access target or the call is\n/// either `undefined` or `null`.\n///\n/// An example of optional chaining:\n///\n/// ```Javascript\n/// const adventurer = {\n///   name: 'Alice',\n///   cat: {\n///     name: 'Dinah'\n///   }\n/// };\n///\n/// console.log(adventurer.cat?.name); // Dinah\n/// console.log(adventurer.dog?.name); // undefined\n/// ```\n///\n/// [spec]: https://tc39.es/ecma262/multipage/ecmascript-language-expressions.html#prod-OptionalExpression\n/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Optional_chaining\n#[cfg_attr(feature = \"serde\", derive(serde::Serialize, serde::Deserialize))]\n#[cfg_attr(feature = \"arbitrary\", derive(arbitrary::Arbitrary))]\n#[derive(Clone, Debug, PartialEq)]\npub struct Optional {\n    target: Box<Expression>,\n    chain: Box<[OptionalOperation]>,\n    span: Span,\n}\n\nimpl Optional {\n    /// Creates a new `Optional` expression.\n    #[inline]\n    #[must_use]\n    pub fn new(target: Expression, chain: Box<[OptionalOperation]>, span: Span) -> Self {\n        Self {\n            target: Box::new(target),\n            chain,\n            span,\n        }\n    }\n\n    /// Gets the target of this `Optional` expression.\n    #[inline]\n    #[must_use]\n    pub fn target(&self) -> &Expression {\n        self.target.as_ref()\n    }\n\n    /// Gets the chain of accesses and calls that will be applied to the target at runtime.\n    #[inline]\n    #[must_use]\n    pub fn chain(&self) -> &[OptionalOperation] {\n        self.chain.as_ref()\n    }\n}\n\nimpl Spanned for Optional {\n    #[inline]\n    fn span(&self) -> Span {\n        self.span\n    }\n}\n\nimpl From<Optional> for Expression {\n    fn from(opt: Optional) -> Self {\n        Self::Optional(opt)\n    }\n}\n\nimpl ToInternedString for Optional {\n    fn to_interned_string(&self, interner: &Interner) -> String {\n        let mut buf = self.target.to_interned_string(interner);\n\n        for item in &*self.chain {\n            buf.push_str(&item.to_interned_string(interner));\n        }\n\n        buf\n    }\n}\n\nimpl VisitWith for Optional {\n    fn visit_with<'a, V>(&'a self, visitor: &mut V) -> ControlFlow<V::BreakTy>\n    where\n        V: Visitor<'a>,\n    {\n        visitor.visit_expression(&self.target)?;\n        for op in &*self.chain {\n            visitor.visit_optional_operation(op)?;\n        }\n        ControlFlow::Continue(())\n    }\n\n    fn visit_with_mut<'a, V>(&'a mut self, visitor: &mut V) -> ControlFlow<V::BreakTy>\n    where\n        V: VisitorMut<'a>,\n    {\n        visitor.visit_expression_mut(&mut self.target)?;\n        for op in &mut *self.chain {\n            visitor.visit_optional_operation_mut(op)?;\n        }\n        ControlFlow::Continue(())\n    }\n}\n"
  },
  {
    "path": "core/ast/src/expression/parenthesized.rs",
    "content": "use super::Expression;\nuse crate::{\n    Span, Spanned,\n    visitor::{VisitWith, Visitor, VisitorMut},\n};\nuse boa_interner::{Interner, ToInternedString};\nuse core::ops::ControlFlow;\n\n/// A parenthesized expression.\n///\n/// More information:\n///  - [ECMAScript reference][spec]\n///  - [MDN documentation][mdn]\n///\n/// [spec]: https://tc39.es/ecma262/#sec-grouping-operator\n/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Grouping\n#[cfg_attr(feature = \"serde\", derive(serde::Serialize, serde::Deserialize))]\n#[cfg_attr(feature = \"arbitrary\", derive(arbitrary::Arbitrary))]\n#[derive(Clone, Debug, PartialEq)]\npub struct Parenthesized {\n    pub(crate) expression: Box<Expression>,\n    span: Span,\n}\n\nimpl Parenthesized {\n    /// Creates a parenthesized expression.\n    #[inline]\n    #[must_use]\n    pub fn new(expression: Expression, span: Span) -> Self {\n        Self {\n            expression: Box::new(expression),\n            span,\n        }\n    }\n\n    /// Gets the expression of this parenthesized expression.\n    #[inline]\n    #[must_use]\n    pub const fn expression(&self) -> &Expression {\n        &self.expression\n    }\n}\n\nimpl Spanned for Parenthesized {\n    #[inline]\n    fn span(&self) -> Span {\n        self.span\n    }\n}\n\nimpl From<Parenthesized> for Expression {\n    fn from(p: Parenthesized) -> Self {\n        Self::Parenthesized(p)\n    }\n}\n\nimpl ToInternedString for Parenthesized {\n    #[inline]\n    fn to_interned_string(&self, interner: &Interner) -> String {\n        format!(\"({})\", self.expression.to_interned_string(interner))\n    }\n}\n\nimpl VisitWith for Parenthesized {\n    fn visit_with<'a, V>(&'a self, visitor: &mut V) -> ControlFlow<V::BreakTy>\n    where\n        V: Visitor<'a>,\n    {\n        visitor.visit_expression(&self.expression)\n    }\n\n    fn visit_with_mut<'a, V>(&'a mut self, visitor: &mut V) -> ControlFlow<V::BreakTy>\n    where\n        V: VisitorMut<'a>,\n    {\n        visitor.visit_expression_mut(&mut self.expression)\n    }\n}\n"
  },
  {
    "path": "core/ast/src/expression/regexp.rs",
    "content": "//! This module contains the ECMAScript representation regular expressions.\n//!\n//! More information:\n//!  - [ECMAScript reference][spec]\n//!  - [MDN documentation][mdn]\n//!\n//! [spec]: https://tc39.es/ecma262/#sec-literals-regular-expression-literals\n//! [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_expressions\n\nuse std::ops::ControlFlow;\n\nuse boa_interner::{Interner, Sym, ToInternedString};\n\nuse crate::{\n    Span, Spanned,\n    visitor::{VisitWith, Visitor, VisitorMut},\n};\n\nuse super::Expression;\n\n/// Regular expressions in ECMAScript.\n///\n/// More information:\n///  - [ECMAScript reference][spec]\n///  - [MDN documentation][mdn]\n///\n/// [spec]: https://tc39.es/ecma262/#sec-literals-regular-expression-literals\n/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_expressions\n#[cfg_attr(feature = \"serde\", derive(serde::Serialize, serde::Deserialize))]\n#[cfg_attr(feature = \"arbitrary\", derive(arbitrary::Arbitrary))]\n#[derive(Debug, Clone, Copy, PartialEq, Eq)]\npub struct RegExpLiteral {\n    pattern: Sym,\n    flags: Sym,\n    span: Span,\n}\n\nimpl RegExpLiteral {\n    /// Create a new [`RegExpLiteral`].\n    #[inline]\n    #[must_use]\n    pub const fn new(pattern: Sym, flags: Sym, span: Span) -> Self {\n        Self {\n            pattern,\n            flags,\n            span,\n        }\n    }\n\n    /// Get the pattern part of the [`RegExpLiteral`].\n    #[inline]\n    #[must_use]\n    pub const fn pattern(&self) -> Sym {\n        self.pattern\n    }\n\n    /// Get the flags part of the [`RegExpLiteral`].\n    #[inline]\n    #[must_use]\n    pub const fn flags(&self) -> Sym {\n        self.flags\n    }\n}\n\nimpl Spanned for RegExpLiteral {\n    #[inline]\n    fn span(&self) -> Span {\n        self.span\n    }\n}\n\nimpl ToInternedString for RegExpLiteral {\n    #[inline]\n    fn to_interned_string(&self, interner: &Interner) -> String {\n        let pattern = interner.resolve_expect(self.pattern);\n        let flags = interner.resolve_expect(self.flags);\n        format!(\"/{pattern}/{flags}\")\n    }\n}\n\nimpl From<RegExpLiteral> for Expression {\n    #[inline]\n    fn from(value: RegExpLiteral) -> Self {\n        Self::RegExpLiteral(value)\n    }\n}\n\nimpl VisitWith for RegExpLiteral {\n    #[inline]\n    fn visit_with<'a, V>(&'a self, visitor: &mut V) -> ControlFlow<V::BreakTy>\n    where\n        V: Visitor<'a>,\n    {\n        visitor.visit_sym(&self.pattern)?;\n        visitor.visit_sym(&self.flags)\n    }\n\n    #[inline]\n    fn visit_with_mut<'a, V>(&'a mut self, visitor: &mut V) -> ControlFlow<V::BreakTy>\n    where\n        V: VisitorMut<'a>,\n    {\n        visitor.visit_sym_mut(&mut self.pattern)?;\n        visitor.visit_sym_mut(&mut self.flags)\n    }\n}\n"
  },
  {
    "path": "core/ast/src/expression/spread.rs",
    "content": "use boa_interner::{Interner, ToInternedString};\nuse core::ops::ControlFlow;\n\nuse crate::{\n    Span, Spanned,\n    visitor::{VisitWith, Visitor, VisitorMut},\n};\n\nuse super::Expression;\n\n/// The `spread` operator allows an iterable such as an array expression or string to be\n/// expanded.\n///\n/// Syntax: `...x`\n///\n/// It expands array expressions or strings in places where zero or more arguments (for\n/// function calls) or elements (for array literals)\n/// are expected, or an object expression to be expanded in places where zero or more key-value\n/// pairs (for object literals) are expected.\n///\n/// More information:\n///  - [ECMAScript reference][spec]\n///  - [MDN documentation][mdn]\n///\n/// [spec]: https://tc39.es/ecma262/#prod-SpreadElement\n/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Spread_syntax\n#[cfg_attr(feature = \"serde\", derive(serde::Serialize, serde::Deserialize))]\n#[cfg_attr(feature = \"arbitrary\", derive(arbitrary::Arbitrary))]\n#[derive(Clone, Debug, PartialEq)]\npub struct Spread {\n    target: Box<Expression>,\n    span: Span,\n}\n\nimpl Spread {\n    /// Creates a [`Spread`] AST Expression.\n    #[inline]\n    #[must_use]\n    pub fn new(target: Expression, span: Span) -> Self {\n        Self {\n            target: Box::new(target),\n            span,\n        }\n    }\n\n    /// Gets the target expression to be expanded by the spread operator.\n    #[inline]\n    #[must_use]\n    pub const fn target(&self) -> &Expression {\n        &self.target\n    }\n}\n\nimpl Spanned for Spread {\n    #[inline]\n    fn span(&self) -> Span {\n        self.span\n    }\n}\n\nimpl ToInternedString for Spread {\n    #[inline]\n    fn to_interned_string(&self, interner: &Interner) -> String {\n        format!(\"...{}\", self.target().to_interned_string(interner))\n    }\n}\n\nimpl From<Spread> for Expression {\n    #[inline]\n    fn from(spread: Spread) -> Self {\n        Self::Spread(spread)\n    }\n}\n\nimpl VisitWith for Spread {\n    fn visit_with<'a, V>(&'a self, visitor: &mut V) -> ControlFlow<V::BreakTy>\n    where\n        V: Visitor<'a>,\n    {\n        visitor.visit_expression(&self.target)\n    }\n\n    fn visit_with_mut<'a, V>(&'a mut self, visitor: &mut V) -> ControlFlow<V::BreakTy>\n    where\n        V: VisitorMut<'a>,\n    {\n        visitor.visit_expression_mut(&mut self.target)\n    }\n}\n"
  },
  {
    "path": "core/ast/src/expression/tagged_template.rs",
    "content": "use super::Expression;\nuse crate::{\n    Span, Spanned,\n    visitor::{VisitWith, Visitor, VisitorMut},\n};\nuse boa_interner::{Interner, Sym, ToInternedString};\nuse core::{fmt::Write as _, ops::ControlFlow};\n\n/// A [`TaggedTemplate`][moz] expression, as defined by the [spec].\n///\n/// `TaggedTemplate`s are a type of template literals that are parsed by a custom function to generate\n/// arbitrary objects from the inner strings and expressions.\n///\n/// [moz]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Template_literals#tagged_templates\n/// [spec]: https://tc39.es/ecma262/#sec-tagged-templates\n#[cfg_attr(feature = \"serde\", derive(serde::Serialize, serde::Deserialize))]\n#[cfg_attr(feature = \"arbitrary\", derive(arbitrary::Arbitrary))]\n#[derive(Clone, Debug, PartialEq)]\npub struct TaggedTemplate {\n    tag: Box<Expression>,\n    raws: Box<[Sym]>,\n    cookeds: Box<[Option<Sym>]>,\n    exprs: Box<[Expression]>,\n    identifier: u64,\n    span: Span,\n}\n\nimpl TaggedTemplate {\n    /// Creates a new tagged template with a tag, the list of raw strings, the cooked strings and\n    /// the expressions.\n    #[inline]\n    #[must_use]\n    pub fn new(\n        tag: Expression,\n        raws: Box<[Sym]>,\n        cookeds: Box<[Option<Sym>]>,\n        exprs: Box<[Expression]>,\n        identifier: u64,\n        span: Span,\n    ) -> Self {\n        Self {\n            tag: tag.into(),\n            raws,\n            cookeds,\n            exprs,\n            identifier,\n            span,\n        }\n    }\n\n    /// Gets the tag function of the template.\n    #[inline]\n    #[must_use]\n    pub const fn tag(&self) -> &Expression {\n        &self.tag\n    }\n\n    /// Gets the inner raw strings of the template.\n    #[inline]\n    #[must_use]\n    pub const fn raws(&self) -> &[Sym] {\n        &self.raws\n    }\n\n    /// Gets the cooked strings of the template.\n    #[inline]\n    #[must_use]\n    pub const fn cookeds(&self) -> &[Option<Sym>] {\n        &self.cookeds\n    }\n\n    /// Gets the interpolated expressions of the template.\n    #[inline]\n    #[must_use]\n    pub const fn exprs(&self) -> &[Expression] {\n        &self.exprs\n    }\n\n    /// Gets the unique identifier of the template.\n    #[inline]\n    #[must_use]\n    pub const fn identifier(&self) -> u64 {\n        self.identifier\n    }\n}\n\nimpl Spanned for TaggedTemplate {\n    #[inline]\n    fn span(&self) -> Span {\n        self.span\n    }\n}\n\nimpl ToInternedString for TaggedTemplate {\n    #[inline]\n    fn to_interned_string(&self, interner: &Interner) -> String {\n        let mut buf = format!(\"{}`\", self.tag.to_interned_string(interner));\n        let mut exprs = self.exprs.iter();\n\n        for raw in &self.raws {\n            let _ = write!(buf, \"{}\", interner.resolve_expect(*raw));\n            if let Some(expr) = exprs.next() {\n                let _ = write!(buf, \"${{{}}}\", expr.to_interned_string(interner));\n            }\n        }\n        buf.push('`');\n\n        buf\n    }\n}\n\nimpl From<TaggedTemplate> for Expression {\n    #[inline]\n    fn from(template: TaggedTemplate) -> Self {\n        Self::TaggedTemplate(template)\n    }\n}\n\nimpl VisitWith for TaggedTemplate {\n    fn visit_with<'a, V>(&'a self, visitor: &mut V) -> ControlFlow<V::BreakTy>\n    where\n        V: Visitor<'a>,\n    {\n        visitor.visit_expression(&self.tag)?;\n        for raw in &*self.raws {\n            visitor.visit_sym(raw)?;\n        }\n        for cooked in self.cookeds.iter().flatten() {\n            visitor.visit_sym(cooked)?;\n        }\n        for expr in &*self.exprs {\n            visitor.visit_expression(expr)?;\n        }\n        ControlFlow::Continue(())\n    }\n\n    fn visit_with_mut<'a, V>(&'a mut self, visitor: &mut V) -> ControlFlow<V::BreakTy>\n    where\n        V: VisitorMut<'a>,\n    {\n        visitor.visit_expression_mut(&mut self.tag)?;\n        for raw in &mut *self.raws {\n            visitor.visit_sym_mut(raw)?;\n        }\n        for cooked in self.cookeds.iter_mut().flatten() {\n            visitor.visit_sym_mut(cooked)?;\n        }\n        for expr in &mut *self.exprs {\n            visitor.visit_expression_mut(expr)?;\n        }\n        ControlFlow::Continue(())\n    }\n}\n"
  },
  {
    "path": "core/ast/src/expression/this.rs",
    "content": "//! `this` ECMAScript expression.\n\nuse crate::{\n    Span, Spanned,\n    visitor::{VisitWith, Visitor, VisitorMut},\n};\nuse boa_interner::{Interner, ToInternedString};\nuse core::ops::ControlFlow;\n\nuse super::Expression;\n\n/// ECMAScript's `this` expression AST node.\n#[cfg_attr(feature = \"serde\", derive(serde::Serialize, serde::Deserialize))]\n#[cfg_attr(feature = \"arbitrary\", derive(arbitrary::Arbitrary))]\n#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]\npub struct This {\n    span: Span,\n}\n\nimpl This {\n    /// Creates a new This AST Expression.\n    #[inline]\n    #[must_use]\n    pub const fn new(span: Span) -> Self {\n        Self { span }\n    }\n}\n\nimpl Spanned for This {\n    #[inline]\n    fn span(&self) -> Span {\n        self.span\n    }\n}\n\nimpl From<This> for Expression {\n    #[inline]\n    fn from(value: This) -> Self {\n        Expression::This(value)\n    }\n}\n\nimpl ToInternedString for This {\n    #[inline]\n    fn to_interned_string(&self, _interner: &Interner) -> String {\n        String::from(\"this\")\n    }\n}\n\nimpl VisitWith for This {\n    fn visit_with<'a, V>(&'a self, _visitor: &mut V) -> ControlFlow<V::BreakTy>\n    where\n        V: Visitor<'a>,\n    {\n        ControlFlow::Continue(())\n    }\n\n    fn visit_with_mut<'a, V>(&'a mut self, _visitor: &mut V) -> ControlFlow<V::BreakTy>\n    where\n        V: VisitorMut<'a>,\n    {\n        ControlFlow::Continue(())\n    }\n}\n"
  },
  {
    "path": "core/ast/src/expression/yield.rs",
    "content": "use boa_interner::{Interner, ToInternedString};\nuse core::ops::ControlFlow;\n\nuse crate::{\n    Span, Spanned,\n    visitor::{VisitWith, Visitor, VisitorMut},\n};\n\nuse super::Expression;\n\n/// The `yield` keyword is used to pause and resume a generator function\n///\n/// More information:\n///  - [ECMAScript reference][spec]\n///  - [MDN documentation][mdn]\n///\n/// [spec]: https://tc39.es/ecma262/#prod-YieldExpression\n/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/yield\n#[cfg_attr(feature = \"serde\", derive(serde::Serialize, serde::Deserialize))]\n#[cfg_attr(feature = \"arbitrary\", derive(arbitrary::Arbitrary))]\n#[derive(Clone, Debug, PartialEq)]\npub struct Yield {\n    target: Option<Box<Expression>>,\n    delegate: bool,\n    span: Span,\n}\n\nimpl Yield {\n    /// Creates a [`Yield`] AST Expression.\n    #[inline]\n    #[must_use]\n    pub fn new(expr: Option<Expression>, delegate: bool, span: Span) -> Self {\n        Self {\n            target: expr.map(Box::new),\n            delegate,\n            span,\n        }\n    }\n\n    /// Gets the target expression of this `Yield` statement.\n    #[inline]\n    pub fn target(&self) -> Option<&Expression> {\n        self.target.as_ref().map(Box::as_ref)\n    }\n\n    /// Returns `true` if this `Yield` statement delegates to another generator or iterable object.\n    #[inline]\n    #[must_use]\n    pub const fn delegate(&self) -> bool {\n        self.delegate\n    }\n}\n\nimpl Spanned for Yield {\n    #[inline]\n    fn span(&self) -> Span {\n        self.span\n    }\n}\n\nimpl From<Yield> for Expression {\n    #[inline]\n    fn from(r#yield: Yield) -> Self {\n        Self::Yield(r#yield)\n    }\n}\n\nimpl ToInternedString for Yield {\n    #[inline]\n    fn to_interned_string(&self, interner: &Interner) -> String {\n        let y = if self.delegate { \"yield*\" } else { \"yield\" };\n        if let Some(ex) = self.target() {\n            format!(\"{y} {}\", ex.to_interned_string(interner))\n        } else {\n            y.to_owned()\n        }\n    }\n}\n\nimpl VisitWith for Yield {\n    fn visit_with<'a, V>(&'a self, visitor: &mut V) -> ControlFlow<V::BreakTy>\n    where\n        V: Visitor<'a>,\n    {\n        if let Some(expr) = &self.target {\n            visitor.visit_expression(expr)\n        } else {\n            ControlFlow::Continue(())\n        }\n    }\n\n    fn visit_with_mut<'a, V>(&'a mut self, visitor: &mut V) -> ControlFlow<V::BreakTy>\n    where\n        V: VisitorMut<'a>,\n    {\n        if let Some(expr) = &mut self.target {\n            visitor.visit_expression_mut(expr)\n        } else {\n            ControlFlow::Continue(())\n        }\n    }\n}\n"
  },
  {
    "path": "core/ast/src/function/arrow_function.rs",
    "content": "use super::{FormalParameterList, FunctionBody};\nuse crate::operations::{ContainsSymbol, contains};\nuse crate::scope::FunctionScopes;\nuse crate::visitor::{VisitWith, Visitor, VisitorMut};\nuse crate::{LinearSpan, LinearSpanIgnoreEq, Span, Spanned};\nuse crate::{\n    expression::{Expression, Identifier},\n    join_nodes,\n};\nuse boa_interner::{Interner, ToIndentedString};\nuse core::{fmt::Write as _, ops::ControlFlow};\n\n/// An arrow function expression, as defined by the [spec].\n///\n/// An [arrow function][mdn] expression is a syntactically compact alternative to a regular function\n/// expression. Arrow function expressions are ill suited as methods, and they cannot be used as\n/// constructors. Arrow functions cannot be used as constructors and will throw an error when\n/// used with new.\n///\n/// [spec]: https://tc39.es/ecma262/#prod-ArrowFunction\n/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/Arrow_functions\n#[cfg_attr(feature = \"serde\", derive(serde::Serialize, serde::Deserialize))]\n#[cfg_attr(feature = \"arbitrary\", derive(arbitrary::Arbitrary))]\n#[derive(Clone, Debug, PartialEq)]\npub struct ArrowFunction {\n    pub(crate) name: Option<Identifier>,\n    pub(crate) parameters: FormalParameterList,\n    pub(crate) body: FunctionBody,\n    pub(crate) contains_direct_eval: bool,\n\n    #[cfg_attr(feature = \"serde\", serde(skip))]\n    pub(crate) scopes: FunctionScopes,\n    linear_span: LinearSpanIgnoreEq,\n    span: Span,\n}\n\nimpl ArrowFunction {\n    /// Creates a new `ArrowFunctionDecl` AST Expression.\n    #[inline]\n    #[must_use]\n    pub fn new(\n        name: Option<Identifier>,\n        parameters: FormalParameterList,\n        body: FunctionBody,\n        linear_span: LinearSpan,\n        span: Span,\n    ) -> Self {\n        let contains_direct_eval = contains(&parameters, ContainsSymbol::DirectEval)\n            || contains(&body, ContainsSymbol::DirectEval);\n        Self {\n            name,\n            parameters,\n            body,\n            contains_direct_eval,\n            scopes: FunctionScopes::default(),\n            linear_span: linear_span.into(),\n            span,\n        }\n    }\n\n    /// Gets the name of the arrow function.\n    #[inline]\n    #[must_use]\n    pub const fn name(&self) -> Option<Identifier> {\n        self.name\n    }\n\n    /// Sets the name of the arrow function.\n    #[inline]\n    pub fn set_name(&mut self, name: Option<Identifier>) {\n        self.name = name;\n    }\n\n    /// Gets the list of parameters of the arrow function.\n    #[inline]\n    #[must_use]\n    pub const fn parameters(&self) -> &FormalParameterList {\n        &self.parameters\n    }\n\n    /// Gets the body of the arrow function.\n    #[inline]\n    #[must_use]\n    pub const fn body(&self) -> &FunctionBody {\n        &self.body\n    }\n\n    /// Returns the scopes of the arrow function.\n    #[inline]\n    #[must_use]\n    pub const fn scopes(&self) -> &FunctionScopes {\n        &self.scopes\n    }\n\n    /// Gets linear span of the function declaration.\n    #[inline]\n    #[must_use]\n    pub const fn linear_span(&self) -> LinearSpan {\n        self.linear_span.0\n    }\n\n    /// Returns `true` if the arrow function contains a direct call to `eval`.\n    #[inline]\n    #[must_use]\n    pub const fn contains_direct_eval(&self) -> bool {\n        self.contains_direct_eval\n    }\n}\n\nimpl Spanned for ArrowFunction {\n    #[inline]\n    fn span(&self) -> Span {\n        self.span\n    }\n}\n\nimpl ToIndentedString for ArrowFunction {\n    fn to_indented_string(&self, interner: &Interner, indentation: usize) -> String {\n        let mut buf = format!(\"({}\", join_nodes(interner, self.parameters.as_ref()));\n        if self.body().statements().is_empty() {\n            buf.push_str(\") => {}\");\n        } else {\n            let _ = write!(\n                buf,\n                \") => {{\\n{}{}}}\",\n                self.body.to_indented_string(interner, indentation + 1),\n                \"    \".repeat(indentation)\n            );\n        }\n        buf\n    }\n}\n\nimpl From<ArrowFunction> for Expression {\n    fn from(decl: ArrowFunction) -> Self {\n        Self::ArrowFunction(decl)\n    }\n}\n\nimpl VisitWith for ArrowFunction {\n    fn visit_with<'a, V>(&'a self, visitor: &mut V) -> ControlFlow<V::BreakTy>\n    where\n        V: Visitor<'a>,\n    {\n        if let Some(ident) = &self.name {\n            visitor.visit_identifier(ident)?;\n        }\n        visitor.visit_formal_parameter_list(&self.parameters)?;\n        visitor.visit_function_body(&self.body)\n    }\n\n    fn visit_with_mut<'a, V>(&'a mut self, visitor: &mut V) -> ControlFlow<V::BreakTy>\n    where\n        V: VisitorMut<'a>,\n    {\n        if let Some(ident) = &mut self.name {\n            visitor.visit_identifier_mut(ident)?;\n        }\n        visitor.visit_formal_parameter_list_mut(&mut self.parameters)?;\n        visitor.visit_function_body_mut(&mut self.body)\n    }\n}\n"
  },
  {
    "path": "core/ast/src/function/async_arrow_function.rs",
    "content": "use super::{FormalParameterList, FunctionBody};\nuse crate::operations::{ContainsSymbol, contains};\nuse crate::scope::FunctionScopes;\nuse crate::visitor::{VisitWith, Visitor, VisitorMut};\nuse crate::{LinearSpan, LinearSpanIgnoreEq, Span, Spanned};\nuse crate::{\n    expression::{Expression, Identifier},\n    join_nodes,\n};\nuse boa_interner::{Interner, ToIndentedString};\nuse core::{fmt::Write as _, ops::ControlFlow};\n\n/// An async arrow function expression, as defined by the [spec].\n///\n/// An [async arrow function][mdn] expression is a syntactically compact alternative to a regular function\n/// expression. Arrow function expressions are ill suited as methods, and they cannot be used as\n/// constructors. Arrow functions cannot be used as constructors and will throw an error when\n/// used with new.\n///\n/// [spec]: https://tc39.es/ecma262/#prod-AsyncArrowFunction\n/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/Arrow_functions\n#[cfg_attr(feature = \"serde\", derive(serde::Serialize, serde::Deserialize))]\n#[cfg_attr(feature = \"arbitrary\", derive(arbitrary::Arbitrary))]\n#[derive(Clone, Debug, PartialEq)]\npub struct AsyncArrowFunction {\n    pub(crate) name: Option<Identifier>,\n    pub(crate) parameters: FormalParameterList,\n    pub(crate) body: FunctionBody,\n    pub(crate) contains_direct_eval: bool,\n\n    #[cfg_attr(feature = \"serde\", serde(skip))]\n    pub(crate) scopes: FunctionScopes,\n    linear_span: LinearSpanIgnoreEq,\n\n    span: Span,\n}\n\nimpl AsyncArrowFunction {\n    /// Creates a new `AsyncArrowFunction` AST Expression.\n    #[inline]\n    #[must_use]\n    pub fn new(\n        name: Option<Identifier>,\n        parameters: FormalParameterList,\n        body: FunctionBody,\n        linear_span: LinearSpan,\n        span: Span,\n    ) -> Self {\n        let contains_direct_eval = contains(&parameters, ContainsSymbol::DirectEval)\n            || contains(&body, ContainsSymbol::DirectEval);\n        Self {\n            name,\n            parameters,\n            body,\n            contains_direct_eval,\n            scopes: FunctionScopes::default(),\n            linear_span: linear_span.into(),\n            span,\n        }\n    }\n\n    /// Gets the name of the async arrow function.\n    #[inline]\n    #[must_use]\n    pub const fn name(&self) -> Option<Identifier> {\n        self.name\n    }\n\n    /// Sets the name of the async arrow function.\n    #[inline]\n    pub fn set_name(&mut self, name: Option<Identifier>) {\n        self.name = name;\n    }\n\n    /// Gets the list of parameters of the async arrow function.\n    #[inline]\n    #[must_use]\n    pub const fn parameters(&self) -> &FormalParameterList {\n        &self.parameters\n    }\n\n    /// Gets the body of the async arrow function.\n    #[inline]\n    #[must_use]\n    pub const fn body(&self) -> &FunctionBody {\n        &self.body\n    }\n\n    /// Returns the scopes of the async arrow function.\n    #[inline]\n    #[must_use]\n    pub const fn scopes(&self) -> &FunctionScopes {\n        &self.scopes\n    }\n\n    /// Gets linear span of the function declaration.\n    #[inline]\n    #[must_use]\n    pub const fn linear_span(&self) -> LinearSpan {\n        self.linear_span.0\n    }\n\n    /// Returns `true` if the function declaration contains a direct call to `eval`.\n    #[inline]\n    #[must_use]\n    pub const fn contains_direct_eval(&self) -> bool {\n        self.contains_direct_eval\n    }\n}\n\nimpl Spanned for AsyncArrowFunction {\n    #[inline]\n    fn span(&self) -> Span {\n        self.span\n    }\n}\n\nimpl ToIndentedString for AsyncArrowFunction {\n    fn to_indented_string(&self, interner: &Interner, indentation: usize) -> String {\n        let mut buf = format!(\"async ({}\", join_nodes(interner, self.parameters.as_ref()));\n        if self.body().statements().is_empty() {\n            buf.push_str(\") => {}\");\n        } else {\n            let _ = write!(\n                buf,\n                \") => {{\\n{}{}}}\",\n                self.body.to_indented_string(interner, indentation + 1),\n                \"    \".repeat(indentation)\n            );\n        }\n        buf\n    }\n}\n\nimpl From<AsyncArrowFunction> for Expression {\n    fn from(decl: AsyncArrowFunction) -> Self {\n        Self::AsyncArrowFunction(decl)\n    }\n}\n\nimpl VisitWith for AsyncArrowFunction {\n    fn visit_with<'a, V>(&'a self, visitor: &mut V) -> ControlFlow<V::BreakTy>\n    where\n        V: Visitor<'a>,\n    {\n        if let Some(ident) = &self.name {\n            visitor.visit_identifier(ident)?;\n        }\n        visitor.visit_formal_parameter_list(&self.parameters)?;\n        visitor.visit_function_body(&self.body)\n    }\n\n    fn visit_with_mut<'a, V>(&'a mut self, visitor: &mut V) -> ControlFlow<V::BreakTy>\n    where\n        V: VisitorMut<'a>,\n    {\n        if let Some(ident) = &mut self.name {\n            visitor.visit_identifier_mut(ident)?;\n        }\n        visitor.visit_formal_parameter_list_mut(&mut self.parameters)?;\n        visitor.visit_function_body_mut(&mut self.body)\n    }\n}\n"
  },
  {
    "path": "core/ast/src/function/async_function.rs",
    "content": "//! Async Function Expression.\n\nuse super::{FormalParameterList, FunctionBody};\nuse crate::{\n    Declaration, LinearSpan, LinearSpanIgnoreEq, Span, Spanned, block_to_string,\n    expression::{Expression, Identifier},\n    join_nodes,\n    operations::{ContainsSymbol, contains},\n    scope::{FunctionScopes, Scope},\n    visitor::{VisitWith, Visitor, VisitorMut},\n};\nuse boa_interner::{Interner, ToIndentedString};\nuse core::{fmt::Write as _, ops::ControlFlow};\n\n/// An async function declaration.\n///\n/// More information:\n///  - [ECMAScript reference][spec]\n///  - [MDN documentation][mdn]\n///\n/// [spec]: https://tc39.es/ecma262/#prod-AsyncFunctionDeclaration\n/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/async_function\n#[cfg_attr(feature = \"serde\", derive(serde::Serialize, serde::Deserialize))]\n#[cfg_attr(feature = \"arbitrary\", derive(arbitrary::Arbitrary))]\n#[derive(Clone, Debug, PartialEq)]\npub struct AsyncFunctionDeclaration {\n    name: Identifier,\n    pub(crate) parameters: FormalParameterList,\n    pub(crate) body: FunctionBody,\n    pub(crate) contains_direct_eval: bool,\n\n    #[cfg_attr(feature = \"serde\", serde(skip))]\n    pub(crate) scopes: FunctionScopes,\n    linear_span: LinearSpanIgnoreEq,\n}\n\nimpl AsyncFunctionDeclaration {\n    /// Creates a new async function declaration.\n    #[inline]\n    #[must_use]\n    pub fn new(\n        name: Identifier,\n        parameters: FormalParameterList,\n        body: FunctionBody,\n        linear_span: LinearSpan,\n    ) -> Self {\n        let contains_direct_eval = contains(&parameters, ContainsSymbol::DirectEval)\n            || contains(&body, ContainsSymbol::DirectEval);\n        Self {\n            name,\n            parameters,\n            body,\n            contains_direct_eval,\n            scopes: FunctionScopes::default(),\n            linear_span: linear_span.into(),\n        }\n    }\n\n    /// Gets the name of the async function declaration.\n    #[inline]\n    #[must_use]\n    pub const fn name(&self) -> Identifier {\n        self.name\n    }\n\n    /// Gets the list of parameters of the async function declaration.\n    #[inline]\n    #[must_use]\n    pub const fn parameters(&self) -> &FormalParameterList {\n        &self.parameters\n    }\n\n    /// Gets the body of the async function declaration.\n    #[inline]\n    #[must_use]\n    pub const fn body(&self) -> &FunctionBody {\n        &self.body\n    }\n\n    /// Gets the scopes of the async function declaration.\n    #[inline]\n    #[must_use]\n    pub const fn scopes(&self) -> &FunctionScopes {\n        &self.scopes\n    }\n\n    /// Gets linear span of the function declaration.\n    #[inline]\n    #[must_use]\n    pub const fn linear_span(&self) -> LinearSpan {\n        self.linear_span.0\n    }\n\n    /// Returns `true` if the async function declaration contains a direct call to `eval`.\n    #[inline]\n    #[must_use]\n    pub const fn contains_direct_eval(&self) -> bool {\n        self.contains_direct_eval\n    }\n}\n\nimpl ToIndentedString for AsyncFunctionDeclaration {\n    fn to_indented_string(&self, interner: &Interner, indentation: usize) -> String {\n        format!(\n            \"async function {}({}) {}\",\n            interner.resolve_expect(self.name.sym()),\n            join_nodes(interner, self.parameters.as_ref()),\n            block_to_string(&self.body.statements, interner, indentation)\n        )\n    }\n}\n\nimpl VisitWith for AsyncFunctionDeclaration {\n    fn visit_with<'a, V>(&'a self, visitor: &mut V) -> ControlFlow<V::BreakTy>\n    where\n        V: Visitor<'a>,\n    {\n        visitor.visit_identifier(&self.name)?;\n        visitor.visit_formal_parameter_list(&self.parameters)?;\n        visitor.visit_function_body(&self.body)\n    }\n\n    fn visit_with_mut<'a, V>(&'a mut self, visitor: &mut V) -> ControlFlow<V::BreakTy>\n    where\n        V: VisitorMut<'a>,\n    {\n        visitor.visit_identifier_mut(&mut self.name)?;\n        visitor.visit_formal_parameter_list_mut(&mut self.parameters)?;\n        visitor.visit_function_body_mut(&mut self.body)\n    }\n}\n\nimpl From<AsyncFunctionDeclaration> for Declaration {\n    #[inline]\n    fn from(f: AsyncFunctionDeclaration) -> Self {\n        Self::AsyncFunctionDeclaration(f)\n    }\n}\n\n/// An async function expression.\n///\n/// More information:\n///  - [ECMAScript reference][spec]\n///  - [MDN documentation][mdn]\n///\n/// [spec]: https://tc39.es/ecma262/#prod-AsyncFunctionExpression\n/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/async_function\n#[cfg_attr(feature = \"serde\", derive(serde::Serialize, serde::Deserialize))]\n#[cfg_attr(feature = \"arbitrary\", derive(arbitrary::Arbitrary))]\n#[derive(Clone, Debug, PartialEq)]\npub struct AsyncFunctionExpression {\n    pub(crate) name: Option<Identifier>,\n    pub(crate) parameters: FormalParameterList,\n    pub(crate) body: FunctionBody,\n    pub(crate) has_binding_identifier: bool,\n    pub(crate) contains_direct_eval: bool,\n\n    #[cfg_attr(feature = \"serde\", serde(skip))]\n    pub(crate) name_scope: Option<Scope>,\n\n    #[cfg_attr(feature = \"serde\", serde(skip))]\n    pub(crate) scopes: FunctionScopes,\n    linear_span: LinearSpanIgnoreEq,\n\n    span: Span,\n}\n\nimpl AsyncFunctionExpression {\n    /// Creates a new async function expression.\n    #[inline]\n    #[must_use]\n    pub fn new(\n        name: Option<Identifier>,\n        parameters: FormalParameterList,\n        body: FunctionBody,\n        linear_span: LinearSpan,\n        has_binding_identifier: bool,\n        span: Span,\n    ) -> Self {\n        let contains_direct_eval = contains(&parameters, ContainsSymbol::DirectEval)\n            || contains(&body, ContainsSymbol::DirectEval);\n        Self {\n            name,\n            parameters,\n            body,\n            has_binding_identifier,\n            name_scope: None,\n            contains_direct_eval,\n            scopes: FunctionScopes::default(),\n            linear_span: linear_span.into(),\n            span,\n        }\n    }\n\n    /// Gets the name of the async function expression.\n    #[inline]\n    #[must_use]\n    pub const fn name(&self) -> Option<Identifier> {\n        self.name\n    }\n\n    /// Gets the list of parameters of the async function expression.\n    #[inline]\n    #[must_use]\n    pub const fn parameters(&self) -> &FormalParameterList {\n        &self.parameters\n    }\n\n    /// Gets the body of the async function expression.\n    #[inline]\n    #[must_use]\n    pub const fn body(&self) -> &FunctionBody {\n        &self.body\n    }\n\n    /// Returns whether the async function expression has a binding identifier.\n    #[inline]\n    #[must_use]\n    pub const fn has_binding_identifier(&self) -> bool {\n        self.has_binding_identifier\n    }\n\n    /// Gets the name scope of the async function expression.\n    #[inline]\n    #[must_use]\n    pub const fn name_scope(&self) -> Option<&Scope> {\n        self.name_scope.as_ref()\n    }\n\n    /// Gets the scopes of the async function expression.\n    #[inline]\n    #[must_use]\n    pub const fn scopes(&self) -> &FunctionScopes {\n        &self.scopes\n    }\n\n    /// Gets linear span of the function declaration.\n    #[inline]\n    #[must_use]\n    pub const fn linear_span(&self) -> LinearSpan {\n        self.linear_span.0\n    }\n\n    /// Returns `true` if the async function expression contains a direct call to `eval`.\n    #[inline]\n    #[must_use]\n    pub const fn contains_direct_eval(&self) -> bool {\n        self.contains_direct_eval\n    }\n}\n\nimpl Spanned for AsyncFunctionExpression {\n    #[inline]\n    fn span(&self) -> Span {\n        self.span\n    }\n}\n\nimpl ToIndentedString for AsyncFunctionExpression {\n    fn to_indented_string(&self, interner: &Interner, indentation: usize) -> String {\n        let mut buf = \"async function\".to_owned();\n        if self.has_binding_identifier\n            && let Some(name) = self.name\n        {\n            let _ = write!(buf, \" {}\", interner.resolve_expect(name.sym()));\n        }\n        let _ = write!(buf, \"({}\", join_nodes(interner, self.parameters.as_ref()));\n        if self.body().statements().is_empty() {\n            buf.push_str(\") {}\");\n        } else {\n            let _ = write!(\n                buf,\n                \") {{\\n{}{}}}\",\n                self.body.to_indented_string(interner, indentation + 1),\n                \"    \".repeat(indentation)\n            );\n        }\n        buf\n    }\n}\n\nimpl From<AsyncFunctionExpression> for Expression {\n    #[inline]\n    fn from(expr: AsyncFunctionExpression) -> Self {\n        Self::AsyncFunctionExpression(expr)\n    }\n}\n\nimpl VisitWith for AsyncFunctionExpression {\n    fn visit_with<'a, V>(&'a self, visitor: &mut V) -> ControlFlow<V::BreakTy>\n    where\n        V: Visitor<'a>,\n    {\n        if let Some(ident) = &self.name {\n            visitor.visit_identifier(ident)?;\n        }\n        visitor.visit_formal_parameter_list(&self.parameters)?;\n        visitor.visit_function_body(&self.body)\n    }\n\n    fn visit_with_mut<'a, V>(&'a mut self, visitor: &mut V) -> ControlFlow<V::BreakTy>\n    where\n        V: VisitorMut<'a>,\n    {\n        if let Some(ident) = &mut self.name {\n            visitor.visit_identifier_mut(ident)?;\n        }\n        visitor.visit_formal_parameter_list_mut(&mut self.parameters)?;\n        visitor.visit_function_body_mut(&mut self.body)\n    }\n}\n"
  },
  {
    "path": "core/ast/src/function/async_generator.rs",
    "content": "//! Async Generator Expression\n\nuse super::{FormalParameterList, FunctionBody};\nuse crate::operations::{ContainsSymbol, contains};\nuse crate::scope::{FunctionScopes, Scope};\nuse crate::visitor::{VisitWith, Visitor, VisitorMut};\nuse crate::{\n    Declaration, Spanned, block_to_string,\n    expression::{Expression, Identifier},\n    join_nodes,\n};\nuse crate::{LinearSpan, LinearSpanIgnoreEq, Span};\nuse boa_interner::{Interner, ToIndentedString};\nuse core::{fmt::Write as _, ops::ControlFlow};\n\n/// An async generator declaration.\n///\n/// More information:\n///  - [ECMAScript reference][spec]\n///  - [MDN documentation][mdn]\n///\n/// [spec]: https://tc39.es/ecma262/#prod-AsyncGeneratorDeclaration\n/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/async_function*\n#[cfg_attr(feature = \"serde\", derive(serde::Serialize, serde::Deserialize))]\n#[cfg_attr(feature = \"arbitrary\", derive(arbitrary::Arbitrary))]\n#[derive(Clone, Debug, PartialEq)]\npub struct AsyncGeneratorDeclaration {\n    name: Identifier,\n    pub(crate) parameters: FormalParameterList,\n    pub(crate) body: FunctionBody,\n    pub(crate) contains_direct_eval: bool,\n\n    #[cfg_attr(feature = \"serde\", serde(skip))]\n    pub(crate) scopes: FunctionScopes,\n    linear_span: LinearSpanIgnoreEq,\n}\n\nimpl AsyncGeneratorDeclaration {\n    /// Creates a new async generator declaration.\n    #[inline]\n    #[must_use]\n    pub fn new(\n        name: Identifier,\n        parameters: FormalParameterList,\n        body: FunctionBody,\n        linear_span: LinearSpan,\n    ) -> Self {\n        let contains_direct_eval = contains(&parameters, ContainsSymbol::DirectEval)\n            || contains(&body, ContainsSymbol::DirectEval);\n        Self {\n            name,\n            parameters,\n            body,\n            contains_direct_eval,\n            scopes: FunctionScopes::default(),\n            linear_span: linear_span.into(),\n        }\n    }\n\n    /// Gets the name of the async generator declaration.\n    #[inline]\n    #[must_use]\n    pub const fn name(&self) -> Identifier {\n        self.name\n    }\n\n    /// Gets the list of parameters of the async generator declaration.\n    #[inline]\n    #[must_use]\n    pub const fn parameters(&self) -> &FormalParameterList {\n        &self.parameters\n    }\n\n    /// Gets the body of the async generator declaration.\n    #[inline]\n    #[must_use]\n    pub const fn body(&self) -> &FunctionBody {\n        &self.body\n    }\n\n    /// Gets the scopes of the async generator declaration.\n    #[inline]\n    #[must_use]\n    pub const fn scopes(&self) -> &FunctionScopes {\n        &self.scopes\n    }\n\n    /// Gets linear span of the function declaration.\n    #[inline]\n    #[must_use]\n    pub const fn linear_span(&self) -> LinearSpan {\n        self.linear_span.0\n    }\n\n    /// Returns `true` if the async generator declaration contains a direct call to `eval`.\n    #[inline]\n    #[must_use]\n    pub const fn contains_direct_eval(&self) -> bool {\n        self.contains_direct_eval\n    }\n}\n\nimpl ToIndentedString for AsyncGeneratorDeclaration {\n    fn to_indented_string(&self, interner: &Interner, indentation: usize) -> String {\n        format!(\n            \"async function* {}({}) {}\",\n            interner.resolve_expect(self.name.sym()),\n            join_nodes(interner, self.parameters.as_ref()),\n            block_to_string(&self.body.statements, interner, indentation)\n        )\n    }\n}\n\nimpl VisitWith for AsyncGeneratorDeclaration {\n    fn visit_with<'a, V>(&'a self, visitor: &mut V) -> ControlFlow<V::BreakTy>\n    where\n        V: Visitor<'a>,\n    {\n        visitor.visit_identifier(&self.name)?;\n        visitor.visit_formal_parameter_list(&self.parameters)?;\n        visitor.visit_function_body(&self.body)\n    }\n\n    fn visit_with_mut<'a, V>(&'a mut self, visitor: &mut V) -> ControlFlow<V::BreakTy>\n    where\n        V: VisitorMut<'a>,\n    {\n        visitor.visit_identifier_mut(&mut self.name)?;\n        visitor.visit_formal_parameter_list_mut(&mut self.parameters)?;\n        visitor.visit_function_body_mut(&mut self.body)\n    }\n}\n\nimpl From<AsyncGeneratorDeclaration> for Declaration {\n    #[inline]\n    fn from(f: AsyncGeneratorDeclaration) -> Self {\n        Self::AsyncGeneratorDeclaration(f)\n    }\n}\n\n/// An async generator expression.\n///\n/// More information:\n///  - [ECMAScript reference][spec]\n///  - [MDN documentation][mdn]\n///\n/// [spec]: https://tc39.es/ecma262/#prod-AsyncGeneratorExpression\n/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/async_function*\n#[cfg_attr(feature = \"serde\", derive(serde::Serialize, serde::Deserialize))]\n#[cfg_attr(feature = \"arbitrary\", derive(arbitrary::Arbitrary))]\n#[derive(Clone, Debug, PartialEq)]\npub struct AsyncGeneratorExpression {\n    pub(crate) name: Option<Identifier>,\n    pub(crate) parameters: FormalParameterList,\n    pub(crate) body: FunctionBody,\n    pub(crate) has_binding_identifier: bool,\n    pub(crate) contains_direct_eval: bool,\n\n    #[cfg_attr(feature = \"serde\", serde(skip))]\n    pub(crate) name_scope: Option<Scope>,\n\n    #[cfg_attr(feature = \"serde\", serde(skip))]\n    pub(crate) scopes: FunctionScopes,\n    linear_span: LinearSpanIgnoreEq,\n\n    span: Span,\n}\n\nimpl AsyncGeneratorExpression {\n    /// Creates a new async generator expression.\n    #[inline]\n    #[must_use]\n    pub fn new(\n        name: Option<Identifier>,\n        parameters: FormalParameterList,\n        body: FunctionBody,\n        linear_span: LinearSpan,\n        has_binding_identifier: bool,\n        span: Span,\n    ) -> Self {\n        let contains_direct_eval = contains(&parameters, ContainsSymbol::DirectEval)\n            || contains(&body, ContainsSymbol::DirectEval);\n        Self {\n            name,\n            parameters,\n            body,\n            has_binding_identifier,\n            name_scope: None,\n            contains_direct_eval,\n            scopes: FunctionScopes::default(),\n            linear_span: linear_span.into(),\n            span,\n        }\n    }\n\n    /// Gets the name of the async generator expression.\n    #[inline]\n    #[must_use]\n    pub const fn name(&self) -> Option<Identifier> {\n        self.name\n    }\n\n    /// Gets the list of parameters of the async generator expression.\n    #[inline]\n    #[must_use]\n    pub const fn parameters(&self) -> &FormalParameterList {\n        &self.parameters\n    }\n\n    /// Gets the body of the async generator expression.\n    #[inline]\n    #[must_use]\n    pub const fn body(&self) -> &FunctionBody {\n        &self.body\n    }\n\n    /// Returns whether the async generator expression has a binding identifier.\n    #[inline]\n    #[must_use]\n    pub const fn has_binding_identifier(&self) -> bool {\n        self.has_binding_identifier\n    }\n\n    /// Gets the name scope of the async generator expression.\n    #[inline]\n    #[must_use]\n    pub const fn name_scope(&self) -> Option<&Scope> {\n        self.name_scope.as_ref()\n    }\n\n    /// Gets the scopes of the async generator expression.\n    #[inline]\n    #[must_use]\n    pub const fn scopes(&self) -> &FunctionScopes {\n        &self.scopes\n    }\n\n    /// Gets linear span of the function declaration.\n    #[inline]\n    #[must_use]\n    pub const fn linear_span(&self) -> LinearSpan {\n        self.linear_span.0\n    }\n\n    /// Returns `true` if the async generator expression contains a direct call to `eval`.\n    #[inline]\n    #[must_use]\n    pub const fn contains_direct_eval(&self) -> bool {\n        self.contains_direct_eval\n    }\n}\n\nimpl Spanned for AsyncGeneratorExpression {\n    #[inline]\n    fn span(&self) -> Span {\n        self.span\n    }\n}\n\nimpl ToIndentedString for AsyncGeneratorExpression {\n    fn to_indented_string(&self, interner: &Interner, indentation: usize) -> String {\n        let mut buf = \"async function*\".to_owned();\n        if self.has_binding_identifier\n            && let Some(name) = self.name\n        {\n            let _ = write!(buf, \" {}\", interner.resolve_expect(name.sym()));\n        }\n        let _ = write!(\n            buf,\n            \"({}) {}\",\n            join_nodes(interner, self.parameters.as_ref()),\n            block_to_string(&self.body.statements, interner, indentation)\n        );\n\n        buf\n    }\n}\n\nimpl From<AsyncGeneratorExpression> for Expression {\n    #[inline]\n    fn from(expr: AsyncGeneratorExpression) -> Self {\n        Self::AsyncGeneratorExpression(expr)\n    }\n}\n\nimpl VisitWith for AsyncGeneratorExpression {\n    fn visit_with<'a, V>(&'a self, visitor: &mut V) -> ControlFlow<V::BreakTy>\n    where\n        V: Visitor<'a>,\n    {\n        if let Some(ident) = &self.name {\n            visitor.visit_identifier(ident)?;\n        }\n        visitor.visit_formal_parameter_list(&self.parameters)?;\n        visitor.visit_function_body(&self.body)\n    }\n\n    fn visit_with_mut<'a, V>(&'a mut self, visitor: &mut V) -> ControlFlow<V::BreakTy>\n    where\n        V: VisitorMut<'a>,\n    {\n        if let Some(ident) = &mut self.name {\n            visitor.visit_identifier_mut(ident)?;\n        }\n        visitor.visit_formal_parameter_list_mut(&mut self.parameters)?;\n        visitor.visit_function_body_mut(&mut self.body)\n    }\n}\n"
  },
  {
    "path": "core/ast/src/function/class.rs",
    "content": "use super::{FormalParameterList, FunctionBody, FunctionExpression};\nuse crate::{\n    Declaration, LinearPosition, LinearSpan, LinearSpanIgnoreEq, Span, Spanned, block_to_string,\n    expression::{Expression, Identifier},\n    join_nodes,\n    operations::{ContainsSymbol, contains},\n    property::{MethodDefinitionKind, PropertyName},\n    scope::{FunctionScopes, Scope},\n    visitor::{VisitWith, Visitor, VisitorMut},\n};\nuse boa_interner::{Interner, Sym, ToIndentedString, ToInternedString};\nuse core::{fmt::Write as _, ops::ControlFlow};\nuse std::hash::Hash;\n\n/// A class declaration.\n///\n/// More information:\n///  - [ECMAScript reference][spec]\n///  - [MDN documentation][mdn]\n///\n/// [spec]: https://tc39.es/ecma262/#prod-ClassDeclaration\n/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/class\n#[cfg_attr(feature = \"serde\", derive(serde::Serialize, serde::Deserialize))]\n#[cfg_attr(feature = \"arbitrary\", derive(arbitrary::Arbitrary))]\n#[derive(Clone, Debug, PartialEq)]\npub struct ClassDeclaration {\n    name: Identifier,\n    pub(crate) super_ref: Option<Expression>,\n    pub(crate) constructor: Option<FunctionExpression>,\n    pub(crate) elements: Box<[ClassElement]>,\n\n    #[cfg_attr(feature = \"serde\", serde(skip))]\n    pub(crate) name_scope: Scope,\n}\n\nimpl ClassDeclaration {\n    /// Creates a new class declaration.\n    #[inline]\n    #[must_use]\n    pub fn new(\n        name: Identifier,\n        super_ref: Option<Expression>,\n        constructor: Option<FunctionExpression>,\n        elements: Box<[ClassElement]>,\n    ) -> Self {\n        Self {\n            name,\n            super_ref,\n            constructor,\n            elements,\n            name_scope: Scope::default(),\n        }\n    }\n\n    /// Returns the name of the class declaration.\n    #[inline]\n    #[must_use]\n    pub const fn name(&self) -> Identifier {\n        self.name\n    }\n\n    /// Returns the super class ref of the class declaration.\n    #[inline]\n    #[must_use]\n    pub const fn super_ref(&self) -> Option<&Expression> {\n        self.super_ref.as_ref()\n    }\n\n    /// Returns the constructor of the class declaration.\n    #[inline]\n    #[must_use]\n    pub const fn constructor(&self) -> Option<&FunctionExpression> {\n        self.constructor.as_ref()\n    }\n\n    /// Gets the list of all fields defined on the class declaration.\n    #[inline]\n    #[must_use]\n    pub const fn elements(&self) -> &[ClassElement] {\n        &self.elements\n    }\n\n    /// Gets the scope containing the class name binding.\n    #[inline]\n    #[must_use]\n    pub const fn name_scope(&self) -> &Scope {\n        &self.name_scope\n    }\n}\n\nimpl ToIndentedString for ClassDeclaration {\n    fn to_indented_string(&self, interner: &Interner, indent_n: usize) -> String {\n        let mut buf = format!(\"class {}\", interner.resolve_expect(self.name.sym()));\n        if let Some(super_ref) = self.super_ref.as_ref() {\n            let _ = write!(buf, \" extends {}\", super_ref.to_interned_string(interner));\n        }\n        if self.elements.is_empty() && self.constructor().is_none() {\n            buf.push_str(\" {}\");\n            return buf;\n        }\n        let indentation = \"    \".repeat(indent_n + 1);\n        buf.push_str(\" {\\n\");\n        if let Some(expr) = &self.constructor {\n            let _ = writeln!(\n                buf,\n                \"{indentation}constructor({}) {}\",\n                join_nodes(interner, expr.parameters().as_ref()),\n                block_to_string(&expr.body.statements, interner, indent_n + 1)\n            );\n        }\n        for element in &self.elements {\n            buf.push_str(&element.to_indented_string(interner, indent_n));\n        }\n        buf.push('}');\n        buf\n    }\n}\n\nimpl VisitWith for ClassDeclaration {\n    fn visit_with<'a, V>(&'a self, visitor: &mut V) -> ControlFlow<V::BreakTy>\n    where\n        V: Visitor<'a>,\n    {\n        visitor.visit_identifier(&self.name)?;\n        if let Some(expr) = &self.super_ref {\n            visitor.visit_expression(expr)?;\n        }\n        if let Some(func) = &self.constructor {\n            visitor.visit_function_expression(func)?;\n        }\n        for elem in &*self.elements {\n            visitor.visit_class_element(elem)?;\n        }\n        ControlFlow::Continue(())\n    }\n\n    fn visit_with_mut<'a, V>(&'a mut self, visitor: &mut V) -> ControlFlow<V::BreakTy>\n    where\n        V: VisitorMut<'a>,\n    {\n        visitor.visit_identifier_mut(&mut self.name)?;\n        if let Some(expr) = &mut self.super_ref {\n            visitor.visit_expression_mut(expr)?;\n        }\n        if let Some(func) = &mut self.constructor {\n            visitor.visit_function_expression_mut(func)?;\n        }\n        for elem in &mut *self.elements {\n            visitor.visit_class_element_mut(elem)?;\n        }\n        ControlFlow::Continue(())\n    }\n}\n\nimpl From<ClassDeclaration> for Declaration {\n    fn from(f: ClassDeclaration) -> Self {\n        Self::ClassDeclaration(Box::new(f))\n    }\n}\n\n/// A class expression.\n///\n/// More information:\n///  - [ECMAScript reference][spec]\n///  - [MDN documentation][mdn]\n///\n/// [spec]: https://tc39.es/ecma262/#prod-ClassExpression\n/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/class\n#[cfg_attr(feature = \"serde\", derive(serde::Serialize, serde::Deserialize))]\n#[cfg_attr(feature = \"arbitrary\", derive(arbitrary::Arbitrary))]\n#[derive(Clone, Debug, PartialEq)]\npub struct ClassExpression {\n    pub(crate) name: Option<Identifier>,\n    pub(crate) super_ref: Option<Expression>,\n    pub(crate) constructor: Option<FunctionExpression>,\n    pub(crate) elements: Box<[ClassElement]>,\n\n    span: Span,\n\n    #[cfg_attr(feature = \"serde\", serde(skip))]\n    pub(crate) name_scope: Option<Scope>,\n}\n\nimpl ClassExpression {\n    /// Creates a new class expression.\n    #[inline]\n    #[must_use]\n    pub fn new(\n        name: Option<Identifier>,\n        super_ref: Option<Expression>,\n        constructor: Option<FunctionExpression>,\n        elements: Box<[ClassElement]>,\n        has_binding_identifier: bool,\n        span: Span,\n    ) -> Self {\n        let name_scope = if has_binding_identifier {\n            Some(Scope::default())\n        } else {\n            None\n        };\n        Self {\n            name,\n            super_ref,\n            constructor,\n            elements,\n            span,\n            name_scope,\n        }\n    }\n\n    /// Returns the name of the class expression.\n    #[inline]\n    #[must_use]\n    pub const fn name(&self) -> Option<Identifier> {\n        self.name\n    }\n\n    /// Returns the super class ref of the class expression.\n    #[inline]\n    #[must_use]\n    pub const fn super_ref(&self) -> Option<&Expression> {\n        self.super_ref.as_ref()\n    }\n\n    /// Returns the constructor of the class expression.\n    #[inline]\n    #[must_use]\n    pub const fn constructor(&self) -> Option<&FunctionExpression> {\n        self.constructor.as_ref()\n    }\n\n    /// Gets the list of all fields defined on the class expression.\n    #[inline]\n    #[must_use]\n    pub const fn elements(&self) -> &[ClassElement] {\n        &self.elements\n    }\n\n    /// Gets the scope containing the class name binding if it exists.\n    #[inline]\n    #[must_use]\n    pub const fn name_scope(&self) -> Option<&Scope> {\n        self.name_scope.as_ref()\n    }\n}\n\nimpl Spanned for ClassExpression {\n    #[inline]\n    fn span(&self) -> Span {\n        self.span\n    }\n}\n\nimpl ToIndentedString for ClassExpression {\n    fn to_indented_string(&self, interner: &Interner, indent_n: usize) -> String {\n        let mut buf = \"class\".to_string();\n        if self.name_scope.is_some()\n            && let Some(name) = self.name\n        {\n            let _ = write!(buf, \" {}\", interner.resolve_expect(name.sym()));\n        }\n        if let Some(super_ref) = self.super_ref.as_ref() {\n            let _ = write!(buf, \" extends {}\", super_ref.to_interned_string(interner));\n        }\n        if self.elements.is_empty() && self.constructor().is_none() {\n            buf.push_str(\" {}\");\n            return buf;\n        }\n        let indentation = \"    \".repeat(indent_n + 1);\n        buf.push_str(\" {\\n\");\n        if let Some(expr) = &self.constructor {\n            let _ = writeln!(\n                buf,\n                \"{indentation}constructor({}) {}\",\n                join_nodes(interner, expr.parameters().as_ref()),\n                block_to_string(&expr.body.statements, interner, indent_n + 1)\n            );\n        }\n        for element in &self.elements {\n            buf.push_str(&element.to_indented_string(interner, indent_n));\n        }\n        buf.push('}');\n        buf\n    }\n}\n\nimpl From<ClassExpression> for Expression {\n    fn from(expr: ClassExpression) -> Self {\n        Self::ClassExpression(Box::new(expr))\n    }\n}\n\nimpl VisitWith for ClassExpression {\n    fn visit_with<'a, V>(&'a self, visitor: &mut V) -> ControlFlow<V::BreakTy>\n    where\n        V: Visitor<'a>,\n    {\n        if let Some(ident) = &self.name {\n            visitor.visit_identifier(ident)?;\n        }\n        if let Some(expr) = &self.super_ref {\n            visitor.visit_expression(expr)?;\n        }\n        if let Some(func) = &self.constructor {\n            visitor.visit_function_expression(func)?;\n        }\n        for elem in &*self.elements {\n            visitor.visit_class_element(elem)?;\n        }\n        ControlFlow::Continue(())\n    }\n\n    fn visit_with_mut<'a, V>(&'a mut self, visitor: &mut V) -> ControlFlow<V::BreakTy>\n    where\n        V: VisitorMut<'a>,\n    {\n        if let Some(ident) = &mut self.name {\n            visitor.visit_identifier_mut(ident)?;\n        }\n        if let Some(expr) = &mut self.super_ref {\n            visitor.visit_expression_mut(expr)?;\n        }\n        if let Some(func) = &mut self.constructor {\n            visitor.visit_function_expression_mut(func)?;\n        }\n        for elem in &mut *self.elements {\n            visitor.visit_class_element_mut(elem)?;\n        }\n        ControlFlow::Continue(())\n    }\n}\n\n/// The body of a class' static block, as defined by the [spec].\n///\n/// Just an alias for [`Script`](crate::Script), since it has the same exact semantics.\n///\n/// [spec]: https://tc39.es/ecma262/#prod-ClassStaticBlockBody\n#[cfg_attr(feature = \"serde\", derive(serde::Serialize, serde::Deserialize))]\n#[cfg_attr(feature = \"arbitrary\", derive(arbitrary::Arbitrary))]\n#[derive(Clone, Debug, PartialEq)]\npub struct StaticBlockBody {\n    pub(crate) body: FunctionBody,\n\n    #[cfg_attr(feature = \"serde\", serde(skip))]\n    pub(crate) scopes: FunctionScopes,\n}\n\nimpl StaticBlockBody {\n    /// Creates a new static block body.\n    #[inline]\n    #[must_use]\n    pub fn new(body: FunctionBody) -> Self {\n        Self {\n            body,\n            scopes: FunctionScopes::default(),\n        }\n    }\n\n    /// Gets the body static block.\n    #[inline]\n    #[must_use]\n    pub const fn statements(&self) -> &FunctionBody {\n        &self.body\n    }\n\n    /// Gets the scopes of the static block body.\n    #[inline]\n    #[must_use]\n    pub const fn scopes(&self) -> &FunctionScopes {\n        &self.scopes\n    }\n}\n\n/// An element that can be within a class.\n///\n/// More information:\n///  - [ECMAScript reference][spec]\n///\n/// [spec]: https://tc39.es/ecma262/#prod-ClassElement\n#[cfg_attr(feature = \"serde\", derive(serde::Serialize, serde::Deserialize))]\n#[cfg_attr(feature = \"arbitrary\", derive(arbitrary::Arbitrary))]\n#[derive(Clone, Debug, PartialEq)]\npub enum ClassElement {\n    /// A method definition.\n    MethodDefinition(ClassMethodDefinition),\n\n    /// A field definition.\n    FieldDefinition(ClassFieldDefinition),\n\n    /// A static field definition, accessible from the class constructor object\n    StaticFieldDefinition(ClassFieldDefinition),\n\n    /// A private field definition, only accessible inside the class declaration.\n    PrivateFieldDefinition(PrivateFieldDefinition),\n\n    /// A private static field definition, only accessible from static methods and fields inside the\n    /// class declaration.\n    PrivateStaticFieldDefinition(PrivateFieldDefinition),\n\n    /// A static block, where a class can have initialization logic for its static fields.\n    StaticBlock(StaticBlockBody),\n}\n\n/// A non-private class element field definition.\n///\n/// More information:\n///  - [ECMAScript reference][spec]\n///\n/// [spec]: https://tc39.es/ecma262/#prod-FieldDefinition\n#[cfg_attr(feature = \"serde\", derive(serde::Serialize, serde::Deserialize))]\n#[cfg_attr(feature = \"arbitrary\", derive(arbitrary::Arbitrary))]\n#[derive(Clone, Debug, PartialEq)]\npub struct ClassFieldDefinition {\n    pub(crate) name: PropertyName,\n    pub(crate) initializer: Option<Expression>,\n\n    #[cfg_attr(feature = \"serde\", serde(skip))]\n    pub(crate) scope: Scope,\n}\n\nimpl ClassFieldDefinition {\n    /// Creates a new class field definition.\n    #[inline]\n    #[must_use]\n    pub fn new(name: PropertyName, initializer: Option<Expression>) -> Self {\n        Self {\n            name,\n            initializer,\n            scope: Scope::default(),\n        }\n    }\n\n    /// Returns the name of the class field definition.\n    #[inline]\n    #[must_use]\n    pub const fn name(&self) -> &PropertyName {\n        &self.name\n    }\n\n    /// Returns the initializer of the class field definition.\n    #[inline]\n    #[must_use]\n    pub const fn initializer(&self) -> Option<&Expression> {\n        self.initializer.as_ref()\n    }\n\n    /// Returns the scope of the class field definition.\n    #[inline]\n    #[must_use]\n    pub const fn scope(&self) -> &Scope {\n        &self.scope\n    }\n}\n\n/// A private class element field definition.\n///\n/// More information:\n///  - [ECMAScript reference][spec]\n///\n/// [spec]: https://tc39.es/ecma262/#prod-FieldDefinition\n#[cfg_attr(feature = \"serde\", derive(serde::Serialize, serde::Deserialize))]\n#[cfg_attr(feature = \"arbitrary\", derive(arbitrary::Arbitrary))]\n#[derive(Clone, Debug, PartialEq)]\npub struct PrivateFieldDefinition {\n    pub(crate) name: PrivateName,\n    pub(crate) initializer: Option<Expression>,\n\n    #[cfg_attr(feature = \"serde\", serde(skip))]\n    pub(crate) scope: Scope,\n}\n\nimpl PrivateFieldDefinition {\n    /// Creates a new private field definition.\n    #[inline]\n    #[must_use]\n    pub fn new(name: PrivateName, initializer: Option<Expression>) -> Self {\n        Self {\n            name,\n            initializer,\n            scope: Scope::default(),\n        }\n    }\n\n    /// Returns the name of the private field definition.\n    #[inline]\n    #[must_use]\n    pub const fn name(&self) -> &PrivateName {\n        &self.name\n    }\n\n    /// Returns the initializer of the private field definition.\n    #[inline]\n    #[must_use]\n    pub const fn initializer(&self) -> Option<&Expression> {\n        self.initializer.as_ref()\n    }\n\n    /// Returns the scope of the private field definition.\n    #[inline]\n    #[must_use]\n    pub const fn scope(&self) -> &Scope {\n        &self.scope\n    }\n}\n\nimpl ToIndentedString for ClassElement {\n    fn to_indented_string(&self, interner: &Interner, indent_n: usize) -> String {\n        let indentation = \"    \".repeat(indent_n + 1);\n        match self {\n            Self::MethodDefinition(m) => m.to_indented_string(interner, indent_n),\n            Self::FieldDefinition(field) => match &field.initializer {\n                Some(expr) => {\n                    format!(\n                        \"{indentation}{} = {};\\n\",\n                        field.name.to_interned_string(interner),\n                        expr.to_no_indent_string(interner, indent_n + 1)\n                    )\n                }\n                None => {\n                    format!(\n                        \"{indentation}{};\\n\",\n                        field.name.to_interned_string(interner),\n                    )\n                }\n            },\n            Self::StaticFieldDefinition(field) => match &field.initializer {\n                Some(expr) => {\n                    format!(\n                        \"{indentation}static {} = {};\\n\",\n                        field.name.to_interned_string(interner),\n                        expr.to_no_indent_string(interner, indent_n + 1)\n                    )\n                }\n                None => {\n                    format!(\n                        \"{indentation}static {};\\n\",\n                        field.name.to_interned_string(interner),\n                    )\n                }\n            },\n            Self::PrivateFieldDefinition(PrivateFieldDefinition {\n                name, initializer, ..\n            }) => match initializer {\n                Some(expr) => {\n                    format!(\n                        \"{indentation}#{} = {};\\n\",\n                        interner.resolve_expect(name.description()),\n                        expr.to_no_indent_string(interner, indent_n + 1)\n                    )\n                }\n                None => {\n                    format!(\n                        \"{indentation}#{};\\n\",\n                        interner.resolve_expect(name.description()),\n                    )\n                }\n            },\n            Self::PrivateStaticFieldDefinition(PrivateFieldDefinition {\n                name,\n                initializer,\n                ..\n            }) => match initializer {\n                Some(expr) => {\n                    format!(\n                        \"{indentation}static #{} = {};\\n\",\n                        interner.resolve_expect(name.description()),\n                        expr.to_no_indent_string(interner, indent_n + 1)\n                    )\n                }\n                None => {\n                    format!(\n                        \"{indentation}static #{};\\n\",\n                        interner.resolve_expect(name.description()),\n                    )\n                }\n            },\n            Self::StaticBlock(block) => {\n                format!(\n                    \"{indentation}static {}\\n\",\n                    block_to_string(&block.body.statements, interner, indent_n + 1)\n                )\n            }\n        }\n    }\n}\n\nimpl VisitWith for ClassElement {\n    fn visit_with<'a, V>(&'a self, visitor: &mut V) -> ControlFlow<V::BreakTy>\n    where\n        V: Visitor<'a>,\n    {\n        match self {\n            Self::MethodDefinition(m) => {\n                match &m.name {\n                    ClassElementName::PropertyName(pn) => {\n                        visitor.visit_property_name(pn)?;\n                    }\n                    ClassElementName::PrivateName(pn) => {\n                        visitor.visit_private_name(pn)?;\n                    }\n                }\n                visitor.visit_formal_parameter_list(&m.parameters)?;\n                visitor.visit_function_body(&m.body)\n            }\n            Self::FieldDefinition(field) | Self::StaticFieldDefinition(field) => {\n                visitor.visit_property_name(&field.name)?;\n                if let Some(expr) = &field.initializer {\n                    visitor.visit_expression(expr)\n                } else {\n                    ControlFlow::Continue(())\n                }\n            }\n            Self::PrivateFieldDefinition(PrivateFieldDefinition {\n                name, initializer, ..\n            })\n            | Self::PrivateStaticFieldDefinition(PrivateFieldDefinition {\n                name,\n                initializer,\n                ..\n            }) => {\n                visitor.visit_private_name(name)?;\n                if let Some(expr) = initializer {\n                    visitor.visit_expression(expr)\n                } else {\n                    ControlFlow::Continue(())\n                }\n            }\n            Self::StaticBlock(block) => visitor.visit_function_body(&block.body),\n        }\n    }\n\n    fn visit_with_mut<'a, V>(&'a mut self, visitor: &mut V) -> ControlFlow<V::BreakTy>\n    where\n        V: VisitorMut<'a>,\n    {\n        match self {\n            Self::MethodDefinition(m) => {\n                match m.name {\n                    ClassElementName::PropertyName(ref mut pn) => {\n                        visitor.visit_property_name_mut(pn)?;\n                    }\n                    ClassElementName::PrivateName(ref mut pn) => {\n                        visitor.visit_private_name_mut(pn)?;\n                    }\n                }\n                visitor.visit_formal_parameter_list_mut(&mut m.parameters)?;\n                visitor.visit_function_body_mut(&mut m.body)\n            }\n            Self::FieldDefinition(field) | Self::StaticFieldDefinition(field) => {\n                visitor.visit_property_name_mut(&mut field.name)?;\n                if let Some(expr) = &mut field.initializer {\n                    visitor.visit_expression_mut(expr)\n                } else {\n                    ControlFlow::Continue(())\n                }\n            }\n            Self::PrivateFieldDefinition(PrivateFieldDefinition {\n                name, initializer, ..\n            })\n            | Self::PrivateStaticFieldDefinition(PrivateFieldDefinition {\n                name,\n                initializer,\n                ..\n            }) => {\n                visitor.visit_private_name_mut(name)?;\n                if let Some(expr) = initializer {\n                    visitor.visit_expression_mut(expr)\n                } else {\n                    ControlFlow::Continue(())\n                }\n            }\n            Self::StaticBlock(block) => visitor.visit_function_body_mut(&mut block.body),\n        }\n    }\n}\n\n/// A method definition.\n///\n/// This type is specific to class method definitions.\n/// It includes private names and the information about whether the method is static or not.\n///\n/// More information:\n///  - [ECMAScript reference][spec]\n///\n/// [spec]: https://tc39.es/ecma262/#prod-MethodDefinition\n#[cfg_attr(feature = \"serde\", derive(serde::Serialize, serde::Deserialize))]\n#[cfg_attr(feature = \"arbitrary\", derive(arbitrary::Arbitrary))]\n#[derive(Clone, Debug, PartialEq)]\npub struct ClassMethodDefinition {\n    name: ClassElementName,\n    pub(crate) parameters: FormalParameterList,\n    pub(crate) body: FunctionBody,\n    pub(crate) contains_direct_eval: bool,\n    kind: MethodDefinitionKind,\n    is_static: bool,\n\n    #[cfg_attr(feature = \"serde\", serde(skip))]\n    pub(crate) scopes: FunctionScopes,\n    linear_span: LinearSpanIgnoreEq,\n}\n\nimpl ClassMethodDefinition {\n    /// Creates a new class method definition.\n    #[inline]\n    #[must_use]\n    pub fn new(\n        name: ClassElementName,\n        parameters: FormalParameterList,\n        body: FunctionBody,\n        kind: MethodDefinitionKind,\n        is_static: bool,\n        start_linear_pos: LinearPosition,\n    ) -> Self {\n        let contains_direct_eval = contains(&parameters, ContainsSymbol::DirectEval)\n            || contains(&body, ContainsSymbol::DirectEval);\n\n        let linear_span = LinearSpan::new(start_linear_pos, body.linear_pos_end());\n\n        Self {\n            name,\n            parameters,\n            body,\n            contains_direct_eval,\n            kind,\n            is_static,\n            scopes: FunctionScopes::default(),\n            linear_span: linear_span.into(),\n        }\n    }\n\n    /// Returns the name of the class method definition.\n    #[inline]\n    #[must_use]\n    pub const fn name(&self) -> &ClassElementName {\n        &self.name\n    }\n\n    /// Returns the parameters of the class method definition.\n    #[inline]\n    #[must_use]\n    pub const fn parameters(&self) -> &FormalParameterList {\n        &self.parameters\n    }\n\n    /// Returns the body of the class method definition.\n    #[inline]\n    #[must_use]\n    pub const fn body(&self) -> &FunctionBody {\n        &self.body\n    }\n\n    /// Returns the kind of the class method definition.\n    #[inline]\n    #[must_use]\n    pub const fn kind(&self) -> MethodDefinitionKind {\n        self.kind\n    }\n\n    /// Returns whether the class method definition is static.\n    #[inline]\n    #[must_use]\n    pub const fn is_static(&self) -> bool {\n        self.is_static\n    }\n\n    /// Returns whether the class method definition is private.\n    #[inline]\n    #[must_use]\n    pub const fn is_private(&self) -> bool {\n        self.name.is_private()\n    }\n\n    /// Gets the scopes of the class method definition.\n    #[inline]\n    #[must_use]\n    pub const fn scopes(&self) -> &FunctionScopes {\n        &self.scopes\n    }\n\n    /// Gets linear span of the function declaration.\n    #[inline]\n    #[must_use]\n    pub const fn linear_span(&self) -> LinearSpan {\n        self.linear_span.0\n    }\n\n    /// Returns `true` if the class method definition contains a direct call to `eval`.\n    #[inline]\n    #[must_use]\n    pub const fn contains_direct_eval(&self) -> bool {\n        self.contains_direct_eval\n    }\n}\n\nimpl ToIndentedString for ClassMethodDefinition {\n    fn to_indented_string(&self, interner: &Interner, indent_n: usize) -> String {\n        let indentation = \"    \".repeat(indent_n + 1);\n        let prefix = match (self.is_static, &self.kind) {\n            (true, MethodDefinitionKind::Get) => \"static get \",\n            (true, MethodDefinitionKind::Set) => \"static set \",\n            (true, MethodDefinitionKind::Ordinary) => \"static \",\n            (true, MethodDefinitionKind::Generator) => \"static *\",\n            (true, MethodDefinitionKind::AsyncGenerator) => \"static async *\",\n            (true, MethodDefinitionKind::Async) => \"static async \",\n            (false, MethodDefinitionKind::Get) => \"get \",\n            (false, MethodDefinitionKind::Set) => \"set \",\n            (false, MethodDefinitionKind::Ordinary) => \"\",\n            (false, MethodDefinitionKind::Generator) => \"*\",\n            (false, MethodDefinitionKind::AsyncGenerator) => \"async *\",\n            (false, MethodDefinitionKind::Async) => \"async \",\n        };\n        let name = self.name.to_interned_string(interner);\n        let parameters = join_nodes(interner, self.parameters.as_ref());\n        let body = block_to_string(&self.body.statements, interner, indent_n + 1);\n        format!(\"{indentation}{prefix}{name}({parameters}) {body}\\n\")\n    }\n}\n\n/// A class element name.\n///\n/// More information:\n///  - [ECMAScript reference][spec]\n///\n/// [spec]: https://tc39.es/ecma262/#prod-ClassElementName\n#[cfg_attr(feature = \"serde\", derive(serde::Serialize, serde::Deserialize))]\n#[cfg_attr(feature = \"arbitrary\", derive(arbitrary::Arbitrary))]\n#[derive(Clone, Debug, PartialEq)]\npub enum ClassElementName {\n    /// A property name.\n    PropertyName(PropertyName),\n\n    /// A private name.\n    PrivateName(PrivateName),\n}\n\nimpl ClassElementName {\n    /// Returns whether the class element name is private.\n    #[inline]\n    #[must_use]\n    pub const fn is_private(&self) -> bool {\n        matches!(self, Self::PrivateName(_))\n    }\n}\n\nimpl ToInternedString for ClassElementName {\n    fn to_interned_string(&self, interner: &Interner) -> String {\n        match &self {\n            Self::PropertyName(name) => name.to_interned_string(interner),\n            Self::PrivateName(name) => format!(\"#{}\", interner.resolve_expect(name.description())),\n        }\n    }\n}\n\n/// A private name as defined by the [spec].\n///\n/// [spec]: https://tc39.es/ecma262/#sec-private-names\n#[cfg_attr(feature = \"serde\", derive(serde::Serialize, serde::Deserialize))]\n#[cfg_attr(feature = \"arbitrary\", derive(arbitrary::Arbitrary))]\n#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]\npub struct PrivateName {\n    /// The `[[Description]]` internal slot of the private name.\n    description: Sym,\n    span: Span,\n}\n\nimpl PrivateName {\n    /// Create a new private name.\n    #[inline]\n    #[must_use]\n    pub const fn new(description: Sym, span: Span) -> Self {\n        Self { description, span }\n    }\n\n    /// Get the description of the private name.\n    #[inline]\n    #[must_use]\n    pub const fn description(&self) -> Sym {\n        self.description\n    }\n}\n\nimpl Spanned for PrivateName {\n    #[inline]\n    fn span(&self) -> Span {\n        self.span\n    }\n}\n\nimpl VisitWith for PrivateName {\n    fn visit_with<'a, V>(&'a self, visitor: &mut V) -> ControlFlow<V::BreakTy>\n    where\n        V: Visitor<'a>,\n    {\n        visitor.visit_sym(&self.description)\n    }\n\n    fn visit_with_mut<'a, V>(&'a mut self, visitor: &mut V) -> ControlFlow<V::BreakTy>\n    where\n        V: VisitorMut<'a>,\n    {\n        visitor.visit_sym_mut(&mut self.description)\n    }\n}\n"
  },
  {
    "path": "core/ast/src/function/generator.rs",
    "content": "use super::{FormalParameterList, FunctionBody};\nuse crate::{\n    Declaration, LinearSpan, LinearSpanIgnoreEq, Span, Spanned, block_to_string,\n    expression::{Expression, Identifier},\n    join_nodes,\n    operations::{ContainsSymbol, contains},\n    scope::{FunctionScopes, Scope},\n    visitor::{VisitWith, Visitor, VisitorMut},\n};\nuse boa_interner::{Interner, ToIndentedString};\nuse core::{fmt::Write as _, ops::ControlFlow};\n\n/// A generator declaration.\n///\n/// More information:\n///  - [ECMAScript reference][spec]\n///  - [MDN documentation][mdn]\n///\n/// [spec]: https://tc39.es/ecma262/#prod-GeneratorDeclaration\n/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/function*\n#[cfg_attr(feature = \"serde\", derive(serde::Serialize, serde::Deserialize))]\n#[cfg_attr(feature = \"arbitrary\", derive(arbitrary::Arbitrary))]\n#[derive(Clone, Debug, PartialEq)]\npub struct GeneratorDeclaration {\n    name: Identifier,\n    pub(crate) parameters: FormalParameterList,\n    pub(crate) body: FunctionBody,\n    pub(crate) contains_direct_eval: bool,\n\n    #[cfg_attr(feature = \"serde\", serde(skip))]\n    pub(crate) scopes: FunctionScopes,\n    linear_span: LinearSpanIgnoreEq,\n}\n\nimpl GeneratorDeclaration {\n    /// Creates a new generator declaration.\n    #[inline]\n    #[must_use]\n    pub fn new(\n        name: Identifier,\n        parameters: FormalParameterList,\n        body: FunctionBody,\n        linear_span: LinearSpan,\n    ) -> Self {\n        let contains_direct_eval = contains(&parameters, ContainsSymbol::DirectEval)\n            || contains(&body, ContainsSymbol::DirectEval);\n        Self {\n            name,\n            parameters,\n            body,\n            contains_direct_eval,\n            scopes: FunctionScopes::default(),\n            linear_span: linear_span.into(),\n        }\n    }\n\n    /// Gets the name of the generator declaration.\n    #[inline]\n    #[must_use]\n    pub const fn name(&self) -> Identifier {\n        self.name\n    }\n\n    /// Gets the list of parameters of the generator declaration.\n    #[inline]\n    #[must_use]\n    pub const fn parameters(&self) -> &FormalParameterList {\n        &self.parameters\n    }\n\n    /// Gets the body of the generator declaration.\n    #[inline]\n    #[must_use]\n    pub const fn body(&self) -> &FunctionBody {\n        &self.body\n    }\n\n    /// Returns the scopes of the generator declaration.\n    #[inline]\n    #[must_use]\n    pub const fn scopes(&self) -> &FunctionScopes {\n        &self.scopes\n    }\n\n    /// Gets linear span of the function declaration.\n    #[inline]\n    #[must_use]\n    pub const fn linear_span(&self) -> LinearSpan {\n        self.linear_span.0\n    }\n\n    /// Returns `true` if the generator declaration contains a direct call to `eval`.\n    #[inline]\n    #[must_use]\n    pub const fn contains_direct_eval(&self) -> bool {\n        self.contains_direct_eval\n    }\n}\n\nimpl ToIndentedString for GeneratorDeclaration {\n    fn to_indented_string(&self, interner: &Interner, indentation: usize) -> String {\n        format!(\n            \"function* {}({}) {}\",\n            interner.resolve_expect(self.name.sym()),\n            join_nodes(interner, self.parameters.as_ref()),\n            block_to_string(&self.body.statements, interner, indentation)\n        )\n    }\n}\n\nimpl VisitWith for GeneratorDeclaration {\n    fn visit_with<'a, V>(&'a self, visitor: &mut V) -> ControlFlow<V::BreakTy>\n    where\n        V: Visitor<'a>,\n    {\n        visitor.visit_identifier(&self.name)?;\n        visitor.visit_formal_parameter_list(&self.parameters)?;\n        visitor.visit_function_body(&self.body)\n    }\n\n    fn visit_with_mut<'a, V>(&'a mut self, visitor: &mut V) -> ControlFlow<V::BreakTy>\n    where\n        V: VisitorMut<'a>,\n    {\n        visitor.visit_identifier_mut(&mut self.name)?;\n        visitor.visit_formal_parameter_list_mut(&mut self.parameters)?;\n        visitor.visit_function_body_mut(&mut self.body)\n    }\n}\n\nimpl From<GeneratorDeclaration> for Declaration {\n    #[inline]\n    fn from(f: GeneratorDeclaration) -> Self {\n        Self::GeneratorDeclaration(f)\n    }\n}\n\n/// A generator expression.\n///\n/// More information:\n///  - [ECMAScript reference][spec]\n///  - [MDN documentation][mdn]\n///\n/// [spec]: https://tc39.es/ecma262/#prod-GeneratorExpression\n/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/function*\n#[cfg_attr(feature = \"serde\", derive(serde::Serialize, serde::Deserialize))]\n#[cfg_attr(feature = \"arbitrary\", derive(arbitrary::Arbitrary))]\n#[derive(Clone, Debug, PartialEq)]\npub struct GeneratorExpression {\n    pub(crate) name: Option<Identifier>,\n    pub(crate) parameters: FormalParameterList,\n    pub(crate) body: FunctionBody,\n    pub(crate) has_binding_identifier: bool,\n    pub(crate) contains_direct_eval: bool,\n\n    #[cfg_attr(feature = \"serde\", serde(skip))]\n    pub(crate) name_scope: Option<Scope>,\n\n    #[cfg_attr(feature = \"serde\", serde(skip))]\n    pub(crate) scopes: FunctionScopes,\n    linear_span: LinearSpanIgnoreEq,\n\n    span: Span,\n}\n\nimpl GeneratorExpression {\n    /// Creates a new generator expression.\n    #[inline]\n    #[must_use]\n    pub fn new(\n        name: Option<Identifier>,\n        parameters: FormalParameterList,\n        body: FunctionBody,\n        linear_span: LinearSpan,\n        has_binding_identifier: bool,\n        span: Span,\n    ) -> Self {\n        let contains_direct_eval = contains(&parameters, ContainsSymbol::DirectEval)\n            || contains(&body, ContainsSymbol::DirectEval);\n        Self {\n            name,\n            parameters,\n            body,\n            has_binding_identifier,\n            name_scope: None,\n            contains_direct_eval,\n            scopes: FunctionScopes::default(),\n            linear_span: linear_span.into(),\n            span,\n        }\n    }\n\n    /// Gets the name of the generator expression.\n    #[inline]\n    #[must_use]\n    pub const fn name(&self) -> Option<Identifier> {\n        self.name\n    }\n\n    /// Gets the list of parameters of the generator expression.\n    #[inline]\n    #[must_use]\n    pub const fn parameters(&self) -> &FormalParameterList {\n        &self.parameters\n    }\n\n    /// Gets the body of the generator expression.\n    #[inline]\n    #[must_use]\n    pub const fn body(&self) -> &FunctionBody {\n        &self.body\n    }\n\n    /// Returns whether the generator expression has a binding identifier.\n    #[inline]\n    #[must_use]\n    pub const fn has_binding_identifier(&self) -> bool {\n        self.has_binding_identifier\n    }\n\n    /// Gets the name scope of the generator expression.\n    #[inline]\n    #[must_use]\n    pub const fn name_scope(&self) -> Option<&Scope> {\n        self.name_scope.as_ref()\n    }\n\n    /// Gets the scopes of the generator expression.\n    #[inline]\n    #[must_use]\n    pub const fn scopes(&self) -> &FunctionScopes {\n        &self.scopes\n    }\n\n    /// Gets linear span of the function declaration.\n    #[inline]\n    #[must_use]\n    pub const fn linear_span(&self) -> LinearSpan {\n        self.linear_span.0\n    }\n\n    /// Returns `true` if the generator expression contains a direct call to `eval`.\n    #[inline]\n    #[must_use]\n    pub const fn contains_direct_eval(&self) -> bool {\n        self.contains_direct_eval\n    }\n}\n\nimpl Spanned for GeneratorExpression {\n    #[inline]\n    fn span(&self) -> Span {\n        self.span\n    }\n}\n\nimpl ToIndentedString for GeneratorExpression {\n    fn to_indented_string(&self, interner: &Interner, indentation: usize) -> String {\n        let mut buf = \"function*\".to_owned();\n        if self.has_binding_identifier\n            && let Some(name) = self.name\n        {\n            let _ = write!(buf, \" {}\", interner.resolve_expect(name.sym()));\n        }\n        let _ = write!(\n            buf,\n            \"({}) {}\",\n            join_nodes(interner, self.parameters.as_ref()),\n            block_to_string(&self.body.statements, interner, indentation)\n        );\n\n        buf\n    }\n}\n\nimpl From<GeneratorExpression> for Expression {\n    #[inline]\n    fn from(expr: GeneratorExpression) -> Self {\n        Self::GeneratorExpression(expr)\n    }\n}\n\nimpl VisitWith for GeneratorExpression {\n    fn visit_with<'a, V>(&'a self, visitor: &mut V) -> ControlFlow<V::BreakTy>\n    where\n        V: Visitor<'a>,\n    {\n        if let Some(ident) = &self.name {\n            visitor.visit_identifier(ident)?;\n        }\n        visitor.visit_formal_parameter_list(&self.parameters)?;\n        visitor.visit_function_body(&self.body)\n    }\n\n    fn visit_with_mut<'a, V>(&'a mut self, visitor: &mut V) -> ControlFlow<V::BreakTy>\n    where\n        V: VisitorMut<'a>,\n    {\n        if let Some(ident) = &mut self.name {\n            visitor.visit_identifier_mut(ident)?;\n        }\n        visitor.visit_formal_parameter_list_mut(&mut self.parameters)?;\n        visitor.visit_function_body_mut(&mut self.body)\n    }\n}\n"
  },
  {
    "path": "core/ast/src/function/mod.rs",
    "content": "//! This module contains Function and Class AST nodes.\n//!\n//! ECMAScript defines multiple types of functions and classes.\n//! They are split into different AST nodes to reduce ambiguity and to make the AST more readable.\n//!\n//! - Functions:\n//!   - [`FunctionDeclaration`]\n//!   - [`FunctionExpression`]\n//! - Async functions:\n//!   - [`AsyncFunctionDeclaration`]\n//!   - [`AsyncFunctionExpression`]\n//! - Generators\n//!   - [`GeneratorDeclaration`]\n//!   - [`GeneratorExpression`]\n//! - Async Generators\n//!   - [`AsyncGeneratorDeclaration`]\n//!   - [`AsyncGeneratorExpression`]\n//! - Arrow Functions\n//!   - [`ArrowFunction`]\n//! - Async Arrow Functions\n//!   - [`AsyncArrowFunction`]\n//! - Classes\n//!   - [`ClassDeclaration`]\n//!   - [`ClassExpression`]\n\nmod arrow_function;\nmod async_arrow_function;\nmod async_function;\nmod async_generator;\nmod class;\nmod generator;\nmod ordinary_function;\nmod parameters;\n\nuse std::ops::ControlFlow;\n\npub use arrow_function::ArrowFunction;\npub use async_arrow_function::AsyncArrowFunction;\npub use async_function::{AsyncFunctionDeclaration, AsyncFunctionExpression};\npub use async_generator::{AsyncGeneratorDeclaration, AsyncGeneratorExpression};\nuse boa_interner::{Interner, ToIndentedString};\npub use class::{\n    ClassDeclaration, ClassElement, ClassElementName, ClassExpression, ClassFieldDefinition,\n    ClassMethodDefinition, PrivateFieldDefinition, PrivateName, StaticBlockBody,\n};\npub use generator::{GeneratorDeclaration, GeneratorExpression};\npub use ordinary_function::{FunctionDeclaration, FunctionExpression};\npub use parameters::{FormalParameter, FormalParameterList, FormalParameterListFlags};\n\nuse crate::{\n    LinearPosition, Span, Spanned, StatementList, StatementListItem,\n    visitor::{VisitWith, Visitor, VisitorMut},\n};\n\n/// A Function body.\n///\n/// Since `Script` and `FunctionBody` have the same semantics, this is currently\n/// only an alias of the former.\n///\n/// More information:\n///  - [ECMAScript reference][spec]\n///\n/// [spec]: https://tc39.es/ecma262/#prod-FunctionBody\n#[cfg_attr(feature = \"serde\", derive(serde::Serialize, serde::Deserialize))]\n#[cfg_attr(feature = \"arbitrary\", derive(arbitrary::Arbitrary))]\n#[derive(Clone, Debug, PartialEq)]\npub struct FunctionBody {\n    pub(crate) statements: StatementList,\n    span: Span,\n}\n\nimpl FunctionBody {\n    /// Creates a new `FunctionBody` AST node.\n    #[inline]\n    #[must_use]\n    pub fn new(statements: StatementList, span: Span) -> Self {\n        Self { statements, span }\n    }\n\n    /// Gets the list of statements.\n    #[inline]\n    #[must_use]\n    pub const fn statements(&self) -> &[StatementListItem] {\n        self.statements.statements()\n    }\n\n    /// Gets the statement list.\n    #[inline]\n    #[must_use]\n    pub const fn statement_list(&self) -> &StatementList {\n        &self.statements\n    }\n\n    /// Get the strict mode.\n    #[inline]\n    #[must_use]\n    pub const fn strict(&self) -> bool {\n        self.statements.strict()\n    }\n\n    /// Get end of linear position in source code.\n    #[inline]\n    #[must_use]\n    pub const fn linear_pos_end(&self) -> LinearPosition {\n        self.statements.linear_pos_end()\n    }\n}\n\nimpl Spanned for FunctionBody {\n    #[inline]\n    fn span(&self) -> Span {\n        self.span\n    }\n}\n\nimpl ToIndentedString for FunctionBody {\n    fn to_indented_string(&self, interner: &Interner, indentation: usize) -> String {\n        self.statements.to_indented_string(interner, indentation)\n    }\n}\n\nimpl VisitWith for FunctionBody {\n    fn visit_with<'a, V>(&'a self, visitor: &mut V) -> ControlFlow<V::BreakTy>\n    where\n        V: Visitor<'a>,\n    {\n        for statement in &*self.statements {\n            visitor.visit_statement_list_item(statement)?;\n        }\n        ControlFlow::Continue(())\n    }\n\n    fn visit_with_mut<'a, V>(&'a mut self, visitor: &mut V) -> ControlFlow<V::BreakTy>\n    where\n        V: VisitorMut<'a>,\n    {\n        for statement in &mut *self.statements.statements {\n            visitor.visit_statement_list_item_mut(statement)?;\n        }\n        ControlFlow::Continue(())\n    }\n}\n"
  },
  {
    "path": "core/ast/src/function/ordinary_function.rs",
    "content": "use super::{FormalParameterList, FunctionBody};\nuse crate::{\n    Declaration, LinearSpan, LinearSpanIgnoreEq, Span, Spanned, block_to_string,\n    expression::{Expression, Identifier},\n    join_nodes,\n    operations::{ContainsSymbol, contains},\n    scope::{FunctionScopes, Scope},\n    scope_analyzer::{\n        analyze_binding_escapes, collect_bindings, optimize_scope_indices_function_constructor,\n    },\n    visitor::{VisitWith, Visitor, VisitorMut},\n};\nuse boa_interner::{Interner, ToIndentedString};\nuse core::{fmt::Write as _, ops::ControlFlow};\n\n/// A function declaration.\n///\n/// More information:\n///  - [ECMAScript reference][spec]\n///  - [MDN documentation][mdn]\n///\n/// [spec]: https://tc39.es/ecma262/#prod-FunctionDeclaration\n/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions\n#[cfg_attr(feature = \"serde\", derive(serde::Serialize, serde::Deserialize))]\n#[cfg_attr(feature = \"arbitrary\", derive(arbitrary::Arbitrary))]\n#[derive(Clone, Debug, PartialEq)]\npub struct FunctionDeclaration {\n    name: Identifier,\n    pub(crate) parameters: FormalParameterList,\n    pub(crate) body: FunctionBody,\n    pub(crate) contains_direct_eval: bool,\n\n    #[cfg_attr(feature = \"serde\", serde(skip))]\n    pub(crate) scopes: FunctionScopes,\n    linear_span: LinearSpanIgnoreEq,\n}\n\nimpl FunctionDeclaration {\n    /// Creates a new function declaration.\n    #[inline]\n    #[must_use]\n    pub fn new(\n        name: Identifier,\n        parameters: FormalParameterList,\n        body: FunctionBody,\n        linear_span: LinearSpan,\n    ) -> Self {\n        let contains_direct_eval = contains(&parameters, ContainsSymbol::DirectEval)\n            || contains(&body, ContainsSymbol::DirectEval);\n        Self {\n            name,\n            parameters,\n            body,\n            contains_direct_eval,\n            scopes: FunctionScopes::default(),\n            linear_span: linear_span.into(),\n        }\n    }\n\n    /// Gets the name of the function declaration.\n    #[inline]\n    #[must_use]\n    pub const fn name(&self) -> Identifier {\n        self.name\n    }\n\n    /// Gets the list of parameters of the function declaration.\n    #[inline]\n    #[must_use]\n    pub const fn parameters(&self) -> &FormalParameterList {\n        &self.parameters\n    }\n\n    /// Gets the body of the function declaration.\n    #[inline]\n    #[must_use]\n    pub const fn body(&self) -> &FunctionBody {\n        &self.body\n    }\n\n    /// Gets the scopes of the function declaration.\n    #[inline]\n    #[must_use]\n    pub const fn scopes(&self) -> &FunctionScopes {\n        &self.scopes\n    }\n\n    /// Gets linear span of the function declaration.\n    #[inline]\n    #[must_use]\n    pub const fn linear_span(&self) -> LinearSpan {\n        self.linear_span.0\n    }\n\n    /// Returns `true` if the function declaration contains a direct call to `eval`.\n    #[inline]\n    #[must_use]\n    pub const fn contains_direct_eval(&self) -> bool {\n        self.contains_direct_eval\n    }\n}\n\nimpl ToIndentedString for FunctionDeclaration {\n    fn to_indented_string(&self, interner: &Interner, indentation: usize) -> String {\n        format!(\n            \"function {}({}) {}\",\n            interner.resolve_expect(self.name.sym()),\n            join_nodes(interner, self.parameters.as_ref()),\n            block_to_string(&self.body.statements, interner, indentation)\n        )\n    }\n}\n\nimpl VisitWith for FunctionDeclaration {\n    fn visit_with<'a, V>(&'a self, visitor: &mut V) -> ControlFlow<V::BreakTy>\n    where\n        V: Visitor<'a>,\n    {\n        visitor.visit_identifier(&self.name)?;\n        visitor.visit_formal_parameter_list(&self.parameters)?;\n        visitor.visit_function_body(&self.body)\n    }\n\n    fn visit_with_mut<'a, V>(&'a mut self, visitor: &mut V) -> ControlFlow<V::BreakTy>\n    where\n        V: VisitorMut<'a>,\n    {\n        visitor.visit_identifier_mut(&mut self.name)?;\n        visitor.visit_formal_parameter_list_mut(&mut self.parameters)?;\n        visitor.visit_function_body_mut(&mut self.body)\n    }\n}\n\nimpl From<FunctionDeclaration> for Declaration {\n    #[inline]\n    fn from(f: FunctionDeclaration) -> Self {\n        Self::FunctionDeclaration(f)\n    }\n}\n\n/// A function expression.\n///\n/// More information:\n///  - [ECMAScript reference][spec]\n///  - [MDN documentation][mdn]\n///\n/// [spec]: https://tc39.es/ecma262/#prod-FunctionExpression\n/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions\n#[cfg_attr(feature = \"serde\", derive(serde::Serialize, serde::Deserialize))]\n#[cfg_attr(feature = \"arbitrary\", derive(arbitrary::Arbitrary))]\n#[derive(Clone, Debug)]\npub struct FunctionExpression {\n    pub(crate) name: Option<Identifier>,\n    pub(crate) parameters: FormalParameterList,\n    pub(crate) body: FunctionBody,\n    pub(crate) has_binding_identifier: bool,\n    pub(crate) contains_direct_eval: bool,\n\n    #[cfg_attr(feature = \"serde\", serde(skip))]\n    pub(crate) name_scope: Option<Scope>,\n\n    #[cfg_attr(feature = \"serde\", serde(skip))]\n    pub(crate) scopes: FunctionScopes,\n\n    span: Span,\n\n    linear_span: Option<LinearSpan>,\n}\n\nimpl PartialEq for FunctionExpression {\n    fn eq(&self, other: &Self) -> bool {\n        // all fields except for `linear_span`\n        self.name == other.name\n            && self.parameters == other.parameters\n            && self.body == other.body\n            && self.has_binding_identifier == other.has_binding_identifier\n            && self.contains_direct_eval == other.contains_direct_eval\n            && self.name_scope == other.name_scope\n            && self.scopes == other.scopes\n            && self.span == other.span\n    }\n}\n\nimpl Spanned for FunctionExpression {\n    #[inline]\n    fn span(&self) -> Span {\n        self.span\n    }\n}\n\nimpl FunctionExpression {\n    /// Creates a new function expression.\n    #[inline]\n    #[must_use]\n    pub fn new(\n        name: Option<Identifier>,\n        parameters: FormalParameterList,\n        body: FunctionBody,\n        linear_span: Option<LinearSpan>,\n        has_binding_identifier: bool,\n        span: Span,\n    ) -> Self {\n        let contains_direct_eval = contains(&parameters, ContainsSymbol::DirectEval)\n            || contains(&body, ContainsSymbol::DirectEval);\n        Self {\n            name,\n            parameters,\n            body,\n            has_binding_identifier,\n            name_scope: None,\n            contains_direct_eval,\n            scopes: FunctionScopes::default(),\n            #[allow(clippy::redundant_closure_for_method_calls)]\n            linear_span,\n            span,\n        }\n    }\n\n    /// Gets the name of the function expression.\n    #[inline]\n    #[must_use]\n    pub const fn name(&self) -> Option<Identifier> {\n        self.name\n    }\n\n    /// Gets the list of parameters of the function expression.\n    #[inline]\n    #[must_use]\n    pub const fn parameters(&self) -> &FormalParameterList {\n        &self.parameters\n    }\n\n    /// Gets the body of the function expression.\n    #[inline]\n    #[must_use]\n    pub const fn body(&self) -> &FunctionBody {\n        &self.body\n    }\n\n    /// Returns whether the function expression has a binding identifier.\n    #[inline]\n    #[must_use]\n    pub const fn has_binding_identifier(&self) -> bool {\n        self.has_binding_identifier\n    }\n\n    /// Gets the name scope of the function expression.\n    #[inline]\n    #[must_use]\n    pub const fn name_scope(&self) -> Option<&Scope> {\n        self.name_scope.as_ref()\n    }\n\n    /// Gets the scopes of the function expression.\n    #[inline]\n    #[must_use]\n    pub const fn scopes(&self) -> &FunctionScopes {\n        &self.scopes\n    }\n\n    /// Gets linear span of the function declaration.\n    #[inline]\n    #[must_use]\n    pub const fn linear_span(&self) -> Option<LinearSpan> {\n        self.linear_span\n    }\n\n    /// Returns `true` if the function expression contains a direct call to `eval`.\n    #[inline]\n    #[must_use]\n    pub const fn contains_direct_eval(&self) -> bool {\n        self.contains_direct_eval\n    }\n\n    /// Analyze the scope of the function expression.\n    ///\n    /// # Errors\n    /// Any scope or binding errors that happened during the analysis.\n    pub fn analyze_scope(\n        &mut self,\n        strict: bool,\n        scope: &Scope,\n        interner: &Interner,\n    ) -> Result<(), &'static str> {\n        collect_bindings(self, strict, false, scope, interner)?;\n        analyze_binding_escapes(self, false, scope.clone(), interner)?;\n        optimize_scope_indices_function_constructor(self, scope);\n        Ok(())\n    }\n}\n\nimpl ToIndentedString for FunctionExpression {\n    fn to_indented_string(&self, interner: &Interner, indentation: usize) -> String {\n        let mut buf = \"function\".to_owned();\n        if self.has_binding_identifier\n            && let Some(name) = self.name\n        {\n            let _ = write!(buf, \" {}\", interner.resolve_expect(name.sym()));\n        }\n        let _ = write!(\n            buf,\n            \"({}) {}\",\n            join_nodes(interner, self.parameters.as_ref()),\n            block_to_string(&self.body.statements, interner, indentation)\n        );\n\n        buf\n    }\n}\n\nimpl From<FunctionExpression> for Expression {\n    #[inline]\n    fn from(expr: FunctionExpression) -> Self {\n        Self::FunctionExpression(expr)\n    }\n}\n\nimpl VisitWith for FunctionExpression {\n    fn visit_with<'a, V>(&'a self, visitor: &mut V) -> ControlFlow<V::BreakTy>\n    where\n        V: Visitor<'a>,\n    {\n        if let Some(ident) = &self.name {\n            visitor.visit_identifier(ident)?;\n        }\n        visitor.visit_formal_parameter_list(&self.parameters)?;\n        visitor.visit_function_body(&self.body)\n    }\n\n    fn visit_with_mut<'a, V>(&'a mut self, visitor: &mut V) -> ControlFlow<V::BreakTy>\n    where\n        V: VisitorMut<'a>,\n    {\n        if let Some(ident) = &mut self.name {\n            visitor.visit_identifier_mut(ident)?;\n        }\n        visitor.visit_formal_parameter_list_mut(&mut self.parameters)?;\n        visitor.visit_function_body_mut(&mut self.body)\n    }\n}\n"
  },
  {
    "path": "core/ast/src/function/parameters.rs",
    "content": "use crate::{\n    declaration::{Binding, Variable},\n    expression::Expression,\n    operations::bound_names,\n    visitor::{VisitWith, Visitor, VisitorMut},\n};\nuse bitflags::bitflags;\nuse boa_interner::{Interner, Sym, ToInternedString};\nuse core::ops::ControlFlow;\nuse rustc_hash::FxHashSet;\n\n/// A list of `FormalParameter`s that describes the parameters of a function, as defined by the [spec].\n///\n/// [spec]: https://tc39.es/ecma262/#prod-FormalParameterList\n#[cfg_attr(feature = \"serde\", derive(serde::Serialize, serde::Deserialize))]\n#[derive(Clone, Debug, Default, PartialEq)]\npub struct FormalParameterList {\n    parameters: Box<[FormalParameter]>,\n    flags: FormalParameterListFlags,\n    length: u32,\n}\n\nimpl FormalParameterList {\n    /// Creates a new empty formal parameter list.\n    #[must_use]\n    pub fn new() -> Self {\n        Self {\n            parameters: Box::new([]),\n            flags: FormalParameterListFlags::default(),\n            length: 0,\n        }\n    }\n\n    /// Creates a `FormalParameterList` from a list of [`FormalParameter`]s.\n    #[must_use]\n    pub fn from_parameters(parameters: Vec<FormalParameter>) -> Self {\n        let mut flags = FormalParameterListFlags::default();\n        let mut length = 0;\n        let mut names = FxHashSet::default();\n\n        for parameter in &parameters {\n            let parameter_names = bound_names(parameter);\n\n            for name in parameter_names {\n                if name == Sym::ARGUMENTS {\n                    flags |= FormalParameterListFlags::HAS_ARGUMENTS;\n                }\n                if names.contains(&name) {\n                    flags |= FormalParameterListFlags::HAS_DUPLICATES;\n                } else {\n                    names.insert(name);\n                }\n            }\n\n            if parameter.is_rest_param() {\n                flags |= FormalParameterListFlags::HAS_REST_PARAMETER;\n            }\n            if parameter.init().is_some() {\n                flags |= FormalParameterListFlags::HAS_EXPRESSIONS;\n            }\n            if parameter.is_rest_param() || parameter.init().is_some() || !parameter.is_identifier()\n            {\n                flags.remove(FormalParameterListFlags::IS_SIMPLE);\n            }\n            if !(flags.contains(FormalParameterListFlags::HAS_EXPRESSIONS)\n                || parameter.is_rest_param()\n                || parameter.init().is_some())\n            {\n                length += 1;\n            }\n        }\n\n        Self {\n            parameters: parameters.into(),\n            flags,\n            length,\n        }\n    }\n\n    /// Returns the length of the parameter list.\n    /// Note that this is not equal to the length of the parameters slice.\n    #[must_use]\n    pub const fn length(&self) -> u32 {\n        self.length\n    }\n\n    /// Returns the parameter list flags.\n    #[must_use]\n    pub const fn flags(&self) -> FormalParameterListFlags {\n        self.flags\n    }\n\n    /// Indicates if the parameter list is simple.\n    #[must_use]\n    pub const fn is_simple(&self) -> bool {\n        self.flags.contains(FormalParameterListFlags::IS_SIMPLE)\n    }\n\n    /// Indicates if the parameter list has duplicate parameters.\n    #[must_use]\n    pub const fn has_duplicates(&self) -> bool {\n        self.flags\n            .contains(FormalParameterListFlags::HAS_DUPLICATES)\n    }\n\n    /// Indicates if the parameter list has a rest parameter.\n    #[must_use]\n    pub const fn has_rest_parameter(&self) -> bool {\n        self.flags\n            .contains(FormalParameterListFlags::HAS_REST_PARAMETER)\n    }\n\n    /// Indicates if the parameter list has expressions in it's parameters.\n    #[must_use]\n    pub const fn has_expressions(&self) -> bool {\n        self.flags\n            .contains(FormalParameterListFlags::HAS_EXPRESSIONS)\n    }\n\n    /// Indicates if the parameter list has parameters named 'arguments'.\n    #[must_use]\n    pub const fn has_arguments(&self) -> bool {\n        self.flags.contains(FormalParameterListFlags::HAS_ARGUMENTS)\n    }\n}\n\nimpl From<Vec<FormalParameter>> for FormalParameterList {\n    fn from(parameters: Vec<FormalParameter>) -> Self {\n        Self::from_parameters(parameters)\n    }\n}\n\nimpl From<FormalParameter> for FormalParameterList {\n    fn from(parameter: FormalParameter) -> Self {\n        Self::from_parameters(vec![parameter])\n    }\n}\n\nimpl AsRef<[FormalParameter]> for FormalParameterList {\n    fn as_ref(&self) -> &[FormalParameter] {\n        &self.parameters\n    }\n}\n\nimpl VisitWith for FormalParameterList {\n    fn visit_with<'a, V>(&'a self, visitor: &mut V) -> ControlFlow<V::BreakTy>\n    where\n        V: Visitor<'a>,\n    {\n        for parameter in &*self.parameters {\n            visitor.visit_formal_parameter(parameter)?;\n        }\n\n        ControlFlow::Continue(())\n    }\n\n    fn visit_with_mut<'a, V>(&'a mut self, visitor: &mut V) -> ControlFlow<V::BreakTy>\n    where\n        V: VisitorMut<'a>,\n    {\n        for parameter in &mut *self.parameters {\n            visitor.visit_formal_parameter_mut(parameter)?;\n        }\n\n        // TODO recompute flags\n        ControlFlow::Continue(())\n    }\n}\n\n#[cfg(feature = \"arbitrary\")]\nimpl<'a> arbitrary::Arbitrary<'a> for FormalParameterList {\n    fn arbitrary(u: &mut arbitrary::Unstructured<'a>) -> arbitrary::Result<Self> {\n        let params: Vec<FormalParameter> = u.arbitrary()?;\n        Ok(Self::from(params))\n    }\n}\n\nbitflags! {\n    /// Flags for a [`FormalParameterList`].\n    #[derive(Debug, Copy, Clone, Eq, PartialEq)]\n    #[cfg_attr(feature = \"serde\", derive(serde::Serialize, serde::Deserialize))]\n    pub struct FormalParameterListFlags: u8 {\n        /// Has only identifier parameters with no initialization expressions.\n        const IS_SIMPLE = 0b0000_0001;\n        /// Has any duplicate parameters.\n        const HAS_DUPLICATES = 0b0000_0010;\n        /// Has a rest parameter.\n        const HAS_REST_PARAMETER = 0b0000_0100;\n        /// Has any initialization expression.\n        const HAS_EXPRESSIONS = 0b0000_1000;\n        /// Has an argument with the name `arguments`.\n        const HAS_ARGUMENTS = 0b0001_0000;\n    }\n}\n\nimpl Default for FormalParameterListFlags {\n    fn default() -> Self {\n        Self::empty().union(Self::IS_SIMPLE)\n    }\n}\n\n/// \"Formal parameter\" is a fancy way of saying \"function parameter\".\n///\n/// In the declaration of a function, the parameters must be identifiers,\n/// not any value like numbers, strings, or objects.\n/// ```text\n/// function foo(formalParameter1, formalParameter2) {\n/// }\n/// ```\n///\n/// More information:\n///  - [ECMAScript reference][spec]\n///  - [MDN documentation][mdn]\n///\n/// [spec]: https://tc39.es/ecma262/#prod-FormalParameter\n/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Errors/Missing_formal_parameter\n#[cfg_attr(feature = \"serde\", derive(serde::Serialize, serde::Deserialize))]\n#[cfg_attr(feature = \"arbitrary\", derive(arbitrary::Arbitrary))]\n#[derive(Clone, Debug, PartialEq)]\npub struct FormalParameter {\n    variable: Variable,\n    is_rest_param: bool,\n}\n\nimpl FormalParameter {\n    /// Creates a new formal parameter.\n    pub fn new<D>(variable: D, is_rest_param: bool) -> Self\n    where\n        D: Into<Variable>,\n    {\n        Self {\n            variable: variable.into(),\n            is_rest_param,\n        }\n    }\n\n    /// Gets the variable of the formal parameter\n    #[must_use]\n    pub const fn variable(&self) -> &Variable {\n        &self.variable\n    }\n\n    /// Gets the initialization node of the formal parameter, if any.\n    #[must_use]\n    pub const fn init(&self) -> Option<&Expression> {\n        self.variable.init()\n    }\n\n    /// Returns `true` if the parameter is a rest parameter.\n    #[must_use]\n    pub const fn is_rest_param(&self) -> bool {\n        self.is_rest_param\n    }\n\n    /// Returns `true` if the parameter is an identifier.\n    #[must_use]\n    pub const fn is_identifier(&self) -> bool {\n        matches!(&self.variable.binding(), Binding::Identifier(_))\n    }\n}\n\nimpl ToInternedString for FormalParameter {\n    fn to_interned_string(&self, interner: &Interner) -> String {\n        let mut buf = if self.is_rest_param {\n            \"...\".to_owned()\n        } else {\n            String::new()\n        };\n        buf.push_str(&self.variable.to_interned_string(interner));\n        buf\n    }\n}\n\nimpl VisitWith for FormalParameter {\n    fn visit_with<'a, V>(&'a self, visitor: &mut V) -> ControlFlow<V::BreakTy>\n    where\n        V: Visitor<'a>,\n    {\n        visitor.visit_variable(&self.variable)\n    }\n\n    fn visit_with_mut<'a, V>(&'a mut self, visitor: &mut V) -> ControlFlow<V::BreakTy>\n    where\n        V: VisitorMut<'a>,\n    {\n        visitor.visit_variable_mut(&mut self.variable)\n    }\n}\n"
  },
  {
    "path": "core/ast/src/keyword/mod.rs",
    "content": "//! The `Keyword` AST node, which represents reserved words of the ECMAScript language.\n//!\n//! The [specification][spec] defines keywords as tokens that match an `IdentifierName`, but also\n//! have special meaning in ECMAScript. In ECMAScript, you cannot use these reserved words as variables,\n//! labels, or function names.\n//!\n//! The [MDN documentation][mdn] contains a more extensive explanation about keywords.\n//!\n//! [spec]: https://tc39.es/ecma262/#sec-keywords-and-reserved-words\n//! [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Lexical_grammar#Keywords\n\nuse crate::expression::operator::binary::{BinaryOp, RelationalOp};\nuse boa_interner::Sym;\nuse boa_macros::utf16;\nuse std::{convert::TryFrom, error, fmt, str::FromStr};\n\n#[cfg(test)]\nmod tests;\n\n/// List of keywords recognized by the JavaScript grammar.\n///\n/// See the [module-level documentation][self] for more details.\n#[cfg_attr(feature = \"serde\", derive(serde::Serialize, serde::Deserialize))]\n#[derive(Clone, Copy, PartialEq, Eq, Debug)]\npub enum Keyword {\n    /// The `await` keyword.\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///  - [MDN documentation][mdn]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#prod-AwaitExpression\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/await\n    Await,\n\n    /// The `async` keyword.\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///  - [MDN documentation][mdn]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#prod-AsyncMethod\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/async_function\n    Async,\n\n    /// The `break` keyword.\n    ///\n    /// More information:\n    ///  - [break `Node` documentation][node]\n    ///  - [ECMAScript reference][spec]\n    ///  - [MDN documentation][mdn]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#prod-BreakStatement\n    /// [node]: ../node/enum.Node.html#variant.Break\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/break\n    Break,\n\n    /// The `case` keyword.\n    ///\n    /// More information:\n    ///  - [switch `Node` documentation][node]\n    ///  - [ECMAScript reference][spec]\n    ///  - [MDN documentation][mdn]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#prod-CaseClause\n    /// [node]: ../node/enum.Node.html#variant.Switch\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/switch\n    Case,\n\n    /// The `catch` keyword.\n    ///\n    /// More information:\n    ///  - [try `Node` documentation][node]\n    ///  - [ECMAScript reference][spec]\n    ///  - [MDN documentation][mdn]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#prod-Catch\n    /// [node]: ../node/enum.Node.html#variant.Try\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/try...catch\n    Catch,\n\n    /// The `class` keyword.\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///  - [MDN documentation][mdn]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#prod-ClassDeclaration\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/class\n    Class,\n\n    /// The `continue` keyword.\n    ///\n    /// More information:\n    ///  - [continue `Node` documentation][node]\n    ///  - [ECMAScript reference][spec]\n    ///  - [MDN documentation][mdn]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#prod-ContinueStatement\n    /// [node]: ../node/enum.Node.html#variant.Continue\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/continue\n    Continue,\n\n    /// The `const` keyword.\n    ///\n    /// More information:\n    ///  - [const `Node` documentation][node]\n    ///  - [ECMAScript reference][spec]\n    ///  - [MDN documentation][mdn]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-let-and-const-declarations\n    /// [node]: ../node/enum.Node.html#variant.ConstDecl\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/const\n    Const,\n\n    /// The `debugger` keyword.\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///  - [MDN documentation][mdn]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-debugger-statement\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/debugger\n    Debugger,\n\n    /// The `default` keyword.\n    ///\n    /// More information:\n    ///  - [switch `Node` documentation][node]\n    ///  - [ECMAScript reference default clause][spec-clause]\n    ///  - [ECMAScript reference default export][spec-export]\n    ///  - [MDN documentation][mdn]\n    ///\n    /// [node]: ../node/enum.Node.html#variant.Switch\n    /// [spec-clause]: https://tc39.es/ecma262/#prod-DefaultClause\n    /// [spec-export]: https://tc39.es/ecma262/#prod-ImportedDefaultBinding\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/default\n    Default,\n\n    /// The `delete` keyword.\n    ///\n    /// More information:\n    ///  - [delete `UnaryOp` documentation][unary]\n    ///  - [ECMAScript reference][spec]\n    ///  - [MDN documentation][mdn]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-delete-operator\n    /// [unary]: ../op/enum.UnaryOp.html#variant.Delete\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/delete\n    Delete,\n\n    /// The `do` keyword.\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///  - [MDN documentation][mdn]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-do-while-statement\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/do...while\n    Do,\n\n    /// The `else` keyword.\n    ///\n    /// More information:\n    ///  - [if `Node` documentation][node]\n    ///  - [ECMAScript reference][spec]\n    ///  - [MDN documentation][mdn]\n    ///\n    /// [node]: ../node/enum.Node.html#variant.If\n    /// [spec]: https://tc39.es/ecma262/#prod-IfStatement\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/if...else\n    Else,\n\n    /// The `enum` keyword.\n    ///\n    /// Future reserved keyword.\n    Enum,\n\n    /// The `export` keyword.\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///  - [MDN documentation][mdn]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-exports\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/export\n    Export,\n\n    /// The `extends` keyword.\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///  - [MDN documentation][mdn]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#prod-ClassHeritage\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Classes/extends\n    Extends,\n\n    /// The `finally` keyword.\n    ///\n    /// More information:\n    ///  - [try `Node` documentation][node]\n    ///  - [ECMAScript reference][spec]\n    ///  - [MDN documentation][mdn]\n    ///\n    /// [node]: ../node/enum.Node.html#variant.Try\n    /// [spec]: https://tc39.es/ecma262/#prod-Finally\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/try...catch\n    Finally,\n\n    /// The `for` keyword.\n    ///\n    /// More information:\n    ///  - [for loop `Node` documentation][node]\n    ///  - [ECMAScript reference][spec]\n    ///  - [MDN documentation][mdn]\n    ///\n    /// [node]: ../node/enum.Node.html#variant.ForLoop\n    /// [spec]: https://tc39.es/ecma262/#prod-ForDeclaration\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/for\n    For,\n\n    /// The `function` keyword.\n    ///\n    /// More information:\n    ///  - [function `Node` documentation][node]\n    ///  - [ECMAScript reference][spec]\n    ///  - [MDN documentation][mdn]\n    ///\n    /// [node]: ../node/enum.Node.html#variant.FunctionDecl\n    /// [spec]: https://tc39.es/ecma262/#sec-terms-and-definitions-function\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/function\n    Function,\n\n    /// The `if` keyword.\n    ///\n    /// More information:\n    ///  - [if `Node` documentation][node]\n    ///  - [ECMAScript reference][spec]\n    ///  - [MDN documentation][mdn]\n    ///\n    /// [node]: ../node/enum.Node.html#variant.If\n    /// [spec]: https://tc39.es/ecma262/#prod-IfStatement\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/if...else\n    If,\n\n    /// The `in` keyword.\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///  - [MDN documentation][mdn]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#prod-RelationalExpression\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/in\n    In,\n\n    /// The `instanceof` keyword.\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///  - [MDN documentation][mdn]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-instanceofoperator\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/instanceof\n    InstanceOf,\n\n    /// The `import` keyword.\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///  - [MDN documentation][mdn]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-imports\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/import\n    Import,\n\n    /// The `let` keyword.\n    ///\n    /// More information:\n    ///  - [let `Node` documentation][node]\n    ///  - [ECMAScript reference][spec]\n    ///  - [MDN documentation][mdn]\n    ///\n    /// [node]: ../node/enum.Node.html#variant.LetDecl\n    /// [spec]: https://tc39.es/ecma262/#sec-let-and-const-declarations\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/let\n    Let,\n\n    /// The `new` keyword.\n    ///\n    /// More information:\n    ///  - [new `Node` documentation][node]\n    ///  - [ECMAScript reference][spec]\n    ///  - [MDN documentation][mdn]\n    ///\n    /// [node]: ../node/enum.Node.html#variant.New\n    /// [spec]: https://tc39.es/ecma262/#prod-NewExpression\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/new\n    New,\n\n    /// The `of` keyword.\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///  - [MDN documentation][mdn]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-for-in-and-for-of-statements\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/for...of\n    Of,\n\n    /// The `return` keyword\n    ///\n    /// More information:\n    ///  - [return `Node` documentation][node]\n    ///  - [ECMAScript reference][spec]\n    ///  - [MDN documentation][mdn]\n    ///\n    /// [node]: ../node/enum.Node.html#variant.Return\n    /// [spec]: https://tc39.es/ecma262/#prod-ReturnStatement\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/return\n    Return,\n\n    /// The `super` keyword\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///  - [MDN documentation][mdn]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-super-keyword\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/super\n    Super,\n\n    /// The `switch` keyword.\n    ///\n    /// More information:\n    ///  - [switch `Node` documentation][node]\n    ///  - [ECMAScript reference][spec]\n    ///  - [MDN documentation][mdn]\n    ///\n    /// [node]: ../node/enum.Node.html#variant.Switch\n    /// [spec]: https://tc39.es/ecma262/#prod-SwitchStatement\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/switch\n    Switch,\n\n    /// The `this` keyword.\n    ///\n    /// More information:\n    ///  - [this `Node` documentation][node]\n    ///  - [ECMAScript reference][spec]\n    ///  - [MDN documentation][mdn]\n    ///\n    /// [node]: ../node/enum.Node.html#variant.This\n    /// [spec]: https://tc39.es/ecma262/#sec-this-keyword\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/this\n    This,\n\n    /// The `throw` keyword.\n    ///\n    /// More information:\n    ///  - [throw `Node` documentation][node]\n    ///  - [ECMAScript reference][spec]\n    ///  - [MDN documentation][mdn]\n    ///\n    /// [node]: ../node/enum.Node.html#variant.Throw\n    /// [spec]: https://tc39.es/ecma262/#sec-throw-statement\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/throw\n    Throw,\n\n    /// The `try` keyword.\n    ///\n    /// More information:\n    ///  - [try `Node` documentation][node]\n    ///  - [ECMAScript reference][spec]\n    ///  - [MDN documentation][mdn]\n    ///\n    /// [node]: ../node/enum.Node.html#variant.Try\n    /// [spec]: https://tc39.es/ecma262/#prod-TryStatement\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/try...catch\n    Try,\n\n    /// The `typeof` keyword.\n    ///\n    /// More information:\n    ///  - [typeof `UnaryOp` documentation][unary]\n    ///  - [ECMAScript reference][spec]\n    ///  - [MDN documentation][mdn]\n    ///\n    /// [unary]: ../op/enum.UnaryOp.html#variant.TypeOf\n    /// [spec]: https://tc39.es/ecma262/#sec-typeof-operator\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/typeof\n    TypeOf,\n\n    /// The `var` keyword.\n    ///\n    /// More information:\n    ///  - [var `Node` documentation][node]\n    ///  - [ECMAScript reference][spec]\n    ///  - [MDN documentation][mdn]\n    ///\n    /// [node]: ../node/enum.Node.html#variant.VarDecl\n    /// [spec]: https://tc39.es/ecma262/#prod-VariableStatement\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/var\n    Var,\n\n    /// The `void` keyword.\n    ///\n    /// More information:\n    ///  - [void `UnaryOp` documentation][unary]\n    ///  - [ECMAScript reference][spec]\n    ///  - [MDN documentation][mdn]\n    ///\n    /// [unary]: ../op/enum.UnaryOp.html#variant.Void\n    /// [spec]: https://tc39.es/ecma262/#sec-void-operator\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/void\n    Void,\n\n    /// The `while` keyword.\n    ///\n    /// More information:\n    ///  - [while `Node` documentation][node]\n    ///  - [ECMAScript reference][spec]\n    ///  - [MDN documentation][mdn]\n    ///\n    /// [node]: ../node/enum.Node.html#variant.While\n    /// [spec]: https://tc39.es/ecma262/#prod-grammar-notation-WhileStatement\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/while\n    While,\n\n    /// The `with` keyword.\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///  - [MDN documentation][mdn]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#prod-WithStatement\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/with\n    With,\n\n    /// The 'yield' keyword.\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///  - [MDN documentation][mdn]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#prod-YieldExpression\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/yield\n    Yield,\n\n    /// The `using` keyword.\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///  - [MDN documentation][mdn]\n    ///\n    /// [spec]: https://tc39.es/proposal-explicit-resource-management/\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/using\n    Using,\n}\n\nimpl Keyword {\n    /// Gets the keyword as a binary operation, if this keyword is the `in` or the `instanceof`\n    /// keywords.\n    #[must_use]\n    pub const fn as_binary_op(self) -> Option<BinaryOp> {\n        match self {\n            Self::In => Some(BinaryOp::Relational(RelationalOp::In)),\n            Self::InstanceOf => Some(BinaryOp::Relational(RelationalOp::InstanceOf)),\n            _ => None,\n        }\n    }\n\n    /// Gets the keyword as a tuple of strings.\n    #[must_use]\n    pub const fn as_str(self) -> (&'static str, &'static [u16]) {\n        match self {\n            Self::Await => (\"await\", utf16!(\"await\")),\n            Self::Async => (\"async\", utf16!(\"async\")),\n            Self::Break => (\"break\", utf16!(\"break\")),\n            Self::Case => (\"case\", utf16!(\"case\")),\n            Self::Catch => (\"catch\", utf16!(\"catch\")),\n            Self::Class => (\"class\", utf16!(\"class\")),\n            Self::Continue => (\"continue\", utf16!(\"continue\")),\n            Self::Const => (\"const\", utf16!(\"const\")),\n            Self::Debugger => (\"debugger\", utf16!(\"debugger\")),\n            Self::Default => (\"default\", utf16!(\"default\")),\n            Self::Delete => (\"delete\", utf16!(\"delete\")),\n            Self::Do => (\"do\", utf16!(\"do\")),\n            Self::Else => (\"else\", utf16!(\"else\")),\n            Self::Enum => (\"enum\", utf16!(\"enum\")),\n            Self::Extends => (\"extends\", utf16!(\"extends\")),\n            Self::Export => (\"export\", utf16!(\"export\")),\n            Self::Finally => (\"finally\", utf16!(\"finally\")),\n            Self::For => (\"for\", utf16!(\"for\")),\n            Self::Function => (\"function\", utf16!(\"function\")),\n            Self::If => (\"if\", utf16!(\"if\")),\n            Self::In => (\"in\", utf16!(\"in\")),\n            Self::InstanceOf => (\"instanceof\", utf16!(\"instanceof\")),\n            Self::Import => (\"import\", utf16!(\"import\")),\n            Self::Let => (\"let\", utf16!(\"let\")),\n            Self::New => (\"new\", utf16!(\"new\")),\n            Self::Of => (\"of\", utf16!(\"of\")),\n            Self::Return => (\"return\", utf16!(\"return\")),\n            Self::Super => (\"super\", utf16!(\"super\")),\n            Self::Switch => (\"switch\", utf16!(\"switch\")),\n            Self::This => (\"this\", utf16!(\"this\")),\n            Self::Throw => (\"throw\", utf16!(\"throw\")),\n            Self::Try => (\"try\", utf16!(\"try\")),\n            Self::TypeOf => (\"typeof\", utf16!(\"typeof\")),\n            Self::Var => (\"var\", utf16!(\"var\")),\n            Self::Void => (\"void\", utf16!(\"void\")),\n            Self::While => (\"while\", utf16!(\"while\")),\n            Self::With => (\"with\", utf16!(\"with\")),\n            Self::Yield => (\"yield\", utf16!(\"yield\")),\n            Self::Using => (\"using\", utf16!(\"using\")),\n        }\n    }\n\n    /// Converts the keyword to a symbol in the given interner.\n    #[must_use]\n    pub const fn to_sym(self) -> Sym {\n        match self {\n            Self::Await => Sym::AWAIT,\n            Self::Async => Sym::ASYNC,\n            Self::Break => Sym::BREAK,\n            Self::Case => Sym::CASE,\n            Self::Catch => Sym::CATCH,\n            Self::Class => Sym::CLASS,\n            Self::Continue => Sym::CONTINUE,\n            Self::Const => Sym::CONST,\n            Self::Debugger => Sym::DEBUGGER,\n            Self::Default => Sym::DEFAULT,\n            Self::Delete => Sym::DELETE,\n            Self::Do => Sym::DO,\n            Self::Else => Sym::ELSE,\n            Self::Enum => Sym::ENUM,\n            Self::Export => Sym::EXPORT,\n            Self::Extends => Sym::EXTENDS,\n            Self::Finally => Sym::FINALLY,\n            Self::For => Sym::FOR,\n            Self::Function => Sym::FUNCTION,\n            Self::If => Sym::IF,\n            Self::In => Sym::IN,\n            Self::InstanceOf => Sym::INSTANCEOF,\n            Self::Import => Sym::IMPORT,\n            Self::Let => Sym::LET,\n            Self::New => Sym::NEW,\n            Self::Of => Sym::OF,\n            Self::Return => Sym::RETURN,\n            Self::Super => Sym::SUPER,\n            Self::Switch => Sym::SWITCH,\n            Self::This => Sym::THIS,\n            Self::Throw => Sym::THROW,\n            Self::Try => Sym::TRY,\n            Self::TypeOf => Sym::TYPEOF,\n            Self::Var => Sym::VAR,\n            Self::Void => Sym::VOID,\n            Self::While => Sym::WHILE,\n            Self::With => Sym::WITH,\n            Self::Yield => Sym::YIELD,\n            Self::Using => Sym::USING,\n        }\n    }\n}\n\nimpl TryFrom<Keyword> for BinaryOp {\n    type Error = KeywordToBinaryOpError;\n\n    fn try_from(value: Keyword) -> Result<Self, Self::Error> {\n        value.as_binary_op().ok_or(KeywordToBinaryOpError)\n    }\n}\n\n/// Error returned when a [`Keyword`] cannot be converted into a [`BinaryOp`].\n#[derive(Debug, Clone, Copy)]\npub struct KeywordToBinaryOpError;\n\nimpl fmt::Display for KeywordToBinaryOpError {\n    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {\n        write!(f, \"keyword is not a binary operator\")\n    }\n}\n\nimpl error::Error for KeywordToBinaryOpError {}\n\n/// The error type which is returned from parsing a [`str`] into a [`Keyword`].\n#[derive(Debug, Clone, Copy)]\npub struct KeywordError;\nimpl fmt::Display for KeywordError {\n    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {\n        write!(f, \"invalid token\")\n    }\n}\n\n// This is important for other errors to wrap this one.\nimpl error::Error for KeywordError {}\n\nimpl FromStr for Keyword {\n    type Err = KeywordError;\n    fn from_str(s: &str) -> Result<Self, Self::Err> {\n        match s {\n            \"await\" => Ok(Self::Await),\n            \"async\" => Ok(Self::Async),\n            \"break\" => Ok(Self::Break),\n            \"case\" => Ok(Self::Case),\n            \"catch\" => Ok(Self::Catch),\n            \"class\" => Ok(Self::Class),\n            \"continue\" => Ok(Self::Continue),\n            \"const\" => Ok(Self::Const),\n            \"debugger\" => Ok(Self::Debugger),\n            \"default\" => Ok(Self::Default),\n            \"delete\" => Ok(Self::Delete),\n            \"do\" => Ok(Self::Do),\n            \"else\" => Ok(Self::Else),\n            \"enum\" => Ok(Self::Enum),\n            \"extends\" => Ok(Self::Extends),\n            \"export\" => Ok(Self::Export),\n            \"finally\" => Ok(Self::Finally),\n            \"for\" => Ok(Self::For),\n            \"function\" => Ok(Self::Function),\n            \"if\" => Ok(Self::If),\n            \"in\" => Ok(Self::In),\n            \"instanceof\" => Ok(Self::InstanceOf),\n            \"import\" => Ok(Self::Import),\n            \"let\" => Ok(Self::Let),\n            \"new\" => Ok(Self::New),\n            \"of\" => Ok(Self::Of),\n            \"return\" => Ok(Self::Return),\n            \"super\" => Ok(Self::Super),\n            \"switch\" => Ok(Self::Switch),\n            \"this\" => Ok(Self::This),\n            \"throw\" => Ok(Self::Throw),\n            \"try\" => Ok(Self::Try),\n            \"typeof\" => Ok(Self::TypeOf),\n            \"var\" => Ok(Self::Var),\n            \"void\" => Ok(Self::Void),\n            \"while\" => Ok(Self::While),\n            \"with\" => Ok(Self::With),\n            \"yield\" => Ok(Self::Yield),\n            \"using\" => Ok(Self::Using),\n            _ => Err(KeywordError),\n        }\n    }\n}\n\nimpl fmt::Display for Keyword {\n    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {\n        fmt::Display::fmt(self.as_str().0, f)\n    }\n}\n"
  },
  {
    "path": "core/ast/src/keyword/tests.rs",
    "content": "#![allow(clippy::cognitive_complexity)]\n\nuse super::*;\n\n/// Gets a list of all the keywords.\nfn all_keywords() -> impl Iterator<Item = Keyword> {\n    [\n        Keyword::Await,\n        Keyword::Async,\n        Keyword::Break,\n        Keyword::Case,\n        Keyword::Catch,\n        Keyword::Class,\n        Keyword::Continue,\n        Keyword::Const,\n        Keyword::Debugger,\n        Keyword::Default,\n        Keyword::Delete,\n        Keyword::Do,\n        Keyword::Else,\n        Keyword::Enum,\n        Keyword::Export,\n        Keyword::Extends,\n        Keyword::Finally,\n        Keyword::For,\n        Keyword::Function,\n        Keyword::If,\n        Keyword::In,\n        Keyword::InstanceOf,\n        Keyword::Import,\n        Keyword::Let,\n        Keyword::New,\n        Keyword::Of,\n        Keyword::Return,\n        Keyword::Super,\n        Keyword::Switch,\n        Keyword::This,\n        Keyword::Throw,\n        Keyword::Try,\n        Keyword::TypeOf,\n        Keyword::Var,\n        Keyword::Void,\n        Keyword::While,\n        Keyword::With,\n        Keyword::Yield,\n    ]\n    .into_iter()\n}\n\n#[test]\nfn as_binary_op() {\n    for k in all_keywords() {\n        match k.as_binary_op() {\n            Some(BinaryOp::Relational(RelationalOp::InstanceOf)) => {\n                assert_eq!(k, Keyword::InstanceOf);\n            }\n            Some(BinaryOp::Relational(RelationalOp::In)) => assert_eq!(k, Keyword::In),\n            None => {\n                assert_ne!(k, Keyword::InstanceOf);\n                assert_ne!(k, Keyword::In);\n            }\n            _ => unreachable!(\"unknown binary operator for keyword {k:?} found\"),\n        }\n    }\n}\n\n#[test]\nfn as_str() {\n    for k in all_keywords() {\n        match k.as_str() {\n            (\"await\", utf16) => {\n                assert_eq!(k, Keyword::Await);\n                assert_eq!(utf16, utf16!(\"await\"));\n            }\n            (\"async\", utf16) => {\n                assert_eq!(k, Keyword::Async);\n                assert_eq!(utf16, utf16!(\"async\"));\n            }\n            (\"break\", utf16) => {\n                assert_eq!(k, Keyword::Break);\n                assert_eq!(utf16, utf16!(\"break\"));\n            }\n            (\"case\", utf16) => {\n                assert_eq!(k, Keyword::Case);\n                assert_eq!(utf16, utf16!(\"case\"));\n            }\n            (\"catch\", utf16) => {\n                assert_eq!(k, Keyword::Catch);\n                assert_eq!(utf16, utf16!(\"catch\"));\n            }\n            (\"class\", utf16) => {\n                assert_eq!(k, Keyword::Class);\n                assert_eq!(utf16, utf16!(\"class\"));\n            }\n            (\"continue\", utf16) => {\n                assert_eq!(k, Keyword::Continue);\n                assert_eq!(utf16, utf16!(\"continue\"));\n            }\n            (\"const\", utf16) => {\n                assert_eq!(k, Keyword::Const);\n                assert_eq!(utf16, utf16!(\"const\"));\n            }\n            (\"debugger\", utf16) => {\n                assert_eq!(k, Keyword::Debugger);\n                assert_eq!(utf16, utf16!(\"debugger\"));\n            }\n            (\"default\", utf16) => {\n                assert_eq!(k, Keyword::Default);\n                assert_eq!(utf16, utf16!(\"default\"));\n            }\n            (\"delete\", utf16) => {\n                assert_eq!(k, Keyword::Delete);\n                assert_eq!(utf16, utf16!(\"delete\"));\n            }\n            (\"do\", utf16) => {\n                assert_eq!(k, Keyword::Do);\n                assert_eq!(utf16, utf16!(\"do\"));\n            }\n            (\"else\", utf16) => {\n                assert_eq!(k, Keyword::Else);\n                assert_eq!(utf16, utf16!(\"else\"));\n            }\n            (\"enum\", utf16) => {\n                assert_eq!(k, Keyword::Enum);\n                assert_eq!(utf16, utf16!(\"enum\"));\n            }\n            (\"extends\", utf16) => {\n                assert_eq!(k, Keyword::Extends);\n                assert_eq!(utf16, utf16!(\"extends\"));\n            }\n            (\"export\", utf16) => {\n                assert_eq!(k, Keyword::Export);\n                assert_eq!(utf16, utf16!(\"export\"));\n            }\n            (\"finally\", utf16) => {\n                assert_eq!(k, Keyword::Finally);\n                assert_eq!(utf16, utf16!(\"finally\"));\n            }\n            (\"for\", utf16) => {\n                assert_eq!(k, Keyword::For);\n                assert_eq!(utf16, utf16!(\"for\"));\n            }\n            (\"function\", utf16) => {\n                assert_eq!(k, Keyword::Function);\n                assert_eq!(utf16, utf16!(\"function\"));\n            }\n            (\"if\", utf16) => {\n                assert_eq!(k, Keyword::If);\n                assert_eq!(utf16, utf16!(\"if\"));\n            }\n            (\"in\", utf16) => {\n                assert_eq!(k, Keyword::In);\n                assert_eq!(utf16, utf16!(\"in\"));\n            }\n            (\"instanceof\", utf16) => {\n                assert_eq!(k, Keyword::InstanceOf);\n                assert_eq!(utf16, utf16!(\"instanceof\"));\n            }\n            (\"import\", utf16) => {\n                assert_eq!(k, Keyword::Import);\n                assert_eq!(utf16, utf16!(\"import\"));\n            }\n            (\"let\", utf16) => {\n                assert_eq!(k, Keyword::Let);\n                assert_eq!(utf16, utf16!(\"let\"));\n            }\n            (\"new\", utf16) => {\n                assert_eq!(k, Keyword::New);\n                assert_eq!(utf16, utf16!(\"new\"));\n            }\n            (\"of\", utf16) => {\n                assert_eq!(k, Keyword::Of);\n                assert_eq!(utf16, utf16!(\"of\"));\n            }\n            (\"return\", utf16) => {\n                assert_eq!(k, Keyword::Return);\n                assert_eq!(utf16, utf16!(\"return\"));\n            }\n            (\"super\", utf16) => {\n                assert_eq!(k, Keyword::Super);\n                assert_eq!(utf16, utf16!(\"super\"));\n            }\n            (\"switch\", utf16) => {\n                assert_eq!(k, Keyword::Switch);\n                assert_eq!(utf16, utf16!(\"switch\"));\n            }\n            (\"this\", utf16) => {\n                assert_eq!(k, Keyword::This);\n                assert_eq!(utf16, utf16!(\"this\"));\n            }\n            (\"throw\", utf16) => {\n                assert_eq!(k, Keyword::Throw);\n                assert_eq!(utf16, utf16!(\"throw\"));\n            }\n            (\"try\", utf16) => {\n                assert_eq!(k, Keyword::Try);\n                assert_eq!(utf16, utf16!(\"try\"));\n            }\n            (\"typeof\", utf16) => {\n                assert_eq!(k, Keyword::TypeOf);\n                assert_eq!(utf16, utf16!(\"typeof\"));\n            }\n            (\"var\", utf16) => {\n                assert_eq!(k, Keyword::Var);\n                assert_eq!(utf16, utf16!(\"var\"));\n            }\n            (\"void\", utf16) => {\n                assert_eq!(k, Keyword::Void);\n                assert_eq!(utf16, utf16!(\"void\"));\n            }\n            (\"while\", utf16) => {\n                assert_eq!(k, Keyword::While);\n                assert_eq!(utf16, utf16!(\"while\"));\n            }\n            (\"with\", utf16) => {\n                assert_eq!(k, Keyword::With);\n                assert_eq!(utf16, utf16!(\"with\"));\n            }\n            (\"yield\", utf16) => {\n                assert_eq!(k, Keyword::Yield);\n                assert_eq!(utf16, utf16!(\"yield\"));\n            }\n            (_, _) => unreachable!(\"unknown keyword {k:?} found\"),\n        }\n    }\n}\n\n#[test]\nfn to_sym() {\n    for k in all_keywords() {\n        match k.to_sym() {\n            Sym::AWAIT => assert_eq!(k, Keyword::Await),\n            Sym::ASYNC => assert_eq!(k, Keyword::Async),\n            Sym::BREAK => assert_eq!(k, Keyword::Break),\n            Sym::CASE => assert_eq!(k, Keyword::Case),\n            Sym::CATCH => assert_eq!(k, Keyword::Catch),\n            Sym::CLASS => assert_eq!(k, Keyword::Class),\n            Sym::CONTINUE => assert_eq!(k, Keyword::Continue),\n            Sym::CONST => assert_eq!(k, Keyword::Const),\n            Sym::DEBUGGER => assert_eq!(k, Keyword::Debugger),\n            Sym::DEFAULT => assert_eq!(k, Keyword::Default),\n            Sym::DELETE => assert_eq!(k, Keyword::Delete),\n            Sym::DO => assert_eq!(k, Keyword::Do),\n            Sym::ELSE => assert_eq!(k, Keyword::Else),\n            Sym::ENUM => assert_eq!(k, Keyword::Enum),\n            Sym::EXPORT => assert_eq!(k, Keyword::Export),\n            Sym::EXTENDS => assert_eq!(k, Keyword::Extends),\n            Sym::FINALLY => assert_eq!(k, Keyword::Finally),\n            Sym::FOR => assert_eq!(k, Keyword::For),\n            Sym::FUNCTION => assert_eq!(k, Keyword::Function),\n            Sym::IF => assert_eq!(k, Keyword::If),\n            Sym::IN => assert_eq!(k, Keyword::In),\n            Sym::INSTANCEOF => assert_eq!(k, Keyword::InstanceOf),\n            Sym::IMPORT => assert_eq!(k, Keyword::Import),\n            Sym::LET => assert_eq!(k, Keyword::Let),\n            Sym::NEW => assert_eq!(k, Keyword::New),\n            Sym::OF => assert_eq!(k, Keyword::Of),\n            Sym::RETURN => assert_eq!(k, Keyword::Return),\n            Sym::SUPER => assert_eq!(k, Keyword::Super),\n            Sym::SWITCH => assert_eq!(k, Keyword::Switch),\n            Sym::THIS => assert_eq!(k, Keyword::This),\n            Sym::THROW => assert_eq!(k, Keyword::Throw),\n            Sym::TRY => assert_eq!(k, Keyword::Try),\n            Sym::TYPEOF => assert_eq!(k, Keyword::TypeOf),\n            Sym::VAR => assert_eq!(k, Keyword::Var),\n            Sym::VOID => assert_eq!(k, Keyword::Void),\n            Sym::WHILE => assert_eq!(k, Keyword::While),\n            Sym::WITH => assert_eq!(k, Keyword::With),\n            Sym::YIELD => assert_eq!(k, Keyword::Yield),\n            _ => unreachable!(\"unknown keyword {k:?} found\"),\n        }\n    }\n}\n\n#[test]\nfn try_into_binary_op() {\n    for k in all_keywords() {\n        match k {\n            Keyword::InstanceOf | Keyword::In => assert!(BinaryOp::try_from(k).is_ok()),\n            Keyword::Await\n            | Keyword::Async\n            | Keyword::Break\n            | Keyword::Case\n            | Keyword::Catch\n            | Keyword::Class\n            | Keyword::Continue\n            | Keyword::Const\n            | Keyword::Debugger\n            | Keyword::Default\n            | Keyword::Delete\n            | Keyword::Do\n            | Keyword::Else\n            | Keyword::Enum\n            | Keyword::Export\n            | Keyword::Extends\n            | Keyword::Finally\n            | Keyword::For\n            | Keyword::Function\n            | Keyword::If\n            | Keyword::Import\n            | Keyword::Let\n            | Keyword::New\n            | Keyword::Of\n            | Keyword::Return\n            | Keyword::Super\n            | Keyword::Switch\n            | Keyword::This\n            | Keyword::Throw\n            | Keyword::Try\n            | Keyword::TypeOf\n            | Keyword::Using\n            | Keyword::Var\n            | Keyword::Void\n            | Keyword::While\n            | Keyword::With\n            | Keyword::Yield => assert!(BinaryOp::try_from(k).is_err()),\n        }\n    }\n}\n\n#[test]\nfn from_str() {\n    for k in all_keywords() {\n        let str = k.as_str().0;\n        assert_eq!(str.parse::<Keyword>().unwrap(), k);\n    }\n\n    for invalid in [\"\", \"testing\", \"invalid keyword\"] {\n        let result = invalid.parse::<Keyword>();\n        let error = result.unwrap_err();\n\n        assert_eq!(error.to_string(), \"invalid token\");\n    }\n}\n"
  },
  {
    "path": "core/ast/src/lib.rs",
    "content": "//! Boa's **`boa_ast`** crate implements an ECMAScript abstract syntax tree.\n//!\n//! # Crate Overview\n//! **`boa_ast`** contains representations of [**Parse Nodes**][grammar] as defined by the ECMAScript\n//! spec. Some `Parse Node`s are not represented by Boa's AST, because a lot of grammar productions\n//! are only used to throw [**Early Errors**][early], and don't influence the evaluation of the AST\n//! itself.\n//!\n//! Boa's AST is mainly split in three main components: [`Declaration`]s, [`Expression`]s and\n//! [`Statement`]s, with [`StatementList`] being the primordial Parse Node that combines\n//! all of them to create a proper AST.\n//!\n//! [grammar]: https://tc39.es/ecma262/#sec-syntactic-grammar\n//! [early]: https://tc39.es/ecma262/#sec-static-semantic-rules\n#![doc = include_str!(\"../ABOUT.md\")]\n#![doc(\n    html_logo_url = \"https://raw.githubusercontent.com/boa-dev/boa/main/assets/logo_black.svg\",\n    html_favicon_url = \"https://raw.githubusercontent.com/boa-dev/boa/main/assets/logo_black.svg\"\n)]\n#![cfg_attr(not(test), forbid(clippy::unwrap_used))]\n#![allow(\n    clippy::module_name_repetitions,\n    clippy::too_many_lines,\n    clippy::option_if_let_else\n)]\n\nmod module_item_list;\nmod position;\nmod punctuator;\nmod source;\nmod source_text;\nmod statement_list;\n\npub mod declaration;\npub mod expression;\npub mod function;\npub mod keyword;\npub mod operations;\npub mod pattern;\npub mod property;\npub mod scope;\npub mod scope_analyzer;\npub mod statement;\npub mod visitor;\n\nuse boa_interner::{Interner, Sym, ToIndentedString, ToInternedString};\nuse boa_string::{JsStr, JsString};\nuse expression::Identifier;\n\npub use self::{\n    declaration::Declaration,\n    expression::Expression,\n    keyword::Keyword,\n    module_item_list::{ModuleItem, ModuleItemList},\n    position::{\n        LinearPosition, LinearSpan, LinearSpanIgnoreEq, Position, PositionGroup, Span, Spanned,\n    },\n    punctuator::Punctuator,\n    source::{Module, Script},\n    source_text::SourceText,\n    statement::Statement,\n    statement_list::{StatementList, StatementListItem},\n};\n\n/// Utility to join multiple Nodes into a single string.\nfn join_nodes<N>(interner: &Interner, nodes: &[N]) -> String\nwhere\n    N: ToInternedString,\n{\n    let mut first = true;\n    let mut buf = String::new();\n    for e in nodes {\n        if first {\n            first = false;\n        } else {\n            buf.push_str(\", \");\n        }\n        buf.push_str(&e.to_interned_string(interner));\n    }\n    buf\n}\n\n/// Displays the body of a block or statement list.\n///\n/// This includes the curly braces at the start and end. This will not indent the first brace,\n/// but will indent the last brace.\nfn block_to_string(body: &StatementList, interner: &Interner, indentation: usize) -> String {\n    if body.statements().is_empty() {\n        \"{}\".to_owned()\n    } else {\n        format!(\n            \"{{\\n{}{}}}\",\n            body.to_indented_string(interner, indentation + 1),\n            \"    \".repeat(indentation)\n        )\n    }\n}\n\n/// Utility trait that adds a `UTF-16` escaped representation to every [`[u16]`][slice].\ntrait ToStringEscaped {\n    /// Decodes `self` as an `UTF-16` encoded string, escaping any unpaired surrogates by its\n    /// codepoint value.\n    fn to_string_escaped(&self) -> String;\n}\n\nimpl ToStringEscaped for [u16] {\n    fn to_string_escaped(&self) -> String {\n        char::decode_utf16(self.iter().copied())\n            .map(|r| match r {\n                Ok(c) => String::from(c),\n                Err(e) => format!(\"\\\\u{:04X}\", e.unpaired_surrogate()),\n            })\n            .collect()\n    }\n}\n\npub(crate) trait ToJsString {\n    fn to_js_string(&self, interner: &Interner) -> JsString;\n}\n\nimpl ToJsString for Sym {\n    #[allow(clippy::cast_possible_truncation)]\n    fn to_js_string(&self, interner: &Interner) -> JsString {\n        let utf16 = interner.resolve_expect(*self).utf16();\n        if interner.is_latin1(*self) {\n            let bytes: Vec<u8> = utf16.iter().map(|&c| c as u8).collect();\n            JsString::from(JsStr::latin1(&bytes))\n        } else {\n            JsString::from(utf16)\n        }\n    }\n}\n\nimpl ToJsString for Identifier {\n    fn to_js_string(&self, interner: &Interner) -> JsString {\n        self.sym().to_js_string(interner)\n    }\n}\n"
  },
  {
    "path": "core/ast/src/module_item_list/mod.rs",
    "content": "//! Module item list AST nodes.\n//!\n//! More information:\n//!  - [ECMAScript specification][spec]\n//!\n//! [spec]: https://tc39.es/ecma262/#sec-modules\n\nuse crate::{\n    StatementListItem,\n    declaration::{\n        ExportDeclaration, ExportEntry, ExportSpecifier, ImportAttribute, ImportDeclaration,\n        ImportEntry, ImportKind, ImportName, IndirectExportEntry, LocalExportEntry,\n        ModuleSpecifier, ReExportImportName, ReExportKind,\n    },\n    operations::{BoundNamesVisitor, bound_names},\n    visitor::{VisitWith, Visitor, VisitorMut},\n};\nuse boa_interner::Sym;\nuse indexmap::IndexSet;\nuse rustc_hash::{FxHashSet, FxHasher};\nuse std::{convert::Infallible, hash::BuildHasherDefault, ops::ControlFlow};\n\n/// Module item list AST node.\n///\n/// It contains a list of module items.\n///\n/// More information:\n///  - [ECMAScript specification][spec]\n///\n/// [spec]: https://tc39.es/ecma262/#prod-ModuleItemList\n#[cfg_attr(feature = \"serde\", derive(serde::Serialize, serde::Deserialize))]\n#[derive(Clone, Debug, Default, PartialEq)]\npub struct ModuleItemList {\n    items: Box<[ModuleItem]>,\n}\n\nimpl ModuleItemList {\n    /// Gets the list of module items.\n    #[inline]\n    #[must_use]\n    pub const fn items(&self) -> &[ModuleItem] {\n        &self.items\n    }\n\n    /// Abstract operation [`ExportedNames`][spec].\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-static-semantics-exportednames\n    #[inline]\n    #[must_use]\n    pub fn exported_names(&self) -> Vec<Sym> {\n        #[derive(Debug)]\n        struct ExportedItemsVisitor<'vec>(&'vec mut Vec<Sym>);\n\n        impl<'ast> Visitor<'ast> for ExportedItemsVisitor<'_> {\n            type BreakTy = Infallible;\n\n            fn visit_import_declaration(\n                &mut self,\n                _: &'ast ImportDeclaration,\n            ) -> ControlFlow<Self::BreakTy> {\n                ControlFlow::Continue(())\n            }\n            fn visit_statement_list_item(\n                &mut self,\n                _: &'ast StatementListItem,\n            ) -> ControlFlow<Self::BreakTy> {\n                ControlFlow::Continue(())\n            }\n            fn visit_export_specifier(\n                &mut self,\n                node: &'ast ExportSpecifier,\n            ) -> ControlFlow<Self::BreakTy> {\n                self.0.push(node.alias());\n                ControlFlow::Continue(())\n            }\n            fn visit_export_declaration(\n                &mut self,\n                node: &'ast ExportDeclaration,\n            ) -> ControlFlow<Self::BreakTy> {\n                match node {\n                    ExportDeclaration::ReExport { kind, .. } => {\n                        match kind {\n                            ReExportKind::Namespaced { name: Some(name) } => self.0.push(*name),\n                            ReExportKind::Namespaced { name: None } => {}\n                            ReExportKind::Named { names } => {\n                                for specifier in &**names {\n                                    self.visit_export_specifier(specifier)?;\n                                }\n                            }\n                        }\n                        ControlFlow::Continue(())\n                    }\n                    ExportDeclaration::List(list) => {\n                        for specifier in &**list {\n                            self.visit_export_specifier(specifier)?;\n                        }\n                        ControlFlow::Continue(())\n                    }\n                    ExportDeclaration::VarStatement(var) => {\n                        BoundNamesVisitor(self.0).visit_var_declaration(var)\n                    }\n                    ExportDeclaration::Declaration(decl) => {\n                        BoundNamesVisitor(self.0).visit_declaration(decl)\n                    }\n                    ExportDeclaration::DefaultFunctionDeclaration(_)\n                    | ExportDeclaration::DefaultGeneratorDeclaration(_)\n                    | ExportDeclaration::DefaultAsyncFunctionDeclaration(_)\n                    | ExportDeclaration::DefaultAsyncGeneratorDeclaration(_)\n                    | ExportDeclaration::DefaultClassDeclaration(_)\n                    | ExportDeclaration::DefaultAssignmentExpression(_) => {\n                        self.0.push(Sym::DEFAULT);\n                        ControlFlow::Continue(())\n                    }\n                }\n            }\n        }\n\n        let mut names = Vec::new();\n\n        let _ = ExportedItemsVisitor(&mut names).visit_module_item_list(self);\n\n        names\n    }\n\n    /// Abstract operation [`ExportedBindings`][spec].\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-static-semantics-exportedbindings\n    #[inline]\n    #[must_use]\n    pub fn exported_bindings(&self) -> FxHashSet<Sym> {\n        #[derive(Debug)]\n        struct ExportedBindingsVisitor<'vec>(&'vec mut FxHashSet<Sym>);\n\n        impl<'ast> Visitor<'ast> for ExportedBindingsVisitor<'_> {\n            type BreakTy = Infallible;\n\n            fn visit_import_declaration(\n                &mut self,\n                _: &'ast ImportDeclaration,\n            ) -> ControlFlow<Self::BreakTy> {\n                ControlFlow::Continue(())\n            }\n            fn visit_statement_list_item(\n                &mut self,\n                _: &'ast StatementListItem,\n            ) -> ControlFlow<Self::BreakTy> {\n                ControlFlow::Continue(())\n            }\n            fn visit_export_specifier(\n                &mut self,\n                node: &'ast ExportSpecifier,\n            ) -> ControlFlow<Self::BreakTy> {\n                self.0.insert(node.private_name());\n                ControlFlow::Continue(())\n            }\n            fn visit_export_declaration(\n                &mut self,\n                node: &'ast ExportDeclaration,\n            ) -> ControlFlow<Self::BreakTy> {\n                let name = match node {\n                    ExportDeclaration::ReExport { .. } => return ControlFlow::Continue(()),\n                    ExportDeclaration::List(list) => {\n                        for specifier in &**list {\n                            self.visit_export_specifier(specifier)?;\n                        }\n                        return ControlFlow::Continue(());\n                    }\n                    ExportDeclaration::DefaultAssignmentExpression(expr) => {\n                        return BoundNamesVisitor(self.0).visit_expression(expr);\n                    }\n                    ExportDeclaration::VarStatement(var) => {\n                        return BoundNamesVisitor(self.0).visit_var_declaration(var);\n                    }\n                    ExportDeclaration::Declaration(decl) => {\n                        return BoundNamesVisitor(self.0).visit_declaration(decl);\n                    }\n                    ExportDeclaration::DefaultFunctionDeclaration(f) => f.name(),\n                    ExportDeclaration::DefaultGeneratorDeclaration(g) => g.name(),\n                    ExportDeclaration::DefaultAsyncFunctionDeclaration(af) => af.name(),\n                    ExportDeclaration::DefaultAsyncGeneratorDeclaration(ag) => ag.name(),\n                    ExportDeclaration::DefaultClassDeclaration(cl) => cl.name(),\n                };\n\n                self.0.insert(name.sym());\n\n                ControlFlow::Continue(())\n            }\n        }\n\n        let mut names = FxHashSet::default();\n\n        let _ = ExportedBindingsVisitor(&mut names).visit_module_item_list(self);\n\n        names\n    }\n\n    /// Operation [`ModuleRequests`][spec].\n    ///\n    /// Gets the list of modules that need to be fetched by the module resolver to link this module.\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-static-semantics-modulerequests\n    #[inline]\n    #[must_use]\n    pub fn requests(&self) -> IndexSet<Sym, BuildHasherDefault<FxHasher>> {\n        #[derive(Debug)]\n        struct RequestsVisitor<'vec>(&'vec mut IndexSet<Sym, BuildHasherDefault<FxHasher>>);\n\n        impl<'ast> Visitor<'ast> for RequestsVisitor<'_> {\n            type BreakTy = Infallible;\n\n            fn visit_statement_list_item(\n                &mut self,\n                _: &'ast StatementListItem,\n            ) -> ControlFlow<Self::BreakTy> {\n                ControlFlow::Continue(())\n            }\n            fn visit_module_specifier(\n                &mut self,\n                node: &'ast ModuleSpecifier,\n            ) -> ControlFlow<Self::BreakTy> {\n                self.0.insert(node.sym());\n                ControlFlow::Continue(())\n            }\n        }\n\n        let mut requests = IndexSet::default();\n\n        let _ = RequestsVisitor(&mut requests).visit_module_item_list(self);\n\n        requests\n    }\n\n    /// Operation [`ImportEntries`][spec].\n    ///\n    /// Gets the list of import entries of this module.\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-static-semantics-importentries\n    #[inline]\n    #[must_use]\n    pub fn import_entries(&self) -> Vec<ImportEntry> {\n        #[derive(Debug)]\n        struct ImportEntriesVisitor<'vec>(&'vec mut Vec<ImportEntry>);\n\n        impl<'ast> Visitor<'ast> for ImportEntriesVisitor<'_> {\n            type BreakTy = Infallible;\n\n            fn visit_module_item(&mut self, node: &'ast ModuleItem) -> ControlFlow<Self::BreakTy> {\n                match node {\n                    ModuleItem::ImportDeclaration(import) => self.visit_import_declaration(import),\n                    ModuleItem::ExportDeclaration(_) | ModuleItem::StatementListItem(_) => {\n                        ControlFlow::Continue(())\n                    }\n                }\n            }\n\n            fn visit_import_declaration(\n                &mut self,\n                node: &'ast ImportDeclaration,\n            ) -> ControlFlow<Self::BreakTy> {\n                let module = node.specifier().sym();\n                let attributes: Box<[ImportAttribute]> = Box::from(node.attributes());\n\n                if let Some(default) = node.default() {\n                    self.0.push(ImportEntry::new(\n                        module,\n                        ImportName::Name(Sym::DEFAULT),\n                        default,\n                        attributes.clone(),\n                    ));\n                }\n\n                match node.kind() {\n                    ImportKind::DefaultOrUnnamed => {}\n                    ImportKind::Namespaced { binding } => {\n                        self.0.push(ImportEntry::new(\n                            module,\n                            ImportName::Namespace,\n                            *binding,\n                            attributes.clone(),\n                        ));\n                    }\n                    ImportKind::Named { names } => {\n                        for name in &**names {\n                            self.0.push(ImportEntry::new(\n                                module,\n                                ImportName::Name(name.export_name()),\n                                name.binding(),\n                                attributes.clone(),\n                            ));\n                        }\n                    }\n                }\n\n                ControlFlow::Continue(())\n            }\n        }\n\n        let mut entries = Vec::default();\n\n        let _ = ImportEntriesVisitor(&mut entries).visit_module_item_list(self);\n\n        entries\n    }\n\n    /// Operation [`ExportEntries`][spec].\n    ///\n    /// Gets the list of export entries of this module.\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-static-semantics-exportentries\n    #[inline]\n    #[must_use]\n    pub fn export_entries(&self) -> Vec<ExportEntry> {\n        #[derive(Debug)]\n        struct ExportEntriesVisitor<'vec>(&'vec mut Vec<ExportEntry>);\n\n        impl<'ast> Visitor<'ast> for ExportEntriesVisitor<'_> {\n            type BreakTy = Infallible;\n\n            fn visit_module_item(&mut self, node: &'ast ModuleItem) -> ControlFlow<Self::BreakTy> {\n                match node {\n                    ModuleItem::ExportDeclaration(import) => self.visit_export_declaration(import),\n                    ModuleItem::ImportDeclaration(_) | ModuleItem::StatementListItem(_) => {\n                        ControlFlow::Continue(())\n                    }\n                }\n            }\n\n            fn visit_export_declaration(\n                &mut self,\n                node: &'ast ExportDeclaration,\n            ) -> ControlFlow<Self::BreakTy> {\n                let name = match node {\n                    ExportDeclaration::ReExport {\n                        kind,\n                        specifier,\n                        attributes,\n                    } => {\n                        let module = specifier.sym();\n                        let attrs = attributes.clone();\n\n                        match kind {\n                            ReExportKind::Namespaced { name: Some(name) } => {\n                                self.0.push(\n                                    IndirectExportEntry::new(\n                                        module,\n                                        ReExportImportName::Star,\n                                        *name,\n                                        attrs.clone(),\n                                    )\n                                    .into(),\n                                );\n                            }\n                            ReExportKind::Namespaced { name: None } => {\n                                self.0.push(ExportEntry::StarReExport {\n                                    module_request: module,\n                                    attributes: attrs.clone(),\n                                });\n                            }\n\n                            ReExportKind::Named { names } => {\n                                for name in &**names {\n                                    self.0.push(\n                                        IndirectExportEntry::new(\n                                            module,\n                                            ReExportImportName::Name(name.private_name()),\n                                            name.alias(),\n                                            attrs.clone(),\n                                        )\n                                        .into(),\n                                    );\n                                }\n                            }\n                        }\n\n                        return ControlFlow::Continue(());\n                    }\n                    ExportDeclaration::List(names) => {\n                        for name in &**names {\n                            self.0.push(\n                                LocalExportEntry::new(name.private_name(), name.alias()).into(),\n                            );\n                        }\n                        return ControlFlow::Continue(());\n                    }\n                    ExportDeclaration::VarStatement(var) => {\n                        for name in bound_names(var) {\n                            self.0.push(LocalExportEntry::new(name, name).into());\n                        }\n                        return ControlFlow::Continue(());\n                    }\n                    ExportDeclaration::Declaration(decl) => {\n                        for name in bound_names(decl) {\n                            self.0.push(LocalExportEntry::new(name, name).into());\n                        }\n                        return ControlFlow::Continue(());\n                    }\n                    ExportDeclaration::DefaultFunctionDeclaration(f) => f.name().sym(),\n                    ExportDeclaration::DefaultGeneratorDeclaration(g) => g.name().sym(),\n                    ExportDeclaration::DefaultAsyncFunctionDeclaration(af) => af.name().sym(),\n                    ExportDeclaration::DefaultAsyncGeneratorDeclaration(ag) => ag.name().sym(),\n                    ExportDeclaration::DefaultClassDeclaration(c) => c.name().sym(),\n                    ExportDeclaration::DefaultAssignmentExpression(_) => Sym::DEFAULT_EXPORT,\n                };\n\n                self.0\n                    .push(LocalExportEntry::new(name, Sym::DEFAULT).into());\n\n                ControlFlow::Continue(())\n            }\n        }\n\n        let mut entries = Vec::default();\n\n        let _ = ExportEntriesVisitor(&mut entries).visit_module_item_list(self);\n\n        entries\n    }\n}\n\nimpl<T> From<T> for ModuleItemList\nwhere\n    T: Into<Box<[ModuleItem]>>,\n{\n    #[inline]\n    fn from(items: T) -> Self {\n        Self {\n            items: items.into(),\n        }\n    }\n}\n\nimpl VisitWith for ModuleItemList {\n    fn visit_with<'a, V>(&'a self, visitor: &mut V) -> ControlFlow<V::BreakTy>\n    where\n        V: Visitor<'a>,\n    {\n        for item in &*self.items {\n            visitor.visit_module_item(item)?;\n        }\n\n        ControlFlow::Continue(())\n    }\n\n    fn visit_with_mut<'a, V>(&'a mut self, visitor: &mut V) -> ControlFlow<V::BreakTy>\n    where\n        V: VisitorMut<'a>,\n    {\n        for item in &mut *self.items {\n            visitor.visit_module_item_mut(item)?;\n        }\n\n        ControlFlow::Continue(())\n    }\n}\n\n/// Module item AST node.\n///\n/// This is an extension over a [`StatementList`](crate::StatementList), which can also include\n/// multiple [`ImportDeclaration`] and [`ExportDeclaration`] nodes, along with\n/// [`StatementListItem`] nodes.\n///\n/// More information:\n///  - [ECMAScript specification][spec]\n///\n/// [spec]: https://tc39.es/ecma262/#prod-ModuleItem\n#[cfg_attr(feature = \"serde\", derive(serde::Serialize, serde::Deserialize))]\n#[derive(Clone, Debug, PartialEq)]\npub enum ModuleItem {\n    /// See [`ImportDeclaration`].\n    ImportDeclaration(ImportDeclaration),\n    /// See [`ExportDeclaration`].\n    ExportDeclaration(Box<ExportDeclaration>),\n    /// See [`StatementListItem`].\n    StatementListItem(StatementListItem),\n}\n\nimpl VisitWith for ModuleItem {\n    fn visit_with<'a, V>(&'a self, visitor: &mut V) -> ControlFlow<V::BreakTy>\n    where\n        V: Visitor<'a>,\n    {\n        match self {\n            Self::ImportDeclaration(i) => visitor.visit_import_declaration(i),\n            Self::ExportDeclaration(e) => visitor.visit_export_declaration(e),\n            Self::StatementListItem(s) => visitor.visit_statement_list_item(s),\n        }\n    }\n\n    fn visit_with_mut<'a, V>(&'a mut self, visitor: &mut V) -> ControlFlow<V::BreakTy>\n    where\n        V: VisitorMut<'a>,\n    {\n        match self {\n            Self::ImportDeclaration(i) => visitor.visit_import_declaration_mut(i),\n            Self::ExportDeclaration(e) => visitor.visit_export_declaration_mut(e),\n            Self::StatementListItem(s) => visitor.visit_statement_list_item_mut(s),\n        }\n    }\n}\n"
  },
  {
    "path": "core/ast/src/operations/mod.rs",
    "content": "//! Definitions of various **Syntax-Directed Operations** used in the [spec].\n//!\n//! [spec]: https://tc39.es/ecma262/#sec-syntax-directed-operations\n\nuse core::ops::ControlFlow;\nuse std::convert::Infallible;\n\nuse boa_interner::{Interner, Sym};\nuse rustc_hash::FxHashSet;\n\nuse crate::{\n    Declaration, Expression, LinearSpan, ModuleItem, Script, Statement, StatementList,\n    StatementListItem,\n    declaration::{\n        Binding, ExportDeclaration, ImportDeclaration, LexicalDeclaration, VarDeclaration, Variable,\n    },\n    expression::{\n        Await, Call, Identifier, NewTarget, OptionalOperationKind, SuperCall, This, Yield,\n        access::{PrivatePropertyAccess, SuperPropertyAccess},\n        literal::PropertyDefinition,\n        operator::BinaryInPrivate,\n    },\n    function::{\n        ArrowFunction, AsyncArrowFunction, AsyncFunctionDeclaration, AsyncFunctionExpression,\n        AsyncGeneratorDeclaration, AsyncGeneratorExpression, ClassDeclaration, ClassElement,\n        ClassElementName, ClassExpression, FormalParameterList, FunctionBody, FunctionDeclaration,\n        FunctionExpression, GeneratorDeclaration, GeneratorExpression, PrivateFieldDefinition,\n    },\n    statement::{\n        LabelledItem, With,\n        iteration::{ForLoopInitializer, IterableLoopInitializer},\n    },\n    visitor::{NodeRef, VisitWith, Visitor},\n};\n\n#[cfg(test)]\nmod tests;\n\n/// Represents all the possible symbols searched for by the [`Contains`][contains] operation.\n///\n/// [contains]: https://tc39.es/ecma262/#sec-syntax-directed-operations-contains\n#[derive(Clone, Copy, Debug, PartialEq, Eq)]\n#[non_exhaustive]\npub enum ContainsSymbol {\n    /// A node with the `super` keyword (`super(args)` or `super.prop`).\n    Super,\n    /// A super property access (`super.prop`).\n    SuperProperty,\n    /// A super constructor call (`super(args)`).\n    SuperCall,\n    /// A yield expression (`yield 5`).\n    YieldExpression,\n    /// An await expression (`await 4`).\n    AwaitExpression,\n    /// The new target expression (`new.target`).\n    NewTarget,\n    /// The body of a class definition.\n    ClassBody,\n    /// The super class of a class definition.\n    ClassHeritage,\n    /// A this expression (`this`).\n    This,\n    /// A method definition.\n    MethodDefinition,\n    /// The `BindingIdentifier` \"eval\" or \"arguments\".\n    EvalOrArguments,\n    /// A direct call to `eval`.\n    DirectEval,\n}\n\n/// Returns `true` if the node contains the given symbol.\n///\n/// This is equivalent to the [`Contains`][spec] syntax operation in the spec.\n///\n/// [spec]: https://tc39.es/ecma262/#sec-static-semantics-contains\n#[must_use]\npub fn contains<N>(node: &N, symbol: ContainsSymbol) -> bool\nwhere\n    N: VisitWith,\n{\n    /// Visitor used by the function to search for a specific symbol in a node.\n    #[derive(Debug, Clone, Copy)]\n    struct ContainsVisitor(ContainsSymbol);\n\n    impl ContainsVisitor {\n        fn visit_contains_eval(&mut self, contains_direct_eval: bool) -> ControlFlow<()> {\n            if self.0 == ContainsSymbol::DirectEval && contains_direct_eval {\n                ControlFlow::Break(())\n            } else {\n                ControlFlow::Continue(())\n            }\n        }\n    }\n\n    impl<'ast> Visitor<'ast> for ContainsVisitor {\n        type BreakTy = ();\n\n        fn visit_with(&mut self, node: &'ast With) -> ControlFlow<Self::BreakTy> {\n            self.visit_expression(node.expression())?;\n            node.statement().visit_with(self)\n        }\n\n        fn visit_call(&mut self, node: &'ast Call) -> ControlFlow<Self::BreakTy> {\n            if self.0 == ContainsSymbol::DirectEval\n                && let Expression::Identifier(ident) = node.function().flatten()\n                && ident.sym() == Sym::EVAL\n            {\n                return ControlFlow::Break(());\n            }\n\n            self.visit_expression(node.function())?;\n            for arg in node.args() {\n                self.visit_expression(arg)?;\n            }\n            ControlFlow::Continue(())\n        }\n\n        fn visit_identifier(&mut self, node: &'ast Identifier) -> ControlFlow<Self::BreakTy> {\n            if self.0 == ContainsSymbol::EvalOrArguments\n                && (node.sym() == Sym::EVAL || node.sym() == Sym::ARGUMENTS)\n            {\n                return ControlFlow::Break(());\n            }\n            ControlFlow::Continue(())\n        }\n\n        fn visit_function_expression(\n            &mut self,\n            node: &'ast FunctionExpression,\n        ) -> ControlFlow<Self::BreakTy> {\n            self.visit_contains_eval(node.contains_direct_eval)?;\n            ControlFlow::Continue(())\n        }\n\n        fn visit_function_declaration(\n            &mut self,\n            node: &'ast FunctionDeclaration,\n        ) -> ControlFlow<Self::BreakTy> {\n            self.visit_contains_eval(node.contains_direct_eval)?;\n            ControlFlow::Continue(())\n        }\n\n        fn visit_async_function_expression(\n            &mut self,\n            node: &'ast AsyncFunctionExpression,\n        ) -> ControlFlow<Self::BreakTy> {\n            self.visit_contains_eval(node.contains_direct_eval)?;\n            ControlFlow::Continue(())\n        }\n\n        fn visit_async_function_declaration(\n            &mut self,\n            node: &'ast AsyncFunctionDeclaration,\n        ) -> ControlFlow<Self::BreakTy> {\n            self.visit_contains_eval(node.contains_direct_eval)?;\n            ControlFlow::Continue(())\n        }\n\n        fn visit_generator_expression(\n            &mut self,\n            node: &'ast GeneratorExpression,\n        ) -> ControlFlow<Self::BreakTy> {\n            self.visit_contains_eval(node.contains_direct_eval)?;\n            ControlFlow::Continue(())\n        }\n\n        fn visit_generator_declaration(\n            &mut self,\n            node: &'ast GeneratorDeclaration,\n        ) -> ControlFlow<Self::BreakTy> {\n            self.visit_contains_eval(node.contains_direct_eval)?;\n            ControlFlow::Continue(())\n        }\n\n        fn visit_async_generator_expression(\n            &mut self,\n            node: &'ast AsyncGeneratorExpression,\n        ) -> ControlFlow<Self::BreakTy> {\n            self.visit_contains_eval(node.contains_direct_eval)?;\n            ControlFlow::Continue(())\n        }\n\n        fn visit_async_generator_declaration(\n            &mut self,\n            node: &'ast AsyncGeneratorDeclaration,\n        ) -> ControlFlow<Self::BreakTy> {\n            self.visit_contains_eval(node.contains_direct_eval)?;\n            ControlFlow::Continue(())\n        }\n\n        fn visit_class_expression(\n            &mut self,\n            node: &'ast ClassExpression,\n        ) -> ControlFlow<Self::BreakTy> {\n            if !node.elements().is_empty() && self.0 == ContainsSymbol::ClassBody {\n                return ControlFlow::Break(());\n            }\n\n            if node.super_ref().is_some() && self.0 == ContainsSymbol::ClassHeritage {\n                return ControlFlow::Break(());\n            }\n\n            node.visit_with(self)\n        }\n\n        fn visit_class_declaration(\n            &mut self,\n            node: &'ast ClassDeclaration,\n        ) -> ControlFlow<Self::BreakTy> {\n            if !node.elements().is_empty() && self.0 == ContainsSymbol::ClassBody {\n                return ControlFlow::Break(());\n            }\n\n            if node.super_ref().is_some() && self.0 == ContainsSymbol::ClassHeritage {\n                return ControlFlow::Break(());\n            }\n\n            node.visit_with(self)\n        }\n\n        // `ComputedPropertyContains`: https://tc39.es/ecma262/#sec-static-semantics-computedpropertycontains\n        fn visit_class_element(&mut self, node: &'ast ClassElement) -> ControlFlow<Self::BreakTy> {\n            match node {\n                ClassElement::MethodDefinition(m) => {\n                    if self.0 == ContainsSymbol::DirectEval {\n                        return ControlFlow::Continue(());\n                    }\n\n                    if let ClassElementName::PropertyName(name) = m.name() {\n                        name.visit_with(self)\n                    } else {\n                        ControlFlow::Continue(())\n                    }\n                }\n                ClassElement::FieldDefinition(field)\n                | ClassElement::StaticFieldDefinition(field) => field.name.visit_with(self),\n                _ => ControlFlow::Continue(()),\n            }\n        }\n\n        fn visit_property_definition(\n            &mut self,\n            node: &'ast PropertyDefinition,\n        ) -> ControlFlow<Self::BreakTy> {\n            if let PropertyDefinition::MethodDefinition(m) = node {\n                if self.0 == ContainsSymbol::DirectEval {\n                    return ControlFlow::Continue(());\n                }\n\n                if self.0 == ContainsSymbol::MethodDefinition {\n                    return ControlFlow::Break(());\n                }\n                return m.name().visit_with(self);\n            }\n\n            node.visit_with(self)\n        }\n\n        fn visit_arrow_function(\n            &mut self,\n            node: &'ast ArrowFunction,\n        ) -> ControlFlow<Self::BreakTy> {\n            if ![\n                ContainsSymbol::NewTarget,\n                ContainsSymbol::SuperProperty,\n                ContainsSymbol::SuperCall,\n                ContainsSymbol::Super,\n                ContainsSymbol::This,\n                ContainsSymbol::DirectEval,\n            ]\n            .contains(&self.0)\n            {\n                return ControlFlow::Continue(());\n            }\n\n            node.visit_with(self)\n        }\n\n        fn visit_async_arrow_function(\n            &mut self,\n            node: &'ast AsyncArrowFunction,\n        ) -> ControlFlow<Self::BreakTy> {\n            if ![\n                ContainsSymbol::NewTarget,\n                ContainsSymbol::SuperProperty,\n                ContainsSymbol::SuperCall,\n                ContainsSymbol::Super,\n                ContainsSymbol::This,\n                ContainsSymbol::DirectEval,\n            ]\n            .contains(&self.0)\n            {\n                return ControlFlow::Continue(());\n            }\n\n            node.visit_with(self)\n        }\n\n        fn visit_super_property_access(\n            &mut self,\n            node: &'ast SuperPropertyAccess,\n        ) -> ControlFlow<Self::BreakTy> {\n            if [ContainsSymbol::SuperProperty, ContainsSymbol::Super].contains(&self.0) {\n                return ControlFlow::Break(());\n            }\n            node.visit_with(self)\n        }\n\n        fn visit_super_call(&mut self, node: &'ast SuperCall) -> ControlFlow<Self::BreakTy> {\n            if [ContainsSymbol::SuperCall, ContainsSymbol::Super].contains(&self.0) {\n                return ControlFlow::Break(());\n            }\n            node.visit_with(self)\n        }\n\n        fn visit_yield(&mut self, node: &'ast Yield) -> ControlFlow<Self::BreakTy> {\n            if self.0 == ContainsSymbol::YieldExpression {\n                return ControlFlow::Break(());\n            }\n\n            node.visit_with(self)\n        }\n\n        fn visit_await(&mut self, node: &'ast Await) -> ControlFlow<Self::BreakTy> {\n            if self.0 == ContainsSymbol::AwaitExpression {\n                return ControlFlow::Break(());\n            }\n\n            node.visit_with(self)\n        }\n\n        fn visit_this(&mut self, _node: &'ast This) -> ControlFlow<Self::BreakTy> {\n            if self.0 == ContainsSymbol::This {\n                return ControlFlow::Break(());\n            }\n            ControlFlow::Continue(())\n        }\n\n        fn visit_new_target(&mut self, _node: &'ast NewTarget) -> ControlFlow<Self::BreakTy> {\n            if self.0 == ContainsSymbol::NewTarget {\n                return ControlFlow::Break(());\n            }\n            ControlFlow::Continue(())\n        }\n    }\n\n    node.visit_with(&mut ContainsVisitor(symbol)).is_break()\n}\n\n/// Returns true if the node contains an identifier reference with name `arguments`.\n///\n/// This is equivalent to the [`ContainsArguments`][spec] syntax operation in the spec.\n///\n/// [spec]: https://tc39.es/ecma262/#sec-static-semantics-containsarguments\n#[must_use]\npub fn contains_arguments<N>(node: &N) -> bool\nwhere\n    N: VisitWith,\n{\n    /// Visitor used by the function to search for an identifier with the name `arguments`.\n    #[derive(Debug, Clone, Copy)]\n    struct ContainsArgsVisitor;\n\n    impl<'ast> Visitor<'ast> for ContainsArgsVisitor {\n        type BreakTy = ();\n\n        fn visit_identifier(&mut self, node: &'ast Identifier) -> ControlFlow<Self::BreakTy> {\n            if node.sym() == Sym::ARGUMENTS {\n                ControlFlow::Break(())\n            } else {\n                ControlFlow::Continue(())\n            }\n        }\n\n        fn visit_function_expression(\n            &mut self,\n            _: &'ast FunctionExpression,\n        ) -> ControlFlow<Self::BreakTy> {\n            ControlFlow::Continue(())\n        }\n\n        fn visit_function_declaration(\n            &mut self,\n            _: &'ast FunctionDeclaration,\n        ) -> ControlFlow<Self::BreakTy> {\n            ControlFlow::Continue(())\n        }\n\n        fn visit_async_function_expression(\n            &mut self,\n            _: &'ast AsyncFunctionExpression,\n        ) -> ControlFlow<Self::BreakTy> {\n            ControlFlow::Continue(())\n        }\n\n        fn visit_async_function_declaration(\n            &mut self,\n            _: &'ast AsyncFunctionDeclaration,\n        ) -> ControlFlow<Self::BreakTy> {\n            ControlFlow::Continue(())\n        }\n\n        fn visit_generator_expression(\n            &mut self,\n            _: &'ast GeneratorExpression,\n        ) -> ControlFlow<Self::BreakTy> {\n            ControlFlow::Continue(())\n        }\n\n        fn visit_generator_declaration(\n            &mut self,\n            _: &'ast GeneratorDeclaration,\n        ) -> ControlFlow<Self::BreakTy> {\n            ControlFlow::Continue(())\n        }\n\n        fn visit_async_generator_expression(\n            &mut self,\n            _: &'ast AsyncGeneratorExpression,\n        ) -> ControlFlow<Self::BreakTy> {\n            ControlFlow::Continue(())\n        }\n\n        fn visit_async_generator_declaration(\n            &mut self,\n            _: &'ast AsyncGeneratorDeclaration,\n        ) -> ControlFlow<Self::BreakTy> {\n            ControlFlow::Continue(())\n        }\n\n        fn visit_class_element(&mut self, node: &'ast ClassElement) -> ControlFlow<Self::BreakTy> {\n            if let ClassElement::MethodDefinition(m) = node\n                && let ClassElementName::PropertyName(name) = m.name()\n            {\n                return name.visit_with(self);\n            }\n\n            node.visit_with(self)\n        }\n\n        fn visit_property_definition(\n            &mut self,\n            node: &'ast PropertyDefinition,\n        ) -> ControlFlow<Self::BreakTy> {\n            if let PropertyDefinition::MethodDefinition(m) = node {\n                m.name().visit_with(self)\n            } else {\n                node.visit_with(self)\n            }\n        }\n    }\n    node.visit_with(&mut ContainsArgsVisitor).is_break()\n}\n\n/// Returns `true` if `method` has a super call in its parameters or body.\n///\n/// This is equivalent to the [`HasDirectSuper`][spec] syntax operation in the spec.\n///\n/// [spec]: https://tc39.es/ecma262/#sec-static-semantics-hasdirectsuper\n#[must_use]\n#[inline]\npub fn has_direct_super_new(params: &FormalParameterList, body: &FunctionBody) -> bool {\n    contains(params, ContainsSymbol::SuperCall) || contains(body, ContainsSymbol::SuperCall)\n}\n\n/// A container that [`BoundNamesVisitor`] can use to push the found identifiers.\npub(crate) trait IdentList {\n    fn add(&mut self, value: Sym, function: bool);\n}\n\nimpl IdentList for Vec<Sym> {\n    fn add(&mut self, value: Sym, _function: bool) {\n        self.push(value);\n    }\n}\n\nimpl IdentList for Vec<(Sym, bool)> {\n    fn add(&mut self, value: Sym, function: bool) {\n        self.push((value, function));\n    }\n}\n\nimpl IdentList for FxHashSet<Sym> {\n    fn add(&mut self, value: Sym, _function: bool) {\n        self.insert(value);\n    }\n}\n\n/// The [`Visitor`] used to obtain the bound names of a node.\n#[derive(Debug)]\npub(crate) struct BoundNamesVisitor<'a, T: IdentList>(pub(crate) &'a mut T);\n\nimpl<'ast, T: IdentList> Visitor<'ast> for BoundNamesVisitor<'_, T> {\n    type BreakTy = Infallible;\n\n    fn visit_identifier(&mut self, node: &'ast Identifier) -> ControlFlow<Self::BreakTy> {\n        self.0.add(node.sym(), false);\n        ControlFlow::Continue(())\n    }\n\n    fn visit_expression(&mut self, _: &'ast Expression) -> ControlFlow<Self::BreakTy> {\n        ControlFlow::Continue(())\n    }\n\n    fn visit_function_expression(\n        &mut self,\n        node: &'ast FunctionExpression,\n    ) -> ControlFlow<Self::BreakTy> {\n        if let Some(ident) = node.name() {\n            self.0.add(ident.sym(), true);\n        }\n        ControlFlow::Continue(())\n    }\n\n    fn visit_function_declaration(\n        &mut self,\n        node: &'ast FunctionDeclaration,\n    ) -> ControlFlow<Self::BreakTy> {\n        self.0.add(node.name().sym(), true);\n        ControlFlow::Continue(())\n    }\n\n    fn visit_generator_expression(\n        &mut self,\n        node: &'ast GeneratorExpression,\n    ) -> ControlFlow<Self::BreakTy> {\n        if let Some(ident) = node.name() {\n            self.0.add(ident.sym(), false);\n        }\n        ControlFlow::Continue(())\n    }\n\n    fn visit_generator_declaration(\n        &mut self,\n        node: &'ast GeneratorDeclaration,\n    ) -> ControlFlow<Self::BreakTy> {\n        self.0.add(node.name().sym(), false);\n        ControlFlow::Continue(())\n    }\n\n    fn visit_async_function_expression(\n        &mut self,\n        node: &'ast AsyncFunctionExpression,\n    ) -> ControlFlow<Self::BreakTy> {\n        if let Some(ident) = node.name() {\n            self.0.add(ident.sym(), false);\n        }\n        ControlFlow::Continue(())\n    }\n\n    fn visit_async_function_declaration(\n        &mut self,\n        node: &'ast AsyncFunctionDeclaration,\n    ) -> ControlFlow<Self::BreakTy> {\n        self.0.add(node.name().sym(), false);\n        ControlFlow::Continue(())\n    }\n\n    fn visit_async_generator_expression(\n        &mut self,\n        node: &'ast AsyncGeneratorExpression,\n    ) -> ControlFlow<Self::BreakTy> {\n        if let Some(ident) = node.name() {\n            self.0.add(ident.sym(), false);\n        }\n        ControlFlow::Continue(())\n    }\n\n    fn visit_async_generator_declaration(\n        &mut self,\n        node: &'ast AsyncGeneratorDeclaration,\n    ) -> ControlFlow<Self::BreakTy> {\n        self.0.add(node.name().sym(), false);\n        ControlFlow::Continue(())\n    }\n\n    fn visit_class_expression(\n        &mut self,\n        node: &'ast ClassExpression,\n    ) -> ControlFlow<Self::BreakTy> {\n        if let Some(ident) = node.name() {\n            self.0.add(ident.sym(), false);\n        }\n        ControlFlow::Continue(())\n    }\n\n    fn visit_class_declaration(\n        &mut self,\n        node: &'ast ClassDeclaration,\n    ) -> ControlFlow<Self::BreakTy> {\n        self.0.add(node.name().sym(), false);\n        ControlFlow::Continue(())\n    }\n\n    fn visit_export_declaration(\n        &mut self,\n        node: &'ast ExportDeclaration,\n    ) -> ControlFlow<Self::BreakTy> {\n        match node {\n            ExportDeclaration::VarStatement(var) => self.visit_var_declaration(var)?,\n            ExportDeclaration::Declaration(decl) => self.visit_declaration(decl)?,\n            ExportDeclaration::DefaultFunctionDeclaration(f) => {\n                self.0.add(f.name().sym(), true);\n            }\n            ExportDeclaration::DefaultGeneratorDeclaration(g) => {\n                self.0.add(g.name().sym(), false);\n            }\n            ExportDeclaration::DefaultAsyncFunctionDeclaration(af) => {\n                self.0.add(af.name().sym(), false);\n            }\n            ExportDeclaration::DefaultAsyncGeneratorDeclaration(ag) => {\n                self.0.add(ag.name().sym(), false);\n            }\n            ExportDeclaration::DefaultClassDeclaration(cl) => {\n                self.0.add(cl.name().sym(), false);\n            }\n            ExportDeclaration::DefaultAssignmentExpression(_) => {\n                self.0.add(Sym::DEFAULT_EXPORT, false);\n            }\n            ExportDeclaration::ReExport { .. } | ExportDeclaration::List(_) => {}\n        }\n\n        ControlFlow::Continue(())\n    }\n}\n\n/// Returns a list with the bound names of an AST node, which may contain duplicates.\n///\n/// This is equivalent to the [`BoundNames`][spec] syntax operation in the spec.\n///\n/// [spec]: https://tc39.es/ecma262/#sec-static-semantics-boundnames\n#[must_use]\npub fn bound_names<'a, N>(node: &'a N) -> Vec<Sym>\nwhere\n    &'a N: Into<NodeRef<'a>>,\n{\n    let mut names = Vec::new();\n    let _ = BoundNamesVisitor(&mut names).visit(node.into());\n\n    names\n}\n\n/// The [`Visitor`] used to obtain the lexically declared names of a node.\n#[derive(Debug)]\nstruct LexicallyDeclaredNamesVisitor<'a, T: IdentList>(&'a mut T);\n\nimpl<'ast, T: IdentList> Visitor<'ast> for LexicallyDeclaredNamesVisitor<'_, T> {\n    type BreakTy = Infallible;\n\n    fn visit_script(&mut self, node: &'ast Script) -> ControlFlow<Self::BreakTy> {\n        top_level_lexicals(node.statements(), self.0);\n        ControlFlow::Continue(())\n    }\n\n    fn visit_function_body(&mut self, node: &'ast FunctionBody) -> ControlFlow<Self::BreakTy> {\n        top_level_lexicals(node.statement_list(), self.0);\n        ControlFlow::Continue(())\n    }\n\n    fn visit_module_item(&mut self, node: &'ast ModuleItem) -> ControlFlow<Self::BreakTy> {\n        match node {\n            // ModuleItem : ImportDeclaration\n            ModuleItem::ImportDeclaration(import) => {\n                // 1. Return the BoundNames of ImportDeclaration.\n                BoundNamesVisitor(self.0).visit_import_declaration(import)\n            }\n\n            // ModuleItem : ExportDeclaration\n            ModuleItem::ExportDeclaration(export) => {\n                // 1. If ExportDeclaration is export VariableStatement, return a new empty List.\n                if matches!(export.as_ref(), ExportDeclaration::VarStatement(_)) {\n                    ControlFlow::Continue(())\n                } else {\n                    // 2. Return the BoundNames of ExportDeclaration.\n                    BoundNamesVisitor(self.0).visit_export_declaration(export)\n                }\n            }\n\n            // ModuleItem : StatementListItem\n            ModuleItem::StatementListItem(item) => {\n                // 1. Return LexicallyDeclaredNames of StatementListItem.\n                self.visit_statement_list_item(item)\n            }\n        }\n    }\n\n    fn visit_expression(&mut self, _: &'ast Expression) -> ControlFlow<Self::BreakTy> {\n        ControlFlow::Continue(())\n    }\n\n    fn visit_statement(&mut self, node: &'ast Statement) -> ControlFlow<Self::BreakTy> {\n        if let Statement::Labelled(labelled) = node {\n            return self.visit_labelled(labelled);\n        }\n        ControlFlow::Continue(())\n    }\n\n    fn visit_declaration(&mut self, node: &'ast Declaration) -> ControlFlow<Self::BreakTy> {\n        BoundNamesVisitor(self.0).visit_declaration(node)\n    }\n\n    fn visit_labelled_item(&mut self, node: &'ast LabelledItem) -> ControlFlow<Self::BreakTy> {\n        match node {\n            LabelledItem::FunctionDeclaration(f) => {\n                BoundNamesVisitor(self.0).visit_function_declaration(f)\n            }\n            LabelledItem::Statement(_) => ControlFlow::Continue(()),\n        }\n    }\n\n    fn visit_function_expression(\n        &mut self,\n        node: &'ast FunctionExpression,\n    ) -> ControlFlow<Self::BreakTy> {\n        self.visit_function_body(node.body())\n    }\n\n    fn visit_function_declaration(\n        &mut self,\n        node: &'ast FunctionDeclaration,\n    ) -> ControlFlow<Self::BreakTy> {\n        self.visit_function_body(node.body())\n    }\n\n    fn visit_async_function_expression(\n        &mut self,\n        node: &'ast AsyncFunctionExpression,\n    ) -> ControlFlow<Self::BreakTy> {\n        self.visit_function_body(node.body())\n    }\n\n    fn visit_async_function_declaration(\n        &mut self,\n        node: &'ast AsyncFunctionDeclaration,\n    ) -> ControlFlow<Self::BreakTy> {\n        self.visit_function_body(node.body())\n    }\n\n    fn visit_generator_expression(\n        &mut self,\n        node: &'ast GeneratorExpression,\n    ) -> ControlFlow<Self::BreakTy> {\n        self.visit_function_body(node.body())\n    }\n\n    fn visit_generator_declaration(\n        &mut self,\n        node: &'ast GeneratorDeclaration,\n    ) -> ControlFlow<Self::BreakTy> {\n        self.visit_function_body(node.body())\n    }\n\n    fn visit_async_generator_expression(\n        &mut self,\n        node: &'ast AsyncGeneratorExpression,\n    ) -> ControlFlow<Self::BreakTy> {\n        self.visit_function_body(node.body())\n    }\n\n    fn visit_async_generator_declaration(\n        &mut self,\n        node: &'ast AsyncGeneratorDeclaration,\n    ) -> ControlFlow<Self::BreakTy> {\n        self.visit_function_body(node.body())\n    }\n\n    fn visit_arrow_function(&mut self, node: &'ast ArrowFunction) -> ControlFlow<Self::BreakTy> {\n        self.visit_function_body(node.body())\n    }\n\n    fn visit_async_arrow_function(\n        &mut self,\n        node: &'ast AsyncArrowFunction,\n    ) -> ControlFlow<Self::BreakTy> {\n        self.visit_function_body(node.body())\n    }\n\n    fn visit_class_element(&mut self, node: &'ast ClassElement) -> ControlFlow<Self::BreakTy> {\n        if let ClassElement::StaticBlock(block) = node {\n            self.visit_function_body(&block.body)?;\n        }\n        ControlFlow::Continue(())\n    }\n\n    fn visit_import_declaration(\n        &mut self,\n        node: &'ast ImportDeclaration,\n    ) -> ControlFlow<Self::BreakTy> {\n        BoundNamesVisitor(self.0).visit_import_declaration(node)\n    }\n\n    fn visit_export_declaration(\n        &mut self,\n        node: &'ast ExportDeclaration,\n    ) -> ControlFlow<Self::BreakTy> {\n        if matches!(node, ExportDeclaration::VarStatement(_)) {\n            return ControlFlow::Continue(());\n        }\n        BoundNamesVisitor(self.0).visit_export_declaration(node)\n    }\n}\n\n/// Returns a list with the lexical bindings of a node, which may contain duplicates.\n///\n/// This is equivalent to the [`LexicallyDeclaredNames`][spec] syntax operation in the spec.\n///\n/// [spec]: https://tc39.es/ecma262/#sec-static-semantics-lexicallydeclarednames\n#[must_use]\npub fn lexically_declared_names<'a, N>(node: &'a N) -> Vec<Sym>\nwhere\n    &'a N: Into<NodeRef<'a>>,\n{\n    let mut names = Vec::new();\n    let _ = LexicallyDeclaredNamesVisitor(&mut names).visit(node.into());\n    names\n}\n\n/// Returns a list with the lexical bindings of a node, which may contain duplicates.\n///\n/// If a declared name originates from a function declaration it is flagged as `true` in the returned\n/// list. (See [B.3.2.4 Changes to Block Static Semantics: Early Errors])\n///\n/// [spec]: https://tc39.es/ecma262/#sec-static-semantics-lexicallydeclarednames\n/// [changes]: https://tc39.es/ecma262/#sec-block-duplicates-allowed-static-semantics\n#[must_use]\npub fn lexically_declared_names_legacy<'a, N>(node: &'a N) -> Vec<(Sym, bool)>\nwhere\n    &'a N: Into<NodeRef<'a>>,\n{\n    let mut names = Vec::new();\n    let _ = LexicallyDeclaredNamesVisitor(&mut names).visit(node.into());\n    names\n}\n\n/// The [`Visitor`] used to obtain the var declared names of a node.\n#[derive(Debug)]\nstruct VarDeclaredNamesVisitor<'a>(&'a mut FxHashSet<Sym>);\n\nimpl<'ast> Visitor<'ast> for VarDeclaredNamesVisitor<'_> {\n    type BreakTy = Infallible;\n\n    fn visit_script(&mut self, node: &'ast Script) -> ControlFlow<Self::BreakTy> {\n        top_level_vars(node.statements(), self.0);\n        ControlFlow::Continue(())\n    }\n\n    fn visit_function_body(&mut self, node: &'ast FunctionBody) -> ControlFlow<Self::BreakTy> {\n        top_level_vars(node.statement_list(), self.0);\n        ControlFlow::Continue(())\n    }\n\n    fn visit_module_item(&mut self, node: &'ast ModuleItem) -> ControlFlow<Self::BreakTy> {\n        match node {\n            // ModuleItem : ImportDeclaration\n            ModuleItem::ImportDeclaration(_) => {\n                // 1. Return a new empty List.\n                ControlFlow::Continue(())\n            }\n\n            // ModuleItem : ExportDeclaration\n            ModuleItem::ExportDeclaration(export) => {\n                // 1. If ExportDeclaration is export VariableStatement, return BoundNames of ExportDeclaration.\n                if let ExportDeclaration::VarStatement(var) = export.as_ref() {\n                    BoundNamesVisitor(self.0).visit_var_declaration(var)\n                } else {\n                    // 2. Return a new empty List.\n                    ControlFlow::Continue(())\n                }\n            }\n\n            ModuleItem::StatementListItem(item) => self.visit_statement_list_item(item),\n        }\n    }\n\n    fn visit_statement(&mut self, node: &'ast Statement) -> ControlFlow<Self::BreakTy> {\n        match node {\n            Statement::Empty\n            | Statement::Debugger\n            | Statement::Expression(_)\n            | Statement::Continue(_)\n            | Statement::Break(_)\n            | Statement::Return(_)\n            | Statement::Throw(_) => ControlFlow::Continue(()),\n            Statement::Block(node) => self.visit(node),\n            Statement::Var(node) => self.visit(node),\n            Statement::If(node) => self.visit(node),\n            Statement::DoWhileLoop(node) => self.visit(node),\n            Statement::WhileLoop(node) => self.visit(node),\n            Statement::ForLoop(node) => self.visit(node),\n            Statement::ForInLoop(node) => self.visit(node),\n            Statement::ForOfLoop(node) => self.visit(node),\n            Statement::Switch(node) => self.visit(node),\n            Statement::Labelled(node) => self.visit(node),\n            Statement::Try(node) => self.visit(node),\n            Statement::With(node) => self.visit(node),\n        }\n    }\n\n    fn visit_statement_list_item(\n        &mut self,\n        node: &'ast StatementListItem,\n    ) -> ControlFlow<Self::BreakTy> {\n        match node {\n            StatementListItem::Statement(stmt) => self.visit_statement(stmt),\n            StatementListItem::Declaration(_) => ControlFlow::Continue(()),\n        }\n    }\n\n    fn visit_variable(&mut self, node: &'ast Variable) -> ControlFlow<Self::BreakTy> {\n        BoundNamesVisitor(self.0).visit_variable(node)\n    }\n\n    fn visit_if(&mut self, node: &'ast crate::statement::If) -> ControlFlow<Self::BreakTy> {\n        if let Some(node) = node.else_node() {\n            self.visit(node)?;\n        }\n        self.visit(node.body())\n    }\n\n    fn visit_do_while_loop(\n        &mut self,\n        node: &'ast crate::statement::DoWhileLoop,\n    ) -> ControlFlow<Self::BreakTy> {\n        self.visit(node.body())\n    }\n\n    fn visit_while_loop(\n        &mut self,\n        node: &'ast crate::statement::WhileLoop,\n    ) -> ControlFlow<Self::BreakTy> {\n        self.visit(node.body())\n    }\n\n    fn visit_for_loop(\n        &mut self,\n        node: &'ast crate::statement::ForLoop,\n    ) -> ControlFlow<Self::BreakTy> {\n        if let Some(ForLoopInitializer::Var(node)) = node.init() {\n            BoundNamesVisitor(self.0).visit_var_declaration(node)?;\n        }\n        self.visit(node.body())\n    }\n\n    fn visit_for_in_loop(\n        &mut self,\n        node: &'ast crate::statement::ForInLoop,\n    ) -> ControlFlow<Self::BreakTy> {\n        if let IterableLoopInitializer::Var(node) = node.initializer() {\n            BoundNamesVisitor(self.0).visit_variable(node)?;\n        }\n        self.visit(node.body())\n    }\n\n    fn visit_for_of_loop(\n        &mut self,\n        node: &'ast crate::statement::ForOfLoop,\n    ) -> ControlFlow<Self::BreakTy> {\n        if let IterableLoopInitializer::Var(node) = node.initializer() {\n            BoundNamesVisitor(self.0).visit_variable(node)?;\n        }\n        self.visit(node.body())\n    }\n\n    fn visit_with(&mut self, node: &'ast With) -> ControlFlow<Self::BreakTy> {\n        self.visit(node.statement())\n    }\n\n    fn visit_switch(&mut self, node: &'ast crate::statement::Switch) -> ControlFlow<Self::BreakTy> {\n        for case in node.cases() {\n            self.visit(case)?;\n        }\n        if let Some(node) = node.default() {\n            self.visit(node)?;\n        }\n        ControlFlow::Continue(())\n    }\n\n    fn visit_labelled_item(&mut self, node: &'ast LabelledItem) -> ControlFlow<Self::BreakTy> {\n        match node {\n            LabelledItem::FunctionDeclaration(_) => ControlFlow::Continue(()),\n            LabelledItem::Statement(stmt) => self.visit(stmt),\n        }\n    }\n\n    fn visit_try(&mut self, node: &'ast crate::statement::Try) -> ControlFlow<Self::BreakTy> {\n        if let Some(node) = node.finally() {\n            self.visit(node)?;\n        }\n        if let Some(node) = node.catch() {\n            self.visit(node.block())?;\n        }\n        self.visit(node.block())\n    }\n\n    fn visit_function_expression(\n        &mut self,\n        node: &'ast FunctionExpression,\n    ) -> ControlFlow<Self::BreakTy> {\n        self.visit_function_body(node.body())\n    }\n\n    fn visit_function_declaration(\n        &mut self,\n        node: &'ast FunctionDeclaration,\n    ) -> ControlFlow<Self::BreakTy> {\n        self.visit_function_body(node.body())\n    }\n\n    fn visit_async_function_expression(\n        &mut self,\n        node: &'ast AsyncFunctionExpression,\n    ) -> ControlFlow<Self::BreakTy> {\n        self.visit_function_body(node.body())\n    }\n\n    fn visit_async_function_declaration(\n        &mut self,\n        node: &'ast AsyncFunctionDeclaration,\n    ) -> ControlFlow<Self::BreakTy> {\n        self.visit_function_body(node.body())\n    }\n\n    fn visit_generator_expression(\n        &mut self,\n        node: &'ast GeneratorExpression,\n    ) -> ControlFlow<Self::BreakTy> {\n        self.visit_function_body(node.body())\n    }\n\n    fn visit_generator_declaration(\n        &mut self,\n        node: &'ast GeneratorDeclaration,\n    ) -> ControlFlow<Self::BreakTy> {\n        self.visit_function_body(node.body())\n    }\n\n    fn visit_async_generator_expression(\n        &mut self,\n        node: &'ast AsyncGeneratorExpression,\n    ) -> ControlFlow<Self::BreakTy> {\n        self.visit_function_body(node.body())\n    }\n\n    fn visit_async_generator_declaration(\n        &mut self,\n        node: &'ast AsyncGeneratorDeclaration,\n    ) -> ControlFlow<Self::BreakTy> {\n        self.visit_function_body(node.body())\n    }\n\n    fn visit_class_element(&mut self, node: &'ast ClassElement) -> ControlFlow<Self::BreakTy> {\n        if let ClassElement::StaticBlock(block) = node {\n            self.visit_function_body(&block.body)?;\n        }\n        node.visit_with(self)\n    }\n\n    fn visit_import_declaration(\n        &mut self,\n        _: &'ast ImportDeclaration,\n    ) -> ControlFlow<Self::BreakTy> {\n        ControlFlow::Continue(())\n    }\n\n    fn visit_export_declaration(\n        &mut self,\n        node: &'ast ExportDeclaration,\n    ) -> ControlFlow<Self::BreakTy> {\n        match node {\n            ExportDeclaration::VarStatement(var) => {\n                BoundNamesVisitor(self.0).visit_var_declaration(var)\n            }\n            _ => ControlFlow::Continue(()),\n        }\n    }\n}\n\n/// Returns a set with the var bindings of a node, with no duplicates.\n///\n/// This is equivalent to the [`VarDeclaredNames`][spec] syntax operation in the spec.\n///\n/// [spec]: https://tc39.es/ecma262/#sec-static-semantics-vardeclarednames\n#[must_use]\npub fn var_declared_names<'a, N>(node: &'a N) -> FxHashSet<Sym>\nwhere\n    &'a N: Into<NodeRef<'a>>,\n{\n    let mut names = FxHashSet::default();\n    let _ = VarDeclaredNamesVisitor(&mut names).visit(node.into());\n    names\n}\n\n/// Utility function that collects the top level lexicals of a statement list into `names`.\n///\n/// This is equivalent to the [`TopLevelLexicallyDeclaredNames`][spec] syntax operation in the spec.\n///\n/// [spec]: https://tc39.es/ecma262/#sec-static-semantics-toplevellexicallydeclarednames\nfn top_level_lexicals<T: IdentList>(stmts: &StatementList, names: &mut T) {\n    for stmt in stmts.statements() {\n        if let StatementListItem::Declaration(decl) = stmt {\n            match decl.as_ref() {\n                // Note\n                // At the top level of a function, or script, function declarations are treated like\n                // var declarations rather than like lexical declarations.\n                Declaration::FunctionDeclaration(_)\n                | Declaration::GeneratorDeclaration(_)\n                | Declaration::AsyncFunctionDeclaration(_)\n                | Declaration::AsyncGeneratorDeclaration(_) => {}\n                Declaration::ClassDeclaration(class) => {\n                    let _ = BoundNamesVisitor(names).visit_class_declaration(class);\n                }\n                Declaration::Lexical(decl) => {\n                    let _ = BoundNamesVisitor(names).visit_lexical_declaration(decl);\n                }\n            }\n        }\n    }\n}\n\n/// Utility function that collects the top level vars of a statement list into `names`.\n///\n/// This is equivalent to the [`TopLevelVarDeclaredNames`][spec] syntax operation in the spec.\n///\n/// [spec]: https://tc39.es/ecma262/#sec-static-semantics-toplevelvardeclarednames\nfn top_level_vars(stmts: &StatementList, names: &mut FxHashSet<Sym>) {\n    for stmt in stmts.statements() {\n        match stmt {\n            StatementListItem::Declaration(decl) => {\n                match decl.as_ref() {\n                    // Note\n                    // At the top level of a function, or script, function declarations are treated like\n                    // var declarations rather than like lexical declarations.\n                    Declaration::FunctionDeclaration(f) => {\n                        let _ = BoundNamesVisitor(names).visit_function_declaration(f);\n                    }\n                    Declaration::GeneratorDeclaration(f) => {\n                        let _ = BoundNamesVisitor(names).visit_generator_declaration(f);\n                    }\n                    Declaration::AsyncFunctionDeclaration(f) => {\n                        let _ = BoundNamesVisitor(names).visit_async_function_declaration(f);\n                    }\n                    Declaration::AsyncGeneratorDeclaration(f) => {\n                        let _ = BoundNamesVisitor(names).visit_async_generator_declaration(f);\n                    }\n                    Declaration::ClassDeclaration(_) | Declaration::Lexical(_) => {}\n                }\n            }\n            StatementListItem::Statement(stmt) => {\n                let mut stmt = Some(stmt.as_ref());\n                while let Some(Statement::Labelled(labelled)) = stmt.as_ref() {\n                    match labelled.item() {\n                        LabelledItem::FunctionDeclaration(f) => {\n                            let _ = BoundNamesVisitor(names).visit_function_declaration(f);\n                            stmt = None;\n                        }\n                        LabelledItem::Statement(s) => stmt = Some(s),\n                    }\n                }\n                if let Some(stmt) = stmt {\n                    let _ = VarDeclaredNamesVisitor(names).visit(stmt);\n                }\n            }\n        }\n    }\n}\n\n/// Returns `true` if all private identifiers in a node are valid.\n///\n/// This is equivalent to the [`AllPrivateIdentifiersValid`][spec] syntax operation in the spec.\n///\n/// [spec]: https://tc39.es/ecma262/#sec-static-semantics-allprivateidentifiersvalid\n#[must_use]\n#[inline]\npub fn all_private_identifiers_valid<'a, N>(node: &'a N, private_names: Vec<Sym>) -> bool\nwhere\n    &'a N: Into<NodeRef<'a>>,\n{\n    AllPrivateIdentifiersValidVisitor(private_names)\n        .visit(node.into())\n        .is_continue()\n}\n\nstruct AllPrivateIdentifiersValidVisitor(Vec<Sym>);\n\nimpl<'ast> Visitor<'ast> for AllPrivateIdentifiersValidVisitor {\n    type BreakTy = ();\n\n    fn visit_class_expression(\n        &mut self,\n        node: &'ast ClassExpression,\n    ) -> ControlFlow<Self::BreakTy> {\n        if let Some(node) = node.super_ref() {\n            self.visit(node)?;\n        }\n\n        let mut names = self.0.clone();\n        for element in node.elements() {\n            match element {\n                ClassElement::MethodDefinition(m) => {\n                    if let ClassElementName::PrivateName(name) = m.name() {\n                        names.push(name.description());\n                    }\n                }\n                ClassElement::PrivateFieldDefinition(PrivateFieldDefinition { name, .. })\n                | ClassElement::PrivateStaticFieldDefinition(PrivateFieldDefinition {\n                    name, ..\n                }) => {\n                    names.push(name.description());\n                }\n                _ => {}\n            }\n        }\n\n        let mut visitor = Self(names);\n\n        if let Some(node) = node.constructor() {\n            visitor.visit(node)?;\n        }\n\n        for element in node.elements() {\n            match element {\n                ClassElement::MethodDefinition(m) => {\n                    if let ClassElementName::PropertyName(name) = m.name() {\n                        visitor.visit(name)?;\n                    }\n                    visitor.visit(m.parameters())?;\n                    visitor.visit(m.body())?;\n                }\n                ClassElement::FieldDefinition(field)\n                | ClassElement::StaticFieldDefinition(field) => {\n                    visitor.visit(&field.name)?;\n                    if let Some(expression) = &field.initializer {\n                        visitor.visit(expression)?;\n                    }\n                }\n                ClassElement::PrivateFieldDefinition(PrivateFieldDefinition {\n                    initializer,\n                    ..\n                })\n                | ClassElement::PrivateStaticFieldDefinition(PrivateFieldDefinition {\n                    initializer,\n                    ..\n                }) => {\n                    if let Some(expression) = initializer {\n                        visitor.visit(expression)?;\n                    }\n                }\n                ClassElement::StaticBlock(block) => {\n                    visitor.visit(&block.body)?;\n                }\n            }\n        }\n\n        ControlFlow::Continue(())\n    }\n\n    fn visit_class_declaration(\n        &mut self,\n        node: &'ast ClassDeclaration,\n    ) -> ControlFlow<Self::BreakTy> {\n        if let Some(node) = node.super_ref() {\n            self.visit(node)?;\n        }\n\n        let mut names = self.0.clone();\n        for element in node.elements() {\n            match element {\n                ClassElement::MethodDefinition(m) => {\n                    if let ClassElementName::PrivateName(name) = m.name() {\n                        names.push(name.description());\n                    }\n                }\n                ClassElement::PrivateFieldDefinition(PrivateFieldDefinition { name, .. })\n                | ClassElement::PrivateStaticFieldDefinition(PrivateFieldDefinition {\n                    name, ..\n                }) => {\n                    names.push(name.description());\n                }\n                _ => {}\n            }\n        }\n\n        let mut visitor = Self(names);\n\n        if let Some(node) = node.constructor() {\n            visitor.visit(node)?;\n        }\n\n        for element in node.elements() {\n            match element {\n                ClassElement::MethodDefinition(m) => {\n                    if let ClassElementName::PropertyName(name) = m.name() {\n                        visitor.visit(name)?;\n                    }\n                    visitor.visit(m.parameters())?;\n                    visitor.visit(m.body())?;\n                }\n                ClassElement::FieldDefinition(field)\n                | ClassElement::StaticFieldDefinition(field) => {\n                    visitor.visit(&field.name)?;\n                    if let Some(expression) = &field.initializer {\n                        visitor.visit(expression)?;\n                    }\n                }\n                ClassElement::PrivateFieldDefinition(PrivateFieldDefinition {\n                    initializer,\n                    ..\n                })\n                | ClassElement::PrivateStaticFieldDefinition(PrivateFieldDefinition {\n                    initializer,\n                    ..\n                }) => {\n                    if let Some(expression) = initializer {\n                        visitor.visit(expression)?;\n                    }\n                }\n                ClassElement::StaticBlock(block) => {\n                    visitor.visit(&block.body)?;\n                }\n            }\n        }\n\n        ControlFlow::Continue(())\n    }\n\n    fn visit_private_property_access(\n        &mut self,\n        node: &'ast PrivatePropertyAccess,\n    ) -> ControlFlow<Self::BreakTy> {\n        if self.0.contains(&node.field().description()) {\n            self.visit(node.target())\n        } else {\n            ControlFlow::Break(())\n        }\n    }\n\n    fn visit_binary_in_private(\n        &mut self,\n        node: &'ast BinaryInPrivate,\n    ) -> ControlFlow<Self::BreakTy> {\n        if self.0.contains(&node.lhs().description()) {\n            self.visit(node.rhs())\n        } else {\n            ControlFlow::Break(())\n        }\n    }\n\n    fn visit_optional_operation_kind(\n        &mut self,\n        node: &'ast OptionalOperationKind,\n    ) -> ControlFlow<Self::BreakTy> {\n        match node {\n            OptionalOperationKind::SimplePropertyAccess { field } => {\n                self.visit_property_access_field(field)\n            }\n            OptionalOperationKind::PrivatePropertyAccess { field } => {\n                if self.0.contains(&field.description()) {\n                    ControlFlow::Continue(())\n                } else {\n                    ControlFlow::Break(())\n                }\n            }\n            OptionalOperationKind::Call { args } => {\n                for arg in args {\n                    self.visit_expression(arg)?;\n                }\n                ControlFlow::Continue(())\n            }\n        }\n    }\n}\n\n/// Errors that can occur when checking labels.\n#[derive(Debug, Clone, Copy)]\npub enum CheckLabelsError {\n    /// A label was used multiple times.\n    DuplicateLabel(Sym),\n\n    /// A `break` statement was used with a label that was not defined.\n    UndefinedBreakTarget(Sym),\n\n    /// A `continue` statement was used with a label that was not defined.\n    UndefinedContinueTarget(Sym),\n\n    /// A `break` statement was used in a non-looping context.\n    IllegalBreakStatement,\n\n    /// A `continue` statement was used in a non-looping context.\n    IllegalContinueStatement,\n}\n\nimpl CheckLabelsError {\n    /// Returns an error message based on the error.\n    #[must_use]\n    pub fn message(&self, interner: &Interner) -> String {\n        match self {\n            Self::DuplicateLabel(label) => {\n                format!(\"duplicate label: {}\", interner.resolve_expect(*label))\n            }\n            Self::UndefinedBreakTarget(label) => {\n                format!(\n                    \"undefined break target: {}\",\n                    interner.resolve_expect(*label)\n                )\n            }\n            Self::UndefinedContinueTarget(label) => format!(\n                \"undefined continue target: {}\",\n                interner.resolve_expect(*label)\n            ),\n            Self::IllegalBreakStatement => \"illegal break statement\".into(),\n            Self::IllegalContinueStatement => \"illegal continue statement\".into(),\n        }\n    }\n}\n\n/// This function checks multiple syntax errors conditions for labels, `break` and `continue`.\n///\n/// The following syntax errors are checked:\n/// - [`ContainsDuplicateLabels`][ContainsDuplicateLabels]\n/// - [`ContainsUndefinedBreakTarget`][ContainsUndefinedBreakTarget]\n/// - [`ContainsUndefinedContinueTarget`][ContainsUndefinedContinueTarget]\n/// - Early errors for [`BreakStatement`][BreakStatement]\n/// - Early errors for [`ContinueStatement`][ContinueStatement]\n///\n/// [ContainsDuplicateLabels]: https://tc39.es/ecma262/#sec-static-semantics-containsduplicatelabels\n/// [ContainsUndefinedBreakTarget]: https://tc39.es/ecma262/#sec-static-semantics-containsundefinedbreaktarget\n/// [ContainsUndefinedContinueTarget]: https://tc39.es/ecma262/#sec-static-semantics-containsundefinedcontinuetarget\n/// [BreakStatement]: https://tc39.es/ecma262/#sec-break-statement-static-semantics-early-errors\n/// [ContinueStatement]: https://tc39.es/ecma262/#sec-continue-statement-static-semantics-early-errors\n///\n/// # Errors\n///\n/// This function returns an error for the first syntax error that is found.\npub fn check_labels<N>(node: &N) -> Result<(), CheckLabelsError>\nwhere\n    N: VisitWith,\n{\n    #[derive(Debug, Clone)]\n    struct CheckLabelsResolver {\n        labels: FxHashSet<Sym>,\n        continue_iteration_labels: FxHashSet<Sym>,\n        continue_labels: Option<FxHashSet<Sym>>,\n        iteration: bool,\n        switch: bool,\n    }\n\n    impl<'ast> Visitor<'ast> for CheckLabelsResolver {\n        type BreakTy = CheckLabelsError;\n\n        fn visit_statement(&mut self, node: &'ast Statement) -> ControlFlow<Self::BreakTy> {\n            match node {\n                Statement::Block(node) => self.visit_block(node),\n                Statement::Var(_)\n                | Statement::Empty\n                | Statement::Debugger\n                | Statement::Expression(_)\n                | Statement::Return(_)\n                | Statement::Throw(_) => ControlFlow::Continue(()),\n                Statement::If(node) => self.visit_if(node),\n                Statement::DoWhileLoop(node) => self.visit_do_while_loop(node),\n                Statement::WhileLoop(node) => self.visit_while_loop(node),\n                Statement::ForLoop(node) => self.visit_for_loop(node),\n                Statement::ForInLoop(node) => self.visit_for_in_loop(node),\n                Statement::ForOfLoop(node) => self.visit_for_of_loop(node),\n                Statement::Switch(node) => self.visit_switch(node),\n                Statement::Labelled(node) => self.visit_labelled(node),\n                Statement::Try(node) => self.visit_try(node),\n                Statement::Continue(node) => self.visit_continue(node),\n                Statement::Break(node) => self.visit_break(node),\n                Statement::With(with) => self.visit_with(with),\n            }\n        }\n\n        fn visit_block(\n            &mut self,\n            node: &'ast crate::statement::Block,\n        ) -> ControlFlow<Self::BreakTy> {\n            let continue_labels = self.continue_labels.take();\n            self.visit_statement_list(node.statement_list())?;\n            self.continue_labels = continue_labels;\n            ControlFlow::Continue(())\n        }\n\n        fn visit_break(\n            &mut self,\n            node: &'ast crate::statement::Break,\n        ) -> ControlFlow<Self::BreakTy> {\n            if let Some(label) = node.label() {\n                if !self.labels.contains(&label) {\n                    return ControlFlow::Break(CheckLabelsError::UndefinedBreakTarget(label));\n                }\n            } else if !self.iteration && !self.switch {\n                return ControlFlow::Break(CheckLabelsError::IllegalBreakStatement);\n            }\n            ControlFlow::Continue(())\n        }\n\n        fn visit_continue(\n            &mut self,\n            node: &'ast crate::statement::Continue,\n        ) -> ControlFlow<Self::BreakTy> {\n            if !self.iteration {\n                return ControlFlow::Break(CheckLabelsError::IllegalContinueStatement);\n            }\n\n            if let Some(label) = node.label()\n                && !self.continue_iteration_labels.contains(&label)\n            {\n                return ControlFlow::Break(CheckLabelsError::UndefinedContinueTarget(label));\n            }\n            ControlFlow::Continue(())\n        }\n\n        fn visit_do_while_loop(\n            &mut self,\n            node: &'ast crate::statement::DoWhileLoop,\n        ) -> ControlFlow<Self::BreakTy> {\n            let continue_labels = self.continue_labels.take();\n            let continue_iteration_labels = self.continue_iteration_labels.clone();\n            if let Some(continue_labels) = &continue_labels {\n                self.continue_iteration_labels.extend(continue_labels);\n            }\n            let iteration = self.iteration;\n            self.iteration = true;\n            self.visit_statement(node.body())?;\n            self.continue_iteration_labels = continue_iteration_labels;\n            self.continue_labels = continue_labels;\n            self.iteration = iteration;\n            ControlFlow::Continue(())\n        }\n\n        fn visit_while_loop(\n            &mut self,\n            node: &'ast crate::statement::WhileLoop,\n        ) -> ControlFlow<Self::BreakTy> {\n            let continue_labels = self.continue_labels.take();\n            let continue_iteration_labels = self.continue_iteration_labels.clone();\n            if let Some(continue_labels) = &continue_labels {\n                self.continue_iteration_labels.extend(continue_labels);\n            }\n            let iteration = self.iteration;\n            self.iteration = true;\n            self.visit_statement(node.body())?;\n            self.continue_iteration_labels = continue_iteration_labels;\n            self.continue_labels = continue_labels;\n            self.iteration = iteration;\n            ControlFlow::Continue(())\n        }\n\n        fn visit_for_loop(\n            &mut self,\n            node: &'ast crate::statement::ForLoop,\n        ) -> ControlFlow<Self::BreakTy> {\n            let continue_labels = self.continue_labels.take();\n            let continue_iteration_labels = self.continue_iteration_labels.clone();\n            if let Some(continue_labels) = &continue_labels {\n                self.continue_iteration_labels.extend(continue_labels);\n            }\n            let iteration = self.iteration;\n            self.iteration = true;\n            self.visit_statement(node.body())?;\n            self.continue_iteration_labels = continue_iteration_labels;\n            self.continue_labels = continue_labels;\n            self.iteration = iteration;\n            ControlFlow::Continue(())\n        }\n\n        fn visit_for_in_loop(\n            &mut self,\n            node: &'ast crate::statement::ForInLoop,\n        ) -> ControlFlow<Self::BreakTy> {\n            let continue_labels = self.continue_labels.take();\n            let continue_iteration_labels = self.continue_iteration_labels.clone();\n            if let Some(continue_labels) = &continue_labels {\n                self.continue_iteration_labels.extend(continue_labels);\n            }\n            let iteration = self.iteration;\n            self.iteration = true;\n            self.visit_statement(node.body())?;\n            self.continue_iteration_labels = continue_iteration_labels;\n            self.continue_labels = continue_labels;\n            self.iteration = iteration;\n            ControlFlow::Continue(())\n        }\n\n        fn visit_for_of_loop(\n            &mut self,\n            node: &'ast crate::statement::ForOfLoop,\n        ) -> ControlFlow<Self::BreakTy> {\n            let continue_labels = self.continue_labels.take();\n            let continue_iteration_labels = self.continue_iteration_labels.clone();\n            if let Some(continue_labels) = &continue_labels {\n                self.continue_iteration_labels.extend(continue_labels);\n            }\n            let iteration = self.iteration;\n            self.iteration = true;\n            self.visit_statement(node.body())?;\n            self.continue_iteration_labels = continue_iteration_labels;\n            self.continue_labels = continue_labels;\n            self.iteration = iteration;\n            ControlFlow::Continue(())\n        }\n\n        fn visit_statement_list_item(\n            &mut self,\n            node: &'ast StatementListItem,\n        ) -> ControlFlow<Self::BreakTy> {\n            let continue_labels = self.continue_labels.take();\n            if let StatementListItem::Statement(stmt) = node {\n                self.visit_statement(stmt)?;\n            }\n            self.continue_labels = continue_labels;\n            ControlFlow::Continue(())\n        }\n\n        fn visit_if(&mut self, node: &'ast crate::statement::If) -> ControlFlow<Self::BreakTy> {\n            let continue_labels = self.continue_labels.take();\n            self.visit_statement(node.body())?;\n            if let Some(stmt) = node.else_node() {\n                self.visit_statement(stmt)?;\n            }\n            self.continue_labels = continue_labels;\n            ControlFlow::Continue(())\n        }\n\n        fn visit_switch(\n            &mut self,\n            node: &'ast crate::statement::Switch,\n        ) -> ControlFlow<Self::BreakTy> {\n            let continue_labels = self.continue_labels.take();\n            let switch = self.switch;\n            self.switch = true;\n            for case in node.cases() {\n                self.visit_statement_list(case.body())?;\n            }\n            if let Some(default) = node.default() {\n                self.visit_statement_list(default)?;\n            }\n            self.continue_labels = continue_labels;\n            self.switch = switch;\n            ControlFlow::Continue(())\n        }\n\n        fn visit_labelled(\n            &mut self,\n            node: &'ast crate::statement::Labelled,\n        ) -> ControlFlow<Self::BreakTy> {\n            let continue_labels = self.continue_labels.clone();\n            if let Some(continue_labels) = &mut self.continue_labels {\n                continue_labels.insert(node.label());\n            } else {\n                let mut continue_labels = FxHashSet::default();\n                continue_labels.insert(node.label());\n                self.continue_labels = Some(continue_labels);\n            }\n\n            if !self.labels.insert(node.label()) {\n                return ControlFlow::Break(CheckLabelsError::DuplicateLabel(node.label()));\n            }\n            self.visit_labelled_item(node.item())?;\n            self.labels.remove(&node.label());\n            self.continue_labels = continue_labels;\n            ControlFlow::Continue(())\n        }\n\n        fn visit_labelled_item(&mut self, node: &'ast LabelledItem) -> ControlFlow<Self::BreakTy> {\n            match node {\n                LabelledItem::Statement(stmt) => self.visit_statement(stmt),\n                LabelledItem::FunctionDeclaration(_) => ControlFlow::Continue(()),\n            }\n        }\n\n        fn visit_try(&mut self, node: &'ast crate::statement::Try) -> ControlFlow<Self::BreakTy> {\n            let continue_labels = self.continue_labels.take();\n            self.visit_block(node.block())?;\n            if let Some(catch) = node.catch() {\n                self.visit_block(catch.block())?;\n            }\n            if let Some(finally) = node.finally() {\n                self.visit_block(finally.block())?;\n            }\n            self.continue_labels = continue_labels;\n            ControlFlow::Continue(())\n        }\n\n        fn visit_module_item_list(\n            &mut self,\n            node: &'ast crate::ModuleItemList,\n        ) -> ControlFlow<Self::BreakTy> {\n            let continue_labels = self.continue_labels.take();\n            for item in node.items() {\n                self.visit_module_item(item)?;\n            }\n            self.continue_labels = continue_labels;\n            ControlFlow::Continue(())\n        }\n\n        fn visit_module_item(&mut self, node: &'ast ModuleItem) -> ControlFlow<Self::BreakTy> {\n            match node {\n                ModuleItem::ImportDeclaration(_) | ModuleItem::ExportDeclaration(_) => {\n                    ControlFlow::Continue(())\n                }\n                ModuleItem::StatementListItem(node) => self.visit_statement_list_item(node),\n            }\n        }\n    }\n\n    let mut visitor = CheckLabelsResolver {\n        labels: FxHashSet::default(),\n        continue_iteration_labels: FxHashSet::default(),\n        continue_labels: None,\n        iteration: false,\n        switch: false,\n    };\n\n    if let ControlFlow::Break(error) = node.visit_with(&mut visitor) {\n        Err(error)\n    } else {\n        Ok(())\n    }\n}\n\n/// Returns `true` if the given node contains a `CoverInitializedName`.\n#[must_use]\npub fn contains_invalid_object_literal<N>(node: &N) -> bool\nwhere\n    N: VisitWith,\n{\n    #[derive(Debug, Clone)]\n    struct ContainsInvalidObjectLiteral {}\n\n    impl<'ast> Visitor<'ast> for ContainsInvalidObjectLiteral {\n        type BreakTy = ();\n\n        fn visit_object_literal(\n            &mut self,\n            node: &'ast crate::expression::literal::ObjectLiteral,\n        ) -> ControlFlow<Self::BreakTy> {\n            for pd in node.properties() {\n                if let PropertyDefinition::CoverInitializedName(..) = pd {\n                    return ControlFlow::Break(());\n                }\n                self.visit_property_definition(pd)?;\n            }\n            ControlFlow::Continue(())\n        }\n    }\n\n    let mut visitor = ContainsInvalidObjectLiteral {};\n\n    node.visit_with(&mut visitor).is_break()\n}\n\n/// The type of a lexically scoped declaration.\n#[derive(Copy, Clone, Debug)]\npub enum LexicallyScopedDeclaration<'a> {\n    /// See [`LexicalDeclaration`]\n    LexicalDeclaration(&'a LexicalDeclaration),\n\n    /// See [`FunctionDeclaration`]\n    FunctionDeclaration(&'a FunctionDeclaration),\n\n    /// See [`GeneratorDeclaration`]\n    GeneratorDeclaration(&'a GeneratorDeclaration),\n\n    /// See [`AsyncFunctionDeclaration`]\n    AsyncFunctionDeclaration(&'a AsyncFunctionDeclaration),\n\n    /// See [`AsyncGeneratorDeclaration`]\n    AsyncGeneratorDeclaration(&'a AsyncGeneratorDeclaration),\n\n    /// See [`ClassDeclaration`]\n    ClassDeclaration(&'a ClassDeclaration),\n\n    /// A default assignment expression as an export declaration.\n    ///\n    /// Only valid inside module exports.\n    AssignmentExpression(&'a Expression),\n}\n\nimpl LexicallyScopedDeclaration<'_> {\n    /// Return the bound names of the declaration.\n    #[must_use]\n    pub fn bound_names(&self) -> Vec<Sym> {\n        match *self {\n            Self::LexicalDeclaration(v) => bound_names(v),\n            Self::FunctionDeclaration(f) => bound_names(f),\n            Self::GeneratorDeclaration(g) => bound_names(g),\n            Self::AsyncFunctionDeclaration(f) => bound_names(f),\n            Self::AsyncGeneratorDeclaration(g) => bound_names(g),\n            Self::ClassDeclaration(cl) => bound_names(cl),\n            Self::AssignmentExpression(expr) => bound_names(expr),\n        }\n    }\n}\n\nimpl<'ast> From<&'ast Declaration> for LexicallyScopedDeclaration<'ast> {\n    fn from(value: &'ast Declaration) -> LexicallyScopedDeclaration<'ast> {\n        match value {\n            Declaration::FunctionDeclaration(f) => Self::FunctionDeclaration(f),\n            Declaration::GeneratorDeclaration(g) => Self::GeneratorDeclaration(g),\n            Declaration::AsyncFunctionDeclaration(af) => Self::AsyncFunctionDeclaration(af),\n            Declaration::AsyncGeneratorDeclaration(ag) => Self::AsyncGeneratorDeclaration(ag),\n            Declaration::ClassDeclaration(c) => Self::ClassDeclaration(c),\n            Declaration::Lexical(lex) => Self::LexicalDeclaration(lex),\n        }\n    }\n}\n\n/// Returns a list of lexically scoped declarations of the given node.\n///\n/// This is equivalent to the [`LexicallyScopedDeclarations`][spec] syntax operation in the spec.\n///\n/// [spec]: https://tc39.es/ecma262/#sec-static-semantics-lexicallyscopeddeclarations\n#[must_use]\npub fn lexically_scoped_declarations<'a, N>(node: &'a N) -> Vec<LexicallyScopedDeclaration<'a>>\nwhere\n    &'a N: Into<NodeRef<'a>>,\n{\n    let mut declarations = Vec::new();\n    let _ = LexicallyScopedDeclarationsVisitor(&mut declarations).visit(node.into());\n    declarations\n}\n\n/// The [`Visitor`] used to obtain the lexically scoped declarations of a node.\n#[derive(Debug)]\nstruct LexicallyScopedDeclarationsVisitor<'a, 'ast>(&'a mut Vec<LexicallyScopedDeclaration<'ast>>);\n\nimpl<'ast> Visitor<'ast> for LexicallyScopedDeclarationsVisitor<'_, 'ast> {\n    type BreakTy = Infallible;\n\n    // ScriptBody : StatementList\n    fn visit_script(&mut self, node: &'ast Script) -> ControlFlow<Self::BreakTy> {\n        // 1. Return TopLevelLexicallyScopedDeclarations of StatementList.\n        TopLevelLexicallyScopedDeclarationsVisitor(self.0).visit_statement_list(node.statements())\n    }\n\n    fn visit_function_body(&mut self, node: &'ast FunctionBody) -> ControlFlow<Self::BreakTy> {\n        // 1. Return TopLevelVarScopedDeclarations of StatementList.\n        TopLevelLexicallyScopedDeclarationsVisitor(self.0)\n            .visit_statement_list(node.statement_list())\n    }\n\n    fn visit_export_declaration(\n        &mut self,\n        node: &'ast ExportDeclaration,\n    ) -> ControlFlow<Self::BreakTy> {\n        let decl = match node {\n            // ExportDeclaration :\n            // export ExportFromClause FromClause ;\n            // export NamedExports ;\n            // export VariableStatement\n            ExportDeclaration::ReExport { .. }\n            | ExportDeclaration::List(_)\n            | ExportDeclaration::VarStatement(_) => {\n                //     1. Return a new empty List.\n                return ControlFlow::Continue(());\n            }\n\n            // ExportDeclaration : export Declaration\n            ExportDeclaration::Declaration(decl) => {\n                // 1. Return a List whose sole element is DeclarationPart of Declaration.\n                decl.into()\n            }\n\n            // ExportDeclaration : export default HoistableDeclaration\n            // 1. Return a List whose sole element is DeclarationPart of HoistableDeclaration.\n            ExportDeclaration::DefaultFunctionDeclaration(f) => {\n                LexicallyScopedDeclaration::FunctionDeclaration(f)\n            }\n            ExportDeclaration::DefaultGeneratorDeclaration(g) => {\n                LexicallyScopedDeclaration::GeneratorDeclaration(g)\n            }\n            ExportDeclaration::DefaultAsyncFunctionDeclaration(af) => {\n                LexicallyScopedDeclaration::AsyncFunctionDeclaration(af)\n            }\n            ExportDeclaration::DefaultAsyncGeneratorDeclaration(ag) => {\n                LexicallyScopedDeclaration::AsyncGeneratorDeclaration(ag)\n            }\n\n            // ExportDeclaration : export default ClassDeclaration\n            ExportDeclaration::DefaultClassDeclaration(c) => {\n                // 1. Return a List whose sole element is ClassDeclaration.\n                LexicallyScopedDeclaration::ClassDeclaration(c)\n            }\n\n            // ExportDeclaration : export default AssignmentExpression ;\n            ExportDeclaration::DefaultAssignmentExpression(expr) => {\n                // 1. Return a List whose sole element is this ExportDeclaration.\n                LexicallyScopedDeclaration::AssignmentExpression(expr)\n            }\n        };\n\n        self.0.push(decl);\n\n        ControlFlow::Continue(())\n    }\n\n    fn visit_statement_list_item(\n        &mut self,\n        node: &'ast StatementListItem,\n    ) -> ControlFlow<Self::BreakTy> {\n        match node {\n            // StatementListItem : Statement\n            StatementListItem::Statement(statement) => {\n                // 1. If Statement is Statement : LabelledStatement , return LexicallyScopedDeclarations of LabelledStatement.\n                if let Statement::Labelled(labelled) = statement.as_ref() {\n                    self.visit_labelled(labelled)\n                } else {\n                    // 2. Return a new empty List.\n                    ControlFlow::Continue(())\n                }\n            }\n\n            // StatementListItem : Declaration\n            StatementListItem::Declaration(declaration) => {\n                // 1. Return a List whose sole element is DeclarationPart of Declaration.\n                self.0.push(declaration.as_ref().into());\n                ControlFlow::Continue(())\n            }\n        }\n    }\n\n    fn visit_labelled_item(&mut self, node: &'ast LabelledItem) -> ControlFlow<Self::BreakTy> {\n        match node {\n            // LabelledItem : FunctionDeclaration\n            LabelledItem::FunctionDeclaration(f) => {\n                // 1. Return « FunctionDeclaration ».\n                self.0\n                    .push(LexicallyScopedDeclaration::FunctionDeclaration(f));\n            }\n\n            // LabelledItem : Statement\n            LabelledItem::Statement(_) => {\n                // 1. Return a new empty List.\n            }\n        }\n        ControlFlow::Continue(())\n    }\n\n    fn visit_module_item(&mut self, node: &'ast ModuleItem) -> ControlFlow<Self::BreakTy> {\n        match node {\n            ModuleItem::StatementListItem(item) => self.visit_statement_list_item(item),\n            ModuleItem::ExportDeclaration(export) => self.visit_export_declaration(export),\n\n            // ModuleItem : ImportDeclaration\n            ModuleItem::ImportDeclaration(_) => {\n                // 1. Return a new empty List.\n                ControlFlow::Continue(())\n            }\n        }\n    }\n}\n/// The [`Visitor`] used to obtain the top level lexically scoped declarations of a node.\n///\n/// This is equivalent to the [`TopLevelLexicallyScopedDeclarations`][spec] syntax operation in the spec.\n///\n/// [spec]: https://tc39.es/ecma262/#sec-static-semantics-toplevellexicallyscopeddeclarations\n#[derive(Debug)]\nstruct TopLevelLexicallyScopedDeclarationsVisitor<'a, 'ast>(\n    &'a mut Vec<LexicallyScopedDeclaration<'ast>>,\n);\n\nimpl<'ast> Visitor<'ast> for TopLevelLexicallyScopedDeclarationsVisitor<'_, 'ast> {\n    type BreakTy = Infallible;\n\n    fn visit_statement_list_item(\n        &mut self,\n        node: &'ast StatementListItem,\n    ) -> ControlFlow<Self::BreakTy> {\n        match node {\n            // StatementListItem : Declaration\n            StatementListItem::Declaration(d) => match d.as_ref() {\n                // 1. If Declaration is Declaration : HoistableDeclaration , then\n                Declaration::FunctionDeclaration(_)\n                | Declaration::GeneratorDeclaration(_)\n                | Declaration::AsyncFunctionDeclaration(_)\n                | Declaration::AsyncGeneratorDeclaration(_) => {\n                    // a. Return a new empty List.\n                }\n\n                // 2. Return « Declaration ».\n                Declaration::ClassDeclaration(cl) => {\n                    self.0\n                        .push(LexicallyScopedDeclaration::ClassDeclaration(cl));\n                }\n                Declaration::Lexical(lex) => {\n                    self.0\n                        .push(LexicallyScopedDeclaration::LexicalDeclaration(lex));\n                }\n            },\n\n            // StatementListItem : Statement\n            StatementListItem::Statement(_) => {\n                // 1. Return a new empty List.\n            }\n        }\n\n        ControlFlow::Continue(())\n    }\n}\n\n/// The type of a var scoped declaration.\n#[derive(Clone, Debug)]\npub enum VarScopedDeclaration {\n    /// See [`VarDeclaration`]\n    VariableDeclaration(Variable),\n\n    /// See [`FunctionDeclaration`]\n    FunctionDeclaration(FunctionDeclaration),\n\n    /// See [`GeneratorDeclaration`]\n    GeneratorDeclaration(GeneratorDeclaration),\n\n    /// See [`AsyncFunctionDeclaration`]\n    AsyncFunctionDeclaration(AsyncFunctionDeclaration),\n\n    /// See [`AsyncGeneratorDeclaration`]\n    AsyncGeneratorDeclaration(AsyncGeneratorDeclaration),\n}\n\nimpl VarScopedDeclaration {\n    /// Return the bound names of the declaration.\n    #[must_use]\n    pub fn bound_names(&self) -> Vec<Sym> {\n        match self {\n            Self::VariableDeclaration(v) => bound_names(v),\n            Self::FunctionDeclaration(f) => bound_names(f),\n            Self::GeneratorDeclaration(g) => bound_names(g),\n            Self::AsyncFunctionDeclaration(f) => bound_names(f),\n            Self::AsyncGeneratorDeclaration(g) => bound_names(g),\n        }\n    }\n\n    /// Return [`LinearSpan`] of this declaration (if there is).\n    #[must_use]\n    pub fn linear_span(&self) -> Option<LinearSpan> {\n        match self {\n            VarScopedDeclaration::FunctionDeclaration(f) => Some(f.linear_span()),\n            VarScopedDeclaration::GeneratorDeclaration(f) => Some(f.linear_span()),\n            VarScopedDeclaration::AsyncFunctionDeclaration(f) => Some(f.linear_span()),\n            VarScopedDeclaration::AsyncGeneratorDeclaration(f) => Some(f.linear_span()),\n            VarScopedDeclaration::VariableDeclaration(_) => None,\n        }\n    }\n}\n\n/// Returns a list of var scoped declarations of the given node.\n///\n/// This is equivalent to the [`VarScopedDeclarations`][spec] syntax operation in the spec.\n///\n/// [spec]: https://tc39.es/ecma262/#sec-static-semantics-varscopeddeclarations\n#[must_use]\npub fn var_scoped_declarations<'a, N>(node: &'a N) -> Vec<VarScopedDeclaration>\nwhere\n    &'a N: Into<NodeRef<'a>>,\n{\n    let mut declarations = Vec::new();\n    let _ = VarScopedDeclarationsVisitor(&mut declarations).visit(node.into());\n    declarations\n}\n\n/// The [`Visitor`] used to obtain the var scoped declarations of a node.\n#[derive(Debug)]\nstruct VarScopedDeclarationsVisitor<'a>(&'a mut Vec<VarScopedDeclaration>);\n\nimpl<'ast> Visitor<'ast> for VarScopedDeclarationsVisitor<'_> {\n    type BreakTy = Infallible;\n\n    // ScriptBody : StatementList\n    fn visit_script(&mut self, node: &'ast Script) -> ControlFlow<Self::BreakTy> {\n        // 1. Return TopLevelVarScopedDeclarations of StatementList.\n        TopLevelVarScopedDeclarationsVisitor(self.0).visit_statement_list(node.statements())\n    }\n\n    fn visit_function_body(&mut self, node: &'ast FunctionBody) -> ControlFlow<Self::BreakTy> {\n        // 1. Return TopLevelVarScopedDeclarations of StatementList.\n        TopLevelVarScopedDeclarationsVisitor(self.0).visit_statement_list(node.statement_list())\n    }\n\n    fn visit_statement(&mut self, node: &'ast Statement) -> ControlFlow<Self::BreakTy> {\n        match node {\n            Statement::Block(s) => self.visit(s),\n            Statement::Var(s) => self.visit(s),\n            Statement::If(s) => self.visit(s),\n            Statement::DoWhileLoop(s) => self.visit(s),\n            Statement::WhileLoop(s) => self.visit(s),\n            Statement::ForLoop(s) => self.visit(s),\n            Statement::ForInLoop(s) => self.visit(s),\n            Statement::ForOfLoop(s) => self.visit(s),\n            Statement::Switch(s) => self.visit(s),\n            Statement::Labelled(s) => self.visit(s),\n            Statement::Try(s) => self.visit(s),\n            Statement::With(s) => self.visit(s),\n            Statement::Empty\n            | Statement::Debugger\n            | Statement::Expression(_)\n            | Statement::Continue(_)\n            | Statement::Break(_)\n            | Statement::Return(_)\n            | Statement::Throw(_) => ControlFlow::Continue(()),\n        }\n    }\n\n    fn visit_statement_list_item(\n        &mut self,\n        node: &'ast StatementListItem,\n    ) -> ControlFlow<Self::BreakTy> {\n        match node {\n            StatementListItem::Declaration(_) => ControlFlow::Continue(()),\n            StatementListItem::Statement(s) => self.visit(s.as_ref()),\n        }\n    }\n\n    fn visit_var_declaration(&mut self, node: &'ast VarDeclaration) -> ControlFlow<Self::BreakTy> {\n        for var in node.0.as_ref() {\n            self.0\n                .push(VarScopedDeclaration::VariableDeclaration(var.clone()));\n        }\n        ControlFlow::Continue(())\n    }\n\n    fn visit_if(&mut self, node: &'ast crate::statement::If) -> ControlFlow<Self::BreakTy> {\n        self.visit(node.body())?;\n        if let Some(else_node) = node.else_node() {\n            self.visit(else_node)?;\n        }\n        ControlFlow::Continue(())\n    }\n\n    fn visit_do_while_loop(\n        &mut self,\n        node: &'ast crate::statement::DoWhileLoop,\n    ) -> ControlFlow<Self::BreakTy> {\n        self.visit(node.body())?;\n        ControlFlow::Continue(())\n    }\n\n    fn visit_while_loop(\n        &mut self,\n        node: &'ast crate::statement::WhileLoop,\n    ) -> ControlFlow<Self::BreakTy> {\n        self.visit(node.body())?;\n        ControlFlow::Continue(())\n    }\n\n    fn visit_for_loop(\n        &mut self,\n        node: &'ast crate::statement::ForLoop,\n    ) -> ControlFlow<Self::BreakTy> {\n        if let Some(ForLoopInitializer::Var(v)) = node.init() {\n            self.visit(v)?;\n        }\n        self.visit(node.body())?;\n        ControlFlow::Continue(())\n    }\n\n    fn visit_for_in_loop(\n        &mut self,\n        node: &'ast crate::statement::ForInLoop,\n    ) -> ControlFlow<Self::BreakTy> {\n        if let IterableLoopInitializer::Var(var) = node.initializer() {\n            self.0\n                .push(VarScopedDeclaration::VariableDeclaration(var.clone()));\n        }\n        self.visit(node.body())?;\n        ControlFlow::Continue(())\n    }\n\n    fn visit_for_of_loop(\n        &mut self,\n        node: &'ast crate::statement::ForOfLoop,\n    ) -> ControlFlow<Self::BreakTy> {\n        if let IterableLoopInitializer::Var(var) = node.initializer() {\n            self.0\n                .push(VarScopedDeclaration::VariableDeclaration(var.clone()));\n        }\n        self.visit(node.body())?;\n        ControlFlow::Continue(())\n    }\n\n    fn visit_with(&mut self, node: &'ast With) -> ControlFlow<Self::BreakTy> {\n        self.visit(node.statement())?;\n        ControlFlow::Continue(())\n    }\n\n    fn visit_switch(&mut self, node: &'ast crate::statement::Switch) -> ControlFlow<Self::BreakTy> {\n        for case in node.cases() {\n            self.visit(case)?;\n        }\n        if let Some(default) = node.default() {\n            self.visit(default)?;\n        }\n        ControlFlow::Continue(())\n    }\n\n    fn visit_case(&mut self, node: &'ast crate::statement::Case) -> ControlFlow<Self::BreakTy> {\n        self.visit(node.body())?;\n        ControlFlow::Continue(())\n    }\n\n    fn visit_labelled_item(&mut self, node: &'ast LabelledItem) -> ControlFlow<Self::BreakTy> {\n        match node {\n            LabelledItem::Statement(s) => self.visit(s),\n            LabelledItem::FunctionDeclaration(_) => ControlFlow::Continue(()),\n        }\n    }\n\n    fn visit_catch(&mut self, node: &'ast crate::statement::Catch) -> ControlFlow<Self::BreakTy> {\n        self.visit(node.block())?;\n        ControlFlow::Continue(())\n    }\n\n    fn visit_module_item(&mut self, node: &'ast ModuleItem) -> ControlFlow<Self::BreakTy> {\n        match node {\n            // ModuleItem : ExportDeclaration\n            ModuleItem::ExportDeclaration(decl) => {\n                if let ExportDeclaration::VarStatement(var) = decl.as_ref() {\n                    //     1. If ExportDeclaration is export VariableStatement, return VarScopedDeclarations of VariableStatement.\n                    self.visit_var_declaration(var)?;\n                }\n                // 2. Return a new empty List.\n            }\n            ModuleItem::StatementListItem(item) => {\n                self.visit_statement_list_item(item)?;\n            }\n            // ModuleItem : ImportDeclaration\n            ModuleItem::ImportDeclaration(_) => {\n                // 1. Return a new empty List.\n            }\n        }\n        ControlFlow::Continue(())\n    }\n}\n\n/// The [`Visitor`] used to obtain the top level var scoped declarations of a node.\n///\n/// This is equivalent to the [`TopLevelVarScopedDeclarations`][spec] syntax operation in the spec.\n///\n/// [spec]: https://tc39.es/ecma262/#sec-static-semantics-toplevelvarscopeddeclarations\n#[derive(Debug)]\nstruct TopLevelVarScopedDeclarationsVisitor<'a>(&'a mut Vec<VarScopedDeclaration>);\n\nimpl<'ast> Visitor<'ast> for TopLevelVarScopedDeclarationsVisitor<'_> {\n    type BreakTy = Infallible;\n\n    fn visit_statement_list_item(\n        &mut self,\n        node: &'ast StatementListItem,\n    ) -> ControlFlow<Self::BreakTy> {\n        match node {\n            StatementListItem::Declaration(d) => {\n                match d.as_ref() {\n                    Declaration::FunctionDeclaration(f) => {\n                        self.0\n                            .push(VarScopedDeclaration::FunctionDeclaration(f.clone()));\n                    }\n                    Declaration::GeneratorDeclaration(f) => {\n                        self.0\n                            .push(VarScopedDeclaration::GeneratorDeclaration(f.clone()));\n                    }\n                    Declaration::AsyncFunctionDeclaration(f) => {\n                        self.0\n                            .push(VarScopedDeclaration::AsyncFunctionDeclaration(f.clone()));\n                    }\n                    Declaration::AsyncGeneratorDeclaration(f) => {\n                        self.0\n                            .push(VarScopedDeclaration::AsyncGeneratorDeclaration(f.clone()));\n                    }\n                    _ => {}\n                }\n                ControlFlow::Continue(())\n            }\n            StatementListItem::Statement(statement) => {\n                if let Statement::Labelled(labelled) = statement.as_ref() {\n                    self.visit(labelled)\n                } else {\n                    VarScopedDeclarationsVisitor(self.0).visit(statement.as_ref())\n                }\n            }\n        }\n    }\n\n    fn visit_labelled_item(&mut self, node: &'ast LabelledItem) -> ControlFlow<Self::BreakTy> {\n        match node {\n            LabelledItem::Statement(Statement::Labelled(s)) => self.visit(s),\n            LabelledItem::Statement(s) => {\n                VarScopedDeclarationsVisitor(self.0).visit(s)?;\n                ControlFlow::Continue(())\n            }\n            LabelledItem::FunctionDeclaration(f) => {\n                self.0\n                    .push(VarScopedDeclaration::FunctionDeclaration(f.clone()));\n                ControlFlow::Continue(())\n            }\n        }\n    }\n}\n\n/// Returns a list function declaration names that are directly contained in a statement lists\n/// `Block`, `CaseClause` or `DefaultClause`.\n/// If the function declaration would cause an early error it is not included in the list.\n///\n/// This behavior is used in the following annexB sections:\n/// * [B.3.2.1 Changes to FunctionDeclarationInstantiation][spec0]\n/// * [B.3.2.2 Changes to GlobalDeclarationInstantiation][spec1]\n/// * [B.3.2.3 Changes to EvalDeclarationInstantiation][spec2]\n///\n/// [spec0]: https://tc39.es/ecma262/#sec-web-compat-functiondeclarationinstantiation\n/// [spec1]: https://tc39.es/ecma262/#sec-web-compat-globaldeclarationinstantiation\n/// [spec2]: https://tc39.es/ecma262/#sec-web-compat-evaldeclarationinstantiation\n#[must_use]\npub fn annex_b_function_declarations_names<'a, N>(node: &'a N) -> Vec<Sym>\nwhere\n    &'a N: Into<NodeRef<'a>>,\n{\n    let mut declarations = Vec::new();\n    let _ = AnnexBFunctionDeclarationNamesVisitor(&mut declarations).visit(node.into());\n    declarations\n}\n\n/// The [`Visitor`] used for [`annex_b_function_declarations_names`].\n#[derive(Debug)]\nstruct AnnexBFunctionDeclarationNamesVisitor<'a>(&'a mut Vec<Sym>);\n\nimpl<'ast> Visitor<'ast> for AnnexBFunctionDeclarationNamesVisitor<'_> {\n    type BreakTy = Infallible;\n\n    fn visit_statement_list_item(\n        &mut self,\n        node: &'ast StatementListItem,\n    ) -> ControlFlow<Self::BreakTy> {\n        match node {\n            StatementListItem::Statement(node) => self.visit(node.as_ref()),\n            StatementListItem::Declaration(_) => ControlFlow::Continue(()),\n        }\n    }\n\n    fn visit_statement(&mut self, node: &'ast Statement) -> ControlFlow<Self::BreakTy> {\n        match node {\n            Statement::Block(node) => self.visit(node),\n            Statement::If(node) => self.visit(node),\n            Statement::DoWhileLoop(node) => self.visit(node),\n            Statement::WhileLoop(node) => self.visit(node),\n            Statement::ForLoop(node) => self.visit(node),\n            Statement::ForInLoop(node) => self.visit(node),\n            Statement::ForOfLoop(node) => self.visit(node),\n            Statement::Switch(node) => self.visit(node),\n            Statement::Labelled(node) => self.visit(node),\n            Statement::Try(node) => self.visit(node),\n            Statement::With(node) => self.visit(node),\n            _ => ControlFlow::Continue(()),\n        }\n    }\n\n    fn visit_block(&mut self, node: &'ast crate::statement::Block) -> ControlFlow<Self::BreakTy> {\n        self.visit(node.statement_list())?;\n        for statement in node.statement_list().statements() {\n            if let StatementListItem::Declaration(declaration) = statement\n                && let Declaration::FunctionDeclaration(function) = &**declaration\n            {\n                let name = function.name();\n                self.0.push(name.sym());\n            }\n        }\n\n        let lexically_declared_names = lexically_declared_names_legacy(node.statement_list());\n\n        self.0\n            .retain(|name| !lexically_declared_names.contains(&(*name, false)));\n\n        ControlFlow::Continue(())\n    }\n\n    fn visit_switch(&mut self, node: &'ast crate::statement::Switch) -> ControlFlow<Self::BreakTy> {\n        for case in node.cases() {\n            self.visit(case)?;\n            for statement in case.body().statements() {\n                if let StatementListItem::Declaration(declaration) = statement\n                    && let Declaration::FunctionDeclaration(function) = &**declaration\n                {\n                    let name = function.name();\n                    self.0.push(name.sym());\n                }\n            }\n        }\n        if let Some(default) = node.default() {\n            self.visit(default)?;\n            for statement in default.statements() {\n                if let StatementListItem::Declaration(declaration) = statement\n                    && let Declaration::FunctionDeclaration(function) = declaration.as_ref()\n                {\n                    let name = function.name();\n                    self.0.push(name.sym());\n                }\n            }\n        }\n\n        let lexically_declared_names = lexically_declared_names_legacy(node);\n\n        self.0\n            .retain(|name| !lexically_declared_names.contains(&(*name, false)));\n\n        ControlFlow::Continue(())\n    }\n\n    fn visit_try(&mut self, node: &'ast crate::statement::Try) -> ControlFlow<Self::BreakTy> {\n        self.visit(node.block())?;\n        if let Some(catch) = node.catch() {\n            self.visit(catch.block())?;\n\n            if let Some(Binding::Pattern(pattern)) = catch.parameter() {\n                let bound_names = bound_names(pattern);\n\n                self.0.retain(|name| !bound_names.contains(name));\n            }\n        }\n        if let Some(finally) = node.finally() {\n            self.visit(finally.block())?;\n        }\n        ControlFlow::Continue(())\n    }\n\n    fn visit_if(&mut self, node: &'ast crate::statement::If) -> ControlFlow<Self::BreakTy> {\n        if let Some(node) = node.else_node() {\n            self.visit(node)?;\n        }\n        self.visit(node.body())\n    }\n\n    fn visit_do_while_loop(\n        &mut self,\n        node: &'ast crate::statement::DoWhileLoop,\n    ) -> ControlFlow<Self::BreakTy> {\n        self.visit(node.body())\n    }\n\n    fn visit_while_loop(\n        &mut self,\n        node: &'ast crate::statement::WhileLoop,\n    ) -> ControlFlow<Self::BreakTy> {\n        self.visit(node.body())\n    }\n\n    fn visit_for_loop(\n        &mut self,\n        node: &'ast crate::statement::ForLoop,\n    ) -> ControlFlow<Self::BreakTy> {\n        self.visit(node.body())?;\n\n        if let Some(ForLoopInitializer::Lexical(node)) = node.init() {\n            let bound_names = bound_names(&node.declaration);\n            self.0.retain(|name| !bound_names.contains(name));\n        }\n\n        ControlFlow::Continue(())\n    }\n\n    fn visit_for_in_loop(\n        &mut self,\n        node: &'ast crate::statement::ForInLoop,\n    ) -> ControlFlow<Self::BreakTy> {\n        self.visit(node.body())?;\n\n        if let IterableLoopInitializer::Let(node) = node.initializer() {\n            let bound_names = bound_names(node);\n            self.0.retain(|name| !bound_names.contains(name));\n        }\n        if let IterableLoopInitializer::Const(node) = node.initializer() {\n            let bound_names = bound_names(node);\n            self.0.retain(|name| !bound_names.contains(name));\n        }\n\n        ControlFlow::Continue(())\n    }\n\n    fn visit_for_of_loop(\n        &mut self,\n        node: &'ast crate::statement::ForOfLoop,\n    ) -> ControlFlow<Self::BreakTy> {\n        self.visit(node.body())?;\n\n        if let IterableLoopInitializer::Let(node) = node.initializer() {\n            let bound_names = bound_names(node);\n            self.0.retain(|name| !bound_names.contains(name));\n        }\n        if let IterableLoopInitializer::Const(node) = node.initializer() {\n            let bound_names = bound_names(node);\n            self.0.retain(|name| !bound_names.contains(name));\n        }\n\n        ControlFlow::Continue(())\n    }\n\n    fn visit_labelled(\n        &mut self,\n        node: &'ast crate::statement::Labelled,\n    ) -> ControlFlow<Self::BreakTy> {\n        if let LabelledItem::Statement(node) = node.item() {\n            self.visit(node)?;\n        }\n        ControlFlow::Continue(())\n    }\n\n    fn visit_with(&mut self, node: &'ast With) -> ControlFlow<Self::BreakTy> {\n        self.visit(node.statement())\n    }\n}\n\n/// Returns `true` if the given statement returns a value.\n#[must_use]\npub fn returns_value<'a, N>(node: &'a N) -> bool\nwhere\n    &'a N: Into<NodeRef<'a>>,\n{\n    ReturnsValueVisitor.visit(node.into()).is_break()\n}\n\n/// The [`Visitor`] used for [`returns_value`].\n#[derive(Debug)]\nstruct ReturnsValueVisitor;\n\nimpl<'ast> Visitor<'ast> for ReturnsValueVisitor {\n    type BreakTy = ();\n\n    fn visit_block(&mut self, node: &'ast crate::statement::Block) -> ControlFlow<Self::BreakTy> {\n        for statement in node.statement_list().statements() {\n            match statement {\n                StatementListItem::Declaration(_) => {}\n                StatementListItem::Statement(node) => self.visit(node.as_ref())?,\n            }\n        }\n        ControlFlow::Continue(())\n    }\n\n    fn visit_statement(&mut self, node: &'ast Statement) -> ControlFlow<Self::BreakTy> {\n        match node {\n            Statement::Empty | Statement::Var(_) => {}\n            Statement::Block(node) => self.visit(node)?,\n            Statement::Labelled(node) => self.visit(node)?,\n            _ => return ControlFlow::Break(()),\n        }\n        ControlFlow::Continue(())\n    }\n\n    fn visit_case(&mut self, node: &'ast crate::statement::Case) -> ControlFlow<Self::BreakTy> {\n        for statement in node.body().statements() {\n            match statement {\n                StatementListItem::Declaration(_) => {}\n                StatementListItem::Statement(node) => self.visit(node.as_ref())?,\n            }\n        }\n        ControlFlow::Continue(())\n    }\n\n    fn visit_labelled(\n        &mut self,\n        node: &'ast crate::statement::Labelled,\n    ) -> ControlFlow<Self::BreakTy> {\n        match node.item() {\n            LabelledItem::Statement(node) => self.visit(node)?,\n            LabelledItem::FunctionDeclaration(_) => {}\n        }\n        ControlFlow::Continue(())\n    }\n}\n"
  },
  {
    "path": "core/ast/src/operations/tests.rs",
    "content": "use boa_interner::Interner;\n\nuse crate::{\n    Position, Span, Statement,\n    expression::{Call, Identifier, NewTarget, This},\n    operations::{ContainsSymbol, contains},\n    statement::With,\n};\n\nfn empty_span() -> Span {\n    Span::new(Position::new(1, 1), Position::new(1, 1))\n}\n\n#[test]\nfn check_contains_this_in_with_statement_expression() {\n    let node = With::new(This::new(empty_span()).into(), Statement::Empty);\n    assert!(contains(&node, ContainsSymbol::This));\n}\n\n#[test]\nfn check_contains_new_target_in_with_statement_expression() {\n    let node = With::new(NewTarget::new(empty_span()).into(), Statement::Empty);\n    assert!(contains(&node, ContainsSymbol::NewTarget));\n}\n\n#[test]\nfn check_contains_new_target_in_call_function_position() {\n    let node = Call::new(\n        NewTarget::new(empty_span()).into(),\n        Box::default(),\n        empty_span(),\n    );\n    assert!(contains(&node, ContainsSymbol::NewTarget));\n}\n\n#[test]\nfn check_contains_this_in_call_argument_position() {\n    let mut interner = Interner::new();\n    let function_name = Identifier::new(interner.get_or_intern(\"func\"), Span::new((1, 1), (1, 5)));\n    let node = Call::new(\n        function_name.into(),\n        vec![This::new(empty_span()).into()].into_boxed_slice(),\n        empty_span(),\n    );\n\n    assert!(contains(&node, ContainsSymbol::This));\n}\n\n#[test]\nfn check_contains_new_target_in_call_argument_position() {\n    let mut interner = Interner::new();\n    let function_name = Identifier::new(interner.get_or_intern(\"func\"), Span::new((1, 1), (1, 5)));\n    let node = Call::new(\n        function_name.into(),\n        vec![NewTarget::new(empty_span()).into()].into_boxed_slice(),\n        empty_span(),\n    );\n\n    assert!(contains(&node, ContainsSymbol::NewTarget));\n}\n"
  },
  {
    "path": "core/ast/src/pattern.rs",
    "content": "//! A pattern binding or assignment node.\n//!\n//! A [`Pattern`] Corresponds to the [`BindingPattern`][spec1] and the [`AssignmentPattern`][spec2]\n//! nodes, each of which is used in different situations and have slightly different grammars.\n//! For example, a variable declaration combined with a destructuring expression is a `BindingPattern`:\n//!\n//! ```Javascript\n//! const obj = { a: 1, b: 2 };\n//! const { a, b } = obj; // BindingPattern\n//! ```\n//!\n//! On the other hand, a simple destructuring expression with already declared variables is called\n//! an `AssignmentPattern`:\n//!\n//! ```Javascript\n//! let a = 1;\n//! let b = 3;\n//! [a, b] = [b, a]; // AssignmentPattern\n//! ```\n//!\n//! [spec1]: https://tc39.es/ecma262/#prod-BindingPattern\n//! [spec2]: https://tc39.es/ecma262/#prod-AssignmentPattern\n//! [destr]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Destructuring_assignment\n\nuse crate::{\n    Expression, Span, Spanned,\n    expression::{Identifier, access::PropertyAccess},\n    property::PropertyName,\n    visitor::{VisitWith, Visitor, VisitorMut},\n};\nuse boa_interner::{Interner, ToInternedString};\nuse core::{fmt::Write as _, ops::ControlFlow};\n\n/// An object or array pattern binding or assignment.\n///\n/// See the [module level documentation][self] for more information.\n#[cfg_attr(feature = \"serde\", derive(serde::Serialize, serde::Deserialize))]\n#[cfg_attr(feature = \"arbitrary\", derive(arbitrary::Arbitrary))]\n#[derive(Clone, Debug, PartialEq)]\npub enum Pattern {\n    /// An object pattern (`let {a, b, c} = object`).\n    Object(ObjectPattern),\n    /// An array pattern (`[a, b, c] = array`).\n    Array(ArrayPattern),\n}\n\nimpl Spanned for Pattern {\n    #[inline]\n    fn span(&self) -> Span {\n        match self {\n            Pattern::Object(object_pattern) => object_pattern.span(),\n            Pattern::Array(array_pattern) => array_pattern.span(),\n        }\n    }\n}\n\nimpl From<ObjectPattern> for Pattern {\n    fn from(obj: ObjectPattern) -> Self {\n        Self::Object(obj)\n    }\n}\n\nimpl From<ArrayPattern> for Pattern {\n    fn from(obj: ArrayPattern) -> Self {\n        Self::Array(obj)\n    }\n}\n\nimpl ToInternedString for Pattern {\n    fn to_interned_string(&self, interner: &Interner) -> String {\n        match &self {\n            Self::Object(o) => o.to_interned_string(interner),\n            Self::Array(a) => a.to_interned_string(interner),\n        }\n    }\n}\n\nimpl VisitWith for Pattern {\n    fn visit_with<'a, V>(&'a self, visitor: &mut V) -> ControlFlow<V::BreakTy>\n    where\n        V: Visitor<'a>,\n    {\n        match self {\n            Self::Object(op) => visitor.visit_object_pattern(op),\n            Self::Array(ap) => visitor.visit_array_pattern(ap),\n        }\n    }\n\n    fn visit_with_mut<'a, V>(&'a mut self, visitor: &mut V) -> ControlFlow<V::BreakTy>\n    where\n        V: VisitorMut<'a>,\n    {\n        match self {\n            Self::Object(op) => visitor.visit_object_pattern_mut(op),\n            Self::Array(ap) => visitor.visit_array_pattern_mut(ap),\n        }\n    }\n}\n\n/// An object binding or assignment pattern.\n///\n/// Corresponds to the [`ObjectBindingPattern`][spec1] and the [`ObjectAssignmentPattern`][spec2]\n/// Parse Nodes.\n///\n/// For more information on what is a valid binding in an object pattern, see [`ObjectPatternElement`].\n///\n/// [spec1]: https://tc39.es/ecma262/#prod-ObjectBindingPattern\n/// [spec2]: https://tc39.es/ecma262/#prod-ObjectAssignmentPattern\n#[cfg_attr(feature = \"serde\", derive(serde::Serialize, serde::Deserialize))]\n#[cfg_attr(feature = \"arbitrary\", derive(arbitrary::Arbitrary))]\n#[derive(Clone, Debug, PartialEq)]\npub struct ObjectPattern {\n    elements: Box<[ObjectPatternElement]>,\n    span: Span,\n}\n\nimpl ToInternedString for ObjectPattern {\n    fn to_interned_string(&self, interner: &Interner) -> String {\n        let mut buf = \"{\".to_owned();\n        for (i, binding) in self.elements.iter().enumerate() {\n            let binding = binding.to_interned_string(interner);\n            let str = if i == self.elements.len() - 1 {\n                format!(\"{binding} \")\n            } else {\n                format!(\"{binding},\")\n            };\n\n            buf.push_str(&str);\n        }\n        if self.elements.is_empty() {\n            buf.push(' ');\n        }\n        buf.push('}');\n        buf\n    }\n}\n\nimpl ObjectPattern {\n    /// Creates a new object binding pattern.\n    #[inline]\n    #[must_use]\n    pub const fn new(elements: Box<[ObjectPatternElement]>, span: Span) -> Self {\n        Self { elements, span }\n    }\n\n    /// Gets the bindings for the object binding pattern.\n    #[inline]\n    #[must_use]\n    pub const fn bindings(&self) -> &[ObjectPatternElement] {\n        &self.elements\n    }\n\n    /// Returns true if the object binding pattern has a rest element.\n    #[inline]\n    #[must_use]\n    pub const fn has_rest(&self) -> bool {\n        matches!(\n            self.elements.last(),\n            Some(ObjectPatternElement::RestProperty { .. })\n        )\n    }\n}\n\nimpl Spanned for ObjectPattern {\n    #[inline]\n    fn span(&self) -> Span {\n        self.span\n    }\n}\n\nimpl VisitWith for ObjectPattern {\n    fn visit_with<'a, V>(&'a self, visitor: &mut V) -> ControlFlow<V::BreakTy>\n    where\n        V: Visitor<'a>,\n    {\n        for elem in &*self.elements {\n            visitor.visit_object_pattern_element(elem)?;\n        }\n        ControlFlow::Continue(())\n    }\n\n    fn visit_with_mut<'a, V>(&'a mut self, visitor: &mut V) -> ControlFlow<V::BreakTy>\n    where\n        V: VisitorMut<'a>,\n    {\n        for elem in &mut *self.elements {\n            visitor.visit_object_pattern_element_mut(elem)?;\n        }\n        ControlFlow::Continue(())\n    }\n}\n\n/// An array binding or assignment pattern.\n///\n/// Corresponds to the [`ArrayBindingPattern`][spec1] and the [`ArrayAssignmentPattern`][spec2]\n/// Parse Nodes.\n///\n/// For more information on what is a valid binding in an array pattern, see [`ArrayPatternElement`].\n///\n/// [spec1]: https://tc39.es/ecma262/#prod-ArrayBindingPattern\n/// [spec2]: https://tc39.es/ecma262/#prod-ArrayAssignmentPattern\n#[cfg_attr(feature = \"serde\", derive(serde::Serialize, serde::Deserialize))]\n#[cfg_attr(feature = \"arbitrary\", derive(arbitrary::Arbitrary))]\n#[derive(Clone, Debug, PartialEq)]\npub struct ArrayPattern {\n    bindings: Box<[ArrayPatternElement]>,\n    span: Span,\n}\n\nimpl ToInternedString for ArrayPattern {\n    fn to_interned_string(&self, interner: &Interner) -> String {\n        let mut buf = \"[\".to_owned();\n        for (i, binding) in self.bindings.iter().enumerate() {\n            if i == self.bindings.len() - 1 {\n                match binding {\n                    ArrayPatternElement::Elision => {\n                        let _ = write!(buf, \"{}, \", binding.to_interned_string(interner));\n                    }\n                    _ => {\n                        let _ = write!(buf, \"{} \", binding.to_interned_string(interner));\n                    }\n                }\n            } else {\n                let _ = write!(buf, \"{},\", binding.to_interned_string(interner));\n            }\n        }\n        buf.push(']');\n        buf\n    }\n}\n\nimpl ArrayPattern {\n    /// Creates a new array binding pattern.\n    #[inline]\n    #[must_use]\n    pub fn new(bindings: Box<[ArrayPatternElement]>, span: Span) -> Self {\n        Self { bindings, span }\n    }\n\n    /// Gets the bindings for the array binding pattern.\n    #[inline]\n    #[must_use]\n    pub const fn bindings(&self) -> &[ArrayPatternElement] {\n        &self.bindings\n    }\n}\n\nimpl Spanned for ArrayPattern {\n    #[inline]\n    fn span(&self) -> Span {\n        self.span\n    }\n}\n\nimpl VisitWith for ArrayPattern {\n    fn visit_with<'a, V>(&'a self, visitor: &mut V) -> ControlFlow<V::BreakTy>\n    where\n        V: Visitor<'a>,\n    {\n        for elem in &*self.bindings {\n            visitor.visit_array_pattern_element(elem)?;\n        }\n        ControlFlow::Continue(())\n    }\n\n    fn visit_with_mut<'a, V>(&'a mut self, visitor: &mut V) -> ControlFlow<V::BreakTy>\n    where\n        V: VisitorMut<'a>,\n    {\n        for elem in &mut *self.bindings {\n            visitor.visit_array_pattern_element_mut(elem)?;\n        }\n        ControlFlow::Continue(())\n    }\n}\n\n/// The different types of bindings that an [`ObjectPattern`] may contain.\n///\n/// Corresponds to the [`BindingProperty`][spec1] and the [`AssignmentProperty`][spec2] nodes.\n///\n/// [spec1]: https://tc39.es/ecma262/#prod-BindingProperty\n/// [spec2]: https://tc39.es/ecma262/#prod-AssignmentProperty\n#[cfg_attr(feature = \"serde\", derive(serde::Serialize, serde::Deserialize))]\n#[cfg_attr(feature = \"arbitrary\", derive(arbitrary::Arbitrary))]\n#[derive(Clone, Debug, PartialEq)]\npub enum ObjectPatternElement {\n    /// `SingleName` represents one of the following properties:\n    ///\n    /// - `SingleName` with an identifier and an optional default initializer.\n    /// - `BindingProperty` with an property name and a `SingleNameBinding` as  the `BindingElement`.\n    ///\n    /// More information:\n    ///  - [ECMAScript reference: 14.3.3 Destructuring Binding Patterns - SingleNameBinding][spec1]\n    ///  - [ECMAScript reference: 14.3.3 Destructuring Binding Patterns - BindingProperty][spec2]\n    ///\n    /// [spec1]: https://tc39.es/ecma262/#prod-SingleNameBinding\n    /// [spec2]: https://tc39.es/ecma262/#prod-BindingProperty\n    SingleName {\n        /// The identifier name of the property to be destructured.\n        name: PropertyName,\n        /// The variable name where the property value will be stored.\n        ident: Identifier,\n        /// An optional default value for the variable, in case the property doesn't exist.\n        default_init: Option<Expression>,\n    },\n\n    /// `RestProperty` represents a `BindingRestProperty` with an identifier.\n    ///\n    /// It also includes a list of the property keys that should be excluded from the rest,\n    /// because they where already assigned.\n    ///\n    /// More information:\n    ///  - [ECMAScript reference: 14.3.3 Destructuring Binding Patterns - BindingRestProperty][spec1]\n    ///\n    /// [spec1]: https://tc39.es/ecma262/#prod-BindingRestProperty\n    RestProperty {\n        /// The variable name where the unassigned properties will be stored.\n        ident: Identifier,\n    },\n\n    /// `AssignmentGetField` represents an `AssignmentProperty` with an expression field member expression `AssignmentElement`.\n    ///\n    /// Note: According to the spec this is not part of an `ObjectBindingPattern`.\n    /// This is only used when a object literal is used to cover an `AssignmentPattern`.\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#prod-AssignmentProperty\n    AssignmentPropertyAccess {\n        /// The identifier name of the property to be destructured.\n        name: PropertyName,\n        /// The property access where the property value will be destructured.\n        access: PropertyAccess,\n        /// An optional default value for the variable, in case the property doesn't exist.\n        default_init: Option<Expression>,\n    },\n\n    /// `AssignmentRestProperty` represents a rest property with a `DestructuringAssignmentTarget`.\n    ///\n    /// Note: According to the spec this is not part of an `ObjectBindingPattern`.\n    /// This is only used when a object literal is used to cover an `AssignmentPattern`.\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#prod-AssignmentRestProperty\n    AssignmentRestPropertyAccess {\n        /// The property access where the unassigned properties will be stored.\n        access: PropertyAccess,\n    },\n\n    /// Pattern represents a property with a `Pattern` as the element.\n    ///\n    /// Additionally to the identifier of the new property and the nested pattern,\n    /// this may also include an optional default initializer.\n    ///\n    /// More information:\n    ///  - [ECMAScript reference: 14.3.3 Destructuring Binding Patterns - BindingProperty][spec1]\n    ///\n    /// [spec1]: https://tc39.es/ecma262/#prod-BindingProperty\n    Pattern {\n        /// The identifier name of the property to be destructured.\n        name: PropertyName,\n        /// The pattern where the property value will be destructured.\n        pattern: Pattern,\n        /// An optional default value for the variable, in case the property doesn't exist.\n        default_init: Option<Expression>,\n    },\n}\n\nimpl ToInternedString for ObjectPatternElement {\n    fn to_interned_string(&self, interner: &Interner) -> String {\n        match self {\n            Self::SingleName {\n                ident,\n                name,\n                default_init,\n            } => {\n                let mut buf = match name {\n                    PropertyName::Literal(name) if name == ident => {\n                        format!(\" {}\", interner.resolve_expect(ident.sym()))\n                    }\n                    PropertyName::Literal(name) => {\n                        format!(\n                            \" {} : {}\",\n                            interner.resolve_expect(name.sym()),\n                            interner.resolve_expect(ident.sym())\n                        )\n                    }\n                    PropertyName::Computed(node) => {\n                        format!(\n                            \" [{}] : {}\",\n                            node.to_interned_string(interner),\n                            interner.resolve_expect(ident.sym())\n                        )\n                    }\n                };\n                if let Some(init) = default_init {\n                    let _ = write!(buf, \" = {}\", init.to_interned_string(interner));\n                }\n                buf\n            }\n            Self::RestProperty { ident } => {\n                format!(\" ... {}\", interner.resolve_expect(ident.sym()))\n            }\n            Self::AssignmentRestPropertyAccess { access } => {\n                format!(\" ... {}\", access.to_interned_string(interner))\n            }\n            Self::AssignmentPropertyAccess {\n                name,\n                access,\n                default_init,\n            } => {\n                let mut buf = match name {\n                    PropertyName::Literal(name) => {\n                        format!(\n                            \" {} : {}\",\n                            interner.resolve_expect(name.sym()),\n                            access.to_interned_string(interner)\n                        )\n                    }\n                    PropertyName::Computed(node) => {\n                        format!(\n                            \" [{}] : {}\",\n                            node.to_interned_string(interner),\n                            access.to_interned_string(interner)\n                        )\n                    }\n                };\n                if let Some(init) = &default_init {\n                    let _ = write!(buf, \" = {}\", init.to_interned_string(interner));\n                }\n                buf\n            }\n            Self::Pattern {\n                name,\n                pattern,\n                default_init,\n            } => {\n                let mut buf = match name {\n                    PropertyName::Literal(name) => {\n                        format!(\n                            \" {} : {}\",\n                            interner.resolve_expect(name.sym()),\n                            pattern.to_interned_string(interner),\n                        )\n                    }\n                    PropertyName::Computed(node) => {\n                        format!(\n                            \" [{}] : {}\",\n                            node.to_interned_string(interner),\n                            pattern.to_interned_string(interner),\n                        )\n                    }\n                };\n                if let Some(init) = default_init {\n                    let _ = write!(buf, \" = {}\", init.to_interned_string(interner));\n                }\n                buf\n            }\n        }\n    }\n}\n\nimpl VisitWith for ObjectPatternElement {\n    fn visit_with<'a, V>(&'a self, visitor: &mut V) -> ControlFlow<V::BreakTy>\n    where\n        V: Visitor<'a>,\n    {\n        match self {\n            Self::SingleName {\n                name,\n                ident,\n                default_init,\n            } => {\n                visitor.visit_property_name(name)?;\n                visitor.visit_identifier(ident)?;\n                if let Some(expr) = default_init {\n                    visitor.visit_expression(expr)\n                } else {\n                    ControlFlow::Continue(())\n                }\n            }\n            Self::RestProperty { ident, .. } => visitor.visit_identifier(ident),\n            Self::AssignmentPropertyAccess {\n                name,\n                access,\n                default_init,\n            } => {\n                visitor.visit_property_name(name)?;\n                visitor.visit_property_access(access)?;\n                if let Some(expr) = default_init {\n                    visitor.visit_expression(expr)\n                } else {\n                    ControlFlow::Continue(())\n                }\n            }\n            Self::AssignmentRestPropertyAccess { access, .. } => {\n                visitor.visit_property_access(access)\n            }\n            Self::Pattern {\n                name,\n                pattern,\n                default_init,\n            } => {\n                visitor.visit_property_name(name)?;\n                visitor.visit_pattern(pattern)?;\n                if let Some(expr) = default_init {\n                    visitor.visit_expression(expr)\n                } else {\n                    ControlFlow::Continue(())\n                }\n            }\n        }\n    }\n\n    fn visit_with_mut<'a, V>(&'a mut self, visitor: &mut V) -> ControlFlow<V::BreakTy>\n    where\n        V: VisitorMut<'a>,\n    {\n        match self {\n            Self::SingleName {\n                name,\n                ident,\n                default_init,\n            } => {\n                visitor.visit_property_name_mut(name)?;\n                visitor.visit_identifier_mut(ident)?;\n                if let Some(expr) = default_init {\n                    visitor.visit_expression_mut(expr)\n                } else {\n                    ControlFlow::Continue(())\n                }\n            }\n            Self::RestProperty { ident, .. } => visitor.visit_identifier_mut(ident),\n            Self::AssignmentPropertyAccess {\n                name,\n                access,\n                default_init,\n            } => {\n                visitor.visit_property_name_mut(name)?;\n                visitor.visit_property_access_mut(access)?;\n                if let Some(expr) = default_init {\n                    visitor.visit_expression_mut(expr)\n                } else {\n                    ControlFlow::Continue(())\n                }\n            }\n            Self::AssignmentRestPropertyAccess { access, .. } => {\n                visitor.visit_property_access_mut(access)\n            }\n            Self::Pattern {\n                name,\n                pattern,\n                default_init,\n            } => {\n                visitor.visit_property_name_mut(name)?;\n                visitor.visit_pattern_mut(pattern)?;\n                if let Some(expr) = default_init {\n                    visitor.visit_expression_mut(expr)\n                } else {\n                    ControlFlow::Continue(())\n                }\n            }\n        }\n    }\n}\n\n/// The different types of bindings that an array binding pattern may contain.\n///\n/// Corresponds to the [`BindingElement`][spec1] and the [`AssignmentElement`][spec2] nodes.\n///\n/// [spec1]: https://tc39.es/ecma262/#prod-BindingElement\n/// [spec2]: https://tc39.es/ecma262/#prod-AssignmentElement\n#[cfg_attr(feature = \"serde\", derive(serde::Serialize, serde::Deserialize))]\n#[cfg_attr(feature = \"arbitrary\", derive(arbitrary::Arbitrary))]\n#[derive(Clone, Debug, PartialEq)]\npub enum ArrayPatternElement {\n    /// Elision represents the elision of an item in the array binding pattern.\n    ///\n    /// An `Elision` may occur at multiple points in the pattern and may be multiple elisions.\n    /// This variant strictly represents one elision. If there are multiple, this should be used multiple times.\n    ///\n    /// More information:\n    ///  - [ECMAScript reference: 13.2.4 Array Initializer - Elision][spec1]\n    ///\n    /// [spec1]: https://tc39.es/ecma262/#prod-Elision\n    Elision,\n\n    /// `SingleName` represents a `SingleName` with an identifier and an optional default initializer.\n    ///\n    /// More information:\n    ///  - [ECMAScript reference: 14.3.3 Destructuring Binding Patterns - SingleNameBinding][spec1]\n    ///\n    /// [spec1]: https://tc39.es/ecma262/#prod-SingleNameBinding\n    SingleName {\n        /// The variable name where the index element will be stored.\n        ident: Identifier,\n        /// An optional default value for the variable, in case the index element doesn't exist.\n        default_init: Option<Expression>,\n    },\n\n    /// `PropertyAccess` represents a binding with a property accessor.\n    ///\n    /// Note: According to the spec this is not part of an `ArrayBindingPattern`.\n    /// This is only used when a array literal is used as the left-hand-side of an assignment expression.\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#prod-AssignmentExpression\n    PropertyAccess {\n        /// The property access where the index element will be stored.\n        access: PropertyAccess,\n        /// An optional default value for the variable, in case the index element doesn't exist.\n        default_init: Option<Expression>,\n    },\n\n    /// Pattern represents a `Pattern` in an `Element` of an array pattern.\n    ///\n    /// The pattern and the optional default initializer are both stored in the `DeclarationPattern`.\n    ///\n    /// More information:\n    ///  - [ECMAScript reference: 14.3.3 Destructuring Binding Patterns - BindingElement][spec1]\n    ///\n    /// [spec1]: https://tc39.es/ecma262/#prod-BindingElement\n    Pattern {\n        /// The pattern where the index element will be stored.\n        pattern: Pattern,\n        /// An optional default value for the pattern, in case the index element doesn't exist.\n        default_init: Option<Expression>,\n    },\n\n    /// `SingleNameRest` represents a `BindingIdentifier` in a `BindingRestElement` of an array pattern.\n    ///\n    /// More information:\n    ///  - [ECMAScript reference: 14.3.3 Destructuring Binding Patterns - BindingRestElement][spec1]\n    ///\n    /// [spec1]: https://tc39.es/ecma262/#prod-BindingRestElement\n    SingleNameRest {\n        /// The variable where the unassigned index elements will be stored.\n        ident: Identifier,\n    },\n\n    /// `PropertyAccess` represents a rest (spread operator) with a property accessor.\n    ///\n    /// Note: According to the spec this is not part of an `ArrayBindingPattern`.\n    /// This is only used when a array literal is used as the left-hand-side of an assignment expression.\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#prod-AssignmentExpression\n    PropertyAccessRest {\n        /// The property access where the unassigned index elements will be stored.\n        access: PropertyAccess,\n    },\n\n    /// `PatternRest` represents a `Pattern` in a `RestElement` of an array pattern.\n    ///\n    /// More information:\n    ///  - [ECMAScript reference: 14.3.3 Destructuring Binding Patterns - BindingRestElement][spec1]\n    ///\n    /// [spec1]: https://tc39.es/ecma262/#prod-BindingRestElement\n    PatternRest {\n        /// The pattern where the unassigned index elements will be stored.\n        pattern: Pattern,\n    },\n}\n\nimpl ToInternedString for ArrayPatternElement {\n    fn to_interned_string(&self, interner: &Interner) -> String {\n        match self {\n            Self::Elision => \" \".to_owned(),\n            Self::SingleName {\n                ident,\n                default_init,\n            } => {\n                let mut buf = format!(\" {}\", interner.resolve_expect(ident.sym()));\n                if let Some(init) = default_init {\n                    let _ = write!(buf, \" = {}\", init.to_interned_string(interner));\n                }\n                buf\n            }\n            Self::PropertyAccess {\n                access,\n                default_init,\n            } => {\n                let mut buf = format!(\" {}\", access.to_interned_string(interner));\n                if let Some(init) = default_init {\n                    let _ = write!(buf, \" = {}\", init.to_interned_string(interner));\n                }\n                buf\n            }\n            Self::Pattern {\n                pattern,\n                default_init,\n            } => {\n                let mut buf = format!(\" {}\", pattern.to_interned_string(interner));\n                if let Some(init) = default_init {\n                    let _ = write!(buf, \" = {}\", init.to_interned_string(interner));\n                }\n                buf\n            }\n            Self::SingleNameRest { ident } => {\n                format!(\" ... {}\", interner.resolve_expect(ident.sym()))\n            }\n            Self::PropertyAccessRest { access } => {\n                format!(\" ... {}\", access.to_interned_string(interner))\n            }\n            Self::PatternRest { pattern } => {\n                format!(\" ... {}\", pattern.to_interned_string(interner))\n            }\n        }\n    }\n}\n\nimpl VisitWith for ArrayPatternElement {\n    fn visit_with<'a, V>(&'a self, visitor: &mut V) -> ControlFlow<V::BreakTy>\n    where\n        V: Visitor<'a>,\n    {\n        match self {\n            Self::SingleName {\n                ident,\n                default_init,\n            } => {\n                visitor.visit_identifier(ident)?;\n                if let Some(expr) = default_init {\n                    visitor.visit_expression(expr)\n                } else {\n                    ControlFlow::Continue(())\n                }\n            }\n            Self::PropertyAccess {\n                access,\n                default_init,\n            } => {\n                visitor.visit_property_access(access)?;\n                if let Some(expr) = default_init {\n                    visitor.visit_expression(expr)\n                } else {\n                    ControlFlow::Continue(())\n                }\n            }\n            Self::PropertyAccessRest { access } => visitor.visit_property_access(access),\n            Self::Pattern {\n                pattern,\n                default_init,\n            } => {\n                visitor.visit_pattern(pattern)?;\n                if let Some(expr) = default_init {\n                    visitor.visit_expression(expr)\n                } else {\n                    ControlFlow::Continue(())\n                }\n            }\n            Self::SingleNameRest { ident } => visitor.visit_identifier(ident),\n            Self::PatternRest { pattern } => visitor.visit_pattern(pattern),\n            Self::Elision => {\n                // special case to be handled by user\n                ControlFlow::Continue(())\n            }\n        }\n    }\n\n    fn visit_with_mut<'a, V>(&'a mut self, visitor: &mut V) -> ControlFlow<V::BreakTy>\n    where\n        V: VisitorMut<'a>,\n    {\n        match self {\n            Self::SingleName {\n                ident,\n                default_init,\n            } => {\n                visitor.visit_identifier_mut(ident)?;\n                if let Some(expr) = default_init {\n                    visitor.visit_expression_mut(expr)\n                } else {\n                    ControlFlow::Continue(())\n                }\n            }\n            Self::PropertyAccess {\n                access,\n                default_init,\n            } => {\n                visitor.visit_property_access_mut(access)?;\n                if let Some(expr) = default_init {\n                    visitor.visit_expression_mut(expr)\n                } else {\n                    ControlFlow::Continue(())\n                }\n            }\n            Self::PropertyAccessRest { access } => visitor.visit_property_access_mut(access),\n            Self::Pattern {\n                pattern,\n                default_init,\n            } => {\n                visitor.visit_pattern_mut(pattern)?;\n                if let Some(expr) = default_init {\n                    visitor.visit_expression_mut(expr)\n                } else {\n                    ControlFlow::Continue(())\n                }\n            }\n            Self::SingleNameRest { ident } => visitor.visit_identifier_mut(ident),\n            Self::PatternRest { pattern } => visitor.visit_pattern_mut(pattern),\n            Self::Elision => {\n                // special case to be handled by user\n                ControlFlow::Continue(())\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "core/ast/src/position.rs",
    "content": "use std::{\n    cmp::Ordering,\n    fmt::{self, Debug},\n    num::NonZeroU32,\n};\n\n/// A position in the ECMAScript source code.\n///\n/// Stores both the column number and the line number.\n///\n/// ## Similar Implementations\n/// [V8: Location](https://cs.chromium.org/chromium/src/v8/src/parsing/scanner.h?type=cs&q=isValid+Location&g=0&l=216)\n#[cfg_attr(feature = \"serde\", derive(serde::Serialize, serde::Deserialize))]\n#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]\npub struct Position {\n    /// Line number.\n    line_number: NonZeroU32,\n    /// Column number.\n    column_number: NonZeroU32,\n}\n\nimpl Default for Position {\n    /// Creates a new [`Position`] with line and column set to `1`.\n    #[inline]\n    fn default() -> Self {\n        Self::new(1, 1)\n    }\n}\n\nimpl Position {\n    /// Creates a new `Position` from Non-Zero values.\n    ///\n    /// # Panics\n    ///\n    /// Will panic if the line number or column number is zero.\n    #[inline]\n    #[track_caller]\n    #[must_use]\n    pub const fn new(line_number: u32, column_number: u32) -> Self {\n        Self {\n            line_number: NonZeroU32::new(line_number).expect(\"line number cannot be 0\"),\n            column_number: NonZeroU32::new(column_number).expect(\"column number cannot be 0\"),\n        }\n    }\n\n    /// Gets the line number of the position.\n    #[inline]\n    #[must_use]\n    pub const fn line_number(self) -> u32 {\n        self.line_number.get()\n    }\n\n    /// Gets the column number of the position.\n    #[inline]\n    #[must_use]\n    pub const fn column_number(self) -> u32 {\n        self.column_number.get()\n    }\n}\n\nimpl fmt::Display for Position {\n    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {\n        write!(f, \"{}:{}\", self.line_number, self.column_number)\n    }\n}\n\nimpl From<PositionGroup> for Position {\n    #[inline]\n    fn from(value: PositionGroup) -> Self {\n        value.pos\n    }\n}\n\nimpl From<(NonZeroU32, NonZeroU32)> for Position {\n    #[inline]\n    fn from(value: (NonZeroU32, NonZeroU32)) -> Self {\n        Position {\n            line_number: value.0,\n            column_number: value.1,\n        }\n    }\n}\n\nimpl From<(u32, u32)> for Position {\n    #[inline]\n    #[track_caller]\n    fn from(value: (u32, u32)) -> Self {\n        Position::new(value.0, value.1)\n    }\n}\n\n#[cfg(feature = \"arbitrary\")]\nimpl<'a> arbitrary::Arbitrary<'a> for Span {\n    fn arbitrary(_u: &mut arbitrary::Unstructured<'a>) -> arbitrary::Result<Self> {\n        Ok(Span::new(Position::new(1, 1), Position::new(1, 1)))\n    }\n}\n\n/// Linear position in the ECMAScript source code.\n///\n/// Stores linear position in the source code.\n#[cfg_attr(feature = \"serde\", derive(serde::Serialize, serde::Deserialize))]\n#[cfg_attr(feature = \"arbitrary\", derive(arbitrary::Arbitrary))]\n#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]\npub struct LinearPosition {\n    pos: usize,\n}\n\nimpl LinearPosition {\n    /// Creates a new `LinearPosition`.\n    #[inline]\n    #[must_use]\n    pub const fn new(pos: usize) -> Self {\n        Self { pos }\n    }\n    /// Gets the linear position.\n    #[inline]\n    #[must_use]\n    pub const fn pos(self) -> usize {\n        self.pos\n    }\n}\nimpl fmt::Display for LinearPosition {\n    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {\n        write!(f, \"{}\", self.pos())\n    }\n}\n\n/// A span in the ECMAScript source code.\n///\n/// Stores a start position and an end position.\n///\n/// Note that spans are of the form [start, end) i.e. that the start position is inclusive\n/// and the end position is exclusive.\n#[cfg_attr(feature = \"serde\", derive(serde::Serialize, serde::Deserialize))]\n#[derive(Clone, Copy, PartialEq, Eq, Hash)]\npub struct Span {\n    start: Position,\n    end: Position,\n}\n\nimpl Debug for Span {\n    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {\n        write!(\n            f,\n            \"Span(({}, {}), ({}, {}))\",\n            self.start.line_number,\n            self.start.column_number,\n            self.end.line_number,\n            self.end.column_number,\n        )\n    }\n}\n\nimpl Span {\n    /// Creates a new `Span`.\n    ///\n    /// # Panics\n    ///\n    /// Panics if the start position is bigger than the end position.\n    #[inline]\n    #[track_caller]\n    #[must_use]\n    pub fn new<T, U>(start: T, end: U) -> Self\n    where\n        T: Into<Position>,\n        U: Into<Position>,\n    {\n        let start = start.into();\n        let end = end.into();\n\n        assert!(start <= end, \"a span cannot start after its end\");\n\n        Self { start, end }\n    }\n\n    /// Gets the starting position of the span.\n    #[inline]\n    #[must_use]\n    pub const fn start(self) -> Position {\n        self.start\n    }\n\n    /// Gets the final position of the span.\n    #[inline]\n    #[must_use]\n    pub const fn end(self) -> Position {\n        self.end\n    }\n\n    /// Checks if this span inclusively contains another span or position.\n    pub fn contains<S>(self, other: S) -> bool\n    where\n        S: Into<Self>,\n    {\n        let other = other.into();\n        self.start <= other.start && self.end >= other.end\n    }\n}\n\nimpl From<Position> for Span {\n    fn from(pos: Position) -> Self {\n        Self {\n            start: pos,\n            end: pos,\n        }\n    }\n}\n\nimpl Spanned for Span {\n    #[inline]\n    fn span(&self) -> Span {\n        *self\n    }\n}\n\nimpl<T: Spanned> Spanned for &T {\n    #[inline]\n    fn span(&self) -> Span {\n        T::span(*self)\n    }\n}\n\nimpl PartialOrd for Span {\n    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {\n        if self == other {\n            Some(Ordering::Equal)\n        } else if self.end < other.start {\n            Some(Ordering::Less)\n        } else if self.start > other.end {\n            Some(Ordering::Greater)\n        } else {\n            None\n        }\n    }\n}\n\nimpl fmt::Display for Span {\n    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {\n        write!(f, \"[{}..{}]\", self.start, self.end)\n    }\n}\n\n/// An element of the AST or any type that can be located in the source with a Span.\npub trait Spanned {\n    /// Returns a span from the current type.\n    #[must_use]\n    fn span(&self) -> Span;\n}\n\n/// A linear span in the ECMAScript source code.\n///\n/// Stores a linear start position and a linear end position.\n///\n/// Note that linear spans are of the form [start, end) i.e. that the\n/// start position is inclusive and the end position is exclusive.\n#[cfg_attr(feature = \"serde\", derive(serde::Serialize, serde::Deserialize))]\n#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Hash)]\npub struct LinearSpan {\n    start: LinearPosition,\n    end: LinearPosition,\n}\nimpl LinearSpan {\n    /// Creates a new `LinearSpan`.\n    ///\n    /// # Panics\n    ///\n    /// Panics if the start position is bigger than the end position.\n    #[inline]\n    #[track_caller]\n    #[must_use]\n    pub const fn new(start: LinearPosition, end: LinearPosition) -> Self {\n        assert!(\n            start.pos <= end.pos,\n            \"a linear span cannot start after its end\"\n        );\n\n        Self { start, end }\n    }\n\n    /// Test if the span is empty.\n    #[inline]\n    #[must_use]\n    pub fn is_empty(self) -> bool {\n        self.start == self.end\n    }\n\n    /// Gets the starting position of the span.\n    #[inline]\n    #[must_use]\n    pub const fn start(self) -> LinearPosition {\n        self.start\n    }\n\n    /// Gets the final position of the span.\n    #[inline]\n    #[must_use]\n    pub const fn end(self) -> LinearPosition {\n        self.end\n    }\n\n    /// Checks if this span inclusively contains another span or position.\n    pub fn contains<S>(self, other: S) -> bool\n    where\n        S: Into<Self>,\n    {\n        let other = other.into();\n        self.start <= other.start && self.end >= other.end\n    }\n\n    /// Gets the starting position of the span.\n    #[inline]\n    #[must_use]\n    pub fn union(self, other: impl Into<Self>) -> Self {\n        let other: Self = other.into();\n        Self {\n            start: LinearPosition::new(self.start.pos.min(other.start.pos)),\n            end: LinearPosition::new(self.end.pos.max(other.end.pos)),\n        }\n    }\n}\n#[cfg(feature = \"arbitrary\")]\nimpl<'a> arbitrary::Arbitrary<'a> for LinearSpan {\n    fn arbitrary(_: &mut arbitrary::Unstructured<'a>) -> arbitrary::Result<Self> {\n        let zero_pos = LinearPosition::new(0);\n        Ok(Self::new(zero_pos, zero_pos))\n    }\n}\n\nimpl From<LinearPosition> for LinearSpan {\n    fn from(pos: LinearPosition) -> Self {\n        Self {\n            start: pos,\n            end: pos,\n        }\n    }\n}\n\nimpl PartialOrd for LinearSpan {\n    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {\n        if self == other {\n            Some(Ordering::Equal)\n        } else if self.end < other.start {\n            Some(Ordering::Less)\n        } else if self.start > other.end {\n            Some(Ordering::Greater)\n        } else {\n            None\n        }\n    }\n}\n\n/// Stores a `LinearSpan` but `PartialEq`, `Eq` always return true.\n#[cfg_attr(feature = \"serde\", derive(serde::Serialize, serde::Deserialize))]\n#[derive(Debug, Clone, Copy)]\npub struct LinearSpanIgnoreEq(pub LinearSpan);\nimpl PartialEq for LinearSpanIgnoreEq {\n    fn eq(&self, _: &Self) -> bool {\n        true\n    }\n}\nimpl From<LinearSpan> for LinearSpanIgnoreEq {\n    fn from(value: LinearSpan) -> Self {\n        Self(value)\n    }\n}\n#[cfg(feature = \"arbitrary\")]\nimpl<'a> arbitrary::Arbitrary<'a> for LinearSpanIgnoreEq {\n    fn arbitrary(u: &mut arbitrary::Unstructured<'a>) -> arbitrary::Result<Self> {\n        Ok(Self(LinearSpan::arbitrary(u)?))\n    }\n}\n\n#[cfg_attr(feature = \"serde\", derive(serde::Serialize, serde::Deserialize))]\n#[derive(Debug, Clone, Copy, PartialEq, Eq)]\n/// A position group of `LinearPosition` and `Position` related to the same position in the ECMAScript source code.\npub struct PositionGroup {\n    pos: Position,\n    linear_pos: LinearPosition,\n}\nimpl PositionGroup {\n    /// Creates a new `PositionGroup`.\n    #[inline]\n    #[must_use]\n    pub const fn new(pos: Position, linear_pos: LinearPosition) -> Self {\n        Self { pos, linear_pos }\n    }\n    /// Get the `Position`.\n    #[inline]\n    #[must_use]\n    pub fn position(&self) -> Position {\n        self.pos\n    }\n    /// Get the `LinearPosition`.\n    #[inline]\n    #[must_use]\n    pub fn linear_position(&self) -> LinearPosition {\n        self.linear_pos\n    }\n\n    /// Gets the line number of the position.\n    #[inline]\n    #[must_use]\n    pub const fn line_number(&self) -> u32 {\n        self.pos.line_number()\n    }\n\n    /// Gets the column number of the position.\n    #[inline]\n    #[must_use]\n    pub const fn column_number(&self) -> u32 {\n        self.pos.column_number()\n    }\n}\n\n#[cfg(test)]\nmod tests {\n    #![allow(clippy::similar_names)]\n    #![allow(unused_must_use)]\n    use super::{LinearPosition, LinearSpan, Position, Span};\n\n    /// Checks that we cannot create a position with 0 as the column.\n    #[test]\n    #[should_panic(expected = \"column number cannot be 0\")]\n    fn invalid_position_column() {\n        Position::new(10, 0);\n    }\n\n    /// Checks that we cannot create a position with 0 as the line.\n    #[test]\n    #[should_panic(expected = \"line number cannot be 0\")]\n    fn invalid_position_line() {\n        Position::new(0, 10);\n    }\n\n    /// Checks that the `PartialEq` implementation of `Position` is consistent.\n    #[test]\n    fn position_equality() {\n        assert_eq!(Position::new(10, 50), Position::new(10, 50));\n        assert_ne!(Position::new(10, 50), Position::new(10, 51));\n        assert_ne!(Position::new(10, 50), Position::new(11, 50));\n        assert_ne!(Position::new(10, 50), Position::new(11, 51));\n    }\n\n    /// Checks that the `PartialEq` implementation of `LinearPosition` is consistent.\n    #[test]\n    fn linear_position_equality() {\n        assert_eq!(LinearPosition::new(1050), LinearPosition::new(1050));\n        assert_ne!(LinearPosition::new(1050), LinearPosition::new(1051));\n    }\n\n    /// Checks that the `PartialOrd` implementation of `Position` is consistent.\n    #[test]\n    fn position_order() {\n        assert!(Position::new(10, 50) < Position::new(10, 51));\n        assert!(Position::new(9, 50) < Position::new(10, 50));\n        assert!(Position::new(10, 50) < Position::new(11, 51));\n        assert!(Position::new(10, 50) < Position::new(11, 49));\n\n        assert!(Position::new(10, 51) > Position::new(10, 50));\n        assert!(Position::new(10, 50) > Position::new(9, 50));\n        assert!(Position::new(11, 51) > Position::new(10, 50));\n        assert!(Position::new(11, 49) > Position::new(10, 50));\n    }\n\n    /// Checks that the `PartialOrd` implementation of `LinearPosition` is consistent.\n    #[test]\n    fn linear_position_order() {\n        assert!(LinearPosition::new(1050) < LinearPosition::new(1051));\n        assert!(LinearPosition::new(1149) > LinearPosition::new(1050));\n    }\n\n    /// Checks that the position getters actually retrieve correct values.\n    #[test]\n    fn position_getters() {\n        let pos = Position::new(10, 50);\n        assert_eq!(pos.line_number(), 10);\n        assert_eq!(pos.column_number(), 50);\n    }\n\n    /// Checks that the string representation of a position is correct.\n    #[test]\n    fn position_to_string() {\n        let pos = Position::new(10, 50);\n\n        assert_eq!(\"10:50\", pos.to_string());\n        assert_eq!(\"10:50\", pos.to_string());\n    }\n\n    /// Checks that we cannot create an invalid span.\n    #[test]\n    #[should_panic(expected = \"a span cannot start after its end\")]\n    fn invalid_span() {\n        let a = Position::new(10, 30);\n        let b = Position::new(10, 50);\n        Span::new(b, a);\n    }\n\n    /// Checks that we cannot create an invalid linear span.\n    #[test]\n    #[should_panic(expected = \"a linear span cannot start after its end\")]\n    fn invalid_linear_span() {\n        let a = LinearPosition::new(1030);\n        let b = LinearPosition::new(1050);\n        LinearSpan::new(b, a);\n    }\n\n    /// Checks that we can create valid spans.\n    #[test]\n    fn span_creation() {\n        let a = Position::new(10, 30);\n        let b = Position::new(10, 50);\n\n        Span::new(a, b);\n        Span::new(a, a);\n        Span::from(a);\n    }\n\n    /// Checks that we can create valid linear spans.\n    #[test]\n    fn linear_span_creation() {\n        let a = LinearPosition::new(1030);\n        let b = LinearPosition::new(1050);\n\n        LinearSpan::new(a, b);\n        let span_aa = LinearSpan::new(a, a);\n        assert_eq!(LinearSpan::from(a), span_aa);\n    }\n\n    /// Checks that the `PartialEq` implementation of `Span` is consistent.\n    #[test]\n    fn span_equality() {\n        let a = Position::new(10, 50);\n        let b = Position::new(10, 52);\n        let c = Position::new(11, 20);\n\n        let span_ab = Span::new(a, b);\n        let span_ab_2 = Span::new(a, b);\n        let span_ac = Span::new(a, c);\n        let span_bc = Span::new(b, c);\n\n        assert_eq!(span_ab, span_ab_2);\n        assert_ne!(span_ab, span_ac);\n        assert_ne!(span_ab, span_bc);\n        assert_ne!(span_bc, span_ac);\n\n        let span_a = Span::from(a);\n        let span_aa = Span::new(a, a);\n\n        assert_eq!(span_a, span_aa);\n    }\n\n    /// Checks that the `PartialEq` implementation of `LinearSpan` is consistent.\n    #[test]\n    fn linear_span_equality() {\n        let a = LinearPosition::new(1030);\n        let b = LinearPosition::new(1050);\n        let c = LinearPosition::new(1150);\n\n        let span_ab = LinearSpan::new(a, b);\n        let span_ab_2 = LinearSpan::new(a, b);\n        let span_ac = LinearSpan::new(a, c);\n        let span_bc = LinearSpan::new(b, c);\n\n        assert_eq!(span_ab, span_ab_2);\n        assert_ne!(span_ab, span_ac);\n        assert_ne!(span_ab, span_bc);\n        assert_ne!(span_bc, span_ac);\n    }\n\n    /// Checks that the getters retrieve the correct value.\n    #[test]\n    fn span_getters() {\n        let a = Position::new(10, 50);\n        let b = Position::new(10, 52);\n\n        let span = Span::new(a, b);\n\n        assert_eq!(span.start(), a);\n        assert_eq!(span.end(), b);\n    }\n\n    /// Checks that the `Span::contains()` method works properly.\n    #[test]\n    fn span_contains() {\n        let a = Position::new(10, 50);\n        let b = Position::new(10, 52);\n        let c = Position::new(11, 20);\n        let d = Position::new(12, 5);\n\n        let span_ac = Span::new(a, c);\n        assert!(span_ac.contains(b));\n\n        let span_ab = Span::new(a, b);\n        let span_cd = Span::new(c, d);\n\n        assert!(!span_ab.contains(span_cd));\n        assert!(span_ab.contains(b));\n\n        let span_ad = Span::new(a, d);\n        let span_bc = Span::new(b, c);\n\n        assert!(span_ad.contains(span_bc));\n        assert!(!span_bc.contains(span_ad));\n\n        let span_ac = Span::new(a, c);\n        let span_bd = Span::new(b, d);\n\n        assert!(!span_ac.contains(span_bd));\n        assert!(!span_bd.contains(span_ac));\n    }\n\n    /// Checks that the `LinearSpan::contains()` method works properly.\n    #[test]\n    fn linear_span_contains() {\n        let a = LinearPosition::new(1050);\n        let b = LinearPosition::new(1080);\n        let c = LinearPosition::new(1120);\n        let d = LinearPosition::new(1125);\n\n        let span_ac = LinearSpan::new(a, c);\n        assert!(span_ac.contains(b));\n\n        let span_ab = LinearSpan::new(a, b);\n        let span_cd = LinearSpan::new(c, d);\n\n        assert!(!span_ab.contains(span_cd));\n        assert!(span_ab.contains(b));\n\n        let span_ad = LinearSpan::new(a, d);\n        let span_bc = LinearSpan::new(b, c);\n\n        assert!(span_ad.contains(span_bc));\n        assert!(!span_bc.contains(span_ad));\n\n        let span_ac = LinearSpan::new(a, c);\n        let span_bd = LinearSpan::new(b, d);\n\n        assert!(!span_ac.contains(span_bd));\n        assert!(!span_bd.contains(span_ac));\n    }\n\n    /// Checks that the string representation of a span is correct.\n    #[test]\n    fn span_to_string() {\n        let a = Position::new(10, 50);\n        let b = Position::new(11, 20);\n        let span = Span::new(a, b);\n\n        assert_eq!(\"[10:50..11:20]\", span.to_string());\n        assert_eq!(\"[10:50..11:20]\", span.to_string());\n    }\n\n    /// Checks that the ordering of spans is correct.\n    #[test]\n    fn span_ordering() {\n        let a = Position::new(10, 50);\n        let b = Position::new(10, 52);\n        let c = Position::new(11, 20);\n        let d = Position::new(12, 5);\n\n        let span_ab = Span::new(a, b);\n        let span_cd = Span::new(c, d);\n\n        assert!(span_ab < span_cd);\n        assert!(span_cd > span_ab);\n    }\n\n    /// Checks that the ordering of linear spans is correct.\n    #[test]\n    fn linear_span_ordering() {\n        let a = LinearPosition::new(1050);\n        let b = LinearPosition::new(1052);\n        let c = LinearPosition::new(1120);\n        let d = LinearPosition::new(1125);\n\n        let span_ab = LinearSpan::new(a, b);\n        let span_cd = LinearSpan::new(c, d);\n\n        let span_ac = LinearSpan::new(a, c);\n        let span_bd = LinearSpan::new(b, d);\n\n        assert!(span_ab < span_cd);\n        assert!(span_cd > span_ab);\n        assert_eq!(span_bd.partial_cmp(&span_ac), None);\n        assert_eq!(span_ac.partial_cmp(&span_bd), None);\n    }\n\n    /// Checks that the ordering of linear spans is correct.\n    #[test]\n    fn linear_union() {\n        let a = LinearPosition::new(1050);\n        let b = LinearPosition::new(1052);\n        let c = LinearPosition::new(1120);\n        let d = LinearPosition::new(1125);\n\n        let span_ab = LinearSpan::new(a, b);\n        let span_ad = LinearSpan::new(a, d);\n        let span_bc = LinearSpan::new(b, c);\n        let span_cd = LinearSpan::new(c, d);\n        let span_ac = LinearSpan::new(a, c);\n        let span_bd = LinearSpan::new(b, d);\n\n        assert_eq!(span_bd.union(a), span_ad);\n        assert_eq!(span_ab.union(a), span_ab);\n        assert_eq!(span_bd.union(span_ac), span_ad);\n        assert_eq!(span_ac.union(span_bd), span_ad);\n        assert_eq!(span_ac.union(span_bd), span_ad);\n        assert_eq!(span_ac.union(b), span_ac);\n        assert_eq!(span_bc.union(span_ab), span_ac);\n        assert_eq!(span_ab.union(span_bc), span_ac);\n        assert_eq!(span_ac.union(span_ab), span_ac);\n        assert_eq!(span_cd.union(a), span_ad);\n        assert_eq!(span_cd.union(span_bc), span_bd);\n    }\n}\n\n// TODO: union Span & LinearSpan into `SpanBase<T>` and then:\n//       * Span = SpanBase<Position>;\n//       * LinearSpan = SpanBase<LinearPosition>;\n//       ?\n"
  },
  {
    "path": "core/ast/src/property.rs",
    "content": "//! Property definition related types, used in object literals and class definitions.\n\nuse super::{Expression, Spanned};\nuse crate::{\n    expression::Identifier,\n    visitor::{VisitWith, Visitor, VisitorMut},\n};\nuse boa_interner::{Interner, ToInternedString};\nuse core::ops::ControlFlow;\n\n/// `PropertyName` can be either a literal or computed.\n///\n/// More information:\n///  - [ECMAScript reference][spec]\n///\n/// [spec]: https://tc39.es/ecma262/#prod-PropertyName\n#[cfg_attr(feature = \"serde\", derive(serde::Serialize, serde::Deserialize))]\n#[cfg_attr(feature = \"arbitrary\", derive(arbitrary::Arbitrary))]\n#[derive(Clone, Debug, PartialEq)]\npub enum PropertyName {\n    /// A `Literal` property name can be either an identifier, a string or a numeric literal.\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#prod-LiteralPropertyName\n    Literal(Identifier),\n\n    /// A `Computed` property name is an expression that gets evaluated and converted into a property name.\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#prod-ComputedPropertyName\n    Computed(Expression),\n}\n\nimpl PropertyName {\n    /// Returns the literal property name if it exists.\n    #[must_use]\n    pub const fn literal(&self) -> Option<Identifier> {\n        if let Self::Literal(ident) = self {\n            Some(*ident)\n        } else {\n            None\n        }\n    }\n\n    /// Returns the expression if the property name is computed.\n    #[must_use]\n    pub const fn computed(&self) -> Option<&Expression> {\n        if let Self::Computed(expr) = self {\n            Some(expr)\n        } else {\n            None\n        }\n    }\n\n    /// Returns either the literal property name or the computed const string property name.\n    #[must_use]\n    pub fn prop_name(&self) -> Option<Identifier> {\n        match self {\n            Self::Literal(ident) => Some(*ident),\n            Self::Computed(Expression::Literal(lit)) => lit\n                .as_string()\n                .map(|value| Identifier::new(value, lit.span())),\n            Self::Computed(_) => None,\n        }\n    }\n}\n\nimpl ToInternedString for PropertyName {\n    fn to_interned_string(&self, interner: &Interner) -> String {\n        match self {\n            Self::Literal(key) => interner.resolve_expect(key.sym()).to_string(),\n            Self::Computed(key) => format!(\"[{}]\", key.to_interned_string(interner)),\n        }\n    }\n}\n\nimpl From<Identifier> for PropertyName {\n    fn from(name: Identifier) -> Self {\n        Self::Literal(name)\n    }\n}\n\nimpl From<Expression> for PropertyName {\n    fn from(name: Expression) -> Self {\n        Self::Computed(name)\n    }\n}\n\nimpl VisitWith for PropertyName {\n    fn visit_with<'a, V>(&'a self, visitor: &mut V) -> ControlFlow<V::BreakTy>\n    where\n        V: Visitor<'a>,\n    {\n        match self {\n            Self::Literal(ident) => visitor.visit_sym(ident.sym_ref()),\n            Self::Computed(expr) => visitor.visit_expression(expr),\n        }\n    }\n\n    fn visit_with_mut<'a, V>(&'a mut self, visitor: &mut V) -> ControlFlow<V::BreakTy>\n    where\n        V: VisitorMut<'a>,\n    {\n        match self {\n            Self::Literal(ident) => visitor.visit_sym_mut(ident.sym_mut()),\n            Self::Computed(expr) => visitor.visit_expression_mut(expr),\n        }\n    }\n}\n\n/// The kind of a method definition.\n#[cfg_attr(feature = \"serde\", derive(serde::Serialize, serde::Deserialize))]\n#[cfg_attr(feature = \"arbitrary\", derive(arbitrary::Arbitrary))]\n#[derive(Copy, Clone, Debug, PartialEq)]\npub enum MethodDefinitionKind {\n    /// A getter method.\n    Get,\n\n    /// A setter method.\n    Set,\n\n    /// An ordinary method.\n    Ordinary,\n\n    /// A generator method.\n    Generator,\n\n    /// An async generator method.\n    AsyncGenerator,\n\n    /// An async method.\n    Async,\n}\n"
  },
  {
    "path": "core/ast/src/punctuator/mod.rs",
    "content": "//! The `Punctuator` enum, which contains all punctuators used in ECMAScript.\n//!\n//! More information:\n//!  - [ECMAScript Reference][spec]\n//!\n//! [spec]: https://tc39.es/ecma262/#prod-Punctuator\n\nuse crate::expression::operator::{\n    assign::AssignOp,\n    binary::{ArithmeticOp, BinaryOp, BitwiseOp, LogicalOp, RelationalOp},\n};\nuse std::fmt::{Display, Error, Formatter};\n\n#[cfg(test)]\nmod tests;\n\n/// All of the punctuators used in ECMAScript.\n///\n/// More information:\n///  - [ECMAScript Reference][spec]\n///\n/// [spec]: https://tc39.es/ecma262/#prod-Punctuator\n#[cfg_attr(feature = \"serde\", derive(serde::Serialize, serde::Deserialize))]\n#[derive(PartialEq, Eq, Clone, Copy, Debug, strum::EnumIter)]\npub enum Punctuator {\n    /// `+`\n    Add,\n    /// `&`\n    And,\n    /// `=>`\n    Arrow,\n    /// `=`\n    Assign,\n    /// `+=`\n    AssignAdd,\n    /// `&=`\n    AssignAnd,\n    /// `&&=`\n    AssignBoolAnd,\n    /// `||=`\n    AssignBoolOr,\n    /// `??=`,\n    AssignCoalesce,\n    /// `/=`\n    AssignDiv,\n    /// `<<=`\n    AssignLeftSh,\n    /// `%=`\n    AssignMod,\n    /// `*=`\n    AssignMul,\n    /// `|=`\n    AssignOr,\n    /// `**=`\n    AssignPow,\n    /// `>>=`\n    AssignRightSh,\n    /// `-=`\n    AssignSub,\n    /// `>>>=`\n    AssignURightSh,\n    /// `^=`\n    AssignXor,\n    /// `&&`\n    BoolAnd,\n    /// `||`\n    BoolOr,\n    /// `}`\n    CloseBlock,\n    /// `]`\n    CloseBracket,\n    /// `)`\n    CloseParen,\n    /// `??`\n    Coalesce,\n    /// `:`\n    Colon,\n    /// `,`\n    Comma,\n    /// `--`\n    Dec,\n    /// `/`\n    Div,\n    /// `.`\n    Dot,\n    /// `==`\n    Eq,\n    /// `>`\n    GreaterThan,\n    /// `>=`\n    GreaterThanOrEq,\n    /// `++`\n    Inc,\n    /// `<<`\n    LeftSh,\n    /// `<`\n    LessThan,\n    /// `<=`\n    LessThanOrEq,\n    /// `%`\n    Mod,\n    /// `*`\n    Mul,\n    /// `~`\n    Neg,\n    /// `!`\n    Not,\n    /// `!=`\n    NotEq,\n    /// `{`\n    OpenBlock,\n    /// `[`\n    OpenBracket,\n    /// `(`\n    OpenParen,\n    /// `?.`\n    Optional,\n    /// `|`\n    Or,\n    /// `**`\n    Exp,\n    /// `?`\n    Question,\n    /// `>>`\n    RightSh,\n    /// `;`\n    Semicolon,\n    /// `...`\n    Spread,\n    /// `===`\n    StrictEq,\n    /// `!==`\n    StrictNotEq,\n    /// `-`\n    Sub,\n    /// `>>>`\n    URightSh,\n    /// `^`\n    Xor,\n}\n\nimpl Punctuator {\n    /// Attempts to convert a punctuator (`+`, `=`...) to an Assign Operator\n    ///\n    /// If there is no match, `None` will be returned.\n    #[must_use]\n    pub const fn as_assign_op(self) -> Option<AssignOp> {\n        match self {\n            Self::Assign => Some(AssignOp::Assign),\n            Self::AssignAdd => Some(AssignOp::Add),\n            Self::AssignAnd => Some(AssignOp::And),\n            Self::AssignBoolAnd => Some(AssignOp::BoolAnd),\n            Self::AssignBoolOr => Some(AssignOp::BoolOr),\n            Self::AssignCoalesce => Some(AssignOp::Coalesce),\n            Self::AssignDiv => Some(AssignOp::Div),\n            Self::AssignLeftSh => Some(AssignOp::Shl),\n            Self::AssignMod => Some(AssignOp::Mod),\n            Self::AssignMul => Some(AssignOp::Mul),\n            Self::AssignOr => Some(AssignOp::Or),\n            Self::AssignPow => Some(AssignOp::Exp),\n            Self::AssignRightSh => Some(AssignOp::Shr),\n            Self::AssignSub => Some(AssignOp::Sub),\n            Self::AssignURightSh => Some(AssignOp::Ushr),\n            Self::AssignXor => Some(AssignOp::Xor),\n            _ => None,\n        }\n    }\n\n    /// Attempts to convert a punctuator (`+`, `=`...) to a Binary Operator\n    ///\n    /// If there is no match, `None` will be returned.\n    #[must_use]\n    pub const fn as_binary_op(self) -> Option<BinaryOp> {\n        match self {\n            Self::Add => Some(BinaryOp::Arithmetic(ArithmeticOp::Add)),\n            Self::Sub => Some(BinaryOp::Arithmetic(ArithmeticOp::Sub)),\n            Self::Mul => Some(BinaryOp::Arithmetic(ArithmeticOp::Mul)),\n            Self::Div => Some(BinaryOp::Arithmetic(ArithmeticOp::Div)),\n            Self::Mod => Some(BinaryOp::Arithmetic(ArithmeticOp::Mod)),\n            Self::Exp => Some(BinaryOp::Arithmetic(ArithmeticOp::Exp)),\n            Self::And => Some(BinaryOp::Bitwise(BitwiseOp::And)),\n            Self::Or => Some(BinaryOp::Bitwise(BitwiseOp::Or)),\n            Self::Xor => Some(BinaryOp::Bitwise(BitwiseOp::Xor)),\n            Self::BoolAnd => Some(BinaryOp::Logical(LogicalOp::And)),\n            Self::BoolOr => Some(BinaryOp::Logical(LogicalOp::Or)),\n            Self::Coalesce => Some(BinaryOp::Logical(LogicalOp::Coalesce)),\n            Self::Eq => Some(BinaryOp::Relational(RelationalOp::Equal)),\n            Self::NotEq => Some(BinaryOp::Relational(RelationalOp::NotEqual)),\n            Self::StrictEq => Some(BinaryOp::Relational(RelationalOp::StrictEqual)),\n            Self::StrictNotEq => Some(BinaryOp::Relational(RelationalOp::StrictNotEqual)),\n            Self::LessThan => Some(BinaryOp::Relational(RelationalOp::LessThan)),\n            Self::GreaterThan => Some(BinaryOp::Relational(RelationalOp::GreaterThan)),\n            Self::GreaterThanOrEq => Some(BinaryOp::Relational(RelationalOp::GreaterThanOrEqual)),\n            Self::LessThanOrEq => Some(BinaryOp::Relational(RelationalOp::LessThanOrEqual)),\n            Self::LeftSh => Some(BinaryOp::Bitwise(BitwiseOp::Shl)),\n            Self::RightSh => Some(BinaryOp::Bitwise(BitwiseOp::Shr)),\n            Self::URightSh => Some(BinaryOp::Bitwise(BitwiseOp::UShr)),\n            Self::Comma => Some(BinaryOp::Comma),\n            _ => None,\n        }\n    }\n\n    /// Retrieves the punctuator as a static string.\n    #[must_use]\n    pub const fn as_str(self) -> &'static str {\n        match self {\n            Self::Add => \"+\",\n            Self::And => \"&\",\n            Self::Arrow => \"=>\",\n            Self::Assign => \"=\",\n            Self::AssignAdd => \"+=\",\n            Self::AssignAnd => \"&=\",\n            Self::AssignBoolAnd => \"&&=\",\n            Self::AssignBoolOr => \"||=\",\n            Self::AssignCoalesce => \"??=\",\n            Self::AssignDiv => \"/=\",\n            Self::AssignLeftSh => \"<<=\",\n            Self::AssignMod => \"%=\",\n            Self::AssignMul => \"*=\",\n            Self::AssignOr => \"|=\",\n            Self::AssignPow => \"**=\",\n            Self::AssignRightSh => \">>=\",\n            Self::AssignSub => \"-=\",\n            Self::AssignURightSh => \">>>=\",\n            Self::AssignXor => \"^=\",\n            Self::BoolAnd => \"&&\",\n            Self::BoolOr => \"||\",\n            Self::Coalesce => \"??\",\n            Self::CloseBlock => \"}\",\n            Self::CloseBracket => \"]\",\n            Self::CloseParen => \")\",\n            Self::Colon => \":\",\n            Self::Comma => \",\",\n            Self::Dec => \"--\",\n            Self::Div => \"/\",\n            Self::Dot => \".\",\n            Self::Eq => \"==\",\n            Self::GreaterThan => \">\",\n            Self::GreaterThanOrEq => \">=\",\n            Self::Inc => \"++\",\n            Self::LeftSh => \"<<\",\n            Self::LessThan => \"<\",\n            Self::LessThanOrEq => \"<=\",\n            Self::Mod => \"%\",\n            Self::Mul => \"*\",\n            Self::Neg => \"~\",\n            Self::Not => \"!\",\n            Self::NotEq => \"!=\",\n            Self::OpenBlock => \"{\",\n            Self::OpenBracket => \"[\",\n            Self::OpenParen => \"(\",\n            Self::Optional => \"?.\",\n            Self::Or => \"|\",\n            Self::Exp => \"**\",\n            Self::Question => \"?\",\n            Self::RightSh => \">>\",\n            Self::Semicolon => \";\",\n            Self::Spread => \"...\",\n            Self::StrictEq => \"===\",\n            Self::StrictNotEq => \"!==\",\n            Self::Sub => \"-\",\n            Self::URightSh => \">>>\",\n            Self::Xor => \"^\",\n        }\n    }\n}\n\nimpl TryFrom<Punctuator> for AssignOp {\n    // TO-DO: proper error type\n    type Error = String;\n\n    fn try_from(punct: Punctuator) -> Result<Self, Self::Error> {\n        punct\n            .as_assign_op()\n            .ok_or_else(|| format!(\"No assignment operator for {punct}\"))\n    }\n}\n\nimpl TryFrom<Punctuator> for BinaryOp {\n    // TO-DO: proper error type\n    type Error = String;\n\n    fn try_from(punct: Punctuator) -> Result<Self, Self::Error> {\n        punct\n            .as_binary_op()\n            .ok_or_else(|| format!(\"No binary operator for {punct}\"))\n    }\n}\n\nimpl Display for Punctuator {\n    fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error> {\n        f.write_str(self.as_str())\n    }\n}\n\nimpl From<Punctuator> for Box<str> {\n    fn from(p: Punctuator) -> Self {\n        p.as_str().into()\n    }\n}\n"
  },
  {
    "path": "core/ast/src/punctuator/tests.rs",
    "content": "#![allow(clippy::cognitive_complexity)]\n\nuse super::*;\n\n/// Gets an iterator over all the existing punctuators.\nfn all_punctuators() -> impl Iterator<Item = Punctuator> {\n    [\n        Punctuator::Add,\n        Punctuator::And,\n        Punctuator::Arrow,\n        Punctuator::Assign,\n        Punctuator::AssignAdd,\n        Punctuator::AssignAnd,\n        Punctuator::AssignBoolAnd,\n        Punctuator::AssignBoolOr,\n        Punctuator::AssignCoalesce,\n        Punctuator::AssignDiv,\n        Punctuator::AssignLeftSh,\n        Punctuator::AssignMod,\n        Punctuator::AssignMul,\n        Punctuator::AssignOr,\n        Punctuator::AssignPow,\n        Punctuator::AssignRightSh,\n        Punctuator::AssignSub,\n        Punctuator::AssignURightSh,\n        Punctuator::AssignXor,\n        Punctuator::BoolAnd,\n        Punctuator::BoolOr,\n        Punctuator::CloseBlock,\n        Punctuator::CloseBracket,\n        Punctuator::CloseParen,\n        Punctuator::Coalesce,\n        Punctuator::Colon,\n        Punctuator::Comma,\n        Punctuator::Dec,\n        Punctuator::Div,\n        Punctuator::Dot,\n        Punctuator::Eq,\n        Punctuator::GreaterThan,\n        Punctuator::GreaterThanOrEq,\n        Punctuator::Inc,\n        Punctuator::LeftSh,\n        Punctuator::LessThan,\n        Punctuator::LessThanOrEq,\n        Punctuator::Mod,\n        Punctuator::Mul,\n        Punctuator::Neg,\n        Punctuator::Not,\n        Punctuator::NotEq,\n        Punctuator::OpenBlock,\n        Punctuator::OpenBracket,\n        Punctuator::OpenParen,\n        Punctuator::Optional,\n        Punctuator::Or,\n        Punctuator::Exp,\n        Punctuator::Question,\n        Punctuator::RightSh,\n        Punctuator::Semicolon,\n        Punctuator::Spread,\n        Punctuator::StrictEq,\n        Punctuator::StrictNotEq,\n        Punctuator::Sub,\n        Punctuator::URightSh,\n        Punctuator::Xor,\n    ]\n    .into_iter()\n}\n\n#[test]\nfn as_assign_op() {\n    for p in all_punctuators() {\n        match p.as_assign_op() {\n            Some(AssignOp::Assign) => assert_eq!(p, Punctuator::Assign),\n            Some(AssignOp::Add) => assert_eq!(p, Punctuator::AssignAdd),\n            Some(AssignOp::And) => assert_eq!(p, Punctuator::AssignAnd),\n            Some(AssignOp::BoolAnd) => assert_eq!(p, Punctuator::AssignBoolAnd),\n            Some(AssignOp::BoolOr) => assert_eq!(p, Punctuator::AssignBoolOr),\n            Some(AssignOp::Coalesce) => assert_eq!(p, Punctuator::AssignCoalesce),\n            Some(AssignOp::Div) => assert_eq!(p, Punctuator::AssignDiv),\n            Some(AssignOp::Shl) => assert_eq!(p, Punctuator::AssignLeftSh),\n            Some(AssignOp::Mod) => assert_eq!(p, Punctuator::AssignMod),\n            Some(AssignOp::Mul) => assert_eq!(p, Punctuator::AssignMul),\n            Some(AssignOp::Or) => assert_eq!(p, Punctuator::AssignOr),\n            Some(AssignOp::Exp) => assert_eq!(p, Punctuator::AssignPow),\n            Some(AssignOp::Shr) => assert_eq!(p, Punctuator::AssignRightSh),\n            Some(AssignOp::Sub) => assert_eq!(p, Punctuator::AssignSub),\n            Some(AssignOp::Ushr) => assert_eq!(p, Punctuator::AssignURightSh),\n            Some(AssignOp::Xor) => assert_eq!(p, Punctuator::AssignXor),\n            None => assert!(\n                ![\n                    Punctuator::Assign,\n                    Punctuator::AssignAdd,\n                    Punctuator::AssignAnd,\n                    Punctuator::AssignBoolAnd,\n                    Punctuator::AssignBoolOr,\n                    Punctuator::AssignCoalesce,\n                    Punctuator::AssignDiv,\n                    Punctuator::AssignLeftSh,\n                    Punctuator::AssignMod,\n                    Punctuator::AssignMul,\n                    Punctuator::AssignOr,\n                    Punctuator::AssignPow,\n                    Punctuator::AssignRightSh,\n                    Punctuator::AssignSub,\n                    Punctuator::AssignURightSh,\n                    Punctuator::AssignXor,\n                ]\n                .contains(&p)\n            ),\n        }\n    }\n}\n\n#[test]\nfn as_binary_op() {\n    for p in all_punctuators() {\n        match p.as_binary_op() {\n            Some(BinaryOp::Arithmetic(ArithmeticOp::Add)) => assert_eq!(p, Punctuator::Add),\n            Some(BinaryOp::Arithmetic(ArithmeticOp::Sub)) => assert_eq!(p, Punctuator::Sub),\n            Some(BinaryOp::Arithmetic(ArithmeticOp::Mul)) => assert_eq!(p, Punctuator::Mul),\n            Some(BinaryOp::Arithmetic(ArithmeticOp::Div)) => assert_eq!(p, Punctuator::Div),\n            Some(BinaryOp::Arithmetic(ArithmeticOp::Mod)) => assert_eq!(p, Punctuator::Mod),\n            Some(BinaryOp::Bitwise(BitwiseOp::And)) => assert_eq!(p, Punctuator::And),\n            Some(BinaryOp::Bitwise(BitwiseOp::Or)) => assert_eq!(p, Punctuator::Or),\n            Some(BinaryOp::Bitwise(BitwiseOp::Xor)) => assert_eq!(p, Punctuator::Xor),\n            Some(BinaryOp::Logical(LogicalOp::And)) => assert_eq!(p, Punctuator::BoolAnd),\n            Some(BinaryOp::Logical(LogicalOp::Or)) => assert_eq!(p, Punctuator::BoolOr),\n            Some(BinaryOp::Logical(LogicalOp::Coalesce)) => assert_eq!(p, Punctuator::Coalesce),\n            Some(BinaryOp::Relational(RelationalOp::Equal)) => assert_eq!(p, Punctuator::Eq),\n            Some(BinaryOp::Relational(RelationalOp::NotEqual)) => assert_eq!(p, Punctuator::NotEq),\n            Some(BinaryOp::Relational(RelationalOp::StrictEqual)) => {\n                assert_eq!(p, Punctuator::StrictEq);\n            }\n            Some(BinaryOp::Relational(RelationalOp::StrictNotEqual)) => {\n                assert_eq!(p, Punctuator::StrictNotEq);\n            }\n            Some(BinaryOp::Relational(RelationalOp::LessThan)) => {\n                assert_eq!(p, Punctuator::LessThan);\n            }\n            Some(BinaryOp::Relational(RelationalOp::GreaterThan)) => {\n                assert_eq!(p, Punctuator::GreaterThan);\n            }\n            Some(BinaryOp::Relational(RelationalOp::GreaterThanOrEqual)) => {\n                assert_eq!(p, Punctuator::GreaterThanOrEq);\n            }\n            Some(BinaryOp::Relational(RelationalOp::LessThanOrEqual)) => {\n                assert_eq!(p, Punctuator::LessThanOrEq);\n            }\n            Some(BinaryOp::Bitwise(BitwiseOp::Shl)) => assert_eq!(p, Punctuator::LeftSh),\n            Some(BinaryOp::Bitwise(BitwiseOp::Shr)) => assert_eq!(p, Punctuator::RightSh),\n            Some(BinaryOp::Bitwise(BitwiseOp::UShr)) => assert_eq!(p, Punctuator::URightSh),\n            Some(BinaryOp::Comma) => assert_eq!(p, Punctuator::Comma),\n            Some(BinaryOp::Arithmetic(ArithmeticOp::Exp)) => {\n                assert_eq!(p, Punctuator::Exp);\n            }\n            None => assert!(\n                ![\n                    Punctuator::Add,\n                    Punctuator::Sub,\n                    Punctuator::Mul,\n                    Punctuator::Div,\n                    Punctuator::Mod,\n                    Punctuator::And,\n                    Punctuator::Or,\n                    Punctuator::Xor,\n                    Punctuator::BoolAnd,\n                    Punctuator::BoolOr,\n                    Punctuator::Coalesce,\n                    Punctuator::Eq,\n                    Punctuator::NotEq,\n                    Punctuator::StrictEq,\n                    Punctuator::StrictNotEq,\n                    Punctuator::LessThan,\n                    Punctuator::GreaterThan,\n                    Punctuator::GreaterThanOrEq,\n                    Punctuator::LessThanOrEq,\n                    Punctuator::LeftSh,\n                    Punctuator::RightSh,\n                    Punctuator::URightSh,\n                    Punctuator::Comma\n                ]\n                .contains(&p)\n            ),\n            Some(BinaryOp::Relational(RelationalOp::In | RelationalOp::InstanceOf)) => {\n                unreachable!()\n            }\n        }\n    }\n}\n\n#[test]\nfn as_str() {\n    for p in all_punctuators() {\n        match p.as_str() {\n            \"+\" => assert_eq!(p, Punctuator::Add),\n            \"&\" => assert_eq!(p, Punctuator::And),\n            \"=>\" => assert_eq!(p, Punctuator::Arrow),\n            \"=\" => assert_eq!(p, Punctuator::Assign),\n            \"+=\" => assert_eq!(p, Punctuator::AssignAdd),\n            \"&=\" => assert_eq!(p, Punctuator::AssignAnd),\n            \"&&=\" => assert_eq!(p, Punctuator::AssignBoolAnd),\n            \"||=\" => assert_eq!(p, Punctuator::AssignBoolOr),\n            \"??=\" => assert_eq!(p, Punctuator::AssignCoalesce),\n            \"/=\" => assert_eq!(p, Punctuator::AssignDiv),\n            \"<<=\" => assert_eq!(p, Punctuator::AssignLeftSh),\n            \"%=\" => assert_eq!(p, Punctuator::AssignMod),\n            \"*=\" => assert_eq!(p, Punctuator::AssignMul),\n            \"|=\" => assert_eq!(p, Punctuator::AssignOr),\n            \"**=\" => assert_eq!(p, Punctuator::AssignPow),\n            \">>=\" => assert_eq!(p, Punctuator::AssignRightSh),\n            \"-=\" => assert_eq!(p, Punctuator::AssignSub),\n            \">>>=\" => assert_eq!(p, Punctuator::AssignURightSh),\n            \"^=\" => assert_eq!(p, Punctuator::AssignXor),\n            \"&&\" => assert_eq!(p, Punctuator::BoolAnd),\n            \"||\" => assert_eq!(p, Punctuator::BoolOr),\n            \"??\" => assert_eq!(p, Punctuator::Coalesce),\n            \"}\" => assert_eq!(p, Punctuator::CloseBlock),\n            \"]\" => assert_eq!(p, Punctuator::CloseBracket),\n            \")\" => assert_eq!(p, Punctuator::CloseParen),\n            \":\" => assert_eq!(p, Punctuator::Colon),\n            \",\" => assert_eq!(p, Punctuator::Comma),\n            \"--\" => assert_eq!(p, Punctuator::Dec),\n            \"/\" => assert_eq!(p, Punctuator::Div),\n            \".\" => assert_eq!(p, Punctuator::Dot),\n            \"==\" => assert_eq!(p, Punctuator::Eq),\n            \">\" => assert_eq!(p, Punctuator::GreaterThan),\n            \">=\" => assert_eq!(p, Punctuator::GreaterThanOrEq),\n            \"++\" => assert_eq!(p, Punctuator::Inc),\n            \"<<\" => assert_eq!(p, Punctuator::LeftSh),\n            \"<\" => assert_eq!(p, Punctuator::LessThan),\n            \"<=\" => assert_eq!(p, Punctuator::LessThanOrEq),\n            \"%\" => assert_eq!(p, Punctuator::Mod),\n            \"*\" => assert_eq!(p, Punctuator::Mul),\n            \"~\" => assert_eq!(p, Punctuator::Neg),\n            \"!\" => assert_eq!(p, Punctuator::Not),\n            \"!=\" => assert_eq!(p, Punctuator::NotEq),\n            \"{\" => assert_eq!(p, Punctuator::OpenBlock),\n            \"[\" => assert_eq!(p, Punctuator::OpenBracket),\n            \"(\" => assert_eq!(p, Punctuator::OpenParen),\n            \"?.\" => assert_eq!(p, Punctuator::Optional),\n            \"|\" => assert_eq!(p, Punctuator::Or),\n            \"**\" => assert_eq!(p, Punctuator::Exp),\n            \"?\" => assert_eq!(p, Punctuator::Question),\n            \">>\" => assert_eq!(p, Punctuator::RightSh),\n            \";\" => assert_eq!(p, Punctuator::Semicolon),\n            \"...\" => assert_eq!(p, Punctuator::Spread),\n            \"===\" => assert_eq!(p, Punctuator::StrictEq),\n            \"!==\" => assert_eq!(p, Punctuator::StrictNotEq),\n            \"-\" => assert_eq!(p, Punctuator::Sub),\n            \">>>\" => assert_eq!(p, Punctuator::URightSh),\n            \"^\" => assert_eq!(p, Punctuator::Xor),\n            _ => unreachable!(\"unknown punctuator {p:?} found\"),\n        }\n    }\n}\n\n#[test]\nfn try_into_assign_op() {\n    for p in all_punctuators() {\n        if p.as_assign_op().is_some() {\n            assert!(TryInto::<AssignOp>::try_into(p).is_ok());\n        } else {\n            assert!(TryInto::<AssignOp>::try_into(p).is_err());\n        }\n    }\n}\n\n#[test]\nfn try_into_binary_op() {\n    for p in all_punctuators() {\n        if p.as_binary_op().is_some() {\n            assert!(TryInto::<BinaryOp>::try_into(p).is_ok());\n        } else {\n            assert!(TryInto::<BinaryOp>::try_into(p).is_err());\n        }\n    }\n}\n\n#[test]\nfn display() {\n    for p in all_punctuators() {\n        assert_eq!(p.as_str(), p.to_string());\n    }\n}\n\n#[test]\nfn into_box() {\n    for p in all_punctuators() {\n        assert_eq!(p.as_str(), Box::<str>::from(p).as_ref());\n    }\n}\n"
  },
  {
    "path": "core/ast/src/scope.rs",
    "content": "//! This module implements the binding scope for various AST nodes.\n//!\n//! Scopes are used to track the bindings of identifiers in the AST.\n\nuse bitflags::bitflags;\nuse boa_string::JsString;\nuse std::{\n    cell::{Cell, RefCell},\n    fmt::Debug,\n    rc::Rc,\n};\n\nbitflags! {\n    #[derive(Debug, Clone, Copy, PartialEq, Eq)]\n    struct BindingFlags: u8 {\n        const MUTABLE  = 1 << 0;\n        const LEX      = 1 << 1;\n        const STRICT   = 1 << 2;\n        const ESCAPES  = 1 << 3;\n        const ACCESSED = 1 << 4;\n    }\n}\n\nimpl BindingFlags {\n    fn is_mutable(self) -> bool {\n        self.contains(BindingFlags::MUTABLE)\n    }\n    fn is_lex(self) -> bool {\n        self.contains(BindingFlags::LEX)\n    }\n    fn is_strict(self) -> bool {\n        self.contains(BindingFlags::STRICT)\n    }\n    fn escapes(self) -> bool {\n        self.contains(BindingFlags::ESCAPES)\n    }\n    fn is_accessed(self) -> bool {\n        self.contains(BindingFlags::ACCESSED)\n    }\n}\n\n#[derive(Clone, Debug, PartialEq)]\nstruct Binding {\n    name: JsString,\n    index: u32,\n    flags: BindingFlags,\n}\n\nimpl Binding {\n    fn is_mutable(&self) -> bool {\n        self.flags.is_mutable()\n    }\n    fn is_lex(&self) -> bool {\n        self.flags.is_lex()\n    }\n    fn is_strict(&self) -> bool {\n        self.flags.is_strict()\n    }\n    fn escapes(&self) -> bool {\n        self.flags.escapes()\n    }\n    fn is_accessed(&self) -> bool {\n        self.flags.is_accessed()\n    }\n}\n\n/// A scope maps bound identifiers to their binding positions.\n///\n/// It can be either a global scope or a function scope or a declarative scope.\n#[derive(Clone, PartialEq)]\npub struct Scope {\n    inner: Rc<Inner>,\n}\n\nimpl Debug for Scope {\n    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {\n        f.debug_struct(\"Scope\")\n            .field(\"outer\", &self.inner.outer)\n            .field(\"index\", &self.inner.index)\n            .field(\"bindings\", &self.inner.bindings)\n            .field(\"function\", &self.inner.function)\n            .finish()\n    }\n}\n\nimpl Default for Scope {\n    fn default() -> Self {\n        Self::new_global()\n    }\n}\n\n#[cfg(feature = \"arbitrary\")]\nimpl<'a> arbitrary::Arbitrary<'a> for Scope {\n    fn arbitrary(_u: &mut arbitrary::Unstructured<'a>) -> arbitrary::Result<Self> {\n        Ok(Self::new_global())\n    }\n}\n\n#[derive(Debug, PartialEq)]\npub(crate) struct Inner {\n    unique_id: u32,\n    outer: Option<Scope>,\n    index: Cell<u32>,\n    bindings: RefCell<Vec<Binding>>,\n    function: bool,\n    // Has the `this` been accessed/escaped outside the function environment boundary.\n    this_escaped: Cell<bool>,\n\n    context: Rc<ScopeContext>,\n}\n\nimpl Scope {\n    /// Creates a new global scope.\n    #[must_use]\n    pub fn new_global() -> Self {\n        Self {\n            inner: Rc::new(Inner {\n                unique_id: 0,\n                outer: None,\n                index: Cell::default(),\n                bindings: RefCell::default(),\n                function: true,\n                this_escaped: Cell::new(false),\n                context: Rc::default(),\n            }),\n        }\n    }\n\n    /// Creates a new scope.\n    #[must_use]\n    pub fn new(parent: Self, function: bool) -> Self {\n        Self {\n            inner: Rc::new(Inner {\n                unique_id: parent.inner.context.next_unique_id(),\n                index: Cell::new(parent.inner.index.get() + 1),\n                bindings: RefCell::default(),\n                function,\n                this_escaped: Cell::new(false),\n                context: parent.inner.context.clone(),\n                outer: Some(parent),\n            }),\n        }\n    }\n\n    /// Checks if the scope has only local bindings.\n    #[must_use]\n    pub fn all_bindings_local(&self) -> bool {\n        // if self.inner.function && self.inn\n        self.inner\n            .bindings\n            .borrow()\n            .iter()\n            .all(|binding| !binding.escapes())\n    }\n\n    /// Marks all bindings in this scope as escaping.\n    pub fn escape_all_bindings(&self) {\n        for binding in self.inner.bindings.borrow_mut().iter_mut() {\n            binding.flags.insert(BindingFlags::ESCAPES);\n        }\n    }\n\n    /// Has this binding escaped.\n    #[must_use]\n    pub fn escaped_this(&self) -> bool {\n        self.inner.this_escaped.get()\n    }\n\n    /// Check if the scope has a lexical binding with the given name.\n    #[must_use]\n    pub fn has_lex_binding(&self, name: &JsString) -> bool {\n        self.inner\n            .bindings\n            .borrow()\n            .iter()\n            .find(|b| &b.name == name)\n            .is_some_and(Binding::is_lex)\n    }\n\n    /// Check if the scope has a binding with the given name.\n    #[must_use]\n    pub fn has_binding(&self, name: &JsString) -> bool {\n        self.inner.bindings.borrow().iter().any(|b| &b.name == name)\n    }\n\n    /// Get the binding locator for a binding with the given name.\n    /// Fall back to the global scope if the binding is not found.\n    #[must_use]\n    pub fn get_identifier_reference(&self, name: JsString) -> IdentifierReference {\n        if let Some(binding) = self.inner.bindings.borrow().iter().find(|b| b.name == name) {\n            IdentifierReference::new(\n                BindingLocator::declarative(\n                    name,\n                    self.inner.index.get(),\n                    binding.index,\n                    self.inner.unique_id,\n                ),\n                binding.is_lex(),\n                binding.escapes(),\n            )\n        } else if let Some(outer) = &self.inner.outer {\n            outer.get_identifier_reference(name)\n        } else {\n            IdentifierReference::new(BindingLocator::global(name), false, true)\n        }\n    }\n\n    /// Returns the number of bindings in this scope.\n    #[must_use]\n    #[allow(clippy::cast_possible_truncation)]\n    pub fn num_bindings(&self) -> u32 {\n        self.inner.bindings.borrow().len() as u32\n    }\n\n    /// Returns the number of bindings in this scope that are not local.\n    #[must_use]\n    #[allow(clippy::cast_possible_truncation)]\n    pub fn num_bindings_non_local(&self) -> u32 {\n        self.inner\n            .bindings\n            .borrow()\n            .iter()\n            .filter(|binding| binding.escapes())\n            .count() as u32\n    }\n\n    /// Adjust the binding indices to exclude local bindings.\n    pub(crate) fn reorder_binding_indices(&self) {\n        let mut bindings = self.inner.bindings.borrow_mut();\n        let mut index = 0;\n        for binding in bindings.iter_mut() {\n            if !binding.escapes() {\n                binding.index = 0;\n                continue;\n            }\n            binding.index = index;\n            index += 1;\n        }\n    }\n\n    /// Returns the index of this scope.\n    #[must_use]\n    pub fn scope_index(&self) -> u32 {\n        self.inner.index.get()\n    }\n\n    /// Set the index of this scope.\n    pub(crate) fn set_index(&self, index: u32) {\n        self.inner.index.set(index);\n    }\n\n    /// Check if the scope is a function scope.\n    #[must_use]\n    pub fn is_function(&self) -> bool {\n        self.inner.function\n    }\n\n    /// Check if the scope is a global scope.\n    #[must_use]\n    pub fn is_global(&self) -> bool {\n        self.inner.outer.is_none()\n    }\n\n    /// Check if a binding with the given name is mutable.\n    ///\n    /// Returns `Some(true)` for mutable bindings (`let`, `var`),\n    /// `Some(false)` for immutable bindings (`const`),\n    /// or `None` if the binding is not found in this or any outer scope.\n    #[must_use]\n    pub fn is_binding_mutable(&self, name: &JsString) -> Option<bool> {\n        if let Some(binding) = self\n            .inner\n            .bindings\n            .borrow()\n            .iter()\n            .find(|b| &b.name == name)\n        {\n            Some(binding.is_mutable())\n        } else if let Some(outer) = &self.inner.outer {\n            outer.is_binding_mutable(name)\n        } else {\n            None\n        }\n    }\n\n    /// Get the locator for a binding name.\n    #[must_use]\n    pub fn get_binding(&self, name: &JsString) -> Option<BindingLocator> {\n        self.inner\n            .bindings\n            .borrow()\n            .iter()\n            .find(|b| &b.name == name)\n            .map(|binding| {\n                BindingLocator::declarative(\n                    name.clone(),\n                    self.inner.index.get(),\n                    binding.index,\n                    self.inner.unique_id,\n                )\n            })\n    }\n\n    /// Get the locator for a binding name.\n    #[must_use]\n    pub fn get_binding_reference(&self, name: &JsString) -> Option<IdentifierReference> {\n        self.inner\n            .bindings\n            .borrow()\n            .iter()\n            .find(|b| &b.name == name)\n            .map(|binding| {\n                IdentifierReference::new(\n                    BindingLocator::declarative(\n                        name.clone(),\n                        self.inner.index.get(),\n                        binding.index,\n                        self.inner.unique_id,\n                    ),\n                    binding.is_lex(),\n                    binding.escapes(),\n                )\n            })\n    }\n\n    /// Simulate a binding access.\n    ///\n    /// - If the binding access crosses a function border, the binding is marked as escaping.\n    /// - If the binding access is in an eval or with scope, the binding is marked as escaping.\n    pub fn access_binding(&self, name: &JsString, eval_or_with: bool) {\n        let mut crossed_function_border = false;\n        let mut current = self;\n        loop {\n            if let Some(binding) = current\n                .inner\n                .bindings\n                .borrow_mut()\n                .iter_mut()\n                .find(|b| &b.name == name)\n            {\n                binding.flags.insert(BindingFlags::ACCESSED);\n                if crossed_function_border || eval_or_with {\n                    binding.flags.insert(BindingFlags::ESCAPES);\n                }\n                return;\n            }\n            if let Some(outer) = &current.inner.outer {\n                if current.inner.function {\n                    crossed_function_border = true;\n                }\n                current = outer;\n            } else {\n                return;\n            }\n        }\n    }\n\n    /// Escape enclosing function environment's `this`.\n    pub fn escape_this_in_enclosing_function_scope(&self) {\n        let mut current = self;\n        let mut crossed_function_border = false;\n\n        loop {\n            if crossed_function_border && current.is_function() {\n                current.inner.this_escaped.set(true);\n                return;\n            }\n            if let Some(outer) = &current.inner.outer {\n                if current.is_function() {\n                    crossed_function_border = true;\n                }\n                current = outer;\n            } else {\n                return;\n            }\n        }\n    }\n\n    /// Creates a mutable binding.\n    #[must_use]\n    #[allow(clippy::cast_possible_truncation)]\n    pub fn create_mutable_binding(&self, name: JsString, function_scope: bool) -> BindingLocator {\n        let mut bindings = self.inner.bindings.borrow_mut();\n        let binding_index = bindings.len() as u32;\n        if let Some(binding) = bindings.iter().find(|b| b.name == name) {\n            return BindingLocator::declarative(\n                name,\n                self.inner.index.get(),\n                binding.index,\n                self.inner.unique_id,\n            );\n        }\n        let mut flags = BindingFlags::MUTABLE;\n        flags.set(BindingFlags::LEX, !function_scope);\n        flags.set(BindingFlags::ESCAPES, self.is_global());\n        bindings.push(Binding {\n            name: name.clone(),\n            index: binding_index,\n            flags,\n        });\n        BindingLocator::declarative(\n            name,\n            self.inner.index.get(),\n            binding_index,\n            self.inner.unique_id,\n        )\n    }\n\n    /// Crate an immutable binding.\n    #[allow(clippy::cast_possible_truncation)]\n    pub(crate) fn create_immutable_binding(&self, name: JsString, strict: bool) {\n        let mut bindings = self.inner.bindings.borrow_mut();\n        if bindings.iter().any(|b| b.name == name) {\n            return;\n        }\n        let binding_index = bindings.len() as u32;\n        let mut flags = BindingFlags::LEX;\n        flags.set(BindingFlags::STRICT, strict);\n        flags.set(BindingFlags::ESCAPES, self.is_global());\n        bindings.push(Binding {\n            name,\n            index: binding_index,\n            flags,\n        });\n    }\n\n    /// Return the binding locator for a mutable binding.\n    ///\n    /// # Errors\n    /// Returns an error if the binding is not mutable or does not exist.\n    pub fn set_mutable_binding(\n        &self,\n        name: JsString,\n    ) -> Result<IdentifierReference, BindingLocatorError> {\n        Ok(\n            match self.inner.bindings.borrow().iter().find(|b| b.name == name) {\n                Some(binding) if binding.is_mutable() => IdentifierReference::new(\n                    BindingLocator::declarative(\n                        name,\n                        self.inner.index.get(),\n                        binding.index,\n                        self.inner.unique_id,\n                    ),\n                    binding.is_lex(),\n                    binding.escapes(),\n                ),\n                Some(binding) if binding.is_strict() => {\n                    return Err(BindingLocatorError::MutateImmutable);\n                }\n                Some(_) => return Err(BindingLocatorError::Silent),\n                None => self.inner.outer.as_ref().map_or_else(\n                    || {\n                        Ok(IdentifierReference::new(\n                            BindingLocator::global(name.clone()),\n                            false,\n                            true,\n                        ))\n                    },\n                    |outer| outer.set_mutable_binding(name.clone()),\n                )?,\n            },\n        )\n    }\n\n    #[cfg(feature = \"annex-b\")]\n    /// Return the binding locator for a set operation on an existing var binding.\n    ///\n    /// # Errors\n    /// Returns an error if the binding is not mutable or does not exist.\n    pub fn set_mutable_binding_var(\n        &self,\n        name: JsString,\n    ) -> Result<IdentifierReference, BindingLocatorError> {\n        if !self.is_function() {\n            return self.inner.outer.as_ref().map_or_else(\n                || {\n                    Ok(IdentifierReference::new(\n                        BindingLocator::global(name.clone()),\n                        false,\n                        true,\n                    ))\n                },\n                |outer| outer.set_mutable_binding_var(name.clone()),\n            );\n        }\n\n        Ok(\n            match self.inner.bindings.borrow().iter().find(|b| b.name == name) {\n                Some(binding) if binding.is_mutable() => IdentifierReference::new(\n                    BindingLocator::declarative(\n                        name,\n                        self.inner.index.get(),\n                        binding.index,\n                        self.inner.unique_id,\n                    ),\n                    binding.is_lex(),\n                    binding.escapes(),\n                ),\n                Some(binding) if binding.is_strict() => {\n                    return Err(BindingLocatorError::MutateImmutable);\n                }\n                Some(_) => return Err(BindingLocatorError::Silent),\n                None => self.inner.outer.as_ref().map_or_else(\n                    || {\n                        Ok(IdentifierReference::new(\n                            BindingLocator::global(name.clone()),\n                            false,\n                            true,\n                        ))\n                    },\n                    |outer| outer.set_mutable_binding_var(name.clone()),\n                )?,\n            },\n        )\n    }\n\n    /// Gets the outer scope of this scope.\n    #[must_use]\n    pub fn outer(&self) -> Option<&Self> {\n        self.inner.outer.as_ref()\n    }\n\n    /// Returns the unique ID of this scope.\n    #[must_use]\n    pub fn unique_id(&self) -> u32 {\n        self.inner.unique_id\n    }\n}\n\n/// Additional state that all Scopes of a single AST share for bookkeeping.\n#[derive(Debug, PartialEq, Default)]\nstruct ScopeContext {\n    /// A counter for unique IDs for Scopes generated as part of this scope tree.\n    ///\n    /// The value is the highest unique ID assigned so far (initialized with 0\n    /// which is also the unique ID of the global scope).\n    ///\n    /// Only used in the root scope.\n    unique_id_ctr: Cell<u32>,\n}\n\nimpl ScopeContext {\n    /// Returns the next unique ID for a scope in this scope tree.\n    fn next_unique_id(&self) -> u32 {\n        let id = self.unique_id_ctr.get() + 1;\n        self.unique_id_ctr.set(id);\n        id\n    }\n}\n\n/// A reference to an identifier in a scope.\n#[derive(Clone, Debug, PartialEq, Eq, Hash)]\npub struct IdentifierReference {\n    locator: BindingLocator,\n    lexical: bool,\n    escapes: bool,\n}\n\nimpl IdentifierReference {\n    /// Create a new identifier reference.\n    pub(crate) fn new(locator: BindingLocator, lexical: bool, escapes: bool) -> Self {\n        Self {\n            locator,\n            lexical,\n            escapes,\n        }\n    }\n\n    /// Get the binding locator for this identifier reference.\n    #[must_use]\n    pub fn locator(&self) -> BindingLocator {\n        self.locator.clone()\n    }\n\n    /// Returns if the binding can be function local.\n    #[must_use]\n    pub fn local(&self) -> bool {\n        self.locator.scope > 0 && !self.escapes\n    }\n\n    /// Returns if the binding is on the global object.\n    #[must_use]\n    pub fn is_global_object(&self) -> bool {\n        self.locator.scope == 0\n    }\n\n    /// Check if this identifier reference is lexical.\n    #[must_use]\n    pub fn is_lexical(&self) -> bool {\n        self.lexical\n    }\n}\n\n/// A binding locator contains all information about a binding that is needed to resolve it at runtime.\n#[derive(Clone, Debug, Eq, Hash, PartialEq)]\npub struct BindingLocator {\n    /// Name of the binding.\n    name: JsString,\n\n    /// Scope of the binding.\n    /// - 0: Global object\n    /// - 1: Global declarative scope\n    /// - n: Stack scope at index n - 2\n    scope: u32,\n\n    /// Index of the binding in the scope.\n    binding_index: u32,\n\n    unique_scope_id: u32,\n}\n\nimpl BindingLocator {\n    /// Creates a new declarative binding locator that has knows indices.\n    pub(crate) const fn declarative(\n        name: JsString,\n        scope_index: u32,\n        binding_index: u32,\n        unique_scope_id: u32,\n    ) -> Self {\n        Self {\n            name,\n            scope: scope_index + 1,\n            binding_index,\n            unique_scope_id,\n        }\n    }\n\n    /// Creates a binding locator that indicates that the binding is on the global object.\n    pub(super) const fn global(name: JsString) -> Self {\n        Self {\n            name,\n            scope: 0,\n            binding_index: 0,\n            unique_scope_id: 0,\n        }\n    }\n\n    /// Returns the name of the binding.\n    #[must_use]\n    pub const fn name(&self) -> &JsString {\n        &self.name\n    }\n\n    /// Returns if the binding is located on the global object.\n    #[must_use]\n    pub const fn is_global(&self) -> bool {\n        self.scope == 0\n    }\n\n    /// Returns the scope of the binding.\n    #[must_use]\n    pub fn scope(&self) -> BindingLocatorScope {\n        match self.scope {\n            0 => BindingLocatorScope::GlobalObject,\n            1 => BindingLocatorScope::GlobalDeclarative,\n            n => BindingLocatorScope::Stack(n - 2),\n        }\n    }\n\n    /// Sets the scope of the binding.\n    pub fn set_scope(&mut self, scope: BindingLocatorScope) {\n        self.scope = match scope {\n            BindingLocatorScope::GlobalObject => 0,\n            BindingLocatorScope::GlobalDeclarative => 1,\n            BindingLocatorScope::Stack(index) => index + 2,\n        };\n    }\n\n    /// Returns the binding index of the binding.\n    #[must_use]\n    pub const fn binding_index(&self) -> u32 {\n        self.binding_index\n    }\n\n    /// Sets the binding index of the binding.\n    pub fn set_binding_index(&mut self, index: u32) {\n        self.binding_index = index;\n    }\n\n    /// Returns the unique scope ID of the binding.\n    #[must_use]\n    pub fn unique_scope_id(&self) -> u32 {\n        self.unique_scope_id\n    }\n}\n\n/// Action that is returned when a fallible binding operation.\n#[derive(Copy, Clone, Debug)]\npub enum BindingLocatorError {\n    /// Trying to mutate immutable binding,\n    MutateImmutable,\n\n    /// Indicates that any action is silently ignored.\n    Silent,\n}\n\n/// The scope in which a binding is located.\n#[derive(Clone, Copy, Debug, PartialEq, Eq)]\npub enum BindingLocatorScope {\n    /// The binding is located on the global object.\n    GlobalObject,\n\n    /// The binding is located in the global declarative scope.\n    GlobalDeclarative,\n\n    /// The binding is located in the scope stack at the given index.\n    Stack(u32),\n}\n\n/// A collection of function scopes.\n#[derive(Clone, Debug, Default, PartialEq)]\npub struct FunctionScopes {\n    pub(crate) function_scope: Scope,\n    pub(crate) parameters_eval_scope: Option<Scope>,\n    pub(crate) parameters_scope: Option<Scope>,\n    pub(crate) lexical_scope: Option<Scope>,\n    pub(crate) mapped_arguments_object: bool,\n    pub(crate) requires_function_scope: bool,\n}\n\nimpl FunctionScopes {\n    /// Returns the function scope for this function.\n    #[must_use]\n    pub fn function_scope(&self) -> &Scope {\n        &self.function_scope\n    }\n\n    /// Returns if the arguments object is accessed in this function.\n    #[must_use]\n    pub fn arguments_object_accessed(&self) -> bool {\n        if self\n            .function_scope\n            .inner\n            .bindings\n            .borrow()\n            .first()\n            .filter(|b| b.name == \"arguments\" && b.is_accessed())\n            .is_some()\n        {\n            return true;\n        }\n\n        if let Some(scope) = &self.parameters_eval_scope\n            && scope\n                .inner\n                .bindings\n                .borrow()\n                .first()\n                .filter(|b| b.name == \"arguments\" && b.is_accessed())\n                .is_some()\n        {\n            return true;\n        }\n\n        false\n    }\n\n    /// Check if the creation of the function scope is required.\n    #[must_use]\n    pub fn requires_function_scope(&self) -> bool {\n        self.requires_function_scope\n    }\n\n    /// Returns the parameters eval scope for this function.\n    #[must_use]\n    pub fn parameters_eval_scope(&self) -> Option<&Scope> {\n        self.parameters_eval_scope.as_ref()\n    }\n\n    /// Returns the parameters scope for this function.\n    #[must_use]\n    pub fn parameters_scope(&self) -> Option<&Scope> {\n        self.parameters_scope.as_ref()\n    }\n\n    /// Returns the lexical scope for this function.\n    #[must_use]\n    pub fn lexical_scope(&self) -> Option<&Scope> {\n        self.lexical_scope.as_ref()\n    }\n\n    /// Returns the effective parameter scope for this function.\n    #[must_use]\n    pub fn parameter_scope(&self) -> Scope {\n        if let Some(parameters_eval_scope) = &self.parameters_eval_scope {\n            return parameters_eval_scope.clone();\n        }\n        self.function_scope.clone()\n    }\n\n    /// Returns the effective body scope for this function.\n    pub(crate) fn body_scope(&self) -> Scope {\n        if let Some(lexical_scope) = &self.lexical_scope {\n            return lexical_scope.clone();\n        }\n        if let Some(parameters_scope) = &self.parameters_scope {\n            return parameters_scope.clone();\n        }\n        if let Some(parameters_eval_scope) = &self.parameters_eval_scope {\n            return parameters_eval_scope.clone();\n        }\n        self.function_scope.clone()\n    }\n\n    /// Marks all bindings in all scopes as escaping.\n    pub(crate) fn escape_all_bindings(&self) {\n        self.function_scope.escape_all_bindings();\n        if let Some(parameters_eval_scope) = &self.parameters_eval_scope {\n            parameters_eval_scope.escape_all_bindings();\n        }\n        if let Some(parameters_scope) = &self.parameters_scope {\n            parameters_scope.escape_all_bindings();\n        }\n        if let Some(lexical_scope) = &self.lexical_scope {\n            lexical_scope.escape_all_bindings();\n        }\n    }\n\n    pub(crate) fn reorder_binding_indices(&self) {\n        self.function_scope.reorder_binding_indices();\n        if let Some(parameters_eval_scope) = &self.parameters_eval_scope {\n            parameters_eval_scope.reorder_binding_indices();\n        }\n        if let Some(parameters_scope) = &self.parameters_scope {\n            parameters_scope.reorder_binding_indices();\n        }\n        if let Some(lexical_scope) = &self.lexical_scope {\n            lexical_scope.reorder_binding_indices();\n        }\n    }\n}\n\n#[cfg(feature = \"arbitrary\")]\nimpl<'a> arbitrary::Arbitrary<'a> for FunctionScopes {\n    fn arbitrary(_u: &mut arbitrary::Unstructured<'a>) -> arbitrary::Result<Self> {\n        Ok(Self {\n            function_scope: Scope::new_global(),\n            parameters_eval_scope: None,\n            parameters_scope: None,\n            lexical_scope: None,\n            mapped_arguments_object: false,\n            requires_function_scope: false,\n        })\n    }\n}\n"
  },
  {
    "path": "core/ast/src/scope_analyzer.rs",
    "content": "//! This module implements the scope analysis for the AST.\n//!\n//! The scope analysis is done in two steps:\n//! 1. Collecting bindings: This step collects all the bindings in the AST and fills the scopes with them.\n//! 2. Analyzing binding escapes: This step analyzes if the bindings escape their function scopes.\n\n#[cfg(feature = \"annex-b\")]\nuse crate::operations::annex_b_function_declarations_names;\nuse crate::{\n    Declaration, Module, Script, StatementListItem, ToJsString,\n    declaration::{Binding, ExportDeclaration, LexicalDeclaration, VariableList},\n    expression::{Identifier, literal::ObjectMethodDefinition},\n    function::{\n        ArrowFunction, AsyncArrowFunction, AsyncFunctionDeclaration, AsyncFunctionExpression,\n        AsyncGeneratorDeclaration, AsyncGeneratorExpression, ClassDeclaration, ClassElement,\n        ClassExpression, FormalParameterList, FunctionBody, FunctionDeclaration,\n        FunctionExpression, GeneratorDeclaration, GeneratorExpression,\n    },\n    operations::{\n        ContainsSymbol, LexicallyScopedDeclaration, VarScopedDeclaration, bound_names, contains,\n        lexically_declared_names, lexically_scoped_declarations, var_declared_names,\n        var_scoped_declarations,\n    },\n    property::PropertyName,\n    scope::{FunctionScopes, IdentifierReference, Scope},\n    statement::{\n        Block, Catch, ForInLoop, ForLoop, ForOfLoop, Switch, With,\n        iteration::{ForLoopInitializer, IterableLoopInitializer},\n    },\n    visitor::{NodeRef, NodeRefMut, VisitorMut},\n};\nuse boa_interner::{Interner, Sym};\nuse rustc_hash::FxHashMap;\nuse std::ops::ControlFlow;\n\n/// Collect bindings and fill the scopes with them.\n///\n/// # Errors\n/// Any break in the control flow that happened during the collection.\npub(crate) fn collect_bindings<'a, N>(\n    node: &'a mut N,\n    strict: bool,\n    eval: bool,\n    scope: &Scope,\n    interner: &Interner,\n) -> Result<(), &'static str>\nwhere\n    &'a mut N: Into<NodeRefMut<'a>>,\n{\n    let mut visitor = BindingCollectorVisitor {\n        strict,\n        eval,\n        in_arrow: false,\n        scope: scope.clone(),\n        interner,\n    };\n    match visitor.visit(node) {\n        ControlFlow::Continue(()) => Ok(()),\n        ControlFlow::Break(reason) => Err(reason),\n    }\n}\n\n/// Analyze if bindings escape their function scopes.\n///\n/// # Errors\n/// Any break in the control flow that happened during the analysis.\npub(crate) fn analyze_binding_escapes<'a, N>(\n    node: &'a mut N,\n    in_eval: bool,\n    scope: Scope,\n    interner: &Interner,\n) -> Result<(), &'static str>\nwhere\n    &'a mut N: Into<NodeRefMut<'a>>,\n{\n    let mut visitor = BindingEscapeAnalyzer {\n        scope,\n        direct_eval: in_eval,\n        with: false,\n        interner,\n    };\n\n    match visitor.visit(node.into()) {\n        ControlFlow::Continue(()) => Ok(()),\n        ControlFlow::Break(reason) => Err(reason),\n    }\n}\n\nstruct BindingEscapeAnalyzer<'interner> {\n    scope: Scope,\n    direct_eval: bool,\n    with: bool,\n    interner: &'interner Interner,\n}\n\nimpl<'ast> VisitorMut<'ast> for BindingEscapeAnalyzer<'_> {\n    type BreakTy = &'static str;\n\n    fn visit_identifier_mut(&mut self, node: &'ast mut Identifier) -> ControlFlow<Self::BreakTy> {\n        let name = node.to_js_string(self.interner);\n        self.scope\n            .access_binding(&name, self.direct_eval || self.with);\n        ControlFlow::Continue(())\n    }\n\n    fn visit_block_mut(&mut self, node: &'ast mut Block) -> ControlFlow<Self::BreakTy> {\n        let direct_eval_old = self.direct_eval;\n        self.direct_eval = node.contains_direct_eval || self.direct_eval;\n        if let Some(scope) = &mut node.scope {\n            if self.direct_eval {\n                scope.escape_all_bindings();\n            }\n            std::mem::swap(&mut self.scope, scope);\n        }\n\n        self.visit_statement_list_mut(&mut node.statements)?;\n        if let Some(scope) = &mut node.scope {\n            std::mem::swap(&mut self.scope, scope);\n            scope.reorder_binding_indices();\n        }\n        self.direct_eval = direct_eval_old;\n        ControlFlow::Continue(())\n    }\n\n    fn visit_switch_mut(&mut self, node: &'ast mut Switch) -> ControlFlow<Self::BreakTy> {\n        self.visit_expression_mut(&mut node.val)?;\n        let direct_eval_old = self.direct_eval;\n        self.direct_eval = node.contains_direct_eval || self.direct_eval;\n        if let Some(scope) = &mut node.scope {\n            if self.direct_eval {\n                scope.escape_all_bindings();\n            }\n            std::mem::swap(&mut self.scope, scope);\n        }\n        for case in &mut node.cases {\n            self.visit_case_mut(case)?;\n        }\n        if let Some(scope) = &mut node.scope {\n            std::mem::swap(&mut self.scope, scope);\n            scope.reorder_binding_indices();\n        }\n        self.direct_eval = direct_eval_old;\n        ControlFlow::Continue(())\n    }\n\n    fn visit_with_mut(&mut self, node: &'ast mut With) -> ControlFlow<Self::BreakTy> {\n        let with = self.with;\n        self.with = true;\n        if self.direct_eval {\n            node.scope.escape_all_bindings();\n        }\n        self.visit_expression_mut(&mut node.expression)?;\n        std::mem::swap(&mut self.scope, &mut node.scope);\n        self.visit_statement_mut(&mut node.statement)?;\n        std::mem::swap(&mut self.scope, &mut node.scope);\n        node.scope.reorder_binding_indices();\n        self.with = with;\n        ControlFlow::Continue(())\n    }\n\n    fn visit_catch_mut(&mut self, node: &'ast mut Catch) -> ControlFlow<Self::BreakTy> {\n        let direct_eval_old = self.direct_eval;\n        self.direct_eval = node.contains_direct_eval || self.direct_eval;\n        if self.direct_eval {\n            node.scope.escape_all_bindings();\n        }\n        std::mem::swap(&mut self.scope, &mut node.scope);\n        if let Some(binding) = &mut node.parameter {\n            self.visit_binding_mut(binding)?;\n        }\n        self.visit_block_mut(&mut node.block)?;\n        std::mem::swap(&mut self.scope, &mut node.scope);\n        node.scope.reorder_binding_indices();\n        self.direct_eval = direct_eval_old;\n        ControlFlow::Continue(())\n    }\n\n    fn visit_for_loop_mut(&mut self, node: &'ast mut ForLoop) -> ControlFlow<Self::BreakTy> {\n        let direct_eval_old = self.direct_eval;\n        self.direct_eval = node.inner.contains_direct_eval || self.direct_eval;\n        if let Some(ForLoopInitializer::Lexical(decl)) = &mut node.inner.init {\n            if self.direct_eval {\n                decl.scope.escape_all_bindings();\n            }\n            std::mem::swap(&mut self.scope, &mut decl.scope);\n        }\n        if let Some(init) = &mut node.inner.init {\n            self.visit_for_loop_initializer_mut(init)?;\n        }\n        if let Some(condition) = &mut node.inner.condition {\n            self.visit_expression_mut(condition)?;\n        }\n        if let Some(final_expr) = &mut node.inner.final_expr {\n            self.visit_expression_mut(final_expr)?;\n        }\n        self.visit_statement_mut(&mut node.inner.body)?;\n        if let Some(ForLoopInitializer::Lexical(decl)) = &mut node.inner.init {\n            std::mem::swap(&mut self.scope, &mut decl.scope);\n            decl.scope.reorder_binding_indices();\n        }\n        self.direct_eval = direct_eval_old;\n        ControlFlow::Continue(())\n    }\n\n    fn visit_for_in_loop_mut(&mut self, node: &'ast mut ForInLoop) -> ControlFlow<Self::BreakTy> {\n        let direct_eval_old = self.direct_eval;\n        if let Some(scope) = &mut node.target_scope {\n            self.direct_eval = node.target_contains_direct_eval || self.direct_eval;\n            if self.direct_eval {\n                scope.escape_all_bindings();\n            }\n            std::mem::swap(&mut self.scope, scope);\n        }\n        self.visit_expression_mut(&mut node.target)?;\n        if let Some(scope) = &mut node.target_scope {\n            self.direct_eval = direct_eval_old;\n            std::mem::swap(&mut self.scope, scope);\n            scope.reorder_binding_indices();\n        }\n        if let Some(scope) = &mut node.scope {\n            self.direct_eval = node.contains_direct_eval || self.direct_eval;\n            if self.direct_eval {\n                scope.escape_all_bindings();\n            }\n            std::mem::swap(&mut self.scope, scope);\n        }\n        self.visit_iterable_loop_initializer_mut(&mut node.initializer)?;\n        self.visit_statement_mut(&mut node.body)?;\n        if let Some(scope) = &mut node.scope {\n            std::mem::swap(&mut self.scope, scope);\n            scope.reorder_binding_indices();\n        }\n        self.direct_eval = direct_eval_old;\n        ControlFlow::Continue(())\n    }\n\n    fn visit_for_of_loop_mut(&mut self, node: &'ast mut ForOfLoop) -> ControlFlow<Self::BreakTy> {\n        let direct_eval_old = self.direct_eval;\n        if let Some(scope) = &mut node.iterable_scope {\n            self.direct_eval = node.iterable_contains_direct_eval || self.direct_eval;\n            if self.direct_eval {\n                scope.escape_all_bindings();\n            }\n            std::mem::swap(&mut self.scope, scope);\n        }\n        self.visit_expression_mut(&mut node.iterable)?;\n        if let Some(scope) = &mut node.iterable_scope {\n            self.direct_eval = direct_eval_old;\n            std::mem::swap(&mut self.scope, scope);\n            scope.reorder_binding_indices();\n        }\n        if let Some(scope) = &mut node.scope {\n            self.direct_eval = node.contains_direct_eval || self.direct_eval;\n            if self.direct_eval {\n                scope.escape_all_bindings();\n            }\n            std::mem::swap(&mut self.scope, scope);\n        }\n        self.visit_iterable_loop_initializer_mut(&mut node.init)?;\n        self.visit_statement_mut(&mut node.body)?;\n        if let Some(scope) = &mut node.scope {\n            std::mem::swap(&mut self.scope, scope);\n            scope.reorder_binding_indices();\n        }\n        self.direct_eval = direct_eval_old;\n        ControlFlow::Continue(())\n    }\n\n    fn visit_function_declaration_mut(\n        &mut self,\n        node: &'ast mut FunctionDeclaration,\n    ) -> ControlFlow<Self::BreakTy> {\n        self.visit_function_like(\n            &mut node.parameters,\n            &mut node.body,\n            &mut node.scopes,\n            node.contains_direct_eval,\n        )\n    }\n\n    fn visit_generator_declaration_mut(\n        &mut self,\n        node: &'ast mut GeneratorDeclaration,\n    ) -> ControlFlow<Self::BreakTy> {\n        self.visit_function_like(\n            &mut node.parameters,\n            &mut node.body,\n            &mut node.scopes,\n            node.contains_direct_eval,\n        )\n    }\n\n    fn visit_async_function_declaration_mut(\n        &mut self,\n        node: &'ast mut AsyncFunctionDeclaration,\n    ) -> ControlFlow<Self::BreakTy> {\n        self.visit_function_like(\n            &mut node.parameters,\n            &mut node.body,\n            &mut node.scopes,\n            node.contains_direct_eval,\n        )\n    }\n\n    fn visit_async_generator_declaration_mut(\n        &mut self,\n        node: &'ast mut AsyncGeneratorDeclaration,\n    ) -> ControlFlow<Self::BreakTy> {\n        self.visit_function_like(\n            &mut node.parameters,\n            &mut node.body,\n            &mut node.scopes,\n            node.contains_direct_eval,\n        )\n    }\n\n    fn visit_function_expression_mut(\n        &mut self,\n        node: &'ast mut FunctionExpression,\n    ) -> ControlFlow<Self::BreakTy> {\n        self.visit_function_like(\n            &mut node.parameters,\n            &mut node.body,\n            &mut node.scopes,\n            node.contains_direct_eval,\n        )\n    }\n\n    fn visit_generator_expression_mut(\n        &mut self,\n        node: &'ast mut GeneratorExpression,\n    ) -> ControlFlow<Self::BreakTy> {\n        self.visit_function_like(\n            &mut node.parameters,\n            &mut node.body,\n            &mut node.scopes,\n            node.contains_direct_eval,\n        )\n    }\n\n    fn visit_async_function_expression_mut(\n        &mut self,\n        node: &'ast mut AsyncFunctionExpression,\n    ) -> ControlFlow<Self::BreakTy> {\n        self.visit_function_like(\n            &mut node.parameters,\n            &mut node.body,\n            &mut node.scopes,\n            node.contains_direct_eval,\n        )\n    }\n\n    fn visit_async_generator_expression_mut(\n        &mut self,\n        node: &'ast mut AsyncGeneratorExpression,\n    ) -> ControlFlow<Self::BreakTy> {\n        self.visit_function_like(\n            &mut node.parameters,\n            &mut node.body,\n            &mut node.scopes,\n            node.contains_direct_eval,\n        )\n    }\n\n    fn visit_arrow_function_mut(\n        &mut self,\n        node: &'ast mut ArrowFunction,\n    ) -> ControlFlow<Self::BreakTy> {\n        self.visit_function_like(\n            &mut node.parameters,\n            &mut node.body,\n            &mut node.scopes,\n            node.contains_direct_eval,\n        )\n    }\n\n    fn visit_async_arrow_function_mut(\n        &mut self,\n        node: &'ast mut AsyncArrowFunction,\n    ) -> ControlFlow<Self::BreakTy> {\n        self.visit_function_like(\n            &mut node.parameters,\n            &mut node.body,\n            &mut node.scopes,\n            node.contains_direct_eval,\n        )\n    }\n\n    fn visit_class_declaration_mut(\n        &mut self,\n        node: &'ast mut ClassDeclaration,\n    ) -> ControlFlow<Self::BreakTy> {\n        node.name_scope.escape_all_bindings();\n        std::mem::swap(&mut self.scope, &mut node.name_scope);\n        if let Some(super_ref) = &mut node.super_ref {\n            self.visit_expression_mut(super_ref)?;\n        }\n        if let Some(constructor) = &mut node.constructor {\n            self.visit_function_expression_mut(constructor)?;\n        }\n        for element in &mut *node.elements {\n            self.visit_class_element_mut(element)?;\n        }\n        std::mem::swap(&mut self.scope, &mut node.name_scope);\n        node.name_scope.reorder_binding_indices();\n        ControlFlow::Continue(())\n    }\n\n    fn visit_class_expression_mut(\n        &mut self,\n        node: &'ast mut ClassExpression,\n    ) -> ControlFlow<Self::BreakTy> {\n        if let Some(name_scope) = &mut node.name_scope {\n            if self.direct_eval {\n                name_scope.escape_all_bindings();\n            }\n            name_scope.escape_all_bindings();\n            std::mem::swap(&mut self.scope, name_scope);\n        }\n        if let Some(super_ref) = &mut node.super_ref {\n            self.visit_expression_mut(super_ref)?;\n        }\n        if let Some(constructor) = &mut node.constructor {\n            self.visit_function_expression_mut(constructor)?;\n        }\n        for element in &mut *node.elements {\n            self.visit_class_element_mut(element)?;\n        }\n        if let Some(name_scope) = &mut node.name_scope {\n            std::mem::swap(&mut self.scope, name_scope);\n            name_scope.reorder_binding_indices();\n        }\n        ControlFlow::Continue(())\n    }\n\n    fn visit_class_element_mut(\n        &mut self,\n        node: &'ast mut ClassElement,\n    ) -> ControlFlow<Self::BreakTy> {\n        match node {\n            ClassElement::MethodDefinition(node) => self.visit_function_like(\n                &mut node.parameters,\n                &mut node.body,\n                &mut node.scopes,\n                node.contains_direct_eval,\n            ),\n            ClassElement::FieldDefinition(field) | ClassElement::StaticFieldDefinition(field) => {\n                self.visit_property_name_mut(&mut field.name)?;\n                if let Some(e) = &mut field.initializer {\n                    std::mem::swap(&mut self.scope, &mut field.scope);\n                    self.visit_expression_mut(e)?;\n                    std::mem::swap(&mut self.scope, &mut field.scope);\n                }\n                ControlFlow::Continue(())\n            }\n            ClassElement::PrivateFieldDefinition(field)\n            | ClassElement::PrivateStaticFieldDefinition(field) => {\n                if let Some(e) = &mut field.initializer {\n                    std::mem::swap(&mut self.scope, &mut field.scope);\n                    self.visit_expression_mut(e)?;\n                    std::mem::swap(&mut self.scope, &mut field.scope);\n                }\n                ControlFlow::Continue(())\n            }\n            ClassElement::StaticBlock(node) => {\n                let contains_direct_eval = contains(node.statements(), ContainsSymbol::DirectEval);\n                self.visit_function_like(\n                    &mut FormalParameterList::default(),\n                    &mut node.body,\n                    &mut node.scopes,\n                    contains_direct_eval,\n                )\n            }\n        }\n    }\n\n    fn visit_object_method_definition_mut(\n        &mut self,\n        node: &'ast mut ObjectMethodDefinition,\n    ) -> ControlFlow<Self::BreakTy> {\n        self.visit_property_name_mut(&mut node.name)?;\n        self.visit_function_like(\n            &mut node.parameters,\n            &mut node.body,\n            &mut node.scopes,\n            node.contains_direct_eval,\n        )\n    }\n\n    fn visit_export_declaration_mut(\n        &mut self,\n        node: &'ast mut ExportDeclaration,\n    ) -> ControlFlow<Self::BreakTy> {\n        match node {\n            ExportDeclaration::ReExport {\n                specifier, kind, ..\n            } => {\n                self.visit_module_specifier_mut(specifier)?;\n                self.visit_re_export_kind_mut(kind)\n            }\n            ExportDeclaration::List(list) => {\n                for item in &mut **list {\n                    self.visit_export_specifier_mut(item)?;\n                }\n                ControlFlow::Continue(())\n            }\n            ExportDeclaration::VarStatement(var) => self.visit_var_declaration_mut(var),\n            ExportDeclaration::Declaration(decl) => self.visit_declaration_mut(decl),\n            ExportDeclaration::DefaultFunctionDeclaration(f) => {\n                self.visit_function_declaration_mut(f)\n            }\n            ExportDeclaration::DefaultGeneratorDeclaration(g) => {\n                self.visit_generator_declaration_mut(g)\n            }\n            ExportDeclaration::DefaultAsyncFunctionDeclaration(af) => {\n                self.visit_async_function_declaration_mut(af)\n            }\n            ExportDeclaration::DefaultAsyncGeneratorDeclaration(ag) => {\n                self.visit_async_generator_declaration_mut(ag)\n            }\n            ExportDeclaration::DefaultClassDeclaration(c) => self.visit_class_declaration_mut(c),\n            ExportDeclaration::DefaultAssignmentExpression(expr) => {\n                let name = Sym::DEFAULT_EXPORT.to_js_string(self.interner);\n                drop(self.scope.create_mutable_binding(name.clone(), false));\n                self.scope.access_binding(&name, true);\n                self.visit_expression_mut(expr)\n            }\n        }\n    }\n\n    fn visit_module_mut(&mut self, node: &'ast mut Module) -> ControlFlow<Self::BreakTy> {\n        let mut scope = node.scope.clone();\n        scope.escape_all_bindings();\n        std::mem::swap(&mut self.scope, &mut scope);\n        self.visit_module_item_list_mut(&mut node.items)?;\n        std::mem::swap(&mut self.scope, &mut scope);\n        scope.reorder_binding_indices();\n        ControlFlow::Continue(())\n    }\n}\n\nimpl BindingEscapeAnalyzer<'_> {\n    fn visit_function_like(\n        &mut self,\n        parameters: &mut FormalParameterList,\n        body: &mut FunctionBody,\n        scopes: &mut FunctionScopes,\n        contains_direct_eval: bool,\n    ) -> ControlFlow<&'static str> {\n        let direct_eval_old = self.direct_eval;\n        self.direct_eval = contains_direct_eval || self.direct_eval;\n        if self.direct_eval {\n            scopes.escape_all_bindings();\n        }\n        let mut scope = scopes.parameter_scope();\n        std::mem::swap(&mut self.scope, &mut scope);\n        self.visit_formal_parameter_list_mut(parameters)?;\n        std::mem::swap(&mut self.scope, &mut scope);\n        scope = scopes.body_scope();\n        std::mem::swap(&mut self.scope, &mut scope);\n        self.visit_function_body_mut(body)?;\n        std::mem::swap(&mut self.scope, &mut scope);\n        if scopes.arguments_object_accessed() && scopes.mapped_arguments_object {\n            let parameter_names = bound_names(parameters);\n            for name in parameter_names {\n                scopes\n                    .parameter_scope()\n                    .access_binding(&name.to_js_string(self.interner), true);\n            }\n        }\n        scopes.reorder_binding_indices();\n        self.direct_eval = direct_eval_old;\n        ControlFlow::Continue(())\n    }\n}\n\nstruct BindingCollectorVisitor<'interner> {\n    strict: bool,\n    eval: bool,\n    scope: Scope,\n    in_arrow: bool,\n    interner: &'interner Interner,\n}\n\nimpl<'ast> VisitorMut<'ast> for BindingCollectorVisitor<'_> {\n    type BreakTy = &'static str;\n\n    fn visit_this_mut(\n        &mut self,\n        _node: &'ast mut crate::expression::This,\n    ) -> ControlFlow<Self::BreakTy> {\n        // NOTE: Arrow functions inherit 'this' from their enclosing scope, so we must escape it.\n        if self.in_arrow {\n            self.scope.escape_this_in_enclosing_function_scope();\n        }\n        ControlFlow::Continue(())\n    }\n\n    fn visit_function_declaration_mut(\n        &mut self,\n        node: &'ast mut FunctionDeclaration,\n    ) -> ControlFlow<Self::BreakTy> {\n        let strict = node.body.strict();\n        self.visit_function_like(\n            &mut node.body,\n            &mut node.parameters,\n            &mut node.scopes,\n            None,\n            &mut None,\n            strict,\n            false,\n        )\n    }\n\n    fn visit_generator_declaration_mut(\n        &mut self,\n        node: &'ast mut GeneratorDeclaration,\n    ) -> ControlFlow<Self::BreakTy> {\n        let strict = node.body.strict();\n        self.visit_function_like(\n            &mut node.body,\n            &mut node.parameters,\n            &mut node.scopes,\n            None,\n            &mut None,\n            strict,\n            false,\n        )\n    }\n\n    fn visit_async_function_declaration_mut(\n        &mut self,\n        node: &'ast mut AsyncFunctionDeclaration,\n    ) -> ControlFlow<Self::BreakTy> {\n        let strict = node.body.strict();\n        self.visit_function_like(\n            &mut node.body,\n            &mut node.parameters,\n            &mut node.scopes,\n            None,\n            &mut None,\n            strict,\n            false,\n        )\n    }\n\n    fn visit_async_generator_declaration_mut(\n        &mut self,\n        node: &'ast mut AsyncGeneratorDeclaration,\n    ) -> ControlFlow<Self::BreakTy> {\n        let strict = node.body.strict();\n        self.visit_function_like(\n            &mut node.body,\n            &mut node.parameters,\n            &mut node.scopes,\n            None,\n            &mut None,\n            strict,\n            false,\n        )\n    }\n\n    fn visit_function_expression_mut(\n        &mut self,\n        node: &'ast mut FunctionExpression,\n    ) -> ControlFlow<Self::BreakTy> {\n        let name = if node.has_binding_identifier {\n            node.name()\n        } else {\n            None\n        };\n        let strict = node.body.strict();\n        self.visit_function_like(\n            &mut node.body,\n            &mut node.parameters,\n            &mut node.scopes,\n            name,\n            &mut node.name_scope,\n            strict,\n            false,\n        )\n    }\n\n    fn visit_generator_expression_mut(\n        &mut self,\n        node: &'ast mut GeneratorExpression,\n    ) -> ControlFlow<Self::BreakTy> {\n        let name = if node.has_binding_identifier {\n            node.name()\n        } else {\n            None\n        };\n        let strict = node.body.strict();\n        self.visit_function_like(\n            &mut node.body,\n            &mut node.parameters,\n            &mut node.scopes,\n            name,\n            &mut node.name_scope,\n            strict,\n            false,\n        )\n    }\n\n    fn visit_async_function_expression_mut(\n        &mut self,\n        node: &'ast mut AsyncFunctionExpression,\n    ) -> ControlFlow<Self::BreakTy> {\n        let name = if node.has_binding_identifier {\n            node.name()\n        } else {\n            None\n        };\n        let strict = node.body.strict();\n        self.visit_function_like(\n            &mut node.body,\n            &mut node.parameters,\n            &mut node.scopes,\n            name,\n            &mut node.name_scope,\n            strict,\n            false,\n        )\n    }\n\n    fn visit_async_generator_expression_mut(\n        &mut self,\n        node: &'ast mut AsyncGeneratorExpression,\n    ) -> ControlFlow<Self::BreakTy> {\n        let name = if node.has_binding_identifier {\n            node.name()\n        } else {\n            None\n        };\n        let strict = node.body.strict();\n        self.visit_function_like(\n            &mut node.body,\n            &mut node.parameters,\n            &mut node.scopes,\n            name,\n            &mut node.name_scope,\n            strict,\n            false,\n        )\n    }\n\n    fn visit_arrow_function_mut(\n        &mut self,\n        node: &'ast mut ArrowFunction,\n    ) -> ControlFlow<Self::BreakTy> {\n        let strict = node.body.strict();\n        self.visit_function_like(\n            &mut node.body,\n            &mut node.parameters,\n            &mut node.scopes,\n            None,\n            &mut None,\n            strict,\n            true,\n        )\n    }\n\n    fn visit_async_arrow_function_mut(\n        &mut self,\n        node: &'ast mut AsyncArrowFunction,\n    ) -> ControlFlow<Self::BreakTy> {\n        let strict = node.body.strict();\n        self.visit_function_like(\n            &mut node.body,\n            &mut node.parameters,\n            &mut node.scopes,\n            None,\n            &mut None,\n            strict,\n            true,\n        )\n    }\n\n    fn visit_class_declaration_mut(\n        &mut self,\n        node: &'ast mut ClassDeclaration,\n    ) -> ControlFlow<Self::BreakTy> {\n        let mut name_scope = Scope::new(self.scope.clone(), false);\n        let name = node.name().to_js_string(self.interner);\n        name_scope.create_immutable_binding(name, true);\n        std::mem::swap(&mut self.scope, &mut name_scope);\n        if let Some(super_ref) = &mut node.super_ref {\n            self.visit_expression_mut(super_ref)?;\n        }\n        if let Some(constructor) = &mut node.constructor {\n            self.visit_function_expression_mut(constructor)?;\n        }\n        for element in &mut *node.elements {\n            self.visit_class_element_mut(element)?;\n        }\n        std::mem::swap(&mut self.scope, &mut name_scope);\n        node.name_scope = name_scope;\n        ControlFlow::Continue(())\n    }\n\n    fn visit_class_expression_mut(\n        &mut self,\n        node: &'ast mut ClassExpression,\n    ) -> ControlFlow<Self::BreakTy> {\n        let mut name_scope = None;\n        if let Some(name) = node.name\n            && node.name_scope.is_some()\n        {\n            let mut scope = Scope::new(self.scope.clone(), false);\n            let name = name.to_js_string(self.interner);\n            scope.create_immutable_binding(name, true);\n            node.name_scope = Some(scope.clone());\n            std::mem::swap(&mut self.scope, &mut scope);\n            name_scope = Some(scope);\n        }\n\n        if let Some(super_ref) = &mut node.super_ref {\n            self.visit_expression_mut(super_ref)?;\n        }\n        if let Some(constructor) = &mut node.constructor {\n            self.visit_function_expression_mut(constructor)?;\n        }\n        for element in &mut *node.elements {\n            self.visit_class_element_mut(element)?;\n        }\n        if let Some(mut scope) = name_scope {\n            std::mem::swap(&mut self.scope, &mut scope);\n        }\n        ControlFlow::Continue(())\n    }\n\n    fn visit_class_element_mut(\n        &mut self,\n        node: &'ast mut ClassElement,\n    ) -> ControlFlow<Self::BreakTy> {\n        match node {\n            ClassElement::MethodDefinition(node) => {\n                let strict = node.body.strict();\n                self.visit_function_like(\n                    &mut node.body,\n                    &mut node.parameters,\n                    &mut node.scopes,\n                    None,\n                    &mut None,\n                    strict,\n                    false,\n                )\n            }\n            ClassElement::FieldDefinition(field) | ClassElement::StaticFieldDefinition(field) => {\n                self.visit_property_name_mut(&mut field.name)?;\n                let mut scope = Scope::new(self.scope.clone(), true);\n                std::mem::swap(&mut self.scope, &mut scope);\n                if let Some(e) = &mut field.initializer {\n                    self.visit_expression_mut(e)?;\n                }\n                std::mem::swap(&mut self.scope, &mut scope);\n                field.scope = scope;\n                ControlFlow::Continue(())\n            }\n            ClassElement::PrivateFieldDefinition(field)\n            | ClassElement::PrivateStaticFieldDefinition(field) => {\n                let mut scope = Scope::new(self.scope.clone(), true);\n                std::mem::swap(&mut self.scope, &mut scope);\n                if let Some(e) = &mut field.initializer {\n                    self.visit_expression_mut(e)?;\n                }\n                std::mem::swap(&mut self.scope, &mut scope);\n                field.scope = scope;\n                ControlFlow::Continue(())\n            }\n            ClassElement::StaticBlock(node) => {\n                let strict = node.body.strict();\n                self.visit_function_like(\n                    &mut node.body,\n                    &mut FormalParameterList::default(),\n                    &mut node.scopes,\n                    None,\n                    &mut None,\n                    strict,\n                    false,\n                )\n            }\n        }\n    }\n\n    fn visit_object_method_definition_mut(\n        &mut self,\n        node: &'ast mut ObjectMethodDefinition,\n    ) -> ControlFlow<Self::BreakTy> {\n        match &mut node.name {\n            PropertyName::Literal(_) => {}\n            PropertyName::Computed(name) => {\n                self.visit_expression_mut(name)?;\n            }\n        }\n        let strict = node.body.strict();\n        self.visit_function_like(\n            &mut node.body,\n            &mut node.parameters,\n            &mut node.scopes,\n            None,\n            &mut None,\n            strict,\n            false,\n        )\n    }\n\n    fn visit_block_mut(&mut self, node: &'ast mut Block) -> ControlFlow<Self::BreakTy> {\n        let mut scope = block_declaration_instantiation(node, self.scope.clone(), self.interner);\n        if let Some(scope) = &mut scope {\n            std::mem::swap(&mut self.scope, scope);\n        }\n        self.visit_statement_list_mut(&mut node.statements)?;\n        if let Some(scope) = &mut scope {\n            std::mem::swap(&mut self.scope, scope);\n        }\n        node.scope = scope;\n        ControlFlow::Continue(())\n    }\n\n    fn visit_switch_mut(&mut self, node: &'ast mut Switch) -> ControlFlow<Self::BreakTy> {\n        self.visit_expression_mut(&mut node.val)?;\n        let mut scope = block_declaration_instantiation(node, self.scope.clone(), self.interner);\n        if let Some(scope) = &mut scope {\n            std::mem::swap(&mut self.scope, scope);\n        }\n        for case in &mut *node.cases {\n            self.visit_case_mut(case)?;\n        }\n        if let Some(scope) = &mut scope {\n            std::mem::swap(&mut self.scope, scope);\n        }\n        node.scope = scope;\n        ControlFlow::Continue(())\n    }\n\n    fn visit_with_mut(&mut self, node: &'ast mut With) -> ControlFlow<Self::BreakTy> {\n        self.visit_expression_mut(&mut node.expression)?;\n        let mut scope = Scope::new(self.scope.clone(), false);\n        std::mem::swap(&mut self.scope, &mut scope);\n        self.visit_statement_mut(&mut node.statement)?;\n        std::mem::swap(&mut self.scope, &mut scope);\n        node.scope = scope;\n        ControlFlow::Continue(())\n    }\n\n    fn visit_catch_mut(&mut self, node: &'ast mut Catch) -> ControlFlow<Self::BreakTy> {\n        let mut scope = Scope::new(self.scope.clone(), false);\n        if let Some(binding) = node.parameter() {\n            match binding {\n                Binding::Identifier(ident) => {\n                    let ident = ident.to_js_string(self.interner);\n                    drop(scope.create_mutable_binding(ident.clone(), false));\n                }\n                Binding::Pattern(pattern) => {\n                    for ident in bound_names(pattern) {\n                        let ident = ident.to_js_string(self.interner);\n                        drop(scope.create_mutable_binding(ident, false));\n                    }\n                }\n            }\n        }\n        std::mem::swap(&mut self.scope, &mut scope);\n        if let Some(binding) = &mut node.parameter {\n            self.visit_binding_mut(binding)?;\n        }\n        self.visit_block_mut(&mut node.block)?;\n        std::mem::swap(&mut self.scope, &mut scope);\n        node.scope = scope;\n        ControlFlow::Continue(())\n    }\n\n    fn visit_for_loop_mut(&mut self, node: &'ast mut ForLoop) -> ControlFlow<Self::BreakTy> {\n        let scope = match &mut node.inner.init {\n            Some(ForLoopInitializer::Lexical(decl)) => {\n                let mut scope = Scope::new(self.scope.clone(), false);\n                let names = bound_names(&decl.declaration);\n                if decl.declaration.is_const() {\n                    for name in &names {\n                        let name = name.to_js_string(self.interner);\n                        scope.create_immutable_binding(name, true);\n                    }\n                } else {\n                    for name in &names {\n                        let name = name.to_js_string(self.interner);\n                        drop(scope.create_mutable_binding(name, false));\n                    }\n                }\n                decl.scope = scope.clone();\n                std::mem::swap(&mut self.scope, &mut scope);\n                Some(scope)\n            }\n            _ => None,\n        };\n        if let Some(fli) = &mut node.inner.init {\n            self.visit_for_loop_initializer_mut(fli)?;\n        }\n        if let Some(expr) = &mut node.inner.condition {\n            self.visit_expression_mut(expr)?;\n        }\n        if let Some(expr) = &mut node.inner.final_expr {\n            self.visit_expression_mut(expr)?;\n        }\n        self.visit_statement_mut(&mut node.inner.body)?;\n        if let Some(mut scope) = scope {\n            std::mem::swap(&mut self.scope, &mut scope);\n        }\n        ControlFlow::Continue(())\n    }\n\n    fn visit_for_in_loop_mut(&mut self, node: &'ast mut ForInLoop) -> ControlFlow<Self::BreakTy> {\n        let initializer_bound_names = match node.initializer() {\n            IterableLoopInitializer::Let(declaration)\n            | IterableLoopInitializer::Const(declaration) => bound_names(declaration),\n            _ => Vec::new(),\n        };\n        if initializer_bound_names.is_empty() {\n            self.visit_expression_mut(&mut node.target)?;\n        } else {\n            let mut scope = Scope::new(self.scope.clone(), false);\n            for name in &initializer_bound_names {\n                let name = name.to_js_string(self.interner);\n                drop(scope.create_mutable_binding(name, false));\n            }\n            std::mem::swap(&mut self.scope, &mut scope);\n            self.visit_expression_mut(&mut node.target)?;\n            std::mem::swap(&mut self.scope, &mut scope);\n            node.target_scope = Some(scope);\n        }\n        let scope = match node.initializer() {\n            IterableLoopInitializer::Let(declaration) => {\n                let scope = Scope::new(self.scope.clone(), false);\n                match declaration {\n                    Binding::Identifier(ident) => {\n                        let ident = ident.to_js_string(self.interner);\n                        drop(scope.create_mutable_binding(ident.clone(), false));\n                    }\n                    Binding::Pattern(pattern) => {\n                        for ident in bound_names(pattern) {\n                            let ident = ident.to_js_string(self.interner);\n                            drop(scope.create_mutable_binding(ident, false));\n                        }\n                    }\n                }\n                Some(scope)\n            }\n            IterableLoopInitializer::Const(declaration) => {\n                let scope = Scope::new(self.scope.clone(), false);\n                match declaration {\n                    Binding::Identifier(ident) => {\n                        let ident = ident.to_js_string(self.interner);\n                        scope.create_immutable_binding(ident.clone(), true);\n                    }\n                    Binding::Pattern(pattern) => {\n                        for ident in bound_names(pattern) {\n                            let ident = ident.to_js_string(self.interner);\n                            scope.create_immutable_binding(ident, true);\n                        }\n                    }\n                }\n                Some(scope)\n            }\n            _ => None,\n        };\n        if let Some(mut scope) = scope {\n            std::mem::swap(&mut self.scope, &mut scope);\n            self.visit_iterable_loop_initializer_mut(&mut node.initializer)?;\n            self.visit_statement_mut(&mut node.body)?;\n            std::mem::swap(&mut self.scope, &mut scope);\n            node.scope = Some(scope);\n        } else {\n            self.visit_iterable_loop_initializer_mut(&mut node.initializer)?;\n            self.visit_statement_mut(&mut node.body)?;\n        }\n        ControlFlow::Continue(())\n    }\n\n    fn visit_for_of_loop_mut(&mut self, node: &'ast mut ForOfLoop) -> ControlFlow<Self::BreakTy> {\n        let initializer_bound_names = match node.initializer() {\n            IterableLoopInitializer::Let(declaration)\n            | IterableLoopInitializer::Const(declaration) => bound_names(declaration),\n            _ => Vec::new(),\n        };\n        if initializer_bound_names.is_empty() {\n            self.visit_expression_mut(&mut node.iterable)?;\n        } else {\n            let mut scope = Scope::new(self.scope.clone(), false);\n            for name in &initializer_bound_names {\n                let name = name.to_js_string(self.interner);\n                drop(scope.create_mutable_binding(name, false));\n            }\n            std::mem::swap(&mut self.scope, &mut scope);\n            self.visit_expression_mut(&mut node.iterable)?;\n            std::mem::swap(&mut self.scope, &mut scope);\n            node.iterable_scope = Some(scope);\n        }\n        let scope = match node.initializer() {\n            IterableLoopInitializer::Let(declaration) => {\n                let scope = Scope::new(self.scope.clone(), false);\n                match declaration {\n                    Binding::Identifier(ident) => {\n                        let ident = ident.to_js_string(self.interner);\n                        drop(scope.create_mutable_binding(ident.clone(), false));\n                    }\n                    Binding::Pattern(pattern) => {\n                        for ident in bound_names(pattern) {\n                            let ident = ident.to_js_string(self.interner);\n                            drop(scope.create_mutable_binding(ident, false));\n                        }\n                    }\n                }\n                Some(scope)\n            }\n            IterableLoopInitializer::Const(declaration) => {\n                let scope = Scope::new(self.scope.clone(), false);\n                match declaration {\n                    Binding::Identifier(ident) => {\n                        let ident = ident.to_js_string(self.interner);\n                        scope.create_immutable_binding(ident.clone(), true);\n                    }\n                    Binding::Pattern(pattern) => {\n                        for ident in bound_names(pattern) {\n                            let ident = ident.to_js_string(self.interner);\n                            scope.create_immutable_binding(ident, true);\n                        }\n                    }\n                }\n                Some(scope)\n            }\n            _ => None,\n        };\n        if let Some(mut scope) = scope {\n            std::mem::swap(&mut self.scope, &mut scope);\n            self.visit_iterable_loop_initializer_mut(&mut node.init)?;\n            self.visit_statement_mut(&mut node.body)?;\n            std::mem::swap(&mut self.scope, &mut scope);\n            node.scope = Some(scope);\n        } else {\n            self.visit_iterable_loop_initializer_mut(&mut node.init)?;\n            self.visit_statement_mut(&mut node.body)?;\n        }\n        ControlFlow::Continue(())\n    }\n\n    fn visit_module_mut(&mut self, node: &'ast mut Module) -> ControlFlow<Self::BreakTy> {\n        let mut scope = Scope::new(self.scope.clone(), true);\n        module_instantiation(node, &scope, self.interner);\n        std::mem::swap(&mut self.scope, &mut scope);\n        self.visit_module_item_list_mut(&mut node.items)?;\n        std::mem::swap(&mut self.scope, &mut scope);\n        node.scope = scope;\n        ControlFlow::Continue(())\n    }\n\n    fn visit_script_mut(&mut self, node: &'ast mut Script) -> ControlFlow<Self::BreakTy> {\n        if self.eval {\n            self.visit_statement_list_mut(node.statements_mut())?;\n        } else {\n            match global_declaration_instantiation(node, &self.scope, self.interner) {\n                Ok(()) => {\n                    self.visit_statement_list_mut(node.statements_mut())?;\n                }\n                Err(e) => return ControlFlow::Break(e),\n            }\n        }\n        ControlFlow::Continue(())\n    }\n}\n\nimpl BindingCollectorVisitor<'_> {\n    #[allow(clippy::too_many_arguments)]\n    fn visit_function_like(\n        &mut self,\n        body: &mut FunctionBody,\n        parameters: &mut FormalParameterList,\n        scopes: &mut FunctionScopes,\n        name: Option<Identifier>,\n        name_scope: &mut Option<Scope>,\n        strict: bool,\n        arrow: bool,\n    ) -> ControlFlow<&'static str> {\n        let strict = self.strict || strict;\n        let old_in_arrow = self.in_arrow;\n        self.in_arrow = arrow;\n\n        let function_scope = if let Some(name) = name {\n            let scope = Scope::new(self.scope.clone(), false);\n            let name = name.to_js_string(self.interner);\n            scope.create_immutable_binding(name, strict);\n            *name_scope = Some(scope.clone());\n            Scope::new(scope, true)\n        } else {\n            Scope::new(self.scope.clone(), true)\n        };\n\n        let function_scopes = function_declaration_instantiation(\n            body,\n            parameters,\n            arrow,\n            strict,\n            function_scope.clone(),\n            self.interner,\n        );\n\n        let mut params_scope = function_scopes.parameter_scope();\n        let mut body_scope = function_scopes.body_scope();\n\n        std::mem::swap(&mut self.scope, &mut params_scope);\n        self.visit_formal_parameter_list_mut(parameters)?;\n        std::mem::swap(&mut self.scope, &mut params_scope);\n\n        std::mem::swap(&mut self.scope, &mut body_scope);\n        self.visit_function_body_mut(body)?;\n        std::mem::swap(&mut self.scope, &mut body_scope);\n\n        *scopes = function_scopes;\n\n        self.in_arrow = old_in_arrow;\n\n        ControlFlow::Continue(())\n    }\n}\n\n/// Optimize scope indices when scopes only contain local bindings.\npub(crate) fn optimize_scope_indices<'a, N>(node: &'a mut N, scope: &Scope)\nwhere\n    &'a mut N: Into<NodeRefMut<'a>>,\n{\n    let mut visitor = ScopeIndexVisitor {\n        index: scope.scope_index(),\n    };\n    let _ = visitor.visit(node.into());\n}\n\n/// Like [`optimize_scope_indices`] but for a [`FunctionExpression`] compiled\n/// by the `Function` constructor with `force_function_scope = true`.\n///\n/// The `Function` constructor always pushes a function scope at runtime, so\n/// the optimizer must account for that even when the function scope would\n/// otherwise be elided.\npub(crate) fn optimize_scope_indices_function_constructor(\n    node: &mut FunctionExpression,\n    scope: &Scope,\n) {\n    let mut visitor = ScopeIndexVisitor {\n        index: scope.scope_index(),\n    };\n    let _ = visitor.visit_function_like(\n        &mut node.body,\n        &mut node.parameters,\n        &mut node.scopes,\n        &mut node.name_scope,\n        false,\n        // Always force the function scope for the Function constructor.\n        true,\n    );\n}\n\nstruct ScopeIndexVisitor {\n    index: u32,\n}\n\nimpl<'ast> VisitorMut<'ast> for ScopeIndexVisitor {\n    type BreakTy = ();\n\n    fn visit_function_declaration_mut(\n        &mut self,\n        node: &'ast mut FunctionDeclaration,\n    ) -> ControlFlow<Self::BreakTy> {\n        let contains_direct_eval = node.contains_direct_eval();\n        self.visit_function_like(\n            &mut node.body,\n            &mut node.parameters,\n            &mut node.scopes,\n            &mut None,\n            false,\n            contains_direct_eval,\n        )\n    }\n\n    fn visit_generator_declaration_mut(\n        &mut self,\n        node: &'ast mut GeneratorDeclaration,\n    ) -> ControlFlow<Self::BreakTy> {\n        let contains_direct_eval = node.contains_direct_eval();\n        self.visit_function_like(\n            &mut node.body,\n            &mut node.parameters,\n            &mut node.scopes,\n            &mut None,\n            false,\n            contains_direct_eval,\n        )\n    }\n\n    fn visit_async_function_declaration_mut(\n        &mut self,\n        node: &'ast mut AsyncFunctionDeclaration,\n    ) -> ControlFlow<Self::BreakTy> {\n        let contains_direct_eval = node.contains_direct_eval();\n        self.visit_function_like(\n            &mut node.body,\n            &mut node.parameters,\n            &mut node.scopes,\n            &mut None,\n            false,\n            contains_direct_eval,\n        )\n    }\n\n    fn visit_async_generator_declaration_mut(\n        &mut self,\n        node: &'ast mut AsyncGeneratorDeclaration,\n    ) -> ControlFlow<Self::BreakTy> {\n        let contains_direct_eval = node.contains_direct_eval();\n        self.visit_function_like(\n            &mut node.body,\n            &mut node.parameters,\n            &mut node.scopes,\n            &mut None,\n            false,\n            contains_direct_eval,\n        )\n    }\n\n    fn visit_function_expression_mut(\n        &mut self,\n        node: &'ast mut FunctionExpression,\n    ) -> ControlFlow<Self::BreakTy> {\n        let contains_direct_eval = node.contains_direct_eval();\n        self.visit_function_like(\n            &mut node.body,\n            &mut node.parameters,\n            &mut node.scopes,\n            &mut node.name_scope,\n            false,\n            contains_direct_eval,\n        )\n    }\n\n    fn visit_generator_expression_mut(\n        &mut self,\n        node: &'ast mut GeneratorExpression,\n    ) -> ControlFlow<Self::BreakTy> {\n        let contains_direct_eval = node.contains_direct_eval();\n        self.visit_function_like(\n            &mut node.body,\n            &mut node.parameters,\n            &mut node.scopes,\n            &mut node.name_scope,\n            false,\n            contains_direct_eval,\n        )\n    }\n\n    fn visit_async_function_expression_mut(\n        &mut self,\n        node: &'ast mut AsyncFunctionExpression,\n    ) -> ControlFlow<Self::BreakTy> {\n        let contains_direct_eval = node.contains_direct_eval();\n        self.visit_function_like(\n            &mut node.body,\n            &mut node.parameters,\n            &mut node.scopes,\n            &mut node.name_scope,\n            false,\n            contains_direct_eval,\n        )\n    }\n\n    fn visit_async_generator_expression_mut(\n        &mut self,\n        node: &'ast mut AsyncGeneratorExpression,\n    ) -> ControlFlow<Self::BreakTy> {\n        let contains_direct_eval = node.contains_direct_eval();\n        self.visit_function_like(\n            &mut node.body,\n            &mut node.parameters,\n            &mut node.scopes,\n            &mut node.name_scope,\n            false,\n            contains_direct_eval,\n        )\n    }\n\n    fn visit_arrow_function_mut(\n        &mut self,\n        node: &'ast mut ArrowFunction,\n    ) -> ControlFlow<Self::BreakTy> {\n        let contains_direct_eval = node.contains_direct_eval();\n        self.visit_function_like(\n            &mut node.body,\n            &mut node.parameters,\n            &mut node.scopes,\n            &mut None,\n            true,\n            contains_direct_eval,\n        )\n    }\n\n    fn visit_async_arrow_function_mut(\n        &mut self,\n        node: &'ast mut AsyncArrowFunction,\n    ) -> ControlFlow<Self::BreakTy> {\n        let contains_direct_eval = node.contains_direct_eval();\n        self.visit_function_like(\n            &mut node.body,\n            &mut node.parameters,\n            &mut node.scopes,\n            &mut None,\n            true,\n            contains_direct_eval,\n        )\n    }\n\n    fn visit_class_declaration_mut(\n        &mut self,\n        node: &'ast mut ClassDeclaration,\n    ) -> ControlFlow<Self::BreakTy> {\n        let index = self.index;\n        if !node.name_scope.all_bindings_local() {\n            self.index += 1;\n        }\n        node.name_scope.set_index(self.index);\n        if let Some(super_ref) = &mut node.super_ref {\n            self.visit_expression_mut(super_ref)?;\n        }\n        if let Some(constructor) = &mut node.constructor {\n            let node = constructor;\n            self.visit_function_like(\n                &mut node.body,\n                &mut node.parameters,\n                &mut node.scopes,\n                &mut node.name_scope,\n                false,\n                true,\n            )?;\n        }\n        for element in &mut *node.elements {\n            self.visit_class_element_mut(element)?;\n        }\n        self.index = index;\n        ControlFlow::Continue(())\n    }\n\n    fn visit_class_expression_mut(\n        &mut self,\n        node: &'ast mut ClassExpression,\n    ) -> ControlFlow<Self::BreakTy> {\n        let index = self.index;\n        if let Some(scope) = &node.name_scope {\n            if !scope.all_bindings_local() {\n                self.index += 1;\n            }\n            scope.set_index(self.index);\n        }\n        if let Some(super_ref) = &mut node.super_ref {\n            self.visit_expression_mut(super_ref)?;\n        }\n        if let Some(constructor) = &mut node.constructor {\n            self.visit_function_like(\n                &mut constructor.body,\n                &mut constructor.parameters,\n                &mut constructor.scopes,\n                &mut constructor.name_scope,\n                false,\n                true,\n            )?;\n        }\n        for element in &mut *node.elements {\n            self.visit_class_element_mut(element)?;\n        }\n        self.index = index;\n        ControlFlow::Continue(())\n    }\n\n    fn visit_class_element_mut(\n        &mut self,\n        node: &'ast mut ClassElement,\n    ) -> ControlFlow<Self::BreakTy> {\n        match node {\n            ClassElement::MethodDefinition(node) => {\n                let contains_direct_eval = node.contains_direct_eval();\n                self.visit_function_like(\n                    &mut node.body,\n                    &mut node.parameters,\n                    &mut node.scopes,\n                    &mut None,\n                    false,\n                    contains_direct_eval,\n                )\n            }\n            ClassElement::FieldDefinition(field) | ClassElement::StaticFieldDefinition(field) => {\n                self.visit_property_name_mut(&mut field.name)?;\n                let index = self.index;\n                self.index += 1;\n                field.scope.set_index(self.index);\n                if let Some(e) = &mut field.initializer {\n                    self.visit_expression_mut(e)?;\n                }\n                self.index = index;\n                ControlFlow::Continue(())\n            }\n            ClassElement::PrivateFieldDefinition(field)\n            | ClassElement::PrivateStaticFieldDefinition(field) => {\n                let index = self.index;\n                self.index += 1;\n                field.scope.set_index(self.index);\n                if let Some(e) = &mut field.initializer {\n                    self.visit_expression_mut(e)?;\n                }\n                self.index = index;\n                ControlFlow::Continue(())\n            }\n            ClassElement::StaticBlock(node) => self.visit_function_like(\n                &mut node.body,\n                &mut FormalParameterList::default(),\n                &mut node.scopes,\n                &mut None,\n                false,\n                true,\n            ),\n        }\n    }\n\n    fn visit_object_method_definition_mut(\n        &mut self,\n        node: &'ast mut ObjectMethodDefinition,\n    ) -> ControlFlow<Self::BreakTy> {\n        match &mut node.name {\n            PropertyName::Literal(_) => {}\n            PropertyName::Computed(name) => {\n                self.visit_expression_mut(name)?;\n            }\n        }\n        let contains_direct_eval = node.contains_direct_eval();\n        self.visit_function_like(\n            &mut node.body,\n            &mut node.parameters,\n            &mut node.scopes,\n            &mut None,\n            false,\n            contains_direct_eval,\n        )\n    }\n\n    fn visit_block_mut(&mut self, node: &'ast mut Block) -> ControlFlow<Self::BreakTy> {\n        let index = self.index;\n        if let Some(scope) = &node.scope {\n            if !scope.all_bindings_local() {\n                self.index += 1;\n            }\n            scope.set_index(self.index);\n        }\n        self.visit_statement_list_mut(&mut node.statements)?;\n        self.index = index;\n        ControlFlow::Continue(())\n    }\n\n    fn visit_switch_mut(&mut self, node: &'ast mut Switch) -> ControlFlow<Self::BreakTy> {\n        let index = self.index;\n        self.visit_expression_mut(&mut node.val)?;\n        if let Some(scope) = &node.scope {\n            if !scope.all_bindings_local() {\n                self.index += 1;\n            }\n            scope.set_index(self.index);\n        }\n        for case in &mut *node.cases {\n            self.visit_case_mut(case)?;\n        }\n        self.index = index;\n        ControlFlow::Continue(())\n    }\n\n    fn visit_with_mut(&mut self, node: &'ast mut With) -> ControlFlow<Self::BreakTy> {\n        let index = self.index;\n        self.visit_expression_mut(&mut node.expression)?;\n        self.index += 1;\n        node.scope.set_index(self.index);\n        self.visit_statement_mut(&mut node.statement)?;\n        self.index = index;\n        ControlFlow::Continue(())\n    }\n\n    fn visit_catch_mut(&mut self, node: &'ast mut Catch) -> ControlFlow<Self::BreakTy> {\n        let index = self.index;\n        if !node.scope.all_bindings_local() {\n            self.index += 1;\n        }\n        node.scope.set_index(self.index);\n        if let Some(binding) = &mut node.parameter {\n            self.visit_binding_mut(binding)?;\n        }\n        self.visit_block_mut(&mut node.block)?;\n        self.index = index;\n        ControlFlow::Continue(())\n    }\n\n    fn visit_for_loop_mut(&mut self, node: &'ast mut ForLoop) -> ControlFlow<Self::BreakTy> {\n        let index = self.index;\n        if let Some(ForLoopInitializer::Lexical(decl)) = &mut node.inner.init {\n            if !decl.scope.all_bindings_local() {\n                self.index += 1;\n            }\n            decl.scope.set_index(self.index);\n        }\n        if let Some(fli) = &mut node.inner.init {\n            self.visit_for_loop_initializer_mut(fli)?;\n        }\n        if let Some(expr) = &mut node.inner.condition {\n            self.visit_expression_mut(expr)?;\n        }\n        if let Some(expr) = &mut node.inner.final_expr {\n            self.visit_expression_mut(expr)?;\n        }\n        self.visit_statement_mut(&mut node.inner.body)?;\n        self.index = index;\n        ControlFlow::Continue(())\n    }\n\n    fn visit_for_in_loop_mut(&mut self, node: &'ast mut ForInLoop) -> ControlFlow<Self::BreakTy> {\n        {\n            let index = self.index;\n            if let Some(scope) = &node.target_scope {\n                if !scope.all_bindings_local() {\n                    self.index += 1;\n                }\n                scope.set_index(self.index);\n            }\n            self.visit_expression_mut(&mut node.target)?;\n            self.index = index;\n        }\n        let index = self.index;\n        if let Some(scope) = &node.scope {\n            if !scope.all_bindings_local() {\n                self.index += 1;\n            }\n            scope.set_index(self.index);\n        }\n        self.visit_iterable_loop_initializer_mut(&mut node.initializer)?;\n        self.visit_statement_mut(&mut node.body)?;\n        self.index = index;\n        ControlFlow::Continue(())\n    }\n\n    fn visit_for_of_loop_mut(&mut self, node: &'ast mut ForOfLoop) -> ControlFlow<Self::BreakTy> {\n        {\n            let index = self.index;\n            if let Some(scope) = &node.iterable_scope {\n                if !scope.all_bindings_local() {\n                    self.index += 1;\n                }\n                scope.set_index(self.index);\n            }\n            self.visit_expression_mut(&mut node.iterable)?;\n            self.index = index;\n        }\n        let index = self.index;\n        if let Some(scope) = &node.scope {\n            if !scope.all_bindings_local() {\n                self.index += 1;\n            }\n            scope.set_index(self.index);\n        }\n        self.visit_iterable_loop_initializer_mut(&mut node.init)?;\n        self.visit_statement_mut(&mut node.body)?;\n        self.index = index;\n        ControlFlow::Continue(())\n    }\n}\n\nimpl ScopeIndexVisitor {\n    fn visit_function_like(\n        &mut self,\n        body: &mut FunctionBody,\n        parameters: &mut FormalParameterList,\n        scopes: &mut FunctionScopes,\n        name_scope: &mut Option<Scope>,\n        arrow: bool,\n        force_function_scope: bool,\n    ) -> ControlFlow<()> {\n        let index = self.index;\n        if let Some(scope) = name_scope {\n            if !scope.all_bindings_local() {\n                self.index += 1;\n            }\n            scope.set_index(self.index);\n        }\n\n        if force_function_scope || !scopes.function_scope().all_bindings_local() {\n            scopes.requires_function_scope = true;\n            self.index += 1;\n        } else if !arrow {\n            assert!(scopes.function_scope().is_function());\n            scopes.requires_function_scope = scopes.function_scope().escaped_this()\n                || contains(parameters, ContainsSymbol::Super)\n                || contains(body, ContainsSymbol::Super)\n                || contains(parameters, ContainsSymbol::NewTarget)\n                || contains(body, ContainsSymbol::NewTarget);\n            self.index += u32::from(scopes.requires_function_scope);\n        }\n\n        scopes.function_scope.set_index(self.index);\n        if let Some(scope) = &scopes.parameters_eval_scope {\n            if !scope.all_bindings_local() {\n                self.index += 1;\n            }\n            scope.set_index(self.index);\n        }\n        self.visit_formal_parameter_list_mut(parameters)?;\n        if let Some(scope) = &scopes.parameters_scope {\n            if !scope.all_bindings_local() {\n                self.index += 1;\n            }\n            scope.set_index(self.index);\n        }\n        if let Some(scope) = &scopes.lexical_scope {\n            if !scope.all_bindings_local() {\n                self.index += 1;\n            }\n            scope.set_index(self.index);\n        }\n        self.visit_function_body_mut(body)?;\n        self.index = index;\n        ControlFlow::Continue(())\n    }\n}\n\n/// `GlobalDeclarationInstantiation ( script, env )`\n///\n/// More information:\n///  - [ECMAScript reference][spec]\n///\n/// [spec]: https://tc39.es/ecma262/#sec-globaldeclarationinstantiation\n///\n/// # Errors\n///\n/// - If a duplicate lexical declaration is found.\nfn global_declaration_instantiation(\n    script: &Script,\n    env: &Scope,\n    interner: &Interner,\n) -> Result<(), &'static str> {\n    // 1. Let lexNames be the LexicallyDeclaredNames of script.\n    let lex_names = lexically_declared_names(script);\n\n    // 2. Let varNames be the VarDeclaredNames of script.\n    let var_names = var_declared_names(script);\n\n    // 3. For each element name of lexNames, do\n    for name in lex_names {\n        let name = name.to_js_string(interner);\n\n        // Note: Our implementation differs from the spec here.\n        // a. If env.HasVarDeclaration(name) is true, throw a SyntaxError exception.\n        // b. If env.HasLexicalDeclaration(name) is true, throw a SyntaxError exception.\n        if env.has_binding(&name) {\n            return Err(\"duplicate lexical declaration\");\n        }\n    }\n\n    // 4. For each element name of varNames, do\n    for name in var_names {\n        let name = name.to_js_string(interner);\n\n        // a. If env.HasLexicalDeclaration(name) is true, throw a SyntaxError exception.\n        if env.has_lex_binding(&name) {\n            return Err(\"duplicate lexical declaration\");\n        }\n    }\n\n    // 13. Let lexDeclarations be the LexicallyScopedDeclarations of script.\n    // 14. Let privateEnv be null.\n    // 15. For each element d of lexDeclarations, do\n    for statement in &**script.statements() {\n        // a. NOTE: Lexically declared names are only instantiated here but not initialized.\n        // b. For each element dn of the BoundNames of d, do\n        //     i. If IsConstantDeclaration of d is true, then\n        //         1. Perform ? env.CreateImmutableBinding(dn, true).\n        //     ii. Else,\n        //         1. Perform ? env.CreateMutableBinding(dn, false).\n        if let StatementListItem::Declaration(declaration) = statement {\n            match declaration.as_ref() {\n                Declaration::ClassDeclaration(class) => {\n                    for name in bound_names(class.as_ref()) {\n                        let name = name.to_js_string(interner);\n                        drop(env.create_mutable_binding(name, false));\n                    }\n                }\n                Declaration::Lexical(LexicalDeclaration::Let(declaration)) => {\n                    for name in bound_names(declaration) {\n                        let name = name.to_js_string(interner);\n                        drop(env.create_mutable_binding(name, false));\n                    }\n                }\n                Declaration::Lexical(LexicalDeclaration::Const(declaration)) => {\n                    for name in bound_names(declaration) {\n                        let name = name.to_js_string(interner);\n                        env.create_immutable_binding(name, true);\n                    }\n                }\n                _ => {}\n            }\n        }\n    }\n\n    Ok(())\n}\n\n/// `BlockDeclarationInstantiation ( code, env )`\n///\n/// More information:\n///  - [ECMAScript reference][spec]\n///\n/// [spec]: https://tc39.es/ecma262/#sec-blockdeclarationinstantiation\nfn block_declaration_instantiation<'a, N>(\n    block: &'a N,\n    scope: Scope,\n    interner: &Interner,\n) -> Option<Scope>\nwhere\n    &'a N: Into<NodeRef<'a>>,\n{\n    let scope = Scope::new(scope, false);\n\n    // 1. Let declarations be the LexicallyScopedDeclarations of code.\n    let declarations = lexically_scoped_declarations(block);\n\n    // 2. Let privateEnv be the running execution context's PrivateEnvironment.\n    // Note: Private environments are currently handled differently.\n\n    // 3. For each element d of declarations, do\n    for d in &declarations {\n        // i. If IsConstantDeclaration of d is true, then\n        if let LexicallyScopedDeclaration::LexicalDeclaration(LexicalDeclaration::Const(d)) = d {\n            // a. For each element dn of the BoundNames of d, do\n            for dn in bound_names::<'_, VariableList>(d) {\n                // 1. Perform ! env.CreateImmutableBinding(dn, true).\n                let dn = dn.to_js_string(interner);\n                scope.create_immutable_binding(dn, true);\n            }\n        }\n        // ii. Else,\n        else {\n            // a. For each element dn of the BoundNames of d, do\n            for dn in d.bound_names() {\n                let dn = dn.to_js_string(interner);\n\n                #[cfg(not(feature = \"annex-b\"))]\n                // 1. Perform ! env.CreateMutableBinding(dn, false). NOTE: This step is replaced in section B.3.2.6.\n                drop(scope.create_mutable_binding(dn, false));\n\n                #[cfg(feature = \"annex-b\")]\n                // 1. If ! env.HasBinding(dn) is false, then\n                if !scope.has_binding(&dn) {\n                    // a. Perform  ! env.CreateMutableBinding(dn, false).\n                    drop(scope.create_mutable_binding(dn, false));\n                }\n            }\n        }\n    }\n\n    if scope.num_bindings() > 0 {\n        Some(scope)\n    } else {\n        None\n    }\n}\n\n/// `FunctionDeclarationInstantiation ( func, argumentsList )`\n///\n/// More information:\n///  - [ECMAScript reference][spec]\n///\n/// [spec]: https://tc39.es/ecma262/#sec-functiondeclarationinstantiation\nfn function_declaration_instantiation(\n    body: &FunctionBody,\n    formals: &FormalParameterList,\n    arrow: bool,\n    strict: bool,\n    function_scope: Scope,\n    interner: &Interner,\n) -> FunctionScopes {\n    let mut scopes = FunctionScopes {\n        function_scope,\n        parameters_eval_scope: None,\n        parameters_scope: None,\n        lexical_scope: None,\n        mapped_arguments_object: false,\n        requires_function_scope: false,\n    };\n\n    // 1. Let calleeContext be the running execution context.\n    // 2. Let code be func.[[ECMAScriptCode]].\n    // 3. Let strict be func.[[Strict]].\n    // 4. Let formals be func.[[FormalParameters]].\n\n    // 5. Let parameterNames be the BoundNames of formals.\n    let mut parameter_names = bound_names(formals);\n\n    // 6. If parameterNames has any duplicate entries, let hasDuplicates be true. Otherwise, let hasDuplicates be false.\n    // 7. Let simpleParameterList be IsSimpleParameterList of formals.\n\n    // 8. Let hasParameterExpressions be ContainsExpression of formals.\n    let has_parameter_expressions = formals.has_expressions();\n\n    // 9. Let varNames be the VarDeclaredNames of code.\n    let var_names = var_declared_names(body);\n\n    // 10. Let varDeclarations be the VarScopedDeclarations of code.\n    let var_declarations = var_scoped_declarations(body);\n\n    // 11. Let lexicalNames be the LexicallyDeclaredNames of code.\n    let lexical_names = lexically_declared_names(body);\n\n    // 12. Let functionNames be a new empty List.\n    let mut function_names = Vec::new();\n\n    // 13. Let functionsToInitialize be a new empty List.\n    // let mut functions_to_initialize = Vec::new();\n\n    // 14. For each element d of varDeclarations, in reverse List order, do\n    for declaration in var_declarations.iter().rev() {\n        // a. If d is neither a VariableDeclaration nor a ForBinding nor a BindingIdentifier, then\n        // a.i. Assert: d is either a FunctionDeclaration, a GeneratorDeclaration, an AsyncFunctionDeclaration, or an AsyncGeneratorDeclaration.\n        // a.ii. Let fn be the sole element of the BoundNames of d.\n        let name = match declaration {\n            VarScopedDeclaration::FunctionDeclaration(f) => f.name(),\n            VarScopedDeclaration::GeneratorDeclaration(f) => f.name(),\n            VarScopedDeclaration::AsyncFunctionDeclaration(f) => f.name(),\n            VarScopedDeclaration::AsyncGeneratorDeclaration(f) => f.name(),\n            VarScopedDeclaration::VariableDeclaration(_) => continue,\n        };\n\n        // a.iii. If functionNames does not contain fn, then\n        if !function_names.contains(&name.sym()) {\n            // 1. Insert fn as the first element of functionNames.\n            function_names.push(name.sym());\n        }\n    }\n\n    function_names.reverse();\n\n    // 15. Let argumentsObjectNeeded be true.\n    let mut arguments_object_needed = true;\n\n    let arguments = Sym::ARGUMENTS;\n\n    // 16. If func.[[ThisMode]] is lexical, then\n    // 17. Else if parameterNames contains \"arguments\", then\n    if arrow || parameter_names.contains(&arguments) {\n        // 16.a. NOTE: Arrow functions never have an arguments object.\n        // 16.b. Set argumentsObjectNeeded to false.\n        // 17.a. Set argumentsObjectNeeded to false.\n        arguments_object_needed = false;\n    }\n    // 18. Else if hasParameterExpressions is false, then\n    else if !has_parameter_expressions {\n        //a. If functionNames contains \"arguments\" or lexicalNames contains \"arguments\", then\n        if function_names.contains(&arguments) || lexical_names.contains(&arguments) {\n            // i. Set argumentsObjectNeeded to false.\n            arguments_object_needed = false;\n        }\n    }\n\n    // 19. If strict is true or hasParameterExpressions is false, then\n    let env = if strict || !has_parameter_expressions {\n        // a. NOTE: Only a single Environment Record is needed for the parameters,\n        //    since calls to eval in strict mode code cannot create new bindings which are visible outside of the eval.\n        // b. Let env be the LexicalEnvironment of calleeContext.\n        scopes.function_scope.clone()\n    }\n    // 20. Else,\n    else {\n        // a. NOTE: A separate Environment Record is needed to ensure that bindings created by\n        //    direct eval calls in the formal parameter list are outside the environment where parameters are declared.\n        // b. Let calleeEnv be the LexicalEnvironment of calleeContext.\n        // c. Let env be NewDeclarativeEnvironment(calleeEnv).\n        // d. Assert: The VariableEnvironment of calleeContext is calleeEnv.\n        // e. Set the LexicalEnvironment of calleeContext to env.\n        let scope = Scope::new(scopes.function_scope.clone(), false);\n        scopes.parameters_eval_scope = Some(scope.clone());\n        scope\n    };\n\n    // 22. If argumentsObjectNeeded is true, then\n    //\n    // NOTE(HalidOdat): Has been moved up, so \"arguments\" gets registered as\n    //     the first binding in the environment with index 0.\n    if arguments_object_needed {\n        let arguments = arguments.to_js_string(interner);\n\n        // c. If strict is true, then\n        if strict {\n            // i. Perform ! env.CreateImmutableBinding(\"arguments\", false).\n            // ii. NOTE: In strict mode code early errors prevent attempting to assign\n            //           to this binding, so its mutability is not observable.\n            env.create_immutable_binding(arguments.clone(), false);\n        }\n        // d. Else,\n        else {\n            // i. Perform ! env.CreateMutableBinding(\"arguments\", false).\n            drop(env.create_mutable_binding(arguments.clone(), false));\n        }\n    }\n\n    // 21. For each String paramName of parameterNames, do\n    for param_name in &parameter_names {\n        let param_name = param_name.to_js_string(interner);\n\n        // a. Let alreadyDeclared be ! env.HasBinding(paramName).\n        let already_declared = env.has_binding(&param_name);\n\n        // b. NOTE: Early errors ensure that duplicate parameter names can only occur in non-strict\n        //    functions that do not have parameter default values or rest parameters.\n\n        // c. If alreadyDeclared is false, then\n        if !already_declared {\n            // i. Perform ! env.CreateMutableBinding(paramName, false).\n            drop(env.create_mutable_binding(param_name.clone(), false));\n\n            // Note: In this case the function contains a mapped arguments object.\n            // Because we do not track (yet) if the mapped arguments object escapes the function,\n            // we have to assume that the binding might escape trough the arguments object.\n            if arguments_object_needed && !strict && formals.is_simple() {\n                scopes.mapped_arguments_object = true;\n            }\n\n            // Note: These steps are not necessary in our implementation.\n            // ii. If hasDuplicates is true, then\n            //     1. Perform ! env.InitializeBinding(paramName, undefined).\n        }\n    }\n\n    // 22. If argumentsObjectNeeded is true, then\n    if arguments_object_needed {\n        // MOVED: a-e.\n        //\n        // NOTE(HalidOdat): Has been moved up, see comment above.\n\n        // f. Let parameterBindings be the list-concatenation of parameterNames and « \"arguments\" ».\n        parameter_names.push(arguments);\n    }\n\n    // 23. Else,\n    //     a. Let parameterBindings be parameterNames.\n    let parameter_bindings = parameter_names.clone();\n\n    // 27. If hasParameterExpressions is false, then\n    // 28. Else,\n    #[allow(unused_variables, unused_mut)]\n    let (mut instantiated_var_names, mut var_env) = if has_parameter_expressions {\n        // a. NOTE: A separate Environment Record is needed to ensure that closures created by\n        //          expressions in the formal parameter list do not have\n        //          visibility of declarations in the function body.\n        // b. Let varEnv be NewDeclarativeEnvironment(env).\n        let var_env = Scope::new(env.clone(), false);\n        scopes.parameters_scope = Some(var_env.clone());\n\n        // c. Set the VariableEnvironment of calleeContext to varEnv.\n\n        // d. Let instantiatedVarNames be a new empty List.\n        let mut instantiated_var_names = Vec::new();\n\n        // e. For each element n of varNames, do\n        for n in var_names {\n            // i. If instantiatedVarNames does not contain n, then\n            if !instantiated_var_names.contains(&n) {\n                // 1. Append n to instantiatedVarNames.\n                instantiated_var_names.push(n);\n\n                let n_string = n.to_js_string(interner);\n\n                // 2. Perform ! varEnv.CreateMutableBinding(n, false).\n                drop(var_env.create_mutable_binding(n_string, false));\n            }\n        }\n\n        (instantiated_var_names, var_env)\n    } else {\n        // a. NOTE: Only a single Environment Record is needed for the parameters and top-level vars.\n        // b. Let instantiatedVarNames be a copy of the List parameterBindings.\n        let mut instantiated_var_names = parameter_bindings;\n\n        // c. For each element n of varNames, do\n        for n in var_names {\n            // i. If instantiatedVarNames does not contain n, then\n            if !instantiated_var_names.contains(&n) {\n                // 1. Append n to instantiatedVarNames.\n                instantiated_var_names.push(n);\n\n                let n = n.to_js_string(interner);\n\n                // 2. Perform ! env.CreateMutableBinding(n, false).\n                // 3. Perform ! env.InitializeBinding(n, undefined).\n                drop(env.create_mutable_binding(n, true));\n            }\n        }\n\n        // d. Let varEnv be env.\n        (instantiated_var_names, env)\n    };\n\n    // 29. NOTE: Annex B.3.2.1 adds additional steps at this point.\n    // 29. If strict is false, then\n    #[cfg(feature = \"annex-b\")]\n    if !strict {\n        // a. For each FunctionDeclaration f that is directly contained in the StatementList\n        //    of a Block, CaseClause, or DefaultClause, do\n        for f in annex_b_function_declarations_names(body) {\n            // i. Let F be StringValue of the BindingIdentifier of f.\n            // ii. If replacing the FunctionDeclaration f with a VariableStatement that has F\n            //     as a BindingIdentifier would not produce any Early Errors\n            //     for func and parameterNames does not contain F, then\n            if !lexical_names.contains(&f) && !parameter_names.contains(&f) {\n                // 1. NOTE: A var binding for F is only instantiated here if it is neither a\n                //    VarDeclaredName, the name of a formal parameter, or another FunctionDeclaration.\n\n                // 2. If initializedBindings does not contain F and F is not \"arguments\", then\n                if !instantiated_var_names.contains(&f) && f != arguments {\n                    let f_string = f.to_js_string(interner);\n\n                    // a. Perform ! varEnv.CreateMutableBinding(F, false).\n                    // b. Perform ! varEnv.InitializeBinding(F, undefined).\n                    drop(var_env.create_mutable_binding(f_string, false));\n\n                    // c. Append F to instantiatedVarNames.\n                    instantiated_var_names.push(f);\n                }\n            }\n        }\n    }\n\n    // 30. If strict is false, then\n    // 31. Else,\n    let lex_env = if strict {\n        // a. Let lexEnv be varEnv.\n        var_env\n    } else {\n        // a. Let lexEnv be NewDeclarativeEnvironment(varEnv).\n        // b. NOTE: Non-strict functions use a separate Environment Record for top-level lexical\n        //    declarations so that a direct eval can determine whether any var scoped declarations\n        //    introduced by the eval code conflict with pre-existing top-level lexically scoped declarations.\n        //    This is not needed for strict functions because a strict direct eval always\n        //    places all declarations into a new Environment Record.\n        let lex_env = Scope::new(var_env, false);\n        scopes.lexical_scope = Some(lex_env.clone());\n        lex_env\n    };\n\n    // 32. Set the LexicalEnvironment of calleeContext to lexEnv.\n    // 33. Let lexDeclarations be the LexicallyScopedDeclarations of code.\n    // 34. For each element d of lexDeclarations, do\n    //     a. NOTE: A lexically declared name cannot be the same as a function/generator declaration,\n    //        formal parameter, or a var name. Lexically declared names are only instantiated here but not initialized.\n    //     b. For each element dn of the BoundNames of d, do\n    //         i. If IsConstantDeclaration of d is true, then\n    //             1. Perform ! lexEnv.CreateImmutableBinding(dn, true).\n    //         ii. Else,\n    //             1. Perform ! lexEnv.CreateMutableBinding(dn, false).\n    for statement in body.statements() {\n        if let StatementListItem::Declaration(declaration) = statement {\n            match declaration.as_ref() {\n                Declaration::ClassDeclaration(class) => {\n                    for name in bound_names(class.as_ref()) {\n                        let name = name.to_js_string(interner);\n                        drop(lex_env.create_mutable_binding(name, false));\n                    }\n                }\n                Declaration::Lexical(LexicalDeclaration::Let(declaration)) => {\n                    for name in bound_names(declaration) {\n                        let name = name.to_js_string(interner);\n                        drop(lex_env.create_mutable_binding(name, false));\n                    }\n                }\n                Declaration::Lexical(LexicalDeclaration::Const(declaration)) => {\n                    for name in bound_names(declaration) {\n                        let name = name.to_js_string(interner);\n                        lex_env.create_immutable_binding(name, true);\n                    }\n                }\n                _ => {}\n            }\n        }\n    }\n\n    // 35. Let privateEnv be the PrivateEnvironment of calleeContext.\n    // 36. For each Parse Node f of functionsToInitialize, do\n\n    if let Some(lexical_scope) = &scopes.lexical_scope\n        && lexical_scope.num_bindings() == 0\n    {\n        scopes.lexical_scope = None;\n    }\n\n    // 37. Return unused.\n    scopes\n}\n\n/// Abstract operation [`InitializeEnvironment ( )`][spec].\n///\n/// [spec]: https://tc39.es/ecma262/#sec-source-text-module-record-initialize-environment\nfn module_instantiation(module: &Module, env: &Scope, interner: &Interner) {\n    for entry in module.items().import_entries() {\n        let local_name = entry.local_name().to_js_string(interner);\n        env.create_immutable_binding(local_name, true);\n    }\n    let var_declarations = var_scoped_declarations(module);\n    let mut declared_var_names = Vec::new();\n    for var in var_declarations {\n        for name in var.bound_names() {\n            let name = name.to_js_string(interner);\n            if !declared_var_names.contains(&name) {\n                drop(env.create_mutable_binding(name.clone(), false));\n                declared_var_names.push(name);\n            }\n        }\n    }\n\n    let lex_declarations = lexically_scoped_declarations(module);\n    for declaration in lex_declarations {\n        match declaration {\n            LexicallyScopedDeclaration::FunctionDeclaration(f) => {\n                let name = bound_names(f)[0].to_js_string(interner);\n                drop(env.create_mutable_binding(name, false));\n            }\n            LexicallyScopedDeclaration::GeneratorDeclaration(g) => {\n                let name = bound_names(g)[0].to_js_string(interner);\n                drop(env.create_mutable_binding(name, false));\n            }\n            LexicallyScopedDeclaration::AsyncFunctionDeclaration(af) => {\n                let name = bound_names(af)[0].to_js_string(interner);\n                drop(env.create_mutable_binding(name, false));\n            }\n            LexicallyScopedDeclaration::AsyncGeneratorDeclaration(ag) => {\n                let name = bound_names(ag)[0].to_js_string(interner);\n                drop(env.create_mutable_binding(name, false));\n            }\n            LexicallyScopedDeclaration::ClassDeclaration(class) => {\n                for name in bound_names(class) {\n                    let name = name.to_js_string(interner);\n                    drop(env.create_mutable_binding(name, false));\n                }\n            }\n            LexicallyScopedDeclaration::LexicalDeclaration(LexicalDeclaration::Const(c)) => {\n                for name in bound_names(c) {\n                    let name = name.to_js_string(interner);\n                    env.create_immutable_binding(name, true);\n                }\n            }\n            LexicallyScopedDeclaration::LexicalDeclaration(LexicalDeclaration::Let(l)) => {\n                for name in bound_names(l) {\n                    let name = name.to_js_string(interner);\n                    drop(env.create_mutable_binding(name, false));\n                }\n            }\n            LexicallyScopedDeclaration::LexicalDeclaration(LexicalDeclaration::Using(u)) => {\n                for name in bound_names(u) {\n                    let name = name.to_js_string(interner);\n                    drop(env.create_mutable_binding(name, false));\n                }\n            }\n            LexicallyScopedDeclaration::LexicalDeclaration(LexicalDeclaration::AwaitUsing(au)) => {\n                for name in bound_names(au) {\n                    let name = name.to_js_string(interner);\n                    drop(env.create_mutable_binding(name, false));\n                }\n            }\n            LexicallyScopedDeclaration::AssignmentExpression(expr) => {\n                for name in bound_names(expr) {\n                    let name = name.to_js_string(interner);\n                    drop(env.create_mutable_binding(name, false));\n                }\n            }\n        }\n    }\n}\n\n/// This struct isused to store bindings created during the declaration of an eval ast node.\n#[derive(Debug, Default)]\npub struct EvalDeclarationBindings {\n    /// New annexb function names created during the declaration of an eval ast node.\n    pub new_annex_b_function_names: Vec<IdentifierReference>,\n\n    /// New function names created during the declaration of an eval ast node.\n    pub new_function_names: FxHashMap<Identifier, (IdentifierReference, bool)>,\n\n    /// New variable names created during the declaration of an eval ast node.\n    pub new_var_names: Vec<IdentifierReference>,\n}\n\n/// `EvalDeclarationInstantiation ( body, varEnv, lexEnv, privateEnv, strict )`\n///\n/// More information:\n///  - [ECMAScript reference][spec]\n///\n/// [spec]: https://tc39.es/ecma262/#sec-evaldeclarationinstantiation\n///\n/// # Errors\n///\n/// * Returns a syntax error if a duplicate lexical declaration is found.\n/// * Returns a syntax error if a variable declaration in an eval function already exists as a lexical variable.\n#[allow(clippy::missing_panics_doc)]\npub(crate) fn eval_declaration_instantiation_scope(\n    body: &Script,\n    strict: bool,\n    var_env: &Scope,\n    lex_env: &Scope,\n    #[allow(unused_variables)] annex_b_function_names: &[Sym],\n    interner: &Interner,\n) -> Result<EvalDeclarationBindings, String> {\n    let mut result = EvalDeclarationBindings::default();\n\n    // 2. Let varDeclarations be the VarScopedDeclarations of body.\n    let var_declarations = var_scoped_declarations(body);\n\n    // 3. If strict is false, then\n    if !strict {\n        // 1. Let varNames be the VarDeclaredNames of body.\n        let var_names = var_declared_names(body);\n\n        // a. If varEnv is a Global Environment Record, then\n        if var_env.is_global() {\n            // i. For each element name of varNames, do\n            for name in &var_names {\n                let name = name.to_js_string(interner);\n\n                // 1. If varEnv.HasLexicalDeclaration(name) is true, throw a SyntaxError exception.\n                // 2. NOTE: eval will not create a global var declaration that would be shadowed by a global lexical declaration.\n                if var_env.has_lex_binding(&name) {\n                    return Err(format!(\n                        \"duplicate lexical declaration {}\",\n                        name.to_std_string_escaped()\n                    ));\n                }\n            }\n        }\n\n        // b. Let thisEnv be lexEnv.\n        let mut this_env = lex_env;\n\n        // c. Assert: The following loop will terminate.\n        // d. Repeat, while thisEnv is not varEnv,\n        while this_env.scope_index() != var_env.scope_index() {\n            // i. If thisEnv is not an Object Environment Record, then\n            // 1. NOTE: The environment of with statements cannot contain any lexical\n            //    declaration so it doesn't need to be checked for var/let hoisting conflicts.\n            // 2. For each element name of varNames, do\n            for name in &var_names {\n                let name = interner.resolve_expect(*name).utf16().into();\n\n                // a. If ! thisEnv.HasBinding(name) is true, then\n                if this_env.has_binding(&name) {\n                    // i. Throw a SyntaxError exception.\n                    // ii. NOTE: Annex B.3.4 defines alternate semantics for the above step.\n                    return Err(format!(\n                        \"variable declaration {} in eval function already exists as a lexical variable\",\n                        name.to_std_string_escaped()\n                    ));\n                }\n                // b. NOTE: A direct eval will not hoist var declaration over a like-named lexical declaration.\n            }\n\n            // ii. Set thisEnv to thisEnv.[[OuterEnv]].\n            if let Some(outer) = this_env.outer() {\n                this_env = outer;\n            } else {\n                break;\n            }\n        }\n    }\n\n    // NOTE: These steps depend on the current environment state are done before bytecode compilation,\n    //       in `eval_declaration_instantiation_context`.\n    //\n    // SKIP: 4. Let privateIdentifiers be a new empty List.\n    // SKIP: 5. Let pointer be privateEnv.\n    // SKIP: 6. Repeat, while pointer is not null,\n    //           a. For each Private Name binding of pointer.[[Names]], do\n    //               i. If privateIdentifiers does not contain binding.[[Description]],\n    //                  append binding.[[Description]] to privateIdentifiers.\n    //           b. Set pointer to pointer.[[OuterPrivateEnvironment]].\n    // SKIP: 7. If AllPrivateIdentifiersValid of body with argument privateIdentifiers is false, throw a SyntaxError exception.\n\n    // 8. Let functionsToInitialize be a new empty List.\n    let mut functions_to_initialize = Vec::new();\n\n    // 9. Let declaredFunctionNames be a new empty List.\n    let mut declared_function_names = Vec::new();\n\n    // 10. For each element d of varDeclarations, in reverse List order, do\n    for declaration in var_declarations.iter().rev() {\n        // a. If d is not either a VariableDeclaration, a ForBinding, or a BindingIdentifier, then\n        // a.i. Assert: d is either a FunctionDeclaration, a GeneratorDeclaration, an AsyncFunctionDeclaration, or an AsyncGeneratorDeclaration.\n        // a.ii. NOTE: If there are multiple function declarations for the same name, the last declaration is used.\n        // a.iii. Let fn be the sole element of the BoundNames of d.\n        let name = match &declaration {\n            VarScopedDeclaration::FunctionDeclaration(f) => f.name(),\n            VarScopedDeclaration::GeneratorDeclaration(f) => f.name(),\n            VarScopedDeclaration::AsyncFunctionDeclaration(f) => f.name(),\n            VarScopedDeclaration::AsyncGeneratorDeclaration(f) => f.name(),\n            VarScopedDeclaration::VariableDeclaration(_) => continue,\n        };\n        // a.iv. If declaredFunctionNames does not contain fn, then\n        if !declared_function_names.contains(&name.sym()) {\n            // 1. If varEnv is a Global Environment Record, then\n            // 2. Append fn to declaredFunctionNames.\n            declared_function_names.push(name.sym());\n\n            // 3. Insert d as the first element of functionsToInitialize.\n            functions_to_initialize.push(declaration.clone());\n        }\n    }\n\n    functions_to_initialize.reverse();\n\n    // 11. NOTE: Annex B.3.2.3 adds additional steps at this point.\n    // 11. If strict is false, then\n    #[cfg(feature = \"annex-b\")]\n    if !strict {\n        // NOTE: This diviates from the specification, we split the first part of defining the annex-b names\n        //       in `eval_declaration_instantiation_context`, because it depends on the context.\n        if !var_env.is_global() {\n            for name in annex_b_function_names {\n                let f = name.to_js_string(interner);\n                // i. Let bindingExists be ! varEnv.HasBinding(F).\n                // ii. If bindingExists is false, then\n                if !var_env.has_binding(&f) {\n                    // i. Perform ! varEnv.CreateMutableBinding(F, true).\n                    // ii. Perform ! varEnv.InitializeBinding(F, undefined).\n                    let binding = var_env.create_mutable_binding(f, true);\n                    result\n                        .new_annex_b_function_names\n                        .push(IdentifierReference::new(\n                            binding,\n                            !var_env.is_function(),\n                            true,\n                        ));\n                }\n            }\n        }\n    }\n\n    // 12. Let declaredVarNames be a new empty List.\n    let mut declared_var_names = Vec::new();\n\n    // 13. For each element d of varDeclarations, do\n    for declaration in var_declarations {\n        // a. If d is either a VariableDeclaration, a ForBinding, or a BindingIdentifier, then\n        let VarScopedDeclaration::VariableDeclaration(declaration) = declaration else {\n            continue;\n        };\n\n        // a.i. For each String vn of the BoundNames of d, do\n        for name in bound_names(&declaration) {\n            // 1. If declaredFunctionNames does not contain vn, then\n            if !declared_function_names.contains(&name) {\n                // a. If varEnv is a Global Environment Record, then\n                // b. If declaredVarNames does not contain vn, then\n                if !declared_var_names.contains(&name) {\n                    // i. Append vn to declaredVarNames.\n                    declared_var_names.push(name);\n                }\n            }\n        }\n    }\n\n    // 14. NOTE: No abnormal terminations occur after this algorithm step unless varEnv is a\n    //           Global Environment Record and the global object is a Proxy exotic object.\n\n    // 15. Let lexDeclarations be the LexicallyScopedDeclarations of body.\n    // 16. For each element d of lexDeclarations, do\n    for statement in &**body.statements() {\n        // a. NOTE: Lexically declared names are only instantiated here but not initialized.\n        // b. For each element dn of the BoundNames of d, do\n        //     i. If IsConstantDeclaration of d is true, then\n        //         1. Perform ? lexEnv.CreateImmutableBinding(dn, true).\n        //     ii. Else,\n        //         1. Perform ? lexEnv.CreateMutableBinding(dn, false).\n        if let StatementListItem::Declaration(declaration) = statement {\n            match declaration.as_ref() {\n                Declaration::ClassDeclaration(class) => {\n                    for name in bound_names(class.as_ref()) {\n                        let name = name.to_js_string(interner);\n                        drop(lex_env.create_mutable_binding(name, false));\n                    }\n                }\n                Declaration::Lexical(LexicalDeclaration::Let(declaration)) => {\n                    for name in bound_names(declaration) {\n                        let name = name.to_js_string(interner);\n                        drop(lex_env.create_mutable_binding(name, false));\n                    }\n                }\n                Declaration::Lexical(LexicalDeclaration::Const(declaration)) => {\n                    for name in bound_names(declaration) {\n                        let name = name.to_js_string(interner);\n                        lex_env.create_immutable_binding(name, true);\n                    }\n                }\n                _ => {}\n            }\n        }\n    }\n\n    // 17. For each Parse Node f of functionsToInitialize, do\n    for function in functions_to_initialize {\n        // a. Let fn be the sole element of the BoundNames of f.\n        let name = match &function {\n            VarScopedDeclaration::FunctionDeclaration(f) => f.name(),\n            VarScopedDeclaration::GeneratorDeclaration(f) => f.name(),\n            VarScopedDeclaration::AsyncFunctionDeclaration(f) => f.name(),\n            VarScopedDeclaration::AsyncGeneratorDeclaration(f) => f.name(),\n            VarScopedDeclaration::VariableDeclaration(_) => {\n                continue;\n            }\n        };\n\n        // c. If varEnv is a Global Environment Record, then\n        // d. Else,\n        if !var_env.is_global() {\n            // b. Let fo be InstantiateFunctionObject of f with arguments lexEnv and privateEnv.\n            let n = name.to_js_string(interner);\n\n            // i. Let bindingExists be ! varEnv.HasBinding(fn).\n            let binding_exists = var_env.has_binding(&n);\n\n            // ii. If bindingExists is false, then\n            // iii. Else,\n            if binding_exists {\n                // 1. Perform ! varEnv.SetMutableBinding(fn, fo, false).\n                let binding = var_env.set_mutable_binding(n).expect(\"must not fail\");\n                result.new_function_names.insert(\n                    name,\n                    (\n                        IdentifierReference::new(binding.locator(), !var_env.is_function(), true),\n                        true,\n                    ),\n                );\n            } else {\n                // 1. NOTE: The following invocation cannot return an abrupt completion because of the validation preceding step 14.\n                // 2. Perform ! varEnv.CreateMutableBinding(fn, true).\n                // 3. Perform ! varEnv.InitializeBinding(fn, fo).\n                let binding = var_env.create_mutable_binding(n, !strict);\n                result.new_function_names.insert(\n                    name,\n                    (\n                        IdentifierReference::new(binding, !var_env.is_function(), true),\n                        false,\n                    ),\n                );\n            }\n        }\n    }\n\n    // 18. For each String vn of declaredVarNames, do\n    for name in declared_var_names {\n        // a. If varEnv is a Global Environment Record, then\n        // b. Else,\n        if !var_env.is_global() {\n            let name = name.to_js_string(interner);\n\n            // i. Let bindingExists be ! varEnv.HasBinding(vn).\n            let binding_exists = var_env.has_binding(&name);\n\n            // ii. If bindingExists is false, then\n            if !binding_exists {\n                // 1. NOTE: The following invocation cannot return an abrupt completion because of the validation preceding step 14.\n                // 2. Perform ! varEnv.CreateMutableBinding(vn, true).\n                // 3. Perform ! varEnv.InitializeBinding(vn, undefined).\n                let binding = var_env.create_mutable_binding(name, true);\n                result.new_var_names.push(IdentifierReference::new(\n                    binding,\n                    !var_env.is_function(),\n                    true,\n                ));\n            }\n        }\n    }\n\n    // 19. Return unused.\n    Ok(result)\n}\n"
  },
  {
    "path": "core/ast/src/source.rs",
    "content": "use std::ops::ControlFlow;\n\nuse boa_interner::{Interner, Sym, ToIndentedString};\n\nuse crate::{\n    ModuleItemList, StatementList,\n    scope::Scope,\n    scope_analyzer::{\n        EvalDeclarationBindings, analyze_binding_escapes, collect_bindings,\n        eval_declaration_instantiation_scope, optimize_scope_indices,\n    },\n    visitor::{VisitWith, Visitor, VisitorMut},\n};\n\n/// A Script source.\n///\n/// More information:\n///  - [ECMAScript reference][spec]\n///\n/// [spec]: https://tc39.es/ecma262/#sec-scripts\n#[cfg_attr(feature = \"serde\", derive(serde::Serialize, serde::Deserialize))]\n#[derive(Clone, Debug, Default)]\npub struct Script {\n    statements: StatementList,\n}\n\nimpl Script {\n    /// Creates a new `ScriptNode`.\n    #[must_use]\n    pub const fn new(statements: StatementList) -> Self {\n        Self { statements }\n    }\n\n    /// Gets the list of statements of this `ScriptNode`.\n    #[must_use]\n    pub const fn statements(&self) -> &StatementList {\n        &self.statements\n    }\n\n    /// Gets a mutable reference to the list of statements of this `ScriptNode`.\n    pub fn statements_mut(&mut self) -> &mut StatementList {\n        &mut self.statements\n    }\n\n    /// Gets the strict mode.\n    #[inline]\n    #[must_use]\n    pub const fn strict(&self) -> bool {\n        self.statements.strict()\n    }\n\n    /// Analyze the scope of the script.\n    ///\n    /// # Errors\n    /// Any scope or binding errors that happened during the analysis.\n    pub fn analyze_scope(\n        &mut self,\n        scope: &Scope,\n        interner: &Interner,\n    ) -> Result<(), &'static str> {\n        collect_bindings(self, self.strict(), false, scope, interner)?;\n        analyze_binding_escapes(self, false, scope.clone(), interner)?;\n        optimize_scope_indices(self, scope);\n        Ok(())\n    }\n\n    /// Analyze the scope of the script in eval mode.\n    ///\n    /// # Errors\n    ///\n    /// Returns an error if the scope analysis fails with a syntax error.\n    pub fn analyze_scope_eval(\n        &mut self,\n        strict: bool,\n        variable_scope: &Scope,\n        lexical_scope: &Scope,\n        annex_b_function_names: &[Sym],\n        interner: &Interner,\n    ) -> Result<EvalDeclarationBindings, String> {\n        let bindings = eval_declaration_instantiation_scope(\n            self,\n            strict,\n            variable_scope,\n            lexical_scope,\n            annex_b_function_names,\n            interner,\n        )?;\n\n        if let Err(reason) = collect_bindings(self, strict, true, lexical_scope, interner) {\n            return Err(format!(\"Failed to analyze scope: {reason}\"));\n        }\n        if let Err(reason) = analyze_binding_escapes(self, true, lexical_scope.clone(), interner) {\n            return Err(format!(\"Failed to analyze scope: {reason}\"));\n        }\n\n        variable_scope.escape_all_bindings();\n        lexical_scope.escape_all_bindings();\n        variable_scope.reorder_binding_indices();\n        lexical_scope.reorder_binding_indices();\n        optimize_scope_indices(self, lexical_scope);\n\n        Ok(bindings)\n    }\n}\n\nimpl VisitWith for Script {\n    fn visit_with<'a, V>(&'a self, visitor: &mut V) -> ControlFlow<V::BreakTy>\n    where\n        V: Visitor<'a>,\n    {\n        self.statements.visit_with(visitor)\n    }\n\n    fn visit_with_mut<'a, V>(&'a mut self, visitor: &mut V) -> ControlFlow<V::BreakTy>\n    where\n        V: VisitorMut<'a>,\n    {\n        self.statements.visit_with_mut(visitor)\n    }\n}\n\nimpl ToIndentedString for Script {\n    fn to_indented_string(&self, interner: &Interner, indentation: usize) -> String {\n        self.statements.to_indented_string(interner, indentation)\n    }\n}\n\nimpl PartialEq for Script {\n    fn eq(&self, other: &Self) -> bool {\n        self.statements == other.statements\n    }\n}\n\n#[cfg(feature = \"arbitrary\")]\nimpl<'a> arbitrary::Arbitrary<'a> for Script {\n    fn arbitrary(u: &mut arbitrary::Unstructured<'a>) -> arbitrary::Result<Self> {\n        let statements = StatementList::arbitrary(u)?;\n        Ok(Self { statements })\n    }\n}\n\n/// A Module source.\n///\n/// More information:\n///  - [ECMAScript reference][spec]\n///\n/// [spec]: https://tc39.es/ecma262/#sec-modules\n#[cfg_attr(feature = \"serde\", derive(serde::Serialize, serde::Deserialize))]\n#[derive(Clone, Debug, Default, PartialEq)]\npub struct Module {\n    pub(crate) items: ModuleItemList,\n\n    #[cfg_attr(feature = \"serde\", serde(skip))]\n    pub(crate) scope: Scope,\n}\n\nimpl Module {\n    /// Creates a new `ModuleNode`.\n    #[must_use]\n    pub fn new(items: ModuleItemList) -> Self {\n        Self {\n            items,\n            scope: Scope::default(),\n        }\n    }\n\n    /// Gets the list of items of this `ModuleNode`.\n    #[must_use]\n    pub const fn items(&self) -> &ModuleItemList {\n        &self.items\n    }\n\n    /// Gets the scope of this `ModuleNode`.\n    #[inline]\n    #[must_use]\n    pub const fn scope(&self) -> &Scope {\n        &self.scope\n    }\n\n    /// Analyze the scope of the module.\n    ///\n    /// # Errors\n    /// Any scope or binding errors that happened during the analysis.\n    pub fn analyze_scope(\n        &mut self,\n        scope: &Scope,\n        interner: &Interner,\n    ) -> Result<(), &'static str> {\n        collect_bindings(self, true, false, scope, interner)?;\n        analyze_binding_escapes(self, false, scope.clone(), interner)?;\n        optimize_scope_indices(self, &self.scope.clone());\n\n        Ok(())\n    }\n}\n\nimpl VisitWith for Module {\n    fn visit_with<'a, V>(&'a self, visitor: &mut V) -> ControlFlow<V::BreakTy>\n    where\n        V: Visitor<'a>,\n    {\n        self.items.visit_with(visitor)\n    }\n\n    fn visit_with_mut<'a, V>(&'a mut self, visitor: &mut V) -> ControlFlow<V::BreakTy>\n    where\n        V: VisitorMut<'a>,\n    {\n        self.items.visit_with_mut(visitor)\n    }\n}\n"
  },
  {
    "path": "core/ast/src/source_text.rs",
    "content": "use crate::{LinearPosition, LinearSpan};\n\n/// Source text.\n#[cfg_attr(feature = \"serde\", derive(serde::Serialize, serde::Deserialize))]\n#[derive(Clone, Debug)]\npub struct SourceText {\n    source_text: Vec<u16>,\n}\n\nimpl SourceText {\n    /// Constructs a new, empty `SourceText` with at least the specified capacity.\n    #[must_use]\n    pub fn with_capacity(capacity: usize) -> Self {\n        Self {\n            source_text: Vec::with_capacity(capacity),\n        }\n    }\n\n    /// Get current `LinearPosition`.\n    #[must_use]\n    pub fn cur_linear_position(&self) -> LinearPosition {\n        LinearPosition::new(self.source_text.len())\n    }\n\n    /// Get code points from `pos` to the current end.\n    #[must_use]\n    pub fn get_code_points_from_pos(&self, pos: LinearPosition) -> &[u16] {\n        &self.source_text[pos.pos()..]\n    }\n\n    /// Get code points within `span`.\n    #[must_use]\n    pub fn get_code_points_from_span(&self, span: LinearSpan) -> &[u16] {\n        &self.source_text[span.start().pos()..span.end().pos()]\n    }\n\n    /// Remove last code point.\n    #[inline]\n    pub fn remove_last_code_point(&mut self) {\n        self.source_text.pop();\n    }\n\n    /// Collect code point.\n    ///\n    /// # Panics\n    ///\n    /// On invalid code point.\n    #[inline]\n    pub fn collect_code_point(&mut self, cp: u32) {\n        if let Ok(cu) = cp.try_into() {\n            self.push(cu);\n            return;\n        }\n        let cp = cp - 0x10000;\n        let cu1 = (cp / 0x400 + 0xD800)\n            .try_into()\n            .expect(\"Invalid code point\");\n        let cu2 = (cp % 0x400 + 0xDC00)\n            .try_into()\n            .expect(\"Invalid code point\");\n        self.push(cu1);\n        self.push(cu2);\n    }\n\n    #[inline]\n    fn push(&mut self, cp: u16) {\n        self.source_text.push(cp);\n    }\n}\n\nconst DEFAULT_CAPACITY: usize = 4 * 1024;\n\nimpl Default for SourceText {\n    fn default() -> Self {\n        Self::with_capacity(DEFAULT_CAPACITY)\n    }\n}\n"
  },
  {
    "path": "core/ast/src/statement/block.rs",
    "content": "//! Block AST node.\n\nuse crate::{\n    Statement, StatementList,\n    operations::{ContainsSymbol, contains},\n    scope::Scope,\n    visitor::{VisitWith, Visitor, VisitorMut},\n};\nuse boa_interner::{Interner, ToIndentedString};\nuse core::ops::ControlFlow;\n\n/// A `block` statement (or compound statement in other languages) is used to group zero or\n/// more statements.\n///\n/// The block statement is often called compound statement in other languages.\n/// It allows you to use multiple statements where ECMAScript expects only one statement.\n/// Combining statements into blocks is a common practice in ECMAScript. The opposite behavior\n/// is possible using an empty statement, where you provide no statement, although one is\n/// required.\n///\n/// More information:\n///  - [ECMAScript reference][spec]\n///  - [MDN documentation][mdn]\n///\n/// [spec]: https://tc39.es/ecma262/#prod-BlockStatement\n/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/block\n#[cfg_attr(feature = \"serde\", derive(serde::Serialize, serde::Deserialize))]\n#[cfg_attr(feature = \"arbitrary\", derive(arbitrary::Arbitrary))]\n#[derive(Clone, Debug, PartialEq, Default)]\npub struct Block {\n    #[cfg_attr(feature = \"serde\", serde(flatten))]\n    pub(crate) statements: StatementList,\n    pub(crate) contains_direct_eval: bool,\n\n    #[cfg_attr(feature = \"serde\", serde(skip))]\n    pub(crate) scope: Option<Scope>,\n}\n\nimpl Block {\n    /// Gets the list of statements and declarations in this block.\n    #[inline]\n    #[must_use]\n    pub const fn statement_list(&self) -> &StatementList {\n        &self.statements\n    }\n\n    /// Gets the scope of the block.\n    #[inline]\n    #[must_use]\n    pub const fn scope(&self) -> Option<&Scope> {\n        self.scope.as_ref()\n    }\n}\n\nimpl<T> From<T> for Block\nwhere\n    T: Into<StatementList>,\n{\n    fn from(list: T) -> Self {\n        let statements = list.into();\n        let contains_direct_eval = contains(&statements, ContainsSymbol::DirectEval);\n        Self {\n            statements,\n            scope: None,\n            contains_direct_eval,\n        }\n    }\n}\n\nimpl ToIndentedString for Block {\n    fn to_indented_string(&self, interner: &Interner, indentation: usize) -> String {\n        format!(\n            \"{{\\n{}{}}}\",\n            self.statements\n                .to_indented_string(interner, indentation + 1),\n            \"    \".repeat(indentation)\n        )\n    }\n}\n\nimpl From<Block> for Statement {\n    #[inline]\n    fn from(block: Block) -> Self {\n        Self::Block(block)\n    }\n}\n\nimpl VisitWith for Block {\n    fn visit_with<'a, V>(&'a self, visitor: &mut V) -> ControlFlow<V::BreakTy>\n    where\n        V: Visitor<'a>,\n    {\n        visitor.visit_statement_list(&self.statements)\n    }\n\n    fn visit_with_mut<'a, V>(&'a mut self, visitor: &mut V) -> ControlFlow<V::BreakTy>\n    where\n        V: VisitorMut<'a>,\n    {\n        visitor.visit_statement_list_mut(&mut self.statements)\n    }\n}\n"
  },
  {
    "path": "core/ast/src/statement/if.rs",
    "content": "//! If statement\n\nuse crate::{\n    expression::Expression,\n    statement::Statement,\n    visitor::{VisitWith, Visitor, VisitorMut},\n};\nuse boa_interner::{Interner, ToIndentedString, ToInternedString};\nuse core::{fmt::Write as _, ops::ControlFlow};\n\n/// The `if` statement executes a statement if a specified condition is [`truthy`][truthy]. If\n/// the condition is [`falsy`][falsy], another statement can be executed.\n///\n/// Multiple `if...else` statements can be nested to create an else if clause.\n///\n/// Note that there is no elseif (in one word) keyword in JavaScript.\n///\n/// More information:\n///  - [ECMAScript reference][spec]\n///  - [MDN documentation][mdn]\n///\n/// [spec]: https://tc39.es/ecma262/#prod-IfStatement\n/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/if...else\n/// [truthy]: https://developer.mozilla.org/en-US/docs/Glossary/truthy\n/// [falsy]: https://developer.mozilla.org/en-US/docs/Glossary/falsy\n/// [expression]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Expressions_and_Operators#Expressions\n#[cfg_attr(feature = \"serde\", derive(serde::Serialize, serde::Deserialize))]\n#[cfg_attr(feature = \"arbitrary\", derive(arbitrary::Arbitrary))]\n#[derive(Clone, Debug, PartialEq)]\npub struct If {\n    condition: Expression,\n    body: Box<Statement>,\n    else_node: Option<Box<Statement>>,\n}\n\nimpl If {\n    /// Gets the condition of the if statement.\n    #[inline]\n    #[must_use]\n    pub const fn cond(&self) -> &Expression {\n        &self.condition\n    }\n\n    /// Gets the body to execute if the condition is true.\n    #[inline]\n    #[must_use]\n    pub const fn body(&self) -> &Statement {\n        &self.body\n    }\n\n    /// Gets the `else` node, if it has one.\n    #[inline]\n    pub fn else_node(&self) -> Option<&Statement> {\n        self.else_node.as_ref().map(Box::as_ref)\n    }\n\n    /// Creates an `If` AST node.\n    #[inline]\n    #[must_use]\n    pub fn new(condition: Expression, body: Statement, else_node: Option<Statement>) -> Self {\n        Self {\n            condition,\n            body: body.into(),\n            else_node: else_node.map(Box::new),\n        }\n    }\n}\n\nimpl ToIndentedString for If {\n    fn to_indented_string(&self, interner: &Interner, indent: usize) -> String {\n        let mut buf = format!(\"if ({}) \", self.cond().to_interned_string(interner));\n        match self.else_node() {\n            Some(else_e) => {\n                let _ = write!(\n                    buf,\n                    \"{} else {}\",\n                    self.body().to_indented_string(interner, indent),\n                    else_e.to_indented_string(interner, indent)\n                );\n            }\n            None => {\n                buf.push_str(&self.body().to_indented_string(interner, indent));\n            }\n        }\n        buf\n    }\n}\n\nimpl From<If> for Statement {\n    fn from(if_stm: If) -> Self {\n        Self::If(if_stm)\n    }\n}\n\nimpl VisitWith for If {\n    fn visit_with<'a, V>(&'a self, visitor: &mut V) -> ControlFlow<V::BreakTy>\n    where\n        V: Visitor<'a>,\n    {\n        visitor.visit_expression(&self.condition)?;\n        visitor.visit_statement(&self.body)?;\n        if let Some(stmt) = &self.else_node {\n            visitor.visit_statement(stmt)?;\n        }\n        ControlFlow::Continue(())\n    }\n\n    fn visit_with_mut<'a, V>(&'a mut self, visitor: &mut V) -> ControlFlow<V::BreakTy>\n    where\n        V: VisitorMut<'a>,\n    {\n        visitor.visit_expression_mut(&mut self.condition)?;\n        visitor.visit_statement_mut(&mut self.body)?;\n        if let Some(stmt) = &mut self.else_node {\n            visitor.visit_statement_mut(stmt)?;\n        }\n        ControlFlow::Continue(())\n    }\n}\n"
  },
  {
    "path": "core/ast/src/statement/iteration/break.rs",
    "content": "use boa_interner::{Interner, Sym, ToInternedString};\nuse core::ops::ControlFlow;\n\nuse crate::Statement;\nuse crate::visitor::{VisitWith, Visitor, VisitorMut};\n\n/// The `break` statement terminates the current loop, switch, or label statement and transfers\n/// program control to the statement following the terminated statement.\n///\n/// The break statement includes an optional label that allows the program to break out of a\n/// labeled statement. The break statement needs to be nested within the referenced label. The\n/// labeled statement can be any block statement; it does not have to be preceded by a loop\n/// statement.\n///\n/// More information:\n///  - [ECMAScript reference][spec]\n///  - [MDN documentation][mdn]\n///\n/// [spec]: https://tc39.es/ecma262/#prod-BreakStatement\n/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/break\n#[cfg_attr(feature = \"serde\", derive(serde::Serialize, serde::Deserialize))]\n#[cfg_attr(feature = \"arbitrary\", derive(arbitrary::Arbitrary))]\n#[derive(Debug, Clone, Copy, PartialEq, Eq)]\npub struct Break {\n    label: Option<Sym>,\n}\n\nimpl Break {\n    /// Creates a `Break` AST node.\n    #[must_use]\n    pub const fn new(label: Option<Sym>) -> Self {\n        Self { label }\n    }\n\n    /// Gets the label of the break statement, if any.\n    #[must_use]\n    pub const fn label(&self) -> Option<Sym> {\n        self.label\n    }\n}\n\nimpl ToInternedString for Break {\n    fn to_interned_string(&self, interner: &Interner) -> String {\n        self.label.map_or_else(\n            || \"break\".to_owned(),\n            |label| format!(\"break {}\", interner.resolve_expect(label)),\n        )\n    }\n}\n\nimpl From<Break> for Statement {\n    fn from(break_smt: Break) -> Self {\n        Self::Break(break_smt)\n    }\n}\n\nimpl VisitWith for Break {\n    fn visit_with<'a, V>(&'a self, visitor: &mut V) -> ControlFlow<V::BreakTy>\n    where\n        V: Visitor<'a>,\n    {\n        if let Some(sym) = &self.label {\n            visitor.visit_sym(sym)\n        } else {\n            ControlFlow::Continue(())\n        }\n    }\n\n    fn visit_with_mut<'a, V>(&'a mut self, visitor: &mut V) -> ControlFlow<V::BreakTy>\n    where\n        V: VisitorMut<'a>,\n    {\n        if let Some(sym) = &mut self.label {\n            visitor.visit_sym_mut(sym)\n        } else {\n            ControlFlow::Continue(())\n        }\n    }\n}\n"
  },
  {
    "path": "core/ast/src/statement/iteration/continue.rs",
    "content": "use crate::statement::Statement;\nuse crate::visitor::{VisitWith, Visitor, VisitorMut};\nuse boa_interner::{Interner, Sym, ToInternedString};\nuse core::ops::ControlFlow;\n\n/// The `continue` statement terminates execution of the statements in the current iteration of\n/// the current or labeled loop, and continues execution of the loop with the next iteration.\n///\n/// The continue statement can include an optional label that allows the program to jump to the\n/// next iteration of a labeled loop statement instead of the current loop. In this case, the\n/// continue statement needs to be nested within this labeled statement.\n///\n/// More information:\n///  - [ECMAScript reference][spec]\n///  - [MDN documentation][mdn]\n///\n/// [spec]: https://tc39.es/ecma262/#prod-ContinueStatement\n/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/continue\n#[cfg_attr(feature = \"serde\", derive(serde::Serialize, serde::Deserialize))]\n#[cfg_attr(feature = \"arbitrary\", derive(arbitrary::Arbitrary))]\n#[derive(Clone, Copy, Debug, PartialEq, Eq)]\npub struct Continue {\n    label: Option<Sym>,\n}\n\nimpl Continue {\n    /// Creates a `Continue` AST node.\n    #[must_use]\n    pub const fn new(label: Option<Sym>) -> Self {\n        Self { label }\n    }\n\n    /// Gets the label of this `Continue` statement.\n    #[must_use]\n    pub const fn label(&self) -> Option<Sym> {\n        self.label\n    }\n}\n\nimpl ToInternedString for Continue {\n    fn to_interned_string(&self, interner: &Interner) -> String {\n        self.label.map_or_else(\n            || \"continue\".to_owned(),\n            |label| format!(\"continue {}\", interner.resolve_expect(label)),\n        )\n    }\n}\n\nimpl From<Continue> for Statement {\n    fn from(cont: Continue) -> Self {\n        Self::Continue(cont)\n    }\n}\n\nimpl VisitWith for Continue {\n    fn visit_with<'a, V>(&'a self, visitor: &mut V) -> ControlFlow<V::BreakTy>\n    where\n        V: Visitor<'a>,\n    {\n        if let Some(sym) = &self.label {\n            visitor.visit_sym(sym)\n        } else {\n            ControlFlow::Continue(())\n        }\n    }\n\n    fn visit_with_mut<'a, V>(&'a mut self, visitor: &mut V) -> ControlFlow<V::BreakTy>\n    where\n        V: VisitorMut<'a>,\n    {\n        if let Some(sym) = &mut self.label {\n            visitor.visit_sym_mut(sym)\n        } else {\n            ControlFlow::Continue(())\n        }\n    }\n}\n"
  },
  {
    "path": "core/ast/src/statement/iteration/do_while_loop.rs",
    "content": "use crate::{\n    expression::Expression,\n    statement::Statement,\n    visitor::{VisitWith, Visitor, VisitorMut},\n};\nuse boa_interner::{Interner, ToIndentedString, ToInternedString};\nuse core::ops::ControlFlow;\n\n/// The `do...while` statement creates a loop that executes a specified statement until the\n/// test condition evaluates to false.\n///\n/// The condition is evaluated after executing the statement, resulting in the specified\n/// statement executing at least once.\n///\n/// More information:\n///  - [ECMAScript reference][spec]\n///  - [MDN documentation][mdn]\n///\n/// [spec]: https://tc39.es/ecma262/#sec-do-while-statement\n/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/do...while\n#[cfg_attr(feature = \"serde\", derive(serde::Serialize, serde::Deserialize))]\n#[cfg_attr(feature = \"arbitrary\", derive(arbitrary::Arbitrary))]\n#[derive(Clone, Debug, PartialEq)]\npub struct DoWhileLoop {\n    body: Box<Statement>,\n    condition: Expression,\n}\n\nimpl DoWhileLoop {\n    /// Gets the body of the do-while loop.\n    #[inline]\n    #[must_use]\n    pub const fn body(&self) -> &Statement {\n        &self.body\n    }\n\n    /// Gets the condition of the do-while loop.\n    #[inline]\n    #[must_use]\n    pub const fn cond(&self) -> &Expression {\n        &self.condition\n    }\n    /// Creates a `DoWhileLoop` AST node.\n    #[inline]\n    #[must_use]\n    pub fn new(body: Statement, condition: Expression) -> Self {\n        Self {\n            body: body.into(),\n            condition,\n        }\n    }\n}\n\nimpl ToIndentedString for DoWhileLoop {\n    fn to_indented_string(&self, interner: &Interner, indentation: usize) -> String {\n        format!(\n            \"do {} while ({})\",\n            self.body().to_indented_string(interner, indentation),\n            self.cond().to_interned_string(interner)\n        )\n    }\n}\n\nimpl From<DoWhileLoop> for Statement {\n    fn from(do_while: DoWhileLoop) -> Self {\n        Self::DoWhileLoop(do_while)\n    }\n}\n\nimpl VisitWith for DoWhileLoop {\n    fn visit_with<'a, V>(&'a self, visitor: &mut V) -> ControlFlow<V::BreakTy>\n    where\n        V: Visitor<'a>,\n    {\n        visitor.visit_statement(&self.body)?;\n        visitor.visit_expression(&self.condition)\n    }\n\n    fn visit_with_mut<'a, V>(&'a mut self, visitor: &mut V) -> ControlFlow<V::BreakTy>\n    where\n        V: VisitorMut<'a>,\n    {\n        visitor.visit_statement_mut(&mut self.body)?;\n        visitor.visit_expression_mut(&mut self.condition)\n    }\n}\n"
  },
  {
    "path": "core/ast/src/statement/iteration/for_in_loop.rs",
    "content": "use crate::operations::{ContainsSymbol, contains};\nuse crate::scope::Scope;\nuse crate::visitor::{VisitWith, Visitor, VisitorMut};\nuse crate::{\n    expression::Expression,\n    statement::{Statement, iteration::IterableLoopInitializer},\n};\nuse boa_interner::{Interner, ToIndentedString, ToInternedString};\nuse core::ops::ControlFlow;\n\n/// A `for...in` loop statement, as defined by the [spec].\n///\n/// [`for...in`][forin] statements loop over all enumerable string properties of an object, including\n/// inherited properties.\n///\n/// [forin]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/for...in\n/// [spec]: https://tc39.es/ecma262/#prod-ForInOfStatement\n#[cfg_attr(feature = \"serde\", derive(serde::Serialize, serde::Deserialize))]\n#[cfg_attr(feature = \"arbitrary\", derive(arbitrary::Arbitrary))]\n#[derive(Clone, Debug, PartialEq)]\npub struct ForInLoop {\n    pub(crate) initializer: IterableLoopInitializer,\n    pub(crate) target: Expression,\n    pub(crate) body: Box<Statement>,\n    pub(crate) target_contains_direct_eval: bool,\n    pub(crate) contains_direct_eval: bool,\n\n    #[cfg_attr(feature = \"serde\", serde(skip))]\n    pub(crate) target_scope: Option<Scope>,\n\n    #[cfg_attr(feature = \"serde\", serde(skip))]\n    pub(crate) scope: Option<Scope>,\n}\n\nimpl ForInLoop {\n    /// Creates a new `ForInLoop`.\n    #[inline]\n    #[must_use]\n    pub fn new(initializer: IterableLoopInitializer, target: Expression, body: Statement) -> Self {\n        let target_contains_direct_eval = contains(&target, ContainsSymbol::DirectEval);\n        let contains_direct_eval = contains(&initializer, ContainsSymbol::DirectEval)\n            || contains(&body, ContainsSymbol::DirectEval);\n        Self {\n            initializer,\n            target,\n            body: body.into(),\n            target_contains_direct_eval,\n            contains_direct_eval,\n            target_scope: None,\n            scope: None,\n        }\n    }\n\n    /// Gets the initializer of the for...in loop.\n    #[inline]\n    #[must_use]\n    pub const fn initializer(&self) -> &IterableLoopInitializer {\n        &self.initializer\n    }\n\n    /// Gets the target object of the for...in loop.\n    #[inline]\n    #[must_use]\n    pub const fn target(&self) -> &Expression {\n        &self.target\n    }\n\n    /// Gets the body of the for...in loop.\n    #[inline]\n    #[must_use]\n    pub const fn body(&self) -> &Statement {\n        &self.body\n    }\n\n    /// Returns the target scope of the for...in loop.\n    #[inline]\n    #[must_use]\n    pub const fn target_scope(&self) -> Option<&Scope> {\n        self.target_scope.as_ref()\n    }\n\n    /// Returns the scope of the for...in loop.\n    #[inline]\n    #[must_use]\n    pub const fn scope(&self) -> Option<&Scope> {\n        self.scope.as_ref()\n    }\n}\n\nimpl ToIndentedString for ForInLoop {\n    fn to_indented_string(&self, interner: &Interner, indentation: usize) -> String {\n        let mut buf = format!(\n            \"for ({} in {}) \",\n            self.initializer.to_interned_string(interner),\n            self.target.to_interned_string(interner)\n        );\n        buf.push_str(&self.body().to_indented_string(interner, indentation));\n\n        buf\n    }\n}\n\nimpl From<ForInLoop> for Statement {\n    #[inline]\n    fn from(for_in: ForInLoop) -> Self {\n        Self::ForInLoop(for_in)\n    }\n}\n\nimpl VisitWith for ForInLoop {\n    fn visit_with<'a, V>(&'a self, visitor: &mut V) -> ControlFlow<V::BreakTy>\n    where\n        V: Visitor<'a>,\n    {\n        visitor.visit_iterable_loop_initializer(&self.initializer)?;\n        visitor.visit_expression(&self.target)?;\n        visitor.visit_statement(&self.body)\n    }\n\n    fn visit_with_mut<'a, V>(&'a mut self, visitor: &mut V) -> ControlFlow<V::BreakTy>\n    where\n        V: VisitorMut<'a>,\n    {\n        visitor.visit_iterable_loop_initializer_mut(&mut self.initializer)?;\n        visitor.visit_expression_mut(&mut self.target)?;\n        visitor.visit_statement_mut(&mut self.body)\n    }\n}\n"
  },
  {
    "path": "core/ast/src/statement/iteration/for_loop.rs",
    "content": "use crate::operations::{ContainsSymbol, contains};\nuse crate::scope::Scope;\nuse crate::visitor::{VisitWith, Visitor, VisitorMut};\nuse crate::{\n    Expression,\n    declaration::{LexicalDeclaration, VarDeclaration},\n    statement::Statement,\n};\nuse boa_interner::{Interner, ToIndentedString, ToInternedString};\nuse core::{fmt::Write as _, ops::ControlFlow};\n\n/// The `for` statement creates a loop that consists of three optional expressions.\n///\n/// A [`for`][mdn] loop repeats until a specified condition evaluates to `false`.\n/// The JavaScript for loop is similar to the Java and C for loop.\n///\n/// More information:\n///  - [ECMAScript reference][spec]\n///\n/// [spec]: https://tc39.es/ecma262/#prod-ForDeclaration\n/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/for\n#[cfg_attr(feature = \"serde\", derive(serde::Serialize, serde::Deserialize))]\n#[cfg_attr(feature = \"arbitrary\", derive(arbitrary::Arbitrary))]\n#[derive(Clone, Debug, PartialEq)]\npub struct ForLoop {\n    #[cfg_attr(feature = \"serde\", serde(flatten))]\n    pub(crate) inner: Box<InnerForLoop>,\n}\n\nimpl ForLoop {\n    /// Creates a new for loop AST node.\n    #[inline]\n    #[must_use]\n    pub fn new(\n        init: Option<ForLoopInitializer>,\n        condition: Option<Expression>,\n        final_expr: Option<Expression>,\n        body: Statement,\n    ) -> Self {\n        Self {\n            inner: Box::new(InnerForLoop::new(init, condition, final_expr, body)),\n        }\n    }\n\n    /// Gets the initialization node.\n    #[inline]\n    #[must_use]\n    pub const fn init(&self) -> Option<&ForLoopInitializer> {\n        self.inner.init()\n    }\n\n    /// Gets the loop condition node.\n    #[inline]\n    #[must_use]\n    pub const fn condition(&self) -> Option<&Expression> {\n        self.inner.condition()\n    }\n\n    /// Gets the final expression node.\n    #[inline]\n    #[must_use]\n    pub const fn final_expr(&self) -> Option<&Expression> {\n        self.inner.final_expr()\n    }\n\n    /// Gets the body of the for loop.\n    #[inline]\n    #[must_use]\n    pub const fn body(&self) -> &Statement {\n        self.inner.body()\n    }\n}\n\nimpl ToIndentedString for ForLoop {\n    fn to_indented_string(&self, interner: &Interner, indentation: usize) -> String {\n        let mut buf = String::from(\"for (\");\n        if let Some(init) = self.init() {\n            buf.push_str(&init.to_interned_string(interner));\n        }\n        buf.push_str(\"; \");\n        if let Some(condition) = self.condition() {\n            buf.push_str(&condition.to_interned_string(interner));\n        }\n        buf.push_str(\"; \");\n        if let Some(final_expr) = self.final_expr() {\n            buf.push_str(&final_expr.to_interned_string(interner));\n        }\n        let _ = write!(\n            buf,\n            \") {}\",\n            self.inner.body().to_indented_string(interner, indentation)\n        );\n\n        buf\n    }\n}\n\nimpl From<ForLoop> for Statement {\n    #[inline]\n    fn from(for_loop: ForLoop) -> Self {\n        Self::ForLoop(for_loop)\n    }\n}\n\nimpl VisitWith for ForLoop {\n    fn visit_with<'a, V>(&'a self, visitor: &mut V) -> ControlFlow<V::BreakTy>\n    where\n        V: Visitor<'a>,\n    {\n        if let Some(fli) = &self.inner.init {\n            visitor.visit_for_loop_initializer(fli)?;\n        }\n        if let Some(expr) = &self.inner.condition {\n            visitor.visit_expression(expr)?;\n        }\n        if let Some(expr) = &self.inner.final_expr {\n            visitor.visit_expression(expr)?;\n        }\n        visitor.visit_statement(&self.inner.body)\n    }\n\n    fn visit_with_mut<'a, V>(&'a mut self, visitor: &mut V) -> ControlFlow<V::BreakTy>\n    where\n        V: VisitorMut<'a>,\n    {\n        if let Some(fli) = &mut self.inner.init {\n            visitor.visit_for_loop_initializer_mut(fli)?;\n        }\n        if let Some(expr) = &mut self.inner.condition {\n            visitor.visit_expression_mut(expr)?;\n        }\n        if let Some(expr) = &mut self.inner.final_expr {\n            visitor.visit_expression_mut(expr)?;\n        }\n        visitor.visit_statement_mut(&mut self.inner.body)\n    }\n}\n\n/// Inner structure to avoid multiple indirections in the heap.\n#[cfg_attr(feature = \"serde\", derive(serde::Serialize, serde::Deserialize))]\n#[cfg_attr(feature = \"arbitrary\", derive(arbitrary::Arbitrary))]\n#[derive(Clone, Debug, PartialEq)]\npub(crate) struct InnerForLoop {\n    pub(crate) init: Option<ForLoopInitializer>,\n    pub(crate) condition: Option<Expression>,\n    pub(crate) final_expr: Option<Expression>,\n    pub(crate) body: Statement,\n    pub(crate) contains_direct_eval: bool,\n}\n\nimpl InnerForLoop {\n    /// Creates a new inner for loop.\n    #[inline]\n    fn new(\n        init: Option<ForLoopInitializer>,\n        condition: Option<Expression>,\n        final_expr: Option<Expression>,\n        body: Statement,\n    ) -> Self {\n        let mut contains_direct_eval = contains(&body, ContainsSymbol::DirectEval);\n        if let Some(init) = &init {\n            contains_direct_eval |= contains(init, ContainsSymbol::DirectEval);\n        }\n        if let Some(condition) = &condition {\n            contains_direct_eval |= contains(condition, ContainsSymbol::DirectEval);\n        }\n        if let Some(final_expr) = &final_expr {\n            contains_direct_eval |= contains(final_expr, ContainsSymbol::DirectEval);\n        }\n        Self {\n            init,\n            condition,\n            final_expr,\n            body,\n            contains_direct_eval,\n        }\n    }\n\n    /// Gets the initialization node.\n    #[inline]\n    const fn init(&self) -> Option<&ForLoopInitializer> {\n        self.init.as_ref()\n    }\n\n    /// Gets the loop condition node.\n    #[inline]\n    const fn condition(&self) -> Option<&Expression> {\n        self.condition.as_ref()\n    }\n\n    /// Gets the final expression node.\n    #[inline]\n    const fn final_expr(&self) -> Option<&Expression> {\n        self.final_expr.as_ref()\n    }\n\n    /// Gets the body of the for loop.\n    #[inline]\n    const fn body(&self) -> &Statement {\n        &self.body\n    }\n}\n\n/// A [`ForLoop`] initializer, as defined by the [spec].\n///\n/// A `ForLoop` initializer differs a lot from an\n/// [`IterableLoopInitializer`][super::IterableLoopInitializer], since it can contain any arbitrary\n/// expression instead of only accessors and patterns. Additionally, it can also contain many variable\n/// declarations instead of only one.\n///\n/// [spec]: https://tc39.es/ecma262/#prod-ForStatement\n#[cfg_attr(feature = \"serde\", derive(serde::Serialize, serde::Deserialize))]\n#[cfg_attr(feature = \"arbitrary\", derive(arbitrary::Arbitrary))]\n#[derive(Clone, Debug, PartialEq)]\npub enum ForLoopInitializer {\n    /// An expression initializer.\n    Expression(Expression),\n    /// A var declaration initializer.\n    Var(VarDeclaration),\n    /// A lexical declaration initializer.\n    Lexical(ForLoopInitializerLexical),\n}\n\n/// A lexical declaration initializer for a `ForLoop`.\n#[cfg_attr(feature = \"serde\", derive(serde::Serialize, serde::Deserialize))]\n#[cfg_attr(feature = \"arbitrary\", derive(arbitrary::Arbitrary))]\n#[derive(Clone, Debug, PartialEq)]\npub struct ForLoopInitializerLexical {\n    pub(crate) declaration: LexicalDeclaration,\n\n    #[cfg_attr(feature = \"serde\", serde(skip))]\n    pub(crate) scope: Scope,\n}\n\nimpl ForLoopInitializerLexical {\n    /// Creates a new lexical declaration initializer.\n    #[inline]\n    #[must_use]\n    pub fn new(declaration: LexicalDeclaration, scope: Scope) -> Self {\n        Self { declaration, scope }\n    }\n\n    /// Returns the declaration of the lexical initializer.\n    #[inline]\n    #[must_use]\n    pub const fn declaration(&self) -> &LexicalDeclaration {\n        &self.declaration\n    }\n\n    /// Returns the scope of the lexical initializer.\n    #[inline]\n    #[must_use]\n    pub const fn scope(&self) -> &Scope {\n        &self.scope\n    }\n}\n\nimpl ToInternedString for ForLoopInitializer {\n    fn to_interned_string(&self, interner: &Interner) -> String {\n        match self {\n            Self::Var(var) => var.to_interned_string(interner),\n            Self::Lexical(lex) => lex.declaration.to_interned_string(interner),\n            Self::Expression(expr) => expr.to_interned_string(interner),\n        }\n    }\n}\n\nimpl From<Expression> for ForLoopInitializer {\n    #[inline]\n    fn from(expr: Expression) -> Self {\n        Self::Expression(expr)\n    }\n}\n\nimpl From<LexicalDeclaration> for ForLoopInitializer {\n    #[inline]\n    fn from(list: LexicalDeclaration) -> Self {\n        Self::Lexical(ForLoopInitializerLexical {\n            declaration: list,\n            scope: Scope::default(),\n        })\n    }\n}\n\nimpl From<VarDeclaration> for ForLoopInitializer {\n    #[inline]\n    fn from(list: VarDeclaration) -> Self {\n        Self::Var(list)\n    }\n}\n\nimpl VisitWith for ForLoopInitializer {\n    fn visit_with<'a, V>(&'a self, visitor: &mut V) -> ControlFlow<V::BreakTy>\n    where\n        V: Visitor<'a>,\n    {\n        match self {\n            Self::Expression(expr) => visitor.visit_expression(expr),\n            Self::Var(vd) => visitor.visit_var_declaration(vd),\n            Self::Lexical(ld) => visitor.visit_lexical_declaration(&ld.declaration),\n        }\n    }\n\n    fn visit_with_mut<'a, V>(&'a mut self, visitor: &mut V) -> ControlFlow<V::BreakTy>\n    where\n        V: VisitorMut<'a>,\n    {\n        match self {\n            Self::Expression(expr) => visitor.visit_expression_mut(expr),\n            Self::Var(vd) => visitor.visit_var_declaration_mut(vd),\n            Self::Lexical(ld) => visitor.visit_lexical_declaration_mut(&mut ld.declaration),\n        }\n    }\n}\n"
  },
  {
    "path": "core/ast/src/statement/iteration/for_of_loop.rs",
    "content": "use crate::operations::{ContainsSymbol, contains};\nuse crate::scope::Scope;\nuse crate::visitor::{VisitWith, Visitor, VisitorMut};\nuse crate::{\n    expression::Expression,\n    statement::{Statement, iteration::IterableLoopInitializer},\n};\nuse boa_interner::{Interner, ToIndentedString, ToInternedString};\nuse core::ops::ControlFlow;\n\n/// A `for...of` loop statement, as defined by the [spec].\n///\n/// [`for..of`][forof] statements loop over a sequence of values obtained from an iterable object (Array,\n/// String, Map, generators).\n///\n/// This type combines `for..of` and [`for await...of`][forawait] statements in a single structure,\n/// since `for await...of` is essentially the same statement but with async iterable objects\n/// as the source of iteration.\n///\n/// [forof]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/for...of\n/// [spec]: https://tc39.es/ecma262/#prod-ForInOfStatement\n/// [forawait]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/for-await...of\n#[cfg_attr(feature = \"serde\", derive(serde::Serialize, serde::Deserialize))]\n#[cfg_attr(feature = \"arbitrary\", derive(arbitrary::Arbitrary))]\n#[derive(Clone, Debug, PartialEq)]\npub struct ForOfLoop {\n    pub(crate) init: IterableLoopInitializer,\n    pub(crate) iterable: Expression,\n    pub(crate) body: Box<Statement>,\n    r#await: bool,\n    pub(crate) iterable_contains_direct_eval: bool,\n    pub(crate) contains_direct_eval: bool,\n\n    #[cfg_attr(feature = \"serde\", serde(skip))]\n    pub(crate) iterable_scope: Option<Scope>,\n\n    #[cfg_attr(feature = \"serde\", serde(skip))]\n    pub(crate) scope: Option<Scope>,\n}\n\nimpl ForOfLoop {\n    /// Creates a new \"for of\" loop AST node.\n    #[inline]\n    #[must_use]\n    pub fn new(\n        init: IterableLoopInitializer,\n        iterable: Expression,\n        body: Statement,\n        r#await: bool,\n    ) -> Self {\n        let iterable_contains_direct_eval = contains(&iterable, ContainsSymbol::DirectEval);\n        let contains_direct_eval = contains(&init, ContainsSymbol::DirectEval)\n            || contains(&body, ContainsSymbol::DirectEval);\n        Self {\n            init,\n            iterable,\n            body: body.into(),\n            iterable_contains_direct_eval,\n            contains_direct_eval,\n            r#await,\n            iterable_scope: None,\n            scope: None,\n        }\n    }\n\n    /// Gets the initializer of the for...of loop.\n    #[inline]\n    #[must_use]\n    pub const fn initializer(&self) -> &IterableLoopInitializer {\n        &self.init\n    }\n\n    /// Gets the iterable expression of the for...of loop.\n    #[inline]\n    #[must_use]\n    pub const fn iterable(&self) -> &Expression {\n        &self.iterable\n    }\n\n    /// Gets the body to execute in the for...of loop.\n    #[inline]\n    #[must_use]\n    pub const fn body(&self) -> &Statement {\n        &self.body\n    }\n\n    /// Returns true if this \"for...of\" loop is an \"for await...of\" loop.\n    #[inline]\n    #[must_use]\n    pub const fn r#await(&self) -> bool {\n        self.r#await\n    }\n\n    /// Return the iterable scope of the for...of loop.\n    #[inline]\n    #[must_use]\n    pub const fn iterable_scope(&self) -> Option<&Scope> {\n        self.iterable_scope.as_ref()\n    }\n\n    /// Return the scope of the for...of loop.\n    #[inline]\n    #[must_use]\n    pub const fn scope(&self) -> Option<&Scope> {\n        self.scope.as_ref()\n    }\n}\n\nimpl ToIndentedString for ForOfLoop {\n    fn to_indented_string(&self, interner: &Interner, indentation: usize) -> String {\n        format!(\n            \"for ({} of {}) {}\",\n            self.init.to_interned_string(interner),\n            self.iterable.to_interned_string(interner),\n            self.body().to_indented_string(interner, indentation)\n        )\n    }\n}\n\nimpl From<ForOfLoop> for Statement {\n    #[inline]\n    fn from(for_of: ForOfLoop) -> Self {\n        Self::ForOfLoop(for_of)\n    }\n}\n\nimpl VisitWith for ForOfLoop {\n    fn visit_with<'a, V>(&'a self, visitor: &mut V) -> ControlFlow<V::BreakTy>\n    where\n        V: Visitor<'a>,\n    {\n        visitor.visit_iterable_loop_initializer(&self.init)?;\n        visitor.visit_expression(&self.iterable)?;\n        visitor.visit_statement(&self.body)\n    }\n\n    fn visit_with_mut<'a, V>(&'a mut self, visitor: &mut V) -> ControlFlow<V::BreakTy>\n    where\n        V: VisitorMut<'a>,\n    {\n        visitor.visit_iterable_loop_initializer_mut(&mut self.init)?;\n        visitor.visit_expression_mut(&mut self.iterable)?;\n        visitor.visit_statement_mut(&mut self.body)\n    }\n}\n"
  },
  {
    "path": "core/ast/src/statement/iteration/mod.rs",
    "content": "//! Iteration nodes\n\nmod r#break;\nmod r#continue;\nmod do_while_loop;\nmod for_in_loop;\nmod for_loop;\nmod for_of_loop;\nmod while_loop;\n\nuse crate::{\n    declaration::{Binding, Variable},\n    expression::{Identifier, access::PropertyAccess},\n    pattern::Pattern,\n};\nuse core::ops::ControlFlow;\n\npub use self::{\n    r#break::Break,\n    r#continue::Continue,\n    do_while_loop::DoWhileLoop,\n    for_in_loop::ForInLoop,\n    for_loop::{ForLoop, ForLoopInitializer, ForLoopInitializerLexical},\n    for_of_loop::ForOfLoop,\n    while_loop::WhileLoop,\n};\nuse crate::visitor::{VisitWith, Visitor, VisitorMut};\nuse boa_interner::{Interner, ToInternedString};\n\n/// A `for-in`, `for-of` and `for-await-of` loop initializer.\n///\n/// The [spec] specifies only single bindings for the listed types of loops, which makes us\n/// unable to use plain `LexicalDeclaration`s or `VarStatement`s as initializers, since those\n/// can have more than one binding.\n///\n/// [spec]: https://tc39.es/ecma262/#prod-ForInOfStatement\n#[cfg_attr(feature = \"serde\", derive(serde::Serialize, serde::Deserialize))]\n#[cfg_attr(feature = \"arbitrary\", derive(arbitrary::Arbitrary))]\n#[derive(Clone, Debug, PartialEq)]\npub enum IterableLoopInitializer {\n    /// An already declared variable.\n    Identifier(Identifier),\n    /// A property access.\n    Access(PropertyAccess),\n    /// A new var declaration.\n    Var(Variable),\n    /// A new let declaration.\n    Let(Binding),\n    /// A new const declaration.\n    Const(Binding),\n    /// A pattern with already declared variables.\n    Pattern(Pattern),\n}\n\nimpl ToInternedString for IterableLoopInitializer {\n    fn to_interned_string(&self, interner: &Interner) -> String {\n        let (binding, pre) = match self {\n            Self::Identifier(ident) => return ident.to_interned_string(interner),\n            Self::Pattern(pattern) => return pattern.to_interned_string(interner),\n            Self::Access(access) => return access.to_interned_string(interner),\n            Self::Var(binding) => (binding.to_interned_string(interner), \"var\"),\n            Self::Let(binding) => (binding.to_interned_string(interner), \"let\"),\n            Self::Const(binding) => (binding.to_interned_string(interner), \"const\"),\n        };\n\n        format!(\"{pre} {binding}\")\n    }\n}\n\nimpl VisitWith for IterableLoopInitializer {\n    fn visit_with<'a, V>(&'a self, visitor: &mut V) -> ControlFlow<V::BreakTy>\n    where\n        V: Visitor<'a>,\n    {\n        match self {\n            Self::Identifier(id) => visitor.visit_identifier(id),\n            Self::Access(pa) => visitor.visit_property_access(pa),\n            Self::Var(b) => visitor.visit_variable(b),\n            Self::Let(b) | Self::Const(b) => visitor.visit_binding(b),\n            Self::Pattern(p) => visitor.visit_pattern(p),\n        }\n    }\n\n    fn visit_with_mut<'a, V>(&'a mut self, visitor: &mut V) -> ControlFlow<V::BreakTy>\n    where\n        V: VisitorMut<'a>,\n    {\n        match self {\n            Self::Identifier(id) => visitor.visit_identifier_mut(id),\n            Self::Access(pa) => visitor.visit_property_access_mut(pa),\n            Self::Var(b) => visitor.visit_variable_mut(b),\n            Self::Let(b) | Self::Const(b) => visitor.visit_binding_mut(b),\n            Self::Pattern(p) => visitor.visit_pattern_mut(p),\n        }\n    }\n}\n"
  },
  {
    "path": "core/ast/src/statement/iteration/while_loop.rs",
    "content": "use crate::{\n    expression::Expression,\n    statement::Statement,\n    visitor::{VisitWith, Visitor, VisitorMut},\n};\nuse boa_interner::{Interner, ToIndentedString, ToInternedString};\nuse core::ops::ControlFlow;\n\n/// The `while` statement creates a loop that executes a specified statement as long as the\n/// test condition evaluates to `true`.\n///\n/// The condition is evaluated before executing the statement.\n///\n/// More information:\n///  - [ECMAScript reference][spec]\n///  - [MDN documentation][mdn]\n///\n/// [spec]: https://tc39.es/ecma262/#prod-grammar-notation-WhileStatement\n/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/while\n#[cfg_attr(feature = \"serde\", derive(serde::Serialize, serde::Deserialize))]\n#[cfg_attr(feature = \"arbitrary\", derive(arbitrary::Arbitrary))]\n#[derive(Clone, Debug, PartialEq)]\npub struct WhileLoop {\n    condition: Expression,\n    body: Box<Statement>,\n}\n\nimpl WhileLoop {\n    /// Creates a `WhileLoop` AST node.\n    #[inline]\n    #[must_use]\n    pub fn new(condition: Expression, body: Statement) -> Self {\n        Self {\n            condition,\n            body: body.into(),\n        }\n    }\n\n    /// Gets the condition of the while loop.\n    #[inline]\n    #[must_use]\n    pub const fn condition(&self) -> &Expression {\n        &self.condition\n    }\n\n    /// Gets the body of the while loop.\n    #[inline]\n    #[must_use]\n    pub const fn body(&self) -> &Statement {\n        &self.body\n    }\n}\n\nimpl ToIndentedString for WhileLoop {\n    fn to_indented_string(&self, interner: &Interner, indentation: usize) -> String {\n        format!(\n            \"while ({}) {}\",\n            self.condition().to_interned_string(interner),\n            self.body().to_indented_string(interner, indentation)\n        )\n    }\n}\n\nimpl From<WhileLoop> for Statement {\n    #[inline]\n    fn from(while_loop: WhileLoop) -> Self {\n        Self::WhileLoop(while_loop)\n    }\n}\n\nimpl VisitWith for WhileLoop {\n    fn visit_with<'a, V>(&'a self, visitor: &mut V) -> ControlFlow<V::BreakTy>\n    where\n        V: Visitor<'a>,\n    {\n        visitor.visit_expression(&self.condition)?;\n        visitor.visit_statement(&self.body)\n    }\n\n    fn visit_with_mut<'a, V>(&'a mut self, visitor: &mut V) -> ControlFlow<V::BreakTy>\n    where\n        V: VisitorMut<'a>,\n    {\n        visitor.visit_expression_mut(&mut self.condition)?;\n        visitor.visit_statement_mut(&mut self.body)\n    }\n}\n"
  },
  {
    "path": "core/ast/src/statement/labelled.rs",
    "content": "use crate::{\n    Statement,\n    function::FunctionDeclaration,\n    visitor::{VisitWith, Visitor, VisitorMut},\n};\nuse boa_interner::{Interner, Sym, ToIndentedString, ToInternedString};\nuse core::ops::ControlFlow;\n\n/// The set of Parse Nodes that can be preceded by a label, as defined by the [spec].\n///\n/// Semantically, a [`Labelled`] statement should only wrap [`Statement`] nodes. However,\n/// old ECMAScript implementations supported [labelled function declarations][label-fn] as an extension\n/// of the grammar. In the ECMAScript 2015 spec, the production of `LabelledStatement` was\n/// modified to include labelled [`FunctionDeclaration`]s as a valid node.\n///\n/// [spec]: https://tc39.es/ecma262/#prod-LabelledItem\n/// [label-fn]: https://tc39.es/ecma262/#sec-labelled-function-declarations\n#[cfg_attr(feature = \"serde\", derive(serde::Serialize, serde::Deserialize))]\n#[cfg_attr(feature = \"arbitrary\", derive(arbitrary::Arbitrary))]\n#[derive(Clone, Debug, PartialEq)]\n#[allow(clippy::large_enum_variant)]\npub enum LabelledItem {\n    /// A labelled [`FunctionDeclaration`].\n    FunctionDeclaration(FunctionDeclaration),\n\n    /// A labelled [`Statement`].\n    Statement(Statement),\n}\n\nimpl LabelledItem {\n    pub(crate) fn to_indented_string(&self, interner: &Interner, indentation: usize) -> String {\n        match self {\n            Self::FunctionDeclaration(f) => f.to_indented_string(interner, indentation),\n            Self::Statement(stmt) => stmt.to_indented_string(interner, indentation),\n        }\n    }\n}\n\nimpl ToInternedString for LabelledItem {\n    fn to_interned_string(&self, interner: &Interner) -> String {\n        self.to_indented_string(interner, 0)\n    }\n}\n\nimpl From<FunctionDeclaration> for LabelledItem {\n    fn from(f: FunctionDeclaration) -> Self {\n        Self::FunctionDeclaration(f)\n    }\n}\n\nimpl From<Statement> for LabelledItem {\n    fn from(stmt: Statement) -> Self {\n        Self::Statement(stmt)\n    }\n}\n\nimpl VisitWith for LabelledItem {\n    fn visit_with<'a, V>(&'a self, visitor: &mut V) -> ControlFlow<V::BreakTy>\n    where\n        V: Visitor<'a>,\n    {\n        match self {\n            Self::FunctionDeclaration(f) => visitor.visit_function_declaration(f),\n            Self::Statement(s) => visitor.visit_statement(s),\n        }\n    }\n\n    fn visit_with_mut<'a, V>(&'a mut self, visitor: &mut V) -> ControlFlow<V::BreakTy>\n    where\n        V: VisitorMut<'a>,\n    {\n        match self {\n            Self::FunctionDeclaration(f) => visitor.visit_function_declaration_mut(f),\n            Self::Statement(s) => visitor.visit_statement_mut(s),\n        }\n    }\n}\n\n/// Labelled statement nodes, as defined by the [spec].\n///\n/// The method [`Labelled::item`] doesn't return a [`Statement`] for compatibility reasons.\n/// See [`LabelledItem`] for more information.\n///\n/// [spec]: https://tc39.es/ecma262/#sec-labelled-statements\n#[cfg_attr(feature = \"serde\", derive(serde::Serialize, serde::Deserialize))]\n#[cfg_attr(feature = \"arbitrary\", derive(arbitrary::Arbitrary))]\n#[derive(Clone, Debug, PartialEq)]\npub struct Labelled {\n    item: Box<LabelledItem>,\n    label: Sym,\n}\n\nimpl Labelled {\n    /// Creates a new `Labelled` statement.\n    #[inline]\n    #[must_use]\n    pub fn new(item: LabelledItem, label: Sym) -> Self {\n        Self {\n            item: Box::new(item),\n            label,\n        }\n    }\n\n    /// Gets the labelled item.\n    #[inline]\n    #[must_use]\n    pub const fn item(&self) -> &LabelledItem {\n        &self.item\n    }\n\n    /// Gets the label name.\n    #[inline]\n    #[must_use]\n    pub const fn label(&self) -> Sym {\n        self.label\n    }\n\n    pub(crate) fn to_indented_string(&self, interner: &Interner, indentation: usize) -> String {\n        format!(\n            \"{}: {}\",\n            interner.resolve_expect(self.label),\n            self.item.to_indented_string(interner, indentation)\n        )\n    }\n}\n\nimpl ToInternedString for Labelled {\n    fn to_interned_string(&self, interner: &Interner) -> String {\n        self.to_indented_string(interner, 0)\n    }\n}\n\nimpl From<Labelled> for Statement {\n    fn from(labelled: Labelled) -> Self {\n        Self::Labelled(labelled)\n    }\n}\n\nimpl VisitWith for Labelled {\n    fn visit_with<'a, V>(&'a self, visitor: &mut V) -> ControlFlow<V::BreakTy>\n    where\n        V: Visitor<'a>,\n    {\n        visitor.visit_labelled_item(&self.item)?;\n        visitor.visit_sym(&self.label)\n    }\n\n    fn visit_with_mut<'a, V>(&'a mut self, visitor: &mut V) -> ControlFlow<V::BreakTy>\n    where\n        V: VisitorMut<'a>,\n    {\n        visitor.visit_labelled_item_mut(&mut self.item)?;\n        visitor.visit_sym_mut(&mut self.label)\n    }\n}\n"
  },
  {
    "path": "core/ast/src/statement/mod.rs",
    "content": "//! The [`Statement`] Parse Node, as defined by the [spec].\n//!\n//! ECMAScript [statements] are mainly composed of control flow operations, such as [`If`],\n//! [`WhileLoop`], and [`Break`]. However, it also contains statements such as [`VarDeclaration`],\n//! [`Block`] or [`Expression`] which are not strictly used for control flow.\n//!\n//! [spec]: https://tc39.es/ecma262/#prod-Statement\n//! [statements]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements\n\nmod block;\nmod r#if;\nmod labelled;\nmod r#return;\nmod switch;\nmod throw;\nmod r#try;\nmod with;\n\npub mod iteration;\n\npub use self::{\n    block::Block,\n    r#if::If,\n    iteration::{Break, Continue, DoWhileLoop, ForInLoop, ForLoop, ForOfLoop, WhileLoop},\n    labelled::{Labelled, LabelledItem},\n    r#return::Return,\n    switch::{Case, Switch},\n    throw::Throw,\n    r#try::{Catch, ErrorHandler, Finally, Try},\n    with::With,\n};\nuse core::ops::ControlFlow;\n\nuse crate::visitor::{VisitWith, Visitor, VisitorMut};\nuse boa_interner::{Interner, ToIndentedString, ToInternedString};\n\nuse super::{declaration::VarDeclaration, expression::Expression};\n\n/// The `Statement` Parse Node.\n///\n/// See the [module level documentation][self] for more information.\n#[cfg_attr(feature = \"serde\", derive(serde::Serialize, serde::Deserialize))]\n#[cfg_attr(feature = \"arbitrary\", derive(arbitrary::Arbitrary))]\n#[derive(Clone, Debug, PartialEq)]\npub enum Statement {\n    /// See [`Block`].\n    Block(Block),\n\n    /// See [`VarDeclaration`]\n    Var(VarDeclaration),\n\n    /// An empty statement.\n    ///\n    /// Empty statements do nothing, just return undefined.\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///  - [MDN documentation][mdn]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#prod-EmptyStatement\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/Empty\n    Empty,\n\n    /// See [`Expression`].\n    Expression(Expression),\n\n    /// See [`If`].\n    If(If),\n\n    /// See [`DoWhileLoop`].\n    DoWhileLoop(DoWhileLoop),\n\n    /// See [`WhileLoop`].\n    WhileLoop(WhileLoop),\n\n    /// See [`ForLoop`].\n    ForLoop(ForLoop),\n\n    /// See [`ForInLoop`].\n    ForInLoop(ForInLoop),\n\n    /// See [`ForOfLoop`].\n    ForOfLoop(ForOfLoop),\n\n    /// See[`Switch`].\n    Switch(Switch),\n\n    /// See [`Continue`].\n    Continue(Continue),\n\n    /// See [`Break`].\n    Break(Break),\n\n    /// See [`Return`].\n    Return(Return),\n\n    /// See [`Labelled`].\n    Labelled(Labelled),\n\n    /// See [`Throw`].\n    Throw(Throw),\n\n    /// See [`Try`].\n    Try(Try),\n\n    /// See [`With`].\n    With(With),\n\n    /// A `debugger` statement.\n    ///\n    /// The debugger statement invokes any available debugging functionality.\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///  - [MDN documentation][mdn]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-debugger-statement\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/debugger\n    Debugger,\n}\n\nimpl Statement {\n    /// Implements the display formatting with indentation.\n    ///\n    /// This will not prefix the value with any indentation. If you want to prefix this with proper\n    /// indents, use [`to_indented_string()`](Self::to_indented_string).\n    pub(super) fn to_no_indent_string(&self, interner: &Interner, indentation: usize) -> String {\n        let mut s = match self {\n            Self::Block(block) => return block.to_indented_string(interner, indentation),\n            Self::Var(var) => var.to_interned_string(interner),\n            Self::Empty => return \";\".to_owned(),\n            Self::Expression(expr) => expr.to_indented_string(interner, indentation),\n            Self::If(if_smt) => return if_smt.to_indented_string(interner, indentation),\n            Self::DoWhileLoop(do_while) => do_while.to_indented_string(interner, indentation),\n            Self::WhileLoop(while_loop) => {\n                return while_loop.to_indented_string(interner, indentation);\n            }\n            Self::ForLoop(for_loop) => return for_loop.to_indented_string(interner, indentation),\n            Self::ForInLoop(for_in) => return for_in.to_indented_string(interner, indentation),\n            Self::ForOfLoop(for_of) => return for_of.to_indented_string(interner, indentation),\n            Self::Switch(switch) => return switch.to_indented_string(interner, indentation),\n            Self::Continue(cont) => cont.to_interned_string(interner),\n            Self::Break(break_smt) => break_smt.to_interned_string(interner),\n            Self::Return(ret) => ret.to_interned_string(interner),\n            Self::Labelled(labelled) => return labelled.to_interned_string(interner),\n            Self::Throw(throw) => throw.to_interned_string(interner),\n            Self::Try(try_catch) => return try_catch.to_indented_string(interner, indentation),\n            Self::With(with) => return with.to_interned_string(interner),\n            Self::Debugger => \"debugger\".to_owned(),\n        };\n        s.push(';');\n        s\n    }\n\n    /// Abstract operation [`IsLabelledFunction`][spec].\n    ///\n    /// This recursively checks if this `Statement` is a labelled function, since adding\n    /// several labels in a function should not change the return value of the abstract operation:\n    ///\n    /// ```Javascript\n    /// l1: l2: l3: l4: function f(){ }\n    /// ```\n    ///\n    /// This should return `true` for that snippet.\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-islabelledfunction\n    #[inline]\n    #[must_use]\n    pub fn is_labelled_function(&self) -> bool {\n        match self {\n            Self::Labelled(stmt) => match stmt.item() {\n                LabelledItem::FunctionDeclaration(_) => true,\n                LabelledItem::Statement(stmt) => stmt.is_labelled_function(),\n            },\n            _ => false,\n        }\n    }\n}\n\nimpl ToIndentedString for Statement {\n    /// Creates a string of the value of the node with the given indentation. For example, an\n    /// indent level of 2 would produce this:\n    ///\n    /// ```js\n    ///         function hello() {\n    ///             console.log(\"hello\");\n    ///         }\n    ///         hello();\n    ///         a = 2;\n    /// ```\n    fn to_indented_string(&self, interner: &Interner, indentation: usize) -> String {\n        let mut buf = match *self {\n            Self::Block(_) => String::new(),\n            _ => \"    \".repeat(indentation),\n        };\n\n        buf.push_str(&self.to_no_indent_string(interner, indentation));\n\n        buf\n    }\n}\n\nimpl VisitWith for Statement {\n    fn visit_with<'a, V>(&'a self, visitor: &mut V) -> ControlFlow<V::BreakTy>\n    where\n        V: Visitor<'a>,\n    {\n        match self {\n            Self::Block(b) => visitor.visit_block(b),\n            Self::Var(v) => visitor.visit_var_declaration(v),\n            Self::Empty | Self::Debugger => {\n                // do nothing; there is nothing to visit here\n                ControlFlow::Continue(())\n            }\n            Self::Expression(e) => visitor.visit_expression(e),\n            Self::If(i) => visitor.visit_if(i),\n            Self::DoWhileLoop(dw) => visitor.visit_do_while_loop(dw),\n            Self::WhileLoop(w) => visitor.visit_while_loop(w),\n            Self::ForLoop(f) => visitor.visit_for_loop(f),\n            Self::ForInLoop(fi) => visitor.visit_for_in_loop(fi),\n            Self::ForOfLoop(fo) => visitor.visit_for_of_loop(fo),\n            Self::Switch(s) => visitor.visit_switch(s),\n            Self::Continue(c) => visitor.visit_continue(c),\n            Self::Break(b) => visitor.visit_break(b),\n            Self::Return(r) => visitor.visit_return(r),\n            Self::Labelled(l) => visitor.visit_labelled(l),\n            Self::Throw(th) => visitor.visit_throw(th),\n            Self::Try(tr) => visitor.visit_try(tr),\n            Self::With(with) => visitor.visit_with(with),\n        }\n    }\n\n    fn visit_with_mut<'a, V>(&'a mut self, visitor: &mut V) -> ControlFlow<V::BreakTy>\n    where\n        V: VisitorMut<'a>,\n    {\n        match self {\n            Self::Block(b) => visitor.visit_block_mut(b),\n            Self::Var(v) => visitor.visit_var_declaration_mut(v),\n            Self::Empty | Self::Debugger => {\n                // do nothing; there is nothing to visit here\n                ControlFlow::Continue(())\n            }\n            Self::Expression(e) => visitor.visit_expression_mut(e),\n            Self::If(i) => visitor.visit_if_mut(i),\n            Self::DoWhileLoop(dw) => visitor.visit_do_while_loop_mut(dw),\n            Self::WhileLoop(w) => visitor.visit_while_loop_mut(w),\n            Self::ForLoop(f) => visitor.visit_for_loop_mut(f),\n            Self::ForInLoop(fi) => visitor.visit_for_in_loop_mut(fi),\n            Self::ForOfLoop(fo) => visitor.visit_for_of_loop_mut(fo),\n            Self::Switch(s) => visitor.visit_switch_mut(s),\n            Self::Continue(c) => visitor.visit_continue_mut(c),\n            Self::Break(b) => visitor.visit_break_mut(b),\n            Self::Return(r) => visitor.visit_return_mut(r),\n            Self::Labelled(l) => visitor.visit_labelled_mut(l),\n            Self::Throw(th) => visitor.visit_throw_mut(th),\n            Self::Try(tr) => visitor.visit_try_mut(tr),\n            Self::With(with) => visitor.visit_with_mut(with),\n        }\n    }\n}\n"
  },
  {
    "path": "core/ast/src/statement/return.rs",
    "content": "use crate::{\n    expression::Expression,\n    statement::Statement,\n    visitor::{VisitWith, Visitor, VisitorMut},\n};\nuse boa_interner::{Interner, ToInternedString};\nuse core::ops::ControlFlow;\n\n/// The `return` statement ends function execution and specifies a value to be returned to the\n/// function caller.\n///\n/// Syntax: `return [expression];`\n///\n/// `expression`:\n///  > The expression whose value is to be returned. If omitted, `undefined` is returned instead.\n///\n/// When a `return` statement is used in a function body, the execution of the function is\n/// stopped. If specified, a given value is returned to the function caller.\n///\n/// More information:\n///  - [ECMAScript reference][spec]\n///  - [MDN documentation][mdn]\n///\n/// [spec]: https://tc39.es/ecma262/#prod-ReturnStatement\n/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/return\n#[cfg_attr(feature = \"serde\", derive(serde::Serialize, serde::Deserialize))]\n#[cfg_attr(feature = \"arbitrary\", derive(arbitrary::Arbitrary))]\n#[derive(Clone, Debug, PartialEq)]\npub struct Return {\n    target: Option<Expression>,\n}\n\nimpl Return {\n    /// Gets the target expression value of this `Return` statement.\n    #[must_use]\n    pub const fn target(&self) -> Option<&Expression> {\n        self.target.as_ref()\n    }\n\n    /// Creates a `Return` AST node.\n    #[must_use]\n    pub const fn new(expression: Option<Expression>) -> Self {\n        Self { target: expression }\n    }\n}\n\nimpl From<Return> for Statement {\n    fn from(return_smt: Return) -> Self {\n        Self::Return(return_smt)\n    }\n}\n\nimpl ToInternedString for Return {\n    fn to_interned_string(&self, interner: &Interner) -> String {\n        self.target().map_or_else(\n            || \"return\".to_owned(),\n            |ex| format!(\"return {}\", ex.to_interned_string(interner)),\n        )\n    }\n}\n\nimpl VisitWith for Return {\n    fn visit_with<'a, V>(&'a self, visitor: &mut V) -> ControlFlow<V::BreakTy>\n    where\n        V: Visitor<'a>,\n    {\n        if let Some(expr) = &self.target {\n            visitor.visit_expression(expr)\n        } else {\n            ControlFlow::Continue(())\n        }\n    }\n\n    fn visit_with_mut<'a, V>(&'a mut self, visitor: &mut V) -> ControlFlow<V::BreakTy>\n    where\n        V: VisitorMut<'a>,\n    {\n        if let Some(expr) = &mut self.target {\n            visitor.visit_expression_mut(expr)\n        } else {\n            ControlFlow::Continue(())\n        }\n    }\n}\n"
  },
  {
    "path": "core/ast/src/statement/switch.rs",
    "content": "//! Switch node.\n\nuse crate::{\n    StatementList,\n    expression::Expression,\n    operations::{ContainsSymbol, contains},\n    scope::Scope,\n    statement::Statement,\n    visitor::{VisitWith, Visitor, VisitorMut},\n};\nuse boa_interner::{Interner, ToIndentedString, ToInternedString};\nuse core::{fmt::Write as _, ops::ControlFlow};\n\n/// A case clause inside a [`Switch`] statement, as defined by the [spec].\n///\n/// Even though every [`Case`] body is a [`StatementList`], it doesn't create a new lexical\n/// environment. This means any variable declared in a `Case` will be considered as part of the\n/// lexical environment of the parent [`Switch`] block.\n///\n/// [spec]: https://tc39.es/ecma262/#prod-CaseClause\n/// [truthy]: https://developer.mozilla.org/en-US/docs/Glossary/Truthy\n#[cfg_attr(feature = \"serde\", derive(serde::Serialize, serde::Deserialize))]\n#[cfg_attr(feature = \"arbitrary\", derive(arbitrary::Arbitrary))]\n#[derive(Clone, Debug, PartialEq)]\npub struct Case {\n    condition: Option<Expression>,\n    body: StatementList,\n}\n\nimpl Case {\n    /// Creates a regular `Case` AST node.\n    #[inline]\n    #[must_use]\n    pub const fn new(condition: Expression, body: StatementList) -> Self {\n        Self {\n            condition: Some(condition),\n            body,\n        }\n    }\n\n    /// Creates a default `Case` AST node.\n    #[inline]\n    #[must_use]\n    pub const fn default(body: StatementList) -> Self {\n        Self {\n            condition: None,\n            body,\n        }\n    }\n\n    /// Gets the condition of the case.\n    ///\n    /// If it's a regular case returns [`Some`] with the [`Expression`],\n    /// otherwise [`None`] is returned if it's the default case.\n    #[inline]\n    #[must_use]\n    pub const fn condition(&self) -> Option<&Expression> {\n        self.condition.as_ref()\n    }\n\n    /// Gets the statement listin the body of the case.\n    #[inline]\n    #[must_use]\n    pub const fn body(&self) -> &StatementList {\n        &self.body\n    }\n\n    /// Check if the case is the `default` case.\n    #[inline]\n    #[must_use]\n    pub const fn is_default(&self) -> bool {\n        self.condition.is_none()\n    }\n}\n\nimpl VisitWith for Case {\n    fn visit_with<'a, V>(&'a self, visitor: &mut V) -> ControlFlow<V::BreakTy>\n    where\n        V: Visitor<'a>,\n    {\n        if let Some(condition) = &self.condition {\n            visitor.visit_expression(condition)?;\n        }\n\n        visitor.visit_statement_list(&self.body)\n    }\n\n    fn visit_with_mut<'a, V>(&'a mut self, visitor: &mut V) -> ControlFlow<V::BreakTy>\n    where\n        V: VisitorMut<'a>,\n    {\n        if let Some(condition) = &mut self.condition {\n            visitor.visit_expression_mut(condition)?;\n        }\n        visitor.visit_statement_list_mut(&mut self.body)\n    }\n}\n\n/// The `switch` statement evaluates an expression, matching the expression's value to a case\n/// clause, and executes statements associated with that case, as well as statements in cases\n/// that follow the matching case.\n///\n/// A `switch` statement first evaluates its expression. It then looks for the first case\n/// clause whose expression evaluates to the same value as the result of the input expression\n/// (using the strict comparison, `===`) and transfers control to that clause, executing the\n/// associated statements. (If multiple cases match the provided value, the first case that\n/// matches is selected, even if the cases are not equal to each other.)\n///\n/// More information:\n///  - [ECMAScript reference][spec]\n///  - [MDN documentation][mdn]\n///\n/// [spec]: https://tc39.es/ecma262/#prod-SwitchStatement\n/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/switch\n#[cfg_attr(feature = \"serde\", derive(serde::Serialize, serde::Deserialize))]\n#[cfg_attr(feature = \"arbitrary\", derive(arbitrary::Arbitrary))]\n#[derive(Clone, Debug, PartialEq)]\npub struct Switch {\n    pub(crate) val: Expression,\n    pub(crate) cases: Box<[Case]>,\n    pub(crate) contains_direct_eval: bool,\n\n    #[cfg_attr(feature = \"serde\", serde(skip))]\n    pub(crate) scope: Option<Scope>,\n}\n\nimpl Switch {\n    /// Creates a `Switch` AST node.\n    #[inline]\n    #[must_use]\n    pub fn new(val: Expression, cases: Box<[Case]>) -> Self {\n        let mut contains_direct_eval = false;\n        for case in &cases {\n            contains_direct_eval |= contains(case, ContainsSymbol::DirectEval);\n        }\n        Self {\n            val,\n            cases,\n            contains_direct_eval,\n            scope: None,\n        }\n    }\n\n    /// Gets the value to switch.\n    #[inline]\n    #[must_use]\n    pub const fn val(&self) -> &Expression {\n        &self.val\n    }\n\n    /// Gets the list of cases for the switch statement.\n    #[inline]\n    #[must_use]\n    pub const fn cases(&self) -> &[Case] {\n        &self.cases\n    }\n\n    /// Gets the default statement list, if any.\n    #[inline]\n    #[must_use]\n    pub fn default(&self) -> Option<&StatementList> {\n        for case in self.cases.as_ref() {\n            if case.is_default() {\n                return Some(case.body());\n            }\n        }\n        None\n    }\n\n    /// Gets the scope of the switch statement.\n    #[inline]\n    #[must_use]\n    pub const fn scope(&self) -> Option<&Scope> {\n        self.scope.as_ref()\n    }\n}\n\nimpl ToIndentedString for Switch {\n    fn to_indented_string(&self, interner: &Interner, indentation: usize) -> String {\n        let indent = \"    \".repeat(indentation);\n        let mut buf = format!(\"switch ({}) {{\\n\", self.val().to_interned_string(interner));\n        for e in &*self.cases {\n            if let Some(condition) = e.condition() {\n                let _ = write!(\n                    buf,\n                    \"{indent}    case {}:\\n{}\",\n                    condition.to_interned_string(interner),\n                    e.body().to_indented_string(interner, indentation + 2)\n                );\n            } else {\n                let _ = write!(\n                    buf,\n                    \"{indent}    default:\\n{}\",\n                    e.body().to_indented_string(interner, indentation + 2)\n                );\n            }\n        }\n\n        let _ = write!(buf, \"{indent}}}\");\n\n        buf\n    }\n}\n\nimpl From<Switch> for Statement {\n    #[inline]\n    fn from(switch: Switch) -> Self {\n        Self::Switch(switch)\n    }\n}\n\nimpl VisitWith for Switch {\n    fn visit_with<'a, V>(&'a self, visitor: &mut V) -> ControlFlow<V::BreakTy>\n    where\n        V: Visitor<'a>,\n    {\n        visitor.visit_expression(&self.val)?;\n        for case in &*self.cases {\n            visitor.visit_case(case)?;\n        }\n        ControlFlow::Continue(())\n    }\n\n    fn visit_with_mut<'a, V>(&'a mut self, visitor: &mut V) -> ControlFlow<V::BreakTy>\n    where\n        V: VisitorMut<'a>,\n    {\n        visitor.visit_expression_mut(&mut self.val)?;\n        for case in &mut *self.cases {\n            visitor.visit_case_mut(case)?;\n        }\n        ControlFlow::Continue(())\n    }\n}\n"
  },
  {
    "path": "core/ast/src/statement/throw.rs",
    "content": "use crate::{\n    Expression,\n    statement::Statement,\n    visitor::{VisitWith, Visitor, VisitorMut},\n};\nuse boa_interner::{Interner, ToInternedString};\nuse core::ops::ControlFlow;\n\n/// The `throw` statement throws a user-defined exception.\n///\n/// Syntax: `throw expression;`\n///\n/// Execution of the current function will stop (the statements after throw won't be executed),\n/// and control will be passed to the first catch block in the call stack. If no catch block\n/// exists among caller functions, the program will terminate.\n///\n/// More information:\n///  - [ECMAScript reference][spec]\n///  - [MDN documentation][mdn]\n///\n/// [spec]: https://tc39.es/ecma262/#prod-ThrowStatement\n/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/throw\n#[cfg_attr(feature = \"serde\", derive(serde::Serialize, serde::Deserialize))]\n#[cfg_attr(feature = \"arbitrary\", derive(arbitrary::Arbitrary))]\n#[derive(Clone, Debug, PartialEq)]\npub struct Throw {\n    target: Expression,\n}\n\nimpl Throw {\n    /// Gets the target expression of this `Throw` statement.\n    #[must_use]\n    pub const fn target(&self) -> &Expression {\n        &self.target\n    }\n\n    /// Creates a `Throw` AST node.\n    #[must_use]\n    pub const fn new(target: Expression) -> Self {\n        Self { target }\n    }\n}\n\nimpl ToInternedString for Throw {\n    fn to_interned_string(&self, interner: &Interner) -> String {\n        format!(\"throw {}\", self.target.to_interned_string(interner))\n    }\n}\n\nimpl From<Throw> for Statement {\n    fn from(trw: Throw) -> Self {\n        Self::Throw(trw)\n    }\n}\n\nimpl VisitWith for Throw {\n    fn visit_with<'a, V>(&'a self, visitor: &mut V) -> ControlFlow<V::BreakTy>\n    where\n        V: Visitor<'a>,\n    {\n        visitor.visit_expression(&self.target)\n    }\n\n    fn visit_with_mut<'a, V>(&'a mut self, visitor: &mut V) -> ControlFlow<V::BreakTy>\n    where\n        V: VisitorMut<'a>,\n    {\n        visitor.visit_expression_mut(&mut self.target)\n    }\n}\n"
  },
  {
    "path": "core/ast/src/statement/try.rs",
    "content": "//! Error handling statements\n\nuse crate::operations::{ContainsSymbol, contains};\nuse crate::scope::Scope;\nuse crate::visitor::{VisitWith, Visitor, VisitorMut};\nuse crate::{\n    declaration::Binding,\n    statement::{Block, Statement},\n};\nuse boa_interner::{Interner, ToIndentedString, ToInternedString};\nuse core::{fmt::Write as _, ops::ControlFlow};\n\n/// The `try...catch` statement marks a block of statements to try and specifies a response\n/// should an exception be thrown.\n///\n/// The `try` statement consists of a `try`-block, which contains one or more statements. `{}`\n/// must always be used, even for single statements. At least one `catch`-block, or a\n/// `finally`-block, must be present.\n///\n/// More information:\n///  - [ECMAScript reference][spec]\n///  - [MDN documentation][mdn]\n///\n/// [spec]: https://tc39.es/ecma262/#prod-TryStatement\n/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/try...catch\n#[cfg_attr(feature = \"serde\", derive(serde::Serialize, serde::Deserialize))]\n#[cfg_attr(feature = \"arbitrary\", derive(arbitrary::Arbitrary))]\n#[derive(Clone, Debug, PartialEq)]\npub struct Try {\n    block: Block,\n    handler: ErrorHandler,\n}\n\n/// The type of error handler in a [`Try`] statement.\n#[cfg_attr(feature = \"serde\", derive(serde::Serialize, serde::Deserialize))]\n#[cfg_attr(feature = \"arbitrary\", derive(arbitrary::Arbitrary))]\n#[derive(Clone, Debug, PartialEq)]\npub enum ErrorHandler {\n    /// A [`Catch`] error handler.\n    Catch(Catch),\n    /// A [`Finally`] error handler.\n    Finally(Finally),\n    /// A [`Catch`] and [`Finally`] error handler.\n    Full(Catch, Finally),\n}\n\nimpl Try {\n    /// Creates a new `Try` AST node.\n    #[inline]\n    #[must_use]\n    pub const fn new(block: Block, handler: ErrorHandler) -> Self {\n        Self { block, handler }\n    }\n\n    /// Gets the `try` block.\n    #[inline]\n    #[must_use]\n    pub const fn block(&self) -> &Block {\n        &self.block\n    }\n\n    /// Gets the `catch` block, if any.\n    #[inline]\n    #[must_use]\n    pub const fn catch(&self) -> Option<&Catch> {\n        match &self.handler {\n            ErrorHandler::Catch(c) | ErrorHandler::Full(c, _) => Some(c),\n            ErrorHandler::Finally(_) => None,\n        }\n    }\n\n    /// Gets the `finally` block, if any.\n    #[inline]\n    #[must_use]\n    pub const fn finally(&self) -> Option<&Finally> {\n        match &self.handler {\n            ErrorHandler::Finally(f) | ErrorHandler::Full(_, f) => Some(f),\n            ErrorHandler::Catch(_) => None,\n        }\n    }\n}\n\nimpl ToIndentedString for Try {\n    fn to_indented_string(&self, interner: &Interner, indentation: usize) -> String {\n        let mut buf = format!(\n            \"{}try {}\",\n            \"    \".repeat(indentation),\n            self.block.to_indented_string(interner, indentation)\n        );\n\n        if let Some(catch) = self.catch() {\n            buf.push_str(&catch.to_indented_string(interner, indentation));\n        }\n\n        if let Some(finally) = self.finally() {\n            buf.push_str(&finally.to_indented_string(interner, indentation));\n        }\n        buf\n    }\n}\n\nimpl From<Try> for Statement {\n    #[inline]\n    fn from(try_catch: Try) -> Self {\n        Self::Try(try_catch)\n    }\n}\n\nimpl VisitWith for Try {\n    fn visit_with<'a, V>(&'a self, visitor: &mut V) -> ControlFlow<V::BreakTy>\n    where\n        V: Visitor<'a>,\n    {\n        visitor.visit_block(&self.block)?;\n        if let Some(catch) = &self.catch() {\n            visitor.visit_catch(catch)?;\n        }\n        if let Some(finally) = &self.finally() {\n            visitor.visit_finally(finally)?;\n        }\n        ControlFlow::Continue(())\n    }\n\n    fn visit_with_mut<'a, V>(&'a mut self, visitor: &mut V) -> ControlFlow<V::BreakTy>\n    where\n        V: VisitorMut<'a>,\n    {\n        visitor.visit_block_mut(&mut self.block)?;\n        match &mut self.handler {\n            ErrorHandler::Catch(c) => visitor.visit_catch_mut(c)?,\n            ErrorHandler::Finally(f) => visitor.visit_finally_mut(f)?,\n            ErrorHandler::Full(c, f) => {\n                visitor.visit_catch_mut(c)?;\n                visitor.visit_finally_mut(f)?;\n            }\n        }\n        ControlFlow::Continue(())\n    }\n}\n\n/// Catch block.\n#[cfg_attr(feature = \"serde\", derive(serde::Serialize, serde::Deserialize))]\n#[cfg_attr(feature = \"arbitrary\", derive(arbitrary::Arbitrary))]\n#[derive(Clone, Debug, PartialEq)]\npub struct Catch {\n    pub(crate) parameter: Option<Binding>,\n    pub(crate) block: Block,\n    pub(crate) contains_direct_eval: bool,\n\n    #[cfg_attr(feature = \"serde\", serde(skip))]\n    pub(crate) scope: Scope,\n}\n\nimpl Catch {\n    /// Creates a new catch block.\n    #[inline]\n    #[must_use]\n    pub fn new(parameter: Option<Binding>, block: Block) -> Self {\n        let mut contains_direct_eval = contains(&block, ContainsSymbol::DirectEval);\n        if let Some(param) = &parameter {\n            contains_direct_eval |= contains(param, ContainsSymbol::DirectEval);\n        }\n        Self {\n            parameter,\n            block,\n            contains_direct_eval,\n            scope: Scope::default(),\n        }\n    }\n\n    /// Gets the parameter of the catch block.\n    #[inline]\n    #[must_use]\n    pub const fn parameter(&self) -> Option<&Binding> {\n        self.parameter.as_ref()\n    }\n\n    /// Retrieves the catch execution block.\n    #[inline]\n    #[must_use]\n    pub const fn block(&self) -> &Block {\n        &self.block\n    }\n\n    /// Returns the scope of the catch block.\n    #[inline]\n    #[must_use]\n    pub const fn scope(&self) -> &Scope {\n        &self.scope\n    }\n}\n\nimpl ToIndentedString for Catch {\n    fn to_indented_string(&self, interner: &Interner, indentation: usize) -> String {\n        let mut buf = \" catch\".to_owned();\n        if let Some(param) = &self.parameter {\n            let _ = write!(buf, \"({})\", param.to_interned_string(interner));\n        }\n        let _ = write!(\n            buf,\n            \" {}\",\n            self.block.to_indented_string(interner, indentation)\n        );\n\n        buf\n    }\n}\n\nimpl VisitWith for Catch {\n    fn visit_with<'a, V>(&'a self, visitor: &mut V) -> ControlFlow<V::BreakTy>\n    where\n        V: Visitor<'a>,\n    {\n        if let Some(binding) = &self.parameter {\n            visitor.visit_binding(binding)?;\n        }\n        visitor.visit_block(&self.block)\n    }\n\n    fn visit_with_mut<'a, V>(&'a mut self, visitor: &mut V) -> ControlFlow<V::BreakTy>\n    where\n        V: VisitorMut<'a>,\n    {\n        if let Some(binding) = &mut self.parameter {\n            visitor.visit_binding_mut(binding)?;\n        }\n        visitor.visit_block_mut(&mut self.block)\n    }\n}\n\n/// Finally block.\n#[cfg_attr(feature = \"serde\", derive(serde::Serialize, serde::Deserialize))]\n#[cfg_attr(feature = \"arbitrary\", derive(arbitrary::Arbitrary))]\n#[derive(Clone, Debug, PartialEq)]\npub struct Finally {\n    block: Block,\n}\n\nimpl Finally {\n    /// Gets the finally block.\n    #[inline]\n    #[must_use]\n    pub const fn block(&self) -> &Block {\n        &self.block\n    }\n}\n\nimpl ToIndentedString for Finally {\n    fn to_indented_string(&self, interner: &Interner, indentation: usize) -> String {\n        format!(\n            \" finally {}\",\n            self.block.to_indented_string(interner, indentation)\n        )\n    }\n}\n\nimpl From<Block> for Finally {\n    #[inline]\n    fn from(block: Block) -> Self {\n        Self { block }\n    }\n}\n\nimpl VisitWith for Finally {\n    fn visit_with<'a, V>(&'a self, visitor: &mut V) -> ControlFlow<V::BreakTy>\n    where\n        V: Visitor<'a>,\n    {\n        visitor.visit_block(&self.block)\n    }\n\n    fn visit_with_mut<'a, V>(&'a mut self, visitor: &mut V) -> ControlFlow<V::BreakTy>\n    where\n        V: VisitorMut<'a>,\n    {\n        visitor.visit_block_mut(&mut self.block)\n    }\n}\n"
  },
  {
    "path": "core/ast/src/statement/with.rs",
    "content": "use crate::{\n    expression::Expression,\n    scope::Scope,\n    statement::Statement,\n    visitor::{VisitWith, Visitor, VisitorMut},\n};\nuse boa_interner::{Interner, ToIndentedString, ToInternedString};\nuse core::ops::ControlFlow;\n\n/// The `with` statement extends the scope chain for a statement.\n///\n/// More information:\n///  - [ECMAScript reference][spec]\n///  - [MDN documentation][mdn]\n///\n/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/with\n/// [spec]: https://tc39.es/ecma262/#prod-WithStatement\n#[cfg_attr(feature = \"serde\", derive(serde::Serialize, serde::Deserialize))]\n#[cfg_attr(feature = \"arbitrary\", derive(arbitrary::Arbitrary))]\n#[derive(Clone, Debug, PartialEq)]\npub struct With {\n    pub(crate) expression: Expression,\n    pub(crate) statement: Box<Statement>,\n\n    #[cfg_attr(feature = \"serde\", serde(skip))]\n    pub(crate) scope: Scope,\n}\n\nimpl With {\n    /// Creates a `With` AST node.\n    #[must_use]\n    pub fn new(expression: Expression, statement: Statement) -> Self {\n        Self {\n            expression,\n            statement: Box::new(statement),\n            scope: Scope::default(),\n        }\n    }\n\n    /// Gets the expression value of this `With` statement.\n    #[must_use]\n    pub const fn expression(&self) -> &Expression {\n        &self.expression\n    }\n\n    /// Gets the statement value of this `With` statement.\n    #[must_use]\n    pub const fn statement(&self) -> &Statement {\n        &self.statement\n    }\n\n    /// Returns the scope of the `With` statement.\n    #[must_use]\n    pub const fn scope(&self) -> &Scope {\n        &self.scope\n    }\n}\n\nimpl From<With> for Statement {\n    fn from(with: With) -> Self {\n        Self::With(with)\n    }\n}\n\nimpl ToIndentedString for With {\n    fn to_indented_string(&self, interner: &Interner, indentation: usize) -> String {\n        format!(\n            \"with ({}) {}\",\n            self.expression().to_interned_string(interner),\n            self.statement().to_indented_string(interner, indentation)\n        )\n    }\n}\n\nimpl VisitWith for With {\n    fn visit_with<'a, V>(&'a self, visitor: &mut V) -> ControlFlow<V::BreakTy>\n    where\n        V: Visitor<'a>,\n    {\n        visitor.visit_expression(&self.expression)?;\n        visitor.visit_statement(&self.statement)\n    }\n\n    fn visit_with_mut<'a, V>(&'a mut self, visitor: &mut V) -> ControlFlow<V::BreakTy>\n    where\n        V: VisitorMut<'a>,\n    {\n        visitor.visit_expression_mut(&mut self.expression)?;\n        visitor.visit_statement_mut(&mut self.statement)\n    }\n}\n"
  },
  {
    "path": "core/ast/src/statement_list.rs",
    "content": "//! Statement list node.\n\nuse super::Declaration;\nuse crate::{\n    LinearPosition,\n    statement::Statement,\n    visitor::{VisitWith, Visitor, VisitorMut},\n};\nuse boa_interner::{Interner, ToIndentedString};\nuse core::ops::ControlFlow;\nuse std::ops::Deref;\n\n/// An item inside a [`StatementList`] Parse Node, as defined by the [spec].\n///\n/// Items in a `StatementList` can be either [`Declaration`]s (functions, classes, let/const declarations)\n/// or [`Statement`]s (if, while, var statement).\n///\n/// [spec]: https://tc39.es/ecma262/#prod-StatementListItem\n#[cfg_attr(feature = \"serde\", derive(serde::Serialize, serde::Deserialize))]\n#[cfg_attr(feature = \"arbitrary\", derive(arbitrary::Arbitrary))]\n#[derive(Clone, Debug, PartialEq)]\npub enum StatementListItem {\n    /// See [`Statement`].\n    Statement(Box<Statement>),\n    /// See [`Declaration`].\n    Declaration(Box<Declaration>),\n}\n\nimpl ToIndentedString for StatementListItem {\n    /// Creates a string of the value of the node with the given indentation. For example, an\n    /// indent level of 2 would produce this:\n    ///\n    /// ```js\n    ///         function hello() {\n    ///             console.log(\"hello\");\n    ///         }\n    ///         hello();\n    ///         a = 2;\n    /// ```\n    fn to_indented_string(&self, interner: &Interner, indentation: usize) -> String {\n        let mut buf = \"    \".repeat(indentation);\n\n        match self {\n            Self::Statement(stmt) => {\n                buf.push_str(&stmt.to_no_indent_string(interner, indentation));\n            }\n            Self::Declaration(decl) => {\n                buf.push_str(&decl.to_indented_string(interner, indentation));\n            }\n        }\n\n        buf\n    }\n}\n\nimpl From<Statement> for StatementListItem {\n    #[inline]\n    fn from(stmt: Statement) -> Self {\n        Self::Statement(Box::new(stmt))\n    }\n}\n\nimpl From<Declaration> for StatementListItem {\n    #[inline]\n    fn from(decl: Declaration) -> Self {\n        Self::Declaration(Box::new(decl))\n    }\n}\n\nimpl VisitWith for StatementListItem {\n    fn visit_with<'a, V>(&'a self, visitor: &mut V) -> ControlFlow<V::BreakTy>\n    where\n        V: Visitor<'a>,\n    {\n        match self {\n            Self::Statement(statement) => visitor.visit_statement(statement),\n            Self::Declaration(declaration) => visitor.visit_declaration(declaration),\n        }\n    }\n\n    fn visit_with_mut<'a, V>(&'a mut self, visitor: &mut V) -> ControlFlow<V::BreakTy>\n    where\n        V: VisitorMut<'a>,\n    {\n        match self {\n            Self::Statement(statement) => visitor.visit_statement_mut(statement),\n            Self::Declaration(declaration) => visitor.visit_declaration_mut(declaration),\n        }\n    }\n}\n\n/// List of statements.\n///\n/// More information:\n///  - [ECMAScript reference][spec]\n///\n/// [spec]: https://tc39.es/ecma262/#prod-StatementList\n#[cfg_attr(feature = \"serde\", derive(serde::Serialize, serde::Deserialize))]\n#[derive(Clone, Debug, Default)]\npub struct StatementList {\n    pub(crate) statements: Box<[StatementListItem]>,\n    linear_pos_end: LinearPosition,\n    strict: bool,\n}\n\nimpl PartialEq for StatementList {\n    fn eq(&self, other: &Self) -> bool {\n        self.statements == other.statements && self.strict == other.strict\n    }\n}\n\nimpl StatementList {\n    /// Creates a new `StatementList` AST node.\n    #[must_use]\n    pub fn new<S>(statements: S, linear_pos_end: LinearPosition, strict: bool) -> Self\n    where\n        S: Into<Box<[StatementListItem]>>,\n    {\n        Self {\n            statements: statements.into(),\n            linear_pos_end,\n            strict,\n        }\n    }\n\n    /// Gets the list of statements.\n    #[inline]\n    #[must_use]\n    pub const fn statements(&self) -> &[StatementListItem] {\n        &self.statements\n    }\n\n    /// Get the strict mode.\n    #[inline]\n    #[must_use]\n    pub const fn strict(&self) -> bool {\n        self.strict\n    }\n\n    /// Get end of linear position in source code.\n    #[inline]\n    #[must_use]\n    pub const fn linear_pos_end(&self) -> LinearPosition {\n        self.linear_pos_end\n    }\n}\n\nimpl From<(Box<[StatementListItem]>, LinearPosition)> for StatementList {\n    #[inline]\n    fn from(value: (Box<[StatementListItem]>, LinearPosition)) -> Self {\n        Self {\n            statements: value.0,\n            linear_pos_end: value.1,\n            strict: false,\n        }\n    }\n}\n\nimpl From<(Vec<StatementListItem>, LinearPosition)> for StatementList {\n    #[inline]\n    fn from(value: (Vec<StatementListItem>, LinearPosition)) -> Self {\n        Self {\n            statements: value.0.into(),\n            linear_pos_end: value.1,\n            strict: false,\n        }\n    }\n}\n\nimpl Deref for StatementList {\n    type Target = [StatementListItem];\n\n    fn deref(&self) -> &Self::Target {\n        &self.statements\n    }\n}\n\nimpl ToIndentedString for StatementList {\n    fn to_indented_string(&self, interner: &Interner, indentation: usize) -> String {\n        let mut buf = String::new();\n        // Print statements\n        for item in &*self.statements {\n            // We rely on the node to add the correct indent.\n            buf.push_str(&item.to_indented_string(interner, indentation));\n\n            buf.push('\\n');\n        }\n        buf\n    }\n}\n\nimpl VisitWith for StatementList {\n    fn visit_with<'a, V>(&'a self, visitor: &mut V) -> ControlFlow<V::BreakTy>\n    where\n        V: Visitor<'a>,\n    {\n        for statement in &*self.statements {\n            visitor.visit_statement_list_item(statement)?;\n        }\n        ControlFlow::Continue(())\n    }\n\n    fn visit_with_mut<'a, V>(&'a mut self, visitor: &mut V) -> ControlFlow<V::BreakTy>\n    where\n        V: VisitorMut<'a>,\n    {\n        for statement in &mut *self.statements {\n            visitor.visit_statement_list_item_mut(statement)?;\n        }\n        ControlFlow::Continue(())\n    }\n}\n\n#[cfg(feature = \"arbitrary\")]\nimpl<'a> arbitrary::Arbitrary<'a> for StatementList {\n    fn arbitrary(u: &mut arbitrary::Unstructured<'a>) -> arbitrary::Result<Self> {\n        Ok(Self {\n            statements: u.arbitrary()?,\n            linear_pos_end: LinearPosition::default(),\n            strict: false, // disable strictness; this is *not* in source data\n        })\n    }\n}\n"
  },
  {
    "path": "core/ast/src/visitor.rs",
    "content": "//! ECMAScript Abstract Syntax Tree visitors.\n//!\n//! This module contains visitors which can be used to inspect or modify AST nodes. This allows for\n//! fine-grained manipulation of ASTs for analysis, rewriting, or instrumentation.\n\nuse std::ops::ControlFlow;\n\nuse crate::{\n    Module, ModuleItem, ModuleItemList, Script, StatementList, StatementListItem,\n    declaration::{\n        Binding, Declaration, ExportDeclaration, ExportSpecifier, ImportAttribute,\n        ImportDeclaration, ImportKind, ImportSpecifier, LexicalDeclaration, ModuleSpecifier,\n        ReExportKind, VarDeclaration, Variable, VariableList,\n    },\n    expression::{\n        Await, Call, Expression, Identifier, ImportCall, ImportMeta, New, NewTarget, Optional,\n        OptionalOperation, OptionalOperationKind, Parenthesized, RegExpLiteral, Spread, SuperCall,\n        TaggedTemplate, This, Yield,\n        access::{\n            PrivatePropertyAccess, PropertyAccess, PropertyAccessField, SimplePropertyAccess,\n            SuperPropertyAccess,\n        },\n        literal::{\n            ArrayLiteral, Literal, ObjectLiteral, ObjectMethodDefinition, PropertyDefinition,\n            TemplateElement, TemplateLiteral,\n        },\n        operator::{\n            Binary, BinaryInPrivate, Conditional, Unary, Update,\n            assign::{Assign, AssignTarget},\n        },\n    },\n    function::{\n        ArrowFunction, AsyncArrowFunction, AsyncFunctionDeclaration, AsyncFunctionExpression,\n        AsyncGeneratorDeclaration, AsyncGeneratorExpression, ClassDeclaration, ClassElement,\n        ClassExpression, FormalParameter, FormalParameterList, FunctionBody, FunctionDeclaration,\n        FunctionExpression, GeneratorDeclaration, GeneratorExpression, PrivateName,\n    },\n    pattern::{ArrayPattern, ArrayPatternElement, ObjectPattern, ObjectPatternElement, Pattern},\n    property::PropertyName,\n    statement::{\n        Block, Case, Catch, Finally, If, Labelled, LabelledItem, Return, Statement, Switch, Throw,\n        Try, With,\n        iteration::{\n            Break, Continue, DoWhileLoop, ForInLoop, ForLoop, ForLoopInitializer, ForOfLoop,\n            IterableLoopInitializer, WhileLoop,\n        },\n    },\n};\nuse boa_interner::Sym;\n\n/// Creates the default visit function implementation for a particular type\nmacro_rules! define_visit {\n    ($fn_name:ident, $type_name:ident) => {\n        #[doc = concat!(\"Visits a `\", stringify!($type_name), \"` with this visitor\")]\n        fn $fn_name(&mut self, node: &'ast $type_name) -> ControlFlow<Self::BreakTy> {\n            node.visit_with(self)\n        }\n    };\n}\n\n/// Creates the default mutable visit function implementation for a particular type\nmacro_rules! define_visit_mut {\n    ($fn_name:ident, $type_name:ident) => {\n        #[doc = concat!(\"Visits a `\", stringify!($type_name), \"` with this visitor, mutably\")]\n        fn $fn_name(&mut self, node: &'ast mut $type_name) -> ControlFlow<Self::BreakTy> {\n            node.visit_with_mut(self)\n        }\n    };\n}\n\n/// Generates the `NodeRef` and `NodeMutRef` enums from a list of variants.\nmacro_rules! node_ref {\n    (\n        $(\n            $Variant:ident\n        ),*\n        $(,)?\n    ) => {\n        /// A reference to a node visitable by a [`Visitor`].\n        #[derive(Debug, Clone, Copy)]\n        #[allow(missing_docs)]\n        pub enum NodeRef<'a> {\n            $(\n                $Variant(&'a $Variant)\n            ),*\n        }\n\n        $(\n            impl<'a> From<&'a $Variant> for NodeRef<'a> {\n                fn from(node: &'a $Variant) -> NodeRef<'a> {\n                    Self::$Variant(node)\n                }\n            }\n        )*\n\n        /// A mutable reference to a node visitable by a [`VisitorMut`].\n        #[derive(Debug)]\n        #[allow(missing_docs)]\n        pub enum NodeRefMut<'a> {\n            $(\n                $Variant(&'a mut $Variant)\n            ),*\n        }\n\n        $(\n            impl<'a> From<&'a mut $Variant> for NodeRefMut<'a> {\n                fn from(node: &'a mut $Variant) -> NodeRefMut<'a> {\n                    Self::$Variant(node)\n                }\n            }\n        )*\n    }\n}\n\nnode_ref! {\n    Script,\n    Module,\n    FunctionBody,\n    StatementList,\n    StatementListItem,\n    Statement,\n    Declaration,\n    FunctionExpression,\n    FunctionDeclaration,\n    GeneratorExpression,\n    GeneratorDeclaration,\n    AsyncFunctionExpression,\n    AsyncFunctionDeclaration,\n    AsyncGeneratorExpression,\n    AsyncGeneratorDeclaration,\n    ClassExpression,\n    ClassDeclaration,\n    LexicalDeclaration,\n    Block,\n    VarDeclaration,\n    Expression,\n    If,\n    DoWhileLoop,\n    WhileLoop,\n    ForLoop,\n    ForInLoop,\n    ForOfLoop,\n    Switch,\n    Continue,\n    Break,\n    Return,\n    Labelled,\n    With,\n    Throw,\n    Try,\n    This,\n    NewTarget,\n    ImportMeta,\n    Identifier,\n    FormalParameterList,\n    ClassElement,\n    PrivateName,\n    VariableList,\n    Variable,\n    Binding,\n    Pattern,\n    Literal,\n    RegExpLiteral,\n    ArrayLiteral,\n    ObjectLiteral,\n    Spread,\n    ArrowFunction,\n    AsyncArrowFunction,\n    TemplateLiteral,\n    PropertyAccess,\n    New,\n    Call,\n    SuperCall,\n    ImportCall,\n    Optional,\n    TaggedTemplate,\n    Assign,\n    Unary,\n    Update,\n    Binary,\n    BinaryInPrivate,\n    Conditional,\n    Await,\n    Yield,\n    Parenthesized,\n    ForLoopInitializer,\n    IterableLoopInitializer,\n    Case,\n    Sym,\n    LabelledItem,\n    Catch,\n    Finally,\n    FormalParameter,\n    PropertyName,\n    ObjectMethodDefinition,\n    ObjectPattern,\n    ArrayPattern,\n    PropertyDefinition,\n    TemplateElement,\n    SimplePropertyAccess,\n    PrivatePropertyAccess,\n    SuperPropertyAccess,\n    OptionalOperation,\n    AssignTarget,\n    ObjectPatternElement,\n    ArrayPatternElement,\n    PropertyAccessField,\n    OptionalOperationKind,\n    ModuleItemList,\n    ModuleItem,\n    ModuleSpecifier,\n    ImportAttribute,\n    ImportKind,\n    ImportDeclaration,\n    ImportSpecifier,\n    ReExportKind,\n    ExportDeclaration,\n    ExportSpecifier,\n}\n\n/// Represents an AST visitor.\n///\n/// This implementation is based largely on [chalk](https://github.com/rust-lang/chalk/blob/23d39c90ceb9242fbd4c43e9368e813e7c2179f7/chalk-ir/src/visit.rs)'s\n/// visitor pattern.\npub trait Visitor<'ast>: Sized {\n    /// Type which will be propagated from the visitor if completing early.\n    type BreakTy;\n\n    define_visit!(visit_script, Script);\n    define_visit!(visit_module, Module);\n    define_visit!(visit_function_body, FunctionBody);\n    define_visit!(visit_statement_list, StatementList);\n    define_visit!(visit_statement_list_item, StatementListItem);\n    define_visit!(visit_statement, Statement);\n    define_visit!(visit_declaration, Declaration);\n    define_visit!(visit_function_expression, FunctionExpression);\n    define_visit!(visit_function_declaration, FunctionDeclaration);\n    define_visit!(visit_generator_expression, GeneratorExpression);\n    define_visit!(visit_generator_declaration, GeneratorDeclaration);\n    define_visit!(visit_async_function_expression, AsyncFunctionExpression);\n    define_visit!(visit_async_function_declaration, AsyncFunctionDeclaration);\n    define_visit!(visit_async_generator_expression, AsyncGeneratorExpression);\n    define_visit!(visit_async_generator_declaration, AsyncGeneratorDeclaration);\n    define_visit!(visit_class_expression, ClassExpression);\n    define_visit!(visit_class_declaration, ClassDeclaration);\n    define_visit!(visit_lexical_declaration, LexicalDeclaration);\n    define_visit!(visit_block, Block);\n    define_visit!(visit_var_declaration, VarDeclaration);\n    define_visit!(visit_expression, Expression);\n    define_visit!(visit_if, If);\n    define_visit!(visit_do_while_loop, DoWhileLoop);\n    define_visit!(visit_while_loop, WhileLoop);\n    define_visit!(visit_for_loop, ForLoop);\n    define_visit!(visit_for_in_loop, ForInLoop);\n    define_visit!(visit_for_of_loop, ForOfLoop);\n    define_visit!(visit_switch, Switch);\n    define_visit!(visit_continue, Continue);\n    define_visit!(visit_break, Break);\n    define_visit!(visit_return, Return);\n    define_visit!(visit_labelled, Labelled);\n    define_visit!(visit_throw, Throw);\n    define_visit!(visit_try, Try);\n    define_visit!(visit_with, With);\n    define_visit!(visit_this, This);\n    define_visit!(visit_identifier, Identifier);\n    define_visit!(visit_formal_parameter_list, FormalParameterList);\n    define_visit!(visit_class_element, ClassElement);\n    define_visit!(visit_private_name, PrivateName);\n    define_visit!(visit_variable_list, VariableList);\n    define_visit!(visit_variable, Variable);\n    define_visit!(visit_binding, Binding);\n    define_visit!(visit_pattern, Pattern);\n    define_visit!(visit_literal, Literal);\n    define_visit!(visit_reg_exp_literal, RegExpLiteral);\n    define_visit!(visit_array_literal, ArrayLiteral);\n    define_visit!(visit_object_literal, ObjectLiteral);\n    define_visit!(visit_spread, Spread);\n    define_visit!(visit_arrow_function, ArrowFunction);\n    define_visit!(visit_async_arrow_function, AsyncArrowFunction);\n    define_visit!(visit_template_literal, TemplateLiteral);\n    define_visit!(visit_property_access, PropertyAccess);\n    define_visit!(visit_new, New);\n    define_visit!(visit_call, Call);\n    define_visit!(visit_super_call, SuperCall);\n    define_visit!(visit_import_call, ImportCall);\n    define_visit!(visit_optional, Optional);\n    define_visit!(visit_tagged_template, TaggedTemplate);\n    define_visit!(visit_assign, Assign);\n    define_visit!(visit_unary, Unary);\n    define_visit!(visit_update, Update);\n    define_visit!(visit_binary, Binary);\n    define_visit!(visit_binary_in_private, BinaryInPrivate);\n    define_visit!(visit_conditional, Conditional);\n    define_visit!(visit_await, Await);\n    define_visit!(visit_yield, Yield);\n    define_visit!(visit_parenthesized, Parenthesized);\n    define_visit!(visit_new_target, NewTarget);\n    define_visit!(visit_import_meta, ImportMeta);\n    define_visit!(visit_for_loop_initializer, ForLoopInitializer);\n    define_visit!(visit_iterable_loop_initializer, IterableLoopInitializer);\n    define_visit!(visit_case, Case);\n    define_visit!(visit_sym, Sym);\n    define_visit!(visit_labelled_item, LabelledItem);\n    define_visit!(visit_catch, Catch);\n    define_visit!(visit_finally, Finally);\n    define_visit!(visit_formal_parameter, FormalParameter);\n    define_visit!(visit_property_name, PropertyName);\n    define_visit!(visit_object_method_definition, ObjectMethodDefinition);\n    define_visit!(visit_object_pattern, ObjectPattern);\n    define_visit!(visit_array_pattern, ArrayPattern);\n    define_visit!(visit_property_definition, PropertyDefinition);\n    define_visit!(visit_template_element, TemplateElement);\n    define_visit!(visit_simple_property_access, SimplePropertyAccess);\n    define_visit!(visit_private_property_access, PrivatePropertyAccess);\n    define_visit!(visit_super_property_access, SuperPropertyAccess);\n    define_visit!(visit_optional_operation, OptionalOperation);\n    define_visit!(visit_assign_target, AssignTarget);\n    define_visit!(visit_object_pattern_element, ObjectPatternElement);\n    define_visit!(visit_array_pattern_element, ArrayPatternElement);\n    define_visit!(visit_property_access_field, PropertyAccessField);\n    define_visit!(visit_optional_operation_kind, OptionalOperationKind);\n    define_visit!(visit_module_item_list, ModuleItemList);\n    define_visit!(visit_module_item, ModuleItem);\n    define_visit!(visit_module_specifier, ModuleSpecifier);\n    define_visit!(visit_import_attribute, ImportAttribute);\n    define_visit!(visit_import_kind, ImportKind);\n    define_visit!(visit_import_declaration, ImportDeclaration);\n    define_visit!(visit_import_specifier, ImportSpecifier);\n    define_visit!(visit_re_export_kind, ReExportKind);\n    define_visit!(visit_export_declaration, ExportDeclaration);\n    define_visit!(visit_export_specifier, ExportSpecifier);\n\n    /// Generic entry point for a node that is visitable by a `Visitor`.\n    ///\n    /// This is usually used for generic functions that need to visit an unnamed AST node.\n    fn visit<N: Into<NodeRef<'ast>>>(&mut self, node: N) -> ControlFlow<Self::BreakTy> {\n        let node = node.into();\n        match node {\n            NodeRef::Script(n) => self.visit_script(n),\n            NodeRef::Module(n) => self.visit_module(n),\n            NodeRef::FunctionBody(n) => self.visit_function_body(n),\n            NodeRef::StatementList(n) => self.visit_statement_list(n),\n            NodeRef::StatementListItem(n) => self.visit_statement_list_item(n),\n            NodeRef::Statement(n) => self.visit_statement(n),\n            NodeRef::Declaration(n) => self.visit_declaration(n),\n            NodeRef::FunctionExpression(n) => self.visit_function_expression(n),\n            NodeRef::FunctionDeclaration(n) => self.visit_function_declaration(n),\n            NodeRef::GeneratorExpression(n) => self.visit_generator_expression(n),\n            NodeRef::GeneratorDeclaration(n) => self.visit_generator_declaration(n),\n            NodeRef::AsyncFunctionExpression(n) => self.visit_async_function_expression(n),\n            NodeRef::AsyncFunctionDeclaration(n) => self.visit_async_function_declaration(n),\n            NodeRef::AsyncGeneratorExpression(n) => self.visit_async_generator_expression(n),\n            NodeRef::AsyncGeneratorDeclaration(n) => self.visit_async_generator_declaration(n),\n            NodeRef::ClassExpression(n) => self.visit_class_expression(n),\n            NodeRef::ClassDeclaration(n) => self.visit_class_declaration(n),\n            NodeRef::LexicalDeclaration(n) => self.visit_lexical_declaration(n),\n            NodeRef::Block(n) => self.visit_block(n),\n            NodeRef::VarDeclaration(n) => self.visit_var_declaration(n),\n            NodeRef::Expression(n) => self.visit_expression(n),\n            NodeRef::If(n) => self.visit_if(n),\n            NodeRef::DoWhileLoop(n) => self.visit_do_while_loop(n),\n            NodeRef::WhileLoop(n) => self.visit_while_loop(n),\n            NodeRef::ForLoop(n) => self.visit_for_loop(n),\n            NodeRef::ForInLoop(n) => self.visit_for_in_loop(n),\n            NodeRef::ForOfLoop(n) => self.visit_for_of_loop(n),\n            NodeRef::Switch(n) => self.visit_switch(n),\n            NodeRef::Continue(n) => self.visit_continue(n),\n            NodeRef::Break(n) => self.visit_break(n),\n            NodeRef::Return(n) => self.visit_return(n),\n            NodeRef::Labelled(n) => self.visit_labelled(n),\n            NodeRef::With(n) => self.visit_with(n),\n            NodeRef::Throw(n) => self.visit_throw(n),\n            NodeRef::Try(n) => self.visit_try(n),\n            NodeRef::This(n) => self.visit_this(n),\n            NodeRef::NewTarget(n) => self.visit_new_target(n),\n            NodeRef::ImportMeta(n) => self.visit_import_meta(n),\n            NodeRef::Identifier(n) => self.visit_identifier(n),\n            NodeRef::FormalParameterList(n) => self.visit_formal_parameter_list(n),\n            NodeRef::ClassElement(n) => self.visit_class_element(n),\n            NodeRef::PrivateName(n) => self.visit_private_name(n),\n            NodeRef::VariableList(n) => self.visit_variable_list(n),\n            NodeRef::Variable(n) => self.visit_variable(n),\n            NodeRef::Binding(n) => self.visit_binding(n),\n            NodeRef::Pattern(n) => self.visit_pattern(n),\n            NodeRef::Literal(n) => self.visit_literal(n),\n            NodeRef::RegExpLiteral(n) => self.visit_reg_exp_literal(n),\n            NodeRef::ArrayLiteral(n) => self.visit_array_literal(n),\n            NodeRef::ObjectLiteral(n) => self.visit_object_literal(n),\n            NodeRef::Spread(n) => self.visit_spread(n),\n            NodeRef::ArrowFunction(n) => self.visit_arrow_function(n),\n            NodeRef::AsyncArrowFunction(n) => self.visit_async_arrow_function(n),\n            NodeRef::TemplateLiteral(n) => self.visit_template_literal(n),\n            NodeRef::PropertyAccess(n) => self.visit_property_access(n),\n            NodeRef::New(n) => self.visit_new(n),\n            NodeRef::Call(n) => self.visit_call(n),\n            NodeRef::SuperCall(n) => self.visit_super_call(n),\n            NodeRef::ImportCall(n) => self.visit_import_call(n),\n            NodeRef::Optional(n) => self.visit_optional(n),\n            NodeRef::TaggedTemplate(n) => self.visit_tagged_template(n),\n            NodeRef::Assign(n) => self.visit_assign(n),\n            NodeRef::Unary(n) => self.visit_unary(n),\n            NodeRef::Update(n) => self.visit_update(n),\n            NodeRef::Binary(n) => self.visit_binary(n),\n            NodeRef::BinaryInPrivate(n) => self.visit_binary_in_private(n),\n            NodeRef::Conditional(n) => self.visit_conditional(n),\n            NodeRef::Await(n) => self.visit_await(n),\n            NodeRef::Yield(n) => self.visit_yield(n),\n            NodeRef::Parenthesized(n) => self.visit_parenthesized(n),\n            NodeRef::ForLoopInitializer(n) => self.visit_for_loop_initializer(n),\n            NodeRef::IterableLoopInitializer(n) => self.visit_iterable_loop_initializer(n),\n            NodeRef::Case(n) => self.visit_case(n),\n            NodeRef::Sym(n) => self.visit_sym(n),\n            NodeRef::LabelledItem(n) => self.visit_labelled_item(n),\n            NodeRef::Catch(n) => self.visit_catch(n),\n            NodeRef::Finally(n) => self.visit_finally(n),\n            NodeRef::FormalParameter(n) => self.visit_formal_parameter(n),\n            NodeRef::PropertyName(n) => self.visit_property_name(n),\n            NodeRef::ObjectMethodDefinition(n) => self.visit_object_method_definition(n),\n            NodeRef::ObjectPattern(n) => self.visit_object_pattern(n),\n            NodeRef::ArrayPattern(n) => self.visit_array_pattern(n),\n            NodeRef::PropertyDefinition(n) => self.visit_property_definition(n),\n            NodeRef::TemplateElement(n) => self.visit_template_element(n),\n            NodeRef::SimplePropertyAccess(n) => self.visit_simple_property_access(n),\n            NodeRef::PrivatePropertyAccess(n) => self.visit_private_property_access(n),\n            NodeRef::SuperPropertyAccess(n) => self.visit_super_property_access(n),\n            NodeRef::OptionalOperation(n) => self.visit_optional_operation(n),\n            NodeRef::AssignTarget(n) => self.visit_assign_target(n),\n            NodeRef::ObjectPatternElement(n) => self.visit_object_pattern_element(n),\n            NodeRef::ArrayPatternElement(n) => self.visit_array_pattern_element(n),\n            NodeRef::PropertyAccessField(n) => self.visit_property_access_field(n),\n            NodeRef::OptionalOperationKind(n) => self.visit_optional_operation_kind(n),\n            NodeRef::ModuleItemList(n) => self.visit_module_item_list(n),\n            NodeRef::ModuleItem(n) => self.visit_module_item(n),\n            NodeRef::ModuleSpecifier(n) => self.visit_module_specifier(n),\n            NodeRef::ImportAttribute(n) => self.visit_import_attribute(n),\n            NodeRef::ImportKind(n) => self.visit_import_kind(n),\n            NodeRef::ImportDeclaration(n) => self.visit_import_declaration(n),\n            NodeRef::ImportSpecifier(n) => self.visit_import_specifier(n),\n            NodeRef::ReExportKind(n) => self.visit_re_export_kind(n),\n            NodeRef::ExportDeclaration(n) => self.visit_export_declaration(n),\n            NodeRef::ExportSpecifier(n) => self.visit_export_specifier(n),\n        }\n    }\n}\n\n/// Represents an AST visitor which can modify AST content.\n///\n/// This implementation is based largely on [chalk](https://github.com/rust-lang/chalk/blob/23d39c90ceb9242fbd4c43e9368e813e7c2179f7/chalk-ir/src/visit.rs)'s\n/// visitor pattern.\npub trait VisitorMut<'ast>: Sized {\n    /// Type which will be propagated from the visitor if completing early.\n    type BreakTy;\n\n    define_visit_mut!(visit_script_mut, Script);\n    define_visit_mut!(visit_module_mut, Module);\n    define_visit_mut!(visit_function_body_mut, FunctionBody);\n    define_visit_mut!(visit_statement_list_mut, StatementList);\n    define_visit_mut!(visit_statement_list_item_mut, StatementListItem);\n    define_visit_mut!(visit_statement_mut, Statement);\n    define_visit_mut!(visit_declaration_mut, Declaration);\n    define_visit_mut!(visit_function_expression_mut, FunctionExpression);\n    define_visit_mut!(visit_function_declaration_mut, FunctionDeclaration);\n    define_visit_mut!(visit_generator_expression_mut, GeneratorExpression);\n    define_visit_mut!(visit_generator_declaration_mut, GeneratorDeclaration);\n    define_visit_mut!(visit_async_function_expression_mut, AsyncFunctionExpression);\n    define_visit_mut!(\n        visit_async_function_declaration_mut,\n        AsyncFunctionDeclaration\n    );\n    define_visit_mut!(\n        visit_async_generator_expression_mut,\n        AsyncGeneratorExpression\n    );\n    define_visit_mut!(\n        visit_async_generator_declaration_mut,\n        AsyncGeneratorDeclaration\n    );\n    define_visit_mut!(visit_class_expression_mut, ClassExpression);\n    define_visit_mut!(visit_class_declaration_mut, ClassDeclaration);\n    define_visit_mut!(visit_lexical_declaration_mut, LexicalDeclaration);\n    define_visit_mut!(visit_block_mut, Block);\n    define_visit_mut!(visit_var_declaration_mut, VarDeclaration);\n    define_visit_mut!(visit_expression_mut, Expression);\n    define_visit_mut!(visit_if_mut, If);\n    define_visit_mut!(visit_do_while_loop_mut, DoWhileLoop);\n    define_visit_mut!(visit_while_loop_mut, WhileLoop);\n    define_visit_mut!(visit_for_loop_mut, ForLoop);\n    define_visit_mut!(visit_for_in_loop_mut, ForInLoop);\n    define_visit_mut!(visit_for_of_loop_mut, ForOfLoop);\n    define_visit_mut!(visit_switch_mut, Switch);\n    define_visit_mut!(visit_continue_mut, Continue);\n    define_visit_mut!(visit_break_mut, Break);\n    define_visit_mut!(visit_return_mut, Return);\n    define_visit_mut!(visit_labelled_mut, Labelled);\n    define_visit_mut!(visit_throw_mut, Throw);\n    define_visit_mut!(visit_try_mut, Try);\n    define_visit_mut!(visit_with_mut, With);\n    define_visit_mut!(visit_this_mut, This);\n    define_visit_mut!(visit_identifier_mut, Identifier);\n    define_visit_mut!(visit_formal_parameter_list_mut, FormalParameterList);\n    define_visit_mut!(visit_class_element_mut, ClassElement);\n    define_visit_mut!(visit_private_name_mut, PrivateName);\n    define_visit_mut!(visit_variable_list_mut, VariableList);\n    define_visit_mut!(visit_variable_mut, Variable);\n    define_visit_mut!(visit_binding_mut, Binding);\n    define_visit_mut!(visit_pattern_mut, Pattern);\n    define_visit_mut!(visit_literal_mut, Literal);\n    define_visit_mut!(visit_reg_exp_literal_mut, RegExpLiteral);\n    define_visit_mut!(visit_array_literal_mut, ArrayLiteral);\n    define_visit_mut!(visit_object_literal_mut, ObjectLiteral);\n    define_visit_mut!(visit_spread_mut, Spread);\n    define_visit_mut!(visit_arrow_function_mut, ArrowFunction);\n    define_visit_mut!(visit_async_arrow_function_mut, AsyncArrowFunction);\n    define_visit_mut!(visit_template_literal_mut, TemplateLiteral);\n    define_visit_mut!(visit_property_access_mut, PropertyAccess);\n    define_visit_mut!(visit_new_mut, New);\n    define_visit_mut!(visit_call_mut, Call);\n    define_visit_mut!(visit_super_call_mut, SuperCall);\n    define_visit_mut!(visit_import_call_mut, ImportCall);\n    define_visit_mut!(visit_optional_mut, Optional);\n    define_visit_mut!(visit_tagged_template_mut, TaggedTemplate);\n    define_visit_mut!(visit_assign_mut, Assign);\n    define_visit_mut!(visit_unary_mut, Unary);\n    define_visit_mut!(visit_update_mut, Update);\n    define_visit_mut!(visit_binary_mut, Binary);\n    define_visit_mut!(visit_binary_in_private_mut, BinaryInPrivate);\n    define_visit_mut!(visit_conditional_mut, Conditional);\n    define_visit_mut!(visit_await_mut, Await);\n    define_visit_mut!(visit_yield_mut, Yield);\n    define_visit_mut!(visit_parenthesized_mut, Parenthesized);\n    define_visit_mut!(visit_new_target_mut, NewTarget);\n    define_visit_mut!(visit_import_meta_mut, ImportMeta);\n    define_visit_mut!(visit_for_loop_initializer_mut, ForLoopInitializer);\n    define_visit_mut!(visit_iterable_loop_initializer_mut, IterableLoopInitializer);\n    define_visit_mut!(visit_case_mut, Case);\n    define_visit_mut!(visit_sym_mut, Sym);\n    define_visit_mut!(visit_labelled_item_mut, LabelledItem);\n    define_visit_mut!(visit_catch_mut, Catch);\n    define_visit_mut!(visit_finally_mut, Finally);\n    define_visit_mut!(visit_formal_parameter_mut, FormalParameter);\n    define_visit_mut!(visit_property_name_mut, PropertyName);\n    define_visit_mut!(visit_object_method_definition_mut, ObjectMethodDefinition);\n    define_visit_mut!(visit_object_pattern_mut, ObjectPattern);\n    define_visit_mut!(visit_array_pattern_mut, ArrayPattern);\n    define_visit_mut!(visit_property_definition_mut, PropertyDefinition);\n    define_visit_mut!(visit_template_element_mut, TemplateElement);\n    define_visit_mut!(visit_simple_property_access_mut, SimplePropertyAccess);\n    define_visit_mut!(visit_private_property_access_mut, PrivatePropertyAccess);\n    define_visit_mut!(visit_super_property_access_mut, SuperPropertyAccess);\n    define_visit_mut!(visit_optional_operation_mut, OptionalOperation);\n    define_visit_mut!(visit_assign_target_mut, AssignTarget);\n    define_visit_mut!(visit_object_pattern_element_mut, ObjectPatternElement);\n    define_visit_mut!(visit_array_pattern_element_mut, ArrayPatternElement);\n    define_visit_mut!(visit_property_access_field_mut, PropertyAccessField);\n    define_visit_mut!(visit_optional_operation_kind_mut, OptionalOperationKind);\n    define_visit_mut!(visit_module_item_list_mut, ModuleItemList);\n    define_visit_mut!(visit_module_item_mut, ModuleItem);\n    define_visit_mut!(visit_module_specifier_mut, ModuleSpecifier);\n    define_visit_mut!(visit_import_attribute_mut, ImportAttribute);\n    define_visit_mut!(visit_import_kind_mut, ImportKind);\n    define_visit_mut!(visit_import_declaration_mut, ImportDeclaration);\n    define_visit_mut!(visit_import_specifier_mut, ImportSpecifier);\n    define_visit_mut!(visit_re_export_kind_mut, ReExportKind);\n    define_visit_mut!(visit_export_declaration_mut, ExportDeclaration);\n    define_visit_mut!(visit_export_specifier_mut, ExportSpecifier);\n\n    /// Generic entry point for a node that is visitable by a `VisitorMut`.\n    ///\n    /// This is usually used for generic functions that need to visit an unnamed AST node.\n    fn visit<N: Into<NodeRefMut<'ast>>>(&mut self, node: N) -> ControlFlow<Self::BreakTy> {\n        let node = node.into();\n        match node {\n            NodeRefMut::Script(n) => self.visit_script_mut(n),\n            NodeRefMut::Module(n) => self.visit_module_mut(n),\n            NodeRefMut::FunctionBody(n) => self.visit_function_body_mut(n),\n            NodeRefMut::StatementList(n) => self.visit_statement_list_mut(n),\n            NodeRefMut::StatementListItem(n) => self.visit_statement_list_item_mut(n),\n            NodeRefMut::Statement(n) => self.visit_statement_mut(n),\n            NodeRefMut::Declaration(n) => self.visit_declaration_mut(n),\n            NodeRefMut::FunctionExpression(n) => self.visit_function_expression_mut(n),\n            NodeRefMut::FunctionDeclaration(n) => self.visit_function_declaration_mut(n),\n            NodeRefMut::GeneratorExpression(n) => self.visit_generator_expression_mut(n),\n            NodeRefMut::GeneratorDeclaration(n) => self.visit_generator_declaration_mut(n),\n            NodeRefMut::AsyncFunctionExpression(n) => self.visit_async_function_expression_mut(n),\n            NodeRefMut::AsyncFunctionDeclaration(n) => self.visit_async_function_declaration_mut(n),\n            NodeRefMut::AsyncGeneratorExpression(n) => self.visit_async_generator_expression_mut(n),\n            NodeRefMut::AsyncGeneratorDeclaration(n) => {\n                self.visit_async_generator_declaration_mut(n)\n            }\n            NodeRefMut::ClassExpression(n) => self.visit_class_expression_mut(n),\n            NodeRefMut::ClassDeclaration(n) => self.visit_class_declaration_mut(n),\n            NodeRefMut::LexicalDeclaration(n) => self.visit_lexical_declaration_mut(n),\n            NodeRefMut::Block(n) => self.visit_block_mut(n),\n            NodeRefMut::VarDeclaration(n) => self.visit_var_declaration_mut(n),\n            NodeRefMut::Expression(n) => self.visit_expression_mut(n),\n            NodeRefMut::If(n) => self.visit_if_mut(n),\n            NodeRefMut::DoWhileLoop(n) => self.visit_do_while_loop_mut(n),\n            NodeRefMut::WhileLoop(n) => self.visit_while_loop_mut(n),\n            NodeRefMut::ForLoop(n) => self.visit_for_loop_mut(n),\n            NodeRefMut::ForInLoop(n) => self.visit_for_in_loop_mut(n),\n            NodeRefMut::ForOfLoop(n) => self.visit_for_of_loop_mut(n),\n            NodeRefMut::Switch(n) => self.visit_switch_mut(n),\n            NodeRefMut::Continue(n) => self.visit_continue_mut(n),\n            NodeRefMut::Break(n) => self.visit_break_mut(n),\n            NodeRefMut::Return(n) => self.visit_return_mut(n),\n            NodeRefMut::Labelled(n) => self.visit_labelled_mut(n),\n            NodeRefMut::With(n) => self.visit_with_mut(n),\n            NodeRefMut::Throw(n) => self.visit_throw_mut(n),\n            NodeRefMut::Try(n) => self.visit_try_mut(n),\n            NodeRefMut::This(n) => self.visit_this_mut(n),\n            NodeRefMut::NewTarget(n) => self.visit_new_target_mut(n),\n            NodeRefMut::ImportMeta(n) => self.visit_import_meta_mut(n),\n            NodeRefMut::Identifier(n) => self.visit_identifier_mut(n),\n            NodeRefMut::FormalParameterList(n) => self.visit_formal_parameter_list_mut(n),\n            NodeRefMut::ClassElement(n) => self.visit_class_element_mut(n),\n            NodeRefMut::PrivateName(n) => self.visit_private_name_mut(n),\n            NodeRefMut::VariableList(n) => self.visit_variable_list_mut(n),\n            NodeRefMut::Variable(n) => self.visit_variable_mut(n),\n            NodeRefMut::Binding(n) => self.visit_binding_mut(n),\n            NodeRefMut::Pattern(n) => self.visit_pattern_mut(n),\n            NodeRefMut::Literal(n) => self.visit_literal_mut(n),\n            NodeRefMut::RegExpLiteral(n) => self.visit_reg_exp_literal_mut(n),\n            NodeRefMut::ArrayLiteral(n) => self.visit_array_literal_mut(n),\n            NodeRefMut::ObjectLiteral(n) => self.visit_object_literal_mut(n),\n            NodeRefMut::Spread(n) => self.visit_spread_mut(n),\n            NodeRefMut::ArrowFunction(n) => self.visit_arrow_function_mut(n),\n            NodeRefMut::AsyncArrowFunction(n) => self.visit_async_arrow_function_mut(n),\n            NodeRefMut::TemplateLiteral(n) => self.visit_template_literal_mut(n),\n            NodeRefMut::PropertyAccess(n) => self.visit_property_access_mut(n),\n            NodeRefMut::New(n) => self.visit_new_mut(n),\n            NodeRefMut::Call(n) => self.visit_call_mut(n),\n            NodeRefMut::SuperCall(n) => self.visit_super_call_mut(n),\n            NodeRefMut::ImportCall(n) => self.visit_import_call_mut(n),\n            NodeRefMut::Optional(n) => self.visit_optional_mut(n),\n            NodeRefMut::TaggedTemplate(n) => self.visit_tagged_template_mut(n),\n            NodeRefMut::Assign(n) => self.visit_assign_mut(n),\n            NodeRefMut::Unary(n) => self.visit_unary_mut(n),\n            NodeRefMut::Update(n) => self.visit_update_mut(n),\n            NodeRefMut::Binary(n) => self.visit_binary_mut(n),\n            NodeRefMut::BinaryInPrivate(n) => self.visit_binary_in_private_mut(n),\n            NodeRefMut::Conditional(n) => self.visit_conditional_mut(n),\n            NodeRefMut::Await(n) => self.visit_await_mut(n),\n            NodeRefMut::Yield(n) => self.visit_yield_mut(n),\n            NodeRefMut::Parenthesized(n) => self.visit_parenthesized_mut(n),\n            NodeRefMut::ForLoopInitializer(n) => self.visit_for_loop_initializer_mut(n),\n            NodeRefMut::IterableLoopInitializer(n) => self.visit_iterable_loop_initializer_mut(n),\n            NodeRefMut::Case(n) => self.visit_case_mut(n),\n            NodeRefMut::Sym(n) => self.visit_sym_mut(n),\n            NodeRefMut::LabelledItem(n) => self.visit_labelled_item_mut(n),\n            NodeRefMut::Catch(n) => self.visit_catch_mut(n),\n            NodeRefMut::Finally(n) => self.visit_finally_mut(n),\n            NodeRefMut::FormalParameter(n) => self.visit_formal_parameter_mut(n),\n            NodeRefMut::PropertyName(n) => self.visit_property_name_mut(n),\n            NodeRefMut::ObjectMethodDefinition(n) => self.visit_object_method_definition_mut(n),\n            NodeRefMut::ObjectPattern(n) => self.visit_object_pattern_mut(n),\n            NodeRefMut::ArrayPattern(n) => self.visit_array_pattern_mut(n),\n            NodeRefMut::PropertyDefinition(n) => self.visit_property_definition_mut(n),\n            NodeRefMut::TemplateElement(n) => self.visit_template_element_mut(n),\n            NodeRefMut::SimplePropertyAccess(n) => self.visit_simple_property_access_mut(n),\n            NodeRefMut::PrivatePropertyAccess(n) => self.visit_private_property_access_mut(n),\n            NodeRefMut::SuperPropertyAccess(n) => self.visit_super_property_access_mut(n),\n            NodeRefMut::OptionalOperation(n) => self.visit_optional_operation_mut(n),\n            NodeRefMut::AssignTarget(n) => self.visit_assign_target_mut(n),\n            NodeRefMut::ObjectPatternElement(n) => self.visit_object_pattern_element_mut(n),\n            NodeRefMut::ArrayPatternElement(n) => self.visit_array_pattern_element_mut(n),\n            NodeRefMut::PropertyAccessField(n) => self.visit_property_access_field_mut(n),\n            NodeRefMut::OptionalOperationKind(n) => self.visit_optional_operation_kind_mut(n),\n            NodeRefMut::ModuleItemList(n) => self.visit_module_item_list_mut(n),\n            NodeRefMut::ModuleItem(n) => self.visit_module_item_mut(n),\n            NodeRefMut::ModuleSpecifier(n) => self.visit_module_specifier_mut(n),\n            NodeRefMut::ImportAttribute(n) => self.visit_import_attribute_mut(n),\n            NodeRefMut::ImportKind(n) => self.visit_import_kind_mut(n),\n            NodeRefMut::ImportDeclaration(n) => self.visit_import_declaration_mut(n),\n            NodeRefMut::ImportSpecifier(n) => self.visit_import_specifier_mut(n),\n            NodeRefMut::ReExportKind(n) => self.visit_re_export_kind_mut(n),\n            NodeRefMut::ExportDeclaration(n) => self.visit_export_declaration_mut(n),\n            NodeRefMut::ExportSpecifier(n) => self.visit_export_specifier_mut(n),\n        }\n    }\n}\n\n/// Denotes that a type may be visited, providing a method which allows a visitor to traverse its\n/// private fields.\npub trait VisitWith {\n    /// Visit this node with the provided visitor.\n    fn visit_with<'a, V>(&'a self, visitor: &mut V) -> ControlFlow<V::BreakTy>\n    where\n        V: Visitor<'a>;\n\n    /// Visit this node with the provided visitor mutably, allowing the visitor to modify private\n    /// fields.\n    fn visit_with_mut<'a, V>(&'a mut self, visitor: &mut V) -> ControlFlow<V::BreakTy>\n    where\n        V: VisitorMut<'a>;\n}\n\n// implementation for Sym as it is out-of-crate\nimpl VisitWith for Sym {\n    fn visit_with<'a, V>(&'a self, _visitor: &mut V) -> ControlFlow<V::BreakTy>\n    where\n        V: Visitor<'a>,\n    {\n        ControlFlow::Continue(())\n    }\n\n    fn visit_with_mut<'a, V>(&'a mut self, _visitor: &mut V) -> ControlFlow<V::BreakTy>\n    where\n        V: VisitorMut<'a>,\n    {\n        ControlFlow::Continue(())\n    }\n}\n"
  },
  {
    "path": "core/ast/tests/scope.rs",
    "content": "//! Tests for the scope analysis of the AST.\n\n#![allow(unused_crate_dependencies)]\n\nuse boa_ast::{\n    Declaration, Expression, LinearPosition, LinearSpan, Script, Span, Statement, StatementList,\n    StatementListItem,\n    declaration::{LexicalDeclaration, Variable},\n    expression::Identifier,\n    function::{FormalParameter, FormalParameterList, FunctionBody, FunctionDeclaration},\n    scope::Scope,\n    statement::Block,\n};\nuse boa_interner::Interner;\nuse boa_string::JsString;\n\n#[test]\nfn empty_script_is_ok() {\n    let scope = &Scope::new_global();\n    let mut script = Script::default();\n    let ok = script.analyze_scope(scope, &Interner::new()).is_ok();\n    assert!(ok);\n    assert_eq!(scope.num_bindings(), 0);\n}\n\n#[test]\nfn script_global_let() {\n    let scope = Scope::new_global();\n    let mut interner = Interner::new();\n    let a = interner.get_or_intern(\"a\");\n    let mut script = Script::new(StatementList::new(\n        [Declaration::Lexical(LexicalDeclaration::Let(\n            vec![Variable::from_identifier(\n                Identifier::new(a, Span::new((1, 1), (1, 1))),\n                None,\n            )]\n            .try_into()\n            .unwrap(),\n        ))\n        .into()],\n        LinearPosition::default(),\n        false,\n    ));\n    let ok = script.analyze_scope(&scope, &interner).is_ok();\n    assert!(ok);\n    assert_eq!(scope.num_bindings(), 1);\n    let a = scope.get_binding_reference(&JsString::from(\"a\")).unwrap();\n    assert!(!a.is_global_object());\n    assert!(a.is_lexical());\n    assert!(!a.local());\n}\n\n#[test]\nfn script_global_const() {\n    let scope = Scope::new_global();\n    let mut interner = Interner::new();\n    let a = interner.get_or_intern(\"a\");\n    let mut script = Script::new(StatementList::new(\n        [Declaration::Lexical(LexicalDeclaration::Const(\n            vec![Variable::from_identifier(\n                Identifier::new(a, Span::new((1, 1), (1, 1))),\n                None,\n            )]\n            .try_into()\n            .unwrap(),\n        ))\n        .into()],\n        LinearPosition::default(),\n        false,\n    ));\n    let ok = script.analyze_scope(&scope, &interner).is_ok();\n    assert!(ok);\n    assert_eq!(scope.num_bindings(), 1);\n    let a = scope.get_binding_reference(&JsString::from(\"a\")).unwrap();\n    assert!(!a.is_global_object());\n    assert!(a.is_lexical());\n    assert!(!a.local());\n}\n\n#[test]\nfn script_block_let() {\n    let scope = Scope::new_global();\n    let mut interner = Interner::new();\n    let a = interner.get_or_intern(\"a\");\n    let mut script = Script::new(StatementList::new(\n        [Statement::Block(Block::from((\n            vec![\n                Declaration::Lexical(LexicalDeclaration::Let(\n                    vec![Variable::from_identifier(\n                        Identifier::new(a, Span::new((1, 1), (1, 1))),\n                        None,\n                    )]\n                    .try_into()\n                    .unwrap(),\n                ))\n                .into(),\n            ],\n            LinearPosition::default(),\n        )))\n        .into()],\n        LinearPosition::default(),\n        false,\n    ));\n    let ok = script.analyze_scope(&scope, &interner).is_ok();\n    assert!(ok);\n    assert_eq!(scope.num_bindings(), 0);\n    let StatementListItem::Statement(statement) = script.statements().first().unwrap() else {\n        panic!(\"Expected a block statement\");\n    };\n    let Statement::Block(block) = statement.as_ref() else {\n        panic!(\"Expected a block statement\");\n    };\n    let scope = block.scope().unwrap();\n    assert_eq!(scope.num_bindings(), 1);\n    let a = scope.get_binding_reference(&JsString::from(\"a\")).unwrap();\n    assert!(!a.is_global_object());\n    assert!(a.is_lexical());\n    assert!(a.local());\n}\n\n#[test]\nfn script_function_mapped_arguments_not_accessed() {\n    let scope = Scope::new_global();\n    let mut interner = Interner::new();\n    let f = interner.get_or_intern(\"f\");\n    let a = interner.get_or_intern(\"a\");\n    let mut script = Script::new(StatementList::new(\n        [Declaration::FunctionDeclaration(FunctionDeclaration::new(\n            Identifier::new(f, Span::new((1, 1), (1, 1))),\n            FormalParameterList::from_parameters(vec![FormalParameter::new(\n                Variable::from_identifier(Identifier::new(a, Span::new((1, 1), (1, 1))), None),\n                false,\n            )]),\n            FunctionBody::new(\n                StatementList::new(\n                    [Declaration::Lexical(LexicalDeclaration::Let(\n                        vec![Variable::from_identifier(\n                            Identifier::new(a, Span::new((1, 1), (1, 1))),\n                            None,\n                        )]\n                        .try_into()\n                        .unwrap(),\n                    ))\n                    .into()],\n                    LinearPosition::default(),\n                    false,\n                ),\n                Span::new((1, 1), (1, 1)),\n            ),\n            LinearSpan::default(),\n        ))\n        .into()],\n        LinearPosition::default(),\n        false,\n    ));\n    let ok = script.analyze_scope(&scope, &interner).is_ok();\n    assert!(ok);\n    assert_eq!(scope.num_bindings(), 0);\n\n    let StatementListItem::Declaration(declaration) = script.statements().first().unwrap() else {\n        panic!(\"Expected a block statement\");\n    };\n    let Declaration::FunctionDeclaration(f) = declaration.as_ref() else {\n        panic!(\"Expected a block statement\");\n    };\n\n    assert_eq!(f.scopes().function_scope().num_bindings(), 2);\n    assert_eq!(f.scopes().parameters_eval_scope(), None);\n    assert_eq!(f.scopes().parameters_scope(), None);\n    assert_eq!(f.scopes().lexical_scope().unwrap().num_bindings(), 1);\n    let arguments = f\n        .scopes()\n        .function_scope()\n        .get_binding_reference(&JsString::from(\"arguments\"))\n        .unwrap();\n    assert!(!f.scopes().arguments_object_accessed());\n    assert!(!arguments.is_global_object());\n    assert!(arguments.is_lexical());\n    assert!(arguments.local());\n    let a = f\n        .scopes()\n        .function_scope()\n        .get_binding_reference(&JsString::from(\"a\"))\n        .unwrap();\n    assert!(!a.is_global_object());\n    assert!(a.is_lexical());\n    assert!(a.local());\n    let a = f\n        .scopes()\n        .lexical_scope()\n        .unwrap()\n        .get_binding_reference(&JsString::from(\"a\"))\n        .unwrap();\n    assert!(!a.is_global_object());\n    assert!(a.is_lexical());\n    assert!(a.local());\n}\n\n#[test]\nfn script_function_mapped_arguments_accessed() {\n    let scope = Scope::new_global();\n    let mut interner = Interner::new();\n    let f = interner.get_or_intern(\"f\");\n    let a = interner.get_or_intern(\"a\");\n    let arguments = interner.get_or_intern(\"arguments\");\n    let mut script = Script::new(StatementList::new(\n        [Declaration::FunctionDeclaration(FunctionDeclaration::new(\n            Identifier::new(f, Span::new((1, 1), (1, 1))),\n            FormalParameterList::from_parameters(vec![FormalParameter::new(\n                Variable::from_identifier(Identifier::new(a, Span::new((1, 1), (1, 1))), None),\n                false,\n            )]),\n            FunctionBody::new(\n                StatementList::new(\n                    [\n                        Declaration::Lexical(LexicalDeclaration::Let(\n                            vec![Variable::from_identifier(\n                                Identifier::new(a, Span::new((1, 1), (1, 1))),\n                                None,\n                            )]\n                            .try_into()\n                            .unwrap(),\n                        ))\n                        .into(),\n                        Statement::Expression(Expression::Identifier(Identifier::new(\n                            arguments,\n                            Span::new((1, 1), (1, 1)),\n                        )))\n                        .into(),\n                    ],\n                    LinearPosition::default(),\n                    false,\n                ),\n                Span::new((1, 1), (1, 1)),\n            ),\n            LinearSpan::default(),\n        ))\n        .into()],\n        LinearPosition::default(),\n        false,\n    ));\n    let ok = script.analyze_scope(&scope, &interner).is_ok();\n    assert!(ok);\n    assert_eq!(scope.num_bindings(), 0);\n    let StatementListItem::Declaration(declaration) = script.statements().first().unwrap() else {\n        panic!(\"Expected a block statement\");\n    };\n    let Declaration::FunctionDeclaration(f) = declaration.as_ref() else {\n        panic!(\"Expected a block statement\");\n    };\n    assert!(f.scopes().arguments_object_accessed());\n    assert_eq!(f.scopes().function_scope().num_bindings(), 2);\n    assert_eq!(f.scopes().parameters_eval_scope(), None);\n    assert_eq!(f.scopes().parameters_scope(), None);\n    assert_eq!(f.scopes().lexical_scope().unwrap().num_bindings(), 1);\n    let arguments = f\n        .scopes()\n        .function_scope()\n        .get_binding_reference(&JsString::from(\"arguments\"))\n        .unwrap();\n    assert!(!arguments.is_global_object());\n    assert!(arguments.is_lexical());\n    assert!(arguments.local());\n    let a = f\n        .scopes()\n        .function_scope()\n        .get_binding_reference(&JsString::from(\"a\"))\n        .unwrap();\n    assert!(!a.is_global_object());\n    assert!(a.is_lexical());\n    assert!(!a.local());\n    let a = f\n        .scopes()\n        .lexical_scope()\n        .unwrap()\n        .get_binding_reference(&JsString::from(\"a\"))\n        .unwrap();\n    assert!(!a.is_global_object());\n    assert!(a.is_lexical());\n    assert!(a.local());\n}\n\n#[test]\nfn multiple_scopes_with_same_parent_have_distinct_ids() {\n    let global = Scope::new_global();\n    let child1 = Scope::new(global.clone(), false);\n    let child2 = Scope::new(global.clone(), false);\n    let child3 = Scope::new(child1.clone(), false);\n    let child4 = Scope::new(child1.clone(), false);\n\n    // Check uniqueness by inserting in a set and asserting that there are\n    // 5 elements numbered from 0 to 4.\n    let mut ids = [global, child1, child2, child3, child4]\n        .into_iter()\n        .map(|scope| scope.unique_id())\n        .collect::<Vec<u32>>();\n    ids.sort_unstable();\n\n    assert_eq!(ids, vec![0, 1, 2, 3, 4]);\n}\n\n#[test]\nfn can_correlate_binding_to_scope() {\n    // GIVEN\n    let global = Scope::new_global();\n    let child1 = Scope::new(global.clone(), false);\n    let child2 = Scope::new(child1.clone(), false);\n    let _unused = child1.create_mutable_binding(\"x\".into(), false);\n\n    // WHEN\n    let binding = child2.get_identifier_reference(\"x\".into());\n\n    // THEN\n    assert_eq!(binding.locator().unique_scope_id(), child1.unique_id());\n}\n"
  },
  {
    "path": "core/engine/ABOUT.md",
    "content": "# About Boa\n\nBoa is an open-source, experimental ECMAScript Engine written in Rust for\nlexing, parsing and executing ECMAScript/JavaScript. Currently, Boa supports some\nof the [language][boa-conformance]. More information can be viewed at [Boa's\nwebsite][boa-web].\n\nTry out the most recent release with Boa's live demo\n[playground][boa-playground].\n\n## Boa Crates\n\n- [**`boa_cli`**][cli] - Boa's CLI && REPL implementation\n- [**`boa_ast`**][ast] - Boa's ECMAScript Abstract Syntax Tree.\n- [**`boa_engine`**][engine] - Boa's implementation of ECMAScript builtin objects and execution.\n- [**`boa_gc`**][gc] - Boa's garbage collector.\n- [**`boa_icu_provider`**][icu] - Boa's ICU4X data provider.\n- [**`boa_interner`**][interner] - Boa's string interner.\n- [**`boa_macros`**][macros] - Boa's macros.\n- [**`boa_parser`**][parser] - Boa's lexer and parser.\n- [**`boa_runtime`**][runtime] - Boa's `WebAPI` features.\n- [**`boa_string`**][string] - Boa's ECMAScript string implementation.\n- [**`boa_wintertc`**][wintertc] - Boa's `WinterTC` (TC55) Minimum Common Web API implementation.\n- [**`tag_ptr`**][tag_ptr] - Utility library that enables a pointer to be associated with a tag of type `usize`.\n- [**`small_btree`**][small_btree] - Utility library that adds the `SmallBTreeMap` data structure.\n\n[boa-conformance]: https://boajs.dev/conformance\n[boa-web]: https://boajs.dev/\n[boa-playground]: https://boajs.dev/playground\n[ast]: https://docs.rs/boa_ast/latest/boa_ast/index.html\n[engine]: https://docs.rs/boa_engine/latest/boa_engine/index.html\n[gc]: https://docs.rs/boa_gc/latest/boa_gc/index.html\n[interner]: https://docs.rs/boa_interner/latest/boa_interner/index.html\n[parser]: https://docs.rs/boa_parser/latest/boa_parser/index.html\n[icu]: https://docs.rs/boa_icu_provider/latest/boa_icu_provider/index.html\n[runtime]: https://docs.rs/boa_runtime/latest/boa_runtime/index.html\n[string]: https://docs.rs/boa_string/latest/boa_string/index.html\n[wintertc]: https://docs.rs/boa_wintertc/latest/boa_wintertc/index.html\n[tag_ptr]: https://docs.rs/tag_ptr/latest/tag_ptr/index.html\n[small_btree]: https://docs.rs/small_btree/latest/small_btree/index.html\n[macros]: https://docs.rs/boa_macros/latest/boa_macros/index.html\n[cli]: https://crates.io/crates/boa_cli\n"
  },
  {
    "path": "core/engine/Cargo.toml",
    "content": "[package]\nname = \"boa_engine\"\nkeywords = [\"javascript\", \"js\", \"compiler\", \"lexer\", \"parser\"]\ncategories = [\"parser-implementations\", \"compilers\"]\nreadme = \"../../README.md\"\ndescription.workspace = true\nversion.workspace = true\nedition.workspace = true\nauthors.workspace = true\nlicense.workspace = true\nrepository.workspace = true\nrust-version.workspace = true\n\n[features]\ndefault = [\"float16\", \"xsum\", \"temporal\"]\n\nembedded_lz4 = [\"boa_macros/embedded_lz4\", \"lz4_flex\"]\n\n# Replace the NaN-boxing implementation of `JsValueInner` with an\n# enum-based implementation. This implementation is less performant\n# but compatible with more platforms.\n# If you encounter the \"assertion left == right failed: Pointer is\n# not 4-bits aligned or over 51-bits.\" error, try this feature.\n# For more details, see https://github.com/boa-dev/boa/issues/4275\n# Disabled by default.\njsvalue-enum = []\ndeser = [\"boa_interner/serde\", \"boa_ast/serde\"]\neither = [\"dep:either\", \"boa_gc/either\"]\n\n# Enables the `Intl` builtin object and bundles a default ICU4X data provider.\n# Prefer this over `intl` if you just want to enable `Intl` without dealing with the\n# generation of ICU4X data.\nintl_bundled = [\"intl\", \"dep:boa_icu_provider\"]\n\n# Enables Boa's `Intl` builtin implementation.\n# Prefer this over `intl_bundled` if you want to reduce the size of the final binary\n# by providing a smaller ICU4X data provider.\nintl = [\n    \"boa_gc/icu\",\n    \"icu_normalizer/serde\",\n    \"dep:icu_locale\",\n    \"dep:icu_datetime\",\n    \"dep:icu_time\",\n    \"dep:icu_plurals\",\n    \"dep:icu_provider\",\n    \"dep:icu_calendar\",\n    \"dep:icu_collator\",\n    \"dep:icu_casemap\",\n    \"dep:icu_list\",\n    \"dep:icu_segmenter\",\n    \"dep:icu_decimal\",\n    \"dep:writeable\",\n    \"dep:sys-locale\",\n    \"dep:yoke\",\n    \"dep:zerofrom\",\n    \"dep:fixed_decimal\",\n    \"dep:tinystr\",\n    \"dep:timezone_provider\",\n    \"timezone_provider/experimental_tzif\",\n]\n\nfuzz = [\"boa_ast/arbitrary\", \"boa_interner/arbitrary\"]\n\n# Enable Boa's VM instruction flowgraph generator.\nflowgraph = []\n\n# Enable Boa's VM instruction tracing.\ntrace = [\"js\"]\n\n# Enable Boa's additional ECMAScript features for web browsers.\nannex-b = [\"boa_ast/annex-b\", \"boa_parser/annex-b\"]\n\n# Enable Boa's Temporal proposal implementation\ntemporal = [\"dep:icu_calendar\", \"dep:temporal_rs\", \"dep:timezone_provider\", \"timezone_provider/experimental_tzif\"]\n\n#Enable access to host system timezone\nsystem-time-zone = [\"dep:iana-time-zone\"]\n\n# Enable experimental features, like Stage 3 proposals.\nexperimental = []\n\n# Enable binding to JS APIs for system related utilities.\njs = [\"dep:web-time\", \"dep:getrandom\", \"getrandom/wasm_js\", \"time/wasm-bindgen\"]\n\n# Enable support for Float16 typed arrays\nfloat16 = [\"dep:float16\"]\n\n# Enable support for `Math.sumPrecise`\nxsum = [\"dep:xsum\"]\n\n# Native Backtraces\nnative-backtrace = []\n\n[dependencies]\ntag_ptr.workspace = true\nboa_interner.workspace = true\nboa_gc = { workspace = true, features = [\"thin-vec\", \"boa_string\", \"arrayvec\"] }\nboa_macros.workspace = true\nboa_ast.workspace = true\nboa_parser.workspace = true\nboa_string.workspace = true\ncow-utils.workspace = true\nfutures-lite.workspace = true\nfloat16 = { version = \"0.1\", optional = true }\nlz4_flex = { workspace = true, optional = true }\nxsum = { version = \"0.1.6\", optional = true }\nserde = { workspace = true, features = [\"derive\", \"rc\"] }\nserde_json.workspace = true\nrand.workspace = true\nnum-traits.workspace = true\nregress.workspace = true\nrustc-hash = { workspace = true, features = [\"std\"] }\nnum-bigint = { workspace = true, features = [\"serde\"] }\nnum-integer.workspace = true\nbitflags.workspace = true\nindexmap = { workspace = true, features = [\"std\"] }\nryu-js.workspace = true\nfast-float2.workspace = true\ntap.workspace = true\nsmall_btree.workspace = true\npaste.workspace = true\nthiserror.workspace = true\ndashmap.workspace = true\nnum_enum.workspace = true\nthin-vec.workspace = true\nitertools = { workspace = true, default-features = false, features = [\"use_alloc\"] }\nicu_normalizer = { workspace = true, features = [\n    \"compiled_data\",\n    \"utf16_iter\",\n] }\nportable-atomic.workspace = true\nbytemuck = { workspace = true, features = [\"derive\"] }\narrayvec.workspace = true\nintrusive-collections.workspace = true\ncfg-if.workspace = true\ntime.workspace = true\nhashbrown.workspace = true\neither = { workspace = true, optional = true }\nstatic_assertions.workspace = true\naligned-vec.workspace = true\ndynify = { workspace = true, features = [\"macros\"] }\nfutures-concurrency.workspace = true\noneshot = { workspace = true, features = [\"async\"] }\nasync-channel.workspace = true\n\n# intl deps\nboa_icu_provider = { workspace = true, features = [\"std\"], optional = true }\nsys-locale = { workspace = true, optional = true }\nicu_provider = { workspace = true, optional = true }\nicu_locale = { workspace = true, default-features = false, features = [\n    \"serde\",\n], optional = true }\nicu_datetime = { workspace = true, default-features = false, features = [\n    \"serde\",\n], optional = true }\nicu_time = { workspace = true, default-features = false, features = [\n    \"serde\",\n], optional = true }\nicu_calendar = { workspace = true, default-features = false, optional = true }\nicu_collator = { workspace = true, default-features = false, features = [\n    \"serde\",\n], optional = true }\nicu_plurals = { workspace = true, default-features = false, features = [\n    \"serde\",\n    \"experimental\",\n], optional = true }\nicu_list = { workspace = true, default-features = false, features = [\n    \"serde\",\n    \"alloc\",\n], optional = true }\nicu_casemap = { workspace = true, default-features = false, features = [\n    \"serde\",\n], optional = true }\nicu_segmenter = { workspace = true, default-features = false, features = [\n    \"auto\",\n    \"serde\",\n], optional = true }\nicu_decimal = { workspace = true, default-features = false, features = [\n    \"serde\",\n], optional = true }\nwriteable = { workspace = true, optional = true }\nyoke = { workspace = true, optional = true }\nzerofrom = { workspace = true, optional = true }\nfixed_decimal = { workspace = true, features = [\n    \"ryu\",\n], optional = true }\ntinystr = { workspace = true, optional = true }\n\n# temporal deps\ntemporal_rs = { workspace = true, optional = true }\ntimezone_provider = { workspace = true, optional = true }\niana-time-zone = { workspace = true, optional = true }\n\n[target.'cfg(all(target_family = \"wasm\", not(any(target_os = \"emscripten\", target_os = \"wasi\"))))'.dependencies]\nweb-time = { workspace = true, optional = true }\n# NOTE: This enables the wasm_js required for rand to work on wasm\ngetrandom = { workspace = true, features = [\"wasm_js\"], optional = true }\n\n[dev-dependencies]\ncriterion.workspace = true\nfloat-cmp.workspace = true\nindoc.workspace = true\ntextwrap.workspace = true\ntest-case.workspace = true\nhusky-rs.workspace = true\n\n[target.x86_64-unknown-linux-gnu.dev-dependencies]\njemallocator.workspace = true\n\n[lib]\ncrate-type = [\"cdylib\", \"lib\"]\nname = \"boa_engine\"\nbench = false\n\n[[bench]]\nname = \"full\"\nharness = false\n\n[lints]\nworkspace = true\n\n[package.metadata.docs.rs]\nall-features = true\n"
  },
  {
    "path": "core/engine/benches/README.md",
    "content": "# Boa Benchmarks\n\nFor each js script in the `bench_scripts` folder, we create three benchmarks:\n\n- Parser => lexing and parsing of the source code\n- Compiler => compilation of the parsed statement list into bytecode\n- Execution => execution of the bytecode in the vm\n\nThe idea is to check the performance of Boa in different scenarios.\nDifferent parts of Boa are benchmarked separately to make the impact of local changes visible.\n"
  },
  {
    "path": "core/engine/benches/bench_scripts/arithmetic_operations.js",
    "content": "((2 + 2) ** 3 / 100 - 5 ** 3 * -1000) ** 2 + 100 - 8;\n"
  },
  {
    "path": "core/engine/benches/bench_scripts/array_access.js",
    "content": "(function () {\n  let testArr = [1, 2, 3, 4, 5];\n\n  let res = testArr[2];\n\n  return res;\n})();\n"
  },
  {
    "path": "core/engine/benches/bench_scripts/array_create.js",
    "content": "(function () {\n  let testArr = [];\n  for (let a = 0; a <= 500; a++) {\n    testArr[a] = \"p\" + a;\n  }\n\n  return testArr;\n})();\n"
  },
  {
    "path": "core/engine/benches/bench_scripts/array_pop.js",
    "content": "(function () {\n  let testArray = [\n    83, 93, 27, 29, 2828, 234, 23, 56, 32, 56, 67, 77, 32, 45, 93, 17, 28, 83,\n    62, 99, 36, 28, 93, 27, 29, 2828, 234, 23, 56, 32, 56, 67, 77, 32, 45, 93,\n    17, 28, 83, 62, 99, 36, 28, 93, 27, 29, 2828, 234, 23, 56, 32, 56, 67, 77,\n    32, 45, 93, 17, 28, 83, 62, 99, 36, 28, 93, 27, 29, 2828, 234, 23, 56, 32,\n    56, 67, 77, 32, 45, 93, 17, 28, 83, 62, 99, 36, 28, 93, 27, 29, 2828, 234,\n    23, 56, 32, 56, 67, 77, 32, 45, 93, 17, 28, 83, 62, 99, 36, 28, 93, 27, 29,\n    2828, 234, 23, 56, 32, 56, 67, 77, 32, 45, 93, 17, 28, 83, 62, 99, 36, 28,\n    93, 27, 29, 2828, 234, 23, 56, 32, 56, 67, 77, 32, 45, 93, 17, 28, 83, 62,\n    99, 36, 28, 93, 27, 29, 2828, 234, 23, 56, 32, 56, 67, 77, 32, 45, 93, 17,\n    28, 83, 62, 99, 36, 28, 93, 27, 29, 2828, 234, 23, 56, 32, 56, 67, 77, 32,\n    45, 93, 17, 28, 83, 62, 99, 36, 28, 93, 27, 29, 2828, 234, 23, 56, 32, 56,\n    67, 77, 32, 45, 93, 17, 28, 83, 62, 99, 36, 28,\n  ];\n\n  while (testArray.length > 0) {\n    testArray.pop();\n  }\n\n  return testArray;\n})();\n"
  },
  {
    "path": "core/engine/benches/bench_scripts/boolean_object_access.js",
    "content": "new Boolean(\n  !new Boolean(\n    new Boolean(\n      !new Boolean(false).valueOf() && new Boolean(true).valueOf(),\n    ).valueOf(),\n  ).valueOf(),\n).valueOf();\n"
  },
  {
    "path": "core/engine/benches/bench_scripts/clean_js.js",
    "content": "!function () {\n\tvar M = new Array();\n\tfor (i = 0; i < 100; i++) {\n\t\tM.push(Math.floor(Math.random() * 100));\n\t}\n\tvar test = [];\n\tfor (i = 0; i < 100; i++) {\n\t\tif (M[i] > 50) {\n\t\t\ttest.push(M[i]);\n\t\t}\n\t}\n\ttest.forEach(elem => {\n        0\n    });\n}();\n"
  },
  {
    "path": "core/engine/benches/bench_scripts/fibonacci.js",
    "content": "(function () {\n  let num = 12;\n\n  function fib(n) {\n    if (n <= 1) return 1;\n    return fib(n - 1) + fib(n - 2);\n  }\n\n  return fib(num);\n})();\n"
  },
  {
    "path": "core/engine/benches/bench_scripts/for_loop.js",
    "content": "(function () {\n  let b = \"hello\";\n  for (let a = 10; a < 100; a += 5) {\n    if (a < 50) {\n      b += \"world\";\n    }\n  }\n\n  return b;\n})();\n"
  },
  {
    "path": "core/engine/benches/bench_scripts/mini_js.js",
    "content": "!function(){var r=new Array();for(i=0;i<100;i++)r.push(Math.floor(100*Math.random()));var a=[];for(i=0;i<100;i++)r[i]>50&&a.push(r[i]);a.forEach(i=>{0})}();\n"
  },
  {
    "path": "core/engine/benches/bench_scripts/number_object_access.js",
    "content": "new Number(\n  new Number(\n    new Number(new Number(100).valueOf() - 10.5).valueOf() + 100,\n  ).valueOf() * 1.6,\n);\n"
  },
  {
    "path": "core/engine/benches/bench_scripts/object_creation.js",
    "content": "(function () {\n  let test = {\n    my_prop: \"hello\",\n    another: 65,\n  };\n\n  return test;\n})();\n"
  },
  {
    "path": "core/engine/benches/bench_scripts/object_prop_access_const.js",
    "content": "(function () {\n  let test = {\n    my_prop: \"hello\",\n    another: 65,\n  };\n\n  return test.my_prop;\n})();\n"
  },
  {
    "path": "core/engine/benches/bench_scripts/object_prop_access_dyn.js",
    "content": "(function () {\n  let test = {\n    my_prop: \"hello\",\n    another: 65,\n  };\n\n  return test[\"my\" + \"_prop\"];\n})();\n"
  },
  {
    "path": "core/engine/benches/bench_scripts/regexp.js",
    "content": "(function () {\n  let regExp = new RegExp(\"hello\", \"i\");\n\n  return regExp.test(\"Hello World\");\n})();\n"
  },
  {
    "path": "core/engine/benches/bench_scripts/regexp_creation.js",
    "content": "(function () {\n  let regExp = new RegExp(\"hello\", \"i\");\n\n  return regExp;\n})();\n"
  },
  {
    "path": "core/engine/benches/bench_scripts/regexp_literal.js",
    "content": "(function () {\n  let regExp = /hello/i;\n\n  return regExp.test(\"Hello World\");\n})();\n"
  },
  {
    "path": "core/engine/benches/bench_scripts/regexp_literal_creation.js",
    "content": "(function () {\n  let regExp = /hello/i;\n\n  return regExp;\n})();\n"
  },
  {
    "path": "core/engine/benches/bench_scripts/string_code_point_sum.js",
    "content": "(() => {\n  let sum = \"\";\n  let string = \"Hello, world!!!\";\n  for (let i = 0; i < string.length; ++i) {\n    sum += string.charCodeAt(i).toString(16);\n  }\n  return sum;\n})();\n"
  },
  {
    "path": "core/engine/benches/bench_scripts/string_compare.js",
    "content": "(function () {\n  var a = \"hello\";\n  var b = \"world\";\n\n  var c = a == b;\n\n  var d = b;\n  var e = d == b;\n})();\n"
  },
  {
    "path": "core/engine/benches/bench_scripts/string_concat.js",
    "content": "(function () {\n  var a = \"hello\";\n  var b = \"world\";\n\n  var c = a + b;\n})();\n"
  },
  {
    "path": "core/engine/benches/bench_scripts/string_copy.js",
    "content": "(function () {\n  var a = \"hello\";\n  var b = a;\n})();\n"
  },
  {
    "path": "core/engine/benches/bench_scripts/string_object_access.js",
    "content": "new String(\n  new String(\n    new String(\n      new String(\"Hello\").valueOf() + new String(\", world\").valueOf(),\n    ).valueOf() + \"!\",\n  ).valueOf(),\n).valueOf();\n"
  },
  {
    "path": "core/engine/benches/bench_scripts/symbol_creation.js",
    "content": "(function () {\n  return Symbol();\n})();\n"
  },
  {
    "path": "core/engine/benches/full.rs",
    "content": "#![allow(unused_crate_dependencies, missing_docs)]\n\n//! Benchmarks of the whole execution engine in Boa.\n\nuse boa_engine::{\n    Context, Source, context::DefaultHooks, object::shape::RootShape, optimizer::OptimizerOptions,\n    realm::Realm, script::Script,\n};\nuse criterion::{Criterion, criterion_group, criterion_main};\nuse std::hint::black_box;\n\n#[cfg(all(target_arch = \"x86_64\", target_os = \"linux\", target_env = \"gnu\"))]\n#[cfg_attr(\n    all(target_arch = \"x86_64\", target_os = \"linux\", target_env = \"gnu\"),\n    global_allocator\n)]\nstatic ALLOC: jemallocator::Jemalloc = jemallocator::Jemalloc;\n\nfn create_realm(c: &mut Criterion) {\n    c.bench_function(\"Create Realm\", move |b| {\n        let root_shape = RootShape::default();\n        b.iter(|| Realm::create(&DefaultHooks, &root_shape));\n    });\n}\n\nmacro_rules! full_benchmarks {\n    ($({$id:literal, $name:ident}),*) => {\n        fn bench_parser(c: &mut Criterion) {\n            $(\n                {\n                    static CODE: &str = include_str!(concat!(\"bench_scripts/\", stringify!($name), \".js\"));\n                    let mut context = Context::default();\n\n                    // Disable optimizations\n                    context.set_optimizer_options(OptimizerOptions::empty());\n\n                    c.bench_function(concat!($id, \" (Parser)\"), move |b| {\n                        b.iter(|| {\n                            Script::parse(\n                                black_box(Source::from_bytes(CODE)),\n                                None,\n                                &mut context,\n                            ).unwrap()\n                        })\n                    });\n                }\n            )*\n        }\n        fn bench_compile(c: &mut Criterion) {\n            $(\n                {\n                    static CODE: &str = include_str!(concat!(\"bench_scripts/\", stringify!($name), \".js\"));\n                    let context = &mut Context::default();\n\n                    // Disable optimizations\n                    context.set_optimizer_options(OptimizerOptions::empty());\n\n                    let script = Script::parse(\n                        black_box(Source::from_bytes(CODE)),\n                        None,\n                        context,\n                    ).unwrap();\n                    c.bench_function(concat!($id, \" (Compiler)\"), move |b| {\n                        b.iter(|| {\n                            script.codeblock(context).unwrap()\n                        })\n                    });\n                }\n            )*\n        }\n        fn bench_execution(c: &mut Criterion) {\n            $(\n                {\n                    static CODE: &str = include_str!(concat!(\"bench_scripts/\", stringify!($name), \".js\"));\n                    let context = &mut Context::default();\n\n                    // Disable optimizations\n                    context.set_optimizer_options(OptimizerOptions::empty());\n\n                    let script = Script::parse(\n                        black_box(Source::from_bytes(CODE)),\n                        None,\n                        context,\n                    ).unwrap();\n                    script.codeblock(context).unwrap();\n                    c.bench_function(concat!($id, \" (Execution)\"), move |b| {\n                        b.iter(|| {\n                            script.evaluate(context).unwrap();\n                        })\n                    });\n                }\n            )*\n        }\n    };\n}\n\nfull_benchmarks!(\n    {\"String Code Point Sum\", string_code_point_sum},\n    {\"Symbols\", symbol_creation},\n    {\"For loop\", for_loop},\n    {\"Fibonacci\", fibonacci},\n    {\"Object Creation\", object_creation},\n    {\"Static Object Property Access\", object_prop_access_const},\n    {\"Dynamic Object Property Access\", object_prop_access_dyn},\n    {\"RegExp Literal Creation\", regexp_literal_creation},\n    {\"RegExp Creation\", regexp_creation},\n    {\"RegExp Literal\", regexp_literal},\n    {\"RegExp\", regexp},\n    {\"Array access\", array_access},\n    {\"Array creation\", array_create},\n    {\"Array pop\", array_pop},\n    {\"String concatenation\", string_concat},\n    {\"String comparison\", string_compare},\n    {\"String copy\", string_copy},\n    {\"Number Object Access\", number_object_access},\n    {\"Boolean Object Access\", boolean_object_access},\n    {\"String Object Access\", string_object_access},\n    {\"Arithmetic operations\", arithmetic_operations},\n    {\"Clean js\", clean_js},\n    {\"Mini js\", mini_js}\n);\n\ncriterion_group!(\n    benches,\n    create_realm,\n    bench_parser,\n    bench_compile,\n    bench_execution,\n);\ncriterion_main!(benches);\n"
  },
  {
    "path": "core/engine/src/bigint.rs",
    "content": "//! Boa's implementation of ECMAScript's bigint primitive type.\n\nuse crate::{JsData, JsResult, JsString, builtins::Number, error::JsNativeError};\nuse boa_gc::{Finalize, Trace};\nuse num_bigint::Sign;\nuse num_integer::Integer;\nuse num_traits::{FromPrimitive, One, ToPrimitive, Zero, pow::Pow};\nuse std::{\n    fmt::{self, Display},\n    ops::{Add, BitAnd, BitOr, BitXor, Div, Mul, Neg, Rem, Shl, Shr, Sub},\n    ptr::NonNull,\n    rc::Rc,\n};\n\n/// The raw bigint type.\npub type RawBigInt = num_bigint::BigInt;\n\n#[cfg(feature = \"deser\")]\nuse serde::{Deserialize, Serialize};\n\n/// JavaScript bigint primitive rust type.\n#[allow(\n    clippy::unsafe_derive_deserialize,\n    reason = \"unsafe methods do not add invariants that need to be held\"\n)]\n#[cfg_attr(feature = \"deser\", derive(Serialize, Deserialize))]\n#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Trace, Finalize, JsData)]\n// Safety: `JsBigInt` doesn't contain any traceable types.\n#[boa_gc(unsafe_empty_trace)]\npub struct JsBigInt {\n    inner: Rc<RawBigInt>,\n}\n\nimpl JsBigInt {\n    /// Create a new [`JsBigInt`].\n    #[must_use]\n    pub fn new<T: Into<Self>>(value: T) -> Self {\n        value.into()\n    }\n\n    /// Create a [`JsBigInt`] with value `0`.\n    #[inline]\n    #[must_use]\n    pub fn zero() -> Self {\n        Self {\n            inner: Rc::new(RawBigInt::zero()),\n        }\n    }\n\n    /// Check if is zero.\n    #[inline]\n    #[must_use]\n    pub fn is_zero(&self) -> bool {\n        self.inner.is_zero()\n    }\n\n    /// Create a [`JsBigInt`] with value `1`.\n    #[inline]\n    #[must_use]\n    pub fn one() -> Self {\n        Self {\n            inner: Rc::new(RawBigInt::one()),\n        }\n    }\n\n    /// Check if is one.\n    #[inline]\n    #[must_use]\n    pub fn is_one(&self) -> bool {\n        self.inner.is_one()\n    }\n\n    /// Convert bigint to string with radix.\n    #[inline]\n    #[must_use]\n    pub fn to_string_radix(&self, radix: u32) -> String {\n        self.inner.to_str_radix(radix)\n    }\n\n    /// Converts the `BigInt` to a f64 type.\n    ///\n    /// Returns `f64::INFINITY` if the `BigInt` is too big.\n    #[inline]\n    #[must_use]\n    pub fn to_f64(&self) -> f64 {\n        self.inner.to_f64().unwrap_or(f64::INFINITY)\n    }\n\n    /// Converts the `BigInt` to a i128 type.\n    ///\n    /// Returns `i128::MAX` if the `BigInt` is too big.\n    #[inline]\n    #[must_use]\n    pub fn to_i128(&self) -> i128 {\n        self.inner.to_i128().unwrap_or(i128::MAX)\n    }\n\n    /// Converts a string to a `BigInt` with the specified radix.\n    #[inline]\n    #[must_use]\n    pub fn from_string_radix(buf: &str, radix: u32) -> Option<Self> {\n        Some(Self {\n            inner: Rc::new(RawBigInt::parse_bytes(buf.as_bytes(), radix)?),\n        })\n    }\n\n    /// Abstract operation `StringToBigInt ( str )`\n    ///\n    /// More information:\n    /// - [ECMAScript reference][spec]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-stringtobigint\n    pub(crate) fn from_js_string(string: &JsString) -> Option<JsBigInt> {\n        // 1. Let text be ! StringToCodePoints(str).\n        // 2. Let literal be ParseText(text, StringIntegerLiteral).\n        // 3. If literal is a List of errors, return undefined.\n        // 4. Let mv be the MV of literal.\n        // 5. Assert: mv is an integer.\n        // 6. Return ℤ(mv).\n        JsBigInt::from_string(string.to_std_string().ok().as_ref()?)\n    }\n\n    /// This function takes a string and converts it to `BigInt` type.\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-stringtobigint\n    #[inline]\n    #[must_use]\n    pub fn from_string(mut string: &str) -> Option<Self> {\n        string = string.trim();\n\n        if string.is_empty() {\n            return Some(Self::zero());\n        }\n\n        let mut radix = 10;\n        if string.starts_with(\"0b\") || string.starts_with(\"0B\") {\n            radix = 2;\n            string = &string[2..];\n        } else if string.starts_with(\"0x\") || string.starts_with(\"0X\") {\n            radix = 16;\n            string = &string[2..];\n        } else if string.starts_with(\"0o\") || string.starts_with(\"0O\") {\n            radix = 8;\n            string = &string[2..];\n        }\n\n        Self::from_string_radix(string, radix)\n    }\n\n    /// Checks for `SameValueZero` equality.\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-numeric-types-bigint-equal\n    #[inline]\n    #[must_use]\n    pub fn same_value_zero(x: &Self, y: &Self) -> bool {\n        // Return BigInt::equal(x, y)\n        Self::equal(x, y)\n    }\n\n    /// Checks for `SameValue` equality.\n    ///\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-numeric-types-bigint-sameValue\n    #[inline]\n    #[must_use]\n    pub fn same_value(x: &Self, y: &Self) -> bool {\n        // Return BigInt::equal(x, y)\n        Self::equal(x, y)\n    }\n\n    /// Checks for mathematical equality.\n    ///\n    /// The abstract operation `BigInt::equal` takes arguments x (a `BigInt`) and y (a `BigInt`).\n    /// It returns `true` if x and y have the same mathematical integer value and false otherwise.\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-numeric-types-bigint-sameValueZero\n    #[inline]\n    #[must_use]\n    pub fn equal(x: &Self, y: &Self) -> bool {\n        x == y\n    }\n\n    /// Returns `x` to the power `y`.\n    #[inline]\n    pub fn pow(x: &Self, y: &Self) -> JsResult<Self> {\n        let y = y\n            .inner\n            .to_biguint()\n            .ok_or_else(|| JsNativeError::range().with_message(\"BigInt negative exponent\"))?;\n\n        let num_bits = (x.inner.bits() as f64\n            * y.to_f64().expect(\"Unable to convert from BigUInt to f64\"))\n        .floor()\n            + 1f64;\n\n        if num_bits > 1_000_000_000f64 {\n            return Err(JsNativeError::range()\n                .with_message(\"Maximum BigInt size exceeded\")\n                .into());\n        }\n\n        Ok(Self::new(x.inner.as_ref().clone().pow(y)))\n    }\n\n    /// Performs the `>>` operation.\n    #[inline]\n    pub fn shift_right(x: &Self, y: &Self) -> JsResult<Self> {\n        match y.inner.to_i32() {\n            Some(n) if n > 0 => Ok(Self::new(x.inner.as_ref().clone().shr(n as usize))),\n            Some(n) => Ok(Self::new(x.inner.as_ref().clone().shl(n.unsigned_abs()))),\n            // y doesn't fit in i32.\n            //\n            // Best-effort safeguard: while the spec doesn't explicitly mandate a\n            // result for implementation-limited shift amounts, the mathematical\n            // definition of BigInt right shift (`floor(x / 2^y)`) from\n            // <https://tc39.es/ecma262/#sec-numeric-types-bigint-signedRightShift>\n            // implies that for very large positive y the result converges to\n            // 0n (x >= 0) or -1n (x < 0). V8 and SpiderMonkey agree.\n            None => match (x.inner.sign(), y.inner.sign()) {\n                // x >> (large positive): all bits are shifted out.\n                (Sign::Minus, Sign::Plus) => Ok(Self::new(RawBigInt::from(-1))),\n                (_, Sign::Plus) => Ok(Self::zero()),\n                // x >> (large negative) is equivalent to x << (large positive), which overflows.\n                (_, _) => Err(JsNativeError::range()\n                    .with_message(\"Maximum BigInt size exceeded\")\n                    .into()),\n            },\n        }\n    }\n\n    /// Performs the `<<` operation.\n    #[inline]\n    pub fn shift_left(x: &Self, y: &Self) -> JsResult<Self> {\n        match y.inner.to_i32() {\n            Some(n) if n > 0 => Ok(Self::new(x.inner.as_ref().clone().shl(n as usize))),\n            Some(n) => Ok(Self::new(x.inner.as_ref().clone().shr(n.unsigned_abs()))),\n            // y doesn't fit in i32.\n            //\n            // Best-effort safeguard: symmetric to shift_right above.\n            // See <https://tc39.es/ecma262/#sec-numeric-types-bigint-leftShift>.\n            None => match (x.inner.sign(), y.inner.sign()) {\n                // x << (large negative) is equivalent to x >> (large positive): all bits shifted out.\n                (Sign::Minus, Sign::Minus) => Ok(Self::new(RawBigInt::from(-1))),\n                (_, Sign::Minus) => Ok(Self::zero()),\n                // x << (large positive) overflows.\n                (_, _) => Err(JsNativeError::range()\n                    .with_message(\"Maximum BigInt size exceeded\")\n                    .into()),\n            },\n        }\n    }\n\n    /// Floored integer modulo.\n    ///\n    /// # Examples\n    /// ```\n    /// # use num_integer::Integer;\n    /// assert_eq!((8).mod_floor(&3), 2);\n    /// assert_eq!((8).mod_floor(&-3), -1);\n    /// ```\n    #[inline]\n    #[must_use]\n    pub fn mod_floor(x: &Self, y: &Self) -> Self {\n        Self::new(x.inner.mod_floor(&y.inner))\n    }\n\n    /// Performs the `+` operation.\n    #[inline]\n    #[must_use]\n    pub fn add(x: &Self, y: &Self) -> Self {\n        Self::new(x.inner.as_ref().clone().add(y.inner.as_ref()))\n    }\n\n    /// Performs the `-` operation.\n    #[inline]\n    #[must_use]\n    pub fn sub(x: &Self, y: &Self) -> Self {\n        Self::new(x.inner.as_ref().clone().sub(y.inner.as_ref()))\n    }\n\n    /// Performs the `*` operation.\n    #[inline]\n    #[must_use]\n    pub fn mul(x: &Self, y: &Self) -> Self {\n        Self::new(x.inner.as_ref().clone().mul(y.inner.as_ref()))\n    }\n\n    /// Performs the `/` operation.\n    #[inline]\n    #[must_use]\n    pub fn div(x: &Self, y: &Self) -> Self {\n        Self::new(x.inner.as_ref().clone().div(y.inner.as_ref()))\n    }\n\n    /// Performs the `%` operation.\n    #[inline]\n    #[must_use]\n    pub fn rem(x: &Self, y: &Self) -> Self {\n        Self::new(x.inner.as_ref().clone().rem(y.inner.as_ref()))\n    }\n\n    /// Performs the `&` operation.\n    #[inline]\n    #[must_use]\n    pub fn bitand(x: &Self, y: &Self) -> Self {\n        Self::new(x.inner.as_ref().clone().bitand(y.inner.as_ref()))\n    }\n\n    /// Performs the `|` operation.\n    #[inline]\n    #[must_use]\n    pub fn bitor(x: &Self, y: &Self) -> Self {\n        Self::new(x.inner.as_ref().clone().bitor(y.inner.as_ref()))\n    }\n\n    /// Performs the `^` operation.\n    #[inline]\n    #[must_use]\n    pub fn bitxor(x: &Self, y: &Self) -> Self {\n        Self::new(x.inner.as_ref().clone().bitxor(y.inner.as_ref()))\n    }\n\n    /// Performs the unary `-` operation.\n    #[inline]\n    #[must_use]\n    pub fn neg(x: &Self) -> Self {\n        Self::new(x.as_inner().neg())\n    }\n\n    /// Performs the unary `!` operation.\n    #[inline]\n    #[must_use]\n    pub fn not(x: &Self) -> Self {\n        Self::new(!x.as_inner())\n    }\n\n    /// Returns a reference to the raw inner value.\n    #[inline]\n    #[must_use]\n    pub fn as_inner(&self) -> &RawBigInt {\n        &self.inner\n    }\n\n    /// Consumes the [`JsBigInt`], returning a pointer to [`RawBigInt`].\n    ///\n    /// To avoid a memory leak the pointer must be converted back to a `JsBigInt` using\n    /// [`JsBigInt::from_raw`].\n    #[inline]\n    #[must_use]\n    #[allow(unused, reason = \"only used in nan-boxed implementation of JsValue\")]\n    pub(crate) fn into_raw(self) -> NonNull<RawBigInt> {\n        // SAFETY: `Rc::into_raw` must always return a non-null pointer.\n        unsafe { NonNull::new_unchecked(Rc::into_raw(self.inner).cast_mut()) }\n    }\n\n    /// Constructs a `JsBigInt` from a pointer to [`RawBigInt`].\n    ///\n    /// The raw pointer must have been previously returned by a call to\n    /// [`JsBigInt::into_raw`].\n    ///\n    /// # Safety\n    ///\n    /// This function is unsafe because improper use may lead to memory unsafety,\n    /// even if the returned `JsBigInt` is never accessed.\n    #[inline]\n    #[must_use]\n    #[allow(unused, reason = \"only used in nan-boxed implementation of JsValue\")]\n    pub(crate) unsafe fn from_raw(ptr: *const RawBigInt) -> Self {\n        Self {\n            // SAFETY: the validity of `ptr` is guaranteed by the caller.\n            inner: unsafe { Rc::from_raw(ptr) },\n        }\n    }\n}\n\nimpl Display for JsBigInt {\n    #[inline]\n    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {\n        Display::fmt(&self.inner, f)\n    }\n}\n\nimpl From<RawBigInt> for JsBigInt {\n    #[inline]\n    fn from(value: RawBigInt) -> Self {\n        Self {\n            inner: Rc::new(value),\n        }\n    }\n}\n\nimpl From<Box<RawBigInt>> for JsBigInt {\n    #[inline]\n    fn from(value: Box<RawBigInt>) -> Self {\n        Self {\n            inner: value.into(),\n        }\n    }\n}\n\nimpl From<i8> for JsBigInt {\n    #[inline]\n    fn from(value: i8) -> Self {\n        Self {\n            inner: Rc::new(RawBigInt::from(value)),\n        }\n    }\n}\n\nimpl From<u8> for JsBigInt {\n    #[inline]\n    fn from(value: u8) -> Self {\n        Self {\n            inner: Rc::new(RawBigInt::from(value)),\n        }\n    }\n}\n\nimpl From<i16> for JsBigInt {\n    #[inline]\n    fn from(value: i16) -> Self {\n        Self {\n            inner: Rc::new(RawBigInt::from(value)),\n        }\n    }\n}\n\nimpl From<u16> for JsBigInt {\n    #[inline]\n    fn from(value: u16) -> Self {\n        Self {\n            inner: Rc::new(RawBigInt::from(value)),\n        }\n    }\n}\n\nimpl From<i32> for JsBigInt {\n    #[inline]\n    fn from(value: i32) -> Self {\n        Self {\n            inner: Rc::new(RawBigInt::from(value)),\n        }\n    }\n}\n\nimpl From<u32> for JsBigInt {\n    #[inline]\n    fn from(value: u32) -> Self {\n        Self {\n            inner: Rc::new(RawBigInt::from(value)),\n        }\n    }\n}\n\nimpl From<i64> for JsBigInt {\n    #[inline]\n    fn from(value: i64) -> Self {\n        Self {\n            inner: Rc::new(RawBigInt::from(value)),\n        }\n    }\n}\n\nimpl From<u64> for JsBigInt {\n    #[inline]\n    fn from(value: u64) -> Self {\n        Self {\n            inner: Rc::new(RawBigInt::from(value)),\n        }\n    }\n}\n\nimpl From<i128> for JsBigInt {\n    #[inline]\n    fn from(value: i128) -> Self {\n        Self {\n            inner: Rc::new(RawBigInt::from(value)),\n        }\n    }\n}\n\nimpl From<u128> for JsBigInt {\n    #[inline]\n    fn from(value: u128) -> Self {\n        Self {\n            inner: Rc::new(RawBigInt::from(value)),\n        }\n    }\n}\n\nimpl From<isize> for JsBigInt {\n    #[inline]\n    fn from(value: isize) -> Self {\n        Self {\n            inner: Rc::new(RawBigInt::from(value)),\n        }\n    }\n}\n\nimpl From<usize> for JsBigInt {\n    #[inline]\n    fn from(value: usize) -> Self {\n        Self {\n            inner: Rc::new(RawBigInt::from(value)),\n        }\n    }\n}\n\n/// The error indicates that the conversion from [`f64`] to [`JsBigInt`] failed.\n#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq, PartialOrd, Ord)]\npub struct TryFromF64Error;\n\nimpl Display for TryFromF64Error {\n    #[inline]\n    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {\n        write!(f, \"Could not convert f64 value to a BigInt type\")\n    }\n}\n\nimpl TryFrom<f64> for JsBigInt {\n    type Error = TryFromF64Error;\n\n    #[inline]\n    fn try_from(n: f64) -> Result<Self, Self::Error> {\n        // If the truncated version of the number is not the\n        // same as the non-truncated version then the floating-point\n        // number contains a fractional part.\n        if !Number::equal(n.trunc(), n) {\n            return Err(TryFromF64Error);\n        }\n        RawBigInt::from_f64(n).map_or(Err(TryFromF64Error), |bigint| Ok(Self::new(bigint)))\n    }\n}\n\nimpl PartialEq<i32> for JsBigInt {\n    #[inline]\n    fn eq(&self, other: &i32) -> bool {\n        self.inner.as_ref() == &RawBigInt::from(*other)\n    }\n}\n\nimpl PartialEq<JsBigInt> for i32 {\n    #[inline]\n    fn eq(&self, other: &JsBigInt) -> bool {\n        &RawBigInt::from(*self) == other.inner.as_ref()\n    }\n}\n\nimpl PartialEq<f64> for JsBigInt {\n    #[inline]\n    fn eq(&self, other: &f64) -> bool {\n        other.fract().is_zero()\n            && RawBigInt::from_f64(*other).is_some_and(|bigint| self.inner.as_ref() == &bigint)\n    }\n}\n\nimpl PartialEq<JsBigInt> for f64 {\n    #[inline]\n    fn eq(&self, other: &JsBigInt) -> bool {\n        self.fract().is_zero()\n            && RawBigInt::from_f64(*self).is_some_and(|bigint| other.inner.as_ref() == &bigint)\n    }\n}\n"
  },
  {
    "path": "core/engine/src/builtins/array/array_iterator.rs",
    "content": "//! This module implements the `ArrayIterator` object.\n//!\n//! More information:\n//!  - [ECMAScript reference][spec]\n//!\n//! [spec]: https://tc39.es/ecma262/#sec-array-iterator-objects\n\nuse crate::{\n    Context, JsData, JsResult,\n    builtins::{\n        Array, BuiltInBuilder, IntrinsicObject, JsValue, iterable::create_iter_result_object,\n        typed_array::TypedArray,\n    },\n    context::intrinsics::Intrinsics,\n    error::JsNativeError,\n    js_string,\n    object::JsObject,\n    property::{Attribute, PropertyNameKind},\n    realm::Realm,\n    symbol::JsSymbol,\n};\nuse boa_gc::{Finalize, Trace};\n\n/// The Array Iterator object represents an iteration over an array. It implements the iterator protocol.\n///\n/// More information:\n///  - [ECMAScript reference][spec]\n///\n/// [spec]: https://tc39.es/ecma262/#sec-array-iterator-objects\n#[derive(Debug, Clone, Finalize, Trace, JsData)]\npub(crate) struct ArrayIterator {\n    array: JsObject,\n    next_index: u64,\n    #[unsafe_ignore_trace]\n    kind: PropertyNameKind,\n    done: bool,\n}\n\nimpl IntrinsicObject for ArrayIterator {\n    fn init(realm: &Realm) {\n        BuiltInBuilder::with_intrinsic::<Self>(realm)\n            .prototype(\n                realm\n                    .intrinsics()\n                    .objects()\n                    .iterator_prototypes()\n                    .iterator(),\n            )\n            .static_method(Self::next, js_string!(\"next\"), 0)\n            .static_property(\n                JsSymbol::to_string_tag(),\n                js_string!(\"Array Iterator\"),\n                Attribute::CONFIGURABLE,\n            )\n            .build();\n    }\n\n    fn get(intrinsics: &Intrinsics) -> JsObject {\n        intrinsics.objects().iterator_prototypes().array()\n    }\n}\n\nimpl ArrayIterator {\n    fn new(array: JsObject, kind: PropertyNameKind) -> Self {\n        Self {\n            array,\n            kind,\n            next_index: 0,\n            done: false,\n        }\n    }\n\n    /// `CreateArrayIterator( array, kind )`\n    ///\n    /// Creates a new iterator over the given array.\n    ///\n    /// More information:\n    ///  - [ECMA reference][spec]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-createarrayiterator\n    pub(crate) fn create_array_iterator(\n        array: JsObject,\n        kind: PropertyNameKind,\n        context: &Context,\n    ) -> JsValue {\n        let array_iterator = JsObject::from_proto_and_data_with_shared_shape(\n            context.root_shape(),\n            context.intrinsics().objects().iterator_prototypes().array(),\n            Self::new(array, kind),\n        );\n        array_iterator.into()\n    }\n\n    /// %ArrayIteratorPrototype%.next( )\n    ///\n    /// Gets the next result in the array.\n    ///\n    /// More information:\n    ///  - [ECMA reference][spec]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-%arrayiteratorprototype%.next\n    pub(crate) fn next(this: &JsValue, _: &[JsValue], context: &mut Context) -> JsResult<JsValue> {\n        let object = this.as_object();\n        let mut array_iterator = object\n            .as_ref()\n            .and_then(JsObject::downcast_mut::<Self>)\n            .ok_or_else(|| JsNativeError::typ().with_message(\"`this` is not an ArrayIterator\"))?;\n        let index = array_iterator.next_index;\n        if array_iterator.done {\n            return Ok(create_iter_result_object(\n                JsValue::undefined(),\n                true,\n                context,\n            ));\n        }\n\n        let len = if let Some(f) = array_iterator.array.downcast_ref::<TypedArray>() {\n            let buf = f.viewed_array_buffer().as_buffer();\n            let Some(buf) = buf\n                .bytes(std::sync::atomic::Ordering::SeqCst)\n                .filter(|buf| !f.is_out_of_bounds(buf.len()))\n            else {\n                return Err(JsNativeError::typ()\n                    .with_message(\"Cannot get value from out of bounds typed array\")\n                    .into());\n            };\n\n            f.array_length(buf.len())\n        } else {\n            array_iterator.array.length_of_array_like(context)?\n        };\n\n        if index >= len {\n            array_iterator.done = true;\n            return Ok(create_iter_result_object(\n                JsValue::undefined(),\n                true,\n                context,\n            ));\n        }\n        array_iterator.next_index = index + 1;\n        match array_iterator.kind {\n            PropertyNameKind::Key => Ok(create_iter_result_object(index.into(), false, context)),\n            PropertyNameKind::Value => {\n                let element_value = array_iterator.array.get(index, context)?;\n                Ok(create_iter_result_object(element_value, false, context))\n            }\n            PropertyNameKind::KeyAndValue => {\n                let element_value = array_iterator.array.get(index, context)?;\n                let result = Array::create_array_from_list([index.into(), element_value], context);\n                Ok(create_iter_result_object(result.into(), false, context))\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "core/engine/src/builtins/array/from_async.rs",
    "content": "use boa_gc::{Finalize, Trace};\n\nuse super::Array;\nuse crate::builtins::AsyncFromSyncIterator;\nuse crate::builtins::iterable::IteratorRecord;\nuse crate::builtins::promise::ResolvingFunctions;\nuse crate::native_function::{CoroutineState, NativeCoroutine};\nuse crate::object::{JsFunction, JsPromise};\nuse crate::{\n    Context, JsArgs, JsError, JsExpect, JsNativeError, JsObject, JsResult, JsSymbol, JsValue,\n    js_string,\n};\nuse std::cell::Cell;\n\nimpl Array {\n    /// [`Array.fromAsync ( asyncItems [ , mapfn [ , thisArg ] ] )`][spec]\n    ///\n    /// The `Array.fromAsync()` static method creates a new,\n    /// shallow-copied Array instance from a list or iterator of Promise-like values.\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///\n    /// [spec]: https://tc39.es/proposal-array-from-async/#sec-array.fromAsync\n    #[allow(clippy::unnecessary_wraps)]\n    pub(crate) fn from_async(\n        this: &JsValue,\n        args: &[JsValue],\n        context: &mut Context,\n    ) -> JsResult<JsValue> {\n        // 1. Let C be the this value.\n        // 2. Let promiseCapability be ! NewPromiseCapability(%Promise%).\n        let (promise, resolvers) = JsPromise::new_pending(context);\n\n        let async_items = args.get_or_undefined(0);\n        let mapfn = args.get_or_undefined(1);\n        let this_arg = args.get_or_undefined(2).clone();\n\n        // 3. Let fromAsyncClosure be a new Abstract Closure with no parameters that captures C, mapfn, and thisArg and\n        //    performs the following steps when called:\n        // 4. Perform AsyncFunctionStart(promiseCapability, fromAsyncClosure).\n        // NOTE: We avoid putting more state onto the coroutines by preprocessing all we can before allocating\n        //       the coroutines.\n        let result: JsResult<()> = (|| {\n            // a. If mapfn is undefined, let mapping be false.\n            let mapfn = if mapfn.is_undefined() {\n                None\n            } else {\n                // b. Else,\n                //     i. If IsCallable(mapfn) is false, throw a TypeError exception.\n                let Some(callable) = mapfn.as_callable() else {\n                    return Err(JsNativeError::typ()\n                        .with_message(\"Array.fromAsync: mapping function must be callable\")\n                        .into());\n                };\n                //     ii. Let mapping be true.\n                Some(JsFunction::from_object_unchecked(callable))\n            };\n\n            // c. Let usingAsyncIterator be ? GetMethod(asyncItems, @@asyncIterator).\n            // d. If usingAsyncIterator is undefined, then\n            //     i. Let usingSyncIterator be ? GetMethod(asyncItems, @@iterator).\n            // e. Let iteratorRecord be undefined.\n            // f. If usingAsyncIterator is not undefined, then\n            let iterator_record = if let Some(method) =\n                async_items.get_method(JsSymbol::async_iterator(), context)?\n            {\n                // i. Set iteratorRecord to ? GetIterator(asyncItems, async, usingAsyncIterator).\n                async_items.get_iterator_from_method(&method, context)?\n            }\n            // g. Else if usingSyncIterator is not undefined, then\n            else if let Some(method) = async_items.get_method(JsSymbol::iterator(), context)? {\n                // i. Set iteratorRecord to ? CreateAsyncFromSyncIterator(GetIterator(asyncItems, sync, usingSyncIterator)).\n                AsyncFromSyncIterator::create(\n                    async_items.get_iterator_from_method(&method, context)?,\n                    context,\n                )\n            }\n            // i. Else,\n            else {\n                // i. NOTE: asyncItems is neither an AsyncIterable nor an Iterable so assume it is an array-like object.\n                // ii. Let arrayLike be ! ToObject(asyncItems).\n                let array_like = async_items.to_object(context)?;\n\n                // iii. Let len be ? LengthOfArrayLike(arrayLike).\n                let len = array_like.length_of_array_like(context)?;\n                // iv. If IsConstructor(C) is true, then\n                let a = if let Some(c) = this.as_constructor() {\n                    // 1. Let A be ? Construct(C, « 𝔽(len) »).\n                    c.construct(&[len.into()], None, context)?\n                }\n                // v. Else,\n                else {\n                    // 1. Let A be ? ArrayCreate(len).\n                    Array::array_create(len, None, context)?\n                };\n\n                let coroutine_state = (\n                    GlobalState {\n                        mapfn,\n                        this_arg,\n                        resolvers: resolvers.clone(),\n                    },\n                    Cell::new(Some(ArrayLikeStateMachine::LoopStart {\n                        array_like,\n                        a,\n                        len,\n                        // iii. Let k be 0.\n                        k: 0,\n                    })),\n                );\n\n                // Try to run the coroutine once to see if it finishes early.\n                // This avoids allocating a new coroutine that will immediately finish.\n                // Spec continues on `from_array_like`...\n                if let CoroutineState::Yielded(value) =\n                    from_array_like(Ok(JsValue::undefined()), &coroutine_state, context)?\n                {\n                    // Coroutine yielded. We need to allocate it for a future execution.\n                    JsPromise::resolve(value, context)?.await_native(\n                        NativeCoroutine::from_copy_closure_with_captures(\n                            from_array_like,\n                            coroutine_state,\n                        ),\n                        context,\n                    );\n                }\n\n                return Ok(());\n            };\n\n            // h. If iteratorRecord is not undefined, then\n\n            // i. If IsConstructor(C) is true, then\n            let a = if let Some(c) = this.as_constructor() {\n                // 1. Let A be ? Construct(C).\n                c.construct(&[], None, context)?\n            }\n            // ii. Else,\n            else {\n                // 1. Let A be ! ArrayCreate(0).\n                Array::array_create(0, None, context)?\n            };\n\n            let coroutine_state = (\n                GlobalState {\n                    mapfn,\n                    this_arg,\n                    resolvers: resolvers.clone(),\n                },\n                Cell::new(Some(AsyncIteratorStateMachine::LoopStart {\n                    // vi. Let k be 0.\n                    k: 0,\n                    a,\n                    iterator_record,\n                })),\n            );\n\n            // Try to run the coroutine once to see if it finishes early.\n            // This avoids allocating a new coroutine that will immediately finish.\n            // Spec continues on `from_async_iterator`...\n            if let CoroutineState::Yielded(value) =\n                from_async_iterator(Ok(JsValue::undefined()), &coroutine_state, context)?\n            {\n                JsPromise::resolve(value, context)?.await_native(\n                    NativeCoroutine::from_copy_closure_with_captures(\n                        from_async_iterator,\n                        coroutine_state,\n                    ),\n                    context,\n                );\n            }\n\n            Ok(())\n        })();\n\n        // AsyncFunctionStart ( promiseCapability, asyncFunctionBody )\n        // https://tc39.es/ecma262/#sec-async-functions-abstract-operations-async-function-start\n        // ->\n        // AsyncBlockStart ( promiseCapability, asyncBody, asyncContext )\n        // https://tc39.es/ecma262/#sec-asyncblockstart\n\n        // i. Assert: result is a throw completion.\n        if let Err(err) = result {\n            // ii. Perform ! Call(promiseCapability.[[Reject]], undefined, « result.[[Value]] »).\n            resolvers\n                .reject\n                .call(&JsValue::undefined(), &[err.into_opaque(context)?], context)\n                .js_expect(\"resolving functions cannot fail\")?;\n        }\n\n        // 5. Return promiseCapability.[[Promise]].\n        Ok(promise.into())\n    }\n}\n\n#[derive(Trace, Finalize)]\nstruct GlobalState {\n    mapfn: Option<JsFunction>,\n    this_arg: JsValue,\n    resolvers: ResolvingFunctions,\n}\n\n#[derive(Trace, Finalize)]\n#[boa_gc(unsafe_no_drop)]\nenum AsyncIteratorStateMachine {\n    LoopStart {\n        a: JsObject,\n        k: u64,\n        iterator_record: IteratorRecord,\n    },\n    LoopContinue {\n        a: JsObject,\n        k: u64,\n        iterator_record: IteratorRecord,\n    },\n    LoopEnd {\n        a: JsObject,\n        k: u64,\n        iterator_record: IteratorRecord,\n        mapped_value: Option<JsResult<JsValue>>,\n    },\n    AsyncIteratorCloseStart {\n        err: JsError,\n        iterator: JsObject,\n    },\n    AsyncIteratorCloseEnd {\n        err: JsError,\n    },\n}\n\n/// Part of [`Array.fromAsync ( asyncItems [ , mapfn [ , thisArg ] ] )`](https://tc39.es/proposal-array-from-async/#sec-array.fromAsync).\nfn from_async_iterator(\n    mut result: JsResult<JsValue>,\n    (global_state, state_machine): &(GlobalState, Cell<Option<AsyncIteratorStateMachine>>),\n    context: &mut Context,\n) -> JsResult<CoroutineState> {\n    let result = (|| {\n        let Some(mut sm) = state_machine.take() else {\n            return Ok(CoroutineState::Done);\n        };\n\n        // iv. Repeat,\n        loop {\n            match sm {\n                AsyncIteratorStateMachine::LoopStart {\n                    a,\n                    k,\n                    iterator_record,\n                } => {\n                    // Inverted conditional makes for a simpler code.\n                    if k < 2u64.pow(53) - 1 {\n                        // 2. Let Pk be ! ToString(𝔽(k)).\n                        // 3. Let nextResult be ? Call(iteratorRecord.[[NextMethod]], iteratorRecord.[[Iterator]]).\n                        let next_result = iterator_record.next_method().call(\n                            &iterator_record.iterator().clone().into(),\n                            &[],\n                            context,\n                        )?;\n\n                        state_machine.set(Some(AsyncIteratorStateMachine::LoopContinue {\n                            a,\n                            k,\n                            iterator_record,\n                        }));\n\n                        // 4. Set nextResult to ? Await(nextResult).\n                        return Ok(CoroutineState::Yielded(next_result));\n                    }\n\n                    // 1. If k ≥ 2**53 - 1, then\n\n                    // a. Let error be ThrowCompletion(a newly created TypeError object).\n                    // b. Return ? AsyncIteratorClose(iteratorRecord, error).\n                    sm = AsyncIteratorStateMachine::AsyncIteratorCloseStart {\n                        err: JsNativeError::typ()\n                            .with_message(\n                                \"Array.fromAsync: \\\n                                            reached the maximum number of elements in an array \\\n                                            (2^53 - 1)\",\n                            )\n                            .into(),\n                        iterator: iterator_record.iterator().clone(),\n                    };\n                }\n                AsyncIteratorStateMachine::LoopContinue {\n                    a,\n                    k,\n                    mut iterator_record,\n                } => {\n                    // `result` is `Await(nextResult)`.\n                    let result = std::mem::replace(&mut result, Ok(JsValue::undefined()));\n\n                    // 5. If nextResult is not an Object, throw a TypeError exception.\n                    // Implicit on the call to `update_result`.\n                    iterator_record.update_result(result?, context)?;\n\n                    // 6. Let done be ? IteratorComplete(nextResult).\n                    // 7. If done is true,\n                    if iterator_record.done() {\n                        // a. Perform ? Set(A, \"length\", 𝔽(k), true).\n                        a.set(js_string!(\"length\"), k, true, context)?;\n\n                        // b. Return Completion Record { [[Type]]: return, [[Value]]: A, [[Target]]: empty }.\n                        // AsyncFunctionStart ( promiseCapability, asyncFunctionBody )\n                        // https://tc39.es/ecma262/#sec-async-functions-abstract-operations-async-function-start\n                        // ->\n                        // AsyncBlockStart ( promiseCapability, asyncBody, asyncContext )\n                        // https://tc39.es/ecma262/#sec-asyncblockstart\n\n                        // g. Else if result is a return completion, then\n                        //        i. Perform ! Call(promiseCapability.[[Resolve]], undefined, « result.[[Value]] »).\n                        global_state\n                            .resolvers\n                            .resolve\n                            .call(&JsValue::undefined(), &[a.into()], context)\n                            .js_expect(\"resolving functions cannot fail\")?;\n\n                        return Ok(CoroutineState::Done);\n                    }\n\n                    // 8. Let nextValue be ? IteratorValue(nextResult).\n                    let next_value = iterator_record.value(context)?;\n                    // 9. If mapping is true, then\n                    if let Some(mapfn) = &global_state.mapfn {\n                        // a. Let mappedValue be Call(mapfn, thisArg, « nextValue, 𝔽(k) »).\n                        // b. IfAbruptCloseAsyncIterator(mappedValue, iteratorRecord).\n                        // https://tc39.es/proposal-array-from-async/#sec-ifabruptcloseasynciterator\n                        let mapped_value = match mapfn.call(\n                            &global_state.this_arg,\n                            &[next_value, k.into()],\n                            context,\n                        ) {\n                            // 1. If value is an abrupt completion, then\n                            Err(err) => {\n                                // a. Perform ? AsyncIteratorClose(iteratorRecord, value).\n                                // b. Return value.\n                                sm = AsyncIteratorStateMachine::AsyncIteratorCloseStart {\n                                    err,\n                                    iterator: iterator_record.iterator().clone(),\n                                };\n                                continue;\n                            }\n                            // 2. Else if value is a Completion Record, set value to value.[[Value]].\n                            Ok(value) => value,\n                        };\n                        state_machine.set(Some(AsyncIteratorStateMachine::LoopEnd {\n                            a,\n                            k,\n                            iterator_record,\n                            mapped_value: None,\n                        }));\n                        // c. Set mappedValue to Await(mappedValue).\n                        return Ok(CoroutineState::Yielded(mapped_value));\n                    }\n\n                    sm = AsyncIteratorStateMachine::LoopEnd {\n                        a,\n                        k,\n                        iterator_record,\n                        // 10. Else, let mappedValue be nextValue.\n                        mapped_value: Some(Ok(next_value)),\n                    }\n                }\n                AsyncIteratorStateMachine::LoopEnd {\n                    a,\n                    k,\n                    iterator_record,\n                    mapped_value,\n                } => {\n                    // Either awaited `mappedValue` or directly set `mappedValue` to `nextValue`.\n                    let result = std::mem::replace(&mut result, Ok(JsValue::undefined()));\n\n                    // d. IfAbruptCloseAsyncIterator(mappedValue, iteratorRecord).\n                    // https://tc39.es/proposal-array-from-async/#sec-ifabruptcloseasynciterator\n                    let mapped_value = match mapped_value.unwrap_or(result) {\n                        // 1. If value is an abrupt completion, then\n                        Err(err) => {\n                            // a. Perform ? AsyncIteratorClose(iteratorRecord, value).\n                            // b. Return value.\n                            sm = AsyncIteratorStateMachine::AsyncIteratorCloseStart {\n                                err,\n                                iterator: iterator_record.iterator().clone(),\n                            };\n                            continue;\n                        }\n                        // 2. Else if value is a Completion Record, set value to value.[[Value]].\n                        Ok(value) => value,\n                    };\n\n                    // 11. Let defineStatus be CreateDataPropertyOrThrow(A, Pk, mappedValue).\n                    sm = if let Err(err) = a.create_data_property_or_throw(k, mapped_value, context)\n                    {\n                        // 12. If defineStatus is an abrupt completion, return ? AsyncIteratorClose(iteratorRecord, defineStatus).\n                        AsyncIteratorStateMachine::AsyncIteratorCloseStart {\n                            err,\n                            iterator: iterator_record.iterator().clone(),\n                        }\n                    } else {\n                        AsyncIteratorStateMachine::LoopStart {\n                            a,\n                            // 13. Set k to k + 1.\n                            k: k + 1,\n                            iterator_record,\n                        }\n                    };\n                }\n                // AsyncIteratorClose ( iteratorRecord, completion )\n                // https://tc39.es/ecma262/#sec-asynciteratorclose\n                // Simplified for only error completions.\n                AsyncIteratorStateMachine::AsyncIteratorCloseStart { err, iterator } => {\n                    // 1. Assert: iteratorRecord.[[Iterator]] is an Object.\n                    // 2. Let iterator be iteratorRecord.[[Iterator]].\n                    // 3. Let innerResult be Completion(GetMethod(iterator, \"return\")).\n                    // 4. If innerResult is a normal completion, then\n                    //     a. Let return be innerResult.[[Value]].\n                    //     b. If return is undefined, return ? completion.\n                    //     c. Set innerResult to Completion(Call(return, iterator)).\n                    //     d. If innerResult is a normal completion, set innerResult to Completion(Await(innerResult.[[Value]])).\n                    // 5. If completion is a throw completion, return ? completion.\n                    let Ok(Some(ret)) = iterator.get_method(js_string!(\"return\"), context) else {\n                        return Err(err);\n                    };\n\n                    let Ok(value) = ret.call(&iterator.into(), &[], context) else {\n                        return Err(err);\n                    };\n\n                    state_machine.set(Some(AsyncIteratorStateMachine::AsyncIteratorCloseEnd {\n                        err,\n                    }));\n                    return Ok(CoroutineState::Yielded(value));\n                }\n                AsyncIteratorStateMachine::AsyncIteratorCloseEnd { err } => {\n                    // Awaited `innerResult.[[Value]]`.\n                    // Only need to return the original error.\n                    return Err(err);\n                }\n            }\n        }\n    })();\n\n    // AsyncFunctionStart ( promiseCapability, asyncFunctionBody )\n    // https://tc39.es/ecma262/#sec-async-functions-abstract-operations-async-function-start\n    // ->\n    // AsyncBlockStart ( promiseCapability, asyncBody, asyncContext )\n    // https://tc39.es/ecma262/#sec-asyncblockstart\n    match result {\n        Ok(cont) => Ok(cont),\n\n        // i. Assert: result is a throw completion.\n        Err(err) => {\n            // ii. Perform ! Call(promiseCapability.[[Reject]], undefined, « result.[[Value]] »).\n            global_state\n                .resolvers\n                .reject\n                .call(&JsValue::undefined(), &[err.into_opaque(context)?], context)\n                .js_expect(\"resolving functions cannot fail\")?;\n            Ok(CoroutineState::Done)\n        }\n    }\n}\n\n#[derive(Trace, Finalize)]\n#[boa_gc(unsafe_no_drop)]\n#[allow(clippy::enum_variant_names)]\nenum ArrayLikeStateMachine {\n    LoopStart {\n        array_like: JsObject,\n        a: JsObject,\n        len: u64,\n        k: u64,\n    },\n    LoopContinue {\n        array_like: JsObject,\n        a: JsObject,\n        len: u64,\n        k: u64,\n    },\n    LoopEnd {\n        array_like: JsObject,\n        a: JsObject,\n        len: u64,\n        k: u64,\n        mapped_value: Option<JsValue>,\n    },\n}\n\n/// Part of [`Array.fromAsync ( asyncItems [ , mapfn [ , thisArg ] ] )`](https://tc39.es/proposal-array-from-async/#sec-array.fromAsync).\nfn from_array_like(\n    mut result: JsResult<JsValue>,\n    (global_state, state_machine): &(GlobalState, Cell<Option<ArrayLikeStateMachine>>),\n    context: &mut Context,\n) -> JsResult<CoroutineState> {\n    let result: JsResult<_> = (|| {\n        let Some(mut sm) = state_machine.take() else {\n            return Ok(CoroutineState::Done);\n        };\n\n        loop {\n            match sm {\n                ArrayLikeStateMachine::LoopStart {\n                    array_like,\n                    a,\n                    len,\n                    k,\n                } => {\n                    // vii. Repeat, while k < len,\n                    if k >= len {\n                        // viii. Perform ? Set(A, \"length\", 𝔽(len), true).\n                        a.set(js_string!(\"length\"), len, true, context)?;\n\n                        // ix. Return Completion Record { [[Type]]: return, [[Value]]: A, [[Target]]: empty }.\n\n                        // AsyncFunctionStart ( promiseCapability, asyncFunctionBody )\n                        // https://tc39.es/ecma262/#sec-async-functions-abstract-operations-async-function-start\n                        // ->\n                        // AsyncBlockStart ( promiseCapability, asyncBody, asyncContext )\n                        // https://tc39.es/ecma262/#sec-asyncblockstart\n\n                        // g. Else if result is a return completion, then\n                        //        i. Perform ! Call(promiseCapability.[[Resolve]], undefined, « result.[[Value]] »).\n                        global_state\n                            .resolvers\n                            .resolve\n                            .call(&JsValue::undefined(), &[a.into()], context)\n                            .js_expect(\"resolving functions cannot fail\")?;\n\n                        return Ok(CoroutineState::Done);\n                    }\n\n                    // 1. Let Pk be ! ToString(𝔽(k)).\n                    // 2. Let kValue be ? Get(arrayLike, Pk).\n                    let k_value = array_like.get(k, context)?;\n                    state_machine.set(Some(ArrayLikeStateMachine::LoopContinue {\n                        array_like,\n                        a,\n                        len,\n                        k,\n                    }));\n\n                    // 3. Set kValue to ? Await(kValue).\n                    return Ok(CoroutineState::Yielded(k_value));\n                }\n                ArrayLikeStateMachine::LoopContinue {\n                    array_like,\n                    a,\n                    len,\n                    k,\n                } => {\n                    // Awaited kValue\n                    let k_value = std::mem::replace(&mut result, Ok(JsValue::undefined()))?;\n\n                    // 4. If mapping is true, then\n                    if let Some(mapfn) = &global_state.mapfn {\n                        // a. Let mappedValue be ? Call(mapfn, thisArg, « kValue, 𝔽(k) »).\n                        let mapped_value =\n                            mapfn.call(&global_state.this_arg, &[k_value, k.into()], context)?;\n                        state_machine.set(Some(ArrayLikeStateMachine::LoopEnd {\n                            array_like,\n                            a,\n                            len,\n                            k,\n                            mapped_value: None,\n                        }));\n\n                        // b. Set mappedValue to ? Await(mappedValue).\n                        return Ok(CoroutineState::Yielded(mapped_value));\n                    }\n                    // 5. Else, let mappedValue be kValue.\n                    sm = ArrayLikeStateMachine::LoopEnd {\n                        array_like,\n                        a,\n                        len,\n                        k,\n                        mapped_value: Some(k_value),\n                    }\n                }\n                ArrayLikeStateMachine::LoopEnd {\n                    array_like,\n                    a,\n                    len,\n                    k,\n                    mapped_value,\n                } => {\n                    // Either awaited `mappedValue` or directly set this from `kValue`.\n                    let result = std::mem::replace(&mut result, Ok(JsValue::undefined()))?;\n                    let mapped_value = mapped_value.unwrap_or(result);\n\n                    // 6. Perform ? CreateDataPropertyOrThrow(A, Pk, mappedValue).\n                    a.create_data_property_or_throw(k, mapped_value, context)?;\n\n                    // 7. Set k to k + 1.\n                    sm = ArrayLikeStateMachine::LoopStart {\n                        array_like,\n                        a,\n                        len,\n                        k: k + 1,\n                    }\n                }\n            }\n        }\n    })();\n\n    // AsyncFunctionStart ( promiseCapability, asyncFunctionBody )\n    // https://tc39.es/ecma262/#sec-async-functions-abstract-operations-async-function-start\n    // ->\n    // AsyncBlockStart ( promiseCapability, asyncBody, asyncContext )\n    // https://tc39.es/ecma262/#sec-asyncblockstart\n    match result {\n        Ok(cont) => Ok(cont),\n        // i. Assert: result is a throw completion.\n        Err(err) => {\n            // ii. Perform ! Call(promiseCapability.[[Reject]], undefined, « result.[[Value]] »).\n            global_state\n                .resolvers\n                .reject\n                .call(&JsValue::undefined(), &[err.into_opaque(context)?], context)\n                .js_expect(\"resolving functions cannot fail\")?;\n            Ok(CoroutineState::Done)\n        }\n    }\n}\n"
  },
  {
    "path": "core/engine/src/builtins/array/mod.rs",
    "content": "//! Boa's implementation of ECMAScript's global `Array` object.\n//!\n//! The ECMAScript `Array` class is a global object that is used in the construction of arrays; which are high-level, list-like objects.\n//!\n//! More information:\n//!  - [ECMAScript reference][spec]\n//!  - [MDN documentation][mdn]\n//!\n//! [spec]: https://tc39.es/ecma262/#sec-array-objects\n//! [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array\n\nuse boa_gc::{Finalize, Trace};\nuse thin_vec::ThinVec;\n\nuse crate::{\n    Context, JsArgs, JsExpect, JsResult, JsString,\n    builtins::{BuiltInObject, Number, iterable::if_abrupt_close_iterator},\n    context::intrinsics::{Intrinsics, StandardConstructor, StandardConstructors},\n    error::JsNativeError,\n    js_string,\n    object::{\n        CONSTRUCTOR, IndexedProperties, JsData, JsObject,\n        internal_methods::{\n            InternalMethodPropertyContext, InternalObjectMethods, ORDINARY_INTERNAL_METHODS,\n            get_prototype_from_constructor, ordinary_define_own_property,\n            ordinary_get_own_property,\n        },\n    },\n    property::{Attribute, PropertyDescriptor, PropertyKey, PropertyNameKind},\n    realm::Realm,\n    string::StaticJsStrings,\n    symbol::JsSymbol,\n    value::{IntegerOrInfinity, JsValue},\n};\nuse std::cmp::{Ordering, min};\n\nuse super::{BuiltInBuilder, BuiltInConstructor, IntrinsicObject};\n\nmod array_iterator;\nuse crate::value::JsVariant;\npub(crate) use array_iterator::ArrayIterator;\n\n#[cfg(feature = \"experimental\")]\nmod from_async;\n\n#[cfg(test)]\nmod tests;\n\n/// Direction for `find_via_predicate`\n#[derive(Clone, Copy, Eq, PartialEq)]\npub(crate) enum Direction {\n    Ascending,\n    Descending,\n}\n\n/// JavaScript `Array` built-in implementation.\n#[derive(Debug, Clone, Copy, Trace, Finalize)]\n#[boa_gc(empty_trace)]\npub(crate) struct Array;\n\n/// Definitions of the internal object methods for array exotic objects.\n///\n/// More information:\n///  - [ECMAScript reference][spec]\n///\n/// [spec]: https://tc39.es/ecma262/#sec-array-exotic-objects\npub(crate) static ARRAY_EXOTIC_INTERNAL_METHODS: InternalObjectMethods = InternalObjectMethods {\n    __define_own_property__: array_exotic_define_own_property,\n    ..ORDINARY_INTERNAL_METHODS\n};\n\nimpl JsData for Array {\n    fn internal_methods(&self) -> &'static InternalObjectMethods {\n        &ARRAY_EXOTIC_INTERNAL_METHODS\n    }\n}\n\nimpl IntrinsicObject for Array {\n    fn init(realm: &Realm) {\n        let symbol_iterator = JsSymbol::iterator();\n        let symbol_unscopables = JsSymbol::unscopables();\n\n        let get_species = BuiltInBuilder::callable(realm, Self::get_species)\n            .name(js_string!(\"get [Symbol.species]\"))\n            .build();\n\n        let values_function = BuiltInBuilder::callable_with_object(\n            realm,\n            realm.intrinsics().objects().array_prototype_values().into(),\n            Self::values,\n        )\n        .name(js_string!(\"values\"))\n        .build();\n\n        let to_string_function = BuiltInBuilder::callable_with_object(\n            realm,\n            realm\n                .intrinsics()\n                .objects()\n                .array_prototype_to_string()\n                .into(),\n            Self::to_string,\n        )\n        .name(js_string!(\"toString\"))\n        .build();\n\n        let unscopables_object = Self::unscopables_object();\n\n        let builder = BuiltInBuilder::from_standard_constructor::<Self>(realm)\n            // Static Methods\n            .static_method(Self::from, js_string!(\"from\"), 1)\n            .static_method(Self::is_array, js_string!(\"isArray\"), 1)\n            .static_method(Self::of, js_string!(\"of\"), 0)\n            .static_accessor(\n                JsSymbol::species(),\n                Some(get_species),\n                None,\n                Attribute::CONFIGURABLE,\n            )\n            .property(\n                StaticJsStrings::LENGTH,\n                0,\n                Attribute::WRITABLE | Attribute::NON_ENUMERABLE | Attribute::PERMANENT,\n            )\n            .method(Self::at, js_string!(\"at\"), 1)\n            .method(Self::concat, js_string!(\"concat\"), 1)\n            .method(Self::copy_within, js_string!(\"copyWithin\"), 2)\n            .method(Self::entries, js_string!(\"entries\"), 0)\n            .method(Self::every, js_string!(\"every\"), 1)\n            .method(Self::fill, js_string!(\"fill\"), 1)\n            .method(Self::filter, js_string!(\"filter\"), 1)\n            .method(Self::find, js_string!(\"find\"), 1)\n            .method(Self::find_index, js_string!(\"findIndex\"), 1)\n            .method(Self::find_last, js_string!(\"findLast\"), 1)\n            .method(Self::find_last_index, js_string!(\"findLastIndex\"), 1)\n            .method(Self::flat, js_string!(\"flat\"), 0)\n            .method(Self::flat_map, js_string!(\"flatMap\"), 1)\n            .method(Self::for_each, js_string!(\"forEach\"), 1)\n            .method(Self::includes_value, js_string!(\"includes\"), 1)\n            .method(Self::index_of, js_string!(\"indexOf\"), 1)\n            .method(Self::join, js_string!(\"join\"), 1)\n            .method(Self::keys, js_string!(\"keys\"), 0)\n            .method(Self::last_index_of, js_string!(\"lastIndexOf\"), 1)\n            .method(Self::map, js_string!(\"map\"), 1)\n            .method(Self::pop, js_string!(\"pop\"), 0)\n            .method(Self::push, js_string!(\"push\"), 1)\n            .method(Self::reduce, js_string!(\"reduce\"), 1)\n            .method(Self::reduce_right, js_string!(\"reduceRight\"), 1)\n            .method(Self::reverse, js_string!(\"reverse\"), 0)\n            .method(Self::shift, js_string!(\"shift\"), 0)\n            .method(Self::slice, js_string!(\"slice\"), 2)\n            .method(Self::some, js_string!(\"some\"), 1)\n            .method(Self::sort, js_string!(\"sort\"), 1)\n            .method(Self::splice, js_string!(\"splice\"), 2)\n            .method(Self::to_locale_string, js_string!(\"toLocaleString\"), 0)\n            .method(Self::to_reversed, js_string!(\"toReversed\"), 0)\n            .method(Self::to_sorted, js_string!(\"toSorted\"), 1)\n            .method(Self::to_spliced, js_string!(\"toSpliced\"), 2)\n            .method(Self::unshift, js_string!(\"unshift\"), 1)\n            .method(Self::with, js_string!(\"with\"), 2)\n            .property(\n                js_string!(\"toString\"),\n                to_string_function,\n                Attribute::WRITABLE | Attribute::NON_ENUMERABLE | Attribute::CONFIGURABLE,\n            )\n            .property(\n                js_string!(\"values\"),\n                values_function.clone(),\n                Attribute::WRITABLE | Attribute::NON_ENUMERABLE | Attribute::CONFIGURABLE,\n            )\n            .property(\n                symbol_iterator,\n                values_function,\n                Attribute::WRITABLE | Attribute::NON_ENUMERABLE | Attribute::CONFIGURABLE,\n            )\n            .property(\n                symbol_unscopables,\n                unscopables_object,\n                Attribute::READONLY | Attribute::NON_ENUMERABLE | Attribute::CONFIGURABLE,\n            );\n\n        #[cfg(feature = \"experimental\")]\n        let builder = builder.static_method(Self::from_async, js_string!(\"fromAsync\"), 1);\n\n        builder.build();\n    }\n\n    fn get(intrinsics: &Intrinsics) -> JsObject {\n        Self::STANDARD_CONSTRUCTOR(intrinsics.constructors()).constructor()\n    }\n}\n\nimpl BuiltInObject for Array {\n    const NAME: JsString = StaticJsStrings::ARRAY;\n}\n\nimpl BuiltInConstructor for Array {\n    const PROTOTYPE_STORAGE_SLOTS: usize = 41;\n    const CONSTRUCTOR_STORAGE_SLOTS: usize = 6;\n\n    const CONSTRUCTOR_ARGUMENTS: usize = 1;\n\n    const STANDARD_CONSTRUCTOR: fn(&StandardConstructors) -> &StandardConstructor =\n        StandardConstructors::array;\n\n    fn constructor(\n        new_target: &JsValue,\n        args: &[JsValue],\n        context: &mut Context,\n    ) -> JsResult<JsValue> {\n        // If NewTarget is undefined, let newTarget be the active function object; else let newTarget be NewTarget.\n        let new_target = &if new_target.is_undefined() {\n            context\n                .active_function_object()\n                .unwrap_or_else(|| context.intrinsics().constructors().array().constructor())\n                .into()\n        } else {\n            new_target.clone()\n        };\n\n        // 2. Let proto be ? GetPrototypeFromConstructor(newTarget, \"%Array.prototype%\").\n        let prototype =\n            get_prototype_from_constructor(new_target, StandardConstructors::array, context)?;\n\n        // 3. Let numberOfArgs be the number of elements in values.\n        let number_of_args = args.len();\n\n        // 4. If numberOfArgs = 0, then\n        if number_of_args == 0 {\n            // 4.a. Return ! ArrayCreate(0, proto).\n            Ok(Self::array_create(0, Some(prototype), context)?.into())\n        // 5. Else if numberOfArgs = 1, then\n        } else if number_of_args == 1 {\n            // a. Let len be values[0].\n            let len = &args[0];\n            // b. Let array be ! ArrayCreate(0, proto).\n            let array = Self::array_create(0, Some(prototype), context)?;\n            // c. If Type(len) is not Number, then\n            #[allow(clippy::if_not_else)]\n            let int_len = if !len.is_number() {\n                // i. Perform ! CreateDataPropertyOrThrow(array, \"0\", len).\n                array\n                    .create_data_property_or_throw(0, len.clone(), context)\n                    .js_expect(\"this CreateDataPropertyOrThrow call must not fail\")?;\n                // ii. Let intLen be 1𝔽.\n                1\n            // d. Else,\n            } else {\n                // i. Let intLen be ! ToUint32(len).\n                let int_len = len\n                    .to_u32(context)\n                    .js_expect(\"this ToUint32 call must not fail\")?;\n                // ii. If SameValueZero(intLen, len) is false, throw a RangeError exception.\n                if !JsValue::same_value_zero(&int_len.into(), len) {\n                    return Err(JsNativeError::range()\n                        .with_message(\"invalid array length\")\n                        .into());\n                }\n                int_len\n            };\n            // e. Perform ! Set(array, \"length\", intLen, true).\n            array\n                .set(StaticJsStrings::LENGTH, int_len, true, context)\n                .js_expect(\"this Set call must not fail\")?;\n            // f. Return array.\n            Ok(array.into())\n        // 6. Else,\n        } else {\n            // 6.a. Assert: numberOfArgs ≥ 2.\n            debug_assert!(number_of_args >= 2);\n\n            // b. Let array be ? ArrayCreate(numberOfArgs, proto).\n            let array = Self::array_create(number_of_args as u64, Some(prototype), context)?;\n\n            // c. Let k be 0.\n            // d. Repeat, while k < numberOfArgs,\n            //    i. Let Pk be ! ToString(𝔽(k)).\n            //    ii. Let itemK be values[k].\n            //    iii. Perform ! CreateDataPropertyOrThrow(array, Pk, itemK).\n            //    iv. Set k to k + 1.\n            array\n                .borrow_mut()\n                .properties_mut()\n                .override_indexed_properties(args.iter().cloned().collect());\n\n            // e. Assert: The mathematical value of array's \"length\" property is numberOfArgs.\n            // f. Return array.\n            Ok(array.into())\n        }\n    }\n}\n\nimpl Array {\n    /// Optimized helper function, that sets the length of the array.\n    fn set_length(o: &JsObject, len: u64, context: &mut Context) -> JsResult<()> {\n        if o.is_array() && len < (2u64.pow(32) - 1) {\n            let mut borrowed_object = o.borrow_mut();\n            if borrowed_object.properties().shape.to_addr_usize()\n                == context\n                    .intrinsics()\n                    .templates()\n                    .array()\n                    .shape()\n                    .to_addr_usize()\n            {\n                // NOTE: The \"length\" property is the first element.\n                borrowed_object.properties_mut().storage[0] = JsValue::new(len);\n                return Ok(());\n            }\n        }\n\n        o.set(StaticJsStrings::LENGTH, len, true, context)?;\n        Ok(())\n    }\n\n    /// Utility for constructing `Array` objects.\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-arraycreate\n    pub(crate) fn array_create(\n        length: u64,\n        prototype: Option<JsObject>,\n        context: &mut Context,\n    ) -> JsResult<JsObject> {\n        // 1. If length > 2^32 - 1, throw a RangeError exception.\n        if length > 2u64.pow(32) - 1 {\n            return Err(JsNativeError::range()\n                .with_message(\"array exceeded max size\")\n                .into());\n        }\n\n        // Fast path:\n        if prototype.is_none() {\n            return Ok(context\n                .intrinsics()\n                .templates()\n                .array()\n                .create(Array, vec![JsValue::new(length)]));\n        }\n\n        // 7. Return A.\n        // 2. If proto is not present, set proto to %Array.prototype%.\n        // 3. Let A be ! MakeBasicObject(« [[Prototype]], [[Extensible]] »).\n        // 4. Set A.[[Prototype]] to proto.\n        // 5. Set A.[[DefineOwnProperty]] as specified in 10.4.2.1.\n        let prototype =\n            prototype.unwrap_or_else(|| context.intrinsics().constructors().array().prototype());\n\n        // Fast path:\n        if context\n            .intrinsics()\n            .templates()\n            .array()\n            .has_prototype(&prototype)\n        {\n            return Ok(context\n                .intrinsics()\n                .templates()\n                .array()\n                .create(Array, vec![JsValue::new(length)]));\n        }\n\n        let array =\n            JsObject::from_proto_and_data_with_shared_shape(context.root_shape(), prototype, Array)\n                .upcast();\n\n        // 6. Perform ! OrdinaryDefineOwnProperty(A, \"length\", PropertyDescriptor { [[Value]]: 𝔽(length), [[Writable]]: true, [[Enumerable]]: false, [[Configurable]]: false }).\n        ordinary_define_own_property(\n            &array,\n            &StaticJsStrings::LENGTH.into(),\n            PropertyDescriptor::builder()\n                .value(length)\n                .writable(true)\n                .enumerable(false)\n                .configurable(false)\n                .build(),\n            &mut InternalMethodPropertyContext::new(context),\n        )?;\n\n        Ok(array)\n    }\n\n    /// Utility for constructing `Array` objects from an iterator of `JsValue`s.\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-createarrayfromlist\n    pub(crate) fn create_array_from_list<I>(elements: I, context: &Context) -> JsObject\n    where\n        I: IntoIterator<Item = JsValue>,\n    {\n        // 1. Assert: elements is a List whose elements are all ECMAScript language values.\n        // 2. Let array be ! ArrayCreate(0).\n        // 3. Let n be 0.\n        // 4. For each element e of elements, do\n        //     a. Perform ! CreateDataPropertyOrThrow(array, ! ToString(𝔽(n)), e).\n        //     b. Set n to n + 1.\n        // 5. Return array.\n        // NOTE: This deviates from the spec, but it should have the same behaviour.\n        let elements: ThinVec<_> = elements.into_iter().collect();\n        let length = elements.len();\n\n        context\n            .intrinsics()\n            .templates()\n            .array()\n            .create_with_indexed_properties(\n                Array,\n                vec![JsValue::new(length)],\n                IndexedProperties::from_dense_js_value(elements),\n            )\n    }\n\n    /// Utility function for concatenating array objects.\n    ///\n    /// Returns a Boolean valued property that if `true` indicates that\n    /// an object should be flattened to its array elements\n    /// by `Array.prototype.concat`.\n    fn is_concat_spreadable(o: &JsValue, context: &mut Context) -> JsResult<bool> {\n        // 1. If Type(O) is not Object, return false.\n        let Some(o) = o.as_object() else {\n            return Ok(false);\n        };\n\n        // 2. Let spreadable be ? Get(O, @@isConcatSpreadable).\n        let spreadable = o.get(JsSymbol::is_concat_spreadable(), context)?;\n\n        // 3. If spreadable is not undefined, return ! ToBoolean(spreadable).\n        if !spreadable.is_undefined() {\n            return Ok(spreadable.to_boolean());\n        }\n\n        // 4. Return ? IsArray(O).\n        o.is_array_abstract()\n    }\n\n    /// `get Array [ @@species ]`\n    ///\n    /// The `Array [ @@species ]` accessor property returns the Array constructor.\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///  - [MDN documentation][mdn]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-get-array-@@species\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/@@species\n    #[allow(clippy::unnecessary_wraps)]\n    fn get_species(this: &JsValue, _: &[JsValue], _: &mut Context) -> JsResult<JsValue> {\n        // 1. Return the this value.\n        Ok(this.clone())\n    }\n\n    /// Utility function used to specify the creation of a new Array object using a constructor\n    /// function that is derived from `original_array`.\n    ///\n    /// see: <https://tc39.es/ecma262/#sec-arrayspeciescreate>\n    pub(crate) fn array_species_create(\n        original_array: &JsObject,\n        length: u64,\n        context: &mut Context,\n    ) -> JsResult<JsObject> {\n        // 1. Let isArray be ? IsArray(originalArray).\n        // 2. If isArray is false, return ? ArrayCreate(length).\n        if !original_array.is_array_abstract()? {\n            return Self::array_create(length, None, context);\n        }\n        // 3. Let C be ? Get(originalArray, \"constructor\").\n        let c = original_array.get(CONSTRUCTOR, context)?;\n\n        // 4. If IsConstructor(C) is true, then\n        if let Some(c) = c.as_constructor() {\n            // a. Let thisRealm be the current Realm Record.\n            let this_realm = context.realm().clone();\n            // b. Let realmC be ? GetFunctionRealm(C).\n            let realm_c = c.get_function_realm(context)?;\n\n            // c. If thisRealm and realmC are not the same Realm Record, then\n            if this_realm != realm_c\n                && c == realm_c.intrinsics().constructors().array().constructor()\n            {\n                // i. If SameValue(C, realmC.[[Intrinsics]].[[%Array%]]) is true, set C to undefined.\n                // Note: fast path to step 6.\n                return Self::array_create(length, None, context);\n            }\n        }\n\n        // 5. If Type(C) is Object, then\n        let c = if let Some(c) = c.as_object() {\n            // 5.a. Set C to ? Get(C, @@species).\n            let c = c.get(JsSymbol::species(), context)?;\n            // 5.b. If C is null, set C to undefined.\n            if c.is_null_or_undefined() {\n                JsValue::undefined()\n            } else {\n                c\n            }\n        } else {\n            c\n        };\n\n        // 6. If C is undefined, return ? ArrayCreate(length).\n        if c.is_undefined() {\n            return Self::array_create(length, None, context);\n        }\n\n        if let Some(c) = c.as_constructor() {\n            // 8. Return ? Construct(C, « 𝔽(length) »).\n            return c.construct(&[JsValue::new(length)], Some(&c), context);\n        }\n\n        // 7. If IsConstructor(C) is false, throw a TypeError exception.\n        Err(JsNativeError::typ()\n            .with_message(\"Symbol.species must be a constructor\")\n            .into())\n    }\n\n    /// `Array.from(arrayLike)`\n    ///\n    /// The `Array.from()` static method creates a new,\n    /// shallow-copied Array instance from an array-like or iterable object.\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///  - [MDN documentation][mdn]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-array.from\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/from\n    pub(crate) fn from(\n        this: &JsValue,\n        args: &[JsValue],\n        context: &mut Context,\n    ) -> JsResult<JsValue> {\n        let items = args.get_or_undefined(0);\n        let mapfn = args.get_or_undefined(1);\n        let this_arg = args.get_or_undefined(2);\n\n        // 2. If mapfn is undefined, let mapping be false\n        // 3. Else,\n        //     a. If IsCallable(mapfn) is false, throw a TypeError exception.\n        //     b. Let mapping be true.\n        let mapping = match mapfn.variant() {\n            JsVariant::Undefined => None,\n            JsVariant::Object(o) if o.is_callable() => Some(o),\n            _ => {\n                return Err(JsNativeError::typ()\n                    .with_message(format!(\"`{}` is not callable\", mapfn.type_of()))\n                    .into());\n            }\n        };\n\n        // 4. Let usingIterator be ? GetMethod(items, @@iterator).\n        let using_iterator = items.get_method(JsSymbol::iterator(), context)?;\n\n        let Some(using_iterator) = using_iterator else {\n            // 6. NOTE: items is not an Iterable so assume it is an array-like object.\n            // 7. Let arrayLike be ! ToObject(items).\n            let array_like = items\n                .to_object(context)\n                .js_expect(\"should not fail according to spec\")?;\n\n            // 8. Let len be ? LengthOfArrayLike(arrayLike).\n            let len = array_like.length_of_array_like(context)?;\n\n            // 9. If IsConstructor(C) is true, then\n            //     a. Let A be ? Construct(C, « 𝔽(len) »).\n            // 10. Else,\n            //     a. Let A be ? ArrayCreate(len).\n            let a = match this.as_constructor() {\n                Some(constructor) => constructor.construct(&[len.into()], None, context)?,\n                _ => Self::array_create(len, None, context)?,\n            };\n\n            // 11. Let k be 0.\n            // 12. Repeat, while k < len,\n            //     ...\n            //     f. Set k to k + 1.\n            for k in 0..len {\n                // a. Let Pk be ! ToString(𝔽(k)).\n                // b. Let kValue be ? Get(arrayLike, Pk).\n                let k_value = array_like.get(k, context)?;\n\n                let mapped_value = if let Some(ref mapfn) = mapping {\n                    // c. If mapping is true, then\n                    //     i. Let mappedValue be ? Call(mapfn, thisArg, « kValue, 𝔽(k) »).\n                    mapfn.call(this_arg, &[k_value, k.into()], context)?\n                } else {\n                    // d. Else, let mappedValue be kValue.\n                    k_value\n                };\n\n                // e. Perform ? CreateDataPropertyOrThrow(A, Pk, mappedValue).\n                a.create_data_property_or_throw(k, mapped_value, context)?;\n            }\n\n            // 13. Perform ? Set(A, \"length\", 𝔽(len), true).\n            a.set(StaticJsStrings::LENGTH, len, true, context)?;\n\n            // 14. Return A.\n            return Ok(a.into());\n        };\n\n        // 5. If usingIterator is not undefined, then\n\n        // a. If IsConstructor(C) is true, then\n        //     i. Let A be ? Construct(C).\n        // b. Else,\n        //     i. Let A be ? ArrayCreate(0en).\n        let a = match this.as_constructor() {\n            Some(constructor) => constructor.construct(&[], None, context)?,\n            _ => Self::array_create(0, None, context)?,\n        };\n\n        // c. Let iteratorRecord be ? GetIteratorFromMethod(items, usingIterator).\n        let mut iterator_record = items.get_iterator_from_method(&using_iterator, context)?;\n\n        // d. Let k be 0.\n        // e. Repeat,\n        //     i. If k ≥ 2^53 - 1 (MAX_SAFE_INTEGER), then\n        //     ...\n        //     ix. Set k to k + 1.\n        for k in 0..9_007_199_254_740_991_u64 {\n            // iii. Let next be ? IteratorStepValue(iteratorRecord).\n            let Some(next) = iterator_record.step_value(context)? else {\n                // iv. If next is done, then\n                //     1. Perform ? Set(A, \"length\", 𝔽(k), true).\n                a.set(StaticJsStrings::LENGTH, k, true, context)?;\n                //     2. Return A.\n                return Ok(a.into());\n            };\n\n            // v. If mapping is true, then\n            let mapped_value = if let Some(ref mapfn) = mapping {\n                // 1. Let mappedValue be Completion(Call(mapper, thisArg, « next, 𝔽(k) »)).\n                let mapped_value = mapfn.call(this_arg, &[next, k.into()], context);\n\n                // 2. IfAbruptCloseIterator(mappedValue, iteratorRecord).\n                if_abrupt_close_iterator!(mapped_value, iterator_record, context)\n            } else {\n                // vi. Else,\n                //     1. Let mappedValue be next.\n                next\n            };\n\n            // vii. Let defineStatus be Completion(CreateDataPropertyOrThrow(A, Pk, mappedValue)).\n            let define_status = a.create_data_property_or_throw(k, mapped_value, context);\n\n            // viii. IfAbruptCloseIterator(defineStatus, iteratorRecord).\n            if_abrupt_close_iterator!(define_status, iterator_record, context);\n        }\n\n        // NOTE: The loop above has to return before it reaches iteration limit,\n        // which is why it's safe to have this as the fallback return\n        //\n        // 1. Let error be ThrowCompletion(a newly created TypeError object).\n        let error = Err(JsNativeError::typ()\n            .with_message(\"Invalid array length\")\n            .into());\n\n        // 2. Return ? IteratorClose(iteratorRecord, error).\n        iterator_record.close(error, context)\n    }\n\n    /// `Array.isArray( arg )`\n    ///\n    /// The isArray function takes one argument arg, and returns the Boolean value true\n    /// if the argument is an object whose class internal property is \"Array\"; otherwise it returns false.\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///  - [MDN documentation][mdn]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-array.isarray\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/isArray\n    pub(crate) fn is_array(\n        _: &JsValue,\n        args: &[JsValue],\n        _context: &mut Context,\n    ) -> JsResult<JsValue> {\n        // 1. Return ? IsArray(arg).\n        args.get_or_undefined(0).is_array().map(Into::into)\n    }\n\n    /// `Array.of(...items)`\n    ///\n    /// The Array.of method creates a new Array instance from a variable number of arguments,\n    /// regardless of the number or type of arguments.\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///  - [MDN documentation][mdn]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-array.of\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/of\n    pub(crate) fn of(this: &JsValue, args: &[JsValue], context: &mut Context) -> JsResult<JsValue> {\n        // 1. Let len be the number of elements in items.\n        // 2. Let lenNumber be 𝔽(len).\n        let len = args.len();\n\n        // 3. Let C be the this value.\n        // 4. If IsConstructor(C) is true, then\n        //     a. Let A be ? Construct(C, « lenNumber »).\n        // 5. Else,\n        //     a. Let A be ? ArrayCreate(len).\n        let a = match this.as_constructor() {\n            Some(constructor) => constructor.construct(&[len.into()], None, context)?,\n            _ => Self::array_create(len as u64, None, context)?,\n        };\n\n        // 6. Let k be 0.\n        // 7. Repeat, while k < len,\n        for (k, value) in args.iter().enumerate() {\n            // a. Let kValue be items[k].\n            // b. Let Pk be ! ToString(𝔽(k)).\n            // c. Perform ? CreateDataPropertyOrThrow(A, Pk, kValue).\n            a.create_data_property_or_throw(k, value.clone(), context)?;\n            // d. Set k to k + 1.\n        }\n\n        // 8. Perform ? Set(A, \"length\", lenNumber, true).\n        Self::set_length(&a, len as u64, context)?;\n\n        // 9. Return A.\n        Ok(a.into())\n    }\n\n    ///'Array.prototype.at(index)'\n    ///\n    /// The `at()` method takes an integer value and returns the item at that\n    /// index, allowing for positive and negative integers. Negative integers\n    /// count back from the last item in the array.\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///  - [MDN documentation][mdn]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-array.prototype.at\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/at\n    pub(crate) fn at(this: &JsValue, args: &[JsValue], context: &mut Context) -> JsResult<JsValue> {\n        //1. let O be ? ToObject(this value)\n        let obj = this.to_object(context)?;\n        //2. let len be ? LengthOfArrayLike(O)\n        let len = obj.length_of_array_like(context)? as i64;\n        //3. let relativeIndex be ? ToIntegerOrInfinity(index)\n        let relative_index = args.get_or_undefined(0).to_integer_or_infinity(context)?;\n        let k = match relative_index {\n            //4. if relativeIndex >= 0, then let k be relativeIndex\n            //check if positive and if below length of array\n            IntegerOrInfinity::Integer(i) if i >= 0 && i < len => i,\n            //5. Else, let k be len + relativeIndex\n            //integer should be negative, so abs() and check if less than or equal to length of array\n            IntegerOrInfinity::Integer(i) if i < 0 && i.abs() <= len => len + i,\n            //handle most likely impossible case of\n            //IntegerOrInfinity::NegativeInfinity || IntegerOrInfinity::PositiveInfinity\n            //by returning undefined\n            _ => return Ok(JsValue::undefined()),\n        };\n        //6. if k < 0  or k >= len,\n        //handled by the above match guards\n        //7. Return ? Get(O, !ToString(𝔽(k)))\n        obj.get(k, context)\n    }\n\n    /// `Array.prototype.concat(...arguments)`\n    ///\n    /// When the concat method is called with zero or more arguments, it returns an\n    /// array containing the array elements of the object followed by the array\n    /// elements of each argument in order.\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///  - [MDN documentation][mdn]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-array.prototype.concat\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/concat\n    pub(crate) fn concat(\n        this: &JsValue,\n        args: &[JsValue],\n        context: &mut Context,\n    ) -> JsResult<JsValue> {\n        // 1. Let O be ? ToObject(this value).\n        let obj = this.to_object(context)?;\n        // 2. Let A be ? ArraySpeciesCreate(O, 0).\n        let arr = Self::array_species_create(&obj, 0, context)?;\n        // 3. Let n be 0.\n        let mut n = 0;\n        // 4. Prepend O to items.\n        // 5. For each element E of items, do\n        for item in std::iter::once(&JsValue::new(obj)).chain(args.iter()) {\n            // a. Let spreadable be ? IsConcatSpreadable(E).\n            let spreadable = Self::is_concat_spreadable(item, context)?;\n            // b. If spreadable is true, then\n            if spreadable {\n                // item is guaranteed to be an object since is_concat_spreadable checks it,\n                // so we can call `.unwrap()`\n                let item = item.as_object().js_expect(\"guaranteed to be an object\")?;\n                // i. Let k be 0.\n                // ii. Let len be ? LengthOfArrayLike(E).\n                let len = item.length_of_array_like(context)?;\n                // iii. If n + len > 2^53 - 1, throw a TypeError exception.\n                if n + len > Number::MAX_SAFE_INTEGER as u64 {\n                    return Err(JsNativeError::typ()\n                        .with_message(\n                            \"length + number of arguments exceeds the max safe integer limit\",\n                        )\n                        .into());\n                }\n                // iv. Repeat, while k < len,\n                for k in 0..len {\n                    // 1. Let P be ! ToString(𝔽(k)).\n                    // 2. Let exists be ? HasProperty(E, P).\n                    // 3. If exists is true, then\n                    // 3.a. Let subElement be ? Get(E, P).\n                    if let Some(sub_element) = item.try_get(k, context)? {\n                        // b. Perform ? CreateDataPropertyOrThrow(A, ! ToString(𝔽(n)), subElement).\n                        arr.create_data_property_or_throw(n, sub_element, context)?;\n                    }\n                    // 4. Set n to n + 1.\n                    n += 1;\n                    // 5. Set k to k + 1.\n                }\n            }\n            // c. Else,\n            else {\n                // i. NOTE: E is added as a single item rather than spread.\n                // ii. If n ≥ 2^53 - 1, throw a TypeError exception.\n                if n >= Number::MAX_SAFE_INTEGER as u64 {\n                    return Err(JsNativeError::typ()\n                        .with_message(\"length exceeds the max safe integer limit\")\n                        .into());\n                }\n                // iii. Perform ? CreateDataPropertyOrThrow(A, ! ToString(𝔽(n)), E).\n                arr.create_data_property_or_throw(n, item.clone(), context)?;\n                // iv. Set n to n + 1.\n                n += 1;\n            }\n        }\n        // 6. Perform ? Set(A, \"length\", 𝔽(n), true).\n        Self::set_length(&arr, n, context)?;\n\n        // 7. Return A.\n        Ok(JsValue::new(arr))\n    }\n\n    /// `Array.prototype.push( ...items )`\n    ///\n    /// The arguments are appended to the end of the array, in the order in which\n    /// they appear. The new length of the array is returned as the result of the\n    /// call.\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///  - [MDN documentation][mdn]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-array.prototype.push\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/push\n    pub(crate) fn push(\n        this: &JsValue,\n        args: &[JsValue],\n        context: &mut Context,\n    ) -> JsResult<JsValue> {\n        // 1. Let O be ? ToObject(this value).\n        let o = this.to_object(context)?;\n        // 2. Let len be ? LengthOfArrayLike(O).\n        let mut len = o.length_of_array_like(context)?;\n        // 3. Let argCount be the number of elements in items.\n        let arg_count = args.len() as u64;\n        // 4. If len + argCount > 2^53 - 1, throw a TypeError exception.\n        if len + arg_count > 2u64.pow(53) - 1 {\n            return Err(JsNativeError::typ()\n                .with_message(\n                    \"the length + the number of arguments exceed the maximum safe integer limit\",\n                )\n                .into());\n        }\n        // 5. For each element E of items, do\n        for element in args.iter().cloned() {\n            // a. Perform ? Set(O, ! ToString(𝔽(len)), E, true).\n            o.set(len, element, true, context)?;\n            // b. Set len to len + 1.\n            len += 1;\n        }\n        // 6. Perform ? Set(O, \"length\", 𝔽(len), true).\n        Self::set_length(&o, len, context)?;\n\n        // 7. Return 𝔽(len).\n        Ok(len.into())\n    }\n\n    /// `Array.prototype.pop()`\n    ///\n    /// The last element of the array is removed from the array and returned.\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///  - [MDN documentation][mdn]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-array.prototype.pop\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/pop\n    pub(crate) fn pop(this: &JsValue, _: &[JsValue], context: &mut Context) -> JsResult<JsValue> {\n        // 1. Let O be ? ToObject(this value).\n        let o = this.to_object(context)?;\n        // 2. Let len be ? LengthOfArrayLike(O).\n        let len = o.length_of_array_like(context)?;\n        // 3. If len = 0, then\n        if len == 0 {\n            // a. Perform ? Set(O, \"length\", +0𝔽, true).\n            Self::set_length(&o, 0, context)?;\n\n            // b. Return undefined.\n            Ok(JsValue::undefined())\n        // 4. Else,\n        } else {\n            // a. Assert: len > 0.\n            // b. Let newLen be 𝔽(len - 1).\n            let new_len = len - 1;\n            // c. Let index be ! ToString(newLen).\n            let index = new_len;\n            // d. Let element be ? Get(O, index).\n            let element = o.get(index, context)?;\n            // e. Perform ? DeletePropertyOrThrow(O, index).\n            o.delete_property_or_throw(index, context)?;\n            // f. Perform ? Set(O, \"length\", newLen, true).\n            Self::set_length(&o, new_len, context)?;\n            // g. Return element.\n            Ok(element)\n        }\n    }\n\n    /// `Array.prototype.forEach( callbackFn [ , thisArg ] )`\n    ///\n    /// This method executes the provided callback function for each element in the array.\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///  - [MDN documentation][mdn]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-array.prototype.foreach\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/forEach\n    pub(crate) fn for_each(\n        this: &JsValue,\n        args: &[JsValue],\n        context: &mut Context,\n    ) -> JsResult<JsValue> {\n        // 1. Let O be ? ToObject(this value).\n        let o = this.to_object(context)?;\n        // 2. Let len be ? LengthOfArrayLike(O).\n        let len = o.length_of_array_like(context)?;\n        // 3. If IsCallable(callbackfn) is false, throw a TypeError exception.\n        let callback = args.get_or_undefined(0).as_callable().ok_or_else(|| {\n            JsNativeError::typ().with_message(\"Array.prototype.forEach: invalid callback function\")\n        })?;\n        // 4. Let k be 0.\n        // 5. Repeat, while k < len,\n        for k in 0..len {\n            // a. Let Pk be ! ToString(𝔽(k)).\n            let pk = k;\n            // b. Let kPresent be ? HasProperty(O, Pk).\n            // c. If kPresent is true, then\n            // c.i. Let kValue be ? Get(O, Pk).\n            if let Some(k_value) = o.try_get(pk, context)? {\n                // ii. Perform ? Call(callbackfn, thisArg, « kValue, 𝔽(k), O »).\n                let this_arg = args.get_or_undefined(1);\n                callback.call(this_arg, &[k_value, k.into(), o.clone().into()], context)?;\n            }\n            // d. Set k to k + 1.\n        }\n        // 6. Return undefined.\n        Ok(JsValue::undefined())\n    }\n\n    /// `Array.prototype.join( separator )`\n    ///\n    /// The elements of the array are converted to Strings, and these Strings are\n    /// then concatenated, separated by occurrences of the separator. If no\n    /// separator is provided, a single comma is used as the separator.\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///  - [MDN documentation][mdn]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-array.prototype.join\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/join\n    pub(crate) fn join(\n        this: &JsValue,\n        args: &[JsValue],\n        context: &mut Context,\n    ) -> JsResult<JsValue> {\n        // 1. Let O be ? ToObject(this value).\n        let o = this.to_object(context)?;\n        // 2. Let len be ? LengthOfArrayLike(O).\n        let len = o.length_of_array_like(context)?;\n        // 3. If separator is undefined, let sep be the single-element String \",\".\n        // 4. Else, let sep be ? ToString(separator).\n        let separator = args.get_or_undefined(0);\n        let separator = if separator.is_undefined() {\n            js_string!(\",\")\n        } else {\n            separator.to_string(context)?\n        };\n\n        // 5. Let R be the empty String.\n        let mut r = Vec::with_capacity(len as usize + len.saturating_sub(1) as usize);\n        // 6. Let k be 0.\n        // 7. Repeat, while k < len,\n        for k in 0..len {\n            // a. If k > 0, set R to the string-concatenation of R and sep.\n            if k > 0 {\n                r.push(separator.clone());\n            }\n            // b. Let element be ? Get(O, ! ToString(𝔽(k))).\n            let element = o.get(k, context)?;\n            // c. If element is undefined, null or the array itself, let next be the empty String; otherwise, let next be ? ToString(element).\n            let next = if element.is_null_or_undefined() || &element == this {\n                js_string!()\n            } else {\n                element.to_string(context)?\n            };\n            // d. Set R to the string-concatenation of R and next.\n            r.push(next.clone());\n            // e. Set k to k + 1.\n        }\n        // 8. Return R.\n        Ok(js_string!(&r[..]).into())\n    }\n\n    /// `Array.prototype.toString( separator )`\n    ///\n    /// The toString function is intentionally generic; it does not require that\n    /// its this value be an Array object. Therefore it can be transferred to\n    /// other kinds of objects for use as a method.\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///  - [MDN documentation][mdn]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-array.prototype.tostring\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/toString\n    #[allow(clippy::wrong_self_convention)]\n    pub(crate) fn to_string(\n        this: &JsValue,\n        _: &[JsValue],\n        context: &mut Context,\n    ) -> JsResult<JsValue> {\n        // 1. Let array be ? ToObject(this value).\n        let array = this.to_object(context)?;\n        // 2. Let func be ? Get(array, \"join\").\n        let func = array.get(js_string!(\"join\"), context)?;\n        // 3. If IsCallable(func) is false, set func to the intrinsic function %Object.prototype.toString%.\n        // 4. Return ? Call(func, array).\n        if let Some(func) = func.as_callable() {\n            func.call(&array.into(), &[], context)\n        } else {\n            crate::builtins::object::OrdinaryObject::to_string(&array.into(), &[], context)\n        }\n    }\n\n    /// `Array.prototype.reverse()`\n    ///\n    /// The elements of the array are rearranged so as to reverse their order.\n    /// The object is returned as the result of the call.\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///  - [MDN documentation][mdn]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-array.prototype.reverse\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/reverse\n    #[allow(clippy::else_if_without_else)]\n    pub(crate) fn reverse(\n        this: &JsValue,\n        _: &[JsValue],\n        context: &mut Context,\n    ) -> JsResult<JsValue> {\n        // 1. Let O be ? ToObject(this value).\n        let o = this.to_object(context)?;\n        // 2. Let len be ? LengthOfArrayLike(O).\n        let len = o.length_of_array_like(context)?;\n        // 3. Let middle be floor(len / 2).\n        let middle = len / 2;\n        // 4. Let lower be 0.\n        let mut lower = 0;\n        // 5. Repeat, while lower ≠ middle,\n        while lower != middle {\n            // a. Let upper be len - lower - 1.\n            let upper = len - lower - 1;\n            // Skipped: b. Let upperP be ! ToString(𝔽(upper)).\n            // Skipped: c. Let lowerP be ! ToString(𝔽(lower)).\n            // d. Let lowerExists be ? HasProperty(O, lowerP).\n            // e. If lowerExists is true, then\n            // e.i. Let lowerValue be ? Get(O, lowerP).\n            let lower_value = o.try_get(lower, context)?;\n            // f. Let upperExists be ? HasProperty(O, upperP).\n            // g. If upperExists is true, then\n            // g.i. Let upperValue be ? Get(O, upperP).\n            let upper_value = o.try_get(upper, context)?;\n            match (lower_value, upper_value) {\n                // h. If lowerExists is true and upperExists is true, then\n                (Some(lower_value), Some(upper_value)) => {\n                    // i. Perform ? Set(O, lowerP, upperValue, true).\n                    o.set(lower, upper_value, true, context)?;\n                    // ii. Perform ? Set(O, upperP, lowerValue, true).\n                    o.set(upper, lower_value, true, context)?;\n                }\n                // i. Else if lowerExists is false and upperExists is true, then\n                (None, Some(upper_value)) => {\n                    // i. Perform ? Set(O, lowerP, upperValue, true).\n                    o.set(lower, upper_value, true, context)?;\n                    // ii. Perform ? DeletePropertyOrThrow(O, upperP).\n                    o.delete_property_or_throw(upper, context)?;\n                }\n                // j. Else if lowerExists is true and upperExists is false, then\n                (Some(lower_value), None) => {\n                    // i. Perform ? DeletePropertyOrThrow(O, lowerP).\n                    o.delete_property_or_throw(lower, context)?;\n                    // ii. Perform ? Set(O, upperP, lowerValue, true).\n                    o.set(upper, lower_value, true, context)?;\n                }\n                // k. Else,\n                (None, None) => {\n                    // i. Assert: lowerExists and upperExists are both false.\n                    // ii. No action is required.\n                }\n            }\n\n            // l. Set lower to lower + 1.\n            lower += 1;\n        }\n        // 6. Return O.\n        Ok(o.into())\n    }\n\n    /// [`Array.prototype.toReversed()`][spec]\n    ///\n    /// Reverses this array, returning the result into a copy of the array.\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-array.prototype.toreversed\n    pub(crate) fn to_reversed(\n        this: &JsValue,\n        _: &[JsValue],\n        context: &mut Context,\n    ) -> JsResult<JsValue> {\n        // 1. Let O be ? ToObject(this value).\n        let o = this.to_object(context)?;\n\n        // 2. Let len be ? LengthOfArrayLike(O).\n        let len = o.length_of_array_like(context)?;\n\n        // 3. Let A be ? ArrayCreate(len).\n        let a = Array::array_create(len, None, context)?;\n\n        // 4. Let k be 0.\n        // 5. Repeat, while k < len,\n        for i in 0..len {\n            // a. Let from be ! ToString(𝔽(len - k - 1)).\n            let from = len - i - 1;\n\n            // b. Let Pk be ! ToString(𝔽(k)).\n            // c. Let fromValue be ? Get(O, from).\n            let from_value = o.get(from, context)?;\n\n            // d. Perform ! CreateDataPropertyOrThrow(A, Pk, fromValue).\n            a.create_data_property_or_throw(i, from_value, context)\n                .js_expect(\"cannot fail per the spec\")?;\n\n            // e. Set k to k + 1.\n        }\n\n        // 6. Return A.\n        Ok(a.into())\n    }\n\n    /// `Array.prototype.shift()`\n    ///\n    /// The first element of the array is removed from the array and returned.\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///  - [MDN documentation][mdn]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-array.prototype.shift\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/shift\n    pub(crate) fn shift(this: &JsValue, _: &[JsValue], context: &mut Context) -> JsResult<JsValue> {\n        // 1. Let O be ? ToObject(this value).\n        let o = this.to_object(context)?;\n        // 2. Let len be ? LengthOfArrayLike(O).\n        let len = o.length_of_array_like(context)?;\n        // 3. If len = 0, then\n        if len == 0 {\n            // a. Perform ? Set(O, \"length\", +0𝔽, true).\n            Self::set_length(&o, 0, context)?;\n\n            // b. Return undefined.\n            return Ok(JsValue::undefined());\n        }\n\n        // Small optimization for arrays using dense properties\n        // TODO: this optimization could be generalized to many other objects with\n        // slot-based dense property maps.\n        if o.is_array() {\n            let mut o_borrow = o.borrow_mut();\n            if let IndexedProperties::DenseI32(dense) =\n                &mut o_borrow.properties_mut().indexed_properties\n                && len <= dense.len() as u64\n            {\n                let v = dense.remove(0);\n                drop(o_borrow);\n                Self::set_length(&o, len - 1, context)?;\n                return Ok(v.into());\n            }\n            if let IndexedProperties::DenseF64(dense) =\n                &mut o_borrow.properties_mut().indexed_properties\n                && len <= dense.len() as u64\n            {\n                let v = dense.remove(0);\n                drop(o_borrow);\n                Self::set_length(&o, len - 1, context)?;\n                return Ok(v.into());\n            }\n            if let Some(dense) = o_borrow.properties_mut().dense_indexed_properties_mut()\n                && len <= dense.len() as u64\n            {\n                let v = dense.remove(0);\n                drop(o_borrow);\n                Self::set_length(&o, len - 1, context)?;\n                return Ok(v);\n            }\n        }\n\n        // 4. Let first be ? Get(O, \"0\").\n        let first = o.get(0, context)?;\n        // 5. Let k be 1.\n        // 6. Repeat, while k < len,\n        for k in 1..len {\n            // a. Let from be ! ToString(𝔽(k)).\n            let from = k;\n            // b. Let to be ! ToString(𝔽(k - 1)).\n            let to = k - 1;\n            // c. Let fromPresent be ? HasProperty(O, from).\n            // d. If fromPresent is true, then\n            // d.i. Let fromVal be ? Get(O, from).\n            if let Some(from_val) = o.try_get(from, context)? {\n                // ii. Perform ? Set(O, to, fromVal, true).\n                o.set(to, from_val, true, context)?;\n            // e. Else,\n            } else {\n                // i. Assert: fromPresent is false.\n                // ii. Perform ? DeletePropertyOrThrow(O, to).\n                o.delete_property_or_throw(to, context)?;\n            }\n            // f. Set k to k + 1.\n        }\n        // 7. Perform ? DeletePropertyOrThrow(O, ! ToString(𝔽(len - 1))).\n        o.delete_property_or_throw(len - 1, context)?;\n        // 8. Perform ? Set(O, \"length\", 𝔽(len - 1), true).\n        Self::set_length(&o, len - 1, context)?;\n        // 9. Return first.\n        Ok(first)\n    }\n\n    /// `Array.prototype.unshift( ...items )`\n    ///\n    /// The arguments are prepended to the start of the array, such that their order\n    /// within the array is the same as the order in which they appear in the\n    /// argument list.\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///  - [MDN documentation][mdn]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-array.prototype.unshift\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/unshift\n    pub(crate) fn unshift(\n        this: &JsValue,\n        args: &[JsValue],\n        context: &mut Context,\n    ) -> JsResult<JsValue> {\n        // 1. Let O be ? ToObject(this value).\n        let o = this.to_object(context)?;\n        // 2. Let len be ? LengthOfArrayLike(O).\n        let len = o.length_of_array_like(context)?;\n        // 3. Let argCount be the number of elements in items.\n        let arg_count = args.len() as u64;\n        // 4. If argCount > 0, then\n        if arg_count > 0 {\n            // a. If len + argCount > 2^53 - 1, throw a TypeError exception.\n            if len + arg_count > 2u64.pow(53) - 1 {\n                return Err(JsNativeError::typ()\n                    .with_message(\"length + number of arguments exceeds the max safe integer limit\")\n                    .into());\n            }\n            // b. Let k be len.\n            let mut k = len;\n            // c. Repeat, while k > 0,\n            while k > 0 {\n                // i. Let from be ! ToString(𝔽(k - 1)).\n                let from = k - 1;\n                // ii. Let to be ! ToString(𝔽(k + argCount - 1)).\n                let to = k + arg_count - 1;\n                // iii. Let fromPresent be ? HasProperty(O, from).\n                // iv. If fromPresent is true, then\n                // iv.1. Let fromValue be ? Get(O, from).\n                if let Some(from_value) = o.try_get(from, context)? {\n                    // 2. Perform ? Set(O, to, fromValue, true).\n                    o.set(to, from_value, true, context)?;\n                // v. Else,\n                } else {\n                    // 1. Assert: fromPresent is false.\n                    // 2. Perform ? DeletePropertyOrThrow(O, to).\n                    o.delete_property_or_throw(to, context)?;\n                }\n                // vi. Set k to k - 1.\n                k -= 1;\n            }\n            // d. Let j be +0𝔽.\n            // e. For each element E of items, do\n            for (j, e) in args.iter().enumerate() {\n                // i. Perform ? Set(O, ! ToString(j), E, true).\n                o.set(j, e.clone(), true, context)?;\n                // ii. Set j to j + 1𝔽.\n            }\n        }\n        // 5. Perform ? Set(O, \"length\", 𝔽(len + argCount), true).\n        Self::set_length(&o, len + arg_count, context)?;\n\n        // 6. Return 𝔽(len + argCount).\n        Ok((len + arg_count).into())\n    }\n\n    /// `Array.prototype.every( callback, [ thisArg ] )`\n    ///\n    /// The every method executes the provided callback function once for each\n    /// element present in the array until it finds the one where callback returns\n    /// a falsy value. It returns `false` if it finds such element, otherwise it\n    /// returns `true`.\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///  - [MDN documentation][mdn]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-array.prototype.every\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/every\n    pub(crate) fn every(\n        this: &JsValue,\n        args: &[JsValue],\n        context: &mut Context,\n    ) -> JsResult<JsValue> {\n        // 1. Let O be ? ToObject(this value).\n        let o = this.to_object(context)?;\n        // 2. Let len be ? LengthOfArrayLike(O).\n        let len = o.length_of_array_like(context)?;\n        // 3. If IsCallable(callbackfn) is false, throw a TypeError exception.\n        let callback = args.get_or_undefined(0).as_callable().ok_or_else(|| {\n            JsNativeError::typ().with_message(\"Array.prototype.every: callback is not callable\")\n        })?;\n\n        let this_arg = args.get_or_undefined(1);\n\n        // 4. Let k be 0.\n        // 5. Repeat, while k < len,\n        for k in 0..len {\n            // a. Let Pk be ! ToString(𝔽(k)).\n            // b. Let kPresent be ? HasProperty(O, Pk).\n            // c. If kPresent is true, then\n            // c.i. Let kValue be ? Get(O, Pk).\n            if let Some(k_value) = o.try_get(k, context)? {\n                // ii. Let testResult be ! ToBoolean(? Call(callbackfn, thisArg, « kValue, 𝔽(k), O »)).\n                let test_result = callback\n                    .call(this_arg, &[k_value, k.into(), o.clone().into()], context)?\n                    .to_boolean();\n                // iii. If testResult is false, return false.\n                if !test_result {\n                    return Ok(JsValue::new(false));\n                }\n            }\n            // d. Set k to k + 1.\n        }\n        // 6. Return true.\n        Ok(JsValue::new(true))\n    }\n\n    /// `Array.prototype.map( callback, [ thisArg ] )`\n    ///\n    /// For each element in the array the callback function is called, and a new\n    /// array is constructed from the return values of these calls.\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///  - [MDN documentation][mdn]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-array.prototype.map\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/map\n    pub(crate) fn map(\n        this: &JsValue,\n        args: &[JsValue],\n        context: &mut Context,\n    ) -> JsResult<JsValue> {\n        // 1. Let O be ? ToObject(this value).\n        let o = this.to_object(context)?;\n        // 2. Let len be ? LengthOfArrayLike(O).\n        let len = o.length_of_array_like(context)?;\n        // 3. If IsCallable(callbackfn) is false, throw a TypeError exception.\n        let callback = args.get_or_undefined(0).as_callable().ok_or_else(|| {\n            JsNativeError::typ().with_message(\"Array.prototype.map: Callbackfn is not callable\")\n        })?;\n\n        // 4. Let A be ? ArraySpeciesCreate(O, len).\n        let a = Self::array_species_create(&o, len, context)?;\n\n        let this_arg = args.get_or_undefined(1);\n\n        // 5. Let k be 0.\n        // 6. Repeat, while k < len,\n        for k in 0..len {\n            // a. Let Pk be ! ToString(𝔽(k)).\n            // b. Let k_present be ? HasProperty(O, Pk).\n            // c. If k_present is true, then\n            // c.i. Let kValue be ? Get(O, Pk).\n            if let Some(k_value) = o.try_get(k, context)? {\n                // ii. Let mappedValue be ? Call(callbackfn, thisArg, « kValue, 𝔽(k), O »).\n                let mapped_value =\n                    callback.call(this_arg, &[k_value, k.into(), o.clone().into()], context)?;\n                // iii. Perform ? CreateDataPropertyOrThrow(A, Pk, mappedValue).\n                a.create_data_property_or_throw(k, mapped_value, context)?;\n            }\n            // d. Set k to k + 1.\n        }\n        // 7. Return A.\n        Ok(a.into())\n    }\n\n    /// `Array.prototype.indexOf( searchElement[, fromIndex ] )`\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///  - [MDN documentation][mdn]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-array.prototype.indexof\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/indexOf\n    pub(crate) fn index_of(\n        this: &JsValue,\n        args: &[JsValue],\n        context: &mut Context,\n    ) -> JsResult<JsValue> {\n        // 1. Let O be ? ToObject(this value).\n        let o = this.to_object(context)?;\n\n        // 2. Let len be ? LengthOfArrayLike(O).\n        let len = o.length_of_array_like(context)? as i64;\n\n        // 3. If len is 0, return -1𝔽.\n        if len == 0 {\n            return Ok(JsValue::new(-1));\n        }\n\n        // 4. Let n be ? ToIntegerOrInfinity(fromIndex).\n        let n = args\n            .get(1)\n            .cloned()\n            .unwrap_or_default()\n            .to_integer_or_infinity(context)?;\n        // 5. Assert: If fromIndex is undefined, then n is 0.\n        let n = match n {\n            // 6. If n is +∞, return -1𝔽.\n            IntegerOrInfinity::PositiveInfinity => return Ok(JsValue::new(-1)),\n            // 7. Else if n is -∞, set n to 0.\n            IntegerOrInfinity::NegativeInfinity => 0,\n            IntegerOrInfinity::Integer(value) => value,\n        };\n\n        // 8. If n ≥ 0, then\n        let mut k;\n        if n >= 0 {\n            // a. Let k be n.\n            k = n;\n        // 9. Else,\n        } else {\n            // a. Let k be len + n.\n            k = len + n;\n            // b. If k < 0, set k to 0.\n            if k < 0 {\n                k = 0;\n            }\n        }\n\n        let search_element = args.get_or_undefined(0);\n\n        // 10. Repeat, while k < len,\n        while k < len {\n            // a. Let kPresent be ? HasProperty(O, ! ToString(𝔽(k))).\n            // b. If kPresent is true, then\n            // b.i. Let elementK be ? Get(O, ! ToString(𝔽(k))).\n            if let Some(element_k) = o.try_get(k, context)? {\n                // ii. Let same be IsStrictlyEqual(searchElement, elementK).\n                // iii. If same is true, return 𝔽(k).\n                if search_element.strict_equals(&element_k) {\n                    return Ok(JsValue::new(k));\n                }\n            }\n            // c. Set k to k + 1.\n            k += 1;\n        }\n        // 11. Return -1𝔽.\n        Ok(JsValue::new(-1))\n    }\n\n    /// `Array.prototype.lastIndexOf( searchElement[, fromIndex ] )`\n    ///\n    ///\n    /// `lastIndexOf` compares searchElement to the elements of the array in descending order\n    /// using the Strict Equality Comparison algorithm, and if found at one or more indices,\n    /// returns the largest such index; otherwise, -1 is returned.\n    ///\n    /// The optional second argument fromIndex defaults to the array's length minus one\n    /// (i.e. the whole array is searched). If it is greater than or equal to the length of the array,\n    /// the whole array will be searched. If it is negative, it is used as the offset from the end\n    /// of the array to compute fromIndex. If the computed index is less than 0, -1 is returned.\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///  - [MDN documentation][mdn]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-array.prototype.lastindexof\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/lastIndexOf\n    pub(crate) fn last_index_of(\n        this: &JsValue,\n        args: &[JsValue],\n        context: &mut Context,\n    ) -> JsResult<JsValue> {\n        // 1. Let O be ? ToObject(this value).\n        let o = this.to_object(context)?;\n\n        // 2. Let len be ? LengthOfArrayLike(O).\n        let len = o.length_of_array_like(context)? as i64;\n\n        // 3. If len is 0, return -1𝔽.\n        if len == 0 {\n            return Ok(JsValue::new(-1));\n        }\n\n        // 4. If fromIndex is present, let n be ? ToIntegerOrInfinity(fromIndex); else let n be len - 1.\n        let n = if let Some(from_index) = args.get(1) {\n            from_index.to_integer_or_infinity(context)?\n        } else {\n            IntegerOrInfinity::Integer(len - 1)\n        };\n\n        let mut k = match n {\n            // 5. If n is -∞, return -1𝔽.\n            IntegerOrInfinity::NegativeInfinity => return Ok(JsValue::new(-1)),\n            // 6. If n ≥ 0, then\n            //     a. Let k be min(n, len - 1).\n            IntegerOrInfinity::Integer(n) if n >= 0 => min(n, len - 1),\n            IntegerOrInfinity::PositiveInfinity => len - 1,\n            // 7. Else,\n            //     a. Let k be len + n.\n            IntegerOrInfinity::Integer(n) => len + n,\n        };\n\n        let search_element = args.get_or_undefined(0);\n\n        // 8. Repeat, while k ≥ 0,\n        while k >= 0 {\n            // a. Let kPresent be ? HasProperty(O, ! ToString(𝔽(k))).\n            // b. If kPresent is true, then\n            // b.i. Let elementK be ? Get(O, ! ToString(𝔽(k))).\n            if let Some(element_k) = o.try_get(k, context)? {\n                // ii. Let same be IsStrictlyEqual(searchElement, elementK).\n                // iii. If same is true, return 𝔽(k).\n                if JsValue::strict_equals(search_element, &element_k) {\n                    return Ok(JsValue::new(k));\n                }\n            }\n            // c. Set k to k - 1.\n            k -= 1;\n        }\n        // 9. Return -1𝔽.\n        Ok(JsValue::new(-1))\n    }\n\n    /// `Array.prototype.find( callback, [thisArg] )`\n    ///\n    /// The find method executes the callback function once for each index of the array\n    /// until the callback returns a truthy value. If so, find immediately returns the value\n    /// of that element. Otherwise, find returns undefined.\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///  - [MDN documentation][mdn]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-array.prototype.find\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/find\n    pub(crate) fn find(\n        this: &JsValue,\n        args: &[JsValue],\n        context: &mut Context,\n    ) -> JsResult<JsValue> {\n        // 1. Let O be ? ToObject(this value).\n        let o = this.to_object(context)?;\n\n        // 2. Let len be ? LengthOfArrayLike(O).\n        let len = o.length_of_array_like(context)?;\n\n        let predicate = args.get_or_undefined(0);\n        let this_arg = args.get_or_undefined(1);\n\n        // 3. Let findRec be ? FindViaPredicate(O, len, ascending, predicate, thisArg).\n        let (_, value) = find_via_predicate(\n            &o,\n            len,\n            Direction::Ascending,\n            predicate,\n            this_arg,\n            context,\n            \"Array.prototype.find\",\n        )?;\n\n        // 4. Return findRec.[[Value]].\n        Ok(value)\n    }\n\n    /// `Array.prototype.findIndex( predicate [ , thisArg ] )`\n    ///\n    /// This method executes the provided predicate function for each element of the array.\n    /// If the predicate function returns `true` for an element, this method returns the index of the element.\n    /// If all elements return `false`, the value `-1` is returned.\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///  - [MDN documentation][mdn]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-array.prototype.findindex\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/findIndex\n    pub(crate) fn find_index(\n        this: &JsValue,\n        args: &[JsValue],\n        context: &mut Context,\n    ) -> JsResult<JsValue> {\n        // 1. Let O be ? ToObject(this value).\n        let o = this.to_object(context)?;\n\n        // 2. Let len be ? LengthOfArrayLike(O).\n        let len = o.length_of_array_like(context)?;\n\n        let predicate = args.get_or_undefined(0);\n        let this_arg = args.get_or_undefined(1);\n\n        // 3. Let findRec be ? FindViaPredicate(O, len, ascending, predicate, thisArg).\n        let (index, _) = find_via_predicate(\n            &o,\n            len,\n            Direction::Ascending,\n            predicate,\n            this_arg,\n            context,\n            \"Array.prototype.findIndex\",\n        )?;\n\n        // 4. Return findRec.[[Index]].\n        Ok(index)\n    }\n\n    /// `Array.prototype.findLast( predicate, [thisArg] )`\n    ///\n    /// findLast calls predicate once for each element of the array, in descending order,\n    /// until it finds one where predicate returns true. If such an element is found, findLast\n    /// immediately returns that element value. Otherwise, findLast returns undefined.\n    ///\n    /// More information:\n    ///  - [ECMAScript proposal][spec]\n    ///\n    /// [spec]: https://tc39.es/proposal-array-find-from-last/#sec-array.prototype.findlast\n    pub(crate) fn find_last(\n        this: &JsValue,\n        args: &[JsValue],\n        context: &mut Context,\n    ) -> JsResult<JsValue> {\n        // 1. Let O be ? ToObject(this value).\n        let o = this.to_object(context)?;\n\n        // 2. Let len be ? LengthOfArrayLike(O).\n        let len = o.length_of_array_like(context)?;\n\n        let predicate = args.get_or_undefined(0);\n        let this_arg = args.get_or_undefined(1);\n\n        // 3. Let findRec be ? FindViaPredicate(O, len, descending, predicate, thisArg).\n        let (_, value) = find_via_predicate(\n            &o,\n            len,\n            Direction::Descending,\n            predicate,\n            this_arg,\n            context,\n            \"Array.prototype.findLast\",\n        )?;\n\n        // 4. Return findRec.[[Value]].\n        Ok(value)\n    }\n\n    /// `Array.prototype.findLastIndex( predicate [ , thisArg ] )`\n    ///\n    /// `findLastIndex` calls predicate once for each element of the array, in descending order,\n    /// until it finds one where predicate returns true. If such an element is found, `findLastIndex`\n    /// immediately returns the index of that element value. Otherwise, `findLastIndex` returns -1.\n    ///\n    /// More information:\n    ///  - [ECMAScript proposal][spec]\n    ///\n    /// [spec]: https://tc39.es/proposal-array-find-from-last/#sec-array.prototype.findlastindex\n    pub(crate) fn find_last_index(\n        this: &JsValue,\n        args: &[JsValue],\n        context: &mut Context,\n    ) -> JsResult<JsValue> {\n        // 1. Let O be ? ToObject(this value).\n        let o = this.to_object(context)?;\n\n        // 2. Let len be ? LengthOfArrayLike(O).\n        let len = o.length_of_array_like(context)?;\n\n        let predicate = args.get_or_undefined(0);\n        let this_arg = args.get_or_undefined(1);\n\n        // 3. Let findRec be ? FindViaPredicate(O, len, descending, predicate, thisArg).\n        let (index, _) = find_via_predicate(\n            &o,\n            len,\n            Direction::Descending,\n            predicate,\n            this_arg,\n            context,\n            \"Array.prototype.findLastIndex\",\n        )?;\n\n        // 4. Return findRec.[[Index]].\n        Ok(index)\n    }\n\n    /// `Array.prototype.flat( [depth] )`\n    ///\n    /// This method creates a new array with all sub-array elements concatenated into it\n    /// recursively up to the specified depth.\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///  - [MDN documentation][mdn]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-array.prototype.flat\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/flat\n    pub(crate) fn flat(\n        this: &JsValue,\n        args: &[JsValue],\n        context: &mut Context,\n    ) -> JsResult<JsValue> {\n        // 1. Let O be ToObject(this value)\n        let o = this.to_object(context)?;\n\n        // 2. Let sourceLen be LengthOfArrayLike(O)\n        let source_len = o.length_of_array_like(context)?;\n\n        // 3. Let depthNum be 1\n        // 4. If depth is not undefined, then set depthNum to IntegerOrInfinity(depth)\n        let depth = args.get_or_undefined(0);\n        let depth_num = if depth.is_undefined() {\n            1\n        } else {\n            // a. Set depthNum to ? ToIntegerOrInfinity(depth).\n            // b. If depthNum < 0, set depthNum to 0.\n            match depth.to_integer_or_infinity(context)? {\n                IntegerOrInfinity::Integer(value) if value >= 0 => value as u64,\n                IntegerOrInfinity::PositiveInfinity => u64::MAX,\n                _ => 0,\n            }\n        };\n\n        // 5. Let A be ArraySpeciesCreate(O, 0)\n        let a = Self::array_species_create(&o, 0, context)?;\n\n        // 6. Perform ? FlattenIntoArray(A, O, sourceLen, 0, depthNum)\n        Self::flatten_into_array(\n            &a,\n            &o,\n            source_len,\n            0,\n            depth_num,\n            None,\n            &JsValue::undefined(),\n            context,\n        )?;\n\n        Ok(a.into())\n    }\n\n    /// `Array.prototype.flatMap( callback, [ thisArg ] )`\n    ///\n    /// This method returns a new array formed by applying a given callback function to\n    /// each element of the array, and then flattening the result by one level. It is\n    /// identical to a `map()` followed by a `flat()` of depth 1, but slightly more\n    /// efficient than calling those two methods separately.\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///  - [MDN documentation][mdn]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-array.prototype.flatMap\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/flatMap\n    pub(crate) fn flat_map(\n        this: &JsValue,\n        args: &[JsValue],\n        context: &mut Context,\n    ) -> JsResult<JsValue> {\n        // 1. Let O be ToObject(this value)\n        let o = this.to_object(context)?;\n\n        // 2. Let sourceLen be LengthOfArrayLike(O)\n        let source_len = o.length_of_array_like(context)?;\n\n        // 3. If ! IsCallable(mapperFunction) is false, throw a TypeError exception.\n        let mapper_function = args.get_or_undefined(0).as_callable().ok_or_else(|| {\n            JsNativeError::typ().with_message(\"flatMap mapper function is not callable\")\n        })?;\n\n        // 4. Let A be ? ArraySpeciesCreate(O, 0).\n        let a = Self::array_species_create(&o, 0, context)?;\n\n        // 5. Perform ? FlattenIntoArray(A, O, sourceLen, 0, 1, mapperFunction, thisArg).\n        Self::flatten_into_array(\n            &a,\n            &o,\n            source_len,\n            0,\n            1,\n            Some(&mapper_function),\n            args.get_or_undefined(1),\n            context,\n        )?;\n\n        // 6. Return A\n        Ok(a.into())\n    }\n\n    /// Abstract method `FlattenIntoArray`.\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-flattenintoarray\n    #[allow(clippy::too_many_arguments)]\n    fn flatten_into_array(\n        target: &JsObject,\n        source: &JsObject,\n        source_len: u64,\n        start: u64,\n        depth: u64,\n        mapper_function: Option<&JsObject>,\n        this_arg: &JsValue,\n        context: &mut Context,\n    ) -> JsResult<u64> {\n        // 1. Assert target is Object\n        // 2. Assert source is Object\n\n        // 3. Assert if mapper_function is present, then:\n        // - IsCallable(mapper_function) is true\n        // - thisArg is present\n        // - depth is 1\n\n        // 4. Let targetIndex be start\n        let mut target_index = start;\n\n        // 5. Let sourceIndex be 0\n        let mut source_index = 0;\n\n        // 6. Repeat, while R(sourceIndex) < sourceLen\n        while source_index < source_len {\n            // a. Let P be ToString(sourceIndex)\n            let p = source_index;\n\n            // b. Let exists be ? HasProperty(source, P).\n            // c. If exists is true, then\n            // c.i. Let element be Get(source, P)\n            if let Some(mut element) = source.try_get(p, context)? {\n                // ii. If mapperFunction is present, then\n                if let Some(mapper_function) = mapper_function {\n                    // 1. Set element to ? Call(mapperFunction, thisArg, <<element, sourceIndex, source>>)\n                    element = mapper_function.call(\n                        this_arg,\n                        &[element, source_index.into(), source.clone().into()],\n                        context,\n                    )?;\n                }\n\n                // iii. Let shouldFlatten be false\n                // iv. If depth > 0, then\n                let should_flatten = if depth > 0 {\n                    // 1. Set shouldFlatten to ? IsArray(element).\n                    element.is_array()?\n                } else {\n                    false\n                };\n\n                // v. If shouldFlatten is true\n                if should_flatten {\n                    // For `should_flatten` to be true, element must be an object.\n                    let element = element.as_object().js_expect(\"must be an object\")?;\n\n                    // 1. If depth is +Infinity let newDepth be +Infinity\n                    let new_depth = if depth == u64::MAX {\n                        u64::MAX\n                    // 2. Else, let newDepth be depth - 1\n                    } else {\n                        depth - 1\n                    };\n\n                    // 3. Let elementLen be ? LengthOfArrayLike(element)\n                    let element_len = element.length_of_array_like(context)?;\n\n                    // 4. Set targetIndex to ? FlattenIntoArray(target, element, elementLen, targetIndex, newDepth)\n                    target_index = Self::flatten_into_array(\n                        target,\n                        &element,\n                        element_len,\n                        target_index,\n                        new_depth,\n                        None,\n                        &JsValue::undefined(),\n                        context,\n                    )?;\n\n                // vi. Else\n                } else {\n                    // 1. If targetIndex >= 2^53 - 1, throw a TypeError exception\n                    if target_index >= Number::MAX_SAFE_INTEGER as u64 {\n                        return Err(JsNativeError::typ()\n                            .with_message(\"Target index exceeded max safe integer value\")\n                            .into());\n                    }\n\n                    // 2. Perform ? CreateDataPropertyOrThrow(target, targetIndex, element)\n                    target.create_data_property_or_throw(target_index, element, context)?;\n\n                    // 3. Set targetIndex to targetIndex + 1\n                    target_index += 1;\n                }\n            }\n            // d. Set sourceIndex to sourceIndex + 1\n            source_index += 1;\n        }\n\n        // 7. Return targetIndex\n        Ok(target_index)\n    }\n\n    /// `Array.prototype.fill( value[, start[, end]] )`\n    ///\n    /// The method fills (modifies) all the elements of an array from start index (default 0)\n    /// to an end index (default array length) with a static value. It returns the modified array.\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///  - [MDN documentation][mdn]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-array.prototype.fill\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/fill\n    pub(crate) fn fill(\n        this: &JsValue,\n        args: &[JsValue],\n        context: &mut Context,\n    ) -> JsResult<JsValue> {\n        // 1. Let O be ? ToObject(this value).\n        let o = this.to_object(context)?;\n\n        // 2. Let len be ? LengthOfArrayLike(O).\n        let len = o.length_of_array_like(context)?;\n\n        // 3. Let relativeStart be ? ToIntegerOrInfinity(start).\n        // 4. If relativeStart is -∞, let k be 0.\n        // 5. Else if relativeStart < 0, let k be max(len + relativeStart, 0).\n        // 6. Else, let k be min(relativeStart, len).\n        let mut k = Self::get_relative_start(context, args.get_or_undefined(1), len)?;\n\n        // 7. If end is undefined, let relativeEnd be len; else let relativeEnd be ? ToIntegerOrInfinity(end).\n        // 8. If relativeEnd is -∞, let final be 0.\n        // 9. Else if relativeEnd < 0, let final be max(len + relativeEnd, 0).\n        // 10. Else, let final be min(relativeEnd, len).\n        let final_ = Self::get_relative_end(context, args.get_or_undefined(2), len)?;\n\n        let value = args.get_or_undefined(0);\n\n        // 11. Repeat, while k < final,\n        while k < final_ {\n            // a. Let Pk be ! ToString(𝔽(k)).\n            let pk = k;\n            // b. Perform ? Set(O, Pk, value, true).\n            o.set(pk, value.clone(), true, context)?;\n            // c. Set k to k + 1.\n            k += 1;\n        }\n        // 12. Return O.\n        Ok(o.into())\n    }\n\n    /// `Array.prototype.includes( valueToFind [, fromIndex] )`\n    ///\n    /// Determines whether an array includes a certain value among its entries, returning `true` or `false` as appropriate.\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///  - [MDN documentation][mdn]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-array.prototype.includes\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/includes\n    pub(crate) fn includes_value(\n        this: &JsValue,\n        args: &[JsValue],\n        context: &mut Context,\n    ) -> JsResult<JsValue> {\n        // 1. Let O be ? ToObject(this value).\n        let o = this.to_object(context)?;\n\n        // 2. Let len be ? LengthOfArrayLike(O).\n        let len = o.length_of_array_like(context)? as i64;\n\n        // 3. If len is 0, return false.\n        if len == 0 {\n            return Ok(JsValue::new(false));\n        }\n\n        // 4. Let n be ? ToIntegerOrInfinity(fromIndex).\n        let n = args\n            .get(1)\n            .cloned()\n            .unwrap_or_default()\n            .to_integer_or_infinity(context)?;\n        // 5. Assert: If fromIndex is undefined, then n is 0.\n        // 6. If n is +∞, return false.\n        // 7. Else if n is -∞, set n to 0.\n        let n = match n {\n            IntegerOrInfinity::PositiveInfinity => return Ok(JsValue::new(false)),\n            IntegerOrInfinity::NegativeInfinity => 0,\n            IntegerOrInfinity::Integer(value) => value,\n        };\n\n        // 8. If n ≥ 0, then\n        let mut k;\n        if n >= 0 {\n            // a. Let k be n.\n            k = n;\n        // 9. Else,\n        } else {\n            // a. Let k be len + n.\n            k = len + n;\n            // b. If k < 0, set k to 0.\n            if k < 0 {\n                k = 0;\n            }\n        }\n\n        let search_element = args.get_or_undefined(0);\n\n        // 10. Repeat, while k < len,\n        while k < len {\n            // a. Let elementK be ? Get(O, ! ToString(𝔽(k))).\n            let element_k = o.get(k, context)?;\n            // b. If SameValueZero(searchElement, elementK) is true, return true.\n            if JsValue::same_value_zero(search_element, &element_k) {\n                return Ok(JsValue::new(true));\n            }\n            // c. Set k to k + 1.\n            k += 1;\n        }\n        // 11. Return false.\n        Ok(JsValue::new(false))\n    }\n\n    /// `Array.prototype.slice( [begin[, end]] )`\n    ///\n    /// The slice method takes two arguments, start and end, and returns an array containing the\n    /// elements of the array from element start up to, but not including, element end (or through the\n    /// end of the array if end is undefined). If start is negative, it is treated as length + start\n    /// where length is the length of the array. If end is negative, it is treated as length + end where\n    /// length is the length of the array.\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///  - [MDN documentation][mdn]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-array.prototype.slice\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/slice\n    pub(crate) fn slice(\n        this: &JsValue,\n        args: &[JsValue],\n        context: &mut Context,\n    ) -> JsResult<JsValue> {\n        // 1. Let O be ? ToObject(this value).\n        let o = this.to_object(context)?;\n\n        // 2. Let len be ? LengthOfArrayLike(O).\n        let len = o.length_of_array_like(context)?;\n\n        // 3. Let relativeStart be ? ToIntegerOrInfinity(start).\n        // 4. If relativeStart is -∞, let k be 0.\n        // 5. Else if relativeStart < 0, let k be max(len + relativeStart, 0).\n        // 6. Else, let k be min(relativeStart, len).\n        let mut k = Self::get_relative_start(context, args.get_or_undefined(0), len)?;\n\n        // 7. If end is undefined, let relativeEnd be len; else let relativeEnd be ? ToIntegerOrInfinity(end).\n        // 8. If relativeEnd is -∞, let final be 0.\n        // 9. Else if relativeEnd < 0, let final be max(len + relativeEnd, 0).\n        // 10. Else, let final be min(relativeEnd, len).\n        let final_ = Self::get_relative_end(context, args.get_or_undefined(1), len)?;\n\n        // 11. Let count be max(final - k, 0).\n        let count = final_.saturating_sub(k);\n\n        // 12. Let A be ? ArraySpeciesCreate(O, count).\n        let a = Self::array_species_create(&o, count, context)?;\n\n        // 13. Let n be 0.\n        let mut n: u64 = 0;\n        // 14. Repeat, while k < final,\n        while k < final_ {\n            // a. Let Pk be ! ToString(𝔽(k)).\n            let pk = k;\n            // b. Let kPresent be ? HasProperty(O, Pk).\n            // c. If kPresent is true, then\n            // c.i. Let kValue be ? Get(O, Pk).\n            if let Some(k_value) = o.try_get(pk, context)? {\n                // ii. Perform ? CreateDataPropertyOrThrow(A, ! ToString(𝔽(n)), kValue).\n                a.create_data_property_or_throw(n, k_value, context)?;\n            }\n            // d. Set k to k + 1.\n            k += 1;\n            // e. Set n to n + 1.\n            n += 1;\n        }\n\n        // 15. Perform ? Set(A, \"length\", 𝔽(n), true).\n        Self::set_length(&a, n, context)?;\n\n        // 16. Return A.\n        Ok(a.into())\n    }\n\n    /// [`Array.prototype.toLocaleString ( [ locales [ , options ] ] )`][spec].\n    ///\n    /// Returns a string representing the elements of the array. The elements are converted to\n    /// strings using their `toLocaleString` methods and these strings are separated by a\n    /// locale-specific string (such as a comma \",\").\n    ///\n    /// More information:\n    ///  - [MDN documentation][mdn]\n    ///\n    /// [spec]: https://tc39.es/ecma402/#sup-array.prototype.tolocalestring\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/toLocaleString\n    pub(crate) fn to_locale_string(\n        this: &JsValue,\n        args: &[JsValue],\n        context: &mut Context,\n    ) -> JsResult<JsValue> {\n        // 1. Let array be ? ToObject(this value).\n        let array = this.to_object(context)?;\n        // 2. Let len be ? ToLength(? Get(array, \"length\")).\n        let len = array.length_of_array_like(context)?;\n\n        let locales = args.get_or_undefined(0);\n        let options = args.get_or_undefined(1);\n\n        // 3. Let separator be the implementation-defined list-separator String value appropriate for the host environment's current locale (such as \", \").\n        let separator = {\n            #[cfg(feature = \"intl\")]\n            {\n                use crate::builtins::intl::locale::default_locale;\n                use icu_list::{\n                    ListFormatter, ListFormatterPreferences, options::ListFormatterOptions,\n                };\n\n                let locale = default_locale(context.intl_provider().locale_canonicalizer()?);\n                let preferences = ListFormatterPreferences::from(&locale);\n                let formatter = ListFormatter::try_new_unit_with_buffer_provider(\n                    context.intl_provider().erased_provider(),\n                    preferences,\n                    ListFormatterOptions::default(),\n                )\n                .map_err(|e| JsNativeError::typ().with_message(e.to_string()))?;\n\n                // Ask ICU for the list pattern literal by formatting two empty elements.\n                // For many locales this yields \", \", but it may differ.\n                js_string!(\n                    formatter.format_to_string(std::iter::once(\"\").chain(std::iter::once(\"\")))\n                )\n            }\n\n            #[cfg(not(feature = \"intl\"))]\n            {\n                js_string!(\", \")\n            }\n        };\n\n        // 4. Let R be the empty String.\n        let mut r = Vec::with_capacity(len as usize + len.saturating_sub(1) as usize);\n\n        // 5. Let k be 0.\n        // 6. Repeat, while k < len,\n        for k in 0..len {\n            // a. If k > 0, then\n            if k > 0 {\n                // i. Set R to the string-concatenation of R and separator.\n                r.extend(separator.iter());\n            }\n\n            // b. Let nextElement be ? Get(array, ! ToString(k)).\n            let next = array.get(k, context)?;\n\n            // c. If nextElement is not undefined or null, then\n            if !next.is_null_or_undefined() {\n                // i. Let S be ? ToString(? Invoke(nextElement, \"toLocaleString\", « locales, options »)).\n                let s = next\n                    .invoke(\n                        js_string!(\"toLocaleString\"),\n                        &[locales.clone(), options.clone()],\n                        context,\n                    )?\n                    .to_string(context)?;\n\n                // ii. Set R to the string-concatenation of R and S.\n                r.extend(s.iter());\n            }\n            //     d. Increase k by 1.\n        }\n        // 7. Return R.\n        Ok(js_string!(&r[..]).into())\n    }\n\n    /// Gets the delete count of a splice operation.\n    fn get_delete_count(\n        len: u64,\n        actual_start: u64,\n        start: Option<&JsValue>,\n        delete_count: Option<&JsValue>,\n        context: &mut Context,\n    ) -> JsResult<u64> {\n        // 8. If start is not present, then\n        let actual_delete_count = if start.is_none() {\n            // a. Let actualDeleteCount be 0.\n            0\n        }\n        // 10. Else,\n        else if let Some(delete_count) = delete_count {\n            // a. Let dc be ? ToIntegerOrInfinity(deleteCount).\n            let dc = delete_count.to_integer_or_infinity(context)?;\n\n            // b. Let actualDeleteCount be the result of clamping dc between 0 and len - actualStart.\n            let max = len - actual_start;\n            match dc {\n                IntegerOrInfinity::Integer(i) => u64::try_from(i)\n                    .unwrap_or_default()\n                    .clamp(0, len - actual_start),\n                IntegerOrInfinity::PositiveInfinity => max,\n                IntegerOrInfinity::NegativeInfinity => 0,\n            }\n        }\n        // 9. Else if deleteCount is not present, then\n        else {\n            // a. Let actualDeleteCount be len - actualStart.\n            len - actual_start\n        };\n\n        Ok(actual_delete_count)\n    }\n\n    /// `Array.prototype.splice ( start, [deleteCount[, ...items]] )`\n    ///\n    /// Splices an array by following\n    /// The deleteCount elements of the array starting at integer index start are replaced by the elements of items.\n    /// An Array object containing the deleted elements (if any) is returned.\n    pub(crate) fn splice(\n        this: &JsValue,\n        args: &[JsValue],\n        context: &mut Context,\n    ) -> JsResult<JsValue> {\n        let start = args.first();\n        let delete_count = args.get(1);\n        let items = args.get(2..).unwrap_or_default();\n\n        Self::splice_internal(this, start, delete_count, items, context)\n    }\n\n    pub(crate) fn splice_internal(\n        this: &JsValue,\n        start: Option<&JsValue>,\n        delete_count: Option<&JsValue>,\n        items: &[JsValue],\n        context: &mut Context,\n    ) -> JsResult<JsValue> {\n        // 1. Let O be ? ToObject(this value).\n        let o = this.to_object(context)?;\n        // 2. Let len be ? LengthOfArrayLike(O).\n        let len = o.length_of_array_like(context)?;\n\n        // 3. Let relativeStart be ? ToIntegerOrInfinity(start).\n        // 4. If relativeStart = -∞, let actualStart be 0.\n        // 5. Else if relativeStart < 0, let actualStart be max(len + relativeStart, 0).\n        // 6. Else, let actualStart be min(relativeStart, len).\n        let actual_start =\n            Self::get_relative_start(context, start.unwrap_or(&JsValue::undefined()), len)?;\n\n        // 7. Let itemCount be the number of elements in items.\n        let item_count = items.len() as u64;\n\n        let actual_delete_count =\n            Self::get_delete_count(len, actual_start, start, delete_count, context)?;\n\n        // If len + itemCount - actualDeleteCount > 2**53 - 1, throw a TypeError exception.\n        if len + item_count - actual_delete_count > Number::MAX_SAFE_INTEGER as u64 {\n            return Err(JsNativeError::typ()\n                .with_message(\"Target splice exceeded max safe integer value\")\n                .into());\n        }\n\n        // 12. Let A be ? ArraySpeciesCreate(O, actualDeleteCount).\n        let arr = Self::array_species_create(&o, actual_delete_count, context)?;\n\n        // 13. Let k be 0.\n        // 14. Repeat, while k < actualDeleteCount,\n        for k in 0..actual_delete_count {\n            // a. Let from be ! ToString(𝔽(actualStart + k)).\n            // b. If ? HasProperty(O, from) is true, then\n            // b.i. Let fromValue be ? Get(O, from).\n            if let Some(from_value) = o.try_get(actual_start + k, context)? {\n                // ii. Perform ? CreateDataPropertyOrThrow(A, ! ToString(𝔽(k)), fromValue).\n                arr.create_data_property_or_throw(k, from_value, context)?;\n            }\n            // c. Set k to k + 1.\n        }\n\n        // 15. Perform ? Set(A, \"length\", 𝔽(actualDeleteCount), true).\n        Self::set_length(&arr, actual_delete_count, context)?;\n\n        let item_count = items.len() as u64;\n\n        match item_count.cmp(&actual_delete_count) {\n            Ordering::Equal => {}\n            // 16. If itemCount < actualDeleteCount, then\n            Ordering::Less => {\n                // a. Set k to actualStart.\n                // b. Repeat, while k < (len - actualDeleteCount),\n                for k in actual_start..(len - actual_delete_count) {\n                    // i. Let from be ! ToString(𝔽(k + actualDeleteCount)).\n                    let from = k + actual_delete_count;\n\n                    // ii. Let to be ! ToString(𝔽(k + itemCount)).\n                    let to = k + item_count;\n\n                    // iii. If ? HasProperty(O, from) is true, then\n                    // iii.1. Let fromValue be ? Get(O, from).\n                    if let Some(from_value) = o.try_get(from, context)? {\n                        // 2. Perform ? Set(O, to, fromValue, true).\n                        o.set(to, from_value, true, context)?;\n                    } else {\n                        // iv. Else,\n                        //     1. Perform ? DeletePropertyOrThrow(O, to).\n                        o.delete_property_or_throw(to, context)?;\n                    }\n                    // v. Set k to k + 1.\n                }\n\n                // c. Set k to len.\n                // d. Repeat, while k > (len - actualDeleteCount + itemCount),\n                for k in ((len - actual_delete_count + item_count)..len).rev() {\n                    // i. Perform ? DeletePropertyOrThrow(O, ! ToString(𝔽(k - 1))).\n                    o.delete_property_or_throw(k, context)?;\n\n                    // ii. Set k to k - 1.\n                }\n            }\n            // 17. Else if itemCount > actualDeleteCount, then\n            Ordering::Greater => {\n                // a. Set k to (len - actualDeleteCount).\n                // b. Repeat, while k > actualStart,\n                for k in (actual_start..len - actual_delete_count).rev() {\n                    // i. Let from be ! ToString(𝔽(k + actualDeleteCount - 1)).\n                    let from = k + actual_delete_count;\n\n                    // ii. Let to be ! ToString(𝔽(k + itemCount - 1)).\n                    let to = k + item_count;\n\n                    // iii. If ? HasProperty(O, from) is true, then\n                    // iii.1. Let fromValue be ? Get(O, from).\n                    if let Some(from_value) = o.try_get(from, context)? {\n                        // 2. Perform ? Set(O, to, fromValue, true).\n                        o.set(to, from_value, true, context)?;\n                    }\n                    // iv. Else,\n                    else {\n                        // 1. Perform ? DeletePropertyOrThrow(O, to).\n                        o.delete_property_or_throw(to, context)?;\n                    }\n                    // v. Set k to k - 1.\n                }\n            }\n        }\n\n        // 18. Set k to actualStart.\n        // 19. For each element E of items, do\n        for (i, item) in items.iter().enumerate() {\n            //     a. Perform ? Set(O, ! ToString(𝔽(k)), E, true).\n            //     b. Set k to k + 1.\n            o.set(actual_start + i as u64, item.clone(), true, context)?;\n        }\n\n        // 20. Perform ? Set(O, \"length\", 𝔽(len - actualDeleteCount + itemCount), true).\n        Self::set_length(&o, len - actual_delete_count + item_count, context)?;\n\n        // 21. Return A.\n        Ok(JsValue::from(arr))\n    }\n\n    /// [`Array.prototype.toSpliced ( start, skipCount, ...items )`][spec]\n    ///\n    /// Splices the target array, returning the result as a new array.\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-array.prototype.tospliced\n    fn to_spliced(this: &JsValue, args: &[JsValue], context: &mut Context) -> JsResult<JsValue> {\n        // 1. Let O be ? ToObject(this value).\n        let o = this.to_object(context)?;\n\n        // 2. Let len be ? LengthOfArrayLike(O).\n        let len = o.length_of_array_like(context)?;\n\n        let start = args.first();\n        let skip_count = args.get(1);\n        let items = args.get(2..).unwrap_or_default();\n\n        // 3. Let relativeStart be ? ToIntegerOrInfinity(start).\n        // 4. If relativeStart is -∞, let actualStart be 0.\n        // 5. Else if relativeStart < 0, let actualStart be max(len + relativeStart, 0).\n        // 6. Else, let actualStart be min(relativeStart, len).\n        let actual_start =\n            Self::get_relative_start(context, start.unwrap_or(&JsValue::undefined()), len)?;\n\n        // 7. Let insertCount be the number of elements in items.\n        let insert_count = items.len() as u64;\n\n        let actual_skip_count =\n            Self::get_delete_count(len, actual_start, start, skip_count, context)?;\n\n        // 11. Let newLen be len + insertCount - actualSkipCount.\n        let new_len = len + insert_count - actual_skip_count;\n\n        // 12. If newLen > 2**53 - 1, throw a TypeError exception.\n        if new_len > Number::MAX_SAFE_INTEGER as u64 {\n            return Err(JsNativeError::typ()\n                .with_message(\"Target splice exceeded max safe integer value\")\n                .into());\n        }\n\n        // 13. Let A be ? ArrayCreate(newLen).\n        let arr = Array::array_create(new_len, None, context)?;\n\n        // 14. Let i be 0.\n        let mut i = 0;\n        // 16. Repeat, while i < actualStart,\n        while i < actual_start {\n            //     a. Let Pi be ! ToString(𝔽(i)).\n            //     b. Let iValue be ? Get(O, Pi).\n            let value = o.get(i, context)?;\n\n            //     c. Perform ! CreateDataPropertyOrThrow(A, Pi, iValue).\n            arr.create_data_property_or_throw(i, value, context)\n                .js_expect(\"cannot fail for a newly created array\")?;\n\n            //     d. Set i to i + 1.\n            i += 1;\n        }\n\n        // 17. For each element E of items, do\n        for item in items.iter().cloned() {\n            //     a. Let Pi be ! ToString(𝔽(i)).\n            //     b. Perform ! CreateDataPropertyOrThrow(A, Pi, E).\n            arr.create_data_property_or_throw(i, item, context)\n                .js_expect(\"cannot fail for a newly created array\")?;\n\n            //     c. Set i to i + 1.\n            i += 1;\n        }\n\n        // 15. Let r be actualStart + actualSkipCount.\n        let mut r = actual_start + actual_skip_count;\n\n        // 18. Repeat, while i < newLen,\n        while i < new_len {\n            //     a. Let Pi be ! ToString(𝔽(i)).\n            //     b. Let from be ! ToString(𝔽(r)).\n            //     c. Let fromValue be ? Get(O, from).\n            let from_value = o.get(r, context)?;\n\n            //     d. Perform ! CreateDataPropertyOrThrow(A, Pi, fromValue).\n            arr.create_data_property_or_throw(i, from_value, context)\n                .js_expect(\"cannot fail for a newly created array\")?;\n\n            //     e. Set i to i + 1.\n            i += 1;\n            //     f. Set r to r + 1.\n            r += 1;\n        }\n\n        // 19. Return A.\n        Ok(arr.into())\n    }\n\n    /// `Array.prototype.filter( callback, [ thisArg ] )`\n    ///\n    /// For each element in the array the callback function is called, and a new\n    /// array is constructed for every value whose callback returned a truthy value.\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///  - [MDN documentation][mdn]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-array.prototype.filter\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/filter\n    pub(crate) fn filter(\n        this: &JsValue,\n        args: &[JsValue],\n        context: &mut Context,\n    ) -> JsResult<JsValue> {\n        // 1. Let O be ? ToObject(this value).\n        let o = this.to_object(context)?;\n\n        // 2. Let len be ? LengthOfArrayLike(O).\n        let length = o.length_of_array_like(context)?;\n\n        // 3. If IsCallable(callbackfn) is false, throw a TypeError exception.\n        let callback = args.get_or_undefined(0).as_callable().ok_or_else(|| {\n            JsNativeError::typ().with_message(\"Array.prototype.filter: `callback` must be callable\")\n        })?;\n        let this_arg = args.get_or_undefined(1);\n\n        // 4. Let A be ? ArraySpeciesCreate(O, 0).\n        let a = Self::array_species_create(&o, 0, context)?;\n\n        // 5. Let k be 0.\n        // 6. Let to be 0.\n        let mut to = 0u32;\n        // 7. Repeat, while k < len,\n        for idx in 0..length {\n            // a. Let Pk be ! ToString(𝔽(k)).\n            // b. Let kPresent be ? HasProperty(O, Pk).\n            // c. If kPresent is true, then\n            // c.i. Let kValue be ? Get(O, Pk).\n            if let Some(element) = o.try_get(idx, context)? {\n                let args = [element.clone(), JsValue::new(idx), JsValue::new(o.clone())];\n\n                // ii. Let selected be ! ToBoolean(? Call(callbackfn, thisArg, « kValue, 𝔽(k), O »)).\n                let selected = callback.call(this_arg, &args, context)?.to_boolean();\n\n                // iii. If selected is true, then\n                if selected {\n                    // 1. Perform ? CreateDataPropertyOrThrow(A, ! ToString(𝔽(to)), kValue).\n                    a.create_data_property_or_throw(to, element, context)?;\n                    // 2. Set to to to + 1.\n                    to += 1;\n                }\n            }\n        }\n\n        // 8. Return A.\n        Ok(a.into())\n    }\n\n    /// Array.prototype.some ( callbackfn [ , thisArg ] )\n    ///\n    /// The some method tests whether at least one element in the array passes\n    /// the test implemented by the provided callback function. It returns a Boolean value,\n    /// true if the callback function returns a truthy value for at least one element\n    /// in the array. Otherwise, false.\n    ///\n    /// Caution: Calling this method on an empty array returns false for any condition!\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///  - [MDN documentation][mdn]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-array.prototype.some\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/some\n    pub(crate) fn some(\n        this: &JsValue,\n        args: &[JsValue],\n        context: &mut Context,\n    ) -> JsResult<JsValue> {\n        // 1. Let O be ? ToObject(this value).\n        let o = this.to_object(context)?;\n        // 2. Let len be ? LengthOfArrayLike(O).\n        let len = o.length_of_array_like(context)?;\n        // 3. If IsCallable(callbackfn) is false, throw a TypeError exception.\n        let callback = args.get_or_undefined(0).as_callable().ok_or_else(|| {\n            JsNativeError::typ().with_message(\"Array.prototype.some: callback is not callable\")\n        })?;\n\n        // 4. Let k be 0.\n        // 5. Repeat, while k < len,\n        for k in 0..len {\n            // a. Let Pk be ! ToString(𝔽(k)).\n            // b. Let kPresent be ? HasProperty(O, Pk).\n            // c. If kPresent is true, then\n            // c.i. Let kValue be ? Get(O, Pk).\n            if let Some(k_value) = o.try_get(k, context)? {\n                // ii. Let testResult be ! ToBoolean(? Call(callbackfn, thisArg, « kValue, 𝔽(k), O »)).\n                let this_arg = args.get_or_undefined(1);\n                let test_result = callback\n                    .call(this_arg, &[k_value, k.into(), o.clone().into()], context)?\n                    .to_boolean();\n                // iii. If testResult is true, return true.\n                if test_result {\n                    return Ok(JsValue::new(true));\n                }\n            }\n            // d. Set k to k + 1.\n        }\n        // 6. Return false.\n        Ok(JsValue::new(false))\n    }\n\n    /// [`SortIndexedProperties ( obj, len, SortCompare, holes )`][spec]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-sortindexedproperties\n    pub(crate) fn sort_indexed_properties<F>(\n        obj: &JsObject,\n        len: u64,\n        sort_compare: F,\n        skip_holes: bool,\n        context: &mut Context,\n    ) -> JsResult<Vec<JsValue>>\n    where\n        F: Fn(&JsValue, &JsValue, &mut Context) -> JsResult<Ordering>,\n    {\n        // 1. Let items be a new empty List.\n        // doesn't matter if it clamps since it's just a best-effort optimization\n        let mut items = Vec::with_capacity(len as usize);\n\n        // 2. Let k be 0.\n        // 3. Repeat, while k < len,\n        for i in 0..len {\n            // a. Let Pk be ! ToString(𝔽(k)).\n            // b. If holes is skip-holes, then\n            let read = if skip_holes {\n                // i. Let kRead be ? HasProperty(obj, Pk).\n                obj.has_property(i, context)?\n            }\n            // c. Else,\n            else {\n                // i. Assert: holes is read-through-holes.\n                // ii. Let kRead be true.\n                true\n            };\n\n            // d. If kRead is true, then\n            if read {\n                // i. Let kValue be ? Get(obj, Pk).\n                // ii. Append kValue to items.\n                items.push(obj.get(i, context)?);\n            }\n            // e. Set k to k + 1.\n        }\n        // 4. Sort items using an implementation-defined sequence of calls to SortCompare. If any such call returns an abrupt completion, stop before performing any further calls to SortCompare and return that Completion Record.\n        let mut sort_err = Ok(());\n        items.sort_by(|x, y| {\n            if sort_err.is_ok() {\n                sort_compare(x, y, context).unwrap_or_else(|err| {\n                    sort_err = Err(err);\n                    Ordering::Equal\n                })\n            } else {\n                Ordering::Equal\n            }\n        });\n        sort_err?;\n\n        // 5. Return items.\n        Ok(items)\n    }\n\n    /// Array.prototype.sort ( comparefn )\n    ///\n    /// The sort method sorts the elements of an array in place and returns the sorted array.\n    /// The default sort order is ascending, built upon converting the elements into strings,\n    /// then comparing their sequences of UTF-16 code units values.\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///  - [MDN documentation][mdn]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-array.prototype.sort\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/sort\n    pub(crate) fn sort(\n        this: &JsValue,\n        args: &[JsValue],\n        context: &mut Context,\n    ) -> JsResult<JsValue> {\n        // 1. If comparefn is not undefined and IsCallable(comparefn) is false, throw a TypeError exception.\n        let comparefn = match args.get_or_undefined(0).variant() {\n            JsVariant::Object(obj) if obj.is_callable() => Some(obj),\n            JsVariant::Undefined => None,\n            _ => {\n                return Err(JsNativeError::typ()\n                    .with_message(\"The comparison function must be either a function or undefined\")\n                    .into());\n            }\n        };\n\n        // 2. Let obj be ? ToObject(this value).\n        let obj = this.to_object(context)?;\n\n        // 3. Let len be ? LengthOfArrayLike(obj).\n        let len = obj.length_of_array_like(context)?;\n\n        // 4. Let SortCompare be a new Abstract Closure with parameters (x, y) that captures comparefn and performs the following steps when called:\n        let sort_compare =\n            |x: &JsValue, y: &JsValue, context: &mut Context| -> JsResult<Ordering> {\n                // a. Return ? CompareArrayElements(x, y, comparefn).\n                compare_array_elements(x, y, comparefn.as_ref(), context)\n            };\n\n        // 5. Let sortedList be ? SortIndexedProperties(obj, len, SortCompare, skip-holes).\n        let sorted = Self::sort_indexed_properties(&obj, len, sort_compare, true, context)?;\n\n        let sorted_len = sorted.len() as u64;\n\n        // 6. Let itemCount be the number of elements in sortedList.\n        // 7. Let j be 0.\n        // 8. Repeat, while j < itemCount,\n        for (j, item) in sorted.into_iter().enumerate() {\n            // a. Perform ? Set(obj, ! ToString(𝔽(j)), sortedList[j], true).\n            obj.set(j, item, true, context)?;\n\n            // b. Set j to j + 1.\n        }\n\n        // 9. NOTE: The call to SortIndexedProperties in step 5 uses skip-holes. The remaining indices\n        //    are deleted to preserve the number of holes that were detected and excluded from the sort.\n        // 10. Repeat, while j < len,\n        for j in sorted_len..len {\n            // a. Perform ? DeletePropertyOrThrow(obj, ! ToString(𝔽(j))).\n            obj.delete_property_or_throw(j, context)?;\n\n            // b. Set j to j + 1.\n        }\n\n        // 11. Return obj.\n        Ok(obj.into())\n    }\n\n    /// [`Array.prototype.toSorted ( comparefn )`][spec]\n    ///\n    /// Orders the target array, returning the result in a new array.\n    ///\n    /// [spec]: https://tc39.es/ecma262/multipage/indexed-collections.html#sec-array.prototype.tosorted\n    pub(crate) fn to_sorted(\n        this: &JsValue,\n        args: &[JsValue],\n        context: &mut Context,\n    ) -> JsResult<JsValue> {\n        // 1. If comparefn is not undefined and IsCallable(comparefn) is false, throw a TypeError exception.\n        let comparefn = match args.get_or_undefined(0).variant() {\n            JsVariant::Object(obj) if obj.is_callable() => Some(obj),\n            JsVariant::Undefined => None,\n            _ => {\n                return Err(JsNativeError::typ()\n                    .with_message(\"The comparison function must be either a function or undefined\")\n                    .into());\n            }\n        };\n\n        // 2. Let O be ? ToObject(this value).\n        let o = this.to_object(context)?;\n\n        // 3. Let len be ? LengthOfArrayLike(O).\n        let len = o.length_of_array_like(context)?;\n\n        // 4. Let A be ? ArrayCreate(len).\n        let arr = Array::array_create(len, None, context)?;\n\n        // 5. Let SortCompare be a new Abstract Closure with parameters (x, y) that captures comparefn and performs the following steps when called:\n        let sort_compare =\n            |x: &JsValue, y: &JsValue, context: &mut Context| -> JsResult<Ordering> {\n                // a. Return ? CompareArrayElements(x, y, comparefn).\n                compare_array_elements(x, y, comparefn.as_ref(), context)\n            };\n\n        // 6. Let sortedList be ? SortIndexedProperties(O, len, SortCompare, read-through-holes).\n        let sorted = Self::sort_indexed_properties(&o, len, sort_compare, false, context)?;\n\n        // 7. Let j be 0.\n        // 8. Repeat, while j < len,\n        for (i, item) in sorted.into_iter().enumerate() {\n            //     a. Perform ! CreateDataPropertyOrThrow(A, ! ToString(𝔽(j)), sortedList[j]).\n            arr.create_data_property_or_throw(i, item, context)\n                .js_expect(\"cannot fail for a newly created array\")?;\n\n            //     b. Set j to j + 1.\n        }\n\n        // 9. Return A.\n        Ok(arr.into())\n    }\n\n    /// `Array.prototype.reduce( callbackFn [ , initialValue ] )`\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///  - [MDN documentation][mdn]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-array.prototype.reduce\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/reduce\n    pub(crate) fn reduce(\n        this: &JsValue,\n        args: &[JsValue],\n        context: &mut Context,\n    ) -> JsResult<JsValue> {\n        // 1. Let O be ? ToObject(this value).\n        let o = this.to_object(context)?;\n\n        // 2. Let len be ? LengthOfArrayLike(O).\n        let len = o.length_of_array_like(context)?;\n\n        // 3. If IsCallable(callbackfn) is false, throw a TypeError exception.\n        let callback = args.get_or_undefined(0).as_callable().ok_or_else(|| {\n            JsNativeError::typ()\n                .with_message(\"Array.prototype.reduce: callback function is not callable\")\n        })?;\n\n        // 4. If len = 0 and initialValue is not present, throw a TypeError exception.\n        if len == 0 && args.get(1).is_none() {\n            return Err(JsNativeError::typ()\n                .with_message(\n                    \"Array.prototype.reduce: called on an empty array and with no initial value\",\n                )\n                .into());\n        }\n\n        // 5. Let k be 0.\n        let mut k = 0;\n        // 6. Let accumulator be undefined.\n        let mut accumulator = JsValue::undefined();\n\n        // 7. If initialValue is present, then\n        if let Some(initial_value) = args.get(1) {\n            // a. Set accumulator to initialValue.\n            accumulator = initial_value.clone();\n        // 8. Else,\n        } else {\n            // a. Let kPresent be false.\n            let mut k_present = false;\n            // b. Repeat, while kPresent is false and k < len,\n            while !k_present && k < len {\n                // i. Let Pk be ! ToString(𝔽(k)).\n                let pk = k;\n                // ii. Set kPresent to ? HasProperty(O, Pk).\n                // iii. If kPresent is true, then\n                // iii.1. Set accumulator to ? Get(O, Pk).\n                if let Some(v) = o.try_get(pk, context)? {\n                    accumulator = v;\n                    k_present = true;\n                } else {\n                    k_present = false;\n                }\n                // iv. Set k to k + 1.\n                k += 1;\n            }\n            // c. If kPresent is false, throw a TypeError exception.\n            if !k_present {\n                return Err(JsNativeError::typ().with_message(\n                    \"Array.prototype.reduce: called on an empty array and with no initial value\",\n                ).into());\n            }\n        }\n\n        // 9. Repeat, while k < len,\n        while k < len {\n            // a. Let Pk be ! ToString(𝔽(k)).\n            let pk = k;\n            // b. Let kPresent be ? HasProperty(O, Pk).\n            // c. If kPresent is true, then\n            // c.i. Let kValue be ? Get(O, Pk).\n            if let Some(k_value) = o.try_get(pk, context)? {\n                // ii. Set accumulator to ? Call(callbackfn, undefined, « accumulator, kValue, 𝔽(k), O »).\n                accumulator = callback.call(\n                    &JsValue::undefined(),\n                    &[accumulator, k_value, k.into(), o.clone().into()],\n                    context,\n                )?;\n            }\n            // d. Set k to k + 1.\n            k += 1;\n        }\n\n        // 10. Return accumulator.\n        Ok(accumulator)\n    }\n\n    /// `Array.prototype.reduceRight( callbackFn [ , initialValue ] )`\n    ///\n    /// The reduceRight method traverses right to left starting from the last defined value in the array,\n    /// accumulating a value using a given callback function. It returns the accumulated value.\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///  - [MDN documentation][mdn]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-array.prototype.reduceright\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/reduceRight\n    pub(crate) fn reduce_right(\n        this: &JsValue,\n        args: &[JsValue],\n        context: &mut Context,\n    ) -> JsResult<JsValue> {\n        // 1. Let O be ? ToObject(this value).\n        let o = this.to_object(context)?;\n\n        // 2. Let len be ? LengthOfArrayLike(O).\n        let len = o.length_of_array_like(context)?;\n\n        // 3. If IsCallable(callbackfn) is false, throw a TypeError exception.\n        let callback = args.get_or_undefined(0).as_callable().ok_or_else(|| {\n            JsNativeError::typ()\n                .with_message(\"Array.prototype.reduceRight: callback function is not callable\")\n        })?;\n\n        // 4. If len is 0 and initialValue is not present, throw a TypeError exception.\n        if len == 0 && args.get(1).is_none() {\n            return Err(JsNativeError::typ().with_message(\n                \"Array.prototype.reduceRight: called on an empty array and with no initial value\",\n            ).into());\n        }\n\n        // 5. Let k be len - 1.\n        let mut k = len as i64 - 1;\n        // 6. Let accumulator be undefined.\n        let mut accumulator = JsValue::undefined();\n        // 7. If initialValue is present, then\n        if let Some(initial_value) = args.get(1) {\n            // a. Set accumulator to initialValue.\n            accumulator = initial_value.clone();\n        // 8. Else,\n        } else {\n            // a. Let kPresent be false.\n            let mut k_present = false;\n            // b. Repeat, while kPresent is false and k ≥ 0,\n            while !k_present && k >= 0 {\n                // i. Let Pk be ! ToString(𝔽(k)).\n                let pk = k;\n                // ii. Set kPresent to ? HasProperty(O, Pk).\n                // iii. If kPresent is true, then\n                // iii.1. Set accumulator to ? Get(O, Pk).\n                if let Some(v) = o.try_get(pk, context)? {\n                    k_present = true;\n                    accumulator = v;\n                } else {\n                    k_present = false;\n                }\n                // iv. Set k to k - 1.\n                k -= 1;\n            }\n            // c. If kPresent is false, throw a TypeError exception.\n            if !k_present {\n                return Err(JsNativeError::typ().with_message(\n                    \"Array.prototype.reduceRight: called on an empty array and with no initial value\",\n                ).into());\n            }\n        }\n\n        // 9. Repeat, while k ≥ 0,\n        while k >= 0 {\n            // a. Let Pk be ! ToString(𝔽(k)).\n            let pk = k;\n            // b. Let kPresent be ? HasProperty(O, Pk).\n            // c. If kPresent is true, then\n            // c.i. Let kValue be ? Get(O, Pk).\n            if let Some(k_value) = o.try_get(pk, context)? {\n                // ii. Set accumulator to ? Call(callbackfn, undefined, « accumulator, kValue, 𝔽(k), O »).\n                accumulator = callback.call(\n                    &JsValue::undefined(),\n                    &[accumulator.clone(), k_value, k.into(), o.clone().into()],\n                    context,\n                )?;\n            }\n            // d. Set k to k - 1.\n            k -= 1;\n        }\n\n        // 10. Return accumulator.\n        Ok(accumulator)\n    }\n\n    /// `Array.prototype.copyWithin ( target, start [ , end ] )`\n    ///\n    /// The `copyWithin()` method shallow copies part of an array to another location\n    /// in the same array and returns it without modifying its length.\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///  - [MDN documentation][mdn]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-array.prototype.copywithin\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/copyWithin\n    pub(crate) fn copy_within(\n        this: &JsValue,\n        args: &[JsValue],\n        context: &mut Context,\n    ) -> JsResult<JsValue> {\n        // 1. Let O be ? ToObject(this value).\n        let o = this.to_object(context)?;\n\n        // 2. Let len be ? LengthOfArrayLike(O).\n        let len = o.length_of_array_like(context)?;\n\n        // 3. Let relativeTarget be ? ToIntegerOrInfinity(target).\n        // 4. If relativeTarget is -∞, let to be 0.\n        // 5. Else if relativeTarget < 0, let to be max(len + relativeTarget, 0).\n        // 6. Else, let to be min(relativeTarget, len).\n        let mut to = Self::get_relative_start(context, args.get_or_undefined(0), len)? as i64;\n\n        // 7. Let relativeStart be ? ToIntegerOrInfinity(start).\n        // 8. If relativeStart is -∞, let from be 0.\n        // 9. Else if relativeStart < 0, let from be max(len + relativeStart, 0).\n        // 10. Else, let from be min(relativeStart, len).\n        let mut from = Self::get_relative_start(context, args.get_or_undefined(1), len)? as i64;\n\n        // 11. If end is undefined, let relativeEnd be len; else let relativeEnd be ? ToIntegerOrInfinity(end).\n        // 12. If relativeEnd is -∞, let final be 0.\n        // 13. Else if relativeEnd < 0, let final be max(len + relativeEnd, 0).\n        // 14. Else, let final be min(relativeEnd, len).\n        let final_ = Self::get_relative_end(context, args.get_or_undefined(2), len)? as i64;\n\n        // 15. Let count be min(final - from, len - to).\n        let mut count = min(final_ - from, len as i64 - to);\n\n        // 16. If from < to and to < from + count, then\n        let direction = if from < to && to < from + count {\n            // b. Set from to from + count - 1.\n            from = from + count - 1;\n            // c. Set to to to + count - 1.\n            to = to + count - 1;\n\n            // a. Let direction be -1.\n            -1\n        // 17. Else,\n        } else {\n            // a. Let direction be 1.\n            1\n        };\n\n        // 18. Repeat, while count > 0,\n        while count > 0 {\n            // a. Let fromKey be ! ToString(𝔽(from)).\n            let from_key = from;\n\n            // b. Let toKey be ! ToString(𝔽(to)).\n            let to_key = to;\n\n            // c. Let fromPresent be ? HasProperty(O, fromKey).\n            // d. If fromPresent is true, then\n            // d.i. Let fromVal be ? Get(O, fromKey).\n            if let Some(from_val) = o.try_get(from_key, context)? {\n                // ii. Perform ? Set(O, toKey, fromVal, true).\n                o.set(to_key, from_val, true, context)?;\n            // e. Else,\n            } else {\n                // i. Assert: fromPresent is false.\n                // ii. Perform ? DeletePropertyOrThrow(O, toKey).\n                o.delete_property_or_throw(to_key, context)?;\n            }\n            // f. Set from to from + direction.\n            from += direction;\n            // g. Set to to to + direction.\n            to += direction;\n            // h. Set count to count - 1.\n            count -= 1;\n        }\n        // 19. Return O.\n        Ok(o.into())\n    }\n\n    /// `Array.prototype.values( )`\n    ///\n    /// The values method returns an iterable that iterates over the values in the array.\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///  - [MDN documentation][mdn]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-array.prototype.values\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/values\n    pub(crate) fn values(\n        this: &JsValue,\n        _: &[JsValue],\n        context: &mut Context,\n    ) -> JsResult<JsValue> {\n        // 1. Let O be ? ToObject(this value).\n        let o = this.to_object(context)?;\n\n        // 2. Return CreateArrayIterator(O, value).\n        Ok(ArrayIterator::create_array_iterator(\n            o,\n            PropertyNameKind::Value,\n            context,\n        ))\n    }\n\n    /// `Array.prototype.keys( )`\n    ///\n    /// The keys method returns an iterable that iterates over the indexes in the array.\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///  - [MDN documentation][mdn]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-array.prototype.keys\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/values\n    pub(crate) fn keys(this: &JsValue, _: &[JsValue], context: &mut Context) -> JsResult<JsValue> {\n        // 1. Let O be ? ToObject(this value).\n        let o = this.to_object(context)?;\n\n        // 2. Return CreateArrayIterator(O, key).\n        Ok(ArrayIterator::create_array_iterator(\n            o,\n            PropertyNameKind::Key,\n            context,\n        ))\n    }\n\n    /// `Array.prototype.entries( )`\n    ///\n    /// The entries method returns an iterable that iterates over the key-value pairs in the array.\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///  - [MDN documentation][mdn]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-array.prototype.entries\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/values\n    pub(crate) fn entries(\n        this: &JsValue,\n        _: &[JsValue],\n        context: &mut Context,\n    ) -> JsResult<JsValue> {\n        // 1. Let O be ? ToObject(this value).\n        let o = this.to_object(context)?;\n\n        // 2. Return CreateArrayIterator(O, key+value).\n        Ok(ArrayIterator::create_array_iterator(\n            o,\n            PropertyNameKind::KeyAndValue,\n            context,\n        ))\n    }\n\n    /// [`Array.prototype.with ( index, value )`][spec]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-array.prototype.with\n    pub(crate) fn with(\n        this: &JsValue,\n        args: &[JsValue],\n        context: &mut Context,\n    ) -> JsResult<JsValue> {\n        // 1. Let O be ? ToObject(this value).\n        let o = this.to_object(context)?;\n\n        // 2. Let len be ? LengthOfArrayLike(O).\n        let len = o.length_of_array_like(context)?;\n\n        // 3. Let relativeIndex be ? ToIntegerOrInfinity(index).\n        let IntegerOrInfinity::Integer(relative_index) =\n            args.get_or_undefined(0).to_integer_or_infinity(context)?\n        else {\n            return Err(JsNativeError::range()\n                .with_message(\"invalid integer index for TypedArray operation\")\n                .into());\n        };\n\n        let value = args.get_or_undefined(1);\n\n        // 4. If relativeIndex ≥ 0, let actualIndex be relativeIndex.\n        let actual_index = u64::try_from(relative_index) // should succeed if `relative_index >= 0`\n            .ok()\n            // 5. Else, let actualIndex be len + relativeIndex.\n            .or_else(|| len.checked_add_signed(relative_index))\n            .filter(|&rel| rel < len)\n            .ok_or_else(|| {\n                // 6. If actualIndex ≥ len or actualIndex < 0, throw a RangeError exception.\n                JsNativeError::range()\n                    .with_message(\"invalid integer index for TypedArray operation\")\n            })?;\n\n        // 7. Let A be ? ArrayCreate(len).\n        let new_array = Array::array_create(len, None, context)?;\n\n        // 8. Let k be 0.\n        // 9. Repeat, while k < len,\n        for k in 0..len {\n            // a. Let Pk be ! ToString(𝔽(k)).\n            let from_value = if k == actual_index {\n                // b. If k is actualIndex, let fromValue be value.\n                value.clone()\n            } else {\n                // c. Else, let fromValue be ? Get(O, Pk).\n                o.get(k, context)?\n            };\n\n            // d. Perform ! CreateDataPropertyOrThrow(A, Pk, fromValue).\n            new_array\n                .create_data_property_or_throw(k, from_value, context)\n                .js_expect(\"cannot fail for a newly created array\")?;\n\n            // e. Set k to k + 1.\n        }\n\n        // 10. Return A.\n        Ok(new_array.into())\n    }\n\n    /// Represents the algorithm to calculate `relativeStart` (or `k`) in array functions.\n    pub(super) fn get_relative_start(\n        context: &mut Context,\n        arg: &JsValue,\n        len: u64,\n    ) -> JsResult<u64> {\n        // 1. Let relativeStart be ? ToIntegerOrInfinity(start).\n        let relative_start = arg.to_integer_or_infinity(context)?;\n        let start = match relative_start {\n            // 2. If relativeStart is -∞, let k be 0.\n            IntegerOrInfinity::NegativeInfinity => 0,\n            // 3. Else if relativeStart < 0, let k be max(len + relativeStart, 0).\n            IntegerOrInfinity::Integer(i) if i < 0 => len.checked_add_signed(i).unwrap_or(0),\n            // 4. Else, let k be min(relativeStart, len).\n            IntegerOrInfinity::Integer(i) => min(i as u64, len),\n\n            // Special case - positive infinity. `len` is always smaller than +inf, thus from (4)\n            IntegerOrInfinity::PositiveInfinity => len,\n        };\n\n        Ok(start)\n    }\n\n    /// Represents the algorithm to calculate `relativeEnd` (or `final`) in array functions.\n    pub(super) fn get_relative_end(\n        context: &mut Context,\n        value: &JsValue,\n        len: u64,\n    ) -> JsResult<u64> {\n        // 1. If end is undefined, let relativeEnd be len [and return it]\n        if value.is_undefined() {\n            Ok(len)\n        } else {\n            // 1. cont, else let relativeEnd be ? ToIntegerOrInfinity(end).\n            let relative_end = value.to_integer_or_infinity(context)?;\n            let end = match relative_end {\n                // 2. If relativeEnd is -∞, let final be 0.\n                IntegerOrInfinity::NegativeInfinity => 0,\n                // 3. Else if relativeEnd < 0, let final be max(len + relativeEnd, 0).\n                IntegerOrInfinity::Integer(i) if i < 0 => len.checked_add_signed(i).unwrap_or(0),\n                // 4. Else, let final be min(relativeEnd, len).\n                // Both `as` casts are safe as both variables are non-negative\n                IntegerOrInfinity::Integer(i) => min(i as u64, len),\n\n                // Special case - positive infinity. `len` is always smaller than +inf, thus from (4)\n                IntegerOrInfinity::PositiveInfinity => len,\n            };\n\n            Ok(end)\n        }\n    }\n\n    /// `Array.prototype [ @@unscopables ]`\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///  - [MDN documentation][mdn]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-array.prototype-@@unscopables\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/@@unscopables\n    pub(crate) fn unscopables_object() -> JsObject {\n        // 1. Let unscopableList be OrdinaryObjectCreate(null).\n        let unscopable_list = JsObject::with_null_proto();\n        let true_prop = PropertyDescriptor::builder()\n            .value(true)\n            .writable(true)\n            .enumerable(true)\n            .configurable(true);\n        {\n            let mut obj = unscopable_list.borrow_mut();\n            // 2. Perform ! CreateDataPropertyOrThrow(unscopableList, \"at\", true).\n            obj.insert(js_string!(\"at\"), true_prop.clone());\n            // 3. Perform ! CreateDataPropertyOrThrow(unscopableList, \"copyWithin\", true).\n            obj.insert(js_string!(\"copyWithin\"), true_prop.clone());\n            // 4. Perform ! CreateDataPropertyOrThrow(unscopableList, \"entries\", true).\n            obj.insert(js_string!(\"entries\"), true_prop.clone());\n            // 5. Perform ! CreateDataPropertyOrThrow(unscopableList, \"fill\", true).\n            obj.insert(js_string!(\"fill\"), true_prop.clone());\n            // 6. Perform ! CreateDataPropertyOrThrow(unscopableList, \"find\", true).\n            obj.insert(js_string!(\"find\"), true_prop.clone());\n            // 7. Perform ! CreateDataPropertyOrThrow(unscopableList, \"findIndex\", true).\n            obj.insert(js_string!(\"findIndex\"), true_prop.clone());\n            // 8. Perform ! CreateDataPropertyOrThrow(unscopableList, \"findLast\", true).\n            obj.insert(js_string!(\"findLast\"), true_prop.clone());\n            // 9. Perform ! CreateDataPropertyOrThrow(unscopableList, \"findLastIndex\", true).\n            obj.insert(js_string!(\"findLastIndex\"), true_prop.clone());\n            // 10. Perform ! CreateDataPropertyOrThrow(unscopableList, \"flat\", true).\n            obj.insert(js_string!(\"flat\"), true_prop.clone());\n            // 11. Perform ! CreateDataPropertyOrThrow(unscopableList, \"flatMap\", true).\n            obj.insert(js_string!(\"flatMap\"), true_prop.clone());\n            // 12. Perform ! CreateDataPropertyOrThrow(unscopableList, \"includes\", true).\n            obj.insert(js_string!(\"includes\"), true_prop.clone());\n            // 13. Perform ! CreateDataPropertyOrThrow(unscopableList, \"keys\", true).\n            obj.insert(js_string!(\"keys\"), true_prop.clone());\n            // 14. Perform ! CreateDataPropertyOrThrow(unscopableList, \"toReversed\", true).\n            obj.insert(js_string!(\"toReversed\"), true_prop.clone());\n            // 15. Perform ! CreateDataPropertyOrThrow(unscopableList, \"toSorted\", true).\n            obj.insert(js_string!(\"toSorted\"), true_prop.clone());\n            // 16. Perform ! CreateDataPropertyOrThrow(unscopableList, \"toSpliced\", true).\n            obj.insert(js_string!(\"toSpliced\"), true_prop.clone());\n            // 17. Perform ! CreateDataPropertyOrThrow(unscopableList, \"values\", true).\n            obj.insert(js_string!(\"values\"), true_prop);\n        }\n\n        // 13. Return unscopableList.\n        unscopable_list\n    }\n}\n\n/// [`CompareArrayElements ( x, y, comparefn )`][spec]\n///\n/// [spec]: https://tc39.es/ecma262/#sec-comparearrayelements\nfn compare_array_elements(\n    x: &JsValue,\n    y: &JsValue,\n    comparefn: Option<&JsObject>,\n    context: &mut Context,\n) -> JsResult<Ordering> {\n    match (x.is_undefined(), y.is_undefined()) {\n        // 1. If x and y are both undefined, return +0𝔽.\n        (true, true) => return Ok(Ordering::Equal),\n        // 2. If x is undefined, return 1𝔽.\n        (true, false) => return Ok(Ordering::Greater),\n        // 3. If y is undefined, return -1𝔽.\n        (false, true) => return Ok(Ordering::Less),\n        _ => {}\n    }\n\n    // 4. If comparefn is not undefined, then\n    if let Some(cmp) = comparefn {\n        let args = [x.clone(), y.clone()];\n        //     a. Let v be ? ToNumber(? Call(comparefn, undefined, « x, y »)).\n        let v = cmp\n            .call(&JsValue::undefined(), &args, context)?\n            .to_number(context)?;\n        //     b. If v is NaN, return +0𝔽.\n        //     c. Return v.\n        return Ok(v.partial_cmp(&0.0).unwrap_or(Ordering::Equal));\n    }\n\n    // 5. Let xString be ? ToString(x).\n    let x_str = x.to_string(context)?;\n\n    // 6. Let yString be ? ToString(y).\n    let y_str = y.to_string(context)?;\n\n    // 7. Let xSmaller be ! IsLessThan(xString, yString, true).\n    // 8. If xSmaller is true, return -1𝔽.\n    // 9. Let ySmaller be ! IsLessThan(yString, xString, true).\n    // 10. If ySmaller is true, return 1𝔽.\n    // 11. Return +0𝔽.\n    // NOTE: skipped IsLessThan because it just makes a lexicographic comparison\n    // when x and y are strings\n    Ok(x_str.cmp(&y_str))\n}\n\n/// `FindViaPredicate ( O, len, direction, predicate, thisArg )`\n///\n/// More information:\n///  - [ECMAScript reference][spec]\n///\n/// [spec]: https://tc39.es/ecma262/#sec-findviapredicate\npub(crate) fn find_via_predicate(\n    o: &JsObject,\n    len: u64,\n    direction: Direction,\n    predicate: &JsValue,\n    this_arg: &JsValue,\n    context: &mut Context,\n    caller_name: &str,\n) -> JsResult<(JsValue, JsValue)> {\n    // 1. If IsCallable(predicate) is false, throw a TypeError exception.\n    let predicate = predicate.as_callable().ok_or_else(|| {\n        JsNativeError::typ().with_message(format!(\"{caller_name}: predicate is not callable\"))\n    })?;\n\n    let indices = match direction {\n        // 2. If direction is ascending, then\n        // a. Let indices be a List of the integers in the interval from 0 (inclusive) to len (exclusive), in ascending order.\n        Direction::Ascending => itertools::Either::Left(0..len),\n        // 3. Else,\n        // a. Let indices be a List of the integers in the interval from 0 (inclusive) to len (exclusive), in descending order.\n        Direction::Descending => itertools::Either::Right((0..len).rev()),\n    };\n\n    // 4. For each integer k of indices, do\n    for k in indices {\n        // a. Let Pk be ! ToString(𝔽(k)).\n        let pk = k;\n\n        // b. NOTE: If O is a TypedArray, the following invocation of Get will return a normal completion.\n        // c. Let kValue be ? Get(O, Pk).\n        let k_value = o.get(pk, context)?;\n\n        // d. Let testResult be ? Call(predicate, thisArg, « kValue, 𝔽(k), O »).\n        let test_result = predicate\n            .call(\n                this_arg,\n                &[k_value.clone(), k.into(), o.clone().into()],\n                context,\n            )?\n            .to_boolean();\n\n        if test_result {\n            // e. If ToBoolean(testResult) is true, return the Record { [[Index]]: 𝔽(k), [[Value]]: kValue }.\n            return Ok((JsValue::new(k), k_value));\n        }\n    }\n\n    // 5. Return the Record { [[Index]]: -1𝔽, [[Value]]: undefined }\n    Ok((JsValue::new(-1), JsValue::undefined()))\n}\n\n/// Define an own property for an array exotic object.\n///\n/// More information:\n///  - [ECMAScript reference][spec]\n///\n/// [spec]: https://tc39.es/ecma262/#sec-array-exotic-objects-defineownproperty-p-desc\nfn array_exotic_define_own_property(\n    obj: &JsObject,\n    key: &PropertyKey,\n    desc: PropertyDescriptor,\n    context: &mut InternalMethodPropertyContext<'_>,\n) -> JsResult<bool> {\n    // 1. Assert: IsPropertyKey(P) is true.\n    match key {\n        // 2. If P is \"length\", then\n        PropertyKey::String(s) if s == &StaticJsStrings::LENGTH => {\n            // a. Return ? ArraySetLength(A, Desc).\n\n            array_set_length(obj, desc, context)\n        }\n        // 3. Else if P is an array index, then\n        PropertyKey::Index(index) => {\n            let index = index.get();\n            let new_len = index + 1;\n\n            // Optimization: If the shape of the object is the array template shape,\n            // we know the position of the \"length\" property.\n            if u64::from(new_len) < (2u64.pow(32) - 1) {\n                let borrowed_object = obj.borrow();\n                if borrowed_object.properties().shape.to_addr_usize()\n                    == context\n                        .intrinsics()\n                        .templates()\n                        .array()\n                        .shape()\n                        .to_addr_usize()\n                {\n                    let old_len = borrowed_object.properties().storage[0].clone();\n                    drop(borrowed_object);\n                    let old_len = old_len.to_u32(context)?;\n                    if new_len >= old_len {\n                        if ordinary_define_own_property(obj, key, desc, context)? {\n                            let mut borrowed_object = obj.borrow_mut();\n                            borrowed_object.properties_mut().storage[0] = JsValue::new(new_len);\n                            return Ok(true);\n                        }\n                        return Ok(false);\n                    }\n                }\n            }\n\n            // a. Let oldLenDesc be OrdinaryGetOwnProperty(A, \"length\").\n            let old_len_desc =\n                ordinary_get_own_property(obj, &StaticJsStrings::LENGTH.into(), context)?\n                    .expect(\"the property descriptor must exist\");\n\n            // b. Assert: ! IsDataDescriptor(oldLenDesc) is true.\n            debug_assert!(old_len_desc.is_data_descriptor());\n\n            // c. Assert: oldLenDesc.[[Configurable]] is false.\n            debug_assert!(!old_len_desc.expect_configurable());\n\n            // d. Let oldLen be oldLenDesc.[[Value]].\n            // e. Assert: oldLen is a non-negative integral Number.\n            // f. Let index be ! ToUint32(P).\n            let old_len = old_len_desc\n                .expect_value()\n                .to_u32(context)\n                .js_expect(\"this ToUint32 call must not fail\")?;\n\n            // g. If index ≥ oldLen and oldLenDesc.[[Writable]] is false, return false.\n            if index >= old_len && !old_len_desc.expect_writable() {\n                return Ok(false);\n            }\n\n            // h. Let succeeded be ! OrdinaryDefineOwnProperty(A, P, Desc).\n            if ordinary_define_own_property(obj, key, desc, context)? {\n                // j. If index ≥ oldLen, then\n                if index >= old_len {\n                    // i. Set oldLenDesc.[[Value]] to index + 1𝔽.\n                    let old_len_desc = PropertyDescriptor::builder()\n                        .value(new_len)\n                        .maybe_writable(old_len_desc.writable())\n                        .maybe_enumerable(old_len_desc.enumerable())\n                        .maybe_configurable(old_len_desc.configurable());\n\n                    // ii. Set succeeded to OrdinaryDefineOwnProperty(A, \"length\", oldLenDesc).\n                    let succeeded = ordinary_define_own_property(\n                        obj,\n                        &StaticJsStrings::LENGTH.into(),\n                        old_len_desc.into(),\n                        context,\n                    )?;\n\n                    // iii. Assert: succeeded is true.\n                    debug_assert!(succeeded);\n                }\n\n                // k. Return true.\n                Ok(true)\n            } else {\n                // i. If succeeded is false, return false.\n                Ok(false)\n            }\n        }\n        // 4. Return OrdinaryDefineOwnProperty(A, P, Desc).\n        _ => ordinary_define_own_property(obj, key, desc, context),\n    }\n}\n\n/// Abstract operation `ArraySetLength ( A, Desc )`\n///\n/// More information:\n///  - [ECMAScript reference][spec]\n///\n/// [spec]: https://tc39.es/ecma262/#sec-arraysetlength\nfn array_set_length(\n    obj: &JsObject,\n    desc: PropertyDescriptor,\n    context: &mut InternalMethodPropertyContext<'_>,\n) -> JsResult<bool> {\n    // 1. If Desc.[[Value]] is absent, then\n    let Some(new_len_val) = desc.value() else {\n        // a. Return OrdinaryDefineOwnProperty(A, \"length\", Desc).\n        return ordinary_define_own_property(obj, &StaticJsStrings::LENGTH.into(), desc, context);\n    };\n\n    // 3. Let newLen be ? ToUint32(Desc.[[Value]]).\n    let new_len = new_len_val.to_u32(context)?;\n\n    // 4. Let numberLen be ? ToNumber(Desc.[[Value]]).\n    let number_len = new_len_val.to_number(context)?;\n\n    // 5. If SameValueZero(newLen, numberLen) is false, throw a RangeError exception.\n    #[allow(clippy::float_cmp)]\n    if f64::from(new_len) != number_len {\n        return Err(JsNativeError::range()\n            .with_message(\"bad length for array\")\n            .into());\n    }\n\n    // 2. Let newLenDesc be a copy of Desc.\n    // 6. Set newLenDesc.[[Value]] to newLen.\n    let mut new_len_desc = PropertyDescriptor::builder()\n        .value(new_len)\n        .maybe_writable(desc.writable())\n        .maybe_enumerable(desc.enumerable())\n        .maybe_configurable(desc.configurable());\n\n    // 7. Let oldLenDesc be OrdinaryGetOwnProperty(A, \"length\").\n    let old_len_desc = ordinary_get_own_property(obj, &StaticJsStrings::LENGTH.into(), context)?\n        .expect(\"the property descriptor must exist\");\n\n    // 8. Assert: ! IsDataDescriptor(oldLenDesc) is true.\n    debug_assert!(old_len_desc.is_data_descriptor());\n\n    // 9. Assert: oldLenDesc.[[Configurable]] is false.\n    debug_assert!(!old_len_desc.expect_configurable());\n\n    // 10. Let oldLen be oldLenDesc.[[Value]].\n    let old_len = old_len_desc.expect_value();\n\n    // 11. If newLen ≥ oldLen, then\n    if new_len >= old_len.to_u32(context)? {\n        // a. Return OrdinaryDefineOwnProperty(A, \"length\", newLenDesc).\n        return ordinary_define_own_property(\n            obj,\n            &StaticJsStrings::LENGTH.into(),\n            new_len_desc.build(),\n            context,\n        );\n    }\n\n    // 12. If oldLenDesc.[[Writable]] is false, return false.\n    if !old_len_desc.expect_writable() {\n        return Ok(false);\n    }\n\n    // 13. If newLenDesc.[[Writable]] is absent or has the value true, let newWritable be true.\n    let new_writable = if new_len_desc.inner().writable().unwrap_or(true) {\n        true\n    }\n    // 14. Else,\n    else {\n        // a. NOTE: Setting the [[Writable]] attribute to false is deferred in case any\n        // elements cannot be deleted.\n        // c. Set newLenDesc.[[Writable]] to true.\n        new_len_desc = new_len_desc.writable(true);\n\n        // b. Let newWritable be false.\n        false\n    };\n\n    // 15. Let succeeded be ! OrdinaryDefineOwnProperty(A, \"length\", newLenDesc).\n    // 16. If succeeded is false, return false.\n    if !ordinary_define_own_property(\n        obj,\n        &StaticJsStrings::LENGTH.into(),\n        new_len_desc.clone().build(),\n        context,\n    )\n    .js_expect(\"this OrdinaryDefineOwnProperty call must not fail\")?\n    {\n        return Ok(false);\n    }\n\n    // 17. For each own property key P of A that is an array index, whose numeric value is\n    // greater than or equal to newLen, in descending numeric index order, do\n    let ordered_keys = {\n        let mut keys: Vec<_> = obj\n            .borrow()\n            .properties\n            .index_property_keys()\n            .filter(|idx| new_len <= *idx && *idx < u32::MAX)\n            .collect();\n        keys.sort_unstable_by(|x, y| y.cmp(x));\n        keys\n    };\n\n    for index in ordered_keys {\n        // a. Let deleteSucceeded be ! A.[[Delete]](P).\n        // b. If deleteSucceeded is false, then\n        if !obj.__delete__(&index.into(), context)? {\n            // i. Set newLenDesc.[[Value]] to ! ToUint32(P) + 1𝔽.\n            new_len_desc = new_len_desc.value(index + 1);\n\n            // ii. If newWritable is false, set newLenDesc.[[Writable]] to false.\n            if !new_writable {\n                new_len_desc = new_len_desc.writable(false);\n            }\n\n            // iii. Perform ! OrdinaryDefineOwnProperty(A, \"length\", newLenDesc).\n            ordinary_define_own_property(\n                obj,\n                &StaticJsStrings::LENGTH.into(),\n                new_len_desc.build(),\n                context,\n            )\n            .js_expect(\"this OrdinaryDefineOwnProperty call must not fail\")?;\n\n            // iv. Return false.\n            return Ok(false);\n        }\n    }\n\n    // 18. If newWritable is false, then\n    if !new_writable {\n        // a. Set succeeded to ! OrdinaryDefineOwnProperty(A, \"length\",\n        // PropertyDescriptor { [[Writable]]: false }).\n        let succeeded = ordinary_define_own_property(\n            obj,\n            &StaticJsStrings::LENGTH.into(),\n            PropertyDescriptor::builder().writable(false).build(),\n            context,\n        )\n        .js_expect(\"this OrdinaryDefineOwnProperty call must not fail\")?;\n\n        // b. Assert: succeeded is true.\n        debug_assert!(succeeded);\n    }\n\n    // 19. Return true.\n    Ok(true)\n}\n"
  },
  {
    "path": "core/engine/src/builtins/array/tests.rs",
    "content": "use super::Array;\nuse crate::{\n    Context, JsNativeErrorKind, JsValue, TestAction, builtins::Number, js_string, run_test_actions,\n};\nuse boa_macros::js_str;\nuse indoc::indoc;\n\n#[test]\nfn is_array() {\n    run_test_actions([\n        TestAction::assert(\"Array.isArray([])\"),\n        TestAction::assert(\"Array.isArray(new Array())\"),\n        TestAction::assert(\"Array.isArray(['a', 'b', 'c'])\"),\n        TestAction::assert(\"Array.isArray([1, 2, 3])\"),\n        TestAction::assert(\"!Array.isArray({})\"),\n        TestAction::assert(\"Array.isArray(new Array)\"),\n        TestAction::assert(\"!Array.isArray()\"),\n        TestAction::assert(\"!Array.isArray({ constructor: Array })\"),\n        TestAction::assert(\n            \"!Array.isArray({ push: Array.prototype.push, concat: Array.prototype.concat })\",\n        ),\n        TestAction::assert(\"!Array.isArray(17)\"),\n        TestAction::assert(\"!Array.isArray({ __proto__: Array.prototype })\"),\n        TestAction::assert(\"!Array.isArray({ length: 0 })\"),\n    ]);\n}\n\n#[test]\nfn of() {\n    run_test_actions([\n        TestAction::run_harness(),\n        TestAction::assert(\"arrayEquals(Array.of(1, 2, 3), [1, 2, 3])\"),\n        TestAction::assert(indoc! {r#\"\n            arrayEquals(\n                Array.of(1, 'a', [], undefined, null),\n                [1, 'a', [], undefined, null]\n            )\n            \"#}),\n        TestAction::assert(\"arrayEquals(Array.of(), [])\"),\n        TestAction::run(\"let a = Array.of.call(Date, 'a', undefined, 3);\"),\n        TestAction::assert(\"a instanceof Date\"),\n        TestAction::assert_eq(\"a[0]\", js_str!(\"a\")),\n        TestAction::assert_eq(\"a[1]\", JsValue::undefined()),\n        TestAction::assert_eq(\"a[2]\", 3),\n        TestAction::assert_eq(\"a.length\", 3),\n    ]);\n}\n\n#[test]\nfn concat() {\n    run_test_actions([\n        TestAction::run_harness(),\n        // Empty ++ Empty\n        TestAction::assert(\"arrayEquals([].concat([]), [])\"),\n        // Empty ++ NonEmpty\n        TestAction::assert(\"arrayEquals([].concat([1]), [1])\"),\n        // NonEmpty ++ Empty\n        TestAction::assert(\"arrayEquals([1].concat([]), [1])\"),\n        // NonEmpty ++ NonEmpty\n        TestAction::assert(\"arrayEquals([1].concat([1]), [1, 1])\"),\n    ]);\n}\n\n#[test]\nfn copy_within() {\n    run_test_actions([\n        TestAction::run_harness(),\n        TestAction::assert(\"arrayEquals([1,2,3,4,5].copyWithin(-2), [1,2,3,1,2])\"),\n        TestAction::assert(\"arrayEquals([1,2,3,4,5].copyWithin(0, 3), [4,5,3,4,5])\"),\n        TestAction::assert(\"arrayEquals([1,2,3,4,5].copyWithin(0, 3, 4), [4,2,3,4,5])\"),\n        TestAction::assert(\"arrayEquals([1,2,3,4,5].copyWithin(-2, -3, -1), [1,2,3,3,4])\"),\n    ]);\n}\n\n#[test]\nfn join() {\n    run_test_actions([\n        TestAction::assert_eq(\"[].join('.')\", js_string!()),\n        TestAction::assert_eq(\"['a'].join('.')\", js_str!(\"a\")),\n        TestAction::assert_eq(\"['a', 'b', 'c'].join('.')\", js_str!(\"a.b.c\")),\n        TestAction::assert_eq(\"let a=[];a[0]=a;a[1]=a;a[2]=a;a.join()\", js_str!(\",,\")),\n    ]);\n}\n\n#[test]\nfn to_string() {\n    run_test_actions([\n        TestAction::assert_eq(\"[].toString()\", js_string!()),\n        TestAction::assert_eq(\"['a'].toString()\", js_str!(\"a\")),\n        TestAction::assert_eq(\"['a', 'b', 'c'].toString()\", js_str!(\"a,b,c\")),\n    ]);\n}\n\n#[test]\nfn every() {\n    // taken from https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/every\n    run_test_actions([\n        TestAction::run(indoc! {r#\"\n                function appendingCallback(elem,index,arr) {\n                    arr.push('new');\n                    return elem !== \"new\";\n                }\n                function deletingCallback(elem,index,arr) {\n                    arr.pop()\n                    return elem < 3;\n                }\n            \"#}),\n        TestAction::assert(\"[11, 23, 45].every(e => e > 10)\"),\n        TestAction::assert(\"[].every(e => e < 10)\"),\n        TestAction::assert(\"![11, 23, 45].every(e => e < 10)\"),\n        TestAction::assert(\"[1,2,3,4].every(appendingCallback)\"),\n        TestAction::assert(\"[1,2,3,4].every(deletingCallback)\"),\n    ]);\n}\n\n#[test]\nfn find() {\n    run_test_actions([TestAction::assert_eq(\n        \"['a', 'b', 'c'].find(e => e == 'a')\",\n        js_str!(\"a\"),\n    )]);\n}\n\n#[test]\nfn find_index() {\n    run_test_actions([\n        TestAction::assert_eq(\"[1, 2, 3].findIndex(e => e == 2)\", 1),\n        TestAction::assert_eq(\"[].findIndex(e => e == 2)\", -1),\n        TestAction::assert_eq(\"[4, 5, 6].findIndex(e => e == 2)\", -1),\n    ]);\n}\n\n#[test]\nfn flat() {\n    run_test_actions([\n        TestAction::run_harness(),\n        TestAction::assert(\"arrayEquals( [[]].flat(), [] )\"),\n        TestAction::assert(indoc! {r#\"\n                arrayEquals(\n                    ['a', ['b', 'c']].flat(),\n                    ['a', 'b', 'c']\n                )\n            \"#}),\n        TestAction::assert(indoc! {r#\"\n                arrayEquals(\n                    ['a', ['b', ['c'], 'd']].flat(2),\n                    ['a', 'b', 'c', 'd']\n                )\n            \"#}),\n        TestAction::assert(\"arrayEquals( [[[[[['a']]]]]].flat(Infinity), ['a'] )\"),\n    ]);\n}\n\n#[test]\nfn flat_map() {\n    run_test_actions([\n        TestAction::run_harness(),\n        TestAction::assert(indoc! {r#\"\n                arrayEquals(\n                    [1, 2, 3].flatMap(i => [i * 2]),\n                    [2, 4, 6]\n                )\n            \"#}),\n        TestAction::assert(indoc! {r#\"\n                arrayEquals(\n                    [\"it's Sunny\", \"in Cali\"].flatMap(x => x.split(\" \")),\n                    [\"it's\", \"Sunny\", \"in\", \"Cali\"]\n                )\n            \"#}),\n    ]);\n}\n\n#[test]\nfn flat_map_with_hole() {\n    run_test_actions([\n        TestAction::run_harness(),\n        TestAction::assert(indoc! {r#\"\n                var arr = [0, 1, 2];\n                delete arr[1];\n                arrayEquals(\n                    arr.flatMap(i => [i * 2]),\n                    [0, 4]\n                )\n            \"#}),\n    ]);\n}\n\n#[test]\nfn flat_map_not_callable() {\n    run_test_actions([TestAction::assert_native_error(\n        indoc! {r#\"\n            var array = [1,2,3];\n            array.flatMap(\"not a function\");\n        \"#},\n        JsNativeErrorKind::Type,\n        \"flatMap mapper function is not callable\",\n    )]);\n}\n\n#[test]\nfn push() {\n    run_test_actions([\n        TestAction::run(\"var arr = [1, 2];\"),\n        TestAction::assert_eq(\"arr.push()\", 2),\n        TestAction::assert_eq(\"arr.push(3, 4)\", 4),\n        TestAction::assert_eq(\"arr[2]\", 3),\n        TestAction::assert_eq(\"arr[3]\", 4),\n    ]);\n}\n\n#[test]\nfn pop() {\n    run_test_actions([\n        TestAction::run_harness(),\n        TestAction::run(indoc! {r#\"\n                var one = [1];\n                var many = [1, 2, 3, 4];\n            \"#}),\n        TestAction::assert_eq(\"[].pop()\", JsValue::undefined()),\n        TestAction::assert_eq(\"one.pop()\", 1),\n        TestAction::assert(\"arrayEquals(one, [])\"),\n        TestAction::assert_eq(\"many.pop()\", 4),\n        TestAction::assert(\"arrayEquals(many, [1, 2, 3])\"),\n    ]);\n}\n\n#[test]\nfn shift() {\n    run_test_actions([\n        TestAction::run_harness(),\n        TestAction::run(indoc! {r#\"\n                var one = [1];\n                var many = [1, 2, 3, 4];\n            \"#}),\n        TestAction::assert_eq(\"[].shift()\", JsValue::undefined()),\n        TestAction::assert_eq(\"one.shift()\", 1),\n        TestAction::assert(\"arrayEquals(one, [])\"),\n        TestAction::assert_eq(\"many.shift()\", 1),\n        TestAction::assert(\"arrayEquals(many, [2, 3, 4])\"),\n    ]);\n}\n\n#[test]\nfn unshift() {\n    run_test_actions([\n        TestAction::run_harness(),\n        TestAction::run(\"var arr = [3, 4];\"),\n        TestAction::assert_eq(\"arr.unshift()\", 2),\n        TestAction::assert_eq(\"arr.unshift(1, 2)\", 4),\n        TestAction::assert(\"arrayEquals(arr, [1, 2, 3, 4])\"),\n    ]);\n}\n\n#[test]\nfn reverse() {\n    run_test_actions([\n        TestAction::run_harness(),\n        TestAction::run(\"var arr = [1, 2];\"),\n        TestAction::assert(\"arrayEquals(arr.reverse(), [2, 1])\"),\n        TestAction::assert(\"arrayEquals(arr, [2, 1])\"),\n    ]);\n}\n\n#[test]\nfn index_of() {\n    run_test_actions([\n        TestAction::run(indoc! {r#\"\n                var one = [\"a\"];\n                var many = [\"a\", \"b\", \"c\"];\n                var duplicates = [\"a\", \"b\", \"c\", \"a\", \"b\"];\n            \"#}),\n        // Empty\n        TestAction::assert_eq(\"[].indexOf('a')\", -1),\n        // One\n        TestAction::assert_eq(\"one.indexOf('a')\", 0),\n        // Missing from one\n        TestAction::assert_eq(\"one.indexOf('b')\", -1),\n        // First in many\n        TestAction::assert_eq(\"many.indexOf('a')\", 0),\n        // Second in many\n        TestAction::assert_eq(\"many.indexOf('b')\", 1),\n        // First in duplicates\n        TestAction::assert_eq(\"duplicates.indexOf('a')\", 0),\n        // Second in duplicates\n        TestAction::assert_eq(\"duplicates.indexOf('b')\", 1),\n        // Positive fromIndex greater than array length\n        TestAction::assert_eq(\"one.indexOf('a', 2)\", -1),\n        // Positive fromIndex missed match\n        TestAction::assert_eq(\"many.indexOf('a', 1)\", -1),\n        // Positive fromIndex matched\n        TestAction::assert_eq(\"many.indexOf('b', 1)\", 1),\n        // Positive fromIndex with duplicates\n        TestAction::assert_eq(\"duplicates.indexOf('a', 1)\", 3),\n        // Negative fromIndex greater than array length\n        TestAction::assert_eq(\"one.indexOf('a', -2)\", 0),\n        // Negative fromIndex missed match\n        TestAction::assert_eq(\"many.indexOf('b', -1)\", -1),\n        // Negative fromIndex matched\n        TestAction::assert_eq(\"many.indexOf('c', -1)\", 2),\n        // Negative fromIndex with duplicates\n        TestAction::assert_eq(\"duplicates.indexOf('b', -2)\", 4),\n    ]);\n}\n\n#[test]\nfn last_index_of() {\n    run_test_actions([\n        TestAction::run(indoc! {r#\"\n                var one = [\"a\"];\n                var many = [\"a\", \"b\", \"c\"];\n                var duplicates = [\"a\", \"b\", \"c\", \"a\", \"b\"];\n            \"#}),\n        // Empty\n        TestAction::assert_eq(\"[].lastIndexOf('a')\", -1),\n        // One\n        TestAction::assert_eq(\"one.lastIndexOf('a')\", 0),\n        // Missing from one\n        TestAction::assert_eq(\"one.lastIndexOf('b')\", -1),\n        // First in many\n        TestAction::assert_eq(\"many.lastIndexOf('a')\", 0),\n        // Second in many\n        TestAction::assert_eq(\"many.lastIndexOf('b')\", 1),\n        // 4th in duplicates\n        TestAction::assert_eq(\"duplicates.lastIndexOf('a')\", 3),\n        // 5th in duplicates\n        TestAction::assert_eq(\"duplicates.lastIndexOf('b')\", 4),\n        // Positive fromIndex greater than array length\n        TestAction::assert_eq(\"one.lastIndexOf('a', 2)\", 0),\n        // Positive fromIndex missed match\n        TestAction::assert_eq(\"many.lastIndexOf('c', 1)\", -1),\n        // Positive fromIndex matched\n        TestAction::assert_eq(\"many.lastIndexOf('b', 1)\", 1),\n        // Positive fromIndex with duplicates\n        TestAction::assert_eq(\"duplicates.lastIndexOf('a', 1)\", 0),\n        // Negative fromIndex greater than array length\n        TestAction::assert_eq(\"one.lastIndexOf('a', -2)\", -1),\n        // Negative fromIndex missed match\n        TestAction::assert_eq(\"many.lastIndexOf('c', -2)\", -1),\n        // Negative fromIndex matched\n        TestAction::assert_eq(\"many.lastIndexOf('c', -1)\", 2),\n        // Negative fromIndex with duplicates\n        TestAction::assert_eq(\"duplicates.lastIndexOf('b', -2)\", 1),\n    ]);\n}\n\n#[test]\nfn fill_obj_ref() {\n    run_test_actions([\n        TestAction::run(indoc! {r#\"\n                let obj = {};\n                let a = new Array(3).fill(obj);\n                obj.hi = 'hi'\n            \"#}),\n        TestAction::assert_eq(\"a[2].hi\", js_str!(\"hi\")),\n    ]);\n}\n\n#[test]\nfn fill() {\n    run_test_actions([\n        TestAction::run_harness(),\n        TestAction::run(\"var a = [1, 2, 3];\"),\n        TestAction::assert(\"arrayEquals(a.fill(4), [4, 4, 4])\"),\n        // make sure the array is modified\n        TestAction::assert(\"arrayEquals(a, [4, 4, 4])\"),\n        TestAction::assert(\"arrayEquals([1, 2, 3].fill(4, '1'), [1, 4, 4])\"),\n        TestAction::assert(\"arrayEquals([1, 2, 3].fill(4, 1, 2), [1, 4, 3])\"),\n        TestAction::assert(\"arrayEquals([1, 2, 3].fill(4, 1, 1), [1, 2, 3])\"),\n        TestAction::assert(\"arrayEquals([1, 2, 3].fill(4, 3, 3), [1, 2, 3])\"),\n        TestAction::assert(\"arrayEquals([1, 2, 3].fill(4, -3, -2), [4, 2, 3])\"),\n        TestAction::assert(\"arrayEquals([1, 2, 3].fill(4, NaN, NaN), [1, 2, 3])\"),\n        TestAction::assert(\"arrayEquals([1, 2, 3].fill(4, 3, 5), [1, 2, 3])\"),\n        TestAction::assert(\"arrayEquals([1, 2, 3].fill(4, '1.2', '2.5'), [1, 4, 3])\"),\n        TestAction::assert(\"arrayEquals([1, 2, 3].fill(4, 'str'), [4, 4, 4])\"),\n        TestAction::assert(\"arrayEquals([1, 2, 3].fill(4, 'str', 'str'), [1, 2, 3])\"),\n        TestAction::assert(\"arrayEquals([1, 2, 3].fill(4, undefined, null), [1, 2, 3])\"),\n        TestAction::assert(\"arrayEquals([1, 2, 3].fill(4, undefined, undefined), [4, 4, 4])\"),\n        TestAction::assert(\"arrayEquals([1, 2, 3].fill(), [undefined, undefined, undefined])\"),\n    ]);\n}\n\n#[test]\nfn includes_value() {\n    run_test_actions([\n        TestAction::run(indoc! {r#\"\n                var one = [\"a\"];\n                var many = [\"a\", \"b\", \"c\"];\n                var duplicates = [\"a\", \"b\", \"c\", \"a\", \"b\"];\n            \"#}),\n        // Empty\n        TestAction::assert(\"![].includes('a')\"),\n        // One\n        TestAction::assert(\"one.includes('a')\"),\n        // Missing from one\n        TestAction::assert(\"!one.includes('b')\"),\n        // In many\n        TestAction::assert(\"many.includes('b')\"),\n        // Missing from many\n        TestAction::assert(\"!many.includes('d')\"),\n        // In duplicates\n        TestAction::assert(\"duplicates.includes('a')\"),\n        // Missing from duplicates\n        TestAction::assert(\"!duplicates.includes('d')\"),\n    ]);\n}\n\n#[test]\nfn map() {\n    run_test_actions([\n        TestAction::run_harness(),\n        TestAction::run(indoc! {r#\"\n                var one = [\"x\"];\n                var many = [\"x\", \"y\", \"z\"];\n            \"#}),\n        // Empty\n        TestAction::assert(\"arrayEquals([].map(v => v + '_'), [])\"),\n        // One\n        TestAction::assert(\"arrayEquals(one.map(v => '_' + v), ['_x'])\"),\n        // Many\n        TestAction::assert(indoc! {r#\"\n                arrayEquals(\n                    many.map(v => '_' + v + '_'),\n                    ['_x_', '_y_', '_z_']\n                )\n            \"#}),\n        // assert the old arrays have not been modified\n        TestAction::assert(\"arrayEquals(one, ['x'])\"),\n        TestAction::assert(\"arrayEquals(many, ['x', 'y', 'z'])\"),\n        // One but it uses `this` inside the callback\n        TestAction::assert(indoc! {r#\"\n                var _this = { answer: 42 };\n\n                function callback() {\n                    return 'The answer to life is: ' + this.answer;\n                }\n\n                arrayEquals(\n                    one.map(callback, _this),\n                    ['The answer to life is: 42']\n                )\n            \"#}),\n    ]);\n}\n\n#[test]\nfn slice() {\n    run_test_actions([\n        TestAction::run_harness(),\n        TestAction::assert(\"arrayEquals([].slice(), [])\"),\n        TestAction::assert(\"arrayEquals(['a'].slice(), ['a'])\"),\n        TestAction::assert(indoc! {r#\"\n                arrayEquals(\n                    [\"a\", \"b\", \"c\", \"d\"].slice(1),\n                    [\"b\", \"c\", \"d\"]\n                )\n            \"#}),\n        TestAction::assert(indoc! {r#\"\n                arrayEquals(\n                    [\"a\", \"b\", \"c\", \"d\"].slice(2, 3),\n                    [\"c\"]\n                )\n            \"#}),\n        TestAction::assert(indoc! {r#\"\n                arrayEquals(\n                    [\"a\", \"b\", \"c\", \"d\"].slice(7),\n                    []\n                )\n            \"#}),\n    ]);\n}\n\n#[test]\nfn for_each() {\n    run_test_actions([\n        TestAction::run(indoc! {r#\"\n                var sum = 0;\n                var indexSum = 0;\n                var listLengthSum = 0;\n                function callingCallback(item, index, list) {\n                    sum += item;\n                    indexSum += index;\n                    listLengthSum += list.length;\n                }\n                [2, 3, 4, 5].forEach(callingCallback);\n            \"#}),\n        TestAction::assert_eq(\"sum\", 14),\n        TestAction::assert_eq(\"indexSum\", 6),\n        TestAction::assert_eq(\"listLengthSum\", 16),\n    ]);\n}\n\n#[test]\nfn for_each_push_value() {\n    run_test_actions([\n        TestAction::run_harness(),\n        TestAction::run(indoc! {r#\"\n                var a = [1, 2, 3, 4];\n                a.forEach((item, index, list) => list.push(item * 2));\n            \"#}),\n        TestAction::assert(indoc! {r#\"\n                arrayEquals(\n                    a,\n                    [1, 2, 3, 4, 2, 4, 6, 8]\n                )\n            \"#}),\n    ]);\n}\n\n#[test]\nfn filter() {\n    run_test_actions([\n        TestAction::run_harness(),\n        TestAction::run(\"var empty = [], one = ['1'], many = ['1', '0', '1'];\"),\n        // Empty\n        TestAction::assert(indoc! {r#\"\n                arrayEquals(\n                    empty.filter(v => v === \"1\"),\n                    []\n                )\n            \"#}),\n        // One filtered on \"1\"\n        TestAction::assert(indoc! {r#\"\n                arrayEquals(\n                    one.filter(v => v === \"1\"),\n                    [\"1\"]\n                )\n            \"#}),\n        //  One filtered on \"0\"\n        TestAction::assert(indoc! {r#\"\n                arrayEquals(\n                    one.filter(v => v === \"0\"),\n                    []\n                )\n            \"#}),\n        // Many filtered on \"1\"\n        TestAction::assert(indoc! {r#\"\n                arrayEquals(\n                    many.filter(v => v === \"1\"),\n                    [\"1\", \"1\"]\n                )\n            \"#}),\n        //  Many filtered on \"0\"\n        TestAction::assert(indoc! {r#\"\n                arrayEquals(\n                    many.filter(v => v === \"0\"),\n                    [\"0\"]\n                )\n            \"#}),\n        // assert the old arrays have not been modified\n        TestAction::assert(\"arrayEquals(one, ['1'])\"),\n        TestAction::assert(\"arrayEquals(many, ['1', '0', '1'])\"),\n    ]);\n}\n\n#[test]\nfn some() {\n    run_test_actions([\n        TestAction::run_harness(),\n        TestAction::run(\"var array = [11, 23, 45];\"),\n        TestAction::assert(\"!array.some(e => e < 10)\"),\n        TestAction::assert(\"![].some(e => e < 10)\"),\n        TestAction::assert(\"array.some(e => e > 10)\"),\n        TestAction::assert(indoc! {r#\"\n                // Cases where callback mutates the array.\n                var appendArray = [1,2,3,4];\n                function appendingCallback(elem, index, arr) {\n                    arr.push('new');\n                    return elem !== \"new\";\n                }\n\n                appendArray.some(appendingCallback)\n            \"#}),\n        TestAction::assert(indoc! {r#\"\n                arrayEquals(\n                    appendArray,\n                    [1, 2, 3, 4, \"new\"]\n                )\n            \"#}),\n        TestAction::assert(indoc! {r#\"\n                var delArray = [1,2,3,4];\n                function deletingCallback(elem,index,arr) {\n                    arr.pop()\n                    return elem < 3;\n                }\n\n                delArray.some(deletingCallback)\n            \"#}),\n        TestAction::assert(indoc! {r#\"\n                arrayEquals(\n                    delArray,\n                    [1, 2, 3]\n                )\n            \"#}),\n    ]);\n}\n\n#[test]\nfn reduce() {\n    run_test_actions([\n        TestAction::run(indoc! {r#\"\n                var arr = [1, 2, 3, 4];\n                function add(acc, x) {\n                    return acc + x;\n                }\n\n                function addIdx(acc, _, idx) {\n                    return acc + idx;\n                }\n\n                function addLen(acc, _x, _idx, arr) {\n                    return acc + arr.length;\n                }\n\n                function addResize(acc, x, idx, arr) {\n                    if(idx == 0) {\n                        arr.length = 3;\n                    }\n                    return acc + x;\n                }\n                var delArray = [1, 2, 3, 4, 5];\n                delete delArray[0];\n                delete delArray[1];\n                delete delArray[3];\n\n            \"#}),\n        // empty array\n        TestAction::assert_eq(\"[].reduce(add, 0)\", 0),\n        // simple with initial value\n        TestAction::assert_eq(\"arr.reduce(add, 0)\", 10),\n        // without initial value\n        TestAction::assert_eq(\"arr.reduce(add)\", 10),\n        // with some items missing\n        TestAction::assert_eq(\"delArray.reduce(add, 0)\", 8),\n        // with index\n        TestAction::assert_eq(\"arr.reduce(addIdx, 0)\", 6),\n        // with array\n        TestAction::assert_eq(\"arr.reduce(addLen, 0)\", 16),\n        // resizing the array as reduce progresses\n        TestAction::assert_eq(\"arr.reduce(addResize, 0)\", 6),\n        // Empty array\n        TestAction::assert_native_error(\n            \"[].reduce((acc, x) => acc + x);\",\n            JsNativeErrorKind::Type,\n            \"Array.prototype.reduce: called on an empty array and with no initial value\",\n        ),\n        // Array with no defined elements\n        TestAction::assert_native_error(\n            indoc! {r#\"\n                var deleteArr = [0, 1];\n                delete deleteArr[0];\n                delete deleteArr[1];\n                deleteArr.reduce((acc, x) => acc + x);\n            \"#},\n            JsNativeErrorKind::Type,\n            \"Array.prototype.reduce: called on an empty array and with no initial value\",\n        ),\n        // No callback\n        TestAction::assert_native_error(\n            indoc! {r#\"\n                var someArr = [0, 1];\n                someArr.reduce('');\n            \"#},\n            JsNativeErrorKind::Type,\n            \"Array.prototype.reduce: callback function is not callable\",\n        ),\n    ]);\n}\n\n#[test]\nfn reduce_right() {\n    run_test_actions([\n        TestAction::run(indoc! {r#\"\n                var arr = [1, 2, 3, 4];\n                function sub(acc, x) {\n                    return acc - x;\n                }\n\n                function subIdx(acc, _, idx) {\n                    return acc - idx;\n                }\n\n                function subLen(acc, _x, _idx, arr) {\n                    return acc - arr.length;\n                }\n\n                function subResize(acc, x, idx, arr) {\n                    if(idx == arr.length - 1) {\n                        arr.length = 1;\n                    }\n                    return acc - x;\n                }\n                function subResize0(acc, x, idx, arr) {\n                    if(idx == arr.length - 2) {\n                        arr.length = 0;\n                    }\n                    return acc - x;\n                }\n                var delArray = [1, 2, 3, 4, 5];\n                delete delArray[0];\n                delete delArray[1];\n                delete delArray[3];\n            \"#}),\n        // empty array\n        TestAction::assert_eq(\"[].reduceRight(sub, 0)\", 0),\n        // simple with initial value\n        TestAction::assert_eq(\"arr.reduceRight(sub, 0)\", -10),\n        // without initial value\n        TestAction::assert_eq(\"arr.reduceRight(sub)\", -2),\n        // with some items missing\n        TestAction::assert_eq(\"delArray.reduceRight(sub, 0)\", -8),\n        // with index\n        TestAction::assert_eq(\"arr.reduceRight(subIdx)\", 1),\n        // with array\n        TestAction::assert_eq(\"arr.reduceRight(subLen)\", -8),\n        // resizing the array as reduce progresses\n        TestAction::assert_eq(\"arr.reduceRight(subResize, 0)\", -5),\n        // reset array\n        TestAction::run(\"arr = [1, 2, 3, 4];\"),\n        // resizing the array to 0 as reduce progresses\n        TestAction::assert_eq(\"arr.reduceRight(subResize0, 0)\", -7),\n        // Empty array\n        TestAction::assert_native_error(\n            \"[].reduceRight((acc, x) => acc + x);\",\n            JsNativeErrorKind::Type,\n            \"Array.prototype.reduceRight: called on an empty array and with no initial value\",\n        ),\n        // Array with no defined elements\n        TestAction::assert_native_error(\n            indoc! {r#\"\n                var deleteArr = [0, 1];\n                delete deleteArr[0];\n                delete deleteArr[1];\n                deleteArr.reduceRight((acc, x) => acc + x);\n            \"#},\n            JsNativeErrorKind::Type,\n            \"Array.prototype.reduceRight: called on an empty array and with no initial value\",\n        ),\n        // No callback\n        TestAction::assert_native_error(\n            indoc! {r#\"\n                var otherArr = [0, 1];\n                otherArr.reduceRight(\"\");\n            \"#},\n            JsNativeErrorKind::Type,\n            \"Array.prototype.reduceRight: callback function is not callable\",\n        ),\n    ]);\n}\n\n#[test]\nfn call_array_constructor_with_one_argument() {\n    run_test_actions([\n        TestAction::run_harness(),\n        TestAction::assert(\"arrayEquals(new Array(0), [])\"),\n        TestAction::assert(\"arrayEquals(new Array(5), [,,,,,])\"),\n        TestAction::assert(\"arrayEquals(new Array('Hello, world!'), ['Hello, world!'])\"),\n    ]);\n}\n\n#[test]\nfn array_values_simple() {\n    run_test_actions([\n        TestAction::run_harness(),\n        TestAction::assert(indoc! {r#\"\n                arrayEquals(\n                    Array.from([1, 2, 3].values()),\n                    [1, 2, 3]\n                )\n            \"#}),\n    ]);\n}\n\n#[test]\nfn array_keys_simple() {\n    run_test_actions([\n        TestAction::run_harness(),\n        TestAction::assert(indoc! {r#\"\n                arrayEquals(\n                    Array.from([1, 2, 3].keys()),\n                    [0, 1, 2]\n                )\n            \"#}),\n    ]);\n}\n\n#[test]\nfn array_entries_simple() {\n    run_test_actions([\n        TestAction::run_harness(),\n        TestAction::assert(indoc! {r#\"\n                arrayEquals(\n                    Array.from([1, 2, 3].entries()),\n                    [\n                        [0, 1],\n                        [1, 2],\n                        [2, 3]\n                    ]\n                )\n            \"#}),\n    ]);\n}\n\n#[test]\nfn array_values_empty() {\n    run_test_actions([\n        TestAction::run_harness(),\n        TestAction::assert(indoc! {r#\"\n                arrayEquals(\n                    Array.from([].values()),\n                    []\n                )\n            \"#}),\n    ]);\n}\n\n#[test]\nfn array_values_sparse() {\n    run_test_actions([\n        TestAction::run_harness(),\n        TestAction::assert(indoc! {r#\"\n                var array = Array();\n                array[3] = 5;\n                arrayEquals(\n                    Array.from(array.values()),\n                    [undefined, undefined, undefined, 5]\n                )\n            \"#}),\n    ]);\n}\n\n#[test]\nfn array_symbol_iterator() {\n    run_test_actions([\n        TestAction::run_harness(),\n        TestAction::assert(indoc! {r#\"\n                arrayEquals(\n                    Array.from([1, 2, 3][Symbol.iterator]()),\n                    [1, 2, 3]\n                )\n            \"#}),\n    ]);\n}\n\n#[test]\nfn array_values_symbol_iterator() {\n    run_test_actions([TestAction::assert(indoc! {r#\"\n                var iterator = [1, 2, 3].values();\n                iterator === iterator[Symbol.iterator]();\n            \"#})]);\n}\n\n#[test]\nfn array_spread_arrays() {\n    run_test_actions([\n        TestAction::run_harness(),\n        TestAction::assert(indoc! {r#\"\n                arrayEquals(\n                    [1, ...[2, 3]],\n                    [1, 2, 3]\n                )\n            \"#}),\n    ]);\n}\n\n#[test]\nfn array_spread_non_iterable() {\n    run_test_actions([TestAction::assert_native_error(\n        \"const array2 = [...5];\",\n        JsNativeErrorKind::Type,\n        \"value with type `number` is not iterable\",\n    )]);\n}\n\n#[test]\nfn get_relative_start() {\n    #[track_caller]\n    fn assert(context: &mut Context, arg: Option<&JsValue>, len: u64, expected: u64) {\n        const UNDEFINED: &JsValue = &JsValue::undefined();\n        let arg = arg.unwrap_or(UNDEFINED);\n        assert_eq!(\n            Array::get_relative_start(context, arg, len).unwrap(),\n            expected\n        );\n    }\n    run_test_actions([TestAction::inspect_context(|ctx| {\n        assert(ctx, None, 10, 0);\n        assert(ctx, Some(&JsValue::undefined()), 10, 0);\n        assert(ctx, Some(&JsValue::new(f64::NEG_INFINITY)), 10, 0);\n        assert(ctx, Some(&JsValue::new(f64::INFINITY)), 10, 10);\n        assert(ctx, Some(&JsValue::new(-1)), 10, 9);\n        assert(ctx, Some(&JsValue::new(1)), 10, 1);\n        assert(ctx, Some(&JsValue::new(-11)), 10, 0);\n        assert(ctx, Some(&JsValue::new(11)), 10, 10);\n        assert(ctx, Some(&JsValue::new(f64::MIN)), 10, 0);\n        assert(ctx, Some(&JsValue::new(Number::MIN_SAFE_INTEGER)), 10, 0);\n        assert(ctx, Some(&JsValue::new(f64::MAX)), 10, 10);\n        // This test is relevant only on 32-bit archs (where usize == u32 thus `len` is u32)\n        assert(ctx, Some(&JsValue::new(Number::MAX_SAFE_INTEGER)), 10, 10);\n    })]);\n}\n\n#[test]\nfn get_relative_end() {\n    #[track_caller]\n    fn assert(context: &mut Context, arg: Option<&JsValue>, len: u64, expected: u64) {\n        const UNDEFINED: &JsValue = &JsValue::undefined();\n        let arg = arg.unwrap_or(UNDEFINED);\n        assert_eq!(\n            Array::get_relative_end(context, arg, len).unwrap(),\n            expected\n        );\n    }\n    run_test_actions([TestAction::inspect_context(|ctx| {\n        assert(ctx, None, 10, 10);\n        assert(ctx, Some(&JsValue::undefined()), 10, 10);\n        assert(ctx, Some(&JsValue::new(f64::NEG_INFINITY)), 10, 0);\n        assert(ctx, Some(&JsValue::new(f64::INFINITY)), 10, 10);\n        assert(ctx, Some(&JsValue::new(-1)), 10, 9);\n        assert(ctx, Some(&JsValue::new(1)), 10, 1);\n        assert(ctx, Some(&JsValue::new(-11)), 10, 0);\n        assert(ctx, Some(&JsValue::new(11)), 10, 10);\n        assert(ctx, Some(&JsValue::new(f64::MIN)), 10, 0);\n        assert(ctx, Some(&JsValue::new(Number::MIN_SAFE_INTEGER)), 10, 0);\n        assert(ctx, Some(&JsValue::new(f64::MAX)), 10, 10);\n        // This test is relevant only on 32-bit archs (where usize == u32 thus `len` is u32)\n        assert(ctx, Some(&JsValue::new(Number::MAX_SAFE_INTEGER)), 10, 10);\n    })]);\n}\n\n#[test]\nfn array_length_is_not_enumerable() {\n    run_test_actions([TestAction::assert(\n        \"!Object.getOwnPropertyDescriptor([], 'length').enumerable\",\n    )]);\n}\n\n#[test]\nfn array_sort() {\n    run_test_actions([\n        TestAction::run_harness(),\n        TestAction::run(\"let arr = ['80', '9', '700', 40, 1, 5, 200];\"),\n        TestAction::assert(indoc! {r#\"\n                arrayEquals(\n                    arr.sort(),\n                    [1, 200, 40, 5, \"700\", \"80\", \"9\"]\n                )\n            \"#}),\n        TestAction::assert(indoc! {r#\"\n                arrayEquals(\n                    arr.sort((a, b) => a - b),\n                    [1, 5, \"9\", 40, \"80\", 200, \"700\"]\n                )\n            \"#}),\n    ]);\n}\n\n#[test]\nfn array_of_neg_zero() {\n    run_test_actions([\n        TestAction::run(\"let arr = [-0, -0, -0, -0];\"),\n        // Assert the parity of all items of the list.\n        TestAction::assert(\"arr.every(x => (1/x) === -Infinity)\"),\n    ]);\n}\n"
  },
  {
    "path": "core/engine/src/builtins/array_buffer/mod.rs",
    "content": "//! Boa's implementation of ECMAScript's global `ArrayBuffer` and `SharedArrayBuffer` objects\n//!\n//! More information:\n//!  - [ECMAScript reference][spec]\n//!  - [MDN documentation][mdn]\n//!\n//! [spec]: https://tc39.es/ecma262/#sec-arraybuffer-objects\n//! [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/ArrayBuffer\n\n#![deny(unsafe_op_in_unsafe_fn)]\n#![deny(clippy::undocumented_unsafe_blocks)]\n\npub(crate) mod shared;\npub(crate) mod utils;\n\n#[cfg(test)]\nmod tests;\n\nuse std::ops::{Deref, DerefMut};\n\nuse aligned_vec::{ABox, AVec, ConstAlign};\npub use shared::SharedArrayBuffer;\nuse std::sync::atomic::Ordering;\n\nuse crate::{\n    Context, JsArgs, JsData, JsResult, JsString, JsValue,\n    builtins::BuiltInObject,\n    context::intrinsics::{Intrinsics, StandardConstructor, StandardConstructors},\n    error::JsNativeError,\n    js_string,\n    object::{JsObject, internal_methods::get_prototype_from_constructor},\n    property::Attribute,\n    realm::Realm,\n    string::StaticJsStrings,\n    symbol::JsSymbol,\n};\nuse boa_gc::{Finalize, GcRef, GcRefMut, Trace};\n\nuse self::utils::{SliceRef, SliceRefMut};\n\nuse super::{\n    Array, BuiltInBuilder, BuiltInConstructor, DataView, IntrinsicObject, typed_array::TypedArray,\n};\n\n/// `Vec`, but aligned to a 64-bit memory address.\npub type AlignedVec<T> = AVec<T, ConstAlign<64>>;\npub(crate) type AlignedBox<T> = ABox<T, ConstAlign<64>>;\n\n#[derive(Debug, Clone, Copy)]\npub(crate) enum BufferRef<B, S> {\n    Buffer(B),\n    SharedBuffer(S),\n}\n\nimpl<B, S> BufferRef<B, S>\nwhere\n    B: Deref<Target = ArrayBuffer>,\n    S: Deref<Target = SharedArrayBuffer>,\n{\n    /// Gets the inner data of the buffer.\n    pub(crate) fn bytes(&self, ordering: Ordering) -> Option<SliceRef<'_>> {\n        match self {\n            Self::Buffer(buf) => buf.deref().bytes().map(SliceRef::Slice),\n            Self::SharedBuffer(buf) => Some(SliceRef::AtomicSlice(buf.deref().bytes(ordering))),\n        }\n    }\n\n    /// Gets the inner data of the buffer without accessing the current atomic length.\n    ///\n    /// Returns `None` if the buffer is detached or if the provided `len` is bigger than\n    /// the allocated buffer.\n    #[track_caller]\n    pub(crate) fn bytes_with_len(&self, len: usize) -> Option<SliceRef<'_>> {\n        match self {\n            Self::Buffer(buf) => buf.deref().bytes_with_len(len).map(SliceRef::Slice),\n            Self::SharedBuffer(buf) => Some(SliceRef::AtomicSlice(buf.deref().bytes_with_len(len))),\n        }\n    }\n\n    pub(crate) fn is_fixed_len(&self) -> bool {\n        match self {\n            Self::Buffer(buf) => buf.is_fixed_len(),\n            Self::SharedBuffer(buf) => buf.is_fixed_len(),\n        }\n    }\n}\n\n#[derive(Debug)]\npub(crate) enum BufferRefMut<B, S> {\n    Buffer(B),\n    SharedBuffer(S),\n}\n\nimpl<B, S> BufferRefMut<B, S>\nwhere\n    B: DerefMut<Target = ArrayBuffer>,\n    S: DerefMut<Target = SharedArrayBuffer>,\n{\n    pub(crate) fn bytes(&mut self, ordering: Ordering) -> Option<SliceRefMut<'_>> {\n        match self {\n            Self::Buffer(buf) => buf.deref_mut().bytes_mut().map(SliceRefMut::Slice),\n            Self::SharedBuffer(buf) => {\n                Some(SliceRefMut::AtomicSlice(buf.deref_mut().bytes(ordering)))\n            }\n        }\n    }\n\n    /// Gets the mutable inner data of the buffer without accessing the current atomic length.\n    ///\n    /// Returns `None` if the buffer is detached or if the provided `len` is bigger than\n    /// the allocated buffer.\n    pub(crate) fn bytes_with_len(&mut self, len: usize) -> Option<SliceRefMut<'_>> {\n        match self {\n            Self::Buffer(buf) => buf\n                .deref_mut()\n                .bytes_with_len_mut(len)\n                .map(SliceRefMut::Slice),\n            Self::SharedBuffer(buf) => Some(SliceRefMut::AtomicSlice(\n                buf.deref_mut().bytes_with_len(len),\n            )),\n        }\n    }\n}\n\n/// A `JsObject` containing a bytes buffer as its inner data.\n#[derive(Debug, Clone, Trace, Finalize)]\n#[boa_gc(unsafe_no_drop)]\npub(crate) enum BufferObject {\n    Buffer(JsObject<ArrayBuffer>),\n    SharedBuffer(JsObject<SharedArrayBuffer>),\n}\n\nimpl From<BufferObject> for JsObject {\n    fn from(value: BufferObject) -> Self {\n        match value {\n            BufferObject::Buffer(buf) => buf.upcast(),\n            BufferObject::SharedBuffer(buf) => buf.upcast(),\n        }\n    }\n}\n\nimpl From<BufferObject> for JsValue {\n    fn from(value: BufferObject) -> Self {\n        JsValue::from(JsObject::from(value))\n    }\n}\n\nimpl BufferObject {\n    /// Gets the buffer data of the object.\n    #[inline]\n    #[must_use]\n    pub(crate) fn as_buffer(\n        &self,\n    ) -> BufferRef<GcRef<'_, ArrayBuffer>, GcRef<'_, SharedArrayBuffer>> {\n        match self {\n            Self::Buffer(buf) => BufferRef::Buffer(GcRef::map(buf.borrow(), |o| o.data())),\n            Self::SharedBuffer(buf) => {\n                BufferRef::SharedBuffer(GcRef::map(buf.borrow(), |o| o.data()))\n            }\n        }\n    }\n\n    /// Gets the mutable buffer data of the object\n    #[inline]\n    #[track_caller]\n    pub(crate) fn as_buffer_mut(\n        &self,\n    ) -> BufferRefMut<GcRefMut<'_, ArrayBuffer>, GcRefMut<'_, SharedArrayBuffer>> {\n        match self {\n            Self::Buffer(buf) => {\n                BufferRefMut::Buffer(GcRefMut::map(buf.borrow_mut(), |o| o.data_mut()))\n            }\n            Self::SharedBuffer(buf) => {\n                BufferRefMut::SharedBuffer(GcRefMut::map(buf.borrow_mut(), |o| o.data_mut()))\n            }\n        }\n    }\n\n    /// Returns `true` if the buffer objects point to the same buffer.\n    #[inline]\n    #[track_caller]\n    pub(crate) fn equals(lhs: &Self, rhs: &Self) -> bool {\n        match (lhs, rhs) {\n            (BufferObject::Buffer(lhs), BufferObject::Buffer(rhs)) => JsObject::equals(lhs, rhs),\n            (BufferObject::SharedBuffer(lhs), BufferObject::SharedBuffer(rhs)) => {\n                if JsObject::equals(lhs, rhs) {\n                    return true;\n                }\n\n                let lhs = lhs.borrow();\n                let rhs = rhs.borrow();\n\n                std::ptr::eq(lhs.data().as_ptr(), rhs.data().as_ptr())\n            }\n            _ => false,\n        }\n    }\n}\n\n/// The internal representation of an `ArrayBuffer` object.\n#[derive(Debug, Clone, Trace, Finalize, JsData)]\npub struct ArrayBuffer {\n    /// The `[[ArrayBufferData]]` internal slot.\n    #[unsafe_ignore_trace]\n    data: Option<AlignedVec<u8>>,\n\n    /// The `[[ArrayBufferMaxByteLength]]` internal slot.\n    max_byte_len: Option<u64>,\n\n    /// The `[[ArrayBufferDetachKey]]` internal slot.\n    detach_key: JsValue,\n}\n\nimpl ArrayBuffer {\n    pub(crate) fn from_data(data: AlignedVec<u8>, detach_key: JsValue) -> Self {\n        Self {\n            data: Some(data),\n            max_byte_len: None,\n            detach_key,\n        }\n    }\n\n    pub(crate) fn len(&self) -> usize {\n        self.data.as_ref().map_or(0, AlignedVec::len)\n    }\n\n    pub(crate) fn bytes(&self) -> Option<&[u8]> {\n        self.data.as_deref()\n    }\n\n    pub(crate) fn bytes_mut(&mut self) -> Option<&mut [u8]> {\n        self.data.as_deref_mut()\n    }\n\n    pub(crate) fn vec_mut(&mut self) -> Option<&mut AlignedVec<u8>> {\n        self.data.as_mut()\n    }\n\n    /// Sets the maximum byte length of the buffer, returning the previous value if present.\n    pub(crate) fn set_max_byte_length(&mut self, max_byte_len: u64) -> Option<u64> {\n        self.max_byte_len.replace(max_byte_len)\n    }\n\n    /// Gets the inner bytes of the buffer without accessing the current atomic length.\n    #[track_caller]\n    pub(crate) fn bytes_with_len(&self, len: usize) -> Option<&[u8]> {\n        if let Some(s) = self.data.as_deref() {\n            Some(&s[..len])\n        } else {\n            None\n        }\n    }\n\n    /// Gets the mutable inner bytes of the buffer without accessing the current atomic length.\n    #[track_caller]\n    pub(crate) fn bytes_with_len_mut(&mut self, len: usize) -> Option<&mut [u8]> {\n        if let Some(s) = self.data.as_deref_mut() {\n            Some(&mut s[..len])\n        } else {\n            None\n        }\n    }\n\n    /// Gets the underlying vector for this buffer.\n    #[must_use]\n    pub fn data(&self) -> Option<&[u8]> {\n        self.data.as_deref()\n    }\n\n    /// Resizes the buffer to the new size, clamped to the maximum byte length if present.\n    pub fn resize(&mut self, new_byte_length: u64) -> JsResult<()> {\n        let Some(max_byte_len) = self.max_byte_len else {\n            return Err(JsNativeError::typ()\n                .with_message(\"ArrayBuffer.resize: cannot resize a fixed-length buffer\")\n                .into());\n        };\n\n        let Some(buf) = self.vec_mut() else {\n            return Err(JsNativeError::typ()\n                .with_message(\"ArrayBuffer.resize: cannot resize a detached buffer\")\n                .into());\n        };\n\n        if new_byte_length > max_byte_len {\n            return Err(JsNativeError::range()\n                .with_message(\n                    \"ArrayBuffer.resize: new byte length exceeds buffer's maximum byte length\",\n                )\n                .into());\n        }\n\n        buf.resize(new_byte_length as usize, 0);\n        Ok(())\n    }\n\n    /// Detaches the inner data of this `ArrayBuffer`, returning the original buffer if still\n    /// present.\n    ///\n    /// # Errors\n    ///\n    /// Throws an error if the provided detach key is invalid.\n    pub fn detach(&mut self, key: &JsValue) -> JsResult<Option<AlignedVec<u8>>> {\n        if !JsValue::same_value(&self.detach_key, key) {\n            return Err(JsNativeError::typ()\n                .with_message(\"Cannot detach array buffer with different key\")\n                .into());\n        }\n\n        Ok(self.data.take())\n    }\n\n    /// `IsDetachedBuffer ( arrayBuffer )`\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-isdetachedbuffer\n    pub(crate) const fn is_detached(&self) -> bool {\n        // 1. If arrayBuffer.[[ArrayBufferData]] is null, return true.\n        // 2. Return false.\n        self.data.is_none()\n    }\n\n    pub(crate) fn is_fixed_len(&self) -> bool {\n        self.max_byte_len.is_none()\n    }\n}\n\nimpl IntrinsicObject for ArrayBuffer {\n    fn init(realm: &Realm) {\n        let flag_attributes = Attribute::CONFIGURABLE | Attribute::NON_ENUMERABLE;\n\n        let get_species = BuiltInBuilder::callable(realm, Self::get_species)\n            .name(js_string!(\"get [Symbol.species]\"))\n            .build();\n\n        let get_byte_length = BuiltInBuilder::callable(realm, Self::get_byte_length)\n            .name(js_string!(\"get byteLength\"))\n            .build();\n\n        let get_resizable = BuiltInBuilder::callable(realm, Self::get_resizable)\n            .name(js_string!(\"get resizable\"))\n            .build();\n\n        let get_max_byte_length = BuiltInBuilder::callable(realm, Self::get_max_byte_length)\n            .name(js_string!(\"get maxByteLength\"))\n            .build();\n\n        #[cfg(feature = \"experimental\")]\n        let get_detached = BuiltInBuilder::callable(realm, Self::get_detached)\n            .name(js_string!(\"get detached\"))\n            .build();\n\n        let builder = BuiltInBuilder::from_standard_constructor::<Self>(realm)\n            .static_accessor(\n                JsSymbol::species(),\n                Some(get_species),\n                None,\n                Attribute::CONFIGURABLE,\n            )\n            .static_method(Self::is_view, js_string!(\"isView\"), 1)\n            .accessor(\n                js_string!(\"byteLength\"),\n                Some(get_byte_length),\n                None,\n                flag_attributes,\n            )\n            .accessor(\n                js_string!(\"resizable\"),\n                Some(get_resizable),\n                None,\n                flag_attributes,\n            )\n            .accessor(\n                js_string!(\"maxByteLength\"),\n                Some(get_max_byte_length),\n                None,\n                flag_attributes,\n            )\n            .method(Self::js_resize, js_string!(\"resize\"), 1)\n            .method(Self::slice, js_string!(\"slice\"), 2)\n            .property(\n                JsSymbol::to_string_tag(),\n                Self::NAME,\n                Attribute::READONLY | Attribute::NON_ENUMERABLE | Attribute::CONFIGURABLE,\n            );\n\n        #[cfg(feature = \"experimental\")]\n        let builder = builder\n            .accessor(\n                js_string!(\"detached\"),\n                Some(get_detached),\n                None,\n                flag_attributes,\n            )\n            .method(Self::transfer::<false>, js_string!(\"transfer\"), 0)\n            .method(\n                Self::transfer::<true>,\n                js_string!(\"transferToFixedLength\"),\n                0,\n            );\n\n        builder.build();\n    }\n\n    fn get(intrinsics: &Intrinsics) -> JsObject {\n        Self::STANDARD_CONSTRUCTOR(intrinsics.constructors()).constructor()\n    }\n}\n\nimpl BuiltInObject for ArrayBuffer {\n    const NAME: JsString = StaticJsStrings::ARRAY_BUFFER;\n}\n\nimpl BuiltInConstructor for ArrayBuffer {\n    const PROTOTYPE_STORAGE_SLOTS: usize = 13;\n    const CONSTRUCTOR_STORAGE_SLOTS: usize = 3;\n    const CONSTRUCTOR_ARGUMENTS: usize = 1;\n\n    const STANDARD_CONSTRUCTOR: fn(&StandardConstructors) -> &StandardConstructor =\n        StandardConstructors::array_buffer;\n\n    /// `ArrayBuffer ( length )`\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-arraybuffer-length\n    fn constructor(\n        new_target: &JsValue,\n        args: &[JsValue],\n        context: &mut Context,\n    ) -> JsResult<JsValue> {\n        // 1. If NewTarget is undefined, throw a TypeError exception.\n        if new_target.is_undefined() {\n            return Err(JsNativeError::typ()\n                .with_message(\"ArrayBuffer.constructor called with undefined new target\")\n                .into());\n        }\n\n        // 2. Let byteLength be ? ToIndex(length).\n        let byte_len = args.get_or_undefined(0).to_index(context)?;\n\n        // 3. Let requestedMaxByteLength be ? GetArrayBufferMaxByteLengthOption(options).\n        let max_byte_len = get_max_byte_len(args.get_or_undefined(1), context)?;\n\n        // 4. Return ? AllocateArrayBuffer(NewTarget, byteLength, requestedMaxByteLength).\n        Ok(Self::allocate(new_target, byte_len, max_byte_len, context)?\n            .upcast()\n            .into())\n    }\n}\n\nimpl ArrayBuffer {\n    /// `ArrayBuffer.isView ( arg )`\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-arraybuffer.isview\n    #[allow(clippy::unnecessary_wraps)]\n    fn is_view(_: &JsValue, args: &[JsValue], _context: &mut Context) -> JsResult<JsValue> {\n        // 1. If Type(arg) is not Object, return false.\n        // 2. If arg has a [[ViewedArrayBuffer]] internal slot, return true.\n        // 3. Return false.\n        Ok(args\n            .get_or_undefined(0)\n            .as_object()\n            .is_some_and(|obj| obj.is::<TypedArray>() || obj.is::<DataView>())\n            .into())\n    }\n\n    /// `get ArrayBuffer [ @@species ]`\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-get-arraybuffer-@@species\n    #[allow(clippy::unnecessary_wraps)]\n    fn get_species(this: &JsValue, _: &[JsValue], _: &mut Context) -> JsResult<JsValue> {\n        // 1. Return the this value.\n        Ok(this.clone())\n    }\n\n    /// `get ArrayBuffer.prototype.byteLength`\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-get-arraybuffer.prototype.bytelength\n    pub(crate) fn get_byte_length(\n        this: &JsValue,\n        _args: &[JsValue],\n        _: &mut Context,\n    ) -> JsResult<JsValue> {\n        // 1. Let O be the this value.\n        // 2. Perform ? RequireInternalSlot(O, [[ArrayBufferData]]).\n        // 3. If IsSharedArrayBuffer(O) is true, throw a TypeError exception.\n        let object = this.as_object();\n        let buf = object\n            .as_ref()\n            .and_then(JsObject::downcast_ref::<Self>)\n            .ok_or_else(|| {\n                JsNativeError::typ()\n                    .with_message(\"get ArrayBuffer.prototype.byteLength called with invalid `this`\")\n            })?;\n\n        // 4. If IsDetachedBuffer(O) is true, return +0𝔽.\n        // 5. Let length be O.[[ArrayBufferByteLength]].\n        // 6. Return 𝔽(length).\n        Ok(buf.len().into())\n    }\n\n    /// [`get ArrayBuffer.prototype.maxByteLength`][spec].\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-get-arraybuffer.prototype.maxbytelength\n    pub(crate) fn get_max_byte_length(\n        this: &JsValue,\n        _args: &[JsValue],\n        _context: &mut Context,\n    ) -> JsResult<JsValue> {\n        // 1. Let O be the this value.\n        // 2. Perform ? RequireInternalSlot(O, [[ArrayBufferData]]).\n        // 3. If IsSharedArrayBuffer(O) is true, throw a TypeError exception.\n        let object = this.as_object();\n        let buf = object\n            .as_ref()\n            .and_then(JsObject::downcast_ref::<Self>)\n            .ok_or_else(|| {\n                JsNativeError::typ().with_message(\n                    \"get ArrayBuffer.prototype.maxByteLength called with invalid `this`\",\n                )\n            })?;\n\n        // 4. If IsDetachedBuffer(O) is true, return +0𝔽.\n        let Some(data) = buf.bytes() else {\n            return Ok(JsValue::from(0));\n        };\n\n        // 5. If IsFixedLengthArrayBuffer(O) is true, then\n        //     a. Let length be O.[[ArrayBufferByteLength]].\n        // 6. Else,\n        //     a. Let length be O.[[ArrayBufferMaxByteLength]].\n        // 7. Return 𝔽(length).\n        Ok(buf.max_byte_len.unwrap_or(data.len() as u64).into())\n    }\n\n    /// [`get ArrayBuffer.prototype.resizable`][spec].\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-get-arraybuffer.prototype.resizable\n    pub(crate) fn get_resizable(\n        this: &JsValue,\n        _args: &[JsValue],\n        _context: &mut Context,\n    ) -> JsResult<JsValue> {\n        // 1. Let O be the this value.\n        // 2. Perform ? RequireInternalSlot(O, [[ArrayBufferData]]).\n        // 3. If IsSharedArrayBuffer(O) is true, throw a TypeError exception.\n        let object = this.as_object();\n        let buf = object\n            .as_ref()\n            .and_then(JsObject::downcast_ref::<Self>)\n            .ok_or_else(|| {\n                JsNativeError::typ()\n                    .with_message(\"get ArrayBuffer.prototype.resizable called with invalid `this`\")\n            })?;\n\n        // 4. If IsFixedLengthArrayBuffer(O) is false, return true; otherwise return false.\n        Ok(JsValue::from(!buf.is_fixed_len()))\n    }\n\n    /// [`get ArrayBuffer.prototype.detached`][spec].\n    ///\n    /// [spec]: https://tc39.es/proposal-arraybuffer-transfer/#sec-get-arraybuffer.prototype.detached\n    #[cfg(feature = \"experimental\")]\n    fn get_detached(\n        this: &JsValue,\n        _args: &[JsValue],\n        _context: &mut Context,\n    ) -> JsResult<JsValue> {\n        // 1. Let O be the this value.\n        // 2. Perform ? RequireInternalSlot(O, [[ArrayBufferData]]).\n        // 3. If IsSharedArrayBuffer(O) is true, throw a TypeError exception.\n        let object = this.as_object();\n        let buf = object\n            .as_ref()\n            .and_then(JsObject::downcast_ref::<Self>)\n            .ok_or_else(|| {\n                JsNativeError::typ()\n                    .with_message(\"get ArrayBuffer.prototype.detached called with invalid `this`\")\n            })?;\n\n        // 4. Return IsDetachedBuffer(O).\n        Ok(buf.is_detached().into())\n    }\n\n    /// [`ArrayBuffer.prototype.resize ( newLength )`][spec].\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-arraybuffer.prototype.resize\n    pub(crate) fn js_resize(\n        this: &JsValue,\n        args: &[JsValue],\n        context: &mut Context,\n    ) -> JsResult<JsValue> {\n        // 1. Let O be the this value.\n        // 2. Perform ? RequireInternalSlot(O, [[ArrayBufferMaxByteLength]]).\n        // 3. If IsSharedArrayBuffer(O) is true, throw a TypeError exception.\n        let buf = this\n            .as_object()\n            .and_then(|o| o.clone().downcast::<Self>().ok())\n            .ok_or_else(|| {\n                JsNativeError::typ()\n                    .with_message(\"ArrayBuffer.prototype.resize called with invalid `this`\")\n            })?;\n\n        // 4. Let newByteLength be ? ToIndex(newLength).\n        let new_byte_length = args.get_or_undefined(0).to_index(context)?;\n\n        // These steps are performed in the `Self::resize` method.\n        // 5. If IsDetachedBuffer(O) is true, throw a TypeError exception.\n        // 6. If newByteLength > O.[[ArrayBufferMaxByteLength]], throw a RangeError exception.\n\n        // TODO: 7. Let hostHandled be ? HostResizeArrayBuffer(O, newByteLength).\n        // 8. If hostHandled is handled, return undefined.\n        // Used in engines to handle Wasm buffers in a special way, but we don't\n        // have a Wasm interpreter in place yet.\n\n        // 9. Let oldBlock be O.[[ArrayBufferData]].\n        // 10. Let newBlock be ? CreateByteDataBlock(newByteLength).\n        // 11. Let copyLength be min(newByteLength, O.[[ArrayBufferByteLength]]).\n        // 12. Perform CopyDataBlockBytes(newBlock, 0, oldBlock, 0, copyLength).\n        // 13. NOTE: Neither creation of the new Data Block nor copying from the old Data Block are observable.\n        //     Implementations may implement this method as in-place growth or shrinkage.\n        // 14. Set O.[[ArrayBufferData]] to newBlock.\n        // 15. Set O.[[ArrayBufferByteLength]] to newByteLength.\n        buf.borrow_mut().data_mut().resize(new_byte_length)?;\n\n        // 16. Return undefined.\n        Ok(JsValue::undefined())\n    }\n\n    /// `ArrayBuffer.prototype.slice ( start, end )`\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-arraybuffer.prototype.slice\n    fn slice(this: &JsValue, args: &[JsValue], context: &mut Context) -> JsResult<JsValue> {\n        // 1. Let O be the this value.\n        // 2. Perform ? RequireInternalSlot(O, [[ArrayBufferData]]).\n        // 3. If IsSharedArrayBuffer(O) is true, throw a TypeError exception.\n        let buf = this\n            .as_object()\n            .and_then(|o| o.clone().downcast::<Self>().ok())\n            .ok_or_else(|| {\n                JsNativeError::typ()\n                    .with_message(\"ArrayBuffer.slice called with invalid `this` value\")\n            })?;\n\n        let len = {\n            let buf = buf.borrow();\n            // 4. If IsDetachedBuffer(O) is true, throw a TypeError exception.\n            if buf.data().is_detached() {\n                return Err(JsNativeError::typ()\n                    .with_message(\"ArrayBuffer.slice called with detached buffer\")\n                    .into());\n            }\n            // 5. Let len be O.[[ArrayBufferByteLength]].\n            buf.data().len() as u64\n        };\n\n        // 6. Let relativeStart be ? ToIntegerOrInfinity(start).\n        // 7. If relativeStart = -∞, let first be 0.\n        // 8. Else if relativeStart < 0, let first be max(len + relativeStart, 0).\n        // 9. Else, let first be min(relativeStart, len).\n        let first = Array::get_relative_start(context, args.get_or_undefined(0), len)?;\n\n        // 10. If end is undefined, let relativeEnd be len; else let relativeEnd be ? ToIntegerOrInfinity(end).\n        // 11. If relativeEnd = -∞, let final be 0.\n        // 12. Else if relativeEnd < 0, let final be max(len + relativeEnd, 0).\n        // 13. Else, let final be min(relativeEnd, len).\n        let final_ = Array::get_relative_end(context, args.get_or_undefined(1), len)?;\n\n        // 14. Let newLen be max(final - first, 0).\n        let new_len = final_.saturating_sub(first);\n\n        // 15. Let ctor be ? SpeciesConstructor(O, %ArrayBuffer%).\n        let ctor = buf\n            .clone()\n            .upcast()\n            .species_constructor(StandardConstructors::array_buffer, context)?;\n\n        // 16. Let new be ? Construct(ctor, « 𝔽(newLen) »).\n        let new = ctor.construct(&[new_len.into()], Some(&ctor), context)?;\n\n        // 17. Perform ? RequireInternalSlot(new, [[ArrayBufferData]]).\n        // 18. If IsSharedArrayBuffer(new) is true, throw a TypeError exception.\n        let Ok(new) = new.downcast::<Self>() else {\n            return Err(JsNativeError::typ()\n                .with_message(\"ArrayBuffer constructor returned invalid object\")\n                .into());\n        };\n\n        // 20. If SameValue(new, O) is true, throw a TypeError exception.\n        if JsObject::equals(&buf, &new) {\n            return Err(JsNativeError::typ()\n                .with_message(\"new ArrayBuffer is the same as this ArrayBuffer\")\n                .into());\n        }\n\n        {\n            // 19. If IsDetachedBuffer(new) is true, throw a TypeError exception.\n            // 25. Let toBuf be new.[[ArrayBufferData]].\n            let mut new = new.borrow_mut();\n            let Some(to_buf) = new.data_mut().bytes_mut() else {\n                return Err(JsNativeError::typ()\n                    .with_message(\"ArrayBuffer constructor returned detached ArrayBuffer\")\n                    .into());\n            };\n\n            // 21. If new.[[ArrayBufferByteLength]] < newLen, throw a TypeError exception.\n            if (to_buf.len() as u64) < new_len {\n                return Err(JsNativeError::typ()\n                    .with_message(\"new ArrayBuffer length too small\")\n                    .into());\n            }\n\n            // 22. NOTE: Side-effects of the above steps may have detached O.\n            // 23. If IsDetachedBuffer(O) is true, throw a TypeError exception.\n            // 24. Let fromBuf be O.[[ArrayBufferData]].\n            let buf = buf.borrow();\n            let Some(from_buf) = buf.data().bytes() else {\n                return Err(JsNativeError::typ()\n                    .with_message(\"ArrayBuffer detached while ArrayBuffer.slice was running\")\n                    .into());\n            };\n\n            // 26. Perform CopyDataBlockBytes(toBuf, 0, fromBuf, first, newLen).\n            let first = first as usize;\n            let new_len = new_len as usize;\n            to_buf[..new_len].copy_from_slice(&from_buf[first..first + new_len]);\n        }\n\n        // 27. Return new.\n        Ok(new.upcast().into())\n    }\n\n    /// [`ArrayBuffer.prototype.transfer ( [ newLength ] )`][transfer] and\n    /// [`ArrayBuffer.prototype.transferToFixedLength ( [ newLength ] )`][transferFL]\n    ///\n    /// [transfer]: https://tc39.es/proposal-arraybuffer-transfer/#sec-arraybuffer.prototype.transfer\n    /// [transferFL]: https://tc39.es/proposal-arraybuffer-transfer/#sec-arraybuffer.prototype.transfertofixedlength\n    #[cfg(feature = \"experimental\")]\n    fn transfer<const TO_FIXED_LENGTH: bool>(\n        this: &JsValue,\n        args: &[JsValue],\n        context: &mut Context,\n    ) -> JsResult<JsValue> {\n        // 1. Let O be the this value.\n        // 2. Return ? ArrayBufferCopyAndDetach(O, newLength, preserve-resizability).\n\n        // Abstract operation `ArrayBufferCopyAndDetach ( arrayBuffer, newLength, preserveResizability )`\n        // https://tc39.es/proposal-arraybuffer-transfer/#sec-arraybuffercopyanddetach\n\n        let new_length = args.get_or_undefined(0);\n\n        // 1. Perform ? RequireInternalSlot(arrayBuffer, [[ArrayBufferData]]).\n        // 2. If IsSharedArrayBuffer(arrayBuffer) is true, throw a TypeError exception.\n        let buf = this\n            .as_object()\n            .and_then(|o| o.clone().downcast::<Self>().ok())\n            .ok_or_else(|| {\n                JsNativeError::typ().with_message(if TO_FIXED_LENGTH {\n                    \"ArrayBuffer.prototype.transferToFixedLength called with invalid `this`\"\n                } else {\n                    \"ArrayBuffer.prototype.transfer called with invalid `this`\"\n                })\n            })?;\n\n        // 3. If newLength is undefined, then\n        let new_len = if new_length.is_undefined() {\n            // a. Let newByteLength be arrayBuffer.[[ArrayBufferByteLength]].\n            buf.borrow().data().len() as u64\n        } else {\n            // 4. Else,\n            //     a. Let newByteLength be ? ToIndex(newLength).\n            new_length.to_index(context)?\n        };\n\n        // 5. If IsDetachedBuffer(arrayBuffer) is true, throw a TypeError exception.\n        let Some(mut bytes) = buf.borrow_mut().data_mut().data.take() else {\n            return Err(JsNativeError::typ()\n                .with_message(\"cannot transfer a detached buffer\")\n                .into());\n        };\n\n        // 6. If preserveResizability is preserve-resizability and IsResizableArrayBuffer(arrayBuffer)\n        //    is true, then\n        //     a. Let newMaxByteLength be arrayBuffer.[[ArrayBufferMaxByteLength]].\n        // 7. Else,\n        //     a. Let newMaxByteLength be empty.\n        let new_max_len = buf\n            .borrow()\n            .data()\n            .max_byte_len\n            .filter(|_| !TO_FIXED_LENGTH);\n\n        // 8. If arrayBuffer.[[ArrayBufferDetachKey]] is not undefined, throw a TypeError exception.\n        if !buf.borrow().data().detach_key.is_undefined() {\n            buf.borrow_mut().data_mut().data = Some(bytes);\n            return Err(JsNativeError::typ()\n                .with_message(\"cannot transfer a buffer with a detach key\")\n                .into());\n        }\n\n        // Effectively, the next steps only create a new object for the same vec, so we can skip all\n        // those steps and just make a single check + trigger the realloc.\n\n        // 9. Let newBuffer be ? AllocateArrayBuffer(%ArrayBuffer%, newByteLength, newMaxByteLength).\n        // 10. Let copyLength be min(newByteLength, arrayBuffer.[[ArrayBufferByteLength]]).\n        // 11. Let fromBlock be arrayBuffer.[[ArrayBufferData]].\n        // 12. Let toBlock be newBuffer.[[ArrayBufferData]].\n        // 13. Perform CopyDataBlockBytes(toBlock, 0, fromBlock, 0, copyLength).\n        // 14. NOTE: Neither creation of the new Data Block nor copying from the old Data Block are\n        //     observable. Implementations may implement this method as a zero-copy move or a realloc.\n        // 15. Perform ! DetachArrayBuffer(arrayBuffer).\n        // 16. Return newBuffer.\n        if let Some(new_max_len) = new_max_len {\n            if new_len > new_max_len {\n                buf.borrow_mut().data_mut().data = Some(bytes);\n                return Err(JsNativeError::range()\n                    .with_message(\"`length` cannot be bigger than `maxByteLength`\")\n                    .into());\n            }\n            // Should only truncate without reallocating.\n            bytes.resize(new_len as usize, 0);\n        } else {\n            bytes.resize(new_len as usize, 0);\n\n            // Realloc the vec to fit onto the new exact length.\n            bytes.shrink_to_fit();\n        }\n\n        let prototype = context\n            .intrinsics()\n            .constructors()\n            .array_buffer()\n            .prototype();\n\n        Ok(JsObject::from_proto_and_data_with_shared_shape(\n            context.root_shape(),\n            prototype,\n            ArrayBuffer {\n                data: Some(bytes),\n                max_byte_len: new_max_len,\n                detach_key: JsValue::undefined(),\n            },\n        )\n        .into())\n    }\n\n    /// `AllocateArrayBuffer ( constructor, byteLength )`\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-allocatearraybuffer\n    pub(crate) fn allocate(\n        constructor: &JsValue,\n        byte_len: u64,\n        max_byte_len: Option<u64>,\n        context: &mut Context,\n    ) -> JsResult<JsObject<ArrayBuffer>> {\n        // 1. Let slots be « [[ArrayBufferData]], [[ArrayBufferByteLength]], [[ArrayBufferDetachKey]] ».\n        // 2. If maxByteLength is present and maxByteLength is not empty, let allocatingResizableBuffer be true; otherwise let allocatingResizableBuffer be false.\n        // 3. If allocatingResizableBuffer is true, then\n        //     a. If byteLength > maxByteLength, throw a RangeError exception.\n        //     b. Append [[ArrayBufferMaxByteLength]] to slots.\n        if let Some(max_byte_len) = max_byte_len\n            && byte_len > max_byte_len\n        {\n            return Err(JsNativeError::range()\n                .with_message(\"`length` cannot be bigger than `maxByteLength`\")\n                .into());\n        }\n\n        // 4. Let obj be ? OrdinaryCreateFromConstructor(constructor, \"%ArrayBuffer.prototype%\", slots).\n        let prototype = get_prototype_from_constructor(\n            constructor,\n            StandardConstructors::array_buffer,\n            context,\n        )?;\n\n        // 5. Let block be ? CreateByteDataBlock(byteLength).\n        // Preemptively allocate for `max_byte_len` if possible.\n        //     a. If it is not possible to create a Data Block block consisting of maxByteLength bytes, throw a RangeError exception.\n        //     b. NOTE: Resizable ArrayBuffers are designed to be implementable with in-place growth. Implementations may\n        //        throw if, for example, virtual memory cannot be reserved up front.\n        let block = create_byte_data_block(byte_len, max_byte_len, context)?;\n\n        let obj = JsObject::new(\n            context.root_shape(),\n            prototype,\n            Self {\n                // 6. Set obj.[[ArrayBufferData]] to block.\n                // 7. Set obj.[[ArrayBufferByteLength]] to byteLength.\n                data: Some(block),\n                // 8. If allocatingResizableBuffer is true, then\n                //    c. Set obj.[[ArrayBufferMaxByteLength]] to maxByteLength.\n                max_byte_len,\n                detach_key: JsValue::undefined(),\n            },\n        );\n\n        // 9. Return obj.\n        Ok(obj)\n    }\n}\n\n/// Abstract operation [`GetArrayBufferMaxByteLengthOption ( options )`][spec]\n///\n/// [spec]: https://tc39.es/ecma262/#sec-getarraybuffermaxbytelengthoption\nfn get_max_byte_len(options: &JsValue, context: &mut Context) -> JsResult<Option<u64>> {\n    // 1. If options is not an Object, return empty.\n    let Some(options) = options.as_object() else {\n        return Ok(None);\n    };\n\n    // 2. Let maxByteLength be ? Get(options, \"maxByteLength\").\n    let max_byte_len = options.get(js_string!(\"maxByteLength\"), context)?;\n\n    // 3. If maxByteLength is undefined, return empty.\n    if max_byte_len.is_undefined() {\n        return Ok(None);\n    }\n\n    // 4. Return ? ToIndex(maxByteLength).\n    max_byte_len.to_index(context).map(Some)\n}\n\n/// `CreateByteDataBlock ( size )` abstract operation.\n///\n/// The abstract operation `CreateByteDataBlock` takes argument `size` (a non-negative\n/// integer). For more information, check the [spec][spec].\n///\n/// [spec]: https://tc39.es/ecma262/#sec-createbytedatablock\npub(crate) fn create_byte_data_block(\n    size: u64,\n    max_buffer_size: Option<u64>,\n    context: &mut Context,\n) -> JsResult<AlignedVec<u8>> {\n    let alloc_size = max_buffer_size.unwrap_or(size);\n\n    assert!(size <= alloc_size);\n\n    if alloc_size > context.host_hooks().max_buffer_size(context) {\n        return Err(JsNativeError::range()\n            .with_message(\"cannot allocate a buffer that exceeds the maximum buffer size\")\n            .into());\n    }\n\n    // 1. Let db be a new Data Block value consisting of size bytes. If it is impossible to\n    //    create such a Data Block, throw a RangeError exception.\n    let alloc_size = alloc_size.try_into().map_err(|e| {\n        JsNativeError::range().with_message(format!(\"couldn't allocate the data block: {e}\"))\n    })?;\n\n    let mut data_block = AlignedVec::<u8>::new(64);\n    data_block.try_reserve_exact(alloc_size).map_err(|e| {\n        let message = match e {\n            aligned_vec::TryReserveError::CapacityOverflow => {\n                format!(\"capacity overflow for size {size} while allocating data block\")\n            }\n            aligned_vec::TryReserveError::AllocError { layout } => {\n                format!(\"invalid layout {layout:?} while allocating data block\")\n            }\n        };\n        JsNativeError::range().with_message(message)\n    })?;\n\n    // since size <= alloc_size, then `size` must also fit inside a `usize`.\n    let size = size as usize;\n\n    // 2. Set all of the bytes of db to 0.\n    data_block.resize(size, 0);\n\n    // 3. Return db.\n    Ok(data_block)\n}\n"
  },
  {
    "path": "core/engine/src/builtins/array_buffer/shared.rs",
    "content": "use std::{\n    ptr,\n    sync::{Arc, atomic::Ordering},\n};\n\nuse portable_atomic::{AtomicU8, AtomicUsize};\n\nuse boa_gc::{Finalize, Trace};\n\nuse crate::{\n    Context, JsArgs, JsData, JsNativeError, JsObject, JsResult, JsString, JsSymbol, JsValue,\n    builtins::{\n        Array, BuiltInBuilder, BuiltInConstructor, BuiltInObject, IntrinsicObject,\n        array_buffer::{AlignedBox, AlignedVec},\n    },\n    context::intrinsics::{Intrinsics, StandardConstructor, StandardConstructors},\n    js_string,\n    object::internal_methods::get_prototype_from_constructor,\n    property::Attribute,\n    realm::Realm,\n    string::StaticJsStrings,\n};\n\nuse super::{get_max_byte_len, utils::copy_shared_to_shared};\n\n/// The internal representation of a `SharedArrayBuffer` object.\n///\n/// This struct implements `Send` and `Sync`, meaning it can be shared between threads\n/// running different JS code at the same time.\n#[derive(Debug, Clone, Trace, Finalize, JsData)]\npub struct SharedArrayBuffer {\n    // Shared buffers cannot be detached.\n    #[unsafe_ignore_trace]\n    data: Arc<Inner>,\n}\n\n#[derive(Debug)]\nstruct Inner {\n    // Technically we should have an `[[ArrayBufferData]]` internal slot,\n    // `[[ArrayBufferByteLengthData]]` and `[[ArrayBufferMaxByteLength]]` slots for growable arrays\n    // or `[[ArrayBufferByteLength]]` for fixed arrays, but we can save some work\n    // by just using this representation instead.\n    //\n    // The maximum buffer length is represented by `buffer.len()`, and `current_len` has the current\n    // buffer length, or `None` if this is a fixed buffer; in this case, `buffer.len()` will be\n    // the true length of the buffer.\n    buffer: AlignedBox<[AtomicU8]>,\n    current_len: Option<AtomicUsize>,\n}\n\nimpl Default for Inner {\n    fn default() -> Self {\n        Self {\n            buffer: AlignedVec::new(0).into_boxed_slice(),\n            current_len: None,\n        }\n    }\n}\n\nimpl SharedArrayBuffer {\n    /// Creates a `SharedArrayBuffer` with an empty buffer.\n    #[must_use]\n    pub fn empty() -> Self {\n        Self {\n            data: Arc::default(),\n        }\n    }\n\n    /// Gets the length of this `SharedArrayBuffer`.\n    pub(crate) fn len(&self, ordering: Ordering) -> usize {\n        self.data\n            .current_len\n            .as_ref()\n            .map_or_else(|| self.data.buffer.len(), |len| len.load(ordering))\n    }\n\n    /// Gets the inner bytes of this `SharedArrayBuffer`.\n    pub(crate) fn bytes(&self, ordering: Ordering) -> &[AtomicU8] {\n        &self.data.buffer[..self.len(ordering)]\n    }\n\n    /// Gets the inner data of the buffer without accessing the current atomic length.\n    #[track_caller]\n    pub(crate) fn bytes_with_len(&self, len: usize) -> &[AtomicU8] {\n        &self.data.buffer[..len]\n    }\n\n    /// Gets a pointer to the internal shared buffer.\n    pub(crate) fn as_ptr(&self) -> *const AtomicU8 {\n        (*self.data.buffer).as_ptr()\n    }\n\n    pub(crate) fn is_fixed_len(&self) -> bool {\n        self.data.current_len.is_none()\n    }\n}\n\nimpl IntrinsicObject for SharedArrayBuffer {\n    fn init(realm: &Realm) {\n        let flag_attributes = Attribute::CONFIGURABLE | Attribute::NON_ENUMERABLE;\n\n        let get_species = BuiltInBuilder::callable(realm, Self::get_species)\n            .name(js_string!(\"get [Symbol.species]\"))\n            .build();\n\n        let get_byte_length = BuiltInBuilder::callable(realm, Self::get_byte_length)\n            .name(js_string!(\"get byteLength\"))\n            .build();\n\n        let get_growable = BuiltInBuilder::callable(realm, Self::get_growable)\n            .name(js_string!(\"get growable\"))\n            .build();\n\n        let get_max_byte_length = BuiltInBuilder::callable(realm, Self::get_max_byte_length)\n            .name(js_string!(\"get maxByteLength\"))\n            .build();\n\n        BuiltInBuilder::from_standard_constructor::<Self>(realm)\n            .static_accessor(\n                JsSymbol::species(),\n                Some(get_species),\n                None,\n                Attribute::CONFIGURABLE,\n            )\n            .accessor(\n                js_string!(\"byteLength\"),\n                Some(get_byte_length),\n                None,\n                flag_attributes,\n            )\n            .accessor(\n                js_string!(\"growable\"),\n                Some(get_growable),\n                None,\n                flag_attributes,\n            )\n            .accessor(\n                js_string!(\"maxByteLength\"),\n                Some(get_max_byte_length),\n                None,\n                flag_attributes,\n            )\n            .method(Self::slice, js_string!(\"slice\"), 2)\n            .method(Self::grow, js_string!(\"grow\"), 1)\n            .property(\n                JsSymbol::to_string_tag(),\n                Self::NAME,\n                Attribute::READONLY | Attribute::NON_ENUMERABLE | Attribute::CONFIGURABLE,\n            )\n            .build();\n    }\n\n    fn get(intrinsics: &Intrinsics) -> JsObject {\n        Self::STANDARD_CONSTRUCTOR(intrinsics.constructors()).constructor()\n    }\n}\n\nimpl BuiltInObject for SharedArrayBuffer {\n    const NAME: JsString = StaticJsStrings::SHARED_ARRAY_BUFFER;\n}\n\nimpl BuiltInConstructor for SharedArrayBuffer {\n    const CONSTRUCTOR_ARGUMENTS: usize = 1;\n    const PROTOTYPE_STORAGE_SLOTS: usize = 9;\n    const CONSTRUCTOR_STORAGE_SLOTS: usize = 2;\n\n    const STANDARD_CONSTRUCTOR: fn(&StandardConstructors) -> &StandardConstructor =\n        StandardConstructors::shared_array_buffer;\n\n    /// `25.1.3.1 SharedArrayBuffer ( length [ , options ] )`\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-sharedarraybuffer-constructor\n    fn constructor(\n        new_target: &JsValue,\n        args: &[JsValue],\n        context: &mut Context,\n    ) -> JsResult<JsValue> {\n        // 1. If NewTarget is undefined, throw a TypeError exception.\n        if new_target.is_undefined() {\n            return Err(JsNativeError::typ()\n                .with_message(\"ArrayBuffer.constructor called with undefined new target\")\n                .into());\n        }\n\n        // 2. Let byteLength be ? ToIndex(length).\n        let byte_len = args.get_or_undefined(0).to_index(context)?;\n\n        // 3. Let requestedMaxByteLength be ? GetArrayBufferMaxByteLengthOption(options).\n        let max_byte_len = get_max_byte_len(args.get_or_undefined(1), context)?;\n\n        // 4. Return ? AllocateSharedArrayBuffer(NewTarget, byteLength, requestedMaxByteLength).\n        Ok(Self::allocate(new_target, byte_len, max_byte_len, context)?\n            .upcast()\n            .into())\n    }\n}\n\nimpl SharedArrayBuffer {\n    /// `get SharedArrayBuffer [ @@species ]`\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-sharedarraybuffer-@@species\n    #[allow(clippy::unnecessary_wraps)]\n    fn get_species(this: &JsValue, _: &[JsValue], _: &mut Context) -> JsResult<JsValue> {\n        // 1. Return the this value.\n        Ok(this.clone())\n    }\n\n    /// `get SharedArrayBuffer.prototype.byteLength`\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-get-sharedarraybuffer.prototype.bytelength\n    pub(crate) fn get_byte_length(\n        this: &JsValue,\n        _args: &[JsValue],\n        _: &mut Context,\n    ) -> JsResult<JsValue> {\n        // 1. Let O be the this value.\n        // 2. Perform ? RequireInternalSlot(O, [[ArrayBufferData]]).\n        // 3. If IsSharedArrayBuffer(O) is true, throw a TypeError exception.\n        let object = this.as_object();\n        let buf = object\n            .as_ref()\n            .and_then(JsObject::downcast_ref::<Self>)\n            .ok_or_else(|| {\n                JsNativeError::typ()\n                    .with_message(\"SharedArrayBuffer.byteLength called with invalid value\")\n            })?;\n\n        // 4. Let length be ArrayBufferByteLength(O, seq-cst).\n        let len = buf.bytes(Ordering::SeqCst).len() as u64;\n\n        // 5. Return 𝔽(length).\n        Ok(len.into())\n    }\n\n    /// [`get SharedArrayBuffer.prototype.growable`][spec].\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-get-sharedarraybuffer.prototype.growable\n    pub(crate) fn get_growable(\n        this: &JsValue,\n        _args: &[JsValue],\n        _context: &mut Context,\n    ) -> JsResult<JsValue> {\n        // 1. Let O be the this value.\n        // 2. Perform ? RequireInternalSlot(O, [[ArrayBufferData]]).\n        // 3. If IsSharedArrayBuffer(O) is false, throw a TypeError exception.\n        let object = this.as_object();\n        let buf = object\n            .as_ref()\n            .and_then(JsObject::downcast_ref::<Self>)\n            .ok_or_else(|| {\n                JsNativeError::typ()\n                    .with_message(\"get SharedArrayBuffer.growable called with invalid `this`\")\n            })?;\n\n        // 4. If IsFixedLengthArrayBuffer(O) is false, return true; otherwise return false.\n        Ok(JsValue::from(!buf.is_fixed_len()))\n    }\n\n    /// [`get SharedArrayBuffer.prototype.maxByteLength`][spec].\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-get-sharedarraybuffer.prototype.maxbytelength\n    pub(crate) fn get_max_byte_length(\n        this: &JsValue,\n        _args: &[JsValue],\n        _context: &mut Context,\n    ) -> JsResult<JsValue> {\n        // 1. Let O be the this value.\n        // 2. Perform ? RequireInternalSlot(O, [[ArrayBufferData]]).\n        // 3. If IsSharedArrayBuffer(O) is false, throw a TypeError exception.\n        let object = this.as_object();\n        let buf = object\n            .as_ref()\n            .and_then(JsObject::downcast_ref::<Self>)\n            .ok_or_else(|| {\n                JsNativeError::typ()\n                    .with_message(\"get SharedArrayBuffer.maxByteLength called with invalid value\")\n            })?;\n\n        // 4. If IsFixedLengthArrayBuffer(O) is true, then\n        //     a. Let length be O.[[ArrayBufferByteLength]].\n        // 5. Else,\n        //     a. Let length be O.[[ArrayBufferMaxByteLength]].\n        // 6. Return 𝔽(length).\n        Ok(buf.data.buffer.len().into())\n    }\n\n    /// [`SharedArrayBuffer.prototype.grow ( newLength )`][spec].\n    ///\n    /// [spec]: https://tc39.es/ecma262/sec-sharedarraybuffer.prototype.grow\n    pub(crate) fn grow(\n        this: &JsValue,\n        args: &[JsValue],\n        context: &mut Context,\n    ) -> JsResult<JsValue> {\n        // 1. Let O be the this value.\n        // 3. If IsSharedArrayBuffer(O) is false, throw a TypeError exception.\n        let Some(buf) = this\n            .as_object()\n            .and_then(|o| o.clone().downcast::<Self>().ok())\n        else {\n            return Err(JsNativeError::typ()\n                .with_message(\"SharedArrayBuffer.grow called with non-object value\")\n                .into());\n        };\n\n        // 2. Perform ? RequireInternalSlot(O, [[ArrayBufferMaxByteLength]]).\n        if buf.borrow().data().is_fixed_len() {\n            return Err(JsNativeError::typ()\n                .with_message(\"SharedArrayBuffer.grow: cannot grow a fixed-length buffer\")\n                .into());\n        }\n\n        // 4. Let newByteLength be ? ToIndex(newLength).\n        let new_byte_len = args.get_or_undefined(0).to_index(context)?;\n\n        // TODO: 5. Let hostHandled be ? HostGrowSharedArrayBuffer(O, newByteLength).\n        // 6. If hostHandled is handled, return undefined.\n        // Used in engines to handle Wasm buffers in a special way, but we don't\n        // have a Wasm interpreter in place yet.\n\n        // 7. Let isLittleEndian be the value of the [[LittleEndian]] field of the surrounding agent's Agent Record.\n        // 8. Let byteLengthBlock be O.[[ArrayBufferByteLengthData]].\n        // 9. Let currentByteLengthRawBytes be GetRawBytesFromSharedBlock(byteLengthBlock, 0, biguint64, true, seq-cst).\n        // 10. Let newByteLengthRawBytes be NumericToRawBytes(biguint64, ℤ(newByteLength), isLittleEndian).\n\n        let buf = buf.borrow();\n        let buf = &buf.data();\n\n        // d. If newByteLength < currentByteLength or newByteLength > O.[[ArrayBufferMaxByteLength]], throw a RangeError exception.\n        // Extracting this condition outside the CAS since throwing early doesn't affect the correct\n        // behaviour of the loop.\n        if new_byte_len > buf.data.buffer.len() as u64 {\n            return Err(JsNativeError::range()\n                .with_message(\n                    \"SharedArrayBuffer.grow: new length cannot be bigger than `maxByteLength`\",\n                )\n                .into());\n        }\n        let new_byte_len = new_byte_len as usize;\n\n        // If we used let-else above to avoid the expect, we would carry a borrow through the `to_index`\n        // call, which could mutably borrow. Another alternative would be to clone the whole\n        // `SharedArrayBuffer`, but it's better to avoid contention with the counter in the `Arc` pointer.\n        let atomic_len = buf\n            .data\n            .current_len\n            .as_ref()\n            .expect(\"already checked that the buffer is not fixed-length\");\n\n        // 11. Repeat,\n        //     a. NOTE: This is a compare-and-exchange loop to ensure that parallel, racing grows of the same buffer are\n        //        totally ordered, are not lost, and do not silently do nothing. The loop exits if it was able to attempt\n        //        to grow uncontended.\n        //     b. Let currentByteLength be ℝ(RawBytesToNumeric(biguint64, currentByteLengthRawBytes, isLittleEndian)).\n        //     c. If newByteLength = currentByteLength, return undefined.\n        //     d. If newByteLength < currentByteLength or newByteLength > O.[[ArrayBufferMaxByteLength]], throw a\n        //        RangeError exception.\n        //     e. Let byteLengthDelta be newByteLength - currentByteLength.\n        //     f. If it is impossible to create a new Shared Data Block value consisting of byteLengthDelta bytes, throw\n        //        a RangeError exception.\n        //     g. NOTE: No new Shared Data Block is constructed and used here. The observable behaviour of growable\n        //        SharedArrayBuffers is specified by allocating a max-sized Shared Data Block at construction time, and\n        //        this step captures the requirement that implementations that run out of memory must throw a RangeError.\n        //     h. Let readByteLengthRawBytes be AtomicCompareExchangeInSharedBlock(byteLengthBlock, 0, 8,\n        //        currentByteLengthRawBytes, newByteLengthRawBytes).\n        //     i. If ByteListEqual(readByteLengthRawBytes, currentByteLengthRawBytes) is true, return undefined.\n        //     j. Set currentByteLengthRawBytes to readByteLengthRawBytes.\n\n        // We require SEQ-CST operations because readers of the buffer also use SEQ-CST operations.\n        atomic_len\n            .fetch_update(Ordering::SeqCst, Ordering::SeqCst, |prev_byte_len| {\n                (prev_byte_len <= new_byte_len).then_some(new_byte_len)\n            })\n            .map_err(|_| {\n                JsNativeError::range()\n                    .with_message(\"SharedArrayBuffer.grow: failed to grow buffer to new length\")\n            })?;\n\n        Ok(JsValue::undefined())\n    }\n\n    /// `SharedArrayBuffer.prototype.slice ( start, end )`\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-sharedarraybuffer.prototype.slice\n    fn slice(this: &JsValue, args: &[JsValue], context: &mut Context) -> JsResult<JsValue> {\n        // 1. Let O be the this value.\n        // 2. Perform ? RequireInternalSlot(O, [[ArrayBufferData]]).\n        // 3. If IsSharedArrayBuffer(O) is false, throw a TypeError exception.\n        let buf = this\n            .as_object()\n            .and_then(|o| o.clone().downcast::<Self>().ok())\n            .ok_or_else(|| {\n                JsNativeError::typ()\n                    .with_message(\"SharedArrayBuffer.slice called with invalid `this` value\")\n            })?;\n\n        // 4. Let len be ArrayBufferByteLength(O, seq-cst).\n        let len = buf.borrow().data().len(Ordering::SeqCst);\n\n        // 5. Let relativeStart be ? ToIntegerOrInfinity(start).\n        // 6. If relativeStart = -∞, let first be 0.\n        // 7. Else if relativeStart < 0, let first be max(len + relativeStart, 0).\n        // 8. Else, let first be min(relativeStart, len).\n        let first = Array::get_relative_start(context, args.get_or_undefined(0), len as u64)?;\n\n        // 9. If end is undefined, let relativeEnd be len; else let relativeEnd be ? ToIntegerOrInfinity(end).\n        // 10. If relativeEnd = -∞, let final be 0.\n        // 11. Else if relativeEnd < 0, let final be max(len + relativeEnd, 0).\n        // 12. Else, let final be min(relativeEnd, len).\n        let final_ = Array::get_relative_end(context, args.get_or_undefined(1), len as u64)?;\n\n        // 13. Let newLen be max(final - first, 0).\n        let new_len = final_.saturating_sub(first);\n\n        // 14. Let ctor be ? SpeciesConstructor(O, %SharedArrayBuffer%).\n        let ctor = buf\n            .clone()\n            .upcast()\n            .species_constructor(StandardConstructors::shared_array_buffer, context)?;\n\n        // 15. Let new be ? Construct(ctor, « 𝔽(newLen) »).\n        let new = ctor.construct(&[new_len.into()], Some(&ctor), context)?;\n\n        {\n            let buf = buf.borrow();\n            let buf = &buf.data();\n            // 16. Perform ? RequireInternalSlot(new, [[ArrayBufferData]]).\n            // 17. If IsSharedArrayBuffer(new) is false, throw a TypeError exception.\n            let new = new.downcast_ref::<Self>().ok_or_else(|| {\n                JsNativeError::typ()\n                    .with_message(\"SharedArrayBuffer constructor returned invalid object\")\n            })?;\n\n            // 18. If new.[[ArrayBufferData]] is O.[[ArrayBufferData]], throw a TypeError exception.\n            if ptr::eq(buf.as_ptr(), new.as_ptr()) {\n                return Err(JsNativeError::typ()\n                    .with_message(\"cannot reuse the same SharedArrayBuffer for a slice operation\")\n                    .into());\n            }\n\n            // 19. If ArrayBufferByteLength(new, seq-cst) < newLen, throw a TypeError exception.\n            if (new.len(Ordering::SeqCst) as u64) < new_len {\n                return Err(JsNativeError::typ()\n                    .with_message(\"invalid size of constructed SharedArrayBuffer\")\n                    .into());\n            }\n\n            let first = first as usize;\n            let new_len = new_len as usize;\n\n            // 20. Let fromBuf be O.[[ArrayBufferData]].\n            let from_buf = &buf.bytes_with_len(len)[first..];\n\n            // 21. Let toBuf be new.[[ArrayBufferData]].\n            let to_buf = new;\n\n            // Sanity check to ensure there is enough space inside `from_buf` for\n            // `new_len` elements.\n            debug_assert!(from_buf.len() >= new_len);\n\n            // 22. Perform CopyDataBlockBytes(toBuf, 0, fromBuf, first, newLen).\n            // SAFETY: `get_slice_range` will always return indices that are in-bounds.\n            // This also means that the newly created buffer will have at least `new_len` elements\n            // to write to.\n            unsafe { copy_shared_to_shared(from_buf.as_ptr(), to_buf.as_ptr(), new_len) }\n        }\n\n        // 23. Return new.\n        Ok(new.into())\n    }\n\n    /// `AllocateSharedArrayBuffer ( constructor, byteLength [ , maxByteLength ] )`\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-allocatesharedarraybuffer\n    pub(crate) fn allocate(\n        constructor: &JsValue,\n        byte_len: u64,\n        max_byte_len: Option<u64>,\n        context: &mut Context,\n    ) -> JsResult<JsObject<SharedArrayBuffer>> {\n        // 1. Let slots be « [[ArrayBufferData]] ».\n        // 2. If maxByteLength is present and maxByteLength is not empty, let allocatingGrowableBuffer\n        //    be true; otherwise let allocatingGrowableBuffer be false.\n        // 3. If allocatingGrowableBuffer is true, then\n        //     a. If byteLength > maxByteLength, throw a RangeError exception.\n        //     b. Append [[ArrayBufferByteLengthData]] and [[ArrayBufferMaxByteLength]] to slots.\n        // 4. Else,\n        //     a. Append [[ArrayBufferByteLength]] to slots.\n        if let Some(max_byte_len) = max_byte_len\n            && byte_len > max_byte_len\n        {\n            return Err(JsNativeError::range()\n                .with_message(\"`length` cannot be bigger than `maxByteLength`\")\n                .into());\n        }\n\n        // 5. Let obj be ? OrdinaryCreateFromConstructor(constructor, \"%SharedArrayBuffer.prototype%\", slots).\n        let prototype = get_prototype_from_constructor(\n            constructor,\n            StandardConstructors::shared_array_buffer,\n            context,\n        )?;\n\n        // 6. If allocatingGrowableBuffer is true, let allocLength be maxByteLength;\n        //    otherwise let allocLength be byteLength.\n        let alloc_len = max_byte_len.unwrap_or(byte_len);\n\n        // 7. Let block be ? CreateSharedByteDataBlock(allocLength).\n        // 8. Set obj.[[ArrayBufferData]] to block.\n        let block = create_shared_byte_data_block(alloc_len, context)?;\n\n        // 9. If allocatingGrowableBuffer is true, then\n        // `byte_len` must fit inside an `usize` thanks to the checks inside\n        // `create_shared_byte_data_block`.\n        // a. Assert: byteLength ≤ maxByteLength.\n        // b. Let byteLengthBlock be ? CreateSharedByteDataBlock(8).\n        // c. Perform SetValueInBuffer(byteLengthBlock, 0, biguint64, ℤ(byteLength), true, seq-cst).\n        // d. Set obj.[[ArrayBufferByteLengthData]] to byteLengthBlock.\n        // e. Set obj.[[ArrayBufferMaxByteLength]] to maxByteLength.\n        let current_len = max_byte_len.map(|_| AtomicUsize::new(byte_len as usize));\n\n        // 10. Else,\n        //     a. Set obj.[[ArrayBufferByteLength]] to byteLength.\n        let obj = JsObject::new(\n            context.root_shape(),\n            prototype,\n            Self {\n                data: Arc::new(Inner {\n                    buffer: block,\n                    current_len,\n                }),\n            },\n        );\n\n        // 11. Return obj.\n        Ok(obj)\n    }\n}\n\n/// [`CreateSharedByteDataBlock ( size )`][spec] abstract operation.\n///\n/// Creates a new `Arc<Vec<AtomicU8>>` that can be used as a backing buffer for a [`SharedArrayBuffer`].\n///\n/// For more information, check the [spec][spec].\n///\n/// [spec]: https://tc39.es/ecma262/#sec-createsharedbytedatablock\npub(crate) fn create_shared_byte_data_block(\n    size: u64,\n    context: &mut Context,\n) -> JsResult<AlignedBox<[AtomicU8]>> {\n    if size > context.host_hooks().max_buffer_size(context) {\n        return Err(JsNativeError::range()\n            .with_message(\n                \"cannot allocate a buffer that exceeds the maximum buffer size\".to_string(),\n            )\n            .into());\n    }\n\n    // 1. Let db be a new Shared Data Block value consisting of size bytes. If it is impossible to\n    //    create such a Shared Data Block, throw a RangeError exception.\n    let size = size.try_into().map_err(|e| {\n        JsNativeError::range().with_message(format!(\"couldn't allocate the data block: {e}\"))\n    })?;\n\n    if size == 0 {\n        // Must ensure we don't allocate a zero-sized buffer.\n        return Ok(AlignedVec::new(0).into_boxed_slice());\n    }\n\n    // 2. Let execution be the [[CandidateExecution]] field of the surrounding agent's Agent Record.\n    // 3. Let eventsRecord be the Agent Events Record of execution.[[EventsRecords]] whose\n    //    [[AgentSignifier]] is AgentSignifier().\n    // 4. Let zero be « 0 ».\n    // 5. For each index i of db, do\n    //     a. Append WriteSharedMemory { [[Order]]: init, [[NoTear]]: true, [[Block]]: db,\n    //        [[ByteIndex]]: i, [[ElementSize]]: 1, [[Payload]]: zero } to eventsRecord.[[EventList]].\n    // 6. Return db.\n    let mut buf = AlignedVec::<u8>::new(0);\n    buf.try_reserve_exact(size).map_err(|e| {\n        let message = match e {\n            aligned_vec::TryReserveError::CapacityOverflow => {\n                format!(\"capacity overflow for size {size} while allocating data block\")\n            }\n            aligned_vec::TryReserveError::AllocError { layout } => {\n                format!(\"invalid layout {layout:?} while allocating data block\")\n            }\n        };\n        JsNativeError::range().with_message(message)\n    })?;\n    buf.resize(size, 0);\n    buf.shrink_to_fit();\n    let (data, align, len, _) = buf.into_raw_parts();\n\n    // 3. Return db.\n    // SAFETY: `[u8]` must be transparently castable to `[AtomicU8]`.\n    Ok(unsafe {\n        AlignedBox::from_raw_parts(align, ptr::slice_from_raw_parts_mut(data.cast(), len))\n    })\n}\n"
  },
  {
    "path": "core/engine/src/builtins/array_buffer/tests.rs",
    "content": "use crate::object::JsArrayBuffer;\nuse crate::{TestAction, run_test_actions};\n\n#[test]\nfn create_byte_data_block() {\n    run_test_actions([TestAction::inspect_context(|context| {\n        // Sunny day\n        assert!(super::create_byte_data_block(100, None, context).is_ok());\n\n        // Rainy day\n        assert!(super::create_byte_data_block(u64::MAX, None, context).is_err());\n    })]);\n}\n\n#[test]\nfn create_shared_byte_data_block() {\n    run_test_actions([TestAction::inspect_context(|context| {\n        // Sunny day\n        assert!(super::shared::create_shared_byte_data_block(100, context).is_ok());\n\n        // Rainy day\n        assert!(super::shared::create_shared_byte_data_block(u64::MAX, context).is_err());\n    })]);\n}\n\n#[test]\nfn resize() {\n    run_test_actions([TestAction::inspect_context(|context| {\n        let data_block = super::create_byte_data_block(100, None, context).unwrap();\n        let js_arr = JsArrayBuffer::from_byte_block(data_block, context)\n            .unwrap()\n            .with_max_byte_length(100);\n        let mut arr = js_arr.borrow_mut();\n\n        // Sunny day\n        assert_eq!(arr.data_mut().resize(50), Ok(()));\n\n        // Rainy day\n        assert!(arr.data_mut().resize(u64::MAX).is_err());\n    })]);\n}\n\n#[test]\nfn get_values() {\n    run_test_actions([\n        TestAction::run(\n            r#\"\n            var buffer = new ArrayBuffer(12);\n            var sample = new DataView(buffer, 0);\n\n            sample.setUint8(0, 127);\n            sample.setUint8(1, 255);\n            sample.setUint8(2, 255);\n            sample.setUint8(3, 255);\n            sample.setUint8(4, 128);\n            sample.setUint8(5, 0);\n            sample.setUint8(6, 0);\n            sample.setUint8(7, 0);\n            sample.setUint8(8, 1);\n            sample.setUint8(9, 0);\n            sample.setUint8(10, 0);\n            sample.setUint8(11, 0);\n        \"#,\n        ),\n        TestAction::assert(\"sample.getUint32(0, false) == 2147483647\"),\n        TestAction::assert(\"sample.getUint32(1, false) == 4294967168\"),\n        TestAction::assert(\"sample.getUint32(2, false) == 4294934528\"),\n        TestAction::assert(\"sample.getUint32(3, false) == 4286578688\"),\n        TestAction::assert(\"sample.getUint32(4, false) == 2147483648\"),\n        TestAction::assert(\"sample.getUint32(5, false) == 1\"),\n        TestAction::assert(\"sample.getUint32(6, false) == 256\"),\n        TestAction::assert(\"sample.getUint32(7, false) == 65536\"),\n        TestAction::assert(\"sample.getUint32(8, false) == 16777216\"),\n        TestAction::assert(\"sample.getUint32(0, true) == 4294967167\"),\n        TestAction::assert(\"sample.getUint32(1, true) == 2164260863\"),\n        TestAction::assert(\"sample.getUint32(2, true) == 8454143\"),\n        TestAction::assert(\"sample.getUint32(3, true) == 33023\"),\n        TestAction::assert(\"sample.getUint32(4, true) == 128\"),\n        TestAction::assert(\"sample.getUint32(5, true) == 16777216\"),\n        TestAction::assert(\"sample.getUint32(6, true) == 65536\"),\n        TestAction::assert(\"sample.getUint32(7, true) == 256\"),\n        TestAction::assert(\"sample.getUint32(8, true) == 1\"),\n    ]);\n}\n\n#[test]\nfn sort() {\n    run_test_actions([\n        TestAction::run(\n            r#\"\n            // This cmp function is needed as the harness does not support TypedArray comparison.\n            function cmp(a, b) {\n                return a.length === b.length && a.every((v, i) => v === b[i]);\n            }\n\n            var TypedArrayCtor = [\n                Int8Array,\n                Uint8Array,\n                Int16Array,\n                Uint16Array,\n                Int32Array,\n                Uint32Array,\n                Float32Array,\n                Float64Array,\n            ];\n\n            var descending = TypedArrayCtor.map((ctor) => new ctor([4, 3, 2, 1]).sort());\n            var mixed = TypedArrayCtor.map((ctor) => new ctor([3, 4, 1, 2]).sort());\n            var repeating = TypedArrayCtor.map((ctor) => new ctor([0, 1, 1, 2, 3, 3, 4]).sort());\n        \"#,\n        ),\n        // Descending\n        TestAction::assert(\"cmp(descending[0], [1, 2, 3, 4])\"),\n        TestAction::assert(\"cmp(descending[1], [1, 2, 3, 4])\"),\n        TestAction::assert(\"cmp(descending[2], [1, 2, 3, 4])\"),\n        TestAction::assert(\"cmp(descending[3], [1, 2, 3, 4])\"),\n        TestAction::assert(\"cmp(descending[4], [1, 2, 3, 4])\"),\n        TestAction::assert(\"cmp(descending[5], [1, 2, 3, 4])\"),\n        TestAction::assert(\"cmp(descending[6], [1, 2, 3, 4])\"),\n        TestAction::assert(\"cmp(descending[7], [1, 2, 3, 4])\"),\n        // Mixed\n        TestAction::assert(\"cmp(mixed[0], [1, 2, 3, 4])\"),\n        TestAction::assert(\"cmp(mixed[1], [1, 2, 3, 4])\"),\n        TestAction::assert(\"cmp(mixed[2], [1, 2, 3, 4])\"),\n        TestAction::assert(\"cmp(mixed[3], [1, 2, 3, 4])\"),\n        TestAction::assert(\"cmp(mixed[4], [1, 2, 3, 4])\"),\n        TestAction::assert(\"cmp(mixed[5], [1, 2, 3, 4])\"),\n        TestAction::assert(\"cmp(mixed[6], [1, 2, 3, 4])\"),\n        TestAction::assert(\"cmp(mixed[7], [1, 2, 3, 4])\"),\n        // Repeating\n        TestAction::assert(\"cmp(repeating[0], [0, 1, 1, 2, 3, 3, 4])\"),\n        TestAction::assert(\"cmp(repeating[1], [0, 1, 1, 2, 3, 3, 4])\"),\n        TestAction::assert(\"cmp(repeating[2], [0, 1, 1, 2, 3, 3, 4])\"),\n        TestAction::assert(\"cmp(repeating[3], [0, 1, 1, 2, 3, 3, 4])\"),\n        TestAction::assert(\"cmp(repeating[4], [0, 1, 1, 2, 3, 3, 4])\"),\n        TestAction::assert(\"cmp(repeating[5], [0, 1, 1, 2, 3, 3, 4])\"),\n        TestAction::assert(\"cmp(repeating[6], [0, 1, 1, 2, 3, 3, 4])\"),\n        TestAction::assert(\"cmp(repeating[7], [0, 1, 1, 2, 3, 3, 4])\"),\n    ]);\n}\n\n#[test]\nfn sort_negative_zero() {\n    run_test_actions([\n        TestAction::run(\n            r#\"\n            // This cmp function is needed as the harness does not support TypedArray comparison.\n            function cmp(a, b) {\n                return a.length === b.length && a.every((v, i) => v === b[i]);\n            }\n\n            var TypedArrayCtor = [Float32Array, Float64Array];\n            var negativeZero = TypedArrayCtor.map((ctor) => new ctor([1, 0, -0, 2]).sort());\n            var infinities = TypedArrayCtor.map((ctor) => new ctor([3, 4, Infinity, -Infinity, 1, 2]).sort());\n        \"#,\n        ),\n        TestAction::assert(\"cmp(negativeZero[0], [-0, 0, 1, 2])\"),\n        TestAction::assert(\"cmp(negativeZero[1], [-0, 0, 1, 2])\"),\n        TestAction::assert(\"cmp(infinities[0], [-Infinity, 1, 2, 3, 4, Infinity])\"),\n        TestAction::assert(\"cmp(infinities[1], [-Infinity, 1, 2, 3, 4, Infinity])\"),\n    ]);\n}\n\n/// Tests `SharedArrayBuffer.prototype.slice` which triggers `copy_shared_to_shared`\n/// (the `batched_atomic_copy_forward` path).\n#[test]\nfn shared_array_buffer_slice() {\n    run_test_actions([\n        TestAction::run(\n            r#\"\n            var sab = new SharedArrayBuffer(16);\n            var view = new Uint8Array(sab);\n            for (var i = 0; i < 16; i++) view[i] = i + 1;\n            var sliced = sab.slice(0);\n            var result = new Uint8Array(sliced);\n        \"#,\n        ),\n        // Verify all 16 bytes copied correctly (exercises u64 batch + head/tail)\n        TestAction::assert(\"result[0] === 1\"),\n        TestAction::assert(\"result[7] === 8\"),\n        TestAction::assert(\"result[15] === 16\"),\n        TestAction::assert(\"result.length === 16\"),\n    ]);\n}\n\n/// Tests `SharedArrayBuffer.prototype.slice` with a partial range and odd sizes\n/// to exercise alignment edge cases in the batched copy.\n#[test]\nfn shared_array_buffer_slice_partial() {\n    run_test_actions([\n        TestAction::run(\n            r#\"\n            var sab = new SharedArrayBuffer(20);\n            var view = new Uint8Array(sab);\n            for (var i = 0; i < 20; i++) view[i] = i * 3;\n\n            // Slice with odd offset and size to hit unaligned head/tail\n            var sliced = sab.slice(3, 14);\n            var result = new Uint8Array(sliced);\n        \"#,\n        ),\n        TestAction::assert(\"result.length === 11\"),\n        TestAction::assert(\"result[0] === 9\"),\n        TestAction::assert(\"result[10] === 39\"),\n    ]);\n}\n\n/// Tests TypedArray.set from a SharedArrayBuffer-backed array to a regular\n/// ArrayBuffer-backed array, triggering `batched_copy_atomic_to_bytes`.\n#[test]\nfn shared_to_regular_typed_array_set() {\n    run_test_actions([\n        TestAction::run(\n            r#\"\n            var sab = new SharedArrayBuffer(16);\n            var src = new Uint8Array(sab);\n            for (var i = 0; i < 16; i++) src[i] = 100 + i;\n\n            var ab = new ArrayBuffer(16);\n            var dest = new Uint8Array(ab);\n            dest.set(src);\n        \"#,\n        ),\n        TestAction::assert(\"dest[0] === 100\"),\n        TestAction::assert(\"dest[7] === 107\"),\n        TestAction::assert(\"dest[15] === 115\"),\n    ]);\n}\n\n/// Tests TypedArray.set from a regular ArrayBuffer-backed array to a\n/// SharedArrayBuffer-backed array, triggering `batched_copy_bytes_to_atomic`.\n#[test]\nfn regular_to_shared_typed_array_set() {\n    run_test_actions([\n        TestAction::run(\n            r#\"\n            var ab = new ArrayBuffer(16);\n            var src = new Uint8Array(ab);\n            for (var i = 0; i < 16; i++) src[i] = 200 + i;\n\n            var sab = new SharedArrayBuffer(16);\n            var dest = new Uint8Array(sab);\n            dest.set(src);\n        \"#,\n        ),\n        TestAction::assert(\"dest[0] === 200\"),\n        TestAction::assert(\"dest[7] === 207\"),\n        TestAction::assert(\"dest[15] === 215\"),\n    ]);\n}\n\n/// Tests forward `copyWithin` on a SharedArrayBuffer-backed typed array,\n/// triggering `copy_shared_to_shared` via `memmove`.\n#[test]\nfn shared_typed_array_copy_within() {\n    run_test_actions([\n        TestAction::run(\n            r#\"\n            var sab = new SharedArrayBuffer(16);\n            var arr = new Uint8Array(sab);\n            for (var i = 0; i < 16; i++) arr[i] = i + 1;\n\n            // Forward copy: copies bytes 4..12 to offset 0\n            arr.copyWithin(0, 4, 12);\n        \"#,\n        ),\n        TestAction::assert(\"arr[0] === 5\"),\n        TestAction::assert(\"arr[7] === 12\"),\n        TestAction::assert(\"arr[8] === 9\"),\n    ]);\n}\n\n/// Tests backward `copyWithin` on a SharedArrayBuffer-backed typed array,\n/// triggering `copy_shared_to_shared_backwards` when source and dest overlap.\n#[test]\nfn shared_typed_array_copy_within_backward() {\n    run_test_actions([\n        TestAction::run(\n            r#\"\n            var sab = new SharedArrayBuffer(16);\n            var arr = new Uint8Array(sab);\n            for (var i = 0; i < 16; i++) arr[i] = i + 1;\n\n            // Backward copy: copies bytes 0..8 to offset 4 (overlapping)\n            arr.copyWithin(4, 0, 8);\n        \"#,\n        ),\n        TestAction::assert(\"arr[0] === 1\"),\n        TestAction::assert(\"arr[3] === 4\"),\n        TestAction::assert(\"arr[4] === 1\"),\n        TestAction::assert(\"arr[11] === 8\"),\n        TestAction::assert(\"arr[12] === 13\"),\n    ]);\n}\n\n/// Tests zero-length slice to exercise the `count == 0` early return.\n#[test]\nfn shared_array_buffer_slice_empty() {\n    run_test_actions([\n        TestAction::run(\n            r#\"\n            var sab = new SharedArrayBuffer(16);\n            var view = new Uint8Array(sab);\n            for (var i = 0; i < 16; i++) view[i] = i + 1;\n            var sliced = sab.slice(5, 5);\n            var result = new Uint8Array(sliced);\n        \"#,\n        ),\n        TestAction::assert(\"result.length === 0\"),\n    ]);\n}\n"
  },
  {
    "path": "core/engine/src/builtins/array_buffer/utils.rs",
    "content": "use std::{ptr, slice::SliceIndex, sync::atomic::Ordering};\n\nuse portable_atomic::{AtomicU8, AtomicU64};\n\nuse crate::{\n    Context, JsResult,\n    builtins::typed_array::{ClampedU8, Element, TypedArrayElement, TypedArrayKind},\n    object::JsObject,\n};\n\nuse super::ArrayBuffer;\n\n#[derive(Clone, Copy)]\npub(crate) enum BytesConstPtr {\n    Bytes(*const u8),\n    AtomicBytes(*const AtomicU8),\n}\n\nimpl BytesConstPtr {\n    /// Offsets this const pointer by a positive amount.\n    pub(crate) unsafe fn add(self, count: usize) -> Self {\n        // SAFETY: the operation is guaranteed to be safe by the caller.\n        unsafe {\n            match self {\n                Self::Bytes(p) => Self::Bytes(p.add(count)),\n                Self::AtomicBytes(p) => Self::AtomicBytes(p.add(count)),\n            }\n        }\n    }\n}\n\n#[derive(Clone, Copy)]\npub(crate) enum BytesMutPtr {\n    Bytes(*mut u8),\n    AtomicBytes(*const AtomicU8),\n}\n\nimpl BytesMutPtr {\n    /// Offsets this mut pointer by a positive amount.\n    pub(crate) unsafe fn add(self, count: usize) -> Self {\n        // SAFETY: the operation is guaranteed to be safe by the caller.\n        unsafe {\n            match self {\n                Self::Bytes(p) => Self::Bytes(p.add(count)),\n                Self::AtomicBytes(p) => Self::AtomicBytes(p.add(count)),\n            }\n        }\n    }\n}\n\n#[derive(Debug, Clone, Copy)]\npub(crate) enum SliceRef<'a> {\n    Slice(&'a [u8]),\n    AtomicSlice(&'a [AtomicU8]),\n}\n\nimpl SliceRef<'_> {\n    /// Gets the byte length of this `SliceRef`.\n    pub(crate) fn len(&self) -> usize {\n        match self {\n            Self::Slice(buf) => buf.len(),\n            Self::AtomicSlice(buf) => buf.len(),\n        }\n    }\n\n    /// Gets a subslice of this `SliceRef`.\n    pub(crate) fn subslice<I>(&self, index: I) -> SliceRef<'_>\n    where\n        I: SliceIndex<[u8], Output = [u8]> + SliceIndex<[AtomicU8], Output = [AtomicU8]>,\n    {\n        match self {\n            Self::Slice(buffer) => SliceRef::Slice(buffer.get(index).expect(\"index out of bounds\")),\n            Self::AtomicSlice(buffer) => {\n                SliceRef::AtomicSlice(buffer.get(index).expect(\"index out of bounds\"))\n            }\n        }\n    }\n\n    /// Copies the slice into a new `Vec<u8>`. For `AtomicSlice`, each byte is loaded with `SeqCst` ordering.\n    #[must_use]\n    pub(crate) fn to_vec(self) -> Vec<u8> {\n        match self {\n            Self::Slice(s) => s.to_vec(),\n            Self::AtomicSlice(s) => {\n                let count = s.len();\n                let mut target = Vec::with_capacity(count);\n                // SAFETY: we only copy `count` bytes, which should be in bounds\n                // for both the target buffer and the source atomic slice.\n                // `target` also has enough capacity for `count` elements and\n                // all its elements are initialized by `memcpy`.\n                unsafe {\n                    memcpy(\n                        BytesConstPtr::AtomicBytes(s.as_ptr()),\n                        BytesMutPtr::Bytes(target.as_mut_ptr()),\n                        count,\n                    );\n                    target.set_len(s.len());\n                }\n                target\n            }\n        }\n    }\n\n    /// Gets the starting address of this `SliceRef`.\n    #[cfg(debug_assertions)]\n    pub(crate) fn addr(&self) -> usize {\n        match self {\n            Self::Slice(buf) => buf.as_ptr().addr(),\n            Self::AtomicSlice(buf) => buf.as_ptr().addr(),\n        }\n    }\n\n    /// Gets a pointer to the underlying slice.\n    pub(crate) fn as_ptr(&self) -> BytesConstPtr {\n        match self {\n            SliceRef::Slice(s) => BytesConstPtr::Bytes(s.as_ptr()),\n            SliceRef::AtomicSlice(s) => BytesConstPtr::AtomicBytes(s.as_ptr()),\n        }\n    }\n\n    /// [`GetValueFromBuffer ( arrayBuffer, byteIndex, type, isTypedArray, order [ , isLittleEndian ] )`][spec]\n    ///\n    /// The start offset is determined by the input buffer instead of a `byteIndex` parameter.\n    ///\n    /// # Safety\n    ///\n    /// - There must be enough bytes in `buffer` to read an element from an array with type `TypedArrayKind`.\n    /// - `buffer` must be aligned to the alignment of said element.\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-getvaluefrombuffer\n    pub(crate) unsafe fn get_value(\n        &self,\n        kind: TypedArrayKind,\n        order: Ordering,\n    ) -> TypedArrayElement {\n        unsafe fn read_elem<T: Element>(buffer: SliceRef<'_>, order: Ordering) -> T {\n            // <https://tc39.es/ecma262/#sec-getvaluefrombuffer>\n\n            // 1. Assert: IsDetachedBuffer(arrayBuffer) is false.\n            // 2. Assert: There are sufficient bytes in arrayBuffer starting at byteIndex to represent a value of type.\n            #[cfg(debug_assertions)]\n            {\n                assert!(buffer.len() >= size_of::<T>());\n                assert_eq!(buffer.addr() % align_of::<T>(), 0);\n            }\n\n            // 3. Let block be arrayBuffer.[[ArrayBufferData]].\n            // 4. Let elementSize be the Element Size value specified in Table 70 for Element Type type.\n            // 5. If IsSharedArrayBuffer(arrayBuffer) is true, then\n            //     a. Let execution be the [[CandidateExecution]] field of the surrounding agent's Agent Record.\n            //     b. Let eventsRecord be the Agent Events Record of execution.[[EventsRecords]] whose [[AgentSignifier]] is AgentSignifier().\n            //     c. If isTypedArray is true and IsNoTearConfiguration(type, order) is true, let noTear be true; otherwise let noTear be false.\n            //     d. Let rawValue be a List of length elementSize whose elements are nondeterministically chosen byte values.\n            //     e. NOTE: In implementations, rawValue is the result of a non-atomic or atomic read instruction on the underlying hardware. The nondeterminism is a semantic prescription of the memory model to describe observable behaviour of hardware with weak consistency.\n            //     f. Let readEvent be ReadSharedMemory { [[Order]]: order, [[NoTear]]: noTear, [[Block]]: block, [[ByteIndex]]: byteIndex, [[ElementSize]]: elementSize }.\n            //     g. Append readEvent to eventsRecord.[[EventList]].\n            //     h. Append Chosen Value Record { [[Event]]: readEvent, [[ChosenValue]]: rawValue } to execution.[[ChosenValues]].\n            // 6. Else,\n            //     a. Let rawValue be a List whose elements are bytes from block at indices in the interval from byteIndex (inclusive) to byteIndex + elementSize (exclusive).\n            // 7. Assert: The number of elements in rawValue is elementSize.\n            // 8. If isLittleEndian is not present, set isLittleEndian to the value of the [[LittleEndian]] field of the surrounding agent's Agent Record.\n            // 9. Return RawBytesToNumeric(type, rawValue, isLittleEndian).\n\n            // SAFETY: The invariants of this operation are ensured by the caller.\n            unsafe { T::read(buffer).load(order) }\n        }\n\n        let buffer = *self;\n\n        // SAFETY: The invariants of this operation are ensured by the caller.\n        unsafe {\n            match kind {\n                TypedArrayKind::Int8 => read_elem::<i8>(buffer, order).into(),\n                TypedArrayKind::Uint8 => read_elem::<u8>(buffer, order).into(),\n                TypedArrayKind::Uint8Clamped => read_elem::<ClampedU8>(buffer, order).into(),\n                TypedArrayKind::Int16 => read_elem::<i16>(buffer, order).into(),\n                TypedArrayKind::Uint16 => read_elem::<u16>(buffer, order).into(),\n                TypedArrayKind::Int32 => read_elem::<i32>(buffer, order).into(),\n                TypedArrayKind::Uint32 => read_elem::<u32>(buffer, order).into(),\n                TypedArrayKind::BigInt64 => read_elem::<i64>(buffer, order).into(),\n                TypedArrayKind::BigUint64 => read_elem::<u64>(buffer, order).into(),\n                #[cfg(feature = \"float16\")]\n                TypedArrayKind::Float16 => {\n                    read_elem::<crate::builtins::typed_array::Float16>(buffer, order).into()\n                }\n                TypedArrayKind::Float32 => read_elem::<f32>(buffer, order).into(),\n                TypedArrayKind::Float64 => read_elem::<f64>(buffer, order).into(),\n            }\n        }\n    }\n\n    /// [`CloneArrayBuffer ( srcBuffer, srcByteOffset, srcLength )`][spec]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-clonearraybuffer\n    pub(crate) fn clone(&self, context: &mut Context) -> JsResult<JsObject<ArrayBuffer>> {\n        // 1. Assert: IsDetachedBuffer(srcBuffer) is false.\n\n        // 2. Let targetBuffer be ? AllocateArrayBuffer(%ArrayBuffer%, srcLength).\n        let target_buffer = ArrayBuffer::allocate(\n            &context\n                .realm()\n                .intrinsics()\n                .constructors()\n                .array_buffer()\n                .constructor()\n                .into(),\n            self.len() as u64,\n            None,\n            context,\n        )?;\n\n        // 3. Let srcBlock be srcBuffer.[[ArrayBufferData]].\n\n        // 4. Let targetBlock be targetBuffer.[[ArrayBufferData]].\n        {\n            let mut target_buffer = target_buffer.borrow_mut();\n            let target_block = target_buffer\n                .data_mut()\n                .bytes_mut()\n                .expect(\"ArrayBuffer cannot be detached here\");\n\n            // 5. Perform CopyDataBlockBytes(targetBlock, 0, srcBlock, srcByteOffset, srcLength).\n\n            // SAFETY: Both buffers are of the same length, `buffer.len()`, which makes this operation\n            // safe.\n            unsafe {\n                memcpy(\n                    self.as_ptr(),\n                    BytesMutPtr::Bytes(target_block.as_mut_ptr()),\n                    self.len(),\n                );\n            }\n        }\n\n        // 6. Return targetBuffer.\n        Ok(target_buffer)\n    }\n}\n\nimpl<'a> From<&'a [u8]> for SliceRef<'a> {\n    fn from(value: &'a [u8]) -> Self {\n        Self::Slice(value)\n    }\n}\n\nimpl<'a> From<&'a [AtomicU8]> for SliceRef<'a> {\n    fn from(value: &'a [AtomicU8]) -> Self {\n        Self::AtomicSlice(value)\n    }\n}\n\n#[derive(Debug)]\npub(crate) enum SliceRefMut<'a> {\n    Slice(&'a mut [u8]),\n    AtomicSlice(&'a [AtomicU8]),\n}\n\nimpl SliceRefMut<'_> {\n    /// Gets the byte length of this `SliceRefMut`.\n    pub(crate) fn len(&self) -> usize {\n        match self {\n            Self::Slice(buf) => buf.len(),\n            Self::AtomicSlice(buf) => buf.len(),\n        }\n    }\n\n    /// Gets a subslice of this `SliceRefMut`.\n    #[expect(unused, reason = \"could still be useful in the future\")]\n    pub(crate) fn subslice<I>(&self, index: I) -> SliceRef<'_>\n    where\n        I: SliceIndex<[u8], Output = [u8]> + SliceIndex<[AtomicU8], Output = [AtomicU8]>,\n    {\n        match self {\n            Self::Slice(buffer) => SliceRef::Slice(buffer.get(index).expect(\"index out of bounds\")),\n            Self::AtomicSlice(buffer) => {\n                SliceRef::AtomicSlice(buffer.get(index).expect(\"index out of bounds\"))\n            }\n        }\n    }\n\n    /// Gets a mutable subslice of this `SliceRefMut`.\n    pub(crate) fn subslice_mut<I>(&mut self, index: I) -> SliceRefMut<'_>\n    where\n        I: SliceIndex<[u8], Output = [u8]> + SliceIndex<[AtomicU8], Output = [AtomicU8]>,\n    {\n        match self {\n            Self::Slice(buffer) => {\n                SliceRefMut::Slice(buffer.get_mut(index).expect(\"index out of bounds\"))\n            }\n            Self::AtomicSlice(buffer) => {\n                SliceRefMut::AtomicSlice(buffer.get(index).expect(\"index out of bounds\"))\n            }\n        }\n    }\n\n    /// Gets the starting address of this `SliceRefMut`.\n    #[cfg(debug_assertions)]\n    pub(crate) fn addr(&self) -> usize {\n        match self {\n            Self::Slice(buf) => buf.as_ptr().addr(),\n            Self::AtomicSlice(buf) => buf.as_ptr().addr(),\n        }\n    }\n\n    /// Gets a pointer to the underlying slice.\n    pub(crate) fn as_ptr(&mut self) -> BytesMutPtr {\n        match self {\n            Self::Slice(s) => BytesMutPtr::Bytes(s.as_mut_ptr()),\n            Self::AtomicSlice(s) => BytesMutPtr::AtomicBytes(s.as_ptr()),\n        }\n    }\n\n    /// `25.1.2.12 SetValueInBuffer ( arrayBuffer, byteIndex, type, value, isTypedArray, order [ , isLittleEndian ] )`\n    ///\n    /// The start offset is determined by the input buffer instead of a `byteIndex` parameter.\n    ///\n    /// # Safety\n    ///\n    /// - There must be enough bytes in `buffer` to write the `TypedArrayElement`.\n    /// - `buffer` must be aligned to the alignment of the `TypedArrayElement`.\n    ///\n    /// # Panics\n    ///\n    /// - Panics if the type of `value` is not equal to the content of `kind`.\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-setvalueinbuffer\n    pub(crate) unsafe fn set_value(&mut self, value: TypedArrayElement, order: Ordering) {\n        unsafe fn write_elem<T: Element>(buffer: SliceRefMut<'_>, value: T, order: Ordering) {\n            // <https://tc39.es/ecma262/#sec-setvalueinbuffer>\n\n            // 1. Assert: IsDetachedBuffer(arrayBuffer) is false.\n            // 2. Assert: There are sufficient bytes in arrayBuffer starting at byteIndex to represent a value of type.\n            // 3. Assert: value is a BigInt if IsBigIntElementType(type) is true; otherwise, value is a Number.\n            #[cfg(debug_assertions)]\n            {\n                assert!(buffer.len() >= size_of::<T>());\n                assert_eq!(buffer.addr() % align_of::<T>(), 0);\n            }\n\n            // 4. Let block be arrayBuffer.[[ArrayBufferData]].\n            // 5. Let elementSize be the Element Size value specified in Table 70 for Element Type type.\n            // 6. If isLittleEndian is not present, set isLittleEndian to the value of the [[LittleEndian]] field of the surrounding agent's Agent Record.\n            // 7. Let rawBytes be NumericToRawBytes(type, value, isLittleEndian).\n            // 8. If IsSharedArrayBuffer(arrayBuffer) is true, then\n            //     a. Let execution be the [[CandidateExecution]] field of the surrounding agent's Agent Record.\n            //     b. Let eventsRecord be the Agent Events Record of execution.[[EventsRecords]] whose [[AgentSignifier]] is AgentSignifier().\n            //     c. If isTypedArray is true and IsNoTearConfiguration(type, order) is true, let noTear be true; otherwise let noTear be false.\n            //     d. Append WriteSharedMemory { [[Order]]: order, [[NoTear]]: noTear, [[Block]]: block, [[ByteIndex]]: byteIndex, [[ElementSize]]: elementSize, [[Payload]]: rawBytes } to eventsRecord.[[EventList]].\n            // 9. Else,\n            //     a. Store the individual bytes of rawBytes into block, starting at block[byteIndex].\n            // 10. Return unused.\n\n            // SAFETY: The invariants of this operation are ensured by the caller.\n            unsafe {\n                T::read_mut(buffer).store(value, order);\n            }\n        }\n\n        // Have to rebind in order to remove the outer `&mut` ref.\n        let buffer = match self {\n            SliceRefMut::Slice(buf) => SliceRefMut::Slice(buf),\n            SliceRefMut::AtomicSlice(buf) => SliceRefMut::AtomicSlice(buf),\n        };\n\n        // SAFETY: The invariants of this operation are ensured by the caller.\n        unsafe {\n            match value {\n                TypedArrayElement::Int8(e) => write_elem(buffer, e, order),\n                TypedArrayElement::Uint8(e) => write_elem(buffer, e, order),\n                TypedArrayElement::Uint8Clamped(e) => write_elem(buffer, e, order),\n                TypedArrayElement::Int16(e) => write_elem(buffer, e, order),\n                TypedArrayElement::Uint16(e) => write_elem(buffer, e, order),\n                TypedArrayElement::Int32(e) => write_elem(buffer, e, order),\n                TypedArrayElement::Uint32(e) => write_elem(buffer, e, order),\n                TypedArrayElement::BigInt64(e) => write_elem(buffer, e, order),\n                TypedArrayElement::BigUint64(e) => write_elem(buffer, e, order),\n                #[cfg(feature = \"float16\")]\n                TypedArrayElement::Float16(e) => write_elem(buffer, e, order),\n                TypedArrayElement::Float32(e) => write_elem(buffer, e, order),\n                TypedArrayElement::Float64(e) => write_elem(buffer, e, order),\n            }\n        }\n    }\n}\n\nimpl<'a> From<&'a mut [u8]> for SliceRefMut<'a> {\n    fn from(value: &'a mut [u8]) -> Self {\n        Self::Slice(value)\n    }\n}\n\nimpl<'a> From<&'a [AtomicU8]> for SliceRefMut<'a> {\n    fn from(value: &'a [AtomicU8]) -> Self {\n        Self::AtomicSlice(value)\n    }\n}\n\nconst BATCH_SIZE: usize = size_of::<u64>();\n\n/// Given a pointer address and total byte count, computes the number of\n/// head bytes needed to reach 8-byte alignment, the number of aligned\n/// 8-byte chunks, and the number of remaining tail bytes.\nfn compute_batch_offsets(ptr_addr: usize, count: usize) -> (usize, usize, usize) {\n    let misalign = ptr_addr % BATCH_SIZE;\n    let head = if misalign == 0 {\n        0\n    } else {\n        (BATCH_SIZE - misalign).min(count)\n    };\n    let remaining = count - head;\n    let chunks = remaining / BATCH_SIZE;\n    let tail = remaining % BATCH_SIZE;\n    (head, chunks, tail)\n}\n\n/// Copies `count` bytes forward from `src` to `dest` using `AtomicU64` for aligned\n/// 8-byte chunks when both pointers share the same misalignment, falling back to\n/// byte-by-byte `AtomicU8` copies otherwise.\n///\n/// # Safety\n///\n/// - `src` must be valid for `count` reads of `AtomicU8`.\n/// - `dest` must be valid for `count` writes of `AtomicU8`.\n/// - The memory regions must not overlap.\nunsafe fn batched_atomic_copy_forward(src: *const AtomicU8, dest: *const AtomicU8, count: usize) {\n    if count == 0 {\n        return;\n    }\n\n    // Batch only if both pointers have the same misalignment, so that aligning\n    // one automatically aligns the other. Otherwise, fall back to byte-by-byte.\n    if (src as usize) % BATCH_SIZE != (dest as usize) % BATCH_SIZE {\n        // SAFETY: ensured by the caller.\n        unsafe {\n            for i in 0..count {\n                (*dest.add(i)).store((*src.add(i)).load(Ordering::Relaxed), Ordering::Relaxed);\n            }\n        }\n        return;\n    }\n\n    let (head, chunks, tail) = compute_batch_offsets(dest as usize, count);\n\n    if chunks == 0 {\n        // SAFETY: ensured by the caller.\n        unsafe {\n            for i in 0..count {\n                (*dest.add(i)).store((*src.add(i)).load(Ordering::Relaxed), Ordering::Relaxed);\n            }\n        }\n        return;\n    }\n\n    // Phase 1: Copy unaligned head bytes until both pointers are 8-byte aligned.\n    // SAFETY: ensured by the caller — both pointers are valid for `count` bytes.\n    unsafe {\n        for i in 0..head {\n            (*dest.add(i)).store((*src.add(i)).load(Ordering::Relaxed), Ordering::Relaxed);\n        }\n    }\n\n    // Verify both pointers are now aligned after Phase 1.\n    #[cfg(debug_assertions)]\n    {\n        debug_assert_eq!((dest as usize + head) % BATCH_SIZE, 0);\n        debug_assert_eq!((src as usize + head) % BATCH_SIZE, 0);\n    }\n\n    // Phase 2: Copy aligned 8-byte chunks using AtomicU64 load/store.\n    // SAFETY: Both `src + head` and `dest + head` are now 8-byte aligned\n    // (same misalignment guaranteed, Phase 1 aligned dest, so src is also aligned).\n    // `AtomicU8` is `#[repr(transparent)]` over `u8`, so casting to `AtomicU64`\n    // is valid when properly aligned.\n    #[allow(clippy::cast_ptr_alignment)]\n    unsafe {\n        let src_u64 = src.add(head).cast::<AtomicU64>();\n        let dest_u64 = dest.add(head).cast::<AtomicU64>();\n        for i in 0..chunks {\n            (*dest_u64.add(i)).store((*src_u64.add(i)).load(Ordering::Relaxed), Ordering::Relaxed);\n        }\n    }\n\n    // Phase 3: Copy remaining tail bytes.\n    let tail_start = head + chunks * BATCH_SIZE;\n    // SAFETY: ensured by the caller — both pointers are valid for `count` bytes.\n    unsafe {\n        for i in 0..tail {\n            (*dest.add(tail_start + i)).store(\n                (*src.add(tail_start + i)).load(Ordering::Relaxed),\n                Ordering::Relaxed,\n            );\n        }\n    }\n}\n\n/// Copies `count` bytes backward from `src` to `dest` using `AtomicU64` for aligned\n/// 8-byte chunks when both pointers share the same misalignment, falling back to\n/// byte-by-byte `AtomicU8` copies otherwise.\n///\n/// # Safety\n///\n/// - `src` must be valid for `count` reads of `AtomicU8`.\n/// - `dest` must be valid for `count` writes of `AtomicU8`.\nunsafe fn batched_atomic_copy_backward(src: *const AtomicU8, dest: *const AtomicU8, count: usize) {\n    if count == 0 {\n        return;\n    }\n\n    // Batch only if both pointers have the same misalignment.\n    if (src as usize) % BATCH_SIZE != (dest as usize) % BATCH_SIZE {\n        // SAFETY: ensured by the caller.\n        unsafe {\n            for i in (0..count).rev() {\n                (*dest.add(i)).store((*src.add(i)).load(Ordering::Relaxed), Ordering::Relaxed);\n            }\n        }\n        return;\n    }\n\n    let (head, chunks, tail) = compute_batch_offsets(dest as usize, count);\n\n    if chunks == 0 {\n        // SAFETY: ensured by the caller.\n        unsafe {\n            for i in (0..count).rev() {\n                (*dest.add(i)).store((*src.add(i)).load(Ordering::Relaxed), Ordering::Relaxed);\n            }\n        }\n        return;\n    }\n\n    let tail_start = head + chunks * BATCH_SIZE;\n\n    // Phase 1: Copy tail bytes backwards.\n    // SAFETY: ensured by the caller — both pointers are valid for `count` bytes.\n    unsafe {\n        for i in (0..tail).rev() {\n            (*dest.add(tail_start + i)).store(\n                (*src.add(tail_start + i)).load(Ordering::Relaxed),\n                Ordering::Relaxed,\n            );\n        }\n    }\n\n    // Verify both pointers are aligned after peeling tail bytes.\n    #[cfg(debug_assertions)]\n    {\n        debug_assert_eq!((dest as usize + tail_start) % BATCH_SIZE, 0);\n        debug_assert_eq!((src as usize + tail_start) % BATCH_SIZE, 0);\n    }\n\n    // Phase 2: Copy aligned 8-byte chunks backwards.\n    // SAFETY: Both `src + head` and `dest + head` are 8-byte aligned\n    // (same misalignment guaranteed, Phase 1 peeled tail bytes).\n    #[allow(clippy::cast_ptr_alignment)]\n    unsafe {\n        let src_u64 = src.add(head).cast::<AtomicU64>();\n        let dest_u64 = dest.add(head).cast::<AtomicU64>();\n        for i in (0..chunks).rev() {\n            (*dest_u64.add(i)).store((*src_u64.add(i)).load(Ordering::Relaxed), Ordering::Relaxed);\n        }\n    }\n\n    // Phase 3: Copy remaining head bytes backwards.\n    // SAFETY: ensured by the caller.\n    unsafe {\n        for i in (0..head).rev() {\n            (*dest.add(i)).store((*src.add(i)).load(Ordering::Relaxed), Ordering::Relaxed);\n        }\n    }\n}\n\n/// Copies `count` bytes from non-atomic `src` to atomic `dest` using `u64`/`AtomicU64`\n/// for aligned 8-byte chunks when both pointers share the same misalignment.\n///\n/// # Safety\n///\n/// - `src` must be valid for `count` reads.\n/// - `dest` must be valid for `count` writes of `AtomicU8`.\n/// - The memory regions must not overlap.\nunsafe fn batched_copy_bytes_to_atomic(src: *const u8, dest: *const AtomicU8, count: usize) {\n    if count == 0 {\n        return;\n    }\n\n    // Batch only if both pointers have the same misalignment.\n    if (src as usize) % BATCH_SIZE != (dest as usize) % BATCH_SIZE {\n        // SAFETY: ensured by the caller.\n        unsafe {\n            for i in 0..count {\n                (*dest.add(i)).store(*src.add(i), Ordering::Relaxed);\n            }\n        }\n        return;\n    }\n\n    let (head, chunks, tail) = compute_batch_offsets(dest as usize, count);\n\n    if chunks == 0 {\n        // SAFETY: ensured by the caller.\n        unsafe {\n            for i in 0..count {\n                (*dest.add(i)).store(*src.add(i), Ordering::Relaxed);\n            }\n        }\n        return;\n    }\n\n    // Phase 1: Head bytes until both pointers are 8-byte aligned.\n    // SAFETY: ensured by the caller.\n    unsafe {\n        for i in 0..head {\n            (*dest.add(i)).store(*src.add(i), Ordering::Relaxed);\n        }\n    }\n\n    // Verify both pointers are now aligned after Phase 1.\n    #[cfg(debug_assertions)]\n    {\n        debug_assert_eq!((dest as usize + head) % BATCH_SIZE, 0);\n        debug_assert_eq!((src as usize + head) % BATCH_SIZE, 0);\n    }\n\n    // Phase 2: Aligned 8-byte chunks.\n    // SAFETY: Both `src + head` and `dest + head` are 8-byte aligned.\n    #[allow(clippy::cast_ptr_alignment)]\n    unsafe {\n        let src_u64 = src.add(head).cast::<u64>();\n        let dest_u64 = dest.add(head).cast::<AtomicU64>();\n        for i in 0..chunks {\n            (*dest_u64.add(i)).store(ptr::read(src_u64.add(i)), Ordering::Relaxed);\n        }\n    }\n\n    // Phase 3: Tail bytes.\n    let tail_start = head + chunks * BATCH_SIZE;\n    // SAFETY: ensured by the caller.\n    unsafe {\n        for i in 0..tail {\n            (*dest.add(tail_start + i)).store(*src.add(tail_start + i), Ordering::Relaxed);\n        }\n    }\n}\n\n/// Copies `count` bytes from atomic `src` to non-atomic `dest` using `AtomicU64`/`u64`\n/// for aligned 8-byte chunks when both pointers share the same misalignment.\n///\n/// # Safety\n///\n/// - `src` must be valid for `count` reads of `AtomicU8`.\n/// - `dest` must be valid for `count` writes.\n/// - The memory regions must not overlap.\nunsafe fn batched_copy_atomic_to_bytes(src: *const AtomicU8, dest: *mut u8, count: usize) {\n    if count == 0 {\n        return;\n    }\n\n    // Batch only if both pointers have the same misalignment.\n    if (src as usize) % BATCH_SIZE != (dest as usize) % BATCH_SIZE {\n        // SAFETY: ensured by the caller.\n        unsafe {\n            for i in 0..count {\n                *dest.add(i) = (*src.add(i)).load(Ordering::Relaxed);\n            }\n        }\n        return;\n    }\n\n    let (head, chunks, tail) = compute_batch_offsets(src as usize, count);\n\n    if chunks == 0 {\n        // SAFETY: ensured by the caller.\n        unsafe {\n            for i in 0..count {\n                *dest.add(i) = (*src.add(i)).load(Ordering::Relaxed);\n            }\n        }\n        return;\n    }\n\n    // Phase 1: Head bytes until both pointers are 8-byte aligned.\n    // SAFETY: ensured by the caller.\n    unsafe {\n        for i in 0..head {\n            *dest.add(i) = (*src.add(i)).load(Ordering::Relaxed);\n        }\n    }\n\n    // Verify both pointers are now aligned after Phase 1.\n    #[cfg(debug_assertions)]\n    {\n        debug_assert_eq!((src as usize + head) % BATCH_SIZE, 0);\n        debug_assert_eq!((dest as usize + head) % BATCH_SIZE, 0);\n    }\n\n    // Phase 2: Aligned 8-byte chunks.\n    // SAFETY: Both `src + head` and `dest + head` are 8-byte aligned.\n    #[allow(clippy::cast_ptr_alignment)]\n    unsafe {\n        let src_u64 = src.add(head).cast::<AtomicU64>();\n        let dest_u64 = dest.add(head).cast::<u64>();\n        for i in 0..chunks {\n            ptr::write(dest_u64.add(i), (*src_u64.add(i)).load(Ordering::Relaxed));\n        }\n    }\n\n    // Phase 3: Tail bytes.\n    let tail_start = head + chunks * BATCH_SIZE;\n    // SAFETY: ensured by the caller.\n    unsafe {\n        for i in 0..tail {\n            *dest.add(tail_start + i) = (*src.add(tail_start + i)).load(Ordering::Relaxed);\n        }\n    }\n}\n\n/// Copies `count` bytes from `src` into `dest` using atomic relaxed loads and stores.\n///\n/// Uses `AtomicU64` for aligned 8-byte chunks and falls back to `AtomicU8` for\n/// unaligned head/tail bytes.\n///\n/// # Safety\n///\n/// - Both `src` and `dest` must have at least `count` bytes to read and write,\n///   respectively.\npub(super) unsafe fn copy_shared_to_shared(\n    src: *const AtomicU8,\n    dest: *const AtomicU8,\n    count: usize,\n) {\n    // SAFETY: The invariants of this operation are ensured by the caller.\n    unsafe { batched_atomic_copy_forward(src, dest, count) }\n}\n\n/// Copies `count` bytes backwards from `src` into `dest` using atomic relaxed loads and stores.\n///\n/// Uses `AtomicU64` for aligned 8-byte chunks and falls back to `AtomicU8` for\n/// unaligned head/tail bytes.\n///\n/// # Safety\n///\n/// - Both `src` and `dest` must have at least `count` bytes to read and write,\n///   respectively.\nunsafe fn copy_shared_to_shared_backwards(\n    src: *const AtomicU8,\n    dest: *const AtomicU8,\n    count: usize,\n) {\n    // SAFETY: The invariants of this operation are ensured by the caller.\n    unsafe { batched_atomic_copy_backward(src, dest, count) }\n}\n\n/// Copies `count` bytes from the buffer `src` into the buffer `dest`, using the atomic ordering\n/// `Ordering::Relaxed` if any of the buffers are atomic.\n///\n/// # Safety\n///\n/// - Both `src` and `dest` must have at least `count` bytes to read and write, respectively.\n/// - The region of memory referenced by `src` must not overlap with the region of memory\n///   referenced by `dest`.\npub(crate) unsafe fn memcpy(src: BytesConstPtr, dest: BytesMutPtr, count: usize) {\n    match (src, dest) {\n        // SAFETY: The invariants of this operation are ensured by the caller of the function.\n        (BytesConstPtr::Bytes(src), BytesMutPtr::Bytes(dest)) => unsafe {\n            ptr::copy_nonoverlapping(src, dest, count);\n        },\n        // SAFETY: The invariants of this operation are ensured by the caller of the function.\n        (BytesConstPtr::Bytes(src), BytesMutPtr::AtomicBytes(dest)) => unsafe {\n            batched_copy_bytes_to_atomic(src, dest, count);\n        },\n        // SAFETY: The invariants of this operation are ensured by the caller of the function.\n        (BytesConstPtr::AtomicBytes(src), BytesMutPtr::Bytes(dest)) => unsafe {\n            batched_copy_atomic_to_bytes(src, dest, count);\n        },\n        // SAFETY: The invariants of this operation are ensured by the caller of the function.\n        (BytesConstPtr::AtomicBytes(src), BytesMutPtr::AtomicBytes(dest)) => unsafe {\n            copy_shared_to_shared(src, dest, count);\n        },\n    }\n}\n\n/// Copies `count` bytes from the position `from` to the position `to` in `buffer`, but always\n/// copying from left to right.\n///\n///\n/// # Safety\n///\n/// - `ptr` must be valid from the offset `ptr + from` for `count` reads of bytes.\n/// - `ptr` must be valid from the offset `ptr + to` for `count` writes of bytes.\n// This looks like a worse version of `memmove`... and it is exactly that...\n// but it's the correct behaviour for a weird usage of `%TypedArray%.prototype.slice` so ¯\\_(ツ)_/¯.\n// Obviously don't use this if you need to implement something that requires a \"proper\" memmove.\npub(crate) unsafe fn memmove_naive(ptr: BytesMutPtr, from: usize, to: usize, count: usize) {\n    match ptr {\n        // SAFETY: The invariants of this operation are ensured by the caller of the function.\n        BytesMutPtr::Bytes(ptr) => unsafe {\n            for i in 0..count {\n                ptr::copy(ptr.add(from + i), ptr.add(to + i), 1);\n            }\n        },\n        // SAFETY: The invariants of this operation are ensured by the caller of the function.\n        BytesMutPtr::AtomicBytes(ptr) => unsafe {\n            let src = ptr.add(from);\n            let dest = ptr.add(to);\n            copy_shared_to_shared(src, dest, count);\n        },\n    }\n}\n\n/// Copies `count` bytes from the position `from` to the position `to` in `buffer`.\n///\n/// # Safety\n///\n/// - `ptr` must be valid from the offset `ptr + from` for `count` reads of bytes.\n/// - `ptr` must be valid from the offset `ptr + to` for `count` writes of bytes.\npub(crate) unsafe fn memmove(ptr: BytesMutPtr, from: usize, to: usize, count: usize) {\n    match ptr {\n        // SAFETY: The invariants of this operation are ensured by the caller of the function.\n        BytesMutPtr::Bytes(ptr) => unsafe {\n            let src = ptr.add(from);\n            let dest = ptr.add(to);\n            ptr::copy(src, dest, count);\n        },\n        // SAFETY: The invariants of this operation are ensured by the caller of the function.\n        BytesMutPtr::AtomicBytes(ptr) => unsafe {\n            let src = ptr.add(from);\n            let dest = ptr.add(to);\n            // Let's draw a simple array.\n            //\n            // | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 |\n            //\n            // Now let's define `from`, `to` and `count` such that the below condition is satisfied.\n            // `from = 0`\n            // `to = 2`\n            // `count = 4`\n            //\n            // We can now imagine that the array is pointed to by our indices:\n            //\n            // | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 |\n            //   ^       ^\n            // from     to\n            //\n            // If we start copying bytes until `from + 2 = to`, we can see that the new array would be:\n            //\n            // | 0 | 1 | 0 | 1 | 0 | 5 | 6 | 7 | 8 |\n            //           ^       ^\n            //    from + 2       to + 2\n            //\n            // However, we've lost the data that was in the index 2! If this process\n            // continues, this'll give the incorrect result:\n            //\n            // | 0 | 1 | 0 | 1 | 0 | 1 | 6 | 7 | 8 |\n            //\n            // To solve this, we just need to copy backwards to ensure we never override data that\n            // we need in next iterations:\n            //\n            // | 0 | 1 | 2 | 3 | 4 | 3 | 6 | 7 | 8 |\n            //               ^       ^\n            //            from      to\n            //\n            // | 0 | 1 | 2 | 3 | 2 | 3 | 6 | 7 | 8 |\n            //           ^       ^\n            //        from      to\n            //\n            // | 0 | 1 | 0 | 1 | 2 | 3 | 6 | 7 | 8 |\n            //   ^       ^\n            // from     to\n            if src < dest {\n                copy_shared_to_shared_backwards(src, dest, count);\n            } else {\n                copy_shared_to_shared(src, dest, count);\n            }\n        },\n    }\n}\n\n#[cfg(test)]\nmod tests_miri {\n    use super::*;\n    use portable_atomic::AtomicU8;\n    use std::sync::atomic::Ordering;\n\n    /// Tests `batched_atomic_copy_forward` with misaligned pointers\n    /// (different misalignment) to exercise the byte-by-byte fallback.\n    #[test]\n    fn batched_forward_misaligned_fallback() {\n        let src_data: Vec<AtomicU8> = (0..32).map(|i| AtomicU8::new(i as u8)).collect();\n        let dest_data: Vec<AtomicU8> = (0..32).map(|_| AtomicU8::new(0)).collect();\n\n        // SAFETY: Vec has 32 elements, offset 1 and 2 are within bounds.\n        let src = unsafe { src_data.as_ptr().add(1) };\n        // SAFETY: Vec has 32 elements, offset 2 is within bounds.\n        let dest = unsafe { dest_data.as_ptr().add(2) };\n        let count = 20;\n\n        // SAFETY: Both pointers are valid for 20 reads/writes (32 - max_offset = 30 >= 20).\n        unsafe { batched_atomic_copy_forward(src, dest, count) };\n\n        for i in 0..count {\n            // SAFETY: `src` is valid for `count` reads starting from its base.\n            let expected = unsafe { (*src.add(i)).load(Ordering::Relaxed) };\n            // SAFETY: `dest` is valid for `count` reads starting from its base.\n            let actual = unsafe { (*dest.add(i)).load(Ordering::Relaxed) };\n            assert_eq!(actual, expected, \"mismatch at index {i}\");\n        }\n    }\n\n    /// Tests `batched_atomic_copy_backward` with misaligned pointers.\n    #[test]\n    fn batched_backward_misaligned_fallback() {\n        let src_data: Vec<AtomicU8> = (0..32).map(|i| AtomicU8::new(i as u8)).collect();\n        let dest_data: Vec<AtomicU8> = (0..32).map(|_| AtomicU8::new(0)).collect();\n\n        // SAFETY: Vec has 32 elements, offset 1 is within bounds.\n        let src = unsafe { src_data.as_ptr().add(1) };\n        // SAFETY: Vec has 32 elements, offset 2 is within bounds.\n        let dest = unsafe { dest_data.as_ptr().add(2) };\n        let count = 20;\n\n        // SAFETY: Both pointers are valid for 20 reads/writes (32 - max_offset = 30 >= 20).\n        unsafe { batched_atomic_copy_backward(src, dest, count) };\n\n        for i in 0..count {\n            // SAFETY: `src` is valid for `count` reads starting from its base.\n            let expected = unsafe { (*src.add(i)).load(Ordering::Relaxed) };\n            // SAFETY: `dest` is valid for `count` reads starting from its base.\n            let actual = unsafe { (*dest.add(i)).load(Ordering::Relaxed) };\n            assert_eq!(actual, expected, \"mismatch at index {i}\");\n        }\n    }\n\n    /// Tests `batched_copy_bytes_to_atomic` with misaligned pointers.\n    #[test]\n    fn batched_bytes_to_atomic_misaligned_fallback() {\n        let src_data: Vec<u8> = (0..32).map(|i| i as u8).collect();\n        let dest_data: Vec<AtomicU8> = (0..32).map(|_| AtomicU8::new(0)).collect();\n\n        // SAFETY: Vec has 32 elements, offset 1 is within bounds.\n        let src = unsafe { src_data.as_ptr().add(1) };\n        // SAFETY: Vec has 32 elements, offset 2 is within bounds.\n        let dest = unsafe { dest_data.as_ptr().add(2) };\n        let count = 20;\n\n        // SAFETY: Both pointers are valid for 20 reads/writes, regions do not overlap.\n        unsafe { batched_copy_bytes_to_atomic(src, dest, count) };\n\n        for i in 0..count {\n            // SAFETY: `src` is valid for `count` reads starting from its base.\n            let expected = unsafe { *src.add(i) };\n            // SAFETY: `dest` is valid for `count` reads starting from its base.\n            let actual = unsafe { (*dest.add(i)).load(Ordering::Relaxed) };\n            assert_eq!(actual, expected, \"mismatch at index {i}\");\n        }\n    }\n\n    /// Tests `batched_copy_atomic_to_bytes` with misaligned pointers.\n    #[test]\n    fn batched_atomic_to_bytes_misaligned_fallback() {\n        let src_data: Vec<AtomicU8> = (0..32).map(|i| AtomicU8::new(i as u8)).collect();\n        let mut dest_data: Vec<u8> = vec![0u8; 32];\n\n        // SAFETY: Vec has 32 elements, offset 1 is within bounds.\n        let src = unsafe { src_data.as_ptr().add(1) };\n        // SAFETY: Vec has 32 elements, offset 2 is within bounds.\n        let dest = unsafe { dest_data.as_mut_ptr().add(2) };\n        let count = 20;\n\n        // SAFETY: Both pointers are valid for 20 reads/writes, regions do not overlap.\n        unsafe { batched_copy_atomic_to_bytes(src, dest, count) };\n\n        for i in 0..count {\n            // SAFETY: `src` is valid for `count` reads starting from its base.\n            let expected = unsafe { (*src.add(i)).load(Ordering::Relaxed) };\n            // SAFETY: `dest` is valid for `count` reads starting from its base.\n            let actual = unsafe { *dest.add(i) };\n            assert_eq!(actual, expected, \"mismatch at index {i}\");\n        }\n    }\n}\n"
  },
  {
    "path": "core/engine/src/builtins/async_function/mod.rs",
    "content": "//! Boa's implementation of ECMAScript's global `AsyncFunction` object.\n//!\n//! More information:\n//!  - [ECMAScript reference][spec]\n//!  - [MDN documentation][mdn]\n//!\n//! [spec]: https://tc39.es/ecma262/#sec-async-function-objects\n//! [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/AsyncFunction\n\nuse crate::{\n    Context, JsResult, JsString, JsValue,\n    builtins::{BuiltInObject, function::BuiltInFunctionObject},\n    context::intrinsics::{Intrinsics, StandardConstructor, StandardConstructors},\n    property::Attribute,\n    realm::Realm,\n    string::StaticJsStrings,\n    symbol::JsSymbol,\n};\n\nuse super::{BuiltInBuilder, BuiltInConstructor, IntrinsicObject};\n\n/// The internal representation of an `AsyncFunction` object.\n#[derive(Debug, Clone, Copy)]\npub struct AsyncFunction;\n\nimpl IntrinsicObject for AsyncFunction {\n    fn init(realm: &Realm) {\n        BuiltInBuilder::from_standard_constructor::<Self>(realm)\n            .prototype(realm.intrinsics().constructors().function().constructor())\n            .inherits(Some(\n                realm.intrinsics().constructors().function().prototype(),\n            ))\n            .property(\n                JsSymbol::to_string_tag(),\n                Self::NAME,\n                Attribute::CONFIGURABLE,\n            )\n            .build();\n    }\n\n    fn get(intrinsics: &Intrinsics) -> crate::object::JsObject {\n        Self::STANDARD_CONSTRUCTOR(intrinsics.constructors()).constructor()\n    }\n}\n\nimpl BuiltInObject for AsyncFunction {\n    const NAME: JsString = StaticJsStrings::ASYNC_FUNCTION;\n}\n\nimpl BuiltInConstructor for AsyncFunction {\n    const CONSTRUCTOR_ARGUMENTS: usize = 1;\n    const PROTOTYPE_STORAGE_SLOTS: usize = 1;\n    const CONSTRUCTOR_STORAGE_SLOTS: usize = 0;\n\n    const STANDARD_CONSTRUCTOR: fn(&StandardConstructors) -> &StandardConstructor =\n        StandardConstructors::async_function;\n\n    /// `AsyncFunction ( p1, p2, … , pn, body )`\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-async-function-constructor-arguments\n    fn constructor(\n        new_target: &JsValue,\n        args: &[JsValue],\n        context: &mut Context,\n    ) -> JsResult<JsValue> {\n        let active_function = context.active_function_object().unwrap_or_else(|| {\n            context\n                .intrinsics()\n                .constructors()\n                .async_function()\n                .constructor()\n        });\n        BuiltInFunctionObject::create_dynamic_function(\n            active_function,\n            new_target,\n            args,\n            true,\n            false,\n            context,\n        )\n        .map(Into::into)\n    }\n}\n"
  },
  {
    "path": "core/engine/src/builtins/async_generator/mod.rs",
    "content": "//! Boa's implementation of ECMAScript's global `AsyncGenerator` object.\n//!\n//! More information:\n//!  - [ECMAScript reference][spec]\n//!\n//! [spec]: https://tc39.es/ecma262/#sec-asyncgenerator-objects\n\nuse crate::{\n    Context, JsArgs, JsData, JsError, JsExpect, JsResult, JsString,\n    builtins::{\n        Promise,\n        generator::GeneratorContext,\n        iterable::create_iter_result_object,\n        promise::{PromiseCapability, if_abrupt_reject_promise},\n    },\n    context::intrinsics::Intrinsics,\n    error::JsNativeError,\n    js_string,\n    native_function::NativeFunction,\n    object::{CONSTRUCTOR, FunctionObjectBuilder, JsObject},\n    property::Attribute,\n    realm::Realm,\n    string::StaticJsStrings,\n    symbol::JsSymbol,\n    value::JsValue,\n    vm::{CompletionRecord, GeneratorResumeKind},\n};\nuse boa_gc::{Finalize, Trace};\nuse std::collections::VecDeque;\n\nuse super::{BuiltInBuilder, IntrinsicObject};\n\n/// Indicates the state of an async generator.\n#[derive(Debug, Clone, Copy, PartialEq)]\npub(crate) enum AsyncGeneratorState {\n    SuspendedStart,\n    SuspendedYield,\n    Executing,\n    DrainingQueue,\n    Completed,\n}\n\n/// `AsyncGeneratorRequest Records`\n///\n/// More information:\n///  - [ECMAScript reference][spec]\n///\n/// [spec]: https://tc39.es/ecma262/#sec-asyncgeneratorrequest-records\n#[derive(Debug, Clone, Finalize, Trace)]\npub(crate) struct AsyncGeneratorRequest {\n    /// The `[[Completion]]` slot.\n    pub(crate) completion: CompletionRecord,\n\n    /// The `[[Capability]]` slot.\n    capability: PromiseCapability,\n}\n\n/// The internal representation of an `AsyncGenerator` object.\n#[derive(Debug, Finalize, Trace, JsData)]\npub struct AsyncGenerator {\n    /// The `[[AsyncGeneratorState]]` internal slot.\n    #[unsafe_ignore_trace]\n    pub(crate) state: AsyncGeneratorState,\n\n    /// The `[[AsyncGeneratorContext]]` internal slot.\n    pub(crate) context: Option<GeneratorContext>,\n\n    /// The `[[AsyncGeneratorQueue]]` internal slot.\n    pub(crate) queue: VecDeque<AsyncGeneratorRequest>,\n}\n\nimpl IntrinsicObject for AsyncGenerator {\n    fn init(realm: &Realm) {\n        BuiltInBuilder::with_intrinsic::<Self>(realm)\n            .prototype(\n                realm\n                    .intrinsics()\n                    .objects()\n                    .iterator_prototypes()\n                    .async_iterator(),\n            )\n            .static_method(Self::next, js_string!(\"next\"), 1)\n            .static_method(Self::r#return, js_string!(\"return\"), 1)\n            .static_method(Self::throw, js_string!(\"throw\"), 1)\n            .static_property(\n                JsSymbol::to_string_tag(),\n                Self::NAME,\n                Attribute::CONFIGURABLE,\n            )\n            .static_property(\n                CONSTRUCTOR,\n                realm\n                    .intrinsics()\n                    .constructors()\n                    .async_generator_function()\n                    .prototype(),\n                Attribute::CONFIGURABLE,\n            )\n            .build();\n    }\n\n    fn get(intrinsics: &Intrinsics) -> JsObject {\n        intrinsics.objects().async_generator()\n    }\n}\n\nimpl AsyncGenerator {\n    const NAME: JsString = StaticJsStrings::ASYNC_GENERATOR;\n\n    /// `AsyncGenerator.prototype.next ( value )`\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-asyncgenerator-prototype-next\n    pub(crate) fn next(\n        this: &JsValue,\n        args: &[JsValue],\n        context: &mut Context,\n    ) -> JsResult<JsValue> {\n        // 1. Let generator be the this value.\n        let generator = this;\n\n        // 2. Let promiseCapability be ! NewPromiseCapability(%Promise%).\n        let promise_capability = PromiseCapability::new(\n            &context.intrinsics().constructors().promise().constructor(),\n            context,\n        )\n        .js_expect(\"cannot fail with promise constructor\")?;\n\n        // 3. Let result be Completion(AsyncGeneratorValidate(generator, empty)).\n        // 4. IfAbruptRejectPromise(result, promiseCapability).\n        let result: JsResult<_> = generator.as_object().ok_or_else(|| {\n            JsNativeError::typ()\n                .with_message(\"generator resumed on non generator object\")\n                .into()\n        });\n        let generator = if_abrupt_reject_promise!(result, promise_capability, context);\n        let result: JsResult<_> = generator.clone().downcast::<Self>().map_err(|_| {\n            JsNativeError::typ()\n                .with_message(\"generator resumed on non generator object\")\n                .into()\n        });\n        let generator = if_abrupt_reject_promise!(result, promise_capability, context);\n\n        // 5. Let state be generator.[[AsyncGeneratorState]].\n        let state = generator.borrow().data().state;\n\n        // 6. If state is completed, then\n        if state == AsyncGeneratorState::Completed {\n            // a. Let iteratorResult be CreateIterResultObject(undefined, true).\n            let iterator_result = create_iter_result_object(JsValue::undefined(), true, context);\n\n            // b. Perform ! Call(promiseCapability.[[Resolve]], undefined, « iteratorResult »).\n            promise_capability.resolve().call(\n                &JsValue::undefined(),\n                &[iterator_result],\n                context,\n            )?;\n\n            // c. Return promiseCapability.[[Promise]].\n            return Ok(promise_capability.promise().clone().into());\n        }\n\n        // 7. Let completion be NormalCompletion(value).\n        let completion = CompletionRecord::Normal(args.get_or_undefined(0).clone());\n\n        // 8. Perform AsyncGeneratorEnqueue(generator, completion, promiseCapability).\n        Self::enqueue(&generator, completion.clone(), promise_capability.clone());\n\n        // 9. If state is either suspendedStart or suspendedYield, then\n        if state == AsyncGeneratorState::SuspendedStart\n            || state == AsyncGeneratorState::SuspendedYield\n        {\n            // a. Perform AsyncGeneratorResume(generator, completion).\n            Self::resume(&generator, completion, context)?;\n        }\n\n        // 11. Return promiseCapability.[[Promise]].\n        Ok(promise_capability.promise().clone().into())\n    }\n\n    /// `AsyncGenerator.prototype.return ( value )`\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-asyncgenerator-prototype-return\n    pub(crate) fn r#return(\n        this: &JsValue,\n        args: &[JsValue],\n        context: &mut Context,\n    ) -> JsResult<JsValue> {\n        // 1. Let generator be the this value.\n        let generator = this;\n\n        // 2. Let promiseCapability be ! NewPromiseCapability(%Promise%).\n        let promise_capability = PromiseCapability::new(\n            &context.intrinsics().constructors().promise().constructor(),\n            context,\n        )\n        .js_expect(\"cannot fail with promise constructor\")?;\n\n        // 3. Let result be Completion(AsyncGeneratorValidate(generator, empty)).\n        // 4. IfAbruptRejectPromise(result, promiseCapability).\n        let result: JsResult<_> = generator.as_object().ok_or_else(|| {\n            JsNativeError::typ()\n                .with_message(\"generator resumed on non generator object\")\n                .into()\n        });\n        let generator_object = if_abrupt_reject_promise!(result, promise_capability, context);\n        let result: JsResult<_> = generator_object.clone().downcast::<Self>().map_err(|_| {\n            JsNativeError::typ()\n                .with_message(\"generator resumed on non generator object\")\n                .into()\n        });\n        let generator = if_abrupt_reject_promise!(result, promise_capability, context);\n\n        // 5. Let completion be Completion Record { [[Type]]: return, [[Value]]: value, [[Target]]: empty }.\n        let return_value = args.get_or_undefined(0).clone();\n        let completion = CompletionRecord::Return(return_value.clone());\n\n        // 6. Perform AsyncGeneratorEnqueue(generator, completion, promiseCapability).\n        Self::enqueue(&generator, completion.clone(), promise_capability.clone());\n\n        // 7. Let state be generator.[[AsyncGeneratorState]].\n        let state = generator.borrow().data().state;\n\n        // 8. If state is either suspended-start or completed, then\n        if state == AsyncGeneratorState::SuspendedStart || state == AsyncGeneratorState::Completed {\n            // a. Set generator.[[AsyncGeneratorState]] to draining-queue.\n            generator.borrow_mut().data_mut().state = AsyncGeneratorState::DrainingQueue;\n\n            // b. Perform ! AsyncGeneratorAwaitReturn(generator).\n            Self::await_return(&generator, return_value, context)?;\n        }\n        // 9. Else if state is suspended-yield, then\n        else if state == AsyncGeneratorState::SuspendedYield {\n            // a. Perform AsyncGeneratorResume(generator, completion).\n            Self::resume(&generator, completion, context)?;\n        }\n        // 10. Else,\n        //     a. Assert: state is either executing or draining-queue.\n\n        // 11. Return promiseCapability.[[Promise]].\n        Ok(promise_capability.promise().clone().into())\n    }\n\n    /// `AsyncGenerator.prototype.throw ( exception )`\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-asyncgenerator-prototype-throw\n    pub(crate) fn throw(\n        this: &JsValue,\n        args: &[JsValue],\n        context: &mut Context,\n    ) -> JsResult<JsValue> {\n        // 1. Let generator be the this value.\n        let generator = this;\n\n        // 2. Let promiseCapability be ! NewPromiseCapability(%Promise%).\n        let promise_capability = PromiseCapability::new(\n            &context.intrinsics().constructors().promise().constructor(),\n            context,\n        )\n        .js_expect(\"cannot fail with promise constructor\")?;\n\n        // 3. Let result be Completion(AsyncGeneratorValidate(generator, empty)).\n        // 4. IfAbruptRejectPromise(result, promiseCapability).\n        let result: JsResult<_> = generator.as_object().ok_or_else(|| {\n            JsNativeError::typ()\n                .with_message(\"generator resumed on non generator object\")\n                .into()\n        });\n        let generator_object = if_abrupt_reject_promise!(result, promise_capability, context);\n        let result: JsResult<_> = generator_object.clone().downcast::<Self>().map_err(|_| {\n            JsNativeError::typ()\n                .with_message(\"generator resumed on non generator object\")\n                .into()\n        });\n        let generator = if_abrupt_reject_promise!(result, promise_capability, context);\n        let mut r#gen = generator.borrow_mut();\n\n        // 5. Let state be generator.[[AsyncGeneratorState]].\n        let mut state = r#gen.data().state;\n\n        // 6. If state is suspendedStart, then\n        if state == AsyncGeneratorState::SuspendedStart {\n            // a. Set generator.[[AsyncGeneratorState]] to completed.\n            r#gen.data_mut().state = AsyncGeneratorState::Completed;\n            r#gen.data_mut().context = None;\n\n            // b. Set state to completed.\n            state = AsyncGeneratorState::Completed;\n        }\n\n        drop(r#gen);\n\n        // 7. If state is completed, then\n        if state == AsyncGeneratorState::Completed {\n            // a. Perform ! Call(promiseCapability.[[Reject]], undefined, « exception »).\n            promise_capability.reject().call(\n                &JsValue::undefined(),\n                &[args.get_or_undefined(0).clone()],\n                context,\n            )?;\n\n            // b. Return promiseCapability.[[Promise]].\n            return Ok(promise_capability.promise().clone().into());\n        }\n\n        // 8. Let completion be ThrowCompletion(exception).\n        let completion =\n            CompletionRecord::Throw(JsError::from_opaque(args.get_or_undefined(0).clone()));\n\n        // 9. Perform AsyncGeneratorEnqueue(generator, completion, promiseCapability).\n        Self::enqueue(&generator, completion.clone(), promise_capability.clone());\n\n        // 10. If state is suspended-yield, then\n        if state == AsyncGeneratorState::SuspendedYield {\n            // a. Perform AsyncGeneratorResume(generator, completion).\n            Self::resume(&generator, completion, context)?;\n        }\n\n        // 11. Else,\n        //     a. Assert: state is either executing or draining-queue.\n\n        // 12. Return promiseCapability.[[Promise]].\n        Ok(promise_capability.promise().clone().into())\n    }\n\n    /// `AsyncGeneratorEnqueue ( generator, completion, promiseCapability )`\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-asyncgeneratorenqueue\n    pub(crate) fn enqueue(\n        generator: &JsObject<AsyncGenerator>,\n        completion: CompletionRecord,\n        promise_capability: PromiseCapability,\n    ) {\n        let mut r#gen = generator.borrow_mut();\n        // 1. Let request be AsyncGeneratorRequest { [[Completion]]: completion, [[Capability]]: promiseCapability }.\n        let request = AsyncGeneratorRequest {\n            completion,\n            capability: promise_capability,\n        };\n\n        // 2. Append request to the end of generator.[[AsyncGeneratorQueue]].\n        r#gen.data_mut().queue.push_back(request);\n    }\n\n    /// `AsyncGeneratorCompleteStep ( generator, completion, done [ , realm ] )`\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///\n    /// # Errors\n    ///\n    /// Returns `EngineError::Panic` if the async generator request queue of `generator` is empty.\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-asyncgeneratorcompletestep\n    pub(crate) fn complete_step(\n        generator: &JsObject<AsyncGenerator>,\n        completion: JsResult<JsValue>,\n        done: bool,\n        realm: Option<Realm>,\n        context: &mut Context,\n    ) -> JsResult<()> {\n        // 1. Assert: generator.[[AsyncGeneratorQueue]] is not empty.\n        // 2. Let next be the first element of generator.[[AsyncGeneratorQueue]].\n        // 3. Remove the first element from generator.[[AsyncGeneratorQueue]].\n        let next = generator\n            .borrow_mut()\n            .data_mut()\n            .queue\n            .pop_front()\n            .js_expect(\"1. Assert: generator.[[AsyncGeneratorQueue]] is not empty.\")?;\n\n        // 4. Let promiseCapability be next.[[Capability]].\n        let promise_capability = &next.capability;\n\n        // 5. Let value be completion.[[Value]].\n        match completion {\n            // 6. If completion is a throw completion, then\n            Err(e) => {\n                // a. Perform ! Call(promiseCapability.[[Reject]], undefined, « value »).\n                promise_capability.reject().call(\n                    &JsValue::undefined(),\n                    &[e.into_opaque(context)?],\n                    context,\n                )?;\n            }\n\n            // 7. Else,\n            Ok(value) => {\n                // a. Assert: completion is a normal completion.\n                // b. If realm is present, then\n                let iterator_result = if let Some(realm) = realm {\n                    // i. Let oldRealm be the running execution context's Realm.\n                    // ii. Set the running execution context's Realm to realm.\n                    let old_realm = context.enter_realm(realm);\n\n                    // iii. Let iteratorResult be CreateIteratorResultObject(value, done).\n                    let iterator_result = create_iter_result_object(value, done, context);\n\n                    // iv. Set the running execution context's Realm to oldRealm.\n                    context.enter_realm(old_realm);\n\n                    iterator_result\n                } else {\n                    // c. Else,\n                    //     i. Let iteratorResult be CreateIteratorResultObject(value, done).\n                    create_iter_result_object(value, done, context)\n                };\n\n                // d. Perform ! Call(promiseCapability.[[Resolve]], undefined, « iteratorResult »).\n                promise_capability.resolve().call(\n                    &JsValue::undefined(),\n                    &[iterator_result],\n                    context,\n                )?;\n            }\n        }\n        // 8. Return unused.\n        Ok(())\n    }\n\n    /// `AsyncGeneratorResume ( generator, completion )`\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///\n    /// # Errors\n    ///\n    /// Returns `EngineError::Panic` if `generator` is neither in the `SuspendedStart` nor in the `SuspendedYield` states.\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-asyncgeneratorresume\n    pub(crate) fn resume(\n        generator: &JsObject<AsyncGenerator>,\n        completion: CompletionRecord,\n        context: &mut Context,\n    ) -> JsResult<()> {\n        // 1. Assert: generator.[[AsyncGeneratorState]] is either suspended-start or suspended-yield.\n        assert!(matches!(\n            generator.borrow().data().state,\n            AsyncGeneratorState::SuspendedStart | AsyncGeneratorState::SuspendedYield\n        ));\n\n        // 2. Let genContext be generator.[[AsyncGeneratorContext]].\n        let mut generator_context = generator\n            .borrow_mut()\n            .data_mut()\n            .context\n            .take()\n            .js_expect(\"generator context cannot be empty here\")?;\n\n        // 5. Set generator.[[AsyncGeneratorState]] to executing.\n        generator.borrow_mut().data_mut().state = AsyncGeneratorState::Executing;\n\n        let (value, resume_kind) = match completion {\n            CompletionRecord::Normal(val) => (val, GeneratorResumeKind::Normal),\n            CompletionRecord::Return(val) => (val, GeneratorResumeKind::Return),\n            CompletionRecord::Throw(err) => (err.into_opaque(context)?, GeneratorResumeKind::Throw),\n        };\n\n        // 3. Let callerContext be the running execution context.\n        // 4. Suspend callerContext.\n        // 6. Push genContext onto the execution context stack; genContext is now the running execution context.\n        let result = generator_context.resume(Some(value), resume_kind, context);\n\n        // 7. Resume the suspended evaluation of genContext using completion as the result of the operation that suspended it. Let result be the Completion Record returned by the resumed computation.\n        generator.borrow_mut().data_mut().context = Some(generator_context);\n\n        // 8. Assert: result is never an abrupt completion.\n        assert!(!result.is_throw_completion());\n\n        // 9. Assert: When we return here, genContext has already been removed from the execution context stack and\n        //    callerContext is the currently running execution context.\n        // 10. Return unused.\n        Ok(())\n    }\n\n    /// `AsyncGeneratorAwaitReturn ( generator )`\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///\n    /// # Errors\n    ///\n    /// Returns `EngineError::Panic` if `generator` is not in the `DrainingQueue` state.\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-asyncgeneratorawaitreturn\n    pub(crate) fn await_return(\n        generator: &JsObject<AsyncGenerator>,\n        value: JsValue,\n        context: &mut Context,\n    ) -> JsResult<()> {\n        // 1. Assert: generator.[[AsyncGeneratorState]] is draining-queue.\n        assert_eq!(\n            generator.borrow().data().state,\n            AsyncGeneratorState::DrainingQueue\n        );\n\n        // 2. Let queue be generator.[[AsyncGeneratorQueue]].\n        // 3. Assert: queue is not empty.\n        // 4. Let next be the first element of queue.\n        // 5. Let completion be Completion(next.[[Completion]]).\n        // 6. Assert: completion is a return completion.\n\n        // 7. Let promiseCompletion be Completion(PromiseResolve(%Promise%, completion.[[Value]])).\n        let promise_completion = Promise::promise_resolve(\n            &context.intrinsics().constructors().promise().constructor(),\n            value,\n            context,\n        );\n\n        let promise = match promise_completion {\n            Ok(value) => value\n                .downcast::<Promise>()\n                .ok()\n                .js_expect(\"%Promise% constructor must always return a Promise object\")?,\n            // 8. If promiseCompletion is an abrupt completion, then\n            Err(e) => {\n                // a. Perform AsyncGeneratorCompleteStep(generator, promiseCompletion, true).\n                Self::complete_step(generator, Err(e), true, None, context)?;\n                // b. Perform AsyncGeneratorDrainQueue(generator).\n                Self::drain_queue(generator, context)?;\n                // c. Return unused.\n                return Ok(());\n            }\n        };\n\n        // 9. Assert: promiseCompletion is a normal completion.\n        // 10. Let promise be promiseCompletion.[[Value]].\n        // 11. Let fulfilledClosure be a new Abstract Closure with parameters (value) that captures generator and performs the following steps when called:\n        // 12. Let onFulfilled be CreateBuiltinFunction(fulfilledClosure, 1, \"\", « »).\n        let on_fulfilled = FunctionObjectBuilder::new(\n            context.realm(),\n            NativeFunction::from_copy_closure_with_captures(\n                |_this, args, generator, context| {\n                    // a. Assert: generator.[[AsyncGeneratorState]] is draining-queue.\n                    assert_eq!(\n                        generator.borrow().data().state,\n                        AsyncGeneratorState::DrainingQueue\n                    );\n\n                    // b. Let result be NormalCompletion(value).\n                    let result = Ok(args.get_or_undefined(0).clone());\n\n                    // c. Perform AsyncGeneratorCompleteStep(generator, result, true).\n                    Self::complete_step(generator, result, true, None, context)?;\n\n                    // d. Perform AsyncGeneratorDrainQueue(generator).\n                    Self::drain_queue(generator, context)?;\n\n                    // e. Return undefined.\n                    Ok(JsValue::undefined())\n                },\n                generator.clone(),\n            ),\n        )\n        .name(js_string!())\n        .length(1)\n        .build();\n\n        // 13. Let rejectedClosure be a new Abstract Closure with parameters (reason) that captures generator and performs the following steps when called:\n        // 14. Let onRejected be CreateBuiltinFunction(rejectedClosure, 1, \"\", « »).\n        let on_rejected = FunctionObjectBuilder::new(\n            context.realm(),\n            NativeFunction::from_copy_closure_with_captures(\n                |_this, args, generator, context| {\n                    // a. Assert: generator.[[AsyncGeneratorState]] is draining-queue.\n                    assert_eq!(\n                        generator.borrow().data().state,\n                        AsyncGeneratorState::DrainingQueue\n                    );\n\n                    // b. Let result be ThrowCompletion(reason).\n                    let result = Err(JsError::from_opaque(args.get_or_undefined(0).clone()));\n\n                    // c. Perform AsyncGeneratorCompleteStep(generator, result, true).\n                    Self::complete_step(generator, result, true, None, context)?;\n\n                    // d. Perform AsyncGeneratorDrainQueue(generator).\n                    Self::drain_queue(generator, context)?;\n\n                    // e. Return undefined.\n                    Ok(JsValue::undefined())\n                },\n                generator.clone(),\n            ),\n        )\n        .name(js_string!())\n        .length(1)\n        .build();\n\n        // 15. Perform PerformPromiseThen(promise, onFulfilled, onRejected).\n        Promise::perform_promise_then(\n            &promise,\n            Some(on_fulfilled),\n            Some(on_rejected),\n            None,\n            context,\n        );\n\n        // 16. Return unused.\n        Ok(())\n    }\n\n    /// `AsyncGeneratorDrainQueue ( generator )`\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///\n    /// # Errors\n    ///\n    /// Returns `EngineError::Panic` if `generator` is not in the `DrainingQueue` state.\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-asyncgeneratordrainqueue\n    pub(crate) fn drain_queue(\n        generator: &JsObject<AsyncGenerator>,\n        context: &mut Context,\n    ) -> JsResult<()> {\n        // 1. Assert: generator.[[AsyncGeneratorState]] is draining-queue.\n        assert_eq!(\n            generator.borrow().data().state,\n            AsyncGeneratorState::DrainingQueue\n        );\n\n        // 2. Let queue be generator.[[AsyncGeneratorQueue]].\n        // 3. If queue is empty, then\n        if generator.borrow().data().queue.is_empty() {\n            // a. Set generator.[[AsyncGeneratorState]] to completed.\n            generator.borrow_mut().data_mut().state = AsyncGeneratorState::Completed;\n            generator.borrow_mut().data_mut().context = None;\n            // b. Return unused.\n            return Ok(());\n        }\n\n        // 4. Let done be false.\n        // 5. Repeat, while done is false,\n        loop {\n            // a. Let next be the first element of queue.\n            let next = generator\n                .borrow()\n                .data()\n                .queue\n                .front()\n                .js_expect(\"must have entry\")?\n                .completion\n                .clone();\n\n            // b. Let completion be Completion(next.[[Completion]]).\n            match next {\n                // c. If completion is a return completion, then\n                CompletionRecord::Return(val) => {\n                    // i. Perform AsyncGeneratorAwaitReturn(generator).\n                    Self::await_return(generator, val, context)?;\n\n                    // ii. Set done to true.\n                    break;\n                }\n                // d. Else,\n                completion => {\n                    // i. If completion is a normal completion, then\n                    //     1. Set completion to NormalCompletion(undefined).\n                    let completion = completion.consume().map(|_| JsValue::undefined());\n\n                    // ii. Perform AsyncGeneratorCompleteStep(generator, completion, true).\n                    Self::complete_step(generator, completion, true, None, context)?;\n\n                    // iii. If queue is empty, then\n                    if generator.borrow().data().queue.is_empty() {\n                        // 1. Set generator.[[AsyncGeneratorState]] to completed.\n                        generator.borrow_mut().data_mut().state = AsyncGeneratorState::Completed;\n                        generator.borrow_mut().data_mut().context = None;\n                        // 2. Set done to true.\n                        break;\n                    }\n                }\n            }\n        }\n\n        // 6. Return unused.\n        Ok(())\n    }\n}\n"
  },
  {
    "path": "core/engine/src/builtins/async_generator_function/mod.rs",
    "content": "//! Boa's implementation of ECMAScript's `AsyncGeneratorFunction` object.\n//!\n//! More information:\n//!  - [ECMAScript reference][spec]\n//!\n//! [spec]: https://tc39.es/ecma262/#sec-asyncgeneratorfunction-objects\n\nuse crate::{\n    Context, JsResult, JsString,\n    builtins::{BuiltInObject, function::BuiltInFunctionObject},\n    context::intrinsics::{Intrinsics, StandardConstructor, StandardConstructors},\n    object::{JsObject, PROTOTYPE},\n    property::Attribute,\n    realm::Realm,\n    string::StaticJsStrings,\n    symbol::JsSymbol,\n    value::JsValue,\n};\n\nuse super::{BuiltInBuilder, BuiltInConstructor, IntrinsicObject};\n\n/// The internal representation of an `AsyncGeneratorFunction` object.\n#[derive(Debug, Clone, Copy)]\npub struct AsyncGeneratorFunction;\n\nimpl IntrinsicObject for AsyncGeneratorFunction {\n    fn init(realm: &Realm) {\n        BuiltInBuilder::from_standard_constructor::<Self>(realm)\n            .inherits(Some(\n                realm.intrinsics().constructors().function().prototype(),\n            ))\n            .constructor_attributes(Attribute::CONFIGURABLE)\n            .property(\n                PROTOTYPE,\n                realm.intrinsics().objects().async_generator(),\n                Attribute::CONFIGURABLE,\n            )\n            .property(\n                JsSymbol::to_string_tag(),\n                Self::NAME,\n                Attribute::CONFIGURABLE,\n            )\n            .build();\n    }\n\n    fn get(intrinsics: &Intrinsics) -> JsObject {\n        Self::STANDARD_CONSTRUCTOR(intrinsics.constructors()).constructor()\n    }\n}\n\nimpl BuiltInObject for AsyncGeneratorFunction {\n    const NAME: JsString = StaticJsStrings::ASYNC_GENERATOR_FUNCTION;\n}\n\nimpl BuiltInConstructor for AsyncGeneratorFunction {\n    const CONSTRUCTOR_ARGUMENTS: usize = 1;\n    const PROTOTYPE_STORAGE_SLOTS: usize = 2;\n    const CONSTRUCTOR_STORAGE_SLOTS: usize = 0;\n\n    const STANDARD_CONSTRUCTOR: fn(&StandardConstructors) -> &StandardConstructor =\n        StandardConstructors::async_generator_function;\n\n    /// `AsyncGeneratorFunction ( p1, p2, … , pn, body )`\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-asyncgeneratorfunction\n    fn constructor(\n        new_target: &JsValue,\n        args: &[JsValue],\n        context: &mut Context,\n    ) -> JsResult<JsValue> {\n        let active_function = context.active_function_object().unwrap_or_else(|| {\n            context\n                .intrinsics()\n                .constructors()\n                .generator_function()\n                .constructor()\n        });\n        BuiltInFunctionObject::create_dynamic_function(\n            active_function,\n            new_target,\n            args,\n            true,\n            true,\n            context,\n        )\n        .map(Into::into)\n    }\n}\n"
  },
  {
    "path": "core/engine/src/builtins/atomics/futex.rs",
    "content": "// TODO: track https://github.com/rust-lang/rfcs/pull/3467 to see if we can use `UnsafeAliased` instead\n// of raw pointers.\n\n// A bit of context about how exactly this thing works.\n//\n// `Atomics.wait/notify` is basically an emulation of the \"futex\" syscall, which internally uses\n// a wait queue attached to a certain memory address, where processes and threads can manipulate\n// it to synchronize between them.\n// More information: https://en.wikipedia.org/wiki/Futex\n//\n// Our emulation of the API is composed by three components:\n//\n// - `FutexWaiters`, which is a map of addresses to the corresponding wait queue for that address.\n//   Internally uses intrusive linked lists to avoid allocating when adding a new waiter, which\n//   reduces the time spent by a thread in the critical section.\n//\n// - `FutexWaiter`, which contains all the data necessary to be able to wake a waiter from another\n//   thread. If `FutexWaiter` is part of a linked list, it has not been woken up. Otherwise,\n//   it has been woken up by a call to `notify`. This is checked after waking up to see\n//   if the waiter was indeed woken up or if it just sporadically woke up, which can happen\n//   while waiting on a `CondVar`.\n//\n// - `CRITICAL_SECTION`, a global static that must be locked before registering or notifying any\n//   waiter. This guarantees that only one agent can write to the wait queues at any point in time.\n//\n// We can emulate a typical execution using the API for demonstration purposes.\n// At the start of the program, we initially have an empty map of wait queues. We represent this\n// graphically as:\n//\n//   Address   │\n//             │\n// ────────────┼────────────────────────────────────────────────────────────────────\n//             │\n//             │\n//   <empty>   │\n//             │\n//             │\n//\n// Each row here will represent an address and the corresponding wait queue for that address.\n//\n// Let's suppose that \"Thread 2\" wants to wait on the address 50. After locking the global mutex,\n// it first creates a new instante of a `FutexWaiter` and passes a pointer to it to the\n// `FutexWaiters::add_waiter`:\n//\n//   Address   │\n//             │\n// ────────────┼──────────────────────────────────────────────────────────────────────\n//             │\n//             │       ┌───────────────┐\n//             │    ┌─►│               │\n//             │    │  │ Thread 2      │\n//             │    │  │ FutexWaiter   │\n//     50      ├────┘  │               │\n//             │       │               │\n//             │       │ cond_var      │\n//             │       │               │\n//             │       │               │\n//             │       └───────────────┘\n//             │\n//\n// Immediately after this, \"Thread 2\" calls `cond_var.wait`, unlocks the global mutex and sleeps\n// until it is notified again (ignoring the spurious wakeups, those are handled in an infinite loop\n// anyways).\n//\n// Now, let's suppose that `Thread 1` has now acquired the lock and now wants to also\n// wait on the address `50`. Doing the same procedure as \"Thread 2\", our map now looks like:\n//\n//   Address   │\n//             │\n// ────────────┼──────────────────────────────────────────────────────────────────────\n//             │\n//             │       ┌───────────────┐        ┌───────────────┐\n//             │    ┌─►│               ├───────►│               │\n//             │    │  │ Thread 2      │        │ Thread 1      │\n//             │    │  │ FutexWaiter   │        │ FutexWaiter   │\n//     50      ├────┘  │               │        │               │\n//             │       │               │        │               │\n//             │       │ cond_var      │        │ cond_var      │\n//             │       │               │◄───────┤               │\n//             │       │               │        │               │\n//             │       └───────────────┘        └───────────────┘\n//             │\n//\n// Note how the head of our list contains the first waiter which was registered, and the\n// tail of our list is our most recent waiter.\n//\n// After \"Thread 1\" sleeps, \"Thread 3\" has the opportunity to lock the global mutex.\n// In this case, \"Thread 3\" will notify one waiter of the address 50 using the `cond_var` inside\n// `FutexWaiter`, and will also remove it from the linked list. In this case\n// the notified thread is \"Thread 2\":\n//\n//   Address   │\n//             │\n// ────────────┼──────────────────────────────────────────────────────────────────────\n//             │\n//             │       ┌────────────────┐       ┌────────────────┐\n//             │       │                │   ┌──►│                │\n//             │       │ Thread 2       │   │   │ Thread 1       │\n//             │       │ FutexWaiter    │   │   │ FutexWaiter    │\n//     50      ├───┐   │                │   │   │                │\n//             │   │   │                │   │   │                │\n//             │   │   │ cond_var       │   │   │ cond_var       │\n//             │   │   │                │   │   │                │\n//             │   │   │                │   │   │                │\n//             │   │   └────────────────┘   │   └────────────────┘\n//             │   │                        │\n//             │   └────────────────────────┘\n//             │\n//\n// Then, when the lock is released and \"Thread 2\" has woken up, it tries to lock the global mutex\n// again, checking if it is still part of the linked list, or manually remove itself from the queue\n// if that's not the case.\n// In this case \"Thread 2\" has already been notified, which doesn't require any other handling, so it just\n// removes the `FutexWaiter` from its stack and returns `AtomicsWaitResult::Ok`.\n//\n//   Address   │\n//             │\n// ────────────┼──────────────────────────────────────────────────────────────────────\n//             │\n//             │                                ┌────────────────┐\n//             │    ┌──────────────────────────►│                │\n//             │    │                           │ Thread 1       │\n//             │    │                           │ FutexWaiter    │\n//     50      ├────┘                           │                │\n//             │                                │                │\n//             │                                │ cond_var       │\n//             │                                │ waiting: true  │\n//             │                                │                │\n//             │                                └────────────────┘\n//             │\n//             │\n//             │\n//\n// In a future point in time, \"Thread 1\" will be notified, which will proceed with the\n// exact same steps as \"Thread 2\", emptying the wait queue and finishing the execution of our\n// program.\n\n#![deny(unsafe_op_in_unsafe_fn)]\n#![deny(clippy::undocumented_unsafe_blocks)]\n#![allow(clippy::expl_impl_clone_on_copy)]\n\nuse std::{\n    cell::Cell,\n    fmt, ptr,\n    sync::{Arc, atomic::Ordering},\n};\n\nuse crate::{\n    Context, JsNativeError, JsResult, JsValue,\n    builtins::{\n        array_buffer::{SharedArrayBuffer, utils::SliceRef},\n        promise::ResolvingFunctions,\n        typed_array::Element,\n    },\n    job::{NativeAsyncJob, TimeoutJob},\n    js_string,\n    sys::time::{Duration, Instant},\n};\n\nuse std::sync::{Condvar, Mutex, MutexGuard};\n\nuse boa_string::JsString;\nuse intrusive_collections::{LinkedList, LinkedListLink, UnsafeRef, intrusive_adapter};\nuse small_btree::{Entry, SmallBTreeMap};\n\n/// The result of the [`wait`] and [`wait_async`] functions.\n#[derive(Debug, Clone, Copy)]\npub(super) enum AtomicsWaitResult {\n    NotEqual,\n    TimedOut,\n    Ok,\n}\n\nimpl AtomicsWaitResult {\n    pub(super) fn to_js_string(self) -> JsString {\n        match self {\n            AtomicsWaitResult::NotEqual => js_string!(\"not-equal\"),\n            AtomicsWaitResult::TimedOut => js_string!(\"timed-out\"),\n            AtomicsWaitResult::Ok => js_string!(\"ok\"),\n        }\n    }\n}\n\n/// Data used by async waiters.\n#[derive(Debug)]\nstruct AsyncWaiterData {\n    // Only here to ensure the buffer does not get collected before all its\n    // waiters.\n    _buffer: SharedArrayBuffer,\n    // Channel to signals the waiter that it has been timed out or notified.\n    sender: oneshot::Sender<AtomicsWaitResult>,\n}\n\n/// A waiter for a memory address.\nstruct FutexWaiter {\n    link: LinkedListLink,\n    cond_var: Condvar,\n    addr: usize,\n    async_data: Cell<Option<AsyncWaiterData>>,\n}\n\nimpl fmt::Debug for FutexWaiter {\n    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {\n        f.debug_struct(\"FutexWaiter\")\n            .field(\"link\", &self.link)\n            .field(\"cond_var\", &self.cond_var)\n            .field(\"addr\", &self.addr)\n            .finish_non_exhaustive()\n    }\n}\n\nintrusive_adapter!(FutexWaiterAdapter = UnsafeRef<FutexWaiter>: FutexWaiter { link => LinkedListLink });\n\nimpl FutexWaiter {\n    /// Creates a new `FutexWaiter` that will block the current thread while waiting.\n    fn new_sync(addr: usize) -> Self {\n        Self {\n            link: LinkedListLink::new(),\n            cond_var: Condvar::new(),\n            addr,\n            async_data: Cell::new(None),\n        }\n    }\n\n    /// Creates a new `FutexWaiter` that will NOT block the current thread while waiting.\n    #[allow(\n        clippy::arc_with_non_send_sync,\n        reason = \"across threads we only access the fields that are `Sync`\"\n    )]\n    fn new_async(data: AsyncWaiterData, addr: usize) -> Arc<FutexWaiter> {\n        Arc::new(Self {\n            link: LinkedListLink::new(),\n            cond_var: Condvar::new(),\n            addr,\n            async_data: Cell::new(Some(data)),\n        })\n    }\n}\n\n/// List of memory addresses and its corresponding list of waiters for that address.\nstruct FutexWaiters {\n    waiters: SmallBTreeMap<usize, LinkedList<FutexWaiterAdapter>, 16>,\n}\n\n// SAFETY: `FutexWaiters` is not constructable outside its `get` method, and it's only exposed by\n//          a global lock, meaning the inner data of `FutexWaiters` (which includes non-Send pointers)\n//          can only be accessed by a single thread at once.\nunsafe impl Send for FutexWaiters {}\n\nimpl FutexWaiters {\n    /// Gets the map of all shared data addresses and its corresponding list of agents waiting on that location.\n    fn get() -> JsResult<MutexGuard<'static, Self>> {\n        static CRITICAL_SECTION: Mutex<FutexWaiters> = Mutex::new(FutexWaiters {\n            waiters: SmallBTreeMap::new(),\n        });\n\n        CRITICAL_SECTION.lock().map_err(|_| {\n            JsNativeError::typ()\n                .with_message(\"failed to synchronize with the agent cluster\")\n                .into()\n        })\n    }\n\n    /// # Safety\n    ///\n    /// - `node` must NOT be linked to an existing waiter list.\n    /// - `node` must always reference a valid instance of `FutexWaiter` until `node` is\n    ///   removed from its linked list. This can happen by either `remove_waiter` or `notify_many`.\n    unsafe fn add_waiter(&mut self, node: &FutexWaiter) {\n        // SAFETY: `node` must point to a valid instance.\n        let node = unsafe { UnsafeRef::from_raw(ptr::from_ref(node)) };\n\n        self.waiters\n            .entry(node.addr)\n            .or_insert_with(|| LinkedList::new(FutexWaiterAdapter::new()))\n            .push_back(node);\n    }\n\n    /// # Safety\n    ///\n    /// - `node` must NOT be linked to an existing waiter list.\n    unsafe fn add_async_waiter(&mut self, node: Arc<FutexWaiter>) {\n        // SAFETY: `node` is not linked by the guarantees of the caller.\n        let node = unsafe { UnsafeRef::from_raw(Arc::into_raw(node)) };\n\n        self.waiters\n            .entry(node.addr)\n            .or_insert_with(|| LinkedList::new(FutexWaiterAdapter::new()))\n            .push_back(node);\n    }\n\n    /// Notifies at most `max_count` waiters that are waiting on the address `addr`, and\n    /// returns the number of waiters that were notified.\n    ///\n    /// Equivalent to [`RemoveWaiters`][remove] and [`NotifyWaiter`][notify], but in a single operation.\n    ///\n    /// [remove]: https://tc39.es/ecma262/#sec-removewaiters\n    /// [notify]: https://tc39.es/ecma262/#sec-notifywaiter\n    fn notify_many(&mut self, addr: usize, max_count: u64) -> u64 {\n        let Entry::Occupied(mut wl) = self.waiters.entry(addr) else {\n            return 0;\n        };\n\n        for i in 0..max_count {\n            let Some(elem) = wl.get_mut().pop_front() else {\n                wl.remove();\n                return i;\n            };\n\n            elem.cond_var.notify_one();\n\n            if let Some(async_data) = elem.async_data.take() {\n                // SAFETY: All entries on the waiter list must be valid, and all async entries\n                // must come from an Arc.\n                unsafe { Arc::from_raw(UnsafeRef::into_raw(elem)) };\n                // - If the async waiter is still awaiting, this should never fail.\n                // - If the async waiter was dropped, the channel is now closed.\n                //   We don't need to handle the error since this can only happen\n                //   if the async waiter task was dropped, and at that point the\n                //   message won't even matter.\n                drop(async_data.sender.send(AtomicsWaitResult::Ok));\n            }\n        }\n\n        if wl.get().is_empty() {\n            wl.remove();\n        }\n\n        max_count\n    }\n\n    /// # Safety\n    ///\n    /// - `node` must point to a valid instance of `FutexWaiter`.\n    /// - `node` must be inside the wait list associated with `node.addr`.\n    #[track_caller]\n    pub(super) unsafe fn remove_waiter(&mut self, node: &FutexWaiter) {\n        debug_assert!(node.link.is_linked());\n\n        let Entry::Occupied(mut wl) = self.waiters.entry(node.addr) else {\n            panic!(\"node was not a valid `FutexWaiter`\");\n        };\n\n        // SAFETY: `node` must be inside the wait list associated with `node.addr`.\n        let node = unsafe {\n            let Some(node) = wl\n                .get_mut()\n                .cursor_mut_from_ptr(ptr::from_ref(node))\n                .remove()\n            else {\n                panic!(\"node was not a valid `FutexWaiter`\")\n            };\n            node\n        };\n\n        if let Some(async_data) = node.async_data.take() {\n            // SAFETY: all async entries must be managed by an Arc.\n            unsafe {\n                Arc::from_raw(UnsafeRef::into_raw(node));\n            }\n            // - If the async waiter is still awaiting, this should never fail.\n            // - If the async waiter was dropped, the channel is now closed.\n            //   We don't need to handle the error since this can only happen\n            //   if the async waiter task was dropped, and at that point the\n            //   message won't even matter.\n            drop(async_data.sender.send(AtomicsWaitResult::TimedOut));\n        }\n\n        if wl.get().is_empty() {\n            wl.remove();\n        }\n    }\n}\n\n/// Adds this agent to the wait queue for the address pointed to by `buffer[offset..]`.\n///\n/// # Safety\n///\n/// - `addr` must be a multiple of `std::mem::size_of::<E>()`.\n/// - `buffer` must contain at least `std::mem::size_of::<E>()` bytes to read starting from `usize`.\n// our implementation guarantees that `SharedArrayBuffer` is always aligned to `u64` at minimum.\npub(super) unsafe fn wait<E: Element + PartialEq>(\n    buffer: &SharedArrayBuffer,\n    buf_len: usize,\n    offset: usize,\n    check: E,\n    timeout: Option<Duration>,\n) -> JsResult<AtomicsWaitResult> {\n    // 11. Let block be buffer.[[ArrayBufferData]].\n    // 12. Let offset be typedArray.[[ByteOffset]].\n    // 13. Let byteIndexInBuffer be (i × 4) + offset.\n    let buffer = &buffer.bytes_with_len(buf_len)[offset..];\n\n    // 14. Let WL be GetWaiterList(block, indexedPosition).\n    // 18. Perform EnterCriticalSection(WL).\n    let mut waiters = FutexWaiters::get()?;\n\n    // 13. Let elementType be TypedArrayElementType(typedArray).\n    // 14. Let w be GetValueFromBuffer(buffer, indexedPosition, elementType, true, SeqCst).\n\n    // SAFETY: The safety of this operation is guaranteed by the caller.\n    let value = unsafe { E::read(SliceRef::AtomicSlice(buffer)).load(Ordering::SeqCst) };\n\n    // 20. If v ≠ w, then\n    // a. Perform LeaveCriticalSection(WL).\n    // b. If mode is sync, return \"not-equal\".\n    if check != value {\n        return Ok(AtomicsWaitResult::NotEqual);\n    }\n\n    // 23. Let now be the time value (UTC) identifying the current time.\n    // 24. Let additionalTimeout be an implementation-defined non-negative mathematical value.\n    // 25. Let timeoutTime be ℝ(now) + t + additionalTimeout.\n    // 26. NOTE: When t is +∞, timeoutTime is also +∞.\n    let timeout_time = timeout.map(|to| (Instant::now(), to));\n\n    // 27. Let waiterRecord be a new Waiter Record { [[AgentSignifier]]: thisAgent, [[PromiseCapability]]: promiseCapability, [[TimeoutTime]]: timeoutTime, [[Result]]: \"ok\" }.\n    // ensure we can have aliased pointers to the waiter in a sound way.\n    let waiter = FutexWaiter::new_sync(buffer.as_ptr().addr());\n\n    // 28. Perform AddWaiter(WL, waiterRecord).\n    // SAFETY: waiter is valid and we call `remove_waiter` below.\n    unsafe {\n        waiters.add_waiter(&waiter);\n    }\n\n    // 18. Let notified be SuspendAgent(WL, W, t).\n\n    // `SuspendAgent(WL, W, t)`\n    // https://tc39.es/ecma262/#sec-suspendthisagent\n\n    // In a couple of places here we early return without removing the waiter from\n    // the waiters list. This could seem unsound, but in reality all our early\n    // returns are because the mutex is poisoned, and if that's the case then\n    // no other thread can read the pointer to the waiter, so we can return safely.\n    let result = loop {\n        if !waiter.link.is_linked() {\n            break AtomicsWaitResult::Ok;\n        }\n\n        if let Some((start, timeout)) = timeout_time {\n            let Some(remaining) = timeout.checked_sub(start.elapsed()) else {\n                break AtomicsWaitResult::TimedOut;\n            };\n\n            // This doesn't use `wait_timeout_while` because it has to mutably borrow `waiter`,\n            // which is a big nono since we have pointers to that location while the borrow is\n            // active.\n            waiters = waiter\n                .cond_var\n                .wait_timeout(waiters, remaining)\n                .map_err(|_| {\n                    JsNativeError::typ()\n                        .with_message(\"failed to synchronize with the agent cluster\")\n                })?\n                .0;\n        } else {\n            waiters = waiter.cond_var.wait(waiters).map_err(|_| {\n                JsNativeError::typ().with_message(\"failed to synchronize with the agent cluster\")\n            })?;\n        }\n    };\n\n    // 19. If notified is true, then\n    //     a. Assert: W is not on the list of waiters in WL.\n    // 20. Else,\n    //     a. Perform RemoveWaiter(WL, W).\n    if waiter.link.is_linked() {\n        // SAFETY: waiter is valid and contained in its waiter list if it is still linked.\n        unsafe {\n            waiters.remove_waiter(&waiter);\n        }\n    }\n\n    // 21. Perform LeaveCriticalSection(WL).\n    drop(waiters);\n\n    // 22. If notified is true, return \"ok\".\n    // 23. Return \"timed-out\".\n    Ok(result)\n}\n\n/// Adds this agent to the wait queue for the address pointed to by `buffer[offset..]`,\n/// without blocking the execution thread.\n///\n/// # Safety\n///\n/// - `addr` must be a multiple of `std::mem::size_of::<E>()`.\n/// - `buffer` must contain at least `std::mem::size_of::<E>()` bytes to read starting from `usize`.\n// our implementation guarantees that `SharedArrayBuffer` is always aligned to `u64` at minimum.\npub(super) unsafe fn wait_async<E: Element + PartialEq>(\n    buffer: &SharedArrayBuffer,\n    buf_len: usize,\n    offset: usize,\n    check: E,\n    timeout: Option<Duration>,\n    functions: ResolvingFunctions,\n    context: &mut Context,\n) -> JsResult<AtomicsWaitResult> {\n    // 11. Let block be buffer.[[ArrayBufferData]].\n    // 12. Let offset be typedArray.[[ByteOffset]].\n    // 13. Let byteIndexInBuffer be (i × 4) + offset.\n    let buf = &buffer.bytes_with_len(buf_len)[offset..];\n\n    // 14. Let WL be GetWaiterList(block, indexedPosition).\n    // 17. Perform EnterCriticalSection(WL).\n    let mut waiters = FutexWaiters::get()?;\n\n    // 18. Let elementType be TypedArrayElementType(typedArray).\n    // 19. Let w be GetValueFromBuffer(buffer, indexedPosition, elementType, true, SeqCst).\n    // SAFETY: The safety of this operation is guaranteed by the caller.\n    let value = unsafe { E::read(SliceRef::AtomicSlice(buf)).load(Ordering::SeqCst) };\n\n    // 20. If v ≠ w, then\n    //     a. Perform LeaveCriticalSection(WL).\n    //     b. If mode is sync, return \"not-equal\".\n    if check != value {\n        return Ok(AtomicsWaitResult::NotEqual);\n    }\n\n    // 21. If t = 0 and mode is async, then\n    //     a. NOTE: There is no special handling of synchronous immediate timeouts. Asynchronous immediate\n    //        timeouts have special handling in order to fail fast and avoid unnecessary Promise jobs.\n    //     b. Perform LeaveCriticalSection(WL).\n    if let Some(timeout) = &timeout\n        && timeout.is_zero()\n    {\n        return Ok(AtomicsWaitResult::TimedOut);\n    }\n\n    // 23. Let now be the time value (UTC) identifying the current time.\n    // 24. Let additionalTimeout be an implementation-defined non-negative mathematical value.\n    // 25. Let timeoutTime be ℝ(now) + t + additionalTimeout.\n    // 26. NOTE: When t is +∞, timeoutTime is also +∞.\n    let (sender, receiver) = oneshot::async_channel();\n\n    // 27. Let waiterRecord be a new Waiter Record { [[AgentSignifier]]: thisAgent,\n    //     [[PromiseCapability]]: promiseCapability, [[TimeoutTime]]: timeoutTime, [[Result]]: \"ok\"}.\n    // ensure we can have aliased pointers to the waiter in a sound way.\n    let waiter = FutexWaiter::new_async(\n        AsyncWaiterData {\n            _buffer: buffer.clone(),\n            sender,\n        },\n        buf.as_ptr().addr(),\n    );\n    let weak_waiter = Arc::downgrade(&waiter);\n\n    // 28. Perform AddWaiter(WL, waiterRecord).\n    // SAFETY: `waiter` is pinned to the heap, so it must be valid.\n    unsafe {\n        waiters.add_async_waiter(waiter.clone());\n    }\n\n    // 30. Else if timeoutTime is finite, then\n    //     a. Perform EnqueueAtomicsWaitAsyncTimeoutJob(WL, waiterRecord).\n    let timeout_cancel = if let Some(timeout) = timeout {\n        // EnqueueAtomicsWaitAsyncTimeoutJob ( WL, waiterRecord )\n        // https://tc39.es/ecma262/#sec-enqueueatomicswaitasynctimeoutjob\n\n        // 1. Let timeoutJob be a new Job Abstract Closure with no parameters that captures WL and waiterRecord and performs the following steps when called:\n        let job = TimeoutJob::with_realm(\n            move |_| {\n                //    a. Perform EnterCriticalSection(WL).\n                let mut waiters = FutexWaiters::get()?;\n\n                //    b. If WL.[[Waiters]] contains waiterRecord, then\n                if let Some(waiter) = weak_waiter.upgrade()\n                    && waiter.link.is_linked()\n                {\n                    // i. Let timeOfJobExecution be the time value (UTC) identifying the current time.\n                    // ii. Assert: ℝ(timeOfJobExecution) ≥ waiterRecord.[[TimeoutTime]] (ignoring potential non-monotonicity of time values).\n                    // iii. Set waiterRecord.[[Result]] to \"timed-out\".\n                    // SAFETY: the node is linked and still valid thanks to the reference count.\n                    unsafe {\n                        // iv. Perform RemoveWaiter(WL, waiterRecord).\n                        waiters.remove_waiter(&waiter);\n                    }\n                }\n\n                //    c. Perform LeaveCriticalSection(WL).\n                //    d. Return unused.\n                Ok(JsValue::undefined())\n            },\n            // 3. Let currentRealm be the current Realm Record.\n            context.realm().clone(),\n            // 2. Let now be the time value (UTC) identifying the current time.\n            timeout,\n        );\n\n        let tc = job.cancelled_flag();\n\n        // 4. Perform HostEnqueueTimeoutJob(timeoutJob, currentRealm, 𝔽(waiterRecord.[[TimeoutTime]]) - now).\n        context.enqueue_job(job.into());\n\n        // 5. Return unused.\n        Some(tc)\n    } else {\n        None\n    };\n\n    context.enqueue_job(\n        NativeAsyncJob::new(async move |context| {\n            if let Ok(result) = receiver.await {\n                // v. Perform NotifyWaiter(WL, waiterRecord).\n                functions\n                    .resolve\n                    .call(\n                        &JsValue::undefined(),\n                        &[result.to_js_string().into()],\n                        &mut context.borrow_mut(),\n                    )\n                    .expect(\"default resolving functions cannot error\");\n            } else {\n                // The \"else\" branch can only happen if the channel was dropped in both\n                // the timeout job and the whole waiters map, which is only possible if\n                // we panicked. We just GIGO then, since it doesn't make sense to\n                // resolve a promise in a thread that is panicking.\n            }\n\n            if let Some(flag) = timeout_cancel {\n                flag.set();\n            }\n\n            Ok(JsValue::undefined())\n        })\n        .into(),\n    );\n\n    Ok(AtomicsWaitResult::Ok)\n}\n\n/// Notifies at most `count` agents waiting on the memory address pointed to by `buffer[offset..]`.\npub(super) fn notify(buffer: &SharedArrayBuffer, offset: usize, count: u64) -> JsResult<u64> {\n    let addr = buffer.as_ptr().addr() + offset;\n\n    // 7. Let WL be GetWaiterList(block, indexedPosition).\n    // 8. Perform EnterCriticalSection(WL).\n    let mut waiters = FutexWaiters::get()?;\n\n    // 9. Let S be RemoveWaiters(WL, c).\n    // 10. For each element W of S, do\n    //     a. Perform NotifyWaiter(WL, W).\n    let count = waiters.notify_many(addr, count);\n\n    // 11. Perform LeaveCriticalSection(WL).\n    drop(waiters);\n\n    Ok(count)\n}\n"
  },
  {
    "path": "core/engine/src/builtins/atomics/mod.rs",
    "content": "//! Boa's implementation of ECMAScript's global `Atomics` object.\n//!\n//! The `Atomics` object contains synchronization methods to orchestrate multithreading\n//! on contexts that live in separate threads.\n//!\n//! More information:\n//!  - [ECMAScript reference][spec]\n//!  - [MDN documentation][mdn]\n//!\n//! [spec]: https://tc39.es/ecma262/#sec-atomics-object\n//! [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Atomics\n\nmod futex;\n\nuse std::sync::atomic::Ordering;\n\nuse crate::{\n    Context, JsArgs, JsNativeError, JsResult, JsString, JsValue,\n    builtins::{BuiltInObject, OrdinaryObject},\n    context::intrinsics::Intrinsics,\n    js_string,\n    object::{JsObject, JsPromise},\n    property::Attribute,\n    realm::Realm,\n    string::StaticJsStrings,\n    symbol::JsSymbol,\n    sys::time::Duration,\n    value::IntegerOrInfinity,\n};\n\nuse super::{\n    BuiltInBuilder, IntrinsicObject,\n    array_buffer::{BufferObject, BufferRef},\n    typed_array::{Atomic, ContentType, Element, TypedArray, TypedArrayElement, TypedArrayKind},\n};\n\n/// Javascript `Atomics` object.\n#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]\npub(crate) struct Atomics;\n\nimpl IntrinsicObject for Atomics {\n    fn init(realm: &Realm) {\n        let builder = BuiltInBuilder::with_intrinsic::<Self>(realm)\n            .static_property(\n                JsSymbol::to_string_tag(),\n                Self::NAME,\n                Attribute::READONLY | Attribute::NON_ENUMERABLE | Attribute::CONFIGURABLE,\n            )\n            .static_method(Atomics::add, js_string!(\"add\"), 3)\n            .static_method(Atomics::bit_and, js_string!(\"and\"), 3)\n            .static_method(Atomics::compare_exchange, js_string!(\"compareExchange\"), 4)\n            .static_method(Atomics::swap, js_string!(\"exchange\"), 3)\n            .static_method(Atomics::is_lock_free, js_string!(\"isLockFree\"), 1)\n            .static_method(Atomics::load, js_string!(\"load\"), 2)\n            .static_method(Atomics::bit_or, js_string!(\"or\"), 3)\n            .static_method(Atomics::store, js_string!(\"store\"), 3)\n            .static_method(Atomics::sub, js_string!(\"sub\"), 3)\n            .static_method(Atomics::wait::<false>, js_string!(\"wait\"), 4)\n            .static_method(Atomics::wait::<true>, js_string!(\"waitAsync\"), 4)\n            .static_method(Atomics::notify, js_string!(\"notify\"), 3)\n            .static_method(Atomics::bit_xor, js_string!(\"xor\"), 3);\n\n        #[cfg(feature = \"experimental\")]\n        let builder = builder.static_method(Atomics::pause, js_string!(\"pause\"), 0);\n\n        builder.build();\n    }\n\n    fn get(intrinsics: &Intrinsics) -> JsObject {\n        intrinsics.objects().atomics()\n    }\n}\n\nimpl BuiltInObject for Atomics {\n    const NAME: JsString = StaticJsStrings::ATOMICS;\n}\n\nmacro_rules! atomic_op {\n    ($(#[$attr:meta])* $name:ident) => {\n        $(#[$attr])* fn $name(_: &JsValue, args: &[JsValue], context: &mut Context) -> JsResult<JsValue> {\n\n            let array = args.get_or_undefined(0);\n            let index = args.get_or_undefined(1);\n            let value = args.get_or_undefined(2);\n\n            // AtomicReadModifyWrite ( typedArray, index, value, op )\n            // <https://tc39.es/ecma262/#sec-atomicreadmodifywrite>\n\n            // 1. Let buffer be ? ValidateIntegerTypedArray(typedArray).\n            let (ta, buf_len) = validate_integer_typed_array(array, false)?;\n\n            // 2. Let indexedPosition be ? ValidateAtomicAccess(typedArray, index).\n            let access = validate_atomic_access(&ta, buf_len, index, context)?;\n\n            // 3. If typedArray.[[ContentType]] is BigInt, let v be ? ToBigInt(value).\n            // 4. Otherwise, let v be 𝔽(? ToIntegerOrInfinity(value)).\n            // 7. Let elementType be TypedArrayElementType(typedArray).\n            let value = access.kind.get_element(value, context)?;\n\n            // 5. If IsDetachedBuffer(buffer) is true, throw a TypeError exception.\n            // 6. NOTE: The above check is not redundant with the check in ValidateIntegerTypedArray because the call\n            //    to ToBigInt or ToIntegerOrInfinity on the preceding lines can have arbitrary side effects, which could\n            //    cause the buffer to become detached.\n            let ta = ta.borrow();\n            let ta = ta.data();\n            let mut buffer = ta.viewed_array_buffer().as_buffer_mut();\n            let Some(mut data) = buffer.bytes_with_len(buf_len) else {\n                return Err(JsNativeError::typ()\n                .with_message(\"cannot execute atomic operation in detached buffer\")\n                .into());\n            };\n            let data = data.subslice_mut(access.byte_offset..);\n\n            // 8. Return GetModifySetValueInBuffer(buffer, indexedPosition, elementType, v, op).\n            // SAFETY: The integer indexed object guarantees that the buffer is aligned.\n            // The call to `validate_atomic_access` guarantees that the index is in-bounds.\n            let value: TypedArrayElement = unsafe {\n                match value {\n                    TypedArrayElement::Int8(num) => {\n                        i8::read_mut(data).$name(num, Ordering::SeqCst).into()\n                    }\n                    TypedArrayElement::Uint8(num) => {\n                        u8::read_mut(data).$name(num, Ordering::SeqCst).into()\n                    }\n                    TypedArrayElement::Int16(num) => i16::read_mut(data)\n                        .$name(num, Ordering::SeqCst)\n                        .into(),\n                    TypedArrayElement::Uint16(num) => u16::read_mut(data)\n                        .$name(num, Ordering::SeqCst)\n                        .into(),\n                    TypedArrayElement::Int32(num) => i32::read_mut(data)\n                        .$name(num, Ordering::SeqCst)\n                        .into(),\n                    TypedArrayElement::Uint32(num) => u32::read_mut(data)\n                        .$name(num, Ordering::SeqCst)\n                        .into(),\n                    TypedArrayElement::BigInt64(num) => i64::read_mut(data)\n                        .$name(num, Ordering::SeqCst)\n                        .into(),\n                    TypedArrayElement::BigUint64(num) => u64::read_mut(data)\n                        .$name(num, Ordering::SeqCst)\n                        .into(),\n                    TypedArrayElement::Uint8Clamped(_)\n                    | TypedArrayElement::Float32(_)\n                    | TypedArrayElement::Float64(_) => unreachable!(\n                        \"must have been filtered out by the call to `validate_integer_typed_array`\"\n                    ),\n                    #[cfg(feature = \"float16\")]\n                    TypedArrayElement::Float16(_) => unreachable!(\n                        \"must have been filtered out by the call to `validate_integer_typed_array`\"\n                    ),\n                }\n            };\n\n            Ok(value.into())\n        }\n    };\n}\n\nimpl Atomics {\n    /// [`Atomics.isLockFree ( size )`][spec]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-atomics.islockfree\n    fn is_lock_free(_: &JsValue, args: &[JsValue], context: &mut Context) -> JsResult<JsValue> {\n        // 1. Let n be ? ToIntegerOrInfinity(size).\n        let n = args.get_or_undefined(0).to_integer_or_infinity(context)?;\n\n        // 2. Let AR be the Agent Record of the surrounding agent.\n        Ok(match n.as_integer() {\n            // 3. If n = 1, return AR.[[IsLockFree1]].\n            Some(1) => <<u8 as Element>::Atomic as Atomic>::is_lock_free(),\n            // 4. If n = 2, return AR.[[IsLockFree2]].\n            Some(2) => <<u16 as Element>::Atomic as Atomic>::is_lock_free(),\n            // 5. If n = 4, return true.\n            Some(4) => true,\n            // 6. If n = 8, return AR.[[IsLockFree8]].\n            Some(8) => <<u64 as Element>::Atomic as Atomic>::is_lock_free(),\n            // 7. Return false.\n            _ => false,\n        }\n        .into())\n    }\n\n    /// [`Atomics.load ( typedArray, index )`][spec]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-atomics.load\n    fn load(_: &JsValue, args: &[JsValue], context: &mut Context) -> JsResult<JsValue> {\n        let array = args.get_or_undefined(0);\n        let index = args.get_or_undefined(1);\n\n        // 1. Let indexedPosition be ? ValidateAtomicAccessOnIntegerTypedArray(typedArray, index).\n        let (ta, buf_len) = validate_integer_typed_array(array, false)?;\n        let access = validate_atomic_access(&ta, buf_len, index, context)?;\n\n        // 2. Perform ? RevalidateAtomicAccess(typedArray, indexedPosition).\n        let ta = ta.borrow();\n        let ta = ta.data();\n        let buffer = ta.viewed_array_buffer().as_buffer();\n        let Some(data) = buffer.bytes_with_len(buf_len) else {\n            return Err(JsNativeError::typ()\n                .with_message(\"cannot execute atomic operation in detached buffer\")\n                .into());\n        };\n        let data = data.subslice(access.byte_offset..);\n\n        // 3. Let buffer be typedArray.[[ViewedArrayBuffer]].\n        // 4. Let elementType be TypedArrayElementType(typedArray).\n        // 5. Return GetValueFromBuffer(buffer, indexedPosition, elementType, true, seq-cst).\n        // SAFETY: The integer indexed object guarantees that the buffer is aligned.\n        // The call to `validate_atomic_access` guarantees that the index is in-bounds.\n        let value = unsafe { data.get_value(access.kind, Ordering::SeqCst) };\n\n        Ok(value.into())\n    }\n\n    /// [`Atomics.store ( typedArray, index, value )`][spec]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-atomics.store\n    fn store(_: &JsValue, args: &[JsValue], context: &mut Context) -> JsResult<JsValue> {\n        let array = args.get_or_undefined(0);\n        let index = args.get_or_undefined(1);\n        let value = args.get_or_undefined(2);\n\n        // 1. Let indexedPosition be ? ValidateAtomicAccessOnIntegerTypedArray(typedArray, index).\n        let (ta, buf_len) = validate_integer_typed_array(array, false)?;\n        let access = validate_atomic_access(&ta, buf_len, index, context)?;\n\n        // bit of a hack to preserve the converted value\n        // 2. If typedArray.[[ContentType]] is bigint, let v be ? ToBigInt(value).\n        let converted: JsValue = if access.kind.content_type() == ContentType::BigInt {\n            value.to_bigint(context)?.into()\n        } else {\n            // 3. Otherwise, let v be 𝔽(? ToIntegerOrInfinity(value)).\n            match value.to_integer_or_infinity(context)? {\n                IntegerOrInfinity::PositiveInfinity => f64::INFINITY,\n                IntegerOrInfinity::Integer(i) => i as f64,\n                IntegerOrInfinity::NegativeInfinity => f64::NEG_INFINITY,\n            }\n            .into()\n        };\n        let value = access.kind.get_element(&converted, context)?;\n\n        // 4. Perform ? RevalidateAtomicAccess(typedArray, indexedPosition).\n        let ta = ta.borrow();\n        let ta = ta.data();\n        let mut buffer = ta.viewed_array_buffer().as_buffer_mut();\n        let Some(mut buffer) = buffer.bytes_with_len(buf_len) else {\n            return Err(JsNativeError::typ()\n                .with_message(\"cannot execute atomic operation in detached buffer\")\n                .into());\n        };\n        let mut data = buffer.subslice_mut(access.byte_offset..);\n\n        // 5. Let buffer be typedArray.[[ViewedArrayBuffer]].\n        // 6. Let elementType be TypedArrayElementType(typedArray).\n        // 7. Perform SetValueInBuffer(buffer, indexedPosition, elementType, v, true, seq-cst).\n        // SAFETY: The integer indexed object guarantees that the buffer is aligned.\n        // The call to `validate_atomic_access` guarantees that the index is in-bounds.\n        unsafe {\n            data.set_value(value, Ordering::SeqCst);\n        }\n\n        // 8. Return v.\n        Ok(converted)\n    }\n\n    /// [`Atomics.compareExchange ( typedArray, index, expectedValue, replacementValue )`][spec]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-atomics.compareexchange\n    fn compare_exchange(_: &JsValue, args: &[JsValue], context: &mut Context) -> JsResult<JsValue> {\n        let array = args.get_or_undefined(0);\n        let index = args.get_or_undefined(1);\n        let expected = args.get_or_undefined(2);\n        let replacement = args.get_or_undefined(3);\n\n        // 1. Let indexedPosition be ? ValidateAtomicAccessOnIntegerTypedArray(typedArray, index).\n        // 2. Let buffer be typedArray.[[ViewedArrayBuffer]].\n        // 3. Let block be buffer.[[ArrayBufferData]].\n        let (ta, buf_len) = validate_integer_typed_array(array, false)?;\n        let access = validate_atomic_access(&ta, buf_len, index, context)?;\n\n        // 4. If typedArray.[[ContentType]] is bigint, then\n        //     a. Let expected be ? ToBigInt(expectedValue).\n        //     b. Let replacement be ? ToBigInt(replacementValue).\n        // 5. Else,\n        //     a. Let expected be 𝔽(? ToIntegerOrInfinity(expectedValue)).\n        //     b. Let replacement be 𝔽(? ToIntegerOrInfinity(replacementValue)).\n        let exp = access.kind.get_element(expected, context)?.to_bits();\n        let rep = access.kind.get_element(replacement, context)?.to_bits();\n\n        // 6. Perform ? RevalidateAtomicAccess(typedArray, indexedPosition).\n        let ta = ta.borrow();\n        let ta = ta.data();\n        let mut buffer = ta.viewed_array_buffer().as_buffer_mut();\n        let Some(mut buffer) = buffer.bytes_with_len(buf_len) else {\n            return Err(JsNativeError::typ()\n                .with_message(\"cannot execute atomic operation in detached buffer\")\n                .into());\n        };\n        let data = buffer.subslice_mut(access.byte_offset..);\n\n        // 7. Let elementType be TypedArrayElementType(typedArray).\n        // 8. Let elementSize be TypedArrayElementSize(typedArray).\n        // 9. Let isLittleEndian be the value of the [[LittleEndian]] field of the surrounding agent's Agent Record.\n        // 10. Let expectedBytes be NumericToRawBytes(elementType, expected, isLittleEndian).\n        // 11. Let replacementBytes be NumericToRawBytes(elementType, replacement, isLittleEndian).\n        // 12. If IsSharedArrayBuffer(buffer) is true, then\n        //     a. Let rawBytesRead be AtomicCompareExchangeInSharedBlock(block, indexedPosition, elementSize, expectedBytes, replacementBytes).\n        // 13. Else,\n        //     a. Let rawBytesRead be a List of length elementSize whose elements are the sequence of elementSize bytes starting with block[indexedPosition].\n        //     b. If ByteListEqual(rawBytesRead, expectedBytes) is true, then\n        //         i. Store the individual bytes of replacementBytes into block, starting at block[indexedPosition].\n        // 14. Return RawBytesToNumeric(elementType, rawBytesRead, isLittleEndian).\n\n        // SAFETY: The integer indexed object guarantees that the buffer is aligned.\n        // The call to `validate_atomic_access` guarantees that the index is in-bounds.\n        let value: TypedArrayElement = unsafe {\n            match access.kind {\n                TypedArrayKind::Int8 => i8::read_mut(data)\n                    .compare_exchange(exp as i8, rep as i8, Ordering::SeqCst)\n                    .into(),\n                TypedArrayKind::Uint8 => u8::read_mut(data)\n                    .compare_exchange(exp as u8, rep as u8, Ordering::SeqCst)\n                    .into(),\n                TypedArrayKind::Int16 => i16::read_mut(data)\n                    .compare_exchange(exp as i16, rep as i16, Ordering::SeqCst)\n                    .into(),\n                TypedArrayKind::Uint16 => u16::read_mut(data)\n                    .compare_exchange(exp as u16, rep as u16, Ordering::SeqCst)\n                    .into(),\n                TypedArrayKind::Int32 => i32::read_mut(data)\n                    .compare_exchange(exp as i32, rep as i32, Ordering::SeqCst)\n                    .into(),\n                TypedArrayKind::Uint32 => u32::read_mut(data)\n                    .compare_exchange(exp as u32, rep as u32, Ordering::SeqCst)\n                    .into(),\n                TypedArrayKind::BigInt64 => i64::read_mut(data)\n                    .compare_exchange(exp as i64, rep as i64, Ordering::SeqCst)\n                    .into(),\n                TypedArrayKind::BigUint64 => u64::read_mut(data)\n                    .compare_exchange(exp, rep, Ordering::SeqCst)\n                    .into(),\n                TypedArrayKind::Uint8Clamped\n                | TypedArrayKind::Float32\n                | TypedArrayKind::Float64 => unreachable!(\n                    \"must have been filtered out by the call to `validate_integer_typed_array`\"\n                ),\n                #[cfg(feature = \"float16\")]\n                TypedArrayKind::Float16 => unreachable!(\n                    \"must have been filtered out by the call to `validate_integer_typed_array`\"\n                ),\n            }\n        };\n\n        Ok(value.into())\n    }\n\n    // =========== Atomics.ops start ===========\n\n    atomic_op! {\n        /// [`Atomics.add ( typedArray, index, value )`][spec]\n        ///\n        /// [spec]: https://tc39.es/ecma262/#sec-atomics.add\n        add\n    }\n\n    atomic_op! {\n        /// [`Atomics.and ( typedArray, index, value )`][spec]\n        ///\n        /// [spec]: https://tc39.es/ecma262/#sec-atomics.and\n        bit_and\n    }\n\n    atomic_op! {\n        /// [`Atomics.exchange ( typedArray, index, value )`][spec]\n        ///\n        /// [spec]: https://tc39.es/ecma262/#sec-atomics.exchange\n        swap\n    }\n\n    atomic_op! {\n        /// [`Atomics.or ( typedArray, index, value )`][spec]\n        ///\n        /// [spec]: https://tc39.es/ecma262/#sec-atomics.or\n        bit_or\n    }\n\n    atomic_op! {\n        /// [`Atomics.sub ( typedArray, index, value )`][spec]\n        ///\n        /// [spec]: https://tc39.es/ecma262/#sec-atomics.sub\n        sub\n    }\n\n    atomic_op! {\n        /// [`Atomics.xor ( typedArray, index, value )`][spec]\n        ///\n        /// [spec]: https://tc39.es/ecma262/#sec-atomics.xor\n        bit_xor\n    }\n\n    /// [`Atomics.wait ( typedArray, index, value, timeout )`][spec]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-atomics.wait\n    fn wait<const ASYNC: bool>(\n        _: &JsValue,\n        args: &[JsValue],\n        context: &mut Context,\n    ) -> JsResult<JsValue> {\n        let array = args.get_or_undefined(0);\n        let index = args.get_or_undefined(1);\n        let value = args.get_or_undefined(2);\n        let timeout = args.get_or_undefined(3);\n\n        // 1. Let taRecord be ? ValidateIntegerTypedArray(typedArray, true).\n        let (ta, buf_len) = validate_integer_typed_array(array, true)?;\n\n        // 2. Let buffer be taRecord.[[Object]].[[ViewedArrayBuffer]].\n        // 3. If IsSharedArrayBuffer(buffer) is false, throw a TypeError exception.\n        let buffer = match ta.borrow().data().viewed_array_buffer() {\n            BufferObject::SharedBuffer(buf) => buf.clone(),\n            BufferObject::Buffer(_) => {\n                return Err(JsNativeError::typ()\n                    .with_message(\"cannot use `ArrayBuffer` for an atomic wait\")\n                    .into());\n            }\n        };\n\n        // 4. Let i be ? ValidateAtomicAccess(taRecord, index).\n        let access = validate_atomic_access(&ta, buf_len, index, context)?;\n\n        // 5. Let arrayTypeName be typedArray.[[TypedArrayName]].\n        let value = if access.kind == TypedArrayKind::BigInt64 {\n            // 6. If typedArray.[[TypedArrayName]] is \"BigInt64Array\", let v be ? ToBigInt64(value).\n            value.to_big_int64(context)?\n        } else {\n            // 7. Else, let v be ? ToInt32(value).\n            i64::from(value.to_i32(context)?)\n        };\n\n        // 8. Let q be ? ToNumber(timeout).\n        // 9. If q is either NaN or +∞𝔽, let t be +∞; else if q is -∞𝔽, let t be 0; else let t be max(ℝ(q), 0).\n        let mut timeout = timeout.to_number(context)?;\n        // convert to nanoseconds to discard any excessively big timeouts.\n        timeout = timeout.clamp(0.0, f64::INFINITY) * 1000.0 * 1000.0;\n        let timeout = if timeout.is_nan() || timeout.is_infinite() || timeout > u64::MAX as f64 {\n            None\n        } else {\n            Some(Duration::from_nanos(timeout as u64))\n        };\n\n        // 10. If mode is sync and AgentCanSuspend() is false, throw a TypeError exception.\n        if !ASYNC && !context.can_block() {\n            return Err(JsNativeError::typ()\n                .with_message(\"agent cannot be suspended\")\n                .into());\n        }\n\n        // SAFETY: the validity of `addr` is verified by our call to `validate_atomic_access`.\n        if ASYNC {\n            let (promise, resolvers) = JsPromise::new_pending(context);\n            let result = unsafe {\n                if access.kind == TypedArrayKind::BigInt64 {\n                    futex::wait_async(\n                        buffer.borrow().data(),\n                        buf_len,\n                        access.byte_offset,\n                        value,\n                        timeout,\n                        resolvers,\n                        context,\n                    )?\n                } else {\n                    // value must fit into `i32` since it came from an `i32` above.\n                    futex::wait_async(\n                        buffer.borrow().data(),\n                        buf_len,\n                        access.byte_offset,\n                        value as i32,\n                        timeout,\n                        resolvers,\n                        context,\n                    )?\n                }\n            };\n\n            let (is_async, value) = match result {\n                futex::AtomicsWaitResult::NotEqual => (false, js_string!(\"not-equal\").into()),\n                futex::AtomicsWaitResult::TimedOut => (false, js_string!(\"timed-out\").into()),\n                futex::AtomicsWaitResult::Ok => (true, promise.into()),\n            };\n\n            Ok(context\n                .intrinsics()\n                .templates()\n                .wait_async()\n                .create(OrdinaryObject, vec![is_async.into(), value])\n                .into())\n        } else {\n            let result = unsafe {\n                if access.kind == TypedArrayKind::BigInt64 {\n                    futex::wait(\n                        buffer.borrow().data(),\n                        buf_len,\n                        access.byte_offset,\n                        value,\n                        timeout,\n                    )?\n                } else {\n                    // value must fit into `i32` since it came from an `i32` above.\n                    futex::wait(\n                        buffer.borrow().data(),\n                        buf_len,\n                        access.byte_offset,\n                        value as i32,\n                        timeout,\n                    )?\n                }\n            };\n\n            Ok(match result {\n                futex::AtomicsWaitResult::NotEqual => js_string!(\"not-equal\"),\n                futex::AtomicsWaitResult::TimedOut => js_string!(\"timed-out\"),\n                futex::AtomicsWaitResult::Ok => js_string!(\"ok\"),\n            }\n            .into())\n        }\n    }\n\n    /// [`Atomics.notify ( typedArray, index, count )`][spec]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-atomics.notify\n    fn notify(_: &JsValue, args: &[JsValue], context: &mut Context) -> JsResult<JsValue> {\n        let array = args.get_or_undefined(0);\n        let index = args.get_or_undefined(1);\n        let count = args.get_or_undefined(2);\n\n        // 1. Let indexedPosition be ? ValidateAtomicAccessOnIntegerTypedArray(typedArray, index, true).\n        let (ta, buf_len) = validate_integer_typed_array(array, true)?;\n        let access = validate_atomic_access(&ta, buf_len, index, context)?;\n\n        // 2. If count is undefined, then\n        let count = if count.is_undefined() {\n            // a. Let c be +∞.\n            u64::MAX\n        } else {\n            // 3. Else,\n            //     a. Let intCount be ? ToIntegerOrInfinity(count).\n            //     b. Let c be max(intCount, 0).\n            match count.to_integer_or_infinity(context)? {\n                IntegerOrInfinity::PositiveInfinity => u64::MAX,\n                IntegerOrInfinity::Integer(i) => i64::max(i, 0) as u64,\n                IntegerOrInfinity::NegativeInfinity => 0,\n            }\n        };\n\n        // 4. Let buffer be typedArray.[[ViewedArrayBuffer]].\n        // 5. Let block be buffer.[[ArrayBufferData]].\n        // 6. If IsSharedArrayBuffer(buffer) is false, return +0𝔽.\n        let ta = ta.borrow();\n        let BufferRef::SharedBuffer(shared) = ta.data().viewed_array_buffer().as_buffer() else {\n            return Ok(0.into());\n        };\n\n        let count = futex::notify(&shared, access.byte_offset, count)?;\n\n        // 12. Let n be the number of elements in S.\n        // 13. Return 𝔽(n).\n        Ok(count.into())\n    }\n\n    /// [`Atomics.pause ( [ iterationNumber ] )`][spec]\n    ///\n    /// [spec]: https://tc39.es/proposal-atomics-microwait/#Atomics.pause\n    #[cfg(feature = \"experimental\")]\n    fn pause(_: &JsValue, args: &[JsValue], context: &mut Context) -> JsResult<JsValue> {\n        use super::Number;\n\n        let iteration_number = args.get_or_undefined(0);\n\n        // 1. If iterationNumber is not undefined, then\n        let iterations = if iteration_number.is_undefined() {\n            1\n        } else {\n            // a. If iterationNumber is not an integral Number, throw a TypeError exception.\n            if !Number::is_integer(iteration_number) {\n                return Err(JsNativeError::typ()\n                    .with_message(\"`iterationNumber` must be an integral Number\")\n                    .into());\n            }\n\n            // b. If ℝ(iterationNumber) < 0, throw a RangeError exception.\n            let iteration_number = iteration_number.to_number(context)? as i16;\n            if iteration_number < 0 {\n                return Err(JsNativeError::range()\n                    .with_message(\"`iterationNumber` must be a positive integer\")\n                    .into());\n            }\n\n            // Clamp to u16 so that the main thread cannot block using this.\n            iteration_number as u16\n        };\n\n        // 2. If the execution environment of the ECMAScript implementation supports a signal that the current executing code\n        //    is in a spin-wait loop, send that signal. An ECMAScript implementation may send that signal multiple times,\n        //    determined by iterationNumber when not undefined. The number of times the signal is sent for an integral Number\n        //    N is at most the number of times it is sent for N + 1.\n        for _ in 0..iterations {\n            std::hint::spin_loop();\n        }\n\n        // 3. Return undefined.\n        Ok(JsValue::undefined())\n    }\n}\n\n/// [`ValidateIntegerTypedArray ( typedArray, waitable )`][spec]\n///\n/// [spec]: https://tc39.es/ecma262/#sec-validateintegertypedarray\nfn validate_integer_typed_array(\n    array: &JsValue,\n    waitable: bool,\n) -> JsResult<(JsObject<TypedArray>, usize)> {\n    // 1. Let taRecord be ? ValidateTypedArray(typedArray, unordered).\n    // 2. NOTE: Bounds checking is not a synchronizing operation when typedArray's backing buffer is a growable SharedArrayBuffer.\n    let ta_record = TypedArray::validate(array, Ordering::Relaxed)?;\n\n    {\n        let array = ta_record.0.borrow();\n\n        // 3. If waitable is true, then\n        if waitable {\n            //     a. If typedArray.[[TypedArrayName]] is neither \"Int32Array\" nor \"BigInt64Array\", throw a TypeError exception.\n            if ![TypedArrayKind::Int32, TypedArrayKind::BigInt64].contains(&array.data().kind()) {\n                return Err(JsNativeError::typ()\n                    .with_message(\"can only atomically wait using Int32 or BigInt64 arrays\")\n                    .into());\n            }\n        } else {\n            // 4. Else,\n            //     a. Let type be TypedArrayElementType(typedArray).\n            //     b. If IsUnclampedIntegerElementType(type) is false and IsBigIntElementType(type) is false, throw a TypeError exception.\n            if !array.data().kind().supports_atomic_ops() {\n                return Err(JsNativeError::typ()\n                    .with_message(\n                        \"platform doesn't support atomic operations on the provided `TypedArray`\",\n                    )\n                    .into());\n            }\n        }\n    }\n\n    // 5. Return taRecord.\n    Ok(ta_record)\n}\n\n#[derive(Debug, Copy, Clone)]\nstruct AtomicAccess {\n    byte_offset: usize,\n    kind: TypedArrayKind,\n}\n\n/// [`ValidateAtomicAccess ( taRecord, requestIndex )`][spec]\n///\n/// [spec]: https://tc39.es/ecma262/#sec-validateatomicaccess\nfn validate_atomic_access(\n    array: &JsObject<TypedArray>,\n    buf_len: usize,\n    request_index: &JsValue,\n    context: &mut Context,\n) -> JsResult<AtomicAccess> {\n    // 5. Let typedArray be taRecord.[[Object]].\n    let (length, kind, offset) = {\n        let array = array.borrow();\n        let array = array.data();\n\n        // 1. Let length be typedArray.[[ArrayLength]].\n        // 6. Let elementSize be TypedArrayElementSize(typedArray).\n        // 7. Let offset be typedArray.[[ByteOffset]].\n        (\n            array.array_length(buf_len),\n            array.kind(),\n            array.byte_offset(),\n        )\n    };\n\n    // 2. Let accessIndex be ? ToIndex(requestIndex).\n    let access_index = request_index.to_index(context)?;\n\n    // 3. Assert: accessIndex ≥ 0.\n    //    ensured by the type.\n\n    // 4. If accessIndex ≥ length, throw a RangeError exception.\n    if access_index >= length {\n        return Err(JsNativeError::range()\n            .with_message(\"index for typed array outside of bounds\")\n            .into());\n    }\n\n    // 8. Return (accessIndex × elementSize) + offset.\n    let offset = ((access_index * kind.element_size()) + offset) as usize;\n    Ok(AtomicAccess {\n        byte_offset: offset,\n        kind,\n    })\n}\n\n#[cfg(test)]\nmod tests;\n"
  },
  {
    "path": "core/engine/src/builtins/atomics/tests.rs",
    "content": "use std::{rc::Rc, time::Duration};\n\nuse crate::{\n    Context, JsValue,\n    builtins::atomics::Atomics,\n    js_string,\n    module::IdleModuleLoader,\n    object::{JsInt32Array, JsPromise, JsSharedArrayBuffer},\n    value::TryFromJs,\n};\n\n#[test]\nfn waiterlist_block_indexedposition_wake() {\n    const NUMAGENT: i32 = 2;\n    const RUNNING: i32 = 4;\n\n    let context = &mut Context::builder()\n        .can_block(true)\n        .module_loader(Rc::new(IdleModuleLoader))\n        .build()\n        .unwrap();\n    let buffer = JsSharedArrayBuffer::new(size_of::<i32>() * 5, context).unwrap();\n    let inner_buffer = buffer.inner();\n    let i32a = JsInt32Array::from_shared_array_buffer(buffer.clone(), context).unwrap();\n\n    std::thread::scope(|s| {\n        let mut threads = Vec::new();\n        for idx in [2, 0] {\n            let buffer = inner_buffer.clone();\n            let handle = s.spawn(move || {\n                let context = &mut Context::builder()\n                    .can_block(true)\n                    .module_loader(Rc::new(IdleModuleLoader))\n                    .build()\n                    .unwrap();\n                let buffer = JsSharedArrayBuffer::from_buffer(buffer, context);\n                let i32a = JsInt32Array::from_shared_array_buffer(buffer, context).unwrap();\n\n                Atomics::add(\n                    &JsValue::undefined(),\n                    &[i32a.clone().into(), RUNNING.into(), 1.into()],\n                    context,\n                )\n                .unwrap();\n\n                let promise = JsPromise::try_from_js(\n                    &Atomics::wait::<true>(\n                        &JsValue::undefined(),\n                        &[\n                            i32a.clone().into(),\n                            idx.into(),\n                            0.into(),\n                            f64::INFINITY.into(),\n                        ],\n                        context,\n                    )\n                    .unwrap()\n                    .as_object()\n                    .unwrap()\n                    .get(js_string!(\"value\"), context)\n                    .unwrap(),\n                    context,\n                )\n                .unwrap();\n\n                assert_eq!(\n                    promise\n                        .await_blocking(context)\n                        .unwrap()\n                        .to_string(context)\n                        .unwrap()\n                        .to_std_string_lossy(),\n                    \"ok\"\n                );\n            });\n            threads.push(handle);\n        }\n\n        while Atomics::load(\n            &JsValue::undefined(),\n            &[i32a.clone().into(), RUNNING.into()],\n            context,\n        )\n        .unwrap()\n        .to_i32(context)\n        .unwrap()\n            != NUMAGENT\n        {}\n\n        std::thread::sleep(Duration::from_millis(100));\n\n        assert_eq!(\n            Atomics::load(\n                &JsValue::undefined(),\n                &[i32a.clone().into(), RUNNING.into()],\n                context,\n            )\n            .unwrap()\n            .to_i32(context)\n            .unwrap(),\n            NUMAGENT\n        );\n\n        for missing in [1, 3] {\n            assert_eq!(\n                Atomics::notify(\n                    &JsValue::undefined(),\n                    &[i32a.clone().into(), missing.into()],\n                    context\n                )\n                .unwrap()\n                .to_i32(context)\n                .unwrap(),\n                0\n            );\n        }\n\n        for exists in [2, 0] {\n            assert_eq!(\n                Atomics::notify(\n                    &JsValue::undefined(),\n                    &[i32a.clone().into(), exists.into()],\n                    context\n                )\n                .unwrap()\n                .to_i32(context)\n                .unwrap(),\n                1\n            );\n        }\n    });\n}\n"
  },
  {
    "path": "core/engine/src/builtins/bigint/mod.rs",
    "content": "//! Boa's implementation of ECMAScript's global `BigInt` object.\n//!\n//! `BigInt` is a built-in object that provides a way to represent whole numbers larger\n//! than the largest number JavaScript can reliably represent with the Number primitive\n//! and represented by the `Number.MAX_SAFE_INTEGER` constant.\n//! `BigInt` can be used for arbitrarily large integers.\n//!\n//! More information:\n//!  - [ECMAScript reference][spec]\n//!  - [MDN documentation][mdn]\n//!\n//! [spec]: https://tc39.es/ecma262/#sec-bigint-objects\n//! [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/BigInt\n\nuse crate::{\n    Context, JsArgs, JsBigInt, JsExpect, JsResult, JsString, JsValue,\n    builtins::BuiltInObject,\n    context::intrinsics::{Intrinsics, StandardConstructor, StandardConstructors},\n    error::JsNativeError,\n    js_string,\n    object::JsObject,\n    property::Attribute,\n    realm::Realm,\n    string::StaticJsStrings,\n    symbol::JsSymbol,\n    value::{IntegerOrInfinity, PreferredType},\n};\nuse num_bigint::ToBigInt;\n\nuse super::{BuiltInBuilder, BuiltInConstructor, IntrinsicObject};\n\n#[cfg(test)]\nmod tests;\n\n/// `BigInt` implementation.\n#[derive(Debug, Clone, Copy)]\npub struct BigInt;\n\nimpl IntrinsicObject for BigInt {\n    fn init(realm: &Realm) {\n        BuiltInBuilder::from_standard_constructor::<Self>(realm)\n            .method(Self::to_string, js_string!(\"toString\"), 0)\n            .method(Self::to_locale_string, js_string!(\"toLocaleString\"), 0)\n            .method(Self::value_of, js_string!(\"valueOf\"), 0)\n            .static_method(Self::as_int_n, js_string!(\"asIntN\"), 2)\n            .static_method(Self::as_uint_n, js_string!(\"asUintN\"), 2)\n            .property(\n                JsSymbol::to_string_tag(),\n                Self::NAME,\n                Attribute::READONLY | Attribute::NON_ENUMERABLE | Attribute::CONFIGURABLE,\n            )\n            .build();\n    }\n\n    fn get(intrinsics: &Intrinsics) -> JsObject {\n        Self::STANDARD_CONSTRUCTOR(intrinsics.constructors()).constructor()\n    }\n}\n\nimpl BuiltInObject for BigInt {\n    const NAME: JsString = StaticJsStrings::BIG_INT;\n}\n\nimpl BuiltInConstructor for BigInt {\n    const CONSTRUCTOR_ARGUMENTS: usize = 1;\n    const PROTOTYPE_STORAGE_SLOTS: usize = 4;\n    const CONSTRUCTOR_STORAGE_SLOTS: usize = 2;\n\n    const STANDARD_CONSTRUCTOR: fn(&StandardConstructors) -> &StandardConstructor =\n        StandardConstructors::bigint;\n\n    /// `BigInt()`\n    ///\n    /// The `BigInt()` constructor is used to create `BigInt` objects.\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///  - [MDN documentation][mdn]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-bigint-objects\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/BigInt/BigInt\n    fn constructor(\n        new_target: &JsValue,\n        args: &[JsValue],\n        context: &mut Context,\n    ) -> JsResult<JsValue> {\n        // 1. If NewTarget is not undefined, throw a TypeError exception.\n        if !new_target.is_undefined() {\n            return Err(JsNativeError::typ()\n                .with_message(\"BigInt is not a constructor\")\n                .into());\n        }\n\n        let value = args.get_or_undefined(0);\n\n        // 2. Let prim be ? ToPrimitive(value, number).\n        let prim = value.to_primitive(context, PreferredType::Number)?;\n\n        // 3. If Type(prim) is Number, return ? NumberToBigInt(prim).\n        if let Some(number) = prim.as_number() {\n            return Self::number_to_bigint(number);\n        }\n\n        // 4. Otherwise, return ? ToBigInt(prim).\n        Ok(prim.to_bigint(context)?.into())\n    }\n}\n\nimpl BigInt {\n    /// `NumberToBigInt ( number )`\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-numbertobigint\n    fn number_to_bigint(number: f64) -> JsResult<JsValue> {\n        // 1. If IsIntegralNumber(number) is false, throw a RangeError exception.\n        if number.is_nan() || number.is_infinite() || number.fract() != 0.0 {\n            return Err(JsNativeError::range()\n                .with_message(format!(\"cannot convert {number} to a BigInt\"))\n                .into());\n        }\n\n        // 2. Return the BigInt value that represents ℝ(number).\n        Ok(JsBigInt::from(\n            number\n                .to_bigint()\n                .js_expect(\"This conversion must be safe\")?,\n        )\n        .into())\n    }\n\n    /// The abstract operation `thisBigIntValue` takes argument value.\n    ///\n    /// The phrase “this `BigInt` value” within the specification of a method refers to the\n    /// result returned by calling the abstract operation `thisBigIntValue` with the `this` value\n    /// of the method invocation passed as the argument.\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-thisbigintvalue\n    fn this_bigint_value(value: &JsValue) -> JsResult<JsBigInt> {\n        value\n            // 1. If Type(value) is BigInt, return value.\n            .as_bigint()\n            // 2. If Type(value) is Object and value has a [[BigIntData]] internal slot, then\n            //    a. Assert: Type(value.[[BigIntData]]) is BigInt.\n            //    b. Return value.[[BigIntData]].\n            .or_else(|| {\n                value\n                    .as_object()\n                    .and_then(|obj| obj.downcast_ref::<JsBigInt>().as_deref().cloned())\n            })\n            // 3. Throw a TypeError exception.\n            .ok_or_else(|| {\n                JsNativeError::typ()\n                    .with_message(\"'this' is not a BigInt\")\n                    .into()\n            })\n    }\n\n    /// `BigInt.prototype.toString( [radix] )`\n    ///\n    /// The `toString()` method returns a string representing the specified `BigInt` object.\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///  - [MDN documentation][mdn]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-bigint.prototype.tostring\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/BigInt/toString\n    #[allow(clippy::wrong_self_convention)]\n    pub(crate) fn to_string(\n        this: &JsValue,\n        args: &[JsValue],\n        context: &mut Context,\n    ) -> JsResult<JsValue> {\n        // 1. Let x be ? thisBigIntValue(this value).\n        let x = Self::this_bigint_value(this)?;\n\n        let radix = args.get_or_undefined(0);\n\n        // 2. If radix is undefined, let radixMV be 10.\n        let radix_mv = if radix.is_undefined() {\n            // 5. If radixMV = 10, return ! ToString(x).\n            // Note: early return optimization.\n            return Ok(js_string!(x.to_string()).into());\n        // 3. Else, let radixMV be ? ToIntegerOrInfinity(radix).\n        } else {\n            radix.to_integer_or_infinity(context)?\n        };\n\n        // 4. If radixMV < 2 or radixMV > 36, throw a RangeError exception.\n        let radix_mv = match radix_mv {\n            IntegerOrInfinity::Integer(i) if (2..=36).contains(&i) => i,\n            _ => {\n                return Err(JsNativeError::range()\n                    .with_message(\"radix must be an integer at least 2 and no greater than 36\")\n                    .into());\n            }\n        };\n\n        // 5. If radixMV = 10, return ! ToString(x).\n        if radix_mv == 10 {\n            return Ok(js_string!(x.to_string()).into());\n        }\n\n        // 1. Let x be ? thisBigIntValue(this value).\n        // 6. Return the String representation of this Number value using the radix specified by radixMV.\n        //    Letters a-z are used for digits with values 10 through 35.\n        //    The precise algorithm is implementation-defined, however the algorithm should be a generalization of that specified in 6.1.6.2.23.\n        Ok(JsValue::new(js_string!(x.to_string_radix(radix_mv as u32))))\n    }\n\n    #[allow(\n        unused_variables,\n        reason = \"`args` is used if the `intl` feature is enabled\"\n    )]\n    fn to_locale_string(\n        this: &JsValue,\n        args: &[JsValue],\n        context: &mut Context,\n    ) -> JsResult<JsValue> {\n        #[cfg(feature = \"intl\")]\n        {\n            // 1. Let x be ? ThisBigIntValue(this value).\n\n            use fixed_decimal::Decimal;\n\n            use crate::builtins::intl::NumberFormat;\n            let x = Self::this_bigint_value(this)?;\n            let locales = args.get_or_undefined(0).clone();\n            let options = args.get_or_undefined(1).clone();\n\n            // 2. Let numberFormat be ? Construct(%Intl.NumberFormat%, « locales, options »).\n            let number_format = NumberFormat::new(&locales, &options, context)?;\n            let x = &mut Decimal::try_from_str(&x.to_string())\n                .map_err(|err| JsNativeError::range().with_message(err.to_string()))?;\n\n            // 3. Return FormatNumeric(numberFormat, ℝ(x)).\n            Ok(js_string!(number_format.format(x).to_string()).into())\n        }\n\n        #[cfg(not(feature = \"intl\"))]\n        {\n            // Arguments are reserved, so we cannot pass them to the `toString` method.\n            Self::to_string(this, &[], context)\n        }\n    }\n\n    /// `BigInt.prototype.valueOf()`\n    ///\n    /// The `valueOf()` method returns the wrapped primitive value of a Number object.\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///  - [MDN documentation][mdn]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-bigint.prototype.valueof\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/BigInt/valueOf\n    pub(crate) fn value_of(this: &JsValue, _: &[JsValue], _: &mut Context) -> JsResult<JsValue> {\n        Ok(JsValue::new(Self::this_bigint_value(this)?))\n    }\n\n    /// `BigInt.asIntN()`\n    ///\n    /// The `BigInt.asIntN()` method wraps the value of a `BigInt` to a signed integer between `-2**(width - 1)` and `2**(width-1) - 1`.\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-bigint.asintn\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/BigInt/asIntN\n    #[allow(clippy::wrong_self_convention)]\n    pub(crate) fn as_int_n(\n        _: &JsValue,\n        args: &[JsValue],\n        context: &mut Context,\n    ) -> JsResult<JsValue> {\n        let (modulo, bits) = Self::calculate_as_uint_n(args, context)?;\n\n        if bits > 0\n            && modulo >= JsBigInt::pow(&JsBigInt::new(2), &JsBigInt::new(i64::from(bits) - 1))?\n        {\n            Ok(JsValue::new(JsBigInt::sub(\n                &modulo,\n                &JsBigInt::pow(&JsBigInt::new(2), &JsBigInt::new(i64::from(bits)))?,\n            )))\n        } else {\n            Ok(JsValue::new(modulo))\n        }\n    }\n\n    /// `BigInt.asUintN()`\n    ///\n    /// The `BigInt.asUintN()` method wraps the value of a `BigInt` to an unsigned integer between `0` and `2**(width) - 1`.\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-bigint.asuintn\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/BigInt/asUintN\n    #[allow(clippy::wrong_self_convention)]\n    pub(crate) fn as_uint_n(\n        _: &JsValue,\n        args: &[JsValue],\n        context: &mut Context,\n    ) -> JsResult<JsValue> {\n        let (modulo, _) = Self::calculate_as_uint_n(args, context)?;\n\n        Ok(JsValue::new(modulo))\n    }\n\n    /// Helper function to wrap the value of a `BigInt` to an unsigned integer.\n    ///\n    /// This function expects the same arguments as `as_uint_n` and wraps the value of a `BigInt`.\n    /// Additionally to the wrapped unsigned value it returns the converted `bits` argument, so it\n    /// can be reused from the `as_int_n` method.\n    fn calculate_as_uint_n(args: &[JsValue], context: &mut Context) -> JsResult<(JsBigInt, u32)> {\n        let bits_arg = args.get_or_undefined(0);\n        let bigint_arg = args.get_or_undefined(1);\n\n        let bits = bits_arg.to_index(context)?;\n        let bits = u32::try_from(bits).unwrap_or(u32::MAX);\n\n        let bigint = bigint_arg.to_bigint(context)?;\n\n        Ok((\n            JsBigInt::mod_floor(\n                &bigint,\n                &JsBigInt::pow(&JsBigInt::new(2), &JsBigInt::new(i64::from(bits)))?,\n            ),\n            bits,\n        ))\n    }\n}\n"
  },
  {
    "path": "core/engine/src/builtins/bigint/tests.rs",
    "content": "use boa_macros::js_str;\n\nuse crate::{JsBigInt, JsNativeErrorKind, TestAction, run_test_actions};\n\n#[test]\nfn equality() {\n    run_test_actions([\n        TestAction::assert(\"0n == 0n\"),\n        TestAction::assert(\"!(1n == 0n)\"),\n        TestAction::assert(\n            \"1000000000000000000000000000000000n == 1000000000000000000000000000000000n\",\n        ),\n        TestAction::assert(\"0n == ''\"),\n        TestAction::assert(\"100n == '100'\"),\n        TestAction::assert(\"!(100n == '100.5')\"),\n        TestAction::assert(\"10000000000000000n == '10000000000000000'\"),\n        TestAction::assert(\"'' == 0n\"),\n        TestAction::assert(\"'100' == 100n\"),\n        TestAction::assert(\"!('100.5' == 100n)\"),\n        TestAction::assert(\"'10000000000000000' == 10000000000000000n\"),\n        TestAction::assert(\"0n == 0\"),\n        TestAction::assert(\"0n == 0.0\"),\n        TestAction::assert(\"100n == 100\"),\n        TestAction::assert(\"100n == 100.0\"),\n        TestAction::assert(\"!(100n == '100.5')\"),\n        TestAction::assert(\"!(100n == '1005')\"),\n        TestAction::assert(\"10000000000000000n == 10000000000000000\"),\n        TestAction::assert(\"0 == 0n\"),\n        TestAction::assert(\"0.0 == 0n\"),\n        TestAction::assert(\"100 == 100n\"),\n        TestAction::assert(\"100.0 == 100n\"),\n        TestAction::assert(\"!(100.5 == 100n)\"),\n        TestAction::assert(\"!(1005 == 100n)\"),\n        TestAction::assert(\"10000000000000000 == 10000000000000000n\"),\n    ]);\n}\n\n#[test]\nfn bigint_function_conversion_from_integer() {\n    run_test_actions([\n        TestAction::assert_eq(\"BigInt(1000)\", JsBigInt::from(1000)),\n        TestAction::assert_eq(\n            \"BigInt(20000000000000000)\",\n            JsBigInt::from_string(\"20000000000000000\").unwrap(),\n        ),\n        TestAction::assert_eq(\n            \"BigInt(1000000000000000000000000000000000)\",\n            JsBigInt::from_string(\"999999999999999945575230987042816\").unwrap(),\n        ),\n    ]);\n}\n\n#[test]\nfn bigint_function_conversion_from_rational() {\n    run_test_actions([\n        TestAction::assert_eq(\"BigInt(0.0)\", JsBigInt::from(0)),\n        TestAction::assert_eq(\"BigInt(1.0)\", JsBigInt::from(1)),\n        TestAction::assert_eq(\"BigInt(10000.0)\", JsBigInt::from(10_000)),\n    ]);\n}\n\n#[test]\nfn bigint_function_throws() {\n    run_test_actions([\n        TestAction::assert_native_error(\n            \"BigInt(0.1)\",\n            JsNativeErrorKind::Range,\n            \"cannot convert 0.1 to a BigInt\",\n        ),\n        TestAction::assert_native_error(\n            \"BigInt(null)\",\n            JsNativeErrorKind::Type,\n            \"cannot convert null to a BigInt\",\n        ),\n        TestAction::assert_native_error(\n            \"BigInt(undefined)\",\n            JsNativeErrorKind::Type,\n            \"cannot convert undefined to a BigInt\",\n        ),\n    ]);\n}\n\n#[test]\nfn bigint_function_conversion_from_string() {\n    run_test_actions([\n        TestAction::assert_eq(\"BigInt('')\", JsBigInt::from(0)),\n        TestAction::assert_eq(\"BigInt('   ')\", JsBigInt::from(0)),\n        TestAction::assert_eq(\n            \"BigInt('200000000000000000')\",\n            JsBigInt::from_string(\"200000000000000000\").unwrap(),\n        ),\n        TestAction::assert_eq(\n            \"BigInt('1000000000000000000000000000000000')\",\n            JsBigInt::from_string(\"1000000000000000000000000000000000\").unwrap(),\n        ),\n        TestAction::assert_eq(\"BigInt('0b1111')\", JsBigInt::from(15)),\n        TestAction::assert_eq(\"BigInt('0o70')\", JsBigInt::from(56)),\n        TestAction::assert_eq(\"BigInt('0xFF')\", JsBigInt::from(255)),\n    ]);\n}\n\n#[test]\nfn operations() {\n    run_test_actions([\n        TestAction::assert_eq(\"10000n + 1000n\", JsBigInt::from(11_000)),\n        TestAction::assert_eq(\"10000n - 1000n\", JsBigInt::from(9_000)),\n        TestAction::assert_eq(\n            \"123456789n * 102030n\",\n            JsBigInt::from_string(\"12596296181670\").unwrap(),\n        ),\n        TestAction::assert_eq(\"15000n / 50n\", JsBigInt::from(300)),\n        TestAction::assert_eq(\"15001n / 50n\", JsBigInt::from(300)),\n        TestAction::assert_native_error(\n            \"1n/0n\",\n            JsNativeErrorKind::Range,\n            \"BigInt division by zero\",\n        ),\n        TestAction::assert_eq(\"15007n % 10n\", JsBigInt::from(7)),\n        TestAction::assert_native_error(\n            \"1n % 0n\",\n            JsNativeErrorKind::Range,\n            \"BigInt division by zero\",\n        ),\n        TestAction::assert_eq(\n            \"100n ** 10n\",\n            JsBigInt::from_string(\"100000000000000000000\").unwrap(),\n        ),\n        TestAction::assert_native_error(\n            \"10n ** (-10n)\",\n            JsNativeErrorKind::Range,\n            \"BigInt negative exponent\",\n        ),\n        TestAction::assert_eq(\"8n << 2n\", JsBigInt::from(32)),\n        TestAction::assert_native_error(\n            \"1000n << 1000000000000000n\",\n            JsNativeErrorKind::Range,\n            \"Maximum BigInt size exceeded\",\n        ),\n        TestAction::assert_eq(\"8n >> 2n\", JsBigInt::from(2)),\n        TestAction::assert_eq(\"1000n >> 1000000000000000n\", JsBigInt::zero()),\n        TestAction::assert_eq(\"(-1000n) >> 1000000000000000n\", JsBigInt::from(-1)),\n        TestAction::assert_eq(\"0n >> 1000000000000000n\", JsBigInt::zero()),\n        // shift_left with large negative amount (equivalent to large right-shift)\n        TestAction::assert_eq(\"1000n << (-1000000000000000n)\", JsBigInt::zero()),\n        TestAction::assert_eq(\"(-1000n) << (-1000000000000000n)\", JsBigInt::from(-1)),\n        TestAction::assert_eq(\"0n << (-1000000000000000n)\", JsBigInt::zero()),\n    ]);\n}\n\n#[test]\nfn to_string() {\n    run_test_actions([\n        TestAction::assert_eq(\"1000n.toString()\", js_str!(\"1000\")),\n        TestAction::assert_eq(\"1000n.toString(2)\", js_str!(\"1111101000\")),\n        TestAction::assert_eq(\"255n.toString(16)\", js_str!(\"ff\")),\n        TestAction::assert_eq(\"1000n.toString(36)\", js_str!(\"rs\")),\n    ]);\n}\n\n#[test]\nfn to_string_invalid_radix() {\n    run_test_actions([\n        TestAction::assert_native_error(\n            \"10n.toString(null)\",\n            JsNativeErrorKind::Range,\n            \"radix must be an integer at least 2 and no greater than 36\",\n        ),\n        TestAction::assert_native_error(\n            \"10n.toString(-1)\",\n            JsNativeErrorKind::Range,\n            \"radix must be an integer at least 2 and no greater than 36\",\n        ),\n        TestAction::assert_native_error(\n            \"10n.toString(37)\",\n            JsNativeErrorKind::Range,\n            \"radix must be an integer at least 2 and no greater than 36\",\n        ),\n    ]);\n}\n\n#[test]\nfn as_int_n() {\n    run_test_actions([\n        TestAction::assert_eq(\"BigInt.asIntN(0, 1n)\", JsBigInt::from(0)),\n        TestAction::assert_eq(\"BigInt.asIntN(1, 1n)\", JsBigInt::from(-1)),\n        TestAction::assert_eq(\"BigInt.asIntN(3, 10n)\", JsBigInt::from(2)),\n        TestAction::assert_eq(\"BigInt.asIntN({}, 1n)\", JsBigInt::from(0)),\n        TestAction::assert_eq(\"BigInt.asIntN(2, 0n)\", JsBigInt::from(0)),\n        TestAction::assert_eq(\"BigInt.asIntN(2, -0n)\", JsBigInt::from(0)),\n        TestAction::assert_eq(\n            \"BigInt.asIntN(2, -123456789012345678901n)\",\n            JsBigInt::from(-1),\n        ),\n        TestAction::assert_eq(\n            \"BigInt.asIntN(2, -123456789012345678900n)\",\n            JsBigInt::from(0),\n        ),\n        TestAction::assert_eq(\n            \"BigInt.asIntN(2, 123456789012345678900n)\",\n            JsBigInt::from(0),\n        ),\n        TestAction::assert_eq(\n            \"BigInt.asIntN(2, 123456789012345678901n)\",\n            JsBigInt::from(1),\n        ),\n        TestAction::assert_eq(\n            \"BigInt.asIntN(200, 0xcffffffffffffffffffffffffffffffffffffffffffffffffffn)\",\n            JsBigInt::from(-1),\n        ),\n        TestAction::assert_eq(\n            \"BigInt.asIntN(201, 0xcffffffffffffffffffffffffffffffffffffffffffffffffffn)\",\n            JsBigInt::from_string(\"1606938044258990275541962092341162602522202993782792835301375\")\n                .unwrap(),\n        ),\n        TestAction::assert_eq(\n            \"BigInt.asIntN(200, 0xc89e081df68b65fedb32cffea660e55df9605650a603ad5fc54n)\",\n            JsBigInt::from_string(\"-741470203160010616172516490008037905920749803227695190508460\")\n                .unwrap(),\n        ),\n        TestAction::assert_eq(\n            \"BigInt.asIntN(201, 0xc89e081df68b65fedb32cffea660e55df9605650a603ad5fc54n)\",\n            JsBigInt::from_string(\"865467841098979659369445602333124696601453190555097644792916\")\n                .unwrap(),\n        ),\n    ]);\n}\n\n#[test]\nfn as_int_n_errors() {\n    run_test_actions([\n        TestAction::assert_native_error(\n            \"BigInt.asIntN(-1, 0n)\",\n            JsNativeErrorKind::Range,\n            \"Index must be between 0 and  2^53 - 1\",\n        ),\n        TestAction::assert_native_error(\n            \"BigInt.asIntN(-2.5, 0n)\",\n            JsNativeErrorKind::Range,\n            \"Index must be between 0 and  2^53 - 1\",\n        ),\n        TestAction::assert_native_error(\n            \"BigInt.asIntN(9007199254740992, 0n)\",\n            JsNativeErrorKind::Range,\n            \"Index must be between 0 and  2^53 - 1\",\n        ),\n        TestAction::assert_native_error(\n            \"BigInt.asIntN(0n, 0n)\",\n            JsNativeErrorKind::Type,\n            \"argument must not be a bigint\",\n        ),\n    ]);\n}\n\n#[test]\nfn as_uint_n() {\n    run_test_actions([\n        TestAction::assert_eq(\"BigInt.asUintN(0, -2n)\", JsBigInt::from(0)),\n        TestAction::assert_eq(\"BigInt.asUintN(0, -1n)\", JsBigInt::from(0)),\n        TestAction::assert_eq(\"BigInt.asUintN(0, 0n)\", JsBigInt::from(0)),\n        TestAction::assert_eq(\"BigInt.asUintN(0, 1n)\", JsBigInt::from(0)),\n        TestAction::assert_eq(\"BigInt.asUintN(0, 2n)\", JsBigInt::from(0)),\n        //\n        TestAction::assert_eq(\"BigInt.asUintN(1, -3n)\", JsBigInt::from(1)),\n        TestAction::assert_eq(\"BigInt.asUintN(1, -2n)\", JsBigInt::from(0)),\n        TestAction::assert_eq(\"BigInt.asUintN(1, -1n)\", JsBigInt::from(1)),\n        TestAction::assert_eq(\"BigInt.asUintN(1, 0n)\", JsBigInt::from(0)),\n        TestAction::assert_eq(\"BigInt.asUintN(1, 1n)\", JsBigInt::from(1)),\n        TestAction::assert_eq(\"BigInt.asUintN(1, 2n)\", JsBigInt::from(0)),\n        TestAction::assert_eq(\"BigInt.asUintN(1, 3n)\", JsBigInt::from(1)),\n        //\n        TestAction::assert_eq(\n            \"BigInt.asUintN(1, -123456789012345678901n)\",\n            JsBigInt::from(1),\n        ),\n        TestAction::assert_eq(\n            \"BigInt.asUintN(1, -123456789012345678900n)\",\n            JsBigInt::from(0),\n        ),\n        TestAction::assert_eq(\n            \"BigInt.asUintN(1, 123456789012345678900n)\",\n            JsBigInt::from(0),\n        ),\n        TestAction::assert_eq(\n            \"BigInt.asUintN(1, 123456789012345678901n)\",\n            JsBigInt::from(1),\n        ),\n        //\n        TestAction::assert_eq(\n            \"BigInt.asUintN(200, 0xbffffffffffffffffffffffffffffffffffffffffffffffffffn)\",\n            JsBigInt::from_string(\"1606938044258990275541962092341162602522202993782792835301375\")\n                .unwrap(),\n        ),\n        TestAction::assert_eq(\n            \"BigInt.asUintN(201, 0xbffffffffffffffffffffffffffffffffffffffffffffffffffn)\",\n            JsBigInt::from_string(\"3213876088517980551083924184682325205044405987565585670602751\")\n                .unwrap(),\n        ),\n    ]);\n}\n\n#[test]\nfn as_uint_n_errors() {\n    run_test_actions([\n        TestAction::assert_native_error(\n            \"BigInt.asUintN(-1, 0n)\",\n            JsNativeErrorKind::Range,\n            \"Index must be between 0 and  2^53 - 1\",\n        ),\n        TestAction::assert_native_error(\n            \"BigInt.asUintN(-2.5, 0n)\",\n            JsNativeErrorKind::Range,\n            \"Index must be between 0 and  2^53 - 1\",\n        ),\n        TestAction::assert_native_error(\n            \"BigInt.asUintN(9007199254740992, 0n)\",\n            JsNativeErrorKind::Range,\n            \"Index must be between 0 and  2^53 - 1\",\n        ),\n        TestAction::assert_native_error(\n            \"BigInt.asUintN(0n, 0n)\",\n            JsNativeErrorKind::Type,\n            \"argument must not be a bigint\",\n        ),\n    ]);\n}\n"
  },
  {
    "path": "core/engine/src/builtins/boolean/mod.rs",
    "content": "//! Boa's implementation of ECMAScript's global `Boolean` object.\n//!\n//! The `Boolean` object is an object wrapper for a boolean value.\n//!\n//! More information:\n//!  - [ECMAScript reference][spec]\n//!  - [MDN documentation][mdn]\n//!\n//! [spec]: https://tc39.es/ecma262/#sec-boolean-object\n//! [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Boolean\n\n#[cfg(test)]\nmod tests;\n\nuse crate::{\n    Context, JsResult, JsString, JsValue,\n    builtins::BuiltInObject,\n    context::intrinsics::{Intrinsics, StandardConstructor, StandardConstructors},\n    error::JsNativeError,\n    js_string,\n    object::{JsObject, internal_methods::get_prototype_from_constructor},\n    realm::Realm,\n    string::StaticJsStrings,\n};\n\nuse super::{BuiltInBuilder, BuiltInConstructor, IntrinsicObject};\n\n/// Boolean implementation.\n#[derive(Debug, Clone, Copy)]\npub(crate) struct Boolean;\n\nimpl IntrinsicObject for Boolean {\n    fn init(realm: &Realm) {\n        BuiltInBuilder::from_standard_constructor::<Self>(realm)\n            .method(Self::to_string, js_string!(\"toString\"), 0)\n            .method(Self::value_of, js_string!(\"valueOf\"), 0)\n            .build();\n    }\n\n    fn get(intrinsics: &Intrinsics) -> JsObject {\n        Self::STANDARD_CONSTRUCTOR(intrinsics.constructors()).constructor()\n    }\n}\n\nimpl BuiltInObject for Boolean {\n    const NAME: JsString = StaticJsStrings::BOOLEAN;\n}\n\nimpl BuiltInConstructor for Boolean {\n    const CONSTRUCTOR_ARGUMENTS: usize = 1;\n    const PROTOTYPE_STORAGE_SLOTS: usize = 2;\n    const CONSTRUCTOR_STORAGE_SLOTS: usize = 0;\n\n    const STANDARD_CONSTRUCTOR: fn(&StandardConstructors) -> &StandardConstructor =\n        StandardConstructors::boolean;\n\n    /// `[[Construct]]` Create a new boolean object\n    ///\n    /// `[[Call]]` Creates a new boolean primitive\n    fn constructor(\n        new_target: &JsValue,\n        args: &[JsValue],\n        context: &mut Context,\n    ) -> JsResult<JsValue> {\n        // Get the argument, if any\n        let data = args.first().is_some_and(JsValue::to_boolean);\n        if new_target.is_undefined() {\n            return Ok(JsValue::new(data));\n        }\n        let prototype =\n            get_prototype_from_constructor(new_target, StandardConstructors::boolean, context)?;\n        let boolean =\n            JsObject::from_proto_and_data_with_shared_shape(context.root_shape(), prototype, data);\n\n        Ok(boolean.into())\n    }\n}\n\nimpl Boolean {\n    /// An Utility function used to get the internal `[[BooleanData]]`.\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-thisbooleanvalue\n    fn this_boolean_value(value: &JsValue) -> JsResult<bool> {\n        value\n            .as_boolean()\n            .or_else(|| {\n                value\n                    .as_object()\n                    .and_then(|obj| obj.downcast_ref::<bool>().as_deref().copied())\n            })\n            .ok_or_else(|| {\n                JsNativeError::typ()\n                    .with_message(\"'this' is not a boolean\")\n                    .into()\n            })\n    }\n\n    /// The `toString()` method returns a string representing the specified `Boolean` object.\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///  - [MDN documentation][mdn]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-boolean-object\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Boolean/toString\n    #[allow(clippy::wrong_self_convention)]\n    pub(crate) fn to_string(this: &JsValue, _: &[JsValue], _: &mut Context) -> JsResult<JsValue> {\n        let boolean = Self::this_boolean_value(this)?;\n        Ok(JsValue::new(if boolean {\n            js_string!(\"true\")\n        } else {\n            js_string!(\"false\")\n        }))\n    }\n\n    /// The `valueOf()` method returns the primitive value of a `Boolean` object.\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    /// - [MDN documentation][mdn]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-boolean.prototype.valueof\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Boolean/valueOf\n    pub(crate) fn value_of(this: &JsValue, _: &[JsValue], _: &mut Context) -> JsResult<JsValue> {\n        Ok(JsValue::new(Self::this_boolean_value(this)?))\n    }\n}\n"
  },
  {
    "path": "core/engine/src/builtins/boolean/tests.rs",
    "content": "use crate::{TestAction, run_test_actions};\nuse indoc::indoc;\n\n/// Test the correct type is returned from call and construct\n#[test]\nfn construct_and_call() {\n    run_test_actions([\n        TestAction::assert_with_op(\"new Boolean(1)\", |val, _| val.is_object()),\n        TestAction::assert_with_op(\"Boolean(0)\", |val, _| val.is_boolean()),\n    ]);\n}\n\n#[test]\nfn constructor_gives_true_instance() {\n    run_test_actions([\n        TestAction::run(indoc! {r#\"\n                var trueVal = new Boolean(true);\n                var trueNum = new Boolean(1);\n                var trueString = new Boolean(\"true\");\n                var trueBool = new Boolean(trueVal);\n            \"#}),\n        // Values should all be objects\n        TestAction::assert_with_op(\"trueVal\", |val, _| val.is_object()),\n        TestAction::assert_with_op(\"trueNum\", |val, _| val.is_object()),\n        TestAction::assert_with_op(\"trueString\", |val, _| val.is_object()),\n        TestAction::assert_with_op(\"trueBool\", |val, _| val.is_object()),\n        // Values should all be truthy\n        TestAction::assert(\"trueVal.valueOf()\"),\n        TestAction::assert(\"trueNum.valueOf()\"),\n        TestAction::assert(\"trueString.valueOf()\"),\n        TestAction::assert(\"trueBool.valueOf()\"),\n    ]);\n}\n\n#[test]\nfn instances_have_correct_proto_set() {\n    run_test_actions([TestAction::assert(\n        \"Object.getPrototypeOf(new Boolean(true)) === Boolean.prototype\",\n    )]);\n}\n"
  },
  {
    "path": "core/engine/src/builtins/builder.rs",
    "content": "use crate::{\n    JsObject, JsString, JsValue, NativeFunction, js_string,\n    native_function::{NativeFunctionObject, NativeFunctionPointer},\n    object::{\n        CONSTRUCTOR, FunctionBinding, JsFunction, JsPrototype, PROTOTYPE,\n        shape::{property_table::PropertyTableInner, slot::SlotAttributes},\n    },\n    property::{Attribute, PropertyDescriptor, PropertyKey},\n    realm::Realm,\n    string::StaticJsStrings,\n};\n\nuse super::{BuiltInConstructor, IntrinsicObject, function::ConstructorKind};\n\n/// Marker for a constructor function.\n// TODO: Remove this marker and use `Constructor` directly.\n#[allow(dead_code)]\npub(crate) struct Constructor {\n    prototype: JsObject,\n    inherits: JsPrototype,\n    attributes: Attribute,\n}\n\n/// Marker for a constructor function without a custom prototype for its instances.\n// TODO: Remove this marker and use `ConstructorNoProto` directly.\n#[allow(dead_code)]\npub(crate) struct ConstructorNoProto;\n\n/// Marker for an ordinary function.\npub(crate) struct OrdinaryFunction;\n\n/// Indicates if the marker is a constructor.\npub(crate) trait IsConstructor {\n    const IS_CONSTRUCTOR: bool;\n}\n\nimpl IsConstructor for Constructor {\n    const IS_CONSTRUCTOR: bool = true;\n}\n\nimpl IsConstructor for ConstructorNoProto {\n    const IS_CONSTRUCTOR: bool = true;\n}\n\nimpl IsConstructor for OrdinaryFunction {\n    const IS_CONSTRUCTOR: bool = false;\n}\n\n/// Marker for a callable object.\npub(crate) struct Callable<Kind> {\n    function: NativeFunctionPointer,\n    name: JsString,\n    length: usize,\n    kind: Kind,\n    realm: Realm,\n}\n\n/// Marker for an ordinary object.\npub(crate) struct OrdinaryObject;\n\n/// Applies the pending builder data to the object.\npub(crate) trait ApplyToObject {\n    fn apply_to(self, object: &JsObject);\n}\n\nimpl ApplyToObject for Constructor {\n    fn apply_to(self, object: &JsObject) {\n        object.insert(\n            PROTOTYPE,\n            PropertyDescriptor::builder()\n                .value(self.prototype.clone())\n                .writable(false)\n                .enumerable(false)\n                .configurable(false),\n        );\n\n        {\n            let mut prototype = self.prototype.borrow_mut();\n            prototype.set_prototype(self.inherits);\n            prototype.insert(\n                CONSTRUCTOR,\n                PropertyDescriptor::builder()\n                    .value(object.clone())\n                    .writable(self.attributes.writable())\n                    .enumerable(self.attributes.enumerable())\n                    .configurable(self.attributes.configurable()),\n            );\n        }\n    }\n}\n\nimpl ApplyToObject for ConstructorNoProto {\n    fn apply_to(self, _: &JsObject) {}\n}\n\nimpl ApplyToObject for OrdinaryFunction {\n    fn apply_to(self, _: &JsObject) {}\n}\n\nimpl<S: ApplyToObject + IsConstructor> ApplyToObject for Callable<S> {\n    fn apply_to(self, object: &JsObject) {\n        {\n            let mut function = object\n                .downcast_mut::<NativeFunctionObject>()\n                .expect(\"Builtin must be a function object\");\n            function.f = NativeFunction::from_fn_ptr(self.function);\n            function.constructor = S::IS_CONSTRUCTOR.then_some(ConstructorKind::Base);\n            function.realm = Some(self.realm);\n        }\n        object.insert(\n            StaticJsStrings::LENGTH,\n            PropertyDescriptor::builder()\n                .value(self.length)\n                .writable(false)\n                .enumerable(false)\n                .configurable(true),\n        );\n        object.insert(\n            js_string!(\"name\"),\n            PropertyDescriptor::builder()\n                .value(self.name)\n                .writable(false)\n                .enumerable(false)\n                .configurable(true),\n        );\n\n        self.kind.apply_to(object);\n    }\n}\n\nimpl ApplyToObject for OrdinaryObject {\n    fn apply_to(self, _: &JsObject) {}\n}\n\n/// Builder for creating built-in objects, like `Array`.\n///\n/// The marker `ObjectType` restricts the methods that can be called depending on the\n/// type of object that is being constructed.\n#[derive(Debug)]\n#[must_use = \"You need to call the `build` method in order for this to correctly assign the inner data\"]\npub(crate) struct BuiltInBuilder<'ctx, Kind> {\n    realm: &'ctx Realm,\n    object: JsObject,\n    kind: Kind,\n    prototype: JsObject,\n}\n\nimpl<'ctx> BuiltInBuilder<'ctx, OrdinaryObject> {\n    pub(crate) fn with_intrinsic<I: IntrinsicObject>(\n        realm: &'ctx Realm,\n    ) -> BuiltInBuilder<'ctx, OrdinaryObject> {\n        BuiltInBuilder {\n            realm,\n            object: I::get(realm.intrinsics()),\n            kind: OrdinaryObject,\n            prototype: realm.intrinsics().constructors().object().prototype(),\n        }\n    }\n}\n\npub(crate) struct BuiltInConstructorWithPrototype<'ctx> {\n    realm: &'ctx Realm,\n    function: NativeFunctionPointer,\n    name: JsString,\n    length: usize,\n\n    constructor_property_table: PropertyTableInner,\n    constructor_storage: Vec<JsValue>,\n    constructor: JsObject,\n    #[cfg(debug_assertions)]\n    constructor_storage_slots_expected: usize,\n\n    prototype_property_table: PropertyTableInner,\n    prototype_storage: Vec<JsValue>,\n    prototype: JsObject,\n    #[cfg(debug_assertions)]\n    prototype_storage_slots_expected: usize,\n    __proto__: JsPrototype,\n    inherits: Option<JsObject>,\n    attributes: Attribute,\n    /// If `Some`, the `constructor` property on the prototype will be installed\n    /// as a get/set accessor pair instead of as a writable data property.\n    /// This is needed by `Iterator.prototype.constructor` per the spec\n    /// (web-compat requirement).\n    constructor_accessor: Option<(JsFunction, JsFunction)>,\n}\n\n#[allow(dead_code)]\nimpl BuiltInConstructorWithPrototype<'_> {\n    /// The number of storage slots that are always present in a standard\n    /// constructor object.\n    ///\n    /// See [`BuiltInConstructorWithPrototype::build`].\n    const OWN_CONSTRUCTOR_STORAGE_SLOTS: usize = 3;\n\n    /// The number of storage slots properties that are always present in a\n    /// standard constructor's prototype object when `constructor` is a **data** property.\n    ///\n    /// See [`BuiltInConstructorWithPrototype::build`].\n    const OWN_PROTOTYPE_STORAGE_SLOTS: usize = 1;\n\n    /// The number of storage slots properties that are always present in a\n    /// standard constructor's prototype object when `constructor` is installed\n    /// as a get/set **accessor** property (two slots: getter + setter).\n    ///\n    /// See [`BuiltInConstructorWithPrototype::constructor_accessor`].\n    const OWN_PROTOTYPE_STORAGE_SLOTS_ACCESSOR: usize = 2;\n\n    /// Specify how many arguments the constructor function takes.\n    ///\n    /// Default is `0`.\n    #[inline]\n    pub(crate) const fn length(mut self, length: usize) -> Self {\n        self.length = length;\n        self\n    }\n\n    /// Specify the name of the constructor function.\n    ///\n    /// Default is `\"\"`\n    pub(crate) fn name(mut self, name: JsString) -> Self {\n        self.name = name;\n        self\n    }\n\n    /// Adds a new static method to the builtin object.\n    pub(crate) fn static_method<B>(\n        mut self,\n        function: NativeFunctionPointer,\n        binding: B,\n        length: usize,\n    ) -> Self\n    where\n        B: Into<FunctionBinding>,\n    {\n        let binding = binding.into();\n        let function = BuiltInBuilder::callable(self.realm, function)\n            .name(binding.name)\n            .length(length)\n            .build();\n\n        debug_assert!(\n            !self\n                .constructor_property_table\n                .map\n                .contains_key(&binding.binding)\n        );\n        self.constructor_property_table.insert(\n            binding.binding,\n            SlotAttributes::WRITABLE | SlotAttributes::CONFIGURABLE,\n        );\n        self.constructor_storage.push(function.into());\n        self\n    }\n\n    /// Adds a new static data property to the builtin object.\n    pub(crate) fn static_property<K, V>(mut self, key: K, value: V, attribute: Attribute) -> Self\n    where\n        K: Into<PropertyKey>,\n        V: Into<JsValue>,\n    {\n        let key = key.into();\n\n        debug_assert!(!self.constructor_property_table.map.contains_key(&key));\n        self.constructor_property_table\n            .insert(key, SlotAttributes::from_bits_truncate(attribute.bits()));\n        self.constructor_storage.push(value.into());\n        self\n    }\n\n    /// Adds a new static accessor property to the builtin object.\n    pub(crate) fn static_accessor<K>(\n        mut self,\n        key: K,\n        get: Option<JsFunction>,\n        set: Option<JsFunction>,\n        attribute: Attribute,\n    ) -> Self\n    where\n        K: Into<PropertyKey>,\n    {\n        let mut attributes = SlotAttributes::from_bits_truncate(attribute.bits());\n        debug_assert!(!attributes.contains(SlotAttributes::WRITABLE));\n        attributes.set(SlotAttributes::GET, get.is_some());\n        attributes.set(SlotAttributes::SET, set.is_some());\n\n        let key = key.into();\n\n        debug_assert!(!self.constructor_property_table.map.contains_key(&key));\n        self.constructor_property_table.insert(key, attributes);\n        self.constructor_storage.extend([\n            get.map(JsValue::new).unwrap_or_default(),\n            set.map(JsValue::new).unwrap_or_default(),\n        ]);\n        self\n    }\n\n    /// Specify the `[[Prototype]]` internal field of the builtin object.\n    ///\n    /// Default is `Function.prototype` for constructors and `Object.prototype` for statics.\n    pub(crate) fn prototype(mut self, prototype: JsObject) -> Self {\n        self.__proto__ = Some(prototype);\n        self\n    }\n\n    /// Adds a new method to the constructor's prototype.\n    pub(crate) fn method<B>(\n        mut self,\n        function: NativeFunctionPointer,\n        binding: B,\n        length: usize,\n    ) -> Self\n    where\n        B: Into<FunctionBinding>,\n    {\n        let binding = binding.into();\n        let function = BuiltInBuilder::callable(self.realm, function)\n            .name(binding.name)\n            .length(length)\n            .build();\n\n        debug_assert!(\n            !self\n                .prototype_property_table\n                .map\n                .contains_key(&binding.binding)\n        );\n        self.prototype_property_table.insert(\n            binding.binding,\n            SlotAttributes::WRITABLE | SlotAttributes::CONFIGURABLE,\n        );\n        self.prototype_storage.push(function.into());\n        self\n    }\n\n    /// Adds a new data property to the constructor's prototype.\n    pub(crate) fn property<K, V>(mut self, key: K, value: V, attribute: Attribute) -> Self\n    where\n        K: Into<PropertyKey>,\n        V: Into<JsValue>,\n    {\n        let key = key.into();\n\n        debug_assert!(!self.prototype_property_table.map.contains_key(&key));\n        self.prototype_property_table\n            .insert(key, SlotAttributes::from_bits_truncate(attribute.bits()));\n        self.prototype_storage.push(value.into());\n        self\n    }\n\n    /// Adds new accessor property to the constructor's prototype.\n    pub(crate) fn accessor<K>(\n        mut self,\n        key: K,\n        get: Option<JsFunction>,\n        set: Option<JsFunction>,\n        attribute: Attribute,\n    ) -> Self\n    where\n        K: Into<PropertyKey>,\n    {\n        let mut attributes = SlotAttributes::from_bits_truncate(attribute.bits());\n        debug_assert!(!attributes.contains(SlotAttributes::WRITABLE));\n        attributes.set(SlotAttributes::GET, get.is_some());\n        attributes.set(SlotAttributes::SET, set.is_some());\n\n        let key = key.into();\n\n        debug_assert!(!self.prototype_property_table.map.contains_key(&key));\n        self.prototype_property_table.insert(key, attributes);\n        self.prototype_storage.extend([\n            get.map(JsValue::new).unwrap_or_default(),\n            set.map(JsValue::new).unwrap_or_default(),\n        ]);\n        self\n    }\n\n    /// Specifies the parent prototype which objects created by this constructor inherit from.\n    ///\n    /// Default is `Object.prototype`.\n    #[allow(clippy::missing_const_for_fn)]\n    pub(crate) fn inherits(mut self, prototype: JsPrototype) -> Self {\n        self.inherits = prototype;\n        self\n    }\n\n    /// Specifies the property attributes of the prototype's \"constructor\" property.\n    pub(crate) const fn constructor_attributes(mut self, attributes: Attribute) -> Self {\n        self.attributes = attributes;\n        self\n    }\n\n    /// Installs `Iterator.prototype.constructor` (or any analogous property) as a\n    /// configurable, non-enumerable **accessor** descriptor instead of the default\n    /// writable data property.\n    ///\n    /// Per the [ECMAScript spec][spec], `Iterator.prototype.constructor` must be an\n    /// accessor for web-compatibility reasons: previously the property did not exist, so\n    /// making it a setter that ignores writes to the prototype avoids breaking existing code\n    /// that assigns `Iterator.prototype.constructor = ...`.\n    ///\n    /// When this method is called the `PROTOTYPE_STORAGE_SLOTS` constant on the\n    /// implementing built-in **must** account for two extra slots (getter + setter)\n    /// instead of the usual one slot for a data property.\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-iterator.prototype.constructor\n    pub(crate) fn constructor_accessor(mut self, get: JsFunction, set: JsFunction) -> Self {\n        self.constructor_accessor = Some((get, set));\n        self\n    }\n\n    #[track_caller]\n    pub(crate) fn build(mut self) {\n        let length = self.length;\n        let name = self.name.clone();\n        let prototype = self.prototype.clone();\n        self = self.static_property(js_string!(\"length\"), length, Attribute::CONFIGURABLE);\n        self = self.static_property(js_string!(\"name\"), name, Attribute::CONFIGURABLE);\n        self = self.static_property(PROTOTYPE, prototype, Attribute::empty());\n\n        // Install the `constructor` property on the prototype — either as a writable\n        // data property (the common case) or as a get/set accessor (needed by\n        // `Iterator.prototype.constructor` for web-compat, see `constructor_accessor`).\n        if let Some((get, set)) = self.constructor_accessor.take() {\n            // Accessor path: two storage slots (getter + setter).  The `CONSTRUCTOR` key\n            // must NOT already be present (no duplicate insertion).\n            let mut attributes = SlotAttributes::CONFIGURABLE;\n            attributes.set(SlotAttributes::GET, true);\n            attributes.set(SlotAttributes::SET, true);\n            debug_assert!(\n                !self\n                    .prototype_property_table\n                    .map\n                    .contains_key(&CONSTRUCTOR.into())\n            );\n            self.prototype_property_table\n                .insert(CONSTRUCTOR.into(), attributes);\n            self.prototype_storage\n                .extend([JsValue::new(get), JsValue::new(set)]);\n            #[cfg(debug_assertions)]\n            assert!(\n                self.prototype_storage.len()\n                    <= self.prototype_storage_slots_expected\n                        + Self::OWN_PROTOTYPE_STORAGE_SLOTS_ACCESSOR,\n                \"expected to allocate at most {} prototype storage slots, got {}. \\\n                constant {}::PROTOTYPE_STORAGE_SLOTS may need to be adjusted\",\n                self.prototype_storage_slots_expected,\n                self.prototype_storage.len() - Self::OWN_PROTOTYPE_STORAGE_SLOTS_ACCESSOR,\n                self.name.display_escaped(),\n            );\n        } else {\n            // Data property path (the default).\n            let attributes = self.attributes;\n            let object = self.constructor.clone();\n            self = self.property(CONSTRUCTOR, object, attributes);\n            #[cfg(debug_assertions)]\n            assert!(\n                self.prototype_storage.len()\n                    <= self.prototype_storage_slots_expected + Self::OWN_PROTOTYPE_STORAGE_SLOTS,\n                \"expected to allocate at most {} prototype storage slots, got {}. \\\n                constant {}::PROTOTYPE_STORAGE_SLOTS may need to be adjusted\",\n                self.prototype_storage_slots_expected,\n                self.prototype_storage.len() - Self::OWN_PROTOTYPE_STORAGE_SLOTS,\n                self.name.display_escaped(),\n            );\n        }\n\n        #[cfg(debug_assertions)]\n        assert!(\n            self.constructor_storage.len()\n                <= self.constructor_storage_slots_expected + Self::OWN_CONSTRUCTOR_STORAGE_SLOTS,\n            \"expected to allocate at most {} constructor storage slots, got {}. \\\n            constant {}::CONSTRUCTOR_STORAGE_SLOTS may need to be adjusted\",\n            self.constructor_storage_slots_expected,\n            self.constructor_storage.len() - Self::OWN_CONSTRUCTOR_STORAGE_SLOTS,\n            self.name.display_escaped(),\n        );\n\n        {\n            let mut prototype = self.prototype.borrow_mut();\n            prototype\n                .properties_mut()\n                .shape\n                .as_unique()\n                .expect(\"The object should have a unique shape\")\n                .override_internal(self.prototype_property_table, self.inherits);\n\n            let prototype_old_storage = std::mem::replace(\n                &mut prototype.properties_mut().storage,\n                self.prototype_storage,\n            );\n\n            debug_assert_eq!(prototype_old_storage.len(), 0);\n        }\n\n        {\n            let mut function = self\n                .constructor\n                .downcast_mut::<NativeFunctionObject>()\n                .expect(\"Builtin must be a function object\");\n            function.f = NativeFunction::from_fn_ptr(self.function);\n            function.constructor = Some(ConstructorKind::Base);\n            function.realm = Some(self.realm.clone());\n        }\n\n        let mut object = self.constructor.borrow_mut();\n        object\n            .properties_mut()\n            .shape\n            .as_unique()\n            .expect(\"The object should have a unique shape\")\n            .override_internal(self.constructor_property_table, self.__proto__);\n\n        let object_old_storage = std::mem::replace(\n            &mut object.properties_mut().storage,\n            self.constructor_storage,\n        );\n\n        debug_assert_eq!(object_old_storage.len(), 0);\n    }\n\n    pub(crate) fn build_without_prototype(mut self) {\n        let length = self.length;\n        let name = self.name.clone();\n        self = self.static_property(js_string!(\"length\"), length, Attribute::CONFIGURABLE);\n        self = self.static_property(js_string!(\"name\"), name, Attribute::CONFIGURABLE);\n\n        {\n            let mut function = self\n                .constructor\n                .downcast_mut::<NativeFunctionObject>()\n                .expect(\"Builtin must be a function object\");\n            function.f = NativeFunction::from_fn_ptr(self.function);\n            function.constructor = Some(ConstructorKind::Base);\n            function.realm = Some(self.realm.clone());\n        }\n\n        let mut object = self.constructor.borrow_mut();\n        object\n            .properties_mut()\n            .shape\n            .as_unique()\n            .expect(\"The object should have a unique shape\")\n            .override_internal(self.constructor_property_table, self.__proto__);\n\n        let object_old_storage = std::mem::replace(\n            &mut object.properties_mut().storage,\n            self.constructor_storage,\n        );\n\n        debug_assert_eq!(object_old_storage.len(), 0);\n    }\n}\n\npub(crate) struct BuiltInCallable<'ctx> {\n    realm: &'ctx Realm,\n    function: NativeFunctionPointer,\n    name: JsString,\n    length: usize,\n}\n\nimpl BuiltInCallable<'_> {\n    /// Specify how many arguments the constructor function takes.\n    ///\n    /// Default is `0`.\n    #[inline]\n    pub(crate) const fn length(mut self, length: usize) -> Self {\n        self.length = length;\n        self\n    }\n\n    /// Specify the name of the constructor function.\n    ///\n    /// Default is `\"\"`\n    pub(crate) fn name(mut self, name: JsString) -> Self {\n        self.name = name;\n        self\n    }\n\n    pub(crate) fn build(self) -> JsFunction {\n        let object = self.realm.intrinsics().templates().function().create(\n            NativeFunctionObject {\n                f: NativeFunction::from_fn_ptr(self.function),\n                name: self.name.clone(),\n                constructor: None,\n                realm: Some(self.realm.clone()),\n            },\n            vec![JsValue::new(self.length), JsValue::new(self.name)],\n        );\n\n        JsFunction::from_object_unchecked(object)\n    }\n}\n\nimpl<'ctx> BuiltInBuilder<'ctx, OrdinaryObject> {\n    pub(crate) fn callable(\n        realm: &'ctx Realm,\n        function: NativeFunctionPointer,\n    ) -> BuiltInCallable<'ctx> {\n        BuiltInCallable {\n            realm,\n            function,\n            length: 0,\n            name: js_string!(),\n        }\n    }\n\n    pub(crate) fn callable_with_intrinsic<I: IntrinsicObject>(\n        realm: &'ctx Realm,\n        function: NativeFunctionPointer,\n    ) -> BuiltInBuilder<'ctx, Callable<OrdinaryFunction>> {\n        BuiltInBuilder {\n            realm,\n            object: I::get(realm.intrinsics()),\n            kind: Callable {\n                function,\n                name: js_string!(),\n                length: 0,\n                kind: OrdinaryFunction,\n                realm: realm.clone(),\n            },\n            prototype: realm.intrinsics().constructors().function().prototype(),\n        }\n    }\n\n    pub(crate) fn callable_with_object(\n        realm: &'ctx Realm,\n        object: JsObject,\n        function: NativeFunctionPointer,\n    ) -> BuiltInBuilder<'ctx, Callable<OrdinaryFunction>> {\n        BuiltInBuilder {\n            realm,\n            object,\n            kind: Callable {\n                function,\n                name: js_string!(),\n                length: 0,\n                kind: OrdinaryFunction,\n                realm: realm.clone(),\n            },\n            prototype: realm.intrinsics().constructors().function().prototype(),\n        }\n    }\n}\n\nimpl<'ctx> BuiltInBuilder<'ctx, Callable<Constructor>> {\n    /// Create a new builder for a constructor function.\n    ///\n    /// This sets the properties ahead of time for optimizations\n    /// (less reallocations).\n    pub(crate) fn from_standard_constructor<SC: BuiltInConstructor>(\n        realm: &'ctx Realm,\n    ) -> BuiltInConstructorWithPrototype<'ctx> {\n        let constructor = SC::STANDARD_CONSTRUCTOR(realm.intrinsics().constructors());\n        BuiltInConstructorWithPrototype {\n            realm,\n            function: SC::constructor,\n            name: js_string!(SC::NAME),\n            length: SC::CONSTRUCTOR_ARGUMENTS,\n            constructor_property_table: PropertyTableInner::with_capacity(\n                SC::CONSTRUCTOR_STORAGE_SLOTS\n                    + BuiltInConstructorWithPrototype::OWN_CONSTRUCTOR_STORAGE_SLOTS,\n            ),\n            constructor_storage: Vec::with_capacity(\n                SC::CONSTRUCTOR_STORAGE_SLOTS\n                    + BuiltInConstructorWithPrototype::OWN_CONSTRUCTOR_STORAGE_SLOTS,\n            ),\n            constructor: constructor.constructor(),\n            #[cfg(debug_assertions)]\n            constructor_storage_slots_expected: SC::CONSTRUCTOR_STORAGE_SLOTS,\n            prototype_property_table: PropertyTableInner::with_capacity(\n                SC::PROTOTYPE_STORAGE_SLOTS\n                    + BuiltInConstructorWithPrototype::OWN_PROTOTYPE_STORAGE_SLOTS,\n            ),\n            prototype_storage: Vec::with_capacity(\n                SC::PROTOTYPE_STORAGE_SLOTS\n                    + BuiltInConstructorWithPrototype::OWN_PROTOTYPE_STORAGE_SLOTS,\n            ),\n            prototype: constructor.prototype(),\n            #[cfg(debug_assertions)]\n            prototype_storage_slots_expected: SC::PROTOTYPE_STORAGE_SLOTS,\n            __proto__: Some(realm.intrinsics().constructors().function().prototype()),\n            inherits: Some(realm.intrinsics().constructors().object().prototype()),\n            attributes: Attribute::WRITABLE | Attribute::CONFIGURABLE | Attribute::NON_ENUMERABLE,\n            constructor_accessor: None,\n        }\n    }\n}\n\nimpl<T> BuiltInBuilder<'_, T> {\n    /// Adds a new static method to the builtin object.\n    pub(crate) fn static_method<B>(\n        self,\n        function: NativeFunctionPointer,\n        binding: B,\n        length: usize,\n    ) -> Self\n    where\n        B: Into<FunctionBinding>,\n    {\n        let binding = binding.into();\n        let function = BuiltInBuilder::callable(self.realm, function)\n            .name(binding.name)\n            .length(length)\n            .build();\n\n        self.object.insert(\n            binding.binding,\n            PropertyDescriptor::builder()\n                .value(function)\n                .writable(true)\n                .enumerable(false)\n                .configurable(true),\n        );\n        self\n    }\n\n    /// Adds a new static data property to the builtin object.\n    pub(crate) fn static_property<K, V>(self, key: K, value: V, attribute: Attribute) -> Self\n    where\n        K: Into<PropertyKey>,\n        V: Into<JsValue>,\n    {\n        let property = PropertyDescriptor::builder()\n            .value(value)\n            .writable(attribute.writable())\n            .enumerable(attribute.enumerable())\n            .configurable(attribute.configurable());\n        self.object.insert(key, property);\n        self\n    }\n\n    /// Specify the `[[Prototype]]` internal field of the builtin object.\n    ///\n    /// Default is `Function.prototype` for constructors and `Object.prototype` for statics.\n    pub(crate) fn prototype(mut self, prototype: JsObject) -> Self {\n        self.prototype = prototype;\n        self\n    }\n}\n\nimpl<FnTyp> BuiltInBuilder<'_, Callable<FnTyp>> {\n    /// Specify how many arguments the constructor function takes.\n    ///\n    /// Default is `0`.\n    #[inline]\n    pub(crate) const fn length(mut self, length: usize) -> Self {\n        self.kind.length = length;\n        self\n    }\n\n    /// Specify the name of the constructor function.\n    ///\n    /// Default is `\"\"`\n    pub(crate) fn name(mut self, name: JsString) -> Self {\n        self.kind.name = name;\n        self\n    }\n}\n\nimpl BuiltInBuilder<'_, OrdinaryObject> {\n    /// Build the builtin object.\n    pub(crate) fn build(self) -> JsObject {\n        self.kind.apply_to(&self.object);\n\n        self.object.set_prototype(Some(self.prototype));\n\n        self.object\n    }\n}\n\nimpl<FnTyp: ApplyToObject + IsConstructor> BuiltInBuilder<'_, Callable<FnTyp>> {\n    /// Build the builtin callable.\n    pub(crate) fn build(self) -> JsFunction {\n        self.kind.apply_to(&self.object);\n\n        self.object.set_prototype(Some(self.prototype));\n\n        JsFunction::from_object_unchecked(self.object)\n    }\n}\n"
  },
  {
    "path": "core/engine/src/builtins/dataview/mod.rs",
    "content": "//! Boa's implementation of ECMAScript's global `DataView` object.\n//!\n//! More information:\n//!  - [ECMAScript reference][spec]\n//!  - [MDN documentation][mdn]\n//!\n//! [spec]: https://tc39.es/ecma262/#sec-dataview-objects\n//! [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/DataView\n\nuse std::sync::atomic::Ordering;\n\nuse crate::{\n    Context, JsArgs, JsData, JsResult, JsString,\n    builtins::BuiltInObject,\n    context::intrinsics::{Intrinsics, StandardConstructor, StandardConstructors},\n    error::JsNativeError,\n    js_string,\n    object::{JsObject, internal_methods::get_prototype_from_constructor},\n    property::Attribute,\n    realm::Realm,\n    string::StaticJsStrings,\n    symbol::JsSymbol,\n    value::JsValue,\n};\nuse boa_gc::{Finalize, Trace};\nuse bytemuck::{bytes_of, bytes_of_mut};\n\nuse super::{\n    BuiltInBuilder, BuiltInConstructor, IntrinsicObject,\n    array_buffer::{\n        BufferObject,\n        utils::{BytesConstPtr, BytesMutPtr, memcpy},\n    },\n    typed_array::{self, TypedArrayElement},\n};\n\n/// The internal representation of a `DataView` object.\n#[derive(Debug, Clone, Trace, Finalize, JsData)]\npub struct DataView {\n    pub(crate) viewed_array_buffer: BufferObject,\n    pub(crate) byte_length: Option<u64>,\n    pub(crate) byte_offset: u64,\n}\n\nimpl DataView {\n    /// Abstract operation [`GetViewByteLength ( viewRecord )`][spec].\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-getviewbytelength\n    fn byte_length(&self, buf_byte_len: usize) -> u64 {\n        // 1. Assert: IsViewOutOfBounds(viewRecord) is false.\n        debug_assert!(!self.is_out_of_bounds(buf_byte_len));\n\n        // 2. Let view be viewRecord.[[Object]].\n        // 3. If view.[[ByteLength]] is not auto, return view.[[ByteLength]].\n        if let Some(byte_length) = self.byte_length {\n            return byte_length;\n        }\n\n        // 4. Assert: IsFixedLengthArrayBuffer(view.[[ViewedArrayBuffer]]) is false.\n\n        // 5. Let byteOffset be view.[[ByteOffset]].\n        // 6. Let byteLength be viewRecord.[[CachedBufferByteLength]].\n        // 7. Assert: byteLength is not detached.\n        // 8. Return byteLength - byteOffset.\n        buf_byte_len as u64 - self.byte_offset\n    }\n\n    /// Abstract operation [`IsViewOutOfBounds ( viewRecord )`][spec].\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-isviewoutofbounds\n    fn is_out_of_bounds(&self, buf_byte_len: usize) -> bool {\n        let buf_byte_len = buf_byte_len as u64;\n        // 1. Let view be viewRecord.[[Object]].\n        // 2. Let bufferByteLength be viewRecord.[[CachedBufferByteLength]].\n        // 3. Assert: IsDetachedBuffer(view.[[ViewedArrayBuffer]]) is true if and only if bufferByteLength is detached.\n        // 4. If bufferByteLength is detached, return true.\n        // handled by the caller\n\n        // 5. Let byteOffsetStart be view.[[ByteOffset]].\n\n        // 6. If view.[[ByteLength]] is auto, then\n        //     a. Let byteOffsetEnd be bufferByteLength.\n        // 7. Else,\n        //     a. Let byteOffsetEnd be byteOffsetStart + view.[[ByteLength]].\n        let byte_offset_end = self\n            .byte_length\n            .map_or(buf_byte_len, |byte_length| byte_length + self.byte_offset);\n\n        // 8. If byteOffsetStart > bufferByteLength or byteOffsetEnd > bufferByteLength, return true.\n        // 9. NOTE: 0-length DataViews are not considered out-of-bounds.\n        // 10. Return false.\n        self.byte_offset > buf_byte_len || byte_offset_end > buf_byte_len\n    }\n}\n\nimpl IntrinsicObject for DataView {\n    fn init(realm: &Realm) {\n        let flag_attributes = Attribute::CONFIGURABLE | Attribute::NON_ENUMERABLE;\n\n        let get_buffer = BuiltInBuilder::callable(realm, Self::get_buffer)\n            .name(js_string!(\"get buffer\"))\n            .build();\n\n        let get_byte_length = BuiltInBuilder::callable(realm, Self::get_byte_length)\n            .name(js_string!(\"get byteLength\"))\n            .build();\n\n        let get_byte_offset = BuiltInBuilder::callable(realm, Self::get_byte_offset)\n            .name(js_string!(\"get byteOffset\"))\n            .build();\n\n        let builder = BuiltInBuilder::from_standard_constructor::<Self>(realm)\n            .accessor(\n                js_string!(\"buffer\"),\n                Some(get_buffer),\n                None,\n                flag_attributes,\n            )\n            .accessor(\n                js_string!(\"byteLength\"),\n                Some(get_byte_length),\n                None,\n                flag_attributes,\n            )\n            .accessor(\n                js_string!(\"byteOffset\"),\n                Some(get_byte_offset),\n                None,\n                flag_attributes,\n            )\n            .method(Self::get_big_int64, js_string!(\"getBigInt64\"), 1)\n            .method(Self::get_big_uint64, js_string!(\"getBigUint64\"), 1)\n            .method(Self::get_float32, js_string!(\"getFloat32\"), 1)\n            .method(Self::get_float64, js_string!(\"getFloat64\"), 1)\n            .method(Self::get_int8, js_string!(\"getInt8\"), 1)\n            .method(Self::get_int16, js_string!(\"getInt16\"), 1)\n            .method(Self::get_int32, js_string!(\"getInt32\"), 1)\n            .method(Self::get_uint8, js_string!(\"getUint8\"), 1)\n            .method(Self::get_uint16, js_string!(\"getUint16\"), 1)\n            .method(Self::get_uint32, js_string!(\"getUint32\"), 1)\n            .method(Self::set_big_int64, js_string!(\"setBigInt64\"), 2)\n            .method(Self::set_big_uint64, js_string!(\"setBigUint64\"), 2)\n            .method(Self::set_float32, js_string!(\"setFloat32\"), 2)\n            .method(Self::set_float64, js_string!(\"setFloat64\"), 2)\n            .method(Self::set_int8, js_string!(\"setInt8\"), 2)\n            .method(Self::set_int16, js_string!(\"setInt16\"), 2)\n            .method(Self::set_int32, js_string!(\"setInt32\"), 2)\n            .method(Self::set_uint8, js_string!(\"setUint8\"), 2)\n            .method(Self::set_uint16, js_string!(\"setUint16\"), 2)\n            .method(Self::set_uint32, js_string!(\"setUint32\"), 2)\n            .property(\n                JsSymbol::to_string_tag(),\n                Self::NAME,\n                Attribute::READONLY | Attribute::NON_ENUMERABLE | Attribute::CONFIGURABLE,\n            );\n\n        #[cfg(feature = \"float16\")]\n        let builder = builder\n            .method(Self::get_float16, js_string!(\"getFloat16\"), 1)\n            .method(Self::set_float16, js_string!(\"setFloat16\"), 2);\n\n        builder.build();\n    }\n\n    fn get(intrinsics: &Intrinsics) -> JsObject {\n        Self::STANDARD_CONSTRUCTOR(intrinsics.constructors()).constructor()\n    }\n}\n\nimpl BuiltInObject for DataView {\n    const NAME: JsString = StaticJsStrings::DATA_VIEW;\n}\n\nimpl BuiltInConstructor for DataView {\n    const CONSTRUCTOR_ARGUMENTS: usize = 1;\n    const PROTOTYPE_STORAGE_SLOTS: usize = 29;\n    const CONSTRUCTOR_STORAGE_SLOTS: usize = 0;\n\n    const STANDARD_CONSTRUCTOR: fn(&StandardConstructors) -> &StandardConstructor =\n        StandardConstructors::data_view;\n\n    /// `DataView ( buffer [ , byteOffset [ , byteLength ] ] )`\n    ///\n    /// The `DataView` view provides a low-level interface for reading and writing multiple number\n    /// types in a binary `ArrayBuffer`, without having to care about the platform's endianness.\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///  - [MDN][mdn]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-dataview-buffer-byteoffset-bytelength\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/DataView/DataView\n    fn constructor(\n        new_target: &JsValue,\n        args: &[JsValue],\n        context: &mut Context,\n    ) -> JsResult<JsValue> {\n        // 1. If NewTarget is undefined, throw a TypeError exception.\n        if new_target.is_undefined() {\n            return Err(JsNativeError::typ()\n                .with_message(\"cannot call `DataView` constructor without `new`\")\n                .into());\n        }\n        let byte_len = args.get_or_undefined(2);\n\n        // 2. Perform ? RequireInternalSlot(buffer, [[ArrayBufferData]]).\n        let buffer = args\n            .get_or_undefined(0)\n            .as_object()\n            .and_then(|o| o.clone().into_buffer_object().ok())\n            .ok_or_else(|| JsNativeError::typ().with_message(\"buffer must be an ArrayBuffer\"))?;\n\n        // 3. Let offset be ? ToIndex(byteOffset).\n        let offset = args.get_or_undefined(1).to_index(context)?;\n\n        let (buf_byte_len, is_fixed_len) = {\n            let buffer = buffer.as_buffer();\n\n            // 4. If IsDetachedBuffer(buffer) is true, throw a TypeError exception.\n            let Some(slice) = buffer.bytes(Ordering::SeqCst) else {\n                return Err(JsNativeError::typ()\n                    .with_message(\"ArrayBuffer is detached\")\n                    .into());\n            };\n\n            // 5. Let bufferByteLength be ArrayBufferByteLength(buffer, seq-cst).\n            let buf_len = slice.len() as u64;\n\n            // 6. If offset > bufferByteLength, throw a RangeError exception.\n            if offset > buf_len {\n                return Err(JsNativeError::range()\n                    .with_message(\"Start offset is outside the bounds of the buffer\")\n                    .into());\n            }\n\n            // 7. Let bufferIsFixedLength be IsFixedLengthArrayBuffer(buffer).\n\n            (buf_len, buffer.is_fixed_len())\n        };\n\n        // 8. If byteLength is undefined, then\n        let view_byte_len = if byte_len.is_undefined() {\n            // a. If bufferIsFixedLength is true, then\n            //     i. Let viewByteLength be bufferByteLength - offset.\n            // b. Else,\n            //     i. Let viewByteLength be auto.\n            is_fixed_len.then_some(buf_byte_len - offset)\n        } else {\n            // 9. Else,\n            //     a. Let viewByteLength be ? ToIndex(byteLength).\n            let byte_len = byte_len.to_index(context)?;\n\n            //     b. If offset + viewByteLength > bufferByteLength, throw a RangeError exception.\n            if offset + byte_len > buf_byte_len {\n                return Err(JsNativeError::range()\n                    .with_message(\"Invalid data view length\")\n                    .into());\n            }\n            Some(byte_len)\n        };\n\n        // 10. Let O be ? OrdinaryCreateFromConstructor(NewTarget, \"%DataView.prototype%\",\n        //     « [[DataView]], [[ViewedArrayBuffer]], [[ByteLength]], [[ByteOffset]] »).\n        let prototype =\n            get_prototype_from_constructor(new_target, StandardConstructors::data_view, context)?;\n\n        // 11. If IsDetachedBuffer(buffer) is true, throw a TypeError exception.\n        // 12. Set bufferByteLength to ArrayBufferByteLength(buffer, seq-cst).\n        let Some(buf_byte_len) = buffer\n            .as_buffer()\n            .bytes(Ordering::SeqCst)\n            .map(|s| s.len() as u64)\n        else {\n            return Err(JsNativeError::typ()\n                .with_message(\"ArrayBuffer can't be detached\")\n                .into());\n        };\n\n        // 13. If offset > bufferByteLength, throw a RangeError exception.\n        if offset > buf_byte_len {\n            return Err(JsNativeError::range()\n                .with_message(\"DataView offset outside of buffer array bounds\")\n                .into());\n        }\n\n        // 14. If byteLength is not undefined, then\n        //     a. If offset + viewByteLength > bufferByteLength, throw a RangeError exception.\n        if !byte_len.is_undefined()\n            && let Some(view_byte_len) = view_byte_len\n            && offset + view_byte_len > buf_byte_len\n        {\n            return Err(JsNativeError::range()\n                .with_message(\"DataView offset outside of buffer array bounds\")\n                .into());\n        }\n\n        let obj = JsObject::from_proto_and_data_with_shared_shape(\n            context.root_shape(),\n            prototype,\n            Self {\n                // 15. Set O.[[ViewedArrayBuffer]] to buffer.\n                viewed_array_buffer: buffer,\n                // 16. Set O.[[ByteLength]] to viewByteLength.\n                byte_length: view_byte_len,\n                // 17. Set O.[[ByteOffset]] to offset.\n                byte_offset: offset,\n            },\n        );\n\n        // 18. Return O.\n        Ok(obj.into())\n    }\n}\n\nimpl DataView {\n    /// `get DataView.prototype.buffer`\n    ///\n    /// The buffer accessor property represents the `ArrayBuffer` or `SharedArrayBuffer` referenced\n    /// by the `DataView` at construction time.\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///  - [MDN][mdn]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-get-dataview.prototype.buffer\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/DataView/buffer\n    pub(crate) fn get_buffer(\n        this: &JsValue,\n        _args: &[JsValue],\n        _: &mut Context,\n    ) -> JsResult<JsValue> {\n        // 1. Let O be the this value.\n        // 2. Perform ? RequireInternalSlot(O, [[DataView]]).\n        let object = this.as_object();\n        let view = object\n            .as_ref()\n            .and_then(JsObject::downcast_ref::<Self>)\n            .ok_or_else(|| JsNativeError::typ().with_message(\"`this` is not a DataView\"))?;\n        // 3. Assert: O has a [[ViewedArrayBuffer]] internal slot.\n        // 4. Let buffer be O.[[ViewedArrayBuffer]].\n        let buffer = view.viewed_array_buffer.clone();\n        // 5. Return buffer.\n        Ok(buffer.into())\n    }\n\n    /// `get DataView.prototype.byteLength`\n    ///\n    /// The `byteLength` accessor property represents the length (in bytes) of the dataview.\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///  - [MDN][mdn]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-get-dataview.prototype.bytelength\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/DataView/byteLength\n    pub(crate) fn get_byte_length(\n        this: &JsValue,\n        _args: &[JsValue],\n        _: &mut Context,\n    ) -> JsResult<JsValue> {\n        // 1. Let O be the this value.\n        // 2. Perform ? RequireInternalSlot(O, [[DataView]]).\n        // 3. Assert: O has a [[ViewedArrayBuffer]] internal slot.\n        let object = this.as_object();\n        let view = object\n            .as_ref()\n            .and_then(JsObject::downcast_ref::<Self>)\n            .ok_or_else(|| JsNativeError::typ().with_message(\"`this` is not a DataView\"))?;\n\n        // 4. Let viewRecord be MakeDataViewWithBufferWitnessRecord(O, seq-cst).\n        // 5. If IsViewOutOfBounds(viewRecord) is true, throw a TypeError exception.\n        let buffer = view.viewed_array_buffer.as_buffer();\n        let Some(slice) = buffer\n            .bytes(Ordering::SeqCst)\n            .filter(|s| !view.is_out_of_bounds(s.len()))\n        else {\n            return Err(JsNativeError::typ()\n                .with_message(\"view out of bounds for its inner buffer\")\n                .into());\n        };\n\n        // 6. Let size be GetViewByteLength(viewRecord).\n        let size = view.byte_length(slice.len());\n\n        // 7. Return 𝔽(size).\n        Ok(size.into())\n    }\n\n    /// `get DataView.prototype.byteOffset`\n    ///\n    /// The `byteOffset` accessor property represents the offset (in bytes) of this view from the\n    /// start of its `ArrayBuffer` or `SharedArrayBuffer`.\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///  - [MDN][mdn]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-get-dataview.prototype.byteoffset\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/DataView/byteOffset\n    pub(crate) fn get_byte_offset(\n        this: &JsValue,\n        _args: &[JsValue],\n        _: &mut Context,\n    ) -> JsResult<JsValue> {\n        // 1. Let O be the this value.\n        // 2. Perform ? RequireInternalSlot(O, [[DataView]]).\n        let object = this.as_object();\n        let view = object\n            .as_ref()\n            .and_then(JsObject::downcast_ref::<Self>)\n            .ok_or_else(|| JsNativeError::typ().with_message(\"`this` is not a DataView\"))?;\n\n        // 3. Assert: O has a [[ViewedArrayBuffer]] internal slot.\n        let buffer = view.viewed_array_buffer.as_buffer();\n        // 4. Let viewRecord be MakeDataViewWithBufferWitnessRecord(O, seq-cst).\n        // 5. If IsViewOutOfBounds(viewRecord) is true, throw a TypeError exception.\n        if buffer\n            .bytes(Ordering::SeqCst)\n            .filter(|b| !view.is_out_of_bounds(b.len()))\n            .is_none()\n        {\n            return Err(JsNativeError::typ()\n                .with_message(\"data view is outside the bounds of its inner buffer\")\n                .into());\n        }\n\n        // 6. Let offset be O.[[ByteOffset]].\n        let offset = view.byte_offset;\n        // 7. Return 𝔽(offset).\n        Ok(offset.into())\n    }\n\n    /// `GetViewValue ( view, requestIndex, isLittleEndian, type )`\n    ///\n    /// The abstract operation `GetViewValue` takes arguments view, requestIndex, `isLittleEndian`,\n    /// and type. It is used by functions on `DataView` instances to retrieve values from the\n    /// view's buffer.\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-getviewvalue\n    fn get_view_value<T: typed_array::Element>(\n        view: &JsValue,\n        request_index: &JsValue,\n        is_little_endian: &JsValue,\n        context: &mut Context,\n    ) -> JsResult<JsValue> {\n        // 1. Perform ? RequireInternalSlot(view, [[DataView]]).\n        // 2. Assert: view has a [[ViewedArrayBuffer]] internal slot.\n        let object = view.as_object();\n        let view = object\n            .as_ref()\n            .and_then(JsObject::downcast_ref::<Self>)\n            .ok_or_else(|| JsNativeError::typ().with_message(\"`this` is not a DataView\"))?;\n\n        // 3. Let getIndex be ? ToIndex(requestIndex).\n        let get_index = request_index.to_index(context)?;\n\n        // 4. Set isLittleEndian to ToBoolean(isLittleEndian).\n        let is_little_endian = is_little_endian.to_boolean();\n\n        // 6. Let viewRecord be MakeDataViewWithBufferWitnessRecord(view, unordered).\n        // 7. NOTE: Bounds checking is not a synchronizing operation when view's backing buffer is a growable SharedArrayBuffer.\n        // 8. If IsViewOutOfBounds(viewRecord) is true, throw a TypeError exception.\n        let buffer = view.viewed_array_buffer.as_buffer();\n        let Some(data) = buffer\n            .bytes(Ordering::Relaxed)\n            .filter(|buf| !view.is_out_of_bounds(buf.len()))\n        else {\n            return Err(JsNativeError::typ()\n                .with_message(\"view out of bounds for its inner buffer\")\n                .into());\n        };\n\n        // 5. Let viewOffset be view.[[ByteOffset]].\n        let view_offset = view.byte_offset;\n\n        // 9. Let viewSize be GetViewByteLength(viewRecord).\n        let view_size = view.byte_length(data.len());\n\n        // 10. Let elementSize be the Element Size value specified in Table 71 for Element Type type.\n        let element_size = size_of::<T>() as u64;\n\n        // 11. If getIndex + elementSize > viewSize, throw a RangeError exception.\n        if get_index + element_size > view_size {\n            return Err(JsNativeError::range()\n                .with_message(\"Offset is outside the bounds of the DataView\")\n                .into());\n        }\n\n        // 12. Let bufferIndex be getIndex + viewOffset.\n        let buffer_index = (get_index + view_offset) as usize;\n\n        let src = data.subslice(buffer_index..);\n\n        debug_assert!(src.len() >= size_of::<T>());\n\n        // 13. Return GetValueFromBuffer(view.[[ViewedArrayBuffer]], bufferIndex, type, false, unordered, isLittleEndian).\n        // SAFETY: All previous checks ensure the element fits in the buffer.\n        let value: TypedArrayElement = unsafe {\n            let mut value = T::zeroed();\n            memcpy(\n                src.as_ptr(),\n                BytesMutPtr::Bytes(bytes_of_mut(&mut value).as_mut_ptr()),\n                size_of::<T>(),\n            );\n\n            if is_little_endian {\n                value.to_little_endian()\n            } else {\n                value.to_big_endian()\n            }\n            .into()\n        };\n\n        Ok(value.into())\n    }\n\n    /// `DataView.prototype.getBigInt64 ( byteOffset [ , littleEndian ] )`\n    ///\n    /// The `getBigInt64()` method gets a signed 64-bit integer (long long) at the specified byte\n    /// offset from the start of the `DataView`.\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///  - [MDN][mdn]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-dataview.prototype.getbigint64\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/DataView/getBigInt64\n    pub(crate) fn get_big_int64(\n        this: &JsValue,\n        args: &[JsValue],\n        context: &mut Context,\n    ) -> JsResult<JsValue> {\n        let byte_offset = args.get_or_undefined(0);\n        let is_little_endian = args.get_or_undefined(1);\n        // 1. Let v be the this value.\n        // 2. Return ? GetViewValue(v, byteOffset, littleEndian, BigInt64).\n        Self::get_view_value::<i64>(this, byte_offset, is_little_endian, context)\n    }\n\n    /// `DataView.prototype.getBigUint64 ( byteOffset [ , littleEndian ] )`\n    ///\n    /// The `getBigUint64()` method gets an unsigned 64-bit integer (unsigned long long) at the\n    /// specified byte offset from the start of the `DataView`.\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///  - [MDN][mdn]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-dataview.prototype.getbiguint64\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/DataView/getBigUint64\n    pub(crate) fn get_big_uint64(\n        this: &JsValue,\n        args: &[JsValue],\n        context: &mut Context,\n    ) -> JsResult<JsValue> {\n        let byte_offset = args.get_or_undefined(0);\n        let is_little_endian = args.get_or_undefined(1);\n        // 1. Let v be the this value.\n        // 2. Return ? GetViewValue(v, byteOffset, littleEndian, BigInt64).\n        Self::get_view_value::<u64>(this, byte_offset, is_little_endian, context)\n    }\n\n    /// `DataView.prototype.getFloat16 ( byteOffset [ , littleEndian ] )`\n    ///\n    /// The `getFloat16()` method gets a signed 16-bit float (float) at the specified byte offset\n    /// from the start of the `DataView`.\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///  - [MDN][mdn]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-dataview.prototype.getfloat16\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/DataView/getFloat16\n    #[cfg(feature = \"float16\")]\n    pub(crate) fn get_float16(\n        this: &JsValue,\n        args: &[JsValue],\n        context: &mut Context,\n    ) -> JsResult<JsValue> {\n        let byte_offset = args.get_or_undefined(0);\n        let is_little_endian = args.get_or_undefined(1);\n        // 1. Let v be the this value.\n        // 2. Return ? GetViewValue(v, byteOffset, littleEndian, BigInt64).\n        Self::get_view_value::<typed_array::Float16>(this, byte_offset, is_little_endian, context)\n    }\n\n    /// `DataView.prototype.getFloat32 ( byteOffset [ , littleEndian ] )`\n    ///\n    /// The `getFloat32()` method gets a signed 32-bit float (float) at the specified byte offset\n    /// from the start of the `DataView`.\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///  - [MDN][mdn]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-dataview.prototype.getfloat32\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/DataView/getFloat32\n    pub(crate) fn get_float32(\n        this: &JsValue,\n        args: &[JsValue],\n        context: &mut Context,\n    ) -> JsResult<JsValue> {\n        let byte_offset = args.get_or_undefined(0);\n        let is_little_endian = args.get_or_undefined(1);\n        // 1. Let v be the this value.\n        // 2. Return ? GetViewValue(v, byteOffset, littleEndian, BigInt64).\n        Self::get_view_value::<f32>(this, byte_offset, is_little_endian, context)\n    }\n\n    /// `DataView.prototype.getFloat64 ( byteOffset [ , littleEndian ] )`\n    ///\n    /// The `getFloat64()` method gets a signed 64-bit float (double) at the specified byte offset\n    /// from the start of the `DataView`.\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///  - [MDN][mdn]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-dataview.prototype.getfloat64\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/DataView/getFloat64\n    pub(crate) fn get_float64(\n        this: &JsValue,\n        args: &[JsValue],\n        context: &mut Context,\n    ) -> JsResult<JsValue> {\n        let byte_offset = args.get_or_undefined(0);\n        let is_little_endian = args.get_or_undefined(1);\n        // 1. Let v be the this value.\n        // 2. Return ? GetViewValue(v, byteOffset, littleEndian, BigInt64).\n        Self::get_view_value::<f64>(this, byte_offset, is_little_endian, context)\n    }\n\n    /// `DataView.prototype.getInt8 ( byteOffset [ , littleEndian ] )`\n    ///\n    /// The `getInt8()` method gets a signed 8-bit integer (byte) at the specified byte offset\n    /// from the start of the `DataView`.\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///  - [MDN][mdn]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-dataview.prototype.getint8\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/DataView/getInt8\n    pub(crate) fn get_int8(\n        this: &JsValue,\n        args: &[JsValue],\n        context: &mut Context,\n    ) -> JsResult<JsValue> {\n        let byte_offset = args.get_or_undefined(0);\n        let is_little_endian = args.get_or_undefined(1);\n        // 1. Let v be the this value.\n        // 2. Return ? GetViewValue(v, byteOffset, littleEndian, BigInt64).\n        Self::get_view_value::<i8>(this, byte_offset, is_little_endian, context)\n    }\n\n    /// `DataView.prototype.getInt16 ( byteOffset [ , littleEndian ] )`\n    ///\n    /// The `getInt16()` method gets a signed 16-bit integer (short) at the specified byte offset\n    /// from the start of the `DataView`.\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///  - [MDN][mdn]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-dataview.prototype.getint16\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/DataView/getInt16\n    pub(crate) fn get_int16(\n        this: &JsValue,\n        args: &[JsValue],\n        context: &mut Context,\n    ) -> JsResult<JsValue> {\n        let byte_offset = args.get_or_undefined(0);\n        let is_little_endian = args.get_or_undefined(1);\n        // 1. Let v be the this value.\n        // 2. Return ? GetViewValue(v, byteOffset, littleEndian, BigInt64).\n        Self::get_view_value::<i16>(this, byte_offset, is_little_endian, context)\n    }\n\n    /// `DataView.prototype.getInt32 ( byteOffset [ , littleEndian ] )`\n    ///\n    /// The `getInt32()` method gets a signed 32-bit integer (long) at the specified byte offset\n    /// from the start of the `DataView`.\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///  - [MDN][mdn]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-dataview.prototype.getint32\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/DataView/getInt32\n    pub(crate) fn get_int32(\n        this: &JsValue,\n        args: &[JsValue],\n        context: &mut Context,\n    ) -> JsResult<JsValue> {\n        let byte_offset = args.get_or_undefined(0);\n        let is_little_endian = args.get_or_undefined(1);\n        // 1. Let v be the this value.\n        // 2. Return ? GetViewValue(v, byteOffset, littleEndian, BigInt64).\n        Self::get_view_value::<i32>(this, byte_offset, is_little_endian, context)\n    }\n\n    /// `DataView.prototype.getUint8 ( byteOffset [ , littleEndian ] )`\n    ///\n    /// The `getUint8()` method gets an unsigned 8-bit integer (unsigned byte) at the specified\n    /// byte offset from the start of the `DataView`.\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///  - [MDN][mdn]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-dataview.prototype.getuint8\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/DataView/getUint8\n    pub(crate) fn get_uint8(\n        this: &JsValue,\n        args: &[JsValue],\n        context: &mut Context,\n    ) -> JsResult<JsValue> {\n        let byte_offset = args.get_or_undefined(0);\n        let is_little_endian = args.get_or_undefined(1);\n        // 1. Let v be the this value.\n        // 2. Return ? GetViewValue(v, byteOffset, littleEndian, BigInt64).\n        Self::get_view_value::<u8>(this, byte_offset, is_little_endian, context)\n    }\n\n    /// `DataView.prototype.getUint16 ( byteOffset [ , littleEndian ] )`\n    ///\n    /// The `getUint16()` method gets an unsigned 16-bit integer (unsigned short) at the specified\n    /// byte offset from the start of the `DataView`.\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///  - [MDN][mdn]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-dataview.prototype.getuint16\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/DataView/getUint16\n    pub(crate) fn get_uint16(\n        this: &JsValue,\n        args: &[JsValue],\n        context: &mut Context,\n    ) -> JsResult<JsValue> {\n        let byte_offset = args.get_or_undefined(0);\n        let is_little_endian = args.get_or_undefined(1);\n        // 1. Let v be the this value.\n        // 2. Return ? GetViewValue(v, byteOffset, littleEndian, BigInt64).\n        Self::get_view_value::<u16>(this, byte_offset, is_little_endian, context)\n    }\n\n    /// `DataView.prototype.getUint32 ( byteOffset [ , littleEndian ] )`\n    ///\n    /// The `getUint32()` method gets an unsigned 32-bit integer (unsigned long) at the specified\n    /// byte offset from the start of the `DataView`.\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///  - [MDN][mdn]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-dataview.prototype.getuint32\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/DataView/getUint32\n    pub(crate) fn get_uint32(\n        this: &JsValue,\n        args: &[JsValue],\n        context: &mut Context,\n    ) -> JsResult<JsValue> {\n        let byte_offset = args.get_or_undefined(0);\n        let is_little_endian = args.get_or_undefined(1);\n        // 1. Let v be the this value.\n        // 2. Return ? GetViewValue(v, byteOffset, littleEndian, BigInt64).\n        Self::get_view_value::<u32>(this, byte_offset, is_little_endian, context)\n    }\n\n    /// `SetViewValue ( view, requestIndex, isLittleEndian, type )`\n    ///\n    /// The abstract operation `SetViewValue` takes arguments view, requestIndex, `isLittleEndian`,\n    /// type, and value. It is used by functions on `DataView` instances to store values into the\n    /// view's buffer.\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-setviewvalue\n    fn set_view_value<T: typed_array::Element>(\n        view: &JsValue,\n        request_index: &JsValue,\n        is_little_endian: &JsValue,\n        value: &JsValue,\n        context: &mut Context,\n    ) -> JsResult<JsValue> {\n        // 1. Perform ? RequireInternalSlot(view, [[DataView]]).\n        // 2. Assert: view has a [[ViewedArrayBuffer]] internal slot.\n        let object = view.as_object();\n        let view = object\n            .as_ref()\n            .and_then(JsObject::downcast_ref::<Self>)\n            .ok_or_else(|| JsNativeError::typ().with_message(\"`this` is not a DataView\"))?;\n\n        // 3. Let getIndex be ? ToIndex(requestIndex).\n        let get_index = request_index.to_index(context)?;\n\n        // 4. If IsBigIntElementType(type) is true, let numberValue be ? ToBigInt(value).\n        // 5. Otherwise, let numberValue be ? ToNumber(value).\n        let value = T::from_js_value(value, context)?;\n\n        // 6. Set isLittleEndian to ToBoolean(isLittleEndian).\n        let is_little_endian = is_little_endian.to_boolean();\n\n        // 8. Let viewRecord be MakeDataViewWithBufferWitnessRecord(view, unordered).\n        // 9. NOTE: Bounds checking is not a synchronizing operation when view's backing buffer is a growable SharedArrayBuffer.\n        // 10. If IsViewOutOfBounds(viewRecord) is true, throw a TypeError exception.\n        let mut buffer = view.viewed_array_buffer.as_buffer_mut();\n\n        let Some(mut data) = buffer\n            .bytes(Ordering::Relaxed)\n            .filter(|buf| !view.is_out_of_bounds(buf.len()))\n        else {\n            return Err(JsNativeError::typ()\n                .with_message(\"view out of bounds for its inner buffer\")\n                .into());\n        };\n\n        // 11. Let viewSize be GetViewByteLength(viewRecord).\n        let view_size = view.byte_length(data.len());\n\n        // 7. Let viewOffset be view.[[ByteOffset]].\n        let view_offset = view.byte_offset;\n\n        // 12. Let elementSize be the Element Size value specified in Table 71 for Element Type type.\n        let elem_size = size_of::<T>();\n\n        // 13. If getIndex + elementSize > viewSize, throw a RangeError exception.\n        if get_index + elem_size as u64 > view_size {\n            return Err(JsNativeError::range()\n                .with_message(\"Offset is outside the bounds of DataView\")\n                .into());\n        }\n\n        // 14. Let bufferIndex be getIndex + viewOffset.\n        let buffer_index = (get_index + view_offset) as usize;\n\n        let mut target = data.subslice_mut(buffer_index..);\n\n        debug_assert!(target.len() >= size_of::<T>());\n\n        // 15. Perform SetValueInBuffer(view.[[ViewedArrayBuffer]], bufferIndex, type, numberValue, false, unordered, isLittleEndian).\n        // SAFETY: All previous checks ensure the element fits in the buffer.\n        unsafe {\n            let value = if is_little_endian {\n                value.to_little_endian()\n            } else {\n                value.to_big_endian()\n            };\n\n            memcpy(\n                BytesConstPtr::Bytes(bytes_of(&value).as_ptr()),\n                target.as_ptr(),\n                size_of::<T>(),\n            );\n        }\n\n        // 16. Return undefined.\n        Ok(JsValue::undefined())\n    }\n\n    /// `DataView.prototype.setBigInt64 ( byteOffset, value [ , littleEndian ] )`\n    ///\n    /// The `setBigInt64()` method stores a signed 64-bit integer (long long) value at the\n    /// specified byte offset from the start of the `DataView`.\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///  - [MDN][mdn]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-dataview.prototype.setbigint64\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/DataView/setBigInt64\n    pub(crate) fn set_big_int64(\n        this: &JsValue,\n        args: &[JsValue],\n        context: &mut Context,\n    ) -> JsResult<JsValue> {\n        let byte_offset = args.get_or_undefined(0);\n        let value = args.get_or_undefined(1);\n        let is_little_endian = args.get_or_undefined(2);\n        // 1. Let v be the this value.\n        // 2. Return ? SetViewValue(v, byteOffset, littleEndian, BigUint64, value).\n        Self::set_view_value::<i64>(this, byte_offset, is_little_endian, value, context)\n    }\n\n    /// `DataView.prototype.setBigUint64 ( byteOffset, value [ , littleEndian ] )`\n    ///\n    /// The `setBigUint64()` method stores an unsigned 64-bit integer (unsigned long long) value at\n    /// the specified byte offset from the start of the `DataView`.\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///  - [MDN][mdn]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-dataview.prototype.setbiguint64\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/DataView/setBigUint64\n    pub(crate) fn set_big_uint64(\n        this: &JsValue,\n        args: &[JsValue],\n        context: &mut Context,\n    ) -> JsResult<JsValue> {\n        let byte_offset = args.get_or_undefined(0);\n        let value = args.get_or_undefined(1);\n        let is_little_endian = args.get_or_undefined(2);\n        // 1. Let v be the this value.\n        // 2. Return ? SetViewValue(v, byteOffset, littleEndian, BigUint64, value).\n        Self::set_view_value::<u64>(this, byte_offset, is_little_endian, value, context)\n    }\n\n    /// `DataView.prototype.setFloat16 ( byteOffset, value [ , littleEndian ] )`\n    ///\n    /// The `setFloat16()` method stores a signed 16-bit float (float) value at the specified byte\n    /// offset from the start of the `DataView`.\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///  - [MDN][mdn]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-dataview.prototype.setfloat16\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/DataView/setFloat16\n    #[cfg(feature = \"float16\")]\n    pub(crate) fn set_float16(\n        this: &JsValue,\n        args: &[JsValue],\n        context: &mut Context,\n    ) -> JsResult<JsValue> {\n        let byte_offset = args.get_or_undefined(0);\n        let value = args.get_or_undefined(1);\n        let is_little_endian = args.get_or_undefined(2);\n        // 1. Let v be the this value.\n        // 2. Return ? SetViewValue(v, byteOffset, littleEndian, Float32, value).\n        Self::set_view_value::<typed_array::Float16>(\n            this,\n            byte_offset,\n            is_little_endian,\n            value,\n            context,\n        )\n    }\n\n    /// `DataView.prototype.setFloat32 ( byteOffset, value [ , littleEndian ] )`\n    ///\n    /// The `setFloat32()` method stores a signed 32-bit float (float) value at the specified byte\n    /// offset from the start of the `DataView`.\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///  - [MDN][mdn]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-dataview.prototype.setfloat32\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/DataView/setFloat32\n    pub(crate) fn set_float32(\n        this: &JsValue,\n        args: &[JsValue],\n        context: &mut Context,\n    ) -> JsResult<JsValue> {\n        let byte_offset = args.get_or_undefined(0);\n        let value = args.get_or_undefined(1);\n        let is_little_endian = args.get_or_undefined(2);\n        // 1. Let v be the this value.\n        // 2. Return ? SetViewValue(v, byteOffset, littleEndian, Float32, value).\n        Self::set_view_value::<f32>(this, byte_offset, is_little_endian, value, context)\n    }\n\n    /// `DataView.prototype.setFloat64 ( byteOffset, value [ , littleEndian ] )`\n    ///\n    /// The `setFloat64()` method stores a signed 64-bit float (double) value at the specified byte\n    /// offset from the start of the `DataView`.\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///  - [MDN][mdn]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-dataview.prototype.setfloat64\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/DataView/setFloat64\n    pub(crate) fn set_float64(\n        this: &JsValue,\n        args: &[JsValue],\n        context: &mut Context,\n    ) -> JsResult<JsValue> {\n        let byte_offset = args.get_or_undefined(0);\n        let value = args.get_or_undefined(1);\n        let is_little_endian = args.get_or_undefined(2);\n        // 1. Let v be the this value.\n        // 2. Return ? SetViewValue(v, byteOffset, littleEndian, Float64, value).\n        Self::set_view_value::<f64>(this, byte_offset, is_little_endian, value, context)\n    }\n\n    /// `DataView.prototype.setInt8 ( byteOffset, value [ , littleEndian ] )`\n    ///\n    /// The `setInt8()` method stores a signed 8-bit integer (byte) value at the specified byte\n    /// offset from the start of the `DataView`.\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///  - [MDN][mdn]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-dataview.prototype.setint8\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/DataView/setInt8\n    pub(crate) fn set_int8(\n        this: &JsValue,\n        args: &[JsValue],\n        context: &mut Context,\n    ) -> JsResult<JsValue> {\n        let byte_offset = args.get_or_undefined(0);\n        let value = args.get_or_undefined(1);\n        let is_little_endian = args.get_or_undefined(2);\n        // 1. Let v be the this value.\n        // 2. Return ? SetViewValue(v, byteOffset, littleEndian, Int8, value).\n        Self::set_view_value::<i8>(this, byte_offset, is_little_endian, value, context)\n    }\n\n    /// `DataView.prototype.setInt16 ( byteOffset, value [ , littleEndian ] )`\n    ///\n    /// The `setInt16()` method stores a signed 16-bit integer (short) value at the specified byte\n    /// offset from the start of the `DataView`.\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///  - [MDN][mdn]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-dataview.prototype.setint16\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/DataView/setInt16\n    pub(crate) fn set_int16(\n        this: &JsValue,\n        args: &[JsValue],\n        context: &mut Context,\n    ) -> JsResult<JsValue> {\n        let byte_offset = args.get_or_undefined(0);\n        let value = args.get_or_undefined(1);\n        let is_little_endian = args.get_or_undefined(2);\n        // 1. Let v be the this value.\n        // 2. Return ? SetViewValue(v, byteOffset, littleEndian, Int16, value).\n        Self::set_view_value::<i16>(this, byte_offset, is_little_endian, value, context)\n    }\n\n    /// `DataView.prototype.setInt32 ( byteOffset, value [ , littleEndian ] )`\n    ///\n    /// The `setInt32()` method stores a signed 32-bit integer (long) value at the specified byte\n    /// offset from the start of the `DataView`.\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///  - [MDN][mdn]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-dataview.prototype.setint32\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/DataView/setInt32\n    pub(crate) fn set_int32(\n        this: &JsValue,\n        args: &[JsValue],\n        context: &mut Context,\n    ) -> JsResult<JsValue> {\n        let byte_offset = args.get_or_undefined(0);\n        let value = args.get_or_undefined(1);\n        let is_little_endian = args.get_or_undefined(2);\n        // 1. Let v be the this value.\n        // 2. Return ? SetViewValue(v, byteOffset, littleEndian, Int32, value).\n        Self::set_view_value::<i32>(this, byte_offset, is_little_endian, value, context)\n    }\n\n    /// `DataView.prototype.setUint8 ( byteOffset, value [ , littleEndian ] )`\n    ///\n    /// The `setUint8()` method stores an unsigned 8-bit integer (byte) value at the specified byte\n    /// offset from the start of the `DataView`.\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///  - [MDN][mdn]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-dataview.prototype.setuint8\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/DataView/setUint8\n    pub(crate) fn set_uint8(\n        this: &JsValue,\n        args: &[JsValue],\n        context: &mut Context,\n    ) -> JsResult<JsValue> {\n        let byte_offset = args.get_or_undefined(0);\n        let value = args.get_or_undefined(1);\n        let is_little_endian = args.get_or_undefined(2);\n        // 1. Let v be the this value.\n        // 2. Return ? SetViewValue(v, byteOffset, littleEndian, Uint8, value).\n        Self::set_view_value::<u8>(this, byte_offset, is_little_endian, value, context)\n    }\n\n    /// `DataView.prototype.setUint16 ( byteOffset, value [ , littleEndian ] )`\n    ///\n    /// The `setUint16()` method stores an unsigned 16-bit integer (unsigned short) value at the\n    /// specified byte offset from the start of the `DataView`.\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///  - [MDN][mdn]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-dataview.prototype.setuint16\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/DataView/setUint16\n    pub(crate) fn set_uint16(\n        this: &JsValue,\n        args: &[JsValue],\n        context: &mut Context,\n    ) -> JsResult<JsValue> {\n        let byte_offset = args.get_or_undefined(0);\n        let value = args.get_or_undefined(1);\n        let is_little_endian = args.get_or_undefined(2);\n        // 1. Let v be the this value.\n        // 2. Return ? SetViewValue(v, byteOffset, littleEndian, Uint16, value).\n        Self::set_view_value::<u16>(this, byte_offset, is_little_endian, value, context)\n    }\n\n    /// `DataView.prototype.setUint32 ( byteOffset, value [ , littleEndian ] )`\n    ///\n    /// The `setUint32()` method stores an unsigned 32-bit integer (unsigned long) value at the\n    /// specified byte offset from the start of the `DataView`.\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///  - [MDN][mdn]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-dataview.prototype.setuint32\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/DataView/setUint32\n    pub(crate) fn set_uint32(\n        this: &JsValue,\n        args: &[JsValue],\n        context: &mut Context,\n    ) -> JsResult<JsValue> {\n        let byte_offset = args.get_or_undefined(0);\n        let value = args.get_or_undefined(1);\n        let is_little_endian = args.get_or_undefined(2);\n        // 1. Let v be the this value.\n        // 2. Return ? SetViewValue(v, byteOffset, littleEndian, Uint32, value).\n        Self::set_view_value::<u32>(this, byte_offset, is_little_endian, value, context)\n    }\n}\n"
  },
  {
    "path": "core/engine/src/builtins/date/mod.rs",
    "content": "//! Boa's implementation of ECMAScript's `Date` object.\n//!\n//! More information:\n//!  - [ECMAScript reference][spec]\n//!  - [MDN documentation][mdn]\n//!\n//! [spec]: https://tc39.es/ecma262/#sec-date-objects\n//! [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date\n\nuse crate::{\n    Context, JsArgs, JsData, JsResult, JsString,\n    builtins::{\n        BuiltInBuilder, BuiltInConstructor, BuiltInObject, IntrinsicObject,\n        date::utils::{\n            MS_PER_MINUTE, date_from_time, date_string, day, hour_from_time, local_time, make_date,\n            make_day, make_full_year, make_time, min_from_time, month_from_time, ms_from_time,\n            pad_five, pad_four, pad_six, pad_three, pad_two, parse_date, sec_from_time, time_clip,\n            time_string, time_within_day, time_zone_string, to_date_string_t, utc_t, week_day,\n            year_from_time,\n        },\n    },\n    context::intrinsics::{Intrinsics, StandardConstructor, StandardConstructors},\n    error::JsNativeError,\n    js_string,\n    object::{JsObject, internal_methods::get_prototype_from_constructor},\n    property::Attribute,\n    realm::Realm,\n    string::StaticJsStrings,\n    symbol::JsSymbol,\n    value::{JsValue, PreferredType},\n};\nuse boa_gc::{Finalize, Trace};\nuse boa_macros::js_str;\n\npub(crate) mod utils;\n\n#[cfg(test)]\nmod tests;\n\n/// The internal representation of a `Date` object.\n#[derive(Debug, Copy, Clone, Trace, Finalize, JsData)]\n#[boa_gc(empty_trace)]\npub struct Date(f64);\n\nimpl Date {\n    /// Creates a new `Date`.\n    pub(crate) const fn new(dt: f64) -> Self {\n        Self(dt)\n    }\n\n    /// Creates a new `Date` from the current UTC time of the host.\n    pub(crate) fn utc_now(context: &mut Context) -> Self {\n        Self(context.clock().system_time_millis() as f64)\n    }\n\n    /// Formats this date as an ISO 8601 string for display purposes.\n    ///\n    /// Returns `None` if the date value is not finite (i.e. `Invalid Date`).\n    pub(crate) fn to_iso_display(self) -> Option<String> {\n        let tv = self.0;\n        if !tv.is_finite() {\n            return None;\n        }\n        let year = year_from_time(tv);\n        let year_str = if year.is_positive() && year >= 10000 {\n            format!(\"+{year:06}\")\n        } else if year >= 0 {\n            format!(\"{year:04}\")\n        } else {\n            format!(\"-{:06}\", year.unsigned_abs())\n        };\n        Some(format!(\n            \"{}-{:02}-{:02}T{:02}:{:02}:{:02}.{:03}Z\",\n            year_str,\n            month_from_time(tv) + 1,\n            date_from_time(tv),\n            hour_from_time(tv),\n            min_from_time(tv),\n            sec_from_time(tv),\n            ms_from_time(tv),\n        ))\n    }\n}\n\nimpl IntrinsicObject for Date {\n    fn init(realm: &Realm) {\n        let to_utc_string = BuiltInBuilder::callable(realm, Self::to_utc_string)\n            .name(js_string!(\"toUTCString\"))\n            .length(0)\n            .build();\n\n        let to_primitive = BuiltInBuilder::callable(realm, Self::to_primitive)\n            .name(js_string!(\"[Symbol.toPrimitive]\"))\n            .length(1)\n            .build();\n\n        let builder = BuiltInBuilder::from_standard_constructor::<Self>(realm)\n            .static_method(Self::now, js_string!(\"now\"), 0)\n            .static_method(Self::parse, js_string!(\"parse\"), 1)\n            .static_method(Self::utc, js_string!(\"UTC\"), 7)\n            .method(Self::get_date::<true>, js_string!(\"getDate\"), 0)\n            .method(Self::get_day::<true>, js_string!(\"getDay\"), 0)\n            .method(Self::get_full_year::<true>, js_string!(\"getFullYear\"), 0)\n            .method(Self::get_hours::<true>, js_string!(\"getHours\"), 0)\n            .method(\n                Self::get_milliseconds::<true>,\n                js_string!(\"getMilliseconds\"),\n                0,\n            )\n            .method(Self::get_minutes::<true>, js_string!(\"getMinutes\"), 0)\n            .method(Self::get_month::<true>, js_string!(\"getMonth\"), 0)\n            .method(Self::get_seconds::<true>, js_string!(\"getSeconds\"), 0)\n            .method(Self::get_time, js_string!(\"getTime\"), 0)\n            .method(\n                Self::get_timezone_offset,\n                js_string!(\"getTimezoneOffset\"),\n                0,\n            )\n            .method(Self::get_date::<false>, js_string!(\"getUTCDate\"), 0)\n            .method(Self::get_day::<false>, js_string!(\"getUTCDay\"), 0)\n            .method(\n                Self::get_full_year::<false>,\n                js_string!(\"getUTCFullYear\"),\n                0,\n            )\n            .method(Self::get_hours::<false>, js_string!(\"getUTCHours\"), 0)\n            .method(\n                Self::get_milliseconds::<false>,\n                js_string!(\"getUTCMilliseconds\"),\n                0,\n            )\n            .method(Self::get_minutes::<false>, js_string!(\"getUTCMinutes\"), 0)\n            .method(Self::get_month::<false>, js_string!(\"getUTCMonth\"), 0)\n            .method(Self::get_seconds::<false>, js_string!(\"getUTCSeconds\"), 0)\n            .method(Self::get_year, js_string!(\"getYear\"), 0)\n            .method(Self::set_date::<true>, js_string!(\"setDate\"), 1)\n            .method(Self::set_full_year::<true>, js_string!(\"setFullYear\"), 3)\n            .method(Self::set_hours::<true>, js_string!(\"setHours\"), 4)\n            .method(\n                Self::set_milliseconds::<true>,\n                js_string!(\"setMilliseconds\"),\n                1,\n            )\n            .method(Self::set_minutes::<true>, js_string!(\"setMinutes\"), 3)\n            .method(Self::set_month::<true>, js_string!(\"setMonth\"), 2)\n            .method(Self::set_seconds::<true>, js_string!(\"setSeconds\"), 2)\n            .method(Self::set_time, js_string!(\"setTime\"), 1)\n            .method(Self::set_date::<false>, js_string!(\"setUTCDate\"), 1)\n            .method(\n                Self::set_full_year::<false>,\n                js_string!(\"setUTCFullYear\"),\n                3,\n            )\n            .method(Self::set_hours::<false>, js_string!(\"setUTCHours\"), 4)\n            .method(\n                Self::set_milliseconds::<false>,\n                js_string!(\"setUTCMilliseconds\"),\n                1,\n            )\n            .method(Self::set_minutes::<false>, js_string!(\"setUTCMinutes\"), 3)\n            .method(Self::set_month::<false>, js_string!(\"setUTCMonth\"), 2)\n            .method(Self::set_seconds::<false>, js_string!(\"setUTCSeconds\"), 2)\n            .method(Self::set_year, js_string!(\"setYear\"), 1)\n            .method(Self::to_date_string, js_string!(\"toDateString\"), 0)\n            .method(Self::to_iso_string, js_string!(\"toISOString\"), 0)\n            .method(Self::to_json, js_string!(\"toJSON\"), 1)\n            .method(\n                Self::to_locale_date_string,\n                js_string!(\"toLocaleDateString\"),\n                0,\n            )\n            .method(Self::to_locale_string, js_string!(\"toLocaleString\"), 0)\n            .method(\n                Self::to_locale_time_string,\n                js_string!(\"toLocaleTimeString\"),\n                0,\n            )\n            .method(Self::to_string, js_string!(\"toString\"), 0)\n            .method(Self::to_time_string, js_string!(\"toTimeString\"), 0)\n            .method(Self::value_of, js_string!(\"valueOf\"), 0)\n            .property(\n                js_string!(\"toGMTString\"),\n                to_utc_string.clone(),\n                Attribute::WRITABLE | Attribute::NON_ENUMERABLE | Attribute::CONFIGURABLE,\n            )\n            .property(\n                js_string!(\"toUTCString\"),\n                to_utc_string,\n                Attribute::WRITABLE | Attribute::NON_ENUMERABLE | Attribute::CONFIGURABLE,\n            )\n            .property(\n                JsSymbol::to_primitive(),\n                to_primitive,\n                Attribute::READONLY | Attribute::NON_ENUMERABLE | Attribute::CONFIGURABLE,\n            );\n\n        #[cfg(feature = \"temporal\")]\n        let builder = builder.method(\n            Self::to_temporal_instant,\n            js_string!(\"toTemporalInstant\"),\n            0,\n        );\n\n        builder.build();\n    }\n\n    fn get(intrinsics: &Intrinsics) -> JsObject {\n        Self::STANDARD_CONSTRUCTOR(intrinsics.constructors()).constructor()\n    }\n}\n\nimpl BuiltInObject for Date {\n    const NAME: JsString = StaticJsStrings::DATE;\n}\n\nimpl BuiltInConstructor for Date {\n    const CONSTRUCTOR_ARGUMENTS: usize = 7;\n    const PROTOTYPE_STORAGE_SLOTS: usize = 48;\n    const CONSTRUCTOR_STORAGE_SLOTS: usize = 3;\n\n    const STANDARD_CONSTRUCTOR: fn(&StandardConstructors) -> &StandardConstructor =\n        StandardConstructors::date;\n\n    /// [`Date ( ...values )`][spec]\n    ///\n    /// - When called as a function, returns a string displaying the current time in the UTC timezone.\n    /// - When called as a constructor, it returns a new `Date` object from the provided arguments.\n    ///   The [MDN documentation][mdn] has a more extensive explanation on the usages and return\n    ///   values for all possible arguments.\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-date-constructor\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/Date\n    fn constructor(\n        new_target: &JsValue,\n        args: &[JsValue],\n        context: &mut Context,\n    ) -> JsResult<JsValue> {\n        // 1. If NewTarget is undefined, then\n        if new_target.is_undefined() {\n            // a. Let now be the time value (UTC) identifying the current time.\n            let now = context.clock().system_time_millis();\n\n            // b. Return ToDateString(now).\n            return Ok(JsValue::from(to_date_string_t(\n                now as f64,\n                context.host_hooks().as_ref(),\n            )));\n        }\n\n        // 2. Let numberOfArgs be the number of elements in values.\n        let dv = match args {\n            // 3. If numberOfArgs = 0, then\n            [] => {\n                // a. Let dv be the time value (UTC) identifying the current time.\n                Self::utc_now(context)\n            }\n            // 4. Else if numberOfArgs = 1, then\n            // a. Let value be values[0].\n            [value] => {\n                // b. If value is an Object and value has a [[DateValue]] internal slot, then\n                let object = value.as_object();\n                let tv =\n                    if let Some(date) = object.as_ref().and_then(JsObject::downcast_ref::<Self>) {\n                        // i. Let tv be value.[[DateValue]].\n                        date.0\n                    }\n                    // c. Else,\n                    else {\n                        // i. Let v be ? ToPrimitive(value).\n                        let v = value.to_primitive(context, PreferredType::Default)?;\n\n                        // ii. If v is a String, then\n                        if let Some(v) = v.as_string() {\n                            // 1. Assert: The next step never returns an abrupt completion because v is a String.\n                            // 2. Let tv be the result of parsing v as a date, in exactly the same manner as for the parse method (21.4.3.2).\n                            let tv = parse_date(&v, context.host_hooks().as_ref());\n                            if let Some(tv) = tv {\n                                tv as f64\n                            } else {\n                                f64::NAN\n                            }\n                        }\n                        // iii. Else,\n                        else {\n                            // 1. Let tv be ? ToNumber(v).\n                            v.to_number(context)?\n                        }\n                    };\n\n                // d. Let dv be TimeClip(tv).\n                Self(time_clip(tv))\n            }\n            // 5. Else,\n            _ => {\n                // Separating this into its own function to simplify the logic.\n                //let dt = Self::construct_date(args, context)?\n                //    .and_then(|dt| context.host_hooks().local_from_naive_local(dt).earliest());\n                //Self(dt.map(|dt| dt.timestamp_millis()))\n\n                // a. Assert: numberOfArgs ≥ 2.\n                // b. Let y be ? ToNumber(values[0]).\n                let y = args.get_or_undefined(0).to_number(context)?;\n\n                // c. Let m be ? ToNumber(values[1]).\n                let m = args.get_or_undefined(1).to_number(context)?;\n\n                // d. If numberOfArgs > 2, let dt be ? ToNumber(values[2]); else let dt be 1𝔽.\n                let dt = args.get(2).map_or(Ok(1.0), |n| n.to_number(context))?;\n\n                // e. If numberOfArgs > 3, let h be ? ToNumber(values[3]); else let h be +0𝔽.\n                let h = args.get(3).map_or(Ok(0.0), |n| n.to_number(context))?;\n\n                // f. If numberOfArgs > 4, let min be ? ToNumber(values[4]); else let min be +0𝔽.\n                let min = args.get(4).map_or(Ok(0.0), |n| n.to_number(context))?;\n\n                // g. If numberOfArgs > 5, let s be ? ToNumber(values[5]); else let s be +0𝔽.\n                let s = args.get(5).map_or(Ok(0.0), |n| n.to_number(context))?;\n\n                // h. If numberOfArgs > 6, let milli be ? ToNumber(values[6]); else let milli be +0𝔽.\n                let milli = args.get(6).map_or(Ok(0.0), |n| n.to_number(context))?;\n\n                // i. Let yr be MakeFullYear(y).\n                let yr = make_full_year(y);\n\n                // j. Let finalDate be MakeDate(MakeDay(yr, m, dt), MakeTime(h, min, s, milli)).\n                let final_date = make_date(make_day(yr, m, dt), make_time(h, min, s, milli));\n\n                // k. Let dv be TimeClip(UTC(finalDate)).\n                Self(time_clip(utc_t(final_date, context.host_hooks().as_ref())))\n            }\n        };\n\n        // 6. Let O be ? OrdinaryCreateFromConstructor(NewTarget, \"%Date.prototype%\", « [[DateValue]] »).\n        let prototype =\n            get_prototype_from_constructor(new_target, StandardConstructors::date, context)?;\n\n        // 7. Set O.[[DateValue]] to dv.\n        let obj =\n            JsObject::from_proto_and_data_with_shared_shape(context.root_shape(), prototype, dv);\n\n        // 8. Return O.\n        Ok(obj.into())\n    }\n}\n\nimpl Date {\n    /// `Date.now()`\n    ///\n    /// The static `Date.now()` method returns the number of milliseconds elapsed since January 1, 1970 00:00:00 UTC.\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///  - [MDN documentation][mdn]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-date.now\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/now\n    #[allow(clippy::unnecessary_wraps)]\n    pub(crate) fn now(_: &JsValue, _: &[JsValue], context: &mut Context) -> JsResult<JsValue> {\n        Ok(JsValue::new(context.clock().system_time_millis()))\n    }\n\n    /// `Date.parse()`\n    ///\n    /// The `Date.parse()` method parses a string representation of a date, and returns the number of milliseconds since\n    /// January 1, 1970, 00:00:00 UTC or `NaN` if the string is unrecognized or, in some cases, contains illegal date\n    /// values.\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///  - [MDN documentation][mdn]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-date.parse\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/parse\n    pub(crate) fn parse(_: &JsValue, args: &[JsValue], context: &mut Context) -> JsResult<JsValue> {\n        let date = args.get_or_undefined(0).to_string(context)?;\n        Ok(parse_date(&date, context.host_hooks().as_ref())\n            .map_or(JsValue::from(f64::NAN), JsValue::from))\n    }\n\n    /// `Date.UTC()`\n    ///\n    /// The `Date.UTC()` method accepts parameters similar to the `Date` constructor, but treats them as UTC.\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///  - [MDN documentation][mdn]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-date.utc\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/UTC\n    pub(crate) fn utc(_: &JsValue, args: &[JsValue], context: &mut Context) -> JsResult<JsValue> {\n        // 1. Let y be ? ToNumber(year).\n        let y = args.get_or_undefined(0).to_number(context)?;\n\n        // 2. If month is present, let m be ? ToNumber(month); else let m be +0𝔽.\n        let m = args\n            .get(1)\n            .map_or(Ok(0f64), |value| value.to_number(context))?;\n\n        // 3. If date is present, let dt be ? ToNumber(date); else let dt be 1𝔽.\n        let dt = args\n            .get(2)\n            .map_or(Ok(1f64), |value| value.to_number(context))?;\n\n        // 4. If hours is present, let h be ? ToNumber(hours); else let h be +0𝔽.\n        let h = args\n            .get(3)\n            .map_or(Ok(0f64), |value| value.to_number(context))?;\n\n        // 5. If minutes is present, let min be ? ToNumber(minutes); else let min be +0𝔽.\n        let min = args\n            .get(4)\n            .map_or(Ok(0f64), |value| value.to_number(context))?;\n\n        // 6. If seconds is present, let s be ? ToNumber(seconds); else let s be +0𝔽.\n        let s = args\n            .get(5)\n            .map_or(Ok(0f64), |value| value.to_number(context))?;\n\n        // 7. If ms is present, let milli be ? ToNumber(ms); else let milli be +0𝔽.\n        let milli = args\n            .get(6)\n            .map_or(Ok(0f64), |value| value.to_number(context))?;\n\n        // 8. Let yr be MakeFullYear(y).\n        let yr = make_full_year(y);\n\n        // 9. Return TimeClip(MakeDate(MakeDay(yr, m, dt), MakeTime(h, min, s, milli))).\n        Ok(JsValue::from(time_clip(make_date(\n            make_day(yr, m, dt),\n            make_time(h, min, s, milli),\n        ))))\n    }\n\n    /// [`Date.prototype.getDate ( )`][local] and\n    /// [`Date.prototype.getUTCDate ( )`][utc].\n    ///\n    /// The `getDate()` method returns the day of the month for the specified date.\n    ///\n    /// [local]: https://tc39.es/ecma262/#sec-date.prototype.getdate\n    /// [utc]: https://tc39.es/ecma262/#sec-date.prototype.getutcdate\n    pub(crate) fn get_date<const LOCAL: bool>(\n        this: &JsValue,\n        _args: &[JsValue],\n        context: &mut Context,\n    ) -> JsResult<JsValue> {\n        // 1. Let dateObject be the this value.\n        // 2. Perform ? RequireInternalSlot(dateObject, [[DateValue]]).\n        // 3. Let t be dateObject.[[DateValue]].\n        let t = this\n            .as_object()\n            .and_then(|obj| obj.downcast_ref::<Date>().as_deref().copied())\n            .ok_or_else(|| JsNativeError::typ().with_message(\"'this' is not a Date\"))?\n            .0;\n\n        // 4. If t is NaN, return NaN.\n        if t.is_nan() {\n            return Ok(JsValue::new(f64::NAN));\n        }\n\n        if LOCAL {\n            // 5. Return DateFromTime(LocalTime(t)).\n            Ok(JsValue::from(date_from_time(local_time(\n                t,\n                context.host_hooks().as_ref(),\n            ))))\n        } else {\n            // 5. Return DateFromTime(t).\n            Ok(JsValue::from(date_from_time(t)))\n        }\n    }\n\n    /// [`Date.prototype.getDay ( )`][local] and\n    /// [`Date.prototype.getUTCDay ( )`][utc].\n    ///\n    /// The `getDay()` method returns the day of the week for the specified date, where 0 represents\n    /// Sunday.\n    ///\n    /// [local]: https://tc39.es/ecma262/#sec-date.prototype.getday\n    /// [utc]: https://tc39.es/ecma262/#sec-date.prototype.getutcday\n    pub(crate) fn get_day<const LOCAL: bool>(\n        this: &JsValue,\n        _args: &[JsValue],\n        context: &mut Context,\n    ) -> JsResult<JsValue> {\n        // 1. Let dateObject be the this value.\n        // 2. Perform ? RequireInternalSlot(dateObject, [[DateValue]]).\n        // 3. Let t be dateObject.[[DateValue]].\n        let t = this\n            .as_object()\n            .and_then(|obj| obj.downcast_ref::<Date>().as_deref().copied())\n            .ok_or_else(|| JsNativeError::typ().with_message(\"'this' is not a Date\"))?\n            .0;\n\n        // 4. If t is NaN, return NaN.\n        if t.is_nan() {\n            return Ok(JsValue::from(f64::NAN));\n        }\n\n        if LOCAL {\n            // 5. Return WeekDay(LocalTime(t)).\n            Ok(JsValue::from(week_day(local_time(\n                t,\n                context.host_hooks().as_ref(),\n            ))))\n        } else {\n            // 5. Return WeekDay(t).\n            Ok(JsValue::from(week_day(t)))\n        }\n    }\n\n    /// [`Date.prototype.getYear()`][spec].\n    ///\n    /// The `getYear()` method returns the year in the specified date according to local time.\n    /// Because `getYear()` does not return full years (\"year 2000 problem\"), it is no longer used\n    /// and has been replaced by the `getFullYear()` method.\n    ///\n    /// More information:\n    ///  - [MDN documentation][mdn]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-date.prototype.getyear\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/getYear\n    pub(crate) fn get_year(\n        this: &JsValue,\n        _args: &[JsValue],\n        context: &mut Context,\n    ) -> JsResult<JsValue> {\n        // 1. Let dateObject be the this value.\n        // 2. Perform ? RequireInternalSlot(dateObject, [[DateValue]]).\n        // 3. Let t be dateObject.[[DateValue]].\n        let t = this\n            .as_object()\n            .and_then(|obj| obj.downcast_ref::<Date>().as_deref().copied())\n            .ok_or_else(|| JsNativeError::typ().with_message(\"'this' is not a Date\"))?\n            .0;\n\n        // 4. If t is NaN, return NaN.\n        if t.is_nan() {\n            return Ok(JsValue::from(f64::NAN));\n        }\n\n        // 5. Return YearFromTime(LocalTime(t)) - 1900𝔽.\n        Ok(JsValue::from(\n            year_from_time(local_time(t, context.host_hooks().as_ref())) - 1900,\n        ))\n    }\n\n    /// [`Date.prototype.getFullYear ( )`][local] and\n    /// [`Date.prototype.getUTCFullYear ( )`][utc].\n    ///\n    /// The `getFullYear()` method returns the year of the specified date.\n    ///\n    /// [local]: https://tc39.es/ecma262/#sec-date.prototype.getfullyear\n    /// [utc]: https://tc39.es/ecma262/#sec-date.prototype.getutcfullyear\n    pub(crate) fn get_full_year<const LOCAL: bool>(\n        this: &JsValue,\n        _args: &[JsValue],\n        context: &mut Context,\n    ) -> JsResult<JsValue> {\n        // 1. Let dateObject be the this value.\n        // 2. Perform ? RequireInternalSlot(dateObject, [[DateValue]]).\n        // 3. Let t be dateObject.[[DateValue]].\n        let t = this\n            .as_object()\n            .and_then(|obj| obj.downcast_ref::<Date>().as_deref().copied())\n            .ok_or_else(|| JsNativeError::typ().with_message(\"'this' is not a Date\"))?\n            .0;\n\n        // 4. If t is NaN, return NaN.\n        if t.is_nan() {\n            return Ok(JsValue::from(f64::NAN));\n        }\n\n        if LOCAL {\n            // 5. Return YearFromTime(LocalTime(t)).\n            Ok(JsValue::from(year_from_time(local_time(\n                t,\n                context.host_hooks().as_ref(),\n            ))))\n        } else {\n            // 5. Return YearFromTime(t).\n            Ok(JsValue::from(year_from_time(t)))\n        }\n    }\n\n    /// [`Date.prototype.getHours ( )`][local] and\n    /// [`Date.prototype.getUTCHours ( )`][utc].\n    ///\n    /// The `getHours()` method returns the hour for the specified date.\n    ///\n    /// [local]: https://tc39.es/ecma262/#sec-date.prototype.gethours\n    /// [utc]: https://tc39.es/ecma262/#sec-date.prototype.getutchours\n    pub(crate) fn get_hours<const LOCAL: bool>(\n        this: &JsValue,\n        _args: &[JsValue],\n        context: &mut Context,\n    ) -> JsResult<JsValue> {\n        // 1. Let dateObject be the this value.\n        // 2. Perform ? RequireInternalSlot(dateObject, [[DateValue]]).\n        // 3. Let t be dateObject.[[DateValue]].\n        let t = this\n            .as_object()\n            .and_then(|obj| obj.downcast_ref::<Date>().as_deref().copied())\n            .ok_or_else(|| JsNativeError::typ().with_message(\"'this' is not a Date\"))?\n            .0;\n\n        // 4. If t is NaN, return NaN.\n        if t.is_nan() {\n            return Ok(JsValue::from(f64::NAN));\n        }\n\n        if LOCAL {\n            // 5. Return HourFromTime(LocalTime(t)).\n            Ok(JsValue::from(hour_from_time(local_time(\n                t,\n                context.host_hooks().as_ref(),\n            ))))\n        } else {\n            // 5. Return HourFromTime(t).\n            Ok(JsValue::from(hour_from_time(t)))\n        }\n    }\n\n    /// [`Date.prototype.getMilliseconds ( )`][local] and\n    /// [`Date.prototype.getUTCMilliseconds ( )`][utc].\n    ///\n    /// The `getMilliseconds()` method returns the milliseconds in the specified date.\n    ///\n    /// [local]: https://tc39.es/ecma262/#sec-date.prototype.getmilliseconds\n    /// [utc]: https://tc39.es/ecma262/#sec-date.prototype.getutcmilliseconds\n    pub(crate) fn get_milliseconds<const LOCAL: bool>(\n        this: &JsValue,\n        _args: &[JsValue],\n        context: &mut Context,\n    ) -> JsResult<JsValue> {\n        // 1. Let dateObject be the this value.\n        // 2. Perform ? RequireInternalSlot(dateObject, [[DateValue]]).\n        // 3. Let t be dateObject.[[DateValue]].\n        let t = this\n            .as_object()\n            .and_then(|obj| obj.downcast_ref::<Date>().as_deref().copied())\n            .ok_or_else(|| JsNativeError::typ().with_message(\"'this' is not a Date\"))?\n            .0;\n\n        // 4. If t is NaN, return NaN.\n        if t.is_nan() {\n            return Ok(JsValue::from(f64::NAN));\n        }\n\n        if LOCAL {\n            // 5. Return msFromTime(LocalTime(t)).\n            Ok(JsValue::from(ms_from_time(local_time(\n                t,\n                context.host_hooks().as_ref(),\n            ))))\n        } else {\n            // 5. Return msFromTime(t).\n            Ok(JsValue::from(ms_from_time(t)))\n        }\n    }\n\n    /// [`Date.prototype.getMinutes ( )`][local] and\n    /// [`Date.prototype.getUTCMinutes ( )`][utc].\n    ///\n    /// The `getMinutes()` method returns the minutes in the specified date.\n    ///\n    /// [local]: https://tc39.es/ecma262/#sec-date.prototype.getminutes\n    /// [utc]: https://tc39.es/ecma262/#sec-date.prototype.getutcminutes\n    pub(crate) fn get_minutes<const LOCAL: bool>(\n        this: &JsValue,\n        _args: &[JsValue],\n        context: &mut Context,\n    ) -> JsResult<JsValue> {\n        // 1. Let dateObject be the this value.\n        // 2. Perform ? RequireInternalSlot(dateObject, [[DateValue]]).\n        // 3. Let t be dateObject.[[DateValue]].\n        let t = this\n            .as_object()\n            .and_then(|obj| obj.downcast_ref::<Date>().as_deref().copied())\n            .ok_or_else(|| JsNativeError::typ().with_message(\"'this' is not a Date\"))?\n            .0;\n\n        // 4. If t is NaN, return NaN.\n        if t.is_nan() {\n            return Ok(JsValue::from(f64::NAN));\n        }\n\n        if LOCAL {\n            // 5. Return MinFromTime(LocalTime(t)).\n            Ok(JsValue::from(min_from_time(local_time(\n                t,\n                context.host_hooks().as_ref(),\n            ))))\n        } else {\n            // 5. Return MinFromTime(t).\n            Ok(JsValue::from(min_from_time(t)))\n        }\n    }\n\n    /// [`Date.prototype.getMonth ( )`][local] and\n    /// [`Date.prototype.getUTCMonth ( )`][utc].\n    ///\n    /// The `getMonth()` method returns the month in the specified date, as a zero-based value\n    /// (where zero indicates the first month of the year).\n    ///\n    /// [local]: https://tc39.es/ecma262/#sec-date.prototype.getmonth\n    /// [utc]: https://tc39.es/ecma262/#sec-date.prototype.getutcmonth\n    pub(crate) fn get_month<const LOCAL: bool>(\n        this: &JsValue,\n        _args: &[JsValue],\n        context: &mut Context,\n    ) -> JsResult<JsValue> {\n        // 1. Let dateObject be the this value.\n        // 2. Perform ? RequireInternalSlot(dateObject, [[DateValue]]).\n        // 3. Let t be dateObject.[[DateValue]].\n        let t = this\n            .as_object()\n            .and_then(|obj| obj.downcast_ref::<Date>().as_deref().copied())\n            .ok_or_else(|| JsNativeError::typ().with_message(\"'this' is not a Date\"))?\n            .0;\n\n        // 4. If t is NaN, return NaN.\n        if t.is_nan() {\n            return Ok(JsValue::from(f64::NAN));\n        }\n\n        if LOCAL {\n            // 5. Return MonthFromTime(LocalTime(t)).\n            Ok(JsValue::from(month_from_time(local_time(\n                t,\n                context.host_hooks().as_ref(),\n            ))))\n        } else {\n            // 5. Return MonthFromTime(t).\n            Ok(JsValue::from(month_from_time(t)))\n        }\n    }\n\n    /// [`Date.prototype.getSeconds ( )`][local] and\n    /// [`Date.prototype.getUTCSeconds ( )`][utc].\n    ///\n    /// The `getSeconds()` method returns the seconds in the specified date.\n    ///\n    /// [local]: https://tc39.es/ecma262/#sec-date.prototype.getseconds\n    /// [utc]: https://tc39.es/ecma262/#sec-date.prototype.getutcseconds\n    pub(crate) fn get_seconds<const LOCAL: bool>(\n        this: &JsValue,\n        _args: &[JsValue],\n        context: &mut Context,\n    ) -> JsResult<JsValue> {\n        // 1. Let dateObject be the this value.\n        // 2. Perform ? RequireInternalSlot(dateObject, [[DateValue]]).\n        // 3. Let t be dateObject.[[DateValue]].\n        let t = this\n            .as_object()\n            .and_then(|obj| obj.downcast_ref::<Date>().as_deref().copied())\n            .ok_or_else(|| JsNativeError::typ().with_message(\"'this' is not a Date\"))?\n            .0;\n\n        // 4. If t is NaN, return NaN.\n        if t.is_nan() {\n            return Ok(JsValue::from(f64::NAN));\n        }\n\n        if LOCAL {\n            // 5. Return SecFromTime(LocalTime(t)).\n            Ok(JsValue::from(sec_from_time(local_time(\n                t,\n                context.host_hooks().as_ref(),\n            ))))\n        } else {\n            // 5. Return SecFromTime(t).\n            Ok(JsValue::from(sec_from_time(t)))\n        }\n    }\n\n    /// `Date.prototype.getTime()`.\n    ///\n    /// The `getTime()` method returns the number of milliseconds since the Unix Epoch.\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///  - [MDN documentation][mdn]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-date.prototype.gettime\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/getTime\n    pub(crate) fn get_time(\n        this: &JsValue,\n        _args: &[JsValue],\n        _context: &mut Context,\n    ) -> JsResult<JsValue> {\n        // 1. Let dateObject be the this value.\n        // 2. Perform ? RequireInternalSlot(dateObject, [[DateValue]]).\n        // 3. Return dateObject.[[DateValue]].\n        Ok(this\n            .as_object()\n            .and_then(|obj| obj.downcast_ref::<Date>().as_deref().copied())\n            .ok_or_else(|| JsNativeError::typ().with_message(\"'this' is not a Date\"))?\n            .0\n            .into())\n    }\n\n    /// `Date.prototype.getTimeZoneOffset()`.\n    ///\n    /// The `getTimezoneOffset()` method returns the time zone difference, in minutes, from current locale (host system\n    /// settings) to UTC.\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///  - [MDN documentation][mdn]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-date.prototype.gettimezoneoffset\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/getTimezoneOffset\n    pub(crate) fn get_timezone_offset(\n        this: &JsValue,\n        _: &[JsValue],\n        context: &mut Context,\n    ) -> JsResult<JsValue> {\n        // 1. Let dateObject be the this value.\n        // 2. Perform ? RequireInternalSlot(dateObject, [[DateValue]]).\n        // 3. Let t be dateObject.[[DateValue]].\n        let t = this\n            .as_object()\n            .and_then(|obj| obj.downcast_ref::<Date>().as_deref().copied())\n            .ok_or_else(|| JsNativeError::typ().with_message(\"'this' is not a Date\"))?\n            .0;\n\n        // 4. If t is NaN, return NaN.\n        if t.is_nan() {\n            return Ok(JsValue::from(f64::NAN));\n        }\n\n        // 5. Return (t - LocalTime(t)) / msPerMinute.\n        Ok(JsValue::from(\n            (t - local_time(t, context.host_hooks().as_ref())) / MS_PER_MINUTE,\n        ))\n    }\n\n    /// [`Date.prototype.setDate ( date )`][local] and\n    /// [`Date.prototype.setUTCDate ( date )`][utc].\n    ///\n    /// The `setDate()` method sets the day of the `Date` object relative to the beginning of the\n    /// currently set month.\n    ///\n    /// [local]: https://tc39.es/ecma262/#sec-date.prototype.setdate\n    /// [utc]: https://tc39.es/ecma262/#sec-date.prototype.setutcdate\n    pub(crate) fn set_date<const LOCAL: bool>(\n        this: &JsValue,\n        args: &[JsValue],\n        context: &mut Context,\n    ) -> JsResult<JsValue> {\n        // 1. Let dateObject be the this value.\n        // 2. Perform ? RequireInternalSlot(dateObject, [[DateValue]]).\n        let object = this.as_object();\n        let date = object\n            .as_ref()\n            .and_then(JsObject::downcast_ref::<Date>)\n            .ok_or_else(|| JsNativeError::typ().with_message(\"'this' is not a Date\"))?;\n\n        // 3. Let t be dateObject.[[DateValue]].\n        let mut t = date.0;\n\n        // NOTE (nekevss): `downcast_ref` is used and then dropped for a short lived borrow.\n        // ToNumber() may call userland code which can modify the underlying date\n        // which will cause a panic. In order to avoid this, we drop the borrow,\n        // here and only `downcast_mut` when date will be modified.\n        drop(date);\n\n        // 4. Let dt be ? ToNumber(date).\n        let dt = args.get_or_undefined(0).to_number(context)?;\n\n        // 5. If t is NaN, return NaN.\n        if t.is_nan() {\n            return Ok(JsValue::from(f64::NAN));\n        }\n\n        if LOCAL {\n            // 6. Set t to LocalTime(t).\n            t = local_time(t, context.host_hooks().as_ref());\n        }\n\n        // 7. Let newDate be MakeDate(MakeDay(YearFromTime(t), MonthFromTime(t), dt), TimeWithinDay(t)).\n        let new_date = make_date(\n            make_day(year_from_time(t).into(), month_from_time(t).into(), dt),\n            time_within_day(t),\n        );\n\n        let u = if LOCAL {\n            // 8. Let u be TimeClip(UTC(newDate)).\n            time_clip(utc_t(new_date, context.host_hooks().as_ref()))\n        } else {\n            // 8. Let v be TimeClip(newDate).\n            time_clip(new_date)\n        };\n\n        let object = this.as_object();\n        let mut date_mut = object\n            .as_ref()\n            .and_then(JsObject::downcast_mut::<Date>)\n            .ok_or_else(|| JsNativeError::typ().with_message(\"'this' is not a Date\"))?;\n\n        // 9. Set dateObject.[[DateValue]] to u.\n        date_mut.0 = u;\n\n        // 10. Return u.\n        Ok(JsValue::from(u))\n    }\n\n    /// [`Date.prototype.setFullYear ( year [ , month [ , date ] ] )`][local] and\n    /// [Date.prototype.setUTCFullYear ( year [ , month [ , date ] ] )][utc].\n    ///\n    /// The `setFullYear()` method sets the full year for a specified date and returns the new\n    /// timestamp.\n    ///\n    /// [local]: https://tc39.es/ecma262/#sec-date.prototype.setfullyear\n    /// [utc]: https://tc39.es/ecma262/#sec-date.prototype.setutcfullyear\n    pub(crate) fn set_full_year<const LOCAL: bool>(\n        this: &JsValue,\n        args: &[JsValue],\n        context: &mut Context,\n    ) -> JsResult<JsValue> {\n        // 1. Let dateObject be the this value.\n        // 2. Perform ? RequireInternalSlot(dateObject, [[DateValue]]).\n        let object = this.as_object();\n        let date = object\n            .as_ref()\n            .and_then(JsObject::downcast_ref::<Date>)\n            .ok_or_else(|| JsNativeError::typ().with_message(\"'this' is not a Date\"))?;\n\n        // 3. Let t be dateObject.[[DateValue]].\n        let t = date.0;\n\n        // NOTE (nekevss): `downcast_ref` is used and then dropped for a short lived borrow.\n        // ToNumber() may call userland code which can modify the underlying date\n        // which will cause a panic. In order to avoid this, we drop the borrow,\n        // here and only `downcast_mut` when date will be modified.\n        drop(date);\n\n        let t = if LOCAL {\n            // 5. If t is NaN, set t to +0𝔽; otherwise, set t to LocalTime(t).\n            if t.is_nan() {\n                0.0\n            } else {\n                local_time(t, context.host_hooks().as_ref())\n            }\n        } else {\n            // 4. If t is NaN, set t to +0𝔽.\n            if t.is_nan() { 0.0 } else { t }\n        };\n\n        // 4. Let y be ? ToNumber(year).\n        let y = args.get_or_undefined(0).to_number(context)?;\n\n        // 6. If month is not present, let m be MonthFromTime(t); otherwise, let m be ? ToNumber(month).\n        let m = if let Some(month) = args.get(1) {\n            month.to_number(context)?\n        } else {\n            month_from_time(t).into()\n        };\n\n        // 7. If date is not present, let dt be DateFromTime(t); otherwise, let dt be ? ToNumber(date).\n        let dt = if let Some(date) = args.get(2) {\n            date.to_number(context)?\n        } else {\n            date_from_time(t).into()\n        };\n\n        // 8. Let newDate be MakeDate(MakeDay(y, m, dt), TimeWithinDay(t)).\n        let new_date = make_date(make_day(y, m, dt), time_within_day(t));\n\n        let u = if LOCAL {\n            // 9. Let u be TimeClip(UTC(newDate)).\n            time_clip(utc_t(new_date, context.host_hooks().as_ref()))\n        } else {\n            // 9. Let u be TimeClip(newDate).\n            time_clip(new_date)\n        };\n\n        let object = this.as_object();\n        let mut date_mut = object\n            .as_ref()\n            .and_then(JsObject::downcast_mut::<Date>)\n            .ok_or_else(|| JsNativeError::typ().with_message(\"'this' is not a Date\"))?;\n\n        // 10. Set dateObject.[[DateValue]] to u.\n        date_mut.0 = u;\n\n        // 11. Return u.\n        Ok(JsValue::from(u))\n    }\n\n    /// [`Date.prototype.setHours ( hour [ , min [ , sec [ , ms ] ] ] )`][local] and\n    /// [`Date.prototype.setUTCHours ( hour [ , min [ , sec [ , ms ] ] ] )`][utc].\n    ///\n    /// The `setHours()` method sets the hours for a specified date, and returns the number\n    /// of milliseconds since January 1, 1970 00:00:00 UTC until the time represented by the\n    /// updated `Date` instance.\n    ///\n    /// [local]: https://tc39.es/ecma262/#sec-date.prototype.sethours\n    /// [utc]: https://tc39.es/ecma262/#sec-date.prototype.setutchours\n    #[allow(clippy::many_single_char_names)]\n    pub(crate) fn set_hours<const LOCAL: bool>(\n        this: &JsValue,\n        args: &[JsValue],\n        context: &mut Context,\n    ) -> JsResult<JsValue> {\n        // 1. Let dateObject be the this value.\n        // 2. Perform ? RequireInternalSlot(dateObject, [[DateValue]]).\n        let object = this.as_object();\n        let date = object\n            .as_ref()\n            .and_then(JsObject::downcast_ref::<Date>)\n            .ok_or_else(|| JsNativeError::typ().with_message(\"'this' is not a Date\"))?;\n\n        // 3. Let t be dateObject.[[DateValue]].\n        let mut t = date.0;\n\n        // NOTE (nekevss): `downcast_ref` is used and then dropped for a short lived borrow.\n        // ToNumber() may call userland code which can modify the underlying date\n        // which will cause a panic. In order to avoid this, we drop the borrow,\n        // here and only `downcast_mut` when date will be modified.\n        drop(date);\n\n        // 4. Let h be ? ToNumber(hour).\n        let h = args.get_or_undefined(0).to_number(context)?;\n\n        // 5. If min is present, let m be ? ToNumber(min).\n        let m = args.get(1).map(|v| v.to_number(context)).transpose()?;\n\n        // 6. If sec is present, let s be ? ToNumber(sec).\n        let s = args.get(2).map(|v| v.to_number(context)).transpose()?;\n\n        // 7. If ms is present, let milli be ? ToNumber(ms).\n        let milli = args.get(3).map(|v| v.to_number(context)).transpose()?;\n\n        // 8. If t is NaN, return NaN.\n        if t.is_nan() {\n            return Ok(JsValue::from(f64::NAN));\n        }\n\n        if LOCAL {\n            // 9. Set t to LocalTime(t).\n            t = local_time(t, context.host_hooks().as_ref());\n        }\n\n        // 10. If min is not present, let m be MinFromTime(t).\n        let m: f64 = m.unwrap_or_else(|| min_from_time(t).into());\n\n        // 11. If sec is not present, let s be SecFromTime(t).\n        let s = s.unwrap_or_else(|| sec_from_time(t).into());\n\n        // 12. If ms is not present, let milli be msFromTime(t).\n        let milli = milli.unwrap_or_else(|| ms_from_time(t).into());\n\n        // 13. Let date be MakeDate(Day(t), MakeTime(h, m, s, milli)).\n        let date = make_date(day(t), make_time(h, m, s, milli));\n\n        let u = if LOCAL {\n            // 14. Let u be TimeClip(UTC(date)).\n            time_clip(utc_t(date, context.host_hooks().as_ref()))\n        } else {\n            // 14. Let u be TimeClip(date).\n            time_clip(date)\n        };\n\n        let object = this.as_object();\n        let mut date_mut = object\n            .as_ref()\n            .and_then(JsObject::downcast_mut::<Date>)\n            .ok_or_else(|| JsNativeError::typ().with_message(\"'this' is not a Date\"))?;\n\n        // 15. Set dateObject.[[DateValue]] to u.\n        date_mut.0 = u;\n\n        // 16. Return u.\n        Ok(JsValue::from(u))\n    }\n\n    /// [`Date.prototype.setMilliseconds ( ms )`[local] and\n    /// [`Date.prototype.setUTCMilliseconds ( ms )`][utc].\n    ///\n    /// The `setMilliseconds()` method sets the milliseconds for a specified date according to local time.\n    ///\n    /// [local]: https://tc39.es/ecma262/#sec-date.prototype.setmilliseconds\n    /// [utc]: https://tc39.es/ecma262/#sec-date.prototype.setutcmilliseconds\n    pub(crate) fn set_milliseconds<const LOCAL: bool>(\n        this: &JsValue,\n        args: &[JsValue],\n        context: &mut Context,\n    ) -> JsResult<JsValue> {\n        // 1. Let dateObject be the this value.\n        // 2. Perform ? RequireInternalSlot(dateObject, [[DateValue]]).\n        let object = this.as_object();\n        let date = object\n            .as_ref()\n            .and_then(JsObject::downcast_ref::<Date>)\n            .ok_or_else(|| JsNativeError::typ().with_message(\"'this' is not a Date\"))?;\n\n        // 3. Let t be dateObject.[[DateValue]].\n        let mut t = date.0;\n\n        // NOTE (nekevss): `downcast_ref` is used and then dropped for a short lived borrow.\n        // ToNumber() may call userland code which can modify the underlying date\n        // which will cause a panic. In order to avoid this, we drop the borrow,\n        // here and only `downcast_mut` when date will be modified.\n        drop(date);\n\n        // 4. Set ms to ? ToNumber(ms).\n        let ms = args.get_or_undefined(0).to_number(context)?;\n\n        // 5. If t is NaN, return NaN.\n        if t.is_nan() {\n            return Ok(JsValue::from(f64::NAN));\n        }\n\n        if LOCAL {\n            // 6. Set t to LocalTime(t).\n            t = local_time(t, context.host_hooks().as_ref());\n        }\n\n        // 7. Let time be MakeTime(HourFromTime(t), MinFromTime(t), SecFromTime(t), ms).\n        let time = make_time(\n            hour_from_time(t).into(),\n            min_from_time(t).into(),\n            sec_from_time(t).into(),\n            ms,\n        );\n\n        let u = if LOCAL {\n            // 8. Let u be TimeClip(UTC(MakeDate(Day(t), time))).\n            time_clip(utc_t(\n                make_date(day(t), time),\n                context.host_hooks().as_ref(),\n            ))\n        } else {\n            // 8. Let u be TimeClip(MakeDate(Day(t), time)).\n            time_clip(make_date(day(t), time))\n        };\n\n        let object = this.as_object();\n        let mut date_mut = object\n            .as_ref()\n            .and_then(JsObject::downcast_mut::<Date>)\n            .ok_or_else(|| JsNativeError::typ().with_message(\"'this' is not a Date\"))?;\n\n        // 9. Set dateObject.[[DateValue]] to u.\n        date_mut.0 = u;\n\n        // 10. Return u.\n        Ok(JsValue::from(u))\n    }\n\n    /// [`Date.prototype.setMinutes ( min [ , sec [ , ms ] ] )`][local] and\n    /// [`Date.prototype.setUTCMinutes ( min [ , sec [ , ms ] ] )`][utc].\n    ///\n    /// The `setMinutes()` method sets the minutes for a specified date.\n    ///\n    /// [local]: https://tc39.es/ecma262/#sec-date.prototype.setminutes\n    /// [utc]: https://tc39.es/ecma262/#sec-date.prototype.setutcminutes\n    pub(crate) fn set_minutes<const LOCAL: bool>(\n        this: &JsValue,\n        args: &[JsValue],\n        context: &mut Context,\n    ) -> JsResult<JsValue> {\n        // 1. Let dateObject be the this value.\n        // 2. Perform ? RequireInternalSlot(dateObject, [[DateValue]]).\n        let object = this.as_object();\n        let date = object\n            .as_ref()\n            .and_then(JsObject::downcast_ref::<Date>)\n            .ok_or_else(|| JsNativeError::typ().with_message(\"'this' is not a Date\"))?;\n\n        // 3. Let t be dateObject.[[DateValue]].\n        let mut t = date.0;\n\n        // NOTE (nekevss): `downcast_ref` is used and then dropped for a short lived borrow.\n        // ToNumber() may call userland code which can modify the underlying date\n        // which will cause a panic. In order to avoid this, we drop the borrow,\n        // here and only `downcast_mut` when date will be modified.\n        drop(date);\n\n        // 4. Let m be ? ToNumber(min).\n        let m = args.get_or_undefined(0).to_number(context)?;\n\n        // 5. If sec is present, let s be ? ToNumber(sec).\n        let s = args.get(1).map(|v| v.to_number(context)).transpose()?;\n\n        // 6. If ms is present, let milli be ? ToNumber(ms).\n        let milli = args.get(2).map(|v| v.to_number(context)).transpose()?;\n\n        // 7. If t is NaN, return NaN.\n        if t.is_nan() {\n            return Ok(JsValue::from(f64::NAN));\n        }\n\n        if LOCAL {\n            // 8. Set t to LocalTime(t).\n            t = local_time(t, context.host_hooks().as_ref());\n        }\n\n        // 9. If sec is not present, let s be SecFromTime(t).\n        let s = s.unwrap_or_else(|| sec_from_time(t).into());\n\n        // 10. If ms is not present, let milli be msFromTime(t).\n        let milli = milli.unwrap_or_else(|| ms_from_time(t).into());\n\n        // 11. Let date be MakeDate(Day(t), MakeTime(HourFromTime(t), m, s, milli)).\n        let date = make_date(day(t), make_time(hour_from_time(t).into(), m, s, milli));\n\n        let u = if LOCAL {\n            // 12. Let u be TimeClip(UTC(date)).\n            time_clip(utc_t(date, context.host_hooks().as_ref()))\n        } else {\n            // 12. Let u be TimeClip(date).\n            time_clip(date)\n        };\n\n        let object = this.as_object();\n        let mut date_mut = object\n            .as_ref()\n            .and_then(JsObject::downcast_mut::<Date>)\n            .ok_or_else(|| JsNativeError::typ().with_message(\"'this' is not a Date\"))?;\n\n        // 13. Set dateObject.[[DateValue]] to u.\n        date_mut.0 = u;\n\n        // 14. Return u.\n        Ok(JsValue::from(u))\n    }\n\n    /// [`Date.prototype.setMonth ( month [ , date ] )`][local] and\n    /// [`Date.prototype.setUTCMonth ( month [ , date ] )`][utc].\n    ///\n    /// The `setMonth()` method sets the month for a specified date according to the currently set\n    /// year.\n    ///\n    /// [local]: https://tc39.es/ecma262/#sec-date.prototype.setmonth\n    /// [utc]: https://tc39.es/ecma262/#sec-date.prototype.setutcmonth\n    pub(crate) fn set_month<const LOCAL: bool>(\n        this: &JsValue,\n        args: &[JsValue],\n        context: &mut Context,\n    ) -> JsResult<JsValue> {\n        // 1. Let dateObject be the this value.\n        // 2. Perform ? RequireInternalSlot(dateObject, [[DateValue]]).\n        let object = this.as_object();\n        let date = object\n            .as_ref()\n            .and_then(JsObject::downcast_ref::<Date>)\n            .ok_or_else(|| JsNativeError::typ().with_message(\"'this' is not a Date\"))?;\n\n        // 3. Let t be dateObject.[[DateValue]].\n        let mut t = date.0;\n\n        // NOTE (nekevss): `downcast_ref` is used and then dropped for a short lived borrow.\n        // ToNumber() may call userland code which can modify the underlying date\n        // which will cause a panic. In order to avoid this, we drop the borrow,\n        // here and only `downcast_mut` when date will be modified.\n        drop(date);\n\n        // 4. Let m be ? ToNumber(month).\n        let m = args.get_or_undefined(0).to_number(context)?;\n\n        // 5. If date is present, let dt be ? ToNumber(date).\n        let dt = args.get(1).map(|v| v.to_number(context)).transpose()?;\n\n        // 6. If t is NaN, return NaN.\n        if t.is_nan() {\n            return Ok(JsValue::from(f64::NAN));\n        }\n\n        // 7. Set t to LocalTime(t).\n        if LOCAL {\n            t = local_time(t, context.host_hooks().as_ref());\n        }\n\n        // 8. If date is not present, let dt be DateFromTime(t).\n        let dt = dt.unwrap_or_else(|| date_from_time(t).into());\n\n        // 9. Let newDate be MakeDate(MakeDay(YearFromTime(t), m, dt), TimeWithinDay(t)).\n        let new_date = make_date(\n            make_day(year_from_time(t).into(), m, dt),\n            time_within_day(t),\n        );\n\n        let u = if LOCAL {\n            // 10. Let u be TimeClip(UTC(newDate)).\n            time_clip(utc_t(new_date, context.host_hooks().as_ref()))\n        } else {\n            // 10. Let u be TimeClip(newDate).\n            time_clip(new_date)\n        };\n\n        let object = this.as_object();\n        let mut date_mut = object\n            .as_ref()\n            .and_then(JsObject::downcast_mut::<Date>)\n            .ok_or_else(|| JsNativeError::typ().with_message(\"'this' is not a Date\"))?;\n\n        // 11. Set dateObject.[[DateValue]] to u.\n        date_mut.0 = u;\n\n        // 12. Return u.\n        Ok(JsValue::from(u))\n    }\n\n    /// [`Date.prototype.setSeconds ( sec [ , ms ] )`[local] and\n    /// [`Date.prototype.setUTCSeconds ( sec [ , ms ] )`][utc].\n    ///\n    /// The `setSeconds()` method sets the seconds for a specified date.\n    ///\n    /// [local]: https://tc39.es/ecma262/#sec-date.prototype.setseconds\n    /// [utc]: https://tc39.es/ecma262/#sec-date.prototype.setutcseconds\n    pub(crate) fn set_seconds<const LOCAL: bool>(\n        this: &JsValue,\n        args: &[JsValue],\n        context: &mut Context,\n    ) -> JsResult<JsValue> {\n        // 1. Let dateObject be the this value.\n        // 2. Perform ? RequireInternalSlot(dateObject, [[DateValue]]).\n        let object = this.as_object();\n        let date = object\n            .as_ref()\n            .and_then(JsObject::downcast_ref::<Date>)\n            .ok_or_else(|| JsNativeError::typ().with_message(\"'this' is not a Date\"))?;\n\n        // 3. Let t be dateObject.[[DateValue]].\n        let mut t = date.0;\n\n        // NOTE (nekevss): `downcast_ref` is used and then dropped for a short lived borrow.\n        // ToNumber() may call userland code which can modify the underlying date\n        // which will cause a panic. In order to avoid this, we drop the borrow,\n        // here and only `downcast_mut` when date will be modified.\n        drop(date);\n\n        // 4. Let s be ? ToNumber(sec).\n        let s = args.get_or_undefined(0).to_number(context)?;\n\n        // 5. If ms is present, let milli be ? ToNumber(ms).\n        let milli = args.get(1).map(|v| v.to_number(context)).transpose()?;\n\n        // 6. If t is NaN, return NaN.\n        if t.is_nan() {\n            return Ok(JsValue::from(f64::NAN));\n        }\n\n        // 7. Set t to LocalTime(t).\n        if LOCAL {\n            t = local_time(t, context.host_hooks().as_ref());\n        }\n\n        // 8. If ms is not present, let milli be msFromTime(t).\n        let milli = milli.unwrap_or_else(|| ms_from_time(t).into());\n\n        // 9. Let date be MakeDate(Day(t), MakeTime(HourFromTime(t), MinFromTime(t), s, milli)).\n        let date = make_date(\n            day(t),\n            make_time(hour_from_time(t).into(), min_from_time(t).into(), s, milli),\n        );\n\n        let u = if LOCAL {\n            // 10. Let u be TimeClip(UTC(date)).\n            time_clip(utc_t(date, context.host_hooks().as_ref()))\n        } else {\n            // 10. Let u be TimeClip(date).\n            time_clip(date)\n        };\n\n        let object = this.as_object();\n        let mut date_mut = object\n            .as_ref()\n            .and_then(JsObject::downcast_mut::<Date>)\n            .ok_or_else(|| JsNativeError::typ().with_message(\"'this' is not a Date\"))?;\n\n        // 11. Set dateObject.[[DateValue]] to u.\n        date_mut.0 = u;\n\n        // 12. Return u.\n        Ok(JsValue::from(u))\n    }\n\n    /// [`Date.prototype.setYear()`][spec].\n    ///\n    /// The `setYear()` method sets the year for a specified date according to local time.\n    ///\n    /// # Note\n    ///\n    /// The [`Self::set_full_year`] method is preferred for nearly all purposes, because it avoids\n    /// the “year 2000 problem.”\n    ///\n    /// More information:\n    ///  - [MDN documentation][mdn]\n    ///\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/setYear\n    /// [spec]: https://tc39.es/ecma262/#sec-date.prototype.setyear\n    pub(crate) fn set_year(\n        this: &JsValue,\n        args: &[JsValue],\n        context: &mut Context,\n    ) -> JsResult<JsValue> {\n        // 1. Let dateObject be the this value.\n        // 2. Perform ? RequireInternalSlot(dateObject, [[DateValue]]).\n        let object = this.as_object();\n        let date = object\n            .as_ref()\n            .and_then(JsObject::downcast_ref::<Date>)\n            .ok_or_else(|| JsNativeError::typ().with_message(\"'this' is not a Date\"))?;\n\n        // 3. Let t be dateObject.[[DateValue]].\n        let t = date.0;\n\n        // NOTE (nekevss): `downcast_ref` is used and then dropped for a short lived borrow.\n        // ToNumber() may call userland code which can modify the underlying date\n        // which will cause a panic. In order to avoid this, we drop the borrow,\n        // here and only `downcast_mut` when date will be modified.\n        drop(date);\n\n        // 4. Let y be ? ToNumber(year).\n        let y = args.get_or_undefined(0).to_number(context)?;\n\n        // 5. If t is NaN, set t to +0𝔽; otherwise, set t to LocalTime(t).\n        let t = if t.is_nan() {\n            0.0\n        } else {\n            local_time(t, context.host_hooks().as_ref())\n        };\n\n        // 6. Let yyyy be MakeFullYear(y).\n        let yyyy = make_full_year(y);\n\n        // 7. Let d be MakeDay(yyyy, MonthFromTime(t), DateFromTime(t)).\n        let d = make_day(yyyy, month_from_time(t).into(), date_from_time(t).into());\n\n        // 8. Let date be MakeDate(d, TimeWithinDay(t)).\n        let date = make_date(d, time_within_day(t));\n\n        // 9. Let u be TimeClip(UTC(date)).\n        let u = time_clip(utc_t(date, context.host_hooks().as_ref()));\n\n        let object = this.as_object();\n        let mut date_mut = object\n            .as_ref()\n            .and_then(JsObject::downcast_mut::<Date>)\n            .ok_or_else(|| JsNativeError::typ().with_message(\"'this' is not a Date\"))?;\n\n        // 10. Set dateObject.[[DateValue]] to u.\n        date_mut.0 = u;\n\n        // 11. Return u.\n        Ok(JsValue::from(u))\n    }\n\n    /// [`Date.prototype.setTime()`][spec].\n    ///\n    /// The `setTime()` method sets the Date object to the time represented by a number of milliseconds\n    /// since January 1, 1970, 00:00:00 UTC.\n    ///\n    /// More information:\n    ///  - [MDN documentation][mdn]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-date.prototype.settime\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/setTime\n    pub(crate) fn set_time(\n        this: &JsValue,\n        args: &[JsValue],\n        context: &mut Context,\n    ) -> JsResult<JsValue> {\n        // 1. Let dateObject be the this value.\n        // 2. Perform ? RequireInternalSlot(dateObject, [[DateValue]]).\n        let object = this.as_object();\n        let date = object\n            .as_ref()\n            .and_then(JsObject::downcast_ref::<Date>)\n            .ok_or_else(|| JsNativeError::typ().with_message(\"'this' is not a Date\"))?;\n\n        // 3. Let t be ? ToNumber(time).\n        let t = args.get_or_undefined(0).to_number(context)?;\n\n        // NOTE (nekevss): `downcast_ref` is used and then dropped for a short lived borrow.\n        // ToNumber() may call userland code which can modify the underlying date\n        // which will cause a panic. In order to avoid this, we drop the borrow,\n        // here and only `downcast_mut` when date will be modified.\n        drop(date);\n\n        // 4. Let v be TimeClip(t).\n        let v = time_clip(t);\n\n        let object = this.as_object();\n        let mut date_mut = object\n            .as_ref()\n            .and_then(JsObject::downcast_mut::<Date>)\n            .ok_or_else(|| JsNativeError::typ().with_message(\"'this' is not a Date\"))?;\n\n        // 5. Set dateObject.[[DateValue]] to v.\n        date_mut.0 = v;\n\n        // 6. Return v.\n        Ok(JsValue::from(v))\n    }\n\n    /// [`Date.prototype.toDateString()`][spec].\n    ///\n    /// The `toDateString()` method returns the date portion of a Date object in English.\n    ///\n    /// More information:\n    ///  - [MDN documentation][mdn]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-date.prototype.todatestring\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/toDateString\n    pub(crate) fn to_date_string(\n        this: &JsValue,\n        _: &[JsValue],\n        context: &mut Context,\n    ) -> JsResult<JsValue> {\n        // 1. Let dateObject be the this value.\n        // 2. Perform ? RequireInternalSlot(dateObject, [[DateValue]]).\n        // 3. Let tv be dateObject.[[DateValue]].\n        let tv = this\n            .as_object()\n            .and_then(|obj| obj.downcast_ref::<Date>().as_deref().copied())\n            .ok_or_else(|| JsNativeError::typ().with_message(\"'this' is not a Date\"))?\n            .0;\n\n        // 4. If tv is NaN, return \"Invalid Date\".\n        if tv.is_nan() {\n            return Ok(js_string!(\"Invalid Date\").into());\n        }\n\n        // 5. Let t be LocalTime(tv).\n        let t = local_time(tv, context.host_hooks().as_ref());\n\n        // 6. Return DateString(t).\n        Ok(JsValue::from(date_string(t)))\n    }\n\n    /// [`Date.prototype.toISOString()`][spec].\n    ///\n    /// The `toISOString()` method returns a string in simplified extended ISO format\n    /// ([ISO 8601][iso8601]).\n    ///\n    /// More information:\n    ///  - [MDN documentation][mdn]\n    ///\n    /// [iso8601]: http://en.wikipedia.org/wiki/ISO_8601\n    /// [spec]: https://tc39.es/ecma262/#sec-date.prototype.toisostring\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/toISOString\n    pub(crate) fn to_iso_string(\n        this: &JsValue,\n        _: &[JsValue],\n        _: &mut Context,\n    ) -> JsResult<JsValue> {\n        // 1. Let dateObject be the this value.\n        // 2. Perform ? RequireInternalSlot(dateObject, [[DateValue]]).\n        // 3. Let tv be dateObject.[[DateValue]].\n        let tv = this\n            .as_object()\n            .and_then(|obj| obj.downcast_ref::<Date>().as_deref().copied())\n            .ok_or_else(|| JsNativeError::typ().with_message(\"'this' is not a Date\"))?\n            .0;\n\n        // 4. If tv is not finite, throw a RangeError exception.\n        if !tv.is_finite() {\n            return Err(JsNativeError::range()\n                .with_message(\"Invalid time value\")\n                .into());\n        }\n\n        // 5. If tv corresponds with a year that cannot be represented in the Date Time String Format, throw a RangeError exception.\n        // 6. Return a String representation of tv in the Date Time String Format on the UTC time scale,\n        //    including all format elements and the UTC offset representation \"Z\".\n        let year = year_from_time(tv);\n        let year = if year >= 10000 {\n            js_string!(js_str!(\"+\"), pad_six(year.unsigned_abs(), &mut [0; 6]))\n        } else if year >= 0 {\n            pad_four(year.unsigned_abs(), &mut [0; 4]).into()\n        } else {\n            js_string!(js_str!(\"-\"), pad_six(year.unsigned_abs(), &mut [0; 6]))\n        };\n        let mut binding = [0; 2];\n        let month = pad_two(month_from_time(tv) + 1, &mut binding);\n        let mut binding = [0; 2];\n        let day = pad_two(date_from_time(tv), &mut binding);\n        let mut binding = [0; 2];\n        let hour = pad_two(hour_from_time(tv), &mut binding);\n        let mut binding = [0; 2];\n        let minute = pad_two(min_from_time(tv), &mut binding);\n        let mut binding = [0; 2];\n        let second = pad_two(sec_from_time(tv), &mut binding);\n        let mut binding = [0; 3];\n        let millisecond = pad_three(ms_from_time(tv), &mut binding);\n\n        Ok(JsValue::from(js_string!(\n            &year,\n            js_str!(\"-\"),\n            month,\n            js_str!(\"-\"),\n            day,\n            js_str!(\"T\"),\n            hour,\n            js_str!(\":\"),\n            minute,\n            js_str!(\":\"),\n            second,\n            js_str!(\".\"),\n            millisecond,\n            js_str!(\"Z\")\n        )))\n    }\n\n    /// [`Date.prototype.toJSON()`][spec].\n    ///\n    /// The `toJSON()` method returns a string representation of the `Date` object.\n    ///\n    /// More information:\n    ///  - [MDN documentation][mdn]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-date.prototype.tojson\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/toJSON\n    pub(crate) fn to_json(\n        this: &JsValue,\n        _: &[JsValue],\n        context: &mut Context,\n    ) -> JsResult<JsValue> {\n        // 1. Let O be ? ToObject(this value).\n        let o = this.to_object(context)?;\n\n        // 2. Let tv be ? ToPrimitive(O, number).\n        let tv = this.to_primitive(context, PreferredType::Number)?;\n\n        // 3. If Type(tv) is Number and tv is not finite, return null.\n        if tv.as_number().is_some_and(|x| !f64::is_finite(x)) {\n            return Ok(JsValue::null());\n        }\n\n        // 4. Return ? Invoke(O, \"toISOString\").\n        let func = o.get(js_string!(\"toISOString\"), context)?;\n        func.call(this, &[], context)\n    }\n\n    /// [`Date.prototype.toLocaleDateString()`][spec].\n    ///\n    /// The `toLocaleDateString()` method returns the date portion of the given Date instance according\n    /// to language-specific conventions.\n    ///\n    /// More information:\n    ///  - [MDN documentation][mdn]\n    ///\n    /// [spec]: https://tc39.es/ecma402/#sup-date.prototype.tolocaledatestring\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/toLocaleDateString\n    #[allow(\n        unused_variables,\n        reason = \"`args` and `context` are used when the `intl` feature is enabled\"\n    )]\n    pub(crate) fn to_locale_date_string(\n        this: &JsValue,\n        args: &[JsValue],\n        context: &mut Context,\n    ) -> JsResult<JsValue> {\n        #[cfg(feature = \"intl\")]\n        {\n            use crate::builtins::intl::date_time_format::{\n                FormatDefaults, FormatType, format_date_time_locale,\n            };\n            // 1. Let dateObject be the this value.\n            // 2. Perform ? RequireInternalSlot(dateObject, [[DateValue]]).\n            // 3. Let x be dateObject.[[DateValue]].\n            let t = this\n                .as_object()\n                .and_then(|obj| obj.downcast_ref::<Date>().as_deref().copied())\n                .ok_or_else(|| JsNativeError::typ().with_message(\"'this' is not a Date\"))?\n                .0;\n            // 4. If x is NaN, return \"Invalid Date\".\n            if t.is_nan() {\n                return Ok(JsValue::new(js_string!(\"Invalid Date\")));\n            }\n            // 5. Let dateFormat be ? CreateDateTimeFormat(%Intl.DateTimeFormat%, locales, options, date, date).\n            // 6. Return ! FormatDateTime(dateFormat, x).\n            let locales = args.get_or_undefined(0);\n            let options = args.get_or_undefined(1);\n            format_date_time_locale(\n                locales,\n                options,\n                FormatType::Date,\n                FormatDefaults::Date,\n                t,\n                context,\n            )\n        }\n        #[cfg(not(feature = \"intl\"))]\n        {\n            Self::to_string(this, &[], context)\n        }\n    }\n\n    /// [`Date.prototype.toLocaleString()`][spec].\n    ///\n    /// The `toLocaleString()` method returns a string representing the specified Date object.\n    ///\n    /// More information:\n    ///  - [MDN documentation][mdn]\n    ///\n    /// [spec]: https://tc39.es/ecma402/#sup-date.prototype.tolocalestring\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/toLocaleString\n    #[allow(\n        unused_variables,\n        reason = \"`args` and `context` are used when the `intl` feature is enabled\"\n    )]\n    pub(crate) fn to_locale_string(\n        this: &JsValue,\n        args: &[JsValue],\n        context: &mut Context,\n    ) -> JsResult<JsValue> {\n        #[cfg(feature = \"intl\")]\n        {\n            use crate::builtins::intl::date_time_format::{\n                FormatDefaults, FormatType, format_date_time_locale,\n            };\n            // 1. Let dateObject be the this value.\n            // 2. Perform ? RequireInternalSlot(dateObject, [[DateValue]]).\n            // 3. Let x be dateObject.[[DateValue]].\n            let t = this\n                .as_object()\n                .and_then(|obj| obj.downcast_ref::<Date>().as_deref().copied())\n                .ok_or_else(|| JsNativeError::typ().with_message(\"'this' is not a Date\"))?\n                .0;\n            // 4. If x is NaN, return \"Invalid Date\".\n            if t.is_nan() {\n                return Ok(JsValue::new(js_string!(\"Invalid Date\")));\n            }\n            // 5. Let dateFormat be ? CreateDateTimeFormat(%Intl.DateTimeFormat%, locales, options, any, all).\n            // 6. Return ! FormatDateTime(dateFormat, x).\n            let locales = args.get_or_undefined(0);\n            let options = args.get_or_undefined(1);\n            format_date_time_locale(\n                locales,\n                options,\n                FormatType::Any,\n                FormatDefaults::All,\n                t,\n                context,\n            )\n        }\n        #[cfg(not(feature = \"intl\"))]\n        {\n            Self::to_string(this, &[], context)\n        }\n    }\n\n    /// [`Date.prototype.toLocaleTimeString()`][spec].\n    ///\n    /// The `toLocaleTimeString()` method returns the time portion of a Date object in human readable\n    /// form in American English.\n    ///\n    /// More information:\n    ///  - [MDN documentation][mdn]\n    ///\n    /// [spec]: https://tc39.es/ecma402/#sup-date.prototype.tolocaletimestring\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/toLocaleTimeString\n    #[allow(\n        unused_variables,\n        reason = \"`args` and `context` are used when the `intl` feature is enabled\"\n    )]\n    pub(crate) fn to_locale_time_string(\n        this: &JsValue,\n        args: &[JsValue],\n        context: &mut Context,\n    ) -> JsResult<JsValue> {\n        #[cfg(feature = \"intl\")]\n        {\n            use crate::builtins::intl::date_time_format::{\n                FormatDefaults, FormatType, format_date_time_locale,\n            };\n            // 1. Let dateObject be the this value.\n            // 2. Perform ? RequireInternalSlot(dateObject, [[DateValue]]).\n            // 3. Let x be dateObject.[[DateValue]].\n            let t = this\n                .as_object()\n                .and_then(|obj| obj.downcast_ref::<Date>().as_deref().copied())\n                .ok_or_else(|| JsNativeError::typ().with_message(\"'this' is not a Date\"))?\n                .0;\n            // 4. If x is NaN, return \"Invalid Date\".\n            if t.is_nan() {\n                return Ok(JsValue::new(js_string!(\"Invalid Date\")));\n            }\n            // 5. Let timeFormat be ? CreateDateTimeFormat(%Intl.DateTimeFormat%, locales, options, time, time).\n            // 6. Return ! FormatDateTime(timeFormat, x).\n            let locales = args.get_or_undefined(0);\n            let options = args.get_or_undefined(1);\n            format_date_time_locale(\n                locales,\n                options,\n                FormatType::Time,\n                FormatDefaults::Time,\n                t,\n                context,\n            )\n        }\n        #[cfg(not(feature = \"intl\"))]\n        {\n            Self::to_string(this, &[], context)\n        }\n    }\n\n    /// [`Date.prototype.toString()`][spec].\n    ///\n    /// The `toString()` method returns a string representing the specified Date object.\n    ///\n    /// More information:\n    ///  - [MDN documentation][mdn]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-date.prototype.tostring\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/toString\n    pub(crate) fn to_string(\n        this: &JsValue,\n        _: &[JsValue],\n        context: &mut Context,\n    ) -> JsResult<JsValue> {\n        // 1. Let dateObject be the this value.\n        // 2. Perform ? RequireInternalSlot(dateObject, [[DateValue]]).\n        // 3. Let tv be dateObject.[[DateValue]].\n        let tv = this\n            .as_object()\n            .and_then(|obj| obj.downcast_ref::<Date>().as_deref().copied())\n            .ok_or_else(|| JsNativeError::typ().with_message(\"'this' is not a Date\"))?\n            .0;\n\n        // 4. Return ToDateString(tv).\n        Ok(JsValue::from(to_date_string_t(\n            tv,\n            context.host_hooks().as_ref(),\n        )))\n    }\n\n    /// [`Date.prototype.toTimeString()`][spec].\n    ///\n    /// The `toTimeString()` method returns the time portion of a Date object in human readable form\n    /// in American English.\n    ///\n    /// More information:\n    ///  - [MDN documentation][mdn]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-date.prototype.totimestring\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/toTimeString\n    pub(crate) fn to_time_string(\n        this: &JsValue,\n        _: &[JsValue],\n        context: &mut Context,\n    ) -> JsResult<JsValue> {\n        // 1. Let dateObject be the this value.\n        // 2. Perform ? RequireInternalSlot(dateObject, [[DateValue]]).\n        // 3. Let tv be dateObject.[[DateValue]].\n        let tv = this\n            .as_object()\n            .and_then(|obj| obj.downcast_ref::<Date>().as_deref().copied())\n            .ok_or_else(|| JsNativeError::typ().with_message(\"'this' is not a Date\"))?\n            .0;\n\n        // 4. If tv is NaN, return \"Invalid Date\".\n        if tv.is_nan() {\n            return Ok(js_string!(\"Invalid Date\").into());\n        }\n\n        // 5. Let t be LocalTime(tv).\n        let t = local_time(tv, context.host_hooks().as_ref());\n\n        // 6. Return the string-concatenation of TimeString(t) and TimeZoneString(tv).\n        Ok(JsValue::from(js_string!(\n            &time_string(t),\n            &time_zone_string(t, context.host_hooks().as_ref())\n        )))\n    }\n\n    /// [`Date.prototype.toUTCString()`][spec].\n    ///\n    /// The `toUTCString()` method returns a string representing the specified Date object.\n    ///\n    /// More information:\n    ///  - [MDN documentation][mdn]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-date.prototype.toutcstring\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/toUTCString\n    pub(crate) fn to_utc_string(\n        this: &JsValue,\n        _args: &[JsValue],\n        _context: &mut Context,\n    ) -> JsResult<JsValue> {\n        // 1. Let dateObject be the this value.\n        // 2. Perform ? RequireInternalSlot(dateObject, [[DateValue]]).\n        // 3. Let tv be dateObject.[[DateValue]].\n        let tv = this\n            .as_object()\n            .and_then(|obj| obj.downcast_ref::<Date>().as_deref().copied())\n            .ok_or_else(|| JsNativeError::typ().with_message(\"'this' is not a Date\"))?\n            .0;\n\n        // 4. If tv is NaN, return \"Invalid Date\".\n        if tv.is_nan() {\n            return Ok(js_string!(\"Invalid Date\").into());\n        }\n\n        // 5. Let weekday be the Name of the entry in Table 63 with the Number WeekDay(tv).\n        let weekday = match week_day(tv) {\n            0 => js_str!(\"Sun\"),\n            1 => js_str!(\"Mon\"),\n            2 => js_str!(\"Tue\"),\n            3 => js_str!(\"Wed\"),\n            4 => js_str!(\"Thu\"),\n            5 => js_str!(\"Fri\"),\n            6 => js_str!(\"Sat\"),\n            _ => unreachable!(),\n        };\n\n        // 6. Let month be the Name of the entry in Table 64 with the Number MonthFromTime(tv).\n        let month = match month_from_time(tv) {\n            0 => js_str!(\"Jan\"),\n            1 => js_str!(\"Feb\"),\n            2 => js_str!(\"Mar\"),\n            3 => js_str!(\"Apr\"),\n            4 => js_str!(\"May\"),\n            5 => js_str!(\"Jun\"),\n            6 => js_str!(\"Jul\"),\n            7 => js_str!(\"Aug\"),\n            8 => js_str!(\"Sep\"),\n            9 => js_str!(\"Oct\"),\n            10 => js_str!(\"Nov\"),\n            11 => js_str!(\"Dec\"),\n            _ => unreachable!(),\n        };\n\n        // 7. Let day be ToZeroPaddedDecimalString(ℝ(DateFromTime(tv)), 2).\n        let mut binding = [0; 2];\n        let day = pad_two(date_from_time(tv), &mut binding);\n\n        // 8. Let yv be YearFromTime(tv).\n        let yv = year_from_time(tv);\n\n        // 9. If yv is +0𝔽 or yv > +0𝔽, let yearSign be the empty String; otherwise, let yearSign be \"-\".\n        let year_sign = if yv >= 0 { js_str!(\"\") } else { js_str!(\"-\") };\n\n        // 10. Let paddedYear be ToZeroPaddedDecimalString(abs(ℝ(yv)), 4).\n        let yv = yv.unsigned_abs();\n        let padded_year: JsString = if yv >= 100_000 {\n            pad_six(yv, &mut [0; 6]).into()\n        } else if yv >= 10000 {\n            pad_five(yv, &mut [0; 5]).into()\n        } else {\n            pad_four(yv, &mut [0; 4]).into()\n        };\n\n        // 11. Return the string-concatenation of\n        // weekday,\n        // \",\",\n        // the code unit 0x0020 (SPACE),\n        // day,\n        // the code unit 0x0020 (SPACE),\n        // month,\n        // the code unit 0x0020 (SPACE),\n        // yearSign,\n        // paddedYear,\n        // the code unit 0x0020 (SPACE),\n        // and TimeString(tv).\n        Ok(JsValue::from(js_string!(\n            weekday,\n            js_str!(\",\"),\n            js_str!(\" \"),\n            day,\n            js_str!(\" \"),\n            month,\n            js_str!(\" \"),\n            year_sign,\n            &padded_year,\n            js_str!(\" \"),\n            &time_string(tv)\n        )))\n    }\n\n    /// [`Date.prototype.valueOf()`][spec].\n    ///\n    /// The `valueOf()` method returns the primitive value of a `Date` object.\n    ///\n    /// More information:\n    ///  - [MDN documentation][mdn]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-date.prototype.valueof\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/valueOf\n    pub(crate) fn value_of(\n        this: &JsValue,\n        _args: &[JsValue],\n        _context: &mut Context,\n    ) -> JsResult<JsValue> {\n        // 1. Let dateObject be the this value.\n        // 2. Perform ? RequireInternalSlot(dateObject, [[DateValue]]).\n        // 3. Return dateObject.[[DateValue]].\n        Ok(this\n            .as_object()\n            .and_then(|obj| obj.downcast_ref::<Date>().as_deref().copied())\n            .ok_or_else(|| JsNativeError::typ().with_message(\"'this' is not a Date\"))?\n            .0\n            .into())\n    }\n\n    /// [`Date.prototype [ @@toPrimitive ] ( hint )`][spec].\n    ///\n    /// The <code>\\[@@toPrimitive\\]()</code> method converts a Date object to a primitive value.\n    ///\n    /// More information:\n    ///  - [MDN documentation][mdn]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-date.prototype-@@toprimitive\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/@@toPrimitive\n    pub(crate) fn to_primitive(\n        this: &JsValue,\n        args: &[JsValue],\n        context: &mut Context,\n    ) -> JsResult<JsValue> {\n        // 1. Let O be the this value.\n        // 2. If Type(O) is not Object, throw a TypeError exception.\n        let o = this.as_object().ok_or_else(|| {\n            JsNativeError::typ().with_message(\"Date.prototype[@@toPrimitive] called on non object\")\n        })?;\n\n        let hint = args.get_or_undefined(0);\n\n        let try_first = match hint.as_string() {\n            // 3. If hint is \"string\" or \"default\", then\n            // a. Let tryFirst be string.\n            Some(string) if string == \"string\" || string == \"default\" => PreferredType::String,\n            // 4. Else if hint is \"number\", then\n            // a. Let tryFirst be number.\n            Some(number) if number == \"number\" => PreferredType::Number,\n            // 5. Else, throw a TypeError exception.\n            _ => {\n                return Err(JsNativeError::typ()\n                    .with_message(\"Date.prototype[@@toPrimitive] called with invalid hint\")\n                    .into());\n            }\n        };\n\n        // 6. Return ? OrdinaryToPrimitive(O, tryFirst).\n        o.ordinary_to_primitive(context, try_first)\n    }\n\n    /// 14.9.1 `Date.prototype.toTemporalInstant ()`\n    ///\n    /// Returns a JavaScript Date as a `Temporal.Instant`.\n    ///\n    /// More information:\n    ///  - [Temporal Proposal][spec]\n    ///  - [MDN documentation][mdn]\n    ///\n    /// [spec]: https://tc39.es/proposal-temporal/#sec-date.prototype.totemporalinstant\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/toTemporalInstant/\n    #[cfg(feature = \"temporal\")]\n    pub(crate) fn to_temporal_instant(\n        this: &JsValue,\n        _: &[JsValue],\n        context: &mut Context,\n    ) -> JsResult<JsValue> {\n        use crate::{builtins::temporal::create_temporal_instant, js_error};\n        use num_traits::FromPrimitive;\n        use temporal_rs::Instant;\n\n        const NS_PER_MILLISECOND: i128 = 1_000_000;\n        // 1. Let dateObject be the this value.\n        // 2. Perform ? RequireInternalSlot(dateObject, [[DateValue]]).\n        // 3. Let t be dateObject.[[DateValue]].\n        let t = this\n            .as_object()\n            .and_then(|obj| obj.downcast_ref::<Date>().as_deref().copied())\n            .ok_or_else(|| js_error!(TypeError: \"'this' is not a Date\"))?\n            .0;\n\n        // 4. Let ns be ? NumberToBigInt(t) × ℤ(10**6).\n        let ns = i128::from_f64(t)\n            .ok_or(js_error!(RangeError: \"[[DateValue]] is not an integral number.\"))?\n            * NS_PER_MILLISECOND;\n        // 5. Return ! CreateTemporalInstant(ns).\n        create_temporal_instant(Instant::try_new(ns)?, None, context)\n    }\n}\n"
  },
  {
    "path": "core/engine/src/builtins/date/tests.rs",
    "content": "use crate::{\n    JsNativeErrorKind, TestAction,\n    builtins::date::utils::fast_atoi::{process_4, process_8},\n    js_string, run_test_actions,\n};\nuse boa_macros::js_str;\nuse indoc::indoc;\nuse time::{OffsetDateTime, macros::format_description};\n\n// NOTE: Javascript Uses 0-based months, where time uses 1-based months.\n// Many of the assertions look wrong because of this.\n\nfn month_from_u8(month: u8) -> time::Month {\n    match month {\n        1 => time::Month::January,\n        2 => time::Month::February,\n        3 => time::Month::March,\n        4 => time::Month::April,\n        5 => time::Month::May,\n        6 => time::Month::June,\n        7 => time::Month::July,\n        8 => time::Month::August,\n        9 => time::Month::September,\n        10 => time::Month::October,\n        11 => time::Month::November,\n        12 => time::Month::December,\n        _ => unreachable!(),\n    }\n}\n\nfn from_local(\n    year: i32,\n    month: u8,\n    date: u8,\n    hour: u8,\n    minute: u8,\n    second: u8,\n    millisecond: u16,\n) -> OffsetDateTime {\n    let t = time::Date::from_calendar_date(year, month_from_u8(month), date)\n        .unwrap()\n        .with_hms_milli(hour, minute, second, millisecond)\n        .unwrap()\n        .assume_utc();\n    let offset = time::UtcOffset::local_offset_at(t).unwrap();\n    t.replace_offset(offset)\n}\n\nfn timestamp_from_local(\n    year: i32,\n    month: u8,\n    date: u8,\n    hour: u8,\n    minute: u8,\n    second: u8,\n    millisecond: u16,\n) -> i64 {\n    let t = from_local(year, month, date, hour, minute, second, millisecond);\n    t.unix_timestamp() * 1000 + i64::from(t.millisecond())\n}\n\nfn timestamp_from_utc(\n    year: i32,\n    month: u8,\n    date: u8,\n    hour: u8,\n    minute: u8,\n    second: u8,\n    millisecond: u16,\n) -> i64 {\n    let t = time::Date::from_calendar_date(year, month_from_u8(month), date)\n        .unwrap()\n        .with_hms_milli(hour, minute, second, millisecond)\n        .unwrap()\n        .assume_utc();\n    t.unix_timestamp() * 1000 + i64::from(t.millisecond())\n}\n\n#[test]\nfn parse_ascii_digits() {\n    let parse_8_ascii_digits = |val: &[u8; 8], len: usize| -> u64 {\n        let val = u64::from_le_bytes(*val);\n        process_8(val, len)\n    };\n    assert_eq!(12_345_678, parse_8_ascii_digits(b\"12345678\", 8));\n    assert_eq!(123_456, parse_8_ascii_digits(b\"123456xx\", 6));\n    assert_eq!(123, parse_8_ascii_digits(b\"123xxxxx\", 3));\n    assert_eq!(123, parse_8_ascii_digits(b\"000123xx\", 6));\n    assert_eq!(0, parse_8_ascii_digits(b\"00000000\", 8));\n    let parse_4_ascii_digits = |val: &[u8; 4], len: usize| -> u64 {\n        let val = u32::from_le_bytes(*val);\n        u64::from(process_4(val, len))\n    };\n    assert_eq!(1234, parse_4_ascii_digits(b\"1234\", 4));\n    assert_eq!(12, parse_4_ascii_digits(b\"12xx\", 2));\n    assert_eq!(3, parse_4_ascii_digits(b\"003x\", 3));\n    assert_eq!(23, parse_4_ascii_digits(b\"023x\", 3));\n    assert_eq!(0, parse_4_ascii_digits(b\"0000\", 4));\n}\n\n#[test]\nfn date_this_time_value() {\n    run_test_actions([TestAction::assert_native_error(\n        \"({toString: Date.prototype.toString}).toString()\",\n        JsNativeErrorKind::Type,\n        \"'this' is not a Date\",\n    )]);\n}\n\n#[test]\nfn date_ctor_call() {\n    run_test_actions([\n        TestAction::run(\"let a = new Date()\"),\n        TestAction::inspect_context(|_| std::thread::sleep(std::time::Duration::from_millis(1))),\n        TestAction::assert(\"a.getTime() != new Date().getTime()\"),\n    ]);\n}\n\n#[test]\nfn date_ctor_call_string() {\n    run_test_actions([TestAction::assert_eq(\n        \"new Date('2020-06-08T09:16:15.779-06:30').getTime()\",\n        timestamp_from_utc(2020, 6, 8, 15, 46, 15, 779),\n    )]);\n}\n\n#[test]\nfn date_ctor_call_string_invalid() {\n    run_test_actions([TestAction::assert_eq(\n        \"new Date('nope').getTime()\",\n        f64::NAN,\n    )]);\n}\n\n#[test]\nfn date_ctor_call_number() {\n    run_test_actions([TestAction::assert_eq(\n        \"new Date(1594199775779).getTime()\",\n        timestamp_from_utc(2020, 7, 8, 9, 16, 15, 779),\n    )]);\n}\n\n#[test]\nfn date_ctor_call_date() {\n    run_test_actions([TestAction::assert_eq(\n        \"new Date(new Date(1594199775779)).getTime()\",\n        timestamp_from_utc(2020, 7, 8, 9, 16, 15, 779),\n    )]);\n}\n\n#[test]\nfn date_ctor_call_multiple() {\n    run_test_actions([TestAction::assert_eq(\n        \"new Date(2020, 6, 8, 9, 16, 15, 779).getTime()\",\n        timestamp_from_local(2020, 7, 8, 9, 16, 15, 779),\n    )]);\n}\n\n#[test]\nfn date_ctor_call_multiple_90s() {\n    run_test_actions([TestAction::assert_eq(\n        \"new Date(99, 6, 8, 9, 16, 15, 779).getTime()\",\n        timestamp_from_local(1999, 7, 8, 9, 16, 15, 779),\n    )]);\n}\n\n#[test]\nfn date_ctor_call_multiple_nan() {\n    run_test_actions([\n        TestAction::assert_eq(\"new Date(1/0, 6, 8, 9, 16, 15, 779).getTime()\", f64::NAN),\n        TestAction::assert_eq(\"new Date(2020, 1/0, 8, 9, 16, 15, 779).getTime()\", f64::NAN),\n        TestAction::assert_eq(\"new Date(2020, 6, 1/0, 9, 16, 15, 779).getTime()\", f64::NAN),\n        TestAction::assert_eq(\"new Date(2020, 6, 8, 1/0, 16, 15, 779).getTime()\", f64::NAN),\n        TestAction::assert_eq(\"new Date(2020, 6, 8, 9, 1/0, 15, 779).getTime()\", f64::NAN),\n        TestAction::assert_eq(\"new Date(2020, 6, 8, 9, 16, 1/0, 779).getTime()\", f64::NAN),\n        TestAction::assert_eq(\"new Date(2020, 6, 8, 9, 16, 15, 1/0).getTime()\", f64::NAN),\n    ]);\n}\n\n#[test]\nfn date_ctor_now_call() {\n    run_test_actions([\n        TestAction::run(\"let a = Date.now()\"),\n        TestAction::inspect_context(|_| std::thread::sleep(std::time::Duration::from_millis(1))),\n        TestAction::assert(\"a != Date.now()\"),\n    ]);\n}\n\n#[test]\nfn date_ctor_parse_call() {\n    run_test_actions([TestAction::assert_eq(\n        \"Date.parse('2020-06-08T09:16:15.779-07:30')\",\n        1_591_634_775_779_i64,\n    )]);\n}\n\n#[test]\nfn date_ctor_utc_call() {\n    run_test_actions([TestAction::assert_eq(\n        \"Date.UTC(2020, 6, 8, 9, 16, 15, 779)\",\n        1_594_199_775_779_i64,\n    )]);\n}\n\n#[test]\nfn date_ctor_utc_call_nan() {\n    run_test_actions([\n        TestAction::assert_eq(\"Date.UTC(1/0, 6, 8, 9, 16, 15, 779)\", f64::NAN),\n        TestAction::assert_eq(\"Date.UTC(2020, 1/0, 8, 9, 16, 15, 779)\", f64::NAN),\n        TestAction::assert_eq(\"Date.UTC(2020, 6, 1/0, 9, 16, 15, 779)\", f64::NAN),\n        TestAction::assert_eq(\"Date.UTC(2020, 6, 8, 1/0, 16, 15, 779)\", f64::NAN),\n        TestAction::assert_eq(\"Date.UTC(2020, 6, 8, 9, 1/0, 15, 779)\", f64::NAN),\n        TestAction::assert_eq(\"Date.UTC(2020, 6, 8, 9, 16, 1/0, 779)\", f64::NAN),\n        TestAction::assert_eq(\"Date.UTC(2020, 6, 8, 9, 16, 15, 1/0)\", f64::NAN),\n    ]);\n}\n\n#[test]\nfn date_proto_get_date_call() {\n    run_test_actions([\n        TestAction::assert_eq(\"new Date(2020, 6, 8, 9, 16, 15, 779).getDate()\", 8),\n        TestAction::assert_eq(\"new Date(1/0).getDate()\", f64::NAN),\n    ]);\n}\n\n#[test]\nfn date_proto_get_day_call() {\n    run_test_actions([\n        TestAction::assert_eq(\"new Date(2020, 6, 8, 9, 16, 15, 779).getDay()\", 3),\n        TestAction::assert_eq(\"new Date(1/0).getDay()\", f64::NAN),\n    ]);\n}\n\n#[test]\nfn date_proto_get_full_year_call() {\n    run_test_actions([\n        TestAction::assert_eq(\"new Date(2020, 6, 8, 9, 16, 15, 779).getFullYear()\", 2020),\n        TestAction::assert_eq(\"new Date(1/0).getFullYear()\", f64::NAN),\n    ]);\n}\n\n#[test]\nfn date_proto_get_hours_call() {\n    run_test_actions([\n        TestAction::assert_eq(\"new Date(2020, 6, 8, 9, 16, 15, 779).getHours()\", 9),\n        TestAction::assert_eq(\"new Date(1/0).getHours()\", f64::NAN),\n    ]);\n}\n\n#[test]\nfn date_proto_get_milliseconds_call() {\n    run_test_actions([\n        TestAction::assert_eq(\n            \"new Date(2020, 6, 8, 9, 16, 15, 779).getMilliseconds()\",\n            779,\n        ),\n        TestAction::assert_eq(\"new Date(1/0).getMilliseconds()\", f64::NAN),\n    ]);\n}\n\n#[test]\nfn date_proto_get_minutes_call() {\n    run_test_actions([\n        TestAction::assert_eq(\"new Date(2020, 6, 8, 9, 16, 15, 779).getMinutes()\", 16),\n        TestAction::assert_eq(\"new Date(1/0).getMinutes()\", f64::NAN),\n    ]);\n}\n\n#[test]\nfn date_proto_get_month() {\n    run_test_actions([\n        TestAction::assert_eq(\"new Date(2020, 6, 8, 9, 16, 15, 779).getMonth()\", 6),\n        TestAction::assert_eq(\"new Date(1/0).getMonth()\", f64::NAN),\n    ]);\n}\n\n#[test]\nfn date_proto_get_seconds() {\n    run_test_actions([\n        TestAction::assert_eq(\"new Date(2020, 6, 8, 9, 16, 15, 779).getSeconds()\", 15),\n        TestAction::assert_eq(\"new Date(1/0).getSeconds()\", f64::NAN),\n    ]);\n}\n\n#[test]\nfn date_proto_get_time() {\n    run_test_actions([\n        TestAction::assert_eq(\n            \"new Date(2020, 6, 8, 9, 16, 15, 779).getTime()\",\n            timestamp_from_local(2020, 7, 8, 9, 16, 15, 779),\n        ),\n        TestAction::assert_eq(\"new Date(1/0).getTime()\", f64::NAN),\n    ]);\n}\n\n#[test]\nfn date_proto_get_year() {\n    run_test_actions([\n        TestAction::assert_eq(\"new Date(2020, 6, 8, 9, 16, 15, 779).getYear()\", 120),\n        TestAction::assert_eq(\"new Date(1/0).getYear()\", f64::NAN),\n    ]);\n}\n\n#[test]\nfn date_proto_get_timezone_offset() {\n    run_test_actions([\n        TestAction::assert(indoc! {r#\"\n                new Date('1975-08-19T23:15:30+07:00').getTimezoneOffset() ===\n                new Date('1975-08-19T23:15:30-02:00').getTimezoneOffset()\n            \"#}),\n        // NB: Host Settings, not TZ specified in the DateTime.\n        TestAction::assert_eq(\n            \"new Date('1975-08-19T23:15:30+07:00').getTimezoneOffset()\",\n            {\n                let t = from_local(1975, 8, 19, 23, 15, 30, 0);\n                -t.offset().whole_seconds() / 60\n            },\n        ),\n    ]);\n}\n\n#[test]\nfn date_proto_get_utc_date_call() {\n    run_test_actions([\n        TestAction::assert_eq(\n            \"new Date(Date.UTC(2020, 6, 8, 9, 16, 15, 779)).getUTCDate()\",\n            8,\n        ),\n        TestAction::assert_eq(\"new Date(1/0).getUTCDate()\", f64::NAN),\n    ]);\n}\n\n#[test]\nfn date_proto_get_utc_day_call() {\n    run_test_actions([\n        TestAction::assert_eq(\n            \"new Date(Date.UTC(2020, 6, 8, 9, 16, 15, 779)).getUTCDay()\",\n            3,\n        ),\n        TestAction::assert_eq(\"new Date(1/0).getUTCDay()\", f64::NAN),\n    ]);\n}\n\n#[test]\nfn date_proto_get_utc_full_year_call() {\n    run_test_actions([\n        TestAction::assert_eq(\n            \"new Date(Date.UTC(2020, 6, 8, 9, 16, 15, 779)).getUTCFullYear()\",\n            2020,\n        ),\n        TestAction::assert_eq(\"new Date(1/0).getUTCFullYear()\", f64::NAN),\n    ]);\n}\n\n#[test]\nfn date_proto_get_utc_hours_call() {\n    run_test_actions([\n        TestAction::assert_eq(\n            \"new Date(Date.UTC(2020, 6, 8, 9, 16, 15, 779)).getUTCHours()\",\n            9,\n        ),\n        TestAction::assert_eq(\"new Date(1/0).getUTCHours()\", f64::NAN),\n    ]);\n}\n\n#[test]\nfn date_proto_get_utc_milliseconds_call() {\n    run_test_actions([\n        TestAction::assert_eq(\n            \"new Date(Date.UTC(2020, 6, 8, 9, 16, 15, 779)).getUTCMilliseconds()\",\n            779,\n        ),\n        TestAction::assert_eq(\"new Date(1/0).getUTCMilliseconds()\", f64::NAN),\n    ]);\n}\n\n#[test]\nfn date_proto_get_utc_minutes_call() {\n    run_test_actions([\n        TestAction::assert_eq(\n            \"new Date(Date.UTC(2020, 6, 8, 9, 16, 15, 779)).getUTCMinutes()\",\n            16,\n        ),\n        TestAction::assert_eq(\"new Date(1/0).getUTCMinutes()\", f64::NAN),\n    ]);\n}\n\n#[test]\nfn date_proto_get_utc_month() {\n    run_test_actions([\n        TestAction::assert_eq(\n            \"new Date(Date.UTC(2020, 6, 8, 9, 16, 15, 779)).getUTCMonth()\",\n            6,\n        ),\n        TestAction::assert_eq(\"new Date(1/0).getUTCMonth()\", f64::NAN),\n    ]);\n}\n\n#[test]\nfn date_proto_get_utc_seconds() {\n    run_test_actions([\n        TestAction::assert_eq(\n            \"new Date(Date.UTC(2020, 6, 8, 9, 16, 15, 779)).getUTCSeconds()\",\n            15,\n        ),\n        TestAction::assert_eq(\"new Date(1/0).getUTCSeconds()\", f64::NAN),\n    ]);\n}\n\n#[test]\nfn date_proto_set_date() {\n    run_test_actions([\n        TestAction::assert_eq(\n            \"new Date(2020, 6, 8, 9, 16, 15, 779).setDate(21)\",\n            timestamp_from_local(2020, 7, 21, 9, 16, 15, 779),\n        ),\n        // Date wraps to previous month for 0.\n        TestAction::assert_eq(\n            \"new Date(2020, 6, 8, 9, 16, 15, 779).setDate(0)\",\n            timestamp_from_local(2020, 6, 30, 9, 16, 15, 779),\n        ),\n        TestAction::assert_eq(\n            \"new Date(2020, 6, 8, 9, 16, 15, 779).setDate(1/0)\",\n            f64::NAN,\n        ),\n    ]);\n}\n\n#[test]\nfn date_proto_set_full_year() {\n    run_test_actions([\n        TestAction::assert_eq(\n            \"new Date(2020, 6, 8, 9, 16, 15, 779).setFullYear(2012)\",\n            timestamp_from_local(2012, 7, 8, 9, 16, 15, 779),\n        ),\n        TestAction::assert_eq(\n            \"new Date(2020, 6, 8, 9, 16, 15, 779).setFullYear(2012, 8)\",\n            timestamp_from_local(2012, 9, 8, 9, 16, 15, 779),\n        ),\n        TestAction::assert_eq(\n            \"new Date(2020, 6, 8, 9, 16, 15, 779).setFullYear(2012, 8, 10)\",\n            timestamp_from_local(2012, 9, 10, 9, 16, 15, 779),\n        ),\n        // Out-of-bounds\n        TestAction::assert_eq(\n            \"new Date(2020, 6, 8, 9, 16, 15, 779).setFullYear(2012, 35)\",\n            timestamp_from_local(2014, 12, 8, 9, 16, 15, 779),\n        ),\n        TestAction::assert_eq(\n            \"new Date(2020, 6, 8, 9, 16, 15, 779).setFullYear(2012, -35)\",\n            timestamp_from_local(2009, 2, 8, 9, 16, 15, 779),\n        ),\n        TestAction::assert_eq(\n            \"new Date(2020, 6, 8, 9, 16, 15, 779).setFullYear(2012, 9, 950)\",\n            timestamp_from_local(2015, 5, 8, 9, 16, 15, 779),\n        ),\n        TestAction::assert_eq(\n            \"new Date(2020, 6, 8, 9, 16, 15, 779).setFullYear(2012, 9, -950)\",\n            timestamp_from_local(2010, 2, 23, 9, 16, 15, 779),\n        ),\n        TestAction::assert_eq(\n            \"new Date(2020, 6, 8, 9, 16, 15, 779).setFullYear(1/0)\",\n            f64::NAN,\n        ),\n    ]);\n}\n\n#[test]\nfn date_proto_set_hours() {\n    run_test_actions([\n        TestAction::assert_eq(\n            \"new Date(2020, 6, 8, 9, 16, 15, 779).setHours(11)\",\n            timestamp_from_local(2020, 7, 8, 11, 16, 15, 779),\n        ),\n        TestAction::assert_eq(\n            \"new Date(2020, 6, 8, 9, 16, 15, 779).setHours(11, 35)\",\n            timestamp_from_local(2020, 7, 8, 11, 35, 15, 779),\n        ),\n        TestAction::assert_eq(\n            \"new Date(2020, 6, 8, 9, 16, 15, 779).setHours(11, 35, 23)\",\n            timestamp_from_local(2020, 7, 8, 11, 35, 23, 779),\n        ),\n        TestAction::assert_eq(\n            \"new Date(2020, 6, 8, 9, 16, 15, 779).setHours(11, 35, 23, 537)\",\n            timestamp_from_local(2020, 7, 8, 11, 35, 23, 537),\n        ),\n        // Out-of-bounds\n        TestAction::assert_eq(\n            \"new Date(2020, 6, 8, 9, 16, 15, 779).setHours(10000, 20000, 30000, 40123)\",\n            timestamp_from_local(2021, 9, 11, 21, 40, 40, 123),\n        ),\n        TestAction::assert_eq(\n            \"new Date(2020, 6, 8, 9, 16, 15, 779).setHours(1/0)\",\n            f64::NAN,\n        ),\n    ]);\n}\n\n#[test]\nfn date_proto_set_milliseconds() {\n    run_test_actions([\n        TestAction::assert_eq(\n            \"new Date(2020, 6, 8, 9, 16, 15, 779).setMilliseconds(597)\",\n            timestamp_from_local(2020, 7, 8, 9, 16, 15, 597),\n        ),\n        // Out-of-bounds\n        // Thorough tests are done by setHours\n        TestAction::assert_eq(\n            \"new Date(2020, 6, 8, 9, 16, 15, 779).setMilliseconds(40123)\",\n            timestamp_from_local(2020, 7, 8, 9, 16, 55, 123),\n        ),\n        TestAction::assert_eq(\n            \"new Date(2020, 6, 8, 9, 16, 15, 779).setMilliseconds(1/0)\",\n            f64::NAN,\n        ),\n    ]);\n}\n\n#[test]\nfn date_proto_set_minutes() {\n    run_test_actions([\n        TestAction::assert_eq(\n            \"new Date(2020, 6, 8, 9, 16, 15, 779).setMinutes(11)\",\n            timestamp_from_local(2020, 7, 8, 9, 11, 15, 779),\n        ),\n        TestAction::assert_eq(\n            \"new Date(2020, 6, 8, 9, 16, 15, 779).setMinutes(11, 35)\",\n            timestamp_from_local(2020, 7, 8, 9, 11, 35, 779),\n        ),\n        TestAction::assert_eq(\n            \"new Date(2020, 6, 8, 9, 16, 15, 779).setMinutes(11, 35, 537)\",\n            timestamp_from_local(2020, 7, 8, 9, 11, 35, 537),\n        ),\n        // Out-of-bounds\n        // Thorough tests are done by setHours\n        TestAction::assert_eq(\n            \"new Date(2020, 6, 8, 9, 16, 15, 779).setMinutes(600000, 30000, 40123)\",\n            timestamp_from_local(2021, 8, 29, 9, 20, 40, 123),\n        ),\n        TestAction::assert_eq(\n            \"new Date(2020, 6, 8, 9, 16, 15, 779).setMinutes(1/0)\",\n            f64::NAN,\n        ),\n    ]);\n}\n\n#[test]\nfn date_proto_set_month() {\n    run_test_actions([\n        TestAction::assert_eq(\n            \"new Date(2020, 6, 8, 9, 16, 15, 779).setMonth(11)\",\n            timestamp_from_local(2020, 12, 8, 9, 16, 15, 779),\n        ),\n        TestAction::assert_eq(\n            \"new Date(2020, 6, 8, 9, 16, 15, 779).setMonth(11, 16)\",\n            timestamp_from_local(2020, 12, 16, 9, 16, 15, 779),\n        ),\n        // Out-of-bounds\n        // Thorough tests are done by setFullYear\n        TestAction::assert_eq(\n            \"new Date(2020, 6, 8, 9, 16, 15, 779).setMonth(40, 83)\",\n            timestamp_from_local(2023, 7, 22, 9, 16, 15, 779),\n        ),\n        TestAction::assert_eq(\n            \"new Date(2020, 6, 8, 9, 16, 15, 779).setMonth(1/0)\",\n            f64::NAN,\n        ),\n    ]);\n}\n\n#[test]\nfn date_proto_set_seconds() {\n    run_test_actions([\n        TestAction::assert_eq(\n            \"new Date(2020, 6, 8, 9, 16, 15, 779).setSeconds(11)\",\n            timestamp_from_local(2020, 7, 8, 9, 16, 11, 779),\n        ),\n        TestAction::assert_eq(\n            \"new Date(2020, 6, 8, 9, 16, 15, 779).setSeconds(11, 487)\",\n            timestamp_from_local(2020, 7, 8, 9, 16, 11, 487),\n        ),\n        // Out-of-bounds\n        // Thorough tests are done by setHour\n        TestAction::assert_eq(\n            \"new Date(2020, 6, 8, 9, 16, 15, 779).setSeconds(40000000, 40123)\",\n            timestamp_from_local(2021, 10, 14, 8, 23, 20, 123),\n        ),\n        TestAction::assert_eq(\n            \"new Date(2020, 6, 8, 9, 16, 15, 779).setSeconds(1/0)\",\n            f64::NAN,\n        ),\n    ]);\n}\n\n#[test]\nfn set_year() {\n    run_test_actions([\n        TestAction::assert_eq(\n            \"new Date(2020, 6, 8, 9, 16, 15, 779).setYear(98)\",\n            timestamp_from_local(1998, 7, 8, 9, 16, 15, 779),\n        ),\n        TestAction::assert_eq(\n            \"new Date(2020, 6, 8, 9, 16, 15, 779).setYear(2001)\",\n            timestamp_from_local(2001, 7, 8, 9, 16, 15, 779),\n        ),\n        TestAction::assert_eq(\n            \"new Date(2020, 6, 8, 9, 16, 15, 779).setYear(1/0)\",\n            f64::NAN,\n        ),\n    ]);\n}\n\n#[test]\nfn date_proto_set_time() {\n    run_test_actions([TestAction::assert_eq(\n        \"new Date().setTime(new Date(2020, 6, 8, 9, 16, 15, 779).getTime())\",\n        timestamp_from_local(2020, 7, 8, 9, 16, 15, 779),\n    )]);\n}\n\n#[test]\nfn date_proto_set_utc_date() {\n    run_test_actions([\n        TestAction::assert_eq(\n            \"new Date(Date.UTC(2020, 6, 8, 9, 16, 15, 779)).setUTCDate(21)\",\n            timestamp_from_utc(2020, 7, 21, 9, 16, 15, 779),\n        ),\n        // Date wraps to previous month for 0.\n        TestAction::assert_eq(\n            \"new Date(Date.UTC(2020, 6, 8, 9, 16, 15, 779)).setUTCDate(0)\",\n            timestamp_from_utc(2020, 6, 30, 9, 16, 15, 779),\n        ),\n        TestAction::assert_eq(\n            \"new Date(Date.UTC(2020, 6, 8, 9, 16, 15, 779)).setUTCDate(1/0)\",\n            f64::NAN,\n        ),\n    ]);\n}\n\n#[test]\nfn date_proto_set_utc_full_year() {\n    run_test_actions([\n        TestAction::assert_eq(\n            \"new Date(Date.UTC(2020, 6, 8, 9, 16, 15, 779)).setUTCFullYear(2012)\",\n            timestamp_from_utc(2012, 7, 8, 9, 16, 15, 779),\n        ),\n        TestAction::assert_eq(\n            \"new Date(Date.UTC(2020, 6, 8, 9, 16, 15, 779)).setUTCFullYear(2012, 8)\",\n            timestamp_from_utc(2012, 9, 8, 9, 16, 15, 779),\n        ),\n        TestAction::assert_eq(\n            \"new Date(Date.UTC(2020, 6, 8, 9, 16, 15, 779)).setUTCFullYear(2012, 8, 10)\",\n            timestamp_from_utc(2012, 9, 10, 9, 16, 15, 779),\n        ),\n        // Out-of-bounds\n        TestAction::assert_eq(\n            \"new Date(Date.UTC(2020, 6, 8, 9, 16, 15, 779)).setUTCFullYear(2012, 35)\",\n            timestamp_from_utc(2014, 12, 8, 9, 16, 15, 779),\n        ),\n        TestAction::assert_eq(\n            \"new Date(Date.UTC(2020, 6, 8, 9, 16, 15, 779)).setUTCFullYear(2012, -35)\",\n            timestamp_from_utc(2009, 2, 8, 9, 16, 15, 779),\n        ),\n        TestAction::assert_eq(\n            \"new Date(Date.UTC(2020, 6, 8, 9, 16, 15, 779)).setUTCFullYear(2012, 9, 950)\",\n            timestamp_from_utc(2015, 5, 8, 9, 16, 15, 779),\n        ),\n        TestAction::assert_eq(\n            \"new Date(Date.UTC(2020, 6, 8, 9, 16, 15, 779)).setUTCFullYear(2012, 9, -950)\",\n            timestamp_from_utc(2010, 2, 23, 9, 16, 15, 779),\n        ),\n        TestAction::assert_eq(\n            \"new Date(Date.UTC(2020, 6, 8, 9, 16, 15, 779)).setUTCFullYear(1/0)\",\n            f64::NAN,\n        ),\n    ]);\n}\n\n#[test]\nfn date_proto_set_utc_hours() {\n    // The local-time constructor `new Date(2020, 6, 8, 9, 16, 15, 779)` converts\n    // local time to UTC internally. The resulting UTC components depend on the\n    // host timezone offset, so we compute them dynamically.\n    let base = from_local(2020, 7, 8, 9, 16, 15, 779).to_offset(time::UtcOffset::UTC);\n    let base_y = base.year();\n    let base_m = base.month() as u8;\n    let base_d = base.day();\n    let base_min = base.minute();\n    let base_sec = base.second();\n    let base_ms = base.millisecond();\n\n    run_test_actions([\n        // setUTCHours(11): only the UTC hour changes; date/min/sec/ms stay.\n        TestAction::assert_eq(\n            \"new Date(2020, 6, 8, 9, 16, 15, 779).setUTCHours(11)\",\n            timestamp_from_utc(base_y, base_m, base_d, 11, base_min, base_sec, base_ms),\n        ),\n        // setUTCHours(h, m): hour and minutes are overridden; sec/ms stay.\n        TestAction::assert_eq(\n            \"new Date(2020, 6, 8, 9, 16, 15, 779).setUTCHours(11, 35)\",\n            timestamp_from_utc(base_y, base_m, base_d, 11, 35, base_sec, base_ms),\n        ),\n        // setUTCHours(h, m, s): hour, minutes, seconds overridden; ms stays.\n        TestAction::assert_eq(\n            \"new Date(2020, 6, 8, 9, 16, 15, 779).setUTCHours(11, 35, 23)\",\n            timestamp_from_utc(base_y, base_m, base_d, 11, 35, 23, base_ms),\n        ),\n        // setUTCHours(h, m, s, ms): all time components overridden.\n        TestAction::assert_eq(\n            \"new Date(2020, 6, 8, 9, 16, 15, 779).setUTCHours(11, 35, 23, 537)\",\n            timestamp_from_utc(base_y, base_m, base_d, 11, 35, 23, 537),\n        ),\n        // Out-of-bounds: all time components specified, overflow wraps across days.\n        // The base timestamp is Day(t) from the local-time date's internal UTC value.\n        TestAction::assert_eq(\n            \"new Date(2020, 6, 8, 9, 16, 15, 779).setUTCHours(10000, 20000, 30000, 40123)\",\n            {\n                // setUTCHours computes MakeDate(Day(t), MakeTime(h, m, s, ms)).\n                // Day(t) is the UTC day count of the base date, so we reconstruct\n                // by starting from the base UTC midnight and adding the overflow.\n                let base_day_ms = timestamp_from_utc(base_y, base_m, base_d, 0, 0, 0, 0);\n                let total_ms = i64::from(10000) * 3_600_000\n                    + i64::from(20000) * 60_000\n                    + i64::from(30000) * 1_000\n                    + 40123;\n                base_day_ms + total_ms\n            },\n        ),\n        TestAction::assert_eq(\n            \"new Date(2020, 6, 8, 9, 16, 15, 779).setUTCHours(1/0)\",\n            f64::NAN,\n        ),\n    ]);\n}\n\n#[test]\nfn date_proto_set_utc_milliseconds() {\n    run_test_actions([\n        TestAction::assert_eq(\n            \"new Date(Date.UTC(2020, 6, 8, 9, 16, 15, 779)).setUTCMilliseconds(597)\",\n            timestamp_from_utc(2020, 7, 8, 9, 16, 15, 597),\n        ),\n        // Out-of-bounds\n        // Thorough tests are done by setHours\n        TestAction::assert_eq(\n            \"new Date(Date.UTC(2020, 6, 8, 9, 16, 15, 779)).setUTCMilliseconds(40123)\",\n            timestamp_from_utc(2020, 7, 8, 9, 16, 55, 123),\n        ),\n        TestAction::assert_eq(\n            \"new Date(Date.UTC(2020, 6, 8, 9, 16, 15, 779)).setUTCMilliseconds(1/0)\",\n            f64::NAN,\n        ),\n    ]);\n}\n\n#[test]\nfn date_proto_set_utc_minutes() {\n    run_test_actions([\n        TestAction::assert_eq(\n            \"new Date(Date.UTC(2020, 6, 8, 9, 16, 15, 779)).setUTCMinutes(11)\",\n            timestamp_from_utc(2020, 7, 8, 9, 11, 15, 779),\n        ),\n        TestAction::assert_eq(\n            \"new Date(Date.UTC(2020, 6, 8, 9, 16, 15, 779)).setUTCMinutes(11, 35)\",\n            timestamp_from_utc(2020, 7, 8, 9, 11, 35, 779),\n        ),\n        TestAction::assert_eq(\n            \"new Date(Date.UTC(2020, 6, 8, 9, 16, 15, 779)).setUTCMinutes(11, 35, 537)\",\n            timestamp_from_utc(2020, 7, 8, 9, 11, 35, 537),\n        ),\n        // Out-of-bounds\n        // Thorough tests are done by setHours\n        TestAction::assert_eq(\n            \"new Date(Date.UTC(2020, 6, 8, 9, 16, 15, 779)).setUTCMinutes(600000, 30000, 40123)\",\n            timestamp_from_utc(2021, 8, 29, 9, 20, 40, 123),\n        ),\n        TestAction::assert_eq(\n            \"new Date(Date.UTC(2020, 6, 8, 9, 16, 15, 779)).setUTCMinutes(1/0)\",\n            f64::NAN,\n        ),\n    ]);\n}\n\n#[test]\nfn date_proto_set_utc_month() {\n    run_test_actions([\n        TestAction::assert_eq(\n            \"new Date(Date.UTC(2020, 6, 8, 9, 16, 15, 779)).setUTCMonth(11)\",\n            timestamp_from_utc(2020, 12, 8, 9, 16, 15, 779),\n        ),\n        TestAction::assert_eq(\n            \"new Date(Date.UTC(2020, 6, 8, 9, 16, 15, 779)).setUTCMonth(11, 16)\",\n            timestamp_from_utc(2020, 12, 16, 9, 16, 15, 779),\n        ),\n        // Out-of-bounds\n        // Thorough tests are done by setFullYear\n        TestAction::assert_eq(\n            \"new Date(Date.UTC(2020, 6, 8, 9, 16, 15, 779)).setUTCMonth(40, 83)\",\n            timestamp_from_utc(2023, 7, 22, 9, 16, 15, 779),\n        ),\n        TestAction::assert_eq(\n            \"new Date(Date.UTC(2020, 6, 8, 9, 16, 15, 779)).setUTCMonth(1/0)\",\n            f64::NAN,\n        ),\n    ]);\n}\n\n#[test]\nfn date_proto_set_utc_seconds() {\n    run_test_actions([\n        TestAction::assert_eq(\n            \"new Date(Date.UTC(2020, 6, 8, 9, 16, 15, 779)).setUTCSeconds(11)\",\n            timestamp_from_utc(2020, 7, 8, 9, 16, 11, 779),\n        ),\n        TestAction::assert_eq(\n            \"new Date(Date.UTC(2020, 6, 8, 9, 16, 15, 779)).setUTCSeconds(11, 487)\",\n            timestamp_from_utc(2020, 7, 8, 9, 16, 11, 487),\n        ),\n        // Out-of-bounds\n        // Thorough tests are done by setHour\n        TestAction::assert_eq(\n            \"new Date(Date.UTC(2020, 6, 8, 9, 16, 15, 779)).setUTCSeconds(40000000, 40123)\",\n            timestamp_from_utc(2021, 10, 14, 8, 23, 20, 123),\n        ),\n        TestAction::assert_eq(\n            \"new Date(Date.UTC(2020, 6, 8, 9, 16, 15, 779)).setUTCSeconds(1/0)\",\n            f64::NAN,\n        ),\n    ]);\n}\n\n#[test]\nfn date_proto_to_date_string() {\n    run_test_actions([TestAction::assert_eq(\n        \"new Date(2020, 6, 8, 9, 16, 15, 779).toDateString()\",\n        js_str!(\"Wed Jul 08 2020\"),\n    )]);\n}\n\n#[test]\nfn date_proto_to_gmt_string() {\n    run_test_actions([TestAction::assert_eq(\n        \"new Date(Date.UTC(2020, 6, 8, 9, 16, 15, 779)).toGMTString()\",\n        js_str!(\"Wed, 08 Jul 2020 09:16:15 GMT\"),\n    )]);\n}\n\n#[test]\nfn date_proto_to_iso_string() {\n    run_test_actions([TestAction::assert_eq(\n        \"new Date(Date.UTC(2020, 6, 8, 9, 16, 15, 779)).toISOString()\",\n        js_str!(\"2020-07-08T09:16:15.779Z\"),\n    )]);\n}\n\n#[test]\nfn date_proto_to_iso_string_year_zero() {\n    run_test_actions([TestAction::assert_eq(\n        r#\"new Date(\"0000-06-15T00:00:00Z\").toISOString()\"#,\n        js_str!(\"0000-06-15T00:00:00.000Z\"),\n    )]);\n}\n\n#[test]\nfn date_proto_to_json() {\n    run_test_actions([TestAction::assert_eq(\n        \"new Date(Date.UTC(2020, 6, 8, 9, 16, 15, 779)).toJSON()\",\n        js_str!(\"2020-07-08T09:16:15.779Z\"),\n    )]);\n}\n\n#[test]\nfn date_proto_to_string() {\n    let to_string_format = format_description!(\n        \"[weekday repr:short] [month repr:short] [day] [year] [hour]:[minute]:[second] GMT[offset_hour sign:mandatory][offset_minute][end]\"\n    );\n    let t = from_local(2020, 7, 8, 9, 16, 15, 779)\n        .format(to_string_format)\n        .unwrap();\n\n    run_test_actions([TestAction::assert_eq(\n        \"new Date(2020, 6, 8, 9, 16, 15, 779).toString()\",\n        js_string!(t),\n    )]);\n}\n\n#[test]\nfn date_proto_to_time_string() {\n    let to_time_string_format = format_description!(\n        \"[hour]:[minute]:[second] GMT[offset_hour sign:mandatory][offset_minute][end]\"\n    );\n    let t = from_local(2020, 7, 8, 9, 16, 15, 779)\n        .format(to_time_string_format)\n        .unwrap();\n\n    run_test_actions([TestAction::assert_eq(\n        \"new Date(2020, 6, 8, 9, 16, 15, 779).toTimeString()\",\n        js_string!(t),\n    )]);\n}\n\n#[test]\nfn date_proto_to_utc_string() {\n    run_test_actions([TestAction::assert_eq(\n        \"new Date(Date.UTC(2020, 6, 8, 9, 16, 15, 779)).toUTCString()\",\n        js_str!(\"Wed, 08 Jul 2020 09:16:15 GMT\"),\n    )]);\n}\n\n#[test]\nfn date_proto_value_of() {\n    run_test_actions([TestAction::assert_eq(\n        \"new Date(Date.UTC(2020, 6, 8, 9, 16, 15, 779)).valueOf()\",\n        1_594_199_775_779_i64,\n    )]);\n}\n\n#[test]\nfn date_neg() {\n    run_test_actions([TestAction::assert_eq(\n        \"-new Date(Date.UTC(2020, 6, 8, 9, 16, 15, 779))\",\n        -1_594_199_775_779_i64,\n    )]);\n}\n\n#[test]\nfn date_json() {\n    run_test_actions([TestAction::assert_eq(\n        \"JSON.stringify({ date: new Date(Date.UTC(2020, 6, 8, 9, 16, 15, 779)) })\",\n        js_string!(r#\"{\"date\":\"2020-07-08T09:16:15.779Z\"}\"#),\n    )]);\n}\n\n#[test]\nfn date_parse_hour24_validation() {\n    run_test_actions([\n        // 24:00:00.000 is valid (midnight end-of-day)\n        TestAction::assert(\"!isNaN(Date.parse('2024-01-01T24:00:00Z'))\"),\n        TestAction::assert(\"!isNaN(Date.parse('2024-01-01T24:00:00.000Z'))\"),\n        // hour 24 with non-zero minutes/seconds/ms must be NaN\n        TestAction::assert(\"isNaN(Date.parse('2024-01-01T24:30:00Z'))\"),\n        TestAction::assert(\"isNaN(Date.parse('2024-01-01T24:00:01Z'))\"),\n        TestAction::assert(\"isNaN(Date.parse('2024-01-01T24:00:00.001Z'))\"),\n    ]);\n}\n\n#[test]\n#[cfg(feature = \"intl\")]\nfn date_proto_to_locale_string_intl() {\n    run_test_actions([\n        // Invalid receiver: spec requires TypeError\n        TestAction::assert_native_error(\n            \"Date.prototype.toLocaleString.call({})\",\n            JsNativeErrorKind::Type,\n            \"'this' is not a Date\",\n        ),\n        TestAction::assert_native_error(\n            \"Date.prototype.toLocaleDateString.call({})\",\n            JsNativeErrorKind::Type,\n            \"'this' is not a Date\",\n        ),\n        TestAction::assert_native_error(\n            \"Date.prototype.toLocaleTimeString.call({})\",\n            JsNativeErrorKind::Type,\n            \"'this' is not a Date\",\n        ),\n        TestAction::assert_eq(\"new Date(NaN).toLocaleString()\", js_str!(\"Invalid Date\")),\n        TestAction::assert(\"typeof new Date(2020, 6, 8).toLocaleString() === 'string'\"),\n        TestAction::assert(\"typeof new Date(2020, 6, 8).toLocaleDateString() === 'string'\"),\n        TestAction::assert(\"typeof new Date(2020, 6, 8).toLocaleTimeString() === 'string'\"),\n        TestAction::assert(\"typeof new Date(0).toLocaleString('en-US') === 'string'\"),\n        TestAction::assert(\"typeof new Date(0).toLocaleDateString('en-US') === 'string'\"),\n        TestAction::assert(\"typeof new Date(0).toLocaleDateString('de-DE') === 'string'\"),\n        TestAction::assert(\"typeof new Date(0).toLocaleTimeString('en-US') === 'string'\"),\n        // Prove locale pipeline: different locales produce different output\n        TestAction::assert(\n            \"new Date(0).toLocaleDateString('en-US') !== new Date(0).toLocaleDateString('de-DE')\",\n        ),\n        TestAction::assert(\n            \"new Date(0).toLocaleString('en-US') !== new Date(0).toLocaleString('de-DE')\",\n        ),\n        TestAction::assert(\n            \"new Date(0).toLocaleTimeString('en-US') !== new Date(0).toLocaleTimeString('de-DE')\",\n        ),\n        // Prove ToDateTimeOptions pipeline: options affect output\n        TestAction::assert(\n            \"typeof new Date(0).toLocaleDateString('en-US', { dateStyle: 'short' }) === 'string'\",\n        ),\n        // Prove output is a string and not empty\n        TestAction::assert(\n            \"new Date(0).toLocaleDateString('en-US', { dateStyle: 'short' }).length > 0\",\n        ),\n    ]);\n}\n"
  },
  {
    "path": "core/engine/src/builtins/date/utils.rs",
    "content": "use crate::{JsStr, JsString, context::HostHooks, js_string, value::IntegerOrInfinity};\nuse boa_macros::js_str;\nuse boa_string::JsStrVariant;\nuse std::slice::Iter;\nuse std::str;\nuse std::{borrow::Cow, iter::Peekable};\nuse time::{OffsetDateTime, PrimitiveDateTime, macros::format_description};\n\n// Time-related Constants\n//\n// More info:\n// - [ECMAScript reference][spec]\n//\n// https://tc39.es/ecma262/#sec-time-related-constants\n\n// HoursPerDay = 24\nconst HOURS_PER_DAY: f64 = 24.0;\n\n// MinutesPerHour = 60\nconst MINUTES_PER_HOUR: f64 = 60.0;\n\n// SecondsPerMinute = 60\nconst SECONDS_PER_MINUTE: f64 = 60.0;\n\n// msPerSecond = 1000𝔽\nconst MS_PER_SECOND: f64 = 1000.0;\n\n// msPerMinute = 60000𝔽 = msPerSecond × 𝔽(SecondsPerMinute)\npub(super) const MS_PER_MINUTE: f64 = MS_PER_SECOND * SECONDS_PER_MINUTE;\n\n// msPerHour = 3600000𝔽 = msPerMinute × 𝔽(MinutesPerHour)\nconst MS_PER_HOUR: f64 = MS_PER_MINUTE * MINUTES_PER_HOUR;\n\n// msPerDay = 86400000𝔽 = msPerHour × 𝔽(HoursPerDay)\nconst MS_PER_DAY: f64 = MS_PER_HOUR * HOURS_PER_DAY;\n\n/// Abstract operation `Day ( t )`\n///\n/// More info:\n/// - [ECMAScript reference][spec]\n///\n/// [spec]: https://tc39.es/ecma262/#sec-day\npub(super) fn day(t: f64) -> f64 {\n    // 1. Return 𝔽(floor(ℝ(t / msPerDay))).\n    (t / MS_PER_DAY).floor()\n}\n\n/// Abstract operation `TimeWithinDay ( t )`\n///\n/// More info:\n/// - [ECMAScript reference][spec]\n///\n/// [spec]: https://tc39.es/ecma262/#sec-timewithinday\npub(super) fn time_within_day(t: f64) -> f64 {\n    // 1. Return 𝔽(ℝ(t) modulo ℝ(msPerDay)).\n    t.rem_euclid(MS_PER_DAY)\n}\n\n/// Abstract operation `DaysInYear ( y )`\n///\n/// More info:\n/// - [ECMAScript reference][spec]\n///\n/// [spec]: https://tc39.es/ecma262/#sec-daysinyear\nfn days_in_year(y: f64) -> u16 {\n    // 1. Let ry be ℝ(y).\n    let ry = y;\n\n    // 2. If (ry modulo 400) = 0, return 366𝔽.\n    if ry.rem_euclid(400.0) == 0.0 {\n        return 366;\n    }\n\n    // 3. If (ry modulo 100) = 0, return 365𝔽.\n    if ry.rem_euclid(100.0) == 0.0 {\n        return 365;\n    }\n\n    // 4. If (ry modulo 4) = 0, return 366𝔽.\n    if ry.rem_euclid(4.0) == 0.0 {\n        return 366;\n    }\n\n    // 5. Return 365𝔽.\n    365\n}\n\n/// Abstract operation `DayFromYear ( y )`\n///\n/// More info:\n/// - [ECMAScript reference][spec]\n///\n/// [spec]: https://tc39.es/ecma262/#sec-dayfromyear\nfn day_from_year(y: f64) -> f64 {\n    // 1. Let ry be ℝ(y).\n    // 2. NOTE: In the following steps, each _numYearsN_ is the number of years divisible by N\n    //          that occur between the epoch and the start of year y.\n    //          (The number is negative if y is before the epoch.)\n\n    // 3. Let numYears1 be (ry - 1970).\n    let num_years_1 = y - 1970.0;\n\n    // 4. Let numYears4 be floor((ry - 1969) / 4).\n    let num_years_4 = ((y - 1969.0) / 4.0).floor();\n\n    // 5. Let numYears100 be floor((ry - 1901) / 100).\n    let num_years_100 = ((y - 1901.0) / 100.0).floor();\n\n    // 6. Let numYears400 be floor((ry - 1601) / 400).\n    let num_years_400 = ((y - 1601.0) / 400.0).floor();\n\n    // 7. Return 𝔽(365 × numYears1 + numYears4 - numYears100 + numYears400).\n    365.0 * num_years_1 + num_years_4 - num_years_100 + num_years_400\n}\n\n/// Abstract operation `TimeFromYear ( y )`\n///\n/// More info:\n/// - [ECMAScript reference][spec]\n///\n/// [spec]: https://tc39.es/ecma262/#sec-timefromyear\nfn time_from_year(y: f64) -> f64 {\n    // 1. Return msPerDay × DayFromYear(y).\n    MS_PER_DAY * day_from_year(y)\n}\n\n/// Abstract operation `YearFromTime ( t )`\n///\n/// More info:\n/// - [ECMAScript reference][spec]\n///\n/// [spec]: https://tc39.es/ecma262/#sec-yearfromtime\npub(crate) fn year_from_time(t: f64) -> i32 {\n    const MS_PER_AVERAGE_YEAR: f64 = 12.0 * 30.436_875 * MS_PER_DAY;\n\n    // 1. Return the largest integral Number y (closest to +∞) such that TimeFromYear(y) ≤ t.\n    let mut year = (((t + MS_PER_AVERAGE_YEAR / 2.0) / MS_PER_AVERAGE_YEAR).floor()) as i32 + 1970;\n    if time_from_year(year.into()) > t {\n        year -= 1;\n    }\n    year\n}\n\n/// Abstract operation `DayWithinYear ( t )`\n///\n/// More info:\n/// - [ECMAScript reference][spec]\n///\n/// [spec]: https://tc39.es/ecma262/#sec-daywithinyear\nfn day_within_year(t: f64) -> u16 {\n    // 1. Return Day(t) - DayFromYear(YearFromTime(t)).\n    (day(t) - day_from_year(year_from_time(t).into())) as u16\n}\n\n/// Abstract operation `InLeapYear ( t )`\n///\n/// More info:\n/// - [ECMAScript reference][spec]\n///\n/// [spec]: https://tc39.es/ecma262/#sec-inleapyear\nfn in_leap_year(t: f64) -> u16 {\n    // 1. If DaysInYear(YearFromTime(t)) is 366𝔽, return 1𝔽; else return +0𝔽.\n    (days_in_year(year_from_time(t).into()) == 366).into()\n}\n\n/// Abstract operation `MonthFromTime ( t )`\n///\n/// More info:\n/// - [ECMAScript reference][spec]\n///\n/// [spec]: https://tc39.es/ecma262/#sec-monthfromtime\npub(crate) fn month_from_time(t: f64) -> u8 {\n    // 1. Let inLeapYear be InLeapYear(t).\n    let in_leap_year = in_leap_year(t);\n\n    // 2. Let dayWithinYear be DayWithinYear(t).\n    let day_within_year = day_within_year(t);\n\n    match day_within_year {\n        // 3. If dayWithinYear < 31𝔽, return +0𝔽.\n        t if t < 31 => 0,\n        // 4. If dayWithinYear < 59𝔽 + inLeapYear, return 1𝔽.\n        t if t < 59 + in_leap_year => 1,\n        // 5. If dayWithinYear < 90𝔽 + inLeapYear, return 2𝔽.\n        t if t < 90 + in_leap_year => 2,\n        // 6. If dayWithinYear < 120𝔽 + inLeapYear, return 3𝔽.\n        t if t < 120 + in_leap_year => 3,\n        // 7. If dayWithinYear < 151𝔽 + inLeapYear, return 4𝔽.\n        t if t < 151 + in_leap_year => 4,\n        // 8. If dayWithinYear < 181𝔽 + inLeapYear, return 5𝔽.\n        t if t < 181 + in_leap_year => 5,\n        // 9. If dayWithinYear < 212𝔽 + inLeapYear, return 6𝔽.\n        t if t < 212 + in_leap_year => 6,\n        // 10. If dayWithinYear < 243𝔽 + inLeapYear, return 7𝔽.\n        t if t < 243 + in_leap_year => 7,\n        // 11. If dayWithinYear < 273𝔽 + inLeapYear, return 8𝔽.\n        t if t < 273 + in_leap_year => 8,\n        // 12. If dayWithinYear < 304𝔽 + inLeapYear, return 9𝔽.\n        t if t < 304 + in_leap_year => 9,\n        // 13. If dayWithinYear < 334𝔽 + inLeapYear, return 10𝔽.\n        t if t < 334 + in_leap_year => 10,\n        // 14. Assert: dayWithinYear < 365𝔽 + inLeapYear.\n        // 15. Return 11𝔽.\n        _ => 11,\n    }\n}\n\n/// Abstract operation `DateFromTime ( t )`\n///\n/// More info:\n/// - [ECMAScript reference][spec]\n///\n/// [spec]: https://tc39.es/ecma262/#sec-datefromtime\npub(crate) fn date_from_time(t: f64) -> u8 {\n    // 1. Let inLeapYear be InLeapYear(t).\n    let in_leap_year = in_leap_year(t);\n\n    // 2. Let dayWithinYear be DayWithinYear(t).\n    let day_within_year = day_within_year(t);\n\n    // 3. Let month be MonthFromTime(t).\n    let month = month_from_time(t);\n\n    let date = match month {\n        // 4. If month is +0𝔽, return dayWithinYear + 1𝔽.\n        0 => day_within_year + 1,\n        // 5. If month is 1𝔽, return dayWithinYear - 30𝔽.\n        1 => day_within_year - 30,\n        // 6. If month is 2𝔽, return dayWithinYear - 58𝔽 - inLeapYear.\n        2 => day_within_year - 58 - in_leap_year,\n        // 7. If month is 3𝔽, return dayWithinYear - 89𝔽 - inLeapYear.\n        3 => day_within_year - 89 - in_leap_year,\n        // 8. If month is 4𝔽, return dayWithinYear - 119𝔽 - inLeapYear.\n        4 => day_within_year - 119 - in_leap_year,\n        // 9. If month is 5𝔽, return dayWithinYear - 150𝔽 - inLeapYear.\n        5 => day_within_year - 150 - in_leap_year,\n        // 10. If month is 6𝔽, return dayWithinYear - 180𝔽 - inLeapYear.\n        6 => day_within_year - 180 - in_leap_year,\n        // 11. If month is 7𝔽, return dayWithinYear - 211𝔽 - inLeapYear.\n        7 => day_within_year - 211 - in_leap_year,\n        // 12. If month is 8𝔽, return dayWithinYear - 242𝔽 - inLeapYear.\n        8 => day_within_year - 242 - in_leap_year,\n        // 13. If month is 9𝔽, return dayWithinYear - 272𝔽 - inLeapYear.\n        9 => day_within_year - 272 - in_leap_year,\n        // 14. If month is 10𝔽, return dayWithinYear - 303𝔽 - inLeapYear.\n        10 => day_within_year - 303 - in_leap_year,\n        // 15. Assert: month is 11𝔽.\n        // 16. Return dayWithinYear - 333𝔽 - inLeapYear.\n        _ => day_within_year - 333 - in_leap_year,\n    };\n    date as u8\n}\n\n/// Abstract operation `WeekDay ( t )`\n///\n/// More info:\n/// - [ECMAScript reference][spec]\n///\n/// [spec]: https://tc39.es/ecma262/#sec-weekday\npub(super) fn week_day(t: f64) -> u8 {\n    // 1. Return 𝔽(ℝ(Day(t) + 4𝔽) modulo 7).\n    (day(t) + 4.0).rem_euclid(7.0) as u8\n}\n\n/// Abstract operation `HourFromTime ( t )`\n///\n/// More info:\n/// - [ECMAScript reference][spec]\n///\n/// [spec]: https://tc39.es/ecma262/#sec-hourfromtime\npub(crate) fn hour_from_time(t: f64) -> u8 {\n    // 1. Return 𝔽(floor(ℝ(t / msPerHour)) modulo HoursPerDay).\n    ((t / MS_PER_HOUR).floor()).rem_euclid(HOURS_PER_DAY) as u8\n}\n\n/// Abstract operation `MinFromTime ( t )`\n///\n/// More info:\n/// - [ECMAScript reference][spec]\n///\n/// [spec]: https://tc39.es/ecma262/#sec-minfromtime\npub(crate) fn min_from_time(t: f64) -> u8 {\n    // 1. Return 𝔽(floor(ℝ(t / msPerMinute)) modulo MinutesPerHour).\n    ((t / MS_PER_MINUTE).floor()).rem_euclid(MINUTES_PER_HOUR) as u8\n}\n\n/// Abstract operation `SecFromTime ( t )`\n///\n/// More info:\n/// - [ECMAScript reference][spec]\n///\n/// [spec]: https://tc39.es/ecma262/#sec-secfromtime\npub(crate) fn sec_from_time(t: f64) -> u8 {\n    // 1. Return 𝔽(floor(ℝ(t / msPerSecond)) modulo SecondsPerMinute).\n    ((t / MS_PER_SECOND).floor()).rem_euclid(SECONDS_PER_MINUTE) as u8\n}\n\n/// Abstract operation `msFromTime ( t )`\n///\n/// More info:\n/// - [ECMAScript reference][spec]\n///\n/// [spec]: https://tc39.es/ecma262/#sec-msfromtime\npub(crate) fn ms_from_time(t: f64) -> u16 {\n    // 1. Return 𝔽(ℝ(t) modulo ℝ(msPerSecond)).\n    t.rem_euclid(MS_PER_SECOND) as u16\n}\n\n/// Abstract operation `LocalTime ( t )`\n///\n/// More info:\n/// - [ECMAScript reference][spec]\n///\n/// [spec]: https://tc39.es/ecma262/#sec-localtime\npub(super) fn local_time(t: f64, hooks: &dyn HostHooks) -> f64 {\n    t + f64::from(local_timezone_offset_seconds(t, hooks)) * MS_PER_SECOND\n}\n\n/// Abstract operation `UTC ( t )`\n///\n/// More info:\n/// - [ECMAScript reference][spec]\n///\n/// [spec]: https://tc39.es/ecma262/#sec-utc-t\npub(super) fn utc_t(t: f64, hooks: &dyn HostHooks) -> f64 {\n    // 1. If t is not finite, return NaN.\n    if !t.is_finite() {\n        return f64::NAN;\n    }\n\n    t - f64::from(local_timezone_offset_seconds(t, hooks)) * MS_PER_SECOND\n}\n\n/// Abstract operation `MakeTime ( hour, min, sec, ms )`\n///\n/// More info:\n/// - [ECMAScript reference][spec]\n///\n/// [spec]: https://tc39.es/ecma262/#sec-maketime\npub(super) fn make_time(hour: f64, min: f64, sec: f64, ms: f64) -> f64 {\n    // 1. If hour is not finite, min is not finite, sec is not finite, or ms is not finite, return NaN.\n    if !hour.is_finite() || !min.is_finite() || !sec.is_finite() || !ms.is_finite() {\n        return f64::NAN;\n    }\n\n    // 2. Let h be 𝔽(! ToIntegerOrInfinity(hour)).\n    let h = hour.abs().floor().copysign(hour);\n\n    // 3. Let m be 𝔽(! ToIntegerOrInfinity(min)).\n    let m = min.abs().floor().copysign(min);\n\n    // 4. Let s be 𝔽(! ToIntegerOrInfinity(sec)).\n    let s = sec.abs().floor().copysign(sec);\n\n    // 5. Let milli be 𝔽(! ToIntegerOrInfinity(ms)).\n    let milli = ms.abs().floor().copysign(ms);\n\n    // 6. Return ((h × msPerHour + m × msPerMinute) + s × msPerSecond) + milli.\n    ((h * MS_PER_HOUR + m * MS_PER_MINUTE) + s * MS_PER_SECOND) + milli\n}\n\n/// Abstract operation `MakeDay ( year, month, date )`\n///\n/// More info:\n/// - [ECMAScript reference][spec]\n///\n/// [spec]: https://tc39.es/ecma262/#sec-makeday\npub(super) fn make_day(year: f64, month: f64, date: f64) -> f64 {\n    // 1. If year is not finite, month is not finite, or date is not finite, return NaN.\n    if !year.is_finite() || !month.is_finite() || !date.is_finite() {\n        return f64::NAN;\n    }\n\n    // 2. Let y be 𝔽(! ToIntegerOrInfinity(year)).\n    let y = year.abs().floor().copysign(year);\n\n    // 3. Let m be 𝔽(! ToIntegerOrInfinity(month)).\n    let m = month.abs().floor().copysign(month);\n\n    // 4. Let dt be 𝔽(! ToIntegerOrInfinity(date)).\n    let dt = date.abs().floor().copysign(date);\n\n    // 5. Let ym be y + 𝔽(floor(ℝ(m) / 12)).\n    let ym = y + (m / 12.0).floor();\n\n    // 6. If ym is not finite, return NaN.\n    if !ym.is_finite() {\n        return f64::NAN;\n    }\n\n    // 7. Let mn be 𝔽(ℝ(m) modulo 12).\n    let mn = m.rem_euclid(12.0) as u8;\n\n    // 8. Find a finite time value t such that YearFromTime(t) is ym, MonthFromTime(t) is mn,\n    //    and DateFromTime(t) is 1𝔽;\n    //    but if this is not possible (because some argument is out of range), return NaN.\n    let rest = if mn > 1 { 1.0 } else { 0.0 };\n    let days_within_year_to_end_of_month = match mn {\n        0 => 0.0,\n        1 => 31.0,\n        2 => 59.0,\n        3 => 90.0,\n        4 => 120.0,\n        5 => 151.0,\n        6 => 181.0,\n        7 => 212.0,\n        8 => 243.0,\n        9 => 273.0,\n        10 => 304.0,\n        11 => 334.0,\n        12 => 365.0,\n        _ => unreachable!(),\n    };\n    let t =\n        (day_from_year(ym + rest) - 365.0 * rest + days_within_year_to_end_of_month) * MS_PER_DAY;\n\n    // 9. Return Day(t) + dt - 1𝔽.\n    day(t) + dt - 1.0\n}\n\n/// Abstract operation `MakeDate ( day, time )`\n///\n/// More info:\n/// - [ECMAScript reference][spec]\n///\n/// [spec]: https://tc39.es/ecma262/#sec-makedate\npub(super) fn make_date(day: f64, time: f64) -> f64 {\n    // 1. If day is not finite or time is not finite, return NaN.\n    if !day.is_finite() || !time.is_finite() {\n        return f64::NAN;\n    }\n\n    // 2. Let tv be day × msPerDay + time.\n    let tv = day * MS_PER_DAY + time;\n\n    // 3. If tv is not finite, return NaN.\n    if !tv.is_finite() {\n        return f64::NAN;\n    }\n\n    // 4. Return tv.\n    tv\n}\n\n/// Abstract operation `MakeFullYear ( year )`\n///\n/// More info:\n/// - [ECMAScript reference][spec]\n///\n/// [spec]: https://tc39.es/ecma262/#sec-makefullyear\npub(super) fn make_full_year(year: f64) -> f64 {\n    // 1. If year is NaN, return NaN.\n    if year.is_nan() {\n        return f64::NAN;\n    }\n\n    // 2. Let truncated be ! ToIntegerOrInfinity(year).\n    let truncated = IntegerOrInfinity::from(year);\n\n    // 3. If truncated is in the inclusive interval from 0 to 99, return 1900𝔽 + 𝔽(truncated).\n    // 4. Return 𝔽(truncated).\n    match truncated {\n        IntegerOrInfinity::Integer(i) if (0..=99).contains(&i) => 1900.0 + i as f64,\n        IntegerOrInfinity::Integer(i) => i as f64,\n        IntegerOrInfinity::PositiveInfinity => f64::INFINITY,\n        IntegerOrInfinity::NegativeInfinity => f64::NEG_INFINITY,\n    }\n}\n\n/// Abstract operation `TimeClip ( time )`\n///\n/// More info:\n/// - [ECMAScript reference][spec]\n///\n/// [spec]: https://tc39.es/ecma262/#sec-timeclip\npub(crate) fn time_clip(time: f64) -> f64 {\n    // 1. If time is not finite, return NaN.\n    if !time.is_finite() {\n        return f64::NAN;\n    }\n\n    // 2. If abs(ℝ(time)) > 8.64 × 10**15, return NaN.\n    if time.abs() > 8.64e15 {\n        return f64::NAN;\n    }\n\n    // 3. Return 𝔽(! ToIntegerOrInfinity(time)).\n    let time = time.trunc();\n    if time.abs() == 0.0 {\n        return 0.0;\n    }\n\n    time\n}\n\n/// Abstract operation `TimeString ( tv )`\n///\n/// More info:\n/// - [ECMAScript reference][spec]\n///\n/// [spec]: https://tc39.es/ecma262/#sec-timestring\npub(super) fn time_string(tv: f64) -> JsString {\n    // 1. Let hour be ToZeroPaddedDecimalString(ℝ(HourFromTime(tv)), 2).\n    let mut binding = [0; 2];\n    let hour = pad_two(hour_from_time(tv), &mut binding);\n\n    // 2. Let minute be ToZeroPaddedDecimalString(ℝ(MinFromTime(tv)), 2).\n    let mut binding = [0; 2];\n    let minute = pad_two(min_from_time(tv), &mut binding);\n\n    // 3. Let second be ToZeroPaddedDecimalStringbindingFromTime(tv)), 2).\n    let mut binding = [0; 2];\n    let second = pad_two(sec_from_time(tv), &mut binding);\n\n    // 4. Return the string-concatenation of\n    //  hour,\n    //  \":\",\n    //  minute,\n    //  \":\",\n    //  second,\n    //  the code unit 0x0020 (SPACE),\n    //  and \"GMT\".\n    js_string!(\n        hour,\n        js_str!(\":\"),\n        minute,\n        js_str!(\":\"),\n        second,\n        js_str!(\" GMT\")\n    )\n}\n\n/// Abstract operation `DateString ( tv )`\n///\n/// More info:\n/// - [ECMAScript reference][spec]\n///\n/// [spec]: https://tc39.es/ecma262/#sec-datestring\npub(super) fn date_string(tv: f64) -> JsString {\n    // 1. Let weekday be the Name of the entry in Table 63 with the Number WeekDay(tv).\n    let weekday = match week_day(tv) {\n        0 => js_str!(\"Sun\"),\n        1 => js_str!(\"Mon\"),\n        2 => js_str!(\"Tue\"),\n        3 => js_str!(\"Wed\"),\n        4 => js_str!(\"Thu\"),\n        5 => js_str!(\"Fri\"),\n        6 => js_str!(\"Sat\"),\n        _ => unreachable!(),\n    };\n\n    // 2. Let month be the Name of the entry in Table 64 with the Number MonthFromTime(tv).\n    let month = match month_from_time(tv) {\n        0 => js_str!(\"Jan\"),\n        1 => js_str!(\"Feb\"),\n        2 => js_str!(\"Mar\"),\n        3 => js_str!(\"Apr\"),\n        4 => js_str!(\"May\"),\n        5 => js_str!(\"Jun\"),\n        6 => js_str!(\"Jul\"),\n        7 => js_str!(\"Aug\"),\n        8 => js_str!(\"Sep\"),\n        9 => js_str!(\"Oct\"),\n        10 => js_str!(\"Nov\"),\n        11 => js_str!(\"Dec\"),\n        _ => unreachable!(),\n    };\n\n    // 3. Let day be ToZeroPaddedDecimalString(ℝ(DateFromTime(tv)), 2).\n    let mut binding = [0; 2];\n    let day = pad_two(date_from_time(tv), &mut binding);\n\n    // 4. Let yv be YearFromTime(tv).\n    let yv = year_from_time(tv);\n\n    // 5. If yv is +0𝔽 or yv > +0𝔽, let yearSign be the empty String; otherwise, let yearSign be \"-\".\n    let year_sign = if yv >= 0 { js_str!(\"\") } else { js_str!(\"-\") };\n\n    // 6. Let paddedYear be ToZeroPaddedDecimalString(abs(ℝ(yv)), 4).\n    let yv = yv.unsigned_abs();\n    let padded_year: JsString = if yv >= 100_000 {\n        pad_six(yv, &mut [0; 6]).into()\n    } else if yv >= 10000 {\n        pad_five(yv, &mut [0; 5]).into()\n    } else {\n        pad_four(yv, &mut [0; 4]).into()\n    };\n\n    // 7. Return the string-concatenation of\n    // weekday,\n    // the code unit 0x0020 (SPACE),\n    // month,\n    // the code unit 0x0020 (SPACE),\n    // day,\n    // the code unit 0x0020 (SPACE),\n    // yearSign,\n    // and paddedYear.\n    js_string!(\n        weekday,\n        js_str!(\" \"),\n        month,\n        js_str!(\" \"),\n        day,\n        js_str!(\" \"),\n        year_sign,\n        &padded_year\n    )\n}\n\n/// Abstract operation `TimeZoneString ( tv )`\n///\n/// More info:\n/// - [ECMAScript reference][spec]\n///\n/// [spec]: https://tc39.es/ecma262/#sec-timezoneestring\npub(super) fn time_zone_string(t: f64, hooks: &dyn HostHooks) -> JsString {\n    // 1. Let systemTimeZoneIdentifier be SystemTimeZoneIdentifier().\n    // 2. If IsTimeZoneOffsetString(systemTimeZoneIdentifier) is true, then\n    //     a. Let offsetNs be ParseTimeZoneOffsetString(systemTimeZoneIdentifier).\n    // 3. Else,\n    //     a. Let offsetNs be GetNamedTimeZoneOffsetNanoseconds(systemTimeZoneIdentifier, ℤ(ℝ(tv) × 10**6)).\n    // 4. Let offset be 𝔽(truncate(offsetNs / 10**6)).\n    let offset = f64::from(local_timezone_offset_seconds(t, hooks)) * MS_PER_SECOND;\n    //let offset = hooks.local_timezone_offset_seconds((t / MS_PER_SECOND).floor() as i64);\n\n    // 5. If offset is +0𝔽 or offset > +0𝔽, then\n    let (offset_sign, abs_offset) = if offset >= 0.0 {\n        // a. Let offsetSign be \"+\".\n        // b. Let absOffset be offset.\n        (js_str!(\"+\"), offset)\n    }\n    // 6. Else,\n    else {\n        // a. Let offsetSign be \"-\".\n        // b. Let absOffset be -offset.\n        (js_str!(\"-\"), -offset)\n    };\n\n    // 7. Let offsetMin be ToZeroPaddedDecimalString(ℝ(MinFromTime(absOffset)), 2).\n    let mut binding = [0; 2];\n    let offset_min = pad_two(min_from_time(abs_offset), &mut binding);\n\n    // 8. Let offsetHour be ToZeroPaddedDecimalString(ℝ(HourFromTime(absOffset)), 2).\n    let mut binding = [0; 2];\n    let offset_hour = pad_two(hour_from_time(abs_offset), &mut binding);\n\n    // 9. Let tzName be an implementation-defined string that is either the empty String or the\n    // string-concatenation of the code unit 0x0020 (SPACE), the code unit 0x0028 (LEFT PARENTHESIS),\n    // an implementation-defined timezone name, and the code unit 0x0029 (RIGHT PARENTHESIS).\n    // 10. Return the string-concatenation of offsetSign, offsetHour, offsetMin, and tzName.\n    js_string!(offset_sign, offset_hour, offset_min)\n}\n\n/// Abstract operation `ToDateString ( tv )`\n///\n/// More info:\n/// - [ECMAScript reference][spec]\n///\n/// [spec]: https://tc39.es/ecma262/#sec-todatestring\npub(super) fn to_date_string_t(tv: f64, hooks: &dyn HostHooks) -> JsString {\n    // 1. If tv is NaN, return \"Invalid Date\".\n    if tv.is_nan() {\n        return js_string!(\"Invalid Date\");\n    }\n\n    // 2. Let t be LocalTime(tv).\n    let t = local_time(tv, hooks);\n\n    // 3. Return the string-concatenation of\n    // DateString(t),\n    // the code unit 0x0020 (SPACE),\n    // TimeString(t),\n    // and TimeZoneString(tv).\n    js_string!(\n        &date_string(t),\n        js_str!(\" \"),\n        &time_string(t),\n        &time_zone_string(t, hooks)\n    )\n}\n\nfn local_timezone_offset_seconds(t: f64, hooks: &dyn HostHooks) -> i32 {\n    let millis = t.rem_euclid(MS_PER_SECOND);\n    let seconds = ((t - millis) / MS_PER_SECOND) as i64;\n    hooks.local_timezone_offset_seconds(seconds)\n}\n\npub(super) fn pad_two(t: u8, output: &mut [u8; 2]) -> JsStr<'_> {\n    *output = if t < 10 {\n        [b'0', b'0' + t]\n    } else {\n        [b'0' + (t / 10), b'0' + (t % 10)]\n    };\n    debug_assert!(output.is_ascii());\n\n    JsStr::latin1(output)\n}\n\npub(super) fn pad_three(t: u16, output: &mut [u8; 3]) -> JsStr<'_> {\n    *output = [\n        b'0' + (t / 100) as u8,\n        b'0' + ((t / 10) % 10) as u8,\n        b'0' + (t % 10) as u8,\n    ];\n\n    JsStr::latin1(output)\n}\n\npub(super) fn pad_four(t: u32, output: &mut [u8; 4]) -> JsStr<'_> {\n    *output = [\n        b'0' + (t / 1000) as u8,\n        b'0' + ((t / 100) % 10) as u8,\n        b'0' + ((t / 10) % 10) as u8,\n        b'0' + (t % 10) as u8,\n    ];\n\n    JsStr::latin1(output)\n}\n\npub(super) fn pad_five(t: u32, output: &mut [u8; 5]) -> JsStr<'_> {\n    *output = [\n        b'0' + (t / 10_000) as u8,\n        b'0' + ((t / 1000) % 10) as u8,\n        b'0' + ((t / 100) % 10) as u8,\n        b'0' + ((t / 10) % 10) as u8,\n        b'0' + (t % 10) as u8,\n    ];\n\n    JsStr::latin1(output)\n}\n\npub(super) fn pad_six(t: u32, output: &mut [u8; 6]) -> JsStr<'_> {\n    *output = [\n        b'0' + (t / 100_000) as u8,\n        b'0' + ((t / 10_000) % 10) as u8,\n        b'0' + ((t / 1000) % 10) as u8,\n        b'0' + ((t / 100) % 10) as u8,\n        b'0' + ((t / 10) % 10) as u8,\n        b'0' + (t % 10) as u8,\n    ];\n\n    JsStr::latin1(output)\n}\n\n/// Parse a date string according to the steps specified in [`Date.parse`][spec].\n///\n/// We parse three different formats:\n/// - The [`Date Time String Format`][spec-format] specified in the spec: `YYYY-MM-DDTHH:mm:ss.sssZ`\n/// - The `toString` format: `Thu Jan 01 1970 00:00:00 GMT+0000`\n/// - The `toUTCString` format: `Thu, 01 Jan 1970 00:00:00 GMT`\n///\n/// [spec]: https://tc39.es/ecma262/#sec-date.parse\n/// [spec-format]: https://tc39.es/ecma262/#sec-date-time-string-format\npub(super) fn parse_date(date: &JsString, hooks: &dyn HostHooks) -> Option<i64> {\n    // All characters must be ASCII so we can return early if we find a non-ASCII character.\n    let owned_js_str = date.as_str();\n    let date = match owned_js_str.variant() {\n        JsStrVariant::Latin1(s) => {\n            if !s.is_ascii() {\n                return None;\n            }\n            // SAFETY: Since all characters are ASCII we can safely convert this into str.\n            Cow::Borrowed(unsafe { str::from_utf8_unchecked(s) })\n        }\n        JsStrVariant::Utf16(s) => {\n            let date = String::from_utf16(s).ok()?;\n            if !date.is_ascii() {\n                return None;\n            }\n            Cow::Owned(date)\n        }\n    };\n\n    // Date Time String Format: 'YYYY-MM-DDTHH:mm:ss.sssZ'\n    if let Some(dt) = DateParser::new(&date, hooks).parse() {\n        return Some(dt);\n    }\n\n    // `toString` format: `Thu Jan 01 1970 00:00:00 GMT+0000`\n    if let Ok(t) = OffsetDateTime::parse(\n        &date,\n        &format_description!(\n            \"[weekday repr:short] [month repr:short] [day] [year] [hour]:[minute]:[second] GMT[offset_hour sign:mandatory][offset_minute][end]\"\n        ),\n    ) {\n        return Some(t.unix_timestamp() * 1000 + i64::from(t.millisecond()));\n    }\n\n    // `toUTCString` format: `Thu, 01 Jan 1970 00:00:00 GMT`\n    if let Ok(t) = PrimitiveDateTime::parse(\n        &date,\n        &format_description!(\n            \"[weekday repr:short], [day] [month repr:short] [year] [hour]:[minute]:[second] GMT[end]\"\n        ),\n    ) {\n        let t = t.assume_utc();\n        return Some(t.unix_timestamp() * 1000 + i64::from(t.millisecond()));\n    }\n\n    None\n}\n\n/// Parses a date string according to the [`Date Time String Format`][spec].\n///\n/// [spec]: https://tc39.es/ecma262/#sec-date-time-string-format\nstruct DateParser<'a> {\n    hooks: &'a dyn HostHooks,\n    input: Peekable<Iter<'a, u8>>,\n    year: i32,\n    month: u32,\n    day: u32,\n    hour: u32,\n    minute: u32,\n    second: u32,\n    millisecond: u32,\n    offset: i64,\n}\n\n// Copied from https://github.com/RoDmitry/atoi_simd/blob/master/src/fallback.rs,\n// which is based on https://rust-malaysia.github.io/code/2020/07/11/faster-integer-parsing.html.\n#[doc(hidden)]\n#[allow(clippy::inline_always)]\npub(in crate::builtins::date) mod fast_atoi {\n    #[inline(always)]\n    pub(in crate::builtins::date) const fn process_8(mut val: u64, len: usize) -> u64 {\n        val <<= 64_usize.saturating_sub(len << 3); // << 3 - same as mult by 8\n        val = (val & 0x0F0F_0F0F_0F0F_0F0F).wrapping_mul(0xA01) >> 8;\n        val = (val & 0x00FF_00FF_00FF_00FF).wrapping_mul(0x64_0001) >> 16;\n        (val & 0x0000_FFFF_0000_FFFF).wrapping_mul(0x2710_0000_0001) >> 32\n    }\n\n    #[inline(always)]\n    pub(in crate::builtins::date) const fn process_4(mut val: u32, len: usize) -> u32 {\n        val <<= 32_usize.saturating_sub(len << 3); // << 3 - same as mult by 8\n        val = (val & 0x0F0F_0F0F).wrapping_mul(0xA01) >> 8;\n        (val & 0x00FF_00FF).wrapping_mul(0x64_0001) >> 16\n    }\n}\n\nimpl<'a> DateParser<'a> {\n    fn new(s: &'a str, hooks: &'a dyn HostHooks) -> Self {\n        Self {\n            hooks,\n            input: s.as_bytes().iter().peekable(),\n            year: 0,\n            month: 1,\n            day: 1,\n            hour: 0,\n            minute: 0,\n            second: 0,\n            millisecond: 0,\n            offset: 0,\n        }\n    }\n\n    fn next_expect(&mut self, expect: u8) -> Option<()> {\n        self.input\n            .next()\n            .and_then(|c| if *c == expect { Some(()) } else { None })\n    }\n\n    fn next_ascii_digit(&mut self) -> Option<u8> {\n        self.input\n            .next()\n            .and_then(|c| if c.is_ascii_digit() { Some(*c) } else { None })\n    }\n\n    fn next_n_ascii_digits<const N: usize>(&mut self) -> Option<[u8; N]> {\n        let mut digits = [0; N];\n        for digit in &mut digits {\n            *digit = self.next_ascii_digit()?;\n        }\n        Some(digits)\n    }\n\n    fn parse_n_ascii_digits<const N: usize>(&mut self) -> Option<u64> {\n        assert!(N <= 8, \"parse_n_ascii_digits parses no more than 8 digits\");\n        if N == 0 {\n            return None;\n        }\n        let ascii_digits = self.next_n_ascii_digits::<N>()?;\n        match N {\n            1..4 => {\n                // When N is small, process digits naively.\n                let mut res = 0;\n                for digit in ascii_digits {\n                    res = res * 10 + u64::from(digit & 0xF);\n                }\n                Some(res)\n            }\n            4 => {\n                // Process digits as an u32 block.\n                let mut src = [0; 4];\n                src[..N].copy_from_slice(&ascii_digits);\n                let val = u32::from_le_bytes(src);\n                Some(u64::from(fast_atoi::process_4(val, N)))\n            }\n            _ => {\n                // Process digits as an u64 block.\n                let mut src = [0; 8];\n                src[..N].copy_from_slice(&ascii_digits);\n                let val = u64::from_le_bytes(src);\n                Some(fast_atoi::process_8(val, N))\n            }\n        }\n    }\n\n    fn finish(&mut self) -> Option<i64> {\n        if self.input.peek().is_some() {\n            return None;\n        }\n\n        // Hour 24 is only valid as 24:00:00.000\n        if self.hour == 24 && (self.minute != 0 || self.second != 0 || self.millisecond != 0) {\n            return None;\n        }\n\n        let date = make_date(\n            make_day(self.year.into(), (self.month - 1).into(), self.day.into()),\n            make_time(\n                self.hour.into(),\n                self.minute.into(),\n                self.second.into(),\n                self.millisecond.into(),\n            ),\n        );\n\n        let date = date + (self.offset as f64) * MS_PER_MINUTE;\n\n        let t = time_clip(date);\n        if t.is_finite() { Some(t as i64) } else { None }\n    }\n\n    fn finish_local(&mut self) -> Option<i64> {\n        if self.input.peek().is_some() {\n            return None;\n        }\n\n        // Hour 24 is only valid as 24:00:00.000\n        if self.hour == 24 && (self.minute != 0 || self.second != 0 || self.millisecond != 0) {\n            return None;\n        }\n\n        let date = make_date(\n            make_day(self.year.into(), (self.month - 1).into(), self.day.into()),\n            make_time(\n                self.hour.into(),\n                self.minute.into(),\n                self.second.into(),\n                self.millisecond.into(),\n            ),\n        );\n\n        let t = time_clip(utc_t(date, self.hooks));\n        if t.is_finite() { Some(t as i64) } else { None }\n    }\n\n    #[allow(clippy::as_conversions)]\n    fn parse(&mut self) -> Option<i64> {\n        self.parse_year()?;\n        match self.input.peek() {\n            Some(b'T') => return self.parse_time(),\n            None => return self.finish(),\n            _ => {}\n        }\n        self.next_expect(b'-')?;\n        self.month = self.parse_n_ascii_digits::<2>()? as u32;\n        if self.month < 1 || self.month > 12 {\n            return None;\n        }\n        match self.input.peek() {\n            Some(b'T') => return self.parse_time(),\n            None => return self.finish(),\n            _ => {}\n        }\n        self.next_expect(b'-')?;\n        self.day = self.parse_n_ascii_digits::<2>()? as u32;\n        if self.day < 1 || self.day > 31 {\n            return None;\n        }\n        match self.input.peek() {\n            Some(b'T') => self.parse_time(),\n            _ => self.finish(),\n        }\n    }\n\n    #[allow(clippy::as_conversions)]\n    fn parse_year(&mut self) -> Option<()> {\n        if let &&sign @ (b'+' | b'-') = self.input.peek()? {\n            // Consume the sign.\n            self.input.next();\n            let year = self.parse_n_ascii_digits::<6>()? as i32;\n            let neg = sign == b'-';\n            if neg && year == 0 {\n                return None;\n            }\n            self.year = if neg { -year } else { year };\n        } else {\n            self.year = self.parse_n_ascii_digits::<4>()? as i32;\n        }\n        Some(())\n    }\n\n    #[allow(clippy::as_conversions)]\n    fn parse_time(&mut self) -> Option<i64> {\n        self.next_expect(b'T')?;\n        self.hour = self.parse_n_ascii_digits::<2>()? as u32;\n        if self.hour > 24 {\n            return None;\n        }\n        self.next_expect(b':')?;\n        self.minute = self.parse_n_ascii_digits::<2>()? as u32;\n        if self.minute > 59 {\n            return None;\n        }\n        match self.input.peek() {\n            Some(b':') => self.input.next(),\n            None => return self.finish_local(),\n            _ => {\n                self.parse_timezone()?;\n                return self.finish();\n            }\n        };\n        self.second = self.parse_n_ascii_digits::<2>()? as u32;\n        if self.second > 59 {\n            return None;\n        }\n        match self.input.peek() {\n            Some(b'.') => self.input.next(),\n            None => return self.finish_local(),\n            _ => {\n                self.parse_timezone()?;\n                return self.finish();\n            }\n        };\n        self.millisecond = self.parse_n_ascii_digits::<3>()? as u32;\n        if self.input.peek().is_some() {\n            self.parse_timezone()?;\n            self.finish()\n        } else {\n            self.finish_local()\n        }\n    }\n\n    #[allow(clippy::as_conversions)]\n    fn parse_timezone(&mut self) -> Option<()> {\n        match self.input.next() {\n            Some(b'Z') => return Some(()),\n            Some(sign @ (b'+' | b'-')) => {\n                let neg = *sign == b'-';\n                let offset_hour = self.parse_n_ascii_digits::<2>()? as i64;\n                if offset_hour > 23 {\n                    return None;\n                }\n                self.offset = if neg { offset_hour } else { -offset_hour } * 60;\n                if self.input.peek().is_none() {\n                    return Some(());\n                }\n                self.next_expect(b':')?;\n                let offset_minute = self.parse_n_ascii_digits::<2>()? as i64;\n                if offset_minute > 59 {\n                    return None;\n                }\n                self.offset += if neg { offset_minute } else { -offset_minute };\n            }\n            _ => return None,\n        }\n        Some(())\n    }\n}\n"
  },
  {
    "path": "core/engine/src/builtins/error/aggregate.rs",
    "content": "//! This module implements the global `AggregateError` object.\n//!\n//! More information:\n//!  - [MDN documentation][mdn]\n//!  - [ECMAScript reference][spec]\n//!\n//! [spec]: https://tc39.es/ecma262/#sec-aggregate-error\n//! [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/AggregateError\n\nuse crate::{\n    Context, JsArgs, JsExpect, JsResult, JsString, JsValue,\n    builtins::{\n        Array, BuiltInBuilder, BuiltInConstructor, BuiltInObject, IntrinsicObject,\n        iterable::IteratorHint,\n    },\n    context::intrinsics::{Intrinsics, StandardConstructor, StandardConstructors},\n    js_string,\n    object::{JsObject, internal_methods::get_prototype_from_constructor},\n    property::{Attribute, PropertyDescriptorBuilder},\n    realm::Realm,\n    string::StaticJsStrings,\n};\n\nuse super::{Error, ErrorKind};\n\n#[derive(Debug, Clone, Copy)]\npub(crate) struct AggregateError;\n\nimpl IntrinsicObject for AggregateError {\n    fn init(realm: &Realm) {\n        let attribute = Attribute::WRITABLE | Attribute::NON_ENUMERABLE | Attribute::CONFIGURABLE;\n        BuiltInBuilder::from_standard_constructor::<Self>(realm)\n            .prototype(realm.intrinsics().constructors().error().constructor())\n            .inherits(Some(realm.intrinsics().constructors().error().prototype()))\n            .property(js_string!(\"name\"), Self::NAME, attribute)\n            .property(js_string!(\"message\"), js_string!(), attribute)\n            .build();\n    }\n\n    fn get(intrinsics: &Intrinsics) -> JsObject {\n        Self::STANDARD_CONSTRUCTOR(intrinsics.constructors()).constructor()\n    }\n}\n\nimpl BuiltInObject for AggregateError {\n    const NAME: JsString = StaticJsStrings::AGGREGATE_ERROR;\n}\n\nimpl BuiltInConstructor for AggregateError {\n    const CONSTRUCTOR_ARGUMENTS: usize = 2;\n    const PROTOTYPE_STORAGE_SLOTS: usize = 2;\n    const CONSTRUCTOR_STORAGE_SLOTS: usize = 0;\n\n    const STANDARD_CONSTRUCTOR: fn(&StandardConstructors) -> &StandardConstructor =\n        StandardConstructors::aggregate_error;\n\n    /// [`AggregateError ( errors, message [ , options ] )`][spec]\n    ///\n    /// Creates a new aggregate error object.\n    ///\n    /// [spec]: AggregateError ( errors, message [ , options ] )\n    fn constructor(\n        new_target: &JsValue,\n        args: &[JsValue],\n        context: &mut Context,\n    ) -> JsResult<JsValue> {\n        // 1. If NewTarget is undefined, let newTarget be the active function object; else let newTarget be NewTarget.\n        let new_target = &if new_target.is_undefined() {\n            context\n                .active_function_object()\n                .unwrap_or_else(|| {\n                    context\n                        .intrinsics()\n                        .constructors()\n                        .aggregate_error()\n                        .constructor()\n                })\n                .into()\n        } else {\n            new_target.clone()\n        };\n        // 2. Let O be ? OrdinaryCreateFromConstructor(newTarget, \"%AggregateError.prototype%\", « [[ErrorData]] »).\n        let prototype = get_prototype_from_constructor(\n            new_target,\n            StandardConstructors::aggregate_error,\n            context,\n        )?;\n        let o = JsObject::from_proto_and_data_with_shared_shape(\n            context.root_shape(),\n            prototype,\n            Error::with_caller_position(ErrorKind::Aggregate, context),\n        )\n        .upcast();\n\n        // 3. If message is not undefined, then\n        let message = args.get_or_undefined(1);\n        if !message.is_undefined() {\n            // a. Let msg be ? ToString(message).\n            let msg = message.to_string(context)?;\n\n            // b. Perform CreateNonEnumerableDataPropertyOrThrow(O, \"message\", msg).\n            o.create_non_enumerable_data_property_or_throw(js_string!(\"message\"), msg, context);\n        }\n\n        // 4. Perform ? InstallErrorCause(O, options).\n        Error::install_error_cause(&o, args.get_or_undefined(2), context)?;\n\n        // 5. Let errorsList be ? IteratorToList(? GetIterator(errors, sync)).\n        let errors = args.get_or_undefined(0);\n        let errors_list = errors\n            .get_iterator(IteratorHint::Sync, context)?\n            .into_list(context)?;\n\n        // 6. Perform ! DefinePropertyOrThrow(O, \"errors\",\n        //    PropertyDescriptor {\n        //      [[Configurable]]: true,\n        //      [[Enumerable]]: false,\n        //      [[Writable]]: true,\n        //      [[Value]]: CreateArrayFromList(errorsList)\n        //    }).\n        o.define_property_or_throw(\n            js_string!(\"errors\"),\n            PropertyDescriptorBuilder::new()\n                .configurable(true)\n                .enumerable(false)\n                .writable(true)\n                .value(Array::create_array_from_list(errors_list, context))\n                .build(),\n            context,\n        )\n        .js_expect(\"should not fail according to spec\")?;\n\n        // 5. Return O.\n        Ok(o.into())\n    }\n}\n"
  },
  {
    "path": "core/engine/src/builtins/error/eval.rs",
    "content": "//! This module implements the global `EvalError` object.\n//!\n//! Indicates an error regarding the global `eval()` function.\n//! This exception is not thrown by JavaScript anymore, however\n//! the `EvalError` object remains for compatibility.\n//!\n//! More information:\n//!  - [MDN documentation][mdn]\n//!  - [ECMAScript reference][spec]\n//!\n//! [spec]: https://tc39.es/ecma262/#sec-native-error-types-used-in-this-standard-evalerror\n//! [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/EvalError\n\nuse crate::{\n    Context, JsResult, JsString, JsValue,\n    builtins::{BuiltInBuilder, BuiltInConstructor, BuiltInObject, IntrinsicObject},\n    context::intrinsics::{Intrinsics, StandardConstructor, StandardConstructors},\n    js_string,\n    object::JsObject,\n    property::Attribute,\n    realm::Realm,\n    string::StaticJsStrings,\n};\n\nuse super::{Error, ErrorKind};\n\n/// JavaScript `EvalError` implementation.\n#[derive(Debug, Clone, Copy)]\npub(crate) struct EvalError;\n\nimpl IntrinsicObject for EvalError {\n    fn init(realm: &Realm) {\n        let attribute = Attribute::WRITABLE | Attribute::NON_ENUMERABLE | Attribute::CONFIGURABLE;\n        BuiltInBuilder::from_standard_constructor::<Self>(realm)\n            .prototype(realm.intrinsics().constructors().error().constructor())\n            .inherits(Some(realm.intrinsics().constructors().error().prototype()))\n            .property(js_string!(\"name\"), Self::NAME, attribute)\n            .property(js_string!(\"message\"), js_string!(), attribute)\n            .build();\n    }\n\n    fn get(intrinsics: &Intrinsics) -> JsObject {\n        Self::STANDARD_CONSTRUCTOR(intrinsics.constructors()).constructor()\n    }\n}\n\nimpl BuiltInObject for EvalError {\n    const NAME: JsString = StaticJsStrings::EVAL_ERROR;\n}\n\nimpl BuiltInConstructor for EvalError {\n    const CONSTRUCTOR_ARGUMENTS: usize = 1;\n    const PROTOTYPE_STORAGE_SLOTS: usize = 2;\n    const CONSTRUCTOR_STORAGE_SLOTS: usize = 0;\n\n    const STANDARD_CONSTRUCTOR: fn(&StandardConstructors) -> &StandardConstructor =\n        StandardConstructors::eval_error;\n\n    /// Create a new error object.\n    fn constructor(\n        new_target: &JsValue,\n        args: &[JsValue],\n        context: &mut Context,\n    ) -> JsResult<JsValue> {\n        Error::native_error_constructor(\n            new_target,\n            args,\n            context,\n            ErrorKind::Eval,\n            StandardConstructors::eval_error,\n        )\n    }\n}\n"
  },
  {
    "path": "core/engine/src/builtins/error/mod.rs",
    "content": "//! Boa's implementation of ECMAScript's global `Error` object.\n//!\n//! Error objects are thrown when runtime errors occur.\n//! The Error object can also be used as a base object for user-defined exceptions.\n//!\n//! More information:\n//!  - [MDN documentation][mdn]\n//!  - [ECMAScript reference][spec]\n//!\n//! [spec]: https://tc39.es/ecma262/#sec-error-objects\n//! [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Error\n\nuse crate::{\n    Context, JsArgs, JsData, JsResult, JsString, JsValue,\n    builtins::BuiltInObject,\n    context::intrinsics::{Intrinsics, StandardConstructor, StandardConstructors},\n    error::{IgnoreEq, JsNativeError},\n    js_string,\n    object::{JsObject, internal_methods::get_prototype_from_constructor},\n    property::Attribute,\n    realm::Realm,\n    string::StaticJsStrings,\n    vm::shadow_stack::{Backtrace, ShadowEntry},\n};\nuse boa_gc::{Finalize, Trace};\nuse boa_macros::js_str;\n\npub(crate) mod aggregate;\npub(crate) mod eval;\npub(crate) mod range;\npub(crate) mod reference;\npub(crate) mod syntax;\npub(crate) mod r#type;\npub(crate) mod uri;\n\n#[cfg(test)]\nmod tests;\n\npub(crate) use self::aggregate::AggregateError;\npub(crate) use self::eval::EvalError;\npub(crate) use self::range::RangeError;\npub(crate) use self::reference::ReferenceError;\npub(crate) use self::syntax::SyntaxError;\npub(crate) use self::r#type::TypeError;\npub(crate) use self::uri::UriError;\n\nuse super::{BuiltInBuilder, BuiltInConstructor, IntrinsicObject};\n\n/// A tag of built-in `Error` object, [ECMAScript spec][spec].\n///\n/// [spec]: https://tc39.es/ecma262/#sec-error-objects\n#[derive(Debug, Copy, Clone, Eq, PartialEq, Trace, Finalize, JsData)]\n#[boa_gc(empty_trace)]\n#[non_exhaustive]\npub enum ErrorKind {\n    /// The `AggregateError` object type.\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-aggregate-error-objects\n    Aggregate,\n\n    /// The `Error` object type.\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-error-objects\n    Error,\n\n    /// The `EvalError` type.\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-native-error-types-used-in-this-standard-evalerror\n    Eval,\n\n    /// The `TypeError` type.\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-native-error-types-used-in-this-standard-typeerror\n    Type,\n\n    /// The `RangeError` type.\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-native-error-types-used-in-this-standard-rangeerror\n    Range,\n\n    /// The `ReferenceError` type.\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-native-error-types-used-in-this-standard-referenceerror\n    Reference,\n\n    /// The `SyntaxError` type.\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-native-error-types-used-in-this-standard-syntaxerror\n    Syntax,\n\n    /// The `URIError` type.\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-native-error-types-used-in-this-standard-urierror\n    Uri,\n}\n\n/// A built-in `Error` object, per the [ECMAScript spec][spec].\n///\n/// This is used internally to convert between [`JsObject`] and\n/// [`JsNativeError`] correctly, but it can also be used to manually create `Error`\n/// objects. However, the recommended way to create them is to construct a\n/// `JsNativeError` first, then call [`JsNativeError::into_opaque`],\n/// which will assign its prototype, properties and kind automatically.\n///\n/// For a description of every error kind and its usage, see\n/// [`JsNativeErrorKind`][crate::error::JsNativeErrorKind].\n///\n/// [spec]: https://tc39.es/ecma262/#sec-error-objects\n#[derive(Debug, Clone, PartialEq, Eq, Trace, Finalize, JsData)]\npub struct Error {\n    pub(crate) tag: ErrorKind,\n\n    // The position of where the Error was created does not affect equality check.\n    #[unsafe_ignore_trace]\n    pub(crate) position: IgnoreEq<Option<ShadowEntry>>,\n\n    // The backtrace captured when this error was thrown. Stored here so it\n    // survives the JsError → JsValue → JsError round-trip through promise\n    // rejection. Does not affect equality checks.\n    #[unsafe_ignore_trace]\n    pub(crate) backtrace: IgnoreEq<Option<Backtrace>>,\n}\n\nimpl Error {\n    /// Create a new [`Error`].\n    #[inline]\n    #[must_use]\n    pub fn new(tag: ErrorKind) -> Self {\n        Self {\n            tag,\n            position: IgnoreEq(None),\n            backtrace: IgnoreEq(None),\n        }\n    }\n\n    /// Create a new [`Error`] with the given optional [`ShadowEntry`].\n    pub(crate) fn with_shadow_entry(tag: ErrorKind, entry: Option<ShadowEntry>) -> Self {\n        Self {\n            tag,\n            position: IgnoreEq(entry),\n            backtrace: IgnoreEq(None),\n        }\n    }\n\n    /// Get the position from the last called function.\n    pub(crate) fn with_caller_position(tag: ErrorKind, context: &Context) -> Self {\n        Self {\n            tag,\n            position: IgnoreEq(context.vm.shadow_stack.caller_position()),\n            backtrace: IgnoreEq(None),\n        }\n    }\n}\n\nimpl IntrinsicObject for Error {\n    fn init(realm: &Realm) {\n        let attribute = Attribute::WRITABLE | Attribute::NON_ENUMERABLE | Attribute::CONFIGURABLE;\n        let builder = BuiltInBuilder::from_standard_constructor::<Self>(realm)\n            .property(js_string!(\"name\"), Self::NAME, attribute)\n            .property(js_string!(\"message\"), js_string!(), attribute)\n            .method(Self::to_string, js_string!(\"toString\"), 0);\n\n        #[cfg(feature = \"experimental\")]\n        let builder = builder.static_method(Error::is_error, js_string!(\"isError\"), 1);\n\n        builder.build();\n    }\n\n    fn get(intrinsics: &Intrinsics) -> JsObject {\n        Self::STANDARD_CONSTRUCTOR(intrinsics.constructors()).constructor()\n    }\n}\n\nimpl BuiltInObject for Error {\n    const NAME: JsString = StaticJsStrings::ERROR;\n}\n\nimpl BuiltInConstructor for Error {\n    const CONSTRUCTOR_ARGUMENTS: usize = 1;\n    const PROTOTYPE_STORAGE_SLOTS: usize = 3;\n    const CONSTRUCTOR_STORAGE_SLOTS: usize = 1;\n\n    const STANDARD_CONSTRUCTOR: fn(&StandardConstructors) -> &StandardConstructor =\n        StandardConstructors::error;\n\n    /// `Error( message [ , options ] )`\n    ///\n    /// Creates a new error object.\n    fn constructor(\n        new_target: &JsValue,\n        args: &[JsValue],\n        context: &mut Context,\n    ) -> JsResult<JsValue> {\n        // 1. If NewTarget is undefined, let newTarget be the active function object; else let newTarget be NewTarget.\n        let new_target = &if new_target.is_undefined() {\n            context\n                .active_function_object()\n                .unwrap_or_else(|| context.intrinsics().constructors().error().constructor())\n                .into()\n        } else {\n            new_target.clone()\n        };\n\n        // 2. Let O be ? OrdinaryCreateFromConstructor(newTarget, \"%Error.prototype%\", « [[ErrorData]] »).\n        let prototype =\n            get_prototype_from_constructor(new_target, StandardConstructors::error, context)?;\n        let o = JsObject::from_proto_and_data_with_shared_shape(\n            context.root_shape(),\n            prototype,\n            Error::with_caller_position(ErrorKind::Error, context),\n        )\n        .upcast();\n\n        // 3. If message is not undefined, then\n        let message = args.get_or_undefined(0);\n        if !message.is_undefined() {\n            // a. Let msg be ? ToString(message).\n            let msg = message.to_string(context)?;\n\n            // b. Perform CreateNonEnumerableDataPropertyOrThrow(O, \"message\", msg).\n            o.create_non_enumerable_data_property_or_throw(js_string!(\"message\"), msg, context);\n        }\n\n        // 4. Perform ? InstallErrorCause(O, options).\n        Self::install_error_cause(&o, args.get_or_undefined(1), context)?;\n\n        // 5. Return O.\n        Ok(o.into())\n    }\n}\n\nimpl Error {\n    pub(crate) fn install_error_cause(\n        o: &JsObject,\n        options: &JsValue,\n        context: &mut Context,\n    ) -> JsResult<()> {\n        // 1. If Type(options) is Object and ? HasProperty(options, \"cause\") is true, then\n        // 1.a. Let cause be ? Get(options, \"cause\").\n        if let Some(options) = options.as_object()\n            && let Some(cause) = options.try_get(js_string!(\"cause\"), context)?\n        {\n            // b. Perform CreateNonEnumerableDataPropertyOrThrow(O, \"cause\", cause).\n            o.create_non_enumerable_data_property_or_throw(js_string!(\"cause\"), cause, context);\n        }\n\n        // 2. Return unused.\n        Ok(())\n    }\n\n    /// `Error.prototype.toString()`\n    ///\n    /// The `toString()` method returns a string representing the specified Error object.\n    ///\n    /// More information:\n    ///  - [MDN documentation][mdn]\n    ///  - [ECMAScript reference][spec]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-error.prototype.tostring\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Error/toString\n    #[allow(clippy::wrong_self_convention)]\n    pub(crate) fn to_string(\n        this: &JsValue,\n        _: &[JsValue],\n        context: &mut Context,\n    ) -> JsResult<JsValue> {\n        // 1. Let O be the this value.\n        // 2. If Type(O) is not Object, throw a TypeError exception.\n        let o = this\n            .as_object()\n            .ok_or_else(|| JsNativeError::typ().with_message(\"'this' is not an Object\"))?;\n\n        // 3. Let name be ? Get(O, \"name\").\n        let name = o.get(js_string!(\"name\"), context)?;\n\n        // 4. If name is undefined, set name to \"Error\"; otherwise set name to ? ToString(name).\n        let name = if name.is_undefined() {\n            js_string!(\"Error\")\n        } else {\n            name.to_string(context)?\n        };\n\n        // 5. Let msg be ? Get(O, \"message\").\n        let msg = o.get(js_string!(\"message\"), context)?;\n\n        // 6. If msg is undefined, set msg to the empty String; otherwise set msg to ? ToString(msg).\n        let msg = if msg.is_undefined() {\n            js_string!()\n        } else {\n            msg.to_string(context)?\n        };\n\n        // 7. If name is the empty String, return msg.\n        if name.is_empty() {\n            return Ok(msg.into());\n        }\n\n        // 8. If msg is the empty String, return name.\n        if msg.is_empty() {\n            return Ok(name.into());\n        }\n\n        // 9. Return the string-concatenation of name, the code unit 0x003A (COLON),\n        // the code unit 0x0020 (SPACE), and msg.\n        Ok(js_string!(&name, js_str!(\": \"), &msg).into())\n    }\n\n    /// [`Error.isError`][spec].\n    ///\n    /// Returns a boolean indicating whether the argument is a built-in Error instance or not.\n    ///\n    /// [spec]: https://tc39.es/proposal-is-error/#sec-error.iserror\n    #[cfg(feature = \"experimental\")]\n    #[allow(clippy::unnecessary_wraps)]\n    fn is_error(_: &JsValue, args: &[JsValue], _: &mut Context) -> JsResult<JsValue> {\n        // 1. Return IsError(arg).\n\n        // https://tc39.es/proposal-is-error/#sec-iserror\n\n        // 1. If argument is not an Object, return false.\n        // 2. If argument has an [[ErrorData]] internal slot, return true.\n        // 3. Return false.\n        Ok(args\n            .get_or_undefined(0)\n            .as_object()\n            .is_some_and(|o| o.is::<Error>())\n            .into())\n    }\n\n    /// Shared constructor logic for all `NativeError` subtypes.\n    ///\n    /// Implements the [`NativeError ( message [ , options ] )`][spec] algorithm,\n    /// which is identical for `EvalError`, `RangeError`, `ReferenceError`,\n    /// `SyntaxError`, `TypeError`, and `URIError`.\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-nativeerror\n    pub(super) fn native_error_constructor(\n        new_target: &JsValue,\n        args: &[JsValue],\n        context: &mut Context,\n        error_kind: ErrorKind,\n        constructor_fn: fn(&StandardConstructors) -> &StandardConstructor,\n    ) -> JsResult<JsValue> {\n        let new_target = &if new_target.is_undefined() {\n            context\n                .active_function_object()\n                .unwrap_or_else(|| {\n                    constructor_fn(context.intrinsics().constructors()).constructor()\n                })\n                .into()\n        } else {\n            new_target.clone()\n        };\n\n        let prototype = get_prototype_from_constructor(new_target, constructor_fn, context)?;\n        let o = JsObject::from_proto_and_data_with_shared_shape(\n            context.root_shape(),\n            prototype,\n            Error::with_caller_position(error_kind, context),\n        )\n        .upcast();\n\n        let message = args.get_or_undefined(0);\n        if !message.is_undefined() {\n            let msg = message.to_string(context)?;\n            o.create_non_enumerable_data_property_or_throw(js_string!(\"message\"), msg, context);\n        }\n\n        Error::install_error_cause(&o, args.get_or_undefined(1), context)?;\n\n        Ok(o.into())\n    }\n}\n"
  },
  {
    "path": "core/engine/src/builtins/error/range.rs",
    "content": "//! This module implements the global `RangeError` object.\n//!\n//! Indicates a value that is not in the set or range of allowable values.\n//!\n//! More information:\n//!  - [MDN documentation][mdn]\n//!  - [ECMAScript reference][spec]\n//!\n//! [spec]: https://tc39.es/ecma262/#sec-native-error-types-used-in-this-standard-rangeerror\n//! [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RangeError\n\nuse crate::{\n    Context, JsResult, JsString, JsValue,\n    builtins::{BuiltInBuilder, BuiltInConstructor, BuiltInObject, IntrinsicObject},\n    context::intrinsics::{Intrinsics, StandardConstructor, StandardConstructors},\n    js_string,\n    object::JsObject,\n    property::Attribute,\n    realm::Realm,\n    string::StaticJsStrings,\n};\n\nuse super::{Error, ErrorKind};\n\n/// JavaScript `RangeError` implementation.\n#[derive(Debug, Clone, Copy)]\npub(crate) struct RangeError;\n\nimpl IntrinsicObject for RangeError {\n    fn init(realm: &Realm) {\n        let attribute = Attribute::WRITABLE | Attribute::NON_ENUMERABLE | Attribute::CONFIGURABLE;\n        BuiltInBuilder::from_standard_constructor::<Self>(realm)\n            .prototype(realm.intrinsics().constructors().error().constructor())\n            .inherits(Some(realm.intrinsics().constructors().error().prototype()))\n            .property(js_string!(\"name\"), Self::NAME, attribute)\n            .property(js_string!(\"message\"), js_string!(), attribute)\n            .build();\n    }\n\n    fn get(intrinsics: &Intrinsics) -> JsObject {\n        Self::STANDARD_CONSTRUCTOR(intrinsics.constructors()).constructor()\n    }\n}\n\nimpl BuiltInObject for RangeError {\n    const NAME: JsString = StaticJsStrings::RANGE_ERROR;\n}\n\nimpl BuiltInConstructor for RangeError {\n    const CONSTRUCTOR_ARGUMENTS: usize = 1;\n    const PROTOTYPE_STORAGE_SLOTS: usize = 2;\n    const CONSTRUCTOR_STORAGE_SLOTS: usize = 0;\n\n    const STANDARD_CONSTRUCTOR: fn(&StandardConstructors) -> &StandardConstructor =\n        StandardConstructors::range_error;\n\n    /// Create a new error object.\n    fn constructor(\n        new_target: &JsValue,\n        args: &[JsValue],\n        context: &mut Context,\n    ) -> JsResult<JsValue> {\n        Error::native_error_constructor(\n            new_target,\n            args,\n            context,\n            ErrorKind::Range,\n            StandardConstructors::range_error,\n        )\n    }\n}\n"
  },
  {
    "path": "core/engine/src/builtins/error/reference.rs",
    "content": "//! This module implements the global `ReferenceError` object.\n//!\n//! Indicates an error that occurs when de-referencing an invalid reference\n//!\n//! More information:\n//!  - [MDN documentation][mdn]\n//!  - [ECMAScript reference][spec]\n//!\n//! [spec]: https://tc39.es/ecma262/#sec-native-error-types-used-in-this-standard-referenceerror\n//! [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/ReferenceError\n\nuse crate::{\n    Context, JsResult, JsString, JsValue,\n    builtins::{BuiltInBuilder, BuiltInConstructor, BuiltInObject, IntrinsicObject},\n    context::intrinsics::{Intrinsics, StandardConstructor, StandardConstructors},\n    js_string,\n    object::JsObject,\n    property::Attribute,\n    realm::Realm,\n    string::StaticJsStrings,\n};\n\nuse super::{Error, ErrorKind};\n\n#[derive(Debug, Clone, Copy)]\npub(crate) struct ReferenceError;\n\nimpl IntrinsicObject for ReferenceError {\n    fn init(realm: &Realm) {\n        let attribute = Attribute::WRITABLE | Attribute::NON_ENUMERABLE | Attribute::CONFIGURABLE;\n        BuiltInBuilder::from_standard_constructor::<Self>(realm)\n            .prototype(realm.intrinsics().constructors().error().constructor())\n            .inherits(Some(realm.intrinsics().constructors().error().prototype()))\n            .property(js_string!(\"name\"), Self::NAME, attribute)\n            .property(js_string!(\"message\"), js_string!(), attribute)\n            .build();\n    }\n\n    fn get(intrinsics: &Intrinsics) -> JsObject {\n        Self::STANDARD_CONSTRUCTOR(intrinsics.constructors()).constructor()\n    }\n}\n\nimpl BuiltInObject for ReferenceError {\n    const NAME: JsString = StaticJsStrings::REFERENCE_ERROR;\n}\n\nimpl BuiltInConstructor for ReferenceError {\n    const CONSTRUCTOR_ARGUMENTS: usize = 1;\n    const PROTOTYPE_STORAGE_SLOTS: usize = 2;\n    const CONSTRUCTOR_STORAGE_SLOTS: usize = 0;\n\n    const STANDARD_CONSTRUCTOR: fn(&StandardConstructors) -> &StandardConstructor =\n        StandardConstructors::reference_error;\n\n    /// Create a new error object.\n    fn constructor(\n        new_target: &JsValue,\n        args: &[JsValue],\n        context: &mut Context,\n    ) -> JsResult<JsValue> {\n        Error::native_error_constructor(\n            new_target,\n            args,\n            context,\n            ErrorKind::Reference,\n            StandardConstructors::reference_error,\n        )\n    }\n}\n"
  },
  {
    "path": "core/engine/src/builtins/error/syntax.rs",
    "content": "//! This module implements the global `SyntaxError` object.\n//!\n//! The `SyntaxError` object represents an error when trying to interpret syntactically invalid code.\n//! It is thrown when the JavaScript context encounters tokens or token order that does not conform\n//! to the syntax of the language when parsing code.\n//!\n//! More information:\n//!  - [MDN documentation][mdn]\n//!  - [ECMAScript reference][spec]\n//!\n//! [spec]: https://tc39.es/ecma262/#sec-native-error-types-used-in-this-standard-syntaxerror\n//! [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/SyntaxError\n\nuse crate::{\n    Context, JsResult, JsString, JsValue,\n    builtins::{BuiltInBuilder, BuiltInConstructor, BuiltInObject, IntrinsicObject},\n    context::intrinsics::{Intrinsics, StandardConstructor, StandardConstructors},\n    js_string,\n    object::JsObject,\n    property::Attribute,\n    realm::Realm,\n    string::StaticJsStrings,\n};\n\nuse super::{Error, ErrorKind};\n\n/// JavaScript `SyntaxError` implementation.\n#[derive(Debug, Clone, Copy)]\npub(crate) struct SyntaxError;\n\nimpl IntrinsicObject for SyntaxError {\n    fn init(realm: &Realm) {\n        let attribute = Attribute::WRITABLE | Attribute::NON_ENUMERABLE | Attribute::CONFIGURABLE;\n        BuiltInBuilder::from_standard_constructor::<Self>(realm)\n            .prototype(realm.intrinsics().constructors().error().constructor())\n            .inherits(Some(realm.intrinsics().constructors().error().prototype()))\n            .property(js_string!(\"name\"), Self::NAME, attribute)\n            .property(js_string!(\"message\"), js_string!(), attribute)\n            .build();\n    }\n\n    fn get(intrinsics: &Intrinsics) -> JsObject {\n        Self::STANDARD_CONSTRUCTOR(intrinsics.constructors()).constructor()\n    }\n}\n\nimpl BuiltInObject for SyntaxError {\n    const NAME: JsString = StaticJsStrings::SYNTAX_ERROR;\n}\n\nimpl BuiltInConstructor for SyntaxError {\n    const CONSTRUCTOR_ARGUMENTS: usize = 1;\n    const PROTOTYPE_STORAGE_SLOTS: usize = 2;\n    const CONSTRUCTOR_STORAGE_SLOTS: usize = 0;\n\n    const STANDARD_CONSTRUCTOR: fn(&StandardConstructors) -> &StandardConstructor =\n        StandardConstructors::syntax_error;\n\n    /// Create a new error object.\n    fn constructor(\n        new_target: &JsValue,\n        args: &[JsValue],\n        context: &mut Context,\n    ) -> JsResult<JsValue> {\n        Error::native_error_constructor(\n            new_target,\n            args,\n            context,\n            ErrorKind::Syntax,\n            StandardConstructors::syntax_error,\n        )\n    }\n}\n"
  },
  {
    "path": "core/engine/src/builtins/error/tests.rs",
    "content": "use crate::{TestAction, run_test_actions};\nuse boa_macros::js_str;\nuse indoc::indoc;\n\n#[test]\nfn error_to_string() {\n    run_test_actions([\n        TestAction::assert_eq(\"(new Error('1')).toString()\", js_str!(\"Error: 1\")),\n        TestAction::assert_eq(\"(new RangeError('2')).toString()\", js_str!(\"RangeError: 2\")),\n        TestAction::assert_eq(\n            \"(new ReferenceError('3')).toString()\",\n            js_str!(\"ReferenceError: 3\"),\n        ),\n        TestAction::assert_eq(\n            \"(new SyntaxError('4')).toString()\",\n            js_str!(\"SyntaxError: 4\"),\n        ),\n        TestAction::assert_eq(\"(new TypeError('5')).toString()\", js_str!(\"TypeError: 5\")),\n        TestAction::assert_eq(\"(new EvalError('6')).toString()\", js_str!(\"EvalError: 6\")),\n        TestAction::assert_eq(\"(new URIError('7')).toString()\", js_str!(\"URIError: 7\")),\n        // no message\n        TestAction::assert_eq(\"(new Error()).toString()\", js_str!(\"Error\")),\n        TestAction::assert_eq(\"(new RangeError()).toString()\", js_str!(\"RangeError\")),\n        TestAction::assert_eq(\n            \"(new ReferenceError()).toString()\",\n            js_str!(\"ReferenceError\"),\n        ),\n        TestAction::assert_eq(\"(new SyntaxError()).toString()\", js_str!(\"SyntaxError\")),\n        TestAction::assert_eq(\"(new TypeError()).toString()\", js_str!(\"TypeError\")),\n        TestAction::assert_eq(\"(new EvalError()).toString()\", js_str!(\"EvalError\")),\n        TestAction::assert_eq(\"(new URIError()).toString()\", js_str!(\"URIError\")),\n        // no name\n        TestAction::assert_eq(\n            indoc! {r#\"\n                let message = new Error('message');\n                message.name = '';\n                message.toString()\n            \"#},\n            js_str!(\"message\"),\n        ),\n    ]);\n}\n\n#[test]\nfn error_names() {\n    run_test_actions([\n        TestAction::assert_eq(\"Error.name\", js_str!(\"Error\")),\n        TestAction::assert_eq(\"EvalError.name\", js_str!(\"EvalError\")),\n        TestAction::assert_eq(\"RangeError.name\", js_str!(\"RangeError\")),\n        TestAction::assert_eq(\"ReferenceError.name\", js_str!(\"ReferenceError\")),\n        TestAction::assert_eq(\"SyntaxError.name\", js_str!(\"SyntaxError\")),\n        TestAction::assert_eq(\"URIError.name\", js_str!(\"URIError\")),\n        TestAction::assert_eq(\"TypeError.name\", js_str!(\"TypeError\")),\n        TestAction::assert_eq(\"AggregateError.name\", js_str!(\"AggregateError\")),\n    ]);\n}\n\n#[test]\nfn error_lengths() {\n    run_test_actions([\n        TestAction::assert_eq(\"Error.length\", 1),\n        TestAction::assert_eq(\"EvalError.length\", 1),\n        TestAction::assert_eq(\"RangeError.length\", 1),\n        TestAction::assert_eq(\"ReferenceError.length\", 1),\n        TestAction::assert_eq(\"SyntaxError.length\", 1),\n        TestAction::assert_eq(\"URIError.length\", 1),\n        TestAction::assert_eq(\"TypeError.length\", 1),\n        TestAction::assert_eq(\"AggregateError.length\", 2),\n    ]);\n}\n"
  },
  {
    "path": "core/engine/src/builtins/error/type.rs",
    "content": "//! This module implements the global `TypeError` object.\n//!\n//! The `TypeError` object represents an error when an operation could not be performed,\n//! typically (but not exclusively) when a value is not of the expected type.\n//!\n//! A `TypeError` may be thrown when:\n//!  - an operand or argument passed to a function is incompatible with the type expected by that operator or function.\n//!  - when attempting to modify a value that cannot be changed.\n//!  - when attempting to use a value in an inappropriate way.\n//!\n//! More information:\n//!  - [MDN documentation][mdn]\n//!  - [ECMAScript reference][spec]\n//!\n//! [spec]: https://tc39.es/ecma262/#sec-native-error-types-used-in-this-standard-typeerror\n//! [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/TypeError\n\nuse crate::{\n    Context, JsResult, JsString, JsValue, NativeFunction,\n    builtins::{BuiltInBuilder, BuiltInConstructor, BuiltInObject, IntrinsicObject},\n    context::intrinsics::{Intrinsics, StandardConstructor, StandardConstructors},\n    error::JsNativeError,\n    js_string,\n    native_function::NativeFunctionObject,\n    object::JsObject,\n    property::Attribute,\n    realm::Realm,\n    string::StaticJsStrings,\n};\n\nuse super::{Error, ErrorKind};\n\n/// JavaScript `TypeError` implementation.\n#[derive(Debug, Clone, Copy)]\npub(crate) struct TypeError;\n\nimpl IntrinsicObject for TypeError {\n    fn init(realm: &Realm) {\n        let attribute = Attribute::WRITABLE | Attribute::NON_ENUMERABLE | Attribute::CONFIGURABLE;\n        BuiltInBuilder::from_standard_constructor::<Self>(realm)\n            .prototype(realm.intrinsics().constructors().error().constructor())\n            .inherits(Some(realm.intrinsics().constructors().error().prototype()))\n            .property(js_string!(\"name\"), Self::NAME, attribute)\n            .property(js_string!(\"message\"), js_string!(), attribute)\n            .build();\n    }\n\n    fn get(intrinsics: &Intrinsics) -> JsObject {\n        Self::STANDARD_CONSTRUCTOR(intrinsics.constructors()).constructor()\n    }\n}\n\nimpl BuiltInObject for TypeError {\n    const NAME: JsString = StaticJsStrings::TYPE_ERROR;\n}\n\nimpl BuiltInConstructor for TypeError {\n    const CONSTRUCTOR_ARGUMENTS: usize = 1;\n    const PROTOTYPE_STORAGE_SLOTS: usize = 2;\n    const CONSTRUCTOR_STORAGE_SLOTS: usize = 0;\n\n    const STANDARD_CONSTRUCTOR: fn(&StandardConstructors) -> &StandardConstructor =\n        StandardConstructors::type_error;\n\n    /// Create a new error object.\n    fn constructor(\n        new_target: &JsValue,\n        args: &[JsValue],\n        context: &mut Context,\n    ) -> JsResult<JsValue> {\n        Error::native_error_constructor(\n            new_target,\n            args,\n            context,\n            ErrorKind::Type,\n            StandardConstructors::type_error,\n        )\n    }\n}\n\n#[derive(Debug, Clone, Copy)]\npub(crate) struct ThrowTypeError;\n\nimpl IntrinsicObject for ThrowTypeError {\n    fn init(realm: &Realm) {\n        let obj = BuiltInBuilder::with_intrinsic::<Self>(realm)\n            .prototype(realm.intrinsics().constructors().function().prototype())\n            .static_property(StaticJsStrings::LENGTH, 0, Attribute::empty())\n            .static_property(js_string!(\"name\"), js_string!(), Attribute::empty())\n            .build();\n\n        {\n            let mut obj = obj\n                .downcast_mut::<NativeFunctionObject>()\n                .expect(\"`%ThrowTypeError%` must be a function\");\n            obj.f = NativeFunction::from_fn_ptr(|_, _, _| {\n                Err(JsNativeError::typ()\n                    .with_message(\n                        \"'caller', 'callee', and 'arguments' properties may not be accessed on strict mode \\\n                        functions or the arguments objects for calls to them\",\n                    )\n                    .into())\n            });\n            obj.name = js_string!();\n            obj.constructor = None;\n            obj.realm = Some(realm.clone());\n        }\n\n        obj.borrow_mut().extensible = false;\n    }\n\n    fn get(intrinsics: &Intrinsics) -> JsObject {\n        intrinsics.objects().throw_type_error().into()\n    }\n}\n"
  },
  {
    "path": "core/engine/src/builtins/error/uri.rs",
    "content": "//! This module implements the global `URIError` object.\n//!\n//! The `URIError` object represents an error when a global URI handling\n//! function was used in a wrong way.\n//!\n//! More information:\n//!  - [MDN documentation][mdn]\n//!  - [ECMAScript reference][spec]\n//!\n//! [spec]: https://tc39.es/ecma262/#sec-native-error-types-used-in-this-standard-urierror\n//! [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/URIError\n\nuse crate::{\n    Context, JsResult, JsString, JsValue,\n    builtins::{BuiltInBuilder, BuiltInConstructor, BuiltInObject, IntrinsicObject},\n    context::intrinsics::{Intrinsics, StandardConstructor, StandardConstructors},\n    js_string,\n    object::JsObject,\n    property::Attribute,\n    realm::Realm,\n    string::StaticJsStrings,\n};\n\nuse super::{Error, ErrorKind};\n\n/// JavaScript `URIError` implementation.\n#[derive(Debug, Clone, Copy)]\npub(crate) struct UriError;\n\nimpl IntrinsicObject for UriError {\n    fn init(realm: &Realm) {\n        let attribute = Attribute::WRITABLE | Attribute::NON_ENUMERABLE | Attribute::CONFIGURABLE;\n        BuiltInBuilder::from_standard_constructor::<Self>(realm)\n            .prototype(realm.intrinsics().constructors().error().constructor())\n            .inherits(Some(realm.intrinsics().constructors().error().prototype()))\n            .property(js_string!(\"name\"), Self::NAME, attribute)\n            .property(js_string!(\"message\"), js_string!(), attribute)\n            .build();\n    }\n\n    fn get(intrinsics: &Intrinsics) -> JsObject {\n        Self::STANDARD_CONSTRUCTOR(intrinsics.constructors()).constructor()\n    }\n}\n\nimpl BuiltInObject for UriError {\n    const NAME: JsString = StaticJsStrings::URI_ERROR;\n}\n\nimpl BuiltInConstructor for UriError {\n    const CONSTRUCTOR_ARGUMENTS: usize = 1;\n    const PROTOTYPE_STORAGE_SLOTS: usize = 2;\n    const CONSTRUCTOR_STORAGE_SLOTS: usize = 0;\n\n    const STANDARD_CONSTRUCTOR: fn(&StandardConstructors) -> &StandardConstructor =\n        StandardConstructors::uri_error;\n\n    /// Create a new error object.\n    fn constructor(\n        new_target: &JsValue,\n        args: &[JsValue],\n        context: &mut Context,\n    ) -> JsResult<JsValue> {\n        Error::native_error_constructor(\n            new_target,\n            args,\n            context,\n            ErrorKind::Uri,\n            StandardConstructors::uri_error,\n        )\n    }\n}\n"
  },
  {
    "path": "core/engine/src/builtins/escape/mod.rs",
    "content": "//! Boa's implementation of ECMAScript's string escaping functions.\n//!\n//! The `escape()` function replaces all characters with escape sequences, with the exception of ASCII\n//! word characters (A–Z, a–z, 0–9, _) and @*_+-./.\n//!\n//! The `unescape()` function replaces any escape sequence with the character that it represents.\n//!\n//! More information:\n//!  - [ECMAScript reference][spec]\n//!\n//! [spec]: https://tc39.es/ecma262/#sec-additional-properties-of-the-global-object\n\nuse crate::{\n    Context, JsArgs, JsObject, JsResult, JsString, JsValue, context::intrinsics::Intrinsics,\n    js_string, realm::Realm, string::StaticJsStrings,\n};\n\nuse super::{BuiltInBuilder, BuiltInObject, IntrinsicObject};\n\n/// The `escape` function\n#[derive(Debug, Clone, Copy)]\npub(crate) struct Escape;\n\nimpl IntrinsicObject for Escape {\n    fn init(realm: &Realm) {\n        BuiltInBuilder::callable_with_intrinsic::<Self>(realm, escape)\n            .name(Self::NAME)\n            .length(1)\n            .build();\n    }\n    fn get(intrinsics: &Intrinsics) -> JsObject {\n        intrinsics.objects().escape().into()\n    }\n}\n\nimpl BuiltInObject for Escape {\n    const NAME: JsString = StaticJsStrings::ESCAPE;\n}\n\n/// Builtin JavaScript `escape ( string )` function.\nfn escape(_: &JsValue, args: &[JsValue], context: &mut Context) -> JsResult<JsValue> {\n    /// Returns `true` if the codepoint `cp` is part of the `unescapedSet`.\n    fn is_unescaped(cp: u16) -> bool {\n        let Ok(cp) = TryInto::<u8>::try_into(cp) else {\n            return false;\n        };\n\n        // 4. Let unescapedSet be the string-concatenation of the ASCII word characters and \"@*+-./\".\n        cp.is_ascii_alphanumeric() || [b'_', b'@', b'*', b'+', b'-', b'.', b'/'].contains(&cp)\n    }\n\n    // 1. Set string to ? ToString(string).\n    let string = args.get_or_undefined(0).to_string(context)?;\n\n    // 3. Let R be the empty String.\n    let mut vec = Vec::with_capacity(string.len());\n\n    // 2. Let len be the length of string.\n    // 5. Let k be 0.\n    // 6. Repeat, while k < len,\n    //     a. Let C be the code unit at index k within string.\n    for cp in &string {\n        // b. If unescapedSet contains C, then\n        if is_unescaped(cp) {\n            // i. Let S be C.\n            vec.push(cp);\n            continue;\n        }\n        // c. Else,\n        //     i. Let n be the numeric value of C.\n        //     ii. If n < 256, then\n        let c = if cp < 256 {\n            //     1. Let hex be the String representation of n, formatted as an uppercase hexadecimal number.\n            //     2. Let S be the string-concatenation of \"%\" and ! StringPad(hex, 2𝔽, \"0\", start).\n            format!(\"%{cp:02X}\")\n        }\n        //     iii. Else,\n        else {\n            //     1. Let hex be the String representation of n, formatted as an uppercase hexadecimal number.\n            //     2. Let S be the string-concatenation of \"%u\" and ! StringPad(hex, 4𝔽, \"0\", start).\n            format!(\"%u{cp:04X}\")\n        };\n        // d. Set R to the string-concatenation of R and S.\n        // e. Set k to k + 1.\n        vec.extend(c.encode_utf16());\n    }\n\n    // 7. Return R.\n    Ok(js_string!(&vec[..]).into())\n}\n\n/// The `unescape` function\n#[derive(Debug, Clone, Copy)]\npub(crate) struct Unescape;\n\nimpl IntrinsicObject for Unescape {\n    fn init(realm: &Realm) {\n        BuiltInBuilder::callable_with_intrinsic::<Self>(realm, unescape)\n            .name(Self::NAME)\n            .length(1)\n            .build();\n    }\n    fn get(intrinsics: &Intrinsics) -> JsObject {\n        intrinsics.objects().unescape().into()\n    }\n}\n\nimpl BuiltInObject for Unescape {\n    const NAME: JsString = StaticJsStrings::UNESCAPE;\n}\n\n/// Builtin JavaScript `unescape ( string )` function.\nfn unescape(_: &JsValue, args: &[JsValue], context: &mut Context) -> JsResult<JsValue> {\n    /// Converts a char `cp` to its corresponding hex digit value.\n    fn to_hex_digit(cp: u16) -> Option<u16> {\n        char::from_u32(u32::from(cp))\n            .and_then(|c| c.to_digit(16))\n            .and_then(|d| d.try_into().ok())\n    }\n\n    // 1. Set string to ? ToString(string).\n    let string = args.get_or_undefined(0).to_string(context)?;\n\n    // 3. Let R be the empty String.\n    let mut vec = Vec::with_capacity(string.len());\n\n    let mut codepoints = <PeekableN<_, 6>>::new(string.iter());\n\n    // 2. Let len be the length of string.\n    // 4. Let k be 0.\n    // 5. Repeat, while k < len,\n    loop {\n        // a. Let C be the code unit at index k within string.\n        let Some(cp) = codepoints.next() else {\n            break;\n        };\n\n        // b. If C is the code unit 0x0025 (PERCENT SIGN), then\n        if cp != u16::from(b'%') {\n            vec.push(cp);\n            continue;\n        }\n\n        //     i. Let hexDigits be the empty String.\n        //     ii. Let optionalAdvance be 0.\n        // TODO: Try blocks :(\n        let Some(unescaped_cp) = (|| match *codepoints.peek_n(5) {\n            // iii. If k + 5 < len and the code unit at index k + 1 within string is the code unit\n            // 0x0075 (LATIN SMALL LETTER U), then\n            [u, n1, n2, n3, n4] if u == u16::from(b'u') => {\n                // 1. Set hexDigits to the substring of string from k + 2 to k + 6.\n                // 2. Set optionalAdvance to 5.\n                let n1 = to_hex_digit(n1)?;\n                let n2 = to_hex_digit(n2)?;\n                let n3 = to_hex_digit(n3)?;\n                let n4 = to_hex_digit(n4)?;\n\n                // TODO: https://github.com/rust-lang/rust/issues/77404\n                for _ in 0..5 {\n                    codepoints.next();\n                }\n\n                Some((n1 << 12) + (n2 << 8) + (n3 << 4) + n4)\n            }\n            // iv. Else if k + 3 ≤ len, then\n            [n1, n2, ..] => {\n                // 1. Set hexDigits to the substring of string from k + 1 to k + 3.\n                // 2. Set optionalAdvance to 2.\n                let n1 = to_hex_digit(n1)?;\n                let n2 = to_hex_digit(n2)?;\n\n                // TODO: https://github.com/rust-lang/rust/issues/77404\n                for _ in 0..2 {\n                    codepoints.next();\n                }\n\n                Some((n1 << 4) + n2)\n            }\n            _ => None,\n        })() else {\n            vec.push(u16::from(b'%'));\n            continue;\n        };\n\n        //     v. Let parseResult be ParseText(StringToCodePoints(hexDigits), HexDigits[~Sep]).\n        //     vi. If parseResult is a Parse Node, then\n        //         1. Let n be the MV of parseResult.\n        //         2. Set C to the code unit whose numeric value is n.\n        //         3. Set k to k + optionalAdvance.\n        // c. Set R to the string-concatenation of R and C.\n        // d. Set k to k + 1.\n        vec.push(unescaped_cp);\n    }\n    // 6. Return R.\n    Ok(js_string!(&vec[..]).into())\n}\n\n/// An iterator that can peek `N` items.\nstruct PeekableN<I, const N: usize>\nwhere\n    I: Iterator,\n{\n    iterator: I,\n    buffer: [I::Item; N],\n    buffered_end: usize,\n}\n\nimpl<I, const N: usize> PeekableN<I, N>\nwhere\n    I: Iterator,\n    I::Item: Default + Copy,\n{\n    /// Creates a new `PeekableN`.\n    fn new(iterator: I) -> Self {\n        Self {\n            iterator,\n            buffer: [I::Item::default(); N],\n            buffered_end: 0,\n        }\n    }\n\n    /// Peeks `n` items from the iterator.\n    fn peek_n(&mut self, count: usize) -> &[I::Item] {\n        if count <= self.buffered_end {\n            return &self.buffer[..count];\n        }\n        for _ in 0..(count - self.buffered_end) {\n            let Some(next) = self.iterator.next() else {\n                return &self.buffer[..self.buffered_end];\n            };\n            self.buffer[self.buffered_end] = next;\n            self.buffered_end += 1;\n        }\n\n        &self.buffer[..count]\n    }\n}\n\nimpl<I, const N: usize> Iterator for PeekableN<I, N>\nwhere\n    I: Iterator,\n    I::Item: Copy,\n{\n    type Item = I::Item;\n\n    fn next(&mut self) -> Option<Self::Item> {\n        if self.buffered_end > 0 {\n            let item = self.buffer[0];\n            self.buffer.rotate_left(1);\n            self.buffered_end -= 1;\n            return Some(item);\n        }\n        self.iterator.next()\n    }\n}\n"
  },
  {
    "path": "core/engine/src/builtins/eval/mod.rs",
    "content": "//! Boa's implementation of ECMAScript's global `eval` function.\n//!\n//! The `eval()` function evaluates ECMAScript code represented as a string.\n//!\n//! More information:\n//!  - [ECMAScript reference][spec]\n//!  - [MDN documentation][mdn]\n//!\n//! [spec]: https://tc39.es/ecma262/#sec-eval-x\n//! [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/eval\n\nuse crate::{\n    Context, JsArgs, JsExpect, JsResult, JsString, JsValue, SpannedSourceText,\n    builtins::{BuiltInObject, function::OrdinaryFunction},\n    bytecompiler::{ByteCompiler, prepare_eval_declaration_instantiation},\n    context::intrinsics::Intrinsics,\n    environments::SavedEnvironments,\n    error::JsNativeError,\n    js_string,\n    object::JsObject,\n    realm::Realm,\n    spanned_source_text::SourceText,\n    string::StaticJsStrings,\n    vm::{CallFrame, CallFrameFlags, source_info::SourcePath},\n};\nuse boa_ast::{\n    operations::{ContainsSymbol, contains, contains_arguments},\n    scope::Scope,\n};\nuse boa_gc::Gc;\nuse boa_parser::{Parser, Source};\n\nuse super::{BuiltInBuilder, IntrinsicObject};\n\n#[derive(Debug, Clone, Copy)]\npub(crate) struct Eval;\n\nimpl IntrinsicObject for Eval {\n    fn init(realm: &Realm) {\n        BuiltInBuilder::callable_with_intrinsic::<Self>(realm, Self::eval)\n            .name(Self::NAME)\n            .length(1)\n            .build();\n    }\n\n    fn get(intrinsics: &Intrinsics) -> JsObject {\n        intrinsics.objects().eval().into()\n    }\n}\n\nimpl BuiltInObject for Eval {\n    const NAME: JsString = StaticJsStrings::EVAL;\n}\n\nimpl Eval {\n    /// `19.2.1 eval ( x )`\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-eval-x\n    fn eval(_: &JsValue, args: &[JsValue], context: &mut Context) -> JsResult<JsValue> {\n        // 1. Return ? PerformEval(x, false, false).\n        Self::perform_eval(args.get_or_undefined(0), false, None, false, context)\n    }\n\n    /// `19.2.1.1 PerformEval ( x, strictCaller, direct )`\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-performeval\n    pub(crate) fn perform_eval(\n        x: &JsValue,\n        direct: bool,\n        lexical_scope: Option<Scope>,\n        mut strict: bool,\n        context: &mut Context,\n    ) -> JsResult<JsValue> {\n        bitflags::bitflags! {\n            /// Flags used to throw early errors on invalid `eval` calls.\n            #[derive(Default)]\n            struct Flags: u8 {\n                const IN_FUNCTION = 0b0001;\n                const IN_METHOD = 0b0010;\n                const IN_DERIVED_CONSTRUCTOR = 0b0100;\n                const IN_CLASS_FIELD_INITIALIZER = 0b1000;\n            }\n        }\n\n        /// Possible actions that can be executed after exiting this function to restore the environment to its\n        /// original state.\n        enum EnvStackAction {\n            Truncate(usize),\n            Restore(SavedEnvironments),\n        }\n\n        // 1. Assert: If direct is false, then strictCaller is also false.\n        debug_assert!(direct || !strict);\n\n        // 2. If Type(x) is not String, return x.\n        // TODO: rework parser to take an iterator of `u32` unicode codepoints\n        let Some(x) = x.as_string() else {\n            return Ok(x.clone());\n        };\n\n        // Because of implementation details the following code differs from the spec.\n\n        // 3. Let evalRealm be the current Realm Record.\n        // 4. NOTE: In the case of a direct eval, evalRealm is the realm of both the caller of eval\n        // and of the eval function itself.\n        let eval_realm = context.realm().clone();\n\n        // 5. Perform ? HostEnsureCanCompileStrings(evalRealm, « », x, direct).\n        context\n            .host_hooks()\n            .ensure_can_compile_strings(eval_realm, &[], &x, direct, context)?;\n\n        // 11. Perform the following substeps in an implementation-defined order, possibly interleaving parsing and error detection:\n        //     a. Let script be ParseText(StringToCodePoints(x), Script).\n        //     b. If script is a List of errors, throw a SyntaxError exception.\n        //     c. If script Contains ScriptBody is false, return undefined.\n        //     d. Let body be the ScriptBody of script.\n        let x = x.to_vec();\n        let source = Source::from_utf16(&x);\n\n        let mut parser = Parser::new(source);\n        parser.set_identifier(context.next_parser_identifier());\n        if strict {\n            parser.set_strict();\n        }\n        let (mut body, source) = parser.parse_eval(direct, context.interner_mut())?;\n\n        // 6. Let inFunction be false.\n        // 7. Let inMethod be false.\n        // 8. Let inDerivedConstructor be false.\n        // 9. Let inClassFieldInitializer be false.\n        // a. Let thisEnvRec be GetThisEnvironment().\n        let flags = match {\n            let frame = context.vm.frame();\n            frame\n                .environments\n                .get_this_environment(frame.realm.environment())\n        }\n        .as_function()\n        {\n            // 10. If direct is true, then\n            //     b. If thisEnvRec is a Function Environment Record, then\n            Some(function_env) if direct => {\n                // i. Let F be thisEnvRec.[[FunctionObject]].\n                let function_object = function_env\n                    .slots()\n                    .function_object()\n                    .downcast_ref::<OrdinaryFunction>()\n                    .js_expect(\"must be function object\")?;\n\n                // ii. Set inFunction to true.\n                let mut flags = Flags::IN_FUNCTION;\n\n                // iii. Set inMethod to thisEnvRec.HasSuperBinding().\n                if function_env.has_super_binding() {\n                    flags |= Flags::IN_METHOD;\n                }\n\n                // iv. If F.[[ConstructorKind]] is derived, set inDerivedConstructor to true.\n                if function_object.is_derived_constructor() {\n                    flags |= Flags::IN_DERIVED_CONSTRUCTOR;\n                }\n\n                // v. Let classFieldInitializerName be F.[[ClassFieldInitializerName]].\n                // vi. If classFieldInitializerName is not empty, set inClassFieldInitializer to true.\n                if function_object.in_class_field_initializer() {\n                    flags |= Flags::IN_CLASS_FIELD_INITIALIZER;\n                }\n\n                flags\n            }\n            _ => Flags::default(),\n        };\n\n        if !flags.contains(Flags::IN_FUNCTION) && contains(&body, ContainsSymbol::NewTarget) {\n            return Err(JsNativeError::syntax()\n                .with_message(\"invalid `new.target` expression inside eval\")\n                .into());\n        }\n        if !flags.contains(Flags::IN_METHOD) && contains(&body, ContainsSymbol::SuperProperty) {\n            return Err(JsNativeError::syntax()\n                .with_message(\"invalid `super` reference inside eval\")\n                .into());\n        }\n        if !flags.contains(Flags::IN_DERIVED_CONSTRUCTOR)\n            && contains(&body, ContainsSymbol::SuperCall)\n        {\n            return Err(JsNativeError::syntax()\n                .with_message(\"invalid `super` call inside eval\")\n                .into());\n        }\n        if flags.contains(Flags::IN_CLASS_FIELD_INITIALIZER) && contains_arguments(&body) {\n            return Err(JsNativeError::syntax()\n                .with_message(\"invalid `arguments` reference inside eval\")\n                .into());\n        }\n\n        strict |= body.strict();\n\n        // Because our environment model does not map directly to the spec, this section looks very different.\n        // 12 - 13 are implicit in the call of `Context::compile_with_new_declarative`.\n        // 14 - 33 are in the following section, together with EvalDeclarationInstantiation.\n        let action = if direct {\n            // If the call to eval is direct, the code is executed in the current environment.\n\n            // Poison the last parent function environment, because it may contain new declarations after/during eval.\n            if !strict {\n                {\n                    let frame = context.vm.frame_mut();\n                    let global = frame.realm.environment();\n                    frame.environments.poison_until_last_function(global);\n                }\n            }\n\n            // Set the compile time environment to the current running environment and save the number of current environments.\n            let environments_len = context.vm.frame().environments.len();\n\n            // Pop any added runtime environments that were not removed during the eval execution.\n            EnvStackAction::Truncate(environments_len)\n        } else {\n            // If the call to eval is indirect, the code is executed in the global environment.\n\n            // Pop all environments before the eval execution.\n            let saved = context.vm.frame_mut().environments.pop_to_global();\n\n            // Restore all environments to the state from before the eval execution.\n            EnvStackAction::Restore(saved)\n        };\n\n        let context = &mut context.guard(move |ctx| match action {\n            EnvStackAction::Truncate(len) => ctx.vm.frame_mut().environments.truncate(len),\n            EnvStackAction::Restore(saved) => {\n                ctx.vm.frame_mut().environments.truncate(0);\n                ctx.vm.frame_mut().environments.restore_from_saved(saved);\n            }\n        });\n\n        let (var_environment, mut variable_scope) =\n            if let Some(e) = context.vm.frame().environments.outer_function_environment() {\n                (e.0, e.1)\n            } else {\n                (\n                    context.realm().environment().clone(),\n                    context.realm().scope().clone(),\n                )\n            };\n\n        let lexical_scope = lexical_scope.unwrap_or(context.realm().scope().clone());\n        let lexical_scope = Scope::new(lexical_scope, strict);\n\n        let mut annex_b_function_names = Vec::new();\n\n        prepare_eval_declaration_instantiation(\n            &mut annex_b_function_names,\n            &body,\n            strict,\n            if strict {\n                &lexical_scope\n            } else {\n                &variable_scope\n            },\n            &lexical_scope,\n            context,\n        )?;\n\n        let in_with = context.vm.frame().environments.has_object_environment();\n\n        let source_text = SourceText::new(source);\n        let spanned_source_text = SpannedSourceText::new_source_only(source_text);\n\n        let mut compiler = ByteCompiler::new(\n            js_string!(\"<eval>\"),\n            body.strict(),\n            false,\n            variable_scope.clone(),\n            lexical_scope.clone(),\n            false,\n            false,\n            context.interner_mut(),\n            in_with,\n            spanned_source_text,\n            // TODO: Could give more information from previous shadow stack.\n            SourcePath::Eval,\n        );\n\n        // Increments the number of open environments to\n        // account for the environment scope pushed to\n        // the context at the end of `perform_eval`.\n        compiler.current_open_environments_count += 1;\n\n        if strict {\n            variable_scope = lexical_scope.clone();\n            compiler.variable_scope = lexical_scope.clone();\n        }\n\n        #[cfg(feature = \"annex-b\")]\n        {\n            compiler\n                .annex_b_function_names\n                .clone_from(&annex_b_function_names);\n        }\n\n        let bindings = body\n            .analyze_scope_eval(\n                strict,\n                &variable_scope,\n                &lexical_scope,\n                &annex_b_function_names,\n                compiler.interner(),\n            )\n            .map_err(|e| JsNativeError::syntax().with_message(e))?;\n\n        compiler.eval_declaration_instantiation(&body, strict, &variable_scope, bindings);\n\n        compiler.compile_statement_list(body.statements(), true, false);\n\n        let code_block = Gc::new(compiler.finish());\n\n        // Strict calls don't need extensions, since all strict eval calls push a new\n        // function environment before evaluating.\n        if !strict {\n            var_environment.extend_from_compile();\n        }\n\n        let env_fp = context.vm.frame().environments.len() as u32;\n        let environments = context.vm.frame().environments.clone();\n        let realm = context.realm().clone();\n        context.vm.push_frame_with_stack(\n            CallFrame::new(code_block.clone(), None, environments, realm)\n                .with_env_fp(env_fp)\n                .with_flags(CallFrameFlags::EXIT_EARLY),\n            JsValue::undefined(),\n            JsValue::null(),\n        );\n\n        context.realm().resize_global_env();\n\n        // Pushing the scope here ensures any function objects created\n        // in `eval_declaration_instantiation` get their proper\n        // environment stack.\n        {\n            let frame = context.vm.frame_mut();\n            let global = frame.realm.environment();\n            frame\n                .environments\n                .push_lexical(lexical_scope.num_bindings_non_local(), global);\n        }\n\n        context\n            .eval_declaration_instantiation(&code_block)\n            .inspect_err(|_| {\n                context.vm.pop_frame();\n            })?;\n\n        let record = context.run();\n        context.vm.pop_frame();\n\n        record.consume()\n    }\n}\n"
  },
  {
    "path": "core/engine/src/builtins/finalization_registry/mod.rs",
    "content": "//! Boa's implementation of ECMAScript's `FinalizationRegistry` object.\n\nuse std::{\n    cell::{Cell, RefCell},\n    slice,\n};\n\nuse boa_gc::{Ephemeron, Finalize, Gc, Trace, WeakGc};\n\nuse crate::{\n    Context, JsArgs, JsData, JsObject, JsResult, JsSymbol, JsValue, JsVariant,\n    context::intrinsics::{Intrinsics, StandardConstructor, StandardConstructors},\n    job::{Job, JobCallback, NativeAsyncJob},\n    js_error, js_string,\n    object::{\n        ErasedVTableObject, JsFunction, VTableObject,\n        internal_methods::get_prototype_from_constructor,\n    },\n    property::Attribute,\n    realm::Realm,\n    string::StaticJsStrings,\n};\n\nuse super::{BuiltInConstructor, BuiltInObject, IntrinsicObject, builder::BuiltInBuilder};\n\n#[cfg(test)]\nmod tests;\n\n/// On GG collection, sends a message to a [`FinalizationRegistry`] indicating that it needs to\n/// be collected.\n#[derive(Trace)]\nstruct CleanupSignaler(#[unsafe_ignore_trace] Cell<Option<async_channel::WeakSender<()>>>);\n\nimpl Finalize for CleanupSignaler {\n    fn finalize(&self) {\n        if let Some(sender) = self.0.take()\n            && let Some(sender) = sender.upgrade()\n        {\n            // We don't need to handle errors:\n            // - If the channel is full, the `FinalizationRegistry` has already\n            //   been enqueued for cleanup.\n            // - If the channel is closed, the `FinalizationRegistry` was\n            //   GC'd, so we don't need to worry about cleanups.\n            let _ = sender.try_send(());\n        }\n    }\n}\n\n///  A cell tracked by a [`FinalizationRegistry`].\n#[derive(Trace, Finalize)]\npub(crate) struct RegistryCell {\n    target: Ephemeron<ErasedVTableObject, CleanupSignaler>,\n    held_value: JsValue,\n    unregister_token: Option<WeakGc<ErasedVTableObject>>,\n}\n\n/// Boa's implementation of ECMAScript's [`FinalizationRegistry`] builtin object.\n///\n/// `FinalizationRegistry` provides a way to request that a cleanup callback get called at some point\n/// when a value registered with the registry has been reclaimed (garbage-collected).\n///\n/// [`FinalizationRegistry`]: https://tc39.es/ecma262/#sec-finalization-registry-objects\n#[derive(Trace, Finalize, JsData)]\npub(crate) struct FinalizationRegistry {\n    realm: Realm,\n    callback: JobCallback,\n    #[unsafe_ignore_trace]\n    cleanup_notifier: async_channel::Sender<()>,\n    cells: Vec<RegistryCell>,\n}\n\nimpl IntrinsicObject for FinalizationRegistry {\n    fn get(intrinsics: &Intrinsics) -> JsObject {\n        Self::STANDARD_CONSTRUCTOR(intrinsics.constructors()).constructor()\n    }\n\n    fn init(realm: &Realm) {\n        BuiltInBuilder::from_standard_constructor::<Self>(realm)\n            .property(\n                JsSymbol::to_string_tag(),\n                js_string!(\"FinalizationRegistry\"),\n                Attribute::CONFIGURABLE,\n            )\n            .method(Self::register, js_string!(\"register\"), 2)\n            .method(Self::unregister, js_string!(\"unregister\"), 1)\n            .build();\n    }\n}\n\nimpl BuiltInObject for FinalizationRegistry {\n    const NAME: crate::JsString = StaticJsStrings::FINALIZATION_REGISTRY;\n\n    const ATTRIBUTE: Attribute = Attribute::WRITABLE.union(Attribute::CONFIGURABLE);\n}\n\nimpl BuiltInConstructor for FinalizationRegistry {\n    const CONSTRUCTOR_ARGUMENTS: usize = 1;\n    const CONSTRUCTOR_STORAGE_SLOTS: usize = 0;\n    const PROTOTYPE_STORAGE_SLOTS: usize = 3;\n\n    const STANDARD_CONSTRUCTOR: fn(&StandardConstructors) -> &StandardConstructor =\n        StandardConstructors::finalization_registry;\n\n    /// Constructor [`FinalizationRegistry ( cleanupCallback )`][cons]\n    ///\n    /// [cons]: https://tc39.es/ecma262/#sec-finalization-registry-cleanup-callback\n    fn constructor(\n        new_target: &JsValue,\n        args: &[JsValue],\n        context: &mut Context,\n    ) -> JsResult<JsValue> {\n        // 1. If NewTarget is undefined, throw a TypeError exception.\n        if new_target.is_undefined() {\n            return Err(js_error!(\n                TypeError: \"FinalizationRegistry: cannot call constructor without `new`\"\n            ));\n        }\n\n        // 2. If IsCallable(cleanupCallback) is false, throw a TypeError exception.\n        let callback = args\n            .get_or_undefined(0)\n            .as_object()\n            .and_then(JsFunction::from_object)\n            .ok_or_else(|| {\n                js_error!(\n                    TypeError: \"FinalizationRegistry: \\\n                        cleanup callback of registry must be callable\"\n                )\n            })?;\n\n        // 3. Let finalizationRegistry be ? OrdinaryCreateFromConstructor(NewTarget,\n        //    \"%FinalizationRegistry.prototype%\", « [[Realm]], [[CleanupCallback]], [[Cells]] »).\n        let prototype = get_prototype_from_constructor(\n            new_target,\n            StandardConstructors::finalization_registry,\n            context,\n        )?;\n\n        // 4. Let fn be the active function object.\n        // 5. Set finalizationRegistry.[[Realm]] to fn.[[Realm]].\n        let realm = context.vm.frame().realm.clone();\n\n        // 6. Set finalizationRegistry.[[CleanupCallback]] to HostMakeJobCallback(cleanupCallback).\n        let callback = context.host_hooks().make_job_callback(callback, context);\n\n        // 7. Set finalizationRegistry.[[Cells]] to a new empty List.\n        let cells = Vec::new();\n\n        let (sender, receiver) = async_channel::bounded(1);\n\n        let registry = JsObject::new_unique(\n            prototype,\n            FinalizationRegistry {\n                realm,\n                callback,\n                cells,\n                cleanup_notifier: sender,\n            },\n        );\n\n        let weak_registry = WeakGc::new(registry.inner());\n\n        {\n            async fn inner_cleanup(\n                weak_registry: WeakGc<VTableObject<FinalizationRegistry>>,\n                receiver: async_channel::Receiver<()>,\n                context: &RefCell<&mut Context>,\n            ) -> JsResult<JsValue> {\n                let Ok(()) = receiver.recv().await else {\n                    return Ok(JsValue::undefined());\n                };\n\n                let Some(registry) = weak_registry.upgrade().map(JsObject::from_inner) else {\n                    return Ok(JsValue::undefined());\n                };\n\n                let result = FinalizationRegistry::cleanup(&registry, &mut context.borrow_mut());\n\n                context\n                    .borrow_mut()\n                    .enqueue_job(Job::FinalizationRegistryCleanupJob(NativeAsyncJob::new(\n                        async move |context| inner_cleanup(weak_registry, receiver, context).await,\n                    )));\n\n                result.map(|()| JsValue::undefined())\n            }\n\n            context.enqueue_job(Job::FinalizationRegistryCleanupJob(NativeAsyncJob::new(\n                async move |ctx| inner_cleanup(weak_registry, receiver, ctx).await,\n            )));\n        }\n\n        // 8. Return finalizationRegistry.\n        Ok(registry.upcast().into())\n    }\n}\n\nimpl FinalizationRegistry {\n    /// [`FinalizationRegistry.prototype.register ( target, heldValue [ , unregisterToken ] )`][spec]\n    ///\n    /// [spec]: https://tc39.es/ecma262/sec-finalization-registry.prototype.register\n    fn register(this: &JsValue, args: &[JsValue], _context: &mut Context) -> JsResult<JsValue> {\n        // 1. Let finalizationRegistry be the this value.\n        // 2. Perform ? RequireInternalSlot(finalizationRegistry, [[Cells]]).\n        let this = this.as_object();\n        let mut registry = this\n            .as_ref()\n            .and_then(JsObject::downcast_mut::<Self>)\n            .ok_or_else(|| {\n                js_error!(\n                    TypeError: \"FinalizationRegistry.prototype.register: \\\n                        invalid object type for `this`\",\n                )\n            })?;\n\n        let target = args.get_or_undefined(0);\n        let held_value = args.get_or_undefined(1);\n        let unregister_token = args.get_or_undefined(2);\n\n        // 3. If CanBeHeldWeakly(target) is false, throw a TypeError exception.\n        //\n        // [`CanBeHeldWeakly ( v )`](https://tc39.es/ecma262/#sec-canbeheldweakly)\n        //\n        // 1. If v is an Object, return true.\n        // 2. If v is a Symbol and KeyForSymbol(v) is undefined, return true.\n        // 3. Return false.\n        //\n        // TODO: support Symbols\n        let Some(target_obj) = target.as_object() else {\n            return Err(js_error!(\n                TypeError: \"FinalizationRegistry.prototype.register: \\\n                    `target` must be an Object or Symbol\",\n            ));\n        };\n\n        // 4. If SameValue(target, heldValue) is true, throw a TypeError exception.\n        if target == held_value {\n            return Err(js_error!(\n                TypeError: \"FinalizationRegistry.prototype.register: \\\n                    `heldValue` cannot be the same as `target`\"\n            ));\n        }\n\n        // 5. If CanBeHeldWeakly(unregisterToken) is false, then\n        //\n        // // [`CanBeHeldWeakly ( v )`](https://tc39.es/ecma262/#sec-canbeheldweakly)\n        //\n        // 1. If v is an Object, return true.\n        // 2. If v is a Symbol and KeyForSymbol(v) is undefined, return true.\n        // 3. Return false.\n        //\n        // TODO: support Symbols\n        let unregister_token = match unregister_token.variant() {\n            JsVariant::Object(obj) => Some(WeakGc::new(obj.inner())),\n            // b. Set unregisterToken to empty.\n            JsVariant::Undefined => None,\n            // a. If unregisterToken is not undefined, throw a TypeError exception.\n            _ => {\n                return Err(js_error!(\n                    TypeError: \"FinalizationRegistry.prototype.register: \\\n                        `unregisterToken` must be an Object, a Symbol, or undefined\",\n                ));\n            }\n        };\n\n        // 6. Let cell be the Record { [[WeakRefTarget]]: target, [[HeldValue]]: heldValue, [[UnregisterToken]]: unregisterToken }.\n        let cell = RegistryCell {\n            target: Ephemeron::new(\n                target_obj.inner(),\n                CleanupSignaler(Cell::new(Some(\n                    registry.cleanup_notifier.clone().downgrade(),\n                ))),\n            ),\n            held_value: held_value.clone(),\n            unregister_token,\n        };\n\n        // 7. Append cell to finalizationRegistry.[[Cells]].\n        registry.cells.push(cell);\n\n        // 8. Return undefined.\n        Ok(JsValue::undefined())\n    }\n\n    /// [`FinalizationRegistry.prototype.unregister ( unregisterToken )`][spec]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-finalization-registry.prototype.unregister\n    fn unregister(this: &JsValue, args: &[JsValue], _context: &mut Context) -> JsResult<JsValue> {\n        // 1. Let finalizationRegistry be the this value.\n        // 2. Perform ? RequireInternalSlot(finalizationRegistry, [[Cells]]).\n        let this = this.as_object();\n        let mut registry = this\n            .as_ref()\n            .and_then(JsObject::downcast_mut::<Self>)\n            .ok_or_else(|| {\n                js_error!(\n                    TypeError: \"FinalizationRegistry.prototype.register: \\\n                    invalid object type for `this`\",\n                )\n            })?;\n\n        // 3. If CanBeHeldWeakly(unregisterToken) is false, throw a TypeError exception.\\\n        //\n        // // [`CanBeHeldWeakly ( v )`](https://tc39.es/ecma262/#sec-canbeheldweakly)\n        //\n        // 1. If v is an Object, return true.\n        // 2. If v is a Symbol and KeyForSymbol(v) is undefined, return true.\n        // 3. Return false.\n        //\n        // TODO: support Symbols\n        let unregister_token = args.get_or_undefined(0).as_object();\n        let unregister_token = unregister_token\n            .as_ref()\n            .map(JsObject::inner)\n            .ok_or_else(|| {\n                js_error!(\n                    TypeError: \"FinalizationRegistry.prototype.unregister: \\\n                                `unregisterToken` must be an Object or a Symbol.\",\n                )\n            })?;\n\n        // 4. Let removed be false.\n        let mut removed = false;\n        let mut i = 0;\n        // 5. For each Record { [[WeakRefTarget]], [[HeldValue]], [[UnregisterToken]] } cell of finalizationRegistry.[[Cells]], do\n        while i < registry.cells.len() {\n            let cell = &registry.cells[i];\n\n            // a. If cell.[[UnregisterToken]] is not empty and SameValue(cell.[[UnregisterToken]], unregisterToken) is true, then\n            if let Some(tok) = cell.unregister_token.as_ref()\n                && let Some(tok) = tok.upgrade()\n                && Gc::ptr_eq(&tok, unregister_token)\n            {\n                // i. Remove cell from finalizationRegistry.[[Cells]].\n                let cell = registry.cells.swap_remove(i);\n                let _key = cell.target.key();\n                // TODO: it might be better to add a special ref for the value that\n                // also preserves the original key instead.\n                cell.target.value().and_then(|v| v.0.take());\n\n                // ii. Set removed to true.\n                removed = true;\n            } else {\n                i += 1;\n            }\n        }\n\n        // 6. Return removed.\n        Ok(removed.into())\n    }\n\n    /// Abstract operation [`CleanupFinalizationRegistry ( finalizationRegistry )`][spec].\n    ///\n    /// Cleans up all the cells of the finalization registry that are determined to be\n    /// unreachable by the garbage collector.\n    ///\n    /// # Panics\n    ///\n    /// Panics if `obj` is not a `FinalizationRegistry` object.\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-cleanup-finalization-registry\n    pub(crate) fn cleanup(\n        obj: &JsObject<FinalizationRegistry>,\n        context: &mut Context,\n    ) -> JsResult<()> {\n        // 1. Assert: finalizationRegistry has [[Cells]] and [[CleanupCallback]] internal slots.\n        // let obj = obj.borrow_mut();\n        // let registry = obj.data_mut();\n\n        // 2. Let callback be finalizationRegistry.[[CleanupCallback]].\n        let callback = std::mem::replace(\n            &mut obj.borrow_mut().data_mut().callback,\n            JobCallback::new(context.intrinsics().objects().throw_type_error(), ()),\n        );\n\n        let mut i = 0;\n        let result = loop {\n            if i >= obj.borrow().data().cells.len() {\n                break Ok(());\n            }\n            // 3. While finalizationRegistry.[[Cells]] contains a Record cell such that cell.[[WeakRefTarget]] is empty, an implementation may perform the following steps:\n            if obj.borrow().data().cells[i].target.has_value() {\n                i += 1;\n            } else {\n                // a. Choose any such cell.\n                // b. Remove cell from finalizationRegistry.[[Cells]].\n                let cell = obj.borrow_mut().data_mut().cells.swap_remove(i);\n                // c. Perform ? HostCallJobCallback(callback, undefined, « cell.[[HeldValue]] »).\n                let result = context.host_hooks().call_job_callback(\n                    &callback,\n                    &JsValue::undefined(),\n                    slice::from_ref(&cell.held_value),\n                    context,\n                );\n\n                if let Err(err) = result {\n                    break Err(err);\n                }\n            }\n        };\n\n        obj.borrow_mut().data_mut().callback = callback;\n\n        // 4. Return unused.\n        result\n    }\n}\n"
  },
  {
    "path": "core/engine/src/builtins/finalization_registry/tests.rs",
    "content": "mod miri {\n\n    use indoc::indoc;\n\n    use crate::{TestAction, run_test_actions};\n\n    #[test]\n    fn finalization_registry_simple() {\n        run_test_actions([\n            TestAction::run(indoc! {r#\"\n            let counter = 0;\n            const registry = new FinalizationRegistry(() => {\n                counter++;\n            });\n\n            registry.register([\"foo\"]);\n        \"#}),\n            TestAction::assert_eq(\"counter\", 0),\n            TestAction::inspect_context(|_| boa_gc::force_collect()),\n            TestAction::inspect_context(|ctx| ctx.run_jobs().unwrap()),\n            // Callback should run at least once\n            TestAction::assert_eq(\"counter\", 1),\n        ]);\n    }\n\n    #[test]\n    fn finalization_registry_unregister() {\n        run_test_actions([\n            TestAction::run(indoc! {r#\"\n            let counter = 0;\n            const registry = new FinalizationRegistry(() => {\n                counter++;\n            });\n\n            {\n                let array = [\"foo\"];\n                registry.register(array, undefined, array);\n                registry.unregister(array);\n            }\n\n        \"#}),\n            TestAction::assert_eq(\"counter\", 0),\n            TestAction::inspect_context(|_| boa_gc::force_collect()),\n            TestAction::inspect_context(|ctx| ctx.run_jobs().unwrap()),\n            // Callback shouldn't run\n            TestAction::assert_eq(\"counter\", 0),\n        ]);\n    }\n\n    #[test]\n    fn finalization_registry_held_value_handover() {\n        run_test_actions([\n            TestAction::run(indoc! {r#\"\n            let counter = 0;\n            const registry = new FinalizationRegistry((value) => {\n                counter += value.increment;\n            });\n\n            registry.register([\"foo\"], { increment: 5 });\n        \"#}),\n            TestAction::assert_eq(\"counter\", 0),\n            TestAction::inspect_context(|_| boa_gc::force_collect()),\n            TestAction::inspect_context(|ctx| ctx.run_jobs().unwrap()),\n            // Registry should handover the held value as argument\n            TestAction::assert_eq(\"counter\", 5),\n        ]);\n    }\n\n    #[test]\n    fn finalization_registry_unrelated_unregister_token() {\n        run_test_actions([\n            TestAction::run(indoc! {r#\"\n            let counter = 0;\n\n            const registry = new FinalizationRegistry((value) => {\n                counter += 1;\n            });\n\n            registry.register([\"foo\"], undefined, {});\n            registry.unregister({});\n        \"#}),\n            TestAction::assert_eq(\"counter\", 0),\n            TestAction::inspect_context(|_| boa_gc::force_collect()),\n            TestAction::inspect_context(|ctx| ctx.run_jobs().unwrap()),\n            // Object should not have been unregistered if the token is not the correct one\n            TestAction::assert_eq(\"counter\", 1),\n        ]);\n    }\n}\n"
  },
  {
    "path": "core/engine/src/builtins/function/arguments.rs",
    "content": "use crate::{\n    Context, JsData, JsExpect, JsResult, JsValue,\n    bytecompiler::ToJsString,\n    environments::DeclarativeEnvironment,\n    object::{\n        JsObject,\n        internal_methods::{\n            InternalMethodPropertyContext, InternalObjectMethods, ORDINARY_INTERNAL_METHODS,\n            ordinary_define_own_property, ordinary_delete, ordinary_get, ordinary_get_own_property,\n            ordinary_set, ordinary_try_get,\n        },\n    },\n    property::{DescriptorKind, PropertyDescriptor, PropertyKey},\n};\nuse boa_ast::{function::FormalParameterList, operations::bound_names, scope::Scope};\nuse boa_gc::{Finalize, Gc, Trace};\nuse boa_interner::Interner;\nuse rustc_hash::FxHashMap;\nuse thin_vec::{ThinVec, thin_vec};\n\n#[derive(Debug, Copy, Clone, Trace, Finalize, JsData)]\n#[boa_gc(empty_trace)]\npub(crate) struct UnmappedArguments;\n\nimpl UnmappedArguments {\n    /// Creates a new unmapped Arguments ordinary object.\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-createunmappedargumentsobject\n    #[allow(clippy::new_ret_no_self)]\n    pub(crate) fn new(arguments_list: &[JsValue], context: &mut Context) -> JsObject {\n        // 1. Let len be the number of elements in argumentsList.\n        let len = arguments_list.len();\n\n        let values_function = context.intrinsics().objects().array_prototype_values();\n        let throw_type_error = context.intrinsics().objects().throw_type_error();\n\n        // 2. Let obj be ! OrdinaryObjectCreate(%Object.prototype%, « [[ParameterMap]] »).\n        // 3. Set obj.[[ParameterMap]] to undefined.\n        // skipped because the `Arguments` enum ensures ordinary argument objects don't have a `[[ParameterMap]]`\n        let obj = context\n            .intrinsics()\n            .templates()\n            .unmapped_arguments()\n            .create(\n                Self,\n                vec![\n                    // 4. Perform DefinePropertyOrThrow(obj, \"length\", PropertyDescriptor { [[Value]]: 𝔽(len),\n                    // [[Writable]]: true, [[Enumerable]]: false, [[Configurable]]: true }).\n                    len.into(),\n                    // 7. Perform ! DefinePropertyOrThrow(obj, @@iterator, PropertyDescriptor {\n                    // [[Value]]: %Array.prototype.values%, [[Writable]]: true, [[Enumerable]]: false,\n                    // [[Configurable]]: true }).\n                    values_function.into(),\n                    // 8. Perform ! DefinePropertyOrThrow(obj, \"callee\", PropertyDescriptor {\n                    // [[Get]]: %ThrowTypeError%, [[Set]]: %ThrowTypeError%, [[Enumerable]]: false,\n                    // [[Configurable]]: false }).\n                    throw_type_error.clone().into(), // get\n                    throw_type_error.into(),         // set\n                ],\n            );\n\n        // 5. Let index be 0.\n        // 6. Repeat, while index < len,\n        //    a. Let val be argumentsList[index].\n        //    b. Perform ! CreateDataPropertyOrThrow(obj, ! ToString(𝔽(index)), val).\n        //    c. Set index to index + 1.\n        obj.borrow_mut()\n            .properties_mut()\n            .override_indexed_properties(arguments_list.iter().cloned().collect());\n\n        // 9. Return obj.\n        obj\n    }\n}\n\n/// `MappedArguments` represents an Arguments exotic object.\n///\n/// This struct stores all the data to access mapped function parameters in their environment.\n#[derive(Debug, Clone, Trace, Finalize)]\npub(crate) struct MappedArguments {\n    #[unsafe_ignore_trace]\n    binding_indices: Vec<Option<u32>>,\n    environment: Gc<DeclarativeEnvironment>,\n}\n\nimpl JsData for MappedArguments {\n    fn internal_methods(&self) -> &'static InternalObjectMethods {\n        static METHODS: InternalObjectMethods = InternalObjectMethods {\n            __get_own_property__: arguments_exotic_get_own_property,\n            __define_own_property__: arguments_exotic_define_own_property,\n            __try_get__: arguments_exotic_try_get,\n            __get__: arguments_exotic_get,\n            __set__: arguments_exotic_set,\n            __delete__: arguments_exotic_delete,\n            ..ORDINARY_INTERNAL_METHODS\n        };\n\n        &METHODS\n    }\n}\n\nimpl MappedArguments {\n    /// Deletes the binding with the given index from the parameter map.\n    pub(crate) fn delete(&mut self, index: u32) {\n        if let Some(binding) = self.binding_indices.get_mut(index as usize) {\n            *binding = None;\n        }\n    }\n\n    /// Get the value of the binding at the given index from the function environment.\n    ///\n    /// Note: This function is the abstract getter closure described in 10.4.4.7.1 `MakeArgGetter ( name, env )`\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-makearggetter\n    pub(crate) fn get(&self, index: u32) -> Option<JsValue> {\n        let binding_index = self\n            .binding_indices\n            .get(index as usize)\n            .copied()\n            .flatten()?;\n        self.environment.get(binding_index)\n    }\n\n    /// Set the value of the binding at the given index in the function environment.\n    ///\n    /// Note: This function is the abstract setter closure described in 10.4.4.7.2 `MakeArgSetter ( name, env )`\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-makeargsetter\n    pub(crate) fn set(&self, index: u32, value: &JsValue) {\n        if let Some(binding_index) = self.binding_indices.get(index as usize).copied().flatten() {\n            self.environment.set(binding_index, value.clone());\n        }\n    }\n}\n\nimpl MappedArguments {\n    pub(crate) fn binding_indices(\n        formals: &FormalParameterList,\n        scope: &Scope,\n        interner: &Interner,\n    ) -> ThinVec<Option<u32>> {\n        // Section 17-19 are done first, for easier object creation in 11.\n        //\n        // The section 17-19 differs from the spec, due to the way the runtime environments work.\n        //\n        // This section creates getters and setters for all mapped arguments.\n        // Getting and setting values on the `arguments` object will actually access the bindings in the environment:\n        // ```\n        // function f(a) {console.log(a); arguments[0] = 1; console.log(a)};\n        // f(0) // 0, 1\n        // ```\n        //\n        // The spec assumes, that identifiers are used at runtime to reference bindings in the environment.\n        // We use indices to access environment bindings at runtime.\n        // To map to function parameters to binding indices, we use the fact, that bindings in a\n        // function environment start with all of the arguments in order:\n        //\n        // Note: The first binding (binding 0) is where \"arguments\" is stored.\n        //\n        // `function f (a,b,c)`\n        // | binding index | `arguments` property key | identifier |\n        // | 1             | 0                        | a          |\n        // | 2             | 1                        | b          |\n        // | 3             | 2                        | c          |\n        //\n        // Notice that the binding index does not correspond to the argument index:\n        // `function f (a,a,b)` => binding indices 0 (a), 1 (b), 2 (c)\n        // | binding index | `arguments` property key | identifier |\n        // | -             | 0                        | -          |\n        // | 1             | 1                        | a          |\n        // | 2             | 2                        | b          |\n        //\n        // While the `arguments` object contains all arguments, they must not be all bound.\n        // In the case of duplicate parameter names, the last one is bound as the environment binding.\n        //\n        // The following logic implements the steps 17-19 adjusted for our environment structure.\n        let mut bindings = FxHashMap::default();\n        let mut property_index = 0;\n        for name in bound_names(formals) {\n            let binding_index = scope\n                .get_binding(&name.to_js_string(interner))\n                .expect(\"binding must exist\")\n                .binding_index();\n\n            let entry = bindings\n                .entry(name)\n                .or_insert((binding_index, property_index));\n\n            entry.1 = property_index;\n            property_index += 1;\n        }\n\n        let mut binding_indices = thin_vec![None; property_index];\n        for (binding_index, property_index) in bindings.values() {\n            binding_indices[*property_index] = Some(*binding_index);\n        }\n\n        binding_indices\n    }\n\n    /// Creates a new mapped Arguments exotic object.\n    ///\n    /// <https://tc39.es/ecma262/#sec-createmappedargumentsobject>\n    #[allow(clippy::new_ret_no_self)]\n    pub(crate) fn new(\n        func: &JsObject,\n        binding_indices: &[Option<u32>],\n        arguments_list: &[JsValue],\n        env: &Gc<DeclarativeEnvironment>,\n        context: &Context,\n    ) -> JsObject {\n        // 1. Assert: formals does not contain a rest parameter, any binding patterns, or any initializers.\n        // It may contain duplicate identifiers.\n        // 2. Let len be the number of elements in argumentsList.\n        let len = arguments_list.len();\n\n        // 3. Let obj be ! MakeBasicObject(« [[Prototype]], [[Extensible]], [[ParameterMap]] »).\n        // 4. Set obj.[[GetOwnProperty]] as specified in 10.4.4.1.\n        // 5. Set obj.[[DefineOwnProperty]] as specified in 10.4.4.2.\n        // 6. Set obj.[[Get]] as specified in 10.4.4.3.\n        // 7. Set obj.[[Set]] as specified in 10.4.4.4.\n        // 8. Set obj.[[Delete]] as specified in 10.4.4.5.\n        // 9. Set obj.[[Prototype]] to %Object.prototype%.\n\n        let range = binding_indices.len().min(len);\n        let map = MappedArguments {\n            binding_indices: binding_indices[..range].to_vec(),\n            environment: env.clone(),\n        };\n\n        // %Array.prototype.values%\n        let values_function = context.intrinsics().objects().array_prototype_values();\n\n        // 11. Set obj.[[ParameterMap]] to map.\n        let obj = context.intrinsics().templates().mapped_arguments().create(\n            map,\n            vec![\n                // 16. Perform ! DefinePropertyOrThrow(obj, \"length\", PropertyDescriptor { [[Value]]: 𝔽(len),\n                // [[Writable]]: true, [[Enumerable]]: false, [[Configurable]]: true }).\n                len.into(),\n                // 20. Perform ! DefinePropertyOrThrow(obj, @@iterator, PropertyDescriptor {\n                // [[Value]]: %Array.prototype.values%, [[Writable]]: true, [[Enumerable]]: false,\n                // [[Configurable]]: true }).\n                values_function.into(),\n                // 21. Perform ! DefinePropertyOrThrow(obj, \"callee\", PropertyDescriptor {\n                // [[Value]]: func, [[Writable]]: true, [[Enumerable]]: false, [[Configurable]]: true }).\n                func.clone().into(),\n            ],\n        );\n\n        // 14. Let index be 0.\n        // 15. Repeat, while index < len,\n        //     a. Let val be argumentsList[index].\n        //     b. Perform ! CreateDataPropertyOrThrow(obj, ! ToString(𝔽(index)), val).\n        //     Note: Direct initialization of indexed array is used here because `CreateDataPropertyOrThrow`\n        //     would cause a panic while executing exotic argument object set methods before the variables\n        //     in the environment are initialized.\n        obj.borrow_mut()\n            .properties_mut()\n            .override_indexed_properties(arguments_list.iter().cloned().collect());\n\n        // 22. Return obj.\n        obj\n    }\n}\n\n/// `[[GetOwnProperty]]` for arguments exotic objects.\n///\n/// More information:\n///  - [ECMAScript reference][spec]\n///\n/// [spec]: https://tc39.es/ecma262/#sec-arguments-exotic-objects-getownproperty-p\npub(crate) fn arguments_exotic_get_own_property(\n    obj: &JsObject,\n    key: &PropertyKey,\n    context: &mut InternalMethodPropertyContext<'_>,\n) -> JsResult<Option<PropertyDescriptor>> {\n    // 1. Let desc be OrdinaryGetOwnProperty(args, P).\n    // 2. If desc is undefined, return desc.\n    let Some(desc) = ordinary_get_own_property(obj, key, context)? else {\n        return Ok(None);\n    };\n\n    // 3. Let map be args.[[ParameterMap]].\n    // 4. Let isMapped be ! HasOwnProperty(map, P).\n    // 5. If isMapped is true, then\n    if let PropertyKey::Index(index) = key\n        && let Some(value) = obj\n            .downcast_ref::<MappedArguments>()\n            .js_expect(\"arguments exotic method must only be callable from arguments objects\")?\n            .get(index.get())\n    {\n        // a. Set desc.[[Value]] to Get(map, P).\n        return Ok(Some(\n            PropertyDescriptor::builder()\n                .value(value)\n                .maybe_writable(desc.writable())\n                .maybe_enumerable(desc.enumerable())\n                .maybe_configurable(desc.configurable())\n                .build(),\n        ));\n    }\n\n    // 6. Return desc.\n    Ok(Some(desc))\n}\n\n/// `[[DefineOwnProperty]]` for arguments exotic objects.\n///\n/// More information:\n///  - [ECMAScript reference][spec]\n///\n/// [spec]: https://tc39.es/ecma262/#sec-arguments-exotic-objects-defineownproperty-p-desc\n#[allow(clippy::needless_pass_by_value)]\npub(crate) fn arguments_exotic_define_own_property(\n    obj: &JsObject,\n    key: &PropertyKey,\n    desc: PropertyDescriptor,\n    context: &mut InternalMethodPropertyContext<'_>,\n) -> JsResult<bool> {\n    // 2. Let isMapped be HasOwnProperty(map, P).\n    let mapped = if let &PropertyKey::Index(index) = &key {\n        // 1. Let map be args.[[ParameterMap]].\n        obj.downcast_ref::<MappedArguments>()\n            .js_expect(\"arguments exotic method must only be callable from arguments objects\")?\n            .get(index.get())\n            .map(|value| (index, value))\n    } else {\n        None\n    };\n\n    let new_arg_desc = match desc.kind() {\n        // 4. If isMapped is true and IsDataDescriptor(Desc) is true, then\n        // a. If Desc.[[Value]] is not present and Desc.[[Writable]] is present and its\n        // value is false, then\n        DescriptorKind::Data {\n            writable: Some(false),\n            value: None,\n        } =>\n        // i. Set newArgDesc to a copy of Desc.\n        // ii. Set newArgDesc.[[Value]] to Get(map, P).\n        {\n            if let Some((_, value)) = &mapped {\n                PropertyDescriptor::builder()\n                    .value(value.clone())\n                    .writable(false)\n                    .maybe_enumerable(desc.enumerable())\n                    .maybe_configurable(desc.configurable())\n                    .build()\n            } else {\n                desc.clone()\n            }\n        }\n\n        // 3. Let newArgDesc be Desc.\n        _ => desc.clone(),\n    };\n\n    // 5. Let allowed be ? OrdinaryDefineOwnProperty(args, P, newArgDesc).\n    // 6. If allowed is false, return false.\n    if !ordinary_define_own_property(obj, key, new_arg_desc, context)? {\n        return Ok(false);\n    }\n\n    // 7. If isMapped is true, then\n    if let Some((index, _)) = mapped {\n        // 1. Let map be args.[[ParameterMap]].\n        let mut map = obj\n            .downcast_mut::<MappedArguments>()\n            .js_expect(\"arguments exotic method must only be callable from arguments objects\")?;\n\n        // a. If IsAccessorDescriptor(Desc) is true, then\n        if desc.is_accessor_descriptor() {\n            // i. Call map.[[Delete]](P).\n            map.delete(index.get());\n        }\n        // b. Else,\n        else {\n            // i. If Desc.[[Value]] is present, then\n            if let Some(value) = desc.value() {\n                // 1. Let setStatus be Set(map, P, Desc.[[Value]], false).\n                // 2. Assert: setStatus is true because formal parameters mapped by argument objects are always writable.\n                map.set(index.get(), value);\n            }\n\n            // ii. If Desc.[[Writable]] is present and its value is false, then\n            if desc.writable() == Some(false) {\n                // 1. Call map.[[Delete]](P).\n                map.delete(index.get());\n            }\n        }\n    }\n\n    // 8. Return true.\n    Ok(true)\n}\n\n/// Internal optimization method for `Arguments` exotic objects.\n///\n/// This method combines the internal methods `OrdinaryHasProperty` and `[[Get]]`.\n///\n/// More information:\n///  - [ECMAScript reference OrdinaryHasProperty][spec0]\n///  - [ECMAScript reference Get][spec1]\n///\n/// [spec0]: https://tc39.es/ecma262/#sec-ordinaryhasproperty\n/// [spec1]: https://tc39.es/ecma262/#sec-arguments-exotic-objects-get-p-receiver\npub(crate) fn arguments_exotic_try_get(\n    obj: &JsObject,\n    key: &PropertyKey,\n    receiver: JsValue,\n    context: &mut InternalMethodPropertyContext<'_>,\n) -> JsResult<Option<JsValue>> {\n    // 1. Let map be args.[[ParameterMap]].\n    // 2. Let isMapped be ! HasOwnProperty(map, P).\n    if let PropertyKey::Index(index) = key\n        && let Some(value) = obj\n            .downcast_ref::<MappedArguments>()\n            .js_expect(\"arguments exotic method must only be callable from arguments objects\")?\n            .get(index.get())\n    {\n        // a. Assert: map contains a formal parameter mapping for P.\n        // b. Return Get(map, P).\n        return Ok(Some(value));\n    }\n\n    // 3. If isMapped is false, then\n    // a. Return ? OrdinaryGet(args, P, Receiver).\n    ordinary_try_get(obj, key, receiver, context)\n}\n\n/// `[[Get]]` for arguments exotic objects.\n///\n/// More information:\n///  - [ECMAScript reference][spec]\n///\n/// [spec]: https://tc39.es/ecma262/#sec-arguments-exotic-objects-get-p-receiver\npub(crate) fn arguments_exotic_get(\n    obj: &JsObject,\n    key: &PropertyKey,\n    receiver: JsValue,\n    context: &mut InternalMethodPropertyContext<'_>,\n) -> JsResult<JsValue> {\n    // 1. Let map be args.[[ParameterMap]].\n    // 2. Let isMapped be ! HasOwnProperty(map, P).\n    if let PropertyKey::Index(index) = key\n        && let Some(value) = obj\n            .downcast_ref::<MappedArguments>()\n            .js_expect(\"arguments exotic method must only be callable from arguments objects\")?\n            .get(index.get())\n    {\n        // a. Assert: map contains a formal parameter mapping for P.\n        // b. Return Get(map, P).\n        return Ok(value);\n    }\n\n    // 3. If isMapped is false, then\n    // a. Return ? OrdinaryGet(args, P, Receiver).\n    ordinary_get(obj, key, receiver, context)\n}\n\n/// `[[Set]]` for arguments exotic objects.\n///\n/// More information:\n///  - [ECMAScript reference][spec]\n///\n/// [spec]: https://tc39.es/ecma262/#sec-arguments-exotic-objects-set-p-v-receiver\npub(crate) fn arguments_exotic_set(\n    obj: &JsObject,\n    key: PropertyKey,\n    value: JsValue,\n    receiver: JsValue,\n    context: &mut InternalMethodPropertyContext<'_>,\n) -> JsResult<bool> {\n    // 1. If SameValue(args, Receiver) is false, then\n    // a. Let isMapped be false.\n    // 2. Else,\n    if let PropertyKey::Index(index) = &key\n        && JsValue::same_value(&obj.clone().into(), &receiver)\n    {\n        // a. Let map be args.[[ParameterMap]].\n        // b. Let isMapped be ! HasOwnProperty(map, P).\n        // 3. If isMapped is true, then\n        // a. Let setStatus be Set(map, P, V, false).\n        // b. Assert: setStatus is true because formal parameters mapped by argument objects are always writable.\n        obj.downcast_ref::<MappedArguments>()\n            .js_expect(\"arguments exotic method must only be callable from arguments objects\")?\n            .set(index.get(), &value);\n    }\n\n    // 4. Return ? OrdinarySet(args, P, V, Receiver).\n    ordinary_set(obj, key, value, receiver, context)\n}\n\n/// `[[Delete]]` for arguments exotic objects.\n///\n/// More information:\n///  - [ECMAScript reference][spec]\n///\n/// [spec]: https://tc39.es/ecma262/#sec-arguments-exotic-objects-delete-p\npub(crate) fn arguments_exotic_delete(\n    obj: &JsObject,\n    key: &PropertyKey,\n    context: &mut InternalMethodPropertyContext<'_>,\n) -> JsResult<bool> {\n    // 3. Let result be ? OrdinaryDelete(args, P).\n    let result = ordinary_delete(obj, key, context)?;\n\n    if result && let PropertyKey::Index(index) = key {\n        // 1. Let map be args.[[ParameterMap]].\n        // 2. Let isMapped be ! HasOwnProperty(map, P).\n        // 4. If result is true and isMapped is true, then\n        // a. Call map.[[Delete]](P).\n        obj.downcast_mut::<MappedArguments>()\n            .js_expect(\"arguments exotic method must only be callable from arguments objects\")?\n            .delete(index.get());\n    }\n\n    // 5. Return result.\n    Ok(result)\n}\n"
  },
  {
    "path": "core/engine/src/builtins/function/bound.rs",
    "content": "use boa_gc::{Finalize, Trace};\n\nuse crate::{\n    Context, JsExpect, JsObject, JsResult, JsValue,\n    object::{\n        JsData,\n        internal_methods::{\n            CallValue, InternalMethodCallContext, InternalObjectMethods, ORDINARY_INTERNAL_METHODS,\n        },\n    },\n};\n\n/// Binds a `Function Object` when `bind` is called.\n#[derive(Debug, Trace, Finalize)]\npub struct BoundFunction {\n    target_function: JsObject,\n    this: JsValue,\n    args: Vec<JsValue>,\n}\n\nimpl JsData for BoundFunction {\n    fn internal_methods(&self) -> &'static InternalObjectMethods {\n        static CONSTRUCTOR_METHODS: InternalObjectMethods = InternalObjectMethods {\n            __call__: bound_function_exotic_call,\n            __construct__: bound_function_exotic_construct,\n            ..ORDINARY_INTERNAL_METHODS\n        };\n\n        static FUNCTION_METHODS: InternalObjectMethods = InternalObjectMethods {\n            __call__: bound_function_exotic_call,\n            ..ORDINARY_INTERNAL_METHODS\n        };\n\n        if self.target_function.is_constructor() {\n            &CONSTRUCTOR_METHODS\n        } else {\n            &FUNCTION_METHODS\n        }\n    }\n}\n\nimpl BoundFunction {\n    /// Abstract operation `BoundFunctionCreate`\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-boundfunctioncreate\n    pub fn create(\n        target_function: JsObject,\n        this: JsValue,\n        args: Vec<JsValue>,\n        context: &mut Context,\n    ) -> JsResult<JsObject> {\n        // 1. Let proto be ? targetFunction.[[GetPrototypeOf]]().\n        let proto = target_function.__get_prototype_of__(context)?;\n\n        // 2. Let internalSlotsList be the internal slots listed in Table 35, plus [[Prototype]] and [[Extensible]].\n        // 3. Let obj be ! MakeBasicObject(internalSlotsList).\n        // 4. Set obj.[[Prototype]] to proto.\n        // 5. Set obj.[[Call]] as described in 10.4.1.1.\n        // 6. If IsConstructor(targetFunction) is true, then\n        // a. Set obj.[[Construct]] as described in 10.4.1.2.\n        // 7. Set obj.[[BoundTargetFunction]] to targetFunction.\n        // 8. Set obj.[[BoundThis]] to boundThis.\n        // 9. Set obj.[[BoundArguments]] to boundArgs.\n        // 10. Return obj.\n        Ok(JsObject::from_proto_and_data_with_shared_shape(\n            context.root_shape(),\n            proto,\n            Self {\n                target_function,\n                this,\n                args,\n            },\n        )\n        .upcast())\n    }\n\n    /// Get a reference to the bound function's this.\n    #[must_use]\n    pub const fn this(&self) -> &JsValue {\n        &self.this\n    }\n\n    /// Get a reference to the bound function's target function.\n    #[must_use]\n    pub const fn target_function(&self) -> &JsObject {\n        &self.target_function\n    }\n\n    /// Get a reference to the bound function's args.\n    #[must_use]\n    pub fn args(&self) -> &[JsValue] {\n        self.args.as_slice()\n    }\n}\n\n/// Internal method `[[Call]]` for Bound Function Exotic Objects\n///\n/// More information:\n///  - [ECMAScript reference][spec]\n///\n/// [spec]: https://tc39.es/ecma262/#sec-bound-function-exotic-objects-call-thisargument-argumentslist\n#[allow(clippy::unnecessary_wraps)]\nfn bound_function_exotic_call(\n    obj: &JsObject,\n    argument_count: usize,\n    context: &mut InternalMethodCallContext<'_>,\n) -> JsResult<CallValue> {\n    let bound_function = obj.downcast_ref::<BoundFunction>().js_expect(\n        \"bound function exotic method should only be callable from bound function objects\",\n    )?;\n\n    // 1. Let target be F.[[BoundTargetFunction]].\n    let target = bound_function.target_function();\n    context\n        .vm\n        .stack\n        .calling_convention_set_function(argument_count, target.clone().into());\n\n    // 2. Let boundThis be F.[[BoundThis]].\n    let bound_this = bound_function.this();\n    context\n        .vm\n        .stack\n        .calling_convention_set_this(argument_count, bound_this.clone());\n\n    // 3. Let boundArgs be F.[[BoundArguments]].\n    let bound_args = bound_function.args();\n\n    // 4. Let args be the list-concatenation of boundArgs and argumentsList.\n    context\n        .vm\n        .stack\n        .calling_convention_insert_arguments(argument_count, bound_args);\n\n    // 5. Return ? Call(target, boundThis, args).\n    Ok(target.__call__(bound_args.len() + argument_count))\n}\n\n/// Internal method `[[Construct]]` for Bound Function Exotic Objects\n///\n/// More information:\n///  - [ECMAScript reference][spec]\n///\n/// [spec]: https://tc39.es/ecma262/#sec-bound-function-exotic-objects-construct-argumentslist-newtarget\n#[allow(clippy::unnecessary_wraps)]\nfn bound_function_exotic_construct(\n    function_object: &JsObject,\n    argument_count: usize,\n    context: &mut InternalMethodCallContext<'_>,\n) -> JsResult<CallValue> {\n    let new_target = context.vm.stack.pop();\n\n    debug_assert!(new_target.is_object(), \"new.target should be an object\");\n\n    let bound_function = function_object.downcast_ref::<BoundFunction>().js_expect(\n        \"bound function exotic method should only be callable from bound function objects\",\n    )?;\n\n    // 1. Let target be F.[[BoundTargetFunction]].\n    let target = bound_function.target_function();\n\n    // 2. Assert: IsConstructor(target) is true.\n\n    // 3. Let boundArgs be F.[[BoundArguments]].\n    let bound_args = bound_function.args();\n\n    // 4. Let args be the list-concatenation of boundArgs and argumentsList.\n    context\n        .vm\n        .stack\n        .calling_convention_insert_arguments(argument_count, bound_args);\n\n    // 5. If SameValue(F, newTarget) is true, set newTarget to target.\n    let function_object: JsValue = function_object.clone().into();\n    let new_target = if JsValue::same_value(&function_object, &new_target) {\n        target.clone().into()\n    } else {\n        new_target\n    };\n\n    // 6. Return ? Construct(target, args, newTarget).\n    context.vm.stack.push(new_target);\n    Ok(target.__construct__(bound_args.len() + argument_count))\n}\n"
  },
  {
    "path": "core/engine/src/builtins/function/mod.rs",
    "content": "//! Boa's implementation of ECMAScript's global `Function` object and Native Functions.\n//!\n//! Objects wrap `Function`s and expose them via call/construct slots.\n//!\n//! The `Function` object is used for matching text with a pattern.\n//!\n//! More information:\n//!  - [ECMAScript reference][spec]\n//!  - [MDN documentation][mdn]\n//!\n//! [spec]: https://tc39.es/ecma262/#sec-function-objects\n//! [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function\n\nuse crate::{\n    Context, JsArgs, JsExpect, JsResult, JsStr, JsString, JsValue, SpannedSourceText,\n    builtins::{\n        BuiltInBuilder, BuiltInConstructor, BuiltInObject, IntrinsicObject, OrdinaryObject,\n    },\n    bytecompiler::FunctionCompiler,\n    context::intrinsics::{Intrinsics, StandardConstructor, StandardConstructors},\n    environments::{EnvironmentStack, FunctionSlots, PrivateEnvironment, ThisBindingStatus},\n    error::JsNativeError,\n    js_error, js_string,\n    native_function::NativeFunctionObject,\n    object::{\n        JsData, JsFunction, JsObject, PrivateElement, PrivateName,\n        internal_methods::{\n            CallValue, InternalMethodCallContext, InternalObjectMethods, ORDINARY_INTERNAL_METHODS,\n            get_prototype_from_constructor,\n        },\n    },\n    property::{Attribute, PropertyDescriptor, PropertyKey},\n    realm::Realm,\n    string::StaticJsStrings,\n    symbol::JsSymbol,\n    value::IntegerOrInfinity,\n    vm::{ActiveRunnable, CallFrame, CallFrameFlags, CodeBlock},\n};\nuse boa_ast::{\n    Position, Span, Spanned, StatementList,\n    function::{FormalParameterList, FunctionBody},\n    operations::{\n        ContainsSymbol, all_private_identifiers_valid, bound_names, contains,\n        lexically_declared_names,\n    },\n    scope::BindingLocatorScope,\n};\nuse boa_gc::{self, Finalize, Gc, Trace, custom_trace};\nuse boa_interner::Sym;\nuse boa_macros::js_str;\nuse boa_parser::{Parser, Source};\nuse thin_vec::ThinVec;\n\nuse super::Proxy;\n\npub(crate) mod arguments;\nmod bound;\n\npub use bound::BoundFunction;\n\n#[cfg(test)]\nmod tests;\n\n/// Represents the `[[ThisMode]]` internal slot of function objects.\n///\n/// More information:\n///  - [ECMAScript specification][spec]\n///\n/// [spec]: https://tc39.es/ecma262/#sec-ecmascript-function-objects\n#[derive(Debug, Trace, Finalize, PartialEq, Eq, Clone)]\npub enum ThisMode {\n    /// The `this` value refers to the `this` value of a lexically enclosing function.\n    Lexical,\n\n    /// The `this` value is used exactly as provided by an invocation of the function.\n    Strict,\n\n    /// The `this` value of `undefined` or `null` is interpreted as a reference to the global object,\n    /// and any other `this` value is first passed to `ToObject`.\n    Global,\n}\n\nimpl ThisMode {\n    /// Returns `true` if the this mode is `Lexical`.\n    #[must_use]\n    pub const fn is_lexical(&self) -> bool {\n        matches!(self, Self::Lexical)\n    }\n\n    /// Returns `true` if the this mode is `Strict`.\n    #[must_use]\n    pub const fn is_strict(&self) -> bool {\n        matches!(self, Self::Strict)\n    }\n\n    /// Returns `true` if the this mode is `Global`.\n    #[must_use]\n    pub const fn is_global(&self) -> bool {\n        matches!(self, Self::Global)\n    }\n}\n\n/// Represents the `[[ConstructorKind]]` internal slot of function objects.\n///\n/// More information:\n///  - [ECMAScript specification][spec]\n///\n/// [spec]: https://tc39.es/ecma262/#sec-ecmascript-function-objects\n#[derive(Clone, Copy, Debug, PartialEq, Eq)]\npub enum ConstructorKind {\n    /// The class constructor is not derived.\n    Base,\n\n    /// The class constructor is a derived class constructor.\n    Derived,\n}\n\nimpl ConstructorKind {\n    /// Returns `true` if the constructor kind is `Base`.\n    #[must_use]\n    pub const fn is_base(&self) -> bool {\n        matches!(self, Self::Base)\n    }\n\n    /// Returns `true` if the constructor kind is `Derived`.\n    #[must_use]\n    pub const fn is_derived(&self) -> bool {\n        matches!(self, Self::Derived)\n    }\n}\n\n/// Record containing the field definition of classes.\n///\n/// More information:\n///  - [ECMAScript specification][spec]\n///\n/// [spec]: https://tc39.es/ecma262/#sec-classfielddefinition-record-specification-type\n#[derive(Clone, Debug, Finalize)]\npub enum ClassFieldDefinition {\n    /// A class field definition with a `string` or `symbol` as a name.\n    Public(PropertyKey, JsFunction, Option<PropertyKey>),\n\n    /// A class field definition with a private name.\n    Private(PrivateName, JsFunction),\n}\n\nunsafe impl Trace for ClassFieldDefinition {\n    custom_trace! {this, mark, {\n        match this {\n            Self::Public(_key, func, _) => {\n                mark(func);\n            }\n            Self::Private(_, func) => {\n                mark(func);\n            }\n        }\n    }}\n}\n\n/// Boa representation of a JavaScript Function Object.\n///\n/// `FunctionBody` is specific to this interpreter, it will either be Rust code or JavaScript code\n/// (AST Node).\n///\n/// <https://tc39.es/ecma262/#sec-ecmascript-function-objects>\n#[derive(Debug, Trace, Finalize)]\npub struct OrdinaryFunction {\n    /// The code block containing the compiled function.\n    pub(crate) code: Gc<CodeBlock>,\n\n    /// The `[[Environment]]` internal slot.\n    pub(crate) environments: EnvironmentStack,\n\n    /// The `[[HomeObject]]` internal slot.\n    pub(crate) home_object: Option<JsObject>,\n\n    /// The `[[ScriptOrModule]]` internal slot.\n    pub(crate) script_or_module: Option<ActiveRunnable>,\n\n    /// The [`Realm`] the function is defined in.\n    pub(crate) realm: Realm,\n\n    /// The `[[Fields]]` internal slot.\n    fields: ThinVec<ClassFieldDefinition>,\n\n    /// The `[[PrivateMethods]]` internal slot.\n    private_methods: ThinVec<(PrivateName, PrivateElement)>,\n}\n\nimpl JsData for OrdinaryFunction {\n    fn internal_methods(&self) -> &'static InternalObjectMethods {\n        static FUNCTION_METHODS: InternalObjectMethods = InternalObjectMethods {\n            __call__: function_call,\n            ..ORDINARY_INTERNAL_METHODS\n        };\n\n        static CONSTRUCTOR_METHODS: InternalObjectMethods = InternalObjectMethods {\n            __call__: function_call,\n            __construct__: function_construct,\n            ..ORDINARY_INTERNAL_METHODS\n        };\n\n        if self.code.has_prototype_property() {\n            &CONSTRUCTOR_METHODS\n        } else {\n            &FUNCTION_METHODS\n        }\n    }\n}\n\nimpl OrdinaryFunction {\n    pub(crate) fn new(\n        code: Gc<CodeBlock>,\n        environments: EnvironmentStack,\n        script_or_module: Option<ActiveRunnable>,\n        realm: Realm,\n    ) -> Self {\n        Self {\n            code,\n            environments,\n            home_object: None,\n            script_or_module,\n            realm,\n            fields: ThinVec::default(),\n            private_methods: ThinVec::default(),\n        }\n    }\n\n    /// Returns the codeblock of the function.\n    #[must_use]\n    pub fn codeblock(&self) -> &CodeBlock {\n        &self.code\n    }\n\n    /// Push a private environment to the function.\n    pub(crate) fn push_private_environment(&mut self, environment: Gc<PrivateEnvironment>) {\n        self.environments.push_private(environment);\n    }\n\n    /// Returns true if the function object is a derived constructor.\n    pub(crate) fn is_derived_constructor(&self) -> bool {\n        self.code.is_derived_constructor()\n    }\n\n    /// Does this function have the `[[ClassFieldInitializerName]]` internal slot set to non-empty value.\n    pub(crate) fn in_class_field_initializer(&self) -> bool {\n        self.code.in_class_field_initializer()\n    }\n\n    /// Returns a reference to the function `[[HomeObject]]` slot if present.\n    pub(crate) const fn get_home_object(&self) -> Option<&JsObject> {\n        self.home_object.as_ref()\n    }\n\n    ///  Sets the `[[HomeObject]]` slot if present.\n    pub(crate) fn set_home_object(&mut self, object: JsObject) {\n        self.home_object = Some(object);\n    }\n\n    /// Returns the values of the `[[Fields]]` internal slot.\n    pub(crate) fn get_fields(&self) -> &[ClassFieldDefinition] {\n        &self.fields\n    }\n\n    /// Pushes a value to the `[[Fields]]` internal slot if present.\n    pub(crate) fn push_field(\n        &mut self,\n        key: PropertyKey,\n        value: JsFunction,\n        function_name: Option<PropertyKey>,\n    ) {\n        self.fields\n            .push(ClassFieldDefinition::Public(key, value, function_name));\n    }\n\n    /// Pushes a private value to the `[[Fields]]` internal slot if present.\n    pub(crate) fn push_field_private(&mut self, name: PrivateName, value: JsFunction) {\n        self.fields.push(ClassFieldDefinition::Private(name, value));\n    }\n\n    /// Returns the values of the `[[PrivateMethods]]` internal slot.\n    pub(crate) fn get_private_methods(&self) -> &[(PrivateName, PrivateElement)] {\n        &self.private_methods\n    }\n\n    /// Pushes a private method to the `[[PrivateMethods]]` internal slot if present.\n    pub(crate) fn push_private_method(&mut self, name: PrivateName, method: PrivateElement) {\n        self.private_methods.push((name, method));\n    }\n\n    /// Gets the `Realm` from where this function originates.\n    #[must_use]\n    pub const fn realm(&self) -> &Realm {\n        &self.realm\n    }\n\n    /// Checks if this function is an ordinary function.\n    pub(crate) fn is_ordinary(&self) -> bool {\n        self.code.is_ordinary()\n    }\n}\n\n/// The internal representation of a `Function` object.\n#[derive(Debug, Clone, Copy)]\npub struct BuiltInFunctionObject;\n\nimpl IntrinsicObject for BuiltInFunctionObject {\n    fn init(realm: &Realm) {\n        let has_instance = BuiltInBuilder::callable(realm, Self::has_instance)\n            .name(js_string!(\"[Symbol.hasInstance]\"))\n            .length(1)\n            .build();\n\n        let throw_type_error = realm.intrinsics().objects().throw_type_error();\n\n        BuiltInBuilder::from_standard_constructor::<Self>(realm)\n            .method(Self::apply, js_string!(\"apply\"), 2)\n            .method(Self::bind, js_string!(\"bind\"), 1)\n            .method(Self::call, js_string!(\"call\"), 1)\n            .method(Self::to_string, js_string!(\"toString\"), 0)\n            .property(JsSymbol::has_instance(), has_instance, Attribute::default())\n            .accessor(\n                js_string!(\"caller\"),\n                Some(throw_type_error.clone()),\n                Some(throw_type_error.clone()),\n                Attribute::CONFIGURABLE,\n            )\n            .accessor(\n                js_string!(\"arguments\"),\n                Some(throw_type_error.clone()),\n                Some(throw_type_error),\n                Attribute::CONFIGURABLE,\n            )\n            .build();\n\n        let prototype = realm.intrinsics().constructors().function().prototype();\n\n        BuiltInBuilder::callable_with_object(realm, prototype.clone(), Self::prototype)\n            .name(js_string!())\n            .length(0)\n            .build();\n\n        prototype.set_prototype(Some(realm.intrinsics().constructors().object().prototype()));\n    }\n\n    fn get(intrinsics: &Intrinsics) -> JsObject {\n        Self::STANDARD_CONSTRUCTOR(intrinsics.constructors()).constructor()\n    }\n}\n\nimpl BuiltInObject for BuiltInFunctionObject {\n    const NAME: JsString = StaticJsStrings::FUNCTION;\n}\n\nimpl BuiltInConstructor for BuiltInFunctionObject {\n    const CONSTRUCTOR_ARGUMENTS: usize = 1;\n    const PROTOTYPE_STORAGE_SLOTS: usize = 10;\n    const CONSTRUCTOR_STORAGE_SLOTS: usize = 0;\n\n    const STANDARD_CONSTRUCTOR: fn(&StandardConstructors) -> &StandardConstructor =\n        StandardConstructors::function;\n\n    /// `Function ( p1, p2, … , pn, body )`\n    ///\n    /// The `apply()` method invokes self with the first argument as the `this` value\n    /// and the rest of the arguments provided as an array (or an array-like object).\n    ///\n    /// More information:\n    ///  - [MDN documentation][mdn]\n    ///  - [ECMAScript reference][spec]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-function-p1-p2-pn-body\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/Function\n    fn constructor(\n        new_target: &JsValue,\n        args: &[JsValue],\n        context: &mut Context,\n    ) -> JsResult<JsValue> {\n        let active_function = context\n            .active_function_object()\n            .unwrap_or_else(|| context.intrinsics().constructors().function().constructor());\n        Self::create_dynamic_function(active_function, new_target, args, false, false, context)\n            .map(Into::into)\n    }\n}\n\nimpl BuiltInFunctionObject {\n    /// `CreateDynamicFunction ( constructor, newTarget, kind, args )`\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-createdynamicfunction\n    pub(crate) fn create_dynamic_function(\n        constructor: JsObject,\n        new_target: &JsValue,\n        args: &[JsValue],\n        r#async: bool,\n        generator: bool,\n        context: &mut Context,\n    ) -> JsResult<JsObject> {\n        // 1. If newTarget is undefined, set newTarget to constructor.\n        let new_target = if new_target.is_undefined() {\n            constructor.into()\n        } else {\n            new_target.clone()\n        };\n\n        let strict = context.is_strict();\n\n        let default = if r#async && generator {\n            // 5. Else,\n            //     a. Assert: kind is async-generator.\n            //     b. Let prefix be \"async function*\".\n            //     c. Let exprSym be the grammar symbol AsyncGeneratorExpression.\n            //     d. Let bodySym be the grammar symbol AsyncGeneratorBody.\n            //     e. Let parameterSym be the grammar symbol FormalParameters[+Yield, +Await].\n            //     f. Let fallbackProto be \"%AsyncGeneratorFunction.prototype%\".\n            StandardConstructors::async_generator_function\n        } else if r#async {\n            // 4. Else if kind is async, then\n            //     a. Let prefix be \"async function\".\n            //     b. Let exprSym be the grammar symbol AsyncFunctionExpression.\n            //     c. Let bodySym be the grammar symbol AsyncFunctionBody.\n            //     d. Let parameterSym be the grammar symbol FormalParameters[~Yield, +Await].\n            //     e. Let fallbackProto be \"%AsyncFunction.prototype%\".\n            StandardConstructors::async_function\n        } else if generator {\n            // 3. Else if kind is generator, then\n            //     a. Let prefix be \"function*\".\n            //     b. Let exprSym be the grammar symbol GeneratorExpression.\n            //     c. Let bodySym be the grammar symbol GeneratorBody.\n            //     d. Let parameterSym be the grammar symbol FormalParameters[+Yield, ~Await].\n            //     e. Let fallbackProto be \"%GeneratorFunction.prototype%\".\n            StandardConstructors::generator_function\n        } else {\n            // 2. If kind is normal, then\n            //     a. Let prefix be \"function\".\n            //     b. Let exprSym be the grammar symbol FunctionExpression.\n            //     c. Let bodySym be the grammar symbol FunctionBody[~Yield, ~Await].\n            //     d. Let parameterSym be the grammar symbol FormalParameters[~Yield, ~Await].\n            //     e. Let fallbackProto be \"%Function.prototype%\".\n            StandardConstructors::function\n        };\n\n        // 22. Let proto be ? GetPrototypeFromConstructor(newTarget, fallbackProto).\n        let prototype = get_prototype_from_constructor(&new_target, default, context)?;\n\n        // 6. Let argCount be the number of elements in parameterArgs.\n        let (body, param_list) = if let Some((body, params)) = args.split_last() {\n            // 7. Let parameterStrings be a new empty List.\n            let mut parameters = Vec::with_capacity(args.len());\n\n            // 8. For each element arg of parameterArgs, do\n            for param in params {\n                // a. Append ? ToString(arg) to parameterStrings.\n                parameters.push(param.to_string(context)?);\n            }\n\n            // 9. Let bodyString be ? ToString(bodyArg).\n            let body = body.to_string(context)?;\n\n            (body, parameters)\n        } else {\n            (js_string!(), Vec::new())\n        };\n        let current_realm = context.realm().clone();\n\n        context.host_hooks().ensure_can_compile_strings(\n            current_realm,\n            &param_list,\n            &body,\n            false,\n            context,\n        )?;\n\n        let parameters = if param_list.is_empty() {\n            FormalParameterList::default()\n        } else {\n            // 12. Let P be the empty String.\n            // 13. If argCount > 0, then\n            //     a. Set P to parameterStrings[0].\n            //     b. Let k be 1.\n            //     c. Repeat, while k < argCount,\n            //         i. Let nextArgString be parameterStrings[k].\n            //         ii. Set P to the string-concatenation of P, \",\" (a comma), and nextArgString.\n            //         iii. Set k to k + 1.\n\n            // TODO: Replace with standard `Iterator::intersperse` iterator method when it's stabilized.\n            //       See: <https://github.com/rust-lang/rust/issues/79524>\n            let parameters = itertools::Itertools::intersperse(\n                param_list.iter().map(JsString::iter),\n                js_str!(\",\").iter(),\n            )\n            .flatten()\n            .collect::<Vec<_>>();\n            let mut parser = Parser::new(Source::from_utf16(&parameters));\n            parser.set_identifier(context.next_parser_identifier());\n\n            // 17. Let parameters be ParseText(StringToCodePoints(P), parameterSym).\n            // 18. If parameters is a List of errors, throw a SyntaxError exception.\n            let parameters = parser\n                .parse_formal_parameters(context.interner_mut(), generator, r#async)\n                .map_err(|e| {\n                    JsNativeError::syntax()\n                        .with_message(format!(\"failed to parse function parameters: {e}\"))\n                })?;\n\n            // It is a Syntax Error if FormalParameters Contains YieldExpression is true.\n            if generator && contains(&parameters, ContainsSymbol::YieldExpression) {\n                return Err(JsNativeError::syntax().with_message(\n                    if r#async {\n                        \"yield expression is not allowed in formal parameter list of async generator\"\n                    } else {\n                        \"yield expression is not allowed in formal parameter list of generator\"\n                    }\n                ).into());\n            }\n\n            // It is a Syntax Error if FormalParameters Contains AwaitExpression is true.\n            if r#async && contains(&parameters, ContainsSymbol::AwaitExpression) {\n                return Err(JsNativeError::syntax()\n                    .with_message(\n                        if generator {\n                            \"await expression is not allowed in formal parameter list of async function\"\n                        } else {\n                            \"await expression is not allowed in formal parameter list of async generator\"\n                        })\n                    .into());\n            }\n\n            parameters\n        };\n\n        let body = if body.is_empty() {\n            FunctionBody::new(StatementList::default(), Span::new((1, 1), (1, 1)))\n        } else {\n            // 14. Let bodyParseString be the string-concatenation of 0x000A (LINE FEED), bodyString, and 0x000A (LINE FEED).\n            let mut body_parse = Vec::with_capacity(body.len());\n            body_parse.push(u16::from(b'\\n'));\n            body_parse.extend(body.iter());\n            body_parse.push(u16::from(b'\\n'));\n\n            // 19. Let body be ParseText(StringToCodePoints(bodyParseString), bodySym).\n            // 20. If body is a List of errors, throw a SyntaxError exception.\n            let mut parser = Parser::new(Source::from_utf16(&body_parse));\n            parser.set_identifier(context.next_parser_identifier());\n\n            // 19. Let body be ParseText(StringToCodePoints(bodyParseString), bodySym).\n            // 20. If body is a List of errors, throw a SyntaxError exception.\n            let body = parser\n                .parse_function_body(context.interner_mut(), generator, r#async)\n                .map_err(|e| {\n                    JsNativeError::syntax()\n                        .with_message(format!(\"failed to parse function body: {e}\"))\n                })?;\n\n            // It is a Syntax Error if AllPrivateIdentifiersValid of StatementList with argument « »\n            // is false unless the source text containing ScriptBody is eval code that is being\n            // processed by a direct eval.\n            // https://tc39.es/ecma262/#sec-scripts-static-semantics-early-errors\n            if !all_private_identifiers_valid(&body, Vec::new()) {\n                return Err(JsNativeError::syntax()\n                    .with_message(\"invalid private identifier usage\")\n                    .into());\n            }\n\n            // 21. NOTE: The parameters and body are parsed separately to ensure that each is valid alone. For example, new Function(\"/*\", \"*/ ) {\") does not evaluate to a function.\n            // 22. NOTE: If this step is reached, sourceText must have the syntax of exprSym (although the reverse implication does not hold). The purpose of the next two steps is to enforce any Early Error rules which apply to exprSym directly.\n            // 23. Let expr be ParseText(sourceText, exprSym).\n            // 24. If expr is a List of errors, throw a SyntaxError exception.\n            // Check for errors that apply for the combination of body and parameters.\n\n            // Early Error: If BindingIdentifier is present and the source text matched by BindingIdentifier is strict mode code,\n            // it is a Syntax Error if the StringValue of BindingIdentifier is \"eval\" or \"arguments\".\n            if body.strict() {\n                for name in bound_names(&parameters) {\n                    if name == Sym::ARGUMENTS || name == Sym::EVAL {\n                        return Err(JsNativeError::syntax()\n                            .with_message(\"Unexpected 'eval' or 'arguments' in strict mode\")\n                            .into());\n                    }\n                }\n            }\n\n            // Early Error: If the source code matching FormalParameters is strict mode code,\n            // the Early Error rules for UniqueFormalParameters : FormalParameters are applied.\n            if (body.strict()) && parameters.has_duplicates() {\n                return Err(JsNativeError::syntax()\n                    .with_message(\"Duplicate parameter name not allowed in this context\")\n                    .into());\n            }\n\n            // Early Error: It is a Syntax Error if FunctionBodyContainsUseStrict of GeneratorBody is true\n            // and IsSimpleParameterList of FormalParameters is false.\n            if body.strict() && !parameters.is_simple() {\n                return Err(JsNativeError::syntax()\n                    .with_message(\n                        \"Illegal 'use strict' directive in function with non-simple parameter list\",\n                    )\n                    .into());\n            }\n\n            // It is a Syntax Error if FunctionBody Contains SuperProperty is true.\n            if contains(&body, ContainsSymbol::SuperProperty) {\n                return Err(JsNativeError::syntax()\n                    .with_message(\"invalid `super` reference\")\n                    .into());\n            }\n\n            // It is a Syntax Error if FunctionBody Contains SuperCall is true.\n            if contains(&body, ContainsSymbol::SuperCall) {\n                return Err(JsNativeError::syntax()\n                    .with_message(\"invalid `super` call\")\n                    .into());\n            }\n\n            // It is a Syntax Error if any element of the BoundNames of FormalParameters\n            // also occurs in the LexicallyDeclaredNames of FunctionBody.\n            // https://tc39.es/ecma262/#sec-function-definitions-static-semantics-early-errors\n            {\n                let lexically_declared_names = lexically_declared_names(&body);\n                for name in bound_names(&parameters) {\n                    if lexically_declared_names.contains(&name) {\n                        return Err(JsNativeError::syntax()\n                            .with_message(format!(\n                                \"Redeclaration of formal parameter `{}`\",\n                                context.interner().resolve_expect(name)\n                            ))\n                            .into());\n                    }\n                }\n            }\n\n            body\n        };\n\n        // TODO: create SourceText : \"anonymous(\" parameters \\n \") {\" body_parse \"}\"\n\n        let function_span_start = Position::new(1, 1);\n        let function_span_end = body.span().end();\n        let mut function = boa_ast::function::FunctionExpression::new(\n            None,\n            parameters,\n            body,\n            None,\n            false,\n            Span::new(function_span_start, function_span_end),\n        );\n        if let Err(reason) =\n            function.analyze_scope(strict, context.realm().scope(), context.interner())\n        {\n            return Err(js_error!(SyntaxError: \"failed to analyze function scope: {}\", reason));\n        }\n\n        let in_with = context.vm.frame().environments.has_object_environment();\n        let spanned_source_text = SpannedSourceText::new_empty();\n\n        let code = FunctionCompiler::new(spanned_source_text)\n            .name(js_string!(\"anonymous\"))\n            .generator(generator)\n            .r#async(r#async)\n            .in_with(in_with)\n            .force_function_scope(true)\n            .compile(\n                function.parameters(),\n                function.body(),\n                context.realm().scope().clone(),\n                context.realm().scope().clone(),\n                function.scopes(),\n                function.contains_direct_eval(),\n                context.interner_mut(),\n            );\n\n        let saved = context.vm.frame_mut().environments.pop_to_global();\n        let function_object = crate::vm::create_function_object(code, prototype, context);\n        context\n            .vm\n            .frame_mut()\n            .environments\n            .restore_from_saved(saved);\n\n        Ok(function_object)\n    }\n\n    /// `Function.prototype.apply ( thisArg, argArray )`\n    ///\n    /// The `apply()` method invokes self with the first argument as the `this` value\n    /// and the rest of the arguments provided as an array (or an array-like object).\n    ///\n    /// More information:\n    ///  - [MDN documentation][mdn]\n    ///  - [ECMAScript reference][spec]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-function.prototype.apply\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/apply\n    fn apply(this: &JsValue, args: &[JsValue], context: &mut Context) -> JsResult<JsValue> {\n        // 1. Let func be the this value.\n        // 2. If IsCallable(func) is false, throw a TypeError exception.\n        let func = this.as_callable().ok_or_else(|| {\n            JsNativeError::typ().with_message(format!(\"{} is not a function\", this.display()))\n        })?;\n\n        let this_arg = args.get_or_undefined(0);\n        let arg_array = args.get_or_undefined(1);\n        // 3. If argArray is undefined or null, then\n        if arg_array.is_null_or_undefined() {\n            // a. Perform PrepareForTailCall().\n            // TODO?: 3.a. PrepareForTailCall\n\n            // b. Return ? Call(func, thisArg).\n            return func.call(this_arg, &[], context);\n        }\n\n        // 4. Let argList be ? CreateListFromArrayLike(argArray).\n        let arg_list = arg_array.create_list_from_array_like(&[], context)?;\n\n        // 5. Perform PrepareForTailCall().\n        // TODO?: 5. PrepareForTailCall\n\n        // 6. Return ? Call(func, thisArg, argList).\n        func.call(this_arg, &arg_list, context)\n    }\n\n    /// `Function.prototype.bind ( thisArg, ...args )`\n    ///\n    /// The `bind()` method creates a new function that, when called, has its\n    /// this keyword set to the provided value, with a given sequence of arguments\n    /// preceding any provided when the new function is called.\n    ///\n    /// More information:\n    ///  - [MDN documentation][mdn]\n    ///  - [ECMAScript reference][spec]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-function.prototype.bind\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_objects/Function/bind\n    fn bind(this: &JsValue, args: &[JsValue], context: &mut Context) -> JsResult<JsValue> {\n        // 1. Let Target be the this value.\n        // 2. If IsCallable(Target) is false, throw a TypeError exception.\n        let target = this.as_callable().ok_or_else(|| {\n            JsNativeError::typ()\n                .with_message(\"cannot bind `this` without a `[[Call]]` internal method\")\n        })?;\n\n        let this_arg = args.get_or_undefined(0).clone();\n        let bound_args = args.get(1..).unwrap_or(&[]).to_vec();\n        let arg_count = bound_args.len() as i64;\n\n        // 3. Let F be ? BoundFunctionCreate(Target, thisArg, args).\n        let f = BoundFunction::create(target.clone(), this_arg, bound_args, context)?;\n\n        // 4. Let L be 0.\n        let mut l = JsValue::new(0);\n\n        // 5. Let targetHasLength be ? HasOwnProperty(Target, \"length\").\n        // 6. If targetHasLength is true, then\n        if target.has_own_property(StaticJsStrings::LENGTH, context)? {\n            // a. Let targetLen be ? Get(Target, \"length\").\n            let target_len = target.get(StaticJsStrings::LENGTH, context)?;\n            // b. If Type(targetLen) is Number, then\n            if target_len.is_number() {\n                // 1. Let targetLenAsInt be ! ToIntegerOrInfinity(targetLen).\n                match target_len\n                    .to_integer_or_infinity(context)\n                    .js_expect(\"to_integer_or_infinity cannot fail for a number\")?\n                {\n                    // i. If targetLen is +∞𝔽, set L to +∞.\n                    IntegerOrInfinity::PositiveInfinity => l = f64::INFINITY.into(),\n                    // ii. Else if targetLen is -∞𝔽, set L to 0.\n                    IntegerOrInfinity::NegativeInfinity => {}\n                    // iii. Else,\n                    IntegerOrInfinity::Integer(target_len) => {\n                        // 2. Assert: targetLenAsInt is finite.\n                        // 3. Let argCount be the number of elements in args.\n                        // 4. Set L to max(targetLenAsInt - argCount, 0).\n                        l = (target_len - arg_count).max(0).into();\n                    }\n                }\n            }\n        }\n\n        // 7. Perform ! SetFunctionLength(F, L).\n        f.define_property_or_throw(\n            StaticJsStrings::LENGTH,\n            PropertyDescriptor::builder()\n                .value(l)\n                .writable(false)\n                .enumerable(false)\n                .configurable(true),\n            context,\n        )\n        .js_expect(\"defining the `length` property for a new object should not fail\")?;\n\n        // 8. Let targetName be ? Get(Target, \"name\").\n        let target_name = target.get(js_string!(\"name\"), context)?;\n\n        // 9. If Type(targetName) is not String, set targetName to the empty String.\n        let target_name = target_name.as_string().unwrap_or_default();\n\n        // 10. Perform SetFunctionName(F, targetName, \"bound\").\n        set_function_name(&f, &target_name.into(), Some(js_str!(\"bound\")), context)?;\n\n        // 11. Return F.\n        Ok(f.into())\n    }\n\n    /// `Function.prototype.call ( thisArg, ...args )`\n    ///\n    /// The `call()` method calls a function with a given this value and arguments provided individually.\n    ///\n    /// More information:\n    ///  - [MDN documentation][mdn]\n    ///  - [ECMAScript reference][spec]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-function.prototype.call\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/call\n    fn call(this: &JsValue, args: &[JsValue], context: &mut Context) -> JsResult<JsValue> {\n        // 1. Let func be the this value.\n        // 2. If IsCallable(func) is false, throw a TypeError exception.\n        let func = this.as_callable().ok_or_else(|| {\n            JsNativeError::typ().with_message(format!(\"{} is not a function\", this.display()))\n        })?;\n        let this_arg = args.get_or_undefined(0);\n\n        // 3. Perform PrepareForTailCall().\n        // TODO?: 3. Perform PrepareForTailCall\n\n        // 4. Return ? Call(func, thisArg, args).\n        func.call(this_arg, args.get(1..).unwrap_or(&[]), context)\n    }\n\n    /// `Function.prototype.toString()`\n    ///\n    /// More information:\n    ///  - [MDN documentation][mdn]\n    ///  - [ECMAScript reference][spec]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-function.prototype.tostring\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/toString\n    #[allow(clippy::wrong_self_convention)]\n    fn to_string(this: &JsValue, _: &[JsValue], context: &mut Context) -> JsResult<JsValue> {\n        // 1. Let func be the this value.\n        let func = this;\n\n        // TODO:\n        //    2. If func is an Object, func has a [[SourceText]] internal slot, func.[[SourceText]] is a sequence of Unicode code points,and HostHasSourceTextAvailable(func) is true, then\n        //        a. Return CodePointsToString(func.[[SourceText]]).\n\n        // 3. If func is a built-in function object, return an implementation-defined String source code representation of func.\n        //    The representation must have the syntax of a NativeFunction. Additionally, if func has an [[InitialName]] internal slot and\n        //    func.[[InitialName]] is a String, the portion of the returned String that would be matched by\n        //    NativeFunctionAccessor_opt PropertyName must be the value of func.[[InitialName]].\n\n        // 4. If func is an Object and IsCallable(func) is true, return an implementation-defined String source code representation of func.\n        //    The representation must have the syntax of a NativeFunction.\n        // 5. Throw a TypeError exception.\n        let Some(object) = func.as_callable() else {\n            return Err(JsNativeError::typ().with_message(\"not a function\").into());\n        };\n\n        if object.is::<NativeFunctionObject>() {\n            let name = {\n                // Is there a case here where if there is no name field on a value\n                // name should default to None? Do all functions have names set?\n                let value = object.get(js_string!(\"name\"), &mut *context)?;\n                if value.is_null_or_undefined() {\n                    js_string!()\n                } else {\n                    value.to_string(context)?\n                }\n            };\n            return Ok(\n                js_string!(js_str!(\"function \"), &name, js_str!(\"() { [native code] }\")).into(),\n            );\n        } else if object.is::<Proxy>() || object.is::<BoundFunction>() {\n            return Ok(js_string!(\"function () { [native code] }\").into());\n        }\n\n        let function = object\n            .downcast_ref::<OrdinaryFunction>()\n            .ok_or_else(|| JsNativeError::typ().with_message(\"not a function\"))?;\n\n        let code = function.codeblock();\n        if let Some(code_points) = code.source_info().text_spanned().to_code_points() {\n            return Ok(JsString::from(code_points).into());\n        }\n\n        Ok(js_string!(\n            js_str!(\"function \"),\n            code.name(),\n            js_str!(\"() { [native code] }\")\n        )\n        .into())\n    }\n\n    /// `Function.prototype [ @@hasInstance ] ( V )`\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-function.prototype-@@hasinstance\n    fn has_instance(this: &JsValue, args: &[JsValue], context: &mut Context) -> JsResult<JsValue> {\n        // 1. Let F be the this value.\n        // 2. Return ? OrdinaryHasInstance(F, V).\n        Ok(JsValue::ordinary_has_instance(this, args.get_or_undefined(0), context)?.into())\n    }\n\n    #[allow(clippy::unnecessary_wraps)]\n    fn prototype(_: &JsValue, _: &[JsValue], _: &mut Context) -> JsResult<JsValue> {\n        Ok(JsValue::undefined())\n    }\n}\n\n/// Abstract operation `SetFunctionName`\n///\n/// More information:\n///  - [ECMAScript reference][spec]\n///\n/// [spec]: https://tc39.es/ecma262/#sec-setfunctionname\npub(crate) fn set_function_name(\n    function: &JsObject,\n    name: &PropertyKey,\n    prefix: Option<JsStr<'_>>,\n    context: &mut Context,\n) -> JsResult<()> {\n    // 1. Assert: F is an extensible object that does not have a \"name\" own property.\n    // 2. If Type(name) is Symbol, then\n    let mut name = match name {\n        PropertyKey::Symbol(sym) => {\n            // a. Let description be name's [[Description]] value.\n            // b. If description is undefined, set name to the empty String.\n            // c. Else, set name to the string-concatenation of \"[\", description, and \"]\".\n            sym.description().map_or_else(\n                || js_string!(),\n                |desc| js_string!(js_str!(\"[\"), &desc, js_str!(\"]\")),\n            )\n        }\n        PropertyKey::String(string) => string.clone(),\n        PropertyKey::Index(index) => js_string!(format!(\"{}\", index.get())),\n    };\n\n    // 3. Else if name is a Private Name, then\n    // a. Set name to name.[[Description]].\n    // todo: implement Private Names\n\n    // 4. If F has an [[InitialName]] internal slot, then\n    // a. Set F.[[InitialName]] to name.\n    // todo: implement [[InitialName]] for builtins\n\n    // 5. If prefix is present, then\n    if let Some(prefix) = prefix {\n        name = js_string!(prefix, js_str!(\" \"), &name);\n        // b. If F has an [[InitialName]] internal slot, then\n        // i. Optionally, set F.[[InitialName]] to name.\n        // todo: implement [[InitialName]] for builtins\n    }\n\n    // 6. Return ! DefinePropertyOrThrow(F, \"name\", PropertyDescriptor { [[Value]]: name,\n    // [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }).\n    function\n        .define_property_or_throw(\n            js_string!(\"name\"),\n            PropertyDescriptor::builder()\n                .value(name)\n                .writable(false)\n                .enumerable(false)\n                .configurable(true),\n            context,\n        )\n        .js_expect(\"defining the `name` property must not fail per the spec\")?;\n\n    Ok(())\n}\n\n/// Call this object.\n///\n/// # Panics\n///\n/// Panics if the object is currently mutably borrowed.\n// <https://tc39.es/ecma262/#sec-prepareforordinarycall>\n// <https://tc39.es/ecma262/#sec-ecmascript-function-objects-call-thisargument-argumentslist>\npub(crate) fn function_call(\n    function_object: &JsObject,\n    argument_count: usize,\n    #[allow(\n        unused_variables,\n        reason = \"Only used if native-backtrace feature is enabled\"\n    )]\n    context: &mut InternalMethodCallContext<'_>,\n) -> JsResult<CallValue> {\n    context.check_runtime_limits()?;\n\n    let function = function_object\n        .downcast_ref::<OrdinaryFunction>()\n        .js_expect(\"not a function\")?;\n    let realm = function.realm().clone();\n\n    if function.code.is_class_constructor() {\n        debug_assert!(\n            function.is_ordinary(),\n            \"only ordinary functions can be classes\"\n        );\n        return Err(JsNativeError::typ()\n            .with_message(\"class constructor cannot be invoked without 'new'\")\n            .with_realm(realm)\n            .into());\n    }\n\n    let code = function.code.clone();\n    let environments = function.environments.clone();\n    let script_or_module = function.script_or_module.clone();\n\n    drop(function);\n\n    let env_fp = environments.len() as u32;\n\n    let frame = CallFrame::new(code, script_or_module, environments, realm)\n        .with_argument_count(argument_count as u32)\n        .with_env_fp(env_fp);\n\n    #[cfg(feature = \"native-backtrace\")]\n    {\n        let native_source_info = context.native_source_info();\n        context\n            .vm\n            .shadow_stack\n            .patch_last_native(native_source_info);\n    }\n\n    context.vm.push_frame(frame);\n    context.vm.set_return_value(JsValue::undefined());\n\n    let context = context.context();\n\n    let lexical_this_mode = context.vm.frame().code_block.this_mode == ThisMode::Lexical;\n    let this = if lexical_this_mode {\n        ThisBindingStatus::Lexical\n    } else {\n        let this = context.vm.stack.get_this(context.vm.frame());\n        if context.vm.frame().code_block.strict() {\n            context.vm.frame_mut().flags |= CallFrameFlags::THIS_VALUE_CACHED;\n            ThisBindingStatus::Initialized(this)\n        } else if this.is_null_or_undefined() {\n            context.vm.frame_mut().flags |= CallFrameFlags::THIS_VALUE_CACHED;\n            let this: JsValue = context.realm().global_this().clone().into();\n            context.vm.stack.set_this(\n                context.vm.frames.last().js_expect(\"frame must exist\")?,\n                this.clone(),\n            );\n            ThisBindingStatus::Initialized(this)\n        } else {\n            let this: JsValue = this\n                .to_object(context)\n                .js_expect(\"conversion cannot fail\")?\n                .into();\n            context.vm.frame_mut().flags |= CallFrameFlags::THIS_VALUE_CACHED;\n            context.vm.stack.set_this(\n                context.vm.frames.last().js_expect(\"frame must exist\")?,\n                this.clone(),\n            );\n            ThisBindingStatus::Initialized(this)\n        }\n    };\n\n    let mut last_env = 0;\n\n    let has_binding_identifier = context.vm.frame().code_block().has_binding_identifier();\n    let has_function_scope = context.vm.frame().code_block().has_function_scope();\n\n    if has_binding_identifier {\n        let frame = context.vm.frame_mut();\n        let global = frame.realm.environment();\n        let index = frame.environments.push_lexical(1, global);\n        frame.environments.put_lexical_value(\n            BindingLocatorScope::Stack(index),\n            0,\n            function_object.clone().into(),\n            global,\n        );\n        last_env += 1;\n    }\n\n    if has_function_scope {\n        let scope = context.vm.frame().code_block().constant_scope(last_env);\n        let frame = context.vm.frame_mut();\n        let global = frame.realm.environment();\n        frame.environments.push_function(\n            scope,\n            FunctionSlots::new(this, function_object.clone(), None),\n            global,\n        );\n    }\n\n    Ok(CallValue::Ready)\n}\n\n/// Construct an instance of this object with the specified arguments.\n///\n/// # Panics\n///\n/// Panics if the object is currently mutably borrowed.\n// <https://tc39.es/ecma262/#sec-ecmascript-function-objects-construct-argumentslist-newtarget>\nfn function_construct(\n    this_function_object: &JsObject,\n    argument_count: usize,\n    context: &mut InternalMethodCallContext<'_>,\n) -> JsResult<CallValue> {\n    context.check_runtime_limits()?;\n\n    let function = this_function_object\n        .downcast_ref::<OrdinaryFunction>()\n        .js_expect(\"not a function\")?;\n    let realm = function.realm().clone();\n\n    debug_assert!(\n        function.is_ordinary(),\n        \"only ordinary functions can be constructed\"\n    );\n\n    let code = function.code.clone();\n    let environments = function.environments.clone();\n    let script_or_module = function.script_or_module.clone();\n    drop(function);\n\n    let env_fp = environments.len() as u32;\n\n    let new_target = context.vm.stack.pop();\n\n    let this = if code.is_derived_constructor() {\n        None\n    } else {\n        // If the prototype of the constructor is not an object, then use the default object\n        // prototype as prototype for the new object\n        // see <https://tc39.es/ecma262/#sec-ordinarycreatefromconstructor>\n        // see <https://tc39.es/ecma262/#sec-getprototypefromconstructor>\n        let prototype =\n            get_prototype_from_constructor(&new_target, StandardConstructors::object, context)?;\n        let this = JsObject::from_proto_and_data_with_shared_shape(\n            context.root_shape(),\n            prototype,\n            OrdinaryObject,\n        )\n        .upcast();\n\n        this.initialize_instance_elements(this_function_object, context)?;\n\n        Some(this)\n    };\n\n    let mut frame = CallFrame::new(code, script_or_module, environments, realm)\n        .with_argument_count(argument_count as u32)\n        .with_env_fp(env_fp)\n        .with_flags(CallFrameFlags::CONSTRUCT);\n\n    // We push the `this` below so we can mark this function as having the this value\n    // cached if it's initialized.\n    frame\n        .flags\n        .set(CallFrameFlags::THIS_VALUE_CACHED, this.is_some());\n\n    #[cfg(feature = \"native-backtrace\")]\n    {\n        let native_source_info = context.native_source_info();\n        context\n            .vm\n            .shadow_stack\n            .patch_last_native(native_source_info);\n    }\n\n    context.vm.push_frame(frame);\n    context.vm.set_return_value(JsValue::undefined());\n\n    let mut last_env = 0;\n\n    let has_binding_identifier = context.vm.frame().code_block().has_binding_identifier();\n    let has_function_scope = context.vm.frame().code_block().has_function_scope();\n\n    if has_binding_identifier {\n        let frame = context.vm.frame_mut();\n        let global = frame.realm.environment();\n        let index = frame.environments.push_lexical(1, global);\n        frame.environments.put_lexical_value(\n            BindingLocatorScope::Stack(index),\n            0,\n            this_function_object.clone().into(),\n            global,\n        );\n        last_env += 1;\n    }\n\n    if has_function_scope {\n        let scope = context.vm.frame().code_block().constant_scope(last_env);\n        let frame = context.vm.frame_mut();\n        let global = frame.realm.environment();\n        frame.environments.push_function(\n            scope,\n            FunctionSlots::new(\n                this.clone().map_or(ThisBindingStatus::Uninitialized, |o| {\n                    ThisBindingStatus::Initialized(o.into())\n                }),\n                this_function_object.clone(),\n                Some(\n                    new_target\n                        .as_object()\n                        .js_expect(\"new.target should be an object\")?\n                        .clone(),\n                ),\n            ),\n            global,\n        );\n    }\n\n    let context = context.context();\n    context.vm.stack.set_this(\n        context.vm.frames.last().js_expect(\"frame must exist\")?,\n        this.map(JsValue::new).unwrap_or_default(),\n    );\n\n    Ok(CallValue::Ready)\n}\n"
  },
  {
    "path": "core/engine/src/builtins/function/tests.rs",
    "content": "use crate::{\n    JsNativeErrorKind, JsValue, TestAction,\n    error::JsNativeError,\n    js_string,\n    native_function::NativeFunction,\n    object::{FunctionObjectBuilder, JsObject},\n    property::{Attribute, PropertyDescriptor},\n    run_test_actions,\n};\nuse boa_macros::js_str;\nuse indoc::indoc;\n\n#[allow(clippy::float_cmp)]\n#[test]\nfn arguments_object() {\n    run_test_actions([\n        TestAction::run(indoc! {r#\"\n                function jason(a, b) {\n                    return arguments[0];\n                }\n            \"#}),\n        TestAction::assert_eq(\"jason(100, 6)\", 100),\n    ]);\n}\n\n#[test]\nfn self_mutating_function_when_calling() {\n    run_test_actions([\n        TestAction::run(indoc! {r#\"\n                function x() {\n                    x.y = 3;\n                }\n                x();\n            \"#}),\n        TestAction::assert_eq(\"x.y\", 3),\n    ]);\n}\n\n#[test]\nfn self_mutating_function_when_constructing() {\n    run_test_actions([\n        TestAction::run(indoc! {r#\"\n                function x() {\n                    x.y = 3;\n                }\n                new x();\n            \"#}),\n        TestAction::assert_eq(\"x.y\", 3),\n    ]);\n}\n\n#[test]\nfn function_prototype() {\n    run_test_actions([\n        TestAction::assert_eq(\"Function.prototype.name\", js_string!()),\n        TestAction::assert_eq(\"Function.prototype.length\", 0),\n        TestAction::assert_eq(\"Function.prototype()\", JsValue::undefined()),\n        TestAction::assert_eq(\n            \"Function.prototype(1, '', new String(''))\",\n            JsValue::undefined(),\n        ),\n        TestAction::assert_native_error(\n            \"new Function.prototype()\",\n            JsNativeErrorKind::Type,\n            \"not a constructor\",\n        ),\n    ]);\n}\n\n#[test]\nfn function_prototype_call() {\n    run_test_actions([TestAction::assert_eq(\n        \"Object.prototype.toString.call(new Error())\",\n        js_str!(\"[object Error]\"),\n    )]);\n}\n\n#[test]\nfn function_prototype_call_throw() {\n    run_test_actions([TestAction::assert_native_error(\n        indoc! {r#\"\n            let call = Function.prototype.call;\n            call(call)\n        \"#},\n        JsNativeErrorKind::Type,\n        \"undefined is not a function\",\n    )]);\n}\n\n#[test]\nfn function_prototype_call_multiple_args() {\n    run_test_actions([\n        TestAction::run(indoc! {r#\"\n            function f(a, b) {\n                this.a = a;\n                this.b = b;\n            }\n            let o = {a: 0, b: 0};\n            f.call(o, 1, 2);\n        \"#}),\n        TestAction::assert_eq(\"o.a\", 1),\n        TestAction::assert_eq(\"o.b\", 2),\n    ]);\n}\n\n#[test]\nfn function_prototype_apply() {\n    run_test_actions([\n        TestAction::run(\"const numbers = [6, 7, 3, 4, 2]\"),\n        TestAction::assert_eq(\"Math.max.apply(null, numbers)\", 7),\n        TestAction::assert_eq(\"Math.min.apply(null, numbers)\", 2),\n    ]);\n}\n\n#[test]\nfn function_prototype_apply_on_object() {\n    run_test_actions([\n        TestAction::run(indoc! {r#\"\n                function f(a, b) {\n                    this.a = a;\n                    this.b = b;\n                }\n                let o = {a: 0, b: 0};\n                f.apply(o, [1, 2]);\n            \"#}),\n        TestAction::assert_eq(\"o.a\", 1),\n        TestAction::assert_eq(\"o.b\", 2),\n    ]);\n}\n\n#[test]\nfn closure_capture_clone() {\n    run_test_actions([\n        TestAction::inspect_context(|ctx| {\n            let string = js_string!(\"Hello\");\n            let object = JsObject::with_object_proto(ctx.intrinsics());\n            object\n                .define_property_or_throw(\n                    js_string!(\"key\"),\n                    PropertyDescriptor::builder()\n                        .value(js_string!(\" world!\"))\n                        .writable(false)\n                        .enumerable(false)\n                        .configurable(false),\n                    ctx,\n                )\n                .unwrap();\n\n            let func = FunctionObjectBuilder::new(\n                ctx.realm(),\n                NativeFunction::from_copy_closure_with_captures(\n                    |_, _, captures, context| {\n                        let (string, object) = &captures;\n\n                        let hw = js_string!(\n                            string,\n                            &object\n                                .__get_own_property__(\n                                    &js_string!(\"key\").into(),\n                                    &mut context.into()\n                                )?\n                                .and_then(|prop| prop.value().and_then(JsValue::as_string))\n                                .ok_or_else(\n                                    || JsNativeError::typ().with_message(\"invalid `key` property\")\n                                )?\n                        );\n                        Ok(hw.into())\n                    },\n                    (string, object),\n                ),\n            )\n            .name(js_str!(\"closure\"))\n            .build();\n\n            ctx.register_global_property(js_str!(\"closure\"), func, Attribute::default())\n                .unwrap();\n        }),\n        TestAction::assert_eq(\"closure()\", js_str!(\"Hello world!\")),\n    ]);\n}\n\n#[test]\nfn function_constructor_early_errors_super() {\n    run_test_actions([\n        TestAction::assert_native_error(\n            \"Function('super()')()\",\n            JsNativeErrorKind::Syntax,\n            \"invalid `super` call\",\n        ),\n        TestAction::assert_native_error(\n            \"Function('super.a')()\",\n            JsNativeErrorKind::Syntax,\n            \"invalid `super` reference\",\n        ),\n    ]);\n}\n"
  },
  {
    "path": "core/engine/src/builtins/generator/mod.rs",
    "content": "//! Boa's implementation of ECMAScript's global `Generator` object.\n//!\n//! A Generator is an instance of a generator function and conforms to both the Iterator and Iterable interfaces.\n//!\n//! More information:\n//!  - [ECMAScript reference][spec]\n//!  - [MDN documentation][mdn]\n//!\n//! [spec]: https://tc39.es/ecma262/#sec-generator-objects\n//! [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Generator\n\nuse crate::{\n    Context, JsArgs, JsData, JsError, JsExpect, JsResult, JsString,\n    builtins::iterable::create_iter_result_object,\n    context::intrinsics::Intrinsics,\n    error::JsNativeError,\n    error::PanicError,\n    js_string,\n    object::{CONSTRUCTOR, JsObject},\n    property::Attribute,\n    realm::Realm,\n    string::StaticJsStrings,\n    symbol::JsSymbol,\n    value::JsValue,\n    vm::{CallFrame, CallFrameFlags, CompletionRecord, GeneratorResumeKind, Stack},\n};\nuse boa_gc::{Finalize, Trace, custom_trace};\n\nuse super::{BuiltInBuilder, IntrinsicObject};\n\n/// Indicates the state of a generator.\n#[derive(Debug, Finalize)]\npub(crate) enum GeneratorState {\n    SuspendedStart {\n        /// The `[[GeneratorContext]]` internal slot.\n        context: GeneratorContext,\n    },\n    SuspendedYield {\n        /// The `[[GeneratorContext]]` internal slot.\n        context: GeneratorContext,\n    },\n    Executing,\n    Completed,\n}\n\n// Need to manually implement, since `Trace` adds a `Drop` impl which disallows destructuring.\nunsafe impl Trace for GeneratorState {\n    custom_trace!(this, mark, {\n        match &this {\n            Self::SuspendedStart { context } | Self::SuspendedYield { context } => mark(context),\n            Self::Executing | Self::Completed => {}\n        }\n    });\n}\n\n/// Holds all information that a generator needs to continue it's execution.\n///\n/// All of the fields must be changed with those that are currently present in the\n/// context/vm before the generator execution starts/resumes and after it has ended/yielded.\n#[derive(Debug, Trace, Finalize)]\npub(crate) struct GeneratorContext {\n    pub(crate) stack: Stack,\n    pub(crate) call_frame: Option<CallFrame>,\n}\n\nimpl GeneratorContext {\n    /// Creates a new `GeneratorContext` from the current `Context` state.\n    pub(crate) fn from_current(context: &mut Context, async_generator: Option<JsObject>) -> Self {\n        let mut frame = context.vm.frame().clone();\n        frame.environments = context.vm.frame().environments.clone();\n        frame.realm = context.realm().clone();\n\n        // Split the stack at fp. The split-off portion starts at what was fp,\n        // so adjust rp and fp to be relative to the new base.\n        let mut stack = context.vm.stack.split_off_frame(&frame);\n        frame.rp -= frame.fp;\n        frame.fp = 0;\n\n        // NOTE: Since we get a pre-built call frame with stack, and we reuse them.\n        //       So we don't need to push the registers in subsequent calls.\n        frame.flags |= CallFrameFlags::REGISTERS_ALREADY_PUSHED;\n\n        if let Some(async_generator) = async_generator {\n            stack.set_register(\n                &frame,\n                CallFrame::ASYNC_GENERATOR_OBJECT_REGISTER_INDEX,\n                async_generator.into(),\n            );\n        }\n\n        Self {\n            call_frame: Some(frame),\n            stack,\n        }\n    }\n\n    /// Resumes execution with `GeneratorContext` as the current execution context.\n    pub(crate) fn resume(\n        &mut self,\n        value: Option<JsValue>,\n        resume_kind: GeneratorResumeKind,\n        context: &mut Context,\n    ) -> CompletionRecord {\n        std::mem::swap(&mut context.vm.stack, &mut self.stack);\n        let Some(frame) = self.call_frame.take() else {\n            return CompletionRecord::Throw(PanicError::new(\"should have a call frame\").into());\n        };\n        let fp = frame.fp;\n        let rp = frame.rp;\n        context.vm.push_frame(frame);\n\n        let frame = context.vm.frame_mut();\n        frame.fp = fp;\n        frame.rp = rp;\n        frame.set_exit_early(true);\n\n        if let Some(value) = value {\n            context.vm.stack.push(value);\n        }\n        context.vm.stack.push(resume_kind);\n\n        let result = context.run();\n\n        std::mem::swap(&mut context.vm.stack, &mut self.stack);\n        self.call_frame = context.vm.pop_frame();\n        assert!(self.call_frame.is_some());\n        result\n    }\n\n    /// Returns the async generator object, if the function that this [`GeneratorContext`] is from an async generator, [`None`] otherwise.\n    pub(crate) fn async_generator_object(&self) -> JsResult<Option<JsObject>> {\n        let Some(frame) = self.call_frame.as_ref() else {\n            return Ok(None);\n        };\n\n        if !frame.code_block().is_async_generator() {\n            return Ok(None);\n        }\n\n        let val = self\n            .stack\n            .get_register(frame, CallFrame::ASYNC_GENERATOR_OBJECT_REGISTER_INDEX)\n            .js_expect(\"registers must have an async generator object\")?;\n\n        Ok(val.as_object())\n    }\n}\n\n/// The internal representation of a `Generator` object.\n#[derive(Debug, Finalize, Trace, JsData)]\npub struct Generator {\n    /// The `[[GeneratorState]]` internal slot.\n    pub(crate) state: GeneratorState,\n}\n\nimpl IntrinsicObject for Generator {\n    fn init(realm: &Realm) {\n        BuiltInBuilder::with_intrinsic::<Self>(realm)\n            .prototype(\n                realm\n                    .intrinsics()\n                    .objects()\n                    .iterator_prototypes()\n                    .iterator(),\n            )\n            .static_method(Self::next, js_string!(\"next\"), 1)\n            .static_method(Self::r#return, js_string!(\"return\"), 1)\n            .static_method(Self::throw, js_string!(\"throw\"), 1)\n            .static_property(\n                JsSymbol::to_string_tag(),\n                Self::NAME,\n                Attribute::CONFIGURABLE,\n            )\n            .static_property(\n                CONSTRUCTOR,\n                realm\n                    .intrinsics()\n                    .constructors()\n                    .generator_function()\n                    .prototype(),\n                Attribute::CONFIGURABLE,\n            )\n            .build();\n    }\n\n    fn get(intrinsics: &Intrinsics) -> JsObject {\n        intrinsics.objects().generator()\n    }\n}\n\nimpl Generator {\n    const NAME: JsString = StaticJsStrings::GENERATOR;\n\n    /// `Generator.prototype.next ( value )`\n    ///\n    /// The `next()` method returns an object with two properties done and value.\n    /// You can also provide a parameter to the next method to send a value to the generator.\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///  - [MDN documentation][mdn]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-generator.prototype.next\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Generator/next\n    pub(crate) fn next(\n        this: &JsValue,\n        args: &[JsValue],\n        context: &mut Context,\n    ) -> JsResult<JsValue> {\n        // 1. Return ? GeneratorResume(this value, value, empty).\n        Self::generator_resume(this, args.get_or_undefined(0).clone(), context)\n    }\n\n    /// `Generator.prototype.return ( value )`\n    ///\n    /// The `return()` method returns the given value and finishes the generator.\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///  - [MDN documentation][mdn]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-generator.prototype.return\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Generator/return\n    pub(crate) fn r#return(\n        this: &JsValue,\n        args: &[JsValue],\n        context: &mut Context,\n    ) -> JsResult<JsValue> {\n        // 1. Let g be the this value.\n        // 2. Let C be Completion { [[Type]]: return, [[Value]]: value, [[Target]]: empty }.\n        // 3. Return ? GeneratorResumeAbrupt(g, C, empty).\n        Self::generator_resume_abrupt(this, Ok(args.get_or_undefined(0).clone()), context)\n    }\n\n    /// `Generator.prototype.throw ( exception )`\n    ///\n    /// The `throw()` method resumes the execution of a generator by throwing an error into it\n    /// and returns an object with two properties done and value.\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///  - [MDN documentation][mdn]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-generator.prototype.throw\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Generator/throw\n    pub(crate) fn throw(\n        this: &JsValue,\n        args: &[JsValue],\n        context: &mut Context,\n    ) -> JsResult<JsValue> {\n        // 1. Let g be the this value.\n        // 2. Let C be ThrowCompletion(exception).\n        // 3. Return ? GeneratorResumeAbrupt(g, C, empty).\n        Self::generator_resume_abrupt(\n            this,\n            Err(JsError::from_opaque(args.get_or_undefined(0).clone())),\n            context,\n        )\n    }\n\n    /// `27.5.3.3 GeneratorResume ( generator, value, generatorBrand )`\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-generatorresume\n    pub(crate) fn generator_resume(\n        r#gen: &JsValue,\n        value: JsValue,\n        context: &mut Context,\n    ) -> JsResult<JsValue> {\n        // 1. Let state be ? GeneratorValidate(generator, generatorBrand).\n        let Some(generator_obj) = r#gen.as_object() else {\n            return Err(JsNativeError::typ()\n                .with_message(\"Generator method called on non generator\")\n                .into());\n        };\n        let mut r#gen = generator_obj.downcast_mut::<Self>().ok_or_else(|| {\n            JsNativeError::typ().with_message(\"generator resumed on non generator object\")\n        })?;\n\n        // 4. Let genContext be generator.[[GeneratorContext]].\n        // 5. Let methodContext be the running execution context.\n        // 6. Suspend methodContext.\n        // 7. Set generator.[[GeneratorState]] to executing.\n        let (mut generator_context, first_execution) =\n            match std::mem::replace(&mut r#gen.state, GeneratorState::Executing) {\n                GeneratorState::Executing => {\n                    return Err(JsNativeError::typ()\n                        .with_message(\"Generator should not be executing\")\n                        .into());\n                }\n                // 2. If state is completed, return CreateIterResultObject(undefined, true).\n                GeneratorState::Completed => {\n                    r#gen.state = GeneratorState::Completed;\n                    return Ok(create_iter_result_object(\n                        JsValue::undefined(),\n                        true,\n                        context,\n                    ));\n                }\n                // 3. Assert: state is either suspendedStart or suspendedYield.\n                GeneratorState::SuspendedStart { context } => (context, true),\n                GeneratorState::SuspendedYield { context } => (context, false),\n            };\n\n        drop(r#gen);\n\n        let record = generator_context.resume(\n            (!first_execution).then_some(value),\n            GeneratorResumeKind::Normal,\n            context,\n        );\n\n        let mut r#gen = generator_obj\n            .downcast_mut::<Self>()\n            .js_expect(\"already checked this object type\")?;\n\n        // 8. Push genContext onto the execution context stack; genContext is now the running execution context.\n        // 9. Resume the suspended evaluation of genContext using NormalCompletion(value) as the result of the operation that suspended it. Let result be the value returned by the resumed computation.\n        // 10. Assert: When we return here, genContext has already been removed from the execution context stack and methodContext is the currently running execution context.\n        // 11. Return Completion(result).\n        match record {\n            CompletionRecord::Normal(value) => {\n                r#gen.state = GeneratorState::SuspendedYield {\n                    context: generator_context,\n                };\n                Ok(value)\n            }\n            CompletionRecord::Return(value) => {\n                r#gen.state = GeneratorState::Completed;\n                Ok(create_iter_result_object(value, true, context))\n            }\n            CompletionRecord::Throw(err) => {\n                r#gen.state = GeneratorState::Completed;\n                Err(err)\n            }\n        }\n    }\n\n    /// `27.5.3.4 GeneratorResumeAbrupt ( generator, abruptCompletion, generatorBrand )`\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-generatorresumeabrupt\n    pub(crate) fn generator_resume_abrupt(\n        r#gen: &JsValue,\n        abrupt_completion: JsResult<JsValue>,\n        context: &mut Context,\n    ) -> JsResult<JsValue> {\n        // 1. Let state be ? GeneratorValidate(generator, generatorBrand).\n        let Some(generator_obj) = r#gen.as_object() else {\n            return Err(JsNativeError::typ()\n                .with_message(\"Generator method called on non generator\")\n                .into());\n        };\n        let mut r#gen = generator_obj.downcast_mut::<Self>().ok_or_else(|| {\n            JsNativeError::typ().with_message(\"generator resumed on non generator object\")\n        })?;\n\n        // 4. Assert: state is suspendedYield.\n        // 5. Let genContext be generator.[[GeneratorContext]].\n        // 6. Let methodContext be the running execution context.\n        // 7. Suspend methodContext.\n        // 8. Set generator.[[GeneratorState]] to executing.\n        let mut generator_context =\n            match std::mem::replace(&mut r#gen.state, GeneratorState::Executing) {\n                GeneratorState::Executing => {\n                    return Err(JsNativeError::typ()\n                        .with_message(\"Generator should not be executing\")\n                        .into());\n                }\n                // 2. If state is suspendedStart, then\n                // 3. If state is completed, then\n                GeneratorState::SuspendedStart { .. } | GeneratorState::Completed => {\n                    // a. Set generator.[[GeneratorState]] to completed.\n                    r#gen.state = GeneratorState::Completed;\n\n                    // b. Once a generator enters the completed state it never leaves it and its\n                    // associated execution context is never resumed. Any execution state associated\n                    // with generator can be discarded at this point.\n\n                    // a. If abruptCompletion.[[Type]] is return, then\n                    if let Ok(value) = abrupt_completion {\n                        // i. Return CreateIterResultObject(abruptCompletion.[[Value]], true).\n                        let value = create_iter_result_object(value, true, context);\n                        return Ok(value);\n                    }\n\n                    // b. Return Completion(abruptCompletion).\n                    return abrupt_completion;\n                }\n                GeneratorState::SuspendedYield { context } => context,\n            };\n\n        // 9. Push genContext onto the execution context stack; genContext is now the running execution context.\n        // 10. Resume the suspended evaluation of genContext using abruptCompletion as the result of the operation that suspended it. Let result be the completion record returned by the resumed computation.\n        // 11. Assert: When we return here, genContext has already been removed from the execution context stack and methodContext is the currently running execution context.\n        // 12. Return Completion(result).\n        drop(r#gen);\n\n        let (value, resume_kind) = match abrupt_completion {\n            Ok(value) => (value, GeneratorResumeKind::Return),\n            Err(err) => (err.into_opaque(context)?, GeneratorResumeKind::Throw),\n        };\n\n        let record = generator_context.resume(Some(value), resume_kind, context);\n\n        let mut r#gen = generator_obj.downcast_mut::<Self>().ok_or_else(|| {\n            JsNativeError::typ().with_message(\"generator resumed on non generator object\")\n        })?;\n\n        match record {\n            CompletionRecord::Normal(value) => {\n                r#gen.state = GeneratorState::SuspendedYield {\n                    context: generator_context,\n                };\n                Ok(value)\n            }\n            CompletionRecord::Return(value) => {\n                r#gen.state = GeneratorState::Completed;\n                Ok(create_iter_result_object(value, true, context))\n            }\n            CompletionRecord::Throw(err) => {\n                r#gen.state = GeneratorState::Completed;\n                Err(err)\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "core/engine/src/builtins/generator_function/mod.rs",
    "content": "//! Boa's implementation of ECMAScript's global `GeneratorFunction` object.\n//!\n//! The `GeneratorFunction` constructor creates a new generator function object.\n//! In ECMAScript, every generator function is actually a `GeneratorFunction` object.\n//!\n//! More information:\n//!  - [ECMAScript reference][spec]\n//!  - [MDN documentation][mdn]\n//!\n//! [spec]: https://tc39.es/ecma262/#sec-generatorfunction-objects\n//! [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/GeneratorFunction\n\nuse crate::{\n    Context, JsResult, JsString,\n    builtins::{BuiltInObject, function::BuiltInFunctionObject},\n    context::intrinsics::{Intrinsics, StandardConstructor, StandardConstructors},\n    object::PROTOTYPE,\n    property::Attribute,\n    realm::Realm,\n    string::StaticJsStrings,\n    symbol::JsSymbol,\n    value::JsValue,\n};\n\nuse super::{BuiltInBuilder, BuiltInConstructor, IntrinsicObject};\n\n/// The internal representation of a `Generator` object.\n#[derive(Debug, Clone, Copy)]\npub struct GeneratorFunction;\n\nimpl IntrinsicObject for GeneratorFunction {\n    fn init(realm: &Realm) {\n        BuiltInBuilder::from_standard_constructor::<Self>(realm)\n            .inherits(Some(\n                realm.intrinsics().constructors().function().prototype(),\n            ))\n            .constructor_attributes(Attribute::CONFIGURABLE)\n            .property(\n                PROTOTYPE,\n                realm.intrinsics().objects().generator(),\n                Attribute::CONFIGURABLE,\n            )\n            .property(\n                JsSymbol::to_string_tag(),\n                Self::NAME,\n                Attribute::CONFIGURABLE,\n            )\n            .build();\n    }\n\n    fn get(intrinsics: &Intrinsics) -> crate::object::JsObject {\n        Self::STANDARD_CONSTRUCTOR(intrinsics.constructors()).constructor()\n    }\n}\n\nimpl BuiltInObject for GeneratorFunction {\n    const NAME: JsString = StaticJsStrings::GENERATOR_FUNCTION;\n}\n\nimpl BuiltInConstructor for GeneratorFunction {\n    const CONSTRUCTOR_ARGUMENTS: usize = 1;\n    const PROTOTYPE_STORAGE_SLOTS: usize = 2;\n    const CONSTRUCTOR_STORAGE_SLOTS: usize = 0;\n\n    const STANDARD_CONSTRUCTOR: fn(&StandardConstructors) -> &StandardConstructor =\n        StandardConstructors::generator_function;\n\n    /// `GeneratorFunction ( p1, p2, … , pn, body )`\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-generatorfunction\n    fn constructor(\n        new_target: &JsValue,\n        args: &[JsValue],\n        context: &mut Context,\n    ) -> JsResult<JsValue> {\n        let active_function = context.active_function_object().unwrap_or_else(|| {\n            context\n                .intrinsics()\n                .constructors()\n                .generator_function()\n                .constructor()\n        });\n        BuiltInFunctionObject::create_dynamic_function(\n            active_function,\n            new_target,\n            args,\n            false,\n            true,\n            context,\n        )\n        .map(Into::into)\n    }\n}\n"
  },
  {
    "path": "core/engine/src/builtins/intl/collator/mod.rs",
    "content": "use boa_gc::{Finalize, Trace, custom_trace};\nuse icu_collator::{\n    CollatorPreferences,\n    options::{AlternateHandling, MaxVariable},\n    preferences::{CollationCaseFirst, CollationNumericOrdering, CollationType},\n    provider::CollationMetadataV1,\n};\n\nuse icu_locale::{Locale, extensions::unicode};\n\nuse crate::{\n    Context, JsArgs, JsData, JsExpect, JsNativeError, JsResult, JsString, JsValue,\n    builtins::{\n        BuiltInBuilder, BuiltInConstructor, BuiltInObject, IntrinsicObject, OrdinaryObject,\n        options::get_option,\n    },\n    context::intrinsics::{Intrinsics, StandardConstructor, StandardConstructors},\n    js_string,\n    native_function::NativeFunction,\n    object::{\n        FunctionObjectBuilder, JsFunction, JsObject,\n        internal_methods::get_prototype_from_constructor,\n    },\n    property::Attribute,\n    realm::Realm,\n    string::StaticJsStrings,\n    symbol::JsSymbol,\n};\n\nuse super::{\n    Service,\n    locale::{canonicalize_locale_list, filter_locales, resolve_locale},\n    options::{IntlOptions, coerce_options_to_object},\n};\n\nmod options;\npub(crate) use options::*;\n\n#[derive(Debug, Finalize, JsData)]\n#[allow(clippy::struct_field_names)]\npub(crate) struct Collator {\n    locale: Locale,\n    collation: Option<CollationType>,\n    numeric: bool,\n    case_first: Option<CollationCaseFirst>,\n    usage: Usage,\n    sensitivity: Sensitivity,\n    ignore_punctuation: bool,\n    collator: icu_collator::Collator,\n    bound_compare: Option<JsFunction>,\n}\n\n// SAFETY: only `bound_compare` is a traceable object.\nunsafe impl Trace for Collator {\n    custom_trace!(this, mark, mark(&this.bound_compare));\n}\n\nimpl Collator {\n    /// Gets the inner [`icu_collator::Collator`] comparator.\n    pub(crate) const fn collator(&self) -> &icu_collator::Collator {\n        &self.collator\n    }\n}\n\nimpl Service for Collator {\n    type LangMarker = CollationMetadataV1;\n\n    type Preferences = CollatorPreferences;\n}\n\nimpl IntrinsicObject for Collator {\n    fn init(realm: &Realm) {\n        let compare = BuiltInBuilder::callable(realm, Self::compare)\n            .name(js_string!(\"get compare\"))\n            .build();\n\n        BuiltInBuilder::from_standard_constructor::<Self>(realm)\n            .static_method(\n                Self::supported_locales_of,\n                js_string!(\"supportedLocalesOf\"),\n                1,\n            )\n            .property(\n                JsSymbol::to_string_tag(),\n                js_string!(\"Intl.Collator\"),\n                Attribute::CONFIGURABLE,\n            )\n            .accessor(\n                js_string!(\"compare\"),\n                Some(compare),\n                None,\n                Attribute::CONFIGURABLE,\n            )\n            .method(Self::resolved_options, js_string!(\"resolvedOptions\"), 0)\n            .build();\n    }\n\n    fn get(intrinsics: &Intrinsics) -> JsObject {\n        Self::STANDARD_CONSTRUCTOR(intrinsics.constructors()).constructor()\n    }\n}\n\nimpl BuiltInObject for Collator {\n    const NAME: JsString = StaticJsStrings::COLLATOR;\n}\n\nimpl BuiltInConstructor for Collator {\n    const CONSTRUCTOR_ARGUMENTS: usize = 0;\n    const PROTOTYPE_STORAGE_SLOTS: usize = 4;\n    const CONSTRUCTOR_STORAGE_SLOTS: usize = 1;\n\n    const STANDARD_CONSTRUCTOR: fn(&StandardConstructors) -> &StandardConstructor =\n        StandardConstructors::collator;\n\n    /// Constructor [`Intl.Collator ( [ locales [ , options ] ] )`][spec].\n    ///\n    /// Constructor for `Collator` objects.\n    ///\n    /// More information:\n    ///  - [MDN documentation][mdn]\n    ///\n    /// [spec]: https://tc39.es/ecma402/#sec-intl.collator\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/Collator\n    fn constructor(\n        new_target: &JsValue,\n        args: &[JsValue],\n        context: &mut Context,\n    ) -> JsResult<JsValue> {\n        // 1. If NewTarget is undefined, let newTarget be the active function object, else let newTarget be NewTarget.\n        let new_target = &if new_target.is_undefined() {\n            context\n                .active_function_object()\n                .unwrap_or_else(|| context.intrinsics().constructors().collator().constructor())\n                .into()\n        } else {\n            new_target.clone()\n        };\n        // 2. Let internalSlotsList be « [[InitializedCollator]], [[Locale]], [[Usage]], [[Sensitivity]], [[IgnorePunctuation]], [[Collation]], [[BoundCompare]] ».\n        // 3. If %Collator%.[[RelevantExtensionKeys]] contains \"kn\", then\n        //     a. Append [[Numeric]] as the last element of internalSlotsList.\n        // 4. If %Collator%.[[RelevantExtensionKeys]] contains \"kf\", then\n        //     a. Append [[CaseFirst]] as the last element of internalSlotsList.\n        // 5. Let collator be ? OrdinaryCreateFromConstructor(newTarget, \"%Collator.prototype%\", internalSlotsList).\n        // 6. Return ? InitializeCollator(collator, locales, options).\n\n        let locales = args.get_or_undefined(0);\n        let options = args.get_or_undefined(1);\n\n        // Abstract operation `InitializeCollator ( collator, locales, options )`\n        // https://tc39.es/ecma402/#sec-initializecollator\n\n        // 1. Let requestedLocales be ? CanonicalizeLocaleList(locales).\n        let requested_locales = canonicalize_locale_list(locales, context)?;\n\n        // 2. Set options to ? CoerceOptionsToObject(options).\n        let options = coerce_options_to_object(options, context)?;\n\n        // 3. Let usage be ? GetOption(options, \"usage\", string, « \"sort\", \"search\" », \"sort\").\n        // 4. Set collator.[[Usage]] to usage.\n        // 5. If usage is \"sort\", then\n        //     a. Let localeData be %Collator%.[[SortLocaleData]].\n        // 6. Else,\n        //     a. Let localeData be %Collator%.[[SearchLocaleData]].\n        let usage = get_option(&options, js_string!(\"usage\"), context)?.unwrap_or_default();\n\n        // 7. Let opt be a new Record.\n        // 8. Let matcher be ? GetOption(options, \"localeMatcher\", string, « \"lookup\", \"best fit\" », \"best fit\").\n        // 9. Set opt.[[localeMatcher]] to matcher.\n        let matcher =\n            get_option(&options, js_string!(\"localeMatcher\"), context)?.unwrap_or_default();\n\n        // 10. Let collation be ? GetOption(options, \"collation\", string, empty, undefined).\n        // 11. If collation is not undefined, then\n        //     a. If collation does not match the Unicode Locale Identifier type nonterminal, throw a RangeError exception.\n        // 12. Set opt.[[co]] to collation.\n        // unicode `type`s that are not valid collation types are considered\n        // \"unsupported\" instead of invalid.\n        let collation = get_option::<unicode::Value>(&options, js_string!(\"collation\"), context)?\n            .and_then(|val| CollationType::try_from(&val).ok());\n\n        // 13. Let numeric be ? GetOption(options, \"numeric\", boolean, empty, undefined).\n        // 14. If numeric is not undefined, then\n        //     a. Let numeric be ! ToString(numeric).\n        // 15. Set opt.[[kn]] to numeric.\n        let numeric = get_option(&options, js_string!(\"numeric\"), context)?;\n\n        // 16. Let caseFirst be ? GetOption(options, \"caseFirst\", string, « \"upper\", \"lower\", \"false\" », undefined).\n        // 17. Set opt.[[kf]] to caseFirst.\n        let case_first = get_option(&options, js_string!(\"caseFirst\"), context)?;\n\n        let mut intl_options = IntlOptions {\n            matcher,\n            preferences: {\n                let mut prefs = CollatorPreferences::default();\n                prefs.collation_type = collation;\n                prefs.numeric_ordering = numeric.map(|kn| {\n                    if kn {\n                        CollationNumericOrdering::True\n                    } else {\n                        CollationNumericOrdering::False\n                    }\n                });\n                prefs.case_first = case_first;\n                prefs\n            },\n        };\n\n        // 18. Let relevantExtensionKeys be %Collator%.[[RelevantExtensionKeys]].\n        // 19. Let r be ResolveLocale(%Collator%.[[AvailableLocales]], requestedLocales, opt, relevantExtensionKeys, localeData).\n        let locale = resolve_locale::<Self>(\n            requested_locales,\n            &mut intl_options,\n            context.intl_provider(),\n        )?;\n\n        // 20. Set collator.[[Locale]] to r.[[locale]].\n        // 21. Let collation be r.[[co]].\n        // 22. If collation is null, let collation be \"default\".\n        // 23. Set collator.[[Collation]] to collation.\n        let collation = intl_options.preferences.collation_type;\n\n        // 24. If relevantExtensionKeys contains \"kn\", then\n        //     a. Set collator.[[Numeric]] to SameValue(r.[[kn]], \"true\").\n        let numeric =\n            intl_options.preferences.numeric_ordering == Some(CollationNumericOrdering::True);\n\n        // 25. If relevantExtensionKeys contains \"kf\", then\n        //     a. Set collator.[[CaseFirst]] to r.[[kf]].\n        let case_first = intl_options.preferences.case_first;\n\n        // 26. Let sensitivity be ? GetOption(options, \"sensitivity\", string, « \"base\", \"accent\", \"case\", \"variant\" », undefined).\n        // 28. Set collator.[[Sensitivity]] to sensitivity.\n        let sensitivity = get_option(&options, js_string!(\"sensitivity\"), context)?\n            // 27. If sensitivity is undefined, then\n            //     a. If usage is \"sort\", then\n            //         i. Let sensitivity be \"variant\".\n            //     b. Else,\n            //         i. Let dataLocale be r.[[dataLocale]].\n            //         ii. Let dataLocaleData be localeData.[[<dataLocale>]].\n            //         iii. Let sensitivity be dataLocaleData.[[sensitivity]].\n            .or_else(|| (usage == Usage::Sort).then_some(Sensitivity::Variant));\n\n        // 29. Let ignorePunctuation be ? GetOption(options, \"ignorePunctuation\", boolean, empty, false).\n        // 30. Set collator.[[IgnorePunctuation]] to ignorePunctuation.\n        let ignore_punctuation = get_option(&options, js_string!(\"ignorePunctuation\"), context)?;\n\n        let (strength, case_level) = sensitivity.map(Sensitivity::to_collator_options).unzip();\n\n        let (alternate_handling, max_variable) = match ignore_punctuation {\n            Some(true) => Some((AlternateHandling::Shifted, MaxVariable::Punctuation)).unzip(),\n            Some(false) => (Some(AlternateHandling::NonIgnorable), None),\n            None => None.unzip(),\n        };\n\n        let mut options = icu_collator::options::CollatorOptions::default();\n        options.strength = strength;\n        options.case_level = case_level;\n        options.alternate_handling = alternate_handling;\n        options.max_variable = max_variable;\n\n        if usage == Usage::Search {\n            intl_options.preferences.collation_type = Some(CollationType::Search);\n        }\n\n        let collator = icu_collator::Collator::try_new_with_buffer_provider(\n            context.intl_provider().erased_provider(),\n            intl_options.preferences,\n            options,\n        )\n        .map_err(|e| JsNativeError::typ().with_message(e.to_string()))?;\n\n        let resolved_options = collator.as_borrowed().resolved_options();\n\n        let prototype =\n            get_prototype_from_constructor(new_target, StandardConstructors::collator, context)?;\n        let collator = JsObject::from_proto_and_data_with_shared_shape(\n            context.root_shape(),\n            prototype,\n            Self {\n                locale,\n                collation,\n                numeric,\n                case_first,\n                usage,\n                sensitivity: sensitivity.unwrap_or(Sensitivity::Variant),\n                ignore_punctuation: resolved_options.alternate_handling\n                    == AlternateHandling::Shifted\n                    && resolved_options.max_variable != MaxVariable::Space,\n                collator,\n                bound_compare: None,\n            },\n        );\n\n        // 31. Return collator.\n        Ok(collator.into())\n    }\n}\n\nimpl Collator {\n    /// [`Intl.Collator.supportedLocalesOf ( locales [ , options ] )`][spec].\n    ///\n    /// Returns an array containing those of the provided locales that are supported in collation\n    /// without having to fall back to the runtime's default locale.\n    ///\n    /// More information:\n    ///  - [MDN documentation][mdn]\n    ///\n    /// [spec]: https://tc39.es/ecma402/#sec-intl.collator.supportedlocalesof\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/Collator/supportedLocalesOf\n    fn supported_locales_of(\n        _: &JsValue,\n        args: &[JsValue],\n        context: &mut Context,\n    ) -> JsResult<JsValue> {\n        let locales = args.get_or_undefined(0);\n        let options = args.get_or_undefined(1);\n\n        // 1. Let availableLocales be %Collator%.[[AvailableLocales]].\n        // 2. Let requestedLocales be ? CanonicalizeLocaleList(locales).\n        let requested_locales = canonicalize_locale_list(locales, context)?;\n\n        // 3. Return ? FilterLocales(availableLocales, requestedLocales, options).\n        filter_locales::<Self>(requested_locales, options, context).map(JsValue::from)\n    }\n\n    /// [`get Intl.Collator.prototype.compare`][spec].\n    ///\n    /// Compares two strings according to the sort order of this Intl.Collator object.\n    ///\n    /// More information:\n    ///  - [MDN documentation][mdn]\n    ///\n    /// [spec]: https://tc39.es/ecma402/#sec-intl.collator.prototype.compare\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/Collator/compare\n    fn compare(this: &JsValue, _: &[JsValue], context: &mut Context) -> JsResult<JsValue> {\n        // 1. Let collator be the this value.\n        // 2. Perform ? RequireInternalSlot(collator, [[InitializedCollator]]).\n        let this = this.as_object().ok_or_else(|| {\n            JsNativeError::typ()\n                .with_message(\"`resolvedOptions` can only be called on a `Collator` object\")\n        })?;\n        let collator_obj = this.clone();\n        let mut collator = this.downcast_mut::<Self>().ok_or_else(|| {\n            JsNativeError::typ()\n                .with_message(\"`resolvedOptions` can only be called on a `Collator` object\")\n        })?;\n\n        // 3. If collator.[[BoundCompare]] is undefined, then\n        //     a. Let F be a new built-in function object as defined in 10.3.3.1.\n        //     b. Set F.[[Collator]] to collator.\n        //     c. Set collator.[[BoundCompare]] to F.\n        let bound_compare = if let Some(f) = collator.bound_compare.clone() {\n            f\n        } else {\n            let bound_compare = FunctionObjectBuilder::new(\n                context.realm(),\n                // 10.3.3.1. Collator Compare Functions\n                // https://tc39.es/ecma402/#sec-collator-compare-functions\n                NativeFunction::from_copy_closure_with_captures(\n                    |_, args, collator, context| {\n                        // 1. Let collator be F.[[Collator]].\n                        // 2. Assert: Type(collator) is Object and collator has an [[InitializedCollator]] internal slot.\n                        let collator = collator\n                            .downcast_ref::<Self>()\n                            .js_expect(\"checked above that the object was a collator object\")?;\n\n                        // 3. If x is not provided, let x be undefined.\n                        // 5. Let X be ? ToString(x).\n                        let x = args\n                            .get_or_undefined(0)\n                            .to_string(context)?\n                            .iter()\n                            .collect::<Vec<_>>();\n\n                        // 4. If y is not provided, let y be undefined.\n                        // 6. Let Y be ? ToString(y).\n                        let y = args\n                            .get_or_undefined(1)\n                            .to_string(context)?\n                            .iter()\n                            .collect::<Vec<_>>();\n\n                        // 7. Return CompareStrings(collator, X, Y).\n\n                        let result = collator.collator.as_borrowed().compare_utf16(&x, &y) as i32;\n\n                        Ok(result.into())\n                    },\n                    collator_obj,\n                ),\n            )\n            .length(2)\n            .build();\n\n            collator.bound_compare = Some(bound_compare.clone());\n            bound_compare\n        };\n\n        // 4. Return collator.[[BoundCompare]].\n        Ok(bound_compare.into())\n    }\n\n    /// [`Intl.Collator.prototype.resolvedOptions ( )`][spec].\n    ///\n    /// Returns a new object with properties reflecting the locale and collation options computed\n    /// during initialization of this `Intl.Collator` object.\n    ///\n    /// More information:\n    ///  - [MDN documentation][mdn]\n    ///\n    /// [spec]: https://tc39.es/ecma402/#sec-intl.collator.prototype.resolvedoptions\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/Collator/resolvedOptions\n    fn resolved_options(this: &JsValue, _: &[JsValue], context: &mut Context) -> JsResult<JsValue> {\n        // 1. Let collator be the this value.\n        // 2. Perform ? RequireInternalSlot(collator, [[InitializedCollator]]).\n        let object = this.as_object();\n        let collator = object\n            .as_ref()\n            .and_then(JsObject::downcast_ref::<Self>)\n            .ok_or_else(|| {\n                JsNativeError::typ()\n                    .with_message(\"`resolvedOptions` can only be called on a `Collator` object\")\n            })?;\n\n        // 3. Let options be OrdinaryObjectCreate(%Object.prototype%).\n        let options = context\n            .intrinsics()\n            .templates()\n            .ordinary_object()\n            .create(OrdinaryObject, vec![]);\n\n        // 4. For each row of Table 4, except the header row, in table order, do\n        //     a. Let p be the Property value of the current row.\n        //     b. Let v be the value of collator's internal slot whose name is the Internal Slot value of the current row.\n        //     c. If the current row has an Extension Key value, then\n        //         i. Let extensionKey be the Extension Key value of the current row.\n        //         ii. If %Collator%.[[RelevantExtensionKeys]] does not contain extensionKey, then\n        //             1. Let v be undefined.\n        //     d. If v is not undefined, then\n        //         i. Perform ! CreateDataPropertyOrThrow(options, p, v).\n        // 5. Return options.\n        options\n            .create_data_property_or_throw(\n                js_string!(\"locale\"),\n                js_string!(collator.locale.to_string()),\n                context,\n            )\n            .js_expect(\"operation must not fail per the spec\")?;\n        options\n            .create_data_property_or_throw(\n                js_string!(\"usage\"),\n                match collator.usage {\n                    Usage::Search => js_string!(\"search\"),\n                    Usage::Sort => js_string!(\"sort\"),\n                },\n                context,\n            )\n            .js_expect(\"operation must not fail per the spec\")?;\n        options\n            .create_data_property_or_throw(\n                js_string!(\"sensitivity\"),\n                match collator.sensitivity {\n                    Sensitivity::Base => js_string!(\"base\"),\n                    Sensitivity::Accent => js_string!(\"accent\"),\n                    Sensitivity::Case => js_string!(\"case\"),\n                    Sensitivity::Variant => js_string!(\"variant\"),\n                },\n                context,\n            )\n            .js_expect(\"operation must not fail per the spec\")?;\n        options\n            .create_data_property_or_throw(\n                js_string!(\"ignorePunctuation\"),\n                collator.ignore_punctuation,\n                context,\n            )\n            .js_expect(\"operation must not fail per the spec\")?;\n        options\n            .create_data_property_or_throw(\n                js_string!(\"collation\"),\n                collator\n                    .collation\n                    .map(|co| js_string!(co.as_str()))\n                    .unwrap_or(js_string!(\"default\")),\n                context,\n            )\n            .js_expect(\"operation must not fail per the spec\")?;\n        options\n            .create_data_property_or_throw(js_string!(\"numeric\"), collator.numeric, context)\n            .js_expect(\"operation must not fail per the spec\")?;\n        if let Some(kf) = collator.case_first {\n            options\n                .create_data_property_or_throw(\n                    js_string!(\"caseFirst\"),\n                    js_string!(kf.as_str()),\n                    context,\n                )\n                .js_expect(\"operation must not fail per the spec\")?;\n        }\n\n        // 5. Return options.\n        Ok(options.into())\n    }\n}\n"
  },
  {
    "path": "core/engine/src/builtins/intl/collator/options.rs",
    "content": "use std::str::FromStr;\n\nuse icu_collator::{\n    CollatorPreferences,\n    options::{CaseLevel, Strength},\n    preferences::{CollationCaseFirst, CollationType},\n    provider::CollationMetadataV1,\n};\nuse icu_locale::LanguageIdentifier;\nuse icu_provider::{\n    DataMarkerAttributes,\n    prelude::icu_locale_core::{extensions::unicode, preferences::LocalePreferences},\n};\n\nuse crate::{\n    Context, JsNativeError, JsResult, JsValue,\n    builtins::{\n        intl::{ServicePreferences, locale::validate_extension},\n        options::{OptionType, ParsableOptionType},\n    },\n    context::icu::IntlProvider,\n};\n\n#[derive(Debug, Clone, Copy)]\npub(crate) enum Sensitivity {\n    Base,\n    Accent,\n    Case,\n    Variant,\n}\n\nimpl Sensitivity {\n    /// Converts the sensitivity option to the equivalent ICU4X collator options.\n    pub(crate) const fn to_collator_options(self) -> (Strength, CaseLevel) {\n        match self {\n            Self::Base => (Strength::Primary, CaseLevel::Off),\n            Self::Accent => (Strength::Secondary, CaseLevel::Off),\n            Self::Case => (Strength::Primary, CaseLevel::On),\n            Self::Variant => (Strength::Tertiary, CaseLevel::On),\n        }\n    }\n}\n\n#[derive(Debug)]\npub(crate) struct ParseSensitivityError;\n\nimpl std::fmt::Display for ParseSensitivityError {\n    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {\n        f.write_str(\"provided string was not `base`, `accent`, `case` or `variant`\")\n    }\n}\n\nimpl FromStr for Sensitivity {\n    type Err = ParseSensitivityError;\n\n    fn from_str(s: &str) -> Result<Self, Self::Err> {\n        match s {\n            \"base\" => Ok(Self::Base),\n            \"accent\" => Ok(Self::Accent),\n            \"case\" => Ok(Self::Case),\n            \"variant\" => Ok(Self::Variant),\n            _ => Err(ParseSensitivityError),\n        }\n    }\n}\n\nimpl ParsableOptionType for Sensitivity {}\n\n#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]\npub(crate) enum Usage {\n    #[default]\n    Sort,\n    Search,\n}\n\n#[derive(Debug)]\npub(crate) struct ParseUsageError;\n\nimpl std::fmt::Display for ParseUsageError {\n    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {\n        f.write_str(\"provided string was not `sort` or `search`\")\n    }\n}\n\nimpl FromStr for Usage {\n    type Err = ParseUsageError;\n\n    fn from_str(s: &str) -> Result<Self, Self::Err> {\n        match s {\n            \"sort\" => Ok(Self::Sort),\n            \"search\" => Ok(Self::Search),\n            _ => Err(ParseUsageError),\n        }\n    }\n}\n\nimpl ParsableOptionType for Usage {}\n\nimpl OptionType for CollationCaseFirst {\n    fn from_value(value: JsValue, context: &mut Context) -> JsResult<Self> {\n        match value.to_string(context)?.to_std_string_escaped().as_str() {\n            \"upper\" => Ok(Self::Upper),\n            \"lower\" => Ok(Self::Lower),\n            \"false\" => Ok(Self::False),\n            _ => Err(JsNativeError::range()\n                .with_message(\"provided string was not `upper`, `lower` or `false`\")\n                .into()),\n        }\n    }\n}\n\nimpl ServicePreferences for CollatorPreferences {\n    fn validate(&mut self, id: &LanguageIdentifier, provider: &IntlProvider) {\n        self.collation_type = self.collation_type.take().filter(|co| {\n            let attr = DataMarkerAttributes::from_str_or_panic(co.as_str());\n            co != &CollationType::Search\n                && validate_extension::<CollationMetadataV1>(id, attr, provider)\n        });\n    }\n\n    impl_service_preferences!(collation_type, numeric_ordering, case_first);\n}\n"
  },
  {
    "path": "core/engine/src/builtins/intl/date_time_format/mod.rs",
    "content": "//! This module implements the global `Intl.DateTimeFormat` object.\n//!\n//! `Intl.DateTimeFormat` is a built-in object that has properties and methods for date and time i18n.\n//!\n//! More information:\n//!  - [ECMAScript reference][spec]\n//!\n//! [spec]: https://tc39.es/ecma402/#datetimeformat-objects\n\nuse crate::{\n    Context, JsArgs, JsData, JsExpect, JsResult, JsString, JsValue, NativeFunction,\n    builtins::{\n        BuiltInBuilder, BuiltInConstructor, BuiltInObject, IntrinsicObject,\n        date::utils::{\n            date_from_time, hour_from_time, min_from_time, month_from_time, ms_from_time,\n            sec_from_time, time_clip, year_from_time,\n        },\n        intl::{\n            Service,\n            date_time_format::options::{DateStyle, FormatMatcher, FormatOptions, TimeStyle},\n            locale::{canonicalize_locale_list, filter_locales, resolve_locale},\n            options::{IntlOptions, coerce_options_to_object},\n        },\n        options::get_option,\n    },\n    context::intrinsics::{Intrinsics, StandardConstructor, StandardConstructors},\n    error::JsNativeError,\n    js_error, js_string,\n    object::{\n        FunctionObjectBuilder, JsFunction, JsObject, ObjectInitializer,\n        internal_methods::get_prototype_from_constructor,\n    },\n    property::{Attribute, PropertyDescriptor},\n    realm::Realm,\n    string::StaticJsStrings,\n};\n\nuse boa_gc::{Finalize, Trace};\nuse icu_calendar::{Iso, preferences::CalendarAlgorithm};\nuse icu_datetime::{\n    DateTimeFormatter, DateTimeFormatterPreferences,\n    fieldsets::{\n        builder::{DateFields, FieldSetBuilder},\n        enums::CompositeFieldSet,\n    },\n    input::{Date, DateTime, Time, TimeZone, UtcOffset},\n    options::{Length, TimePrecision},\n    preferences::HourCycle as IcuHourCycle,\n};\nuse icu_decimal::preferences::NumberingSystem;\nuse icu_decimal::provider::DecimalSymbolsV1;\nuse icu_locale::{Locale, extensions::unicode::Value};\nuse icu_time::{\n    TimeZoneInfo, ZonedDateTime,\n    zone::{IanaParser, models::Base},\n};\nuse timezone_provider::provider::TimeZoneId;\n\nmod options;\n\n#[cfg(all(test, feature = \"intl_bundled\"))]\nmod tests;\n\n#[derive(Debug, Clone)]\npub(crate) enum FormatTimeZone {\n    UtcOffset(UtcOffset),\n    Identifier((TimeZone, TimeZoneId)),\n}\n\nimpl FormatTimeZone {\n    pub(crate) fn to_time_zone_info(&self) -> TimeZoneInfo<Base> {\n        match self {\n            Self::Identifier((tz, _)) => tz.without_offset(),\n            Self::UtcOffset(utc_offset) => TimeZone::UNKNOWN.with_offset(Some(*utc_offset)),\n        }\n    }\n}\n\n/// JavaScript `Intl.DateTimeFormat` object.\n#[derive(Debug, Clone, Trace, Finalize, JsData)]\n#[boa_gc(unsafe_empty_trace)] // Safety: No traceable types\n#[allow(dead_code)]\npub(crate) struct DateTimeFormat {\n    locale: Locale,\n    calendar_algorithm: Option<CalendarAlgorithm>, // TODO: Potentially remove ?\n    numbering_system: Option<NumberingSystem>,\n    hour_cycle: Option<IcuHourCycle>,\n    date_style: Option<DateStyle>,\n    time_style: Option<TimeStyle>,\n    time_zone: FormatTimeZone,\n    fieldset: CompositeFieldSet,\n    formatter: DateTimeFormatter<CompositeFieldSet>,\n    bound_format: Option<JsFunction>,\n    resolved_options: Option<JsObject>,\n}\n\nimpl Service for DateTimeFormat {\n    type LangMarker = DecimalSymbolsV1;\n\n    type Preferences = DateTimeFormatterPreferences;\n}\n\nimpl IntrinsicObject for DateTimeFormat {\n    fn init(realm: &Realm) {\n        use crate::JsSymbol;\n        let get_format = BuiltInBuilder::callable(realm, Self::get_format)\n            .name(js_string!(\"get format\"))\n            .build();\n\n        BuiltInBuilder::from_standard_constructor::<Self>(realm)\n            .static_method(\n                Self::supported_locales_of,\n                js_string!(\"supportedLocalesOf\"),\n                1,\n            )\n            .accessor(\n                js_string!(\"format\"),\n                Some(get_format),\n                None,\n                Attribute::CONFIGURABLE,\n            )\n            .method(Self::resolved_options, js_string!(\"resolvedOptions\"), 0)\n            .property(\n                JsSymbol::to_string_tag(),\n                js_string!(\"Intl.DateTimeFormat\"),\n                Attribute::READONLY | Attribute::NON_ENUMERABLE | Attribute::CONFIGURABLE,\n            )\n            .build();\n    }\n\n    fn get(intrinsics: &Intrinsics) -> JsObject {\n        Self::STANDARD_CONSTRUCTOR(intrinsics.constructors()).constructor()\n    }\n}\n\nimpl BuiltInObject for DateTimeFormat {\n    const NAME: JsString = StaticJsStrings::DATE_TIME_FORMAT;\n}\n\nimpl BuiltInConstructor for DateTimeFormat {\n    const CONSTRUCTOR_ARGUMENTS: usize = 0;\n    const PROTOTYPE_STORAGE_SLOTS: usize = 4;\n    const CONSTRUCTOR_STORAGE_SLOTS: usize = 1;\n\n    const STANDARD_CONSTRUCTOR: fn(&StandardConstructors) -> &StandardConstructor =\n        StandardConstructors::date_time_format;\n    /// The `Intl.DateTimeFormat` constructor is the `%DateTimeFormat%` intrinsic object and a standard built-in property of the `Intl` object.\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///  - [MDN documentation][mdn]\n    ///\n    /// [spec]: https://tc39.es/ecma402/#datetimeformat-objects\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/DateTimeFormat\n    fn constructor(\n        new_target: &JsValue,\n        args: &[JsValue],\n        context: &mut Context,\n    ) -> JsResult<JsValue> {\n        // NOTE (nekevss): separate calls to `CreateDateTimeFormat` to avoid clone.\n        // 1. If NewTarget is undefined, let newTarget be the active function object, else let newTarget be NewTarget.\n        let new_target_inner = &if new_target.is_undefined() {\n            context\n                .active_function_object()\n                .unwrap_or_else(|| {\n                    context\n                        .intrinsics()\n                        .constructors()\n                        .date_time_format()\n                        .constructor()\n                })\n                .into()\n        } else {\n            new_target.clone()\n        };\n        let locales = args.get_or_undefined(0);\n        let options = args.get_or_undefined(1);\n\n        // 2. Let dateTimeFormat be ? CreateDateTimeFormat(newTarget, locales, options, any, date).\n        let dtf = create_date_time_format(\n            locales,\n            options,\n            FormatType::Any,\n            FormatDefaults::Date,\n            context,\n        )?;\n        let prototype = get_prototype_from_constructor(\n            new_target_inner,\n            StandardConstructors::date_time_format,\n            context,\n        )?;\n        let date_time_format = JsObject::from_proto_and_data(prototype, dtf);\n\n        // 3. If the implementation supports the normative optional constructor mode of 4.3 Note 1, then\n        //     a. Let this be the this value.\n        //     b. Return ? ChainDateTimeFormat(dateTimeFormat, NewTarget, this).\n        // ChainDateTimeFormat ( dateTimeFormat, newTarget, this )\n        // <https://tc39.es/ecma402/#sec-chaindatetimeformat>\n\n        let this = context.vm.stack.get_this(context.vm.frame());\n        let Some(this_obj) = this.as_object() else {\n            return Ok(date_time_format.into());\n        };\n\n        let constructor = context\n            .intrinsics()\n            .constructors()\n            .date_time_format()\n            .constructor();\n\n        // 1. If newTarget is undefined and ? OrdinaryHasInstance(%Intl.DateTimeFormat%, this) is true, then\n        if new_target.is_undefined()\n            && JsValue::ordinary_has_instance(&constructor.into(), &this, context)?\n        {\n            let fallback_symbol = context\n                .intrinsics()\n                .objects()\n                .intl()\n                .borrow()\n                .data()\n                .fallback_symbol();\n\n            // a. Perform ? DefinePropertyOrThrow(this, %Intl%.[[FallbackSymbol]],\n            //    PropertyDescriptor{ [[Value]]: dateTimeFormat, [[Writable]]: false,\n            //    [[Enumerable]]: false, [[Configurable]]: false }).\n            this_obj.define_property_or_throw(\n                fallback_symbol,\n                PropertyDescriptor::builder()\n                    .value(date_time_format)\n                    .writable(false)\n                    .enumerable(false)\n                    .configurable(false),\n                context,\n            )?;\n            // b. Return this.\n            Ok(this)\n        } else {\n            // 4. Return dateTimeFormat.\n            Ok(date_time_format.into())\n        }\n    }\n}\n\nimpl DateTimeFormat {\n    fn get_format(this: &JsValue, _: &[JsValue], context: &mut Context) -> JsResult<JsValue> {\n        // 1. Let dtf be the this value.\n        // 2. If the implementation supports the normative optional constructor mode of 4.3 Note 1, then\n        //     a. Set dtf to ? UnwrapDateTimeFormat(dtf).\n        // 3. Perform ? RequireInternalSlot(dtf, [[InitializedDateTimeFormat]]).\n        let dtf_object = unwrap_date_time_format(this, context)?;\n        let dtf_clone = dtf_object.clone();\n        let mut dtf = dtf_object.borrow_mut();\n\n        // 4. If dtf.[[BoundFormat]] is undefined, then\n        // 5. Return dtf.[[BoundFormat]].\n        if let Some(bound_format) = &dtf.data_mut().bound_format.clone() {\n            Ok(bound_format.clone().into())\n        } else {\n            // a. Let F be a new built-in function object as defined in DateTime Format Functions (11.5.4).\n            let bound_format = FunctionObjectBuilder::new(\n                context.realm(),\n                NativeFunction::from_copy_closure_with_captures(\n                    |_, args, dtf, context| {\n                        // 1. Let dtf be F.[[DateTimeFormat]].\n                        // 2. Assert: dtf is an Object and dtf has an [[InitializedDateTimeFormat]] internal slot.\n                        let date = args.get_or_undefined(0);\n                        // 3. If date is not provided or is undefined, then\n                        let x = if date.is_undefined() {\n                            // NOTE (nekevss) i64 should be sufficient for a millisecond\n                            // representation.\n                            // a. Let x be ! Call(%Date.now%, undefined).\n                            context.clock().system_time_millis() as f64\n                        // 4. Else,\n                        } else {\n                            // NOTE (nekevss) The i64 covers all MAX_SAFE_INTEGER values.\n                            // a. Let x be ? ToNumber(date).\n                            date.to_number(context)?\n                        };\n\n                        // 5. Return ? FormatDateTime(dtf, x).\n                        // A.O 11.5.6 PartitionDateTimePattern: 1. TimeClip(x). 2. If NaN throw. Then ToLocalTime and format.\n                        let x = time_clip(x);\n                        if x.is_nan() {\n                            return Err(js_error!(RangeError: \"formatted date cannot be NaN\"));\n                        }\n                        let result = format_timestamp_with_dtf(dtf.borrow().data(), x, context)?;\n                        Ok(JsValue::from(result))\n                    },\n                    dtf_clone,\n                ),\n            )\n            .length(1)\n            .build();\n\n            // b. Set F.[[DateTimeFormat]] to dtf.\n            // c. Set dtf.[[BoundFormat]] to F.\n            dtf.data_mut().bound_format = Some(bound_format.clone());\n            Ok(bound_format.into())\n        }\n    }\n\n    /// [`Intl.DateTimeFormat.supportedLocalesOf ( locales [ , options ] )`][spec]\n    ///\n    /// Returns an array containing those of the provided locales that are\n    /// supported in date and time formatting without having to fall back to\n    /// the runtime's default locale.\n    ///\n    /// More information:\n    ///  - [MDN documentation][mdn]\n    ///\n    /// [spec]: https://tc39.es/ecma402/#sec-intl.datetimeformat.supportedlocalesof\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/DateTimeFormat/supportedLocalesOf\n    fn supported_locales_of(\n        _: &JsValue,\n        args: &[JsValue],\n        context: &mut Context,\n    ) -> JsResult<JsValue> {\n        let locales = args.get_or_undefined(0);\n        let options = args.get_or_undefined(1);\n\n        // 1. Let availableLocales be %DateTimeFormat%.[[AvailableLocales]].\n        // 2. Let requestedLocales be ? CanonicalizeLocaleList(locales).\n        let requested_locales = canonicalize_locale_list(locales, context)?;\n\n        // 3. Return ? FilterLocales(availableLocales, requestedLocales, options).\n        filter_locales::<Self>(requested_locales, options, context).map(JsValue::from)\n    }\n\n    /// [`Intl.DateTimeFormat.prototype.resolvedOptions ( )`][spec]\n    ///\n    /// [spec]: https://tc39.es/ecma402/#sec-intl.datetimeformat.prototype.resolvedoptions\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/DateTimeFormat/resolvedOptions\n    fn resolved_options(this: &JsValue, _: &[JsValue], context: &mut Context) -> JsResult<JsValue> {\n        //This function provides access to the locale and options computed during initialization of the object.\n\n        // 1. Let dtf be the this value.\n        // 2. If the implementation supports the normative optional constructor mode of 4.3 Note 1, then\n        //       a. Set dtf to ? UnwrapDateTimeFormat(dtf).\n        // 3. Perform ? RequireInternalSlot(dtf, [[InitializedDateTimeFormat]]).\n        let dtf_object = unwrap_date_time_format(this, context)?;\n        if let Some(cached) = dtf_object.borrow().data().resolved_options.clone() {\n            return Ok(cached.into());\n        }\n\n        // 4. Let options be OrdinaryObjectCreate(%Object.prototype%).\n        // 5. For each row of Table 15, except the header row, in table order, do\n        //      a. Let p be the Property value of the current row.\n        //      b. If there is an Internal Slot value in the current row, then\n        //          i. Let v be the value of dtf's internal slot whose name is the Internal Slot value of the current row.\n        //      c. Else,\n        //          i. Let format be dtf.[[DateTimeFormat]].\n        //          ii. If format has a field [[<p>]] and dtf.[[DateStyle]] is undefined and dtf.[[TimeStyle]] is undefined, then\n        //              1. Let v be format.[[<p>]].\n        //          iii. Else,\n        //              1. Let v be undefined.\n        //      d. If v is not undefined, then\n        //          i. If there is a Conversion value in the current row, then\n        //              1. Let conversion be the Conversion value of the current row.\n        //              2. If conversion is hour12, then\n        //              a. If v is \"h11\" or \"h12\", set v to true. Otherwise, set v to false.\n        //              3. Else,\n        //              a. Assert: conversion is number.\n        //              b. Set v to 𝔽(v).\n        //          ii. Perform ! CreateDataPropertyOrThrow(options, p, v).\n        let result = {\n            let dtf = dtf_object.borrow();\n            let dtf = dtf.data();\n\n            let mut options = ObjectInitializer::new(context);\n            options.property(\n                js_string!(\"locale\"),\n                js_string!(dtf.locale.to_string()),\n                Attribute::all(),\n            );\n\n            if let Some(ca) = &dtf.calendar_algorithm {\n                options.property(\n                    js_string!(\"calendar\"),\n                    js_string!(ca.as_str()),\n                    Attribute::all(),\n                );\n            }\n\n            if let Some(nu) = &dtf.numbering_system {\n                options.property(\n                    js_string!(\"numberingSystem\"),\n                    js_string!(nu.as_str()),\n                    Attribute::all(),\n                );\n            }\n\n            let time_zone_str = match &dtf.time_zone {\n                FormatTimeZone::UtcOffset(offset) => {\n                    let seconds = offset.to_seconds();\n                    let hours = seconds / 3600;\n                    let minutes = (seconds.abs() % 3600) / 60;\n                    format!(\"{hours:+03}:{minutes:02}\")\n                }\n                FormatTimeZone::Identifier((tz, _id)) => tz.to_string(),\n            };\n            options.property(\n                js_string!(\"timeZone\"),\n                js_string!(time_zone_str),\n                Attribute::all(),\n            );\n\n            if let Some(hc) = &dtf.hour_cycle {\n                options.property(\n                    js_string!(\"hourCycle\"),\n                    js_string!(hc.as_str()),\n                    Attribute::all(),\n                );\n                //h11/h12 -> true, h23/h24 -> false , because its h12 conversion time\n                let hour12 = matches!(hc, IcuHourCycle::H11 | IcuHourCycle::H12);\n                options.property(js_string!(\"hour12\"), hour12, Attribute::all());\n            }\n\n            if let Some(ds) = dtf.date_style {\n                let ds_str = match ds {\n                    DateStyle::Full => \"full\",\n                    DateStyle::Long => \"long\",\n                    DateStyle::Medium => \"medium\",\n                    DateStyle::Short => \"short\",\n                };\n                options.property(\n                    js_string!(\"dateStyle\"),\n                    js_string!(ds_str),\n                    Attribute::all(),\n                );\n            }\n\n            if let Some(ts) = dtf.time_style {\n                let ts_str = match ts {\n                    TimeStyle::Full => \"full\",\n                    TimeStyle::Long => \"long\",\n                    TimeStyle::Medium => \"medium\",\n                    TimeStyle::Short => \"short\",\n                };\n                options.property(\n                    js_string!(\"timeStyle\"),\n                    js_string!(ts_str),\n                    Attribute::all(),\n                );\n            }\n\n            options.build()\n        };\n\n        // 6. Return options.\n        dtf_object.borrow_mut().data_mut().resolved_options = Some(result.clone());\n        Ok(result.into())\n    }\n}\n\n// Represents a ISO8601 ToLocalTime Record\n//\n// https://tc39.es/ecma402/#sec-datetimeformat-tolocaltime-records\nstruct ToLocalTime {\n    year: i32,\n    month: u8,\n    day: u8,\n    hour: u8,\n    minute: u8,\n    second: u8,\n    subsecond: u32,\n}\n\nimpl ToLocalTime {\n    // NOTE (nekevss): we may need to adjust the below steps.\n    //\n    // The core problem is how to adopt the spec steps while also\n    // acting as a proper intermediate between built-ins and ICU4X.\n    /// The below steps are adapted from 11.5.6 and 11.5.12\n    pub(crate) fn from_local_epoch_milliseconds(local_millis: f64) -> JsResult<Self> {\n        // 11.5.6, 1. Let x be TimeClip(x).\n        let x = time_clip(local_millis);\n        // 11.5.6, 2. If x is NaN, throw a RangeError exception.\n        if x.is_nan() {\n            return Err(js_error!(RangeError: \"formattable time value cannot be NaN\"));\n        }\n        // NOTE: The switch to BigInt just for the value to be reverted to float\n        // during ToLocalTime calculations\n        // 11.5.6, 3. Let epochNanoseconds be ℤ(ℝ(x) × 10**6).\n        let epoch_nanoseconds = (x * 1_000_000f64) as i128;\n\n        // We convert back to milliseconds: 𝔽(floor(tz / 10**6))\n        let t = epoch_nanoseconds.div_euclid(1_000_000) as f64;\n\n        // 11.5.5, Step 12. Return ToLocalTime record values\n        // Also see, 11.5.12\n        let year = year_from_time(t);\n        let month = month_from_time(t);\n        let day = date_from_time(t);\n        let hour = hour_from_time(t);\n        let minute = min_from_time(t);\n        let second = sec_from_time(t);\n        let ms = u32::from(ms_from_time(t));\n\n        // 11.5.5, Step 15.f.v. If p is \"month\", set v to v + 1.\n        let month = month + 1; // This month is zero based (0-11)\n\n        Ok(Self {\n            year,\n            month,\n            day,\n            hour,\n            minute,\n            second,\n            subsecond: ms * 1_000_000,\n        })\n    }\n\n    pub(crate) fn to_formattable_datetime(&self) -> JsResult<DateTime<Iso>> {\n        Ok(DateTime {\n            date: Date::try_new_iso(self.year, self.month, self.day)\n                .ok()\n                .js_expect(\"TimeClip ensures valid range.\")?,\n            time: Time::try_new(self.hour, self.minute, self.second, self.subsecond)\n                .ok()\n                .js_expect(\"valid values\")?,\n        })\n    }\n}\n\n// ==== Abstract Operations ====\n\n/// Creates a [`DateTimeFormat`] struct (internal slots only). The constructor wraps this in a\n/// `JsObject` with the correct prototype; Date.prototype.toLocaleString (and friends) use it\n/// directly with [`format_timestamp_with_dtf`] without allocating a JS object.\npub(crate) fn create_date_time_format(\n    locales: &JsValue,\n    options: &JsValue,\n    date_time_format_type: FormatType,\n    defaults: FormatDefaults,\n    context: &mut Context,\n) -> JsResult<DateTimeFormat> {\n    // 2. Let hour12 be undefined. <- TODO\n    // 3. Let modifyResolutionOptions be a new Abstract Closure with parameters (options) that captures hour12 and performs the following steps when called:\n    //        a. Set hour12 to options.[[hour12]].\n    //        b. Remove field [[hour12]] from options.\n    //        c. If hour12 is not undefined, set options.[[hc]] to null.\n    // 4. Let optionsResolution be ? ResolveOptions(%Intl.DateTimeFormat%, %Intl.DateTimeFormat%.[[LocaleData]],\n    // locales, options, « coerce-options », modifyResolutionOptions).\n    //\n    // NOTE: We inline ResolveOptions here. (Could be worked into an abstract operation util function)\n    // ResolveOptions 1. Let requestedLocales be ? CanonicalizeLocaleList(locales).\n    let requested_locales = canonicalize_locale_list(locales, context)?;\n    // NOTE: skip ResolveOptions 2, which is based on `REQUIRE-OPTIONS` vs `COERCE-OPTIONS`\n    // ResolveOptions 3. If specialBehaviours is present and contains coerce-options,\n    // set options to ? CoerceOptionsToObject(options). Otherwise, set options to ? GetOptionsObject(options).\n    let options = coerce_options_to_object(options, context)?;\n    // ResolveOptions 4. Let matcher be ? GetOption(options, \"localeMatcher\", string, « \"lookup\", \"best fit\" », \"best fit\").\n    let matcher = get_option(&options, js_string!(\"localeMatcher\"), context)?.unwrap_or_default();\n\n    // NOTE: We unroll the below const loop in step 6 using the\n    // ResolutionOptionDescriptors from the internal slots\n    // https://tc39.es/ecma402/#sec-intl.datetimeformat-internal-slots\n    let mut preferences = DateTimeFormatterPreferences::default();\n\n    // 6. For each Resolution Option Descriptor desc of constructor.[[ResolutionOptionDescriptors]], do\n    // a. If desc has a [[Type]] field, let type be desc.[[Type]]. Otherwise, let type be string.\n    // b. If desc has a [[Values]] field, let values be desc.[[Values]]. Otherwise, let values be empty.\n    // c. Let value be ? GetOption(options, desc.[[Property]], type, values, undefined).\n    // d. If value is not undefined, then\n    // i. Set value to ! ToString(value).\n    // ii. If value cannot be matched by the type Unicode locale nonterminal, throw a RangeError exception.\n    // e. Let key be desc.[[Key]].\n    // f. Set opt.[[<key>]] to value.\n\n    // Handle { [[Key]]: \"ca\", [[Property]]: \"calendar\" }\n    preferences.calendar_algorithm =\n        get_option::<Value>(&options, js_string!(\"calendar\"), context)?\n            .map(|ca| CalendarAlgorithm::try_from(&ca))\n            .transpose()\n            .map_err(|_icu4x_error| js_error!(RangeError: \"unknown calendar algorithm\"))?;\n\n    // { [[Key]]: \"nu\", [[Property]]: \"numberingSystem\" }\n    preferences.numbering_system =\n        get_option::<Value>(&options, js_string!(\"numberingSystem\"), context)?\n            .map(NumberingSystem::try_from)\n            .transpose()\n            .map_err(|_icu4x_error| js_error!(RangeError: \"unknown numbering system\"))?;\n\n    // { [[Key]]: \"hour12\", [[Property]]: \"hour12\", [[Type]]: boolean }\n    let hour_12 = get_option::<bool>(&options, js_string!(\"hour12\"), context)?;\n\n    // { [[Key]]: \"hc\", [[Property]]: \"hourCycle\", [[Values]]: « \"h11\", \"h12\", \"h23\", \"h24\" » }\n    preferences.hour_cycle =\n        get_option::<options::HourCycle>(&options, js_string!(\"hourCycle\"), context)?\n            .map(|hc| {\n                // Handle steps 3.a-c here\n                // c. If hour12 is not undefined, set options.[[hc]] to null.\n                if hour_12.is_some() {\n                    Ok(None)\n                } else {\n                    IcuHourCycle::try_from(hc).map(Some)\n                }\n            })\n            .transpose()\n            .map_err(|_icu4x_error| js_error!(RangeError: \"unknown hour cycle\"))?\n            .flatten();\n\n    let mut intl_options = IntlOptions {\n        matcher,\n        preferences,\n    };\n\n    // ResolveOptions 8. Let resolution be ResolveLocale(constructor.[[AvailableLocales]], requestedLocales,\n    // opt, constructor.[[RelevantExtensionKeys]], localeData).\n    let resolved_locale = resolve_locale::<DateTimeFormat>(\n        requested_locales,\n        &mut intl_options,\n        context.intl_provider(),\n    )?;\n\n    // TODO: The resolved calendar, numbering system, and hour cycle should come from\n    // the ICU4X locale resolution result, not hardcoded defaults. However, ICU4X does\n    // not yet expose getters for these computed values on DateTimeFormatter.\n    // This means e.g. `new Intl.DateTimeFormat(\"ar\").resolvedOptions().numberingSystem`\n    // incorrectly returns \"latn\" instead of \"arab\".\n    // Tracked at: unicode-org/icu4x#5868\n    if intl_options.preferences.calendar_algorithm.is_none() {\n        intl_options.preferences.calendar_algorithm = CalendarAlgorithm::try_from(\n            &Value::try_from_str(\"gregory\").expect(\"'gregory' is a valid BCP 47 value\"),\n        )\n        .ok();\n    }\n\n    if intl_options.preferences.numbering_system.is_none() {\n        intl_options.preferences.numbering_system = NumberingSystem::try_from(\n            Value::try_from_str(\"latn\").expect(\"'latn' is a valid BCP 47 value\"),\n        )\n        .ok();\n    }\n\n    if intl_options.preferences.hour_cycle.is_none() {\n        intl_options.preferences.hour_cycle = IcuHourCycle::try_from(\n            &Value::try_from_str(\"h12\").expect(\"'h12' is a valid BCP 47 value\"),\n        )\n        .ok();\n    }\n    // 5. Set options to optionsResolution.[[Options]].\n    // 6. Let r be optionsResolution.[[ResolvedLocale]].\n    // 7. Set (deferred) dateTimeFormat.[[Locale]] to r.[[Locale]].\n    // 8. Let (deferred) resolvedCalendar be r.[[ca]].\n    // 9. Set (deferred) dateTimeFormat.[[Calendar]] to resolvedCalendar.\n    // 10. Set (deferred) dateTimeFormat.[[NumberingSystem]] to r.[[nu]].\n    // 11. Let (deferred) resolvedLocaleData be r.[[LocaleData]].\n\n    // TODO: Handle hour12 and hc\n    // 12. If hour12 is true, then\n    // a. Let hc be resolvedLocaleData.[[hourCycle12]].\n    // 13. Else if hour12 is false, then\n    // a. Let hc be resolvedLocaleData.[[hourCycle24]].\n    // 14. Else,\n    // a. Assert: hour12 is undefined.\n    // b. Let hc be r.[[hc]].\n    // c. If hc is null, set hc to resolvedLocaleData.[[hourCycle]].\n\n    // 15. Let timeZone be ? Get(options, \"timeZone\").\n    let time_zone = options.get(js_string!(\"timeZone\"), context)?;\n\n    // 16. If timeZone is undefined, then\n    let time_zone = if time_zone.is_undefined() {\n        // TODO (nekevss): Resolve system time zone\n        // a. Set timeZone to SystemTimeZoneIdentifier().\n        JsString::from(\"Etc/UTC\")\n    // 17. Else,\n    } else {\n        // a. Set timeZone to ? ToString(timeZone).\n        time_zone.to_string(context)?\n    };\n    // 18. If IsTimeZoneOffsetString(timeZone) is true, then\n    let time_zone_string = time_zone.to_std_string_escaped();\n    // Note: Should a timezone enum be part of temporal_rs, icu_time, or an ECMA402 wrapper lib\n    let time_zone = if let Ok(utc_offset) = UtcOffset::try_from_str(&time_zone_string) {\n        //  a. Let parseResult be ParseText(StringToCodePoints(timeZone), UTCOffset).\n        //  b. Assert: parseResult is a Parse Node.\n        //  c. If parseResult contains more than one MinuteSecond Parse Node, throw a RangeError exception.\n        //  d. Let offsetNanoseconds be ParseTimeZoneOffsetString(timeZone).\n        //  e. Let offsetMinutes be offsetNanoseconds / (6 × 10**10).\n        //  f. Assert: offsetMinutes is an integer.\n        //  g. Set timeZone to FormatOffsetTimeZoneIdentifier(offsetMinutes).\n        FormatTimeZone::UtcOffset(utc_offset)\n    } else {\n        // 19. Else,\n        //  a. Let timeZoneIdentifierRecord be GetAvailableNamedTimeZoneIdentifier(timeZone).\n        //  b. If timeZoneIdentifierRecord is empty, throw a RangeError exception.\n        //  c. Set timeZone to timeZoneIdentifierRecord.[[PrimaryIdentifier]].\n        let parser =\n            IanaParser::try_new_with_buffer_provider(context.intl_provider().erased_provider())\n                .map_err(|_| {\n                    JsNativeError::error().with_message(\"Failed to init time zone data provider\")\n                })?;\n        let time_zone = parser.as_borrowed().parse(&time_zone_string);\n        let time_zone_id = context\n            .timezone_provider()\n            .get(time_zone_string.as_bytes())\n            .map_err(|_| {\n                JsNativeError::range()\n                    .with_message(format!(\"{time_zone_string:#?} was not a valid time zone.\"))\n            })?;\n        FormatTimeZone::Identifier((time_zone, time_zone_id))\n    };\n    // 20. (deferred) Set dateTimeFormat.[[TimeZone]] to timeZone.\n\n    // 21. Let formatOptions be a new Record.\n    // 22. Set formatOptions.[[hourCycle]] to hc.\n    // 23. Let hasExplicitFormatComponents be false.\n\n    // NOTE (nekevss): Step 24 is adopted in the `FormatOptions`\n    // 24. For each row of Table 16, except the header row, in table order, do\n    //         a. Let prop be the name given in the Property column of the current row.\n    //         b. If prop is \"fractionalSecondDigits\", then\n    //                i. Let value be ? GetNumberOption(options, \"fractionalSecondDigits\", 1, 3, undefined).\n    //         c. Else,\n    //                i. Let values be a List whose elements are the strings given in the Values column of the current row.\n    //                ii. Let value be ? GetOption(options, prop, string, values, undefined).\n    //         d. Set formatOptions.[[<prop>]] to value.\n    //         e. If value is not undefined, then\n    //                i. Set hasExplicitFormatComponents to true.\n    let mut format_options = FormatOptions::try_init(&options, preferences.hour_cycle, context)?;\n\n    // TODO: how should formatMatcher be used?\n    // 25. Let formatMatcher be ? GetOption(options, \"formatMatcher\", string, « \"basic\", \"best fit\" », \"best fit\").\n    let format_matcher =\n        get_option::<FormatMatcher>(&options, js_string!(\"formatMatcher\"), context)?\n            .unwrap_or(FormatMatcher::BestFit);\n    // 26. Let dateStyle be ? GetOption(options, \"dateStyle\", string, « \"full\", \"long\", \"medium\", \"short\" », undefined).\n    let date_style = get_option::<DateStyle>(&options, js_string!(\"dateStyle\"), context)?;\n    // 27. Set dateTimeFormat.[[DateStyle]] to dateStyle.\n    // 28. Let timeStyle be ? GetOption(options, \"timeStyle\", string, « \"full\", \"long\", \"medium\", \"short\" », undefined).\n    let time_style = get_option::<TimeStyle>(&options, js_string!(\"timeStyle\"), context)?;\n    // 29. (deferred) Set dateTimeFormat.[[TimeStyle]] to timeStyle.\n    // 30. If dateStyle is not undefined or timeStyle is not undefined, then\n    let fieldset = if date_style.is_some() || time_style.is_some() {\n        // a. If hasExplicitFormatComponents is true, then\n        if format_options.has_explicit_format_components() {\n            // i. Throw a TypeError exception.\n            return Err(\n                js_error!(TypeError: \"cannot have explicit format components when timeStyle or dateStyle is defined\"),\n            );\n        }\n        // b. If required is date and timeStyle is not undefined, then\n        if date_time_format_type == FormatType::Date && time_style.is_some() {\n            // i. Throw a TypeError exception.\n            return Err(\n                js_error!(TypeError: \"timeStyle cannot be defined for a date DateTimeFormat\"),\n            );\n        }\n        // c. If required is time and dateStyle is not undefined, then\n        if date_time_format_type == FormatType::Time && date_style.is_some() {\n            // i. Throw a TypeError exception.\n            return Err(\n                js_error!(TypeError: \"dateStyle cannot be defined for a time DateTimeFormat\"),\n            );\n        }\n        // TODO (nekevss): implement d-e\n        // TODO (nekevss): Do we have access to the styles?\n        // d. Let styles be resolvedLocaleData.[[styles]].[[<resolvedCalendar>]].\n        // e. Let bestFormat be DateTimeStyleFormat(dateStyle, timeStyle, styles).\n        date_time_style_format(date_style, time_style)?\n    // 31. Else,\n    } else {\n        // a. Let needDefaults be true.\n        // b. If required is date or any, then\n        // i. For each property name prop of « \"weekday\", \"year\", \"month\", \"day\" », do\n        // 1. Let value be formatOptions.[[<prop>]].\n        // 2. If value is not undefined, set needDefaults to false.\n        // c. If required is time or any, then\n        // i. For each property name prop of « \"dayPeriod\", \"hour\", \"minute\", \"second\", \"fractionalSecondDigits\" », do\n        // 1. Let value be formatOptions.[[<prop>]].\n        // 2. If value is not undefined, set needDefaults to false.\n        let needs_defaults = format_options.check_dtf_type(date_time_format_type);\n        // d. If needDefaults is true and defaults is either date or all, then\n        if needs_defaults && defaults != FormatDefaults::Time {\n            // i. For each property name prop of « \"year\", \"month\", \"day\" », do\n            // 1. Set formatOptions.[[<prop>]] to \"numeric\".\n            format_options.set_date_defaults();\n        }\n        // e. If needDefaults is true and defaults is either time or all, then\n        if needs_defaults && defaults != FormatDefaults::Date {\n            // i. For each property name prop of « \"hour\", \"minute\", \"second\" », do\n            // 1. Set formatOptions.[[<prop>]] to \"numeric\".\n            format_options.set_time_defaults();\n        }\n        // TODO (nekevss): Do we have access to the localized formats via `icu_datetime`. Is there\n        // a specific API by which this is accessed.\n        //\n        // f. Let formats be resolvedLocaleData.[[formats]].[[<resolvedCalendar>]].\n        // g. If formatMatcher is \"basic\", then\n        // i. Let bestFormat be BasicFormatMatcher(formatOptions, formats).\n        // h. Else,\n        // i. Let bestFormat be BestFitFormatMatcher(formatOptions, formats).\n        match format_matcher {\n            FormatMatcher::Basic | FormatMatcher::BestFit => {\n                best_fit_date_time_format(&format_options)?\n            }\n        }\n    };\n    // 32. Set dateTimeFormat.[[DateTimeFormat]] to bestFormat.\n    // 33. If bestFormat has a field [[hour]], then\n    // a. Set dateTimeFormat.[[HourCycle]] to hc.\n    // 34. Return dateTimeFormat.\n    let formatter = DateTimeFormatter::try_new_with_buffer_provider(\n        context.intl_provider().erased_provider(),\n        resolved_locale.clone().into(),\n        fieldset,\n    )\n    .map_err(|e| JsNativeError::range().with_message(format!(\"failed to load formatter: {e}\")))?;\n\n    Ok(DateTimeFormat {\n        locale: resolved_locale,\n        calendar_algorithm: intl_options.preferences.calendar_algorithm,\n        numbering_system: intl_options.preferences.numbering_system,\n        hour_cycle: intl_options.preferences.hour_cycle,\n        date_style,\n        time_style,\n        time_zone,\n        fieldset,\n        formatter,\n        bound_format: None,\n        resolved_options: None,\n    })\n}\n\n/// Formats a timestamp (epoch milliseconds) using the given [`DateTimeFormat`] internals.\n///\n/// This is the shared implementation used by:\n/// - the bound `format` function created in `get_format`, and\n/// - [`format_date_time_locale`] used by `Date.prototype.toLocaleString` (and friends).\n///\n/// It corresponds to the *post*-`TimeClip` portion of\n/// [`FormatDateTime(dtf, x)`](https://tc39.es/ecma402/#sec-formatdatetime),\n/// and the `ToLocalTime` / `PartitionDateTimePattern` logic from\n/// [11.5.6](https://tc39.es/ecma402/#sec-partitiondatetimepattern) and\n/// [11.5.12](https://tc39.es/ecma402/#sec-tolocaltime).\n///\n/// Callers must have already applied `TimeClip` and `NaN` check\n/// (`FormatDateTime` steps 1–2). This helper implements:\n///\n/// 11.5.6 `PartitionDateTimePattern` ( dtf, x )\n/// 1. Let x be TimeClip(x). (Done by caller)\n/// 2. If x is `NaN`, throw a `RangeError` exception. (Done by caller)\n/// 3. Let epochNanoseconds be ℤ(ℝ(x) × 10^6).\n/// 4. Let timeZone be dtf.[[`TimeZone`]].\n/// 5. Let offsetNs be GetOffsetNanosecondsFor(timeZone, epochNanoseconds).\n/// 6. Let tz be 𝔽(ℝ(x) + ℝ(offsetNs) / 10^6).\n///\n/// Then calls `ToLocalTime::from_local_epoch_milliseconds` to obtain calendar fields,\n/// and formats the resulting `ZonedDateTime` with ICU4X.\nfn format_timestamp_with_dtf(\n    dtf: &DateTimeFormat,\n    timestamp: f64,\n    context: &mut Context,\n) -> JsResult<JsString> {\n    // PartitionDateTimePattern ( dtf, x ) step 3:\n    // Let epochNanoseconds be ℤ(ℝ(x) × 10^6).\n    //\n    // NOTE: `timestamp` is already `TimeClip`'d by the caller and represents *UTC epoch milliseconds*.\n    let epoch_ns = timestamp as i128 * 1_000_000;\n\n    // PartitionDateTimePattern ( dtf, x ) step 4:\n    // Let timeZone be dtf.[[`TimeZone`]].\n    let time_zone = &dtf.time_zone;\n\n    // PartitionDateTimePattern ( dtf, x ) step 5:\n    // Let offsetNs be GetOffsetNanosecondsFor(timeZone, epochNanoseconds).\n    //\n    // NOTE: the spec describes the offset in *nanoseconds*. Internally, we obtain/normalize it to\n    // seconds (and then milliseconds) for use with `ToLocalTime::from_local_epoch_milliseconds`.\n    let time_zone_offset_seconds = match time_zone {\n        FormatTimeZone::UtcOffset(offset) => offset.to_seconds(),\n        FormatTimeZone::Identifier((_, time_zone_id)) => {\n            let offset_seconds = context\n                .timezone_provider()\n                .transition_nanoseconds_for_utc_epoch_nanoseconds(*time_zone_id, epoch_ns)\n                .map_err(\n                    |_e| js_error!(RangeError: \"unable to determine transition nanoseconds\"),\n                )?;\n            offset_seconds.0 as i32\n        }\n    };\n\n    // PartitionDateTimePattern ( dtf, x ) step 6:\n    // Let tz be 𝔽(ℝ(x) + ℝ(offsetNs) / 10^6).\n    let tz = timestamp + f64::from(time_zone_offset_seconds * 1_000);\n    let fields = ToLocalTime::from_local_epoch_milliseconds(tz)?;\n    let dt = fields.to_formattable_datetime()?;\n    let tz_info = time_zone.to_time_zone_info();\n    let tz_info_at_time = tz_info.at_date_time_iso(dt);\n    let zdt = ZonedDateTime {\n        date: dt.date,\n        time: dt.time,\n        zone: tz_info_at_time,\n    };\n    let result = dtf.formatter.format(&zdt).to_string();\n    Ok(JsString::from(result))\n}\n\nfn date_time_style_format(\n    date_style: Option<DateStyle>,\n    time_style: Option<TimeStyle>,\n) -> JsResult<CompositeFieldSet> {\n    let mut builder = FieldSetBuilder::default();\n    builder.length = match date_style {\n        Some(DateStyle::Full | DateStyle::Long) => Some(Length::Long),\n        Some(DateStyle::Medium) => Some(Length::Medium),\n        Some(DateStyle::Short) => Some(Length::Short),\n        None => match time_style {\n            Some(TimeStyle::Full | TimeStyle::Long) => Some(Length::Long),\n            Some(TimeStyle::Medium) => Some(Length::Medium),\n            Some(TimeStyle::Short) => Some(Length::Short),\n            None => return Err(js_error!(TypeError: \"dateStyle or timeStyle must be defined\")),\n        },\n    };\n    builder.date_fields = match date_style {\n        Some(DateStyle::Full) => Some(DateFields::YMDE),\n        Some(DateStyle::Long | DateStyle::Medium | DateStyle::Short) => Some(DateFields::YMD),\n        None => None, // NOTE: timeStyle being undefined is checked when setting length\n    };\n    builder.time_precision = match time_style {\n        Some(TimeStyle::Full | TimeStyle::Long | TimeStyle::Medium) => Some(TimePrecision::Second),\n        Some(TimeStyle::Short) => Some(TimePrecision::Minute),\n        None => None, // NOTE: dateStyle being undefined is checked when setting length\n    };\n    builder\n        .build_composite()\n        .map_err(|e| JsNativeError::range().with_message(e.to_string()).into())\n}\n\nfn best_fit_date_time_format(format_options: &FormatOptions) -> JsResult<CompositeFieldSet> {\n    let mut builder = FieldSetBuilder::default();\n    builder.length = format_options.to_length();\n    builder.date_fields = format_options.to_date_fields();\n    builder.time_precision = format_options.to_time_fields();\n    builder.zone_style = format_options.to_zone_style();\n    builder\n        .build_composite()\n        .map_err(|e| JsNativeError::range().with_message(e.to_string()).into())\n}\n\n/// Represents the `required` and `defaults` arguments in the abstract operation\n/// `toDateTimeOptions`.\n#[derive(Debug, Clone, Copy, PartialEq)]\npub(crate) enum FormatType {\n    Date,\n    Time,\n    Any,\n}\n\n/// Indicates which default fields should be applied when `ToDateTimeOptions`\n/// determines defaults are needed. `All` applies both date and time defaults.\n#[derive(Debug, Clone, Copy, PartialEq)]\npub(crate) enum FormatDefaults {\n    Date,\n    Time,\n    /// Apply both date and time defaults (e.g. for `toLocaleString`).\n    All,\n}\n\n/// Abstract operation [`UnwrapDateTimeFormat ( dtf )`][spec].\n///\n/// This also checks that the returned object is a `DateTimeFormat`, which skips the\n/// call to `RequireInternalSlot`.\n///\n/// [spec]: https://tc39.es/ecma402/#sec-unwrapdatetimeformat\nfn unwrap_date_time_format(\n    dtf: &JsValue,\n    context: &mut Context,\n) -> JsResult<JsObject<DateTimeFormat>> {\n    // 1. If Type(dtf) is not Object, throw a TypeError exception.\n    let dtf_o = dtf.as_object().ok_or_else(|| {\n        JsNativeError::typ()\n            .with_message(\"value was not an initialized `Intl.DateTimeFormat` object\")\n    })?;\n\n    if let Ok(dtf) = dtf_o.clone().downcast::<DateTimeFormat>() {\n        // 3. Return dtf.\n        return Ok(dtf);\n    }\n\n    // 2. If dtf does not have an [[InitializedDateTimeFormat]] internal slot and\n    //    ? OrdinaryHasInstance(%Intl.DateTimeFormat%, dtf) is true, then\n    let constructor = context\n        .intrinsics()\n        .constructors()\n        .date_time_format()\n        .constructor();\n    if JsValue::ordinary_has_instance(&constructor.into(), dtf, context)? {\n        let fallback_symbol = context\n            .intrinsics()\n            .objects()\n            .intl()\n            .borrow()\n            .data()\n            .fallback_symbol();\n\n        //    a. Return ? Get(dtf, %Intl%.[[FallbackSymbol]]).\n        if let Some(dtf) = dtf_o\n            .get(fallback_symbol, context)?\n            .as_object()\n            .and_then(|o| o.downcast::<DateTimeFormat>().ok())\n        {\n            return Ok(dtf);\n        }\n    }\n\n    Err(JsNativeError::typ()\n        .with_message(\"object was not an initialized `Intl.DateTimeFormat` object\")\n        .into())\n}\n\n/// Shared helper used by Date.prototype.toLocaleString,\n/// Date.prototype.toLocaleDateString, and Date.prototype.toLocaleTimeString.\n/// Applies `ToDateTimeOptions` defaults, calls [`create_date_time_format`], and formats\n/// the timestamp via [`format_timestamp_with_dtf`] without allocating a JS object.\n#[allow(clippy::too_many_arguments)]\npub(crate) fn format_date_time_locale(\n    locales: &JsValue,\n    options: &JsValue,\n    format_type: FormatType,\n    defaults: FormatDefaults,\n    timestamp: f64,\n    context: &mut Context,\n) -> JsResult<JsValue> {\n    let options = coerce_options_to_object(options, context)?;\n    if format_type != FormatType::Time\n        && get_option::<DateStyle>(&options, js_string!(\"dateStyle\"), context)?.is_none()\n    {\n        options.create_data_property_or_throw(\n            js_string!(\"dateStyle\"),\n            JsValue::from(js_string!(\"long\")),\n            context,\n        )?;\n    }\n    if format_type != FormatType::Date\n        && get_option::<TimeStyle>(&options, js_string!(\"timeStyle\"), context)?.is_none()\n    {\n        options.create_data_property_or_throw(\n            js_string!(\"timeStyle\"),\n            JsValue::from(js_string!(\"long\")),\n            context,\n        )?;\n    }\n    let options_value = options.into();\n    let dtf = create_date_time_format(locales, &options_value, format_type, defaults, context)?;\n    // FormatDateTime steps 1–2: TimeClip and NaN check (format_timestamp_with_dtf does ToLocalTime + format only).\n    let x = time_clip(timestamp);\n    if x.is_nan() {\n        return Err(js_error!(RangeError: \"formatted date cannot be NaN\"));\n    }\n    let result = format_timestamp_with_dtf(&dtf, x, context)?;\n    Ok(JsValue::from(result))\n}\n"
  },
  {
    "path": "core/engine/src/builtins/intl/date_time_format/options.rs",
    "content": "//! Intl.DateTimeFormat options module\n\nuse crate::{\n    Context, JsError, JsNativeError, JsObject, JsResult, JsValue,\n    builtins::{\n        intl::{\n            ServicePreferences, date_time_format::FormatType, locale::validate_extension,\n            options::get_number_option,\n        },\n        options::{OptionType, get_option},\n    },\n    context::icu::IntlProvider,\n    js_error, js_string,\n};\nuse icu_calendar::cal::{\n    Buddhist, ChineseTraditional, Coptic, Ethiopian, Gregorian, Hebrew, Hijri, Indian, Japanese,\n    KoreanTraditional, Persian, Roc, hijri,\n};\nuse icu_datetime::{\n    DateTimeFormatterPreferences,\n    fieldsets::builder::{DateFields, ZoneStyle},\n    options::{Length, SubsecondDigits as IcuSubsecondDigits, TimePrecision},\n    preferences::{CalendarAlgorithm, HijriCalendarAlgorithm, HourCycle as IcuHourCycle},\n    scaffold::CldrCalendar,\n};\n\nuse icu_decimal::provider::DecimalSymbolsV1;\nuse icu_locale::extensions::unicode::Value;\nuse icu_provider::{\n    DataMarker, DataMarkerAttributes, DryDataProvider,\n    prelude::icu_locale_core::{\n        LanguageIdentifier, extensions::unicode, preferences::LocalePreferences,\n    },\n};\n\npub(crate) enum HourCycle {\n    H11,\n    H12,\n    H23,\n    H24,\n}\n\nimpl OptionType for HourCycle {\n    fn from_value(value: JsValue, context: &mut Context) -> JsResult<Self> {\n        match value.to_string(context)?.to_std_string_escaped().as_ref() {\n            \"h11\" => Ok(Self::H11),\n            \"h12\" => Ok(Self::H12),\n            \"h23\" => Ok(Self::H23),\n            \"h24\" => Ok(Self::H24),\n            _ => Err(js_error!(RangeError: \"unknown hourCycle option\")),\n        }\n    }\n}\n\nimpl TryFrom<HourCycle> for IcuHourCycle {\n    type Error = JsError;\n    fn try_from(hc: HourCycle) -> Result<Self, Self::Error> {\n        match hc {\n            HourCycle::H11 => Ok(IcuHourCycle::H11),\n            HourCycle::H12 => Ok(IcuHourCycle::H12),\n            HourCycle::H23 => Ok(IcuHourCycle::H23),\n            // TODO: Work on support for H24, potentially remove depending on fate\n            // of H24 option.\n            HourCycle::H24 => Err(js_error!(RangeError: \"h24 not currently supported.\")),\n        }\n    }\n}\n\npub(super) enum FormatMatcher {\n    Basic,\n    BestFit,\n}\n\nimpl OptionType for FormatMatcher {\n    fn from_value(value: JsValue, context: &mut Context) -> JsResult<Self> {\n        match value.to_string(context)?.to_std_string_escaped().as_ref() {\n            \"basic\" => Ok(Self::Basic),\n            \"best fit\" => Ok(Self::BestFit),\n            _ => Err(js_error!(RangeError: \"unknown formatMatcher option\")),\n        }\n    }\n}\n\n#[derive(Debug, Clone, Copy)]\npub(super) enum DateStyle {\n    Full,\n    Long,\n    Medium,\n    Short,\n}\n\nimpl OptionType for DateStyle {\n    fn from_value(value: JsValue, context: &mut Context) -> JsResult<Self> {\n        match value.to_string(context)?.to_std_string_escaped().as_ref() {\n            \"full\" => Ok(Self::Full),\n            \"long\" => Ok(Self::Long),\n            \"medium\" => Ok(Self::Medium),\n            \"short\" => Ok(Self::Short),\n            _ => Err(js_error!(RangeError: \"unknown dateStyle option\")),\n        }\n    }\n}\n\n#[derive(Debug, Clone, Copy)]\npub(super) enum TimeStyle {\n    Full,\n    Long,\n    Medium,\n    Short,\n}\n\nimpl OptionType for TimeStyle {\n    fn from_value(value: JsValue, context: &mut Context) -> JsResult<Self> {\n        match value.to_string(context)?.to_std_string_escaped().as_ref() {\n            \"full\" => Ok(Self::Full),\n            \"long\" => Ok(Self::Long),\n            \"medium\" => Ok(Self::Medium),\n            \"short\" => Ok(Self::Short),\n            _ => Err(js_error!(RangeError: \"unknown timeStyle option\")),\n        }\n    }\n}\n\nimpl OptionType for CalendarAlgorithm {\n    fn from_value(value: JsValue, context: &mut Context) -> JsResult<Self> {\n        let s = value.to_string(context)?.to_std_string_escaped();\n        Value::try_from_str(&s)\n            .ok()\n            .and_then(|v| CalendarAlgorithm::try_from(&v).ok())\n            .ok_or_else(|| {\n                JsNativeError::range()\n                    .with_message(format!(\"provided calendar `{s}` is invalid\"))\n                    .into()\n            })\n    }\n}\n\n// TODO: track https://github.com/unicode-org/icu4x/issues/6597 and\n// https://github.com/tc39/ecma402/issues/1002 for resolution on\n// `HourCycle::H24`.\nimpl OptionType for IcuHourCycle {\n    fn from_value(value: JsValue, context: &mut Context) -> JsResult<Self> {\n        match value.to_string(context)?.to_std_string_escaped().as_str() {\n            \"h11\" => Ok(IcuHourCycle::H11),\n            \"h12\" => Ok(IcuHourCycle::H12),\n            \"h23\" => Ok(IcuHourCycle::H23),\n            _ => Err(js_error!(RangeError: \"provided hour cycle was not `h11`, `h12` or `h23`\")),\n        }\n    }\n}\n\n// ==== Formatting options ====\n//\n// This section includes formatting options that act as an intermediary between\n// user space and ICU4X's datetimeformat composite fields.\n\npub(super) struct FormatOptions {\n    _hour_cycle: Option<IcuHourCycle>,                 // -> ???\n    week_day: Option<WeekDay>,                         // e -> Maps to DateField\n    era: Option<Era>,                                  // G -> Maps to YearStyle\n    year: Option<Year>,                                // Y -> Maps to DateField\n    month: Option<Month>,                              // M -> Maps to DateField\n    day: Option<Day>,                                  // D -> Maps to DateField\n    day_period: Option<DayPeriod>,                     // a -> ???\n    hour: Option<Hour>,                                // Maps to TimePrecision\n    minute: Option<Minute>,                            // Maps to TimePrecision\n    second: Option<Second>,                            // Maps to TimePrecision\n    fractional_second_digits: Option<SubsecondDigits>, // Maps to TimePrecision\n    time_zone_name: Option<TimeZoneName>,              // Maps to ZoneStyle\n}\n\nimpl FormatOptions {\n    pub(super) fn try_init(\n        options: &JsObject,\n        hour_cycle: Option<IcuHourCycle>, // TODO: Is option correct?\n        context: &mut Context,\n    ) -> JsResult<Self> {\n        // Below is adapted and inlined from Step 24 of `CreateDateTimeFormat`\n        let week_day = get_option::<WeekDay>(options, js_string!(\"weekDay\"), context)?;\n        let era = get_option::<Era>(options, js_string!(\"era\"), context)?;\n        let year = get_option::<Year>(options, js_string!(\"year\"), context)?;\n        let month = get_option::<Month>(options, js_string!(\"month\"), context)?;\n        let day = get_option::<Day>(options, js_string!(\"day\"), context)?;\n        let day_period = get_option::<DayPeriod>(options, js_string!(\"dayPeriod\"), context)?;\n        let hour = get_option::<Hour>(options, js_string!(\"hour\"), context)?;\n        let minute = get_option::<Minute>(options, js_string!(\"minute\"), context)?;\n        let second = get_option::<Second>(options, js_string!(\"second\"), context)?;\n        let fractional_second_digits =\n            get_number_option(options, js_string!(\"fractionalSecondDigits\"), 1, 3, context)?\n                .map(SubsecondDigits::from_i32);\n        let time_zone_name =\n            get_option::<TimeZoneName>(options, js_string!(\"timeZoneName\"), context)?;\n\n        Ok(Self {\n            _hour_cycle: hour_cycle,\n            week_day,\n            era,\n            year,\n            month,\n            day,\n            day_period,\n            hour,\n            minute,\n            second,\n            fractional_second_digits,\n            time_zone_name,\n        })\n    }\n\n    pub(super) fn set_date_defaults(&mut self) {\n        self.year = Some(Year::Numeric);\n        self.month = Some(Month::Numeric);\n        self.day = Some(Day::Numeric);\n    }\n\n    pub(super) fn set_time_defaults(&mut self) {\n        self.hour = Some(Hour::Numeric);\n        self.minute = Some(Minute::Numeric);\n        self.second = Some(Second::Numeric);\n    }\n\n    pub(super) fn has_explicit_format_components(&self) -> bool {\n        match self {\n            Self {\n                week_day: None,\n                era: None,\n                year: None,\n                month: None,\n                day: None,\n                day_period: None,\n                hour: None,\n                minute: None,\n                second: None,\n                fractional_second_digits: None,\n                time_zone_name: None,\n                ..\n            } => false,\n            // If any of the format fields is Some(_), return true\n            _ => true,\n        }\n    }\n\n    pub(super) fn check_dtf_type(&self, required: FormatType) -> bool {\n        // a. Let needDefaults be true.\n        // b. If required is date or any, then\n        // i. For each property name prop of « \"weekday\", \"year\", \"month\", \"day\" », do\n        // 1. Let value be formatOptions.[[<prop>]].\n        // 2. If value is not undefined, set needDefaults to false.\n        // c. If required is time or any, then\n        // i. For each property name prop of « \"dayPeriod\", \"hour\", \"minute\", \"second\", \"fractionalSecondDigits\" », do\n        // 1. Let value be formatOptions.[[<prop>]].\n        // 2. If value is not undefined, set needDefaults to false.\n        if required != FormatType::Time && self.has_date_defaults() {\n            return false;\n        }\n        if required != FormatType::Date && self.has_time_defaults() {\n            return false;\n        }\n        true\n    }\n\n    pub(super) fn has_date_defaults(&self) -> bool {\n        self.week_day.is_some() || self.year.is_some() || self.month.is_some() || self.day.is_some()\n    }\n\n    pub(super) fn has_time_defaults(&self) -> bool {\n        self.day_period.is_some()\n            || self.hour.is_some()\n            || self.minute.is_some()\n            || self.second.is_some()\n            || self.fractional_second_digits.is_some()\n    }\n\n    pub(super) fn to_length(&self) -> Option<Length> {\n        match (self.month, self.week_day, self.day_period, self.era) {\n            (Some(month), _, _, _) => Some(month.to_length()),\n            (None, Some(week_day), _, _) => Some(week_day.to_length()),\n            (None, None, Some(day_period), _) => Some(day_period.to_length()),\n            (None, None, None, Some(era)) => Some(era.to_length()),\n            (None, None, None, None) => None,\n        }\n    }\n\n    /// Convert the current `FormatOptions` to a [`DateFields`].\n    pub(super) fn to_date_fields(&self) -> Option<DateFields> {\n        match (self.year, self.month, self.day, self.week_day) {\n            (Some(_y), _m, _d, Some(_e)) => Some(DateFields::YMDE),\n            (Some(_y), _m, Some(_d), None) => Some(DateFields::YMD),\n            (Some(_y), Some(_m), None, None) => Some(DateFields::YM),\n            (Some(_y), None, None, None) => Some(DateFields::Y),\n            (None, Some(_m), _d, Some(_e)) => Some(DateFields::MDE),\n            (None, Some(_m), Some(_d), None) => Some(DateFields::MD),\n            (None, Some(_m), None, None) => Some(DateFields::M),\n            (None, None, Some(_d), Some(_e)) => Some(DateFields::DE),\n            (None, None, Some(_d), None) => Some(DateFields::D),\n            (None, None, None, Some(_e)) => Some(DateFields::E),\n            (None, None, None, None) => None,\n        }\n    }\n\n    /// Convert the current `FormatOptions` to a [`TimePrecision`].\n    pub(super) fn to_time_fields(&self) -> Option<TimePrecision> {\n        match (\n            self.hour,\n            self.minute,\n            self.second,\n            self.fractional_second_digits,\n        ) {\n            (_h, _m, _s, Some(digits)) => Some(TimePrecision::Subsecond(digits.into())),\n            (_h, _m, Some(_s), None) => Some(TimePrecision::Second),\n            (_h, Some(_m), None, None) => Some(TimePrecision::Minute),\n            (Some(_h), None, None, None) => Some(TimePrecision::Hour),\n            (None, None, None, None) => None,\n        }\n    }\n\n    /// Convert the current `FormatOptions` to a [`ZoneStyle`].\n    pub(super) fn to_zone_style(&self) -> Option<ZoneStyle> {\n        self.time_zone_name.map(TimeZoneName::to_zone_style)\n    }\n}\n\n// ==== Format Options ====\n\n#[derive(Debug, Clone, Copy)]\npub(crate) enum WeekDay {\n    Narrow,\n    Short,\n    Long,\n}\n\nimpl OptionType for WeekDay {\n    fn from_value(value: JsValue, context: &mut Context) -> JsResult<Self> {\n        match value.to_string(context)?.to_std_string_escaped().as_ref() {\n            \"narrow\" => Ok(Self::Narrow),\n            \"short\" => Ok(Self::Short),\n            \"long\" => Ok(Self::Long),\n            _ => Err(js_error!(RangeError: \"unknown weekDay option\")),\n        }\n    }\n}\n\nimpl WeekDay {\n    pub(crate) fn to_length(self) -> Length {\n        match self {\n            Self::Long => Length::Long,\n            Self::Short => Length::Medium,\n            Self::Narrow => Length::Short,\n        }\n    }\n}\n\n#[derive(Debug, Clone, Copy)]\npub(crate) enum Era {\n    Narrow,\n    Short,\n    Long,\n}\n\nimpl Era {\n    pub(crate) fn to_length(self) -> Length {\n        match self {\n            Self::Long => Length::Long,\n            Self::Short => Length::Medium,\n            Self::Narrow => Length::Short,\n        }\n    }\n}\n\nimpl OptionType for Era {\n    fn from_value(value: JsValue, context: &mut Context) -> JsResult<Self> {\n        match value.to_string(context)?.to_std_string_escaped().as_ref() {\n            \"narrow\" => Ok(Self::Narrow),\n            \"short\" => Ok(Self::Short),\n            \"long\" => Ok(Self::Long),\n            _ => Err(js_error!(RangeError: \"unknown era option\")),\n        }\n    }\n}\n\n#[derive(Debug, Clone, Copy)]\npub(crate) enum Year {\n    TwoDigit,\n    Numeric,\n}\n\nimpl OptionType for Year {\n    fn from_value(value: JsValue, context: &mut Context) -> JsResult<Self> {\n        match value.to_string(context)?.to_std_string_escaped().as_ref() {\n            \"2-digit\" => Ok(Self::TwoDigit),\n            \"numeric\" => Ok(Self::Numeric),\n            _ => Err(js_error!(RangeError: \"unknown year option\")),\n        }\n    }\n}\n\n#[derive(Debug, Clone, Copy)]\npub(crate) enum Month {\n    TwoDigit,\n    Numeric,\n    Narrow,\n    Short,\n    Long,\n}\n\nimpl Month {\n    pub(crate) fn to_length(self) -> Length {\n        // NOTE (nekevss): after a brief glance, narrow does not appear to be\n        // currently supported by ICU4X ... TBD\n        match self {\n            Self::Long => Length::Long,\n            Self::Short => Length::Medium,\n            _ => Length::Short,\n        }\n    }\n}\n\nimpl OptionType for Month {\n    fn from_value(value: JsValue, context: &mut Context) -> JsResult<Self> {\n        match value.to_string(context)?.to_std_string_escaped().as_ref() {\n            \"2-digit\" => Ok(Self::TwoDigit),\n            \"numeric\" => Ok(Self::Numeric),\n            \"narrow\" => Ok(Self::Narrow),\n            \"short\" => Ok(Self::Short),\n            \"long\" => Ok(Self::Long),\n            _ => Err(js_error!(RangeError: \"unknown month option\")),\n        }\n    }\n}\n\n#[derive(Debug, Clone, Copy)]\npub(crate) enum Day {\n    TwoDigit,\n    Numeric,\n}\n\nimpl OptionType for Day {\n    fn from_value(value: JsValue, context: &mut Context) -> JsResult<Self> {\n        match value.to_string(context)?.to_std_string_escaped().as_ref() {\n            \"2-digit\" => Ok(Self::TwoDigit),\n            \"numeric\" => Ok(Self::Numeric),\n            _ => Err(js_error!(RangeError: \"unknown day option\")),\n        }\n    }\n}\n\n#[derive(Debug, Clone, Copy)]\npub(crate) enum DayPeriod {\n    Narrow,\n    Short,\n    Long,\n}\n\nimpl DayPeriod {\n    pub(crate) fn to_length(self) -> Length {\n        match self {\n            Self::Long => Length::Long,\n            Self::Short => Length::Medium,\n            Self::Narrow => Length::Short,\n        }\n    }\n}\n\nimpl OptionType for DayPeriod {\n    fn from_value(value: JsValue, context: &mut Context) -> JsResult<Self> {\n        match value.to_string(context)?.to_std_string_escaped().as_ref() {\n            \"narrow\" => Ok(Self::Narrow),\n            \"short\" => Ok(Self::Short),\n            \"long\" => Ok(Self::Long),\n            _ => Err(js_error!(RangeError: \"unknown dayPeriod option\")),\n        }\n    }\n}\n\n#[derive(Debug, Clone, Copy)]\npub(crate) enum Hour {\n    TwoDigit,\n    Numeric,\n}\n\nimpl OptionType for Hour {\n    fn from_value(value: JsValue, context: &mut Context) -> JsResult<Self> {\n        match value.to_string(context)?.to_std_string_escaped().as_ref() {\n            \"2-digit\" => Ok(Self::TwoDigit),\n            \"numeric\" => Ok(Self::Numeric),\n            _ => Err(js_error!(RangeError: \"unknown hour option\")),\n        }\n    }\n}\n\n#[derive(Debug, Clone, Copy)]\npub(crate) enum Minute {\n    TwoDigit,\n    Numeric,\n}\n\nimpl OptionType for Minute {\n    fn from_value(value: JsValue, context: &mut Context) -> JsResult<Self> {\n        match value.to_string(context)?.to_std_string_escaped().as_ref() {\n            \"2-digit\" => Ok(Self::TwoDigit),\n            \"numeric\" => Ok(Self::Numeric),\n            _ => Err(js_error!(RangeError: \"unknown minute option\")),\n        }\n    }\n}\n\n#[derive(Debug, Clone, Copy)]\npub(crate) enum Second {\n    TwoDigit,\n    Numeric,\n}\n\nimpl OptionType for Second {\n    fn from_value(value: JsValue, context: &mut Context) -> JsResult<Self> {\n        match value.to_string(context)?.to_std_string_escaped().as_ref() {\n            \"2-digit\" => Ok(Self::TwoDigit),\n            \"numeric\" => Ok(Self::Numeric),\n            _ => Err(js_error!(RangeError: \"unknown second option\")),\n        }\n    }\n}\n\n#[derive(Debug, Clone, Copy)]\npub(crate) enum SubsecondDigits {\n    S1,\n    S2,\n    S3,\n}\n\nimpl SubsecondDigits {\n    fn from_i32(i: i32) -> SubsecondDigits {\n        match i {\n            1 => SubsecondDigits::S1,\n            2 => SubsecondDigits::S2,\n            3 => SubsecondDigits::S3,\n            _ => unreachable!(\"subSecondDigits must be previously constrained.\"),\n        }\n    }\n}\n\nimpl From<SubsecondDigits> for IcuSubsecondDigits {\n    fn from(value: SubsecondDigits) -> Self {\n        match value {\n            SubsecondDigits::S1 => IcuSubsecondDigits::S1,\n            SubsecondDigits::S2 => IcuSubsecondDigits::S2,\n            SubsecondDigits::S3 => IcuSubsecondDigits::S3,\n        }\n    }\n}\n\n#[derive(Debug, Clone, Copy)]\npub(crate) enum TimeZoneName {\n    Short,\n    Long,\n    ShortOffset,\n    LongOffset,\n    ShortGeneric,\n    LongGeneric,\n}\n\nimpl OptionType for TimeZoneName {\n    fn from_value(value: JsValue, context: &mut Context) -> JsResult<Self> {\n        match value.to_string(context)?.to_std_string_escaped().as_ref() {\n            \"short\" => Ok(Self::Short),\n            \"long\" => Ok(Self::Long),\n            \"shortOffset\" => Ok(Self::ShortOffset),\n            \"longOffset\" => Ok(Self::LongOffset),\n            \"shortGeneric\" => Ok(Self::ShortGeneric),\n            \"longGeneric\" => Ok(Self::LongGeneric),\n            _ => Err(js_error!(RangeError: \"unknown timeZoneName option\")),\n        }\n    }\n}\n\nimpl TimeZoneName {\n    fn to_zone_style(self) -> ZoneStyle {\n        match self {\n            TimeZoneName::LongGeneric => ZoneStyle::GenericLong,\n            TimeZoneName::ShortGeneric => ZoneStyle::GenericShort,\n            TimeZoneName::LongOffset => ZoneStyle::LocalizedOffsetLong,\n            TimeZoneName::ShortOffset => ZoneStyle::LocalizedOffsetShort,\n            TimeZoneName::Long => ZoneStyle::SpecificLong,\n            TimeZoneName::Short => ZoneStyle::SpecificShort,\n        }\n    }\n}\n\n// The below handles the [[RelevantExtensionKeys]] of DateTimeFormatters\n// internal slots.\n//\n// See https://tc39.es/ecma402/#sec-intl.datetimeformat-internal-slots\nimpl ServicePreferences for DateTimeFormatterPreferences {\n    fn validate(&mut self, id: &LanguageIdentifier, provider: &IntlProvider) {\n        // Handle LDML unicode key \"nu\", Numbering system\n        self.numbering_system = self.numbering_system.take().filter(|nu| {\n            let attr = DataMarkerAttributes::from_str_or_panic(nu.as_str());\n            validate_extension::<DecimalSymbolsV1>(id, attr, provider)\n        });\n\n        // Handle LDML unicode key \"ca\", Calendar algorithm\n        self.calendar_algorithm = self.calendar_algorithm.take().filter(|ca| match ca {\n            CalendarAlgorithm::Buddhist => has_calendar_data_for_locale::<Buddhist>(id, provider),\n            CalendarAlgorithm::Chinese => {\n                has_calendar_data_for_locale::<ChineseTraditional>(id, provider)\n            }\n            CalendarAlgorithm::Coptic => has_calendar_data_for_locale::<Coptic>(id, provider),\n            CalendarAlgorithm::Dangi => {\n                has_calendar_data_for_locale::<KoreanTraditional>(id, provider)\n            }\n            CalendarAlgorithm::Ethiopic => has_calendar_data_for_locale::<Ethiopian>(id, provider),\n            CalendarAlgorithm::Gregory => has_calendar_data_for_locale::<Gregorian>(id, provider),\n            CalendarAlgorithm::Hebrew => has_calendar_data_for_locale::<Hebrew>(id, provider),\n            CalendarAlgorithm::Indian => has_calendar_data_for_locale::<Indian>(id, provider),\n            CalendarAlgorithm::Japanese => has_calendar_data_for_locale::<Japanese>(id, provider),\n            CalendarAlgorithm::Persian => has_calendar_data_for_locale::<Persian>(id, provider),\n            CalendarAlgorithm::Roc => has_calendar_data_for_locale::<Roc>(id, provider),\n            CalendarAlgorithm::Hijri(Some(\n                HijriCalendarAlgorithm::Civil | HijriCalendarAlgorithm::Tbla,\n            )) => has_calendar_data_for_locale::<Hijri<hijri::TabularAlgorithm>>(id, provider),\n            CalendarAlgorithm::Hijri(Some(HijriCalendarAlgorithm::Umalqura)) => {\n                has_calendar_data_for_locale::<Hijri<hijri::UmmAlQura>>(id, provider)\n            }\n            CalendarAlgorithm::Hijri(Some(HijriCalendarAlgorithm::Rgsa) | None) => true,\n            _ => false,\n        });\n\n        // NOTE (nekevss): issue: this will not support `H24` as ICU4X does\n        // not currently support it.\n        //\n        // track: https://github.com/unicode-org/icu4x/issues/6597\n        // Handle LDML unicode key \"hc\", Hour cycle\n        // No need to validate hour_cycle since it only affects formatting\n        // behaviour.\n    }\n\n    impl_service_preferences!(numbering_system, calendar_algorithm, hour_cycle);\n}\n\nfn has_calendar_data_for_locale<C: CldrCalendar>(\n    id: &LanguageIdentifier,\n    provider: &IntlProvider,\n) -> bool\nwhere\n    IntlProvider: DryDataProvider<C::YearNamesV1>,\n{\n    use icu_datetime::provider::neo::marker_attrs;\n    use icu_provider::prelude::{\n        DataIdentifierBorrowed, DataRequest, DataRequestMetadata,\n        icu_locale_core::preferences::LocalePreferences,\n    };\n\n    let info = <C::YearNamesV1 as DataMarker>::INFO;\n    let locale = info.make_locale(LocalePreferences::from(id));\n    let req = DataRequest {\n        id: DataIdentifierBorrowed::for_marker_attributes_and_locale(marker_attrs::ABBR, &locale),\n        metadata: {\n            let mut md = DataRequestMetadata::default();\n            md.silent = true;\n            md\n        },\n    };\n\n    let Ok(md) = DryDataProvider::dry_load(provider, req) else {\n        return false;\n    };\n\n    md.locale.is_none_or(|loc| !loc.is_unknown())\n}\n"
  },
  {
    "path": "core/engine/src/builtins/intl/date_time_format/tests.rs",
    "content": "use indoc::indoc;\n\nuse crate::{TestAction, run_test_actions};\n\n// Intl.DateTimeFormat tests\n\n#[cfg(feature = \"intl_bundled\")]\n#[test]\nfn dtf_basic() {\n    run_test_actions([\n        TestAction::run(indoc! {\"\n            // Setup date\n            const date = new Date(Date.UTC(2020, 11, 20, 3, 23, 16, 738));\n\n            let formatter = new Intl.DateTimeFormat('en-US');\n            let result = formatter.format(date);\n        \"}),\n        TestAction::assert_eq(\"result === '12/20/20'\", true),\n    ]);\n    run_test_actions([\n        TestAction::run(indoc! {\"\n            // Setup date\n            const date = new Date(Date.UTC(2020, 11, 20, 3, 23, 16, 738));\n\n            let formatter = new Intl.DateTimeFormat('en-US', { dateStyle: 'full' });\n            let result = formatter.format(date);\n        \"}),\n        TestAction::assert_eq(\"result === 'Sunday, December 20, 2020'\", true),\n    ]);\n    run_test_actions([\n        TestAction::run(indoc! {\"\n            // Setup date\n            const date = new Date(Date.UTC(2020, 11, 20, 3, 23, 16, 738));\n\n            let formatter = new Intl.DateTimeFormat('en-GB');\n            let result = formatter.format(date);\n        \"}),\n        TestAction::assert_eq(\"result === '20/12/2020'\", true),\n    ]);\n    run_test_actions([\n        TestAction::run(indoc! {\"\n            // Setup date\n            const date = new Date(Date.UTC(2020, 11, 20, 3, 23, 16, 738));\n\n            let formatter = new Intl.DateTimeFormat('en-GB', {\n                dateStyle: 'full',\n                timeStyle: 'long',\n            });\n            let result = formatter.format(date);\n        \"}),\n        TestAction::assert_eq(\"result === 'Sunday, 20 December 2020 at 03:23:16'\", true),\n    ]);\n    run_test_actions([\n        TestAction::run(indoc! {\"\n            // Setup date\n            const date = new Date(Date.UTC(2020, 11, 20, 3, 23, 16, 738));\n\n            let formatter = new Intl.DateTimeFormat('en-GB', {\n                dateStyle: 'full',\n                timeStyle: 'long',\n                timeZone: 'Australia/Sydney',\n            });\n            let result = formatter.format(date);\n        \"}),\n        TestAction::assert_eq(\"result === 'Sunday, 20 December 2020 at 14:23:16'\", true),\n    ]);\n}\n"
  },
  {
    "path": "core/engine/src/builtins/intl/list_format/mod.rs",
    "content": "use std::fmt::Write;\n\nuse boa_gc::{Finalize, Trace};\nuse icu_list::{\n    ListFormatter, ListFormatterPreferences,\n    options::{ListFormatterOptions, ListLength},\n    provider::{ListAndV1, ListFormatterPatterns},\n};\nuse icu_locale::Locale;\n\nuse crate::{\n    Context, JsArgs, JsData, JsExpect, JsNativeError, JsResult, JsString, JsValue,\n    builtins::{\n        Array, BuiltInBuilder, BuiltInConstructor, BuiltInObject, IntrinsicObject, OrdinaryObject,\n        intl::options::EmptyPreferences,\n        iterable::IteratorHint,\n        options::{get_option, get_options_object},\n    },\n    context::intrinsics::{Intrinsics, StandardConstructor, StandardConstructors},\n    js_string,\n    object::{JsObject, internal_methods::get_prototype_from_constructor},\n    property::Attribute,\n    realm::Realm,\n    string::StaticJsStrings,\n    symbol::JsSymbol,\n};\n\nuse super::{\n    Service,\n    locale::{canonicalize_locale_list, filter_locales, resolve_locale},\n    options::IntlOptions,\n};\n\nmod options;\npub(crate) use options::*;\n\n#[derive(Debug, Trace, Finalize, JsData)]\n// Safety: `ListFormat` only contains non-traceable types.\n#[boa_gc(unsafe_empty_trace)]\npub(crate) struct ListFormat {\n    locale: Locale,\n    typ: ListFormatType,\n    style: ListLength,\n    native: ListFormatter,\n}\n\nimpl Service for ListFormat {\n    type LangMarker = ListAndV1;\n\n    const ATTRIBUTES: &'static icu_provider::DataMarkerAttributes = ListFormatterPatterns::WIDE;\n\n    type Preferences = EmptyPreferences;\n}\n\nimpl IntrinsicObject for ListFormat {\n    fn init(realm: &Realm) {\n        BuiltInBuilder::from_standard_constructor::<Self>(realm)\n            .static_method(\n                Self::supported_locales_of,\n                js_string!(\"supportedLocalesOf\"),\n                1,\n            )\n            .property(\n                JsSymbol::to_string_tag(),\n                js_string!(\"Intl.ListFormat\"),\n                Attribute::CONFIGURABLE,\n            )\n            .method(Self::format, js_string!(\"format\"), 1)\n            .method(Self::format_to_parts, js_string!(\"formatToParts\"), 1)\n            .method(Self::resolved_options, js_string!(\"resolvedOptions\"), 0)\n            .build();\n    }\n\n    fn get(intrinsics: &Intrinsics) -> JsObject {\n        Self::STANDARD_CONSTRUCTOR(intrinsics.constructors()).constructor()\n    }\n}\n\nimpl BuiltInObject for ListFormat {\n    const NAME: JsString = StaticJsStrings::LIST_FORMAT;\n}\n\nimpl BuiltInConstructor for ListFormat {\n    const CONSTRUCTOR_ARGUMENTS: usize = 0;\n    const PROTOTYPE_STORAGE_SLOTS: usize = 4;\n    const CONSTRUCTOR_STORAGE_SLOTS: usize = 1;\n\n    const STANDARD_CONSTRUCTOR: fn(&StandardConstructors) -> &StandardConstructor =\n        StandardConstructors::list_format;\n\n    /// Constructor [`Intl.ListFormat ( [ locales [ , options ] ] )`][spec].\n    ///\n    /// Constructor for `ListFormat` objects.\n    ///\n    /// More information:\n    ///  - [MDN documentation][mdn]\n    ///\n    /// [spec]: https://tc39.es/ecma402/#sec-Intl.ListFormat\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/ListFormat\n    fn constructor(\n        new_target: &JsValue,\n        args: &[JsValue],\n        context: &mut Context,\n    ) -> JsResult<JsValue> {\n        // 1. If NewTarget is undefined, throw a TypeError exception.\n        if new_target.is_undefined() {\n            return Err(JsNativeError::typ()\n                .with_message(\"cannot call `Intl.ListFormat` constructor without `new`\")\n                .into());\n        }\n\n        let locales = args.get_or_undefined(0);\n        let options = args.get_or_undefined(1);\n\n        // 3. Let requestedLocales be ? CanonicalizeLocaleList(locales).\n        let requested_locales = canonicalize_locale_list(locales, context)?;\n\n        // 4. Set options to ? GetOptionsObject(options).\n        let options = get_options_object(options)?;\n\n        // 5. Let opt be a new Record.\n        // 6. Let matcher be ? GetOption(options, \"localeMatcher\", string, « \"lookup\", \"best fit\" », \"best fit\").\n        let matcher =\n            get_option(&options, js_string!(\"localeMatcher\"), context)?.unwrap_or_default();\n\n        // 7. Set opt.[[localeMatcher]] to matcher.\n        // 8. Let localeData be %ListFormat%.[[LocaleData]].\n        // 9. Let r be ResolveLocale(%ListFormat%.[[AvailableLocales]], requestedLocales, opt, %ListFormat%.[[RelevantExtensionKeys]], localeData).\n        // 10. Set listFormat.[[Locale]] to r.[[locale]].\n        let locale = resolve_locale::<Self>(\n            requested_locales,\n            &mut IntlOptions {\n                matcher,\n                ..Default::default()\n            },\n            context.intl_provider(),\n        )?;\n\n        // 11. Let type be ? GetOption(options, \"type\", string, « \"conjunction\", \"disjunction\", \"unit\" », \"conjunction\").\n        // 12. Set listFormat.[[Type]] to type.\n        let typ = get_option(&options, js_string!(\"type\"), context)?.unwrap_or_default();\n\n        // 13. Let style be ? GetOption(options, \"style\", string, « \"long\", \"short\", \"narrow\" », \"long\").\n        // 14. Set listFormat.[[Style]] to style.\n        let style = get_option(&options, js_string!(\"style\"), context)?.unwrap_or(ListLength::Wide);\n\n        // 15. Let dataLocale be r.[[dataLocale]].\n        // 16. Let dataLocaleData be localeData.[[<dataLocale>]].\n        // 17. Let dataLocaleTypes be dataLocaleData.[[<type>]].\n        // 18. Set listFormat.[[Templates]] to dataLocaleTypes.[[<style>]].\n        let prefs = ListFormatterPreferences::from(&locale);\n        let options = ListFormatterOptions::default().with_length(style);\n        let formatter = match typ {\n            ListFormatType::Conjunction => ListFormatter::try_new_and_with_buffer_provider(\n                context.intl_provider().erased_provider(),\n                prefs,\n                options,\n            ),\n            ListFormatType::Disjunction => ListFormatter::try_new_or_with_buffer_provider(\n                context.intl_provider().erased_provider(),\n                prefs,\n                options,\n            ),\n            ListFormatType::Unit => ListFormatter::try_new_unit_with_buffer_provider(\n                context.intl_provider().erased_provider(),\n                prefs,\n                options,\n            ),\n        }\n        .map_err(|e| JsNativeError::typ().with_message(e.to_string()))?;\n\n        // 2. Let listFormat be ? OrdinaryCreateFromConstructor(NewTarget, \"%ListFormat.prototype%\", « [[InitializedListFormat]], [[Locale]], [[Type]], [[Style]], [[Templates]] »).\n        let prototype =\n            get_prototype_from_constructor(new_target, StandardConstructors::list_format, context)?;\n        let list_format = JsObject::from_proto_and_data_with_shared_shape(\n            context.root_shape(),\n            prototype,\n            Self {\n                locale,\n                typ,\n                style,\n                native: formatter,\n            },\n        );\n\n        // 19. Return listFormat.\n        Ok(list_format.into())\n    }\n}\n\nimpl ListFormat {\n    /// [`Intl.ListFormat.supportedLocalesOf ( locales [ , options ] )`][spec].\n    ///\n    /// Returns an array containing those of the provided locales that are supported in list\n    /// formatting without having to fall back to the runtime's default locale.\n    ///\n    /// More information:\n    ///  - [MDN documentation][mdn]\n    ///\n    /// [spec]: https://tc39.es/ecma402/#sec-Intl.ListFormat.supportedLocalesOf\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/ListFormat/supportedLocalesOf\n    fn supported_locales_of(\n        _: &JsValue,\n        args: &[JsValue],\n        context: &mut Context,\n    ) -> JsResult<JsValue> {\n        let locales = args.get_or_undefined(0);\n        let options = args.get_or_undefined(1);\n\n        // 1. Let availableLocales be %ListFormat%.[[AvailableLocales]].\n        // 2. Let requestedLocales be ? CanonicalizeLocaleList(locales).\n        let requested_locales = canonicalize_locale_list(locales, context)?;\n\n        // 3. Return ? FilterLocales(availableLocales, requestedLocales, options).\n        filter_locales::<Self>(requested_locales, options, context).map(JsValue::from)\n    }\n\n    /// [`Intl.ListFormat.prototype.format ( list )`][spec].\n    ///\n    /// Returns a language-specific formatted string representing the elements of the list.\n    ///\n    /// More information:\n    ///  - [MDN documentation][mdn]\n    ///\n    /// [spec]: https://tc39.es/ecma402/#sec-Intl.ListFormat.prototype.format\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/ListFormat/format\n    fn format(this: &JsValue, args: &[JsValue], context: &mut Context) -> JsResult<JsValue> {\n        // 1. Let lf be the this value.\n        // 2. Perform ? RequireInternalSlot(lf, [[InitializedListFormat]]).\n        let object = this.as_object();\n        let lf = object\n            .as_ref()\n            .and_then(|o| o.downcast_ref::<Self>())\n            .ok_or_else(|| {\n                JsNativeError::typ()\n                    .with_message(\"`format` can only be called on a `ListFormat` object\")\n            })?;\n\n        // 3. Let stringList be ? StringListFromIterable(list).\n        // TODO: support for UTF-16 unpaired surrogates formatting\n        let strings = string_list_from_iterable(args.get_or_undefined(0), context)?;\n\n        let formatted = lf\n            .native\n            .format_to_string(strings.into_iter().map(|s| s.to_std_string_escaped()));\n\n        // 4. Return ! FormatList(lf, stringList).\n        Ok(js_string!(formatted).into())\n    }\n\n    /// [`Intl.ListFormat.prototype.formatToParts ( list )`][spec].\n    ///\n    /// Returns a language-specific formatted string representing the elements of the list.\n    ///\n    /// More information:\n    ///  - [MDN documentation][mdn]\n    ///\n    /// [spec]: https://tc39.es/ecma402/#sec-Intl.ListFormat.prototype.formatToParts\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/ListFormat/formatToParts\n    fn format_to_parts(\n        this: &JsValue,\n        args: &[JsValue],\n        context: &mut Context,\n    ) -> JsResult<JsValue> {\n        // TODO: maybe try to move this into icu4x?\n        use writeable::{PartsWrite, Writeable};\n\n        #[derive(Debug, Clone)]\n        enum Part {\n            Literal(String),\n            Element(String),\n        }\n\n        impl Part {\n            const fn typ(&self) -> &'static str {\n                match self {\n                    Self::Literal(_) => \"literal\",\n                    Self::Element(_) => \"element\",\n                }\n            }\n\n            #[allow(clippy::missing_const_for_fn)]\n            fn value(self) -> String {\n                match self {\n                    Self::Literal(s) | Self::Element(s) => s,\n                }\n            }\n        }\n\n        #[derive(Debug, Clone)]\n        struct WriteString(String);\n\n        impl Write for WriteString {\n            fn write_str(&mut self, s: &str) -> std::fmt::Result {\n                self.0.write_str(s)\n            }\n\n            fn write_char(&mut self, c: char) -> std::fmt::Result {\n                self.0.write_char(c)\n            }\n        }\n\n        impl PartsWrite for WriteString {\n            type SubPartsWrite = Self;\n\n            fn with_part(\n                &mut self,\n                _part: writeable::Part,\n                mut f: impl FnMut(&mut Self::SubPartsWrite) -> std::fmt::Result,\n            ) -> std::fmt::Result {\n                f(self)\n            }\n        }\n\n        #[derive(Debug, Clone)]\n        struct PartsCollector(Vec<Part>);\n\n        impl Write for PartsCollector {\n            fn write_str(&mut self, _: &str) -> std::fmt::Result {\n                Ok(())\n            }\n        }\n\n        impl PartsWrite for PartsCollector {\n            type SubPartsWrite = WriteString;\n\n            fn with_part(\n                &mut self,\n                part: writeable::Part,\n                mut f: impl FnMut(&mut Self::SubPartsWrite) -> core::fmt::Result,\n            ) -> core::fmt::Result {\n                assert!(part.category == \"list\");\n                let mut string = WriteString(String::new());\n                f(&mut string)?;\n                if !string.0.is_empty() {\n                    match part.value {\n                        \"element\" => self.0.push(Part::Element(string.0)),\n                        \"literal\" => self.0.push(Part::Literal(string.0)),\n                        _ => unreachable!(),\n                    }\n                }\n                Ok(())\n            }\n        }\n\n        // 1. Let lf be the this value.\n        // 2. Perform ? RequireInternalSlot(lf, [[InitializedListFormat]]).\n        let object = this.as_object();\n        let lf = object\n            .as_ref()\n            .and_then(|o| o.downcast_ref::<Self>())\n            .ok_or_else(|| {\n                JsNativeError::typ()\n                    .with_message(\"`formatToParts` can only be called on a `ListFormat` object\")\n            })?;\n\n        // 3. Let stringList be ? StringListFromIterable(list).\n        // TODO: support for UTF-16 unpaired surrogates formatting\n        let strings = string_list_from_iterable(args.get_or_undefined(0), context)?\n            .into_iter()\n            .map(|s| s.to_std_string_escaped());\n\n        // 4. Return ! FormatListToParts(lf, stringList).\n\n        // Abstract operation `FormatListToParts ( listFormat, list )`\n        // https://tc39.es/ecma402/#sec-formatlisttoparts\n\n        // 1. Let parts be ! CreatePartsFromList(listFormat, list).\n        let mut parts = PartsCollector(Vec::new());\n        lf.native\n            .format(strings)\n            .write_to_parts(&mut parts)\n            .map_err(|e| JsNativeError::typ().with_message(e.to_string()))?;\n\n        // 2. Let result be ! ArrayCreate(0).\n        let result = Array::array_create(0, None, context)\n            .js_expect(\"creating an empty array with default proto must not fail\")?;\n\n        // 3. Let n be 0.\n        // 4. For each Record { [[Type]], [[Value]] } part in parts, do\n        for (n, part) in parts.0.into_iter().enumerate() {\n            // a. Let O be OrdinaryObjectCreate(%Object.prototype%).\n            let o = context\n                .intrinsics()\n                .templates()\n                .ordinary_object()\n                .create(OrdinaryObject, vec![]);\n\n            // b. Perform ! CreateDataPropertyOrThrow(O, \"type\", part.[[Type]]).\n            o.create_data_property_or_throw(js_string!(\"type\"), js_string!(part.typ()), context)\n                .js_expect(\"operation must not fail per the spec\")?;\n\n            // c. Perform ! CreateDataPropertyOrThrow(O, \"value\", part.[[Value]]).\n            o.create_data_property_or_throw(js_string!(\"value\"), js_string!(part.value()), context)\n                .js_expect(\"operation must not fail per the spec\")?;\n\n            // d. Perform ! CreateDataPropertyOrThrow(result, ! ToString(n), O).\n            result\n                .create_data_property_or_throw(n, o, context)\n                .js_expect(\"operation must not fail per the spec\")?;\n\n            // e. Increment n by 1.\n        }\n\n        // 5. Return result.\n        Ok(result.into())\n    }\n\n    /// [`Intl.ListFormat.prototype.resolvedOptions ( )`][spec].\n    ///\n    /// Returns a new object with properties reflecting the locale and style formatting options\n    /// computed during the construction of the current `Intl.ListFormat` object.\n    ///\n    /// More information:\n    ///  - [MDN documentation][mdn]\n    ///\n    /// [spec]: https://tc39.es/ecma402/#sec-Intl.ListFormat.prototype.resolvedoptions\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/ListFormat/resolvedOptions\n    fn resolved_options(this: &JsValue, _: &[JsValue], context: &mut Context) -> JsResult<JsValue> {\n        // 1. Let lf be the this value.\n        // 2. Perform ? RequireInternalSlot(lf, [[InitializedListFormat]]).\n        let object = this.as_object();\n        let lf = object\n            .as_ref()\n            .and_then(|o| o.downcast_ref::<Self>())\n            .ok_or_else(|| {\n                JsNativeError::typ()\n                    .with_message(\"`resolvedOptions` can only be called on a `ListFormat` object\")\n            })?;\n\n        // 3. Let options be OrdinaryObjectCreate(%Object.prototype%).\n        let options = context\n            .intrinsics()\n            .templates()\n            .ordinary_object()\n            .create(OrdinaryObject, vec![]);\n\n        // 4. For each row of Table 11, except the header row, in table order, do\n        //     a. Let p be the Property value of the current row.\n        //     b. Let v be the value of lf's internal slot whose name is the Internal Slot value of the current row.\n        //     c. Assert: v is not undefined.\n        //     d. Perform ! CreateDataPropertyOrThrow(options, p, v).\n        options\n            .create_data_property_or_throw(\n                js_string!(\"locale\"),\n                js_string!(lf.locale.to_string()),\n                context,\n            )\n            .js_expect(\"operation must not fail per the spec\")?;\n        options\n            .create_data_property_or_throw(\n                js_string!(\"type\"),\n                match lf.typ {\n                    ListFormatType::Conjunction => js_string!(\"conjunction\"),\n                    ListFormatType::Disjunction => js_string!(\"disjunction\"),\n                    ListFormatType::Unit => js_string!(\"unit\"),\n                },\n                context,\n            )\n            .js_expect(\"operation must not fail per the spec\")?;\n        options\n            .create_data_property_or_throw(\n                js_string!(\"style\"),\n                match lf.style {\n                    ListLength::Wide => js_string!(\"long\"),\n                    ListLength::Short => js_string!(\"short\"),\n                    ListLength::Narrow => js_string!(\"narrow\"),\n                    _ => unreachable!(),\n                },\n                context,\n            )\n            .js_expect(\"operation must not fail per the spec\")?;\n\n        // 5. Return options.\n        Ok(options.into())\n    }\n}\n\n/// Abstract operation [`StringListFromIterable ( iterable )`][spec]\n///\n/// [spec]: https://tc39.es/ecma402/#sec-createstringlistfromiterable\nfn string_list_from_iterable(iterable: &JsValue, context: &mut Context) -> JsResult<Vec<JsString>> {\n    // 1. If iterable is undefined, then\n    if iterable.is_undefined() {\n        //     a. Return a new empty List.\n        return Ok(Vec::new());\n    }\n\n    // 2. Let iteratorRecord be ? GetIterator(iterable, sync).\n    let mut iterator = iterable.get_iterator(IteratorHint::Sync, context)?;\n\n    // 3. Let list be a new empty List.\n    let mut list = Vec::new();\n\n    // 4. Let next be true.\n    // 5. Repeat, while next is not false,\n    //     a. Let next be ? IteratorStepValue(iteratorRecord).\n    while let Some(next) = iterator.step_value(context)? {\n        // c. If next is not a String, then\n        let Some(s) = next.as_string() else {\n            // i. Let error be ThrowCompletion(a newly created TypeError object).\n            // ii. Return ? IteratorClose(iteratorRecord, error).\n            return Err(iterator\n                .close(\n                    Err(JsNativeError::typ()\n                        .with_message(\"StringListFromIterable: can only format strings into a list\")\n                        .into()),\n                    context,\n                )\n                .expect_err(\"`close` should return the provided error\"));\n        };\n\n        // d. Append next to list.\n        list.push(s);\n    }\n\n    //     b. If next is done, then\n    //         i. Return list.\n    Ok(list)\n}\n"
  },
  {
    "path": "core/engine/src/builtins/intl/list_format/options.rs",
    "content": "use std::str::FromStr;\n\nuse icu_list::options::ListLength;\n\nuse crate::{\n    Context, JsNativeError, JsResult, JsValue,\n    builtins::options::{OptionType, ParsableOptionType},\n};\n\n#[derive(Debug, Clone, Copy, Default)]\npub(crate) enum ListFormatType {\n    #[default]\n    Conjunction,\n    Disjunction,\n    Unit,\n}\n\n#[derive(Debug)]\npub(crate) struct ParseListFormatTypeError;\n\nimpl std::fmt::Display for ParseListFormatTypeError {\n    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {\n        f.write_str(\"provided string was not `conjunction`, `disjunction` or `unit`\")\n    }\n}\n\nimpl FromStr for ListFormatType {\n    type Err = ParseListFormatTypeError;\n\n    fn from_str(s: &str) -> Result<Self, Self::Err> {\n        match s {\n            \"conjunction\" => Ok(Self::Conjunction),\n            \"disjunction\" => Ok(Self::Disjunction),\n            \"unit\" => Ok(Self::Unit),\n            _ => Err(ParseListFormatTypeError),\n        }\n    }\n}\n\nimpl ParsableOptionType for ListFormatType {}\n\nimpl OptionType for ListLength {\n    fn from_value(value: JsValue, context: &mut Context) -> JsResult<Self> {\n        match value.to_string(context)?.to_std_string_escaped().as_str() {\n            \"long\" => Ok(Self::Wide),\n            \"short\" => Ok(Self::Short),\n            \"narrow\" => Ok(Self::Narrow),\n            _ => Err(JsNativeError::range()\n                .with_message(\"provided string was not `long`, `short` or `narrow`\")\n                .into()),\n        }\n    }\n}\n"
  },
  {
    "path": "core/engine/src/builtins/intl/locale/mod.rs",
    "content": "use crate::{builtins::options::get_option, realm::Realm, string::StaticJsStrings};\nuse icu_locale::{\n    extensions::unicode::Value,\n    extensions_unicode_key as key, extensions_unicode_value as value,\n    preferences::extensions::unicode::keywords::{CollationCaseFirst, HourCycle},\n};\n\n#[cfg(all(test, feature = \"intl_bundled\"))]\nmod tests;\n\nmod utils;\npub(crate) use utils::*;\n\nmod options;\n\nuse crate::{\n    Context, JsArgs, JsNativeError, JsResult, JsString, JsValue,\n    builtins::{BuiltInBuilder, BuiltInConstructor, BuiltInObject, IntrinsicObject},\n    context::intrinsics::{Intrinsics, StandardConstructor, StandardConstructors},\n    js_string,\n    object::{JsObject, internal_methods::get_prototype_from_constructor},\n    property::Attribute,\n    symbol::JsSymbol,\n};\n\nuse super::options::coerce_options_to_object;\n\n#[derive(Debug, Clone)]\npub(crate) struct Locale;\n\nimpl IntrinsicObject for Locale {\n    fn init(realm: &Realm) {\n        let base_name = BuiltInBuilder::callable(realm, Self::base_name)\n            .name(js_string!(\"get baseName\"))\n            .build();\n\n        let calendar = BuiltInBuilder::callable(realm, Self::calendar)\n            .name(js_string!(\"get calendar\"))\n            .build();\n\n        let case_first = BuiltInBuilder::callable(realm, Self::case_first)\n            .name(js_string!(\"get caseFirst\"))\n            .build();\n\n        let collation = BuiltInBuilder::callable(realm, Self::collation)\n            .name(js_string!(\"get collation\"))\n            .build();\n\n        let hour_cycle = BuiltInBuilder::callable(realm, Self::hour_cycle)\n            .name(js_string!(\"get hourCycle\"))\n            .build();\n\n        let numeric = BuiltInBuilder::callable(realm, Self::numeric)\n            .name(js_string!(\"get numeric\"))\n            .build();\n\n        let numbering_system = BuiltInBuilder::callable(realm, Self::numbering_system)\n            .name(js_string!(\"get numberingSystem\"))\n            .build();\n\n        let language = BuiltInBuilder::callable(realm, Self::language)\n            .name(js_string!(\"get language\"))\n            .build();\n\n        let script = BuiltInBuilder::callable(realm, Self::script)\n            .name(js_string!(\"get script\"))\n            .build();\n\n        let region = BuiltInBuilder::callable(realm, Self::region)\n            .name(js_string!(\"get region\"))\n            .build();\n\n        let variants = BuiltInBuilder::callable(realm, Self::variants)\n            .name(js_string!(\"get variants\"))\n            .build();\n\n        BuiltInBuilder::from_standard_constructor::<Self>(realm)\n            .property(\n                JsSymbol::to_string_tag(),\n                js_string!(\"Intl.Locale\"),\n                Attribute::CONFIGURABLE,\n            )\n            .method(Self::maximize, js_string!(\"maximize\"), 0)\n            .method(Self::minimize, js_string!(\"minimize\"), 0)\n            .method(Self::to_string, js_string!(\"toString\"), 0)\n            .accessor(\n                js_string!(\"baseName\"),\n                Some(base_name),\n                None,\n                Attribute::CONFIGURABLE,\n            )\n            .accessor(\n                js_string!(\"calendar\"),\n                Some(calendar),\n                None,\n                Attribute::CONFIGURABLE,\n            )\n            .accessor(\n                js_string!(\"caseFirst\"),\n                Some(case_first),\n                None,\n                Attribute::CONFIGURABLE,\n            )\n            .accessor(\n                js_string!(\"collation\"),\n                Some(collation),\n                None,\n                Attribute::CONFIGURABLE,\n            )\n            .accessor(\n                js_string!(\"hourCycle\"),\n                Some(hour_cycle),\n                None,\n                Attribute::CONFIGURABLE,\n            )\n            .accessor(\n                js_string!(\"numeric\"),\n                Some(numeric),\n                None,\n                Attribute::CONFIGURABLE,\n            )\n            .accessor(\n                js_string!(\"numberingSystem\"),\n                Some(numbering_system),\n                None,\n                Attribute::CONFIGURABLE,\n            )\n            .accessor(\n                js_string!(\"language\"),\n                Some(language),\n                None,\n                Attribute::CONFIGURABLE,\n            )\n            .accessor(\n                js_string!(\"script\"),\n                Some(script),\n                None,\n                Attribute::CONFIGURABLE,\n            )\n            .accessor(\n                js_string!(\"region\"),\n                Some(region),\n                None,\n                Attribute::CONFIGURABLE,\n            )\n            .accessor(\n                js_string!(\"variants\"),\n                Some(variants),\n                None,\n                Attribute::CONFIGURABLE,\n            )\n            .build();\n    }\n\n    fn get(intrinsics: &Intrinsics) -> JsObject {\n        Self::STANDARD_CONSTRUCTOR(intrinsics.constructors()).constructor()\n    }\n}\n\nimpl BuiltInObject for Locale {\n    const NAME: JsString = StaticJsStrings::LOCALE;\n}\n\nimpl BuiltInConstructor for Locale {\n    const CONSTRUCTOR_ARGUMENTS: usize = 1;\n    const PROTOTYPE_STORAGE_SLOTS: usize = 26;\n    const CONSTRUCTOR_STORAGE_SLOTS: usize = 0;\n\n    const STANDARD_CONSTRUCTOR: fn(&StandardConstructors) -> &StandardConstructor =\n        StandardConstructors::locale;\n\n    /// Constructor [`Intl.Locale ( tag [ , options ] )`][spec].\n    ///\n    /// Constructor for `Locale` objects.\n    ///\n    /// More information:\n    ///  - [MDN documentation][mdn]\n    ///\n    /// [spec]: https://tc39.es/ecma402/#sec-Intl.Locale\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/Locale\n    fn constructor(\n        new_target: &JsValue,\n        args: &[JsValue],\n        context: &mut Context,\n    ) -> JsResult<JsValue> {\n        // 1. If NewTarget is undefined, throw a TypeError exception.\n        if new_target.is_undefined() {\n            return Err(JsNativeError::typ()\n                .with_message(\"cannot call `Intl.Locale` constructor without `new`\")\n                .into());\n        }\n\n        let tag = args.get_or_undefined(0);\n        let options = args.get_or_undefined(1);\n\n        // 2. Let localeExtensionKeys be %Locale%.[[LocaleExtensionKeys]].\n        // 3. Let internalSlotsList be « [[InitializedLocale]], [[Locale]], [[Calendar]], [[Collation]], [[HourCycle]], [[NumberingSystem]] ».\n        // 4. If relevantExtensionKeys contains \"kf\", then\n        //     a. Append [[CaseFirst]] as the last element of internalSlotsList.\n        // 5. If relevantExtensionKeys contains \"kn\", then\n        //     a. Append [[Numeric]] as the last element of internalSlotsList.\n        // 6. Let locale be ? OrdinaryCreateFromConstructor(NewTarget, \"%Locale.prototype%\", internalSlotsList).\n        let prototype =\n            get_prototype_from_constructor(new_target, StandardConstructors::locale, context)?;\n\n        // 7. If Type(tag) is not String or Object, throw a TypeError exception.\n        let mut tag = locale_from_value(tag, context)?;\n\n        // 10. Set options to ? CoerceOptionsToObject(options).\n        let options = &coerce_options_to_object(options, context)?;\n\n        // 11. Set tag to ? UpdateLanguageId(tag, options).\n\n        // Abstract operation [`UpdateLanguageId ( tag, options )`][https://tc39.es/ecma402/#sec-updatelanguageid]\n        {\n            // 1. Let baseName be GetLocaleBaseName(tag).\n            // 2. Let language be ? GetOption(options, \"language\", string, empty, GetLocaleLanguage(baseName)).\n            // 3. If language cannot be matched by the unicode_language_subtag Unicode locale nonterminal, throw a RangeError exception.\n            let language = get_option(options, js_string!(\"language\"), context)?;\n\n            // 4. Let script be ? GetOption(options, \"script\", string, empty, GetLocaleScript(baseName)).\n            // 5. If script is not undefined, then\n            //        a. If script cannot be matched by the unicode_script_subtag Unicode locale nonterminal, throw a RangeError exception.\n            let script = get_option(options, js_string!(\"script\"), context)?;\n\n            // 6. Let region be ? GetOption(options, \"region\", string, empty, GetLocaleRegion(baseName)).\n            // 7. If region is not undefined, then\n            //        a. If region cannot be matched by the unicode_region_subtag Unicode locale nonterminal, throw a RangeError exception.\n            let region = get_option(options, js_string!(\"region\"), context)?;\n\n            // 8. Let variants be ? GetOption(options, \"variants\", string, empty, GetLocaleVariants(baseName)).\n            // 9. If variants is not undefined, then\n            let variants = get_option(options, js_string!(\"variants\"), context)?;\n\n            // 10. Let allExtensions be the suffix of tag following baseName.\n            // 11. Let newTag be language.\n            if let Some(language) = language {\n                tag.id.language = language;\n            }\n            // 12. If script is not undefined, set newTag to the string-concatenation of newTag, \"-\", and script.\n            if let Some(script) = script {\n                tag.id.script = Some(script);\n            }\n            // 13. If region is not undefined, set newTag to the string-concatenation of newTag, \"-\", and region.\n            if let Some(region) = region {\n                tag.id.region = Some(region);\n            }\n            // 14. If variants is not undefined, set newTag to the string-concatenation of newTag, \"-\", and variants.\n            if let Some(variants) = variants {\n                tag.id.variants = variants;\n            }\n            // 15. Set newTag to the string-concatenation of newTag and allExtensions.\n            // 16. Return newTag.\n        }\n\n        // 14. Let opt be a new Record.\n        // 15. Let calendar be ? GetOption(options, \"calendar\", string, empty, undefined).\n        // 16. If calendar is not undefined, then\n        //     a. If calendar cannot be matched by the type Unicode locale nonterminal, throw a RangeError exception.\n        // 17. Set opt.[[ca]] to calendar.\n        let ca = get_option(options, js_string!(\"calendar\"), context)?;\n\n        // 18. Let collation be ? GetOption(options, \"collation\", string, empty, undefined).\n        // 19. If collation is not undefined, then\n        //     a. If collation does not match the Unicode Locale Identifier type nonterminal, throw a RangeError exception.\n        // 20. Set opt.[[co]] to collation.\n        let co = get_option(options, js_string!(\"collation\"), context)?;\n\n        // 21. Let hc be ? GetOption(options, \"hourCycle\", string, « \"h11\", \"h12\", \"h23\", \"h24\" », undefined).\n        // 22. Set opt.[[hc]] to hc.\n        let hc = get_option::<HourCycle>(options, js_string!(\"hourCycle\"), context)?;\n\n        // 23. Let kf be ? GetOption(options, \"caseFirst\", string, « \"upper\", \"lower\", \"false\" », undefined).\n        // 24. Set opt.[[kf]] to kf.\n        let kf = get_option::<CollationCaseFirst>(options, js_string!(\"caseFirst\"), context)?;\n\n        // 25. Let kn be ? GetOption(options, \"numeric\", boolean, empty, undefined).\n        // 26. If kn is not undefined, set kn to ! ToString(kn).\n        // 27. Set opt.[[kn]] to kn.\n        let kn = get_option::<bool>(options, js_string!(\"numeric\"), context)?;\n\n        // 28. Let numberingSystem be ? GetOption(options, \"numberingSystem\", string, empty, undefined).\n        // 29. If numberingSystem is not undefined, then\n        //     a. If numberingSystem does not match the Unicode Locale Identifier type nonterminal, throw a RangeError exception.\n        // 30. Set opt.[[nu]] to numberingSystem.\n        let nu = get_option(options, js_string!(\"numberingSystem\"), context)?;\n\n        // 31. Let r be MakeLocaleRecord(tag, opt, localeExtensionKeys).\n        // 32. Set locale.[[Locale]] to r.[[locale]].\n        if let Some(ca) = ca {\n            // 33. Set locale.[[Calendar]] to r.[[ca]].\n            tag.extensions.unicode.keywords.set(key!(\"ca\"), ca);\n        }\n        if let Some(co) = co {\n            // 34. Set locale.[[Collation]] to r.[[co]].\n            tag.extensions.unicode.keywords.set(key!(\"co\"), co);\n        }\n        if let Some(hc) = hc {\n            // 35. Set locale.[[HourCycle]] to r.[[hc]].\n            tag.extensions.unicode.keywords.set(key!(\"hc\"), hc.into());\n        }\n        if let Some(kf) = kf {\n            // 36. If localeExtensionKeys contains \"kf\", then\n            //     a. Set locale.[[CaseFirst]] to r.[[kf]].\n            tag.extensions.unicode.keywords.set(key!(\"kf\"), kf.into());\n        }\n        if let Some(kn) = kn {\n            // 37. If localeExtensionKeys contains \"kn\", then\n            //     a. If SameValue(r.[[kn]], \"true\") is true or r.[[kn]] is the empty String, then\n            //         i. Set locale.[[Numeric]] to true.\n            //     b. Else,\n            //         i. Set locale.[[Numeric]] to false.\n            tag.extensions.unicode.keywords.set(\n                key!(\"kn\"),\n                if kn { value!(\"true\") } else { value!(\"false\") },\n            );\n        }\n        if let Some(nu) = nu {\n            // 38. Set locale.[[NumberingSystem]] to r.[[nu]].\n            tag.extensions.unicode.keywords.set(key!(\"nu\"), nu);\n        }\n\n        context\n            .intl_provider()\n            .locale_canonicalizer()?\n            .canonicalize(&mut tag);\n\n        let locale =\n            JsObject::from_proto_and_data_with_shared_shape(context.root_shape(), prototype, tag);\n\n        // 39. Return locale.\n        Ok(locale.into())\n    }\n}\n\nimpl Locale {\n    /// [`Intl.Locale.prototype.maximize ( )`][spec].\n    ///\n    /// More information:\n    ///  - [MDN documentation][mdn]\n    ///\n    /// [spec]: https://tc39.es/ecma402/#sec-Intl.Locale.prototype.maximize\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/Locale/maximize\n    pub(crate) fn maximize(\n        this: &JsValue,\n        _: &[JsValue],\n        context: &mut Context,\n    ) -> JsResult<JsValue> {\n        // 1. Let loc be the this value.\n        // 2. Perform ? RequireInternalSlot(loc, [[InitializedLocale]]).\n        let object = this.as_object();\n        let mut loc = object\n            .as_ref()\n            .and_then(|o| o.downcast_ref::<icu_locale::Locale>())\n            .ok_or_else(|| {\n                JsNativeError::typ()\n                    .with_message(\"`Locale.maximize` can only be called on a `Locale` object\")\n            })?\n            .clone();\n\n        // 3. Let maximal be the result of the Add Likely Subtags algorithm applied to loc.[[Locale]]. If an error is signaled, set maximal to loc.[[Locale]].\n        context\n            .intl_provider()\n            .locale_expander()?\n            .maximize(&mut loc.id);\n\n        // 4. Return ! Construct(%Locale%, maximal).\n        let prototype = context.intrinsics().constructors().locale().prototype();\n        Ok(\n            JsObject::from_proto_and_data_with_shared_shape(context.root_shape(), prototype, loc)\n                .into(),\n        )\n    }\n\n    /// [`Intl.Locale.prototype.minimize ( )`][spec]\n    ///\n    /// More information:\n    ///  - [MDN documentation][mdn]\n    ///\n    /// [spec]: https://tc39.es/ecma402/#sec-Intl.Locale.prototype.minimize\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/Locale/minimize\n    pub(crate) fn minimize(\n        this: &JsValue,\n        _: &[JsValue],\n        context: &mut Context,\n    ) -> JsResult<JsValue> {\n        // 1. Let loc be the this value.\n        // 2. Perform ? RequireInternalSlot(loc, [[InitializedLocale]]).\n        let object = this.as_object();\n        let mut loc = object\n            .as_ref()\n            .and_then(|o| o.downcast_ref::<icu_locale::Locale>())\n            .ok_or_else(|| {\n                JsNativeError::typ().with_message(\n                    \"`Locale.prototype.minimize` can only be called on a `Locale` object\",\n                )\n            })?\n            .clone();\n\n        // 3. Let minimal be the result of the Remove Likely Subtags algorithm applied to loc.[[Locale]]. If an error is signaled, set minimal to loc.[[Locale]].\n        context\n            .intl_provider()\n            .locale_expander()?\n            .minimize(&mut loc.id);\n\n        // 4. Return ! Construct(%Locale%, minimal).\n        let prototype = context.intrinsics().constructors().locale().prototype();\n        Ok(\n            JsObject::from_proto_and_data_with_shared_shape(context.root_shape(), prototype, loc)\n                .into(),\n        )\n    }\n\n    /// [`Intl.Locale.prototype.toString ( )`][spec].\n    ///\n    /// More information:\n    ///  - [MDN documentation][mdn]\n    ///\n    /// [mdn]: https://tc39.es/ecma402/#sec-Intl.Locale.prototype.toString\n    /// [spec]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/Locale/toString\n    pub(crate) fn to_string(this: &JsValue, _: &[JsValue], _: &mut Context) -> JsResult<JsValue> {\n        // 1. Let loc be the this value.\n        // 2. Perform ? RequireInternalSlot(loc, [[InitializedLocale]]).\n        let object = this.as_object();\n        let loc = object\n            .as_ref()\n            .and_then(|o| o.downcast_ref::<icu_locale::Locale>())\n            .ok_or_else(|| {\n                JsNativeError::typ().with_message(\n                    \"`Locale.prototype.toString` can only be called on a `Locale` object\",\n                )\n            })?;\n\n        // 3. Return loc.[[Locale]].\n        Ok(js_string!(loc.to_string()).into())\n    }\n\n    /// [`get Intl.Locale.prototype.baseName`][spec].\n    ///\n    /// More information:\n    ///  - [MDN documentation][mdn]\n    ///\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/Locale/baseName\n    /// [spec]: https://tc39.es/ecma402/#sec-Intl.Locale.prototype.baseName\n    pub(crate) fn base_name(this: &JsValue, _: &[JsValue], _: &mut Context) -> JsResult<JsValue> {\n        // 1. Let loc be the this value.\n        // 2. Perform ? RequireInternalSlot(loc, [[InitializedLocale]]).\n        let object = this.as_object();\n        let loc = object\n            .as_ref()\n            .and_then(|o| o.downcast_ref::<icu_locale::Locale>())\n            .ok_or_else(|| {\n                JsNativeError::typ().with_message(\n                    \"`get Locale.prototype.baseName` can only be called on a `Locale` object\",\n                )\n            })?;\n\n        // 3. Let locale be loc.[[Locale]].\n        // 4. Return the substring of locale corresponding to the unicode_language_id production.\n        Ok(js_string!(loc.id.to_string()).into())\n    }\n\n    /// [`get Intl.Locale.prototype.calendar`][spec].\n    ///\n    /// More information:\n    ///  - [MDN documentation][mdn]\n    ///\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/Locale/calendar\n    /// [spec]: https://tc39.es/ecma402/#sec-Intl.Locale.prototype.calendar\n    pub(crate) fn calendar(this: &JsValue, _: &[JsValue], _: &mut Context) -> JsResult<JsValue> {\n        // 1. Let loc be the this value.\n        // 2. Perform ? RequireInternalSlot(loc, [[InitializedLocale]]).\n        let object = this.as_object();\n        let loc = object\n            .as_ref()\n            .and_then(|o| o.downcast_ref::<icu_locale::Locale>())\n            .ok_or_else(|| {\n                JsNativeError::typ().with_message(\n                    \"`get Locale.prototype.calendar` can only be called on a `Locale` object\",\n                )\n            })?;\n\n        // 3. Return loc.[[Calendar]].\n        Ok(loc\n            .extensions\n            .unicode\n            .keywords\n            .get(&key!(\"ca\"))\n            .map(|v| js_string!(v.to_string()).into())\n            .unwrap_or_default())\n    }\n\n    /// [`get Intl.Locale.prototype.caseFirst`][spec].\n    ///\n    /// More information:\n    ///  - [MDN documentation][mdn]\n    ///\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/Locale/calendar\n    /// [spec]: https://tc39.es/ecma402/#sec-Intl.Locale.prototype.calendar\n    pub(crate) fn case_first(this: &JsValue, _: &[JsValue], _: &mut Context) -> JsResult<JsValue> {\n        // 1. Let loc be the this value.\n        // 2. Perform ? RequireInternalSlot(loc, [[InitializedLocale]]).\n        let object = this.as_object();\n        let loc = object\n            .as_ref()\n            .and_then(|o| o.downcast_ref::<icu_locale::Locale>())\n            .ok_or_else(|| {\n                JsNativeError::typ().with_message(\n                    \"`get Locale.prototype.caseFirst` can only be called on a `Locale` object\",\n                )\n            })?;\n\n        // 3. Return loc.[[CaseFirst]].\n        Ok(loc\n            .extensions\n            .unicode\n            .keywords\n            .get(&key!(\"kf\"))\n            .map(|v| js_string!(v.to_string()).into())\n            .unwrap_or_default())\n    }\n\n    /// [`get Intl.Locale.prototype.collation`][spec].\n    ///\n    /// More information:\n    ///  - [MDN documentation][mdn]\n    ///\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/Locale/collation\n    /// [spec]: https://tc39.es/ecma402/#sec-Intl.Locale.prototype.collation\n    pub(crate) fn collation(this: &JsValue, _: &[JsValue], _: &mut Context) -> JsResult<JsValue> {\n        // 1. Let loc be the this value.\n        // 2. Perform ? RequireInternalSlot(loc, [[InitializedLocale]]).\n        let object = this.as_object();\n        let loc = object\n            .as_ref()\n            .and_then(|o| o.downcast_ref::<icu_locale::Locale>())\n            .ok_or_else(|| {\n                JsNativeError::typ().with_message(\n                    \"`get Locale.prototype.collation` can only be called on a `Locale` object\",\n                )\n            })?;\n\n        // 3. Return loc.[[Collation]].\n        Ok(loc\n            .extensions\n            .unicode\n            .keywords\n            .get(&key!(\"co\"))\n            .map(|v| js_string!(v.to_string()).into())\n            .unwrap_or_default())\n    }\n\n    /// [`get Intl.Locale.prototype.hourCycle`][spec].\n    ///\n    /// More information:\n    ///  - [MDN documentation][mdn]\n    ///\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/Locale/hourCycle\n    /// [spec]: https://tc39.es/ecma402/#sec-Intl.Locale.prototype.hourCycle\n    pub(crate) fn hour_cycle(this: &JsValue, _: &[JsValue], _: &mut Context) -> JsResult<JsValue> {\n        // 1. Let loc be the this value.\n        // 2. Perform ? RequireInternalSlot(loc, [[InitializedLocale]]).\n        let object = this.as_object();\n        let loc = object\n            .as_ref()\n            .and_then(|o| o.downcast_ref::<icu_locale::Locale>())\n            .ok_or_else(|| {\n                JsNativeError::typ().with_message(\n                    \"`get Locale.prototype.hourCycle` can only be called on a `Locale` object\",\n                )\n            })?;\n\n        // 3. Return loc.[[HourCycle]].\n        Ok(loc\n            .extensions\n            .unicode\n            .keywords\n            .get(&key!(\"hc\"))\n            .map(|v| js_string!(v.to_string()).into())\n            .unwrap_or_default())\n    }\n\n    /// [`get Intl.Locale.prototype.numeric`][spec].\n    ///\n    /// More information:\n    ///  - [MDN documentation][mdn]\n    ///\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/Locale/numeric\n    /// [spec]: https://tc39.es/ecma402/#sec-Intl.Locale.prototype.numeric\n    pub(crate) fn numeric(this: &JsValue, _: &[JsValue], _: &mut Context) -> JsResult<JsValue> {\n        // 1. Let loc be the this value.\n        // 2. Perform ? RequireInternalSlot(loc, [[InitializedLocale]]).\n        let object = this.as_object();\n        let loc = object\n            .as_ref()\n            .and_then(|o| o.downcast_ref::<icu_locale::Locale>())\n            .ok_or_else(|| {\n                JsNativeError::typ().with_message(\n                    \"`get Locale.prototype.numeric` can only be called on a `Locale` object\",\n                )\n            })?;\n\n        // 3. Return loc.[[Numeric]].\n        let kn = loc\n            .extensions\n            .unicode\n            .keywords\n            .get(&key!(\"kn\"))\n            .is_some_and(Value::is_empty);\n\n        Ok(JsValue::from(kn))\n    }\n\n    /// [`get Intl.Locale.prototype.numberingSystem`][spec].\n    ///\n    /// More information:\n    ///  - [MDN documentation][mdn]\n    ///\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/Locale/numeric\n    /// [spec]: https://tc39.es/ecma402/#sec-Intl.Locale.prototype.numeric\n    pub(crate) fn numbering_system(\n        this: &JsValue,\n        _: &[JsValue],\n        _: &mut Context,\n    ) -> JsResult<JsValue> {\n        // 1. Let loc be the this value.\n        // 2. Perform ? RequireInternalSlot(loc, [[InitializedLocale]]).\n        let object = this.as_object();\n        let loc = object\n            .as_ref()\n            .and_then(|o| o.downcast_ref::<icu_locale::Locale>())\n            .ok_or_else(|| {\n                JsNativeError::typ().with_message(\n                    \"`get Locale.prototype.numberingSystem` can only be called on a `Locale` object\",\n                )\n            })?;\n\n        // 3. Return loc.[[NumberingSystem]].\n        Ok(loc\n            .extensions\n            .unicode\n            .keywords\n            .get(&key!(\"nu\"))\n            .map(|v| js_string!(v.to_string()).into())\n            .unwrap_or_default())\n    }\n\n    /// [`get Intl.Locale.prototype.language`][spec].\n    ///\n    /// More information:\n    ///  - [MDN documentation][mdn]\n    ///\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/Locale/language\n    /// [spec]: https://tc39.es/ecma402/#sec-Intl.Locale.prototype.language\n    pub(crate) fn language(this: &JsValue, _: &[JsValue], _: &mut Context) -> JsResult<JsValue> {\n        // 1. Let loc be the this value.\n        // 2. Perform ? RequireInternalSlot(loc, [[InitializedLocale]]).\n        let object = this.as_object();\n        let loc = object\n            .as_ref()\n            .and_then(|o| o.downcast_ref::<icu_locale::Locale>())\n            .ok_or_else(|| {\n                JsNativeError::typ().with_message(\n                    \"`get Locale.prototype.language` can only be called on a `Locale` object\",\n                )\n            })?;\n\n        // 3. Let locale be loc.[[Locale]].\n        // 4. Assert: locale matches the unicode_locale_id production.\n        // 5. Return the substring of locale corresponding to the unicode_language_subtag production of the unicode_language_id.\n        Ok(js_string!(loc.id.language.to_string()).into())\n    }\n\n    /// [`get Intl.Locale.prototype.script`][spec].\n    ///\n    /// More information:\n    ///  - [MDN documentation][mdn]\n    ///\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/Locale/script\n    /// [spec]: https://tc39.es/ecma402/#sec-Intl.Locale.prototype.script\n    pub(crate) fn script(this: &JsValue, _: &[JsValue], _: &mut Context) -> JsResult<JsValue> {\n        // 1. Let loc be the this value.\n        // 2. Perform ? RequireInternalSlot(loc, [[InitializedLocale]]).\n        let object = this.as_object();\n        let loc = object\n            .as_ref()\n            .and_then(|o| o.downcast_ref::<icu_locale::Locale>())\n            .ok_or_else(|| {\n                JsNativeError::typ().with_message(\n                    \"`get Locale.prototype.script` can only be called on a `Locale` object\",\n                )\n            })?;\n\n        // 3. Let locale be loc.[[Locale]].\n        // 4. Assert: locale matches the unicode_locale_id production.\n        // 5. If the unicode_language_id production of locale does not contain the [\"-\" unicode_script_subtag] sequence, return undefined.\n        // 6. Return the substring of locale corresponding to the unicode_script_subtag production of the unicode_language_id.\n        Ok(loc\n            .id\n            .script\n            .map(|sc| js_string!(sc.to_string()).into())\n            .unwrap_or_default())\n    }\n\n    /// [`get Intl.Locale.prototype.region`][spec].\n    ///\n    /// More information:\n    ///  - [MDN documentation][mdn]\n    ///\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/Locale/region\n    /// [spec]: https://tc39.es/ecma402/#sec-Intl.Locale.prototype.region\n    pub(crate) fn region(this: &JsValue, _: &[JsValue], _: &mut Context) -> JsResult<JsValue> {\n        // 1. Let loc be the this value.\n        // 2. Perform ? RequireInternalSlot(loc, [[InitializedLocale]]).\n        let object = this.as_object();\n        let loc = object\n            .as_ref()\n            .and_then(|o| o.downcast_ref::<icu_locale::Locale>())\n            .ok_or_else(|| {\n                JsNativeError::typ().with_message(\n                    \"`get Locale.prototype.region` can only be called on a `Locale` object\",\n                )\n            })?;\n\n        // 3. Let locale be loc.[[Locale]].\n        // 4. Assert: locale matches the unicode_locale_id production.\n        // 5. If the unicode_language_id production of locale does not contain the [\"-\" unicode_region_subtag] sequence, return undefined.\n        // 6. Return the substring of locale corresponding to the unicode_region_subtag production of the unicode_language_id.\n        Ok(loc\n            .id\n            .region\n            .map(|sc| js_string!(sc.to_string()).into())\n            .unwrap_or_default())\n    }\n\n    /// [`get Intl.Locale.prototype.variants`][spec].\n    ///\n    /// More information:\n    ///  - [MDN documentation][mdn]\n    ///\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/Locale/variants\n    /// [spec]: https://tc39.es/ecma402/#sec-Intl.Locale.prototype.variants\n    pub(crate) fn variants(this: &JsValue, _: &[JsValue], _: &mut Context) -> JsResult<JsValue> {\n        // 1. Let loc be the this value.\n        // 2. Perform ? RequireInternalSlot(loc, [[InitializedLocale]]).\n        let loc = this.as_object();\n        let loc = loc\n            .as_ref()\n            .and_then(|o| o.downcast_ref::<icu_locale::Locale>())\n            .ok_or_else(|| {\n                JsNativeError::typ().with_message(\n                    \"`get Locale.prototype.variants` can only be called on a `Locale` object\",\n                )\n            })?;\n\n        // 3. Return GetLocaleVariants(loc.[[Locale]]).\n\n        // Abstract operation `GetLocaleVariants ( locale )`\n        // <https://tc39.es/ecma402/#sec-getlocalevariants>\n\n        // 1. Let baseName be GetLocaleBaseName(locale).\n        // 2. NOTE: Each subtag in baseName that is preceded by \"-\" is either a unicode_script_subtag,\n        //    unicode_region_subtag, or unicode_variant_subtag, but any substring matched by unicode_variant_subtag\n        //    is strictly longer than any prefix thereof which could also be matched by one of the other productions.\n        // 3. Let variants be the longest suffix of baseName that starts with a \"-\" followed by a substring\n        //    that is matched by the unicode_variant_subtag Unicode locale nonterminal. If there is no such\n        //    suffix, return undefined.\n        if loc.id.variants.is_empty() {\n            return Ok(JsValue::undefined());\n        }\n\n        // 4. Return the substring of variants from 1.\n        Ok(js_string!(loc.id.variants.to_string()).into())\n    }\n}\n"
  },
  {
    "path": "core/engine/src/builtins/intl/locale/options.rs",
    "content": "use icu_locale::{\n    extensions::unicode::Value,\n    subtags::{Language, Region, Script, Variant, Variants},\n};\n\nuse crate::{Context, JsNativeError, builtins::options::OptionType};\n\nimpl OptionType for Value {\n    fn from_value(value: crate::JsValue, context: &mut Context) -> crate::JsResult<Self> {\n        let val = value.to_string(context)?.to_std_string_escaped();\n\n        if val.is_empty() {\n            return Err(JsNativeError::range()\n                .with_message(\"nonterminal `type` must be at least 3 characters long\")\n                .into());\n        }\n\n        let val = val\n            .parse::<Self>()\n            .map_err(|e| JsNativeError::range().with_message(e.to_string()))?;\n\n        for subtag in val.clone() {\n            if subtag.len() < 3 {\n                return Err(JsNativeError::range()\n                    .with_message(\"nonterminal `type` must be at least 3 characters long\")\n                    .into());\n            }\n        }\n\n        Ok(val)\n    }\n}\n\nimpl OptionType for Language {\n    fn from_value(value: crate::JsValue, context: &mut Context) -> crate::JsResult<Self> {\n        value\n            .to_string(context)?\n            .to_std_string_escaped()\n            .parse::<Self>()\n            .map_err(|e| JsNativeError::range().with_message(e.to_string()).into())\n    }\n}\n\nimpl OptionType for Script {\n    fn from_value(value: crate::JsValue, context: &mut Context) -> crate::JsResult<Self> {\n        value\n            .to_string(context)?\n            .to_std_string_escaped()\n            .parse::<Self>()\n            .map_err(|e| JsNativeError::range().with_message(e.to_string()).into())\n    }\n}\n\nimpl OptionType for Region {\n    fn from_value(value: crate::JsValue, context: &mut Context) -> crate::JsResult<Self> {\n        value\n            .to_string(context)?\n            .to_std_string_escaped()\n            .parse::<Self>()\n            .map_err(|e| JsNativeError::range().with_message(e.to_string()).into())\n    }\n}\n\nimpl OptionType for Variants {\n    fn from_value(value: crate::JsValue, context: &mut Context) -> crate::JsResult<Self> {\n        let variants = value.to_string(context)?.to_std_string_escaped();\n        // a. If variants is the empty String, throw a RangeError exception.\n        if variants.is_empty() {\n            return Err(JsNativeError::range()\n                .with_message(\"locale variants cannot be empty\")\n                .into());\n        }\n\n        // b. Let lowerVariants be the ASCII-lowercase of variants.\n        // c. Let variantSubtags be StringSplitToList(lowerVariants, \"-\").\n        let variants = variants.split('-');\n\n        // d. For each element variant of variantSubtags, do\n        let mut v = Vec::with_capacity(variants.size_hint().0);\n        for variant in variants {\n            //        i. If variant cannot be matched by the unicode_variant_subtag Unicode locale nonterminal, throw a RangeError exception.\n            let variant = variant\n                .parse::<Variant>()\n                .map_err(|e| JsNativeError::range().with_message(e.to_string()))?;\n\n            // e. If variantSubtags contains any duplicate elements, throw a RangeError exception.\n            let Err(index) = v.binary_search(&variant) else {\n                return Err(JsNativeError::range()\n                    .with_message(\"locale variants cannot have duplicate subtags\")\n                    .into());\n            };\n            v.insert(index, variant);\n        }\n\n        Ok(Variants::from_vec_unchecked(v))\n    }\n}\n"
  },
  {
    "path": "core/engine/src/builtins/intl/locale/tests.rs",
    "content": "use icu_decimal::provider::DecimalSymbolsV1;\nuse icu_locale::{\n    Locale, extensions_unicode_key as key, extensions_unicode_value as value, locale,\n    preferences::extensions::unicode::keywords::NumberingSystem,\n};\nuse icu_plurals::provider::PluralsCardinalV1;\nuse icu_provider::{\n    DataIdentifierBorrowed, DataLocale, DataProvider, DataRequest, DataRequestMetadata,\n    DryDataProvider,\n    prelude::icu_locale_core::{LanguageIdentifier, extensions::unicode},\n};\n\nuse crate::{\n    builtins::intl::{\n        Service, ServicePreferences,\n        locale::{default_locale, resolve_locale},\n        options::{IntlOptions, LocaleMatcher},\n    },\n    context::icu::IntlProvider,\n};\n\n#[derive(Debug, Copy, Clone)]\nstruct TestPreferences {\n    nu: Option<NumberingSystem>,\n}\n\nimpl From<&Locale> for TestPreferences {\n    fn from(value: &Locale) -> Self {\n        Self {\n            nu: value\n                .extensions\n                .unicode\n                .keywords\n                .get(&unicode::key!(\"nu\"))\n                .and_then(|nu| NumberingSystem::try_from(nu.clone()).ok()),\n        }\n    }\n}\n\nimpl ServicePreferences for TestPreferences {\n    fn validate(&mut self, id: &LanguageIdentifier, provider: &IntlProvider) {\n        if self.nu.is_some() {\n            return;\n        }\n\n        let locale = &DataLocale::from(id);\n        let req = DataRequest {\n            id: DataIdentifierBorrowed::for_locale(locale),\n            metadata: DataRequestMetadata::default(),\n        };\n        let data = DataProvider::<DecimalSymbolsV1>::load(provider, req).unwrap();\n        let preferred = data.payload.get().numsys();\n        self.nu = Some(\n            NumberingSystem::try_from(unicode::Value::try_from_str(preferred).unwrap()).unwrap(),\n        );\n    }\n\n    fn as_unicode(&self) -> unicode::Unicode {\n        let mut exts = unicode::Unicode::new();\n        if let Some(nu) = self.nu {\n            exts.keywords.set(unicode::key!(\"nu\"), nu.into());\n        }\n        exts\n    }\n\n    fn extended(&self, other: &Self) -> Self {\n        let mut result = *self;\n        if result.nu.is_none() {\n            result.nu = other.nu;\n        }\n        result\n    }\n\n    fn intersection(&self, other: &Self) -> Self {\n        let mut inter = *self;\n        if inter.nu != other.nu {\n            inter.nu.take();\n        }\n        inter\n    }\n}\n\nstruct TestService;\n\nimpl Service for TestService {\n    type LangMarker = PluralsCardinalV1;\n\n    type Preferences = TestPreferences;\n}\n\n#[test]\nfn locale_resolution() {\n    let provider = IntlProvider::try_new_buffer(boa_icu_provider::buffer());\n    let mut default = default_locale(provider.locale_canonicalizer().unwrap());\n    default = <IntlProvider as DryDataProvider<<TestService as Service>::LangMarker>>::dry_load(\n        &provider,\n        DataRequest {\n            id: DataIdentifierBorrowed::for_locale(&default.clone().into()),\n            metadata: {\n                let mut md = DataRequestMetadata::default();\n                md.silent = true;\n                md\n            },\n        },\n    )\n    .unwrap()\n    .locale\n    .map_or(default, DataLocale::into_locale);\n\n    default\n        .extensions\n        .unicode\n        .keywords\n        .set(key!(\"nu\"), value!(\"latn\"));\n\n    // test lookup\n    let mut options = IntlOptions {\n        matcher: LocaleMatcher::Lookup,\n        preferences: TestPreferences {\n            nu: Some(NumberingSystem::try_from(value!(\"latn\")).unwrap()),\n        },\n    };\n    let locale = resolve_locale::<TestService>([], &mut options, &provider).unwrap();\n    assert_eq!(locale, default);\n\n    // test best fit\n    let mut options = IntlOptions {\n        matcher: LocaleMatcher::BestFit,\n        preferences: TestPreferences {\n            nu: Some(NumberingSystem::try_from(value!(\"latn\")).unwrap()),\n        },\n    };\n\n    let locale = resolve_locale::<TestService>([], &mut options, &provider).unwrap();\n    assert_eq!(locale, default);\n\n    // requested: [es-ES]\n    let mut options = IntlOptions {\n        matcher: LocaleMatcher::Lookup,\n        preferences: TestPreferences { nu: None },\n    };\n\n    let locale =\n        resolve_locale::<TestService>([locale!(\"bn-Arab\")], &mut options, &provider).unwrap();\n    assert_eq!(locale, \"bn-u-nu-beng\".parse().unwrap());\n}\n"
  },
  {
    "path": "core/engine/src/builtins/intl/locale/utils.rs",
    "content": "use crate::{\n    Context, JsNativeError, JsResult, JsValue,\n    builtins::{\n        Array,\n        intl::{\n            Service, ServicePreferences,\n            options::{IntlOptions, LocaleMatcher, coerce_options_to_object},\n        },\n        options::get_option,\n    },\n    context::icu::IntlProvider,\n    js_string,\n    object::JsObject,\n};\n\nuse icu_locale::extensions::unicode::value;\nuse icu_locale::{LanguageIdentifier, Locale, LocaleCanonicalizer};\nuse icu_provider::{\n    DataIdentifierBorrowed, DataLocale, DataMarker, DataMarkerAttributes, DataRequest,\n    DataRequestMetadata, DryDataProvider,\n};\nuse indexmap::IndexSet;\n\nuse tap::TapOptional;\n\n/// Abstract operation `DefaultLocale ( )`\n///\n/// Returns a String value representing the structurally valid and canonicalized\n/// Unicode BCP 47 locale identifier for the host environment's current locale.\n///\n/// More information:\n///  - [ECMAScript reference][spec]\n///\n/// [spec]: https://tc39.es/ecma402/#sec-defaultlocale\npub(crate) fn default_locale(canonicalizer: &LocaleCanonicalizer) -> Locale {\n    sys_locale::get_locale()\n        .and_then(|loc| loc.parse::<Locale>().ok())\n        .tap_some_mut(|loc| {\n            canonicalizer.canonicalize(loc);\n        })\n        .unwrap_or(Locale::UNKNOWN)\n}\n\n/// Gets the `Locale` struct from a `JsValue`.\npub(crate) fn locale_from_value(tag: &JsValue, context: &mut Context) -> JsResult<Locale> {\n    // ii. If Type(kValue) is not String or Object, throw a TypeError exception.\n    if !(tag.is_object() || tag.is_string()) {\n        return Err(JsNativeError::typ()\n            .with_message(\"locale should be a String or Object\")\n            .into());\n    }\n    // iii. If Type(kValue) is Object and kValue has an [[InitializedLocale]] internal slot, then\n    let object = tag.as_object();\n    if let Some(tag) = object.as_ref().and_then(|obj| obj.downcast_ref::<Locale>()) {\n        // 1. Let tag be kValue.[[Locale]].\n        // No need to canonicalize since all `Locale` objects should already be canonicalized.\n        return Ok(tag.clone());\n    }\n\n    // iv. Else,\n    // 1. Let tag be ? ToString(kValue).\n    let tag = tag.to_string(context)?.to_std_string_escaped();\n    if tag.contains('_') {\n        return Err(JsNativeError::range()\n            .with_message(\"locale is not a structurally valid language tag\")\n            .into());\n    }\n\n    let mut tag = tag\n        .parse()\n        // v. If IsStructurallyValidLanguageTag(tag) is false, throw a RangeError exception.\n        .map_err(|_| {\n            JsNativeError::range().with_message(\"locale is not a structurally valid language tag\")\n        })?;\n\n    // All callers of `locale_from_value` canonicalize the result immediately after, so\n    // we canonicalize here instead.\n    context\n        .intl_provider()\n        .locale_canonicalizer()?\n        .canonicalize(&mut tag);\n\n    // TODO: Remove this workaround once ICU4X supports canonicalization of\n    // Unicode extension value aliases (unicode-org/icu4x#3483).\n    // Currently ICU4X does not canonicalize \"yes\" → \"true\" for certain keys.\n    let keys: Vec<_> = tag\n        .extensions\n        .unicode\n        .keywords\n        .iter()\n        .map(|(k, _)| *k)\n        .collect();\n\n    for k in keys {\n        match k.as_str() {\n            \"kb\" | \"kc\" | \"kh\" | \"kk\" | \"kn\" => {\n                if let Some(v) = tag.extensions.unicode.keywords.get_mut(&k)\n                    && *v == value!(\"yes\")\n                {\n                    *v = value!(\"true\");\n                }\n            }\n            _ => {}\n        }\n    }\n    Ok(tag)\n}\n\n/// Abstract operation `CanonicalizeLocaleList ( locales )`\n///\n/// Converts an array of [`JsValue`]s containing structurally valid\n/// [Unicode BCP 47 locale identifiers][bcp-47] into their [canonical form][canon].\n///\n/// For efficiency, this returns [`Locale`]s instead of [`String`]s, since\n/// `Locale` allows us to modify individual parts of the locale without scanning\n/// the whole string again.\n///\n/// More information:\n///  - [ECMAScript reference][spec]\n///\n/// [spec]: https://tc39.es/ecma402/#sec-canonicalizelocalelist\n/// [bcp-47]: https://unicode.org/reports/tr35/#Unicode_locale_identifier\n/// [canon]: https://unicode.org/reports/tr35/#LocaleId_Canonicalization\npub(crate) fn canonicalize_locale_list(\n    locales: &JsValue,\n    context: &mut Context,\n) -> JsResult<Vec<Locale>> {\n    // 1. If locales is undefined, then\n    if locales.is_undefined() {\n        // a. Return a new empty List.\n        return Ok(Vec::default());\n    }\n\n    // 2. Let seen be a new empty List.\n    let mut seen = IndexSet::new();\n\n    // 3. If Type(locales) is String or Type(locales) is Object and locales has an [[InitializedLocale]] internal slot, then\n    let o = if locales.is_string() || locales.as_object().is_some_and(|o| o.is::<Locale>()) {\n        // a. Let O be CreateArrayFromList(« locales »).\n        Array::create_array_from_list([locales.clone()], context)\n    } else {\n        // 4. Else,\n        // a. Let O be ? ToObject(locales).\n        locales.to_object(context)?\n    };\n\n    // 5. Let len be ? ToLength(? Get(O, \"length\")).\n    let len = o.length_of_array_like(context)?;\n\n    // 6 Let k be 0.\n    // 7. Repeat, while k < len,\n    for k in 0..len {\n        // a. Let Pk be ToString(k).\n        // b. Let kPresent be ? HasProperty(O, Pk).\n        // c. If kPresent is true, then\n        // c.i. Let kValue be ? Get(O, Pk).\n        if let Some(k_value) = o.try_get(k, context)? {\n            let tag = locale_from_value(&k_value, context)?;\n\n            // vii. If canonicalizedTag is not an element of seen, append canonicalizedTag as the last element of seen.\n            seen.insert(tag);\n        }\n        // d. Increase k by 1.\n    }\n\n    // 8. Return seen.\n    Ok(seen.into_iter().collect())\n}\n\n/// Abstract operation [`LookupMatchingLocaleByPrefix ( availableLocales, requestedLocales )`][prefix]\n/// and [`LookupMatchingLocaleByBestFit ( availableLocales, requestedLocales )`][best]\n///\n/// Compares `requestedLocales`, which must be a `List` as returned by `CanonicalizeLocaleList`,\n/// against the locales in `availableLocales` and determines the best available language to\n/// meet the request.\n///\n/// # Notes\n///\n/// - This differs a bit from the spec, since we don't have an `[[AvailableLocales]]`\n///   list to compare with. However, we can do data requests to a [`DryDataProvider`]\n///   in order to see if a certain [`Locale`] is supported.\n///\n/// - Calling this function with a singleton [`DataMarker`] will always return `None`.\n///\n/// [prefix]: https://tc39.es/ecma402/#sec-lookupmatchinglocalebyprefix\n/// [best]: https://tc39.es/ecma402/#sec-lookupmatchinglocalebybestfit\nfn lookup_matching_locale_by_prefix<S: Service>(\n    requested_locales: impl IntoIterator<Item = Locale>,\n    provider: &IntlProvider,\n) -> Option<Locale>\nwhere\n    IntlProvider: DryDataProvider<S::LangMarker>,\n{\n    // 1. For each element locale of requestedLocales, do\n    for locale in requested_locales {\n        // a. Let extension be empty.\n        // b. If locale contains a Unicode locale extension sequence, then\n        //     i. Set extension to the Unicode locale extension sequence of locale.\n        //     ii. Set locale to the String value that is locale with any Unicode locale extension sequences removed.\n        let mut locale = locale.clone();\n        let id = std::mem::replace(&mut locale.id, LanguageIdentifier::UNKNOWN);\n        locale.extensions.transform.clear();\n        locale.extensions.private.clear();\n\n        // c. Let prefix be locale.\n        let mut prefix = id.into();\n\n        // d. Repeat, while prefix is not the empty String,\n        // We don't use a `while !prefix.is_und()` because it could be that prefix is und at the start,\n        // so we need to make the request at least once.\n        loop {\n            // i. If availableLocales contains prefix, return the Record { [[locale]]: prefix, [[extension]]: extension }.\n            // ICU4X requires doing data requests in order to check if a locale\n            // is part of the set of supported locales.\n            let response = DryDataProvider::dry_load(\n                provider,\n                DataRequest {\n                    id: DataIdentifierBorrowed::for_marker_attributes_and_locale(\n                        S::ATTRIBUTES,\n                        &prefix,\n                    ),\n                    metadata: {\n                        let mut metadata = DataRequestMetadata::default();\n                        metadata.silent = true;\n                        metadata\n                    },\n                },\n            );\n\n            if let Ok(metadata) = response {\n                // `metadata.locale` returns None when the provider doesn't have a fallback mechanism,\n                // but supports the required locale. However, if the provider has a fallback mechanism,\n                // this will return `Some(locale)`, where the locale is the used locale after applying\n                // the fallback algorithm, even if the used locale is exactly the same as the required\n                // locale.\n                match metadata.locale {\n                    Some(loc) if loc.into_locale().id == prefix.into_locale().id => {\n                        locale.id = prefix.into_locale().id;\n                        return Some(locale);\n                    }\n                    None => {\n                        locale.id = prefix.into_locale().id;\n                        return Some(locale);\n                    }\n                    _ => {}\n                }\n            }\n\n            // ii. If prefix contains \"-\" (code unit 0x002D HYPHEN-MINUS), let pos be the index into prefix of the last occurrence of \"-\"; else let pos be 0.\n            // iii. Repeat, while pos ≥ 2 and the substring of prefix from pos - 2 to pos - 1 is \"-\",\n            //     1. Set pos to pos - 2.\n            // iv. Set prefix to the substring of prefix from 0 to pos.\n            // Since the definition of `LanguageIdentifier` allows us to manipulate it\n            // without using strings, we can replace these steps by a simpler\n            // algorithm.\n            if prefix.variant.take().is_none()\n                && prefix.region.take().is_none()\n                && prefix.script.take().is_none()\n            {\n                break;\n            }\n        }\n    }\n\n    // 2. Return undefined.\n    None\n}\n\n/// Abstract operation [`LookupMatchingLocaleByBestFit ( availableLocales, requestedLocales )`][spec]\n///\n/// Compares `requestedLocales`, which must be a `List` as returned by `CanonicalizeLocaleList`,\n/// against the locales in `availableLocales` and determines the best available language to\n/// meet the request. The algorithm is implementation dependent, but should produce results\n/// that a typical user of the requested locales would perceive as at least as good as those\n/// produced by the `LookupMatcher` abstract operation.\n///\n/// [spec]: https://tc39.es/ecma402/#sec-bestfitmatcher\nfn lookup_matching_locale_by_best_fit<S: Service>(\n    requested_locales: impl IntoIterator<Item = Locale>,\n    provider: &IntlProvider,\n) -> Option<Locale>\nwhere\n    IntlProvider: DryDataProvider<S::LangMarker>,\n{\n    for mut locale in requested_locales {\n        let id = std::mem::replace(&mut locale.id, LanguageIdentifier::UNKNOWN);\n\n        // Only leave unicode extensions when returning the locale.\n        locale.extensions.transform.clear();\n        locale.extensions.private.clear();\n\n        let dl = &DataLocale::from(&id);\n\n        let Ok(response) = DryDataProvider::dry_load(\n            provider,\n            DataRequest {\n                id: DataIdentifierBorrowed::for_marker_attributes_and_locale(S::ATTRIBUTES, dl),\n                metadata: {\n                    let mut md = DataRequestMetadata::default();\n                    md.silent = true;\n                    md\n                },\n            },\n        ) else {\n            continue;\n        };\n\n        if id == LanguageIdentifier::UNKNOWN {\n            return Some(locale);\n        }\n\n        if let Some(id) = response\n            .locale\n            .map(|dl| dl.into_locale().id)\n            .or(Some(id))\n            .filter(|loc| loc != &LanguageIdentifier::UNKNOWN)\n        {\n            locale.id = id;\n            return Some(locale);\n        }\n    }\n    None\n}\n\n/// Abstract operation `ResolveLocale ( availableLocales, requestedLocales, options, relevantExtensionKeys, localeData )`\n///\n/// Compares a BCP 47 language priority list `requestedLocales` against the locales\n/// in `availableLocales` and determines the best available language to meet the request.\n/// `availableLocales`, `requestedLocales`, and `relevantExtensionKeys` must be provided as\n/// `List` values, options and `localeData` as Records.\n///\n/// More information:\n///  - [ECMAScript reference][spec]\n///\n/// [spec]: https://tc39.es/ecma402/#sec-resolvelocale\npub(in crate::builtins::intl) fn resolve_locale<S>(\n    requested_locales: impl IntoIterator<Item = Locale>,\n    options: &mut IntlOptions<S::Preferences>,\n    provider: &IntlProvider,\n) -> JsResult<Locale>\nwhere\n    S: Service,\n    IntlProvider: DryDataProvider<S::LangMarker>,\n{\n    // 1. Let matcher be options.[[localeMatcher]].\n    // 2. If matcher is \"lookup\", then\n    //     a. Let r be LookupMatchingLocaleByPrefix(availableLocales, requestedLocales).\n    // 3. Else,\n    //     a. Let r be LookupMatchingLocaleByBestFit(availableLocales, requestedLocales).\n    // 4. If r is undefined, set r to the Record { [[locale]]: DefaultLocale(), [[extension]]: empty }.\n    let found_locale = if options.matcher == LocaleMatcher::Lookup {\n        lookup_matching_locale_by_prefix::<S>(requested_locales, provider)\n    } else {\n        lookup_matching_locale_by_best_fit::<S>(requested_locales, provider)\n    };\n\n    let mut found_locale = if let Some(loc) = found_locale {\n        loc\n    } else {\n        let default = default_locale(provider.locale_canonicalizer()?);\n        lookup_matching_locale_by_best_fit::<S>([default], provider).ok_or_else(|| {\n            JsNativeError::typ().with_message(\"could not find i18n data for Intl service\")\n        })?\n    };\n\n    // From here, the spec differs significantly from the implementation,\n    // since ICU4X allows us to skip some steps and modularize the\n    // extension resolution algorithm. However, the original spec is left here\n    // for completion purposes.\n\n    // 5. Let result be a new Record.\n    // 6. Set result.[[dataLocale]] to foundLocale.\n    // 7. If r has an [[extension]] field, then\n    //     a. Let components be ! UnicodeExtensionComponents(r.[[extension]]).\n    //     b. Let keywords be components.[[Keywords]].\n    // 9. For each element key of relevantExtensionKeys, do\n    //     a. Let foundLocaleData be localeData.[[<foundLocale>]].\n    //     b. Assert: Type(foundLocaleData) is Record.\n    //     c. Let keyLocaleData be foundLocaleData.[[<key>]].\n    //     d. Assert: Type(keyLocaleData) is List.\n    //     e. Let value be keyLocaleData[0].\n    //     f. Assert: Type(value) is either String or Null.\n    //     g. Let supportedExtensionAddition be \"\".\n    //     h. If r has an [[extension]] field, then\n    //         i. If keywords contains an element whose [[Key]] is the same as key, then\n    //             1. Let entry be the element of keywords whose [[Key]] is the same as key.\n    //             2. Let requestedValue be entry.[[Value]].\n    //             3. If requestedValue is not the empty String, then\n    //                 a. If keyLocaleData contains requestedValue, then\n    //                     i. Let value be requestedValue.\n    //                     ii. Let supportedExtensionAddition be the string-concatenation of \"-\", key, \"-\", and value.\n    //             4. Else if keyLocaleData contains \"true\", then\n    //                 a. Let value be \"true\".\n    //                 b. Let supportedExtensionAddition be the string-concatenation of \"-\" and key.\n    //     i. If options has a field [[<key>]], then\n    //         i. Let optionsValue be options.[[<key>]].\n    //         ii. Assert: Type(optionsValue) is either String, Undefined, or Null.\n    //         iii. If Type(optionsValue) is String, then\n    //             1. Let optionsValue be the string optionsValue after performing the algorithm steps to transform\n    //                Unicode extension values to canonical syntax per Unicode Technical Standard #35 LDML § 3.2.1\n    //                Canonical Unicode Locale Identifiers, treating key as ukey and optionsValue as uvalue productions.\n    //             2. Let optionsValue be the string optionsValue after performing the algorithm steps to replace\n    //                Unicode extension values with their canonical form per Unicode Technical Standard #35 LDML § 3.2.1\n    //                Canonical Unicode Locale Identifiers, treating key as ukey and optionsValue as uvalue productions.\n    //             3. If optionsValue is the empty String, then\n    //                 a. Let optionsValue be \"true\".\n    //         iv. If keyLocaleData contains optionsValue, then\n    //             1. If SameValue(optionsValue, value) is false, then\n    //                 a. Let value be optionsValue.\n    //                 b. Let supportedExtensionAddition be \"\".\n    //     j. Set result.[[<key>]] to value.\n    //     k. Append supportedExtensionAddition to supportedExtension.\n    // 10. If the number of elements in supportedExtension is greater than 2, then\n    //     a. Let foundLocale be InsertUnicodeExtensionAndCanonicalize(foundLocale, supportedExtension).\n    // 11. Set result.[[locale]] to foundLocale.\n\n    // This is basically an adaptation of the process above, which\n    // ensures:\n    // - All options provided by the locale are valid.\n    // - All options provided by args are valid.\n    // - Options provided by args are extended (but not overridden) by\n    //   options provided in the locale.\n    // - Only the locale options that extended the args options are\n    //   added to the final locale.\n    provider\n        .locale_canonicalizer()?\n        .canonicalize(&mut found_locale);\n    let mut locale_prefs = S::Preferences::from(&found_locale);\n\n    options.preferences.validate(&found_locale.id, provider);\n    locale_prefs.validate(&found_locale.id, provider);\n\n    // This should not touch the found locale.\n    let prefs = locale_prefs.extended(&options.preferences);\n\n    found_locale.extensions.unicode = prefs.intersection(&locale_prefs).as_unicode();\n    options.preferences = prefs;\n\n    // 12. Return result.\n    Ok(found_locale)\n}\n\n/// Abstract operation [`FilterLocales ( availableLocales, requestedLocales, options )`][spec]\n///\n/// Returns the subset of the provided BCP 47 language priority list requestedLocales for which\n/// availableLocales has a matching locale.\n///\n/// # Note\n///\n/// Calling this function with a Service that has a singleton `LangMarker` will always return `None`.\n///\n/// [spec]: https://tc39.es/ecma402/#sec-supportedlocales\npub(in crate::builtins::intl) fn filter_locales<S: Service>(\n    requested_locales: Vec<Locale>,\n    options: &JsValue,\n    context: &mut Context,\n) -> JsResult<JsObject>\nwhere\n    IntlProvider: DryDataProvider<S::LangMarker>,\n{\n    // 1. Set options to ? CoerceOptionsToObject(options).\n    let options = coerce_options_to_object(options, context)?;\n\n    // 2. Let matcher be ? GetOption(options, \"localeMatcher\", string, « \"lookup\", \"best fit\" », \"best fit\").\n    let matcher = get_option(&options, js_string!(\"localeMatcher\"), context)?.unwrap_or_default();\n\n    // 3. Let subset be a new empty List.\n    let mut subset = Vec::with_capacity(requested_locales.len());\n\n    // 4. For each element locale of requestedLocales, do\n    for locale in requested_locales {\n        // a. Let noExtensionsLocale be the String value that is locale with any Unicode locale extension sequences removed.\n        let mut no_ext_loc = locale.clone();\n        no_ext_loc.extensions.unicode.clear();\n        let loc_match = match matcher {\n            // b. If matcher is \"lookup\", then\n            //     i. Let match be LookupMatchingLocaleByPrefix(availableLocales, noExtensionsLocale).\n            LocaleMatcher::Lookup => {\n                lookup_matching_locale_by_prefix::<S>([no_ext_loc], context.intl_provider())\n            }\n            // c. Else,\n            //     i. Let match be LookupMatchingLocaleByBestFit(availableLocales, noExtensionsLocale).\n            LocaleMatcher::BestFit => {\n                lookup_matching_locale_by_best_fit::<S>([no_ext_loc], context.intl_provider())\n            }\n        };\n\n        // d. If match is not undefined, append locale to subset.\n        if loc_match.is_some() {\n            subset.push(locale);\n        }\n    }\n\n    // 5. Return CreateArrayFromList(subset).\n    Ok(Array::create_array_from_list(\n        subset\n            .into_iter()\n            .map(|loc| js_string!(loc.to_string()).into()),\n        context,\n    ))\n}\n\n/// Validates that the unicode extension `key` with `value` is a valid extension value for the\n/// `language`.\n///\n/// # Note\n///\n/// Calling this function with a singleton `DataMarker` will always return `None`.\npub(in crate::builtins::intl) fn validate_extension<M: DataMarker>(\n    language: &LanguageIdentifier,\n    attributes: &DataMarkerAttributes,\n    provider: &impl DryDataProvider<M>,\n) -> bool {\n    let locale = DataLocale::from(language);\n    let req = DataRequest {\n        id: DataIdentifierBorrowed::for_marker_attributes_and_locale(attributes, &locale),\n        metadata: {\n            let mut metadata = DataRequestMetadata::default();\n            metadata.silent = true;\n            metadata\n        },\n    };\n\n    provider.dry_load(req).is_ok()\n}\n\n#[cfg(all(test, feature = \"intl_bundled\"))]\nmod tests {\n    use icu_locale::{Locale, langid, locale};\n    use icu_plurals::provider::PluralsCardinalV1;\n\n    struct TestService;\n\n    impl Service for TestService {\n        type LangMarker = PluralsCardinalV1;\n        type Preferences = EmptyPreferences;\n    }\n\n    use crate::{\n        builtins::intl::{\n            Service,\n            locale::utils::{lookup_matching_locale_by_best_fit, lookup_matching_locale_by_prefix},\n            options::EmptyPreferences,\n        },\n        context::icu::IntlProvider,\n    };\n\n    #[test]\n    fn best_fit() {\n        let icu = &IntlProvider::try_new_buffer(boa_icu_provider::buffer());\n\n        assert_eq!(\n            lookup_matching_locale_by_best_fit::<TestService>([locale!(\"en\")], icu),\n            Some(locale!(\"en\"))\n        );\n\n        assert_eq!(\n            lookup_matching_locale_by_best_fit::<TestService>([locale!(\"es-ES\")], icu),\n            Some(locale!(\"es\"))\n        );\n\n        assert_eq!(\n            lookup_matching_locale_by_best_fit::<TestService>([locale!(\"kr\")], icu),\n            None\n        );\n    }\n\n    #[test]\n    fn lookup_match() {\n        let icu = &IntlProvider::try_new_buffer(boa_icu_provider::buffer());\n\n        // requested: [fr-FR-u-hc-h12]\n        let requested: Locale = \"fr-FR-u-hc-h12\".parse().unwrap();\n\n        let result =\n            lookup_matching_locale_by_prefix::<TestService>([requested.clone()], icu).unwrap();\n        assert_eq!(result.id, langid!(\"fr\"));\n        assert_eq!(result.extensions, requested.extensions);\n\n        // requested: [kr-KR-u-hc-h12, gr-GR-u-hc-h24-x-4a, es-ES-valencia-u-ca-gregory, uz-Cyrl]\n        let kr = \"kr-KR-u-hc-h12\".parse().unwrap();\n        let gr = \"gr-GR-u-hc-h24-x-4a\".parse().unwrap();\n        let es: Locale = \"es-ES-valencia-u-ca-gregory\".parse().unwrap();\n        let uz = locale!(\"uz-Cyrl\");\n        let requested = vec![kr, gr, es.clone(), uz];\n\n        let res = lookup_matching_locale_by_best_fit::<TestService>(requested, icu).unwrap();\n        assert_eq!(res.id, langid!(\"es\"));\n        assert_eq!(res.extensions, es.extensions);\n    }\n}\n"
  },
  {
    "path": "core/engine/src/builtins/intl/mod.rs",
    "content": "//! Boa's implementation of ECMAScript's global `Intl` object.\n//!\n//! The `Intl` namespace object contains several constructors as well as functionality common to the\n//! internationalization constructors and other language sensitive functions. Collectively, they\n//! comprise the ECMAScript Internationalization API, which provides language sensitive string\n//! comparison, number formatting, date and time formatting, and more.\n//!\n//! More information:\n//!  - [ECMAScript reference][spec]\n//!  - [MDN documentation][mdn]\n//!\n//!\n//! [spec]: https://tc39.es/ecma402/#intl-object\n//! [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl\n\nuse crate::{\n    Context, JsArgs, JsData, JsResult, JsString, JsValue,\n    builtins::{Array, BuiltInBuilder, BuiltInObject, IntrinsicObject},\n    context::{icu::IntlProvider, intrinsics::Intrinsics},\n    js_string,\n    object::JsObject,\n    property::Attribute,\n    realm::Realm,\n    string::StaticJsStrings,\n    symbol::JsSymbol,\n};\n\nuse boa_gc::{Finalize, Trace};\nuse icu_locale::{LanguageIdentifier, extensions::unicode};\nuse icu_provider::{DataMarker, DataMarkerAttributes};\nuse static_assertions::const_assert;\n\npub(crate) use self::{\n    collator::Collator, date_time_format::DateTimeFormat, list_format::ListFormat, locale::Locale,\n    number_format::NumberFormat, plural_rules::PluralRules, segmenter::Segmenter,\n};\n\n/// Macro to easily implement `ServicePreferences`.\n///\n/// This macro receives a list of fields, and adds the methods to\n/// correctly implement `ServicePreferences` from the provided fields.\nmacro_rules! impl_service_preferences {\n    ($($field:ident),*) => {\n        fn extended(&self, other: &Self) -> Self {\n            let mut result = *self;\n            result.extend(*other);\n            result\n        }\n\n        fn as_unicode(&self) -> unicode::Unicode {\n            let mut exts = unicode::Unicode::new();\n\n            $(\n                if let Some(key) = &self.$field\n                    && let Some((key, value)) = $crate::builtins::intl::get_kv_from_pref(key)\n                {\n                    exts.keywords.set(key, value);\n                }\n            )*\n\n            exts\n        }\n\n        fn intersection(&self, other: &Self) -> Self {\n            let mut inter = *self;\n            if inter.locale_preferences != other.locale_preferences {\n                inter.locale_preferences = LocalePreferences::default();\n            }\n\n            $(\n                if inter.$field != other.$field {\n                    inter.$field.take();\n                }\n            )*\n\n            inter\n        }\n    };\n}\n\npub(crate) mod collator;\npub(crate) mod date_time_format;\npub(crate) mod list_format;\npub(crate) mod locale;\npub(crate) mod number_format;\npub(crate) mod plural_rules;\npub(crate) mod segmenter;\n\nmod options;\n\n// No singletons are allowed as lang markers.\n// Hopefully, we'll be able to migrate this to the definition of `Service` in the future\n// (https://github.com/rust-lang/rust/issues/76560)\nconst_assert! {!<Collator as Service>::LangMarker::INFO.is_singleton}\nconst_assert! {!<ListFormat as Service>::LangMarker::INFO.is_singleton}\nconst_assert! {!<NumberFormat as Service>::LangMarker::INFO.is_singleton}\nconst_assert! {!<PluralRules as Service>::LangMarker::INFO.is_singleton}\nconst_assert! {!<Segmenter as Service>::LangMarker::INFO.is_singleton}\nconst_assert! {!<DateTimeFormat as Service>::LangMarker::INFO.is_singleton}\n\n/// JavaScript `Intl` object.\n#[derive(Debug, Clone, Trace, Finalize, JsData)]\n#[boa_gc(unsafe_empty_trace)]\npub struct Intl {\n    fallback_symbol: JsSymbol,\n}\n\nimpl Intl {\n    /// Gets this realm's `Intl` object's `[[FallbackSymbol]]` slot.\n    #[must_use]\n    pub fn fallback_symbol(&self) -> JsSymbol {\n        self.fallback_symbol.clone()\n    }\n\n    pub(crate) fn new() -> Option<Self> {\n        let fallback_symbol = JsSymbol::new(Some(js_string!(\"IntlLegacyConstructedSymbol\")))?;\n        Some(Self { fallback_symbol })\n    }\n}\n\nimpl IntrinsicObject for Intl {\n    fn init(realm: &Realm) {\n        BuiltInBuilder::with_intrinsic::<Self>(realm)\n            .static_property(\n                JsSymbol::to_string_tag(),\n                Self::NAME,\n                Attribute::CONFIGURABLE,\n            )\n            .static_property(\n                Collator::NAME,\n                realm.intrinsics().constructors().collator().constructor(),\n                Collator::ATTRIBUTE,\n            )\n            .static_property(\n                ListFormat::NAME,\n                realm\n                    .intrinsics()\n                    .constructors()\n                    .list_format()\n                    .constructor(),\n                ListFormat::ATTRIBUTE,\n            )\n            .static_property(\n                Locale::NAME,\n                realm.intrinsics().constructors().locale().constructor(),\n                Locale::ATTRIBUTE,\n            )\n            .static_property(\n                Segmenter::NAME,\n                realm.intrinsics().constructors().segmenter().constructor(),\n                Segmenter::ATTRIBUTE,\n            )\n            .static_property(\n                PluralRules::NAME,\n                realm\n                    .intrinsics()\n                    .constructors()\n                    .plural_rules()\n                    .constructor(),\n                PluralRules::ATTRIBUTE,\n            )\n            .static_property(\n                DateTimeFormat::NAME,\n                realm\n                    .intrinsics()\n                    .constructors()\n                    .date_time_format()\n                    .constructor(),\n                DateTimeFormat::ATTRIBUTE,\n            )\n            .static_property(\n                NumberFormat::NAME,\n                realm\n                    .intrinsics()\n                    .constructors()\n                    .number_format()\n                    .constructor(),\n                NumberFormat::ATTRIBUTE,\n            )\n            .static_method(\n                Self::get_canonical_locales,\n                js_string!(\"getCanonicalLocales\"),\n                1,\n            )\n            .build();\n    }\n\n    fn get(intrinsics: &Intrinsics) -> JsObject {\n        intrinsics.objects().intl().upcast()\n    }\n}\n\nimpl BuiltInObject for Intl {\n    const NAME: JsString = StaticJsStrings::INTL;\n}\n\nimpl Intl {\n    /// `Intl.getCanonicalLocales ( locales )`\n    ///\n    /// Returns an array containing the canonical locale names.\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///  - [MDN docs][mdn]\n    ///\n    /// [spec]: https://tc39.es/ecma402/#sec-intl.getcanonicallocales\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/getCanonicalLocales\n    pub(crate) fn get_canonical_locales(\n        _: &JsValue,\n        args: &[JsValue],\n        context: &mut Context,\n    ) -> JsResult<JsValue> {\n        let locales = args.get_or_undefined(0);\n\n        // 1. Let ll be ? CanonicalizeLocaleList(locales).\n        let ll = locale::canonicalize_locale_list(locales, context)?;\n\n        // 2. Return CreateArrayFromList(ll).\n        Ok(JsValue::new(Array::create_array_from_list(\n            ll.into_iter().map(|loc| js_string!(loc.to_string()).into()),\n            context,\n        )))\n    }\n}\n\nfn get_kv_from_pref<T: icu_locale::preferences::PreferenceKey>(\n    pref: &T,\n) -> Option<(unicode::Key, unicode::Value)> {\n    T::unicode_extension_key().zip(pref.unicode_extension_value())\n}\n\n/// A set of preferences that can be provided to a [`Service`] through\n/// a locale.\ntrait ServicePreferences: for<'a> From<&'a icu_locale::Locale> + Clone {\n    /// Validates that every preference value is available.\n    ///\n    /// This usually entails having to query the `IntlProvider` to check\n    /// if it has the required data to support the requested values.\n    fn validate(&mut self, id: &LanguageIdentifier, provider: &IntlProvider);\n\n    /// Converts this set of preferences into a Unicode locale extension.\n    fn as_unicode(&self) -> unicode::Unicode;\n\n    /// Extends all values set in `self` with the values set in `other`.\n    fn extended(&self, other: &Self) -> Self;\n\n    /// Gets the set of preference values that are the same in `self` and `other`.\n    fn intersection(&self, other: &Self) -> Self;\n}\n\n/// A service component that is part of the `Intl` API.\n///\n/// This needs to be implemented for every `Intl` service in order to use the functions\n/// defined in `locale::utils`, such as [`resolve_locale`][locale::resolve_locale].\ntrait Service {\n    /// The data marker used to decide which locales are supported by this service.\n    type LangMarker: DataMarker;\n\n    /// The attributes used to resolve the locale.\n    const ATTRIBUTES: &'static DataMarkerAttributes = DataMarkerAttributes::empty();\n\n    /// The set of preferences used to resolve the provided locale.\n    type Preferences: ServicePreferences;\n}\n"
  },
  {
    "path": "core/engine/src/builtins/intl/number_format/mod.rs",
    "content": "use std::cell::Cell;\n\nuse boa_gc::{Finalize, Trace};\nuse fixed_decimal::{Decimal, FloatPrecision, SignDisplay};\nuse icu_decimal::{\n    DecimalFormatter, DecimalFormatterPreferences, FormattedDecimal,\n    options::{DecimalFormatterOptions, GroupingStrategy},\n    preferences::NumberingSystem,\n    provider::{DecimalDigitsV1, DecimalSymbolsV1},\n};\n\nmod options;\nuse icu_locale::{Locale, extensions::unicode::Value};\nuse icu_provider::{DataMarker, DataMarkerAttributes, DynamicDataProvider, buf::BufferMarker};\nuse num_bigint::BigInt;\nuse num_traits::Num;\npub(crate) use options::*;\n\nuse super::{\n    Service,\n    locale::{canonicalize_locale_list, filter_locales, resolve_locale},\n    options::{IntlOptions, coerce_options_to_object},\n};\nuse crate::{\n    Context, JsArgs, JsData, JsNativeError, JsObject, JsResult, JsString, JsSymbol, JsValue,\n    NativeFunction,\n    builtins::{\n        BuiltInConstructor, BuiltInObject, IntrinsicObject, builder::BuiltInBuilder,\n        options::get_option,\n    },\n    context::intrinsics::{Intrinsics, StandardConstructor, StandardConstructors},\n    js_string,\n    object::{\n        FunctionObjectBuilder, JsFunction, ObjectInitializer,\n        internal_methods::get_prototype_from_constructor,\n    },\n    property::{Attribute, PropertyDescriptor},\n    realm::Realm,\n    string::StaticJsStrings,\n    value::PreferredType,\n};\nuse crate::{js_error, value::JsVariant};\n\n#[cfg(test)]\nmod tests;\n\n#[derive(Debug, Trace, Finalize, JsData)]\n// Safety: `NumberFormat` only contains non-traceable types.\n#[boa_gc(unsafe_empty_trace)]\npub(crate) struct NumberFormat {\n    locale: Locale,\n    formatter: DecimalFormatter,\n    numbering_system: NumberingSystem,\n    unit_options: UnitFormatOptions,\n    digit_options: DigitFormatOptions,\n    notation: Notation,\n    use_grouping: GroupingStrategy,\n    sign_display: SignDisplay,\n    bound_format: Option<JsFunction>,\n}\n\nimpl NumberFormat {\n    /// [`FormatNumeric ( numberFormat, x )`][full] and [`FormatNumericToParts ( numberFormat, x )`][parts].\n    ///\n    /// The returned struct implements `Writable`, allowing to either write the number as a full\n    /// string or by parts.\n    ///\n    /// [full]: https://tc39.es/ecma402/#sec-formatnumber\n    /// [parts]: https://tc39.es/ecma402/#sec-formatnumbertoparts\n    pub(crate) fn format<'a>(&'a self, value: &'a mut Decimal) -> FormattedDecimal<'a> {\n        // TODO: Missing support from ICU4X for Percent/Currency/Unit formatting.\n        // TODO: Missing support from ICU4X for Scientific/Engineering/Compact notation.\n\n        self.digit_options.format_fixed_decimal(value);\n        value.apply_sign_display(self.sign_display);\n\n        self.formatter.format(value)\n    }\n}\n\nimpl Service for NumberFormat {\n    type LangMarker = DecimalSymbolsV1;\n\n    type Preferences = DecimalFormatterPreferences;\n}\n\nimpl IntrinsicObject for NumberFormat {\n    fn init(realm: &Realm) {\n        let get_format = BuiltInBuilder::callable(realm, Self::get_format)\n            .name(js_string!(\"get format\"))\n            .build();\n\n        BuiltInBuilder::from_standard_constructor::<Self>(realm)\n            .static_method(\n                Self::supported_locales_of,\n                js_string!(\"supportedLocalesOf\"),\n                1,\n            )\n            .property(\n                JsSymbol::to_string_tag(),\n                js_string!(\"Intl.NumberFormat\"),\n                Attribute::CONFIGURABLE,\n            )\n            .accessor(\n                js_string!(\"format\"),\n                Some(get_format),\n                None,\n                Attribute::CONFIGURABLE,\n            )\n            .method(Self::resolved_options, js_string!(\"resolvedOptions\"), 0)\n            .build();\n    }\n\n    fn get(intrinsics: &Intrinsics) -> JsObject {\n        Self::STANDARD_CONSTRUCTOR(intrinsics.constructors()).constructor()\n    }\n}\n\nimpl BuiltInObject for NumberFormat {\n    const NAME: JsString = StaticJsStrings::NUMBER_FORMAT;\n}\n\nimpl BuiltInConstructor for NumberFormat {\n    const CONSTRUCTOR_ARGUMENTS: usize = 0;\n    const PROTOTYPE_STORAGE_SLOTS: usize = 4;\n    const CONSTRUCTOR_STORAGE_SLOTS: usize = 1;\n\n    const STANDARD_CONSTRUCTOR: fn(&StandardConstructors) -> &StandardConstructor =\n        StandardConstructors::number_format;\n\n    /// [`Intl.NumberFormat ( [ locales [ , options ] ] )`][spec].\n    ///\n    /// [spec]: https://tc39.es/ecma402/#sec-intl.numberformat\n    fn constructor(\n        new_target: &JsValue,\n        args: &[JsValue],\n        context: &mut Context,\n    ) -> JsResult<JsValue> {\n        let locales = args.get_or_undefined(0);\n        let options = args.get_or_undefined(1);\n\n        // 1. If NewTarget is undefined, let newTarget be the active function object, else let newTarget be NewTarget.\n        let new_target_inner = &if new_target.is_undefined() {\n            context\n                .active_function_object()\n                .unwrap_or_else(|| {\n                    context\n                        .intrinsics()\n                        .constructors()\n                        .number_format()\n                        .constructor()\n                })\n                .into()\n        } else {\n            new_target.clone()\n        };\n\n        // 2. Let numberFormat be ? OrdinaryCreateFromConstructor(newTarget, \"%Intl.NumberFormat.prototype%\", « [[InitializedNumberFormat]], [[Locale]], [[DataLocale]], [[NumberingSystem]], [[Style]], [[Unit]], [[UnitDisplay]], [[Currency]], [[CurrencyDisplay]], [[CurrencySign]], [[MinimumIntegerDigits]], [[MinimumFractionDigits]], [[MaximumFractionDigits]], [[MinimumSignificantDigits]], [[MaximumSignificantDigits]], [[RoundingType]], [[Notation]], [[CompactDisplay]], [[UseGrouping]], [[SignDisplay]], [[RoundingIncrement]], [[RoundingMode]], [[ComputedRoundingPriority]], [[TrailingZeroDisplay]], [[BoundFormat]] »).\n        let prototype = get_prototype_from_constructor(\n            new_target_inner,\n            StandardConstructors::number_format,\n            context,\n        )?;\n\n        let number_format = Self::new(locales, options, context)?;\n\n        let number_format = JsObject::from_proto_and_data_with_shared_shape(\n            context.root_shape(),\n            prototype,\n            number_format,\n        );\n\n        // 31. Return unused.\n\n        // 4. If the implementation supports the normative optional constructor mode of 4.3 Note 1, then\n        //     a. Let this be the this value.\n        //     b. Return ? ChainNumberFormat(numberFormat, NewTarget, this).\n        // ChainNumberFormat ( numberFormat, newTarget, this )\n        // <https://tc39.es/ecma402/#sec-chainnumberformat>\n\n        let this = context.vm.stack.get_this(context.vm.frame());\n        let Some(this_obj) = this.as_object() else {\n            return Ok(number_format.into());\n        };\n\n        let constructor = context\n            .intrinsics()\n            .constructors()\n            .number_format()\n            .constructor();\n\n        // 1. If newTarget is undefined and ? OrdinaryHasInstance(%Intl.NumberFormat%, this) is true, then\n        if new_target.is_undefined()\n            && JsValue::ordinary_has_instance(&constructor.into(), &this, context)?\n        {\n            let fallback_symbol = context\n                .intrinsics()\n                .objects()\n                .intl()\n                .borrow()\n                .data()\n                .fallback_symbol();\n\n            // a. Perform ? DefinePropertyOrThrow(this, %Intl%.[[FallbackSymbol]], PropertyDescriptor{ [[Value]]: numberFormat, [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: false }).\n            this_obj.define_property_or_throw(\n                fallback_symbol,\n                PropertyDescriptor::builder()\n                    .value(number_format)\n                    .writable(false)\n                    .enumerable(false)\n                    .configurable(false),\n                context,\n            )?;\n            // b. Return this.\n            Ok(this)\n        } else {\n            // 2. Return numberFormat.\n            Ok(number_format.into())\n        }\n    }\n}\n\nimpl NumberFormat {\n    /// Creates a new instance of `NumberFormat`.\n    pub(crate) fn new(\n        locales: &JsValue,\n        options: &JsValue,\n        context: &mut Context,\n    ) -> JsResult<Self> {\n        // 3. Perform ? InitializeNumberFormat(numberFormat, locales, options).\n\n        // `InitializeNumberFormat ( numberFormat, locales, options )`\n        // https://tc39.es/ecma402/#sec-initializenumberformat\n\n        // 1. Let requestedLocales be ? CanonicalizeLocaleList(locales).\n        let requested_locales = canonicalize_locale_list(locales, context)?;\n        // 2. Set options to ? CoerceOptionsToObject(options).\n        let options = coerce_options_to_object(options, context)?;\n\n        // 3. Let opt be a new Record.\n\n        // 4. Let matcher be ? GetOption(options, \"localeMatcher\", string, « \"lookup\", \"best fit\" », \"best fit\").\n        // 5. Set opt.[[localeMatcher]] to matcher.\n        let matcher =\n            get_option(&options, js_string!(\"localeMatcher\"), context)?.unwrap_or_default();\n\n        // 6. Let numberingSystem be ? GetOption(options, \"numberingSystem\", string, empty, undefined).\n        // 7. If numberingSystem is not undefined, then\n        //     a. If numberingSystem cannot be matched by the type Unicode locale nonterminal, throw a RangeError exception.\n        // 8. Set opt.[[nu]] to numberingSystem.\n        let numbering_system =\n            get_option::<NumberingSystem>(&options, js_string!(\"numberingSystem\"), context)?;\n\n        let mut intl_options = IntlOptions {\n            matcher,\n            preferences: {\n                let mut prefs = DecimalFormatterPreferences::default();\n                prefs.numbering_system = numbering_system;\n                prefs\n            },\n        };\n\n        // 9. Let localeData be %Intl.NumberFormat%.[[LocaleData]].\n        // 10. Let r be ResolveLocale(%Intl.NumberFormat%.[[AvailableLocales]], requestedLocales, opt, %Intl.NumberFormat%.[[RelevantExtensionKeys]], localeData).\n        let locale = resolve_locale::<Self>(\n            requested_locales,\n            &mut intl_options,\n            context.intl_provider(),\n        )?;\n\n        // 11. Set numberFormat.[[Locale]] to r.[[locale]].\n        // 12. Set numberFormat.[[DataLocale]] to r.[[dataLocale]].\n        // 13. Set numberFormat.[[NumberingSystem]] to r.[[nu]].\n\n        // 14. Perform ? SetNumberFormatUnitOptions(numberFormat, options).\n        let unit_options = UnitFormatOptions::from_options(&options, context)?;\n\n        // 15. Let style be numberFormat.[[Style]].\n        // 16. If style is \"currency\", then\n        let (min_fractional, max_fractional) = if unit_options.style() == Style::Currency {\n            // TODO: Missing support from ICU4X\n            // a. Let currency be numberFormat.[[Currency]].\n            // b. Let cDigits be CurrencyDigits(currency).\n            // c. Let mnfdDefault be cDigits.\n            // d. Let mxfdDefault be cDigits.\n            return Err(JsNativeError::typ().with_message(\"unimplemented\").into());\n        } else {\n            // 17. Else,\n            (\n                // a. Let mnfdDefault be 0.\n                0,\n                // b. If style is \"percent\", then\n                if unit_options.style() == Style::Percent {\n                    // i. Let mxfdDefault be 0.\n                    0\n                } else {\n                    // c. Else,\n                    //    i. Let mxfdDefault be 3.\n                    3\n                },\n            )\n        };\n\n        // 18. Let notation be ? GetOption(options, \"notation\", string, « \"standard\", \"scientific\", \"engineering\", \"compact\" », \"standard\").\n        // 19. Set numberFormat.[[Notation]] to notation.\n        let notation = get_option(&options, js_string!(\"notation\"), context)?.unwrap_or_default();\n\n        // 20. Perform ? SetNumberFormatDigitOptions(numberFormat, options, mnfdDefault, mxfdDefault, notation).\n        let digit_options = DigitFormatOptions::from_options(\n            &options,\n            min_fractional,\n            max_fractional,\n            notation,\n            context,\n        )?;\n\n        // 21. Let compactDisplay be ? GetOption(options, \"compactDisplay\", string, « \"short\", \"long\" », \"short\").\n        let compact_display =\n            get_option(&options, js_string!(\"compactDisplay\"), context)?.unwrap_or_default();\n\n        // 22. Let defaultUseGrouping be \"auto\".\n        let mut default_use_grouping = GroupingStrategy::Auto;\n\n        let notation = match notation {\n            NotationKind::Standard => Notation::Standard,\n            NotationKind::Scientific => Notation::Scientific,\n            NotationKind::Engineering => Notation::Engineering,\n            // 23. If notation is \"compact\", then\n            NotationKind::Compact => {\n                // b. Set defaultUseGrouping to \"min2\".\n                default_use_grouping = GroupingStrategy::Min2;\n\n                // a. Set numberFormat.[[CompactDisplay]] to compactDisplay.\n                Notation::Compact {\n                    display: compact_display,\n                }\n            }\n        };\n\n        // 24. NOTE: For historical reasons, the strings \"true\" and \"false\" are accepted and replaced with the default value.\n        // 25. Let useGrouping be ? GetBooleanOrStringNumberFormatOption(options, \"useGrouping\",\n        //     « \"min2\", \"auto\", \"always\", \"true\", \"false\" », defaultUseGrouping).\n        // 26. If useGrouping is \"true\" or useGrouping is \"false\", set useGrouping to defaultUseGrouping.\n        // 27. If useGrouping is true, set useGrouping to \"always\".\n        // 28. Set numberFormat.[[UseGrouping]] to useGrouping.\n        // useGrouping requires special handling because of the \"true\" and \"false\" exceptions.\n        // We could also modify the `OptionType` interface but it complicates it a lot just for\n        // a single exception.\n        let use_grouping = 'block: {\n            // GetBooleanOrStringNumberFormatOption ( options, property, stringValues, fallback )\n            // <https://tc39.es/ecma402/#sec-getbooleanorstringnumberformatoption>\n\n            // 1. Let value be ? Get(options, property).\n            let value = options.get(js_string!(\"useGrouping\"), context)?;\n\n            // 2. If value is undefined, return fallback.\n            if value.is_undefined() {\n                break 'block default_use_grouping;\n            }\n            // 3. If value is true, return true.\n            if let Some(true) = value.as_boolean() {\n                break 'block GroupingStrategy::Always;\n            }\n\n            // 4. If ToBoolean(value) is false, return false.\n            if !value.to_boolean() {\n                break 'block GroupingStrategy::Never;\n            }\n\n            // 5. Set value to ? ToString(value).\n            // 6. If stringValues does not contain value, throw a RangeError exception.\n            // 7. Return value.\n            match value.to_string(context)?.to_std_string_escaped().as_str() {\n                \"min2\" => GroupingStrategy::Min2,\n                \"auto\" => GroupingStrategy::Auto,\n                \"always\" => GroupingStrategy::Always,\n                // special handling for historical reasons\n                \"true\" | \"false\" => default_use_grouping,\n                _ => {\n                    return Err(JsNativeError::range()\n                        .with_message(\n                            \"expected one of `min2`, `auto`, `always`, `true`, or `false`\",\n                        )\n                        .into());\n                }\n            }\n        };\n\n        // 29. Let signDisplay be ? GetOption(options, \"signDisplay\", string, « \"auto\", \"never\", \"always\", \"exceptZero\", \"negative\" », \"auto\").\n        // 30. Set numberFormat.[[SignDisplay]] to signDisplay.\n        let sign_display =\n            get_option(&options, js_string!(\"signDisplay\"), context)?.unwrap_or(SignDisplay::Auto);\n\n        let mut options = DecimalFormatterOptions::default();\n        options.grouping_strategy = Some(use_grouping);\n\n        let (formatter, numbering_system) = {\n            struct RequestInspector<'a> {\n                inner: &'a dyn DynamicDataProvider<BufferMarker>,\n                nu: Cell<Option<Box<DataMarkerAttributes>>>,\n            }\n            impl DynamicDataProvider<BufferMarker> for RequestInspector<'_> {\n                fn load_data(\n                    &self,\n                    marker: icu_provider::DataMarkerInfo,\n                    req: icu_provider::DataRequest<'_>,\n                ) -> Result<icu_provider::DataResponse<BufferMarker>, icu_provider::DataError>\n                {\n                    if marker.id == DecimalDigitsV1::INFO.id {\n                        self.nu.set(Some(req.id.marker_attributes.to_owned()));\n                    }\n                    self.inner.load_data(marker, req)\n                }\n            }\n\n            let inspector = RequestInspector {\n                inner: context.intl_provider().erased_provider(),\n                nu: Cell::new(None),\n            };\n            let formatter = DecimalFormatter::try_new_with_buffer_provider(\n                &inspector,\n                (&locale).into(),\n                options,\n            )\n            .map_err(|err| JsNativeError::typ().with_message(err.to_string()))?;\n\n            let nu = (|| {\n                let nu = inspector.nu.into_inner()?;\n                let nu = Value::try_from_str(&nu).ok()?;\n                NumberingSystem::try_from(nu).ok()\n            })()\n            .ok_or_else(|| {\n                js_error!(\n                    TypeError: \"could not obtain resolved numbering system from Intl provider\"\n                )\n            })?;\n\n            (formatter, nu)\n        };\n\n        Ok(NumberFormat {\n            locale,\n            numbering_system,\n            formatter,\n            unit_options,\n            digit_options,\n            notation,\n            use_grouping,\n            sign_display,\n            bound_format: None,\n        })\n    }\n\n    /// [`Intl.NumberFormat.supportedLocalesOf ( locales [ , options ] )`][spec].\n    ///\n    /// Returns an array containing those of the provided locales that are supported in number format\n    /// without having to fall back to the runtime's default locale.\n    ///\n    /// More information:\n    ///  - [MDN documentation][mdn]\n    ///\n    /// [spec]: https://tc39.es/ecma402/#sec-intl.numberformat.supportedlocalesof\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/NumberFormat/supportedLocalesOf\n    fn supported_locales_of(\n        _: &JsValue,\n        args: &[JsValue],\n        context: &mut Context,\n    ) -> JsResult<JsValue> {\n        let locales = args.get_or_undefined(0);\n        let options = args.get_or_undefined(1);\n\n        // 1. Let availableLocales be %Intl.NumberFormat%.[[AvailableLocales]].\n        // 2. Let requestedLocales be ? CanonicalizeLocaleList(locales).\n        let requested_locales = canonicalize_locale_list(locales, context)?;\n\n        // 3. Return ? FilterLocales(availableLocales, requestedLocales, options).\n        filter_locales::<Self>(requested_locales, options, context).map(JsValue::from)\n    }\n\n    /// [`get Intl.NumberFormat.prototype.format`][spec].\n    ///\n    /// [spec]: https://tc39.es/ecma402/#sec-intl.numberformat.prototype.format\n    fn get_format(this: &JsValue, _: &[JsValue], context: &mut Context) -> JsResult<JsValue> {\n        // 1. Let nf be the this value.\n        // 2. If the implementation supports the normative optional constructor mode of 4.3 Note 1, then\n        //     a. Set nf to ? UnwrapNumberFormat(nf).\n        // 3. Perform ? RequireInternalSlot(nf, [[InitializedNumberFormat]]).\n        let nf = unwrap_number_format(this, context)?;\n        let nf_clone = nf.clone();\n        let mut nf = nf.borrow_mut();\n\n        let bound_format = if let Some(f) = nf.data_mut().bound_format.clone() {\n            f\n        } else {\n            // 4. If nf.[[BoundFormat]] is undefined, then\n            //     a. Let F be a new built-in function object as defined in Number Format Functions (15.5.2).\n            //     b. Set F.[[NumberFormat]] to nf.\n            //     c. Set nf.[[BoundFormat]] to F.\n            let bound_format = FunctionObjectBuilder::new(\n                context.realm(),\n                // Number Format Functions\n                // <https://tc39.es/ecma402/#sec-number-format-functions>\n                NativeFunction::from_copy_closure_with_captures(\n                    |_, args, nf, context| {\n                        // 1. Let nf be F.[[NumberFormat]].\n                        // 2. Assert: Type(nf) is Object and nf has an [[InitializedNumberFormat]] internal slot.\n\n                        // 3. If value is not provided, let value be undefined.\n                        let value = args.get_or_undefined(0);\n\n                        // 4. Let x be ? ToIntlMathematicalValue(value).\n                        let mut x = to_intl_mathematical_value(value, context)?;\n\n                        // 5. Return FormatNumeric(nf, x).\n                        Ok(js_string!(nf.borrow().data().format(&mut x).to_string()).into())\n                    },\n                    nf_clone,\n                ),\n            )\n            .length(2)\n            .build();\n\n            nf.data_mut().bound_format = Some(bound_format.clone());\n            bound_format\n        };\n\n        // 5. Return nf.[[BoundFormat]].\n        Ok(bound_format.into())\n    }\n\n    /// [`Intl.NumberFormat.prototype.resolvedOptions ( )`][spec].\n    ///\n    /// Returns a new object with properties reflecting the locale and options computed during the\n    /// construction of the current `Intl.NumberFormat` object.\n    ///\n    /// More information:\n    ///  - [MDN documentation][mdn]\n    ///\n    /// [spec]: https://tc39.es/ecma402/#sec-intl.numberformat.prototype.resolvedoptions\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/NumberFormat/resolvedOptions\n    fn resolved_options(this: &JsValue, _: &[JsValue], context: &mut Context) -> JsResult<JsValue> {\n        // This function provides access to the locale and options computed during initialization of the object.\n\n        // 1. Let nf be the this value.\n        // 2. If the implementation supports the normative optional constructor mode of 4.3 Note 1, then\n        //     a. Set nf to ? UnwrapNumberFormat(nf).\n        // 3. Perform ? RequireInternalSlot(nf, [[InitializedNumberFormat]]).\n        let nf = unwrap_number_format(this, context)?;\n        let nf = nf.borrow();\n        let nf = nf.data();\n\n        // 4. Let options be OrdinaryObjectCreate(%Object.prototype%).\n        // 5. For each row of Table 12, except the header row, in table order, do\n        //     a. Let p be the Property value of the current row.\n        //     b. Let v be the value of nf's internal slot whose name is the Internal Slot value of the current row.\n        //     c. If v is not undefined, then\n        //         i. If there is a Conversion value in the current row, then\n        //             1. Assert: The Conversion value of the current row is number.\n        //             2. Set v to 𝔽(v).\n        //         ii. Perform ! CreateDataPropertyOrThrow(options, p, v).\n        let mut options = ObjectInitializer::new(context);\n        options.property(\n            js_string!(\"locale\"),\n            js_string!(nf.locale.to_string()),\n            Attribute::all(),\n        );\n        options.property(\n            js_string!(\"numberingSystem\"),\n            js_string!(nf.numbering_system.as_str()),\n            Attribute::all(),\n        );\n\n        options.property(\n            js_string!(\"style\"),\n            nf.unit_options.style().to_js_string(),\n            Attribute::all(),\n        );\n\n        match &nf.unit_options {\n            UnitFormatOptions::Currency {\n                currency,\n                display,\n                sign,\n            } => {\n                options.property(\n                    js_string!(\"currency\"),\n                    currency.to_js_string(),\n                    Attribute::all(),\n                );\n                options.property(\n                    js_string!(\"currencyDisplay\"),\n                    display.to_js_string(),\n                    Attribute::all(),\n                );\n                options.property(\n                    js_string!(\"currencySign\"),\n                    sign.to_js_string(),\n                    Attribute::all(),\n                );\n            }\n            UnitFormatOptions::Unit { unit, display } => {\n                options.property(js_string!(\"unit\"), unit.to_js_string(), Attribute::all());\n                options.property(\n                    js_string!(\"unitDisplay\"),\n                    display.to_js_string(),\n                    Attribute::all(),\n                );\n            }\n            UnitFormatOptions::Decimal | UnitFormatOptions::Percent => {}\n        }\n\n        options.property(\n            js_string!(\"minimumIntegerDigits\"),\n            nf.digit_options.minimum_integer_digits,\n            Attribute::all(),\n        );\n\n        if let Some(Extrema { minimum, maximum }) = nf.digit_options.rounding_type.fraction_digits()\n        {\n            options\n                .property(\n                    js_string!(\"minimumFractionDigits\"),\n                    minimum,\n                    Attribute::all(),\n                )\n                .property(\n                    js_string!(\"maximumFractionDigits\"),\n                    maximum,\n                    Attribute::all(),\n                );\n        }\n\n        if let Some(Extrema { minimum, maximum }) =\n            nf.digit_options.rounding_type.significant_digits()\n        {\n            options\n                .property(\n                    js_string!(\"minimumSignificantDigits\"),\n                    minimum,\n                    Attribute::all(),\n                )\n                .property(\n                    js_string!(\"maximumSignificantDigits\"),\n                    maximum,\n                    Attribute::all(),\n                );\n        }\n\n        let use_grouping = match nf.use_grouping {\n            GroupingStrategy::Auto => js_string!(\"auto\").into(),\n            GroupingStrategy::Never => JsValue::from(false),\n            GroupingStrategy::Always => js_string!(\"always\").into(),\n            GroupingStrategy::Min2 => js_string!(\"min2\").into(),\n            _ => {\n                return Err(JsNativeError::typ()\n                    .with_message(\"unsupported useGrouping value\")\n                    .into());\n            }\n        };\n\n        options\n            .property(js_string!(\"useGrouping\"), use_grouping, Attribute::all())\n            .property(\n                js_string!(\"notation\"),\n                nf.notation.kind().to_js_string(),\n                Attribute::all(),\n            );\n\n        if let Notation::Compact { display } = nf.notation {\n            options.property(\n                js_string!(\"compactDisplay\"),\n                display.to_js_string(),\n                Attribute::all(),\n            );\n        }\n\n        let sign_display = match nf.sign_display {\n            SignDisplay::Auto => js_string!(\"auto\"),\n            SignDisplay::Never => js_string!(\"never\"),\n            SignDisplay::Always => js_string!(\"always\"),\n            SignDisplay::ExceptZero => js_string!(\"exceptZero\"),\n            SignDisplay::Negative => js_string!(\"negative\"),\n            _ => {\n                return Err(JsNativeError::typ()\n                    .with_message(\"unsupported signDisplay value\")\n                    .into());\n            }\n        };\n\n        options\n            .property(js_string!(\"signDisplay\"), sign_display, Attribute::all())\n            .property(\n                js_string!(\"roundingIncrement\"),\n                nf.digit_options.rounding_increment.to_u16(),\n                Attribute::all(),\n            )\n            .property(\n                js_string!(\"roundingPriority\"),\n                nf.digit_options.rounding_priority.to_js_string(),\n                Attribute::all(),\n            )\n            .property(\n                js_string!(\"trailingZeroDisplay\"),\n                nf.digit_options.trailing_zero_display.to_js_string(),\n                Attribute::all(),\n            );\n\n        // 6. Return options.\n        Ok(options.build().into())\n    }\n}\n\n/// Abstract operation [`UnwrapNumberFormat ( nf )`][spec].\n///\n/// This also checks that the returned object is a `NumberFormat`, which skips the\n/// call to `RequireInternalSlot`.\n///\n/// [spec]: https://tc39.es/ecma402/#sec-unwrapnumberformat\nfn unwrap_number_format(nf: &JsValue, context: &mut Context) -> JsResult<JsObject<NumberFormat>> {\n    // 1. If Type(nf) is not Object, throw a TypeError exception.\n    let nf_o = nf.as_object().ok_or_else(|| {\n        JsNativeError::typ().with_message(\"value was not an `Intl.NumberFormat` object\")\n    })?;\n\n    if let Ok(nf) = nf_o.clone().downcast::<NumberFormat>() {\n        // 3. Return nf.\n        return Ok(nf);\n    }\n\n    // 2. If nf does not have an [[InitializedNumberFormat]] internal slot and ? OrdinaryHasInstance(%Intl.NumberFormat%, nf)\n    //    is true, then\n    let constructor = context\n        .intrinsics()\n        .constructors()\n        .number_format()\n        .constructor();\n    if JsValue::ordinary_has_instance(&constructor.into(), nf, context)? {\n        let fallback_symbol = context\n            .intrinsics()\n            .objects()\n            .intl()\n            .borrow()\n            .data()\n            .fallback_symbol();\n\n        //    a. Return ? Get(nf, %Intl%.[[FallbackSymbol]]).\n        if let Some(nf) = nf_o\n            .get(fallback_symbol, context)?\n            .as_object()\n            .and_then(|o| o.downcast::<NumberFormat>().ok())\n        {\n            return Ok(nf);\n        }\n    }\n\n    Err(JsNativeError::typ()\n        .with_message(\"object was not an `Intl.NumberFormat` object\")\n        .into())\n}\n\n/// Abstract operation [`ToIntlMathematicalValue ( value )`][spec].\n///\n/// [spec]: https://tc39.es/ecma402/#sec-tointlmathematicalvalue\npub(crate) fn to_intl_mathematical_value(\n    value: &JsValue,\n    context: &mut Context,\n) -> JsResult<Decimal> {\n    // 1. Let primValue be ? ToPrimitive(value, number).\n    let prim_value = value.to_primitive(context, PreferredType::Number)?;\n\n    // TODO: Add support in `Decimal` for infinity and NaN, which\n    // should remove the returned errors.\n    match prim_value.variant() {\n        // 2. If Type(primValue) is BigInt, return ℝ(primValue).\n        JsVariant::BigInt(bi) => Decimal::try_from_str(&bi.to_string())\n            .map_err(|err| JsNativeError::range().with_message(err.to_string()).into()),\n        // 3. If Type(primValue) is String, then\n        //     a. Let str be primValue.\n        JsVariant::String(s) => {\n            // 5. Let text be StringToCodePoints(str).\n            // 6. Let literal be ParseText(text, StringNumericLiteral).\n            // 7. If literal is a List of errors, return not-a-number.\n            // 8. Let intlMV be the StringIntlMV of literal.\n            // 9. If intlMV is a mathematical value, then\n            //     a. Let rounded be RoundMVResult(abs(intlMV)).\n            //     b. If rounded is +∞𝔽 and intlMV < 0, return negative-infinity.\n            //     c. If rounded is +∞𝔽, return positive-infinity.\n            //     d. If rounded is +0𝔽 and intlMV < 0, return negative-zero.\n            //     e. If rounded is +0𝔽, return 0.\n            js_string_to_fixed_decimal(&s).ok_or_else(|| {\n                JsNativeError::syntax()\n                    .with_message(\"could not parse the provided string\")\n                    .into()\n            })\n        }\n        // 4. Else,\n        _ => {\n            // a. Let x be ? ToNumber(primValue).\n            // b. If x is -0𝔽, return negative-zero.\n            // c. Let str be Number::toString(x, 10).\n            let x = prim_value.to_number(context)?;\n\n            Decimal::try_from_f64(x, FloatPrecision::RoundTrip)\n                .map_err(|err| JsNativeError::range().with_message(err.to_string()).into())\n        }\n    }\n}\n\n/// Abstract operation [`StringToNumber ( str )`][spec], but specialized for the conversion\n/// to a `FixedDecimal`.\n///\n/// [spec]: https://tc39.es/ecma262/#sec-stringtonumber\n// TODO: Introduce `Infinity` and `NaN` to `Decimal` to make this operation\n// infallible.\npub(crate) fn js_string_to_fixed_decimal(string: &JsString) -> Option<Decimal> {\n    // 1. Let text be ! StringToCodePoints(str).\n    // 2. Let literal be ParseText(text, StringNumericLiteral).\n    let Ok(string) = string.trim().to_std_string() else {\n        // 3. If literal is a List of errors, return NaN.\n        return None;\n    };\n    // 4. Return StringNumericValue of literal.\n    match string.as_str() {\n        \"\" => return Some(Decimal::from(0)),\n        \"-Infinity\" | \"Infinity\" | \"+Infinity\" => return None,\n        _ => {}\n    }\n\n    let mut s = string.bytes();\n    let base = match (s.next(), s.next()) {\n        (Some(b'0'), Some(b'b' | b'B')) => Some(2),\n        (Some(b'0'), Some(b'o' | b'O')) => Some(8),\n        (Some(b'0'), Some(b'x' | b'X')) => Some(16),\n        // Make sure that no further variants of \"infinity\" are parsed.\n        (Some(b'i' | b'I'), _) => {\n            return None;\n        }\n        _ => None,\n    };\n\n    // Parse numbers that begin with `0b`, `0o` and `0x`.\n    let s = if let Some(base) = base {\n        let string = &string[2..];\n        if string.is_empty() {\n            return None;\n        }\n        let int = BigInt::from_str_radix(string, base).ok()?;\n\n        int.to_string()\n    } else {\n        string\n    };\n\n    Decimal::try_from_str(&s).ok()\n}\n"
  },
  {
    "path": "core/engine/src/builtins/intl/number_format/options.rs",
    "content": "use std::fmt;\n\nuse fixed_decimal::{\n    Decimal, FloatPrecision, RoundingIncrement as BaseMultiple, SignDisplay, SignedRoundingMode,\n    UnsignedRoundingMode,\n};\n\nuse boa_macros::js_str;\nuse icu_decimal::{\n    DecimalFormatterPreferences, preferences::NumberingSystem, provider::DecimalSymbolsV1,\n};\nuse icu_locale::extensions::unicode::Value;\nuse icu_provider::{\n    DataMarkerAttributes,\n    prelude::icu_locale_core::{\n        LanguageIdentifier, extensions::unicode, preferences::LocalePreferences,\n    },\n};\nuse tinystr::TinyAsciiStr;\n\nuse crate::{\n    Context, JsExpect, JsNativeError, JsObject, JsResult, JsStr, JsString, JsValue,\n    builtins::{\n        intl::{\n            ServicePreferences,\n            locale::validate_extension,\n            options::{default_number_option, get_number_option},\n        },\n        options::{OptionType, ParsableOptionType, get_option},\n    },\n    context::icu::IntlProvider,\n    js_string,\n};\n\nimpl OptionType for SignedRoundingMode {\n    fn from_value(value: JsValue, context: &mut Context) -> JsResult<Self> {\n        match value.to_string(context)?.to_std_string_escaped().as_str() {\n            \"expand\" => Ok(Self::Unsigned(UnsignedRoundingMode::Expand)),\n            \"trunc\" => Ok(Self::Unsigned(UnsignedRoundingMode::Trunc)),\n            \"halfExpand\" => Ok(Self::Unsigned(UnsignedRoundingMode::HalfExpand)),\n            \"halfTrunc\" => Ok(Self::Unsigned(UnsignedRoundingMode::HalfTrunc)),\n            \"halfEven\" => Ok(Self::Unsigned(UnsignedRoundingMode::HalfEven)),\n            \"ceil\" => Ok(Self::Ceil),\n            \"floor\" => Ok(Self::Floor),\n            \"halfCeil\" => Ok(Self::HalfCeil),\n            \"halfFloor\" => Ok(Self::HalfFloor),\n            _ => Err(JsNativeError::range()\n                .with_message(\"provided string was not a valid rounding type\")\n                .into()),\n        }\n    }\n}\n\nimpl OptionType for NumberingSystem {\n    fn from_value(value: JsValue, context: &mut Context) -> JsResult<Self> {\n        let s = value.to_string(context)?.to_std_string_escaped();\n        Value::try_from_str(&s)\n            .ok()\n            .and_then(|v| NumberingSystem::try_from(v).ok())\n            .filter(|nu| nu.as_str().len() >= 3)\n            .ok_or_else(|| {\n                JsNativeError::range()\n                    .with_message(format!(\"provided numbering system `{s}` is invalid\"))\n                    .into()\n            })\n    }\n}\n\n#[derive(Debug, Copy, Clone, Default, Eq, PartialEq)]\npub(crate) enum Style {\n    #[default]\n    Decimal,\n    Percent,\n    Currency,\n    Unit,\n}\n\nimpl Style {\n    pub(crate) fn to_js_string(self) -> JsString {\n        match self {\n            Style::Decimal => js_string!(\"decimal\"),\n            Style::Percent => js_string!(\"percent\"),\n            Style::Currency => js_string!(\"currency\"),\n            Style::Unit => js_string!(\"unit\"),\n        }\n    }\n}\n\n#[derive(Debug)]\npub(crate) struct ParseStyleError;\n\nimpl fmt::Display for ParseStyleError {\n    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {\n        f.write_str(\"provided string was not a valid style option\")\n    }\n}\n\nimpl std::str::FromStr for Style {\n    type Err = ParseStyleError;\n\n    fn from_str(s: &str) -> Result<Self, Self::Err> {\n        match s {\n            \"decimal\" => Ok(Self::Decimal),\n            \"percent\" => Ok(Self::Percent),\n            \"currency\" => Ok(Self::Currency),\n            \"unit\" => Ok(Self::Unit),\n            _ => Err(ParseStyleError),\n        }\n    }\n}\n\nimpl ParsableOptionType for Style {}\n\n#[derive(Debug, Copy, Clone, Default, Eq, PartialEq)]\npub(crate) enum CurrencyDisplay {\n    Code,\n    #[default]\n    Symbol,\n    NarrowSymbol,\n    Name,\n}\n\nimpl CurrencyDisplay {\n    pub(crate) fn to_js_string(self) -> JsString {\n        match self {\n            CurrencyDisplay::Code => js_string!(\"code\"),\n            CurrencyDisplay::Symbol => js_string!(\"symbol\"),\n            CurrencyDisplay::NarrowSymbol => js_string!(\"narrowSymbol\"),\n            CurrencyDisplay::Name => js_string!(\"name\"),\n        }\n    }\n}\n\n#[derive(Debug)]\npub(crate) struct ParseCurrencyDisplayError;\n\nimpl fmt::Display for ParseCurrencyDisplayError {\n    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {\n        f.write_str(\"provided string was not a valid currencyDisplay option\")\n    }\n}\n\nimpl std::str::FromStr for CurrencyDisplay {\n    type Err = ParseCurrencyDisplayError;\n\n    fn from_str(s: &str) -> Result<Self, Self::Err> {\n        match s {\n            \"code\" => Ok(Self::Code),\n            \"symbol\" => Ok(Self::Symbol),\n            \"narrowSymbol\" => Ok(Self::NarrowSymbol),\n            \"name\" => Ok(Self::Name),\n            _ => Err(ParseCurrencyDisplayError),\n        }\n    }\n}\n\nimpl ParsableOptionType for CurrencyDisplay {}\n\n#[derive(Debug, Copy, Clone, Default, Eq, PartialEq)]\npub(crate) enum CurrencySign {\n    #[default]\n    Standard,\n    Accounting,\n}\n\nimpl CurrencySign {\n    pub(crate) fn to_js_string(self) -> JsString {\n        match self {\n            CurrencySign::Standard => js_string!(\"standard\"),\n            CurrencySign::Accounting => js_string!(\"accounting\"),\n        }\n    }\n}\n\n#[derive(Debug)]\npub(crate) struct ParseCurrencySignError;\n\nimpl fmt::Display for ParseCurrencySignError {\n    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {\n        f.write_str(\"provided string was not a valid currencySign option\")\n    }\n}\n\nimpl std::str::FromStr for CurrencySign {\n    type Err = ParseCurrencySignError;\n\n    fn from_str(s: &str) -> Result<Self, Self::Err> {\n        match s {\n            \"standard\" => Ok(Self::Standard),\n            \"accounting\" => Ok(Self::Accounting),\n            _ => Err(ParseCurrencySignError),\n        }\n    }\n}\n\nimpl ParsableOptionType for CurrencySign {}\n\n#[derive(Debug, Copy, Clone, Default, Eq, PartialEq)]\npub(crate) enum UnitDisplay {\n    #[default]\n    Short,\n    Narrow,\n    Long,\n}\n\nimpl UnitDisplay {\n    pub(crate) fn to_js_string(self) -> JsString {\n        match self {\n            UnitDisplay::Short => js_string!(\"short\"),\n            UnitDisplay::Narrow => js_string!(\"narrow\"),\n            UnitDisplay::Long => js_string!(\"long\"),\n        }\n    }\n}\n\n#[derive(Debug)]\npub(crate) struct ParseUnitDisplayError;\n\nimpl fmt::Display for ParseUnitDisplayError {\n    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {\n        f.write_str(\"provided string was not a valid unitDisplay option\")\n    }\n}\n\nimpl std::str::FromStr for UnitDisplay {\n    type Err = ParseUnitDisplayError;\n\n    fn from_str(s: &str) -> Result<Self, Self::Err> {\n        match s {\n            \"short\" => Ok(Self::Short),\n            \"narrow\" => Ok(Self::Narrow),\n            \"long\" => Ok(Self::Long),\n            _ => Err(ParseUnitDisplayError),\n        }\n    }\n}\n\nimpl ParsableOptionType for UnitDisplay {}\n\n#[derive(Debug, Copy, Clone, Eq, PartialEq)]\npub(crate) struct Currency {\n    // INVARIANT: `inner` must contain only uppercase alphabetic letters.\n    inner: TinyAsciiStr<3>,\n}\n\nimpl Currency {\n    pub(crate) fn to_js_string(self) -> JsString {\n        let bytes = self.inner.as_bytes();\n        js_string!(&[\n            u16::from(bytes[0]),\n            u16::from(bytes[1]),\n            u16::from(bytes[2])\n        ])\n    }\n}\n\n#[derive(Debug)]\npub(crate) struct ParseCurrencyError;\n\nimpl fmt::Display for ParseCurrencyError {\n    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {\n        f.write_str(\"provided string was not a valid currency\")\n    }\n}\n\nimpl std::str::FromStr for Currency {\n    type Err = ParseCurrencyError;\n\n    /// Equivalent to [`IsWellFormedCurrencyCode ( currency )`][spec].\n    ///\n    /// [spec]: https://tc39.es/ecma402/#sec-iswellformedcurrencycode\n    fn from_str(s: &str) -> Result<Self, Self::Err> {\n        // 1. If the length of currency is not 3, return false.\n        let bytes = s.as_bytes();\n\n        if bytes.len() != 3 {\n            return Err(ParseCurrencyError);\n        }\n\n        let curr = TinyAsciiStr::try_from_utf8(bytes).map_err(|_| ParseCurrencyError)?;\n\n        // 2. Let normalized be the ASCII-uppercase of currency.\n        // 3. If normalized contains any code unit outside of 0x0041 through 0x005A (corresponding\n        //    to Unicode characters LATIN CAPITAL LETTER A through LATIN CAPITAL LETTER Z), return false.\n        if !curr.is_ascii_alphabetic() {\n            return Err(ParseCurrencyError);\n        }\n\n        // 4. Return true.\n        Ok(Currency {\n            inner: curr.to_ascii_uppercase(),\n        })\n    }\n}\n\nimpl ParsableOptionType for Currency {}\n\n#[derive(Debug, Eq, PartialEq)]\npub(crate) struct Unit {\n    // INVARIANT: `numerator` must only contain ASCII lowercase alphabetic letters or `-`.\n    numerator: JsStr<'static>,\n    // INVARIANT: if `denominator` is not empty, it must only contain ASCII lowercase alphabetic letters or `-`\n    denominator: JsStr<'static>,\n}\n\nimpl Unit {\n    /// Gets the corresponding `JsString` of this unit.\n    pub(crate) fn to_js_string(&self) -> JsString {\n        if self.denominator.is_empty() {\n            js_string!(self.numerator)\n        } else {\n            // TODO: this is not optimal for now, but the new JS strings should\n            // allow us to optimize this to simple casts from ASCII to JsString.\n            js_string!(self.numerator, js_str!(\"-per-\"), self.denominator)\n        }\n    }\n}\n\n#[derive(Debug)]\npub(crate) struct ParseUnitError;\n\nimpl fmt::Display for ParseUnitError {\n    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {\n        f.write_str(\"provided string was not a valid unit\")\n    }\n}\n\nimpl std::str::FromStr for Unit {\n    type Err = ParseUnitError;\n\n    /// Equivalent to [`IsWellFormedUnitIdentifier ( unitIdentifier )`][spec].\n    ///\n    /// [spec]: https://tc39.es/ecma402/#sec-iswellformedunitidentifier\n    fn from_str(s: &str) -> Result<Self, Self::Err> {\n        const SANCTIONED_UNITS: [&str; 45] = [\n            \"acre\",\n            \"bit\",\n            \"byte\",\n            \"celsius\",\n            \"centimeter\",\n            \"day\",\n            \"degree\",\n            \"fahrenheit\",\n            \"fluid-ounce\",\n            \"foot\",\n            \"gallon\",\n            \"gigabit\",\n            \"gigabyte\",\n            \"gram\",\n            \"hectare\",\n            \"hour\",\n            \"inch\",\n            \"kilobit\",\n            \"kilobyte\",\n            \"kilogram\",\n            \"kilometer\",\n            \"liter\",\n            \"megabit\",\n            \"megabyte\",\n            \"meter\",\n            \"microsecond\",\n            \"mile\",\n            \"mile-scandinavian\",\n            \"milliliter\",\n            \"millimeter\",\n            \"millisecond\",\n            \"minute\",\n            \"month\",\n            \"nanosecond\",\n            \"ounce\",\n            \"percent\",\n            \"petabyte\",\n            \"pound\",\n            \"second\",\n            \"stone\",\n            \"terabit\",\n            \"terabyte\",\n            \"week\",\n            \"yard\",\n            \"year\",\n        ];\n\n        let (num, den) = s\n            .split_once(\"-per-\")\n            .filter(|(_, den)| !den.is_empty())\n            .unwrap_or((s, \"\"));\n\n        let num = SANCTIONED_UNITS\n            .binary_search(&num)\n            .map(|i| SANCTIONED_UNITS[i])\n            .map_err(|_| ParseUnitError)?;\n\n        let num = JsStr::latin1(num.as_bytes());\n\n        let den = if den.is_empty() {\n            JsStr::EMPTY\n        } else {\n            let value = SANCTIONED_UNITS\n                .binary_search(&den)\n                .map(|i| SANCTIONED_UNITS[i])\n                .map_err(|_| ParseUnitError)?;\n\n            JsStr::latin1(value.as_bytes())\n        };\n\n        Ok(Self {\n            numerator: num,\n            denominator: den,\n        })\n    }\n}\n\nimpl ParsableOptionType for Unit {}\n\n#[derive(Debug)]\n#[allow(variant_size_differences)] // 40 bytes is not big enough to require moving `Unit` to the heap.\npub(crate) enum UnitFormatOptions {\n    Decimal,\n    Percent,\n    Currency {\n        currency: Currency,\n        display: CurrencyDisplay,\n        sign: CurrencySign,\n    },\n    Unit {\n        unit: Unit,\n        display: UnitDisplay,\n    },\n}\n\nimpl UnitFormatOptions {\n    /// Gets the style variant of the `UnitFormatOptions`.\n    pub(crate) fn style(&self) -> Style {\n        match self {\n            Self::Decimal => Style::Decimal,\n            Self::Percent => Style::Percent,\n            Self::Currency { .. } => Style::Currency,\n            Self::Unit { .. } => Style::Unit,\n        }\n    }\n\n    /// Abstract operation [`SetNumberFormatUnitOptions ( intlObj, options )`][spec].\n    ///\n    /// [spec]: https://tc39.es/ecma402/#sec-setnumberformatunitoptions\n    pub(crate) fn from_options(options: &JsObject, context: &mut Context) -> JsResult<Self> {\n        // 1. Let style be ? GetOption(options, \"style\", string, « \"decimal\", \"percent\", \"currency\", \"unit\" », \"decimal\").\n        // 2. Set intlObj.[[Style]] to style.\n        let style: Style = get_option(options, js_string!(\"style\"), context)?.unwrap_or_default();\n\n        // 3. Let currency be ? GetOption(options, \"currency\", string, empty, undefined).\n        // 5. Else,\n        //     a. If IsWellFormedCurrencyCode(currency) is false, throw a RangeError exception.\n        let currency = get_option(options, js_string!(\"currency\"), context)?;\n\n        // 4. If currency is undefined, then\n        if currency.is_none() {\n            // a. If style is \"currency\", throw a TypeError exception.\n            if style == Style::Currency {\n                return Err(JsNativeError::typ()\n                    .with_message(\n                        \"cannot format on the currency style without specifying a target currency\",\n                    )\n                    .into());\n            }\n        }\n\n        // 6. Let currencyDisplay be ? GetOption(options, \"currencyDisplay\", string, « \"code\", \"symbol\", \"narrowSymbol\", \"name\" », \"symbol\").\n        let currency_display =\n            get_option(options, js_string!(\"currencyDisplay\"), context)?.unwrap_or_default();\n\n        // 7. Let currencySign be ? GetOption(options, \"currencySign\", string, « \"standard\", \"accounting\" », \"standard\").\n        let currency_sign =\n            get_option(options, js_string!(\"currencySign\"), context)?.unwrap_or_default();\n\n        // 8. Let unit be ? GetOption(options, \"unit\", string, empty, undefined).\n        // 10. Else,\n        //     a. If IsWellFormedUnitIdentifier(unit) is false, throw a RangeError exception.\n        let unit = get_option(options, js_string!(\"unit\"), context)?;\n        // 9. If unit is undefined, then\n        if unit.is_none() {\n            // a. If style is \"unit\", throw a TypeError exception.\n            if style == Style::Unit {\n                return Err(JsNativeError::typ()\n                    .with_message(\n                        \"cannot format on the unit style without specifying a target unit\",\n                    )\n                    .into());\n            }\n        }\n\n        // 11. Let unitDisplay be ? GetOption(options, \"unitDisplay\", string, « \"short\", \"narrow\", \"long\" », \"short\").\n        let unit_display =\n            get_option(options, js_string!(\"unitDisplay\"), context)?.unwrap_or_default();\n\n        // 14. Return unused.\n        Ok(match style {\n            Style::Decimal => UnitFormatOptions::Decimal,\n            Style::Percent => UnitFormatOptions::Percent,\n            // 12. If style is \"currency\", then\n            Style::Currency => {\n                UnitFormatOptions::Currency {\n                    // a. Set intlObj.[[Currency]] to the ASCII-uppercase of currency.\n                    currency: currency.js_expect(\"asserted above that `currency` is not None\")?,\n                    // b. Set intlObj.[[CurrencyDisplay]] to currencyDisplay.\n                    display: currency_display,\n                    // c. Set intlObj.[[CurrencySign]] to currencySign.\n                    sign: currency_sign,\n                }\n            }\n            // 13. If style is \"unit\", then\n            Style::Unit => {\n                UnitFormatOptions::Unit {\n                    //     a. Set intlObj.[[Unit]] to unit.\n                    unit: unit.js_expect(\"asserted above that `unit` is not None\")?,\n                    // b. Set intlObj.[[UnitDisplay]] to unitDisplay.\n                    display: unit_display,\n                }\n            }\n        })\n    }\n}\n\n#[derive(Debug)]\npub(crate) struct DigitFormatOptions {\n    pub(crate) minimum_integer_digits: u8,\n    pub(crate) rounding_increment: RoundingIncrement,\n    pub(crate) rounding_mode: SignedRoundingMode,\n    pub(crate) trailing_zero_display: TrailingZeroDisplay,\n    pub(crate) rounding_type: RoundingType,\n    pub(crate) rounding_priority: RoundingPriority,\n}\n\nimpl DigitFormatOptions {\n    /// Abstract operation [`SetNumberFormatDigitOptions ( intlObj, options, mnfdDefault, mxfdDefault, notation )`][spec].\n    ///\n    /// Gets the digit format options of the number formatter from the options object and the requested notation.\n    ///\n    /// [spec]: https://tc39.es/ecma402/#sec-setnfdigitoptions\n    pub(crate) fn from_options(\n        options: &JsObject,\n        min_float_digits_default: u8,\n        mut max_float_digits_default: u8,\n        notation: NotationKind,\n        context: &mut Context,\n    ) -> JsResult<Self> {\n        // 1. Let mnid be ? GetNumberOption(options, \"minimumIntegerDigits,\", 1, 21, 1).\n        let minimum_integer_digits =\n            get_number_option(options, js_string!(\"minimumIntegerDigits\"), 1, 21, context)?\n                .unwrap_or(1);\n        // 2. Let mnfd be ? Get(options, \"minimumFractionDigits\").\n        let min_float_digits = options.get(js_string!(\"minimumFractionDigits\"), context)?;\n        // 3. Let mxfd be ? Get(options, \"maximumFractionDigits\").\n        let max_float_digits = options.get(js_string!(\"maximumFractionDigits\"), context)?;\n        // 4. Let mnsd be ? Get(options, \"minimumSignificantDigits\").\n        let min_sig_digits = options.get(js_string!(\"minimumSignificantDigits\"), context)?;\n        // 5. Let mxsd be ? Get(options, \"maximumSignificantDigits\").\n        let max_sig_digits = options.get(js_string!(\"maximumSignificantDigits\"), context)?;\n\n        // 7. Let roundingIncrement be ? GetNumberOption(options, \"roundingIncrement\", 1, 5000, 1).\n        // 8. If roundingIncrement is not in « 1, 2, 5, 10, 20, 25, 50, 100, 200, 250, 500, 1000, 2000, 2500, 5000 », throw a RangeError exception.\n        let rounding_increment =\n            get_number_option(options, js_string!(\"roundingIncrement\"), 1, 5000, context)?\n                .unwrap_or(1);\n        let rounding_increment =\n            RoundingIncrement::from_u16(rounding_increment).ok_or_else(|| {\n                JsNativeError::range().with_message(\"invalid value for option `roundingIncrement`\")\n            })?;\n\n        // 9. Let roundingMode be ? GetOption(options, \"roundingMode\", string, « \"ceil\", \"floor\", \"expand\", \"trunc\", \"halfCeil\", \"halfFloor\", \"halfExpand\", \"halfTrunc\", \"halfEven\" », \"halfExpand\").\n        let rounding_mode = get_option(options, js_string!(\"roundingMode\"), context)?.unwrap_or(\n            SignedRoundingMode::Unsigned(UnsignedRoundingMode::HalfExpand),\n        );\n\n        // 10. Let roundingPriority be ? GetOption(options, \"roundingPriority\", string, « \"auto\", \"morePrecision\", \"lessPrecision\" », \"auto\").\n        let mut rounding_priority =\n            get_option(options, js_string!(\"roundingPriority\"), context)?.unwrap_or_default();\n\n        // 11. Let trailingZeroDisplay be ? GetOption(options, \"trailingZeroDisplay\", string, « \"auto\", \"stripIfInteger\" », \"auto\").\n        let trailing_zero_display =\n            get_option(options, js_string!(\"trailingZeroDisplay\"), context)?.unwrap_or_default();\n\n        // 12. NOTE: All fields required by SetNumberFormatDigitOptions have now been read from options. The remainder of this AO interprets the options and may throw exceptions.\n\n        // 13. If roundingIncrement is not 1, set mxfdDefault to mnfdDefault.\n        if rounding_increment.to_u16() != 1 {\n            max_float_digits_default = min_float_digits_default;\n        }\n\n        // 17. If mnsd is not undefined or mxsd is not undefined, then\n        //     a. Let hasSd be true.\n        // 18. Else,\n        //     a. Let hasSd be false.\n        let has_sig_limits = !min_sig_digits.is_undefined() || !max_sig_digits.is_undefined();\n\n        // 19. If mnfd is not undefined or mxfd is not undefined, then\n        //     a. Let hasFd be true.\n        // 20. Else,\n        //     a. Let hasFd be false.\n        let has_float_limits = !min_float_digits.is_undefined() || !max_float_digits.is_undefined();\n\n        // 21. Let needSd be true.\n        // 22. Let needFd be true.\n        let (need_sig_limits, need_frac_limits) = if rounding_priority == RoundingPriority::Auto {\n            // 23. If roundingPriority is \"auto\", then\n            //     a. Set needSd to hasSd.\n            //     b. If needSd is true, or hasFd is false and notation is \"compact\", then\n            //         i. Set needFd to false.\n            (\n                has_sig_limits,\n                !has_sig_limits && (has_float_limits || notation != NotationKind::Compact),\n            )\n        } else {\n            (true, true)\n        };\n\n        // 24. If needSd is true, then\n        let sig_digits = if need_sig_limits {\n            // a. If hasSd is true, then\n            let extrema = if has_sig_limits {\n                // i. Set intlObj.[[MinimumSignificantDigits]] to ? DefaultNumberOption(mnsd, 1, 21, 1).\n                let min_sig = default_number_option(&min_sig_digits, 1, 21, context)?.unwrap_or(1);\n                // ii. Set intlObj.[[MaximumSignificantDigits]] to ? DefaultNumberOption(mxsd, intlObj.[[MinimumSignificantDigits]], 21, 21).\n                let max_sig =\n                    default_number_option(&max_sig_digits, min_sig, 21, context)?.unwrap_or(21);\n\n                Extrema {\n                    minimum: min_sig,\n                    maximum: max_sig,\n                }\n            } else {\n                // b. Else,\n                Extrema {\n                    // i. Set intlObj.[[MinimumSignificantDigits]] to 1.\n                    minimum: 1,\n                    // ii. Set intlObj.[[MaximumSignificantDigits]] to 21.\n                    maximum: 21,\n                }\n            };\n            assert!(extrema.minimum <= extrema.maximum);\n            Some(extrema)\n        } else {\n            None\n        };\n\n        // 25. If needFd is true, then\n        let fractional_digits = if need_frac_limits {\n            //     a. If hasFd is true, then\n            let extrema = if has_float_limits {\n                // i. Set mnfd to ? DefaultNumberOption(mnfd, 0, 100, undefined).\n                let min_float_digits = default_number_option(&min_float_digits, 0, 100, context)?;\n                // ii. Set mxfd to ? DefaultNumberOption(mxfd, 0, 100, undefined).\n                let max_float_digits = default_number_option(&max_float_digits, 0, 100, context)?;\n\n                let (min_float_digits, max_float_digits) =\n                    match (min_float_digits, max_float_digits) {\n                        (Some(min_float_digits), Some(max_float_digits)) => {\n                            // v. Else if mnfd is greater than mxfd, throw a RangeError exception.\n                            if min_float_digits > max_float_digits {\n                                return Err(JsNativeError::range().with_message(\n                            \"`minimumFractionDigits` cannot be bigger than `maximumFractionDigits`\",\n                        ).into());\n                            }\n                            (min_float_digits, max_float_digits)\n                        }\n                        // iv. Else if mxfd is undefined, set mxfd to max(mxfdDefault, mnfd).\n                        (Some(min_float_digits), None) => (\n                            min_float_digits,\n                            u8::max(max_float_digits_default, min_float_digits),\n                        ),\n                        // iii. If mnfd is undefined, set mnfd to min(mnfdDefault, mxfd).\n                        (None, Some(max_float_digits)) => (\n                            u8::min(min_float_digits_default, max_float_digits),\n                            max_float_digits,\n                        ),\n                        (None, None) => {\n                            unreachable!(\n                                \"`has_fd` can only be true if `mnfd` or `mxfd` is not undefined\"\n                            )\n                        }\n                    };\n\n                Extrema {\n                    // vi. Set intlObj.[[MinimumFractionDigits]] to mnfd.\n                    minimum: min_float_digits,\n                    // vii. Set intlObj.[[MaximumFractionDigits]] to mxfd.\n                    maximum: max_float_digits,\n                }\n            } else {\n                // b. Else,\n                Extrema {\n                    //    i. Set intlObj.[[MinimumFractionDigits]] to mnfdDefault.\n                    minimum: min_float_digits_default,\n                    //    ii. Set intlObj.[[MaximumFractionDigits]] to mxfdDefault.\n                    maximum: max_float_digits_default,\n                }\n            };\n            assert!(extrema.minimum <= extrema.maximum);\n            Some(extrema)\n        } else {\n            None\n        };\n\n        let rounding_type = match (sig_digits, fractional_digits) {\n            // 26. If needSd is false and needFd is false, then\n            (None, None) => {\n                // f. Set intlObj.[[ComputedRoundingPriority]] to \"morePrecision\".\n                rounding_priority = RoundingPriority::MorePrecision;\n                // e. Set intlObj.[[RoundingType]] to morePrecision.\n                RoundingType::MorePrecision {\n                    significant_digits: Extrema {\n                        // c. Set intlObj.[[MinimumSignificantDigits]] to 1.\n                        minimum: 1,\n                        // d. Set intlObj.[[MaximumSignificantDigits]] to 2.\n                        maximum: 2,\n                    },\n                    fraction_digits: Extrema {\n                        // a. Set intlObj.[[MinimumFractionDigits]] to 0.\n                        minimum: 0,\n                        // b. Set intlObj.[[MaximumFractionDigits]] to 0.\n                        maximum: 0,\n                    },\n                }\n            }\n            (Some(significant_digits), Some(fraction_digits)) => match rounding_priority {\n                RoundingPriority::MorePrecision => RoundingType::MorePrecision {\n                    significant_digits,\n                    fraction_digits,\n                },\n                RoundingPriority::LessPrecision => RoundingType::LessPrecision {\n                    significant_digits,\n                    fraction_digits,\n                },\n                RoundingPriority::Auto => {\n                    unreachable!(\"Cannot have both roundings when the priority is `Auto`\")\n                }\n            },\n            (Some(sig), None) => RoundingType::SignificantDigits(sig),\n            (None, Some(frac)) => RoundingType::FractionDigits(frac),\n        };\n\n        if rounding_increment.to_u16() != 1 {\n            let RoundingType::FractionDigits(range) = rounding_type else {\n                return Err(JsNativeError::typ()\n                    .with_message(\n                        \"option `roundingIncrement` invalid for the current set of options\",\n                    )\n                    .into());\n            };\n\n            if range.minimum != range.maximum {\n                return Err(JsNativeError::range()\n                    .with_message(\n                        \"option `roundingIncrement` invalid for the current set of options\",\n                    )\n                    .into());\n            }\n        }\n\n        Ok(Self {\n            // 6. Set intlObj.[[MinimumIntegerDigits]] to mnid.\n            minimum_integer_digits,\n            // 14. Set intlObj.[[RoundingIncrement]] to roundingIncrement.\n            rounding_increment,\n            // 15. Set intlObj.[[RoundingMode]] to roundingMode.\n            rounding_mode,\n            // 16. Set intlObj.[[TrailingZeroDisplay]] to trailingZeroDisplay.\n            trailing_zero_display,\n            rounding_type,\n            rounding_priority,\n        })\n    }\n\n    /// Abstract operation [`FormatNumericToString ( intlObject, x )`][spec].\n    ///\n    /// Formats a `FixedDecimal` with the specified digit format options.\n    ///\n    /// [spec]: https://tc39.es/ecma402/#sec-formatnumberstring\n    pub(crate) fn format_fixed_decimal(&self, number: &mut Decimal) {\n        fn round(\n            number: &mut Decimal,\n            position: i16,\n            mode: SignedRoundingMode,\n            multiple: BaseMultiple,\n        ) {\n            number.round_with_mode_and_increment(position, mode, multiple);\n        }\n\n        // <https://tc39.es/ecma402/#sec-torawprecision>\n        fn to_raw_precision(\n            number: &mut Decimal,\n            min_precision: u8,\n            max_precision: u8,\n            rounding_mode: SignedRoundingMode,\n        ) -> i16 {\n            let msb = number.nonzero_magnitude_start();\n            let min_msb = msb - i16::from(min_precision) + 1;\n            let max_msb = msb - i16::from(max_precision) + 1;\n            round(number, max_msb, rounding_mode, BaseMultiple::MultiplesOf1);\n            number.trim_end();\n            number.pad_end(min_msb);\n            max_msb\n        }\n\n        // <https://tc39.es/ecma402/#sec-torawfixed>\n        fn to_raw_fixed(\n            number: &mut Decimal,\n            min_fraction: u8,\n            max_fraction: u8,\n            rounding_increment: RoundingIncrement,\n            rounding_mode: SignedRoundingMode,\n        ) -> i16 {\n            #[cfg(debug_assertions)]\n            if rounding_increment.to_u16() != 1 {\n                assert_eq!(min_fraction, max_fraction);\n            }\n\n            round(\n                number,\n                i16::from(rounding_increment.magnitude_offset) - i16::from(max_fraction),\n                rounding_mode,\n                rounding_increment.multiple,\n            );\n            number.trim_end();\n            number.pad_end(-i16::from(min_fraction));\n            -i16::from(max_fraction)\n        }\n\n        // 3. Let unsignedRoundingMode be GetUnsignedRoundingMode(intlObject.[[RoundingMode]], isNegative).\n        // Skipping because `FixedDecimal`'s API already provides methods equivalent to `RoundingMode`s.\n\n        match self.rounding_type {\n            // 4. If intlObject.[[RoundingType]] is significantDigits, then\n            RoundingType::SignificantDigits(Extrema { minimum, maximum }) => {\n                // a. Let result be ToRawPrecision(x, intlObject.[[MinimumSignificantDigits]], intlObject.[[MaximumSignificantDigits]], unsignedRoundingMode).\n                to_raw_precision(number, minimum, maximum, self.rounding_mode);\n            }\n            // 5. Else if intlObject.[[RoundingType]] is fractionDigits, then\n            RoundingType::FractionDigits(Extrema { minimum, maximum }) => {\n                // a. Let result be ToRawFixed(x, intlObject.[[MinimumFractionDigits]], intlObject.[[MaximumFractionDigits]], intlObject.[[RoundingIncrement]], unsignedRoundingMode).\n                to_raw_fixed(\n                    number,\n                    minimum,\n                    maximum,\n                    self.rounding_increment,\n                    self.rounding_mode,\n                );\n            }\n            // 6. Else,\n            RoundingType::MorePrecision {\n                significant_digits,\n                fraction_digits,\n            }\n            | RoundingType::LessPrecision {\n                significant_digits,\n                fraction_digits,\n            } => {\n                let prefer_more_precision =\n                    matches!(self.rounding_type, RoundingType::MorePrecision { .. });\n                // a. Let sResult be ToRawPrecision(x, intlObject.[[MinimumSignificantDigits]], intlObject.[[MaximumSignificantDigits]], unsignedRoundingMode).\n                let mut fixed = number.clone();\n                let s_magnitude = to_raw_precision(\n                    number,\n                    significant_digits.minimum,\n                    significant_digits.maximum,\n                    self.rounding_mode,\n                );\n                // b. Let fResult be ToRawFixed(x, intlObject.[[MinimumFractionDigits]], intlObject.[[MaximumFractionDigits]], intlObject.[[RoundingIncrement]], unsignedRoundingMode).\n                let f_magnitude = to_raw_fixed(\n                    &mut fixed,\n                    fraction_digits.minimum,\n                    fraction_digits.maximum,\n                    self.rounding_increment,\n                    self.rounding_mode,\n                );\n\n                // c. If intlObject.[[RoundingType]] is morePrecision, then\n                //     i. If sResult.[[RoundingMagnitude]] ≤ fResult.[[RoundingMagnitude]], then\n                //         1. Let result be sResult.\n                //     ii. Else,\n                //         1. Let result be fResult.\n                // d. Else,\n                //     i. Assert: intlObject.[[RoundingType]] is lessPrecision.\n                //     ii. If sResult.[[RoundingMagnitude]] ≤ fResult.[[RoundingMagnitude]], then\n                //         1. Let result be fResult.\n                //     iii. Else,\n                //         1. Let result be sResult.\n                if (prefer_more_precision && f_magnitude < s_magnitude)\n                    || (!prefer_more_precision && s_magnitude <= f_magnitude)\n                {\n                    *number = fixed;\n                }\n            }\n        }\n\n        // 7. Set x to result.[[RoundedNumber]].\n        // 8. Let string be result.[[FormattedString]].\n        // 9. If intlObject.[[TrailingZeroDisplay]] is \"stripIfInteger\" and x modulo 1 = 0, then\n        if self.trailing_zero_display == TrailingZeroDisplay::StripIfInteger {\n            // a. Let i be StringIndexOf(string, \".\", 0).\n            // b. If i ≠ -1, set string to the substring of string from 0 to i.\n            number.trim_end_if_integer();\n        }\n\n        // 10. Let int be result.[[IntegerDigitsCount]].\n        // 11. Let minInteger be intlObject.[[MinimumIntegerDigits]].\n        // 12. If int < minInteger, then\n        //     a. Let forwardZeros be the String consisting of minInteger - int occurrences of the code unit 0x0030 (DIGIT ZERO).\n        //     b. Set string to the string-concatenation of forwardZeros and string.\n        number.pad_start(i16::from(self.minimum_integer_digits));\n\n        // 13. If isNegative is true, then\n        //     a. If x is 0, set x to negative-zero. Otherwise, set x to -x.\n        // As mentioned above, `FixedDecimal` has support for this.\n    }\n\n    /// Abstract operation [`FormatNumericToString ( intlObject, x )`][spec].\n    ///\n    /// Converts the input number to a `FixedDecimal` with the specified digit format options.\n    ///\n    /// [spec]: https://tc39.es/ecma402/#sec-formatnumberstring\n    pub(crate) fn format_f64(&self, number: f64) -> Decimal {\n        // 1. If x is negative-zero, then\n        //     a. Let isNegative be true.\n        //     b. Set x to 0.\n        // 2. Else,\n        //     a. Assert: x is a mathematical value.\n        //     b. If x < 0, let isNegative be true; else let isNegative be false.\n        //     c. If isNegative is true, then\n        //         i. Set x to -x.\n        // We can skip these steps, because `FixedDecimal` already provides support for\n        // negative zeroes.\n        let mut number = Decimal::try_from_f64(number, FloatPrecision::RoundTrip)\n            .expect(\"`number` must be finite\");\n\n        self.format_fixed_decimal(&mut number);\n\n        // 14. Return the Record { [[RoundedNumber]]: x, [[FormattedString]]: string }.\n        number\n    }\n}\n\n/// The increment of a rounding operation.\n///\n/// This differs from [`fixed_decimal::RoundingIncrement`] because ECMA402 accepts\n/// several more increments than `fixed_decimal`, but all increments can be decomposed\n/// into the target multiple and the magnitude offset.\n///\n/// For example, rounding the number `0.02456` to the increment 200 at position\n/// -3 is equivalent to rounding the same number to the increment 2 at position -1, and adding\n/// trailing zeroes.\n#[derive(Debug, Copy, Clone, Eq, PartialEq)]\npub(crate) struct RoundingIncrement {\n    multiple: BaseMultiple,\n    magnitude_offset: u8,\n}\n\nimpl RoundingIncrement {\n    /// Creates a `RoundingIncrement` from its base multiple (1, 2, 5, or 25) and its\n    /// exponent (1, 10, 100, or 1000).\n    #[cfg(test)]\n    pub(crate) const fn from_parts(multiple: BaseMultiple, exponent: u8) -> Option<Self> {\n        if exponent > 3 {\n            return None;\n        }\n\n        Some(Self {\n            multiple,\n            magnitude_offset: exponent,\n        })\n    }\n\n    /// Creates a `RoundingIncrement` from the numeric value of the increment.\n    pub(crate) fn from_u16(increment: u16) -> Option<Self> {\n        let mut offset = 0u8;\n        let multiple = loop {\n            let rem = increment % 10u16.checked_pow(u32::from(offset + 1))?;\n\n            if rem != 0 {\n                break increment / 10u16.pow(u32::from(offset));\n            }\n\n            offset += 1;\n        };\n\n        if offset > 3 {\n            return None;\n        }\n\n        let multiple = match multiple {\n            1 => BaseMultiple::MultiplesOf1,\n            2 => BaseMultiple::MultiplesOf2,\n            5 => BaseMultiple::MultiplesOf5,\n            25 => BaseMultiple::MultiplesOf25,\n            _ => return None,\n        };\n\n        Some(RoundingIncrement {\n            multiple,\n            magnitude_offset: offset,\n        })\n    }\n\n    /// Gets the numeric value of this `RoundingIncrement`.\n    pub(crate) fn to_u16(self) -> u16 {\n        u16::from(self.magnitude_offset + 1)\n            * match self.multiple {\n                BaseMultiple::MultiplesOf1 => 1,\n                BaseMultiple::MultiplesOf2 => 2,\n                BaseMultiple::MultiplesOf5 => 5,\n                BaseMultiple::MultiplesOf25 => 25,\n                _ => {\n                    debug_assert!(false, \"base multiples can only be 1, 2, 5, or 25\");\n                    1\n                }\n            }\n    }\n}\n\n#[derive(Debug, Copy, Clone, Default, PartialEq, Eq)]\npub(crate) enum CompactDisplay {\n    #[default]\n    Short,\n    Long,\n}\n\nimpl CompactDisplay {\n    pub(crate) fn to_js_string(self) -> JsString {\n        match self {\n            CompactDisplay::Short => js_string!(\"short\"),\n            CompactDisplay::Long => js_string!(\"long\"),\n        }\n    }\n}\n\n#[derive(Debug)]\npub(crate) struct ParseCompactDisplayError;\n\nimpl fmt::Display for ParseCompactDisplayError {\n    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {\n        f.write_str(\"provided string was not a valid compactDisplay option\")\n    }\n}\n\nimpl std::str::FromStr for CompactDisplay {\n    type Err = ParseCompactDisplayError;\n\n    fn from_str(s: &str) -> Result<Self, Self::Err> {\n        match s {\n            \"short\" => Ok(Self::Short),\n            \"long\" => Ok(Self::Long),\n            _ => Err(ParseCompactDisplayError),\n        }\n    }\n}\n\nimpl ParsableOptionType for CompactDisplay {}\n\n#[derive(Debug, Copy, Clone, Default, PartialEq, Eq)]\npub(crate) enum NotationKind {\n    #[default]\n    Standard,\n    Scientific,\n    Engineering,\n    Compact,\n}\n\nimpl NotationKind {\n    pub(crate) fn to_js_string(self) -> JsString {\n        match self {\n            NotationKind::Standard => js_string!(\"standard\"),\n            NotationKind::Scientific => js_string!(\"scientific\"),\n            NotationKind::Engineering => js_string!(\"engineering\"),\n            NotationKind::Compact => js_string!(\"compact\"),\n        }\n    }\n}\n\n#[derive(Debug)]\npub(crate) struct ParseNotationKindError;\n\nimpl fmt::Display for ParseNotationKindError {\n    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {\n        f.write_str(\"provided string was not a valid notation option\")\n    }\n}\n\nimpl std::str::FromStr for NotationKind {\n    type Err = ParseNotationKindError;\n\n    fn from_str(s: &str) -> Result<Self, Self::Err> {\n        match s {\n            \"standard\" => Ok(Self::Standard),\n            \"scientific\" => Ok(Self::Scientific),\n            \"engineering\" => Ok(Self::Engineering),\n            \"compact\" => Ok(Self::Compact),\n            _ => Err(ParseNotationKindError),\n        }\n    }\n}\n\nimpl ParsableOptionType for NotationKind {}\n\n#[derive(Debug, Copy, Clone, Eq, PartialEq)]\npub(crate) enum Notation {\n    Standard,\n    Scientific,\n    Engineering,\n    Compact { display: CompactDisplay },\n}\n\nimpl Notation {\n    pub(crate) fn kind(self) -> NotationKind {\n        match self {\n            Notation::Standard => NotationKind::Standard,\n            Notation::Scientific => NotationKind::Scientific,\n            Notation::Engineering => NotationKind::Engineering,\n            Notation::Compact { .. } => NotationKind::Compact,\n        }\n    }\n}\n\n#[derive(Debug, Copy, Clone, Default, Eq, PartialEq)]\npub(crate) enum RoundingPriority {\n    #[default]\n    Auto,\n    MorePrecision,\n    LessPrecision,\n}\n\nimpl RoundingPriority {\n    pub(crate) fn to_js_string(self) -> JsString {\n        match self {\n            RoundingPriority::Auto => js_string!(\"auto\"),\n            RoundingPriority::MorePrecision => js_string!(\"morePrecision\"),\n            RoundingPriority::LessPrecision => js_string!(\"lessPrecision\"),\n        }\n    }\n}\n\n#[derive(Debug)]\npub(crate) struct ParseRoundingPriorityError;\n\nimpl fmt::Display for ParseRoundingPriorityError {\n    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {\n        f.write_str(\"provided string was not a valid rounding priority\")\n    }\n}\n\nimpl std::str::FromStr for RoundingPriority {\n    type Err = ParseRoundingPriorityError;\n\n    fn from_str(s: &str) -> Result<Self, Self::Err> {\n        match s {\n            \"auto\" => Ok(Self::Auto),\n            \"morePrecision\" => Ok(Self::MorePrecision),\n            \"lessPrecision\" => Ok(Self::LessPrecision),\n            _ => Err(ParseRoundingPriorityError),\n        }\n    }\n}\n\nimpl ParsableOptionType for RoundingPriority {}\n\n#[derive(Debug, Copy, Clone, Default, PartialEq, Eq)]\npub(crate) enum TrailingZeroDisplay {\n    #[default]\n    Auto,\n    StripIfInteger,\n}\n\nimpl TrailingZeroDisplay {\n    pub(crate) fn to_js_string(self) -> JsString {\n        match self {\n            TrailingZeroDisplay::Auto => js_string!(\"auto\"),\n            TrailingZeroDisplay::StripIfInteger => js_string!(\"stripIfInteger\"),\n        }\n    }\n}\n\n#[derive(Debug)]\npub(crate) struct ParseTrailingZeroDisplayError;\n\nimpl fmt::Display for ParseTrailingZeroDisplayError {\n    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {\n        f.write_str(\"provided string was not a valid trailing zero display option\")\n    }\n}\n\nimpl std::str::FromStr for TrailingZeroDisplay {\n    type Err = ParseTrailingZeroDisplayError;\n\n    fn from_str(s: &str) -> Result<Self, Self::Err> {\n        match s {\n            \"auto\" => Ok(Self::Auto),\n            \"stripIfInteger\" => Ok(Self::StripIfInteger),\n            _ => Err(ParseTrailingZeroDisplayError),\n        }\n    }\n}\n\nimpl ParsableOptionType for TrailingZeroDisplay {}\n\nimpl OptionType for SignDisplay {\n    fn from_value(value: JsValue, context: &mut Context) -> JsResult<Self> {\n        match value.to_string(context)?.to_std_string_escaped().as_str() {\n            \"auto\" => Ok(Self::Auto),\n            \"never\" => Ok(Self::Never),\n            \"always\" => Ok(Self::Always),\n            \"exceptZero\" => Ok(Self::ExceptZero),\n            \"negative\" => Ok(Self::Negative),\n            _ => Err(JsNativeError::range()\n                .with_message(\n                    \"provided string was not `auto`, `never`, `always`, `exceptZero`, or `negative`\",\n                )\n                .into()),\n        }\n    }\n}\n\n#[derive(Debug, Copy, Clone)]\npub(crate) struct Extrema<T> {\n    pub(crate) minimum: T,\n    pub(crate) maximum: T,\n}\n\n#[derive(Debug, Copy, Clone)]\npub(crate) enum RoundingType {\n    MorePrecision {\n        significant_digits: Extrema<u8>,\n        fraction_digits: Extrema<u8>,\n    },\n    LessPrecision {\n        significant_digits: Extrema<u8>,\n        fraction_digits: Extrema<u8>,\n    },\n    SignificantDigits(Extrema<u8>),\n    FractionDigits(Extrema<u8>),\n}\n\nimpl RoundingType {\n    /// Gets the significant digit limits of the rounding type, or `None` otherwise.\n    pub(crate) const fn significant_digits(self) -> Option<Extrema<u8>> {\n        match self {\n            Self::MorePrecision {\n                significant_digits, ..\n            }\n            | Self::LessPrecision {\n                significant_digits, ..\n            }\n            | Self::SignificantDigits(significant_digits) => Some(significant_digits),\n            Self::FractionDigits(_) => None,\n        }\n    }\n\n    /// Gets the fraction digit limits of the rounding type, or `None` otherwise.\n    pub(crate) const fn fraction_digits(self) -> Option<Extrema<u8>> {\n        match self {\n            Self::MorePrecision {\n                fraction_digits, ..\n            }\n            | Self::LessPrecision {\n                fraction_digits, ..\n            }\n            | Self::FractionDigits(fraction_digits) => Some(fraction_digits),\n            Self::SignificantDigits(_) => None,\n        }\n    }\n}\n\nimpl ServicePreferences for DecimalFormatterPreferences {\n    fn validate(&mut self, id: &LanguageIdentifier, provider: &IntlProvider) {\n        self.numbering_system = self.numbering_system.take().filter(|nu| {\n            let attr = DataMarkerAttributes::from_str_or_panic(nu.as_str());\n            validate_extension::<DecimalSymbolsV1>(id, attr, provider)\n        });\n    }\n\n    impl_service_preferences!(numbering_system);\n}\n"
  },
  {
    "path": "core/engine/src/builtins/intl/number_format/tests.rs",
    "content": "use crate::builtins::intl::number_format::RoundingIncrement;\nuse fixed_decimal::RoundingIncrement::*;\n\n#[test]\nfn u16_to_rounding_increment_sunny_day() {\n    #[rustfmt::skip]\n    let valid_cases: [(u16, RoundingIncrement); 15] = [\n        // Singles\n        (1, RoundingIncrement::from_parts(MultiplesOf1, 0).unwrap()),\n        (2, RoundingIncrement::from_parts(MultiplesOf2, 0).unwrap()),\n        (5, RoundingIncrement::from_parts(MultiplesOf5, 0).unwrap()),\n        // Tens\n        (10, RoundingIncrement::from_parts(MultiplesOf1, 1).unwrap()),\n        (20, RoundingIncrement::from_parts(MultiplesOf2, 1).unwrap()),\n        (25, RoundingIncrement::from_parts(MultiplesOf25, 0).unwrap()),\n        (50, RoundingIncrement::from_parts(MultiplesOf5, 1).unwrap()),\n        // Hundreds\n        (100, RoundingIncrement::from_parts(MultiplesOf1, 2).unwrap()),\n        (200, RoundingIncrement::from_parts(MultiplesOf2, 2).unwrap()),\n        (250, RoundingIncrement::from_parts(MultiplesOf25, 1).unwrap()),\n        (500, RoundingIncrement::from_parts(MultiplesOf5, 2).unwrap()),\n        // Thousands\n        (1000, RoundingIncrement::from_parts(MultiplesOf1, 3).unwrap()),\n        (2000, RoundingIncrement::from_parts(MultiplesOf2, 3).unwrap()),\n        (2500, RoundingIncrement::from_parts(MultiplesOf25, 2).unwrap()),\n        (5000, RoundingIncrement::from_parts(MultiplesOf5, 3).unwrap()),\n    ];\n\n    for (num, increment) in valid_cases {\n        assert_eq!(RoundingIncrement::from_u16(num), Some(increment));\n    }\n}\n\n#[test]\nfn u16_to_rounding_increment_rainy_day() {\n    const INVALID_CASES: [u16; 9] = [0, 4, 6, 24, 10000, 65535, 7373, 140, 1500];\n\n    for num in INVALID_CASES {\n        assert!(RoundingIncrement::from_u16(num).is_none());\n    }\n}\n"
  },
  {
    "path": "core/engine/src/builtins/intl/options.rs",
    "content": "use std::{fmt, str::FromStr};\n\nuse icu_locale::{LanguageIdentifier, extensions::unicode};\nuse num_traits::FromPrimitive;\n\nuse crate::{\n    Context, JsNativeError, JsResult, JsString, JsValue,\n    builtins::{OrdinaryObject, intl::ServicePreferences, options::ParsableOptionType},\n    context::icu::IntlProvider,\n    object::JsObject,\n};\n\n/// `IntlOptions` aggregates the `locale_matcher` selector and any other object\n/// property needed for `Intl` object constructors.\n///\n/// It is used as the type of the `options` parameter in the operation `resolve_locale`.\n#[derive(Debug, Default)]\npub(super) struct IntlOptions<O> {\n    pub(super) matcher: LocaleMatcher,\n    pub(super) preferences: O,\n}\n\n#[derive(Clone, Copy, Debug, PartialEq, Eq, Default)]\npub(super) enum LocaleMatcher {\n    Lookup,\n    #[default]\n    BestFit,\n}\n\n#[derive(Debug)]\npub(super) struct ParseLocaleMatcherError;\n\nimpl fmt::Display for ParseLocaleMatcherError {\n    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {\n        \"provided string was not `lookup` or `best fit`\".fmt(f)\n    }\n}\n\nimpl FromStr for LocaleMatcher {\n    type Err = ParseLocaleMatcherError;\n\n    fn from_str(s: &str) -> Result<Self, Self::Err> {\n        match s {\n            \"lookup\" => Ok(Self::Lookup),\n            \"best fit\" => Ok(Self::BestFit),\n            _ => Err(ParseLocaleMatcherError),\n        }\n    }\n}\n\nimpl ParsableOptionType for LocaleMatcher {}\n\n#[derive(Clone, Copy, Debug, PartialEq, Eq, Default)]\npub(super) struct EmptyPreferences;\n\nimpl From<&icu_locale::Locale> for EmptyPreferences {\n    fn from(_: &icu_locale::Locale) -> Self {\n        Self\n    }\n}\n\nimpl ServicePreferences for EmptyPreferences {\n    fn validate(&mut self, _: &LanguageIdentifier, _: &IntlProvider) {}\n    fn as_unicode(&self) -> unicode::Unicode {\n        unicode::Unicode::new()\n    }\n    fn extended(&self, _: &Self) -> Self {\n        Self\n    }\n    fn intersection(&self, _: &Self) -> Self {\n        Self\n    }\n}\n\n/// Abstract operation `GetNumberOption ( options, property, minimum, maximum, fallback )`\n///\n/// Extracts the value of the property named `property` from the provided `options`\n/// object, converts it to a `Number value`, checks whether it is in the allowed range,\n/// and fills in a `fallback` value if necessary.\n///\n/// More information:\n///  - [ECMAScript reference][spec]\n///\n/// [spec]: https://tc39.es/ecma402/#sec-getnumberoption\npub(super) fn get_number_option<T>(\n    options: &JsObject,\n    property: JsString,\n    minimum: T,\n    maximum: T,\n    context: &mut Context,\n) -> JsResult<Option<T>>\nwhere\n    T: Into<f64> + FromPrimitive,\n{\n    // 1. Assert: Type(options) is Object.\n    // 2. Let value be ? Get(options, property).\n    let value = options.get(property, context)?;\n\n    // 3. Return ? DefaultNumberOption(value, minimum, maximum, fallback).\n    default_number_option(&value, minimum, maximum, context)\n}\n\n/// Abstract operation [`DefaultNumberOption ( value, minimum, maximum, fallback )`][spec]\n///\n/// Converts `value` to a `Number value`, checks whether it is in the allowed range,\n/// and fills in a `fallback` value if necessary.\n///\n/// [spec]: https://tc39.es/ecma402/#sec-defaultnumberoption\npub(super) fn default_number_option<T>(\n    value: &JsValue,\n    minimum: T,\n    maximum: T,\n    context: &mut Context,\n) -> JsResult<Option<T>>\nwhere\n    T: Into<f64> + FromPrimitive,\n{\n    // 1. If value is undefined, return fallback.\n    if value.is_undefined() {\n        return Ok(None);\n    }\n\n    // 2. Set value to ? ToNumber(value).\n    let value = value.to_number(context)?;\n\n    // 3. If value is NaN or less than minimum or greater than maximum, throw a RangeError exception.\n    if value.is_nan() || value < minimum.into() || value > maximum.into() {\n        return Err(JsNativeError::range()\n            .with_message(\"DefaultNumberOption: value is out of range.\")\n            .into());\n    }\n\n    // 4. Return floor(value).\n    // We already asserted the range of `value` with the conditional above.\n    Ok(T::from_f64(value))\n}\n\n/// Abstract operation [`CoerceOptionsToObject ( options )`][spec]\n///\n/// Coerces `options` into a [`JsObject`] suitable for use with [`get_option`], defaulting to an\n/// empty `JsObject`.\n/// Because it coerces non-null primitive values into objects, its use is discouraged for new\n/// functionality in favour of [`get_options_object`].\n///\n/// [spec]: https://tc39.es/ecma402/#sec-coerceoptionstoobject\n/// [`get_option`]: crate::builtins::options::get_option\n/// [`get_options_object`]: crate::builtins::options::get_options_object\npub(super) fn coerce_options_to_object(\n    options: &JsValue,\n    context: &mut Context,\n) -> JsResult<JsObject> {\n    // If options is undefined, then\n    if options.is_undefined() {\n        // a. Return OrdinaryObjectCreate(null).\n        return Ok(JsObject::from_proto_and_data_with_shared_shape(\n            context.root_shape(),\n            None,\n            OrdinaryObject,\n        )\n        .upcast());\n    }\n\n    // 2. Return ? ToObject(options).\n    options.to_object(context)\n}\n"
  },
  {
    "path": "core/engine/src/builtins/intl/plural_rules/mod.rs",
    "content": "mod options;\n\nuse boa_gc::{Finalize, Trace};\nuse fixed_decimal::{CompactDecimal, Decimal, SignedRoundingMode, UnsignedRoundingMode};\nuse icu_locale::Locale;\nuse icu_plurals::{\n    PluralCategory, PluralRuleType, PluralRules as NativePluralRules, PluralRulesOptions,\n    PluralRulesPreferences, PluralRulesWithRanges, provider::PluralsCardinalV1,\n};\n\nuse crate::{\n    Context, JsArgs, JsData, JsNativeError, JsObject, JsResult, JsString, JsSymbol, JsValue,\n    builtins::{\n        Array, BuiltInBuilder, BuiltInConstructor, BuiltInObject, IntrinsicObject,\n        intl::options::EmptyPreferences, options::get_option,\n    },\n    context::intrinsics::{Intrinsics, StandardConstructor, StandardConstructors},\n    js_string,\n    object::{ObjectInitializer, internal_methods::get_prototype_from_constructor},\n    property::Attribute,\n    realm::Realm,\n    string::StaticJsStrings,\n};\n\nuse super::{\n    Service,\n    locale::{canonicalize_locale_list, filter_locales, resolve_locale},\n    number_format::{DigitFormatOptions, Extrema, NotationKind},\n    options::{IntlOptions, coerce_options_to_object},\n};\n\n#[derive(Debug, Trace, Finalize, JsData)]\n// SAFETY: `PluralRules` doesn't contain any traceable data.\n#[boa_gc(unsafe_empty_trace)]\npub(crate) struct PluralRules {\n    locale: Locale,\n    native: PluralRulesWithRanges<NativePluralRules>,\n    rule_type: PluralRuleType,\n    notation: NotationKind,\n    format_options: DigitFormatOptions,\n}\n\nimpl Service for PluralRules {\n    type LangMarker = PluralsCardinalV1;\n\n    type Preferences = EmptyPreferences;\n}\n\nimpl IntrinsicObject for PluralRules {\n    fn init(realm: &Realm) {\n        BuiltInBuilder::from_standard_constructor::<Self>(realm)\n            .static_method(\n                Self::supported_locales_of,\n                js_string!(\"supportedLocalesOf\"),\n                1,\n            )\n            .property(\n                JsSymbol::to_string_tag(),\n                js_string!(\"Intl.PluralRules\"),\n                Attribute::CONFIGURABLE,\n            )\n            .method(Self::resolved_options, js_string!(\"resolvedOptions\"), 0)\n            .method(Self::select, js_string!(\"select\"), 1)\n            .method(Self::select_range, js_string!(\"selectRange\"), 2)\n            .build();\n    }\n\n    fn get(intrinsics: &Intrinsics) -> JsObject {\n        Self::STANDARD_CONSTRUCTOR(intrinsics.constructors()).constructor()\n    }\n}\n\nimpl BuiltInObject for PluralRules {\n    const NAME: JsString = StaticJsStrings::PLURAL_RULES;\n}\n\nimpl BuiltInConstructor for PluralRules {\n    const CONSTRUCTOR_ARGUMENTS: usize = 0;\n    const PROTOTYPE_STORAGE_SLOTS: usize = 4;\n    const CONSTRUCTOR_STORAGE_SLOTS: usize = 1;\n\n    const STANDARD_CONSTRUCTOR: fn(&StandardConstructors) -> &StandardConstructor =\n        StandardConstructors::plural_rules;\n\n    fn constructor(\n        new_target: &JsValue,\n        args: &[JsValue],\n        context: &mut Context,\n    ) -> JsResult<JsValue> {\n        // 1. If NewTarget is undefined, throw a TypeError exception.\n        if new_target.is_undefined() {\n            return Err(JsNativeError::typ()\n                .with_message(\"cannot call `Intl.PluralRules` constructor without `new`\")\n                .into());\n        }\n\n        // 2. Let pluralRules be ? OrdinaryCreateFromConstructor(NewTarget, \"%PluralRules.prototype%\",\n        //    « [[InitializedPluralRules]], [[Locale]], [[Type]], [[MinimumIntegerDigits]],\n        //    [[MinimumFractionDigits]], [[MaximumFractionDigits]], [[MinimumSignificantDigits]],\n        //    [[MaximumSignificantDigits]], [[RoundingType]], [[RoundingIncrement]], [[RoundingMode]],\n        //    [[ComputedRoundingPriority]], [[TrailingZeroDisplay]] »).\n        let proto = get_prototype_from_constructor(\n            new_target,\n            StandardConstructors::plural_rules,\n            context,\n        )?;\n\n        let locales = args.get_or_undefined(0);\n        let options = args.get_or_undefined(1);\n\n        // 3. Let optionsResolution be ? ResolveOptions(%Intl.PluralRules%, %Intl.PluralRules%.[[LocaleData]], locales, options, « coerce-options »).\n        // 4. Set options to optionsResolution.[[Options]].\n        // 5. Let r be optionsResolution.[[ResolvedLocale]].\n        // 6. Set pluralRules.[[Locale]] to r.[[Locale]].\n        // Inlined steps since every constructor needs its own handling.\n        let requested_locales = canonicalize_locale_list(locales, context)?;\n        let options = coerce_options_to_object(options, context)?;\n        let matcher =\n            get_option(&options, js_string!(\"localeMatcher\"), context)?.unwrap_or_default();\n        let locale = resolve_locale::<Self>(\n            requested_locales,\n            &mut IntlOptions {\n                matcher,\n                ..Default::default()\n            },\n            context.intl_provider(),\n        )?;\n\n        // 7. Let t be ? GetOption(options, \"type\", string, « \"cardinal\", \"ordinal\" », \"cardinal\").\n        // 8. Set pluralRules.[[Type]] to t.\n        let rule_type =\n            get_option(&options, js_string!(\"type\"), context)?.unwrap_or(PluralRuleType::Cardinal);\n\n        // 9. Let notation be ? GetOption(options, \"notation\", string, « \"standard\", \"scientific\", \"engineering\", \"compact\" », \"standard\").\n        // 10. Set pluralRules.[[Notation]] to notation.\n        let notation = get_option(&options, js_string!(\"notation\"), context)?\n            .unwrap_or(NotationKind::Standard);\n\n        // 11. Perform ? SetNumberFormatDigitOptions(pluralRules, options, 0, 3, notation).\n        let format_options = DigitFormatOptions::from_options(&options, 0, 3, notation, context)?;\n\n        let prefs = PluralRulesPreferences::from(&locale);\n        let opts = PluralRulesOptions::from(rule_type);\n\n        let native = PluralRulesWithRanges::try_new_with_buffer_provider(\n            context.intl_provider().erased_provider(),\n            prefs,\n            opts,\n        )\n        .map_err(|e| JsNativeError::typ().with_message(e.to_string()))?;\n\n        // 12. Return pluralRules.\n        Ok(JsObject::from_proto_and_data_with_shared_shape(\n            context.root_shape(),\n            proto,\n            Self {\n                locale,\n                native,\n                rule_type,\n                notation,\n                format_options,\n            },\n        )\n        .into())\n    }\n}\n\nimpl PluralRules {\n    /// [`Intl.PluralRules.prototype.select ( value )`][spec].\n    ///\n    /// Returns a string indicating which plural rule to use for locale-aware formatting of a number.\n    ///\n    /// More information:\n    ///  - [MDN documentation][mdn]\n    ///\n    /// [spec]: https://tc39.es/ecma402/#sec-intl.pluralrules.prototype.select\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/PluralRules/select\n    fn select(this: &JsValue, args: &[JsValue], context: &mut Context) -> JsResult<JsValue> {\n        // 1. Let pr be the this value.\n        // 2. Perform ? RequireInternalSlot(pr, [[InitializedPluralRules]]).\n        let object = this.as_object();\n        let plural_rules = object\n            .as_ref()\n            .and_then(|o| o.downcast_ref::<Self>())\n            .ok_or_else(|| {\n                JsNativeError::typ()\n                    .with_message(\"`select` can only be called on an `Intl.PluralRules` object\")\n            })?;\n\n        let n = args.get_or_undefined(0).to_number(context)?;\n\n        Ok(plural_category_to_js_string(resolve_plural(&plural_rules, n).category).into())\n    }\n\n    /// [`Intl.PluralRules.prototype.selectRange ( start, end )`][spec].\n    ///\n    /// Receives two values and returns a string indicating which plural rule to use for\n    /// locale-aware formatting of the indicated range.\n    ///\n    /// More information:\n    /// - [MDN documentation][mdn]\n    ///\n    /// [spec]: https://tc39.es/ecma402/#sec-intl.pluralrules.prototype.selectrange\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/PluralRules/selectRange\n    fn select_range(this: &JsValue, args: &[JsValue], context: &mut Context) -> JsResult<JsValue> {\n        // 1. Let pr be the this value.\n        // 2. Perform ? RequireInternalSlot(pr, [[InitializedPluralRules]]).\n        let object = this.as_object();\n        let plural_rules = object\n            .as_ref()\n            .and_then(|o| o.downcast_ref::<Self>())\n            .ok_or_else(|| {\n                JsNativeError::typ().with_message(\n                    \"`select_range` can only be called on an `Intl.PluralRules` object\",\n                )\n            })?;\n\n        // 3. If start is undefined or end is undefined, throw a TypeError exception.\n        let x = args.get_or_undefined(0);\n        let y = args.get_or_undefined(1);\n        if x.is_undefined() || y.is_undefined() {\n            return Err(JsNativeError::typ()\n                .with_message(\"extremum of range cannot be `undefined`\")\n                .into());\n        }\n\n        // 4. Let x be ? ToNumber(start).\n        let x = x.to_number(context)?;\n        // 5. Let y be ? ToNumber(end).\n        let y = y.to_number(context)?;\n\n        // 6. Return ? ResolvePluralRange(pr, x, y).\n        // ResolvePluralRange(pr, x, y)\n        // <https://tc39.es/ecma402/#sec-resolvepluralrange>\n\n        // 1. If x is NaN or y is NaN, throw a RangeError exception.\n        if x.is_nan() || y.is_nan() {\n            return Err(JsNativeError::range()\n                .with_message(\"arguments of selectRange cannot be NaN\")\n                .into());\n        }\n\n        // 2. Let xp be ResolvePlural(pluralRules, x).\n        let x = resolve_plural(&plural_rules, x);\n        // 3. Let yp be ResolvePlural(pluralRules, y).\n        let y = resolve_plural(&plural_rules, y);\n\n        // 4. If xp.[[FormattedString]] is yp.[[FormattedString]], then\n        if x.formatted == y.formatted {\n            // a. Return xp.[[PluralCategory]].\n            return Ok(plural_category_to_js_string(x.category).into());\n        }\n\n        // 5. Let locale be pluralRules.[[Locale]].\n        // 6. Let type be pluralRules.[[Type]].\n        // 7. Return PluralRuleSelectRange(locale, type, xp.[[PluralCategory]], yp.[[PluralCategory]]).\n        Ok(\n            plural_category_to_js_string(plural_rules.native.resolve_range(x.category, y.category))\n                .into(),\n        )\n    }\n\n    /// [`Intl.PluralRules.supportedLocalesOf ( locales [ , options ] )`][spec].\n    ///\n    /// Returns an array containing those of the provided locales that are supported in plural rules\n    /// without having to fall back to the runtime's default locale.\n    ///\n    /// More information:\n    ///  - [MDN documentation][mdn]\n    ///\n    /// [spec]: https://tc39.es/ecma402/#sec-intl.pluralrules.supportedlocalesof\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/PluralRules/supportedLocalesOf\n    fn supported_locales_of(\n        _: &JsValue,\n        args: &[JsValue],\n        context: &mut Context,\n    ) -> JsResult<JsValue> {\n        let locales = args.get_or_undefined(0);\n        let options = args.get_or_undefined(1);\n\n        // 1. Let availableLocales be %PluralRules%.[[AvailableLocales]].\n        // 2. Let requestedLocales be ? CanonicalizeLocaleList(locales).\n        let requested_locales = canonicalize_locale_list(locales, context)?;\n\n        // 3. Return ? FilterLocales(availableLocales, requestedLocales, options).\n        filter_locales::<Self>(requested_locales, options, context).map(JsValue::from)\n    }\n\n    /// [`Intl.PluralRules.prototype.resolvedOptions ( )`][spec].\n    ///\n    /// Returns a new object with properties reflecting the locale and options computed during the\n    /// construction of the current `Intl.PluralRules` object.\n    ///\n    /// More information:\n    ///  - [MDN documentation][mdn]\n    ///\n    /// [spec]: https://tc39.es/ecma402/#sec-intl.pluralrules.prototype.resolvedoptions\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/PluralRules/resolvedOptions\n    fn resolved_options(this: &JsValue, _: &[JsValue], context: &mut Context) -> JsResult<JsValue> {\n        // 1. Let pr be the this value.\n        // 2. Perform ? RequireInternalSlot(pr, [[InitializedPluralRules]]).\n        let object = this.as_object();\n        let plural_rules = object\n            .as_ref()\n            .and_then(|o| o.downcast_ref::<Self>())\n            .ok_or_else(|| {\n                JsNativeError::typ().with_message(\n                    \"`resolved_options` can only be called on an `Intl.PluralRules` object\",\n                )\n            })?;\n\n        // 3. Let options be OrdinaryObjectCreate(%Object.prototype%).\n        // 4. Let pluralCategories be a List of Strings containing all possible results of\n        //    PluralRuleSelect for the selected locale pr.[[Locale]], sorted according to the following\n        //    order: \"zero\", \"one\", \"two\", \"few\", \"many\", \"other\".\n        let plural_categories = plural_rules\n            .native\n            .rules()\n            .categories()\n            .map(|category| plural_category_to_js_string(category).into());\n\n        // 5. For each row of Table 30, except the header row, in table order, do\n        //        a. Let p be the Property value of the current row.\n        //        b. If p is \"pluralCategories\", then\n        //               i. Let v be CreateArrayFromList(pluralCategories).\n        //        c. Else,\n        //               i. Let v be the value of pr's internal slot whose name is the Internal Slot value of the current row.\n        //        d. If v is not undefined, then\n        //               i. If there is a Conversion value in the current row, then\n        //                      1. Assert: The Conversion value of the current row is number.\n        //                      2. Set v to 𝔽(v).\n        //               ii. Perform ! CreateDataPropertyOrThrow(options, p, v).\n        let mut options = ObjectInitializer::new(context);\n        options\n            .property(\n                js_string!(\"locale\"),\n                js_string!(plural_rules.locale.to_string()),\n                Attribute::all(),\n            )\n            .property(\n                js_string!(\"type\"),\n                match plural_rules.rule_type {\n                    PluralRuleType::Cardinal => js_string!(\"cardinal\"),\n                    PluralRuleType::Ordinal => js_string!(\"ordinal\"),\n                    _ => js_string!(\"unknown\"),\n                },\n                Attribute::all(),\n            )\n            .property(\n                js_string!(\"notation\"),\n                plural_rules.notation.to_js_string(),\n                Attribute::all(),\n            )\n            .property(\n                js_string!(\"minimumIntegerDigits\"),\n                plural_rules.format_options.minimum_integer_digits,\n                Attribute::all(),\n            );\n\n        if let Some(Extrema { minimum, maximum }) =\n            plural_rules.format_options.rounding_type.fraction_digits()\n        {\n            options\n                .property(\n                    js_string!(\"minimumFractionDigits\"),\n                    minimum,\n                    Attribute::all(),\n                )\n                .property(\n                    js_string!(\"maximumFractionDigits\"),\n                    maximum,\n                    Attribute::all(),\n                );\n        }\n\n        if let Some(Extrema { minimum, maximum }) = plural_rules\n            .format_options\n            .rounding_type\n            .significant_digits()\n        {\n            options\n                .property(\n                    js_string!(\"minimumSignificantDigits\"),\n                    minimum,\n                    Attribute::all(),\n                )\n                .property(\n                    js_string!(\"maximumSignificantDigits\"),\n                    maximum,\n                    Attribute::all(),\n                );\n        }\n\n        let plural_categories = Array::create_array_from_list(plural_categories, options.context());\n        options\n            .property(\n                js_string!(\"pluralCategories\"),\n                plural_categories,\n                Attribute::all(),\n            )\n            .property(\n                js_string!(\"roundingIncrement\"),\n                plural_rules.format_options.rounding_increment.to_u16(),\n                Attribute::all(),\n            )\n            .property(\n                js_string!(\"roundingMode\"),\n                match plural_rules.format_options.rounding_mode {\n                    SignedRoundingMode::Unsigned(UnsignedRoundingMode::Expand) => {\n                        js_string!(\"expand\")\n                    }\n                    SignedRoundingMode::Unsigned(UnsignedRoundingMode::Trunc) => {\n                        js_string!(\"trunc\")\n                    }\n                    SignedRoundingMode::Unsigned(UnsignedRoundingMode::HalfExpand) => {\n                        js_string!(\"halfExpand\")\n                    }\n                    SignedRoundingMode::Unsigned(UnsignedRoundingMode::HalfTrunc) => {\n                        js_string!(\"halfTrunc\")\n                    }\n                    SignedRoundingMode::Unsigned(UnsignedRoundingMode::HalfEven) => {\n                        js_string!(\"halfEven\")\n                    }\n                    SignedRoundingMode::Ceil => js_string!(\"ceil\"),\n                    SignedRoundingMode::Floor => js_string!(\"floor\"),\n                    SignedRoundingMode::HalfCeil => js_string!(\"halfCeil\"),\n                    SignedRoundingMode::HalfFloor => js_string!(\"halfFloor\"),\n                    _ => unreachable!(\"unhandled variant of `SignedRoundingMode`\"),\n                },\n                Attribute::all(),\n            )\n            .property(\n                js_string!(\"roundingPriority\"),\n                js_string!(plural_rules.format_options.rounding_priority.to_js_string()),\n                Attribute::all(),\n            )\n            .property(\n                js_string!(\"trailingZeroDisplay\"),\n                plural_rules\n                    .format_options\n                    .trailing_zero_display\n                    .to_js_string(),\n                Attribute::all(),\n            );\n\n        // 6. Return options.\n        Ok(options.build().into())\n    }\n}\n\n#[derive(Debug)]\n#[allow(unused)] // Will be used when we implement `selectRange`\nstruct ResolvedPlural {\n    category: PluralCategory,\n    formatted: Option<Decimal>,\n}\n\n/// Abstract operation [`ResolvePlural ( pluralRules, n )`][spec]\n///\n/// Gets the plural corresponding to the number with the provided formatting options.\n///\n/// [spec]: https://tc39.es/ecma402/#sec-resolveplural\nfn resolve_plural(plural_rules: &PluralRules, n: f64) -> ResolvedPlural {\n    // 1. Assert: Type(pluralRules) is Object.\n    // 2. Assert: pluralRules has an [[InitializedPluralRules]] internal slot.\n    // 3. Assert: Type(n) is Number.\n    // 4. If n is not a finite Number, then\n    if !n.is_finite() {\n        // a. Return \"other\".\n        return ResolvedPlural {\n            category: PluralCategory::Other,\n            formatted: None,\n        };\n    }\n\n    // 5. Let locale be pluralRules.[[Locale]].\n    // 6. Let type be pluralRules.[[Type]].\n    // 7. Let res be ! FormatNumericToString(pluralRules, n).\n    let fixed = plural_rules.format_options.format_f64(n);\n\n    // 8. Let s be res.[[FormattedString]].\n    // 9. Let operands be ! GetOperands(s).\n    // 10. Let p be ! PluralRuleSelect(locale, type, n, operands).\n\n    let category = if let NotationKind::Compact = plural_rules.notation {\n        // TODO: Handle scientific and engineering notation once ICU4X adds support:\n        // https://github.com/unicode-org/icu4x/issues/3983\n        // https://github.com/unicode-org/icu4x/issues/3984\n\n        // get the compact exponent from magnitude\n        let exp = (*fixed.magnitude_range().end()).max(0) as u8;\n        //instead of using full number, this constructs a compact representation (eg: 1500000 => significand = 1.5, exponent= 6)\n        let compact = CompactDecimal::from_significand_and_exponent(fixed.clone(), exp);\n        plural_rules.native.rules().category_for(&compact)\n    } else {\n        plural_rules.native.rules().category_for(&fixed)\n    };\n\n    // 11. Return the Record { [[PluralCategory]]: p, [[FormattedString]]: s }.\n    ResolvedPlural {\n        category,\n        formatted: Some(fixed),\n    }\n}\n\nfn plural_category_to_js_string(category: PluralCategory) -> JsString {\n    match category {\n        PluralCategory::Zero => js_string!(\"zero\"),\n        PluralCategory::One => js_string!(\"one\"),\n        PluralCategory::Two => js_string!(\"two\"),\n        PluralCategory::Few => js_string!(\"few\"),\n        PluralCategory::Many => js_string!(\"many\"),\n        PluralCategory::Other => js_string!(\"other\"),\n    }\n}\n"
  },
  {
    "path": "core/engine/src/builtins/intl/plural_rules/options.rs",
    "content": "use icu_plurals::PluralRuleType;\n\nuse crate::{Context, JsNativeError, JsResult, JsValue, builtins::options::OptionType};\n\nimpl OptionType for PluralRuleType {\n    fn from_value(value: JsValue, context: &mut Context) -> JsResult<Self> {\n        match value.to_string(context)?.to_std_string_escaped().as_str() {\n            \"cardinal\" => Ok(Self::Cardinal),\n            \"ordinal\" => Ok(Self::Ordinal),\n            _ => Err(JsNativeError::range()\n                .with_message(\"provided string was not `cardinal` or `ordinal`\")\n                .into()),\n        }\n    }\n}\n"
  },
  {
    "path": "core/engine/src/builtins/intl/segmenter/iterator.rs",
    "content": "use boa_gc::{Finalize, Trace};\nuse icu_segmenter::{\n    iterators::{GraphemeClusterBreakIterator, SentenceBreakIterator, WordBreakIterator},\n    scaffold::{Latin1, Utf16},\n};\n\nuse crate::{\n    Context, JsData, JsExpect, JsNativeError, JsObject, JsResult, JsString, JsSymbol, JsValue,\n    builtins::{BuiltInBuilder, IntrinsicObject, iterable::create_iter_result_object},\n    context::intrinsics::Intrinsics,\n    js_string,\n    property::Attribute,\n    realm::Realm,\n};\n\nuse super::{Segmenter, create_segment_data_object};\n\npub(crate) enum NativeSegmentIterator<'l, 's> {\n    GraphemeUtf16(GraphemeClusterBreakIterator<'l, 's, Utf16>),\n    WordUtf16(WordBreakIterator<'l, 's, Utf16>),\n    SentenceUtf16(SentenceBreakIterator<'l, 's, Utf16>),\n    GraphemeLatin1(GraphemeClusterBreakIterator<'l, 's, Latin1>),\n    WordLatin1(WordBreakIterator<'l, 's, Latin1>),\n    SentenceLatin1(SentenceBreakIterator<'l, 's, Latin1>),\n}\n\nimpl Iterator for NativeSegmentIterator<'_, '_> {\n    type Item = usize;\n\n    fn next(&mut self) -> Option<Self::Item> {\n        match self {\n            NativeSegmentIterator::GraphemeUtf16(g) => g.next(),\n            NativeSegmentIterator::WordUtf16(w) => w.next(),\n            NativeSegmentIterator::SentenceUtf16(s) => s.next(),\n            NativeSegmentIterator::GraphemeLatin1(g) => g.next(),\n            NativeSegmentIterator::WordLatin1(w) => w.next(),\n            NativeSegmentIterator::SentenceLatin1(s) => s.next(),\n        }\n    }\n}\n\nimpl NativeSegmentIterator<'_, '_> {\n    /// If the iterator is a word break iterator, returns `Some(true)` when the segment preceding\n    /// the current boundary is word-like.\n    pub(crate) fn is_word_like(&self) -> Option<bool> {\n        match self {\n            Self::WordLatin1(w) => Some(w.is_word_like()),\n            Self::WordUtf16(w) => Some(w.is_word_like()),\n            _ => None,\n        }\n    }\n}\n\n#[derive(Debug, Trace, Finalize, JsData)]\npub(crate) struct SegmentIterator {\n    segmenter: JsObject,\n    string: JsString,\n    next_segment_index: usize,\n}\n\nimpl IntrinsicObject for SegmentIterator {\n    fn init(realm: &Realm) {\n        BuiltInBuilder::with_intrinsic::<Self>(realm)\n            .static_property(\n                JsSymbol::to_string_tag(),\n                js_string!(\"Segmenter String Iterator\"),\n                Attribute::CONFIGURABLE,\n            )\n            .static_method(Self::next, js_string!(\"next\"), 0)\n            .build();\n    }\n\n    fn get(intrinsics: &Intrinsics) -> JsObject {\n        intrinsics.objects().iterator_prototypes().segment()\n    }\n}\n\nimpl SegmentIterator {\n    /// [`CreateSegmentIterator ( segmenter, string )`][spec]\n    ///\n    /// [spec]: https://tc39.es/ecma402/#sec-createsegmentiterator\n    pub(crate) fn create(segmenter: JsObject, string: JsString, context: &mut Context) -> JsObject {\n        // 1. Let internalSlotsList be « [[IteratingSegmenter]], [[IteratedString]], [[IteratedStringNextSegmentCodeUnitIndex]] ».\n        // 2. Let iterator be OrdinaryObjectCreate(%SegmentIteratorPrototype%, internalSlotsList).\n        // 3. Set iterator.[[IteratingSegmenter]] to segmenter.\n        // 4. Set iterator.[[IteratedString]] to string.\n        // 5. Set iterator.[[IteratedStringNextSegmentCodeUnitIndex]] to 0.\n        // 6. Return iterator.\n        JsObject::from_proto_and_data_with_shared_shape(\n            context.root_shape(),\n            context\n                .intrinsics()\n                .objects()\n                .iterator_prototypes()\n                .segment(),\n            Self {\n                segmenter,\n                string,\n                next_segment_index: 0,\n            },\n        )\n        .upcast()\n    }\n    /// [`%SegmentIteratorPrototype%.next ( )`][spec]\n    ///\n    /// [spec]: https://tc39.es/ecma402/#sec-%segmentiteratorprototype%.next\n    fn next(this: &JsValue, _: &[JsValue], context: &mut Context) -> JsResult<JsValue> {\n        // 1. Let iterator be the this value.\n        // 2. Perform ? RequireInternalSlot(iterator, [[IteratingSegmenter]]).\n        let object = this.as_object();\n        let mut iter = object\n            .as_ref()\n            .and_then(JsObject::downcast_mut::<Self>)\n            .ok_or_else(|| {\n                JsNativeError::typ()\n                    .with_message(\"`next` can only be called on a `Segment Iterator` object\")\n            })?;\n\n        // 5. Let startIndex be iterator.[[IteratedStringNextSegmentCodeUnitIndex]].\n        let start = iter.next_segment_index;\n\n        // 4. Let string be iterator.[[IteratedString]].\n        // 6. Let endIndex be ! FindBoundary(segmenter, string, startIndex, after).\n        let Some((end, is_word_like)) = iter.string.get(start..).and_then(|string| {\n            // 3. Let segmenter be iterator.[[IteratingSegmenter]].\n            let segmenter = iter\n                .segmenter\n                .downcast_ref::<Segmenter>()\n                .js_expect(\"segment iterator object should contain a segmenter\")\n                .ok()?;\n            let mut segments = segmenter.native.segment(string.variant());\n            // the first elem is always 0.\n            segments.next();\n            segments\n                .next()\n                .map(|end| (start + end, segments.is_word_like()))\n        }) else {\n            // 7. If endIndex is not finite, then\n            //     a. Return CreateIterResultObject(undefined, true).\n            return Ok(create_iter_result_object(\n                JsValue::undefined(),\n                true,\n                context,\n            ));\n        };\n        // 8. Set iterator.[[IteratedStringNextSegmentCodeUnitIndex]] to endIndex.\n        iter.next_segment_index = end;\n\n        // 9. Let segmentData be ! CreateSegmentDataObject(segmenter, string, startIndex, endIndex).\n        let segment_data =\n            create_segment_data_object(iter.string.clone(), start..end, is_word_like, context);\n\n        // 10. Return CreateIterResultObject(segmentData, false).\n        Ok(create_iter_result_object(\n            segment_data.into(),\n            false,\n            context,\n        ))\n    }\n}\n"
  },
  {
    "path": "core/engine/src/builtins/intl/segmenter/mod.rs",
    "content": "use std::ops::Range;\n\nuse crate::{\n    Context, JsArgs, JsData, JsNativeError, JsResult, JsString, JsSymbol, JsValue,\n    builtins::{\n        BuiltInBuilder, BuiltInConstructor, BuiltInObject, IntrinsicObject,\n        intl::options::EmptyPreferences,\n        options::{get_option, get_options_object},\n    },\n    context::intrinsics::{Intrinsics, StandardConstructor, StandardConstructors},\n    js_string,\n    object::{JsObject, ObjectInitializer, internal_methods::get_prototype_from_constructor},\n    property::Attribute,\n    realm::Realm,\n    string::StaticJsStrings,\n};\nuse boa_gc::{Finalize, Trace};\nuse boa_string::JsStrVariant;\nuse icu_collator::provider::CollationDiacriticsV1;\nuse icu_locale::Locale;\nuse icu_segmenter::{\n    GraphemeClusterSegmenter, SentenceSegmenter, WordSegmenter,\n    options::{SentenceBreakOptions, WordBreakOptions},\n};\n\nmod iterator;\nmod options;\nmod segments;\npub(crate) use iterator::*;\npub(crate) use options::*;\npub(crate) use segments::*;\n\nuse super::{\n    Service,\n    locale::{canonicalize_locale_list, filter_locales, resolve_locale},\n    options::IntlOptions,\n};\n\n#[derive(Debug, Trace, Finalize, JsData)]\n// SAFETY: `Segmenter` doesn't contain any traceable data.\n#[boa_gc(unsafe_empty_trace)]\npub(crate) struct Segmenter {\n    locale: Locale,\n    native: NativeSegmenter,\n}\n\n#[derive(Debug)]\npub(crate) enum NativeSegmenter {\n    Grapheme(Box<GraphemeClusterSegmenter>),\n    Word(Box<WordSegmenter>),\n    Sentence(Box<SentenceSegmenter>),\n}\n\nimpl NativeSegmenter {\n    /// Gets the granularity level of this `NativeSegmenter`.\n    pub(crate) const fn granularity(&self) -> Granularity {\n        match self {\n            Self::Grapheme(_) => Granularity::Grapheme,\n            Self::Word(_) => Granularity::Word,\n            Self::Sentence(_) => Granularity::Sentence,\n        }\n    }\n\n    /// Segment the passed string, returning an iterator with the index boundaries\n    /// of the segments.\n    pub(crate) fn segment<'l, 's>(\n        &'l self,\n        input: JsStrVariant<'s>,\n    ) -> NativeSegmentIterator<'l, 's> {\n        match input {\n            JsStrVariant::Latin1(input) => match self {\n                Self::Grapheme(g) => {\n                    NativeSegmentIterator::GraphemeLatin1(g.as_borrowed().segment_latin1(input))\n                }\n                Self::Word(w) => {\n                    NativeSegmentIterator::WordLatin1(w.as_borrowed().segment_latin1(input))\n                }\n                Self::Sentence(s) => {\n                    NativeSegmentIterator::SentenceLatin1(s.as_borrowed().segment_latin1(input))\n                }\n            },\n            JsStrVariant::Utf16(input) => match self {\n                Self::Grapheme(g) => {\n                    NativeSegmentIterator::GraphemeUtf16(g.as_borrowed().segment_utf16(input))\n                }\n                Self::Word(w) => {\n                    NativeSegmentIterator::WordUtf16(w.as_borrowed().segment_utf16(input))\n                }\n                Self::Sentence(s) => {\n                    NativeSegmentIterator::SentenceUtf16(s.as_borrowed().segment_utf16(input))\n                }\n            },\n        }\n    }\n}\n\nimpl Service for Segmenter {\n    // TODO: Track https://github.com/unicode-org/icu4x/issues/3284\n    // and replace when segmenters are locale-aware.\n    type LangMarker = CollationDiacriticsV1;\n\n    type Preferences = EmptyPreferences;\n}\n\nimpl IntrinsicObject for Segmenter {\n    fn init(realm: &Realm) {\n        BuiltInBuilder::from_standard_constructor::<Self>(realm)\n            .static_method(\n                Self::supported_locales_of,\n                js_string!(\"supportedLocalesOf\"),\n                1,\n            )\n            .property(\n                JsSymbol::to_string_tag(),\n                js_string!(\"Intl.Segmenter\"),\n                Attribute::CONFIGURABLE,\n            )\n            .method(Self::resolved_options, js_string!(\"resolvedOptions\"), 0)\n            .method(Self::segment, js_string!(\"segment\"), 1)\n            .build();\n    }\n\n    fn get(intrinsics: &Intrinsics) -> JsObject {\n        Self::STANDARD_CONSTRUCTOR(intrinsics.constructors()).constructor()\n    }\n}\n\nimpl BuiltInObject for Segmenter {\n    const NAME: JsString = StaticJsStrings::SEGMENTER;\n}\n\nimpl BuiltInConstructor for Segmenter {\n    const CONSTRUCTOR_ARGUMENTS: usize = 0;\n    const PROTOTYPE_STORAGE_SLOTS: usize = 3;\n    const CONSTRUCTOR_STORAGE_SLOTS: usize = 1;\n\n    const STANDARD_CONSTRUCTOR: fn(&StandardConstructors) -> &StandardConstructor =\n        StandardConstructors::segmenter;\n\n    fn constructor(\n        new_target: &JsValue,\n        args: &[JsValue],\n        context: &mut Context,\n    ) -> JsResult<JsValue> {\n        // 1. If NewTarget is undefined, throw a TypeError exception.\n        if new_target.is_undefined() {\n            return Err(JsNativeError::typ()\n                .with_message(\"cannot call `Intl.Collator` constructor without `new`\")\n                .into());\n        }\n        let locales = args.get_or_undefined(0);\n        let options = args.get_or_undefined(1);\n\n        // 4. Let requestedLocales be ? CanonicalizeLocaleList(locales).\n        let requested_locales = canonicalize_locale_list(locales, context)?;\n\n        // 5. Set options to ? GetOptionsObject(options).\n        let options = get_options_object(options)?;\n\n        // 6. Let opt be a new Record.\n        // 7. Let matcher be ? GetOption(options, \"localeMatcher\", string, « \"lookup\", \"best fit\" », \"best fit\").\n        let matcher =\n            get_option(&options, js_string!(\"localeMatcher\"), context)?.unwrap_or_default();\n\n        // 8. Set opt.[[localeMatcher]] to matcher.\n        // 9. Let localeData be %Segmenter%.[[LocaleData]].\n        // 10. Let r be ResolveLocale(%Segmenter%.[[AvailableLocales]], requestedLocales, opt, %Segmenter%.[[RelevantExtensionKeys]], localeData).\n        // 11. Set segmenter.[[Locale]] to r.[[locale]].\n        let locale = resolve_locale::<Self>(\n            requested_locales,\n            &mut IntlOptions {\n                matcher,\n                ..Default::default()\n            },\n            context.intl_provider(),\n        )?;\n\n        // 12. Let granularity be ? GetOption(options, \"granularity\", string, « \"grapheme\", \"word\", \"sentence\" », \"grapheme\").\n        let granularity =\n            get_option(&options, js_string!(\"granularity\"), context)?.unwrap_or_default();\n\n        // 13. Set segmenter.[[SegmenterGranularity]] to granularity.\n        let native = match granularity {\n            Granularity::Grapheme => GraphemeClusterSegmenter::try_new_with_buffer_provider(\n                &context.intl_provider().erased_provider(),\n            )\n            .map(|s| NativeSegmenter::Grapheme(Box::new(s))),\n            Granularity::Word => WordSegmenter::try_new_auto_with_buffer_provider(\n                &context.intl_provider().erased_provider(),\n                {\n                    let mut options = WordBreakOptions::default();\n                    options.content_locale = Some(&locale.id);\n                    options\n                },\n            )\n            .map(|s| NativeSegmenter::Word(Box::new(s))),\n            Granularity::Sentence => SentenceSegmenter::try_new_with_buffer_provider(\n                &context.intl_provider().erased_provider(),\n                {\n                    let mut options = SentenceBreakOptions::default();\n                    options.content_locale = Some(&locale.id);\n                    options\n                },\n            )\n            .map(|s| NativeSegmenter::Sentence(Box::new(s))),\n        }\n        .map_err(|err| JsNativeError::typ().with_message(err.to_string()))?;\n\n        let segmenter = Self { locale, native };\n\n        // 2. Let internalSlotsList be « [[InitializedSegmenter]], [[Locale]], [[SegmenterGranularity]] ».\n        // 3. Let segmenter be ? OrdinaryCreateFromConstructor(NewTarget, \"%Segmenter.prototype%\", internalSlotsList).\n\n        let proto =\n            get_prototype_from_constructor(new_target, StandardConstructors::segmenter, context)?;\n\n        let segmenter =\n            JsObject::from_proto_and_data_with_shared_shape(context.root_shape(), proto, segmenter);\n\n        // 14. Return segmenter.\n        Ok(segmenter.into())\n    }\n}\n\nimpl Segmenter {\n    /// [`Intl.Segmenter.supportedLocalesOf ( locales [ , options ] )`][spec].\n    ///\n    /// Returns an array containing those of the provided locales that are supported in segmenting\n    /// without having to fall back to the runtime's default locale.\n    ///\n    /// More information:\n    ///  - [MDN documentation][mdn]\n    ///\n    /// [spec]: https://tc39.es/ecma402/#sec-intl.segmenter.supportedlocalesof\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/Segmenter/supportedLocalesOf\n    fn supported_locales_of(\n        _: &JsValue,\n        args: &[JsValue],\n        context: &mut Context,\n    ) -> JsResult<JsValue> {\n        let locales = args.get_or_undefined(0);\n        let options = args.get_or_undefined(1);\n\n        // 1. Let availableLocales be %Segmenter%.[[AvailableLocales]].\n        // 2. Let requestedLocales be ? CanonicalizeLocaleList(locales).\n        let requested_locales = canonicalize_locale_list(locales, context)?;\n\n        // 3. Return ? FilterLocales(availableLocales, requestedLocales, options).\n        filter_locales::<Self>(requested_locales, options, context).map(JsValue::from)\n    }\n\n    /// [`Intl.Segmenter.prototype.resolvedOptions ( )`][spec].\n    ///\n    /// Returns a new object with properties reflecting the locale and style formatting options\n    /// computed during the construction of the current `Intl.Segmenter` object.\n    ///\n    /// More information:\n    ///  - [MDN documentation][mdn]\n    ///\n    /// [spec]: https://tc39.es/ecma402/#sec-Intl.Segmenter.prototype.resolvedoptions\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/Segmenter/resolvedOptions\n    fn resolved_options(this: &JsValue, _: &[JsValue], context: &mut Context) -> JsResult<JsValue> {\n        // 1. Let segmenter be the this value.\n        // 2. Perform ? RequireInternalSlot(segmenter, [[InitializedSegmenter]]).\n        let object = this.as_object();\n        let segmenter = object\n            .as_ref()\n            .and_then(JsObject::downcast_ref::<Self>)\n            .ok_or_else(|| {\n                JsNativeError::typ().with_message(\n                    \"`resolved_options` can only be called on an `Intl.Segmenter` object\",\n                )\n            })?;\n\n        // 3. Let options be OrdinaryObjectCreate(%Object.prototype%).\n        // 4. For each row of Table 19, except the header row, in table order, do\n        //     a. Let p be the Property value of the current row.\n        //     b. Let v be the value of segmenter's internal slot whose name is the Internal Slot value of the current row.\n        //     c. Assert: v is not undefined.\n        //     d. Perform ! CreateDataPropertyOrThrow(options, p, v).\n        let options = ObjectInitializer::new(context)\n            .property(\n                js_string!(\"locale\"),\n                js_string!(segmenter.locale.to_string()),\n                Attribute::all(),\n            )\n            .property(\n                js_string!(\"granularity\"),\n                js_string!(segmenter.native.granularity().to_string()),\n                Attribute::all(),\n            )\n            .build();\n\n        // 5. Return options.\n        Ok(options.into())\n    }\n\n    /// [`Intl.Segmenter.prototype.segment ( string )`][spec].\n    ///\n    /// Segments a string according to the locale and granularity of this `Intl.Segmenter` object.\n    ///\n    /// [spec]: https://tc39.es/ecma402/#sec-intl.segmenter.prototype.segment\n    fn segment(this: &JsValue, args: &[JsValue], context: &mut Context) -> JsResult<JsValue> {\n        // 1. Let segmenter be the this value.\n        // 2. Perform ? RequireInternalSlot(segmenter, [[InitializedSegmenter]]).\n        let segmenter = this.as_object().filter(|o| o.is::<Self>()).ok_or_else(|| {\n            JsNativeError::typ()\n                .with_message(\"`resolved_options` can only be called on an `Intl.Segmenter` object\")\n        })?;\n\n        // 3. Let string be ? ToString(string).\n        let string = args.get_or_undefined(0).to_string(context)?;\n\n        // 4. Return ! CreateSegmentsObject(segmenter, string).\n        Ok(Segments::create(segmenter.clone(), string, context).into())\n    }\n}\n\n/// [`CreateSegmentDataObject ( segmenter, string, startIndex, endIndex )`][spec].\n///\n/// [spec]: https://tc39.es/ecma402/#sec-createsegmentdataobject\nfn create_segment_data_object(\n    string: JsString,\n    range: Range<usize>,\n    is_word_like: Option<bool>,\n    context: &mut Context,\n) -> JsObject {\n    // 1. Let len be the length of string.\n    // 2. Assert: startIndex ≥ 0.\n    // ensured by `usize`.\n    // 3. Assert: endIndex ≤ len.\n    assert!(range.end <= string.len());\n    // 4. Assert: startIndex < endIndex.\n    assert!(range.start < range.end);\n\n    let start = range.start;\n\n    // 6. Let segment be the substring of string from startIndex to endIndex.\n    let segment = string.get(range).expect(\"range already checked\");\n\n    // 5. Let result be OrdinaryObjectCreate(%Object.prototype%).\n    let object = &mut ObjectInitializer::new(context);\n\n    object\n        // 7. Perform ! CreateDataPropertyOrThrow(result, \"segment\", segment).\n        .property(js_string!(\"segment\"), segment, Attribute::all())\n        // 8. Perform ! CreateDataPropertyOrThrow(result, \"index\", 𝔽(startIndex)).\n        .property(js_string!(\"index\"), start, Attribute::all())\n        // 9. Perform ! CreateDataPropertyOrThrow(result, \"input\", string).\n        .property(js_string!(\"input\"), string, Attribute::all());\n\n    // 10. Let granularity be segmenter.[[SegmenterGranularity]].\n    // 11. If granularity is \"word\", then\n    if let Some(is_word_like) = is_word_like {\n        //     a. Let isWordLike be a Boolean value indicating whether the segment in string is \"word-like\" according to locale segmenter.[[Locale]].\n        //     b. Perform ! CreateDataPropertyOrThrow(result, \"isWordLike\", isWordLike).\n        object.property(js_string!(\"isWordLike\"), is_word_like, Attribute::all());\n    }\n\n    // 12. Return result.\n    object.build()\n}\n"
  },
  {
    "path": "core/engine/src/builtins/intl/segmenter/options.rs",
    "content": "use std::fmt::Display;\n\nuse crate::builtins::options::ParsableOptionType;\n\n#[derive(Debug, Clone, Copy, Default)]\npub(crate) enum Granularity {\n    #[default]\n    Grapheme,\n    Word,\n    Sentence,\n}\n\nimpl Display for Granularity {\n    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {\n        match self {\n            Self::Grapheme => \"grapheme\",\n            Self::Word => \"word\",\n            Self::Sentence => \"sentence\",\n        }\n        .fmt(f)\n    }\n}\n\n#[derive(Debug)]\npub(crate) struct ParseGranularityError;\n\nimpl Display for ParseGranularityError {\n    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {\n        f.write_str(\"provided string was not `grapheme`, `word` or `sentence`\")\n    }\n}\n\nimpl std::str::FromStr for Granularity {\n    type Err = ParseGranularityError;\n\n    fn from_str(s: &str) -> Result<Self, Self::Err> {\n        match s {\n            \"grapheme\" => Ok(Self::Grapheme),\n            \"word\" => Ok(Self::Word),\n            \"sentence\" => Ok(Self::Sentence),\n            _ => Err(ParseGranularityError),\n        }\n    }\n}\n\nimpl ParsableOptionType for Granularity {}\n"
  },
  {
    "path": "core/engine/src/builtins/intl/segmenter/segments.rs",
    "content": "use boa_gc::{Finalize, Trace};\nuse itertools::Itertools;\n\nuse crate::{\n    Context, JsArgs, JsData, JsExpect, JsNativeError, JsObject, JsResult, JsString, JsSymbol,\n    JsValue,\n    builtins::{BuiltInBuilder, IntrinsicObject},\n    context::intrinsics::Intrinsics,\n    js_string,\n    realm::Realm,\n};\n\nuse super::{SegmentIterator, Segmenter, create_segment_data_object};\n\n#[derive(Debug, Trace, Finalize, JsData)]\npub(crate) struct Segments {\n    segmenter: JsObject,\n    string: JsString,\n}\n\nimpl IntrinsicObject for Segments {\n    fn init(realm: &Realm) {\n        BuiltInBuilder::with_intrinsic::<Self>(realm)\n            .static_method(Self::containing, js_string!(\"containing\"), 1)\n            .static_method(Self::iterator, JsSymbol::iterator(), 0)\n            .build();\n    }\n\n    fn get(intrinsics: &Intrinsics) -> JsObject {\n        intrinsics.objects().segments_prototype()\n    }\n}\n\nimpl Segments {\n    /// [`CreateSegmentsObject ( segmenter, string )`][spec]\n    ///\n    /// [spec]: https://tc39.es/ecma402/#sec-createsegmentsobject\n    pub(crate) fn create(segmenter: JsObject, string: JsString, context: &mut Context) -> JsObject {\n        // 1. Let internalSlotsList be « [[SegmentsSegmenter]], [[SegmentsString]] ».\n        // 2. Let segments be OrdinaryObjectCreate(%SegmentsPrototype%, internalSlotsList).\n        // 3. Set segments.[[SegmentsSegmenter]] to segmenter.\n        // 4. Set segments.[[SegmentsString]] to string.\n        // 5. Return segments.\n        JsObject::from_proto_and_data_with_shared_shape(\n            context.root_shape(),\n            context.intrinsics().objects().segments_prototype(),\n            Self { segmenter, string },\n        )\n        .upcast()\n    }\n\n    /// [`%SegmentsPrototype%.containing ( index )`][spec]\n    ///\n    /// [spec]: https://tc39.es/ecma402/#sec-%segmentsprototype%.containing\n    fn containing(this: &JsValue, args: &[JsValue], context: &mut Context) -> JsResult<JsValue> {\n        // 1. Let segments be the this value.\n        // 2. Perform ? RequireInternalSlot(segments, [[SegmentsSegmenter]]).\n        let object = this.as_object();\n        let segments = object\n            .as_ref()\n            .and_then(JsObject::downcast_ref::<Self>)\n            .ok_or_else(|| {\n                JsNativeError::typ()\n                    .with_message(\"`containing` can only be called on a `Segments` object\")\n            })?;\n\n        // 3. Let segmenter be segments.[[SegmentsSegmenter]].\n        let segmenter = segments\n            .segmenter\n            .downcast_ref::<Segmenter>()\n            .js_expect(\"segments object should contain a segmenter\")?;\n\n        // 4. Let string be segments.[[SegmentsString]].\n        // 5. Let len be the length of string.\n        let len = segments.string.len() as i64;\n\n        // 6. Let n be ? ToIntegerOrInfinity(index).\n        let Some(n) = args\n            .get_or_undefined(0)\n            .to_integer_or_infinity(context)?\n            .as_integer()\n            // 7. If n < 0 or n ≥ len, return undefined.\n            .filter(|i| (0..len).contains(i))\n            .map(|n| n as usize)\n        else {\n            return Ok(JsValue::undefined());\n        };\n\n        // 8. Let startIndex be ! FindBoundary(segmenter, string, n, before).\n        // 9. Let endIndex be ! FindBoundary(segmenter, string, n, after).\n        let (range, is_word_like) = {\n            let mut segments = segmenter.native.segment(segments.string.variant());\n            std::iter::from_fn(|| segments.next().map(|i| (i, segments.is_word_like())))\n                .tuple_windows()\n                .find(|((i, _), (j, _))| (*i..*j).contains(&n))\n                .map(|((i, _), (j, word))| ((i..j), word))\n                .js_expect(\"string should have at least a length of 1, and `n` must be in range\")?\n        };\n\n        // 10. Return ! CreateSegmentDataObject(segmenter, string, startIndex, endIndex).\n        Ok(\n            create_segment_data_object(segments.string.clone(), range, is_word_like, context)\n                .into(),\n        )\n    }\n\n    /// [`%SegmentsPrototype% [ @@iterator ] ( )`][spec]\n    ///\n    /// [spec]: https://tc39.es/ecma402/#sec-%segmentsprototype%-@@iterator\n    fn iterator(this: &JsValue, _: &[JsValue], context: &mut Context) -> JsResult<JsValue> {\n        // 1. Let segments be the this value.\n        // 2. Perform ? RequireInternalSlot(segments, [[SegmentsSegmenter]]).\n        let object = this.as_object();\n        let segments = object\n            .as_ref()\n            .and_then(|o| o.downcast_ref::<Self>())\n            .ok_or_else(|| {\n                JsNativeError::typ()\n                    .with_message(\"`containing` can only be called on a `Segments` object\")\n            })?;\n\n        // 3. Let segmenter be segments.[[SegmentsSegmenter]].\n        // 4. Let string be segments.[[SegmentsString]].\n        // 5. Return ! CreateSegmentIterator(segmenter, string).\n        Ok(\n            SegmentIterator::create(segments.segmenter.clone(), segments.string.clone(), context)\n                .into(),\n        )\n    }\n}\n"
  },
  {
    "path": "core/engine/src/builtins/is_html_dda.rs",
    "content": "//! Implementation of the `[[IsHTMLDDA]]` internal slot.\n//!\n//! Objects with this internal slot have special behavior:\n//! - `typeof` returns `\"undefined\"`\n//! - `ToBoolean` returns `false`\n//! - Abstract equality with `null` or `undefined` returns `true`\n//!\n//! More information:\n//!  - [ECMAScript reference][spec]\n//!\n//! [spec]: https://tc39.es/ecma262/#sec-IsHTMLDDA-internal-slot\n\nuse boa_gc::{Finalize, Trace};\n\nuse crate::{\n    JsResult, JsValue,\n    object::{\n        JsData, JsObject,\n        internal_methods::{\n            CallValue, InternalMethodCallContext, InternalObjectMethods, ORDINARY_INTERNAL_METHODS,\n        },\n    },\n};\n\n/// Marker struct for objects with the `[[IsHTMLDDA]]` internal slot.\n///\n/// This is used by the `$262.IsHTMLDDA` test harness object and models the\n/// legacy `document.all` behavior per ECMAScript Annex B §B.3.6.\n///\n/// The object is callable — when called, it returns `undefined`.\n#[derive(Debug, Clone, Copy, Trace, Finalize)]\n#[boa_gc(empty_trace)]\npub struct IsHTMLDDA;\n\nimpl JsData for IsHTMLDDA {\n    fn internal_methods(&self) -> &'static InternalObjectMethods {\n        static IS_HTML_DDA_INTERNAL_METHODS: InternalObjectMethods = InternalObjectMethods {\n            __call__: is_html_dda_call,\n            ..ORDINARY_INTERNAL_METHODS\n        };\n        &IS_HTML_DDA_INTERNAL_METHODS\n    }\n}\n\n/// The `[[Call]]` internal method for `IsHTMLDDA` objects.\n///\n/// When called, simply returns `undefined`.\n#[allow(clippy::unnecessary_wraps)]\nfn is_html_dda_call(\n    _obj: &JsObject,\n    argument_count: usize,\n    context: &mut InternalMethodCallContext<'_>,\n) -> JsResult<CallValue> {\n    // Pop the arguments, function, and this from the stack.\n    let _args = context\n        .vm\n        .stack\n        .calling_convention_pop_arguments(argument_count);\n    let _func = context.vm.stack.pop();\n    let _this = context.vm.stack.pop();\n\n    // Push undefined as the return value.\n    context.vm.stack.push(JsValue::undefined());\n\n    Ok(CallValue::Complete)\n}\n"
  },
  {
    "path": "core/engine/src/builtins/iterable/async_from_sync_iterator.rs",
    "content": "use crate::{\n    Context, JsArgs, JsData, JsError, JsExpect, JsNativeError, JsResult, JsValue,\n    builtins::{\n        BuiltInBuilder, IntrinsicObject, Promise,\n        iterable::{IteratorRecord, IteratorResult, create_iter_result_object},\n        promise::{PromiseCapability, if_abrupt_reject_promise},\n    },\n    context::intrinsics::Intrinsics,\n    js_string,\n    native_function::NativeFunction,\n    object::{FunctionObjectBuilder, JsObject},\n    realm::Realm,\n};\nuse boa_gc::{Finalize, Trace};\n\n/// `%AsyncFromSyncIteratorPrototype%` object.\n///\n/// More information:\n///  - [ECMA reference][spec]\n///\n/// [spec]: https://tc39.es/ecma262/#sec-properties-of-async-from-sync-iterator-instances\n#[derive(Clone, Debug, Finalize, Trace, JsData)]\npub(crate) struct AsyncFromSyncIterator {\n    // The [[SyncIteratorRecord]] internal slot.\n    sync_iterator_record: IteratorRecord,\n}\n\nimpl IntrinsicObject for AsyncFromSyncIterator {\n    fn init(realm: &Realm) {\n        BuiltInBuilder::with_intrinsic::<Self>(realm)\n            .prototype(\n                realm\n                    .intrinsics()\n                    .objects()\n                    .iterator_prototypes()\n                    .async_iterator(),\n            )\n            .static_method(Self::next, js_string!(\"next\"), 1)\n            .static_method(Self::r#return, js_string!(\"return\"), 1)\n            .static_method(Self::throw, js_string!(\"throw\"), 1)\n            .build();\n    }\n\n    fn get(intrinsics: &Intrinsics) -> JsObject {\n        intrinsics\n            .objects()\n            .iterator_prototypes()\n            .async_from_sync_iterator()\n    }\n}\n\nimpl AsyncFromSyncIterator {\n    /// `CreateAsyncFromSyncIterator ( syncIteratorRecord )`\n    ///\n    /// More information:\n    ///  - [ECMA reference][spec]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-createasyncfromsynciterator\n    pub(crate) fn create(\n        sync_iterator_record: IteratorRecord,\n        context: &mut Context,\n    ) -> IteratorRecord {\n        // 1. Let asyncIterator be OrdinaryObjectCreate(%AsyncFromSyncIteratorPrototype%, « [[SyncIteratorRecord]] »).\n        // 2. Set asyncIterator.[[SyncIteratorRecord]] to syncIteratorRecord.\n        let async_iterator = JsObject::from_proto_and_data_with_shared_shape(\n            context.root_shape(),\n            context\n                .intrinsics()\n                .objects()\n                .iterator_prototypes()\n                .async_from_sync_iterator(),\n            Self {\n                sync_iterator_record,\n            },\n        )\n        .upcast();\n\n        // 3. Let nextMethod be ! Get(asyncIterator, \"next\").\n        let next_method = async_iterator\n            .get(js_string!(\"next\"), context)\n            .expect(\"async from sync iterator prototype must have next method\");\n\n        // 4. Let iteratorRecord be the Iterator Record { [[Iterator]]: asyncIterator, [[NextMethod]]: nextMethod, [[Done]]: false }.\n        // 5. Return iteratorRecord.\n        IteratorRecord::new(async_iterator, next_method)\n    }\n\n    /// `%AsyncFromSyncIteratorPrototype%.next ( [ value ] )`\n    ///\n    /// More information:\n    ///  - [ECMA reference][spec]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-%asyncfromsynciteratorprototype%.next\n    fn next(this: &JsValue, args: &[JsValue], context: &mut Context) -> JsResult<JsValue> {\n        // 1. Let O be the this value.\n        // 2. Assert: O is an Object that has a [[SyncIteratorRecord]] internal slot.\n        // 4. Let syncIteratorRecord be O.[[SyncIteratorRecord]].\n        let object = this.as_object();\n        let mut sync_iterator_record = object\n            .as_ref()\n            .and_then(JsObject::downcast_ref::<Self>)\n            .js_expect(\"async from sync iterator prototype must be object\")?\n            .sync_iterator_record\n            .clone();\n\n        // 3. Let promiseCapability be ! NewPromiseCapability(%Promise%).\n        let promise_capability = PromiseCapability::new(\n            &context.intrinsics().constructors().promise().constructor(),\n            context,\n        )\n        .js_expect(\"cannot fail with promise constructor\")?;\n\n        // 5. If value is present, then\n        //     a. Let result be Completion(IteratorNext(syncIteratorRecord, value)).\n        // 6. Else,\n        //     a. Let result be Completion(IteratorNext(syncIteratorRecord)).\n        let result = sync_iterator_record.next(args.first(), context);\n\n        // 7. IfAbruptRejectPromise(result, promiseCapability).\n        let result = if_abrupt_reject_promise!(result, promise_capability, context);\n\n        // 8. Return AsyncFromSyncIteratorContinuation(result, promiseCapability, syncIteratorRecord, true).\n        Self::continuation(\n            &result,\n            &promise_capability,\n            sync_iterator_record,\n            true,\n            context,\n        )\n    }\n\n    /// `%AsyncFromSyncIteratorPrototype%.return ( [ value ] )`\n    ///\n    /// More information:\n    ///  - [ECMA reference][spec]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-%asyncfromsynciteratorprototype%.return\n    fn r#return(this: &JsValue, args: &[JsValue], context: &mut Context) -> JsResult<JsValue> {\n        // 1. Let O be the this value.\n        // 2. Assert: O is an Object that has a [[SyncIteratorRecord]] internal slot.\n        // 4. Let syncIteratorRecord be O.[[SyncIteratorRecord]].\n        let object = this.as_object();\n        let sync_iterator_record = object\n            .as_ref()\n            .and_then(JsObject::downcast_ref::<Self>)\n            .js_expect(\"async from sync iterator prototype must be object\")?\n            .sync_iterator_record\n            .clone();\n        // 5. Let syncIterator be syncIteratorRecord.[[Iterator]].\n        let sync_iterator = sync_iterator_record.iterator().clone();\n\n        // 3. Let promiseCapability be ! NewPromiseCapability(%Promise%).\n        let promise_capability = PromiseCapability::new(\n            &context.intrinsics().constructors().promise().constructor(),\n            context,\n        )\n        .js_expect(\"cannot fail with promise constructor\")?;\n\n        // 6. Let return be Completion(GetMethod(syncIterator, \"return\")).\n        let r#return = sync_iterator.get_method(js_string!(\"return\"), context);\n\n        // 7. IfAbruptRejectPromise(return, promiseCapability).\n        let r#return = if_abrupt_reject_promise!(r#return, promise_capability, context);\n\n        let result = match (r#return, args.first()) {\n            // 8. If return is undefined, then\n            (None, _) => {\n                // a. Let iterResult be CreateIterResultObject(value, true).\n                let iter_result =\n                    create_iter_result_object(args.get_or_undefined(0).clone(), true, context);\n\n                // b. Perform ! Call(promiseCapability.[[Resolve]], undefined, « iterResult »).\n                promise_capability\n                    .resolve()\n                    .call(&JsValue::undefined(), &[iter_result], context)\n                    .js_expect(\"cannot fail according to spec\")?;\n\n                // c. Return promiseCapability.[[Promise]].\n                return Ok(promise_capability.promise().clone().into());\n            }\n            // 9. If value is present, then\n            (Some(r#return), Some(value)) => {\n                // a. Let result be Completion(Call(return, syncIterator, « value »)).\n                r#return.call(\n                    &sync_iterator.clone().into(),\n                    std::slice::from_ref(value),\n                    context,\n                )\n            }\n            // 10. Else,\n            (Some(r#return), None) => {\n                // a. Let result be Completion(Call(return, syncIterator)).\n                r#return.call(&sync_iterator.clone().into(), &[], context)\n            }\n        };\n\n        // 12. If Type(result) is not Object, then\n        //     a. Perform ! Call(promiseCapability.[[Reject]], undefined, « a newly created TypeError object »).\n        let result = result.and_then(IteratorResult::from_value);\n\n        // 11. IfAbruptRejectPromise(result, promiseCapability).\n        let result = if_abrupt_reject_promise!(result, promise_capability, context);\n\n        // 13. Return AsyncFromSyncIteratorContinuation(result, promiseCapability, syncIteratorRecord, false).\n        Self::continuation(\n            &result,\n            &promise_capability,\n            sync_iterator_record,\n            false,\n            context,\n        )\n    }\n\n    /// `%AsyncFromSyncIteratorPrototype%.throw ( [ value ] )`\n    ///\n    /// More information:\n    ///  - [ECMA reference][spec]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-%asyncfromsynciteratorprototype%.throw\n    fn throw(this: &JsValue, args: &[JsValue], context: &mut Context) -> JsResult<JsValue> {\n        // 1. Let O be the this value.\n        // 2. Assert: O is an Object that has a [[SyncIteratorRecord]] internal slot.\n        // 4. Let syncIteratorRecord be O.[[SyncIteratorRecord]].\n        let object = this.as_object();\n        let sync_iterator_record = object\n            .as_ref()\n            .and_then(JsObject::downcast_ref::<Self>)\n            .js_expect(\"async from sync iterator prototype must be object\")?\n            .sync_iterator_record\n            .clone();\n        // 5. Let syncIterator be syncIteratorRecord.[[Iterator]].\n        let sync_iterator = sync_iterator_record.iterator().clone();\n\n        // 3. Let promiseCapability be ! NewPromiseCapability(%Promise%).\n        let promise_capability = PromiseCapability::new(\n            &context.intrinsics().constructors().promise().constructor(),\n            context,\n        )\n        .js_expect(\"cannot fail with promise constructor\")?;\n\n        // 6. Let throw be Completion(GetMethod(syncIterator, \"throw\")).\n        let throw = sync_iterator.get_method(js_string!(\"throw\"), context);\n\n        // 7. IfAbruptRejectPromise(throw, promiseCapability).\n        let throw = if_abrupt_reject_promise!(throw, promise_capability, context);\n\n        let result = match (throw, args.first()) {\n            // 8. If throw is undefined, then\n            (None, _) => {\n                // a. NOTE: If syncIterator does not have a throw method, close it to give it a chance to clean up before we reject the capability.\n                // b. Let closeCompletion be NormalCompletion(empty).\n                // c. Let result be Completion(IteratorClose(syncIteratorRecord, closeCompletion)).\n                let result = sync_iterator_record.close(Ok(JsValue::undefined()), context);\n                // d. IfAbruptRejectPromise(result, promiseCapability).\n                if_abrupt_reject_promise!(result, promise_capability, context);\n\n                // e. NOTE: The next step throws a TypeError to indicate that there was a protocol violation: syncIterator does not have a throw method.\n                // f. NOTE: If closing syncIterator does not throw then the result of that operation is ignored, even if it yields a rejected promise.\n                // g. Perform ! Call(promiseCapability.[[Reject]], undefined, « a newly created TypeError object »).\n                promise_capability\n                    .reject()\n                    .call(\n                        &JsValue::undefined(),\n                        &[JsNativeError::typ()\n                            .with_message(\"sync iterator does not have a throw method\")\n                            .into_opaque(context)\n                            .into()],\n                        context,\n                    )\n                    .js_expect(\"cannot fail according to spec\")?;\n\n                // h. Return promiseCapability.[[Promise]].\n                return Ok(promise_capability.promise().clone().into());\n            }\n            // 9. If value is present, then\n            (Some(throw), Some(value)) => {\n                // a. Let result be Completion(Call(throw, syncIterator, « value »)).\n                throw.call(\n                    &sync_iterator.clone().into(),\n                    std::slice::from_ref(value),\n                    context,\n                )\n            }\n            // 10. Else,\n            (Some(throw), None) => {\n                // a. Let result be Completion(Call(throw, syncIterator)).\n                throw.call(&sync_iterator.clone().into(), &[], context)\n            }\n        };\n\n        // 12. If Type(result) is not Object, then\n        // a. Perform ! Call(promiseCapability.[[Reject]], undefined, « a newly created TypeError object »).\n        let result = result.and_then(IteratorResult::from_value);\n\n        // 11. IfAbruptRejectPromise(result, promiseCapability).\n        let result = if_abrupt_reject_promise!(result, promise_capability, context);\n\n        // 13. Return Return AsyncFromSyncIteratorContinuation(result, promiseCapability, syncIteratorRecord, true).\n        Self::continuation(\n            &result,\n            &promise_capability,\n            sync_iterator_record,\n            true,\n            context,\n        )\n    }\n\n    /// `AsyncFromSyncIteratorContinuation ( result, promiseCapability )`\n    ///\n    /// More information:\n    ///  - [ECMA reference][spec]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-asyncfromsynciteratorcontinuation\n    fn continuation(\n        result: &IteratorResult,\n        promise_capability: &PromiseCapability,\n        sync_iterator_record: IteratorRecord,\n        close_on_rejection: bool,\n        context: &mut Context,\n    ) -> JsResult<JsValue> {\n        // 1. NOTE: Because promiseCapability is derived from the intrinsic %Promise%,\n        // the calls to promiseCapability.[[Reject]] entailed by the\n        // use IfAbruptRejectPromise below are guaranteed not to throw.\n\n        // 2. Let done be Completion(IteratorComplete(result)).\n        let done = result.complete(context);\n\n        // 3. IfAbruptRejectPromise(done, promiseCapability).\n        let done = if_abrupt_reject_promise!(done, promise_capability, context);\n\n        // 4. Let value be Completion(IteratorValue(result)).\n        let value = result.value(context);\n\n        // 5. IfAbruptRejectPromise(value, promiseCapability).\n        let value = if_abrupt_reject_promise!(value, promise_capability, context);\n\n        // 6. Let valueWrapper be Completion(PromiseResolve(%Promise%, value)).\n        let value_wrapper = match Promise::promise_resolve(\n            &context.intrinsics().constructors().promise().constructor(),\n            value,\n            context,\n        ) {\n            // 7. If valueWrapper is an abrupt completion, done is false, and closeOnRejection is\n            //    true, then\n            Err(e) if !done && close_on_rejection => {\n                // a. Set valueWrapper to Completion(IteratorClose(syncIteratorRecord, valueWrapper)).\n                Err(sync_iterator_record.close(Err(e), context).expect_err(\n                    \"closing an iterator with an error must always return an error back\",\n                ))\n            }\n            other => other.map(|o| {\n                o.downcast::<Promise>()\n                    .expect(\"%Promise% constructor must return a `Promise` object\")\n            }),\n        };\n\n        // 8. IfAbruptRejectPromise(valueWrapper, promiseCapability).\n        let value_wrapper = if_abrupt_reject_promise!(value_wrapper, promise_capability, context);\n\n        // 9. Let unwrap be a new Abstract Closure with parameters (value)\n        // that captures done and performs the following steps when called:\n        // 10. Let onFulfilled be CreateBuiltinFunction(unwrap, 1, \"\", « »).\n        let on_fulfilled = FunctionObjectBuilder::new(\n            context.realm(),\n            NativeFunction::from_copy_closure(move |_this, args, context| {\n                // a. Return CreateIterResultObject(value, done).\n                Ok(create_iter_result_object(\n                    args.get_or_undefined(0).clone(),\n                    done,\n                    context,\n                ))\n            }),\n        )\n        .name(js_string!())\n        .length(1)\n        .build();\n\n        // 11. NOTE: onFulfilled is used when processing the \"value\" property of an\n        // IteratorResult object in order to wait for its value if it is a promise and\n        // re-package the result in a new \"unwrapped\" IteratorResult object.\n\n        // 12. If done is true, or if closeOnRejection is false, then\n        let on_rejected = if done || !close_on_rejection {\n            // a. Let onRejected be undefined.\n            None\n        } else {\n            // 13. Else,\n            //     a. Let closeIterator be a new Abstract Closure with parameters (error) that\n            //        captures syncIteratorRecord and performs the following steps when called:\n            //     b. Let onRejected be CreateBuiltinFunction(closeIterator, 1, \"\", « »).\n            //     c. NOTE: onRejected is used to close the Iterator when the \"value\" property of an\n            //        IteratorResult object it yields is a rejected promise.\n            Some(\n                FunctionObjectBuilder::new(\n                    context.realm(),\n                    NativeFunction::from_copy_closure_with_captures(\n                        |_this, args, iter, context| {\n                            // i. Return ? IteratorClose(syncIteratorRecord, ThrowCompletion(error)).\n                            iter.close(\n                                Err(JsError::from_opaque(args.get_or_undefined(0).clone())),\n                                context,\n                            )\n                        },\n                        sync_iterator_record,\n                    ),\n                )\n                .name(js_string!())\n                .length(1)\n                .build(),\n            )\n        };\n\n        // 14. Perform PerformPromiseThen(valueWrapper, onFulfilled, undefined, promiseCapability).\n        Promise::perform_promise_then(\n            &value_wrapper,\n            Some(on_fulfilled),\n            on_rejected,\n            Some(promise_capability.clone()),\n            context,\n        );\n\n        // 15. Return promiseCapability.[[Promise]].\n        Ok(promise_capability.promise().clone().into())\n    }\n}\n"
  },
  {
    "path": "core/engine/src/builtins/iterable/iterator_constructor.rs",
    "content": "//! Boa's implementation of the ECMAScript `Iterator` constructor.\n//!\n//! The `Iterator` constructor is designed to be subclassed. It may be used as the\n//! value of an extends clause of a class definition.\n//!\n//! More information:\n//!  - [ECMAScript reference][spec]\n//!\n//! [spec]: https://tc39.es/ecma262/#sec-iterator-constructor\n\nuse crate::{\n    Context, JsArgs, JsData, JsResult, JsString, JsValue,\n    builtins::{\n        BuiltInBuilder, BuiltInConstructor, BuiltInObject, IntrinsicObject, object::OrdinaryObject,\n    },\n    context::intrinsics::{Intrinsics, StandardConstructor, StandardConstructors},\n    error::JsNativeError,\n    js_string,\n    object::JsObject,\n    property::Attribute,\n    realm::Realm,\n    string::StaticJsStrings,\n    symbol::JsSymbol,\n};\nuse boa_gc::{Finalize, Trace};\n\nuse super::{\n    if_abrupt_close_iterator,\n    iterator_helper::{IteratorHelper, IteratorHelperOp},\n    wrap_for_valid_iterator::WrapForValidIterator,\n};\n\n/// The `Iterator` constructor.\n///\n/// More information:\n///  - [ECMAScript reference][spec]\n///\n/// [spec]: https://tc39.es/ecma262/#sec-iterator-constructor\n#[derive(Debug, Clone, Trace, Finalize, JsData)]\npub(crate) struct IteratorConstructor;\n\nimpl IntrinsicObject for IteratorConstructor {\n    fn init(realm: &Realm) {\n        let get_constructor = BuiltInBuilder::callable(realm, Self::get_constructor)\n            .name(js_string!(\"get constructor\"))\n            .build();\n        let set_constructor = BuiltInBuilder::callable(realm, Self::set_constructor)\n            .name(js_string!(\"set constructor\"))\n            .build();\n        let get_to_string_tag = BuiltInBuilder::callable(realm, Self::get_to_string_tag)\n            .name(js_string!(\"get [Symbol.toStringTag]\"))\n            .build();\n        let set_to_string_tag = BuiltInBuilder::callable(realm, Self::set_to_string_tag)\n            .name(js_string!(\"set [Symbol.toStringTag]\"))\n            .build();\n\n        // Per the spec, `Iterator.prototype.constructor` must be a configurable,\n        // non-enumerable get/set accessor (web-compat requirement).  We use the\n        // builder's `constructor_accessor` support so the property is part of the\n        // shared-shape allocation rather than a post-build override.\n        BuiltInBuilder::from_standard_constructor::<Self>(realm)\n            .inherits(Some(\n                realm\n                    .intrinsics()\n                    .objects()\n                    .iterator_prototypes()\n                    .iterator(),\n            ))\n            // Static methods\n            .static_method(Self::from, js_string!(\"from\"), 1)\n            .static_method(Self::concat, js_string!(\"concat\"), 0)\n            // Prototype methods — lazy (return IteratorHelper)\n            .method(Self::map, js_string!(\"map\"), 1)\n            .method(Self::filter, js_string!(\"filter\"), 1)\n            .method(Self::take, js_string!(\"take\"), 1)\n            .method(Self::drop, js_string!(\"drop\"), 1)\n            .method(Self::flat_map, js_string!(\"flatMap\"), 1)\n            // Prototype methods — eager (consume the iterator)\n            .method(Self::reduce, js_string!(\"reduce\"), 1)\n            .method(Self::to_array, js_string!(\"toArray\"), 0)\n            .method(Self::for_each, js_string!(\"forEach\"), 1)\n            .method(Self::some, js_string!(\"some\"), 1)\n            .method(Self::every, js_string!(\"every\"), 1)\n            .method(Self::find, js_string!(\"find\"), 1)\n            // Accessor: Iterator.prototype[@@toStringTag]\n            .accessor(\n                JsSymbol::to_string_tag(),\n                Some(get_to_string_tag),\n                Some(set_to_string_tag),\n                Attribute::CONFIGURABLE,\n            )\n            // Accessor: Iterator.prototype.constructor (web-compat, 2 slots)\n            .constructor_accessor(get_constructor, set_constructor)\n            .build();\n    }\n\n    fn get(intrinsics: &Intrinsics) -> JsObject {\n        intrinsics.constructors().iterator().constructor()\n    }\n}\n\nimpl BuiltInObject for IteratorConstructor {\n    const NAME: JsString = StaticJsStrings::ITERATOR;\n}\n\nimpl BuiltInConstructor for IteratorConstructor {\n    const PROTOTYPE_STORAGE_SLOTS: usize = 14; // 11 methods + @@toStringTag accessor (2 slots) + constructor accessor (2 slots)\n    const CONSTRUCTOR_STORAGE_SLOTS: usize = 2;\n    const CONSTRUCTOR_ARGUMENTS: usize = 0;\n    const STANDARD_CONSTRUCTOR: fn(&StandardConstructors) -> &StandardConstructor =\n        StandardConstructors::iterator;\n\n    /// `Iterator ( )`\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-iterator\n    fn constructor(\n        new_target: &JsValue,\n        _args: &[JsValue],\n        context: &mut Context,\n    ) -> JsResult<JsValue> {\n        // 1. If NewTarget is undefined or the active function object, throw a TypeError exception.\n        if new_target.is_undefined()\n            || new_target\n                == &context\n                    .active_function_object()\n                    .unwrap_or_else(|| context.intrinsics().constructors().iterator().constructor())\n                    .into()\n        {\n            return Err(JsNativeError::typ()\n                .with_message(if new_target.is_undefined() {\n                    \"Iterator constructor requires 'new'\"\n                } else {\n                    \"Abstract class Iterator not directly constructable\"\n                })\n                .into());\n        }\n\n        // 2. Return ? OrdinaryCreateFromConstructor(NewTarget, \"%Iterator.prototype%\").\n        let prototype = crate::object::internal_methods::get_prototype_from_constructor(\n            new_target,\n            StandardConstructors::iterator,\n            context,\n        )?;\n\n        // Create an ordinary object (Iterator instances have no internal data slots).\n        Ok(JsObject::from_proto_and_data_with_shared_shape(\n            context.root_shape(),\n            prototype,\n            OrdinaryObject,\n        )\n        .upcast()\n        .into())\n    }\n}\n\nimpl IteratorConstructor {\n    // ==================== Static Methods ====================\n\n    /// `Iterator.from ( O )`\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-iterator.from\n    fn from(_this: &JsValue, args: &[JsValue], context: &mut Context) -> JsResult<JsValue> {\n        let o = args.get_or_undefined(0);\n\n        // 1. Let iteratorRecord be ? GetIteratorFlattenable(O, iterate-strings).\n        let iterator_record = super::get_iterator_flattenable(o, true, context)?;\n\n        // 2. Let hasInstance be ? OrdinaryHasInstance(%Iterator%, iteratorRecord.[[Iterator]]).\n        let iterator_constructor = context.intrinsics().constructors().iterator().constructor();\n        let has_instance = JsValue::ordinary_has_instance(\n            &iterator_constructor.clone().into(),\n            &iterator_record.iterator().clone().into(),\n            context,\n        )?;\n\n        // 3. If hasInstance is true, then\n        if has_instance {\n            // a. Return iteratorRecord.[[Iterator]].\n            return Ok(iterator_record.iterator().clone().into());\n        }\n\n        // 4. Let wrapper be OrdinaryObjectCreate(%WrapForValidIteratorPrototype%, « [[Iterated]] »).\n        // 5. Set wrapper.[[Iterated]] to iteratorRecord.\n        // 6. Return wrapper.\n        let wrapper = JsObject::from_proto_and_data_with_shared_shape(\n            context.root_shape(),\n            context\n                .intrinsics()\n                .objects()\n                .iterator_prototypes()\n                .wrap_for_valid_iterator(),\n            WrapForValidIterator {\n                iterated: iterator_record,\n            },\n        );\n\n        Ok(wrapper.into())\n    }\n\n    fn concat(_this: &JsValue, args: &[JsValue], context: &mut Context) -> JsResult<JsValue> {\n        // 1. Let iterables be a new empty List.\n        let mut iterables = Vec::with_capacity(args.len());\n\n        // 2. For each element item of items, do\n        for item in args {\n            // a. If item is not an Object, throw a TypeError exception.\n            if !item.is_object() {\n                return Err(JsNativeError::typ()\n                    .with_message(\"Iterator.concat requires iterable objects\")\n                    .into());\n            }\n\n            // b. Let method be ? GetMethod(item, %Symbol.iterator%).\n            // c. If method is undefined, throw a TypeError exception.\n            let method = item\n                .get_method(JsSymbol::iterator(), context)?\n                .ok_or_else(|| {\n                    JsNativeError::typ()\n                        .with_message(\"Iterator.concat requires objects with @@iterator\")\n                })?;\n\n            // d. Append the Record { [[OpenMethod]]: method, [[Iterable]]: item } to iterables.\n            iterables.push((method, item.clone()));\n        }\n\n        // 3. Let closure be a new Abstract Closure with no parameters that captures iterables\n        //    and performs the following steps when called:\n        //    (implemented via IteratorHelperOp::Concat in execute_next)\n        // 4-5. Let result be CreateIteratorFromClosure(closure, \"Iterator Helper\", ...)\n        //      with [[UnderlyingIterators]] set to a new empty List.\n        let helper = IteratorHelper::create(\n            vec![],\n            IteratorHelperOp::Concat {\n                iterables,\n                current_index: 0,\n                inner: None,\n            },\n            context,\n        );\n\n        // 6. Return result.\n        Ok(helper.into())\n    }\n\n    // ==================== Prototype Accessor Properties ====================\n\n    /// `get Iterator.prototype.constructor`\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-iterator.prototype.constructor\n    #[allow(clippy::unnecessary_wraps)]\n    fn get_constructor(\n        _this: &JsValue,\n        _args: &[JsValue],\n        context: &mut Context,\n    ) -> JsResult<JsValue> {\n        Ok(context\n            .intrinsics()\n            .constructors()\n            .iterator()\n            .constructor()\n            .into())\n    }\n\n    /// `set Iterator.prototype.constructor`\n    ///\n    /// `SetterThatIgnoresPrototypeProperties(this, %Iterator.prototype%, \"constructor\", v)`\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-iterator.prototype.constructor\n    fn set_constructor(\n        this: &JsValue,\n        args: &[JsValue],\n        context: &mut Context,\n    ) -> JsResult<JsValue> {\n        Self::setter_that_ignores_prototype_properties(\n            this,\n            &context.intrinsics().constructors().iterator().prototype(),\n            js_string!(\"constructor\"),\n            args.get_or_undefined(0),\n            context,\n        )\n    }\n\n    /// `get Iterator.prototype[@@toStringTag]`\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-iterator.prototype-%40%40tostringtag\n    #[allow(clippy::unnecessary_wraps)]\n    fn get_to_string_tag(\n        _this: &JsValue,\n        _args: &[JsValue],\n        _context: &mut Context,\n    ) -> JsResult<JsValue> {\n        Ok(js_string!(\"Iterator\").into())\n    }\n\n    /// `set Iterator.prototype[@@toStringTag]`\n    ///\n    /// `SetterThatIgnoresPrototypeProperties(this, %Iterator.prototype%, @@toStringTag, v)`\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-iterator.prototype-%40%40tostringtag\n    fn set_to_string_tag(\n        this: &JsValue,\n        args: &[JsValue],\n        context: &mut Context,\n    ) -> JsResult<JsValue> {\n        Self::setter_that_ignores_prototype_properties(\n            this,\n            &context.intrinsics().constructors().iterator().prototype(),\n            JsSymbol::to_string_tag(),\n            args.get_or_undefined(0),\n            context,\n        )\n    }\n\n    /// `SetterThatIgnoresPrototypeProperties ( this, home, p, v )`\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-SetterThatIgnoresPrototypeProperties\n    fn setter_that_ignores_prototype_properties<K: Into<crate::property::PropertyKey>>(\n        this: &JsValue,\n        home: &JsObject,\n        p: K,\n        v: &JsValue,\n        context: &mut Context,\n    ) -> JsResult<JsValue> {\n        let p = p.into();\n\n        // 1. If this is not an Object, then\n        let Some(this_obj) = this.as_object() else {\n            // a. Throw a TypeError exception.\n            return Err(JsNativeError::typ()\n                .with_message(\"Cannot set property on a non-object\")\n                .into());\n        };\n\n        // 2. If this is home, then\n        if JsObject::equals(&this_obj, home) {\n            // a. NOTE: Throwing here emulates the behavior of a Set handler ...\n            // b. Throw a TypeError exception.\n            return Err(JsNativeError::typ()\n                .with_message(\"Cannot set property directly on the prototype\")\n                .into());\n        }\n\n        // 3. Let desc be ? this.[[GetOwnProperty]](p).\n        let desc = this_obj.__get_own_property__(&p, &mut context.into())?;\n\n        // 4. If desc is undefined, then\n        if desc.is_none() {\n            // a. Perform ? CreateDataPropertyOrThrow(this, p, v).\n            this_obj.create_data_property_or_throw(p, v.clone(), context)?;\n        } else {\n            // 5. Else,\n            // a. Perform ? Set(this, p, v, true).\n            this_obj.set(p, v.clone(), true, context)?;\n        }\n\n        // 6. Return undefined.\n        Ok(JsValue::undefined())\n    }\n\n    // ==================== Prototype Methods — Lazy ====================\n\n    /// `Iterator.prototype.map ( mapper )`\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-iterator.prototype.map\n    fn map(this: &JsValue, args: &[JsValue], context: &mut Context) -> JsResult<JsValue> {\n        // 1. Let O be the this value.\n        // 2. If O is not an Object, throw a TypeError exception.\n        let o = this.as_object().ok_or_else(|| {\n            JsNativeError::typ().with_message(\"Iterator.prototype.map called on non-object\")\n        })?;\n\n        // 3. Let iterated be ? GetIteratorDirect(O).\n        let iterated = super::get_iterator_direct(&o, context)?;\n\n        // 4. If IsCallable(mapper) is false, then\n        //    a. Let error be ThrowCompletion(a newly created TypeError object).\n        //    b. Return ? IteratorClose(iterated, error).\n        let mapper = args.get_or_undefined(0);\n        let Some(mapper_obj) = mapper.as_callable() else {\n            return iterated.close(\n                Err(JsNativeError::typ()\n                    .with_message(\"Iterator.prototype.map: mapper is not callable\")\n                    .into()),\n                context,\n            );\n        };\n\n        // 5-17. Create IteratorHelper with map operation.\n        let helper = IteratorHelper::create(\n            vec![iterated],\n            IteratorHelperOp::Map {\n                mapper: mapper_obj.clone(),\n                counter: 0,\n            },\n            context,\n        );\n\n        Ok(helper.into())\n    }\n\n    /// `Iterator.prototype.filter ( predicate )`\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-iterator.prototype.filter\n    fn filter(this: &JsValue, args: &[JsValue], context: &mut Context) -> JsResult<JsValue> {\n        // 1. Let O be the this value.\n        // 2. If O is not an Object, throw a TypeError exception.\n        let o = this.as_object().ok_or_else(|| {\n            JsNativeError::typ().with_message(\"Iterator.prototype.filter called on non-object\")\n        })?;\n\n        // 3. Let iterated be ? GetIteratorDirect(O).\n        let iterated = super::get_iterator_direct(&o, context)?;\n\n        // 4. If IsCallable(predicate) is false, then\n        //    a. Let error be ThrowCompletion(a newly created TypeError object).\n        //    b. Return ? IteratorClose(iterated, error).\n        let predicate = args.get_or_undefined(0);\n        let Some(predicate_obj) = predicate.as_callable() else {\n            return iterated.close(\n                Err(JsNativeError::typ()\n                    .with_message(\"Iterator.prototype.filter: predicate is not callable\")\n                    .into()),\n                context,\n            );\n        };\n\n        // 5-13. Create IteratorHelper with filter operation.\n        let helper = IteratorHelper::create(\n            vec![iterated],\n            IteratorHelperOp::Filter {\n                predicate: predicate_obj.clone(),\n                counter: 0,\n            },\n            context,\n        );\n\n        Ok(helper.into())\n    }\n\n    /// `Iterator.prototype.take ( limit )`\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-iterator.prototype.take\n    fn take(this: &JsValue, args: &[JsValue], context: &mut Context) -> JsResult<JsValue> {\n        // 1. Let O be the this value.\n        // 2. If O is not an Object, throw a TypeError exception.\n        let o = this.as_object().ok_or_else(|| {\n            JsNativeError::typ().with_message(\"Iterator.prototype.take called on non-object\")\n        })?;\n\n        // 3. Let iterated be ? GetIteratorDirect(O).\n        let iterated = super::get_iterator_direct(&o, context)?;\n\n        // 4. Let numLimit be ? ToNumber(limit).\n        let limit = args.get_or_undefined(0);\n        let num_limit = if_abrupt_close_iterator!(limit.to_number(context), iterated, context);\n\n        // 5. If numLimit is NaN, throw a RangeError exception.\n        if num_limit.is_nan() {\n            return iterated.close(\n                Err(JsNativeError::range()\n                    .with_message(\"Iterator.prototype.take: limit is NaN\")\n                    .into()),\n                context,\n            );\n        }\n\n        // 6. Let integerLimit be ! ToIntegerOrInfinity(numLimit).\n        let integer_limit =\n            if_abrupt_close_iterator!(limit.to_integer_or_infinity(context), iterated, context);\n\n        // 7. If integerLimit < 0, throw a RangeError exception.\n        let integer_limit = match integer_limit {\n            crate::value::IntegerOrInfinity::Integer(n) if n < 0 => {\n                return iterated.close(\n                    Err(JsNativeError::range()\n                        .with_message(\"Iterator.prototype.take: limit is negative\")\n                        .into()),\n                    context,\n                );\n            }\n            crate::value::IntegerOrInfinity::Integer(n) => n as u64,\n            crate::value::IntegerOrInfinity::PositiveInfinity => u64::MAX,\n            crate::value::IntegerOrInfinity::NegativeInfinity => {\n                return iterated.close(\n                    Err(JsNativeError::range()\n                        .with_message(\"Iterator.prototype.take: limit is negative infinity\")\n                        .into()),\n                    context,\n                );\n            }\n        };\n\n        // 8-10. Return CreateIteratorHelper with a take closure.\n        let helper = IteratorHelper::create(\n            vec![iterated],\n            IteratorHelperOp::Take {\n                remaining: integer_limit,\n            },\n            context,\n        );\n\n        Ok(helper.into())\n    }\n\n    /// `Iterator.prototype.drop ( limit )`\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-iterator.prototype.drop\n    fn drop(this: &JsValue, args: &[JsValue], context: &mut Context) -> JsResult<JsValue> {\n        // 1. Let O be the this value.\n        // 2. If O is not an Object, throw a TypeError exception.\n        let o = this.as_object().ok_or_else(|| {\n            JsNativeError::typ().with_message(\"Iterator.prototype.drop called on non-object\")\n        })?;\n\n        // 3. Let iterated be ? GetIteratorDirect(O).\n        let iterated = super::get_iterator_direct(&o, context)?;\n\n        // 4. Let numLimit be ? ToNumber(limit).\n        let limit = args.get_or_undefined(0);\n        let num_limit = if_abrupt_close_iterator!(limit.to_number(context), iterated, context);\n\n        // 5. If numLimit is NaN, throw a RangeError exception.\n        if num_limit.is_nan() {\n            return iterated.close(\n                Err(JsNativeError::range()\n                    .with_message(\"Iterator.prototype.drop: limit is NaN\")\n                    .into()),\n                context,\n            );\n        }\n\n        // 6. Let integerLimit be ! ToIntegerOrInfinity(numLimit).\n        let integer_limit =\n            if_abrupt_close_iterator!(limit.to_integer_or_infinity(context), iterated, context);\n\n        // 7. If integerLimit < 0, throw a RangeError exception.\n        let integer_limit = match integer_limit {\n            crate::value::IntegerOrInfinity::Integer(n) if n < 0 => {\n                return iterated.close(\n                    Err(JsNativeError::range()\n                        .with_message(\"Iterator.prototype.drop: limit is negative\")\n                        .into()),\n                    context,\n                );\n            }\n            crate::value::IntegerOrInfinity::Integer(n) => n as u64,\n            crate::value::IntegerOrInfinity::PositiveInfinity => u64::MAX,\n            crate::value::IntegerOrInfinity::NegativeInfinity => {\n                return iterated.close(\n                    Err(JsNativeError::range()\n                        .with_message(\"Iterator.prototype.drop: limit is negative infinity\")\n                        .into()),\n                    context,\n                );\n            }\n        };\n\n        // 8-10. Return CreateIteratorHelper with a drop closure.\n        let helper = IteratorHelper::create(\n            vec![iterated],\n            IteratorHelperOp::Drop {\n                remaining: integer_limit,\n                done_dropping: false,\n            },\n            context,\n        );\n\n        Ok(helper.into())\n    }\n\n    /// `Iterator.prototype.flatMap ( mapper )`\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-iterator.prototype.flatmap\n    fn flat_map(this: &JsValue, args: &[JsValue], context: &mut Context) -> JsResult<JsValue> {\n        // 1. Let O be the this value.\n        // 2. If O is not an Object, throw a TypeError exception.\n        let o = this.as_object().ok_or_else(|| {\n            JsNativeError::typ().with_message(\"Iterator.prototype.flatMap called on non-object\")\n        })?;\n\n        // 3. Let iterated be ? GetIteratorDirect(O).\n        let iterated = super::get_iterator_direct(&o, context)?;\n\n        // 4. If IsCallable(mapper) is false, then\n        //    a. Let error be ThrowCompletion(a newly created TypeError object).\n        //    b. Return ? IteratorClose(iterated, error).\n        let mapper = args.get_or_undefined(0);\n        let Some(mapper_obj) = mapper.as_callable() else {\n            return iterated.close(\n                Err(JsNativeError::typ()\n                    .with_message(\"Iterator.prototype.flatMap: mapper is not callable\")\n                    .into()),\n                context,\n            );\n        };\n\n        // 5+. Create IteratorHelper with flatMap operation.\n        let helper = IteratorHelper::create(\n            vec![iterated],\n            IteratorHelperOp::FlatMap {\n                mapper: mapper_obj.clone(),\n                counter: 0,\n                inner_iterator: None,\n            },\n            context,\n        );\n\n        Ok(helper.into())\n    }\n\n    // ==================== Prototype Methods — Eager (Consuming) ====================\n\n    /// `Iterator.prototype.reduce ( reducer [ , initialValue ] )`\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-iterator.prototype.reduce\n    fn reduce(this: &JsValue, args: &[JsValue], context: &mut Context) -> JsResult<JsValue> {\n        // 1. Let O be the this value.\n        // 2. If O is not an Object, throw a TypeError exception.\n        let o = this.as_object().ok_or_else(|| {\n            JsNativeError::typ().with_message(\"Iterator.prototype.reduce called on non-object\")\n        })?;\n\n        // 3. Let iterated be ? GetIteratorDirect(O).\n        let mut iterated = super::get_iterator_direct(&o, context)?;\n\n        // 4. If IsCallable(reducer) is false, then\n        //    a. Let error be ThrowCompletion(a newly created TypeError object).\n        //    b. Return ? IteratorClose(iterated, error).\n        let Some(reducer) = args.get_or_undefined(0).as_callable() else {\n            return iterated.close(\n                Err(JsNativeError::typ()\n                    .with_message(\"Iterator.prototype.reduce: reducer is not callable\")\n                    .into()),\n                context,\n            );\n        };\n\n        let mut accumulator;\n        let mut counter;\n\n        // If initialValue is not present\n        if args.len() < 2 {\n            // Let accumulator be ? IteratorStepValue(iterated).\n            let first = iterated.step_value(context)?;\n            match first {\n                None => {\n                    return Err(JsNativeError::typ()\n                        .with_message(\n                            \"Iterator.prototype.reduce: reduce of empty iterator with no initial value\",\n                        )\n                        .into());\n                }\n                Some(val) => {\n                    accumulator = val;\n                    counter = 1u64;\n                }\n            }\n        } else {\n            accumulator = args.get_or_undefined(1).clone();\n            counter = 0;\n        }\n\n        // Repeat\n        loop {\n            let value = iterated.step_value(context)?;\n            match value {\n                None => return Ok(accumulator),\n                Some(value) => {\n                    let result = reducer.call(\n                        &JsValue::undefined(),\n                        &[accumulator, value, JsValue::new(counter)],\n                        context,\n                    );\n\n                    match result {\n                        Ok(val) => {\n                            accumulator = val;\n                        }\n                        Err(err) => {\n                            return iterated.close(Err(err), context);\n                        }\n                    }\n\n                    counter += 1;\n                }\n            }\n        }\n    }\n\n    /// `Iterator.prototype.toArray ( )`\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-iterator.prototype.toarray\n    fn to_array(this: &JsValue, _args: &[JsValue], context: &mut Context) -> JsResult<JsValue> {\n        let o = this.as_object().ok_or_else(|| {\n            JsNativeError::typ().with_message(\"Iterator.prototype.toArray called on non-object\")\n        })?;\n\n        let iterated = super::get_iterator_direct(&o, context)?;\n\n        // Let items be a new empty List.\n        // Repeat ... append to items.\n        let items = iterated.into_list(context)?;\n\n        // Return CreateArrayFromList(items).\n        Ok(crate::builtins::array::Array::create_array_from_list(items, context).into())\n    }\n\n    /// `Iterator.prototype.forEach ( fn )`\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-iterator.prototype.foreach\n    fn for_each(this: &JsValue, args: &[JsValue], context: &mut Context) -> JsResult<JsValue> {\n        // 1. Let O be the this value.\n        // 2. If O is not an Object, throw a TypeError exception.\n        let o = this.as_object().ok_or_else(|| {\n            JsNativeError::typ().with_message(\"Iterator.prototype.forEach called on non-object\")\n        })?;\n\n        // 3. Let iterated be ? GetIteratorDirect(O).\n        let mut iterated = super::get_iterator_direct(&o, context)?;\n\n        // 4. If IsCallable(fn) is false, then\n        //    a. Let error be ThrowCompletion(a newly created TypeError object).\n        //    b. Return ? IteratorClose(iterated, error).\n        let Some(func) = args.get_or_undefined(0).as_callable() else {\n            return iterated.close(\n                Err(JsNativeError::typ()\n                    .with_message(\"Iterator.prototype.forEach: argument is not callable\")\n                    .into()),\n                context,\n            );\n        };\n        let mut counter = 0u64;\n\n        loop {\n            let value = iterated.step_value(context)?;\n            match value {\n                None => return Ok(JsValue::undefined()),\n                Some(value) => {\n                    let result = func.call(\n                        &JsValue::undefined(),\n                        &[value, JsValue::new(counter)],\n                        context,\n                    );\n\n                    if let Err(err) = result {\n                        return iterated.close(Err(err), context);\n                    }\n\n                    counter += 1;\n                }\n            }\n        }\n    }\n\n    /// `Iterator.prototype.some ( predicate )`\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-iterator.prototype.some\n    fn some(this: &JsValue, args: &[JsValue], context: &mut Context) -> JsResult<JsValue> {\n        // 1. Let O be the this value.\n        // 2. If O is not an Object, throw a TypeError exception.\n        let o = this.as_object().ok_or_else(|| {\n            JsNativeError::typ().with_message(\"Iterator.prototype.some called on non-object\")\n        })?;\n\n        // 3. Let iterated be ? GetIteratorDirect(O).\n        let mut iterated = super::get_iterator_direct(&o, context)?;\n\n        // 4. If IsCallable(predicate) is false, then\n        //    a. Let error be ThrowCompletion(a newly created TypeError object).\n        //    b. Return ? IteratorClose(iterated, error).\n        let Some(predicate) = args.get_or_undefined(0).as_callable() else {\n            return iterated.close(\n                Err(JsNativeError::typ()\n                    .with_message(\"Iterator.prototype.some: predicate is not callable\")\n                    .into()),\n                context,\n            );\n        };\n        let mut counter = 0u64;\n\n        loop {\n            let value = iterated.step_value(context)?;\n            match value {\n                None => return Ok(JsValue::new(false)),\n                Some(value) => {\n                    let result = predicate.call(\n                        &JsValue::undefined(),\n                        &[value, JsValue::new(counter)],\n                        context,\n                    );\n\n                    match result {\n                        Ok(val) => {\n                            if val.to_boolean() {\n                                return iterated.close(Ok(JsValue::new(true)), context);\n                            }\n                        }\n                        Err(err) => {\n                            return iterated.close(Err(err), context);\n                        }\n                    }\n\n                    counter += 1;\n                }\n            }\n        }\n    }\n\n    /// `Iterator.prototype.every ( predicate )`\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-iterator.prototype.every\n    fn every(this: &JsValue, args: &[JsValue], context: &mut Context) -> JsResult<JsValue> {\n        // 1. Let O be the this value.\n        // 2. If O is not an Object, throw a TypeError exception.\n        let o = this.as_object().ok_or_else(|| {\n            JsNativeError::typ().with_message(\"Iterator.prototype.every called on non-object\")\n        })?;\n\n        // 3. Let iterated be ? GetIteratorDirect(O).\n        let mut iterated = super::get_iterator_direct(&o, context)?;\n\n        // 4. If IsCallable(predicate) is false, then\n        //    a. Let error be ThrowCompletion(a newly created TypeError object).\n        //    b. Return ? IteratorClose(iterated, error).\n        let Some(predicate) = args.get_or_undefined(0).as_callable() else {\n            return iterated.close(\n                Err(JsNativeError::typ()\n                    .with_message(\"Iterator.prototype.every: predicate is not callable\")\n                    .into()),\n                context,\n            );\n        };\n        let mut counter = 0u64;\n\n        loop {\n            let value = iterated.step_value(context)?;\n            match value {\n                None => return Ok(JsValue::new(true)),\n                Some(value) => {\n                    let result = predicate.call(\n                        &JsValue::undefined(),\n                        &[value, JsValue::new(counter)],\n                        context,\n                    );\n\n                    match result {\n                        Ok(val) => {\n                            if !val.to_boolean() {\n                                return iterated.close(Ok(JsValue::new(false)), context);\n                            }\n                        }\n                        Err(err) => {\n                            return iterated.close(Err(err), context);\n                        }\n                    }\n\n                    counter += 1;\n                }\n            }\n        }\n    }\n\n    /// `Iterator.prototype.find ( predicate )`\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-iterator.prototype.find\n    fn find(this: &JsValue, args: &[JsValue], context: &mut Context) -> JsResult<JsValue> {\n        // 1. Let O be the this value.\n        // 2. If O is not an Object, throw a TypeError exception.\n        let o = this.as_object().ok_or_else(|| {\n            JsNativeError::typ().with_message(\"Iterator.prototype.find called on non-object\")\n        })?;\n\n        // 3. Let iterated be ? GetIteratorDirect(O).\n        let mut iterated = super::get_iterator_direct(&o, context)?;\n\n        // 4. If IsCallable(predicate) is false, then\n        //    a. Let error be ThrowCompletion(a newly created TypeError object).\n        //    b. Return ? IteratorClose(iterated, error).\n        let Some(predicate) = args.get_or_undefined(0).as_callable() else {\n            return iterated.close(\n                Err(JsNativeError::typ()\n                    .with_message(\"Iterator.prototype.find: predicate is not callable\")\n                    .into()),\n                context,\n            );\n        };\n        let mut counter = 0u64;\n\n        loop {\n            let value = iterated.step_value(context)?;\n            match value {\n                None => return Ok(JsValue::undefined()),\n                Some(value) => {\n                    let result = predicate.call(\n                        &JsValue::undefined(),\n                        &[value.clone(), JsValue::new(counter)],\n                        context,\n                    );\n\n                    match result {\n                        Ok(val) => {\n                            if val.to_boolean() {\n                                return iterated.close(Ok(value), context);\n                            }\n                        }\n                        Err(err) => {\n                            return iterated.close(Err(err), context);\n                        }\n                    }\n\n                    counter += 1;\n                }\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "core/engine/src/builtins/iterable/iterator_helper.rs",
    "content": "//! Boa's implementation of the `Iterator Helper` objects.\n//!\n//! An Iterator Helper object is an ordinary object that represents a lazy transformation\n//! of some specific source iterator object. There is not a named constructor for\n//! Iterator Helper objects. Instead, Iterator Helper objects are created by calling\n//! certain methods of Iterator instance objects.\n//!\n//! More information:\n//!  - [ECMAScript reference][spec]\n//!\n//! [spec]: https://tc39.es/ecma262/#sec-iterator-helper-objects\n\nuse crate::{\n    Context, JsData, JsResult, JsValue,\n    builtins::{BuiltInBuilder, IntrinsicObject, iterable::create_iter_result_object},\n    context::intrinsics::Intrinsics,\n    error::JsNativeError,\n    js_string,\n    object::JsObject,\n    property::Attribute,\n    realm::Realm,\n    symbol::JsSymbol,\n};\nuse boa_gc::{Finalize, Trace};\n\nuse super::IteratorRecord;\n\n/// The type of lazy iterator helper operation.\n#[derive(Debug, Trace, Finalize)]\npub(crate) enum IteratorHelperOp {\n    /// `Iterator.prototype.map(mapper)` — yields `mapper(value, counter)`.\n    Map { mapper: JsObject, counter: u64 },\n    /// `Iterator.prototype.filter(predicate)` — yields values where `predicate(value, counter)` is truthy.\n    Filter { predicate: JsObject, counter: u64 },\n    /// `Iterator.prototype.take(limit)` — yields at most `limit` values.\n    Take { remaining: u64 },\n    /// `Iterator.prototype.drop(limit)` — skips `limit` values, then yields the rest.\n    Drop { remaining: u64, done_dropping: bool },\n    /// `Iterator.prototype.flatMap(mapper)` — yields values from the iterators returned by `mapper`.\n    FlatMap {\n        mapper: JsObject,\n        counter: u64,\n        inner_iterator: Option<IteratorRecord>,\n    },\n    Concat {\n        iterables: Vec<(JsObject, JsValue)>,\n        current_index: usize,\n        inner: Option<IteratorRecord>,\n    },\n}\n\n/// Represents the state of an `IteratorHelper`'s internal generator.\n#[derive(Debug, Trace, Finalize)]\npub(crate) enum IteratorHelperState {\n    /// The helper has been created but `.next()` has not yet been called.\n    SuspendedStart,\n    /// The helper has yielded a value and is waiting for the next `.next()` call.\n    SuspendedYield,\n    /// The helper's `.next()` is currently executing.\n    Executing,\n    /// The helper has finished (either exhausted the underlying iterator or was closed).\n    Completed,\n}\n\n/// The internal representation of an `Iterator Helper` object.\n///\n/// More information:\n///  - [ECMAScript reference][spec]\n///\n/// [spec]: https://tc39.es/ecma262/#sec-iterator-helper-objects\n#[derive(Debug, Finalize, Trace, JsData)]\npub(crate) struct IteratorHelper {\n    pub(crate) underlying_iterators: Vec<IteratorRecord>,\n\n    /// `[[GeneratorState]]` — tracks the state of this helper's internal generator.\n    pub(crate) state: IteratorHelperState,\n\n    /// The specific lazy operation this helper performs.\n    pub(crate) op: IteratorHelperOp,\n}\n\nimpl IntrinsicObject for IteratorHelper {\n    fn init(realm: &Realm) {\n        BuiltInBuilder::with_intrinsic::<Self>(realm)\n            .prototype(realm.intrinsics().constructors().iterator().prototype())\n            .static_method(Self::next, js_string!(\"next\"), 0)\n            .static_method(Self::r#return, js_string!(\"return\"), 0)\n            .static_property(\n                JsSymbol::to_string_tag(),\n                js_string!(\"Iterator Helper\"),\n                Attribute::NON_ENUMERABLE | Attribute::CONFIGURABLE,\n            )\n            .build();\n    }\n\n    fn get(intrinsics: &Intrinsics) -> JsObject {\n        intrinsics.objects().iterator_prototypes().iterator_helper()\n    }\n}\n\nimpl IteratorHelper {\n    /// `%IteratorHelperPrototype%.next ( )`\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-%25iteratorhelperprototype%25.next\n    pub(crate) fn next(\n        this: &JsValue,\n        _args: &[JsValue],\n        context: &mut Context,\n    ) -> JsResult<JsValue> {\n        // 1. Return ? GeneratorResume(this value, undefined, \"Iterator Helper\").\n        let object = this.as_object().ok_or_else(|| {\n            JsNativeError::typ().with_message(\"Iterator Helper method called on non-object\")\n        })?;\n\n        let mut helper = object.downcast_mut::<Self>().ok_or_else(|| {\n            JsNativeError::typ()\n                .with_message(\"Iterator Helper method called on incompatible object\")\n        })?;\n\n        match helper.state {\n            IteratorHelperState::Executing => {\n                return Err(JsNativeError::typ()\n                    .with_message(\"Iterator Helper is already executing\")\n                    .into());\n            }\n            IteratorHelperState::Completed => {\n                return Ok(create_iter_result_object(\n                    JsValue::undefined(),\n                    true,\n                    context,\n                ));\n            }\n            IteratorHelperState::SuspendedStart | IteratorHelperState::SuspendedYield => {}\n        }\n\n        // Set state to Executing (re-entrancy guard).\n        helper.state = IteratorHelperState::Executing;\n        drop(helper);\n\n        // Execute the closure based on the operation type.\n        // Returns Ok((result_object, done)) so we can update state without re-reading\n        // the \"done\" field via a property getter (which could trigger arbitrary JS).\n        let result = Self::execute_next(&object, context);\n\n        // Post-execution: update state based on result.\n        let mut helper = object\n            .downcast_mut::<Self>()\n            .expect(\"object type already verified\");\n\n        match &result {\n            Ok((_, done)) => {\n                if *done {\n                    helper.state = IteratorHelperState::Completed;\n                } else {\n                    helper.state = IteratorHelperState::SuspendedYield;\n                }\n            }\n            Err(_) => {\n                helper.state = IteratorHelperState::Completed;\n            }\n        }\n\n        result.map(|(val, _)| val)\n    }\n\n    /// Execute one step of the iterator helper closure.\n    /// Returns `Ok((result_object, done))` where `done` is `true` when the iteration\n    /// is complete. Using a dedicated flag avoids re-reading the `done` property via\n    /// a property getter, which would trigger arbitrary user code.\n    fn execute_next(object: &JsObject, context: &mut Context) -> JsResult<(JsValue, bool)> {\n        // Map arm: extract + clone what we need from op, then separately borrow underlying_iterators\n        {\n            let mut helper = object\n                .downcast_mut::<Self>()\n                .expect(\"object type already verified\");\n            if let IteratorHelperOp::Map { mapper, counter } = &mut helper.op {\n                let mapper = mapper.clone();\n                let count = *counter;\n                *counter += 1;\n                drop(helper);\n\n                let mut helper = object\n                    .downcast_mut::<Self>()\n                    .expect(\"object type already verified\");\n                let iterated = &mut helper.underlying_iterators[0];\n                let value = iterated.step_value(context)?;\n                match value {\n                    None => {\n                        return Ok((\n                            create_iter_result_object(JsValue::undefined(), true, context),\n                            true,\n                        ));\n                    }\n                    Some(value) => {\n                        let mapper_result = mapper.call(\n                            &JsValue::undefined(),\n                            &[value, JsValue::new(count)],\n                            context,\n                        );\n                        return match mapper_result {\n                            Ok(result_value) => Ok((\n                                create_iter_result_object(result_value, false, context),\n                                false,\n                            )),\n                            Err(err) => {\n                                drop(iterated.close(Err(err.clone()), context));\n                                Err(err)\n                            }\n                        };\n                    }\n                }\n            }\n        }\n\n        // Filter arm\n        {\n            let helper = object\n                .downcast_mut::<Self>()\n                .expect(\"object type already verified\");\n            if let IteratorHelperOp::Filter { predicate, .. } = &helper.op {\n                let predicate = predicate.clone();\n                // We'll loop; snapshot/increment counter each iteration.\n                // We can't hold a borrow across context calls, so snapshot counter now,\n                // then re-borrow each iteration.\n                drop(helper);\n\n                loop {\n                    let count = {\n                        let mut h = object\n                            .downcast_mut::<Self>()\n                            .expect(\"object type already verified\");\n                        let IteratorHelperOp::Filter { counter, .. } = &mut h.op else {\n                            unreachable!()\n                        };\n                        let c = *counter;\n                        *counter += 1;\n                        c\n                    };\n\n                    let mut helper = object\n                        .downcast_mut::<Self>()\n                        .expect(\"object type already verified\");\n                    let iterated = &mut helper.underlying_iterators[0];\n                    let value = iterated.step_value(context)?;\n                    match value {\n                        None => {\n                            return Ok((\n                                create_iter_result_object(JsValue::undefined(), true, context),\n                                true,\n                            ));\n                        }\n                        Some(value) => {\n                            let selected_result = predicate.call(\n                                &JsValue::undefined(),\n                                &[value.clone(), JsValue::new(count)],\n                                context,\n                            );\n                            match selected_result {\n                                Ok(selected) => {\n                                    if selected.to_boolean() {\n                                        return Ok((\n                                            create_iter_result_object(value, false, context),\n                                            false,\n                                        ));\n                                    }\n                                }\n                                Err(err) => {\n                                    drop(iterated.close(Err(err.clone()), context));\n                                    return Err(err);\n                                }\n                            }\n                        }\n                    }\n                }\n            }\n        }\n\n        // Take arm\n        {\n            let mut helper = object\n                .downcast_mut::<Self>()\n                .expect(\"object type already verified\");\n            if let IteratorHelperOp::Take { remaining } = &mut helper.op {\n                if *remaining == 0 {\n                    drop(helper);\n                    let helper = object\n                        .downcast_mut::<Self>()\n                        .expect(\"object type already verified\");\n                    let close_result =\n                        helper.underlying_iterators[0].close(Ok(JsValue::undefined()), context);\n                    drop(helper);\n                    close_result?;\n                    return Ok((\n                        create_iter_result_object(JsValue::undefined(), true, context),\n                        true,\n                    ));\n                }\n                *remaining -= 1;\n                drop(helper);\n\n                let mut helper = object\n                    .downcast_mut::<Self>()\n                    .expect(\"object type already verified\");\n                let value = helper.underlying_iterators[0].step_value(context)?;\n                return match value {\n                    None => Ok((\n                        create_iter_result_object(JsValue::undefined(), true, context),\n                        true,\n                    )),\n                    Some(v) => Ok((create_iter_result_object(v, false, context), false)),\n                };\n            }\n        }\n\n        // Drop arm\n        {\n            let mut helper = object\n                .downcast_mut::<Self>()\n                .expect(\"object type already verified\");\n            if let IteratorHelperOp::Drop {\n                remaining,\n                done_dropping,\n            } = &mut helper.op\n            {\n                let should_skip = !*done_dropping;\n                let skip_count = *remaining;\n                if should_skip {\n                    *done_dropping = true;\n                    *remaining = 0;\n                }\n                drop(helper);\n\n                if should_skip {\n                    for _ in 0..skip_count {\n                        let mut helper = object\n                            .downcast_mut::<Self>()\n                            .expect(\"object type already verified\");\n                        let value = helper.underlying_iterators[0].step_value(context)?;\n                        if value.is_none() {\n                            return Ok((\n                                create_iter_result_object(JsValue::undefined(), true, context),\n                                true,\n                            ));\n                        }\n                    }\n                }\n\n                let mut helper = object\n                    .downcast_mut::<Self>()\n                    .expect(\"object type already verified\");\n                let value = helper.underlying_iterators[0].step_value(context)?;\n                return match value {\n                    None => Ok((\n                        create_iter_result_object(JsValue::undefined(), true, context),\n                        true,\n                    )),\n                    Some(v) => Ok((create_iter_result_object(v, false, context), false)),\n                };\n            }\n        }\n\n        // Concat arm — Iterator.concat closure steps:\n        // https://tc39.es/ecma262/#sec-iterator.concat\n        {\n            let is_concat = {\n                let helper = object\n                    .downcast_mut::<Self>()\n                    .expect(\"object type already verified\");\n                matches!(helper.op, IteratorHelperOp::Concat { .. })\n            };\n\n            if is_concat {\n                loop {\n                    let has_inner = {\n                        let helper = object\n                            .downcast_mut::<Self>()\n                            .expect(\"object type already verified\");\n                        let IteratorHelperOp::Concat { inner, .. } = &helper.op else {\n                            unreachable!()\n                        };\n                        inner.is_some()\n                    };\n\n                    if has_inner {\n                        // Clone the inner iterator record and drop the borrow before\n                        // calling step_value, since user code may re-enter .next().\n                        let mut inner_iter = {\n                            let helper = object\n                                .downcast_mut::<Self>()\n                                .expect(\"object type already verified\");\n                            let IteratorHelperOp::Concat { inner, .. } = &helper.op else {\n                                unreachable!()\n                            };\n                            inner.clone().expect(\"checked above\")\n                        };\n                        // 3.b.v. Let innerValue be ? IteratorStepValue(iteratorRecord).\n                        let inner_value = inner_iter.step_value(context)?;\n                        if let Some(val) = inner_value {\n                            // 3.b.v.2. Let completion be Completion(Yield(innerValue)).\n                            return Ok((create_iter_result_object(val, false, context), false));\n                        }\n                        // 3.b.v.1. If innerValue is done, then set innerAlive to false.\n                        let mut helper = object\n                            .downcast_mut::<Self>()\n                            .expect(\"object type already verified\");\n                        let IteratorHelperOp::Concat {\n                            inner,\n                            current_index,\n                            ..\n                        } = &mut helper.op\n                        else {\n                            unreachable!()\n                        };\n                        *inner = None;\n                        *current_index += 1;\n                        continue;\n                    }\n\n                    let iterable_data = {\n                        let helper = object\n                            .downcast_mut::<Self>()\n                            .expect(\"object type already verified\");\n                        let IteratorHelperOp::Concat {\n                            iterables,\n                            current_index,\n                            ..\n                        } = &helper.op\n                        else {\n                            unreachable!()\n                        };\n                        if *current_index >= iterables.len() {\n                            None\n                        } else {\n                            Some((\n                                iterables[*current_index].0.clone(),\n                                iterables[*current_index].1.clone(),\n                            ))\n                        }\n                    };\n\n                    // 3.a. If current_index >= iterables.len(), return undefined, done.\n                    let Some((open_method, iterable)) = iterable_data else {\n                        return Ok((\n                            create_iter_result_object(JsValue::undefined(), true, context),\n                            true,\n                        ));\n                    };\n\n                    // 3.b.i. Let iter be ? Call(iterable.[[OpenMethod]], iterable.[[Iterable]]).\n                    let iter = open_method.call(&iterable, &[], context)?;\n                    // 3.b.ii. If iter is not an Object, throw a TypeError exception.\n                    let iter_obj = iter.as_object().ok_or_else(|| {\n                        JsNativeError::typ()\n                            .with_message(\"Iterator.concat: iterator is not an object\")\n                    })?;\n                    // 3.b.iii. Let iteratorRecord be ? GetIteratorDirect(iter).\n                    let iterator_record = super::get_iterator_direct(&iter_obj, context)?;\n\n                    // Append iteratorRecord to O.[[UnderlyingIterators]].\n                    let mut helper = object\n                        .downcast_mut::<Self>()\n                        .expect(\"object type already verified\");\n                    helper.underlying_iterators.push(iterator_record.clone());\n                    let IteratorHelperOp::Concat { inner, .. } = &mut helper.op else {\n                        unreachable!()\n                    };\n                    *inner = Some(iterator_record);\n                }\n            }\n        }\n\n        // FlatMap arm\n        {\n            loop {\n                // Try to get value from active inner iterator first\n                let has_inner = {\n                    let helper = object\n                        .downcast_mut::<Self>()\n                        .expect(\"object type already verified\");\n                    if let IteratorHelperOp::FlatMap { inner_iterator, .. } = &helper.op {\n                        inner_iterator.is_some()\n                    } else {\n                        false\n                    }\n                };\n\n                if has_inner {\n                    let mut helper = object\n                        .downcast_mut::<Self>()\n                        .expect(\"object type already verified\");\n                    let IteratorHelperOp::FlatMap { inner_iterator, .. } = &mut helper.op else {\n                        unreachable!()\n                    };\n                    let inner = inner_iterator.as_mut().expect(\"checked above\");\n                    let inner_value = inner.step_value(context)?;\n                    if let Some(val) = inner_value {\n                        return Ok((create_iter_result_object(val, false, context), false));\n                    }\n                    // Inner exhausted — clear it.\n                    drop(helper);\n                    let mut helper = object\n                        .downcast_mut::<Self>()\n                        .expect(\"object type already verified\");\n                    let IteratorHelperOp::FlatMap { inner_iterator, .. } = &mut helper.op else {\n                        unreachable!()\n                    };\n                    *inner_iterator = None;\n                }\n\n                // Get data from op then drop borrow, then access underlying_iterators\n                let (mapper, count) = {\n                    let mut helper = object\n                        .downcast_mut::<Self>()\n                        .expect(\"object type already verified\");\n                    let IteratorHelperOp::FlatMap {\n                        mapper, counter, ..\n                    } = &mut helper.op\n                    else {\n                        unreachable!()\n                    };\n                    let c = *counter;\n                    *counter += 1;\n                    (mapper.clone(), c)\n                };\n\n                let mut helper = object\n                    .downcast_mut::<Self>()\n                    .expect(\"object type already verified\");\n                let iterated = &mut helper.underlying_iterators[0];\n                let value = iterated.step_value(context)?;\n\n                match value {\n                    None => {\n                        return Ok((\n                            create_iter_result_object(JsValue::undefined(), true, context),\n                            true,\n                        ));\n                    }\n                    Some(value) => {\n                        let mapper_result = mapper.call(\n                            &JsValue::undefined(),\n                            &[value, JsValue::new(count)],\n                            context,\n                        );\n\n                        let inner_value = match mapper_result {\n                            Ok(m) => m,\n                            Err(err) => {\n                                drop(iterated.close(Err(err.clone()), context));\n                                return Err(err);\n                            }\n                        };\n                        drop(helper);\n\n                        // Get inner iterator\n                        let inner_record =\n                            match super::get_iterator_flattenable(&inner_value, false, context) {\n                                Ok(record) => record,\n                                Err(err) => {\n                                    let helper = object\n                                        .downcast_mut::<Self>()\n                                        .expect(\"object type already verified\");\n                                    drop(\n                                        helper.underlying_iterators[0]\n                                            .close(Err(err.clone()), context),\n                                    );\n                                    return Err(err);\n                                }\n                            };\n\n                        let mut helper = object\n                            .downcast_mut::<Self>()\n                            .expect(\"object type already verified\");\n                        let IteratorHelperOp::FlatMap { inner_iterator, .. } = &mut helper.op\n                        else {\n                            unreachable!()\n                        };\n                        *inner_iterator = Some(inner_record);\n                        // Loop to get a value from the new inner iterator.\n                    }\n                }\n            }\n        }\n    }\n\n    /// `%IteratorHelperPrototype%.return ( )`\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-%25iteratorhelperprototype%25.return\n    pub(crate) fn r#return(\n        this: &JsValue,\n        _args: &[JsValue],\n        context: &mut Context,\n    ) -> JsResult<JsValue> {\n        let object = this.as_object().ok_or_else(|| {\n            JsNativeError::typ().with_message(\"Iterator Helper method called on non-object\")\n        })?;\n\n        let mut helper = object.downcast_mut::<Self>().ok_or_else(|| {\n            JsNativeError::typ()\n                .with_message(\"Iterator Helper method called on incompatible object\")\n        })?;\n\n        match helper.state {\n            // 4. If O.[[GeneratorState]] is suspended-start, then\n            // 5. If O.[[GeneratorState]] is suspended-yield, then\n            IteratorHelperState::SuspendedStart\n            | IteratorHelperState::SuspendedYield\n            | IteratorHelperState::Executing => {\n                // a. Set O.[[GeneratorState]] to completed.\n                helper.state = IteratorHelperState::Completed;\n                // b. Let iters be O.[[UnderlyingIterators]].\n                let iterators: Vec<IteratorRecord> =\n                    helper.underlying_iterators.drain(..).collect();\n                drop(helper);\n\n                // c. Return ? IteratorCloseAll(iters, NormalCompletion(undefined)).\n                //\n                // IteratorCloseAll (§7.4.12):\n                // 1. For each element iter of iters, in reverse List order, do\n                //    a. Set completion to Completion(IteratorClose(iter, completion)).\n                // 2. Return ? completion.\n                let mut completion: JsResult<JsValue> = Ok(JsValue::undefined());\n                for iter in iterators.iter().rev() {\n                    completion = iter.close(completion.clone(), context);\n                }\n                completion?;\n                Ok(create_iter_result_object(\n                    JsValue::undefined(),\n                    true,\n                    context,\n                ))\n            }\n\n            IteratorHelperState::Completed => Ok(create_iter_result_object(\n                JsValue::undefined(),\n                true,\n                context,\n            )),\n        }\n    }\n\n    /// Creates a new `IteratorHelper` object.\n    pub(crate) fn create(\n        underlying_iterators: Vec<IteratorRecord>,\n        op: IteratorHelperOp,\n        context: &mut Context,\n    ) -> JsObject {\n        JsObject::from_proto_and_data_with_shared_shape(\n            context.root_shape(),\n            context\n                .intrinsics()\n                .objects()\n                .iterator_prototypes()\n                .iterator_helper(),\n            Self {\n                underlying_iterators,\n                state: IteratorHelperState::SuspendedStart,\n                op,\n            },\n        )\n        .upcast()\n    }\n}\n"
  },
  {
    "path": "core/engine/src/builtins/iterable/mod.rs",
    "content": "//! Boa's implementation of ECMAScript's `IteratorRecord` and iterator prototype objects.\n\nuse crate::{\n    Context, JsResult, JsValue,\n    builtins::{BuiltInBuilder, IntrinsicObject},\n    context::intrinsics::Intrinsics,\n    error::JsNativeError,\n    js_string,\n    object::JsObject,\n    realm::Realm,\n    symbol::JsSymbol,\n};\nuse boa_gc::{Finalize, Trace};\n\nmod async_from_sync_iterator;\npub(crate) mod iterator_constructor;\npub(crate) mod iterator_helper;\npub(crate) mod wrap_for_valid_iterator;\n\n#[cfg(test)]\nmod tests;\n\npub(crate) use async_from_sync_iterator::AsyncFromSyncIterator;\n\n/// `IfAbruptCloseIterator ( value, iteratorRecord )`\n///\n/// `IfAbruptCloseIterator` is a shorthand for a sequence of algorithm steps that use an `Iterator`\n/// Record.\n///\n/// More information:\n///  - [ECMA reference][spec]\n///\n///  [spec]: https://tc39.es/ecma262/#sec-ifabruptcloseiterator\nmacro_rules! if_abrupt_close_iterator {\n    ($value:expr, $iterator_record:expr, $context:expr) => {\n        match $value {\n            // 1. If value is an abrupt completion, return ? IteratorClose(iteratorRecord, value).\n            Err(err) => return $iterator_record.close(Err(err), $context),\n            // 2. Else if value is a Completion Record, set value to value.\n            Ok(value) => value,\n        }\n    };\n}\n\n// Export macro to crate level\npub(crate) use if_abrupt_close_iterator;\n\nuse super::OrdinaryObject;\n\n/// The built-in iterator prototypes.\n#[derive(Debug, Trace, Finalize)]\npub struct IteratorPrototypes {\n    /// The `IteratorPrototype` object.\n    iterator: JsObject,\n\n    /// The `AsyncIteratorPrototype` object.\n    async_iterator: JsObject,\n\n    /// The `AsyncFromSyncIteratorPrototype` prototype object.\n    async_from_sync_iterator: JsObject,\n\n    /// The `ArrayIteratorPrototype` prototype object.\n    array: JsObject,\n\n    /// The `SetIteratorPrototype` prototype object.\n    set: JsObject,\n\n    /// The `StringIteratorPrototype` prototype object.\n    string: JsObject,\n\n    /// The `RegExpStringIteratorPrototype` prototype object.\n    regexp_string: JsObject,\n\n    /// The `MapIteratorPrototype` prototype object.\n    map: JsObject,\n\n    /// The `%SegmentIteratorPrototype%` prototype object.\n    #[cfg(feature = \"intl\")]\n    segment: JsObject,\n\n    /// The `%IteratorHelperPrototype%` prototype object.\n    iterator_helper: JsObject,\n\n    /// The `%WrapForValidIteratorPrototype%` prototype object.\n    wrap_for_valid_iterator: JsObject,\n}\n\nimpl Default for IteratorPrototypes {\n    fn default() -> Self {\n        Self {\n            iterator: JsObject::with_null_proto(),\n            async_iterator: JsObject::with_null_proto(),\n            async_from_sync_iterator: JsObject::with_null_proto(),\n            array: JsObject::with_null_proto(),\n            set: JsObject::with_null_proto(),\n            string: JsObject::with_null_proto(),\n            regexp_string: JsObject::with_null_proto(),\n            map: JsObject::with_null_proto(),\n            #[cfg(feature = \"intl\")]\n            segment: JsObject::with_null_proto(),\n            iterator_helper: JsObject::with_null_proto(),\n            wrap_for_valid_iterator: JsObject::with_null_proto(),\n        }\n    }\n}\n\nimpl IteratorPrototypes {\n    /// Returns the `ArrayIteratorPrototype` object.\n    #[inline]\n    #[must_use]\n    pub fn array(&self) -> JsObject {\n        self.array.clone()\n    }\n\n    /// Returns the `IteratorPrototype` object.\n    #[inline]\n    #[must_use]\n    pub fn iterator(&self) -> JsObject {\n        self.iterator.clone()\n    }\n\n    /// Returns the `AsyncIteratorPrototype` object.\n    #[inline]\n    #[must_use]\n    pub fn async_iterator(&self) -> JsObject {\n        self.async_iterator.clone()\n    }\n\n    /// Returns the `AsyncFromSyncIteratorPrototype` object.\n    #[inline]\n    #[must_use]\n    pub fn async_from_sync_iterator(&self) -> JsObject {\n        self.async_from_sync_iterator.clone()\n    }\n\n    /// Returns the `SetIteratorPrototype` object.\n    #[inline]\n    #[must_use]\n    pub fn set(&self) -> JsObject {\n        self.set.clone()\n    }\n\n    /// Returns the `StringIteratorPrototype` object.\n    #[inline]\n    #[must_use]\n    pub fn string(&self) -> JsObject {\n        self.string.clone()\n    }\n\n    /// Returns the `RegExpStringIteratorPrototype` object.\n    #[inline]\n    #[must_use]\n    pub fn regexp_string(&self) -> JsObject {\n        self.regexp_string.clone()\n    }\n\n    /// Returns the `MapIteratorPrototype` object.\n    #[inline]\n    #[must_use]\n    pub fn map(&self) -> JsObject {\n        self.map.clone()\n    }\n\n    /// Returns the `%SegmentIteratorPrototype%` object.\n    #[inline]\n    #[must_use]\n    #[cfg(feature = \"intl\")]\n    pub fn segment(&self) -> JsObject {\n        self.segment.clone()\n    }\n\n    /// Returns the `%IteratorHelperPrototype%` object.\n    #[inline]\n    #[must_use]\n    pub fn iterator_helper(&self) -> JsObject {\n        self.iterator_helper.clone()\n    }\n\n    /// Returns the `%WrapForValidIteratorPrototype%` object.\n    #[inline]\n    #[must_use]\n    pub fn wrap_for_valid_iterator(&self) -> JsObject {\n        self.wrap_for_valid_iterator.clone()\n    }\n}\n\n/// `%IteratorPrototype%` object\n///\n/// More information:\n///  - [ECMA reference][spec]\n///\n/// [spec]: https://tc39.es/ecma262/#sec-%iteratorprototype%-object\npub(crate) struct Iterator;\n\nimpl IntrinsicObject for Iterator {\n    fn init(realm: &Realm) {\n        BuiltInBuilder::with_intrinsic::<Self>(realm)\n            .static_method(|v, _, _| Ok(v.clone()), JsSymbol::iterator(), 0)\n            .build();\n    }\n\n    fn get(intrinsics: &Intrinsics) -> JsObject {\n        intrinsics.objects().iterator_prototypes().iterator()\n    }\n}\n\n/// `%AsyncIteratorPrototype%` object\n///\n/// More information:\n///  - [ECMA reference][spec]\n///\n/// [spec]: https://tc39.es/ecma262/#sec-asynciteratorprototype\npub(crate) struct AsyncIterator;\n\nimpl IntrinsicObject for AsyncIterator {\n    fn init(realm: &Realm) {\n        BuiltInBuilder::with_intrinsic::<Self>(realm)\n            .static_method(|v, _, _| Ok(v.clone()), JsSymbol::async_iterator(), 0)\n            .build();\n    }\n\n    fn get(intrinsics: &Intrinsics) -> JsObject {\n        intrinsics.objects().iterator_prototypes().async_iterator()\n    }\n}\n\n/// `CreateIterResultObject( value, done )`\n///\n/// Generates an object supporting the `IteratorResult` interface.\npub fn create_iter_result_object(value: JsValue, done: bool, context: &mut Context) -> JsValue {\n    // 1. Assert: Type(done) is Boolean.\n    // 2. Let obj be ! OrdinaryObjectCreate(%Object.prototype%).\n    // 3. Perform ! CreateDataPropertyOrThrow(obj, \"value\", value).\n    // 4. Perform ! CreateDataPropertyOrThrow(obj, \"done\", done).\n    let obj = context\n        .intrinsics()\n        .templates()\n        .iterator_result()\n        .create(OrdinaryObject, vec![value, done.into()]);\n\n    // 5. Return obj.\n    obj.into()\n}\n\n/// Iterator hint for `GetIterator`.\n#[derive(Debug, Clone, Copy, PartialEq, Eq)]\npub enum IteratorHint {\n    /// Hints that the iterator should be sync.\n    Sync,\n\n    /// Hints that the iterator should be async.\n    Async,\n}\n\nimpl JsValue {\n    /// `GetIteratorFromMethod ( obj, method )`\n    ///\n    /// More information:\n    ///  - [ECMA reference][spec]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-getiteratorfrommethod\n    pub fn get_iterator_from_method(\n        &self,\n        method: &JsObject,\n        context: &mut Context,\n    ) -> JsResult<IteratorRecord> {\n        // 1. Let iterator be ? Call(method, obj).\n        let iterator = method.call(self, &[], context)?;\n        // 2. If iterator is not an Object, throw a TypeError exception.\n        let iterator_obj = iterator.as_object().ok_or_else(|| {\n            JsNativeError::typ().with_message(\"returned iterator is not an object\")\n        })?;\n        // 3. Let nextMethod be ? Get(iterator, \"next\").\n        let next_method = iterator_obj.get(js_string!(\"next\"), context)?;\n        // 4. Let iteratorRecord be the Iterator Record { [[Iterator]]: iterator, [[NextMethod]]: nextMethod, [[Done]]: false }.\n        // 5. Return iteratorRecord.\n        Ok(IteratorRecord::new(iterator_obj.clone(), next_method))\n    }\n\n    /// `GetIterator ( obj, kind )`\n    ///\n    /// More information:\n    ///  - [ECMA reference][spec]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-getiterator\n    pub fn get_iterator(\n        &self,\n        hint: IteratorHint,\n        context: &mut Context,\n    ) -> JsResult<IteratorRecord> {\n        let method = match hint {\n            // 1. If kind is async, then\n            IteratorHint::Async => {\n                // a. Let method be ? GetMethod(obj, %Symbol.asyncIterator%).\n                let Some(method) = self.get_method(JsSymbol::async_iterator(), context)? else {\n                    // b. If method is undefined, then\n                    //     i. Let syncMethod be ? GetMethod(obj, %Symbol.iterator%).\n                    let sync_method =\n                        self.get_method(JsSymbol::iterator(), context)?\n                            .ok_or_else(|| {\n                                // ii. If syncMethod is undefined, throw a TypeError exception.\n                                JsNativeError::typ().with_message(format!(\n                                    \"value with type `{}` is not iterable\",\n                                    self.type_of()\n                                ))\n                            })?;\n                    // iii. Let syncIteratorRecord be ? GetIteratorFromMethod(obj, syncMethod).\n                    let sync_iterator_record =\n                        self.get_iterator_from_method(&sync_method, context)?;\n                    // iv. Return CreateAsyncFromSyncIterator(syncIteratorRecord).\n                    return Ok(AsyncFromSyncIterator::create(sync_iterator_record, context));\n                };\n\n                Some(method)\n            }\n            // 2. Else,\n            IteratorHint::Sync => {\n                // a. Let method be ? GetMethod(obj, %Symbol.iterator%).\n                self.get_method(JsSymbol::iterator(), context)?\n            }\n        };\n\n        let method = method.ok_or_else(|| {\n            // 3. If method is undefined, throw a TypeError exception.\n            JsNativeError::typ().with_message(format!(\n                \"value with type `{}` is not iterable\",\n                self.type_of()\n            ))\n        })?;\n\n        // 4. Return ? GetIteratorFromMethod(obj, method).\n        self.get_iterator_from_method(&method, context)\n    }\n}\n\n/// The result of the iteration process.\n#[derive(Debug, Clone, Trace, Finalize)]\npub struct IteratorResult {\n    object: JsObject,\n}\n\nimpl IteratorResult {\n    /// Gets a new `IteratorResult` from a value. Returns `Err` if\n    /// the value is not a [`JsObject`]\n    pub(crate) fn from_value(value: JsValue) -> JsResult<Self> {\n        if let Some(object) = value.into_object() {\n            Ok(Self { object })\n        } else {\n            Err(JsNativeError::typ()\n                .with_message(\"next value should be an object\")\n                .into())\n        }\n    }\n\n    /// Gets the inner object of this `IteratorResult`.\n    pub(crate) const fn object(&self) -> &JsObject {\n        &self.object\n    }\n\n    /// `IteratorComplete ( iterResult )`\n    ///\n    /// The abstract operation `IteratorComplete` takes argument `iterResult` (an `Object`) and\n    /// returns either a normal completion containing a `Boolean` or a throw completion.\n    ///\n    /// More information:\n    ///  - [ECMA reference][spec]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-iteratorcomplete\n    #[inline]\n    pub fn complete(&self, context: &mut Context) -> JsResult<bool> {\n        // 1. Return ToBoolean(? Get(iterResult, \"done\")).\n        Ok(self.object.get(js_string!(\"done\"), context)?.to_boolean())\n    }\n\n    /// `IteratorValue ( iterResult )`\n    ///\n    /// The abstract operation `IteratorValue` takes argument `iterResult` (an `Object`) and\n    /// returns either a normal completion containing an ECMAScript language value or a throw\n    /// completion.\n    ///\n    /// More information:\n    ///  - [ECMA reference][spec]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-iteratorvalue\n    #[inline]\n    pub fn value(&self, context: &mut Context) -> JsResult<JsValue> {\n        // 1. Return ? Get(iterResult, \"value\").\n        self.object.get(js_string!(\"value\"), context)\n    }\n}\n\n/// Iterator Record\n///\n/// An Iterator Record is a Record value used to encapsulate an\n/// `Iterator` or `AsyncIterator` along with the `next` method.\n///\n/// More information:\n///  - [ECMA reference][spec]\n///\n/// [spec]: https://tc39.es/ecma262/#sec-iterator-records\n#[derive(Clone, Debug, Finalize, Trace)]\npub struct IteratorRecord {\n    /// `[[Iterator]]`\n    ///\n    /// An object that conforms to the `Iterator` or `AsyncIterator` interface.\n    iterator: JsObject,\n\n    /// `[[NextMethod]]`\n    ///\n    /// The `next` method of the `[[Iterator]]` object.\n    next_method: JsValue,\n\n    /// `[[Done]]`\n    ///\n    /// Whether the iterator has been closed.\n    done: bool,\n\n    /// The result of the last call to `next`.\n    last_result: IteratorResult,\n}\n\nimpl IteratorRecord {\n    /// Creates a new `IteratorRecord` with the given iterator object, next method and `done` flag.\n    #[inline]\n    #[must_use]\n    pub fn new(iterator: JsObject, next_method: JsValue) -> Self {\n        Self {\n            iterator,\n            next_method,\n            done: false,\n            last_result: IteratorResult {\n                object: JsObject::with_null_proto(),\n            },\n        }\n    }\n\n    /// Get the `[[Iterator]]` field of the `IteratorRecord`.\n    pub(crate) const fn iterator(&self) -> &JsObject {\n        &self.iterator\n    }\n\n    /// Gets the `[[NextMethod]]` field of the `IteratorRecord`.\n    pub(crate) const fn next_method(&self) -> &JsValue {\n        &self.next_method\n    }\n\n    /// Gets the last result object of the iterator record.\n    pub(crate) const fn last_result(&self) -> &IteratorResult {\n        &self.last_result\n    }\n\n    /// Runs `f`, setting the `done` field of this `IteratorRecord` to `true` if `f` returns\n    /// an error.\n    fn set_done_on_err<R, F>(&mut self, f: F) -> JsResult<R>\n    where\n        F: FnOnce(&mut Self) -> JsResult<R>,\n    {\n        let result = f(self);\n        if result.is_err() {\n            self.done = true;\n        }\n        result\n    }\n\n    /// Gets the current value of the `IteratorRecord`.\n    pub(crate) fn value(&mut self, context: &mut Context) -> JsResult<JsValue> {\n        self.set_done_on_err(|iter| iter.last_result.value(context))\n    }\n\n    /// Get the `[[Done]]` field of the `IteratorRecord`.\n    pub(crate) const fn done(&self) -> bool {\n        self.done\n    }\n\n    /// Updates the current result value of this iterator record.\n    pub(crate) fn update_result(&mut self, result: JsValue, context: &mut Context) -> JsResult<()> {\n        self.set_done_on_err(|iter| {\n            // 3. If Type(result) is not Object, throw a TypeError exception.\n            // 4. Return result.\n            // `IteratorResult::from_value` does this for us.\n\n            // `IteratorStep(iteratorRecord)`\n            // https://tc39.es/ecma262/#sec-iteratorstep\n\n            // 1. Let result be ? IteratorNext(iteratorRecord).\n            let result = IteratorResult::from_value(result)?;\n            // 2. Let done be ? IteratorComplete(result).\n            // 3. If done is true, return false.\n            iter.done = result.complete(context)?;\n\n            iter.last_result = result;\n\n            Ok(())\n        })\n    }\n\n    /// `IteratorNext ( iteratorRecord [ , value ] )`\n    ///\n    /// The abstract operation `IteratorNext` takes argument `iteratorRecord` (an `Iterator`\n    /// Record) and optional argument `value` (an ECMAScript language value) and returns either a\n    /// normal completion containing an `Object` or a throw completion.\n    ///\n    /// More information:\n    ///  - [ECMA reference][spec]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-iteratornext\n    pub(crate) fn next(\n        &mut self,\n        value: Option<&JsValue>,\n        context: &mut Context,\n    ) -> JsResult<IteratorResult> {\n        // 1. If value is not present, then\n        //     a. Let result be Completion(Call(iteratorRecord.[[NextMethod]], iteratorRecord.[[Iterator]])).\n        // 2. Else,\n        //     a. Let result be Completion(Call(iteratorRecord.[[NextMethod]], iteratorRecord.[[Iterator]], « value »)).\n        // 3. If result is a throw completion, then\n        //     a. Set iteratorRecord.[[Done]] to true.\n        //     b. Return ? result.\n        // 4. Set result to ! result.\n        // 5. If result is not an Object, then\n        //     a. Set iteratorRecord.[[Done]] to true.\n        //     b. Throw a TypeError exception.\n        // 6. Return result.\n        // NOTE: In this case, `set_done_on_err` does all the heavylifting for us, which\n        // simplifies the instructions below.\n        self.set_done_on_err(|iter| {\n            iter.next_method\n                .call(\n                    &iter.iterator.clone().into(),\n                    value.map_or(&[], std::slice::from_ref),\n                    context,\n                )\n                .and_then(IteratorResult::from_value)\n        })\n    }\n\n    /// `IteratorStep ( iteratorRecord )`\n    ///\n    /// Updates the `IteratorRecord` and returns `true` if the next result record returned\n    /// `done: true`, otherwise returns `false`. This differs slightly from the spec, but also\n    /// simplifies some logic around iterators.\n    ///\n    /// More information:\n    ///  - [ECMA reference][spec]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-iteratorstep\n    pub(crate) fn step(&mut self, context: &mut Context) -> JsResult<bool> {\n        self.set_done_on_err(|iter| {\n            // 1. Let result be ? IteratorNext(iteratorRecord).\n            let result = iter.next(None, context)?;\n\n            // 2. Let done be Completion(IteratorComplete(result)).\n            // 3. If done is a throw completion, then\n            //     a. Set iteratorRecord.[[Done]] to true.\n            //     b. Return ? done.\n            // 4. Set done to ! done.\n            // 5. If done is true, then\n            //     a. Set iteratorRecord.[[Done]] to true.\n            //     b. Return done.\n            iter.done = result.complete(context)?;\n\n            iter.last_result = result;\n\n            // 6. Return result.\n            Ok(iter.done)\n        })\n    }\n\n    /// `IteratorStepValue ( iteratorRecord )`\n    ///\n    /// Updates the `IteratorRecord` and returns `Some(value)` if the next result record returned\n    /// `done: true`, otherwise returns `None`.\n    ///\n    /// More information:\n    ///  - [ECMA reference][spec]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-iteratorstepvalue\n    pub(crate) fn step_value(&mut self, context: &mut Context) -> JsResult<Option<JsValue>> {\n        // 1. Let result be ? IteratorStep(iteratorRecord).\n        if self.step(context)? {\n            // 2. If result is done, then\n            //     a. Return done.\n            Ok(None)\n        } else {\n            // 3. Let value be Completion(IteratorValue(result)).\n            // 4. If value is a throw completion, then\n            //     a. Set iteratorRecord.[[Done]] to true.\n            // 5. Return ? value.\n            self.value(context).map(Some)\n        }\n    }\n\n    /// `IteratorClose ( iteratorRecord, completion )`\n    ///\n    /// The abstract operation `IteratorClose` takes arguments `iteratorRecord` (an\n    /// [Iterator Record][Self]) and `completion` (a `Completion` Record) and returns a\n    /// `Completion` Record. It is used to notify an iterator that it should perform any actions it\n    /// would normally perform when it has reached its completed state.\n    ///\n    /// More information:\n    ///  - [ECMA reference][spec]\n    ///\n    ///  [spec]: https://tc39.es/ecma262/#sec-iteratorclose\n    pub(crate) fn close(\n        &self,\n        completion: JsResult<JsValue>,\n        context: &mut Context,\n    ) -> JsResult<JsValue> {\n        // 1. Assert: Type(iteratorRecord.[[Iterator]]) is Object.\n\n        // 2. Let iterator be iteratorRecord.[[Iterator]].\n        let iterator = &self.iterator;\n\n        // 3. Let innerResult be Completion(GetMethod(iterator, \"return\")).\n        let inner_result = iterator.get_method(js_string!(\"return\"), context);\n\n        // 4. If innerResult.[[Type]] is normal, then\n        let inner_result = match inner_result {\n            Ok(inner_result) => {\n                // a. Let return be innerResult.[[Value]].\n                let r#return = inner_result;\n\n                if let Some(r#return) = r#return {\n                    // c. Set innerResult to Completion(Call(return, iterator)).\n                    r#return.call(&iterator.clone().into(), &[], context)\n                } else {\n                    // b. If return is undefined, return ? completion.\n                    return completion;\n                }\n            }\n            Err(inner_result) => {\n                // 5. If completion.[[Type]] is throw, return ? completion.\n                completion?;\n\n                // 6. If innerResult.[[Type]] is throw, return ? innerResult.\n                return Err(inner_result);\n            }\n        };\n\n        // 5. If completion.[[Type]] is throw, return ? completion.\n        let completion = completion?;\n\n        // 6. If innerResult.[[Type]] is throw, return ? innerResult.\n        let inner_result = inner_result?;\n\n        if inner_result.is_object() {\n            // 8. Return ? completion.\n            Ok(completion)\n        } else {\n            // 7. If Type(innerResult.[[Value]]) is not Object, throw a TypeError exception.\n            Err(JsNativeError::typ()\n                .with_message(\"inner result was not an object\")\n                .into())\n        }\n    }\n\n    /// `IteratorToList ( iteratorRecord )`\n    ///\n    /// More information:\n    ///  - [ECMA reference][spec]\n    ///\n    ///  [spec]: https://tc39.es/ecma262/#sec-iteratortolist\n    pub(crate) fn into_list(mut self, context: &mut Context) -> JsResult<Vec<JsValue>> {\n        // 1. Let values be a new empty List.\n        let mut values = Vec::new();\n\n        // 2. Repeat,\n        //     a. Let next be ? IteratorStepValue(iteratorRecord).\n        while let Some(value) = self.step_value(context)? {\n            // c. Append next to values.\n            values.push(value);\n        }\n\n        //     b. If next is done, then\n        //         i. Return values.\n        Ok(values)\n    }\n}\n\n/// `GetIteratorDirect ( obj )`\n///\n/// The abstract operation `GetIteratorDirect` takes argument `obj` (an Object)\n/// and returns either a normal completion containing an Iterator Record or a\n/// throw completion.\n///\n/// More information:\n///  - [ECMAScript reference][spec]\n///\n/// [spec]: https://tc39.es/ecma262/#sec-getiteratordirect\npub(crate) fn get_iterator_direct(\n    obj: &JsObject,\n    context: &mut Context,\n) -> JsResult<IteratorRecord> {\n    // 1. Let nextMethod be ? Get(obj, \"next\").\n    let next_method = obj.get(js_string!(\"next\"), context)?;\n    // 2. Let iteratorRecord be the Iterator Record { [[Iterator]]: obj, [[NextMethod]]: nextMethod, [[Done]]: false }.\n    // 3. Return iteratorRecord.\n    Ok(IteratorRecord::new(obj.clone(), next_method))\n}\n\n/// `GetIteratorFlattenable ( obj, stringHandling )`\n///\n/// The abstract operation `GetIteratorFlattenable` takes arguments `obj` (an ECMAScript\n/// language value) and `stringHandling` (iterate-strings or reject-strings) and returns\n/// either a normal completion containing an Iterator Record or a throw completion.\n///\n/// More information:\n///  - [ECMAScript reference][spec]\n///\n/// [spec]: https://tc39.es/ecma262/#sec-getiteratorflattenable\npub(crate) fn get_iterator_flattenable(\n    obj: &JsValue,\n    iterate_strings: bool,\n    context: &mut Context,\n) -> JsResult<IteratorRecord> {\n    // 1. If obj is not an Object, then\n    if !obj.is_object() {\n        // a. If stringHandling is reject-strings or obj is not a String, throw a TypeError exception.\n        if !iterate_strings || !obj.is_string() {\n            return Err(JsNativeError::typ()\n                .with_message(\"GetIteratorFlattenable: value is not an object\")\n                .into());\n        }\n    }\n\n    // 2. Let method be ? GetMethod(obj, @@iterator).\n    let method = obj.get_method(JsSymbol::iterator(), context)?;\n\n    match method {\n        // 3. If method is undefined, then\n        None => {\n            // a. Let iterator be obj.\n            let iterator = obj.as_object().ok_or_else(|| {\n                JsNativeError::typ()\n                    .with_message(\"GetIteratorFlattenable: value is not iterable and not an object\")\n            })?;\n\n            // b. Return ? GetIteratorDirect(iterator).\n            get_iterator_direct(&iterator, context)\n        }\n        // 4. Else,\n        Some(method) => {\n            // a. Let iterator be ? Call(method, obj).\n            let iterator = method.call(obj, &[], context)?;\n\n            // b. If iterator is not an Object, throw a TypeError exception.\n            let iterator_obj = iterator.as_object().ok_or_else(|| {\n                JsNativeError::typ()\n                    .with_message(\"GetIteratorFlattenable: iterator result is not an object\")\n            })?;\n\n            // c. Return ? GetIteratorDirect(iterator).\n            get_iterator_direct(&iterator_obj, context)\n        }\n    }\n}\n"
  },
  {
    "path": "core/engine/src/builtins/iterable/tests.rs",
    "content": "//! Tests for the Iterator Helpers proposal implementation (#4444).\n\nuse crate::{JsNativeErrorKind, JsValue, TestAction, run_test_actions};\nuse boa_macros::js_str;\n\n// ── Iterator Constructor ──────────────────────────────────────────────────────\n\n#[test]\nfn iterator_constructor_requires_new() {\n    run_test_actions([TestAction::assert_native_error(\n        \"Iterator()\",\n        JsNativeErrorKind::Type,\n        \"Iterator constructor requires 'new'\",\n    )]);\n}\n\n#[test]\nfn iterator_constructor_abstract_class() {\n    run_test_actions([TestAction::assert_native_error(\n        \"new Iterator()\",\n        JsNativeErrorKind::Type,\n        \"Abstract class Iterator not directly constructable\",\n    )]);\n}\n\n#[test]\nfn iterator_subclass_can_be_constructed() {\n    run_test_actions([TestAction::assert(\n        \"class MyIter extends Iterator {\n           next() { return {value: 42, done: false}; }\n         }\n         new MyIter() instanceof Iterator\",\n    )]);\n}\n\n#[test]\nfn iterator_to_string_tag() {\n    run_test_actions([TestAction::assert_eq(\n        \"Iterator.prototype[Symbol.toStringTag]\",\n        js_str!(\"Iterator\"),\n    )]);\n}\n\n// ── Iterator.from() ───────────────────────────────────────────────────────────\n\n#[test]\nfn iterator_from_array() {\n    run_test_actions([TestAction::assert_eq(\n        \"Iterator.from([1, 2, 3]).toArray().join(',')\",\n        js_str!(\"1,2,3\"),\n    )]);\n}\n\n#[test]\nfn iterator_from_string() {\n    run_test_actions([TestAction::assert_eq(\n        \"Iterator.from('abc').toArray().join(',')\",\n        js_str!(\"a,b,c\"),\n    )]);\n}\n\n#[test]\nfn iterator_from_returns_object() {\n    run_test_actions([TestAction::assert_eq(\n        \"typeof Iterator.from([1, 2, 3])\",\n        js_str!(\"object\"),\n    )]);\n}\n\n// ── Lazy — map ────────────────────────────────────────────────────────────────\n\n#[test]\nfn iterator_map_basic() {\n    run_test_actions([TestAction::assert_eq(\n        \"Iterator.from([1, 2, 3]).map(x => x * 2).toArray().join(',')\",\n        js_str!(\"2,4,6\"),\n    )]);\n}\n\n#[test]\nfn iterator_map_counter() {\n    run_test_actions([TestAction::assert_eq(\n        \"Iterator.from(['a','b','c']).map((v, i) => i).toArray().join(',')\",\n        js_str!(\"0,1,2\"),\n    )]);\n}\n\n#[test]\nfn iterator_map_mapper_not_callable_throws() {\n    run_test_actions([TestAction::assert_native_error(\n        \"Iterator.from([1]).map(42)\",\n        JsNativeErrorKind::Type,\n        \"Iterator.prototype.map: mapper is not callable\",\n    )]);\n}\n\n// ── Lazy — filter ─────────────────────────────────────────────────────────────\n\n#[test]\nfn iterator_filter_basic() {\n    run_test_actions([TestAction::assert_eq(\n        \"Iterator.from([1,2,3,4,5]).filter(x => x % 2 === 0).toArray().join(',')\",\n        js_str!(\"2,4\"),\n    )]);\n}\n\n#[test]\nfn iterator_filter_none_match() {\n    run_test_actions([TestAction::assert_eq(\n        \"Iterator.from([1,2,3]).filter(x => x > 99).toArray().length\",\n        0,\n    )]);\n}\n\n// ── Lazy — take ───────────────────────────────────────────────────────────────\n\n#[test]\nfn iterator_take_basic() {\n    run_test_actions([TestAction::assert_eq(\n        \"Iterator.from([1,2,3,4,5]).take(3).toArray().join(',')\",\n        js_str!(\"1,2,3\"),\n    )]);\n}\n\n#[test]\nfn iterator_take_zero() {\n    run_test_actions([TestAction::assert_eq(\n        \"Iterator.from([1,2,3]).take(0).toArray().length\",\n        0,\n    )]);\n}\n\n#[test]\nfn iterator_take_negative_throws() {\n    run_test_actions([TestAction::assert_native_error(\n        \"Iterator.from([1]).take(-1)\",\n        JsNativeErrorKind::Range,\n        \"Iterator.prototype.take: limit is negative\",\n    )]);\n}\n\n#[test]\nfn iterator_take_nan_throws() {\n    run_test_actions([TestAction::assert_native_error(\n        \"Iterator.from([1]).take(NaN)\",\n        JsNativeErrorKind::Range,\n        \"Iterator.prototype.take: limit is NaN\",\n    )]);\n}\n\n#[test]\nfn iterator_take_more_than_length() {\n    run_test_actions([TestAction::assert_eq(\n        \"Iterator.from([1,2,3]).take(100).toArray().join(',')\",\n        js_str!(\"1,2,3\"),\n    )]);\n}\n\n// ── Lazy — drop ───────────────────────────────────────────────────────────────\n\n#[test]\nfn iterator_drop_basic() {\n    run_test_actions([TestAction::assert_eq(\n        \"Iterator.from([1,2,3,4,5]).drop(2).toArray().join(',')\",\n        js_str!(\"3,4,5\"),\n    )]);\n}\n\n#[test]\nfn iterator_drop_more_than_length() {\n    run_test_actions([TestAction::assert_eq(\n        \"Iterator.from([1,2,3]).drop(10).toArray().length\",\n        0,\n    )]);\n}\n\n#[test]\nfn iterator_drop_zero() {\n    run_test_actions([TestAction::assert_eq(\n        \"Iterator.from([1,2,3]).drop(0).toArray().join(',')\",\n        js_str!(\"1,2,3\"),\n    )]);\n}\n\n// ── Lazy — flatMap ────────────────────────────────────────────────────────────\n\n#[test]\nfn iterator_flat_map_basic() {\n    run_test_actions([TestAction::assert_eq(\n        \"Iterator.from([1,2,3]).flatMap(x => [x, x*10]).toArray().join(',')\",\n        js_str!(\"1,10,2,20,3,30\"),\n    )]);\n}\n\n#[test]\nfn iterator_flat_map_inner_empty() {\n    run_test_actions([TestAction::assert_eq(\n        \"Iterator.from([1,2,3]).flatMap(x => []).toArray().length\",\n        0,\n    )]);\n}\n\n#[test]\nfn iterator_flat_map_not_callable_throws() {\n    run_test_actions([TestAction::assert_native_error(\n        \"Iterator.from([1]).flatMap(42)\",\n        JsNativeErrorKind::Type,\n        \"Iterator.prototype.flatMap: mapper is not callable\",\n    )]);\n}\n\n// ── Eager — reduce ────────────────────────────────────────────────────────────\n\n#[test]\nfn iterator_reduce_with_initial_value() {\n    run_test_actions([TestAction::assert_eq(\n        \"Iterator.from([1,2,3,4]).reduce((acc, x) => acc + x, 0)\",\n        10,\n    )]);\n}\n\n#[test]\nfn iterator_reduce_no_initial_value() {\n    run_test_actions([TestAction::assert_eq(\n        \"Iterator.from([1,2,3]).reduce((acc, x) => acc + x)\",\n        6,\n    )]);\n}\n\n#[test]\nfn iterator_reduce_empty_no_initial_throws() {\n    run_test_actions([TestAction::assert_native_error(\n        \"Iterator.from([]).reduce((acc, x) => acc + x)\",\n        JsNativeErrorKind::Type,\n        \"Iterator.prototype.reduce: reduce of empty iterator with no initial value\",\n    )]);\n}\n\n// ── Eager — toArray ───────────────────────────────────────────────────────────\n\n#[test]\nfn iterator_to_array_basic() {\n    run_test_actions([TestAction::assert_eq(\n        \"JSON.stringify(Iterator.from([1,2,3]).toArray())\",\n        js_str!(\"[1,2,3]\"),\n    )]);\n}\n\n#[test]\nfn iterator_to_array_empty() {\n    run_test_actions([TestAction::assert_eq(\n        \"Iterator.from([]).toArray().length\",\n        0,\n    )]);\n}\n\n// ── Eager — forEach ───────────────────────────────────────────────────────────\n\n#[test]\nfn iterator_for_each_basic() {\n    run_test_actions([\n        TestAction::run(\"let sum = 0; Iterator.from([1,2,3]).forEach(x => { sum += x; });\"),\n        TestAction::assert_eq(\"sum\", 6),\n    ]);\n}\n\n// ── Eager — some / every / find ───────────────────────────────────────────────\n\n#[test]\nfn iterator_some_true() {\n    run_test_actions([TestAction::assert(\n        \"Iterator.from([1,2,3]).some(x => x === 2)\",\n    )]);\n}\n\n#[test]\nfn iterator_some_false() {\n    run_test_actions([TestAction::assert(\n        \"!Iterator.from([1,2,3]).some(x => x === 99)\",\n    )]);\n}\n\n#[test]\nfn iterator_every_true() {\n    run_test_actions([TestAction::assert(\n        \"Iterator.from([2,4,6]).every(x => x % 2 === 0)\",\n    )]);\n}\n\n#[test]\nfn iterator_every_false() {\n    run_test_actions([TestAction::assert(\n        \"!Iterator.from([2,4,5]).every(x => x % 2 === 0)\",\n    )]);\n}\n\n#[test]\nfn iterator_find_found() {\n    run_test_actions([TestAction::assert_eq(\n        \"Iterator.from([1,2,3]).find(x => x > 1)\",\n        2,\n    )]);\n}\n\n#[test]\nfn iterator_find_not_found() {\n    run_test_actions([TestAction::assert_eq(\n        \"Iterator.from([1,2,3]).find(x => x > 99)\",\n        JsValue::undefined(),\n    )]);\n}\n\n// ── IteratorHelper protocol ───────────────────────────────────────────────────\n\n#[test]\nfn iterator_helper_to_string_tag() {\n    run_test_actions([TestAction::assert_eq(\n        \"Iterator.from([1]).map(x => x)[Symbol.toStringTag]\",\n        js_str!(\"Iterator Helper\"),\n    )]);\n}\n\n#[test]\nfn iterator_helper_return_closes_underlying() {\n    run_test_actions([\n        TestAction::run(\n            \"let closed = false;\n             const iter = {\n               [Symbol.iterator]() { return this; },\n               next() { return {value: 1, done: false}; },\n               return() { closed = true; return {value: undefined, done: true}; }\n             };\n             const helper = Iterator.from(iter).map(x => x);\n             helper.return();\",\n        ),\n        TestAction::assert(\"closed\"),\n    ]);\n}\n\n// ── Chaining ─────────────────────────────────────────────────────────────────\n\n#[test]\nfn iterator_chaining() {\n    run_test_actions([TestAction::assert_eq(\n        \"Iterator.from([1,2,3,4,5,6,7,8,9,10])\n           .filter(x => x % 2 === 0)\n           .map(x => x * 3)\n           .take(3)\n           .toArray()\n           .join(',')\",\n        js_str!(\"6,12,18\"),\n    )]);\n}\n\n#[test]\nfn iterator_prototype_iterator_self() {\n    run_test_actions([TestAction::assert(\n        \"Iterator.prototype[Symbol.iterator]() === Iterator.prototype\",\n    )]);\n}\n\n#[test]\nfn iterator_concat_basic() {\n    run_test_actions([TestAction::assert_eq(\n        \"Iterator.concat([1,2],[3,4]).toArray().join(',')\",\n        js_str!(\"1,2,3,4\"),\n    )]);\n}\n\n#[test]\nfn iterator_concat_zero_arguments() {\n    run_test_actions([TestAction::assert_eq(\n        \"Iterator.concat().toArray().length\",\n        0,\n    )]);\n}\n\n#[test]\nfn iterator_concat_single_argument() {\n    run_test_actions([TestAction::assert_eq(\n        \"Iterator.concat([1,2]).toArray().join(',')\",\n        js_str!(\"1,2\"),\n    )]);\n}\n\n#[test]\nfn iterator_concat_three_arguments() {\n    run_test_actions([TestAction::assert_eq(\n        \"Iterator.concat([1],[2],[3]).toArray().join(',')\",\n        js_str!(\"1,2,3\"),\n    )]);\n}\n\n#[test]\nfn iterator_concat_lazy_next() {\n    run_test_actions([TestAction::assert_eq(\n        \"Iterator.concat([1,2],[3,4]).next().value\",\n        1,\n    )]);\n}\n\n#[test]\nfn iterator_concat_non_object_throws() {\n    run_test_actions([TestAction::assert_native_error(\n        \"Iterator.concat(42)\",\n        JsNativeErrorKind::Type,\n        \"Iterator.concat requires iterable objects\",\n    )]);\n}\n\n#[test]\nfn iterator_concat_missing_iterator_throws() {\n    run_test_actions([TestAction::assert_native_error(\n        \"Iterator.concat({})\",\n        JsNativeErrorKind::Type,\n        \"Iterator.concat requires objects with @@iterator\",\n    )]);\n}\n\n#[test]\nfn iterator_concat_return_closes_inner() {\n    run_test_actions([\n        TestAction::run(\n            \"let closed = false;\n             const iter = {\n                 [Symbol.iterator]() {\n                     return {\n                         next() { return { value: 1, done: false }; },\n                         return() { closed = true; return { done: true }; }\n                     };\n                 }\n             };\n             const it = Iterator.concat(iter);\n             it.next();\n             it.return();\",\n        ),\n        TestAction::assert(\"closed\"),\n    ]);\n}\n\n#[test]\nfn iterator_concat_return_result_shape() {\n    run_test_actions([TestAction::assert(\n        \"const it = Iterator.concat([1,2]); it.next();\n         const r = it.return(); r.done === true && r.value === undefined\",\n    )]);\n}\n"
  },
  {
    "path": "core/engine/src/builtins/iterable/wrap_for_valid_iterator.rs",
    "content": "//! Boa's implementation of the `%WrapForValidIteratorPrototype%` object.\n//!\n//! This object wraps an iterator record to make it a valid `Iterator` instance\n//! by inheriting from `%Iterator.prototype%`.\n//!\n//! More information:\n//!  - [ECMAScript reference][spec]\n//!\n//! [spec]: https://tc39.es/ecma262/#sec-wrapforvaliditeratorprototype-object\n\nuse crate::{\n    Context, JsData, JsResult, JsValue,\n    builtins::{BuiltInBuilder, IntrinsicObject, iterable::create_iter_result_object},\n    context::intrinsics::Intrinsics,\n    error::JsNativeError,\n    js_string,\n    object::JsObject,\n    realm::Realm,\n};\nuse boa_gc::{Finalize, Trace};\n\nuse super::IteratorRecord;\n\n/// The internal representation of a `WrapForValidIterator` object.\n///\n/// More information:\n///  - [ECMAScript reference][spec]\n///\n/// [spec]: https://tc39.es/ecma262/#sec-wrapforvaliditeratorprototype-object\n#[derive(Debug, Finalize, Trace, JsData)]\npub(crate) struct WrapForValidIterator {\n    /// `[[Iterated]]` — the iterator record this wrapper delegates to.\n    pub(crate) iterated: IteratorRecord,\n}\n\nimpl IntrinsicObject for WrapForValidIterator {\n    fn init(realm: &Realm) {\n        BuiltInBuilder::with_intrinsic::<Self>(realm)\n            .prototype(realm.intrinsics().constructors().iterator().prototype())\n            .static_method(Self::next, js_string!(\"next\"), 0)\n            .static_method(Self::r#return, js_string!(\"return\"), 0)\n            .build();\n    }\n\n    fn get(intrinsics: &Intrinsics) -> JsObject {\n        intrinsics\n            .objects()\n            .iterator_prototypes()\n            .wrap_for_valid_iterator()\n    }\n}\n\nimpl WrapForValidIterator {\n    /// `%WrapForValidIteratorPrototype%.next ( )`\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-%25wrapforvaliditeratorprototype%25.next\n    pub(crate) fn next(\n        this: &JsValue,\n        _args: &[JsValue],\n        context: &mut Context,\n    ) -> JsResult<JsValue> {\n        // 1. Let O be this value.\n        // 2. Perform ? RequireInternalSlot(O, [[Iterated]]).\n        let object = this.as_object().ok_or_else(|| {\n            JsNativeError::typ().with_message(\"WrapForValidIterator method called on non-object\")\n        })?;\n\n        let wrapper = object.downcast_mut::<Self>().ok_or_else(|| {\n            JsNativeError::typ()\n                .with_message(\"WrapForValidIterator method called on incompatible object\")\n        })?;\n\n        // 3. Let iteratorRecord be O.[[Iterated]].\n        // 4. Return ? Call(iteratorRecord.[[NextMethod]], iteratorRecord.[[Iterator]]).\n        let next_method = wrapper.iterated.next_method().clone();\n        let iterator = wrapper.iterated.iterator().clone();\n        drop(wrapper);\n\n        next_method.call(&iterator.into(), &[], context)\n    }\n\n    /// `%WrapForValidIteratorPrototype%.return ( )`\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-%25wrapforvaliditeratorprototype%25.return\n    pub(crate) fn r#return(\n        this: &JsValue,\n        _args: &[JsValue],\n        context: &mut Context,\n    ) -> JsResult<JsValue> {\n        // 1. Let O be this value.\n        // 2. Perform ? RequireInternalSlot(O, [[Iterated]]).\n        let object = this.as_object().ok_or_else(|| {\n            JsNativeError::typ().with_message(\"WrapForValidIterator method called on non-object\")\n        })?;\n\n        let wrapper = object.downcast_mut::<Self>().ok_or_else(|| {\n            JsNativeError::typ()\n                .with_message(\"WrapForValidIterator method called on incompatible object\")\n        })?;\n\n        // 3. Let iterator be O.[[Iterated]].[[Iterator]].\n        let iterator = wrapper.iterated.iterator().clone();\n        drop(wrapper);\n\n        // 4. Assert: iterator is an Object.\n        // 5. Let returnMethod be ? GetMethod(iterator, \"return\").\n        let return_method = iterator.get_method(js_string!(\"return\"), context)?;\n\n        match return_method {\n            // 6. If returnMethod is undefined, then\n            None => {\n                // a. Return CreateIterResultObject(undefined, true).\n                Ok(create_iter_result_object(\n                    JsValue::undefined(),\n                    true,\n                    context,\n                ))\n            }\n            // 7. Return ? Call(returnMethod, iterator).\n            Some(return_method) => return_method.call(&iterator.into(), &[], context),\n        }\n    }\n}\n"
  },
  {
    "path": "core/engine/src/builtins/json/mod.rs",
    "content": "//! Boa's implementation of ECMAScript's global `JSON` object.\n//!\n//! The `JSON` object contains methods for parsing [JavaScript Object Notation (JSON)][spec]\n//! and converting values to JSON. It can't be called or constructed, and aside from its\n//! two method properties, it has no interesting functionality of its own.\n//!\n//! More information:\n//!  - [ECMAScript reference][spec]\n//!  - [MDN documentation][mdn]\n//!  - [JSON specification][json]\n//!\n//! [spec]: https://tc39.es/ecma262/#sec-json\n//! [json]: https://www.json.org/json-en.html\n//! [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON\n\nuse std::{borrow::Cow, iter::once};\n\nuse boa_ast::scope::Scope;\nuse boa_gc::{Finalize, Gc, Trace};\nuse boa_macros::utf16;\nuse itertools::Itertools;\n\nuse crate::JsExpect;\nuse crate::{\n    Context, JsArgs, JsBigInt, JsData, JsResult, JsString, JsValue, SpannedSourceText,\n    builtins::BuiltInObject,\n    bytecompiler::ByteCompiler,\n    context::intrinsics::Intrinsics,\n    error::JsNativeError,\n    js_string,\n    object::{IntegrityLevel, JsObject, internal_methods::InternalMethodPropertyContext},\n    property::{Attribute, PropertyNameKind},\n    realm::Realm,\n    string::{CodePoint, StaticJsStrings},\n    symbol::JsSymbol,\n    value::IntegerOrInfinity,\n    vm::{CallFrame, CallFrameFlags, source_info::SourcePath},\n};\nuse boa_parser::{Parser, Source};\n\nuse super::{BuiltInBuilder, IntrinsicObject};\n\n/// Converts a nibble (0-15) to its lowercase hex digit as a UTF-16 code unit.\nconst fn to_hex_digit(val: u16) -> u16 {\n    match val & 0xF {\n        v @ 0..=9 => b'0' as u16 + v,\n        v => b'a' as u16 + (v - 10),\n    }\n}\n\n#[cfg(test)]\nmod tests;\n\n/// Marker struct for the `[[IsRawJSON]]` internal slot.\n#[derive(Debug, Trace, Finalize, JsData)]\npub(crate) struct RawJson;\n\n/// A tree node representing the structure of parsed JSON, preserving source\n/// text spans for primitive values. Used to provide `context.source` to\n/// the reviver function in `JSON.parse`.\n#[derive(Debug)]\nenum JsonNode {\n    /// A primitive value (string, number, boolean, null) with its original source text.\n    Primitive(String),\n    /// An array of child nodes.\n    Array(Vec<JsonNode>),\n    /// An object with key-value pairs.\n    Object(Vec<(String, JsonNode)>),\n}\n\n/// Visitor that builds a `JsonNode` source tree by walking the parsed AST,\n/// extracting source text from `LinearSpan` information tracked by the parser.\n/// Uses a stack to build the tree bottom-up during depth-first traversal.\nstruct JsonSourceVisitor<'a> {\n    source_text: &'a boa_ast::SourceText,\n    interner: &'a boa_interner::Interner,\n    /// Stack used to build the tree bottom-up. Leaf nodes (literals) are pushed\n    /// first, then parent nodes (arrays/objects) pop their children and push themselves.\n    stack: Vec<JsonNode>,\n}\n\nimpl<'a> JsonSourceVisitor<'a> {\n    /// Creates a new `JsonSourceVisitor`.\n    fn new(source_text: &'a boa_ast::SourceText, interner: &'a boa_interner::Interner) -> Self {\n        Self {\n            source_text,\n            interner,\n            stack: Vec::new(),\n        }\n    }\n\n    /// Consumes the visitor and returns the final `JsonNode` tree.\n    fn finish(mut self) -> Option<JsonNode> {\n        self.stack.pop()\n    }\n}\n\nimpl<'ast> boa_ast::visitor::Visitor<'ast> for JsonSourceVisitor<'_> {\n    type BreakTy = std::convert::Infallible;\n\n    fn visit_literal(\n        &mut self,\n        node: &'ast boa_ast::expression::literal::Literal,\n    ) -> std::ops::ControlFlow<Self::BreakTy> {\n        let span = node.linear_span();\n        let code_points = self.source_text.get_code_points_from_span(span);\n        let text = String::from_utf16_lossy(code_points);\n        self.stack.push(JsonNode::Primitive(text));\n        std::ops::ControlFlow::Continue(())\n    }\n\n    fn visit_array_literal(\n        &mut self,\n        node: &'ast boa_ast::expression::literal::ArrayLiteral,\n    ) -> std::ops::ControlFlow<Self::BreakTy> {\n        let count = node.as_ref().len();\n        let base = self.stack.len();\n\n        // Visit each element, which pushes child nodes onto the stack\n        for elem in node.as_ref() {\n            if let Some(expr) = elem {\n                self.visit_expression(expr)?;\n            } else {\n                // Sparse element (should not happen in JSON)\n                self.stack.push(JsonNode::Primitive(\"null\".to_string()));\n            }\n        }\n\n        // Pop the children that were just pushed and collect into array\n        let children: Vec<JsonNode> = self.stack.drain(base..).collect();\n        debug_assert_eq!(children.len(), count);\n        self.stack.push(JsonNode::Array(children));\n        std::ops::ControlFlow::Continue(())\n    }\n\n    fn visit_object_literal(\n        &mut self,\n        node: &'ast boa_ast::expression::literal::ObjectLiteral,\n    ) -> std::ops::ControlFlow<Self::BreakTy> {\n        use boa_ast::expression::literal::PropertyDefinition;\n\n        let base = self.stack.len();\n        let mut keys: Vec<String> = Vec::new();\n\n        for prop in node.properties() {\n            if let PropertyDefinition::Property(name, value) = prop {\n                let key = if let Some(ident) = name.literal() {\n                    self.interner.resolve_expect(ident.sym()).to_string()\n                } else {\n                    String::new()\n                };\n                keys.push(key);\n                // Visit the value expression, which pushes the child node\n                self.visit_expression(value)?;\n            }\n        }\n\n        // Pop the value nodes and zip with keys\n        let values: Vec<JsonNode> = self.stack.drain(base..).collect();\n        debug_assert_eq!(keys.len(), values.len());\n        let entries: Vec<(String, JsonNode)> = keys.into_iter().zip(values).collect();\n        self.stack.push(JsonNode::Object(entries));\n        std::ops::ControlFlow::Continue(())\n    }\n\n    fn visit_unary(\n        &mut self,\n        node: &'ast boa_ast::expression::operator::Unary,\n    ) -> std::ops::ControlFlow<Self::BreakTy> {\n        use boa_ast::expression::operator::unary::UnaryOp;\n        use boa_ast::visitor::VisitWith;\n\n        if node.op() == UnaryOp::Minus\n            && let boa_ast::Expression::Literal(lit) = node.target()\n        {\n            let span = lit.linear_span();\n            let code_points = self.source_text.get_code_points_from_span(span);\n            let num_text = String::from_utf16_lossy(code_points);\n            self.stack.push(JsonNode::Primitive(format!(\"-{num_text}\")));\n            return std::ops::ControlFlow::Continue(());\n        }\n\n        // Default: recurse into children\n        node.visit_with(self)\n    }\n\n    fn visit_parenthesized(\n        &mut self,\n        node: &'ast boa_ast::expression::Parenthesized,\n    ) -> std::ops::ControlFlow<Self::BreakTy> {\n        // Unwrap parentheses and visit inner expression\n        self.visit_expression(node.expression())\n    }\n}\n\n/// JavaScript `JSON` global object.\n#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]\npub(crate) struct Json;\n\nimpl IntrinsicObject for Json {\n    fn init(realm: &Realm) {\n        let to_string_tag = JsSymbol::to_string_tag();\n        let attribute = Attribute::READONLY | Attribute::NON_ENUMERABLE | Attribute::CONFIGURABLE;\n\n        BuiltInBuilder::with_intrinsic::<Self>(realm)\n            .static_method(Self::parse, js_string!(\"parse\"), 2)\n            .static_method(Self::stringify, js_string!(\"stringify\"), 3)\n            .static_method(Self::raw_json, js_string!(\"rawJSON\"), 1)\n            .static_method(Self::is_raw_json, js_string!(\"isRawJSON\"), 1)\n            .static_property(to_string_tag, Self::NAME, attribute)\n            .build();\n    }\n\n    fn get(intrinsics: &Intrinsics) -> JsObject {\n        intrinsics.objects().json()\n    }\n}\n\nimpl BuiltInObject for Json {\n    const NAME: JsString = StaticJsStrings::JSON;\n}\n\nimpl Json {\n    /// `JSON.parse( text[, reviver] )`\n    ///\n    /// This `JSON` method parses a JSON string, constructing the JavaScript value or object described by the string.\n    ///\n    /// An optional `reviver` function can be provided to perform a transformation on the resulting object before it is returned.\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///  - [MDN documentation][mdn]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-json.parse\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/parse\n    pub(crate) fn parse(_: &JsValue, args: &[JsValue], context: &mut Context) -> JsResult<JsValue> {\n        // 1. Let jsonString be ? ToString(text).\n        let json_string = args\n            .first()\n            .cloned()\n            .unwrap_or_default()\n            .to_string(context)?\n            .to_std_string()\n            .map_err(|e| JsNativeError::syntax().with_message(e.to_string()))?;\n\n        // 2. Parse ! StringToCodePoints(jsonString) as a JSON text as specified in ECMA-404.\n        //    Throw a SyntaxError exception if it is not a valid JSON text as defined in that specification.\n        if let Err(e) = serde_json::from_str::<serde_json::Value>(&json_string) {\n            return Err(JsNativeError::syntax().with_message(e.to_string()).into());\n        }\n\n        // Check if a reviver is provided, to determine if we need source text tracking\n        let has_reviver = args.get_or_undefined(1).is_callable();\n\n        // 3. Let scriptString be the string-concatenation of \"(\", jsonString, and \");\".\n        let script_string = format!(\"({json_string});\");\n\n        // 4-10. Parse and evaluate the script\n        let source = Source::from_bytes(&script_string);\n        let mut parser = Parser::new(source);\n        parser.set_json_parse();\n\n        // Use parse_script_with_source to get SourceText for span-based source extraction\n        let (script, source_text) =\n            parser.parse_script_with_source(&Scope::new_global(), context.interner_mut())?;\n\n        // Build the source tree from the AST if a reviver is present.\n        // This walks the parsed AST and extracts source text from LinearSpan information,\n        // avoiding the need to re-parse the JSON string separately.\n        let source_tree = if has_reviver {\n            let expr = script.statements().statements().first().and_then(|item| {\n                if let boa_ast::StatementListItem::Statement(stmt) = item\n                    && let boa_ast::Statement::Expression(expr) = stmt.as_ref()\n                {\n                    return Some(expr);\n                }\n                None\n            });\n            expr.and_then(|e| {\n                use boa_ast::visitor::Visitor;\n                let mut visitor = JsonSourceVisitor::new(&source_text, context.interner());\n                let _ = visitor.visit_expression(e);\n                visitor.finish()\n            })\n        } else {\n            None\n        };\n\n        let code_block = {\n            let in_with = context.vm.frame().environments.has_object_environment();\n            let spanned_source_text = SpannedSourceText::new_source_only(\n                crate::spanned_source_text::SourceText::new(source_text),\n            );\n            let mut compiler = ByteCompiler::new(\n                js_string!(\"<json>\"),\n                script.strict(),\n                true,\n                context.realm().scope().clone(),\n                context.realm().scope().clone(),\n                false,\n                false,\n                context.interner_mut(),\n                in_with,\n                spanned_source_text,\n                SourcePath::Json,\n            );\n            compiler.compile_statement_list(script.statements(), true, false);\n            Gc::new(compiler.finish())\n        };\n\n        let realm = context.realm().clone();\n\n        let env_fp = context.vm.frame().environments.len() as u32;\n        context.vm.push_frame_with_stack(\n            CallFrame::new(\n                code_block,\n                None,\n                context.vm.frame().environments.clone(),\n                realm,\n            )\n            .with_env_fp(env_fp)\n            .with_flags(CallFrameFlags::EXIT_EARLY),\n            JsValue::undefined(),\n            JsValue::null(),\n        );\n\n        context.realm().resize_global_env();\n        let record = context.run();\n        context.vm.pop_frame();\n\n        let unfiltered = record.consume()?;\n\n        // 11. If IsCallable(reviver) is true, then\n        if let Some(obj) = args.get_or_undefined(1).as_callable() {\n            // a. Let root be ! OrdinaryObjectCreate(%Object.prototype%).\n            let root = JsObject::with_object_proto(context.intrinsics());\n\n            // b. Let rootName be the empty String.\n            // c. Perform ! CreateDataPropertyOrThrow(root, rootName, unfiltered).\n            root.create_data_property_or_throw(js_string!(), unfiltered, context)\n                .js_expect(\"CreateDataPropertyOrThrow should never throw here\")?;\n\n            // d. Return ? InternalizeJSONProperty(root, rootName, reviver).\n            Self::internalize_json_property(\n                &root,\n                js_string!(),\n                &obj,\n                source_tree.as_ref(),\n                context,\n            )\n        } else {\n            // 12. Else,\n            // a. Return unfiltered.\n            Ok(unfiltered)\n        }\n    }\n\n    /// `25.5.1.1 InternalizeJSONProperty ( holder, name, reviver )`\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-internalizejsonproperty\n    fn internalize_json_property(\n        holder: &JsObject,\n        name: JsString,\n        reviver: &JsObject,\n        source_node: Option<&JsonNode>,\n        context: &mut Context,\n    ) -> JsResult<JsValue> {\n        // 1. Let val be ? Get(holder, name).\n        let val = holder.get(name.clone(), context)?;\n\n        // 2. If Type(val) is Object, then\n        if let Some(obj) = val.as_object() {\n            // a. Let isArray be ? IsArray(val).\n            // b. If isArray is true, then\n            if obj.is_array_abstract()? {\n                // i. Let I be 0.\n                // ii. Let len be ? LengthOfArrayLike(val).\n                // iii. Repeat, while I < len,\n                let len = obj.length_of_array_like(context)? as i64;\n                let children = match source_node {\n                    Some(JsonNode::Array(children)) => Some(children),\n                    _ => None,\n                };\n                for i in 0..len {\n                    let child_node = children.and_then(|c| c.get(i as usize));\n                    // 1. Let prop be ! ToString(𝔽(I)).\n                    // 2. Let newElement be ? InternalizeJSONProperty(val, prop, reviver).\n                    let new_element = Self::internalize_json_property(\n                        &obj,\n                        i.into(),\n                        reviver,\n                        child_node,\n                        context,\n                    )?;\n\n                    // 3. If newElement is undefined, then\n                    if new_element.is_undefined() {\n                        // a. Perform ? val.[[Delete]](prop).\n                        obj.__delete__(\n                            &i.into(),\n                            &mut InternalMethodPropertyContext::new(context),\n                        )?;\n                    }\n                    // 4. Else,\n                    else {\n                        // a. Perform ? CreateDataProperty(val, prop, newElement).\n                        obj.create_data_property(i, new_element, context)?;\n                    }\n                }\n            }\n            // c. Else,\n            else {\n                // i. Let keys be ? EnumerableOwnPropertyNames(val, key).\n                let keys = obj.enumerable_own_property_names(PropertyNameKind::Key, context)?;\n                let entries = match source_node {\n                    Some(JsonNode::Object(entries)) => Some(entries),\n                    _ => None,\n                };\n\n                // ii. For each String P of keys, do\n                for p in keys {\n                    // This is safe, because EnumerableOwnPropertyNames with 'key' type only returns strings.\n                    let p = p\n                        .as_string()\n                        .js_expect(\"EnumerableOwnPropertyNames only returns strings\")?;\n\n                    let p_std = p.to_std_string_escaped();\n                    let child_node =\n                        entries.and_then(|e| e.iter().rfind(|(k, _)| k == &p_std).map(|(_, v)| v));\n\n                    // 1. Let newElement be ? InternalizeJSONProperty(val, P, reviver).\n                    let new_element = Self::internalize_json_property(\n                        &obj,\n                        p.clone(),\n                        reviver,\n                        child_node,\n                        context,\n                    )?;\n\n                    // 2. If newElement is undefined, then\n                    if new_element.is_undefined() {\n                        // a. Perform ? val.[[Delete]](P).\n                        obj.__delete__(\n                            &p.into(),\n                            &mut InternalMethodPropertyContext::new(context),\n                        )?;\n                    }\n                    // 3. Else,\n                    else {\n                        // a. Perform ? CreateDataProperty(val, P, newElement).\n                        obj.create_data_property(p, new_element, context)?;\n                    }\n                }\n            }\n        }\n\n        // Build the context object for the reviver call.\n        // For primitive JSON values: context = { source: \"<original text>\" }\n        // For objects/arrays or modified values: context = {} (no source property)\n        // Per spec, source is only provided when the value is still the same\n        // primitive that was produced by parsing the original JSON text.\n        let ctx_obj = JsObject::with_object_proto(context.intrinsics());\n        if let Some(JsonNode::Primitive(source_text)) = source_node {\n            // Check if the current value matches what the source text produces.\n            // If the reviver modified the value, it won't match and we skip source.\n            let value_matches = match source_text.as_str() {\n                \"null\" => val.is_null(),\n                \"true\" => val.as_boolean() == Some(true),\n                \"false\" => val.as_boolean() == Some(false),\n                s if s.starts_with('\"') => {\n                    // String: compare parsed string value\n                    if let Some(js_str) = val.as_string() {\n                        serde_json::from_str::<String>(s)\n                            .map(|parsed| js_str.to_std_string_escaped() == parsed)\n                            .unwrap_or(false)\n                    } else {\n                        false\n                    }\n                }\n                s => {\n                    // Number: compare parsed number value\n                    // Exact comparison is intentional: we want to detect if the\n                    // value was replaced by the reviver, not approximate equality.\n                    #[allow(clippy::float_cmp)]\n                    if let Some(n) = val.as_number() {\n                        s.parse::<f64>().map(|parsed| n == parsed).unwrap_or(false)\n                    } else {\n                        false\n                    }\n                }\n            };\n            if value_matches {\n                ctx_obj.create_data_property_or_throw(\n                    js_string!(\"source\"),\n                    JsValue::from(js_string!(source_text.as_str())),\n                    context,\n                )?;\n            }\n        }\n\n        // 3. Return ? Call(reviver, holder, « name, val, context »).\n        reviver.call(\n            &holder.clone().into(),\n            &[name.into(), val, ctx_obj.into()],\n            context,\n        )\n    }\n\n    /// `JSON.rawJSON ( text )`\n    ///\n    /// Creates a raw JSON object from the given text.\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-json.rawjson\n    pub(crate) fn raw_json(\n        _: &JsValue,\n        args: &[JsValue],\n        context: &mut Context,\n    ) -> JsResult<JsValue> {\n        // 1. Let jsonString be ? ToString(text).\n        let json_string = args.get_or_undefined(0).to_string(context)?;\n\n        // 2. Throw a SyntaxError exception if jsonString is the empty String, or if\n        //    either the first or last code unit of jsonString is any of 0x0009\n        //    (CHARACTER TABULATION), 0x000A (LINE FEED), 0x000D (CARRIAGE RETURN), or\n        //    0x0020 (SPACE).\n        let std_string = json_string\n            .to_std_string()\n            .map_err(|e| JsNativeError::syntax().with_message(e.to_string()))?;\n\n        if std_string.is_empty() {\n            return Err(JsNativeError::syntax()\n                .with_message(\"JSON.rawJSON text must not be the empty string\")\n                .into());\n        }\n\n        let first = std_string.as_bytes()[0];\n        let last = std_string.as_bytes()[std_string.len() - 1];\n        if matches!(first, b'\\t' | b'\\n' | b'\\r' | b' ')\n            || matches!(last, b'\\t' | b'\\n' | b'\\r' | b' ')\n        {\n            return Err(JsNativeError::syntax()\n                .with_message(\"JSON.rawJSON text must not start or end with whitespace\")\n                .into());\n        }\n\n        // 3. Parse StringToCodePoints(jsonString) as a JSON text as specified in ECMA-404.\n        //    Throw a SyntaxError exception if it is not a valid JSON text as defined in that\n        //    specification, or if its outermost value is an object or array.\n        let parsed: serde_json::Value = serde_json::from_str(&std_string)\n            .map_err(|e| JsNativeError::syntax().with_message(e.to_string()))?;\n\n        // Must not be an object or array\n        match parsed {\n            serde_json::Value::Object(_) | serde_json::Value::Array(_) => {\n                return Err(JsNativeError::syntax()\n                    .with_message(\"JSON.rawJSON text must not be an object or array\")\n                    .into());\n            }\n            _ => {}\n        }\n\n        // 3. Let internalSlotsList be « [[IsRawJSON]] ».\n        // 4. Let obj be OrdinaryObjectCreate(null, internalSlotsList).\n        let obj = JsObject::from_proto_and_data(None::<JsObject>, RawJson);\n\n        // 5. Perform ! CreateDataPropertyOrThrow(obj, \"rawJSON\", jsonString).\n        obj.create_data_property_or_throw(js_string!(\"rawJSON\"), json_string, context)\n            .js_expect(\"CreateDataPropertyOrThrow should never throw here\")?;\n\n        // 6. Perform ! SetIntegrityLevel(obj, frozen).\n        obj.set_integrity_level(IntegrityLevel::Frozen, context)\n            .js_expect(\"SetIntegrityLevel should never throw here\")?;\n\n        // 7. Return obj.\n        Ok(obj.into())\n    }\n\n    /// `JSON.isRawJSON ( O )`\n    ///\n    /// Checks if the given value is a raw JSON object.\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-json.israwjson\n    #[allow(clippy::unnecessary_wraps)]\n    pub(crate) fn is_raw_json(\n        _: &JsValue,\n        args: &[JsValue],\n        _context: &mut Context,\n    ) -> JsResult<JsValue> {\n        // 1. If Type(O) is Object and O has an [[IsRawJSON]] internal slot, return true.\n        // 2. Return false.\n        let value = args.get_or_undefined(0);\n        let result = value.as_object().is_some_and(|obj| obj.is::<RawJson>());\n        Ok(result.into())\n    }\n\n    /// `JSON.stringify( value[, replacer[, space]] )`\n    ///\n    /// This `JSON` method converts a JavaScript object or value to a JSON string.\n    ///\n    /// This method optionally replaces values if a `replacer` function is specified or\n    /// optionally including only the specified properties if a replacer array is specified.\n    ///\n    /// An optional `space` argument can be supplied of type `String` or `Number` that's used to insert\n    /// white space into the output JSON string for readability purposes.\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///  - [MDN documentation][mdn]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-json.stringify\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify\n    pub(crate) fn stringify(\n        _: &JsValue,\n        args: &[JsValue],\n        context: &mut Context,\n    ) -> JsResult<JsValue> {\n        // 1. Let stack be a new empty List.\n        let stack = Vec::new();\n\n        // 2. Let indent be the empty String.\n        let indent = js_string!();\n\n        // 3. Let PropertyList and ReplacerFunction be undefined.\n        let mut property_list = None;\n        let mut replacer_function = None;\n\n        let replacer = args.get_or_undefined(1);\n\n        // 4. If Type(replacer) is Object, then\n        if let Some(replacer_obj) = replacer.as_object() {\n            // a. If IsCallable(replacer) is true, then\n            if replacer_obj.is_callable() {\n                // i. Set ReplacerFunction to replacer.\n                replacer_function = Some(replacer_obj.clone());\n            // b. Else,\n            } else {\n                // i. Let isArray be ? IsArray(replacer).\n                // ii. If isArray is true, then\n                if replacer_obj.is_array_abstract()? {\n                    // 1. Set PropertyList to a new empty List.\n                    let mut property_set = indexmap::IndexSet::new();\n\n                    // 2. Let len be ? LengthOfArrayLike(replacer).\n                    let len = replacer_obj.length_of_array_like(context)?;\n\n                    // 3. Let k be 0.\n                    let mut k = 0;\n\n                    // 4. Repeat, while k < len,\n                    while k < len {\n                        // a. Let prop be ! ToString(𝔽(k)).\n                        // b. Let v be ? Get(replacer, prop).\n                        let v = replacer_obj.get(k, context)?;\n\n                        // c. Let item be undefined.\n                        // d. If Type(v) is String, set item to v.\n                        // e. Else if Type(v) is Number, set item to ! ToString(v).\n                        // f. Else if Type(v) is Object, then\n                        // g. If item is not undefined and item is not currently an element of PropertyList, then\n                        // i. Append item to the end of PropertyList.\n                        if let Some(s) = v.as_string() {\n                            property_set.insert(s);\n                        } else if v.is_number() {\n                            property_set.insert(\n                                v.to_string(context)\n                                    .js_expect(\"ToString cannot fail on number value\")?,\n                            );\n                        } else if let Some(obj) = v.as_object()\n                            && (obj.is::<JsString>() || obj.is::<f64>())\n                        {\n                            // i. If v has a [[StringData]] or [[NumberData]] internal slot, set item to ? ToString(v).\n                            property_set.insert(v.to_string(context)?);\n                        }\n\n                        // h. Set k to k + 1.\n                        k += 1;\n                    }\n                    property_list = Some(property_set.into_iter().collect());\n                }\n            }\n        }\n\n        let mut space = args.get_or_undefined(2).clone();\n\n        // 5. If Type(space) is Object, then\n        if let Some(space_obj) = space.as_object() {\n            // a. If space has a [[NumberData]] internal slot, then\n            if space_obj.is::<f64>() {\n                // i. Set space to ? ToNumber(space).\n                space = space.to_number(context)?.into();\n            }\n            // b. Else if space has a [[StringData]] internal slot, then\n            else if space_obj.is::<JsString>() {\n                // i. Set space to ? ToString(space).\n                space = space.to_string(context)?.into();\n            }\n        }\n\n        // 6. If Type(space) is Number, then\n        let gap = if space.is_number() {\n            // a. Let spaceMV be ! ToIntegerOrInfinity(space).\n            // b. Set spaceMV to min(10, spaceMV).\n            // c. If spaceMV < 1, let gap be the empty String; otherwise let gap be the String value containing spaceMV occurrences of the code unit 0x0020 (SPACE).\n            match space\n                .to_integer_or_infinity(context)\n                .js_expect(\"ToIntegerOrInfinity cannot fail on number\")?\n            {\n                IntegerOrInfinity::PositiveInfinity => js_string!(\"          \"),\n                IntegerOrInfinity::NegativeInfinity => js_string!(),\n                IntegerOrInfinity::Integer(i) if i < 1 => js_string!(),\n                IntegerOrInfinity::Integer(i) => {\n                    let mut s = String::new();\n                    let i = std::cmp::min(10, i);\n                    for _ in 0..i {\n                        s.push(' ');\n                    }\n                    s.into()\n                }\n            }\n        // 7. Else if Type(space) is String, then\n        } else if let Some(s) = space.as_string() {\n            // a. If the length of space is 10 or less, let gap be space; otherwise let gap be the substring of space from 0 to 10.\n            s.get(..10).unwrap_or(s)\n        // 8. Else,\n        } else {\n            // a. Let gap be the empty String.\n            js_string!()\n        };\n\n        // 9. Let wrapper be ! OrdinaryObjectCreate(%Object.prototype%).\n        let wrapper = JsObject::with_object_proto(context.intrinsics());\n\n        // 10. Perform ! CreateDataPropertyOrThrow(wrapper, the empty String, value).\n        wrapper\n            .create_data_property_or_throw(js_string!(), args.get_or_undefined(0).clone(), context)\n            .js_expect(\"CreateDataPropertyOrThrow should never fail here\")?;\n\n        // 11. Let state be the Record { [[ReplacerFunction]]: ReplacerFunction, [[Stack]]: stack, [[Indent]]: indent, [[Gap]]: gap, [[PropertyList]]: PropertyList }.\n        let mut state = StateRecord {\n            replacer_function,\n            stack,\n            indent,\n            gap,\n            property_list,\n        };\n\n        // 12. Return ? SerializeJSONProperty(state, the empty String, wrapper).\n        Ok(\n            Self::serialize_json_property(&mut state, js_string!(), &wrapper, context)?\n                .map(Into::into)\n                .unwrap_or_default(),\n        )\n    }\n\n    /// `25.5.2.1 SerializeJSONProperty ( state, key, holder )`\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-serializejsonproperty\n    fn serialize_json_property(\n        state: &mut StateRecord,\n        key: JsString,\n        holder: &JsObject,\n        context: &mut Context,\n    ) -> JsResult<Option<JsString>> {\n        // 1. Let value be ? Get(holder, key).\n        let mut value = holder.get(key.clone(), context)?;\n\n        // 2. If Type(value) is Object or BigInt, then\n        if value.is_object() || value.is_bigint() {\n            // a. Let toJSON be ? GetV(value, \"toJSON\").\n            let to_json = value.get_v(js_string!(\"toJSON\"), context)?;\n\n            // b. If IsCallable(toJSON) is true, then\n            if let Some(obj) = to_json.as_callable() {\n                // i. Set value to ? Call(toJSON, value, « key »).\n                value = obj.call(&value, &[key.clone().into()], context)?;\n            }\n        }\n\n        // 3. If state.[[ReplacerFunction]] is not undefined, then\n        if let Some(obj) = &state.replacer_function {\n            // a. Set value to ? Call(state.[[ReplacerFunction]], holder, « key, value »).\n            value = obj.call(&holder.clone().into(), &[key.into(), value], context)?;\n        }\n\n        // 4. If Type(value) is Object, then\n        if let Some(obj) = value.as_object() {\n            // a. If value has a [[NumberData]] internal slot, then\n            if obj.is::<f64>() {\n                // i. Set value to ? ToNumber(value).\n                value = value.to_number(context)?.into();\n            }\n            // b. Else if value has a [[StringData]] internal slot, then\n            else if obj.is::<JsString>() {\n                // i. Set value to ? ToString(value).\n                value = value.to_string(context)?.into();\n            }\n            // c. Else if value has a [[BooleanData]] internal slot, then\n            else if let Some(boolean) = obj.downcast_ref::<bool>() {\n                // i. Set value to value.[[BooleanData]].\n                value = (*boolean).into();\n            }\n            // d. Else if value has a [[BigIntData]] internal slot, then\n            else if let Some(bigint) = obj.downcast_ref::<JsBigInt>() {\n                // i. Set value to value.[[BigIntData]].\n                value = bigint.clone().into();\n            }\n            // e. Else if value has a [[IsRawJSON]] internal slot, then\n            else if obj.is::<RawJson>() {\n                // i. Return the value of the \"rawJSON\" property of value.\n                let raw = obj.get(js_string!(\"rawJSON\"), context)?;\n                return Ok(raw.as_string());\n            }\n        }\n\n        // 5. If value is null, return \"null\".\n        if value.is_null() {\n            return Ok(Some(js_string!(\"null\")));\n        }\n\n        // 6. If value is true, return \"true\".\n        // 7. If value is false, return \"false\".\n        if value.is_boolean() {\n            return Ok(Some(js_string!(if value.to_boolean() {\n                \"true\"\n            } else {\n                \"false\"\n            })));\n        }\n\n        // 8. If Type(value) is String, return QuoteJSONString(value).\n        if let Some(s) = value.as_string() {\n            return Ok(Some(Self::quote_json_string(&s)));\n        }\n\n        // 9. If Type(value) is Number, then\n        if let Some(n) = value.as_number() {\n            // a. If value is finite, return ! ToString(value).\n            if n.is_finite() {\n                return Ok(Some(\n                    value\n                        .to_string(context)\n                        .js_expect(\"ToString should never fail here\")?,\n                ));\n            }\n\n            // b. Return \"null\".\n            return Ok(Some(js_string!(\"null\")));\n        }\n\n        // 10. If Type(value) is BigInt, throw a TypeError exception.\n        if value.is_bigint() {\n            return Err(JsNativeError::typ()\n                .with_message(\"cannot serialize bigint to JSON\")\n                .into());\n        }\n\n        // 11. If Type(value) is Object and IsCallable(value) is false, then\n        if let Some(obj) = value.as_object()\n            && !obj.is_callable()\n        {\n            // a. Let isArray be ? IsArray(value).\n            // b. If isArray is true, return ? SerializeJSONArray(state, value).\n            // c. Return ? SerializeJSONObject(state, value).\n            return if obj.is_array_abstract()? {\n                Ok(Some(Self::serialize_json_array(state, &obj, context)?))\n            } else {\n                Ok(Some(Self::serialize_json_object(state, &obj, context)?))\n            };\n        }\n\n        // 12. Return undefined.\n        Ok(None)\n    }\n\n    /// `25.5.2.2 QuoteJSONString ( value )`\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-quotejsonstring\n    fn quote_json_string(value: &JsString) -> JsString {\n        let mut buf = [0; 2];\n        // 1. Let product be the String value consisting solely of the code unit 0x0022 (QUOTATION MARK).\n        let mut product = Vec::with_capacity(value.len() + 2);\n        product.push('\"' as u16);\n\n        // 2. For each code point C of ! StringToCodePoints(value), do\n        for code_point in value.code_points() {\n            match code_point {\n                // a. If C is listed in the \"Code Point\" column of Table 73, then\n                // i. Set product to the string-concatenation of product and the\n                // escape sequence for C as specified in the \"Escape Sequence\"\n                // column of the corresponding row.\n                CodePoint::Unicode('\\u{0008}') => product.extend_from_slice(utf16!(r\"\\b\")),\n                CodePoint::Unicode('\\u{0009}') => product.extend_from_slice(utf16!(r\"\\t\")),\n                CodePoint::Unicode('\\u{000A}') => product.extend_from_slice(utf16!(r\"\\n\")),\n                CodePoint::Unicode('\\u{000C}') => product.extend_from_slice(utf16!(r\"\\f\")),\n                CodePoint::Unicode('\\u{000D}') => product.extend_from_slice(utf16!(r\"\\r\")),\n                CodePoint::Unicode('\\u{0022}') => product.extend_from_slice(utf16!(r#\"\\\"\"#)),\n                CodePoint::Unicode('\\u{005C}') => product.extend_from_slice(utf16!(r\"\\\\\")),\n                // b. Else if C has a numeric value less than 0x0020 (SPACE), or\n                // if C has the same numeric value as a leading surrogate or\n                // trailing surrogate, then\n                //     i. Let unit be the code unit whose numeric value is that\n                //     of C.\n                //     ii. Set product to the string-concatenation of product\n                //     and UnicodeEscape(unit).\n                CodePoint::Unicode(c) if c < '\\u{0020}' => {\n                    let val = c as u16;\n                    product.extend_from_slice(&[\n                        '\\\\' as u16,\n                        'u' as u16,\n                        to_hex_digit(val >> 12),\n                        to_hex_digit((val >> 8) & 0xF),\n                        to_hex_digit((val >> 4) & 0xF),\n                        to_hex_digit(val & 0xF),\n                    ]);\n                }\n                CodePoint::UnpairedSurrogate(surr) => {\n                    product.extend_from_slice(&[\n                        '\\\\' as u16,\n                        'u' as u16,\n                        to_hex_digit(surr >> 12),\n                        to_hex_digit((surr >> 8) & 0xF),\n                        to_hex_digit((surr >> 4) & 0xF),\n                        to_hex_digit(surr & 0xF),\n                    ]);\n                }\n                // c. Else,\n                CodePoint::Unicode(c) => {\n                    // i. Set product to the string-concatenation of product and ! UTF16EncodeCodePoint(C).\n                    product.extend_from_slice(c.encode_utf16(&mut buf));\n                }\n            }\n        }\n\n        // 3. Set product to the string-concatenation of product and the code unit 0x0022 (QUOTATION MARK).\n        product.push('\"' as u16);\n\n        // 4. Return product.\n        js_string!(&product[..])\n    }\n\n    /// `25.5.2.4 SerializeJSONObject ( state, value )`\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-serializejsonobject\n    fn serialize_json_object(\n        state: &mut StateRecord,\n        value: &JsObject,\n        context: &mut Context,\n    ) -> JsResult<JsString> {\n        // 1. If state.[[Stack]] contains value, throw a TypeError exception because the structure is cyclical.\n        if state.stack.contains(value) {\n            return Err(JsNativeError::typ()\n                .with_message(\"cyclic object value\")\n                .into());\n        }\n\n        // 2. Append value to state.[[Stack]].\n        state.stack.push(value.clone());\n\n        // 3. Let stepback be state.[[Indent]].\n        let stepback = state.indent.clone();\n\n        // 4. Set state.[[Indent]] to the string-concatenation of state.[[Indent]] and state.[[Gap]].\n        state.indent = js_string!(&state.indent, &state.gap);\n\n        // 5. If state.[[PropertyList]] is not undefined, then\n        let k = if let Some(p) = &state.property_list {\n            // a. Let K be state.[[PropertyList]].\n            p.clone()\n        // 6. Else,\n        } else {\n            // a. Let K be ? EnumerableOwnPropertyNames(value, key).\n            let keys = value.enumerable_own_property_names(PropertyNameKind::Key, context)?;\n            // Unwrap is safe, because EnumerableOwnPropertyNames with kind \"key\" only returns string values.\n            keys.iter()\n                .map(|v| {\n                    v.to_string(context)\n                        .js_expect(\"EnumerableOwnPropertyNames only returns strings\")\n                })\n                .collect::<Result<Vec<_>, _>>()?\n        };\n\n        // 7. Let partial be a new empty List.\n        let mut partial = Vec::with_capacity(k.len());\n\n        // 8. For each element P of K, do\n        for p in &k {\n            // a. Let strP be ? SerializeJSONProperty(state, P, value).\n            let str_p = Self::serialize_json_property(state, p.clone(), value, context)?;\n\n            // b. If strP is not undefined, then\n            if let Some(str_p) = str_p {\n                // i. Let member be QuoteJSONString(P).\n                let mut member = Self::quote_json_string(p).iter().collect::<Vec<_>>();\n\n                // ii. Set member to the string-concatenation of member and \":\".\n                member.push(':' as u16);\n\n                // iii. If state.[[Gap]] is not the empty String, then\n                if !state.gap.is_empty() {\n                    // 1. Set member to the string-concatenation of member and the code unit 0x0020 (SPACE).\n                    member.push(' ' as u16);\n                }\n\n                // iv. Set member to the string-concatenation of member and strP.\n                member.extend(str_p.iter());\n\n                // v. Append member to partial.\n                partial.push(member);\n            }\n        }\n\n        // 9. If partial is empty, then\n        let r#final = if partial.is_empty() {\n            // a. Let final be \"{}\".\n            js_string!(\"{}\")\n        // 10. Else,\n        } else {\n            // a. If state.[[Gap]] is the empty String, then\n            if state.gap.is_empty() {\n                // i. Let properties be the String value formed by concatenating all the element Strings of partial\n                //    with each adjacent pair of Strings separated with the code unit 0x002C (COMMA).\n                //    A comma is not inserted either before the first String or after the last String.\n                // ii. Let final be the string-concatenation of \"{\", properties, and \"}\".\n                let separator = utf16!(\",\");\n                let result = once(utf16!(\"{\"))\n                    .chain(Itertools::intersperse(\n                        partial.iter().map(Vec::as_slice),\n                        separator,\n                    ))\n                    .chain(once(utf16!(\"}\")))\n                    .flatten()\n                    .copied()\n                    .collect::<Vec<_>>();\n                js_string!(&result[..])\n            // b. Else,\n            } else {\n                // i. Let separator be the string-concatenation of the code unit 0x002C (COMMA),\n                //    the code unit 0x000A (LINE FEED), and state.[[Indent]].\n                let mut separator = utf16!(\",\\n\").to_vec();\n                separator.extend(state.indent.iter());\n                // ii. Let properties be the String value formed by concatenating all the element Strings of partial\n                //     with each adjacent pair of Strings separated with separator.\n                //     The separator String is not inserted either before the first String or after the last String.\n                // iii. Let final be the string-concatenation of \"{\", the code\n                //      unit 0x000A (LINE FEED), state.[[Indent]], properties,\n                //      the code unit 0x000A (LINE FEED), stepback, and \"}\".\n                let indent_vec = state.indent.to_vec();\n                let stepback_vec = stepback.to_vec();\n                let result = [utf16!(\"{\\n\"), &indent_vec[..]]\n                    .into_iter()\n                    .chain(Itertools::intersperse(\n                        partial.iter().map(Vec::as_slice),\n                        &separator,\n                    ))\n                    .chain([utf16!(\"\\n\"), &stepback_vec[..], utf16!(\"}\")])\n                    .flatten()\n                    .copied()\n                    .collect::<Vec<_>>();\n                js_string!(&result[..])\n            }\n        };\n\n        // 11. Remove the last element of state.[[Stack]].\n        state.stack.pop();\n\n        // 12. Set state.[[Indent]] to stepback.\n        state.indent = stepback;\n\n        // 13. Return final.\n        Ok(r#final)\n    }\n\n    /// `25.5.2.5 SerializeJSONArray ( state, value )`\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-serializejsonarray\n    fn serialize_json_array(\n        state: &mut StateRecord,\n        value: &JsObject,\n        context: &mut Context,\n    ) -> JsResult<JsString> {\n        // 1. If state.[[Stack]] contains value, throw a TypeError exception because the structure is cyclical.\n        if state.stack.contains(value) {\n            return Err(JsNativeError::typ()\n                .with_message(\"cyclic object value\")\n                .into());\n        }\n\n        // 2. Append value to state.[[Stack]].\n        state.stack.push(value.clone());\n\n        // 3. Let stepback be state.[[Indent]].\n        let stepback = state.indent.clone();\n\n        // 4. Set state.[[Indent]] to the string-concatenation of state.[[Indent]] and state.[[Gap]].\n        state.indent = js_string!(&state.indent, &state.gap);\n\n        // 6. Let len be ? LengthOfArrayLike(value).\n        let len = value.length_of_array_like(context)?;\n\n        // 5. Let partial be a new empty List.\n        let mut partial = Vec::with_capacity(len as usize);\n\n        // 7. Let index be 0.\n        let mut index = 0;\n\n        // 8. Repeat, while index < len,\n        while index < len {\n            // a. Let strP be ? SerializeJSONProperty(state, ! ToString(𝔽(index)), value).\n            let str_p = Self::serialize_json_property(state, index.into(), value, context)?;\n\n            // b. If strP is undefined, then\n            if let Some(str_p) = str_p {\n                // i. Append strP to partial.\n                partial.push(Cow::Owned(str_p.iter().collect::<_>()));\n            // c. Else,\n            } else {\n                // i. Append \"null\" to partial.\n                partial.push(Cow::Borrowed(utf16!(\"null\")));\n            }\n\n            // d. Set index to index + 1.\n            index += 1;\n        }\n\n        // 9. If partial is empty, then\n        let r#final = if partial.is_empty() {\n            // a. Let final be \"[]\".\n            js_string!(\"[]\")\n        // 10. Else,\n        } else {\n            // a. If state.[[Gap]] is the empty String, then\n            if state.gap.is_empty() {\n                // i. Let properties be the String value formed by concatenating all the element Strings of partial\n                //    with each adjacent pair of Strings separated with the code unit 0x002C (COMMA).\n                //    A comma is not inserted either before the first String or after the last String.\n                // ii. Let final be the string-concatenation of \"[\", properties, and \"]\".\n                let separator = utf16!(\",\");\n                let result = once(utf16!(\"[\"))\n                    .chain(Itertools::intersperse(\n                        partial.iter().map(Cow::as_ref),\n                        separator,\n                    ))\n                    .chain(once(utf16!(\"]\")))\n                    .flatten()\n                    .copied()\n                    .collect::<Vec<_>>();\n                js_string!(&result[..])\n            // b. Else,\n            } else {\n                // i. Let separator be the string-concatenation of the code unit 0x002C (COMMA),\n                //    the code unit 0x000A (LINE FEED), and state.[[Indent]].\n                let mut separator = utf16!(\",\\n\").to_vec();\n                separator.extend(state.indent.iter());\n                // ii. Let properties be the String value formed by concatenating all the element Strings of partial\n                //     with each adjacent pair of Strings separated with separator.\n                //     The separator String is not inserted either before the first String or after the last String.\n                // iii. Let final be the string-concatenation of \"[\", the code unit 0x000A (LINE FEED), state.[[Indent]], properties, the code unit 0x000A (LINE FEED), stepback, and \"]\".\n                let indent_vec = state.indent.to_vec();\n                let stepback_vec = stepback.to_vec();\n                let result = [utf16!(\"[\\n\"), &indent_vec[..]]\n                    .into_iter()\n                    .chain(Itertools::intersperse(\n                        partial.iter().map(Cow::as_ref),\n                        &separator,\n                    ))\n                    .chain([utf16!(\"\\n\"), &stepback_vec[..], utf16!(\"]\")])\n                    .flatten()\n                    .copied()\n                    .collect::<Vec<_>>();\n                js_string!(&result[..])\n            }\n        };\n\n        // 11. Remove the last element of state.[[Stack]].\n        state.stack.pop();\n\n        // 12. Set state.[[Indent]] to stepback.\n        state.indent = stepback;\n\n        // 13. Return final.\n        Ok(r#final)\n    }\n}\n\nstruct StateRecord {\n    replacer_function: Option<JsObject>,\n    stack: Vec<JsObject>,\n    indent: JsString,\n    gap: JsString,\n    property_list: Option<Vec<JsString>>,\n}\n"
  },
  {
    "path": "core/engine/src/builtins/json/tests.rs",
    "content": "use boa_macros::js_str;\nuse indoc::indoc;\n\nuse crate::{JsNativeErrorKind, JsValue, TestAction, js_string, run_test_actions};\n\n#[test]\nfn json_sanity() {\n    run_test_actions([\n        TestAction::assert_eq(r#\"JSON.parse('{\"aaa\":\"bbb\"}').aaa\"#, js_str!(\"bbb\")),\n        TestAction::assert_eq(\n            r#\"JSON.stringify({aaa: 'bbb'})\"#,\n            js_string!(r#\"{\"aaa\":\"bbb\"}\"#),\n        ),\n    ]);\n}\n\n#[test]\nfn json_stringify_remove_undefined_values_from_objects() {\n    run_test_actions([TestAction::assert_eq(\n        r#\"JSON.stringify({ aaa: undefined, bbb: 'ccc' })\"#,\n        js_string!(r#\"{\"bbb\":\"ccc\"}\"#),\n    )]);\n}\n\n#[test]\nfn json_stringify_remove_function_values_from_objects() {\n    run_test_actions([TestAction::assert_eq(\n        r#\"JSON.stringify({ aaa: () => {}, bbb: 'ccc' })\"#,\n        js_string!(r#\"{\"bbb\":\"ccc\"}\"#),\n    )]);\n}\n\n#[test]\nfn json_stringify_remove_symbols_from_objects() {\n    run_test_actions([TestAction::assert_eq(\n        r#\"JSON.stringify({ aaa: Symbol(), bbb: 'ccc' })\"#,\n        js_string!(r#\"{\"bbb\":\"ccc\"}\"#),\n    )]);\n}\n\n#[test]\nfn json_stringify_replacer_array_strings() {\n    run_test_actions([TestAction::assert_eq(\n        r#\"JSON.stringify({aaa: 'bbb', bbb: 'ccc', ccc: 'ddd'}, ['aaa', 'bbb'])\"#,\n        js_string!(r#\"{\"aaa\":\"bbb\",\"bbb\":\"ccc\"}\"#),\n    )]);\n}\n\n#[test]\nfn json_stringify_replacer_array_numbers() {\n    run_test_actions([TestAction::assert_eq(\n        r#\"JSON.stringify({ 0: 'aaa', 1: 'bbb', 2: 'ccc'}, [1, 2])\"#,\n        js_string!(r#\"{\"1\":\"bbb\",\"2\":\"ccc\"}\"#),\n    )]);\n}\n\n#[test]\nfn json_stringify_replacer_function() {\n    run_test_actions([TestAction::assert_eq(\n        indoc! {r#\"\n            JSON.stringify({ aaa: 1, bbb: 2}, (key, value) => {\n                if (key === 'aaa') {\n                    return undefined;\n                }\n\n                return value;\n            })\n        \"#},\n        js_string!(r#\"{\"bbb\":2}\"#),\n    )]);\n}\n\n#[test]\nfn json_stringify_arrays() {\n    run_test_actions([TestAction::assert_eq(\n        \"JSON.stringify(['a', 'b'])\",\n        js_string!(r#\"[\"a\",\"b\"]\"#),\n    )]);\n}\n\n#[test]\nfn json_stringify_object_array() {\n    run_test_actions([TestAction::assert_eq(\n        \"JSON.stringify([{a: 'b'}, {b: 'c'}])\",\n        js_string!(r#\"[{\"a\":\"b\"},{\"b\":\"c\"}]\"#),\n    )]);\n}\n\n#[test]\nfn json_stringify_array_converts_undefined_to_null() {\n    run_test_actions([TestAction::assert_eq(\n        \"JSON.stringify([undefined])\",\n        js_str!(\"[null]\"),\n    )]);\n}\n\n#[test]\nfn json_stringify_array_converts_function_to_null() {\n    run_test_actions([TestAction::assert_eq(\n        \"JSON.stringify([() => {}])\",\n        js_str!(\"[null]\"),\n    )]);\n}\n\n#[test]\nfn json_stringify_array_converts_symbol_to_null() {\n    run_test_actions([TestAction::assert_eq(\n        \"JSON.stringify([Symbol()])\",\n        js_str!(\"[null]\"),\n    )]);\n}\n#[test]\nfn json_stringify_function_replacer_propagate_error() {\n    run_test_actions([TestAction::assert_opaque_error(\n        \"JSON.stringify({x: 1}, (key, value) => { throw 1 })\",\n        1,\n    )]);\n}\n\n#[test]\nfn json_stringify_function() {\n    run_test_actions([TestAction::assert_eq(\n        \"JSON.stringify(() => {})\",\n        JsValue::undefined(),\n    )]);\n}\n\n#[test]\nfn json_stringify_undefined() {\n    run_test_actions([TestAction::assert_eq(\n        \"JSON.stringify(undefined)\",\n        JsValue::undefined(),\n    )]);\n}\n\n#[test]\nfn json_stringify_symbol() {\n    run_test_actions([TestAction::assert_eq(\n        \"JSON.stringify(Symbol())\",\n        JsValue::undefined(),\n    )]);\n}\n\n#[test]\nfn json_stringify_no_args() {\n    run_test_actions([TestAction::assert_eq(\n        \"JSON.stringify()\",\n        JsValue::undefined(),\n    )]);\n}\n\n#[test]\nfn json_stringify_fractional_numbers() {\n    run_test_actions([TestAction::assert_eq(\"JSON.stringify(1.2)\", js_str!(\"1.2\"))]);\n}\n\n#[test]\nfn json_stringify_pretty_print() {\n    run_test_actions([TestAction::assert_eq(\n        r#\"JSON.stringify({a: \"b\", b: \"c\"}, undefined, 4)\"#,\n        js_string!(indoc! {r#\"\n            {\n                \"a\": \"b\",\n                \"b\": \"c\"\n            }\"#\n        }),\n    )]);\n}\n\n#[test]\nfn json_stringify_pretty_print_four_spaces() {\n    run_test_actions([TestAction::assert_eq(\n        r#\"JSON.stringify({a: \"b\", b: \"c\"}, undefined, 4.3)\"#,\n        js_string!(indoc! {r#\"\n            {\n                \"a\": \"b\",\n                \"b\": \"c\"\n            }\"#\n        }),\n    )]);\n}\n\n#[test]\nfn json_stringify_pretty_print_twenty_spaces() {\n    run_test_actions([TestAction::assert_eq(\n        r#\"JSON.stringify({a: \"b\", b: \"c\"}, undefined, 20)\"#,\n        js_string!(indoc! {r#\"\n            {\n                      \"a\": \"b\",\n                      \"b\": \"c\"\n            }\"#\n        }),\n    )]);\n}\n\n#[test]\nfn json_stringify_pretty_print_with_number_object() {\n    run_test_actions([TestAction::assert_eq(\n        r#\"JSON.stringify({a: \"b\", b: \"c\"}, undefined, new Number(10))\"#,\n        js_string!(indoc! {r#\"\n            {\n                      \"a\": \"b\",\n                      \"b\": \"c\"\n            }\"#\n        }),\n    )]);\n}\n\n#[test]\nfn json_stringify_pretty_print_bad_space_argument() {\n    run_test_actions([TestAction::assert_eq(\n        r#\"JSON.stringify({a: \"b\", b: \"c\"}, undefined, [])\"#,\n        js_string!(r#\"{\"a\":\"b\",\"b\":\"c\"}\"#),\n    )]);\n}\n\n#[test]\nfn json_stringify_pretty_print_with_too_long_string() {\n    run_test_actions([TestAction::assert_eq(\n        r#\"JSON.stringify({a: \"b\", b: \"c\"}, undefined, \"abcdefghijklmn\")\"#,\n        js_string!(indoc! {r#\"\n            {\n            abcdefghij\"a\": \"b\",\n            abcdefghij\"b\": \"c\"\n            }\"#\n        }),\n    )]);\n}\n\n#[test]\nfn json_stringify_pretty_print_with_string_object() {\n    run_test_actions([TestAction::assert_eq(\n        r#\"JSON.stringify({a: \"b\", b: \"c\"}, undefined, new String(\"abcd\"))\"#,\n        js_string!(indoc! {r#\"\n            {\n            abcd\"a\": \"b\",\n            abcd\"b\": \"c\"\n            }\"#\n        }),\n    )]);\n}\n\n#[test]\nfn json_parse_array_with_reviver() {\n    run_test_actions([\n        TestAction::run_harness(),\n        TestAction::run(indoc! {r#\"\n                function reviver(k, v){\n                    if (typeof v == 'number') {\n                        return v * 2;\n                    } else {\n                        return v;\n                    }\n                }\n            \"#}),\n        TestAction::assert(\"arrayEquals(JSON.parse('[1,2,3,4]', reviver), [2,4,6,8])\"),\n    ]);\n}\n\n#[test]\nfn json_parse_object_with_reviver() {\n    run_test_actions([\n        TestAction::run(indoc! {r#\"\n                var jsonString = JSON.stringify({\n                    firstname: \"boa\",\n                    lastname: \"snake\"\n                });\n\n                function dataReviver(key, value) {\n                    if (key == 'lastname') {\n                        return 'interpreter';\n                    } else {\n                        return value;\n                    }\n                }\n\n                var jsonObj = JSON.parse(jsonString, dataReviver);\n            \"#}),\n        TestAction::assert_eq(\"jsonObj.firstname\", js_str!(\"boa\")),\n        TestAction::assert_eq(\"jsonObj.lastname\", js_str!(\"interpreter\")),\n    ]);\n}\n\n#[test]\nfn json_parse_sets_prototypes() {\n    run_test_actions([\n        TestAction::run(indoc! {r#\"\n                const jsonString = \"{\\\"ob\\\":{\\\"ject\\\":1},\\\"arr\\\": [0,1]}\";\n                const jsonObj = JSON.parse(jsonString);\n            \"#}),\n        TestAction::assert(\"Object.getPrototypeOf(jsonObj.ob) === Object.prototype\"),\n        TestAction::assert(\"Object.getPrototypeOf(jsonObj.arr) === Array.prototype\"),\n    ]);\n}\n\n#[test]\nfn json_fields_should_be_enumerable() {\n    run_test_actions([\n        TestAction::assert(indoc! {r#\"\n                var a = JSON.parse('{\"x\":0}');\n                a.propertyIsEnumerable('x')\n            \"#}),\n        TestAction::assert(indoc! {r#\"\n                var b = JSON.parse('[0, 1]');\n                b.propertyIsEnumerable('0');\n            \"#}),\n    ]);\n}\n\n#[test]\nfn json_parse_with_no_args_throws_syntax_error() {\n    run_test_actions([TestAction::assert_native_error(\n        \"JSON.parse();\",\n        JsNativeErrorKind::Syntax,\n        \"expected value at line 1 column 1\",\n    )]);\n}\n"
  },
  {
    "path": "core/engine/src/builtins/map/map_iterator.rs",
    "content": "//! This module implements the `MapIterator` object.\n//!\n//! More information:\n//!  - [ECMAScript reference][spec]\n//!\n//! [spec]: https://tc39.es/ecma262/#sec-map-iterator-objects\n\nuse super::ordered_map::OrderedMap;\nuse crate::{\n    Context, JsData, JsResult,\n    builtins::{\n        Array, BuiltInBuilder, IntrinsicObject, JsValue, iterable::create_iter_result_object,\n    },\n    context::intrinsics::Intrinsics,\n    error::JsNativeError,\n    js_string,\n    object::JsObject,\n    property::{Attribute, PropertyNameKind},\n    realm::Realm,\n    symbol::JsSymbol,\n};\nuse boa_gc::{Finalize, Trace};\n\n#[derive(Debug, Trace)]\nstruct MapIteratorLock(JsObject<OrderedMap<JsValue>>);\n\nimpl MapIteratorLock {\n    fn new(js_object: JsObject<OrderedMap<JsValue>>) -> Self {\n        js_object.borrow_mut().data_mut().lock();\n        Self(js_object)\n    }\n}\n\nimpl Finalize for MapIteratorLock {\n    fn finalize(&self) {\n        self.0.borrow_mut().data_mut().unlock();\n    }\n}\n\n/// The Map Iterator object represents an iteration over a map. It implements the iterator protocol.\n///\n/// More information:\n///  - [ECMAScript reference][spec]\n///\n/// [spec]: https://tc39.es/ecma262/#sec-map-iterator-objects\n#[derive(Debug, Finalize, Trace, JsData)]\npub(crate) struct MapIterator {\n    iterated_map: Option<MapIteratorLock>,\n    next_index: usize,\n    #[unsafe_ignore_trace]\n    iteration_kind: PropertyNameKind,\n}\n\nimpl IntrinsicObject for MapIterator {\n    fn init(realm: &Realm) {\n        BuiltInBuilder::with_intrinsic::<Self>(realm)\n            .prototype(\n                realm\n                    .intrinsics()\n                    .objects()\n                    .iterator_prototypes()\n                    .iterator(),\n            )\n            .static_method(Self::next, js_string!(\"next\"), 0)\n            .static_property(\n                JsSymbol::to_string_tag(),\n                js_string!(\"Map Iterator\"),\n                Attribute::CONFIGURABLE,\n            )\n            .build();\n    }\n\n    fn get(intrinsics: &Intrinsics) -> JsObject {\n        intrinsics.objects().iterator_prototypes().map()\n    }\n}\n\nimpl MapIterator {\n    /// Abstract operation `CreateMapIterator( map, kind )`\n    ///\n    /// Creates a new iterator over the given map.\n    ///\n    /// More information:\n    ///  - [ECMA reference][spec]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-createmapiterator\n    pub(crate) fn create_map_iterator(\n        map: JsObject<OrderedMap<JsValue>>,\n        kind: PropertyNameKind,\n        context: &mut Context,\n    ) -> JsValue {\n        let iter = Self {\n            iterated_map: Some(MapIteratorLock::new(map)),\n            next_index: 0,\n            iteration_kind: kind,\n        };\n        let map_iterator = JsObject::from_proto_and_data_with_shared_shape(\n            context.root_shape(),\n            context.intrinsics().objects().iterator_prototypes().map(),\n            iter,\n        );\n        map_iterator.into()\n    }\n\n    /// %MapIteratorPrototype%.next( )\n    ///\n    /// Advances the iterator and gets the next result in the map.\n    ///\n    /// More information:\n    ///  - [ECMA reference][spec]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-%mapiteratorprototype%.next\n    pub(crate) fn next(this: &JsValue, _: &[JsValue], context: &mut Context) -> JsResult<JsValue> {\n        let object = this.as_object();\n        let mut map_iterator = object\n            .as_ref()\n            .and_then(JsObject::downcast_mut::<Self>)\n            .ok_or_else(|| JsNativeError::typ().with_message(\"`this` is not a MapIterator\"))?;\n\n        let item_kind = map_iterator.iteration_kind;\n\n        if let Some(obj) = map_iterator.iterated_map.take() {\n            let e = {\n                let mut entries = obj.0.borrow_mut();\n                let entries = entries.data_mut();\n                let len = entries.full_len();\n                loop {\n                    let element = entries\n                        .get_index(map_iterator.next_index)\n                        .map(|(v, k)| (v.clone(), k.clone()));\n                    map_iterator.next_index += 1;\n                    if element.is_some() || map_iterator.next_index >= len {\n                        break element;\n                    }\n                }\n            };\n            if let Some((key, value)) = e {\n                let item = match item_kind {\n                    PropertyNameKind::Key => Ok(create_iter_result_object(key, false, context)),\n                    PropertyNameKind::Value => Ok(create_iter_result_object(value, false, context)),\n                    PropertyNameKind::KeyAndValue => {\n                        let result = Array::create_array_from_list([key, value], context);\n                        Ok(create_iter_result_object(result.into(), false, context))\n                    }\n                };\n                map_iterator.iterated_map = Some(obj);\n                return item;\n            }\n        }\n\n        Ok(create_iter_result_object(\n            JsValue::undefined(),\n            true,\n            context,\n        ))\n    }\n}\n"
  },
  {
    "path": "core/engine/src/builtins/map/mod.rs",
    "content": "//! Boa's implementation of ECMAScript's global `Map` object.\n//!\n//! The ECMAScript `Map` class is a global object that is used in the construction of maps; which\n//! are high-level, key-value stores.\n//!\n//! More information:\n//!  - [ECMAScript reference][spec]\n//!  - [MDN documentation][mdn]\n//!\n//! [spec]: https://tc39.es/ecma262/#sec-map-objects\n//! [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map\n\nuse crate::{\n    Context, JsArgs, JsResult, JsString, JsValue,\n    builtins::{BuiltInObject, iterable::IteratorHint, map::ordered_map::MapLock},\n    context::intrinsics::{Intrinsics, StandardConstructor, StandardConstructors},\n    error::JsNativeError,\n    js_error, js_string,\n    object::{JsFunction, JsObject, internal_methods::get_prototype_from_constructor},\n    property::{Attribute, PropertyNameKind},\n    realm::Realm,\n    string::StaticJsStrings,\n    symbol::JsSymbol,\n};\n\nuse super::{\n    BuiltInBuilder, BuiltInConstructor, IntrinsicObject, canonicalize_keyed_collection_key,\n    iterable::if_abrupt_close_iterator,\n};\n\nmod map_iterator;\npub(crate) use map_iterator::MapIterator;\n\npub mod ordered_map;\nuse ordered_map::OrderedMap;\n\n#[cfg(test)]\nmod tests;\n\n#[derive(Debug, Clone)]\npub(crate) struct Map;\n\nimpl IntrinsicObject for Map {\n    fn init(realm: &Realm) {\n        let get_species = BuiltInBuilder::callable(realm, Self::get_species)\n            .name(js_string!(\"get [Symbol.species]\"))\n            .build();\n\n        let get_size = BuiltInBuilder::callable(realm, Self::get_size)\n            .name(js_string!(\"get size\"))\n            .build();\n\n        let entries_function = BuiltInBuilder::callable(realm, Self::entries)\n            .name(js_string!(\"entries\"))\n            .build();\n\n        BuiltInBuilder::from_standard_constructor::<Self>(realm)\n            .static_method(Self::group_by, js_string!(\"groupBy\"), 2)\n            .static_accessor(\n                JsSymbol::species(),\n                Some(get_species),\n                None,\n                Attribute::CONFIGURABLE,\n            )\n            .property(\n                js_string!(\"entries\"),\n                entries_function.clone(),\n                Attribute::WRITABLE | Attribute::NON_ENUMERABLE | Attribute::CONFIGURABLE,\n            )\n            .property(\n                JsSymbol::iterator(),\n                entries_function,\n                Attribute::WRITABLE | Attribute::NON_ENUMERABLE | Attribute::CONFIGURABLE,\n            )\n            .property(\n                JsSymbol::to_string_tag(),\n                Self::NAME,\n                Attribute::READONLY | Attribute::NON_ENUMERABLE | Attribute::CONFIGURABLE,\n            )\n            .method(Self::clear, js_string!(\"clear\"), 0)\n            .method(Self::delete, js_string!(\"delete\"), 1)\n            .method(Self::for_each, js_string!(\"forEach\"), 1)\n            .method(Self::get, js_string!(\"get\"), 1)\n            .method(Self::has, js_string!(\"has\"), 1)\n            .method(Self::keys, js_string!(\"keys\"), 0)\n            .method(Self::set, js_string!(\"set\"), 2)\n            .method(Self::values, js_string!(\"values\"), 0)\n            .method(Self::get_or_insert, js_string!(\"getOrInsert\"), 2)\n            .method(\n                Self::get_or_insert_computed,\n                js_string!(\"getOrInsertComputed\"),\n                2,\n            )\n            .accessor(\n                js_string!(\"size\"),\n                Some(get_size),\n                None,\n                Attribute::CONFIGURABLE,\n            )\n            .build();\n    }\n\n    fn get(intrinsics: &Intrinsics) -> JsObject {\n        Self::STANDARD_CONSTRUCTOR(intrinsics.constructors()).constructor()\n    }\n}\n\nimpl BuiltInObject for Map {\n    const NAME: JsString = StaticJsStrings::MAP;\n}\n\nimpl BuiltInConstructor for Map {\n    const CONSTRUCTOR_ARGUMENTS: usize = 0;\n    const PROTOTYPE_STORAGE_SLOTS: usize = 15;\n    const CONSTRUCTOR_STORAGE_SLOTS: usize = 3;\n\n    const STANDARD_CONSTRUCTOR: fn(&StandardConstructors) -> &StandardConstructor =\n        StandardConstructors::map;\n\n    /// `Map ( [ iterable ] )`\n    ///\n    /// Constructor for `Map` objects.\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///  - [MDN documentation][mdn]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-map-iterable\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map/Map\n    fn constructor(\n        new_target: &JsValue,\n        args: &[JsValue],\n        context: &mut Context,\n    ) -> JsResult<JsValue> {\n        // 1. If NewTarget is undefined, throw a TypeError exception.\n        if new_target.is_undefined() {\n            return Err(js_error!(\n                TypeError: \"cannot call `Map` constructor without new\"\n            ));\n        }\n\n        // 2. Let map be ? OrdinaryCreateFromConstructor(NewTarget, \"%Map.prototype%\", « [[MapData]] »).\n        // 3. Set map.[[MapData]] to a new empty List.\n        let prototype =\n            get_prototype_from_constructor(new_target, StandardConstructors::map, context)?;\n        let map = JsObject::from_proto_and_data_with_shared_shape(\n            context.root_shape(),\n            prototype,\n            <OrderedMap<JsValue>>::new(),\n        )\n        .upcast();\n\n        // 4. If iterable is either undefined or null, return map.\n        let iterable = match args.get_or_undefined(0) {\n            val if !val.is_null_or_undefined() => val,\n            _ => return Ok(map.into()),\n        };\n\n        // 5. Let adder be ? Get(map, \"set\").\n        // 6. If IsCallable(adder) is false, throw a TypeError exception.\n        let adder = map\n            .get(js_string!(\"set\"), context)?\n            .as_function()\n            .ok_or_else(\n                || js_error!(TypeError: \"constructor `Map` cannot use non-callable adder\"),\n            )?;\n\n        // 7. Return ? AddEntriesFromIterable(map, iterable, adder).\n        add_entries_from_iterable(&map, iterable, &adder, context)\n    }\n}\n\nimpl Map {\n    /// `get Map [ @@species ]`\n    ///\n    /// The `Map [ @@species ]` accessor property returns the Map constructor.\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///  - [MDN documentation][mdn]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-get-map-@@species\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map/@@species\n    #[allow(clippy::unnecessary_wraps)]\n    fn get_species(this: &JsValue, _: &[JsValue], _: &mut Context) -> JsResult<JsValue> {\n        // 1. Return the this value.\n        Ok(this.clone())\n    }\n\n    /// `Map.prototype.entries()`\n    ///\n    /// Returns a new Iterator object that contains the [key, value] pairs for each element in the Map object in insertion order.\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///  - [MDN documentation][mdn]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-map.prototype.entries\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map/entries\n    pub(crate) fn entries(\n        this: &JsValue,\n        _: &[JsValue],\n        context: &mut Context,\n    ) -> JsResult<JsValue> {\n        // 1. Let M be the this value.\n        let this = this\n            .as_object()\n            .and_then(|o| o.downcast::<OrderedMap<JsValue>>().ok())\n            .ok_or_else(|| {\n                js_error!(\n                    TypeError: \"method `Map.prototype.entries` called on incompatible receiver\"\n                )\n            })?;\n\n        // 2. Return ? CreateMapIterator(M, key+value).\n        Ok(MapIterator::create_map_iterator(\n            this,\n            PropertyNameKind::KeyAndValue,\n            context,\n        ))\n    }\n\n    /// `Map.prototype.keys()`\n    ///\n    /// Returns a new Iterator object that contains the keys for each element in the Map object in insertion order.\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///  - [MDN documentation][mdn]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-map.prototype.keys\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map/keys\n    pub(crate) fn keys(this: &JsValue, _: &[JsValue], context: &mut Context) -> JsResult<JsValue> {\n        // 1. Let M be the this value.\n        let this = this\n            .as_object()\n            .and_then(|o| o.downcast::<OrderedMap<JsValue>>().ok())\n            .ok_or_else(|| js_error!(TypeError: \"method `Map.prototype.keys` called on incompatible receiver\"))?;\n\n        // 2. Return ? CreateMapIterator(M, key).\n        Ok(MapIterator::create_map_iterator(\n            this,\n            PropertyNameKind::Key,\n            context,\n        ))\n    }\n\n    /// `Map.prototype.set( key, value )`\n    ///\n    /// Inserts a new entry in the Map object.\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///  - [MDN documentation][mdn]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-map.prototype.set\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map/set\n    pub(crate) fn set(this: &JsValue, args: &[JsValue], _: &mut Context) -> JsResult<JsValue> {\n        let value = args.get_or_undefined(1);\n\n        // 1. Let M be the this value.\n        // 2. Perform ? RequireInternalSlot(M, [[MapData]]).\n        let map = this\n            .as_object()\n            .and_then(|o| o.downcast::<OrderedMap<JsValue>>().ok())\n            .ok_or_else(|| {\n                js_error!(TypeError: \"method `Map.prototype.set` called on incompatible receiver\")\n            })?;\n\n        // 3. Set key to CanonicalizeKeyedCollectionKey(key).\n        let key = canonicalize_keyed_collection_key(args.get_or_undefined(0).clone());\n\n        // 4. For each Record { [[Key]], [[Value]] } p of entries, do\n        //    a. If p.[[Key]] is not empty and SameValueZero(p.[[Key]], key) is true, then\n        //       i. Set p.[[Value]] to value.\n        // 5. Let p be the Record { [[Key]]: key, [[Value]]: value }.\n        // 6. Append p to M.[[MapData]].\n        map.borrow_mut().data_mut().insert(key, value.clone());\n\n        // 7. Return M.\n        Ok(this.clone())\n    }\n\n    /// `get Map.prototype.size`\n    ///\n    /// Obtains the size of the map, filtering empty keys to ensure it updates\n    /// while iterating.\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///  - [MDN documentation][mdn]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-get-map.prototype.size\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map/size\n    pub(crate) fn get_size(this: &JsValue, _: &[JsValue], _: &mut Context) -> JsResult<JsValue> {\n        // 1. Let M be the this value.\n        // 2. Perform ? RequireInternalSlot(M, [[MapData]]).\n        // 3. Let entries be the List that is M.[[MapData]].\n        // 4. Let count be 0.\n        // 5. For each Record { [[Key]], [[Value]] } p of entries, do\n        // a. If p.[[Key]] is not empty, set count to count + 1.\n        // 6. Return 𝔽(count).\n        Ok(this\n            .as_object()\n            .and_then(|o| o.downcast::<OrderedMap<JsValue>>().ok())\n            .ok_or_else(|| {\n                js_error!(TypeError: \"method `Map.prototype.set` called on incompatible receiver\")\n            })?\n            .borrow()\n            .data()\n            .len()\n            .into())\n    }\n\n    /// `Map.prototype.delete( key )`\n    ///\n    /// Removes the element associated with the key, if it exists.\n    /// Returns true if there was an element, and false otherwise.\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///  - [MDN documentation][mdn]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-map.prototype.delete\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map/delete\n    pub(crate) fn delete(this: &JsValue, args: &[JsValue], _: &mut Context) -> JsResult<JsValue> {\n        // 1. Let M be the this value.\n        // 2. Perform ? RequireInternalSlot(M, [[MapData]]).\n        let map = this\n            .as_object()\n            .and_then(|o| o.downcast::<OrderedMap<JsValue>>().ok())\n            .ok_or_else(|| {\n                js_error!(TypeError: \"method `Map.prototype.delete` called on incompatible receiver\")\n            })?;\n\n        // 3. Set key to CanonicalizeKeyedCollectionKey(key).\n        let key = canonicalize_keyed_collection_key(args.get_or_undefined(0).clone());\n\n        // 4. For each Record { [[Key]], [[Value]] } p of M.[[MapData]], do\n        //    a. If p.[[Key]] is not empty and SameValue(p.[[Key]], key) is true, then\n        //       i. Set p.[[Key]] to empty.\n        //       ii. Set p.[[Value]] to empty.\n        //       iii. Return true.\n        // 5. Return false.\n        Ok(map.borrow_mut().data_mut().remove(&key).is_some().into())\n    }\n\n    /// `Map.prototype.get( key )`\n    ///\n    /// Returns the value associated with the key, or undefined if there is none.\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///  - [MDN documentation][mdn]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-map.prototype.get\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map/get\n    pub(crate) fn get(this: &JsValue, args: &[JsValue], _: &mut Context) -> JsResult<JsValue> {\n        // 1. Let M be the this value.\n        // 2. Perform ? RequireInternalSlot(M, [[MapData]]).\n        let map = this\n            .as_object()\n            .and_then(|o| o.downcast::<OrderedMap<JsValue>>().ok())\n            .ok_or_else(|| {\n                js_error!(TypeError: \"method `Map.prototype.get` called on incompatible receiver\")\n            })?;\n\n        // 3. Set key to CanonicalizeKeyedCollectionKey(key).\n        let key = canonicalize_keyed_collection_key(args.get_or_undefined(0).clone());\n\n        // 4. For each Record { [[Key]], [[Value]] } p of M.[[MapData]], do\n        //    a. If p.[[Key]] is not empty and SameValue(p.[[Key]], key) is true, return p.[[Value]].\n        // 5. Return undefined.\n        Ok(map.borrow().data().get(&key).cloned().unwrap_or_default())\n    }\n\n    /// `Map.prototype.clear( )`\n    ///\n    /// Removes all entries from the map.\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///  - [MDN documentation][mdn]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-map.prototype.clear\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map/clear\n    pub(crate) fn clear(this: &JsValue, _: &[JsValue], _: &mut Context) -> JsResult<JsValue> {\n        // 1. Let M be the this value.\n        // 2. Perform ? RequireInternalSlot(M, [[MapData]]).\n        let map = this\n            .as_object()\n            .and_then(|o| o.downcast::<OrderedMap<JsValue>>().ok())\n            .ok_or_else(|| {\n                js_error!(\n                    TypeError: \"method `Map.prototype.clear` called on incompatible receiver\"\n                )\n            })?;\n\n        // 3. For each Record { [[Key]], [[Value]] } p of M.[[MapData]], do\n        //    a. Set p.[[Key]] to empty.\n        //    b. Set p.[[Value]] to empty.\n        map.borrow_mut().data_mut().clear();\n\n        // 4. Return undefined.\n        Ok(JsValue::undefined())\n    }\n\n    /// `Map.prototype.has( key )`\n    ///\n    /// Checks if the map contains an entry with the given key.\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///  - [MDN documentation][mdn]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-map.prototype.has\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map/has\n    pub(crate) fn has(this: &JsValue, args: &[JsValue], _: &mut Context) -> JsResult<JsValue> {\n        // 1. Let M be the this value.\n        // 2. Perform ? RequireInternalSlot(M, [[MapData]]).\n        let map = this\n            .as_object()\n            .and_then(|o| o.downcast::<OrderedMap<JsValue>>().ok())\n            .ok_or_else(|| {\n                js_error!(\n                    TypeError: \"method `Map.prototype.has` called on incompatible receiver\"\n                )\n            })?;\n\n        // 3. Set key to CanonicalizeKeyedCollectionKey(key).\n        let key = canonicalize_keyed_collection_key(args.get_or_undefined(0).clone());\n\n        // 4. For each Record { [[Key]], [[Value]] } p of M.[[MapData]], do\n        //    a. If p.[[Key]] is not empty and SameValue(p.[[Key]], key) is true, return true.\n        // 5. Return false.\n        Ok(map.borrow().data().contains_key(&key).into())\n    }\n\n    /// `Map.prototype.forEach( callbackFn [ , thisArg ] )`\n    ///\n    /// Executes the provided callback function for each key-value pair in the map.\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///  - [MDN documentation][mdn]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-map.prototype.foreach\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map/forEach\n    pub(crate) fn for_each(\n        this: &JsValue,\n        args: &[JsValue],\n        context: &mut Context,\n    ) -> JsResult<JsValue> {\n        // 1. Let M be the this value.\n        // 2. Perform ? RequireInternalSlot(M, [[MapData]]).\n        let map = this\n            .as_object()\n            .and_then(|o| o.downcast::<OrderedMap<JsValue>>().ok())\n            .ok_or_else(|| {\n                js_error!(\n                    TypeError: \"method `Map.prototype.forEach` called on incompatible receiver\"\n                )\n            })?;\n\n        // 3. If IsCallable(callbackfn) is false, throw a TypeError exception.\n        let Some(callback) = args.get_or_undefined(0).as_callable() else {\n            return Err(js_error!(\n                TypeError:\n                    \"Method Map.prototype.forEach called with non-callable callback function\"\n            ));\n        };\n\n        let this_arg = args.get_or_undefined(1);\n\n        // NOTE:\n        //\n        // forEach does not directly mutate the object on which it is called but\n        // the object may be mutated by the calls to callbackfn. Each entry of a\n        // map's [[MapData]] is only visited once. New keys added after the call\n        // to forEach begins are visited. A key will be revisited if it is deleted\n        // after it has been visited and then re-added before the forEach call completes.\n        // Keys that are deleted after the call to forEach begins and before being visited\n        // are not visited unless the key is added again before the forEach call completes.\n        let _lock = MapLock::new(&map);\n\n        // 4. Let entries be the List that is M.[[MapData]].\n        // 5. For each Record { [[Key]], [[Value]] } e of entries, do\n        let mut index = 0;\n        loop {\n            let arguments = {\n                let map = map.borrow();\n                let map = map.data();\n                if index < map.full_len() {\n                    map.get_index(index)\n                        .map(|(k, v)| [v.clone(), k.clone(), this.clone()])\n                } else {\n                    // 6. Return undefined.\n                    return Ok(JsValue::undefined());\n                }\n            };\n\n            // a. If e.[[Key]] is not empty, then\n            if let Some(arguments) = arguments {\n                // i. Perform ? Call(callbackfn, thisArg, « e.[[Value]], e.[[Key]], M »).\n                callback.call(this_arg, &arguments, context)?;\n            }\n\n            index += 1;\n        }\n    }\n\n    /// Call `f` for each `(key, value)` in the `Map`.\n    ///\n    /// Can not be used in [`Self::for_each`] because in that case will be\n    /// incorrect order for next steps of the algo:\n    /// ```txt\n    /// 2. Perform ? RequireInternalSlot(M, [[MapData]]).\n    /// 3. If IsCallable(callbackfn) is false, throw a TypeError exception.\n    /// ```\n    pub(crate) fn for_each_native<F>(this: &JsValue, mut f: F) -> JsResult<()>\n    where\n        F: FnMut(JsValue, JsValue) -> JsResult<()>,\n    {\n        // See `Self::for_each` for comments on the algo.\n\n        let map = this\n            .as_object()\n            .and_then(|o| o.downcast::<OrderedMap<JsValue>>().ok())\n            .ok_or_else(|| {\n                js_error!(\n                    TypeError: \"method `Map.prototype.forEach` called on incompatible receiver\"\n                )\n            })?;\n\n        let _lock = MapLock::new(&map);\n\n        let mut index = 0;\n        loop {\n            let (k, v) = {\n                let map = map.borrow();\n                let map = map.data();\n                if index >= map.full_len() {\n                    return Ok(());\n                }\n\n                let Some((k, v)) = map.get_index(index) else {\n                    continue;\n                };\n\n                (k.clone(), v.clone())\n            };\n\n            f(k, v)?;\n            index += 1;\n        }\n    }\n\n    /// `Map.prototype.values()`\n    ///\n    /// Returns a new Iterator object that contains the values for each element in the Map object in insertion order.\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///  - [MDN documentation][mdn]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-map.prototype.values\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map/values\n    pub(crate) fn values(\n        this: &JsValue,\n        _: &[JsValue],\n        context: &mut Context,\n    ) -> JsResult<JsValue> {\n        // 1. Let M be the this value.\n        // 2. Return ? CreateMapIterator(M, value).\n        let this = this\n            .as_object()\n            .and_then(|o| o.downcast::<OrderedMap<JsValue>>().ok())\n            .ok_or_else(|| {\n                js_error!(\n                    TypeError: \"method `Map.prototype.values` called on incompatible receiver\"\n                )\n            })?;\n\n        Ok(MapIterator::create_map_iterator(\n            this,\n            PropertyNameKind::Value,\n            context,\n        ))\n    }\n\n    /// `Map.prototype.getOrInsert(key, value)`\n    ///\n    /// Given a key and a value, the getOrInsert method will return the existing value if it exists.\n    /// Otherwise insert the provided default value and return that value.\n    ///\n    /// More information:\n    ///  - [Upsert proposal reference][spec]\n    ///  - [MDN documentation][mdn]\n    ///\n    /// [spec]: https://tc39.es/proposal-upsert/#sec-map.prototype.getOrInsert\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map/getOrInsert\n    pub(crate) fn get_or_insert(\n        this: &JsValue,\n        args: &[JsValue],\n        _: &mut Context,\n    ) -> JsResult<JsValue> {\n        let value = args.get_or_undefined(1);\n\n        // 1. Let M be the this value.\n        // 2. Perform ? RequireInternalSlot(M, [[MapData]]).\n        let map = this\n            .as_object()\n            .and_then(|o| o.downcast::<OrderedMap<JsValue>>().ok())\n            .ok_or_else(|| {\n                js_error!(\n                    TypeError: \"method `Map.prototype.getOrInsert` called on incompatible receiver\"\n                )\n            })?;\n\n        // 3. Set key to CanonicalizeKeyedCollectionKey(key).\n        let key = canonicalize_keyed_collection_key(args.get_or_undefined(0).clone());\n\n        // 4. For each Record { [[Key]], [[Value]] } p of M.[[MapData]], do\n        if let Some(existing) = map.borrow().data().get(&key) {\n            // a. If p.[[Key]] is not empty and SameValue(p.[[Key]], key) is true, return p.[[Value]].\n            return Ok(existing.clone());\n        }\n\n        // 5. Let p be the Record { [[Key]]: key, [[Value]]: value }.\n        // 6. Append p to M.[[MapData]].\n        map.borrow_mut().data_mut().insert(key, value.clone());\n\n        // 7. Return value.\n        Ok(value.clone())\n    }\n\n    /// `Map.prototype.getOrInsertComputed(key, callback)`\n    ///\n    /// If the key exists in the Map, returns the existing value.\n    /// Otherwise computes a new value by calling `callback` with the key,\n    /// inserts it into the Map, and returns it.\n    ///\n    /// More information:\n    ///  - [Upsert proposal reference][spec]\n    ///  - [MDN documentation][mdn]\n    ///\n    /// [spec]: https://tc39.es/proposal-upsert/#sec-map.prototype.getOrInsertComputed\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map/getOrInsertComputed\n    pub(crate) fn get_or_insert_computed(\n        this: &JsValue,\n        args: &[JsValue],\n        context: &mut Context,\n    ) -> JsResult<JsValue> {\n        // 1. Let M be the this value.\n        // 2. Perform ? RequireInternalSlot(M, [[MapData]]).\n        let map = this\n            .as_object()\n            .and_then(|o| o.downcast::<OrderedMap<JsValue>>().ok())\n            .ok_or_else(|| {\n                js_error!(\n                    TypeError: \"method `Map.prototype.getOrInsertComputed` called on incompatible receiver\"\n                )\n            })?;\n\n        // 3. If IsCallable(callbackfn) is false, throw a TypeError exception.\n        let Some(callback_fn) = args.get_or_undefined(1).as_callable() else {\n            return Err(js_error!(\n                TypeError: \"method `Map.prototype.getOrInsertComputed` called with non-callable callback function\"\n            ));\n        };\n\n        // 4. Set key to CanonicalizeKeyedCollectionKey(key).\n        let key = canonicalize_keyed_collection_key(args.get_or_undefined(0).clone());\n\n        // 5. For each Record { [[Key]], [[Value]] } p of M.[[MapData]], do\n        if let Some(existing) = map.borrow().data().get(&key) {\n            // a. If p.[[Key]] is not empty and SameValue(p.[[Key]], key) is true, return p.[[Value]].\n            return Ok(existing.clone());\n        }\n\n        // 6. Let value be ? Call(callback, undefined, « key »).\n        // 7. NOTE: The Map may have been modified during execution of callback.\n        let value = callback_fn.call(&JsValue::undefined(), std::slice::from_ref(&key), context)?;\n\n        // 8. For each Record { [[Key]], [[Value]] } p of M.[[MapData]], do\n        //    a. If p.[[Key]] is not empty and SameValue(p.[[Key]], key) is true, then\n        //       i. Set p.[[Value]] to value.\n        //       ii. Return value.\n        // 9. Let p be the Record { [[Key]]: key, [[Value]]: value }.\n        // 10. Append p to M.[[MapData]].\n        // [`OrderedMap::insert`] handles both cases\n        map.borrow_mut()\n            .data_mut()\n            .insert(key.clone(), value.clone());\n        // 11. Return value.\n        Ok(value)\n    }\n\n    /// [`Map.groupBy ( items, callbackfn )`][spec]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-map.groupby\n    pub(crate) fn group_by(\n        _: &JsValue,\n        args: &[JsValue],\n        context: &mut Context,\n    ) -> JsResult<JsValue> {\n        use std::hash::BuildHasherDefault;\n\n        use indexmap::IndexMap;\n        use rustc_hash::FxHasher;\n\n        use crate::builtins::{Array, Number, iterable::if_abrupt_close_iterator};\n\n        let items = args.get_or_undefined(0);\n        let callback = args.get_or_undefined(1);\n        // 1. Let groups be ? GroupBy(items, callbackfn, zero).\n\n        // `GroupBy`\n        // https://tc39.es/ecma262/#sec-groupby\n        // inlined to change the key type.\n\n        // 1. Perform ? RequireObjectCoercible(items).\n        items.require_object_coercible()?;\n\n        // 2. If IsCallable(callbackfn) is false, throw a TypeError exception.\n        let callback = callback.as_callable().ok_or_else(|| {\n            js_error!(\n                TypeError:\n                    \"method `Map.prototype.groupBy` called with non-callable callback function\"\n            )\n        })?;\n\n        // 3. Let groups be a new empty List.\n        let mut groups: IndexMap<JsValue, Vec<JsValue>, BuildHasherDefault<FxHasher>> =\n            IndexMap::default();\n\n        // 4. Let iteratorRecord be ? GetIterator(items, sync).\n        let mut iterator = items.get_iterator(IteratorHint::Sync, context)?;\n\n        // 5. Let k be 0.\n        let mut k = 0u64;\n\n        // 6. Repeat,\n        loop {\n            // a. If k ≥ 2^53 - 1, then\n            if k >= Number::MAX_SAFE_INTEGER as u64 {\n                // i. Let error be ThrowCompletion(a newly created TypeError object).\n                let error = JsNativeError::typ()\n                    .with_message(\"exceeded maximum safe integer\")\n                    .into();\n\n                // ii. Return ? IteratorClose(iteratorRecord, error).\n                return iterator.close(Err(error), context);\n            }\n\n            // b. Let next be ? IteratorStepValue(iteratorRecord).\n            let Some(next) = iterator.step_value(context)? else {\n                // c. If next is false, then\n                // i. Return groups.\n                break;\n            };\n\n            // d. Let value be next.\n            let value = next;\n\n            // e. Let key be Completion(Call(callbackfn, undefined, « value, 𝔽(k) »)).\n            let key = callback.call(&JsValue::undefined(), &[value.clone(), k.into()], context);\n\n            // f. IfAbruptCloseIterator(key, iteratorRecord).\n            let key = if_abrupt_close_iterator!(key, iterator, context);\n\n            // h. Else,\n            //     i. Assert: keyCoercion is collection.\n            //     ii. Set key to CanonicalizeKeyedCollectionKey(key).\n            let key = canonicalize_keyed_collection_key(key);\n\n            // i. Perform AddValueToKeyedGroup(groups, key, value).\n            groups.entry(key).or_default().push(value);\n\n            // j. Set k to k + 1.\n            k += 1;\n        }\n\n        // 2. Let map be ! Construct(%Map%).\n        let mut map: OrderedMap<JsValue> = OrderedMap::new();\n\n        // 3. For each Record { [[Key]], [[Elements]] } g of groups, do\n        for (key, elements) in groups {\n            // a. Let elements be CreateArrayFromList(g.[[Elements]]).\n            let elements = Array::create_array_from_list(elements, context);\n\n            // b. Let entry be the Record { [[Key]]: g.[[Key]], [[Value]]: elements }.\n            // c. Append entry to map.[[MapData]].\n            map.insert(key, elements.into());\n        }\n\n        let proto = context.intrinsics().constructors().map().prototype();\n\n        // 4. Return map.\n        Ok(\n            JsObject::from_proto_and_data_with_shared_shape(context.root_shape(), proto, map)\n                .into(),\n        )\n    }\n}\n\n/// `AddEntriesFromIterable`\n///\n/// Allows adding entries to a map from any object that has a `@@Iterator` field.\n///\n/// More information:\n///  - [ECMAScript reference][spec]\n///\n/// [spec]: https://tc39.es/ecma262/#sec-add-entries-from-iterable\npub(crate) fn add_entries_from_iterable(\n    target: &JsObject,\n    iterable: &JsValue,\n    adder: &JsFunction,\n    context: &mut Context,\n) -> JsResult<JsValue> {\n    // 1. Let iteratorRecord be ? GetIterator(iterable, sync).\n    let mut iterator_record = iterable.get_iterator(IteratorHint::Sync, context)?;\n\n    // 2. Repeat,\n    //     a. Let next be ? IteratorStepValue(iteratorRecord).\n    //     b. If next is done, return target.\n    while let Some(next) = iterator_record.step_value(context)? {\n        let Some(next) = next.as_object() else {\n            //     c. If next is not an Object, then\n            //         i. Let error be ThrowCompletion(a newly created TypeError object).\n            //         ii. Return ? IteratorClose(iteratorRecord, error).\n            let err = Err(JsNativeError::typ()\n                .with_message(\"cannot get key and value from primitive item of `iterable`\")\n                .into());\n\n            // ii. Return ? IteratorClose(iteratorRecord, error).\n            return iterator_record.close(err, context);\n        };\n\n        //     d. Let k be Completion(Get(next, \"0\")).\n        //     e. IfAbruptCloseIterator(k, iteratorRecord).\n        let key = if_abrupt_close_iterator!(next.get(0, context), iterator_record, context);\n\n        //     f. Let v be Completion(Get(next, \"1\")).\n        //     g. IfAbruptCloseIterator(v, iteratorRecord).\n        let value = if_abrupt_close_iterator!(next.get(1, context), iterator_record, context);\n\n        //     h. Let status be Completion(Call(adder, target, « k, v »)).\n        //     i. IfAbruptCloseIterator(status, iteratorRecord).\n        let status = adder.call(&target.clone().into(), &[key, value], context);\n        if_abrupt_close_iterator!(status, iterator_record, context);\n    }\n\n    Ok(target.clone().into())\n}\n"
  },
  {
    "path": "core/engine/src/builtins/map/ordered_map.rs",
    "content": "//! Implements a map type that preserves insertion order.\n\nuse crate::{JsData, JsValue, object::JsObject};\nuse boa_gc::{Finalize, Trace, custom_trace};\nuse indexmap::{Equivalent, IndexMap};\nuse std::{\n    fmt::Debug,\n    hash::{Hash, Hasher},\n};\n\n#[derive(PartialEq, Eq, Clone, Debug)]\npub(crate) enum MapKey {\n    Key(JsValue),\n    Empty(usize), // Necessary to ensure empty keys are still unique.\n}\n\n// This ensures that a MapKey::Key(value) hashes to the same as value. The derived PartialEq implementation still holds.\nimpl Hash for MapKey {\n    fn hash<H: Hasher>(&self, state: &mut H) {\n        match self {\n            Self::Key(v) => v.hash(state),\n            Self::Empty(e) => e.hash(state),\n        }\n    }\n}\n\nimpl Equivalent<MapKey> for JsValue {\n    fn equivalent(&self, key: &MapKey) -> bool {\n        match key {\n            MapKey::Key(v) => v == self,\n            MapKey::Empty(_) => false,\n        }\n    }\n}\n\n/// A structure wrapping `indexmap::IndexMap`.\n#[derive(Clone, Finalize, JsData)]\npub struct OrderedMap<V> {\n    map: IndexMap<MapKey, Option<V>>,\n    lock: u32,\n    empty_count: usize,\n}\n\nunsafe impl<V: Trace> Trace for OrderedMap<V> {\n    custom_trace!(this, mark, {\n        for (k, v) in &this.map {\n            if let MapKey::Key(key) = k {\n                mark(key);\n            }\n            mark(v);\n        }\n    });\n}\n\nimpl<V: Debug> Debug for OrderedMap<V> {\n    fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {\n        self.map.fmt(formatter)\n    }\n}\n\nimpl<V> Default for OrderedMap<V> {\n    fn default() -> Self {\n        Self::new()\n    }\n}\n\nimpl<V> OrderedMap<V> {\n    /// Creates a new empty `OrderedMap`.\n    #[must_use]\n    pub fn new() -> Self {\n        Self {\n            map: IndexMap::new(),\n            lock: 0,\n            empty_count: 0,\n        }\n    }\n\n    /// Creates a new empty `OrderedMap` with the specified capacity.\n    #[must_use]\n    pub fn with_capacity(capacity: usize) -> Self {\n        Self {\n            map: IndexMap::with_capacity(capacity),\n            lock: 0,\n            empty_count: 0,\n        }\n    }\n\n    /// Return the number of key-value pairs in the map, including empty values.\n    ///\n    /// Computes in **O(1)** time.\n    #[must_use]\n    pub fn full_len(&self) -> usize {\n        self.map.len()\n    }\n\n    /// Gets the number of key-value pairs in the map, not including empty values.\n    ///\n    /// Computes in **O(1)** time.\n    #[must_use]\n    pub fn len(&self) -> usize {\n        self.map.len() - self.empty_count\n    }\n\n    /// Returns true if the map contains no elements.\n    ///\n    /// Computes in **O(1)** time.\n    #[must_use]\n    pub fn is_empty(&self) -> bool {\n        self.len() == 0\n    }\n\n    /// Insert a key-value pair in the map.\n    ///\n    /// If an equivalent key already exists in the map: the key remains and\n    /// retains in its place in the order, its corresponding value is updated\n    /// with `value` and the older value is returned inside `Some(_)`.\n    ///\n    /// If no equivalent key existed in the map: the new key-value pair is\n    /// inserted, last in order, and `None` is returned.\n    ///\n    /// Computes in **O(1)** time (amortized average).\n    pub fn insert(&mut self, key: JsValue, value: V) -> Option<V> {\n        self.map.insert(MapKey::Key(key), Some(value)).flatten()\n    }\n\n    /// Remove the key-value pair equivalent to `key` and return\n    /// its value.\n    ///\n    /// Like `Vec::remove`, the pair is removed by shifting all of the\n    /// elements that follow it, preserving their relative order.\n    /// **This perturbs the index of all of those elements!**\n    ///\n    /// Return `None` if `key` is not in map.\n    ///\n    /// Computes in **O(n)** time (average).\n    pub fn remove(&mut self, key: &JsValue) -> Option<V> {\n        if self.lock == 0 {\n            self.map.shift_remove(key).flatten()\n        } else if self.map.contains_key(key) {\n            self.map.insert(MapKey::Empty(self.empty_count), None);\n            self.empty_count += 1;\n            self.map.swap_remove(key).flatten()\n        } else {\n            None\n        }\n    }\n\n    /// Removes all elements from the map and resets the counter of\n    /// empty entries.\n    pub fn clear(&mut self) {\n        self.map.clear();\n        self.map.shrink_to_fit();\n        self.empty_count = 0;\n    }\n\n    /// Return a reference to the value stored for `key`, if it is present,\n    /// else `None`.\n    ///\n    /// Computes in **O(1)** time (average).\n    pub fn get(&self, key: &JsValue) -> Option<&V> {\n        self.map.get(key).and_then(Option::as_ref)\n    }\n\n    /// Get a key-value pair by index.\n    ///\n    /// Valid indices are `0 <= index < self.full_len()`.\n    ///\n    /// Computes in O(1) time.\n    #[must_use]\n    pub fn get_index(&self, index: usize) -> Option<(&JsValue, &V)> {\n        if let (MapKey::Key(key), Some(value)) = self.map.get_index(index)? {\n            Some((key, value))\n        } else {\n            None\n        }\n    }\n\n    /// Return an iterator over the key-value pairs of the map, in their order\n    pub fn iter(&self) -> impl Iterator<Item = (&JsValue, &V)> {\n        self.map.iter().filter_map(|o| {\n            if let (MapKey::Key(key), Some(value)) = o {\n                Some((key, value))\n            } else {\n                None\n            }\n        })\n    }\n\n    /// Return `true` if an equivalent to `key` exists in the map.\n    ///\n    /// Computes in **O(1)** time (average).\n    #[must_use]\n    pub fn contains_key(&self, key: &JsValue) -> bool {\n        self.map.contains_key(key)\n    }\n\n    /// Increases the lock counter.\n    ///\n    /// This allows objects to be removed from the map during iteration without affecting the indexes until the iteration has completed.\n    pub(crate) fn lock(&mut self) {\n        self.lock += 1;\n    }\n\n    /// Decreases the lock counter and, if 0, removes all empty entries.\n    pub(crate) fn unlock(&mut self) {\n        self.lock -= 1;\n        if self.lock == 0 {\n            self.map.retain(|k, _| matches!(k, MapKey::Key(_)));\n            self.empty_count = 0;\n        }\n    }\n}\n\npub(crate) struct MapLock<'a, V: Trace + 'static>(&'a JsObject<OrderedMap<V>>);\n\nimpl<'a, V: Trace + 'static> MapLock<'a, V> {\n    pub(crate) fn new(js_object: &'a JsObject<OrderedMap<V>>) -> Self {\n        js_object.borrow_mut().data_mut().lock();\n        Self(js_object)\n    }\n}\n\nimpl<V: Trace + 'static> Drop for MapLock<'_, V> {\n    fn drop(&mut self) {\n        self.0.borrow_mut().data_mut().unlock();\n    }\n}\n"
  },
  {
    "path": "core/engine/src/builtins/map/tests.rs",
    "content": "use crate::{JsNativeErrorKind, JsValue, TestAction, run_test_actions};\nuse boa_macros::js_str;\nuse indoc::indoc;\n\n#[test]\nfn construct() {\n    run_test_actions([\n        TestAction::assert_eq(\"(new Map()).size\", 0),\n        TestAction::assert_eq(\"(new Map([['1', 'one'], ['2', 'two']])).size\", 2),\n    ]);\n}\n\n#[test]\nfn clone() {\n    run_test_actions([\n        TestAction::run(indoc! {r#\"\n                let original = new Map([[\"1\", \"one\"], [\"2\", \"two\"]]);\n                let clone = new Map(original);\n            \"#}),\n        TestAction::assert_eq(\"clone.size\", 2),\n        TestAction::assert_eq(\"original.set('3', 'three'); original.size\", 3),\n        TestAction::assert_eq(\"clone.size\", 2),\n    ]);\n}\n\n#[test]\nfn symbol_iterator() {\n    run_test_actions([\n        TestAction::run_harness(),\n        TestAction::run(indoc! {r#\"\n                const map1 = new Map();\n                map1.set('0', 'foo');\n                map1.set(1, 'bar');\n                const iterator = map1[Symbol.iterator]();\n                let item1 = iterator.next();\n                let item2 = iterator.next();\n                let item3 = iterator.next();\n            \"#}),\n        TestAction::assert(\"arrayEquals(item1.value, ['0', 'foo'])\"),\n        TestAction::assert(\"arrayEquals(item2.value, [1, 'bar'])\"),\n        TestAction::assert_eq(\"item3.value\", JsValue::undefined()),\n        TestAction::assert(\"item3.done\"),\n    ]);\n}\n\n// Should behave the same as symbol_iterator\n#[test]\nfn entries() {\n    run_test_actions([\n        TestAction::run_harness(),\n        TestAction::run(indoc! {r#\"\n                const map1 = new Map();\n                map1.set('0', 'foo');\n                map1.set(1, 'bar');\n                const iterator = map1.entries();\n                let item1 = iterator.next();\n                let item2 = iterator.next();\n                let item3 = iterator.next();\n            \"#}),\n        TestAction::assert(\"arrayEquals(item1.value, ['0', 'foo'])\"),\n        TestAction::assert(\"arrayEquals(item2.value, [1, 'bar'])\"),\n        TestAction::assert_eq(\"item3.value\", JsValue::undefined()),\n        TestAction::assert(\"item3.done\"),\n    ]);\n}\n\n#[test]\nfn merge() {\n    run_test_actions([\n        TestAction::run(indoc! {r#\"\n                let first = new Map([[\"1\", \"one\"], [\"2\", \"two\"]]);\n                let second = new Map([[\"2\", \"second two\"], [\"3\", \"three\"]]);\n                let third = new Map([[\"4\", \"four\"], [\"5\", \"five\"]]);\n                let merged1 = new Map([...first, ...second]);\n                let merged2 = new Map([...second, ...third]);\n            \"#}),\n        TestAction::assert_eq(\"merged1.size\", 3),\n        TestAction::assert_eq(\"merged1.get('2')\", js_str!(\"second two\")),\n        TestAction::assert_eq(\"merged2.size\", 4),\n    ]);\n}\n\n#[test]\nfn get() {\n    run_test_actions([\n        TestAction::run(indoc! {r#\"\n                let map = new Map([[\"1\", \"one\"], [\"2\", \"two\"]]);\n            \"#}),\n        TestAction::assert_eq(\"map.get('1')\", js_str!(\"one\")),\n        TestAction::assert_eq(\"map.get('2')\", js_str!(\"two\")),\n        TestAction::assert_eq(\"map.get('3')\", JsValue::undefined()),\n        TestAction::assert_eq(\"map.get()\", JsValue::undefined()),\n    ]);\n}\n\n#[test]\nfn set() {\n    run_test_actions([\n        TestAction::run(\"let map = new Map();\"),\n        TestAction::assert(\"map.set(); map.has(undefined)\"),\n        TestAction::assert_eq(\"map.get()\", JsValue::undefined()),\n        TestAction::assert_eq(\"map.set('1', 'one'); map.get('1')\", js_str!(\"one\")),\n        TestAction::assert(\"map.set('2'); map.has('2')\"),\n        TestAction::assert_eq(\"map.get('2')\", JsValue::undefined()),\n    ]);\n}\n\n#[test]\nfn clear() {\n    run_test_actions([\n        TestAction::run(indoc! {r#\"\n                let map = new Map([[\"1\", \"one\"], [\"2\", \"two\"]]);\n                map.clear();\n            \"#}),\n        TestAction::assert_eq(\"map.size\", 0),\n    ]);\n}\n\n#[test]\nfn delete() {\n    run_test_actions([\n        TestAction::run(\"let map = new Map([['1', 'one'], ['2', 'two']]);\"),\n        TestAction::assert_eq(\"map.size\", 2),\n        TestAction::assert(\"map.delete('1')\"),\n        TestAction::assert(\"!map.has('1')\"),\n        TestAction::assert(\"!map.delete('1')\"),\n    ]);\n}\n\n#[test]\nfn has() {\n    run_test_actions([\n        TestAction::run(\"let map = new Map([['1', 'one']]);\"),\n        TestAction::assert(\"!map.has()\"),\n        TestAction::assert(\"map.has('1')\"),\n        TestAction::assert(\"!map.has('2')\"),\n    ]);\n}\n\n#[test]\nfn keys() {\n    run_test_actions([\n        TestAction::run(indoc! {r#\"\n                const map1 = new Map();\n                map1.set('0', 'foo');\n                map1.set(1, 'bar');\n                const keysIterator = map1.keys();\n                let item1 = keysIterator.next();\n                let item2 = keysIterator.next();\n                let item3 = keysIterator.next();\n            \"#}),\n        TestAction::assert_eq(\"item1.value\", js_str!(\"0\")),\n        TestAction::assert_eq(\"item2.value\", 1),\n        TestAction::assert(\"item3.done\"),\n    ]);\n}\n\n#[test]\nfn for_each() {\n    run_test_actions([\n        TestAction::run(indoc! {r#\"\n                let map = new Map([[1, 5], [2, 10], [3, 15]]);\n                let valueSum = 0;\n                let keySum = 0;\n                let sizeSum = 0;\n                function callingCallback(value, key, map) {\n                    valueSum += value;\n                    keySum += key;\n                    sizeSum += map.size;\n                }\n                map.forEach(callingCallback);\n            \"#}),\n        TestAction::assert_eq(\"valueSum\", 30),\n        TestAction::assert_eq(\"keySum\", 6),\n        TestAction::assert_eq(\"sizeSum\", 9),\n    ]);\n}\n\n#[test]\nfn values() {\n    run_test_actions([\n        TestAction::run(indoc! {r#\"\n                const map1 = new Map();\n                map1.set('0', 'foo');\n                map1.set(1, 'bar');\n                const valuesIterator = map1.values();\n                let item1 = valuesIterator.next();\n                let item2 = valuesIterator.next();\n                let item3 = valuesIterator.next();\n            \"#}),\n        TestAction::assert_eq(\"item1.value\", js_str!(\"foo\")),\n        TestAction::assert_eq(\"item2.value\", js_str!(\"bar\")),\n        TestAction::assert(\"item3.done\"),\n    ]);\n}\n\n#[test]\nfn modify_key() {\n    run_test_actions([\n        TestAction::run(indoc! {r#\"\n                let obj = new Object();\n                let map = new Map([[obj, \"one\"]]);\n                obj.field = \"Value\";\n            \"#}),\n        TestAction::assert_eq(\"map.get(obj)\", js_str!(\"one\")),\n    ]);\n}\n\n#[test]\nfn order() {\n    run_test_actions([\n        TestAction::run_harness(),\n        TestAction::run(\"let map = new Map([[1, 'one'], [2, 'two']]);\"),\n        TestAction::assert(\"arrayEquals(Array.from(map.keys()), [1, 2])\"),\n        TestAction::run(\"map.set(1, 'five')\"),\n        TestAction::assert(\"arrayEquals(Array.from(map.keys()), [1, 2])\"),\n        TestAction::run(\"map.set()\"),\n        TestAction::assert(\"arrayEquals(Array.from(map.keys()), [1, 2, undefined])\"),\n        TestAction::run(\"map.delete(2)\"),\n        TestAction::assert(\"arrayEquals(Array.from(map.keys()), [1, undefined])\"),\n        TestAction::run(\"map.set(2, 'two')\"),\n        TestAction::assert(\"arrayEquals(Array.from(map.keys()), [1, undefined, 2])\"),\n    ]);\n}\n\n#[test]\nfn recursive_display() {\n    run_test_actions([\n        TestAction::run(indoc! {r#\"\n                let map = new Map();\n                let array = new Array([map]);\n            \"#}),\n        TestAction::assert_with_op(\"map.set('y', map)\", |v, _| {\n            v.display().to_string() == r#\"Map { \"y\" → Map(1) }\"#\n        }),\n        TestAction::assert_with_op(\"map.set('z', array)\", |v, _| {\n            v.display().to_string() == r#\"Map { \"y\" → Map(2), \"z\" → Array(1) }\"#\n        }),\n    ]);\n}\n\n#[test]\nfn not_a_function() {\n    run_test_actions([TestAction::assert_native_error(\n        \"let map = Map()\",\n        JsNativeErrorKind::Type,\n        \"cannot call `Map` constructor without new\",\n    )]);\n}\n\n#[test]\nfn for_each_delete() {\n    run_test_actions([\n        TestAction::run_harness(),\n        TestAction::run(indoc! {r#\"\n                let map = new Map([[0, \"a\"], [1, \"b\"], [2, \"c\"]]);\n                let result = [];\n                map.forEach(function(value, key) {\n                    if (key === 0) {\n                        map.delete(0);\n                        map.set(3, \"d\");\n                    }\n                    result.push([key, value]);\n                })\n            \"#}),\n        TestAction::assert(indoc! {r#\"\n                arrayEquals(\n                    result,\n                    [\n                        [0, \"a\"],\n                        [1, \"b\"],\n                        [2, \"c\"],\n                        [3, \"d\"]\n                    ]\n                )\n            \"#}),\n    ]);\n}\n\n#[test]\nfn for_of_delete() {\n    run_test_actions([\n        TestAction::run_harness(),\n        TestAction::run(indoc! {r#\"\n                let map = new Map([[0, \"a\"], [1, \"b\"], [2, \"c\"]]);\n                let result = [];\n                for (a of map) {\n                    if (a[0] === 0) {\n                        map.delete(0);\n                        map.set(3, \"d\");\n                    }\n                    result.push([a[0], a[1]]);\n                }\n            \"#}),\n        TestAction::assert(indoc! {r#\"\n                arrayEquals(\n                    result,\n                    [\n                        [0, \"a\"],\n                        [1, \"b\"],\n                        [2, \"c\"],\n                        [3, \"d\"]\n                    ]\n                )\n            \"#}),\n    ]);\n}\n\n#[test]\nfn get_or_insert_inserts_on_miss() {\n    run_test_actions([\n        TestAction::run(\"let map = new Map();\"),\n        TestAction::assert_eq(\"map.getOrInsert('x', 42)\", 42),\n        TestAction::assert(\"map.has('x')\"),\n        TestAction::assert_eq(\"map.get('x')\", 42),\n    ]);\n}\n\n#[test]\nfn get_or_insert_returns_existing_on_hit() {\n    run_test_actions([\n        TestAction::run(\"let map = new Map([['y', 99]]);\"),\n        TestAction::assert_eq(\"map.getOrInsert('y', 123)\", 99),\n        TestAction::assert_eq(\"map.get('y')\", 99), // unchanged\n    ]);\n}\n\n#[test]\nfn get_or_insert_canonicalizes_key() {\n    run_test_actions([\n        TestAction::run(\"let map = new Map();\"),\n        // -0 and +0 should canonicalize to +0\n        TestAction::assert_eq(\"map.getOrInsert(-0, 'minus zero')\", js_str!(\"minus zero\")),\n        TestAction::assert(\"map.has(0)\"),\n        TestAction::assert_eq(\"map.get(0)\", js_str!(\"minus zero\")),\n    ]);\n}\n\n#[test]\nfn get_or_insert_with_undefined_value() {\n    run_test_actions([\n        TestAction::run(\"let map = new Map();\"),\n        TestAction::assert_eq(\"map.getOrInsert('z', undefined)\", JsValue::undefined()),\n        TestAction::assert(\"map.has('z')\"),\n        TestAction::assert_eq(\"map.get('z')\", JsValue::undefined()),\n    ]);\n}\n\n#[test]\nfn get_or_insert_computed_this_not_map() {\n    run_test_actions([TestAction::assert_native_error(\n        \"Map.prototype.getOrInsertComputed.call({}, 'k', x => x)\",\n        JsNativeErrorKind::Type,\n        \"method `Map.prototype.getOrInsertComputed` called on incompatible receiver\",\n    )]);\n}\n\n#[test]\nfn get_or_insert_computed_requires_callable() {\n    run_test_actions([TestAction::assert_native_error(\n        \"new Map().getOrInsertComputed('k', undefined)\",\n        JsNativeErrorKind::Type,\n        \"method `Map.prototype.getOrInsertComputed` called with non-callable callback function\",\n    )]);\n}\n\n#[test]\nfn get_or_insert_computed_not_called_on_hit() {\n    run_test_actions([\n        TestAction::run(\"const m = new Map([['k', 7]]); let calls = 0;\"),\n        TestAction::assert_eq(\n            \"m.getOrInsertComputed('k', (key) => { calls++; return 1; })\",\n            7,\n        ),\n        TestAction::assert_eq(\"calls\", 0),\n        TestAction::assert_eq(\"m.get('k')\", 7),\n    ]);\n}\n\n#[test]\nfn get_or_insert_computed_this_is_undefined_and_key_canonicalized() {\n    run_test_actions([\n        TestAction::run(\n            r#\"\n            const m = new Map();\n            let seenThis, seenKey;\n            const v = m.getOrInsertComputed(-0, function(k) { 'use strict'; seenThis = this; seenKey = k; return 'ok'; });\n        \"#,\n        ),\n        // `this` inside callback is undefined\n        TestAction::assert(\"seenThis === undefined\"),\n        // key argument is canonicalized (-0 → +0)\n        TestAction::assert(\"Object.is(seenKey, 0)\"),\n        TestAction::assert_eq(\"v\", js_str!(\"ok\")),\n        TestAction::assert(\"m.has(0)\"),\n        TestAction::assert_eq(\"m.get(0)\", js_str!(\"ok\")),\n    ]);\n}\n"
  },
  {
    "path": "core/engine/src/builtins/math/mod.rs",
    "content": "//! Boa's implementation of ECMAScript's global `Math` object.\n//!\n//! `Math` is a built-in object that has properties and methods for mathematical constants and functions. It’s not a function object.\n//!\n//! `Math` works with the `Number` type. It doesn't work with `BigInt`.\n//!\n//! More information:\n//!  - [ECMAScript reference][spec]\n//!  - [MDN documentation][mdn]\n//!\n//! [spec]: https://tc39.es/ecma262/#sec-math-object\n//! [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math\n\nuse crate::{\n    Context, JsArgs, JsResult, JsString, JsValue, builtins::BuiltInObject,\n    context::intrinsics::Intrinsics, js_string, object::JsObject, property::Attribute,\n    realm::Realm, string::StaticJsStrings, symbol::JsSymbol,\n};\n\nuse super::{BuiltInBuilder, IntrinsicObject};\n\n#[cfg(test)]\nmod tests;\n\n/// Javascript `Math` object.\n#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]\npub(crate) struct Math;\n\nimpl IntrinsicObject for Math {\n    fn init(realm: &Realm) {\n        let attribute = Attribute::READONLY | Attribute::NON_ENUMERABLE | Attribute::PERMANENT;\n        let builder = BuiltInBuilder::with_intrinsic::<Self>(realm)\n            .static_property(js_string!(\"E\"), std::f64::consts::E, attribute)\n            .static_property(js_string!(\"LN10\"), std::f64::consts::LN_10, attribute)\n            .static_property(js_string!(\"LN2\"), std::f64::consts::LN_2, attribute)\n            .static_property(js_string!(\"LOG10E\"), std::f64::consts::LOG10_E, attribute)\n            .static_property(js_string!(\"LOG2E\"), std::f64::consts::LOG2_E, attribute)\n            .static_property(js_string!(\"PI\"), std::f64::consts::PI, attribute)\n            .static_property(\n                js_string!(\"SQRT1_2\"),\n                std::f64::consts::FRAC_1_SQRT_2,\n                attribute,\n            )\n            .static_property(js_string!(\"SQRT2\"), std::f64::consts::SQRT_2, attribute)\n            .static_method(Self::abs, js_string!(\"abs\"), 1)\n            .static_method(Self::acos, js_string!(\"acos\"), 1)\n            .static_method(Self::acosh, js_string!(\"acosh\"), 1)\n            .static_method(Self::asin, js_string!(\"asin\"), 1)\n            .static_method(Self::asinh, js_string!(\"asinh\"), 1)\n            .static_method(Self::atan, js_string!(\"atan\"), 1)\n            .static_method(Self::atanh, js_string!(\"atanh\"), 1)\n            .static_method(Self::atan2, js_string!(\"atan2\"), 2)\n            .static_method(Self::cbrt, js_string!(\"cbrt\"), 1)\n            .static_method(Self::ceil, js_string!(\"ceil\"), 1)\n            .static_method(Self::clz32, js_string!(\"clz32\"), 1)\n            .static_method(Self::cos, js_string!(\"cos\"), 1)\n            .static_method(Self::cosh, js_string!(\"cosh\"), 1)\n            .static_method(Self::exp, js_string!(\"exp\"), 1)\n            .static_method(Self::expm1, js_string!(\"expm1\"), 1)\n            .static_method(Self::floor, js_string!(\"floor\"), 1)\n            .static_method(Self::fround, js_string!(\"fround\"), 1)\n            .static_method(Self::hypot, js_string!(\"hypot\"), 2)\n            .static_method(Self::imul, js_string!(\"imul\"), 2)\n            .static_method(Self::log, js_string!(\"log\"), 1)\n            .static_method(Self::log1p, js_string!(\"log1p\"), 1)\n            .static_method(Self::log10, js_string!(\"log10\"), 1)\n            .static_method(Self::log2, js_string!(\"log2\"), 1)\n            .static_method(Self::max, js_string!(\"max\"), 2)\n            .static_method(Self::min, js_string!(\"min\"), 2)\n            .static_method(Self::pow, js_string!(\"pow\"), 2)\n            .static_method(Self::random, js_string!(\"random\"), 0)\n            .static_method(Self::round, js_string!(\"round\"), 1)\n            .static_method(Self::sign, js_string!(\"sign\"), 1)\n            .static_method(Self::sin, js_string!(\"sin\"), 1)\n            .static_method(Self::sinh, js_string!(\"sinh\"), 1)\n            .static_method(Self::sqrt, js_string!(\"sqrt\"), 1)\n            .static_method(Self::tan, js_string!(\"tan\"), 1)\n            .static_method(Self::tanh, js_string!(\"tanh\"), 1)\n            .static_method(Self::trunc, js_string!(\"trunc\"), 1)\n            .static_property(\n                JsSymbol::to_string_tag(),\n                Self::NAME,\n                Attribute::READONLY | Attribute::NON_ENUMERABLE | Attribute::CONFIGURABLE,\n            );\n\n        #[cfg(feature = \"float16\")]\n        let builder = builder.static_method(Self::f16round, js_string!(\"f16round\"), 1);\n\n        #[cfg(feature = \"xsum\")]\n        let builder = builder.static_method(Self::sum_precise, js_string!(\"sumPrecise\"), 1);\n\n        builder.build();\n    }\n\n    fn get(intrinsics: &Intrinsics) -> JsObject {\n        intrinsics.objects().math()\n    }\n}\n\nimpl BuiltInObject for Math {\n    const NAME: JsString = StaticJsStrings::MATH;\n}\n\nimpl Math {\n    /// Get the absolute value of a number.\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///  - [MDN documentation][mdn]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-math.abs\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/abs\n    pub(crate) fn abs(_: &JsValue, args: &[JsValue], context: &mut Context) -> JsResult<JsValue> {\n        Ok(args\n            .get_or_undefined(0)\n            // 1. Let n be ? ToNumber(x).\n            .to_number(context)?\n            // 3. If n is -0𝔽, return +0𝔽.\n            // 2. If n is NaN, return NaN.\n            // 4. If n is -∞𝔽, return +∞𝔽.\n            // 5. If n < +0𝔽, return -n.\n            // 6. Return n.\n            .abs()\n            .into())\n    }\n\n    /// Get the arccos of a number.\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///  - [MDN documentation][mdn]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-math.acos\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/acos\n    pub(crate) fn acos(_: &JsValue, args: &[JsValue], context: &mut Context) -> JsResult<JsValue> {\n        Ok(args\n            .get_or_undefined(0)\n            // 1. Let n be ? ToNumber(x).\n            .to_number(context)?\n            // 2. If n is NaN, n > 1𝔽, or n < -1𝔽, return NaN.\n            // 3. If n is 1𝔽, return +0𝔽.\n            // 4. Return an implementation-approximated value representing the result of the inverse cosine of ℝ(n).\n            .acos()\n            .into())\n    }\n\n    /// Get the hyperbolic arccos of a number.\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///  - [MDN documentation][mdn]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-math.acosh\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/acosh\n    pub(crate) fn acosh(_: &JsValue, args: &[JsValue], context: &mut Context) -> JsResult<JsValue> {\n        Ok(args\n            .get_or_undefined(0)\n            // 1. Let n be ? ToNumber(x).\n            .to_number(context)?\n            // 4. If n < 1𝔽, return NaN.\n            // 2. If n is NaN or n is +∞𝔽, return n.\n            // 3. If n is 1𝔽, return +0𝔽.\n            // 5. Return an implementation-approximated value representing the result of the inverse hyperbolic cosine of ℝ(n).\n            .acosh()\n            .into())\n    }\n\n    /// Get the arcsine of a number.\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///  - [MDN documentation][mdn]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-math.asin\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/asin\n    pub(crate) fn asin(_: &JsValue, args: &[JsValue], context: &mut Context) -> JsResult<JsValue> {\n        Ok(args\n            .get_or_undefined(0)\n            // 1. Let n be ? ToNumber(x).\n            .to_number(context)?\n            // 2. If n is NaN, n is +0𝔽, or n is -0𝔽, return n.\n            // 3. If n > 1𝔽 or n < -1𝔽, return NaN.\n            // 4. Return an implementation-approximated value representing the result of the inverse sine of ℝ(n).\n            .asin()\n            .into())\n    }\n\n    /// Get the hyperbolic arcsine of a number.\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///  - [MDN documentation][mdn]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-math.asinh\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/asinh\n    pub(crate) fn asinh(_: &JsValue, args: &[JsValue], context: &mut Context) -> JsResult<JsValue> {\n        Ok(args\n            .get_or_undefined(0)\n            // 1. Let n be ? ToNumber(x).\n            .to_number(context)?\n            // 2. If n is NaN, n is +0𝔽, n is -0𝔽, n is +∞𝔽, or n is -∞𝔽, return n.\n            // 3. Return an implementation-approximated value representing the result of the inverse hyperbolic sine of ℝ(n).\n            .asinh()\n            .into())\n    }\n\n    /// Get the arctangent of a number.\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///  - [MDN documentation][mdn]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-math.atan\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/atan\n    pub(crate) fn atan(_: &JsValue, args: &[JsValue], context: &mut Context) -> JsResult<JsValue> {\n        Ok(args\n            .get_or_undefined(0)\n            // 1. Let n be ? ToNumber(x).\n            .to_number(context)?\n            // 2. If n is NaN, n is +0𝔽, or n is -0𝔽, return n.\n            // 3. If n is +∞𝔽, return an implementation-approximated value representing π / 2.\n            // 4. If n is -∞𝔽, return an implementation-approximated value representing -π / 2.\n            // 5. Return an implementation-approximated value representing the result of the inverse tangent of ℝ(n).\n            .atan()\n            .into())\n    }\n\n    /// Get the hyperbolic arctangent of a number.\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///  - [MDN documentation][mdn]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-math.atanh\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/atanh\n    pub(crate) fn atanh(_: &JsValue, args: &[JsValue], context: &mut Context) -> JsResult<JsValue> {\n        Ok(args\n            .get_or_undefined(0)\n            // 1. Let n be ? ToNumber(x).\n            .to_number(context)?\n            // 2. If n is NaN, n is +0𝔽, or n is -0𝔽, return n.\n            // 3. If n > 1𝔽 or n < -1𝔽, return NaN.\n            // 4. If n is 1𝔽, return +∞𝔽.\n            // 5. If n is -1𝔽, return -∞𝔽.\n            // 6. Return an implementation-approximated value representing the result of the inverse hyperbolic tangent of ℝ(n).\n            .atanh()\n            .into())\n    }\n\n    /// Get the four quadrant arctangent of the quotient y / x.\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///  - [MDN documentation][mdn]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-math.atan2\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/atan2\n    pub(crate) fn atan2(_: &JsValue, args: &[JsValue], context: &mut Context) -> JsResult<JsValue> {\n        // 1. Let ny be ? ToNumber(y).\n        let y = args.get_or_undefined(0).to_number(context)?;\n\n        // 2. Let nx be ? ToNumber(x).\n        let x = args.get_or_undefined(1).to_number(context)?;\n\n        // 4. If ny is +∞𝔽, then\n        // a. If nx is +∞𝔽, return an implementation-approximated value representing π / 4.\n        // b. If nx is -∞𝔽, return an implementation-approximated value representing 3π / 4.\n        // c. Return an implementation-approximated value representing π / 2.\n        // 5. If ny is -∞𝔽, then\n        // a. If nx is +∞𝔽, return an implementation-approximated value representing -π / 4.\n        // b. If nx is -∞𝔽, return an implementation-approximated value representing -3π / 4.\n        // c. Return an implementation-approximated value representing -π / 2.\n        // 6. If ny is +0𝔽, then\n        // a. If nx > +0𝔽 or nx is +0𝔽, return +0𝔽.\n        // b. Return an implementation-approximated value representing π.\n        // 7. If ny is -0𝔽, then\n        // a. If nx > +0𝔽 or nx is +0𝔽, return -0𝔽.\n        // b. Return an implementation-approximated value representing -π.\n        // 8. Assert: ny is finite and is neither +0𝔽 nor -0𝔽.\n        // 9. If ny > +0𝔽, then\n        // a. If nx is +∞𝔽, return +0𝔽.\n        // b. If nx is -∞𝔽, return an implementation-approximated value representing π.\n        // c. If nx is +0𝔽 or nx is -0𝔽, return an implementation-approximated value representing π / 2.\n        // 10. If ny < +0𝔽, then\n        // a. If nx is +∞𝔽, return -0𝔽.\n        // b. If nx is -∞𝔽, return an implementation-approximated value representing -π.\n        // c. If nx is +0𝔽 or nx is -0𝔽, return an implementation-approximated value representing -π / 2.\n        // 11. Assert: nx is finite and is neither +0𝔽 nor -0𝔽.\n        // 12. Return an implementation-approximated value representing the result of the inverse tangent of the quotient ℝ(ny) / ℝ(nx).\n        Ok(y.atan2(x).into())\n    }\n\n    /// Get the cubic root of a number.\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///  - [MDN documentation][mdn]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-math.cbrt\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/cbrt\n    pub(crate) fn cbrt(_: &JsValue, args: &[JsValue], context: &mut Context) -> JsResult<JsValue> {\n        Ok(args\n            .get_or_undefined(0)\n            // 1. Let n be ? ToNumber(x).\n            .to_number(context)?\n            // 2. If n is NaN, n is +0𝔽, n is -0𝔽, n is +∞𝔽, or n is -∞𝔽, return n.\n            // 3. Return an implementation-approximated value representing the result of the cube root of ℝ(n).\n            .cbrt()\n            .into())\n    }\n\n    /// Get lowest integer above a number.\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///  - [MDN documentation][mdn]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-math.ceil\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/ceil\n    pub(crate) fn ceil(_: &JsValue, args: &[JsValue], context: &mut Context) -> JsResult<JsValue> {\n        Ok(args\n            .get_or_undefined(0)\n            // 1. Let n be ? ToNumber(x).\n            .to_number(context)?\n            // 2. If n is NaN, n is +0𝔽, n is -0𝔽, n is +∞𝔽, or n is -∞𝔽, return n.\n            // 3. If n < +0𝔽 and n > -1𝔽, return -0𝔽.\n            // 4. If n is an integral Number, return n.\n            // 5. Return the smallest (closest to -∞) integral Number value that is not less than n.\n            .ceil()\n            .into())\n    }\n\n    /// Get the number of leading zeros in the 32 bit representation of a number\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///  - [MDN documentation][mdn]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-math.clz32\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/clz32\n    pub(crate) fn clz32(_: &JsValue, args: &[JsValue], context: &mut Context) -> JsResult<JsValue> {\n        Ok(args\n            .get_or_undefined(0)\n            // 1. Let n be ? ToUint32(x).\n            .to_u32(context)?\n            // 2. Let p be the number of leading zero bits in the unsigned 32-bit binary representation of n.\n            // 3. Return 𝔽(p).\n            .leading_zeros()\n            .into())\n    }\n\n    /// Get the cosine of a number.\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///  - [MDN documentation][mdn]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-math.cos\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/cos\n    pub(crate) fn cos(_: &JsValue, args: &[JsValue], context: &mut Context) -> JsResult<JsValue> {\n        Ok(args\n            .get_or_undefined(0)\n            // 1. Let n be ? ToNumber(x).\n            .to_number(context)?\n            // 2. If n is NaN, n is +∞𝔽, or n is -∞𝔽, return NaN.\n            // 3. If n is +0𝔽 or n is -0𝔽, return 1𝔽.\n            // 4. Return an implementation-approximated value representing the result of the cosine of ℝ(n).\n            .cos()\n            .into())\n    }\n\n    /// Get the hyperbolic cosine of a number.\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///  - [MDN documentation][mdn]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-math.cosh\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/cosh\n    pub(crate) fn cosh(_: &JsValue, args: &[JsValue], context: &mut Context) -> JsResult<JsValue> {\n        Ok(args\n            .get_or_undefined(0)\n            // 1. Let n be ? ToNumber(x).\n            .to_number(context)?\n            // 2. If n is NaN, return NaN.\n            // 3. If n is +∞𝔽 or n is -∞𝔽, return +∞𝔽.\n            // 4. If n is +0𝔽 or n is -0𝔽, return 1𝔽.\n            // 5. Return an implementation-approximated value representing the result of the hyperbolic cosine of ℝ(n).\n            .cosh()\n            .into())\n    }\n\n    /// Get the power to raise the natural logarithm to get the number.\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///  - [MDN documentation][mdn]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-math.exp\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/exp\n    pub(crate) fn exp(_: &JsValue, args: &[JsValue], context: &mut Context) -> JsResult<JsValue> {\n        Ok(args\n            .get_or_undefined(0)\n            // 1. Let n be ? ToNumber(x).\n            .to_number(context)?\n            // 2. If n is NaN or n is +∞𝔽, return n.\n            // 3. If n is +0𝔽 or n is -0𝔽, return 1𝔽.\n            // 4. If n is -∞𝔽, return +0𝔽.\n            // 5. Return an implementation-approximated value representing the result of the exponential function of ℝ(n).\n            .exp()\n            .into())\n    }\n\n    /// The `Math.expm1()` function returns e^x - 1, where x is the argument, and e the base of\n    /// the natural logarithms. The result is computed in a way that is accurate even when the\n    /// value of x is close 0\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///  - [MDN documentation][mdn]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-math.expm1\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/expm1\n    pub(crate) fn expm1(_: &JsValue, args: &[JsValue], context: &mut Context) -> JsResult<JsValue> {\n        Ok(args\n            .get_or_undefined(0)\n            // 1. Let n be ? ToNumber(x).\n            .to_number(context)?\n            // 2. If n is NaN, n is +0𝔽, n is -0𝔽, or n is +∞𝔽, return n.\n            // 3. If n is -∞𝔽, return -1𝔽.\n            // 4. Return an implementation-approximated value representing the result of subtracting 1 from the exponential function of ℝ(n).\n            .exp_m1()\n            .into())\n    }\n\n    /// Get the highest integer below a number.\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///  - [MDN documentation][mdn]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-math.floor\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/floor\n    pub(crate) fn floor(_: &JsValue, args: &[JsValue], context: &mut Context) -> JsResult<JsValue> {\n        Ok(args\n            .get_or_undefined(0)\n            // 1. Let n be ? ToNumber(x).\n            .to_number(context)?\n            // 2. If n is NaN, n is +0𝔽, n is -0𝔽, n is +∞𝔽, or n is -∞𝔽, return n.\n            // 3. If n < 1𝔽 and n > +0𝔽, return +0𝔽.\n            // 4. If n is an integral Number, return n.\n            // 5. Return the greatest (closest to +∞) integral Number value that is not greater than n.\n            .floor()\n            .into())\n    }\n\n    /// The `Math.f16round()` static method returns the nearest 16-bit half precision\n    /// float representation of a number.\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///  - [MDN documentation][mdn]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-math.round\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/f16round\n    #[allow(clippy::float_cmp)]\n    #[cfg(feature = \"float16\")]\n    pub(crate) fn f16round(\n        _: &JsValue,\n        args: &[JsValue],\n        context: &mut Context,\n    ) -> JsResult<JsValue> {\n        let num = args\n            .get_or_undefined(0)\n            //1. Let n be ? ToNumber(x).\n            .to_number(context)?;\n\n        // 2. If n is NaN, return NaN.\n        // 3. If n is one of +0𝔽, -0𝔽, +∞𝔽, or -∞𝔽, return n.\n        // 4. Let n16 be the result of converting n to IEEE 754-2019 binary16 format using roundTiesToEven mode.\n        // 5. Let n64 be the result of converting n16 to IEEE 754-2019 binary64 format.\n        // 6. Return the ECMAScript Number value corresponding to n64.\n        Ok(float16::f16::from_f64(num).to_f64().into())\n    }\n\n    /// Get the nearest 32-bit single precision float representation of a number.\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///  - [MDN documentation][mdn]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-math.fround\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/fround\n    pub(crate) fn fround(\n        _: &JsValue,\n        args: &[JsValue],\n        context: &mut Context,\n    ) -> JsResult<JsValue> {\n        // 1. Let n be ? ToNumber(x).\n        let x = args.get_or_undefined(0).to_number(context)?;\n\n        // 2. If n is NaN, return NaN.\n        // 3. If n is one of +0𝔽, -0𝔽, +∞𝔽, or -∞𝔽, return n.\n        // 4. Let n32 be the result of converting n to a value in IEEE 754-2019 binary32 format using roundTiesToEven mode.\n        // 5. Let n64 be the result of converting n32 to a value in IEEE 754-2019 binary64 format.\n        // 6. Return the ECMAScript Number value corresponding to n64.\n        Ok(f64::from(x as f32).into())\n    }\n\n    /// Get an approximation of the square root of the sum of squares of all arguments.\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///  - [MDN documentation][mdn]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-math.hypot\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/hypot\n    pub(crate) fn hypot(_: &JsValue, args: &[JsValue], context: &mut Context) -> JsResult<JsValue> {\n        // 1. Let coerced be a new empty List.\n        // 2. For each element arg of args, do\n        // a. Let n be ? ToNumber(arg).\n        // b. Append n to coerced.\n        // 3. For each element number of coerced, do\n        // 5. For each element number of coerced, do\n        let mut result = 0f64;\n        for arg in args {\n            let num = arg.to_number(context)?;\n            // a. If number is +∞𝔽 or number is -∞𝔽, return +∞𝔽.\n            // 4. Let onlyZero be true.\n            // a. If number is NaN, return NaN.\n            // b. If number is neither +0𝔽 nor -0𝔽, set onlyZero to false.\n            // 6. If onlyZero is true, return +0𝔽.\n            // 7. Return an implementation-approximated value representing the square root of the sum of squares of the mathematical values of the elements of coerced.\n            result = result.hypot(num);\n        }\n\n        Ok(result.into())\n    }\n\n    /// Get the result of the C-like 32-bit multiplication of the two parameters.\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///  - [MDN documentation][mdn]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-math.imul\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/imul\n    pub(crate) fn imul(_: &JsValue, args: &[JsValue], context: &mut Context) -> JsResult<JsValue> {\n        // 1. Let a be ℝ(? ToUint32(x)).\n        let x = args.get_or_undefined(0).to_u32(context)?;\n\n        // 2. Let b be ℝ(? ToUint32(y)).\n        let y = args.get_or_undefined(1).to_u32(context)?;\n\n        // 3. Let product be (a × b) modulo 2^32.\n        // 4. If product ≥ 2^31, return 𝔽(product - 2^32); otherwise return 𝔽(product).\n        Ok((x.wrapping_mul(y) as i32).into())\n    }\n\n    /// Get the natural logarithm of a number.\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///  - [MDN documentation][mdn]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-math.log\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/log\n    pub(crate) fn log(_: &JsValue, args: &[JsValue], context: &mut Context) -> JsResult<JsValue> {\n        Ok(args\n            .get_or_undefined(0)\n            // 1. Let n be ? ToNumber(x).\n            .to_number(context)?\n            // 2. If n is NaN or n is +∞𝔽, return n.\n            // 3. If n is 1𝔽, return +0𝔽.\n            // 4. If n is +0𝔽 or n is -0𝔽, return -∞𝔽.\n            // 5. If n < +0𝔽, return NaN.\n            // 6. Return an implementation-approximated value representing the result of the natural logarithm of ℝ(n).\n            .ln()\n            .into())\n    }\n\n    /// Get approximation to the natural logarithm of 1 + x.\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///  - [MDN documentation][mdn]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-math.log1p\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/log1p\n    pub(crate) fn log1p(_: &JsValue, args: &[JsValue], context: &mut Context) -> JsResult<JsValue> {\n        Ok(args\n            .get_or_undefined(0)\n            // 1. Let n be ? ToNumber(x).\n            .to_number(context)?\n            // 2. If n is NaN, n is +0𝔽, n is -0𝔽, or n is +∞𝔽, return n.\n            // 3. If n is -1𝔽, return -∞𝔽.\n            // 4. If n < -1𝔽, return NaN.\n            // 5. Return an implementation-approximated value representing the result of the natural logarithm of 1 + ℝ(n).\n            .ln_1p()\n            .into())\n    }\n\n    /// Get the base 10 logarithm of the number.\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///  - [MDN documentation][mdn]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-math.log10\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/log10\n    pub(crate) fn log10(_: &JsValue, args: &[JsValue], context: &mut Context) -> JsResult<JsValue> {\n        Ok(args\n            .get_or_undefined(0)\n            // 1. Let n be ? ToNumber(x).\n            .to_number(context)?\n            // 2. If n is NaN or n is +∞𝔽, return n.\n            // 3. If n is 1𝔽, return +0𝔽.\n            // 4. If n is +0𝔽 or n is -0𝔽, return -∞𝔽.\n            // 5. If n < +0𝔽, return NaN.\n            // 6. Return an implementation-approximated value representing the result of the base 10 logarithm of ℝ(n).\n            .log10()\n            .into())\n    }\n\n    /// Get the base 2 logarithm of the number.\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///  - [MDN documentation][mdn]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-math.log2\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/log2\n    pub(crate) fn log2(_: &JsValue, args: &[JsValue], context: &mut Context) -> JsResult<JsValue> {\n        Ok(args\n            .get_or_undefined(0)\n            // 1. Let n be ? ToNumber(x).\n            .to_number(context)?\n            // 2. If n is NaN or n is +∞𝔽, return n.\n            // 3. If n is 1𝔽, return +0𝔽.\n            // 4. If n is +0𝔽 or n is -0𝔽, return -∞𝔽.\n            // 5. If n < +0𝔽, return NaN.\n            // 6. Return an implementation-approximated value representing the result of the base 2 logarithm of ℝ(n).\n            .log2()\n            .into())\n    }\n\n    /// Get the maximum of several numbers.\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///  - [MDN documentation][mdn]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-math.max\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/max\n    pub(crate) fn max(_: &JsValue, args: &[JsValue], context: &mut Context) -> JsResult<JsValue> {\n        // 1. Let coerced be a new empty List.\n        // 2. For each element arg of args, do\n        // b. Append n to coerced.\n        // 3. Let highest be -∞𝔽.\n        let mut highest = f64::NEG_INFINITY;\n\n        // 4. For each element number of coerced, do\n        for arg in args {\n            // a. Let n be ? ToNumber(arg).\n            let num = arg.to_number(context)?;\n\n            highest = if highest.is_nan() {\n                continue;\n            } else if num.is_nan() {\n                // a. If number is NaN, return NaN.\n                f64::NAN\n            } else {\n                match (highest, num) {\n                    // b. When x and y are +0𝔽 -0𝔽, return +0𝔽.\n                    (x, y) if x == 0f64 && y == 0f64 && x.signum() != y.signum() => 0f64,\n                    // c. Otherwise, return the maximum value.\n                    (x, y) => x.max(y),\n                }\n            };\n        }\n        // 5. Return highest.\n        Ok(highest.into())\n    }\n\n    /// Get the minimum of several numbers.\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///  - [MDN documentation][mdn]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-math.min\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/min\n    pub(crate) fn min(_: &JsValue, args: &[JsValue], context: &mut Context) -> JsResult<JsValue> {\n        // 1. Let coerced be a new empty List.\n        // 2. For each element arg of args, do\n        // b. Append n to coerced.\n        // 3. Let lowest be +∞𝔽.\n        let mut lowest = f64::INFINITY;\n\n        // 4. For each element number of coerced, do\n        for arg in args {\n            // a. Let n be ? ToNumber(arg).\n            let num = arg.to_number(context)?;\n\n            lowest = if lowest.is_nan() {\n                continue;\n            } else if num.is_nan() {\n                // a. If number is NaN, return NaN.\n                f64::NAN\n            } else {\n                match (lowest, num) {\n                    // b. When x and y are +0𝔽 -0𝔽, return -0𝔽.\n                    (x, y) if x == 0f64 && y == 0f64 && x.signum() != y.signum() => -0f64,\n                    // c. Otherwise, return the minimum value.\n                    (x, y) => x.min(y),\n                }\n            };\n        }\n        // 5. Return lowest.\n        Ok(lowest.into())\n    }\n\n    /// Raise a number to a power.\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///  - [MDN documentation][mdn]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-math.pow\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/pow\n    #[allow(clippy::float_cmp)]\n    pub(crate) fn pow(_: &JsValue, args: &[JsValue], context: &mut Context) -> JsResult<JsValue> {\n        // 1. Set base to ? ToNumber(base).\n        let x = args.get_or_undefined(0).to_number(context)?;\n\n        // 2. Set exponent to ? ToNumber(exponent).\n        let y = args.get_or_undefined(1).to_number(context)?;\n\n        // 3. Return Number::exponentiate(base, exponent).\n\n        // https://github.com/rust-lang/rust/issues/60468\n\n        // https://tc39.es/ecma262/multipage/ecmascript-data-types-and-values.html#sec-numeric-types-number-exponentiate\n        // 6.1.6.1.3 Number::exponentiate ( base, exponent )\n        //  1. If exponent is NaN, return NaN.\n        if y.is_nan() {\n            return Ok(f64::NAN.into());\n        }\n\n        // 9. If exponent is +∞𝔽, then\n        //   a. If abs(ℝ(base)) > 1, return +∞𝔽.\n        //   b. If abs(ℝ(base)) = 1, return NaN.\n        //   c. If abs(ℝ(base)) < 1, return +0𝔽.\n        // 10. If exponent is -∞𝔽, then\n        //   a. If abs(ℝ(base)) > 1, return +0𝔽.\n        //   b. If abs(ℝ(base)) = 1, return NaN.\n        //   c. If abs(ℝ(base)) < 1, return +∞𝔽.\n        if f64::abs(x) == 1f64 && y.is_infinite() {\n            return Ok(f64::NAN.into());\n        }\n\n        Ok(x.powf(y).into())\n    }\n\n    /// Generate a random floating-point number between `0` and `1`.\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///  - [MDN documentation][mdn]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-math.random\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/random\n    #[allow(clippy::unnecessary_wraps)]\n    pub(crate) fn random(_: &JsValue, _: &[JsValue], _: &mut Context) -> JsResult<JsValue> {\n        // NOTE: Each Math.random function created for distinct realms must produce a distinct sequence of values from successive calls.\n        Ok(rand::random::<f64>().into())\n    }\n\n    /// Round a number to the nearest integer.\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///  - [MDN documentation][mdn]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-math.round\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/round\n    #[allow(clippy::float_cmp)]\n    pub(crate) fn round(_: &JsValue, args: &[JsValue], context: &mut Context) -> JsResult<JsValue> {\n        let num = args\n            .get_or_undefined(0)\n            //1. Let n be ? ToNumber(x).\n            .to_number(context)?;\n\n        //2. If n is NaN, +∞𝔽, -∞𝔽, or an integral Number, return n.\n        //3. If n < 0.5𝔽 and n > +0𝔽, return +0𝔽.\n        //4. If n < +0𝔽 and n ≥ -0.5𝔽, return -0𝔽.\n        //5. Return the integral Number closest to n, preferring the Number closer to +∞ in the case of a tie.\n\n        if num.fract() == -0.5 {\n            Ok(num.ceil().into())\n        } else {\n            Ok(num.round().into())\n        }\n    }\n\n    /// Get the sign of a number.\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///  - [MDN documentation][mdn]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-math.sign\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/sign\n    pub(crate) fn sign(_: &JsValue, args: &[JsValue], context: &mut Context) -> JsResult<JsValue> {\n        // 1. Let n be ? ToNumber(x).\n        let n = args.get_or_undefined(0).to_number(context)?;\n\n        // 2. If n is NaN, n is +0𝔽, or n is -0𝔽, return n.\n        if n == 0f64 {\n            return Ok(n.into());\n        }\n        // 3. If n < +0𝔽, return -1𝔽.\n        // 4. Return 1𝔽.\n        Ok(n.signum().into())\n    }\n\n    /// Get the sine of a number.\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///  - [MDN documentation][mdn]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-math.sin\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/sin\n    pub(crate) fn sin(_: &JsValue, args: &[JsValue], context: &mut Context) -> JsResult<JsValue> {\n        Ok(args\n            .get_or_undefined(0)\n            // 1. Let n be ? ToNumber(x).\n            .to_number(context)?\n            // 2. If n is NaN, n is +0𝔽, or n is -0𝔽, return n.\n            // 3. If n is +∞𝔽 or n is -∞𝔽, return NaN.\n            // 4. Return an implementation-approximated value representing the result of the sine of ℝ(n).\n            .sin()\n            .into())\n    }\n\n    /// Get the hyperbolic sine of a number.\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///  - [MDN documentation][mdn]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-math.sinh\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/sinh\n    pub(crate) fn sinh(_: &JsValue, args: &[JsValue], context: &mut Context) -> JsResult<JsValue> {\n        Ok(args\n            .get_or_undefined(0)\n            // 1. Let n be ? ToNumber(x).\n            .to_number(context)?\n            // 2. If n is NaN, n is +0𝔽, n is -0𝔽, n is +∞𝔽, or n is -∞𝔽, return n.\n            // 3. Return an implementation-approximated value representing the result of the hyperbolic sine of ℝ(n).\n            .sinh()\n            .into())\n    }\n\n    /// Get the square root of a number.\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///  - [MDN documentation][mdn]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-math.sqrt\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/sqrt\n    pub(crate) fn sqrt(_: &JsValue, args: &[JsValue], context: &mut Context) -> JsResult<JsValue> {\n        Ok(args\n            .get_or_undefined(0)\n            // 1. Let n be ? ToNumber(x).\n            .to_number(context)?\n            // 2. If n is NaN, n is +0𝔽, n is -0𝔽, or n is +∞𝔽, return n.\n            // 3. If n < +0𝔽, return NaN.\n            // 4. Return an implementation-approximated value representing the result of the square root of ℝ(n).\n            .sqrt()\n            .into())\n    }\n\n    /// Get the tangent of a number.\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///  - [MDN documentation][mdn]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-math.tan\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/tan\n    pub(crate) fn tan(_: &JsValue, args: &[JsValue], context: &mut Context) -> JsResult<JsValue> {\n        Ok(args\n            .get_or_undefined(0)\n            // 1. Let n be ? ToNumber(x).\n            .to_number(context)?\n            // 2. If n is NaN, n is +0𝔽, or n is -0𝔽, return n.\n            // 3. If n is +∞𝔽, or n is -∞𝔽, return NaN.\n            // 4. Return an implementation-approximated value representing the result of the tangent of ℝ(n).\n            .tan()\n            .into())\n    }\n\n    /// Get the hyperbolic tangent of a number.\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///  - [MDN documentation][mdn]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-math.tanh\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/tanh\n    pub(crate) fn tanh(_: &JsValue, args: &[JsValue], context: &mut Context) -> JsResult<JsValue> {\n        Ok(args\n            .get_or_undefined(0)\n            // 1. Let n be ? ToNumber(x).\n            .to_number(context)?\n            // 2. If n is NaN, n is +0𝔽, or n is -0𝔽, return n.\n            // 3. If n is +∞𝔽, return 1𝔽.\n            // 4. If n is -∞𝔽, return -1𝔽.\n            // 5. Return an implementation-approximated value representing the result of the hyperbolic tangent of ℝ(n).\n            .tanh()\n            .into())\n    }\n\n    /// Get the integer part of a number.\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///  - [MDN documentation][mdn]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-math.trunc\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/trunc\n    pub(crate) fn trunc(_: &JsValue, args: &[JsValue], context: &mut Context) -> JsResult<JsValue> {\n        Ok(args\n            .get_or_undefined(0)\n            // 1. Let n be ? ToNumber(x).\n            .to_number(context)?\n            // 2. If n is NaN, n is +0𝔽, n is -0𝔽, n is +∞𝔽, or n is -∞𝔽, return n.\n            // 3. If n < 1𝔽 and n > +0𝔽, return +0𝔽.\n            // 4. If n < +0𝔽 and n > -1𝔽, return -0𝔽.\n            // 5. Return the integral Number nearest n in the direction of +0𝔽.\n            .trunc()\n            .into())\n    }\n\n    /// Sum each value in an iterable\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///  - [MDN documentation][mdn]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-math.sumprecise\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/sumPrecise\n    #[cfg(feature = \"xsum\")]\n    pub(crate) fn sum_precise(\n        _: &JsValue,\n        args: &[JsValue],\n        context: &mut Context,\n    ) -> JsResult<JsValue> {\n        const ITERATION_MAX: u64 = 2u64.pow(53) - 1;\n        use xsum::{Xsum, XsumLarge, XsumSmall, XsumVariant, constants::XSUM_THRESHOLD};\n\n        use crate::{builtins::iterable::IteratorHint, js_error};\n\n        let items = args.get_or_undefined(0);\n        // NOTE (nekevss): inline `RequireObjectCoercible`\n        // 1. Perform ? RequireObjectCoercible(items).\n        // RequireObjectCoercible, 1. If argument is either undefined or null, throw a TypeError exception.\n        // RequireObjectCoercible, 2. Return argument.\n        if items.is_null_or_undefined() {\n            return Err(js_error!(TypeError: \"value must be object coercible.\"));\n        }\n\n        // 2. Let iteratorRecord be ? GetIterator(items, sync).\n        let mut iterator_record = items.get_iterator(IteratorHint::Sync, context)?;\n        // 3. Let state be minus-zero.\n        // 4. Let sum be 0.\n        let length = if iterator_record.iterator().is_array() {\n            iterator_record\n                .iterator()\n                .length_of_array_like(context)\n                .ok()\n        } else {\n            None\n        };\n        let mut sum = if let Some(length) = length {\n            if length <= XSUM_THRESHOLD as u64 {\n                XsumVariant::Small(XsumSmall::new())\n            } else {\n                XsumVariant::Large(XsumLarge::new())\n            }\n        } else {\n            XsumVariant::Small(XsumSmall::new())\n        };\n        // 5. Let count be 0.\n        let mut count = 0;\n        // 6. Let next be not-started.\n        // 7. Repeat, while next is not done,\n        // a. Set next to ? IteratorStepValue(iteratorRecord).\n        while let Some(next) = iterator_record.step_value(context)? {\n            // b. If next is not done, then\n            // i. If count ≥ 2**53 - 1, then\n            if count >= ITERATION_MAX {\n                // 1. NOTE: This step is not expected to be reached in practice and is\n                // included only so that implementations may rely on inputs being\n                // \"reasonably sized\" without violating this specification.\n                // 2. Let error be ThrowCompletion(a newly created RangeError object).\n                let err = Err(js_error!(RangeError: \"Summation exceeded maximum iterations\"));\n                // 3. Return ? IteratorClose(iteratorRecord, error).\n                return iterator_record.close(err, context);\n            }\n            // ii. If next is not a Number, then\n            let Some(number) = next.as_number() else {\n                // 1. Let error be ThrowCompletion(a newly created TypeError object).\n                let err = Err(js_error!(TypeError: \"sumPrecise can only be called on a number.\"));\n                // 2. Return ? IteratorClose(iteratorRecord, error).\n                return iterator_record.close(err, context);\n            };\n            // iii. Let n be next.\n            // iv. If state is not not-a-number, then\n            // 1. If n is NaN, then\n            // a. Set state to not-a-number.\n            // 2. Else if n is +∞𝔽, then\n            // a. If state is minus-infinity, set state to not-a-number.\n            // b. Else, set state to plus-infinity.\n            // 3. Else if n is -∞𝔽, then\n            // a. If state is plus-infinity, set state to not-a-number.\n            // b. Else, set state to minus-infinity.\n            // 4. Else if n is not -0𝔽 and state is either minus-zero or finite, then\n            // a. Set state to finite.\n            // b. Set sum to sum + ℝ(n).\n            sum.add(number);\n            // v. Set count to count + 1.\n            count += 1;\n        }\n        // 8. If state is not-a-number, return NaN.\n        // 9. If state is plus-infinity, return +∞𝔽.\n        // 10. If state is minus-infinity, return -∞𝔽.\n        // 11. If state is minus-zero, return -0𝔽.\n        // 12. Return 𝔽(sum).\n        Ok(sum.sum().into())\n    }\n}\n"
  },
  {
    "path": "core/engine/src/builtins/math/tests.rs",
    "content": "use crate::{TestAction, run_test_actions};\n\n#[test]\nfn abs() {\n    run_test_actions([\n        TestAction::assert_eq(\"Math.abs(3 - 5)\", 2.0),\n        TestAction::assert_eq(\"Math.abs(1.23456 - 7.89012)\", 6.655_559_999_999_999_5),\n    ]);\n}\n\n#[test]\nfn acos() {\n    run_test_actions([\n        TestAction::assert_eq(\"Math.acos(8 / 10)\", 0.643_501_108_793_284_3),\n        TestAction::assert_eq(\"Math.acos(5 / 3)\", f64::NAN),\n        TestAction::assert_eq(\"Math.acos(1)\", 0.0),\n        TestAction::assert_eq(\"Math.acos(2)\", f64::NAN),\n    ]);\n}\n\n#[test]\nfn acosh() {\n    run_test_actions([\n        TestAction::assert_eq(\"Math.acosh(2)\", 1.316_957_896_924_816_6),\n        TestAction::assert_eq(\"Math.acosh(-1)\", f64::NAN),\n        TestAction::assert_eq(\"Math.acosh(0.5)\", f64::NAN),\n    ]);\n}\n\n#[test]\nfn asin() {\n    run_test_actions([\n        TestAction::assert_eq(\"Math.asin(6 / 10)\", 0.643_501_108_793_284_4),\n        TestAction::assert_eq(\"Math.asin(5 / 3)\", f64::NAN),\n    ]);\n}\n\n#[test]\nfn asinh() {\n    run_test_actions([\n        TestAction::assert_eq(\"Math.asinh(1)\", 0.881_373_587_019_543),\n        TestAction::assert_eq(\"Math.asinh(0)\", 0.0),\n    ]);\n}\n\n#[test]\nfn atan() {\n    run_test_actions([\n        TestAction::assert_eq(\"Math.atan(1)\", std::f64::consts::FRAC_PI_4),\n        TestAction::assert_eq(\"Math.atan(0)\", 0.0),\n        TestAction::assert_eq(\"Math.atan(-0)\", -0.0),\n    ]);\n}\n\n#[test]\nfn atan2() {\n    run_test_actions([\n        TestAction::assert_eq(\"Math.atan2(90, 15)\", 1.405_647_649_380_269_9),\n        TestAction::assert_eq(\"Math.atan2(15, 90)\", 0.165_148_677_414_626_83),\n    ]);\n}\n\n#[test]\nfn cbrt() {\n    run_test_actions([\n        TestAction::assert_eq(\"Math.cbrt(64)\", 4.0),\n        TestAction::assert_eq(\"Math.cbrt(-1)\", -1.0),\n        TestAction::assert_eq(\"Math.cbrt(1)\", 1.0),\n    ]);\n}\n\n#[test]\nfn ceil() {\n    run_test_actions([\n        TestAction::assert_eq(\"Math.ceil(1.95)\", 2.0),\n        TestAction::assert_eq(\"Math.ceil(4)\", 4.0),\n        TestAction::assert_eq(\"Math.ceil(-7.004)\", -7.0),\n    ]);\n}\n\n#[test]\nfn clz32() {\n    run_test_actions([\n        TestAction::assert_eq(\"Math.clz32()\", 32),\n        TestAction::assert_eq(\"Math.clz32({})\", 32),\n        TestAction::assert_eq(\"Math.clz32(-173)\", 0),\n        TestAction::assert_eq(\"Math.clz32('1')\", 31),\n        TestAction::assert_eq(\"Math.clz32(2147483647)\", 1),\n        TestAction::assert_eq(\"Math.clz32(Infinity)\", 32),\n        TestAction::assert_eq(\"Math.clz32(true)\", 31),\n        TestAction::assert_eq(\"Math.clz32(0)\", 32),\n    ]);\n}\n\n#[test]\nfn cos() {\n    run_test_actions([\n        TestAction::assert_eq(\"Math.cos(0)\", 1.0),\n        TestAction::assert_eq(\"Math.cos(1)\", 0.540_302_305_868_139_8),\n    ]);\n}\n\n#[test]\nfn cosh() {\n    run_test_actions([\n        TestAction::assert_eq(\"Math.cosh(0)\", 1.0),\n        TestAction::assert_eq(\"Math.cosh(1)\", 1.543_080_634_815_243_7),\n        TestAction::assert_eq(\"Math.cosh(-1)\", 1.543_080_634_815_243_7),\n    ]);\n}\n\n#[test]\nfn exp() {\n    run_test_actions([\n        TestAction::assert_eq(\"Math.exp(0)\", 1.0),\n        TestAction::assert_eq(\"Math.exp(-1)\", 0.367_879_441_171_442_33),\n        TestAction::assert_eq(\"Math.exp(2)\", 7.389_056_098_930_65),\n    ]);\n}\n\n#[test]\nfn expm1() {\n    run_test_actions([\n        TestAction::assert_eq(\"Math.expm1()\", f64::NAN),\n        TestAction::assert_eq(\"Math.expm1({})\", f64::NAN),\n        TestAction::assert_with_op(\"Math.expm1(1)\", |v, _| {\n            float_cmp::approx_eq!(f64, v.as_number().unwrap(), 1.718_281_828_459_045)\n        }),\n        TestAction::assert_with_op(\"Math.expm1(-1)\", |v, _| {\n            float_cmp::approx_eq!(f64, v.as_number().unwrap(), -0.632_120_558_828_557_7)\n        }),\n        TestAction::assert_with_op(\"Math.expm1(0)\", |v, _| {\n            float_cmp::approx_eq!(f64, v.as_number().unwrap(), 0.0)\n        }),\n        TestAction::assert_with_op(\"Math.expm1(2)\", |v, _| {\n            float_cmp::approx_eq!(f64, v.as_number().unwrap(), 6.389_056_098_930_65)\n        }),\n    ]);\n}\n\n#[test]\nfn floor() {\n    run_test_actions([\n        TestAction::assert_eq(\"Math.floor(1.95)\", 1.0),\n        TestAction::assert_eq(\"Math.floor(-3.01)\", -4.0),\n        TestAction::assert_eq(\"Math.floor(3.01)\", 3.0),\n    ]);\n}\n\n#[test]\nfn fround() {\n    run_test_actions([\n        TestAction::assert_eq(\"Math.fround(NaN)\", f64::NAN),\n        TestAction::assert_eq(\"Math.fround(Infinity)\", f64::INFINITY),\n        TestAction::assert_eq(\"Math.fround(5)\", 5.0),\n        TestAction::assert_eq(\"Math.fround(5.5)\", 5.5),\n        TestAction::assert_eq(\"Math.fround(5.05)\", 5.050_000_190_734_863),\n        TestAction::assert_eq(\"Math.fround(-5.05)\", -5.050_000_190_734_863),\n        TestAction::assert_eq(\"Math.fround()\", f64::NAN),\n    ]);\n}\n\n#[test]\nfn hypot() {\n    run_test_actions([\n        TestAction::assert_eq(\"Math.hypot()\", 0.0),\n        TestAction::assert_eq(\"Math.hypot(3, 4)\", 5.0),\n        TestAction::assert_eq(\"Math.hypot(5, 12)\", 13.0),\n        TestAction::assert_eq(\"Math.hypot(3, 4, -5)\", 7.071_067_811_865_475_5),\n        TestAction::assert_eq(\"Math.hypot(4, [5], 6)\", 8.774_964_387_392_123),\n        TestAction::assert_eq(\"Math.hypot(3, -Infinity)\", f64::INFINITY),\n        TestAction::assert_eq(\"Math.hypot(12)\", 12.0),\n    ]);\n}\n\n#[test]\nfn imul() {\n    run_test_actions([\n        TestAction::assert_eq(\"Math.imul(3, 4)\", 12),\n        TestAction::assert_eq(\"Math.imul(-5, 12)\", -60),\n        TestAction::assert_eq(\"Math.imul(0xffffffff, 5)\", -5),\n        TestAction::assert_eq(\"Math.imul(0xfffffffe, 5)\", -10),\n        TestAction::assert_eq(\"Math.imul(12)\", 0),\n        TestAction::assert_eq(\"Math.imul()\", 0),\n    ]);\n}\n\n#[test]\nfn log() {\n    run_test_actions([\n        TestAction::assert_eq(\"Math.log(1)\", 0.0),\n        TestAction::assert_eq(\"Math.log(10)\", std::f64::consts::LN_10),\n        TestAction::assert_eq(\"Math.log(-1)\", f64::NAN),\n    ]);\n}\n\n#[test]\nfn log1p() {\n    run_test_actions([\n        TestAction::assert_eq(\"Math.log1p(1)\", std::f64::consts::LN_2),\n        TestAction::assert_eq(\"Math.log1p(0)\", 0.0),\n        TestAction::assert_eq(\"Math.log1p(-0.9999999999999999)\", -36.736_800_569_677_1),\n        TestAction::assert_eq(\"Math.log1p(-1)\", f64::NEG_INFINITY),\n        TestAction::assert_eq(\"Math.log1p(-1.000000000000001)\", f64::NAN),\n        TestAction::assert_eq(\"Math.log1p(-2)\", f64::NAN),\n        TestAction::assert_eq(\"Math.log1p()\", f64::NAN),\n    ]);\n}\n\n#[test]\nfn log10() {\n    run_test_actions([\n        TestAction::assert_eq(\"Math.log10(2)\", std::f64::consts::LOG10_2),\n        TestAction::assert_eq(\"Math.log10(1)\", 0.0),\n        TestAction::assert_eq(\"Math.log10(-2)\", f64::NAN),\n    ]);\n}\n\n#[test]\nfn log2() {\n    run_test_actions([\n        TestAction::assert_eq(\"Math.log2(3)\", 1.584_962_500_721_156),\n        TestAction::assert_eq(\"Math.log2(1)\", 0.0),\n        TestAction::assert_eq(\"Math.log2(-2)\", f64::NAN),\n    ]);\n}\n\n#[test]\nfn max() {\n    run_test_actions([\n        TestAction::assert_eq(\"Math.max(10, 20)\", 20.0),\n        TestAction::assert_eq(\"Math.max(-10, -20)\", -10.0),\n        TestAction::assert_eq(\"Math.max(-10, 20)\", 20.0),\n    ]);\n}\n\n#[test]\nfn min() {\n    run_test_actions([\n        TestAction::assert_eq(\"Math.min(10, 20)\", 10.0),\n        TestAction::assert_eq(\"Math.min(-10, -20)\", -20.0),\n        TestAction::assert_eq(\"Math.min(-10, 20)\", -10.0),\n    ]);\n}\n\n#[test]\nfn pow() {\n    run_test_actions([\n        TestAction::assert_eq(\"Math.pow(2, 10)\", 1_024.0),\n        TestAction::assert_eq(\"Math.pow(-7, 2)\", 49.0),\n        TestAction::assert_eq(\"Math.pow(4, 0.5)\", 2.0),\n        TestAction::assert_eq(\"Math.pow(7, -2)\", 0.020_408_163_265_306_12),\n        TestAction::assert_eq(\"Math.pow(1, NaN)\", f64::NAN),\n        TestAction::assert_eq(\"Math.pow(1, Infinity)\", f64::NAN),\n        TestAction::assert_eq(\"Math.pow(1, -Infinity)\", f64::NAN),\n        TestAction::assert_eq(\"Math.pow(-1, Infinity)\", f64::NAN),\n        TestAction::assert_eq(\"Math.pow(-1, -Infinity)\", f64::NAN),\n    ]);\n}\n\n#[test]\nfn round() {\n    run_test_actions([\n        TestAction::assert_eq(\"Math.round(20.5)\", 21.0),\n        TestAction::assert_eq(\"Math.round(-20.3)\", -20.0),\n    ]);\n}\n\n#[test]\nfn sign() {\n    run_test_actions([\n        TestAction::assert_eq(\"Math.sign(3)\", 1.0),\n        TestAction::assert_eq(\"Math.sign(-3)\", -1.0),\n        TestAction::assert_eq(\"Math.sign(0)\", 0.0),\n    ]);\n}\n\n#[test]\nfn sin() {\n    run_test_actions([\n        TestAction::assert_eq(\"Math.sin(0)\", 0.0),\n        TestAction::assert_eq(\"Math.sin(1)\", 0.841_470_984_807_896_5),\n    ]);\n}\n\n#[test]\nfn sinh() {\n    run_test_actions([\n        TestAction::assert_eq(\"Math.sinh(0)\", 0.0),\n        TestAction::assert_eq(\"Math.sinh(1)\", 1.175_201_193_643_801_4),\n    ]);\n}\n\n#[test]\nfn sqrt() {\n    run_test_actions([\n        TestAction::assert_eq(\"Math.sqrt(0)\", 0.0),\n        TestAction::assert_eq(\"Math.sqrt(2)\", std::f64::consts::SQRT_2),\n        TestAction::assert_eq(\"Math.sqrt(9)\", 3.0),\n    ]);\n}\n\n#[test]\nfn tan() {\n    run_test_actions([TestAction::assert_with_op(\"Math.tan(1.1)\", |v, _| {\n        float_cmp::approx_eq!(f64, v.as_number().unwrap(), 1.964_759_657_248_652_5)\n    })]);\n}\n\n#[test]\nfn tanh() {\n    run_test_actions([\n        TestAction::assert_eq(\"Math.tanh(1)\", 0.761_594_155_955_764_9),\n        TestAction::assert_eq(\"Math.tanh(0)\", 0.0),\n    ]);\n}\n\n#[test]\nfn trunc() {\n    run_test_actions([\n        TestAction::assert_eq(\"Math.trunc(13.37)\", 13.0),\n        TestAction::assert_eq(\"Math.trunc(0.123)\", 0.0),\n    ]);\n}\n"
  },
  {
    "path": "core/engine/src/builtins/mod.rs",
    "content": "//! Boa's ECMAScript built-in object implementations, e.g. Object, String, Math, Array, etc.\n\npub mod array;\npub mod array_buffer;\npub mod async_function;\npub mod async_generator;\npub mod async_generator_function;\npub mod atomics;\npub mod bigint;\npub mod boolean;\npub mod dataview;\npub mod date;\npub mod error;\npub mod eval;\npub mod finalization_registry;\npub mod function;\npub mod generator;\npub mod generator_function;\n#[cfg(feature = \"annex-b\")]\npub mod is_html_dda;\npub mod iterable;\npub mod json;\npub mod map;\npub mod math;\npub mod number;\npub mod object;\npub mod promise;\npub mod proxy;\npub mod reflect;\npub mod regexp;\npub mod set;\npub mod string;\npub mod symbol;\npub mod typed_array;\npub mod uri;\npub mod weak;\npub mod weak_map;\npub mod weak_set;\n\nmod builder;\n\nuse builder::BuiltInBuilder;\nuse error::Error;\nuse num_traits::Zero;\n\n#[cfg(feature = \"annex-b\")]\npub mod escape;\n\n#[cfg(feature = \"intl\")]\npub mod intl;\n\n// TODO: remove `cfg` when `Temporal` gets to stage 4.\n#[cfg(any(feature = \"intl\", feature = \"temporal\"))]\npub(crate) mod options;\n\n#[cfg(feature = \"temporal\")]\npub mod temporal;\n\npub(crate) use self::{\n    array::Array,\n    async_function::AsyncFunction,\n    bigint::BigInt,\n    boolean::Boolean,\n    dataview::DataView,\n    date::Date,\n    error::{\n        AggregateError, EvalError, RangeError, ReferenceError, SyntaxError, TypeError, UriError,\n    },\n    eval::Eval,\n    finalization_registry::FinalizationRegistry,\n    function::BuiltInFunctionObject,\n    json::Json,\n    map::Map,\n    math::Math,\n    number::{IsFinite, IsNaN, Number, ParseFloat, ParseInt},\n    object::OrdinaryObject,\n    promise::Promise,\n    proxy::Proxy,\n    reflect::Reflect,\n    regexp::RegExp,\n    set::Set,\n    string::String,\n    symbol::Symbol,\n    typed_array::{\n        BigInt64Array, BigUint64Array, Float32Array, Float64Array, Int8Array, Int16Array,\n        Int32Array, Uint8Array, Uint8ClampedArray, Uint16Array, Uint32Array,\n    },\n};\n\nuse crate::{\n    Context, JsResult, JsString, JsValue,\n    builtins::{\n        array::ArrayIterator,\n        array_buffer::{ArrayBuffer, SharedArrayBuffer},\n        async_generator::AsyncGenerator,\n        async_generator_function::AsyncGeneratorFunction,\n        atomics::Atomics,\n        error::r#type::ThrowTypeError,\n        generator::Generator,\n        generator_function::GeneratorFunction,\n        iterable::iterator_constructor::IteratorConstructor,\n        iterable::iterator_helper::IteratorHelper,\n        iterable::wrap_for_valid_iterator::WrapForValidIterator,\n        iterable::{AsyncFromSyncIterator, AsyncIterator, Iterator},\n        map::MapIterator,\n        regexp::RegExpStringIterator,\n        set::SetIterator,\n        string::StringIterator,\n        typed_array::BuiltinTypedArray,\n        uri::{DecodeUri, DecodeUriComponent, EncodeUri, EncodeUriComponent},\n        weak::WeakRef,\n        weak_map::WeakMap,\n        weak_set::WeakSet,\n    },\n    context::intrinsics::{Intrinsics, StandardConstructor, StandardConstructors},\n    js_string,\n    object::JsObject,\n    property::{Attribute, PropertyDescriptor},\n    realm::Realm,\n};\n\n/// A [Well-Known Intrinsic Object].\n///\n/// Well-known intrinsics are built-in objects that are explicitly referenced by the algorithms of\n/// the specification and which usually have realm-specific identities.\n///\n/// [Well-Known Intrinsic Object]: https://tc39.es/ecma262/#sec-well-known-intrinsic-objects\npub(crate) trait IntrinsicObject {\n    /// Initializes the intrinsic object.\n    ///\n    /// This is where the methods, properties, static methods and the constructor of a built-in must\n    /// be initialized to be accessible from ECMAScript.\n    fn init(realm: &Realm);\n\n    /// Gets the intrinsic object.\n    fn get(intrinsics: &Intrinsics) -> JsObject;\n}\n\n/// A [built-in object].\n///\n/// This trait must be implemented for any global built-in that lives in the global context of a script.\n///\n/// [built-in object]: https://tc39.es/ecma262/#sec-built-in-object\npub(crate) trait BuiltInObject: IntrinsicObject {\n    /// Binding name of the builtin inside the global object.\n    ///\n    /// E.g. If you want access the properties of a `Complex` built-in with the name `Cplx` you must\n    /// assign `\"Cplx\"` to this constant, making any property inside it accessible from ECMAScript\n    /// as `Cplx.prop`\n    // `JsString` can only be const-constructed for static strings.\n    #[allow(clippy::declare_interior_mutable_const)]\n    const NAME: JsString;\n\n    /// Property attribute flags of the built-in. Check [`Attribute`] for more information.\n    const ATTRIBUTE: Attribute = Attribute::WRITABLE\n        .union(Attribute::NON_ENUMERABLE)\n        .union(Attribute::CONFIGURABLE);\n}\n\n/// A [built-in object] that is also a constructor.\n///\n/// This trait must be implemented for any global built-in that can also be called with `new` to\n/// construct an object instance e.g. `Array`, `Map` or `Object`.\n///\n/// [built-in object]: https://tc39.es/ecma262/#sec-built-in-object\npub(crate) trait BuiltInConstructor: BuiltInObject {\n    /// The minimum storage slots that need to be allocated for the constructor's\n    /// prototype object.\n    ///\n    /// This is always equivalent to the number of plain properties + 2 times the\n    /// number of properties that require accessor functions.\n    ///\n    /// Note that a \"storage slot\" is any `JsValue` that needs to be stored\n    /// in the prototype object; for accessors the storage count would need\n    /// to be increased by two, since accessors can have a getter and a setter\n    /// value.\n    const PROTOTYPE_STORAGE_SLOTS: usize;\n\n    /// The minimum storage slots that need to be allocated for the constructor\n    /// object.\n    ///\n    /// This is always equivalent to the number of plain static properties + 2\n    /// times the number of static properties that require accessor functions.\n    ///\n    /// Note that a \"storage slot\" is any `JsValue` that needs to be stored\n    /// in the constructor object; for accessors the storage count would need\n    /// to be increased by two, since accessors can have a getter and a setter\n    /// value.\n    const CONSTRUCTOR_STORAGE_SLOTS: usize;\n\n    /// The amount of arguments the constructor function takes.\n    const CONSTRUCTOR_ARGUMENTS: usize;\n\n    /// The corresponding standard constructor of this constructor.\n    const STANDARD_CONSTRUCTOR: fn(&StandardConstructors) -> &StandardConstructor;\n\n    /// The native constructor function.\n    fn constructor(\n        new_target: &JsValue,\n        args: &[JsValue],\n        context: &mut Context,\n    ) -> JsResult<JsValue>;\n}\n\nfn global_binding<B: BuiltInObject>(context: &mut Context) -> JsResult<()> {\n    let name = B::NAME;\n    let attr = B::ATTRIBUTE;\n    let intrinsic = B::get(context.intrinsics());\n    let global_object = context.global_object();\n\n    global_object.define_property_or_throw(\n        name,\n        PropertyDescriptor::builder()\n            .value(intrinsic)\n            .writable(attr.writable())\n            .enumerable(attr.enumerable())\n            .configurable(attr.configurable())\n            .build(),\n        context,\n    )?;\n    Ok(())\n}\n\n/// [`CanonicalizeKeyedCollectionKey ( key )`][spec]\n///\n/// The abstract operation `CanonicalizeKeyedCollectionKey` takes argument key (an ECMAScript\n/// language value) and returns an ECMAScript language value. It performs the following steps\n/// when called:\n///\n///    1. If key is -0𝔽, return +0𝔽.\n///    2. Return key.\n///\n/// [spec]: https://tc39.es/ecma262/multipage/keyed-collections.html#sec-canonicalizekeyedcollectionkey\npub(crate) fn canonicalize_keyed_collection_key(value: JsValue) -> JsValue {\n    match value.as_number() {\n        Some(n) if n.is_zero() => JsValue::new(0),\n        _ => value,\n    }\n}\n\nimpl Realm {\n    /// Abstract operation [`CreateIntrinsics ( realmRec )`][spec]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-createintrinsics\n    pub(crate) fn initialize(&self) {\n        BuiltInFunctionObject::init(self);\n        OrdinaryObject::init(self);\n        Iterator::init(self);\n        AsyncIterator::init(self);\n        AsyncFromSyncIterator::init(self);\n        // IteratorConstructor must init first — IteratorHelper and WrapForValidIterator\n        // set their [[Prototype]] to Iterator.prototype (the constructor's prototype),\n        // so the constructor must be fully initialized first.\n        IteratorConstructor::init(self);\n        WrapForValidIterator::init(self);\n        IteratorHelper::init(self);\n        Math::init(self);\n        Json::init(self);\n        Array::init(self);\n        ArrayIterator::init(self);\n        Proxy::init(self);\n        ArrayBuffer::init(self);\n        SharedArrayBuffer::init(self);\n        BigInt::init(self);\n        Boolean::init(self);\n        Date::init(self);\n        DataView::init(self);\n        Map::init(self);\n        MapIterator::init(self);\n        IsFinite::init(self);\n        IsNaN::init(self);\n        ParseInt::init(self);\n        ParseFloat::init(self);\n        Number::init(self);\n        Eval::init(self);\n        Set::init(self);\n        SetIterator::init(self);\n        String::init(self);\n        StringIterator::init(self);\n        RegExp::init(self);\n        RegExpStringIterator::init(self);\n        BuiltinTypedArray::init(self);\n        Int8Array::init(self);\n        Uint8Array::init(self);\n        Uint8ClampedArray::init(self);\n        Int16Array::init(self);\n        Uint16Array::init(self);\n        Int32Array::init(self);\n        Uint32Array::init(self);\n        BigInt64Array::init(self);\n        BigUint64Array::init(self);\n        #[cfg(feature = \"float16\")]\n        typed_array::Float16Array::init(self);\n        Float32Array::init(self);\n        Float64Array::init(self);\n        Symbol::init(self);\n        Error::init(self);\n        RangeError::init(self);\n        ReferenceError::init(self);\n        TypeError::init(self);\n        ThrowTypeError::init(self);\n        SyntaxError::init(self);\n        EvalError::init(self);\n        UriError::init(self);\n        AggregateError::init(self);\n        Reflect::init(self);\n        Generator::init(self);\n        GeneratorFunction::init(self);\n        Promise::init(self);\n        AsyncFunction::init(self);\n        AsyncGenerator::init(self);\n        AsyncGeneratorFunction::init(self);\n        EncodeUri::init(self);\n        EncodeUriComponent::init(self);\n        DecodeUri::init(self);\n        DecodeUriComponent::init(self);\n        WeakRef::init(self);\n        WeakMap::init(self);\n        WeakSet::init(self);\n        Atomics::init(self);\n        FinalizationRegistry::init(self);\n\n        #[cfg(feature = \"annex-b\")]\n        {\n            escape::Escape::init(self);\n            escape::Unescape::init(self);\n        }\n\n        #[cfg(feature = \"intl\")]\n        {\n            intl::Intl::init(self);\n            intl::Collator::init(self);\n            intl::ListFormat::init(self);\n            intl::Locale::init(self);\n            intl::DateTimeFormat::init(self);\n            intl::Segmenter::init(self);\n            intl::segmenter::Segments::init(self);\n            intl::segmenter::SegmentIterator::init(self);\n            intl::PluralRules::init(self);\n            intl::NumberFormat::init(self);\n        }\n\n        #[cfg(feature = \"temporal\")]\n        {\n            temporal::Temporal::init(self);\n            temporal::Now::init(self);\n            temporal::Instant::init(self);\n            temporal::Duration::init(self);\n            temporal::PlainDate::init(self);\n            temporal::PlainTime::init(self);\n            temporal::PlainDateTime::init(self);\n            temporal::PlainMonthDay::init(self);\n            temporal::PlainYearMonth::init(self);\n            temporal::ZonedDateTime::init(self);\n        }\n    }\n}\n\n/// Abstract operation [`SetDefaultGlobalBindings ( realmRec )`][spec].\n///\n/// [spec]: https://tc39.es/ecma262/#sec-setdefaultglobalbindings\npub(crate) fn set_default_global_bindings(context: &mut Context) -> JsResult<()> {\n    let global_object = context.global_object();\n\n    global_object.define_property_or_throw(\n        js_string!(\"globalThis\"),\n        PropertyDescriptor::builder()\n            .value(context.realm().global_this().clone())\n            .writable(true)\n            .enumerable(false)\n            .configurable(true),\n        context,\n    )?;\n    let restricted = PropertyDescriptor::builder()\n        .writable(false)\n        .enumerable(false)\n        .configurable(false);\n    global_object.define_property_or_throw(\n        js_string!(\"Infinity\"),\n        restricted.clone().value(f64::INFINITY),\n        context,\n    )?;\n    global_object.define_property_or_throw(\n        js_string!(\"NaN\"),\n        restricted.clone().value(f64::NAN),\n        context,\n    )?;\n    global_object.define_property_or_throw(\n        js_string!(\"undefined\"),\n        restricted.value(JsValue::undefined()),\n        context,\n    )?;\n\n    global_binding::<BuiltInFunctionObject>(context)?;\n    global_binding::<OrdinaryObject>(context)?;\n    global_binding::<Math>(context)?;\n    global_binding::<Json>(context)?;\n    global_binding::<Array>(context)?;\n    global_binding::<Proxy>(context)?;\n    global_binding::<ArrayBuffer>(context)?;\n    global_binding::<SharedArrayBuffer>(context)?;\n    global_binding::<BigInt>(context)?;\n    global_binding::<Boolean>(context)?;\n    global_binding::<Date>(context)?;\n    global_binding::<DataView>(context)?;\n    global_binding::<Map>(context)?;\n    global_binding::<IsFinite>(context)?;\n    global_binding::<IsNaN>(context)?;\n    global_binding::<ParseInt>(context)?;\n    global_binding::<ParseFloat>(context)?;\n    global_binding::<Number>(context)?;\n    global_binding::<Eval>(context)?;\n    global_binding::<Set>(context)?;\n    global_binding::<String>(context)?;\n    global_binding::<RegExp>(context)?;\n    global_binding::<BuiltinTypedArray>(context)?;\n    global_binding::<Int8Array>(context)?;\n    global_binding::<Uint8Array>(context)?;\n    global_binding::<Uint8ClampedArray>(context)?;\n    global_binding::<Int16Array>(context)?;\n    global_binding::<Uint16Array>(context)?;\n    global_binding::<Int32Array>(context)?;\n    global_binding::<Uint32Array>(context)?;\n    global_binding::<BigInt64Array>(context)?;\n    global_binding::<BigUint64Array>(context)?;\n    #[cfg(feature = \"float16\")]\n    global_binding::<typed_array::Float16Array>(context)?;\n    global_binding::<Float32Array>(context)?;\n    global_binding::<Float64Array>(context)?;\n    global_binding::<Symbol>(context)?;\n    global_binding::<Error>(context)?;\n    global_binding::<RangeError>(context)?;\n    global_binding::<ReferenceError>(context)?;\n    global_binding::<TypeError>(context)?;\n    global_binding::<SyntaxError>(context)?;\n    global_binding::<EvalError>(context)?;\n    global_binding::<UriError>(context)?;\n    global_binding::<AggregateError>(context)?;\n    global_binding::<Reflect>(context)?;\n    global_binding::<Promise>(context)?;\n    global_binding::<EncodeUri>(context)?;\n    global_binding::<EncodeUriComponent>(context)?;\n    global_binding::<DecodeUri>(context)?;\n    global_binding::<DecodeUriComponent>(context)?;\n    global_binding::<WeakRef>(context)?;\n    global_binding::<WeakMap>(context)?;\n    global_binding::<WeakSet>(context)?;\n    global_binding::<IteratorConstructor>(context)?;\n    global_binding::<Atomics>(context)?;\n    global_binding::<FinalizationRegistry>(context)?;\n\n    #[cfg(feature = \"annex-b\")]\n    {\n        global_binding::<escape::Escape>(context)?;\n        global_binding::<escape::Unescape>(context)?;\n    }\n\n    #[cfg(feature = \"intl\")]\n    global_binding::<intl::Intl>(context)?;\n\n    #[cfg(feature = \"temporal\")]\n    {\n        global_binding::<temporal::Temporal>(context)?;\n    }\n\n    Ok(())\n}\n"
  },
  {
    "path": "core/engine/src/builtins/number/conversions.rs",
    "content": "/// Converts a 64-bit floating point number to an `i32` according to the [`ToInt32`][ToInt32] algorithm.\n///\n/// [ToInt32]: https://tc39.es/ecma262/#sec-toint32\n#[allow(clippy::float_cmp)]\n#[cfg(not(all(target_arch = \"aarch64\", target_feature = \"jsconv\")))]\npub(crate) fn f64_to_int32(number: f64) -> i32 {\n    const SIGN_MASK: u64 = 0x8000_0000_0000_0000;\n    const EXPONENT_MASK: u64 = 0x7FF0_0000_0000_0000;\n    const SIGNIFICAND_MASK: u64 = 0x000F_FFFF_FFFF_FFFF;\n    const HIDDEN_BIT: u64 = 0x0010_0000_0000_0000;\n    const PHYSICAL_SIGNIFICAND_SIZE: i32 = 52; // Excludes the hidden bit.\n    const SIGNIFICAND_SIZE: i32 = 53;\n\n    const EXPONENT_BIAS: i32 = 0x3FF + PHYSICAL_SIGNIFICAND_SIZE;\n    const DENORMAL_EXPONENT: i32 = -EXPONENT_BIAS + 1;\n\n    fn is_denormal(number: f64) -> bool {\n        (number.to_bits() & EXPONENT_MASK) == 0\n    }\n\n    fn exponent(number: f64) -> i32 {\n        if is_denormal(number) {\n            return DENORMAL_EXPONENT;\n        }\n\n        let d64 = number.to_bits();\n        let biased_e = ((d64 & EXPONENT_MASK) >> PHYSICAL_SIGNIFICAND_SIZE) as i32;\n\n        biased_e - EXPONENT_BIAS\n    }\n\n    fn significand(number: f64) -> u64 {\n        let d64 = number.to_bits();\n        let significand = d64 & SIGNIFICAND_MASK;\n\n        if is_denormal(number) {\n            significand\n        } else {\n            significand + HIDDEN_BIT\n        }\n    }\n\n    fn sign(number: f64) -> i64 {\n        if (number.to_bits() & SIGN_MASK) == 0 {\n            1\n        } else {\n            -1\n        }\n    }\n\n    if number.is_finite() && number <= f64::from(i32::MAX) && number >= f64::from(i32::MIN) {\n        let i = number as i32;\n        if f64::from(i) == number {\n            return i;\n        }\n    }\n\n    let exponent = exponent(number);\n    let bits = if exponent < 0 {\n        if exponent <= -SIGNIFICAND_SIZE {\n            return 0;\n        }\n\n        significand(number) >> -exponent\n    } else {\n        if exponent > 31 {\n            return 0;\n        }\n\n        (significand(number) << exponent) & 0xFFFF_FFFF\n    };\n\n    (sign(number) * (bits as i64)) as i32\n}\n\n/// Converts a 64-bit floating point number to an `i32` using [`FJCVTZS`][FJCVTZS] instruction on `ARMv8.3`.\n///\n/// [FJCVTZS]: https://developer.arm.com/documentation/dui0801/h/A64-Floating-point-Instructions/FJCVTZS\n#[cfg(all(target_arch = \"aarch64\", target_feature = \"jsconv\"))]\npub(crate) fn f64_to_int32(number: f64) -> i32 {\n    if number.is_nan() {\n        return 0;\n    }\n    let ret: i32;\n    // SAFETY: Number is not nan so no floating-point exception should throw.\n    unsafe {\n        std::arch::asm!(\n            \"fjcvtzs {dst:w}, {src:d}\",\n            src = in(vreg) number,\n            dst = out(reg) ret,\n        );\n    }\n    ret\n}\n\n/// Converts a 64-bit floating point number to an `i32` using [`FJCVTZS`][FJCVTZS] instruction on `ARMv8.3`.\n///\n/// [FJCVTZS]: https://developer.arm.com/documentation/dui0801/h/A64-Floating-point-Instructions/FJCVTZS\n#[cfg(all(target_arch = \"aarch64\", target_feature = \"jsconv\"))]\npub(crate) fn f64_to_uint32(number: f64) -> u32 {\n    f64_to_int32(number) as u32\n}\n\n/// Converts a 64-bit floating point number to an `u32` according to the [`ToUint32`][ToUint32] algorithm.\n///\n/// [ToUint32]: https://tc39.es/ecma262/#sec-touint32\n#[cfg(not(all(target_arch = \"aarch64\", target_feature = \"jsconv\")))]\npub(crate) fn f64_to_uint32(number: f64) -> u32 {\n    f64_to_int32(number) as u32\n}\n\n#[test]\nfn f64_to_int32_conversion() {\n    use crate::builtins::Number;\n\n    assert_eq!(f64_to_int32(0.0), 0);\n    assert_eq!(f64_to_int32(-0.0), 0);\n    assert_eq!(f64_to_int32(f64::NAN), 0);\n    assert_eq!(f64_to_int32(f64::INFINITY), 0);\n    assert_eq!(f64_to_int32(f64::NEG_INFINITY), 0);\n    assert_eq!(f64_to_int32((i64::from(i32::MAX) + 1) as f64), i32::MIN);\n    assert_eq!(f64_to_int32((i64::from(i32::MIN) - 1) as f64), i32::MAX);\n\n    assert_eq!(f64_to_int32(Number::MAX_SAFE_INTEGER + 1.0), 0);\n    assert_eq!(f64_to_int32(Number::MIN_SAFE_INTEGER - 1.0), 0);\n}\n"
  },
  {
    "path": "core/engine/src/builtins/number/globals.rs",
    "content": "use crate::{\n    Context, JsArgs, JsResult, JsStr, JsString, JsValue,\n    builtins::{BuiltInBuilder, BuiltInObject, IntrinsicObject},\n    context::intrinsics::Intrinsics,\n    object::JsObject,\n    realm::Realm,\n    string::StaticJsStrings,\n};\n\nuse boa_macros::js_str;\nuse boa_string::JsStrVariant;\n\n/// Builtin javascript 'isFinite(number)' function.\n///\n/// Converts the argument to a number, throwing a type error if the conversion is invalid.\n///\n/// If the number is `NaN`, `+∞`, or `-∞`, `false` is returned.\n///\n/// Otherwise true is returned.\n///\n/// More information:\n///  - [ECMAScript reference][spec]\n///  - [MDN documentation][mdn]\n///\n/// [spec]: https://tc39.es/ecma262/#sec-isfinite-number\n/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/isFinite\nfn is_finite(_: &JsValue, args: &[JsValue], context: &mut Context) -> JsResult<JsValue> {\n    if let Some(value) = args.first() {\n        let number = value.to_number(context)?;\n        Ok(number.is_finite().into())\n    } else {\n        Ok(false.into())\n    }\n}\n\npub(crate) struct IsFinite;\n\nimpl IntrinsicObject for IsFinite {\n    fn init(realm: &Realm) {\n        BuiltInBuilder::callable_with_intrinsic::<Self>(realm, is_finite)\n            .name(Self::NAME)\n            .length(1)\n            .build();\n    }\n\n    fn get(intrinsics: &Intrinsics) -> JsObject {\n        intrinsics.objects().is_finite().into()\n    }\n}\n\nimpl BuiltInObject for IsFinite {\n    const NAME: JsString = StaticJsStrings::IS_FINITE;\n}\n\n/// Builtin javascript 'isNaN(number)' function.\n///\n/// Converts the argument to a number, throwing a type error if the conversion is invalid.\n///\n/// If the number is `NaN`, `true` is returned.\n///\n/// Otherwise false is returned.\n///\n/// More information:\n///  - [ECMAScript reference][spec]\n///  - [MDN documentation][mdn]\n///\n/// [spec]: https://tc39.es/ecma262/#sec-isnan-number\n/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/isNaN\npub(crate) fn is_nan(_: &JsValue, args: &[JsValue], context: &mut Context) -> JsResult<JsValue> {\n    if let Some(value) = args.first() {\n        let number = value.to_number(context)?;\n        Ok(number.is_nan().into())\n    } else {\n        Ok(true.into())\n    }\n}\n\npub(crate) struct IsNaN;\n\nimpl IntrinsicObject for IsNaN {\n    fn init(realm: &Realm) {\n        BuiltInBuilder::callable_with_intrinsic::<Self>(realm, is_nan)\n            .name(Self::NAME)\n            .length(1)\n            .build();\n    }\n\n    fn get(intrinsics: &Intrinsics) -> JsObject {\n        intrinsics.objects().is_nan().into()\n    }\n}\n\nimpl BuiltInObject for IsNaN {\n    const NAME: JsString = StaticJsStrings::IS_NAN;\n}\n\nfn from_js_str_radix(src: JsStr<'_>, radix: u8) -> Option<f64> {\n    /// Determines if a string of text of that length of that radix could be guaranteed to be\n    /// stored in the given type T.\n    /// Note that if the radix is known to the compiler, it is just the check of digits.len that\n    /// is done at runtime.\n    fn can_not_overflow(radix: u8, digits_len: usize) -> bool {\n        usize::from(radix) <= 16 && digits_len <= size_of::<u64>() * 2\n    }\n\n    const fn to_digit(input: u8, radix: u8) -> Option<u8> {\n        // If not a digit, a number greater than radix will be created.\n        let mut digit = input.wrapping_sub(b'0');\n        if radix > 10 {\n            debug_assert!(radix <= 36, \"to_digit: radix is too high (maximum 36)\");\n            if digit < 10 {\n                return Some(digit);\n            }\n            // Force the 6th bit to be set to ensure ascii is lower case.\n            digit = (input | 0b10_0000).wrapping_sub(b'a').saturating_add(10);\n        }\n        // FIXME: once then_some is const fn, use it here\n        if digit < radix { Some(digit) } else { None }\n    }\n\n    let src = src\n        .iter()\n        .map(|x| u8::try_from(x).expect(\"should be ascii string\"));\n\n    let result = if can_not_overflow(radix, src.len()) {\n        let mut result = 0;\n        for c in src {\n            result = result * u64::from(radix) + u64::from(to_digit(c, radix)?);\n        }\n        result as f64\n    } else {\n        let mut result = 0f64;\n        for c in src {\n            result = result * f64::from(radix) + f64::from(to_digit(c, radix)?);\n        }\n        result\n    };\n\n    Some(result)\n}\n\n/// Builtin javascript 'parseInt(str, radix)' function.\n///\n/// Parses the given string as an integer using the given radix as a base.\n///\n/// An argument of type Number (i.e. Integer or Rational) is also accepted in place of string.\n///\n/// The radix must be an integer in the range [2, 36] inclusive.\n///\n/// More information:\n///  - [ECMAScript reference][spec]\n///  - [MDN documentation][mdn]\n///\n/// [spec]: https://tc39.es/ecma262/#sec-parseint-string-radix\n/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/parseInt\npub(crate) fn parse_int(_: &JsValue, args: &[JsValue], context: &mut Context) -> JsResult<JsValue> {\n    let (Some(string), radix) = (args.first(), args.get_or_undefined(1)) else {\n        // Not enough arguments to parseInt.\n        return Ok(JsValue::nan());\n    };\n\n    // OPTIMIZATION: We can skip the round-trip when the value is already a number.\n    if let Some(int) = string.as_i32()\n        && radix.is_null_or_undefined()\n    {\n        return Ok(JsValue::new(int));\n    }\n\n    // 1. Let inputString be ? ToString(string).\n    let input_string = string.to_string(context)?;\n\n    // 2. Let S be ! TrimString(inputString, start).\n    let mut s = input_string.trim_start();\n\n    // 3. Let sign be 1.\n    // 4. If S is not empty and the first code unit of S is the code unit 0x002D (HYPHEN-MINUS),\n    //    set sign to -1.\n    let sign = if !s.is_empty() && s.starts_with(js_str!(\"-\")) {\n        -1\n    } else {\n        1\n    };\n\n    // 5. If S is not empty and the first code unit of S is the code unit 0x002B (PLUS SIGN) or\n    //    the code unit 0x002D (HYPHEN-MINUS), remove the first code unit from S.\n    if !s.is_empty() && (s.starts_with(js_str!(\"+\")) || s.starts_with(js_str!(\"-\"))) {\n        s = s.get(1..).expect(\"already checked that it's not empty\");\n    }\n\n    // 6. Let R be ℝ(? ToInt32(radix)).\n    let r = radix.to_i32(context)?;\n\n    // 7. Let stripPrefix be true.\n    let mut strip_prefix = true;\n\n    // 8. If R ≠ 0, then\n    #[allow(clippy::if_not_else)]\n    let mut r = if r != 0 {\n        //     a. If R < 2 or R > 36, return NaN.\n        if !(2..=36).contains(&r) {\n            return Ok(JsValue::nan());\n        }\n\n        //     b. If R ≠ 16, set stripPrefix to false.\n        if r != 16 {\n            strip_prefix = false;\n        }\n        r as u8\n    } else {\n        // 9. Else,\n        //     a. Set R to 10.\n        10\n    };\n\n    // 10. If stripPrefix is true, then\n    //     a. If the length of S is at least 2 and the first two code units of S are either \"0x\" or \"0X\", then\n    //         i. Remove the first two code units from S.\n    //         ii. Set R to 16.\n    if strip_prefix\n        && s.len() >= 2\n        && (s.starts_with(js_str!(\"0x\")) || s.starts_with(js_str!(\"0X\")))\n    {\n        s = s\n            .get(2..)\n            .expect(\"already checked that it contains at least two chars\");\n\n        r = 16;\n    }\n\n    // 11. If S contains a code unit that is not a radix-R digit, let end be the index within S of the\n    //     first such code unit; otherwise, let end be the length of S.\n    let end = char::decode_utf16(s.iter())\n        .position(|code| !code.is_ok_and(|c| c.is_digit(u32::from(r))))\n        .unwrap_or(s.len());\n\n    // 12. Let Z be the substring of S from 0 to end.\n    let z = s.get(..end).expect(\"should be in range\");\n\n    // 13. If Z is empty, return NaN.\n    if z.is_empty() {\n        return Ok(JsValue::nan());\n    }\n\n    // 14. Let mathInt be the integer value that is represented by Z in radix-R notation, using the\n    //     letters A-Z and a-z for digits with values 10 through 35. (However, if R is 10 and Z contains\n    //     more than 20 significant digits, every significant digit after the 20th may be replaced by a\n    //     0 digit, at the option of the implementation; and if R is not 2, 4, 8, 10, 16, or 32, then\n    //     mathInt may be an implementation-approximated value representing the integer value that is\n    //     represented by Z in radix-R notation.)\n    let math_int = from_js_str_radix(z.as_str(), r).expect(\"Already checked\");\n\n    // 15. If mathInt = 0, then\n    //     a. If sign = -1, return -0𝔽.\n    //     b. Return +0𝔽.\n    if math_int == 0_f64 {\n        if sign == -1 {\n            return Ok(JsValue::new(-0_f64));\n        }\n\n        return Ok(JsValue::new(0));\n    }\n\n    // 16. Return 𝔽(sign × mathInt).\n    Ok(JsValue::new(f64::from(sign) * math_int))\n}\n\npub(crate) struct ParseInt;\n\nimpl IntrinsicObject for ParseInt {\n    fn init(realm: &Realm) {\n        BuiltInBuilder::callable_with_intrinsic::<Self>(realm, parse_int)\n            .name(Self::NAME)\n            .length(2)\n            .build();\n    }\n\n    fn get(intrinsics: &Intrinsics) -> JsObject {\n        intrinsics.objects().parse_int().into()\n    }\n}\n\nimpl BuiltInObject for ParseInt {\n    const NAME: JsString = StaticJsStrings::PARSE_INT;\n}\n\n/// Builtin javascript 'parseFloat(str)' function.\n///\n/// Parses the given string as a floating point value.\n///\n/// An argument of type Number (i.e. Integer or Rational) is also accepted in place of string.\n///\n/// To improve performance an Integer type Number is returned in place of a Rational if the given\n/// string can be parsed and stored as an Integer.\n///\n/// More information:\n///  - [ECMAScript reference][spec]\n///  - [MDN documentation][mdn]\n///\n/// [spec]: https://tc39.es/ecma262/#sec-parsefloat-string\n/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/parseFloat\npub(crate) fn parse_float(\n    _: &JsValue,\n    args: &[JsValue],\n    context: &mut Context,\n) -> JsResult<JsValue> {\n    let Some(string) = args.first() else {\n        return Ok(JsValue::nan());\n    };\n\n    // OPTIMIZATION: We can skip the round-trip when the value is already a number.\n    if string.is_number() {\n        // Special case for negative zero - it should become positive zero\n        if string.is_negative_zero() {\n            return Ok(JsValue::new(0));\n        }\n\n        return Ok(string.clone());\n    }\n\n    // 1. Let inputString be ? ToString(string).\n    let input_string = string.to_string(context)?;\n\n    // 2. Let trimmedString be ! TrimString(inputString, start).\n    let trimmed_string = input_string.trim_start();\n\n    // 3. Let trimmed be StringToCodePoints(trimmedString).\n    // 4. Let trimmedPrefix be the longest prefix of trimmed that satisfies the syntax of a StrDecimalLiteral, which might be trimmed itself. If there is no such prefix, return NaN.\n    // 5. Let parsedNumber be ParseText(trimmedPrefix, StrDecimalLiteral).\n    // 6. Assert: parsedNumber is a Parse Node.\n    // 7. Return the StringNumericValue of parsedNumber.\n    let (positive, prefix) = match trimmed_string\n        .code_unit_at(0)\n        .and_then(|c| char::from_u32(u32::from(c)))\n    {\n        Some('+') => (\n            true,\n            trimmed_string\n                .get(1..)\n                .unwrap_or(StaticJsStrings::EMPTY_STRING),\n        ),\n        Some('-') => (\n            false,\n            trimmed_string\n                .get(1..)\n                .unwrap_or(StaticJsStrings::EMPTY_STRING),\n        ),\n        _ => (true, trimmed_string.clone()),\n    };\n\n    if prefix.starts_with(js_str!(\"Infinity\")) {\n        if positive {\n            return Ok(JsValue::positive_infinity());\n        }\n        return Ok(JsValue::negative_infinity());\n    } else if let Some('I' | 'i') = prefix\n        .code_unit_at(0)\n        .and_then(|c| char::from_u32(u32::from(c)))\n    {\n        return Ok(JsValue::nan());\n    }\n\n    let value = match trimmed_string.variant() {\n        JsStrVariant::Latin1(s) => fast_float2::parse_partial::<f64, _>(s),\n        JsStrVariant::Utf16(s) => {\n            // TODO: Explore adding direct UTF-16 parsing support to fast_float2.\n            let s = String::from_utf16_lossy(s);\n            fast_float2::parse_partial::<f64, _>(s.as_bytes())\n        }\n    };\n\n    Ok(value.map_or_else(\n        |_| JsValue::nan(),\n        |(f, len)| {\n            if len > 0 {\n                JsValue::new(f)\n            } else {\n                JsValue::nan()\n            }\n        },\n    ))\n}\n\npub(crate) struct ParseFloat;\n\nimpl IntrinsicObject for ParseFloat {\n    fn init(realm: &Realm) {\n        BuiltInBuilder::callable_with_intrinsic::<Self>(realm, parse_float)\n            .name(Self::NAME)\n            .length(1)\n            .build();\n    }\n\n    fn get(intrinsics: &Intrinsics) -> JsObject {\n        intrinsics.objects().parse_float().into()\n    }\n}\n\nimpl BuiltInObject for ParseFloat {\n    const NAME: JsString = StaticJsStrings::PARSE_FLOAT;\n}\n"
  },
  {
    "path": "core/engine/src/builtins/number/mod.rs",
    "content": "//! Boa's implementation of ECMAScript's global `Number` object.\n//!\n//! The `Number` ECMAScript object is a wrapper object allowing you to work with numerical values.\n//! A `Number` object is created using the `Number()` constructor. A primitive type object number is created using the `Number()` **function**.\n//!\n//! The ECMAScript `Number` type is double-precision 64-bit binary format IEEE 754 value. In more recent implementations,\n//! ECMAScript also supports integers with arbitrary precision using the `BigInt` type.\n//!\n//! More information:\n//!  - [ECMAScript reference][spec]\n//!  - [MDN documentation][mdn]\n//!\n//! [spec]: https://tc39.es/ecma262/#sec-number-object\n//! [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number\n\nuse crate::{\n    Context, JsArgs, JsResult, JsString,\n    builtins::BuiltInObject,\n    context::intrinsics::{Intrinsics, StandardConstructor, StandardConstructors},\n    error::JsNativeError,\n    js_string,\n    object::{JsObject, internal_methods::get_prototype_from_constructor},\n    property::Attribute,\n    realm::Realm,\n    string::StaticJsStrings,\n    value::{AbstractRelation, IntegerOrInfinity, JsValue},\n};\nuse cow_utils::CowUtils;\nuse num_traits::float::FloatCore;\n\nmod globals;\npub(crate) use globals::{IsFinite, IsNaN, ParseFloat, ParseInt};\n\nmod conversions;\n\nuse super::{BuiltInBuilder, BuiltInConstructor, IntrinsicObject};\nuse crate::value::JsVariant;\npub(crate) use conversions::{f64_to_int32, f64_to_uint32};\n\n#[cfg(test)]\nmod tests;\n\nconst BUF_SIZE: usize = 2200;\n\n/// `Number` implementation.\n#[derive(Debug, Clone, Copy)]\npub(crate) struct Number;\n\nimpl IntrinsicObject for Number {\n    fn init(realm: &Realm) {\n        let attribute = Attribute::READONLY | Attribute::NON_ENUMERABLE | Attribute::PERMANENT;\n\n        BuiltInBuilder::from_standard_constructor::<Self>(realm)\n            .static_property(js_string!(\"EPSILON\"), f64::EPSILON, attribute)\n            .static_property(\n                js_string!(\"MAX_SAFE_INTEGER\"),\n                Self::MAX_SAFE_INTEGER,\n                attribute,\n            )\n            .static_property(\n                js_string!(\"MIN_SAFE_INTEGER\"),\n                Self::MIN_SAFE_INTEGER,\n                attribute,\n            )\n            .static_property(js_string!(\"MAX_VALUE\"), Self::MAX_VALUE, attribute)\n            .static_property(js_string!(\"MIN_VALUE\"), Self::MIN_VALUE, attribute)\n            .static_property(\n                js_string!(\"NEGATIVE_INFINITY\"),\n                f64::NEG_INFINITY,\n                attribute,\n            )\n            .static_property(js_string!(\"POSITIVE_INFINITY\"), f64::INFINITY, attribute)\n            .static_property(js_string!(\"NaN\"), f64::NAN, attribute)\n            .static_property(\n                js_string!(\"parseInt\"),\n                realm.intrinsics().objects().parse_int(),\n                Attribute::WRITABLE | Attribute::NON_ENUMERABLE | Attribute::CONFIGURABLE,\n            )\n            .static_property(\n                js_string!(\"parseFloat\"),\n                realm.intrinsics().objects().parse_float(),\n                Attribute::WRITABLE | Attribute::NON_ENUMERABLE | Attribute::CONFIGURABLE,\n            )\n            .static_method(Self::number_is_finite, js_string!(\"isFinite\"), 1)\n            .static_method(Self::number_is_nan, js_string!(\"isNaN\"), 1)\n            .static_method(Self::is_safe_integer, js_string!(\"isSafeInteger\"), 1)\n            .static_method(Self::number_is_integer, js_string!(\"isInteger\"), 1)\n            .method(Self::to_exponential, js_string!(\"toExponential\"), 1)\n            .method(Self::to_fixed, js_string!(\"toFixed\"), 1)\n            .method(Self::to_locale_string, js_string!(\"toLocaleString\"), 0)\n            .method(Self::to_precision, js_string!(\"toPrecision\"), 1)\n            .method(Self::to_string, js_string!(\"toString\"), 1)\n            .method(Self::value_of, js_string!(\"valueOf\"), 0)\n            .build();\n    }\n\n    fn get(intrinsics: &Intrinsics) -> JsObject {\n        Self::STANDARD_CONSTRUCTOR(intrinsics.constructors()).constructor()\n    }\n}\n\nimpl BuiltInObject for Number {\n    const NAME: JsString = StaticJsStrings::NUMBER;\n}\n\nimpl BuiltInConstructor for Number {\n    const CONSTRUCTOR_ARGUMENTS: usize = 1;\n    const PROTOTYPE_STORAGE_SLOTS: usize = 6;\n    const CONSTRUCTOR_STORAGE_SLOTS: usize = 14;\n\n    const STANDARD_CONSTRUCTOR: fn(&StandardConstructors) -> &StandardConstructor =\n        StandardConstructors::number;\n\n    /// `Number( value )`\n    fn constructor(\n        new_target: &JsValue,\n        args: &[JsValue],\n        context: &mut Context,\n    ) -> JsResult<JsValue> {\n        let data = match args.first() {\n            Some(value) => value.to_numeric_number(context)?,\n            None => 0.0,\n        };\n        if new_target.is_undefined() {\n            return Ok(JsValue::new(data));\n        }\n        let prototype =\n            get_prototype_from_constructor(new_target, StandardConstructors::number, context)?;\n        let this =\n            JsObject::from_proto_and_data_with_shared_shape(context.root_shape(), prototype, data);\n        Ok(this.into())\n    }\n}\n\nimpl Number {\n    /// The `Number.MAX_SAFE_INTEGER` constant represents the maximum safe integer in JavaScript (`2^53 - 1`).\n    ///\n    /// /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///  - [MDN documentation][mdn]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-number.max_safe_integer\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number/MAX_SAFE_INTEGER\n    pub(crate) const MAX_SAFE_INTEGER: f64 = 9_007_199_254_740_991_f64;\n\n    /// The `Number.MIN_SAFE_INTEGER` constant represents the minimum safe integer in JavaScript (`-(2^53 - 1)`).\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///  - [MDN documentation][mdn]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-number.min_safe_integer\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number/MIN_SAFE_INTEGER\n    pub(crate) const MIN_SAFE_INTEGER: f64 = -9_007_199_254_740_991_f64;\n\n    /// The `Number.MAX_VALUE` property represents the maximum numeric value representable in JavaScript.\n    ///\n    /// The `MAX_VALUE` property has a value of approximately `1.79E+308`, or `2^1024`.\n    /// Values larger than `MAX_VALUE` are represented as `Infinity`.\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///  - [MDN documentation][mdn]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-number.max_value\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number/MAX_VALUE\n    pub(crate) const MAX_VALUE: f64 = f64::MAX;\n\n    /// The `Number.MIN_VALUE` property represents the smallest positive numeric value representable in JavaScript.\n    ///\n    /// The `MIN_VALUE` property is the number closest to `0`, not the most negative number, that JavaScript can represent.\n    /// It has a value of approximately `5e-324`. Values smaller than `MIN_VALUE` (\"underflow values\") are converted to `0`.\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///  - [MDN documentation][mdn]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-number.min_value\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number/MIN_VALUE\n    pub(crate) const MIN_VALUE: f64 = 5e-324;\n\n    /// This function returns a `JsResult` of the number `Value`.\n    ///\n    /// If the `Value` is a `Number` primitive of `Number` object the number is returned.\n    /// Otherwise an `TypeError` is thrown.\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-thisnumbervalue\n    fn this_number_value(value: &JsValue) -> JsResult<f64> {\n        value\n            .as_number()\n            .or_else(|| {\n                value\n                    .as_object()\n                    .and_then(|obj| obj.downcast_ref::<f64>().as_deref().copied())\n            })\n            .ok_or_else(|| {\n                JsNativeError::typ()\n                    .with_message(\"'this' is not a number\")\n                    .into()\n            })\n    }\n\n    /// `Number.prototype.toExponential( [fractionDigits] )`\n    ///\n    /// The `toExponential()` method returns a string representing the Number object in exponential notation.\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///  - [MDN documentation][mdn]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-number.prototype.toexponential\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number/toExponential\n    #[allow(clippy::wrong_self_convention)]\n    pub(crate) fn to_exponential(\n        this: &JsValue,\n        args: &[JsValue],\n        context: &mut Context,\n    ) -> JsResult<JsValue> {\n        // 1. Let x be ? thisNumberValue(this value).\n        let this_num = Self::this_number_value(this)?;\n        let precision = match args.first() {\n            None => None,\n            Some(x) if x.is_undefined() => None,\n            // 2. Let f be ? ToIntegerOrInfinity(fractionDigits).\n            Some(n) => Some(n.to_integer_or_infinity(context)?),\n        };\n        // 4. If x is not finite, return ! Number::toString(x).\n        if !this_num.is_finite() {\n            return Ok(JsValue::new(JsString::from(this_num)));\n        }\n        // Get rid of the '-' sign for -0.0\n        let this_num = if this_num == 0. { 0. } else { this_num };\n        let this_str_num = match precision {\n            None => f64_to_exponential(this_num),\n            Some(IntegerOrInfinity::Integer(precision)) if (0..=100).contains(&precision) =>\n            // 5. If f < 0 or f > 100, throw a RangeError exception.\n            {\n                f64_to_exponential_with_precision(this_num, precision as usize)\n            }\n            _ => {\n                return Err(JsNativeError::range()\n                    .with_message(\"toExponential() argument must be between 0 and 100\")\n                    .into());\n            }\n        };\n        Ok(JsValue::new(this_str_num))\n    }\n\n    /// `Number.prototype.toFixed( [digits] )`\n    ///\n    /// The `toFixed()` method formats a number using fixed-point notation\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///  - [MDN documentation][mdn]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-number.prototype.tofixed\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number/toFixed\n    #[allow(clippy::wrong_self_convention)]\n    pub(crate) fn to_fixed(\n        this: &JsValue,\n        args: &[JsValue],\n        context: &mut Context,\n    ) -> JsResult<JsValue> {\n        // 1. Let this_num be ? thisNumberValue(this value).\n        let this_num = Self::this_number_value(this)?;\n\n        // 2. Let f be ? ToIntegerOrInfinity(fractionDigits).\n        // 3. Assert: If fractionDigits is undefined, then f is 0.\n        let precision = args.get_or_undefined(0).to_integer_or_infinity(context)?;\n\n        // 4, 5. If f < 0 or f > 100, throw a RangeError exception.\n        let precision = precision\n            .as_integer()\n            .filter(|i| (0..=100).contains(i))\n            .ok_or_else(|| {\n                JsNativeError::range()\n                    .with_message(\"toFixed() digits argument must be between 0 and 100\")\n            })? as u8;\n\n        let mut buffer = ryu_js::Buffer::new();\n        let string = buffer.format_to_fixed(this_num, precision);\n\n        Ok(js_string!(string).into())\n    }\n\n    /// `Number.prototype.toLocaleString( [locales [, options]] )`\n    ///\n    /// The `toLocaleString()` method returns a string with a language-sensitive representation of this number.\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///  - [MDN documentation][mdn]\n    ///\n    /// [spec]: https://tc39.es/ecma402/#sup-number.prototype.tolocalestring\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number/toLocaleString\n    #[allow(clippy::wrong_self_convention)]\n    #[allow(\n        unused_variables,\n        reason = \"`args` and `context` are used if the `intl` feature is enabled\"\n    )]\n    pub(crate) fn to_locale_string(\n        this: &JsValue,\n        args: &[JsValue],\n        context: &mut Context,\n    ) -> JsResult<JsValue> {\n        // 1. Let x be ? ThisNumberValue(this value).\n        let x = Self::this_number_value(this)?;\n\n        #[cfg(feature = \"intl\")]\n        {\n            use fixed_decimal::{Decimal, FloatPrecision};\n\n            use crate::builtins::intl::NumberFormat;\n\n            if !x.is_finite() {\n                return Ok(JsValue::new(js_string!(x)));\n            }\n\n            let locales = args.get_or_undefined(0).clone();\n            let options = args.get_or_undefined(1).clone();\n\n            // 2. Let numberFormat be ? Construct(%Intl.NumberFormat%, « locales, options »).\n            let number_format = NumberFormat::new(&locales, &options, context)?;\n            let mut x = Decimal::try_from_f64(x, FloatPrecision::RoundTrip)\n                .map_err(|err| JsNativeError::range().with_message(err.to_string()))?;\n\n            // 3. Return FormatNumeric(numberFormat, ! ToIntlMathematicalValue(x)).\n            Ok(js_string!(number_format.format(&mut x).to_string()).into())\n        }\n\n        #[cfg(not(feature = \"intl\"))]\n        {\n            Ok(JsValue::new(js_string!(x)))\n        }\n    }\n\n    /// `flt_str_to_exp` - used in `to_precision`\n    ///\n    /// This function traverses a string representing a number,\n    /// returning the floored log10 of this number.\n    fn flt_str_to_exp(flt: &str) -> i32 {\n        let mut non_zero_encountered = false;\n        let mut dot_encountered = false;\n        for (i, c) in flt.chars().enumerate() {\n            if c == '.' {\n                if non_zero_encountered {\n                    return (i as i32) - 1;\n                }\n                dot_encountered = true;\n            } else if c != '0' {\n                if dot_encountered {\n                    return 1 - (i as i32);\n                }\n                non_zero_encountered = true;\n            }\n        }\n        (flt.len() as i32) - 1\n    }\n\n    /// `round_to_precision` - used in `to_precision`\n    ///\n    /// This procedure has two roles:\n    /// - If there are enough or more than enough digits in the\n    ///   string to show the required precision, the number\n    ///   represented by these digits is rounded using string\n    ///   manipulation.\n    /// - Else, zeroes are appended to the string.\n    /// - Additionally, sometimes the exponent was wrongly computed and\n    ///   while up-rounding we find that we need an extra digit. When this\n    ///   happens, we return true so that the calling context can adjust\n    ///   the exponent. The string is kept at an exact length of `precision`.\n    ///\n    /// When this procedure returns, `digits` is exactly `precision` long.\n    fn round_to_precision(digits: &mut String, precision: usize) -> bool {\n        if digits.len() > precision {\n            let to_round = digits.split_off(precision);\n            let mut digit = digits\n                .pop()\n                .expect(\"already checked that length is bigger than precision\")\n                as u8;\n            if let Some(first) = to_round.chars().next()\n                && first > '4'\n            {\n                digit += 1;\n            }\n\n            if digit as char == ':' {\n                // ':' is '9' + 1\n                // need to propagate the increment backward\n                let mut replacement = String::from(\"0\");\n                let mut propagated = false;\n                for c in digits.chars().rev() {\n                    let d = match (c, propagated) {\n                        ('0'..='8', false) => (c as u8 + 1) as char,\n                        (_, false) => '0',\n                        (_, true) => c,\n                    };\n                    replacement.push(d);\n                    if d != '0' {\n                        propagated = true;\n                    }\n                }\n                digits.clear();\n                let replacement = if propagated {\n                    replacement.as_str()\n                } else {\n                    digits.push('1');\n                    &replacement.as_str()[1..]\n                };\n                for c in replacement.chars().rev() {\n                    digits.push(c);\n                }\n                !propagated\n            } else {\n                digits.push(digit as char);\n                false\n            }\n        } else {\n            digits.push_str(&\"0\".repeat(precision - digits.len()));\n            false\n        }\n    }\n\n    /// `Number.prototype.toPrecision( [precision] )`\n    ///\n    /// The `toPrecision()` method returns a string representing the Number object to the specified precision.\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///  - [MDN documentation][mdn]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-number.prototype.toprecision\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number/toPrecision\n    #[allow(clippy::wrong_self_convention)]\n    pub(crate) fn to_precision(\n        this: &JsValue,\n        args: &[JsValue],\n        context: &mut Context,\n    ) -> JsResult<JsValue> {\n        let precision = args.get_or_undefined(0);\n\n        // 1 & 6\n        let mut this_num = Self::this_number_value(this)?;\n        // 2\n        if precision.is_undefined() {\n            return Self::to_string(this, &[], context);\n        }\n\n        // 3\n        let precision = precision.to_integer_or_infinity(context)?;\n\n        // 4\n        if !this_num.is_finite() {\n            return Self::to_string(this, &[], context);\n        }\n\n        let precision = match precision {\n            IntegerOrInfinity::Integer(x) if (1..=100).contains(&x) => x as usize,\n            _ => {\n                // 5\n                return Err(JsNativeError::range()\n                    .with_message(\"precision must be an integer at least 1 and no greater than 100\")\n                    .into());\n            }\n        };\n        let precision_i32 = precision as i32;\n\n        // 7\n        let mut prefix = String::new(); // spec: 's'\n        let mut suffix: String; // spec: 'm'\n        let mut exponent: i32; // spec: 'e'\n\n        // 8\n        if this_num < 0.0 {\n            prefix.push('-');\n            this_num = -this_num;\n        }\n\n        // 9\n        if this_num == 0.0 {\n            suffix = \"0\".repeat(precision);\n            exponent = 0;\n        // 10\n        } else {\n            // Due to f64 limitations, this part differs a bit from the spec,\n            // but has the same effect. It manipulates the string constructed\n            // by `format`: digits with an optional dot between two of them.\n            suffix = format!(\"{this_num:.100}\");\n\n            // a: getting an exponent\n            exponent = Self::flt_str_to_exp(&suffix);\n            // b: getting relevant digits only\n            if exponent < 0 {\n                suffix = suffix.split_off((1 - exponent) as usize);\n            } else if let Some(n) = suffix.find('.') {\n                suffix.remove(n);\n            }\n            // impl: having exactly `precision` digits in `suffix`\n            if Self::round_to_precision(&mut suffix, precision) {\n                exponent += 1;\n            }\n\n            // c: switching to scientific notation\n            let great_exp = exponent >= precision_i32;\n            if exponent < -6 || great_exp {\n                // ii\n                if precision > 1 {\n                    suffix.insert(1, '.');\n                }\n                // vi\n                suffix.push('e');\n                // iii\n                if great_exp {\n                    suffix.push('+');\n                }\n                // iv, v\n                suffix.push_str(&exponent.to_string());\n\n                return Ok(JsValue::new(js_string!(prefix + &suffix)));\n            }\n        }\n\n        // 11\n        let e_inc = exponent + 1;\n        if e_inc == precision_i32 {\n            return Ok(JsValue::new(js_string!(prefix + &suffix)));\n        }\n\n        // 12\n        if exponent >= 0 {\n            suffix.insert(e_inc as usize, '.');\n        // 13\n        } else {\n            prefix.push('0');\n            prefix.push('.');\n            prefix.push_str(&\"0\".repeat(-e_inc as usize));\n        }\n\n        // 14\n        Ok(JsValue::new(js_string!(prefix + &suffix)))\n    }\n\n    // https://golang.org/src/math/nextafter.go\n    fn next_after(x: f64, y: f64) -> f64 {\n        if x.is_nan() || y.is_nan() {\n            f64::NAN\n        } else if (x - y) == 0. {\n            x\n        } else if x == 0.0 {\n            f64::from_bits(1).copysign(y)\n        } else if y > x || x > 0.0 {\n            f64::from_bits(x.to_bits() + 1)\n        } else {\n            f64::from_bits(x.to_bits() - 1)\n        }\n    }\n\n    // https://chromium.googlesource.com/v8/v8/+/refs/heads/master/src/numbers/conversions.cc#1230\n    #[allow(clippy::wrong_self_convention)]\n    pub(crate) fn to_js_string_radix(mut value: f64, radix: u8) -> JsString {\n        assert!(radix >= 2);\n        assert!(radix <= 36);\n        assert!(value.is_finite());\n        // assert_ne!(0.0, value);\n\n        // Character array used for conversion.\n        // Temporary buffer for the result. We start with the decimal point in the\n        // middle and write to the left for the integer part and to the right for the\n        // fractional part. 1024 characters for the exponent and 52 for the mantissa\n        // either way, with additional space for sign, decimal point and string\n        // termination should be sufficient.\n        let mut buffer: [u8; BUF_SIZE] = [0; BUF_SIZE];\n        let (int_buf, frac_buf) = buffer.split_at_mut(BUF_SIZE / 2);\n        let mut fraction_cursor = 0;\n        let negative = value.is_sign_negative();\n        if negative {\n            value = -value;\n        }\n        // Split the value into an integer part and a fractional part.\n        // let mut integer = value.trunc();\n        // let mut fraction = value.fract();\n        let mut integer = value.floor();\n        let mut fraction = value - integer;\n\n        // We only compute fractional digits up to the input double's precision.\n        let mut delta = 0.5 * (Self::next_after(value, f64::MAX) - value);\n        delta = Self::next_after(0.0, f64::MAX).max(delta);\n        assert!(delta > 0.0);\n        if fraction >= delta {\n            // Insert decimal point.\n            frac_buf[fraction_cursor] = b'.';\n            fraction_cursor += 1;\n            loop {\n                // Shift up by one digit.\n                fraction *= f64::from(radix);\n                delta *= f64::from(radix);\n                // Write digit.\n                let digit = fraction as u32;\n                frac_buf[fraction_cursor] = std::char::from_digit(digit, u32::from(radix))\n                    .expect(\"radix already checked\")\n                    as u8;\n                fraction_cursor += 1;\n                // Calculate remainder.\n                fraction -= f64::from(digit);\n                // Round to even.\n                if fraction + delta > 1.0\n                    && (fraction > 0.5 || (fraction - 0.5).abs() < f64::EPSILON && digit & 1 != 0)\n                {\n                    loop {\n                        // We need to back trace already written digits in case of carry-over.\n                        fraction_cursor -= 1;\n                        if fraction_cursor == 0 {\n                            //              CHECK_EQ('.', buffer[fraction_cursor]);\n                            // Carry over to the integer part.\n                            integer += 1.;\n                        } else {\n                            let c: u8 = frac_buf[fraction_cursor];\n                            // Reconstruct digit.\n                            let digit = if c > b'9' { c - b'a' + 10 } else { c - b'0' };\n                            if digit + 1 >= radix {\n                                continue;\n                            }\n                            frac_buf[fraction_cursor] =\n                                std::char::from_digit(u32::from(digit + 1), u32::from(radix))\n                                    .expect(\"digit was not a valid number in the given radix\")\n                                    as u8;\n                            fraction_cursor += 1;\n                        }\n                        break;\n                    }\n                    break;\n                }\n                if fraction < delta {\n                    break;\n                }\n            }\n        }\n\n        // Compute integer digits. Fill unrepresented digits with zero.\n        let mut int_iter = int_buf.iter_mut().enumerate().rev();\n        while FloatCore::integer_decode(integer / f64::from(radix)).1 > 0 {\n            integer /= f64::from(radix);\n            *int_iter.next().expect(\"integer buffer exhausted\").1 = b'0';\n        }\n\n        loop {\n            let remainder = integer % f64::from(radix);\n            *int_iter.next().expect(\"integer buffer exhausted\").1 =\n                std::char::from_digit(remainder as u32, u32::from(radix))\n                    .expect(\"remainder not a digit in the given number\") as u8;\n            integer = (integer - remainder) / f64::from(radix);\n            if integer <= 0f64 {\n                break;\n            }\n        }\n        // Add sign and terminate string.\n        if negative {\n            *int_iter.next().expect(\"integer buffer exhausted\").1 = b'-';\n        }\n        assert!(fraction_cursor < BUF_SIZE);\n\n        let integer_cursor = int_iter.next().expect(\"integer buffer exhausted\").0 + 1;\n        let fraction_cursor = fraction_cursor + BUF_SIZE / 2;\n        js_string!(&*String::from_utf8_lossy(\n            &buffer[integer_cursor..fraction_cursor]\n        ))\n    }\n\n    /// `Number.prototype.toString( [radix] )`\n    ///\n    /// The `toString()` method returns a string representing the specified Number object.\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///  - [MDN documentation][mdn]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-number.prototype.tostring\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number/toString\n    #[allow(clippy::wrong_self_convention)]\n    pub(crate) fn to_string(\n        this: &JsValue,\n        args: &[JsValue],\n        context: &mut Context,\n    ) -> JsResult<JsValue> {\n        // 1. Let x be ? thisNumberValue(this value).\n        let x = Self::this_number_value(this)?;\n\n        let radix = args.get_or_undefined(0);\n        let radix_number = if radix.is_undefined() {\n            // 2. If radix is undefined, let radixNumber be 10.\n            10\n        } else {\n            // 3. Else, let radixMV be ? ToIntegerOrInfinity(radix).\n            radix\n                .to_integer_or_infinity(context)?\n                .as_integer()\n                // 4. If radixNumber < 2 or radixNumber > 36, throw a RangeError exception.\n                .filter(|i| (2..=36).contains(i))\n                .ok_or_else(|| {\n                    JsNativeError::range()\n                        .with_message(\"radix must be an integer at least 2 and no greater than 36\")\n                })?\n        } as u8;\n\n        // 5. If radixNumber = 10, return ! ToString(x).\n        if radix_number == 10 {\n            return Ok(JsValue::new(JsString::from(x)));\n        }\n\n        if x == -0. {\n            return Ok(JsValue::new(js_string!(\"0\")));\n        } else if x.is_nan() {\n            return Ok(JsValue::new(js_string!(\"NaN\")));\n        } else if x.is_infinite() && x.is_sign_positive() {\n            return Ok(JsValue::new(js_string!(\"Infinity\")));\n        } else if x.is_infinite() && x.is_sign_negative() {\n            return Ok(JsValue::new(js_string!(\"-Infinity\")));\n        }\n\n        // This is a Optimization from the v8 source code to print values that can fit in a single character\n        // Since the actual num_to_string allocates a 2200 bytes buffer for actual conversion\n        // I am not sure if this part is effective as the v8 equivalent https://chromium.googlesource.com/v8/v8/+/refs/heads/master/src/builtins/number.tq#53\n        // // Fast case where the result is a one character string.\n        // if x.is_sign_positive() && x.fract() == 0.0 && x < radix_number as f64 {\n        //     return Ok(std::char::from_digit(x as u32, radix_number as u32).unwrap().to_string().into())\n        // }\n\n        // 6. Return the String representation of this Number value using the radix specified by radixNumber.\n        Ok(JsValue::new(Self::to_js_string_radix(x, radix_number)))\n    }\n\n    /// `Number.prototype.toString()`\n    ///\n    /// The `valueOf()` method returns the wrapped primitive value of a Number object.\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///  - [MDN documentation][mdn]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-number.prototype.valueof\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number/valueOf\n    pub(crate) fn value_of(this: &JsValue, _: &[JsValue], _: &mut Context) -> JsResult<JsValue> {\n        Ok(JsValue::new(Self::this_number_value(this)?))\n    }\n\n    /// `Number.isFinite( number )`\n    ///\n    /// Checks if the argument is a number, returning false if it isn't.\n    ///\n    /// If the number is `NaN`, `+∞`, or `-∞`, `false` is returned.\n    ///\n    /// Otherwise true is returned.\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///  - [MDN documentation][mdn]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-number.isfinite\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number/isFinite\n    #[allow(clippy::unnecessary_wraps)]\n    pub(crate) fn number_is_finite(\n        _: &JsValue,\n        args: &[JsValue],\n        _ctx: &mut Context,\n    ) -> JsResult<JsValue> {\n        // 1. If number is not a Number, return false.\n        // 2. If number is not finite, return false.\n        // 3. Otherwise, return true.\n        Ok(JsValue::new(args.first().is_some_and(\n            |val| match val.variant() {\n                JsVariant::Integer32(_) => true,\n                JsVariant::Float64(number) => number.is_finite(),\n                _ => false,\n            },\n        )))\n    }\n\n    /// `Number.isInteger( number )`\n    ///\n    /// Checks if the argument is an integer.\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///  - [MDN documentation][mdn]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-number.isinteger\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number/isInteger\n    #[allow(clippy::unnecessary_wraps)]\n    pub(crate) fn number_is_integer(\n        _: &JsValue,\n        args: &[JsValue],\n        _ctx: &mut Context,\n    ) -> JsResult<JsValue> {\n        Ok(args.first().is_some_and(Self::is_integer).into())\n    }\n\n    /// `Number.isNaN( number )`\n    ///\n    /// Checks if the argument is a number, returning false if it isn't.\n    ///\n    /// If the number is `NaN`, `true` is returned.\n    ///\n    /// Otherwise false is returned.\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///  - [MDN documentation][mdn]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-isnan-number\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number/isNaN\n    #[allow(clippy::unnecessary_wraps)]\n    pub(crate) fn number_is_nan(\n        _: &JsValue,\n        args: &[JsValue],\n        _ctx: &mut Context,\n    ) -> JsResult<JsValue> {\n        Ok(JsValue::new(\n            if let Some(number) = args.first().and_then(JsValue::as_number) {\n                number.is_nan()\n            } else {\n                false\n            },\n        ))\n    }\n\n    /// `Number.isSafeInteger( number )`\n    ///\n    /// Checks if the argument is an integer, returning false if it isn't.\n    ///\n    /// If `abs(number) ≤ MAX_SAFE_INTEGER`, `true` is returned.\n    ///\n    /// Otherwise false is returned.\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///  - [MDN documentation][mdn]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-isnan-number\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number/isNaN\n    #[allow(clippy::unnecessary_wraps)]\n    pub(crate) fn is_safe_integer(\n        _: &JsValue,\n        args: &[JsValue],\n        _ctx: &mut Context,\n    ) -> JsResult<JsValue> {\n        Ok(JsValue::new(match args.first().map(JsValue::variant) {\n            Some(JsVariant::Integer32(_)) => true,\n            Some(JsVariant::Float64(number)) if Self::is_float_integer(number) => {\n                number.abs() <= Self::MAX_SAFE_INTEGER\n            }\n            _ => false,\n        }))\n    }\n\n    /// Checks if the argument is a finite integer number value.\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-isinteger\n    pub(crate) fn is_integer(val: &JsValue) -> bool {\n        match val.variant() {\n            JsVariant::Integer32(_) => true,\n            JsVariant::Float64(number) => Self::is_float_integer(number),\n            _ => false,\n        }\n    }\n\n    /// Checks if the float argument is an integer.\n    #[allow(clippy::float_cmp)]\n    pub(crate) fn is_float_integer(number: f64) -> bool {\n        number.is_finite() && number.trunc() == number\n    }\n\n    /// The abstract operation `Number::equal` takes arguments\n    /// x (a Number) and y (a Number). It performs the following steps when called:\n    ///\n    /// <https://tc39.es/ecma262/#sec-numeric-types-number-equal>\n    #[allow(clippy::float_cmp)]\n    pub(crate) fn equal(x: f64, y: f64) -> bool {\n        x == y\n    }\n\n    /// The abstract operation `Number::sameValue` takes arguments\n    /// x (a Number) and y (a Number). It performs the following steps when called:\n    ///\n    /// <https://tc39.es/ecma262/#sec-numeric-types-number-sameValue>\n    #[allow(clippy::float_cmp)]\n    pub(crate) fn same_value(a: f64, b: f64) -> bool {\n        if a.is_nan() && b.is_nan() {\n            return true;\n        }\n        a == b && a.signum() == b.signum()\n    }\n\n    /// The abstract operation `Number::sameValueZero` takes arguments\n    /// x (a Number) and y (a Number). It performs the following steps when called:\n    ///\n    /// <https://tc39.es/ecma262/#sec-numeric-types-number-sameValueZero>\n    #[allow(clippy::float_cmp)]\n    pub(crate) fn same_value_zero(x: f64, y: f64) -> bool {\n        if x.is_nan() && y.is_nan() {\n            return true;\n        }\n\n        x == y\n    }\n\n    #[allow(clippy::float_cmp)]\n    pub(crate) fn less_than(x: f64, y: f64) -> AbstractRelation {\n        if x.is_nan() || y.is_nan() {\n            return AbstractRelation::Undefined;\n        }\n        if x == y || x == 0.0 && y == -0.0 || x == -0.0 && y == 0.0 {\n            return AbstractRelation::False;\n        }\n        if x.is_infinite() && x.is_sign_positive() {\n            return AbstractRelation::False;\n        }\n        if y.is_infinite() && y.is_sign_positive() {\n            return AbstractRelation::True;\n        }\n        if x.is_infinite() && x.is_sign_negative() {\n            return AbstractRelation::True;\n        }\n        if y.is_infinite() && y.is_sign_negative() {\n            return AbstractRelation::False;\n        }\n        (x < y).into()\n    }\n\n    pub(crate) fn not(x: f64) -> i32 {\n        let x = f64_to_int32(x);\n        !x\n    }\n}\n\n/// Helper function that formats a float as a ES6-style exponential number string.\nfn f64_to_exponential(n: f64) -> JsString {\n    let s = format!(\"{n:e}\");\n    match n.abs() {\n        x if x >= 1.0 || x == 0.0 => js_string!(s.cow_replace('e', \"e+\")),\n        _ => js_string!(s),\n    }\n}\n\n/// Helper function that formats a float as a ES6-style exponential number string with a given precision.\n// We can't use the same approach as in `f64_to_exponential`\n// because in cases like (0.999).toExponential(0) the result will be 1e0.\n// Instead we get the index of 'e', and if the next character is not '-' we insert the plus sign\nfn f64_to_exponential_with_precision(n: f64, prec: usize) -> JsString {\n    let mut res = format!(\"{n:.prec$e}\");\n    let idx = res.find('e').expect(\"'e' not found in exponential string\");\n    if res.as_bytes()[idx + 1] != b'-' {\n        res.insert(idx + 1, '+');\n    }\n    js_string!(res)\n}\n"
  },
  {
    "path": "core/engine/src/builtins/number/tests.rs",
    "content": "use boa_macros::js_str;\n\nuse crate::{\n    JsNativeErrorKind, TestAction, builtins::Number, run_test_actions, value::AbstractRelation,\n};\n\n#[test]\nfn integer_number_primitive_to_number_object() {\n    run_test_actions([TestAction::assert_eq(\"(100).toString()\", js_str!(\"100\"))]);\n}\n\n#[test]\nfn call_number() {\n    run_test_actions([\n        TestAction::assert_eq(\"Number()\", 0),\n        TestAction::assert_eq(\"Number(1)\", 1),\n        TestAction::assert_eq(\"Number(2.1)\", 2.1),\n        TestAction::assert_eq(\"Number('3.2')\", 3.2),\n        TestAction::assert_eq(\"Number(true)\", 1),\n        TestAction::assert_eq(\"Number(false)\", 0),\n        TestAction::assert_eq(\"Number('I am not a number')\", f64::NAN),\n        TestAction::assert_eq(\"Number('2.34e+2')\", 234),\n    ]);\n}\n\n#[test]\nfn to_exponential() {\n    run_test_actions([\n        TestAction::assert_eq(\"Number().toExponential()\", js_str!(\"0e+0\")),\n        TestAction::assert_eq(\"Number(5).toExponential()\", js_str!(\"5e+0\")),\n        TestAction::assert_eq(\"Number(1.234).toExponential()\", js_str!(\"1.234e+0\")),\n        TestAction::assert_eq(\"Number(1234).toExponential()\", js_str!(\"1.234e+3\")),\n        TestAction::assert_eq(\n            \"Number('I am also not a number').toExponential()\",\n            js_str!(\"NaN\"),\n        ),\n        TestAction::assert_eq(\"Number('1.23e+2').toExponential()\", js_str!(\"1.23e+2\")),\n    ]);\n}\n\n#[test]\nfn to_fixed() {\n    run_test_actions([\n        TestAction::assert_eq(\"Number().toFixed()\", js_str!(\"0\")),\n        TestAction::assert_eq(\"Number('3.456e+4').toFixed()\", js_str!(\"34560\")),\n        TestAction::assert_eq(\"Number('3.456e-4').toFixed()\", js_str!(\"0\")),\n        TestAction::assert_eq(\"Number(5).toFixed()\", js_str!(\"5\")),\n        TestAction::assert_eq(\"Number('I am also not a number').toFixed()\", js_str!(\"NaN\")),\n        TestAction::assert_eq(\"(1.35).toFixed(1)\", js_str!(\"1.4\")),\n        // Test cases from https://source.chromium.org/chromium/chromium/src/+/main:v8/test/mjsunit/number-tostring-func.js;l=157-240;drc=aa3518a0f37245ebe8f062dce97ee492e2a41652\n        TestAction::assert_eq(\"(NaN).toFixed(2)\", js_str!(\"NaN\")),\n        TestAction::assert_eq(\"(1/0).toFixed(2)\", js_str!(\"Infinity\")),\n        TestAction::assert_eq(\"(-1/0).toFixed(2)\", js_str!(\"-Infinity\")),\n        TestAction::assert_eq(\n            \"(1111111111111111111111).toFixed(8)\",\n            js_str!(\"1.1111111111111111e+21\"),\n        ),\n        TestAction::assert_eq(\"(0.1).toFixed(1)\", js_str!(\"0.1\")),\n        TestAction::assert_eq(\"(0.1).toFixed(2)\", js_str!(\"0.10\")),\n        TestAction::assert_eq(\"(0.1).toFixed(3)\", js_str!(\"0.100\")),\n        TestAction::assert_eq(\"(0.01).toFixed(2)\", js_str!(\"0.01\")),\n        TestAction::assert_eq(\"(0.01).toFixed(3)\", js_str!(\"0.010\")),\n        TestAction::assert_eq(\"(0.01).toFixed(4)\", js_str!(\"0.0100\")),\n        TestAction::assert_eq(\"(0.001).toFixed(2)\", js_str!(\"0.00\")),\n        TestAction::assert_eq(\"(0.001).toFixed(3)\", js_str!(\"0.001\")),\n        TestAction::assert_eq(\"(0.001).toFixed(4)\", js_str!(\"0.0010\")),\n        TestAction::assert_eq(\"(1).toFixed(4)\", js_str!(\"1.0000\")),\n        TestAction::assert_eq(\"(1).toFixed(1)\", js_str!(\"1.0\")),\n        TestAction::assert_eq(\"(1).toFixed(0)\", js_str!(\"1\")),\n        TestAction::assert_eq(\"(12).toFixed(0)\", js_str!(\"12\")),\n        TestAction::assert_eq(\"(1.1).toFixed(0)\", js_str!(\"1\")),\n        TestAction::assert_eq(\"(12.1).toFixed(0)\", js_str!(\"12\")),\n        TestAction::assert_eq(\"(1.12).toFixed(0)\", js_str!(\"1\")),\n        TestAction::assert_eq(\"(12.12).toFixed(0)\", js_str!(\"12\")),\n        TestAction::assert_eq(\"(0.0000006).toFixed(7)\", js_str!(\"0.0000006\")),\n        TestAction::assert_eq(\"(0.00000006).toFixed(8)\", js_str!(\"0.00000006\")),\n        TestAction::assert_eq(\"(0.00000006).toFixed(9)\", js_str!(\"0.000000060\")),\n        TestAction::assert_eq(\"(0.00000006).toFixed(10)\", js_str!(\"0.0000000600\")),\n        TestAction::assert_eq(\"(0).toFixed(0)\", js_str!(\"0\")),\n        TestAction::assert_eq(\"(0).toFixed(1)\", js_str!(\"0.0\")),\n        TestAction::assert_eq(\"(0).toFixed(2)\", js_str!(\"0.00\")),\n        TestAction::assert_eq(\n            \"(-1111111111111111111111).toFixed(8)\",\n            js_str!(\"-1.1111111111111111e+21\"),\n        ),\n        TestAction::assert_eq(\"(-0.1).toFixed(1)\", js_str!(\"-0.1\")),\n        TestAction::assert_eq(\"(-0.1).toFixed(2)\", js_str!(\"-0.10\")),\n        TestAction::assert_eq(\"(-0.1).toFixed(3)\", js_str!(\"-0.100\")),\n        TestAction::assert_eq(\"(-0.01).toFixed(2)\", js_str!(\"-0.01\")),\n        TestAction::assert_eq(\"(-0.01).toFixed(3)\", js_str!(\"-0.010\")),\n        TestAction::assert_eq(\"(-0.01).toFixed(4)\", js_str!(\"-0.0100\")),\n        TestAction::assert_eq(\"(-0.001).toFixed(2)\", js_str!(\"-0.00\")),\n        TestAction::assert_eq(\"(-0.001).toFixed(3)\", js_str!(\"-0.001\")),\n        TestAction::assert_eq(\"(-0.001).toFixed(4)\", js_str!(\"-0.0010\")),\n        TestAction::assert_eq(\"(-1).toFixed(4)\", js_str!(\"-1.0000\")),\n        TestAction::assert_eq(\"(-1).toFixed(1)\", js_str!(\"-1.0\")),\n        TestAction::assert_eq(\"(-1).toFixed(0)\", js_str!(\"-1\")),\n        TestAction::assert_eq(\"(-1.1).toFixed(0)\", js_str!(\"-1\")),\n        TestAction::assert_eq(\"(-12.1).toFixed(0)\", js_str!(\"-12\")),\n        TestAction::assert_eq(\"(-1.12).toFixed(0)\", js_str!(\"-1\")),\n        TestAction::assert_eq(\"(-12.12).toFixed(0)\", js_str!(\"-12\")),\n        TestAction::assert_eq(\"(-0.0000006).toFixed(7)\", js_str!(\"-0.0000006\")),\n        TestAction::assert_eq(\"(-0.00000006).toFixed(8)\", js_str!(\"-0.00000006\")),\n        TestAction::assert_eq(\"(-0.00000006).toFixed(9)\", js_str!(\"-0.000000060\")),\n        TestAction::assert_eq(\"(-0.00000006).toFixed(10)\", js_str!(\"-0.0000000600\")),\n        TestAction::assert_eq(\"(-0).toFixed(0)\", js_str!(\"0\")),\n        TestAction::assert_eq(\"(-0).toFixed(1)\", js_str!(\"0.0\")),\n        TestAction::assert_eq(\"(-0).toFixed(2)\", js_str!(\"0.00\")),\n        TestAction::assert_eq(\"(0.00001).toFixed(5)\", js_str!(\"0.00001\")),\n        TestAction::assert_eq(\n            \"(0.0000000000000000001).toFixed(20)\",\n            js_str!(\"0.00000000000000000010\"),\n        ),\n        TestAction::assert_eq(\"(0.00001).toFixed(17)\", js_str!(\"0.00001000000000000\")),\n        TestAction::assert_eq(\"(1).toFixed(17)\", js_str!(\"1.00000000000000000\")),\n        TestAction::assert_eq(\n            \"(100000000000000128).toFixed(1)\",\n            js_str!(\"100000000000000128.0\"),\n        ),\n        TestAction::assert_eq(\n            \"(10000000000000128).toFixed(2)\",\n            js_str!(\"10000000000000128.00\"),\n        ),\n        TestAction::assert_eq(\n            \"(10000000000000128).toFixed(20)\",\n            js_str!(\"10000000000000128.00000000000000000000\"),\n        ),\n        TestAction::assert_eq(\"(-42).toFixed(3)\", js_str!(\"-42.000\")),\n        TestAction::assert_eq(\n            \"(-0.0000000000000000001).toFixed(20)\",\n            js_str!(\"-0.00000000000000000010\"),\n        ),\n        TestAction::assert_eq(\n            \"(0.123123123123123).toFixed(20)\",\n            js_str!(\"0.12312312312312299889\"),\n        ),\n        TestAction::assert_eq(\n            \"(-1000000000000000128).toFixed()\",\n            js_str!(\"-1000000000000000128\"),\n        ),\n        TestAction::assert_eq(\"(0).toFixed()\", js_str!(\"0\")),\n        TestAction::assert_eq(\n            \"(1000000000000000128).toFixed()\",\n            js_str!(\"1000000000000000128\"),\n        ),\n        TestAction::assert_eq(\"(1000).toFixed()\", js_str!(\"1000\")),\n        TestAction::assert_eq(\"(0.00001).toFixed()\", js_str!(\"0\")),\n        // Test that we round up even when the last digit generated is even.\n        // dtoa does not do this in its original form.\n        TestAction::assert_eq(\"(0.5).toFixed(0)\", js_str!(\"1\")),\n        TestAction::assert_eq(\"(-0.5).toFixed(0)\", js_str!(\"-1\")),\n        TestAction::assert_eq(\"(1.25).toFixed(1)\", js_str!(\"1.3\")),\n        // This is bizarre, but Spidermonkey and KJS behave the same.\n        TestAction::assert_eq(\"(234.2040).toFixed(4)\", js_str!(\"234.2040\")),\n        TestAction::assert_eq(\"(234.2040506).toFixed(4)\", js_str!(\"234.2041\")),\n    ]);\n}\n\n// https://github.com/boa-dev/boa/issues/2609\n#[test]\nfn issue_2609() {\n    run_test_actions([\n        TestAction::assert_eq(\"(1.25).toFixed(1)\", js_str!(\"1.3\")),\n        TestAction::assert_eq(\"(1.35).toFixed(1)\", js_str!(\"1.4\")),\n    ]);\n}\n\n#[test]\nfn to_locale_string() {\n    run_test_actions([\n        TestAction::assert_eq(\"Number().toLocaleString()\", js_str!(\"0\")),\n        TestAction::assert_eq(\"Number(5).toLocaleString()\", js_str!(\"5\")),\n        TestAction::assert_eq(\"Number(-25).toLocaleString()\", js_str!(\"-25\")),\n        TestAction::assert_eq(\"NaN.toLocaleString()\", js_str!(\"NaN\")),\n        TestAction::assert_eq(\"Infinity.toLocaleString()\", js_str!(\"Infinity\")),\n        TestAction::assert_eq(\"(-Infinity).toLocaleString()\", js_str!(\"-Infinity\")),\n    ]);\n}\n\n#[test]\n#[cfg(feature = \"intl\")]\nfn to_locale_string_intl() {\n    run_test_actions([\n        TestAction::assert_eq(\"(345600).toLocaleString('en-US')\", js_str!(\"345,600\")),\n        TestAction::assert_eq(\"(1234.5).toLocaleString('de-DE')\", js_str!(\"1.234,5\")),\n        TestAction::assert_eq(\n            \"(1000).toLocaleString('en-US', { useGrouping: false })\",\n            js_str!(\"1000\"),\n        ),\n        TestAction::assert_eq(\n            \"(12.3).toLocaleString('en-US', { minimumFractionDigits: 2 })\",\n            js_str!(\"12.30\"),\n        ),\n    ]);\n}\n\n#[test]\nfn to_precision() {\n    const ERROR: &str = \"precision must be an integer at least 1 and no greater than 100\";\n    run_test_actions([\n        TestAction::assert_eq(\"(1/0).toPrecision(3)\", js_str!(\"Infinity\")),\n        TestAction::assert_eq(\"Number().toPrecision()\", js_str!(\"0\")),\n        TestAction::assert_eq(\"Number().toPrecision(undefined)\", js_str!(\"0\")),\n        TestAction::assert_eq(\"(123456789).toPrecision(1)\", js_str!(\"1e+8\")),\n        TestAction::assert_eq(\"(123456789).toPrecision(4)\", js_str!(\"1.235e+8\")),\n        TestAction::assert_eq(\"(123456789).toPrecision(9)\", js_str!(\"123456789\")),\n        TestAction::assert_eq(\"(-123456789).toPrecision(4)\", js_str!(\"-1.235e+8\")),\n        TestAction::assert_eq(\n            \"(123456789).toPrecision(50)\",\n            js_str!(\"123456789.00000000000000000000000000000000000000000\"),\n        ),\n        TestAction::assert_eq(\"(0.1).toPrecision(4)\", js_str!(\"0.1000\")),\n        TestAction::assert_eq(\n            \"(1/3).toPrecision(60)\",\n            js_str!(\"0.333333333333333314829616256247390992939472198486328125000000\"),\n        ),\n        TestAction::assert_native_error(\"(1).toPrecision(101)\", JsNativeErrorKind::Range, ERROR),\n        TestAction::assert_native_error(\"(1).toPrecision(0)\", JsNativeErrorKind::Range, ERROR),\n        TestAction::assert_native_error(\"(1).toPrecision(-2000)\", JsNativeErrorKind::Range, ERROR),\n        TestAction::assert_native_error(\"(1).toPrecision('%')\", JsNativeErrorKind::Range, ERROR),\n    ]);\n}\n\n#[test]\nfn to_string() {\n    run_test_actions([\n        TestAction::assert_eq(\"Number(NaN).toString()\", js_str!(\"NaN\")),\n        TestAction::assert_eq(\"Number(1/0).toString()\", js_str!(\"Infinity\")),\n        TestAction::assert_eq(\"Number(-1/0).toString()\", js_str!(\"-Infinity\")),\n        TestAction::assert_eq(\"Number(0).toString()\", js_str!(\"0\")),\n        TestAction::assert_eq(\"Number(9).toString()\", js_str!(\"9\")),\n        TestAction::assert_eq(\"Number(90).toString()\", js_str!(\"90\")),\n        TestAction::assert_eq(\"Number(90.12).toString()\", js_str!(\"90.12\")),\n        TestAction::assert_eq(\"Number(0.1).toString()\", js_str!(\"0.1\")),\n        TestAction::assert_eq(\"Number(0.01).toString()\", js_str!(\"0.01\")),\n        TestAction::assert_eq(\"Number(0.0123).toString()\", js_str!(\"0.0123\")),\n        TestAction::assert_eq(\"Number(0.00001).toString()\", js_str!(\"0.00001\")),\n        TestAction::assert_eq(\"Number(0.000001).toString()\", js_str!(\"0.000001\")),\n        TestAction::assert_eq(\"Number(NaN).toString(16)\", js_str!(\"NaN\")),\n        TestAction::assert_eq(\"Number(1/0).toString(16)\", js_str!(\"Infinity\")),\n        TestAction::assert_eq(\"Number(-1/0).toString(16)\", js_str!(\"-Infinity\")),\n        TestAction::assert_eq(\"Number(0).toString(16)\", js_str!(\"0\")),\n        TestAction::assert_eq(\"Number(9).toString(16)\", js_str!(\"9\")),\n        TestAction::assert_eq(\"Number(90).toString(16)\", js_str!(\"5a\")),\n        TestAction::assert_eq(\"Number(90.12).toString(16)\", js_str!(\"5a.1eb851eb852\")),\n        TestAction::assert_eq(\"Number(0.1).toString(16)\", js_str!(\"0.1999999999999a\")),\n        TestAction::assert_eq(\"Number(0.01).toString(16)\", js_str!(\"0.028f5c28f5c28f6\")),\n        TestAction::assert_eq(\"Number(0.0123).toString(16)\", js_str!(\"0.032617c1bda511a\")),\n        TestAction::assert_eq(\n            \"Number(111111111111111111111).toString(16)\",\n            js_str!(\"605f9f6dd18bc8000\"),\n        ),\n        TestAction::assert_eq(\n            \"Number(1111111111111111111111).toString(16)\",\n            js_str!(\"3c3bc3a4a2f75c0000\"),\n        ),\n        TestAction::assert_eq(\n            \"Number(11111111111111111111111).toString(16)\",\n            js_str!(\"25a55a46e5da9a00000\"),\n        ),\n        TestAction::assert_eq(\n            \"Number(0.00001).toString(16)\",\n            js_str!(\"0.0000a7c5ac471b4788\"),\n        ),\n        TestAction::assert_eq(\n            \"Number(0.000001).toString(16)\",\n            js_str!(\"0.000010c6f7a0b5ed8d\"),\n        ),\n        TestAction::assert_eq(\n            \"Number(0.0000001).toString(16)\",\n            js_str!(\"0.000001ad7f29abcaf48\"),\n        ),\n        TestAction::assert_eq(\n            \"Number(0.00000012).toString(16)\",\n            js_str!(\"0.000002036565348d256\"),\n        ),\n        TestAction::assert_eq(\n            \"Number(0.000000123).toString(16)\",\n            js_str!(\"0.0000021047ee22aa466\"),\n        ),\n        TestAction::assert_eq(\n            \"Number(0.00000001).toString(16)\",\n            js_str!(\"0.0000002af31dc4611874\"),\n        ),\n        TestAction::assert_eq(\n            \"Number(0.000000012).toString(16)\",\n            js_str!(\"0.000000338a23b87483be\"),\n        ),\n        TestAction::assert_eq(\n            \"Number(0.0000000123).toString(16)\",\n            js_str!(\"0.00000034d3fe36aaa0a2\"),\n        ),\n        TestAction::assert_eq(\"Number(-0).toString(16)\", js_str!(\"0\")),\n        TestAction::assert_eq(\"Number(-9).toString(16)\", js_str!(\"-9\")),\n        //\n        TestAction::assert_eq(\"Number(-90).toString(16)\", js_str!(\"-5a\")),\n        TestAction::assert_eq(\"Number(-90.12).toString(16)\", js_str!(\"-5a.1eb851eb852\")),\n        TestAction::assert_eq(\"Number(-0.1).toString(16)\", js_str!(\"-0.1999999999999a\")),\n        TestAction::assert_eq(\"Number(-0.01).toString(16)\", js_str!(\"-0.028f5c28f5c28f6\")),\n        TestAction::assert_eq(\n            \"Number(-0.0123).toString(16)\",\n            js_str!(\"-0.032617c1bda511a\"),\n        ),\n        TestAction::assert_eq(\n            \"Number(-111111111111111111111).toString(16)\",\n            js_str!(\"-605f9f6dd18bc8000\"),\n        ),\n        TestAction::assert_eq(\n            \"Number(-1111111111111111111111).toString(16)\",\n            js_str!(\"-3c3bc3a4a2f75c0000\"),\n        ),\n        TestAction::assert_eq(\n            \"Number(-11111111111111111111111).toString(16)\",\n            js_str!(\"-25a55a46e5da9a00000\"),\n        ),\n        TestAction::assert_eq(\n            \"Number(-0.00001).toString(16)\",\n            js_str!(\"-0.0000a7c5ac471b4788\"),\n        ),\n        TestAction::assert_eq(\n            \"Number(-0.000001).toString(16)\",\n            js_str!(\"-0.000010c6f7a0b5ed8d\"),\n        ),\n        TestAction::assert_eq(\n            \"Number(-0.0000001).toString(16)\",\n            js_str!(\"-0.000001ad7f29abcaf48\"),\n        ),\n        TestAction::assert_eq(\n            \"Number(-0.00000012).toString(16)\",\n            js_str!(\"-0.000002036565348d256\"),\n        ),\n        TestAction::assert_eq(\n            \"Number(-0.000000123).toString(16)\",\n            js_str!(\"-0.0000021047ee22aa466\"),\n        ),\n        TestAction::assert_eq(\n            \"Number(-0.00000001).toString(16)\",\n            js_str!(\"-0.0000002af31dc4611874\"),\n        ),\n        TestAction::assert_eq(\n            \"Number(-0.000000012).toString(16)\",\n            js_str!(\"-0.000000338a23b87483be\"),\n        ),\n        TestAction::assert_eq(\n            \"Number(-0.0000000123).toString(16)\",\n            js_str!(\"-0.00000034d3fe36aaa0a2\"),\n        ),\n    ]);\n}\n\n#[test]\nfn num_to_string_exponential() {\n    run_test_actions([\n        TestAction::assert_eq(\"(0).toString()\", js_str!(\"0\")),\n        TestAction::assert_eq(\"(-0).toString()\", js_str!(\"0\")),\n        TestAction::assert_eq(\n            \"(111111111111111111111).toString()\",\n            js_str!(\"111111111111111110000\"),\n        ),\n        TestAction::assert_eq(\n            \"(1111111111111111111111).toString()\",\n            js_str!(\"1.1111111111111111e+21\"),\n        ),\n        TestAction::assert_eq(\n            \"(11111111111111111111111).toString()\",\n            js_str!(\"1.1111111111111111e+22\"),\n        ),\n        TestAction::assert_eq(\"(0.0000001).toString()\", js_str!(\"1e-7\")),\n        TestAction::assert_eq(\"(0.00000012).toString()\", js_str!(\"1.2e-7\")),\n        TestAction::assert_eq(\"(0.000000123).toString()\", js_str!(\"1.23e-7\")),\n        TestAction::assert_eq(\"(0.00000001).toString()\", js_str!(\"1e-8\")),\n        TestAction::assert_eq(\"(0.000000012).toString()\", js_str!(\"1.2e-8\")),\n        TestAction::assert_eq(\"(0.0000000123).toString()\", js_str!(\"1.23e-8\")),\n    ]);\n}\n\n#[test]\nfn value_of() {\n    // TODO: In addition to parsing numbers from strings, parse them bare As of October 2019\n    // the parser does not understand scientific e.g., Xe+Y or -Xe-Y notation.\n    run_test_actions([\n        TestAction::assert_eq(\"Number().valueOf()\", 0),\n        TestAction::assert_eq(\"Number('123').valueOf()\", 123),\n        TestAction::assert_eq(\"Number(1.234).valueOf()\", 1.234),\n        TestAction::assert_eq(\"Number('1.2e+4').valueOf()\", 12_000),\n        TestAction::assert_eq(\"Number('-1.2e+4').valueOf()\", -12_000),\n    ]);\n}\n\n#[test]\nfn equal() {\n    assert!(Number::equal(0.0, 0.0));\n    assert!(Number::equal(-0.0, 0.0));\n    assert!(Number::equal(0.0, -0.0));\n    assert!(!Number::equal(f64::NAN, -0.0));\n    assert!(!Number::equal(0.0, f64::NAN));\n\n    assert!(Number::equal(1.0, 1.0));\n}\n\n#[test]\nfn same_value() {\n    assert!(Number::same_value(0.0, 0.0));\n    assert!(!Number::same_value(-0.0, 0.0));\n    assert!(!Number::same_value(0.0, -0.0));\n    assert!(!Number::same_value(f64::NAN, -0.0));\n    assert!(!Number::same_value(0.0, f64::NAN));\n    assert!(Number::equal(1.0, 1.0));\n}\n\n#[test]\nfn less_than() {\n    assert_eq!(\n        Number::less_than(f64::NAN, 0.0),\n        AbstractRelation::Undefined\n    );\n    assert_eq!(\n        Number::less_than(0.0, f64::NAN),\n        AbstractRelation::Undefined\n    );\n    assert_eq!(\n        Number::less_than(f64::NEG_INFINITY, 0.0),\n        AbstractRelation::True\n    );\n    assert_eq!(\n        Number::less_than(0.0, f64::NEG_INFINITY),\n        AbstractRelation::False\n    );\n    assert_eq!(\n        Number::less_than(f64::INFINITY, 0.0),\n        AbstractRelation::False\n    );\n    assert_eq!(\n        Number::less_than(0.0, f64::INFINITY),\n        AbstractRelation::True\n    );\n}\n\n#[test]\nfn same_value_zero() {\n    assert!(Number::same_value_zero(0.0, 0.0));\n    assert!(Number::same_value_zero(-0.0, 0.0));\n    assert!(Number::same_value_zero(0.0, -0.0));\n    assert!(!Number::same_value_zero(f64::NAN, -0.0));\n    assert!(!Number::same_value_zero(0.0, f64::NAN));\n    assert!(Number::equal(1.0, 1.0));\n}\n\n#[test]\nfn from_bigint() {\n    run_test_actions([\n        TestAction::assert_eq(\"Number(0n)\", 0),\n        TestAction::assert_eq(\"Number(100000n)\", 100_000),\n        TestAction::assert_eq(\"Number(100000n)\", 100_000),\n        TestAction::assert_eq(\"Number(1n << 1240n)\", f64::INFINITY),\n    ]);\n}\n\n#[test]\nfn number_constants() {\n    run_test_actions([\n        TestAction::assert_eq(\"Number.EPSILON\", f64::EPSILON),\n        TestAction::assert_eq(\"Number.MAX_SAFE_INTEGER\", Number::MAX_SAFE_INTEGER),\n        TestAction::assert_eq(\"Number.MIN_SAFE_INTEGER\", Number::MIN_SAFE_INTEGER),\n        TestAction::assert_eq(\"Number.MAX_VALUE\", f64::MAX),\n        TestAction::assert_eq(\"Number.MIN_VALUE\", Number::MIN_VALUE),\n        TestAction::assert_eq(\"Number.POSITIVE_INFINITY\", f64::INFINITY),\n        TestAction::assert_eq(\"Number.NEGATIVE_INFINITY\", -f64::INFINITY),\n    ]);\n}\n\n#[test]\nfn parse_int() {\n    run_test_actions([\n        TestAction::assert_eq(\"parseInt('6')\", 6),\n        TestAction::assert_eq(\"parseInt('-9')\", -9),\n        TestAction::assert_eq(\"parseInt(100)\", 100),\n        TestAction::assert_eq(\"parseInt(100.5)\", 100),\n        TestAction::assert_eq(\"parseInt('0xA')\", 10),\n        // This test demonstrates that this version of parseInt treats strings starting with 0 to be parsed with\n        // a radix 10 if no radix is specified. Some alternative implementations default to a radix of 8.\n        TestAction::assert_eq(\"parseInt('018')\", 18),\n        TestAction::assert_eq(\"parseInt('hello')\", f64::NAN),\n        TestAction::assert_eq(\"parseInt(undefined)\", f64::NAN),\n        // Shows that no arguments to parseInt is treated the same as if undefined was\n        // passed as the first argument.\n        TestAction::assert_eq(\"parseInt()\", f64::NAN),\n        // Shows that extra arguments to parseInt are ignored.\n        TestAction::assert_eq(\"parseInt('100', 10, 10)\", 100),\n    ]);\n}\n\n#[test]\nfn parse_int_varying_radix() {\n    let base_str = \"1000\";\n    let tests = (2..36).flat_map(|radix| {\n        let expected = i32::from_str_radix(base_str, radix).unwrap();\n        [\n            TestAction::assert_eq(format!(\"parseInt('{base_str}', {radix} )\"), expected),\n            TestAction::assert_eq(format!(\"parseInt('-{base_str}', {radix} )\"), -expected),\n        ]\n    });\n\n    run_test_actions(tests);\n}\n\n#[test]\nfn parse_float() {\n    run_test_actions([\n        TestAction::assert_eq(\"parseFloat('6.5')\", 6.5),\n        TestAction::assert_eq(\"parseFloat(10)\", 10),\n        TestAction::assert_eq(\"parseFloat('8')\", 8),\n        TestAction::assert_eq(\"parseFloat(17.5)\", 17.5),\n        TestAction::assert_eq(\"parseFloat('-99.7')\", -99.7),\n        TestAction::assert_eq(\"parseFloat('hello')\", f64::NAN),\n        TestAction::assert_eq(\"parseFloat(undefined)\", f64::NAN),\n        // No arguments to parseFloat is treated the same as passing undefined as the first argument.\n        TestAction::assert_eq(\"parseFloat()\", f64::NAN),\n        // Shows that the parseFloat function ignores extra arguments.\n        TestAction::assert_eq(\"parseFloat('100.5', 10)\", 100.5),\n    ]);\n}\n\n#[test]\nfn global_is_finite() {\n    run_test_actions([\n        TestAction::assert(\"!isFinite(Infinity)\"),\n        TestAction::assert(\"!isFinite(NaN)\"),\n        TestAction::assert(\"!isFinite(-Infinity)\"),\n        TestAction::assert(\"isFinite(0)\"),\n        TestAction::assert(\"isFinite(2e64)\"),\n        TestAction::assert(\"isFinite(910)\"),\n        TestAction::assert(\"isFinite(null)\"),\n        TestAction::assert(\"isFinite('0')\"),\n        TestAction::assert(\"!isFinite()\"),\n    ]);\n}\n\n#[test]\nfn global_is_nan() {\n    run_test_actions([\n        TestAction::assert(\"isNaN(NaN)\"),\n        TestAction::assert(\"isNaN('NaN')\"),\n        TestAction::assert(\"isNaN(undefined)\"),\n        TestAction::assert(\"isNaN({})\"),\n        TestAction::assert(\"!isNaN(true)\"),\n        TestAction::assert(\"!isNaN(null)\"),\n        TestAction::assert(\"!isNaN(37)\"),\n        TestAction::assert(\"!isNaN('37')\"),\n        TestAction::assert(\"!isNaN('37.37')\"),\n        TestAction::assert(\"isNaN('37,5')\"),\n        TestAction::assert(\"isNaN('123ABC')\"),\n        // Incorrect due to ToNumber implementation inconsistencies.\n        // TestAction::assert(\"isNaN('')\"),\n        // TestAction::assert(\"isNaN(' ')\"),\n        TestAction::assert(\"isNaN('blabla')\"),\n    ]);\n}\n\n#[test]\nfn number_is_finite() {\n    run_test_actions([\n        TestAction::assert(\"!Number.isFinite(Infinity)\"),\n        TestAction::assert(\"!Number.isFinite(NaN)\"),\n        TestAction::assert(\"!Number.isFinite(-Infinity)\"),\n        TestAction::assert(\"Number.isFinite(0)\"),\n        TestAction::assert(\"Number.isFinite(2e64)\"),\n        TestAction::assert(\"Number.isFinite(910)\"),\n        TestAction::assert(\"!Number.isFinite(null)\"),\n        TestAction::assert(\"!Number.isFinite('0')\"),\n        TestAction::assert(\"!Number.isFinite()\"),\n        TestAction::assert(\"!Number.isFinite({})\"),\n        TestAction::assert(\"Number.isFinite(Number(5))\"),\n        TestAction::assert(\"!Number.isFinite(new Number(5))\"),\n        TestAction::assert(\"!Number.isFinite(new Number(5))\"),\n        TestAction::assert(\"!Number.isFinite(BigInt(5))\"),\n    ]);\n}\n\n#[test]\nfn number_is_integer() {\n    run_test_actions([\n        TestAction::assert(\"Number.isInteger(0)\"),\n        TestAction::assert(\"Number.isInteger(1)\"),\n        TestAction::assert(\"Number.isInteger(-100000)\"),\n        TestAction::assert(\"Number.isInteger(99999999999999999999999)\"),\n        TestAction::assert(\"!Number.isInteger(0.1)\"),\n        TestAction::assert(\"!Number.isInteger(Math.PI)\"),\n        TestAction::assert(\"!Number.isInteger(NaN)\"),\n        TestAction::assert(\"!Number.isInteger(Infinity)\"),\n        TestAction::assert(\"!Number.isInteger(-Infinity)\"),\n        TestAction::assert(\"!Number.isInteger('10')\"),\n        TestAction::assert(\"!Number.isInteger(true)\"),\n        TestAction::assert(\"!Number.isInteger(false)\"),\n        TestAction::assert(\"!Number.isInteger([1])\"),\n        TestAction::assert(\"Number.isInteger(5.0)\"),\n        TestAction::assert(\"!Number.isInteger(5.000000000000001)\"),\n        TestAction::assert(\"Number.isInteger(5.0000000000000001)\"),\n        TestAction::assert(\"!Number.isInteger(Number(5.000000000000001))\"),\n        TestAction::assert(\"Number.isInteger(Number(5.0000000000000001))\"),\n        TestAction::assert(\"!Number.isInteger()\"),\n        TestAction::assert(\"!Number.isInteger(new Number(5))\"),\n    ]);\n}\n\n#[test]\nfn number_is_nan() {\n    run_test_actions([\n        TestAction::assert(\"Number.isNaN(NaN)\"),\n        TestAction::assert(\"Number.isNaN(Number.NaN)\"),\n        TestAction::assert(\"Number.isNaN(0 / 0)\"),\n        TestAction::assert(\"!Number.isNaN(undefined)\"),\n        TestAction::assert(\"!Number.isNaN({})\"),\n        TestAction::assert(\"!Number.isNaN(true)\"),\n        TestAction::assert(\"!Number.isNaN(null)\"),\n        TestAction::assert(\"!Number.isNaN(37)\"),\n        TestAction::assert(\"!Number.isNaN('37')\"),\n        TestAction::assert(\"!Number.isNaN('37.37')\"),\n        TestAction::assert(\"!Number.isNaN('37,5')\"),\n        TestAction::assert(\"!Number.isNaN('123ABC')\"),\n        // Incorrect due to ToNumber implementation inconsistencies.\n        //TestAction::assert(\"!Number.isNaN('')\"),\n        //TestAction::assert(\"!Number.isNaN(' ')\"),\n        TestAction::assert(\"!Number.isNaN('blabla')\"),\n        TestAction::assert(\"!Number.isNaN(Number(5))\"),\n        TestAction::assert(\"Number.isNaN(Number(NaN))\"),\n        TestAction::assert(\"!Number.isNaN(BigInt(5))\"),\n        TestAction::assert(\"!Number.isNaN(new Number(5))\"),\n        TestAction::assert(\"!Number.isNaN(new Number(NaN))\"),\n    ]);\n}\n\n#[test]\nfn number_is_safe_integer() {\n    run_test_actions([\n        TestAction::assert(\"Number.isSafeInteger(3)\"),\n        TestAction::assert(\"!Number.isSafeInteger(Math.pow(2, 53))\"),\n        TestAction::assert(\"Number.isSafeInteger(Math.pow(2, 53) - 1)\"),\n        TestAction::assert(\"!Number.isSafeInteger(NaN)\"),\n        TestAction::assert(\"!Number.isSafeInteger(Infinity)\"),\n        TestAction::assert(\"!Number.isSafeInteger('3')\"),\n        TestAction::assert(\"!Number.isSafeInteger(3.1)\"),\n        TestAction::assert(\"Number.isSafeInteger(3.0)\"),\n        TestAction::assert(\"!Number.isSafeInteger(new Number(5))\"),\n    ]);\n}\n\n// https://github.com/boa-dev/boa/issues/2717\n#[test]\nfn issue_2717() {\n    run_test_actions([\n        TestAction::assert_eq(\n            \"(0.1600057092765239).toString(36)\",\n            js_str!(\"0.5rd85dm1ixq\"),\n        ),\n        TestAction::assert_eq(\n            \"(0.23046743672210102).toString(36)\",\n            js_str!(\"0.8aoosla2phj\"),\n        ),\n    ]);\n}\n\n#[test]\nfn to_precision_edge_cases() {\n    run_test_actions([\n        TestAction::assert_eq(\"(NaN).toPrecision(3)\", js_str!(\"NaN\")),\n        TestAction::assert_eq(\"(Infinity).toPrecision(3)\", js_str!(\"Infinity\")),\n        TestAction::assert_eq(\"(-Infinity).toPrecision(3)\", js_str!(\"-Infinity\")),\n        TestAction::assert_eq(\"(-0).toPrecision(5)\", js_str!(\"0.0000\")),\n        // Carry-over rounding tests\n        TestAction::assert_eq(\"(9.95).toPrecision(2)\", js_str!(\"9.9\")),\n        TestAction::assert_eq(\"(99.95).toPrecision(3)\", js_str!(\"100\")),\n        TestAction::assert_eq(\"(999.95).toPrecision(4)\", js_str!(\"1000\")),\n        TestAction::assert_eq(\"(9.5).toPrecision(1)\", js_str!(\"1e+1\")),\n        TestAction::assert_eq(\"(123.456).toPrecision(5)\", js_str!(\"123.46\")),\n        TestAction::assert_eq(\"(0.00456).toPrecision(3)\", js_str!(\"0.00456\")),\n        TestAction::assert_eq(\"(0.0000001).toPrecision(2)\", js_str!(\"1.0e-7\")),\n        TestAction::assert_eq(\"(0.000000123).toPrecision(3)\", js_str!(\"1.23e-7\")),\n        TestAction::assert_eq(\"(123456789).toPrecision(3)\", js_str!(\"1.23e+8\")),\n        TestAction::assert_eq(\"(0.1 + 0.2).toPrecision(1)\", js_str!(\"0.3\")),\n        TestAction::assert_eq(\"(123).toPrecision(3)\", js_str!(\"123\")),\n        TestAction::assert_eq(\"(123).toPrecision(4)\", js_str!(\"123.0\")),\n        TestAction::assert_eq(\"(0).toPrecision(1)\", js_str!(\"0\")),\n        TestAction::assert_eq(\"(1).toPrecision(1)\", js_str!(\"1\")),\n        TestAction::assert_eq(\"(9).toPrecision(1)\", js_str!(\"9\")),\n    ]);\n}\n"
  },
  {
    "path": "core/engine/src/builtins/object/for_in_iterator.rs",
    "content": "//! This module implements the `ForInIterator` object.\n//!\n//! More information:\n//!  - [ECMAScript reference][spec]\n//!\n//! [spec]: https://tc39.es/ecma262/#sec-for-in-iterator-objects\n\nuse crate::{\n    Context, JsData, JsResult, JsString, JsValue, NativeFunction,\n    builtins::iterable::create_iter_result_object,\n    error::JsNativeError,\n    js_string,\n    object::{FunctionObjectBuilder, JsObject, internal_methods::InternalMethodPropertyContext},\n    property::PropertyKey,\n};\nuse boa_gc::{Finalize, Trace};\nuse rustc_hash::FxHashSet;\nuse std::collections::VecDeque;\n\n/// The `ForInIterator` object represents an iteration over some specific object.\n/// It implements the iterator protocol.\n///\n/// More information:\n///  - [ECMAScript reference][spec]\n///\n/// [spec]: https://tc39.es/ecma262/#sec-for-in-iterator-objects\n#[derive(Debug, Clone, Finalize, Trace, JsData)]\npub(crate) struct ForInIterator {\n    object: JsValue,\n    visited_keys: FxHashSet<JsString>,\n    remaining_keys: VecDeque<JsString>,\n    object_was_visited: bool,\n}\n\nimpl ForInIterator {\n    fn new(object: JsValue) -> Self {\n        Self {\n            object,\n            visited_keys: FxHashSet::default(),\n            remaining_keys: VecDeque::default(),\n            object_was_visited: false,\n        }\n    }\n\n    /// `CreateForInIterator( object )`\n    ///\n    /// Creates a new iterator over the given object.\n    ///\n    /// Returns the iterator object and its `next` method as a pair,\n    /// avoiding the need to look up `next` through the prototype chain.\n    ///\n    /// More information:\n    ///  - [ECMA reference][spec]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-createforiniterator\n    pub(crate) fn create_for_in_iterator(\n        object: JsValue,\n        context: &Context,\n    ) -> (JsObject, JsValue) {\n        let iterator = JsObject::from_proto_and_data_with_shared_shape(\n            context.root_shape(),\n            context\n                .intrinsics()\n                .objects()\n                .iterator_prototypes()\n                .iterator(),\n            Self::new(object),\n        )\n        .upcast();\n\n        let next_method =\n            FunctionObjectBuilder::new(context.realm(), NativeFunction::from_fn_ptr(Self::next))\n                .name(js_string!(\"next\"))\n                .length(0)\n                .build();\n\n        (iterator, next_method.into())\n    }\n\n    /// %ForInIteratorPrototype%.next( )\n    ///\n    /// Gets the next result in the object.\n    ///\n    /// More information:\n    ///  - [ECMA reference][spec]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-%foriniteratorprototype%.next\n    pub(crate) fn next(this: &JsValue, _: &[JsValue], context: &mut Context) -> JsResult<JsValue> {\n        let object = this.as_object();\n        let mut iterator = object\n            .as_ref()\n            .and_then(|o| o.downcast_mut::<Self>())\n            .ok_or_else(|| JsNativeError::typ().with_message(\"`this` is not a ForInIterator\"))?;\n        let mut object = iterator.object.to_object(context)?;\n        loop {\n            if !iterator.object_was_visited {\n                let keys = object\n                    .__own_property_keys__(&mut InternalMethodPropertyContext::new(context))?;\n                for k in keys {\n                    match k {\n                        PropertyKey::String(ref k) => {\n                            iterator.remaining_keys.push_back(k.clone());\n                        }\n                        PropertyKey::Index(i) => {\n                            iterator.remaining_keys.push_back(i.get().into());\n                        }\n                        PropertyKey::Symbol(_) => {}\n                    }\n                }\n                iterator.object_was_visited = true;\n            }\n            while let Some(r) = iterator.remaining_keys.pop_front() {\n                if !iterator.visited_keys.contains(&r)\n                    && let Some(desc) = object.__get_own_property__(\n                        &PropertyKey::from(r.clone()),\n                        &mut InternalMethodPropertyContext::new(context),\n                    )?\n                {\n                    iterator.visited_keys.insert(r.clone());\n                    if desc.expect_enumerable() {\n                        return Ok(create_iter_result_object(JsValue::new(r), false, context));\n                    }\n                }\n            }\n            let proto = object.prototype().clone();\n            match proto {\n                Some(o) => {\n                    object = o;\n                }\n                _ => {\n                    return Ok(create_iter_result_object(\n                        JsValue::undefined(),\n                        true,\n                        context,\n                    ));\n                }\n            }\n            iterator.object = JsValue::new(object.clone());\n            iterator.object_was_visited = false;\n        }\n    }\n}\n"
  },
  {
    "path": "core/engine/src/builtins/object/mod.rs",
    "content": "//! Boa's implementation of ECMAScript's global `Object` object.\n//!\n//! The `Object` class represents one of ECMAScript's data types.\n//!\n//! It is used to store various keyed collections and more complex entities.\n//! Objects can be created using the `Object()` constructor or the\n//! object initializer / literal syntax.\n//!\n//! More information:\n//!  - [ECMAScript reference][spec]\n//!  - [MDN documentation][mdn]\n//!\n//! [spec]: https://tc39.es/ecma262/#sec-objects\n//! [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object\n\nuse super::{\n    Array, BuiltInBuilder, BuiltInConstructor, Date, IntrinsicObject, RegExp, error::Error,\n};\nuse crate::builtins::function::arguments::{MappedArguments, UnmappedArguments};\nuse crate::value::JsVariant;\nuse crate::{\n    Context, JsArgs, JsData, JsExpect, JsResult, JsString,\n    builtins::{BuiltInObject, iterable::IteratorHint, map},\n    context::intrinsics::{Intrinsics, StandardConstructor, StandardConstructors},\n    js_error, js_string,\n    native_function::NativeFunction,\n    object::{\n        FunctionObjectBuilder, IntegrityLevel, JsObject,\n        internal_methods::{InternalMethodPropertyContext, get_prototype_from_constructor},\n    },\n    property::{Attribute, PropertyDescriptor, PropertyKey, PropertyNameKind},\n    realm::Realm,\n    string::StaticJsStrings,\n    symbol::JsSymbol,\n    value::JsValue,\n};\nuse boa_gc::{Finalize, Trace};\nuse boa_macros::js_str;\nuse tap::{Conv, Pipe};\n\npub(crate) mod for_in_iterator;\n#[cfg(test)]\nmod tests;\n\n/// An ordinary Javascript `Object`.\n#[derive(Debug, Default, Clone, Copy, Trace, Finalize, JsData)]\n#[boa_gc(empty_trace)]\npub struct OrdinaryObject;\n\nimpl IntrinsicObject for OrdinaryObject {\n    fn init(realm: &Realm) {\n        let legacy_proto_getter = BuiltInBuilder::callable(realm, Self::legacy_proto_getter)\n            .name(js_string!(\"get __proto__\"))\n            .build();\n\n        let legacy_setter_proto = BuiltInBuilder::callable(realm, Self::legacy_proto_setter)\n            .name(js_string!(\"set __proto__\"))\n            .length(1)\n            .build();\n\n        BuiltInBuilder::from_standard_constructor::<Self>(realm)\n            .inherits(None)\n            .accessor(\n                js_string!(\"__proto__\"),\n                Some(legacy_proto_getter),\n                Some(legacy_setter_proto),\n                Attribute::NON_ENUMERABLE | Attribute::CONFIGURABLE,\n            )\n            .method(Self::has_own_property, js_string!(\"hasOwnProperty\"), 1)\n            .method(\n                Self::property_is_enumerable,\n                js_string!(\"propertyIsEnumerable\"),\n                1,\n            )\n            .method(Self::to_string, js_string!(\"toString\"), 0)\n            .method(Self::to_locale_string, js_string!(\"toLocaleString\"), 0)\n            .method(Self::value_of, js_string!(\"valueOf\"), 0)\n            .method(Self::is_prototype_of, js_string!(\"isPrototypeOf\"), 1)\n            .method(\n                Self::legacy_define_getter,\n                js_string!(\"__defineGetter__\"),\n                2,\n            )\n            .method(\n                Self::legacy_define_setter,\n                js_string!(\"__defineSetter__\"),\n                2,\n            )\n            .method(\n                Self::legacy_lookup_getter,\n                js_string!(\"__lookupGetter__\"),\n                1,\n            )\n            .method(\n                Self::legacy_lookup_setter,\n                js_string!(\"__lookupSetter__\"),\n                1,\n            )\n            .static_method(Self::create, js_string!(\"create\"), 2)\n            .static_method(Self::set_prototype_of, js_string!(\"setPrototypeOf\"), 2)\n            .static_method(Self::get_prototype_of, js_string!(\"getPrototypeOf\"), 1)\n            .static_method(Self::define_property, js_string!(\"defineProperty\"), 3)\n            .static_method(Self::define_properties, js_string!(\"defineProperties\"), 2)\n            .static_method(Self::assign, js_string!(\"assign\"), 2)\n            .static_method(Self::is, js_string!(\"is\"), 2)\n            .static_method(Self::keys, js_string!(\"keys\"), 1)\n            .static_method(Self::values, js_string!(\"values\"), 1)\n            .static_method(Self::entries, js_string!(\"entries\"), 1)\n            .static_method(Self::seal, js_string!(\"seal\"), 1)\n            .static_method(Self::is_sealed, js_string!(\"isSealed\"), 1)\n            .static_method(Self::freeze, js_string!(\"freeze\"), 1)\n            .static_method(Self::is_frozen, js_string!(\"isFrozen\"), 1)\n            .static_method(Self::prevent_extensions, js_string!(\"preventExtensions\"), 1)\n            .static_method(Self::is_extensible, js_string!(\"isExtensible\"), 1)\n            .static_method(\n                Self::get_own_property_descriptor,\n                js_string!(\"getOwnPropertyDescriptor\"),\n                2,\n            )\n            .static_method(\n                Self::get_own_property_descriptors,\n                js_string!(\"getOwnPropertyDescriptors\"),\n                1,\n            )\n            .static_method(\n                Self::get_own_property_names,\n                js_string!(\"getOwnPropertyNames\"),\n                1,\n            )\n            .static_method(\n                Self::get_own_property_symbols,\n                js_string!(\"getOwnPropertySymbols\"),\n                1,\n            )\n            .static_method(Self::has_own, js_string!(\"hasOwn\"), 2)\n            .static_method(Self::from_entries, js_string!(\"fromEntries\"), 1)\n            .static_method(Self::group_by, js_string!(\"groupBy\"), 2)\n            .build();\n    }\n\n    fn get(intrinsics: &Intrinsics) -> JsObject {\n        Self::STANDARD_CONSTRUCTOR(intrinsics.constructors()).constructor()\n    }\n}\n\nimpl BuiltInObject for OrdinaryObject {\n    const NAME: JsString = StaticJsStrings::OBJECT;\n}\n\nimpl BuiltInConstructor for OrdinaryObject {\n    const CONSTRUCTOR_ARGUMENTS: usize = 1;\n    const PROTOTYPE_STORAGE_SLOTS: usize = 12;\n    const CONSTRUCTOR_STORAGE_SLOTS: usize = 23;\n\n    const STANDARD_CONSTRUCTOR: fn(&StandardConstructors) -> &StandardConstructor =\n        StandardConstructors::object;\n\n    fn constructor(\n        new_target: &JsValue,\n        args: &[JsValue],\n        context: &mut Context,\n    ) -> JsResult<JsValue> {\n        // 1. If NewTarget is neither undefined nor the active function object, then\n        if !new_target.is_undefined()\n            && new_target\n                != &context\n                    .active_function_object()\n                    .unwrap_or_else(|| context.intrinsics().constructors().object().constructor())\n                    .into()\n        {\n            //     a. Return ? OrdinaryCreateFromConstructor(NewTarget, \"%Object.prototype%\").\n            let prototype =\n                get_prototype_from_constructor(new_target, StandardConstructors::object, context)?;\n            let object = JsObject::from_proto_and_data_with_shared_shape(\n                context.root_shape(),\n                prototype,\n                OrdinaryObject,\n            );\n            return Ok(object.into());\n        }\n\n        let value = args.get_or_undefined(0);\n\n        // 2. If value is undefined or null, return OrdinaryObjectCreate(%Object.prototype%).\n        if value.is_null_or_undefined() {\n            Ok(JsObject::with_object_proto(context.intrinsics()).into())\n        } else {\n            // 3. Return ! ToObject(value).\n            value.to_object(context).map(JsValue::from)\n        }\n    }\n}\n\nimpl OrdinaryObject {\n    /// `get Object.prototype.__proto__`\n    ///\n    /// The `__proto__` getter function exposes the value of the\n    /// internal `[[Prototype]]` of an object.\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///  - [MDN documentation][mdn]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-get-object.prototype.__proto__\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/proto\n    pub fn legacy_proto_getter(\n        this: &JsValue,\n        _: &[JsValue],\n        context: &mut Context,\n    ) -> JsResult<JsValue> {\n        // 1. Let O be ? ToObject(this value).\n        let obj = this.to_object(context)?;\n\n        // 2. Return ? O.[[GetPrototypeOf]]().\n        let proto = obj.__get_prototype_of__(&mut InternalMethodPropertyContext::new(context))?;\n\n        Ok(proto.map_or(JsValue::null(), JsValue::new))\n    }\n\n    /// `set Object.prototype.__proto__`\n    ///\n    /// The `__proto__` setter allows the `[[Prototype]]` of\n    /// an object to be mutated.\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///  - [MDN documentation][mdn]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-set-object.prototype.__proto__\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/proto\n    pub fn legacy_proto_setter(\n        this: &JsValue,\n        args: &[JsValue],\n        context: &mut Context,\n    ) -> JsResult<JsValue> {\n        // 1. Let O be ? RequireObjectCoercible(this value).\n        let this = this.require_object_coercible()?;\n\n        // 2. If Type(proto) is neither Object nor Null, return undefined.\n        let proto = match args.get_or_undefined(0).variant() {\n            JsVariant::Object(proto) => Some(proto.clone()),\n            JsVariant::Null => None,\n            _ => return Ok(JsValue::undefined()),\n        };\n\n        // 3. If Type(O) is not Object, return undefined.\n        let JsVariant::Object(object) = this.variant() else {\n            return Ok(JsValue::undefined());\n        };\n\n        // 4. Let status be ? O.[[SetPrototypeOf]](proto).\n        let status =\n            object.__set_prototype_of__(proto, &mut InternalMethodPropertyContext::new(context))?;\n\n        // 5. If status is false, throw a TypeError exception.\n        if !status {\n            return Err(js_error!(TypeError: \"__proto__ called on null or undefined\"));\n        }\n\n        // 6. Return undefined.\n        Ok(JsValue::undefined())\n    }\n\n    /// `Object.prototype.__defineGetter__(prop, func)`\n    ///\n    /// Binds an object's property to a function to be called when that property is looked up.\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///  - [MDN documentation][mdn]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-object.prototype.__defineGetter__\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/__defineGetter__\n    pub fn legacy_define_getter(\n        this: &JsValue,\n        args: &[JsValue],\n        context: &mut Context,\n    ) -> JsResult<JsValue> {\n        let getter = args.get_or_undefined(1);\n\n        // 1. Let O be ? ToObject(this value).\n        let obj = this.to_object(context)?;\n\n        // 2. If IsCallable(getter) is false, throw a TypeError exception.\n        if !getter.is_callable() {\n            return Err(js_error!(TypeError:\n                \"Object.prototype.__defineGetter__: expected 'getter' to be a Function object\",\n            ));\n        }\n\n        // 3. Let desc be PropertyDescriptor { [[Get]]: getter, [[Enumerable]]: true, [[Configurable]]: true }.\n        let desc = PropertyDescriptor::builder()\n            .get(getter.clone())\n            .enumerable(true)\n            .configurable(true);\n\n        // 4. Let key be ? ToPropertyKey(P).\n        let key = args.get_or_undefined(0).to_property_key(context)?;\n\n        // 5. Perform ? DefinePropertyOrThrow(O, key, desc).\n        obj.define_property_or_throw(key, desc, context)?;\n\n        // 6. Return undefined.\n        Ok(JsValue::undefined())\n    }\n\n    /// `Object.prototype.__defineSetter__(prop, func)`\n    ///\n    /// Binds an object's property to a function to be called when an attempt is made to set that property.\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///  - [MDN documentation][mdn]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-object.prototype.__defineSetter__\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/__defineSetter__\n    pub fn legacy_define_setter(\n        this: &JsValue,\n        args: &[JsValue],\n        context: &mut Context,\n    ) -> JsResult<JsValue> {\n        let setter = args.get_or_undefined(1);\n\n        // 1. Let O be ? ToObject(this value).\n        let obj = this.to_object(context)?;\n\n        // 2. If IsCallable(setter) is false, throw a TypeError exception.\n        if !setter.is_callable() {\n            return Err(js_error!(TypeError:\n                \"Object.prototype.__defineSetter__: expected 'setter' to be a Function object\",\n            ));\n        }\n\n        // 3. Let desc be PropertyDescriptor { [[Set]]: setter, [[Enumerable]]: true, [[Configurable]]: true }.\n        let desc = PropertyDescriptor::builder()\n            .set(setter.clone())\n            .enumerable(true)\n            .configurable(true);\n\n        // 4. Let key be ? ToPropertyKey(P).\n        let key = args.get_or_undefined(0).to_property_key(context)?;\n\n        // 5. Perform ? DefinePropertyOrThrow(O, key, desc).\n        obj.define_property_or_throw(key, desc, context)?;\n\n        // 6. Return undefined.\n        Ok(JsValue::undefined())\n    }\n\n    /// `Object.prototype.__lookupGetter__(prop)`\n    ///\n    /// Returns the function bound as a getter to the specified property.\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///  - [MDN documentation][mdn]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-object.prototype.__lookupGetter__\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/__lookupGetter__\n    pub fn legacy_lookup_getter(\n        this: &JsValue,\n        args: &[JsValue],\n        context: &mut Context,\n    ) -> JsResult<JsValue> {\n        // 1. Let O be ? ToObject(this value).\n        let mut obj = this.to_object(context)?;\n\n        // 2. Let key be ? ToPropertyKey(P).\n        let key = args.get_or_undefined(0).to_property_key(context)?;\n\n        // 3. Repeat\n        loop {\n            // a. Let desc be ? O.[[GetOwnProperty]](key).\n\n            let desc =\n                obj.__get_own_property__(&key, &mut InternalMethodPropertyContext::new(context))?;\n\n            // b. If desc is not undefined, then\n            if let Some(current_desc) = desc {\n                // i. If IsAccessorDescriptor(desc) is true, return desc.[[Get]].\n                return if current_desc.is_accessor_descriptor() {\n                    Ok(current_desc.expect_get().clone())\n                } else {\n                    // ii. Return undefined.\n                    Ok(JsValue::undefined())\n                };\n            }\n            match obj.__get_prototype_of__(&mut InternalMethodPropertyContext::new(context))? {\n                // c. Set O to ? O.[[GetPrototypeOf]]().\n                Some(o) => obj = o,\n                // d. If O is null, return undefined.\n                None => return Ok(JsValue::undefined()),\n            }\n        }\n    }\n    /// `Object.prototype.__lookupSetter__(prop)`\n    ///\n    /// Returns the function bound as a getter to the specified property.\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///  - [MDN documentation][mdn]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-object.prototype.__lookupSetter__\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/__lookupSetter__\n    pub fn legacy_lookup_setter(\n        this: &JsValue,\n        args: &[JsValue],\n        context: &mut Context,\n    ) -> JsResult<JsValue> {\n        // 1. Let O be ? ToObject(this value).\n        let mut obj = this.to_object(context)?;\n\n        // 2. Let key be ? ToPropertyKey(P).\n        let key = args.get_or_undefined(0).to_property_key(context)?;\n\n        // 3. Repeat\n        loop {\n            // a. Let desc be ? O.[[GetOwnProperty]](key).\n\n            let desc =\n                obj.__get_own_property__(&key, &mut InternalMethodPropertyContext::new(context))?;\n\n            // b. If desc is not undefined, then\n            if let Some(current_desc) = desc {\n                // i. If IsAccessorDescriptor(desc) is true, return desc.[[Set]].\n                return if current_desc.is_accessor_descriptor() {\n                    Ok(current_desc.expect_set().clone())\n                } else {\n                    // ii. Return undefined.\n                    Ok(JsValue::undefined())\n                };\n            }\n            match obj.__get_prototype_of__(&mut InternalMethodPropertyContext::new(context))? {\n                // c. Set O to ? O.[[GetPrototypeOf]]().\n                Some(o) => obj = o,\n                // d. If O is null, return undefined.\n                None => return Ok(JsValue::undefined()),\n            }\n        }\n    }\n\n    /// `Object.create( proto, [propertiesObject] )`\n    ///\n    /// Creates a new object from the provided prototype.\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///  - [MDN documentation][mdn]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-object.create\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/create\n    pub fn create(_: &JsValue, args: &[JsValue], context: &mut Context) -> JsResult<JsValue> {\n        let prototype = args.get_or_undefined(0);\n        let properties = args.get_or_undefined(1);\n\n        let obj = match prototype.variant() {\n            JsVariant::Object(_) | JsVariant::Null => {\n                JsObject::from_proto_and_data_with_shared_shape(\n                    context.root_shape(),\n                    prototype.as_object(),\n                    OrdinaryObject,\n                )\n                .upcast()\n            }\n            _ => {\n                return Err(js_error!(TypeError:\n                    \"Object.create: expected 'proto' to be an Object or null, got `{}`\",\n                    prototype.type_of()\n                ));\n            }\n        };\n\n        if !properties.is_undefined() {\n            object_define_properties(&obj, properties, context)?;\n            return Ok(obj.into());\n        }\n\n        Ok(obj.into())\n    }\n\n    /// `Object.getOwnPropertyDescriptor( object, property )`\n    ///\n    /// Returns an object describing the configuration of a specific property on a given object.\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///  - [MDN documentation][mdn]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-object.getownpropertydescriptor\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/getOwnPropertyDescriptor\n    pub fn get_own_property_descriptor(\n        _: &JsValue,\n        args: &[JsValue],\n        context: &mut Context,\n    ) -> JsResult<JsValue> {\n        // 1. Let obj be ? ToObject(O).\n        let obj = args.get_or_undefined(0).to_object(context)?;\n\n        // 2. Let key be ? ToPropertyKey(P).\n        let key = args.get_or_undefined(1).to_property_key(context)?;\n\n        // 3. Let desc be ? obj.[[GetOwnProperty]](key).\n\n        let desc =\n            obj.__get_own_property__(&key, &mut InternalMethodPropertyContext::new(context))?;\n\n        // 4. Return FromPropertyDescriptor(desc).\n        Self::from_property_descriptor(desc, context)\n    }\n\n    /// `Object.getOwnPropertyDescriptors( object )`\n    ///\n    /// Returns all own property descriptors of a given object.\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///  - [MDN documentation][mdn]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-object.getownpropertydescriptors\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/getOwnPropertyDescriptors\n    pub fn get_own_property_descriptors(\n        _: &JsValue,\n        args: &[JsValue],\n        context: &mut Context,\n    ) -> JsResult<JsValue> {\n        // 1. Let obj be ? ToObject(O).\n        let obj = args.get_or_undefined(0).to_object(context)?;\n\n        // 2. Let ownKeys be ? obj.[[OwnPropertyKeys]]().\n        let own_keys =\n            obj.__own_property_keys__(&mut InternalMethodPropertyContext::new(context))?;\n\n        // 3. Let descriptors be OrdinaryObjectCreate(%Object.prototype%).\n        let descriptors = JsObject::with_object_proto(context.intrinsics());\n\n        // 4. For each element key of ownKeys, do\n        for key in own_keys {\n            // a. Let desc be ? obj.[[GetOwnProperty]](key).\n\n            let desc =\n                obj.__get_own_property__(&key, &mut InternalMethodPropertyContext::new(context))?;\n\n            // b. Let descriptor be FromPropertyDescriptor(desc).\n            let descriptor = Self::from_property_descriptor(desc, context)?;\n\n            // c. If descriptor is not undefined,\n            //    perform ! CreateDataPropertyOrThrow(descriptors, key, descriptor).\n            if !descriptor.is_undefined() {\n                descriptors\n                    .create_data_property_or_throw(key, descriptor, context)\n                    .js_expect(\"should not fail according to spec\")?;\n            }\n        }\n\n        // 5. Return descriptors.\n        Ok(descriptors.into())\n    }\n\n    /// The abstract operation `FromPropertyDescriptor`.\n    ///\n    /// [ECMAScript reference][spec]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-frompropertydescriptor\n    pub(crate) fn from_property_descriptor(\n        desc: Option<PropertyDescriptor>,\n        context: &mut Context,\n    ) -> JsResult<JsValue> {\n        // 1. If Desc is undefined, return undefined.\n        let Some(desc) = desc else {\n            return Ok(JsValue::undefined());\n        };\n\n        // 2. Let obj be ! OrdinaryObjectCreate(%Object.prototype%).\n        // 3. Assert: obj is an extensible ordinary object with no own properties.\n        let obj = JsObject::with_object_proto(context.intrinsics());\n\n        // 4. If Desc has a [[Value]] field, then\n        if let Some(value) = desc.value() {\n            // a. Perform ! CreateDataPropertyOrThrow(obj, \"value\", Desc.[[Value]]).\n            obj.create_data_property_or_throw(js_string!(\"value\"), value.clone(), context)\n                .js_expect(\"CreateDataPropertyOrThrow cannot fail here\")?;\n        }\n\n        // 5. If Desc has a [[Writable]] field, then\n        if let Some(writable) = desc.writable() {\n            // a. Perform ! CreateDataPropertyOrThrow(obj, \"writable\", Desc.[[Writable]]).\n            obj.create_data_property_or_throw(js_string!(\"writable\"), writable, context)\n                .js_expect(\"CreateDataPropertyOrThrow cannot fail here\")?;\n        }\n\n        // 6. If Desc has a [[Get]] field, then\n        if let Some(get) = desc.get() {\n            // a. Perform ! CreateDataPropertyOrThrow(obj, \"get\", Desc.[[Get]]).\n            obj.create_data_property_or_throw(js_string!(\"get\"), get.clone(), context)\n                .js_expect(\"CreateDataPropertyOrThrow cannot fail here\")?;\n        }\n\n        // 7. If Desc has a [[Set]] field, then\n        if let Some(set) = desc.set() {\n            // a. Perform ! CreateDataPropertyOrThrow(obj, \"set\", Desc.[[Set]]).\n            obj.create_data_property_or_throw(js_string!(\"set\"), set.clone(), context)\n                .js_expect(\"CreateDataPropertyOrThrow cannot fail here\")?;\n        }\n\n        // 8. If Desc has an [[Enumerable]] field, then\n        if let Some(enumerable) = desc.enumerable() {\n            // a. Perform ! CreateDataPropertyOrThrow(obj, \"enumerable\", Desc.[[Enumerable]]).\n            obj.create_data_property_or_throw(js_string!(\"enumerable\"), enumerable, context)\n                .js_expect(\"CreateDataPropertyOrThrow cannot fail here\")?;\n        }\n\n        // 9. If Desc has a [[Configurable]] field, then\n        if let Some(configurable) = desc.configurable() {\n            // a. Perform ! CreateDataPropertyOrThrow(obj, \"configurable\", Desc.[[Configurable]]).\n            obj.create_data_property_or_throw(js_string!(\"configurable\"), configurable, context)\n                .js_expect(\"CreateDataPropertyOrThrow cannot fail here\")?;\n        }\n\n        // 10. Return obj.\n        Ok(obj.into())\n    }\n\n    /// Uses the `SameValue` algorithm to check equality of objects\n    pub fn is(_: &JsValue, args: &[JsValue], _: &mut Context) -> JsResult<JsValue> {\n        let x = args.get_or_undefined(0);\n        let y = args.get_or_undefined(1);\n\n        Ok(JsValue::same_value(x, y).into())\n    }\n\n    /// Get the `prototype` of an object.\n    ///\n    /// [More information][spec]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-object.setprototypeof\n    pub fn get_prototype_of(\n        _: &JsValue,\n        args: &[JsValue],\n        context: &mut Context,\n    ) -> JsResult<JsValue> {\n        if args.is_empty() {\n            return Err(js_error!(TypeError:\n                \"Object.getPrototypeOf: At least 1 argument required, but only 0 passed\",\n            ));\n        }\n\n        // 1. Let obj be ? ToObject(O).\n        let obj = args[0].clone().to_object(context)?;\n\n        // 2. Return ? obj.[[GetPrototypeOf]]().\n        Ok(obj\n            .__get_prototype_of__(&mut InternalMethodPropertyContext::new(context))?\n            .map_or(JsValue::null(), JsValue::new))\n    }\n\n    /// Set the `prototype` of an object.\n    ///\n    /// [More information][spec]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-object.setprototypeof\n    pub fn set_prototype_of(\n        _: &JsValue,\n        args: &[JsValue],\n        context: &mut Context,\n    ) -> JsResult<JsValue> {\n        if args.len() < 2 {\n            return Err(js_error!(TypeError:\n                \"Object.setPrototypeOf: At least 2 arguments required, but only {} passed\",\n                args.len()\n            ));\n        }\n\n        // 1. Set O to ? RequireObjectCoercible(O).\n        let o = args\n            .first()\n            .cloned()\n            .unwrap_or_default()\n            .require_object_coercible()?\n            .clone();\n\n        let proto = match args.get_or_undefined(1).variant() {\n            JsVariant::Object(obj) => Some(obj.clone()),\n            JsVariant::Null => None,\n            // 2. If Type(proto) is neither Object nor Null, throw a TypeError exception.\n            val => {\n                return Err(js_error!(TypeError:\n                    \"Object.setPrototypeOf: expected 'proto' to be an Object or null, got `{}`\",\n                    val.type_of()\n                ));\n            }\n        };\n\n        let Some(obj) = o.as_object() else {\n            // 3. If Type(O) is not Object, return O.\n            return Ok(o);\n        };\n\n        // 4. Let status be ? O.[[SetPrototypeOf]](proto).\n        let status =\n            obj.__set_prototype_of__(proto, &mut InternalMethodPropertyContext::new(context))?;\n\n        if !status {\n            return Err(js_error!(TypeError: \"can't set prototype of this object\"));\n        }\n\n        // 6. Return O.\n        Ok(o)\n    }\n\n    /// `Object.prototype.isPrototypeOf( proto )`\n    ///\n    /// Check whether or not an object exists within another object's prototype chain.\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///  - [MDN documentation][mdn]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-object.prototype.isprototypeof\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/isPrototypeOf\n    pub fn is_prototype_of(\n        this: &JsValue,\n        args: &[JsValue],\n        context: &mut Context,\n    ) -> JsResult<JsValue> {\n        let v = args.get_or_undefined(0);\n        if !v.is_object() {\n            return Ok(JsValue::new(false));\n        }\n        let mut v = v.clone();\n        let o = JsValue::new(this.to_object(context)?);\n        loop {\n            v = Self::get_prototype_of(this, &[v], context)?;\n            if v.is_null() {\n                return Ok(JsValue::new(false));\n            }\n            if JsValue::same_value(&o, &v) {\n                return Ok(JsValue::new(true));\n            }\n        }\n    }\n\n    /// Define a property in an object\n    pub fn define_property(\n        _: &JsValue,\n        args: &[JsValue],\n        context: &mut Context,\n    ) -> JsResult<JsValue> {\n        if let Some(object) = args.get_or_undefined(0).as_object() {\n            let key = args\n                .get(1)\n                .unwrap_or(&JsValue::undefined())\n                .to_property_key(context)?;\n            let desc = args\n                .get(2)\n                .unwrap_or(&JsValue::undefined())\n                .to_property_descriptor(context)?;\n\n            object.define_property_or_throw(key, desc, context)?;\n\n            Ok(object.clone().into())\n        } else {\n            Err(js_error!(TypeError: \"Object.defineProperty: expected 'this' to be an Object\"))\n        }\n    }\n\n    /// `Object.defineProperties( proto, [propertiesObject] )`\n    ///\n    /// Creates or update own properties to the object\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///  - [MDN documentation][mdn]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-object.defineproperties\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/defineProperties\n    pub fn define_properties(\n        _: &JsValue,\n        args: &[JsValue],\n        context: &mut Context,\n    ) -> JsResult<JsValue> {\n        let arg = args.get_or_undefined(0);\n        if let Some(obj) = arg.as_object() {\n            let props = args.get_or_undefined(1);\n            object_define_properties(&obj, props, context)?;\n            Ok(arg.clone())\n        } else {\n            Err(js_error!(TypeError: \"Object.defineProperties: expected 'this' to be an Object\"))\n        }\n    }\n\n    /// `Object.prototype.valueOf()`\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///  - [MDN documentation][mdn]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-object.prototype.valueof\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/valueOf\n    pub fn value_of(this: &JsValue, _: &[JsValue], context: &mut Context) -> JsResult<JsValue> {\n        // 1. Return ? ToObject(this value).\n        Ok(this.to_object(context)?.into())\n    }\n\n    /// `Object.prototype.toString()`\n    ///\n    /// This method returns a string representing the object.\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///  - [MDN documentation][mdn]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-object.prototype.tostring\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/toString\n    #[allow(clippy::wrong_self_convention)]\n    pub fn to_string(this: &JsValue, _: &[JsValue], context: &mut Context) -> JsResult<JsValue> {\n        // 1. If the this value is undefined, return \"[object Undefined]\".\n        if this.is_undefined() {\n            return Ok(js_string!(\"[object Undefined]\").into());\n        }\n        // 2. If the this value is null, return \"[object Null]\".\n        if this.is_null() {\n            return Ok(js_string!(\"[object Null]\").into());\n        }\n        // 3. Let O be ! ToObject(this value).\n        let o = this\n            .to_object(context)\n            .js_expect(\"toObject cannot fail here\")?;\n\n        //  4. Let isArray be ? IsArray(O).\n        //  5. If isArray is true, let builtinTag be \"Array\".\n        let builtin_tag = if o.is_array_abstract()? {\n            js_str!(\"Array\")\n        } else if o.is::<UnmappedArguments>() || o.is::<MappedArguments>() {\n            // 6. Else if O has a [[ParameterMap]] internal slot, let builtinTag be \"Arguments\".\n            js_str!(\"Arguments\")\n        } else if o.is_callable() {\n            // 7. Else if O has a [[Call]] internal method, let builtinTag be \"Function\".\n            js_str!(\"Function\")\n        } else if o.is::<Error>() {\n            // 8. Else if O has an [[ErrorData]] internal slot, let builtinTag be \"Error\".\n            js_str!(\"Error\")\n        } else if o.is::<bool>() {\n            // 9. Else if O has a [[BooleanData]] internal slot, let builtinTag be \"Boolean\".\n            js_str!(\"Boolean\")\n        } else if o.is::<f64>() {\n            // 10. Else if O has a [[NumberData]] internal slot, let builtinTag be \"Number\".\n            js_str!(\"Number\")\n        } else if o.is::<JsString>() {\n            // 11. Else if O has a [[StringData]] internal slot, let builtinTag be \"String\".\n            js_str!(\"String\")\n        } else if o.is::<Date>() {\n            // 12. Else if O has a [[DateValue]] internal slot, let builtinTag be \"Date\".\n            js_str!(\"Date\")\n        } else if o.is::<RegExp>() {\n            // 13. Else if O has a [[RegExpMatcher]] internal slot, let builtinTag be \"RegExp\".\n            js_str!(\"RegExp\")\n        } else {\n            // 14. Else, let builtinTag be \"Object\".\n            js_str!(\"Object\")\n        };\n\n        // 15. Let tag be ? Get(O, @@toStringTag).\n        let tag = o.get(JsSymbol::to_string_tag(), context)?;\n\n        // 16. If Type(tag) is not String, set tag to builtinTag.\n        let tag = tag.as_string();\n        let tag = tag.as_ref().map_or(builtin_tag, JsString::as_str);\n\n        // 17. Return the string-concatenation of \"[object \", tag, and \"]\".\n        Ok(js_string!(js_str!(\"[object \"), tag, js_str!(\"]\")).into())\n    }\n\n    /// `Object.prototype.toLocaleString( [ reserved1 [ , reserved2 ] ] )`\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///  - [MDN documentation][mdn]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-object.prototype.tolocalestring\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/toLocaleString\n    #[allow(clippy::wrong_self_convention)]\n    pub fn to_locale_string(\n        this: &JsValue,\n        _: &[JsValue],\n        context: &mut Context,\n    ) -> JsResult<JsValue> {\n        // 1. Let O be the this value.\n        // 2. Return ? Invoke(O, \"toString\").\n        this.invoke(js_string!(\"toString\"), &[], context)\n    }\n\n    /// `Object.prototype.hasOwnProperty( property )`\n    ///\n    /// The method returns a boolean indicating whether the object has the specified property\n    /// as its own property (as opposed to inheriting it).\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///  - [MDN documentation][mdn]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-object.prototype.hasownproperty\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/hasOwnProperty\n    pub fn has_own_property(\n        this: &JsValue,\n        args: &[JsValue],\n        context: &mut Context,\n    ) -> JsResult<JsValue> {\n        // 1. Let P be ? ToPropertyKey(V).\n        let key = args.get_or_undefined(0).to_property_key(context)?;\n\n        // 2. Let O be ? ToObject(this value).\n        let object = this.to_object(context)?;\n\n        // 3. Return ? HasOwnProperty(O, P).\n        Ok(object.has_own_property(key, context)?.into())\n    }\n\n    /// `Object.prototype.propertyIsEnumerable( property )`\n    ///\n    /// This method returns a Boolean indicating whether the specified property is\n    /// enumerable and is the object's own property.\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///  - [MDN documentation][mdn]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-object.prototype.propertyisenumerable\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/propertyIsEnumerable\n    pub fn property_is_enumerable(\n        this: &JsValue,\n        args: &[JsValue],\n        context: &mut Context,\n    ) -> JsResult<JsValue> {\n        let Some(key) = args.first() else {\n            return Ok(JsValue::new(false));\n        };\n\n        let key = key.to_property_key(context)?;\n\n        let own_prop = this\n            .to_object(context)?\n            .__get_own_property__(&key, &mut InternalMethodPropertyContext::new(context))?;\n\n        own_prop\n            .as_ref()\n            .and_then(PropertyDescriptor::enumerable)\n            .unwrap_or_default()\n            .conv::<JsValue>()\n            .pipe(Ok)\n    }\n\n    /// `Object.assign( target, ...sources )`\n    ///\n    /// This method copies all enumerable own properties from one or more\n    /// source objects to a target object. It returns the target object.\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///  - [MDN documentation][mdn]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-object.assign\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/assign\n    pub fn assign(_: &JsValue, args: &[JsValue], context: &mut Context) -> JsResult<JsValue> {\n        // 1. Let to be ? ToObject(target).\n        let to = args.get_or_undefined(0).to_object(context)?;\n\n        // 2. If only one argument was passed, return to.\n        if args.len() == 1 {\n            return Ok(to.into());\n        }\n\n        // 3. For each element nextSource of sources, do\n        for source in &args[1..] {\n            // 3.a. If nextSource is neither undefined nor null, then\n            if !source.is_null_or_undefined() {\n                // 3.a.i. Let from be ! ToObject(nextSource).\n                let from = source\n                    .to_object(context)\n                    .js_expect(\"this ToObject call must not fail\")?;\n                // 3.a.ii. Let keys be ? from.[[OwnPropertyKeys]]().\n                let keys =\n                    from.__own_property_keys__(&mut InternalMethodPropertyContext::new(context))?;\n                // 3.a.iii. For each element nextKey of keys, do\n                for key in keys {\n                    // 3.a.iii.1. Let desc be ? from.[[GetOwnProperty]](nextKey).\n\n                    if let Some(desc) = from.__get_own_property__(\n                        &key,\n                        &mut InternalMethodPropertyContext::new(context),\n                    )? {\n                        // 3.a.iii.2. If desc is not undefined and desc.[[Enumerable]] is true, then\n                        if desc.expect_enumerable() {\n                            // 3.a.iii.2.a. Let propValue be ? Get(from, nextKey).\n                            let property = from.get(key.clone(), context)?;\n                            // 3.a.iii.2.b. Perform ? Set(to, nextKey, propValue, true).\n                            to.set(key, property, true, context)?;\n                        }\n                    }\n                }\n            }\n        }\n\n        // 4. Return to.\n        Ok(to.into())\n    }\n\n    /// `Object.keys( target )`\n    ///\n    /// This method returns an array of a given object's own enumerable\n    /// property names, iterated in the same order that a normal loop would.\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///  - [MDN documentation][mdn]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-object.keys\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/keys\n    pub fn keys(_: &JsValue, args: &[JsValue], context: &mut Context) -> JsResult<JsValue> {\n        // 1. Let obj be ? ToObject(target).\n        let obj = args\n            .first()\n            .cloned()\n            .unwrap_or_default()\n            .to_object(context)?;\n\n        // 2. Let nameList be ? EnumerableOwnPropertyNames(obj, key).\n        let name_list = obj.enumerable_own_property_names(PropertyNameKind::Key, context)?;\n\n        // 3. Return CreateArrayFromList(nameList).\n        let result = Array::create_array_from_list(name_list, context);\n\n        Ok(result.into())\n    }\n\n    /// `Object.values( target )`\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///  - [MDN documentation][mdn]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-object.values\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/values\n    pub fn values(_: &JsValue, args: &[JsValue], context: &mut Context) -> JsResult<JsValue> {\n        // 1. Let obj be ? ToObject(target).\n        let obj = args\n            .first()\n            .cloned()\n            .unwrap_or_default()\n            .to_object(context)?;\n\n        // 2. Let nameList be ? EnumerableOwnPropertyNames(obj, value).\n        let name_list = obj.enumerable_own_property_names(PropertyNameKind::Value, context)?;\n\n        // 3. Return CreateArrayFromList(nameList).\n        let result = Array::create_array_from_list(name_list, context);\n\n        Ok(result.into())\n    }\n\n    /// `Object.entries( target )`\n    ///\n    /// This method returns an array of a given object's own enumerable string-keyed property [key, value] pairs.\n    /// This is the same as iterating with a for...in loop,\n    /// except that a for...in loop enumerates properties in the prototype chain as well).\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///  - [MDN documentation][mdn]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-object.entries\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/entries\n    pub fn entries(_: &JsValue, args: &[JsValue], context: &mut Context) -> JsResult<JsValue> {\n        // 1. Let obj be ? ToObject(target).\n        let obj = args\n            .first()\n            .cloned()\n            .unwrap_or_default()\n            .to_object(context)?;\n\n        // 2. Let nameList be ? EnumerableOwnPropertyNames(obj, key+value).\n        let name_list =\n            obj.enumerable_own_property_names(PropertyNameKind::KeyAndValue, context)?;\n\n        // 3. Return CreateArrayFromList(nameList).\n        let result = Array::create_array_from_list(name_list, context);\n\n        Ok(result.into())\n    }\n\n    /// `Object.seal( target )`\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///  - [MDN documentation][mdn]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-object.seal\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/seal\n    pub fn seal(_: &JsValue, args: &[JsValue], context: &mut Context) -> JsResult<JsValue> {\n        let o = args.get_or_undefined(0);\n\n        if let Some(o) = o.as_object() {\n            // 2. Let status be ? SetIntegrityLevel(O, sealed).\n            let status = o.set_integrity_level(IntegrityLevel::Sealed, context)?;\n            // 3. If status is false, throw a TypeError exception.\n            if !status {\n                return Err(js_error!(TypeError: \"cannot seal object\"));\n            }\n        }\n        // 1. If Type(O) is not Object, return O.\n        // 4. Return O.\n        Ok(o.clone())\n    }\n\n    /// `Object.isSealed( target )`\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///  - [MDN documentation][mdn]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-object.issealed\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/isSealed\n    pub fn is_sealed(_: &JsValue, args: &[JsValue], context: &mut Context) -> JsResult<JsValue> {\n        let o = args.get_or_undefined(0);\n\n        // 1. If Type(O) is not Object, return true.\n        // 2. Return ? TestIntegrityLevel(O, sealed).\n        if let Some(o) = o.as_object() {\n            Ok(o.test_integrity_level(IntegrityLevel::Sealed, context)?\n                .into())\n        } else {\n            Ok(JsValue::new(true))\n        }\n    }\n\n    /// `Object.freeze( target )`\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///  - [MDN documentation][mdn]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-object.freeze\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/freeze\n    pub fn freeze(_: &JsValue, args: &[JsValue], context: &mut Context) -> JsResult<JsValue> {\n        let o = args.get_or_undefined(0);\n\n        if let Some(o) = o.as_object() {\n            // 2. Let status be ? SetIntegrityLevel(O, frozen).\n            let status = o.set_integrity_level(IntegrityLevel::Frozen, context)?;\n            // 3. If status is false, throw a TypeError exception.\n            if !status {\n                return Err(js_error!(TypeError: \"cannot freeze object\"));\n            }\n        }\n        // 1. If Type(O) is not Object, return O.\n        // 4. Return O.\n        Ok(o.clone())\n    }\n\n    /// `Object.isFrozen( target )`\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///  - [MDN documentation][mdn]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-object.isfrozen\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/isFrozen\n    pub fn is_frozen(_: &JsValue, args: &[JsValue], context: &mut Context) -> JsResult<JsValue> {\n        let o = args.get_or_undefined(0);\n\n        // 1. If Type(O) is not Object, return true.\n        // 2. Return ? TestIntegrityLevel(O, frozen).\n        if let Some(o) = o.as_object() {\n            Ok(o.test_integrity_level(IntegrityLevel::Frozen, context)?\n                .into())\n        } else {\n            Ok(JsValue::new(true))\n        }\n    }\n\n    /// `Object.preventExtensions( target )`\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///  - [MDN documentation][mdn]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-object.preventextensions\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/preventExtensions\n    pub fn prevent_extensions(\n        _: &JsValue,\n        args: &[JsValue],\n        context: &mut Context,\n    ) -> JsResult<JsValue> {\n        let o = args.get_or_undefined(0);\n\n        if let Some(o) = o.as_object() {\n            // 2. Let status be ? O.[[PreventExtensions]]().\n            let status =\n                o.__prevent_extensions__(&mut InternalMethodPropertyContext::new(context))?;\n            // 3. If status is false, throw a TypeError exception.\n            if !status {\n                return Err(js_error!(TypeError: \"cannot prevent extensions\"));\n            }\n        }\n        // 1. If Type(O) is not Object, return O.\n        // 4. Return O.\n        Ok(o.clone())\n    }\n\n    /// `Object.isExtensible( target )`\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///  - [MDN documentation][mdn]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-object.isextensible\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/isExtensible\n    pub fn is_extensible(\n        _: &JsValue,\n        args: &[JsValue],\n        context: &mut Context,\n    ) -> JsResult<JsValue> {\n        let o = args.get_or_undefined(0);\n        // 1. If Type(O) is not Object, return false.\n        if let Some(o) = o.as_object() {\n            // 2. Return ? IsExtensible(O).\n            Ok(o.is_extensible(context)?.into())\n        } else {\n            Ok(JsValue::new(false))\n        }\n    }\n\n    /// `Object.getOwnPropertyNames( object )`\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///  - [MDN documentation][mdn]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-object.getownpropertynames\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/getOwnPropertyNames\n    pub fn get_own_property_names(\n        _: &JsValue,\n        args: &[JsValue],\n        context: &mut Context,\n    ) -> JsResult<JsValue> {\n        // 1. Return ? GetOwnPropertyKeys(O, string).\n        let o = args.get_or_undefined(0);\n        get_own_property_keys(o, PropertyKeyType::String, context)\n    }\n\n    /// `Object.getOwnPropertySymbols( object )`\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///  - [MDN documentation][mdn]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-object.getownpropertysymbols\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/getOwnPropertySymbols\n    pub fn get_own_property_symbols(\n        _: &JsValue,\n        args: &[JsValue],\n        context: &mut Context,\n    ) -> JsResult<JsValue> {\n        // 1. Return ? GetOwnPropertyKeys(O, symbol).\n        let o = args.get_or_undefined(0);\n        get_own_property_keys(o, PropertyKeyType::Symbol, context)\n    }\n\n    /// `Object.hasOwn( object, property )`\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///  - [MDN documentation][mdn]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-object.hasown\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/hasOwn\n    pub fn has_own(_: &JsValue, args: &[JsValue], context: &mut Context) -> JsResult<JsValue> {\n        // 1. Let obj be ? ToObject(O).\n        let obj = args.get_or_undefined(0).to_object(context)?;\n\n        // 2. Let key be ? ToPropertyKey(P).\n        let key = args.get_or_undefined(1).to_property_key(context)?;\n\n        // 3. Return ? HasOwnProperty(obj, key).\n        Ok(obj.has_own_property(key, context)?.into())\n    }\n\n    /// `Object.fromEntries( iterable )`\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///  - [MDN documentation][mdn]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-object.fromentries\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/fromEntries\n    pub fn from_entries(_: &JsValue, args: &[JsValue], context: &mut Context) -> JsResult<JsValue> {\n        // 1. Perform ? RequireObjectCoercible(iterable).\n        let iterable = args.get_or_undefined(0).require_object_coercible()?;\n\n        // 2. Let obj be ! OrdinaryObjectCreate(%Object.prototype%).\n        // 3. Assert: obj is an extensible ordinary object with no own properties.\n        let obj = JsObject::with_object_proto(context.intrinsics());\n\n        // 4. Let closure be a new Abstract Closure with parameters (key, value) that captures\n        // obj and performs the following steps when called:\n        let closure = FunctionObjectBuilder::new(\n            context.realm(),\n            NativeFunction::from_copy_closure_with_captures(\n                |_, args, obj, context| {\n                    let key = args.get_or_undefined(0);\n                    let value = args.get_or_undefined(1);\n\n                    // a. Let propertyKey be ? ToPropertyKey(key).\n                    let property_key = key.to_property_key(context)?;\n\n                    // b. Perform ! CreateDataPropertyOrThrow(obj, propertyKey, value).\n                    obj.create_data_property_or_throw(property_key, value.clone(), context)?;\n\n                    // c. Return undefined.\n                    Ok(JsValue::undefined())\n                },\n                obj.clone(),\n            ),\n        );\n\n        // 5. Let adder be ! CreateBuiltinFunction(closure, 2, \"\", « »).\n        let adder = closure.length(2).name(\"\").build();\n\n        // 6. Return ? AddEntriesFromIterable(obj, iterable, adder).\n        map::add_entries_from_iterable(&obj, iterable, &adder, context)\n    }\n\n    /// [`Object.groupBy ( items, callbackfn )`][spec]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-object.groupby\n    pub(crate) fn group_by(\n        _: &JsValue,\n        args: &[JsValue],\n        context: &mut Context,\n    ) -> JsResult<JsValue> {\n        use std::hash::BuildHasherDefault;\n\n        use indexmap::IndexMap;\n        use rustc_hash::FxHasher;\n\n        use crate::builtins::{Number, iterable::if_abrupt_close_iterator};\n\n        let items = args.get_or_undefined(0);\n        let callback = args.get_or_undefined(1);\n        // 1. Let groups be ? GroupBy(items, callbackfn, property).\n\n        // `GroupBy`\n        // https://tc39.es/ecma262/#sec-groupby\n        // inlined to change the key type.\n\n        // 1. Perform ? RequireObjectCoercible(items).\n        items.require_object_coercible()?;\n\n        // 2. If IsCallable(callbackfn) is false, throw a TypeError exception.\n        let callback = callback\n            .as_callable()\n            .ok_or_else(|| js_error!(TypeError: \"callback must be a callable object\"))?;\n\n        // 3. Let groups be a new empty List.\n        let mut groups: IndexMap<PropertyKey, Vec<JsValue>, BuildHasherDefault<FxHasher>> =\n            IndexMap::default();\n\n        // 4. Let iteratorRecord be ? GetIterator(items, sync).\n        let mut iterator = items.get_iterator(IteratorHint::Sync, context)?;\n\n        // 5. Let k be 0.\n        let mut k = 0u64;\n\n        // 6. Repeat,\n        loop {\n            // a. If k ≥ 2^53 - 1, then\n            if k >= Number::MAX_SAFE_INTEGER as u64 {\n                // i. Let error be ThrowCompletion(a newly created TypeError object).\n                let error = js_error!(TypeError: \"exceeded maximum safe integer\");\n\n                // ii. Return ? IteratorClose(iteratorRecord, error).\n                return iterator.close(Err(error), context);\n            }\n\n            // b. Let next be ? IteratorStepValue(iteratorRecord).\n            let Some(next) = iterator.step_value(context)? else {\n                // c. If next is false, then\n                // i. Return groups.\n                break;\n            };\n\n            // d. Let value be next.\n            let value = next;\n\n            // e. Let key be Completion(Call(callbackfn, undefined, « value, 𝔽(k) »)).\n            let key = callback.call(&JsValue::undefined(), &[value.clone(), k.into()], context);\n\n            // f. IfAbruptCloseIterator(key, iteratorRecord).\n            let key = if_abrupt_close_iterator!(key, iterator, context);\n\n            // g. If keyCoercion is property, then\n            //     i. Set key to Completion(ToPropertyKey(key)).\n            let key = key.to_property_key(context);\n\n            //     ii. IfAbruptCloseIterator(key, iteratorRecord).\n            let key = if_abrupt_close_iterator!(key, iterator, context);\n\n            // i. Perform AddValueToKeyedGroup(groups, key, value).\n            groups.entry(key).or_default().push(value);\n\n            // j. Set k to k + 1.\n            k += 1;\n        }\n\n        // 2. Let obj be OrdinaryObjectCreate(null).\n        let obj = JsObject::with_null_proto();\n\n        // 3. For each Record { [[Key]], [[Elements]] } g of groups, do\n        for (key, elements) in groups {\n            // a. Let elements be CreateArrayFromList(g.[[Elements]]).\n            let elements = Array::create_array_from_list(elements, context);\n\n            // b. Perform ! CreateDataPropertyOrThrow(obj, g.[[Key]], elements).\n            obj.create_data_property_or_throw(key, elements, context)\n                .js_expect(\"cannot fail for a newly created object\")?;\n        }\n\n        // 4. Return obj.\n        Ok(obj.into())\n    }\n}\n\n/// The abstract operation `ObjectDefineProperties`\n///\n/// More information:\n///  - [ECMAScript reference][spec]\n///\n/// [spec]: https://tc39.es/ecma262/#sec-object.defineproperties\nfn object_define_properties(\n    object: &JsObject,\n    props: &JsValue,\n    context: &mut Context,\n) -> JsResult<()> {\n    // 1. Assert: Type(O) is Object.\n    // 2. Let props be ? ToObject(Properties).\n    let props = &props.to_object(context)?;\n\n    // 3. Let keys be ? props.[[OwnPropertyKeys]]().\n    let keys = props.__own_property_keys__(&mut InternalMethodPropertyContext::new(context))?;\n\n    // 4. Let descriptors be a new empty List.\n    let mut descriptors: Vec<(PropertyKey, PropertyDescriptor)> = Vec::new();\n\n    // 5. For each element nextKey of keys, do\n    for next_key in keys {\n        // a. Let propDesc be ? props.[[GetOwnProperty]](nextKey).\n        // b. If propDesc is not undefined and propDesc.[[Enumerable]] is true, then\n\n        if let Some(prop_desc) = props\n            .__get_own_property__(&next_key, &mut InternalMethodPropertyContext::new(context))?\n            && prop_desc.expect_enumerable()\n        {\n            // i. Let descObj be ? Get(props, nextKey).\n            let desc_obj = props.get(next_key.clone(), context)?;\n\n            // ii. Let desc be ? ToPropertyDescriptor(descObj).\n            let desc = desc_obj.to_property_descriptor(context)?;\n\n            // iii. Append the pair (a two element List) consisting of nextKey and desc to the end of descriptors.\n            descriptors.push((next_key, desc));\n        }\n    }\n\n    // 6. For each element pair of descriptors, do\n    // a. Let P be the first element of pair.\n    // b. Let desc be the second element of pair.\n    for (p, d) in descriptors {\n        // c. Perform ? DefinePropertyOrThrow(O, P, desc).\n        object.define_property_or_throw(p, d, context)?;\n    }\n\n    // 7. Return O.\n    Ok(())\n}\n\n/// Type enum used in the abstract operation `GetOwnPropertyKeys`.\n#[derive(Debug, Copy, Clone)]\nenum PropertyKeyType {\n    String,\n    Symbol,\n}\n\n/// The abstract operation `GetOwnPropertyKeys`.\n///\n/// More information:\n///  - [ECMAScript reference][spec]\n///\n/// [spec]: https://tc39.es/ecma262/#sec-getownpropertykeys\nfn get_own_property_keys(\n    o: &JsValue,\n    r#type: PropertyKeyType,\n    context: &mut Context,\n) -> JsResult<JsValue> {\n    // 1. Let obj be ? ToObject(o).\n    let obj = o.to_object(context)?;\n\n    // 2. Let keys be ? obj.[[OwnPropertyKeys]]().\n    let keys = obj.__own_property_keys__(&mut InternalMethodPropertyContext::new(context))?;\n\n    // 3. Let nameList be a new empty List.\n    // 4. For each element nextKey of keys, do\n    let name_list = keys.iter().filter_map(|next_key| {\n        // a. If Type(nextKey) is Symbol and type is symbol or Type(nextKey) is String and type is string, then\n        // i. Append nextKey as the last element of nameList.\n        match (r#type, &next_key) {\n            (PropertyKeyType::String, PropertyKey::String(_))\n            | (PropertyKeyType::Symbol, PropertyKey::Symbol(_)) => Some(next_key.into()),\n            (PropertyKeyType::String, PropertyKey::Index(index)) => {\n                Some(js_string!(index.get()).into())\n            }\n            _ => None,\n        }\n    });\n\n    // 5. Return CreateArrayFromList(nameList).\n    Ok(Array::create_array_from_list(name_list, context).into())\n}\n"
  },
  {
    "path": "core/engine/src/builtins/object/tests.rs",
    "content": "use crate::{JsNativeErrorKind, JsValue, TestAction, run_test_actions};\nuse boa_macros::js_str;\nuse indoc::indoc;\n\n#[test]\nfn object_create_length() {\n    run_test_actions([TestAction::assert_eq(\"Object.create.length\", 2)]);\n}\n\n#[test]\nfn object_create_with_regular_object() {\n    run_test_actions([TestAction::assert_eq(\"Object.create({ a: 5 }).a\", 5)]);\n}\n\n#[test]\nfn object_create_with_undefined() {\n    run_test_actions([TestAction::assert_native_error(\n        \"Object.create()\",\n        JsNativeErrorKind::Type,\n        \"Object.create: expected 'proto' to be an Object or null, got `undefined`\",\n    )]);\n}\n\n#[test]\nfn object_create_with_number() {\n    run_test_actions([TestAction::assert_native_error(\n        \"Object.create(5)\",\n        JsNativeErrorKind::Type,\n        \"Object.create: expected 'proto' to be an Object or null, got `number`\",\n    )]);\n}\n\n#[test]\nfn object_create_with_function() {\n    run_test_actions([TestAction::assert(indoc! {r#\"\n            const x = function (){};\n            const bar = Object.create(x);\n            bar.__proto__ === x\n        \"#})]);\n}\n\n#[test]\nfn object_is() {\n    run_test_actions([\n        TestAction::run(indoc! {r#\"\n                var foo = { a: 1};\n                var bar = { a: 1};\n            \"#}),\n        TestAction::assert(\"Object.is('foo', 'foo')\"),\n        TestAction::assert(\"!Object.is('foo', 'bar')\"),\n        TestAction::assert(\"!Object.is([], [])\"),\n        TestAction::assert(\"Object.is(foo, foo)\"),\n        TestAction::assert(\"!Object.is(foo, bar)\"),\n        TestAction::assert(\"Object.is(null, null)\"),\n        TestAction::assert(\"!Object.is(0, -0)\"),\n        TestAction::assert(\"Object.is(-0, -0)\"),\n        TestAction::assert(\"Object.is(NaN, 0/0)\"),\n        TestAction::assert(\"Object.is()\"),\n        TestAction::assert(\"Object.is(undefined)\"),\n    ]);\n}\n\n#[test]\nfn object_has_own_property() {\n    run_test_actions([\n        TestAction::run(indoc! {r#\"\n            let symA = Symbol('a');\n            let symB = Symbol('b');\n\n            let x = {\n                undefinedProp: undefined,\n                nullProp: null,\n                someProp: 1,\n                [symA]: 2,\n                100: 3,\n            };\n        \"#}),\n        TestAction::assert(\"!x.hasOwnProperty('hasOwnProperty')\"),\n        TestAction::assert(\"x.hasOwnProperty('undefinedProp')\"),\n        TestAction::assert(\"x.hasOwnProperty('nullProp')\"),\n        TestAction::assert(\"x.hasOwnProperty('someProp')\"),\n        TestAction::assert(\"!x.hasOwnProperty(symB)\"),\n        TestAction::assert(\"x.hasOwnProperty(symA)\"),\n        TestAction::assert(\"!x.hasOwnProperty(1000)\"),\n        TestAction::assert(\"x.hasOwnProperty(100)\"),\n    ]);\n}\n\n#[test]\nfn object_has_own() {\n    run_test_actions([\n        TestAction::run(indoc! {r#\"\n            let symA = Symbol('a');\n            let symB = Symbol('b');\n\n            let x = {\n                undefinedProp: undefined,\n                nullProp: null,\n                someProp: 1,\n                [symA]: 2,\n                100: 3,\n            };\n        \"#}),\n        TestAction::assert(\"!Object.hasOwn(x, 'hasOwnProperty')\"),\n        TestAction::assert(\"Object.hasOwn(x, 'undefinedProp')\"),\n        TestAction::assert(\"Object.hasOwn(x, 'nullProp')\"),\n        TestAction::assert(\"Object.hasOwn(x, 'someProp')\"),\n        TestAction::assert(\"!Object.hasOwn(x, symB)\"),\n        TestAction::assert(\"Object.hasOwn(x, symA)\"),\n        TestAction::assert(\"!Object.hasOwn(x, 1000)\"),\n        TestAction::assert(\"Object.hasOwn(x, 100)\"),\n    ]);\n}\n\n#[test]\nfn object_property_is_enumerable() {\n    run_test_actions([\n        TestAction::run(\"let x = { enumerableProp: 'yes' };\"),\n        TestAction::assert(\"x.propertyIsEnumerable('enumerableProp')\"),\n        TestAction::assert(\"!x.propertyIsEnumerable('hasOwnProperty')\"),\n        TestAction::assert(\"!x.propertyIsEnumerable('not_here')\"),\n        TestAction::assert(\"!x.propertyIsEnumerable()\"),\n    ]);\n}\n\n#[test]\nfn object_to_string() {\n    run_test_actions([\n        TestAction::run(indoc! {r#\"\n                Array.prototype.toString = Object.prototype.toString;\n                Function.prototype.toString = Object.prototype.toString;\n                Error.prototype.toString = Object.prototype.toString;\n                Boolean.prototype.toString = Object.prototype.toString;\n                Number.prototype.toString = Object.prototype.toString;\n                String.prototype.toString = Object.prototype.toString;\n                Date.prototype.toString = Object.prototype.toString;\n                RegExp.prototype.toString = Object.prototype.toString;\n            \"#}),\n        TestAction::assert_eq(\n            \"Object.prototype.toString.call(undefined)\",\n            js_str!(\"[object Undefined]\"),\n        ),\n        TestAction::assert_eq(\n            \"Object.prototype.toString.call(null)\",\n            js_str!(\"[object Null]\"),\n        ),\n        TestAction::assert_eq(\"[].toString()\", js_str!(\"[object Array]\")),\n        TestAction::assert_eq(\"(() => {}).toString()\", js_str!(\"[object Function]\")),\n        TestAction::assert_eq(\"(new Error('')).toString()\", js_str!(\"[object Error]\")),\n        TestAction::assert_eq(\"Boolean().toString()\", js_str!(\"[object Boolean]\")),\n        TestAction::assert_eq(\"Number(42).toString()\", js_str!(\"[object Number]\")),\n        TestAction::assert_eq(\"String('boa').toString()\", js_str!(\"[object String]\")),\n        TestAction::assert_eq(\"(new Date()).toString()\", js_str!(\"[object Date]\")),\n        TestAction::assert_eq(\"/boa/.toString()\", js_str!(\"[object RegExp]\")),\n        TestAction::assert_eq(\"({}).toString()\", js_str!(\"[object Object]\")),\n    ]);\n}\n\n#[test]\nfn define_symbol_property() {\n    run_test_actions([\n        TestAction::run(indoc! {r#\"\n                let obj = {};\n                let sym = Symbol(\"key\");\n                Object.defineProperty(obj, sym, { value: \"val\" });\n            \"#}),\n        TestAction::assert_eq(\"obj[sym]\", js_str!(\"val\")),\n    ]);\n}\n\n#[test]\nfn get_own_property_descriptor_1_arg_returns_undefined() {\n    run_test_actions([TestAction::assert_eq(\n        \"Object.getOwnPropertyDescriptor({a: 2})\",\n        JsValue::undefined(),\n    )]);\n}\n\n#[test]\nfn get_own_property_descriptor() {\n    run_test_actions([\n        TestAction::run(indoc! {r#\"\n                let obj = {a: 2};\n                let prop = Object.getOwnPropertyDescriptor(obj, \"a\");\n            \"#}),\n        TestAction::assert(\"prop.enumerable\"),\n        TestAction::assert(\"prop.writable\"),\n        TestAction::assert(\"prop.configurable\"),\n        TestAction::assert_eq(\"prop.value\", 2),\n    ]);\n}\n\n#[test]\nfn get_own_property_descriptors() {\n    run_test_actions([\n        TestAction::run(indoc! {r#\"\n                let obj = {a: 1, b: 2};\n                let props = Object.getOwnPropertyDescriptors(obj);\n            \"#}),\n        TestAction::assert(\"props.a.enumerable\"),\n        TestAction::assert(\"props.a.writable\"),\n        TestAction::assert(\"props.a.configurable\"),\n        TestAction::assert_eq(\"props.a.value\", 1),\n        TestAction::assert(\"props.b.enumerable\"),\n        TestAction::assert(\"props.b.writable\"),\n        TestAction::assert(\"props.b.configurable\"),\n        TestAction::assert_eq(\"props.b.value\", 2),\n    ]);\n}\n\n#[test]\nfn object_define_properties() {\n    run_test_actions([\n        TestAction::run(indoc! {r#\"\n                const obj = {};\n\n                Object.defineProperties(obj, {\n                    p: {\n                        value: 42,\n                        writable: true\n                    }\n                });\n\n                const prop = Object.getOwnPropertyDescriptor(obj, 'p');\n            \"#}),\n        TestAction::assert(\"!prop.enumerable\"),\n        TestAction::assert(\"prop.writable\"),\n        TestAction::assert(\"!prop.configurable\"),\n        TestAction::assert_eq(\"prop.value\", 42),\n    ]);\n}\n\n#[test]\nfn object_is_prototype_of() {\n    run_test_actions([TestAction::assert(\n        \"Object.prototype.isPrototypeOf(String.prototype)\",\n    )]);\n}\n\n#[test]\nfn object_get_own_property_names_invalid_args() {\n    const ERROR: &str = \"cannot convert 'null' or 'undefined' to object\";\n\n    run_test_actions([\n        TestAction::assert_native_error(\n            \"Object.getOwnPropertyNames()\",\n            JsNativeErrorKind::Type,\n            ERROR,\n        ),\n        TestAction::assert_native_error(\n            \"Object.getOwnPropertyNames(null)\",\n            JsNativeErrorKind::Type,\n            ERROR,\n        ),\n        TestAction::assert_native_error(\n            \"Object.getOwnPropertyNames(undefined)\",\n            JsNativeErrorKind::Type,\n            ERROR,\n        ),\n    ]);\n}\n\n#[test]\nfn object_get_own_property_names() {\n    run_test_actions([\n        TestAction::run_harness(),\n        TestAction::assert(indoc! {r#\"\n                arrayEquals(\n                    Object.getOwnPropertyNames(0),\n                    []\n                )\n            \"#}),\n        TestAction::assert(indoc! {r#\"\n                arrayEquals(\n                    Object.getOwnPropertyNames(false),\n                    []\n                )\n            \"#}),\n        TestAction::assert(indoc! {r#\"\n                arrayEquals(\n                    Object.getOwnPropertyNames(Symbol(\"a\")),\n                    []\n                )\n            \"#}),\n        TestAction::assert(indoc! {r#\"\n                arrayEquals(\n                    Object.getOwnPropertyNames({}),\n                    []\n                )\n            \"#}),\n        TestAction::assert(indoc! {r#\"\n                arrayEquals(\n                    Object.getOwnPropertyNames(NaN),\n                    []\n                )\n            \"#}),\n        TestAction::assert(indoc! {r#\"\n                arrayEquals(\n                    Object.getOwnPropertyNames([1, 2, 3]),\n                    [\"0\", \"1\", \"2\", \"length\"]\n                )\n            \"#}),\n        TestAction::assert(indoc! {r#\"\n                arrayEquals(\n                    Object.getOwnPropertyNames({\n                        \"a\": 1,\n                        \"b\": 2,\n                        [ Symbol(\"c\") ]: 3,\n                        [ Symbol(\"d\") ]: 4,\n                    }),\n                    [\"a\", \"b\"]\n                )\n            \"#}),\n    ]);\n}\n\n#[test]\nfn object_get_own_property_symbols_invalid_args() {\n    const ERROR: &str = \"cannot convert 'null' or 'undefined' to object\";\n\n    run_test_actions([\n        TestAction::assert_native_error(\n            \"Object.getOwnPropertySymbols()\",\n            JsNativeErrorKind::Type,\n            ERROR,\n        ),\n        TestAction::assert_native_error(\n            \"Object.getOwnPropertySymbols(null)\",\n            JsNativeErrorKind::Type,\n            ERROR,\n        ),\n        TestAction::assert_native_error(\n            \"Object.getOwnPropertySymbols(undefined)\",\n            JsNativeErrorKind::Type,\n            ERROR,\n        ),\n    ]);\n}\n\n#[test]\nfn object_get_own_property_symbols() {\n    run_test_actions([\n        TestAction::run_harness(),\n        TestAction::assert(indoc! {r#\"\n                arrayEquals(\n                    Object.getOwnPropertySymbols(0),\n                    []\n                )\n            \"#}),\n        TestAction::assert(indoc! {r#\"\n                arrayEquals(\n                    Object.getOwnPropertySymbols(false),\n                    []\n                )\n            \"#}),\n        TestAction::assert(indoc! {r#\"\n                arrayEquals(\n                    Object.getOwnPropertySymbols(Symbol(\"a\")),\n                    []\n                )\n            \"#}),\n        TestAction::assert(indoc! {r#\"\n                arrayEquals(\n                    Object.getOwnPropertySymbols({}),\n                    []\n                )\n            \"#}),\n        TestAction::assert(indoc! {r#\"\n                arrayEquals(\n                    Object.getOwnPropertySymbols(NaN),\n                    []\n                )\n            \"#}),\n        TestAction::assert(indoc! {r#\"\n                arrayEquals(\n                    Object.getOwnPropertySymbols([1, 2, 3]),\n                    []\n                )\n            \"#}),\n        TestAction::assert(indoc! {r#\"\n                let c = Symbol(\"c\");\n                let d = Symbol(\"d\");\n                arrayEquals(\n                    Object.getOwnPropertySymbols({\n                        \"a\": 1,\n                        \"b\": 2,\n                        [ c ]: 3,\n                        [ d ]: 4,\n                    }),\n                    [c, d]\n                )\n            \"#}),\n    ]);\n}\n\n#[test]\nfn object_from_entries_invalid_args() {\n    const ERROR: &str = \"cannot convert null or undefined to Object\";\n\n    run_test_actions([\n        TestAction::assert_native_error(\"Object.fromEntries()\", JsNativeErrorKind::Type, ERROR),\n        TestAction::assert_native_error(\"Object.fromEntries(null)\", JsNativeErrorKind::Type, ERROR),\n        TestAction::assert_native_error(\n            \"Object.fromEntries(undefined)\",\n            JsNativeErrorKind::Type,\n            ERROR,\n        ),\n    ]);\n}\n\n#[test]\nfn object_from_entries() {\n    run_test_actions([\n        TestAction::run(indoc! {r#\"\n                let sym = Symbol(\"sym\");\n                let map = Object.fromEntries([\n                    [\"long key\", 1],\n                    [\"short\", 2],\n                    [sym, 3],\n                    [5, 4],\n                ]);\n            \"#}),\n        TestAction::assert_eq(\"map['long key']\", 1),\n        TestAction::assert_eq(\"map.short\", 2),\n        TestAction::assert_eq(\"map[sym]\", 3),\n        TestAction::assert_eq(\"map[5]\", 4),\n    ]);\n}\n\n#[test]\nfn object_prototype_proto_accessor_properties() {\n    run_test_actions([\n        TestAction::run(\n            \"let desc = Object.getOwnPropertyDescriptor(Object.prototype, '__proto__');\",\n        ),\n        TestAction::assert_eq(\"desc.get.length\", 0),\n        TestAction::assert_eq(\"desc.set.length\", 1),\n        TestAction::assert_eq(\"desc.get.name\", js_str!(\"get __proto__\")),\n        TestAction::assert_eq(\"desc.set.name\", js_str!(\"set __proto__\")),\n    ]);\n}\n"
  },
  {
    "path": "core/engine/src/builtins/options.rs",
    "content": "//! Utilities to parse, validate and get options in builtins.\n\nuse std::{fmt, str::FromStr};\n\nuse crate::value::JsVariant;\nuse crate::{Context, JsNativeError, JsResult, JsString, JsValue, object::JsObject};\n\n/// A type used as an option parameter for [`get_option`].\npub(crate) trait OptionType: Sized {\n    /// Parses a [`JsValue`] into an instance of `Self`.\n    ///\n    /// Roughly equivalent to the algorithm steps of [9.12.13.3-7][spec], but allows for parsing\n    /// steps instead of returning a pure string, number or boolean.\n    ///\n    /// [spec]: https://tc39.es/ecma402/#sec-getoption\n    fn from_value(value: JsValue, context: &mut Context) -> JsResult<Self>;\n}\n\n/// A type that implements [`OptionType`] by parsing a string.\n///\n/// This automatically implements `OptionType` for a type if the type implements `FromStr`.\npub(crate) trait ParsableOptionType: FromStr {}\n\nimpl<T: ParsableOptionType> OptionType for T\nwhere\n    T::Err: fmt::Display,\n{\n    fn from_value(value: JsValue, context: &mut Context) -> JsResult<Self> {\n        value\n            .to_string(context)?\n            .to_std_string_escaped()\n            .parse::<Self>()\n            .map_err(|err| JsNativeError::range().with_message(err.to_string()).into())\n    }\n}\n\n/// Abstract operation [`GetOption ( options, property, type, values, fallback )`][spec]\n///\n/// Extracts the value of the property named `property` from the provided `options` object,\n/// converts it to the required `type` and checks whether it is one of a `List` of allowed\n/// `values`. If `values` is undefined, there is no fixed set of values and any is permitted.\n/// If the value is `undefined`, `required` would technically determine if the function should\n/// return `None` or an `Err`, but handling this on the caller's side using [`Option::ok_or_else`]\n/// should provide better context for error messages.\n///\n/// This is a safer alternative to `GetOption`, which tries to parse from the\n/// provided property a valid variant of the provided type `T`. It doesn't accept\n/// a `type` parameter since the type can specify in its implementation of [`OptionType`] whether\n/// it wants to parse from a [`str`] or convert directly from a boolean or number.\n///\n/// [spec]: https://tc39.es/ecma402/#sec-getoption\npub(crate) fn get_option<T: OptionType>(\n    options: &JsObject,\n    property: JsString,\n    context: &mut Context,\n) -> JsResult<Option<T>> {\n    // 1. Let value be ? Get(options, property).\n    let value = options.get(property, context)?;\n\n    // 2. If value is undefined, then\n    if value.is_undefined() {\n        // a. If default is required, throw a RangeError exception.\n        // b. Return default.\n        return Ok(None);\n    }\n\n    // The steps 3 to 7 must be made for each `OptionType`.\n    T::from_value(value, context).map(Some)\n}\n\n/// Abstract operation [`GetOptionsObject ( options )`][spec]\n///\n/// Returns a [`JsObject`] suitable for use with [`get_option`], either `options` itself or a\n/// default empty `JsObject`. It throws a `TypeError` if `options` is not undefined and not a `JsObject`.\n///\n/// [spec]: https://tc39.es/ecma402/#sec-getoptionsobject\npub(crate) fn get_options_object(options: &JsValue) -> JsResult<JsObject> {\n    match options.variant() {\n        // If options is undefined, then\n        JsVariant::Undefined => {\n            // a. Return OrdinaryObjectCreate(null).\n            Ok(JsObject::with_null_proto())\n        }\n        // 2. If Type(options) is Object, then\n        JsVariant::Object(obj) => {\n            // a. Return options.\n            Ok(obj.clone())\n        }\n        // 3. Throw a TypeError exception.\n        _ => Err(JsNativeError::typ()\n            .with_message(\"GetOptionsObject: provided options is not an object\")\n            .into()),\n    }\n}\n\n// Common options used in several builtins\n\nimpl OptionType for bool {\n    fn from_value(value: JsValue, _: &mut Context) -> JsResult<Self> {\n        // 5. If type is \"boolean\", then\n        //      a. Set value to ! ToBoolean(value).\n        Ok(value.to_boolean())\n    }\n}\n\nimpl OptionType for JsString {\n    fn from_value(value: JsValue, context: &mut Context) -> JsResult<Self> {\n        // 6. If type is \"string\", then\n        //      a. Set value to ? ToString(value).\n        value.to_string(context)\n    }\n}\n\nimpl OptionType for f64 {\n    fn from_value(value: JsValue, context: &mut Context) -> JsResult<Self> {\n        let value = value.to_number(context)?;\n\n        if !value.is_finite() {\n            return Err(JsNativeError::range()\n                .with_message(\"numeric option must be finite.\")\n                .into());\n        }\n\n        Ok(value)\n    }\n}\n"
  },
  {
    "path": "core/engine/src/builtins/promise/mod.rs",
    "content": "//! Boa's implementation of ECMAScript's global `Promise` object.\n\n#[cfg(test)]\nmod tests;\n\nuse super::{\n    BuiltInBuilder, BuiltInConstructor, IntrinsicObject,\n    iterable::{IteratorHint, IteratorRecord},\n};\n#[cfg(feature = \"experimental\")]\nuse crate::object::internal_methods::InternalMethodPropertyContext;\n#[cfg(feature = \"experimental\")]\nuse crate::property::PropertyKey;\nuse crate::{\n    Context, JsArgs, JsError, JsExpect, JsResult, JsString,\n    builtins::{Array, BuiltInObject},\n    context::intrinsics::{Intrinsics, StandardConstructor, StandardConstructors},\n    error::JsNativeError,\n    job::{JobCallback, PromiseJob},\n    js_string,\n    native_function::NativeFunction,\n    object::{\n        CONSTRUCTOR, FunctionObjectBuilder, JsFunction, JsObject,\n        internal_methods::get_prototype_from_constructor,\n    },\n    property::Attribute,\n    realm::Realm,\n    string::StaticJsStrings,\n    symbol::JsSymbol,\n    value::JsValue,\n};\nuse boa_gc::{Finalize, Gc, GcRefCell, Trace, custom_trace};\nuse boa_macros::JsData;\n#[cfg(feature = \"experimental\")]\nuse std::cell::RefCell;\nuse std::{cell::Cell, rc::Rc};\n\n// ==================== Public API ====================\n\n/// The current state of a [`Promise`].\n#[derive(Debug, Clone, Finalize, PartialEq, Eq)]\npub enum PromiseState {\n    /// The promise hasn't been resolved.\n    Pending,\n    /// The promise was fulfilled with a success value.\n    Fulfilled(JsValue),\n    /// The promise was rejected with a failure reason.\n    Rejected(JsValue),\n}\n\nunsafe impl Trace for PromiseState {\n    custom_trace!(this, mark, {\n        match this {\n            Self::Fulfilled(v) | Self::Rejected(v) => mark(v),\n            Self::Pending => {}\n        }\n    });\n}\n\nimpl PromiseState {\n    /// Gets the inner `JsValue` of a fulfilled promise state, or returns `None` if\n    /// the state is not `Fulfilled`.\n    #[must_use]\n    pub const fn as_fulfilled(&self) -> Option<&JsValue> {\n        match self {\n            Self::Fulfilled(v) => Some(v),\n            _ => None,\n        }\n    }\n\n    /// Gets the inner `JsValue` of a rejected promise state, or returns `None` if\n    /// the state is not `Rejected`.\n    #[must_use]\n    pub const fn as_rejected(&self) -> Option<&JsValue> {\n        match self {\n            Self::Rejected(v) => Some(v),\n            _ => None,\n        }\n    }\n}\n\n/// The internal representation of a `Promise` object.\n#[derive(Debug, Trace, Finalize, JsData)]\npub struct Promise {\n    state: PromiseState,\n    fulfill_reactions: Vec<ReactionRecord>,\n    reject_reactions: Vec<ReactionRecord>,\n    handled: bool,\n}\n\n/// The operation type of the [`HostPromiseRejectionTracker`][fn] abstract operation.\n///\n/// # Note\n///\n/// Per the spec:\n///\n/// > If operation is \"handle\", an implementation should not hold a reference to promise in a way\n/// > that would interfere with garbage collection. An implementation may hold a reference to promise\n/// > if operation is \"reject\", since it is expected that rejections will be rare and not on hot code paths.\n///\n/// [fn]: https://tc39.es/ecma262/#sec-host-promise-rejection-tracker\n#[derive(Debug, Clone, Copy, PartialEq, Eq)]\npub enum OperationType {\n    /// A promise was rejected without any handlers.\n    Reject,\n    /// A handler was added to a rejected promise for the first time.\n    Handle,\n}\n\n/// Functions used to resolve a pending promise.\n///\n/// This is equivalent to the parameters `resolveFunc` and `rejectFunc` of the executor passed to\n/// the [`Promise()`] constructor.\n///\n/// Both functions are always associated with the promise from which they were created. This\n/// means that by simply calling `resolve.call(this, &[values], context)` or\n/// `reject.call(this, &[error], context)`, the state of the original promise will be updated with\n/// the resolution value.\n///\n/// [`Promise()`]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/Promise\n#[derive(Debug, Clone, Finalize)]\npub struct ResolvingFunctions {\n    /// The `resolveFunc` parameter of the executor passed to `Promise()`.\n    pub resolve: JsFunction,\n    /// The `rejectFunc` parameter of the executor passed to `Promise()`.\n    pub reject: JsFunction,\n}\n\n// Manually implementing `Trace` to allow destructuring.\nunsafe impl Trace for ResolvingFunctions {\n    custom_trace!(this, mark, {\n        mark(&this.resolve);\n        mark(&this.reject);\n    });\n}\n\n// ==================== Private API ====================\n\n/// `IfAbruptRejectPromise ( value, capability )`\n///\n/// `IfAbruptRejectPromise` is a shorthand for a sequence of algorithm steps that use a `PromiseCapability` Record.\n///\n/// More information:\n///  - [ECMAScript reference][spec]\n///\n/// [spec]: https://tc39.es/ecma262/#sec-ifabruptrejectpromise\nmacro_rules! if_abrupt_reject_promise {\n    ($value:expr, $capability:expr, $context: expr) => {\n        match $value {\n            // 1. If value is an abrupt completion, then\n            Err(err) => {\n                let err = err.into_opaque($context)?;\n                // a. Perform ? Call(capability.[[Reject]], undefined, « value.[[Value]] »).\n                $capability\n                    .reject()\n                    .call(&JsValue::undefined(), &[err], $context)?;\n\n                // b. Return capability.[[Promise]].\n                return Ok($capability.promise().clone().into());\n            }\n            // 2. Else if value is a Completion Record, set value to value.[[Value]].\n            Ok(value) => value,\n        }\n    };\n}\n\npub(crate) use if_abrupt_reject_promise;\n\n/// The internal `PromiseCapability` data type.\n///\n/// More information:\n///  - [ECMAScript reference][spec]\n///\n/// [spec]: https://tc39.es/ecma262/#sec-promisecapability-records\n#[derive(Debug, Clone, Finalize)]\npub(crate) struct PromiseCapability {\n    /// The `[[Promise]]` field.\n    pub(crate) promise: JsObject,\n\n    /// The resolving functions,\n    pub(crate) functions: ResolvingFunctions,\n}\n\n// SAFETY: manually implementing `Trace` to allow destructuring.\nunsafe impl Trace for PromiseCapability {\n    custom_trace!(this, mark, {\n        mark(&this.promise);\n        mark(&this.functions);\n    });\n}\n\n/// The internal `PromiseReaction` data type.\n///\n/// More information:\n///  - [ECMAScript reference][spec]\n///\n/// [spec]: https://tc39.es/ecma262/#sec-promisereaction-records\n#[derive(Debug, Trace, Finalize)]\npub(crate) struct ReactionRecord {\n    /// The `[[Capability]]` field.\n    promise_capability: Option<PromiseCapability>,\n\n    /// The `[[Type]]` field.\n    #[unsafe_ignore_trace]\n    reaction_type: ReactionType,\n\n    /// The `[[Handler]]` field.\n    handler: Option<JobCallback>,\n}\n\n/// The `[[Type]]` field values of a `PromiseReaction` record.\n///\n/// More information:\n///  - [ECMAScript reference][spec]\n///\n/// [spec]: https://tc39.es/ecma262/#sec-promisereaction-records\n#[derive(Debug, Clone, Copy)]\nenum ReactionType {\n    Fulfill,\n    Reject,\n}\n\nimpl PromiseCapability {\n    /// `NewPromiseCapability ( C )`\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-newpromisecapability\n    pub(crate) fn new(c: &JsObject, context: &mut Context) -> JsResult<Self> {\n        #[derive(Debug, Clone, Trace, Finalize)]\n        struct RejectResolve {\n            reject: JsValue,\n            resolve: JsValue,\n        }\n\n        // 1. If IsConstructor(C) is false, throw a TypeError exception.\n        if !c.is_constructor() {\n            return Err(JsNativeError::typ()\n                .with_message(\"PromiseCapability: expected constructor\")\n                .into());\n        }\n\n        // 2. NOTE: C is assumed to be a constructor function that supports the parameter conventions of the Promise constructor (see 27.2.3.1).\n        // 3. Let promiseCapability be the PromiseCapability Record { [[Promise]]: undefined, [[Resolve]]: undefined, [[Reject]]: undefined }.\n        let promise_capability = Gc::new(GcRefCell::new(RejectResolve {\n            reject: JsValue::undefined(),\n            resolve: JsValue::undefined(),\n        }));\n\n        // 4. Let executorClosure be a new Abstract Closure with parameters (resolve, reject) that captures promiseCapability and performs the following steps when called:\n        // 5. Let executor be CreateBuiltinFunction(executorClosure, 2, \"\", « »).\n        let executor = FunctionObjectBuilder::new(\n            context.realm(),\n            NativeFunction::from_copy_closure_with_captures(\n                |_this, args: &[JsValue], captures, _| {\n                    let mut promise_capability = captures.borrow_mut();\n                    // a. If promiseCapability.[[Resolve]] is not undefined, throw a TypeError exception.\n                    if !promise_capability.resolve.is_undefined() {\n                        return Err(JsNativeError::typ()\n                            .with_message(\"promiseCapability.[[Resolve]] is not undefined\")\n                            .into());\n                    }\n\n                    // b. If promiseCapability.[[Reject]] is not undefined, throw a TypeError exception.\n                    if !promise_capability.reject.is_undefined() {\n                        return Err(JsNativeError::typ()\n                            .with_message(\"promiseCapability.[[Reject]] is not undefined\")\n                            .into());\n                    }\n\n                    let resolve = args.get_or_undefined(0);\n                    let reject = args.get_or_undefined(1);\n\n                    // c. Set promiseCapability.[[Resolve]] to resolve.\n                    promise_capability.resolve = resolve.clone();\n\n                    // d. Set promiseCapability.[[Reject]] to reject.\n                    promise_capability.reject = reject.clone();\n\n                    // e. Return undefined.\n                    Ok(JsValue::undefined())\n                },\n                promise_capability.clone(),\n            ),\n        )\n        .name(\"\")\n        .length(2)\n        .build()\n        .into();\n\n        // 6. Let promise be ? Construct(C, « executor »).\n        let promise = c.construct(&[executor], None, context)?;\n\n        let promise_capability = promise_capability.borrow();\n\n        let resolve = promise_capability.resolve.clone();\n        let reject = promise_capability.reject.clone();\n\n        // 7. If IsCallable(promiseCapability.[[Resolve]]) is false, throw a TypeError exception.\n        let resolve = resolve\n            .as_object()\n            .and_then(JsFunction::from_object)\n            .ok_or_else(|| {\n                JsNativeError::typ().with_message(\"promiseCapability.[[Resolve]] is not callable\")\n            })?;\n\n        // 8. If IsCallable(promiseCapability.[[Reject]]) is false, throw a TypeError exception.\n        let reject = reject\n            .as_object()\n            .and_then(JsFunction::from_object)\n            .ok_or_else(|| {\n                JsNativeError::typ().with_message(\"promiseCapability.[[Reject]] is not callable\")\n            })?;\n\n        // 9. Set promiseCapability.[[Promise]] to promise.\n        // 10. Return promiseCapability.\n        Ok(Self {\n            promise,\n            functions: ResolvingFunctions { resolve, reject },\n        })\n    }\n\n    /// Returns the promise object.\n    pub(crate) const fn promise(&self) -> &JsObject {\n        &self.promise\n    }\n\n    /// Returns the resolve function.\n    pub(crate) const fn resolve(&self) -> &JsFunction {\n        &self.functions.resolve\n    }\n\n    /// Returns the reject function.\n    pub(crate) const fn reject(&self) -> &JsFunction {\n        &self.functions.reject\n    }\n}\n\nimpl IntrinsicObject for Promise {\n    fn init(realm: &Realm) {\n        let get_species = BuiltInBuilder::callable(realm, Self::get_species)\n            .name(js_string!(\"get [Symbol.species]\"))\n            .build();\n\n        let builder = BuiltInBuilder::from_standard_constructor::<Self>(realm)\n            .static_method(Self::all, js_string!(\"all\"), 1)\n            .static_method(Self::all_settled, js_string!(\"allSettled\"), 1)\n            .static_method(Self::any, js_string!(\"any\"), 1)\n            .static_method(Self::race, js_string!(\"race\"), 1)\n            .static_method(Self::reject, js_string!(\"reject\"), 1)\n            .static_method(Self::resolve, js_string!(\"resolve\"), 1)\n            .static_method(Self::r#try, js_string!(\"try\"), 1)\n            .static_method(Self::with_resolvers, js_string!(\"withResolvers\"), 0)\n            .static_accessor(\n                JsSymbol::species(),\n                Some(get_species),\n                None,\n                Attribute::CONFIGURABLE,\n            )\n            .method(Self::then, js_string!(\"then\"), 2)\n            .method(Self::catch, js_string!(\"catch\"), 1)\n            .method(Self::finally, js_string!(\"finally\"), 1)\n            // <https://tc39.es/ecma262/#sec-promise.prototype-@@tostringtag>\n            .property(\n                JsSymbol::to_string_tag(),\n                Self::NAME,\n                Attribute::READONLY | Attribute::NON_ENUMERABLE | Attribute::CONFIGURABLE,\n            );\n\n        #[cfg(feature = \"experimental\")]\n        let builder = builder\n            .static_method(Self::all_keyed, js_string!(\"allKeyed\"), 1)\n            .static_method(Self::all_settled_keyed, js_string!(\"allSettledKeyed\"), 1);\n\n        builder.build();\n    }\n\n    fn get(intrinsics: &Intrinsics) -> JsObject {\n        Self::STANDARD_CONSTRUCTOR(intrinsics.constructors()).constructor()\n    }\n}\n\nimpl BuiltInObject for Promise {\n    const NAME: JsString = StaticJsStrings::PROMISE;\n}\n\nimpl BuiltInConstructor for Promise {\n    const CONSTRUCTOR_ARGUMENTS: usize = 1;\n    const PROTOTYPE_STORAGE_SLOTS: usize = 4;\n    const CONSTRUCTOR_STORAGE_SLOTS: usize = 12;\n\n    const STANDARD_CONSTRUCTOR: fn(&StandardConstructors) -> &StandardConstructor =\n        StandardConstructors::promise;\n\n    /// `Promise ( executor )`\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-promise-executor\n    fn constructor(\n        new_target: &JsValue,\n        args: &[JsValue],\n        context: &mut Context,\n    ) -> JsResult<JsValue> {\n        // 1. If NewTarget is undefined, throw a TypeError exception.\n        if new_target.is_undefined() {\n            return Err(JsNativeError::typ()\n                .with_message(\"Promise NewTarget cannot be undefined\")\n                .into());\n        }\n\n        // 2. If IsCallable(executor) is false, throw a TypeError exception.\n        let executor = args\n            .get_or_undefined(0)\n            .as_callable()\n            .ok_or_else(|| JsNativeError::typ().with_message(\"Promise executor is not callable\"))?;\n\n        // 3. Let promise be ? OrdinaryCreateFromConstructor(NewTarget, \"%Promise.prototype%\", « [[PromiseState]], [[PromiseResult]], [[PromiseFulfillReactions]], [[PromiseRejectReactions]], [[PromiseIsHandled]] »).\n        let promise =\n            get_prototype_from_constructor(new_target, StandardConstructors::promise, context)?;\n\n        let promise = JsObject::from_proto_and_data_with_shared_shape(\n            context.root_shape(),\n            promise,\n            // 4. Set promise.[[PromiseState]] to pending.\n            // 5. Set promise.[[PromiseFulfillReactions]] to a new empty List.\n            // 6. Set promise.[[PromiseRejectReactions]] to a new empty List.\n            // 7. Set promise.[[PromiseIsHandled]] to false.\n            Self::new(),\n        );\n\n        // 8. Let resolvingFunctions be CreateResolvingFunctions(promise).\n        let resolving_functions = Self::create_resolving_functions(&promise, context);\n\n        // 9. Let completion Completion(Call(executor, undefined, « resolvingFunctions.[[Resolve]], resolvingFunctions.[[Reject]] »)be ).\n        let completion = executor.call(\n            &JsValue::undefined(),\n            &[\n                resolving_functions.resolve.clone().into(),\n                resolving_functions.reject.clone().into(),\n            ],\n            context,\n        );\n\n        // 10. If completion is an abrupt completion, then\n        if let Err(e) = completion {\n            let e = e.into_opaque(context)?;\n            // a. Perform ? Call(resolvingFunctions.[[Reject]], undefined, « completion.[[Value]] »).\n            resolving_functions\n                .reject\n                .call(&JsValue::undefined(), &[e], context)?;\n        }\n\n        // 11. Return promise.\n        Ok(promise.into())\n    }\n}\n\nimpl Promise {\n    /// Creates a new, pending `Promise`.\n    pub(crate) fn new() -> Self {\n        Self {\n            state: PromiseState::Pending,\n            fulfill_reactions: Vec::default(),\n            reject_reactions: Vec::default(),\n            handled: false,\n        }\n    }\n\n    /// Gets the current state of the promise.\n    pub(crate) const fn state(&self) -> &PromiseState {\n        &self.state\n    }\n\n    /// [`Promise.try ( callbackfn, ...args )`][spec]\n    ///\n    /// Calls the given function and returns a new promise that is resolved if the function\n    /// completes normally and rejected if it throws.\n    ///\n    /// [spec]: https://tc39.es/proposal-promise-try/#sec-promise.try\n    pub(crate) fn r#try(\n        this: &JsValue,\n        args: &[JsValue],\n        context: &mut Context,\n    ) -> JsResult<JsValue> {\n        let callback = args.get_or_undefined(0);\n        let callback_args = args.get(1..).unwrap_or(&[]);\n\n        // 1. Let C be the this value.\n        // 2. If C is not an Object, throw a TypeError exception.\n        let c = this.as_object().ok_or_else(|| {\n            JsNativeError::typ().with_message(\"Promise.try() called on a non-object\")\n        })?;\n\n        // 3. Let promiseCapability be ? NewPromiseCapability(C).\n        let promise_capability = PromiseCapability::new(&c, context)?;\n\n        // 4. Let status be Completion(Call(callbackfn, undefined, args)).\n        let status = callback.call(&JsValue::undefined(), callback_args, context);\n\n        match status {\n            // 5. If status is an abrupt completion, then\n            Err(err) => {\n                let value = err.into_opaque(context)?;\n\n                // a. Perform ? Call(promiseCapability.[[Reject]], undefined, « status.[[Value]] »).\n                promise_capability.functions.reject.call(\n                    &JsValue::undefined(),\n                    &[value],\n                    context,\n                )?;\n            }\n            // 6. Else,\n            Ok(value) => {\n                // a. Perform ? Call(promiseCapability.[[Resolve]], undefined, « status.[[Value]] »).\n                promise_capability.functions.resolve.call(\n                    &JsValue::undefined(),\n                    &[value],\n                    context,\n                )?;\n            }\n        }\n\n        // 7. Return promiseCapability.[[Promise]].\n        Ok(promise_capability.promise.clone().into())\n    }\n\n    /// [`Promise.withResolvers ( )`][spec]\n    ///\n    /// Creates a new promise that is pending, and returns that promise plus the resolve and reject\n    /// functions associated with it.\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-promise.withResolvers\n    pub(crate) fn with_resolvers(\n        this: &JsValue,\n        _args: &[JsValue],\n        context: &mut Context,\n    ) -> JsResult<JsValue> {\n        // 1. Let C be the this value.\n\n        use super::OrdinaryObject;\n        let c = this.as_object().ok_or_else(|| {\n            JsNativeError::typ().with_message(\"Promise.withResolvers() called on a non-object\")\n        })?;\n\n        // 2. Let promiseCapability be ? NewPromiseCapability(C).\n        let PromiseCapability {\n            promise,\n            functions: ResolvingFunctions { resolve, reject },\n        } = PromiseCapability::new(&c, context)?;\n\n        // 3. Let obj be OrdinaryObjectCreate(%Object.prototype%).\n        // 4. Perform ! CreateDataPropertyOrThrow(obj, \"promise\", promiseCapability.[[Promise]]).\n        // 5. Perform ! CreateDataPropertyOrThrow(obj, \"resolve\", promiseCapability.[[Resolve]]).\n        // 6. Perform ! CreateDataPropertyOrThrow(obj, \"reject\", promiseCapability.[[Reject]]).\n        let obj = context.intrinsics().templates().with_resolvers().create(\n            OrdinaryObject,\n            vec![promise.into(), resolve.into(), reject.into()],\n        );\n\n        // 7. Return obj.\n        Ok(obj.into())\n    }\n\n    /// `Promise.all ( iterable )`\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///  - [MDN documentation][mdn]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-promise.all\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/all\n    pub(crate) fn all(\n        this: &JsValue,\n        args: &[JsValue],\n        context: &mut Context,\n    ) -> JsResult<JsValue> {\n        // 1. Let C be the this value.\n        let c = this.as_object().ok_or_else(|| {\n            JsNativeError::typ().with_message(\"Promise.all() called on a non-object\")\n        })?;\n\n        // 2. Let promiseCapability be ? NewPromiseCapability(C).\n        let promise_capability = PromiseCapability::new(&c, context)?;\n\n        // 3. Let promiseResolve be Completion(GetPromiseResolve(C)).\n        let promise_resolve = Self::get_promise_resolve(&c, context);\n\n        // 4. IfAbruptRejectPromise(promiseResolve, promiseCapability).\n        let promise_resolve =\n            if_abrupt_reject_promise!(promise_resolve, promise_capability, context);\n\n        // 5. Let iteratorRecord be Completion(GetIterator(iterable, sync)).\n        let iterator_record = args\n            .get_or_undefined(0)\n            .get_iterator(IteratorHint::Sync, context);\n\n        // 6. IfAbruptRejectPromise(iteratorRecord, promiseCapability).\n        let mut iterator_record =\n            if_abrupt_reject_promise!(iterator_record, promise_capability, context);\n\n        // 7. Let result be Completion(PerformPromiseAll(iteratorRecord, C, promiseCapability, promiseResolve)).\n        let mut result = Self::perform_promise_all(\n            &mut iterator_record,\n            &c,\n            &promise_capability,\n            &promise_resolve,\n            context,\n        )\n        .map(JsValue::from);\n\n        // 8. If result is an abrupt completion, then\n        if result.is_err() {\n            // a. If iteratorRecord.[[Done]] is false, set result to Completion(IteratorClose(iteratorRecord, result)).\n            if !iterator_record.done() {\n                result = iterator_record.close(result, context);\n            }\n\n            // b. IfAbruptRejectPromise(result, promiseCapability).\n            let result = if_abrupt_reject_promise!(result, promise_capability, context);\n\n            return Ok(result);\n        }\n\n        // 9. Return ? result.\n        result\n    }\n\n    /// `PerformPromiseAll ( iteratorRecord, constructor, resultCapability, promiseResolve )`\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-performpromiseall\n    pub(crate) fn perform_promise_all(\n        iterator_record: &mut IteratorRecord,\n        constructor: &JsObject,\n        result_capability: &PromiseCapability,\n        promise_resolve: &JsObject,\n        context: &mut Context,\n    ) -> JsResult<JsObject> {\n        #[derive(Debug, Trace, Finalize)]\n        struct ResolveElementCaptures {\n            #[unsafe_ignore_trace]\n            already_called: Rc<Cell<bool>>,\n            index: usize,\n            values: Gc<GcRefCell<Vec<JsValue>>>,\n            capability_resolve: JsFunction,\n            #[unsafe_ignore_trace]\n            remaining_elements_count: Rc<Cell<i32>>,\n        }\n\n        // 1. Let values be a new empty List.\n        let values = Gc::new(GcRefCell::new(Vec::new()));\n\n        // 2. Let remainingElementsCount be the Record { [[Value]]: 1 }.\n        let remaining_elements_count = Rc::new(Cell::new(1));\n\n        // 3. Let index be 0.\n        let mut index = 0;\n\n        // 4. Repeat,\n        while let Some(next) = iterator_record.step_value(context)? {\n            // c. Append undefined to values.\n            values.borrow_mut().push(JsValue::undefined());\n\n            // d. Let nextPromise be ? Call(promiseResolve, constructor, « next »).\n            let next_promise =\n                promise_resolve.call(&constructor.clone().into(), &[next], context)?;\n\n            // e. Let steps be the algorithm steps defined in Promise.all Resolve Element Functions.\n            // f. Let length be the number of non-optional parameters of the function definition in Promise.all Resolve Element Functions.\n            // g. Let onFulfilled be CreateBuiltinFunction(steps, length, \"\", « [[AlreadyCalled]], [[Index]], [[Values]], [[Capability]], [[RemainingElements]] »).\n            // h. Set onFulfilled.[[AlreadyCalled]] to false.\n            // i. Set onFulfilled.[[Index]] to index.\n            // j. Set onFulfilled.[[Values]] to values.\n            // k. Set onFulfilled.[[Capability]] to resultCapability.\n            // l. Set onFulfilled.[[RemainingElements]] to remainingElementsCount.\n            let on_fulfilled = FunctionObjectBuilder::new(\n                context.realm(),\n                NativeFunction::from_copy_closure_with_captures(\n                    |_, args, captures, context| {\n                        // https://tc39.es/ecma262/#sec-promise.all-resolve-element-functions\n\n                        // 1. Let F be the active function object.\n                        // 2. If F.[[AlreadyCalled]] is true, return undefined.\n                        if captures.already_called.get() {\n                            return Ok(JsValue::undefined());\n                        }\n\n                        // 3. Set F.[[AlreadyCalled]] to true.\n                        captures.already_called.set(true);\n\n                        // 4. Let index be F.[[Index]].\n                        // 5. Let values be F.[[Values]].\n                        // 6. Let promiseCapability be F.[[Capability]].\n                        // 7. Let remainingElementsCount be F.[[RemainingElements]].\n\n                        // 8. Set values[index] to x.\n                        captures.values.borrow_mut()[captures.index] =\n                            args.get_or_undefined(0).clone();\n\n                        // 9. Set remainingElementsCount.[[Value]] to remainingElementsCount.[[Value]] - 1.\n                        captures\n                            .remaining_elements_count\n                            .set(captures.remaining_elements_count.get() - 1);\n\n                        // 10. If remainingElementsCount.[[Value]] is 0, then\n                        if captures.remaining_elements_count.get() == 0 {\n                            // a. Let valuesArray be CreateArrayFromList(values).\n                            let values_array = Array::create_array_from_list(\n                                captures.values.borrow().as_slice().iter().cloned(),\n                                context,\n                            );\n\n                            // b. Return ? Call(promiseCapability.[[Resolve]], undefined, « valuesArray »).\n                            return captures.capability_resolve.call(\n                                &JsValue::undefined(),\n                                &[values_array.into()],\n                                context,\n                            );\n                        }\n\n                        // 11. Return undefined.\n                        Ok(JsValue::undefined())\n                    },\n                    ResolveElementCaptures {\n                        already_called: Rc::new(Cell::new(false)),\n                        index,\n                        values: values.clone(),\n                        capability_resolve: result_capability.functions.resolve.clone(),\n                        remaining_elements_count: remaining_elements_count.clone(),\n                    },\n                ),\n            )\n            .name(\"\")\n            .length(1)\n            .constructor(false)\n            .build();\n\n            // m. Set remainingElementsCount.[[Value]] to remainingElementsCount.[[Value]] + 1.\n            remaining_elements_count.set(remaining_elements_count.get() + 1);\n\n            // n. Perform ? Invoke(nextPromise, \"then\", « onFulfilled, resultCapability.[[Reject]] »).\n            next_promise.invoke(\n                js_string!(\"then\"),\n                &[\n                    on_fulfilled.into(),\n                    result_capability.functions.reject.clone().into(),\n                ],\n                context,\n            )?;\n\n            // o. Set index to index + 1.\n            index += 1;\n        }\n\n        // b. If next is done, then\n        //     i. Set remainingElementsCount.[[Value]] to remainingElementsCount.[[Value]] - 1.\n        remaining_elements_count.set(remaining_elements_count.get() - 1);\n\n        //     ii. If remainingElementsCount.[[Value]] = 0, then\n        if remaining_elements_count.get() == 0 {\n            // 1. Let valuesArray be CreateArrayFromList(values).\n            let values_array =\n                Array::create_array_from_list(values.borrow().iter().cloned(), context);\n\n            // 2. Perform ? Call(resultCapability.[[Resolve]], undefined, « valuesArray »).\n            result_capability.functions.resolve.call(\n                &JsValue::undefined(),\n                &[values_array.into()],\n                context,\n            )?;\n        }\n\n        //     iii. Return resultCapability.[[Promise]].\n        Ok(result_capability.promise.clone())\n    }\n\n    /// `Promise.allSettled ( iterable )`\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///  - [MDN documentation][mdn]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-promise.allsettled\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/allSettled\n    pub(crate) fn all_settled(\n        this: &JsValue,\n        args: &[JsValue],\n        context: &mut Context,\n    ) -> JsResult<JsValue> {\n        // 1. Let C be the this value.\n        let c = this.as_object().ok_or_else(|| {\n            JsNativeError::typ().with_message(\"Promise.allSettled() called on a non-object\")\n        })?;\n\n        // 2. Let promiseCapability be ? NewPromiseCapability(C).\n        let promise_capability = PromiseCapability::new(&c, context)?;\n\n        // 3. Let promiseResolve be Completion(GetPromiseResolve(C)).\n        let promise_resolve = Self::get_promise_resolve(&c, context);\n\n        // 4. IfAbruptRejectPromise(promiseResolve, promiseCapability).\n        let promise_resolve =\n            if_abrupt_reject_promise!(promise_resolve, promise_capability, context);\n\n        // 5. Let iteratorRecord be Completion(GetIterator(iterable, sync)).\n        let iterator_record = args\n            .get_or_undefined(0)\n            .get_iterator(IteratorHint::Sync, context);\n\n        // 6. IfAbruptRejectPromise(iteratorRecord, promiseCapability).\n        let mut iterator_record =\n            if_abrupt_reject_promise!(iterator_record, promise_capability, context);\n\n        // 7. Let result be Completion(PerformPromiseAllSettled(iteratorRecord, C, promiseCapability, promiseResolve)).\n        let mut result = Self::perform_promise_all_settled(\n            &mut iterator_record,\n            &c,\n            &promise_capability,\n            &promise_resolve,\n            context,\n        )\n        .map(JsValue::from);\n\n        // 8. If result is an abrupt completion, then\n        if result.is_err() {\n            // a. If iteratorRecord.[[Done]] is false, set result to Completion(IteratorClose(iteratorRecord, result)).\n            if !iterator_record.done() {\n                result = iterator_record.close(result, context);\n            }\n\n            // b. IfAbruptRejectPromise(result, promiseCapability).\n            let result = if_abrupt_reject_promise!(result, promise_capability, context);\n\n            return Ok(result);\n        }\n\n        // 9. Return ? result.\n        result\n    }\n\n    /// `PerformPromiseAllSettled ( iteratorRecord, constructor, resultCapability, promiseResolve )`\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-performpromiseallsettled\n    pub(crate) fn perform_promise_all_settled(\n        iterator_record: &mut IteratorRecord,\n        constructor: &JsObject,\n        result_capability: &PromiseCapability,\n        promise_resolve: &JsObject,\n        context: &mut Context,\n    ) -> JsResult<JsObject> {\n        #[derive(Debug, Trace, Finalize)]\n        struct ResolveRejectElementCaptures {\n            #[unsafe_ignore_trace]\n            already_called: Rc<Cell<bool>>,\n            index: usize,\n            values: Gc<GcRefCell<Vec<JsValue>>>,\n            capability: JsFunction,\n            #[unsafe_ignore_trace]\n            remaining_elements: Rc<Cell<i32>>,\n        }\n\n        // 1. Let values be a new empty List.\n        let values = Gc::new(GcRefCell::new(Vec::new()));\n\n        // 2. Let remainingElementsCount be the Record { [[Value]]: 1 }.\n        let remaining_elements_count = Rc::new(Cell::new(1));\n\n        // 3. Let index be 0.\n        let mut index = 0;\n\n        // 4. Repeat,\n        while let Some(next) = iterator_record.step_value(context)? {\n            // c. Append undefined to values.\n            values.borrow_mut().push(JsValue::undefined());\n\n            // d. Let nextPromise be ? Call(promiseResolve, constructor, « next »).\n            let next_promise =\n                promise_resolve.call(&constructor.clone().into(), &[next], context)?;\n\n            // h. Let alreadyCalled be the Record { [[Value]]: false }.\n            let already_called = Rc::new(Cell::new(false));\n\n            // e. Let stepsFulfilled be the algorithm steps defined in Promise.allSettled Resolve Element Functions.\n            // f. Let lengthFulfilled be the number of non-optional parameters of the function definition in Promise.allSettled Resolve Element Functions.\n            // g. Let onFulfilled be CreateBuiltinFunction(stepsFulfilled, lengthFulfilled, \"\", « [[AlreadyCalled]], [[Index]], [[Values]], [[Capability]], [[RemainingElements]] »).\n            // i. Set onFulfilled.[[AlreadyCalled]] to alreadyCalled.\n            // j. Set onFulfilled.[[Index]] to index.\n            // k. Set onFulfilled.[[Values]] to values.\n            // l. Set onFulfilled.[[Capability]] to resultCapability.\n            // m. Set onFulfilled.[[RemainingElements]] to remainingElementsCount.\n            let on_fulfilled = FunctionObjectBuilder::new(\n                context.realm(),\n                NativeFunction::from_copy_closure_with_captures(\n                    |_, args, captures, context| {\n                        // https://tc39.es/ecma262/#sec-promise.allsettled-resolve-element-functions\n\n                        // 1. Let F be the active function object.\n                        // 2. Let alreadyCalled be F.[[AlreadyCalled]].\n\n                        // 3. If alreadyCalled.[[Value]] is true, return undefined.\n                        if captures.already_called.get() {\n                            return Ok(JsValue::undefined());\n                        }\n\n                        // 4. Set alreadyCalled.[[Value]] to true.\n                        captures.already_called.set(true);\n\n                        // 5. Let index be F.[[Index]].\n                        // 6. Let values be F.[[Values]].\n                        // 7. Let promiseCapability be F.[[Capability]].\n                        // 8. Let remainingElementsCount be F.[[RemainingElements]].\n\n                        // 9. Let obj be OrdinaryObjectCreate(%Object.prototype%).\n                        let obj = JsObject::with_object_proto(context.intrinsics());\n\n                        // 10. Perform ! CreateDataPropertyOrThrow(obj, \"status\", \"fulfilled\").\n                        obj.create_data_property_or_throw(\n                            js_string!(\"status\"),\n                            js_string!(\"fulfilled\"),\n                            context,\n                        )\n                        .js_expect(\"cannot fail per spec\")?;\n\n                        // 11. Perform ! CreateDataPropertyOrThrow(obj, \"value\", x).\n                        obj.create_data_property_or_throw(\n                            js_string!(\"value\"),\n                            args.get_or_undefined(0).clone(),\n                            context,\n                        )\n                        .js_expect(\"cannot fail per spec\")?;\n\n                        // 12. Set values[index] to obj.\n                        captures.values.borrow_mut()[captures.index] = obj.into();\n\n                        // 13. Set remainingElementsCount.[[Value]] to remainingElementsCount.[[Value]] - 1.\n                        captures\n                            .remaining_elements\n                            .set(captures.remaining_elements.get() - 1);\n\n                        // 14. If remainingElementsCount.[[Value]] is 0, then\n                        if captures.remaining_elements.get() == 0 {\n                            // a. Let valuesArray be CreateArrayFromList(values).\n                            let values_array = Array::create_array_from_list(\n                                captures.values.borrow().as_slice().iter().cloned(),\n                                context,\n                            );\n\n                            // b. Return ? Call(promiseCapability.[[Resolve]], undefined, « valuesArray »).\n                            return captures.capability.call(\n                                &JsValue::undefined(),\n                                &[values_array.into()],\n                                context,\n                            );\n                        }\n\n                        // 15. Return undefined.\n                        Ok(JsValue::undefined())\n                    },\n                    ResolveRejectElementCaptures {\n                        already_called: already_called.clone(),\n                        index,\n                        values: values.clone(),\n                        capability: result_capability.functions.resolve.clone(),\n                        remaining_elements: remaining_elements_count.clone(),\n                    },\n                ),\n            )\n            .name(\"\")\n            .length(1)\n            .constructor(false)\n            .build();\n\n            // n. Let stepsRejected be the algorithm steps defined in Promise.allSettled Reject Element Functions.\n            // o. Let lengthRejected be the number of non-optional parameters of the function definition in Promise.allSettled Reject Element Functions.\n            // p. Let onRejected be CreateBuiltinFunction(stepsRejected, lengthRejected, \"\", « [[AlreadyCalled]], [[Index]], [[Values]], [[Capability]], [[RemainingElements]] »).\n            // q. Set onRejected.[[AlreadyCalled]] to alreadyCalled.\n            // r. Set onRejected.[[Index]] to index.\n            // s. Set onRejected.[[Values]] to values.\n            // t. Set onRejected.[[Capability]] to resultCapability.\n            // u. Set onRejected.[[RemainingElements]] to remainingElementsCount.\n            let on_rejected = FunctionObjectBuilder::new(\n                context.realm(),\n                NativeFunction::from_copy_closure_with_captures(\n                    |_, args, captures, context| {\n                        // https://tc39.es/ecma262/#sec-promise.allsettled-reject-element-functions\n\n                        // 1. Let F be the active function object.\n                        // 2. Let alreadyCalled be F.[[AlreadyCalled]].\n\n                        // 3. If alreadyCalled.[[Value]] is true, return undefined.\n                        if captures.already_called.get() {\n                            return Ok(JsValue::undefined());\n                        }\n\n                        // 4. Set alreadyCalled.[[Value]] to true.\n                        captures.already_called.set(true);\n\n                        // 5. Let index be F.[[Index]].\n                        // 6. Let values be F.[[Values]].\n                        // 7. Let promiseCapability be F.[[Capability]].\n                        // 8. Let remainingElementsCount be F.[[RemainingElements]].\n\n                        // 9. Let obj be OrdinaryObjectCreate(%Object.prototype%).\n                        let obj = JsObject::with_object_proto(context.intrinsics());\n\n                        // 10. Perform ! CreateDataPropertyOrThrow(obj, \"status\", \"rejected\").\n                        obj.create_data_property_or_throw(\n                            js_string!(\"status\"),\n                            js_string!(\"rejected\"),\n                            context,\n                        )\n                        .js_expect(\"cannot fail per spec\")?;\n\n                        // 11. Perform ! CreateDataPropertyOrThrow(obj, \"reason\", x).\n                        obj.create_data_property_or_throw(\n                            js_string!(\"reason\"),\n                            args.get_or_undefined(0).clone(),\n                            context,\n                        )\n                        .js_expect(\"cannot fail per spec\")?;\n\n                        // 12. Set values[index] to obj.\n                        captures.values.borrow_mut()[captures.index] = obj.into();\n\n                        // 13. Set remainingElementsCount.[[Value]] to remainingElementsCount.[[Value]] - 1.\n                        captures\n                            .remaining_elements\n                            .set(captures.remaining_elements.get() - 1);\n\n                        // 14. If remainingElementsCount.[[Value]] is 0, then\n                        if captures.remaining_elements.get() == 0 {\n                            // a. Let valuesArray be CreateArrayFromList(values).\n                            let values_array = Array::create_array_from_list(\n                                captures.values.borrow().as_slice().iter().cloned(),\n                                context,\n                            );\n\n                            // b. Return ? Call(promiseCapability.[[Resolve]], undefined, « valuesArray »).\n                            return captures.capability.call(\n                                &JsValue::undefined(),\n                                &[values_array.into()],\n                                context,\n                            );\n                        }\n\n                        // 15. Return undefined.\n                        Ok(JsValue::undefined())\n                    },\n                    ResolveRejectElementCaptures {\n                        already_called,\n                        index,\n                        values: values.clone(),\n                        capability: result_capability.functions.resolve.clone(),\n                        remaining_elements: remaining_elements_count.clone(),\n                    },\n                ),\n            )\n            .name(\"\")\n            .length(1)\n            .constructor(false)\n            .build();\n\n            // v. Set remainingElementsCount.[[Value]] to remainingElementsCount.[[Value]] + 1.\n            remaining_elements_count.set(remaining_elements_count.get() + 1);\n\n            // w. Perform ? Invoke(nextPromise, \"then\", « onFulfilled, onRejected »).\n            next_promise.invoke(\n                js_string!(\"then\"),\n                &[on_fulfilled.into(), on_rejected.into()],\n                context,\n            )?;\n\n            // x. Set index to index + 1.\n            index += 1;\n        }\n\n        // b. If next is done, then\n        //     i. Set remainingElementsCount.[[Value]] to remainingElementsCount.[[Value]] - 1.\n        remaining_elements_count.set(remaining_elements_count.get() - 1);\n\n        //     ii. If remainingElementsCount.[[Value]] = 0, then\n        if remaining_elements_count.get() == 0 {\n            // 1. Let valuesArray be CreateArrayFromList(values).\n            let values_array =\n                Array::create_array_from_list(values.borrow().as_slice().iter().cloned(), context);\n\n            // 2. Perform ? Call(resultCapability.[[Resolve]], undefined, « valuesArray »).\n            result_capability.functions.resolve.call(\n                &JsValue::undefined(),\n                &[values_array.into()],\n                context,\n            )?;\n        }\n\n        //     iii. Return resultCapability.[[Promise]].\n        Ok(result_capability.promise.clone())\n    }\n\n    /// `Promise.allKeyed ( promises )`\n    ///\n    /// More information:\n    ///  - [TC39 proposal spec][spec]\n    ///\n    /// [spec]: https://tc39.es/proposal-await-dictionary/#sec-promise.allkeyed\n    #[cfg(feature = \"experimental\")]\n    pub(crate) fn all_keyed(\n        this: &JsValue,\n        args: &[JsValue],\n        context: &mut Context,\n    ) -> JsResult<JsValue> {\n        Self::keyed_common(this, args, context, KeyedVariant::All, \"allKeyed\")\n    }\n\n    /// `Promise.allSettledKeyed ( promises )`\n    ///\n    /// More information:\n    ///  - [TC39 proposal spec][spec]\n    ///\n    /// [spec]: https://tc39.es/proposal-await-dictionary/#sec-promise.allsettledkeyed\n    #[cfg(feature = \"experimental\")]\n    pub(crate) fn all_settled_keyed(\n        this: &JsValue,\n        args: &[JsValue],\n        context: &mut Context,\n    ) -> JsResult<JsValue> {\n        Self::keyed_common(\n            this,\n            args,\n            context,\n            KeyedVariant::AllSettled,\n            \"allSettledKeyed\",\n        )\n    }\n\n    /// Shared entry-point logic for `Promise.allKeyed` and `Promise.allSettledKeyed`.\n    #[cfg(feature = \"experimental\")]\n    fn keyed_common(\n        this: &JsValue,\n        args: &[JsValue],\n        context: &mut Context,\n        variant: KeyedVariant,\n        name: &str,\n    ) -> JsResult<JsValue> {\n        // 1. Let C be the this value.\n        let c = this.as_object().ok_or_else(|| {\n            JsNativeError::typ().with_message(format!(\"Promise.{name}() called on a non-object\"))\n        })?;\n\n        // 2. Let promiseCapability be ? NewPromiseCapability(C).\n        let promise_capability = PromiseCapability::new(&c, context)?;\n\n        // 3. Let promiseResolve be Completion(GetPromiseResolve(C)).\n        let promise_resolve = Self::get_promise_resolve(&c, context);\n\n        // 4. IfAbruptRejectPromise(promiseResolve, promiseCapability).\n        let promise_resolve =\n            if_abrupt_reject_promise!(promise_resolve, promise_capability, context);\n\n        // 5. If promises is not an Object, then\n        let promises = args.get_or_undefined(0);\n        let Some(promises_obj) = promises.as_object() else {\n            // a. Let error be a newly created TypeError object.\n            let error = JsNativeError::typ()\n                .with_message(format!(\"Promise.{name}() expects an object argument\"))\n                .into_opaque(context);\n            // b. Perform ? Call(promiseCapability.[[Reject]], undefined, « error »).\n            promise_capability.functions.reject.call(\n                &JsValue::undefined(),\n                &[error.into()],\n                context,\n            )?;\n            // c. Return promiseCapability.[[Promise]].\n            return Ok(promise_capability.promise.clone().into());\n        };\n\n        // 6. Let result be Completion(PerformPromiseAllKeyed(variant, promises, C, promiseCapability, promiseResolve)).\n        let result = Self::perform_promise_all_keyed(\n            variant,\n            &promises_obj,\n            &c,\n            &promise_capability,\n            &promise_resolve,\n            context,\n        );\n\n        // 7. IfAbruptRejectPromise(result, promiseCapability).\n        let _result = if_abrupt_reject_promise!(result, promise_capability, context);\n\n        // 8. Return promiseCapability.[[Promise]].\n        Ok(promise_capability.promise.clone().into())\n    }\n\n    /// `PerformPromiseAllKeyed ( variant, promises, constructor, resultCapability, promiseResolve )`\n    ///\n    /// More information:\n    ///  - [TC39 proposal spec][spec]\n    ///\n    /// [spec]: https://tc39.es/proposal-await-dictionary/#sec-performpromiseallkeyed\n    #[cfg(feature = \"experimental\")]\n    fn perform_promise_all_keyed(\n        variant: KeyedVariant,\n        promises: &JsObject,\n        constructor: &JsObject,\n        result_capability: &PromiseCapability,\n        promise_resolve: &JsObject,\n        context: &mut Context,\n    ) -> JsResult<JsValue> {\n        #[derive(Debug, Trace, Finalize)]\n        struct KeyedResolveCaptures {\n            #[unsafe_ignore_trace]\n            already_called: Rc<Cell<bool>>,\n            index: usize,\n            #[unsafe_ignore_trace]\n            variant: KeyedVariant,\n            #[unsafe_ignore_trace]\n            keys: Rc<RefCell<Vec<PropertyKey>>>,\n            values: Gc<GcRefCell<Vec<JsValue>>>,\n            capability: JsFunction,\n            #[unsafe_ignore_trace]\n            remaining_elements: Rc<Cell<i32>>,\n        }\n\n        // 1. Let allKeys be ? promises.[[OwnPropertyKeys]]().\n        let all_keys = promises.own_property_keys(context)?;\n\n        // 2. Let keys be a new empty List.\n        let keys = Rc::new(RefCell::new(Vec::new()));\n\n        // 3. Let values be a new empty List.\n        let values = Gc::new(GcRefCell::new(Vec::new()));\n\n        // 4. Let remainingElementsCount be the Record { [[Value]]: 1 }.\n        let remaining_elements_count = Rc::new(Cell::new(1));\n\n        // 5. Let index be 0.\n        let mut index = 0;\n\n        // 6. For each element key of allKeys, do\n        for key in all_keys {\n            // a. Let desc be ? promises.[[GetOwnProperty]](key).\n            let desc = promises\n                .__get_own_property__(&key, &mut InternalMethodPropertyContext::new(context))?;\n\n            // b. If desc is not undefined and desc.[[Enumerable]] is true, then\n            if let Some(desc) = desc\n                && desc.expect_enumerable()\n            {\n                // i. Let value be ? Get(promises, key).\n                let value = promises.get(key.clone(), context)?;\n\n                // ii. Append key to keys.\n                keys.borrow_mut().push(key);\n\n                // iii. Append undefined to values.\n                values.borrow_mut().push(JsValue::undefined());\n\n                // iv. Let nextPromise be ? Call(promiseResolve, constructor, « value »).\n                let next_promise =\n                    promise_resolve.call(&JsValue::from(constructor.clone()), &[value], context)?;\n\n                // v. Let alreadyCalled be the Record { [[Value]]: false }.\n                let already_called = Rc::new(Cell::new(false));\n\n                // vi. Let onFulfilled be a new Abstract Closure...\n                let on_fulfilled = FunctionObjectBuilder::new(\n                    context.realm(),\n                    NativeFunction::from_copy_closure_with_captures(\n                        |_, args, captures, context| {\n                            // 1. If alreadyCalled.[[Value]] is true, return undefined.\n                            if captures.already_called.get() {\n                                return Ok(JsValue::undefined());\n                            }\n\n                            // 2. Set alreadyCalled.[[Value]] to true.\n                            captures.already_called.set(true);\n\n                            let x = args.get_or_undefined(0).clone();\n\n                            // 3. If variant is all, then\n                            if captures.variant == KeyedVariant::All {\n                                // a. Set values[index] to x.\n                                captures.values.borrow_mut()[captures.index] = x;\n                            } else {\n                                // 4. Else (variant is all-settled)\n                                // a. Let obj be OrdinaryObjectCreate(%Object.prototype%).\n                                let obj = JsObject::with_object_proto(context.intrinsics());\n                                // b. Perform ! CreateDataPropertyOrThrow(obj, \"status\", \"fulfilled\").\n                                obj.create_data_property_or_throw(\n                                    js_string!(\"status\"),\n                                    js_string!(\"fulfilled\"),\n                                    context,\n                                )\n                                .js_expect(\"cannot fail per spec\")?;\n                                // c. Perform ! CreateDataPropertyOrThrow(obj, \"value\", x).\n                                obj.create_data_property_or_throw(js_string!(\"value\"), x, context)\n                                    .js_expect(\"cannot fail per spec\")?;\n                                // d. Set values[index] to obj.\n                                captures.values.borrow_mut()[captures.index] = obj.into();\n                            }\n\n                            // 5. Set remainingElementsCount.[[Value]] to remainingElementsCount.[[Value]] - 1.\n                            captures\n                                .remaining_elements\n                                .set(captures.remaining_elements.get() - 1);\n\n                            // 6. If remainingElementsCount.[[Value]] = 0, then\n                            if captures.remaining_elements.get() == 0 {\n                                // a. Let result be CreateKeyedPromiseCombinatorResultObject(keys, values).\n                                let result = create_keyed_result_object(\n                                    &captures.keys.borrow(),\n                                    &captures.values.borrow(),\n                                    context,\n                                );\n                                // b. Return ? Call(resultCapability.[[Resolve]], undefined, « result »).\n                                return captures.capability.call(\n                                    &JsValue::undefined(),\n                                    &[result.into()],\n                                    context,\n                                );\n                            }\n\n                            // 7. Return undefined.\n                            Ok(JsValue::undefined())\n                        },\n                        KeyedResolveCaptures {\n                            already_called: already_called.clone(),\n                            index,\n                            variant,\n                            keys: keys.clone(),\n                            values: values.clone(),\n                            capability: result_capability.functions.resolve.clone(),\n                            remaining_elements: remaining_elements_count.clone(),\n                        },\n                    ),\n                )\n                .name(\"\")\n                .length(1)\n                .constructor(false)\n                .build();\n\n                // vii-viii. Build onRejected\n                let on_rejected = if variant == KeyedVariant::All {\n                    // If variant is all, let onRejected be resultCapability.[[Reject]].\n                    result_capability.functions.reject.clone().into()\n                } else {\n                    // Else (variant is all-settled), let onRejected be a new Abstract Closure...\n                    let on_rejected_fn = FunctionObjectBuilder::new(\n                        context.realm(),\n                        NativeFunction::from_copy_closure_with_captures(\n                            |_, args, captures, context| {\n                                // 1. If alreadyCalled.[[Value]] is true, return undefined.\n                                if captures.already_called.get() {\n                                    return Ok(JsValue::undefined());\n                                }\n\n                                // 2. Set alreadyCalled.[[Value]] to true.\n                                captures.already_called.set(true);\n\n                                let x = args.get_or_undefined(0).clone();\n\n                                // 3. Let obj be OrdinaryObjectCreate(%Object.prototype%).\n                                let obj = JsObject::with_object_proto(context.intrinsics());\n                                // 4. Perform ! CreateDataPropertyOrThrow(obj, \"status\", \"rejected\").\n                                obj.create_data_property_or_throw(\n                                    js_string!(\"status\"),\n                                    js_string!(\"rejected\"),\n                                    context,\n                                )\n                                .js_expect(\"cannot fail per spec\")?;\n                                // 5. Perform ! CreateDataPropertyOrThrow(obj, \"reason\", x).\n                                obj.create_data_property_or_throw(js_string!(\"reason\"), x, context)\n                                    .js_expect(\"cannot fail per spec\")?;\n                                // 6. Set values[index] to obj.\n                                captures.values.borrow_mut()[captures.index] = obj.into();\n\n                                // 7. Set remainingElementsCount.[[Value]] to remainingElementsCount.[[Value]] - 1.\n                                captures\n                                    .remaining_elements\n                                    .set(captures.remaining_elements.get() - 1);\n\n                                // 8. If remainingElementsCount.[[Value]] = 0, then\n                                if captures.remaining_elements.get() == 0 {\n                                    let result = create_keyed_result_object(\n                                        &captures.keys.borrow(),\n                                        &captures.values.borrow(),\n                                        context,\n                                    );\n                                    return captures.capability.call(\n                                        &JsValue::undefined(),\n                                        &[result.into()],\n                                        context,\n                                    );\n                                }\n\n                                // 9. Return undefined.\n                                Ok(JsValue::undefined())\n                            },\n                            KeyedResolveCaptures {\n                                already_called,\n                                index,\n                                variant,\n                                keys: keys.clone(),\n                                values: values.clone(),\n                                capability: result_capability.functions.resolve.clone(),\n                                remaining_elements: remaining_elements_count.clone(),\n                            },\n                        ),\n                    )\n                    .name(\"\")\n                    .length(1)\n                    .constructor(false)\n                    .build();\n\n                    on_rejected_fn.into()\n                };\n\n                // ix. Set remainingElementsCount.[[Value]] to remainingElementsCount.[[Value]] + 1.\n                remaining_elements_count.set(remaining_elements_count.get() + 1);\n\n                // x. Perform ? Invoke(nextPromise, \"then\", « onFulfilled, onRejected »).\n                next_promise.invoke(\n                    js_string!(\"then\"),\n                    &[on_fulfilled.into(), on_rejected],\n                    context,\n                )?;\n\n                // xi. Set index to index + 1.\n                index += 1;\n            }\n        }\n\n        // 7. Set remainingElementsCount.[[Value]] to remainingElementsCount.[[Value]] - 1.\n        remaining_elements_count.set(remaining_elements_count.get() - 1);\n\n        // 8. If remainingElementsCount.[[Value]] = 0, then\n        if remaining_elements_count.get() == 0 {\n            // a. Let result be CreateKeyedPromiseCombinatorResultObject(keys, values).\n            let result = create_keyed_result_object(&keys.borrow(), &values.borrow(), context);\n            // b. Perform ? Call(resultCapability.[[Resolve]], undefined, « result »).\n            result_capability.functions.resolve.call(\n                &JsValue::undefined(),\n                &[result.into()],\n                context,\n            )?;\n        }\n\n        // 9. Return resultCapability.[[Promise]].\n        Ok(result_capability.promise.clone().into())\n    }\n\n    /// `Promise.any ( iterable )`\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///  - [MDN documentation][mdn]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-promise.any\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/any\n    pub(crate) fn any(\n        this: &JsValue,\n        args: &[JsValue],\n        context: &mut Context,\n    ) -> JsResult<JsValue> {\n        // 1. Let C be the this value.\n        let c = this.as_object().ok_or_else(|| {\n            JsNativeError::typ().with_message(\"Promise.any() called on a non-object\")\n        })?;\n\n        // 2. Let promiseCapability be ? NewPromiseCapability(C).\n        let promise_capability = PromiseCapability::new(&c, context)?;\n\n        // 3. Let promiseResolve be Completion(GetPromiseResolve(C)).\n        let promise_resolve = Self::get_promise_resolve(&c, context);\n\n        // 4. IfAbruptRejectPromise(promiseResolve, promiseCapability).\n        let promise_resolve =\n            if_abrupt_reject_promise!(promise_resolve, promise_capability, context);\n\n        // 5. Let iteratorRecord be Completion(GetIterator(iterable, sync)).\n        let iterator_record = args\n            .get_or_undefined(0)\n            .get_iterator(IteratorHint::Sync, context);\n\n        // 6. IfAbruptRejectPromise(iteratorRecord, promiseCapability).\n        let mut iterator_record =\n            if_abrupt_reject_promise!(iterator_record, promise_capability, context);\n\n        // 7. Let result be Completion(PerformPromiseAny(iteratorRecord, C, promiseCapability, promiseResolve)).\n        let mut result = Self::perform_promise_any(\n            &mut iterator_record,\n            &c,\n            &promise_capability,\n            &promise_resolve,\n            context,\n        )\n        .map(JsValue::from);\n\n        // 8. If result is an abrupt completion, then\n        if result.is_err() {\n            // a. If iteratorRecord.[[Done]] is false, set result to Completion(IteratorClose(iteratorRecord, result)).\n            if !iterator_record.done() {\n                result = iterator_record.close(result, context);\n            }\n\n            // b. IfAbruptRejectPromise(result, promiseCapability).\n            let result = if_abrupt_reject_promise!(result, promise_capability, context);\n\n            return Ok(result);\n        }\n\n        // 9. Return ? result.\n        result\n    }\n\n    /// `PerformPromiseAny ( iteratorRecord, constructor, resultCapability, promiseResolve )`\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-performpromiseany\n    pub(crate) fn perform_promise_any(\n        iterator_record: &mut IteratorRecord,\n        constructor: &JsObject,\n        result_capability: &PromiseCapability,\n        promise_resolve: &JsObject,\n        context: &mut Context,\n    ) -> JsResult<JsObject> {\n        #[derive(Debug, Trace, Finalize)]\n        struct RejectElementCaptures {\n            #[unsafe_ignore_trace]\n            already_called: Rc<Cell<bool>>,\n            index: usize,\n            errors: Gc<GcRefCell<Vec<JsValue>>>,\n            capability_reject: JsFunction,\n            #[unsafe_ignore_trace]\n            remaining_elements_count: Rc<Cell<i32>>,\n        }\n\n        // 1. Let errors be a new empty List.\n        let errors = Gc::new(GcRefCell::new(Vec::new()));\n\n        // 2. Let remainingElementsCount be the Record { [[Value]]: 1 }.\n        let remaining_elements_count = Rc::new(Cell::new(1));\n\n        // 3. Let index be 0.\n        let mut index = 0;\n\n        // 4. Repeat,\n        //     a. Let next be ? IteratorStepValue(iteratorRecord).\n        while let Some(next) = iterator_record.step_value(context)? {\n            // c. Append undefined to errors.\n            errors.borrow_mut().push(JsValue::undefined());\n\n            // d. Let nextPromise be ? Call(promiseResolve, constructor, « next »).\n            let next_promise =\n                promise_resolve.call(&constructor.clone().into(), &[next], context)?;\n\n            // e. Let stepsRejected be the algorithm steps defined in Promise.any Reject Element Functions.\n            // f. Let lengthRejected be the number of non-optional parameters of the function definition in Promise.any Reject Element Functions.\n            // g. Let onRejected be CreateBuiltinFunction(stepsRejected, lengthRejected, \"\", « [[AlreadyCalled]], [[Index]], [[Errors]], [[Capability]], [[RemainingElements]] »).\n            // h. Set onRejected.[[AlreadyCalled]] to false.\n            // i. Set onRejected.[[Index]] to index.\n            // j. Set onRejected.[[Errors]] to errors.\n            // k. Set onRejected.[[Capability]] to resultCapability.\n            // l. Set onRejected.[[RemainingElements]] to remainingElementsCount.\n            let on_rejected = FunctionObjectBuilder::new(\n                context.realm(),\n                NativeFunction::from_copy_closure_with_captures(\n                    |_, args, captures, context| {\n                        // https://tc39.es/ecma262/#sec-promise.any-reject-element-functions\n\n                        // 1. Let F be the active function object.\n\n                        // 2. If F.[[AlreadyCalled]] is true, return undefined.\n                        if captures.already_called.get() {\n                            return Ok(JsValue::undefined());\n                        }\n\n                        // 3. Set F.[[AlreadyCalled]] to true.\n                        captures.already_called.set(true);\n\n                        // 4. Let index be F.[[Index]].\n                        // 5. Let errors be F.[[Errors]].\n                        // 6. Let promiseCapability be F.[[Capability]].\n                        // 7. Let remainingElementsCount be F.[[RemainingElements]].\n\n                        // 8. Set errors[index] to x.\n                        captures.errors.borrow_mut()[captures.index] =\n                            args.get_or_undefined(0).clone();\n\n                        // 9. Set remainingElementsCount.[[Value]] to remainingElementsCount.[[Value]] - 1.\n                        captures\n                            .remaining_elements_count\n                            .set(captures.remaining_elements_count.get() - 1);\n\n                        // 10. If remainingElementsCount.[[Value]] is 0, then\n                        if captures.remaining_elements_count.get() == 0 {\n                            // a. Let error be a newly created AggregateError object.\n                            // b. Perform ! DefinePropertyOrThrow(error, \"errors\", PropertyDescriptor { [[Configurable]]: true, [[Enumerable]]: false, [[Writable]]: true, [[Value]]: CreateArrayFromList(errors) }).\n                            let error = JsNativeError::aggregate(\n                                captures\n                                    .errors\n                                    .borrow()\n                                    .iter()\n                                    .cloned()\n                                    .map(JsError::from_opaque)\n                                    .collect(),\n                            )\n                            .with_message(\"no promise in Promise.any was fulfilled.\");\n\n                            // c. Return ? Call(promiseCapability.[[Reject]], undefined, « error »).\n                            return captures.capability_reject.call(\n                                &JsValue::undefined(),\n                                &[error.into_opaque(context).into()],\n                                context,\n                            );\n                        }\n\n                        // 11. Return undefined.\n                        Ok(JsValue::undefined())\n                    },\n                    RejectElementCaptures {\n                        already_called: Rc::new(Cell::new(false)),\n                        index,\n                        errors: errors.clone(),\n                        capability_reject: result_capability.functions.reject.clone(),\n                        remaining_elements_count: remaining_elements_count.clone(),\n                    },\n                ),\n            )\n            .name(\"\")\n            .length(1)\n            .constructor(false)\n            .build();\n\n            // m. Set remainingElementsCount.[[Value]] to remainingElementsCount.[[Value]] + 1.\n            remaining_elements_count.set(remaining_elements_count.get() + 1);\n\n            // n. Perform ? Invoke(nextPromise, \"then\", « resultCapability.[[Resolve]], onRejected »).\n            next_promise.invoke(\n                js_string!(\"then\"),\n                &[\n                    result_capability.functions.resolve.clone().into(),\n                    on_rejected.into(),\n                ],\n                context,\n            )?;\n\n            // o. Set index to index + 1.\n            index += 1;\n        }\n\n        //     b. If next is done, then\n        //         i. Set remainingElementsCount.[[Value]] to remainingElementsCount.[[Value]] - 1.\n        remaining_elements_count.set(remaining_elements_count.get() - 1);\n        //         ii. If remainingElementsCount.[[Value]] = 0, then\n        if remaining_elements_count.get() == 0 {\n            // 1. Let error be a newly created AggregateError object.\n            let error = JsNativeError::aggregate(\n                errors\n                    .borrow()\n                    .iter()\n                    .cloned()\n                    .map(JsError::from_opaque)\n                    .collect(),\n            )\n            .with_message(\"no promise in Promise.any was fulfilled.\");\n\n            // 2. Perform ! DefinePropertyOrThrow(error, \"errors\", PropertyDescriptor { [[Configurable]]: true, [[Enumerable]]: false, [[Writable]]: true, [[Value]]: CreateArrayFromList(errors) }).\n            // 3. Return ThrowCompletion(error).\n            return Err(error.into());\n        }\n\n        //         iii. Return resultCapability.[[Promise]].\n        Ok(result_capability.promise.clone())\n    }\n\n    /// `Promise.race ( iterable )`\n    ///\n    /// The `race` function returns a new promise which is settled in the same way as the first\n    /// passed promise to settle. It resolves all elements of the passed `iterable` to promises.\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///  - [MDN documentation][mdn]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-promise.race\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/race\n    pub(crate) fn race(\n        this: &JsValue,\n        args: &[JsValue],\n        context: &mut Context,\n    ) -> JsResult<JsValue> {\n        let iterable = args.get_or_undefined(0);\n\n        // 1. Let C be the this value.\n        let c = this.as_object().ok_or_else(|| {\n            JsNativeError::typ().with_message(\"Promise.race() called on a non-object\")\n        })?;\n\n        // 2. Let promiseCapability be ? NewPromiseCapability(C).\n        let promise_capability = PromiseCapability::new(&c, context)?;\n\n        // 3. Let promiseResolve be Completion(GetPromiseResolve(C)).\n        let promise_resolve = Self::get_promise_resolve(&c, context);\n\n        // 4. IfAbruptRejectPromise(promiseResolve, promiseCapability).\n        let promise_resolve =\n            if_abrupt_reject_promise!(promise_resolve, promise_capability, context);\n\n        // 5. Let iteratorRecord be Completion(GetIterator(iterable, sync)).\n        let iterator_record = iterable.get_iterator(IteratorHint::Sync, context);\n\n        // 6. IfAbruptRejectPromise(iteratorRecord, promiseCapability).\n        let mut iterator_record =\n            if_abrupt_reject_promise!(iterator_record, promise_capability, context);\n\n        // 7. Let result be Completion(PerformPromiseRace(iteratorRecord, C, promiseCapability, promiseResolve)).\n        let mut result = Self::perform_promise_race(\n            &mut iterator_record,\n            &c,\n            &promise_capability,\n            &promise_resolve,\n            context,\n        )\n        .map(JsValue::from);\n\n        // 8. If result is an abrupt completion, then\n        if result.is_err() {\n            // a. If iteratorRecord.[[Done]] is false, set result to Completion(IteratorClose(iteratorRecord, result)).\n            if !iterator_record.done() {\n                result = iterator_record.close(result, context);\n            }\n\n            // b. IfAbruptRejectPromise(result, promiseCapability).\n            let result = if_abrupt_reject_promise!(result, promise_capability, context);\n\n            Ok(result)\n        } else {\n            // 9. Return ? result.\n            result\n        }\n    }\n\n    /// `PerformPromiseRace ( iteratorRecord, constructor, resultCapability, promiseResolve )`\n    ///\n    /// The abstract operation `PerformPromiseRace` takes arguments `iteratorRecord`, `constructor`\n    /// (a constructor), `resultCapability` (a [`PromiseCapability`] Record), and `promiseResolve`\n    /// (a function object) and returns either a normal completion containing an ECMAScript\n    /// language value or a throw completion.\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-performpromiserace\n    pub(crate) fn perform_promise_race(\n        iterator_record: &mut IteratorRecord,\n        constructor: &JsObject,\n        result_capability: &PromiseCapability,\n        promise_resolve: &JsObject,\n        context: &mut Context,\n    ) -> JsResult<JsObject> {\n        let constructor = constructor.clone().into();\n\n        // 1. Repeat,\n        //     a. Let next be ? IteratorStepValue(iteratorRecord).\n        while let Some(next) = iterator_record.step_value(context)? {\n            // c. Let nextPromise be ? Call(promiseResolve, constructor, « next »).\n            let next_promise = promise_resolve.call(&constructor, &[next], context)?;\n            // d. Perform ? Invoke(nextPromise, \"then\", « resultCapability.[[Resolve]], resultCapability.[[Reject]] »).\n            next_promise.invoke(\n                js_string!(\"then\"),\n                &[\n                    result_capability.functions.resolve.clone().into(),\n                    result_capability.functions.reject.clone().into(),\n                ],\n                context,\n            )?;\n        }\n\n        //     b. If next is done, then\n        //         i. Return resultCapability.[[Promise]].\n        Ok(result_capability.promise.clone())\n    }\n\n    /// `Promise.reject ( r )`\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///  - [MDN documentation][mdn]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-promise.reject\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/reject\n    pub(crate) fn reject(\n        this: &JsValue,\n        args: &[JsValue],\n        context: &mut Context,\n    ) -> JsResult<JsValue> {\n        let r = args.get_or_undefined(0).clone();\n\n        // 1. Let C be the this value.\n        let c = this.as_object().ok_or_else(|| {\n            JsNativeError::typ().with_message(\"Promise.reject() called on a non-object\")\n        })?;\n\n        Self::promise_reject(&c, JsError::from_opaque(r), context).map(JsValue::from)\n    }\n\n    /// Utility function to create a rejected promise.\n    pub(crate) fn promise_reject(\n        c: &JsObject,\n        e: JsError,\n        context: &mut Context,\n    ) -> JsResult<JsObject> {\n        let e = e.into_opaque(context)?;\n\n        // 2. Let promiseCapability be ? NewPromiseCapability(C).\n        let promise_capability = PromiseCapability::new(c, context)?;\n\n        // 3. Perform ? Call(promiseCapability.[[Reject]], undefined, « r »).\n        promise_capability\n            .functions\n            .reject\n            .call(&JsValue::undefined(), &[e], context)?;\n\n        // 4. Return promiseCapability.[[Promise]].\n        Ok(promise_capability.promise.clone())\n    }\n\n    /// `Promise.resolve ( x )`\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///  - [MDN documentation][mdn]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-promise.resolve\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/resolve\n    pub(crate) fn resolve(\n        this: &JsValue,\n        args: &[JsValue],\n        context: &mut Context,\n    ) -> JsResult<JsValue> {\n        let x = args.get_or_undefined(0);\n\n        // 1. Let C be the this value.\n        // 2. If Type(C) is not Object, throw a TypeError exception.\n        let c = this.as_object().ok_or_else(|| {\n            JsNativeError::typ().with_message(\"Promise.resolve() called on a non-object\")\n        })?;\n\n        // 3. Return ? PromiseResolve(C, x).\n        Self::promise_resolve(&c, x.clone(), context).map(JsValue::from)\n    }\n\n    /// `PromiseResolve ( C, x )`\n    ///\n    /// The abstract operation `PromiseResolve` takes arguments `C` (a constructor) and `x` (an\n    /// ECMAScript language value) and returns either a normal completion containing an ECMAScript\n    /// language value or a throw completion. It returns a new promise resolved with `x`.\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-promise-resolve\n    pub(crate) fn promise_resolve(\n        c: &JsObject,\n        x: JsValue,\n        context: &mut Context,\n    ) -> JsResult<JsObject> {\n        // 1. If IsPromise(x) is true, then\n        if let Some(x) = x.as_promise_object() {\n            let x = x.upcast();\n            // a. Let xConstructor be ? Get(x, \"constructor\").\n            let x_constructor = x.get(CONSTRUCTOR, context)?;\n            // b. If SameValue(xConstructor, C) is true, return x.\n            if x_constructor\n                .as_object()\n                .is_some_and(|o| JsObject::equals(&o, c))\n            {\n                return Ok(x);\n            }\n        }\n\n        // 2. Let promiseCapability be ? NewPromiseCapability(C).\n        let promise_capability = PromiseCapability::new(&c.clone(), context)?;\n\n        // 3. Perform ? Call(promiseCapability.[[Resolve]], undefined, « x »).\n        promise_capability\n            .functions\n            .resolve\n            .call(&JsValue::undefined(), &[x], context)?;\n\n        // 4. Return promiseCapability.[[Promise]].\n        Ok(promise_capability.promise.clone())\n    }\n\n    /// `get Promise [ @@species ]`\n    ///\n    /// The `Promise [ @@species ]` accessor property returns the Promise constructor.\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///  - [MDN documentation][mdn]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-get-promise-@@species\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/@@species\n    #[allow(clippy::unnecessary_wraps)]\n    fn get_species(this: &JsValue, _: &[JsValue], _: &mut Context) -> JsResult<JsValue> {\n        // 1. Return the this value.\n        Ok(this.clone())\n    }\n\n    /// `Promise.prototype.catch ( onRejected )`\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///  - [MDN documentation][mdn]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-promise.prototype.catch\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/catch\n    pub(crate) fn catch(\n        this: &JsValue,\n        args: &[JsValue],\n        context: &mut Context,\n    ) -> JsResult<JsValue> {\n        let on_rejected = args.get_or_undefined(0);\n\n        // 1. Let promise be the this value.\n        let promise = this;\n        // 2. Return ? Invoke(promise, \"then\", « undefined, onRejected »).\n        promise.invoke(\n            js_string!(\"then\"),\n            &[JsValue::undefined(), on_rejected.clone()],\n            context,\n        )\n    }\n\n    /// `Promise.prototype.finally ( onFinally )`\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///  - [MDN documentation][mdn]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-promise.prototype.finally\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/finally\n    pub(crate) fn finally(\n        this: &JsValue,\n        args: &[JsValue],\n        context: &mut Context,\n    ) -> JsResult<JsValue> {\n        // 1. Let promise be the this value.\n        let promise = this;\n\n        // 2. If Type(promise) is not Object, throw a TypeError exception.\n        let Some(promise) = promise.as_object() else {\n            return Err(JsNativeError::typ()\n                .with_message(\"finally called with a non-object promise\")\n                .into());\n        };\n\n        // 3. Let C be ? SpeciesConstructor(promise, %Promise%).\n        let c = promise.species_constructor(StandardConstructors::promise, context)?;\n\n        // 4. Assert: IsConstructor(C) is true.\n        debug_assert!(c.is_constructor());\n\n        let on_finally = args.get_or_undefined(0);\n\n        let Some(on_finally) = on_finally.as_object().and_then(JsFunction::from_object) else {\n            // 5. If IsCallable(onFinally) is false, then\n            //    a. Let thenFinally be onFinally.\n            //    b. Let catchFinally be onFinally.\n            // 7. Return ? Invoke(promise, \"then\", « thenFinally, catchFinally »).\n            let then = promise.get(js_string!(\"then\"), context)?;\n            return then.call(this, &[on_finally.clone(), on_finally.clone()], context);\n        };\n\n        let (then_finally, catch_finally) =\n            Self::then_catch_finally_closures(c, on_finally, context);\n\n        // 7. Return ? Invoke(promise, \"then\", « thenFinally, catchFinally »).\n        let then = promise.get(js_string!(\"then\"), context)?;\n        then.call(this, &[then_finally.into(), catch_finally.into()], context)\n    }\n\n    pub(crate) fn then_catch_finally_closures(\n        c: JsObject,\n        on_finally: JsFunction,\n        context: &mut Context,\n    ) -> (JsFunction, JsFunction) {\n        /// Capture object for the `thenFinallyClosure` abstract closure.\n        #[derive(Debug, Trace, Finalize)]\n        struct FinallyCaptures {\n            on_finally: JsFunction,\n            c: JsObject,\n        }\n\n        // a. Let thenFinallyClosure be a new Abstract Closure with parameters (value) that captures onFinally and C and performs the following steps when called:\n        let then_finally_closure = FunctionObjectBuilder::new(\n            context.realm(),\n            NativeFunction::from_copy_closure_with_captures(\n                |_this, args, captures, context| {\n                    /// Capture object for the abstract `returnValue` closure.\n                    #[derive(Debug, Trace, Finalize)]\n                    struct ReturnValueCaptures {\n                        value: JsValue,\n                    }\n\n                    let value = args.get_or_undefined(0);\n\n                    // i. Let result be ? Call(onFinally, undefined).\n                    let result = captures\n                        .on_finally\n                        .call(&JsValue::undefined(), &[], context)?;\n\n                    // ii. Let promise be ? PromiseResolve(C, result).\n                    let promise = Self::promise_resolve(&captures.c, result, context)?;\n\n                    // iii. Let returnValue be a new Abstract Closure with no parameters that captures value and performs the following steps when called:\n                    let return_value = FunctionObjectBuilder::new(\n                        context.realm(),\n                        NativeFunction::from_copy_closure_with_captures(\n                            |_this, _args, captures, _context| {\n                                // 1. Return value.\n                                Ok(captures.value.clone())\n                            },\n                            ReturnValueCaptures {\n                                value: value.clone(),\n                            },\n                        ),\n                    );\n\n                    // iv. Let valueThunk be CreateBuiltinFunction(returnValue, 0, \"\", « »).\n                    let value_thunk = return_value.length(0).name(\"\").build();\n\n                    // v. Return ? Invoke(promise, \"then\", « valueThunk »).\n                    promise.invoke(js_string!(\"then\"), &[value_thunk.into()], context)\n                },\n                FinallyCaptures {\n                    on_finally: on_finally.clone(),\n                    c: c.clone(),\n                },\n            ),\n        );\n\n        // b. Let thenFinally be CreateBuiltinFunction(thenFinallyClosure, 1, \"\", « »).\n        let then_finally = then_finally_closure.length(1).name(\"\").build();\n\n        // c. Let catchFinallyClosure be a new Abstract Closure with parameters (reason) that captures onFinally and C and performs the following steps when called:\n        let catch_finally_closure = FunctionObjectBuilder::new(\n            context.realm(),\n            NativeFunction::from_copy_closure_with_captures(\n                |_this, args, captures, context| {\n                    /// Capture object for the abstract `throwReason` closure.\n                    #[derive(Debug, Trace, Finalize)]\n                    struct ThrowReasonCaptures {\n                        reason: JsValue,\n                    }\n\n                    let reason = args.get_or_undefined(0);\n\n                    // i. Let result be ? Call(onFinally, undefined).\n                    let result = captures\n                        .on_finally\n                        .call(&JsValue::undefined(), &[], context)?;\n\n                    // ii. Let promise be ? PromiseResolve(C, result).\n                    let promise = Self::promise_resolve(&captures.c, result, context)?;\n\n                    // iii. Let throwReason be a new Abstract Closure with no parameters that captures reason and performs the following steps when called:\n                    let throw_reason = FunctionObjectBuilder::new(\n                        context.realm(),\n                        NativeFunction::from_copy_closure_with_captures(\n                            |_this, _args, captures, _context| {\n                                // 1. Return ThrowCompletion(reason).\n                                Err(JsError::from_opaque(captures.reason.clone()))\n                            },\n                            ThrowReasonCaptures {\n                                reason: reason.clone(),\n                            },\n                        ),\n                    );\n\n                    // iv. Let thrower be CreateBuiltinFunction(throwReason, 0, \"\", « »).\n                    let thrower = throw_reason.length(0).name(\"\").build();\n\n                    // v. Return ? Invoke(promise, \"then\", « thrower »).\n                    promise.invoke(js_string!(\"then\"), &[thrower.into()], context)\n                },\n                FinallyCaptures { on_finally, c },\n            ),\n        );\n\n        // d. Let catchFinally be CreateBuiltinFunction(catchFinallyClosure, 1, \"\", « »).\n        let catch_finally = catch_finally_closure.length(1).name(\"\").build();\n\n        (then_finally, catch_finally)\n    }\n\n    /// `Promise.prototype.then ( onFulfilled, onRejected )`\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///  - [MDN documentation][mdn]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-promise.prototype.then\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/then\n    pub(crate) fn then(\n        this: &JsValue,\n        args: &[JsValue],\n        context: &mut Context,\n    ) -> JsResult<JsValue> {\n        // 1. Let promise be the this value.\n        let promise = this;\n\n        // 2. If IsPromise(promise) is false, throw a TypeError exception.\n        let promise = promise.as_promise_object().ok_or_else(|| {\n            JsNativeError::typ().with_message(\"Promise.prototype.then: this is not a promise\")\n        })?;\n\n        let on_fulfilled = args\n            .get_or_undefined(0)\n            .as_object()\n            .and_then(JsFunction::from_object);\n        let on_rejected = args\n            .get_or_undefined(1)\n            .as_object()\n            .and_then(JsFunction::from_object);\n\n        // continues in `Promise::inner_then`\n        Self::inner_then(&promise, on_fulfilled, on_rejected, context).map(JsValue::from)\n    }\n\n    /// Schedules callback functions for the eventual completion of `promise` — either fulfillment\n    /// or rejection.\n    pub(crate) fn inner_then(\n        promise: &JsObject<Promise>,\n        on_fulfilled: Option<JsFunction>,\n        on_rejected: Option<JsFunction>,\n        context: &mut Context,\n    ) -> JsResult<JsObject> {\n        // 3. Let C be ? SpeciesConstructor(promise, %Promise%).\n        let c = promise\n            .clone()\n            .upcast()\n            .species_constructor(StandardConstructors::promise, context)?;\n\n        // 4. Let resultCapability be ? NewPromiseCapability(C).\n        let result_capability = PromiseCapability::new(&c, context)?;\n        let result_promise = result_capability.promise.clone();\n\n        // 5. Return PerformPromiseThen(promise, onFulfilled, onRejected, resultCapability).\n        Self::perform_promise_then(\n            promise,\n            on_fulfilled,\n            on_rejected,\n            Some(result_capability),\n            context,\n        );\n\n        Ok(result_promise)\n    }\n\n    /// `PerformPromiseThen ( promise, onFulfilled, onRejected [ , resultCapability ] )`\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-performpromisethen\n    pub(crate) fn perform_promise_then(\n        promise: &JsObject<Promise>,\n        on_fulfilled: Option<JsFunction>,\n        on_rejected: Option<JsFunction>,\n        result_capability: Option<PromiseCapability>,\n        context: &mut Context,\n    ) {\n        // 1. Assert: IsPromise(promise) is true.\n\n        // 2. If resultCapability is not present, then\n        //   a. Set resultCapability to undefined.\n\n        // 3. If IsCallable(onFulfilled) is false, then\n        //   a. Let onFulfilledJobCallback be empty.\n        // Argument already asserts this.\n        let on_fulfilled_job_callback = on_fulfilled\n            // 4. Else,\n            //   a. Let onFulfilledJobCallback be HostMakeJobCallback(onFulfilled).\n            .map(|f| context.host_hooks().make_job_callback(f, context));\n\n        // 5. If IsCallable(onRejected) is false, then\n        //   a. Let onRejectedJobCallback be empty.\n        // Argument already asserts this.\n        let on_rejected_job_callback = on_rejected\n            // 6. Else,\n            //   a. Let onRejectedJobCallback be HostMakeJobCallback(onRejected).\n            .map(|f| context.host_hooks().make_job_callback(f, context));\n\n        // 7. Let fulfillReaction be the PromiseReaction { [[Capability]]: resultCapability, [[Type]]: Fulfill, [[Handler]]: onFulfilledJobCallback }.\n        let fulfill_reaction = ReactionRecord {\n            promise_capability: result_capability.clone(),\n            reaction_type: ReactionType::Fulfill,\n            handler: on_fulfilled_job_callback,\n        };\n\n        // 8. Let rejectReaction be the PromiseReaction { [[Capability]]: resultCapability, [[Type]]: Reject, [[Handler]]: onRejectedJobCallback }.\n        let reject_reaction = ReactionRecord {\n            promise_capability: result_capability,\n            reaction_type: ReactionType::Reject,\n            handler: on_rejected_job_callback,\n        };\n\n        let (state, handled) = {\n            let promise = promise.borrow_mut();\n            let promise = promise.data();\n            (promise.state.clone(), promise.handled)\n        };\n\n        match state {\n            // 9. If promise.[[PromiseState]] is pending, then\n            PromiseState::Pending => {\n                let mut promise = promise.borrow_mut();\n                let promise = promise.data_mut();\n                //   a. Append fulfillReaction as the last element of the List that is promise.[[PromiseFulfillReactions]].\n                promise.fulfill_reactions.push(fulfill_reaction);\n\n                //   b. Append rejectReaction as the last element of the List that is promise.[[PromiseRejectReactions]].\n                promise.reject_reactions.push(reject_reaction);\n            }\n\n            // 10. Else if promise.[[PromiseState]] is fulfilled, then\n            //   a. Let value be promise.[[PromiseResult]].\n            PromiseState::Fulfilled(ref value) => {\n                //   b. Let fulfillJob be NewPromiseReactionJob(fulfillReaction, value).\n                let fulfill_job =\n                    new_promise_reaction_job(fulfill_reaction, value.clone(), context);\n\n                //   c. Perform HostEnqueuePromiseJob(fulfillJob.[[Job]], fulfillJob.[[Realm]]).\n                context\n                    .job_executor()\n                    .enqueue_job(fulfill_job.into(), context);\n            }\n\n            // 11. Else,\n            //   a. Assert: The value of promise.[[PromiseState]] is rejected.\n            //   b. Let reason be promise.[[PromiseResult]].\n            PromiseState::Rejected(ref reason) => {\n                //   c. If promise.[[PromiseIsHandled]] is false, perform HostPromiseRejectionTracker(promise, \"handle\").\n                if !handled {\n                    context.host_hooks().promise_rejection_tracker(\n                        promise,\n                        OperationType::Handle,\n                        context,\n                    );\n                }\n\n                //   d. Let rejectJob be NewPromiseReactionJob(rejectReaction, reason).\n                let reject_job = new_promise_reaction_job(reject_reaction, reason.clone(), context);\n\n                //   e. Perform HostEnqueuePromiseJob(rejectJob.[[Job]], rejectJob.[[Realm]]).\n                context\n                    .job_executor()\n                    .enqueue_job(reject_job.into(), context);\n            }\n        }\n\n        // 12. Set promise.[[PromiseIsHandled]] to true.\n        promise.borrow_mut().data_mut().handled = true;\n\n        // 13. If resultCapability is undefined, then\n        //   a. Return undefined.\n        // 14. Else,\n        //   a. Return resultCapability.[[Promise]].\n        // skipped because we can already access the promise from `result_capability`\n    }\n\n    /// `GetPromiseResolve ( promiseConstructor )`\n    ///\n    /// The abstract operation `GetPromiseResolve` takes argument `promiseConstructor` (a\n    /// constructor) and returns either a normal completion containing a function object or a throw\n    /// completion.\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-getpromiseresolve\n    pub(crate) fn get_promise_resolve(\n        promise_constructor: &JsObject,\n        context: &mut Context,\n    ) -> JsResult<JsObject> {\n        // 1. Let promiseResolve be ? Get(promiseConstructor, \"resolve\").\n        let promise_resolve = promise_constructor.get(js_string!(\"resolve\"), context)?;\n\n        // 2. If IsCallable(promiseResolve) is false, throw a TypeError exception.\n        promise_resolve.as_callable().ok_or_else(|| {\n            JsNativeError::typ()\n                .with_message(\"retrieving a non-callable promise resolver\")\n                .into()\n        })\n    }\n\n    /// `CreateResolvingFunctions ( promise )`\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-createresolvingfunctions\n    pub(crate) fn create_resolving_functions(\n        promise: &JsObject<Promise>,\n        context: &mut Context,\n    ) -> ResolvingFunctions {\n        /// `TriggerPromiseReactions ( reactions, argument )`\n        ///\n        /// The abstract operation `TriggerPromiseReactions` takes arguments `reactions` (a `List` of\n        /// `PromiseReaction` Records) and `argument` and returns unused. It enqueues a new `Job` for\n        /// each record in `reactions`. Each such `Job` processes the `[[Type]]` and `[[Handler]]` of\n        /// the `PromiseReaction` Record, and if the `[[Handler]]` is not `empty`, calls it passing the\n        /// given argument. If the `[[Handler]]` is `empty`, the behaviour is determined by the\n        /// `[[Type]]`.\n        ///\n        /// More information:\n        ///  - [ECMAScript reference][spec]\n        ///\n        /// [spec]: https://tc39.es/ecma262/#sec-triggerpromisereactions\n        fn trigger_promise_reactions(\n            reactions: Vec<ReactionRecord>,\n            argument: &JsValue,\n            context: &mut Context,\n        ) {\n            // 1. For each element reaction of reactions, do\n            for reaction in reactions {\n                // a. Let job be NewPromiseReactionJob(reaction, argument).\n                let job = new_promise_reaction_job(reaction, argument.clone(), context);\n\n                // b. Perform HostEnqueuePromiseJob(job.[[Job]], job.[[Realm]]).\n                context.job_executor().enqueue_job(job.into(), context);\n            }\n            // 2. Return unused.\n        }\n\n        /// `FulfillPromise ( promise, value )`\n        ///\n        /// The abstract operation `FulfillPromise` takes arguments `promise` and `value` and returns\n        /// `unused`.\n        ///\n        /// More information:\n        ///  - [ECMAScript reference][spec]\n        ///\n        /// [spec]: https://tc39.es/ecma262/#sec-fulfillpromise\n        ///\n        /// # Panics\n        ///\n        /// Panics if `Promise` is not pending.\n        fn fulfill_promise(promise: &JsObject<Promise>, value: JsValue, context: &mut Context) {\n            let mut promise = promise.borrow_mut();\n            let promise = promise.data_mut();\n\n            // 1. Assert: The value of promise.[[PromiseState]] is pending.\n            assert!(\n                matches!(promise.state, PromiseState::Pending),\n                \"promise was not pending\"\n            );\n\n            // reordering these statements does not affect the semantics\n\n            // 2. Let reactions be promise.[[PromiseFulfillReactions]].\n            // 4. Set promise.[[PromiseFulfillReactions]] to undefined.\n            let reactions = std::mem::take(&mut promise.fulfill_reactions);\n\n            // 5. Set promise.[[PromiseRejectReactions]] to undefined.\n            promise.reject_reactions.clear();\n\n            // 7. Perform TriggerPromiseReactions(reactions, value).\n            trigger_promise_reactions(reactions, &value, context);\n\n            // 3. Set promise.[[PromiseResult]] to value.\n            // 6. Set promise.[[PromiseState]] to fulfilled.\n            promise.state = PromiseState::Fulfilled(value);\n\n            // 8. Return unused.\n        }\n\n        /// `RejectPromise ( promise, reason )`\n        ///\n        /// The abstract operation `RejectPromise` takes arguments `promise` and `reason` and returns\n        /// `unused`.\n        ///\n        /// More information:\n        ///  - [ECMAScript reference][spec]\n        ///\n        /// [spec]: https://tc39.es/ecma262/#sec-rejectpromise\n        ///\n        /// # Panics\n        ///\n        /// Panics if `Promise` is not pending.\n        fn reject_promise(promise: &JsObject<Promise>, reason: JsValue, context: &mut Context) {\n            let handled = {\n                let mut promise = promise.borrow_mut();\n                let promise = promise.data_mut();\n\n                // 1. Assert: The value of promise.[[PromiseState]] is pending.\n                assert!(\n                    matches!(promise.state, PromiseState::Pending),\n                    \"Expected promise.[[PromiseState]] to be pending\"\n                );\n\n                // reordering these statements does not affect the semantics\n\n                // 2. Let reactions be promise.[[PromiseRejectReactions]].\n                // 5. Set promise.[[PromiseRejectReactions]] to undefined.\n                let reactions = std::mem::take(&mut promise.reject_reactions);\n\n                // 4. Set promise.[[PromiseFulfillReactions]] to undefined.\n                promise.fulfill_reactions.clear();\n\n                // 8. Perform TriggerPromiseReactions(reactions, reason).\n                trigger_promise_reactions(reactions, &reason, context);\n\n                // 3. Set promise.[[PromiseResult]] to reason.\n                // 6. Set promise.[[PromiseState]] to rejected.\n                promise.state = PromiseState::Rejected(reason);\n\n                promise.handled\n            };\n\n            // 7. If promise.[[PromiseIsHandled]] is false, perform HostPromiseRejectionTracker(promise, \"reject\").\n            if !handled {\n                context.host_hooks().promise_rejection_tracker(\n                    promise,\n                    OperationType::Reject,\n                    context,\n                );\n            }\n\n            // 9. Return unused.\n        }\n\n        // 1. Let alreadyResolved be the Record { [[Value]]: false }.\n        // 5. Set resolve.[[Promise]] to promise.\n        // 6. Set resolve.[[AlreadyResolved]] to alreadyResolved.\n        let promise = Gc::new(Cell::new(Some(promise.clone())));\n\n        // 2. Let stepsResolve be the algorithm steps defined in Promise Resolve Functions.\n        // 3. Let lengthResolve be the number of non-optional parameters of the function definition in Promise Resolve Functions.\n        // 4. Let resolve be CreateBuiltinFunction(stepsResolve, lengthResolve, \"\", « [[Promise]], [[AlreadyResolved]] »).\n        let resolve = FunctionObjectBuilder::new(\n            context.realm(),\n            NativeFunction::from_copy_closure_with_captures(\n                |_this, args, captures, context| {\n                    // https://tc39.es/ecma262/#sec-promise-resolve-functions\n\n                    // 1. Let F be the active function object.\n                    // 2. Assert: F has a [[Promise]] internal slot whose value is an Object.\n                    // 3. Let promise be F.[[Promise]].\n                    // 4. Let alreadyResolved be F.[[AlreadyResolved]].\n                    // 5. If alreadyResolved.[[Value]] is true, return undefined.\n                    // 6. Set alreadyResolved.[[Value]] to true.\n                    let Some(promise) = captures.take() else {\n                        return Ok(JsValue::undefined());\n                    };\n\n                    let resolution = args.get_or_undefined(0);\n\n                    // 7. If SameValue(resolution, promise) is true, then\n                    if JsValue::same_value(resolution, &promise.clone().into()) {\n                        //   a. Let selfResolutionError be a newly created TypeError object.\n                        let self_resolution_error = JsNativeError::typ()\n                            .with_message(\"SameValue(resolution, promise) is true\")\n                            .into_opaque(context);\n\n                        //   b. Perform RejectPromise(promise, selfResolutionError).\n                        reject_promise(&promise, self_resolution_error.into(), context);\n\n                        //   c. Return undefined.\n                        return Ok(JsValue::undefined());\n                    }\n\n                    let Some(then) = resolution.as_object() else {\n                        // 8. If Type(resolution) is not Object, then\n                        //   a. Perform FulfillPromise(promise, resolution).\n                        fulfill_promise(&promise, resolution.clone(), context);\n\n                        //   b. Return undefined.\n                        return Ok(JsValue::undefined());\n                    };\n\n                    // 9. Let then be Completion(Get(resolution, \"then\")).\n                    let then_action = match then.get(js_string!(\"then\"), context) {\n                        // 10. If then is an abrupt completion, then\n                        Err(e) => {\n                            //   a. Perform RejectPromise(promise, then.[[Value]]).\n                            reject_promise(&promise, e.into_opaque(context)?, context);\n\n                            //   b. Return undefined.\n                            return Ok(JsValue::undefined());\n                        }\n                        // 11. Let thenAction be then.[[Value]].\n                        Ok(then) => then,\n                    };\n\n                    // 12. If IsCallable(thenAction) is false, then\n                    let Some(then_action) =\n                        then_action.as_object().and_then(JsFunction::from_object)\n                    else {\n                        // a. Perform FulfillPromise(promise, resolution).\n                        fulfill_promise(&promise, resolution.clone(), context);\n\n                        //   b. Return undefined.\n                        return Ok(JsValue::undefined());\n                    };\n\n                    // 13. Let thenJobCallback be HostMakeJobCallback(thenAction).\n                    let then_job_callback =\n                        context.host_hooks().make_job_callback(then_action, context);\n\n                    // 14. Let job be NewPromiseResolveThenableJob(promise, resolution, thenJobCallback).\n                    let job = new_promise_resolve_thenable_job(\n                        promise.clone(),\n                        resolution.clone(),\n                        then_job_callback,\n                        context,\n                    );\n\n                    // 15. Perform HostEnqueuePromiseJob(job.[[Job]], job.[[Realm]]).\n                    context.job_executor().enqueue_job(job.into(), context);\n\n                    // 16. Return undefined.\n                    Ok(JsValue::undefined())\n                },\n                promise.clone(),\n            ),\n        )\n        .name(\"\")\n        .length(1)\n        .constructor(false)\n        .build();\n\n        // 10. Set reject.[[Promise]] to promise.\n        // 11. Set reject.[[AlreadyResolved]] to alreadyResolved.\n        // 7. Let stepsReject be the algorithm steps defined in Promise Reject Functions.\n        // 8. Let lengthReject be the number of non-optional parameters of the function definition in Promise Reject Functions.\n        // 9. Let reject be CreateBuiltinFunction(stepsReject, lengthReject, \"\", « [[Promise]], [[AlreadyResolved]] »).\n        let reject = FunctionObjectBuilder::new(\n            context.realm(),\n            NativeFunction::from_copy_closure_with_captures(\n                |_this, args, captures, context| {\n                    // https://tc39.es/ecma262/#sec-promise-reject-functions\n\n                    // 1. Let F be the active function object.\n                    // 2. Assert: F has a [[Promise]] internal slot whose value is an Object.\n                    // 3. Let promise be F.[[Promise]].\n                    // 4. Let alreadyResolved be F.[[AlreadyResolved]].\n                    // 5. If alreadyResolved.[[Value]] is true, return undefined.\n                    // 6. Set alreadyResolved.[[Value]] to true.\n                    let Some(promise) = captures.take() else {\n                        return Ok(JsValue::undefined());\n                    };\n\n                    // 7. Perform RejectPromise(promise, reason).\n                    reject_promise(&promise, args.get_or_undefined(0).clone(), context);\n\n                    // 8. Return undefined.\n                    Ok(JsValue::undefined())\n                },\n                promise,\n            ),\n        )\n        .name(\"\")\n        .length(1)\n        .constructor(false)\n        .build();\n\n        // 12. Return the Record { [[Resolve]]: resolve, [[Reject]]: reject }.\n        ResolvingFunctions { resolve, reject }\n    }\n}\n\n/// More information:\n///  - [ECMAScript reference][spec]\n///\n/// [spec]: https://tc39.es/ecma262/#sec-newpromisereactionjob\nfn new_promise_reaction_job(\n    mut reaction: ReactionRecord,\n    argument: JsValue,\n    context: &mut Context,\n) -> PromiseJob {\n    // Inverting order since `job` captures `reaction` by value.\n\n    // 2. Let handlerRealm be null.\n    // 3. If reaction.[[Handler]] is not empty, then\n    //   a. Let getHandlerRealmResult be Completion(GetFunctionRealm(reaction.[[Handler]].[[Callback]])).\n    //   b. If getHandlerRealmResult is a normal completion, set handlerRealm to getHandlerRealmResult.[[Value]].\n    //   c. Else, set handlerRealm to the current Realm Record.\n    //   d. NOTE: handlerRealm is never null unless the handler is undefined. When the handler is a\n    // revoked Proxy and no ECMAScript code runs, handlerRealm is used to create error objects.\n    let realm = reaction\n        .handler\n        .as_ref()\n        .and_then(|handler| handler.callback().get_function_realm(context).ok())\n        .unwrap_or_else(|| context.realm().clone());\n\n    // 1. Let job be a new Job Abstract Closure with no parameters that captures reaction and argument and performs the following steps when called:\n    let job = move |context: &mut Context| {\n        //   a. Let promiseCapability be reaction.[[Capability]].\n        let promise_capability = reaction.promise_capability.take();\n        //   b. Let type be reaction.[[Type]].\n        let reaction_type = reaction.reaction_type;\n        //   c. Let handler be reaction.[[Handler]].\n        let handler = reaction.handler.take();\n\n        let handler_result = match handler {\n            // d. If handler is empty, then\n            None => match reaction_type {\n                // i. If type is Fulfill, let handlerResult be NormalCompletion(argument).\n                ReactionType::Fulfill => Ok(argument.clone()),\n                // ii. Else,\n                //   1. Assert: type is Reject.\n                ReactionType::Reject => {\n                    // 2. Let handlerResult be ThrowCompletion(argument).\n                    Err(argument.clone())\n                }\n            },\n            //   e. Else, let handlerResult be Completion(HostCallJobCallback(handler, undefined, « argument »)).\n            Some(handler) => match context.host_hooks().call_job_callback(\n                &handler,\n                &JsValue::undefined(),\n                std::slice::from_ref(&argument),\n                context,\n            ) {\n                Ok(v) => Ok(v),\n                Err(e) => Err(e.into_opaque(context)?),\n            },\n        };\n\n        match promise_capability {\n            None => {\n                // f. If promiseCapability is undefined, then\n                //    i. Assert: handlerResult is not an abrupt completion.\n                assert!(\n                    handler_result.is_ok(),\n                    \"Assertion: <handlerResult is not an abrupt completion> failed\"\n                );\n\n                // ii. Return empty.\n                Ok(JsValue::undefined())\n            }\n            Some(promise_capability_record) => {\n                // g. Assert: promiseCapability is a PromiseCapability Record.\n                let PromiseCapability {\n                    promise: _,\n                    functions: ResolvingFunctions { resolve, reject },\n                } = &promise_capability_record;\n\n                match handler_result {\n                    // h. If handlerResult is an abrupt completion, then\n                    Err(value) => {\n                        // i. Return ? Call(promiseCapability.[[Reject]], undefined, « handlerResult.[[Value]] »).\n                        reject.call(&JsValue::undefined(), &[value], context)\n                    }\n\n                    // i. Else,\n                    Ok(value) => {\n                        // i. Return ? Call(promiseCapability.[[Resolve]], undefined, « handlerResult.[[Value]] »).\n                        resolve.call(&JsValue::undefined(), &[value], context)\n                    }\n                }\n            }\n        }\n    };\n\n    // 4. Return the Record { [[Job]]: job, [[Realm]]: handlerRealm }.\n    PromiseJob::with_realm(job, realm)\n}\n\n/// More information:\n///  - [ECMAScript reference][spec]\n///\n/// [spec]: https://tc39.es/ecma262/#sec-newpromiseresolvethenablejob\nfn new_promise_resolve_thenable_job(\n    promise_to_resolve: JsObject<Promise>,\n    thenable: JsValue,\n    then: JobCallback,\n    context: &mut Context,\n) -> PromiseJob {\n    // Inverting order since `job` captures variables by value.\n\n    // 2. Let getThenRealmResult be Completion(GetFunctionRealm(then.[[Callback]])).\n    // 3. If getThenRealmResult is a normal completion, let thenRealm be getThenRealmResult.[[Value]].\n    // 4. Else, let thenRealm be the current Realm Record.\n    // 5. NOTE: thenRealm is never null. When then.[[Callback]] is a revoked Proxy and no code runs, thenRealm is used to create error objects.\n    let realm = then\n        .callback()\n        .get_function_realm(context)\n        .unwrap_or_else(|_| context.realm().clone());\n\n    // 1. Let job be a new Job Abstract Closure with no parameters that captures promiseToResolve, thenable, and then and performs the following steps when called:\n    let job = move |context: &mut Context| {\n        //    a. Let resolvingFunctions be CreateResolvingFunctions(promiseToResolve).\n        let resolving_functions = Promise::create_resolving_functions(&promise_to_resolve, context);\n\n        //    b. Let thenCallResult be Completion(HostCallJobCallback(then, thenable, « resolvingFunctions.[[Resolve]], resolvingFunctions.[[Reject]] »)).\n        let then_call_result = context.host_hooks().call_job_callback(\n            &then,\n            &thenable,\n            &[\n                resolving_functions.resolve.clone().into(),\n                resolving_functions.reject.clone().into(),\n            ],\n            context,\n        );\n\n        //    c. If thenCallResult is an abrupt completion, then\n        if let Err(value) = then_call_result {\n            let value = value.into_opaque(context)?;\n            //    i. Return ? Call(resolvingFunctions.[[Reject]], undefined, « thenCallResult.[[Value]] »).\n            return resolving_functions\n                .reject\n                .call(&JsValue::undefined(), &[value], context);\n        }\n\n        //    d. Return ? thenCallResult.\n        then_call_result\n    };\n\n    // 6. Return the Record { [[Job]]: job, [[Realm]]: thenRealm }.\n    PromiseJob::with_realm(job, realm)\n}\n\n#[cfg(feature = \"experimental\")]\n/// Variant for the `PerformPromiseAllKeyed` algorithm.\n#[derive(Debug, Clone, Copy, PartialEq, Eq)]\nenum KeyedVariant {\n    /// `Promise.allKeyed` — resolves when all promises resolve.\n    All,\n    /// `Promise.allSettledKeyed` — resolves when all promises settle.\n    AllSettled,\n}\n\n#[cfg(feature = \"experimental\")]\n/// `CreateKeyedPromiseCombinatorResultObject ( keys, values )`\n///\n/// Creates a null-prototype object with data properties mapping keys to values.\n///\n/// More information:\n///  - [TC39 proposal spec][spec]\n///\n/// [spec]: https://tc39.es/proposal-await-dictionary/#sec-createkeyedpromisecombinatorresultobject\nfn create_keyed_result_object(\n    keys: &[PropertyKey],\n    values: &[JsValue],\n    context: &mut Context,\n) -> JsObject {\n    // 1. Assert: The number of elements in keys is the same as the number of elements in values.\n    debug_assert_eq!(keys.len(), values.len());\n\n    // 2. Let obj be OrdinaryObjectCreate(null).\n    let obj = JsObject::with_null_proto();\n\n    // 3. For each integer i such that 0 ≤ i < the number of elements in keys, in ascending order, do\n    for (key, value) in keys.iter().zip(values.iter()) {\n        // a. Perform ! CreateDataPropertyOrThrow(obj, keys[i], values[i]).\n        obj.create_data_property_or_throw(key.clone(), value.clone(), context)\n            .expect(\"cannot fail per spec\");\n    }\n\n    // 4. Return obj.\n    obj\n}\n"
  },
  {
    "path": "core/engine/src/builtins/promise/tests.rs",
    "content": "use crate::{TestAction, run_test_actions};\nuse indoc::indoc;\n\n#[test]\nfn promise() {\n    run_test_actions([\n        TestAction::run(indoc! {r#\"\n                    let count = 0;\n                    const promise = new Promise((resolve, reject) => {\n                        count += 1;\n                        resolve(undefined);\n                    }).then((_) => (count += 1));\n                    count += 1;\n                \"#}),\n        TestAction::assert_eq(\"count\", 2),\n        TestAction::inspect_context(|ctx| ctx.run_jobs().unwrap()),\n        TestAction::assert_eq(\"count\", 3),\n    ]);\n}\n\n#[test]\nfn promise_all_resolves_values() {\n    run_test_actions([\n        TestAction::run(indoc! {r#\"\n            var values = [];\n            var p = Promise.all([Promise.resolve(1), Promise.resolve(2)]);\n            p.then(v => { values = v; });\n        \"#}),\n        TestAction::inspect_context(|ctx| ctx.run_jobs().unwrap()),\n        TestAction::assert_eq(\"values.length\", 2),\n        TestAction::assert_eq(\"values[0]\", 1),\n        TestAction::assert_eq(\"values[1]\", 2),\n    ]);\n}\n\n#[test]\nfn promise_all_rejects() {\n    run_test_actions([\n        TestAction::run(indoc! {r#\"\n            var err = null;\n            var p = Promise.all([Promise.resolve(1), Promise.reject(2)]);\n            p.catch(e => { err = e; });\n        \"#}),\n        TestAction::inspect_context(|ctx| ctx.run_jobs().unwrap()),\n        TestAction::assert_eq(\"err\", 2),\n    ]);\n}\n\n#[test]\nfn promise_any_resolves_first_success() {\n    run_test_actions([\n        TestAction::run(indoc! {r#\"\n            var val = null;\n            var p = Promise.any([Promise.reject(1), Promise.resolve(2)]);\n            p.then(v => { val = v; });\n        \"#}),\n        TestAction::inspect_context(|ctx| ctx.run_jobs().unwrap()),\n        TestAction::assert_eq(\"val\", 2),\n    ]);\n}\n\n#[test]\nfn promise_all_settled_resolves_results() {\n    run_test_actions([\n        TestAction::run(indoc! {r#\"\n            var values = [];\n            Promise.allSettled([\n                Promise.resolve(1),\n                Promise.reject(2)\n            ]).then(results => {\n                values = [\n                    results[0].status,\n                    results[0].value,\n                    results[1].status,\n                    results[1].reason\n                ];\n            });\n        \"#}),\n        TestAction::inspect_context(|ctx| ctx.run_jobs().unwrap()),\n        TestAction::assert_eq(\"values[0]\", crate::js_string!(\"fulfilled\")),\n        TestAction::assert_eq(\"values[1]\", 1),\n        TestAction::assert_eq(\"values[2]\", crate::js_string!(\"rejected\")),\n        TestAction::assert_eq(\"values[3]\", 2),\n    ]);\n}\n\n#[test]\nfn promise_race_resolves_first() {\n    run_test_actions([\n        TestAction::run(indoc! {r#\"\n            var val = null;\n            Promise.race([\n                Promise.resolve(10),\n                Promise.resolve(20)\n            ]).then(v => { val = v; });\n        \"#}),\n        TestAction::inspect_context(|ctx| ctx.run_jobs().unwrap()),\n        TestAction::assert_eq(\"val\", 10),\n    ]);\n}\n"
  },
  {
    "path": "core/engine/src/builtins/proxy/mod.rs",
    "content": "//! Boa's implementation of ECMAScript's global `Proxy` object.\n//!\n//! The `Proxy` object enables you to create a proxy for another object,\n//! which can intercept and redefine fundamental operations for that object.\n//!\n//! More information:\n//!  - [ECMAScript reference][spec]\n//!  - [MDN documentation][mdn]\n//!\n//! [spec]: https://tc39.es/ecma262/#sec-proxy-objects\n//! [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Proxy\n\nuse super::{BuiltInBuilder, BuiltInConstructor, IntrinsicObject, OrdinaryObject};\nuse crate::JsExpect;\nuse crate::object::internal_methods::InternalMethodCallContext;\nuse crate::value::JsVariant;\nuse crate::{\n    Context, JsArgs, JsResult, JsString, JsValue,\n    builtins::{BuiltInObject, array},\n    context::intrinsics::{Intrinsics, StandardConstructor, StandardConstructors},\n    error::JsNativeError,\n    js_string,\n    native_function::NativeFunction,\n    object::{\n        JsData, JsFunction, JsObject, JsPrototype,\n        internal_methods::{\n            CallValue, InternalMethodPropertyContext, InternalObjectMethods,\n            ORDINARY_INTERNAL_METHODS, is_compatible_property_descriptor,\n        },\n        shape::slot::SlotAttributes,\n    },\n    property::{PropertyDescriptor, PropertyKey},\n    realm::Realm,\n    string::StaticJsStrings,\n    value::Type,\n};\nuse boa_gc::{Finalize, GcRefCell, Trace};\nuse rustc_hash::FxHashSet;\n/// Javascript `Proxy` object.\n#[derive(Debug, Clone, Trace, Finalize)]\npub struct Proxy {\n    // (target, handler)\n    data: Option<(JsObject, JsObject)>,\n}\n\nimpl JsData for Proxy {\n    fn internal_methods(&self) -> &'static InternalObjectMethods {\n        static BASIC: InternalObjectMethods = InternalObjectMethods {\n            __get_prototype_of__: proxy_exotic_get_prototype_of,\n            __set_prototype_of__: proxy_exotic_set_prototype_of,\n            __is_extensible__: proxy_exotic_is_extensible,\n            __prevent_extensions__: proxy_exotic_prevent_extensions,\n            __get_own_property__: proxy_exotic_get_own_property,\n            __define_own_property__: proxy_exotic_define_own_property,\n            __has_property__: proxy_exotic_has_property,\n            __try_get__: proxy_exotic_try_get,\n            __get__: proxy_exotic_get,\n            __set__: proxy_exotic_set,\n            __delete__: proxy_exotic_delete,\n            __own_property_keys__: proxy_exotic_own_property_keys,\n            ..ORDINARY_INTERNAL_METHODS\n        };\n\n        static CALLABLE: InternalObjectMethods = InternalObjectMethods {\n            __call__: proxy_exotic_call,\n            ..BASIC\n        };\n\n        static CONSTRUCTOR: InternalObjectMethods = InternalObjectMethods {\n            __call__: proxy_exotic_call,\n            __construct__: proxy_exotic_construct,\n            ..BASIC\n        };\n\n        let Some(data) = &self.data else {\n            return &BASIC;\n        };\n\n        if data.0.is_constructor() {\n            &CONSTRUCTOR\n        } else if data.0.is_callable() {\n            &CALLABLE\n        } else {\n            &BASIC\n        }\n    }\n}\n\nimpl IntrinsicObject for Proxy {\n    fn init(realm: &Realm) {\n        BuiltInBuilder::from_standard_constructor::<Self>(realm)\n            .static_method(Self::revocable, js_string!(\"revocable\"), 2)\n            .build_without_prototype();\n    }\n\n    fn get(intrinsics: &Intrinsics) -> JsObject {\n        Self::STANDARD_CONSTRUCTOR(intrinsics.constructors()).constructor()\n    }\n}\n\nimpl BuiltInObject for Proxy {\n    const NAME: JsString = StaticJsStrings::PROXY;\n}\n\nimpl BuiltInConstructor for Proxy {\n    const CONSTRUCTOR_ARGUMENTS: usize = 2;\n    const PROTOTYPE_STORAGE_SLOTS: usize = 0;\n    const CONSTRUCTOR_STORAGE_SLOTS: usize = 1;\n\n    const STANDARD_CONSTRUCTOR: fn(&StandardConstructors) -> &StandardConstructor =\n        StandardConstructors::proxy;\n\n    /// `28.2.1.1 Proxy ( target, handler )`\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-proxy-target-handler\n    fn constructor(\n        new_target: &JsValue,\n        args: &[JsValue],\n        context: &mut Context,\n    ) -> JsResult<JsValue> {\n        // 1. If NewTarget is undefined, throw a TypeError exception.\n        if new_target.is_undefined() {\n            return Err(JsNativeError::typ()\n                .with_message(\"Proxy constructor called on undefined new target\")\n                .into());\n        }\n\n        // 2. Return ? ProxyCreate(target, handler).\n        Self::create(args.get_or_undefined(0), args.get_or_undefined(1), context).map(JsValue::from)\n    }\n}\n\nimpl Proxy {\n    pub(crate) fn new(target: JsObject, handler: JsObject) -> Self {\n        Self {\n            data: Some((target, handler)),\n        }\n    }\n\n    /// This is an internal method only built for usage in the proxy internal methods.\n    ///\n    /// It returns the (target, handler) of the proxy.\n    pub(crate) fn try_data(&self) -> JsResult<(JsObject, JsObject)> {\n        self.data.clone().ok_or_else(|| {\n            JsNativeError::typ()\n                .with_message(\"Proxy object has empty handler and target\")\n                .into()\n        })\n    }\n\n    // `10.5.14 ProxyCreate ( target, handler )`\n    //\n    // More information:\n    //  - [ECMAScript reference][spec]\n    //\n    // [spec]: https://tc39.es/ecma262/#sec-proxycreate\n    pub(crate) fn create(\n        target: &JsValue,\n        handler: &JsValue,\n        context: &mut Context,\n    ) -> JsResult<JsObject> {\n        // 1. If Type(target) is not Object, throw a TypeError exception.\n        let target = target.as_object().ok_or_else(|| {\n            JsNativeError::typ().with_message(\"Proxy constructor called with non-object target\")\n        })?;\n\n        // 2. If Type(handler) is not Object, throw a TypeError exception.\n        let handler = handler.as_object().ok_or_else(|| {\n            JsNativeError::typ().with_message(\"Proxy constructor called with non-object handler\")\n        })?;\n\n        // 3. Let P be ! MakeBasicObject(« [[ProxyHandler]], [[ProxyTarget]] »).\n        // 4. Set P's essential internal methods, except for [[Call]] and [[Construct]], to the definitions specified in 10.5.\n        // 5. If IsCallable(target) is true, then\n        // a. Set P.[[Call]] as specified in 10.5.12.\n        // b. If IsConstructor(target) is true, then\n        // i. Set P.[[Construct]] as specified in 10.5.13.\n        // 6. Set P.[[ProxyTarget]] to target.\n        // 7. Set P.[[ProxyHandler]] to handler.\n        let p = JsObject::from_proto_and_data_with_shared_shape(\n            context.root_shape(),\n            context.intrinsics().constructors().object().prototype(),\n            Self::new(target.clone(), handler.clone()),\n        )\n        .upcast();\n\n        // 8. Return P.\n        Ok(p)\n    }\n\n    pub(crate) fn revoker(proxy: JsObject, context: &mut Context) -> JsFunction {\n        // 3. Let revoker be ! CreateBuiltinFunction(revokerClosure, 0, \"\", « [[RevocableProxy]] »).\n        // 4. Set revoker.[[RevocableProxy]] to p.\n\n        NativeFunction::from_copy_closure_with_captures(\n            |_, _, revocable_proxy, _| {\n                // a. Let F be the active function object.\n                // b. Let p be F.[[RevocableProxy]].\n                // d. Set F.[[RevocableProxy]] to null.\n                if let Some(p) = std::mem::take(&mut *revocable_proxy.borrow_mut()) {\n                    // e. Assert: p is a Proxy object.\n                    // f. Set p.[[ProxyTarget]] to null.\n                    // g. Set p.[[ProxyHandler]] to null.\n                    p.downcast_mut::<Proxy>()\n                        .js_expect(\"[[RevocableProxy]] must be a proxy object\")?\n                        .data = None;\n                }\n\n                // c. If p is null, return undefined.\n                // h. Return undefined.\n                Ok(JsValue::undefined())\n            },\n            GcRefCell::new(Some(proxy)),\n        )\n        .to_js_function(context.realm())\n    }\n\n    /// `28.2.2.1 Proxy.revocable ( target, handler )`\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-proxy.revocable\n    fn revocable(_: &JsValue, args: &[JsValue], context: &mut Context) -> JsResult<JsValue> {\n        // 1. Let p be ? ProxyCreate(target, handler).\n        let p = Self::create(args.get_or_undefined(0), args.get_or_undefined(1), context)?;\n\n        // Revoker creation steps on `Proxy::revoker`\n        let revoker = Self::revoker(p.clone(), context);\n\n        // 5. Let result be ! OrdinaryObjectCreate(%Object.prototype%).\n        let result = JsObject::with_object_proto(context.intrinsics());\n\n        // 6. Perform ! CreateDataPropertyOrThrow(result, \"proxy\", p).\n        result\n            .create_data_property_or_throw(js_string!(\"proxy\"), p, context)\n            .js_expect(\"CreateDataPropertyOrThrow cannot fail here\")?;\n\n        // 7. Perform ! CreateDataPropertyOrThrow(result, \"revoke\", revoker).\n        result\n            .create_data_property_or_throw(js_string!(\"revoke\"), revoker, context)\n            .js_expect(\"CreateDataPropertyOrThrow cannot fail here\")?;\n\n        // 8. Return result.\n        Ok(result.into())\n    }\n}\n\n/// `10.5.1 [[GetPrototypeOf]] ( )`\n///\n/// More information:\n///  - [ECMAScript reference][spec]\n///\n/// [spec]: https://tc39.es/ecma262/#sec-proxy-object-internal-methods-and-internal-slots-getprototypeof\npub(crate) fn proxy_exotic_get_prototype_of(\n    obj: &JsObject,\n    context: &mut Context,\n) -> JsResult<JsPrototype> {\n    // 1. Let handler be O.[[ProxyHandler]].\n    // 2. If handler is null, throw a TypeError exception.\n    // 3. Assert: Type(handler) is Object.\n    // 4. Let target be O.[[ProxyTarget]].\n    let (target, handler) = obj\n        .downcast_ref::<Proxy>()\n        .js_expect(\"Proxy object internal internal method called on non-proxy object\")?\n        .try_data()?;\n\n    // 5. Let trap be ? GetMethod(handler, \"getPrototypeOf\").\n    let Some(trap) = handler.get_method(js_string!(\"getPrototypeOf\"), context)? else {\n        // 6. If trap is undefined, then\n        // a. Return ? target.[[GetPrototypeOf]]().\n        return target.__get_prototype_of__(context);\n    };\n\n    // 7. Let handlerProto be ? Call(trap, handler, « target »).\n    let handler_proto = trap.call(&handler.into(), &[target.clone().into()], context)?;\n\n    // 8. If Type(handlerProto) is neither Object nor Null, throw a TypeError exception.\n    let handler_proto = match handler_proto.variant() {\n        JsVariant::Object(obj) => Some(obj.clone()),\n        JsVariant::Null => None,\n        _ => {\n            return Err(JsNativeError::typ()\n                .with_message(\"Proxy trap result is neither object nor null\")\n                .into());\n        }\n    };\n\n    // 9. Let extensibleTarget be ? IsExtensible(target).\n    // 10. If extensibleTarget is true, return handlerProto.\n    if target.is_extensible(context)? {\n        return Ok(handler_proto);\n    }\n\n    // 11. Let targetProto be ? target.[[GetPrototypeOf]]().\n    let target_proto = target.__get_prototype_of__(context)?;\n\n    // 12. If SameValue(handlerProto, targetProto) is false, throw a TypeError exception.\n    if handler_proto != target_proto {\n        return Err(JsNativeError::typ()\n            .with_message(\"Proxy trap returned unexpected prototype\")\n            .into());\n    }\n\n    // 13. Return handlerProto.\n    Ok(handler_proto)\n}\n\n/// `10.5.2 [[SetPrototypeOf]] ( V )`\n///\n/// More information:\n///  - [ECMAScript reference][spec]\n///\n/// [spec]: https://tc39.es/ecma262/#sec-proxy-object-internal-methods-and-internal-slots-setprototypeof-v\npub(crate) fn proxy_exotic_set_prototype_of(\n    obj: &JsObject,\n    val: JsPrototype,\n    context: &mut Context,\n) -> JsResult<bool> {\n    // 1. Let handler be O.[[ProxyHandler]].\n    // 2. If handler is null, throw a TypeError exception.\n    // 3. Assert: Type(handler) is Object.\n    // 4. Let target be O.[[ProxyTarget]].\n    let (target, handler) = obj\n        .downcast_ref::<Proxy>()\n        .js_expect(\"Proxy object internal internal method called on non-proxy object\")?\n        .try_data()?;\n\n    // 5. Let trap be ? GetMethod(handler, \"setPrototypeOf\").\n    let Some(trap) = handler.get_method(js_string!(\"setPrototypeOf\"), context)? else {\n        // 6. If trap is undefined, then\n        // a. Return ? target.[[SetPrototypeOf]](V).\n        return target.__set_prototype_of__(val, context);\n    };\n\n    // 7. Let booleanTrapResult be ! ToBoolean(? Call(trap, handler, « target, V »)).\n    // 8. If booleanTrapResult is false, return false.\n    if !trap\n        .call(\n            &handler.into(),\n            &[\n                target.clone().into(),\n                val.clone().map_or(JsValue::null(), Into::into),\n            ],\n            context,\n        )?\n        .to_boolean()\n    {\n        return Ok(false);\n    }\n\n    // 9. Let extensibleTarget be ? IsExtensible(target).\n    // 10. If extensibleTarget is true, return true.\n    if target.is_extensible(context)? {\n        return Ok(true);\n    }\n\n    // 11. Let targetProto be ? target.[[GetPrototypeOf]]().\n    let target_proto = target.__get_prototype_of__(context)?;\n\n    // 12. If SameValue(V, targetProto) is false, throw a TypeError exception.\n    if val != target_proto {\n        return Err(JsNativeError::typ()\n            .with_message(\"Proxy trap failed to set prototype\")\n            .into());\n    }\n\n    // 13. Return true.\n    Ok(true)\n}\n\n/// `10.5.3 [[IsExtensible]] ( )`\n///\n/// More information:\n///  - [ECMAScript reference][spec]\n///\n/// [spec]: https://tc39.es/ecma262/#sec-proxy-object-internal-methods-and-internal-slots-isextensible\npub(crate) fn proxy_exotic_is_extensible(obj: &JsObject, context: &mut Context) -> JsResult<bool> {\n    // 1. Let handler be O.[[ProxyHandler]].\n    // 2. If handler is null, throw a TypeError exception.\n    // 3. Assert: Type(handler) is Object.\n    // 4. Let target be O.[[ProxyTarget]].\n    let (target, handler) = obj\n        .downcast_ref::<Proxy>()\n        .js_expect(\"Proxy object internal internal method called on non-proxy object\")?\n        .try_data()?;\n\n    // 5. Let trap be ? GetMethod(handler, \"isExtensible\").\n    let Some(trap) = handler.get_method(js_string!(\"isExtensible\"), context)? else {\n        // 6. If trap is undefined, then\n        // a. Return ? IsExtensible(target).\n        return target.is_extensible(context);\n    };\n\n    // 7. Let booleanTrapResult be ! ToBoolean(? Call(trap, handler, « target »)).\n    let boolean_trap_result = trap\n        .call(&handler.into(), &[target.clone().into()], context)?\n        .to_boolean();\n\n    // 8. Let targetResult be ? IsExtensible(target).\n    let target_result = target.is_extensible(context)?;\n\n    // 9. If SameValue(booleanTrapResult, targetResult) is false, throw a TypeError exception.\n    if boolean_trap_result != target_result {\n        return Err(JsNativeError::typ()\n            .with_message(\"Proxy trap returned unexpected extensible value\")\n            .into());\n    }\n\n    // 10. Return booleanTrapResult.\n    Ok(boolean_trap_result)\n}\n\n/// `10.5.4 [[PreventExtensions]] ( )`\n///\n/// More information:\n///  - [ECMAScript reference][spec]\n///\n/// [spec]: https://tc39.es/ecma262/#sec-proxy-object-internal-methods-and-internal-slots-preventextensions\npub(crate) fn proxy_exotic_prevent_extensions(\n    obj: &JsObject,\n    context: &mut Context,\n) -> JsResult<bool> {\n    // 1. Let handler be O.[[ProxyHandler]].\n    // 2. If handler is null, throw a TypeError exception.\n    // 3. Assert: Type(handler) is Object.\n    // 4. Let target be O.[[ProxyTarget]].\n    let (target, handler) = obj\n        .downcast_ref::<Proxy>()\n        .js_expect(\"Proxy object internal internal method called on non-proxy object\")?\n        .try_data()?;\n\n    // 5. Let trap be ? GetMethod(handler, \"preventExtensions\").\n    let Some(trap) = handler.get_method(js_string!(\"preventExtensions\"), context)? else {\n        // 6. If trap is undefined, then\n        // a. Return ? target.[[PreventExtensions]]().\n        return target.__prevent_extensions__(context);\n    };\n\n    // 7. Let booleanTrapResult be ! ToBoolean(? Call(trap, handler, « target »)).\n    let boolean_trap_result = trap\n        .call(&handler.into(), &[target.clone().into()], context)?\n        .to_boolean();\n\n    // 8. If booleanTrapResult is true, then\n    if boolean_trap_result && target.is_extensible(context)? {\n        // a. Let extensibleTarget be ? IsExtensible(target).\n        // b. If extensibleTarget is true, throw a TypeError exception.\n        return Err(JsNativeError::typ()\n            .with_message(\"Proxy trap failed to set extensible\")\n            .into());\n    }\n\n    // 9. Return booleanTrapResult.\n    Ok(boolean_trap_result)\n}\n\n/// `10.5.5 [[GetOwnProperty]] ( P )`\n///\n/// More information:\n///  - [ECMAScript reference][spec]\n///\n/// [spec]: https://tc39.es/ecma262/#sec-proxy-object-internal-methods-and-internal-slots-getownproperty-p\npub(crate) fn proxy_exotic_get_own_property(\n    obj: &JsObject,\n    key: &PropertyKey,\n    context: &mut InternalMethodPropertyContext<'_>,\n) -> JsResult<Option<PropertyDescriptor>> {\n    context.slot().attributes |= SlotAttributes::NOT_CACHEABLE;\n\n    // 1. Let handler be O.[[ProxyHandler]].\n    // 2. If handler is null, throw a TypeError exception.\n    // 3. Assert: Type(handler) is Object.\n    // 4. Let target be O.[[ProxyTarget]].\n    let (target, handler) = obj\n        .downcast_ref::<Proxy>()\n        .js_expect(\"Proxy object internal internal method called on non-proxy object\")?\n        .try_data()?;\n\n    // 5. Let trap be ? GetMethod(handler, \"getOwnPropertyDescriptor\").\n    let Some(trap) = handler.get_method(js_string!(\"getOwnPropertyDescriptor\"), context)? else {\n        // 6. If trap is undefined, then\n        // a. Return ? target.[[GetOwnProperty]](P).\n        return target.__get_own_property__(key, context);\n    };\n\n    // 7. Let trapResultObj be ? Call(trap, handler, « target, P »).\n    let trap_result_obj = trap.call(\n        &handler.into(),\n        &[target.clone().into(), key.clone().into()],\n        context,\n    )?;\n\n    // 8. If Type(trapResultObj) is neither Object nor Undefined, throw a TypeError exception.\n    if !trap_result_obj.is_object() && !trap_result_obj.is_undefined() {\n        return Err(JsNativeError::typ()\n            .with_message(\"Proxy trap result is neither object nor undefined\")\n            .into());\n    }\n\n    // 9. Let targetDesc be ? target.[[GetOwnProperty]](P).\n    let target_desc = target.__get_own_property__(key, context)?;\n\n    // 10. If trapResultObj is undefined, then\n    if trap_result_obj.is_undefined() {\n        if let Some(desc) = target_desc {\n            // b. If targetDesc.[[Configurable]] is false, throw a TypeError exception.\n            if !desc.expect_configurable() {\n                return Err(JsNativeError::typ()\n                    .with_message(\n                        \"Proxy trap result is undefined and target result is not configurable\",\n                    )\n                    .into());\n            }\n\n            // c. Let extensibleTarget be ? IsExtensible(target).\n            // d. If extensibleTarget is false, throw a TypeError exception.\n            if !target.is_extensible(context)? {\n                return Err(JsNativeError::typ()\n                    .with_message(\"Proxy trap result is undefined and target is not extensible\")\n                    .into());\n            }\n            // e. Return undefined.\n            return Ok(None);\n        }\n\n        // a. If targetDesc is undefined, return undefined.\n        return Ok(None);\n    }\n\n    // 11. Let extensibleTarget be ? IsExtensible(target).\n    let extensible_target = target.is_extensible(context)?;\n\n    // 12. Let resultDesc be ? ToPropertyDescriptor(trapResultObj).\n    let result_desc = trap_result_obj.to_property_descriptor(context)?;\n\n    // 13. Call CompletePropertyDescriptor(resultDesc).\n    let result_desc = result_desc.complete_property_descriptor();\n\n    // 14. Let valid be IsCompatiblePropertyDescriptor(extensibleTarget, resultDesc, targetDesc).\n    // 15. If valid is false, throw a TypeError exception.\n    if !is_compatible_property_descriptor(\n        extensible_target,\n        result_desc.clone(),\n        target_desc.clone(),\n    ) {\n        return Err(JsNativeError::typ()\n            .with_message(\"Proxy trap returned unexpected property\")\n            .into());\n    }\n\n    // 16. If resultDesc.[[Configurable]] is false, then\n    if !result_desc.expect_configurable() {\n        // a. If targetDesc is undefined or targetDesc.[[Configurable]] is true, then\n        match &target_desc {\n            Some(desc) if !desc.expect_configurable() => {\n                // b. If resultDesc has a [[Writable]] field and resultDesc.[[Writable]] is false, then\n                if result_desc.writable() == Some(false) {\n                    // i. If targetDesc.[[Writable]] is true, throw a TypeError exception.\n                    if desc.expect_writable() {\n                        return\n                            Err(JsNativeError::typ().with_message(\"Proxy trap result is writable and not configurable while target result is not configurable\").into())\n                        ;\n                    }\n                }\n            }\n            // i. Throw a TypeError exception.\n            _ => {\n                return Err(JsNativeError::typ()\n                    .with_message(\n                        \"Proxy trap result is not configurable and target result is undefined\",\n                    )\n                    .into());\n            }\n        }\n    }\n\n    // 17. Return resultDesc.\n    Ok(Some(result_desc))\n}\n\n/// `10.5.6 [[DefineOwnProperty]] ( P, Desc )`\n///\n/// More information:\n///  - [ECMAScript reference][spec]\n///\n/// [spec]: https://tc39.es/ecma262/#sec-proxy-object-internal-methods-and-internal-slots-defineownproperty-p-desc\npub(crate) fn proxy_exotic_define_own_property(\n    obj: &JsObject,\n    key: &PropertyKey,\n    desc: PropertyDescriptor,\n    context: &mut InternalMethodPropertyContext<'_>,\n) -> JsResult<bool> {\n    context.slot().attributes |= SlotAttributes::NOT_CACHEABLE;\n\n    // 1. Let handler be O.[[ProxyHandler]].\n    // 2. If handler is null, throw a TypeError exception.\n    // 3. Assert: Type(handler) is Object.\n    // 4. Let target be O.[[ProxyTarget]].\n    let (target, handler) = obj\n        .downcast_ref::<Proxy>()\n        .js_expect(\"Proxy object internal internal method called on non-proxy object\")?\n        .try_data()?;\n\n    // 5. Let trap be ? GetMethod(handler, \"defineProperty\").\n    let Some(trap) = handler.get_method(js_string!(\"defineProperty\"), context)? else {\n        // 6. If trap is undefined, then\n        // a. Return ? target.[[DefineOwnProperty]](P, Desc).\n        return target.__define_own_property__(key, desc, context);\n    };\n\n    // 7. Let descObj be FromPropertyDescriptor(Desc).\n    let desc_obj = OrdinaryObject::from_property_descriptor(Some(desc.clone()), context)?;\n\n    // 8. Let booleanTrapResult be ! ToBoolean(? Call(trap, handler, « target, P, descObj »)).\n    // 9. If booleanTrapResult is false, return false.\n    if !trap\n        .call(\n            &handler.into(),\n            &[target.clone().into(), key.clone().into(), desc_obj],\n            context,\n        )?\n        .to_boolean()\n    {\n        return Ok(false);\n    }\n\n    // 10. Let targetDesc be ? target.[[GetOwnProperty]](P).\n    let target_desc = target.__get_own_property__(key, context)?;\n\n    // 11. Let extensibleTarget be ? IsExtensible(target).\n    let extensible_target = target.is_extensible(context)?;\n\n    // 12. If Desc has a [[Configurable]] field and if Desc.[[Configurable]] is false, then\n    let setting_config_false = matches!(desc.configurable(), Some(false));\n\n    match target_desc {\n        // 14. If targetDesc is undefined, then\n        None => {\n            // a. If extensibleTarget is false, throw a TypeError exception.\n            if !extensible_target {\n                return Err(JsNativeError::typ()\n                    .with_message(\"Proxy trap failed to set property\")\n                    .into());\n            }\n\n            // b. If settingConfigFalse is true, throw a TypeError exception.\n            if setting_config_false {\n                return Err(JsNativeError::typ()\n                    .with_message(\"Proxy trap failed to set property\")\n                    .into());\n            }\n        }\n        // 15. Else,\n        Some(target_desc) => {\n            // a. If IsCompatiblePropertyDescriptor(extensibleTarget, Desc, targetDesc) is false, throw a TypeError exception.\n            if !is_compatible_property_descriptor(\n                extensible_target,\n                desc.clone(),\n                Some(target_desc.clone()),\n            ) {\n                return Err(JsNativeError::typ()\n                    .with_message(\"Proxy trap set property to unexpected value\")\n                    .into());\n            }\n\n            // b. If settingConfigFalse is true and targetDesc.[[Configurable]] is true, throw a TypeError exception.\n            if setting_config_false && target_desc.expect_configurable() {\n                return Err(JsNativeError::typ()\n                    .with_message(\"Proxy trap set property with unexpected configurable field\")\n                    .into());\n            }\n\n            // c. If IsDataDescriptor(targetDesc) is true, targetDesc.[[Configurable]] is false, and targetDesc.[[Writable]] is true, then\n            if target_desc.is_data_descriptor()\n                && !target_desc.expect_configurable()\n                && target_desc.expect_writable()\n            {\n                // i. If Desc has a [[Writable]] field and Desc.[[Writable]] is false, throw a TypeError exception.\n                if let Some(writable) = desc.writable()\n                    && !writable\n                {\n                    return Err(JsNativeError::typ()\n                        .with_message(\"Proxy trap set property with unexpected writable field\")\n                        .into());\n                }\n            }\n        }\n    }\n\n    // 16. Return true.\n    Ok(true)\n}\n\n/// `10.5.7 [[HasProperty]] ( P )`\n///\n/// More information:\n///  - [ECMAScript reference][spec]\n///\n/// [spec]: https://tc39.es/ecma262/#sec-proxy-object-internal-methods-and-internal-slots-hasproperty-p\npub(crate) fn proxy_exotic_has_property(\n    obj: &JsObject,\n    key: &PropertyKey,\n    context: &mut InternalMethodPropertyContext<'_>,\n) -> JsResult<bool> {\n    context.slot().attributes |= SlotAttributes::NOT_CACHEABLE;\n\n    // 1. Let handler be O.[[ProxyHandler]].\n    // 2. If handler is null, throw a TypeError exception.\n    // 3. Assert: Type(handler) is Object.\n    // 4. Let target be O.[[ProxyTarget]].\n    let (target, handler) = obj\n        .downcast_ref::<Proxy>()\n        .js_expect(\"Proxy object internal internal method called on non-proxy object\")?\n        .try_data()?;\n\n    // 5. Let trap be ? GetMethod(handler, \"has\").\n    let Some(trap) = handler.get_method(js_string!(\"has\"), context)? else {\n        // 6. If trap is undefined, then\n        // a. Return ? target.[[HasProperty]](P).\n        return target.has_property(key.clone(), context);\n    };\n\n    // 7. Let booleanTrapResult be ! ToBoolean(? Call(trap, handler, « target, P »)).\n    let boolean_trap_result = trap\n        .call(\n            &handler.into(),\n            &[target.clone().into(), key.clone().into()],\n            context,\n        )?\n        .to_boolean();\n\n    // 8. If booleanTrapResult is false, then\n    if !boolean_trap_result {\n        // a. Let targetDesc be ? target.[[GetOwnProperty]](P).\n        let target_desc = target.__get_own_property__(key, context)?;\n\n        // b. If targetDesc is not undefined, then\n        if let Some(target_desc) = target_desc {\n            // i. If targetDesc.[[Configurable]] is false, throw a TypeError exception.\n            if !target_desc.expect_configurable() {\n                return Err(JsNativeError::typ()\n                    .with_message(\"Proxy trap returned unexpected property\")\n                    .into());\n            }\n\n            // ii. Let extensibleTarget be ? IsExtensible(target).\n            // iii. If extensibleTarget is false, throw a TypeError exception.\n            if !target.is_extensible(context)? {\n                return Err(JsNativeError::typ()\n                    .with_message(\"Proxy trap returned unexpected property\")\n                    .into());\n            }\n        }\n    }\n\n    // 9. Return booleanTrapResult.\n    Ok(boolean_trap_result)\n}\n\n/// Internal optimization method for `Proxy` exotic objects.\n///\n/// This method combines the internal methods `[[HasProperty]]` and `[[Get]]`.\n///\n/// More information:\n///  - [ECMAScript reference HasProperty][spec0]\n///  - [ECMAScript reference Get][spec1]\n///\n/// [spec0]: https://tc39.es/ecma262/#sec-proxy-object-internal-methods-and-internal-slots-hasproperty-p\n/// [spec1]: https://tc39.es/ecma262/#sec-proxy-object-internal-methods-and-internal-slots-get-p-receiver\npub(crate) fn proxy_exotic_try_get(\n    obj: &JsObject,\n    key: &PropertyKey,\n    receiver: JsValue,\n    context: &mut InternalMethodPropertyContext<'_>,\n) -> JsResult<Option<JsValue>> {\n    // Note: For now, this just calls the normal methods. Could be optimized further.\n    if proxy_exotic_has_property(obj, key, context)? {\n        Ok(Some(proxy_exotic_get(obj, key, receiver, context)?))\n    } else {\n        Ok(None)\n    }\n}\n\n/// `10.5.8 [[Get]] ( P, Receiver )`\n///\n/// More information:\n///  - [ECMAScript reference][spec]\n///\n/// [spec]: https://tc39.es/ecma262/#sec-proxy-object-internal-methods-and-internal-slots-get-p-receiver\npub(crate) fn proxy_exotic_get(\n    obj: &JsObject,\n    key: &PropertyKey,\n    receiver: JsValue,\n    context: &mut InternalMethodPropertyContext<'_>,\n) -> JsResult<JsValue> {\n    // Proxy object can't be cached.\n    context.slot().attributes |= SlotAttributes::NOT_CACHEABLE;\n\n    // 1. Let handler be O.[[ProxyHandler]].\n    // 2. If handler is null, throw a TypeError exception.\n    // 3. Assert: Type(handler) is Object.\n    // 4. Let target be O.[[ProxyTarget]].\n    let (target, handler) = obj\n        .downcast_ref::<Proxy>()\n        .js_expect(\"Proxy object internal internal method called on non-proxy object\")?\n        .try_data()?;\n\n    // 5. Let trap be ? GetMethod(handler, \"get\").\n    let Some(trap) = handler.get_method(js_string!(\"get\"), context)? else {\n        // 6. If trap is undefined, then\n        // a. Return ? target.[[Get]](P, Receiver).\n        return target.__get__(key, receiver, context);\n    };\n\n    // 7. Let trapResult be ? Call(trap, handler, « target, P, Receiver »).\n    let trap_result = trap.call(\n        &handler.into(),\n        &[target.clone().into(), key.clone().into(), receiver],\n        context,\n    )?;\n\n    // 8. Let targetDesc be ? target.[[GetOwnProperty]](P).\n    let target_desc = target.__get_own_property__(key, context)?;\n\n    // 9. If targetDesc is not undefined and targetDesc.[[Configurable]] is false, then\n    if let Some(target_desc) = target_desc\n        && !target_desc.expect_configurable()\n    {\n        // a. If IsDataDescriptor(targetDesc) is true and targetDesc.[[Writable]] is false, then\n        if target_desc.is_data_descriptor() && !target_desc.expect_writable() {\n            // i. If SameValue(trapResult, targetDesc.[[Value]]) is false, throw a TypeError exception.\n            if !JsValue::same_value(&trap_result, target_desc.expect_value()) {\n                return Err(JsNativeError::typ()\n                    .with_message(\"Proxy trap returned unexpected data descriptor\")\n                    .into());\n            }\n        }\n\n        // b. If IsAccessorDescriptor(targetDesc) is true and targetDesc.[[Get]] is undefined, then\n        if target_desc.is_accessor_descriptor() && target_desc.expect_get().is_undefined() {\n            // i. If trapResult is not undefined, throw a TypeError exception.\n            if !trap_result.is_undefined() {\n                return Err(JsNativeError::typ()\n                    .with_message(\"Proxy trap returned unexpected accessor descriptor\")\n                    .into());\n            }\n        }\n    }\n\n    // 10. Return trapResult.\n    Ok(trap_result)\n}\n\n/// `10.5.9 [[Set]] ( P, V, Receiver )`\n///\n/// More information:\n///  - [ECMAScript reference][spec]\n///\n/// [spec]: https://tc39.es/ecma262/#sec-proxy-object-internal-methods-and-internal-slots-set-p-v-receiver\npub(crate) fn proxy_exotic_set(\n    obj: &JsObject,\n    key: PropertyKey,\n    value: JsValue,\n    receiver: JsValue,\n    context: &mut InternalMethodPropertyContext<'_>,\n) -> JsResult<bool> {\n    context.slot().attributes |= SlotAttributes::NOT_CACHEABLE;\n\n    // 1. Let handler be O.[[ProxyHandler]].\n    // 2. If handler is null, throw a TypeError exception.\n    // 3. Assert: Type(handler) is Object.\n    // 4. Let target be O.[[ProxyTarget]].\n    let (target, handler) = obj\n        .downcast_ref::<Proxy>()\n        .js_expect(\"Proxy object internal internal method called on non-proxy object\")?\n        .try_data()?;\n\n    // 5. Let trap be ? GetMethod(handler, \"set\").\n    let Some(trap) = handler.get_method(js_string!(\"set\"), context)? else {\n        // 6. If trap is undefined, then\n        // a. Return ? target.[[Set]](P, V, Receiver).\n        return target.__set__(key, value, receiver, context);\n    };\n\n    // 7. Let booleanTrapResult be ! ToBoolean(? Call(trap, handler, « target, P, V, Receiver »)).\n    // 8. If booleanTrapResult is false, return false.\n    if !trap\n        .call(\n            &handler.into(),\n            &[\n                target.clone().into(),\n                key.clone().into(),\n                value.clone(),\n                receiver,\n            ],\n            context,\n        )?\n        .to_boolean()\n    {\n        return Ok(false);\n    }\n\n    // 9. Let targetDesc be ? target.[[GetOwnProperty]](P).\n    let target_desc = target.__get_own_property__(&key, context)?;\n\n    // 10. If targetDesc is not undefined and targetDesc.[[Configurable]] is false, then\n    if let Some(target_desc) = target_desc\n        && !target_desc.expect_configurable()\n    {\n        // a. If IsDataDescriptor(targetDesc) is true and targetDesc.[[Writable]] is false, then\n        if target_desc.is_data_descriptor() && !target_desc.expect_writable() {\n            // i. If SameValue(V, targetDesc.[[Value]]) is false, throw a TypeError exception.\n            if !JsValue::same_value(&value, target_desc.expect_value()) {\n                return Err(JsNativeError::typ()\n                    .with_message(\"Proxy trap set unexpected data descriptor\")\n                    .into());\n            }\n        }\n\n        // b. If IsAccessorDescriptor(targetDesc) is true, then\n        if target_desc.is_accessor_descriptor() {\n            // i. If targetDesc.[[Set]] is undefined, throw a TypeError exception.\n            match target_desc.set().map(JsValue::is_undefined) {\n                None | Some(true) => {\n                    return Err(JsNativeError::typ()\n                        .with_message(\"Proxy trap set unexpected accessor descriptor\")\n                        .into());\n                }\n                _ => {}\n            }\n        }\n    }\n\n    // 11. Return true.\n    Ok(true)\n}\n\n/// `10.5.10 [[Delete]] ( P )`\n///\n/// More information:\n///  - [ECMAScript reference][spec]\n///\n/// [spec]: https://tc39.es/ecma262/#sec-proxy-object-internal-methods-and-internal-slots-delete-p\npub(crate) fn proxy_exotic_delete(\n    obj: &JsObject,\n    key: &PropertyKey,\n    context: &mut InternalMethodPropertyContext<'_>,\n) -> JsResult<bool> {\n    // 1. Let handler be O.[[ProxyHandler]].\n    // 2. If handler is null, throw a TypeError exception.\n    // 3. Assert: Type(handler) is Object.\n    // 4. Let target be O.[[ProxyTarget]].\n    let (target, handler) = obj\n        .downcast_ref::<Proxy>()\n        .js_expect(\"Proxy object internal internal method called on non-proxy object\")?\n        .try_data()?;\n\n    // 5. Let trap be ? GetMethod(handler, \"deleteProperty\").\n    let Some(trap) = handler.get_method(js_string!(\"deleteProperty\"), context)? else {\n        // 6. If trap is undefined, then\n        // a. Return ? target.[[Delete]](P).\n        return target.__delete__(key, context);\n    };\n\n    // 7. Let booleanTrapResult be ! ToBoolean(? Call(trap, handler, « target, P »)).\n    // 8. If booleanTrapResult is false, return false.\n    if !trap\n        .call(\n            &handler.into(),\n            &[target.clone().into(), key.clone().into()],\n            context,\n        )?\n        .to_boolean()\n    {\n        return Ok(false);\n    }\n\n    // 9. Let targetDesc be ? target.[[GetOwnProperty]](P).\n    match target.__get_own_property__(key, context)? {\n        // 10. If targetDesc is undefined, return true.\n        None => return Ok(true),\n        // 11. If targetDesc.[[Configurable]] is false, throw a TypeError exception.\n        Some(target_desc) => {\n            if !target_desc.expect_configurable() {\n                return Err(JsNativeError::typ()\n                    .with_message(\"Proxy trap failed to delete property\")\n                    .into());\n            }\n        }\n    }\n\n    // 12. Let extensibleTarget be ? IsExtensible(target).\n    // 13. If extensibleTarget is false, throw a TypeError exception.\n    if !target.is_extensible(context)? {\n        return Err(JsNativeError::typ()\n            .with_message(\"Proxy trap failed to delete property\")\n            .into());\n    }\n\n    // 14. Return true.\n    Ok(true)\n}\n\n/// `10.5.11 [[OwnPropertyKeys]] ( )`\n///\n/// More information:\n///  - [ECMAScript reference][spec]\n///\n/// [spec]: https://tc39.es/ecma262/#sec-proxy-object-internal-methods-and-internal-slots-ownpropertykeys\npub(crate) fn proxy_exotic_own_property_keys(\n    obj: &JsObject,\n    context: &mut Context,\n) -> JsResult<Vec<PropertyKey>> {\n    // 1. Let handler be O.[[ProxyHandler]].\n    // 2. If handler is null, throw a TypeError exception.\n    // 3. Assert: Type(handler) is Object.\n    // 4. Let target be O.[[ProxyTarget]].\n    let (target, handler) = obj\n        .downcast_ref::<Proxy>()\n        .js_expect(\"Proxy object internal internal method called on non-proxy object\")?\n        .try_data()?;\n\n    // 5. Let trap be ? GetMethod(handler, \"ownKeys\").\n    let Some(trap) = handler.get_method(js_string!(\"ownKeys\"), context)? else {\n        // 6. If trap is undefined, then\n        // a. Return ? target.[[OwnPropertyKeys]]().\n        return target.__own_property_keys__(context);\n    };\n\n    // 7. Let trapResultArray be ? Call(trap, handler, « target »).\n    let trap_result_array = trap.call(&handler.into(), &[target.clone().into()], context)?;\n\n    // 8. Let trapResult be ? CreateListFromArrayLike(trapResultArray, « String, Symbol »).\n    let trap_result_raw =\n        trap_result_array.create_list_from_array_like(&[Type::String, Type::Symbol], context)?;\n\n    // 9. If trapResult contains any duplicate entries, throw a TypeError exception.\n    let mut unchecked_result_keys: FxHashSet<PropertyKey> = FxHashSet::default();\n    let mut trap_result = Vec::new();\n    for value in &trap_result_raw {\n        match value.variant() {\n            JsVariant::String(s) => {\n                if !unchecked_result_keys.insert(s.clone().into()) {\n                    return Err(JsNativeError::typ()\n                        .with_message(\"Proxy trap result contains duplicate string property keys\")\n                        .into());\n                }\n                trap_result.push(s.clone().into());\n            }\n            JsVariant::Symbol(s) => {\n                if !unchecked_result_keys.insert(s.clone().into()) {\n                    return Err(JsNativeError::typ()\n                        .with_message(\"Proxy trap result contains duplicate symbol property keys\")\n                        .into());\n                }\n                trap_result.push(s.clone().into());\n            }\n            _ => {}\n        }\n    }\n\n    // 10. Let extensibleTarget be ? IsExtensible(target).\n    let extensible_target = target.is_extensible(context)?;\n\n    // 11. Let targetKeys be ? target.[[OwnPropertyKeys]]().\n    // 12. Assert: targetKeys is a List of property keys.\n    // 13. Assert: targetKeys contains no duplicate entries.\n    let target_keys = target.__own_property_keys__(context)?;\n\n    // 14. Let targetConfigurableKeys be a new empty List.\n    // 15. Let targetNonconfigurableKeys be a new empty List.\n    let mut target_configurable_keys = Vec::new();\n    let mut target_nonconfigurable_keys = Vec::new();\n\n    // 16. For each element key of targetKeys, do\n    for key in target_keys {\n        // a. Let desc be ? target.[[GetOwnProperty]](key).\n        match target.__get_own_property__(&key, &mut context.into())? {\n            // b. If desc is not undefined and desc.[[Configurable]] is false, then\n            Some(desc) if !desc.expect_configurable() => {\n                // i. Append key as an element of targetNonconfigurableKeys.\n                target_nonconfigurable_keys.push(key);\n            }\n            // c. Else,\n            _ => {\n                // i. Append key as an element of targetConfigurableKeys.\n                target_configurable_keys.push(key);\n            }\n        }\n    }\n\n    // 17. If extensibleTarget is true and targetNonconfigurableKeys is empty, then\n    if extensible_target && target_nonconfigurable_keys.is_empty() {\n        // a. Return trapResult.\n        return Ok(trap_result);\n    }\n\n    // 18. Let uncheckedResultKeys be a List whose elements are the elements of trapResult.\n    // 19. For each element key of targetNonconfigurableKeys, do\n    for key in target_nonconfigurable_keys {\n        // a. If key is not an element of uncheckedResultKeys, throw a TypeError exception.\n        // b. Remove key from uncheckedResultKeys.\n        if !unchecked_result_keys.remove(&key) {\n            return Err(JsNativeError::typ()\n                .with_message(\"Proxy trap failed to return all non-configurable property keys\")\n                .into());\n        }\n    }\n\n    // 20. If extensibleTarget is true, return trapResult.\n    if extensible_target {\n        return Ok(trap_result);\n    }\n\n    // 21. For each element key of targetConfigurableKeys, do\n    for key in target_configurable_keys {\n        // a. If key is not an element of uncheckedResultKeys, throw a TypeError exception.\n        // b. Remove key from uncheckedResultKeys.\n        if !unchecked_result_keys.remove(&key) {\n            return Err(JsNativeError::typ()\n                .with_message(\"Proxy trap failed to return all configurable property keys\")\n                .into());\n        }\n    }\n\n    // 22. If uncheckedResultKeys is not empty, throw a TypeError exception.\n    if !unchecked_result_keys.is_empty() {\n        return Err(JsNativeError::typ()\n            .with_message(\"Proxy trap failed to return all property keys\")\n            .into());\n    }\n\n    // 23. Return trapResult.\n    Ok(trap_result)\n}\n\n/// `10.5.12 [[Call]] ( thisArgument, argumentsList )`\n///\n/// More information:\n///  - [ECMAScript reference][spec]\n///\n/// [spec]: https://tc39.es/ecma262/#sec-proxy-object-internal-methods-and-internal-slots-call-thisargument-argumentslist\nfn proxy_exotic_call(\n    obj: &JsObject,\n    argument_count: usize,\n    context: &mut InternalMethodCallContext<'_>,\n) -> JsResult<CallValue> {\n    // 1. Let handler be O.[[ProxyHandler]].\n    // 2. If handler is null, throw a TypeError exception.\n    // 3. Assert: Type(handler) is Object.\n    // 4. Let target be O.[[ProxyTarget]].\n    let (target, handler) = obj\n        .downcast_ref::<Proxy>()\n        .js_expect(\"Proxy object internal internal method called on non-proxy object\")?\n        .try_data()?;\n\n    // 5. Let trap be ? GetMethod(handler, \"apply\").\n    let Some(trap) = handler.get_method(js_string!(\"apply\"), context)? else {\n        // 6. If trap is undefined, then\n        // a. Return ? Call(target, thisArgument, argumentsList).\n        return Ok(target.__call__(argument_count));\n    };\n\n    let args = context\n        .vm\n        .stack\n        .calling_convention_pop_arguments(argument_count);\n\n    // 7. Let argArray be ! CreateArrayFromList(argumentsList).\n    let arg_array = array::Array::create_array_from_list(args, context);\n\n    // 8. Return ? Call(trap, handler, « target, thisArgument, argArray »).\n    let _func = context.vm.stack.pop();\n    let this = context.vm.stack.pop();\n\n    context.vm.stack.push(handler); // This\n    context.vm.stack.push(trap.clone()); // Function\n\n    context.vm.stack.push(target);\n    context.vm.stack.push(this);\n    context.vm.stack.push(arg_array);\n    Ok(trap.__call__(3))\n}\n\n/// `[[Construct]] ( argumentsList, newTarget )`\n///\n/// More information:\n///  - [ECMAScript reference][spec]\n///\n/// [spec]: https://tc39.es/ecma262/#sec-proxy-object-internal-methods-and-internal-slots-construct-argumentslist-newtarget\nfn proxy_exotic_construct(\n    obj: &JsObject,\n    argument_count: usize,\n    context: &mut InternalMethodCallContext<'_>,\n) -> JsResult<CallValue> {\n    // 1. Let handler be O.[[ProxyHandler]].\n    // 2. If handler is null, throw a TypeError exception.\n    // 3. Assert: Type(handler) is Object.\n    // 4. Let target be O.[[ProxyTarget]].\n    let (target, handler) = obj\n        .downcast_ref::<Proxy>()\n        .js_expect(\"Proxy object internal internal method called on non-proxy object\")?\n        .try_data()?;\n\n    // 5. Assert: IsConstructor(target) is true.\n    assert!(target.is_constructor());\n\n    // 6. Let trap be ? GetMethod(handler, \"construct\").\n    let Some(trap) = handler.get_method(js_string!(\"construct\"), context)? else {\n        // 7. If trap is undefined, then\n        // a. Return ? Construct(target, argumentsList, newTarget).\n        return Ok(target.__construct__(argument_count));\n    };\n\n    let new_target = context.vm.stack.pop();\n    let args = context\n        .vm\n        .stack\n        .calling_convention_pop_arguments(argument_count);\n    let _func = context.vm.stack.pop();\n    let _this = context.vm.stack.pop();\n\n    // 8. Let argArray be ! CreateArrayFromList(argumentsList).\n    let arg_array = array::Array::create_array_from_list(args, context);\n\n    // 9. Let newObj be ? Call(trap, handler, « target, argArray, newTarget »).\n    let new_obj = trap.call(\n        &handler.into(),\n        &[target.into(), arg_array.into(), new_target],\n        context,\n    )?;\n\n    // 10. If Type(newObj) is not Object, throw a TypeError exception.\n    let new_obj = new_obj.as_object().ok_or_else(|| {\n        JsNativeError::typ().with_message(\"Proxy trap constructor returned non-object value\")\n    })?;\n\n    // 11. Return newObj.\n    context.vm.stack.push(new_obj);\n    Ok(CallValue::Complete)\n}\n\n#[cfg(test)]\nmod tests;\n"
  },
  {
    "path": "core/engine/src/builtins/proxy/tests.rs",
    "content": "use crate::{JsNativeErrorKind, TestAction, run_test_actions};\nuse indoc::indoc;\n\n#[test]\nfn proxy_cannot_report_non_configurable_as_configurable() {\n    run_test_actions([TestAction::assert_native_error(\n        indoc! {r#\"\n            const target = {};\n            Object.defineProperty(target, 'foo', { value: 1, configurable: false });\n\n            const proxy = new Proxy(target, {\n              getOwnPropertyDescriptor() {\n                return { value: 1, configurable: true };\n              }\n            });\n\n            Object.getOwnPropertyDescriptor(proxy, 'foo');\n        \"#},\n        JsNativeErrorKind::Type,\n        \"Proxy trap returned unexpected property\",\n    )]);\n}\n\n#[test]\nfn proxy_cannot_hide_non_configurable_property() {\n    run_test_actions([TestAction::assert_native_error(\n        indoc! {r#\"\n            const target = {};\n            Object.defineProperty(target, 'foo', { value: 1, configurable: false });\n\n            const proxy = new Proxy(target, {\n              has() { return false; }\n            });\n\n            'foo' in proxy;\n        \"#},\n        JsNativeErrorKind::Type,\n        \"Proxy trap returned unexpected property\",\n    )]);\n}\n\n#[test]\nfn proxy_cannot_report_extensible_as_non_extensible() {\n    run_test_actions([TestAction::assert_native_error(\n        indoc! {r#\"\n            const proxy = new Proxy({}, {\n              isExtensible() { return false; }\n            });\n\n            Object.isExtensible(proxy);\n        \"#},\n        JsNativeErrorKind::Type,\n        \"Proxy trap returned unexpected extensible value\",\n    )]);\n}\n\n#[test]\nfn proxy_cannot_report_non_extensible_as_extensible() {\n    run_test_actions([TestAction::assert_native_error(\n        indoc! {r#\"\n            const target = Object.preventExtensions({});\n\n            const proxy = new Proxy(target, {\n              isExtensible() { return true; }\n            });\n\n            Object.isExtensible(proxy);\n        \"#},\n        JsNativeErrorKind::Type,\n        \"Proxy trap returned unexpected extensible value\",\n    )]);\n}\n\n#[test]\nfn proxy_ownkeys_must_include_non_configurable_keys() {\n    run_test_actions([TestAction::assert_native_error(\n        indoc! {r#\"\n            const target = {};\n            Object.defineProperty(target, 'foo', { value: 1, configurable: false });\n\n            const proxy = new Proxy(target, {\n              ownKeys() { return []; }\n            });\n\n            Object.keys(proxy);\n        \"#},\n        JsNativeErrorKind::Type,\n        \"Proxy trap failed to return all non-configurable property keys\",\n    )]);\n}\n\n#[test]\nfn proxy_ownkeys_cannot_report_duplicate_keys() {\n    run_test_actions([TestAction::assert_native_error(\n        indoc! {r#\"\n            const proxy = new Proxy({}, {\n              ownKeys() { return ['a','a']; }\n            });\n\n            Object.keys(proxy);\n        \"#},\n        JsNativeErrorKind::Type,\n        \"Proxy trap result contains duplicate string property keys\",\n    )]);\n}\n\n#[test]\nfn proxy_defineproperty_respects_target_invariants() {\n    run_test_actions([TestAction::assert_native_error(\n        indoc! {r#\"\n            const target = Object.preventExtensions({});\n\n            const proxy = new Proxy(target, {\n              defineProperty() { return true; }\n            });\n\n            Object.defineProperty(proxy, 'foo', { value: 1 });\n        \"#},\n        JsNativeErrorKind::Type,\n        \"Proxy trap failed to set property\",\n    )]);\n}\n\n#[test]\nfn proxy_getprototypeof_invariant() {\n    run_test_actions([TestAction::assert_native_error(\n        indoc! {r#\"\n            const target = Object.preventExtensions({});\n\n            const proxy = new Proxy(target, {\n              getPrototypeOf() { return Array.prototype; }\n            });\n\n            Object.getPrototypeOf(proxy);\n        \"#},\n        JsNativeErrorKind::Type,\n        \"Proxy trap returned unexpected prototype\",\n    )]);\n}\n\n#[test]\nfn proxy_setprototypeof_invariant() {\n    run_test_actions([TestAction::assert_native_error(\n        indoc! {r#\"\n            const target = Object.preventExtensions({});\n\n            const proxy = new Proxy(target, {\n              setPrototypeOf() { return true; }\n            });\n\n            Object.setPrototypeOf(proxy, Array.prototype);\n        \"#},\n        JsNativeErrorKind::Type,\n        \"Proxy trap failed to set prototype\",\n    )]);\n}\n\n#[test]\nfn proxy_preventextensions_invariant() {\n    run_test_actions([TestAction::assert_native_error(\n        indoc! {r#\"\n            const proxy = new Proxy({}, {\n              preventExtensions() { return true; }\n            });\n\n            Object.preventExtensions(proxy);\n        \"#},\n        JsNativeErrorKind::Type,\n        \"Proxy trap failed to set extensible\",\n    )]);\n}\n\n#[test]\nfn proxy_ownkeys_symbol_invariant() {\n    run_test_actions([TestAction::assert_native_error(\n        indoc! {r#\"\n            const sym = Symbol(\"a\");\n\n            const target = {};\n            Object.defineProperty(target, sym, {\n              value: 1,\n              configurable: false\n            });\n\n            const proxy = new Proxy(target, {\n              ownKeys() { return []; }\n            });\n\n            Reflect.ownKeys(proxy);\n        \"#},\n        JsNativeErrorKind::Type,\n        \"Proxy trap failed to return all non-configurable property keys\",\n    )]);\n}\n\n#[test]\nfn proxy_ownkeys_non_extensible_invariant() {\n    run_test_actions([TestAction::assert_native_error(\n        indoc! {r#\"\n            const target = Object.preventExtensions({ a: 1 });\n\n            const proxy = new Proxy(target, {\n              ownKeys() { return []; }\n            });\n\n            Object.keys(proxy);\n        \"#},\n        JsNativeErrorKind::Type,\n        \"Proxy trap failed to return all configurable property keys\",\n    )]);\n}\n"
  },
  {
    "path": "core/engine/src/builtins/reflect/mod.rs",
    "content": "//! Boa's implementation of ECMAScript's global `Reflect` object.\n//!\n//! The `Reflect` global object is a built-in object that provides methods for interceptable\n//! ECMAScript operations.\n//!\n//! More information:\n//!  - [ECMAScript reference][spec]\n//!  - [MDN documentation][mdn]\n//!\n//! [spec]: https://tc39.es/ecma262/#sec-reflect-object\n//! [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Reflect\n\nuse super::{Array, BuiltInBuilder, IntrinsicObject};\nuse crate::value::JsVariant;\nuse crate::{\n    Context, JsArgs, JsResult, JsString, JsValue,\n    builtins::{self, BuiltInObject},\n    context::intrinsics::Intrinsics,\n    error::JsNativeError,\n    js_string,\n    object::{JsObject, internal_methods::InternalMethodPropertyContext},\n    property::Attribute,\n    realm::Realm,\n    string::StaticJsStrings,\n    symbol::JsSymbol,\n};\n\n#[cfg(test)]\nmod tests;\n\n/// Javascript `Reflect` object.\n#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]\npub(crate) struct Reflect;\n\nimpl IntrinsicObject for Reflect {\n    fn init(realm: &Realm) {\n        let to_string_tag = JsSymbol::to_string_tag();\n\n        BuiltInBuilder::with_intrinsic::<Self>(realm)\n            .static_method(Self::apply, js_string!(\"apply\"), 3)\n            .static_method(Self::construct, js_string!(\"construct\"), 2)\n            .static_method(Self::define_property, js_string!(\"defineProperty\"), 3)\n            .static_method(Self::delete_property, js_string!(\"deleteProperty\"), 2)\n            .static_method(Self::get, js_string!(\"get\"), 2)\n            .static_method(\n                Self::get_own_property_descriptor,\n                js_string!(\"getOwnPropertyDescriptor\"),\n                2,\n            )\n            .static_method(Self::get_prototype_of, js_string!(\"getPrototypeOf\"), 1)\n            .static_method(Self::has, js_string!(\"has\"), 2)\n            .static_method(Self::is_extensible, js_string!(\"isExtensible\"), 1)\n            .static_method(Self::own_keys, js_string!(\"ownKeys\"), 1)\n            .static_method(Self::prevent_extensions, js_string!(\"preventExtensions\"), 1)\n            .static_method(Self::set, js_string!(\"set\"), 3)\n            .static_method(Self::set_prototype_of, js_string!(\"setPrototypeOf\"), 2)\n            .static_property(\n                to_string_tag,\n                Self::NAME,\n                Attribute::READONLY | Attribute::NON_ENUMERABLE | Attribute::CONFIGURABLE,\n            )\n            .build();\n    }\n\n    fn get(intrinsics: &Intrinsics) -> JsObject {\n        intrinsics.objects().reflect()\n    }\n}\n\nimpl BuiltInObject for Reflect {\n    const NAME: JsString = StaticJsStrings::REFLECT;\n}\n\nimpl Reflect {\n    /// Calls a target function with arguments.\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///  - [MDN documentation][mdn]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-reflect.apply\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Reflect/apply\n    pub(crate) fn apply(_: &JsValue, args: &[JsValue], context: &mut Context) -> JsResult<JsValue> {\n        let target = args\n            .first()\n            .and_then(JsValue::as_object)\n            .ok_or_else(|| JsNativeError::typ().with_message(\"target must be a function\"))?;\n        let this_arg = args.get_or_undefined(1);\n        let args_list = args.get_or_undefined(2);\n\n        if !target.is_callable() {\n            return Err(JsNativeError::typ()\n                .with_message(\"target must be a function\")\n                .into());\n        }\n        let args = args_list.create_list_from_array_like(&[], context)?;\n        target.call(this_arg, &args, context)\n    }\n\n    /// Calls a target function as a constructor with arguments.\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///  - [MDN documentation][mdn]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-reflect.construct\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Reflect/construct\n    pub(crate) fn construct(\n        _: &JsValue,\n        args: &[JsValue],\n        context: &mut Context,\n    ) -> JsResult<JsValue> {\n        // 1. If IsConstructor(target) is false, throw a TypeError exception.\n        let target = args\n            .get_or_undefined(0)\n            .as_constructor()\n            .ok_or_else(|| JsNativeError::typ().with_message(\"target must be a constructor\"))?;\n\n        let new_target = if let Some(new_target) = args.get(2) {\n            // 3. Else if IsConstructor(newTarget) is false, throw a TypeError exception.\n            new_target.as_constructor().ok_or_else(|| {\n                JsNativeError::typ().with_message(\"newTarget must be a constructor\")\n            })?\n        } else {\n            // 2. If newTarget is not present, set newTarget to target.\n            target.clone()\n        };\n\n        // 4. Let args be ? CreateListFromArrayLike(argumentsList).\n        let args = args\n            .get_or_undefined(1)\n            .create_list_from_array_like(&[], context)?;\n\n        // 5. Return ? Construct(target, args, newTarget).\n        target\n            .construct(&args, Some(&new_target), context)\n            .map(JsValue::from)\n    }\n\n    /// Defines a property on an object.\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///  - [MDN documentation][mdn]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-reflect.defineProperty\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Reflect/defineProperty\n    pub(crate) fn define_property(\n        _: &JsValue,\n        args: &[JsValue],\n        context: &mut Context,\n    ) -> JsResult<JsValue> {\n        let target = args\n            .first()\n            .and_then(JsValue::as_object)\n            .ok_or_else(|| JsNativeError::typ().with_message(\"target must be an object\"))?;\n        let key = args.get_or_undefined(1).to_property_key(context)?;\n        let prop_desc: JsValue = args\n            .get(2)\n            .and_then(JsValue::as_object)\n            .ok_or_else(|| {\n                JsNativeError::typ().with_message(\"property descriptor must be an object\")\n            })?\n            .into();\n\n        target\n            .__define_own_property__(\n                &key,\n                prop_desc.to_property_descriptor(context)?,\n                &mut InternalMethodPropertyContext::new(context),\n            )\n            .map(Into::into)\n    }\n\n    /// Defines a property on an object.\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///  - [MDN documentation][mdn]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-reflect.deleteproperty\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Reflect/deleteProperty\n    pub(crate) fn delete_property(\n        _: &JsValue,\n        args: &[JsValue],\n        context: &mut Context,\n    ) -> JsResult<JsValue> {\n        let target = args\n            .first()\n            .and_then(JsValue::as_object)\n            .ok_or_else(|| JsNativeError::typ().with_message(\"target must be an object\"))?;\n        let key = args.get_or_undefined(1).to_property_key(context)?;\n\n        Ok(target\n            .__delete__(&key, &mut InternalMethodPropertyContext::new(context))?\n            .into())\n    }\n\n    /// Gets a property of an object.\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///  - [MDN documentation][mdn]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-reflect.get\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Reflect/get\n    pub(crate) fn get(_: &JsValue, args: &[JsValue], context: &mut Context) -> JsResult<JsValue> {\n        // 1. If Type(target) is not Object, throw a TypeError exception.\n        let target = args\n            .first()\n            .and_then(JsValue::as_object)\n            .ok_or_else(|| JsNativeError::typ().with_message(\"target must be an object\"))?;\n        // 2. Let key be ? ToPropertyKey(propertyKey).\n        let key = args.get_or_undefined(1).to_property_key(context)?;\n        // 3. If receiver is not present, then\n        // 3.a. Set receiver to target.\n        let receiver = args\n            .get(2)\n            .cloned()\n            .unwrap_or_else(|| target.clone().into());\n        // 4. Return ? target.[[Get]](key, receiver).\n\n        target.__get__(\n            &key,\n            receiver,\n            &mut InternalMethodPropertyContext::new(context),\n        )\n    }\n\n    /// Gets a property of an object.\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///  - [MDN documentation][mdn]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-reflect.getownpropertydescriptor\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Reflect/getOwnPropertyDescriptor\n    pub(crate) fn get_own_property_descriptor(\n        _: &JsValue,\n        args: &[JsValue],\n        context: &mut Context,\n    ) -> JsResult<JsValue> {\n        if args.get_or_undefined(0).is_object() {\n            // This function is the same as Object.prototype.getOwnPropertyDescriptor, that why\n            // it is invoked here.\n            builtins::object::OrdinaryObject::get_own_property_descriptor(\n                &JsValue::undefined(),\n                args,\n                context,\n            )\n        } else {\n            Err(JsNativeError::typ()\n                .with_message(\"target must be an object\")\n                .into())\n        }\n    }\n\n    /// Gets the prototype of an object.\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///  - [MDN documentation][mdn]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-reflect.getprototypeof\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Reflect/getPrototypeOf\n    pub(crate) fn get_prototype_of(\n        _: &JsValue,\n        args: &[JsValue],\n        context: &mut Context,\n    ) -> JsResult<JsValue> {\n        let target = args\n            .first()\n            .and_then(JsValue::as_object)\n            .ok_or_else(|| JsNativeError::typ().with_message(\"target must be an object\"))?;\n        Ok(target\n            .__get_prototype_of__(&mut InternalMethodPropertyContext::new(context))?\n            .map_or(JsValue::null(), JsValue::new))\n    }\n\n    /// Returns `true` if the object has the property, `false` otherwise.\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///  - [MDN documentation][mdn]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-reflect.has\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Reflect/has\n    pub(crate) fn has(_: &JsValue, args: &[JsValue], context: &mut Context) -> JsResult<JsValue> {\n        let target = args\n            .first()\n            .and_then(JsValue::as_object)\n            .ok_or_else(|| JsNativeError::typ().with_message(\"target must be an object\"))?;\n        let key = args\n            .get(1)\n            .unwrap_or(&JsValue::undefined())\n            .to_property_key(context)?;\n\n        Ok(target\n            .__has_property__(&key, &mut InternalMethodPropertyContext::new(context))?\n            .into())\n    }\n\n    /// Returns `true` if the object is extensible, `false` otherwise.\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///  - [MDN documentation][mdn]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-reflect.isextensible\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Reflect/isExtensible\n    pub(crate) fn is_extensible(\n        _: &JsValue,\n        args: &[JsValue],\n        context: &mut Context,\n    ) -> JsResult<JsValue> {\n        let target = args\n            .first()\n            .and_then(JsValue::as_object)\n            .ok_or_else(|| JsNativeError::typ().with_message(\"target must be an object\"))?;\n        Ok(target\n            .__is_extensible__(&mut InternalMethodPropertyContext::new(context))?\n            .into())\n    }\n\n    /// Returns an array of object own property keys.\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///  - [MDN documentation][mdn]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-reflect.ownkeys\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Reflect/ownKeys\n    pub(crate) fn own_keys(\n        _: &JsValue,\n        args: &[JsValue],\n        context: &mut Context,\n    ) -> JsResult<JsValue> {\n        let target = args\n            .first()\n            .and_then(JsValue::as_object)\n            .ok_or_else(|| JsNativeError::typ().with_message(\"target must be an object\"))?;\n\n        let keys: Vec<JsValue> = target\n            .__own_property_keys__(&mut InternalMethodPropertyContext::new(context))?\n            .into_iter()\n            .map(Into::into)\n            .collect();\n\n        Ok(Array::create_array_from_list(keys, context).into())\n    }\n\n    /// Prevents new properties from ever being added to an object.\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///  - [MDN documentation][mdn]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-reflect.preventextensions\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Reflect/preventExtensions\n    pub(crate) fn prevent_extensions(\n        _: &JsValue,\n        args: &[JsValue],\n        context: &mut Context,\n    ) -> JsResult<JsValue> {\n        let target = args\n            .first()\n            .and_then(JsValue::as_object)\n            .ok_or_else(|| JsNativeError::typ().with_message(\"target must be an object\"))?;\n\n        Ok(target\n            .__prevent_extensions__(&mut InternalMethodPropertyContext::new(context))?\n            .into())\n    }\n\n    /// Sets a property of an object.\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///  - [MDN documentation][mdn]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-reflect.set\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Reflect/set\n    pub(crate) fn set(_: &JsValue, args: &[JsValue], context: &mut Context) -> JsResult<JsValue> {\n        let target = args\n            .first()\n            .and_then(JsValue::as_object)\n            .ok_or_else(|| JsNativeError::typ().with_message(\"target must be an object\"))?;\n        let key = args.get_or_undefined(1).to_property_key(context)?;\n        let value = args.get_or_undefined(2);\n        let receiver = args\n            .get(3)\n            .cloned()\n            .unwrap_or_else(|| target.clone().into());\n\n        Ok(target\n            .__set__(\n                key,\n                value.clone(),\n                receiver,\n                &mut InternalMethodPropertyContext::new(context),\n            )?\n            .into())\n    }\n\n    /// Sets the prototype of an object.\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///  - [MDN documentation][mdn]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-reflect.setprototypeof\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Reflect/setPrototypeOf\n    pub(crate) fn set_prototype_of(\n        _: &JsValue,\n        args: &[JsValue],\n        context: &mut Context,\n    ) -> JsResult<JsValue> {\n        let target = args\n            .first()\n            .and_then(JsValue::as_object)\n            .ok_or_else(|| JsNativeError::typ().with_message(\"target must be an object\"))?;\n        let proto = match args.get_or_undefined(1).variant() {\n            JsVariant::Object(obj) => Some(obj.clone()),\n            JsVariant::Null => None,\n            _ => {\n                return Err(JsNativeError::typ()\n                    .with_message(\"proto must be an object or null\")\n                    .into());\n            }\n        };\n        Ok(target\n            .__set_prototype_of__(proto, &mut InternalMethodPropertyContext::new(context))?\n            .into())\n    }\n}\n"
  },
  {
    "path": "core/engine/src/builtins/reflect/tests.rs",
    "content": "use crate::{JsValue, TestAction, run_test_actions};\nuse boa_macros::js_str;\nuse indoc::indoc;\n\n#[test]\nfn apply() {\n    run_test_actions([\n        TestAction::run(indoc! {r#\"\n                var called = {};\n                function f(n) { called.result = n };\n                Reflect.apply(f, undefined, [42]);\n            \"#}),\n        TestAction::assert_eq(\"called.result\", 42),\n    ]);\n}\n\n#[test]\nfn construct() {\n    run_test_actions([\n        TestAction::run(indoc! {r#\"\n                var called = {};\n                function f(n) { called.result = n };\n                Reflect.construct(f, [42]);\n            \"#}),\n        TestAction::assert_eq(\"called.result\", 42),\n    ]);\n}\n\n#[test]\nfn define_property() {\n    run_test_actions([\n        TestAction::run(indoc! {r#\"\n                let obj = {};\n                Reflect.defineProperty(obj, 'p', { value: 42 });\n            \"#}),\n        TestAction::assert_eq(\"obj.p\", 42),\n    ]);\n}\n\n#[test]\nfn delete_property() {\n    run_test_actions([\n        TestAction::run(\"let obj = { p: 42 };\"),\n        TestAction::assert(\"Reflect.deleteProperty(obj, 'p')\"),\n        TestAction::assert_eq(\"obj.p\", JsValue::undefined()),\n    ]);\n}\n\n#[test]\nfn get() {\n    run_test_actions([\n        TestAction::run(\"let obj = { p: 42 };\"),\n        TestAction::assert_eq(\"Reflect.get(obj, 'p')\", 42),\n    ]);\n}\n\n#[test]\nfn get_own_property_descriptor() {\n    run_test_actions([\n        TestAction::run(\"let obj = { p: 42 };\"),\n        TestAction::assert_eq(\"Reflect.getOwnPropertyDescriptor(obj, 'p').value\", 42),\n    ]);\n}\n\n#[test]\nfn get_prototype_of() {\n    run_test_actions([\n        TestAction::run(indoc! {r#\"\n                function F() { this.p = 42 };\n                let f = new F();\n            \"#}),\n        TestAction::assert_eq(\"Reflect.getPrototypeOf(f).constructor.name\", js_str!(\"F\")),\n    ]);\n}\n\n#[test]\nfn has() {\n    run_test_actions([\n        TestAction::run(\"let obj = { p: 42 };\"),\n        TestAction::assert(\"Reflect.has(obj, 'p')\"),\n        TestAction::assert(\"!Reflect.has(obj, 'p2')\"),\n    ]);\n}\n\n#[test]\nfn is_extensible() {\n    run_test_actions([\n        TestAction::run(\"let obj = { p: 42 };\"),\n        TestAction::assert(\"Reflect.isExtensible(obj)\"),\n    ]);\n}\n\n#[test]\nfn own_keys() {\n    run_test_actions([\n        TestAction::run_harness(),\n        TestAction::run(\"let obj = { p: 42 };\"),\n        TestAction::assert(indoc! {r#\"\n                arrayEquals(\n                    Reflect.ownKeys(obj),\n                    [\"p\"]\n                )\n            \"#}),\n    ]);\n}\n\n#[test]\nfn prevent_extensions() {\n    run_test_actions([\n        TestAction::run(\"let obj = { p: 42 };\"),\n        TestAction::assert(\"Reflect.preventExtensions(obj)\"),\n        TestAction::assert(\"!Reflect.isExtensible(obj)\"),\n    ]);\n}\n\n#[test]\nfn set() {\n    run_test_actions([\n        TestAction::run(indoc! {r#\"\n                let obj = {};\n                Reflect.set(obj, 'p', 42);\n            \"#}),\n        TestAction::assert_eq(\"obj.p\", 42),\n    ]);\n}\n\n#[test]\nfn set_prototype_of() {\n    run_test_actions([\n        TestAction::run(indoc! {r#\"\n                function F() { this.p = 42 };\n                let obj = {}\n                Reflect.setPrototypeOf(obj, F);\n            \"#}),\n        TestAction::assert_eq(\"Reflect.getPrototypeOf(obj).name\", js_str!(\"F\")),\n    ]);\n}\n"
  },
  {
    "path": "core/engine/src/builtins/regexp/mod.rs",
    "content": "//! Boa's implementation of ECMAScript's global `RegExp` object.\n//!\n//! The `RegExp` object is used for matching text with a pattern.\n//!\n//! More information:\n//!  - [ECMAScript reference][spec]\n//!  - [MDN documentation][mdn]\n//!\n//! [spec]: https://tc39.es/ecma262/#sec-regexp-constructor\n//! [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp\n\nuse crate::{\n    Context, JsArgs, JsData, JsResult, JsString,\n    builtins::{BuiltInObject, array::Array, string},\n    context::intrinsics::{Intrinsics, StandardConstructor, StandardConstructors},\n    error::JsNativeError,\n    js_string,\n    object::{CONSTRUCTOR, JsObject, internal_methods::get_prototype_from_constructor},\n    property::Attribute,\n    realm::Realm,\n    string::{CodePoint, CommonJsStringBuilder, JsStrVariant, StaticJsStrings},\n    symbol::JsSymbol,\n    value::JsValue,\n};\nuse boa_gc::{Finalize, Trace};\nuse boa_macros::{js_str, utf16};\nuse boa_parser::lexer::regex::RegExpFlags;\nuse regress::{Flags, Range, Regex};\nuse std::str::FromStr;\n\nuse super::{BuiltInBuilder, BuiltInConstructor, IntrinsicObject};\n\nmod regexp_string_iterator;\npub(crate) use regexp_string_iterator::RegExpStringIterator;\n#[cfg(test)]\nmod tests;\n\n/// The internal representation of a `RegExp` object.\n#[derive(Debug, Clone, Trace, Finalize, JsData)]\n// Safety: `RegExp` does not contain any objects which needs to be traced, so this is safe.\n#[boa_gc(unsafe_empty_trace)]\npub struct RegExp {\n    /// Regex matcher.\n    matcher: Regex,\n    flags: RegExpFlags,\n    original_source: JsString,\n    original_flags: JsString,\n}\n\nimpl RegExp {\n    /// Returns the original source string of the regex (e.g. `\"regex-test\"`).\n    pub(crate) fn original_source(&self) -> &JsString {\n        &self.original_source\n    }\n\n    /// Returns the original flags string of the regex (e.g. `\"gi\"`).\n    pub(crate) fn original_flags(&self) -> &JsString {\n        &self.original_flags\n    }\n}\n\nimpl IntrinsicObject for RegExp {\n    fn init(realm: &Realm) {\n        let get_species = BuiltInBuilder::callable(realm, Self::get_species)\n            .name(js_string!(\"get [Symbol.species]\"))\n            .build();\n\n        let flag_attributes = Attribute::CONFIGURABLE | Attribute::NON_ENUMERABLE;\n\n        let get_has_indices = BuiltInBuilder::callable(realm, Self::get_has_indices)\n            .name(js_string!(\"get hasIndices\"))\n            .build();\n        let get_global = BuiltInBuilder::callable(realm, Self::get_global)\n            .name(js_string!(\"get global\"))\n            .build();\n        let get_ignore_case = BuiltInBuilder::callable(realm, Self::get_ignore_case)\n            .name(js_string!(\"get ignoreCase\"))\n            .build();\n        let get_multiline = BuiltInBuilder::callable(realm, Self::get_multiline)\n            .name(js_string!(\"get multiline\"))\n            .build();\n        let get_dot_all = BuiltInBuilder::callable(realm, Self::get_dot_all)\n            .name(js_string!(\"get dotAll\"))\n            .build();\n        let get_unicode = BuiltInBuilder::callable(realm, Self::get_unicode)\n            .name(js_string!(\"get unicode\"))\n            .build();\n        let get_unicode_sets = BuiltInBuilder::callable(realm, Self::get_unicode_sets)\n            .name(js_string!(\"get unicodeSets\"))\n            .build();\n        let get_sticky = BuiltInBuilder::callable(realm, Self::get_sticky)\n            .name(js_string!(\"get sticky\"))\n            .build();\n        let get_flags = BuiltInBuilder::callable(realm, Self::get_flags)\n            .name(js_string!(\"get flags\"))\n            .build();\n        let get_source = BuiltInBuilder::callable(realm, Self::get_source)\n            .name(js_string!(\"get source\"))\n            .build();\n        let regexp = BuiltInBuilder::from_standard_constructor::<Self>(realm)\n            .static_method(Self::escape, js_string!(\"escape\"), 1)\n            .static_accessor(\n                JsSymbol::species(),\n                Some(get_species),\n                None,\n                Attribute::CONFIGURABLE,\n            )\n            .property(js_string!(\"lastIndex\"), 0, Attribute::all())\n            .method(Self::test, js_string!(\"test\"), 1)\n            .method(Self::exec, js_string!(\"exec\"), 1)\n            .method(Self::to_string, js_string!(\"toString\"), 0)\n            .method(Self::r#match, JsSymbol::r#match(), 1)\n            .method(Self::match_all, JsSymbol::match_all(), 1)\n            .method(Self::replace, JsSymbol::replace(), 2)\n            .method(Self::search, JsSymbol::search(), 1)\n            .method(Self::split, JsSymbol::split(), 2)\n            .accessor(\n                js_string!(\"hasIndices\"),\n                Some(get_has_indices),\n                None,\n                flag_attributes,\n            )\n            .accessor(\n                js_string!(\"global\"),\n                Some(get_global),\n                None,\n                flag_attributes,\n            )\n            .accessor(\n                js_string!(\"ignoreCase\"),\n                Some(get_ignore_case),\n                None,\n                flag_attributes,\n            )\n            .accessor(\n                js_string!(\"multiline\"),\n                Some(get_multiline),\n                None,\n                flag_attributes,\n            )\n            .accessor(\n                js_string!(\"dotAll\"),\n                Some(get_dot_all),\n                None,\n                flag_attributes,\n            )\n            .accessor(\n                js_string!(\"unicode\"),\n                Some(get_unicode),\n                None,\n                flag_attributes,\n            )\n            .accessor(\n                js_string!(\"unicodeSets\"),\n                Some(get_unicode_sets),\n                None,\n                flag_attributes,\n            )\n            .accessor(\n                js_string!(\"sticky\"),\n                Some(get_sticky),\n                None,\n                flag_attributes,\n            )\n            .accessor(js_string!(\"flags\"), Some(get_flags), None, flag_attributes)\n            .accessor(\n                js_string!(\"source\"),\n                Some(get_source),\n                None,\n                flag_attributes,\n            );\n\n        #[cfg(feature = \"annex-b\")]\n        let regexp = regexp.method(Self::compile, js_string!(\"compile\"), 2);\n\n        regexp.build();\n    }\n\n    fn get(intrinsics: &Intrinsics) -> JsObject {\n        Self::STANDARD_CONSTRUCTOR(intrinsics.constructors()).constructor()\n    }\n}\n\nimpl BuiltInObject for RegExp {\n    const NAME: JsString = StaticJsStrings::REG_EXP;\n}\n\nimpl BuiltInConstructor for RegExp {\n    const CONSTRUCTOR_ARGUMENTS: usize = 2;\n    const PROTOTYPE_STORAGE_SLOTS: usize = 30;\n    const CONSTRUCTOR_STORAGE_SLOTS: usize = 3;\n\n    const STANDARD_CONSTRUCTOR: fn(&StandardConstructors) -> &StandardConstructor =\n        StandardConstructors::regexp;\n\n    /// `22.2.3.1 RegExp ( pattern, flags )`\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-regexp-pattern-flags\n    fn constructor(\n        new_target: &JsValue,\n        args: &[JsValue],\n        context: &mut Context,\n    ) -> JsResult<JsValue> {\n        let pattern = args.get_or_undefined(0);\n        let flags = args.get_or_undefined(1);\n\n        // 1. Let patternIsRegExp be ? IsRegExp(pattern).\n        let pattern_is_regexp = Self::is_reg_exp(pattern, context)?;\n\n        // 2. If NewTarget is undefined, then\n        // 3. Else, let newTarget be NewTarget.\n        if new_target.is_undefined() {\n            // a. Let newTarget be the active function object.\n            let new_target = context\n                .active_function_object()\n                .map_or(JsValue::undefined(), JsValue::new);\n\n            // b. If patternIsRegExp is true and flags is undefined, then\n            if let Some(pattern) = &pattern_is_regexp\n                && flags.is_undefined()\n            {\n                // i. Let patternConstructor be ? Get(pattern, \"constructor\").\n                let pattern_constructor = pattern.get(CONSTRUCTOR, context)?;\n\n                // ii. If SameValue(newTarget, patternConstructor) is true, return pattern.\n                if JsValue::same_value(&new_target, &pattern_constructor) {\n                    return Ok(pattern.clone().into());\n                }\n            }\n        }\n\n        // 4. If pattern is an Object and pattern has a [[RegExpMatcher]] internal slot, then\n        let object = pattern.clone().as_object();\n        let (p, f) =\n            if let Some(pattern) = object.as_ref().and_then(JsObject::downcast_ref::<RegExp>) {\n                // a. Let P be pattern.[[OriginalSource]].\n                let p = pattern.original_source.clone().into();\n\n                // b. If flags is undefined, let F be pattern.[[OriginalFlags]].\n                let f = if flags.is_undefined() {\n                    pattern.original_flags.clone().into()\n                // c. Else, let F be flags.\n                } else {\n                    flags.clone()\n                };\n\n                (p, f)\n            } else if let Some(pattern) = &pattern_is_regexp {\n                // a. Let P be ? Get(pattern, \"source\").\n                let p = pattern.get(js_string!(\"source\"), context)?;\n\n                // b. If flags is undefined, then\n                let f = if flags.is_undefined() {\n                    // i. Let F be ? Get(pattern, \"flags\").\n                    pattern.get(js_string!(\"flags\"), context)?\n                // c. Else,\n                } else {\n                    // i. Let F be flags.\n                    flags.clone()\n                };\n\n                (p, f)\n            // 6. Else,\n            } else {\n                // a. Let P be pattern.\n                // b. Let F be flags.\n                (pattern.clone(), flags.clone())\n            };\n\n        // 7. Let O be ? RegExpAlloc(newTarget).\n        let proto =\n            get_prototype_from_constructor(new_target, StandardConstructors::regexp, context)?;\n\n        // 8.Return ? RegExpInitialize(O, P, F).\n        Self::initialize(Some(proto), &p, &f, context)\n    }\n}\n\nimpl RegExp {\n    /// `7.2.8 IsRegExp ( argument )`\n    ///\n    /// This modified to return the object if it's `true`, [`None`] otherwise.\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-isregexp\n    pub(crate) fn is_reg_exp(\n        argument: &JsValue,\n        context: &mut Context,\n    ) -> JsResult<Option<JsObject>> {\n        // 1. If argument is not an Object, return false.\n        let Some(argument) = argument.as_object() else {\n            return Ok(None);\n        };\n\n        // 2. Let matcher be ? Get(argument, @@match).\n        let matcher = argument.get(JsSymbol::r#match(), context)?;\n\n        // 3. If matcher is not undefined, return ToBoolean(matcher).\n        if !matcher.is_undefined() {\n            return Ok(matcher.to_boolean().then_some(argument));\n        }\n\n        // 4. If argument has a [[RegExpMatcher]] internal slot, return true.\n        if argument.is::<RegExp>() {\n            return Ok(Some(argument));\n        }\n\n        // 5. Return false.\n        Ok(None)\n    }\n\n    /// Compiles a `RegExp` from the provided pattern and flags.\n    ///\n    /// Equivalent to the beginning of [`RegExpInitialize ( obj, pattern, flags )`][spec]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-regexpinitialize\n    fn compile_native_regexp(\n        pattern: &JsValue,\n        flags: &JsValue,\n        context: &mut Context,\n    ) -> JsResult<RegExp> {\n        // 1. If pattern is undefined, let P be the empty String.\n        // 2. Else, let P be ? ToString(pattern).\n        let p = if pattern.is_undefined() {\n            js_string!()\n        } else {\n            pattern.to_string(context)?\n        };\n\n        // 3. If flags is undefined, let F be the empty String.\n        // 4. Else, let F be ? ToString(flags).\n        let f = if flags.is_undefined() {\n            js_string!()\n        } else {\n            flags.to_string(context)?\n        };\n\n        // 5. If F contains any code unit other than \"g\", \"i\", \"m\", \"s\", \"u\", \"v\", or \"y\"\n        //    or if it contains the same code unit more than once, throw a SyntaxError exception.\n        // TODO: Should directly parse the JsString instead of converting to String\n        let flags = match RegExpFlags::from_str(&f.to_std_string_escaped()) {\n            Err(msg) => return Err(JsNativeError::syntax().with_message(msg).into()),\n            Ok(result) => result,\n        };\n\n        // 13. Let parseResult be ParsePattern(patternText, u, v).\n        // 14. If parseResult is a non-empty List of SyntaxError objects, throw a SyntaxError exception.\n\n        // If u or v flag is set, fullUnicode is true — compile as full codepoints.\n        let full_unicode =\n            flags.contains(RegExpFlags::UNICODE) || flags.contains(RegExpFlags::UNICODE_SETS);\n\n        let matcher = if full_unicode {\n            // Unicode mode (u/v flag) OR pattern has named groups:\n            // compile as full Unicode codepoints.\n            Regex::from_unicode(p.code_points().map(CodePoint::as_u32), Flags::from(flags))\n                .map_err(|error| {\n                    JsNativeError::syntax()\n                        .with_message(format!(\"failed to create matcher: {}\", error.text))\n                })?\n        } else {\n            // Non-Unicode mode with no named groups:\n            // compile as raw UTF-16 code units so that surrogate pairs\n            // (e.g. 𠮷 = [0xD842, 0xDFB7]) are matched correctly by find_from_ucs2.\n            let utf16_units = p.code_points().flat_map(|cp| {\n                let mut buf = [0u16; 2];\n                match cp {\n                    CodePoint::Unicode(c) => c\n                        .encode_utf16(&mut buf)\n                        .iter()\n                        .map(|&u| u32::from(u))\n                        .collect::<Vec<_>>(),\n                    CodePoint::UnpairedSurrogate(s) => vec![u32::from(s)],\n                }\n            });\n            Regex::from_unicode(utf16_units, Flags::from(flags)).map_err(|error| {\n                JsNativeError::syntax()\n                    .with_message(format!(\"failed to create matcher: {}\", error.text))\n            })?\n        };\n\n        // 15. Assert: parseResult is a Pattern Parse Node.\n        // 16. Set obj.[[OriginalSource]] to P.\n        // 17. Set obj.[[OriginalFlags]] to F.\n        // 18. Let capturingGroupsCount be CountLeftCapturingParensWithin(parseResult).\n        // 19. Let rer be the RegExp Record { [[IgnoreCase]]: i, [[Multiline]]: m,\n        //     [[DotAll]]: s, [[Unicode]]: u, [[UnicodeSets]]: v,\n        //     [[CapturingGroupsCount]]: capturingGroupsCount }.\n        // 20. Set obj.[[RegExpRecord]] to rer.\n        // 21. Set obj.[[RegExpMatcher]] to CompilePattern of parseResult with argument rer.\n        Ok(RegExp {\n            matcher,\n            flags,\n            original_source: p,\n            original_flags: f,\n        })\n    }\n\n    /// `RegExpInitialize ( obj, pattern, flags )`\n    ///\n    /// If prototype is `None`, initializes the prototype to `%RegExp%.prototype`.\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-regexpinitialize\n    pub(crate) fn initialize(\n        prototype: Option<JsObject>,\n        pattern: &JsValue,\n        flags: &JsValue,\n        context: &mut Context,\n    ) -> JsResult<JsValue> {\n        // Has the steps  of `RegExpInitialize`.\n        let regexp = Self::compile_native_regexp(pattern, flags, context)?;\n\n        // 22. Perform ? Set(obj, \"lastIndex\", +0𝔽, true).\n        let obj = if let Some(prototype) = prototype {\n            let mut template = context\n                .intrinsics()\n                .templates()\n                .regexp_without_proto()\n                .clone();\n            template.set_prototype(prototype);\n            template.create(regexp, vec![0.into()])\n        } else {\n            context\n                .intrinsics()\n                .templates()\n                .regexp()\n                .create(regexp, vec![0.into()])\n        };\n\n        // 23. Return obj.\n        Ok(obj.into())\n    }\n\n    /// `22.2.3.2.4 RegExpCreate ( P, F )`\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-regexpcreate\n    pub(crate) fn create(p: &JsValue, f: &JsValue, context: &mut Context) -> JsResult<JsValue> {\n        // 1. Let obj be ? RegExpAlloc(%RegExp%).\n        // 2. Return ? RegExpInitialize(obj, P, F).\n        Self::initialize(None, p, f, context)\n    }\n\n    /// `get RegExp [ @@species ]`\n    ///\n    /// The `RegExp [ @@species ]` accessor property returns the `RegExp` constructor.\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///  - [MDN documentation][mdn]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-get-regexp-@@species\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp/@@species\n    #[allow(clippy::unnecessary_wraps)]\n    fn get_species(this: &JsValue, _: &[JsValue], _: &mut Context) -> JsResult<JsValue> {\n        // 1. Return the this value.\n        Ok(this.clone())\n    }\n\n    fn regexp_has_flag(this: &JsValue, flag: u8, context: &mut Context) -> JsResult<JsValue> {\n        if let Some(object) = this.as_object() {\n            if let Some(regexp) = object.downcast_ref::<RegExp>() {\n                return Ok(JsValue::new(match flag {\n                    b'd' => regexp.flags.contains(RegExpFlags::HAS_INDICES),\n                    b'g' => regexp.flags.contains(RegExpFlags::GLOBAL),\n                    b'm' => regexp.flags.contains(RegExpFlags::MULTILINE),\n                    b's' => regexp.flags.contains(RegExpFlags::DOT_ALL),\n                    b'i' => regexp.flags.contains(RegExpFlags::IGNORE_CASE),\n                    b'u' => regexp.flags.contains(RegExpFlags::UNICODE),\n                    b'v' => regexp.flags.contains(RegExpFlags::UNICODE_SETS),\n                    b'y' => regexp.flags.contains(RegExpFlags::STICKY),\n                    _ => unreachable!(),\n                }));\n            }\n\n            if JsObject::equals(\n                &object,\n                &context.intrinsics().constructors().regexp().prototype(),\n            ) {\n                return Ok(JsValue::undefined());\n            }\n        }\n\n        let name = match flag {\n            b'd' => \"hasIndices\",\n            b'g' => \"global\",\n            b'm' => \"multiline\",\n            b's' => \"dotAll\",\n            b'i' => \"ignoreCase\",\n            b'u' => \"unicode\",\n            b'v' => \"unicodeSets\",\n            b'y' => \"sticky\",\n            _ => unreachable!(),\n        };\n\n        Err(JsNativeError::typ()\n            .with_message(format!(\n                \"RegExp.prototype.{name} getter called on non-RegExp object\",\n            ))\n            .into())\n    }\n\n    /// `get RegExp.prototype.hasIndices`\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///  - [MDN documentation][mdn]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-get-regexp.prototype.hasindices\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp/global\n    pub(crate) fn get_has_indices(\n        this: &JsValue,\n        _: &[JsValue],\n        context: &mut Context,\n    ) -> JsResult<JsValue> {\n        Self::regexp_has_flag(this, b'd', context)\n    }\n\n    /// `get RegExp.prototype.global`\n    ///\n    /// The `global` property indicates whether or not the \"`g`\" flag is used with the regular expression.\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///  - [MDN documentation][mdn]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-get-regexp.prototype.global\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp/global\n    pub(crate) fn get_global(\n        this: &JsValue,\n        _: &[JsValue],\n        context: &mut Context,\n    ) -> JsResult<JsValue> {\n        Self::regexp_has_flag(this, b'g', context)\n    }\n\n    /// `get RegExp.prototype.ignoreCase`\n    ///\n    /// The `ignoreCase` property indicates whether or not the \"`i`\" flag is used with the regular expression.\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///  - [MDN documentation][mdn]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-get-regexp.prototype.ignorecase\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp/ignoreCase\n    pub(crate) fn get_ignore_case(\n        this: &JsValue,\n        _: &[JsValue],\n        context: &mut Context,\n    ) -> JsResult<JsValue> {\n        Self::regexp_has_flag(this, b'i', context)\n    }\n\n    /// `get RegExp.prototype.multiline`\n    ///\n    /// The multiline property indicates whether or not the \"m\" flag is used with the regular expression.\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///  - [MDN documentation][mdn]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-get-regexp.prototype.multiline\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp/multiline\n    pub(crate) fn get_multiline(\n        this: &JsValue,\n        _: &[JsValue],\n        context: &mut Context,\n    ) -> JsResult<JsValue> {\n        Self::regexp_has_flag(this, b'm', context)\n    }\n\n    /// `get RegExp.prototype.dotAll`\n    ///\n    /// The `dotAll` property indicates whether or not the \"`s`\" flag is used with the regular expression.\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///  - [MDN documentation][mdn]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-get-regexp.prototype.dotAll\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp/dotAll\n    pub(crate) fn get_dot_all(\n        this: &JsValue,\n        _: &[JsValue],\n        context: &mut Context,\n    ) -> JsResult<JsValue> {\n        Self::regexp_has_flag(this, b's', context)\n    }\n\n    /// `get RegExp.prototype.unicode`\n    ///\n    /// The unicode property indicates whether or not the \"`u`\" flag is used with a regular expression.\n    /// unicode is a read-only property of an individual regular expression instance.\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///  - [MDN documentation][mdn]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-get-regexp.prototype.unicode\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp/unicode\n    pub(crate) fn get_unicode(\n        this: &JsValue,\n        _: &[JsValue],\n        context: &mut Context,\n    ) -> JsResult<JsValue> {\n        Self::regexp_has_flag(this, b'u', context)\n    }\n\n    /// `get RegExp.prototype.unicodeSets`\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///  - [MDN documentation][mdn]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-get-regexp.prototype.unicodesets\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp/unicodeSets\n    pub(crate) fn get_unicode_sets(\n        this: &JsValue,\n        _: &[JsValue],\n        context: &mut Context,\n    ) -> JsResult<JsValue> {\n        Self::regexp_has_flag(this, b'v', context)\n    }\n\n    /// `get RegExp.prototype.sticky`\n    ///\n    /// This flag indicates that it matches only from the index indicated by the `lastIndex` property\n    /// of this regular expression in the target string (and does not attempt to match from any later indexes).\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///  - [MDN documentation][mdn]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-get-regexp.prototype.sticky\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp/sticky\n    pub(crate) fn get_sticky(\n        this: &JsValue,\n        _: &[JsValue],\n        context: &mut Context,\n    ) -> JsResult<JsValue> {\n        Self::regexp_has_flag(this, b'y', context)\n    }\n\n    /// `get RegExp.prototype.flags`\n    ///\n    /// The `flags` property returns a string consisting of the [`flags`][flags] of the current regular expression object.\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///  - [MDN documentation][mdn]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-get-regexp.prototype.flags\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp/flags\n    /// [flags]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_Expressions#Advanced_searching_with_flags_2\n    pub(crate) fn get_flags(\n        this: &JsValue,\n        _: &[JsValue],\n        context: &mut Context,\n    ) -> JsResult<JsValue> {\n        // 1. Let R be the this value.\n        // 2. If R is not an Object, throw a TypeError exception.\n        let Some(object) = this.as_object() else {\n            return Err(JsNativeError::typ()\n                .with_message(\"RegExp.prototype.flags getter called on non-object\")\n                .into());\n        };\n\n        // 3. Let codeUnits be a new empty List.\n        let mut code_units = String::new();\n\n        // 4. Let hasIndices be ToBoolean(? Get(R, \"hasIndices\")).\n        // 5. If hasIndices is true, append the code unit 0x0064 (LATIN SMALL LETTER D) to codeUnits.\n        if object.get(js_string!(\"hasIndices\"), context)?.to_boolean() {\n            code_units.push('d');\n        }\n\n        // 6. Let global be ToBoolean(? Get(R, \"global\")).\n        // 7. If global is true, append the code unit 0x0067 (LATIN SMALL LETTER G) to codeUnits.\n        if object.get(js_string!(\"global\"), context)?.to_boolean() {\n            code_units.push('g');\n        }\n\n        // 8. Let ignoreCase be ToBoolean(? Get(R, \"ignoreCase\")).\n        // 9. If ignoreCase is true, append the code unit 0x0069 (LATIN SMALL LETTER I) to codeUnits.\n        if object.get(js_string!(\"ignoreCase\"), context)?.to_boolean() {\n            code_units.push('i');\n        }\n\n        // 10. Let multiline be ToBoolean(? Get(R, \"multiline\")).\n        // 11. If multiline is true, append the code unit 0x006D (LATIN SMALL LETTER M) to codeUnits.\n        if object.get(js_string!(\"multiline\"), context)?.to_boolean() {\n            code_units.push('m');\n        }\n\n        // 12. Let dotAll be ToBoolean(? Get(R, \"dotAll\")).\n        // 13. If dotAll is true, append the code unit 0x0073 (LATIN SMALL LETTER S) to codeUnits.\n        if object.get(js_string!(\"dotAll\"), context)?.to_boolean() {\n            code_units.push('s');\n        }\n\n        // 14. Let unicode be ToBoolean(? Get(R, \"unicode\")).\n        // 15. If unicode is true, append the code unit 0x0075 (LATIN SMALL LETTER U) to codeUnits.\n        if object.get(js_string!(\"unicode\"), context)?.to_boolean() {\n            code_units.push('u');\n        }\n\n        // 16. Let unicodeSets be ToBoolean(? Get(R, \"unicodeSets\")).\n        // 17. If unicodeSets is true, append the code unit 0x0076 (LATIN SMALL LETTER V) to codeUnits.\n        if object.get(js_string!(\"unicodeSets\"), context)?.to_boolean() {\n            code_units.push('v');\n        }\n\n        // 18. Let sticky be ToBoolean(? Get(R, \"sticky\")).\n        // 19. If sticky is true, append the code unit 0x0079 (LATIN SMALL LETTER Y) to codeUnits.\n        if object.get(js_string!(\"sticky\"), context)?.to_boolean() {\n            code_units.push('y');\n        }\n\n        // 20. Return the String value whose code units are the elements of the List codeUnits.\n        //     If codeUnits has no elements, the empty String is returned.\n        Ok(JsString::from(code_units).into())\n    }\n\n    /// `get RegExp.prototype.source`\n    ///\n    /// The `source` property returns a `String` containing the source text of the regexp object,\n    /// and it doesn't contain the two forward slashes on both sides and any flags.\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///  - [MDN documentation][mdn]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-get-regexp.prototype.source\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp/source\n    pub(crate) fn get_source(\n        this: &JsValue,\n        _: &[JsValue],\n        context: &mut Context,\n    ) -> JsResult<JsValue> {\n        // 1. Let R be the this value.\n        // 2. If Type(R) is not Object, throw a TypeError exception.\n        let Some(object) = this.as_object() else {\n            return Err(JsNativeError::typ()\n                .with_message(\"RegExp.prototype.source method called on incompatible value\")\n                .into());\n        };\n\n        let casted = object.downcast_ref::<RegExp>();\n        match casted {\n            // 3. If R does not have an [[OriginalSource]] internal slot, then\n            None => {\n                // a. If SameValue(R, %RegExp.prototype%) is true, return \"(?:)\".\n                // b. Otherwise, throw a TypeError exception.\n                if JsValue::same_value(\n                    this,\n                    &JsValue::new(context.intrinsics().constructors().regexp().prototype()),\n                ) {\n                    Ok(JsValue::new(js_string!(\"(?:)\")))\n                } else {\n                    Err(JsNativeError::typ()\n                        .with_message(\"RegExp.prototype.source method called on incompatible value\")\n                        .into())\n                }\n            }\n            // 4. Assert: R has an [[OriginalFlags]] internal slot.\n            Some(re) => {\n                // 5. Let src be R.[[OriginalSource]].\n                // 6. Let flags be R.[[OriginalFlags]].\n                // 7. Return EscapeRegExpPattern(src, flags).\n                Ok(Self::escape_pattern(\n                    &re.original_source,\n                    &re.original_flags,\n                ))\n            }\n        }\n    }\n\n    /// `22.2.3.2.5 EscapeRegExpPattern ( P, F )`\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-escaperegexppattern\n    fn escape_pattern(src: &JsString, _flags: &JsString) -> JsValue {\n        if src.is_empty() {\n            js_string!(\"(?:)\").into()\n        } else {\n            let mut s = Vec::with_capacity(src.len());\n            let mut buf = [0; 2];\n            for c in src.code_points() {\n                match c {\n                    CodePoint::Unicode('/') => s.extend_from_slice(utf16!(r\"\\/\")),\n                    CodePoint::Unicode('\\n') => s.extend_from_slice(utf16!(r\"\\n\")),\n                    CodePoint::Unicode('\\r') => s.extend_from_slice(utf16!(r\"\\r\")),\n                    CodePoint::Unicode('\\u{2028}') => s.extend_from_slice(utf16!(r\"\\u2028\")),\n                    CodePoint::Unicode('\\u{2029}') => s.extend_from_slice(utf16!(r\"\\u2029\")),\n                    CodePoint::Unicode(c) => s.extend_from_slice(c.encode_utf16(&mut buf)),\n                    CodePoint::UnpairedSurrogate(surr) => s.push(surr),\n                }\n            }\n\n            JsValue::new(js_string!(&s[..]))\n        }\n    }\n\n    /// `RegExp.escape( string )`\n    ///\n    /// The `RegExp.escape()` static method escapes any potential regex syntax characters in a string,\n    /// and returns a new string that can be safely used as a literal pattern for the `RegExp()` constructor.\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///  - [MDN documentation][mdn]\n    ///\n    /// [spec]: https://tc39.es/proposal-regex-escaping/#sec-regexp.escape\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp/escape\n    ///\n    /// Helper function to check if a character is a `WhiteSpace` character\n    fn is_whitespace(ch: char) -> bool {\n        matches!(\n            ch,\n            '\\u{0009}' | // <TAB>\n            '\\u{000B}' | // <VT>\n            '\\u{000C}' | // <FF>\n            '\\u{0020}' | // <SP>\n            '\\u{00A0}' | // <NBSP>\n            '\\u{FEFF}' | // <ZWNBSP>\n            '\\u{1680}' | // Ogham Space Mark\n            '\\u{2000}' | '\\u{2001}' | '\\u{2002}' | '\\u{2003}' | '\\u{2004}' |\n            '\\u{2005}' | '\\u{2006}' | '\\u{2007}' | '\\u{2008}' | '\\u{2009}' |\n            '\\u{200A}' | // Various space separators\n            '\\u{202F}' | // Narrow No-Break Space\n            '\\u{205F}' | // Medium Mathematical Space\n            '\\u{3000}' // Ideographic Space\n        )\n    }\n\n    /// Helper function to check if a character is a `LineTerminator` character\n    fn is_line_terminator(ch: char) -> bool {\n        matches!(\n            ch,\n            '\\u{000A}' | // <LF>\n            '\\u{000D}' | // <CR>\n            '\\u{2028}' | // <LS>\n            '\\u{2029}' // <PS>\n        )\n    }\n\n    pub(crate) fn escape(_: &JsValue, args: &[JsValue], _: &mut Context) -> JsResult<JsValue> {\n        let arg = args.get_or_undefined(0);\n\n        // 1. If S is not a String, throw a TypeError exception.\n        let Some(string) = arg.as_string() else {\n            return Err(JsNativeError::typ()\n                .with_message(\"RegExp.escape requires a string argument\")\n                .into());\n        };\n\n        // 2. Let escaped be the empty String.\n        let mut escaped = CommonJsStringBuilder::new();\n\n        // 3. Let cpList be StringToCodePoints(S).\n        // 4. For each code point c of cpList, do\n        for (index, c) in string.code_points().enumerate() {\n            let code = c.as_u32();\n\n            // 4.a. If escaped is the empty String and c is matched by either DecimalDigit or AsciiLetter, then\n            if index == 0\n                && let CodePoint::Unicode(ch) = c\n                && (ch.is_ascii_digit() || ch.is_ascii_alphabetic())\n            {\n                // 4.a.ii-v. Escape using \\xXX format\n                let escape_seq = format!(\"\\\\x{code:02x}\");\n                escaped.push(escape_seq.as_str());\n                continue;\n            }\n\n            // 4.b. Else, set escaped to the string-concatenation of escaped and EncodeForRegExpEscape(c).\n            match c {\n                CodePoint::Unicode(ch) => {\n                    // EncodeForRegExpEscape step 1: SyntaxCharacter or U+002F (SOLIDUS)\n                    if matches!(\n                        ch,\n                        '^' | '$'\n                            | '\\\\'\n                            | '.'\n                            | '*'\n                            | '+'\n                            | '?'\n                            | '('\n                            | ')'\n                            | '['\n                            | ']'\n                            | '{'\n                            | '}'\n                            | '|'\n                            | '/'\n                    ) {\n                        escaped.push('\\\\');\n                        escaped.push(ch);\n                    }\n                    // Step 2: ControlEscape characters (Table 64)\n                    else if ch == '\\x09' {\n                        escaped.push(\"\\\\t\");\n                    } else if ch == '\\x0A' {\n                        escaped.push(\"\\\\n\");\n                    } else if ch == '\\x0B' {\n                        escaped.push(\"\\\\v\");\n                    } else if ch == '\\x0C' {\n                        escaped.push(\"\\\\f\");\n                    } else if ch == '\\x0D' {\n                        escaped.push(\"\\\\r\");\n                    }\n                    // Step 3-5: otherPunctuators or WhiteSpace or LineTerminator\n                    else if matches!(\n                        ch,\n                        ',' | '-'\n                            | '='\n                            | '<'\n                            | '>'\n                            | '#'\n                            | '&'\n                            | '!'\n                            | '%'\n                            | ':'\n                            | ';'\n                            | '@'\n                            | '~'\n                            | '\\''\n                            | '`'\n                            | '\"'\n                    ) || Self::is_whitespace(ch)\n                        || Self::is_line_terminator(ch)\n                    {\n                        let code = ch as u32;\n                        if code <= 0xFF {\n                            // Use \\xXX format\n                            let escape_seq = format!(\"\\\\x{code:02x}\");\n                            escaped.push(escape_seq.as_str());\n                        } else {\n                            // Use \\uXXXX format\n                            let escape_seq = format!(\"\\\\u{code:04x}\");\n                            escaped.push(escape_seq.as_str());\n                        }\n                    }\n                    // Step 6: All other Unicode characters\n                    else {\n                        escaped.push(ch);\n                    }\n                }\n                CodePoint::UnpairedSurrogate(surr) => {\n                    // Escape unpaired surrogates using \\uXXXX format\n                    let escape_seq = format!(\"\\\\u{surr:04x}\");\n                    escaped.push(escape_seq.as_str());\n                }\n            }\n        }\n\n        // 5. Return escaped.\n        Ok(JsValue::new(escaped.build()))\n    }\n\n    /// `RegExp.prototype.test( string )`\n    ///\n    /// The `test()` method executes a search for a match between a regular expression and a specified string.\n    ///\n    /// Returns `true` or `false`.\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///  - [MDN documentation][mdn]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-regexp.prototype.test\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp/test\n    pub(crate) fn test(\n        this: &JsValue,\n        args: &[JsValue],\n        context: &mut Context,\n    ) -> JsResult<JsValue> {\n        // 1. Let R be the this value.\n        // 2. If Type(R) is not Object, throw a TypeError exception.\n        let this = this.as_object().ok_or_else(|| {\n            JsNativeError::typ()\n                .with_message(\"RegExp.prototype.test method called on incompatible value\")\n        })?;\n\n        // 3. Let string be ? ToString(S).\n        let arg_str = args\n            .first()\n            .cloned()\n            .unwrap_or_default()\n            .to_string(context)?;\n\n        // 4. Let match be ? RegExpExec(R, string).\n        let m = Self::abstract_exec(&this, arg_str, context)?;\n\n        // 5. If match is not null, return true; else return false.\n        if m.is_some() {\n            Ok(JsValue::new(true))\n        } else {\n            Ok(JsValue::new(false))\n        }\n    }\n\n    /// `RegExp.prototype.exec( string )`\n    ///\n    /// The `exec()` method executes a search for a match in a specified string.\n    ///\n    /// Returns a result array, or `null`.\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///  - [MDN documentation][mdn]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-regexp.prototype.exec\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp/exec\n    pub(crate) fn exec(\n        this: &JsValue,\n        args: &[JsValue],\n        context: &mut Context,\n    ) -> JsResult<JsValue> {\n        // 1. Let R be the this value.\n        // 2. Perform ? RequireInternalSlot(R, [[RegExpMatcher]]).\n        let this = this.as_object();\n        let obj = this\n            .and_then(|o| o.downcast::<RegExp>().ok())\n            .ok_or_else(|| {\n                JsNativeError::typ().with_message(\"RegExp.prototype.exec called with invalid value\")\n            })?;\n\n        // 3. Let S be ? ToString(string).\n        let arg_str = args.get_or_undefined(0).to_string(context)?;\n\n        // 4. Return ? RegExpBuiltinExec(R, S).\n        (Self::abstract_builtin_exec(obj, &arg_str, context)?)\n            .map_or_else(|| Ok(JsValue::null()), |v| Ok(v.into()))\n    }\n\n    /// `22.2.5.2.1 RegExpExec ( R, S )`\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-regexpexec\n    pub(crate) fn abstract_exec(\n        this: &JsObject,\n        input: JsString,\n        context: &mut Context,\n    ) -> JsResult<Option<JsObject>> {\n        // 1. Assert: Type(R) is Object.\n        // 2. Assert: Type(S) is String.\n\n        // 3. Let exec be ? Get(R, \"exec\").\n        let exec = this.get(js_string!(\"exec\"), context)?;\n\n        // 4. If IsCallable(exec) is true, then\n        if let Some(exec) = exec.as_callable() {\n            // a. Let result be ? Call(exec, R, « S »).\n            let result = exec.call(&this.clone().into(), &[input.into()], context)?;\n\n            // b. If Type(result) is neither Object nor Null, throw a TypeError exception.\n            if !result.is_object() && !result.is_null() {\n                return Err(JsNativeError::typ()\n                    .with_message(\"regexp exec returned neither object nor null\")\n                    .into());\n            }\n\n            // c. Return result.\n            return Ok(result.as_object());\n        }\n\n        // 5. Perform ? RequireInternalSlot(R, [[RegExpMatcher]]).\n        let Ok(this) = this.clone().downcast::<RegExp>() else {\n            return Err(JsNativeError::typ()\n                .with_message(\"RegExpExec called with invalid value\")\n                .into());\n        };\n\n        // 6. Return ? RegExpBuiltinExec(R, S).\n        Self::abstract_builtin_exec(this, &input, context)\n    }\n\n    /// `22.2.7.2 RegExpBuiltinExec ( R, S )`\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-regexpbuiltinexec\n    pub(crate) fn abstract_builtin_exec(\n        this: JsObject<RegExp>,\n        input: &JsString,\n        context: &mut Context,\n    ) -> JsResult<Option<JsObject>> {\n        let rx = this.borrow().data().clone();\n        let this = this.upcast();\n\n        // 1. Let length be the length of S.\n        let length = input.len() as u64;\n\n        // 2. Let lastIndex be ℝ(? ToLength(? Get(R, \"lastIndex\"))).\n        let mut last_index = this\n            .get(js_string!(\"lastIndex\"), context)?\n            .to_length(context)?;\n\n        // 3. Let flags be R.[[OriginalFlags]].\n        let flags = &rx.original_flags;\n\n        // 4. If flags contains \"g\", let global be true; else let global be false.\n        let global = flags.contains(b'g');\n\n        // 5. If flags contains \"y\", let sticky be true; else let sticky be false.\n        let sticky = flags.contains(b'y');\n\n        // 6. If flags contains \"d\", let hasIndices be true; else let hasIndices be false.\n        let has_indices = flags.contains(b'd');\n\n        // 7. If global is false and sticky is false, set lastIndex to 0.\n        if !global && !sticky {\n            last_index = 0;\n        }\n\n        // 8. Let matcher be R.[[RegExpMatcher]].\n        let matcher = &rx.matcher;\n\n        // 9. If flags contains \"u\" or flags contains \"v\", let fullUnicode be true; else let fullUnicode be false.\n        let full_unicode = flags.contains(b'u') || flags.contains(b'v');\n\n        // NOTE: The following steps are take care of by regress:\n        //\n        // SKIP: 10. Let matchSucceeded be false.\n        // SKIP: 11. If fullUnicode is true, let input be StringToCodePoints(S). Otherwise, let input be a List whose elements are the code units that are the elements of S.\n        // SKIP: 12. NOTE: Each element of input is considered to be a character.\n        // SKIP: 13. Repeat, while matchSucceeded is false,\n\n        // 13.a. If lastIndex > length, then\n        if last_index > length {\n            // i. If global is true or sticky is true, then\n            if global || sticky {\n                // 1. Perform ? Set(R, \"lastIndex\", +0𝔽, true).\n                this.set(js_string!(\"lastIndex\"), 0, true, context)?;\n            }\n\n            // ii. Return null.\n            return Ok(None);\n        }\n\n        // 13.b. Let inputIndex be the index into input of the character that was obtained from element lastIndex of S.\n        // 13.c. Let r be matcher(input, inputIndex).\n        let r: Option<regress::Match> = match (full_unicode, input.as_str().variant()) {\n            (true | false, JsStrVariant::Latin1(_)) => {\n                // TODO: Currently regress does not support latin1 encoding.\n                let input = input.to_vec();\n\n                // NOTE: We can use the faster ucs2 variant since there will never be two byte unicode.\n                matcher.find_from_ucs2(&input, last_index as usize).next()\n            }\n            (true, JsStrVariant::Utf16(input)) => {\n                matcher.find_from_utf16(input, last_index as usize).next()\n            }\n            (false, JsStrVariant::Utf16(input)) => {\n                matcher.find_from_ucs2(input, last_index as usize).next()\n            }\n        };\n\n        let Some(match_value) = r else {\n            // d. If r is failure, then\n            //\n            // NOTE: Merged the following steps (since we no longer have a loop):\n            //       13.d.i. If sticky is true, then\n            //       13.a.i. If global is true or sticky is true, then\n            if global || sticky {\n                // 1. Perform ? Set(R, \"lastIndex\", +0𝔽, true).\n                this.set(js_string!(\"lastIndex\"), 0, true, context)?;\n            }\n\n            // MOVE: ii. Set lastIndex to AdvanceStringIndex(S, lastIndex, fullUnicode).\n            // NOTE: Handled within the regress matches iterator, see below for last_index assignment.\n\n            // NOTE: Merged  and  steps:\n            //       13.a.ii.  Return null.\n            //       13.d.i.2. Return null.\n            return Ok(None);\n        };\n\n        // e. Else\n        // SKIP: i. Assert: r is a MatchState.\n        // SKIP: ii. Set matchSucceeded to true.\n\n        // NOTE: regress currently doesn't support the sticky flag so we have to emulate it.\n        if sticky && match_value.start() != last_index as usize {\n            // 1. Perform ? Set(R, \"lastIndex\", +0𝔽, true).\n            this.set(js_string!(\"lastIndex\"), 0, true, context)?;\n\n            // 2. Return null.\n            return Ok(None);\n        }\n\n        // 13.d.ii. Set lastIndex to AdvanceStringIndex(S, lastIndex, fullUnicode).\n        // NOTE: Calculation of last_index is done in regress.\n        last_index = match_value.start() as u64;\n\n        // 14. Let e be r's endIndex value.\n        // 15. If fullUnicode is true, set e to GetStringIndex(S, e).\n        // NOTE: Step 15 is already taken care of by regress.\n        let e = match_value.end();\n\n        // 16. If global is true or sticky is true, then\n        if global || sticky {\n            // a. Perform ? Set(R, \"lastIndex\", 𝔽(e), true).\n            this.set(js_string!(\"lastIndex\"), e, true, context)?;\n        }\n\n        // 17. Let n be the number of elements in r's captures List.\n        let n = match_value.captures.len() as u64;\n        // 18. Assert: n = R.[[RegExpRecord]].[[CapturingGroupsCount]].\n        // 19. Assert: n < 232 - 1.\n        debug_assert!(n < (1u64 << 32) - 1);\n\n        // 20. Let A be ! ArrayCreate(n + 1).\n        // 21. Assert: The mathematical value of A's \"length\" property is n + 1.\n        let a = Array::array_create(n + 1, None, context)?;\n\n        // 22. Perform ! CreateDataPropertyOrThrow(A, \"index\", 𝔽(lastIndex)).\n        a.create_data_property_or_throw(js_string!(\"index\"), last_index, context)?;\n\n        // 23. Perform ! CreateDataPropertyOrThrow(A, \"input\", S).\n        a.create_data_property_or_throw(js_string!(\"input\"), input.clone(), context)?;\n\n        // 24. Let match be the Match Record { [[StartIndex]]: lastIndex, [[EndIndex]]: e }.\n        // Immediately convert it to an array according to 22.2.7.7 GetMatchIndexPair(S, match)\n        // 1. Assert: match.[[StartIndex]] ≤ match.[[EndIndex]] ≤ the length of S.\n        // 2. Return CreateArrayFromList(« 𝔽(match.[[StartIndex]]), 𝔽(match.[[EndIndex]]) »).\n        let match_record = Array::create_array_from_list(\n            [match_value.start().into(), match_value.end().into()],\n            context,\n        );\n\n        // 25. Let indices be a new empty List.\n        let indices = Array::array_create(n + 1, None, context)?;\n\n        // 27. Append match to indices.\n        indices.create_data_property_or_throw(0, match_record, context)?;\n\n        // 28. Let matchedSubstr be GetMatchString(S, match).\n        let matched_substr = input.get_expect((last_index as usize)..(e));\n\n        // 29. Perform ! CreateDataPropertyOrThrow(A, \"0\", matchedSubstr).\n        a.create_data_property_or_throw(0, matched_substr, context)?;\n\n        let named_groups = match_value\n            .named_groups()\n            .collect::<Vec<(&str, Option<Range>)>>();\n\n        // Combines:\n        // 26. Let groupNames be a new empty List.\n        // 30. If R contains any GroupName, then\n        // 31. Else,\n        // 33. For each integer i such that 1 ≤ i ≤ n, in ascending order, do\n        #[allow(clippy::if_not_else)]\n        let (groups, group_names) = if !named_groups.clone().is_empty() {\n            // a. Let groups be OrdinaryObjectCreate(null).\n            let groups = JsObject::with_null_proto();\n            let group_names = JsObject::with_null_proto();\n\n            // e. If the ith capture of R was defined with a GroupName, then\n            // i. Let s be the CapturingGroupName of that GroupName.\n            // ii. Perform ! CreateDataPropertyOrThrow(groups, s, capturedValue).\n            // iii. Append s to groupNames.\n            for (name, range) in named_groups {\n                let name = js_string!(name);\n                if let Some(range) = range {\n                    let value = input.get_expect(range.clone());\n\n                    groups.create_data_property_or_throw(name.clone(), value, context)?;\n\n                    // 22.2.7.8 MakeMatchIndicesIndexPairArray ( S, indices, groupNames, hasGroups )\n                    // a. Let matchIndices be indices[i].\n                    // b. If matchIndices is not undefined, then\n                    // i. Let matchIndexPair be GetMatchIndexPair(S, matchIndices).\n                    // d. Perform ! CreateDataPropertyOrThrow(A, ! ToString(𝔽(i)), matchIndexPair).\n                    group_names.create_data_property_or_throw(\n                        name.clone(),\n                        Array::create_array_from_list(\n                            [range.start.into(), range.end.into()],\n                            context,\n                        ),\n                        context,\n                    )?;\n                } else {\n                    groups.create_data_property_or_throw(\n                        name.clone(),\n                        JsValue::undefined(),\n                        context,\n                    )?;\n\n                    // 22.2.7.8 MakeMatchIndicesIndexPairArray ( S, indices, groupNames, hasGroups )\n                    // c. Else,\n                    // i. Let matchIndexPair be undefined.\n                    // d. Perform ! CreateDataPropertyOrThrow(A, ! ToString(𝔽(i)), matchIndexPair).\n                    group_names.create_data_property_or_throw(\n                        name,\n                        JsValue::undefined(),\n                        context,\n                    )?;\n                }\n            }\n\n            (groups.into(), group_names.into())\n        } else {\n            // a. Let groups be undefined.\n            (JsValue::undefined(), JsValue::undefined())\n        };\n\n        // 22.2.7.8 MakeMatchIndicesIndexPairArray ( S, indices, groupNames, hasGroups )\n        // 8. Perform ! CreateDataPropertyOrThrow(A, \"groups\", groups).\n        indices.create_data_property_or_throw(js_string!(\"groups\"), group_names, context)?;\n\n        // 32. Perform ! CreateDataPropertyOrThrow(A, \"groups\", groups).\n        a.create_data_property_or_throw(js_string!(\"groups\"), groups, context)?;\n\n        // 27. For each integer i such that i ≥ 1 and i ≤ n, in ascending order, do\n        for i in 1..=n {\n            // a. Let captureI be ith element of r's captures List.\n            let capture = match_value.group(i as usize);\n\n            // b. If captureI is undefined, let capturedValue be undefined.\n            // c. Else if fullUnicode is true, then\n            // d. Else,\n            let captured_value = capture.clone().map_or_else(JsValue::undefined, |range| {\n                js_string!(input.get_expect(range)).into()\n            });\n\n            // e. Perform ! CreateDataPropertyOrThrow(A, ! ToString(𝔽(i)), capturedValue).\n            a.create_data_property_or_throw(i, captured_value.clone(), context)?;\n\n            // 22.2.7.8 MakeMatchIndicesIndexPairArray ( S, indices, groupNames, hasGroups )\n            if has_indices {\n                // b. If matchIndices is not undefined, then\n                // i. Let matchIndexPair be GetMatchIndexPair(S, matchIndices).\n                // c. Else,\n                // i. Let matchIndexPair be undefined.\n                let indices_range = capture.map_or_else(JsValue::undefined, |range| {\n                    Array::create_array_from_list([range.start.into(), range.end.into()], context)\n                        .into()\n                });\n\n                // d. Perform ! CreateDataPropertyOrThrow(A, ! ToString(𝔽(i)), matchIndexPair).\n                indices.create_data_property_or_throw(i, indices_range, context)?;\n            }\n        }\n\n        // 34. If hasIndices is true, then\n        // a. Let indicesArray be MakeMatchIndicesIndexPairArray(S, indices, groupNames, hasGroups).\n        // b. Perform ! CreateDataPropertyOrThrow(A, \"indices\", indicesArray).\n        if has_indices {\n            a.create_data_property_or_throw(js_string!(\"indices\"), indices, context)?;\n        }\n\n        // 35. Return A.\n        Ok(Some(a))\n    }\n\n    /// `RegExp.prototype[ @@match ]( string )`\n    ///\n    /// This method retrieves the matches when matching a string against a regular expression.\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///  - [MDN documentation][mdn]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-regexp.prototype-@@match\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp/@@match\n    pub(crate) fn r#match(\n        this: &JsValue,\n        args: &[JsValue],\n        context: &mut Context,\n    ) -> JsResult<JsValue> {\n        // 1. Let rx be the this value.\n        // 2. If rx is not an Object, throw a TypeError exception.\n        let Some(rx) = this.as_object() else {\n            return Err(JsNativeError::typ()\n                .with_message(\"RegExp.prototype.match method called on incompatible value\")\n                .into());\n        };\n\n        // 3. Let S be ? ToString(string).\n        let arg_str = args.get_or_undefined(0).to_string(context)?;\n\n        // 4. Let flags be ? ToString(? Get(rx, \"flags\")).\n        let flags = rx.get(js_string!(\"flags\"), context)?.to_string(context)?;\n\n        // 5. If flags does not contain \"g\", then\n        if !flags.contains(b'g') {\n            // a. Return ? RegExpExec(rx, S).\n            return (Self::abstract_exec(&rx, arg_str, context)?)\n                .map_or_else(|| Ok(JsValue::null()), |v| Ok(v.into()));\n        }\n\n        // 6. Else,\n\n        // a. If flags contains \"u\" or flags contains \"v\", let fullUnicode be true. Otherwise, let fullUnicode be false.\n        let full_unicode = flags.contains(b'u') || flags.contains(b'v');\n\n        // b. Perform ? Set(rx, \"lastIndex\", +0𝔽, true).\n        rx.set(js_string!(\"lastIndex\"), 0, true, context)?;\n\n        // c. Let A be ! ArrayCreate(0).\n        let a = Array::array_create(0, None, context)?;\n\n        // d. Let n be 0.\n        let mut n = 0;\n\n        // e. Repeat,\n        loop {\n            // i. Let result be ? RegExpExec(rx, S).\n            let result = Self::abstract_exec(&rx, arg_str.clone(), context)?;\n\n            // ii. If result is null, then\n            // iii. Else,\n            if let Some(result) = result {\n                // 1. Let matchStr be ? ToString(? Get(result, \"0\")).\n                let match_str = result.get(0, context)?.to_string(context)?;\n\n                // 2. Perform ! CreateDataPropertyOrThrow(A, ! ToString(𝔽(n)), matchStr).\n                a.create_data_property_or_throw(n, match_str.clone(), context)?;\n\n                // 3. If matchStr is the empty String, then\n                if match_str.is_empty() {\n                    // a. Let thisIndex be ℝ(? ToLength(? Get(rx, \"lastIndex\"))).\n                    let this_index = rx\n                        .get(js_string!(\"lastIndex\"), context)?\n                        .to_length(context)?;\n\n                    // b. Let nextIndex be AdvanceStringIndex(S, thisIndex, fullUnicode).\n                    let next_index = advance_string_index(&arg_str, this_index, full_unicode);\n\n                    // c. Perform ? Set(rx, \"lastIndex\", 𝔽(nextIndex), true).\n                    rx.set(\n                        js_string!(\"lastIndex\"),\n                        JsValue::new(next_index),\n                        true,\n                        context,\n                    )?;\n                }\n\n                // 4. Set n to n + 1.\n                n += 1;\n            } else {\n                // 1. If n = 0, return null.\n                if n == 0 {\n                    return Ok(JsValue::null());\n                }\n                // 2. Return A.\n                return Ok(a.into());\n            }\n        }\n    }\n\n    /// `RegExp.prototype.toString()`\n    ///\n    /// Return a string representing the regular expression.\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///  - [MDN documentation][mdn]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-regexp.prototype.tostring\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp/toString\n    #[allow(clippy::wrong_self_convention)]\n    pub(crate) fn to_string(\n        this: &JsValue,\n        _: &[JsValue],\n        context: &mut Context,\n    ) -> JsResult<JsValue> {\n        // 1. Let R be the this value.\n        // 2. If R is not an Object, throw a TypeError exception.\n        let regexp = this.as_object().ok_or_else(|| {\n            JsNativeError::typ()\n                .with_message(\"RegExp.prototype.toString method called on incompatible value\")\n        })?;\n\n        // 3. Let pattern be ? ToString(? Get(R, \"source\")).\n        let pattern = regexp\n            .get(js_string!(\"source\"), context)?\n            .to_string(context)?;\n\n        // 4. Let flags be ? ToString(? Get(R, \"flags\")).\n        let flags = regexp\n            .get(js_string!(\"flags\"), context)?\n            .to_string(context)?;\n\n        // 5. Let result be the string-concatenation of \"/\", pattern, \"/\", and flags.\n        // 6. Return result.\n        Ok(js_string!(js_str!(\"/\"), &pattern, js_str!(\"/\"), &flags).into())\n    }\n\n    /// `RegExp.prototype[ @@matchAll ]( string )`\n    ///\n    /// The `[@@matchAll]` method returns all matches of the regular expression against a string.\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///  - [MDN documentation][mdn]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-regexp-prototype-matchall\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp/@@matchAll\n    pub(crate) fn match_all(\n        this: &JsValue,\n        args: &[JsValue],\n        context: &mut Context,\n    ) -> JsResult<JsValue> {\n        // 1. Let R be the this value.\n        // 2. If Type(R) is not Object, throw a TypeError exception.\n        let regexp = this.as_object().ok_or_else(|| {\n            JsNativeError::typ()\n                .with_message(\"RegExp.prototype.match_all method called on incompatible value\")\n        })?;\n\n        // 3. Let S be ? ToString(string).\n        let arg_str = args.get_or_undefined(0).to_string(context)?;\n\n        // 4. Let C be ? SpeciesConstructor(R, %RegExp%).\n        let c = regexp.species_constructor(StandardConstructors::regexp, context)?;\n\n        // 5. Let flags be ? ToString(? Get(R, \"flags\")).\n        let flags = regexp\n            .get(js_string!(\"flags\"), context)?\n            .to_string(context)?;\n\n        // 6. Let matcher be ? Construct(C, « R, flags »).\n        let matcher = c.construct(&[this.clone(), flags.clone().into()], Some(&c), context)?;\n\n        // 7. Let lastIndex be ? ToLength(? Get(R, \"lastIndex\")).\n        let last_index = regexp\n            .get(js_string!(\"lastIndex\"), context)?\n            .to_length(context)?;\n\n        // 8. Perform ? Set(matcher, \"lastIndex\", lastIndex, true).\n        matcher.set(js_string!(\"lastIndex\"), last_index, true, context)?;\n\n        // 9. If flags contains \"g\", let global be true.\n        // 10. Else, let global be false.\n        let global = flags.contains(b'g');\n\n        // 11. If flags contains \"u\", let fullUnicode be true.\n        // 12. Else, let fullUnicode be false.\n        let unicode = flags.contains(b'u') || flags.contains(b'v');\n\n        // 13. Return ! CreateRegExpStringIterator(matcher, S, global, fullUnicode).\n        Ok(RegExpStringIterator::create_regexp_string_iterator(\n            matcher.clone(),\n            arg_str,\n            global,\n            unicode,\n            context,\n        ))\n    }\n\n    /// `RegExp.prototype [ @@replace ] ( string, replaceValue )`\n    ///\n    /// The [@@replace]() method replaces some or all matches of a this pattern in a string by a replacement,\n    /// and returns the result of the replacement as a new string.\n    /// The replacement can be a string or a function to be called for each match.\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///  - [MDN documentation][mdn]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-regexp.prototype-@@replace\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp/@@replace\n    pub(crate) fn replace(\n        this: &JsValue,\n        args: &[JsValue],\n        context: &mut Context,\n    ) -> JsResult<JsValue> {\n        // Helper enum.\n        enum CallableOrString {\n            FunctionalReplace(JsObject),\n            ReplaceValue(JsString),\n        }\n\n        // 1. Let rx be the this value.\n        // 2. If rx is not an Object, throw a TypeError exception.\n        let rx = this.as_object().ok_or_else(|| {\n            JsNativeError::typ().with_message(\n                \"RegExp.prototype[Symbol.replace] method called on incompatible value\",\n            )\n        })?;\n\n        // 3. Let S be ? ToString(string).\n        let s = args.get_or_undefined(0).to_string(context)?;\n\n        // 4. Let lengthS be the length of S.\n        let length_s = s.len();\n\n        let replace_value = args.get_or_undefined(1);\n\n        // 5. Let functionalReplace be IsCallable(replaceValue).\n        let functional_replace = replace_value.as_callable();\n\n        // 6. If functionalReplace is false, then\n        let replace_value = if let Some(callable) = functional_replace {\n            CallableOrString::FunctionalReplace(callable)\n        } else {\n            // a. Set replaceValue to ? ToString(replaceValue).\n            CallableOrString::ReplaceValue(replace_value.to_string(context)?)\n        };\n\n        // 7. Let flags be ? ToString(? Get(rx, \"flags\")).\n        let flags = rx.get(js_string!(\"flags\"), context)?.to_string(context)?;\n\n        // 8. If flags contains \"g\", let global be true. Otherwise, let global be false.\n        let global = flags.contains(b'g');\n\n        // 9. If global is true, then\n        let full_unicode = if global {\n            // a. If flags contains \"u\", let fullUnicode be true. Otherwise, let fullUnicode be false.\n            let full_unicode = flags.contains(b'u');\n\n            // b. Perform ? Set(rx, \"lastIndex\", +0𝔽, true).\n            rx.set(js_string!(\"lastIndex\"), 0, true, context)?;\n\n            full_unicode\n        } else {\n            false\n        };\n\n        // 10. Let results be a new empty List.\n        let mut results = Vec::new();\n\n        // SKIPPED: 11. Let done be false.\n        //\n        // NOTE(HalidOdat): We don't keep track of `done`, we just break when done is true.\n\n        // 12. Repeat, while done is false,\n        loop {\n            // a. Let result be ? RegExpExec(rx, S).\n            let result = Self::abstract_exec(&rx, s.clone(), context)?;\n\n            // b. If result is null, set done to true.\n            let Some(result) = result else {\n                // SKIPPED: 1. Set done to true.\n                break;\n            };\n\n            // c. Else,\n            //  i. Append result to results.\n            results.push(result.clone());\n\n            //  ii. If global is false, then\n            if !global {\n                // SKIPPED: 1. Set done to true.\n                break;\n            }\n\n            //  iii. Else,\n            //    1. Let matchStr be ? ToString(? Get(result, \"0\")).\n            let match_str = result.get(0, context)?.to_string(context)?;\n\n            //    2. If matchStr is the empty String, then\n            if match_str.is_empty() {\n                // a. Let thisIndex be ℝ(? ToLength(? Get(rx, \"lastIndex\"))).\n                let this_index = rx\n                    .get(js_string!(\"lastIndex\"), context)?\n                    .to_length(context)?;\n\n                // b. Let nextIndex be AdvanceStringIndex(S, thisIndex, fullUnicode).\n                let next_index = advance_string_index(&s, this_index, full_unicode);\n\n                // c. Perform ? Set(rx, \"lastIndex\", 𝔽(nextIndex), true).\n                rx.set(\n                    js_string!(\"lastIndex\"),\n                    JsValue::new(next_index),\n                    true,\n                    context,\n                )?;\n            }\n        }\n\n        // 16. If nextSourcePosition ≥ lengthS, return accumulatedResult.\n        // 17. Return the string-concatenation of accumulatedResult and the substring of S from nextSourcePosition.\n\n        // 13. Let accumulatedResult be the empty String.\n        let mut accumulated_result = vec![];\n\n        // 14. Let nextSourcePosition be 0.\n        let mut next_source_position = 0;\n\n        // 15. For each element result of results, do\n        for result in results {\n            // a. Let resultLength be ? LengthOfArrayLike(result).\n            let result_length = result.length_of_array_like(context)? as i64;\n\n            // b. Let nCaptures be max(resultLength - 1, 0).\n            let n_captures = std::cmp::max(result_length - 1, 0);\n\n            // c. Let matched be ? ToString(? Get(result, \"0\")).\n            let matched = result.get(0, context)?.to_string(context)?;\n\n            // d. Let matchLength be the length of matched.\n            let match_length = matched.len();\n\n            // e. Let position be ? ToIntegerOrInfinity(? Get(result, \"index\")).\n            let position = result\n                .get(js_string!(\"index\"), context)?\n                .to_integer_or_infinity(context)?;\n\n            // f. Set position to the result of clamping position between 0 and lengthS.\n            let position = position.clamp_finite(0, length_s as i64) as usize;\n\n            // g. Let captures be a new empty List.\n            let mut captures = Vec::new();\n\n            // h. Let n be 1.\n            // i. Repeat, while n ≤ nCaptures,\n            for n in 1..=n_captures {\n                // i. Let capN be ? Get(result, ! ToString(𝔽(n))).\n                let mut cap_n = result.get(n, context)?;\n\n                // ii. If capN is not undefined, then\n                if !cap_n.is_undefined() {\n                    // 1. Set capN to ? ToString(capN).\n                    cap_n = cap_n.to_string(context)?.into();\n                }\n\n                // iii. Append capN to captures.\n                captures.push(cap_n);\n\n                // iv. NOTE: When n = 1, the preceding step puts the first element into captures (at index 0).\n                //     More generally, the nth capture (the characters captured by the nth set of capturing parentheses)\n                //     is at captures[n - 1].\n                //\n                // v. Set n to n + 1.\n            }\n\n            // j. Let namedCaptures be ? Get(result, \"groups\").\n            let mut named_captures = result.get(js_string!(\"groups\"), context)?;\n\n            let replacement = match replace_value {\n                // k. If functionalReplace is true, then\n                CallableOrString::FunctionalReplace(ref replace_value) => {\n                    // i. Let replacerArgs be the list-concatenation of « matched », captures, and « 𝔽(position), S ».\n                    let mut replacer_args = vec![JsValue::new(matched)];\n                    replacer_args.extend(captures);\n                    replacer_args.push(position.into());\n                    replacer_args.push(s.clone().into());\n\n                    // ii. If namedCaptures is not undefined, then\n                    if !named_captures.is_undefined() {\n                        // 1. Append namedCaptures to replacerArgs.\n                        replacer_args.push(named_captures);\n                    }\n\n                    // iii. Let replValue be ? Call(replaceValue, undefined, replacerArgs).\n                    let repl_value =\n                        replace_value.call(&JsValue::undefined(), &replacer_args, context)?;\n\n                    // iv. Let replacement be ? ToString(replValue).\n                    repl_value.to_string(context)?\n                }\n                // l. Else,\n                CallableOrString::ReplaceValue(ref replace_value) => {\n                    // i. If namedCaptures is not undefined, then\n                    if !named_captures.is_undefined() {\n                        // 1. Set namedCaptures to ? ToObject(namedCaptures).\n                        named_captures = named_captures.to_object(context)?.into();\n                    }\n\n                    // ii. Let replacement be ? GetSubstitution(matched, S, position, captures, namedCaptures, replaceValue).\n                    string::get_substitution(\n                        &matched,\n                        &s,\n                        position,\n                        &captures,\n                        &named_captures,\n                        replace_value,\n                        context,\n                    )?\n                }\n            };\n\n            // m. If position ≥ nextSourcePosition, then\n            if position >= next_source_position {\n                // i. NOTE: position should not normally move backwards.\n                //    If it does, it is an indication of an ill-behaving RegExp subclass or use of\n                //    an access triggered side-effect to change the global flag or other characteristics of rx.\n                //    In such cases, the corresponding substitution is ignored.\n\n                // ii. Set accumulatedResult to the string-concatenation of accumulatedResult, the substring of S from nextSourcePosition to position, and replacement.\n                accumulated_result.extend(s.get_expect(next_source_position..position).iter());\n                accumulated_result.extend(replacement.iter());\n\n                // iii. Set nextSourcePosition to position + matchLength.\n                next_source_position = position + match_length;\n            }\n        }\n\n        // 16. If nextSourcePosition ≥ lengthS, return accumulatedResult.\n        if next_source_position >= length_s {\n            return Ok(js_string!(&accumulated_result[..]).into());\n        }\n\n        // 17. Return the string-concatenation of accumulatedResult and the substring of S from nextSourcePosition.\n        Ok(js_string!(\n            &JsString::from(&accumulated_result[..]),\n            &s.get_expect(next_source_position..)\n        )\n        .into())\n    }\n\n    /// `RegExp.prototype[ @@search ]( string )`\n    ///\n    /// This method executes a search for a match between a this regular expression and a string.\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///  - [MDN documentation][mdn]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-regexp.prototype-@@search\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp/@@search\n    pub(crate) fn search(\n        this: &JsValue,\n        args: &[JsValue],\n        context: &mut Context,\n    ) -> JsResult<JsValue> {\n        // 1. Let rx be the this value.\n        // 2. If Type(rx) is not Object, throw a TypeError exception.\n        let rx = this.as_object().ok_or_else(|| {\n            JsNativeError::typ()\n                .with_message(\"RegExp.prototype[Symbol.search] method called on incompatible value\")\n        })?;\n\n        // 3. Let S be ? ToString(string).\n        let arg_str = args.get_or_undefined(0).to_string(context)?;\n\n        // 4. Let previousLastIndex be ? Get(rx, \"lastIndex\").\n        let previous_last_index = rx.get(js_string!(\"lastIndex\"), context)?;\n\n        // 5. If SameValue(previousLastIndex, +0𝔽) is false, then\n        if !JsValue::same_value(&previous_last_index, &JsValue::new(0)) {\n            // a. Perform ? Set(rx, \"lastIndex\", +0𝔽, true).\n            rx.set(js_string!(\"lastIndex\"), 0, true, context)?;\n        }\n\n        // 6. Let result be ? RegExpExec(rx, S).\n        let result = Self::abstract_exec(&rx, arg_str, context)?;\n\n        // 7. Let currentLastIndex be ? Get(rx, \"lastIndex\").\n        let current_last_index = rx.get(js_string!(\"lastIndex\"), context)?;\n\n        // 8. If SameValue(currentLastIndex, previousLastIndex) is false, then\n        if !JsValue::same_value(&current_last_index, &previous_last_index) {\n            // a. Perform ? Set(rx, \"lastIndex\", previousLastIndex, true).\n            rx.set(js_string!(\"lastIndex\"), previous_last_index, true, context)?;\n        }\n\n        // 9. If result is null, return -1𝔽.\n        // 10. Return ? Get(result, \"index\").\n        result.map_or_else(\n            || Ok(JsValue::new(-1)),\n            |result| result.get(js_string!(\"index\"), context),\n        )\n    }\n\n    /// `RegExp.prototype [ @@split ] ( string, limit )`\n    ///\n    /// The [@@split]() method splits a String object into an array of strings by separating the string into substrings.\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///  - [MDN documentation][mdn]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-regexp.prototype-@@split\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp/@@split\n    pub(crate) fn split(\n        this: &JsValue,\n        args: &[JsValue],\n        context: &mut Context,\n    ) -> JsResult<JsValue> {\n        // 1. Let rx be the this value.\n        // 2. If Type(rx) is not Object, throw a TypeError exception.\n        let rx = this.as_object().ok_or_else(|| {\n            JsNativeError::typ()\n                .with_message(\"RegExp.prototype.split method called on incompatible value\")\n        })?;\n\n        // 3. Let S be ? ToString(string).\n        let arg_str = args.get_or_undefined(0).to_string(context)?;\n\n        // 4. Let C be ? SpeciesConstructor(rx, %RegExp%).\n        let constructor = rx.species_constructor(StandardConstructors::regexp, context)?;\n\n        // 5. Let flags be ? ToString(? Get(rx, \"flags\")).\n        let flags = rx.get(js_string!(\"flags\"), context)?.to_string(context)?;\n\n        // 6. If flags contains \"u\", let unicodeMatching be true.\n        // 7. Else, let unicodeMatching be false.\n        let unicode = flags.contains(b'u');\n\n        // 8. If flags contains \"y\", let newFlags be flags.\n        // 9. Else, let newFlags be the string-concatenation of flags and \"y\".\n        let new_flags = if flags.contains(b'y') {\n            flags\n        } else {\n            js_string!(&flags, js_str!(\"y\"))\n        };\n\n        // 10. Let splitter be ? Construct(C, « rx, newFlags »).\n        let splitter = constructor.construct(\n            &[this.clone(), new_flags.into()],\n            Some(&constructor),\n            context,\n        )?;\n\n        // 11. Let A be ! ArrayCreate(0).\n        let a = Array::array_create(0, None, context)?;\n\n        // 12. Let lengthA be 0.\n        let mut length_a = 0;\n\n        // 13. If limit is undefined, let lim be 2^32 - 1; else let lim be ℝ(? ToUint32(limit)).\n        let limit = args.get_or_undefined(1);\n        let lim = if limit.is_undefined() {\n            u32::MAX\n        } else {\n            limit.to_u32(context)?\n        };\n\n        // 14. If lim is 0, return A.\n        if lim == 0 {\n            return Ok(a.into());\n        }\n\n        // 15. Let size be the length of S.\n        let size = arg_str.len() as u64;\n\n        // 16. If size is 0, then\n        if size == 0 {\n            // a. Let z be ? RegExpExec(splitter, S).\n            let result = Self::abstract_exec(&splitter, arg_str.clone(), context)?;\n\n            // b. If z is not null, return A.\n            if result.is_some() {\n                return Ok(a.into());\n            }\n\n            // c. Perform ! CreateDataPropertyOrThrow(A, \"0\", S).\n            a.create_data_property_or_throw(0, arg_str, context)?;\n\n            // d. Return A.\n            return Ok(a.into());\n        }\n\n        // 17. Let p be 0.\n        // 18. Let q be p.\n        let mut p = 0;\n        let mut q = p;\n\n        // 19. Repeat, while q < size,\n        while q < size {\n            // a. Perform ? Set(splitter, \"lastIndex\", 𝔽(q), true).\n            splitter.set(js_string!(\"lastIndex\"), JsValue::new(q), true, context)?;\n\n            // b. Let z be ? RegExpExec(splitter, S).\n            let result = Self::abstract_exec(&splitter, arg_str.clone(), context)?;\n\n            // c. If z is null, set q to AdvanceStringIndex(S, q, unicodeMatching).\n            // d. Else,\n            if let Some(result) = result {\n                // i. Let e be ℝ(? ToLength(? Get(splitter, \"lastIndex\"))).\n                let mut e = splitter\n                    .get(js_string!(\"lastIndex\"), context)?\n                    .to_length(context)?;\n\n                // ii. Set e to min(e, size).\n                e = std::cmp::min(e, size);\n\n                // iii. If e = p, set q to AdvanceStringIndex(S, q, unicodeMatching).\n                // iv. Else,\n                if e == p {\n                    q = advance_string_index(&arg_str, q, unicode);\n                } else {\n                    // 1. Let T be the substring of S from p to q.\n                    let arg_str_substring = arg_str.get_expect(p as usize..q as usize);\n\n                    // 2. Perform ! CreateDataPropertyOrThrow(A, ! ToString(𝔽(lengthA)), T).\n                    a.create_data_property_or_throw(length_a, arg_str_substring, context)?;\n\n                    // 3. Set lengthA to lengthA + 1.\n                    length_a += 1;\n\n                    // 4. If lengthA = lim, return A.\n                    if length_a == lim {\n                        return Ok(a.into());\n                    }\n\n                    // 5. Set p to e.\n                    p = e;\n\n                    // 6. Let numberOfCaptures be ? LengthOfArrayLike(z).\n                    let mut number_of_captures = result.length_of_array_like(context)? as isize;\n\n                    // 7. Set numberOfCaptures to max(numberOfCaptures - 1, 0).\n                    number_of_captures = std::cmp::max(number_of_captures - 1, 0);\n\n                    // 8. Let i be 1.\n                    // 9. Repeat, while i ≤ numberOfCaptures,\n                    for i in 1..=number_of_captures {\n                        // a. Let nextCapture be ? Get(z, ! ToString(𝔽(i))).\n                        let next_capture = result.get(i, context)?;\n\n                        // b. Perform ! CreateDataPropertyOrThrow(A, ! ToString(𝔽(lengthA)), nextCapture).\n                        a.create_data_property_or_throw(length_a, next_capture, context)?;\n\n                        // d. Set lengthA to lengthA + 1.\n                        length_a += 1;\n\n                        // e. If lengthA = lim, return A.\n                        if length_a == lim {\n                            return Ok(a.into());\n                        }\n                    }\n\n                    // 10. Set q to p.\n                    q = p;\n                }\n            } else {\n                q = advance_string_index(&arg_str, q, unicode);\n            }\n        }\n\n        // 20. Let T be the substring of S from p to size.\n        let arg_str_substring = arg_str.get_expect(p as usize..size as usize);\n\n        // 21. Perform ! CreateDataPropertyOrThrow(A, ! ToString(𝔽(lengthA)), T).\n        a.create_data_property_or_throw(length_a, arg_str_substring, context)?;\n\n        // 22. Return A.\n        Ok(a.into())\n    }\n\n    /// [`RegExp.prototype.compile ( pattern, flags )`][spec]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-regexp.prototype.compile\n    #[cfg(feature = \"annex-b\")]\n    fn compile(this: &JsValue, args: &[JsValue], context: &mut Context) -> JsResult<JsValue> {\n        // 1. Let O be the this value.\n        // 2. Perform ? RequireInternalSlot(O, [[RegExpMatcher]]).\n\n        let this = this\n            .as_object()\n            .filter(|o| o.is::<RegExp>())\n            .ok_or_else(|| {\n                JsNativeError::typ()\n                    .with_message(\"`RegExp.prototype.compile` cannot be called for a non-object\")\n            })?;\n        let pattern = args.get_or_undefined(0);\n        let flags = args.get_or_undefined(1);\n        // 3. If pattern is an Object and pattern has a [[RegExpMatcher]] internal slot, then\n        let (pattern, flags) = if let Some((p, f)) = pattern.as_object().and_then(|o| {\n            o.downcast_ref::<RegExp>()\n                .map(|rx| (rx.original_source.clone(), rx.original_flags.clone()))\n        }) {\n            //     a. If flags is not undefined, throw a TypeError exception.\n            if !flags.is_undefined() {\n                return Err(JsNativeError::typ()\n                    .with_message(\n                        \"`RegExp.prototype.compile` cannot be \\\n                called with both a RegExp initializer and new flags\",\n                    )\n                    .into());\n            }\n            //     b. Let P be pattern.[[OriginalSource]].\n            //     c. Let F be pattern.[[OriginalFlags]].\n            (p.into(), f.into())\n        } else {\n            // 4. Else,\n            //     a. Let P be pattern.\n            //     b. Let F be flags.\n            (pattern.clone(), flags.clone())\n        };\n\n        let regexp = Self::compile_native_regexp(&pattern, &flags, context)?;\n\n        // 5. Return ? RegExpInitialize(O, P, F).\n        {\n            *this\n                .downcast_mut::<RegExp>()\n                .expect(\"already checked that the object was a RegExp\") = regexp;\n        }\n\n        this.set(js_string!(\"lastIndex\"), 0, true, context)?;\n\n        Ok(this.into())\n    }\n}\n\n/// `22.2.5.2.3 AdvanceStringIndex ( S, index, unicode )`\n///\n/// More information:\n///  - [ECMAScript reference][spec]\n///\n/// [spec]: https://tc39.es/ecma262/#sec-advancestringindex\nfn advance_string_index(s: &JsString, index: u64, unicode: bool) -> u64 {\n    // Regress only works with utf8, so this function differs from the spec.\n\n    // 1. Assert: index ≤ 2^53 - 1.\n\n    // 2. If unicode is false, return index + 1.\n    if !unicode {\n        return index + 1;\n    }\n\n    // 3. Let length be the number of code units in S.\n    let length = s.len() as u64;\n\n    // 4. If index + 1 ≥ length, return index + 1.\n    if index + 1 > length {\n        return index + 1;\n    }\n\n    // 5. Let cp be ! CodePointAt(S, index).\n    let code_point = s.code_point_at(index as usize);\n\n    index + code_point.code_unit_count() as u64\n}\n"
  },
  {
    "path": "core/engine/src/builtins/regexp/regexp_string_iterator.rs",
    "content": "//! This module implements the global `RegExp String Iterator` object.\n//!\n//! A `RegExp` String Iterator is an object, that represents a specific iteration over some\n//! specific String instance object, matching against some specific `RegExp` instance object.\n//! There is not a named constructor for `RegExp` String Iterator objects. Instead, `RegExp`\n//! String Iterator objects are created by calling certain methods of `RegExp` instance objects.\n//!\n//! More information:\n//!  - [ECMAScript reference][spec]\n//!\n//! [spec]: https://tc39.es/ecma262/#sec-regexp-string-iterator-objects\n\nuse crate::{\n    Context, JsData, JsResult, JsString, JsValue,\n    builtins::{BuiltInBuilder, IntrinsicObject, iterable::create_iter_result_object, regexp},\n    context::intrinsics::Intrinsics,\n    error::JsNativeError,\n    js_string,\n    object::JsObject,\n    property::Attribute,\n    realm::Realm,\n    symbol::JsSymbol,\n};\nuse boa_gc::{Finalize, Trace};\nuse regexp::{RegExp, advance_string_index};\n\n/// The `RegExp String Iterator` object.\n///\n/// More information:\n///  - [ECMAScript reference][spec]\n///\n/// [spec]: https://tc39.es/ecma262/#sec-regexp-string-iterator-objects\n#[derive(Debug, Clone, Finalize, Trace, JsData)]\npub(crate) struct RegExpStringIterator {\n    matcher: JsObject,\n    string: JsString,\n    global: bool,\n    unicode: bool,\n    completed: bool,\n}\n\nimpl IntrinsicObject for RegExpStringIterator {\n    fn init(realm: &Realm) {\n        BuiltInBuilder::with_intrinsic::<Self>(realm)\n            .prototype(\n                realm\n                    .intrinsics()\n                    .objects()\n                    .iterator_prototypes()\n                    .iterator(),\n            )\n            .static_method(Self::next, js_string!(\"next\"), 0)\n            .static_property(\n                JsSymbol::to_string_tag(),\n                js_string!(\"RegExp String Iterator\"),\n                Attribute::CONFIGURABLE,\n            )\n            .build();\n    }\n\n    fn get(intrinsics: &Intrinsics) -> JsObject {\n        intrinsics.objects().iterator_prototypes().regexp_string()\n    }\n}\n\n// TODO: See todos in create_regexp_string_iterator and next.\nimpl RegExpStringIterator {\n    fn new(matcher: JsObject, string: JsString, global: bool, unicode: bool) -> Self {\n        Self {\n            matcher,\n            string,\n            global,\n            unicode,\n            completed: false,\n        }\n    }\n\n    /// `22.2.7.1 CreateRegExpStringIterator ( R, S, global, fullUnicode )`\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-createregexpstringiterator\n    pub(crate) fn create_regexp_string_iterator(\n        matcher: JsObject,\n        string: JsString,\n        global: bool,\n        unicode: bool,\n        context: &mut Context,\n    ) -> JsValue {\n        // TODO: Implement this with closures and generators.\n        //       For now all values of the closure are stored in RegExpStringIterator and the actual closure execution is in `.next()`.\n\n        // 1. Assert: Type(S) is String.\n        // 2. Assert: Type(global) is Boolean.\n        // 3. Assert: Type(fullUnicode) is Boolean.\n\n        // 4. Let closure be a new Abstract Closure with no parameters that captures R, S, global,\n        //    and fullUnicode and performs the following steps when called:\n\n        // 5. Return ! CreateIteratorFromClosure(closure, \"%RegExpStringIteratorPrototype%\", %RegExpStringIteratorPrototype%).\n\n        let regexp_string_iterator = JsObject::from_proto_and_data_with_shared_shape(\n            context.root_shape(),\n            context\n                .intrinsics()\n                .objects()\n                .iterator_prototypes()\n                .regexp_string(),\n            Self::new(matcher, string, global, unicode),\n        );\n\n        regexp_string_iterator.into()\n    }\n\n    /// `%RegExpStringIteratorPrototype%.next ( )`\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-%regexpstringiteratorprototype%.next\n    pub(crate) fn next(this: &JsValue, _: &[JsValue], context: &mut Context) -> JsResult<JsValue> {\n        let object = this.as_object();\n        let mut iterator = object\n            .as_ref()\n            .and_then(JsObject::downcast_mut::<Self>)\n            .ok_or_else(|| {\n                JsNativeError::typ().with_message(\"`this` is not a RegExpStringIterator\")\n            })?;\n        if iterator.completed {\n            return Ok(create_iter_result_object(\n                JsValue::undefined(),\n                true,\n                context,\n            ));\n        }\n\n        // TODO: This is the code that should be created as a closure in create_regexp_string_iterator.\n\n        // i. Let match be ? RegExpExec(R, S).\n        let m = RegExp::abstract_exec(&iterator.matcher, iterator.string.clone(), context)?;\n\n        if let Some(m) = m {\n            // iii. If global is false, then\n            if !iterator.global {\n                // 1. Perform ? Yield(match).\n                // 2. Return undefined.\n                iterator.completed = true;\n                return Ok(create_iter_result_object(m.into(), false, context));\n            }\n\n            // iv. Let matchStr be ? ToString(? Get(match, \"0\")).\n            let m_str = m.get(0, context)?.to_string(context)?;\n\n            // v. If matchStr is the empty String, then\n            if m_str.is_empty() {\n                // 1. Let thisIndex be ℝ(? ToLength(? Get(R, \"lastIndex\"))).\n                let this_index = iterator\n                    .matcher\n                    .get(js_string!(\"lastIndex\"), context)?\n                    .to_length(context)?;\n\n                // 2. Let nextIndex be ! AdvanceStringIndex(S, thisIndex, fullUnicode).\n                let next_index =\n                    advance_string_index(&iterator.string, this_index, iterator.unicode);\n\n                // 3. Perform ? Set(R, \"lastIndex\", 𝔽(nextIndex), true).\n                iterator\n                    .matcher\n                    .set(js_string!(\"lastIndex\"), next_index, true, context)?;\n            }\n\n            // vi. Perform ? Yield(match).\n            Ok(create_iter_result_object(m.into(), false, context))\n        } else {\n            // ii. If match is null, return undefined.\n            iterator.completed = true;\n            Ok(create_iter_result_object(\n                JsValue::undefined(),\n                true,\n                context,\n            ))\n        }\n    }\n}\n"
  },
  {
    "path": "core/engine/src/builtins/regexp/tests.rs",
    "content": "use crate::{\n    JsNativeErrorKind, JsValue, TestAction, js_string, native_function::NativeFunctionObject,\n    run_test_actions,\n};\nuse boa_macros::js_str;\nuse indoc::indoc;\n\n#[test]\nfn constructors() {\n    run_test_actions([\n        TestAction::run(indoc! {r#\"\n                var constructed = new RegExp(\"[0-9]+(\\\\.[0-9]+)?\");\n                var literal = /[0-9]+(\\.[0-9]+)?/;\n                var ctor_literal = new RegExp(/[0-9]+(\\.[0-9]+)?/);\n            \"#}),\n        TestAction::assert(\"constructed.test('1.0')\"),\n        TestAction::assert(\"literal.test('1.0')\"),\n        TestAction::assert(\"ctor_literal.test('1.0')\"),\n    ]);\n}\n\n#[test]\nfn species() {\n    run_test_actions([\n        TestAction::run(indoc! {r#\"\n        var descriptor = Object.getOwnPropertyDescriptor(RegExp, Symbol.species);\n        var accessor = descriptor.get;\n        var name = Object.getOwnPropertyDescriptor(accessor, \"name\");\n        var length = Object.getOwnPropertyDescriptor(accessor, \"length\");\n        var thisVal = {};\n        \"#}),\n        // length\n        TestAction::assert_eq(\"length.value\", 0),\n        TestAction::assert(\"!length.enumerable\"),\n        TestAction::assert(\"!length.writable\"),\n        TestAction::assert(\"length.configurable\"),\n        // return-value\n        TestAction::assert(\"Object.is(accessor.call(thisVal), thisVal)\"),\n        // symbol-species-name\n        TestAction::assert_eq(\"name.value\", js_str!(\"get [Symbol.species]\")),\n        TestAction::assert(\"!name.enumerable\"),\n        TestAction::assert(\"!name.writable\"),\n        TestAction::assert(\"name.configurable\"),\n        // symbol-species\n        TestAction::assert_eq(\"descriptor.set\", JsValue::undefined()),\n        TestAction::assert_with_op(\"accessor\", |v, _| {\n            v.as_object()\n                .is_some_and(|o| o.is::<NativeFunctionObject>())\n        }),\n        TestAction::assert(\"!descriptor.enumerable\"),\n        TestAction::assert(\"descriptor.configurable\"),\n    ]);\n}\n\n#[test]\nfn flags() {\n    run_test_actions([\n        TestAction::run(indoc! {r#\"\n                var re_gi = /test/gi;\n                var re_sm = /test/sm;\n                var re_u = /test/u;\n            \"#}),\n        TestAction::assert(\"re_gi.global\"),\n        TestAction::assert(\"re_gi.ignoreCase\"),\n        TestAction::assert(\"!re_gi.multiline\"),\n        TestAction::assert(\"!re_gi.dotAll\"),\n        TestAction::assert(\"!re_gi.unicode\"),\n        TestAction::assert(\"!re_gi.sticky\"),\n        TestAction::assert_eq(\"re_gi.flags\", js_str!(\"gi\")),\n        //\n        TestAction::assert(\"!re_sm.global\"),\n        TestAction::assert(\"!re_sm.ignoreCase\"),\n        TestAction::assert(\"re_sm.multiline\"),\n        TestAction::assert(\"re_sm.dotAll\"),\n        TestAction::assert(\"!re_sm.unicode\"),\n        TestAction::assert(\"!re_sm.sticky\"),\n        TestAction::assert_eq(\"re_sm.flags\", js_str!(\"ms\")),\n        //\n        TestAction::assert(\"!re_u.global\"),\n        TestAction::assert(\"!re_u.ignoreCase\"),\n        TestAction::assert(\"!re_u.multiline\"),\n        TestAction::assert(\"!re_u.dotAll\"),\n        TestAction::assert(\"re_u.unicode\"),\n        TestAction::assert(\"!re_u.sticky\"),\n        TestAction::assert_eq(\"re_u.flags\", js_str!(\"u\")),\n    ]);\n}\n\n#[test]\nfn escape() {\n    run_test_actions([\n        TestAction::assert_eq(\n            r\"RegExp.escape('The Quick Brown Fox')\",\n            js_str!(\"\\\\x54he\\\\x20Quick\\\\x20Brown\\\\x20Fox\"),\n        ),\n        TestAction::assert_eq(\n            r\"RegExp.escape('Buy it. use it. break it. fix it.')\",\n            js_str!(\"\\\\x42uy\\\\x20it\\\\.\\\\x20use\\\\x20it\\\\.\\\\x20break\\\\x20it\\\\.\\\\x20fix\\\\x20it\\\\.\"),\n        ),\n        TestAction::assert_eq(r\"RegExp.escape('(*.*)')\", js_str!(\"\\\\(\\\\*\\\\.\\\\*\\\\)\")),\n        TestAction::assert_eq(r\"RegExp.escape('｡^･ｪ･^｡')\", js_str!(\"｡\\\\^･ｪ･\\\\^｡\")),\n        TestAction::assert_eq(\n            r\"RegExp.escape('😊 *_* +_+ ... 👍')\",\n            js_str!(\"😊\\\\x20\\\\*_\\\\*\\\\x20\\\\+_\\\\+\\\\x20\\\\.\\\\.\\\\.\\\\x20👍\"),\n        ),\n        TestAction::assert_eq(\n            r\"RegExp.escape('\\\\d \\\\D (?:)')\",\n            js_str!(\"\\\\\\\\d\\\\x20\\\\\\\\D\\\\x20\\\\(\\\\?\\\\x3a\\\\)\"),\n        ),\n        TestAction::assert_native_error(\n            \"RegExp.escape(1)\",\n            JsNativeErrorKind::Type,\n            \"RegExp.escape requires a string argument\",\n        ),\n    ]);\n}\n\n#[test]\nfn last_index() {\n    run_test_actions([\n        TestAction::run(r\"var regex = /[0-9]+(\\.[0-9]+)?/g;\"),\n        TestAction::assert_eq(\"regex.lastIndex\", 0),\n        TestAction::assert(\"regex.test('1.0foo')\"),\n        TestAction::assert_eq(\"regex.lastIndex\", 3),\n        TestAction::assert(\"!regex.test('1.0foo')\"),\n        TestAction::assert_eq(\"regex.lastIndex\", 0),\n    ]);\n}\n\n#[test]\nfn exec() {\n    run_test_actions([\n        TestAction::run_harness(),\n        TestAction::run(indoc! {r\"\n                var re = /quick\\s(brown).+?(jumps)/ig;\n                var result = re.exec('The Quick Brown Fox Jumps Over The Lazy Dog');\n            \"}),\n        TestAction::assert(indoc! {r#\"\n            arrayEquals(\n                result,\n                [\"Quick Brown Fox Jumps\", \"Brown\", \"Jumps\"]\n            )\n        \"#}),\n        TestAction::assert_eq(\"result.index\", 4),\n        TestAction::assert_eq(\n            \"result.input\",\n            js_str!(\"The Quick Brown Fox Jumps Over The Lazy Dog\"),\n        ),\n    ]);\n}\n\n#[test]\nfn no_panic_on_parse_fail() {\n    run_test_actions([\n        TestAction::assert_native_error(\n            r\"var re = /]/u;\",\n            JsNativeErrorKind::Syntax,\n            \"Invalid regular expression literal: Invalid atom character at line 1, col 10\",\n        ),\n        TestAction::assert_native_error(\n            r\"var re = /a{/u;\",\n            JsNativeErrorKind::Syntax,\n            \"Invalid regular expression literal: Invalid quantifier at line 1, col 10\",\n        ),\n    ]);\n}\n\n#[test]\nfn to_string() {\n    run_test_actions([\n        TestAction::assert_eq(\"(new RegExp('a+b+c')).toString()\", js_str!(\"/a+b+c/\")),\n        TestAction::assert_eq(\"(new RegExp('bar', 'g')).toString()\", js_str!(\"/bar/g\")),\n        TestAction::assert_eq(r\"(new RegExp('\\\\n', 'g')).toString()\", js_string!(r\"/\\n/g\")),\n        TestAction::assert_eq(r\"/\\n/g.toString()\", js_string!(r\"/\\n/g\")),\n        TestAction::assert_eq(r\"/,\\;/.toString()\", js_string!(r\"/,\\;/\")),\n    ]);\n}\n#[test]\nfn search() {\n    const ERROR: &str = \"RegExp.prototype[Symbol.search] method called on incompatible value\";\n    run_test_actions([\n        TestAction::run(indoc! {r#\"\n                var search = Object.getOwnPropertyDescriptor(RegExp.prototype, Symbol.search);\n                var length = Object.getOwnPropertyDescriptor(search.value, 'length');\n                var name = Object.getOwnPropertyDescriptor(search.value, 'name');\n            \"#}),\n        // prop-desc\n        TestAction::assert(\"!search.enumerable\"),\n        TestAction::assert(\"search.writable\"),\n        TestAction::assert(\"search.configurable\"),\n        // length\n        TestAction::assert_eq(\"length.value\", 1),\n        TestAction::assert(\"!length.enumerable\"),\n        TestAction::assert(\"!length.writable\"),\n        TestAction::assert(\"length.configurable\"),\n        // name\n        TestAction::assert_eq(\"name.value\", js_str!(\"[Symbol.search]\")),\n        TestAction::assert(\"!name.enumerable\"),\n        TestAction::assert(\"!name.writable\"),\n        TestAction::assert(\"name.configurable\"),\n        // success-return-val\n        TestAction::assert_eq(\"/a/[Symbol.search]('abc')\", 0),\n        TestAction::assert_eq(\"/b/[Symbol.search]('abc')\", 1),\n        TestAction::assert_eq(\"/c/[Symbol.search]('abc')\", 2),\n        // failure-return-val\n        TestAction::assert_eq(\"/z/[Symbol.search]('a')\", -1),\n        // coerce-string\n        TestAction::assert_eq(\n            indoc! {r#\"\n                /ring/[Symbol.search]({\n                    toString: function() {\n                        return 'toString value';\n                    }\n                });\n            \"#},\n            4,\n        ),\n        // this-val-non-obj\n        TestAction::assert_native_error(\"search.value.call()\", JsNativeErrorKind::Type, ERROR),\n        TestAction::assert_native_error(\n            \"search.value.call(undefined)\",\n            JsNativeErrorKind::Type,\n            ERROR,\n        ),\n        TestAction::assert_native_error(\"search.value.call(null)\", JsNativeErrorKind::Type, ERROR),\n        TestAction::assert_native_error(\"search.value.call(true)\", JsNativeErrorKind::Type, ERROR),\n        TestAction::assert_native_error(\n            \"search.value.call('string')\",\n            JsNativeErrorKind::Type,\n            ERROR,\n        ),\n        TestAction::assert_native_error(\n            \"search.value.call(Symbol.search)\",\n            JsNativeErrorKind::Type,\n            ERROR,\n        ),\n        TestAction::assert_native_error(\"search.value.call(86)\", JsNativeErrorKind::Type, ERROR),\n        // u-lastindex-advance\n        TestAction::assert_eq(r\"/\\udf06/u[Symbol.search]('\\ud834\\udf06')\", -1),\n        TestAction::assert_eq(\"/a/[Symbol.search](\\\"a\\\")\", 0),\n        TestAction::assert_eq(\"/a/[Symbol.search](\\\"ba\\\")\", 1),\n        TestAction::assert_eq(\"/a/[Symbol.search](\\\"bb\\\")\", -1),\n        TestAction::assert_eq(\"/u/[Symbol.search](null)\", 1),\n        TestAction::assert_eq(\"/d/[Symbol.search](undefined)\", 2),\n    ]);\n}\n\n#[test]\nfn regular_expression_construction_independent_of_global_reg_exp() {\n    let regex = \"/abc/\";\n    run_test_actions([\n        TestAction::run(regex),\n        TestAction::run(\"RegExp = null\"),\n        TestAction::run(regex),\n    ]);\n}\n\n#[test]\nfn regexp_no_panic_on_empty_class_quantifier() {\n    // Regression test for https://github.com/boa-dev/boa/issues/2718\n    // `/[]*1/u.exec()` previously caused a panic in the regress crate.\n    // It should return null without panicking.\n    run_test_actions([TestAction::assert_eq(\"/[]*1/u.exec()\", JsValue::null())]);\n}\n"
  },
  {
    "path": "core/engine/src/builtins/set/mod.rs",
    "content": "//! Boa's implementation of ECMAScript's global `Set` object.\n//!\n//! The ECMAScript `Set` class is a global object that is used in the construction of sets; which\n//! are high-level, collections of values.\n//!\n//! More information:\n//!  - [ECMAScript reference][spec]\n//!  - [MDN documentation][mdn]\n//!\n//! [spec]: https://tc39.es/ecma262/#sec-set-objects\n//! [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Set\n\nmod set_iterator;\n\n#[cfg(test)]\nmod tests;\n\npub mod ordered_set;\n\nuse self::ordered_set::OrderedSet;\nuse super::iterable::IteratorHint;\nuse crate::{\n    Context, JsArgs, JsResult, JsString, JsValue,\n    builtins::{\n        BuiltInBuilder, BuiltInConstructor, BuiltInObject, IntrinsicObject,\n        canonicalize_keyed_collection_key, set::ordered_set::SetLock,\n    },\n    context::intrinsics::{Intrinsics, StandardConstructor, StandardConstructors},\n    error::JsNativeError,\n    js_error, js_string,\n    object::{JsObject, internal_methods::get_prototype_from_constructor},\n    property::{Attribute, PropertyNameKind},\n    realm::Realm,\n    string::StaticJsStrings,\n    symbol::JsSymbol,\n};\nuse boa_engine::value::IntegerOrInfinity;\npub(crate) use set_iterator::SetIterator;\n\n/// A record containing information about a Set-like object.\n#[derive(Debug)]\nstruct SetRecord {\n    /// The size of the Set-like object.\n    size: usize,\n    /// The `has` method of the Set-like object.\n    has: JsObject,\n    /// The `keys` method of the Set-like object.\n    keys: JsObject,\n}\n\n/// Implementation of the abstract operation `GetSetRecord`.\n///\n/// More information:\n/// - [ECMAScript specification][spec]\n///\n/// [spec]: https://tc39.es/ecma262/#sec-getsetrecord\nfn get_set_record(obj: &JsValue, context: &mut Context) -> JsResult<SetRecord> {\n    // 1. If obj is not an Object, throw a TypeError exception.\n    let obj = obj.as_object().ok_or_else(|| {\n        JsNativeError::typ().with_message(\"Set operation called with non-object argument\")\n    })?;\n\n    // 2. Let rawSize be ? Get(obj, \"size\").\n    let raw_size = obj.get(js_string!(\"size\"), context)?;\n\n    // 3. Let numSize be ? ToNumber(rawSize).\n    // 4. NOTE: If rawSize is undefined, then numSize will be NaN.\n    let num_size = raw_size.to_number(context)?;\n\n    // 5. If numSize is NaN, throw a TypeError exception.\n    if num_size.is_nan() {\n        return Err(JsNativeError::typ()\n            .with_message(\"size is undefined\")\n            .into());\n    }\n\n    // 6. Let intSize be ! ToIntegerOrInfinity(numSize).\n    let int_size = IntegerOrInfinity::from(num_size);\n    // 7. If intSize < 0, throw a RangeError exception.\n    let size: usize = match int_size {\n        IntegerOrInfinity::NegativeInfinity => {\n            return Err(JsNativeError::range()\n                .with_message(\"Set size must be non-negative\")\n                .into());\n        }\n        IntegerOrInfinity::Integer(size) if size < 0 => {\n            return Err(JsNativeError::range()\n                .with_message(\"Set size must be non-negative\")\n                .into());\n        }\n        IntegerOrInfinity::Integer(size) => size as usize,\n        IntegerOrInfinity::PositiveInfinity => usize::MAX,\n    };\n\n    // 8. Let has be ? Get(obj, \"has\").\n    let has = obj.get(js_string!(\"has\"), context)?;\n\n    // 9. If IsCallable(has) is false, throw a TypeError exception.\n    let has = has.as_callable().ok_or_else(|| {\n        JsNativeError::typ().with_message(\"Set-like object must have a callable 'has' method\")\n    })?;\n\n    // 10. Let keys be ? Get(obj, \"keys\").\n    let keys = obj.get(js_string!(\"keys\"), context)?;\n\n    // 11. If IsCallable(keys) is false, throw a TypeError exception.\n    let keys = keys.as_callable().ok_or_else(|| {\n        JsNativeError::typ().with_message(\"Set-like object must have a callable 'keys' method\")\n    })?;\n\n    // 12. Return a new Set Record { [[SetObject]]: obj, [[Size]]: intSize, [[Has]]: has, [[Keys]]: keys }.\n    Ok(SetRecord {\n        size,\n        has: has.clone(),\n        keys: keys.clone(),\n    })\n}\n\n#[derive(Debug, Clone)]\npub(crate) struct Set;\n\nimpl IntrinsicObject for Set {\n    fn get(intrinsics: &Intrinsics) -> JsObject {\n        Self::STANDARD_CONSTRUCTOR(intrinsics.constructors()).constructor()\n    }\n    fn init(realm: &Realm) {\n        let get_species = BuiltInBuilder::callable(realm, Self::get_species)\n            .name(js_string!(\"get [Symbol.species]\"))\n            .build();\n\n        let size_getter = BuiltInBuilder::callable(realm, Self::size_getter)\n            .name(js_string!(\"get size\"))\n            .build();\n\n        let values_function = BuiltInBuilder::callable(realm, Self::values)\n            .name(js_string!(\"values\"))\n            .build();\n\n        BuiltInBuilder::from_standard_constructor::<Self>(realm)\n            .static_accessor(\n                JsSymbol::species(),\n                Some(get_species),\n                None,\n                Attribute::CONFIGURABLE,\n            )\n            .method(Self::add, js_string!(\"add\"), 1)\n            .method(Self::clear, js_string!(\"clear\"), 0)\n            .method(Self::delete, js_string!(\"delete\"), 1)\n            .method(Self::entries, js_string!(\"entries\"), 0)\n            .method(Self::for_each, js_string!(\"forEach\"), 1)\n            .method(Self::has, js_string!(\"has\"), 1)\n            .method(Self::difference, js_string!(\"difference\"), 1)\n            .method(Self::intersection, js_string!(\"intersection\"), 1)\n            .method(Self::is_disjoint_from, js_string!(\"isDisjointFrom\"), 1)\n            .method(Self::is_subset_of, js_string!(\"isSubsetOf\"), 1)\n            .method(Self::is_superset_of, js_string!(\"isSupersetOf\"), 1)\n            .method(\n                Self::symmetric_difference,\n                js_string!(\"symmetricDifference\"),\n                1,\n            )\n            .method(Self::union, js_string!(\"union\"), 1)\n            .property(\n                js_string!(\"keys\"),\n                values_function.clone(),\n                Attribute::WRITABLE | Attribute::NON_ENUMERABLE | Attribute::CONFIGURABLE,\n            )\n            .accessor(\n                js_string!(\"size\"),\n                Some(size_getter),\n                None,\n                Attribute::CONFIGURABLE,\n            )\n            .property(\n                js_string!(\"values\"),\n                values_function.clone(),\n                Attribute::WRITABLE | Attribute::NON_ENUMERABLE | Attribute::CONFIGURABLE,\n            )\n            .property(\n                JsSymbol::iterator(),\n                values_function,\n                Attribute::WRITABLE | Attribute::NON_ENUMERABLE | Attribute::CONFIGURABLE,\n            )\n            .property(\n                JsSymbol::to_string_tag(),\n                Self::NAME,\n                Attribute::READONLY | Attribute::NON_ENUMERABLE | Attribute::CONFIGURABLE,\n            )\n            .build();\n    }\n}\n\nimpl BuiltInObject for Set {\n    const NAME: JsString = StaticJsStrings::SET;\n}\n\nimpl BuiltInConstructor for Set {\n    const CONSTRUCTOR_ARGUMENTS: usize = 0;\n    const PROTOTYPE_STORAGE_SLOTS: usize = 19;\n    const CONSTRUCTOR_STORAGE_SLOTS: usize = 2;\n    const STANDARD_CONSTRUCTOR: fn(&StandardConstructors) -> &StandardConstructor =\n        StandardConstructors::set;\n\n    /// [`Set ( [ iterable ] )`][spec]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-set-iterable\n    fn constructor(\n        new_target: &JsValue,\n        args: &[JsValue],\n        context: &mut Context,\n    ) -> JsResult<JsValue> {\n        // 1. If NewTarget is undefined, throw a TypeError exception.\n        if new_target.is_undefined() {\n            return Err(JsNativeError::typ()\n                .with_message(\"calling a builtin Set constructor without new is forbidden\")\n                .into());\n        }\n\n        // 2. Let set be ? OrdinaryCreateFromConstructor(NewTarget, \"%Set.prototype%\", « [[SetData]] »).\n        // 3. Set set.[[SetData]] to a new empty List.\n        let prototype =\n            get_prototype_from_constructor(new_target, StandardConstructors::set, context)?;\n        let set = JsObject::from_proto_and_data_with_shared_shape(\n            context.root_shape(),\n            prototype,\n            OrderedSet::default(),\n        )\n        .upcast();\n\n        // 4. If iterable is either undefined or null, return set.\n        let iterable = args.get_or_undefined(0);\n        if iterable.is_null_or_undefined() {\n            return Ok(set.into());\n        }\n\n        // 5. Let adder be ? Get(set, \"add\").\n        let adder = set.get(js_string!(\"add\"), context)?;\n\n        // 6. If IsCallable(adder) is false, throw a TypeError exception.\n        let adder = adder.as_callable().ok_or_else(|| {\n            JsNativeError::typ().with_message(\"'add' of 'newTarget' is not a function\")\n        })?;\n\n        // 7. Let iteratorRecord be ? GetIterator(iterable, sync).\n        let mut iterator_record = iterable.clone().get_iterator(IteratorHint::Sync, context)?;\n\n        // 8. Repeat,\n        //     a. Let next be ? IteratorStepValue(iteratorRecord).\n        while let Some(next) = iterator_record.step_value(context)? {\n            // c. Let status be Completion(Call(adder, set, « next »)).\n            if let Err(status) = adder.call(&set.clone().into(), &[next], context) {\n                // d. IfAbruptCloseIterator(status, iteratorRecord).\n                return iterator_record.close(Err(status), context);\n            }\n        }\n\n        //     b. If next is done, return set.\n        Ok(set.into())\n    }\n}\n\nimpl Set {\n    /// `get Set [ @@species ]`\n    ///\n    /// The Set[Symbol.species] accessor property returns the Set constructor.\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///  - [MDN documentation][mdn]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-get-set-@@species\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Set/@@species\n    #[allow(clippy::unnecessary_wraps)]\n    fn get_species(this: &JsValue, _: &[JsValue], _: &mut Context) -> JsResult<JsValue> {\n        // 1. Return the this value.\n        Ok(this.clone())\n    }\n\n    /// `Set.prototype.add( value )`\n    ///\n    /// This method adds an entry with value into the set. Returns the set object\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///  - [MDN documentation][mdn]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-set.prototype.add\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Set/add\n    pub(crate) fn add(this: &JsValue, args: &[JsValue], _: &mut Context) -> JsResult<JsValue> {\n        // 1. Let S be the this value.\n        // 2. Perform ? RequireInternalSlot(S, [[SetData]]).\n        let set = this\n            .as_object()\n            .and_then(|o| o.downcast::<OrderedSet>().ok())\n            .ok_or_else(|| {\n                js_error!(TypeError: \"method `Set.prototype.add` called on incompatible receiver\")\n            })?;\n\n        // 3. Set value to CanonicalizeKeyedCollectionKey(value).\n        let value = canonicalize_keyed_collection_key(args.get_or_undefined(0).clone());\n\n        // 4. For each element e of S.[[SetData]], do\n        //   a. If e is not empty and SameValueZero(e, value) is true, then\n        //     i. Return S.\n        // 5. Append value to S.[[SetData]].\n        set.borrow_mut().data_mut().add(value.clone());\n\n        Ok(this.clone())\n        // 6. Return S.\n    }\n\n    /// `Set.prototype.clear( )`\n    ///\n    /// This method removes all entries from the set.\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///  - [MDN documentation][mdn]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-set.prototype.clear\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Set/clear\n    pub(crate) fn clear(this: &JsValue, _: &[JsValue], _: &mut Context) -> JsResult<JsValue> {\n        // 1. Let S be the this value.\n        // 2. Perform ? RequireInternalSlot(S, [[SetData]]).\n        // 3. For each element e of S.[[SetData]], do\n        //        a. Replace the element of S.[[SetData]] whose value is e with an element whose value is empty.\n        this.as_object()\n            .and_then(|o| o.downcast::<OrderedSet>().ok())\n            .ok_or_else(|| {\n                js_error!(\n                    TypeError: \"method `Set.prototype.clear` called on incompatible receiver\"\n                )\n            })?\n            .borrow_mut()\n            .data_mut()\n            .clear();\n\n        // 4. Return undefined.\n        Ok(JsValue::undefined())\n    }\n\n    /// `Set.prototype.delete( value )`\n    ///\n    /// This method removes the entry for the given value if it exists.\n    /// Returns true if there was an element, false otherwise.\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///  - [MDN documentation][mdn]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-set.prototype.delete\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Set/delete\n    pub(crate) fn delete(this: &JsValue, args: &[JsValue], _: &mut Context) -> JsResult<JsValue> {\n        // 1. Let S be the this value.\n        // 2. Perform ? RequireInternalSlot(S, [[SetData]]).\n        let this = this\n            .as_object()\n            .and_then(|o| o.downcast::<OrderedSet>().ok())\n            .ok_or_else(|| {\n                js_error!(\n                    TypeError: \"method `Set.prototype.delete` called on incompatible receiver\"\n                )\n            })?;\n\n        let value = canonicalize_keyed_collection_key(args.get_or_undefined(0).clone());\n\n        // 3. For each element e of S.[[SetData]], do\n        // a. If e is not empty and SameValueZero(e, value) is true, then\n        // i. Replace the element of S.[[SetData]] whose value is e with an element whose value is empty.\n        // ii. Return true.\n        // 4. Return false.\n        Ok(this.borrow_mut().data_mut().delete(&value).into())\n    }\n\n    /// `Set.prototype.entries( )`\n    ///\n    /// This method returns an iterator over the entries of the set\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///  - [MDN documentation][mdn]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-set.prototype.entries\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Set/entries\n    pub(crate) fn entries(\n        this: &JsValue,\n        _: &[JsValue],\n        context: &mut Context,\n    ) -> JsResult<JsValue> {\n        let this = this\n            .as_object()\n            .and_then(|o| o.downcast::<OrderedSet>().ok())\n            .ok_or_else(|| {\n                js_error!(\n                    TypeError: \"method `Set.prototype.entries` called on incompatible receiver\"\n                )\n            })?;\n\n        Ok(SetIterator::create_set_iterator(\n            this.clone(),\n            PropertyNameKind::KeyAndValue,\n            context,\n        ))\n    }\n\n    /// `Set.prototype.forEach( callbackFn [ , thisArg ] )`\n    ///\n    /// This method executes the provided callback function for each value in the set\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///  - [MDN documentation][mdn]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-set.prototype.foreach\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Set/foreach\n    pub(crate) fn for_each(\n        this: &JsValue,\n        args: &[JsValue],\n        context: &mut Context,\n    ) -> JsResult<JsValue> {\n        // 1. Let S be the this value.\n        // 2. Perform ? RequireInternalSlot(S, [[SetData]]).\n\n        let obj = this\n            .as_object()\n            .and_then(|o| o.downcast::<OrderedSet>().ok())\n            .ok_or_else(|| {\n                js_error!(\n                    TypeError: \"method `Set.prototype.forEach` called on incompatible receiver\"\n                )\n            })?;\n\n        // 3. If IsCallable(callbackfn) is false, throw a TypeError exception.\n        let Some(callback_fn) = args.get_or_undefined(0).as_callable() else {\n            return Err(js_error!(\n                TypeError:\n                    \"Method Set.prototype.forEach called with non-callable callback function\"\n            ));\n        };\n\n        let _lock = SetLock::new(&obj);\n\n        // 4. Let entries be S.[[SetData]].\n        // 5. Let numEntries be the number of elements in entries.\n        // 6. Let index be 0.\n        let mut index = 0;\n\n        // 7. Repeat, while index < numEntries,\n        while index < obj.borrow().data().full_len() {\n            // a. Let e be entries[index].\n            let e = obj.borrow().data().get_index(index).cloned();\n\n            // b. Set index to index + 1.\n            index += 1;\n\n            // c. If e is not empty, then\n            if let Some(e) = e {\n                // i. Perform ? Call(callbackfn, thisArg, « e, e, S »).\n                // ii. NOTE: The number of elements in entries may have increased during execution of callbackfn.\n                // iii. Set numEntries to the number of elements in entries.\n                callback_fn.call(\n                    args.get_or_undefined(1),\n                    &[e.clone(), e.clone(), this.clone()],\n                    context,\n                )?;\n            }\n        }\n\n        // 8. Return undefined.\n        Ok(JsValue::undefined())\n    }\n\n    /// Call `f` for each `(value)` in the `Set`.\n    ///\n    /// Can not be used in [`Self::for_each`] because it will be running an\n    /// incorrect order for next steps of the algo:\n    /// ```txt\n    /// 2. Perform ? RequireInternalSlot(M, [[SetData]]).\n    /// 3. If IsCallable(callbackfn) is false, throw a TypeError exception.\n    /// ```\n    pub(crate) fn for_each_native<F>(this: &JsValue, mut f: F) -> JsResult<()>\n    where\n        F: FnMut(JsValue) -> JsResult<()>,\n    {\n        // See `Self::for_each` for comments on the algo.\n\n        let set = this.as_object();\n        let set = set\n            .and_then(|obj| obj.downcast::<OrderedSet>().ok())\n            .ok_or_else(|| {\n                js_error!(\n                    TypeError: \"method `Set.prototype.forEach` called on incompatible receiver\"\n                )\n            })?;\n        let _lock = SetLock::new(&set);\n\n        let mut index = 0;\n        loop {\n            let v = {\n                let set = set.borrow();\n                let set = set.data();\n\n                if index < set.full_len() {\n                    if let Some(k) = set.get_index(index) {\n                        k.clone()\n                    } else {\n                        continue;\n                    }\n                } else {\n                    return Ok(());\n                }\n            };\n\n            f(v)?;\n            index += 1;\n        }\n    }\n\n    /// `Map.prototype.has( key )`\n    ///\n    /// This method checks if the map contains an entry with the given key.\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///  - [MDN documentation][mdn]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-map.prototype.has\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map/has\n    pub(crate) fn has(this: &JsValue, args: &[JsValue], _: &mut Context) -> JsResult<JsValue> {\n        // 1. Let M be the this value.\n        // 2. Perform ? RequireInternalSlot(S, [[SetData]]).\n        let this = this\n            .as_object()\n            .and_then(|o| o.downcast::<OrderedSet>().ok())\n            .ok_or_else(|| {\n                js_error!(\n                    TypeError: \"method `Set.prototype.has` called on incompatible receiver\"\n                )\n            })?;\n\n        // 3. Set value to CanonicalizeKeyedCollectionKey(key).\n        let value = args.get_or_undefined(0);\n        let value = canonicalize_keyed_collection_key(value.clone());\n\n        // 4. For each element e of S.[[SetData]], do\n        //    a. If e is not empty and SameValue(e, value) is true, return true.\n        // 5. Return false.\n        Ok(this.borrow().data().contains(&value).into())\n    }\n\n    /// `Set.prototype.values( )`\n    ///\n    /// This method returns an iterator over the values of the set\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///  - [MDN documentation][mdn]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-set.prototype.values\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Set/values\n    pub(crate) fn values(\n        this: &JsValue,\n        _: &[JsValue],\n        context: &mut Context,\n    ) -> JsResult<JsValue> {\n        let this = this\n            .as_object()\n            .and_then(|o| o.downcast::<OrderedSet>().ok())\n            .ok_or_else(|| js_error!(TypeError: \"method `Set.prototype.values` called on incompatible receiver\"))?;\n\n        Ok(SetIterator::create_set_iterator(\n            this,\n            PropertyNameKind::Value,\n            context,\n        ))\n    }\n\n    /// `Set.prototype.isDisjointFrom ( other )`\n    ///\n    /// This method checks whether the current Set and the given iterable `other` have no elements in common.\n    /// It returns `true` if the two Sets are disjoint (i.e., they have no overlapping elements),\n    /// and `false` otherwise.\n    ///\n    /// More information:\n    /// - [ECMAScript reference][spec]\n    /// - [MDN documentation][mdn]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-set.prototype.isdisjointfrom\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Set/isDisjointFrom\n    pub(crate) fn is_disjoint_from(\n        this: &JsValue,\n        args: &[JsValue],\n        context: &mut Context,\n    ) -> JsResult<JsValue> {\n        // 1. Let O be the this value.\n        // 2. Perform ? RequireInternalSlot(O, [[SetData]]).\n\n        let this = this\n            .as_object()\n            .and_then(|o| o.downcast::<OrderedSet>().ok())\n            .ok_or_else(|| js_error!(TypeError: \"method `Set.prototype.isDisjointFrom` called on incompatible receiver\"))?;\n\n        // 3. Let otherRec be ? GetSetRecord(other).\n        let other = args.get_or_undefined(0);\n        let other_rec = get_set_record(other, context)?;\n\n        // 4. If SetDataSize(O.[[SetData]]) ≤ otherRec.[[Size]], then\n        if this.borrow().data().len() <= other_rec.size {\n            // a. Let thisSize be the number of elements in O.[[SetData]].\n            let mut this_size = this.borrow().data().full_len();\n            // b. Let index be 0.\n            let mut index = 0;\n            // c. Repeat, while index < thisSize,\n            while index < this_size {\n                // i. Let e be O.[[SetData]][index].\n                let e = this.borrow().data().get_index(index).cloned();\n\n                // ii. Set index to index + 1.\n                index += 1;\n\n                // iii. If e is not empty, then\n                if let Some(e) = e {\n                    // 1. Let inOther be ToBoolean(? Call(otherRec.[[Has]], otherRec.[[SetObject]], « e »)).\n                    let in_other = other_rec.has.call(other, &[e], context)?.to_boolean();\n\n                    // 2. If inOther is true, return false.\n                    if in_other {\n                        return Ok(JsValue::from(false));\n                    }\n\n                    // 3. NOTE: The number of elements in O.[[SetData]] may have increased during execution of otherRec.[[Has]].\n                    // 4. Set thisSize to the number of elements in O.[[SetData]].\n                    this_size = this.borrow().data().full_len();\n                }\n            }\n        } else {\n            // 5. Else,\n            //    a. Let keysIter be ? GetIteratorFromMethod(otherRec.[[SetObject]], otherRec.[[Keys]]).\n            let mut keys_iter = other.get_iterator_from_method(&other_rec.keys, context)?;\n\n            //    b. Let next be not-started.\n            //    c. Repeat, while next is not done,\n            //       i. Set next to ? IteratorStepValue(keysIter).\n            while let Some(next) = keys_iter.step_value(context)? {\n                //   ii. If next is not done, then\n                //       1. If SetDataHas(O.[[SetData]], next) is true, then\n                let next = canonicalize_keyed_collection_key(next);\n\n                if this.borrow().data().contains(&next) {\n                    //      a. Perform ? IteratorClose(keysIter, NormalCompletion(unused)).\n                    keys_iter.close(Ok(JsValue::undefined()), context)?;\n\n                    //      b. Return false.\n                    return Ok(JsValue::from(false));\n                }\n            }\n        }\n        // 6. Return true.\n        Ok(JsValue::from(true))\n    }\n\n    /// `Set.prototype.isSubsetOf ( other )`\n    ///\n    /// This method checks whether the current Set is a subset of the given iterable `other`.\n    /// It returns `true` if all elements of the current Set are present in the given iterable,\n    /// and `false` otherwise.\n    ///\n    /// More information:\n    /// - [ECMAScript reference][spec]\n    /// - [MDN documentation][mdn]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-set.prototype.issubsetof\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Set/isSubsetOf\n    pub(crate) fn is_subset_of(\n        this: &JsValue,\n        args: &[JsValue],\n        context: &mut Context,\n    ) -> JsResult<JsValue> {\n        // 1. Let O be the this value.\n        // 2. Perform ? RequireInternalSlot(O, [[SetData]]).\n\n        let this = this\n            .as_object()\n            .and_then(|o| o.downcast::<OrderedSet>().ok())\n            .ok_or_else(|| js_error!(TypeError: \"method `Set.prototype.isSubsetOf` called on incompatible receiver\"))?;\n\n        // 3. Let otherRec be ? GetSetRecord(other).\n        let other = args.get_or_undefined(0);\n        let other_rec = get_set_record(other, context)?;\n        // 4. If SetDataSize(O.[[SetData]]) > otherRec.[[Size]], return false.\n        if this.borrow().data().len() > other_rec.size {\n            return Ok(JsValue::from(false));\n        }\n\n        // 5. Let thisSize be the number of elements in O.[[SetData]].\n        let mut this_size = this.borrow().data().full_len();\n        // 6. Let index be 0.\n        let mut index = 0;\n\n        // 7. Repeat, while index < thisSize,\n        while index < this_size {\n            // a. Let e be O.[[SetData]][index].\n            let e = this.borrow().data().get_index(index).cloned();\n\n            // b. Set index to index + 1.\n            index += 1;\n\n            // c. If e is not empty, then\n            if let Some(e) = e {\n                // i. Let inOther be ToBoolean(? Call(otherRec.[[Has]], otherRec.[[SetObject]], « e »)).\n                let in_other = other_rec.has.call(other, &[e], context)?.to_boolean();\n\n                // ii. If inOther is false, return false.\n                if !in_other {\n                    return Ok(JsValue::from(false));\n                }\n\n                // iii. NOTE: The number of elements in O.[[SetData]] may have increased during execution of otherRec.[[Has]].\n                // iv. Set thisSize to the number of elements in O.[[SetData]].\n                this_size = this.borrow().data().full_len();\n            }\n        }\n\n        // 8. Return true.\n        Ok(JsValue::from(true))\n    }\n\n    /// `Set.prototype.isSupersetOf ( other )`\n    ///\n    /// This method checks whether the current Set is a superset of the given iterable `other`.\n    /// It returns `true` if the current Set contains all elements from the given iterable,\n    /// and `false` otherwise.\n    ///\n    /// More information:\n    /// - [ECMAScript reference][spec]\n    /// - [MDN documentation][mdn]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-set.prototype.issupersetof\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Set/isSupersetOf\n    pub(crate) fn is_superset_of(\n        this: &JsValue,\n        args: &[JsValue],\n        context: &mut Context,\n    ) -> JsResult<JsValue> {\n        // 1. Let O be the this value.\n        // 2. Perform ? RequireInternalSlot(O, [[SetData]]).\n\n        let this = this\n            .as_object()\n            .and_then(|o| o.downcast::<OrderedSet>().ok())\n            .ok_or_else(|| js_error!(TypeError: \"method `Set.prototype.isSupersetOf` called on incompatible receiver\"))?;\n\n        // 3. Let otherRec be ? GetSetRecord(other).\n        let other = args.get_or_undefined(0);\n        let other_rec = get_set_record(other, context)?;\n\n        // 4. If SetDataSize(O.[[SetData]]) < otherRec.[[Size]], return false.\n        if this.borrow().data().len() < other_rec.size {\n            return Ok(JsValue::from(false));\n        }\n\n        // 5. Let keysIter be ? GetIteratorFromMethod(otherRec.[[SetObject]], otherRec.[[Keys]]).\n        let mut keys_iter = other.get_iterator_from_method(&other_rec.keys, context)?;\n\n        // 6. Let next be not-started.\n        // 7. Repeat, while next is not done,\n        //    a. Set next to ? IteratorStepValue(keysIter).\n        while let Some(next) = keys_iter.step_value(context)? {\n            //  b. If next is not done, then\n            //     i. If SetDataHas(O.[[SetData]], next) is false, then\n            let next = canonicalize_keyed_collection_key(next);\n            if !this.borrow().data().contains(&next) {\n                // 1. Perform ? IteratorClose(keysIter, NormalCompletion(unused)).\n                keys_iter.close(Ok(JsValue::undefined()), context)?;\n                // 2. Return false.\n                return Ok(JsValue::from(false));\n            }\n        }\n\n        // 8. Return true.\n        Ok(JsValue::from(true))\n    }\n\n    /// ` Set.prototype.symmetricDifference(other)`\n    ///\n    /// Returns a new set containing the symmetric difference between the current set (`this`)\n    /// and the provided set (`other`)\n    ///\n    /// More information:\n    /// - [ECMAScript reference][spec]\n    /// - [MDN documentation][mdn]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-set.prototype.symmetricdifference\n    /// [mdn]: https://developer.mozilla.org/en-USSet/docs/Web/JavaScript/Reference/Global_Objects/Set/symmetricDifference\n    pub(crate) fn symmetric_difference(\n        this: &JsValue,\n        args: &[JsValue],\n        context: &mut Context,\n    ) -> JsResult<JsValue> {\n        // 1. Let O be the this value.\n        // 2. Perform ? RequireInternalSlot(O, [[SetData]]).\n        let this = this\n            .as_object()\n            .and_then(|o| o.downcast::<OrderedSet>().ok())\n            .ok_or_else(|| js_error!(TypeError: \"method `Set.prototype.symmetricDifference` called on incompatible receiver\"))?;\n\n        // 3. Let otherRec be ? GetSetRecord(other).\n        let other = args.get_or_undefined(0);\n        let other_rec = get_set_record(other, context)?;\n\n        // 4. Let keysIter be ? GetIteratorFromMethod(otherRec.[[SetObject]], otherRec.[[Keys]]).\n        let mut keys_iter = other.get_iterator_from_method(&other_rec.keys, context)?;\n\n        // 5. Let resultSetData be a copy of O.[[SetData]].\n        let mut result_set = this.borrow().data().clone();\n\n        // 6. Let next be not-started.\n        // 7. Repeat, while next is not done,\n        while let Some(next) = keys_iter.step_value(context)? {\n            //  a. Set next to ? IteratorStepValue(keysIter).\n            //  b. If next is not done, then\n            //    i. Set next to CanonicalizeKeyedCollectionKey(next).\n            let next = canonicalize_keyed_collection_key(next);\n\n            //    ii. Let resultIndex be SetDataIndex(resultSetData, next).\n            //    iii. If resultIndex is not-found, let alreadyInResult be false. Otherwise let alreadyInResult be true.\n\n            //    iv. If SetDataHas(O.[[SetData]], next) is true, then\n            //  1. If alreadyInResult is true, set resultSetData[resultIndex] to empty.\n            if this.borrow().data().contains(&next) {\n                result_set.delete(&next);\n            }\n            //    v. Else,\n            //      1. If alreadyInResult is false, append next to resultSetData.\n            else {\n                result_set.add(next);\n            }\n        }\n\n        //     8. Let result be OrdinaryObjectCreate(%Set.prototype%, « [[SetData]] »).\n        //     9. Set result.[[SetData]] to resultSetData.\n        //     10. Return result.\n        Ok(JsObject::from_proto_and_data_with_shared_shape(\n            context.root_shape(),\n            context.intrinsics().constructors().set().prototype(),\n            result_set,\n        )\n        .into())\n    }\n\n    /// `Set.prototype.union ( other )`\n    ///\n    /// Returns a new set containing the union of the elements in the current set (`this`)\n    /// and the set provided as the argument (`other`).\n    ///\n    /// More information:\n    /// - [ECMAScript reference][spec]\n    /// - [MDN documentation][mdn]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-set.prototype.union\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Set/union\n    pub(crate) fn union(\n        this: &JsValue,\n        args: &[JsValue],\n        context: &mut Context,\n    ) -> JsResult<JsValue> {\n        // 1. Let O be the this value.\n        // 2. Perform ? RequireInternalSlot(O, [[SetData]]).\n\n        let this = this\n            .as_object()\n            .and_then(|o| o.downcast::<OrderedSet>().ok())\n            .ok_or_else(|| js_error!(TypeError: \"method `Set.prototype.union` called on incompatible receiver\"))?;\n\n        // 3. Let otherRec be ? GetSetRecord(other).\n        let other = args.get_or_undefined(0);\n        let other_rec = get_set_record(other, context)?;\n\n        // 4. Let keysIter be ? GetIteratorFromMethod(otherRec.[[SetObject]], otherRec.[[Keys]]).\n        let mut keys_iter = other.get_iterator_from_method(&other_rec.keys, context)?;\n\n        // 5. Let resultSetData be a copy of O.[[SetData]].\n        let mut result_set = this.borrow().data().clone();\n\n        // 6. Let next be not-started.\n        // 7. Repeat, while next is not done,\n        //        a. Set next to ? IteratorStepValue(keysIter).\n        //        b. If next is not done, then\n        //               i. Set next to CanonicalizeKeyedCollectionKey(next).\n        //               ii. If SetDataHas(resultSetData, next) is false, then\n        //                       1. Append next to resultSetData.\n        while let Some(next) = keys_iter.step_value(context)? {\n            result_set.add(canonicalize_keyed_collection_key(next));\n        }\n\n        // 8. Let result be OrdinaryObjectCreate(%Set.prototype%, « [[SetData]] »).\n        // 9. Set result.[[SetData]] to resultSetData.\n        // 10. Return result.\n        Ok(JsObject::from_proto_and_data_with_shared_shape(\n            context.root_shape(),\n            context.intrinsics().constructors().set().prototype(),\n            result_set,\n        )\n        .into())\n    }\n\n    /// `Set.prototype.intersection ( other )`\n    ///\n    /// This method returns a new Set containing all elements that are present in both\n    /// the current Set and the given iterable `other`.\n    ///\n    /// It effectively computes the intersection of the two Sets.\n    ///\n    /// More information:\n    /// - [ECMAScript reference][spec]\n    /// - [MDN documentation][mdn]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-set.prototype.intersection\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Set/intersection\n    pub(crate) fn intersection(\n        this: &JsValue,\n        args: &[JsValue],\n        context: &mut Context,\n    ) -> JsResult<JsValue> {\n        // 1. Let S be the this value.\n        // 2. Perform ? RequireInternalSlot(O, [[SetData]]).\n        let this = this\n            .as_object()\n            .and_then(|o| o.downcast::<OrderedSet>().ok())\n            .ok_or_else(|| js_error!(TypeError: \"method `Set.prototype.intersection` called on incompatible receiver\"))?;\n\n        // 3. Let otherRec be ? GetSetRecord(other).\n        let other = args.get_or_undefined(0);\n        let other_rec = get_set_record(other, context)?;\n\n        // 4. Let resultSetData be a new empty List.\n        let mut result_set = OrderedSet::new();\n\n        // 5. If SetDataSize(O.[[SetData]]) ≤ otherRec.[[Size]], then\n        if this.borrow().data().len() <= other_rec.size {\n            // a. Let thisSize be the number of elements in O.[[SetData]].\n            let mut this_size = this.borrow().data().full_len();\n            // b. Let index be 0.\n            let mut index = 0;\n            // c. Repeat, while index < thisSize,\n            while index < this_size {\n                // i. Let e be O.[[SetData]][index].\n                let e = this.borrow().data().get_index(index).cloned();\n                // ii. Set index to index + 1.\n                index += 1;\n\n                // iii. If e is not empty, then\n                let Some(e) = e else {\n                    continue;\n                };\n\n                //      1. Let inOther be ToBoolean(? Call(otherRec.[[Has]], otherRec.[[SetObject]], « e »)).\n                let in_other = other_rec\n                    .has\n                    .call(other, std::slice::from_ref(&e), context)?;\n                //      2. If inOther is true, then\n                //         a. NOTE: It is possible for earlier calls to otherRec.[[Has]] to remove and re-add an element of O.[[SetData]], which can cause the same element to be visited twice during this iteration.\n                if in_other.to_boolean() {\n                    //     b. If SetDataHas(resultSetData, e) is false, then\n                    //        i. Append e to resultSetData.\n                    result_set.add(e);\n                    //  3. NOTE: The number of elements in O.[[SetData]] may have increased during execution of otherRec.[[Has]].\n                    //  4. Set thisSize to the number of elements in O.[[SetData]].\n                    this_size = this.borrow().data().full_len();\n                }\n            }\n\n        // 6. Else,\n        } else {\n            // a. Let keysIter be ? GetIteratorFromMethod(otherRec.[[SetObject]], otherRec.[[Keys]]).\n            let mut keys_iter = other.get_iterator_from_method(&other_rec.keys, context)?;\n            // b. Let next be not-started.\n            // c. Repeat, while next is not done,\n            while let Some(next) = keys_iter.step_value(context)? {\n                // i. Set next to ? IteratorStepValue(keysIter).\n                // ii. If next is not done, then\n                //     1. Set next to CanonicalizeKeyedCollectionKey(next).\n                let next = canonicalize_keyed_collection_key(next);\n                //     2. Let inThis be SetDataHas(O.[[SetData]], next).\n                //     3. If inThis is true, then\n                if this.borrow().data().contains(&next) {\n                    //        a. NOTE: Because other is an arbitrary object, it is possible for its \"keys\" iterator to produce the same value more than once.\n                    //        b. If SetDataHas(resultSetData, next) is false, then\n                    //           i. Append next to resultSetData.\n                    result_set.add(next);\n                }\n            }\n        }\n\n        // 7. Let result be OrdinaryObjectCreate(%Set.prototype%, « [[SetData]] »).\n        // 8. Set result.[[SetData]] to resultSetData.\n        // 9. Return result.\n        Ok(JsObject::from_proto_and_data_with_shared_shape(\n            context.root_shape(),\n            context.intrinsics().constructors().set().prototype(),\n            result_set,\n        )\n        .into())\n    }\n\n    /// ` Set.prototype.difference ( other ) `\n    ///\n    /// This method returns a new Set containing all elements that are in the current Set\n    /// but not in the given iterable `other`.\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///  - [MDN documentation][mdn]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-set.prototype.difference\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Set/difference\n    pub(crate) fn difference(\n        this: &JsValue,\n        args: &[JsValue],\n        context: &mut Context,\n    ) -> JsResult<JsValue> {\n        // 1. Let O be the this value.\n        // 2. Perform ? RequireInternalSlot(O, [[SetData]]).\n        let this = this\n            .as_object()\n            .and_then(|o| o.downcast::<OrderedSet>().ok())\n            .ok_or_else(|| {\n                js_error!(\n                    TypeError: \"method `Set.prototype.difference` called on incompatible receiver\"\n                )\n            })?;\n\n        // 3. Let otherRec be ? GetSetRecord(other).\n        let other = args.get_or_undefined(0);\n        let other_rec = get_set_record(other, context)?;\n\n        // 4. Let resultSetData be a copy of O.[[SetData]].\n        let mut result_set = this.borrow().data().clone();\n\n        // 5. If SetDataSize(O.[[SetData]]) ≤ otherRec.[[Size]], then\n        if result_set.len() <= other_rec.size {\n            // a. Let thisSize be the number of elements in O.[[SetData]].\n            let this_size = this.borrow().data().full_len();\n            // b. Let index be 0.\n            let mut index = 0;\n\n            //  c. Repeat, while index < thisSize,\n            while index < this_size {\n                // i. Let e be resultSetData[index].\n                let e = result_set.get_index(index).cloned();\n\n                // ii. If e is not empty, then\n                if let Some(e) = e {\n                    // 1. Let inOther be ToBoolean(? Call(otherRec.[[Has]], otherRec.[[SetObject]], « e »)).\n                    let in_other = other_rec\n                        .has\n                        .call(other, std::slice::from_ref(&e), context)?\n                        .to_boolean();\n                    // 2. If inOther is true, then\n                    if in_other {\n                        // a. Set resultSetData[index] to empty.\n                        result_set.delete(&e);\n\n                        // No need to increment the index, after deletion we're\n                        // in the correct next position.\n                        continue;\n                    }\n                }\n\n                // iii. Set index to index + 1.\n                index += 1;\n            }\n        }\n        // 6. Else,\n        else {\n            // a. Let keysIter be ? GetIteratorFromMethod(otherRec.[[SetObject]], otherRec.[[Keys]]).\n            let mut keys_iter = other.get_iterator_from_method(&other_rec.keys, context)?;\n            // b. Let next be not-started.\n            // c. Repeat, while next is not done,\n            //     i. Set next to ? IteratorStepValue(keysIter).\n            while let Some(next) = keys_iter.step_value(context)? {\n                // ii. If next is not done, then\n                //     1. Set next to CanonicalizeKeyedCollectionKey(next).\n                let next = canonicalize_keyed_collection_key(next);\n                //     2. Let valueIndex be SetDataIndex(resultSetData, next).\n                //     3. If valueIndex is not not-found, then\n                //        a. Set resultSetData[valueIndex] to empty.\n                result_set.delete(&next);\n            }\n        }\n\n        // 7. Let result be OrdinaryObjectCreate(%Set.prototype%, « [[SetData]] »).\n        // 8. Set result.[[SetData]] to resultSetData.\n        // 9. Return result.\n        Ok(JsObject::from_proto_and_data_with_shared_shape(\n            context.root_shape(),\n            context.intrinsics().constructors().set().prototype(),\n            result_set,\n        )\n        .into())\n    }\n\n    fn size_getter(this: &JsValue, _: &[JsValue], _: &mut Context) -> JsResult<JsValue> {\n        Ok(this\n            .as_object()\n            .and_then(|o| o.downcast_ref::<OrderedSet>().map(|o| o.len()))\n            .ok_or_else(|| js_error!(TypeError: \"method `get Set.prototype.size` called on incompatible receiver\"))?.into())\n    }\n}\n"
  },
  {
    "path": "core/engine/src/builtins/set/ordered_set.rs",
    "content": "//! Implements a set type that preserves insertion order.\n\nuse crate::{JsData, JsValue, builtins::map::ordered_map::MapKey, object::JsObject};\nuse boa_gc::{Finalize, Trace, custom_trace};\nuse indexmap::IndexSet;\nuse std::fmt::Debug;\n\n/// A type wrapping `indexmap::IndexSet`\n#[derive(Default, Clone, Finalize, JsData)]\npub struct OrderedSet {\n    inner: IndexSet<MapKey>,\n    lock: u32,\n    empty_count: usize,\n}\n\nunsafe impl Trace for OrderedSet {\n    custom_trace!(this, mark, {\n        for v in &this.inner {\n            if let MapKey::Key(v) = v {\n                mark(v);\n            }\n        }\n    });\n}\n\nimpl Debug for OrderedSet {\n    fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {\n        self.inner.fmt(formatter)\n    }\n}\n\nimpl OrderedSet {\n    /// Creates a new empty `OrderedSet`.\n    #[must_use]\n    pub fn new() -> Self {\n        Self::default()\n    }\n\n    /// Creates a new empty `OrderedSet` with the specified capacity.\n    #[must_use]\n    pub fn with_capacity(capacity: usize) -> Self {\n        Self {\n            inner: IndexSet::with_capacity(capacity),\n            lock: 0,\n            empty_count: 0,\n        }\n    }\n\n    /// Return the number of elements in the set, including empty elements.\n    ///\n    /// Computes in **O(1)** time.\n    #[must_use]\n    pub fn full_len(&self) -> usize {\n        self.inner.len()\n    }\n\n    /// Return the number of elements in the set.\n    ///\n    /// Computes in **O(1)** time.\n    #[must_use]\n    pub fn len(&self) -> usize {\n        self.inner.len() - self.empty_count\n    }\n\n    /// Returns true if the set contains no elements.\n    ///\n    /// Computes in **O(1)** time.\n    #[must_use]\n    pub fn is_empty(&self) -> bool {\n        self.inner.len() == 0\n    }\n\n    /// Insert a value pair in the set.\n    ///\n    /// If an equivalent value already exists in the set, returns `false` leaving\n    /// original value in the set and without altering its insertion order.\n    ///\n    /// If no equivalent value existed in the set, returns `true` and inserts\n    /// the new value at the end of the set.\n    ///\n    /// Computes in **O(1)** time (amortized average).\n    pub fn add(&mut self, value: JsValue) -> bool {\n        self.inner.insert(MapKey::Key(value))\n    }\n\n    /// Delete the `value` from the set and return true if successful\n    ///\n    /// Return `false` if `value` is not in set.\n    ///\n    /// Computes in **O(n)** time (average).\n    pub fn delete(&mut self, value: &JsValue) -> bool {\n        if self.lock == 0 {\n            self.inner.shift_remove(value)\n        } else if self.inner.contains(value) {\n            self.inner.insert(MapKey::Empty(self.empty_count));\n            self.empty_count += 1;\n            self.inner.swap_remove(value)\n        } else {\n            false\n        }\n    }\n\n    /// Removes all elements in the set, while preserving its capacity.\n    #[inline]\n    pub fn clear(&mut self) {\n        self.inner.clear();\n        self.inner.shrink_to_fit();\n        self.empty_count = 0;\n    }\n\n    /// Checks if a given value is present in the set\n    ///\n    /// Return `true` if `value` is present in set, false otherwise.\n    ///\n    /// Computes in **O(n)** time (average).\n    #[must_use]\n    pub fn contains(&self, value: &JsValue) -> bool {\n        self.inner.contains(value)\n    }\n\n    /// Get a key-value pair by index\n    /// Valid indices are 0 <= `index` < `self.len()`\n    /// Computes in O(1) time.\n    #[must_use]\n    pub fn get_index(&self, index: usize) -> Option<&JsValue> {\n        if let MapKey::Key(value) = self.inner.get_index(index)? {\n            Some(value)\n        } else {\n            None\n        }\n    }\n\n    /// Return an iterator over the values of the set, in their order\n    pub fn iter(&self) -> impl Iterator<Item = &JsValue> {\n        self.inner.iter().filter_map(|v| {\n            if let MapKey::Key(v) = v {\n                Some(v)\n            } else {\n                None\n            }\n        })\n    }\n\n    /// Increases the lock counter and returns a lock object that will decrement the counter when dropped.\n    ///\n    /// This allows objects to be removed from the set during iteration without affecting the indexes until the iteration has completed.\n    pub(crate) fn lock(&mut self) {\n        self.lock += 1;\n    }\n\n    /// Decreases the lock counter and, if 0, removes all empty entries.\n    pub(crate) fn unlock(&mut self) {\n        self.lock -= 1;\n        if self.lock == 0 {\n            self.inner.retain(|k| matches!(k, MapKey::Key(_)));\n            self.empty_count = 0;\n        }\n    }\n}\n\npub(crate) struct SetLock<'a>(&'a JsObject<OrderedSet>);\n\nimpl<'a> SetLock<'a> {\n    pub(crate) fn new(js_object: &'a JsObject<OrderedSet>) -> Self {\n        js_object.borrow_mut().data_mut().lock();\n        Self(js_object)\n    }\n}\n\nimpl Drop for SetLock<'_> {\n    fn drop(&mut self) {\n        self.0.borrow_mut().data_mut().unlock();\n    }\n}\n"
  },
  {
    "path": "core/engine/src/builtins/set/set_iterator.rs",
    "content": "//! This module implements the `SetIterator` object.\n//!\n//! More information:\n//!  - [ECMAScript reference][spec]\n//!\n//! [spec]: https://tc39.es/ecma262/#sec-set-iterator-objects\n\nuse super::ordered_set::OrderedSet;\nuse crate::{\n    Context, JsData, JsResult,\n    builtins::{\n        Array, BuiltInBuilder, IntrinsicObject, JsValue, iterable::create_iter_result_object,\n    },\n    context::intrinsics::Intrinsics,\n    error::JsNativeError,\n    js_string,\n    object::JsObject,\n    property::{Attribute, PropertyNameKind},\n    realm::Realm,\n    symbol::JsSymbol,\n};\nuse boa_gc::{Finalize, Trace};\n\n#[derive(Debug, Trace)]\nstruct SetIteratorLock(JsObject<OrderedSet>);\n\nimpl SetIteratorLock {\n    fn new(js_object: JsObject<OrderedSet>) -> Self {\n        js_object.borrow_mut().data_mut().lock();\n        Self(js_object)\n    }\n}\n\nimpl Finalize for SetIteratorLock {\n    fn finalize(&self) {\n        self.0.borrow_mut().data_mut().unlock();\n    }\n}\n\n/// The Set Iterator object represents an iteration over a set. It implements the iterator protocol.\n///\n/// More information:\n///  - [ECMAScript reference][spec]\n///\n/// [spec]: https://tc39.es/ecma262/#sec-set-iterator-objects\n#[derive(Debug, Finalize, Trace, JsData)]\npub(crate) struct SetIterator {\n    iterated_set: Option<SetIteratorLock>,\n    next_index: usize,\n    #[unsafe_ignore_trace]\n    iteration_kind: PropertyNameKind,\n}\n\nimpl IntrinsicObject for SetIterator {\n    fn init(realm: &Realm) {\n        BuiltInBuilder::with_intrinsic::<Self>(realm)\n            .prototype(\n                realm\n                    .intrinsics()\n                    .objects()\n                    .iterator_prototypes()\n                    .iterator(),\n            )\n            .static_method(Self::next, js_string!(\"next\"), 0)\n            .static_property(\n                JsSymbol::to_string_tag(),\n                js_string!(\"Set Iterator\"),\n                Attribute::CONFIGURABLE,\n            )\n            .build();\n    }\n\n    fn get(intrinsics: &Intrinsics) -> JsObject {\n        intrinsics.objects().iterator_prototypes().set()\n    }\n}\n\nimpl SetIterator {\n    /// Abstract operation `CreateSetIterator( set, kind )`\n    ///\n    /// Creates a new iterator over the given set.\n    ///\n    /// More information:\n    ///  - [ECMA reference][spec]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-createsetiterator\n    pub(crate) fn create_set_iterator(\n        set: JsObject<OrderedSet>,\n        kind: PropertyNameKind,\n        context: &Context,\n    ) -> JsValue {\n        let set_iterator = JsObject::from_proto_and_data_with_shared_shape(\n            context.root_shape(),\n            context.intrinsics().objects().iterator_prototypes().set(),\n            Self {\n                iterated_set: Some(SetIteratorLock::new(set)),\n                next_index: 0,\n                iteration_kind: kind,\n            },\n        );\n        set_iterator.into()\n    }\n\n    /// %SetIteratorPrototype%.next( )\n    ///\n    /// Advances the iterator and gets the next result in the set.\n    ///\n    /// More information:\n    ///  - [ECMA reference][spec]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-%setiteratorprototype%.next\n    pub(crate) fn next(this: &JsValue, _: &[JsValue], context: &mut Context) -> JsResult<JsValue> {\n        let object = this.as_object();\n        let mut set_iterator = object\n            .as_ref()\n            .and_then(JsObject::downcast_mut::<Self>)\n            .ok_or_else(|| JsNativeError::typ().with_message(\"`this` is not an SetIterator\"))?;\n\n        let item_kind = set_iterator.iteration_kind;\n\n        if let Some(obj) = set_iterator.iterated_set.take() {\n            let e = {\n                let mut entries = obj.0.borrow_mut();\n                let entries = entries.data_mut();\n                let len = entries.full_len();\n                loop {\n                    let element = entries.get_index(set_iterator.next_index);\n                    set_iterator.next_index += 1;\n                    if element.is_some() || set_iterator.next_index >= len {\n                        break element.cloned();\n                    }\n                }\n            };\n            if let Some(element) = e {\n                let item = match item_kind {\n                    PropertyNameKind::KeyAndValue => {\n                        let result =\n                            Array::create_array_from_list([element.clone(), element], context);\n                        Ok(create_iter_result_object(result.into(), false, context))\n                    }\n                    _ => Ok(create_iter_result_object(element, false, context)),\n                };\n                set_iterator.iterated_set = Some(obj);\n                return item;\n            }\n        }\n\n        Ok(create_iter_result_object(\n            JsValue::undefined(),\n            true,\n            context,\n        ))\n    }\n}\n"
  },
  {
    "path": "core/engine/src/builtins/set/tests.rs",
    "content": "use crate::{JsNativeErrorKind, TestAction, run_test_actions};\nuse indoc::indoc;\n\n#[test]\nfn construct() {\n    run_test_actions([\n        TestAction::assert_eq(\"(new Set()).size\", 0),\n        TestAction::assert_eq(\"(new Set(['one', 'two'])).size\", 2),\n    ]);\n}\n\n#[test]\nfn clone() {\n    run_test_actions([\n        TestAction::run(indoc! {r#\"\n                let original = new Set([\"one\", \"two\"]);\n                let clone = new Set(original);\n            \"#}),\n        TestAction::assert_eq(\"clone.size\", 2),\n        TestAction::assert_eq(\n            indoc! {r#\"\n                original.add(\"three\");\n                original.size\n            \"#},\n            3,\n        ),\n        TestAction::assert_eq(\"clone.size\", 2),\n    ]);\n}\n\n#[test]\nfn symbol_iterator() {\n    run_test_actions([\n        TestAction::run_harness(),\n        TestAction::run(indoc! {r#\"\n                const set1 = new Set();\n                set1.add('foo');\n                set1.add('bar');\n            \"#}),\n        TestAction::assert(indoc! {r#\"\n                arrayEquals(\n                    Array.from(set1[Symbol.iterator]()),\n                    [\"foo\", \"bar\"]\n                )\n            \"#}),\n    ]);\n}\n\n#[test]\nfn entries() {\n    run_test_actions([\n        TestAction::run_harness(),\n        TestAction::run(indoc! {r#\"\n                const set1 = new Set();\n                set1.add('foo');\n                set1.add('bar')\n            \"#}),\n        TestAction::assert(indoc! {r#\"\n                arrayEquals(\n                    Array.from(set1.entries()),\n                    [[\"foo\", \"foo\"], [\"bar\", \"bar\"]]\n                )\n            \"#}),\n    ]);\n}\n\n#[test]\nfn merge() {\n    run_test_actions([\n        TestAction::run(indoc! {r#\"\n                let first = new Set([\"one\", \"two\"]);\n                let second = new Set([\"three\", \"four\"]);\n                let third = new Set([\"four\", \"five\"]);\n                let merged1 = new Set([...first, ...second]);\n                let merged2 = new Set([...second, ...third]);\n            \"#}),\n        TestAction::assert_eq(\"merged1.size\", 4),\n        TestAction::assert_eq(\"merged2.size\", 3),\n    ]);\n}\n\n#[test]\nfn clear() {\n    run_test_actions([\n        TestAction::run(indoc! {r#\"\n            let set = new Set([\"one\", \"two\"]);\n            set.clear();\n        \"#}),\n        TestAction::assert_eq(\"set.size\", 0),\n    ]);\n}\n\n#[test]\nfn delete() {\n    run_test_actions([\n        TestAction::run(\"let set = new Set(['one', 'two'])\"),\n        TestAction::assert(\"set.delete('one')\"),\n        TestAction::assert_eq(\"set.size\", 1),\n        TestAction::assert(\"!set.delete('one')\"),\n    ]);\n}\n\n#[test]\nfn has() {\n    run_test_actions([\n        TestAction::run(\"let set = new Set(['one', 'two']);\"),\n        TestAction::assert(\"set.has('one')\"),\n        TestAction::assert(\"set.has('two')\"),\n        TestAction::assert(\"!set.has('three')\"),\n        TestAction::assert(\"!set.has()\"),\n    ]);\n}\n\n#[test]\nfn values_and_keys() {\n    run_test_actions([\n        TestAction::run_harness(),\n        TestAction::run(indoc! {r#\"\n                const set1 = new Set();\n                set1.add('foo');\n                set1.add('bar');\n            \"#}),\n        TestAction::assert(indoc! {r#\"\n            arrayEquals(\n                Array.from(set1.values()),\n                [\"foo\", \"bar\"]\n            )\n        \"#}),\n        TestAction::assert(\"set1.values == set1.keys\"),\n    ]);\n}\n\n#[test]\nfn for_each() {\n    run_test_actions([\n        TestAction::run(indoc! {r#\"\n                let set = new Set([5, 10, 15]);\n                let value1Sum = 0;\n                let value2Sum = 0;\n                let sizeSum = 0;\n                function callingCallback(value1, value2, set) {\n                    value1Sum += value1;\n                    value2Sum += value2;\n                    sizeSum += set.size;\n                }\n                set.forEach(callingCallback);\n            \"#}),\n        TestAction::assert_eq(\"value1Sum\", 30),\n        TestAction::assert_eq(\"value2Sum\", 30),\n        TestAction::assert_eq(\"sizeSum\", 9),\n    ]);\n}\n\n#[test]\nfn recursive_display() {\n    run_test_actions([\n        TestAction::run(indoc! {r#\"\n                let set = new Set();\n                let array = new Array([set]);\n                set.add(set);\n            \"#}),\n        TestAction::assert_with_op(\"set\", |v, _| v.display().to_string() == \"Set { Set(1) }\"),\n        TestAction::assert_with_op(\"set.add(array)\", |v, _| {\n            v.display().to_string() == \"Set { Set(2), Array(1) }\"\n        }),\n    ]);\n}\n\n#[test]\nfn not_a_function() {\n    run_test_actions([TestAction::assert_native_error(\n        \"Set()\",\n        JsNativeErrorKind::Type,\n        \"calling a builtin Set constructor without new is forbidden\",\n    )]);\n}\n\n#[test]\nfn difference() {\n    run_test_actions([\n        TestAction::run(indoc! {r#\"\n            let setA = new Set([1, 3, 5, 7, 9]);\n            let setB = new Set([1, 4, 9]);\n        \"#}),\n        TestAction::assert_with_op(\"setA.difference(setB)\", |v, _| {\n            v.display().to_string() == \"Set { 3, 5, 7 }\"\n        }),\n        TestAction::assert_with_op(\"setB.difference(setA)\", |v, _| {\n            v.display().to_string() == \"Set { 4 }\"\n        }),\n    ]);\n}\n\n#[test]\nfn difference_equal_set() {\n    run_test_actions([\n        TestAction::run(indoc! {r#\"\n            let setA = new Set([1, 3, 5, 7, 9]);\n            let setB = new Set([1, 4, 5, 7, 9]);\n        \"#}),\n        TestAction::assert_with_op(\"setA.difference(setB)\", |v, _| {\n            v.display().to_string() == \"Set { 3 }\"\n        }),\n        TestAction::assert_with_op(\"setB.difference(setA)\", |v, _| {\n            v.display().to_string() == \"Set { 4 }\"\n        }),\n    ]);\n}\n\n#[test]\nfn difference_empty() {\n    run_test_actions([\n        TestAction::run(indoc! {r#\"\n           let setA = new Set([1, 3, 5, 7, 9]);\n           let setB = new Set([]);\n        \"#}),\n        TestAction::assert_with_op(\"setA.difference(setB)\", |v, _| {\n            v.display().to_string() == \"Set { 1, 3, 5, 7, 9 }\"\n        }),\n        TestAction::assert_with_op(\"setB.difference(setA)\", |v, _| {\n            v.display().to_string() == \"Set(0)\"\n        }),\n    ]);\n}\n\n#[test]\nfn intersection() {\n    run_test_actions([\n        TestAction::run(indoc! {r#\"\n            let setA = new Set([1,2,3]);\n            let setB = new Set([1,4,3]);\n            let setC = new Set([]);\n            \"#}),\n        TestAction::assert_with_op(\"setA.intersection(setB)\", |v, _| {\n            v.display().to_string() == \"Set { 1, 3 }\"\n        }),\n        TestAction::assert_with_op(\"setB.intersection(setA)\", |v, _| {\n            v.display().to_string() == \"Set { 1, 3 }\"\n        }),\n        TestAction::assert_with_op(\"setA.intersection(setA)\", |v, _| {\n            v.display().to_string() == \"Set { 1, 2, 3 }\"\n        }),\n        TestAction::assert_with_op(\"setB.intersection(setB)\", |v, _| {\n            v.display().to_string() == \"Set { 1, 4, 3 }\"\n        }),\n        TestAction::assert_with_op(\"setB.intersection(setC)\", |v, _| {\n            v.display().to_string() == \"Set(0)\"\n        }),\n        TestAction::assert_with_op(\"setA.intersection(setC)\", |v, _| {\n            v.display().to_string() == \"Set(0)\"\n        }),\n    ]);\n}\n\n#[test]\nfn is_dist_joint_from() {\n    run_test_actions([\n        TestAction::run(indoc! {r#\"\n            let setA = new Set([1, 2, 3]);\n            let setB = new Set([1, 4, 6]);\n            let setC = new Set([4, 8, 15, 16 ,23 ,42]);\n            \"#}),\n        TestAction::assert_with_op(\"setA.isDisjointFrom(setB)\", |v, _| {\n            !v.as_boolean().unwrap_or(false)\n        }),\n        TestAction::assert_with_op(\"setA.isDisjointFrom(setC)\", |v, _| {\n            v.as_boolean().unwrap_or(true)\n        }),\n    ]);\n}\n\n#[test]\nfn is_subset_of() {\n    run_test_actions([\n        TestAction::run(indoc! {r#\"\n            let setA = new Set([4, 8, 15]);\n            let setB = new Set([1, 4, 6]);\n            let setC = new Set([4, 8, 15, 16 ,23 ,42]);\n            let setD = new Set([16]);\n            let setE = new Set([]);\n            \"#}),\n        TestAction::assert_with_op(\"setA.isSubsetOf(setB)\", |v, _| {\n            !v.as_boolean().unwrap_or(false)\n        }),\n        TestAction::assert_with_op(\"setA.isSubsetOf(setC)\", |v, _| {\n            v.as_boolean().unwrap_or(true)\n        }),\n        TestAction::assert_with_op(\"setB.isSubsetOf(setC)\", |v, _| {\n            !v.as_boolean().unwrap_or(false)\n        }),\n        TestAction::assert_with_op(\"setC.isSubsetOf(setC)\", |v, _| {\n            v.as_boolean().unwrap_or(true)\n        }),\n        TestAction::assert_with_op(\"setD.isSubsetOf(setC)\", |v, _| {\n            v.as_boolean().unwrap_or(true)\n        }),\n        TestAction::assert_with_op(\"setE.isSubsetOf(setC)\", |v, _| {\n            v.as_boolean().unwrap_or(true)\n        }),\n        TestAction::assert_with_op(\"setA.isSubsetOf(setE)\", |v, _| {\n            !v.as_boolean().unwrap_or(false)\n        }),\n    ]);\n}\n\n#[test]\nfn is_superset_of() {\n    run_test_actions([\n        TestAction::run(indoc! {r#\"\n            let setA = new Set([\"JavaScript\", \"HTML\", \"CSS\"]);\n            let setB = new Set([\"HTML\", \"CSS\"]);\n            \"#}),\n        TestAction::assert_with_op(\"setA.isSupersetOf(setB)\", |v, _| {\n            v.as_boolean().unwrap_or(false)\n        }),\n        TestAction::assert_with_op(\"setB.isSupersetOf(setA)\", |v, _| {\n            !v.as_boolean().unwrap_or(false)\n        }),\n    ]);\n}\n\n#[test]\nfn symmetric_difference_different_sets_strings() {\n    run_test_actions([\n        TestAction::run(indoc! {r#\"\n            let setA = new Set([\"JavaScript\", \"HTML\", \"CSS\"]);\n            let setB = new Set([\"Python\", \"Java\", \"JavaScript\", \"PHP\"]);\n            \"#}),\n        TestAction::assert_with_op(\"setA.symmetricDifference(setB)\", |v, _| {\n            v.display().to_string() == \"Set { \\\"HTML\\\", \\\"CSS\\\", \\\"Python\\\", \\\"Java\\\", \\\"PHP\\\" }\"\n        }),\n        TestAction::assert_with_op(\"setB.symmetricDifference(setA)\", |v, _| {\n            v.display().to_string() == \"Set { \\\"Python\\\", \\\"Java\\\", \\\"PHP\\\", \\\"HTML\\\", \\\"CSS\\\" }\"\n        }),\n    ]);\n}\n\n#[test]\nfn symmetric_difference_different_sets_numbers() {\n    run_test_actions([\n        TestAction::run(indoc! {r#\"\n            let setC = new Set([2, 4, 6, 8]);\n            let setD = new Set([1, 4, 9]);\n            \"#}),\n        TestAction::assert_with_op(\"setC.symmetricDifference(setD)\", |v, _| {\n            v.display().to_string() == \"Set { 2, 6, 8, 1, 9 }\"\n        }),\n        TestAction::assert_with_op(\"setD.symmetricDifference(setC)\", |v, _| {\n            v.display().to_string() == \"Set { 1, 9, 2, 6, 8 }\"\n        }),\n    ]);\n}\n\n#[test]\nfn symmetric_difference_same_set() {\n    run_test_actions([\n        TestAction::run(indoc! {r#\"\n            let setA = new Set([\"JavaScript\", \"HTML\", \"CSS\"]);\n            let setACopy = setA;\n        \"#}),\n        TestAction::assert_with_op(\"setA.symmetricDifference(setACopy)\", |v, _| {\n            v.display().to_string() == \"Set(0)\"\n        }),\n    ]);\n}\n\n// Alternative test that programmatically creates a new Set with the same content.\n#[test]\nfn symmetric_difference_with_identical_content() {\n    run_test_actions([\n        TestAction::run(indoc! {r#\"\n            let setA = new Set([\"JavaScript\", \"HTML\", \"CSS\"]);\n            // Создаем функцию, которая вернет новый Set с тем же содержимым\n            function getIdenticalSet() {\n                return new Set(Array.from(setA));\n            }\n            \"#}),\n        // We use a function to get a new Set object with the same content.\n        TestAction::assert_with_op(\"setA.symmetricDifference(getIdenticalSet())\", |v, _| {\n            v.display().to_string() == \"Set(0)\"\n        }),\n    ]);\n}\n\n#[test]\nfn union() {\n    run_test_actions([\n        TestAction::run(indoc! {r#\"\n            let setA = new Set([2, 4, 6, 8]);\n            let setB = new Set([1, 4, 9]);\n            \"#}),\n        TestAction::assert_with_op(\"setA.union(setB)\", |v, _| {\n            v.display().to_string() == \"Set { 2, 4, 6, 8, 1, 9 }\"\n        }),\n        TestAction::assert_with_op(\"setB.union(setA)\", |v, _| {\n            v.display().to_string() == \"Set { 1, 4, 9, 2, 6, 8 }\"\n        }),\n    ]);\n}\n\n#[test]\nfn union_same_set() {\n    run_test_actions([\n        TestAction::run(indoc! {r#\"\n            let setA = new Set([1, 4, 9]);\n            \"#}),\n        TestAction::assert_with_op(\"setA.union(setA)\", |v, _| {\n            v.display().to_string() == \"Set { 1, 4, 9 }\"\n        }),\n    ]);\n}\n\n/// `Set.prototype.intersection` — happy-path: elements present in both sets.\n#[test]\nfn intersection_common_elements() {\n    run_test_actions([\n        TestAction::run(indoc! {r#\"\n            let setA = new Set([1, 2, 3, 4]);\n            let setB = new Set([3, 4, 5, 6]);\n        \"#}),\n        // Both directions must yield the same logical result (order follows `this`).\n        TestAction::assert_with_op(\"setA.intersection(setB)\", |v, _| {\n            v.display().to_string() == \"Set { 3, 4 }\"\n        }),\n        TestAction::assert_with_op(\"setB.intersection(setA)\", |v, _| {\n            v.display().to_string() == \"Set { 3, 4 }\"\n        }),\n        // Intersecting a set with itself yields an identical set.\n        TestAction::assert_with_op(\"setA.intersection(setA)\", |v, _| {\n            v.display().to_string() == \"Set { 1, 2, 3, 4 }\"\n        }),\n    ]);\n}\n\n/// `Set.prototype.intersection` — disjoint sets produce an empty result.\n#[test]\nfn intersection_no_common_elements() {\n    run_test_actions([\n        TestAction::run(indoc! {r#\"\n            let setA = new Set([1, 2, 3]);\n            let setB = new Set([4, 5, 6]);\n            let setEmpty = new Set([]);\n        \"#}),\n        // Two sets with nothing in common.\n        TestAction::assert_with_op(\"setA.intersection(setB)\", |v, _| {\n            v.display().to_string() == \"Set(0)\"\n        }),\n        // Intersection with an empty set is always empty.\n        TestAction::assert_with_op(\"setA.intersection(setEmpty)\", |v, _| {\n            v.display().to_string() == \"Set(0)\"\n        }),\n        TestAction::assert_with_op(\"setEmpty.intersection(setA)\", |v, _| {\n            v.display().to_string() == \"Set(0)\"\n        }),\n    ]);\n}\n\n/// Set.prototype.intersection — this is not a Set → \\\n#[test]\nfn intersection_this_not_a_set() {\n    run_test_actions([TestAction::assert_native_error(\n        \"Set.prototype.intersection.call({}, new Set([1, 2, 3]))\",\n        JsNativeErrorKind::Type,\n        \"method `Set.prototype.intersection` called on incompatible receiver\",\n    )]);\n}\n\n/// Set.prototype.intersection — other is not a valid Set-like object → \\\n#[test]\nfn intersection_other_not_set_like() {\n    run_test_actions([\n        // Primitive value — GetSetRecord rejects non-objects.\n        TestAction::assert_native_error(\n            \"new Set([1, 2]).intersection(42)\",\n            JsNativeErrorKind::Type,\n            \"Set operation called with non-object argument\",\n        ),\n        // Plain object whose `size` is `undefined` (NaN after ToNumber).\n        TestAction::assert_native_error(\n            \"new Set([1, 2]).intersection({})\",\n            JsNativeErrorKind::Type,\n            \"size is undefined\",\n        ),\n        // `has` is present but not callable.\n        TestAction::assert_native_error(\n            \"new Set([1, 2]).intersection({ size: 0, has: 1, keys: () => [][Symbol.iterator]() })\",\n            JsNativeErrorKind::Type,\n            \"Set-like object must have a callable 'has' method\",\n        ),\n        // `keys` is present but not callable.\n        TestAction::assert_native_error(\n            \"new Set([1, 2]).intersection({ size: 0, has: () => false, keys: 1 })\",\n            JsNativeErrorKind::Type,\n            \"Set-like object must have a callable 'keys' method\",\n        ),\n    ]);\n}\n"
  },
  {
    "path": "core/engine/src/builtins/string/mod.rs",
    "content": "//! This module implements the global `String` object.\n//!\n//! The `String` global object is a constructor for strings or a sequence of characters.\n//!\n//! More information:\n//!  - [ECMAScript reference][spec]\n//!  - [MDN documentation][mdn]\n//!\n//! [spec]: https://tc39.es/ecma262/#sec-string-object\n//! [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String\n\nuse crate::{\n    Context, JsArgs, JsExpect, JsResult, JsString, JsValue,\n    builtins::{Array, BuiltInObject, Number, RegExp},\n    context::intrinsics::{Intrinsics, StandardConstructor, StandardConstructors},\n    error::JsNativeError,\n    js_string,\n    object::{JsObject, internal_methods::get_prototype_from_constructor},\n    property::{Attribute, PropertyDescriptor},\n    realm::Realm,\n    string::{CodePoint, StaticJsStrings},\n    symbol::JsSymbol,\n    value::IntegerOrInfinity,\n};\nuse boa_macros::utf16;\n\nuse cow_utils::CowUtils;\nuse icu_normalizer::{ComposingNormalizer, DecomposingNormalizer};\nuse std::{\n    borrow::Cow,\n    cmp::{max, min},\n};\n\nuse super::{BuiltInBuilder, BuiltInConstructor, IntrinsicObject};\n\nmod string_iterator;\npub(crate) use string_iterator::StringIterator;\n\n#[cfg(feature = \"annex-b\")]\npub use crate::{JsStr, js_str};\n\n/// The set of normalizers required for the `String.prototype.normalize` function.\n#[derive(Debug)]\npub(crate) struct StringNormalizers {\n    pub(crate) nfc: ComposingNormalizer,\n    pub(crate) nfkc: ComposingNormalizer,\n    pub(crate) nfd: DecomposingNormalizer,\n    pub(crate) nfkd: DecomposingNormalizer,\n}\n\n#[cfg(test)]\nmod tests;\n\n#[derive(Clone, Copy, Eq, PartialEq)]\npub(crate) enum Placement {\n    Start,\n    End,\n}\n\n/// JavaScript `String` implementation.\n#[derive(Debug, Clone, Copy)]\npub(crate) struct String;\n\nimpl IntrinsicObject for String {\n    fn init(realm: &Realm) {\n        let trim_start = BuiltInBuilder::callable(realm, Self::trim_start)\n            .length(0)\n            .name(js_string!(\"trimStart\"))\n            .build();\n\n        let trim_end = BuiltInBuilder::callable(realm, Self::trim_end)\n            .length(0)\n            .name(js_string!(\"trimEnd\"))\n            .build();\n\n        #[cfg(feature = \"annex-b\")]\n        let trim_left = trim_start.clone();\n\n        #[cfg(feature = \"annex-b\")]\n        let trim_right = trim_end.clone();\n\n        let attribute = Attribute::READONLY | Attribute::NON_ENUMERABLE | Attribute::PERMANENT;\n        let builder = BuiltInBuilder::from_standard_constructor::<Self>(realm)\n            .property(js_string!(\"length\"), 0, attribute)\n            .property(\n                js_string!(\"trimStart\"),\n                trim_start,\n                Attribute::WRITABLE | Attribute::NON_ENUMERABLE | Attribute::CONFIGURABLE,\n            )\n            .property(\n                js_string!(\"trimEnd\"),\n                trim_end,\n                Attribute::WRITABLE | Attribute::NON_ENUMERABLE | Attribute::CONFIGURABLE,\n            )\n            .static_method(Self::raw, js_string!(\"raw\"), 1)\n            .static_method(Self::from_char_code, js_string!(\"fromCharCode\"), 1)\n            .static_method(Self::from_code_point, js_string!(\"fromCodePoint\"), 1)\n            .method(Self::char_at, js_string!(\"charAt\"), 1)\n            .method(Self::char_code_at, js_string!(\"charCodeAt\"), 1)\n            .method(Self::code_point_at, js_string!(\"codePointAt\"), 1)\n            .method(Self::to_string, js_string!(\"toString\"), 0)\n            .method(Self::concat, js_string!(\"concat\"), 1)\n            .method(Self::repeat, js_string!(\"repeat\"), 1)\n            .method(Self::slice, js_string!(\"slice\"), 2)\n            .method(Self::starts_with, js_string!(\"startsWith\"), 1)\n            .method(Self::ends_with, js_string!(\"endsWith\"), 1)\n            .method(Self::includes, js_string!(\"includes\"), 1)\n            .method(Self::index_of, js_string!(\"indexOf\"), 1)\n            .method(Self::is_well_formed, js_string!(\"isWellFormed\"), 0)\n            .method(Self::last_index_of, js_string!(\"lastIndexOf\"), 1)\n            .method(Self::locale_compare, js_string!(\"localeCompare\"), 1)\n            .method(Self::r#match, js_string!(\"match\"), 1)\n            .method(Self::normalize, js_string!(\"normalize\"), 0)\n            .method(Self::pad_end, js_string!(\"padEnd\"), 1)\n            .method(Self::pad_start, js_string!(\"padStart\"), 1)\n            .method(Self::trim, js_string!(\"trim\"), 0)\n            .method(Self::to_case::<false>, js_string!(\"toLowerCase\"), 0)\n            .method(Self::to_case::<true>, js_string!(\"toUpperCase\"), 0)\n            .method(Self::to_well_formed, js_string!(\"toWellFormed\"), 0)\n            .method(\n                Self::to_locale_case::<false>,\n                js_string!(\"toLocaleLowerCase\"),\n                0,\n            )\n            .method(\n                Self::to_locale_case::<true>,\n                js_string!(\"toLocaleUpperCase\"),\n                0,\n            )\n            .method(Self::substring, js_string!(\"substring\"), 2)\n            .method(Self::split, js_string!(\"split\"), 2)\n            .method(Self::value_of, js_string!(\"valueOf\"), 0)\n            .method(Self::match_all, js_string!(\"matchAll\"), 1)\n            .method(Self::replace, js_string!(\"replace\"), 2)\n            .method(Self::replace_all, js_string!(\"replaceAll\"), 2)\n            .method(Self::iterator, JsSymbol::iterator(), 0)\n            .method(Self::search, js_string!(\"search\"), 1)\n            .method(Self::at, js_string!(\"at\"), 1);\n\n        #[cfg(feature = \"annex-b\")]\n        let builder = {\n            builder\n                .property(\n                    js_string!(\"trimLeft\"),\n                    trim_left,\n                    Attribute::WRITABLE | Attribute::NON_ENUMERABLE | Attribute::CONFIGURABLE,\n                )\n                .property(\n                    js_string!(\"trimRight\"),\n                    trim_right,\n                    Attribute::WRITABLE | Attribute::NON_ENUMERABLE | Attribute::CONFIGURABLE,\n                )\n                .method(Self::substr, js_string!(\"substr\"), 2)\n                .method(Self::anchor, js_string!(\"anchor\"), 1)\n                .method(Self::big, js_string!(\"big\"), 0)\n                .method(Self::blink, js_string!(\"blink\"), 0)\n                .method(Self::bold, js_string!(\"bold\"), 0)\n                .method(Self::fixed, js_string!(\"fixed\"), 0)\n                .method(Self::fontcolor, js_string!(\"fontcolor\"), 1)\n                .method(Self::fontsize, js_string!(\"fontsize\"), 1)\n                .method(Self::italics, js_string!(\"italics\"), 0)\n                .method(Self::link, js_string!(\"link\"), 1)\n                .method(Self::small, js_string!(\"small\"), 0)\n                .method(Self::strike, js_string!(\"strike\"), 0)\n                .method(Self::sub, js_string!(\"sub\"), 0)\n                .method(Self::sup, js_string!(\"sup\"), 0)\n        };\n\n        builder.build();\n    }\n\n    fn get(intrinsics: &Intrinsics) -> JsObject {\n        Self::STANDARD_CONSTRUCTOR(intrinsics.constructors()).constructor()\n    }\n}\n\nimpl BuiltInObject for String {\n    const NAME: JsString = StaticJsStrings::STRING;\n}\n\nimpl BuiltInConstructor for String {\n    const CONSTRUCTOR_ARGUMENTS: usize = 1;\n    const PROTOTYPE_STORAGE_SLOTS: usize = 52;\n    const CONSTRUCTOR_STORAGE_SLOTS: usize = 3;\n\n    const STANDARD_CONSTRUCTOR: fn(&StandardConstructors) -> &StandardConstructor =\n        StandardConstructors::string;\n\n    /// Constructor `String( value )`\n    ///\n    /// <https://tc39.es/ecma262/#sec-string-constructor-string-value>\n    fn constructor(\n        new_target: &JsValue,\n        args: &[JsValue],\n        context: &mut Context,\n    ) -> JsResult<JsValue> {\n        // This value is used by console.log and other routines to match Object type\n        // to its Javascript Identifier (global constructor method name)\n        let string = match args.first() {\n            // 2. Else,\n            // a. If NewTarget is undefined and Type(value) is Symbol, return SymbolDescriptiveString(value).\n            Some(value) if new_target.is_undefined() && value.is_symbol() => {\n                return Ok(value\n                    .as_symbol()\n                    .expect(\"Already checked for a symbol\")\n                    .descriptive_string()\n                    .into());\n            }\n            // b. Let s be ? ToString(value).\n            Some(value) => value.to_string(context)?,\n            // 1. If value is not present, let s be the empty String.\n            None => js_string!(),\n        };\n\n        // 3. If NewTarget is undefined, return s.\n        if new_target.is_undefined() {\n            return Ok(string.into());\n        }\n\n        let prototype =\n            get_prototype_from_constructor(new_target, StandardConstructors::string, context)?;\n        // 4. Return ! StringCreate(s, ? GetPrototypeFromConstructor(NewTarget, \"%String.prototype%\")).\n        Ok(Self::string_create(string, prototype, context).into())\n    }\n}\n\nimpl String {\n    /// JavaScript strings must be between `0` and less than positive `Infinity` and cannot be a negative number.\n    /// The range of allowed values can be described like this: `[0, +∞)`.\n    ///\n    /// The resulting string can also not be larger than the maximum string size,\n    /// which can differ in JavaScript engines. In Boa it is `2^32 - 1`\n    pub(crate) const MAX_STRING_LENGTH: usize = u32::MAX as usize;\n\n    /// Abstract function `StringCreate( value, prototype )`.\n    ///\n    /// Call this function if you want to create a `String` exotic object.\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-stringcreate\n    fn string_create(value: JsString, prototype: JsObject, context: &mut Context) -> JsObject {\n        // 7. Let length be the number of code unit elements in value.\n        let len = value.len();\n\n        // 1. Let S be ! MakeBasicObject(« [[Prototype]], [[Extensible]], [[StringData]] »).\n        // 2. Set S.[[Prototype]] to prototype.\n        // 3. Set S.[[StringData]] to value.\n        // 4. Set S.[[GetOwnProperty]] as specified in 10.4.3.1.\n        // 5. Set S.[[DefineOwnProperty]] as specified in 10.4.3.2.\n        // 6. Set S.[[OwnPropertyKeys]] as specified in 10.4.3.3.\n        let s =\n            JsObject::from_proto_and_data_with_shared_shape(context.root_shape(), prototype, value)\n                .upcast();\n\n        // 8. Perform ! DefinePropertyOrThrow(S, \"length\", PropertyDescriptor { [[Value]]: 𝔽(length),\n        // [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: false }).\n        s.define_property_or_throw(\n            StaticJsStrings::LENGTH,\n            PropertyDescriptor::builder()\n                .value(len)\n                .writable(false)\n                .enumerable(false)\n                .configurable(false),\n            context,\n        )\n        .expect(\"length definition for a new string must not fail\");\n\n        // 9. Return S.\n        s\n    }\n\n    /// Abstract operation `thisStringValue( value )`\n    ///\n    /// More information:\n    /// - [ECMAScript reference][spec]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#thisstringvalue\n    fn this_string_value(this: &JsValue) -> JsResult<JsString> {\n        // 1. If Type(value) is String, return value.\n        this.as_string()\n            // 2. If Type(value) is Object and value has a [[StringData]] internal slot, then\n            //     a. Let s be value.[[StringData]].\n            //     b. Assert: Type(s) is String.\n            //     c. Return s.\n            .or_else(|| {\n                this.as_object()\n                    .and_then(|obj| obj.downcast_ref::<JsString>().as_deref().cloned())\n            })\n            // 3. Throw a TypeError exception.\n            .ok_or_else(|| {\n                JsNativeError::typ()\n                    .with_message(\"'this' is not a string\")\n                    .into()\n            })\n    }\n\n    /// `String.fromCodePoint(num1[, ...[, numN]])`\n    ///\n    /// The static `String.fromCodePoint()` method returns a string created by using the specified sequence of code points.\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///  - [MDN documentation][mdn]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-string.fromcodepoint\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/fromCodePoint\n    pub(crate) fn from_code_point(\n        _: &JsValue,\n        args: &[JsValue],\n        context: &mut Context,\n    ) -> JsResult<JsValue> {\n        // 1. Let result be the empty String.\n        let mut result = Vec::with_capacity(args.len());\n\n        let mut buf = [0; 2];\n\n        // 2. For each element next of codePoints, do\n        for arg in args {\n            // a. Let nextCP be ? ToNumber(next).\n            let nextcp = arg.to_number(context)?;\n\n            // b. If ! IsIntegralNumber(nextCP) is false, throw a RangeError exception.\n            if !Number::is_float_integer(nextcp) {\n                return Err(JsNativeError::range()\n                    .with_message(format!(\"codepoint `{nextcp}` is not an integer\"))\n                    .into());\n            }\n\n            // c. If ℝ(nextCP) < 0 or ℝ(nextCP) > 0x10FFFF, throw a RangeError exception.\n            if nextcp < 0.0 || nextcp > f64::from(0x0010_FFFF) {\n                return Err(JsNativeError::range()\n                    .with_message(format!(\"codepoint `{nextcp}` outside of Unicode range\"))\n                    .into());\n            }\n\n            // SAFETY:\n            // - `nextcp` is not NaN (by the call to `is_float_integer`).\n            // - `nextcp` is not infinite (by the call to `is_float_integer`).\n            // - `nextcp` is in the u32 range (by the check above).\n            let nextcp = unsafe { nextcp.to_int_unchecked::<u32>() };\n\n            // d. Set result to the string-concatenation of result and ! UTF16EncodeCodePoint(ℝ(nextCP)).\n            result.extend_from_slice(match u16::try_from(nextcp) {\n                Ok(ref cp) => std::slice::from_ref(cp),\n                Err(_) => char::from_u32(nextcp)\n                    .expect(\"u32 is in range and cannot be a surrogate by the conversion above\")\n                    .encode_utf16(&mut buf),\n            });\n        }\n\n        // 3. Assert: If codePoints is empty, then result is the empty String.\n        // 4. Return result.\n        Ok(js_string!(&result[..]).into())\n    }\n\n    /// `String.raw( template, ...substitutions )`\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///  - [MDN documentation][mdn]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-string.raw\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/raw\n    pub(crate) fn raw(_: &JsValue, args: &[JsValue], context: &mut Context) -> JsResult<JsValue> {\n        let substitutions = args.get(1..).unwrap_or_default();\n\n        // 1. Let numberOfSubstitutions be the number of elements in substitutions.\n        let number_of_substitutions = substitutions.len() as u64;\n\n        // 2. Let cooked be ? ToObject(template).\n        let cooked = args.get_or_undefined(0).to_object(context)?;\n\n        // 3. Let raw be ? ToObject(? Get(cooked, \"raw\")).\n        let raw = cooked.get(js_string!(\"raw\"), context)?.to_object(context)?;\n\n        // 4. Let literalSegments be ? LengthOfArrayLike(raw).\n        let literal_segments = raw.length_of_array_like(context)?;\n\n        // 5. If literalSegments ≤ 0, return the empty String.\n        // This is not <= because a `usize` is always positive.\n        if literal_segments == 0 {\n            return Ok(js_string!().into());\n        }\n\n        // 6. Let stringElements be a new empty List.\n        let mut string_elements = vec![];\n\n        // 7. Let nextIndex be 0.\n        let mut next_index = 0;\n        // 8. Repeat,\n        loop {\n            // a. Let nextKey be ! ToString(𝔽(nextIndex)).\n            let next_key = next_index;\n\n            // b. Let nextSeg be ? ToString(? Get(raw, nextKey)).\n            let next_seg = raw.get(next_key, context)?.to_string(context)?;\n\n            // c. Append the code unit elements of nextSeg to the end of stringElements.\n            string_elements.extend(next_seg.iter());\n\n            // d. If nextIndex + 1 = literalSegments, then\n            if next_index + 1 == literal_segments {\n                // i. Return the String value whose code units are the elements in the List stringElements.\n                //    If stringElements has no elements, the empty String is returned.\n                return Ok(js_string!(&string_elements[..]).into());\n            }\n\n            // e. If nextIndex < numberOfSubstitutions, let next be substitutions[nextIndex].\n            let next = if next_index < number_of_substitutions {\n                substitutions.get_or_undefined(next_index as usize).clone()\n\n            // f. Else, let next be the empty String.\n            } else {\n                js_string!().into()\n            };\n\n            // g. Let nextSub be ? ToString(next).\n            let next_sub = next.to_string(context)?;\n\n            // h. Append the code unit elements of nextSub to the end of stringElements.\n            string_elements.extend(next_sub.iter());\n\n            // i. Set nextIndex to nextIndex + 1.\n            next_index += 1;\n        }\n    }\n\n    /// `String.fromCharCode(...codeUnits)`\n    ///\n    /// Construct a `String` from one or more code points (as numbers).\n    /// More information:\n    /// - [ECMAScript reference][spec]\n    ///\n    /// [spec]: https://tc39.es/ecma262/multipage/text-processing.html#sec-string.fromcharcode\n    pub(crate) fn from_char_code(\n        _: &JsValue,\n        args: &[JsValue],\n        context: &mut Context,\n    ) -> JsResult<JsValue> {\n        // 1. Let result be the empty String.\n        let mut result = Vec::with_capacity(args.len());\n\n        // 2. For each element next of codeUnits, do\n        for next in args {\n            // a. Let nextCU be the code unit whose numeric value is ℝ(? ToUint16(next)).\n            let next_cu = next.to_uint16(context)?;\n\n            // b. Set result to the string-concatenation of result and nextCU.\n            result.push(next_cu);\n        }\n\n        // 3. Return result.\n        Ok(js_string!(&result[..]).into())\n    }\n\n    /// `String.prototype.toString ( )`\n    ///\n    /// More information:\n    /// - [ECMAScript reference][spec]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-string.prototype.tostring\n    #[allow(clippy::wrong_self_convention)]\n    pub(crate) fn to_string(this: &JsValue, _: &[JsValue], _: &mut Context) -> JsResult<JsValue> {\n        // 1. Return ? thisStringValue(this value).\n        Ok(Self::this_string_value(this)?.into())\n    }\n\n    /// `String.prototype.charAt( index )`\n    ///\n    /// The `String` object's `charAt()` method returns a new string consisting of the single UTF-16 code unit located at the specified offset into the string.\n    ///\n    /// Characters in a string are indexed from left to right. The index of the first character is `0`,\n    /// and the index of the last character—in a string called `stringName`—is `stringName.length - 1`.\n    /// If the `index` you supply is out of this range, JavaScript returns an empty string.\n    ///\n    /// If no index is provided to `charAt()`, the default is `0`.\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///  - [MDN documentation][mdn]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-string.prototype.charat\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/charAt\n    pub(crate) fn char_at(\n        this: &JsValue,\n        args: &[JsValue],\n        context: &mut Context,\n    ) -> JsResult<JsValue> {\n        // 1. Let O be ? RequireObjectCoercible(this value).\n        let this = this.require_object_coercible()?;\n\n        // 2. Let S be ? ToString(O).\n        let string = this.to_string(context)?;\n\n        // 3. Let position be ? ToIntegerOrInfinity(pos).\n        let position = args.get_or_undefined(0).to_integer_or_infinity(context)?;\n\n        match position {\n            // 4. Let size be the length of S.\n            // 6. Return the substring of S from position to position + 1.\n            IntegerOrInfinity::Integer(i) if i >= 0 && i < string.len() as i64 => {\n                let i = i as usize;\n                Ok(js_string!(string.get_expect(i..=i)).into())\n            }\n            // 5. If position < 0 or position ≥ size, return the empty String.\n            _ => Ok(js_string!().into()),\n        }\n    }\n\n    /// `String.prototype.at ( index )`\n    ///\n    /// This String object's `at()` method returns a String consisting of the single UTF-16 code unit located at the specified position.\n    /// Returns undefined if the given index cannot be found.\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///  - [MDN documentation][mdn]\n    ///\n    /// [spec]: https://tc39.es/proposal-relative-indexing-method/#sec-string.prototype.at\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/at\n    pub(crate) fn at(this: &JsValue, args: &[JsValue], context: &mut Context) -> JsResult<JsValue> {\n        // 1. Let O be ? RequireObjectCoercible(this value).\n        let this = this.require_object_coercible()?;\n\n        // 2. Let S be ? ToString(O).\n        let s = this.to_string(context)?;\n\n        // 3. Let len be the length of S.\n        let len = s.len() as i64;\n\n        // 4. Let relativeIndex be ? ToIntegerOrInfinity(index).\n        let relative_index = args.get_or_undefined(0).to_integer_or_infinity(context)?;\n        let k = match relative_index {\n            // 5. If relativeIndex ≥ 0, then\n            // a. Let k be relativeIndex.\n            IntegerOrInfinity::Integer(i) if i >= 0 && i < len => i as usize,\n            // 6. Else,\n            // a. Let k be len + relativeIndex.\n            IntegerOrInfinity::Integer(i) if i < 0 && (-i) <= len => (len + i) as usize,\n            // 7. If k < 0 or k ≥ len, return undefined.\n            _ => return Ok(JsValue::undefined()),\n        };\n\n        // 8. Return the substring of S from k to k + 1.\n        Ok(js_string!(s.get_expect(k..=k)).into())\n    }\n\n    /// `String.prototype.codePointAt( index )`\n    ///\n    /// The `codePointAt()` method returns an integer between `0` to `1114111` (`0x10FFFF`) representing the UTF-16 code unit at the given index.\n    ///\n    /// If no UTF-16 surrogate pair begins at the index, the code point at the index is returned.\n    ///\n    /// `codePointAt()` returns `undefined` if the given index is less than `0`, or if it is equal to or greater than the `length` of the string.\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///  - [MDN documentation][mdn]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-string.prototype.codepointat\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/codePointAt\n    pub(crate) fn code_point_at(\n        this: &JsValue,\n        args: &[JsValue],\n        context: &mut Context,\n    ) -> JsResult<JsValue> {\n        // 1. Let O be ? RequireObjectCoercible(this value).\n        let this = this.require_object_coercible()?;\n\n        // 2. Let S be ? ToString(O).\n        let string = this.to_string(context)?;\n\n        // 3. Let position be ? ToIntegerOrInfinity(pos).\n        let position = args.get_or_undefined(0).to_integer_or_infinity(context)?;\n\n        match position {\n            // 4. Let size be the length of S.\n            IntegerOrInfinity::Integer(i) if i >= 0 && i < string.len() as i64 => {\n                // 6. Let cp be ! CodePointAt(S, position).\n                // 7. Return 𝔽(cp.[[CodePoint]]).\n                Ok(string\n                    .code_point_at(usize::try_from(i).expect(\"already checked that i >= 0\"))\n                    .as_u32()\n                    .into())\n            }\n            // 5. If position < 0 or position ≥ size, return undefined.\n            _ => Ok(JsValue::undefined()),\n        }\n    }\n\n    /// `String.prototype.charCodeAt( index )`\n    ///\n    /// The `charCodeAt()` method returns an integer between `0` and `65535` representing the UTF-16 code unit at the given index.\n    ///\n    /// Unicode code points range from `0` to `1114111` (`0x10FFFF`). The first 128 Unicode code points are a direct match of the ASCII character encoding.\n    ///\n    /// `charCodeAt()` returns `NaN` if the given index is less than `0`, or if it is equal to or greater than the `length` of the string.\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///  - [MDN documentation][mdn]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-string.prototype.charcodeat\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/charCodeAt\n    pub(crate) fn char_code_at(\n        this: &JsValue,\n        args: &[JsValue],\n        context: &mut Context,\n    ) -> JsResult<JsValue> {\n        // 1. Let O be ? RequireObjectCoercible(this value).\n        let this = this.require_object_coercible()?;\n\n        // 2. Let S be ? ToString(O).\n        let string = this.to_string(context)?;\n\n        // 3. Let position be ? ToIntegerOrInfinity(pos).\n        let position = args.get_or_undefined(0).to_integer_or_infinity(context)?;\n\n        match position {\n            // 4. Let size be the length of S.\n            IntegerOrInfinity::Integer(i) if i >= 0 && i < string.len() as i64 => {\n                // 6. Return the Number value for the numeric value of the code unit at index position within the String S.\n                // SAFETY: already validated the index.\n                Ok(unsafe { string.code_unit_at(i as usize).unwrap_unchecked() }.into())\n            }\n            // 5. If position < 0 or position ≥ size, return NaN.\n            _ => Ok(JsValue::nan()),\n        }\n    }\n\n    /// `String.prototype.concat( str1[, ...strN] )`\n    ///\n    /// The `concat()` method concatenates the string arguments to the calling string and returns a new string.\n    ///\n    /// Changes to the original string or the returned string don't affect the other.\n    ///\n    /// If the arguments are not of the type string, they are converted to string values before concatenating.\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///  - [MDN documentation][mdn]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-string.prototype.concat\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/concat\n    pub(crate) fn concat(\n        this: &JsValue,\n        args: &[JsValue],\n        context: &mut Context,\n    ) -> JsResult<JsValue> {\n        // 1. Let O be ? RequireObjectCoercible(this value).\n        let this = this.require_object_coercible()?;\n\n        // 2. Let S be ? ToString(O).\n        let mut string = this.to_string(context)?;\n\n        // 3. Let R be S.\n        // 4. For each element next of args, do\n        for arg in args {\n            // a. Let nextString be ? ToString(next).\n            // b. Set R to the string-concatenation of R and nextString.\n            string = js_string!(&string, &arg.to_string(context)?);\n        }\n\n        // 5. Return R.\n        Ok(JsValue::new(string))\n    }\n\n    /// `String.prototype.repeat( count )`\n    ///\n    /// The `repeat()` method constructs and returns a new string which contains the specified number of\n    /// copies of the string on which it was called, concatenated together.\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///  - [MDN documentation][mdn]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-string.prototype.repeat\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/repeat\n    pub(crate) fn repeat(\n        this: &JsValue,\n        args: &[JsValue],\n        context: &mut Context,\n    ) -> JsResult<JsValue> {\n        // 1. Let O be ? RequireObjectCoercible(this value).\n        let this = this.require_object_coercible()?;\n\n        // 2. Let S be ? ToString(O).\n        let string = this.to_string(context)?;\n\n        // 3. Let n be ? ToIntegerOrInfinity(count).\n        let n = args.get_or_undefined(0).to_integer_or_infinity(context)?;\n\n        // 4. If n < 0 or n is +∞, throw a RangeError exception.\n        let n = match n {\n            IntegerOrInfinity::Integer(i) if i < 0 => {\n                return Err(JsNativeError::range()\n                    .with_message(\"String.prototype.repeat: count must be non-negative\")\n                    .into());\n            }\n            IntegerOrInfinity::PositiveInfinity => {\n                return Err(JsNativeError::range()\n                    .with_message(\"String.prototype.repeat: count must be less than infinity\")\n                    .into());\n            }\n            IntegerOrInfinity::NegativeInfinity => {\n                return Err(JsNativeError::range()\n                    .with_message(\"String.prototype.repeat: count must be non-negative\")\n                    .into());\n            }\n            IntegerOrInfinity::Integer(i) => i,\n        };\n\n        let len = string.len();\n\n        if n == 0 || string.is_empty() {\n            return Ok(js_string!().into());\n        }\n\n        if u64::try_from(n)\n            .ok()\n            .and_then(|n| n.checked_mul(len as u64))\n            .is_none_or(|total_len| total_len > (Self::MAX_STRING_LENGTH as u64))\n        {\n            return Err(JsNativeError::range()\n                .with_message(\n                    \"repeat count must be a positive finite number \\\n                        that doesn't overflow the maximum string length (2^32 - 1)\",\n                )\n                .into());\n        }\n\n        let n = n as usize;\n\n        // Charge each repetition against the VM loop-iteration limit.\n        let mut result = Vec::with_capacity(n);\n        for _ in 0..n {\n            crate::vm::opcode::IncrementLoopIteration::operation((), context)?;\n            result.push(string.as_str());\n        }\n\n        // 6. Return the String value that is made from n copies of S appended together.\n        Ok(JsString::concat_array(&result).into())\n    }\n\n    /// `String.prototype.slice( beginIndex [, endIndex] )`\n    ///\n    /// The `slice()` method extracts a section of a string and returns it as a new string, without modifying the original string.\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///  - [MDN documentation][mdn]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-string.prototype.slice\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/slice\n    pub(crate) fn slice(\n        this: &JsValue,\n        args: &[JsValue],\n        context: &mut Context,\n    ) -> JsResult<JsValue> {\n        // 1. Let O be ? RequireObjectCoercible(this value).\n        let this = this.require_object_coercible()?;\n\n        // 2. Let S be ? ToString(O).\n        let string = this.to_string(context)?;\n\n        // 3. Let len be the length of S.\n        let len = string.len() as i64;\n\n        // 4. Let intStart be ? ToIntegerOrInfinity(start).\n        let from = match args.get_or_undefined(0).to_integer_or_infinity(context)? {\n            // 6. Else if intStart < 0, let from be max(len + intStart, 0).\n            IntegerOrInfinity::Integer(i) if i < 0 => max(len + i, 0),\n\n            // 7. Else, let from be min(intStart, len).\n            IntegerOrInfinity::Integer(i) => min(i, len),\n            IntegerOrInfinity::PositiveInfinity => len,\n\n            // 5. If intStart is -∞, let from be 0.\n            IntegerOrInfinity::NegativeInfinity => 0,\n        } as usize;\n\n        // 8. If end is undefined, let intEnd be len; else let intEnd be ? ToIntegerOrInfinity(end).\n        let to = match args\n            .get(1)\n            .filter(|end| !end.is_undefined())\n            .map(|end| end.to_integer_or_infinity(context))\n            .transpose()?\n            .unwrap_or(IntegerOrInfinity::Integer(len))\n        {\n            // 10. Else if intEnd < 0, let to be max(len + intEnd, 0).\n            IntegerOrInfinity::Integer(i) if i < 0 => max(len + i, 0),\n\n            // 11. Else, let to be min(intEnd, len).\n            IntegerOrInfinity::Integer(i) => min(i, len),\n            IntegerOrInfinity::PositiveInfinity => len,\n\n            // 9. If intEnd is -∞, let to be 0.\n            IntegerOrInfinity::NegativeInfinity => 0,\n        } as usize;\n\n        // 12. If from ≥ to, return the empty String.\n        if from >= to {\n            Ok(js_string!().into())\n        } else {\n            // 13. Return the substring of S from from to to.\n            // SAFETY: We already checked that `from` and `to` are within bounds.\n            Ok(unsafe { JsString::slice_unchecked(&string, from, to).into() })\n        }\n    }\n\n    /// `String.prototype.startWith( searchString[, position] )`\n    ///\n    /// The `startsWith()` method determines whether a string begins with the characters of a specified string, returning `true` or `false` as appropriate.\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///  - [MDN documentation][mdn]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-string.prototype.startswith\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/startsWith\n    pub(crate) fn starts_with(\n        this: &JsValue,\n        args: &[JsValue],\n        context: &mut Context,\n    ) -> JsResult<JsValue> {\n        // 1. Let O be ? RequireObjectCoercible(this value).\n        let this = this.require_object_coercible()?;\n\n        // 2. Let S be ? ToString(O).\n        let string = this.to_string(context)?;\n\n        let search_string = args.get_or_undefined(0);\n\n        // 3. Let isRegExp be ? IsRegExp(searchString).\n        // 4. If isRegExp is true, throw a TypeError exception.\n        if RegExp::is_reg_exp(search_string, context)?.is_some() {\n            return Err(JsNativeError::typ().with_message(\n                \"First argument to String.prototype.startsWith must not be a regular expression\",\n            ).into());\n        }\n\n        // 5. Let searchStr be ? ToString(searchString).\n        let search_string = search_string.to_string(context)?;\n\n        // 6. Let len be the length of S.\n        let len = string.len() as i64;\n\n        // 7. If position is undefined, let pos be 0; else let pos be ? ToIntegerOrInfinity(position).\n        let pos = args\n            .get_or_undefined(1)\n            .map_or(Ok(IntegerOrInfinity::Integer(0)), |pos| {\n                pos.to_integer_or_infinity(context)\n            })?;\n\n        // 8. Let start be the result of clamping pos between 0 and len.\n        let start = pos.clamp_finite(0, len) as usize;\n\n        // 9. Let searchLength be the length of searchStr.\n        let search_length = search_string.len();\n\n        // 10. If searchLength = 0, return true.\n        if search_length == 0 {\n            return Ok(JsValue::new(true));\n        }\n\n        // 11. Let end be start + searchLength.\n        let end = start + search_length;\n\n        // 12. If end > len, return false.\n        if end > len as usize {\n            Ok(JsValue::new(false))\n        } else {\n            // 13. Let substring be the substring of S from start to end.\n            // 14. Return ! SameValueNonNumeric(substring, searchStr).\n            // `SameValueNonNumeric` forwards to `==`, so directly check\n            // equality to avoid converting to `JsValue`\n            Ok(JsValue::new(search_string == string.get_expect(start..end)))\n        }\n    }\n\n    /// `String.prototype.endsWith( searchString[, length] )`\n    ///\n    /// The `endsWith()` method determines whether a string ends with the characters of a specified string, returning `true` or `false` as appropriate.\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///  - [MDN documentation][mdn]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-string.prototype.endswith\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/endsWith\n    pub(crate) fn ends_with(\n        this: &JsValue,\n        args: &[JsValue],\n        context: &mut Context,\n    ) -> JsResult<JsValue> {\n        // 1. Let O be ? RequireObjectCoercible(this value).\n        let this = this.require_object_coercible()?;\n\n        // 2. Let S be ? ToString(O).\n        let string = this.to_string(context)?;\n\n        let search_str = match args.get_or_undefined(0) {\n            // 3. Let isRegExp be ? IsRegExp(searchString).\n            // 4. If isRegExp is true, throw a TypeError exception.\n            search_string if RegExp::is_reg_exp(search_string, context)?.is_some() => {\n                return Err(JsNativeError::typ().with_message(\n                    \"First argument to String.prototype.endsWith must not be a regular expression\",\n                ).into());\n            }\n            // 5. Let searchStr be ? ToString(searchString).\n            search_string => search_string.to_string(context)?,\n        };\n\n        // 6. Let len be the length of S.\n        let len = string.len() as i64;\n\n        // 7. If endPosition is undefined, let pos be len; else let pos be ? ToIntegerOrInfinity(endPosition).\n        let end = match args.get_or_undefined(1) {\n            end_position if end_position.is_undefined() => IntegerOrInfinity::Integer(len),\n            end_position => end_position.to_integer_or_infinity(context)?,\n        };\n\n        // 8. Let end be the result of clamping pos between 0 and len.\n        let end = end.clamp_finite(0, len) as usize;\n\n        // 9. Let searchLength be the length of searchStr.\n        let search_length = search_str.len();\n\n        // 10. If searchLength = 0, return true.\n        if search_length == 0 {\n            return Ok(true.into());\n        }\n\n        // 11. Let start be end - searchLength.\n        if let Some(start) = end.checked_sub(search_length) {\n            // 13. Let substring be the substring of S from start to end.\n            // 14. Return ! SameValueNonNumeric(substring, searchStr).\n            // `SameValueNonNumeric` forwards to `==`, so directly check\n            // equality to avoid converting to `JsValue`\n            Ok(JsValue::new(search_str == string.get_expect(start..end)))\n        } else {\n            // 12. If start < 0, return false.\n            Ok(false.into())\n        }\n    }\n\n    /// `String.prototype.includes( searchString[, position] )`\n    ///\n    /// The `includes()` method determines whether one string may be found within another string, returning `true` or `false` as appropriate.\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///  - [MDN documentation][mdn]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-string.prototype.includes\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/includes\n    pub(crate) fn includes(\n        this: &JsValue,\n        args: &[JsValue],\n        context: &mut Context,\n    ) -> JsResult<JsValue> {\n        // 1. Let O be ? RequireObjectCoercible(this value).\n        let this = this.require_object_coercible()?;\n\n        // 2. Let S be ? ToString(O).\n        let string = this.to_string(context)?;\n\n        let search_str = match args.get_or_undefined(0) {\n            // 3. Let isRegExp be ? IsRegExp(searchString).\n            search_string if RegExp::is_reg_exp(search_string, context)?.is_some() => {\n                return Err(JsNativeError::typ().with_message(\n                    // 4. If isRegExp is true, throw a TypeError exception.\n                    \"First argument to String.prototype.includes must not be a regular expression\",\n                ).into());\n            }\n            // 5. Let searchStr be ? ToString(searchString).\n            search_string => search_string.to_string(context)?,\n        };\n\n        // 6. Let pos be ? ToIntegerOrInfinity(position).\n        // 7. Assert: If position is undefined, then pos is 0.\n        let pos = args.get_or_undefined(1).to_integer_or_infinity(context)?;\n\n        // 8. Let len be the length of S.\n        // 9. Let start be the result of clamping pos between 0 and len.\n        let start = pos.clamp_finite(0, string.len() as i64) as usize;\n\n        // 10. Let index be ! StringIndexOf(S, searchStr, start).\n        // 11. If index is not -1, return true.\n        // 12. Return false.\n        Ok(string.index_of(search_str.as_str(), start).is_some().into())\n    }\n\n    /// `String.prototype.replace( regexp|substr, newSubstr|function )`\n    ///\n    /// The `replace()` method returns a new string with some or all matches of a `pattern` replaced by a `replacement`.\n    ///\n    /// The `pattern` can be a string or a `RegExp`, and the `replacement` can be a string or a function to be called for each match.\n    /// If `pattern` is a string, only the first occurrence will be replaced.\n    ///\n    /// The original string is left unchanged.\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///  - [MDN documentation][mdn]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-string.prototype.replace\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/replace\n    pub(crate) fn replace(\n        this: &JsValue,\n        args: &[JsValue],\n        context: &mut Context,\n    ) -> JsResult<JsValue> {\n        // Helper enum.\n        enum CallableOrString {\n            FunctionalReplace(JsObject),\n            ReplaceValue(JsString),\n        }\n\n        // 1. Let O be ? RequireObjectCoercible(this value).\n        let o = this.require_object_coercible()?;\n\n        let search_value = args.get_or_undefined(0);\n        let replace_value = args.get_or_undefined(1);\n\n        // 2. If searchValue is an Object, then\n        if search_value.is_object() {\n            // a. Let replacer be ? GetMethod(searchValue, @@replace).\n            let replacer = search_value.get_method(JsSymbol::replace(), context)?;\n\n            // b. If replacer is not undefined, then\n            if let Some(replacer) = replacer {\n                // i. Return ? Call(replacer, searchValue, « O, replaceValue »).\n                return replacer.call(search_value, &[o.clone(), replace_value.clone()], context);\n            }\n        }\n\n        // 3. Let string be ? ToString(O).\n        let string = o.to_string(context)?;\n\n        // 4. Let searchString be ? ToString(searchValue).\n        let search_string = search_value.to_string(context)?;\n\n        // 5. Let functionalReplace be IsCallable(replaceValue).\n        let functional_replace = replace_value.as_callable();\n\n        // 6. If functionalReplace is false, then\n        let replace_value = if let Some(callable) = functional_replace {\n            CallableOrString::FunctionalReplace(callable)\n        } else {\n            // a. Set replaceValue to ? ToString(replaceValue).\n            CallableOrString::ReplaceValue(replace_value.to_string(context)?)\n        };\n\n        // 7. Let searchLength be the length of searchString.\n        let search_length = search_string.len();\n\n        // 8. Let position be ! StringIndexOf(string, searchString, 0).\n        // 9. If position is -1, return string.\n        let Some(position) = string.index_of(search_string.as_str(), 0) else {\n            return Ok(string.into());\n        };\n\n        // 10. Let preserved be the substring of string from 0 to position.\n        let preserved = string.get_expect(..position);\n\n        let replacement = match replace_value {\n            // 11. If functionalReplace is true, then\n            CallableOrString::FunctionalReplace(replace_fn) => {\n                // a. Let replacement be ? ToString(? Call(replaceValue, undefined, « searchString, 𝔽(position), string »)).\n                replace_fn\n                    .call(\n                        &JsValue::undefined(),\n                        &[search_string.into(), position.into(), string.clone().into()],\n                        context,\n                    )?\n                    .to_string(context)?\n            }\n            // 12. Else,\n            CallableOrString::ReplaceValue(replace_value) => {\n                // a. Assert: Type(replaceValue) is String.\n                // b. Let captures be a new empty List.\n                let captures = Vec::new();\n\n                // c. Let replacement be ! GetSubstitution(searchString, string, position, captures, undefined, replaceValue).\n                get_substitution(\n                    &search_string,\n                    &string,\n                    position,\n                    &captures,\n                    &JsValue::undefined(),\n                    &replace_value,\n                    context,\n                )?\n            }\n        };\n\n        // 13. Return the string-concatenation of preserved, replacement, and the substring of string from position + searchLength.\n        Ok(js_string!(\n            &preserved,\n            &replacement,\n            &string.get_expect(position + search_length..)\n        )\n        .into())\n    }\n\n    /// `22.1.3.18 String.prototype.replaceAll ( searchValue, replaceValue )`\n    ///\n    /// The `replaceAll()` method returns a new string with all matches of a pattern replaced by a\n    /// replacement.\n    ///\n    /// The pattern can be a string or a `RegExp`, and the replacement can be a string or a\n    /// function to be called for each match.\n    ///\n    /// The original string is left unchanged.\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///  - [MDN documentation][mdn]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-string.prototype.replaceall\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/replace\n    pub(crate) fn replace_all(\n        this: &JsValue,\n        args: &[JsValue],\n        context: &mut Context,\n    ) -> JsResult<JsValue> {\n        // 1. Let O be ? RequireObjectCoercible(this value).\n        let o = this.require_object_coercible()?;\n\n        let search_value = args.get_or_undefined(0);\n        let replace_value = args.get_or_undefined(1);\n\n        // 2. If searchValue is an Object, then\n        if search_value.is_object() {\n            // a. Let isRegExp be ? IsRegExp(searchValue).\n            // b. If isRegExp is true, then\n            if let Some(obj) = RegExp::is_reg_exp(search_value, context)? {\n                // i. Let flags be ? Get(searchValue, \"flags\").\n                let flags = obj.get(js_string!(\"flags\"), context)?;\n\n                // ii. Perform ? RequireObjectCoercible(flags).\n                flags.require_object_coercible()?;\n\n                // iii. If ? ToString(flags) does not contain \"g\", throw a TypeError exception.\n                if !flags.to_string(context)?.contains(b'g') {\n                    return Err(JsNativeError::typ()\n                        .with_message(\n                            \"String.prototype.replaceAll called with a non-global RegExp argument\",\n                        )\n                        .into());\n                }\n            }\n\n            // c. Let replacer be ? GetMethod(searchValue, @@replace).\n            let replacer = search_value.get_method(JsSymbol::replace(), context)?;\n\n            // d. If replacer is not undefined, then\n            if let Some(replacer) = replacer {\n                // i. Return ? Call(replacer, searchValue, « O, replaceValue »).\n                return replacer.call(search_value, &[o.clone(), replace_value.clone()], context);\n            }\n        }\n\n        // 3. Let string be ? ToString(O).\n        let string = o.to_string(context)?;\n\n        // 4. Let searchString be ? ToString(searchValue).\n        let search_string = search_value.to_string(context)?;\n\n        // 5. Let functionalReplace be IsCallable(replaceValue).\n        let replace = if let Some(f) = replace_value.as_callable() {\n            Ok(f)\n        } else {\n            // 6. If functionalReplace is false, then\n            // a. Set replaceValue to ? ToString(replaceValue).\n            Err(replace_value.to_string(context)?)\n        };\n\n        // 7. Let searchLength be the length of searchString.\n        let search_length = search_string.len();\n\n        // 8. Let advanceBy be max(1, searchLength).\n        let advance_by = max(1, search_length);\n\n        // 9. Let matchPositions be a new empty List.\n        let mut match_positions = Vec::new();\n\n        // 10. Let position be ! StringIndexOf(string, searchString, 0).\n        let mut position = string.index_of(search_string.as_str(), 0);\n\n        // 11. Repeat, while position is not -1,\n        while let Some(p) = position {\n            // a. Append position to the end of matchPositions.\n            match_positions.push(p);\n\n            // b. Set position to ! StringIndexOf(string, searchString, position + advanceBy).\n            position = string.index_of(search_string.as_str(), p + advance_by);\n        }\n\n        // 12. Let endOfLastMatch be 0.\n        let mut end_of_last_match = 0;\n\n        // 13. Let result be the empty String.\n        let mut result = Vec::with_capacity(string.len());\n\n        // 14. For each element p of matchPositions, do\n        for p in match_positions {\n            // a. Let preserved be the substring of string from endOfLastMatch to p.\n            let preserved = string.get_expect(end_of_last_match..p);\n\n            // c. Else,\n            let replacement = match replace {\n                // b. If functionalReplace is true, then\n                Ok(ref replace_fn) => {\n                    // i. Let replacement be ? ToString(? Call(replaceValue, undefined, « searchString, 𝔽(p), string »)).\n                    replace_fn\n                        .call(\n                            &JsValue::undefined(),\n                            &[\n                                search_string.clone().into(),\n                                p.into(),\n                                string.clone().into(),\n                            ],\n                            context,\n                        )?\n                        .to_string(context)?\n                }\n                // i. Assert: Type(replaceValue) is String.\n                // ii. Let captures be a new empty List.\n                // iii. Let replacement be ! GetSubstitution(searchString, string, p, captures, undefined, replaceValue).\n                Err(ref replace_str) => get_substitution(\n                    &search_string,\n                    &string,\n                    p,\n                    &[],\n                    &JsValue::undefined(),\n                    replace_str,\n                    context,\n                )\n                .js_expect(\"GetSubstitution should never fail here.\")?,\n            };\n\n            // d. Set result to the string-concatenation of result, preserved, and replacement.\n            result.extend(preserved.iter());\n            result.extend(replacement.iter());\n\n            // e. Set endOfLastMatch to p + searchLength.\n            end_of_last_match = p + search_length;\n        }\n\n        // 15. If endOfLastMatch < the length of string, then\n        if end_of_last_match < string.len() {\n            // a. Set result to the string-concatenation of result and the substring of string from endOfLastMatch.\n            result.extend(string.get_expect(end_of_last_match..).iter());\n        }\n\n        // 16. Return result.\n        Ok(js_string!(&result[..]).into())\n    }\n\n    /// `String.prototype.indexOf( searchValue[, fromIndex] )`\n    ///\n    /// The `indexOf()` method returns the index within the calling `String` object of the first occurrence\n    /// of the specified value, starting the search at `fromIndex`.\n    ///\n    /// Returns `-1` if the value is not found.\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///  - [MDN documentation][mdn]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-string.prototype.indexof\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/indexOf\n    pub(crate) fn index_of(\n        this: &JsValue,\n        args: &[JsValue],\n        context: &mut Context,\n    ) -> JsResult<JsValue> {\n        // 1. Let O be ? RequireObjectCoercible(this value).\n        let this = this.require_object_coercible()?;\n\n        // 2. Let S be ? ToString(O).\n        let string = this.to_string(context)?;\n\n        // 3. Let searchStr be ? ToString(searchString).\n        let search_str = args.get_or_undefined(0).to_string(context)?;\n\n        // 4. Let pos be ? ToIntegerOrInfinity(position).\n        // 5. Assert: If position is undefined, then pos is 0.\n        let pos = args.get_or_undefined(1).to_integer_or_infinity(context)?;\n\n        // 6. Let len be the length of S.\n        let len = string.len() as i64;\n\n        // 7. Let start be the result of clamping pos between 0 and len.\n        let start = pos.clamp_finite(0, len) as usize;\n\n        // 8. Return 𝔽(! StringIndexOf(S, searchStr, start)).\n        Ok(string\n            .index_of(search_str.as_str(), start)\n            .map_or(-1, |i| i as i64)\n            .into())\n    }\n\n    /// `String.prototype.isWellFormed ( )`\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///  - [MDN documentation][mdn]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-string.prototype.iswellformed\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/isWellFormed\n    pub(crate) fn is_well_formed(\n        this: &JsValue,\n        _: &[JsValue],\n        context: &mut Context,\n    ) -> JsResult<JsValue> {\n        // 1. Let O be ? RequireObjectCoercible(this value).\n        let o = this.require_object_coercible()?;\n\n        // 2. Let S be ? ToString(O).\n        let s = o.to_string(context)?;\n\n        // 3. Return IsStringWellFormedUnicode(S).\n        for code_point in s.code_points() {\n            if let CodePoint::UnpairedSurrogate(_) = code_point {\n                return Ok(false.into());\n            }\n        }\n        Ok(true.into())\n    }\n\n    /// `String.prototype.lastIndexOf( searchValue[, fromIndex] )`\n    ///\n    /// The `lastIndexOf()` method returns the index within the calling `String` object of the last occurrence\n    /// of the specified value, searching backwards from `fromIndex`.\n    ///\n    /// Returns `-1` if the value is not found.\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///  - [MDN documentation][mdn]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-string.prototype.lastindexof\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/lastIndexOf\n    pub(crate) fn last_index_of(\n        this: &JsValue,\n        args: &[JsValue],\n        context: &mut Context,\n    ) -> JsResult<JsValue> {\n        // 1. Let O be ? RequireObjectCoercible(this value).\n        let this = this.require_object_coercible()?;\n\n        // 2. Let S be ? ToString(O).\n        let string = this.to_string(context)?;\n\n        // 3. Let searchStr be ? ToString(searchString).\n        let search_str = args.get_or_undefined(0).to_string(context)?;\n\n        // 4. Let numPos be ? ToNumber(position).\n        // 5. Assert: If position is undefined, then numPos is NaN.\n        let num_pos = args.get_or_undefined(1).to_number(context)?;\n\n        // 6. If numPos is NaN, let pos be +∞; otherwise, let pos be ! ToIntegerOrInfinity(numPos).\n        let pos = if num_pos.is_nan() {\n            IntegerOrInfinity::PositiveInfinity\n        } else {\n            JsValue::new(num_pos)\n                .to_integer_or_infinity(context)\n                .js_expect(\"Already called `to_number so this must not fail.\")?\n        };\n\n        // 7. Let len be the length of S.\n        let len = string.len();\n        // 8. Let start be the result of clamping pos between 0 and len.\n        let start = pos.clamp_finite(0, len as i64) as usize;\n\n        // 9. If searchStr is the empty String, return 𝔽(start).\n        if search_str.is_empty() {\n            return Ok(JsValue::new(start));\n        }\n\n        // 10. Let searchLen be the length of searchStr.\n        let search_len = search_str.len();\n\n        if let Some(end) = len.checked_sub(search_len) {\n            // 11. For each non-negative integer i starting with start such that i ≤ len - searchLen, in descending order, do\n            for i in (0..=min(start, end)).rev() {\n                // a. Let candidate be the substring of S from i to i + searchLen.\n                let candidate = string.get_expect(i..i + search_len);\n\n                // b. If candidate is the same sequence of code units as searchStr, return 𝔽(i).\n                if candidate == search_str {\n                    return Ok(i.into());\n                }\n            }\n        }\n\n        // 12. Return -1𝔽.\n        Ok(JsValue::new(-1))\n    }\n\n    /// [`String.prototype.localeCompare ( that [ , locales [ , options ] ] )`][spec]\n    ///\n    /// Returns a number indicating whether a reference string comes before, or after, or is the\n    /// same as the given string in sort order.\n    ///\n    /// More information:\n    ///  - [MDN documentation][mdn]\n    ///\n    /// [spec]: https://tc39.es/ecma402/#sup-String.prototype.localeCompare\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/localeCompare\n    pub(crate) fn locale_compare(\n        this: &JsValue,\n        args: &[JsValue],\n        context: &mut Context,\n    ) -> JsResult<JsValue> {\n        // 1. Let O be ? RequireObjectCoercible(this value).\n        let o = this.require_object_coercible()?;\n\n        // 2. Let S be ? ToString(O).\n        let s = o.to_string(context)?;\n\n        // 3. Let thatValue be ? ToString(that).\n        let that_value = args.get_or_undefined(0).to_string(context)?;\n\n        let cmp = {\n            #[cfg(feature = \"intl\")]\n            {\n                use crate::builtins::intl::Collator;\n\n                // 4. Let collator be ? Construct(%Collator%, « locales, options »).\n                let collator = Collator::constructor(\n                    &context\n                        .intrinsics()\n                        .constructors()\n                        .collator()\n                        .constructor()\n                        .into(),\n                    args.get(1..).unwrap_or_default(),\n                    context,\n                )?;\n\n                let object = collator.as_object();\n                let collator = object\n                    .as_ref()\n                    .and_then(|o| o.downcast_ref::<Collator>())\n                    .js_expect(\"constructor must return a `Collator` object\")?;\n\n                let s = s.iter().collect::<Vec<_>>();\n                let that_value = that_value.iter().collect::<Vec<_>>();\n\n                collator\n                    .collator()\n                    .as_borrowed()\n                    .compare_utf16(&s, &that_value) as i8\n            }\n\n            // Default to common comparison if the user doesn't have `Intl` enabled.\n            #[cfg(not(feature = \"intl\"))]\n            {\n                s.cmp(&that_value) as i8\n            }\n        };\n\n        // 5. Return CompareStrings(collator, S, thatValue).\n        Ok(cmp.into())\n    }\n\n    /// `String.prototype.match( regexp )`\n    ///\n    /// The `match()` method retrieves the result of matching a **string** against a [`regular expression`][regex].\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///  - [MDN documentation][mdn]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-string.prototype.match\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/match\n    /// [regex]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_Expressions\n    pub(crate) fn r#match(\n        this: &JsValue,\n        args: &[JsValue],\n        context: &mut Context,\n    ) -> JsResult<JsValue> {\n        // 1. Let O be ? RequireObjectCoercible(this value).\n        let o = this.require_object_coercible()?;\n\n        // 2. If regexp is an Object, then\n        let regexp = args.get_or_undefined(0);\n        if regexp.is_object() {\n            // a. Let matcher be ? GetMethod(regexp, @@match).\n            let matcher = regexp.get_method(JsSymbol::r#match(), context)?;\n            // b. If matcher is not undefined, then\n            if let Some(matcher) = matcher {\n                // i. Return ? Call(matcher, regexp, « O »).\n                return matcher.call(regexp, std::slice::from_ref(o), context);\n            }\n        }\n\n        // 3. Let S be ? ToString(O).\n        let s = o.to_string(context)?;\n\n        // 4. Let rx be ? RegExpCreate(regexp, undefined).\n        let rx = RegExp::create(regexp, &JsValue::undefined(), context)?;\n\n        // 5. Return ? Invoke(rx, @@match, « S »).\n        rx.invoke(JsSymbol::r#match(), &[JsValue::new(s)], context)\n    }\n\n    /// Abstract operation `StringPad ( O, maxLength, fillString, placement )`.\n    ///\n    /// Performs the actual string padding for padStart/End.\n    ///\n    /// More information:\n    /// - [ECMAScript reference][spec]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-stringpad\n    pub(crate) fn string_pad(\n        object: &JsValue,\n        max_length: &JsValue,\n        fill_string: &JsValue,\n        placement: Placement,\n        context: &mut Context,\n    ) -> JsResult<JsValue> {\n        // 1. Let S be ? ToString(O).\n        let string = object.to_string(context)?;\n\n        // 2. Let intMaxLength be ℝ(? ToLength(maxLength)).\n        let int_max_length = max_length.to_length(context)?;\n\n        // 3. Let stringLength be the length of S.\n        let string_length = string.len() as u64;\n\n        // 4. If intMaxLength ≤ stringLength, return S.\n        if int_max_length <= string_length {\n            return Ok(string.into());\n        }\n\n        // 5. If fillString is undefined, let filler be the String value consisting solely of the code unit 0x0020 (SPACE).\n        let filler = if fill_string.is_undefined() {\n            js_string!(\"\\u{0020}\")\n        } else {\n            // 6. Else, let filler be ? ToString(fillString).\n            fill_string.to_string(context)?\n        };\n\n        // 7. If filler is the empty String, return S.\n        if filler.is_empty() {\n            return Ok(string.into());\n        }\n\n        // 8. Let fillLen be intMaxLength - stringLength.\n        let fill_len = int_max_length - string_length;\n\n        // Check if the resulting string would exceed the maximum string length\n        if int_max_length > (Self::MAX_STRING_LENGTH as u64) {\n            return Err(JsNativeError::range()\n                .with_message(format!(\n                    \"cannot create a string longer than the maximum allowed length ({})\",\n                    Self::MAX_STRING_LENGTH\n                ))\n                .into());\n        }\n\n        let filler_len = filler.len() as u64;\n\n        // 9. Let truncatedStringFiller be the String value consisting of repeated\n        // concatenations of filler truncated to length fillLen.\n        let repetitions = {\n            let q = fill_len / filler_len;\n            let r = fill_len % filler_len;\n            if r == 0 { q } else { q + 1 }\n        };\n\n        let mut truncated_string_filler = Vec::with_capacity(fill_len as usize);\n        let filler_slice = filler.to_vec();\n        for _ in 0..repetitions {\n            let remaining = fill_len as usize - truncated_string_filler.len();\n            if remaining >= filler_slice.len() {\n                truncated_string_filler.extend_from_slice(&filler_slice);\n            } else {\n                truncated_string_filler.extend_from_slice(&filler_slice[..remaining]);\n                break;\n            }\n        }\n        let truncated_string_filler = JsString::from(&truncated_string_filler[..]);\n\n        // 10. If placement is start, return the string-concatenation of truncatedStringFiller and S.\n        if placement == Placement::Start {\n            Ok(js_string!(&truncated_string_filler, &string).into())\n        } else {\n            // 11. Else, return the string-concatenation of S and truncatedStringFiller.\n            Ok(js_string!(&string, &truncated_string_filler).into())\n        }\n    }\n\n    /// `String.prototype.padEnd( targetLength[, padString] )`\n    ///\n    /// The `padEnd()` method pads the current string with a given string (repeated, if needed) so that the resulting string reaches a given length.\n    ///\n    /// The padding is applied from the end of the current string.\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///  - [MDN documentation][mdn]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-string.prototype.padend\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/padEnd\n    pub(crate) fn pad_end(\n        this: &JsValue,\n        args: &[JsValue],\n        context: &mut Context,\n    ) -> JsResult<JsValue> {\n        // 1. Let O be ? RequireObjectCoercible(this value).\n        let this = this.require_object_coercible()?;\n\n        let max_length = args.get_or_undefined(0);\n        let fill_string = args.get_or_undefined(1);\n\n        // 2. Return ? StringPad(O, maxLength, fillString, end).\n        Self::string_pad(this, max_length, fill_string, Placement::End, context)\n    }\n\n    /// `String.prototype.padStart( targetLength [, padString] )`\n    ///\n    /// The `padStart()` method pads the current string with another string (multiple times, if needed) until the resulting string reaches the given length.\n    ///\n    /// The padding is applied from the start of the current string.\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///  - [MDN documentation][mdn]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-string.prototype.padstart\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/padStart\n    pub(crate) fn pad_start(\n        this: &JsValue,\n        args: &[JsValue],\n        context: &mut Context,\n    ) -> JsResult<JsValue> {\n        // 1. Let O be ? RequireObjectCoercible(this value).\n        let this = this.require_object_coercible()?;\n\n        let max_length = args.get_or_undefined(0);\n        let fill_string = args.get_or_undefined(1);\n\n        // 2. Return ? StringPad(O, maxLength, fillString, end).\n        Self::string_pad(this, max_length, fill_string, Placement::Start, context)\n    }\n\n    /// `String.prototype.trim()`\n    ///\n    /// The `trim()` method removes whitespace from both ends of a string.\n    ///\n    /// Whitespace in this context is all the whitespace characters (space, tab, no-break space, etc.) and all the line terminator characters (LF, CR, etc.).\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///  - [MDN documentation][mdn]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-string.prototype.trim\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/trim\n    pub(crate) fn trim(this: &JsValue, _: &[JsValue], context: &mut Context) -> JsResult<JsValue> {\n        // 1. Let S be the this value.\n        // 2. Return ? TrimString(S, start+end).\n        let object = this.require_object_coercible()?;\n        let string = object.to_string(context)?;\n        Ok(js_string!(string.trim()).into())\n    }\n\n    /// `String.prototype.trimStart()`\n    ///\n    /// The `trimStart()` method removes whitespace from the beginning of a string.\n    ///\n    /// Whitespace in this context is all the whitespace characters (space, tab, no-break space, etc.) and all the line terminator characters (LF, CR, etc.).\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///  - [MDN documentation][mdn]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-string.prototype.trimstart\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/trimStart\n    pub(crate) fn trim_start(\n        this: &JsValue,\n        _: &[JsValue],\n        context: &mut Context,\n    ) -> JsResult<JsValue> {\n        // 1. Let S be the this value.\n        // 2. Return ? TrimString(S, start).\n        let object = this.require_object_coercible()?;\n        let string = object.to_string(context)?;\n        Ok(js_string!(string.trim_start()).into())\n    }\n\n    /// `String.prototype.trimEnd()`\n    ///\n    /// The `trimEnd()` method removes whitespace from the end of a string.\n    ///\n    /// Whitespace in this context is all the whitespace characters (space, tab, no-break space, etc.) and all the line terminator characters (LF, CR, etc.).\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///  - [MDN documentation][mdn]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-string.prototype.trimend\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/trimEnd\n    pub(crate) fn trim_end(\n        this: &JsValue,\n        _: &[JsValue],\n        context: &mut Context,\n    ) -> JsResult<JsValue> {\n        // 1. Let S be the this value.\n        // 2. Return ? TrimString(S, end).\n        let object = this.require_object_coercible()?;\n        let string = object.to_string(context)?;\n        Ok(string.trim_end().into())\n    }\n\n    /// [`String.prototype.toUpperCase()`][upper] and [`String.prototype.toLowerCase()`][lower]\n    ///\n    /// The case methods return the calling string value converted to uppercase or lowercase.\n    ///\n    /// The value will be **converted** to a string if it isn't one.\n    ///\n    /// More information:\n    ///  - [MDN documentation][mdn]\n    ///\n    /// [upper]: https://tc39.es/ecma262/#sec-string.prototype.toUppercase\n    /// [lower]: https://tc39.es/ecma262/#sec-string.prototype.toLowercase\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/toUpperCase\n    pub(crate) fn to_case<const UPPER: bool>(\n        this: &JsValue,\n        _: &[JsValue],\n        context: &mut Context,\n    ) -> JsResult<JsValue> {\n        // 1. Let O be ? RequireObjectCoercible(this value).\n        let this = this.require_object_coercible()?;\n\n        // 2. Let S be ? ToString(O).\n        let string = this.to_string(context)?;\n\n        // 3. Let sText be ! StringToCodePoints(S).\n        // 4. Let upperText be the result of toUppercase(sText), according to\n        // the Unicode Default Case Conversion algorithm.\n        let text = string.map_valid_segments(|s| {\n            let cow_str = if UPPER {\n                s.cow_to_uppercase()\n            } else {\n                s.cow_to_lowercase()\n            };\n            match cow_str {\n                Cow::Borrowed(_) => s,\n                Cow::Owned(replaced_str) => replaced_str,\n            }\n        });\n\n        // 5. Let L be ! CodePointsToString(upperText).\n        // 6. Return L.\n        Ok(text.into())\n    }\n\n    /// [`String.prototype.toLocaleLowerCase ( [ locales ] )`][lower] and\n    /// [`String.prototype.toLocaleUpperCase ( [ locales ] )`][upper]\n    ///\n    /// [lower]: https://tc39.es/ecma402/#sup-string.prototype.tolocalelowercase\n    /// [upper]: https://tc39.es/ecma402/#sup-string.prototype.tolocaleuppercase\n    pub(crate) fn to_locale_case<const UPPER: bool>(\n        this: &JsValue,\n        args: &[JsValue],\n        context: &mut Context,\n    ) -> JsResult<JsValue> {\n        #[cfg(feature = \"intl\")]\n        {\n            use super::intl::locale::{canonicalize_locale_list, default_locale};\n\n            // 1. Let O be ? RequireObjectCoercible(this value).\n            let this = this.require_object_coercible()?;\n\n            // 2. Let S be ? ToString(O).\n            let string = this.to_string(context)?;\n\n            // 3. Return ? TransformCase(S, locales, lower).\n\n            //  TransformCase ( S, locales, targetCase )\n            // https://tc39.es/ecma402/#sec-transform-case\n\n            // 1. Let requestedLocales be ? CanonicalizeLocaleList(locales).\n            // 2. If requestedLocales is not an empty List, then\n            let requested_locale = if let Some(locale) =\n                canonicalize_locale_list(args.get_or_undefined(0), context)?\n                    .into_iter()\n                    .next()\n            {\n                // a. Let requestedLocale be requestedLocales[0].\n                locale\n            } else {\n                // 3. Else,\n                //     a. Let requestedLocale be ! DefaultLocale().\n                default_locale(context.intl_provider().locale_canonicalizer()?)\n            };\n\n            // 4. Let noExtensionsLocale be the String value that is requestedLocale with any Unicode locale extension sequences (6.2.1) removed.\n            // 5. Let availableLocales be a List with language tags that includes the languages for which the Unicode\n            //    Character Database contains language sensitive case mappings. Implementations may add additional\n            //    language tags if they support case mapping for additional locales.\n            // 6. Let match be LookupMatchingLocaleByPrefix(availableLocales, noExtensionsLocale).\n            // 7. If match is not undefined, let locale be match.[[locale]]; else let locale be \"und\".\n            // ICU4X already handles locales in the correct way.\n            let casemapper = context.intl_provider().case_mapper()?;\n\n            // 8. Let codePoints be StringToCodePoints(S).\n            let result = string.map_valid_segments(|segment| {\n                if UPPER {\n                    // 10. Else,\n                    //     a. Assert: targetCase is upper.\n                    //     b. Let newCodePoints be a List whose elements are the result of an uppercase transformation of codePoints according to an implementation-derived algorithm using locale or the Unicode Default Case Conversion algorithm.\n                    casemapper\n                        .as_borrowed()\n                        .uppercase_to_string(&segment, &requested_locale.id)\n                        .into()\n                } else {\n                    // 9. If targetCase is lower, then\n                    //     a. Let newCodePoints be a List whose elements are the result of a lowercase transformation of codePoints according to an implementation-derived algorithm using locale or the Unicode Default Case Conversion algorithm.\n                    casemapper\n                        .as_borrowed()\n                        .lowercase_to_string(&segment, &requested_locale.id)\n                        .into()\n                }\n            });\n\n            // 11. Return CodePointsToString(newCodePoints).\n            Ok(result.into())\n        }\n\n        #[cfg(not(feature = \"intl\"))]\n        {\n            Self::to_case::<UPPER>(this, args, context)\n        }\n    }\n\n    /// `String.prototype.toWellFormed ( )`\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///  - [MDN documentation][mdn]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-string.prototype.towellformed\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/toWellFormed\n    pub(crate) fn to_well_formed(\n        this: &JsValue,\n        _: &[JsValue],\n        context: &mut Context,\n    ) -> JsResult<JsValue> {\n        // 1. Let O be ? RequireObjectCoercible(this value).\n        let o = this.require_object_coercible()?;\n\n        // 2. Let S be ? ToString(O).\n        let s = o.to_string(context)?;\n\n        // 3. Let strLen be the length of S.\n        // 4. Let k be 0.\n        // 5. Let result be the empty String.\n        // 6. Repeat, while k < strLen,\n        // a. Let cp be CodePointAt(S, k).\n        // b. If cp.[[IsUnpairedSurrogate]] is true, then\n        // i. Set result to the string-concatenation of result and 0xFFFD (REPLACEMENT CHARACTER).\n        // c. Else,\n        // i. Set result to the string-concatenation of result and UTF16EncodeCodePoint(cp.[[CodePoint]]).\n        // d. Set k to k + cp.[[CodeUnitCount]].\n        let result = s\n            .code_points()\n            .map(|code_point| match code_point {\n                CodePoint::UnpairedSurrogate(_) => '\\u{FFFD}',\n                CodePoint::Unicode(char) => char,\n            })\n            .collect::<std::string::String>();\n\n        // 7. Return result.\n        Ok(js_string!(result).into())\n    }\n\n    /// `String.prototype.substring( indexStart[, indexEnd] )`\n    ///\n    /// The `substring()` method returns the part of the `string` between the start and end indexes, or to the end of the string.\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///  - [MDN documentation][mdn]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-string.prototype.substring\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/substring\n    pub(crate) fn substring(\n        this: &JsValue,\n        args: &[JsValue],\n        context: &mut Context,\n    ) -> JsResult<JsValue> {\n        // 1. Let O be ? RequireObjectCoercible(this value).\n        let this = this.require_object_coercible()?;\n\n        // 2. Let S be ? ToString(O).\n        let string = this.to_string(context)?;\n\n        // 3. Let len be the length of S.\n        let len = string.len() as i64;\n\n        // 4. Let intStart be ? ToIntegerOrInfinity(start).\n        let int_start = args.get_or_undefined(0).to_integer_or_infinity(context)?;\n\n        // 5. If end is undefined, let intEnd be len; else let intEnd be ? ToIntegerOrInfinity(end).\n        let int_end = args\n            .get_or_undefined(1)\n            .map_or(Ok(IntegerOrInfinity::Integer(len)), |end| {\n                end.to_integer_or_infinity(context)\n            })?;\n\n        // 6. Let finalStart be the result of clamping intStart between 0 and len.\n        let final_start = int_start.clamp_finite(0, len) as usize;\n\n        // 7. Let finalEnd be the result of clamping intEnd between 0 and len.\n        let final_end = int_end.clamp_finite(0, len) as usize;\n\n        // 8. Let from be min(finalStart, finalEnd).\n        let from = min(final_start, final_end);\n\n        // 9. Let to be max(finalStart, finalEnd).\n        let to = max(final_start, final_end);\n\n        // 10. Return the substring of S from from to to.\n        // SAFETY: We already checked that `from` and `to` are within bounds.\n        Ok(unsafe { JsString::slice_unchecked(&string, from, to).into() })\n    }\n\n    /// `String.prototype.split ( separator, limit )`\n    ///\n    /// The `split()` method divides a String into an ordered list of substrings, puts these substrings into an array, and returns the array.\n    /// The division is done by searching for a pattern; where the pattern is provided as the first parameter in the method's call.\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///  - [MDN documentation][mdn]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-string.prototype.split\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/split\n    pub(crate) fn split(\n        this: &JsValue,\n        args: &[JsValue],\n        context: &mut Context,\n    ) -> JsResult<JsValue> {\n        // 1. Let O be ? RequireObjectCoercible(this value).\n        let this = this.require_object_coercible()?;\n\n        let separator = args.get_or_undefined(0);\n        let limit = args.get_or_undefined(1);\n\n        // 2. If separator is an Object, then\n        if separator.is_object() {\n            // a. Let splitter be ? GetMethod(separator, @@split).\n            let splitter = separator.get_method(JsSymbol::split(), context)?;\n            // b. If splitter is not undefined, then\n            if let Some(splitter) = splitter {\n                // i. Return ? Call(splitter, separator, « O, limit »).\n                return splitter.call(separator, &[this.clone(), limit.clone()], context);\n            }\n        }\n\n        // 3. Let S be ? ToString(O).\n        let this_str = this.to_string(context)?;\n\n        // 4.  If limit is undefined, let lim be 2^32 - 1; else let lim be ℝ(? ToUint32(limit)).\n        let lim = if limit.is_undefined() {\n            u32::MAX\n        } else {\n            limit.to_u32(context)?\n        } as usize;\n\n        // 5. Let R be ? ToString(separator).\n        let separator_str = separator.to_string(context)?;\n\n        // 6. If lim = 0, return A.\n        if lim == 0 {\n            // a. Return ! CreateArrayFromList(« »).\n            return Ok(Array::create_array_from_list([], context).into());\n        }\n\n        // 7. If separator is undefined, then\n        if separator.is_undefined() {\n            // a. Return ! CreateArrayFromList(« S »).\n            return Ok(Array::create_array_from_list([this_str.into()], context).into());\n        }\n\n        // 8. Let separatorLength be the length of R.\n        let separator_length = separator_str.len();\n\n        // 9. If separatorLength is 0, then\n        if separator_length == 0 {\n            // a. Let head be the substring of S from 0 to lim.\n            // b. Let codeUnits be a List consisting of the sequence of code units that are the elements of head.\n            let head_str = this_str.get(..lim).unwrap_or(this_str);\n            let head = head_str\n                .iter()\n                .map(|code| js_string!(std::slice::from_ref(&code)).into());\n\n            // c. Return ! CreateArrayFromList(codeUnits).\n            return Ok(Array::create_array_from_list(head, context).into());\n        }\n\n        // 10. If S is the empty String, return ! CreateArrayFromList(« S »).\n        if this_str.is_empty() {\n            return Ok(Array::create_array_from_list([this_str.into()], context).into());\n        }\n\n        // 11. Let substrings be a new empty List.\n        let mut substrings = vec![];\n\n        // 12. Let i be 0.\n        let mut i = 0;\n\n        // 13. Let j be ! StringIndexOf(S, R, 0).\n        let mut j = this_str.index_of(separator_str.as_str(), 0);\n\n        // 14. Repeat, while j is not -1\n        while let Some(index) = j {\n            // a. Let T be the substring of S from i to j.\n            // b. Append T as the last element of substrings.\n            substrings.push(this_str.slice(i, index));\n\n            // c. If the number of elements of substrings is lim, return ! CreateArrayFromList(substrings).\n            if substrings.len() == lim {\n                return Ok(Array::create_array_from_list(\n                    substrings.into_iter().map(JsValue::from),\n                    context,\n                )\n                .into());\n            }\n            // d. Set i to j + separatorLength.\n            i = index + separator_length;\n\n            // e. Set j to ! StringIndexOf(S, R, i).\n            j = this_str.index_of(separator_str.as_str(), i);\n        }\n\n        // 15. Let T be the substring of S from i.\n        // 16. Append T to substrings.\n        substrings.push(this_str.slice(i, this_str.len()));\n\n        // 17. Return ! CreateArrayFromList(substrings).\n        Ok(\n            Array::create_array_from_list(substrings.into_iter().map(JsValue::from), context)\n                .into(),\n        )\n    }\n\n    /// `String.prototype.valueOf()`\n    ///\n    /// The `valueOf()` method returns the primitive value of a `String` object.\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///  - [MDN documentation][mdn]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-string.prototype.value_of\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/valueOf\n    pub(crate) fn value_of(\n        this: &JsValue,\n        _args: &[JsValue],\n        _context: &mut Context,\n    ) -> JsResult<JsValue> {\n        // 1. Return ? thisStringValue(this value).\n        Self::this_string_value(this).map(JsValue::from)\n    }\n\n    /// `String.prototype.matchAll( regexp )`\n    ///\n    /// The `matchAll()` method returns an iterator of all results matching a string against a [`regular expression`][regex], including [capturing groups][cg].\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///  - [MDN documentation][mdn]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-string.prototype.matchall\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/matchAll\n    /// [regex]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_Expressions\n    /// [cg]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_Expressions/Groups_and_Ranges\n    pub(crate) fn match_all(\n        this: &JsValue,\n        args: &[JsValue],\n        context: &mut Context,\n    ) -> JsResult<JsValue> {\n        // 1. Let O be ? RequireObjectCoercible(this value).\n        let o = this.require_object_coercible()?;\n\n        // 2. If regexp is an Object, then\n        let regexp = args.get_or_undefined(0);\n        if regexp.is_object() {\n            // a. Let isRegExp be ? IsRegExp(regexp).\n            // b. If isRegExp is true, then\n            if let Some(regexp) = RegExp::is_reg_exp(regexp, context)? {\n                // i. Let flags be ? Get(regexp, \"flags\").\n                let flags = regexp.get(js_string!(\"flags\"), context)?;\n\n                // ii. Perform ? RequireObjectCoercible(flags).\n                flags.require_object_coercible()?;\n\n                // iii. If ? ToString(flags) does not contain \"g\", throw a TypeError exception.\n                if !flags.to_string(context)?.contains(b'g') {\n                    return Err(JsNativeError::typ()\n                        .with_message(\n                            \"String.prototype.matchAll called with a non-global RegExp argument\",\n                        )\n                        .into());\n                }\n            }\n            // c. Let matcher be ? GetMethod(regexp, @@matchAll).\n            let matcher = regexp.get_method(JsSymbol::match_all(), context)?;\n            // d. If matcher is not undefined, then\n            if let Some(matcher) = matcher {\n                return matcher.call(regexp, std::slice::from_ref(o), context);\n            }\n        }\n\n        // 3. Let S be ? ToString(O).\n        let s = o.to_string(context)?;\n\n        // 4. Let rx be ? RegExpCreate(regexp, \"g\").\n        let rx = RegExp::create(regexp, &JsValue::new(js_string!(\"g\")), context)?;\n\n        // 5. Return ? Invoke(rx, @@matchAll, « S »).\n        rx.invoke(JsSymbol::match_all(), &[JsValue::new(s)], context)\n    }\n\n    /// `String.prototype.normalize( [ form ] )`\n    ///\n    /// The `normalize()` method normalizes a string into a form specified in the Unicode® Standard Annex #15\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///  - [MDN documentation][mdn]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-string.prototype.normalize\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/normalize\n    pub(crate) fn normalize(\n        this: &JsValue,\n        args: &[JsValue],\n        context: &mut Context,\n    ) -> JsResult<JsValue> {\n        /// Represents the type of normalization applied to a [`JsString`]\n        #[derive(Clone, Copy)]\n        pub(crate) enum Normalization {\n            Nfc,\n            Nfd,\n            Nfkc,\n            Nfkd,\n        }\n\n        // 1. Let O be ? RequireObjectCoercible(this value).\n        let this = this.require_object_coercible()?;\n\n        // 2. Let S be ? ToString(O).\n        let s = this.to_string(context)?;\n\n        // 6. Let ns be the String value that is the result of normalizing S\n        // into the normalization form named by f as specified in\n        // https://unicode.org/reports/tr15/.\n        let first = args.get_or_undefined(0);\n        // 3. If form is undefined, let f be \"NFC\".\n        let normalization = if first.is_undefined() {\n            Normalization::Nfc\n        } else {\n            // 4. Else, let f be ? ToString(form).\n            match first.to_string(context)? {\n                ntype if &ntype == \"NFC\" => Normalization::Nfc,\n                ntype if &ntype == \"NFD\" => Normalization::Nfd,\n                ntype if &ntype == \"NFKC\" => Normalization::Nfkc,\n                ntype if &ntype == \"NFKD\" => Normalization::Nfkd,\n                // 5. If f is not one of \"NFC\", \"NFD\", \"NFKC\", or \"NFKD\", throw a RangeError exception.\n                _ => {\n                    return Err(JsNativeError::range()\n                        .with_message(\n                            \"The normalization form should be one of NFC, NFD, NFKC, NFKD.\",\n                        )\n                        .into());\n                }\n            }\n        };\n\n        let normalizers = {\n            #[cfg(not(feature = \"intl\"))]\n            {\n                const NORMALIZERS: StringNormalizers = StringNormalizers {\n                    nfc: ComposingNormalizer::new_nfc().static_to_owned(),\n                    nfkc: ComposingNormalizer::new_nfkc().static_to_owned(),\n                    nfd: DecomposingNormalizer::new_nfd().static_to_owned(),\n                    nfkd: DecomposingNormalizer::new_nfkd().static_to_owned(),\n                };\n                &NORMALIZERS\n            }\n            #[cfg(feature = \"intl\")]\n            {\n                context.intl_provider().string_normalizers()?\n            }\n        };\n\n        let s = s.iter().collect::<Vec<_>>();\n\n        let result = match normalization {\n            Normalization::Nfc => normalizers.nfc.as_borrowed().normalize_utf16(&s),\n            Normalization::Nfd => normalizers.nfd.as_borrowed().normalize_utf16(&s),\n            Normalization::Nfkc => normalizers.nfkc.as_borrowed().normalize_utf16(&s),\n            Normalization::Nfkd => normalizers.nfkd.as_borrowed().normalize_utf16(&s),\n        };\n\n        // 7. Return ns.\n        Ok(js_string!(&result[..]).into())\n    }\n\n    /// `String.prototype.search( regexp )`\n    ///\n    /// The `search()` method executes a search for a match between a regular expression and this String object.\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///  - [MDN documentation][mdn]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-string.prototype.search\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/search\n    pub(crate) fn search(\n        this: &JsValue,\n        args: &[JsValue],\n        context: &mut Context,\n    ) -> JsResult<JsValue> {\n        // 1. Let O be ? RequireObjectCoercible(this value).\n        let o = this.require_object_coercible()?;\n\n        // 2. If regexp is an Object, then\n        let regexp = args.get_or_undefined(0);\n        if regexp.is_object() {\n            // a. Let searcher be ? GetMethod(regexp, @@search).\n            let searcher = regexp.get_method(JsSymbol::search(), context)?;\n            // b. If searcher is not undefined, then\n            if let Some(searcher) = searcher {\n                // i. Return ? Call(searcher, regexp, « O »).\n                return searcher.call(regexp, std::slice::from_ref(o), context);\n            }\n        }\n\n        // 3. Let string be ? ToString(O).\n        let string = o.to_string(context)?;\n\n        // 4. Let rx be ? RegExpCreate(regexp, undefined).\n        let rx = RegExp::create(regexp, &JsValue::undefined(), context)?;\n\n        // 5. Return ? Invoke(rx, @@search, « string »).\n        rx.invoke(JsSymbol::search(), &[JsValue::new(string)], context)\n    }\n\n    pub(crate) fn iterator(\n        this: &JsValue,\n        _: &[JsValue],\n        context: &mut Context,\n    ) -> JsResult<JsValue> {\n        // 1. Let O be ? RequireObjectCoercible(this value).\n        let o = this.require_object_coercible()?;\n        // 2. Let s be ? ToString(O).\n        let s = o.to_string(context)?;\n\n        Ok(StringIterator::create_string_iterator(s, context).into())\n    }\n}\n\n#[cfg(feature = \"annex-b\")]\nimpl String {\n    /// `String.prototype.substr( start[, length] )`\n    ///\n    /// The `substr()` method returns a portion of the string, starting at the specified index and extending for a given number of characters afterward.\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///  - [MDN documentation][mdn]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-string.prototype.substr\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/substr\n    /// <https://tc39.es/ecma262/#sec-string.prototype.substr>\n    pub(crate) fn substr(\n        this: &JsValue,\n        args: &[JsValue],\n        context: &mut Context,\n    ) -> JsResult<JsValue> {\n        // 1. Let O be ? RequireObjectCoercible(this value).\n        let o = this.require_object_coercible()?;\n\n        // 2. Let S be ? ToString(O).\n        let s = o.to_string(context)?;\n\n        // 3. Let size be the length of S.\n        let size = s.len() as i64;\n\n        // 4. Let intStart be ? ToIntegerOrInfinity(start).\n        let start = args.get_or_undefined(0);\n        let int_start = start.to_integer_or_infinity(context)?;\n\n        let int_start = match int_start {\n            // 5. If intStart is -∞, set intStart to 0.\n            IntegerOrInfinity::NegativeInfinity => 0,\n            // 6. Else if intStart < 0, set intStart to max(size + intStart, 0).\n            IntegerOrInfinity::Integer(int_start) if int_start < 0 => max(size + int_start, 0),\n            IntegerOrInfinity::Integer(int_start) => int_start,\n            // 7. Else, set intStart to min(intStart, size).\n            //\n            // NOTE: size will always be smaller than +∞\n            IntegerOrInfinity::PositiveInfinity => size,\n        } as usize;\n\n        // 8. If length is undefined, let intLength be size;\n        //    otherwise let intLength be ? ToIntegerOrInfinity(length).\n        let length = args.get_or_undefined(1);\n        let int_length = if length.is_undefined() {\n            IntegerOrInfinity::Integer(size)\n        } else {\n            length.to_integer_or_infinity(context)?\n        };\n\n        // 9. Set intLength to the result of clamping intLength between 0 and size.\n        let int_length = match int_length {\n            IntegerOrInfinity::NegativeInfinity => 0,\n            IntegerOrInfinity::PositiveInfinity => size,\n            IntegerOrInfinity::Integer(i) => i.clamp(0, size),\n        } as usize;\n\n        // 10. Let intEnd be min(intStart + intLength, size).\n        let int_end = min(int_start + int_length, size as usize);\n\n        // 11. Return the substring of S from intStart to intEnd.\n        if let Some(substr) = s.get(int_start..int_end) {\n            Ok(substr.into())\n        } else {\n            Ok(js_string!().into())\n        }\n    }\n\n    /// `CreateHTML(string, tag, attribute, value)`\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-createhtml\n    pub(crate) fn create_html(\n        string: &JsValue,\n        tag: JsStr<'_>,\n        attribute_and_value: Option<(JsStr<'_>, &JsValue)>,\n        context: &mut Context,\n    ) -> JsResult<JsValue> {\n        // 1. Let str be ? RequireObjectCoercible(string).\n        let str = string.require_object_coercible()?;\n\n        // 2. Let S be ? ToString(str).\n        let s = str.to_string(context)?;\n\n        // 3. Let p1 be the string-concatenation of \"<\" and tag.\n        let mut p1 = js_string!(js_str!(\"<\"), tag);\n\n        // 4. If attribute is not the empty String, then\n        if let Some((attribute, value)) = attribute_and_value {\n            // a. Let V be ? ToString(value).\n            let v = value.to_string(context)?;\n\n            // b. Let escapedV be the String value that is the same as V except that each occurrence\n            //    of the code unit 0x0022 (QUOTATION MARK) in V has been replaced with the six\n            //    code unit sequence \"&quot;\".\n            let mut escaped_v = Vec::with_capacity(v.len());\n            for c in &v {\n                if c == 0x0022 {\n                    escaped_v.extend(utf16!(\"&quot;\"));\n                    continue;\n                }\n                escaped_v.push(c);\n            }\n\n            // c. Set p1 to the string-concatenation of:\n            //    p1\n            //    the code unit 0x0020 (SPACE)\n            //    attribute\n            //    the code unit 0x003D (EQUALS SIGN)\n            //    the code unit 0x0022 (QUOTATION MARK)\n            //    escapedV\n            //    the code unit 0x0022 (QUOTATION MARK)\n            p1 = js_string!(\n                &p1,\n                js_str!(\" \"),\n                attribute,\n                js_str!(\"=\\\"\"),\n                &JsString::from(&escaped_v[..]),\n                js_str!(\"\\\"\")\n            );\n        }\n\n        // 5. Let p2 be the string-concatenation of p1 and \">\".\n        // 6. Let p3 be the string-concatenation of p2 and S.\n        // 7. Let p4 be the string-concatenation of p3, \"</\", tag, and \">\".\n        let p4 = js_string!(&p1, js_str!(\">\"), &s, js_str!(\"</\"), tag, js_str!(\">\"));\n\n        // 8. Return p4.\n        Ok(p4.into())\n    }\n\n    /// `String.prototype.anchor( name )`\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///  - [MDN documentation][mdn]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-string.prototype.anchor\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/anchor\n    pub(crate) fn anchor(\n        this: &JsValue,\n        args: &[JsValue],\n        context: &mut Context,\n    ) -> JsResult<JsValue> {\n        let name = args.get_or_undefined(0);\n\n        // 1. Let S be the this value.\n        let s = this;\n        // 2. Return ? CreateHTML(S, \"a\", \"name\", name).\n        Self::create_html(s, js_str!(\"a\"), Some((js_str!(\"name\"), name)), context)\n    }\n\n    /// `String.prototype.big( )`\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///  - [MDN documentation][mdn]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-string.prototype.big\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/big\n    pub(crate) fn big(this: &JsValue, _: &[JsValue], context: &mut Context) -> JsResult<JsValue> {\n        // 1. Let S be the this value.\n        let s = this;\n        // 2. Return ? CreateHTML(S, \"big\", \"\", \"\").\n        Self::create_html(s, js_str!(\"big\"), None, context)\n    }\n\n    /// `String.prototype.blink( )`\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///  - [MDN documentation][mdn]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-string.prototype.blink\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/blink\n    pub(crate) fn blink(this: &JsValue, _: &[JsValue], context: &mut Context) -> JsResult<JsValue> {\n        // 1. Let S be the this value.\n        let s = this;\n        // 2. Return ? CreateHTML(S, \"blink\", \"\", \"\").\n        Self::create_html(s, js_str!(\"blink\"), None, context)\n    }\n\n    /// `String.prototype.bold( )`\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///  - [MDN documentation][mdn]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-string.prototype.bold\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/bold\n    pub(crate) fn bold(this: &JsValue, _: &[JsValue], context: &mut Context) -> JsResult<JsValue> {\n        // 1. Let S be the this value.\n        let s = this;\n        // 2. Return ? CreateHTML(S, \"b\", \"\", \"\").\n        Self::create_html(s, js_str!(\"b\"), None, context)\n    }\n\n    /// `String.prototype.fixed( )`\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///  - [MDN documentation][mdn]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-string.prototype.fixed\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/fixed\n    pub(crate) fn fixed(this: &JsValue, _: &[JsValue], context: &mut Context) -> JsResult<JsValue> {\n        // 1. Let S be the this value.\n        let s = this;\n        // 2. Return ? CreateHTML(S, \"big\", \"\", \"\").\n        Self::create_html(s, js_str!(\"tt\"), None, context)\n    }\n\n    /// `String.prototype.fontcolor( color )`\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///  - [MDN documentation][mdn]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-string.prototype.fontcolor\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/fontcolor\n    pub(crate) fn fontcolor(\n        this: &JsValue,\n        args: &[JsValue],\n        context: &mut Context,\n    ) -> JsResult<JsValue> {\n        let color = args.get_or_undefined(0);\n\n        // 1. Let S be the this value.\n        let s = this;\n        // 2. Return ? CreateHTML(S, \"font\", \"color\", color).\n        Self::create_html(s, js_str!(\"font\"), Some((js_str!(\"color\"), color)), context)\n    }\n\n    /// `String.prototype.fontsize( size )`\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///  - [MDN documentation][mdn]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-string.prototype.fontsize\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/fontsize\n    pub(crate) fn fontsize(\n        this: &JsValue,\n        args: &[JsValue],\n        context: &mut Context,\n    ) -> JsResult<JsValue> {\n        let size = args.get_or_undefined(0);\n\n        // 1. Let S be the this value.\n        let s = this;\n        // 2. Return ? CreateHTML(S, \"font\", \"size\", size).\n        Self::create_html(s, js_str!(\"font\"), Some((js_str!(\"size\"), size)), context)\n    }\n\n    /// `String.prototype.italics( )`\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///  - [MDN documentation][mdn]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-string.prototype.italics\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/italics\n    pub(crate) fn italics(\n        this: &JsValue,\n        _: &[JsValue],\n        context: &mut Context,\n    ) -> JsResult<JsValue> {\n        // 1. Let S be the this value.\n        let s = this;\n        // 2. Return ? CreateHTML(S, \"i\", \"\", \"\").\n        Self::create_html(s, js_str!(\"i\"), None, context)\n    }\n\n    /// `String.prototype.link( url )`\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///  - [MDN documentation][mdn]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-string.prototype.link\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/link\n    pub(crate) fn link(\n        this: &JsValue,\n        args: &[JsValue],\n        context: &mut Context,\n    ) -> JsResult<JsValue> {\n        let url = args.get_or_undefined(0);\n\n        // 1. Let S be the this value.\n        let s = this;\n        // 2. Return ? CreateHTML(S, \"a\", \"href\", url).\n        Self::create_html(s, js_str!(\"a\"), Some((js_str!(\"href\"), url)), context)\n    }\n\n    /// `String.prototype.small( )`\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///  - [MDN documentation][mdn]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-string.prototype.small\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/small\n    pub(crate) fn small(this: &JsValue, _: &[JsValue], context: &mut Context) -> JsResult<JsValue> {\n        // 1. Let S be the this value.\n        let s = this;\n        // 2. Return ? CreateHTML(S, \"small\", \"\", \"\").\n        Self::create_html(s, js_str!(\"small\"), None, context)\n    }\n\n    /// `String.prototype.strike( )`\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///  - [MDN documentation][mdn]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-string.prototype.strike\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/strike\n    pub(crate) fn strike(\n        this: &JsValue,\n        _: &[JsValue],\n        context: &mut Context,\n    ) -> JsResult<JsValue> {\n        // 1. Let S be the this value.\n        let s = this;\n        // 2. Return ? CreateHTML(S, \"strike\", \"\", \"\").\n        Self::create_html(s, js_str!(\"strike\"), None, context)\n    }\n\n    /// `String.prototype.sub( )`\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///  - [MDN documentation][mdn]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-string.prototype.sub\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/sub\n    pub(crate) fn sub(this: &JsValue, _: &[JsValue], context: &mut Context) -> JsResult<JsValue> {\n        // 1. Let S be the this value.\n        let s = this;\n        // 2. Return ? CreateHTML(S, \"sub\", \"\", \"\").\n        Self::create_html(s, js_str!(\"sub\"), None, context)\n    }\n\n    /// `String.prototype.sup( )`\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///  - [MDN documentation][mdn]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-string.prototype.sup\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/sup\n    pub(crate) fn sup(this: &JsValue, _: &[JsValue], context: &mut Context) -> JsResult<JsValue> {\n        // 1. Let S be the this value.\n        let s = this;\n        // 2. Return ? CreateHTML(S, \"sup\", \"\", \"\").\n        Self::create_html(s, js_str!(\"sup\"), None, context)\n    }\n}\n\n/// Abstract operation `GetSubstitution ( matched, str, position, captures, namedCaptures, replacement )`\n///\n/// More information:\n///  - [ECMAScript reference][spec]\n///\n/// [spec]: https://tc39.es/ecma262/#sec-getsubstitution\npub(crate) fn get_substitution(\n    matched: &JsString,\n    str: &JsString,\n    position: usize,\n    captures: &[JsValue],\n    named_captures: &JsValue,\n    replacement: &JsString,\n    context: &mut Context,\n) -> JsResult<JsString> {\n    let mut buf = [0; 2];\n    // 1. Assert: Type(matched) is String.\n\n    // 2. Let matchLength be the number of code units in matched.\n    let match_length = matched.len();\n\n    // 3. Assert: Type(str) is String.\n\n    // 4. Let stringLength be the number of code units in str.\n    let str_length = str.len();\n\n    // 5. Assert: position ≤ stringLength.\n    // 6. Assert: captures is a possibly empty List of Strings.\n    // 7. Assert: Type(replacement) is String.\n\n    // 8. Let tailPos be position + matchLength.\n    let tail_pos = position + match_length;\n\n    // 10. Let result be the String value derived from replacement by copying code unit elements\n    //     from replacement to result while performing replacements as specified in Table 58.\n    //     These $ replacements are done left-to-right, and, once such a replacement is performed,\n    //     the new replacement text is not subject to further replacements.\n    let mut result = vec![];\n    let mut chars = replacement.code_points().peekable();\n\n    while let Some(first) = chars.next() {\n        if first == CodePoint::Unicode('$') {\n            let second = chars.next();\n            let second_is_digit = second\n                .and_then(CodePoint::as_char)\n                .as_ref()\n                .is_some_and(char::is_ascii_digit);\n\n            match second {\n                // $$\n                Some(CodePoint::Unicode('$')) => {\n                    // $\n                    result.push('$' as u16);\n                }\n                // $&\n                Some(CodePoint::Unicode('&')) => {\n                    // matched\n                    result.extend(matched.iter());\n                }\n                // $`\n                Some(CodePoint::Unicode('`')) => {\n                    // The replacement is the substring of str from 0 to position.\n                    result.extend(str.get_expect(..position).iter());\n                }\n                // $'\n                Some(CodePoint::Unicode('\\'')) => {\n                    // If tailPos ≥ stringLength, the replacement is the empty String.\n                    // Otherwise the replacement is the substring of str from tailPos.\n                    if tail_pos < str_length {\n                        result.extend(str.get_expect(tail_pos..).iter());\n                    }\n                }\n                // $nn\n                // f. Else if templateRemainder starts with \"$\" followed by 1 or more decimal digits, then\n                Some(CodePoint::Unicode(second)) if second_is_digit => {\n                    // i. If templateRemainder starts with \"$\" followed by 2 or more decimal digits, let digitCount be 2. Otherwise, let digitCount be 1.\n                    // ii. Let ref be the substring of templateRemainder from 0 to 1 + digitCount.\n                    // iii. Let digits be the substring of templateRemainder from 1 to 1 + digitCount.\n                    // iv. Let index be ℝ(StringToNumber(digits)).\n                    let mut index = second\n                        .to_digit(10)\n                        .expect(\"could not convert character to digit after checking it\")\n                        as usize;\n\n                    // vi. Let captureLen be the number of elements in captures.\n                    let capture_len = captures.len();\n\n                    // NOTE(HalidOdat): We deviate from the spec, because of a bug in GetSubstitutions\n                    //\n                    // See: https://github.com/tc39/ecma262/issues/1426\n                    if let Some(digit) = chars\n                        .peek()\n                        .copied()\n                        .and_then(CodePoint::as_char)\n                        .and_then(|n| n.to_digit(10))\n                    {\n                        // If there is two digits, and it's not in range fallback to one digit.\n                        let two_digit_index = index * 10 + digit as usize;\n                        if (1..=capture_len).contains(&two_digit_index) {\n                            index = two_digit_index;\n                            chars.next();\n                        }\n                    }\n\n                    // v. Assert: 0 ≤ index ≤ 99.\n                    debug_assert!((0..=99).contains(&index));\n\n                    // vii. If 1 ≤ index ≤ captureLen, then\n                    if (1..=capture_len).contains(&index) {\n                        // 1. Let capture be captures[index - 1].\n                        // 2. If capture is undefined, then\n                        //     a. Let refReplacement be the empty String.\n                        // 3. Else,\n                        //     a. Let refReplacement be capture.\n                        if let Some(capture) = captures.get(index - 1)\n                            && let Some(s) = capture.as_string()\n                        {\n                            result.extend(s.iter());\n                        }\n\n                    // viii. Else,\n                    } else {\n                        // 1. Let refReplacement be ref.\n                        result.extend_from_slice(&['$' as u16, second as u16]);\n                    }\n                }\n                // $<\n                Some(CodePoint::Unicode('<')) => {\n                    // 1. If namedCaptures is undefined, the replacement text is the String \"$<\".\n                    // 2. Else,\n                    if named_captures.is_undefined() {\n                        result.extend_from_slice(utf16!(\"$<\"));\n                    } else {\n                        // a. Assert: Type(namedCaptures) is Object.\n                        let named_captures = named_captures\n                            .as_object()\n                            .js_expect(\"should be an object according to spec\")?;\n\n                        // b. Scan until the next > U+003E (GREATER-THAN SIGN).\n                        let mut group_name = vec![];\n                        let mut found = false;\n                        loop {\n                            match chars.next() {\n                                Some(CodePoint::Unicode('>')) => {\n                                    found = true;\n                                    break;\n                                }\n                                Some(c) => group_name.extend_from_slice(c.encode_utf16(&mut buf)),\n                                None => break,\n                            }\n                        }\n\n                        // c. If none is found, the replacement text is the String \"$<\".\n                        #[allow(clippy::if_not_else)]\n                        if !found {\n                            result.extend_from_slice(utf16!(\"$<\"));\n                            result.extend_from_slice(&group_name);\n                        // d. Else,\n                        } else {\n                            // i. Let groupName be the enclosed substring.\n                            let group_name = js_string!(&group_name[..]);\n                            // ii. Let capture be ? Get(namedCaptures, groupName).\n                            let capture = named_captures.get(group_name, context)?;\n\n                            // iii. If capture is undefined, replace the text through > with the empty String.\n                            // iv. Otherwise, replace the text through > with ? ToString(capture).\n                            if !capture.is_undefined() {\n                                result.extend(capture.to_string(context)?.iter());\n                            }\n                        }\n                    }\n                }\n                // $?, ? is none of the above\n                _ => {\n                    result.push('$' as u16);\n                    if let Some(second) = second {\n                        result.extend_from_slice(second.encode_utf16(&mut buf));\n                    }\n                }\n            }\n        } else {\n            result.extend_from_slice(first.encode_utf16(&mut buf));\n        }\n    }\n\n    // 11. Return result.\n    Ok(js_string!(&result[..]))\n}\n"
  },
  {
    "path": "core/engine/src/builtins/string/string_iterator.rs",
    "content": "//! This module implements the `StringIterator` object.\n//!\n//! More information:\n//!  - [ECMAScript reference][spec]\n//!\n//! [spec]: https://tc39.es/ecma262/#sec-string-iterator-objects\n\nuse crate::{\n    Context, JsData, JsResult, JsString, JsValue,\n    builtins::{BuiltInBuilder, IntrinsicObject, iterable::create_iter_result_object},\n    context::intrinsics::Intrinsics,\n    error::JsNativeError,\n    js_string,\n    object::JsObject,\n    property::Attribute,\n    realm::Realm,\n    symbol::JsSymbol,\n};\nuse boa_gc::{Finalize, Trace};\n\n/// The `StringIterator` object represents an iteration over a string. It implements the iterator protocol.\n///\n/// More information:\n///  - [ECMAScript reference][spec]\n///\n/// [spec]: https://tc39.es/ecma262/#sec-string-iterator-objects\n#[derive(Debug, Clone, Finalize, Trace, JsData)]\npub(crate) struct StringIterator {\n    string: JsString,\n    next_index: usize,\n}\n\nimpl IntrinsicObject for StringIterator {\n    fn init(realm: &Realm) {\n        BuiltInBuilder::with_intrinsic::<Self>(realm)\n            .prototype(\n                realm\n                    .intrinsics()\n                    .objects()\n                    .iterator_prototypes()\n                    .iterator(),\n            )\n            .static_method(Self::next, js_string!(\"next\"), 0)\n            .static_property(\n                JsSymbol::to_string_tag(),\n                js_string!(\"String Iterator\"),\n                Attribute::CONFIGURABLE,\n            )\n            .build();\n    }\n\n    fn get(intrinsics: &Intrinsics) -> JsObject {\n        intrinsics.objects().iterator_prototypes().string()\n    }\n}\n\nimpl StringIterator {\n    /// Create a new `StringIterator`.\n    pub(crate) fn create_string_iterator(string: JsString, context: &mut Context) -> JsObject {\n        JsObject::from_proto_and_data_with_shared_shape(\n            context.root_shape(),\n            context\n                .intrinsics()\n                .objects()\n                .iterator_prototypes()\n                .string(),\n            Self {\n                string,\n                next_index: 0,\n            },\n        )\n        .upcast()\n    }\n\n    /// `StringIterator.prototype.next( )`\n    pub(crate) fn next(this: &JsValue, _: &[JsValue], context: &mut Context) -> JsResult<JsValue> {\n        let object = this.as_object();\n        let mut string_iterator = object\n            .as_ref()\n            .and_then(JsObject::downcast_mut::<Self>)\n            .ok_or_else(|| JsNativeError::typ().with_message(\"`this` is not an ArrayIterator\"))?;\n\n        if string_iterator.string.is_empty() {\n            return Ok(create_iter_result_object(\n                JsValue::undefined(),\n                true,\n                context,\n            ));\n        }\n        let native_string = &string_iterator.string;\n        let len = native_string.len();\n        let position = string_iterator.next_index;\n        if position >= len {\n            string_iterator.string = js_string!();\n            return Ok(create_iter_result_object(\n                JsValue::undefined(),\n                true,\n                context,\n            ));\n        }\n        let code_point = native_string.code_point_at(position);\n        string_iterator.next_index += code_point.code_unit_count();\n        let result_string = crate::builtins::string::String::substring(\n            &string_iterator.string.clone().into(),\n            &[position.into(), string_iterator.next_index.into()],\n            context,\n        )?;\n        Ok(create_iter_result_object(result_string, false, context))\n    }\n}\n"
  },
  {
    "path": "core/engine/src/builtins/string/tests.rs",
    "content": "use boa_macros::js_str;\nuse indoc::indoc;\n\nuse crate::{JsNativeErrorKind, JsValue, TestAction, js_string, run_test_actions};\n\n#[test]\nfn length() {\n    //TEST262: https://github.com/tc39/test262/blob/master/test/built-ins/String/length.js\n    run_test_actions([\n        TestAction::run(indoc! {r\"\n                const a = new String(' ');\n                const b = new String('\\ud834\\udf06');\n                const c = new String(' \\b ');\n                const d = new String('中文长度')\n            \"}),\n        // unicode surrogate pair length should be 1\n        // utf16/usc2 length should be 2\n        // utf8 length should be 4\n        TestAction::assert_eq(\"a.length\", 1),\n        TestAction::assert_eq(\"b.length\", 2),\n        TestAction::assert_eq(\"c.length\", 3),\n        TestAction::assert_eq(\"d.length\", 4),\n    ]);\n}\n\n#[test]\nfn new_string_has_length() {\n    run_test_actions([\n        TestAction::run(\"let a = new String(\\\"1234\\\");\"),\n        TestAction::assert_eq(\"a.length\", 4),\n    ]);\n}\n\n#[test]\nfn new_string_has_length_not_enumerable() {\n    run_test_actions([\n        TestAction::run(\"let a = new String(\\\"1234\\\");\"),\n        TestAction::assert(\"!a.propertyIsEnumerable('length')\"),\n    ]);\n}\n\n#[test]\nfn new_utf8_string_has_length() {\n    run_test_actions([\n        TestAction::run(\"let a = new String(\\\"中文\\\");\"),\n        TestAction::assert_eq(\"a.length\", 2),\n    ]);\n}\n\n#[test]\nfn concat() {\n    run_test_actions([\n        TestAction::run(indoc! {r#\"\n                var hello = new String('Hello, ');\n                var world = new String('world! ');\n                var nice = new String('Have a nice day.');\n            \"#}),\n        TestAction::assert_eq(\n            \"hello.concat(world, nice)\",\n            js_str!(\"Hello, world! Have a nice day.\"),\n        ),\n        TestAction::assert_eq(\n            \"hello + world + nice\",\n            js_str!(\"Hello, world! Have a nice day.\"),\n        ),\n    ]);\n}\n\n#[test]\nfn generic_concat() {\n    run_test_actions([\n        TestAction::run(indoc! {r#\"\n                Number.prototype.concat = String.prototype.concat;\n                let number = new Number(100);\n            \"#}),\n        TestAction::assert_eq(\"number.concat(' - 50', ' = 50')\", js_str!(\"100 - 50 = 50\")),\n    ]);\n}\n\n#[test]\n/// Test the correct type is returned from call and construct\nfn construct_and_call() {\n    run_test_actions([\n        TestAction::assert_with_op(\"new String('Hello')\", |v, _| v.is_object()),\n        TestAction::assert_with_op(\"String('world')\", |v, _| v.is_string()),\n    ]);\n}\n\n#[test]\nfn repeat() {\n    run_test_actions([\n        TestAction::run(indoc! {r#\"\n            var empty = new String('');\n            var en = new String('english');\n            var zh = new String('中文');\n        \"#}),\n        TestAction::assert_eq(\"empty.repeat(0)\", js_string!()),\n        TestAction::assert_eq(\"empty.repeat(1)\", js_string!()),\n        TestAction::assert_eq(\"en.repeat(0)\", js_string!()),\n        TestAction::assert_eq(\"zh.repeat(0)\", js_string!()),\n        TestAction::assert_eq(\"en.repeat(1)\", js_str!(\"english\")),\n        TestAction::assert_eq(\"zh.repeat(2)\", js_str!(\"中文中文\")),\n    ]);\n}\n\n#[test]\nfn repeat_respects_loop_limit() {\n    run_test_actions([\n        // Set a small loop iteration limit so long repeats trip it.\n        TestAction::inspect_context(|context| {\n            context.runtime_limits_mut().set_loop_iteration_limit(10);\n        }),\n        // Plain JS loop should hit the runtime limit.\n        TestAction::assert_runtime_limit_error(\n            \"for (let i = 0; i < 100; ++i) {}\",\n            crate::error::RuntimeLimitError::LoopIteration,\n        ),\n        // String.prototype.repeat should use the same mechanism and error.\n        TestAction::assert_runtime_limit_error(\n            \"'x'.repeat(100)\",\n            crate::error::RuntimeLimitError::LoopIteration,\n        ),\n    ]);\n}\n\n#[test]\nfn repeat_large_count_hits_limit() {\n    run_test_actions([\n        TestAction::inspect_context(|context| {\n            context.runtime_limits_mut().set_loop_iteration_limit(50);\n        }),\n        // Large repeat count with a small loop limit should raise the same runtime limit error.\n        TestAction::assert_runtime_limit_error(\n            \"'a'.repeat(10_000)\",\n            crate::error::RuntimeLimitError::LoopIteration,\n        ),\n    ]);\n}\n\n#[test]\nfn repeat_throws_when_count_is_negative() {\n    run_test_actions([TestAction::assert_native_error(\n        \"'x'.repeat(-1)\",\n        JsNativeErrorKind::Range,\n        \"String.prototype.repeat: count must be non-negative\",\n    )]);\n}\n\n#[test]\nfn repeat_throws_when_count_is_infinity() {\n    run_test_actions([TestAction::assert_native_error(\n        \"'x'.repeat(Infinity)\",\n        JsNativeErrorKind::Range,\n        \"String.prototype.repeat: count must be less than infinity\",\n    )]);\n}\n\n#[test]\nfn repeat_throws_when_count_overflows_max_length() {\n    run_test_actions([TestAction::assert_native_error(\n        \"'x'.repeat(2 ** 64)\",\n        JsNativeErrorKind::Range,\n        \"repeat count must be a positive finite number \\\n                  that doesn't overflow the maximum string length (2^32 - 1)\",\n    )]);\n}\n\n#[test]\nfn repeat_generic() {\n    run_test_actions([\n        TestAction::run(\"Number.prototype.repeat = String.prototype.repeat;\"),\n        TestAction::assert_eq(\"(0).repeat(0)\", js_string!()),\n        TestAction::assert_eq(\"(1).repeat(1)\", js_str!(\"1\")),\n        TestAction::assert_eq(\"(1).repeat(5)\", js_str!(\"11111\")),\n        TestAction::assert_eq(\"(12).repeat(3)\", js_str!(\"121212\")),\n    ]);\n}\n\n#[test]\nfn replace() {\n    run_test_actions([TestAction::assert_eq(\n        indoc! {r#\"\n            \"abc\".replace(\"a\", \"2\")\n        \"#},\n        js_str!(\"2bc\"),\n    )]);\n}\n\n#[test]\nfn replace_no_match() {\n    run_test_actions([TestAction::assert_eq(\n        indoc! {r#\"\n            \"abc\".replace(/d/, \"$&$&\")\n        \"#},\n        js_str!(\"abc\"),\n    )]);\n}\n\n#[test]\nfn replace_with_capture_groups() {\n    run_test_actions([TestAction::assert_eq(\n        indoc! {r#\"\n            \"John Smith\".replace(/(\\w+)\\s(\\w+)/, '$2, $1')\n        \"#},\n        js_str!(\"Smith, John\"),\n    )]);\n}\n\n#[test]\nfn replace_with_tenth_capture_group() {\n    run_test_actions([TestAction::assert_eq(\n        indoc! {r#\"\n            var re = /(\\d)(\\d)(\\d)(\\d)(\\d)(\\d)(\\d)(\\d)(\\d)(\\d)/;\n            \"0123456789\".replace(re, '$10')\n        \"#},\n        js_str!(\"9\"),\n    )]);\n}\n\n#[test]\nfn replace_substitutions() {\n    run_test_actions([\n        TestAction::run(indoc! {r#\"\n            var re = / two /;\n            var a = \"one two three\";\n            var dollar = a.replace(re, \" $$ \");\n            var matched = a.replace(re, \"$&$&\");\n            var start = a.replace(re, \" $` \");\n            var end = a.replace(re, \" $' \");\n            var no_sub = a.replace(re, \" $_ \");\n        \"#}),\n        TestAction::assert_eq(\"a.replace(re, \\\" $$ \\\")\", js_str!(\"one $ three\")),\n        TestAction::assert_eq(\"a.replace(re, \\\"$&$&\\\")\", js_str!(\"one two  two three\")),\n        TestAction::assert_eq(\"a.replace(re, \\\" $` \\\")\", js_str!(\"one one three\")),\n        TestAction::assert_eq(\"a.replace(re, \\\" $' \\\")\", js_str!(\"one three three\")),\n        TestAction::assert_eq(\"a.replace(re, \\\" $_ \\\")\", js_str!(\"one $_ three\")),\n    ]);\n}\n\n#[test]\nfn replace_with_function() {\n    run_test_actions([\n        TestAction::run(indoc! {r#\"\n                var p1, p2, p3, length;\n                var replacer = (match, cap1, cap2, cap3, len) => {\n                    p1 = cap1;\n                    p2 = cap2;\n                    p3 = cap3;\n                    length = len;\n                    return \"awesome!\";\n                };\n            \"#}),\n        TestAction::assert_eq(\n            \"\\\"ecmascript is cool\\\".replace(/c(o)(o)(l)/, replacer)\",\n            js_str!(\"ecmascript is awesome!\"),\n        ),\n        TestAction::assert_eq(\"p1\", js_str!(\"o\")),\n        TestAction::assert_eq(\"p2\", js_str!(\"o\")),\n        TestAction::assert_eq(\"p3\", js_str!(\"l\")),\n        TestAction::assert_eq(\"length\", 14),\n    ]);\n}\n\n#[test]\nfn starts_with() {\n    run_test_actions([\n        TestAction::run(indoc! {r#\"\n                var empty = '';\n                var en = 'english';\n                var zh = '中文';\n            \"#}),\n        TestAction::assert(\"empty.startsWith('')\"),\n        TestAction::assert(\"en.startsWith('e')\"),\n        TestAction::assert(\"zh.startsWith('中')\"),\n        TestAction::assert(\"(new String(empty)).startsWith('')\"),\n        TestAction::assert(\"(new String(en)).startsWith('e')\"),\n        TestAction::assert(\"(new String(zh)).startsWith('中')\"),\n    ]);\n}\n\n#[test]\nfn starts_with_with_regex_arg() {\n    run_test_actions([TestAction::assert_native_error(\n        \"'Saturday night'.startsWith(/Saturday/)\",\n        JsNativeErrorKind::Type,\n        \"First argument to String.prototype.startsWith must not be a regular expression\",\n    )]);\n}\n\n#[test]\nfn ends_with() {\n    run_test_actions([\n        TestAction::run(indoc! {r#\"\n                var empty = '';\n                var en = 'english';\n                var zh = '中文';\n            \"#}),\n        TestAction::assert(\"empty.endsWith('')\"),\n        TestAction::assert(\"en.endsWith('h')\"),\n        TestAction::assert(\"zh.endsWith('文')\"),\n        TestAction::assert(\"(new String(empty)).endsWith('')\"),\n        TestAction::assert(\"(new String(en)).endsWith('h')\"),\n        TestAction::assert(\"(new String(zh)).endsWith('文')\"),\n    ]);\n}\n\n#[test]\nfn ends_with_with_regex_arg() {\n    run_test_actions([TestAction::assert_native_error(\n        \"'Saturday night'.endsWith(/night/)\",\n        JsNativeErrorKind::Type,\n        \"First argument to String.prototype.endsWith must not be a regular expression\",\n    )]);\n}\n\n#[test]\nfn includes() {\n    run_test_actions([\n        TestAction::run(indoc! {r#\"\n                var empty = '';\n                var en = 'english';\n                var zh = '中文';\n            \"#}),\n        TestAction::assert(\"empty.includes('')\"),\n        TestAction::assert(\"en.includes('g')\"),\n        TestAction::assert(\"zh.includes('文')\"),\n        TestAction::assert(\"(new String(empty)).includes('')\"),\n        TestAction::assert(\"(new String(en)).includes('g')\"),\n        TestAction::assert(\"(new String(zh)).includes('文')\"),\n    ]);\n}\n\n#[test]\nfn includes_with_regex_arg() {\n    run_test_actions([TestAction::assert_native_error(\n        \"'Saturday night'.includes(/day/)\",\n        JsNativeErrorKind::Type,\n        \"First argument to String.prototype.includes must not be a regular expression\",\n    )]);\n}\n\n#[test]\nfn match_all_one() {\n    run_test_actions([\n        TestAction::run_harness(),\n        TestAction::run(indoc! {r\"\n            var groupMatches = 'test1test2'.matchAll(/t(e)(st(\\d?))/g);\n            var m1 = groupMatches.next();\n            var m2 = groupMatches.next();\n            var m3 = groupMatches.next();\n        \"}),\n        TestAction::assert(\"!m1.done\"),\n        TestAction::assert(\"!m2.done\"),\n        TestAction::assert(\"m3.done\"),\n        TestAction::assert(indoc! {r#\"\n            arrayEquals(\n                m1.value,\n                [\"test1\", \"e\", \"st1\", \"1\"]\n            )\n        \"#}),\n        TestAction::assert_eq(\"m1.value.index\", 0),\n        TestAction::assert_eq(\"m1.value.input\", js_str!(\"test1test2\")),\n        TestAction::assert_eq(\"m1.value.groups\", JsValue::undefined()),\n        TestAction::assert(indoc! {r#\"\n            arrayEquals(\n                m2.value,\n                [\"test2\", \"e\", \"st2\", \"2\"]\n            )\n        \"#}),\n        TestAction::assert_eq(\"m2.value.index\", 5),\n        TestAction::assert_eq(\"m2.value.input\", js_str!(\"test1test2\")),\n        TestAction::assert_eq(\"m2.value.groups\", JsValue::undefined()),\n        TestAction::assert_eq(\"m3.value\", JsValue::undefined()),\n    ]);\n}\n\n#[test]\nfn match_all_two() {\n    run_test_actions([\n        TestAction::run_harness(),\n        TestAction::run(indoc! {r#\"\n            var regexp = RegExp('foo[a-z]*','g');\n            var str = 'table football, foosball';\n            var matches = str.matchAll(regexp);\n            var m1 = matches.next();\n            var m2 = matches.next();\n            var m3 = matches.next();\n        \"#}),\n        TestAction::assert(\"!m1.done\"),\n        TestAction::assert(\"!m2.done\"),\n        TestAction::assert(\"m3.done\"),\n        TestAction::assert(indoc! {r#\"\n            arrayEquals(\n                m1.value,\n                [\"football\"]\n            )\n        \"#}),\n        TestAction::assert_eq(\"m1.value.index\", 6),\n        TestAction::assert_eq(\"m1.value.input\", js_str!(\"table football, foosball\")),\n        TestAction::assert_eq(\"m1.value.groups\", JsValue::undefined()),\n        TestAction::assert(indoc! {r#\"\n            arrayEquals(\n                m2.value,\n                [\"foosball\"]\n            )\n        \"#}),\n        TestAction::assert_eq(\"m2.value.index\", 16),\n        TestAction::assert_eq(\"m2.value.input\", js_str!(\"table football, foosball\")),\n        TestAction::assert_eq(\"m2.value.groups\", JsValue::undefined()),\n        TestAction::assert_eq(\"m3.value\", JsValue::undefined()),\n    ]);\n}\n\n#[test]\nfn test_match() {\n    run_test_actions([\n        TestAction::run_harness(),\n        TestAction::run(indoc! {r#\"\n                var str = new String('The Quick Brown Fox Jumps Over The Lazy Dog');\n                var result1 = str.match(/quick\\s(brown).+?(jumps)/i);\n                var result2 = str.match(/[A-Z]/g);\n                var result3 = str.match(\"T\");\n                var result4 = str.match(RegExp(\"B\", 'g'));\n            \"#}),\n        TestAction::assert(indoc! {r#\"\n                arrayEquals(\n                    result1,\n                    [\"Quick Brown Fox Jumps\", \"Brown\", \"Jumps\"]\n                )\n            \"#}),\n        TestAction::assert_eq(\"result1.index\", 4),\n        TestAction::assert_eq(\n            \"result1.input\",\n            js_str!(\"The Quick Brown Fox Jumps Over The Lazy Dog\"),\n        ),\n        TestAction::assert(indoc! {r#\"\n                arrayEquals(\n                    result2,\n                    [\"T\", \"Q\", \"B\", \"F\", \"J\", \"O\", \"T\", \"L\", \"D\"]\n                )\n            \"#}),\n        TestAction::assert(indoc! {r#\"\n                arrayEquals(\n                    result3,\n                    [\"T\"]\n                )\n            \"#}),\n        TestAction::assert_eq(\"result3.index\", 0),\n        TestAction::assert_eq(\n            \"result3.input\",\n            js_str!(\"The Quick Brown Fox Jumps Over The Lazy Dog\"),\n        ),\n        TestAction::assert(indoc! {r#\"\n                arrayEquals(\n                    result4,\n                    [\"B\"]\n                )\n            \"#}),\n    ]);\n}\n\n#[test]\nfn trim() {\n    run_test_actions([\n        TestAction::assert_eq(r\"'Hello'.trim()\", js_str!(\"Hello\")),\n        TestAction::assert_eq(r\"' \\nHello'.trim()\", js_str!(\"Hello\")),\n        TestAction::assert_eq(r\"'Hello \\n\\r'.trim()\", js_str!(\"Hello\")),\n        TestAction::assert_eq(r\"' Hello '.trim()\", js_str!(\"Hello\")),\n    ]);\n}\n\n#[test]\nfn trim_start() {\n    run_test_actions([\n        TestAction::assert_eq(r\"'Hello'.trimStart()\", js_str!(\"Hello\")),\n        TestAction::assert_eq(r\"' \\nHello'.trimStart()\", js_str!(\"Hello\")),\n        TestAction::assert_eq(r\"'Hello \\n\\r'.trimStart()\", js_str!(\"Hello \\n\\r\")),\n        TestAction::assert_eq(r\"' Hello '.trimStart()\", js_str!(\"Hello \")),\n    ]);\n}\n\n#[test]\nfn trim_end() {\n    run_test_actions([\n        TestAction::assert_eq(r\"'Hello'.trimEnd()\", js_str!(\"Hello\")),\n        TestAction::assert_eq(r\"' \\nHello'.trimEnd()\", js_str!(\" \\nHello\")),\n        TestAction::assert_eq(r\"'Hello \\n\\r'.trimEnd()\", js_str!(\"Hello\")),\n        TestAction::assert_eq(r\"' Hello '.trimEnd()\", js_str!(\" Hello\")),\n    ]);\n}\n\n#[test]\nfn split() {\n    run_test_actions([\n        TestAction::run_harness(),\n        TestAction::assert(indoc! {r#\"\n                arrayEquals(\n                    \"Hello\".split(),\n                    [\"Hello\"]\n                )\n            \"#}),\n        TestAction::assert(indoc! {r#\"\n                arrayEquals(\n                    'Hello'.split(null),\n                    ['Hello']\n                )\n            \"#}),\n        TestAction::assert(indoc! {r#\"\n                arrayEquals(\n                    'Hello'.split(undefined),\n                    ['Hello']\n                )\n            \"#}),\n        TestAction::assert(indoc! {r#\"\n                arrayEquals(\n                    'Hello'.split(''),\n                    ['H','e','l','l','o']\n                )\n            \"#}),\n        TestAction::assert(indoc! {r#\"\n                arrayEquals(\n                    'x1x2'.split('x'),\n                    ['','1','2']\n                )\n            \"#}),\n        TestAction::assert(indoc! {r#\"\n                arrayEquals(\n                    'x1x2x'.split('x'),\n                    ['','1','2','']\n                )\n            \"#}),\n        TestAction::assert(indoc! {r#\"\n                arrayEquals(\n                    'x1x2x'.split('x', 0),\n                    []\n                )\n            \"#}),\n        TestAction::assert(indoc! {r#\"\n                arrayEquals(\n                    'x1x2x'.split('x', 2),\n                    ['','1']\n                )\n            \"#}),\n        TestAction::assert(indoc! {r#\"\n                arrayEquals(\n                    'x1x2x'.split('x', 10),\n                    ['','1','2','']\n                )\n            \"#}),\n        TestAction::assert(indoc! {r#\"\n                arrayEquals(\n                    'x1x2x'.split(1),\n                    ['x','x2x']\n                )\n            \"#}),\n        TestAction::assert(indoc! {r#\"\n                arrayEquals(\n                    'Hello'.split(null, 0),\n                    []\n                )\n            \"#}),\n        TestAction::assert(indoc! {r#\"\n                arrayEquals(\n                    'Hello'.split(undefined, 0),\n                    []\n                )\n            \"#}),\n        TestAction::assert(indoc! {r#\"\n                arrayEquals(\n                    ''.split(),\n                    ['']\n                )\n            \"#}),\n        TestAction::assert(indoc! {r#\"\n                arrayEquals(\n                    ''.split(undefined),\n                    ['']\n                )\n            \"#}),\n        TestAction::assert(indoc! {r#\"\n                arrayEquals(\n                    ''.split(''),\n                    []\n                )\n            \"#}),\n        TestAction::assert(indoc! {r#\"\n                arrayEquals(\n                    ''.split('1'),\n                    ['']\n                )\n            \"#}),\n        TestAction::assert(indoc! {r\"\n                arrayEquals(\n                    '\\u{1D7D8}\\u{1D7D9}\\u{1D7DA}\\u{1D7DB}'.split(''),\n                    ['\\uD835', '\\uDFD8', '\\uD835', '\\uDFD9', '\\uD835', '\\uDFDA', '\\uD835', '\\uDFDB']\n                )\n            \"}),\n    ]);\n}\n\n#[test]\nfn split_with_symbol_split_method() {\n    run_test_actions([\n        TestAction::run_harness(),\n        TestAction::assert_eq(\n            indoc! {r#\"\n                let sep_a = {};\n                sep_a[Symbol.split] = function(s, limit) { return s + limit.toString(); };\n                'hello'.split(sep_a, 10)\n            \"#},\n            js_str!(\"hello10\"),\n        ),\n        TestAction::assert(indoc! {r#\"\n                let sep_b = {};\n                sep_b[Symbol.split] = undefined;\n                arrayEquals(\n                    'hello'.split(sep_b),\n                    ['hello']\n                )\n            \"#}),\n        TestAction::assert_native_error(\n            indoc! {r#\"\n                let sep_c = {};\n                sep_c[Symbol.split] = 10;\n                'hello'.split(sep_c, 10);\n            \"#},\n            JsNativeErrorKind::Type,\n            \"value returned for property of object is not a function\",\n        ),\n    ]);\n}\n\n#[test]\nfn index_of_with_no_arguments() {\n    run_test_actions([\n        TestAction::assert_eq(\"''.indexOf()\", -1),\n        TestAction::assert_eq(\"'undefined'.indexOf()\", 0),\n        TestAction::assert_eq(\"'a1undefined'.indexOf()\", 2),\n        TestAction::assert_eq(\"'a1undefined1a'.indexOf()\", 2),\n        TestAction::assert_eq(\"'µµµundefined'.indexOf()\", 3),\n        TestAction::assert_eq(\"'µµµundefinedµµµ'.indexOf()\", 3),\n    ]);\n}\n\n#[test]\nfn index_of_with_string_search_string_argument() {\n    run_test_actions([\n        TestAction::assert_eq(\"''.indexOf('undefined')\", -1),\n        TestAction::assert_eq(\"'undefined'.indexOf('undefined')\", 0),\n        TestAction::assert_eq(\"'a1undefined'.indexOf('undefined')\", 2),\n        TestAction::assert_eq(\"'a1undefined1a'.indexOf('undefined')\", 2),\n        TestAction::assert_eq(\"'µµµundefined'.indexOf('undefined')\", 3),\n        TestAction::assert_eq(\"'µµµundefinedµµµ'.indexOf('undefined')\", 3),\n    ]);\n}\n\n#[test]\nfn index_of_with_non_string_search_string_argument() {\n    run_test_actions([\n        TestAction::assert_eq(\"''.indexOf(1)\", -1),\n        TestAction::assert_eq(\"'1'.indexOf(1)\", 0),\n        TestAction::assert_eq(\"'true'.indexOf(true)\", 0),\n        TestAction::assert_eq(\"'ab100ba'.indexOf(100)\", 2),\n        TestAction::assert_eq(\"'µµµfalse'.indexOf(true)\", -1),\n        TestAction::assert_eq(\"'µµµ5µµµ'.indexOf(5)\", 3),\n    ]);\n}\n\n#[test]\nfn index_of_with_from_index_argument() {\n    run_test_actions([\n        TestAction::assert_eq(\"''.indexOf('x', 2)\", -1),\n        TestAction::assert_eq(\"'x'.indexOf('x', 2)\", -1),\n        TestAction::assert_eq(\"'abcx'.indexOf('x', 2)\", 3),\n        TestAction::assert_eq(\"'µµµxµµµ'.indexOf('x', 2)\", 3),\n        TestAction::assert_eq(\"'µµµxµµµ'.indexOf('x', 10000000)\", -1),\n    ]);\n}\n\n#[test]\nfn generic_index_of() {\n    run_test_actions([\n        TestAction::run(\"Number.prototype.indexOf = String.prototype.indexOf\"),\n        TestAction::assert_eq(\"'10'.indexOf(9)\", -1),\n        TestAction::assert_eq(\"'10'.indexOf(0)\", 1),\n        TestAction::assert_eq(\"'10'.indexOf('0')\", 1),\n    ]);\n}\n\n#[test]\nfn index_of_empty_search_string() {\n    run_test_actions([\n        TestAction::assert_eq(\"''.indexOf('')\", 0),\n        TestAction::assert_eq(\"''.indexOf('', 10)\", 0),\n        TestAction::assert_eq(\"'ABC'.indexOf('', 1)\", 1),\n        TestAction::assert_eq(\"'ABC'.indexOf('', 2)\", 2),\n        TestAction::assert_eq(\"'ABC'.indexOf('', 10)\", 3),\n    ]);\n}\n\n#[test]\nfn last_index_of_with_no_arguments() {\n    run_test_actions([\n        TestAction::assert_eq(\"''.lastIndexOf()\", -1),\n        TestAction::assert_eq(\"'undefined'.lastIndexOf()\", 0),\n        TestAction::assert_eq(\"'a1undefined'.lastIndexOf()\", 2),\n        TestAction::assert_eq(\"'a1undefined1aundefined'.lastIndexOf()\", 13),\n        TestAction::assert_eq(\"'µµµundefinedundefined'.lastIndexOf()\", 12),\n        TestAction::assert_eq(\"'µµµundefinedµµµundefined'.lastIndexOf()\", 15),\n    ]);\n}\n\n#[test]\nfn last_index_of_with_string_search_string_argument() {\n    run_test_actions([\n        TestAction::assert_eq(\"''.lastIndexOf('hello')\", -1),\n        TestAction::assert_eq(\"'undefined'.lastIndexOf('undefined')\", 0),\n        TestAction::assert_eq(\"'a1undefined'.lastIndexOf('undefined')\", 2),\n        TestAction::assert_eq(\"'a1undefined1aundefined'.lastIndexOf('undefined')\", 13),\n        TestAction::assert_eq(\"'µµµundefinedundefined'.lastIndexOf('undefined')\", 12),\n        TestAction::assert_eq(\"'µµµundefinedµµµundefined'.lastIndexOf('undefined')\", 15),\n    ]);\n}\n\n#[test]\nfn last_index_of_with_non_string_search_string_argument() {\n    run_test_actions([\n        TestAction::assert_eq(\"''.lastIndexOf(1)\", -1),\n        TestAction::assert_eq(\"'1'.lastIndexOf(1)\", 0),\n        TestAction::assert_eq(\"'11'.lastIndexOf(1)\", 1),\n        TestAction::assert_eq(\"'truefalsetrue'.lastIndexOf(true)\", 9),\n        TestAction::assert_eq(\"'ab100ba'.lastIndexOf(100)\", 2),\n        TestAction::assert_eq(\"'µµµfalse'.lastIndexOf(true)\", -1),\n        TestAction::assert_eq(\"'µµµ5µµµ65µ'.lastIndexOf(5)\", 8),\n    ]);\n}\n\n#[test]\nfn last_index_of_with_from_index_argument() {\n    run_test_actions([\n        TestAction::assert_eq(\"''.lastIndexOf('x', 2)\", -1),\n        TestAction::assert_eq(\"'x'.lastIndexOf('x', 2)\", 0),\n        TestAction::assert_eq(\"'abcxx'.lastIndexOf('x', 2)\", -1),\n        TestAction::assert_eq(\"'µµµxµµµ'.lastIndexOf('x', 2)\", -1),\n        TestAction::assert_eq(\"'µµµxµµµ'.lastIndexOf('x', 10000000)\", 3),\n    ]);\n}\n\n#[test]\nfn last_index_with_empty_search_string() {\n    run_test_actions([\n        TestAction::assert_eq(\"''.lastIndexOf('')\", 0),\n        TestAction::assert_eq(\"'x'.lastIndexOf('', 2)\", 1),\n        TestAction::assert_eq(\"'abcxx'.lastIndexOf('', 4)\", 4),\n        TestAction::assert_eq(\"'µµµxµµµ'.lastIndexOf('', 2)\", 2),\n        TestAction::assert_eq(\"'µµµxµµµ'.lastIndexOf('', 10000000)\", 7),\n    ]);\n}\n\n#[test]\nfn generic_last_index_of() {\n    run_test_actions([\n        TestAction::run(\"Number.prototype.lastIndexOf = String.prototype.lastIndexOf\"),\n        TestAction::assert_eq(\"(1001).lastIndexOf(9)\", -1),\n        TestAction::assert_eq(\"(1001).lastIndexOf(0)\", 2),\n        TestAction::assert_eq(\"(1001).lastIndexOf('0')\", 2),\n    ]);\n}\n\n#[test]\nfn last_index_non_integer_position_argument() {\n    run_test_actions([\n        TestAction::assert_eq(\"''.lastIndexOf('x', new Number(4))\", -1),\n        TestAction::assert_eq(\"'abc'.lastIndexOf('b', new Number(1))\", 1),\n        TestAction::assert_eq(\"'abcx'.lastIndexOf('x', new String('1'))\", -1),\n        TestAction::assert_eq(\"'abcx'.lastIndexOf('x', new String('100'))\", 3),\n        TestAction::assert_eq(\"'abcx'.lastIndexOf('x', null)\", -1),\n    ]);\n}\n\n#[test]\nfn char_at() {\n    run_test_actions([\n        TestAction::assert_eq(\"'abc'.charAt(-1)\", js_string!()),\n        TestAction::assert_eq(\"'abc'.charAt(1)\", js_str!(\"b\")),\n        TestAction::assert_eq(\"'abc'.charAt(9)\", js_string!()),\n        TestAction::assert_eq(\"'abc'.charAt()\", js_str!(\"a\")),\n        TestAction::assert_eq(\"'abc'.charAt(null)\", js_str!(\"a\")),\n        TestAction::assert_eq(r\"'\\uDBFF'.charAt(0)\", js_string!(&[0xDBFFu16])),\n    ]);\n}\n\n#[test]\nfn char_code_at() {\n    run_test_actions([\n        TestAction::assert_eq(\"'abc'.charCodeAt(-1)\", f64::NAN),\n        TestAction::assert_eq(\"'abc'.charCodeAt(1)\", 98),\n        TestAction::assert_eq(\"'abc'.charCodeAt(9)\", f64::NAN),\n        TestAction::assert_eq(\"'abc'.charCodeAt()\", 97),\n        TestAction::assert_eq(\"'abc'.charCodeAt(null)\", 97),\n        TestAction::assert_eq(\"'\\\\uFFFF'.charCodeAt(0)\", 65535),\n    ]);\n}\n\n#[test]\nfn code_point_at() {\n    run_test_actions([\n        TestAction::assert_eq(\"'abc'.codePointAt(-1)\", JsValue::undefined()),\n        TestAction::assert_eq(\"'abc'.codePointAt(1)\", 98),\n        TestAction::assert_eq(\"'abc'.codePointAt(9)\", JsValue::undefined()),\n        TestAction::assert_eq(\"'abc'.codePointAt()\", 97),\n        TestAction::assert_eq(\"'abc'.codePointAt(null)\", 97),\n        TestAction::assert_eq(r\"'\\uD800\\uDC00'.codePointAt(0)\", 65_536),\n        TestAction::assert_eq(r\"'\\uD800\\uDFFF'.codePointAt(0)\", 66_559),\n        TestAction::assert_eq(r\"'\\uDBFF\\uDC00'.codePointAt(0)\", 1_113_088),\n        TestAction::assert_eq(r\"'\\uDBFF\\uDFFF'.codePointAt(0)\", 1_114_111),\n        TestAction::assert_eq(r\"'\\uD800\\uDC00'.codePointAt(1)\", 56_320),\n        TestAction::assert_eq(r\"'\\uD800\\uDFFF'.codePointAt(1)\", 57_343),\n        TestAction::assert_eq(r\"'\\uDBFF\\uDC00'.codePointAt(1)\", 56_320),\n        TestAction::assert_eq(r\"'\\uDBFF\\uDFFF'.codePointAt(1)\", 57_343),\n    ]);\n}\n\n#[test]\nfn slice() {\n    run_test_actions([\n        TestAction::assert_eq(\"'abc'.slice()\", js_str!(\"abc\")),\n        TestAction::assert_eq(\"'abc'.slice(1)\", js_str!(\"bc\")),\n        TestAction::assert_eq(\"'abc'.slice(-1)\", js_str!(\"c\")),\n        TestAction::assert_eq(\"'abc'.slice(0, 9)\", js_str!(\"abc\")),\n        TestAction::assert_eq(\"'abc'.slice(9, 10)\", js_string!()),\n    ]);\n}\n\n#[test]\nfn empty_iter() {\n    run_test_actions([\n        TestAction::run(indoc! {r#\"\n                let iter = new String()[Symbol.iterator]();\n                let next = iter.next();\n            \"#}),\n        TestAction::assert_eq(\"next.value\", JsValue::undefined()),\n        TestAction::assert(\"next.done\"),\n    ]);\n}\n\n#[test]\nfn ascii_iter() {\n    run_test_actions([\n        TestAction::run_harness(),\n        TestAction::assert(indoc! {r#\"\n                arrayEquals(\n                    Array.from(new String(\"Hello World\")[Symbol.iterator]()),\n                    [\"H\", \"e\", \"l\", \"l\", \"o\", \" \", \"W\", \"o\", \"r\", \"l\", \"d\"]\n                )\n            \"#}),\n    ]);\n}\n\n#[test]\nfn unicode_iter() {\n    run_test_actions([\n        TestAction::run_harness(),\n        TestAction::assert(indoc! {r#\"\n                arrayEquals(\n                    Array.from(new String(\"C🙂🙂l W🙂rld\")[Symbol.iterator]()),\n                    [\"C\", \"🙂\", \"🙂\", \"l\", \" \", \"W\", \"🙂\", \"r\", \"l\", \"d\"]\n                )\n            \"#}),\n    ]);\n}\n\n#[test]\nfn string_get_property() {\n    run_test_actions([\n        TestAction::assert_eq(\"'abc'[-1]\", JsValue::undefined()),\n        TestAction::assert_eq(\"'abc'[1]\", js_str!(\"b\")),\n        TestAction::assert_eq(\"'abc'[2]\", js_str!(\"c\")),\n        TestAction::assert_eq(\"'abc'[3]\", JsValue::undefined()),\n        TestAction::assert_eq(\"'abc'['foo']\", JsValue::undefined()),\n        TestAction::assert_eq(\"'😀'[0]\", js_string!(&[0xD83D])),\n    ]);\n}\n\n#[test]\nfn search() {\n    run_test_actions([\n        TestAction::assert_eq(\"'aa'.search(/b/)\", -1),\n        TestAction::assert_eq(\"'aa'.search(/a/)\", 0),\n        TestAction::assert_eq(\"'aa'.search(/a/g)\", 0),\n        TestAction::assert_eq(\"'ba'.search(/a/)\", 1),\n    ]);\n}\n\n#[test]\nfn from_code_point() {\n    // Taken from https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/fromCodePoint\n    run_test_actions([\n        TestAction::assert_eq(\"String.fromCodePoint(42)\", js_str!(\"*\")),\n        TestAction::assert_eq(\"String.fromCodePoint(65, 90)\", js_str!(\"AZ\")),\n        TestAction::assert_eq(\"String.fromCodePoint(0x404)\", js_str!(\"Є\")),\n        TestAction::assert_eq(\n            \"String.fromCodePoint(0x2f804)\",\n            js_string!(&[0xD87E, 0xDC04]),\n        ),\n        TestAction::assert_eq(\n            \"String.fromCodePoint(0x1D306, 0x1D307)\",\n            js_string!(&[0xD834, 0xDF06, 0xD834, 0xDF07]),\n        ),\n        // Should encode to unpaired surrogates\n        TestAction::assert_eq(\n            \"String.fromCharCode(0xD800, 0xD8FF)\",\n            js_string!(&[0xD800, 0xD8FF]),\n        ),\n        TestAction::assert_eq(\n            \"String.fromCodePoint(9731, 9733, 9842, 0x4F60)\",\n            js_str!(\"☃★♲你\"),\n        ),\n        TestAction::assert_native_error(\n            \"String.fromCodePoint('_')\",\n            JsNativeErrorKind::Range,\n            \"codepoint `NaN` is not an integer\",\n        ),\n        TestAction::assert_native_error(\n            \"String.fromCodePoint(Infinity)\",\n            JsNativeErrorKind::Range,\n            \"codepoint `inf` is not an integer\",\n        ),\n        TestAction::assert_native_error(\n            \"String.fromCodePoint(-1)\",\n            JsNativeErrorKind::Range,\n            \"codepoint `-1` outside of Unicode range\",\n        ),\n        TestAction::assert_native_error(\n            \"String.fromCodePoint(3.14)\",\n            JsNativeErrorKind::Range,\n            \"codepoint `3.14` is not an integer\",\n        ),\n        TestAction::assert_native_error(\n            \"String.fromCodePoint(3e-2)\",\n            JsNativeErrorKind::Range,\n            \"codepoint `0.03` is not an integer\",\n        ),\n        TestAction::assert_native_error(\n            \"String.fromCodePoint(NaN)\",\n            JsNativeErrorKind::Range,\n            \"codepoint `NaN` is not an integer\",\n        ),\n    ]);\n}\n\n#[test]\nfn match_with_symbol_match_method() {\n    run_test_actions([TestAction::assert_eq(\n        indoc! {r#\"\n                const obj = {\n                  [Symbol.match](str) {\n                    return [\"custom\", str];\n                  }\n                };\n                \"abc\".match(obj)[0]\n            \"#},\n        js_str!(\"custom\"),\n    )]);\n}\n\n#[test]\nfn match_with_overridden_exec() {\n    run_test_actions([TestAction::assert_eq(\n        indoc! {r#\"\n                let r = /a/;\n                r.exec = () => [\"fake\"];\n                \"abc\".match(r)[0]\n            \"#},\n        js_str!(\"fake\"),\n    )]);\n}\n"
  },
  {
    "path": "core/engine/src/builtins/symbol/mod.rs",
    "content": "//! Boa's implementation of ECMAScript's global `Symbol` object.\n//!\n//! The data type symbol is a primitive data type.\n//! The `Symbol()` function returns a value of type symbol, has static properties that expose\n//! several members of built-in objects, has static methods that expose the global symbol registry,\n//! and resembles a built-in object class, but is incomplete as a constructor because it does not\n//! support the syntax \"`new Symbol()`\".\n//!\n//! Every symbol value returned from `Symbol()` is unique.\n//!\n//! More information:\n//! - [MDN documentation][mdn]\n//! - [ECMAScript reference][spec]\n//!\n//! [spec]: https://tc39.es/ecma262/#sec-symbol-value\n//! [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Symbol\n\n#[cfg(test)]\nmod tests;\n\nuse std::{hash::BuildHasherDefault, sync::LazyLock};\n\nuse crate::{\n    Context, JsArgs, JsResult, JsString,\n    builtins::BuiltInObject,\n    context::intrinsics::{Intrinsics, StandardConstructor, StandardConstructors},\n    error::JsNativeError,\n    js_string,\n    object::JsObject,\n    property::Attribute,\n    realm::Realm,\n    string::StaticJsStrings,\n    symbol::JsSymbol,\n    value::JsValue,\n};\nuse dashmap::DashMap;\nuse rustc_hash::FxHasher;\n\nuse super::{BuiltInBuilder, BuiltInConstructor, IntrinsicObject};\n\nstatic GLOBAL_SYMBOL_REGISTRY: LazyLock<GlobalSymbolRegistry> =\n    LazyLock::new(GlobalSymbolRegistry::new);\n\ntype FxDashMap<K, V> = DashMap<K, V, BuildHasherDefault<FxHasher>>;\n\n// We previously used `JsString` instead of `Box<[u16]>` for this, but since the glocal symbol\n// registry needed to be global, we had to either make `JsString` thread-safe or directly store\n// its info into the registry. `JsSymbol` is already a pretty niche feature of JS, and we expect only\n// advanced users to utilize it. On the other hand, almost every JS programmer uses `JsString`s, and\n// the first option would impact performance for all `JsString`s in general. For those reasons, we\n// opted for the second option, but we should try to optimize this in the future.\nstruct GlobalSymbolRegistry {\n    keys: FxDashMap<Box<[u16]>, JsSymbol>,\n    symbols: FxDashMap<JsSymbol, Box<[u16]>>,\n}\n\nimpl GlobalSymbolRegistry {\n    fn new() -> Self {\n        Self {\n            keys: FxDashMap::default(),\n            symbols: FxDashMap::default(),\n        }\n    }\n\n    fn get_or_create_symbol(&self, key: &JsString) -> JsResult<JsSymbol> {\n        let slice = key.iter().collect::<Vec<_>>();\n        if let Some(symbol) = self.keys.get(&slice[..]) {\n            return Ok(symbol.clone());\n        }\n\n        let symbol = JsSymbol::new(Some(key.clone())).ok_or_else(|| {\n            JsNativeError::range()\n                .with_message(\"reached the maximum number of symbols that can be created\")\n        })?;\n        self.keys\n            .insert(slice.clone().into_boxed_slice(), symbol.clone());\n        self.symbols\n            .insert(symbol.clone(), slice.into_boxed_slice());\n        Ok(symbol)\n    }\n\n    fn get_key(&self, sym: &JsSymbol) -> Option<JsString> {\n        if let Some(key) = self.symbols.get(sym) {\n            return Some(js_string!(&**key));\n        }\n\n        None\n    }\n}\n\n/// The internal representation of a `Symbol` object.\n#[derive(Debug, Clone, Copy)]\npub struct Symbol;\n\nimpl IntrinsicObject for Symbol {\n    fn init(realm: &Realm) {\n        let symbol_async_iterator = JsSymbol::async_iterator();\n        let symbol_has_instance = JsSymbol::has_instance();\n        let symbol_is_concat_spreadable = JsSymbol::is_concat_spreadable();\n        let symbol_iterator = JsSymbol::iterator();\n        let symbol_match = JsSymbol::r#match();\n        let symbol_match_all = JsSymbol::match_all();\n        let symbol_replace = JsSymbol::replace();\n        let symbol_search = JsSymbol::search();\n        let symbol_species = JsSymbol::species();\n        let symbol_split = JsSymbol::split();\n        let symbol_to_primitive = JsSymbol::to_primitive();\n        let symbol_to_string_tag = JsSymbol::to_string_tag();\n        let symbol_unscopables = JsSymbol::unscopables();\n        let symbol_dispose = JsSymbol::dispose();\n        let symbol_async_dispose = JsSymbol::async_dispose();\n\n        let attribute = Attribute::READONLY | Attribute::NON_ENUMERABLE | Attribute::PERMANENT;\n\n        let to_primitive = BuiltInBuilder::callable(realm, Self::to_primitive)\n            .name(js_string!(\"[Symbol.toPrimitive]\"))\n            .length(1)\n            .build();\n\n        let get_description = BuiltInBuilder::callable(realm, Self::get_description)\n            .name(js_string!(\"get description\"))\n            .build();\n\n        BuiltInBuilder::from_standard_constructor::<Self>(realm)\n            .static_method(Self::for_, js_string!(\"for\"), 1)\n            .static_method(Self::key_for, js_string!(\"keyFor\"), 1)\n            .static_property(\n                js_string!(\"asyncIterator\"),\n                symbol_async_iterator,\n                attribute,\n            )\n            .static_property(js_string!(\"hasInstance\"), symbol_has_instance, attribute)\n            .static_property(\n                js_string!(\"isConcatSpreadable\"),\n                symbol_is_concat_spreadable,\n                attribute,\n            )\n            .static_property(js_string!(\"iterator\"), symbol_iterator, attribute)\n            .static_property(js_string!(\"match\"), symbol_match, attribute)\n            .static_property(js_string!(\"matchAll\"), symbol_match_all, attribute)\n            .static_property(js_string!(\"replace\"), symbol_replace, attribute)\n            .static_property(js_string!(\"search\"), symbol_search, attribute)\n            .static_property(js_string!(\"species\"), symbol_species, attribute)\n            .static_property(js_string!(\"split\"), symbol_split, attribute)\n            .static_property(\n                js_string!(\"toPrimitive\"),\n                symbol_to_primitive.clone(),\n                attribute,\n            )\n            .static_property(\n                js_string!(\"toStringTag\"),\n                symbol_to_string_tag.clone(),\n                attribute,\n            )\n            .static_property(js_string!(\"unscopables\"), symbol_unscopables, attribute)\n            .static_property(js_string!(\"dispose\"), symbol_dispose, attribute)\n            .static_property(js_string!(\"asyncDispose\"), symbol_async_dispose, attribute)\n            .method(Self::to_string, js_string!(\"toString\"), 0)\n            .method(Self::value_of, js_string!(\"valueOf\"), 0)\n            .accessor(\n                js_string!(\"description\"),\n                Some(get_description),\n                None,\n                Attribute::CONFIGURABLE | Attribute::NON_ENUMERABLE,\n            )\n            .property(\n                symbol_to_string_tag,\n                Self::NAME,\n                Attribute::READONLY | Attribute::NON_ENUMERABLE | Attribute::CONFIGURABLE,\n            )\n            .property(\n                symbol_to_primitive,\n                to_primitive,\n                Attribute::READONLY | Attribute::NON_ENUMERABLE | Attribute::CONFIGURABLE,\n            )\n            .build();\n    }\n\n    fn get(intrinsics: &Intrinsics) -> JsObject {\n        Self::STANDARD_CONSTRUCTOR(intrinsics.constructors()).constructor()\n    }\n}\n\nimpl BuiltInObject for Symbol {\n    const NAME: JsString = StaticJsStrings::SYMBOL;\n}\n\nimpl BuiltInConstructor for Symbol {\n    const CONSTRUCTOR_ARGUMENTS: usize = 0;\n    const PROTOTYPE_STORAGE_SLOTS: usize = 6;\n    const CONSTRUCTOR_STORAGE_SLOTS: usize = 17;\n\n    const STANDARD_CONSTRUCTOR: fn(&StandardConstructors) -> &StandardConstructor =\n        StandardConstructors::symbol;\n\n    /// The `Symbol()` constructor returns a value of type symbol.\n    ///\n    /// It is incomplete as a constructor because it does not support\n    /// the syntax `new Symbol()` and it is not intended to be subclassed.\n    ///\n    /// More information:\n    /// - [ECMAScript reference][spec]\n    /// - [MDN documentation][mdn]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-symbol-description\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Symbol/Symbol\n    fn constructor(\n        new_target: &JsValue,\n        args: &[JsValue],\n        context: &mut Context,\n    ) -> JsResult<JsValue> {\n        // 1. If NewTarget is not undefined, throw a TypeError exception.\n        if !new_target.is_undefined() {\n            return Err(JsNativeError::typ()\n                .with_message(\"Symbol is not a constructor\")\n                .into());\n        }\n\n        // 2. If description is undefined, let descString be undefined.\n        // 3. Else, let descString be ? ToString(description).\n        let description = match args.first() {\n            Some(value) if !value.is_undefined() => Some(value.to_string(context)?),\n            _ => None,\n        };\n\n        // 4. Return a new unique Symbol value whose [[Description]] value is descString.\n        Ok(JsSymbol::new(description)\n            .ok_or_else(|| {\n                JsNativeError::range()\n                    .with_message(\"reached the maximum number of symbols that can be created\")\n            })?\n            .into())\n    }\n}\n\nimpl Symbol {\n    fn this_symbol_value(value: &JsValue) -> JsResult<JsSymbol> {\n        value\n            .as_symbol()\n            .or_else(|| {\n                value\n                    .as_object()\n                    .and_then(|obj| obj.downcast_ref::<JsSymbol>().as_deref().cloned())\n            })\n            .ok_or_else(|| {\n                JsNativeError::typ()\n                    .with_message(\"'this' is not a Symbol\")\n                    .into()\n            })\n    }\n\n    /// `Symbol.prototype.toString()`\n    ///\n    /// This method returns a string representing the specified `Symbol` object.\n    ///\n    /// More information:\n    /// - [MDN documentation][mdn]\n    /// - [ECMAScript reference][spec]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-symbol.prototype.tostring\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Symbol/toString\n    #[allow(clippy::wrong_self_convention)]\n    pub(crate) fn to_string(this: &JsValue, _: &[JsValue], _: &mut Context) -> JsResult<JsValue> {\n        // 1. Let sym be ? thisSymbolValue(this value).\n        let symbol = Self::this_symbol_value(this)?;\n\n        // 2. Return SymbolDescriptiveString(sym).\n        Ok(symbol.descriptive_string().into())\n    }\n\n    /// `Symbol.prototype.valueOf()`\n    ///\n    /// This method returns a `Symbol` that is the primitive value of the specified `Symbol` object.\n    ///\n    /// More information:\n    /// - [MDN documentation][mdn]\n    /// - [ECMAScript reference][spec]\n    ///\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Symbol/valueOf\n    /// [spec]: https://tc39.es/ecma262/#sec-symbol.prototype.valueof\n    pub(crate) fn value_of(this: &JsValue, _: &[JsValue], _: &mut Context) -> JsResult<JsValue> {\n        // 1. Return ? thisSymbolValue(this value).\n        let symbol = Self::this_symbol_value(this)?;\n        Ok(symbol.into())\n    }\n\n    /// `get Symbol.prototype.description`\n    ///\n    /// This accessor returns the description of the `Symbol` object.\n    ///\n    /// More information:\n    /// - [MDN documentation][mdn]\n    /// - [ECMAScript reference][spec]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-symbol.prototype.description\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Symbol/description\n    pub(crate) fn get_description(\n        this: &JsValue,\n        _: &[JsValue],\n        _: &mut Context,\n    ) -> JsResult<JsValue> {\n        // 1. Let s be the this value.\n        // 2. Let sym be ? thisSymbolValue(s).\n        let sym = Self::this_symbol_value(this)?;\n\n        // 3. Return sym.[[Description]].\n        Ok(sym\n            .description()\n            .map_or(JsValue::undefined(), JsValue::from))\n    }\n\n    /// `Symbol.for( key )`\n    ///\n    /// More information:\n    /// - [MDN documentation][mdn]\n    /// - [ECMAScript reference][spec]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-symbol.prototype.for\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Symbol/for\n    pub(crate) fn for_(_: &JsValue, args: &[JsValue], context: &mut Context) -> JsResult<JsValue> {\n        // 1. Let stringKey be ? ToString(key).\n        let string_key = args\n            .first()\n            .cloned()\n            .unwrap_or_default()\n            .to_string(context)?;\n        // 2. For each element e of the GlobalSymbolRegistry List, do\n        //     a. If SameValue(e.[[Key]], stringKey) is true, return e.[[Symbol]].\n        // 3. Assert: GlobalSymbolRegistry does not currently contain an entry for stringKey.\n        // 4. Let newSymbol be a new unique Symbol value whose [[Description]] value is stringKey.\n        // 5. Append the Record { [[Key]]: stringKey, [[Symbol]]: newSymbol } to the GlobalSymbolRegistry List.\n        // 6. Return newSymbol.\n        GLOBAL_SYMBOL_REGISTRY\n            .get_or_create_symbol(&string_key)\n            .map(JsValue::from)\n    }\n\n    /// `Symbol.keyFor( sym )`\n    ///\n    ///\n    /// More information:\n    /// - [MDN documentation][mdn]\n    /// - [ECMAScript reference][spec]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-symbol.prototype.keyfor\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Symbol/keyFor\n    pub(crate) fn key_for(_: &JsValue, args: &[JsValue], _: &mut Context) -> JsResult<JsValue> {\n        // 1. If Type(sym) is not Symbol, throw a TypeError exception.\n        let sym = args.get_or_undefined(0).as_symbol().ok_or_else(|| {\n            JsNativeError::typ().with_message(\"Symbol.keyFor: sym is not a symbol\")\n        })?;\n\n        // 2. For each element e of the GlobalSymbolRegistry List (see 20.4.2.2), do\n        //     a. If SameValue(e.[[Symbol]], sym) is true, return e.[[Key]].\n        // 3. Assert: GlobalSymbolRegistry does not currently contain an entry for sym.\n        // 4. Return undefined.\n\n        Ok(GLOBAL_SYMBOL_REGISTRY\n            .get_key(&sym)\n            .map(JsValue::from)\n            .unwrap_or_default())\n    }\n\n    /// `Symbol.prototype [ @@toPrimitive ]`\n    ///\n    /// This function is called by ECMAScript language operators to convert a Symbol object to a primitive value.\n    /// NOTE: The argument is ignored\n    ///\n    /// More information:\n    /// - [MDN documentation][mdn]\n    /// - [ECMAScript reference][spec]\n    ///\n    /// [spec]: https://tc39.es/ecma262/multipage/#sec-symbol.prototype-@@toprimitive\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Symbol/@@toPrimitive\n    pub(crate) fn to_primitive(\n        this: &JsValue,\n        _: &[JsValue],\n        _: &mut Context,\n    ) -> JsResult<JsValue> {\n        let sym = Self::this_symbol_value(this)?;\n        // 1. Return ? thisSymbolValue(this value).\n        Ok(sym.into())\n    }\n}\n"
  },
  {
    "path": "core/engine/src/builtins/symbol/tests.rs",
    "content": "use crate::{JsValue, TestAction, run_test_actions};\nuse boa_macros::js_str;\nuse indoc::indoc;\n\n#[test]\nfn call_symbol_and_check_return_type() {\n    run_test_actions([TestAction::assert_with_op(\"Symbol()\", |val, _| {\n        val.is_symbol()\n    })]);\n}\n\n#[test]\nfn print_symbol_expect_description() {\n    run_test_actions([TestAction::assert_eq(\n        \"String(Symbol('Hello'))\",\n        js_str!(\"Symbol(Hello)\"),\n    )]);\n}\n\n#[test]\nfn symbol_access() {\n    run_test_actions([\n        TestAction::run(indoc! {r#\"\n                var x = {};\n                var sym1 = Symbol(\"Hello\");\n                var sym2 = Symbol(\"Hello\");\n                x[sym1] = 10;\n                x[sym2] = 20;\n            \"#}),\n        TestAction::assert_eq(\"x[sym1]\", 10),\n        TestAction::assert_eq(\"x[sym2]\", 20),\n        TestAction::assert_eq(\"x['Symbol(Hello)']\", JsValue::undefined()),\n    ]);\n}\n"
  },
  {
    "path": "core/engine/src/builtins/temporal/calendar/mod.rs",
    "content": "//! An implementation of the `Temporal` proposal's Calendar builtin.\n\nuse std::str::FromStr;\n\nuse super::extract_from_temporal_type;\nuse crate::{Context, JsNativeError, JsObject, JsResult, JsValue, js_string};\nuse temporal_rs::Calendar;\n\n// -- `Calendar` Abstract Operations --\n\n/// 12.2.9 `GetTemporalCalendarSlotValueWithISODefault ( item )`\n#[allow(unused)]\npub(crate) fn get_temporal_calendar_slot_value_with_default(\n    item: &JsObject,\n    context: &mut Context,\n) -> JsResult<Calendar> {\n    // 1. If item has an [[InitializedTemporalDate]], [[InitializedTemporalDateTime]], [[InitializedTemporalMonthDay]],\n    // [[InitializedTemporalYearMonth]], or [[InitializedTemporalZonedDateTime]] internal slot, then\n    if let Some(calendar) = extract_from_temporal_type(\n        item,\n        |d| Ok(Some(d.inner.calendar().clone())),\n        |dt| Ok(Some(dt.inner.calendar().clone())),\n        |ym| Ok(Some(ym.inner.calendar().clone())),\n        |md| Ok(Some(md.inner.calendar().clone())),\n        |zdt| Ok(Some(zdt.inner.calendar().clone())),\n    )? {\n        // a. Return item.[[Calendar]].\n        return Ok(calendar);\n    }\n\n    // 2. Let calendarLike be ? Get(item, \"calendar\").\n    let calendar_like = item.get(js_string!(\"calendar\"), context)?;\n    // 3. If calendarLike is undefined, then\n    if calendar_like.is_undefined() {\n        // a. Return \"iso8601\".\n        return Ok(Calendar::ISO);\n    }\n    // 4. Return ? ToTemporalCalendarIdentifier(calendarLike).\n    to_temporal_calendar_identifier(&calendar_like)\n}\n\n/// `12.2.8 ToTemporalCalendarIdentifier ( temporalCalendarLike )`\npub(crate) fn to_temporal_calendar_identifier(calendar_like: &JsValue) -> JsResult<Calendar> {\n    // 1. If temporalCalendarLike is an Object, then\n    if let Some(calendar_like) = calendar_like.as_object() {\n        // a. If temporalCalendarLike has an [[InitializedTemporalDate]], [[InitializedTemporalDateTime]],\n        // [[InitializedTemporalMonthDay]], [[InitializedTemporalYearMonth]], or [[InitializedTemporalZonedDateTime]] internal slot, then\n        if let Some(calendar) = extract_from_temporal_type(\n            &calendar_like,\n            |d| Ok(Some(d.inner.calendar().clone())),\n            |dt| Ok(Some(dt.inner.calendar().clone())),\n            |ym| Ok(Some(ym.inner.calendar().clone())),\n            |md| Ok(Some(md.inner.calendar().clone())),\n            |zdt| Ok(Some(zdt.inner.calendar().clone())),\n        )? {\n            // i. Return temporalCalendarLike.[[Calendar]].\n            return Ok(calendar);\n        }\n    }\n\n    // 2. If temporalCalendarLike is not a String, throw a TypeError exception.\n    let Some(calendar_id) = calendar_like.as_string() else {\n        return Err(JsNativeError::typ()\n            .with_message(\"temporalCalendarLike is not a string.\")\n            .into());\n    };\n    // 3. Let identifier be ? ParseTemporalCalendarString(temporalCalendarLike).\n    // 4. Return ? CanonicalizeCalendar(identifier).\n    Ok(Calendar::from_str(&calendar_id.to_std_string_escaped())?)\n}\n"
  },
  {
    "path": "core/engine/src/builtins/temporal/duration/mod.rs",
    "content": "// Boa's implementation of the `Temporal.Duration` built-in object.\n\nuse super::{\n    DateTimeValues, get_relative_to_option,\n    options::{TemporalUnitGroup, get_digits_option, get_temporal_unit},\n};\nuse crate::value::JsVariant;\nuse crate::{\n    Context, JsArgs, JsData, JsError, JsNativeError, JsObject, JsResult, JsString, JsSymbol,\n    JsValue,\n    builtins::{\n        BuiltInBuilder, BuiltInConstructor, BuiltInObject, IntrinsicObject,\n        options::{get_option, get_options_object},\n    },\n    context::intrinsics::{Intrinsics, StandardConstructor, StandardConstructors},\n    js_string,\n    object::internal_methods::get_prototype_from_constructor,\n    property::Attribute,\n    realm::Realm,\n    string::StaticJsStrings,\n};\nuse boa_gc::{Finalize, Trace};\nuse temporal_rs::{\n    Duration as InnerDuration,\n    options::{RoundingIncrement, RoundingMode, RoundingOptions, ToStringRoundingOptions, Unit},\n    partial::PartialDuration,\n};\n\n#[cfg(test)]\nmod tests;\n\n/// The `Temporal.Duration` built-in implementation\n///\n/// More information:\n///\n/// - [ECMAScript Temporal proposal][spec]\n/// - [MDN reference][mdn]\n/// - [`temporal_rs` documentation][temporal_rs-docs]\n///\n/// [spec]: https://tc39.es/proposal-temporal/#sec-temporal-duration-objects\n/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Temporal/Duration\n/// [temporal_rs-docs]: https://docs.rs/temporal_rs/latest/temporal_rs/struct.Duration.html\n#[derive(Debug, Clone, Trace, Finalize, JsData)]\n#[boa_gc(unsafe_empty_trace)] // Safety: Does not contain any traceable fields.\npub struct Duration {\n    pub(crate) inner: Box<InnerDuration>,\n}\n\nimpl Duration {\n    pub(crate) fn new(inner: InnerDuration) -> Self {\n        Self {\n            inner: Box::new(inner),\n        }\n    }\n}\n\nimpl BuiltInObject for Duration {\n    const NAME: JsString = StaticJsStrings::DURATION_NAME;\n}\n\nimpl IntrinsicObject for Duration {\n    fn init(realm: &Realm) {\n        let get_years = BuiltInBuilder::callable(realm, Self::get_years)\n            .name(js_string!(\"get Years\"))\n            .build();\n\n        let get_months = BuiltInBuilder::callable(realm, Self::get_months)\n            .name(js_string!(\"get Months\"))\n            .build();\n\n        let get_weeks = BuiltInBuilder::callable(realm, Self::get_weeks)\n            .name(js_string!(\"get Weeks\"))\n            .build();\n\n        let get_days = BuiltInBuilder::callable(realm, Self::get_days)\n            .name(js_string!(\"get Days\"))\n            .build();\n\n        let get_hours = BuiltInBuilder::callable(realm, Self::get_hours)\n            .name(js_string!(\"get Hours\"))\n            .build();\n\n        let get_minutes = BuiltInBuilder::callable(realm, Self::get_minutes)\n            .name(js_string!(\"get Minutes\"))\n            .build();\n\n        let get_seconds = BuiltInBuilder::callable(realm, Self::get_seconds)\n            .name(js_string!(\"get Seconds\"))\n            .build();\n\n        let get_milliseconds = BuiltInBuilder::callable(realm, Self::get_milliseconds)\n            .name(js_string!(\"get Milliseconds\"))\n            .build();\n\n        let get_microseconds = BuiltInBuilder::callable(realm, Self::get_microseconds)\n            .name(js_string!(\"get Microseconds\"))\n            .build();\n\n        let get_nanoseconds = BuiltInBuilder::callable(realm, Self::get_nanoseconds)\n            .name(js_string!(\"get Nanoseconds\"))\n            .build();\n\n        let get_sign = BuiltInBuilder::callable(realm, Self::get_sign)\n            .name(js_string!(\"get Sign\"))\n            .build();\n\n        let is_blank = BuiltInBuilder::callable(realm, Self::get_blank)\n            .name(js_string!(\"get blank\"))\n            .build();\n\n        BuiltInBuilder::from_standard_constructor::<Self>(realm)\n            .property(\n                JsSymbol::to_string_tag(),\n                StaticJsStrings::DURATION_TAG,\n                Attribute::READONLY | Attribute::NON_ENUMERABLE | Attribute::CONFIGURABLE,\n            )\n            .accessor(\n                js_string!(\"years\"),\n                Some(get_years),\n                None,\n                Attribute::CONFIGURABLE,\n            )\n            .accessor(\n                js_string!(\"months\"),\n                Some(get_months),\n                None,\n                Attribute::CONFIGURABLE,\n            )\n            .accessor(\n                js_string!(\"weeks\"),\n                Some(get_weeks),\n                None,\n                Attribute::CONFIGURABLE,\n            )\n            .accessor(\n                js_string!(\"days\"),\n                Some(get_days),\n                None,\n                Attribute::CONFIGURABLE,\n            )\n            .accessor(\n                js_string!(\"hours\"),\n                Some(get_hours),\n                None,\n                Attribute::CONFIGURABLE,\n            )\n            .accessor(\n                js_string!(\"minutes\"),\n                Some(get_minutes),\n                None,\n                Attribute::CONFIGURABLE,\n            )\n            .accessor(\n                js_string!(\"seconds\"),\n                Some(get_seconds),\n                None,\n                Attribute::CONFIGURABLE,\n            )\n            .accessor(\n                js_string!(\"milliseconds\"),\n                Some(get_milliseconds),\n                None,\n                Attribute::CONFIGURABLE,\n            )\n            .accessor(\n                js_string!(\"microseconds\"),\n                Some(get_microseconds),\n                None,\n                Attribute::CONFIGURABLE,\n            )\n            .accessor(\n                js_string!(\"nanoseconds\"),\n                Some(get_nanoseconds),\n                None,\n                Attribute::CONFIGURABLE,\n            )\n            .accessor(\n                js_string!(\"sign\"),\n                Some(get_sign),\n                None,\n                Attribute::CONFIGURABLE,\n            )\n            .accessor(\n                js_string!(\"blank\"),\n                Some(is_blank),\n                None,\n                Attribute::CONFIGURABLE,\n            )\n            .static_method(Self::from, js_string!(\"from\"), 1)\n            .static_method(Self::compare, js_string!(\"compare\"), 2)\n            .method(Self::with, js_string!(\"with\"), 1)\n            .method(Self::negated, js_string!(\"negated\"), 0)\n            .method(Self::abs, js_string!(\"abs\"), 0)\n            .method(Self::add, js_string!(\"add\"), 1)\n            .method(Self::subtract, js_string!(\"subtract\"), 1)\n            .method(Self::round, js_string!(\"round\"), 1)\n            .method(Self::total, js_string!(\"total\"), 1)\n            .method(Self::to_string, js_string!(\"toString\"), 0)\n            .method(Self::to_locale_string, js_string!(\"toLocaleString\"), 0)\n            .method(Self::to_json, js_string!(\"toJSON\"), 0)\n            .method(Self::value_of, js_string!(\"valueOf\"), 0)\n            .build();\n    }\n\n    fn get(intrinsics: &Intrinsics) -> JsObject {\n        Self::STANDARD_CONSTRUCTOR(intrinsics.constructors()).constructor()\n    }\n}\n\nimpl BuiltInConstructor for Duration {\n    const CONSTRUCTOR_ARGUMENTS: usize = 0;\n    const PROTOTYPE_STORAGE_SLOTS: usize = 36;\n    const CONSTRUCTOR_STORAGE_SLOTS: usize = 2;\n\n    const STANDARD_CONSTRUCTOR: fn(&StandardConstructors) -> &StandardConstructor =\n        StandardConstructors::duration;\n\n    fn constructor(\n        new_target: &JsValue,\n        args: &[JsValue],\n        context: &mut Context,\n    ) -> JsResult<JsValue> {\n        // 1. If NewTarget is undefined, then\n        if new_target.is_undefined() {\n            // a. Throw a TypeError exception.\n            return Err(JsNativeError::typ()\n                .with_message(\"NewTarget cannot be undefined for Temporal.Duration constructor.\")\n                .into());\n        }\n\n        // 2. If years is undefined, let y be 0; else let y be ? ToIntegerIfIntegral(years).\n        let years = args.get_or_undefined(0).map_or(Ok(0), |v| {\n            let finite = v.to_finitef64(context)?;\n            finite\n                .as_integer_if_integral::<i64>()\n                .map_err(JsError::from)\n        })?;\n\n        // 3. If months is undefined, let mo be 0; else let mo be ? ToIntegerIfIntegral(months).\n        let months = args.get_or_undefined(1).map_or(Ok(0), |v| {\n            let finite = v.to_finitef64(context)?;\n            finite\n                .as_integer_if_integral::<i64>()\n                .map_err(JsError::from)\n        })?;\n\n        // 4. If weeks is undefined, let w be 0; else let w be ? ToIntegerIfIntegral(weeks).\n        let weeks = args.get_or_undefined(2).map_or(Ok(0), |v| {\n            let finite = v.to_finitef64(context)?;\n            finite\n                .as_integer_if_integral::<i64>()\n                .map_err(JsError::from)\n        })?;\n\n        // 5. If days is undefined, let d be 0; else let d be ? ToIntegerIfIntegral(days).\n        let days = args.get_or_undefined(3).map_or(Ok(0), |v| {\n            let finite = v.to_finitef64(context)?;\n            finite\n                .as_integer_if_integral::<i64>()\n                .map_err(JsError::from)\n        })?;\n\n        // 6. If hours is undefined, let h be 0; else let h be ? ToIntegerIfIntegral(hours).\n        let hours = args.get_or_undefined(4).map_or(Ok(0), |v| {\n            let finite = v.to_finitef64(context)?;\n            finite\n                .as_integer_if_integral::<i64>()\n                .map_err(JsError::from)\n        })?;\n\n        // 7. If minutes is undefined, let m be 0; else let m be ? ToIntegerIfIntegral(minutes).\n        let minutes = args.get_or_undefined(5).map_or(Ok(0), |v| {\n            let finite = v.to_finitef64(context)?;\n            finite\n                .as_integer_if_integral::<i64>()\n                .map_err(JsError::from)\n        })?;\n\n        // 8. If seconds is undefined, let s be 0; else let s be ? ToIntegerIfIntegral(seconds).\n        let seconds = args.get_or_undefined(6).map_or(Ok(0), |v| {\n            let finite = v.to_finitef64(context)?;\n            finite\n                .as_integer_if_integral::<i64>()\n                .map_err(JsError::from)\n        })?;\n\n        // 9. If milliseconds is undefined, let ms be 0; else let ms be ? ToIntegerIfIntegral(milliseconds).\n        let milliseconds = args.get_or_undefined(7).map_or(Ok(0), |v| {\n            let finite = v.to_finitef64(context)?;\n            finite\n                .as_integer_if_integral::<i64>()\n                .map_err(JsError::from)\n        })?;\n\n        // 10. If microseconds is undefined, let mis be 0; else let mis be ? ToIntegerIfIntegral(microseconds).\n        let microseconds = args.get_or_undefined(8).map_or(Ok(0), |v| {\n            let finite = v.to_finitef64(context)?;\n            finite\n                .as_integer_if_integral::<i128>()\n                .map_err(JsError::from)\n        })?;\n\n        // 11. If nanoseconds is undefined, let ns be 0; else let ns be ? ToIntegerIfIntegral(nanoseconds).\n        let nanoseconds = args.get_or_undefined(9).map_or(Ok(0), |v| {\n            let finite = v.to_finitef64(context)?;\n            finite\n                .as_integer_if_integral::<i128>()\n                .map_err(JsError::from)\n        })?;\n\n        let record = InnerDuration::new(\n            years,\n            months,\n            weeks,\n            days,\n            hours,\n            minutes,\n            seconds,\n            milliseconds,\n            microseconds,\n            nanoseconds,\n        )?;\n\n        // 12. Return ? CreateTemporalDuration(y, mo, w, d, h, m, s, ms, mis, ns, NewTarget).\n        create_temporal_duration(record, Some(new_target), context).map(Into::into)\n    }\n}\n\n// ==== Duration accessor property implementations ====\n\nimpl Duration {\n    // Internal utility function for getting `Duration` field values.\n    fn get_internal_field(this: &JsValue, field: &DateTimeValues) -> JsResult<JsValue> {\n        let object = this.as_object();\n        let duration = object\n            .as_ref()\n            .and_then(JsObject::downcast_ref::<Self>)\n            .ok_or_else(|| {\n                JsNativeError::typ().with_message(\"this value must be a Duration object.\")\n            })?;\n\n        let inner = &duration.inner;\n\n        match field {\n            DateTimeValues::Year => Ok(JsValue::new(inner.years())),\n            DateTimeValues::Month => Ok(JsValue::new(inner.months())),\n            DateTimeValues::Week => Ok(JsValue::new(inner.weeks())),\n            DateTimeValues::Day => Ok(JsValue::new(inner.days())),\n            DateTimeValues::Hour => Ok(JsValue::new(inner.hours())),\n            DateTimeValues::Minute => Ok(JsValue::new(inner.minutes())),\n            DateTimeValues::Second => Ok(JsValue::new(inner.seconds())),\n            DateTimeValues::Millisecond => Ok(JsValue::new(inner.milliseconds())),\n            DateTimeValues::Microsecond => Ok(JsValue::new(inner.microseconds() as f64)),\n            DateTimeValues::Nanosecond => Ok(JsValue::new(inner.nanoseconds() as f64)),\n            DateTimeValues::MonthCode => unreachable!(\n                \"Any other DateTimeValue fields on Duration would be an implementation error.\"\n            ),\n        }\n    }\n\n    /// 7.3.3 get `Temporal.Duration.prototype.years`\n    ///\n    /// More information:\n    ///\n    /// - [ECMAScript Temporal proposal][spec]\n    /// - [MDN reference][mdn]\n    /// - [`temporal_rs` documentation][temporal_rs-docs]\n    ///\n    /// [spec]: https://tc39.es/proposal-temporal/#sec-get-temporal.duration.prototype.years\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Temporal/Duration/years\n    /// [temporal_rs-docs]: https://docs.rs/temporal_rs/latest/temporal_rs/struct.Duration.html#method.years\n    fn get_years(this: &JsValue, _: &[JsValue], _: &mut Context) -> JsResult<JsValue> {\n        Self::get_internal_field(this, &DateTimeValues::Year)\n    }\n\n    // 7.3.4 get `Temporal.Duration.prototype.months`\n    ///\n    /// More information:\n    ///\n    /// - [ECMAScript Temporal proposal][spec]\n    /// - [MDN reference][mdn]\n    /// - [`temporal_rs` documentation][temporal_rs-docs]\n    ///\n    /// [spec]: https://tc39.es/proposal-temporal/#sec-get-temporal.duration.prototype.months\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Temporal/Duration/months\n    /// [temporal_rs-docs]: https://docs.rs/temporal_rs/latest/temporal_rs/struct.Duration.html#method.months\n    fn get_months(this: &JsValue, _: &[JsValue], _: &mut Context) -> JsResult<JsValue> {\n        Self::get_internal_field(this, &DateTimeValues::Month)\n    }\n\n    /// 7.3.5 get `Temporal.Duration.prototype.weeks`\n    ///\n    /// More information:\n    ///\n    /// - [ECMAScript Temporal proposal][spec]\n    /// - [MDN reference][mdn]\n    /// - [`temporal_rs` documentation][temporal_rs-docs]\n    ///\n    /// [spec]: https://tc39.es/proposal-temporal/#sec-get-temporal.duration.prototype.weeks\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Temporal/Duration/weeks\n    /// [temporal_rs-docs]: https://docs.rs/temporal_rs/latest/temporal_rs/struct.Duration.html#method.weeks\n    fn get_weeks(this: &JsValue, _: &[JsValue], _: &mut Context) -> JsResult<JsValue> {\n        Self::get_internal_field(this, &DateTimeValues::Week)\n    }\n\n    /// 7.3.6 get `Temporal.Duration.prototype.days`\n    ///\n    /// More information:\n    ///\n    /// - [ECMAScript Temporal proposal][spec]\n    /// - [MDN reference][mdn]\n    /// - [`temporal_rs` documentation][temporal_rs-docs]\n    ///\n    /// [spec]: https://tc39.es/proposal-temporal/#sec-get-temporal.duration.prototype.days\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Temporal/Duration/days\n    /// [temporal_rs-docs]: https://docs.rs/temporal_rs/latest/temporal_rs/struct.Duration.html#method.days\n    fn get_days(this: &JsValue, _: &[JsValue], _: &mut Context) -> JsResult<JsValue> {\n        Self::get_internal_field(this, &DateTimeValues::Day)\n    }\n\n    /// 7.3.7 get `Temporal.Duration.prototype.hours`\n    ///\n    /// More information:\n    ///\n    /// - [ECMAScript Temporal proposal][spec]\n    /// - [MDN reference][mdn]\n    /// - [`temporal_rs` documentation][temporal_rs-docs]\n    ///\n    /// [spec]: https://tc39.es/proposal-temporal/#sec-get-temporal.duration.prototype.hours\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Temporal/Duration/hours\n    /// [temporal_rs-docs]: https://docs.rs/temporal_rs/latest/temporal_rs/struct.Duration.html#method.hours\n    fn get_hours(this: &JsValue, _: &[JsValue], _: &mut Context) -> JsResult<JsValue> {\n        Self::get_internal_field(this, &DateTimeValues::Hour)\n    }\n\n    /// 7.3.8 get `Temporal.Duration.prototype.minutes`\n    ///\n    /// More information:\n    ///\n    /// - [ECMAScript Temporal proposal][spec]\n    /// - [MDN reference][mdn]\n    /// - [`temporal_rs` documentation][temporal_rs-docs]\n    ///\n    /// [spec]: https://tc39.es/proposal-temporal/#sec-get-temporal.duration.prototype.minutes\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Temporal/Duration/minutes\n    /// [temporal_rs-docs]: https://docs.rs/temporal_rs/latest/temporal_rs/struct.Duration.html#method.minutes\n    fn get_minutes(this: &JsValue, _: &[JsValue], _: &mut Context) -> JsResult<JsValue> {\n        Self::get_internal_field(this, &DateTimeValues::Minute)\n    }\n\n    /// 7.3.9 get `Temporal.Duration.prototype.seconds`\n    ///\n    /// More information:\n    ///\n    /// - [ECMAScript Temporal proposal][spec]\n    /// - [MDN reference][mdn]\n    /// - [`temporal_rs` documentation][temporal_rs-docs]\n    ///\n    /// [spec]: https://tc39.es/proposal-temporal/#sec-get-temporal.duration.prototype.seconds\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Temporal/Duration/seconds\n    /// [temporal_rs-docs]: https://docs.rs/temporal_rs/latest/temporal_rs/struct.Duration.html#method.seconds\n    fn get_seconds(this: &JsValue, _: &[JsValue], _: &mut Context) -> JsResult<JsValue> {\n        Self::get_internal_field(this, &DateTimeValues::Second)\n    }\n\n    /// 7.3.10 get `Temporal.Duration.prototype.milliseconds`\n    ///\n    /// More information:\n    ///\n    /// - [ECMAScript Temporal proposal][spec]\n    /// - [MDN reference][mdn]\n    /// - [`temporal_rs` documentation][temporal_rs-docs]\n    ///\n    /// [spec]: https://tc39.es/proposal-temporal/#sec-get-temporal.duration.prototype.milliseconds\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Temporal/Duration/milliseconds\n    /// [temporal_rs-docs]: https://docs.rs/temporal_rs/latest/temporal_rs/struct.Duration.html#method.milliseconds\n    fn get_milliseconds(this: &JsValue, _: &[JsValue], _: &mut Context) -> JsResult<JsValue> {\n        Self::get_internal_field(this, &DateTimeValues::Millisecond)\n    }\n\n    /// 7.3.11 get `Temporal.Duration.prototype.microseconds`\n    ///\n    /// More information:\n    ///\n    /// - [ECMAScript Temporal proposal][spec]\n    /// - [MDN reference][mdn]\n    /// - [`temporal_rs` documentation][temporal_rs-docs]\n    ///\n    /// [spec]: https://tc39.es/proposal-temporal/#sec-get-temporal.duration.prototype.microseconds\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Temporal/Duration/microseconds\n    /// [temporal_rs-docs]: https://docs.rs/temporal_rs/latest/temporal_rs/struct.Duration.html#method.microseconds\n    fn get_microseconds(this: &JsValue, _: &[JsValue], _: &mut Context) -> JsResult<JsValue> {\n        Self::get_internal_field(this, &DateTimeValues::Microsecond)\n    }\n\n    /// 7.3.12 get `Temporal.Duration.prototype.nanoseconds`\n    ///\n    /// More information:\n    ///\n    /// - [ECMAScript Temporal proposal][spec]\n    /// - [MDN reference][mdn]\n    /// - [`temporal_rs` documentation][temporal_rs-docs]\n    ///\n    /// [spec]: https://tc39.es/proposal-temporal/#sec-get-temporal.duration.prototype.nanoseconds\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Temporal/Duration/nanoseconds\n    /// [temporal_rs-docs]: https://docs.rs/temporal_rs/latest/temporal_rs/struct.Duration.html#method.nanoseconds\n    fn get_nanoseconds(this: &JsValue, _: &[JsValue], _: &mut Context) -> JsResult<JsValue> {\n        Self::get_internal_field(this, &DateTimeValues::Nanosecond)\n    }\n\n    /// 7.3.13 get `Temporal.Duration.prototype.sign`\n    ///\n    /// More information:\n    ///\n    /// - [ECMAScript Temporal proposal][spec]\n    /// - [MDN reference][mdn]\n    /// - [`temporal_rs` documentation][temporal_rs-docs]\n    ///\n    /// [spec]: https://tc39.es/proposal-temporal/#sec-get-temporal.duration.prototype.sign\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Temporal/Duration/sign\n    /// [temporal_rs-docs]: https://docs.rs/temporal_rs/latest/temporal_rs/struct.Duration.html#method.sign\n    fn get_sign(this: &JsValue, _: &[JsValue], _: &mut Context) -> JsResult<JsValue> {\n        // 1. Let duration be the this value.\n        // 2. Perform ? RequireInternalSlot(duration, [[InitializedTemporalDuration]]).\n        let object = this.as_object();\n        let duration = object\n            .as_ref()\n            .and_then(JsObject::downcast_ref::<Self>)\n            .ok_or_else(|| {\n                JsNativeError::typ().with_message(\"this value must be a Duration object.\")\n            })?;\n\n        // 3. Return 𝔽(! DurationSign(duration.[[Years]], duration.[[Months]], duration.[[Weeks]],\n        // duration.[[Days]], duration.[[Hours]], duration.[[Minutes]], duration.[[Seconds]],\n        // duration.[[Milliseconds]], duration.[[Microseconds]], duration.[[Nanoseconds]])).\n        Ok((duration.inner.sign() as i8).into())\n    }\n\n    /// 7.3.14 get `Temporal.Duration.prototype.blank`\n    ///\n    /// More information:\n    ///\n    /// - [ECMAScript Temporal proposal][spec]\n    /// - [MDN reference][mdn]\n    /// - [`temporal_rs` documentation][temporal_rs-docs]\n    ///\n    /// [spec]: https://tc39.es/proposal-temporal/#sec-get-temporal.duration.prototype.blank\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Temporal/Duration/blank\n    /// [temporal_rs-docs]: https://docs.rs/temporal_rs/latest/temporal_rs/struct.Duration.html#method.blank\n    fn get_blank(this: &JsValue, _: &[JsValue], _: &mut Context) -> JsResult<JsValue> {\n        // 1. Let duration be the this value.\n        // 2. Perform ? RequireInternalSlot(duration, [[InitializedTemporalDuration]]).\n        let object = this.as_object();\n        let duration = object\n            .as_ref()\n            .and_then(JsObject::downcast_ref::<Self>)\n            .ok_or_else(|| {\n                JsNativeError::typ().with_message(\"this value must be a Duration object.\")\n            })?;\n\n        // 3. Let sign be ! DurationSign(duration.[[Years]], duration.[[Months]], duration.[[Weeks]],\n        // duration.[[Days]], duration.[[Hours]], duration.[[Minutes]], duration.[[Seconds]],\n        // duration.[[Milliseconds]], duration.[[Microseconds]], duration.[[Nanoseconds]]).\n        // 4. If sign = 0, return true.\n        // 5. Return false.\n        Ok(duration.inner.is_zero().into())\n    }\n}\n\n// ==== Duration static methods implementation ====\n\nimpl Duration {\n    /// 7.2.2 `Temporal.Duration.from ( item )`\n    ///\n    /// More information:\n    ///\n    /// - [ECMAScript Temporal proposal][spec]\n    /// - [MDN reference][mdn]\n    ///\n    /// [spec]: https://tc39.es/proposal-temporal/#sec-temporal.duration.from\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Temporal/Duration/from\n    fn from(_: &JsValue, args: &[JsValue], context: &mut Context) -> JsResult<JsValue> {\n        let item = args.get_or_undefined(0);\n        // 1. If item is an Object and item has an [[InitializedTemporalDuration]] internal slot, then\n        let object = item.as_object();\n        if let Some(duration) = object.as_ref().and_then(JsObject::downcast_ref::<Self>) {\n            // a. Return ! CreateTemporalDuration(item.[[Years]], item.[[Months]], item.[[Weeks]],\n            // item.[[Days]], item.[[Hours]], item.[[Minutes]], item.[[Seconds]], item.[[Milliseconds]],\n            // item.[[Microseconds]], item.[[Nanoseconds]]).\n            return create_temporal_duration(*duration.inner, None, context).map(Into::into);\n        }\n\n        // 2. Return ? ToTemporalDuration(item).\n        create_temporal_duration(to_temporal_duration_record(item, context)?, None, context)\n            .map(Into::into)\n    }\n\n    /// 7.2.3 `Temporal.Duration.compare ( one, two [ , options ] )`\n    ///\n    /// More information:\n    ///\n    /// - [ECMAScript Temporal proposal][spec]\n    /// - [MDN reference][mdn]\n    /// - [`temporal_rs` documentation][temporal_rs-docs]\n    ///\n    /// [spec]: https://tc39.es/proposal-temporal/#sec-temporal.duration.compare\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Temporal/Duration/compare\n    /// [temporal_rs-docs]: https://docs.rs/temporal_rs/latest/temporal_rs/struct.Duration.html#method.compare\n    fn compare(_: &JsValue, args: &[JsValue], context: &mut Context) -> JsResult<JsValue> {\n        // 1. Set one to ? ToTemporalDuration(one).\n        let one = to_temporal_duration(args.get_or_undefined(0), context)?;\n        // 2. Set two to ? ToTemporalDuration(two).\n        let two = to_temporal_duration(args.get_or_undefined(1), context)?;\n        // 3. Let resolvedOptions be ? GetOptionsObject(options).\n        let options = get_options_object(args.get_or_undefined(2))?;\n        // 4. Let relativeToRecord be ? GetTemporalRelativeToOption(resolvedOptions).\n        let relative_to = get_relative_to_option(&options, context)?;\n\n        Ok(\n            (one.compare_with_provider(&two, relative_to, context.timezone_provider())? as i8)\n                .into(),\n        )\n    }\n}\n\n// ==== Duration methods implementation ====\n\nimpl Duration {\n    /// 7.3.15 `Temporal.Duration.prototype.with ( temporalDurationLike )`\n    ///\n    /// More information:\n    ///\n    /// - [ECMAScript Temporal proposal][spec]\n    /// - [MDN reference][mdn]\n    ///\n    /// [spec]: https://tc39.es/proposal-temporal/#sec-temporal.duration.prototype.with\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Temporal/Duration/with\n    pub(crate) fn with(\n        this: &JsValue,\n        args: &[JsValue],\n        context: &mut Context,\n    ) -> JsResult<JsValue> {\n        // 1. Let duration be the this value.\n        // 2. Perform ? RequireInternalSlot(duration, [[InitializedTemporalDuration]]).\n        let object = this.as_object();\n        let duration = object\n            .as_ref()\n            .and_then(JsObject::downcast_ref::<Self>)\n            .ok_or_else(|| {\n                JsNativeError::typ().with_message(\"this value must be a Duration object.\")\n            })?;\n\n        // 3. Let temporalDurationLike be ? ToTemporalPartialDurationRecord(temporalDurationLike).\n        let temporal_duration_like =\n            to_temporal_partial_duration(args.get_or_undefined(0), context)?;\n\n        // 4. If temporalDurationLike.[[Years]] is not undefined, then\n        // a. Let years be temporalDurationLike.[[Years]].\n        // 5. Else,\n        // a. Let years be duration.[[Years]].\n        let years = temporal_duration_like\n            .years\n            .unwrap_or(duration.inner.years());\n\n        // 6. If temporalDurationLike.[[Months]] is not undefined, then\n        // a. Let months be temporalDurationLike.[[Months]].\n        // 7. Else,\n        // a. Let months be duration.[[Months]].\n        let months = temporal_duration_like\n            .months\n            .unwrap_or(duration.inner.months());\n\n        // 8. If temporalDurationLike.[[Weeks]] is not undefined, then\n        // a. Let weeks be temporalDurationLike.[[Weeks]].\n        // 9. Else,\n        // a. Let weeks be duration.[[Weeks]].\n        let weeks = temporal_duration_like\n            .weeks\n            .unwrap_or(duration.inner.weeks());\n\n        // 10. If temporalDurationLike.[[Days]] is not undefined, then\n        // a. Let days be temporalDurationLike.[[Days]].\n        // 11. Else,\n        // a. Let days be duration.[[Days]].\n        let days = temporal_duration_like.days.unwrap_or(duration.inner.days());\n\n        // 12. If temporalDurationLike.[[Hours]] is not undefined, then\n        // a. Let hours be temporalDurationLike.[[Hours]].\n        // 13. Else,\n        // a. Let hours be duration.[[Hours]].\n        let hours = temporal_duration_like\n            .hours\n            .unwrap_or(duration.inner.hours());\n\n        // 14. If temporalDurationLike.[[Minutes]] is not undefined, then\n        // a. Let minutes be temporalDurationLike.[[Minutes]].\n        // 15. Else,\n        // a. Let minutes be duration.[[Minutes]].\n        let minutes = temporal_duration_like\n            .minutes\n            .unwrap_or(duration.inner.minutes());\n\n        // 16. If temporalDurationLike.[[Seconds]] is not undefined, then\n        // a. Let seconds be temporalDurationLike.[[Seconds]].\n        // 17. Else,\n        // a. Let seconds be duration.[[Seconds]].\n        let seconds = temporal_duration_like\n            .seconds\n            .unwrap_or(duration.inner.seconds());\n\n        // 18. If temporalDurationLike.[[Milliseconds]] is not undefined, then\n        // a. Let milliseconds be temporalDurationLike.[[Milliseconds]].\n        // 19. Else,\n        // a. Let milliseconds be duration.[[Milliseconds]].\n        let milliseconds = temporal_duration_like\n            .milliseconds\n            .unwrap_or(duration.inner.milliseconds());\n\n        // 20. If temporalDurationLike.[[Microseconds]] is not undefined, then\n        // a. Let microseconds be temporalDurationLike.[[Microseconds]].\n        // 21. Else,\n        // a. Let microseconds be duration.[[Microseconds]].\n        let microseconds = temporal_duration_like\n            .microseconds\n            .unwrap_or(duration.inner.microseconds());\n\n        // 22. If temporalDurationLike.[[Nanoseconds]] is not undefined, then\n        // a. Let nanoseconds be temporalDurationLike.[[Nanoseconds]].\n        // 23. Else,\n        // a. Let nanoseconds be duration.[[Nanoseconds]].\n        let nanoseconds = temporal_duration_like\n            .nanoseconds\n            .unwrap_or(duration.inner.nanoseconds());\n\n        // 24. Return ? CreateTemporalDuration(years, months, weeks, days, hours, minutes, seconds, milliseconds, microseconds, nanoseconds).\n        let new_duration = InnerDuration::new(\n            years,\n            months,\n            weeks,\n            days,\n            hours,\n            minutes,\n            seconds,\n            milliseconds,\n            microseconds,\n            nanoseconds,\n        )?;\n        create_temporal_duration(new_duration, None, context).map(Into::into)\n    }\n\n    /// 7.3.16 `Temporal.Duration.prototype.negated ( )`\n    ///\n    /// More information:\n    ///\n    /// - [ECMAScript Temporal proposal][spec]\n    /// - [MDN reference][mdn]\n    /// - [`temporal_rs` documentation][temporal_rs-docs]\n    ///\n    /// [spec]: https://tc39.es/proposal-temporal/#sec-temporal.duration.prototype.negated\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Temporal/Duration/negated\n    /// [temporal_rs-docs]: https://docs.rs/temporal_rs/latest/temporal_rs/struct.Duration.html#method.negated\n    pub(crate) fn negated(\n        this: &JsValue,\n        _: &[JsValue],\n        context: &mut Context,\n    ) -> JsResult<JsValue> {\n        // 1. Let duration be the this value.\n        // 2. Perform ? RequireInternalSlot(duration, [[InitializedTemporalDuration]]).\n        // 3. Return ! CreateNegatedTemporalDuration(duration).\n        let object = this.as_object();\n        let duration = object\n            .as_ref()\n            .and_then(JsObject::downcast_ref::<Self>)\n            .ok_or_else(|| {\n                JsNativeError::typ().with_message(\"this value must be a Duration object.\")\n            })?;\n\n        create_temporal_duration(duration.inner.negated(), None, context).map(Into::into)\n    }\n\n    /// 7.3.17 `Temporal.Duration.prototype.abs ( )`\n    ///\n    /// More information:\n    ///\n    /// - [ECMAScript Temporal proposal][spec]\n    /// - [MDN reference][mdn]\n    /// - [`temporal_rs` documentation][temporal_rs-docs]\n    ///\n    /// [spec]: https://tc39.es/proposal-temporal/#sec-temporal.duration.prototype.abs\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Temporal/Duration/abs\n    /// [temporal_rs-docs]: https://docs.rs/temporal_rs/latest/temporal_rs/struct.Duration.html#method.abs\n    pub(crate) fn abs(this: &JsValue, _: &[JsValue], context: &mut Context) -> JsResult<JsValue> {\n        // 1. Let duration be the this value.\n        // 2. Perform ? RequireInternalSlot(duration, [[InitializedTemporalDuration]]).\n        // 3. Return ! CreateTemporalDuration(abs(duration.[[Years]]), abs(duration.[[Months]]),\n        //    abs(duration.[[Weeks]]), abs(duration.[[Days]]), abs(duration.[[Hours]]), abs(duration.[[Minutes]]),\n        //    abs(duration.[[Seconds]]), abs(duration.[[Milliseconds]]), abs(duration.[[Microseconds]]), abs(duration.[[Nanoseconds]])).\n        let object = this.as_object();\n        let duration = object\n            .as_ref()\n            .and_then(JsObject::downcast_ref::<Self>)\n            .ok_or_else(|| {\n                JsNativeError::typ().with_message(\"this value must be a Duration object.\")\n            })?;\n\n        create_temporal_duration(duration.inner.abs(), None, context).map(Into::into)\n    }\n\n    /// 7.3.18 `Temporal.Duration.prototype.add ( other [ , options ] )`\n    ///\n    /// More information:\n    ///\n    /// - [ECMAScript Temporal proposal][spec]\n    /// - [MDN reference][mdn]\n    /// - [`temporal_rs` documentation][temporal_rs-docs]\n    ///\n    /// [spec]: https://tc39.es/proposal-temporal/#sec-temporal.duration.prototype.add\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Temporal/Duration/add\n    /// [temporal_rs-docs]: https://docs.rs/temporal_rs/latest/temporal_rs/struct.Duration.html#method.add\n    pub(crate) fn add(\n        this: &JsValue,\n        args: &[JsValue],\n        context: &mut Context,\n    ) -> JsResult<JsValue> {\n        // 1.Let duration be the this value.\n        // 2. Perform ? RequireInternalSlot(duration, [[InitializedTemporalDuration]]).\n        let object = this.as_object();\n        let duration = object\n            .as_ref()\n            .and_then(JsObject::downcast_ref::<Self>)\n            .ok_or_else(|| {\n                JsNativeError::typ().with_message(\"this value must be a Duration object.\")\n            })?;\n\n        // 3. Return ? AddDurations(add, duration, other).\n        let other = to_temporal_duration_record(args.get_or_undefined(0), context)?;\n\n        create_temporal_duration(duration.inner.add(&other)?, None, context).map(Into::into)\n    }\n\n    /// 7.3.19 `Temporal.Duration.prototype.subtract ( other [ , options ] )`\n    ///\n    /// More information:\n    ///\n    /// - [ECMAScript Temporal proposal][spec]\n    /// - [MDN reference][mdn]\n    /// - [`temporal_rs` documentation][temporal_rs-docs]\n    ///\n    /// [spec]: https://tc39.es/proposal-temporal/#sec-temporal.duration.prototype.subtract\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Temporal/Duration/subtract\n    /// [temporal_rs-docs]: https://docs.rs/temporal_rs/latest/temporal_rs/struct.Duration.html#method.subtract\n    pub(crate) fn subtract(\n        this: &JsValue,\n        args: &[JsValue],\n        context: &mut Context,\n    ) -> JsResult<JsValue> {\n        // 1.Let duration be the this value.\n        // 2. Perform ? RequireInternalSlot(duration, [[InitializedTemporalDuration]]).\n        let object = this.as_object();\n        let duration = object\n            .as_ref()\n            .and_then(JsObject::downcast_ref::<Self>)\n            .ok_or_else(|| {\n                JsNativeError::typ().with_message(\"this value must be a Duration object.\")\n            })?;\n\n        let other = to_temporal_duration_record(args.get_or_undefined(0), context)?;\n\n        // 3. Return ? AddDurations(add, duration, other).\n        create_temporal_duration(duration.inner.subtract(&other)?, None, context).map(Into::into)\n    }\n\n    /// 7.3.20 `Temporal.Duration.prototype.round ( roundTo )`\n    ///\n    /// More information:\n    ///\n    /// - [ECMAScript Temporal proposal][spec]\n    /// - [MDN reference][mdn]\n    /// - [`temporal_rs` documentation][temporal_rs-docs]\n    ///\n    /// [spec]: https://tc39.es/proposal-temporal/#sec-temporal.duration.prototype.round\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Temporal/Duration/round\n    /// [temporal_rs-docs]: https://docs.rs/temporal_rs/latest/temporal_rs/struct.Duration.html#method.round\n    pub(crate) fn round(\n        this: &JsValue,\n        args: &[JsValue],\n        context: &mut Context,\n    ) -> JsResult<JsValue> {\n        // 1. Let duration be the this value.\n        // 2. Perform ? RequireInternalSlot(duration, [[InitializedTemporalDuration]]).\n        let object = this.as_object();\n        let duration = object\n            .as_ref()\n            .and_then(JsObject::downcast_ref::<Self>)\n            .ok_or_else(|| {\n                JsNativeError::typ().with_message(\"this value must be a Duration object.\")\n            })?;\n\n        let round_to = match args.first().map(JsValue::variant) {\n            // 3. If roundTo is undefined, then\n            None | Some(JsVariant::Undefined) => {\n                return Err(JsNativeError::typ()\n                    .with_message(\"roundTo cannot be undefined.\")\n                    .into());\n            }\n            // 4. If Type(roundTo) is String, then\n            Some(JsVariant::String(rt)) => {\n                // a. Let paramString be roundTo.\n                let param_string = rt.clone();\n                // b. Set roundTo to OrdinaryObjectCreate(null).\n                let new_round_to = JsObject::with_null_proto();\n                // c. Perform ! CreateDataPropertyOrThrow(roundTo, \"smallestUnit\", paramString).\n                new_round_to.create_data_property_or_throw(\n                    js_string!(\"smallestUnit\"),\n                    param_string,\n                    context,\n                )?;\n                new_round_to\n            }\n            // 5. Else,\n            Some(round_to) => {\n                // TODO: remove this clone.\n                // a. Set roundTo to ? GetOptionsObject(roundTo).\n                get_options_object(&JsValue::from(round_to))?\n            }\n        };\n\n        // NOTE: 6 & 7 unused in favor of `is_none()`.\n        // 6. Let smallestUnitPresent be true.\n        // 7. Let largestUnitPresent be true.\n        let mut options = RoundingOptions::default();\n\n        // 8. NOTE: The following steps read options and perform independent validation in alphabetical order (ToRelativeTemporalObject reads \"relativeTo\", ToTemporalRoundingIncrement reads \"roundingIncrement\" and ToTemporalRoundingMode reads \"roundingMode\").\n        // 9. Let largestUnit be ? GetTemporalUnit(roundTo, \"largestUnit\", datetime, undefined, « \"auto\" »).\n        options.largest_unit = get_temporal_unit(\n            &round_to,\n            js_string!(\"largestUnit\"),\n            TemporalUnitGroup::DateTime,\n            Some([Unit::Auto].into()),\n            context,\n        )?;\n\n        // 10. Let relativeToRecord be ? ToRelativeTemporalObject(roundTo).\n        // 11. Let zonedRelativeTo be relativeToRecord.[[ZonedRelativeTo]].\n        // 12. Let plainRelativeTo be relativeToRecord.[[PlainRelativeTo]].\n        let relative_to = get_relative_to_option(&round_to, context)?;\n\n        // 13. Let roundingIncrement be ? ToTemporalRoundingIncrement(roundTo).\n        options.increment =\n            get_option::<RoundingIncrement>(&round_to, js_string!(\"roundingIncrement\"), context)?;\n\n        // 14. Let roundingMode be ? ToTemporalRoundingMode(roundTo, \"halfExpand\").\n        options.rounding_mode =\n            get_option::<RoundingMode>(&round_to, js_string!(\"roundingMode\"), context)?;\n\n        // 15. Let smallestUnit be ? GetTemporalUnit(roundTo, \"smallestUnit\", datetime, undefined).\n        options.smallest_unit = get_temporal_unit(\n            &round_to,\n            js_string!(\"smallestUnit\"),\n            TemporalUnitGroup::DateTime,\n            None,\n            context,\n        )?;\n\n        // NOTE: execute step 21 earlier before initial values are shadowed.\n        // 21. If smallestUnitPresent is false and largestUnitPresent is false, then\n\n        let rounded_duration = duration.inner.round_with_provider(\n            options,\n            relative_to,\n            context.timezone_provider(),\n        )?;\n        create_temporal_duration(rounded_duration, None, context).map(Into::into)\n    }\n\n    /// 7.3.21 `Temporal.Duration.prototype.total ( totalOf )`\n    ///\n    /// More information:\n    ///\n    /// - [ECMAScript Temporal proposal][spec]\n    /// - [MDN reference][mdn]\n    /// - [`temporal_rs` documentation][temporal_rs-docs]\n    ///\n    /// [spec]: https://tc39.es/proposal-temporal/#sec-temporal.duration.prototype.total\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Temporal/Duration/total\n    /// [temporal_rs-docs]: https://docs.rs/temporal_rs/latest/temporal_rs/struct.Duration.html#method.total\n    pub(crate) fn total(\n        this: &JsValue,\n        args: &[JsValue],\n        context: &mut Context,\n    ) -> JsResult<JsValue> {\n        // 1. Let duration be the this value.\n        // 2. Perform ? RequireInternalSlot(duration, [[InitializedTemporalDuration]]).\n        let object = this.as_object();\n        let duration = object\n            .as_ref()\n            .and_then(JsObject::downcast_ref::<Self>)\n            .ok_or_else(|| {\n                JsNativeError::typ().with_message(\"this value must be a Duration object.\")\n            })?;\n\n        let total_of = args.get_or_undefined(0);\n\n        let total_of = match total_of.variant() {\n            // 3. If totalOf is undefined, throw a TypeError exception.\n            JsVariant::Undefined => {\n                return Err(JsNativeError::typ()\n                    .with_message(\"totalOf cannot be undefined.\")\n                    .into());\n            }\n            // 4. If Type(totalOf) is String, then\n            JsVariant::String(param_string) => {\n                // a. Let paramString be totalOf.\n                // b. Set totalOf to OrdinaryObjectCreate(null).\n                let total_of = JsObject::with_null_proto();\n                // c. Perform ! CreateDataPropertyOrThrow(totalOf, \"unit\", paramString).\n                total_of.create_data_property_or_throw(\n                    js_string!(\"unit\"),\n                    param_string.clone(),\n                    context,\n                )?;\n                total_of\n            }\n            // 5. Else,\n            _ => {\n                // a. Set totalOf to ? GetOptionsObject(totalOf).\n                get_options_object(total_of)?\n            }\n        };\n\n        // 6. NOTE: The following steps read options and perform independent validation in alphabetical order (ToRelativeTemporalObject reads \"relativeTo\").\n        // 7. Let relativeToRecord be ? ToRelativeTemporalObject(totalOf).\n        // 8. Let zonedRelativeTo be relativeToRecord.[[ZonedRelativeTo]].\n        // 9. Let plainRelativeTo be relativeToRecord.[[PlainRelativeTo]].\n        let relative_to = get_relative_to_option(&total_of, context)?;\n\n        // 10. Let unit be ? GetTemporalUnit(totalOf, \"unit\", datetime, required).\n        let unit = get_temporal_unit(\n            &total_of,\n            js_string!(\"unit\"),\n            TemporalUnitGroup::DateTime,\n            None,\n            context,\n        )?\n        .ok_or_else(|| JsNativeError::range().with_message(\"unit cannot be undefined.\"))?;\n\n        Ok(duration\n            .inner\n            .total_with_provider(unit, relative_to, context.timezone_provider())?\n            .as_inner()\n            .into())\n    }\n\n    /// 7.3.22 `Temporal.Duration.prototype.toString ( [ options ] )`\n    ///\n    /// More information:\n    ///\n    /// - [ECMAScript Temporal proposal][spec]\n    /// - [MDN reference][mdn]\n    /// - [`temporal_rs` documentation][temporal_rs-docs]\n    ///\n    /// [spec]: https://tc39.es/proposal-temporal/#sec-temporal.duration.prototype.tostring\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Temporal/Duration/toString\n    /// [temporal_rs-docs]: https://docs.rs/temporal_rs/latest/temporal_rs/struct.Duration.html#method.as_temporal_string\n    pub(crate) fn to_string(\n        this: &JsValue,\n        args: &[JsValue],\n        context: &mut Context,\n    ) -> JsResult<JsValue> {\n        let object = this.as_object();\n        let duration = object\n            .as_ref()\n            .and_then(JsObject::downcast_ref::<Self>)\n            .ok_or_else(|| {\n                JsNativeError::typ().with_message(\"this value must be a Duration object.\")\n            })?;\n\n        let options = get_options_object(args.get_or_undefined(0))?;\n        let precision = get_digits_option(&options, context)?;\n        let rounding_mode =\n            get_option::<RoundingMode>(&options, js_string!(\"roundingMode\"), context)?;\n        let smallest_unit = get_option::<Unit>(&options, js_string!(\"smallestUnit\"), context)?;\n\n        let result = duration.inner.as_temporal_string(ToStringRoundingOptions {\n            precision,\n            smallest_unit,\n            rounding_mode,\n        })?;\n\n        Ok(JsString::from(result).into())\n    }\n\n    /// 7.3.23 `Temporal.Duration.prototype.toJSON ( )`\n    ///\n    /// More information:\n    ///\n    /// - [ECMAScript Temporal proposal][spec]\n    /// - [MDN reference][mdn]\n    ///\n    /// [spec]: https://tc39.es/proposal-temporal/#sec-temporal.duration.prototype.tojson\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Temporal/Duration/toJSON\n    pub(crate) fn to_json(this: &JsValue, _: &[JsValue], _: &mut Context) -> JsResult<JsValue> {\n        let object = this.as_object();\n        let duration = object\n            .as_ref()\n            .and_then(JsObject::downcast_ref::<Self>)\n            .ok_or_else(|| {\n                JsNativeError::typ().with_message(\"this value must be a Duration object.\")\n            })?;\n\n        let result = duration\n            .inner\n            .as_temporal_string(ToStringRoundingOptions::default())?;\n\n        Ok(JsString::from(result).into())\n    }\n\n    /// 7.3.24 `Temporal.Duration.prototype.toLocaleString ( )`\n    ///\n    /// More information:\n    ///\n    /// - [ECMAScript Temporal proposal][spec]\n    /// - [MDN reference][mdn]\n    ///\n    /// [spec]: https://tc39.es/proposal-temporal/#sec-temporal.duration.prototype.tolocalestring\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Temporal/Duration/toLocaleString\n    pub(crate) fn to_locale_string(\n        this: &JsValue,\n        _: &[JsValue],\n        _: &mut Context,\n    ) -> JsResult<JsValue> {\n        // TODO: Update for ECMA-402 compliance\n        let object = this.as_object();\n        let duration = object\n            .as_ref()\n            .and_then(JsObject::downcast_ref::<Self>)\n            .ok_or_else(|| {\n                JsNativeError::typ().with_message(\"this value must be a Duration object.\")\n            })?;\n\n        let result = duration\n            .inner\n            .as_temporal_string(ToStringRoundingOptions::default())?;\n\n        Ok(JsString::from(result).into())\n    }\n\n    /// 7.3.25 `Temporal.Duration.prototype.valueOf ( )`\n    ///\n    /// More information:\n    ///\n    /// - [ECMAScript Temporal proposal][spec]\n    /// - [MDN reference][mdn]\n    ///\n    /// [spec]: https://tc39.es/proposal-temporal/#sec-temporal.duration.prototype.valueof\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Temporal/Duration/valueOf\n    pub(crate) fn value_of(_this: &JsValue, _: &[JsValue], _: &mut Context) -> JsResult<JsValue> {\n        Err(JsNativeError::typ()\n            .with_message(\"`valueOf` not supported by Temporal built-ins. See 'compare', 'equals', or `toString`\")\n            .into())\n    }\n}\n\n// -- Duration Abstract Operations --\n\n/// 7.5.12 `ToTemporalDuration ( item )`\npub(crate) fn to_temporal_duration(\n    item: &JsValue,\n    context: &mut Context,\n) -> JsResult<InnerDuration> {\n    // 1a. If Type(item) is Object\n    // 1b. and item has an [[InitializedTemporalDuration]] internal slot, then\n    let object = item.as_object();\n    if let Some(duration) = object.as_ref().and_then(JsObject::downcast_ref::<Duration>) {\n        return Ok(*duration.inner);\n    }\n\n    // 2. Let result be ? ToTemporalDurationRecord(item).\n    let result = to_temporal_duration_record(item, context)?;\n    // 3. Return ! CreateTemporalDuration(result.[[Years]], result.[[Months]], result.[[Weeks]], result.[[Days]], result.[[Hours]], result.[[Minutes]], result.[[Seconds]], result.[[Milliseconds]], result.[[Microseconds]], result.[[Nanoseconds]]).\n    Ok(result)\n}\n\n/// 7.5.13 `ToTemporalDurationRecord ( temporalDurationLike )`\npub(crate) fn to_temporal_duration_record(\n    temporal_duration_like: &JsValue,\n    context: &mut Context,\n) -> JsResult<InnerDuration> {\n    // 1. If Type(temporalDurationLike) is not Object, then\n    let Some(duration_obj) = temporal_duration_like.as_object() else {\n        // a. If temporalDurationLike is not a String, throw a TypeError exception.\n        let Some(duration_string) = temporal_duration_like.as_string() else {\n            return Err(JsNativeError::typ()\n                .with_message(\"Invalid TemporalDurationLike value.\")\n                .into());\n        };\n\n        // b. Return ? ParseTemporalDurationString(temporalDurationLike).\n        return duration_string\n            .to_std_string_escaped()\n            .parse::<InnerDuration>()\n            .map_err(Into::into);\n    };\n\n    // 2. If temporalDurationLike has an [[InitializedTemporalDuration]] internal slot, then\n    if let Some(duration) = duration_obj.downcast_ref::<Duration>() {\n        // a. Return ! CreateDurationRecord(temporalDurationLike.[[Years]], temporalDurationLike.[[Months]], temporalDurationLike.[[Weeks]], temporalDurationLike.[[Days]], temporalDurationLike.[[Hours]], temporalDurationLike.[[Minutes]], temporalDurationLike.[[Seconds]], temporalDurationLike.[[Milliseconds]], temporalDurationLike.[[Microseconds]], temporalDurationLike.[[Nanoseconds]]).\n        return Ok(*duration.inner);\n    }\n\n    // 3. Let result be a new Duration Record with each field set to 0.\n    // 4. Let partial be ? ToTemporalPartialDurationRecord(temporalDurationLike).\n    let partial = to_temporal_partial_duration(temporal_duration_like, context)?;\n\n    // 5. If partial.[[Years]] is not undefined, set result.[[Years]] to partial.[[Years]].\n\n    // 6. If partial.[[Months]] is not undefined, set result.[[Months]] to partial.[[Months]].\n    // 7. If partial.[[Weeks]] is not undefined, set result.[[Weeks]] to partial.[[Weeks]].\n    // 8. If partial.[[Days]] is not undefined, set result.[[Days]] to partial.[[Days]].\n    // 9. If partial.[[Hours]] is not undefined, set result.[[Hours]] to partial.[[Hours]].\n    // 10. If partial.[[Minutes]] is not undefined, set result.[[Minutes]] to partial.[[Minutes]].\n    // 11. If partial.[[Seconds]] is not undefined, set result.[[Seconds]] to partial.[[Seconds]].\n    // 12. If partial.[[Milliseconds]] is not undefined, set result.[[Milliseconds]] to partial.[[Milliseconds]].\n    // 13. If partial.[[Microseconds]] is not undefined, set result.[[Microseconds]] to partial.[[Microseconds]].\n    // 14. If partial.[[Nanoseconds]] is not undefined, set result.[[Nanoseconds]] to partial.[[Nanoseconds]].\n    // 15. If ! IsValidDuration(result.[[Years]], result.[[Months]], result.[[Weeks]], result.[[Days]], result.[[Hours]], result.[[Minutes]], result.[[Seconds]], result.[[Milliseconds]], result.[[Microseconds]], result.[[Nanoseconds]]) is false, then\n    // a. Throw a RangeError exception.\n    // 16. Return result.\n    InnerDuration::from_partial_duration(partial).map_err(Into::into)\n}\n\n/// 7.5.14 `CreateTemporalDuration ( years, months, weeks, days, hours, minutes, seconds, milliseconds, microseconds, nanoseconds [ , newTarget ] )`\npub(crate) fn create_temporal_duration(\n    inner: InnerDuration,\n    new_target: Option<&JsValue>,\n    context: &mut Context,\n) -> JsResult<JsObject> {\n    // 1. If ! IsValidDuration(years, months, weeks, days, hours, minutes, seconds, milliseconds, microseconds, nanoseconds) is false, throw a RangeError exception.\n\n    // 2. If newTarget is not present, set newTarget to %Temporal.Duration%.\n    let new_target = if let Some(target) = new_target {\n        target.clone()\n    } else {\n        context\n            .realm()\n            .intrinsics()\n            .constructors()\n            .duration()\n            .constructor()\n            .into()\n    };\n\n    // 3. Let object be ? OrdinaryCreateFromConstructor(newTarget, \"%Temporal.Duration.prototype%\", « [[InitializedTemporalDuration]], [[Years]], [[Months]], [[Weeks]], [[Days]], [[Hours]], [[Minutes]], [[Seconds]], [[Milliseconds]], [[Microseconds]], [[Nanoseconds]] »).\n    let prototype =\n        get_prototype_from_constructor(&new_target, StandardConstructors::duration, context)?;\n\n    // 4. Set object.[[Years]] to ℝ(𝔽(years)).\n    // 5. Set object.[[Months]] to ℝ(𝔽(months)).\n    // 6. Set object.[[Weeks]] to ℝ(𝔽(weeks)).\n    // 7. Set object.[[Days]] to ℝ(𝔽(days)).\n    // 8. Set object.[[Hours]] to ℝ(𝔽(hours)).\n    // 9. Set object.[[Minutes]] to ℝ(𝔽(minutes)).\n    // 10. Set object.[[Seconds]] to ℝ(𝔽(seconds)).\n    // 11. Set object.[[Milliseconds]] to ℝ(𝔽(milliseconds)).\n    // 12. Set object.[[Microseconds]] to ℝ(𝔽(microseconds)).\n    // 13. Set object.[[Nanoseconds]] to ℝ(𝔽(nanoseconds)).\n\n    let obj = JsObject::from_proto_and_data(prototype, Duration::new(inner));\n    // 14. Return object.\n    Ok(obj)\n}\n\n/// Equivalent to 7.5.13 `ToTemporalPartialDurationRecord ( temporalDurationLike )`\npub(crate) fn to_temporal_partial_duration(\n    duration_like: &JsValue,\n    context: &mut Context,\n) -> JsResult<PartialDuration> {\n    // 1. If Type(temporalDurationLike) is not Object, then\n    let Some(unknown_object) = duration_like.as_object() else {\n        // a. Throw a TypeError exception.\n        return Err(JsNativeError::typ()\n            .with_message(\"temporalDurationLike must be an object.\")\n            .into());\n    };\n\n    // 2. Let result be a new partial Duration Record with each field set to undefined.\n    // 3. NOTE: The following steps read properties and perform independent validation in alphabetical order.\n    // 4. Let days be ? Get(temporalDurationLike, \"days\").\n    // 5. If days is not undefined, set result.[[Days]] to ? ToIntegerIfIntegral(days).\n    let days = unknown_object\n        .get(js_string!(\"days\"), context)?\n        .map(|v| {\n            let finite = v.to_finitef64(context)?;\n            finite\n                .as_integer_if_integral::<i64>()\n                .map_err(JsError::from)\n        })\n        .transpose()?;\n\n    // 6. Let hours be ? Get(temporalDurationLike, \"hours\").\n    // 7. If hours is not undefined, set result.[[Hours]] to ? ToIntegerIfIntegral(hours).\n    let hours = unknown_object\n        .get(js_string!(\"hours\"), context)?\n        .map(|v| {\n            let finite = v.to_finitef64(context)?;\n            finite\n                .as_integer_if_integral::<i64>()\n                .map_err(JsError::from)\n        })\n        .transpose()?;\n\n    // 8. Let microseconds be ? Get(temporalDurationLike, \"microseconds\").\n    // 9. If microseconds is not undefined, set result.[[Microseconds]] to ? ToIntegerIfIntegral(microseconds).\n    let microseconds = unknown_object\n        .get(js_string!(\"microseconds\"), context)?\n        .map(|v| {\n            let finite = v.to_finitef64(context)?;\n            finite\n                .as_integer_if_integral::<i128>()\n                .map_err(JsError::from)\n        })\n        .transpose()?;\n\n    // 10. Let milliseconds be ? Get(temporalDurationLike, \"milliseconds\").\n    // 11. If milliseconds is not undefined, set result.[[Milliseconds]] to ? ToIntegerIfIntegral(milliseconds).\n    let milliseconds = unknown_object\n        .get(js_string!(\"milliseconds\"), context)?\n        .map(|v| {\n            let finite = v.to_finitef64(context)?;\n            finite\n                .as_integer_if_integral::<i64>()\n                .map_err(JsError::from)\n        })\n        .transpose()?;\n\n    // 12. Let minutes be ? Get(temporalDurationLike, \"minutes\").\n    // 13. If minutes is not undefined, set result.[[Minutes]] to ? ToIntegerIfIntegral(minutes).\n    let minutes = unknown_object\n        .get(js_string!(\"minutes\"), context)?\n        .map(|v| {\n            let finite = v.to_finitef64(context)?;\n            finite\n                .as_integer_if_integral::<i64>()\n                .map_err(JsError::from)\n        })\n        .transpose()?;\n\n    // 14. Let months be ? Get(temporalDurationLike, \"months\").\n    // 15. If months is not undefined, set result.[[Months]] to ? ToIntegerIfIntegral(months).\n    let months = unknown_object\n        .get(js_string!(\"months\"), context)?\n        .map(|v| {\n            let finite = v.to_finitef64(context)?;\n            finite\n                .as_integer_if_integral::<i64>()\n                .map_err(JsError::from)\n        })\n        .transpose()?;\n\n    // 16. Let nanoseconds be ? Get(temporalDurationLike, \"nanoseconds\").\n    // 17. If nanoseconds is not undefined, set result.[[Nanoseconds]] to ? ToIntegerIfIntegral(nanoseconds).\n    let nanoseconds = unknown_object\n        .get(js_string!(\"nanoseconds\"), context)?\n        .map(|v| {\n            let finite = v.to_finitef64(context)?;\n            finite\n                .as_integer_if_integral::<i128>()\n                .map_err(JsError::from)\n        })\n        .transpose()?;\n\n    // 18. Let seconds be ? Get(temporalDurationLike, \"seconds\").\n    // 19. If seconds is not undefined, set result.[[Seconds]] to ? ToIntegerIfIntegral(seconds).\n    let seconds = unknown_object\n        .get(js_string!(\"seconds\"), context)?\n        .map(|v| {\n            let finite = v.to_finitef64(context)?;\n            finite\n                .as_integer_if_integral::<i64>()\n                .map_err(JsError::from)\n        })\n        .transpose()?;\n\n    // 20. Let weeks be ? Get(temporalDurationLike, \"weeks\").\n    // 21. If weeks is not undefined, set result.[[Weeks]] to ? ToIntegerIfIntegral(weeks).\n    let weeks = unknown_object\n        .get(js_string!(\"weeks\"), context)?\n        .map(|v| {\n            let finite = v.to_finitef64(context)?;\n            finite\n                .as_integer_if_integral::<i64>()\n                .map_err(JsError::from)\n        })\n        .transpose()?;\n\n    // 22. Let years be ? Get(temporalDurationLike, \"years\").\n    // 23. If years is not undefined, set result.[[Years]] to ? ToIntegerIfIntegral(years).\n    let years = unknown_object\n        .get(js_string!(\"years\"), context)?\n        .map(|v| {\n            let finite = v.to_finitef64(context)?;\n            finite\n                .as_integer_if_integral::<i64>()\n                .map_err(JsError::from)\n        })\n        .transpose()?;\n\n    let partial = PartialDuration {\n        years,\n        months,\n        weeks,\n        days,\n        hours,\n        minutes,\n        seconds,\n        milliseconds,\n        microseconds,\n        nanoseconds,\n    };\n\n    // 24. If years is undefined, and months is undefined, and weeks is undefined, and days\n    // is undefined, and hours is undefined, and minutes is undefined, and seconds is\n    // undefined, and milliseconds is undefined, and microseconds is undefined, and\n    // nanoseconds is undefined, throw a TypeError exception.\n    if partial.is_empty() {\n        return Err(JsNativeError::typ()\n            .with_message(\"PartialDurationRecord must have a defined field.\")\n            .into());\n    }\n\n    // 25. Return result.\n    Ok(partial)\n}\n"
  },
  {
    "path": "core/engine/src/builtins/temporal/duration/tests.rs",
    "content": "use crate::{TestAction, run_test_actions};\n\n#[test]\nfn duration_constructor() {\n    run_test_actions([\n        TestAction::run(\"let dur = new Temporal.Duration(1, 1, 0, 1)\"),\n        TestAction::assert_eq(\"dur.years\", 1),\n        TestAction::assert_eq(\"dur.months\", 1),\n        TestAction::assert_eq(\"dur.weeks\", 0),\n        TestAction::assert_eq(\"dur.days\", 1),\n        TestAction::assert_eq(\"dur.milliseconds\", 0),\n    ]);\n}\n\n#[test]\nfn duration_abs() {\n    run_test_actions([\n        TestAction::run(\"let dur = new Temporal.Duration(-1, -1, 0, -1)\"),\n        TestAction::assert_eq(\"dur.sign\", -1),\n        TestAction::run(\"let abs = dur.abs()\"),\n        TestAction::assert_eq(\"abs.years\", 1),\n        TestAction::assert_eq(\"abs.months\", 1),\n        TestAction::assert_eq(\"abs.weeks\", 0),\n        TestAction::assert_eq(\"abs.days\", 1),\n        TestAction::assert_eq(\"abs.milliseconds\", 0),\n    ]);\n}\n\n#[test]\nfn basic() {\n    run_test_actions([\n        TestAction::run(\n            r#\"\n            var dur = new Temporal.Duration(5, 5, 5, 5, 5, 5, 5, 5, 5, 0);\n        \"#,\n        ),\n        TestAction::assert_eq(\"dur.years\", 5),\n        TestAction::assert_eq(\"dur.months\", 5),\n        TestAction::assert_eq(\"dur.weeks\", 5),\n        TestAction::assert_eq(\"dur.days\", 5),\n        TestAction::assert_eq(\"dur.hours\", 5),\n        TestAction::assert_eq(\"dur.minutes\", 5),\n        TestAction::assert_eq(\"dur.seconds\", 5),\n        TestAction::assert_eq(\"dur.milliseconds\", 5),\n        TestAction::assert_eq(\"dur.microseconds\", 5),\n        TestAction::assert_eq(\"dur.nanoseconds\", 0),\n        // Negative\n        TestAction::run(\"dur = new Temporal.Duration(-5, -5, -5, -5, -5, -5, -5, -5, -5, 0)\"),\n        TestAction::assert_eq(\"dur.years\", -5),\n        TestAction::assert_eq(\"dur.months\", -5),\n        TestAction::assert_eq(\"dur.weeks\", -5),\n        TestAction::assert_eq(\"dur.days\", -5),\n        TestAction::assert_eq(\"dur.hours\", -5),\n        TestAction::assert_eq(\"dur.minutes\", -5),\n        TestAction::assert_eq(\"dur.seconds\", -5),\n        TestAction::assert_eq(\"dur.milliseconds\", -5),\n        TestAction::assert_eq(\"dur.microseconds\", -5),\n        TestAction::assert_eq(\"dur.nanoseconds\", 0),\n        // Negative Zero\n        TestAction::run(\"dur = new Temporal.Duration(-0, -0, -0, -0, -0, -0, -0, -0, -0, 0)\"),\n        TestAction::assert_eq(\"dur.years\", 0),\n        TestAction::assert_eq(\"dur.months\", 0),\n        TestAction::assert_eq(\"dur.weeks\", 0),\n        TestAction::assert_eq(\"dur.days\", 0),\n        TestAction::assert_eq(\"dur.hours\", 0),\n        TestAction::assert_eq(\"dur.minutes\", 0),\n        TestAction::assert_eq(\"dur.seconds\", 0),\n        TestAction::assert_eq(\"dur.milliseconds\", 0),\n        TestAction::assert_eq(\"dur.microseconds\", 0),\n        TestAction::assert_eq(\"dur.nanoseconds\", 0),\n    ]);\n}\n"
  },
  {
    "path": "core/engine/src/builtins/temporal/error.rs",
    "content": "use temporal_rs::error::{ErrorKind, TemporalError};\n\nuse crate::{JsError, JsNativeError};\n\nimpl From<TemporalError> for JsNativeError {\n    #[cfg_attr(feature = \"native-backtrace\", track_caller)]\n    fn from(value: TemporalError) -> Self {\n        match value.kind() {\n            ErrorKind::Range | ErrorKind::Syntax => {\n                JsNativeError::range().with_message(value.into_message().to_owned())\n            }\n            ErrorKind::Type => JsNativeError::typ().with_message(value.into_message().to_owned()),\n            ErrorKind::Generic => {\n                JsNativeError::error().with_message(value.into_message().to_owned())\n            }\n            ErrorKind::Assert => JsNativeError::error().with_message(\"internal engine error\"),\n        }\n    }\n}\n\nimpl From<TemporalError> for JsError {\n    #[cfg_attr(feature = \"native-backtrace\", track_caller)]\n    fn from(value: TemporalError) -> Self {\n        let native: JsNativeError = value.into();\n        native.into()\n    }\n}\n"
  },
  {
    "path": "core/engine/src/builtins/temporal/instant/mod.rs",
    "content": "//! Boa's implementation of ECMAScript's `Temporal.Instant` built-in object.\n\nuse super::options::{get_difference_settings, get_digits_option};\nuse super::{create_temporal_zoneddatetime, to_temporal_timezone_identifier};\nuse crate::js_error;\nuse crate::value::JsVariant;\nuse crate::{\n    Context, JsArgs, JsBigInt, JsData, JsNativeError, JsObject, JsResult, JsString, JsSymbol,\n    JsValue,\n    builtins::{\n        BuiltInBuilder, BuiltInConstructor, BuiltInObject, IntrinsicObject,\n        options::{get_option, get_options_object},\n        temporal::{\n            ZonedDateTime,\n            duration::{create_temporal_duration, to_temporal_duration_record},\n            options::{TemporalUnitGroup, get_temporal_unit},\n        },\n    },\n    context::intrinsics::{Intrinsics, StandardConstructor, StandardConstructors},\n    js_string,\n    object::internal_methods::get_prototype_from_constructor,\n    property::Attribute,\n    realm::Realm,\n    string::StaticJsStrings,\n    value::PreferredType,\n};\nuse boa_gc::{Finalize, Trace};\nuse num_traits::ToPrimitive;\nuse temporal_rs::options::{ToStringRoundingOptions, Unit};\nuse temporal_rs::{\n    Instant as InnerInstant,\n    options::{RoundingIncrement, RoundingMode, RoundingOptions},\n};\n\n/// The `Temporal.Instant` built-in implementation\n///\n/// More information:\n///\n/// - [ECMAScript Temporal proposal][spec]\n/// - [MDN reference][mdn]\n/// - [`temporal_rs` documentation][temporal_rs-docs]\n///\n/// [spec]: https://tc39.es/proposal-temporal/#sec-temporal-instant-objects\n/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Temporal/Instant\n/// [temporal_rs-docs]: https://docs.rs/temporal_rs/latest/temporal_rs/struct.Instant.html\n#[derive(Debug, Clone, Trace, Finalize, JsData)]\n#[boa_gc(unsafe_empty_trace)] // Safety: Does not contain any traceable fields.\npub struct Instant {\n    pub(crate) inner: Box<InnerInstant>,\n}\n\nimpl Instant {\n    pub(crate) fn new(inner: InnerInstant) -> Self {\n        Self {\n            inner: Box::new(inner),\n        }\n    }\n}\n\nimpl BuiltInObject for Instant {\n    const NAME: JsString = StaticJsStrings::INSTANT_NAME;\n}\n\nimpl IntrinsicObject for Instant {\n    fn init(realm: &Realm) {\n        let get_millis = BuiltInBuilder::callable(realm, Self::get_epoch_milliseconds)\n            .name(js_string!(\"get epochMilliseconds\"))\n            .build();\n\n        let get_nanos = BuiltInBuilder::callable(realm, Self::get_epoch_nanoseconds)\n            .name(js_string!(\"get epochNanoseconds\"))\n            .build();\n\n        BuiltInBuilder::from_standard_constructor::<Self>(realm)\n            .property(\n                JsSymbol::to_string_tag(),\n                StaticJsStrings::INSTANT_TAG,\n                Attribute::CONFIGURABLE,\n            )\n            .accessor(\n                js_string!(\"epochMilliseconds\"),\n                Some(get_millis),\n                None,\n                Attribute::CONFIGURABLE,\n            )\n            .accessor(\n                js_string!(\"epochNanoseconds\"),\n                Some(get_nanos),\n                None,\n                Attribute::CONFIGURABLE,\n            )\n            .static_method(Self::from, js_string!(\"from\"), 1)\n            .static_method(\n                Self::from_epoch_milliseconds,\n                js_string!(\"fromEpochMilliseconds\"),\n                1,\n            )\n            .static_method(\n                Self::from_epoch_nanoseconds,\n                js_string!(\"fromEpochNanoseconds\"),\n                1,\n            )\n            .static_method(Self::compare, js_string!(\"compare\"), 2)\n            .method(Self::add, js_string!(\"add\"), 1)\n            .method(Self::subtract, js_string!(\"subtract\"), 1)\n            .method(Self::until, js_string!(\"until\"), 1)\n            .method(Self::since, js_string!(\"since\"), 1)\n            .method(Self::round, js_string!(\"round\"), 1)\n            .method(Self::equals, js_string!(\"equals\"), 1)\n            .method(Self::to_string, js_string!(\"toString\"), 0)\n            .method(Self::to_locale_string, js_string!(\"toLocaleString\"), 0)\n            .method(Self::to_json, js_string!(\"toJSON\"), 0)\n            .method(Self::value_of, js_string!(\"valueOf\"), 0)\n            .method(\n                Self::to_zoned_date_time_iso,\n                js_string!(\"toZonedDateTimeISO\"),\n                1,\n            )\n            .build();\n    }\n\n    fn get(intrinsics: &Intrinsics) -> JsObject {\n        Self::STANDARD_CONSTRUCTOR(intrinsics.constructors()).constructor()\n    }\n}\n\nimpl BuiltInConstructor for Instant {\n    const CONSTRUCTOR_ARGUMENTS: usize = 1;\n    const PROTOTYPE_STORAGE_SLOTS: usize = 16;\n    const CONSTRUCTOR_STORAGE_SLOTS: usize = 4;\n\n    const STANDARD_CONSTRUCTOR: fn(&StandardConstructors) -> &StandardConstructor =\n        StandardConstructors::instant;\n\n    fn constructor(\n        new_target: &JsValue,\n        args: &[JsValue],\n        context: &mut Context,\n    ) -> JsResult<JsValue> {\n        // 1. If NewTarget is undefined, then\n        if new_target.is_undefined() {\n            // a. Throw a TypeError exception.\n            return Err(JsNativeError::typ()\n                .with_message(\"Temporal.Instant new target cannot be undefined.\")\n                .into());\n        }\n\n        // 2. Let epochNanoseconds be ? ToBigInt(epochNanoseconds).\n        let epoch_nanos = args.get_or_undefined(0).to_bigint(context)?;\n\n        // 3. If ! IsValidEpochNanoseconds(epochNanoseconds) is false, throw a RangeError exception.\n        // NOTE: temporal_rs::Instant asserts that the epochNanoseconds are valid.\n        let instant = InnerInstant::try_new(epoch_nanos.as_inner().to_i128().unwrap_or(i128::MAX))?;\n        // 4. Return ? CreateTemporalInstant(epochNanoseconds, NewTarget).\n        create_temporal_instant(instant, Some(new_target.clone()), context)\n    }\n}\n\n// ==== Instant static methods implementation ====\n\nimpl Instant {\n    /// 8.2.2 Temporal.Instant.from ( item )\n    ///\n    /// More information:\n    ///\n    /// - [ECMAScript Temporal proposal][spec]\n    /// - [MDN reference][mdn]\n    ///\n    /// [spec]: https://tc39.es/proposal-temporal/#sec-temporal.instant.from\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Temporal/Instant/from\n    pub(crate) fn from(_: &JsValue, args: &[JsValue], context: &mut Context) -> JsResult<JsValue> {\n        // 1. If item is an Object and item has an [[InitializedTemporalInstant]] internal slot, then\n        // a. Return ! CreateTemporalInstant(item.[[Nanoseconds]]).\n        // 2. Return ? ToTemporalInstant(item).\n        create_temporal_instant(\n            to_temporal_instant(args.get_or_undefined(0), context)?,\n            None,\n            context,\n        )\n    }\n\n    /// 8.2.3 `Temporal.Instant.fromEpochMilliseconds ( epochMilliseconds )`\n    ///\n    /// More information:\n    ///\n    /// - [ECMAScript Temporal proposal][spec]\n    /// - [MDN reference][mdn]\n    /// - [`temporal_rs` documentation][temporal_rs-docs]\n    ///\n    /// [spec]: https://tc39.es/proposal-temporal/#sec-temporal.instant.from\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Temporal/Instant/from\n    /// [temporal_rs-docs]: https://docs.rs/temporal_rs/latest/temporal_rs/struct.Instant.html#method.from_epoch_milliseconds\n    pub(crate) fn from_epoch_milliseconds(\n        _: &JsValue,\n        args: &[JsValue],\n        context: &mut Context,\n    ) -> JsResult<JsValue> {\n        // 1. Set epochMilliseconds to ? ToNumber(epochMilliseconds).\n        let epoch_millis_f64 = args.get_or_undefined(0).to_number(context)?;\n        // NOTE: inline NumberToBigInt. It checks if the number is integral\n        // 2. Set epochMilliseconds to ? NumberToBigInt(epochMilliseconds).\n        if !epoch_millis_f64.is_finite() || epoch_millis_f64.fract() != 0.0 {\n            return Err(js_error!(RangeError: \"number is not integral\"));\n        }\n        // 3. Let epochNanoseconds be epochMilliseconds × ℤ(10**6).\n        // 4. If IsValidEpochNanoseconds(epochNanoseconds) is false, throw a RangeError exception.\n        // 5. Return ! CreateTemporalInstant(epochNanoseconds).\n        create_temporal_instant(\n            InnerInstant::from_epoch_milliseconds(epoch_millis_f64.to_i64().unwrap_or(i64::MAX))?,\n            None,\n            context,\n        )\n    }\n\n    /// 8.2.4 `Temporal.Instant.fromEpochNanoseconds ( epochNanoseconds )`\n    ///\n    /// More information:\n    ///\n    /// - [ECMAScript Temporal proposal][spec]\n    /// - [MDN reference][mdn]\n    /// - [`temporal_rs` documentation][temporal_rs-docs]\n    ///\n    /// [spec]: https://tc39.es/proposal-temporal/#sec-temporal.instant.from\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Temporal/Instant/from\n    /// [temporal_rs-docs]: https://docs.rs/temporal_rs/latest/temporal_rs/struct.Instant.html#method.try_new\n    pub(crate) fn from_epoch_nanoseconds(\n        _: &JsValue,\n        args: &[JsValue],\n        context: &mut Context,\n    ) -> JsResult<JsValue> {\n        // 1. Set epochNanoseconds to ? ToBigInt(epochNanoseconds).\n        let epoch_nanos = args.get_or_undefined(0).to_bigint(context)?;\n        // 2. If IsValidEpochNanoseconds(epochNanoseconds) is false, throw a RangeError exception.\n        // 3. Return ! CreateTemporalInstant(epochNanoseconds).\n        let nanos = epoch_nanos.as_inner().to_i128();\n        create_temporal_instant(\n            InnerInstant::try_new(nanos.unwrap_or(i128::MAX))?,\n            None,\n            context,\n        )\n    }\n\n    /// 8.2.5 Temporal.Instant.compare ( one, two )\n    ///\n    /// More information:\n    ///\n    /// - [ECMAScript Temporal proposal][spec]\n    /// - [MDN reference][mdn]\n    /// - [`temporal_rs` documentation][temporal_rs-docs]\n    ///\n    /// [spec]: https://tc39.es/proposal-temporal/#sec-temporal.instant.compare\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Temporal/Instant/compare\n    /// [temporal_rs-docs]: https://docs.rs/temporal_rs/latest/temporal_rs/struct.Instant.html#impl-PartialOrd-for-Instant\n    pub(crate) fn compare(\n        _: &JsValue,\n        args: &[JsValue],\n        context: &mut Context,\n    ) -> JsResult<JsValue> {\n        // 1. Set one to ? ToTemporalInstant(one).\n        let one = to_temporal_instant(args.get_or_undefined(0), context)?;\n        // 2. Set two to ? ToTemporalInstant(two).\n        let two = to_temporal_instant(args.get_or_undefined(1), context)?;\n        // 3. Return 𝔽(CompareEpochNanoseconds(one.[[Nanoseconds]], two.[[Nanoseconds]])).\n        Ok((one.cmp(&two) as i8).into())\n    }\n}\n\n// ==== Instant accessors implementation ====\n\nimpl Instant {\n    /// 8.3.4 get Temporal.Instant.prototype.epochMilliseconds\n    ///\n    /// More information:\n    ///\n    /// - [ECMAScript Temporal proposal][spec]\n    /// - [MDN reference][mdn]\n    /// - [`temporal_rs` documentation][temporal_rs-docs]\n    ///\n    /// [spec]: https://tc39.es/proposal-temporal/#sec-get-temporal.instant.epochmilliseconds\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Temporal/Instant/epochmilliseconds\n    /// [temporal_rs-docs]: https://docs.rs/temporal_rs/latest/temporal_rs/struct.Instant.html#method.epoch_milliseconds\n    pub(crate) fn get_epoch_milliseconds(\n        this: &JsValue,\n        _: &[JsValue],\n        _: &mut Context,\n    ) -> JsResult<JsValue> {\n        // 1. Let instant be the this value.\n        // 2. Perform ? RequireInternalSlot(instant, [[InitializedTemporalInstant]]).\n        let object = this.as_object();\n        let instant = object\n            .as_ref()\n            .and_then(JsObject::downcast_ref::<Self>)\n            .ok_or_else(|| {\n                JsNativeError::typ().with_message(\"the this object must be an instant object.\")\n            })?;\n        // 3. Let ns be instant.[[Nanoseconds]].\n        // 4. Let ms be floor(ℝ(ns) / 10^6).\n        // 5. Return 𝔽(ms).\n        Ok(instant.inner.epoch_milliseconds().into())\n    }\n\n    /// 8.3.6 get Temporal.Instant.prototype.epochNanoseconds\n    ///\n    /// More information:\n    ///\n    /// - [ECMAScript Temporal proposal][spec]\n    /// - [MDN reference][mdn]\n    /// - [`temporal_rs` documentation][temporal_rs-docs]\n    ///\n    /// [spec]: https://tc39.es/proposal-temporal/#sec-get-temporal.instant.epochnanoseconds\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Temporal/Instant/epochNanoseconds\n    /// [temporal_rs-docs]: https://docs.rs/temporal_rs/latest/temporal_rs/struct.Instant.html#method.epoch_nanoseconds\n    pub(crate) fn get_epoch_nanoseconds(\n        this: &JsValue,\n        _: &[JsValue],\n        _: &mut Context,\n    ) -> JsResult<JsValue> {\n        // 1. Let instant be the this value.\n        // 2. Perform ? RequireInternalSlot(instant, [[InitializedTemporalInstant]]).\n        let object = this.as_object();\n        let instant = object\n            .as_ref()\n            .and_then(JsObject::downcast_ref::<Self>)\n            .ok_or_else(|| {\n                JsNativeError::typ().with_message(\"the this object must be an instant object.\")\n            })?;\n        // 3. Let ns be instant.[[Nanoseconds]].\n        // 4. Return ns.\n        Ok(JsBigInt::from(instant.inner.epoch_nanoseconds().as_i128()).into())\n    }\n}\n\n// ==== Instant methods implementation ====\n\nimpl Instant {\n    /// 8.3.7 `Temporal.Instant.prototype.add ( temporalDurationLike )`\n    ///\n    /// More information:\n    ///\n    /// - [ECMAScript Temporal proposal][spec]\n    /// - [MDN reference][mdn]\n    /// - [`temporal_rs` documentation][temporal_rs-docs]\n    ///\n    /// [spec]: https://tc39.es/proposal-temporal/#sec-temporal.instant.add\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Temporal/Instant/add\n    /// [temporal_rs-docs]: https://docs.rs/temporal_rs/latest/temporal_rs/struct.Instant.html#method.add\n    pub(crate) fn add(\n        this: &JsValue,\n        args: &[JsValue],\n        context: &mut Context,\n    ) -> JsResult<JsValue> {\n        // 1. Let instant be the this value.\n        // 2. Perform ? RequireInternalSlot(instant, [[InitializedTemporalInstant]]).\n        let object = this.as_object();\n        let instant = object\n            .as_ref()\n            .and_then(JsObject::downcast_ref::<Self>)\n            .ok_or_else(|| {\n                JsNativeError::typ().with_message(\"the this object must be an instant object.\")\n            })?;\n\n        // 3. Return ? AddDurationToOrSubtractDurationFromInstant(add, instant, temporalDurationLike).\n        let temporal_duration_like =\n            to_temporal_duration_record(args.get_or_undefined(0), context)?;\n        let result = instant.inner.add(&temporal_duration_like)?;\n        create_temporal_instant(result, None, context)\n    }\n\n    /// 8.3.8 `Temporal.Instant.prototype.subtract ( temporalDurationLike )`\n    ///\n    /// More information:\n    ///\n    /// - [ECMAScript Temporal proposal][spec]\n    /// - [MDN reference][mdn]\n    /// - [`temporal_rs` documentation][temporal_rs-docs]\n    ///\n    /// [spec]: https://tc39.es/proposal-temporal/#sec-temporal.instant.subtract\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Temporal/Instant/subtract\n    /// [temporal_rs-docs]: https://docs.rs/temporal_rs/latest/temporal_rs/struct.Instant.html#method.subtract\n    pub(crate) fn subtract(\n        this: &JsValue,\n        args: &[JsValue],\n        context: &mut Context,\n    ) -> JsResult<JsValue> {\n        // 1. Let instant be the this value.\n        // 2. Perform ? RequireInternalSlot(instant, [[InitializedTemporalInstant]]).\n        let object = this.as_object();\n        let instant = object\n            .as_ref()\n            .and_then(JsObject::downcast_ref::<Self>)\n            .ok_or_else(|| {\n                JsNativeError::typ().with_message(\"the this object must be an instant object.\")\n            })?;\n\n        // 3. Return ? AddDurationToOrSubtractDurationFromInstant(subtract, instant, temporalDurationLike).\n        let temporal_duration_like =\n            to_temporal_duration_record(args.get_or_undefined(0), context)?;\n        let result = instant.inner.subtract(&temporal_duration_like)?;\n        create_temporal_instant(result, None, context)\n    }\n\n    /// 8.3.9 `Temporal.Instant.prototype.until ( other [ , options ] )`\n    ///\n    /// More information:\n    ///\n    /// - [ECMAScript Temporal proposal][spec]\n    /// - [MDN reference][mdn]\n    /// - [`temporal_rs` documentation][temporal_rs-docs]\n    ///\n    /// [spec]: https://tc39.es/proposal-temporal/#sec-temporal.instant.until\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Temporal/Instant/until\n    /// [temporal_rs-docs]: https://docs.rs/temporal_rs/latest/temporal_rs/struct.Instant.html#method.until\n    pub(crate) fn until(\n        this: &JsValue,\n        args: &[JsValue],\n        context: &mut Context,\n    ) -> JsResult<JsValue> {\n        // 1. Let instant be the this value.\n        // 2. Perform ? RequireInternalSlot(instant, [[InitializedTemporalInstant]]).\n        let object = this.as_object();\n        let instant = object\n            .as_ref()\n            .and_then(JsObject::downcast_ref::<Self>)\n            .ok_or_else(|| {\n                JsNativeError::typ().with_message(\"the this object must be an instant object.\")\n            })?;\n\n        // 3. Return ? DifferenceTemporalInstant(until, instant, other, options).\n        let other = to_temporal_instant(args.get_or_undefined(0), context)?;\n\n        // Fetch the necessary options.\n        let settings =\n            get_difference_settings(&get_options_object(args.get_or_undefined(1))?, context)?;\n        let result = instant.inner.until(&other, settings)?;\n        create_temporal_duration(result, None, context).map(Into::into)\n    }\n\n    /// 8.3.10 `Temporal.Instant.prototype.since ( other [ , options ] )`\n    ///\n    /// More information:\n    ///\n    /// - [ECMAScript Temporal proposal][spec]\n    /// - [MDN reference][mdn]\n    /// - [`temporal_rs` documentation][temporal_rs-docs]\n    ///\n    /// [spec]: https://tc39.es/proposal-temporal/#sec-temporal.instant.since\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Temporal/Instant/since\n    /// [temporal_rs-docs]: https://docs.rs/temporal_rs/latest/temporal_rs/struct.Instant.html#method.since\n    pub(crate) fn since(\n        this: &JsValue,\n        args: &[JsValue],\n        context: &mut Context,\n    ) -> JsResult<JsValue> {\n        // 1. Let instant be the this value.\n        // 2. Perform ? RequireInternalSlot(instant, [[InitializedTemporalInstant]]).\n        let object = this.as_object();\n        let instant = object\n            .as_ref()\n            .and_then(JsObject::downcast_ref::<Self>)\n            .ok_or_else(|| {\n                JsNativeError::typ().with_message(\"the this object must be an instant object.\")\n            })?;\n\n        // 3. Return ? DifferenceTemporalInstant(since, instant, other, options).\n        let other = to_temporal_instant(args.get_or_undefined(0), context)?;\n        let settings =\n            get_difference_settings(&get_options_object(args.get_or_undefined(1))?, context)?;\n        let result = instant.inner.since(&other, settings)?;\n        create_temporal_duration(result, None, context).map(Into::into)\n    }\n\n    /// 8.3.11 `Temporal.Instant.prototype.round ( roundTo )`\n    ///\n    /// More information:\n    ///\n    /// - [ECMAScript Temporal proposal][spec]\n    /// - [MDN reference][mdn]\n    /// - [`temporal_rs` documentation][temporal_rs-docs]\n    ///\n    /// [spec]: https://tc39.es/proposal-temporal/#sec-temporal.instant.round\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Temporal/Instant/round\n    /// [temporal_rs-docs]: https://docs.rs/temporal_rs/latest/temporal_rs/struct.Instant.html#method.round\n    pub(crate) fn round(\n        this: &JsValue,\n        args: &[JsValue],\n        context: &mut Context,\n    ) -> JsResult<JsValue> {\n        // 1. Let instant be the this value.\n        // 2. Perform ? RequireInternalSlot(instant, [[InitializedTemporalInstant]]).\n        let object = this.as_object();\n        let instant = object\n            .as_ref()\n            .and_then(JsObject::downcast_ref::<Self>)\n            .ok_or_else(|| {\n                JsNativeError::typ().with_message(\"the this object must be an instant object.\")\n            })?;\n\n        let round_to = match args.first().map(JsValue::variant) {\n            // 3. If roundTo is undefined, then\n            None | Some(JsVariant::Undefined) => {\n                return Err(JsNativeError::typ()\n                    .with_message(\"roundTo cannot be undefined.\")\n                    .into());\n            }\n            // 4. If Type(roundTo) is String, then\n            Some(JsVariant::String(rt)) => {\n                // a. Let paramString be roundTo.\n                let param_string = rt.clone();\n                // b. Set roundTo to OrdinaryObjectCreate(null).\n                let new_round_to = JsObject::with_null_proto();\n                // c. Perform ! CreateDataPropertyOrThrow(roundTo, \"smallestUnit\", paramString).\n                new_round_to.create_data_property_or_throw(\n                    js_string!(\"smallestUnit\"),\n                    param_string,\n                    context,\n                )?;\n                new_round_to\n            }\n            // 5. Else,\n            Some(round_to) => {\n                // TODO: remove this clone.\n                // a. Set roundTo to ? GetOptionsObject(roundTo).\n                get_options_object(&JsValue::from(round_to))?\n            }\n        };\n\n        // 6. NOTE: The following steps read options and perform independent validation in\n        // alphabetical order (ToTemporalRoundingIncrement reads \"roundingIncrement\" and ToTemporalRoundingMode reads \"roundingMode\").\n        let mut options = RoundingOptions::default();\n        // 7. Let roundingIncrement be ? ToTemporalRoundingIncrement(roundTo).\n        options.increment =\n            get_option::<RoundingIncrement>(&round_to, js_string!(\"roundingIncrement\"), context)?;\n\n        // 8. Let roundingMode be ? ToTemporalRoundingMode(roundTo, \"halfExpand\").\n        options.rounding_mode =\n            get_option::<RoundingMode>(&round_to, js_string!(\"roundingMode\"), context)?;\n\n        // 9. Let smallestUnit be ? GetTemporalUnit(roundTo, \"smallestUnit\"), time, required).\n        let smallest_unit = get_temporal_unit(\n            &round_to,\n            js_string!(\"smallestUnit\"),\n            TemporalUnitGroup::Time,\n            None,\n            context,\n        )?\n        .ok_or_else(|| JsNativeError::range().with_message(\"smallestUnit cannot be undefined.\"))?;\n\n        options.smallest_unit = Some(smallest_unit);\n\n        // 10. If smallestUnit is \"hour\"), then\n        // a. Let maximum be HoursPerDay.\n        // 11. Else if smallestUnit is \"minute\"), then\n        // a. Let maximum be MinutesPerHour × HoursPerDay.\n        // 12. Else if smallestUnit is \"second\"), then\n        // a. Let maximum be SecondsPerMinute × MinutesPerHour × HoursPerDay.\n        // 13. Else if smallestUnit is \"millisecond\"), then\n        // a. Let maximum be ℝ(msPerDay).\n        // 14. Else if smallestUnit is \"microsecond\"), then\n        // a. Let maximum be 10^3 × ℝ(msPerDay).\n        // 15. Else,\n        // a. Assert: smallestUnit is \"nanosecond\".\n        // b. Let maximum be nsPerDay.\n        // unreachable here functions as 15.a.\n        // 16. Perform ? ValidateTemporalRoundingIncrement(roundingIncrement, maximum, true).\n        // 17. Let roundedNs be RoundTemporalInstant(instant.[[Nanoseconds]], roundingIncrement, smallestUnit, roundingMode).\n        let result = instant.inner.round(options)?;\n\n        // 18. Return ! CreateTemporalInstant(roundedNs).\n        create_temporal_instant(result, None, context)\n    }\n\n    /// 8.3.12 `Temporal.Instant.prototype.equals ( other )`\n    ///\n    /// More information:\n    ///\n    /// - [ECMAScript Temporal proposal][spec]\n    /// - [MDN reference][mdn]\n    /// - [`temporal_rs` documentation][temporal_rs-docs]\n    ///\n    /// [spec]: https://tc39.es/proposal-temporal/#sec-temporal.instant.equals\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Temporal/Instant/equals\n    /// [temporal_rs-docs]: https://docs.rs/temporal_rs/latest/temporal_rs/struct.Instant.html#impl-PartialEq-for-Instant\n    pub(crate) fn equals(\n        this: &JsValue,\n        args: &[JsValue],\n        context: &mut Context,\n    ) -> JsResult<JsValue> {\n        // 1. Let instant be the this value.\n        // 2. Perform ? RequireInternalSlot(instant, [[InitializedTemporalInstant]]).\n        // 4. If instant.[[Nanoseconds]] ≠ other.[[Nanoseconds]], return false.\n        // 5. Return true.\n        let object = this.as_object();\n        let instant = object\n            .as_ref()\n            .and_then(JsObject::downcast_ref::<Self>)\n            .ok_or_else(|| {\n                JsNativeError::typ().with_message(\"the this object must be an instant object.\")\n            })?;\n\n        // 3. Set other to ? ToTemporalInstant(other).\n        let other = args.get_or_undefined(0);\n        let other_instant = to_temporal_instant(other, context)?;\n\n        if *instant.inner != other_instant {\n            return Ok(false.into());\n        }\n        Ok(true.into())\n    }\n\n    /// 8.3.11 `Temporal.Instant.prototype.toString ( [ options ] )`\n    ///\n    /// More information:\n    ///\n    /// - [ECMAScript Temporal proposal][spec]\n    /// - [MDN reference][mdn]\n    /// - [`temporal_rs` documentation][temporal_rs-docs]\n    ///\n    /// [spec]: https://tc39.es/proposal-temporal/#sec-temporal.instant.tostring\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Temporal/Instant/toString\n    /// [temporal_rs-docs]: https://docs.rs/temporal_rs/latest/temporal_rs/struct.Instant.html#method.to_ixdtf_string\n    fn to_string(this: &JsValue, args: &[JsValue], context: &mut Context) -> JsResult<JsValue> {\n        let object = this.as_object();\n        let instant = object\n            .as_ref()\n            .and_then(JsObject::downcast_ref::<Self>)\n            .ok_or_else(|| {\n                JsNativeError::typ()\n                    .with_message(\"the this object must be a Temporal.Instant object.\")\n            })?;\n\n        let options = get_options_object(args.get_or_undefined(0))?;\n\n        let precision = get_digits_option(&options, context)?;\n        let rounding_mode =\n            get_option::<RoundingMode>(&options, js_string!(\"roundingMode\"), context)?;\n        let smallest_unit = get_option::<Unit>(&options, js_string!(\"smallestUnit\"), context)?;\n        // NOTE: There may be an order-of-operations here due to a check on Unit groups and smallest_unit value.\n        let timezone = options\n            .get(js_string!(\"timeZone\"), context)?\n            .map(|v| to_temporal_timezone_identifier(v, context))\n            .transpose()?;\n\n        let options = ToStringRoundingOptions {\n            precision,\n            smallest_unit,\n            rounding_mode,\n        };\n\n        let ixdtf = instant.inner.to_ixdtf_string_with_provider(\n            timezone,\n            options,\n            context.timezone_provider(),\n        )?;\n\n        Ok(JsString::from(ixdtf).into())\n    }\n\n    /// 8.3.12 `Temporal.Instant.prototype.toLocaleString ( [ locales [ , options ] ] )`\n    ///\n    /// More information:\n    ///\n    /// - [ECMAScript Temporal proposal][spec]\n    /// - [MDN reference][mdn]\n    ///\n    /// [spec]: https://tc39.es/proposal-temporal/#sec-temporal.instant.tolocalestring\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Temporal/Instant/toLocaleString\n    fn to_locale_string(this: &JsValue, _: &[JsValue], context: &mut Context) -> JsResult<JsValue> {\n        // TODO: Update for ECMA-402 compliance\n        let object = this.as_object();\n        let instant = object\n            .as_ref()\n            .and_then(JsObject::downcast_ref::<Self>)\n            .ok_or_else(|| {\n                JsNativeError::typ()\n                    .with_message(\"the this object must be a Temporal.Instant object.\")\n            })?;\n\n        let ixdtf = instant.inner.to_ixdtf_string_with_provider(\n            None,\n            ToStringRoundingOptions::default(),\n            context.timezone_provider(),\n        )?;\n        Ok(JsString::from(ixdtf).into())\n    }\n\n    /// 8.3.13 `Temporal.Instant.prototype.toJSON ( )`\n    ///\n    /// More information:\n    ///\n    /// - [ECMAScript Temporal proposal][spec]\n    /// - [MDN reference][mdn]\n    ///\n    /// [spec]: https://tc39.es/proposal-temporal/#sec-temporal.instant.tojson\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Temporal/Instant/toJSON\n    fn to_json(this: &JsValue, _: &[JsValue], context: &mut Context) -> JsResult<JsValue> {\n        let object = this.as_object();\n        let instant = object\n            .as_ref()\n            .and_then(JsObject::downcast_ref::<Self>)\n            .ok_or_else(|| {\n                JsNativeError::typ()\n                    .with_message(\"the this object must be a Temporal.Instant object.\")\n            })?;\n\n        let ixdtf = instant.inner.to_ixdtf_string_with_provider(\n            None,\n            ToStringRoundingOptions::default(),\n            context.timezone_provider(),\n        )?;\n        Ok(JsString::from(ixdtf).into())\n    }\n\n    /// 8.3.14 `Temporal.Instant.prototype.valueOf ( )`\n    ///\n    /// More information:\n    ///\n    /// - [ECMAScript Temporal proposal][spec]\n    /// - [MDN reference][mdn]\n    ///\n    /// [spec]: https://tc39.es/proposal-temporal/#sec-temporal.instant.valueof\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Temporal/Instant/valueOf\n    fn value_of(_this: &JsValue, _: &[JsValue], _: &mut Context) -> JsResult<JsValue> {\n        Err(JsNativeError::typ()\n            .with_message(\"`valueOf` not supported by Temporal built-ins. See 'compare', 'equals', or `toString`\")\n            .into())\n    }\n\n    /// 8.3.15 `Temporal.Instant.prototype.toZonedDateTimeISO ( timeZone )`\n    ///\n    /// More information:\n    ///\n    /// - [ECMAScript Temporal proposal][spec]\n    /// - [MDN reference][mdn]\n    /// - [`temporal_rs` documentation][temporal_rs-docs]\n    ///\n    /// [spec]: https://tc39.es/proposal-temporal/#sec-temporal.instant.tozoneddatetimeiso\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Temporal/Instant/toZonedDateTimeISO\n    /// [temporal_rs-docs]: https://docs.rs/temporal_rs/latest/temporal_rs/struct.Instant.html#method.to_zoned_date_time_iso\n    pub(crate) fn to_zoned_date_time_iso(\n        this: &JsValue,\n        args: &[JsValue],\n        context: &mut Context,\n    ) -> JsResult<JsValue> {\n        // 1. Let instant be the this value.\n        // 2. Perform ? RequireInternalSlot(instant, [[InitializedTemporalInstant]]).\n        let object = this.as_object();\n        let instant = object\n            .as_ref()\n            .and_then(JsObject::downcast_ref::<Self>)\n            .ok_or_else(|| {\n                JsNativeError::typ()\n                    .with_message(\"the this object must be a Temporal.Instant object.\")\n            })?;\n\n        // 3. Set timeZone to ? ToTemporalTimeZoneIdentifier(timeZone).\n        let timezone = to_temporal_timezone_identifier(args.get_or_undefined(0), context)?;\n\n        // 4. Return ! CreateTemporalZonedDateTime(instant.[[EpochNanoseconds]], timeZone, \"iso8601\").\n        let zdt = instant\n            .inner\n            .to_zoned_date_time_iso_with_provider(timezone, context.timezone_provider())?;\n        create_temporal_zoneddatetime(zdt, None, context).map(Into::into)\n    }\n}\n\n// ==== Instant Abstract Operations ====\n\n/// 8.5.2 `CreateTemporalInstant ( epochNanoseconds [ , newTarget ] )`\n#[inline]\npub(crate) fn create_temporal_instant(\n    instant: InnerInstant,\n    new_target: Option<JsValue>,\n    context: &mut Context,\n) -> JsResult<JsValue> {\n    // 1. Assert: ! IsValidEpochNanoseconds(epochNanoseconds) is true.\n    // 2. If newTarget is not present, set newTarget to %Temporal.Instant%.\n    let new_target = new_target.unwrap_or_else(|| {\n        context\n            .realm()\n            .intrinsics()\n            .constructors()\n            .instant()\n            .constructor()\n            .into()\n    });\n    // 3. Let object be ? OrdinaryCreateFromConstructor(newTarget, \"%Temporal.Instant.prototype%\"), « [[InitializedTemporalInstant]], [[Nanoseconds]] »).\n    let proto =\n        get_prototype_from_constructor(&new_target, StandardConstructors::instant, context)?;\n\n    // 4. Set object.[[Nanoseconds]] to epochNanoseconds.\n    let obj = JsObject::from_proto_and_data(proto, Instant::new(instant));\n\n    // 5. Return object.\n    Ok(obj.into())\n}\n\n/// 8.5.3 `ToTemporalInstant ( item )`\n#[inline]\nfn to_temporal_instant(item: &JsValue, context: &mut Context) -> JsResult<InnerInstant> {\n    // 1.If item is an Object, then\n    let item = if let Some(obj) = item.as_object() {\n        // a. If item has an [[InitializedTemporalInstant]] internal slot, then\n        //     i. Return item.\n        // b. If item has an [[InitializedTemporalZonedDateTime]] internal slot, then\n        //     i. Return ! CreateTemporalInstant(item.[[Nanoseconds]]).\n        // c. NOTE: This use of ToPrimitive allows Instant-like objects to be converted.\n        // d. Set item to ? ToPrimitive(item, string).\n        if let Some(instant) = obj.downcast_ref::<Instant>() {\n            return Ok(*instant.inner);\n        } else if let Some(zdt) = obj.downcast_ref::<ZonedDateTime>() {\n            return Ok(zdt.inner.to_instant());\n        }\n        item.to_primitive(context, PreferredType::String)?\n    } else {\n        item.clone()\n    };\n\n    let Some(string_to_parse) = item.as_string() else {\n        return Err(JsNativeError::typ()\n            .with_message(\"Invalid type to convert to a Temporal.Instant.\")\n            .into());\n    };\n\n    // 3. Let parsed be ? ParseTemporalInstantString(item).\n    // 4. If parsed.[[TimeZone]].[[Z]] is true, let offsetNanoseconds be 0; otherwise, let offsetNanoseconds be ! ParseDateTimeUTCOffset(parsed.[[TimeZone]].[[OffsetString]]).\n    // 5. If abs(ISODateToEpochDays(parsed.[[Year]], parsed.[[Month]] - 1, parsed.[[Day]])) > 10**8, throw a RangeError exception.\n    // 6. Let epochNanoseconds be GetUTCEpochNanoseconds(parsed.[[Year]], parsed.[[Month]], parsed.[[Day]], parsed.[[Hour]], parsed.[[Minute]], parsed.[[Second]], parsed.[[Millisecond]], parsed.[[Microsecond]], parsed.[[Nanosecond]], offsetNanoseconds).\n    // 7. If IsValidEpochNanoseconds(epochNanoseconds) is false, throw a RangeError exception.\n    // 8. Return ! CreateTemporalInstant(epochNanoseconds).\n    // 2. If item is not a String, throw a TypeError exception.\n    string_to_parse\n        .to_std_string_escaped()\n        .parse::<InnerInstant>()\n        .map_err(Into::into)\n}\n"
  },
  {
    "path": "core/engine/src/builtins/temporal/mod.rs",
    "content": "//! The ECMAScript `Temporal` stage 3 built-in implementation.\n//!\n//! Boa's Temporal implementation uses the `temporal_rs` crate\n//! for the core functionality of the implementation.\n//!\n//! More information:\n//!\n//! [spec]: https://tc39.es/proposal-temporal/\n\nmod calendar;\nmod duration;\nmod error;\nmod instant;\nmod now;\nmod options;\nmod plain_date;\nmod plain_date_time;\nmod plain_month_day;\nmod plain_time;\nmod plain_year_month;\nmod zoneddatetime;\n\n#[cfg(test)]\nmod tests;\n\npub use self::{\n    duration::*, instant::*, now::*, plain_date::*, plain_date_time::*, plain_month_day::*,\n    plain_time::*, plain_year_month::*, zoneddatetime::*,\n};\n\nuse crate::{\n    Context, JsNativeError, JsObject, JsResult, JsString, JsSymbol, JsValue,\n    builtins::{\n        BuiltInBuilder, BuiltInObject, IntrinsicObject,\n        temporal::calendar::get_temporal_calendar_slot_value_with_default,\n    },\n    context::intrinsics::Intrinsics,\n    js_string,\n    property::Attribute,\n    realm::Realm,\n    string::StaticJsStrings,\n};\nuse temporal_rs::{\n    PlainDate as TemporalDate, ZonedDateTime as TemporalZonedDateTime,\n    partial::PartialZonedDateTime, primitive::FiniteF64,\n};\nuse temporal_rs::{options::RelativeTo, partial::PartialDate};\n\n// An enum representing common fields across `Temporal` objects.\npub(crate) enum DateTimeValues {\n    Year,\n    Month,\n    MonthCode,\n    Week,\n    Day,\n    Hour,\n    Minute,\n    Second,\n    Millisecond,\n    Microsecond,\n    Nanosecond,\n}\n\n/// `Temporal` built-in implementation\n///\n/// The Temporal implementation in Boa uses `temporal_rs` for the\n/// core implementation.\n///\n/// More information:\n///  - [ECMAScript Temporal proposal][spec]\n///  - [MDN Reference][mdn]\n///  - [`temporal_rs` docs][temporal_rs-docs]\n///\n/// [spec]: https://tc39.es/proposal-temporal/#sec-temporal-objects\n/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Temporal\n/// [temporal_rs-docs]: https://docs.rs/temporal_rs/latest/temporal_rs/\n#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]\npub(crate) struct Temporal;\n\nimpl BuiltInObject for Temporal {\n    const NAME: JsString = StaticJsStrings::TEMPORAL;\n}\n\nimpl IntrinsicObject for Temporal {\n    fn init(realm: &Realm) {\n        BuiltInBuilder::with_intrinsic::<Self>(realm)\n            .static_property(\n                JsSymbol::to_string_tag(),\n                Self::NAME,\n                Attribute::READONLY | Attribute::NON_ENUMERABLE | Attribute::CONFIGURABLE,\n            )\n            .static_property(\n                js_string!(\"Now\"),\n                realm.intrinsics().objects().now(),\n                Attribute::WRITABLE | Attribute::NON_ENUMERABLE | Attribute::CONFIGURABLE,\n            )\n            .static_property(\n                js_string!(\"Duration\"),\n                realm.intrinsics().constructors().duration().constructor(),\n                Attribute::WRITABLE | Attribute::NON_ENUMERABLE | Attribute::CONFIGURABLE,\n            )\n            .static_property(\n                js_string!(\"Instant\"),\n                realm.intrinsics().constructors().instant().constructor(),\n                Attribute::WRITABLE | Attribute::NON_ENUMERABLE | Attribute::CONFIGURABLE,\n            )\n            .static_property(\n                js_string!(\"PlainDate\"),\n                realm.intrinsics().constructors().plain_date().constructor(),\n                Attribute::WRITABLE | Attribute::NON_ENUMERABLE | Attribute::CONFIGURABLE,\n            )\n            .static_property(\n                js_string!(\"PlainDateTime\"),\n                realm\n                    .intrinsics()\n                    .constructors()\n                    .plain_date_time()\n                    .constructor(),\n                Attribute::WRITABLE | Attribute::NON_ENUMERABLE | Attribute::CONFIGURABLE,\n            )\n            .static_property(\n                js_string!(\"PlainMonthDay\"),\n                realm\n                    .intrinsics()\n                    .constructors()\n                    .plain_month_day()\n                    .constructor(),\n                Attribute::WRITABLE | Attribute::NON_ENUMERABLE | Attribute::CONFIGURABLE,\n            )\n            .static_property(\n                js_string!(\"PlainTime\"),\n                realm.intrinsics().constructors().plain_time().constructor(),\n                Attribute::WRITABLE | Attribute::NON_ENUMERABLE | Attribute::CONFIGURABLE,\n            )\n            .static_property(\n                js_string!(\"PlainYearMonth\"),\n                realm\n                    .intrinsics()\n                    .constructors()\n                    .plain_year_month()\n                    .constructor(),\n                Attribute::WRITABLE | Attribute::NON_ENUMERABLE | Attribute::CONFIGURABLE,\n            )\n            .static_property(\n                js_string!(\"ZonedDateTime\"),\n                realm\n                    .intrinsics()\n                    .constructors()\n                    .zoned_date_time()\n                    .constructor(),\n                Attribute::WRITABLE | Attribute::NON_ENUMERABLE | Attribute::CONFIGURABLE,\n            )\n            .build();\n    }\n\n    fn get(intrinsics: &Intrinsics) -> JsObject {\n        intrinsics.objects().temporal()\n    }\n}\n\n// -- Temporal Abstract Operations --\n\npub(crate) fn get_relative_to_option(\n    options: &JsObject,\n    context: &mut Context,\n) -> JsResult<Option<RelativeTo>> {\n    // Let value be ? Get(options, \"relativeTo\").\n    let value = options.get(js_string!(\"relativeTo\"), context)?;\n    // 2. If value is undefined, return the Record { [[PlainRelativeTo]]: undefined, [[ZonedRelativeTo]]: undefined }.\n    if value.is_undefined() {\n        return Ok(None);\n    }\n    // 3. Let offsetBehaviour be option.\n    // 4. Let matchBehaviour be match-exactly.\n    // 5. If value is an Object, then\n    if let Some(object) = value.as_object() {\n        // a. If value has an [[InitializedTemporalZonedDateTime]] internal slot, then\n        if let Some(zdt) = object.downcast_ref::<ZonedDateTime>() {\n            // i. Return the Record { [[PlainRelativeTo]]: undefined, [[ZonedRelativeTo]]: value }.\n            return Ok(Some(RelativeTo::ZonedDateTime(zdt.inner.as_ref().clone())));\n        // b. If value has an [[InitializedTemporalDate]] internal slot, then\n        } else if let Some(date) = object.downcast_ref::<PlainDate>() {\n            // i. Return the Record { [[PlainRelativeTo]]: value, [[ZonedRelativeTo]]: undefined }.\n            return Ok(Some(RelativeTo::PlainDate(date.inner.clone())));\n        // c. If value has an [[InitializedTemporalDateTime]] internal slot, then\n        } else if let Some(dt) = object.downcast_ref::<PlainDateTime>() {\n            // i. Let plainDate be ! CreateTemporalDate(value.[[ISODateTime]].[[ISODate]], value.[[Calendar]]).\n            // ii. Return the Record { [[PlainRelativeTo]]: plainDate, [[ZonedRelativeTo]]: undefined }.\n            return Ok(Some(RelativeTo::PlainDate(dt.inner.clone().into())));\n        }\n        // d. Let calendar be ? GetTemporalCalendarIdentifierWithISODefault(value).\n        let calendar = get_temporal_calendar_slot_value_with_default(&object, context)?;\n        // TODO: Check behavior around Partial here.\n        // e. Let fields be ? PrepareCalendarFields(calendar, value, « year, month, month-code, day », « hour, minute, second, millisecond, microsecond, nanosecond, offset, time-zone », «»).\n        let (fields, timezone) = to_zoned_date_time_fields(\n            &object,\n            &calendar,\n            ZdtFieldsType::TimeZoneNotRequired,\n            context,\n        )?;\n        // f. Let result be ? InterpretTemporalDateTimeFields(calendar, fields, constrain).\n        // g. Let timeZone be fields.[[TimeZone]].\n        // h. Let offsetString be fields.[[OffsetString]].\n        // i. If offsetString is unset, then\n        // i. Set offsetBehaviour to wall.\n        // j. Let isoDate be result.[[ISODate]].\n        // TODO: Update in temporal_rs\n        if timezone.is_none() {\n            return Ok(Some(RelativeTo::PlainDate(TemporalDate::from_partial(\n                PartialDate {\n                    calendar_fields: fields.calendar_fields,\n                    calendar,\n                },\n                None,\n            )?)));\n        }\n        // k. Let time be result.[[Time]].\n        let zdt = TemporalZonedDateTime::from_partial_with_provider(\n            PartialZonedDateTime {\n                fields,\n                timezone,\n                calendar,\n            },\n            None,\n            None,\n            None,\n            context.timezone_provider(),\n        )?;\n        return Ok(Some(RelativeTo::ZonedDateTime(zdt)));\n    }\n    // 6. Else,\n    // a. If value is not a String, throw a TypeError exception.\n    let Some(relative_to_str) = value.as_string() else {\n        return Err(JsNativeError::typ()\n            .with_message(\"relativeTo must be an object or string.\")\n            .into());\n    };\n    // Steps 7-12 are handled by temporal_rs\n    Ok(Some(RelativeTo::try_from_str_with_provider(\n        &relative_to_str.to_std_string_escaped(),\n        context.timezone_provider(),\n    )?))\n}\n\n// 13.26 IsPartialTemporalObject ( object )\npub(crate) fn is_partial_temporal_object(\n    value: &JsValue,\n    context: &mut Context,\n) -> JsResult<Option<JsObject>> {\n    // 1. If value is not an Object, return false.\n    let Some(obj) = value.as_object() else {\n        return Ok(None);\n    };\n\n    // 2. If value has an [[InitializedTemporalDate]], [[InitializedTemporalDateTime]],\n    // [[InitializedTemporalMonthDay]], [[InitializedTemporalTime]],\n    // [[InitializedTemporalYearMonth]], or\n    // [[InitializedTemporalZonedDateTime]] internal slot, return false.\n    if obj.is::<PlainDate>()\n        || obj.is::<PlainDateTime>()\n        || obj.is::<PlainMonthDay>()\n        || obj.is::<PlainYearMonth>()\n        || obj.is::<PlainTime>()\n        || obj.is::<ZonedDateTime>()\n    {\n        return Ok(None);\n    }\n\n    // 3. Let calendarProperty be ? Get(value, \"calendar\").\n    let calendar_property = obj.get(js_string!(\"calendar\"), context)?;\n    // 4. If calendarProperty is not undefined, return false.\n    if !calendar_property.is_undefined() {\n        return Ok(None);\n    }\n    // 5. Let timeZoneProperty be ? Get(value, \"timeZone\").\n    let time_zone_property = obj.get(js_string!(\"timeZone\"), context)?;\n    // 6. If timeZoneProperty is not undefined, return false.\n    if !time_zone_property.is_undefined() {\n        return Ok(None);\n    }\n    // 7. Return true.\n    Ok(Some(obj))\n}\n\nimpl JsValue {\n    pub(crate) fn to_finitef64(&self, context: &mut Context) -> JsResult<FiniteF64> {\n        let number = self.to_number(context)?;\n        let result = FiniteF64::try_from(number)?;\n        Ok(result)\n    }\n}\n\nfn extract_from_temporal_type<DF, DTF, YMF, MDF, ZDTF, Ret>(\n    object: &JsObject,\n    date_f: DF,\n    datetime_f: DTF,\n    year_month_f: YMF,\n    month_day_f: MDF,\n    zoned_datetime_f: ZDTF,\n) -> JsResult<Option<Ret>>\nwhere\n    DF: FnOnce(&PlainDate) -> JsResult<Option<Ret>>,\n    DTF: FnOnce(&PlainDateTime) -> JsResult<Option<Ret>>,\n    YMF: FnOnce(&PlainYearMonth) -> JsResult<Option<Ret>>,\n    MDF: FnOnce(&PlainMonthDay) -> JsResult<Option<Ret>>,\n    ZDTF: FnOnce(&ZonedDateTime) -> JsResult<Option<Ret>>,\n{\n    if let Some(date) = object.downcast_ref::<PlainDate>() {\n        return date_f(&date);\n    } else if let Some(dt) = object.downcast_ref::<PlainDateTime>() {\n        return datetime_f(&dt);\n    } else if let Some(ym) = object.downcast_ref::<PlainYearMonth>() {\n        return year_month_f(&ym);\n    } else if let Some(md) = object.downcast_ref::<PlainMonthDay>() {\n        return month_day_f(&md);\n    } else if let Some(dt) = object.downcast_ref::<ZonedDateTime>() {\n        return zoned_datetime_f(&dt);\n    }\n\n    Ok(None)\n}\n"
  },
  {
    "path": "core/engine/src/builtins/temporal/now.rs",
    "content": "//! Boa's implementation of `Temporal.Now` ECMAScript global namespace object.\n\nuse crate::{\n    Context, JsArgs, JsObject, JsResult, JsString, JsSymbol, JsValue,\n    builtins::{BuiltInBuilder, BuiltInObject, IntrinsicObject},\n    context::intrinsics::Intrinsics,\n    js_string,\n    property::Attribute,\n    realm::Realm,\n    string::StaticJsStrings,\n};\nuse temporal_rs::{\n    TemporalResult, TimeZone,\n    host::{HostClock, HostHooks, HostTimeZone},\n    now::Now as InnerNow,\n    provider::TimeZoneProvider,\n    unix_time::EpochNanoseconds,\n};\n\n#[cfg(feature = \"system-time-zone\")]\nuse temporal_rs::TemporalError;\n\nuse super::{\n    create_temporal_date, create_temporal_datetime, create_temporal_instant, create_temporal_time,\n    create_temporal_zoneddatetime, to_temporal_timezone_identifier,\n};\n\n/// The `Temporal.Now` global namespace object\n///\n/// More information:\n///\n/// - [ECMAScript Temporal proposal][spec]\n/// - [MDN reference][mdn]\n///\n/// [spec]: https://tc39.es/proposal-temporal/#sec-temporal-now-object\n/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Temporal/Now\n#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]\npub struct Now;\n\nimpl IntrinsicObject for Now {\n    /// Initializes the `Temporal.Now` object.\n    fn init(realm: &Realm) {\n        // is an ordinary object.\n        // has a [[Prototype]] internal slot whose value is %Object.prototype%.\n        // is not a function object.\n        // does not have a [[Construct]] internal method; it cannot be used as a constructor with the new operator.\n        // does not have a [[Call]] internal method; it cannot be invoked as a function.\n        BuiltInBuilder::with_intrinsic::<Self>(realm)\n            .static_property(\n                JsSymbol::to_string_tag(),\n                StaticJsStrings::NOW_TAG,\n                Attribute::READONLY | Attribute::NON_ENUMERABLE | Attribute::CONFIGURABLE,\n            )\n            .static_method(Self::time_zone_id, js_string!(\"timeZoneId\"), 0)\n            .static_method(Self::instant, js_string!(\"instant\"), 0)\n            .static_method(Self::plain_date_time_iso, js_string!(\"plainDateTimeISO\"), 0)\n            .static_method(Self::zoned_date_time_iso, js_string!(\"zonedDateTimeISO\"), 0)\n            .static_method(Self::plain_date_iso, js_string!(\"plainDateISO\"), 0)\n            .static_method(Self::plain_time_iso, js_string!(\"plainTimeISO\"), 0)\n            .build();\n    }\n\n    fn get(intrinsics: &Intrinsics) -> JsObject {\n        intrinsics.objects().now()\n    }\n}\n\nimpl BuiltInObject for Now {\n    const NAME: JsString = StaticJsStrings::NOW_NAME;\n}\n\nimpl Now {\n    /// 2.2.1 `Temporal.Now.timeZoneId ( )`\n    ///\n    /// Returns the currently active system time zone identifier.\n    ///\n    /// More information:\n    ///  - [ECMAScript specification][spec]\n    ///  - [MDN reference][mdn]\n    ///\n    /// [spec]: https://tc39.es/proposal-temporal/#sec-temporal.now.timezone\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Temporal/Now/timeZoneId\n    fn time_zone_id(_: &JsValue, _: &[JsValue], context: &mut Context) -> JsResult<JsValue> {\n        // TODO: this should be optimized once system time zone is in context\n        // 1. Return ! SystemTimeZone().\n        let context: &Context = context;\n        let time_zone = context.get_system_time_zone(context.timezone_provider())?;\n        Ok(JsString::from(time_zone.identifier_with_provider(context.timezone_provider())?).into())\n    }\n\n    /// 2.2.2 `Temporal.Now.instant()`\n    ///\n    /// Returns the current time as an `Temporal.Instant`.\n    ///\n    /// More information:\n    ///  - [ECMAscript specification][spec]\n    ///  - [MDN reference][mdn]\n    ///\n    /// [spec]: https://tc39.es/proposal-temporal/#sec-temporal.now.instant\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Temporal/Now/instant\n    fn instant(_: &JsValue, _: &[JsValue], context: &mut Context) -> JsResult<JsValue> {\n        let now: InnerNow<&Context> = InnerNow::new(context);\n        let instant = now.instant()?;\n        create_temporal_instant(instant, None, context)\n    }\n\n    /// 2.2.3 `Temporal.Now.plainDateTimeISO ( [ temporalTimeZoneLike ] )`\n    ///\n    /// Returns the current date and time as a `Temporal.PlainDateTime` with an ISO8601 calendar.\n    ///\n    /// Takes an optional time zone, which defaults to the system time zone if undefined.\n    ///\n    /// More information:\n    ///  - [ECMAscript specification][spec]\n    ///  - [MDN reference][mdn]\n    ///\n    /// [spec]: https://tc39.es/proposal-temporal/#sec-temporal.now.plaindatetimeiso\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Temporal/Now/plainDateTimeISO\n    fn plain_date_time_iso(\n        _: &JsValue,\n        args: &[JsValue],\n        context: &mut Context,\n    ) -> JsResult<JsValue> {\n        let time_zone = args\n            .get_or_undefined(0)\n            .map(|v| to_temporal_timezone_identifier(v, context))\n            .transpose()?;\n\n        let now: InnerNow<&Context> = InnerNow::new(context);\n\n        let datetime =\n            now.plain_date_time_iso_with_provider(time_zone, context.timezone_provider())?;\n        create_temporal_datetime(datetime, None, context).map(Into::into)\n    }\n\n    /// 2.2.4 `Temporal.Now.zonedDateTimeISO ( [ temporalTimeZoneLike ] )`\n    ///\n    /// Returns the current date and time as a `Temporal.ZonedDateTime` with an ISO8601 calendar.\n    ///\n    /// Takes an optional time zone, which defaults to the system time zone if undefined.\n    ///\n    /// More information:\n    ///  - [ECMAscript specification][spec]\n    ///  - [MDN reference][mdn]\n    ///\n    /// [spec]: https://tc39.es/proposal-temporal/#sec-temporal.now.zoneddatetimeiso\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Temporal/Now/zonedDateTimeISO\n    fn zoned_date_time_iso(\n        _: &JsValue,\n        args: &[JsValue],\n        context: &mut Context,\n    ) -> JsResult<JsValue> {\n        let time_zone = args\n            .get_or_undefined(0)\n            .map(|v| to_temporal_timezone_identifier(v, context))\n            .transpose()?;\n\n        let now: InnerNow<&Context> = InnerNow::new(context);\n        let zdt = now.zoned_date_time_iso_with_provider(time_zone, context.timezone_provider())?;\n        create_temporal_zoneddatetime(zdt, None, context).map(Into::into)\n    }\n\n    /// 2.2.5 `Temporal.Now.plainDateISO ( [ temporalTimeZoneLike ] )`\n    ///\n    /// Returns the current date as a `Temporal.PlainDate` with an ISO8601 calendar.\n    ///\n    /// Takes an optional time zone, which defaults to the system time zone if undefined.\n    ///\n    /// More information:\n    ///  - [ECMAscript specification][spec]\n    ///  - [MDN reference][mdn]\n    ///\n    /// [spec]: https://tc39.es/proposal-temporal/#sec-temporal.now.plaindateiso\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Temporal/Now/plainDateISO\n    fn plain_date_iso(_: &JsValue, args: &[JsValue], context: &mut Context) -> JsResult<JsValue> {\n        let time_zone = args\n            .get_or_undefined(0)\n            .map(|v| to_temporal_timezone_identifier(v, context))\n            .transpose()?;\n\n        let now: InnerNow<&Context> = InnerNow::new(context);\n\n        let pd = now.plain_date_iso_with_provider(time_zone, context.timezone_provider())?;\n        create_temporal_date(pd, None, context).map(Into::into)\n    }\n\n    /// 2.2.6 `Temporal.Now.plainTimeISO ( [ temporalTimeZoneLike ] )`\n    ///\n    /// Returns the current time as a `Temporal.PlainTime` with an ISO8601 calendar.\n    ///\n    /// Takes an optional time zone, which defaults to the system time zone if undefined.\n    ///\n    /// More information:\n    ///  - [ECMAscript specification][spec]\n    ///  - [MDN reference][mdn]\n    ///\n    /// [spec]: https://tc39.es/proposal-temporal/#sec-temporal.now.plaintimeiso\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Temporal/Now/plainTimeISO\n    fn plain_time_iso(_: &JsValue, args: &[JsValue], context: &mut Context) -> JsResult<JsValue> {\n        let time_zone = args\n            .get_or_undefined(0)\n            .map(|v| to_temporal_timezone_identifier(v, context))\n            .transpose()?;\n\n        let now: InnerNow<&Context> = InnerNow::new(context);\n\n        let pt = now.plain_time_iso_with_provider(time_zone, context.timezone_provider())?;\n        create_temporal_time(pt, None, context).map(Into::into)\n    }\n}\n\nimpl HostHooks for &Context {}\n\nimpl HostClock for &Context {\n    fn get_host_epoch_nanoseconds(&self) -> TemporalResult<EpochNanoseconds> {\n        // Temporal needs actual Unix epoch time, not monotonic time\n        let millis = self.clock().system_time_millis();\n        let nanos = i128::from(millis) * 1_000_000;\n        Ok(EpochNanoseconds::from(nanos))\n    }\n}\n\nimpl HostTimeZone for &Context {\n    fn get_host_time_zone(\n        &self,\n        provider: &(impl TimeZoneProvider + ?Sized),\n    ) -> TemporalResult<TimeZone> {\n        #[cfg(not(feature = \"system-time-zone\"))]\n        {\n            Ok(TimeZone::utc_with_provider(provider))\n        }\n        #[cfg(feature = \"system-time-zone\")]\n        {\n            iana_time_zone::get_timezone()\n                .map_err(|_| {\n                    TemporalError::range().with_message(\"Unable to fetch system time zone\")\n                })\n                .and_then(|id| TimeZone::try_from_str_with_provider(&id, provider))\n        }\n    }\n}\n"
  },
  {
    "path": "core/engine/src/builtins/temporal/options.rs",
    "content": "//! Temporal Option types.\n\n// Implementation Note:\n//\n// The below Option types are adapted from the types laid out by\n// the Temporal proposal's polyfill types that can be found at the\n// below link.\n//\n// https://github.com/tc39/proposal-temporal/blob/main/polyfill/index.d.ts\n\nuse crate::{\n    Context, JsNativeError, JsObject, JsResult, JsString, JsValue,\n    builtins::options::{OptionType, ParsableOptionType, get_option},\n    js_string,\n};\nuse temporal_rs::{\n    options::{\n        DifferenceSettings, Disambiguation, DisplayCalendar, DisplayOffset, DisplayTimeZone,\n        OffsetDisambiguation, Overflow, RoundingIncrement, RoundingMode, Unit,\n    },\n    parsers::Precision,\n    provider::TransitionDirection,\n};\n\n// TODO: Expand docs on the below options.\n\n/// Gets the `TemporalUnit` from an options object.\n#[inline]\npub(crate) fn get_temporal_unit(\n    options: &JsObject,\n    key: JsString,\n    unit_group: TemporalUnitGroup,\n    extra_values: Option<Vec<Unit>>,\n    context: &mut Context,\n) -> JsResult<Option<Unit>> {\n    let extra = extra_values.unwrap_or_default();\n    let mut unit_values = unit_group.group();\n    unit_values.extend(extra);\n\n    let unit = get_option(options, key, context)?;\n\n    if let Some(u) = &unit\n        && !unit_values.contains(u)\n    {\n        return Err(JsNativeError::range()\n            .with_message(\"TemporalUnit was not part of the valid UnitGroup.\")\n            .into());\n    }\n\n    Ok(unit)\n}\n\n#[inline]\npub(crate) fn get_difference_settings(\n    options: &JsObject,\n    context: &mut Context,\n) -> JsResult<DifferenceSettings> {\n    let mut settings = DifferenceSettings::default();\n    settings.largest_unit = get_option::<Unit>(options, js_string!(\"largestUnit\"), context)?;\n    settings.increment =\n        get_option::<RoundingIncrement>(options, js_string!(\"roundingIncrement\"), context)?;\n    settings.rounding_mode =\n        get_option::<RoundingMode>(options, js_string!(\"roundingMode\"), context)?;\n    settings.smallest_unit = get_option::<Unit>(options, js_string!(\"smallestUnit\"), context)?;\n    Ok(settings)\n}\n\npub(crate) fn get_digits_option(options: &JsObject, context: &mut Context) -> JsResult<Precision> {\n    // 1. Let digitsValue be ? Get(options, \"fractionalSecondDigits\").\n    let digits_value = options.get(js_string!(\"fractionalSecondDigits\"), context)?;\n    // 2. If digitsValue is undefined, return auto.\n    if digits_value.is_undefined() {\n        return Ok(Precision::Auto);\n    }\n    // 3. If digitsValue is not a Number, then\n    let Some(digits_number) = digits_value.as_number() else {\n        // a. If ? ToString(digitsValue) is not \"auto\", throw a RangeError exception.\n        if digits_value.to_string(context)? != js_string!(\"auto\") {\n            return Err(JsNativeError::range()\n                .with_message(\"fractionalSecondDigits must be a digit or 'auto'\")\n                .into());\n        }\n        // b. Return auto.\n        return Ok(Precision::Auto);\n    };\n\n    // 4. If digitsValue is NaN, +∞𝔽, or -∞𝔽, throw a RangeError exception.\n    if !digits_number.is_finite() {\n        return Err(JsNativeError::range()\n            .with_message(\"fractionalSecondDigits must be a finite number\")\n            .into());\n    }\n    // 5. Let digitCount be floor(ℝ(digitsValue)).\n    let digits = digits_number.floor() as i32;\n    // 6. If digitCount < 0 or digitCount > 9, throw a RangeError exception.\n    if !(0..=9).contains(&digits) {\n        return Err(JsNativeError::range()\n            .with_message(\"fractionalSecondDigits must be in an inclusive range of 0-9\")\n            .into());\n    }\n    // 7. Return digitCount.\n    Ok(Precision::Digit(digits as u8))\n}\n\n#[derive(Debug, Clone, Copy)]\n#[allow(unused)]\npub(crate) enum TemporalUnitGroup {\n    Date, // Need to assert if this is needed anymore with the removal of `Temporal.Calendar`\n    Time,\n    DateTime,\n}\n\nimpl TemporalUnitGroup {\n    fn group(self) -> Vec<Unit> {\n        use TemporalUnitGroup::{Date, DateTime, Time};\n\n        match self {\n            Date => date_units().collect(),\n            Time => time_units().collect(),\n            DateTime => datetime_units().collect(),\n        }\n    }\n}\n\nfn time_units() -> impl Iterator<Item = Unit> {\n    [\n        Unit::Hour,\n        Unit::Minute,\n        Unit::Second,\n        Unit::Millisecond,\n        Unit::Microsecond,\n        Unit::Nanosecond,\n    ]\n    .iter()\n    .copied()\n}\n\nfn date_units() -> impl Iterator<Item = Unit> {\n    [Unit::Year, Unit::Month, Unit::Week, Unit::Day]\n        .iter()\n        .copied()\n}\n\nfn datetime_units() -> impl Iterator<Item = Unit> {\n    date_units().chain(time_units())\n}\n\nimpl ParsableOptionType for Unit {}\nimpl ParsableOptionType for Overflow {}\nimpl ParsableOptionType for Disambiguation {}\nimpl ParsableOptionType for OffsetDisambiguation {}\nimpl ParsableOptionType for RoundingMode {}\nimpl ParsableOptionType for DisplayCalendar {}\nimpl ParsableOptionType for DisplayOffset {}\nimpl ParsableOptionType for DisplayTimeZone {}\nimpl ParsableOptionType for TransitionDirection {}\n\nimpl OptionType for RoundingIncrement {\n    fn from_value(value: JsValue, context: &mut Context) -> JsResult<Self> {\n        let value = value.to_number(context)?;\n\n        Ok(RoundingIncrement::try_from(value)?)\n    }\n}\n"
  },
  {
    "path": "core/engine/src/builtins/temporal/plain_date/mod.rs",
    "content": "//! Boa's implementation of the ECMAScript `Temporal.PlainDate` built-in object.\n//!\n//! This implementation is a ECMAScript compliant wrapper around the `temporal_rs` library.\n\nuse std::str::FromStr;\n\nuse crate::{\n    Context, JsArgs, JsData, JsError, JsNativeError, JsObject, JsResult, JsString, JsSymbol,\n    JsValue,\n    builtins::{\n        BuiltInBuilder, BuiltInConstructor, BuiltInObject, IntrinsicObject,\n        options::{get_option, get_options_object},\n        temporal::calendar::to_temporal_calendar_identifier,\n    },\n    context::intrinsics::{Intrinsics, StandardConstructor, StandardConstructors},\n    js_string,\n    object::internal_methods::get_prototype_from_constructor,\n    property::Attribute,\n    realm::Realm,\n    string::StaticJsStrings,\n    value::IntoOrUndefined,\n};\nuse boa_gc::{Finalize, Trace};\nuse icu_calendar::AnyCalendarKind;\nuse temporal_rs::{\n    Calendar, MonthCode, PlainDate as InnerDate, TinyAsciiStr,\n    fields::CalendarFields,\n    options::{DisplayCalendar, Overflow},\n    partial::PartialDate,\n};\n\nuse super::{\n    PlainDateTime, ZonedDateTime, calendar::get_temporal_calendar_slot_value_with_default,\n    create_temporal_datetime, create_temporal_duration, create_temporal_zoneddatetime,\n    options::get_difference_settings, to_temporal_duration_record, to_temporal_time,\n    to_temporal_timezone_identifier,\n};\nuse super::{create_temporal_month_day, create_temporal_year_month};\n\n#[cfg(feature = \"temporal\")]\n#[cfg(test)]\nmod tests;\n\n/// `Temporal.PlainDate` built-in implementation\n///\n/// A `Temporal.PlainDate` is a calendar date represented by ISO date fields\n/// and a calendar system.\n///\n/// More information:\n///\n/// - [ECMAScript Temporal proposal][spec]\n/// - [MDN reference][mdn]\n/// - [`temporal_rs` documentation][temporal_rs-docs]\n///\n/// [spec]: https://tc39.es/proposal-temporal/#sec-temporal-plaindate-objects\n/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Temporal/PlainDate\n/// [temporal_rs-docs]: https://docs.rs/temporal_rs/latest/temporal_rs/struct.PlainDate.html\n#[derive(Debug, Clone, Trace, Finalize, JsData)]\n#[boa_gc(unsafe_empty_trace)]\npub struct PlainDate {\n    pub(crate) inner: InnerDate,\n}\n\nimpl PlainDate {\n    pub(crate) fn new(inner: InnerDate) -> Self {\n        Self { inner }\n    }\n}\n\nimpl BuiltInObject for PlainDate {\n    const NAME: JsString = StaticJsStrings::PLAIN_DATE_NAME;\n}\n\nimpl IntrinsicObject for PlainDate {\n    fn init(realm: &Realm) {\n        let get_calendar_id = BuiltInBuilder::callable(realm, Self::get_calendar_id)\n            .name(js_string!(\"get calendarId\"))\n            .build();\n\n        let get_era = BuiltInBuilder::callable(realm, Self::get_era)\n            .name(js_string!(\"get era\"))\n            .build();\n\n        let get_era_year = BuiltInBuilder::callable(realm, Self::get_era_year)\n            .name(js_string!(\"get eraYear\"))\n            .build();\n\n        let get_year = BuiltInBuilder::callable(realm, Self::get_year)\n            .name(js_string!(\"get year\"))\n            .build();\n\n        let get_month = BuiltInBuilder::callable(realm, Self::get_month)\n            .name(js_string!(\"get month\"))\n            .build();\n\n        let get_month_code = BuiltInBuilder::callable(realm, Self::get_month_code)\n            .name(js_string!(\"get monthCode\"))\n            .build();\n\n        let get_day = BuiltInBuilder::callable(realm, Self::get_day)\n            .name(js_string!(\"get day\"))\n            .build();\n\n        let get_day_of_week = BuiltInBuilder::callable(realm, Self::get_day_of_week)\n            .name(js_string!(\"get dayOfWeek\"))\n            .build();\n\n        let get_day_of_year = BuiltInBuilder::callable(realm, Self::get_day_of_year)\n            .name(js_string!(\"get dayOfYear\"))\n            .build();\n\n        let get_week_of_year = BuiltInBuilder::callable(realm, Self::get_week_of_year)\n            .name(js_string!(\"get weekOfYear\"))\n            .build();\n\n        let get_year_of_week = BuiltInBuilder::callable(realm, Self::get_year_of_week)\n            .name(js_string!(\"get yearOfWeek\"))\n            .build();\n\n        let get_days_in_week = BuiltInBuilder::callable(realm, Self::get_days_in_week)\n            .name(js_string!(\"get daysInWeek\"))\n            .build();\n\n        let get_days_in_month = BuiltInBuilder::callable(realm, Self::get_days_in_month)\n            .name(js_string!(\"get daysInMonth\"))\n            .build();\n\n        let get_days_in_year = BuiltInBuilder::callable(realm, Self::get_days_in_year)\n            .name(js_string!(\"get daysInYear\"))\n            .build();\n\n        let get_months_in_year = BuiltInBuilder::callable(realm, Self::get_months_in_year)\n            .name(js_string!(\"get monthsInYear\"))\n            .build();\n\n        let get_in_leap_year = BuiltInBuilder::callable(realm, Self::get_in_leap_year)\n            .name(js_string!(\"get inLeapYear\"))\n            .build();\n\n        BuiltInBuilder::from_standard_constructor::<Self>(realm)\n            .property(\n                JsSymbol::to_string_tag(),\n                StaticJsStrings::PLAIN_DATE_TAG,\n                Attribute::CONFIGURABLE,\n            )\n            .accessor(\n                js_string!(\"calendarId\"),\n                Some(get_calendar_id),\n                None,\n                Attribute::CONFIGURABLE,\n            )\n            .accessor(\n                js_string!(\"era\"),\n                Some(get_era),\n                None,\n                Attribute::CONFIGURABLE,\n            )\n            .accessor(\n                js_string!(\"eraYear\"),\n                Some(get_era_year),\n                None,\n                Attribute::CONFIGURABLE,\n            )\n            .accessor(\n                js_string!(\"year\"),\n                Some(get_year),\n                None,\n                Attribute::CONFIGURABLE,\n            )\n            .accessor(\n                js_string!(\"month\"),\n                Some(get_month),\n                None,\n                Attribute::CONFIGURABLE,\n            )\n            .accessor(\n                js_string!(\"monthCode\"),\n                Some(get_month_code),\n                None,\n                Attribute::CONFIGURABLE,\n            )\n            .accessor(\n                js_string!(\"day\"),\n                Some(get_day),\n                None,\n                Attribute::CONFIGURABLE,\n            )\n            .accessor(\n                js_string!(\"dayOfWeek\"),\n                Some(get_day_of_week),\n                None,\n                Attribute::CONFIGURABLE,\n            )\n            .accessor(\n                js_string!(\"dayOfYear\"),\n                Some(get_day_of_year),\n                None,\n                Attribute::CONFIGURABLE,\n            )\n            .accessor(\n                js_string!(\"weekOfYear\"),\n                Some(get_week_of_year),\n                None,\n                Attribute::CONFIGURABLE,\n            )\n            .accessor(\n                js_string!(\"yearOfWeek\"),\n                Some(get_year_of_week),\n                None,\n                Attribute::CONFIGURABLE,\n            )\n            .accessor(\n                js_string!(\"daysInWeek\"),\n                Some(get_days_in_week),\n                None,\n                Attribute::CONFIGURABLE,\n            )\n            .accessor(\n                js_string!(\"daysInMonth\"),\n                Some(get_days_in_month),\n                None,\n                Attribute::CONFIGURABLE,\n            )\n            .accessor(\n                js_string!(\"daysInYear\"),\n                Some(get_days_in_year),\n                None,\n                Attribute::CONFIGURABLE,\n            )\n            .accessor(\n                js_string!(\"monthsInYear\"),\n                Some(get_months_in_year),\n                None,\n                Attribute::CONFIGURABLE,\n            )\n            .accessor(\n                js_string!(\"inLeapYear\"),\n                Some(get_in_leap_year),\n                None,\n                Attribute::CONFIGURABLE,\n            )\n            .static_method(Self::from, js_string!(\"from\"), 1)\n            .static_method(Self::compare, js_string!(\"compare\"), 2)\n            .method(Self::to_plain_year_month, js_string!(\"toPlainYearMonth\"), 0)\n            .method(Self::to_plain_month_day, js_string!(\"toPlainMonthDay\"), 0)\n            .method(Self::add, js_string!(\"add\"), 1)\n            .method(Self::subtract, js_string!(\"subtract\"), 1)\n            .method(Self::with, js_string!(\"with\"), 1)\n            .method(Self::with_calendar, js_string!(\"withCalendar\"), 1)\n            .method(Self::until, js_string!(\"until\"), 1)\n            .method(Self::since, js_string!(\"since\"), 1)\n            .method(Self::equals, js_string!(\"equals\"), 1)\n            .method(Self::to_plain_date_time, js_string!(\"toPlainDateTime\"), 0)\n            .method(Self::to_zoned_date_time, js_string!(\"toZonedDateTime\"), 1)\n            .method(Self::to_string, js_string!(\"toString\"), 0)\n            .method(Self::to_locale_string, js_string!(\"toLocaleString\"), 0)\n            .method(Self::to_json, js_string!(\"toJSON\"), 0)\n            .method(Self::value_of, js_string!(\"valueOf\"), 0)\n            .build();\n    }\n\n    fn get(intrinsics: &Intrinsics) -> JsObject {\n        Self::STANDARD_CONSTRUCTOR(intrinsics.constructors()).constructor()\n    }\n}\n\nimpl BuiltInConstructor for PlainDate {\n    const CONSTRUCTOR_ARGUMENTS: usize = 3;\n    const PROTOTYPE_STORAGE_SLOTS: usize = 48;\n    const CONSTRUCTOR_STORAGE_SLOTS: usize = 2;\n\n    const STANDARD_CONSTRUCTOR: fn(&StandardConstructors) -> &StandardConstructor =\n        StandardConstructors::plain_date;\n\n    fn constructor(\n        new_target: &JsValue,\n        args: &[JsValue],\n        context: &mut Context,\n    ) -> JsResult<JsValue> {\n        if new_target.is_undefined() {\n            return Err(JsNativeError::typ()\n                .with_message(\"NewTarget cannot be undefined.\")\n                .into());\n        }\n\n        let year = args\n            .get_or_undefined(0)\n            .to_finitef64(context)?\n            .as_integer_with_truncation::<i32>();\n        let month = args\n            .get_or_undefined(1)\n            .to_finitef64(context)?\n            .as_integer_with_truncation::<u8>();\n        let day = args\n            .get_or_undefined(2)\n            .to_finitef64(context)?\n            .as_integer_with_truncation::<u8>();\n        let calendar_slot = args\n            .get_or_undefined(3)\n            .map(|s| {\n                s.as_string()\n                    .as_ref()\n                    .map(JsString::to_std_string_lossy)\n                    .ok_or_else(|| JsNativeError::typ().with_message(\"calendar must be a string.\"))\n            })\n            .transpose()?\n            .map(|s| Calendar::try_from_utf8(s.as_bytes()))\n            .transpose()?\n            .unwrap_or_default();\n\n        let inner = InnerDate::try_new(year, month, day, calendar_slot)?;\n\n        Ok(create_temporal_date(inner, Some(new_target), context)?.into())\n    }\n}\n\n// ==== `PlainDate` accessors implementation ====\n\nimpl PlainDate {\n    /// 3.3.3 get `Temporal.PlainDate.prototype.calendarId`\n    ///\n    /// Returns the calendar identifier for this `Temporal.PlainDate`.\n    ///\n    /// More information:\n    ///\n    /// - [ECMAScript Temporal proposal][spec]\n    /// - [MDN reference][mdn]\n    /// - [`temporal_rs` documentation][temporal_rs-docs]\n    ///\n    /// [spec]: https://tc39.es/proposal-temporal/#sec-get-temporal.plaindate.prototype.calendarid\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Temporal/PlainDate/calendarId\n    /// [temporal_rs-docs]: https://docs.rs/temporal_rs/latest/temporal_rs/struct.PlainDate.html#method.calendar\n    fn get_calendar_id(this: &JsValue, _: &[JsValue], _: &mut Context) -> JsResult<JsValue> {\n        let object = this.as_object();\n        let date = object\n            .as_ref()\n            .and_then(JsObject::downcast_ref::<Self>)\n            .ok_or_else(|| {\n                JsNativeError::typ().with_message(\"the this object must be a PlainDate object.\")\n            })?;\n\n        Ok(JsString::from(date.inner.calendar().identifier()).into())\n    }\n\n    /// 3.3.4 get `Temporal.PlainDate.prototype.era`\n    ///\n    /// Returns the era identifier for this `Temporal.PlainDate`.\n    ///\n    /// More information:\n    ///\n    /// - [ECMAScript Temporal proposal][spec]\n    /// - [MDN reference][mdn]\n    /// - [`temporal_rs` documentation][temporal_rs-docs]\n    ///\n    /// [spec]: https://tc39.es/proposal-temporal/#sec-get-temporal.plaindate.prototype.era\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Temporal/PlainDate/era\n    /// [temporal_rs-docs]: https://docs.rs/temporal_rs/latest/temporal_rs/struct.PlainDate.html#method.era\n    fn get_era(this: &JsValue, _: &[JsValue], _: &mut Context) -> JsResult<JsValue> {\n        let obj = this\n            .as_object()\n            .ok_or_else(|| JsNativeError::typ().with_message(\"this must be an object.\"))?;\n\n        let Some(date) = obj.downcast_ref::<Self>() else {\n            return Err(JsNativeError::typ()\n                .with_message(\"the this object must be a PlainDate object.\")\n                .into());\n        };\n\n        Ok(date\n            .inner\n            .era()\n            .map(|s| JsString::from(s.as_str()))\n            .into_or_undefined())\n    }\n\n    /// 3.3.5 get `Temporal.PlainDate.prototype.eraYear`\n    ///\n    /// Returns the year within the era for this `Temporal.PlainDate`.\n    ///\n    /// More information:\n    ///\n    /// - [ECMAScript Temporal proposal][spec]\n    /// - [MDN reference][mdn]\n    /// - [`temporal_rs` documentation][temporal_rs-docs]\n    ///\n    /// [spec]: https://tc39.es/proposal-temporal/#sec-get-temporal.plaindate.prototype.erayear\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Temporal/PlainDate/eraYear\n    /// [temporal_rs-docs]: https://docs.rs/temporal_rs/latest/temporal_rs/struct.PlainDate.html#method.era_year\n    fn get_era_year(this: &JsValue, _: &[JsValue], _: &mut Context) -> JsResult<JsValue> {\n        let obj = this\n            .as_object()\n            .ok_or_else(|| JsNativeError::typ().with_message(\"this must be an object.\"))?;\n\n        let Some(date) = obj.downcast_ref::<Self>() else {\n            return Err(JsNativeError::typ()\n                .with_message(\"the this object must be a PlainDate object.\")\n                .into());\n        };\n\n        Ok(date.inner.era_year().into_or_undefined())\n    }\n\n    /// 3.3.6 get `Temporal.PlainDate.prototype.year`\n    ///\n    /// Returns the calendar year for this `Temporal.PlainDate`.\n    ///\n    /// More information:\n    ///\n    /// - [ECMAScript Temporal proposal][spec]\n    /// - [MDN reference][mdn]\n    /// - [`temporal_rs` documentation][temporal_rs-docs]\n    ///\n    /// [spec]: https://tc39.es/proposal-temporal/#sec-get-temporal.plaindate.prototype.year\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Temporal/PlainDate/year\n    /// [temporal_rs-docs]: https://docs.rs/temporal_rs/latest/temporal_rs/struct.PlainDate.html#method.year\n    fn get_year(this: &JsValue, _: &[JsValue], _: &mut Context) -> JsResult<JsValue> {\n        let obj = this\n            .as_object()\n            .ok_or_else(|| JsNativeError::typ().with_message(\"this must be an object.\"))?;\n\n        let Some(date) = obj.downcast_ref::<Self>() else {\n            return Err(JsNativeError::typ()\n                .with_message(\"the this object must be a PlainDate object.\")\n                .into());\n        };\n\n        Ok(date.inner.year().into())\n    }\n\n    /// 3.3.7 get `Temporal.PlainDate.prototype.month`\n    ///\n    /// Returns the calendar month for this `Temporal.PlainDate`.\n    ///\n    /// More information:\n    ///\n    /// - [ECMAScript Temporal proposal][spec]\n    /// - [MDN reference][mdn]\n    /// - [`temporal_rs` documentation][temporal_rs-docs]\n    ///\n    /// [spec]: https://tc39.es/proposal-temporal/#sec-get-temporal.plaindate.prototype.month\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Temporal/PlainDate/month\n    /// [temporal_rs-docs]: https://docs.rs/temporal_rs/latest/temporal_rs/struct.PlainDate.html#method.month\n    fn get_month(this: &JsValue, _: &[JsValue], _: &mut Context) -> JsResult<JsValue> {\n        let obj = this\n            .as_object()\n            .ok_or_else(|| JsNativeError::typ().with_message(\"this must be an object.\"))?;\n\n        let Some(date) = obj.downcast_ref::<Self>() else {\n            return Err(JsNativeError::typ()\n                .with_message(\"the this object must be a PlainDate object.\")\n                .into());\n        };\n\n        Ok(date.inner.month().into())\n    }\n\n    /// 3.3.8 get Temporal.PlainDate.prototype.monthCode\n    ///\n    /// Returns the month code of the calendar month for this `Temporal.PlainDate`.\n    ///\n    /// More information:\n    ///\n    /// - [ECMAScript Temporal proposal][spec]\n    /// - [MDN reference][mdn]\n    /// - [`temporal_rs` documentation][temporal_rs-docs]\n    ///\n    /// [spec]: https://tc39.es/proposal-temporal/#sec-get-temporal.plaindate.prototype.monthcode\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Temporal/PlainDate/monthCode\n    /// [temporal_rs-docs]: https://docs.rs/temporal_rs/latest/temporal_rs/struct.PlainDate.html#method.month_code\n    fn get_month_code(this: &JsValue, _: &[JsValue], _: &mut Context) -> JsResult<JsValue> {\n        let obj = this\n            .as_object()\n            .ok_or_else(|| JsNativeError::typ().with_message(\"this must be an object.\"))?;\n\n        let Some(date) = obj.downcast_ref::<Self>() else {\n            return Err(JsNativeError::typ()\n                .with_message(\"the this object must be a PlainDate object.\")\n                .into());\n        };\n\n        Ok(JsString::from(date.inner.month_code().as_str()).into())\n    }\n\n    /// 3.3.9 get `Temporal.PlainDate.prototype.day`\n    ///\n    /// Returns the day in the calendar month for this `Temporal.PlainDate`.\n    ///\n    /// More information:\n    ///\n    /// - [ECMAScript Temporal proposal][spec]\n    /// - [MDN reference][mdn]\n    /// - [`temporal_rs` documentation][temporal_rs-docs]\n    ///\n    /// [spec]: https://tc39.es/proposal-temporal/#sec-get-temporal.plaindate.prototype.day\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Temporal/PlainDate/day\n    /// [temporal_rs-docs]: https://docs.rs/temporal_rs/latest/temporal_rs/struct.PlainDate.html#method.day\n    fn get_day(this: &JsValue, _: &[JsValue], _: &mut Context) -> JsResult<JsValue> {\n        let obj = this\n            .as_object()\n            .ok_or_else(|| JsNativeError::typ().with_message(\"this must be an object.\"))?;\n\n        let Some(date) = obj.downcast_ref::<Self>() else {\n            return Err(JsNativeError::typ()\n                .with_message(\"the this object must be a PlainDate object.\")\n                .into());\n        };\n\n        Ok(date.inner.day().into())\n    }\n\n    /// 3.3.10 get `Temporal.PlainDate.prototype.dayOfWeek`\n    ///\n    /// More information:\n    ///\n    /// - [ECMAScript Temporal proposal][spec]\n    /// - [MDN reference][mdn]\n    /// - [`temporal_rs` documentation][temporal_rs-docs]\n    ///\n    /// [spec]: https://tc39.es/proposal-temporal/#sec-get-temporal.plaindate.prototype.dayofweek\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Temporal/PlainDate/dayOfWeek\n    /// [temporal_rs-docs]: https://docs.rs/temporal_rs/latest/temporal_rs/struct.PlainDate.html#method.day_of_week\n    fn get_day_of_week(this: &JsValue, _: &[JsValue], _: &mut Context) -> JsResult<JsValue> {\n        let obj = this\n            .as_object()\n            .ok_or_else(|| JsNativeError::typ().with_message(\"this must be an object.\"))?;\n\n        let Some(date) = obj.downcast_ref::<Self>() else {\n            return Err(JsNativeError::typ()\n                .with_message(\"the this object must be a PlainDate object.\")\n                .into());\n        };\n\n        Ok(date.inner.day_of_week().into())\n    }\n\n    /// 3.3.11 get `Temporal.PlainDate.prototype.dayOfYear`\n    ///\n    /// More information:\n    ///\n    /// - [ECMAScript Temporal proposal][spec]\n    /// - [MDN reference][mdn]\n    /// - [`temporal_rs` documentation][temporal_rs-docs]\n    ///\n    /// [spec]: https://tc39.es/proposal-temporal/#sec-get-temporal.plaindate.prototype.dayofyear\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Temporal/PlainDate/dayOfYear\n    /// [temporal_rs-docs]: https://docs.rs/temporal_rs/latest/temporal_rs/struct.PlainDate.html#method.day_of_year\n    fn get_day_of_year(this: &JsValue, _: &[JsValue], _: &mut Context) -> JsResult<JsValue> {\n        let obj = this\n            .as_object()\n            .ok_or_else(|| JsNativeError::typ().with_message(\"this must be an object.\"))?;\n\n        let Some(date) = obj.downcast_ref::<Self>() else {\n            return Err(JsNativeError::typ()\n                .with_message(\"the this object must be a PlainDate object.\")\n                .into());\n        };\n\n        Ok(date.inner.day_of_year().into())\n    }\n\n    /// 3.3.12 get `Temporal.PlainDate.prototype.weekOfYear`\n    ///\n    /// More information:\n    ///\n    /// - [ECMAScript Temporal proposal][spec]\n    /// - [MDN reference][mdn]\n    /// - [`temporal_rs` documentation][temporal_rs-docs]\n    ///\n    /// [spec]: https://tc39.es/proposal-temporal/#sec-get-temporal.plaindate.prototype.weekofyear\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Temporal/PlainDate/weekOfYear\n    /// [temporal_rs-docs]: https://docs.rs/temporal_rs/latest/temporal_rs/struct.PlainDate.html#method.week_of_year\n    fn get_week_of_year(this: &JsValue, _: &[JsValue], _: &mut Context) -> JsResult<JsValue> {\n        let obj = this\n            .as_object()\n            .ok_or_else(|| JsNativeError::typ().with_message(\"this must be an object.\"))?;\n\n        let Some(date) = obj.downcast_ref::<Self>() else {\n            return Err(JsNativeError::typ()\n                .with_message(\"the this object must be a PlainDate object.\")\n                .into());\n        };\n\n        Ok(date.inner.week_of_year().into_or_undefined())\n    }\n\n    /// 3.3.13 get `Temporal.PlainDate.prototype.yearOfWeek`\n    ///\n    /// More information:\n    ///\n    /// - [ECMAScript Temporal proposal][spec]\n    /// - [MDN reference][mdn]\n    /// - [`temporal_rs` documentation][temporal_rs-docs]\n    ///\n    /// [spec]: https://tc39.es/proposal-temporal/#sec-get-temporal.plaindate.prototype.yearofweek\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Temporal/PlainDate/yearOfWeek\n    /// [temporal_rs-docs]: https://docs.rs/temporal_rs/latest/temporal_rs/struct.PlainDate.html#method.year_of_week\n    fn get_year_of_week(this: &JsValue, _: &[JsValue], _: &mut Context) -> JsResult<JsValue> {\n        let obj = this\n            .as_object()\n            .ok_or_else(|| JsNativeError::typ().with_message(\"this must be an object.\"))?;\n\n        let Some(date) = obj.downcast_ref::<Self>() else {\n            return Err(JsNativeError::typ()\n                .with_message(\"the this object must be a PlainDate object.\")\n                .into());\n        };\n\n        Ok(date.inner.year_of_week().into_or_undefined())\n    }\n\n    /// 3.3.14 get `Temporal.PlainDate.prototype.daysInWeek`\n    ///\n    /// More information:\n    ///\n    /// - [ECMAScript Temporal proposal][spec]\n    /// - [MDN reference][mdn]\n    /// - [`temporal_rs` documentation][temporal_rs-docs]\n    ///\n    /// [spec]: https://tc39.es/proposal-temporal/#sec-get-temporal.plaindate.prototype.daysinweek\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Temporal/PlainDate/daysInWeek\n    /// [temporal_rs-docs]: https://docs.rs/temporal_rs/latest/temporal_rs/struct.PlainDate.html#method.days_in_week\n    fn get_days_in_week(this: &JsValue, _: &[JsValue], _: &mut Context) -> JsResult<JsValue> {\n        let obj = this\n            .as_object()\n            .ok_or_else(|| JsNativeError::typ().with_message(\"this must be an object.\"))?;\n\n        let Some(date) = obj.downcast_ref::<Self>() else {\n            return Err(JsNativeError::typ()\n                .with_message(\"the this object must be a PlainDate object.\")\n                .into());\n        };\n\n        Ok(date.inner.days_in_week().into())\n    }\n\n    /// 3.3.15 get `Temporal.PlainDate.prototype.daysInMonth`\n    ///\n    /// More information:\n    ///\n    /// - [ECMAScript Temporal proposal][spec]\n    /// - [MDN reference][mdn]\n    /// - [`temporal_rs` documentation][temporal_rs-docs]\n    ///\n    /// [spec]: https://tc39.es/proposal-temporal/#sec-get-temporal.plaindate.prototype.daysinmonth\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Temporal/PlainDate/daysInMonth\n    /// [temporal_rs-docs]: https://docs.rs/temporal_rs/latest/temporal_rs/struct.PlainDate.html#method.days_in_month\n    fn get_days_in_month(this: &JsValue, _: &[JsValue], _: &mut Context) -> JsResult<JsValue> {\n        let obj = this\n            .as_object()\n            .ok_or_else(|| JsNativeError::typ().with_message(\"this must be an object.\"))?;\n\n        let Some(date) = obj.downcast_ref::<Self>() else {\n            return Err(JsNativeError::typ()\n                .with_message(\"the this object must be a PlainDate object.\")\n                .into());\n        };\n\n        Ok(date.inner.days_in_month().into())\n    }\n\n    /// 3.3.16 get `Temporal.PlainDate.prototype.daysInYear`\n    ///\n    /// More information:\n    ///\n    /// - [ECMAScript Temporal proposal][spec]\n    /// - [MDN reference][mdn]\n    /// - [`temporal_rs` documentation][temporal_rs-docs]\n    ///\n    /// [spec]: https://tc39.es/proposal-temporal/#sec-get-temporal.plaindate.prototype.daysinyear\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Temporal/PlainDate/daysInYear\n    /// [temporal_rs-docs]: https://docs.rs/temporal_rs/latest/temporal_rs/struct.PlainDate.html#method.days_in_year\n    fn get_days_in_year(this: &JsValue, _: &[JsValue], _: &mut Context) -> JsResult<JsValue> {\n        let obj = this\n            .as_object()\n            .ok_or_else(|| JsNativeError::typ().with_message(\"this must be an object.\"))?;\n\n        let Some(date) = obj.downcast_ref::<Self>() else {\n            return Err(JsNativeError::typ()\n                .with_message(\"the this object must be a PlainDate object.\")\n                .into());\n        };\n\n        Ok(date.inner.days_in_year().into())\n    }\n\n    /// 3.3.17 get `Temporal.PlainDate.prototype.monthsInYear`\n    ///\n    /// More information:\n    ///\n    /// - [ECMAScript Temporal proposal][spec]\n    /// - [MDN reference][mdn]\n    /// - [`temporal_rs` documentation][temporal_rs-docs]\n    ///\n    /// [spec]: https://tc39.es/proposal-temporal/#sec-get-temporal.plaindate.prototype.monthsinyear\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Temporal/PlainDate/monthsInYear\n    /// [temporal_rs-docs]: https://docs.rs/temporal_rs/latest/temporal_rs/struct.PlainDate.html#method.months_in_year\n    fn get_months_in_year(this: &JsValue, _: &[JsValue], _: &mut Context) -> JsResult<JsValue> {\n        let obj = this\n            .as_object()\n            .ok_or_else(|| JsNativeError::typ().with_message(\"this must be an object.\"))?;\n\n        let Some(date) = obj.downcast_ref::<Self>() else {\n            return Err(JsNativeError::typ()\n                .with_message(\"the this object must be a PlainDate object.\")\n                .into());\n        };\n\n        Ok(date.inner.months_in_year().into())\n    }\n\n    /// 3.3.18 get `Temporal.PlainDate.prototype.inLeapYear`\n    ///\n    /// More information:\n    ///\n    /// - [ECMAScript Temporal proposal][spec]\n    /// - [MDN reference][mdn]\n    /// - [`temporal_rs` documentation][temporal_rs-docs]\n    ///\n    /// [spec]: https://tc39.es/proposal-temporal/#sec-get-temporal.plaindate.prototype.inleapyear\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Temporal/PlainDate/inLeapYear\n    /// [temporal_rs-docs]: https://docs.rs/temporal_rs/latest/temporal_rs/struct.PlainDate.html#method.inLeapYear\n    fn get_in_leap_year(this: &JsValue, _: &[JsValue], _: &mut Context) -> JsResult<JsValue> {\n        let obj = this\n            .as_object()\n            .ok_or_else(|| JsNativeError::typ().with_message(\"this must be an object.\"))?;\n\n        let Some(date) = obj.downcast_ref::<Self>() else {\n            return Err(JsNativeError::typ()\n                .with_message(\"the this object must be a PlainDate object.\")\n                .into());\n        };\n\n        Ok(date.inner.in_leap_year().into())\n    }\n}\n\n// ==== `PlainDate` static methods implementation ====\n\nimpl PlainDate {\n    /// 3.2.2 `Temporal.PlainDate.from ( item [ , options ] )`\n    ///\n    /// More information:\n    ///\n    /// - [ECMAScript Temporal proposal][spec]\n    /// - [MDN reference][mdn]\n    ///\n    /// [spec]: https://tc39.es/proposal-temporal/#sec-temporal.plaindate.from\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Temporal/PlainDate/from\n    fn from(_: &JsValue, args: &[JsValue], context: &mut Context) -> JsResult<JsValue> {\n        let item = args.get_or_undefined(0);\n        let options = args.get(1);\n\n        let object = item.as_object();\n        if let Some(date) = object.as_ref().and_then(JsObject::downcast_ref::<Self>) {\n            let options = get_options_object(options.unwrap_or(&JsValue::undefined()))?;\n            let _ = get_option::<Overflow>(&options, js_string!(\"overflow\"), context)?;\n            return create_temporal_date(date.inner.clone(), None, context).map(Into::into);\n        }\n\n        let resolved_date = to_temporal_date(item, options.cloned(), context)?;\n        create_temporal_date(resolved_date, None, context).map(Into::into)\n    }\n\n    /// 3.2.3 `Temporal.PlainDate.compare ( one, two )`\n    ///\n    /// More information:\n    ///\n    /// - [ECMAScript Temporal proposal][spec]\n    /// - [MDN reference][mdn]\n    /// - [`temporal_rs` documentation][temporal_rs-docs]\n    ///\n    /// [spec]: https://tc39.es/proposal-temporal/#sec-temporal.plaindate.compare\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Temporal/PlainDate/compare\n    /// [temporal_rs-docs]: https://docs.rs/temporal_rs/latest/temporal_rs/struct.PlainDate.html#method.compare_iso\n    fn compare(_: &JsValue, args: &[JsValue], context: &mut Context) -> JsResult<JsValue> {\n        let one = to_temporal_date(args.get_or_undefined(0), None, context)?;\n        let two = to_temporal_date(args.get_or_undefined(1), None, context)?;\n\n        Ok((one.compare_iso(&two) as i8).into())\n    }\n}\n\n// ==== `PlainDate` method implementation ====\n\nimpl PlainDate {\n    /// 3.3.19 `Temporal.PlainDate.toPlainYearMonth ( )`\n    ///\n    /// More information:\n    ///\n    /// - [ECMAScript Temporal proposal][spec]\n    /// - [MDN reference][mdn]\n    /// - [`temporal_rs` documentation][temporal_rs-docs]\n    ///\n    /// [spec]: https://tc39.es/proposal-temporal/#sec-temporal.plaindate.prototype.toplainyearmonth\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Temporal/PlainDate/toPlainYearMonth\n    /// [temporal_rs-docs]: https://docs.rs/temporal_rs/latest/temporal_rs/struct.PlainDate.html#method.to_plain_year_month\n    fn to_plain_year_month(\n        this: &JsValue,\n        _: &[JsValue],\n        context: &mut Context,\n    ) -> JsResult<JsValue> {\n        // 1. Let temporalDate be the this value.\n        // 2. Perform ? RequireInternalSlot(temporalDate, [[InitializedTemporalDate]]).\n        let object = this.as_object();\n        let date = object\n            .as_ref()\n            .and_then(JsObject::downcast_ref::<Self>)\n            .ok_or_else(|| {\n                JsNativeError::typ().with_message(\"the this object must be a PlainDate object.\")\n            })?;\n\n        let year_month = date.inner.to_plain_year_month()?;\n        create_temporal_year_month(year_month, None, context)\n    }\n\n    /// 3.3.20 `Temporal.PlainDate.toPlainMonthDay ( )`\n    ///\n    /// More information:\n    ///\n    /// - [ECMAScript Temporal proposal][spec]\n    /// - [MDN reference][mdn]\n    /// - [`temporal_rs` documentation][temporal_rs-docs]\n    ///\n    /// [spec]: https://tc39.es/proposal-temporal/#sec-temporal.plaindate.prototype.toplainmonthday\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Temporal/PlainDate/toPlainMonthDay\n    /// [temporal_rs-docs]: https://docs.rs/temporal_rs/latest/temporal_rs/struct.PlainDate.html#method.to_plain_month_day\n    fn to_plain_month_day(\n        this: &JsValue,\n        _: &[JsValue],\n        context: &mut Context,\n    ) -> JsResult<JsValue> {\n        // 1. Let temporalDate be the this value.\n        // 2. Perform ? RequireInternalSlot(temporalDate, [[InitializedTemporalDate]]).\n        let object = this.as_object();\n        let date = object\n            .as_ref()\n            .and_then(JsObject::downcast_ref::<Self>)\n            .ok_or_else(|| {\n                JsNativeError::typ().with_message(\"the this object must be a PlainDate object.\")\n            })?;\n\n        let month_day = date.inner.to_plain_month_day()?;\n        create_temporal_month_day(month_day, None, context)\n    }\n\n    /// 3.3.21 `Temporal.PlainDate.add ( temporalDurationLike [ , options ] )`\n    ///\n    /// More information:\n    ///\n    /// - [ECMAScript Temporal proposal][spec]\n    /// - [MDN reference][mdn]\n    /// - [`temporal_rs` documentation][temporal_rs-docs]\n    ///\n    /// [spec]: https://tc39.es/proposal-temporal/#sec-temporal.plaindate.prototype.add\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Temporal/PlainDate/add\n    /// [temporal_rs-docs]: https://docs.rs/temporal_rs/latest/temporal_rs/struct.PlainDate.html#method.add\n    fn add(this: &JsValue, args: &[JsValue], context: &mut Context) -> JsResult<JsValue> {\n        // 1. Let temporalDate be the this value.\n        // 2. Perform ? RequireInternalSlot(temporalDate, [[InitializedTemporalDate]]).\n        let object = this.as_object();\n        let date = object\n            .as_ref()\n            .and_then(JsObject::downcast_ref::<Self>)\n            .ok_or_else(|| {\n                JsNativeError::typ().with_message(\"the this object must be a PlainDate object.\")\n            })?;\n\n        // 3. Let duration be ? ToTemporalDuration(temporalDurationLike).\n        let duration = to_temporal_duration_record(args.get_or_undefined(0), context)?;\n\n        // 4. Set options to ? GetOptionsObject(options).\n        let options = get_options_object(args.get_or_undefined(1))?;\n\n        let overflow = get_option::<Overflow>(&options, js_string!(\"overflow\"), context)?;\n\n        // 5. Let calendarRec be ? CreateCalendarMethodsRecord(temporalDate.[[Calendar]], « date-add »).\n        // 6. Return ? AddDate(calendarRec, temporalDate, duration, options).\n        let resolved_date = date.inner.add(&duration, overflow)?;\n        create_temporal_date(resolved_date, None, context).map(Into::into)\n    }\n\n    /// 3.3.22 `Temporal.PlainDate.subtract ( temporalDurationLike [ , options ] )`\n    ///\n    /// More information:\n    ///\n    /// - [ECMAScript Temporal proposal][spec]\n    /// - [MDN reference][mdn]\n    /// - [`temporal_rs` documentation][temporal_rs-docs]\n    ///\n    /// [spec]: https://tc39.es/proposal-temporal/#sec-temporal.plaindate.prototype.subtract\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Temporal/PlainDate/subtract\n    /// [temporal_rs-docs]: https://docs.rs/temporal_rs/latest/temporal_rs/struct.PlainDate.html#method.subtract\n    fn subtract(this: &JsValue, args: &[JsValue], context: &mut Context) -> JsResult<JsValue> {\n        // 1. Let temporalDate be the this value.\n        // 2. Perform ? RequireInternalSlot(temporalDate, [[InitializedTemporalDate]]).\n        let object = this.as_object();\n        let date = object\n            .as_ref()\n            .and_then(JsObject::downcast_ref::<Self>)\n            .ok_or_else(|| {\n                JsNativeError::typ().with_message(\"the this object must be a PlainDate object.\")\n            })?;\n\n        // 3. Let duration be ? ToTemporalDuration(temporalDurationLike).\n        let duration = to_temporal_duration_record(args.get_or_undefined(0), context)?;\n\n        // 4. Set options to ? GetOptionsObject(options).\n        let options = get_options_object(args.get_or_undefined(1))?;\n        let overflow = get_option::<Overflow>(&options, js_string!(\"overflow\"), context)?;\n\n        // 5. Let negatedDuration be CreateNegatedTemporalDuration(duration).\n        // 6. Let calendarRec be ? CreateCalendarMethodsRecord(temporalDate.[[Calendar]], « date-add »).\n        // 7. Return ? AddDate(calendarRec, temporalDate, negatedDuration, options).\n        let resolved_date = date.inner.subtract(&duration, overflow)?;\n        create_temporal_date(resolved_date, None, context).map(Into::into)\n    }\n\n    // 3.3.23 `Temporal.PlainDate.prototype.with ( temporalDateLike [ , options ] )`\n    ///\n    /// More information:\n    ///\n    /// - [ECMAScript Temporal proposal][spec]\n    /// - [MDN reference][mdn]\n    /// - [`temporal_rs` documentation][temporal_rs-docs]\n    ///\n    /// [spec]: https://tc39.es/proposal-temporal/#sec-temporal.plaindate.prototype.with\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Temporal/PlainDate/with\n    /// [temporal_rs-docs]: https://docs.rs/temporal_rs/latest/temporal_rs/struct.PlainDate.html#method.with\n    fn with(this: &JsValue, args: &[JsValue], context: &mut Context) -> JsResult<JsValue> {\n        // 1. Let temporalDate be the this value.\n        // 2. Perform ? RequireInternalSlot(temporalDate, [[InitializedTemporalDate]]).\n        let object = this.as_object();\n        let date = object\n            .as_ref()\n            .and_then(JsObject::downcast_ref::<Self>)\n            .ok_or_else(|| {\n                JsNativeError::typ().with_message(\"the this object must be a PlainDate object.\")\n            })?;\n\n        // 3. If ? IsPartialTemporalObject(temporalDateLike) is false, throw a TypeError exception.\n        let Some(partial_object) =\n            super::is_partial_temporal_object(args.get_or_undefined(0), context)?\n        else {\n            return Err(JsNativeError::typ()\n                .with_message(\"with object was not a PartialTemporalObject.\")\n                .into());\n        };\n\n        // SKIP: Steps 4-9 are handled by the with method of temporal_rs's Date\n        // 4. Let calendar be temporalDate.[[Calendar]].\n        // 5. Let fields be ISODateToFields(calendar, temporalDate.[[ISODate]], date).\n        // 6. Let partialDate be ? PrepareCalendarFields(calendar, temporalDateLike, « year, month, month-code, day », « », partial).\n        // 7. Set fields to CalendarMergeFields(calendar, fields, partialDate).\n        let fields = to_calendar_fields(&partial_object, date.inner.calendar(), context)?;\n\n        // 8. Let resolvedOptions be ? GetOptionsObject(options).\n        let options = get_options_object(args.get_or_undefined(1))?;\n        // 9. Let overflow be ? GetTemporalOverflowOption(resolvedOptions).\n        let overflow = get_option::<Overflow>(&options, js_string!(\"overflow\"), context)?;\n\n        // 10. Return ? CalendarDateFromFields(calendarRec, fields, resolvedOptions).\n        let resolved_date = date.inner.with(fields, overflow)?;\n        create_temporal_date(resolved_date, None, context).map(Into::into)\n    }\n\n    // UPDATED: nekevss, 2025-07-21\n    /// 3.3.24 `Temporal.PlainDate.prototype.withCalendar ( calendarLike )`\n    ///\n    /// More information:\n    ///\n    /// - [ECMAScript Temporal proposal][spec]\n    /// - [MDN reference][mdn]\n    /// - [`temporal_rs` documentation][temporal_rs-docs]\n    ///\n    /// [spec]: https://tc39.es/proposal-temporal/#sec-temporal.plaindate.prototype.withcalendar\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Temporal/PlainDate/withCalendar\n    /// [temporal_rs-docs]: https://docs.rs/temporal_rs/latest/temporal_rs/struct.PlainDate.html#method.with_calendar\n    fn with_calendar(this: &JsValue, args: &[JsValue], context: &mut Context) -> JsResult<JsValue> {\n        // 1. Let plainDate be the this value.\n        let object = this.as_object();\n        // 2. Perform ? RequireInternalSlot(plainDate, [[InitializedTemporalDate]]).\n        let date = object\n            .as_ref()\n            .and_then(JsObject::downcast_ref::<Self>)\n            .ok_or_else(|| {\n                JsNativeError::typ().with_message(\"the this object must be a PlainDate object.\")\n            })?;\n\n        // 3. Let calendar be ? ToTemporalCalendarIdentifier(calendarLike).\n        let calendar = to_temporal_calendar_identifier(args.get_or_undefined(0))?;\n        // 4. Return ! CreateTemporalDate(plainDate.[[ISODate]], calendar).\n        let resolved_date = date.inner.with_calendar(calendar);\n        create_temporal_date(resolved_date, None, context).map(Into::into)\n    }\n\n    /// 3.3.25 `Temporal.PlainDate.prototype.until ( other [ , options ] )`\n    ///\n    /// More information:\n    ///\n    /// - [ECMAScript Temporal proposal][spec]\n    /// - [MDN reference][mdn]\n    /// - [`temporal_rs` documentation][temporal_rs-docs]\n    ///\n    /// [spec]: https://tc39.es/proposal-temporal/#sec-temporal.plaindate.prototype.until\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Temporal/PlainDate/until\n    /// [temporal_rs-docs]: https://docs.rs/temporal_rs/latest/temporal_rs/struct.PlainDate.html#method.until\n    fn until(this: &JsValue, args: &[JsValue], context: &mut Context) -> JsResult<JsValue> {\n        // 1. Let temporalDate be the this value.\n        // 2. Perform ? RequireInternalSlot(temporalDate, [[InitializedTemporalDate]]).\n        let object = this.as_object();\n        let date = object\n            .as_ref()\n            .and_then(JsObject::downcast_ref::<Self>)\n            .ok_or_else(|| {\n                JsNativeError::typ().with_message(\"the this object must be a PlainDate object.\")\n            })?;\n\n        let other = to_temporal_date(args.get_or_undefined(0), None, context)?;\n\n        // 3. Return ? DifferenceTemporalPlainDate(until, temporalDate, other, options).\n        let options = get_options_object(args.get_or_undefined(1))?;\n        let settings = get_difference_settings(&options, context)?;\n\n        create_temporal_duration(date.inner.until(&other, settings)?, None, context).map(Into::into)\n    }\n\n    /// 3.3.26 `Temporal.PlainDate.prototype.since ( other [ , options ] )`\n    ///\n    /// More information:\n    ///\n    /// - [ECMAScript Temporal proposal][spec]\n    /// - [MDN reference][mdn]\n    /// - [`temporal_rs` documentation][temporal_rs-docs]\n    ///\n    /// [spec]: https://tc39.es/proposal-temporal/#sec-temporal.plaindate.prototype.since\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Temporal/PlainDate/since\n    /// [temporal_rs-docs]: https://docs.rs/temporal_rs/latest/temporal_rs/struct.PlainDate.html#method.since\n    fn since(this: &JsValue, args: &[JsValue], context: &mut Context) -> JsResult<JsValue> {\n        // 1. Let temporalDate be the this value.\n        // 2. Perform ? RequireInternalSlot(temporalDate, [[InitializedTemporalDate]]).\n        let object = this.as_object();\n        let date = object\n            .as_ref()\n            .and_then(JsObject::downcast_ref::<Self>)\n            .ok_or_else(|| {\n                JsNativeError::typ().with_message(\"the this object must be a PlainDate object.\")\n            })?;\n\n        // 3. Return ? DifferenceTemporalPlainDate(since, temporalDate, other, options).\n        let other = to_temporal_date(args.get_or_undefined(0), None, context)?;\n\n        let options = get_options_object(args.get_or_undefined(1))?;\n        let settings = get_difference_settings(&options, context)?;\n\n        create_temporal_duration(date.inner.since(&other, settings)?, None, context).map(Into::into)\n    }\n\n    /// 3.3.27 `Temporal.PlainDate.prototype.equals ( other )`\n    ///\n    /// More information:\n    ///\n    /// - [ECMAScript Temporal proposal][spec]\n    /// - [MDN reference][mdn]\n    /// - [`temporal_rs` documentation][temporal_rs-docs]\n    ///\n    /// [spec]: https://tc39.es/proposal-temporal/#sec-temporal.plaindate.prototype.equals\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Temporal/PlainDate/equals\n    /// [temporal_rs-docs]: https://docs.rs/temporal_rs/latest/temporal_rs/struct.PlainDate.html#impl-Eq-for-PlainDate\n    fn equals(this: &JsValue, args: &[JsValue], context: &mut Context) -> JsResult<JsValue> {\n        let object = this.as_object();\n        let date = object\n            .as_ref()\n            .and_then(JsObject::downcast_ref::<Self>)\n            .ok_or_else(|| {\n                JsNativeError::typ().with_message(\"the this object must be a PlainDate object.\")\n            })?;\n\n        let other = to_temporal_date(args.get_or_undefined(0), None, context)?;\n\n        Ok((date.inner == other).into())\n    }\n\n    /// 3.3.28 `Temporal.PlainDate.prototype.toPlainDateTime ( [ temporalTime ] )`\n    ///\n    /// More information:\n    ///\n    /// - [ECMAScript Temporal proposal][spec]\n    /// - [MDN reference][mdn]\n    /// - [`temporal_rs` documentation][temporal_rs-docs]\n    ///\n    /// [spec]: https://tc39.es/proposal-temporal/#sec-temporal.plaindate.prototype.toplaindatetime\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Temporal/PlainDate/toPlainDateTime\n    /// [temporal_rs-docs]: https://docs.rs/temporal_rs/latest/temporal_rs/struct.PlainDate.html#method.to_plain_date_time\n    fn to_plain_date_time(\n        this: &JsValue,\n        args: &[JsValue],\n        context: &mut Context,\n    ) -> JsResult<JsValue> {\n        // 1. Let temporalDate be the this value.\n        // 2. Perform ? RequireInternalSlot(temporalDate, [[InitializedTemporalDate]]).\n        let object = this.as_object();\n        let date = object\n            .as_ref()\n            .and_then(JsObject::downcast_ref::<Self>)\n            .ok_or_else(|| {\n                JsNativeError::typ().with_message(\"the this object must be a PlainDate object.\")\n            })?;\n\n        // 3. Set temporalTime to ? ToTemporalTimeOrMidnight(temporalTime).\n        let time = args\n            .get_or_undefined(0)\n            .map(|v| to_temporal_time(v, None, context))\n            .transpose()?;\n        // 4. Return ? CreateTemporalDateTime(temporalDate.[[ISOYear]], temporalDate.[[ISOMonth]], temporalDate.[[ISODay]], temporalTime.[[ISOHour]], temporalTime.[[ISOMinute]], temporalTime.[[ISOSecond]], temporalTime.[[ISOMillisecond]], temporalTime.[[ISOMicrosecond]], temporalTime.[[ISONanosecond]], temporalDate.[[Calendar]]).\n        create_temporal_datetime(date.inner.to_plain_date_time(time)?, None, context)\n            .map(Into::into)\n    }\n\n    /// 3.3.29 `Temporal.PlainDate.prototype.toZonedDateTime ( item )`\n    ///\n    /// More information:\n    ///\n    /// - [ECMAScript Temporal proposal][spec]\n    /// - [MDN reference][mdn]\n    /// - [`temporal_rs` documentation][temporal_rs-docs]\n    ///\n    /// [spec]: https://tc39.es/proposal-temporal/#sec-temporal.plaindate.prototype.tozoneddatetime\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Temporal/PlainDate/toZonedDateTime\n    /// [temporal_rs-docs]: https://docs.rs/temporal_rs/latest/temporal_rs/struct.PlainDate.html#method.to_zoned_date_time\n    fn to_zoned_date_time(\n        this: &JsValue,\n        args: &[JsValue],\n        context: &mut Context,\n    ) -> JsResult<JsValue> {\n        // 1. Let temporalDate be the this value.\n        // 2. Perform ? RequireInternalSlot(temporalDate, [[InitializedTemporalDate]]).\n        let object = this.as_object();\n        let date = object\n            .as_ref()\n            .and_then(JsObject::downcast_ref::<Self>)\n            .ok_or_else(|| {\n                JsNativeError::typ().with_message(\"the this object must be a PlainDate object.\")\n            })?;\n\n        let item = args.get_or_undefined(0);\n        // 3. If item is an Object, then\n        let (timezone, time) = if let Some(obj) = item.as_object() {\n            // a. Let timeZoneLike be ? Get(item, \"timeZone\").\n            let time_zone_like = obj.get(js_string!(\"timeZone\"), context)?;\n            // b. If timeZoneLike is undefined, then\n            if time_zone_like.is_undefined() {\n                // i. Let timeZone be ? ToTemporalTimeZoneIdentifier(item).\n                // ii. Let temporalTime be undefined.\n                (\n                    to_temporal_timezone_identifier(&time_zone_like, context)?,\n                    None,\n                )\n            // c. Else,\n            } else {\n                // i. Let timeZone be ? ToTemporalTimeZoneIdentifier(timeZoneLike).\n                let tz = to_temporal_timezone_identifier(&time_zone_like, context)?;\n                // ii. Let temporalTime be ? Get(item, \"plainTime\").\n                let plain_time = obj\n                    .get(js_string!(\"plainTime\"), context)?\n                    .map(|v| to_temporal_time(v, None, context))\n                    .transpose()?;\n\n                (tz, plain_time)\n            }\n        // 4. Else,\n        } else {\n            // a. Let timeZone be ? ToTemporalTimeZoneIdentifier(item).\n            // b. Let temporalTime be undefined.\n            (to_temporal_timezone_identifier(item, context)?, None)\n        };\n\n        let result = date.inner.to_zoned_date_time_with_provider(\n            timezone,\n            time,\n            context.timezone_provider(),\n        )?;\n\n        // 7. Return ! CreateTemporalZonedDateTime(epochNs, timeZone, temporalDate.[[Calendar]]).\n        create_temporal_zoneddatetime(result, None, context).map(Into::into)\n    }\n\n    /// 3.3.30 `Temporal.PlainDate.prototype.toString ( [ options ] )`\n    ///\n    /// More information:\n    ///\n    /// - [ECMAScript Temporal proposal][spec]\n    /// - [MDN reference][mdn]\n    /// - [`temporal_rs` documentation][temporal_rs-docs]\n    ///\n    /// [spec]: https://tc39.es/proposal-temporal/#sec-temporal.plaindate.prototype.tostring\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Temporal/PlainDate/toString\n    /// [temporal_rs-docs]: https://docs.rs/temporal_rs/latest/temporal_rs/struct.PlainDate.html#method.to_ixdtf_string\n    fn to_string(this: &JsValue, args: &[JsValue], context: &mut Context) -> JsResult<JsValue> {\n        let object = this.as_object();\n        let date = object\n            .as_ref()\n            .and_then(JsObject::downcast_ref::<Self>)\n            .ok_or_else(|| {\n                JsNativeError::typ().with_message(\"the this object must be a PlainDate object.\")\n            })?;\n\n        let options = get_options_object(args.get_or_undefined(0))?;\n        let display_calendar =\n            get_option::<DisplayCalendar>(&options, js_string!(\"calendarName\"), context)?\n                .unwrap_or(DisplayCalendar::Auto);\n        Ok(JsString::from(date.inner.to_ixdtf_string(display_calendar)).into())\n    }\n\n    /// 3.3.31 `Temporal.PlainDate.prototype.toLocaleString ( [ locales [ , options ] ] )`\n    ///\n    /// More information:\n    ///\n    /// - [ECMAScript Temporal proposal][spec]\n    /// - [MDN reference][mdn]\n    ///\n    /// [spec]: https://tc39.es/proposal-temporal/#sec-temporal.plaindate.prototype.tolocalestring\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Temporal/PlainDate/toLocaleString\n    fn to_locale_string(this: &JsValue, _: &[JsValue], _: &mut Context) -> JsResult<JsValue> {\n        // TODO: Update for ECMA-402 compliance\n        let object = this.as_object();\n        let date = object\n            .as_ref()\n            .and_then(JsObject::downcast_ref::<Self>)\n            .ok_or_else(|| {\n                JsNativeError::typ().with_message(\"the this object must be a PlainDate object.\")\n            })?;\n\n        Ok(JsString::from(date.inner.to_string()).into())\n    }\n\n    /// 3.3.32 `Temporal.PlainDate.prototype.toJSON ( )`\n    ///\n    /// More information:\n    ///\n    /// - [ECMAScript Temporal proposal][spec]\n    /// - [MDN reference][mdn]\n    ///\n    /// [spec]: https://tc39.es/proposal-temporal/#sec-temporal.plaindate.prototype.toJSON\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Temporal/PlainDate/toJSON\n    fn to_json(this: &JsValue, _: &[JsValue], _: &mut Context) -> JsResult<JsValue> {\n        let object = this.as_object();\n        let date = object\n            .as_ref()\n            .and_then(JsObject::downcast_ref::<Self>)\n            .ok_or_else(|| {\n                JsNativeError::typ().with_message(\"the this object must be a PlainDate object.\")\n            })?;\n\n        Ok(JsString::from(date.inner.to_string()).into())\n    }\n\n    /// 3.3.33 `Temporal.PlainDate.prototype.valueOf ( )`\n    ///\n    /// More information:\n    ///\n    /// - [ECMAScript Temporal proposal][spec]\n    /// - [MDN reference][mdn]\n    ///\n    /// [spec]: https://tc39.es/proposal-temporal/#sec-temporal.plaindate.prototype.valueof\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Temporal/PlainDate/valueOf\n    pub(crate) fn value_of(_this: &JsValue, _: &[JsValue], _: &mut Context) -> JsResult<JsValue> {\n        Err(JsNativeError::typ()\n            .with_message(\"`valueOf` not supported by Temporal built-ins. See 'compare', 'equals', or `toString`\")\n            .into())\n    }\n}\n\n// ==== `PlainDate` Abstract Operations ====\n\n/// 3.5.3 `CreateTemporalDate ( isoYear, isoMonth, isoDay, calendar [ , newTarget ] )`\npub(crate) fn create_temporal_date(\n    inner: InnerDate,\n    new_target: Option<&JsValue>,\n    context: &mut Context,\n) -> JsResult<JsObject> {\n    // 1. If IsValidISODate(isoYear, isoMonth, isoDay) is false, throw a RangeError exception.\n    // 2. If ISODateTimeWithinLimits(isoYear, isoMonth, isoDay, 12, 0, 0, 0, 0, 0) is false, throw a RangeError exception.\n\n    // 3. If newTarget is not present, set newTarget to %Temporal.PlainDate%.\n    let new_target = if let Some(new_target) = new_target {\n        new_target.clone()\n    } else {\n        context\n            .realm()\n            .intrinsics()\n            .constructors()\n            .plain_date()\n            .constructor()\n            .into()\n    };\n\n    // 4. Let object be ? OrdinaryCreateFromConstructor(newTarget, \"%Temporal.PlainDate.prototype%\", « [[InitializedTemporalDate]], [[ISOYear]], [[ISOMonth]], [[ISODay]], [[Calendar]] »).\n    let prototype =\n        get_prototype_from_constructor(&new_target, StandardConstructors::plain_date, context)?;\n\n    // 5. Set object.[[ISOYear]] to isoYear.\n    // 6. Set object.[[ISOMonth]] to isoMonth.\n    // 7. Set object.[[ISODay]] to isoDay.\n    // 8. Set object.[[Calendar]] to calendar.\n    let obj = JsObject::from_proto_and_data(prototype, PlainDate::new(inner));\n\n    // 9. Return object.\n    Ok(obj)\n}\n\n/// 3.5.4 `ToTemporalDate ( item [ , options ] )`\n///\n/// Converts an ambiguous `JsValue` into a `PlainDate`\npub(crate) fn to_temporal_date(\n    item: &JsValue,\n    options: Option<JsValue>,\n    context: &mut Context,\n) -> JsResult<InnerDate> {\n    // 1. If options is not present, set options to undefined.\n    let options = options.unwrap_or_default();\n\n    // 2. Assert: Type(options) is Object or Undefined.\n    // 3. If options is not undefined, set options to ? SnapshotOwnProperties(? GetOptionsObject(options), null).\n\n    // 4. If Type(item) is Object, then\n    if let Some(object) = item.as_object() {\n        // a. If item has an [[InitializedTemporalDate]] internal slot, then\n        if let Some(date) = object.downcast_ref::<PlainDate>() {\n            let _options_obj = get_options_object(&options)?;\n            return Ok(date.inner.clone());\n        // b. If item has an [[InitializedTemporalZonedDateTime]] internal slot, then\n        } else if let Some(zdt) = object.downcast_ref::<ZonedDateTime>() {\n            let options_obj = get_options_object(&options)?;\n            // i. Perform ? ToTemporalOverflow(options).\n            let _overflow = get_option(&options_obj, js_string!(\"overflow\"), context)?\n                .unwrap_or(Overflow::Constrain);\n\n            // ii. Let instant be ! CreateTemporalInstant(item.[[Nanoseconds]]).\n            // iii. Let plainDateTime be ? GetPlainDateTimeFor(item.[[TimeZone]], instant, item.[[Calendar]]).\n            // iv. Return ! CreateTemporalDate(plainDateTime.[[ISOYear]], plainDateTime.[[ISOMonth]], plainDateTime.[[ISODay]], plainDateTime.[[Calendar]]).\n            return Ok(zdt.inner.to_plain_date());\n        // c. If item has an [[InitializedTemporalDateTime]] internal slot, then\n        } else if let Some(dt) = object.downcast_ref::<PlainDateTime>() {\n            let options_obj = get_options_object(&options)?;\n            // i. Perform ? ToTemporalOverflow(options).\n            let _overflow = get_option(&options_obj, js_string!(\"overflow\"), context)?\n                .unwrap_or(Overflow::Constrain);\n\n            let date = InnerDate::from(dt.inner.clone());\n\n            // ii. Return ! CreateTemporalDate(item.[[ISOYear]], item.[[ISOMonth]], item.[[ISODay]], item.[[Calendar]]).\n            return Ok(date);\n        }\n        // NOTE: d. is called in to_partial_date_record\n        // d. Let calendar be ? GetTemporalCalendarIdentifierWithISODefault(item).\n        // e. Let fields be ? PrepareCalendarFields(calendar, item, « year, month, month-code, day », «», «»).\n        let partial = to_partial_date_record(&object, context)?;\n        // f. Let resolvedOptions be ? GetOptionsObject(options).\n        let resolved_options = get_options_object(&options)?;\n        // g. Let overflow be ? GetTemporalOverflowOption(resolvedOptions).\n        let overflow = get_option::<Overflow>(&resolved_options, js_string!(\"overflow\"), context)?;\n        // h. Let isoDate be ? CalendarDateFromFields(calendar, fields, overflow).\n        // i. Return ! CreateTemporalDate(isoDate, calendar).\n        return Ok(InnerDate::from_partial(partial, overflow)?);\n    }\n\n    // 5. If item is not a String, throw a TypeError exception.\n    let Some(date_like_string) = item.as_string() else {\n        return Err(JsNativeError::typ()\n            .with_message(\"ToTemporalDate item must be an object or string.\")\n            .into());\n    };\n\n    // 4. Let result be ? ParseISODateTime(item, « TemporalDateTimeString[~Zoned] »).\n    let result = date_like_string\n        .to_std_string_escaped()\n        .parse::<InnerDate>()\n        .map_err(|err| JsNativeError::range().with_message(err.to_string()))?;\n\n    // 5. Let calendar be result.[[Calendar]].\n    // 6. If calendar is empty, set calendar to \"iso8601\".\n    // 7. Set calendar to ? CanonicalizeCalendar(calendar).\n    // 8. Let resolvedOptions be ? GetOptionsObject(options).\n    let resolved_options = get_options_object(&options)?;\n    // 9. Perform ? GetTemporalOverflowOption(resolvedOptions).\n    let _overflow = get_option::<Overflow>(&resolved_options, js_string!(\"overflow\"), context)?\n        .unwrap_or(Overflow::Constrain);\n\n    // 10. Let isoDate be CreateISODateRecord(result.[[Year]], result.[[Month]], result.[[Day]]).\n    // 11. Return ? CreateTemporalDate(isoDate, calendar).\n    Ok(result)\n}\n\n// TODO: For order of operations, `to_partial_date_record` may need to take a `Option<Calendar>` arg.\npub(crate) fn to_partial_date_record(\n    partial_object: &JsObject,\n    context: &mut Context,\n) -> JsResult<PartialDate> {\n    let calendar = get_temporal_calendar_slot_value_with_default(partial_object, context)?;\n    // TODO: Most likely need to use an iterator to handle.\n    let calendar_fields = to_calendar_fields(partial_object, &calendar, context)?;\n    Ok(PartialDate {\n        calendar_fields,\n        calendar,\n    })\n}\n\npub(crate) fn to_calendar_fields(\n    obj: &JsObject,\n    calendar: &Calendar,\n    context: &mut Context,\n) -> JsResult<CalendarFields> {\n    let day = obj\n        .get(js_string!(\"day\"), context)?\n        .map(|v| {\n            let finite = v.to_finitef64(context)?;\n            finite\n                .as_positive_integer_with_truncation()\n                .map_err(JsError::from)\n        })\n        .transpose()?;\n    // TODO: `temporal_rs` needs a `has_era` method\n    let has_no_era = calendar.kind() == AnyCalendarKind::Iso\n        || calendar.kind() == AnyCalendarKind::Chinese\n        || calendar.kind() == AnyCalendarKind::Dangi;\n    let (era, era_year) = if has_no_era {\n        (None, None)\n    } else {\n        let era = obj\n            .get(js_string!(\"era\"), context)?\n            .map(|v| {\n                let v = v.to_primitive(context, crate::value::PreferredType::String)?;\n                let Some(era) = v.as_string() else {\n                    return Err(JsError::from(\n                        JsNativeError::typ()\n                            .with_message(\"The monthCode field value must be a string.\"),\n                    ));\n                };\n                // TODO: double check if an invalid monthCode is a range or type error.\n                TinyAsciiStr::<19>::try_from_str(&era.to_std_string_escaped())\n                    .map_err(|e| JsError::from(JsNativeError::range().with_message(e.to_string())))\n            })\n            .transpose()?;\n        let era_year = obj\n            .get(js_string!(\"eraYear\"), context)?\n            .map(|v| {\n                let finite = v.to_finitef64(context)?;\n                Ok::<i32, JsError>(finite.as_integer_with_truncation::<i32>())\n            })\n            .transpose()?;\n        (era, era_year)\n    };\n    let month = obj\n        .get(js_string!(\"month\"), context)?\n        .map(|v| {\n            let finite = v.to_finitef64(context)?;\n            finite\n                .as_positive_integer_with_truncation()\n                .map_err(JsError::from)\n        })\n        .transpose()?;\n    let month_code = obj\n        .get(js_string!(\"monthCode\"), context)?\n        .map(|v| {\n            let v = v.to_primitive(context, crate::value::PreferredType::String)?;\n            let Some(month_code) = v.as_string() else {\n                return Err(JsNativeError::typ()\n                    .with_message(\"The monthCode field value must be a string.\")\n                    .into());\n            };\n            MonthCode::from_str(&month_code.to_std_string_escaped()).map_err(JsError::from)\n        })\n        .transpose()?;\n    let year = obj\n        .get(js_string!(\"year\"), context)?\n        .map(|v| {\n            let finite = v.to_finitef64(context)?;\n            Ok::<i32, JsError>(finite.as_integer_with_truncation::<i32>())\n        })\n        .transpose()?;\n    Ok(CalendarFields {\n        year,\n        month,\n        month_code,\n        day,\n        era,\n        era_year,\n    })\n}\n"
  },
  {
    "path": "core/engine/src/builtins/temporal/plain_date/tests.rs",
    "content": "use crate::{JsNativeErrorKind, TestAction, run_test_actions};\n\n#[test]\nfn property_bag_null_option_value() {\n    run_test_actions([TestAction::assert_native_error(\n        \"Temporal.PlainDate.from({ year: 1976, month: 11, day: 18}, null)\",\n        JsNativeErrorKind::Type,\n        \"GetOptionsObject: provided options is not an object\",\n    )]);\n}\n\n#[test]\nfn pd_null_option_value() {\n    run_test_actions([TestAction::assert_native_error(\n        \"Temporal.PlainDate.from(new Temporal.PlainDate(1976, 11, 18), null)\",\n        JsNativeErrorKind::Type,\n        \"GetOptionsObject: provided options is not an object\",\n    )]);\n}\n\n#[test]\nfn pdt_null_option_value() {\n    run_test_actions([TestAction::assert_native_error(\n        \"Temporal.PlainDate.from(new Temporal.PlainDateTime(1976, 11, 18), null)\",\n        JsNativeErrorKind::Type,\n        \"GetOptionsObject: provided options is not an object\",\n    )]);\n}\n\n#[test]\nfn zdt_null_option_value() {\n    run_test_actions([TestAction::assert_native_error(\n        \"Temporal.PlainDate.from(new Temporal.ZonedDateTime(0n, 'UTC'), null)\",\n        JsNativeErrorKind::Type,\n        \"GetOptionsObject: provided options is not an object\",\n    )]);\n}\n\n#[test]\nfn string_null_option_value() {\n    run_test_actions([TestAction::assert_native_error(\n        \"Temporal.PlainDate.from('1976-11-18Z', null)\",\n        JsNativeErrorKind::Range,\n        \"RangeError: Unexpected character found after parsing was completed.\",\n    )]);\n}\n"
  },
  {
    "path": "core/engine/src/builtins/temporal/plain_date_time/mod.rs",
    "content": "//! Boa's implementation of the ECMAScript `Temporal.PlainDateTime` built-in object.\n\nuse std::str::FromStr;\n\nuse crate::{\n    Context, JsArgs, JsData, JsError, JsNativeError, JsObject, JsResult, JsString, JsSymbol,\n    JsValue,\n    builtins::{\n        BuiltInBuilder, BuiltInConstructor, BuiltInObject, IntrinsicObject,\n        options::{get_option, get_options_object},\n        temporal::calendar::to_temporal_calendar_identifier,\n    },\n    context::intrinsics::{Intrinsics, StandardConstructor, StandardConstructors},\n    js_string,\n    object::internal_methods::get_prototype_from_constructor,\n    property::Attribute,\n    realm::Realm,\n    string::StaticJsStrings,\n    value::IntoOrUndefined,\n};\nuse boa_gc::{Finalize, Trace};\n\n#[cfg(test)]\nmod tests;\n\nuse icu_calendar::AnyCalendarKind;\nuse temporal_rs::{\n    Calendar, MonthCode, PlainDateTime as InnerDateTime, TinyAsciiStr,\n    fields::{CalendarFields, DateTimeFields},\n    options::{\n        Disambiguation, DisplayCalendar, Overflow, RoundingIncrement, RoundingMode,\n        RoundingOptions, ToStringRoundingOptions, Unit,\n    },\n    partial::{PartialDateTime, PartialTime},\n};\n\nuse super::{\n    PlainDate, ZonedDateTime,\n    calendar::get_temporal_calendar_slot_value_with_default,\n    create_temporal_date, create_temporal_duration, create_temporal_time,\n    create_temporal_zoneddatetime,\n    options::{TemporalUnitGroup, get_difference_settings, get_digits_option, get_temporal_unit},\n    to_temporal_duration_record, to_temporal_time, to_temporal_timezone_identifier,\n};\nuse crate::value::JsVariant;\n\n/// The `Temporal.PlainDateTime` built-in implementation.\n///\n/// More information:\n///\n/// - [ECMAScript Temporal proposal][spec]\n/// - [MDN reference][mdn]\n/// - [`temporal_rs` documentation][temporal_rs-docs]\n///\n/// [spec]: https://tc39.es/proposal-temporal/#sec-temporal-plaindatetime-objects\n/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Temporal/PlainDateTime\n/// [temporal_rs-docs]: https://docs.rs/temporal_rs/latest/temporal_rs/struct.PlainDateTime.html\n#[derive(Debug, Clone, Trace, Finalize, JsData)]\n#[boa_gc(unsafe_empty_trace)] // Safety: InnerDateTime does not contain any traceable types.\npub struct PlainDateTime {\n    pub(crate) inner: InnerDateTime,\n}\n\nimpl PlainDateTime {\n    fn new(inner: InnerDateTime) -> Self {\n        Self { inner }\n    }\n\n    pub(crate) fn inner(&self) -> &InnerDateTime {\n        &self.inner\n    }\n}\n\nimpl BuiltInObject for PlainDateTime {\n    const NAME: JsString = StaticJsStrings::PLAIN_DATETIME_NAME;\n}\n\nimpl IntrinsicObject for PlainDateTime {\n    fn init(realm: &Realm) {\n        let get_calendar_id = BuiltInBuilder::callable(realm, Self::get_calendar_id)\n            .name(js_string!(\"get calendarId\"))\n            .build();\n\n        let get_era = BuiltInBuilder::callable(realm, Self::get_era)\n            .name(js_string!(\"get era\"))\n            .build();\n\n        let get_era_year = BuiltInBuilder::callable(realm, Self::get_era_year)\n            .name(js_string!(\"get eraYear\"))\n            .build();\n\n        let get_year = BuiltInBuilder::callable(realm, Self::get_year)\n            .name(js_string!(\"get year\"))\n            .build();\n\n        let get_month = BuiltInBuilder::callable(realm, Self::get_month)\n            .name(js_string!(\"get month\"))\n            .build();\n\n        let get_month_code = BuiltInBuilder::callable(realm, Self::get_month_code)\n            .name(js_string!(\"get monthCode\"))\n            .build();\n\n        let get_day = BuiltInBuilder::callable(realm, Self::get_day)\n            .name(js_string!(\"get day\"))\n            .build();\n\n        let get_hour = BuiltInBuilder::callable(realm, Self::get_hour)\n            .name(js_string!(\"get hour\"))\n            .build();\n\n        let get_minute = BuiltInBuilder::callable(realm, Self::get_minute)\n            .name(js_string!(\"get minute\"))\n            .build();\n\n        let get_second = BuiltInBuilder::callable(realm, Self::get_second)\n            .name(js_string!(\"get second\"))\n            .build();\n\n        let get_millisecond = BuiltInBuilder::callable(realm, Self::get_millisecond)\n            .name(js_string!(\"get millisecond\"))\n            .build();\n\n        let get_microsecond = BuiltInBuilder::callable(realm, Self::get_microsecond)\n            .name(js_string!(\"get microsecond\"))\n            .build();\n\n        let get_nanosecond = BuiltInBuilder::callable(realm, Self::get_nanosecond)\n            .name(js_string!(\"get nanosecond\"))\n            .build();\n\n        let get_day_of_week = BuiltInBuilder::callable(realm, Self::get_day_of_week)\n            .name(js_string!(\"get dayOfWeek\"))\n            .build();\n\n        let get_day_of_year = BuiltInBuilder::callable(realm, Self::get_day_of_year)\n            .name(js_string!(\"get dayOfYear\"))\n            .build();\n\n        let get_week_of_year = BuiltInBuilder::callable(realm, Self::get_week_of_year)\n            .name(js_string!(\"get weekOfYear\"))\n            .build();\n\n        let get_year_of_week = BuiltInBuilder::callable(realm, Self::get_year_of_week)\n            .name(js_string!(\"get yearOfWeek\"))\n            .build();\n\n        let get_days_in_week = BuiltInBuilder::callable(realm, Self::get_days_in_week)\n            .name(js_string!(\"get daysInWeek\"))\n            .build();\n\n        let get_days_in_month = BuiltInBuilder::callable(realm, Self::get_days_in_month)\n            .name(js_string!(\"get daysInMonth\"))\n            .build();\n\n        let get_days_in_year = BuiltInBuilder::callable(realm, Self::get_days_in_year)\n            .name(js_string!(\"get daysInYear\"))\n            .build();\n\n        let get_months_in_year = BuiltInBuilder::callable(realm, Self::get_months_in_year)\n            .name(js_string!(\"get monthsInYear\"))\n            .build();\n\n        let get_in_leap_year = BuiltInBuilder::callable(realm, Self::get_in_leap_year)\n            .name(js_string!(\"get inLeapYear\"))\n            .build();\n\n        BuiltInBuilder::from_standard_constructor::<Self>(realm)\n            .property(\n                JsSymbol::to_string_tag(),\n                StaticJsStrings::PLAIN_DATETIME_TAG,\n                Attribute::CONFIGURABLE,\n            )\n            .accessor(\n                js_string!(\"calendarId\"),\n                Some(get_calendar_id),\n                None,\n                Attribute::CONFIGURABLE,\n            )\n            .accessor(\n                js_string!(\"era\"),\n                Some(get_era),\n                None,\n                Attribute::CONFIGURABLE,\n            )\n            .accessor(\n                js_string!(\"eraYear\"),\n                Some(get_era_year),\n                None,\n                Attribute::CONFIGURABLE,\n            )\n            .accessor(\n                js_string!(\"year\"),\n                Some(get_year),\n                None,\n                Attribute::CONFIGURABLE,\n            )\n            .accessor(\n                js_string!(\"month\"),\n                Some(get_month),\n                None,\n                Attribute::CONFIGURABLE,\n            )\n            .accessor(\n                js_string!(\"monthCode\"),\n                Some(get_month_code),\n                None,\n                Attribute::CONFIGURABLE,\n            )\n            .accessor(\n                js_string!(\"day\"),\n                Some(get_day),\n                None,\n                Attribute::CONFIGURABLE,\n            )\n            .accessor(\n                js_string!(\"hour\"),\n                Some(get_hour),\n                None,\n                Attribute::CONFIGURABLE,\n            )\n            .accessor(\n                js_string!(\"minute\"),\n                Some(get_minute),\n                None,\n                Attribute::CONFIGURABLE,\n            )\n            .accessor(\n                js_string!(\"second\"),\n                Some(get_second),\n                None,\n                Attribute::CONFIGURABLE,\n            )\n            .accessor(\n                js_string!(\"millisecond\"),\n                Some(get_millisecond),\n                None,\n                Attribute::CONFIGURABLE,\n            )\n            .accessor(\n                js_string!(\"microsecond\"),\n                Some(get_microsecond),\n                None,\n                Attribute::CONFIGURABLE,\n            )\n            .accessor(\n                js_string!(\"nanosecond\"),\n                Some(get_nanosecond),\n                None,\n                Attribute::CONFIGURABLE,\n            )\n            .accessor(\n                js_string!(\"dayOfWeek\"),\n                Some(get_day_of_week),\n                None,\n                Attribute::CONFIGURABLE,\n            )\n            .accessor(\n                js_string!(\"dayOfYear\"),\n                Some(get_day_of_year),\n                None,\n                Attribute::CONFIGURABLE,\n            )\n            .accessor(\n                js_string!(\"weekOfYear\"),\n                Some(get_week_of_year),\n                None,\n                Attribute::CONFIGURABLE,\n            )\n            .accessor(\n                js_string!(\"yearOfWeek\"),\n                Some(get_year_of_week),\n                None,\n                Attribute::CONFIGURABLE,\n            )\n            .accessor(\n                js_string!(\"daysInWeek\"),\n                Some(get_days_in_week),\n                None,\n                Attribute::CONFIGURABLE,\n            )\n            .accessor(\n                js_string!(\"daysInMonth\"),\n                Some(get_days_in_month),\n                None,\n                Attribute::CONFIGURABLE,\n            )\n            .accessor(\n                js_string!(\"daysInYear\"),\n                Some(get_days_in_year),\n                None,\n                Attribute::CONFIGURABLE,\n            )\n            .accessor(\n                js_string!(\"monthsInYear\"),\n                Some(get_months_in_year),\n                None,\n                Attribute::CONFIGURABLE,\n            )\n            .accessor(\n                js_string!(\"inLeapYear\"),\n                Some(get_in_leap_year),\n                None,\n                Attribute::CONFIGURABLE,\n            )\n            .static_method(Self::from, js_string!(\"from\"), 1)\n            .static_method(Self::compare, js_string!(\"compare\"), 2)\n            .method(Self::with, js_string!(\"with\"), 1)\n            .method(Self::with_plain_time, js_string!(\"withPlainTime\"), 0)\n            .method(Self::with_calendar, js_string!(\"withCalendar\"), 1)\n            .method(Self::add, js_string!(\"add\"), 1)\n            .method(Self::subtract, js_string!(\"subtract\"), 1)\n            .method(Self::until, js_string!(\"until\"), 1)\n            .method(Self::since, js_string!(\"since\"), 1)\n            .method(Self::round, js_string!(\"round\"), 1)\n            .method(Self::equals, js_string!(\"equals\"), 1)\n            .method(Self::to_string, js_string!(\"toString\"), 0)\n            .method(Self::to_locale_string, js_string!(\"toLocaleString\"), 0)\n            .method(Self::to_json, js_string!(\"toJSON\"), 0)\n            .method(Self::value_of, js_string!(\"valueOf\"), 0)\n            .method(Self::to_zoned_date_time, js_string!(\"toZonedDateTime\"), 1)\n            .method(Self::to_plain_date, js_string!(\"toPlainDate\"), 0)\n            .method(Self::to_plain_time, js_string!(\"toPlainTime\"), 0)\n            .build();\n    }\n\n    fn get(intrinsics: &Intrinsics) -> JsObject {\n        Self::STANDARD_CONSTRUCTOR(intrinsics.constructors()).constructor()\n    }\n}\n\nimpl BuiltInConstructor for PlainDateTime {\n    const CONSTRUCTOR_ARGUMENTS: usize = 3;\n    const PROTOTYPE_STORAGE_SLOTS: usize = 61;\n    const CONSTRUCTOR_STORAGE_SLOTS: usize = 2;\n\n    const STANDARD_CONSTRUCTOR: fn(&StandardConstructors) -> &StandardConstructor =\n        StandardConstructors::plain_date_time;\n\n    fn constructor(\n        new_target: &JsValue,\n        args: &[JsValue],\n        context: &mut Context,\n    ) -> JsResult<JsValue> {\n        // 1. If NewTarget is undefined, then\n        if new_target.is_undefined() {\n            // a. Throw a TypeError exception.\n            return Err(JsNativeError::typ()\n                .with_message(\"NewTarget cannot be undefined when constructing PlainDatedt.\")\n                .into());\n        }\n\n        // 2. Set isoYear to ? ToIntegerWithTruncation(isoYear).\n        let iso_year = args\n            .get_or_undefined(0)\n            .to_finitef64(context)?\n            .as_integer_with_truncation::<i32>();\n        // 3. Set isoMonth to ? ToIntegerWithTruncation(isoMonth).\n        let iso_month = args\n            .get_or_undefined(1)\n            .to_finitef64(context)?\n            .as_integer_with_truncation::<u8>();\n        // 4. Set isoDay to ? ToIntegerWithTruncation(isoDay).\n        let iso_day = args\n            .get_or_undefined(2)\n            .to_finitef64(context)?\n            .as_integer_with_truncation::<u8>();\n        // 5. If hour is undefined, set hour to 0; else set hour to ? ToIntegerWithTruncation(hour).\n        let hour = args.get_or_undefined(3).map_or(Ok::<u8, JsError>(0), |v| {\n            let finite = v.to_finitef64(context)?;\n            let int = finite.as_integer_with_truncation::<i8>();\n            if int < 0 {\n                return Err(JsNativeError::range()\n                    .with_message(\"invalid time field\")\n                    .into());\n            }\n            Ok(int as u8)\n        })?;\n        // 6. If minute is undefined, set minute to 0; else set minute to ? ToIntegerWithTruncation(minute).\n        let minute = args.get_or_undefined(4).map_or(Ok::<u8, JsError>(0), |v| {\n            let finite = v.to_finitef64(context)?;\n            let int = finite.as_integer_with_truncation::<i8>();\n            if int < 0 {\n                return Err(JsNativeError::range()\n                    .with_message(\"invalid time field\")\n                    .into());\n            }\n            Ok(int as u8)\n        })?;\n\n        // 7. If second is undefined, set second to 0; else set second to ? ToIntegerWithTruncation(second).\n        let second = args.get_or_undefined(5).map_or(Ok::<u8, JsError>(0), |v| {\n            let finite = v.to_finitef64(context)?;\n            let int = finite.as_integer_with_truncation::<i8>();\n            if int < 0 {\n                return Err(JsNativeError::range()\n                    .with_message(\"invalid time field\")\n                    .into());\n            }\n            Ok(int as u8)\n        })?;\n\n        // 8. If millisecond is undefined, set millisecond to 0; else set millisecond to ? ToIntegerWithTruncation(millisecond).\n        let millisecond = args\n            .get_or_undefined(6)\n            .map_or(Ok::<u16, JsError>(0), |v| {\n                let finite = v.to_finitef64(context)?;\n                let int = finite.as_integer_with_truncation::<i16>();\n                if int < 0 {\n                    return Err(JsNativeError::range()\n                        .with_message(\"invalid time field\")\n                        .into());\n                }\n                Ok(int as u16)\n            })?;\n\n        // 9. If microsecond is undefined, set microsecond to 0; else set microsecond to ? ToIntegerWithTruncation(microsecond).\n        let microsecond = args\n            .get_or_undefined(7)\n            .map_or(Ok::<u16, JsError>(0), |v| {\n                let finite = v.to_finitef64(context)?;\n                let int = finite.as_integer_with_truncation::<i16>();\n                if int < 0 {\n                    return Err(JsNativeError::range()\n                        .with_message(\"invalid time field\")\n                        .into());\n                }\n                Ok(int as u16)\n            })?;\n\n        // 10. If nanosecond is undefined, set nanosecond to 0; else set nanosecond to ? ToIntegerWithTruncation(nanosecond).\n        let nanosecond = args\n            .get_or_undefined(8)\n            .map_or(Ok::<u16, JsError>(0), |v| {\n                let finite = v.to_finitef64(context)?;\n                let int = finite.as_integer_with_truncation::<i16>();\n                if int < 0 {\n                    return Err(JsNativeError::range()\n                        .with_message(\"invalid time field\")\n                        .into());\n                }\n                Ok(int as u16)\n            })?;\n\n        let calendar_slot = args\n            .get_or_undefined(9)\n            .map(|s| {\n                s.as_string()\n                    .as_ref()\n                    .map(JsString::to_std_string_lossy)\n                    .ok_or_else(|| JsNativeError::typ().with_message(\"calendar must be a string.\"))\n            })\n            .transpose()?\n            .map(|s| Calendar::try_from_utf8(s.as_bytes()))\n            .transpose()?\n            .unwrap_or_default();\n\n        let dt = InnerDateTime::try_new(\n            iso_year,\n            iso_month,\n            iso_day,\n            hour,\n            minute,\n            second,\n            millisecond,\n            microsecond,\n            nanosecond,\n            calendar_slot,\n        )?;\n\n        // 12. Return ? CreateTemporalDateTime(isoYear, isoMonth, isoDay, hour, minute, second, millisecond, microsecond, nanosecond, calendar, NewTarget).\n        create_temporal_datetime(dt, Some(new_target), context).map(Into::into)\n    }\n}\n\n// ==== `PlainDateTimeTime` accessor methods implementation ====\n\nimpl PlainDateTime {\n    /// 5.3.3 get `Temporal.PlainDateTime.prototype.calendarId`\n    ///\n    /// More information:\n    ///\n    /// - [ECMAScript Temporal proposal][spec]\n    /// - [MDN reference][mdn]\n    /// - [`temporal_rs` documentation][temporal_rs-docs]\n    ///\n    /// [spec]: https://tc39.es/proposal-temporal/#sec-get-temporal.plaindatetime.prototype.calendarid\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Temporal/PlainDateTime/calendarId\n    /// [temporal_rs-docs]: https://docs.rs/temporal_rs/latest/temporal_rs/struct.PlainDateTime.html#method.calendar\n    fn get_calendar_id(this: &JsValue, _: &[JsValue], _: &mut Context) -> JsResult<JsValue> {\n        let object = this.as_object();\n        let dt = object\n            .as_ref()\n            .and_then(JsObject::downcast_ref::<Self>)\n            .ok_or_else(|| {\n                JsNativeError::typ().with_message(\"the this object must be a PlainDateTime object.\")\n            })?;\n\n        Ok(JsString::from(dt.inner.calendar().identifier()).into())\n    }\n\n    /// 5.3.4 get `Temporal.PlainDateTime.prototype.era`\n    ///\n    /// More information:\n    ///\n    /// - [ECMAScript Temporal proposal][spec]\n    /// - [MDN reference][mdn]\n    /// - [`temporal_rs` documentation][temporal_rs-docs]\n    ///\n    /// [spec]: https://tc39.es/proposal-temporal/#sec-get-temporal.plaindatetime.prototype.era\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Temporal/PlainDateTime/era\n    /// [temporal_rs-docs]: https://docs.rs/temporal_rs/latest/temporal_rs/struct.PlainDateTime.html#method.era\n    fn get_era(this: &JsValue, _: &[JsValue], _: &mut Context) -> JsResult<JsValue> {\n        let object = this.as_object();\n        let dt = object\n            .as_ref()\n            .and_then(JsObject::downcast_ref::<Self>)\n            .ok_or_else(|| {\n                JsNativeError::typ().with_message(\"the this object must be a PlainDateTime object.\")\n            })?;\n\n        Ok(dt\n            .inner\n            .era()\n            .map(|s| JsString::from(s.as_str()))\n            .into_or_undefined())\n    }\n\n    /// 5.3.5 get `Temporal.PlainDateTime.prototype.eraYear`\n    ///\n    /// More information:\n    ///\n    /// - [ECMAScript Temporal proposal][spec]\n    /// - [MDN reference][mdn]\n    /// - [`temporal_rs` documentation][temporal_rs-docs]\n    ///\n    /// [spec]: https://tc39.es/proposal-temporal/#sec-get-temporal.plaindatetime.prototype.erayear\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Temporal/PlainDateTime/eraYear\n    /// [temporal_rs-docs]: https://docs.rs/temporal_rs/latest/temporal_rs/struct.PlainDateTime.html#method.era_year\n    fn get_era_year(this: &JsValue, _: &[JsValue], _: &mut Context) -> JsResult<JsValue> {\n        let object = this.as_object();\n        let dt = object\n            .as_ref()\n            .and_then(JsObject::downcast_ref::<Self>)\n            .ok_or_else(|| {\n                JsNativeError::typ().with_message(\"the this object must be a PlainDateTime object.\")\n            })?;\n\n        Ok(dt.inner.era_year().into_or_undefined())\n    }\n\n    /// 5.3.6 get `Temporal.PlainDateTime.prototype.year`\n    ///\n    /// More information:\n    ///\n    /// - [ECMAScript Temporal proposal][spec]\n    /// - [MDN reference][mdn]\n    /// - [`temporal_rs` documentation][temporal_rs-docs]\n    ///\n    /// [spec]: https://tc39.es/proposal-temporal/#sec-get-temporal.plaindatetime.prototype.year\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Temporal/PlainDateTime/year\n    /// [temporal_rs-docs]: https://docs.rs/temporal_rs/latest/temporal_rs/struct.PlainDateTime.html#method.year\n    fn get_year(this: &JsValue, _: &[JsValue], _: &mut Context) -> JsResult<JsValue> {\n        let object = this.as_object();\n        let dt = object\n            .as_ref()\n            .and_then(JsObject::downcast_ref::<Self>)\n            .ok_or_else(|| {\n                JsNativeError::typ().with_message(\"the this object must be a PlainDateTime object.\")\n            })?;\n\n        Ok(dt.inner.year().into())\n    }\n\n    /// 5.3.7 get `Temporal.PlainDateTime.prototype.month`\n    ///\n    /// More information:\n    ///\n    /// - [ECMAScript Temporal proposal][spec]\n    /// - [MDN reference][mdn]\n    /// - [`temporal_rs` documentation][temporal_rs-docs]\n    ///\n    /// [spec]: https://tc39.es/proposal-temporal/#sec-get-temporal.plaindatetime.prototype.month\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Temporal/PlainDateTime/month\n    /// [temporal_rs-docs]: https://docs.rs/temporal_rs/latest/temporal_rs/struct.PlainDateTime.html\n    fn get_month(this: &JsValue, _: &[JsValue], _: &mut Context) -> JsResult<JsValue> {\n        let object = this.as_object();\n        let dt = object\n            .as_ref()\n            .and_then(JsObject::downcast_ref::<Self>)\n            .ok_or_else(|| {\n                JsNativeError::typ().with_message(\"the this object must be a PlainDateTime object.\")\n            })?;\n\n        Ok(dt.inner.month().into())\n    }\n\n    /// 5.3.8 get `Temporal.PlainDateTime.prototype.monthCode`\n    ///\n    /// More information:\n    ///\n    /// - [ECMAScript Temporal proposal][spec]\n    /// - [MDN reference][mdn]\n    /// - [`temporal_rs` documentation][temporal_rs-docs]\n    ///\n    /// [spec]: https://tc39.es/proposal-temporal/#sec-get-temporal.plaindatetime.prototype.monthcode\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Temporal/PlainDateTime/monthCode\n    /// [temporal_rs-docs]: https://docs.rs/temporal_rs/latest/temporal_rs/struct.PlainDateTime.html#method.month_code\n    fn get_month_code(this: &JsValue, _: &[JsValue], _: &mut Context) -> JsResult<JsValue> {\n        let object = this.as_object();\n        let dt = object\n            .as_ref()\n            .and_then(JsObject::downcast_ref::<Self>)\n            .ok_or_else(|| {\n                JsNativeError::typ().with_message(\"the this object must be a PlainDateTime object.\")\n            })?;\n\n        Ok(JsString::from(dt.inner.month_code().as_str()).into())\n    }\n\n    /// 5.3.9 get `Temporal.PlainDateTime.prototype.day`\n    ///\n    /// More information:\n    ///\n    /// - [ECMAScript Temporal proposal][spec]\n    /// - [MDN reference][mdn]\n    /// - [`temporal_rs` documentation][temporal_rs-docs]\n    ///\n    /// [spec]: https://tc39.es/proposal-temporal/#sec-get-temporal.plaindatetime.prototype.day\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Temporal/PlainDateTime/day\n    /// [temporal_rs-docs]: https://docs.rs/temporal_rs/latest/temporal_rs/struct.PlainDateTime.html#method.day\n    fn get_day(this: &JsValue, _: &[JsValue], _: &mut Context) -> JsResult<JsValue> {\n        let object = this.as_object();\n        let dt = object\n            .as_ref()\n            .and_then(JsObject::downcast_ref::<Self>)\n            .ok_or_else(|| {\n                JsNativeError::typ().with_message(\"the this object must be a PlainDateTime object.\")\n            })?;\n\n        Ok(dt.inner.day().into())\n    }\n\n    /// 5.3.10 get `Temporal.PlainDateTime.prototype.hour`\n    ///\n    /// More information:\n    ///\n    /// - [ECMAScript Temporal proposal][spec]\n    /// - [MDN reference][mdn]\n    /// - [`temporal_rs` documentation][temporal_rs-docs]\n    ///\n    /// [spec]: https://tc39.es/proposal-temporal/#sec-get-temporal.plaindatetime.prototype.hour\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Temporal/PlainDateTime/hour\n    /// [temporal_rs-docs]: https://docs.rs/temporal_rs/latest/temporal_rs/struct.PlainDateTime.html#method.hour\n    fn get_hour(this: &JsValue, _: &[JsValue], _: &mut Context) -> JsResult<JsValue> {\n        // 1. Let dateTime be the this value.\n        // 2. Perform ? RequireInternalSlot(dateTime, [[InitializedTemporalDateTime]]).\n        let object = this.as_object();\n        let dt = object\n            .as_ref()\n            .and_then(JsObject::downcast_ref::<Self>)\n            .ok_or_else(|| {\n                JsNativeError::typ().with_message(\"the this object must be a PlainDateTime object.\")\n            })?;\n\n        // 3. Return 𝔽(datedt.[[ISOHour]]).\n        Ok(dt.inner.hour().into())\n    }\n\n    /// 5.3.11 get `Temporal.PlainDateTime.prototype.minute`\n    ///\n    /// More information:\n    ///\n    /// - [ECMAScript Temporal proposal][spec]\n    /// - [MDN reference][mdn]\n    /// - [`temporal_rs` documentation][temporal_rs-docs]\n    ///\n    /// [spec]: https://tc39.es/proposal-temporal/#sec-get-temporal.plaindatetime.prototype.minute\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Temporal/PlainDateTime/minute\n    /// [temporal_rs-docs]: https://docs.rs/temporal_rs/latest/temporal_rs/struct.PlainDateTime.html#method.minute\n    fn get_minute(this: &JsValue, _: &[JsValue], _: &mut Context) -> JsResult<JsValue> {\n        // 1. Let dateTime be the this value.\n        // 2. Perform ? RequireInternalSlot(dateTime, [[InitializedTemporalDateTime]]).\n        let object = this.as_object();\n        let dt = object\n            .as_ref()\n            .and_then(JsObject::downcast_ref::<Self>)\n            .ok_or_else(|| {\n                JsNativeError::typ().with_message(\"the this object must be a PlainDateTime object.\")\n            })?;\n\n        // 3. Return 𝔽(datedt.[[ISOMinute]]).\n        Ok(dt.inner.minute().into())\n    }\n\n    /// 5.3.12 get `Temporal.PlainDateTime.prototype.second`\n    ///\n    /// More information:\n    ///\n    /// - [ECMAScript Temporal proposal][spec]\n    /// - [MDN reference][mdn]\n    /// - [`temporal_rs` documentation][temporal_rs-docs]\n    ///\n    /// [spec]: https://tc39.es/proposal-temporal/#sec-get-temporal.plaindatetime.prototype.second\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Temporal/PlainDateTime/second\n    /// [temporal_rs-docs]: https://docs.rs/temporal_rs/latest/temporal_rs/struct.PlainDateTime.html#method.second\n    fn get_second(this: &JsValue, _: &[JsValue], _: &mut Context) -> JsResult<JsValue> {\n        // 1. Let dateTime be the this value.\n        // 2. Perform ? RequireInternalSlot(dateTime, [[InitializedTemporalDateTime]]).\n        let object = this.as_object();\n        let dt = object\n            .as_ref()\n            .and_then(JsObject::downcast_ref::<Self>)\n            .ok_or_else(|| {\n                JsNativeError::typ().with_message(\"the this object must be a PlainDateTime object.\")\n            })?;\n\n        // 3. Return 𝔽(datedt.[[ISOSecond]]).\n        Ok(dt.inner.second().into())\n    }\n\n    /// 5.3.13 get `Temporal.PlainDateTime.prototype.millisecond`\n    ///\n    /// More information:\n    ///\n    /// - [ECMAScript Temporal proposal][spec]\n    /// - [MDN reference][mdn]\n    /// - [`temporal_rs` documentation][temporal_rs-docs]\n    ///\n    /// [spec]: https://tc39.es/proposal-temporal/#sec-get-temporal.plaindatetime.prototype.millisecond\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Temporal/PlainDateTime/millisecond\n    /// [temporal_rs-docs]: https://docs.rs/temporal_rs/latest/temporal_rs/struct.PlainDateTime.html#method.millisecond\n    fn get_millisecond(this: &JsValue, _: &[JsValue], _: &mut Context) -> JsResult<JsValue> {\n        // 1. Let dateTime be the this value.\n        // 2. Perform ? RequireInternalSlot(dateTime, [[InitializedTemporalDateTime]]).\n        let object = this.as_object();\n        let dt = object\n            .as_ref()\n            .and_then(JsObject::downcast_ref::<Self>)\n            .ok_or_else(|| {\n                JsNativeError::typ().with_message(\"the this object must be a PlainDateTime object.\")\n            })?;\n\n        // 3. Return 𝔽(datedt.[[ISOMillisecond]]).\n        Ok(dt.inner.millisecond().into())\n    }\n\n    /// 5.3.14 get `Temporal.PlainDateTime.prototype.microsecond`\n    ///\n    /// More information:\n    ///\n    /// - [ECMAScript Temporal proposal][spec]\n    /// - [MDN reference][mdn]\n    /// - [`temporal_rs` documentation][temporal_rs-docs]\n    ///\n    /// [spec]: https://tc39.es/proposal-temporal/#sec-get-temporal.plaindatetime.prototype.microsecond\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Temporal/PlainDateTime/microsecond\n    /// [temporal_rs-docs]: https://docs.rs/temporal_rs/latest/temporal_rs/struct.PlainDateTime.html#method.microsecond\n    fn get_microsecond(this: &JsValue, _: &[JsValue], _: &mut Context) -> JsResult<JsValue> {\n        // 1. Let dateTime be the this value.\n        // 2. Perform ? RequireInternalSlot(dateTime, [[InitializedTemporalDateTime]]).\n        let object = this.as_object();\n        let dt = object\n            .as_ref()\n            .and_then(JsObject::downcast_ref::<Self>)\n            .ok_or_else(|| {\n                JsNativeError::typ().with_message(\"the this object must be a PlainDateTime object.\")\n            })?;\n\n        // 3. Return 𝔽(datedt.[[ISOMicrosecond]]).\n        Ok(dt.inner.microsecond().into())\n    }\n\n    /// 5.3.15 get `Temporal.PlainDateTime.prototype.nanosecond`\n    ///\n    /// More information:\n    ///\n    /// - [ECMAScript Temporal proposal][spec]\n    /// - [MDN reference][mdn]\n    /// - [`temporal_rs` documentation][temporal_rs-docs]\n    ///\n    /// [spec]: https://tc39.es/proposal-temporal/#sec-get-temporal.plaindatetime.prototype.nanosecond\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Temporal/PlainDateTime/nanosecond\n    /// [temporal_rs-docs]: https://docs.rs/temporal_rs/latest/temporal_rs/struct.PlainDateTime.html#method.nanosecond\n    fn get_nanosecond(this: &JsValue, _: &[JsValue], _: &mut Context) -> JsResult<JsValue> {\n        // 1. Let dateTime be the this value.\n        // 2. Perform ? RequireInternalSlot(dateTime, [[InitializedTemporalDateTime]]).\n        let object = this.as_object();\n        let dt = object\n            .as_ref()\n            .and_then(JsObject::downcast_ref::<Self>)\n            .ok_or_else(|| {\n                JsNativeError::typ().with_message(\"the this object must be a PlainDateTime object.\")\n            })?;\n\n        // 3. Return 𝔽(datedt.[[ISONanosecond]]).\n        Ok(dt.inner.nanosecond().into())\n    }\n\n    /// 5.3.16 get `Temporal.PlainDateTime.prototype.dayOfWeek`\n    ///\n    /// More information:\n    ///\n    /// - [ECMAScript Temporal proposal][spec]\n    /// - [MDN reference][mdn]\n    /// - [`temporal_rs` documentation][temporal_rs-docs]\n    ///\n    /// [spec]: https://tc39.es/proposal-temporal/#sec-get-temporal.plaindatetime.prototype.dayofweek\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Temporal/PlainDateTime/dayOfWeek\n    /// [temporal_rs-docs]: https://docs.rs/temporal_rs/latest/temporal_rs/struct.PlainDateTime.html#method.day_of_week\n    fn get_day_of_week(this: &JsValue, _: &[JsValue], _: &mut Context) -> JsResult<JsValue> {\n        let object = this.as_object();\n        let dt = object\n            .as_ref()\n            .and_then(JsObject::downcast_ref::<Self>)\n            .ok_or_else(|| {\n                JsNativeError::typ().with_message(\"the this object must be a PlainDateTime object.\")\n            })?;\n\n        Ok(dt.inner.day_of_week().into())\n    }\n\n    /// 5.3.17 get `Temporal.PlainDateTime.prototype.dayOfYear`\n    ///\n    /// More information:\n    ///\n    /// - [ECMAScript Temporal proposal][spec]\n    /// - [MDN reference][mdn]\n    /// - [`temporal_rs` documentation][temporal_rs-docs]\n    ///\n    /// [spec]: https://tc39.es/proposal-temporal/#sec-get-temporal.plaindatetime.prototype.dayofyear\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Temporal/PlainDateTime/dayOfYear\n    /// [temporal_rs-docs]: https://docs.rs/temporal_rs/latest/temporal_rs/struct.PlainDateTime.html#method.day_of_year\n    fn get_day_of_year(this: &JsValue, _: &[JsValue], _: &mut Context) -> JsResult<JsValue> {\n        let object = this.as_object();\n        let dt = object\n            .as_ref()\n            .and_then(JsObject::downcast_ref::<Self>)\n            .ok_or_else(|| {\n                JsNativeError::typ().with_message(\"the this object must be a PlainDateTime object.\")\n            })?;\n\n        Ok(dt.inner.day_of_year().into())\n    }\n\n    /// 5.3.18 get `Temporal.PlainDateTime.prototype.weekOfYear`\n    ///\n    /// More information:\n    ///\n    /// - [ECMAScript Temporal proposal][spec]\n    /// - [MDN reference][mdn]\n    /// - [`temporal_rs` documentation][temporal_rs-docs]\n    ///\n    /// [spec]: https://tc39.es/proposal-temporal/#sec-get-temporal.plaindatetime.prototype.weekofyear\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Temporal/PlainDateTime/weekOfYear\n    /// [temporal_rs-docs]: https://docs.rs/temporal_rs/latest/temporal_rs/struct.PlainDateTime.html#method.week_of_year\n    fn get_week_of_year(this: &JsValue, _: &[JsValue], _: &mut Context) -> JsResult<JsValue> {\n        let object = this.as_object();\n        let dt = object\n            .as_ref()\n            .and_then(JsObject::downcast_ref::<Self>)\n            .ok_or_else(|| {\n                JsNativeError::typ().with_message(\"the this object must be a PlainDateTime object.\")\n            })?;\n\n        Ok(dt.inner.week_of_year().into_or_undefined())\n    }\n\n    /// 5.3.19 get `Temporal.PlainDateTime.prototype.yearOfWeek`\n    ///\n    /// More information:\n    ///\n    /// - [ECMAScript Temporal proposal][spec]\n    /// - [MDN reference][mdn]\n    /// - [`temporal_rs` documentation][temporal_rs-docs]\n    ///\n    /// [spec]: https://tc39.es/proposal-temporal/#sec-get-temporal.plaindatetime.prototype.yearofweek\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Temporal/PlainDateTime/yearOfWeek\n    /// [temporal_rs-docs]: https://docs.rs/temporal_rs/latest/temporal_rs/struct.PlainDateTime.html#method.year_of_week\n    fn get_year_of_week(this: &JsValue, _: &[JsValue], _: &mut Context) -> JsResult<JsValue> {\n        let object = this.as_object();\n        let dt = object\n            .as_ref()\n            .and_then(JsObject::downcast_ref::<Self>)\n            .ok_or_else(|| {\n                JsNativeError::typ().with_message(\"the this object must be a PlainDateTime object.\")\n            })?;\n\n        Ok(dt.inner.year_of_week().into_or_undefined())\n    }\n\n    /// 5.3.20 get `Temporal.PlainDateTime.prototype.daysInWeek`\n    ///\n    /// More information:\n    ///\n    /// - [ECMAScript Temporal proposal][spec]\n    /// - [MDN reference][mdn]\n    /// - [`temporal_rs` documentation][temporal_rs-docs]\n    ///\n    /// [spec]: https://tc39.es/proposal-temporal/#sec-get-temporal.plaindatetime.prototype.daysinweek\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Temporal/PlainDateTime/daysInWeek\n    /// [temporal_rs-docs]: https://docs.rs/temporal_rs/latest/temporal_rs/struct.PlainDateTime.html#method.days_in_week\n    fn get_days_in_week(this: &JsValue, _: &[JsValue], _: &mut Context) -> JsResult<JsValue> {\n        let object = this.as_object();\n        let dt = object\n            .as_ref()\n            .and_then(JsObject::downcast_ref::<Self>)\n            .ok_or_else(|| {\n                JsNativeError::typ().with_message(\"the this object must be a PlainDateTime object.\")\n            })?;\n\n        Ok(dt.inner.days_in_week().into())\n    }\n\n    /// 5.3.21 get `Temporal.PlainDateTime.prototype.daysInMonth`\n    ///\n    /// More information:\n    ///\n    /// - [ECMAScript Temporal proposal][spec]\n    /// - [MDN reference][mdn]\n    /// - [`temporal_rs` documentation][temporal_rs-docs]\n    ///\n    /// [spec]: https://tc39.es/proposal-temporal/#sec-get-temporal.plaindatetime.prototype.daysinmonth\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Temporal/PlainDateTime/daysInMonth\n    /// [temporal_rs-docs]: https://docs.rs/temporal_rs/latest/temporal_rs/struct.PlainDateTime.html#method.days_in_month\n    fn get_days_in_month(this: &JsValue, _: &[JsValue], _: &mut Context) -> JsResult<JsValue> {\n        let object = this.as_object();\n        let dt = object\n            .as_ref()\n            .and_then(JsObject::downcast_ref::<Self>)\n            .ok_or_else(|| {\n                JsNativeError::typ().with_message(\"the this object must be a PlainDateTime object.\")\n            })?;\n\n        Ok(dt.inner.days_in_month().into())\n    }\n\n    /// 5.3.22 get `Temporal.PlainDateTime.prototype.daysInYear`\n    ///\n    /// More information:\n    ///\n    /// - [ECMAScript Temporal proposal][spec]\n    /// - [MDN reference][mdn]\n    /// - [`temporal_rs` documentation][temporal_rs-docs]\n    ///\n    /// [spec]: https://tc39.es/proposal-temporal/#sec-get-temporal.plaindatetime.prototype.daysinyear\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Temporal/PlainDateTime/daysInYear\n    /// [temporal_rs-docs]: https://docs.rs/temporal_rs/latest/temporal_rs/struct.PlainDateTime.html#method.days_in_year\n    fn get_days_in_year(this: &JsValue, _: &[JsValue], _: &mut Context) -> JsResult<JsValue> {\n        let object = this.as_object();\n        let dt = object\n            .as_ref()\n            .and_then(JsObject::downcast_ref::<Self>)\n            .ok_or_else(|| {\n                JsNativeError::typ().with_message(\"the this object must be a PlainDateTime object.\")\n            })?;\n\n        Ok(dt.inner.days_in_year().into())\n    }\n\n    /// 5.3.23 get `Temporal.PlainDateTime.prototype.monthsInYear`\n    ///\n    /// More information:\n    ///\n    /// - [ECMAScript Temporal proposal][spec]\n    /// - [MDN reference][mdn]\n    /// - [`temporal_rs` documentation][temporal_rs-docs]\n    ///\n    /// [spec]: https://tc39.es/proposal-temporal/#sec-get-temporal.plaindatetime.prototype.monthsinyear\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Temporal/PlainDateTime/monthsInYear\n    /// [temporal_rs-docs]: https://docs.rs/temporal_rs/latest/temporal_rs/struct.PlainDateTime.html#method.months_in_year\n    fn get_months_in_year(this: &JsValue, _: &[JsValue], _: &mut Context) -> JsResult<JsValue> {\n        let object = this.as_object();\n        let dt = object\n            .as_ref()\n            .and_then(JsObject::downcast_ref::<Self>)\n            .ok_or_else(|| {\n                JsNativeError::typ().with_message(\"the this object must be a PlainDateTime object.\")\n            })?;\n\n        Ok(dt.inner.months_in_year().into())\n    }\n\n    /// 5.3.24 get `Temporal.PlainDateTime.prototype.inLeapYear`\n    ///\n    /// More information:\n    ///\n    /// - [ECMAScript Temporal proposal][spec]\n    /// - [MDN reference][mdn]\n    /// - [`temporal_rs` documentation][temporal_rs-docs]\n    ///\n    /// [spec]: https://tc39.es/proposal-temporal/#sec-get-temporal.plaindatetime.prototype.inleapyear\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Temporal/PlainDateTime/inLeapYear\n    /// [temporal_rs-docs]: https://docs.rs/temporal_rs/latest/temporal_rs/struct.PlainDateTime.html#method.in_leap_year\n    fn get_in_leap_year(this: &JsValue, _: &[JsValue], _: &mut Context) -> JsResult<JsValue> {\n        let object = this.as_object();\n        let dt = object\n            .as_ref()\n            .and_then(JsObject::downcast_ref::<Self>)\n            .ok_or_else(|| {\n                JsNativeError::typ().with_message(\"the this object must be a PlainDateTime object.\")\n            })?;\n\n        Ok(dt.inner.in_leap_year().into())\n    }\n}\n\n// ==== PlainDateTime static methods implementation ====\n\nimpl PlainDateTime {\n    /// 5.2.2 `Temporal.PlainDateTime.from ( item [ , options ] )`\n    ///\n    /// More information:\n    ///\n    /// - [ECMAScript Temporal proposal][spec]\n    /// - [MDN reference][mdn]\n    ///\n    /// [spec]: https://tc39.es/proposal-temporal/#sec-temporal.plaindatetime.from\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Temporal/PlainDateTime/from\n    fn from(_: &JsValue, args: &[JsValue], context: &mut Context) -> JsResult<JsValue> {\n        let item = args.get_or_undefined(0);\n        // 1. Set options to ? GetOptionsObject(options).\n        let options = args.get(1);\n        // 2. If item is an Object and item has an [[InitializedTemporalDateTime]] internal slot, then\n        let object = item.as_object();\n        let dt = if let Some(pdt) = object.as_ref().and_then(JsObject::downcast_ref::<Self>) {\n            // a. Perform ? GetTemporalOverflowOption(options).\n            let options = get_options_object(args.get_or_undefined(1))?;\n            let _ = get_option::<Overflow>(&options, js_string!(\"overflow\"), context)?;\n            // b. Return ! CreateTemporalDateTime(item.[[ISOYear]], item.[[ISOMonth]],\n            // item.[[ISODay]], item.[[ISOHour]], item.[[ISOMinute]], item.[[ISOSecond]],\n            // item.[[ISOMillisecond]], item.[[ISOMicrosecond]], item.[[ISONanosecond]],\n            // item.[[Calendar]]).\n            pdt.inner.clone()\n        } else {\n            to_temporal_datetime(item, options.cloned(), context)?\n        };\n\n        // 3. Return ? ToTemporalDateTime(item, options).\n        create_temporal_datetime(dt, None, context).map(Into::into)\n    }\n\n    /// 5.2.3 `Temporal.PlainDateTime.compare ( one, two )`\n    ///\n    /// More information:\n    ///\n    /// - [ECMAScript Temporal proposal][spec]\n    /// - [MDN reference][mdn]\n    /// - [`temporal_rs` documentation][temporal_rs-docs]\n    ///\n    /// [spec]: https://tc39.es/proposal-temporal/#sec-temporal.plaindatetime.compare\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Temporal/PlainDateTime/compare\n    /// [temporal_rs-docs]: https://docs.rs/temporal_rs/latest/temporal_rs/struct.PlainDateTime.html#method.compare_iso\n    fn compare(_: &JsValue, args: &[JsValue], context: &mut Context) -> JsResult<JsValue> {\n        // 1. Set one to ? ToTemporalDateTime(one).\n        let one = to_temporal_datetime(args.get_or_undefined(0), None, context)?;\n        // 2. Set two to ? ToTemporalDateTime(two).\n        let two = to_temporal_datetime(args.get_or_undefined(1), None, context)?;\n\n        // 3. Return 𝔽(CompareISODateTime(one.[[ISOYear]], one.[[ISOMonth]], one.[[ISODay]],\n        // one.[[ISOHour]], one.[[ISOMinute]], one.[[ISOSecond]], one.[[ISOMillisecond]],\n        // one.[[ISOMicrosecond]], one.[[ISONanosecond]], two.[[ISOYear]], two.[[ISOMonth]],\n        // two.[[ISODay]], two.[[ISOHour]], two.[[ISOMinute]], two.[[ISOSecond]],\n        // two.[[ISOMillisecond]], two.[[ISOMicrosecond]], two.[[ISONanosecond]])).\n        Ok((one.compare_iso(&two) as i8).into())\n    }\n}\n\n// ==== PlainDateTime methods implementation ====\n\nimpl PlainDateTime {\n    ///  5.3.25 `Temporal.PlainDateTime.prototype.with ( temporalDateTimeLike [ , options ] )`\n    ///\n    /// More information:\n    ///\n    /// - [ECMAScript Temporal proposal][spec]\n    /// - [MDN reference][mdn]\n    /// - [`temporal_rs` documentation][temporal_rs-docs]\n    ///\n    /// [spec]: https://tc39.es/proposal-temporal/#sec-temporal.plaindatetime.prototype.with\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Temporal/PlainDateTime/with\n    /// [temporal_rs-docs]: https://docs.rs/temporal_rs/latest/temporal_rs/struct.PlainDateTime.html#method.with\n    fn with(this: &JsValue, args: &[JsValue], context: &mut Context) -> JsResult<JsValue> {\n        // 1. Let plainDateTime be the this value.\n        // 2. Perform ? RequireInternalSlot(plainDateTime, [[InitializedTemporalDateTime]]).\n        let object = this.as_object();\n        let dt = object\n            .as_ref()\n            .and_then(JsObject::downcast_ref::<Self>)\n            .ok_or_else(|| {\n                JsNativeError::typ().with_message(\"the this object must be a PlainDateTime object.\")\n            })?;\n\n        // 3. If ? IsPartialTemporalObject(temporalDateTimeLike) is false, throw a TypeError exception.\n        let Some(partial_object) =\n            super::is_partial_temporal_object(args.get_or_undefined(0), context)?\n        else {\n            return Err(JsNativeError::typ()\n                .with_message(\"with object was not a PartialTemporalObject.\")\n                .into());\n        };\n        // 4. Let calendar be plainDateTime.[[Calendar]].\n        // 5. Let fields be ISODateToFields(calendar, plainDateTime.[[ISODateTime]].[[ISODate]], date).\n        // 6. Set fields.[[Hour]] to plainDateTime.[[ISODateTime]].[[Time]].[[Hour]].\n        // 7. Set fields.[[Minute]] to plainDateTime.[[ISODateTime]].[[Time]].[[Minute]].\n        // 8. Set fields.[[Second]] to plainDateTime.[[ISODateTime]].[[Time]].[[Second]].\n        // 9. Set fields.[[Millisecond]] to plainDateTime.[[ISODateTime]].[[Time]].[[Millisecond]].\n        // 10. Set fields.[[Microsecond]] to plainDateTime.[[ISODateTime]].[[Time]].[[Microsecond]].\n        // 11. Set fields.[[Nanosecond]] to plainDateTime.[[ISODateTime]].[[Time]].[[Nanosecond]].\n        // 12. Let partialDateTime be ? PrepareCalendarFields(calendar, temporalDateTimeLike, « year, month, month-code, day », « hour, minute, second, millisecond, microsecond, nanosecond », partial).\n        // 13. Set fields to CalendarMergeFields(calendar, fields, partialDateTime).\n        let fields = to_date_time_fields(&partial_object, dt.inner.calendar(), context)?;\n        // 14. Let resolvedOptions be ? GetOptionsObject(options).\n        let options = get_options_object(args.get_or_undefined(1))?;\n        // 15. Let overflow be ? GetTemporalOverflowOption(resolvedOptions).\n        let overflow = get_option::<Overflow>(&options, js_string!(\"overflow\"), context)?;\n\n        // 16. Let result be ? InterpretTemporalDateTimeFields(calendar, fields, overflow).\n        // 17. Return ? CreateTemporalDateTime(result, calendar).\n        create_temporal_datetime(dt.inner.with(fields, overflow)?, None, context).map(Into::into)\n    }\n\n    /// 5.3.26 Temporal.PlainDateTime.prototype.withPlainTime ( `[ plainTimeLike ]` )\n    ///\n    /// More information:\n    ///\n    /// - [ECMAScript Temporal proposal][spec]\n    /// - [MDN reference][mdn]\n    /// - [`temporal_rs` documentation][temporal_rs-docs]\n    ///\n    /// [spec]: https://tc39.es/proposal-temporal/#sec-temporal.plaindatetime.prototype.withplaintime\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Temporal/PlainDateTime/withPlainTime\n    /// [temporal_rs-docs]: https://docs.rs/temporal_rs/latest/temporal_rs/struct.PlainDateTime.html#method.with_plain_time\n    fn with_plain_time(\n        this: &JsValue,\n        args: &[JsValue],\n        context: &mut Context,\n    ) -> JsResult<JsValue> {\n        let object = this.as_object();\n        let dt = object\n            .as_ref()\n            .and_then(JsObject::downcast_ref::<Self>)\n            .ok_or_else(|| {\n                JsNativeError::typ().with_message(\"the this object must be a PlainDateTime object.\")\n            })?;\n\n        let time = args\n            .get_or_undefined(0)\n            .map(|v| to_temporal_time(v, None, context))\n            .transpose()?;\n\n        create_temporal_datetime(dt.inner.with_time(time)?, None, context).map(Into::into)\n    }\n\n    /// 5.3.27 `Temporal.PlainDateTime.prototype.withCalendar ( calendarLike )`\n    ///\n    /// More information:\n    ///\n    /// - [ECMAScript Temporal proposal][spec]\n    /// - [MDN reference][mdn]\n    /// - [`temporal_rs` documentation][temporal_rs-docs]\n    ///\n    /// [spec]: https://tc39.es/proposal-temporal/#sec-temporal.plaindatetime.prototype.withCalendar\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Temporal/PlainDateTime/withCalendar\n    /// [temporal_rs-docs]: https://docs.rs/temporal_rs/latest/temporal_rs/struct.PlainDateTime.html#method.with_calendar\n    fn with_calendar(this: &JsValue, args: &[JsValue], context: &mut Context) -> JsResult<JsValue> {\n        let object = this.as_object();\n        let dt = object\n            .as_ref()\n            .and_then(JsObject::downcast_ref::<Self>)\n            .ok_or_else(|| {\n                JsNativeError::typ().with_message(\"the this object must be a PlainDateTime object.\")\n            })?;\n\n        let calendar = to_temporal_calendar_identifier(args.get_or_undefined(0))?;\n\n        create_temporal_datetime(dt.inner.with_calendar(calendar), None, context).map(Into::into)\n    }\n\n    /// 5.3.28 `Temporal.PlainDateTime.prototype.add ( temporalDurationLike [ , options ] )`\n    ///\n    /// More information:\n    ///\n    /// - [ECMAScript Temporal proposal][spec]\n    /// - [MDN reference][mdn]\n    /// - [`temporal_rs` documentation][temporal_rs-docs]\n    ///\n    /// [spec]: https://tc39.es/proposal-temporal/#sec-temporal.plaindatetime.prototype.add\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Temporal/PlainDateTime/add\n    /// [temporal_rs-docs]: https://docs.rs/temporal_rs/latest/temporal_rs/struct.PlainDateTime.html#method.add\n    fn add(this: &JsValue, args: &[JsValue], context: &mut Context) -> JsResult<JsValue> {\n        // 1. Let temporalDate be the this value.\n        // 2. Perform ? RequireInternalSlot(temporalDate, [[InitializedTemporalDate]]).\n        let object = this.as_object();\n        let dt = object\n            .as_ref()\n            .and_then(JsObject::downcast_ref::<Self>)\n            .ok_or_else(|| {\n                JsNativeError::typ().with_message(\"the this object must be a PlainDateTime object.\")\n            })?;\n\n        // 3. Let duration be ? ToTemporalDuration(temporalDurationLike).\n        let duration = to_temporal_duration_record(args.get_or_undefined(0), context)?;\n\n        // 4. Set options to ? GetOptionsObject(options).\n        let options = get_options_object(args.get_or_undefined(1))?;\n        let overflow = get_option::<Overflow>(&options, js_string!(\"overflow\"), context)?;\n\n        // 5. Let calendarRec be ? CreateCalendarMethodsRecord(temporalDate.[[Calendar]], « date-add »).\n        // 6. Return ? AddDate(calendarRec, temporalDate, duration, options).\n        create_temporal_datetime(dt.inner.add(&duration, overflow)?, None, context).map(Into::into)\n    }\n\n    /// 5.3.29 `Temporal.PlainDateTime.prototype.subtract ( temporalDurationLike [ , options ] )`\n    ///\n    /// More information:\n    ///\n    /// - [ECMAScript Temporal proposal][spec]\n    /// - [MDN reference][mdn]\n    /// - [`temporal_rs` documentation][temporal_rs-docs]\n    ///\n    /// [spec]: https://tc39.es/proposal-temporal/#sec-temporal.plaindatetime.prototype.subtract\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Temporal/PlainDateTime/subtract\n    /// [temporal_rs-docs]: https://docs.rs/temporal_rs/latest/temporal_rs/struct.PlainDateTime.html#method.subtract\n    fn subtract(this: &JsValue, args: &[JsValue], context: &mut Context) -> JsResult<JsValue> {\n        // 1. Let temporalDate be the this value.\n        // 2. Perform ? RequireInternalSlot(temporalDate, [[InitializedTemporalDate]]).\n        let object = this.as_object();\n        let dt = object\n            .as_ref()\n            .and_then(JsObject::downcast_ref::<Self>)\n            .ok_or_else(|| {\n                JsNativeError::typ().with_message(\"the this object must be a PlainDateTime object.\")\n            })?;\n\n        // 3. Let duration be ? ToTemporalDuration(temporalDurationLike).\n        let duration = to_temporal_duration_record(args.get_or_undefined(0), context)?;\n\n        // 4. Set options to ? GetOptionsObject(options).\n        let options = get_options_object(args.get_or_undefined(1))?;\n        let overflow = get_option::<Overflow>(&options, js_string!(\"overflow\"), context)?;\n\n        // 5. Let negatedDuration be CreateNegatedTemporalDuration(duration).\n        // 6. Let calendarRec be ? CreateCalendarMethodsRecord(temporalDate.[[Calendar]], « date-add »).\n        // 7. Return ? AddDate(calendarRec, temporalDate, negatedDuration, options).\n        create_temporal_datetime(dt.inner.subtract(&duration, overflow)?, None, context)\n            .map(Into::into)\n    }\n\n    /// 5.3.30 `Temporal.PlainDateTime.prototype.until ( other [ , options ] )`\n    ///\n    /// More information:\n    ///\n    /// - [ECMAScript Temporal proposal][spec]\n    /// - [MDN reference][mdn]\n    /// - [`temporal_rs` documentation][temporal_rs-docs]\n    ///\n    /// [spec]: https://tc39.es/proposal-temporal/#sec-temporal.plaindatetime.prototype.until\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Temporal/PlainDateTime/until\n    /// [temporal_rs-docs]: https://docs.rs/temporal_rs/latest/temporal_rs/struct.PlainDateTime.html#method.until\n    fn until(this: &JsValue, args: &[JsValue], context: &mut Context) -> JsResult<JsValue> {\n        let object = this.as_object();\n        let dt = object\n            .as_ref()\n            .and_then(JsObject::downcast_ref::<Self>)\n            .ok_or_else(|| {\n                JsNativeError::typ().with_message(\"the this object must be a PlainDateTime object.\")\n            })?;\n\n        let other = to_temporal_datetime(args.get_or_undefined(0), None, context)?;\n\n        let options = get_options_object(args.get_or_undefined(1))?;\n        let settings = get_difference_settings(&options, context)?;\n\n        create_temporal_duration(dt.inner.until(&other, settings)?, None, context).map(Into::into)\n    }\n\n    /// 5.3.31 `Temporal.PlainDateTime.prototype.since ( other [ , options ] )`\n    ///\n    /// More information:\n    ///\n    /// - [ECMAScript Temporal proposal][spec]\n    /// - [MDN reference][mdn]\n    /// - [`temporal_rs` documentation][temporal_rs-docs]\n    ///\n    /// [spec]: https://tc39.es/proposal-temporal/#sec-temporal.plaindatetime.prototype.since\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Temporal/PlainDateTime/since\n    /// [temporal_rs-docs]: https://docs.rs/temporal_rs/latest/temporal_rs/struct.PlainDateTime.html#method.since\n    fn since(this: &JsValue, args: &[JsValue], context: &mut Context) -> JsResult<JsValue> {\n        let object = this.as_object();\n        let dt = object\n            .as_ref()\n            .and_then(JsObject::downcast_ref::<Self>)\n            .ok_or_else(|| {\n                JsNativeError::typ().with_message(\"the this object must be a PlainDateTime object.\")\n            })?;\n\n        let other = to_temporal_datetime(args.get_or_undefined(0), None, context)?;\n\n        let options = get_options_object(args.get_or_undefined(1))?;\n        let settings = get_difference_settings(&options, context)?;\n\n        create_temporal_duration(dt.inner.since(&other, settings)?, None, context).map(Into::into)\n    }\n\n    /// 5.3.32 Temporal.PlainDateTime.prototype.round ( roundTo )\n    ///\n    /// More information:\n    ///\n    /// - [ECMAScript Temporal proposal][spec]\n    /// - [MDN reference][mdn]\n    /// - [`temporal_rs` documentation][temporal_rs-docs]\n    ///\n    /// [spec]: https://tc39.es/proposal-temporal/#sec-temporal.plaindatetime.prototype.round\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Temporal/PlainDateTime/round\n    /// [temporal_rs-docs]: https://docs.rs/temporal_rs/latest/temporal_rs/struct.PlainDateTime.html#method.round\n    fn round(this: &JsValue, args: &[JsValue], context: &mut Context) -> JsResult<JsValue> {\n        let object = this.as_object();\n        let dt = object\n            .as_ref()\n            .and_then(JsObject::downcast_ref::<Self>)\n            .ok_or_else(|| {\n                JsNativeError::typ().with_message(\"the this object must be a PlainTime object.\")\n            })?;\n\n        let round_to = match args.first().map(JsValue::variant) {\n            // 3. If roundTo is undefined, then\n            None | Some(JsVariant::Undefined) => {\n                return Err(JsNativeError::typ()\n                    .with_message(\"roundTo cannot be undefined.\")\n                    .into());\n            }\n            // 4. If Type(roundTo) is String, then\n            Some(JsVariant::String(rt)) => {\n                // a. Let paramString be roundTo.\n                let param_string = rt.clone();\n                // b. Set roundTo to OrdinaryObjectCreate(null).\n                let new_round_to = JsObject::with_null_proto();\n                // c. Perform ! CreateDataPropertyOrThrow(roundTo, \"smallestUnit\", paramString).\n                new_round_to.create_data_property_or_throw(\n                    js_string!(\"smallestUnit\"),\n                    param_string,\n                    context,\n                )?;\n                new_round_to\n            }\n            // 5. Else,\n            Some(round_to) => {\n                // a. Set roundTo to ? GetOptionsObject(roundTo).\n                get_options_object(&JsValue::from(round_to))?\n            }\n        };\n\n        let mut options = RoundingOptions::default();\n\n        options.increment =\n            get_option::<RoundingIncrement>(&round_to, js_string!(\"roundingIncrement\"), context)?;\n\n        // 8. Let roundingMode be ? ToTemporalRoundingMode(roundTo, \"halfExpand\").\n        options.rounding_mode =\n            get_option::<RoundingMode>(&round_to, js_string!(\"roundingMode\"), context)?;\n\n        // 9. Let smallestUnit be ? GetTemporalUnit(roundTo, \"smallestUnit\", TIME, REQUIRED, undefined).\n        options.smallest_unit = get_temporal_unit(\n            &round_to,\n            js_string!(\"smallestUnit\"),\n            TemporalUnitGroup::Time,\n            Some(vec![Unit::Day]),\n            context,\n        )?;\n\n        create_temporal_datetime(dt.inner().round(options)?, None, context).map(Into::into)\n    }\n\n    /// 5.3.33 Temporal.PlainDateTime.prototype.equals ( other )\n    ///\n    /// More information:\n    ///\n    /// - [ECMAScript Temporal proposal][spec]\n    /// - [MDN reference][mdn]\n    /// - [`temporal_rs` documentation][temporal_rs-docs]\n    ///\n    /// [spec]: https://tc39.es/proposal-temporal/#sec-temporal.plaindatetime.prototype.equals\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Temporal/PlainDateTime/equals\n    /// [temporal_rs-docs]: https://docs.rs/temporal_rs/latest/temporal_rs/struct.PlainDateTime.html#impl-Eq-for-PlainDateTime\n    fn equals(this: &JsValue, args: &[JsValue], context: &mut Context) -> JsResult<JsValue> {\n        // 1. Let dateTime be the this value.\n        // 2. Perform ? RequireInternalSlot(dateTime, [[InitializedTemporalDateTime]]).\n        let object = this.as_object();\n        let dt = object\n            .as_ref()\n            .and_then(JsObject::downcast_ref::<Self>)\n            .ok_or_else(|| {\n                JsNativeError::typ().with_message(\"the this object must be a PlainDateTime object.\")\n            })?;\n\n        // 3. Set other to ? ToTemporalDateTime(other).\n        let other = to_temporal_datetime(args.get_or_undefined(0), None, context)?;\n\n        // 4. Let result be CompareISODateTime(dateTime.[[ISOYear]], dateTime.[[ISOMonth]],\n        // dateTime.[[ISODay]], dateTime.[[ISOHour]], dateTime.[[ISOMinute]],\n        // dateTime.[[ISOSecond]], dateTime.[[ISOMillisecond]], dateTime.[[ISOMicrosecond]],\n        // dateTime.[[ISONanosecond]], other.[[ISOYear]], other.[[ISOMonth]], other.[[ISODay]],\n        // other.[[ISOHour]], other.[[ISOMinute]], other.[[ISOSecond]], other.[[ISOMillisecond]],\n        // other.[[ISOMicrosecond]], other.[[ISONanosecond]]).\n        // 5. If result is not 0, return false.\n        // 6. Return ? CalendarEquals(dateTime.[[Calendar]], other.[[Calendar]]).\n        Ok((dt.inner == other).into())\n    }\n\n    /// 5.3.34 `Temporal.PlainDateTime.prototype.toString ( [ options ] )`\n    ///\n    /// More information:\n    ///\n    /// - [ECMAScript Temporal proposal][spec]\n    /// - [MDN reference][mdn]\n    /// - [`temporal_rs` documentation][temporal_rs-docs]\n    ///\n    /// [spec]: https://tc39.es/proposal-temporal/#sec-temporal.plaindatetime.prototype.with\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Temporal/PlainDateTime/with\n    /// [temporal_rs-docs]: https://docs.rs/temporal_rs/latest/temporal_rs/struct.PlainDateTime.html#method.to_ixdtf_string\n    fn to_string(this: &JsValue, args: &[JsValue], context: &mut Context) -> JsResult<JsValue> {\n        let object = this.as_object();\n        let dt = object\n            .as_ref()\n            .and_then(JsObject::downcast_ref::<Self>)\n            .ok_or_else(|| {\n                JsNativeError::typ().with_message(\"the this object must be a PlainDateTime object.\")\n            })?;\n\n        let options = get_options_object(args.get_or_undefined(0))?;\n\n        let show_calendar =\n            get_option::<DisplayCalendar>(&options, js_string!(\"calendarName\"), context)?\n                .unwrap_or(DisplayCalendar::Auto);\n        let precision = get_digits_option(&options, context)?;\n        let rounding_mode =\n            get_option::<RoundingMode>(&options, js_string!(\"roundingMode\"), context)?;\n        let smallest_unit = get_option::<Unit>(&options, js_string!(\"smallestUnit\"), context)?;\n\n        let ixdtf = dt.inner.to_ixdtf_string(\n            ToStringRoundingOptions {\n                precision,\n                smallest_unit,\n                rounding_mode,\n            },\n            show_calendar,\n        )?;\n        Ok(JsString::from(ixdtf).into())\n    }\n\n    /// 5.3.35 `Temporal.PlainDateTime.prototype.toLocaleString ( [ locales [ , options ] ] )`\n    ///\n    /// More information:\n    ///\n    /// - [ECMAScript Temporal proposal][spec]\n    /// - [MDN reference][mdn]\n    ///\n    /// [spec]: https://tc39.es/proposal-temporal/#sec-temporal.plaindatetime.prototype.with\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Temporal/PlainDateTime/with\n    fn to_locale_string(this: &JsValue, _args: &[JsValue], _: &mut Context) -> JsResult<JsValue> {\n        // TODO: Update for ECMA-402 compliance\n        let object = this.as_object();\n        let dt = object\n            .as_ref()\n            .and_then(JsObject::downcast_ref::<Self>)\n            .ok_or_else(|| {\n                JsNativeError::typ().with_message(\"the this object must be a PlainDateTime object.\")\n            })?;\n\n        let ixdtf = dt\n            .inner\n            .to_ixdtf_string(ToStringRoundingOptions::default(), DisplayCalendar::Auto)?;\n        Ok(JsString::from(ixdtf).into())\n    }\n\n    /// 5.3.36 `Temporal.PlainDateTime.prototype.toJSON ( )`\n    ///\n    /// More information:\n    ///\n    /// - [ECMAScript Temporal proposal][spec]\n    /// - [MDN reference][mdn]\n    ///\n    /// [spec]: https://tc39.es/proposal-temporal/#sec-temporal.plaindatetime.prototype.with\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Temporal/PlainDateTime/with\n    fn to_json(this: &JsValue, _: &[JsValue], _: &mut Context) -> JsResult<JsValue> {\n        let object = this.as_object();\n        let dt = object\n            .as_ref()\n            .and_then(JsObject::downcast_ref::<Self>)\n            .ok_or_else(|| {\n                JsNativeError::typ().with_message(\"the this object must be a PlainDateTime object.\")\n            })?;\n\n        let ixdtf = dt\n            .inner\n            .to_ixdtf_string(ToStringRoundingOptions::default(), DisplayCalendar::Auto)?;\n        Ok(JsString::from(ixdtf).into())\n    }\n\n    /// 5.3.37 `Temporal.PlainDateTime.prototype.valueOf ( )`\n    ///\n    /// More information:\n    ///\n    /// - [ECMAScript Temporal proposal][spec]\n    /// - [MDN reference][mdn]\n    ///\n    /// [spec]: https://tc39.es/proposal-temporal/#sec-temporal.plaindatetime.prototype.with\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Temporal/PlainDateTime/with\n    pub(crate) fn value_of(_this: &JsValue, _: &[JsValue], _: &mut Context) -> JsResult<JsValue> {\n        Err(JsNativeError::typ()\n            .with_message(\"`valueOf` not supported by Temporal built-ins. See 'compare', 'equals', or `toString`\")\n            .into())\n    }\n\n    /// 5.3.38 `Temporal.PlainDateTime.prototype.toZonedDateTime ( temporalTimeZoneLike [ , options ] )`\n    ///\n    /// More information:\n    ///\n    /// - [ECMAScript Temporal proposal][spec]\n    /// - [MDN reference][mdn]\n    /// - [`temporal_rs` documentation][temporal_rs-docs]\n    ///\n    /// [spec]: https://tc39.es/proposal-temporal/#sec-temporal.plaindatetime.prototype.tozoneddatetime\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Temporal/PlainDateTime/toZonedDateTime\n    /// [temporal_rs-docs]: https://docs.rs/temporal_rs/latest/temporal_rs/struct.PlainDateTime.html#method.to_zoned_date_time\n    fn to_zoned_date_time(\n        this: &JsValue,\n        args: &[JsValue],\n        context: &mut Context,\n    ) -> JsResult<JsValue> {\n        // 1. Let dateTime be the this value.\n        // 2. Perform ? RequireInternalSlot(dateTime, [[InitializedTemporalDateTime]]).\n        let object = this.as_object();\n        let dt = object\n            .as_ref()\n            .and_then(JsObject::downcast_ref::<Self>)\n            .ok_or_else(|| {\n                JsNativeError::typ().with_message(\"the this object must be a PlainDateTime object.\")\n            })?;\n        // 3. Let timeZone be ? ToTemporalTimeZoneIdentifier(temporalTimeZoneLike).\n        let timezone = to_temporal_timezone_identifier(args.get_or_undefined(0), context)?;\n        // 4. Let resolvedOptions be ? GetOptionsObject(options).\n        let options = get_options_object(args.get_or_undefined(1))?;\n        // 5. Let disambiguation be ? GetTemporalDisambiguationOption(resolvedOptions).\n        let disambiguation =\n            get_option::<Disambiguation>(&options, js_string!(\"disambiguation\"), context)?\n                .unwrap_or_default();\n\n        // 6. Let epochNs be ? GetEpochNanosecondsFor(timeZone, dateTime.[[ISODateTime]], disambiguation).\n        // 7. Return ! CreateTemporalZonedDateTime(epochNs, timeZone, dateTime.[[Calendar]]).\n\n        let result = dt.inner.to_zoned_date_time_with_provider(\n            timezone,\n            disambiguation,\n            context.timezone_provider(),\n        )?;\n        create_temporal_zoneddatetime(result, None, context).map(Into::into)\n    }\n\n    /// 5.3.39 `Temporal.PlainDateTime.prototype.toPlainDate ( )`\n    ///\n    /// More information:\n    ///\n    /// - [ECMAScript Temporal proposal][spec]\n    /// - [MDN reference][mdn]\n    /// - [`temporal_rs` documentation][temporal_rs-docs]\n    ///\n    /// [spec]: https://tc39.es/proposal-temporal/#sec-temporal.plaindatetime.prototype.toplaindate\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Temporal/PlainDateTime/toPlainDate\n    /// [temporal_rs-docs]: https://docs.rs/temporal_rs/latest/temporal_rs/struct.PlainDateTime.html#method.to_plain_date\n    fn to_plain_date(this: &JsValue, _: &[JsValue], context: &mut Context) -> JsResult<JsValue> {\n        let object = this.as_object();\n        let dt = object\n            .as_ref()\n            .and_then(JsObject::downcast_ref::<Self>)\n            .ok_or_else(|| {\n                JsNativeError::typ().with_message(\"the this object must be a PlainDateTime object.\")\n            })?;\n\n        let result = dt.inner.to_plain_date();\n        create_temporal_date(result, None, context).map(Into::into)\n    }\n\n    /// 5.3.40 `Temporal.PlainDateTime.prototype.toPlainTime ( )`\n    ///\n    /// More information:\n    ///\n    /// - [ECMAScript Temporal proposal][spec]\n    /// - [MDN reference][mdn]\n    /// - [`temporal_rs` documentation][temporal_rs-docs]\n    ///\n    /// [spec]: https://tc39.es/proposal-temporal/#sec-temporal.plaindatetime.prototype.toplaintime\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Temporal/PlainDateTime/toPlainTime\n    /// [temporal_rs-docs]: https://docs.rs/temporal_rs/latest/temporal_rs/struct.PlainDateTime.html#method.to_plain_time\n    fn to_plain_time(this: &JsValue, _: &[JsValue], context: &mut Context) -> JsResult<JsValue> {\n        let object = this.as_object();\n        let dt = object\n            .as_ref()\n            .and_then(JsObject::downcast_ref::<Self>)\n            .ok_or_else(|| {\n                JsNativeError::typ().with_message(\"the this object must be a PlainDateTime object.\")\n            })?;\n\n        let result = dt.inner.to_plain_time();\n        create_temporal_time(result, None, context).map(Into::into)\n    }\n}\n\n// ==== PlainDateTime Abstract Operations ====\n\npub(crate) fn create_temporal_datetime(\n    inner: InnerDateTime,\n    new_target: Option<&JsValue>,\n    context: &mut Context,\n) -> JsResult<JsObject> {\n    // NOTE(nekevss): The below validations should be upheld with the creation of `InnerDateTime`.\n    // 1. If IsValidISODate(isoYear, isoMonth, isoDay) is false, throw a RangeError exception.\n    // 2. If IsValidTime(hour, minute, second, millisecond, microsecond, nanosecond) is false, throw a RangeError exception.\n    // 3. If ISODateTimeWithinLimits(isoYear, isoMonth, isoDay, hour, minute, second, millisecond, microsecond, nanosecond) is false, then\n    // a. Throw a RangeError exception.\n\n    // 4. If newTarget is not present, set newTarget to %Temporal.PlainDateTime%.\n    let new_target = if let Some(new_target) = new_target {\n        new_target.clone()\n    } else {\n        context\n            .realm()\n            .intrinsics()\n            .constructors()\n            .plain_date_time()\n            .constructor()\n            .into()\n    };\n\n    // 5. Let object be ? OrdinaryCreateFromConstructor(newTarget, \"%Temporal.PlainDateTime.prototype%\", « [[InitializedTemporalDateTime]], [[ISOYear]], [[ISOMonth]], [[ISODay]], [[ISOHour]], [[ISOMinute]], [[ISOSecond]], [[ISOMillisecond]], [[ISOMicrosecond]], [[ISONanosecond]], [[Calendar]] »).\n    let prototype = get_prototype_from_constructor(\n        &new_target,\n        StandardConstructors::plain_date_time,\n        context,\n    )?;\n\n    // 6. Set object.[[ISOYear]] to isoYear.\n    // 7. Set object.[[ISOMonth]] to isoMonth.\n    // 8. Set object.[[ISODay]] to isoDay.\n    // 9. Set object.[[ISOHour]] to hour.\n    // 10. Set object.[[ISOMinute]] to minute.\n    // 11. Set object.[[ISOSecond]] to second.\n    // 12. Set object.[[ISOMillisecond]] to millisecond.\n    // 13. Set object.[[ISOMicrosecond]] to microsecond.\n    // 14. Set object.[[ISONanosecond]] to nanosecond.\n    // 15. Set object.[[Calendar]] to calendar.\n    let obj = JsObject::from_proto_and_data(prototype, PlainDateTime::new(inner));\n\n    // 16. Return object.\n    Ok(obj)\n}\n\npub(crate) fn to_temporal_datetime(\n    value: &JsValue,\n    options: Option<JsValue>,\n    context: &mut Context,\n) -> JsResult<InnerDateTime> {\n    // 1. If options is not present, set options to undefined.\n    // 2. Let resolvedOptions be ? SnapshotOwnProperties(! GetOptionsObject(options), null).\n    // 3. If item is an Object, then\n    if let Some(object) = value.as_object() {\n        // a. If item has an [[InitializedTemporalDateTime]] internal slot, then\n        if let Some(dt) = object.downcast_ref::<PlainDateTime>() {\n            // i. Return item.\n            return Ok(dt.inner.clone());\n        // b. If item has an [[InitializedTemporalZonedDateTime]] internal slot, then\n        } else if let Some(zdt) = object.downcast_ref::<ZonedDateTime>() {\n            // i. Perform ? GetTemporalOverflowOption(resolvedOptions).\n            let options = get_options_object(&options.unwrap_or_default())?;\n            let _ = get_option::<Overflow>(&options, js_string!(\"overflow\"), context)?;\n            // ii. Let instant be ! CreateTemporalInstant(item.[[Nanoseconds]]).\n            // iii. Let timeZoneRec be ? CreateTimeZoneMethodsRecord(item.[[TimeZone]], « get-offset-nanoseconds-for »).\n            // iv. Return ? GetPlainDateTimeFor(timeZoneRec, instant, item.[[Calendar]]).\n            return Ok(zdt.inner.to_plain_date_time());\n        // c. If item has an [[InitializedTemporalDate]] internal slot, then\n        } else if let Some(date) = object.downcast_ref::<PlainDate>() {\n            // i. Perform ? GetTemporalOverflowOption(resolvedOptions).\n            let options = get_options_object(&options.unwrap_or_default())?;\n            let _ = get_option::<Overflow>(&options, js_string!(\"overflow\"), context)?;\n            // ii. Return ? CreateTemporalDateTime(item.[[ISOYear]], item.[[ISOMonth]], item.[[ISODay]], 0, 0, 0, 0, 0, 0, item.[[Calendar]]).\n            return Ok(date.inner.to_plain_date_time(None)?);\n        }\n\n        // d. Let calendar be ? GetTemporalCalendarSlotValueWithISODefault(item).\n        // e. Let calendarRec be ? CreateCalendarMethodsRecord(calendar, « date-from-fields, fields »).\n        // f. Let fields be ? PrepareCalendarFields(calendarRec, item, « \"day\", \"month\",\n        // \"monthCode\", \"year\" », « \"hour\", \"microsecond\", \"millisecond\", \"minute\",\n        // \"nanosecond\", \"second\" », «»)\n        // TODO: Move validation to `temporal_rs`.\n        let partial_dt = to_partial_datetime(&object, context)?;\n        let resolved_options = get_options_object(&options.unwrap_or_default())?;\n        // g. Let result be ? InterpretTemporalDateTimeFields(calendarRec, fields, resolvedOptions).\n        let overflow = get_option::<Overflow>(&resolved_options, js_string!(\"overflow\"), context)?;\n        return InnerDateTime::from_partial(partial_dt, overflow).map_err(Into::into);\n    }\n    // 4. Else,\n    //     a. If item is not a String, throw a TypeError exception.\n    let Some(string) = value.as_string() else {\n        return Err(JsNativeError::typ()\n            .with_message(\"Cannot convert unrecognized value to PlainDateTime.\")\n            .into());\n    };\n    // b. Let result be ? ParseTemporalDateTimeString(item).\n    // c. Assert: IsValidISODate(result.[[Year]], result.[[Month]], result.[[Day]]) is true.\n    // d. Assert: IsValidTime(result.[[Hour]], result.[[Minute]], result.[[Second]],\n    // result.[[Millisecond]], result.[[Microsecond]], result.[[Nanosecond]]) is true.\n    // e. Let calendar be result.[[Calendar]].\n    // f. If calendar is empty, set calendar to \"iso8601\".\n    // g. If IsBuiltinCalendar(calendar) is false, throw a RangeError exception.\n    // h. Set calendar to CanonicalizeUValue(\"ca\", calendar).\n    let date = string.to_std_string_escaped().parse::<InnerDateTime>()?;\n    // i. Perform ? GetTemporalOverflowOption(resolvedOptions).\n    let resolved_options = get_options_object(&options.unwrap_or_default())?;\n    let _ = get_option::<Overflow>(&resolved_options, js_string!(\"overflow\"), context)?;\n    // 5. Return ? CreateTemporalDateTime(result.[[Year]], result.[[Month]], result.[[Day]],\n    // result.[[Hour]], result.[[Minute]], result.[[Second]], result.[[Millisecond]],\n    // result.[[Microsecond]], result.[[Nanosecond]], calendar).\n    Ok(date)\n}\n\nfn to_partial_datetime(\n    partial_object: &JsObject,\n    context: &mut Context,\n) -> JsResult<PartialDateTime> {\n    let calendar = get_temporal_calendar_slot_value_with_default(partial_object, context)?;\n    let fields = to_date_time_fields(partial_object, &calendar, context)?;\n    Ok(PartialDateTime { fields, calendar })\n}\n\nfn to_date_time_fields(\n    partial_object: &JsObject,\n    calendar: &Calendar,\n    context: &mut Context,\n) -> JsResult<DateTimeFields> {\n    let day = partial_object\n        .get(js_string!(\"day\"), context)?\n        .map(|v| {\n            let finite = v.to_finitef64(context)?;\n            finite\n                .as_positive_integer_with_truncation()\n                .map_err(JsError::from)\n        })\n        .transpose()?;\n    let hour = partial_object\n        .get(js_string!(\"hour\"), context)?\n        .map(|v| {\n            let finite = v.to_finitef64(context)?;\n            Ok::<u8, JsError>(finite.as_integer_with_truncation::<u8>())\n        })\n        .transpose()?;\n    // TODO: `temporal_rs` needs a `has_era` method\n    let has_no_era = calendar.kind() == AnyCalendarKind::Iso\n        || calendar.kind() == AnyCalendarKind::Chinese\n        || calendar.kind() == AnyCalendarKind::Dangi;\n    let (era, era_year) = if has_no_era {\n        (None, None)\n    } else {\n        let era = partial_object\n            .get(js_string!(\"era\"), context)?\n            .map(|v| {\n                let v = v.to_primitive(context, crate::value::PreferredType::String)?;\n                let Some(era) = v.as_string() else {\n                    return Err(JsError::from(\n                        JsNativeError::typ()\n                            .with_message(\"The monthCode field value must be a string.\"),\n                    ));\n                };\n                // TODO: double check if an invalid monthCode is a range or type error.\n                TinyAsciiStr::<19>::try_from_str(&era.to_std_string_escaped())\n                    .map_err(|e| JsError::from(JsNativeError::range().with_message(e.to_string())))\n            })\n            .transpose()?;\n        let era_year = partial_object\n            .get(js_string!(\"eraYear\"), context)?\n            .map(|v| {\n                let finite = v.to_finitef64(context)?;\n                Ok::<i32, JsError>(finite.as_integer_with_truncation::<i32>())\n            })\n            .transpose()?;\n        (era, era_year)\n    };\n    let microsecond = partial_object\n        .get(js_string!(\"microsecond\"), context)?\n        .map(|v| {\n            let finite = v.to_finitef64(context)?;\n            Ok::<u16, JsError>(finite.as_integer_with_truncation::<u16>())\n        })\n        .transpose()?;\n\n    let millisecond = partial_object\n        .get(js_string!(\"millisecond\"), context)?\n        .map(|v| {\n            let finite = v.to_finitef64(context)?;\n            Ok::<u16, JsError>(finite.as_integer_with_truncation::<u16>())\n        })\n        .transpose()?;\n\n    let minute = partial_object\n        .get(js_string!(\"minute\"), context)?\n        .map(|v| {\n            let finite = v.to_finitef64(context)?;\n            Ok::<u8, JsError>(finite.as_integer_with_truncation::<u8>())\n        })\n        .transpose()?;\n\n    let month = partial_object\n        .get(js_string!(\"month\"), context)?\n        .map(|v| {\n            let finite = v.to_finitef64(context)?;\n            finite\n                .as_positive_integer_with_truncation()\n                .map_err(JsError::from)\n        })\n        .transpose()?;\n\n    let month_code = partial_object\n        .get(js_string!(\"monthCode\"), context)?\n        .map(|v| {\n            let v = v.to_primitive(context, crate::value::PreferredType::String)?;\n            let Some(month_code) = v.as_string() else {\n                return Err(JsNativeError::typ()\n                    .with_message(\"The monthCode field value must be a string.\")\n                    .into());\n            };\n            MonthCode::from_str(&month_code.to_std_string_escaped()).map_err(JsError::from)\n        })\n        .transpose()?;\n\n    let nanosecond = partial_object\n        .get(js_string!(\"nanosecond\"), context)?\n        .map(|v| {\n            let finite = v.to_finitef64(context)?;\n            Ok::<u16, JsError>(finite.as_integer_with_truncation::<u16>())\n        })\n        .transpose()?;\n\n    let second = partial_object\n        .get(js_string!(\"second\"), context)?\n        .map(|v| {\n            let finite = v.to_finitef64(context)?;\n            Ok::<u8, JsError>(finite.as_integer_with_truncation::<u8>())\n        })\n        .transpose()?;\n\n    let year = partial_object\n        .get(js_string!(\"year\"), context)?\n        .map(|v| {\n            let finite = v.to_finitef64(context)?;\n            Ok::<i32, JsError>(finite.as_integer_with_truncation::<i32>())\n        })\n        .transpose()?;\n\n    let calendar_fields = CalendarFields {\n        year,\n        month,\n        month_code,\n        day,\n        era,\n        era_year,\n    };\n    let time = PartialTime {\n        hour,\n        minute,\n        second,\n        millisecond,\n        microsecond,\n        nanosecond,\n    };\n\n    Ok(DateTimeFields {\n        calendar_fields,\n        time,\n    })\n}\n"
  },
  {
    "path": "core/engine/src/builtins/temporal/plain_date_time/tests.rs",
    "content": "use crate::{TestAction, run_test_actions};\n\n#[test]\nfn pdt_year_of_week_basic() {\n    run_test_actions([\n        TestAction::run(\n            \"let pdt = new Temporal.PlainDateTime(1976, 11, 18, 15, 23, 30, 123, 456, 789, 'iso8601')\",\n        ),\n        TestAction::assert_eq(\"pdt.yearOfWeek\", 1976),\n    ]);\n}\n"
  },
  {
    "path": "core/engine/src/builtins/temporal/plain_month_day/mod.rs",
    "content": "//! Boa's implementation of the ECMAScript `Temporal.PlainMonthDay` built-in object.\nuse std::str::FromStr;\n\nuse crate::{\n    Context, JsArgs, JsData, JsError, JsNativeError, JsObject, JsResult, JsString, JsSymbol,\n    JsValue,\n    builtins::{\n        BuiltInBuilder, BuiltInConstructor, BuiltInObject, IntrinsicObject,\n        options::{get_option, get_options_object},\n        temporal::{calendar::get_temporal_calendar_slot_value_with_default, to_calendar_fields},\n    },\n    context::intrinsics::{Intrinsics, StandardConstructor, StandardConstructors},\n    js_string,\n    object::internal_methods::get_prototype_from_constructor,\n    property::Attribute,\n    realm::Realm,\n    string::StaticJsStrings,\n};\nuse boa_gc::{Finalize, Trace};\n\nuse temporal_rs::{\n    Calendar, MonthCode, PlainMonthDay as InnerMonthDay,\n    fields::CalendarFields,\n    options::{DisplayCalendar, Overflow},\n    parsed_intermediates::ParsedDate,\n    partial::PartialDate,\n};\n\nuse super::{DateTimeValues, create_temporal_date, is_partial_temporal_object};\n\n/// The `Temporal.PlainMonthDay` built-in implementation\n///\n/// More information:\n///\n/// - [ECMAScript Temporal proposal][spec]\n/// - [MDN reference][mdn]\n/// - [`temporal_rs` documentation][temporal_rs-docs]\n///\n/// [spec]: https://tc39.es/proposal-temporal/#sec-temporal-plainmonthday-objects\n/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Temporal/PlainMonthDay\n/// [temporal_rs-docs]: https://docs.rs/temporal_rs/latest/temporal_rs/struct.PlainMonthDay.html\n#[derive(Debug, Clone, Trace, Finalize, JsData)]\n#[boa_gc(unsafe_empty_trace)] // Safety: PlainMonthDay contains no traceable inner fields.\npub struct PlainMonthDay {\n    pub(crate) inner: InnerMonthDay,\n}\n\nimpl PlainMonthDay {\n    fn new(inner: InnerMonthDay) -> Self {\n        Self { inner }\n    }\n}\n\nimpl BuiltInObject for PlainMonthDay {\n    const NAME: JsString = StaticJsStrings::PLAIN_MD_NAME;\n}\n\nimpl IntrinsicObject for PlainMonthDay {\n    fn init(realm: &Realm) {\n        let get_day = BuiltInBuilder::callable(realm, Self::get_day)\n            .name(js_string!(\"get day\"))\n            .build();\n\n        let get_month_code = BuiltInBuilder::callable(realm, Self::get_month_code)\n            .name(js_string!(\"get monthCode\"))\n            .build();\n\n        let get_calendar_id = BuiltInBuilder::callable(realm, Self::get_calendar_id)\n            .name(js_string!(\"get calendarId\"))\n            .build();\n\n        BuiltInBuilder::from_standard_constructor::<Self>(realm)\n            .property(\n                JsSymbol::to_string_tag(),\n                StaticJsStrings::PLAIN_MD_TAG,\n                Attribute::CONFIGURABLE,\n            )\n            .accessor(\n                js_string!(\"day\"),\n                Some(get_day),\n                None,\n                Attribute::CONFIGURABLE,\n            )\n            .accessor(\n                js_string!(\"monthCode\"),\n                Some(get_month_code),\n                None,\n                Attribute::CONFIGURABLE,\n            )\n            .accessor(\n                js_string!(\"calendarId\"),\n                Some(get_calendar_id),\n                None,\n                Attribute::CONFIGURABLE,\n            )\n            .static_method(Self::from, js_string!(\"from\"), 1)\n            .method(Self::with, js_string!(\"with\"), 1)\n            .method(Self::equals, js_string!(\"equals\"), 1)\n            .method(Self::to_string, js_string!(\"toString\"), 0)\n            .method(Self::to_locale_string, js_string!(\"toLocaleString\"), 0)\n            .method(Self::to_json, js_string!(\"toJSON\"), 0)\n            .method(Self::value_of, js_string!(\"valueOf\"), 0)\n            .method(Self::to_plain_date, js_string!(\"toPlainDate\"), 1)\n            .build();\n    }\n\n    fn get(intrinsics: &Intrinsics) -> JsObject {\n        Self::STANDARD_CONSTRUCTOR(intrinsics.constructors()).constructor()\n    }\n}\n\nimpl BuiltInConstructor for PlainMonthDay {\n    const CONSTRUCTOR_ARGUMENTS: usize = 2;\n    const PROTOTYPE_STORAGE_SLOTS: usize = 14;\n    const CONSTRUCTOR_STORAGE_SLOTS: usize = 1;\n\n    const STANDARD_CONSTRUCTOR: fn(&StandardConstructors) -> &StandardConstructor =\n        StandardConstructors::plain_month_day;\n\n    fn constructor(\n        new_target: &JsValue,\n        args: &[JsValue],\n        context: &mut Context,\n    ) -> JsResult<JsValue> {\n        // 1. If NewTarget is undefined, then\n        if new_target.is_undefined() {\n            // a. Throw a TypeError exception.\n            return Err(JsNativeError::typ()\n                .with_message(\"NewTarget cannot be undefined when constructing a PlainYearMonth.\")\n                .into());\n        }\n\n        // We can ignore 2 as the underlying temporal library handles the reference year\n        let m = args\n            .get_or_undefined(0)\n            .to_finitef64(context)?\n            .as_integer_with_truncation::<u8>();\n\n        let d = args\n            .get_or_undefined(1)\n            .to_finitef64(context)?\n            .as_integer_with_truncation::<u8>();\n\n        let calendar = args\n            .get_or_undefined(2)\n            .map(|s| {\n                s.as_string()\n                    .as_ref()\n                    .map(JsString::to_std_string_lossy)\n                    .ok_or_else(|| JsNativeError::typ().with_message(\"calendar must be a string.\"))\n            })\n            .transpose()?\n            .map(|s| Calendar::try_from_utf8(s.as_bytes()))\n            .transpose()?\n            .unwrap_or_default();\n\n        let ref_year = args\n            .get_or_undefined(3)\n            .map(|v| {\n                let finite = v.to_finitef64(context)?;\n                Ok::<i32, JsError>(finite.as_integer_with_truncation::<i32>())\n            })\n            .transpose()?;\n\n        let inner = InnerMonthDay::new_with_overflow(m, d, calendar, Overflow::Reject, ref_year)?;\n        create_temporal_month_day(inner, Some(new_target), context)\n    }\n}\n\n// ==== `Temporal.PlainMonthDay` static methods implementation ====\n\nimpl PlainMonthDay {\n    /// 10.2.2 `Temporal.PlainMonthDay.from ( item [ , options ] )`\n    ///\n    /// More information:\n    ///\n    /// - [ECMAScript Temporal proposal][spec]\n    /// - [MDN reference][mdn]\n    ///\n    /// [spec]: https://tc39.es/proposal-temporal/#sec-temporal.plainmonthday.from\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Temporal/PlainMonthDay/from\n    fn from(_: &JsValue, args: &[JsValue], context: &mut Context) -> JsResult<JsValue> {\n        let item = args.get_or_undefined(0);\n        let options = args.get_or_undefined(1);\n        let inner = to_temporal_month_day(item, options, context)?;\n        create_temporal_month_day(inner, None, context)\n    }\n}\n\n// ==== `PlainMonthDay` Accessor Implementations ====\n\nimpl PlainMonthDay {\n    // Helper for retrieving internal fields\n    fn get_internal_field(this: &JsValue, field: &DateTimeValues) -> JsResult<JsValue> {\n        let object = this.as_object();\n        let month_day = object\n            .as_ref()\n            .and_then(JsObject::downcast_ref::<Self>)\n            .ok_or_else(|| {\n                JsNativeError::typ().with_message(\"this value must be a PlainMonthDay object.\")\n            })?;\n        let inner = &month_day.inner;\n        match field {\n            DateTimeValues::Day => Ok(inner.day().into()),\n            DateTimeValues::MonthCode => Ok(js_string!(inner.month_code().as_str()).into()),\n            _ => unreachable!(),\n        }\n    }\n\n    /// 10.3.3 get `Temporal.PlainMonthDay.prototype.calendarId`\n    ///\n    /// More information:\n    ///\n    /// - [ECMAScript Temporal proposal][spec]\n    /// - [MDN reference][mdn]\n    /// - [`temporal_rs` documentation][temporal_rs-docs]\n    ///\n    /// [spec]: https://tc39.es/proposal-temporal/#sec-get-temporal.plainmonthday.calendarid\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Temporal/PlainMonthDay/calendarId\n    /// [temporal_rs-docs]: https://docs.rs/temporal_rs/latest/temporal_rs/struct.PlainMonthDay.html#method.calendar\n    fn get_calendar_id(this: &JsValue, _: &[JsValue], _: &mut Context) -> JsResult<JsValue> {\n        let object = this.as_object();\n        let month_day = object\n            .as_ref()\n            .and_then(JsObject::downcast_ref::<Self>)\n            .ok_or_else(|| {\n                JsNativeError::typ().with_message(\"this value must be a PlainMonthDay object.\")\n            })?;\n        Ok(js_string!(month_day.inner.calendar().identifier()).into())\n    }\n\n    /// 10.3.4 get `Temporal.PlainMonthDay.prototype.day`\n    ///\n    /// More information:\n    ///\n    /// - [ECMAScript Temporal proposal][spec]\n    /// - [MDN reference][mdn]\n    /// - [`temporal_rs` documentation][temporal_rs-docs]\n    ///\n    /// [spec]: https://tc39.es/proposal-temporal/#sec-get-temporal.plainmonthday.day\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Temporal/PlainMonthDay/day\n    /// [temporal_rs-docs]: https://docs.rs/temporal_rs/latest/temporal_rs/struct.PlainMonthDay.html#method.day\n    fn get_day(this: &JsValue, _: &[JsValue], _: &mut Context) -> JsResult<JsValue> {\n        Self::get_internal_field(this, &DateTimeValues::Day)\n    }\n\n    /// 10.3.5 get `Temporal.PlainMonthDay.prototype.monthCode`\n    ///\n    /// More information:\n    ///\n    /// - [ECMAScript Temporal proposal][spec]\n    /// - [MDN reference][mdn]\n    /// - [`temporal_rs` documentation][temporal_rs-docs]\n    ///\n    /// [spec]: https://tc39.es/proposal-temporal/#sec-get-temporal.plainmonthday.monthcode\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Temporal/PlainMonthDay/monthCode\n    /// [temporal_rs-docs]: https://docs.rs/temporal_rs/latest/temporal_rs/struct.PlainMonthDay.html#method.month_code\n    fn get_month_code(this: &JsValue, _: &[JsValue], _: &mut Context) -> JsResult<JsValue> {\n        Self::get_internal_field(this, &DateTimeValues::MonthCode)\n    }\n}\n\n// ==== `Temporal.PlainMonthDay` Methods ====\n\nimpl PlainMonthDay {\n    /// 10.3.6 `Temporal.PlainMonthDay.prototype.with ( temporalMonthDayLike [ , options ] )`\n    ///\n    /// More information:\n    ///\n    /// - [ECMAScript Temporal proposal][spec]\n    /// - [MDN reference][mdn]\n    /// - [`temporal_rs` documentation][temporal_rs-docs]\n    ///\n    /// [spec]: https://tc39.es/proposal-temporal/#sec-temporal.plainmonthday.with\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Temporal/PlainMonthDay/with\n    /// [temporal_rs-docs]: https://docs.rs/temporal_rs/latest/temporal_rs/struct.PlainMonthDay.html#method.with\n    fn with(this: &JsValue, args: &[JsValue], context: &mut Context) -> JsResult<JsValue> {\n        // 1. Let monthDay be the this value.\n        // 2. Perform ? RequireInternalSlot(monthDay, [[InitializedTemporalMonthDay]]).\n        let object = this.as_object();\n        let month_day = object\n            .as_ref()\n            .and_then(JsObject::downcast_ref::<Self>)\n            .ok_or_else(|| {\n                JsNativeError::typ().with_message(\"this value must be a PlainMonthDay object.\")\n            })?;\n\n        // 3. If ? IsPartialTemporalObject(temporalMonthDayLike) is false, throw a TypeError exception.\n        let Some(object) = is_partial_temporal_object(args.get_or_undefined(0), context)? else {\n            return Err(JsNativeError::typ()\n                .with_message(\"temporalMonthDayLike was not a partial object\")\n                .into());\n        };\n        // 4. Let calendar be monthDay.[[Calendar]].\n        // 5. Let fields be ISODateToFields(calendar, monthDay.[[ISODate]], month-day).\n        // 6. Let partialMonthDay be ? PrepareCalendarFields(calendar, temporalMonthDayLike, « year, month, month-code, day », « », partial).\n        let fields = to_calendar_fields(&object, month_day.inner.calendar(), context)?;\n        // 7. Set fields to CalendarMergeFields(calendar, fields, partialMonthDay).\n        // 8. Let resolvedOptions be ? GetOptionsObject(options).\n        let resolved_options = get_options_object(args.get_or_undefined(1))?;\n        // 9. Let overflow be ? GetTemporalOverflowOption(resolvedOptions).\n        let overflow = get_option::<Overflow>(&resolved_options, js_string!(\"overflow\"), context)?;\n        // 10. Let isoDate be ? CalendarMonthDayFromFields(calendar, fields, overflow).\n        // 11. Return ! CreateTemporalMonthDay(isoDate, calendar).\n        create_temporal_month_day(month_day.inner.with(fields, overflow)?, None, context)\n    }\n\n    /// 10.3.7 `Temporal.PlainMonthDay.prototype.equals ( other )`\n    ///\n    /// More information:\n    ///\n    /// - [ECMAScript Temporal proposal][spec]\n    /// - [MDN reference][mdn]\n    /// - [`temporal_rs` documentation][temporal_rs-docs]\n    ///\n    /// [spec]: https://tc39.es/proposal-temporal/#sec-temporal.plainmonthday.equals\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Temporal/PlainMonthDay/equals\n    /// [temporal_rs-docs]: https://docs.rs/temporal_rs/latest/temporal_rs/struct.PlainMonthDay.html#impl-PartialEq-for-PlainMonthDay\n    fn equals(this: &JsValue, args: &[JsValue], context: &mut Context) -> JsResult<JsValue> {\n        let object = this.as_object();\n        let month_day = object\n            .as_ref()\n            .and_then(JsObject::downcast_ref::<Self>)\n            .ok_or_else(|| {\n                JsNativeError::typ().with_message(\"this value must be a PlainMonthDay object.\")\n            })?;\n\n        let other =\n            to_temporal_month_day(args.get_or_undefined(0), &JsValue::undefined(), context)?;\n\n        Ok((month_day.inner == other).into())\n    }\n\n    /// 10.3.8 `Temporal.PlainMonthDay.prototype.toString ( [ options ] )`\n    ///\n    /// More information:\n    ///\n    /// - [ECMAScript Temporal proposal][spec]\n    /// - [MDN reference][mdn]\n    /// - [`temporal_rs` documentation][temporal_rs-docs]\n    ///\n    /// [spec]: https://tc39.es/proposal-temporal/#sec-temporal.plainmonthday.tostring\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Temporal/PlainMonthDay/toString\n    /// [temporal_rs-docs]: https://docs.rs/temporal_rs/latest/temporal_rs/struct.PlainMonthDay.html#method.to_ixdtf_string\n    fn to_string(this: &JsValue, args: &[JsValue], context: &mut Context) -> JsResult<JsValue> {\n        // 1. Let monthDay be the this value.\n        // 2. Perform ? RequireInternalSlot(monthDay, [[InitializedTemporalMonthDay]]).\n        let object = this.as_object();\n        let month_day = object\n            .as_ref()\n            .and_then(JsObject::downcast_ref::<Self>)\n            .ok_or_else(|| {\n                JsNativeError::typ().with_message(\"this value must be a PlainMonthDay object.\")\n            })?;\n\n        // 3. Set options to ? NormalizeOptionsObject(options).\n        let options = get_options_object(args.get_or_undefined(0))?;\n        // 4. Let showCalendar be ? ToShowCalendarOption(options).\n        // Get calendarName from the options object\n        let show_calendar =\n            get_option::<DisplayCalendar>(&options, js_string!(\"calendarName\"), context)?\n                .unwrap_or(DisplayCalendar::Auto);\n\n        let ixdtf = month_day.inner.to_ixdtf_string(show_calendar);\n        Ok(JsString::from(ixdtf).into())\n    }\n\n    /// 10.3.9 `Temporal.PlainMonthDay.prototype.toLocaleString ( [ locales [ , options ] ] )`\n    ///\n    /// More information:\n    ///\n    /// - [ECMAScript Temporal proposal][spec]\n    /// - [MDN reference][mdn]\n    ///\n    /// [spec]: https://tc39.es/proposal-temporal/#sec-temporal.plainmonthday.tolocalestring\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Temporal/PlainMonthDay/toLocaleString\n    pub(crate) fn to_locale_string(\n        this: &JsValue,\n        _: &[JsValue],\n        _: &mut Context,\n    ) -> JsResult<JsValue> {\n        // TODO: Update for ECMA-402 compliance\n        let object = this.as_object();\n        let month_day = object\n            .as_ref()\n            .and_then(JsObject::downcast_ref::<Self>)\n            .ok_or_else(|| {\n                JsNativeError::typ().with_message(\"this value must be a PlainMonthDay object.\")\n            })?;\n\n        Ok(JsString::from(month_day.inner.to_string()).into())\n    }\n\n    /// 10.3.10 `Temporal.PlainMonthDay.prototype.toJSON ( )`\n    ///\n    /// More information:\n    ///\n    /// - [ECMAScript Temporal proposal][spec]\n    /// - [MDN reference][mdn]\n    ///\n    /// [spec]: https://tc39.es/proposal-temporal/#sec-temporal.plainmonthday.tojson\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Temporal/PlainMonthDay/toJSON\n    pub(crate) fn to_json(this: &JsValue, _: &[JsValue], _: &mut Context) -> JsResult<JsValue> {\n        let object = this.as_object();\n        let month_day = object\n            .as_ref()\n            .and_then(JsObject::downcast_ref::<Self>)\n            .ok_or_else(|| {\n                JsNativeError::typ().with_message(\"this value must be a PlainMonthDay object.\")\n            })?;\n\n        Ok(JsString::from(month_day.inner.to_string()).into())\n    }\n\n    /// 9.3.11 `Temporal.PlainMonthDay.prototype.valueOf ( )`\n    ///\n    /// More information:\n    ///\n    /// - [ECMAScript Temporal proposal][spec]\n    /// - [MDN reference][mdn]\n    ///\n    /// [spec]: https://tc39.es/proposal-temporal/#sec-temporal.plainmonthday.valueof\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Temporal/PlainMonthDay/valueOf\n    pub(crate) fn value_of(_this: &JsValue, _: &[JsValue], _: &mut Context) -> JsResult<JsValue> {\n        Err(JsNativeError::typ()\n            .with_message(\"`valueOf` not supported by Temporal built-ins. See 'compare', 'equals', or `toString`\")\n            .into())\n    }\n\n    /// 10.3.12 `Temporal.PlainMonthDay.prototype.toPlainDate ( item )`\n    ///\n    /// More information:\n    ///\n    /// - [ECMAScript Temporal proposal][spec]\n    /// - [MDN reference][mdn]\n    /// - [`temporal_rs` documentation][temporal_rs-docs]\n    ///\n    /// [spec]: https://tc39.es/proposal-temporal/#sec-temporal.plainmonthday.toplaindate\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Temporal/PlainMonthDay/toPlainDate\n    /// [temporal_rs-docs]: https://docs.rs/temporal_rs/latest/temporal_rs/struct.PlainMonthDay.html#method.to_plain_date\n    fn to_plain_date(this: &JsValue, args: &[JsValue], context: &mut Context) -> JsResult<JsValue> {\n        // 1. Let monthDay be the this value.\n        // 2. Perform ? RequireInternalSlot(monthDay, [[InitializedTemporalMonthDay]]).\n        let object = this.as_object();\n        let month_day = object\n            .as_ref()\n            .and_then(JsObject::downcast_ref::<Self>)\n            .ok_or_else(|| {\n                JsNativeError::typ().with_message(\"this value must be a PlainMonthDay object.\")\n            })?;\n\n        // 3. If item is not an Object, then\n        let Some(item) = args.get_or_undefined(0).as_object() else {\n            // a. Throw a TypeError exception.\n            return Err(JsNativeError::typ()\n                .with_message(\"toPlainDate item must be an object\")\n                .into());\n        };\n\n        // TODO: Handle and implement the below\n        // 4. Let calendar be monthDay.[[Calendar]].\n        // 5. Let fields be ISODateToFields(calendar, monthDay.[[ISODate]], month-day).\n        // 6. Let inputFields be ? PrepareCalendarFields(calendar, item, « year », « », « »).\n        let year = item\n            .get(js_string!(\"year\"), context)?\n            .map(|v| {\n                let finite = v.to_finitef64(context)?;\n                Ok::<i32, JsError>(finite.as_integer_with_truncation::<i32>())\n            })\n            .transpose()?;\n\n        let fields = CalendarFields::new().with_optional_year(year);\n\n        // 7. Let mergedFields be CalendarMergeFields(calendar, fields, inputFields).\n        // 8. Let isoDate be ? CalendarDateFromFields(calendar, mergedFields, constrain).\n        // 9. Return ! CreateTemporalDate(isoDate, calendar).\n        let result = month_day.inner.to_plain_date(Some(fields))?;\n        create_temporal_date(result, None, context).map(Into::into)\n    }\n}\n\n// ==== `PlainMonthDay` Abstract Operations ====\n\npub(crate) fn create_temporal_month_day(\n    inner: InnerMonthDay,\n    new_target: Option<&JsValue>,\n    context: &mut Context,\n) -> JsResult<JsValue> {\n    // 1. If IsValidISODate(referenceISOYear, isoMonth, isoDay) is false, throw a RangeError exception.\n    // 2. If ISODateTimeWithinLimits(referenceISOYear, isoMonth, isoDay, 12, 0, 0, 0, 0, 0) is false, throw a RangeError exception.\n\n    // 3. If newTarget is not present, set newTarget to %Temporal.PlainMonthDay%.\n    let new_target = if let Some(target) = new_target {\n        target.clone()\n    } else {\n        context\n            .realm()\n            .intrinsics()\n            .constructors()\n            .plain_month_day()\n            .constructor()\n            .into()\n    };\n\n    // 4. Let object be ? OrdinaryCreateFromConstructor(newTarget, \"%Temporal.PlainMonthDay.prototype%\", « [[InitializedTemporalMonthDay]], [[ISOMonth]], [[ISODay]], [[ISOYear]], [[Calendar]] »).\n    let proto = get_prototype_from_constructor(\n        &new_target,\n        StandardConstructors::plain_month_day,\n        context,\n    )?;\n\n    // 5. Set object.[[ISOMonth]] to isoMonth.\n    // 6. Set object.[[ISODay]] to isoDay.\n    // 7. Set object.[[Calendar]] to calendar.\n    // 8. Set object.[[ISOYear]] to referenceISOYear.\n    let obj = JsObject::from_proto_and_data(proto, PlainMonthDay::new(inner));\n\n    // 9. Return object.\n    Ok(obj.into())\n}\n\nfn to_temporal_month_day(\n    item: &JsValue,\n    options: &JsValue,\n    context: &mut Context,\n) -> JsResult<InnerMonthDay> {\n    // NOTE: One should be guaranteed by caller\n    // 1. If options is not present, set options to undefined.\n    // 2. If item is a Object, then\n    if let Some(obj) = item.as_object() {\n        // a. If item has an [[InitializedTemporalMonthDay]] internal slot, then\n        if let Some(md) = obj.downcast_ref::<PlainMonthDay>() {\n            // i. Let resolvedOptions be ? GetOptionsObject(options).\n            let options = get_options_object(options)?;\n            // ii. Perform ? GetTemporalOverflowOption(resolvedOptions).\n            let _ = get_option::<Overflow>(&options, js_string!(\"overflow\"), context)?;\n            // iii. Return ! CreateTemporalMonthDay(item.[[ISODate]], item.[[Calendar]]).\n            return Ok(md.inner.clone());\n        }\n\n        // b. Let calendar be ? GetTemporalCalendarIdentifierWithISODefault(item).\n        let calendar = get_temporal_calendar_slot_value_with_default(&obj, context)?;\n        // NOTE: inlined\n        // c. Let fields be ? PrepareCalendarFields(calendar, item, « year, month, month-code, day », «», «»).\n        let day = obj\n            .get(js_string!(\"day\"), context)?\n            .map(|v| {\n                let finite = v.to_finitef64(context)?;\n                finite\n                    .as_positive_integer_with_truncation::<u8>()\n                    .map_err(JsError::from)\n            })\n            .transpose()?;\n\n        let month = obj\n            .get(js_string!(\"month\"), context)?\n            .map(|v| {\n                let finite = v.to_finitef64(context)?;\n                finite\n                    .as_positive_integer_with_truncation::<u8>()\n                    .map_err(JsError::from)\n            })\n            .transpose()?;\n\n        let month_code = obj\n            .get(js_string!(\"monthCode\"), context)?\n            .map(|v| {\n                let primitive = v.to_primitive(context, crate::value::PreferredType::String)?;\n                let Some(month_code) = primitive.as_string() else {\n                    return Err(JsNativeError::typ()\n                        .with_message(\"The monthCode field value must be a string.\")\n                        .into());\n                };\n                MonthCode::from_str(&month_code.to_std_string_escaped()).map_err(JsError::from)\n            })\n            .transpose()?;\n\n        let year = obj\n            .get(js_string!(\"year\"), context)?\n            .map(|v| {\n                let finite = v.to_finitef64(context)?;\n                Ok::<i32, JsError>(finite.as_integer_with_truncation::<i32>())\n            })\n            .transpose()?;\n\n        let partial_date = PartialDate::new()\n            .with_month(month)\n            .with_day(day)\n            .with_year(year)\n            .with_month_code(month_code)\n            .with_calendar(calendar);\n\n        // d. Let resolvedOptions be ? GetOptionsObject(options).\n        let options = get_options_object(options)?;\n        // e. Let overflow be ? GetTemporalOverflowOption(resolvedOptions).\n        let overflow = get_option::<Overflow>(&options, js_string!(\"overflow\"), context)?;\n        // f. Let isoDate be ? CalendarMonthDayFromFields(calendar, fields, overflow).\n        // g. Return ! CreateTemporalMonthDay(isoDate, calendar).\n        return Ok(InnerMonthDay::from_partial(partial_date, overflow)?);\n    }\n\n    // 3. If item is not a String, throw a TypeError exception.\n    let Some(md_string) = item.as_string() else {\n        return Err(JsNativeError::typ()\n            .with_message(\"item must be an object or a string\")\n            .into());\n    };\n    // 4. Let result be ? ParseISODateTime(item, « TemporalMonthDayString »).\n    // 5. Let calendar be result.[[Calendar]].\n    // 6. If calendar is empty, set calendar to \"iso8601\".\n    // 7. Set calendar to ? CanonicalizeCalendar(calendar).\n    let parse_record =\n        ParsedDate::month_day_from_utf8(md_string.to_std_string_escaped().as_bytes())?;\n    // 8. Let resolvedOptions be ? GetOptionsObject(options).\n    let options = get_options_object(options)?;\n    // 9. Perform ? GetTemporalOverflowOption(resolvedOptions).\n    let _ = get_option::<Overflow>(&options, js_string!(\"overflow\"), context)?;\n    // 10. If calendar is \"iso8601\", then\n    // a. Let referenceISOYear be 1972 (the first ISO 8601 leap year after the epoch).\n    // b. Let isoDate be CreateISODateRecord(referenceISOYear, result.[[Month]], result.[[Day]]).\n    // c. Return ! CreateTemporalMonthDay(isoDate, calendar).\n    // 11. Let isoDate be CreateISODateRecord(result.[[Year]], result.[[Month]], result.[[Day]]).\n    // 12. If ISODateWithinLimits(isoDate) is false, throw a RangeError exception.\n    // 13. Set result to ISODateToFields(calendar, isoDate, month-day).\n    // 14. NOTE: The following operation is called with constrain regardless of the value of overflow, in order for the calendar to store a canonical value in the [[Year]] field of the [[ISODate]] internal slot of the result.\n    // 15. Set isoDate to ? CalendarMonthDayFromFields(calendar, result, constrain).\n    // 16. Return ! CreateTemporalMonthDay(isoDate, calendar).\n    Ok(InnerMonthDay::from_parsed(parse_record)?)\n}\n"
  },
  {
    "path": "core/engine/src/builtins/temporal/plain_time/mod.rs",
    "content": "//! Boa's implementation of the ECMAScript `Temporal.PlainTime` built-in object.\n\nuse super::{\n    PlainDateTime, ZonedDateTime, create_temporal_duration,\n    options::{TemporalUnitGroup, get_difference_settings, get_temporal_unit},\n    to_temporal_duration_record,\n};\nuse crate::{\n    Context, JsArgs, JsData, JsError, JsNativeError, JsObject, JsResult, JsString, JsSymbol,\n    JsValue,\n    builtins::{\n        BuiltInBuilder, BuiltInConstructor, BuiltInObject, IntrinsicObject,\n        options::{get_option, get_options_object},\n    },\n    context::intrinsics::{Intrinsics, StandardConstructor, StandardConstructors},\n    js_string,\n    object::internal_methods::get_prototype_from_constructor,\n    property::Attribute,\n    realm::Realm,\n    string::StaticJsStrings,\n};\nuse crate::{builtins::temporal::options::get_digits_option, value::JsVariant};\nuse boa_gc::{Finalize, Trace};\nuse num_traits::{AsPrimitive, PrimInt};\nuse temporal_rs::{\n    PlainTime as PlainTimeInner,\n    options::{\n        Overflow, RoundingIncrement, RoundingMode, RoundingOptions, ToStringRoundingOptions, Unit,\n    },\n    partial::PartialTime,\n    primitive::FiniteF64,\n};\n\n#[cfg(test)]\nmod tests;\n\n/// The `Temporal.PlainTime` built-in implementation.\n///\n/// More information:\n///\n/// - [ECMAScript Temporal proposal][spec]\n/// - [MDN reference][mdn]\n/// - [`temporal_rs` documentation][temporal_rs-docs]\n///\n/// [spec]: https://tc39.es/proposal-temporal/#sec-temporal-plaintime-objects\n/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Temporal/PlainTime\n/// [temporal_rs-docs]: https://docs.rs/temporal_rs/latest/temporal_rs/struct.PlainTime.html\n#[derive(Debug, Clone, Copy, Trace, Finalize, JsData)]\n#[boa_gc(unsafe_empty_trace)] // Safety: PlainTimeInner does not contain any traceable types.\npub struct PlainTime {\n    inner: PlainTimeInner,\n}\n\nimpl BuiltInObject for PlainTime {\n    const NAME: JsString = StaticJsStrings::PLAIN_TIME_NAME;\n}\n\nimpl IntrinsicObject for PlainTime {\n    fn init(realm: &Realm) {\n        let get_hour = BuiltInBuilder::callable(realm, Self::get_hour)\n            .name(js_string!(\"get hour\"))\n            .build();\n\n        let get_minute = BuiltInBuilder::callable(realm, Self::get_minute)\n            .name(js_string!(\"get minute\"))\n            .build();\n\n        let get_second = BuiltInBuilder::callable(realm, Self::get_second)\n            .name(js_string!(\"get second\"))\n            .build();\n\n        let get_millisecond = BuiltInBuilder::callable(realm, Self::get_millisecond)\n            .name(js_string!(\"get millisecond\"))\n            .build();\n\n        let get_microsecond = BuiltInBuilder::callable(realm, Self::get_microsecond)\n            .name(js_string!(\"get microsecond\"))\n            .build();\n\n        let get_nanosecond = BuiltInBuilder::callable(realm, Self::get_nanosecond)\n            .name(js_string!(\"get nanosecond\"))\n            .build();\n\n        BuiltInBuilder::from_standard_constructor::<Self>(realm)\n            .property(\n                JsSymbol::to_string_tag(),\n                StaticJsStrings::PLAIN_TIME_TAG,\n                Attribute::CONFIGURABLE,\n            )\n            .accessor(\n                js_string!(\"hour\"),\n                Some(get_hour),\n                None,\n                Attribute::CONFIGURABLE,\n            )\n            .accessor(\n                js_string!(\"minute\"),\n                Some(get_minute),\n                None,\n                Attribute::CONFIGURABLE,\n            )\n            .accessor(\n                js_string!(\"second\"),\n                Some(get_second),\n                None,\n                Attribute::CONFIGURABLE,\n            )\n            .accessor(\n                js_string!(\"millisecond\"),\n                Some(get_millisecond),\n                None,\n                Attribute::CONFIGURABLE,\n            )\n            .accessor(\n                js_string!(\"microsecond\"),\n                Some(get_microsecond),\n                None,\n                Attribute::CONFIGURABLE,\n            )\n            .accessor(\n                js_string!(\"nanosecond\"),\n                Some(get_nanosecond),\n                None,\n                Attribute::CONFIGURABLE,\n            )\n            .static_method(Self::from, js_string!(\"from\"), 1)\n            .static_method(Self::compare, js_string!(\"compare\"), 2)\n            .method(Self::add, js_string!(\"add\"), 1)\n            .method(Self::subtract, js_string!(\"subtract\"), 1)\n            .method(Self::with, js_string!(\"with\"), 1)\n            .method(Self::until, js_string!(\"until\"), 1)\n            .method(Self::since, js_string!(\"since\"), 1)\n            .method(Self::round, js_string!(\"round\"), 1)\n            .method(Self::equals, js_string!(\"equals\"), 1)\n            .method(Self::to_string, js_string!(\"toString\"), 0)\n            .method(Self::to_locale_string, js_string!(\"toLocaleString\"), 0)\n            .method(Self::to_json, js_string!(\"toJSON\"), 0)\n            .method(Self::value_of, js_string!(\"valueOf\"), 0)\n            .build();\n    }\n\n    fn get(intrinsics: &Intrinsics) -> JsObject {\n        Self::STANDARD_CONSTRUCTOR(intrinsics.constructors()).constructor()\n    }\n}\n\nimpl BuiltInConstructor for PlainTime {\n    const CONSTRUCTOR_ARGUMENTS: usize = 0;\n    const PROTOTYPE_STORAGE_SLOTS: usize = 24;\n    const CONSTRUCTOR_STORAGE_SLOTS: usize = 2;\n\n    const STANDARD_CONSTRUCTOR: fn(&StandardConstructors) -> &StandardConstructor =\n        StandardConstructors::plain_time;\n\n    fn constructor(\n        new_target: &JsValue,\n        args: &[JsValue],\n        context: &mut Context,\n    ) -> JsResult<JsValue> {\n        // 1. If NewTarget is undefined, then\n        if new_target.is_undefined() {\n            // a. Throw a TypeError exception.\n            return Err(JsNativeError::typ()\n                .with_message(\"NewTarget cannot be undefined.\")\n                .into());\n        }\n\n        // 2. If hour is undefined, set hour to 0; else set hour to ? ToIntegerWithTruncation(hour).\n        let hour = args.get_or_undefined(0).map_or(Ok::<u8, JsError>(0), |v| {\n            let finite = v.to_finitef64(context)?;\n            let int = finite.as_integer_with_truncation::<i8>();\n            if int < 0 {\n                return Err(JsNativeError::range()\n                    .with_message(\"invalid time field\")\n                    .into());\n            }\n            Ok(int as u8)\n        })?;\n        // 3. If minute is undefined, set minute to 0; else set minute to ? ToIntegerWithTruncation(minute).\n        let minute = args.get_or_undefined(1).map_or(Ok::<u8, JsError>(0), |v| {\n            let finite = v.to_finitef64(context)?;\n            let int = finite.as_integer_with_truncation::<i8>();\n            if int < 0 {\n                return Err(JsNativeError::range()\n                    .with_message(\"invalid time field\")\n                    .into());\n            }\n            Ok(int as u8)\n        })?;\n        // 4. If second is undefined, set second to 0; else set second to ? ToIntegerWithTruncation(second).\n        let second = args.get_or_undefined(2).map_or(Ok::<u8, JsError>(0), |v| {\n            let finite = v.to_finitef64(context)?;\n            let int = finite.as_integer_with_truncation::<i8>();\n            if int < 0 {\n                return Err(JsNativeError::range()\n                    .with_message(\"invalid time field\")\n                    .into());\n            }\n            Ok(int as u8)\n        })?;\n\n        // 5. If millisecond is undefined, set millisecond to 0; else set millisecond to ? ToIntegerWithTruncation(millisecond).\n        let millisecond = args\n            .get_or_undefined(3)\n            .map_or(Ok::<u16, JsError>(0), |v| {\n                let finite = v.to_finitef64(context)?;\n                let int = finite.as_integer_with_truncation::<i16>();\n                if int < 0 {\n                    return Err(JsNativeError::range()\n                        .with_message(\"invalid time field\")\n                        .into());\n                }\n                Ok(int as u16)\n            })?;\n\n        // 6. If microsecond is undefined, set microsecond to 0; else set microsecond to ? ToIntegerWithTruncation(microsecond).\n        let microsecond = args\n            .get_or_undefined(4)\n            .map_or(Ok::<u16, JsError>(0), |v| {\n                let finite = v.to_finitef64(context)?;\n                let int = finite.as_integer_with_truncation::<i16>();\n                if int < 0 {\n                    return Err(JsNativeError::range()\n                        .with_message(\"invalid time field\")\n                        .into());\n                }\n                Ok(int as u16)\n            })?;\n\n        // 7. If nanosecond is undefined, set nanosecond to 0; else set nanosecond to ? ToIntegerWithTruncation(nanosecond).\n        let nanosecond = args\n            .get_or_undefined(5)\n            .map_or(Ok::<u16, JsError>(0), |v| {\n                let finite = v.to_finitef64(context)?;\n                let int = finite.as_integer_with_truncation::<i16>();\n                if int < 0 {\n                    return Err(JsNativeError::range()\n                        .with_message(\"invalid time field\")\n                        .into());\n                }\n                Ok(int as u16)\n            })?;\n\n        let inner =\n            PlainTimeInner::try_new(hour, minute, second, millisecond, microsecond, nanosecond)?;\n\n        // 8. Return ? CreateTemporalTime(hour, minute, second, millisecond, microsecond, nanosecond, NewTarget).\n        create_temporal_time(inner, Some(new_target), context).map(Into::into)\n    }\n}\n\n// ==== PlainTime accessor methods implementation ====\n\nimpl PlainTime {\n    /// 4.3.3 get `Temporal.PlainTime.prototype.hour`\n    ///\n    /// More information:\n    ///\n    /// - [ECMAScript Temporal proposal][spec]\n    /// - [MDN reference][mdn]\n    /// - [`temporal_rs` documentation][temporal_rs-docs]\n    ///\n    /// [spec]: https://tc39.es/proposal-temporal/#sec-get-temporal.plaintime.prototype.hour\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Temporal/PlainTime/hour\n    /// [temporal_rs-docs]: https://docs.rs/temporal_rs/latest/temporal_rs/struct.PlainTime.html#method.hour\n    fn get_hour(this: &JsValue, _: &[JsValue], _: &mut Context) -> JsResult<JsValue> {\n        // 1. Let temporalTime be the this value.\n        // 2. Perform ? RequireInternalSlot(temporalTime, [[InitializedTemporalTime]]).\n        let object = this.as_object();\n        let time = object\n            .as_ref()\n            .and_then(JsObject::downcast_ref::<Self>)\n            .ok_or_else(|| {\n                JsNativeError::typ().with_message(\"the this object must be a PlainTime object.\")\n            })?;\n\n        // 3. Return 𝔽(temporalTime.[[ISOHour]]).\n        Ok(time.inner.hour().into())\n    }\n\n    /// 4.3.4 get `Temporal.PlainTime.prototype.minute`\n    ///\n    /// More information:\n    ///\n    /// - [ECMAScript Temporal proposal][spec]\n    /// - [MDN reference][mdn]\n    /// - [`temporal_rs` documentation][temporal_rs-docs]\n    ///\n    /// [spec]: https://tc39.es/proposal-temporal/#sec-get-temporal.plaintime.prototype.minute\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Temporal/PlainTime/minute\n    /// [temporal_rs-docs]: https://docs.rs/temporal_rs/latest/temporal_rs/struct.PlainTime.html#method.minute\n    fn get_minute(this: &JsValue, _: &[JsValue], _: &mut Context) -> JsResult<JsValue> {\n        // 1. Let temporalTime be the this value.\n        // 2. Perform ? RequireInternalSlot(temporalTime, [[InitializedTemporalTime]]).\n        let object = this.as_object();\n        let time = object\n            .as_ref()\n            .and_then(JsObject::downcast_ref::<Self>)\n            .ok_or_else(|| {\n                JsNativeError::typ().with_message(\"the this object must be a PlainTime object.\")\n            })?;\n\n        // 3. Return 𝔽(temporalTime.[[ISOMinute]]).\n        Ok(time.inner.minute().into())\n    }\n\n    /// 4.3.5 get `Temporal.PlainTime.prototype.second`\n    ///\n    /// More information:\n    ///\n    /// - [ECMAScript Temporal proposal][spec]\n    /// - [MDN reference][mdn]\n    /// - [`temporal_rs` documentation][temporal_rs-docs]\n    ///\n    /// [spec]: https://tc39.es/proposal-temporal/#sec-get-temporal.plaintime.prototype.second\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Temporal/PlainTime/second\n    /// [temporal_rs-docs]: https://docs.rs/temporal_rs/latest/temporal_rs/struct.PlainTime.html#method.second\n    fn get_second(this: &JsValue, _: &[JsValue], _: &mut Context) -> JsResult<JsValue> {\n        // 1. Let temporalTime be the this value.\n        // 2. Perform ? RequireInternalSlot(temporalTime, [[InitializedTemporalTime]]).\n        let object = this.as_object();\n        let time = object\n            .as_ref()\n            .and_then(JsObject::downcast_ref::<Self>)\n            .ok_or_else(|| {\n                JsNativeError::typ().with_message(\"the this object must be a PlainTime object.\")\n            })?;\n\n        // 3. Return 𝔽(temporalTime.[[ISOSecond]]).\n        Ok(time.inner.second().into())\n    }\n\n    /// 4.3.6 get `Temporal.PlainTime.prototype.millisecond`\n    ///\n    /// More information:\n    ///\n    /// - [ECMAScript Temporal proposal][spec]\n    /// - [MDN reference][mdn]\n    /// - [`temporal_rs` documentation][temporal_rs-docs]\n    ///\n    /// [spec]: https://tc39.es/proposal-temporal/#sec-get-temporal.plaintime.prototype.millisecond\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Temporal/PlainTime/millisecond\n    /// [temporal_rs-docs]: https://docs.rs/temporal_rs/latest/temporal_rs/struct.PlainTime.html#method.millisecond\n    fn get_millisecond(this: &JsValue, _: &[JsValue], _: &mut Context) -> JsResult<JsValue> {\n        // 1. Let temporalTime be the this value.\n        // 2. Perform ? RequireInternalSlot(temporalTime, [[InitializedTemporalTime]]).\n        let object = this.as_object();\n        let time = object\n            .as_ref()\n            .and_then(JsObject::downcast_ref::<Self>)\n            .ok_or_else(|| {\n                JsNativeError::typ().with_message(\"the this object must be a PlainTime object.\")\n            })?;\n\n        // 3. Return 𝔽(temporalTime.[[ISOMillisecond]]).\n        Ok(time.inner.millisecond().into())\n    }\n\n    /// 4.3.7 get `Temporal.PlainTime.prototype.microsecond`\n    ///\n    /// More information:\n    ///\n    /// - [ECMAScript Temporal proposal][spec]\n    /// - [MDN reference][mdn]\n    /// - [`temporal_rs` documentation][temporal_rs-docs]\n    ///\n    /// [spec]: https://tc39.es/proposal-temporal/#sec-get-temporal.plaintime.prototype.microsecond\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Temporal/PlainTime/microsecond\n    /// [temporal_rs-docs]: https://docs.rs/temporal_rs/latest/temporal_rs/struct.PlainTime.html#method.microsecond\n    fn get_microsecond(this: &JsValue, _: &[JsValue], _: &mut Context) -> JsResult<JsValue> {\n        // 1. Let temporalTime be the this value.\n        // 2. Perform ? RequireInternalSlot(temporalTime, [[InitializedTemporalTime]]).\n        let object = this.as_object();\n        let time = object\n            .as_ref()\n            .and_then(JsObject::downcast_ref::<Self>)\n            .ok_or_else(|| {\n                JsNativeError::typ().with_message(\"the this object must be a PlainTime object.\")\n            })?;\n\n        // 3. Return 𝔽(temporalTime.[[ISOMicrosecond]]).\n        Ok(time.inner.microsecond().into())\n    }\n\n    /// 4.3.8 get `Temporal.PlainTime.prototype.nanosecond`\n    ///\n    /// More information:\n    ///\n    /// - [ECMAScript Temporal proposal][spec]\n    /// - [MDN reference][mdn]\n    /// - [`temporal_rs` documentation][temporal_rs-docs]\n    ///\n    /// [spec]: https://tc39.es/proposal-temporal/#sec-get-temporal.plaintime.prototype.nanosecond\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Temporal/PlainTime/nanosecond\n    /// [temporal_rs-docs]: https://docs.rs/temporal_rs/latest/temporal_rs/struct.PlainTime.html#method.nanosecond\n    fn get_nanosecond(this: &JsValue, _: &[JsValue], _: &mut Context) -> JsResult<JsValue> {\n        // 1. Let temporalTime be the this value.\n        // 2. Perform ? RequireInternalSlot(temporalTime, [[InitializedTemporalTime]]).\n        let object = this.as_object();\n        let time = object\n            .as_ref()\n            .and_then(JsObject::downcast_ref::<Self>)\n            .ok_or_else(|| {\n                JsNativeError::typ().with_message(\"the this object must be a PlainTime object.\")\n            })?;\n\n        // 3. Return 𝔽(temporalTime.[[ISONanosecond]]).\n        Ok(time.inner.nanosecond().into())\n    }\n}\n\n// ==== PlainTime static methods implementation ====\n\nimpl PlainTime {\n    /// 4.2.2 `Temporal.PlainTime.from ( item [ , options ] )`\n    ///\n    /// More information:\n    ///\n    /// - [ECMAScript Temporal proposal][spec]\n    /// - [MDN reference][mdn]\n    ///\n    /// [spec]: https://tc39.es/proposal-temporal/#sec-temporal.plaintime.from\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Temporal/PlainTime/from\n    fn from(_: &JsValue, args: &[JsValue], context: &mut Context) -> JsResult<JsValue> {\n        // 1. Return ? ToTemporalTime(item, options).\n        let plain_time = to_temporal_time(args.get_or_undefined(0), args.get(1), context)?;\n        create_temporal_time(plain_time, None, context).map(Into::into)\n    }\n\n    /// 4.2.3 `Temporal.PlainTime.compare ( one, two )`\n    ///\n    /// More information:\n    ///\n    /// - [ECMAScript Temporal proposal][spec]\n    /// - [MDN reference][mdn]\n    /// - [`temporal_rs` documentation][temporal_rs-docs]\n    ///\n    /// [spec]: https://tc39.es/proposal-temporal/#sec-temporal.plaintime.compare\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Temporal/PlainTime/compare\n    /// [temporal_rs-docs]: https://docs.rs/temporal_rs/latest/temporal_rs/struct.PlainTime.html#impl-Ord-for-PlainTime\n    fn compare(_: &JsValue, args: &[JsValue], context: &mut Context) -> JsResult<JsValue> {\n        // 1. Set one to ? ToTemporalTime(one).\n        let one = to_temporal_time(args.get_or_undefined(0), None, context)?;\n        // 2. Set two to ? ToTemporalTime(two).\n        let two = to_temporal_time(args.get_or_undefined(1), None, context)?;\n        // 3. Return 𝔽(CompareTemporalTime(one.[[ISOHour]], one.[[ISOMinute]], one.[[ISOSecond]],\n        // one.[[ISOMillisecond]], one.[[ISOMicrosecond]], one.[[ISONanosecond]], two.[[ISOHour]],\n        // two.[[ISOMinute]], two.[[ISOSecond]], two.[[ISOMillisecond]], two.[[ISOMicrosecond]],\n        // two.[[ISONanosecond]])).\n        Ok((one.cmp(&two) as i8).into())\n    }\n}\n\n// ==== PlainTime.prototype method implementations ====\n\nimpl PlainTime {\n    /// 4.3.9 `Temporal.PlainTime.prototype.add ( temporalDurationLike )`\n    ///\n    /// More information:\n    ///\n    /// - [ECMAScript Temporal proposal][spec]\n    /// - [MDN reference][mdn]\n    /// - [`temporal_rs` documentation][temporal_rs-docs]\n    ///\n    /// [spec]: https://tc39.es/proposal-temporal/#sec-temporal.plaintime.prototype.add\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Temporal/PlainTime/add\n    /// [temporal_rs-docs]: https://docs.rs/temporal_rs/latest/temporal_rs/struct.PlainTime.html#method.add\n    fn add(this: &JsValue, args: &[JsValue], context: &mut Context) -> JsResult<JsValue> {\n        // 1. Let temporalTime be the this value.\n        // 2. Perform ? RequireInternalSlot(temporalTime, [[InitializedTemporalTime]]).\n        let object = this.as_object();\n        let time = object\n            .as_ref()\n            .and_then(JsObject::downcast_ref::<Self>)\n            .ok_or_else(|| {\n                JsNativeError::typ().with_message(\"the this object must be a PlainTime object.\")\n            })?;\n\n        let temporal_duration_like = args.get_or_undefined(0);\n        let duration = to_temporal_duration_record(temporal_duration_like, context)?;\n\n        // 3. Return ? AddDurationToOrSubtractDurationFromPlainTime(add, temporalTime, temporalDurationLike).\n        create_temporal_time(time.inner.add(&duration)?, None, context).map(Into::into)\n    }\n\n    /// 4.3.10 `Temporal.PlainTime.prototype.subtract ( temporalDurationLike )`\n    ///\n    /// More information:\n    ///\n    /// - [ECMAScript Temporal proposal][spec]\n    /// - [MDN reference][mdn]\n    /// - [`temporal_rs` documentation][temporal_rs-docs]\n    ///\n    /// [spec]: https://tc39.es/proposal-temporal/#sec-temporal.plaintime.prototype.subtract\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Temporal/PlainTime/subtract\n    /// [temporal_rs-docs]: https://docs.rs/temporal_rs/latest/temporal_rs/struct.PlainTime.html#method.subtract\n    fn subtract(this: &JsValue, args: &[JsValue], context: &mut Context) -> JsResult<JsValue> {\n        // 1. Let temporalTime be the this value.\n        // 2. Perform ? RequireInternalSlot(temporalTime, [[InitializedTemporalTime]]).\n        let object = this.as_object();\n        let time = object\n            .as_ref()\n            .and_then(JsObject::downcast_ref::<Self>)\n            .ok_or_else(|| {\n                JsNativeError::typ().with_message(\"the this object must be a PlainTime object.\")\n            })?;\n\n        let temporal_duration_like = args.get_or_undefined(0);\n        let duration = to_temporal_duration_record(temporal_duration_like, context)?;\n\n        // 3. Return ? AddDurationToOrSubtractDurationFromPlainTime(subtract, temporalTime, temporalDurationLike).\n        create_temporal_time(time.inner.subtract(&duration)?, None, context).map(Into::into)\n    }\n\n    /// 4.3.11 `Temporal.PlainTime.prototype.with ( temporalTimeLike [ , options ] )`\n    ///\n    /// More information:\n    ///\n    /// - [ECMAScript Temporal proposal][spec]\n    /// - [MDN reference][mdn]\n    /// - [`temporal_rs` documentation][temporal_rs-docs]\n    ///\n    /// [spec]: https://tc39.es/proposal-temporal/#sec-temporal.plaintime.prototype.with\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Temporal/PlainTime/with\n    /// [temporal_rs-docs]: https://docs.rs/temporal_rs/latest/temporal_rs/struct.PlainTime.html#method.with\n    fn with(this: &JsValue, args: &[JsValue], context: &mut Context) -> JsResult<JsValue> {\n        // 1.Let temporalTime be the this value.\n        // 2. Perform ? RequireInternalSlot(temporalTime, [[InitializedTemporalTime]]).\n        let object = this.as_object();\n        let time = object\n            .as_ref()\n            .and_then(JsObject::downcast_ref::<Self>)\n            .ok_or_else(|| {\n                JsNativeError::typ().with_message(\"the this object must be a PlainTime object.\")\n            })?;\n\n        // 3. If ? IsPartialTemporalObject(temporalTimeLike) is false, throw a TypeError exception.\n        // 4. Set options to ? GetOptionsObject(options).\n        let Some(partial_object) =\n            super::is_partial_temporal_object(args.get_or_undefined(0), context)?\n        else {\n            return Err(JsNativeError::typ()\n                .with_message(\"with object was not a PartialTemporalObject.\")\n                .into());\n        };\n\n        // Steps 5-16 equate to the below\n        let partial = to_js_partial_time_record(&partial_object, context)?;\n        // 17. Let resolvedOptions be ? GetOptionsObject(options).\n        // 18. Let overflow be ? GetTemporalOverflowOption(resolvedOptions).\n        let options = get_options_object(args.get_or_undefined(1))?;\n        let overflow = get_option::<Overflow>(&options, js_string!(\"overflow\"), context)?;\n\n        create_temporal_time(\n            time.inner\n                .with(partial.as_temporal_partial_time(overflow)?, overflow)?,\n            None,\n            context,\n        )\n        .map(Into::into)\n    }\n\n    /// 4.3.12 `Temporal.PlainTime.prototype.until ( other [ , options ] )`\n    ///\n    /// More information:\n    ///\n    /// - [ECMAScript Temporal proposal][spec]\n    /// - [MDN reference][mdn]\n    /// - [`temporal_rs` documentation][temporal_rs-docs]\n    ///\n    /// [spec]: https://tc39.es/proposal-temporal/#sec-temporal.plaintime.prototype.until\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Temporal/PlainTime/until\n    /// [temporal_rs-docs]: https://docs.rs/temporal_rs/latest/temporal_rs/struct.PlainTime.html#method.until\n    fn until(this: &JsValue, args: &[JsValue], context: &mut Context) -> JsResult<JsValue> {\n        let object = this.as_object();\n        let time = object\n            .as_ref()\n            .and_then(JsObject::downcast_ref::<Self>)\n            .ok_or_else(|| {\n                JsNativeError::typ().with_message(\"the this object must be a PlainTime object.\")\n            })?;\n\n        let other = to_temporal_time(args.get_or_undefined(0), None, context)?;\n\n        let settings =\n            get_difference_settings(&get_options_object(args.get_or_undefined(1))?, context)?;\n\n        let result = time.inner.until(&other, settings)?;\n\n        create_temporal_duration(result, None, context).map(Into::into)\n    }\n\n    /// 4.3.13 `Temporal.PlainTime.prototype.since ( other [ , options ] )`\n    ///\n    /// More information:\n    ///\n    /// - [ECMAScript Temporal proposal][spec]\n    /// - [MDN reference][mdn]\n    /// - [`temporal_rs` documentation][temporal_rs-docs]\n    ///\n    /// [spec]: https://tc39.es/proposal-temporal/#sec-temporal.plaintime.prototype.since\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Temporal/PlainTime/since\n    /// [temporal_rs-docs]: https://docs.rs/temporal_rs/latest/temporal_rs/struct.PlainTime.html#method.since\n    fn since(this: &JsValue, args: &[JsValue], context: &mut Context) -> JsResult<JsValue> {\n        let object = this.as_object();\n        let time = object\n            .as_ref()\n            .and_then(JsObject::downcast_ref::<Self>)\n            .ok_or_else(|| {\n                JsNativeError::typ().with_message(\"the this object must be a PlainTime object.\")\n            })?;\n\n        let other = to_temporal_time(args.get_or_undefined(0), None, context)?;\n\n        let settings =\n            get_difference_settings(&get_options_object(args.get_or_undefined(1))?, context)?;\n\n        let result = time.inner.since(&other, settings)?;\n\n        create_temporal_duration(result, None, context).map(Into::into)\n    }\n\n    /// 4.3.14 Temporal.PlainTime.prototype.round ( roundTo )\n    ///\n    /// More information:\n    ///\n    /// - [ECMAScript Temporal proposal][spec]\n    /// - [MDN reference][mdn]\n    /// - [`temporal_rs` documentation][temporal_rs-docs]\n    ///\n    /// [spec]: https://tc39.es/proposal-temporal/#sec-temporal.plaintime.prototype.round\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Temporal/PlainTime/round\n    /// [temporal_rs-docs]: https://docs.rs/temporal_rs/latest/temporal_rs/struct.PlainTime.html#method.round\n    fn round(this: &JsValue, args: &[JsValue], context: &mut Context) -> JsResult<JsValue> {\n        // 1. Let temporalTime be the this value.\n        // 2. Perform ? RequireInternalSlot(temporalTime, [[InitializedTemporalTime]]).\n        let object = this.as_object();\n        let time = object\n            .as_ref()\n            .and_then(JsObject::downcast_ref::<Self>)\n            .ok_or_else(|| {\n                JsNativeError::typ().with_message(\"the this object must be a PlainTime object.\")\n            })?;\n\n        let round_to = match args.first().map(JsValue::variant) {\n            // 3. If roundTo is undefined, then\n            None | Some(JsVariant::Undefined) => {\n                return Err(JsNativeError::typ()\n                    .with_message(\"roundTo cannot be undefined.\")\n                    .into());\n            }\n            // 4. If Type(roundTo) is String, then\n            Some(JsVariant::String(rt)) => {\n                // a. Let paramString be roundTo.\n                let param_string = rt.clone();\n                // b. Set roundTo to OrdinaryObjectCreate(null).\n                let new_round_to = JsObject::with_null_proto();\n                // c. Perform ! CreateDataPropertyOrThrow(roundTo, \"smallestUnit\", paramString).\n                new_round_to.create_data_property_or_throw(\n                    js_string!(\"smallestUnit\"),\n                    param_string,\n                    context,\n                )?;\n                new_round_to\n            }\n            // 5. Else,\n            Some(round_to) => {\n                // a. Set roundTo to ? GetOptionsObject(roundTo).\n                get_options_object(&JsValue::from(round_to))?\n            }\n        };\n\n        let mut options = RoundingOptions::default();\n        // 6. NOTE: The following steps read options and perform independent validation in alphabetical order (ToTemporalRoundingIncrement reads \"roundingIncrement\" and ToTemporalRoundingMode reads \"roundingMode\").\n        // 7. Let roundingIncrement be ? ToTemporalRoundingIncrement(roundTo).\n        options.increment =\n            get_option::<RoundingIncrement>(&round_to, js_string!(\"roundingIncrement\"), context)?;\n\n        // 8. Let roundingMode be ? ToTemporalRoundingMode(roundTo, \"halfExpand\").\n        options.rounding_mode =\n            get_option::<RoundingMode>(&round_to, js_string!(\"roundingMode\"), context)?;\n\n        // 9. Let smallestUnit be ? GetTemporalUnit(roundTo, \"smallestUnit\", time, required).\n        options.smallest_unit = get_temporal_unit(\n            &round_to,\n            js_string!(\"smallestUnit\"),\n            TemporalUnitGroup::Time,\n            None,\n            context,\n        )?;\n\n        // 10. Let maximum be MaximumTemporalDurationRoundingIncrement(smallestUnit).\n        // 11. Assert: maximum is not undefined.\n        // 12. Perform ? ValidateTemporalRoundingIncrement(roundingIncrement, maximum, false).\n        // 13. Let result be RoundTime(temporalTime.[[ISOHour]], temporalTime.[[ISOMinute]], temporalTime.[[ISOSecond]], temporalTime.[[ISOMillisecond]], temporalTime.[[ISOMicrosecond]], temporalTime.[[ISONanosecond]], roundingIncrement, smallestUnit, roundingMode).\n        let result = time.inner.round(options)?;\n\n        // 14. Return ! CreateTemporalTime(result.[[Hour]], result.[[Minute]], result.[[Second]], result.[[Millisecond]], result.[[Microsecond]], result.[[Nanosecond]]).\n        create_temporal_time(result, None, context).map(Into::into)\n    }\n\n    /// 4.3.15 Temporal.PlainTime.prototype.equals ( other )\n    ///\n    /// More information:\n    ///\n    /// - [ECMAScript Temporal proposal][spec]\n    /// - [MDN reference][mdn]\n    /// - [`temporal_rs` documentation][temporal_rs-docs]\n    ///\n    /// [spec]: https://tc39.es/proposal-temporal/#sec-temporal.plaintime.prototype.equals\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Temporal/PlainTime/equals\n    /// [temporal_rs-docs]: https://docs.rs/temporal_rs/latest/temporal_rs/struct.PlainTime.html#impl-Eq-for-PlainTime\n    fn equals(this: &JsValue, args: &[JsValue], context: &mut Context) -> JsResult<JsValue> {\n        // 1. Let temporalTime be the this value.\n        // 2. Perform ? RequireInternalSlot(temporalTime, [[InitializedTemporalTime]]).\n        let object = this.as_object();\n        let time = object\n            .as_ref()\n            .and_then(JsObject::downcast_ref::<Self>)\n            .ok_or_else(|| {\n                JsNativeError::typ().with_message(\"the this object must be a PlainTime object.\")\n            })?;\n\n        // 3. Set other to ? ToTemporalTime(other).\n        let other = to_temporal_time(args.get_or_undefined(0), None, context)?;\n        // 4. If temporalTime.[[ISOHour]] ≠ other.[[ISOHour]], return false.\n        // 5. If temporalTime.[[ISOMinute]] ≠ other.[[ISOMinute]], return false.\n        // 6. If temporalTime.[[ISOSecond]] ≠ other.[[ISOSecond]], return false.\n        // 7. If temporalTime.[[ISOMillisecond]] ≠ other.[[ISOMillisecond]], return false.\n        // 8. If temporalTime.[[ISOMicrosecond]] ≠ other.[[ISOMicrosecond]], return false.\n        // 9. If temporalTime.[[ISONanosecond]] ≠ other.[[ISONanosecond]], return false.\n        // 10. Return true.\n        Ok((time.inner == other).into())\n    }\n\n    /// 4.3.16 `Temporal.PlainTime.prototype.toString ( [ options ] )`\n    ///\n    /// More information:\n    ///\n    /// - [ECMAScript Temporal proposal][spec]\n    /// - [MDN reference][mdn]\n    /// - [`temporal_rs` documentation][temporal_rs-docs]\n    ///\n    /// [spec]: https://tc39.es/proposal-temporal/#sec-temporal.plaintime.prototype.tostring\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Temporal/PlainTime/toString\n    /// [temporal_rs-docs]: https://docs.rs/temporal_rs/latest/temporal_rs/struct.PlainTime.html#method.to_ixdtf_string\n    fn to_string(this: &JsValue, args: &[JsValue], context: &mut Context) -> JsResult<JsValue> {\n        let object = this.as_object();\n        let time = object\n            .as_ref()\n            .and_then(JsObject::downcast_ref::<Self>)\n            .ok_or_else(|| {\n                JsNativeError::typ().with_message(\"the this object must be a PlainTime object.\")\n            })?;\n\n        let options = get_options_object(args.get_or_undefined(0))?;\n\n        let precision = get_digits_option(&options, context)?;\n        let rounding_mode =\n            get_option::<RoundingMode>(&options, js_string!(\"roundingMode\"), context)?;\n        let smallest_unit = get_option::<Unit>(&options, js_string!(\"smallestUnit\"), context)?;\n\n        let options = ToStringRoundingOptions {\n            precision,\n            rounding_mode,\n            smallest_unit,\n        };\n\n        let ixdtf = time.inner.to_ixdtf_string(options)?;\n\n        Ok(JsString::from(ixdtf).into())\n    }\n\n    /// 4.3.17 `Temporal.PlainTime.prototype.toLocaleString ( [ locales [ , options ] ] )`\n    ///\n    /// More information:\n    ///\n    /// - [ECMAScript Temporal proposal][spec]\n    /// - [MDN reference][mdn]\n    ///\n    /// [spec]: https://tc39.es/proposal-temporal/#sec-temporal.plaintime.prototype.tolocalestring\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Temporal/PlainTime/toLocaleString\n    fn to_locale_string(this: &JsValue, _: &[JsValue], _: &mut Context) -> JsResult<JsValue> {\n        // TODO: Update for ECMA-402 compliance\n        let object = this.as_object();\n        let time = object\n            .as_ref()\n            .and_then(JsObject::downcast_ref::<Self>)\n            .ok_or_else(|| {\n                JsNativeError::typ().with_message(\"the this object must be a PlainTime object.\")\n            })?;\n\n        let ixdtf = time\n            .inner\n            .to_ixdtf_string(ToStringRoundingOptions::default())?;\n        Ok(JsString::from(ixdtf).into())\n    }\n\n    /// 4.3.18 `Temporal.PlainTime.prototype.toJSON ( )`\n    ///\n    /// More information:\n    ///\n    /// - [ECMAScript Temporal proposal][spec]\n    /// - [MDN reference][mdn]\n    ///\n    /// [spec]: https://tc39.es/proposal-temporal/#sec-temporal.plaintime.prototype.tojson\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Temporal/PlainTime/toJSON\n    fn to_json(this: &JsValue, _: &[JsValue], _: &mut Context) -> JsResult<JsValue> {\n        let object = this.as_object();\n        let time = object\n            .as_ref()\n            .and_then(JsObject::downcast_ref::<Self>)\n            .ok_or_else(|| {\n                JsNativeError::typ().with_message(\"the this object must be a PlainTime object.\")\n            })?;\n\n        let ixdtf = time\n            .inner\n            .to_ixdtf_string(ToStringRoundingOptions::default())?;\n        Ok(JsString::from(ixdtf).into())\n    }\n\n    /// 4.3.19 `Temporal.PlainTime.prototype.valueOf ( )`\n    ///\n    /// More information:\n    ///\n    /// - [ECMAScript Temporal proposal][spec]\n    /// - [MDN reference][mdn]\n    ///\n    /// [spec]: https://tc39.es/proposal-temporal/#sec-temporal.plaintime.prototype.valueof\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Temporal/PlainTime/valueOf\n    fn value_of(_this: &JsValue, _: &[JsValue], _: &mut Context) -> JsResult<JsValue> {\n        // 1. Throw a TypeError exception.\n        Err(JsNativeError::typ()\n            .with_message(\"`valueOf` not supported by Temporal built-ins. See 'compare', 'equals', or `toString`\")\n            .into())\n    }\n}\n\n// ==== PlainTime Abstract Operations ====\n\npub(crate) fn create_temporal_time(\n    inner: PlainTimeInner,\n    new_target: Option<&JsValue>,\n    context: &mut Context,\n) -> JsResult<JsObject> {\n    // Note: IsValidTime is enforced by Time.\n    // 1. If IsValidTime(hour, minute, second, millisecond, microsecond, nanosecond) is false, throw a RangeError exception.\n\n    // 2. If newTarget is not present, set newTarget to %Temporal.PlainTime%.\n    let new_target = if let Some(new_target) = new_target {\n        new_target.clone()\n    } else {\n        context\n            .realm()\n            .intrinsics()\n            .constructors()\n            .plain_time()\n            .constructor()\n            .into()\n    };\n\n    // 3. Let object be ? OrdinaryCreateFromConstructor(newTarget, \"%Temporal.PlainTime.prototype%\", « [[InitializedTemporalTime]], [[ISOHour]], [[ISOMinute]], [[ISOSecond]], [[ISOMillisecond]], [[ISOMicrosecond]], [[ISONanosecond]] »).\n    let prototype =\n        get_prototype_from_constructor(&new_target, StandardConstructors::plain_time, context)?;\n\n    // 4. Set object.[[ISOHour]] to hour.\n    // 5. Set object.[[ISOMinute]] to minute.\n    // 6. Set object.[[ISOSecond]] to second.\n    // 7. Set object.[[ISOMillisecond]] to millisecond.\n    // 8. Set object.[[ISOMicrosecond]] to microsecond.\n    // 9. Set object.[[ISONanosecond]] to nanosecond.\n    let obj = JsObject::from_proto_and_data(prototype, PlainTime { inner });\n\n    // 10. Return object.\n    Ok(obj)\n}\n\n/// 4.5.3 `ToTemporalTime ( item [ , overflow ] )`\npub(crate) fn to_temporal_time(\n    value: &JsValue,\n    options: Option<&JsValue>,\n    context: &mut Context,\n) -> JsResult<PlainTimeInner> {\n    // 1.If overflow is not present, set overflow to \"constrain\".\n    let binding = JsValue::undefined();\n    let options = options.unwrap_or(&binding);\n    // 2. If item is an Object, then\n    match value.variant() {\n        JsVariant::Object(object) => {\n            // a. If item has an [[InitializedTemporalTime]] internal slot, then\n            if let Some(time) = object.downcast_ref::<PlainTime>() {\n                // i. Return item.\n                let options = get_options_object(options)?;\n                let _overflow = get_option::<Overflow>(&options, js_string!(\"overflow\"), context)?;\n                return Ok(time.inner);\n            // b. If item has an [[InitializedTemporalZonedDateTime]] internal slot, then\n            } else if let Some(zdt) = object.downcast_ref::<ZonedDateTime>() {\n                // i. Let instant be ! CreateTemporalInstant(item.[[Nanoseconds]]).\n                // ii. Let timeZoneRec be ? CreateTimeZoneMethodsRecord(item.[[TimeZone]], « get-offset-nanoseconds-for »).\n                // iii. Let plainDateTime be ? GetPlainDateTimeFor(timeZoneRec, instant, item.[[Calendar]]).\n                // iv. Return ! CreateTemporalTime(plainDateTime.[[ISOHour]], plainDateTime.[[ISOMinute]],\n                // plainDateTime.[[ISOSecond]], plainDateTime.[[ISOMillisecond]], plainDateTime.[[ISOMicrosecond]],\n                // plainDateTime.[[ISONanosecond]]).\n                let options = get_options_object(options)?;\n                let _overflow = get_option::<Overflow>(&options, js_string!(\"overflow\"), context)?;\n                return Ok(zdt.inner.to_plain_time());\n            // c. If item has an [[InitializedTemporalDateTime]] internal slot, then\n            } else if let Some(dt) = object.downcast_ref::<PlainDateTime>() {\n                // i. Return ! CreateTemporalTime(item.[[ISOHour]], item.[[ISOMinute]],\n                // item.[[ISOSecond]], item.[[ISOMillisecond]], item.[[ISOMicrosecond]],\n                // item.[[ISONanosecond]]).\n                let options = get_options_object(options)?;\n                let _overflow = get_option::<Overflow>(&options, js_string!(\"overflow\"), context)?;\n                return Ok(PlainTimeInner::from(dt.inner.clone()));\n            }\n            // d. Let result be ? ToTemporalTimeRecord(item).\n            // e. Set result to ? RegulateTime(result.[[Hour]], result.[[Minute]],\n            // result.[[Second]], result.[[Millisecond]], result.[[Microsecond]],\n            // result.[[Nanosecond]], overflow).\n            let partial = to_js_partial_time_record(&object, context)?;\n\n            let options = get_options_object(options)?;\n            let overflow = get_option::<Overflow>(&options, js_string!(\"overflow\"), context)?;\n\n            PlainTimeInner::from_partial(partial.as_temporal_partial_time(overflow)?, overflow)\n                .map_err(Into::into)\n        }\n        // 3. Else,\n        JsVariant::String(str) => {\n            // b. Let result be ? ParseTemporalTimeString(item).\n            // c. Assert: IsValidTime(result.[[Hour]], result.[[Minute]], result.[[Second]], result.[[Millisecond]], result.[[Microsecond]], result.[[Nanosecond]]) is true.\n            let result = str.to_std_string_escaped().parse::<PlainTimeInner>()?;\n\n            let options = get_options_object(options)?;\n            let _overflow = get_option::<Overflow>(&options, js_string!(\"overflow\"), context)?;\n\n            Ok(result)\n        }\n        // a. If item is not a String, throw a TypeError exception.\n        _ => Err(JsNativeError::typ()\n            .with_message(\"Invalid value for converting to PlainTime.\")\n            .into()),\n    }\n\n    // 4. Return ! CreateTemporalTime(result.[[Hour]], result.[[Minute]], result.[[Second]], result.[[Millisecond]], result.[[Microsecond]], result.[[Nanosecond]]).\n}\n\n/// A `PartialTime` represents partially filled `Time` fields.\n#[derive(Debug, Default, Clone, Copy, PartialEq)]\npub struct JsPartialTime {\n    /// A potentially set `hour` field.\n    pub hour: Option<FiniteF64>,\n    /// A potentially set `minute` field.\n    pub minute: Option<FiniteF64>,\n    /// A potentially set `second` field.\n    pub second: Option<FiniteF64>,\n    /// A potentially set `millisecond` field.\n    pub millisecond: Option<FiniteF64>,\n    /// A potentially set `microsecond` field.\n    pub microsecond: Option<FiniteF64>,\n    /// A potentially set `nanosecond` field.\n    pub nanosecond: Option<FiniteF64>,\n}\n\nimpl JsPartialTime {\n    fn as_temporal_partial_time(&self, overflow: Option<Overflow>) -> JsResult<PartialTime> {\n        fn check(value: Option<FiniteF64>, typ: &'static str, max: u16) -> JsResult<()> {\n            if let Some(value) = value\n                && value.as_inner().is_sign_negative()\n            {\n                return Err(JsNativeError::range()\n                    .with_message(format!(\"time value '{typ}' not in 0..{max}: {value}\"))\n                    .into());\n            }\n            Ok(())\n        }\n\n        fn truncate<T>(value: Option<FiniteF64>) -> Option<T>\n        where\n            T: PrimInt + AsPrimitive<f64>,\n            f64: AsPrimitive<T>,\n        {\n            value\n                .as_ref()\n                .map(FiniteF64::as_integer_with_truncation::<T>)\n        }\n\n        if overflow == Some(Overflow::Reject) {\n            check(self.hour, \"hour\", 23)?;\n            check(self.minute, \"minute\", 59)?;\n            check(self.second, \"second\", 59)?;\n            check(self.millisecond, \"millisecond\", 999)?;\n            check(self.microsecond, \"microsecond\", 999)?;\n            check(self.nanosecond, \"nanosecond\", 999)?;\n        }\n\n        Ok(PartialTime::new()\n            .with_hour(truncate(self.hour))\n            .with_minute(truncate(self.minute))\n            .with_second(truncate(self.second))\n            .with_millisecond(truncate(self.millisecond))\n            .with_microsecond(truncate(self.microsecond))\n            .with_nanosecond(truncate(self.nanosecond)))\n    }\n}\n\npub(crate) fn to_js_partial_time_record(\n    partial_object: &JsObject,\n    context: &mut Context,\n) -> JsResult<JsPartialTime> {\n    let hour = partial_object\n        .get(js_string!(\"hour\"), context)?\n        .map(|v| v.to_finitef64(context))\n        .transpose()?;\n\n    let microsecond = partial_object\n        .get(js_string!(\"microsecond\"), context)?\n        .map(|v| v.to_finitef64(context))\n        .transpose()?;\n\n    let millisecond = partial_object\n        .get(js_string!(\"millisecond\"), context)?\n        .map(|v| v.to_finitef64(context))\n        .transpose()?;\n\n    let minute = partial_object\n        .get(js_string!(\"minute\"), context)?\n        .map(|v| v.to_finitef64(context))\n        .transpose()?;\n\n    let nanosecond = partial_object\n        .get(js_string!(\"nanosecond\"), context)?\n        .map(|v| v.to_finitef64(context))\n        .transpose()?;\n\n    let second = partial_object\n        .get(js_string!(\"second\"), context)?\n        .map(|v| v.to_finitef64(context))\n        .transpose()?;\n\n    Ok(JsPartialTime {\n        hour,\n        minute,\n        second,\n        millisecond,\n        microsecond,\n        nanosecond,\n    })\n}\n"
  },
  {
    "path": "core/engine/src/builtins/temporal/plain_time/tests.rs",
    "content": "use indoc::indoc;\n\nuse crate::{JsNativeErrorKind, TestAction, run_test_actions};\n\n#[test]\nfn with_overflow_reject_throws_on_negative_components() {\n    // Temporal Builtin tests.\n    run_test_actions([\n        TestAction::run(indoc! {\"\n            let plain = new Temporal.PlainTime();\n            let options = {overflow: 'reject'};\n        \"}),\n        TestAction::assert_native_error(\n            \"plain.with({hour: -1}, options)\",\n            JsNativeErrorKind::Range,\n            \"time value 'hour' not in 0..23: -1\",\n        ),\n        TestAction::assert_native_error(\n            \"plain.with({minute: -1}, options)\",\n            JsNativeErrorKind::Range,\n            \"time value 'minute' not in 0..59: -1\",\n        ),\n        TestAction::assert_native_error(\n            \"plain.with({second: -1}, options)\",\n            JsNativeErrorKind::Range,\n            \"time value 'second' not in 0..59: -1\",\n        ),\n        TestAction::assert_native_error(\n            \"plain.with({millisecond: -1}, options)\",\n            JsNativeErrorKind::Range,\n            \"time value 'millisecond' not in 0..999: -1\",\n        ),\n        TestAction::assert_native_error(\n            \"plain.with({microsecond: -1}, options)\",\n            JsNativeErrorKind::Range,\n            \"time value 'microsecond' not in 0..999: -1\",\n        ),\n        TestAction::assert_native_error(\n            \"plain.with({nanosecond: -1}, options)\",\n            JsNativeErrorKind::Range,\n            \"time value 'nanosecond' not in 0..999: -1\",\n        ),\n    ]);\n}\n"
  },
  {
    "path": "core/engine/src/builtins/temporal/plain_year_month/mod.rs",
    "content": "//! Boa's implementation of the `Temporal.PlainYearMonth` built-in object.\n\nuse std::str::FromStr;\n\nuse crate::{\n    Context, JsArgs, JsData, JsError, JsNativeError, JsObject, JsResult, JsString, JsSymbol,\n    JsValue,\n    builtins::{\n        BuiltInBuilder, BuiltInConstructor, BuiltInObject, IntrinsicObject,\n        options::{get_option, get_options_object},\n    },\n    context::intrinsics::{Intrinsics, StandardConstructor, StandardConstructors},\n    js_string,\n    object::internal_methods::get_prototype_from_constructor,\n    property::Attribute,\n    realm::Realm,\n    string::StaticJsStrings,\n    value::IntoOrUndefined,\n};\nuse boa_gc::{Finalize, Trace};\n\nuse icu_calendar::AnyCalendarKind;\nuse temporal_rs::{\n    Calendar, Duration, MonthCode, PlainYearMonth as InnerYearMonth, TinyAsciiStr,\n    fields::{CalendarFields, YearMonthCalendarFields},\n    options::{DisplayCalendar, Overflow},\n    partial::PartialYearMonth,\n};\n\nuse super::{\n    DateTimeValues, calendar::get_temporal_calendar_slot_value_with_default, create_temporal_date,\n    create_temporal_duration, is_partial_temporal_object, options::get_difference_settings,\n    to_temporal_duration,\n};\n\n/// The `Temporal.PlainYearMonth` built-in implementation\n///\n/// More information:\n///\n/// - [ECMAScript Temporal proposal][spec]\n/// - [MDN reference][mdn]\n/// - [`temporal_rs` documentation][temporal_rs-docs]\n///\n/// [spec]: https://tc39.es/proposal-temporal/#sec-temporal-plainyearmonth-objects\n/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Temporal/PlainYearMonth\n/// [temporal_rs-docs]: https://docs.rs/temporal_rs/latest/temporal_rs/struct.PlainYearMonth.html\n#[derive(Debug, Clone, Trace, Finalize, JsData)]\n#[boa_gc(unsafe_empty_trace)]\npub struct PlainYearMonth {\n    pub(crate) inner: InnerYearMonth,\n}\n\nimpl PlainYearMonth {\n    pub(crate) fn new(inner: InnerYearMonth) -> Self {\n        Self { inner }\n    }\n}\n\nimpl BuiltInObject for PlainYearMonth {\n    const NAME: JsString = StaticJsStrings::PLAIN_YM_NAME;\n}\n\nimpl IntrinsicObject for PlainYearMonth {\n    fn init(realm: &Realm) {\n        let get_calendar_id = BuiltInBuilder::callable(realm, Self::get_calendar_id)\n            .name(js_string!(\"get calendarId\"))\n            .build();\n\n        let get_era_year = BuiltInBuilder::callable(realm, Self::get_era_year)\n            .name(js_string!(\"get eraYear\"))\n            .build();\n\n        let get_era = BuiltInBuilder::callable(realm, Self::get_era)\n            .name(js_string!(\"get era\"))\n            .build();\n\n        let get_year = BuiltInBuilder::callable(realm, Self::get_year)\n            .name(js_string!(\"get year\"))\n            .build();\n\n        let get_month = BuiltInBuilder::callable(realm, Self::get_month)\n            .name(js_string!(\"get month\"))\n            .build();\n\n        let get_month_code = BuiltInBuilder::callable(realm, Self::get_month_code)\n            .name(js_string!(\"get monthCode\"))\n            .build();\n\n        let get_days_in_month = BuiltInBuilder::callable(realm, Self::get_days_in_month)\n            .name(js_string!(\"get daysInMonth\"))\n            .build();\n\n        let get_days_in_year = BuiltInBuilder::callable(realm, Self::get_days_in_year)\n            .name(js_string!(\"get daysInYear\"))\n            .build();\n\n        let get_months_in_year = BuiltInBuilder::callable(realm, Self::get_months_in_year)\n            .name(js_string!(\"get monthsInYear\"))\n            .build();\n\n        let get_in_leap_year = BuiltInBuilder::callable(realm, Self::get_in_leap_year)\n            .name(js_string!(\"get inLeapYear\"))\n            .build();\n\n        BuiltInBuilder::from_standard_constructor::<Self>(realm)\n            .property(\n                JsSymbol::to_string_tag(),\n                StaticJsStrings::PLAIN_YM_TAG,\n                Attribute::CONFIGURABLE,\n            )\n            .accessor(\n                js_string!(\"calendarId\"),\n                Some(get_calendar_id),\n                None,\n                Attribute::CONFIGURABLE,\n            )\n            .accessor(\n                js_string!(\"era\"),\n                Some(get_era),\n                None,\n                Attribute::CONFIGURABLE,\n            )\n            .accessor(\n                js_string!(\"eraYear\"),\n                Some(get_era_year),\n                None,\n                Attribute::CONFIGURABLE,\n            )\n            .accessor(\n                js_string!(\"year\"),\n                Some(get_year),\n                None,\n                Attribute::CONFIGURABLE,\n            )\n            .accessor(\n                js_string!(\"month\"),\n                Some(get_month),\n                None,\n                Attribute::CONFIGURABLE,\n            )\n            .accessor(\n                js_string!(\"monthCode\"),\n                Some(get_month_code),\n                None,\n                Attribute::CONFIGURABLE,\n            )\n            .accessor(\n                js_string!(\"daysInMonth\"),\n                Some(get_days_in_month),\n                None,\n                Attribute::CONFIGURABLE,\n            )\n            .accessor(\n                js_string!(\"daysInYear\"),\n                Some(get_days_in_year),\n                None,\n                Attribute::CONFIGURABLE,\n            )\n            .accessor(\n                js_string!(\"monthsInYear\"),\n                Some(get_months_in_year),\n                None,\n                Attribute::CONFIGURABLE,\n            )\n            .accessor(\n                js_string!(\"inLeapYear\"),\n                Some(get_in_leap_year),\n                None,\n                Attribute::CONFIGURABLE,\n            )\n            .static_method(Self::from, js_string!(\"from\"), 1)\n            .static_method(Self::compare, js_string!(\"compare\"), 2)\n            .method(Self::with, js_string!(\"with\"), 1)\n            .method(Self::add, js_string!(\"add\"), 1)\n            .method(Self::subtract, js_string!(\"subtract\"), 1)\n            .method(Self::until, js_string!(\"until\"), 1)\n            .method(Self::since, js_string!(\"since\"), 1)\n            .method(Self::equals, js_string!(\"equals\"), 1)\n            .method(Self::to_string, js_string!(\"toString\"), 0)\n            .method(Self::to_locale_string, js_string!(\"toLocaleString\"), 0)\n            .method(Self::to_json, js_string!(\"toJSON\"), 0)\n            .method(Self::value_of, js_string!(\"valueOf\"), 0)\n            .method(Self::to_plain_date, js_string!(\"toPlainDate\"), 1)\n            .build();\n    }\n\n    fn get(intrinsics: &Intrinsics) -> JsObject {\n        Self::STANDARD_CONSTRUCTOR(intrinsics.constructors()).constructor()\n    }\n}\n\nimpl BuiltInConstructor for PlainYearMonth {\n    const CONSTRUCTOR_ARGUMENTS: usize = 2;\n    const PROTOTYPE_STORAGE_SLOTS: usize = 32;\n    const CONSTRUCTOR_STORAGE_SLOTS: usize = 2;\n\n    const STANDARD_CONSTRUCTOR: fn(&StandardConstructors) -> &StandardConstructor =\n        StandardConstructors::plain_year_month;\n\n    fn constructor(\n        new_target: &JsValue,\n        args: &[JsValue],\n        context: &mut Context,\n    ) -> JsResult<JsValue> {\n        // 1. If NewTarget is undefined, then\n        if new_target.is_undefined() {\n            // a. Throw a TypeError exception.\n            return Err(JsNativeError::typ()\n                .with_message(\"NewTarget cannot be undefined when constructing a PlainYearMonth.\")\n                .into());\n        }\n\n        // 2. If referenceISODay is undefined, then\n        // a. Set referenceISODay to 1𝔽.\n        // 3. Let y be ? ToIntegerWithTruncation(isoYear).\n        let y = args\n            .get_or_undefined(0)\n            .to_finitef64(context)?\n            .as_integer_with_truncation::<i32>();\n\n        // 4. Let m be ? ToIntegerWithTruncation(isoMonth).\n        let m = args\n            .get_or_undefined(1)\n            .to_finitef64(context)?\n            .as_integer_with_truncation::<u8>();\n\n        // 5. Let calendar be ? ToTemporalCalendarSlotValue(calendarLike, \"iso8601\").\n        let calendar = args\n            .get_or_undefined(2)\n            .map(|s| {\n                s.as_string()\n                    .as_ref()\n                    .map(JsString::to_std_string_lossy)\n                    .ok_or_else(|| JsNativeError::typ().with_message(\"calendar must be a string.\"))\n            })\n            .transpose()?\n            .map(|s| Calendar::try_from_utf8(s.as_bytes()))\n            .transpose()?\n            .unwrap_or_default();\n\n        // 6. Let ref be ? ToIntegerWithTruncation(referenceISODay).\n        let ref_day = args\n            .get_or_undefined(3)\n            .map(|v| {\n                let finite = v.to_finitef64(context)?;\n                Ok::<u8, JsError>(finite.as_integer_with_truncation::<u8>())\n            })\n            .transpose()?;\n\n        // 7. Return ? CreateTemporalYearMonth(y, m, calendar, ref, NewTarget).\n        let inner = InnerYearMonth::new_with_overflow(y, m, ref_day, calendar, Overflow::Reject)?;\n\n        create_temporal_year_month(inner, Some(new_target), context)\n    }\n}\n\n// ==== `Temporal.PlainYearMonth` static methods implementation ====\n\nimpl PlainYearMonth {\n    /// 9.2.2 `Temporal.PlainYearMonth.from ( item [ , options ] )`\n    ///\n    /// More information:\n    ///\n    /// - [ECMAScript Temporal proposal][spec]\n    /// - [MDN reference][mdn]\n    ///\n    /// [spec]: https://tc39.es/proposal-temporal/#sec-temporal.plainyearmonth.from\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Temporal/PlainYearMonth/from\n    fn from(_: &JsValue, args: &[JsValue], context: &mut Context) -> JsResult<JsValue> {\n        // 1. Return ? ToTemporalYearMonth(item, options).\n        let item = args.get_or_undefined(0);\n        let options = args.get_or_undefined(1);\n        let inner = to_temporal_year_month(item, Some(options.clone()), context)?;\n        create_temporal_year_month(inner, None, context)\n    }\n\n    /// 9.2.3 `Temporal.PlainYearMonth.compare ( one, two )`\n    ///\n    /// More information:\n    ///\n    /// - [ECMAScript Temporal proposal][spec]\n    /// - [MDN reference][mdn]\n    /// - [`temporal_rs` documentation][temporal_rs-docs]\n    ///\n    /// [spec]: https://tc39.es/proposal-temporal/#sec-temporal.plainyearmonth.compare\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Temporal/PlainYearMonth/compare\n    /// [temporal_rs-docs]: https://docs.rs/temporal_rs/latest/temporal_rs/struct.PlainYearMonth.html#method.compare_iso\n    fn compare(_: &JsValue, args: &[JsValue], context: &mut Context) -> JsResult<JsValue> {\n        let one = to_temporal_year_month(args.get_or_undefined(0), None, context)?;\n        let two = to_temporal_year_month(args.get_or_undefined(1), None, context)?;\n        Ok((one.compare_iso(&two) as i8).into())\n    }\n}\n\n// ==== `PlainYearMonth` accessors implementation ====\n\nimpl PlainYearMonth {\n    // Helper for retrieving internal fields\n    fn get_internal_field(this: &JsValue, field: &DateTimeValues) -> JsResult<JsValue> {\n        let object = this.as_object();\n        let year_month = object\n            .as_ref()\n            .and_then(JsObject::downcast_ref::<Self>)\n            .ok_or_else(|| {\n                JsNativeError::typ().with_message(\"this value must be a PlainYearMonth object.\")\n            })?;\n        let inner = &year_month.inner;\n        match field {\n            DateTimeValues::Year => Ok(inner.year().into()),\n            DateTimeValues::Month => Ok(inner.month().into()),\n            DateTimeValues::MonthCode => {\n                Ok(JsString::from(InnerYearMonth::month_code(inner).as_str()).into())\n            }\n            _ => unreachable!(),\n        }\n    }\n\n    /// 9.3.3 get `Temporal.PlainYearMonth.prototype.calendarId`\n    ///\n    /// More information:\n    ///\n    /// - [ECMAScript Temporal proposal][spec]\n    /// - [MDN reference][mdn]\n    /// - [`temporal_rs` documentation][temporal_rs-docs]\n    ///\n    /// [spec]: https://tc39.es/proposal-temporal/#sec-get-temporal.plainyearmonth.calendarid\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Temporal/PlainYearMonth/calendarId\n    /// [temporal_rs-docs]: https://docs.rs/temporal_rs/latest/temporal_rs/struct.PlainYearMonth.html#method.calendar\n    fn get_calendar_id(this: &JsValue, _: &[JsValue], _: &mut Context) -> JsResult<JsValue> {\n        let obj = this\n            .as_object()\n            .ok_or_else(|| JsNativeError::typ().with_message(\"this must be an object.\"))?;\n\n        let Ok(year_month) = obj.clone().downcast::<Self>() else {\n            return Err(JsNativeError::typ()\n                .with_message(\"the this object must be a PlainYearMonth object.\")\n                .into());\n        };\n\n        let calendar = year_month.borrow().data().inner.calendar().clone();\n        Ok(js_string!(calendar.identifier()).into())\n    }\n\n    /// 9.3.4 get `Temporal.PlainYearMonth.prototype.era`\n    ///\n    /// More information:\n    ///\n    /// - [ECMAScript Temporal proposal][spec]\n    /// - [MDN reference][mdn]\n    /// - [`temporal_rs` documentation][temporal_rs-docs]\n    ///\n    /// [spec]: https://tc39.es/proposal-temporal/#sec-get-temporal.plainyearmonth.era\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Temporal/PlainYearMonth/era\n    /// [temporal_rs-docs]: https://docs.rs/temporal_rs/latest/temporal_rs/struct.PlainYearMonth.html#method.era\n    fn get_era(this: &JsValue, _: &[JsValue], _: &mut Context) -> JsResult<JsValue> {\n        let object = this.as_object();\n        let year_month = object\n            .as_ref()\n            .and_then(JsObject::downcast_ref::<Self>)\n            .ok_or_else(|| {\n                JsNativeError::typ().with_message(\"this value must be a PlainYearMonth object.\")\n            })?;\n\n        Ok(year_month\n            .inner\n            .era()\n            .map(|s| JsString::from(s.as_str()))\n            .into_or_undefined())\n    }\n\n    /// 9.3.5 get `Temporal.PlainYearMonth.prototype.eraYear`\n    ///\n    /// More information:\n    ///\n    /// - [ECMAScript Temporal proposal][spec]\n    /// - [MDN reference][mdn]\n    /// - [`temporal_rs` documentation][temporal_rs-docs]\n    ///\n    /// [spec]: https://tc39.es/proposal-temporal/#sec-get-temporal.plainyearmonth.erayear\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Temporal/PlainYearMonth/eraYear\n    /// [temporal_rs-docs]: https://docs.rs/temporal_rs/latest/temporal_rs/struct.PlainYearMonth.html#method.era_year\n    fn get_era_year(this: &JsValue, _: &[JsValue], _: &mut Context) -> JsResult<JsValue> {\n        let object = this.as_object();\n        let year_month = object\n            .as_ref()\n            .and_then(JsObject::downcast_ref::<Self>)\n            .ok_or_else(|| {\n                JsNativeError::typ().with_message(\"this value must be a PlainYearMonth object.\")\n            })?;\n        Ok(year_month.inner.era_year().into_or_undefined())\n    }\n\n    /// 9.3.6 get `Temporal.PlainYearMonth.prototype.year`\n    ///\n    /// More information:\n    ///\n    /// - [ECMAScript Temporal proposal][spec]\n    /// - [MDN reference][mdn]\n    /// - [`temporal_rs` documentation][temporal_rs-docs]\n    ///\n    /// [spec]: https://tc39.es/proposal-temporal/#sec-get-temporal.plainyearmonth.year\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Temporal/PlainYearMonth/year\n    /// [temporal_rs-docs]: https://docs.rs/temporal_rs/latest/temporal_rs/struct.PlainYearMonth.html#method.year\n    fn get_year(this: &JsValue, _: &[JsValue], _: &mut Context) -> JsResult<JsValue> {\n        Self::get_internal_field(this, &DateTimeValues::Year)\n    }\n\n    /// 9.3.7 get `Temporal.PlainYearMonth.prototype.month`\n    ///\n    /// More information:\n    ///\n    /// - [ECMAScript Temporal proposal][spec]\n    /// - [MDN reference][mdn]\n    /// - [`temporal_rs` documentation][temporal_rs-docs]\n    ///\n    /// [spec]: https://tc39.es/proposal-temporal/#sec-get-temporal.plainyearmonth.month\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Temporal/PlainYearMonth/month\n    /// [temporal_rs-docs]: https://docs.rs/temporal_rs/latest/temporal_rs/struct.PlainYearMonth.html#method.month\n    fn get_month(this: &JsValue, _: &[JsValue], _: &mut Context) -> JsResult<JsValue> {\n        Self::get_internal_field(this, &DateTimeValues::Month)\n    }\n\n    /// 9.3.8 get `Temporal.PlainYearMonth.prototype.monthCode`\n    ///\n    /// More information:\n    ///\n    /// - [ECMAScript Temporal proposal][spec]\n    /// - [MDN reference][mdn]\n    /// - [`temporal_rs` documentation][temporal_rs-docs]\n    ///\n    /// [spec]: https://tc39.es/proposal-temporal/#sec-get-temporal.plainyearmonth.monthcode\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Temporal/PlainYearMonth/monthCode\n    /// [temporal_rs-docs]: https://docs.rs/temporal_rs/latest/temporal_rs/struct.PlainYearMonth.html#method.month_code\n    fn get_month_code(this: &JsValue, _: &[JsValue], _: &mut Context) -> JsResult<JsValue> {\n        Self::get_internal_field(this, &DateTimeValues::MonthCode)\n    }\n\n    /// 9.3.9 get `Temporal.PlainYearMonth.prototype.daysInYear`\n    ///\n    /// More information:\n    ///\n    /// - [ECMAScript Temporal proposal][spec]\n    /// - [MDN reference][mdn]\n    /// - [`temporal_rs` documentation][temporal_rs-docs]\n    ///\n    /// [spec]: https://tc39.es/proposal-temporal/#sec-get-temporal.plainyearmonth.daysinyear\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Temporal/PlainYearMonth/daysInYear\n    /// [temporal_rs-docs]: https://docs.rs/temporal_rs/latest/temporal_rs/struct.PlainYearMonth.html#method.days_in_year\n    fn get_days_in_year(this: &JsValue, _: &[JsValue], _: &mut Context) -> JsResult<JsValue> {\n        let object = this.as_object();\n        let year_month = object\n            .as_ref()\n            .and_then(JsObject::downcast_ref::<Self>)\n            .ok_or_else(|| {\n                JsNativeError::typ().with_message(\"this value must be a PlainYearMonth object.\")\n            })?;\n        let inner = &year_month.inner;\n        Ok(inner.days_in_year().into())\n    }\n\n    /// 9.3.10 get `Temporal.PlainYearMonth.prototype.daysInMonth`\n    ///\n    /// More information:\n    ///\n    /// - [ECMAScript Temporal proposal][spec]\n    /// - [MDN reference][mdn]\n    /// - [`temporal_rs` documentation][temporal_rs-docs]\n    ///\n    /// [spec]: https://tc39.es/proposal-temporal/#sec-get-temporal.plainyearmonth.daysinmonth\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Temporal/PlainYearMonth/daysInMonth\n    /// [temporal_rs-docs]: https://docs.rs/temporal_rs/latest/temporal_rs/struct.PlainYearMonth.html#method.days_in_month\n    fn get_days_in_month(this: &JsValue, _: &[JsValue], _: &mut Context) -> JsResult<JsValue> {\n        let object = this.as_object();\n        let year_month = object\n            .as_ref()\n            .and_then(JsObject::downcast_ref::<Self>)\n            .ok_or_else(|| {\n                JsNativeError::typ().with_message(\"this value must be a PlainYearMonth object.\")\n            })?;\n        let inner = &year_month.inner;\n        Ok(inner.days_in_month().into())\n    }\n\n    /// 9.3.11 get `Temporal.PlainYearMonth.prototype.monthsInYear`\n    ///\n    /// More information:\n    ///\n    /// - [ECMAScript Temporal proposal][spec]\n    /// - [MDN reference][mdn]\n    /// - [`temporal_rs` documentation][temporal_rs-docs]\n    ///\n    /// [spec]: https://tc39.es/proposal-temporal/#sec-get-temporal.plainyearmonth.monthsinyear\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Temporal/PlainYearMonth/monthsInYear\n    /// [temporal_rs-docs]: https://docs.rs/temporal_rs/latest/temporal_rs/struct.PlainYearMonth.html#method.months_in_year\n    fn get_months_in_year(this: &JsValue, _: &[JsValue], _: &mut Context) -> JsResult<JsValue> {\n        let object = this.as_object();\n        let year_month = object\n            .as_ref()\n            .and_then(JsObject::downcast_ref::<Self>)\n            .ok_or_else(|| {\n                JsNativeError::typ().with_message(\"this value must be a PlainYearMonth object.\")\n            })?;\n        let inner = &year_month.inner;\n        Ok(inner.months_in_year().into())\n    }\n\n    /// 9.3.12 get `Temporal.PlainYearMonth.prototype.inLeapYear`\n    ///\n    /// More information:\n    ///\n    /// - [ECMAScript Temporal proposal][spec]\n    /// - [MDN reference][mdn]\n    /// - [`temporal_rs` documentation][temporal_rs-docs]\n    ///\n    /// [spec]: https://tc39.es/proposal-temporal/#sec-get-temporal.plainyearmonth.inleapyear\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Temporal/PlainYearMonth/inLeapYear\n    /// [temporal_rs-docs]: https://docs.rs/temporal_rs/latest/temporal_rs/struct.PlainYearMonth.html#method.in_leap_year\n    fn get_in_leap_year(this: &JsValue, _: &[JsValue], _: &mut Context) -> JsResult<JsValue> {\n        let object = this.as_object();\n        let year_month = object\n            .as_ref()\n            .and_then(JsObject::downcast_ref::<Self>)\n            .ok_or_else(|| {\n                JsNativeError::typ().with_message(\"this value must be a PlainYearMonth object.\")\n            })?;\n\n        Ok(year_month.inner.in_leap_year().into())\n    }\n}\n\n// ==== `PlainYearMonth` method implementations ====\n\nimpl PlainYearMonth {\n    /// 9.3.13 `Temporal.PlainYearMonth.prototype.with ( temporalYearMonthLike [ , options ] )`\n    ///\n    /// More information:\n    ///\n    /// - [ECMAScript Temporal proposal][spec]\n    /// - [MDN reference][mdn]\n    /// - [`temporal_rs` documentation][temporal_rs-docs]\n    ///\n    /// [spec]: https://tc39.es/proposal-temporal/#sec-temporal.plainyearmonth.with\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Temporal/PlainYearMonth/with\n    /// [temporal_rs-docs]: https://docs.rs/temporal_rs/latest/temporal_rs/struct.PlainYearMonth.html#method.with\n    fn with(this: &JsValue, args: &[JsValue], context: &mut Context) -> JsResult<JsValue> {\n        // 1. Let yearMonth be the this value.\n        // 2. Perform ? RequireInternalSlot(yearMonth, [[InitializedTemporalYearMonth]]).\n        let object = this.as_object();\n        let year_month = object\n            .as_ref()\n            .and_then(JsObject::downcast_ref::<Self>)\n            .ok_or_else(|| {\n                JsNativeError::typ().with_message(\"this value must be a PlainYearMonth object.\")\n            })?;\n\n        // 3. If ? IsPartialTemporalObject(temporalYearMonthLike) is false, throw a TypeError exception.\n        let Some(obj) = is_partial_temporal_object(args.get_or_undefined(0), context)? else {\n            return Err(JsNativeError::typ()\n                .with_message(\"temporalYearMonthLike was not a partial object\")\n                .into());\n        };\n        // 4. Let calendar be yearMonth.[[Calendar]].\n        // 5. Let fields be ISODateToFields(calendar, yearMonth.[[ISODate]], year-month).\n        // 6. Let partialYearMonth be ? PrepareCalendarFields(calendar, temporalYearMonthLike, « year, month, month-code », « », partial).\n        // 7. Set fields to CalendarMergeFields(calendar, fields, partialYearMonth).\n        let fields = to_year_month_calendar_fields(&obj, year_month.inner.calendar(), context)?;\n        // NOTE: Update temporal_rs to handle this.\n        if fields.is_empty() {\n            return Err(JsNativeError::typ()\n                .with_message(\"temporalYearMonthLike cannot be an empty object\")\n                .into());\n        }\n        // 8. Let resolvedOptions be ? GetOptionsObject(options).\n        let resolved_options = get_options_object(args.get_or_undefined(1))?;\n        // 9. Let overflow be ? GetTemporalOverflowOption(resolvedOptions).\n        let overflow = get_option::<Overflow>(&resolved_options, js_string!(\"overflow\"), context)?\n            .unwrap_or_default();\n        // 10. Let isoDate be ? CalendarYearMonthFromFields(calendar, fields, overflow).\n        let result = year_month.inner.with(fields, Some(overflow))?;\n        // 11. Return ! CreateTemporalYearMonth(isoDate, calendar).\n        create_temporal_year_month(result, None, context)\n    }\n\n    /// 9.3.14 `Temporal.PlainYearMonth.prototype.add ( temporalDurationLike [ , options ] )`\n    ///\n    /// More information:\n    ///\n    /// - [ECMAScript Temporal proposal][spec]\n    /// - [MDN reference][mdn]\n    /// - [`temporal_rs` documentation][temporal_rs-docs]\n    ///\n    /// [spec]: https://tc39.es/proposal-temporal/#sec-temporal.plainyearmonth.add\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Temporal/PlainYearMonth/add\n    /// [temporal_rs-docs]: https://docs.rs/temporal_rs/latest/temporal_rs/struct.PlainYearMonth.html#method.add\n    fn add(this: &JsValue, args: &[JsValue], context: &mut Context) -> JsResult<JsValue> {\n        let duration_like = args.get_or_undefined(0);\n        let options = get_options_object(args.get_or_undefined(1))?;\n\n        add_or_subtract_duration(true, this, duration_like, &options, context)\n    }\n\n    /// 9.3.15 `Temporal.PlainYearMonth.prototype.subtract ( temporalDurationLike [ , options ] )`\n    ///\n    /// More information:\n    ///\n    /// - [ECMAScript Temporal proposal][spec]\n    /// - [MDN reference][mdn]\n    /// - [`temporal_rs` documentation][temporal_rs-docs]\n    ///\n    /// [spec]: https://tc39.es/proposal-temporal/#sec-temporal.plainyearmonth.subtract\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Temporal/PlainYearMonth/subtract\n    /// [temporal_rs-docs]: https://docs.rs/temporal_rs/latest/temporal_rs/struct.PlainYearMonth.html#method.subtract\n    fn subtract(this: &JsValue, args: &[JsValue], context: &mut Context) -> JsResult<JsValue> {\n        let duration_like = args.get_or_undefined(0);\n        let options = get_options_object(args.get_or_undefined(1))?;\n\n        add_or_subtract_duration(false, this, duration_like, &options, context)\n    }\n\n    /// 9.3.16 `Temporal.PlainYearMonth.prototype.until ( other [ , options ] )`\n    ///\n    /// More information:\n    ///\n    /// - [ECMAScript Temporal proposal][spec]\n    /// - [MDN reference][mdn]\n    /// - [`temporal_rs` documentation][temporal_rs-docs]\n    ///\n    /// [spec]: https://tc39.es/proposal-temporal/#sec-temporal.plainyearmonth.until\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Temporal/PlainYearMonth/until\n    /// [temporal_rs-docs]: https://docs.rs/temporal_rs/latest/temporal_rs/struct.PlainYearMonth.html#method.until\n    fn until(this: &JsValue, args: &[JsValue], context: &mut Context) -> JsResult<JsValue> {\n        let object = this.as_object();\n        let year_month = object\n            .as_ref()\n            .and_then(JsObject::downcast_ref::<Self>)\n            .ok_or_else(|| {\n                JsNativeError::typ().with_message(\"this value must be a PlainYearMonth object.\")\n            })?;\n\n        let other = to_temporal_year_month(args.get_or_undefined(0), None, context)?;\n\n        if year_month.inner.calendar() != other.calendar() {\n            return Err(JsNativeError::range()\n                .with_message(\"calendars are not the same.\")\n                .into());\n        }\n\n        let resolved_options = get_options_object(args.get_or_undefined(1))?;\n        // TODO: Disallowed units must be rejected in `temporal_rs`.\n        let settings = get_difference_settings(&resolved_options, context)?;\n        let result = year_month.inner.until(&other, settings)?;\n        create_temporal_duration(result, None, context).map(Into::into)\n    }\n\n    /// 9.3.17 `Temporal.PlainYearMonth.prototype.since ( other [ , options ] )`\n    ///\n    /// More information:\n    ///\n    /// - [ECMAScript Temporal proposal][spec]\n    /// - [MDN reference][mdn]\n    /// - [`temporal_rs` documentation][temporal_rs-docs]\n    ///\n    /// [spec]: https://tc39.es/proposal-temporal/#sec-temporal.plainyearmonth.since\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Temporal/PlainYearMonth/since\n    /// [temporal_rs-docs]: https://docs.rs/temporal_rs/latest/temporal_rs/struct.PlainYearMonth.html#method.since\n    fn since(this: &JsValue, args: &[JsValue], context: &mut Context) -> JsResult<JsValue> {\n        let object = this.as_object();\n        let year_month = object\n            .as_ref()\n            .and_then(JsObject::downcast_ref::<Self>)\n            .ok_or_else(|| {\n                JsNativeError::typ().with_message(\"this value must be a PlainYearMonth object.\")\n            })?;\n\n        let other = to_temporal_year_month(args.get_or_undefined(0), None, context)?;\n\n        if year_month.inner.calendar() != other.calendar() {\n            return Err(JsNativeError::range()\n                .with_message(\"calendars are not the same.\")\n                .into());\n        }\n\n        let resolved_options = get_options_object(args.get_or_undefined(1))?;\n        // TODO: Disallowed units must be rejected in `temporal_rs`.\n        let settings = get_difference_settings(&resolved_options, context)?;\n        let result = year_month.inner.since(&other, settings)?;\n        create_temporal_duration(result, None, context).map(Into::into)\n    }\n\n    /// 9.3.18 `Temporal.PlainYearMonth.prototype.equals ( other )`\n    ///\n    /// More information:\n    ///\n    /// - [ECMAScript Temporal proposal][spec]\n    /// - [MDN reference][mdn]\n    /// - [`temporal_rs` documentation][temporal_rs-docs]\n    ///\n    /// [spec]: https://tc39.es/proposal-temporal/#sec-temporal.plainyearmonth.equals\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Temporal/PlainYearMonth/equals\n    /// [temporal_rs-docs]: https://docs.rs/temporal_rs/latest/temporal_rs/struct.PlainYearMonth.html#impl-PartialEq-for-PlainYearMonth\n    fn equals(this: &JsValue, args: &[JsValue], context: &mut Context) -> JsResult<JsValue> {\n        let object = this.as_object();\n        let year_month = object\n            .as_ref()\n            .and_then(JsObject::downcast_ref::<Self>)\n            .ok_or_else(|| {\n                JsNativeError::typ().with_message(\"this value must be a PlainYearMonth object.\")\n            })?;\n\n        let other = to_temporal_year_month(args.get_or_undefined(0), None, context)?;\n\n        Ok((year_month.inner == other).into())\n    }\n\n    /// 9.3.19 `Temporal.PlainYearMonth.prototype.toString ( [ options ] )`\n    ///\n    /// More information:\n    ///\n    /// - [ECMAScript Temporal proposal][spec]\n    /// - [MDN reference][mdn]\n    /// - [`temporal_rs` documentation][temporal_rs-docs]\n    ///\n    /// [spec]: https://tc39.es/proposal-temporal/#sec-temporal.plainyearmonth.tostring\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Temporal/PlainYearMonth/toString\n    /// [temporal_rs-docs]: https://docs.rs/temporal_rs/latest/temporal_rs/struct.PlainYearMonth.html#method.to_ixdtf_string\n    fn to_string(this: &JsValue, args: &[JsValue], context: &mut Context) -> JsResult<JsValue> {\n        // 1. Let YearMonth be the this value.\n        // 2. Perform ? RequireInternalSlot(yearMonth, [[InitializedTemporalYearMonth]]).\n        let object = this.as_object();\n        let year_month = object\n            .as_ref()\n            .and_then(JsObject::downcast_ref::<Self>)\n            .ok_or_else(|| {\n                JsNativeError::typ().with_message(\"this value must be a PlainYearMonth object.\")\n            })?;\n\n        // 3. Set options to ? NormalizeOptionsObject(options).\n        let options = get_options_object(args.get_or_undefined(0))?;\n        // 4. Let showCalendar be ? ToShowCalendarOption(options).\n        // Get calendarName from the options object\n        let show_calendar =\n            get_option::<DisplayCalendar>(&options, js_string!(\"calendarName\"), context)?\n                .unwrap_or(DisplayCalendar::Auto);\n\n        let ixdtf = year_month.inner.to_ixdtf_string(show_calendar);\n        Ok(JsString::from(ixdtf).into())\n    }\n\n    /// 9.3.20 `Temporal.PlainYearMonth.prototype.toLocaleString ( [ locales [ , options ] ] )`\n    ///\n    /// More information:\n    ///\n    /// - [ECMAScript Temporal proposal][spec]\n    /// - [MDN reference][mdn]\n    ///\n    /// [spec]: https://tc39.es/proposal-temporal/#sec-temporal.plainyearmonth.tolocalestring\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Temporal/PlainYearMonth/toLocaleString\n    pub(crate) fn to_locale_string(\n        this: &JsValue,\n        _: &[JsValue],\n        _: &mut Context,\n    ) -> JsResult<JsValue> {\n        // TODO: Update for ECMA-402 compliance\n        let object = this.as_object();\n        let year_month = object\n            .as_ref()\n            .and_then(JsObject::downcast_ref::<Self>)\n            .ok_or_else(|| {\n                JsNativeError::typ().with_message(\"this value must be a PlainYearMonth object.\")\n            })?;\n\n        Ok(JsString::from(year_month.inner.to_string()).into())\n    }\n\n    /// 9.3.21 `Temporal.PlainYearMonth.prototype.toJSON ( )`\n    ///\n    /// More information:\n    ///\n    /// - [ECMAScript Temporal proposal][spec]\n    /// - [MDN reference][mdn]\n    ///\n    /// [spec]: https://tc39.es/proposal-temporal/#sec-temporal.plainyearmonth.tojson\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Temporal/PlainYearMonth/toJSON\n    pub(crate) fn to_json(this: &JsValue, _: &[JsValue], _: &mut Context) -> JsResult<JsValue> {\n        let object = this.as_object();\n        let year_month = object\n            .as_ref()\n            .and_then(JsObject::downcast_ref::<Self>)\n            .ok_or_else(|| {\n                JsNativeError::typ().with_message(\"this value must be a PlainYearMonth object.\")\n            })?;\n\n        Ok(JsString::from(year_month.inner.to_string()).into())\n    }\n\n    /// 9.3.22 `Temporal.PlainYearMonth.prototype.valueOf ( )`\n    ///\n    /// More information:\n    ///\n    /// - [ECMAScript Temporal proposal][spec]\n    /// - [MDN reference][mdn]\n    ///\n    /// [spec]: https://tc39.es/proposal-temporal/#sec-temporal.plainyearmonth.valueof\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Temporal/PlainYearMonth/valueOf\n    pub(crate) fn value_of(_this: &JsValue, _: &[JsValue], _: &mut Context) -> JsResult<JsValue> {\n        Err(JsNativeError::typ()\n            .with_message(\"`valueOf` not supported by Temporal built-ins. See 'compare', 'equals', or `toString`\")\n            .into())\n    }\n\n    /// 9.3.23 `Temporal.PlainYearMonth.prototype.toPlainDate ( item )`\n    ///\n    /// More information:\n    ///\n    /// - [ECMAScript Temporal proposal][spec]\n    /// - [MDN reference][mdn]\n    /// - [`temporal_rs` documentation][temporal_rs-docs]\n    ///\n    /// [spec]: https://tc39.es/proposal-temporal/#sec-temporal.plainyearmonth.toplaindate\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Temporal/PlainYearMonth/toPlainDate\n    /// [temporal_rs-docs]: https://docs.rs/temporal_rs/latest/temporal_rs/struct.PlainYearMonth.html#method.to_plain_date\n    fn to_plain_date(this: &JsValue, args: &[JsValue], context: &mut Context) -> JsResult<JsValue> {\n        // 1. Let yearMonth be the this value.\n        // 2. Perform ? RequireInternalSlot(yearMonth, [[InitializedTemporalYearMonth]]).\n        let object = this.as_object();\n        let year_month = object\n            .as_ref()\n            .and_then(JsObject::downcast_ref::<Self>)\n            .ok_or_else(|| {\n                JsNativeError::typ().with_message(\"this value must be a PlainYearMonth object.\")\n            })?;\n\n        // 3. If item is not an Object, then\n        let Some(obj) = args.get_or_undefined(0).as_object() else {\n            // a. Throw a TypeError exception.\n            return Err(JsNativeError::typ()\n                .with_message(\"toPlainDate item must be an object.\")\n                .into());\n        };\n        // 4. Let calendar be yearMonth.[[Calendar]].\n        // 5. Let fields be ISODateToFields(calendar, yearMonth.[[ISODate]], year-month).\n        // 6. Let inputFields be ? PrepareCalendarFields(calendar, item, « day », « », « »).\n        let day = obj\n            .get(js_string!(\"day\"), context)?\n            .map(|v| {\n                let finite = v.to_finitef64(context)?;\n                finite\n                    .as_positive_integer_with_truncation::<u8>()\n                    .map_err(JsError::from)\n            })\n            .transpose()?;\n\n        let fields = CalendarFields::new().with_optional_day(day);\n\n        // 7. Let mergedFields be CalendarMergeFields(calendar, fields, inputFields).\n        // 8. Let isoDate be ? CalendarDateFromFields(calendar, mergedFields, constrain).\n        let result = year_month.inner.to_plain_date(Some(fields))?;\n        // 9. Return ! CreateTemporalDate(isoDate, calendar).\n        create_temporal_date(result, None, context).map(Into::into)\n    }\n}\n\n// ==== PlainYearMonth Abstract Operations ====\n\n/// 9.5.2 `ToTemporalYearMonth ( item [ , options ] )`\nfn to_temporal_year_month(\n    value: &JsValue,\n    options: Option<JsValue>,\n    context: &mut Context,\n) -> JsResult<InnerYearMonth> {\n    // If options is not present, set options to undefined.\n    let options = options.unwrap_or_default();\n    // 2. If item is an Object, then\n    if let Some(obj) = value.as_object() {\n        // a. If item has an [[InitializedTemporalYearMonth]] internal slot, then\n        if let Some(ym) = obj.downcast_ref::<PlainYearMonth>() {\n            // i. Let resolvedOptions be ? GetOptionsObject(options).\n            let resolved_options = get_options_object(&options)?;\n            // ii. Perform ? GetTemporalOverflowOption(resolvedOptions).\n            let _overflow =\n                get_option::<Overflow>(&resolved_options, js_string!(\"overflow\"), context)?\n                    .unwrap_or(Overflow::Constrain);\n            // iii. Return ! CreateTemporalYearMonth(item.[[ISODate]], item.[[Calendar]]).\n            return Ok(ym.inner.clone());\n        }\n        // b. Let calendar be ? GetTemporalCalendarIdentifierWithISODefault(item).\n        // c. Let fields be ? PrepareCalendarFields(calendar, item, « year, month, month-code », «», «»).\n        let partial = to_partial_year_month(&obj, context)?;\n        // d. Let resolvedOptions be ? GetOptionsObject(options).\n        let resolved_options = get_options_object(&options)?;\n        // e. Let overflow be ? GetTemporalOverflowOption(resolvedOptions).\n        let overflow = get_option::<Overflow>(&resolved_options, js_string!(\"overflow\"), context)?;\n        // f. Let isoDate be ? CalendarYearMonthFromFields(calendar, fields, overflow).\n        // g. Return ! CreateTemporalYearMonth(isoDate, calendar).\n        return Ok(InnerYearMonth::from_partial(partial, overflow)?);\n    }\n\n    // 3. If item is not a String, throw a TypeError exception.\n    let Some(ym_string) = value.as_string() else {\n        return Err(JsNativeError::typ()\n            .with_message(\"toTemporalYearMonth target must be an object or string\")\n            .into());\n    };\n\n    // 4. Let result be ? ParseISODateTime(item, « TemporalYearMonthString »).\n    let result = InnerYearMonth::from_str(&ym_string.to_std_string_escaped())?;\n    // 5. Let calendar be result.[[Calendar]].\n    // 6. If calendar is empty, set calendar to \"iso8601\".\n    // 7. Set calendar to ? CanonicalizeCalendar(calendar).\n    // 8. Let resolvedOptions be ? GetOptionsObject(options).\n    let resolved_options = get_options_object(&options)?;\n    // 9. Perform ? GetTemporalOverflowOption(resolvedOptions).\n    let _overflow = get_option::<Overflow>(&resolved_options, js_string!(\"overflow\"), context)?\n        .unwrap_or(Overflow::Constrain);\n\n    // 10. Let isoDate be CreateISODateRecord(result.[[Year]], result.[[Month]], result.[[Day]]).\n    // 11. If ISOYearMonthWithinLimits(isoDate) is false, throw a RangeError exception.\n    // 12. Set result to ISODateToFields(calendar, isoDate, year-month).\n    // 13. NOTE: The following operation is called with constrain regardless of the value of overflow, in order for the calendar to store a canonical value in the [[Day]] field of the [[ISODate]] internal slot of the result.\n    // 14. Set isoDate to ? CalendarYearMonthFromFields(calendar, result, constrain).\n    // 15. Return ! CreateTemporalYearMonth(isoDate, calendar).\n    Ok(result)\n}\n\n// 9.5.6 `CreateTemporalYearMonth ( isoYear, isoMonth, calendar, referenceISODay [ , newTarget ] )`\npub(crate) fn create_temporal_year_month(\n    ym: InnerYearMonth,\n    new_target: Option<&JsValue>,\n    context: &mut Context,\n) -> JsResult<JsValue> {\n    // 1. If IsValidISODate(isoYear, isoMonth, referenceISODay) is false, throw a RangeError exception.\n    // 2. If ! ISOYearMonthWithinLimits(isoYear, isoMonth) is false, throw a RangeError exception.\n\n    // 3. If newTarget is not present, set newTarget to %Temporal.PlainYearMonth%.\n    let new_target = if let Some(target) = new_target {\n        target.clone()\n    } else {\n        context\n            .realm()\n            .intrinsics()\n            .constructors()\n            .plain_year_month()\n            .constructor()\n            .into()\n    };\n\n    // 4. Let object be ? OrdinaryCreateFromConstructor(newTarget, \"%Temporal.PlainYearMonth.prototype%\", « [[InitializedTemporalYearMonth]], [[ISOYear]], [[ISOMonth]], [[ISODay]], [[Calendar]] »).\n    let proto = get_prototype_from_constructor(\n        &new_target,\n        StandardConstructors::plain_year_month,\n        context,\n    )?;\n\n    // 5. Set object.[[ISOYear]] to isoYear.\n    // 6. Set object.[[ISOMonth]] to isoMonth.\n    // 7. Set object.[[Calendar]] to calendar.\n    // 8. Set object.[[ISODay]] to referenceISODay.\n\n    let obj = JsObject::from_proto_and_data(proto, PlainYearMonth::new(ym));\n\n    // 9. Return object.\n    Ok(obj.into())\n}\n\n// 9.5.9 AddDurationToOrSubtractDurationFromPlainYearMonth ( operation, yearMonth, temporalDurationLike, options )\nfn add_or_subtract_duration(\n    is_addition: bool,\n    this: &JsValue,\n    duration_like: &JsValue,\n    options: &JsObject,\n    context: &mut Context,\n) -> JsResult<JsValue> {\n    let duration: Duration = if duration_like.is_object() {\n        to_temporal_duration(duration_like, context)?\n    } else if let Some(duration_string) = duration_like.as_string() {\n        Duration::from_str(duration_string.to_std_string_escaped().as_str())?\n    } else {\n        return Err(JsNativeError::typ()\n            .with_message(\"cannot handler string durations yet.\")\n            .into());\n    };\n\n    let overflow =\n        get_option(options, js_string!(\"overflow\"), context)?.unwrap_or(Overflow::Constrain);\n\n    let object = this.as_object();\n    let year_month = object\n        .as_ref()\n        .and_then(JsObject::downcast_ref::<PlainYearMonth>)\n        .ok_or_else(|| {\n            JsNativeError::typ().with_message(\"this value must be a PlainYearMonth object.\")\n        })?;\n\n    let inner = &year_month.inner;\n    let year_month_result = if is_addition {\n        inner.add(&duration, overflow)?\n    } else {\n        inner.subtract(&duration, overflow)?\n    };\n\n    create_temporal_year_month(year_month_result, None, context)\n}\n\nfn to_partial_year_month(\n    partial_object: &JsObject,\n    context: &mut Context,\n) -> JsResult<PartialYearMonth> {\n    // a. Let calendar be ? ToTemporalCalendar(item).\n    let calendar = get_temporal_calendar_slot_value_with_default(partial_object, context)?;\n    let calendar_fields = to_year_month_calendar_fields(partial_object, &calendar, context)?;\n    Ok(PartialYearMonth {\n        calendar_fields,\n        calendar,\n    })\n}\n\npub(crate) fn to_year_month_calendar_fields(\n    partial_object: &JsObject,\n    calendar: &Calendar,\n    context: &mut Context,\n) -> JsResult<YearMonthCalendarFields> {\n    // TODO: `temporal_rs` needs a `has_era` method\n    let has_no_era = calendar.kind() == AnyCalendarKind::Iso\n        || calendar.kind() == AnyCalendarKind::Chinese\n        || calendar.kind() == AnyCalendarKind::Dangi;\n    let (era, era_year) = if has_no_era {\n        (None, None)\n    } else {\n        let era = partial_object\n            .get(js_string!(\"era\"), context)?\n            .map(|v| {\n                let v = v.to_primitive(context, crate::value::PreferredType::String)?;\n                let Some(era) = v.as_string() else {\n                    return Err(JsError::from(\n                        JsNativeError::typ()\n                            .with_message(\"The monthCode field value must be a string.\"),\n                    ));\n                };\n                // TODO: double check if an invalid monthCode is a range or type error.\n                TinyAsciiStr::<19>::try_from_str(&era.to_std_string_escaped())\n                    .map_err(|e| JsError::from(JsNativeError::range().with_message(e.to_string())))\n            })\n            .transpose()?;\n        let era_year = partial_object\n            .get(js_string!(\"eraYear\"), context)?\n            .map(|v| {\n                let finite = v.to_finitef64(context)?;\n                Ok::<i32, JsError>(finite.as_integer_with_truncation::<i32>())\n            })\n            .transpose()?;\n        (era, era_year)\n    };\n\n    let month = partial_object\n        .get(js_string!(\"month\"), context)?\n        .map(|v| {\n            let finite = v.to_finitef64(context)?;\n            finite\n                .as_positive_integer_with_truncation::<u8>()\n                .map_err(JsError::from)\n        })\n        .transpose()?;\n    let month_code = partial_object\n        .get(js_string!(\"monthCode\"), context)?\n        .map(|v| {\n            let v = v.to_primitive(context, crate::value::PreferredType::String)?;\n            let Some(month_code) = v.as_string() else {\n                return Err(JsNativeError::typ()\n                    .with_message(\"The monthCode field value must be a string.\")\n                    .into());\n            };\n            MonthCode::from_str(&month_code.to_std_string_escaped()).map_err(JsError::from)\n        })\n        .transpose()?;\n\n    let year = partial_object\n        .get(js_string!(\"year\"), context)?\n        .map(|v| {\n            let finite = v.to_finitef64(context)?;\n            Ok::<i32, JsError>(finite.as_integer_with_truncation::<i32>())\n        })\n        .transpose()?;\n\n    Ok(YearMonthCalendarFields::new()\n        .with_era(era)\n        .with_era_year(era_year)\n        .with_optional_year(year)\n        .with_optional_month(month)\n        .with_optional_month_code(month_code))\n}\n"
  },
  {
    "path": "core/engine/src/builtins/temporal/tests.rs",
    "content": "use boa_macros::js_str;\n\nuse crate::{JsValue, TestAction, run_test_actions};\n\n// Temporal Object tests.\n\n#[test]\nfn temporal_object() {\n    // Temporal Builtin tests.\n    run_test_actions([\n        TestAction::assert_eq(\n            \"Object.prototype.toString.call(Temporal)\",\n            js_str!(\"[object Temporal]\"),\n        ),\n        TestAction::assert_eq(\"String(Temporal)\", js_str!(\"[object Temporal]\")),\n        TestAction::assert_eq(\"Object.keys(Temporal).length === 0\", true),\n    ]);\n}\n\n#[test]\nfn now_object() {\n    // Now Builtin tests.\n    run_test_actions([\n        TestAction::assert_eq(\"Object.isExtensible(Temporal.Now)\", true),\n        TestAction::assert_eq(\n            \"Object.prototype.toString.call(Temporal.Now)\",\n            js_str!(\"[object Temporal.Now]\"),\n        ),\n        TestAction::assert_eq(\n            \"Object.getPrototypeOf(Temporal.Now) === Object.prototype\",\n            true,\n        ),\n        TestAction::assert_eq(\"Temporal.Now.prototype\", JsValue::undefined()),\n    ]);\n}\n\n// Date Equations\n"
  },
  {
    "path": "core/engine/src/builtins/temporal/zoneddatetime/mod.rs",
    "content": "//! Boa's implementation of the ECMAScript `Temporal.ZonedDateTime` built-in object\n\nuse std::str::FromStr;\n\nuse crate::{\n    Context, JsArgs, JsBigInt, JsData, JsError, JsNativeError, JsObject, JsResult, JsString,\n    JsSymbol, JsValue, JsVariant,\n    builtins::{\n        BuiltInBuilder, BuiltInConstructor, BuiltInObject, IntrinsicObject,\n        options::{get_option, get_options_object},\n        temporal::{calendar::to_temporal_calendar_identifier, options::get_digits_option},\n    },\n    context::intrinsics::{Intrinsics, StandardConstructor, StandardConstructors},\n    js_string,\n    object::internal_methods::get_prototype_from_constructor,\n    property::Attribute,\n    realm::Realm,\n    string::StaticJsStrings,\n    value::{IntoOrUndefined, PreferredType},\n};\nuse boa_gc::{Finalize, Trace};\nuse cow_utils::CowUtils;\nuse icu_calendar::AnyCalendarKind;\nuse temporal_rs::{\n    Calendar, MonthCode, TimeZone, TinyAsciiStr, UtcOffset, ZonedDateTime as ZonedDateTimeInner,\n    fields::{CalendarFields, ZonedDateTimeFields},\n    options::{\n        Disambiguation, DisplayCalendar, DisplayOffset, DisplayTimeZone, OffsetDisambiguation,\n        Overflow, RoundingIncrement, RoundingMode, RoundingOptions, ToStringRoundingOptions, Unit,\n    },\n    parsed_intermediates::ParsedZonedDateTime,\n    partial::{PartialTime, PartialZonedDateTime},\n    provider::TransitionDirection,\n};\n\nuse super::{\n    calendar::get_temporal_calendar_slot_value_with_default,\n    create_temporal_date, create_temporal_datetime, create_temporal_duration,\n    create_temporal_instant, create_temporal_time, is_partial_temporal_object,\n    options::{TemporalUnitGroup, get_difference_settings, get_temporal_unit},\n    to_temporal_duration, to_temporal_time,\n};\n\n/// The `Temporal.ZonedDateTime` built-in implementation\n///\n/// More information:\n///\n/// - [ECMAScript Temporal proposal][spec]\n/// - [MDN reference][mdn]\n/// - [`temporal_rs` documentation][temporal_rs-docs]\n///\n/// [spec]: https://tc39.es/proposal-temporal/#sec-temporal-zoneddatetime-objects\n/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Temporal/ZonedDateTime\n/// [temporal_rs-docs]: https://docs.rs/temporal_rs/latest/temporal_rs/struct.ZonedDateTime.html\n#[derive(Debug, Clone, Trace, Finalize, JsData)]\n#[boa_gc(unsafe_empty_trace)] // Safety: Does not contain any traceable fields.\npub struct ZonedDateTime {\n    pub(crate) inner: Box<ZonedDateTimeInner>,\n}\n\nimpl ZonedDateTime {\n    pub(crate) fn new(inner: ZonedDateTimeInner) -> Self {\n        Self {\n            inner: Box::new(inner),\n        }\n    }\n}\n\nimpl BuiltInObject for ZonedDateTime {\n    const NAME: JsString = StaticJsStrings::ZONED_DT_NAME;\n}\n\nimpl IntrinsicObject for ZonedDateTime {\n    fn init(realm: &Realm) {\n        let get_calendar_id = BuiltInBuilder::callable(realm, Self::get_calendar_id)\n            .name(js_string!(\"get calendarId\"))\n            .build();\n\n        let get_timezone_id = BuiltInBuilder::callable(realm, Self::get_timezone_id)\n            .name(js_string!(\"get timeZoneId\"))\n            .build();\n\n        let get_era = BuiltInBuilder::callable(realm, Self::get_era)\n            .name(js_string!(\"get era\"))\n            .build();\n\n        let get_era_year = BuiltInBuilder::callable(realm, Self::get_era_year)\n            .name(js_string!(\"get eraYear\"))\n            .build();\n\n        let get_year = BuiltInBuilder::callable(realm, Self::get_year)\n            .name(js_string!(\"get year\"))\n            .build();\n\n        let get_month = BuiltInBuilder::callable(realm, Self::get_month)\n            .name(js_string!(\"get month\"))\n            .build();\n\n        let get_month_code = BuiltInBuilder::callable(realm, Self::get_month_code)\n            .name(js_string!(\"get monthCode\"))\n            .build();\n\n        let get_day = BuiltInBuilder::callable(realm, Self::get_day)\n            .name(js_string!(\"get day\"))\n            .build();\n\n        let get_hour = BuiltInBuilder::callable(realm, Self::get_hour)\n            .name(js_string!(\"get hour\"))\n            .build();\n\n        let get_minute = BuiltInBuilder::callable(realm, Self::get_minute)\n            .name(js_string!(\"get minute\"))\n            .build();\n\n        let get_second = BuiltInBuilder::callable(realm, Self::get_second)\n            .name(js_string!(\"get second\"))\n            .build();\n\n        let get_millisecond = BuiltInBuilder::callable(realm, Self::get_millisecond)\n            .name(js_string!(\"get millisecond\"))\n            .build();\n\n        let get_microsecond = BuiltInBuilder::callable(realm, Self::get_microsecond)\n            .name(js_string!(\"get microsecond\"))\n            .build();\n\n        let get_nanosecond = BuiltInBuilder::callable(realm, Self::get_nanosecond)\n            .name(js_string!(\"get nanosecond\"))\n            .build();\n\n        let get_epoch_milliseconds = BuiltInBuilder::callable(realm, Self::get_epoch_milliseconds)\n            .name(js_string!(\"get epochMilliseconds\"))\n            .build();\n\n        let get_epoch_nanoseconds = BuiltInBuilder::callable(realm, Self::get_epoch_nanoseconds)\n            .name(js_string!(\"get epochNanoseconds\"))\n            .build();\n\n        let get_day_of_week = BuiltInBuilder::callable(realm, Self::get_day_of_week)\n            .name(js_string!(\"get dayOfWeek\"))\n            .build();\n\n        let get_day_of_year = BuiltInBuilder::callable(realm, Self::get_day_of_year)\n            .name(js_string!(\"get dayOfYear\"))\n            .build();\n\n        let get_week_of_year = BuiltInBuilder::callable(realm, Self::get_week_of_year)\n            .name(js_string!(\"get weekOfYear\"))\n            .build();\n\n        let get_hours_in_day = BuiltInBuilder::callable(realm, Self::get_hours_in_day)\n            .name(js_string!(\"get daysInWeek\"))\n            .build();\n\n        let get_year_of_week = BuiltInBuilder::callable(realm, Self::get_year_of_week)\n            .name(js_string!(\"get yearOfWeek\"))\n            .build();\n\n        let get_days_in_week = BuiltInBuilder::callable(realm, Self::get_days_in_week)\n            .name(js_string!(\"get daysInWeek\"))\n            .build();\n\n        let get_days_in_month = BuiltInBuilder::callable(realm, Self::get_days_in_month)\n            .name(js_string!(\"get daysInMonth\"))\n            .build();\n\n        let get_days_in_year = BuiltInBuilder::callable(realm, Self::get_days_in_year)\n            .name(js_string!(\"get daysInYear\"))\n            .build();\n\n        let get_months_in_year = BuiltInBuilder::callable(realm, Self::get_months_in_year)\n            .name(js_string!(\"get monthsInYear\"))\n            .build();\n\n        let get_in_leap_year = BuiltInBuilder::callable(realm, Self::get_in_leap_year)\n            .name(js_string!(\"get inLeapYear\"))\n            .build();\n\n        let get_offset_nanos = BuiltInBuilder::callable(realm, Self::get_offset_nanoseconds)\n            .name(js_string!(\"get offsetNanoseconds\"))\n            .build();\n\n        let get_offset = BuiltInBuilder::callable(realm, Self::get_offset)\n            .name(js_string!(\"get offset\"))\n            .build();\n\n        BuiltInBuilder::from_standard_constructor::<Self>(realm)\n            .property(\n                JsSymbol::to_string_tag(),\n                StaticJsStrings::ZONED_DT_TAG,\n                Attribute::CONFIGURABLE,\n            )\n            .accessor(\n                js_string!(\"calendarId\"),\n                Some(get_calendar_id),\n                None,\n                Attribute::CONFIGURABLE,\n            )\n            .accessor(\n                js_string!(\"timeZoneId\"),\n                Some(get_timezone_id),\n                None,\n                Attribute::CONFIGURABLE,\n            )\n            .accessor(\n                js_string!(\"era\"),\n                Some(get_era),\n                None,\n                Attribute::CONFIGURABLE,\n            )\n            .accessor(\n                js_string!(\"eraYear\"),\n                Some(get_era_year),\n                None,\n                Attribute::CONFIGURABLE,\n            )\n            .accessor(\n                js_string!(\"year\"),\n                Some(get_year),\n                None,\n                Attribute::CONFIGURABLE,\n            )\n            .accessor(\n                js_string!(\"month\"),\n                Some(get_month),\n                None,\n                Attribute::CONFIGURABLE,\n            )\n            .accessor(\n                js_string!(\"monthCode\"),\n                Some(get_month_code),\n                None,\n                Attribute::CONFIGURABLE,\n            )\n            .accessor(\n                js_string!(\"day\"),\n                Some(get_day),\n                None,\n                Attribute::CONFIGURABLE,\n            )\n            .accessor(\n                js_string!(\"hour\"),\n                Some(get_hour),\n                None,\n                Attribute::CONFIGURABLE,\n            )\n            .accessor(\n                js_string!(\"minute\"),\n                Some(get_minute),\n                None,\n                Attribute::CONFIGURABLE,\n            )\n            .accessor(\n                js_string!(\"second\"),\n                Some(get_second),\n                None,\n                Attribute::CONFIGURABLE,\n            )\n            .accessor(\n                js_string!(\"millisecond\"),\n                Some(get_millisecond),\n                None,\n                Attribute::CONFIGURABLE,\n            )\n            .accessor(\n                js_string!(\"microsecond\"),\n                Some(get_microsecond),\n                None,\n                Attribute::CONFIGURABLE,\n            )\n            .accessor(\n                js_string!(\"nanosecond\"),\n                Some(get_nanosecond),\n                None,\n                Attribute::CONFIGURABLE,\n            )\n            .accessor(\n                js_string!(\"epochMilliseconds\"),\n                Some(get_epoch_milliseconds),\n                None,\n                Attribute::CONFIGURABLE,\n            )\n            .accessor(\n                js_string!(\"epochNanoseconds\"),\n                Some(get_epoch_nanoseconds),\n                None,\n                Attribute::CONFIGURABLE,\n            )\n            .accessor(\n                js_string!(\"dayOfWeek\"),\n                Some(get_day_of_week),\n                None,\n                Attribute::CONFIGURABLE,\n            )\n            .accessor(\n                js_string!(\"dayOfYear\"),\n                Some(get_day_of_year),\n                None,\n                Attribute::CONFIGURABLE,\n            )\n            .accessor(\n                js_string!(\"weekOfYear\"),\n                Some(get_week_of_year),\n                None,\n                Attribute::CONFIGURABLE,\n            )\n            .accessor(\n                js_string!(\"yearOfWeek\"),\n                Some(get_year_of_week),\n                None,\n                Attribute::CONFIGURABLE,\n            )\n            .accessor(\n                js_string!(\"hoursInDay\"),\n                Some(get_hours_in_day),\n                None,\n                Attribute::CONFIGURABLE,\n            )\n            .accessor(\n                js_string!(\"daysInWeek\"),\n                Some(get_days_in_week),\n                None,\n                Attribute::CONFIGURABLE,\n            )\n            .accessor(\n                js_string!(\"daysInMonth\"),\n                Some(get_days_in_month),\n                None,\n                Attribute::CONFIGURABLE,\n            )\n            .accessor(\n                js_string!(\"daysInYear\"),\n                Some(get_days_in_year),\n                None,\n                Attribute::CONFIGURABLE,\n            )\n            .accessor(\n                js_string!(\"monthsInYear\"),\n                Some(get_months_in_year),\n                None,\n                Attribute::CONFIGURABLE,\n            )\n            .accessor(\n                js_string!(\"inLeapYear\"),\n                Some(get_in_leap_year),\n                None,\n                Attribute::CONFIGURABLE,\n            )\n            .accessor(\n                js_string!(\"offsetNanoseconds\"),\n                Some(get_offset_nanos),\n                None,\n                Attribute::CONFIGURABLE,\n            )\n            .accessor(\n                js_string!(\"offset\"),\n                Some(get_offset),\n                None,\n                Attribute::CONFIGURABLE,\n            )\n            .static_method(Self::from, js_string!(\"from\"), 1)\n            .static_method(Self::compare, js_string!(\"compare\"), 2)\n            .method(Self::with, js_string!(\"with\"), 1)\n            .method(Self::with_plain_time, js_string!(\"withPlainTime\"), 0)\n            .method(Self::with_timezone, js_string!(\"withTimeZone\"), 1)\n            .method(Self::with_calendar, js_string!(\"withCalendar\"), 1)\n            .method(Self::add, js_string!(\"add\"), 1)\n            .method(Self::subtract, js_string!(\"subtract\"), 1)\n            .method(Self::until, js_string!(\"until\"), 1)\n            .method(Self::since, js_string!(\"since\"), 1)\n            .method(Self::round, js_string!(\"round\"), 1)\n            .method(Self::equals, js_string!(\"equals\"), 1)\n            .method(Self::to_string, js_string!(\"toString\"), 0)\n            .method(Self::to_locale_string, js_string!(\"toLocaleString\"), 0)\n            .method(Self::to_json, js_string!(\"toJSON\"), 0)\n            .method(Self::value_of, js_string!(\"valueOf\"), 0)\n            .method(Self::start_of_day, js_string!(\"startOfDay\"), 0)\n            .method(\n                Self::get_time_zone_transition,\n                js_string!(\"getTimeZoneTransition\"),\n                1,\n            )\n            .method(Self::to_instant, js_string!(\"toInstant\"), 0)\n            .method(Self::to_plain_date, js_string!(\"toPlainDate\"), 0)\n            .method(Self::to_plain_time, js_string!(\"toPlainTime\"), 0)\n            .method(Self::to_plain_date_time, js_string!(\"toPlainDateTime\"), 0)\n            .build();\n    }\n\n    fn get(intrinsics: &Intrinsics) -> JsObject {\n        Self::STANDARD_CONSTRUCTOR(intrinsics.constructors()).constructor()\n    }\n}\n\nimpl BuiltInConstructor for ZonedDateTime {\n    const CONSTRUCTOR_ARGUMENTS: usize = 2;\n    const PROTOTYPE_STORAGE_SLOTS: usize = 77;\n    const CONSTRUCTOR_STORAGE_SLOTS: usize = 2;\n\n    const STANDARD_CONSTRUCTOR: fn(&StandardConstructors) -> &StandardConstructor =\n        StandardConstructors::zoned_date_time;\n\n    fn constructor(\n        new_target: &JsValue,\n        args: &[JsValue],\n        context: &mut Context,\n    ) -> JsResult<JsValue> {\n        // 1. If NewTarget is undefined, then\n        if new_target.is_undefined() {\n            // a. Throw a TypeError exception.\n            return Err(JsNativeError::typ()\n                .with_message(\"NewTarget cannot be undefined.\")\n                .into());\n        }\n        //  2. Set epochNanoseconds to ? ToBigInt(epochNanoseconds).\n        //  3. If IsValidEpochNanoseconds(epochNanoseconds) is false, throw a RangeError exception.\n        let epoch_nanos = args.get_or_undefined(0).to_bigint(context)?;\n\n        //  4. If timeZone is not a String, throw a TypeError exception.\n        let Some(timezone_str) = args.get_or_undefined(1).as_string() else {\n            return Err(JsNativeError::typ()\n                .with_message(\"timeZone must be a string.\")\n                .into());\n        };\n\n        //  5. Let timeZoneParse be ? ParseTimeZoneIdentifier(timeZone).\n        //  6. If timeZoneParse.[[OffsetMinutes]] is empty, then\n        // a. Let identifierRecord be GetAvailableNamezdtimeZoneIdentifier(timeZoneParse.[[Name]]).\n        // b. If identifierRecord is empty, throw a RangeError exception.\n        // c. Set timeZone to identifierRecord.[[Identifier]].\n        //  7. Else,\n        // a. Set timeZone to FormatOffsetTimeZoneIdentifier(timeZoneParse.[[OffsetMinutes]]).\n        let timezone = TimeZone::try_from_identifier_str_with_provider(\n            &timezone_str.to_std_string_escaped(),\n            context.timezone_provider(),\n        )?;\n\n        //  8. If calendar is undefined, set calendar to \"iso8601\".\n        //  9. If calendar is not a String, throw a TypeError exception.\n        //  10. Set calendar to ? CanonicalizeCalendar(calendar).\n        let calendar = args\n            .get_or_undefined(2)\n            .map(|s| {\n                s.as_string()\n                    .as_ref()\n                    .map(JsString::to_std_string_lossy)\n                    .ok_or_else(|| JsNativeError::typ().with_message(\"calendar must be a string.\"))\n            })\n            .transpose()?\n            .map(|s| Calendar::try_from_utf8(s.as_bytes()))\n            .transpose()?\n            .unwrap_or_default();\n\n        let inner = ZonedDateTimeInner::try_new_with_provider(\n            epoch_nanos.to_i128(),\n            timezone,\n            calendar,\n            context.timezone_provider(),\n        )?;\n\n        //  11. Return ? CreateTemporalZonedDateTime(epochNanoseconds, timeZone, calendar, NewTarget).\n        create_temporal_zoneddatetime(inner, Some(new_target), context).map(Into::into)\n    }\n}\n\n// ==== `ZonedDateTime` accessor property methods ====\n\nimpl ZonedDateTime {\n    /// 6.3.3 get `Temporal.ZonedDateTime.prototype.calendarId`\n    ///\n    /// More information:\n    ///\n    /// - [ECMAScript Temporal proposal][spec]\n    /// - [MDN reference][mdn]\n    /// - [`temporal_rs` documentation][temporal_rs-docs]\n    ///\n    /// [spec]: https://tc39.es/proposal-temporal/#sec-get-temporal.zoneddatetime.prototype.calendarid\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Temporal/ZonedDateTime/calendarId\n    /// [temporal_rs-docs]: https://docs.rs/temporal_rs/latest/temporal_rs/struct.ZonedDateTime.html#method.calendar\n    fn get_calendar_id(this: &JsValue, _: &[JsValue], _: &mut Context) -> JsResult<JsValue> {\n        let object = this.as_object();\n        let zdt = object\n            .as_ref()\n            .and_then(JsObject::downcast_ref::<Self>)\n            .ok_or_else(|| {\n                JsNativeError::typ().with_message(\"the this object must be a ZonedDateTime object.\")\n            })?;\n\n        Ok(JsString::from(zdt.inner.calendar().identifier()).into())\n    }\n\n    /// 6.3.4 get `Temporal.ZonedDateTime.prototype.timeZoneId`\n    ///\n    /// More information:\n    ///\n    /// - [ECMAScript Temporal proposal][spec]\n    /// - [MDN reference][mdn]\n    /// - [`temporal_rs` documentation][temporal_rs-docs]\n    ///\n    /// [spec]: https://tc39.es/proposal-temporal/#sec-get-temporal.zoneddatetime.prototype.timezoneid\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Temporal/ZonedDateTime/timeZoneId\n    /// [temporal_rs-docs]: https://docs.rs/temporal_rs/latest/temporal_rs/struct.ZonedDateTime.html#method.timezone\n    fn get_timezone_id(this: &JsValue, _: &[JsValue], context: &mut Context) -> JsResult<JsValue> {\n        let object = this.as_object();\n        let zdt = object\n            .as_ref()\n            .and_then(JsObject::downcast_ref::<Self>)\n            .ok_or_else(|| {\n                JsNativeError::typ().with_message(\"the this object must be a ZonedDateTime object.\")\n            })?;\n\n        Ok(JsString::from(\n            zdt.inner\n                .time_zone()\n                .identifier_with_provider(context.timezone_provider())?,\n        )\n        .into())\n    }\n\n    /// 6.3.5 get `Temporal.ZonedDateTime.prototype.era`\n    ///\n    /// More information:\n    ///\n    /// - [ECMAScript Temporal proposal][spec]\n    /// - [MDN reference][mdn]\n    /// - [`temporal_rs` documentation][temporal_rs-docs]\n    ///\n    /// [spec]: https://tc39.es/proposal-temporal/#sec-get-temporal.zoneddatetime.prototype.era\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Temporal/ZonedDateTime/era\n    /// [temporal_rs-docs]: https://docs.rs/temporal_rs/latest/temporal_rs/struct.ZonedDateTime.html#method.era\n    fn get_era(this: &JsValue, _: &[JsValue], _: &mut Context) -> JsResult<JsValue> {\n        let object = this.as_object();\n        let zdt = object\n            .as_ref()\n            .and_then(JsObject::downcast_ref::<Self>)\n            .ok_or_else(|| {\n                JsNativeError::typ().with_message(\"the this object must be a ZonedDateTime object.\")\n            })?;\n\n        let era = zdt.inner.era();\n        Ok(era\n            .map(|tinystr| JsString::from(tinystr.cow_to_lowercase()))\n            .into_or_undefined())\n    }\n\n    /// 6.3.6 get `Temporal.ZonedDateTime.prototype.eraYear`\n    ///\n    /// More information:\n    ///\n    /// - [ECMAScript Temporal proposal][spec]\n    /// - [MDN reference][mdn]\n    /// - [`temporal_rs` documentation][temporal_rs-docs]\n    ///\n    /// [spec]: https://tc39.es/proposal-temporal/#sec-get-temporal.zoneddatetime.prototype.erayear\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Temporal/ZonedDateTime/eraYear\n    /// [temporal_rs-docs]: https://docs.rs/temporal_rs/latest/temporal_rs/struct.ZonedDateTime.html#method.era_year\n    fn get_era_year(this: &JsValue, _: &[JsValue], _: &mut Context) -> JsResult<JsValue> {\n        let object = this.as_object();\n        let zdt = object\n            .as_ref()\n            .and_then(JsObject::downcast_ref::<Self>)\n            .ok_or_else(|| {\n                JsNativeError::typ().with_message(\"the this object must be a ZonedDateTime object.\")\n            })?;\n\n        Ok(zdt.inner.era_year().into_or_undefined())\n    }\n\n    /// 6.3.7 get `Temporal.ZonedDateTime.prototype.year`\n    ///\n    /// More information:\n    ///\n    /// - [ECMAScript Temporal proposal][spec]\n    /// - [MDN reference][mdn]\n    /// - [`temporal_rs` documentation][temporal_rs-docs]\n    ///\n    /// [spec]: https://tc39.es/proposal-temporal/#sec-get-temporal.zoneddatetime.prototype.year\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Temporal/ZonedDateTime/year\n    /// [temporal_rs-docs]: https://docs.rs/temporal_rs/latest/temporal_rs/struct.ZonedDateTime.html#method.year\n    fn get_year(this: &JsValue, _: &[JsValue], _: &mut Context) -> JsResult<JsValue> {\n        let object = this.as_object();\n        let zdt = object\n            .as_ref()\n            .and_then(JsObject::downcast_ref::<Self>)\n            .ok_or_else(|| {\n                JsNativeError::typ().with_message(\"the this object must be a ZonedDateTime object.\")\n            })?;\n\n        Ok(zdt.inner.year().into())\n    }\n\n    /// 6.3.8 get `Temporal.ZonedDateTime.prototype.month`\n    ///\n    /// More information:\n    ///\n    /// - [ECMAScript Temporal proposal][spec]\n    /// - [MDN reference][mdn]\n    /// - [`temporal_rs` documentation][temporal_rs-docs]\n    ///\n    /// [spec]: https://tc39.es/proposal-temporal/#sec-get-temporal.zoneddatetime.prototype.month\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Temporal/ZonedDateTime/month\n    /// [temporal_rs-docs]: https://docs.rs/temporal_rs/latest/temporal_rs/struct.ZonedDateTime.html#method.month\n    fn get_month(this: &JsValue, _: &[JsValue], _: &mut Context) -> JsResult<JsValue> {\n        let object = this.as_object();\n        let zdt = object\n            .as_ref()\n            .and_then(JsObject::downcast_ref::<Self>)\n            .ok_or_else(|| {\n                JsNativeError::typ().with_message(\"the this object must be a ZonedDateTime object.\")\n            })?;\n\n        Ok(zdt.inner.month().into())\n    }\n\n    /// 6.3.9 get `Temporal.ZonedDateTime.prototype.monthCode`\n    ///\n    /// More information:\n    ///\n    /// - [ECMAScript Temporal proposal][spec]\n    /// - [MDN reference][mdn]\n    /// - [`temporal_rs` documentation][temporal_rs-docs]\n    ///\n    /// [spec]: https://tc39.es/proposal-temporal/#sec-get-temporal.zoneddatetime.prototype.monthcode\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Temporal/ZonedDateTime/monthCode\n    /// [temporal_rs-docs]: https://docs.rs/temporal_rs/latest/temporal_rs/struct.ZonedDateTime.html#method.month_code\n    fn get_month_code(this: &JsValue, _: &[JsValue], _: &mut Context) -> JsResult<JsValue> {\n        let object = this.as_object();\n        let zdt = object\n            .as_ref()\n            .and_then(JsObject::downcast_ref::<Self>)\n            .ok_or_else(|| {\n                JsNativeError::typ().with_message(\"the this object must be a ZonedDateTime object.\")\n            })?;\n\n        Ok(JsString::from(zdt.inner.month_code().as_str()).into())\n    }\n\n    /// 6.3.10 get `Temporal.ZonedDateTime.prototype.day`\n    ///\n    /// More information:\n    ///\n    /// - [ECMAScript Temporal proposal][spec]\n    /// - [MDN reference][mdn]\n    /// - [`temporal_rs` documentation][temporal_rs-docs]\n    ///\n    /// [spec]: https://tc39.es/proposal-temporal/#sec-get-temporal.zoneddatetime.prototype.day\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Temporal/ZonedDateTime/day\n    /// [temporal_rs-docs]: https://docs.rs/temporal_rs/latest/temporal_rs/struct.ZonedDateTime.html#method.day\n    fn get_day(this: &JsValue, _: &[JsValue], _: &mut Context) -> JsResult<JsValue> {\n        let object = this.as_object();\n        let zdt = object\n            .as_ref()\n            .and_then(JsObject::downcast_ref::<Self>)\n            .ok_or_else(|| {\n                JsNativeError::typ().with_message(\"the this object must be a ZonedDateTime object.\")\n            })?;\n\n        Ok(zdt.inner.day().into())\n    }\n\n    /// 6.3.11 get `Temporal.ZonedDateTime.prototype.hour`\n    ///\n    /// More information:\n    ///\n    /// - [ECMAScript Temporal proposal][spec]\n    /// - [MDN reference][mdn]\n    /// - [`temporal_rs` documentation][temporal_rs-docs]\n    ///\n    /// [spec]: https://tc39.es/proposal-temporal/#sec-get-temporal.zoneddatetime.prototype.hour\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Temporal/ZonedDateTime/hour\n    /// [temporal_rs-docs]: https://docs.rs/temporal_rs/latest/temporal_rs/struct.ZonedDateTime.html#method.hour\n    fn get_hour(this: &JsValue, _: &[JsValue], _: &mut Context) -> JsResult<JsValue> {\n        let object = this.as_object();\n        let zdt = object\n            .as_ref()\n            .and_then(JsObject::downcast_ref::<Self>)\n            .ok_or_else(|| {\n                JsNativeError::typ().with_message(\"the this object must be a ZonedDateTime object.\")\n            })?;\n\n        Ok(zdt.inner.hour().into())\n    }\n\n    /// 6.3.12 get `Temporal.ZonedDateTime.prototype.minute`\n    ///\n    /// More information:\n    ///\n    /// - [ECMAScript Temporal proposal][spec]\n    /// - [MDN reference][mdn]\n    /// - [`temporal_rs` documentation][temporal_rs-docs]\n    ///\n    /// [spec]: https://tc39.es/proposal-temporal/#sec-get-temporal.zoneddatetime.prototype.minute\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Temporal/ZonedDateTime/minute\n    /// [temporal_rs-docs]: https://docs.rs/temporal_rs/latest/temporal_rs/struct.ZonedDateTime.html#method.minute\n    fn get_minute(this: &JsValue, _: &[JsValue], _: &mut Context) -> JsResult<JsValue> {\n        let object = this.as_object();\n        let zdt = object\n            .as_ref()\n            .and_then(JsObject::downcast_ref::<Self>)\n            .ok_or_else(|| {\n                JsNativeError::typ().with_message(\"the this object must be a ZonedDateTime object.\")\n            })?;\n\n        Ok(zdt.inner.minute().into())\n    }\n\n    /// 6.3.13 get `Temporal.ZonedDateTime.prototype.second`\n    ///\n    /// More information:\n    ///\n    /// - [ECMAScript Temporal proposal][spec]\n    /// - [MDN reference][mdn]\n    /// - [`temporal_rs` documentation][temporal_rs-docs]\n    ///\n    /// [spec]: https://tc39.es/proposal-temporal/#sec-get-temporal.zoneddatetime.prototype.second\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Temporal/ZonedDateTime/second\n    /// [temporal_rs-docs]: https://docs.rs/temporal_rs/latest/temporal_rs/struct.ZonedDateTime.html#method.second\n    fn get_second(this: &JsValue, _: &[JsValue], _: &mut Context) -> JsResult<JsValue> {\n        let object = this.as_object();\n        let zdt = object\n            .as_ref()\n            .and_then(JsObject::downcast_ref::<Self>)\n            .ok_or_else(|| {\n                JsNativeError::typ().with_message(\"the this object must be a ZonedDateTime object.\")\n            })?;\n\n        Ok(zdt.inner.second().into())\n    }\n\n    /// 6.3.14 get `Temporal.ZonedDateTime.prototype.millisecond`\n    ///\n    /// More information:\n    ///\n    /// - [ECMAScript Temporal proposal][spec]\n    /// - [MDN reference][mdn]\n    /// - [`temporal_rs` documentation][temporal_rs-docs]\n    ///\n    /// [spec]: https://tc39.es/proposal-temporal/#sec-get-temporal.zoneddatetime.prototype.millisecond\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Temporal/ZonedDateTime/millisecond\n    /// [temporal_rs-docs]: https://docs.rs/temporal_rs/latest/temporal_rs/struct.ZonedDateTime.html#method.millisecond\n    fn get_millisecond(this: &JsValue, _: &[JsValue], _: &mut Context) -> JsResult<JsValue> {\n        let object = this.as_object();\n        let zdt = object\n            .as_ref()\n            .and_then(JsObject::downcast_ref::<Self>)\n            .ok_or_else(|| {\n                JsNativeError::typ().with_message(\"the this object must be a ZonedDateTime object.\")\n            })?;\n\n        Ok(zdt.inner.millisecond().into())\n    }\n\n    /// 6.3.15 get `Temporal.ZonedDateTime.prototype.microsecond`\n    ///\n    /// More information:\n    ///\n    /// - [ECMAScript Temporal proposal][spec]\n    /// - [MDN reference][mdn]\n    /// - [`temporal_rs` documentation][temporal_rs-docs]\n    ///\n    /// [spec]: https://tc39.es/proposal-temporal/#sec-get-temporal.zoneddatetime.prototype.microsecond\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Temporal/ZonedDateTime/microsecond\n    /// [temporal_rs-docs]: https://docs.rs/temporal_rs/latest/temporal_rs/struct.ZonedDateTime.html#method.microsecond\n    fn get_microsecond(this: &JsValue, _: &[JsValue], _: &mut Context) -> JsResult<JsValue> {\n        let object = this.as_object();\n        let zdt = object\n            .as_ref()\n            .and_then(JsObject::downcast_ref::<Self>)\n            .ok_or_else(|| {\n                JsNativeError::typ().with_message(\"the this object must be a ZonedDateTime object.\")\n            })?;\n\n        Ok(zdt.inner.microsecond().into())\n    }\n\n    /// 6.3.16 get `Temporal.ZonedDateTime.prototype.nanosecond`\n    ///\n    /// More information:\n    ///\n    /// - [ECMAScript Temporal proposal][spec]\n    /// - [MDN reference][mdn]\n    /// - [`temporal_rs` documentation][temporal_rs-docs]\n    ///\n    /// [spec]: https://tc39.es/proposal-temporal/#sec-get-temporal.zoneddatetime.prototype.nanosecond\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Temporal/ZonedDateTime/nanosecond\n    /// [temporal_rs-docs]: https://docs.rs/temporal_rs/latest/temporal_rs/struct.ZonedDateTime.html#method.nanosecond\n    fn get_nanosecond(this: &JsValue, _: &[JsValue], _: &mut Context) -> JsResult<JsValue> {\n        let object = this.as_object();\n        let zdt = object\n            .as_ref()\n            .and_then(JsObject::downcast_ref::<Self>)\n            .ok_or_else(|| {\n                JsNativeError::typ().with_message(\"the this object must be a ZonedDateTime object.\")\n            })?;\n\n        Ok(zdt.inner.nanosecond().into())\n    }\n\n    /// 6.3.17 get `Temporal.ZonedDateTime.prototype.epochMilliseconds`\n    ///\n    /// More information:\n    ///\n    /// - [ECMAScript Temporal proposal][spec]\n    /// - [MDN reference][mdn]\n    /// - [`temporal_rs` documentation][temporal_rs-docs]\n    ///\n    /// [spec]: https://tc39.es/proposal-temporal/#sec-get-temporal.zoneddatetime.prototype.epochmilliseconds\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Temporal/ZonedDateTime/epochMilliseconds\n    /// [temporal_rs-docs]: https://docs.rs/temporal_rs/latest/temporal_rs/struct.ZonedDateTime.html#method.epoch_milliseconds\n    fn get_epoch_milliseconds(this: &JsValue, _: &[JsValue], _: &mut Context) -> JsResult<JsValue> {\n        let object = this.as_object();\n        let zdt = object\n            .as_ref()\n            .and_then(JsObject::downcast_ref::<Self>)\n            .ok_or_else(|| {\n                JsNativeError::typ().with_message(\"the this object must be a ZonedDateTime object.\")\n            })?;\n\n        Ok(zdt.inner.epoch_milliseconds().into())\n    }\n\n    /// 6.3.18 get `Temporal.ZonedDateTime.prototype.epochNanoseconds`\n    ///\n    /// More information:\n    ///\n    /// - [ECMAScript Temporal proposal][spec]\n    /// - [MDN reference][mdn]\n    /// - [`temporal_rs` documentation][temporal_rs-docs]\n    ///\n    /// [spec]: https://tc39.es/proposal-temporal/#sec-get-temporal.zoneddatetime.prototype.epochnanoseconds\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Temporal/ZonedDateTime/epochNanoseconds\n    /// [temporal_rs-docs]: https://docs.rs/temporal_rs/latest/temporal_rs/struct.ZonedDateTime.html#method.epoch_nanoseconds\n    fn get_epoch_nanoseconds(this: &JsValue, _: &[JsValue], _: &mut Context) -> JsResult<JsValue> {\n        let object = this.as_object();\n        let zdt = object\n            .as_ref()\n            .and_then(JsObject::downcast_ref::<Self>)\n            .ok_or_else(|| {\n                JsNativeError::typ().with_message(\"the this object must be a ZonedDateTime object.\")\n            })?;\n\n        Ok(JsBigInt::from(zdt.inner.epoch_nanoseconds().as_i128()).into())\n    }\n\n    /// 6.3.19 get `Temporal.ZonedDateTime.prototype.dayOfWeek`\n    ///\n    /// More information:\n    ///\n    /// - [ECMAScript Temporal proposal][spec]\n    /// - [MDN reference][mdn]\n    /// - [`temporal_rs` documentation][temporal_rs-docs]\n    ///\n    /// [spec]: https://tc39.es/proposal-temporal/#sec-get-temporal.zoneddatetime.prototype.dayofweek\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Temporal/ZonedDateTime/dayOfWeek\n    /// [temporal_rs-docs]: https://docs.rs/temporal_rs/latest/temporal_rs/struct.ZonedDateTime.html#method.day_of_week\n    fn get_day_of_week(this: &JsValue, _: &[JsValue], _: &mut Context) -> JsResult<JsValue> {\n        let object = this.as_object();\n        let zdt = object\n            .as_ref()\n            .and_then(JsObject::downcast_ref::<Self>)\n            .ok_or_else(|| {\n                JsNativeError::typ().with_message(\"the this object must be a ZonedDateTime object.\")\n            })?;\n\n        Ok(zdt.inner.day_of_week().into())\n    }\n\n    /// 6.3.20 get `Temporal.ZonedDateTime.prototype.dayOfYear`\n    ///\n    /// More information:\n    ///\n    /// - [ECMAScript Temporal proposal][spec]\n    /// - [MDN reference][mdn]\n    /// - [`temporal_rs` documentation][temporal_rs-docs]\n    ///\n    /// [spec]: https://tc39.es/proposal-temporal/#sec-get-temporal.zoneddatetime.prototype.dayofyear\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Temporal/ZonedDateTime/dayOfYear\n    /// [temporal_rs-docs]: https://docs.rs/temporal_rs/latest/temporal_rs/struct.ZonedDateTime.html#method.day_of_year\n    fn get_day_of_year(this: &JsValue, _: &[JsValue], _: &mut Context) -> JsResult<JsValue> {\n        let object = this.as_object();\n        let zdt = object\n            .as_ref()\n            .and_then(JsObject::downcast_ref::<Self>)\n            .ok_or_else(|| {\n                JsNativeError::typ().with_message(\"the this object must be a ZonedDateTime object.\")\n            })?;\n\n        Ok(zdt.inner.day_of_year().into())\n    }\n\n    /// 6.3.21 get `Temporal.ZonedDateTime.prototype.weekOfYear`\n    ///\n    /// More information:\n    ///\n    /// - [ECMAScript Temporal proposal][spec]\n    /// - [MDN reference][mdn]\n    /// - [`temporal_rs` documentation][temporal_rs-docs]\n    ///\n    /// [spec]: https://tc39.es/proposal-temporal/#sec-get-temporal.zoneddatetime.prototype.weekofyear\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Temporal/ZonedDateTime/weekOfYear\n    /// [temporal_rs-docs]: https://docs.rs/temporal_rs/latest/temporal_rs/struct.ZonedDateTime.html#method.week_of_year\n    fn get_week_of_year(this: &JsValue, _: &[JsValue], _: &mut Context) -> JsResult<JsValue> {\n        let object = this.as_object();\n        let zdt = object\n            .as_ref()\n            .and_then(JsObject::downcast_ref::<Self>)\n            .ok_or_else(|| {\n                JsNativeError::typ().with_message(\"the this object must be a ZonedDateTime object.\")\n            })?;\n\n        Ok(zdt.inner.week_of_year().into_or_undefined())\n    }\n\n    /// 6.3.22 get `Temporal.ZonedDateTime.prototype.yearOfWeek`\n    ///\n    /// More information:\n    ///\n    /// - [ECMAScript Temporal proposal][spec]\n    /// - [MDN reference][mdn]\n    /// - [`temporal_rs` documentation][temporal_rs-docs]\n    ///\n    /// [spec]: https://tc39.es/proposal-temporal/#sec-get-temporal.zoneddatetime.prototype.yearofweek\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Temporal/ZonedDateTime/yearOfWeek\n    /// [temporal_rs-docs]: https://docs.rs/temporal_rs/latest/temporal_rs/struct.ZonedDateTime.html#method.year_of_week\n    fn get_year_of_week(this: &JsValue, _: &[JsValue], _: &mut Context) -> JsResult<JsValue> {\n        let object = this.as_object();\n        let zdt = object\n            .as_ref()\n            .and_then(JsObject::downcast_ref::<Self>)\n            .ok_or_else(|| {\n                JsNativeError::typ().with_message(\"the this object must be a ZonedDateTime object.\")\n            })?;\n\n        Ok(zdt.inner.year_of_week().into_or_undefined())\n    }\n\n    /// 6.3.23 get `Temporal.ZonedDateTime.prototype.hoursInDay`\n    ///\n    /// More information:\n    ///\n    /// - [ECMAScript Temporal proposal][spec]\n    /// - [MDN reference][mdn]\n    /// - [`temporal_rs` documentation][temporal_rs-docs]\n    ///\n    /// [spec]: https://tc39.es/proposal-temporal/#sec-get-temporal.zoneddatetime.prototype.hoursinday\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Temporal/ZonedDateTime/hoursInDay\n    /// [temporal_rs-docs]: https://docs.rs/temporal_rs/latest/temporal_rs/struct.ZonedDateTime.html#method.hours_in_day\n    fn get_hours_in_day(this: &JsValue, _: &[JsValue], context: &mut Context) -> JsResult<JsValue> {\n        let object = this.as_object();\n        let zdt = object\n            .as_ref()\n            .and_then(JsObject::downcast_ref::<Self>)\n            .ok_or_else(|| {\n                JsNativeError::typ().with_message(\"the this object must be a ZonedDateTime object.\")\n            })?;\n\n        Ok(zdt\n            .inner\n            .hours_in_day_with_provider(context.timezone_provider())?\n            .into())\n    }\n\n    /// 6.3.24 get `Temporal.ZonedDateTime.prototype.daysInWeek`\n    ///\n    /// More information:\n    ///\n    /// - [ECMAScript Temporal proposal][spec]\n    /// - [MDN reference][mdn]\n    /// - [`temporal_rs` documentation][temporal_rs-docs]\n    ///\n    /// [spec]: https://tc39.es/proposal-temporal/#sec-get-temporal.zoneddatetime.prototype.daysinweek\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Temporal/ZonedDateTime/daysInWeek\n    /// [temporal_rs-docs]: https://docs.rs/temporal_rs/latest/temporal_rs/struct.ZonedDateTime.html#method.days_in_week\n    fn get_days_in_week(this: &JsValue, _: &[JsValue], _: &mut Context) -> JsResult<JsValue> {\n        let object = this.as_object();\n        let zdt = object\n            .as_ref()\n            .and_then(JsObject::downcast_ref::<Self>)\n            .ok_or_else(|| {\n                JsNativeError::typ().with_message(\"the this object must be a ZonedDateTime object.\")\n            })?;\n\n        Ok(zdt.inner.days_in_week().into())\n    }\n\n    /// 6.3.25 get `Temporal.ZonedDateTime.prototype.daysInMonth`\n    ///\n    /// More information:\n    ///\n    /// - [ECMAScript Temporal proposal][spec]\n    /// - [MDN reference][mdn]\n    /// - [`temporal_rs` documentation][temporal_rs-docs]\n    ///\n    /// [spec]: https://tc39.es/proposal-temporal/#sec-get-temporal.zoneddatetime.prototype.daysinmonth\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Temporal/ZonedDateTime/daysInMonth\n    /// [temporal_rs-docs]: https://docs.rs/temporal_rs/latest/temporal_rs/struct.ZonedDateTime.html#method.days_in_month\n    fn get_days_in_month(this: &JsValue, _: &[JsValue], _: &mut Context) -> JsResult<JsValue> {\n        let object = this.as_object();\n        let zdt = object\n            .as_ref()\n            .and_then(JsObject::downcast_ref::<Self>)\n            .ok_or_else(|| {\n                JsNativeError::typ().with_message(\"the this object must be a ZonedDateTime object.\")\n            })?;\n\n        Ok(zdt.inner.days_in_month().into())\n    }\n\n    /// 6.3.26 get `Temporal.ZonedDateTime.prototype.daysInYear`\n    ///\n    /// More information:\n    ///\n    /// - [ECMAScript Temporal proposal][spec]\n    /// - [MDN reference][mdn]\n    /// - [`temporal_rs` documentation][temporal_rs-docs]\n    ///\n    /// [spec]: https://tc39.es/proposal-temporal/#sec-get-temporal.zoneddatetime.prototype.daysinyear\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Temporal/ZonedDateTime/daysInYear\n    /// [temporal_rs-docs]: https://docs.rs/temporal_rs/latest/temporal_rs/struct.ZonedDateTime.html#method.days_in_year\n    fn get_days_in_year(this: &JsValue, _: &[JsValue], _: &mut Context) -> JsResult<JsValue> {\n        let object = this.as_object();\n        let zdt = object\n            .as_ref()\n            .and_then(JsObject::downcast_ref::<Self>)\n            .ok_or_else(|| {\n                JsNativeError::typ().with_message(\"the this object must be a ZonedDateTime object.\")\n            })?;\n\n        Ok(zdt.inner.days_in_year().into())\n    }\n\n    /// 6.3.27 get `Temporal.ZonedDateTime.prototype.monthsInYear`\n    ///\n    /// More information:\n    ///\n    /// - [ECMAScript Temporal proposal][spec]\n    /// - [MDN reference][mdn]\n    /// - [`temporal_rs` documentation][temporal_rs-docs]\n    ///\n    /// [spec]: https://tc39.es/proposal-temporal/#sec-get-temporal.zoneddatetime.prototype.monthsinyear\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Temporal/ZonedDateTime/monthsInYear\n    /// [temporal_rs-docs]: https://docs.rs/temporal_rs/latest/temporal_rs/struct.ZonedDateTime.html#method.months_in_year\n    fn get_months_in_year(this: &JsValue, _: &[JsValue], _: &mut Context) -> JsResult<JsValue> {\n        let object = this.as_object();\n        let zdt = object\n            .as_ref()\n            .and_then(JsObject::downcast_ref::<Self>)\n            .ok_or_else(|| {\n                JsNativeError::typ().with_message(\"the this object must be a ZonedDateTime object.\")\n            })?;\n\n        Ok(zdt.inner.months_in_year().into())\n    }\n\n    /// 6.3.28 get `Temporal.ZonedDateTime.prototype.inLeapYear`\n    ///\n    /// More information:\n    ///\n    /// - [ECMAScript Temporal proposal][spec]\n    /// - [MDN reference][mdn]\n    /// - [`temporal_rs` documentation][temporal_rs-docs]\n    ///\n    /// [spec]: https://tc39.es/proposal-temporal/#sec-get-temporal.zoneddatetime.prototype.inleapyear\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Temporal/ZonedDateTime/inLeapYear\n    /// [temporal_rs-docs]: https://docs.rs/temporal_rs/latest/temporal_rs/struct.ZonedDateTime.html#method.in_leap_year\n    fn get_in_leap_year(this: &JsValue, _: &[JsValue], _: &mut Context) -> JsResult<JsValue> {\n        let object = this.as_object();\n        let zdt = object\n            .as_ref()\n            .and_then(JsObject::downcast_ref::<Self>)\n            .ok_or_else(|| {\n                JsNativeError::typ().with_message(\"the this object must be a ZonedDateTime object.\")\n            })?;\n\n        Ok(zdt.inner.in_leap_year().into())\n    }\n\n    /// 6.3.29 get Temporal.ZonedDateTime.prototype.offsetNanoseconds\n    ///\n    /// More information:\n    ///\n    /// - [ECMAScript Temporal proposal][spec]\n    /// - [MDN reference][mdn]\n    /// - [`temporal_rs` documentation][temporal_rs-docs]\n    ///\n    /// [spec]: https://tc39.es/proposal-temporal/#sec-get-temporal.zoneddatetime.prototype.offsetnanoseconds\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Temporal/ZonedDateTime/offsetNanoseconds\n    /// [temporal_rs-docs]: https://docs.rs/temporal_rs/latest/temporal_rs/struct.ZonedDateTime.html#method.offset_nanoseconds\n    fn get_offset_nanoseconds(this: &JsValue, _: &[JsValue], _: &mut Context) -> JsResult<JsValue> {\n        let object = this.as_object();\n        let zdt = object\n            .as_ref()\n            .and_then(JsObject::downcast_ref::<Self>)\n            .ok_or_else(|| {\n                JsNativeError::typ().with_message(\"the this object must be a ZonedDateTime object.\")\n            })?;\n\n        Ok(zdt.inner.offset_nanoseconds().into())\n    }\n\n    /// 6.3.30 get Temporal.ZonedDateTime.prototype.offset\n    ///\n    /// More information:\n    ///\n    /// - [ECMAScript Temporal proposal][spec]\n    /// - [MDN reference][mdn]\n    /// - [`temporal_rs` documentation][temporal_rs-docs]\n    ///\n    /// [spec]: https://tc39.es/proposal-temporal/#sec-get-temporal.zoneddatetime.prototype.offset\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Temporal/ZonedDateTime/offset\n    /// [temporal_rs-docs]: https://docs.rs/temporal_rs/latest/temporal_rs/struct.ZonedDateTime.html#method.offset\n    fn get_offset(this: &JsValue, _: &[JsValue], _: &mut Context) -> JsResult<JsValue> {\n        let object = this.as_object();\n        let zdt = object\n            .as_ref()\n            .and_then(JsObject::downcast_ref::<Self>)\n            .ok_or_else(|| {\n                JsNativeError::typ().with_message(\"the this object must be a ZonedDateTime object.\")\n            })?;\n\n        Ok(JsString::from(zdt.inner.offset()).into())\n    }\n}\n\n// ==== `ZonedDateTime` static methods implementation ====\n\nimpl ZonedDateTime {\n    /// 6.2.2 `Temporal.ZonedDateTime.from ( item [ , options ] )`\n    ///\n    /// More information:\n    ///\n    /// - [ECMAScript Temporal proposal][spec]\n    /// - [MDN reference][mdn]\n    ///\n    /// [spec]: https://tc39.es/proposal-temporal/#sec-temporal.zoneddatetime.from\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Temporal/ZonedDateTime/from\n    fn from(_: &JsValue, args: &[JsValue], context: &mut Context) -> JsResult<JsValue> {\n        // 1. Return ? ToTemporalZonedDateTime(item, options).\n        let item = args.get_or_undefined(0);\n        let options = args.get(1);\n        let inner = to_temporal_zoneddatetime(item, options, context)?;\n        create_temporal_zoneddatetime(inner, None, context).map(Into::into)\n    }\n\n    /// 6.2.3 `Temporal.ZonedDateTime.compare ( one, two )`\n    ///\n    /// More information:\n    ///\n    /// - [ECMAScript Temporal proposal][spec]\n    /// - [MDN reference][mdn]\n    /// - [`temporal_rs` documentation][temporal_rs-docs]\n    ///\n    /// [spec]: https://tc39.es/proposal-temporal/#sec-temporal.zoneddatetime.compare\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Temporal/ZonedDateTime/compare\n    /// [temporal_rs-docs]: https://docs.rs/temporal_rs/latest/temporal_rs/struct.ZonedDateTime.html#method.compare_instant\n    fn compare(_: &JsValue, args: &[JsValue], context: &mut Context) -> JsResult<JsValue> {\n        // 1. Return ? ToTemporalZonedDateTime(item, options).\n        let one = to_temporal_zoneddatetime(args.get_or_undefined(0), None, context)?;\n        let two = to_temporal_zoneddatetime(args.get_or_undefined(1), None, context)?;\n        Ok((one.compare_instant(&two) as i8).into())\n    }\n}\n\n// ==== `ZonedDateTime` methods implementation ====\n\nimpl ZonedDateTime {\n    /// 6.3.31 `Temporal.ZonedDateTime.prototype.with ( temporalZonedDateTimeLike [ , options ] )`\n    ///\n    /// More information:\n    ///\n    /// - [ECMAScript Temporal proposal][spec]\n    /// - [MDN reference][mdn]\n    /// - [`temporal_rs` documentation][temporal_rs-docs]\n    ///\n    /// [spec]: https://tc39.es/proposal-temporal/#sec-temporal.zoneddatetime.prototype.with\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Temporal/ZonedDateTime/with\n    /// [temporal_rs-docs]: https://docs.rs/temporal_rs/latest/temporal_rs/struct.ZonedDateTime.html#method.with\n    fn with(this: &JsValue, args: &[JsValue], context: &mut Context) -> JsResult<JsValue> {\n        // 1. Let zonedDateTime be the this value.\n        // 2. Perform ? RequireInternalSlot(zonedDateTime, [[InitializedTemporalZonedDateTime]]).\n        let object = this.as_object();\n        let zdt = object\n            .as_ref()\n            .and_then(JsObject::downcast_ref::<Self>)\n            .ok_or_else(|| {\n                JsNativeError::typ().with_message(\"the this object must be a ZonedDateTime object.\")\n            })?;\n        // 3. If ? IsPartialTemporalObject(temporalZonedDateTimeLike) is false, throw a TypeError exception.\n        let Some(obj) = is_partial_temporal_object(args.get_or_undefined(0), context)? else {\n            return Err(JsNativeError::typ()\n                .with_message(\"temporalZonedDateTimeLike was not a partial object\")\n                .into());\n        };\n        // 4. Let epochNs be zonedDateTime.[[EpochNanoseconds]].\n        // 5. Let timeZone be zonedDateTime.[[TimeZone]].\n        // 6. Let calendar be zonedDateTime.[[Calendar]].\n        // 7. Let offsetNanoseconds be GetOffsetNanosecondsFor(timeZone, epochNs).\n        // 8. Let isoDateTime be GetISODateTimeFor(timeZone, epochNs).\n        // 9. Let fields be ISODateToFields(calendar, isoDateTime.[[ISODate]], date).\n        // 10. Set fields.[[Hour]] to isoDateTime.[[Time]].[[Hour]].\n        // 11. Set fields.[[Minute]] to isoDateTime.[[Time]].[[Minute]].\n        // 12. Set fields.[[Second]] to isoDateTime.[[Time]].[[Second]].\n        // 13. Set fields.[[Millisecond]] to isoDateTime.[[Time]].[[Millisecond]].\n        // 14. Set fields.[[Microsecond]] to isoDateTime.[[Time]].[[Microsecond]].\n        // 15. Set fields.[[Nanosecond]] to isoDateTime.[[Time]].[[Nanosecond]].\n        // 16. Set fields.[[OffsetString]] to FormatUTCOffsetNanoseconds(offsetNanoseconds).\n        // 17. Let partialZonedDateTime be ? PrepareCalendarFields(calendar, temporalZonedDateTimeLike, « year, month, month-code, day », « hour, minute, second, millisecond, microsecond, nanosecond, offset », partial).\n        // 18. Set fields to CalendarMergeFields(calendar, fields, partialZonedDateTime).\n        let (fields, _) = to_zoned_date_time_fields(\n            &obj,\n            zdt.inner.calendar(),\n            ZdtFieldsType::NoTimeZone,\n            context,\n        )?;\n\n        // 19. Let resolvedOptions be ? GetOptionsObject(options).\n        let resolved_options = get_options_object(args.get_or_undefined(1))?;\n        // 20. Let disambiguation be ? GetTemporalDisambiguationOption(resolvedOptions).\n        let disambiguation =\n            get_option::<Disambiguation>(&resolved_options, js_string!(\"disambiguation\"), context)?;\n        // 21. Let offset be ? GetTemporalOffsetOption(resolvedOptions, prefer).\n        let offset =\n            get_option::<OffsetDisambiguation>(&resolved_options, js_string!(\"offset\"), context)?;\n        // 22. Let overflow be ? GetTemporalOverflowOption(resolvedOptions).\n        let overflow = get_option::<Overflow>(&resolved_options, js_string!(\"overflow\"), context)?;\n\n        let result = zdt.inner.with_with_provider(\n            fields,\n            disambiguation,\n            offset,\n            overflow,\n            context.timezone_provider(),\n        )?;\n        create_temporal_zoneddatetime(result, None, context).map(Into::into)\n    }\n\n    /// 6.3.32 `Temporal.ZonedDateTime.prototype.withPlainTime ( [ plainTimeLike ] )`\n    ///\n    /// More information:\n    ///\n    /// - [ECMAScript Temporal proposal][spec]\n    /// - [MDN reference][mdn]\n    /// - [`temporal_rs` documentation][temporal_rs-docs]\n    ///\n    /// [spec]: https://tc39.es/proposal-temporal/#sec-temporal.zoneddatetime.prototype.withPlainTime\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Temporal/ZonedDateTime/withPlainTime\n    /// [temporal_rs-docs]: https://docs.rs/temporal_rs/latest/temporal_rs/struct.ZonedDateTime.html#method.with_plain_time\n    fn with_plain_time(\n        this: &JsValue,\n        args: &[JsValue],\n        context: &mut Context,\n    ) -> JsResult<JsValue> {\n        let object = this.as_object();\n        let zdt = object\n            .as_ref()\n            .and_then(JsObject::downcast_ref::<Self>)\n            .ok_or_else(|| {\n                JsNativeError::typ().with_message(\"the this object must be a ZonedDateTime object.\")\n            })?;\n\n        let time = args\n            .get_or_undefined(0)\n            .map(|v| to_temporal_time(v, None, context))\n            .transpose()?;\n\n        let inner = zdt\n            .inner\n            .with_plain_time_and_provider(time, context.timezone_provider())?;\n        create_temporal_zoneddatetime(inner, None, context).map(Into::into)\n    }\n\n    /// 6.3.33 `Temporal.ZonedDateTime.prototype.withTimeZone ( timeZoneLike )`\n    ///\n    /// More information:\n    ///\n    /// - [ECMAScript Temporal proposal][spec]\n    /// - [MDN reference][mdn]\n    /// - [`temporal_rs` documentation][temporal_rs-docs]\n    ///\n    /// [spec]: https://tc39.es/proposal-temporal/#sec-temporal.zoneddatetime.prototype.withtimezone\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Temporal/ZonedDateTime/withTimeZone\n    /// [temporal_rs-docs]: https://docs.rs/temporal_rs/latest/temporal_rs/struct.ZonedDateTime.html#method.with_timezone\n    fn with_timezone(this: &JsValue, args: &[JsValue], context: &mut Context) -> JsResult<JsValue> {\n        let object = this.as_object();\n        let zdt = object\n            .as_ref()\n            .and_then(JsObject::downcast_ref::<Self>)\n            .ok_or_else(|| {\n                JsNativeError::typ().with_message(\"the this object must be a ZonedDateTime object.\")\n            })?;\n\n        let timezone = to_temporal_timezone_identifier(args.get_or_undefined(0), context)?;\n\n        let inner = zdt\n            .inner\n            .with_time_zone_with_provider(timezone, context.timezone_provider())?;\n        create_temporal_zoneddatetime(inner, None, context).map(Into::into)\n    }\n\n    /// 6.3.34 `Temporal.ZonedDateTime.prototype.withCalendar ( calendarLike )`\n    ///\n    /// More information:\n    ///\n    /// - [ECMAScript Temporal proposal][spec]\n    /// - [MDN reference][mdn]\n    /// - [`temporal_rs` documentation][temporal_rs-docs]\n    ///\n    /// [spec]: https://tc39.es/proposal-temporal/#sec-temporal.zoneddatetime.prototype.withcalendar\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Temporal/ZonedDateTime/withCalendar\n    /// [temporal_rs-docs]: https://docs.rs/temporal_rs/latest/temporal_rs/struct.ZonedDateTime.html#method.with_calendar\n    fn with_calendar(this: &JsValue, args: &[JsValue], context: &mut Context) -> JsResult<JsValue> {\n        let object = this.as_object();\n        let zdt = object\n            .as_ref()\n            .and_then(JsObject::downcast_ref::<Self>)\n            .ok_or_else(|| {\n                JsNativeError::typ().with_message(\"the this object must be a ZonedDateTime object.\")\n            })?;\n\n        let calendar = to_temporal_calendar_identifier(args.get_or_undefined(0))?;\n\n        let inner = zdt.inner.with_calendar(calendar);\n        create_temporal_zoneddatetime(inner, None, context).map(Into::into)\n    }\n\n    /// 6.3.35 `Temporal.ZonedDateTime.prototype.add ( temporalDurationLike [ , options ] )`\n    ///\n    /// More information:\n    ///\n    /// - [ECMAScript Temporal proposal][spec]\n    /// - [MDN reference][mdn]\n    /// - [`temporal_rs` documentation][temporal_rs-docs]\n    ///\n    /// [spec]: https://tc39.es/proposal-temporal/#sec-temporal.zoneddatetime.prototype.add\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Temporal/ZonedDateTime/add\n    /// [temporal_rs-docs]: https://docs.rs/temporal_rs/latest/temporal_rs/struct.ZonedDateTime.html#method.add\n    fn add(this: &JsValue, args: &[JsValue], context: &mut Context) -> JsResult<JsValue> {\n        let object = this.as_object();\n        let zdt = object\n            .as_ref()\n            .and_then(JsObject::downcast_ref::<Self>)\n            .ok_or_else(|| {\n                JsNativeError::typ().with_message(\"the this object must be a ZonedDateTime object.\")\n            })?;\n\n        let duration = to_temporal_duration(args.get_or_undefined(0), context)?;\n\n        let options = get_options_object(args.get_or_undefined(1))?;\n        let overflow = get_option::<Overflow>(&options, js_string!(\"overflow\"), context)?;\n\n        let result =\n            zdt.inner\n                .add_with_provider(&duration, overflow, context.timezone_provider())?;\n        create_temporal_zoneddatetime(result, None, context).map(Into::into)\n    }\n\n    /// 6.3.36 `Temporal.ZonedDateTime.prototype.subtract ( temporalDurationLike [ , options ] )`\n    ///\n    /// More information:\n    ///\n    /// - [ECMAScript Temporal proposal][spec]\n    /// - [MDN reference][mdn]\n    /// - [`temporal_rs` documentation][temporal_rs-docs]\n    ///\n    /// [spec]: https://tc39.es/proposal-temporal/#sec-temporal.zoneddatetime.prototype.subtract\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Temporal/ZonedDateTime/subtract\n    /// [temporal_rs-docs]: https://docs.rs/temporal_rs/latest/temporal_rs/struct.ZonedDateTime.html#method.subtract\n    fn subtract(this: &JsValue, args: &[JsValue], context: &mut Context) -> JsResult<JsValue> {\n        let object = this.as_object();\n        let zdt = object\n            .as_ref()\n            .and_then(JsObject::downcast_ref::<Self>)\n            .ok_or_else(|| {\n                JsNativeError::typ().with_message(\"the this object must be a ZonedDateTime object.\")\n            })?;\n\n        let duration = to_temporal_duration(args.get_or_undefined(0), context)?;\n\n        let options = get_options_object(args.get_or_undefined(1))?;\n        let overflow = get_option::<Overflow>(&options, js_string!(\"overflow\"), context)?;\n\n        let result =\n            zdt.inner\n                .subtract_with_provider(&duration, overflow, context.timezone_provider())?;\n        create_temporal_zoneddatetime(result, None, context).map(Into::into)\n    }\n\n    /// 6.3.37 `Temporal.ZonedDateTime.prototype.until ( other [ , options ] )`\n    ///\n    /// More information:\n    ///\n    /// - [ECMAScript Temporal proposal][spec]\n    /// - [MDN reference][mdn]\n    /// - [`temporal_rs` documentation][temporal_rs-docs]\n    ///\n    /// [spec]: https://tc39.es/proposal-temporal/#sec-temporal.zoneddatetime.prototype.until\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Temporal/ZonedDateTime/until\n    /// [temporal_rs-docs]: https://docs.rs/temporal_rs/latest/temporal_rs/struct.ZonedDateTime.html#method.until\n    fn until(this: &JsValue, args: &[JsValue], context: &mut Context) -> JsResult<JsValue> {\n        let object = this.as_object();\n        let zdt = object\n            .as_ref()\n            .and_then(JsObject::downcast_ref::<Self>)\n            .ok_or_else(|| {\n                JsNativeError::typ().with_message(\"the this object must be a ZonedDateTime object.\")\n            })?;\n\n        let other = to_temporal_zoneddatetime(args.get_or_undefined(0), None, context)?;\n\n        let options = get_options_object(args.get_or_undefined(1))?;\n        let settings = get_difference_settings(&options, context)?;\n\n        let result =\n            zdt.inner\n                .until_with_provider(&other, settings, context.timezone_provider())?;\n        create_temporal_duration(result, None, context).map(Into::into)\n    }\n\n    /// 6.3.38 `Temporal.ZonedDateTime.prototype.since ( other [ , options ] )`\n    ///\n    /// More information:\n    ///\n    /// - [ECMAScript Temporal proposal][spec]\n    /// - [MDN reference][mdn]\n    /// - [`temporal_rs` documentation][temporal_rs-docs]\n    ///\n    /// [spec]: https://tc39.es/proposal-temporal/#sec-temporal.zoneddatetime.prototype.since\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Temporal/ZonedDateTime/since\n    /// [temporal_rs-docs]: https://docs.rs/temporal_rs/latest/temporal_rs/struct.ZonedDateTime.html#method.since\n    fn since(this: &JsValue, args: &[JsValue], context: &mut Context) -> JsResult<JsValue> {\n        let object = this.as_object();\n        let zdt = object\n            .as_ref()\n            .and_then(JsObject::downcast_ref::<Self>)\n            .ok_or_else(|| {\n                JsNativeError::typ().with_message(\"the this object must be a ZonedDateTime object.\")\n            })?;\n\n        let other = to_temporal_zoneddatetime(args.get_or_undefined(0), None, context)?;\n\n        let options = get_options_object(args.get_or_undefined(1))?;\n        let settings = get_difference_settings(&options, context)?;\n\n        let result =\n            zdt.inner\n                .since_with_provider(&other, settings, context.timezone_provider())?;\n        create_temporal_duration(result, None, context).map(Into::into)\n    }\n\n    /// 6.3.39 `Temporal.ZonedDateTime.prototype.round ( roundTo )`\n    ///\n    /// More information:\n    ///\n    /// - [ECMAScript Temporal proposal][spec]\n    /// - [MDN reference][mdn]\n    /// - [`temporal_rs` documentation][temporal_rs-docs]\n    ///\n    /// [spec]: https://tc39.es/proposal-temporal/#sec-temporal.zoneddatetime.prototype.round\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Temporal/ZonedDateTime/round\n    /// [temporal_rs-docs]: https://docs.rs/temporal_rs/latest/temporal_rs/struct.ZonedDateTime.html#method.round\n    fn round(this: &JsValue, args: &[JsValue], context: &mut Context) -> JsResult<JsValue> {\n        // 1. Let zonedDateTime be the this value.\n        // 2. Perform ? RequireInternalSlot(zonedDateTime, [[InitializedTemporalZonedDateTime]]).\n        let object = this.as_object();\n        let zdt = object\n            .as_ref()\n            .and_then(JsObject::downcast_ref::<Self>)\n            .ok_or_else(|| {\n                JsNativeError::typ().with_message(\"the this object must be a ZonedDateTime object.\")\n            })?;\n\n        let round_to = match args.first().map(JsValue::variant) {\n            // 3. If roundTo is undefined, then\n            None | Some(JsVariant::Undefined) => {\n                // a. Throw a TypeError exception.\n                return Err(JsNativeError::typ()\n                    .with_message(\"roundTo cannot be undefined.\")\n                    .into());\n            }\n            // 4. If Type(roundTo) is String, then\n            Some(JsVariant::String(rt)) => {\n                // a. Let paramString be roundTo.\n                let param_string = rt.clone();\n                // b. Set roundTo to OrdinaryObjectCreate(null).\n                let new_round_to = JsObject::with_null_proto();\n                // c. Perform ! CreateDataPropertyOrThrow(roundTo, \"smallestUnit\", paramString).\n                new_round_to.create_data_property_or_throw(\n                    js_string!(\"smallestUnit\"),\n                    param_string,\n                    context,\n                )?;\n                new_round_to\n            }\n            // 5. Else,\n            Some(round_to) => {\n                // a. Set roundTo to ? GetOptionsObject(roundTo).\n                get_options_object(&JsValue::from(round_to))?\n            }\n        };\n\n        // 6. NOTE: The following steps read options and perform independent validation\n        // in alphabetical order (GetRoundingIncrementOption reads \"roundingIncrement\"\n        // and GetRoundingModeOption reads \"roundingMode\").\n        let mut options = RoundingOptions::default();\n\n        // 7. Let roundingIncrement be ? GetRoundingIncrementOption(roundTo).\n        options.increment =\n            get_option::<RoundingIncrement>(&round_to, js_string!(\"roundingIncrement\"), context)?;\n\n        // 8. Let roundingMode be ? GetRoundingModeOption(roundTo, half-expand).\n        options.rounding_mode =\n            get_option::<RoundingMode>(&round_to, js_string!(\"roundingMode\"), context)?;\n\n        // 9. Let smallestUnit be ? GetTemporalUnitValuedOption(roundTo, \"smallestUnit\", time, required, « day »).\n        options.smallest_unit = get_temporal_unit(\n            &round_to,\n            js_string!(\"smallestUnit\"),\n            TemporalUnitGroup::Time,\n            Some(vec![Unit::Day]),\n            context,\n        )?;\n\n        let result = zdt\n            .inner\n            .round_with_provider(options, context.timezone_provider())?;\n        create_temporal_zoneddatetime(result, None, context).map(Into::into)\n    }\n\n    /// 6.3.40 `Temporal.ZonedDateTime.prototype.equals ( other )`\n    ///\n    /// More information:\n    ///\n    /// - [ECMAScript Temporal proposal][spec]\n    /// - [MDN reference][mdn]\n    /// - [`temporal_rs` documentation][temporal_rs-docs]\n    ///\n    /// [spec]: https://tc39.es/proposal-temporal/#sec-temporal.zoneddatetime.prototype.equals\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Temporal/ZonedDateTime/equals\n    /// [temporal_rs-docs]: https://docs.rs/temporal_rs/latest/temporal_rs/struct.ZonedDateTime.html#impl-PartialEq-for-ZonedDateTime\n    fn equals(this: &JsValue, args: &[JsValue], context: &mut Context) -> JsResult<JsValue> {\n        let object = this.as_object();\n        let zdt = object\n            .as_ref()\n            .and_then(JsObject::downcast_ref::<Self>)\n            .ok_or_else(|| {\n                JsNativeError::typ().with_message(\"the this object must be a ZonedDateTime object.\")\n            })?;\n\n        let other = to_temporal_zoneddatetime(args.get_or_undefined(0), None, context)?;\n        Ok(zdt\n            .inner\n            .equals_with_provider(&other, context.timezone_provider())?\n            .into())\n    }\n\n    /// 6.3.41 `Temporal.ZonedDateTime.prototype.toString ( [ options ] )`\n    ///\n    /// More information:\n    ///\n    /// - [ECMAScript Temporal proposal][spec]\n    /// - [MDN reference][mdn]\n    /// - [`temporal_rs` documentation][temporal_rs-docs]\n    ///\n    /// [spec]: https://tc39.es/proposal-temporal/#sec-temporal.zoneddatetime.prototype.tostring\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Temporal/ZonedDateTime/toString\n    /// [temporal_rs-docs]: https://docs.rs/temporal_rs/latest/temporal_rs/struct.ZonedDateTime.html#method.to_ixdtf_string\n    fn to_string(this: &JsValue, args: &[JsValue], context: &mut Context) -> JsResult<JsValue> {\n        let object = this.as_object();\n        let zdt = object\n            .as_ref()\n            .and_then(JsObject::downcast_ref::<Self>)\n            .ok_or_else(|| {\n                JsNativeError::typ().with_message(\"the this object must be a ZonedDateTime object.\")\n            })?;\n\n        let options = get_options_object(args.get_or_undefined(0))?;\n\n        let show_calendar =\n            get_option::<DisplayCalendar>(&options, js_string!(\"calendarName\"), context)?\n                .unwrap_or(DisplayCalendar::Auto);\n        let precision = get_digits_option(&options, context)?;\n        let show_offset = get_option::<DisplayOffset>(&options, js_string!(\"offset\"), context)?\n            .unwrap_or(DisplayOffset::Auto);\n        let rounding_mode =\n            get_option::<RoundingMode>(&options, js_string!(\"roundingMode\"), context)?;\n        let smallest_unit = get_option::<Unit>(&options, js_string!(\"smallestUnit\"), context)?;\n        // NOTE: There may be an order-of-operations here due to a check on Unit groups and smallest_unit value.\n        let display_timezone =\n            get_option::<DisplayTimeZone>(&options, js_string!(\"timeZoneName\"), context)?\n                .unwrap_or(DisplayTimeZone::Auto);\n\n        let options = ToStringRoundingOptions {\n            precision,\n            smallest_unit,\n            rounding_mode,\n        };\n        let ixdtf = zdt.inner.to_ixdtf_string_with_provider(\n            show_offset,\n            display_timezone,\n            show_calendar,\n            options,\n            context.timezone_provider(),\n        )?;\n\n        Ok(JsString::from(ixdtf).into())\n    }\n\n    /// 6.3.42 `Temporal.ZonedDateTime.prototype.toLocaleString ( [ locales [ , options ] ] )`\n    ///\n    /// More information:\n    ///\n    /// - [ECMAScript Temporal proposal][spec]\n    /// - [MDN reference][mdn]\n    ///\n    /// [spec]: https://tc39.es/proposal-temporal/#sec-temporal.zoneddatetime.prototype.tolocalestring\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Temporal/ZonedDateTime/toLocaleString\n    fn to_locale_string(this: &JsValue, _: &[JsValue], context: &mut Context) -> JsResult<JsValue> {\n        // TODO: Update for ECMA-402 compliance\n        let object = this.as_object();\n        let zdt = object\n            .as_ref()\n            .and_then(JsObject::downcast_ref::<Self>)\n            .ok_or_else(|| {\n                JsNativeError::typ().with_message(\"the this object must be a ZonedDateTime object.\")\n            })?;\n\n        let ixdtf = zdt.inner.to_ixdtf_string_with_provider(\n            DisplayOffset::Auto,\n            DisplayTimeZone::Auto,\n            DisplayCalendar::Auto,\n            ToStringRoundingOptions::default(),\n            context.timezone_provider(),\n        )?;\n\n        Ok(JsString::from(ixdtf).into())\n    }\n\n    /// 6.3.43 `Temporal.ZonedDateTime.prototype.toJSON ( )`\n    ///\n    /// More information:\n    ///\n    /// - [ECMAScript Temporal proposal][spec]\n    /// - [MDN reference][mdn]\n    ///\n    /// [spec]: https://tc39.es/proposal-temporal/#sec-temporal.zoneddatetime.prototype.tojson\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Temporal/ZonedDateTime/toJSON\n    fn to_json(this: &JsValue, _: &[JsValue], context: &mut Context) -> JsResult<JsValue> {\n        let object = this.as_object();\n        let zdt = object\n            .as_ref()\n            .and_then(JsObject::downcast_ref::<Self>)\n            .ok_or_else(|| {\n                JsNativeError::typ().with_message(\"the this object must be a ZonedDateTime object.\")\n            })?;\n\n        let ixdtf = zdt.inner.to_ixdtf_string_with_provider(\n            DisplayOffset::Auto,\n            DisplayTimeZone::Auto,\n            DisplayCalendar::Auto,\n            ToStringRoundingOptions::default(),\n            context.timezone_provider(),\n        )?;\n\n        Ok(JsString::from(ixdtf).into())\n    }\n\n    /// 6.3.44 `Temporal.ZonedDateTime.prototype.valueOf ( )`\n    ///\n    /// More information:\n    ///\n    /// - [ECMAScript Temporal proposal][spec]\n    /// - [MDN reference][mdn]\n    ///\n    /// [spec]: https://tc39.es/proposal-temporal/#sec-temporal.zoneddatetime.prototype.valueof\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Temporal/ZonedDateTime/valueOf\n    pub(crate) fn value_of(_this: &JsValue, _: &[JsValue], _: &mut Context) -> JsResult<JsValue> {\n        Err(JsNativeError::typ()\n            .with_message(\"`valueOf` not supported by Temporal built-ins. See 'compare', 'equals', or `toString`\")\n            .into())\n    }\n\n    /// 6.3.45 `Temporal.ZonedDateTime.prototype.startOfDay ( )`\n    ///\n    /// More information:\n    ///\n    /// - [ECMAScript Temporal proposal][spec]\n    /// - [MDN reference][mdn]\n    /// - [`temporal_rs` documentation][temporal_rs-docs]\n    ///\n    /// [spec]: https://tc39.es/proposal-temporal/#sec-temporal.zoneddatetime.prototype.startofday\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Temporal/ZonedDateTime/startOfDay\n    /// [temporal_rs-docs]: https://docs.rs/temporal_rs/latest/temporal_rs/struct.ZonedDateTime.html#method.start_of_day\n    fn start_of_day(this: &JsValue, _: &[JsValue], context: &mut Context) -> JsResult<JsValue> {\n        let object = this.as_object();\n        let zdt = object\n            .as_ref()\n            .and_then(JsObject::downcast_ref::<Self>)\n            .ok_or_else(|| {\n                JsNativeError::typ().with_message(\"the this object must be a ZonedDateTime object.\")\n            })?;\n\n        let new = zdt\n            .inner\n            .start_of_day_with_provider(context.timezone_provider())?;\n        create_temporal_zoneddatetime(new, None, context).map(Into::into)\n    }\n\n    /// 6.3.46 `Temporal.ZonedDateTime.prototype.getTimeZoneTransition ( directionParam )`\n    ///\n    /// More information:\n    ///\n    /// - [ECMAScript Temporal proposal][spec]\n    /// - [MDN reference][mdn]\n    /// - [`temporal_rs` documentation][temporal_rs-docs]\n    ///\n    /// [spec]: https://tc39.es/proposal-temporal/#sec-temporal.zoneddatetime.prototype.gettimezonetransition\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Temporal/ZonedDateTime/getTimeZoneTransition\n    /// [temporal_rs-docs]: https://docs.rs/temporal_rs/latest/temporal_rs/struct.ZonedDateTime.html#method.get_time_zone_transition_with_provider\n    fn get_time_zone_transition(\n        this: &JsValue,\n        args: &[JsValue],\n        context: &mut Context,\n    ) -> JsResult<JsValue> {\n        // 1. Let zonedDateTime be the this value.\n        // 2. Perform ? RequireInternalSlot(zonedDateTime, [[InitializedTemporalZonedDateTime]]).\n        // 3. Let timeZone be zonedDateTime.[[TimeZone]].\n        let object = this.as_object();\n        let zdt = object\n            .as_ref()\n            .and_then(JsObject::downcast_ref::<Self>)\n            .ok_or_else(|| {\n                JsNativeError::typ().with_message(\"the this object must be a ZonedDateTime object.\")\n            })?;\n\n        let direction_param = args.get_or_undefined(0);\n        // 4. If directionParam is undefined, throw a TypeError exception.\n        if direction_param.is_undefined() {\n            return Err(JsNativeError::typ()\n                .with_message(\"getTimeZoneTransition directionParam cannot be undefined.\")\n                .into());\n        }\n        // 5. If directionParam is a String, then\n        let options_obj = if let Some(param_str) = direction_param.as_string() {\n            // a. Let paramString be directionParam.\n            // b. Set directionParam to OrdinaryObjectCreate(null).\n            let obj = JsObject::with_null_proto();\n            // c. Perform ! CreateDataPropertyOrThrow(directionParam, \"direction\", paramString).\n            obj.create_data_property_or_throw(\n                js_string!(\"direction\"),\n                JsValue::from(param_str.clone()),\n                context,\n            )?;\n            obj\n        // 6. Else,\n        } else {\n            // a. Set directionParam to ? GetOptionsObject(directionParam).\n            get_options_object(direction_param)?\n        };\n\n        // TODO: step 7\n        // 7. Let direction be ? GetDirectionOption(directionParam).\n        let direction =\n            get_option::<TransitionDirection>(&options_obj, js_string!(\"direction\"), context)?\n                .ok_or_else(|| {\n                    JsNativeError::range().with_message(\"direction option is required.\")\n                })?;\n\n        // Step 8-12\n        let result = zdt\n            .inner\n            .get_time_zone_transition_with_provider(direction, context.timezone_provider())?;\n\n        match result {\n            Some(zdt) => create_temporal_zoneddatetime(zdt, None, context).map(Into::into),\n            None => Ok(JsValue::null()),\n        }\n    }\n\n    /// 6.3.47 `Temporal.ZonedDateTime.prototype.toInstant ( )`\n    ///\n    /// More information:\n    ///\n    /// - [ECMAScript Temporal proposal][spec]\n    /// - [MDN reference][mdn]\n    /// - [`temporal_rs` documentation][temporal_rs-docs]\n    ///\n    /// [spec]: https://tc39.es/proposal-temporal/#sec-temporal.zoneddatetime.prototype.toinstant\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Temporal/ZonedDateTime/toInstant\n    /// [temporal_rs-docs]: https://docs.rs/temporal_rs/latest/temporal_rs/struct.ZonedDateTime.html#method.to_instant\n    fn to_instant(this: &JsValue, _: &[JsValue], context: &mut Context) -> JsResult<JsValue> {\n        let object = this.as_object();\n        let zdt = object\n            .as_ref()\n            .and_then(JsObject::downcast_ref::<Self>)\n            .ok_or_else(|| {\n                JsNativeError::typ().with_message(\"the this object must be a ZonedDateTime object.\")\n            })?;\n\n        create_temporal_instant(zdt.inner.to_instant(), None, context)\n    }\n\n    /// 6.3.48 `Temporal.ZonedDateTime.prototype.toPlainDate ( )`\n    ///\n    /// More information:\n    ///\n    /// - [ECMAScript Temporal proposal][spec]\n    /// - [MDN reference][mdn]\n    /// - [`temporal_rs` documentation][temporal_rs-docs]\n    ///\n    /// [spec]: https://tc39.es/proposal-temporal/#sec-temporal.zoneddatetime.prototype.toplaindate\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Temporal/ZonedDateTime/toPlainDate\n    /// [temporal_rs-docs]: https://docs.rs/temporal_rs/latest/temporal_rs/struct.ZonedDateTime.html#method.to_plain_date\n    fn to_plain_date(this: &JsValue, _: &[JsValue], context: &mut Context) -> JsResult<JsValue> {\n        let object = this.as_object();\n        let zdt = object\n            .as_ref()\n            .and_then(JsObject::downcast_ref::<Self>)\n            .ok_or_else(|| {\n                JsNativeError::typ().with_message(\"the this object must be a ZonedDateTime object.\")\n            })?;\n\n        let inner = zdt.inner.to_plain_date();\n        create_temporal_date(inner, None, context).map(Into::into)\n    }\n\n    /// 6.3.49 `Temporal.ZonedDateTime.prototype.toPlainTime ( )`\n    ///\n    /// More information:\n    ///\n    /// - [ECMAScript Temporal proposal][spec]\n    /// - [MDN reference][mdn]\n    /// - [`temporal_rs` documentation][temporal_rs-docs]\n    ///\n    /// [spec]: https://tc39.es/proposal-temporal/#sec-temporal.zoneddatetime.prototype.toplaintime\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Temporal/ZonedDateTime/toPlainTime\n    /// [temporal_rs-docs]: https://docs.rs/temporal_rs/latest/temporal_rs/struct.ZonedDateTime.html#method.to_plain_time\n    fn to_plain_time(this: &JsValue, _: &[JsValue], context: &mut Context) -> JsResult<JsValue> {\n        let object = this.as_object();\n        let zdt = object\n            .as_ref()\n            .and_then(JsObject::downcast_ref::<Self>)\n            .ok_or_else(|| {\n                JsNativeError::typ().with_message(\"the this object must be a ZonedDateTime object.\")\n            })?;\n\n        let new = zdt.inner.to_plain_time();\n        create_temporal_time(new, None, context).map(Into::into)\n    }\n\n    /// 6.3.50 `Temporal.ZonedDateTime.prototype.toPlainDateTime ( )`\n    ///\n    /// More information:\n    ///\n    /// - [ECMAScript Temporal proposal][spec]\n    /// - [MDN reference][mdn]\n    /// - [`temporal_rs` documentation][temporal_rs-docs]\n    ///\n    /// [spec]: https://tc39.es/proposal-temporal/#sec-temporal.zoneddatetime.prototype.toplaindatetime\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Temporal/ZonedDateTime/toPlainDateTime\n    /// [temporal_rs-docs]: https://docs.rs/temporal_rs/latest/temporal_rs/struct.ZonedDateTime.html#method.to_plain_datetime\n    fn to_plain_date_time(\n        this: &JsValue,\n        _: &[JsValue],\n        context: &mut Context,\n    ) -> JsResult<JsValue> {\n        let object = this.as_object();\n        let zdt = object\n            .as_ref()\n            .and_then(JsObject::downcast_ref::<Self>)\n            .ok_or_else(|| {\n                JsNativeError::typ().with_message(\"the this object must be a ZonedDateTime object.\")\n            })?;\n\n        let new = zdt.inner.to_plain_date_time();\n        create_temporal_datetime(new, None, context).map(Into::into)\n    }\n}\n\n// ==== ZonedDateTime Abstract Operations ====\n\n/// 6.5.3 `CreateTemporalZonedDateTime ( epochNanoseconds, timeZone, calendar [ , newTarget ] )`\npub(crate) fn create_temporal_zoneddatetime(\n    inner: ZonedDateTimeInner,\n    new_target: Option<&JsValue>,\n    context: &mut Context,\n) -> JsResult<JsObject> {\n    // 1. Assert: IsValidEpochNanoseconds(epochNanoseconds) is true.\n    // 2. If newTarget is not present, set newTarget to %Temporal.ZonedDateTime%.\n    let new_target = new_target.cloned().unwrap_or(\n        context\n            .realm()\n            .intrinsics()\n            .constructors()\n            .zoned_date_time()\n            .constructor()\n            .into(),\n    );\n    // 3. Let object be ? OrdinaryCreateFromConstructor(newTarget, \"%Temporal.ZonedDateTime.prototype%\", « [[InitializezdtemporalZonedDateTime]], [[EpochNanoseconds]], [[TimeZone]], [[Calendar]] »).\n    let prototype = get_prototype_from_constructor(\n        &new_target,\n        StandardConstructors::zoned_date_time,\n        context,\n    )?;\n    // 4. Set object.[[EpochNanoseconds]] to epochNanoseconds.\n    // 5. Set object.[[TimeZone]] to timeZone.\n    // 6. Set object.[[Calendar]] to calendar.\n    let obj = JsObject::from_proto_and_data(prototype, ZonedDateTime::new(inner));\n\n    // 7. Return object.\n    Ok(obj)\n}\n\n/// 6.5.2 `ToTemporalZonedDateTime ( item [ , options ] )`\npub(crate) fn to_temporal_zoneddatetime(\n    value: &JsValue,\n    options: Option<&JsValue>,\n    context: &mut Context,\n) -> JsResult<ZonedDateTimeInner> {\n    // 1. If options is not present, set options to undefined.\n    // 2. Let offsetBehaviour be option.\n    // 3. Let matchBehaviour be match-exactly.\n    // 4. If item is an Object, then\n    match value.variant() {\n        JsVariant::Object(object) => {\n            // a. If item has an [[InitializedTemporalZonedDateTime]] internal slot, then\n            if let Some(zdt) = object.downcast_ref::<ZonedDateTime>() {\n                // i. NOTE: The following steps, and similar ones below, read options\n                // and perform independent validation in alphabetical order\n                // (GetTemporalDisambiguationOption reads \"disambiguation\", GetTemporalOffsetOption\n                // reads \"offset\", and GetTemporalOverflowOption reads \"overflow\").\n                // ii. Let resolvedOptions be ? GetOptionsObject(options).\n                let options = get_options_object(options.unwrap_or(&JsValue::undefined()))?;\n                // iii. Perform ? GetTemporalDisambiguationOption(resolvedOptions).\n                let _disambiguation =\n                    get_option::<Disambiguation>(&options, js_string!(\"disambiguation\"), context)?\n                        .unwrap_or(Disambiguation::Compatible);\n                // iv. Perform ? GetTemporalOffsetOption(resolvedOptions, reject).\n                let _offset_option =\n                    get_option::<OffsetDisambiguation>(&options, js_string!(\"offset\"), context)?\n                        .unwrap_or(OffsetDisambiguation::Reject);\n                // v. Perform ? GetTemporalOverflowOption(resolvedOptions).\n                let _overflow = get_option::<Overflow>(&options, js_string!(\"overflow\"), context)?\n                    .unwrap_or_default();\n                // vi. Return ! CreateTemporalZonedDateTime(item.[[EpochNanoseconds]], item.[[TimeZone]], item.[[Calendar]]).\n                return Ok(zdt.inner.as_ref().clone());\n            }\n            let partial = to_partial_zoneddatetime(&object, context)?;\n            // f. If offsetString is unset, the\n            // i. Set offsetBehaviour to wall.\n            // g. Let resolvedOptions be ? GetOptionsObject(options).\n            let options = get_options_object(options.unwrap_or(&JsValue::undefined()))?;\n            // h. Let disambiguation be ? GetTemporalDisambiguationOption(resolvedOptions).\n            let disambiguation =\n                get_option::<Disambiguation>(&options, js_string!(\"disambiguation\"), context)?;\n            // i. Let offsetOption be ? GetTemporalOffsetOption(resolvedOptions, reject).\n            let offset_option =\n                get_option::<OffsetDisambiguation>(&options, js_string!(\"offset\"), context)?;\n            // j. Let overflow be ? GetTemporalOverflowOption(resolvedOptions).\n            let overflow = get_option::<Overflow>(&options, js_string!(\"overflow\"), context)?;\n            // k. Let result be ? InterpretTemporalDateTimeFields(calendar, fields, overflow).\n            // l. Let isoDate be result.[[ISODate]].\n            // m. Let time be result.[[Time]].\n            Ok(ZonedDateTimeInner::from_partial_with_provider(\n                partial,\n                overflow,\n                disambiguation,\n                offset_option,\n                context.timezone_provider(),\n            )?)\n        }\n        JsVariant::String(zdt_source) => {\n            // b. Let result be ? ParseISODateTime(item, « TemporalDateTimeString[+Zoned] »).\n            let parsed = ParsedZonedDateTime::from_utf8_with_provider(\n                zdt_source.to_std_string_escaped().as_bytes(),\n                context.timezone_provider(),\n            )?;\n            // c. Let annotation be result.[[TimeZone]].[[TimeZoneAnnotation]].\n            // d. Assert: annotation is not empty.\n            // e. Let timeZone be ? ToTemporalTimeZoneIdentifier(annotation).\n            // f. Let offsetString be result.[[TimeZone]].[[OffsetString]].\n            // g. If result.[[TimeZone]].[[Z]] is true, then\n            // i. Set offsetBehaviour to exact.\n            // h. Else if offsetString is empty, then\n            // i. Set offsetBehaviour to wall.\n            // i. Let calendar be result.[[Calendar]].\n            // j. If calendar is empty, set calendar to \"iso8601\".\n            // k. Set calendar to ? CanonicalizeCalendar(calendar).\n            // l. Set matchBehaviour to match-minutes.\n            // m. Let resolvedOptions be ? GetOptionsObject(options).\n            let options = get_options_object(options.unwrap_or(&JsValue::undefined()))?;\n            // n. Let disambiguation be ? GetTemporalDisambiguationOption(resolvedOptions).\n            let disambiguation =\n                get_option::<Disambiguation>(&options, js_string!(\"disambiguation\"), context)?\n                    .unwrap_or(Disambiguation::Compatible);\n            // o. Let offsetOption be ? GetTemporalOffsetOption(resolvedOptions, reject).\n            let offset_option =\n                get_option::<OffsetDisambiguation>(&options, js_string!(\"offset\"), context)?\n                    .unwrap_or(OffsetDisambiguation::Reject);\n            // p. Perform ? GetTemporalOverflowOption(resolvedOptions).\n            let _overflow = get_option::<Overflow>(&options, js_string!(\"overflow\"), context)?;\n            // q. Let isoDate be CreateISODateRecord(result.[[Year]], result.[[Month]], result.[[Day]]).\n            // r. Let time be result.[[Time]].\n            // 6. Let offsetNanoseconds be 0.\n            // 7. If offsetBehaviour is option, then\n            //        a. Set offsetNanoseconds to ! ParseDateTimeUTCOffset(offsetString).\n            // 8. Let epochNanoseconds be ? InterpretISODateTimeOffset(isoDate, time, offsetBehaviour, offsetNanoseconds, timeZone, disambiguation, offsetOption, matchBehaviour).\n            Ok(ZonedDateTimeInner::from_parsed_with_provider(\n                parsed,\n                disambiguation,\n                offset_option,\n                context.timezone_provider(),\n            )?)\n        }\n        // 5. Else,\n        // a. If item is not a String, throw a TypeError exception.\n        _ => Err(JsNativeError::typ()\n            .with_message(\"Temporal.ZonedDateTime.from only accepts an object or string.\")\n            .into()),\n    }\n    // 9. Return ! CreateTemporalZonedDateTime(epochNanoseconds, timeZone, calendar).\n}\n\npub(crate) fn to_temporal_timezone_identifier(\n    value: &JsValue,\n    context: &mut Context,\n) -> JsResult<TimeZone> {\n    // 1. If temporalTimeZoneLike is an Object, then\n    //    a. If temporalTimeZoneLike has an [[InitializedTemporalZonedDateTime]] internal slot, then\n    if let Some(obj) = value.as_object()\n        && let Some(zdt) = obj.downcast_ref::<ZonedDateTime>()\n    {\n        // i. Return temporalTimeZoneLike.[[TimeZone]].\n        return Ok(*zdt.inner.time_zone());\n    }\n\n    // 2. If temporalTimeZoneLike is not a String, throw a TypeError exception.\n    let Some(tz_string) = value.as_string() else {\n        return Err(JsNativeError::typ()\n            .with_message(\"timeZone must be a string or Temporal.ZonedDateTime\")\n            .into());\n    };\n\n    // 3. Let parseResult be ? ParseTemporalTimeZoneString(temporalTimeZoneLike).\n    // 4. Let offsetMinutes be parseResult.[[OffsetMinutes]].\n    // 5. If offsetMinutes is not empty, return FormatOffsetTimeZoneIdentifier(offsetMinutes).\n    // 6. Let name be parseResult.[[Name]].\n    // 7. Let timeZoneIdentifierRecord be GetAvailableNamedTimeZoneIdentifier(name).\n    // 8. If timeZoneIdentifierRecord is empty, throw a RangeError exception.\n    // 9. Return timeZoneIdentifierRecord.[[Identifier]].\n    let timezone = TimeZone::try_from_str_with_provider(\n        &tz_string.to_std_string_escaped(),\n        context.timezone_provider(),\n    )?;\n\n    Ok(timezone)\n}\n\nfn to_offset_string(value: &JsValue, context: &mut Context) -> JsResult<UtcOffset> {\n    // 1. Let offset be ? ToPrimitive(argument, string).\n    let offset = value.to_primitive(context, PreferredType::String)?;\n    // 2. If offset is not a String, throw a TypeError exception.\n    let Some(offset_string) = offset.as_string() else {\n        return Err(JsNativeError::typ()\n            .with_message(\"offset must be a String.\")\n            .into());\n    };\n    // 3. Perform ? ParseDateTimeUTCOffset(offset).\n    let result = UtcOffset::from_str(&offset_string.to_std_string_escaped())?;\n    // 4. Return offset.\n    Ok(result)\n}\n\npub(crate) fn to_partial_zoneddatetime(\n    partial_object: &JsObject,\n    context: &mut Context,\n) -> JsResult<PartialZonedDateTime> {\n    // NOTE (nekevss): Why do we have to list out all of the get operations? Well, order of operations Watson!\n    // b. Let calendar be ? GetTemporalCalendarIdentifierWithISODefault(item).\n    // c. Let fields be ? PrepareCalendarFields(calendar, item, « year, month, month-code, day », « hour, minute, second, millisecond, microsecond, nanosecond, offset, time-zone », « time-zone »).\n    let calendar = get_temporal_calendar_slot_value_with_default(partial_object, context)?;\n    let (fields, timezone) = to_zoned_date_time_fields(\n        partial_object,\n        &calendar,\n        ZdtFieldsType::TimeZoneRequired,\n        context,\n    )?;\n    Ok(PartialZonedDateTime {\n        fields,\n        timezone,\n        calendar,\n    })\n}\n\n/// This distinguishes the type of `PrepareCalendarField` call used.\n#[derive(Debug, Clone, Copy, PartialEq)]\npub enum ZdtFieldsType {\n    /// Do not call to the `timeZone` property.\n    NoTimeZone,\n    /// Call to `timeZone`, value can be undefined.\n    TimeZoneNotRequired,\n    /// Call to `timeZone`, value must exist.\n    TimeZoneRequired,\n}\n\npub(crate) fn to_zoned_date_time_fields(\n    partial_object: &JsObject,\n    calendar: &Calendar,\n    zdt_fields_type: ZdtFieldsType,\n    context: &mut Context,\n) -> JsResult<(ZonedDateTimeFields, Option<TimeZone>)> {\n    let day = partial_object\n        .get(js_string!(\"day\"), context)?\n        .map(|v| {\n            let finite = v.to_finitef64(context)?;\n            finite\n                .as_positive_integer_with_truncation()\n                .map_err(JsError::from)\n        })\n        .transpose()?;\n    let hour = partial_object\n        .get(js_string!(\"hour\"), context)?\n        .map(|v| {\n            let finite = v.to_finitef64(context)?;\n            Ok::<u8, JsError>(finite.as_integer_with_truncation::<u8>())\n        })\n        .transpose()?;\n    // TODO: `temporal_rs` needs a `has_era` method\n    let has_no_era = calendar.kind() == AnyCalendarKind::Iso\n        || calendar.kind() == AnyCalendarKind::Chinese\n        || calendar.kind() == AnyCalendarKind::Dangi;\n    let (era, era_year) = if has_no_era {\n        (None, None)\n    } else {\n        let era = partial_object\n            .get(js_string!(\"era\"), context)?\n            .map(|v| {\n                let v = v.to_primitive(context, PreferredType::String)?;\n                let Some(era) = v.as_string() else {\n                    return Err(JsError::from(\n                        JsNativeError::typ()\n                            .with_message(\"The monthCode field value must be a string.\"),\n                    ));\n                };\n                // TODO: double check if an invalid monthCode is a range or type error.\n                TinyAsciiStr::<19>::try_from_str(&era.to_std_string_escaped())\n                    .map_err(|e| JsError::from(JsNativeError::range().with_message(e.to_string())))\n            })\n            .transpose()?;\n        let era_year = partial_object\n            .get(js_string!(\"eraYear\"), context)?\n            .map(|v| {\n                let finite = v.to_finitef64(context)?;\n                Ok::<i32, JsError>(finite.as_integer_with_truncation::<i32>())\n            })\n            .transpose()?;\n        (era, era_year)\n    };\n    let microsecond = partial_object\n        .get(js_string!(\"microsecond\"), context)?\n        .map(|v| {\n            let finite = v.to_finitef64(context)?;\n            Ok::<u16, JsError>(finite.as_integer_with_truncation::<u16>())\n        })\n        .transpose()?;\n\n    let millisecond = partial_object\n        .get(js_string!(\"millisecond\"), context)?\n        .map(|v| {\n            let finite = v.to_finitef64(context)?;\n            Ok::<u16, JsError>(finite.as_integer_with_truncation::<u16>())\n        })\n        .transpose()?;\n\n    let minute = partial_object\n        .get(js_string!(\"minute\"), context)?\n        .map(|v| {\n            let finite = v.to_finitef64(context)?;\n            Ok::<u8, JsError>(finite.as_integer_with_truncation::<u8>())\n        })\n        .transpose()?;\n\n    let month = partial_object\n        .get(js_string!(\"month\"), context)?\n        .map(|v| {\n            let finite = v.to_finitef64(context)?;\n            finite\n                .as_positive_integer_with_truncation()\n                .map_err(JsError::from)\n        })\n        .transpose()?;\n\n    let month_code = partial_object\n        .get(js_string!(\"monthCode\"), context)?\n        .map(|v| {\n            let v = v.to_primitive(context, PreferredType::String)?;\n            let Some(month_code) = v.as_string() else {\n                return Err(JsNativeError::typ()\n                    .with_message(\"The monthCode field value must be a string.\")\n                    .into());\n            };\n            MonthCode::from_str(&month_code.to_std_string_escaped()).map_err(JsError::from)\n        })\n        .transpose()?;\n\n    let nanosecond = partial_object\n        .get(js_string!(\"nanosecond\"), context)?\n        .map(|v| {\n            let finite = v.to_finitef64(context)?;\n            Ok::<u16, JsError>(finite.as_integer_with_truncation::<u16>())\n        })\n        .transpose()?;\n\n    let offset = partial_object\n        .get(js_string!(\"offset\"), context)?\n        .map(|v| to_offset_string(v, context))\n        .transpose()?;\n\n    let second = partial_object\n        .get(js_string!(\"second\"), context)?\n        .map(|v| {\n            let finite = v.to_finitef64(context)?;\n            Ok::<u8, JsError>(finite.as_integer_with_truncation::<u8>())\n        })\n        .transpose()?;\n\n    let time_zone = match zdt_fields_type {\n        ZdtFieldsType::NoTimeZone => None,\n        ZdtFieldsType::TimeZoneNotRequired | ZdtFieldsType::TimeZoneRequired => {\n            let time_zone = partial_object\n                .get(js_string!(\"timeZone\"), context)?\n                .map(|v| to_temporal_timezone_identifier(v, context))\n                .transpose()?;\n            if zdt_fields_type == ZdtFieldsType::TimeZoneRequired && time_zone.is_none() {\n                return Err(JsNativeError::typ()\n                    .with_message(\"timeZone is required to construct ZonedDateTime.\")\n                    .into());\n            }\n            time_zone\n        }\n    };\n\n    let year = partial_object\n        .get(js_string!(\"year\"), context)?\n        .map(|v| {\n            let finite = v.to_finitef64(context)?;\n            Ok::<i32, JsError>(finite.as_integer_with_truncation::<i32>())\n        })\n        .transpose()?;\n\n    let calendar_fields = CalendarFields::new()\n        .with_optional_year(year)\n        .with_optional_month(month)\n        .with_optional_month_code(month_code)\n        .with_optional_day(day)\n        .with_era(era)\n        .with_era_year(era_year);\n\n    let time = PartialTime::new()\n        .with_hour(hour)\n        .with_minute(minute)\n        .with_second(second)\n        .with_millisecond(millisecond)\n        .with_microsecond(microsecond)\n        .with_nanosecond(nanosecond);\n\n    Ok((\n        ZonedDateTimeFields {\n            calendar_fields,\n            time,\n            offset,\n        },\n        time_zone,\n    ))\n}\n"
  },
  {
    "path": "core/engine/src/builtins/typed_array/builtin.rs",
    "content": "use std::{\n    cmp::{self, min},\n    sync::atomic::Ordering,\n};\n\nuse num_traits::Zero;\n\nuse super::{\n    ContentType, TypedArray, TypedArrayKind, TypedArrayMarker, object::typed_array_set_element,\n};\nuse crate::{\n    Context, JsArgs, JsExpect, JsNativeError, JsObject, JsResult, JsString, JsSymbol, JsValue,\n    builtins::{\n        Array, BuiltInBuilder, BuiltInConstructor, BuiltInObject, IntrinsicObject,\n        array::{ArrayIterator, Direction, find_via_predicate},\n        array_buffer::{\n            ArrayBuffer, BufferObject,\n            utils::{SliceRefMut, memcpy, memmove},\n        },\n    },\n    context::intrinsics::{Intrinsics, StandardConstructor, StandardConstructors},\n    js_string,\n    object::internal_methods::get_prototype_from_constructor,\n    property::{Attribute, PropertyNameKind},\n    realm::Realm,\n    string::StaticJsStrings,\n    value::IntegerOrInfinity,\n};\nuse crate::{builtins::array_buffer::utils::memmove_naive, value::JsVariant};\n\n/// The JavaScript `%TypedArray%` object.\n///\n/// <https://tc39.es/ecma262/#sec-%typedarray%-intrinsic-object>\n#[derive(Debug, Clone, Copy)]\npub(crate) struct BuiltinTypedArray;\n\nimpl IntrinsicObject for BuiltinTypedArray {\n    fn init(realm: &Realm) {\n        let get_species = BuiltInBuilder::callable(realm, Self::get_species)\n            .name(js_string!(\"get [Symbol.species]\"))\n            .build();\n\n        let get_buffer = BuiltInBuilder::callable(realm, Self::buffer)\n            .name(js_string!(\"get buffer\"))\n            .build();\n\n        let get_byte_length = BuiltInBuilder::callable(realm, Self::byte_length)\n            .name(js_string!(\"get byteLength\"))\n            .build();\n\n        let get_byte_offset = BuiltInBuilder::callable(realm, Self::byte_offset)\n            .name(js_string!(\"get byteOffset\"))\n            .build();\n\n        let get_length = BuiltInBuilder::callable(realm, Self::length)\n            .name(js_string!(\"get length\"))\n            .build();\n\n        let get_to_string_tag = BuiltInBuilder::callable(realm, Self::to_string_tag)\n            .name(js_string!(\"get [Symbol.toStringTag]\"))\n            .build();\n\n        let values_function = BuiltInBuilder::callable(realm, Self::values)\n            .name(js_string!(\"values\"))\n            .length(0)\n            .build();\n\n        BuiltInBuilder::from_standard_constructor::<Self>(realm)\n            .static_accessor(\n                JsSymbol::species(),\n                Some(get_species),\n                None,\n                Attribute::CONFIGURABLE,\n            )\n            .property(\n                JsSymbol::iterator(),\n                values_function.clone(),\n                Attribute::WRITABLE | Attribute::NON_ENUMERABLE | Attribute::CONFIGURABLE,\n            )\n            .accessor(\n                js_string!(\"buffer\"),\n                Some(get_buffer),\n                None,\n                Attribute::CONFIGURABLE | Attribute::NON_ENUMERABLE,\n            )\n            .accessor(\n                js_string!(\"byteLength\"),\n                Some(get_byte_length),\n                None,\n                Attribute::CONFIGURABLE | Attribute::NON_ENUMERABLE,\n            )\n            .accessor(\n                js_string!(\"byteOffset\"),\n                Some(get_byte_offset),\n                None,\n                Attribute::CONFIGURABLE | Attribute::NON_ENUMERABLE,\n            )\n            .accessor(\n                StaticJsStrings::LENGTH,\n                Some(get_length),\n                None,\n                Attribute::CONFIGURABLE | Attribute::NON_ENUMERABLE,\n            )\n            .accessor(\n                JsSymbol::to_string_tag(),\n                Some(get_to_string_tag),\n                None,\n                Attribute::CONFIGURABLE | Attribute::NON_ENUMERABLE,\n            )\n            .static_method(Self::from, js_string!(\"from\"), 1)\n            .static_method(Self::of, js_string!(\"of\"), 0)\n            .method(Self::at, js_string!(\"at\"), 1)\n            .method(Self::copy_within, js_string!(\"copyWithin\"), 2)\n            .method(Self::entries, js_string!(\"entries\"), 0)\n            .method(Self::every, js_string!(\"every\"), 1)\n            .method(Self::fill, js_string!(\"fill\"), 1)\n            .method(Self::filter, js_string!(\"filter\"), 1)\n            .method(Self::find, js_string!(\"find\"), 1)\n            .method(Self::find_index, js_string!(\"findIndex\"), 1)\n            .method(Self::find_last, js_string!(\"findLast\"), 1)\n            .method(Self::find_last_index, js_string!(\"findLastIndex\"), 1)\n            .method(Self::for_each, js_string!(\"forEach\"), 1)\n            .method(Self::includes, js_string!(\"includes\"), 1)\n            .method(Self::index_of, js_string!(\"indexOf\"), 1)\n            .method(Self::join, js_string!(\"join\"), 1)\n            .method(Self::keys, js_string!(\"keys\"), 0)\n            .method(Self::last_index_of, js_string!(\"lastIndexOf\"), 1)\n            .method(Self::map, js_string!(\"map\"), 1)\n            .method(Self::reduce, js_string!(\"reduce\"), 1)\n            .method(Self::reduceright, js_string!(\"reduceRight\"), 1)\n            .method(Self::reverse, js_string!(\"reverse\"), 0)\n            .method(Self::set, js_string!(\"set\"), 1)\n            .method(Self::slice, js_string!(\"slice\"), 2)\n            .method(Self::some, js_string!(\"some\"), 1)\n            .method(Self::sort, js_string!(\"sort\"), 1)\n            .method(Self::subarray, js_string!(\"subarray\"), 2)\n            .method(Self::to_locale_string, js_string!(\"toLocaleString\"), 0)\n            .method(Self::to_reversed, js_string!(\"toReversed\"), 0)\n            .method(Self::to_sorted, js_string!(\"toSorted\"), 1)\n            .method(Self::with, js_string!(\"with\"), 2)\n            // 23.2.3.29 %TypedArray%.prototype.toString ( )\n            // The initial value of the %TypedArray%.prototype.toString data property is the same\n            // built-in function object as the Array.prototype.toString method defined in 23.1.3.30.\n            .property(\n                js_string!(\"toString\"),\n                realm.intrinsics().objects().array_prototype_to_string(),\n                Attribute::WRITABLE | Attribute::NON_ENUMERABLE | Attribute::CONFIGURABLE,\n            )\n            .property(\n                js_string!(\"values\"),\n                values_function,\n                Attribute::WRITABLE | Attribute::NON_ENUMERABLE | Attribute::CONFIGURABLE,\n            )\n            .build();\n    }\n\n    fn get(intrinsics: &Intrinsics) -> JsObject {\n        Self::STANDARD_CONSTRUCTOR(intrinsics.constructors()).constructor()\n    }\n}\n\nimpl BuiltInObject for BuiltinTypedArray {\n    const NAME: JsString = StaticJsStrings::TYPED_ARRAY;\n}\n\nimpl BuiltInConstructor for BuiltinTypedArray {\n    const CONSTRUCTOR_ARGUMENTS: usize = 0;\n    const PROTOTYPE_STORAGE_SLOTS: usize = 42;\n    const CONSTRUCTOR_STORAGE_SLOTS: usize = 4;\n\n    const STANDARD_CONSTRUCTOR: fn(&StandardConstructors) -> &StandardConstructor =\n        StandardConstructors::typed_array;\n\n    /// `%TypedArray% ( )`\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-%typedarray%\n    fn constructor(_: &JsValue, _: &[JsValue], _: &mut Context) -> JsResult<JsValue> {\n        // 1. Throw a TypeError exception.\n        Err(JsNativeError::typ()\n            .with_message(\"the TypedArray constructor should never be called directly\")\n            .into())\n    }\n}\n\nimpl BuiltinTypedArray {\n    /// `%TypedArray%.from ( source [ , mapfn [ , thisArg ] ] )`\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-%typedarray%.from\n    fn from(this: &JsValue, args: &[JsValue], context: &mut Context) -> JsResult<JsValue> {\n        // 1. Let C be the this value.\n        // 2. If IsConstructor(C) is false, throw a TypeError exception.\n        let constructor = match this.as_object() {\n            Some(obj) if obj.is_constructor() => obj,\n            _ => {\n                return Err(JsNativeError::typ()\n                    .with_message(\"TypedArray.from called on non-constructable value\")\n                    .into());\n            }\n        };\n\n        let mapping = match args.get(1).map(JsValue::variant) {\n            // 3. If mapfn is undefined, let mapping be false.\n            None | Some(JsVariant::Undefined) => None,\n            // 4. Else,\n            // b. Let mapping be true.\n            Some(JsVariant::Object(obj)) if obj.is_callable() => Some(obj),\n            // a. If IsCallable(mapfn) is false, throw a TypeError exception.\n            _ => {\n                return Err(JsNativeError::typ()\n                    .with_message(\"TypedArray.from called with non-callable mapfn\")\n                    .into());\n            }\n        };\n\n        // 5. Let usingIterator be ? GetMethod(source, @@iterator).\n        let source = args.get_or_undefined(0);\n        let using_iterator = source.get_method(JsSymbol::iterator(), context)?;\n\n        let this_arg = args.get_or_undefined(2);\n\n        // 6. If usingIterator is not undefined, then\n        if let Some(using_iterator) = using_iterator {\n            // a. Let values be ? IterableToList(source, usingIterator).\n            let values = source\n                .get_iterator_from_method(&using_iterator, context)?\n                .into_list(context)?;\n\n            // b. Let len be the number of elements in values.\n            // c. Let targetObj be ? TypedArrayCreate(C, « 𝔽(len) »).\n            let target_obj = Self::create(&constructor, &[values.len().into()], context)?.upcast();\n\n            // d. Let k be 0.\n            // e. Repeat, while k < len,\n            for (k, k_value) in values.iter().enumerate() {\n                // i. Let Pk be ! ToString(𝔽(k)).\n                // ii. Let kValue be the first element of values and remove that element from values.\n                // iii. If mapping is true, then\n                let mapped_value = if let Some(map_fn) = &mapping {\n                    // 1. Let mappedValue be ? Call(mapfn, thisArg, « kValue, 𝔽(k) »).\n                    map_fn.call(this_arg, &[k_value.clone(), k.into()], context)?\n                }\n                // iv. Else, let mappedValue be kValue.\n                else {\n                    k_value.clone()\n                };\n\n                // v. Perform ? Set(targetObj, Pk, mappedValue, true).\n                target_obj.set(k, mapped_value, true, context)?;\n            }\n\n            // f. Assert: values is now an empty List.\n            // g. Return targetObj.\n            return Ok(target_obj.into());\n        }\n\n        // 7. NOTE: source is not an Iterable so assume it is already an array-like object.\n        // 8. Let arrayLike be ! ToObject(source).\n        let array_like = source\n            .to_object(context)\n            .js_expect(\"ToObject cannot fail here\")?;\n\n        // 9. Let len be ? LengthOfArrayLike(arrayLike).\n        let len = array_like.length_of_array_like(context)?;\n\n        // 10. Let targetObj be ? TypedArrayCreate(C, « 𝔽(len) »).\n        let target_obj = Self::create(&constructor, &[len.into()], context)?.upcast();\n\n        // 11. Let k be 0.\n        // 12. Repeat, while k < len,\n        for k in 0..len {\n            // a. Let Pk be ! ToString(𝔽(k)).\n            // b. Let kValue be ? Get(arrayLike, Pk).\n            let k_value = array_like.get(k, context)?;\n\n            // c. If mapping is true, then\n            let mapped_value = if let Some(map_fn) = &mapping {\n                // i. Let mappedValue be ? Call(mapfn, thisArg, « kValue, 𝔽(k) »).\n                map_fn.call(this_arg, &[k_value, k.into()], context)?\n            }\n            // d. Else, let mappedValue be kValue.\n            else {\n                k_value\n            };\n\n            // e. Perform ? Set(targetObj, Pk, mappedValue, true).\n            target_obj.set(k, mapped_value, true, context)?;\n        }\n\n        // 13. Return targetObj.\n        Ok(target_obj.into())\n    }\n\n    /// [`TypedArrayCreateSameType ( exemplar, argumentList )`][spec]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-typedarray-create-same-type\n    fn from_kind_and_length(\n        kind: TypedArrayKind,\n        length: u64,\n        context: &mut Context,\n    ) -> JsResult<JsObject> {\n        let constructor =\n            kind.standard_constructor()(context.intrinsics().constructors()).constructor();\n\n        Self::create(&constructor, &[length.into()], context).map(JsObject::upcast)\n    }\n\n    /// `%TypedArray%.of ( ...items )`\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-%typedarray%.of\n    fn of(this: &JsValue, args: &[JsValue], context: &mut Context) -> JsResult<JsValue> {\n        // 1. Let len be the number of elements in items.\n\n        // 2. Let C be the this value.\n        // 3. If IsConstructor(C) is false, throw a TypeError exception.\n        let constructor = match this.as_object() {\n            Some(obj) if obj.is_constructor() => obj,\n            _ => {\n                return Err(JsNativeError::typ()\n                    .with_message(\"TypedArray.of called on non-constructable value\")\n                    .into());\n            }\n        };\n\n        // 4. Let newObj be ? TypedArrayCreate(C, « 𝔽(len) »).\n        let new_obj = Self::create(&constructor, &[args.len().into()], context)?.upcast();\n\n        // 5. Let k be 0.\n        // 6. Repeat, while k < len,\n        for (k, k_value) in args.iter().enumerate() {\n            // a. Let kValue be items[k].\n            // b. Let Pk be ! ToString(𝔽(k)).\n            // c. Perform ? Set(newObj, Pk, kValue, true).\n            new_obj.set(k, k_value.clone(), true, context)?;\n        }\n\n        // 7. Return newObj.\n        Ok(new_obj.into())\n    }\n\n    /// `get %TypedArray% [ @@species ]`\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-get-%typedarray%-@@species\n    #[allow(clippy::unnecessary_wraps)]\n    pub(super) fn get_species(this: &JsValue, _: &[JsValue], _: &mut Context) -> JsResult<JsValue> {\n        // 1. Return the this value.\n        Ok(this.clone())\n    }\n\n    /// `%TypedArray%.prototype.at ( index )`\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-%typedarray%.prototype.at\n    pub(crate) fn at(this: &JsValue, args: &[JsValue], context: &mut Context) -> JsResult<JsValue> {\n        // 1. Let O be the this value.\n        // 2. Let taRecord be ? ValidateTypedArray(O, seq-cst).\n        let (o, buf_len) = TypedArray::validate(this, Ordering::SeqCst)?;\n\n        // 3. Let len be TypedArrayLength(taRecord).\n        let len = o.borrow().data().array_length(buf_len) as i64;\n\n        // 4. Let relativeIndex be ? ToIntegerOrInfinity(index).\n        let relative_index = args.get_or_undefined(0).to_integer_or_infinity(context)?;\n\n        let k = match relative_index {\n            // Note: Early undefined return on infinity.\n            IntegerOrInfinity::PositiveInfinity | IntegerOrInfinity::NegativeInfinity => {\n                return Ok(JsValue::undefined());\n            }\n            // 5. If relativeIndex ≥ 0, then\n            // a. Let k be relativeIndex.\n            IntegerOrInfinity::Integer(i) if i >= 0 => i,\n            // 6. Else,\n            // a. Let k be len + relativeIndex.\n            IntegerOrInfinity::Integer(i) => len + i,\n        };\n\n        // 7. If k < 0 or k ≥ len, return undefined.\n        if k < 0 || k >= len {\n            return Ok(JsValue::undefined());\n        }\n\n        // 8. Return ! Get(O, ! ToString(𝔽(k))).\n        Ok(o.upcast()\n            .get(k, context)\n            .js_expect(\"Get cannot fail here\")?)\n    }\n\n    /// `get %TypedArray%.prototype.buffer`\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-get-%typedarray%.prototype.buffer\n    pub(crate) fn buffer(this: &JsValue, _: &[JsValue], _: &mut Context) -> JsResult<JsValue> {\n        // 1. Let O be the this value.\n        // 2. Perform ? RequireInternalSlot(O, [[TypedArrayName]]).\n        // 3. Assert: O has a [[ViewedArrayBuffer]] internal slot.\n        let object = this.as_object();\n        let ta = object\n            .as_ref()\n            .and_then(JsObject::downcast_ref::<TypedArray>)\n            .ok_or_else(|| {\n                JsNativeError::typ().with_message(\"`this` is not a typed array object\")\n            })?;\n\n        // 4. Let buffer be O.[[ViewedArrayBuffer]].\n        // 5. Return buffer.\n        Ok(ta.viewed_array_buffer().clone().into())\n    }\n\n    /// `get %TypedArray%.prototype.byteLength`\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-get-%typedarray%.prototype.bytelength\n    pub(crate) fn byte_length(this: &JsValue, _: &[JsValue], _: &mut Context) -> JsResult<JsValue> {\n        // 1. Let O be the this value.\n        // 2. Perform ? RequireInternalSlot(O, [[TypedArrayName]]).\n        // 3. Assert: O has a [[ViewedArrayBuffer]] internal slot.\n        let object = this.as_object();\n        let ta = object\n            .as_ref()\n            .and_then(JsObject::downcast_ref::<TypedArray>)\n            .ok_or_else(|| {\n                JsNativeError::typ().with_message(\"`this` is not a typed array object\")\n            })?;\n\n        // 4. Let taRecord be MakeTypedArrayWithBufferWitnessRecord(O, seq-cst).\n        let buf_len = ta\n            .viewed_array_buffer()\n            .as_buffer()\n            .bytes(Ordering::SeqCst)\n            .map(|s| s.len())\n            .unwrap_or_default();\n\n        // 5. Let size be TypedArrayByteLength(taRecord).\n        // 6. Return 𝔽(size).\n        Ok(ta.byte_length(buf_len).into())\n    }\n\n    /// `get %TypedArray%.prototype.byteOffset`\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-get-%typedarray%.prototype.byteoffset\n    pub(crate) fn byte_offset(this: &JsValue, _: &[JsValue], _: &mut Context) -> JsResult<JsValue> {\n        // 1. Let O be the this value.\n        // 2. Perform ? RequireInternalSlot(O, [[TypedArrayName]]).\n        // 3. Assert: O has a [[ViewedArrayBuffer]] internal slot.\n        let object = this.as_object();\n        let ta = object\n            .as_ref()\n            .and_then(JsObject::downcast_ref::<TypedArray>)\n            .ok_or_else(|| {\n                JsNativeError::typ().with_message(\"Value is not a typed array object\")\n            })?;\n\n        // 4. Let taRecord be MakeTypedArrayWithBufferWitnessRecord(O, seq-cst).\n        // 5. If IsTypedArrayOutOfBounds(taRecord) is true, return +0𝔽.\n        if ta\n            .viewed_array_buffer()\n            .as_buffer()\n            .bytes(Ordering::SeqCst)\n            .filter(|s| !ta.is_out_of_bounds(s.len()))\n            .is_none()\n        {\n            return Ok(0.into());\n        }\n\n        // 6. Let offset be O.[[ByteOffset]].\n        // 7. Return 𝔽(offset).\n        Ok(ta.byte_offset().into())\n    }\n\n    /// `%TypedArray%.prototype.copyWithin ( target, start [ , end ] )`\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-%typedarray%.prototype.copywithin\n    pub(crate) fn copy_within(\n        this: &JsValue,\n        args: &[JsValue],\n        context: &mut Context,\n    ) -> JsResult<JsValue> {\n        // 1. Let O be the this value.\n        // 2. Let taRecord be ? ValidateTypedArray(O, seq-cst).\n        let (ta, buf_len) = TypedArray::validate(this, Ordering::SeqCst)?;\n\n        // 3. Let len be TypedArrayLength(taRecord).\n        let len = ta.borrow().data().array_length(buf_len);\n\n        // 4. Let relativeTarget be ? ToIntegerOrInfinity(target).\n        // 5. If relativeTarget is -∞, let to be 0.\n        // 6. Else if relativeTarget < 0, let to be max(len + relativeTarget, 0).\n        // 7. Else, let to be min(relativeTarget, len).\n        let to = Array::get_relative_start(context, args.get_or_undefined(0), len)?;\n\n        // 8. Let relativeStart be ? ToIntegerOrInfinity(start).\n        // 9. If relativeStart is -∞, let from be 0.\n        // 10. Else if relativeStart < 0, let from be max(len + relativeStart, 0).\n        // 11. Else, let from be min(relativeStart, len).\n        let from = Array::get_relative_start(context, args.get_or_undefined(1), len)?;\n\n        // 12. If end is undefined, let relativeEnd be len; else let relativeEnd be ? ToIntegerOrInfinity(end).\n        // 13. If relativeEnd is -∞, let final be 0.\n        // 14. Else if relativeEnd < 0, let final be max(len + relativeEnd, 0).\n        // 15. Else, let final be min(relativeEnd, len).\n        let final_ = Array::get_relative_end(context, args.get_or_undefined(2), len)?;\n\n        // 16. Let count be min(final - from, len - to).\n        let count = match (final_.checked_sub(from), len.checked_sub(to)) {\n            (Some(lhs), Some(rhs)) => min(lhs, rhs),\n            _ => 0,\n        };\n\n        // 17. If count > 0, then\n        if count > 0 {\n            let ta = ta.borrow();\n            let ta = ta.data();\n\n            // a. NOTE: The copying must be performed in a manner that preserves the bit-level encoding of the source data.\n            // b. Let buffer be O.[[ViewedArrayBuffer]].\n            // c. Set taRecord to MakeTypedArrayWithBufferWitnessRecord(O, seq-cst).\n            // d. If IsTypedArrayOutOfBounds(taRecord) is true, throw a TypeError exception.\n            let buffer_obj = ta.viewed_array_buffer();\n            let mut buffer = buffer_obj.as_buffer_mut();\n            let Some(mut buf) = buffer\n                .bytes(Ordering::SeqCst)\n                .filter(|s| !ta.is_out_of_bounds(s.len()))\n            else {\n                return Err(JsNativeError::typ()\n                    .with_message(\"typed array is outside the bounds of its inner buffer\")\n                    .into());\n            };\n\n            // e. Set len to TypedArrayLength(taRecord).\n            let len = ta.array_length(buf.len());\n\n            // f. Let elementSize be TypedArrayElementSize(O).\n            let element_size = ta.kind().element_size();\n\n            // g. Let byteOffset be O.[[ByteOffset]].\n            let byte_offset = ta.byte_offset();\n\n            // h. Let bufferByteLimit be (len × elementSize) + byteOffset.\n            let buffer_byte_limit = ((len * element_size) + byte_offset) as usize;\n\n            // i. Let toByteIndex be (targetIndex × elementSize) + byteOffset.\n            let to_byte_index = (to * element_size + byte_offset) as usize;\n\n            // j. Let fromByteIndex be (startIndex × elementSize) + byteOffset.\n            let from_byte_index = (from * element_size + byte_offset) as usize;\n\n            // k. Let countBytes be count × elementSize.\n            let mut count_bytes = (count * element_size) as usize;\n\n            // Readjust considering the buffer_byte_limit. A resize could\n            // have readjusted the buffer size, which could put `count_bytes`\n            // outside the allowed range.\n            if to_byte_index >= buffer_byte_limit || from_byte_index >= buffer_byte_limit {\n                return Ok(this.clone());\n            }\n\n            count_bytes = min(\n                count_bytes,\n                min(\n                    buffer_byte_limit - to_byte_index,\n                    buffer_byte_limit - from_byte_index,\n                ),\n            );\n\n            // l. If fromByteIndex < toByteIndex and toByteIndex < fromByteIndex + countBytes, then\n            //     i. Let direction be -1.\n            //     ii. Set fromByteIndex to fromByteIndex + countBytes - 1.\n            //     iii. Set toByteIndex to toByteIndex + countBytes - 1.\n            // m. Else,\n            //     i. Let direction be 1.\n            // n. Repeat, while countBytes > 0,\n            //     i. If fromByteIndex < bufferByteLimit and toByteIndex < bufferByteLimit, then\n            //         1. Let value be GetValueFromBuffer(buffer, fromByteIndex, uint8, true, unordered).\n            //         2. Perform SetValueInBuffer(buffer, toByteIndex, uint8, value, true, unordered).\n            //         3. Set fromByteIndex to fromByteIndex + direction.\n            //         4. Set toByteIndex to toByteIndex + direction.\n            //         5. Set countBytes to countBytes - 1.\n            //     ii. Else,\n            //         1. Set countBytes to 0.\n\n            #[cfg(debug_assertions)]\n            {\n                assert!(buf.subslice_mut(from_byte_index..).len() >= count_bytes);\n                assert!(buf.subslice_mut(to_byte_index..).len() >= count_bytes);\n            }\n\n            // SAFETY: All previous checks are made to ensure this memmove is always in-bounds,\n            // making this operation safe.\n            unsafe {\n                memmove(buf.as_ptr(), from_byte_index, to_byte_index, count_bytes);\n            }\n        }\n\n        // 18. Return O.\n        Ok(this.clone())\n    }\n\n    /// `%TypedArray%.prototype.entries ( )`\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-%typedarray%.prototype.entries\n    pub(crate) fn entries(\n        this: &JsValue,\n        _: &[JsValue],\n        context: &mut Context,\n    ) -> JsResult<JsValue> {\n        // 1. Let O be the this value.\n        // 2. Perform ? ValidateTypedArray(O, seq-cst).\n        let (ta, _) = TypedArray::validate(this, Ordering::SeqCst)?;\n\n        // 3. Return CreateArrayIterator(O, key+value).\n        Ok(ArrayIterator::create_array_iterator(\n            ta.upcast(),\n            PropertyNameKind::KeyAndValue,\n            context,\n        ))\n    }\n\n    /// `%TypedArray%.prototype.every ( callbackfn [ , thisArg ] )`\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-%typedarray%.prototype.every\n    pub(crate) fn every(\n        this: &JsValue,\n        args: &[JsValue],\n        context: &mut Context,\n    ) -> JsResult<JsValue> {\n        // 1. Let O be the this value.\n        // 2. Let taRecord be ? ValidateTypedArray(O, seq-cst).\n        let (ta, buf_len) = TypedArray::validate(this, Ordering::SeqCst)?;\n\n        // 3. Let len be TypedArrayLength(taRecord).\n        let len = ta.borrow().data().array_length(buf_len);\n\n        // 4. If IsCallable(callbackfn) is false, throw a TypeError exception.\n        let callback_fn = match args.get_or_undefined(0).as_object() {\n            Some(obj) if obj.is_callable() => obj,\n            _ => {\n                return Err(JsNativeError::typ()\n                    .with_message(\n                        \"TypedArray.prototype.every called with non-callable callback function\",\n                    )\n                    .into());\n            }\n        };\n\n        // 5. Let k be 0.\n        // 6. Repeat, while k < len,\n        let ta = ta.upcast();\n        for k in 0..len {\n            // a. Let Pk be ! ToString(𝔽(k)).\n            // b. Let kValue be ! Get(O, Pk).\n            let k_value = ta.get(k, context)?;\n\n            // c. Let testResult be ! ToBoolean(? Call(callbackfn, thisArg, « kValue, 𝔽(k), O »)).\n            let test_result = callback_fn\n                .call(\n                    args.get_or_undefined(1),\n                    &[k_value, k.into(), this.clone()],\n                    context,\n                )?\n                .to_boolean();\n\n            // d. If testResult is false, return false.\n            if !test_result {\n                return Ok(false.into());\n            }\n        }\n\n        // 7. Return true.\n        Ok(true.into())\n    }\n\n    /// `%TypedArray%.prototype.fill ( value [ , start [ , end ] ] )`\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-%typedarray%.prototype.fill\n    pub(crate) fn fill(\n        this: &JsValue,\n        args: &[JsValue],\n        context: &mut Context,\n    ) -> JsResult<JsValue> {\n        // 1. Let O be the this value.\n        // 2. Let taRecord be ? ValidateTypedArray(O, seq-cst).\n        let (ta, buf_len) = TypedArray::validate(this, Ordering::SeqCst)?;\n\n        // 3. Let len be TypedArrayLength(taRecord).\n        let len = ta.borrow().data().array_length(buf_len);\n\n        let value: JsValue = if ta.borrow().data().kind().content_type() == ContentType::BigInt {\n            // 4. If O.[[ContentType]] is BigInt, set value to ? ToBigInt(value).\n            args.get_or_undefined(0).to_bigint(context)?.into()\n        } else {\n            // 5. Otherwise, set value to ? ToNumber(value).\n            args.get_or_undefined(0).to_number(context)?.into()\n        };\n\n        // 6. Let relativeStart be ? ToIntegerOrInfinity(start).\n        // 7. If relativeStart = -∞, let startIndex be 0.\n        // 8. Else if relativeStart < 0, let startIndex be max(len + relativeStart, 0).\n        // 9. Else, let startIndex be min(relativeStart, len).\n        let start_index = Array::get_relative_start(context, args.get_or_undefined(1), len)?;\n\n        // 10. If end is undefined, let relativeEnd be len; else let relativeEnd be ? ToIntegerOrInfinity(end).\n        // 11. If relativeEnd = -∞, let endIndex be 0.\n        // 12. Else if relativeEnd < 0, let endIndex be max(len + relativeEnd, 0).\n        // 13. Else, let endIndex be min(relativeEnd, len).\n        let end_index = Array::get_relative_end(context, args.get_or_undefined(2), len)?;\n\n        // 14. Set taRecord to MakeTypedArrayWithBufferWitnessRecord(O, seq-cst).\n        // 15. If IsTypedArrayOutOfBounds(taRecord) is true, throw a TypeError exception.\n        let len = {\n            let ta = ta.borrow();\n\n            let Some(buf_len) = ta\n                .data()\n                .viewed_array_buffer()\n                .as_buffer()\n                .bytes(Ordering::SeqCst)\n                .filter(|b| !ta.data().is_out_of_bounds(b.len()))\n                .map(|b| b.len())\n            else {\n                return Err(JsNativeError::typ()\n                    .with_message(\"typed array is outside the bounds of its inner buffer\")\n                    .into());\n            };\n\n            // 16. Set len to TypedArrayLength(taRecord).\n            ta.data().array_length(buf_len)\n        };\n\n        // 17. Set endIndex to min(endIndex, len).\n        let end_index = min(end_index, len);\n\n        // 18. Let k be startIndex.\n        // 19. Repeat, while k < endIndex,\n\n        let ta = ta.upcast();\n        for k in start_index..end_index {\n            // a. Let Pk be ! ToString(𝔽(k)).\n            // b. Perform ! Set(O, Pk, value, true).\n            ta.set(k, value.clone(), true, context)\n                .js_expect(\"Set cannot fail here\")?;\n\n            // c. Set k to k + 1.\n        }\n\n        // 20. Return O.\n        Ok(this.clone())\n    }\n\n    /// `%TypedArray%.prototype.filter ( callbackfn [ , thisArg ] )`\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-%typedarray%.prototype.filter\n    pub(crate) fn filter(\n        this: &JsValue,\n        args: &[JsValue],\n        context: &mut Context,\n    ) -> JsResult<JsValue> {\n        // 1. Let O be the this value.\n        // 2. Let taRecord be ? ValidateTypedArray(O, seq-cst).\n        let (ta, buf_len) = TypedArray::validate(this, Ordering::SeqCst)?;\n\n        // 3. Let len be TypedArrayLength(taRecord).\n        let len = ta.borrow().data().array_length(buf_len);\n        let typed_array_kind = ta.borrow().data().kind();\n\n        // 4. If IsCallable(callbackfn) is false, throw a TypeError exception.\n        let callback_fn =\n            match args.get_or_undefined(0).as_object() {\n                Some(obj) if obj.is_callable() => obj,\n                _ => return Err(JsNativeError::typ()\n                    .with_message(\n                        \"TypedArray.prototype.filter called with non-callable callback function\",\n                    )\n                    .into()),\n            };\n\n        // 5. Let kept be a new empty List.\n        let mut kept = Vec::new();\n\n        // 6. Let k be 0.\n        // 7. Let captured be 0.\n        let mut captured = 0;\n\n        // 8. Repeat, while k < len,\n        let ta = ta.upcast();\n        for k in 0..len {\n            // a. Let Pk be ! ToString(𝔽(k)).\n            // b. Let kValue be ! Get(O, Pk).\n            let k_value = ta.get(k, context).js_expect(\"Get cannot fail here\")?;\n\n            // c. Let selected be ! ToBoolean(? Call(callbackfn, thisArg, « kValue, 𝔽(k), O »)).#\n            let selected = callback_fn\n                .call(\n                    args.get_or_undefined(1),\n                    &[k_value.clone(), k.into(), this.clone()],\n                    context,\n                )?\n                .to_boolean();\n\n            // d. If selected is true, then\n            if selected {\n                // i. Append kValue to the end of kept.\n                kept.push(k_value);\n\n                // ii. Set captured to captured + 1.\n                captured += 1;\n            }\n        }\n\n        // 9. Let A be ? TypedArraySpeciesCreate(O, « 𝔽(captured) »).\n        let a = Self::species_create(&ta, typed_array_kind, &[captured.into()], context)?.upcast();\n\n        // 10. Let n be 0.\n        // 11. For each element e of kept, do\n        for (n, e) in kept.iter().enumerate() {\n            // a. Perform ! Set(A, ! ToString(𝔽(n)), e, true).\n            a.set(n, e.clone(), true, context)\n                .js_expect(\"Set cannot fail here\")?;\n            // b. Set n to n + 1.\n        }\n\n        // 12. Return A.\n        Ok(a.into())\n    }\n\n    /// `%TypedArray%.prototype.find ( predicate [ , thisArg ] )`\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-%typedarray%.prototype.find\n    pub(crate) fn find(\n        this: &JsValue,\n        args: &[JsValue],\n        context: &mut Context,\n    ) -> JsResult<JsValue> {\n        // 1. Let O be the this value.\n        // 2. Let taRecord be ? ValidateTypedArray(O, seq-cst).\n        let (ta, buf_len) = TypedArray::validate(this, Ordering::SeqCst)?;\n\n        // 3. Let len be TypedArrayLength(taRecord).\n        let len = ta.borrow().data().array_length(buf_len);\n\n        let predicate = args.get_or_undefined(0);\n        let this_arg = args.get_or_undefined(1);\n\n        // 4. Let findRec be ? FindViaPredicate(O, len, ascending, predicate, thisArg).\n        let (_, value) = find_via_predicate(\n            &ta.upcast(),\n            len,\n            Direction::Ascending,\n            predicate,\n            this_arg,\n            context,\n            \"TypedArray.prototype.find\",\n        )?;\n\n        // 5. Return findRec.[[Value]].\n        Ok(value)\n    }\n\n    /// `%TypedArray%.prototype.findIndex ( predicate [ , thisArg ] )`\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-%typedarray%.prototype.findindex\n    pub(crate) fn find_index(\n        this: &JsValue,\n        args: &[JsValue],\n        context: &mut Context,\n    ) -> JsResult<JsValue> {\n        // 1. Let O be the this value.\n        // 2. Let taRecord be ? ValidateTypedArray(O, seq-cst).\n        let (ta, buf_len) = TypedArray::validate(this, Ordering::SeqCst)?;\n\n        // 3. Let len be TypedArrayLength(taRecord).\n        let len = ta.borrow().data().array_length(buf_len);\n\n        let predicate = args.get_or_undefined(0);\n        let this_arg = args.get_or_undefined(1);\n\n        // 4. Let findRec be ? FindViaPredicate(O, len, ascending, predicate, thisArg).\n        let (index, _) = find_via_predicate(\n            &ta.upcast(),\n            len,\n            Direction::Ascending,\n            predicate,\n            this_arg,\n            context,\n            \"TypedArray.prototype.findIndex\",\n        )?;\n\n        // 5. Return findRec.[[Index]].\n        Ok(index)\n    }\n\n    /// `%TypedArray%.prototype.findLast ( predicate [ , thisArg ] )`\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-%typedarray%.prototype.findlast\n    pub(crate) fn find_last(\n        this: &JsValue,\n        args: &[JsValue],\n        context: &mut Context,\n    ) -> JsResult<JsValue> {\n        // 1. Let O be the this value.\n        // 2. Let taRecord be ? ValidateTypedArray(O, seq-cst).\n        let (ta, buf_len) = TypedArray::validate(this, Ordering::SeqCst)?;\n\n        // 3. Let len be TypedArrayLength(taRecord).\n        let len = ta.borrow().data().array_length(buf_len);\n\n        let predicate = args.get_or_undefined(0);\n        let this_arg = args.get_or_undefined(1);\n\n        // 4. Let findRec be ? FindViaPredicate(O, len, descending, predicate, thisArg).\n        let (_, value) = find_via_predicate(\n            &ta.upcast(),\n            len,\n            Direction::Descending,\n            predicate,\n            this_arg,\n            context,\n            \"TypedArray.prototype.findLast\",\n        )?;\n\n        // 5. Return findRec.[[Value]].\n        Ok(value)\n    }\n\n    /// `%TypedArray%.prototype.findLastIndex ( predicate [ , thisArg ] )`\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-%typedarray%.prototype.findlastindex\n    pub(crate) fn find_last_index(\n        this: &JsValue,\n        args: &[JsValue],\n        context: &mut Context,\n    ) -> JsResult<JsValue> {\n        // 1. Let O be the this value.\n        // 2. Let taRecord be ? ValidateTypedArray(O, seq-cst).\n        let (ta, buf_len) = TypedArray::validate(this, Ordering::SeqCst)?;\n\n        // 3. Let len be TypedArrayLength(taRecord).\n        let len = ta.borrow().data().array_length(buf_len);\n\n        let predicate = args.get_or_undefined(0);\n        let this_arg = args.get_or_undefined(1);\n\n        // 4. Let findRec be ? FindViaPredicate(O, len, descending, predicate, thisArg).\n        let (index, _) = find_via_predicate(\n            &ta.upcast(),\n            len,\n            Direction::Descending,\n            predicate,\n            this_arg,\n            context,\n            \"TypedArray.prototype.findLastIndex\",\n        )?;\n\n        // 5. Return findRec.[[Index]].\n        Ok(index)\n    }\n\n    /// `%TypedArray%.prototype.forEach ( callbackfn [ , thisArg ] )`\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-%typedarray%.prototype.foreach\n    pub(crate) fn for_each(\n        this: &JsValue,\n        args: &[JsValue],\n        context: &mut Context,\n    ) -> JsResult<JsValue> {\n        // 1. Let O be the this value.\n        // 2. Let taRecord be ? ValidateTypedArray(O, seq-cst).\n        let (ta, buf_len) = TypedArray::validate(this, Ordering::SeqCst)?;\n\n        // 3. Let len be TypedArrayLength(taRecord).\n        let len = ta.borrow().data().array_length(buf_len);\n\n        // 4. If IsCallable(callbackfn) is false, throw a TypeError exception.\n        let callback_fn =\n            match args.get_or_undefined(0).as_object() {\n                Some(obj) if obj.is_callable() => obj,\n                _ => return Err(JsNativeError::typ()\n                    .with_message(\n                        \"TypedArray.prototype.foreach called with non-callable callback function\",\n                    )\n                    .into()),\n            };\n\n        // 5. Let k be 0.\n        // 6. Repeat, while k < len,\n        let ta = ta.upcast();\n        for k in 0..len {\n            // a. Let Pk be ! ToString(𝔽(k)).\n            // b. Let kValue be ! Get(O, Pk).\n            let k_value = ta.get(k, context).js_expect(\"Get cannot fail here\")?;\n\n            // c. Perform ? Call(callbackfn, thisArg, « kValue, 𝔽(k), O »).\n            callback_fn.call(\n                args.get_or_undefined(1),\n                &[k_value, k.into(), this.clone()],\n                context,\n            )?;\n        }\n\n        // 7. Return undefined.\n        Ok(JsValue::undefined())\n    }\n\n    /// `%TypedArray%.prototype.includes ( searchElement [ , fromIndex ] )`\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-%typedarray%.prototype.includes\n    pub(crate) fn includes(\n        this: &JsValue,\n        args: &[JsValue],\n        context: &mut Context,\n    ) -> JsResult<JsValue> {\n        // 1. Let O be the this value.\n        // 2. Let taRecord be ? ValidateTypedArray(O, seq-cst).\n        let (ta, buf_len) = TypedArray::validate(this, Ordering::SeqCst)?;\n\n        // 3. Let len be TypedArrayLength(taRecord).\n        let len = ta.borrow().data().array_length(buf_len);\n\n        // 4. If len is 0, return false.\n        if len == 0 {\n            return Ok(false.into());\n        }\n\n        // 5. Let n be ? ToIntegerOrInfinity(fromIndex).\n        // 6. Assert: If fromIndex is undefined, then n is 0.\n        let n = args.get_or_undefined(1).to_integer_or_infinity(context)?;\n\n        let n = match n {\n            // 7. If n is +∞, return false.\n            IntegerOrInfinity::PositiveInfinity => return Ok(false.into()),\n            // 8. Else if n is -∞, set n to 0.\n            IntegerOrInfinity::NegativeInfinity => 0,\n            IntegerOrInfinity::Integer(i) => i,\n        };\n\n        // 9. If n ≥ 0, then\n        let k = if n >= 0 {\n            // a. Let k be n.\n            n as u64\n        } else {\n            // 10. Else,\n            // a. Let k be len + n.\n            // b. If k < 0, set k to 0.\n            len.saturating_add_signed(n)\n        };\n\n        // 11. Repeat, while k < len,\n        let ta = ta.upcast();\n        for k in k..len {\n            // a. Let elementK be ! Get(O, ! ToString(𝔽(k))).\n            let element_k = ta.get(k, context).js_expect(\"Get cannot fail here\")?;\n\n            // b. If SameValueZero(searchElement, elementK) is true, return true.\n            if JsValue::same_value_zero(args.get_or_undefined(0), &element_k) {\n                return Ok(true.into());\n            }\n\n            // c. Set k to k + 1.\n        }\n\n        // 12. Return false.\n        Ok(false.into())\n    }\n\n    /// `%TypedArray%.prototype.indexOf ( searchElement [ , fromIndex ] )`\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-%typedarray%.prototype.indexof\n    pub(crate) fn index_of(\n        this: &JsValue,\n        args: &[JsValue],\n        context: &mut Context,\n    ) -> JsResult<JsValue> {\n        // 1. Let O be the this value.\n        // 2. Let taRecord be ? ValidateTypedArray(O, seq-cst).\n        let (ta, buf_len) = TypedArray::validate(this, Ordering::SeqCst)?;\n\n        // 3. Let len be TypedArrayLength(taRecord).\n        let len = ta.borrow().data().array_length(buf_len);\n\n        // 4. If len is 0, return -1𝔽.\n        if len == 0 {\n            return Ok((-1).into());\n        }\n\n        // 5. Let n be ? ToIntegerOrInfinity(fromIndex).\n        // 6. Assert: If fromIndex is undefined, then n is 0.\n        let n = args.get_or_undefined(1).to_integer_or_infinity(context)?;\n\n        let n = match n {\n            // 7. If n is +∞, return -1𝔽.\n            IntegerOrInfinity::PositiveInfinity => return Ok((-1).into()),\n            // 8. Else if n is -∞, set n to 0.\n            IntegerOrInfinity::NegativeInfinity => 0,\n            IntegerOrInfinity::Integer(i) => i,\n        };\n\n        // 9. If n ≥ 0, then\n        let k = if n >= 0 {\n            // a. Let k be n.\n            n as u64\n        // 10. Else,\n        } else {\n            // a. Let k be len + n.\n            // b. If k < 0, set k to 0.\n            len.saturating_add_signed(n)\n        };\n\n        // 11. Repeat, while k < len,\n        let ta = ta.upcast();\n        for k in k..len {\n            // a. Let kPresent be ! HasProperty(O, ! ToString(𝔽(k))).\n            // b. If kPresent is true, then\n            // b.i. Let elementK be ! Get(O, ! ToString(𝔽(k))).\n            //   ii. Let same be IsStrictlyEqual(searchElement, elementK).\n            //   iii. If same is true, return 𝔽(k).\n            if let Some(element_k) = ta.try_get(k, context).js_expect(\"Get cannot fail here\")?\n                && args.get_or_undefined(0).strict_equals(&element_k)\n            {\n                return Ok(k.into());\n            }\n\n            // c. Set k to k + 1.\n        }\n\n        // 12. Return -1𝔽.\n        Ok((-1).into())\n    }\n\n    /// `%TypedArray%.prototype.join ( separator )`\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-%typedarray%.prototype.join\n    pub(crate) fn join(\n        this: &JsValue,\n        args: &[JsValue],\n        context: &mut Context,\n    ) -> JsResult<JsValue> {\n        // 1. Let O be the this value.\n        // 2. Let taRecord be ? ValidateTypedArray(O, seq-cst).\n        let (ta, buf_len) = TypedArray::validate(this, Ordering::SeqCst)?;\n\n        // 3. Let len be TypedArrayLength(taRecord).\n        let len = ta.borrow().data().array_length(buf_len);\n\n        // 4. If separator is undefined, let sep be the single-element String \",\".\n        let separator = args.get_or_undefined(0);\n        let sep = if separator.is_undefined() {\n            js_string!(\",\")\n        // 5. Else, let sep be ? ToString(separator).\n        } else {\n            separator.to_string(context)?\n        };\n\n        // 6. Let R be the empty String.\n        let mut r = Vec::with_capacity(len as usize);\n\n        // 7. Let k be 0.\n        // 8. Repeat, while k < len,\n        let ta = ta.upcast();\n        for k in 0..len {\n            // a. If k > 0, set R to the string-concatenation of R and sep.\n            if k > 0 {\n                r.extend(sep.iter());\n            }\n\n            // b. Let element be ! Get(O, ! ToString(𝔽(k))).\n            let element = ta.get(k, context).js_expect(\"Get cannot fail here\")?;\n\n            // c. If element is undefined, let next be the empty String; otherwise, let next be ! ToString(element).\n            // d. Set R to the string-concatenation of R and next.\n            if !element.is_undefined() {\n                r.extend(element.to_string(context)?.iter());\n            }\n        }\n\n        // 9. Return R.\n        Ok(js_string!(&r[..]).into())\n    }\n\n    /// `%TypedArray%.prototype.keys ( )`\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-%typedarray%.prototype.keys\n    pub(crate) fn keys(this: &JsValue, _: &[JsValue], context: &mut Context) -> JsResult<JsValue> {\n        // 1. Let O be the this value.\n        // 2. Let taRecord be ? ValidateTypedArray(O, seq-cst).\n        let (ta, _) = TypedArray::validate(this, Ordering::SeqCst)?;\n\n        // 3. Return CreateArrayIterator(O, key).\n        Ok(ArrayIterator::create_array_iterator(\n            ta.upcast(),\n            PropertyNameKind::Key,\n            context,\n        ))\n    }\n\n    /// `%TypedArray%.prototype.lastIndexOf ( searchElement [ , fromIndex ] )`\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-%typedarray%.prototype.lastindexof\n    pub(crate) fn last_index_of(\n        this: &JsValue,\n        args: &[JsValue],\n        context: &mut Context,\n    ) -> JsResult<JsValue> {\n        // 1. Let O be the this value.\n        // 2. Let taRecord be ? ValidateTypedArray(O, seq-cst).\n        let (ta, buf_len) = TypedArray::validate(this, Ordering::SeqCst)?;\n\n        // 3. Let len be TypedArrayLength(taRecord).\n        let len = ta.borrow().data().array_length(buf_len);\n\n        // 4. If len is 0, return -1𝔽.\n        if len == 0 {\n            return Ok((-1).into());\n        }\n\n        // 5. If fromIndex is present, let n be ? ToIntegerOrInfinity(fromIndex); else let n be len - 1.\n        let k = match args.get(1) {\n            None => len,\n            Some(n) => {\n                let n = n.to_integer_or_infinity(context)?;\n                match n {\n                    // 6. If n is -∞, return -1𝔽.\n                    IntegerOrInfinity::NegativeInfinity => return Ok((-1).into()),\n                    // 7. If n ≥ 0, then\n                    // a. Let k be min(n, len - 1).\n                    IntegerOrInfinity::Integer(i) if i >= 0 => min(i as u64 + 1, len),\n                    IntegerOrInfinity::PositiveInfinity => len,\n                    // 8. Else,\n                    // a. Let k be len + n.\n                    IntegerOrInfinity::Integer(i) => len.saturating_add_signed(i + 1),\n                }\n            }\n        };\n\n        // 9. Repeat, while k ≥ 0,\n        let ta = ta.upcast();\n        for k in (0..k).rev() {\n            // a. Let kPresent be ! HasProperty(O, ! ToString(𝔽(k))).\n            // b. If kPresent is true, then\n            // b.i. Let elementK be ! Get(O, ! ToString(𝔽(k))).\n            //   ii. Let same be IsStrictlyEqual(searchElement, elementK).\n            //   iii. If same is true, return 𝔽(k).\n            if let Some(element_k) = ta.try_get(k, context).js_expect(\"Get cannot fail here\")?\n                && args.get_or_undefined(0).strict_equals(&element_k)\n            {\n                return Ok(k.into());\n            }\n\n            // c. Set k to k - 1.\n        }\n\n        // 10. Return -1𝔽.\n        Ok((-1).into())\n    }\n\n    /// `get %TypedArray%.prototype.length`\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-get-%typedarray%.prototype.length\n    pub(crate) fn length(this: &JsValue, _: &[JsValue], _: &mut Context) -> JsResult<JsValue> {\n        // 1. Let O be the this value.\n        // 2. Perform ? RequireInternalSlot(O, [[TypedArrayName]]).\n        // 3. Assert: O has [[ViewedArrayBuffer]] and [[ArrayLength]] internal slots.\n        let object = this.as_object();\n        let ta = object\n            .as_ref()\n            .and_then(JsObject::downcast_ref::<TypedArray>)\n            .ok_or_else(|| {\n                JsNativeError::typ().with_message(\"`this` is not a typed array object\")\n            })?;\n\n        // 4. Let taRecord be MakeTypedArrayWithBufferWitnessRecord(O, seq-cst).\n        // 5. If IsTypedArrayOutOfBounds(taRecord) is true, return +0𝔽.\n        let buf = ta.viewed_array_buffer().as_buffer();\n        let Some(buf) = buf\n            .bytes(Ordering::SeqCst)\n            .filter(|s| !ta.is_out_of_bounds(s.len()))\n        else {\n            return Ok(0.into());\n        };\n\n        // 6. Let length be TypedArrayLength(taRecord).\n        // 7. Return 𝔽(length).\n        Ok(ta.array_length(buf.len()).into())\n    }\n\n    /// `%TypedArray%.prototype.map ( callbackfn [ , thisArg ] )`\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-%typedarray%.prototype.map\n    pub(crate) fn map(\n        this: &JsValue,\n        args: &[JsValue],\n        context: &mut Context,\n    ) -> JsResult<JsValue> {\n        // 1. Let O be the this value.\n        // 2. Let taRecord be ? ValidateTypedArray(O, seq-cst).\n        let (ta, buf_len) = TypedArray::validate(this, Ordering::SeqCst)?;\n\n        // 3. Let len be TypedArrayLength(taRecord).\n        let len = ta.borrow().data().array_length(buf_len);\n\n        let typed_array_kind = ta.borrow().data().kind();\n\n        // 4. If IsCallable(callbackfn) is false, throw a TypeError exception.\n        let callback_fn = match args.get_or_undefined(0).as_object() {\n            Some(obj) if obj.is_callable() => obj,\n            _ => {\n                return Err(JsNativeError::typ()\n                    .with_message(\n                        \"TypedArray.prototype.map called with non-callable callback function\",\n                    )\n                    .into());\n            }\n        };\n\n        let ta = ta.upcast();\n\n        // 5. Let A be ? TypedArraySpeciesCreate(O, « 𝔽(len) »).\n        let a = Self::species_create(&ta, typed_array_kind, &[len.into()], context)?.upcast();\n\n        // 6. Let k be 0.\n        // 7. Repeat, while k < len,\n        for k in 0..len {\n            // a. Let Pk be ! ToString(𝔽(k)).\n            // b. Let kValue be ! Get(O, Pk).\n            let k_value = ta.get(k, context).js_expect(\"Get cannot fail here\")?;\n\n            // c. Let mappedValue be ? Call(callbackfn, thisArg, « kValue, 𝔽(k), O »).\n            let mapped_value = callback_fn.call(\n                args.get_or_undefined(1),\n                &[k_value, k.into(), this.clone()],\n                context,\n            )?;\n\n            // d. Perform ? Set(A, Pk, mappedValue, true).\n            a.set(k, mapped_value, true, context)?;\n        }\n\n        // 8. Return A.\n        Ok(a.into())\n    }\n\n    /// `%TypedArray%.prototype.reduce ( callbackfn [ , initialValue ] )`\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-%typedarray%.prototype.reduce\n    pub(crate) fn reduce(\n        this: &JsValue,\n        args: &[JsValue],\n        context: &mut Context,\n    ) -> JsResult<JsValue> {\n        // 1. Let O be the this value.\n        // 2. Let taRecord be ? ValidateTypedArray(O, seq-cst).\n        let (ta, buf_len) = TypedArray::validate(this, Ordering::SeqCst)?;\n\n        // 3. Let len be TypedArrayLength(taRecord).\n        let len = ta.borrow().data().array_length(buf_len);\n\n        // 4. If IsCallable(callbackfn) is false, throw a TypeError exception.\n        let callback_fn =\n            match args.get_or_undefined(0).as_object() {\n                Some(obj) if obj.is_callable() => obj,\n                _ => return Err(JsNativeError::typ()\n                    .with_message(\n                        \"TypedArray.prototype.reduce called with non-callable callback function\",\n                    )\n                    .into()),\n            };\n\n        // 5. If len = 0 and initialValue is not present, throw a TypeError exception.\n        if len == 0 && args.get(1).is_none() {\n            return Err(JsNativeError::typ()\n                .with_message(\"Typed array length is 0 and initial value is not present\")\n                .into());\n        }\n\n        let ta = ta.upcast();\n\n        // 6. Let k be 0.\n        let mut k = 0;\n\n        // 7. Let accumulator be undefined.\n        // 8. If initialValue is present, then\n        let mut accumulator = if let Some(initial_value) = args.get(1) {\n            // a. Set accumulator to initialValue.\n            initial_value.clone()\n        // 9. Else,\n        } else {\n            // a. Let Pk be ! ToString(𝔽(k)).\n            // b. Set accumulator to ! Get(O, Pk).\n            // c. Set k to k + 1.\n            k += 1;\n            ta.get(0, context).js_expect(\"Get cannot fail here\")?\n        };\n\n        // 10. Repeat, while k < len,\n        for k in k..len {\n            // a. Let Pk be ! ToString(𝔽(k)).\n            // b. Let kValue be ! Get(O, Pk).\n            let k_value = ta.get(k, context).js_expect(\"Get cannot fail here\")?;\n\n            // c. Set accumulator to ? Call(callbackfn, undefined, « accumulator, kValue, 𝔽(k), O »).\n            accumulator = callback_fn.call(\n                &JsValue::undefined(),\n                &[accumulator, k_value, k.into(), this.clone()],\n                context,\n            )?;\n\n            // d. Set k to k + 1.\n        }\n\n        // 11. Return accumulator.\n        Ok(accumulator)\n    }\n\n    /// `%TypedArray%.prototype.reduceRight ( callbackfn [ , initialValue ] )`\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-%typedarray%.prototype.reduceright\n    pub(crate) fn reduceright(\n        this: &JsValue,\n        args: &[JsValue],\n        context: &mut Context,\n    ) -> JsResult<JsValue> {\n        // 1. Let O be the this value.\n        // 2. Let taRecord be ? ValidateTypedArray(O, seq-cst).\n        let (ta, buf_len) = TypedArray::validate(this, Ordering::SeqCst)?;\n\n        // 3. Let len be TypedArrayLength(taRecord).\n        let len = ta.borrow().data().array_length(buf_len);\n\n        // 4. If IsCallable(callbackfn) is false, throw a TypeError exception.\n        let callback_fn = match args.get_or_undefined(0).as_object() {\n            Some(obj) if obj.is_callable() => obj,\n            _ => return Err(JsNativeError::typ()\n                .with_message(\n                    \"TypedArray.prototype.reduceright called with non-callable callback function\",\n                )\n                .into()),\n        };\n\n        // 5. If len = 0 and initialValue is not present, throw a TypeError exception.\n        if len == 0 && args.get(1).is_none() {\n            return Err(JsNativeError::typ()\n                .with_message(\"Typed array length is 0 and initial value is not present\")\n                .into());\n        }\n\n        let ta = ta.upcast();\n\n        // 6. Let k be len - 1.\n        // 7. Let accumulator be undefined.\n        // 8. If initialValue is present, then\n        let (mut accumulator, k) = if let Some(initial_value) = args.get(1) {\n            // a. Set accumulator to initialValue.\n            (initial_value.clone(), len)\n        // 9. Else,\n        } else {\n            // a. Let Pk be ! ToString(𝔽(k)).\n            // b. Set accumulator to ! Get(O, Pk).\n            let accumulator = ta.get(len - 1, context).js_expect(\"Get cannot fail here\")?;\n\n            // c. Set k to k - 1.\n            (accumulator, len - 1)\n        };\n\n        // 10. Repeat, while k ≥ 0,\n        for k in (0..k).rev() {\n            // a. Let Pk be ! ToString(𝔽(k)).\n            // b. Let kValue be ! Get(O, Pk).\n            let k_value = ta.get(k, context).js_expect(\"Get cannot fail here\")?;\n\n            // c. Set accumulator to ? Call(callbackfn, undefined, « accumulator, kValue, 𝔽(k), O »).\n            accumulator = callback_fn.call(\n                &JsValue::undefined(),\n                &[accumulator, k_value, k.into(), this.clone()],\n                context,\n            )?;\n\n            // d. Set k to k - 1.\n        }\n\n        // 11. Return accumulator.\n        Ok(accumulator)\n    }\n\n    /// `%TypedArray%.prototype.reverse ( )`\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-%typedarray%.prototype.reverse\n    #[allow(clippy::float_cmp)]\n    pub(crate) fn reverse(\n        this: &JsValue,\n        _: &[JsValue],\n        context: &mut Context,\n    ) -> JsResult<JsValue> {\n        // 1. Let O be the this value.\n        // 2. Let taRecord be ? ValidateTypedArray(O, seq-cst).\n        let (ta, buf_len) = TypedArray::validate(this, Ordering::SeqCst)?;\n\n        // 3. Let len be TypedArrayLength(taRecord).\n        let len = ta.borrow().data().array_length(buf_len);\n\n        let ta = ta.upcast();\n\n        // 4. Let middle be floor(len / 2).\n        let middle = len / 2;\n\n        // 5. Let lower be 0.\n        let mut lower = 0;\n        // 6. Repeat, while lower ≠ middle,\n        while lower != middle {\n            // a. Let upper be len - lower - 1.\n            let upper = len - lower - 1;\n\n            // b. Let upperP be ! ToString(𝔽(upper)).\n            // c. Let lowerP be ! ToString(𝔽(lower)).\n            // d. Let lowerValue be ! Get(O, lowerP).\n            let lower_value = ta.get(lower, context).js_expect(\"Get cannot fail here\")?;\n            // e. Let upperValue be ! Get(O, upperP).\n            let upper_value = ta.get(upper, context).js_expect(\"Get cannot fail here\")?;\n\n            // f. Perform ! Set(O, lowerP, upperValue, true).\n            ta.set(lower, upper_value, true, context)\n                .js_expect(\"Set cannot fail here\")?;\n            // g. Perform ! Set(O, upperP, lowerValue, true).\n            ta.set(upper, lower_value, true, context)\n                .js_expect(\"Set cannot fail here\")?;\n\n            // h. Set lower to lower + 1.\n            lower += 1;\n        }\n\n        // 7. Return O.\n        Ok(this.clone())\n    }\n\n    /// [`%TypedArray%.prototype.toReversed ( )`][spec]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-%typedarray%.prototype.toreversed\n    pub(crate) fn to_reversed(\n        this: &JsValue,\n        _: &[JsValue],\n        context: &mut Context,\n    ) -> JsResult<JsValue> {\n        // 1. Let O be the this value.\n        // 2. Let taRecord be ? ValidateTypedArray(O, seq-cst).\n        let (ta, buf_len) = TypedArray::validate(this, Ordering::SeqCst)?;\n\n        // 3. Let len be TypedArrayLength(taRecord).\n        let len = ta.borrow().data().array_length(buf_len);\n        let kind = ta.borrow().data().kind();\n\n        // 4. Let A be ? TypedArrayCreateSameType(O, « 𝔽(length) »).\n        let new_array = Self::from_kind_and_length(kind, len, context)?;\n\n        // 5. Let k be 0.\n        // 6. Repeat, while k < length,\n        let ta = ta.upcast();\n        for k in 0..len {\n            // a. Let from be ! ToString(𝔽(length - k - 1)).\n            // b. Let Pk be ! ToString(𝔽(k)).\n            // c. Let fromValue be ! Get(O, from).\n            let value = ta\n                .get(len - k - 1, context)\n                .js_expect(\"cannot fail per the spec\")?;\n            // d. Perform ! Set(A, Pk, fromValue, true).\n            new_array\n                .set(k, value, true, context)\n                .js_expect(\"cannot fail per the spec\")?;\n            // e. Set k to k + 1.\n        }\n\n        // 7. Return A.\n        Ok(new_array.into())\n    }\n\n    /// `%TypedArray%.prototype.set ( source [ , offset ] )`\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-%typedarray%.prototype.set\n    pub(crate) fn set(\n        this: &JsValue,\n        args: &[JsValue],\n        context: &mut Context,\n    ) -> JsResult<JsValue> {\n        // 1. Let target be the this value.\n        // 2. Perform ? RequireInternalSlot(target, [[TypedArrayName]]).\n        // 3. Assert: target has a [[ViewedArrayBuffer]] internal slot.\n        let target = this\n            .as_object()\n            .and_then(|o| o.clone().downcast::<TypedArray>().ok())\n            .ok_or_else(|| {\n                JsNativeError::typ()\n                    .with_message(\"TypedArray.set must be called on typed array object\")\n            })?;\n\n        // 4. Let targetOffset be ? ToIntegerOrInfinity(offset).\n        let target_offset = args.get_or_undefined(1).to_integer_or_infinity(context)?;\n\n        // 5. If targetOffset < 0, throw a RangeError exception.\n        let target_offset = match target_offset {\n            IntegerOrInfinity::Integer(i) if i < 0 => {\n                return Err(JsNativeError::range()\n                    .with_message(\"TypedArray.set called with negative offset\")\n                    .into());\n            }\n            IntegerOrInfinity::NegativeInfinity => {\n                return Err(JsNativeError::range()\n                    .with_message(\"TypedArray.set called with negative offset\")\n                    .into());\n            }\n            IntegerOrInfinity::PositiveInfinity => U64OrPositiveInfinity::PositiveInfinity,\n            IntegerOrInfinity::Integer(i) => U64OrPositiveInfinity::U64(i as u64),\n        };\n\n        // 6. If source is an Object that has a [[TypedArrayName]] internal slot, then\n        let source = args.get_or_undefined(0);\n        if let Some(source) = source\n            .as_object()\n            .and_then(|o| o.clone().downcast::<TypedArray>().ok())\n        {\n            // a. Perform ? SetTypedArrayFromTypedArray(target, targetOffset, source).\n            Self::set_typed_array_from_typed_array(&target, &target_offset, &source, context)?;\n        }\n        // 7. Else,\n        else {\n            // a. Perform ? SetTypedArrayFromArrayLike(target, targetOffset, source).\n            Self::set_typed_array_from_array_like(&target, &target_offset, source, context)?;\n        }\n\n        // 8. Return undefined.\n        Ok(JsValue::undefined())\n    }\n\n    /// `3.2.3.24.1 SetTypedArrayFromTypedArray ( target, targetOffset, source )`\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-settypedarrayfromtypedarray\n    fn set_typed_array_from_typed_array(\n        target: &JsObject<TypedArray>,\n        target_offset: &U64OrPositiveInfinity,\n        source: &JsObject<TypedArray>,\n        context: &mut Context,\n    ) -> JsResult<()> {\n        // 1. Let targetBuffer be target.[[ViewedArrayBuffer]].\n        // 2. Let targetRecord be MakeTypedArrayWithBufferWitnessRecord(target, seq-cst).\n        // 3. If IsTypedArrayOutOfBounds(targetRecord) is true, throw a TypeError exception.\n        let target_array = target.borrow();\n        let target_buf_obj = target_array.data().viewed_array_buffer().clone();\n        let Some(target_buf_len) = target_buf_obj\n            .as_buffer()\n            .bytes(Ordering::SeqCst)\n            .filter(|s| !target_array.data().is_out_of_bounds(s.len()))\n            .map(|s| s.len())\n        else {\n            return Err(JsNativeError::typ()\n                .with_message(\"typed array is outside the bounds of its inner buffer\")\n                .into());\n        };\n\n        // 4. Let targetLength be TypedArrayLength(targetRecord).\n        let target_length = target_array.data().array_length(target_buf_len);\n\n        // 5. Let srcBuffer be source.[[ViewedArrayBuffer]].\n        // 6. Let srcRecord be MakeTypedArrayWithBufferWitnessRecord(source, seq-cst).\n        // 7. If IsTypedArrayOutOfBounds(srcRecord) is true, throw a TypeError exception.\n        let src_array = source.borrow();\n        let mut src_buf_obj = src_array.data().viewed_array_buffer().clone();\n        let Some(mut src_buf_len) = src_buf_obj\n            .as_buffer()\n            .bytes(Ordering::SeqCst)\n            .filter(|s| !src_array.data().is_out_of_bounds(s.len()))\n            .map(|s| s.len())\n        else {\n            return Err(JsNativeError::typ()\n                .with_message(\"typed array is outside the bounds of its inner buffer\")\n                .into());\n        };\n\n        // 8. Let srcLength be TypedArrayLength(srcRecord).\n        let src_length = src_array.data().array_length(src_buf_len);\n\n        // 9. Let targetType be TypedArrayElementType(target).\n        let target_type = target_array.data().kind();\n\n        // 10. Let targetElementSize be TypedArrayElementSize(target).\n        let target_element_size = target_type.element_size();\n\n        // 11. Let targetByteOffset be target.[[ByteOffset]].\n        let target_byte_offset = target_array.data().byte_offset();\n\n        // 12. Let srcType be TypedArrayElementType(source).\n        let src_type = src_array.data().kind();\n\n        // 13. Let srcElementSize be TypedArrayElementSize(source).\n        let src_element_size = src_type.element_size();\n\n        // 14. Let srcByteOffset be source.[[ByteOffset]].\n        let src_byte_offset = src_array.data().byte_offset();\n\n        // a. Let srcByteLength be source.[[ByteLength]].\n        let src_byte_length = src_array.data().byte_length(src_buf_len);\n\n        drop(target_array);\n        drop(src_array);\n\n        // 15. If targetOffset = +∞, throw a RangeError exception.\n        let U64OrPositiveInfinity::U64(target_offset) = target_offset else {\n            return Err(JsNativeError::range()\n                .with_message(\"Target offset cannot be Infinity\")\n                .into());\n        };\n\n        // 16. If srcLength + targetOffset > targetLength, throw a RangeError exception.\n        if src_length + target_offset > target_length {\n            return Err(JsNativeError::range()\n                .with_message(\"Source typed array and target offset longer than target typed array\")\n                .into());\n        }\n\n        // 17. If target.[[ContentType]] is not source.[[ContentType]], throw a TypeError exception.\n        if target_type.content_type() != src_type.content_type() {\n            return Err(JsNativeError::typ()\n                .with_message(\n                    \"Source typed array and target typed array have different content types\",\n                )\n                .into());\n        }\n\n        // 18. If IsSharedArrayBuffer(srcBuffer) is true, IsSharedArrayBuffer(targetBuffer) is true,\n        //     and srcBuffer.[[ArrayBufferData]] is targetBuffer.[[ArrayBufferData]], let\n        //     sameSharedArrayBuffer be true; otherwise, let sameSharedArrayBuffer be false.\n        // 19. If SameValue(srcBuffer, targetBuffer) is true or sameSharedArrayBuffer is true, then\n        let src_byte_index = if BufferObject::equals(&src_buf_obj, &target_buf_obj) {\n            // a. Let srcByteLength be source.[[ByteLength]].\n            let src_byte_offset = src_byte_offset as usize;\n            let src_byte_length = src_byte_length as usize;\n\n            let s = {\n                let slice = src_buf_obj.as_buffer();\n                let slice = slice\n                    .bytes_with_len(src_buf_len)\n                    .js_expect(\"Already checked for detached buffer\")?;\n\n                // b. Set srcBuffer to ? CloneArrayBuffer(srcBuffer, srcByteOffset, srcByteLength, %ArrayBuffer%).\n                // c. NOTE: %ArrayBuffer% is used to clone srcBuffer because is it known to not have any observable side-effects.\n                let subslice = slice.subslice(src_byte_offset..src_byte_offset + src_byte_length);\n                src_buf_len = subslice.len();\n\n                subslice.clone(context)?\n            };\n\n            src_buf_obj = BufferObject::Buffer(s);\n\n            // d. Let srcByteIndex be 0.\n            0\n        }\n        // 20. Else,\n        else {\n            // a. Let srcByteIndex be srcByteOffset.\n            src_byte_offset\n        };\n\n        // 22. Let targetByteIndex be targetOffset × targetElementSize + targetByteOffset.\n        let target_byte_index = target_offset * target_element_size + target_byte_offset;\n\n        let src_buffer = src_buf_obj.as_buffer();\n        let src_buffer = src_buffer\n            .bytes_with_len(src_buf_len)\n            .js_expect(\"Already checked for detached buffer\")?;\n\n        let mut target_buffer = target_buf_obj.as_buffer_mut();\n        let mut target_buffer = target_buffer\n            .bytes_with_len(target_buf_len)\n            .js_expect(\"Already checked for detached buffer\")?;\n\n        // 24. If srcType is the same as targetType, then\n        if src_type == target_type {\n            let src_byte_index = src_byte_index as usize;\n            let target_byte_index = target_byte_index as usize;\n            let byte_count = (target_element_size * src_length) as usize;\n\n            // a. NOTE: If srcType and targetType are the same, the transfer must be performed in a manner that preserves the bit-level encoding of the source data.\n            // b. Repeat, while targetByteIndex < limit,\n            // i. Let value be GetValueFromBuffer(srcBuffer, srcByteIndex, Uint8, true, Unordered).\n            // ii. Perform SetValueInBuffer(targetBuffer, targetByteIndex, Uint8, value, true, Unordered).\n            // iii. Set srcByteIndex to srcByteIndex + 1.\n            // iv. Set targetByteIndex to targetByteIndex + 1.\n            let src = src_buffer.subslice(src_byte_index..);\n            let mut target = target_buffer.subslice_mut(target_byte_index..);\n\n            #[cfg(debug_assertions)]\n            {\n                assert!(src.len() >= byte_count);\n                assert!(target.len() >= byte_count);\n            }\n\n            // SAFETY: We already asserted that the indices are in bounds.\n            unsafe {\n                memcpy(src.as_ptr(), target.as_ptr(), byte_count);\n            }\n        }\n        // 25. Else,\n        else {\n            // 23. Let limit be targetByteIndex + targetElementSize × srcLength.\n            let limit = (target_byte_index + target_element_size * src_length) as usize;\n\n            let mut src_byte_index = src_byte_index as usize;\n            let mut target_byte_index = target_byte_index as usize;\n\n            // a. Repeat, while targetByteIndex < limit,\n            while target_byte_index < limit {\n                // i. Let value be GetValueFromBuffer(srcBuffer, srcByteIndex, srcType, true, Unordered).\n\n                let value = unsafe {\n                    src_buffer\n                        .subslice(src_byte_index..)\n                        .get_value(src_type, Ordering::Relaxed)\n                };\n\n                let value = JsValue::from(value);\n\n                let value = target_type\n                    .get_element(&value, context)\n                    .js_expect(\"value can only be f64 or BigInt\")?;\n\n                // ii. Perform SetValueInBuffer(targetBuffer, targetByteIndex, targetType, value, true, Unordered).\n                // SAFETY: previous checks preserve the validity  of the indices.\n                unsafe {\n                    target_buffer\n                        .subslice_mut(target_byte_index..)\n                        .set_value(value, Ordering::Relaxed);\n                }\n\n                // iii. Set srcByteIndex to srcByteIndex + srcElementSize.\n                src_byte_index += src_element_size as usize;\n\n                // iv. Set targetByteIndex to targetByteIndex + targetElementSize.\n                target_byte_index += target_element_size as usize;\n            }\n        }\n\n        Ok(())\n    }\n\n    /// `SetTypedArrayFromArrayLike ( target, targetOffset, source )`\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-settypedarrayfromarraylike\n    fn set_typed_array_from_array_like(\n        target: &JsObject<TypedArray>,\n        target_offset: &U64OrPositiveInfinity,\n        source: &JsValue,\n        context: &mut Context,\n    ) -> JsResult<()> {\n        // 3. Let targetLength be TypedArrayLength(targetRecord).\n        let target_length = {\n            let target = target.borrow();\n            let target = target.data();\n\n            // 1. Let targetRecord be MakeTypedArrayWithBufferWitnessRecord(target, seq-cst).\n            // 2. If IsTypedArrayOutOfBounds(targetRecord) is true, throw a TypeError exception.\n            let Some(buf_len) = target\n                .viewed_array_buffer()\n                .as_buffer()\n                .bytes(Ordering::SeqCst)\n                .filter(|s| !target.is_out_of_bounds(s.len()))\n                .map(|s| s.len())\n            else {\n                return Err(JsNativeError::typ()\n                    .with_message(\"typed array is outside the bounds of its inner buffer\")\n                    .into());\n            };\n\n            // 3. Let targetLength be target.[[ArrayLength]].\n            target.array_length(buf_len)\n        };\n\n        // 4. Let src be ? ToObject(source).\n        let src = source.to_object(context)?;\n\n        // 5. Let srcLength be ? LengthOfArrayLike(src).\n        let src_length = src.length_of_array_like(context)?;\n\n        // 6. If targetOffset = +∞, throw a RangeError exception.\n        let target_offset = match target_offset {\n            U64OrPositiveInfinity::U64(target_offset) => target_offset,\n            U64OrPositiveInfinity::PositiveInfinity => {\n                return Err(JsNativeError::range()\n                    .with_message(\"Target offset cannot be positive infinity\")\n                    .into());\n            }\n        };\n\n        // 7. If srcLength + targetOffset > targetLength, throw a RangeError exception.\n        if src_length + target_offset > target_length {\n            return Err(JsNativeError::range()\n                .with_message(\"Source object and target offset longer than target typed array\")\n                .into());\n        }\n\n        // 8. Let k be 0.\n        // 9. Repeat, while k < srcLength,\n        let target = target.clone().upcast();\n        for k in 0..src_length {\n            // a. Let Pk be ! ToString(𝔽(k)).\n            // b. Let value be ? Get(src, Pk).\n            let value = src.get(k, context)?;\n\n            // c. Let targetIndex be 𝔽(targetOffset + k).\n            let target_index = target_offset + k;\n\n            // d. Perform ? IntegerIndexedElementSet(target, targetIndex, value).\n            typed_array_set_element(&target, target_index as f64, &value, &mut context.into())?;\n\n            // e. Set k to k + 1.\n        }\n\n        // 10. Return unused.\n        Ok(())\n    }\n\n    /// `%TypedArray%.prototype.slice ( start, end )`\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-%typedarray%.prototype.slice\n    pub(crate) fn slice(\n        this: &JsValue,\n        args: &[JsValue],\n        context: &mut Context,\n    ) -> JsResult<JsValue> {\n        // 1. Let O be the this value.\n        // 2. Let taRecord be ? ValidateTypedArray(O, seq-cst).\n        let (src, buf_len) = TypedArray::validate(this, Ordering::SeqCst)?;\n\n        let src_borrow = src.borrow();\n\n        // 3. Let srcArrayLength be TypedArrayLength(taRecord).\n        let src_len = src_borrow.data().array_length(buf_len);\n\n        // e. Let srcType be TypedArrayElementType(O).\n        let src_type = src_borrow.data().kind();\n\n        drop(src_borrow);\n\n        // 4. Let relativeStart be ? ToIntegerOrInfinity(start).\n        // 5. If relativeStart = -∞, let startIndex be 0.\n        // 6. Else if relativeStart < 0, let startIndex be max(srcArrayLength + relativeStart, 0).\n        // 7. Else, let startIndex be min(relativeStart, srcArrayLength).\n        let start_index = Array::get_relative_start(context, args.get_or_undefined(0), src_len)?;\n\n        // 8. If end is undefined, let relativeEnd be srcArrayLength; else let relativeEnd be ? ToIntegerOrInfinity(end).\n        // 9. If relativeEnd = -∞, let endIndex be 0.\n        // 10. Else if relativeEnd < 0, let endIndex be max(srcArrayLength + relativeEnd, 0).\n        // 11. Else, let endIndex be min(relativeEnd, srcArrayLength).\n        let end_index = Array::get_relative_end(context, args.get_or_undefined(1), src_len)?;\n\n        // 12. Let countBytes be max(endIndex - startIndex, 0).\n        let count = end_index.saturating_sub(start_index);\n\n        // 13. Let A be ? TypedArraySpeciesCreate(O, « 𝔽(countBytes) »).\n        let target =\n            Self::species_create(&src.clone().upcast(), src_type, &[count.into()], context)?;\n\n        // 14. If countBytes > 0, then\n        if count == 0 {\n            // 15. Return A.\n            return Ok(target.upcast().into());\n        }\n\n        let src_borrow = src.borrow();\n        let target_borrow = target.borrow();\n\n        // a. Set taRecord to MakeTypedArrayWithBufferWitnessRecord(O, seq-cst).\n        // b. If IsTypedArrayOutOfBounds(taRecord) is true, throw a TypeError exception.\n        let src_buf_borrow = src_borrow.data().viewed_array_buffer().as_buffer();\n        let Some(src_buf) = src_buf_borrow\n            .bytes(Ordering::SeqCst)\n            .filter(|s| !src_borrow.data().is_out_of_bounds(s.len()))\n        else {\n            return Err(JsNativeError::typ()\n                .with_message(\"typed array is outside the bounds of its inner buffer\")\n                .into());\n        };\n\n        let src_buf_len = src_buf.len();\n\n        // c. Set endIndex to min(endIndex, TypedArrayLength(taRecord)).\n        let end_index = min(end_index, src_borrow.data().array_length(src_buf_len));\n\n        // d. Set countBytes to maxlen(endIndex - startIndex, 0).\n        let count = end_index.saturating_sub(start_index) as usize;\n\n        // The inner buffer may have resized between getting the indices and getting the buffer\n        // itself. Check that the count is not zero again before proceeding.\n        if count == 0 {\n            drop(target_borrow);\n            return Ok(target.upcast().into());\n        }\n\n        // f. Let targetType be TypedArrayElementType(A).\n        let target_type = target_borrow.data().kind();\n\n        if src_type != target_type {\n            // h. Else,\n            drop(src_buf_borrow);\n            drop((src_borrow, target_borrow));\n\n            // i. Let n be 0.\n            // ii. Let k be startIndex.\n            // iii. Repeat, while k < endIndex,\n            let src = src.upcast();\n            let target = target.upcast();\n            for (n, k) in (start_index..end_index).enumerate() {\n                // 1. Let Pk be ! ToString(𝔽(k)).\n                // 2. Let kValue be ! Get(O, Pk).\n                let k_value = src.get(k, context).js_expect(\"Get cannot fail here\")?;\n\n                // 3. Perform ! Set(A, ! ToString(𝔽(n)), kValue, true).\n                target\n                    .set(n, k_value, true, context)\n                    .js_expect(\"Set cannot fail here\")?;\n\n                // 4. Set k to k + 1.\n                // 5. Set n to n + 1.\n            }\n\n            // 15. Return A.\n            return Ok(target.into());\n        }\n\n        // g. If srcType is targetType, then\n        {\n            let byte_count = count * src_type.element_size() as usize;\n\n            // i. NOTE: The transfer must be performed in a manner that preserves the bit-level encoding of the source data.\n            // ii. Let srcBuffer be O.[[ViewedArrayBuffer]].\n            // iii. Let targetBuffer be A.[[ViewedArrayBuffer]].\n\n            // iv. Let elementSize be TypedArrayElementSize(O).\n            let element_size = src_type.element_size();\n\n            // v. Let srcByteOffset be O.[[ByteOffset]].\n            let src_byte_offset = src_borrow.data().byte_offset();\n\n            // vi. Let srcByteIndex be (startIndex × elementSize) + srcByteOffset.\n            let src_byte_index = (start_index * element_size + src_byte_offset) as usize;\n\n            // vii. Let targetByteIndex be A.[[ByteOffset]].\n            let target_byte_index = target_borrow.data().byte_offset() as usize;\n\n            // viii. Let endByteIndex be targetByteIndex + (countBytes × elementSize).\n            // Not needed by the impl.\n\n            // ix. Repeat, while targetByteIndex < endByteIndex,\n            //     1. Let value be GetValueFromBuffer(srcBuffer, srcByteIndex, uint8, true, unordered).\n            //     2. Perform SetValueInBuffer(targetBuffer, targetByteIndex, uint8, value, true, unordered).\n            //     3. Set srcByteIndex to srcByteIndex + 1.\n            //     4. Set targetByteIndex to targetByteIndex + 1.\n            if BufferObject::equals(\n                src_borrow.data().viewed_array_buffer(),\n                target_borrow.data().viewed_array_buffer(),\n            ) {\n                // cannot borrow the target mutably (overlapping bytes), but we can move the data instead.\n                drop(src_buf_borrow);\n\n                let mut src_buf_borrow = src_borrow.data().viewed_array_buffer().as_buffer_mut();\n                let mut src_buf = src_buf_borrow\n                    .bytes_with_len(src_buf_len)\n                    .js_expect(\"already checked that the buffer is not detached\")?;\n\n                #[cfg(debug_assertions)]\n                {\n                    assert!(src_buf.subslice_mut(src_byte_index..).len() >= byte_count);\n                    assert!(src_buf.subslice_mut(target_byte_index..).len() >= byte_count);\n                }\n\n                // SAFETY: All previous checks put the copied bytes at least within the bounds of `src_buf`.\n                unsafe {\n                    memmove_naive(\n                        src_buf.as_ptr(),\n                        src_byte_index,\n                        target_byte_index,\n                        byte_count,\n                    );\n                }\n            } else {\n                let mut target_buf = target_borrow.data().viewed_array_buffer().as_buffer_mut();\n                let mut target_buf = target_buf\n                    .bytes(Ordering::SeqCst)\n                    .js_expect(\"newly created array cannot be detached\")?;\n\n                #[cfg(debug_assertions)]\n                {\n                    assert!(src_buf.subslice(src_byte_index..).len() >= byte_count);\n                    assert!(target_buf.subslice_mut(target_byte_index..).len() >= byte_count);\n                }\n\n                // SAFETY: All previous checks put the indices at least within the bounds of `src_buffer`.\n                // Also, `target_buffer` is precisely allocated to fit all sliced elements from\n                // `src_buffer`, making this operation safe.\n                unsafe {\n                    memcpy(\n                        src_buf.as_ptr().add(src_byte_index),\n                        target_buf.as_ptr().add(target_byte_index),\n                        byte_count,\n                    );\n                }\n            }\n        }\n\n        drop(target_borrow);\n        // 15. Return A.\n        Ok(target.upcast().into())\n    }\n\n    /// `%TypedArray%.prototype.some ( callbackfn [ , thisArg ] )`\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-%typedarray%.prototype.some\n    pub(crate) fn some(\n        this: &JsValue,\n        args: &[JsValue],\n        context: &mut Context,\n    ) -> JsResult<JsValue> {\n        // 1. Let O be the this value.\n        // 2. Let taRecord be ? ValidateTypedArray(O, seq-cst).\n        let (ta, buf_len) = TypedArray::validate(this, Ordering::SeqCst)?;\n\n        // 3. Let len be TypedArrayLength(taRecord).\n        let len = ta.borrow().data().array_length(buf_len);\n\n        // 4. If IsCallable(callbackfn) is false, throw a TypeError exception.\n        let callback_fn = match args.get_or_undefined(0).as_object() {\n            Some(obj) if obj.is_callable() => obj,\n            _ => {\n                return Err(JsNativeError::typ()\n                    .with_message(\n                        \"TypedArray.prototype.some called with non-callable callback function\",\n                    )\n                    .into());\n            }\n        };\n\n        // 5. Let k be 0.\n        // 6. Repeat, while k < len,\n        let ta = ta.upcast();\n        for k in 0..len {\n            // a. Let Pk be ! ToString(𝔽(k)).\n            // b. Let kValue be ! Get(O, Pk).\n            let k_value = ta.get(k, context).js_expect(\"Get cannot fail here\")?;\n\n            // c. Let testResult be ! ToBoolean(? Call(callbackfn, thisArg, « kValue, 𝔽(k), O »)).\n            // d. If testResult is true, return true.\n            if callback_fn\n                .call(\n                    args.get_or_undefined(1),\n                    &[k_value, k.into(), this.clone()],\n                    context,\n                )?\n                .to_boolean()\n            {\n                return Ok(true.into());\n            }\n        }\n\n        // 7. Return false.\n        Ok(false.into())\n    }\n\n    /// `%TypedArray%.prototype.sort ( comparefn )`\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-%typedarray%.prototype.sort\n    pub(crate) fn sort(\n        this: &JsValue,\n        args: &[JsValue],\n        context: &mut Context,\n    ) -> JsResult<JsValue> {\n        // 1. If comparefn is not undefined and IsCallable(comparefn) is false, throw a TypeError exception.\n        let compare_fn = match args.first().map(JsValue::variant) {\n            None | Some(JsVariant::Undefined) => None,\n            Some(JsVariant::Object(obj)) if obj.is_callable() => Some(obj),\n            _ => {\n                return Err(JsNativeError::typ()\n                    .with_message(\"TypedArray.sort called with non-callable comparefn\")\n                    .into());\n            }\n        };\n\n        // 2. Let obj be the this value.\n        // 3. Let taRecord be ? ValidateTypedArray(obj, seq-cst).\n        let (ta, buf_len) = TypedArray::validate(this, Ordering::SeqCst)?;\n\n        // 4. Let len be TypedArrayLength(taRecord).\n        let len = ta.borrow().data().array_length(buf_len);\n\n        // 5. NOTE: The following closure performs a numeric comparison rather than the string comparison used in 23.1.3.30.\n        // 6. Let SortCompare be a new Abstract Closure with parameters (x, y) that captures comparefn and performs the following steps when called:\n        let sort_compare =\n            |x: &JsValue, y: &JsValue, context: &mut Context| -> JsResult<cmp::Ordering> {\n                // a. Return ? CompareTypedArrayElements(x, y, comparefn).\n                compare_typed_array_elements(x, y, compare_fn.as_ref(), context)\n            };\n\n        let ta = ta.upcast();\n        // 7. Let sortedList be ? SortIndexedProperties(obj, len, SortCompare, read-through-holes).\n        let sorted = Array::sort_indexed_properties(&ta, len, sort_compare, false, context)?;\n\n        // 8. Let j be 0.\n        // 9. Repeat, while j < len,\n        for (j, item) in sorted.into_iter().enumerate() {\n            // a. Perform ! Set(obj, ! ToString(𝔽(j)), sortedList[j], true).\n            ta.set(j, item, true, context)\n                .js_expect(\"cannot fail per spec\")?;\n\n            // b. Set j to j + 1.\n        }\n\n        // 10. Return obj.\n        Ok(ta.into())\n    }\n\n    /// [`%TypedArray%.prototype.toSorted ( comparefn )`][spec]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-%typedarray%.prototype.tosorted\n    pub(crate) fn to_sorted(\n        this: &JsValue,\n        args: &[JsValue],\n        context: &mut Context,\n    ) -> JsResult<JsValue> {\n        // 1. If comparefn is not undefined and IsCallable(comparefn) is false, throw a TypeError exception.\n        let compare_fn = match args.first().map(JsValue::variant) {\n            None | Some(JsVariant::Undefined) => None,\n            Some(JsVariant::Object(obj)) if obj.is_callable() => Some(obj),\n            _ => {\n                return Err(JsNativeError::typ()\n                    .with_message(\"TypedArray.sort called with non-callable comparefn\")\n                    .into());\n            }\n        };\n\n        // 2. Let O be the this value.\n        // 3. Let taRecord be ? ValidateTypedArray(O, seq-cst).\n        let (ta, buf_len) = TypedArray::validate(this, Ordering::SeqCst)?;\n\n        // 4. Let len be TypedArrayLength(taRecord).\n        let len = ta.borrow().data().array_length(buf_len);\n\n        // 5. Let A be ? TypedArrayCreateSameType(O, « 𝔽(len) »).\n        let new_array = Self::from_kind_and_length(ta.borrow().data().kind(), len, context)?;\n\n        // 6. NOTE: The following closure performs a numeric comparison rather than the string comparison used in 23.1.3.34.\n        // 7. Let SortCompare be a new Abstract Closure with parameters (x, y) that captures comparefn and performs the following steps when called:\n        let sort_compare =\n            |x: &JsValue, y: &JsValue, context: &mut Context| -> JsResult<cmp::Ordering> {\n                // a. Return ? CompareTypedArrayElements(x, y, comparefn).\n                compare_typed_array_elements(x, y, compare_fn.as_ref(), context)\n            };\n\n        let ta = ta.upcast();\n\n        // 8. Let sortedList be ? SortIndexedProperties(O, len, SortCompare, read-through-holes).\n        let sorted = Array::sort_indexed_properties(&ta, len, sort_compare, false, context)?;\n\n        //  9. Let j be 0.\n        //  10. Repeat, while j < len;\n        for (j, item) in sorted.into_iter().enumerate() {\n            // a. Perform ! Set(A, ! ToString(𝔽(j)), sortedList[j], true).\n            new_array\n                .set(j, item, true, context)\n                .js_expect(\"cannot fail per spec\")?;\n\n            // b. Set j to j + 1.\n        }\n        // 11. Return A.\n        Ok(new_array.into())\n    }\n\n    /// `%TypedArray%.prototype.subarray ( begin, end )`\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-%typedarray%.prototype.subarray\n    pub(crate) fn subarray(\n        this: &JsValue,\n        args: &[JsValue],\n        context: &mut Context,\n    ) -> JsResult<JsValue> {\n        // 1. Let O be the this value.\n        // 2. Perform ? RequireInternalSlot(O, [[TypedArrayName]]).\n        // 3. Assert: O has a [[ViewedArrayBuffer]] internal slot.\n        let src = this\n            .as_object()\n            .and_then(|o| o.clone().downcast::<TypedArray>().ok())\n            .ok_or_else(|| {\n                JsNativeError::typ().with_message(\"Value is not a typed array object\")\n            })?;\n\n        let src_borrow = src.borrow();\n\n        // 4. Let buffer be O.[[ViewedArrayBuffer]].\n        let buffer = src_borrow.data().viewed_array_buffer().clone();\n\n        // 5. Let srcRecord be MakeTypedArrayWithBufferWitnessRecord(O, seq-cst).\n        // 6. If IsTypedArrayOutOfBounds(srcRecord) is true, then\n        //     a. Let srcLength be 0.\n        // 7. Else,\n        //     a. Let srcLength be TypedArrayLength(srcRecord).\n        let src_len = if let Some(buf) = buffer.as_buffer().bytes(Ordering::SeqCst)\n            && !src_borrow.data().is_out_of_bounds(buf.len())\n        {\n            src_borrow.data().array_length(buf.len())\n        } else {\n            0\n        };\n\n        let kind = src_borrow.data().kind();\n\n        // 12. Let elementSize be TypedArrayElementSize(O).\n        let element_size = kind.element_size();\n\n        // 13. Let srcByteOffset be O.[[ByteOffset]].\n        let src_byte_offset = src_borrow.data().byte_offset();\n\n        let is_auto_length = src_borrow.data().is_auto_length();\n\n        drop(src_borrow);\n\n        // 8. Let relativeStart be ? ToIntegerOrInfinity(start).\n        // 9. If relativeStart = -∞, let startIndex be 0.\n        // 10. Else if relativeStart < 0, let startIndex be max(srcLength + relativeStart, 0).\n        // 11. Else, let startIndex be min(relativeStart, srcLength).\n        let start_index = Array::get_relative_start(context, args.get_or_undefined(0), src_len)?;\n\n        // 14. Let beginByteOffset be srcByteOffset + (startIndex × elementSize).\n        let begin_byte_offset = src_byte_offset + (start_index * element_size);\n\n        let end = args.get_or_undefined(1);\n\n        // 15. If O.[[ArrayLength]] is auto and end is undefined, then\n        if is_auto_length && end.is_undefined() {\n            // a. Let argumentsList be « buffer, 𝔽(beginByteOffset) ».\n\n            // 17. Return ? TypedArraySpeciesCreate(O, argumentsList).\n            Ok(Self::species_create(\n                &src.upcast(),\n                kind,\n                &[buffer.into(), begin_byte_offset.into()],\n                context,\n            )?\n            .upcast()\n            .into())\n        } else {\n            // 16. Else,\n            //     a. If end is undefined, let relativeEnd be srcLength; else let relativeEnd be ? ToIntegerOrInfinity(end).\n            //     b. If relativeEnd = -∞, let endIndex be 0.\n            //     c. Else if relativeEnd < 0, let endIndex be max(srcLength + relativeEnd, 0).\n            //     d. Else, let endIndex be min(relativeEnd, srcLength).\n            let end_index = Array::get_relative_end(context, end, src_len)?;\n\n            //     e. Let newLength be max(endIndex - startIndex, 0).\n            let new_len = end_index.saturating_sub(start_index);\n\n            //     f. Let argumentsList be « buffer, 𝔽(beginByteOffset), 𝔽(newLength) ».\n            Ok(Self::species_create(\n                &src.upcast(),\n                kind,\n                &[buffer.into(), begin_byte_offset.into(), new_len.into()],\n                context,\n            )?\n            .upcast()\n            .into())\n        }\n    }\n\n    /// `%TypedArray%.prototype.toLocaleString ( [ reserved1 [ , reserved2 ] ] )`\n    /// `Array.prototype.toLocaleString ( [ locales [ , options ] ] )`\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///  - [ECMA-402 reference][spec-402]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-%typedarray%.prototype.tolocalestring\n    /// [spec-402]: https://402.ecma-international.org/10.0/#sup-array.prototype.tolocalestring\n    pub(crate) fn to_locale_string(\n        this: &JsValue,\n        args: &[JsValue],\n        context: &mut Context,\n    ) -> JsResult<JsValue> {\n        // This is a distinct method that implements the same algorithm as Array.prototype.toLocaleString as defined in\n        // 23.1.3.32 except that TypedArrayLength is called in place of performing a [[Get]] of \"length\".\n\n        let array = this.as_object().ok_or_else(|| {\n            JsNativeError::typ().with_message(\"Value is not a typed array object\")\n        })?;\n\n        let (len, is_fixed_len) = {\n            let o = array.downcast_ref::<TypedArray>().ok_or_else(|| {\n                JsNativeError::typ().with_message(\"Value is not a typed array object\")\n            })?;\n            let buf = o.viewed_array_buffer().as_buffer();\n            let Some((buf_len, is_fixed_len)) = buf\n                .bytes(Ordering::SeqCst)\n                .filter(|s| !o.is_out_of_bounds(s.len()))\n                .map(|s| (s.len(), buf.is_fixed_len()))\n            else {\n                return Err(JsNativeError::typ()\n                    .with_message(\"typed array is outside the bounds of its inner buffer\")\n                    .into());\n            };\n\n            (o.array_length(buf_len), is_fixed_len)\n        };\n\n        let locales = args.get_or_undefined(0);\n        let options = args.get_or_undefined(1);\n\n        let separator = {\n            #[cfg(feature = \"intl\")]\n            {\n                use crate::builtins::intl::locale::default_locale;\n                use icu_list::{\n                    ListFormatter, ListFormatterPreferences, options::ListFormatterOptions,\n                };\n\n                let locale = default_locale(context.intl_provider().locale_canonicalizer()?);\n                let preferences = ListFormatterPreferences::from(&locale);\n                let formatter = ListFormatter::try_new_unit_with_buffer_provider(\n                    context.intl_provider().erased_provider(),\n                    preferences,\n                    ListFormatterOptions::default(),\n                )\n                .map_err(|e| JsNativeError::typ().with_message(e.to_string()))?;\n\n                // Ask ICU for the list pattern literal by formatting two empty elements.\n                // For many locales this yields \", \", but it may differ.\n                js_string!(\n                    formatter.format_to_string(std::iter::once(\"\").chain(std::iter::once(\"\")))\n                )\n            }\n\n            #[cfg(not(feature = \"intl\"))]\n            {\n                js_string!(\", \")\n            }\n        };\n\n        let mut r = Vec::with_capacity((len + len.saturating_sub(1)) as usize);\n\n        for k in 0..len {\n            if k > 0 {\n                r.extend(separator.iter());\n            }\n\n            let next_element = array.get(k, context)?;\n\n            // Mirrors the behaviour of `join`, but the compiler\n            // could unswitch the loop using `is_fixed_len`.\n            if is_fixed_len || !next_element.is_undefined() {\n                let s = next_element\n                    .invoke(\n                        js_string!(\"toLocaleString\"),\n                        &[locales.clone(), options.clone()],\n                        context,\n                    )?\n                    .to_string(context)?;\n\n                r.extend(s.iter());\n            }\n        }\n\n        Ok(js_string!(&r[..]).into())\n    }\n\n    /// `%TypedArray%.prototype.values ( )`\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-%typedarray%.prototype.values\n    pub(crate) fn values(\n        this: &JsValue,\n        _: &[JsValue],\n        context: &mut Context,\n    ) -> JsResult<JsValue> {\n        // 1. Let O be the this value.\n        // 2. Perform ? ValidateTypedArray(O, seq-cst).\n        let (ta, _) = TypedArray::validate(this, Ordering::SeqCst)?;\n\n        // 3. Return CreateArrayIterator(O, value).\n        Ok(ArrayIterator::create_array_iterator(\n            ta.upcast(),\n            PropertyNameKind::Value,\n            context,\n        ))\n    }\n\n    /// [`%TypedArray%.prototype.with ( index, value )`][spec]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-%typedarray%.prototype.with\n    pub(crate) fn with(\n        this: &JsValue,\n        args: &[JsValue],\n        context: &mut Context,\n    ) -> JsResult<JsValue> {\n        // 1. Let O be the this value.\n        // 2. Let taRecord be ? ValidateTypedArray(O, seq-cst).\n        let (ta, buf_len) = TypedArray::validate(this, Ordering::SeqCst)?;\n\n        // 3. Let len be TypedArrayLength(taRecord).\n        let len = ta.borrow().data().array_length(buf_len);\n        let kind = ta.borrow().data().kind();\n\n        // 4. Let relativeIndex be ? ToIntegerOrInfinity(index).\n        // triggers any conversion errors before throwing range errors.\n        let relative_index = args.get_or_undefined(0).to_integer_or_infinity(context)?;\n\n        let value = args.get_or_undefined(1);\n\n        // 7. If O.[[ContentType]] is bigint, let numericValue be ? ToBigInt(value).\n        let numeric_value: JsValue = if kind.content_type() == ContentType::BigInt {\n            value.to_bigint(context)?.into()\n        } else {\n            // 8. Else, let numericValue be ? ToNumber(value).\n            value.to_number(context)?.into()\n        };\n\n        // 9. If IsValidIntegerIndex(O, 𝔽(actualIndex)) is false, throw a RangeError exception.\n        let IntegerOrInfinity::Integer(relative_index) = relative_index else {\n            return Err(JsNativeError::range()\n                .with_message(\"invalid integer index for TypedArray operation\")\n                .into());\n        };\n        let actual_index = (|| {\n            let rel = u64::try_from(relative_index)\n                .ok()\n                .or_else(|| len.checked_add_signed(relative_index))?;\n\n            let inner = ta.borrow();\n            let buf = inner.data().viewed_array_buffer().as_buffer();\n            let s = buf.bytes(Ordering::Relaxed)?;\n            inner.data().validate_index_u64(rel, s.len())\n        })()\n        .ok_or_else(|| {\n            JsNativeError::range().with_message(\"invalid integer index for TypedArray operation\")\n        })?;\n\n        // 10. Let A be ? TypedArrayCreateSameType(O, « 𝔽(len) »).\n        let new_array = Self::from_kind_and_length(kind, len, context)?;\n\n        // 11. Let k be 0.\n        // 12. Repeat, while k < len,\n        let ta = ta.upcast();\n        for k in 0..len {\n            // a. Let Pk be ! ToString(𝔽(k)).\n            let value = if k == actual_index {\n                // b. If k is actualIndex, let fromValue be numericValue.\n                numeric_value.clone()\n            } else {\n                // c. Else, let fromValue be ! Get(O, Pk).\n                ta.get(k, context).js_expect(\"cannot fail per the spec\")?\n            };\n            // d. Perform ! Set(A, Pk, fromValue, true).\n            new_array\n                .set(k, value, true, context)\n                .js_expect(\"cannot fail per the spec\")?;\n\n            // e. Set k to k + 1.\n        }\n\n        // 13. Return A.\n        Ok(new_array.into())\n    }\n\n    /// `get %TypedArray%.prototype [ @@toStringTag ]`\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-get-%typedarray%.prototype-@@tostringtag\n    #[allow(clippy::unnecessary_wraps)]\n    pub(crate) fn to_string_tag(\n        this: &JsValue,\n        _: &[JsValue],\n        _: &mut Context,\n    ) -> JsResult<JsValue> {\n        // 1. Let O be the this value.\n        // 2. If Type(O) is not Object, return undefined.\n        // 3. If O does not have a [[TypedArrayName]] internal slot, return undefined.\n        // 4. Let name be O.[[TypedArrayName]].\n        // 5. Assert: Type(name) is String.\n        // 6. Return name.\n        Ok(this\n            .as_object()\n            .and_then(|obj| {\n                obj.downcast_ref::<TypedArray>()\n                    .map(|o| o.kind().js_name().into())\n            })\n            .unwrap_or_default())\n    }\n\n    /// `TypedArraySpeciesCreate ( exemplar, argumentList )`\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#typedarray-species-create\n    fn species_create(\n        exemplar: &JsObject,\n        kind: TypedArrayKind,\n        args: &[JsValue],\n        context: &mut Context,\n    ) -> JsResult<JsObject<TypedArray>> {\n        // 1. Let defaultConstructor be the intrinsic object listed in column one of Table 73 for exemplar.[[TypedArrayName]].\n        let default_constructor = kind.standard_constructor();\n\n        // 2. Let constructor be ? SpeciesConstructor(exemplar, defaultConstructor).\n        let constructor = exemplar.species_constructor(default_constructor, context)?;\n\n        // 3. Let result be ? TypedArrayCreate(constructor, argumentList).\n        let result = Self::create(&constructor, args, context)?;\n\n        // 4. Assert: result has [[TypedArrayName]] and [[ContentType]] internal slots.\n        // 5. If result.[[ContentType]] ≠ exemplar.[[ContentType]], throw a TypeError exception.\n        if result.borrow().data().kind().content_type() != kind.content_type() {\n            return Err(JsNativeError::typ()\n                .with_message(\"New typed array has different context type than exemplar\")\n                .into());\n        }\n\n        // 6. Return result.\n        Ok(result)\n    }\n\n    /// [`TypedArrayCreateFromConstructor ( constructor, argumentList )`][spec].\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-typedarraycreatefromconstructor\n    fn create(\n        constructor: &JsObject,\n        args: &[JsValue],\n        context: &mut Context,\n    ) -> JsResult<JsObject<TypedArray>> {\n        // 1. Let newTypedArray be ? Construct(constructor, argumentList).\n        let new_typed_array = constructor.construct(args, Some(constructor), context)?;\n\n        // 2. Let taRecord be ? ValidateTypedArray(newTypedArray, seq-cst).\n        let (new_ta, buf_len) =\n            TypedArray::validate(&JsValue::new(new_typed_array), Ordering::SeqCst)?;\n\n        // 3. If the number of elements in argumentList is 1 and argumentList[0] is a Number, then\n        if args.len() == 1\n            && let Some(number) = args[0].as_number()\n        {\n            let new_ta = new_ta.borrow();\n            // a. If IsTypedArrayOutOfBounds(taRecord) is true, throw a TypeError exception.\n            if new_ta.data().is_out_of_bounds(buf_len) {\n                return Err(JsNativeError::typ()\n                    .with_message(\"new typed array outside of the bounds of its inner buffer\")\n                    .into());\n            }\n\n            // b. Let length be TypedArrayLength(taRecord).\n            // c. If length < ℝ(argumentList[0]), throw a TypeError exception.\n            if (new_ta.data().array_length(buf_len) as f64) < number {\n                return Err(JsNativeError::typ()\n                    .with_message(\"new typed array length is smaller than expected\")\n                    .into());\n            }\n        }\n\n        // 4. Return newTypedArray.\n        Ok(new_ta)\n    }\n\n    /// [`AllocateTypedArrayBuffer ( O, length )`][spec]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-allocatetypedarraybuffer\n    fn allocate_buffer<T: TypedArrayMarker>(\n        length: u64,\n        context: &mut Context,\n    ) -> JsResult<TypedArray> {\n        // 1. Assert: O.[[ViewedArrayBuffer]] is undefined.\n\n        // 2. Let constructorName be the String value of O.[[TypedArrayName]].\n        // 3. Let elementSize be the Element Size value specified in Table 73 for constructorName.\n        let element_size = T::ERASED.element_size();\n\n        // 4. Let byteLength be elementSize × length.\n        let byte_length = element_size * length;\n\n        // 5. Let data be ? AllocateArrayBuffer(%ArrayBuffer%, byteLength).\n        let data = ArrayBuffer::allocate(\n            &context\n                .intrinsics()\n                .constructors()\n                .array_buffer()\n                .constructor()\n                .into(),\n            byte_length,\n            None,\n            context,\n        )?;\n\n        // 10. Return O.\n        Ok(TypedArray::new(\n            // 6. Set O.[[ViewedArrayBuffer]] to data.\n            BufferObject::Buffer(data),\n            T::ERASED,\n            // 8. Set O.[[ByteOffset]] to 0.\n            0,\n            // 7. Set O.[[ByteLength]] to byteLength.\n            Some(byte_length),\n            // 9. Set O.[[ArrayLength]] to length.\n            Some(length),\n        ))\n    }\n\n    /// <https://tc39.es/ecma262/#sec-initializetypedarrayfromlist>\n    pub(crate) fn initialize_from_list<T: TypedArrayMarker>(\n        proto: JsObject,\n        values: Vec<JsValue>,\n        context: &mut Context,\n    ) -> JsResult<JsObject> {\n        // 1. Let len be the number of elements in values.\n        let len = values.len() as u64;\n        // 2. Perform ? AllocateTypedArrayBuffer(O, len).\n        let buf = Self::allocate_buffer::<T>(len, context)?;\n        let obj = JsObject::from_proto_and_data_with_shared_shape(context.root_shape(), proto, buf)\n            .upcast();\n\n        // 3. Let k be 0.\n        // 4. Repeat, while k < len,\n        for (k, k_value) in values.into_iter().enumerate() {\n            // a. Let Pk be ! ToString(𝔽(k)).\n            // b. Let kValue be the first element of values and remove that element from values.\n            // c. Perform ? Set(O, Pk, kValue, true).\n            obj.set(k, k_value, true, context)?;\n            // d. Set k to k + 1.\n        }\n\n        // 5. Assert: values is now an empty List.\n        // It no longer exists.\n        Ok(obj)\n    }\n\n    /// `AllocateTypedArray ( constructorName, newTarget, defaultProto [ , length ] )`\n    ///\n    /// It is used to validate and create an instance of a `TypedArray` constructor. If the `length`\n    /// argument is passed, an `ArrayBuffer` of that length is also allocated and associated with the\n    /// new `TypedArray` instance. `AllocateTypedArray` provides common semantics that is used by\n    /// `TypedArray`.\n    ///\n    /// For more information, check the [spec][spec].\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-allocatetypedarray\n    pub(super) fn allocate<T: TypedArrayMarker>(\n        new_target: &JsValue,\n        length: u64,\n        context: &mut Context,\n    ) -> JsResult<JsObject> {\n        // 1. Let proto be ? GetPrototypeFromConstructor(newTarget, defaultProto).\n        let proto = get_prototype_from_constructor(new_target, T::STANDARD_CONSTRUCTOR, context)?;\n\n        // 3. Assert: obj.[[ViewedArrayBuffer]] is undefined.\n        // 4. Set obj.[[TypedArrayName]] to constructorName.\n        // 5. If constructorName is \"BigInt64Array\" or \"BigUint64Array\", set obj.[[ContentType]] to BigInt.\n        // 6. Otherwise, set obj.[[ContentType]] to Number.\n        // 7. If length is not present, then\n        // a. Set obj.[[ByteLength]] to 0.\n        // b. Set obj.[[ByteOffset]] to 0.\n        // c. Set obj.[[ArrayLength]] to 0.\n\n        // 8. Else,\n        // a. Perform ? AllocateTypedArrayBuffer(obj, length).\n        let indexed = Self::allocate_buffer::<T>(length, context)?;\n\n        // 2. Let obj be ! IntegerIndexedObjectCreate(proto).\n        let obj =\n            JsObject::from_proto_and_data_with_shared_shape(context.root_shape(), proto, indexed)\n                .upcast();\n\n        // 9. Return obj.\n        Ok(obj)\n    }\n\n    /// `InitializeTypedArrayFromTypedArray ( O, srcArray )`\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-initializetypedarrayfromtypedarray\n    pub(super) fn initialize_from_typed_array<T: TypedArrayMarker>(\n        proto: JsObject,\n        src_array: &JsObject<TypedArray>,\n        context: &mut Context,\n    ) -> JsResult<JsObject> {\n        let src_array = src_array.borrow();\n        let src_array = src_array.data();\n\n        // 1. Let srcData be srcArray.[[ViewedArrayBuffer]].\n        let src_data = src_array.viewed_array_buffer();\n        let src_data = src_data.as_buffer();\n\n        // 2. Let elementType be TypedArrayElementType(O).\n        let element_type = T::ERASED;\n        // 3. Let elementSize be TypedArrayElementSize(O).\n        let element_size = element_type.element_size();\n\n        // 4. Let srcType be TypedArrayElementType(srcArray).\n        let src_type = src_array.kind();\n        // 5. Let srcElementSize be TypedArrayElementSize(srcArray).\n        let src_element_size = src_type.element_size();\n        // 6. Let srcByteOffset be srcArray.[[ByteOffset]].\n        let src_byte_offset = src_array.byte_offset();\n\n        // 7. Let srcRecord be MakeTypedArrayWithBufferWitnessRecord(srcArray, seq-cst).\n        // 8. If IsTypedArrayOutOfBounds(srcRecord) is true, throw a TypeError exception.\n        let Some(src_data) = src_data\n            .bytes(Ordering::SeqCst)\n            .filter(|buf| !src_array.is_out_of_bounds(buf.len()))\n        else {\n            return Err(JsNativeError::typ()\n                .with_message(\"Cannot initialize typed array from invalid buffer\")\n                .into());\n        };\n\n        // 9. Let elementLength be TypedArrayLength(srcRecord).\n        let element_length = src_array.array_length(src_data.len());\n        // 10. Let byteLength be elementSize × elementLength.\n        let byte_length = element_size * element_length;\n\n        // 11. If elementType is srcType, then\n\n        let new_buffer = if element_type == src_type {\n            let start = src_byte_offset as usize;\n            let count = byte_length as usize;\n            // a. Let data be ? CloneArrayBuffer(srcData, srcByteOffset, byteLength).\n            src_data.subslice(start..start + count).clone(context)?\n        } else {\n            // 12. Else,\n            //     a. Let data be ? AllocateArrayBuffer(%ArrayBuffer%, byteLength).\n            let data_obj = ArrayBuffer::allocate(\n                &context\n                    .realm()\n                    .intrinsics()\n                    .constructors()\n                    .array_buffer()\n                    .constructor()\n                    .into(),\n                byte_length,\n                None,\n                context,\n            )?;\n            {\n                let mut data = data_obj.borrow_mut();\n                let mut data = SliceRefMut::Slice(\n                    data.data_mut()\n                        .bytes_mut()\n                        .js_expect(\"a new buffer cannot be detached\")?,\n                );\n\n                // b. If srcArray.[[ContentType]] is not O.[[ContentType]], throw a TypeError exception.\n                if src_type.content_type() != element_type.content_type() {\n                    return Err(JsNativeError::typ()\n                        .with_message(\"Cannot initialize typed array from different content type\")\n                        .into());\n                }\n\n                let src_element_size = src_element_size as usize;\n                let target_element_size = element_size as usize;\n\n                // c. Let srcByteIndex be srcByteOffset.\n                let mut src_byte_index = src_byte_offset as usize;\n\n                // d. Let targetByteIndex be 0.\n                let mut target_byte_index = 0;\n\n                // e. Let count be elementLength.\n                let mut count = element_length;\n\n                // f. Repeat, while count > 0,\n                while count > 0 {\n                    // i. Let value be GetValueFromBuffer(srcData, srcByteIndex, srcType, true, unordered).\n                    // SAFETY: All integer indexed objects are always in-bounds and properly\n                    // aligned to their underlying buffer.\n                    let value = unsafe {\n                        src_data\n                            .subslice(src_byte_index..)\n                            .get_value(src_type, Ordering::Relaxed)\n                    };\n\n                    let value = value.cast(element_type);\n\n                    // ii. Perform SetValueInBuffer(data, targetByteIndex, elementType, value, true, unordered).\n                    // SAFETY: The newly created buffer has at least `element_size * element_length`\n                    // bytes available, which makes `target_byte_index` always in-bounds.\n                    unsafe {\n                        data.subslice_mut(target_byte_index..)\n                            .set_value(value, Ordering::Relaxed);\n                    }\n\n                    // iii. Set srcByteIndex to srcByteIndex + srcElementSize.\n                    src_byte_index += src_element_size;\n\n                    // iv. Set targetByteIndex to targetByteIndex + elementSize.\n                    target_byte_index += target_element_size;\n\n                    // v. Set count to count - 1.\n                    count -= 1;\n                }\n            }\n\n            data_obj\n        };\n\n        // 13. Set O.[[ViewedArrayBuffer]] to data.\n        // 14. Set O.[[ByteLength]] to byteLength.\n        // 15. Set O.[[ByteOffset]] to 0.\n        // 16. Set O.[[ArrayLength]] to elementLength.\n        let obj = JsObject::from_proto_and_data_with_shared_shape(\n            context.root_shape(),\n            proto,\n            TypedArray::new(\n                BufferObject::Buffer(new_buffer),\n                element_type,\n                0,\n                Some(byte_length),\n                Some(element_length),\n            ),\n        )\n        .upcast();\n\n        // 17. Return unused.\n        Ok(obj)\n    }\n\n    /// [`InitializeTypedArrayFromArrayBuffer ( O, buffer, byteOffset, length )`][spec].\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-initializetypedarrayfromarraybuffer\n    pub(super) fn initialize_from_array_buffer<T: TypedArrayMarker>(\n        proto: JsObject,\n        buffer: BufferObject,\n        byte_offset: &JsValue,\n        length: &JsValue,\n        context: &mut Context,\n    ) -> JsResult<JsObject> {\n        // 1. Let elementSize be TypedArrayElementSize(O).\n        let element_size = T::ERASED.element_size();\n\n        // 2. Let offset be ? ToIndex(byteOffset).\n        let offset = byte_offset.to_index(context)?;\n\n        // 3. If offset modulo elementSize ≠ 0, throw a RangeError exception.\n        if offset % element_size != 0 {\n            return Err(JsNativeError::range()\n                .with_message(\"byte offset of typed array must be aligned\")\n                .into());\n        }\n\n        // 4. Let bufferIsFixedLength be IsFixedLengthArrayBuffer(buffer).\n        let is_fixed_length = buffer.as_buffer().is_fixed_len();\n\n        // 5. If length is not undefined, then\n        let new_length = if length.is_undefined() {\n            None\n        } else {\n            // a. Let newLength be ? ToIndex(length).\n            Some(length.to_index(context)?)\n        };\n\n        let buffer_byte_length = {\n            let buffer = buffer.as_buffer();\n\n            // 6. If IsDetachedBuffer(buffer) is true, throw a TypeError exception.\n            let Some(data) = buffer.bytes(Ordering::SeqCst) else {\n                return Err(JsNativeError::typ()\n                    .with_message(\"cannot construct typed array from detached buffer\")\n                    .into());\n            };\n\n            // 7. Let bufferByteLength be ArrayBufferByteLength(buffer, seq-cst).\n            data.len() as u64\n        };\n\n        let (byte_length, array_length) = if let Some(new_length) = new_length {\n            // 9. Else,\n            //    b. Else,\n            //       i. Let newByteLength be newLength × elementSize.\n            let new_byte_length = new_length * element_size;\n\n            //       ii. If offset + newByteLength > bufferByteLength, throw a RangeError exception.\n            if offset + new_byte_length > buffer_byte_length {\n                return Err(JsNativeError::range()\n                    .with_message(\n                        \"cannot create a typed array spanning a byte range outside of its buffer\",\n                    )\n                    .into());\n            }\n\n            //    c. Set O.[[ByteLength]] to newByteLength.\n            //    d. Set O.[[ArrayLength]] to newByteLength / elementSize.\n            (Some(new_byte_length), Some(new_length))\n        } else if !is_fixed_length {\n            // 8. If length is undefined and bufferIsFixedLength is false, then\n            //    a. If offset > bufferByteLength, throw a RangeError exception.\n            if offset > buffer_byte_length {\n                return Err(JsNativeError::range()\n                    .with_message(\"TypedArray offset outside of buffer length\")\n                    .into());\n            }\n\n            //    b. Set O.[[ByteLength]] to auto.\n            //    c. Set O.[[ArrayLength]] to auto.\n            (None, None)\n        } else {\n            // a. If length is undefined, then\n            //    i. If bufferByteLength modulo elementSize ≠ 0, throw a RangeError exception.\n            if buffer_byte_length % element_size != 0 {\n                return Err(JsNativeError::range()\n                    .with_message(\"cannot construct a typed array with an unaligned buffer\")\n                    .into());\n            }\n\n            //    ii. Let newByteLength be bufferByteLength - offset.\n            //    iii. If newByteLength < 0, throw a RangeError exception.\n            let Some(new_byte_length) = buffer_byte_length.checked_sub(offset) else {\n                return Err(JsNativeError::range()\n                    .with_message(\"offset of typed array exceeds buffer size\")\n                    .into());\n            };\n\n            // c. Set O.[[ByteLength]] to newByteLength.\n            // d. Set O.[[ArrayLength]] to newByteLength / elementSize.\n            (Some(new_byte_length), Some(new_byte_length / element_size))\n        };\n\n        // 10. Set O.[[ViewedArrayBuffer]] to buffer.\n        // 11. Set O.[[ByteOffset]] to offset.\n        // 12. Return unused.\n        Ok(JsObject::from_proto_and_data_with_shared_shape(\n            context.root_shape(),\n            proto,\n            TypedArray::new(buffer, T::ERASED, offset, byte_length, array_length),\n        )\n        .upcast())\n    }\n\n    /// `InitializeTypedArrayFromArrayLike ( O, arrayLike )`\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-initializetypedarrayfromarraylike\n    pub(super) fn initialize_from_array_like<T: TypedArrayMarker>(\n        proto: JsObject,\n        array_like: &JsObject,\n        context: &mut Context,\n    ) -> JsResult<JsObject> {\n        // 1. Let len be ? LengthOfArrayLike(arrayLike).\n        let len = array_like.length_of_array_like(context)?;\n\n        // 2. Perform ? AllocateTypedArrayBuffer(O, len).\n        let buf = Self::allocate_buffer::<T>(len, context)?;\n        let obj = JsObject::from_proto_and_data_with_shared_shape(context.root_shape(), proto, buf)\n            .upcast();\n\n        // 3. Let k be 0.\n        // 4. Repeat, while k < len,\n        for k in 0..len {\n            // a. Let Pk be ! ToString(𝔽(k)).\n            // b. Let kValue be ? Get(arrayLike, Pk).\n            let k_value = array_like.get(k, context)?;\n\n            // c. Perform ? Set(O, Pk, kValue, true).\n            obj.set(k, k_value, true, context)?;\n        }\n\n        Ok(obj)\n    }\n}\n\n#[derive(Debug)]\nenum U64OrPositiveInfinity {\n    U64(u64),\n    PositiveInfinity,\n}\n\n/// `CompareTypedArrayElements ( x, y, comparefn )`\n///\n/// More information:\n///  - [ECMAScript reference][spec]\n///\n/// [spec]: https://tc39.es/ecma262/#sec-comparetypedarrayelements\nfn compare_typed_array_elements(\n    x: &JsValue,\n    y: &JsValue,\n    compare_fn: Option<&JsObject>,\n    context: &mut Context,\n) -> JsResult<cmp::Ordering> {\n    // 1. Assert: x is a Number and y is a Number, or x is a BigInt and y is a BigInt.\n\n    // 2. If comparefn is not undefined, then\n    if let Some(compare_fn) = compare_fn {\n        // a. Let v be ? ToNumber(? Call(comparefn, undefined, « x, y »)).\n        let v = compare_fn\n            .call(&JsValue::undefined(), &[x.clone(), y.clone()], context)?\n            .to_number(context)?;\n\n        // b. If v is NaN, return +0𝔽.\n        if v.is_nan() {\n            return Ok(cmp::Ordering::Equal);\n        }\n\n        // c. Return v.\n        if v.is_sign_positive() {\n            return Ok(cmp::Ordering::Greater);\n        }\n        return Ok(cmp::Ordering::Less);\n    }\n\n    match (x.variant(), y.variant()) {\n        (JsVariant::BigInt(x), JsVariant::BigInt(y)) => {\n            // Note: Other steps are not relevant for BigInts.\n            // 6. If x < y, return -1𝔽.\n            // 7. If x > y, return 1𝔽.\n            // 10. Return +0𝔽.\n            Ok(x.cmp(&y))\n        }\n        (JsVariant::Integer32(x), JsVariant::Integer32(y)) => {\n            // Note: Other steps are not relevant for integers.\n            // 6. If x < y, return -1𝔽.\n            // 7. If x > y, return 1𝔽.\n            // 10. Return +0𝔽.\n            Ok(x.cmp(&y))\n        }\n        (JsVariant::Float64(x), JsVariant::Float64(y)) => {\n            // 3. If x and y are both NaN, return +0𝔽.\n            if x.is_nan() && y.is_nan() {\n                return Ok(cmp::Ordering::Equal);\n            }\n\n            // 4. If x is NaN, return 1𝔽.\n            if x.is_nan() {\n                return Ok(cmp::Ordering::Greater);\n            }\n\n            // 5. If y is NaN, return -1𝔽.\n            if y.is_nan() {\n                return Ok(cmp::Ordering::Less);\n            }\n\n            // 6. If x < y, return -1𝔽.\n            if x < y {\n                return Ok(cmp::Ordering::Less);\n            }\n\n            // 7. If x > y, return 1𝔽.\n            if x > y {\n                return Ok(cmp::Ordering::Greater);\n            }\n\n            // 8. If x is -0𝔽 and y is +0𝔽, return -1𝔽.\n            if x.is_sign_negative() && x.is_zero() && y.is_sign_positive() && y.is_zero() {\n                return Ok(cmp::Ordering::Less);\n            }\n\n            // 9. If x is +0𝔽 and y is -0𝔽, return 1𝔽.\n            if x.is_sign_positive() && x.is_zero() && y.is_sign_negative() && y.is_zero() {\n                return Ok(cmp::Ordering::Greater);\n            }\n\n            // 10. Return +0𝔽.\n            Ok(cmp::Ordering::Equal)\n        }\n        _ => unreachable!(\"x and y must be both Numbers or BigInts\"),\n    }\n}\n\n/// Abstract operation `IsValidIntegerIndex ( O, index )`.\n///\n/// Returns `true` if the index is valid, or `false` otherwise.\n///\n/// More information:\n///  - [ECMAScript reference][spec]\n///\n/// [spec]: https://tc39.es/ecma262/#sec-isvalidintegerindex\npub(crate) fn is_valid_integer_index(obj: &JsObject, index: f64) -> JsResult<bool> {\n    let inner = obj.downcast_ref::<TypedArray>().js_expect(\n        \"integer indexed exotic method should only be callable from TypedArray objects\",\n    )?;\n\n    let buf = inner.viewed_array_buffer();\n    let buf = buf.as_buffer();\n\n    // 1. If IsDetachedBuffer(O.[[ViewedArrayBuffer]]) is true, return false.\n    // 4. Let taRecord be MakeTypedArrayWithBufferWitnessRecord(O, unordered).\n    // 5. NOTE: Bounds checking is not a synchronizing operation when O's backing buffer is a growable SharedArrayBuffer.\n    let Some(buf_len) = buf.bytes(Ordering::Relaxed).map(|s| s.len()) else {\n        return Ok(false);\n    };\n\n    Ok(inner.validate_index(index, buf_len).is_some())\n}\n"
  },
  {
    "path": "core/engine/src/builtins/typed_array/element/atomic.rs",
    "content": "use std::{convert::identity, sync::atomic::Ordering};\n\nuse portable_atomic::{\n    AtomicI8, AtomicI16, AtomicI32, AtomicI64, AtomicU8, AtomicU16, AtomicU32, AtomicU64,\n};\n\n/// An atomic type that supports atomic operations.\npub(crate) trait Atomic {\n    /// The \"plain\" type of the atomic e.g. `AtomicU8::Plain == u8`\n    type Plain;\n\n    /// Loads the value of this atomic.\n    fn load(&self, order: Ordering) -> Self::Plain;\n\n    /// Stores `value` on this atomic.\n    fn store(&self, val: Self::Plain, order: Ordering);\n\n    /// Computes the `+` operation between `self` and `value`, storing the result\n    /// on `self` and returning the old value. This operation wraps on overflow.\n    fn add(&self, val: Self::Plain, order: Ordering) -> Self::Plain;\n\n    /// Computes the `&` operation between `self` and `value`, storing the result\n    /// on `self` and returning the old value.\n    fn bit_and(&self, val: Self::Plain, order: Ordering) -> Self::Plain;\n\n    /// Compares the current value of `self` with `expected`, storing `replacement`\n    /// if they're equal and returning its old value in all cases.\n    fn compare_exchange(\n        &self,\n        expected: Self::Plain,\n        replacement: Self::Plain,\n        order: Ordering,\n    ) -> Self::Plain;\n\n    /// Swaps `self` with `value`, returning the old value of `self`.\n    fn swap(&self, val: Self::Plain, order: Ordering) -> Self::Plain;\n\n    /// Computes the `|` operation between `self` and `value`, storing the result\n    /// on `self` and returning the old value.\n    fn bit_or(&self, val: Self::Plain, order: Ordering) -> Self::Plain;\n\n    /// Computes the `-` operation between `self` and `value`, storing the result\n    /// on `self` and returning the old value. This operation wraps on overflow.\n    fn sub(&self, val: Self::Plain, order: Ordering) -> Self::Plain;\n\n    /// Computes the `^` operation between `self` and `value`, storing the result\n    /// on `self` and returning the old value.\n    fn bit_xor(&self, val: Self::Plain, order: Ordering) -> Self::Plain;\n\n    /// Checks if this atomic does not use any locks to support atomic operations.\n    fn is_lock_free() -> bool;\n}\n\nmacro_rules! atomic {\n    ( $atomic:ty, $plain:ty ) => {\n        impl Atomic for $atomic {\n            type Plain = $plain;\n\n            fn load(&self, order: Ordering) -> Self::Plain {\n                <$atomic>::load(self, order)\n            }\n\n            fn store(&self, val: Self::Plain, order: Ordering) {\n                <$atomic>::store(self, val, order);\n            }\n\n            fn add(&self, val: Self::Plain, order: Ordering) -> Self::Plain {\n                <$atomic>::fetch_add(self, val, order)\n            }\n\n            fn bit_and(&self, val: Self::Plain, order: Ordering) -> Self::Plain {\n                <$atomic>::fetch_and(self, val, order)\n            }\n\n            fn compare_exchange(\n                &self,\n                expected: Self::Plain,\n                replacement: Self::Plain,\n                order: Ordering,\n            ) -> Self::Plain {\n                <$atomic>::compare_exchange(self, expected, replacement, order, order)\n                    .map_or_else(identity, identity)\n            }\n\n            fn swap(&self, val: Self::Plain, order: Ordering) -> Self::Plain {\n                <$atomic>::swap(self, val, order)\n            }\n\n            fn bit_or(&self, val: Self::Plain, order: Ordering) -> Self::Plain {\n                <$atomic>::fetch_or(self, val, order)\n            }\n\n            fn sub(&self, val: Self::Plain, order: Ordering) -> Self::Plain {\n                <$atomic>::fetch_sub(self, val, order)\n            }\n\n            fn bit_xor(&self, val: Self::Plain, order: Ordering) -> Self::Plain {\n                <$atomic>::fetch_xor(self, val, order)\n            }\n\n            fn is_lock_free() -> bool {\n                <$atomic>::is_lock_free()\n            }\n        }\n    };\n}\n\natomic!(AtomicU8, u8);\natomic!(AtomicI8, i8);\natomic!(AtomicU16, u16);\natomic!(AtomicI16, i16);\natomic!(AtomicU32, u32);\natomic!(AtomicI32, i32);\natomic!(AtomicU64, u64);\natomic!(AtomicI64, i64);\n"
  },
  {
    "path": "core/engine/src/builtins/typed_array/element/mod.rs",
    "content": "#![deny(unsafe_op_in_unsafe_fn)]\n#![allow(clippy::cast_ptr_alignment)] // Invariants are checked by the caller.\n\nmod atomic;\n\npub(crate) use self::atomic::Atomic;\n\nuse std::ops::{BitOr, BitXor};\nuse std::sync::atomic::Ordering;\nuse std::{convert::identity, ops::BitAnd};\n\nuse bytemuck::{AnyBitPattern, NoUninit};\nuse num_traits::{WrappingAdd, WrappingSub};\nuse portable_atomic::{\n    AtomicI8, AtomicI16, AtomicI32, AtomicI64, AtomicU8, AtomicU16, AtomicU32, AtomicU64,\n};\n\nuse crate::{\n    Context, JsResult, JsValue,\n    builtins::{\n        array_buffer::utils::{SliceRef, SliceRefMut},\n        typed_array::TypedArrayElement,\n    },\n    value::Numeric,\n};\n\n/// A reference to an element inside an array buffer.\n#[derive(Debug, Copy, Clone)]\npub(crate) enum ElementRef<'a, E: Element> {\n    Atomic(&'a E::Atomic),\n    Plain(&'a E),\n}\n\nimpl<E: Element> ElementRef<'_, E> {\n    /// Loads the value of this reference.\n    pub(crate) fn load(&self, order: Ordering) -> E {\n        match self {\n            ElementRef::Atomic(num) => E::from_plain(num.load(order)),\n            ElementRef::Plain(num) => **num,\n        }\n    }\n}\n\n/// A mutable reference to an element inside an array buffer.\npub(crate) enum ElementRefMut<'a, E: Element> {\n    Atomic(&'a E::Atomic),\n    Plain(&'a mut E),\n}\n\nimpl<E: Element> ElementRefMut<'_, E> {\n    /// Stores `value` on this mutable reference.\n    pub(crate) fn store(&mut self, value: E, order: Ordering) {\n        match self {\n            ElementRefMut::Atomic(num) => num.store(value.to_plain(), order),\n            ElementRefMut::Plain(num) => **num = value,\n        }\n    }\n}\n\nimpl<E: Element> ElementRefMut<'_, E>\nwhere\n    E::Atomic: Atomic<Plain = E>,\n{\n    /// Computes the `+` operation between `self` and `value`, storing the result\n    /// on `self` and returning the old value. This operation wraps on overflow.\n    pub(crate) fn add(&mut self, value: E, order: Ordering) -> E\n    where\n        E: WrappingAdd,\n    {\n        match self {\n            ElementRefMut::Atomic(num) => num.add(value, order),\n            ElementRefMut::Plain(num) => {\n                let new = num.wrapping_add(&value);\n                std::mem::replace(num, new)\n            }\n        }\n    }\n\n    /// Computes the `&` operation between `self` and `value`, storing the result\n    /// on `self` and returning the old value.\n    pub(crate) fn bit_and(&mut self, value: E, order: Ordering) -> E\n    where\n        E: BitAnd<Output = E>,\n    {\n        match self {\n            ElementRefMut::Atomic(num) => num.bit_and(value, order),\n            ElementRefMut::Plain(num) => {\n                let new = **num & value;\n                std::mem::replace(num, new)\n            }\n        }\n    }\n\n    /// Compares the current value of `self` with `expected`, exchanging it with `replacement`\n    /// if they're equal and returning its old value in all cases.\n    pub(crate) fn compare_exchange(&mut self, expected: E, replacement: E, order: Ordering) -> E\n    where\n        E: Eq,\n    {\n        match self {\n            ElementRefMut::Atomic(num) => num.compare_exchange(expected, replacement, order),\n            ElementRefMut::Plain(num) => {\n                let old = **num;\n                if old == expected {\n                    **num = replacement;\n                }\n                old\n            }\n        }\n    }\n\n    /// Swaps `self` with `value`, returning the old value of `self`.\n    pub(crate) fn swap(&mut self, value: E, order: Ordering) -> E {\n        match self {\n            ElementRefMut::Atomic(num) => num.swap(value, order),\n            ElementRefMut::Plain(num) => std::mem::replace(num, value),\n        }\n    }\n\n    /// Computes the `|` operation between `self` and `value`, storing the result\n    /// on `self` and returning the old value.\n    pub(crate) fn bit_or(&mut self, value: E, order: Ordering) -> E\n    where\n        E: BitOr<Output = E>,\n    {\n        match self {\n            ElementRefMut::Atomic(num) => num.bit_or(value, order),\n            ElementRefMut::Plain(num) => {\n                let new = **num | value;\n                std::mem::replace(num, new)\n            }\n        }\n    }\n\n    /// Computes the `-` operation between `self` and `value`, storing the result\n    /// on `self` and returning the old value. This operation wraps on overflow.\n    pub(crate) fn sub(&mut self, value: E, order: Ordering) -> E\n    where\n        E: WrappingSub,\n    {\n        match self {\n            ElementRefMut::Atomic(num) => num.sub(value, order),\n            ElementRefMut::Plain(num) => {\n                let new = num.wrapping_sub(&value);\n                std::mem::replace(num, new)\n            }\n        }\n    }\n\n    /// Computes the `^` operation between `self` and `value`, storing the result\n    /// on `self` and returning the old value.\n    pub(crate) fn bit_xor(&mut self, value: E, order: Ordering) -> E\n    where\n        E: BitXor<Output = E>,\n    {\n        match self {\n            ElementRefMut::Atomic(num) => num.bit_xor(value, order),\n            ElementRefMut::Plain(num) => {\n                let new = **num ^ value;\n                std::mem::replace(num, new)\n            }\n        }\n    }\n}\n\n/// An `u8` that clamps instead of overflowing when converting from a `JsValue`.\n#[derive(Debug, Clone, Copy, Eq, PartialEq, Ord, PartialOrd, Hash, AnyBitPattern, NoUninit)]\n#[repr(transparent)]\npub(crate) struct ClampedU8(pub(crate) u8);\n\nimpl ClampedU8 {\n    /// Converts this `ClampedU8` to its big endian representation.\n    pub(crate) fn to_be(self) -> Self {\n        Self(self.0.to_be())\n    }\n\n    /// Converts this `ClampedU8` to its little endian representation.\n    pub(crate) fn to_le(self) -> Self {\n        Self(self.0.to_le())\n    }\n}\n\nimpl From<ClampedU8> for Numeric {\n    fn from(value: ClampedU8) -> Self {\n        Numeric::Number(value.0.into())\n    }\n}\n\n/// A 16-bit float implementing missing traits from the inner `f16`,\n/// used for [`Float16Array`][super::Float16Array].\n#[cfg(feature = \"float16\")]\n#[derive(Debug, Clone, Copy, PartialEq, PartialOrd)]\n#[repr(transparent)]\npub(crate) struct Float16(pub(crate) float16::f16);\n\n#[cfg(feature = \"float16\")]\nimpl From<Float16> for Numeric {\n    fn from(value: Float16) -> Self {\n        Numeric::Number(value.0.into())\n    }\n}\n\n#[cfg(feature = \"float16\")]\nimpl std::hash::Hash for Float16 {\n    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {\n        state.write(self.0.to_le_bytes().as_ref());\n    }\n}\n\n#[cfg(feature = \"float16\")]\nunsafe impl bytemuck::Zeroable for Float16 {}\n\n#[cfg(feature = \"float16\")]\nunsafe impl bytemuck::Pod for Float16 {}\n\n/// A native element that can be inside a `TypedArray`.\npub(crate) trait Element:\n    Sized + Into<TypedArrayElement> + NoUninit + AnyBitPattern\n{\n    /// The atomic type used for shared array buffers.\n    type Atomic: Atomic;\n\n    /// Converts a `JsValue` into the native element `Self`.\n    fn from_js_value(value: &JsValue, context: &mut Context) -> JsResult<Self>;\n\n    /// Converts from the plain type of an atomic to `Self`.\n    fn from_plain(bytes: <Self::Atomic as Atomic>::Plain) -> Self;\n\n    /// Converts from `Self` to the plain type of an atomic.\n    fn to_plain(self) -> <Self::Atomic as Atomic>::Plain;\n\n    /// Gets the little endian representation of `Self`.\n    fn to_little_endian(self) -> Self;\n\n    /// Gets the big endian representation of `Self`.\n    fn to_big_endian(self) -> Self;\n\n    /// Reads `Self` from the `buffer`.\n    ///\n    /// This will always read values in the native endianness of the target architecture.\n    ///\n    /// # Safety\n    ///\n    /// - `buffer` must be aligned to the native alignment of `Self`.\n    /// - `buffer` must contain enough bytes to read `std::sizeof::<Self>` bytes.\n    unsafe fn read(buffer: SliceRef<'_>) -> ElementRef<'_, Self>;\n\n    /// Writes the bytes of this element into `buffer`.\n    ///\n    /// This will always write values in the native endianness of the target architecture.\n    ///\n    /// # Safety\n    ///\n    /// - `buffer` must be aligned to the native alignment of `Self`.\n    /// - `buffer` must contain enough bytes to store `std::sizeof::<Self>` bytes.\n    unsafe fn read_mut(buffer: SliceRefMut<'_>) -> ElementRefMut<'_, Self>;\n}\n\nmacro_rules! element {\n    ( $element:ty, $atomic:ty, from_js: $from_js:path $(,)?) => {\n        element!(\n            $element,\n            $atomic,\n            from_js: $from_js,\n            from_plain: identity,\n            to_plain: identity,\n            to_be: |this: $element| this.to_be(),\n            to_le: |this: $element| this.to_le()\n        );\n    };\n    (\n        $element:ty,\n        $atomic:ty,\n        from_js: $from_js:expr,\n        from_plain: $from_plain:expr,\n        to_plain: $to_plain:expr,\n        to_be: $to_be:expr,\n        to_le: $to_le:expr $(,)?\n    ) => {\n        #[allow(clippy::redundant_closure_call)]\n        #[allow(clippy::undocumented_unsafe_blocks)] // Invariants are checked by the caller.\n        impl Element for $element {\n            type Atomic = $atomic;\n\n            fn from_js_value(value: &JsValue, context: &mut Context) -> JsResult<Self> {\n                $from_js(value, context)\n            }\n\n            fn from_plain(plain: <Self::Atomic as Atomic>::Plain) -> Self {\n                $from_plain(plain)\n            }\n\n            fn to_plain(self) -> <Self::Atomic as Atomic>::Plain {\n                $to_plain(self)\n            }\n\n            fn to_big_endian(self) -> Self {\n                $to_be(self)\n            }\n\n            fn to_little_endian(self) -> Self {\n                $to_le(self)\n            }\n\n            unsafe fn read(buffer: SliceRef<'_>) -> ElementRef<'_, Self> {\n                #[cfg(debug_assertions)]\n                {\n                    assert!(buffer.len() >= std::mem::size_of::<Self>());\n                    assert!(buffer.addr() % std::mem::align_of::<Self>() == 0);\n                }\n\n                match buffer {\n                    SliceRef::Slice(buffer) => unsafe {\n                        ElementRef::Plain(&*buffer.as_ptr().cast())\n                    },\n                    SliceRef::AtomicSlice(buffer) => unsafe {\n                        ElementRef::Atomic(&*buffer.as_ptr().cast::<Self::Atomic>())\n                    },\n                }\n            }\n\n            unsafe fn read_mut(buffer: SliceRefMut<'_>) -> ElementRefMut<'_, Self> {\n                #[cfg(debug_assertions)]\n                {\n                    assert!(buffer.len() >= std::mem::size_of::<Self>());\n                    assert!(buffer.addr() % std::mem::align_of::<Self>() == 0);\n                }\n\n                match buffer {\n                    SliceRefMut::Slice(buffer) => unsafe {\n                        ElementRefMut::Plain(&mut *buffer.as_mut_ptr().cast())\n                    },\n                    SliceRefMut::AtomicSlice(buffer) => unsafe {\n                        ElementRefMut::Atomic(&*buffer.as_ptr().cast::<Self::Atomic>())\n                    },\n                }\n            }\n        }\n    };\n}\n\nelement!(u8, AtomicU8, from_js: JsValue::to_uint8);\nelement!(i8, AtomicI8, from_js: JsValue::to_int8);\nelement!(u16, AtomicU16, from_js: JsValue::to_uint16);\nelement!(i16, AtomicI16, from_js: JsValue::to_int16);\nelement!(u32, AtomicU32, from_js: JsValue::to_u32);\nelement!(i32, AtomicI32, from_js: JsValue::to_i32);\nelement!(u64, AtomicU64, from_js: JsValue::to_big_uint64);\nelement!(i64, AtomicI64, from_js: JsValue::to_big_int64);\n\nelement!(\n    ClampedU8,\n    AtomicU8,\n    from_js: |value: &JsValue, context| value.to_uint8_clamp(context).map(ClampedU8),\n    from_plain: ClampedU8,\n    to_plain: |c: ClampedU8| c.0,\n    to_be: |this: ClampedU8| this.to_be(),\n    to_le: |this: ClampedU8| this.to_le(),\n);\n\n#[cfg(feature = \"float16\")]\nelement!(\n    Float16,\n    AtomicU16,\n    from_js: |value: &JsValue, context| value.to_f16(context).map(Float16),\n    from_plain: |a: u16| Float16(float16::f16::from_bits(a)),\n    to_plain: |f: Float16| f.0.to_bits(),\n    to_be: |this: Float16| Float16(float16::f16::from_bits(this.0.to_bits().to_be())),\n    to_le: |this: Float16| Float16(float16::f16::from_bits(this.0.to_bits().to_le())),\n);\n\nelement!(\n    f32,\n    AtomicU32,\n    from_js: |value: &JsValue, context| value.to_number(context).map(|f| f as f32),\n    from_plain: f32::from_bits,\n    to_plain: |f: f32| f.to_bits(),\n    to_be: |this: f32| f32::from_bits(this.to_bits().to_be()),\n    to_le: |this: f32| f32::from_bits(this.to_bits().to_le()),\n);\n\nelement!(\n    f64,\n    AtomicU64,\n    from_js: |value: &JsValue, context| value.to_number(context),\n    from_plain: f64::from_bits,\n    to_plain: |f: f64| f.to_bits(),\n    to_be: |this: f64| f64::from_bits(this.to_bits().to_be()),\n    to_le: |this: f64| f64::from_bits(this.to_bits().to_le()),\n);\n"
  },
  {
    "path": "core/engine/src/builtins/typed_array/mod.rs",
    "content": "//! Boa's implementation of ECMAScript's global `TypedArray` objects.\n//!\n//! A `TypedArray` object describes an array-like view of an underlying binary data buffer.\n//! There is no global property named `TypedArray`, nor is there a directly visible `TypedArray` constructor.\n//! Instead, there are a number of different global properties,\n//! whose values are typed array constructors for specific element types.\n//!\n//! More information:\n//!  - [ECMAScript reference][spec]\n//!  - [MDN documentation][mdn]\n//!\n//! [spec]: https://tc39.es/ecma262/#sec-typedarray-objects\n//! [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/TypedArray\n\nuse crate::{\n    Context, JsArgs, JsResult, JsString,\n    builtins::{BuiltInBuilder, BuiltInConstructor, BuiltInObject, IntrinsicObject},\n    context::intrinsics::{Intrinsics, StandardConstructor, StandardConstructors},\n    error::JsNativeError,\n    js_string,\n    object::{JsObject, internal_methods::get_prototype_from_constructor},\n    property::Attribute,\n    realm::Realm,\n    string::StaticJsStrings,\n    symbol::JsSymbol,\n    value::{JsValue, Numeric},\n};\nuse boa_gc::{Finalize, Trace};\n\nmod builtin;\nmod element;\nmod object;\n\npub(crate) use builtin::{BuiltinTypedArray, is_valid_integer_index};\n#[cfg(feature = \"float16\")]\npub(crate) use element::Float16;\npub(crate) use element::{Atomic, ClampedU8, Element};\npub use object::TypedArray;\n\npub(crate) trait TypedArrayMarker {\n    type Element: Element;\n    const ERASED: TypedArrayKind;\n}\n\nimpl<T: TypedArrayMarker> IntrinsicObject for T {\n    fn get(intrinsics: &Intrinsics) -> JsObject {\n        Self::STANDARD_CONSTRUCTOR(intrinsics.constructors()).constructor()\n    }\n\n    fn init(realm: &Realm) {\n        let get_species = BuiltInBuilder::callable(realm, BuiltinTypedArray::get_species)\n            .name(js_string!(\"get [Symbol.species]\"))\n            .build();\n\n        BuiltInBuilder::from_standard_constructor::<Self>(realm)\n            .prototype(\n                realm\n                    .intrinsics()\n                    .constructors()\n                    .typed_array()\n                    .constructor(),\n            )\n            .inherits(Some(\n                realm.intrinsics().constructors().typed_array().prototype(),\n            ))\n            .static_accessor(\n                JsSymbol::species(),\n                Some(get_species),\n                None,\n                Attribute::CONFIGURABLE,\n            )\n            .property(\n                js_string!(\"BYTES_PER_ELEMENT\"),\n                size_of::<T::Element>(),\n                Attribute::READONLY | Attribute::NON_ENUMERABLE | Attribute::PERMANENT,\n            )\n            .static_property(\n                js_string!(\"BYTES_PER_ELEMENT\"),\n                size_of::<T::Element>(),\n                Attribute::READONLY | Attribute::NON_ENUMERABLE | Attribute::PERMANENT,\n            )\n            .build();\n    }\n}\n\nimpl<T: TypedArrayMarker> BuiltInObject for T {\n    const NAME: JsString = <Self as TypedArrayMarker>::ERASED.js_name();\n    const ATTRIBUTE: Attribute = Attribute::WRITABLE\n        .union(Attribute::NON_ENUMERABLE)\n        .union(Attribute::CONFIGURABLE);\n}\n\nimpl<T: TypedArrayMarker> BuiltInConstructor for T {\n    const CONSTRUCTOR_ARGUMENTS: usize = 3;\n    const PROTOTYPE_STORAGE_SLOTS: usize = 1;\n    const CONSTRUCTOR_STORAGE_SLOTS: usize = 3;\n\n    const STANDARD_CONSTRUCTOR: fn(&StandardConstructors) -> &StandardConstructor =\n        <Self as TypedArrayMarker>::ERASED.standard_constructor();\n\n    /// `23.2.5.1 TypedArray ( ...args )`\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-typedarray\n    fn constructor(\n        new_target: &JsValue,\n        args: &[JsValue],\n        context: &mut Context,\n    ) -> JsResult<JsValue> {\n        // 1. If NewTarget is undefined, throw a TypeError exception.\n        if new_target.is_undefined() {\n            return Err(JsNativeError::typ()\n                .with_message(format!(\n                    \"new target was undefined when constructing an {}\",\n                    T::ERASED.name()\n                ))\n                .into());\n        }\n\n        // 2. Let constructorName be the String value of the Constructor Name value specified in Table 72 for this TypedArray constructor.\n        // 3. Let proto be \"%TypedArray.prototype%\".\n\n        // 4. Let numberOfArgs be the number of elements in args.\n        let number_of_args = args.len();\n\n        // 5. If numberOfArgs = 0, then\n        if number_of_args == 0 {\n            // a. Return ? AllocateTypedArray(constructorName, NewTarget, proto, 0).\n            return Ok(BuiltinTypedArray::allocate::<T>(new_target, 0, context)?.into());\n        }\n        // 6. Else,\n\n        // a. Let firstArgument be args[0].\n        let first_argument = &args[0];\n\n        // b. If Type(firstArgument) is Object, then\n        let Some(first_argument) = first_argument.as_object() else {\n            // c. Else,\n            // i. Assert: firstArgument is not an Object.\n            // Ensured by the let-else\n\n            // ii. Let elementLength be ? ToIndex(firstArgument).\n            let element_length = first_argument.to_index(context)?;\n\n            // iii. Return ? AllocateTypedArray(constructorName, NewTarget, proto, elementLength).\n            return BuiltinTypedArray::allocate::<T>(new_target, element_length, context)\n                .map(JsValue::from);\n        };\n\n        let first_argument = first_argument.clone();\n\n        // i. Let O be ? AllocateTypedArray(constructorName, NewTarget, proto).\n        let proto = get_prototype_from_constructor(new_target, T::STANDARD_CONSTRUCTOR, context)?;\n\n        // ii. If firstArgument has a [[TypedArrayName]] internal slot, then\n        let first_argument = match first_argument.downcast::<TypedArray>() {\n            Ok(arr) => {\n                // 1. Perform ? InitializeTypedArrayFromTypedArray(O, firstArgument).\n\n                // v. Return O.\n                return BuiltinTypedArray::initialize_from_typed_array::<T>(proto, &arr, context)\n                    .map(JsValue::from);\n            }\n            Err(obj) => obj,\n        };\n\n        // iii. Else if firstArgument has an [[ArrayBufferData]] internal slot, then\n        let first_argument = match first_argument.into_buffer_object() {\n            Ok(buf) => {\n                // 1. If numberOfArgs > 1, let byteOffset be args[1]; else let byteOffset be undefined.\n                let byte_offset = args.get_or_undefined(1);\n\n                // 2. If numberOfArgs > 2, let length be args[2]; else let length be undefined.\n                let length = args.get_or_undefined(2);\n\n                // 3. Perform ? InitializeTypedArrayFromArrayBuffer(O, firstArgument, byteOffset, length).\n\n                // v. Return O.\n                return BuiltinTypedArray::initialize_from_array_buffer::<T>(\n                    proto,\n                    buf,\n                    byte_offset,\n                    length,\n                    context,\n                )\n                .map(JsValue::from);\n            }\n            Err(obj) => obj,\n        };\n\n        // iv. Else,\n\n        // 1. Assert: Type(firstArgument) is Object and firstArgument does not have\n        // either a [[TypedArrayName]] or an [[ArrayBufferData]] internal slot.\n\n        // 2. Let usingIterator be ? GetMethod(firstArgument, @@iterator).\n        let using_iterator = first_argument.get_method(JsSymbol::iterator(), context)?;\n\n        // 3. If usingIterator is not undefined, then\n        if let Some(using_iterator) = using_iterator {\n            // a. Let values be ? IteratorToList(? GetIteratorFromMethod(firstArgument, usingIterator)).\n            let values = JsValue::from(first_argument.clone())\n                .get_iterator_from_method(&using_iterator, context)?\n                .into_list(context)?;\n\n            // b. Perform ? InitializeTypedArrayFromList(O, values).\n            BuiltinTypedArray::initialize_from_list::<T>(proto, values, context)\n        } else {\n            // 4. Else,\n\n            // a. NOTE: firstArgument is not an Iterable so assume it is already an array-like object.\n            // b. Perform ? InitializeTypedArrayFromArrayLike(O, firstArgument).\n            BuiltinTypedArray::initialize_from_array_like::<T>(proto, &first_argument, context)\n        }\n        .map(JsValue::from)\n\n        // v. Return O.\n    }\n}\n\n/// JavaScript `Int8Array` built-in implementation.\n#[derive(Debug, Copy, Clone)]\npub struct Int8Array;\n\nimpl TypedArrayMarker for Int8Array {\n    type Element = i8;\n\n    const ERASED: TypedArrayKind = TypedArrayKind::Int8;\n}\n\n/// JavaScript `Uint8Array` built-in implementation.\n#[derive(Debug, Copy, Clone)]\npub struct Uint8Array;\n\nimpl TypedArrayMarker for Uint8Array {\n    type Element = u8;\n\n    const ERASED: TypedArrayKind = TypedArrayKind::Uint8;\n}\n\n/// JavaScript `Uint8ClampedArray` built-in implementation.\n#[derive(Debug, Copy, Clone)]\npub struct Uint8ClampedArray;\n\nimpl TypedArrayMarker for Uint8ClampedArray {\n    type Element = ClampedU8;\n\n    const ERASED: TypedArrayKind = TypedArrayKind::Uint8Clamped;\n}\n\n/// JavaScript `Int16Array` built-in implementation.\n#[derive(Debug, Copy, Clone)]\npub struct Int16Array;\n\nimpl TypedArrayMarker for Int16Array {\n    type Element = i16;\n\n    const ERASED: TypedArrayKind = TypedArrayKind::Int16;\n}\n\n/// JavaScript `Uint16Array` built-in implementation.\n#[derive(Debug, Copy, Clone)]\npub struct Uint16Array;\n\nimpl TypedArrayMarker for Uint16Array {\n    type Element = u16;\n\n    const ERASED: TypedArrayKind = TypedArrayKind::Uint16;\n}\n\n/// JavaScript `Int32Array` built-in implementation.\n#[derive(Debug, Copy, Clone)]\npub struct Int32Array;\n\nimpl TypedArrayMarker for Int32Array {\n    type Element = i32;\n\n    const ERASED: TypedArrayKind = TypedArrayKind::Int32;\n}\n\n/// JavaScript `Uint32Array` built-in implementation.\n#[derive(Debug, Copy, Clone)]\npub struct Uint32Array;\n\nimpl TypedArrayMarker for Uint32Array {\n    type Element = u32;\n\n    const ERASED: TypedArrayKind = TypedArrayKind::Uint32;\n}\n\n/// JavaScript `BigInt64Array` built-in implementation.\n#[derive(Debug, Copy, Clone)]\npub struct BigInt64Array;\n\nimpl TypedArrayMarker for BigInt64Array {\n    type Element = i64;\n\n    const ERASED: TypedArrayKind = TypedArrayKind::BigInt64;\n}\n\n/// JavaScript `BigUint64Array` built-in implementation.\n#[derive(Debug, Copy, Clone)]\npub struct BigUint64Array;\n\nimpl TypedArrayMarker for BigUint64Array {\n    type Element = u64;\n\n    const ERASED: TypedArrayKind = TypedArrayKind::BigUint64;\n}\n\n/// JavaScript `Float32Array` built-in implementation.\n#[derive(Debug, Copy, Clone)]\n#[cfg(feature = \"float16\")]\npub struct Float16Array;\n\n#[cfg(feature = \"float16\")]\nimpl TypedArrayMarker for Float16Array {\n    type Element = Float16;\n\n    const ERASED: TypedArrayKind = TypedArrayKind::Float16;\n}\n\n/// JavaScript `Float32Array` built-in implementation.\n#[derive(Debug, Copy, Clone)]\npub struct Float32Array;\n\nimpl TypedArrayMarker for Float32Array {\n    type Element = f32;\n\n    const ERASED: TypedArrayKind = TypedArrayKind::Float32;\n}\n\n/// JavaScript `Float64Array` built-in implementation.\n#[derive(Debug, Copy, Clone)]\npub struct Float64Array;\n\nimpl TypedArrayMarker for Float64Array {\n    type Element = f64;\n\n    const ERASED: TypedArrayKind = TypedArrayKind::Float64;\n}\n\n/// Type of the array content.\n#[derive(Debug, Clone, Copy, PartialEq)]\npub(crate) enum ContentType {\n    Number,\n    BigInt,\n}\n\n/// List of all typed array kinds.\n#[derive(Debug, Copy, Clone, Eq, PartialEq, Trace, Finalize)]\n#[boa_gc(empty_trace)]\npub enum TypedArrayKind {\n    /// 8-bit signed integers.\n    Int8,\n    /// 8-bit unsigned integers.\n    Uint8,\n    /// 8-bit unsigned integers clamped to 0–255.\n    Uint8Clamped,\n    /// 16-bit signed integers.\n    Int16,\n    /// 16-bit unsigned integers.\n    Uint16,\n    /// 32-bit signed integers.\n    Int32,\n    /// 32-bit unsigned integers.\n    Uint32,\n    /// 64-bit signed integers in the platform byte order.\n    BigInt64,\n    /// 64-bit unsigned integers in the platform byte order.\n    BigUint64,\n    /// 16-bit floating point numbers in the platform byte order.\n    #[cfg(feature = \"float16\")]\n    Float16,\n    /// 32-bit floating point numbers in the platform byte order.\n    Float32,\n    /// 64-bit floating point numbers in the platform byte order.\n    Float64,\n}\n\nimpl TypedArrayKind {\n    /// Gets the name of this `TypedArrayKind` as a `JsString`.\n    pub(crate) const fn js_name(self) -> JsString {\n        match self {\n            TypedArrayKind::Int8 => StaticJsStrings::INT8_ARRAY,\n            TypedArrayKind::Uint8 => StaticJsStrings::UINT8_ARRAY,\n            TypedArrayKind::Uint8Clamped => StaticJsStrings::UINT8_CLAMPED_ARRAY,\n            TypedArrayKind::Int16 => StaticJsStrings::INT16_ARRAY,\n            TypedArrayKind::Uint16 => StaticJsStrings::UINT16_ARRAY,\n            TypedArrayKind::Int32 => StaticJsStrings::INT32_ARRAY,\n            TypedArrayKind::Uint32 => StaticJsStrings::UINT32_ARRAY,\n            TypedArrayKind::BigInt64 => StaticJsStrings::BIG_INT64_ARRAY,\n            TypedArrayKind::BigUint64 => StaticJsStrings::BIG_UINT64_ARRAY,\n            #[cfg(feature = \"float16\")]\n            TypedArrayKind::Float16 => StaticJsStrings::FLOAT16_ARRAY,\n            TypedArrayKind::Float32 => StaticJsStrings::FLOAT32_ARRAY,\n            TypedArrayKind::Float64 => StaticJsStrings::FLOAT64_ARRAY,\n        }\n    }\n\n    /// Gets the name of this `TypedArrayKind` as a `str`\n    pub(crate) const fn name(self) -> &'static str {\n        match self {\n            TypedArrayKind::Int8 => \"Int8\",\n            TypedArrayKind::Uint8 => \"Uint8\",\n            TypedArrayKind::Uint8Clamped => \"Uint8Clamped\",\n            TypedArrayKind::Int16 => \"Int16\",\n            TypedArrayKind::Uint16 => \"Uint16\",\n            TypedArrayKind::Int32 => \"Int32\",\n            TypedArrayKind::Uint32 => \"Uint32\",\n            TypedArrayKind::BigInt64 => \"BigInt64\",\n            TypedArrayKind::BigUint64 => \"BigUint64\",\n            #[cfg(feature = \"float16\")]\n            TypedArrayKind::Float16 => \"Float16\",\n            TypedArrayKind::Float32 => \"Float32\",\n            TypedArrayKind::Float64 => \"Float64\",\n        }\n    }\n\n    /// Gets the standard constructor accessor of this `TypedArrayKind`.\n    pub(crate) const fn standard_constructor(\n        self,\n    ) -> fn(&StandardConstructors) -> &StandardConstructor {\n        match self {\n            TypedArrayKind::Int8 => StandardConstructors::typed_int8_array,\n            TypedArrayKind::Uint8 => StandardConstructors::typed_uint8_array,\n            TypedArrayKind::Uint8Clamped => StandardConstructors::typed_uint8clamped_array,\n            TypedArrayKind::Int16 => StandardConstructors::typed_int16_array,\n            TypedArrayKind::Uint16 => StandardConstructors::typed_uint16_array,\n            TypedArrayKind::Int32 => StandardConstructors::typed_int32_array,\n            TypedArrayKind::Uint32 => StandardConstructors::typed_uint32_array,\n            TypedArrayKind::BigInt64 => StandardConstructors::typed_bigint64_array,\n            TypedArrayKind::BigUint64 => StandardConstructors::typed_biguint64_array,\n            #[cfg(feature = \"float16\")]\n            TypedArrayKind::Float16 => StandardConstructors::typed_float16_array,\n            TypedArrayKind::Float32 => StandardConstructors::typed_float32_array,\n            TypedArrayKind::Float64 => StandardConstructors::typed_float64_array,\n        }\n    }\n\n    /// Returns `true` if this kind of typed array supports `Atomics` operations\n    ///\n    /// Equivalent to `IsUnclampedIntegerElementType(type) is true || IsBigIntElementType(type) is true`.\n    pub(crate) fn supports_atomic_ops(self) -> bool {\n        match self {\n            TypedArrayKind::Int8\n            | TypedArrayKind::Uint8\n            | TypedArrayKind::Int16\n            | TypedArrayKind::Uint16\n            | TypedArrayKind::Int32\n            | TypedArrayKind::Uint32\n            | TypedArrayKind::BigInt64\n            | TypedArrayKind::BigUint64 => true,\n            // `f32` and `f64` support atomic operations on certain platforms, but it's not common and\n            // could require polyfilling the operations using CAS.\n            // `u8` clamps to the limits, which atomic operations don't support since\n            // they always overflow.\n            TypedArrayKind::Uint8Clamped | TypedArrayKind::Float32 | TypedArrayKind::Float64 => {\n                false\n            }\n            #[cfg(feature = \"float16\")]\n            TypedArrayKind::Float16 => false,\n        }\n    }\n\n    /// Gets the size of the type of element of this `TypedArrayKind`.\n    pub(crate) const fn element_size(self) -> u64 {\n        match self {\n            TypedArrayKind::Int8 | TypedArrayKind::Uint8 | TypedArrayKind::Uint8Clamped => {\n                size_of::<u8>() as u64\n            }\n            TypedArrayKind::Int16 | TypedArrayKind::Uint16 => size_of::<u16>() as u64,\n            #[cfg(feature = \"float16\")]\n            TypedArrayKind::Float16 => size_of::<u16>() as u64,\n            TypedArrayKind::Int32 | TypedArrayKind::Uint32 | TypedArrayKind::Float32 => {\n                size_of::<u32>() as u64\n            }\n            TypedArrayKind::BigInt64 | TypedArrayKind::BigUint64 | TypedArrayKind::Float64 => {\n                size_of::<u64>() as u64\n            }\n        }\n    }\n\n    /// Returns the content type of this `TypedArrayKind`.\n    pub(crate) const fn content_type(self) -> ContentType {\n        match self {\n            TypedArrayKind::BigInt64 | TypedArrayKind::BigUint64 => ContentType::BigInt,\n            TypedArrayKind::Int8\n            | TypedArrayKind::Uint8\n            | TypedArrayKind::Uint8Clamped\n            | TypedArrayKind::Int16\n            | TypedArrayKind::Uint16\n            | TypedArrayKind::Int32\n            | TypedArrayKind::Uint32\n            | TypedArrayKind::Float32\n            | TypedArrayKind::Float64 => ContentType::Number,\n            #[cfg(feature = \"float16\")]\n            TypedArrayKind::Float16 => ContentType::Number,\n        }\n    }\n\n    /// Convert `value` into the typed array element corresponding to this `TypedArrayKind`.\n    pub(crate) fn get_element(\n        self,\n        value: &JsValue,\n        context: &mut Context,\n    ) -> JsResult<TypedArrayElement> {\n        match self {\n            TypedArrayKind::Int8 => value.to_int8(context).map(TypedArrayElement::Int8),\n            TypedArrayKind::Uint8 => value.to_uint8(context).map(TypedArrayElement::Uint8),\n            TypedArrayKind::Uint8Clamped => value\n                .to_uint8_clamp(context)\n                .map(|u| TypedArrayElement::Uint8Clamped(ClampedU8(u))),\n            TypedArrayKind::Int16 => value.to_int16(context).map(TypedArrayElement::Int16),\n            TypedArrayKind::Uint16 => value.to_uint16(context).map(TypedArrayElement::Uint16),\n            TypedArrayKind::Int32 => value.to_i32(context).map(TypedArrayElement::Int32),\n            TypedArrayKind::Uint32 => value.to_u32(context).map(TypedArrayElement::Uint32),\n            TypedArrayKind::BigInt64 => {\n                value.to_big_int64(context).map(TypedArrayElement::BigInt64)\n            }\n            TypedArrayKind::BigUint64 => value\n                .to_big_uint64(context)\n                .map(TypedArrayElement::BigUint64),\n            #[cfg(feature = \"float16\")]\n            TypedArrayKind::Float16 => value\n                .to_f16(context)\n                .map(|f| TypedArrayElement::Float16(Float16(f))),\n            TypedArrayKind::Float32 => value\n                .to_number(context)\n                .map(|f| TypedArrayElement::Float32(f as f32)),\n            TypedArrayKind::Float64 => value.to_number(context).map(TypedArrayElement::Float64),\n        }\n    }\n\n    /// Convert `value` into the typed array element corresponding to this `TypedArrayKind`,\n    /// assuming the `ContentType` of this kind is `Number`.\n    pub(crate) fn to_element_f64(self, value: f64) -> TypedArrayElement {\n        match self {\n            TypedArrayKind::Int8 => TypedArrayElement::Int8(value as i8),\n            TypedArrayKind::Uint8 => TypedArrayElement::Uint8(value as u8),\n            TypedArrayKind::Uint8Clamped => {\n                TypedArrayElement::Uint8Clamped(ClampedU8(value.clamp(0.0, 255.0).round() as u8))\n            }\n            TypedArrayKind::Int16 => TypedArrayElement::Int16(value as i16),\n            TypedArrayKind::Uint16 => TypedArrayElement::Uint16(value as u16),\n            TypedArrayKind::Int32 => TypedArrayElement::Int32(value as i32),\n            TypedArrayKind::Uint32 => TypedArrayElement::Uint32(value as u32),\n            #[cfg(feature = \"float16\")]\n            TypedArrayKind::Float16 => {\n                TypedArrayElement::Float16(Float16(float16::f16::from_f64(value)))\n            }\n            TypedArrayKind::Float32 => TypedArrayElement::Float32(value as f32),\n            TypedArrayKind::Float64 => TypedArrayElement::Float64(value),\n            TypedArrayKind::BigInt64 | TypedArrayKind::BigUint64 => {\n                panic!(\"cannot convert f64 to BigInt typed array element\")\n            }\n        }\n    }\n\n    /// Convert `value` into the typed array element corresponding to this `TypedArrayKind`,\n    /// assuming the `ContentType` of this kind is `BigInt`.\n    pub(crate) fn to_element_i64(self, value: i64) -> TypedArrayElement {\n        match self {\n            TypedArrayKind::BigInt64 => TypedArrayElement::BigInt64(value),\n            TypedArrayKind::BigUint64 => TypedArrayElement::BigUint64(value as u64),\n            _ => panic!(\"cannot convert i64 to Number typed array element\"),\n        }\n    }\n}\n\n/// An element of a certain `TypedArray` kind.\n#[derive(Debug, Copy, Clone, PartialEq)]\npub(crate) enum TypedArrayElement {\n    Int8(i8),\n    Uint8(u8),\n    Uint8Clamped(ClampedU8),\n    Int16(i16),\n    Uint16(u16),\n    Int32(i32),\n    Uint32(u32),\n    BigInt64(i64),\n    BigUint64(u64),\n    #[cfg(feature = \"float16\")]\n    Float16(Float16),\n    Float32(f32),\n    Float64(f64),\n}\n\nimpl TypedArrayElement {\n    /// Converts the element into its extended bytes representation as an `u64`.\n    ///\n    /// This is guaranteed to never fail, since all numeric types supported by JS are less than\n    /// 8 bytes long.\n    pub(crate) fn to_bits(self) -> u64 {\n        #[allow(clippy::cast_lossless)]\n        match self {\n            TypedArrayElement::Int8(num) => num as u64,\n            TypedArrayElement::Uint8(num) => num as u64,\n            TypedArrayElement::Uint8Clamped(num) => num.0 as u64,\n            TypedArrayElement::Int16(num) => num as u64,\n            TypedArrayElement::Uint16(num) => num as u64,\n            TypedArrayElement::Int32(num) => num as u64,\n            TypedArrayElement::Uint32(num) => num as u64,\n            TypedArrayElement::BigInt64(num) => num as u64,\n            TypedArrayElement::BigUint64(num) => num,\n            #[cfg(feature = \"float16\")]\n            TypedArrayElement::Float16(num) => num.0.to_bits() as u64,\n            TypedArrayElement::Float32(num) => num.to_bits() as u64,\n            TypedArrayElement::Float64(num) => num.to_bits(),\n        }\n    }\n\n    /// Gets the `f64` representation of this `TypedArrayElement`.\n    ///\n    /// # Panics\n    ///\n    /// Panics if the `ContentType` of `self` is not `Number`.\n    pub(crate) fn as_f64(self) -> f64 {\n        match self {\n            TypedArrayElement::Int8(v) => v.into(),\n            TypedArrayElement::Uint8(v) => v.into(),\n            TypedArrayElement::Uint8Clamped(v) => v.0.into(),\n            TypedArrayElement::Int16(v) => v.into(),\n            TypedArrayElement::Uint16(v) => v.into(),\n            TypedArrayElement::Int32(v) => v.into(),\n            TypedArrayElement::Uint32(v) => v.into(),\n            #[cfg(feature = \"float16\")]\n            TypedArrayElement::Float16(v) => v.0.to_f64(),\n            TypedArrayElement::Float32(v) => v.into(),\n            TypedArrayElement::Float64(v) => v,\n            TypedArrayElement::BigInt64(_) | TypedArrayElement::BigUint64(_) => {\n                panic!(\"cannot convert BigInt typed array element to f64\")\n            }\n        }\n    }\n\n    /// Gets the `i64` representation of this `TypedArrayElement`.\n    ///\n    /// # Panics\n    ///\n    /// Panics if the `ContentType` of `self` is not `BigInt`.\n    pub(crate) fn as_i64(self) -> i64 {\n        match self {\n            TypedArrayElement::BigInt64(v) => v,\n            TypedArrayElement::BigUint64(v) => v as i64,\n            _ => panic!(\"cannot convert Number typed array element to i64\"),\n        }\n    }\n\n    /// Casts this `TypedArrayElement` into another `TypedArrayKind`.\n    ///\n    /// # Panics\n    ///\n    /// Panics if the `ContentType` of `self` and `target` don't match.\n    pub(crate) fn cast(self, target: TypedArrayKind) -> Self {\n        match target.content_type() {\n            ContentType::Number => target.to_element_f64(self.as_f64()),\n            ContentType::BigInt => target.to_element_i64(self.as_i64()),\n        }\n    }\n}\n\nimpl From<i8> for TypedArrayElement {\n    fn from(value: i8) -> Self {\n        Self::Int8(value)\n    }\n}\n\nimpl From<u8> for TypedArrayElement {\n    fn from(value: u8) -> Self {\n        Self::Uint8(value)\n    }\n}\n\nimpl From<ClampedU8> for TypedArrayElement {\n    fn from(value: ClampedU8) -> Self {\n        Self::Uint8Clamped(value)\n    }\n}\n\nimpl From<i16> for TypedArrayElement {\n    fn from(value: i16) -> Self {\n        Self::Int16(value)\n    }\n}\n\nimpl From<u16> for TypedArrayElement {\n    fn from(value: u16) -> Self {\n        Self::Uint16(value)\n    }\n}\n\nimpl From<i32> for TypedArrayElement {\n    fn from(value: i32) -> Self {\n        Self::Int32(value)\n    }\n}\n\nimpl From<u32> for TypedArrayElement {\n    fn from(value: u32) -> Self {\n        Self::Uint32(value)\n    }\n}\n\nimpl From<i64> for TypedArrayElement {\n    fn from(value: i64) -> Self {\n        Self::BigInt64(value)\n    }\n}\n\nimpl From<u64> for TypedArrayElement {\n    fn from(value: u64) -> Self {\n        Self::BigUint64(value)\n    }\n}\n\n#[cfg(feature = \"float16\")]\nimpl From<Float16> for TypedArrayElement {\n    fn from(value: Float16) -> Self {\n        Self::Float16(value)\n    }\n}\n\nimpl From<f32> for TypedArrayElement {\n    fn from(value: f32) -> Self {\n        Self::Float32(value)\n    }\n}\n\nimpl From<f64> for TypedArrayElement {\n    fn from(value: f64) -> Self {\n        Self::Float64(value)\n    }\n}\n\nimpl From<TypedArrayElement> for JsValue {\n    fn from(value: TypedArrayElement) -> Self {\n        match value {\n            TypedArrayElement::Int8(value) => Numeric::from(value),\n            TypedArrayElement::Uint8(value) => Numeric::from(value),\n            TypedArrayElement::Uint8Clamped(value) => Numeric::from(value),\n            TypedArrayElement::Int16(value) => Numeric::from(value),\n            TypedArrayElement::Uint16(value) => Numeric::from(value),\n            TypedArrayElement::Int32(value) => Numeric::from(value),\n            TypedArrayElement::Uint32(value) => Numeric::from(value),\n            TypedArrayElement::BigInt64(value) => Numeric::from(value),\n            TypedArrayElement::BigUint64(value) => Numeric::from(value),\n            #[cfg(feature = \"float16\")]\n            TypedArrayElement::Float16(value) => Numeric::from(value),\n            TypedArrayElement::Float32(value) => Numeric::from(value),\n            TypedArrayElement::Float64(value) => Numeric::from(value),\n        }\n        .into()\n    }\n}\n\n#[cfg(test)]\nmod tests;\n"
  },
  {
    "path": "core/engine/src/builtins/typed_array/object.rs",
    "content": "//! This module implements the `TypedArray` exotic object.\n\nuse std::sync::atomic::Ordering;\n\nuse crate::{\n    Context, JsExpect, JsNativeError, JsResult, JsString, JsValue,\n    builtins::array_buffer::BufferObject,\n    object::{\n        JsData, JsObject,\n        internal_methods::{\n            InternalMethodPropertyContext, InternalObjectMethods, ORDINARY_INTERNAL_METHODS,\n            ordinary_define_own_property, ordinary_delete, ordinary_get, ordinary_get_own_property,\n            ordinary_has_property, ordinary_prevent_extensions, ordinary_set, ordinary_try_get,\n        },\n    },\n    property::{PropertyDescriptor, PropertyKey},\n};\nuse boa_gc::{Finalize, Trace};\nuse boa_macros::js_str;\n\nuse super::{TypedArrayKind, is_valid_integer_index};\n\n/// A `TypedArray` object is an exotic object that performs special handling of integer\n/// index property keys.\n///\n/// More information:\n///  - [ECMAScript reference][spec]\n///\n/// [spec]: https://tc39.es/ecma262/#sec-typedarray-exotic-objects\n#[derive(Debug, Clone, Trace, Finalize)]\npub struct TypedArray {\n    viewed_array_buffer: BufferObject,\n    kind: TypedArrayKind,\n    byte_offset: u64,\n    byte_length: Option<u64>,\n    array_length: Option<u64>,\n}\n\nimpl JsData for TypedArray {\n    fn internal_methods(&self) -> &'static InternalObjectMethods {\n        static METHODS: InternalObjectMethods = InternalObjectMethods {\n            __get_own_property__: typed_array_exotic_get_own_property,\n            __has_property__: typed_array_exotic_has_property,\n            __define_own_property__: typed_array_exotic_define_own_property,\n            __try_get__: typed_array_exotic_try_get,\n            __get__: typed_array_exotic_get,\n            __set__: typed_array_exotic_set,\n            __delete__: typed_array_exotic_delete,\n            __own_property_keys__: typed_array_exotic_own_property_keys,\n            __prevent_extensions__: typed_array_exotic_prevent_extensions,\n            ..ORDINARY_INTERNAL_METHODS\n        };\n\n        &METHODS\n    }\n}\n\nimpl TypedArray {\n    pub(crate) const fn new(\n        viewed_array_buffer: BufferObject,\n        kind: TypedArrayKind,\n        byte_offset: u64,\n        byte_length: Option<u64>,\n        array_length: Option<u64>,\n    ) -> Self {\n        Self {\n            viewed_array_buffer,\n            kind,\n            byte_offset,\n            byte_length,\n            array_length,\n        }\n    }\n\n    /// Returns `true` if the typed array has an automatic array length.\n    pub(crate) fn is_auto_length(&self) -> bool {\n        self.array_length.is_none()\n    }\n\n    /// Abstract operation [`IsTypedArrayOutOfBounds ( taRecord )`][spec].\n    ///\n    /// [spec]: https://tc39.es/ecma262/sec-istypedarrayoutofbounds\n    pub(crate) fn is_out_of_bounds(&self, buf_byte_len: usize) -> bool {\n        // Checks when allocating the buffer ensure the length fits inside an `u64`.\n        let buf_byte_len = buf_byte_len as u64;\n\n        // 1. Let O be taRecord.[[Object]].\n        // 2. Let bufferByteLength be taRecord.[[CachedBufferByteLength]].\n        // 3. Assert: IsDetachedBuffer(O.[[ViewedArrayBuffer]]) is true if and only if bufferByteLength is detached.\n        // 4. If bufferByteLength is detached, return true.\n        // Handled by the caller\n\n        // 5. Let byteOffsetStart be O.[[ByteOffset]].\n        let byte_start = self.byte_offset;\n\n        // 6. If O.[[ArrayLength]] is auto, then\n        //     a. Let byteOffsetEnd be bufferByteLength.\n        let byte_end = self.array_length.map_or(buf_byte_len, |arr_len| {\n            // 7. Else,\n            //     a. Let elementSize be TypedArrayElementSize(O).\n            let element_size = self.kind.element_size();\n\n            //     b. Let byteOffsetEnd be byteOffsetStart + O.[[ArrayLength]] × elementSize.\n            byte_start + arr_len * element_size\n        });\n\n        // 8. If byteOffsetStart > bufferByteLength or byteOffsetEnd > bufferByteLength, return true.\n        // 9. NOTE: 0-length TypedArrays are not considered out-of-bounds.\n        // 10. Return false.\n        byte_start > buf_byte_len || byte_end > buf_byte_len\n    }\n\n    /// Get the `TypedArray` object's byte offset.\n    #[must_use]\n    pub const fn byte_offset(&self) -> u64 {\n        self.byte_offset\n    }\n\n    /// Get the `TypedArray` object's typed array kind.\n    #[must_use]\n    pub const fn kind(&self) -> TypedArrayKind {\n        self.kind\n    }\n\n    /// Get a reference to the `TypedArray` object's viewed array buffer.\n    #[must_use]\n    pub(crate) const fn viewed_array_buffer(&self) -> &BufferObject {\n        &self.viewed_array_buffer\n    }\n\n    /// [`TypedArrayByteLength ( taRecord )`][spec].\n    ///\n    /// Get the `TypedArray` object's byte length.\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-typedarraybytelength\n    #[must_use]\n    pub fn byte_length(&self, buf_byte_len: usize) -> u64 {\n        // 1. If IsTypedArrayOutOfBounds(taRecord) is true, return 0.\n        if self.is_out_of_bounds(buf_byte_len) {\n            return 0;\n        }\n\n        // 2. Let length be TypedArrayLength(taRecord).\n        let length = self.array_length(buf_byte_len);\n        // 3. If length = 0, return 0.\n        if length == 0 {\n            return 0;\n        }\n\n        // 4. Let O be taRecord.[[Object]].\n\n        // 5. If O.[[ByteLength]] is not auto, return O.[[ByteLength]].\n        if let Some(byte_length) = self.byte_length {\n            return byte_length;\n        }\n\n        // 6. Let elementSize be TypedArrayElementSize(O).\n        let elem_size = self.kind.element_size();\n\n        // 7. Return length × elementSize.\n        // Should not overflow thanks to the checks at creation time.\n        length * elem_size\n    }\n\n    /// [`TypedArrayLength ( taRecord )`][spec].\n    ///\n    /// Get the `TypedArray` object's array length.\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-typedarraylength\n    #[must_use]\n    pub fn array_length(&self, buf_byte_len: usize) -> u64 {\n        // 1. Assert: IsTypedArrayOutOfBounds(taRecord) is false.\n        debug_assert!(!self.is_out_of_bounds(buf_byte_len));\n        let buf_byte_len = buf_byte_len as u64;\n\n        // 2. Let O be taRecord.[[Object]].\n\n        // 3. If O.[[ArrayLength]] is not auto, return O.[[ArrayLength]].\n        if let Some(array_length) = self.array_length {\n            return array_length;\n        }\n        // 4. Assert: IsFixedLengthArrayBuffer(O.[[ViewedArrayBuffer]]) is false.\n\n        // 5. Let byteOffset be O.[[ByteOffset]].\n        let byte_offset = self.byte_offset;\n        // 6. Let elementSize be TypedArrayElementSize(O).\n        let elem_size = self.kind.element_size();\n\n        // 7. Let byteLength be taRecord.[[CachedBufferByteLength]].\n        // 8. Assert: byteLength is not detached.\n        // 9. Return floor((byteLength - byteOffset) / elementSize).\n        (buf_byte_len - byte_offset) / elem_size\n    }\n\n    /// Abstract operation [`ValidateTypedArray ( O, order )`][spec].\n    ///\n    /// [spec]: https://tc39.es/ecma262/sec-validatetypedarray\n    pub(crate) fn validate(this: &JsValue, order: Ordering) -> JsResult<(JsObject<Self>, usize)> {\n        // 1. Perform ? RequireInternalSlot(O, [[TypedArrayName]]).\n        let obj = this\n            .as_object()\n            .and_then(|o| o.clone().downcast::<Self>().ok())\n            .ok_or_else(|| {\n                JsNativeError::typ().with_message(\"`this` is not a typed array object\")\n            })?;\n\n        let len = {\n            let array = obj.borrow();\n            let buffer = array.data().viewed_array_buffer().as_buffer();\n            // 2. Assert: O has a [[ViewedArrayBuffer]] internal slot.\n            // 3. Let taRecord be MakeTypedArrayWithBufferWitnessRecord(O, order).\n            // 4. If IsTypedArrayOutOfBounds(taRecord) is true, throw a TypeError exception.\n            let Some(buf) = buffer\n                .bytes(order)\n                .filter(|buf| !array.data().is_out_of_bounds(buf.len()))\n            else {\n                return Err(JsNativeError::typ()\n                    .with_message(\"typed array is outside the bounds of its inner buffer\")\n                    .into());\n            };\n            buf.len()\n        };\n\n        // 5. Return taRecord.\n        Ok((obj, len))\n    }\n\n    /// Validates `index` to be in bounds for the inner buffer of this `TypedArray`.\n    ///\n    /// Note: if this is only used for bounds checking, it is recommended to use\n    /// the `Ordering::Relaxed` ordering to get the buffer slice.\n    pub(crate) fn validate_index(&self, index: f64, buf_len: usize) -> Option<u64> {\n        // 2. If IsIntegralNumber(index) is false, return false.\n        if index.is_nan() || index.is_infinite() || index.fract() != 0.0 {\n            return None;\n        }\n\n        // 3. If index is -0𝔽, return false.\n        if index == 0.0 && index.is_sign_negative() {\n            return None;\n        }\n\n        // 6. If IsTypedArrayOutOfBounds(taRecord) is true, return false.\n        if self.is_out_of_bounds(buf_len) {\n            return None;\n        }\n\n        // 7. Let length be TypedArrayLength(taRecord).\n        let length = self.array_length(buf_len);\n\n        // 8. If ℝ(index) < 0 or ℝ(index) ≥ length, return false.\n        if index < 0.0 || index >= length as f64 {\n            return None;\n        }\n\n        // 9. Return true.\n        Some(index as u64)\n    }\n\n    /// Validates a `u64` index to be in bounds for the inner buffer of this `TypedArray`.\n    ///\n    /// This is an optimized variant of [`validate_index`](Self::validate_index) for cases where\n    /// the index is already known to be a non-negative integer (`u64`), skipping the redundant\n    /// checks for `NaN`, infinity, fractional values, and negative zero.\n    pub(crate) fn validate_index_u64(&self, index: u64, buf_len: usize) -> Option<u64> {\n        // 1. If IsTypedArrayOutOfBounds(taRecord) is true, return false.\n        if self.is_out_of_bounds(buf_len) {\n            return None;\n        }\n\n        // 2. Let length be TypedArrayLength(taRecord).\n        let length = self.array_length(buf_len);\n\n        // 3. If index ≥ length, return false.\n        if index >= length {\n            return None;\n        }\n\n        // 4. Return true.\n        Some(index)\n    }\n}\n\n// Integer-Indexed Exotic Objects [[PreventExtensions]] ( O )\n// https://tc39.es/ecma262/#sec-integer-indexed-exotic-objects-preventextensions\n//\n// 1. If IsTypedArrayFixedLength(O) is false, return false.\n// 2. Return OrdinaryPreventExtensions(O).\npub(crate) fn typed_array_exotic_prevent_extensions(\n    obj: &JsObject,\n    context: &mut Context,\n) -> JsResult<bool> {\n    let is_fixed_length = {\n        let ta = obj\n            .downcast_ref::<TypedArray>()\n            .js_expect(\"must be a TypedArray\")?;\n\n        ta.viewed_array_buffer().as_buffer().is_fixed_len()\n    };\n\n    // 1. If IsTypedArrayFixedLength(O) is false, return false.\n    if !is_fixed_length {\n        return Ok(false);\n    }\n\n    // 2. Return OrdinaryPreventExtensions(O).\n    ordinary_prevent_extensions(obj, context)\n}\n\n/// `CanonicalNumericIndexString ( argument )`\n///\n/// More information:\n///  - [ECMAScript reference][spec]\n///\n/// [spec]: https://tc39.es/ecma262/#sec-canonicalnumericindexstring\nfn canonical_numeric_index_string(argument: &JsString) -> Option<f64> {\n    // 1. If argument is \"-0\", return -0𝔽.\n    if argument == &js_str!(\"-0\") {\n        return Some(-0.0);\n    }\n\n    // 2. Let n be ! ToNumber(argument).\n    let n = argument.to_number();\n\n    // 3. If ! ToString(n) is argument, return n.\n    if &JsString::from(n) == argument {\n        return Some(n);\n    }\n\n    // 4. Return undefined.\n    None\n}\n\n/// `[[GetOwnProperty]]` internal method for `TypedArray` exotic objects.\n///\n/// More information:\n///  - [ECMAScript reference][spec]\n///\n/// [spec]: https://tc39.es/ecma262/#sec-typedarray-getownproperty\npub(crate) fn typed_array_exotic_get_own_property(\n    obj: &JsObject,\n    key: &PropertyKey,\n    context: &mut InternalMethodPropertyContext<'_>,\n) -> JsResult<Option<PropertyDescriptor>> {\n    let p = match key {\n        PropertyKey::String(key) => {\n            // 1.a. Let numericIndex be CanonicalNumericIndexString(P).\n            canonical_numeric_index_string(key)\n        }\n        PropertyKey::Index(index) => Some(index.get().into()),\n        PropertyKey::Symbol(_) => None,\n    };\n\n    // 1. If P is a String, then\n    // 1.b. If numericIndex is not undefined, then\n    if let Some(numeric_index) = p {\n        // i. Let value be IntegerIndexedElementGet(O, numericIndex).\n        let value = typed_array_get_element(obj, numeric_index)?;\n\n        // ii. If value is undefined, return undefined.\n        // iii. Return the PropertyDescriptor { [[Value]]: value, [[Writable]]: true, [[Enumerable]]: true, [[Configurable]]: true }.\n        return Ok(value.map(|v| {\n            PropertyDescriptor::builder()\n                .value(v)\n                .writable(true)\n                .enumerable(true)\n                .configurable(true)\n                .build()\n        }));\n    }\n\n    // 2. Return OrdinaryGetOwnProperty(O, P).\n    ordinary_get_own_property(obj, key, context)\n}\n\n/// `[[HasProperty]]` internal method for `TypedArray` exotic objects.\n///\n/// More information:\n///  - [ECMAScript reference][spec]\n///\n/// [spec]: https://tc39.es/ecma262/#sec-typedarray-hasproperty\npub(crate) fn typed_array_exotic_has_property(\n    obj: &JsObject,\n    key: &PropertyKey,\n    context: &mut InternalMethodPropertyContext<'_>,\n) -> JsResult<bool> {\n    let p = match key {\n        PropertyKey::String(key) => {\n            // 1.a. Let numericIndex be CanonicalNumericIndexString(P).\n            canonical_numeric_index_string(key)\n        }\n        PropertyKey::Index(index) => Some(index.get().into()),\n        PropertyKey::Symbol(_) => None,\n    };\n\n    // 1. If P is a String, then\n    // 1.b. If numericIndex is not undefined, return IsValidIntegerIndex(O, numericIndex).\n    if let Some(numeric_index) = p {\n        return is_valid_integer_index(obj, numeric_index);\n    }\n\n    // 2. Return ? OrdinaryHasProperty(O, P).\n    ordinary_has_property(obj, key, context)\n}\n\n/// `[[DefineOwnProperty]]` internal method for `TypedArray` exotic objects.\n///\n/// More information:\n///  - [ECMAScript reference][spec]\n///\n/// [spec]: https://tc39.es/ecma262/#sec-typedarray-defineownproperty\npub(crate) fn typed_array_exotic_define_own_property(\n    obj: &JsObject,\n    key: &PropertyKey,\n    desc: PropertyDescriptor,\n    context: &mut InternalMethodPropertyContext<'_>,\n) -> JsResult<bool> {\n    let p = match key {\n        PropertyKey::String(key) => {\n            // 1.a. Let numericIndex be CanonicalNumericIndexString(P).\n            canonical_numeric_index_string(key)\n        }\n        PropertyKey::Index(index) => Some(index.get().into()),\n        PropertyKey::Symbol(_) => None,\n    };\n\n    // 1. If P is a String, then\n    // 1.b. If numericIndex is not undefined, then\n    if let Some(numeric_index) = p {\n        // i. If IsValidIntegerIndex(O, numericIndex) is false, return false.\n        if !is_valid_integer_index(obj, numeric_index)? {\n            return Ok(false);\n        }\n\n        // ii. If Desc has a [[Configurable]] field and Desc.[[Configurable]] is false, return false.\n        if desc.configurable() == Some(false) {\n            return Ok(false);\n        }\n\n        // iii. If Desc has an [[Enumerable]] field and Desc.[[Enumerable]] is false, return false.\n        if desc.enumerable() == Some(false) {\n            return Ok(false);\n        }\n\n        // iv. If IsAccessorDescriptor(Desc) is true, return false.\n        if desc.is_accessor_descriptor() {\n            return Ok(false);\n        }\n\n        // v. If Desc has a [[Writable]] field and Desc.[[Writable]] is false, return false.\n        if desc.writable() == Some(false) {\n            return Ok(false);\n        }\n\n        // vi. If Desc has a [[Value]] field, perform ? IntegerIndexedElementSet(O, numericIndex, Desc.[[Value]]).\n        if let Some(value) = desc.value() {\n            typed_array_set_element(obj, numeric_index, value, context)?;\n        }\n\n        // vii. Return true.\n        return Ok(true);\n    }\n\n    // 2. Return ! OrdinaryDefineOwnProperty(O, P, Desc).\n    ordinary_define_own_property(obj, key, desc, context)\n}\n\n/// Internal optimization method for `TypedArray` exotic objects.\n///\n/// This method combines the internal methods `[[HasProperty]]` and `[[Get]]`.\n///\n/// More information:\n///  - [ECMAScript reference HasProperty][spec0]\n///  - [ECMAScript reference Get][spec1]\n///\n/// [spec0]: https://tc39.es/ecma262/#sec-typedarray-hasproperty\n/// [spec1]: https://tc39.es/ecma262/#sec-typedarray-get\npub(crate) fn typed_array_exotic_try_get(\n    obj: &JsObject,\n    key: &PropertyKey,\n    receiver: JsValue,\n    context: &mut InternalMethodPropertyContext<'_>,\n) -> JsResult<Option<JsValue>> {\n    let p = match key {\n        PropertyKey::String(key) => {\n            // 1.a. Let numericIndex be CanonicalNumericIndexString(P).\n            canonical_numeric_index_string(key)\n        }\n        PropertyKey::Index(index) => Some(index.get().into()),\n        PropertyKey::Symbol(_) => None,\n    };\n\n    // 1. If P is a String, then\n    // 1.b. If numericIndex is not undefined, then\n    if let Some(numeric_index) = p {\n        // i. Return IntegerIndexedElementGet(O, numericIndex).\n        return typed_array_get_element(obj, numeric_index);\n    }\n\n    // 2. Return ? OrdinaryGet(O, P, Receiver).\n    ordinary_try_get(obj, key, receiver, context)\n}\n\n/// Internal method `[[Get]]` for `TypedArray` exotic objects.\n///\n/// More information:\n///  - [ECMAScript reference][spec]\n///\n/// [spec]: https://tc39.es/ecma262/#sec-typedarray-get\npub(crate) fn typed_array_exotic_get(\n    obj: &JsObject,\n    key: &PropertyKey,\n    receiver: JsValue,\n    context: &mut InternalMethodPropertyContext<'_>,\n) -> JsResult<JsValue> {\n    let p = match key {\n        PropertyKey::String(key) => {\n            // 1.a. Let numericIndex be CanonicalNumericIndexString(P).\n            canonical_numeric_index_string(key)\n        }\n        PropertyKey::Index(index) => Some(index.get().into()),\n        PropertyKey::Symbol(_) => None,\n    };\n\n    // 1. If P is a String, then\n    // 1.b. If numericIndex is not undefined, then\n    if let Some(numeric_index) = p {\n        // i. Return IntegerIndexedElementGet(O, numericIndex).\n        return Ok(typed_array_get_element(obj, numeric_index)?.unwrap_or_default());\n    }\n\n    // 2. Return ? OrdinaryGet(O, P, Receiver).\n    ordinary_get(obj, key, receiver, context)\n}\n\n/// Internal method `[[Set]]` for `TypedArray` exotic objects.\n///\n/// More information:\n///  - [ECMAScript reference][spec]\n///\n/// [spec]: https://tc39.es/ecma262/#sec-typedarray-set\npub(crate) fn typed_array_exotic_set(\n    obj: &JsObject,\n    key: PropertyKey,\n    value: JsValue,\n    receiver: JsValue,\n    context: &mut InternalMethodPropertyContext<'_>,\n) -> JsResult<bool> {\n    let p = match &key {\n        PropertyKey::String(key) => {\n            // 1.a. Let numericIndex be CanonicalNumericIndexString(P).\n            canonical_numeric_index_string(key)\n        }\n        PropertyKey::Index(index) => Some(index.get().into()),\n        PropertyKey::Symbol(_) => None,\n    };\n\n    // 1. If P is a String, then\n    // 1.b. If numericIndex is not undefined, then\n    if let Some(numeric_index) = p {\n        // i. If SameValue(O, Receiver) is true, then\n        if JsValue::same_value(&obj.clone().into(), &receiver) {\n            // 1. Perform ? IntegerIndexedElementSet(O, numericIndex, V).\n            typed_array_set_element(obj, numeric_index, &value, context)?;\n\n            // 2. Return true.\n            return Ok(true);\n        }\n\n        // ii. If IsValidIntegerIndex(O, numericIndex) is false, return true.\n        if !is_valid_integer_index(obj, numeric_index)? {\n            return Ok(true);\n        }\n    }\n\n    // 2. Return ? OrdinarySet(O, P, V, Receiver).\n    ordinary_set(obj, key, value, receiver, context)\n}\n\n/// Internal method `[[Delete]]` for `TypedArray` exotic objects.\n///\n/// More information:\n///  - [ECMAScript reference][spec]\n///\n/// [spec]: https://tc39.es/ecma262/#sec-typedarray-delete\npub(crate) fn typed_array_exotic_delete(\n    obj: &JsObject,\n    key: &PropertyKey,\n    context: &mut InternalMethodPropertyContext<'_>,\n) -> JsResult<bool> {\n    let p = match &key {\n        PropertyKey::String(key) => {\n            // 1.a. Let numericIndex be CanonicalNumericIndexString(P).\n            canonical_numeric_index_string(key)\n        }\n        PropertyKey::Index(index) => Some(index.get().into()),\n        PropertyKey::Symbol(_) => None,\n    };\n\n    // 1. If P is a String, then\n    // 1.b. If numericIndex is not undefined, then\n    if let Some(numeric_index) = p {\n        // i. If IsValidIntegerIndex(O, numericIndex) is false, return true; else return false.\n        return Ok(!is_valid_integer_index(obj, numeric_index)?);\n    }\n\n    // 2. Return ! OrdinaryDelete(O, P).\n    ordinary_delete(obj, key, context)\n}\n\n/// Internal method `[[OwnPropertyKeys]]` for `TypedArray` exotic objects.\n///\n/// More information:\n///  - [ECMAScript reference][spec]\n///\n/// [spec]: https://tc39.es/ecma262/#sec-typedarray-ownpropertykeys\n#[allow(clippy::unnecessary_wraps)]\npub(crate) fn typed_array_exotic_own_property_keys(\n    obj: &JsObject,\n    _context: &mut Context,\n) -> JsResult<Vec<PropertyKey>> {\n    let inner = obj\n        .downcast_ref::<TypedArray>()\n        .js_expect(\"TypedArray exotic method should only be callable from TypedArray objects\")?;\n\n    // 1. Let taRecord be MakeTypedArrayWithBufferWitnessRecord(O, seq-cst).\n    // 2. Let keys be a new empty List.\n    // 3. If IsTypedArrayOutOfBounds(taRecord) is false, then\n    let mut keys = match inner\n        .viewed_array_buffer\n        .as_buffer()\n        .bytes(Ordering::SeqCst)\n    {\n        Some(buf) if !inner.is_out_of_bounds(buf.len()) => {\n            // a. Let length be TypedArrayLength(taRecord).\n            let length = inner.array_length(buf.len());\n\n            // b. For each integer i such that 0 ≤ i < length, in ascending order, do\n            //    i. Append ! ToString(𝔽(i)) to keys.\n            (0..length).map(PropertyKey::from).collect()\n        }\n        _ => Vec::new(),\n    };\n    drop(inner);\n\n    // 4. For each own property key P of O such that P is a String and P is not an integer index, in ascending chronological order of property creation, do\n    //     a. Append P to keys.\n    // 5. For each own property key P of O such that P is a Symbol, in ascending chronological order of property creation, do\n    //     a. Append P to keys.\n    keys.extend(obj.borrow().properties.shape.keys());\n\n    // 6. Return keys.\n    Ok(keys)\n}\n\n/// Abstract operation `TypedArrayGetElement ( O, index )`.\n///\n/// More information:\n///  - [ECMAScript reference][spec]\n///\n/// [spec]: https://tc39.es/ecma262/sec-typedarraygetelement\nfn typed_array_get_element(obj: &JsObject, index: f64) -> JsResult<Option<JsValue>> {\n    let inner = obj\n        .downcast_ref::<TypedArray>()\n        .js_expect(\"typed_array_get_element should only be called on TypedArray objects\")?;\n    let buffer = inner.viewed_array_buffer();\n    let buffer = buffer.as_buffer();\n\n    // 1. If IsValidIntegerIndex(O, index) is false, return undefined.\n    let Some(buffer) = buffer.bytes(Ordering::Relaxed) else {\n        return Ok(None);\n    };\n\n    let Some(index) = inner.validate_index(index, buffer.len()) else {\n        return Ok(None);\n    };\n\n    // 2. Let offset be O.[[ByteOffset]].\n    let offset = inner.byte_offset();\n\n    // 3. Let elementSize be TypedArrayElementSize(O).\n    let size = inner.kind.element_size();\n\n    // 4. Let byteIndexInBuffer be (ℝ(index) × elementSize) + offset.\n    let byte_index = ((index * size) + offset) as usize;\n\n    // 5. Let elementType be TypedArrayElementType(O).\n    let elem_type = inner.kind();\n\n    // 6. Return GetValueFromBuffer(O.[[ViewedArrayBuffer]], byteIndexInBuffer, elementType, true, unordered).\n    // SAFETY: The TypedArray object guarantees that the buffer is aligned.\n    // The call to `is_valid_integer_index` guarantees that the index is in-bounds.\n    let value = unsafe {\n        buffer\n            .subslice(byte_index..)\n            .get_value(elem_type, Ordering::Relaxed)\n    };\n\n    Ok(Some(value.into()))\n}\n\n/// Abstract operation `TypedArraySetElement ( O, index, value )`.\n///\n/// More information:\n///  - [ECMAScript reference][spec]\n///\n/// [spec]: https://tc39.es/ecma262/#sec-typedarraysetelement\npub(crate) fn typed_array_set_element(\n    obj: &JsObject,\n    index: f64,\n    value: &JsValue,\n    context: &mut InternalMethodPropertyContext<'_>,\n) -> JsResult<()> {\n    let obj = obj\n        .clone()\n        .downcast::<TypedArray>()\n        .ok()\n        .js_expect(\"function can only be called for typed array objects\")?;\n    // b. Let arrayTypeName be the String value of O.[[TypedArrayName]].\n    // e. Let elementType be the Element Type value in Table 73 for arrayTypeName.\n    let elem_type = obj.borrow().data().kind();\n\n    // 1. If O.[[ContentType]] is BigInt, let numValue be ? ToBigInt(value).\n    // 2. Otherwise, let numValue be ? ToNumber(value).\n    let value = elem_type.get_element(value, context)?;\n\n    // 3. If IsValidIntegerIndex(O, index) is true, then\n    let array = obj.borrow();\n    let mut buffer = array.data().viewed_array_buffer().as_buffer_mut();\n    let Some(mut buffer) = buffer.bytes(Ordering::Relaxed) else {\n        return Ok(());\n    };\n    let Some(index) = array.data().validate_index(index, buffer.len()) else {\n        return Ok(());\n    };\n\n    //     a. Let offset be O.[[ByteOffset]].\n    let offset = array.data().byte_offset();\n\n    //     b. Let elementSize be TypedArrayElementSize(O).\n    let size = elem_type.element_size();\n\n    //     c. Let byteIndexInBuffer be (ℝ(index) × elementSize) + offset.\n    let byte_index = ((index * size) + offset) as usize;\n\n    //     e. Perform SetValueInBuffer(O.[[ViewedArrayBuffer]], byteIndexInBuffer, elementType, numValue, true, unordered).\n    // SAFETY: The TypedArray object guarantees that the buffer is aligned.\n    // The call to `validate_index` guarantees that the index is in-bounds.\n    unsafe {\n        buffer\n            .subslice_mut(byte_index..)\n            .set_value(value, Ordering::Relaxed);\n    }\n\n    // 4. Return unused.\n    Ok(())\n}\n"
  },
  {
    "path": "core/engine/src/builtins/typed_array/tests.rs",
    "content": "use crate::{JsNativeErrorKind, TestAction, run_test_actions};\n\n#[test]\nfn uint8array_constructor_length() {\n    run_test_actions([\n        TestAction::run(\"let a = new Uint8Array(4);\"),\n        TestAction::assert_eq(\"a.length\", 4),\n        TestAction::assert_eq(\"a.byteLength\", 4),\n    ]);\n}\n\n#[test]\nfn uint8array_constructor_from_array() {\n    run_test_actions([\n        TestAction::run(\"let a = new Uint8Array([1, 2, 3]);\"),\n        TestAction::assert_eq(\"a.length\", 3),\n        TestAction::assert_eq(\"a[1]\", 2),\n    ]);\n}\n\n#[test]\nfn uint8array_constructor_from_array_buffer() {\n    run_test_actions([\n        TestAction::run(\"let buffer = new ArrayBuffer(4); let a = new Uint8Array(buffer);\"),\n        TestAction::assert_eq(\"a.length\", 4),\n    ]);\n}\n\n#[test]\nfn uint8array_read_write_semantics() {\n    run_test_actions([\n        TestAction::run(\"let a = new Uint8Array(2); a[0] = 42;\"),\n        TestAction::assert_eq(\"a[0]\", 42),\n    ]);\n}\n\n#[test]\nfn uint8array_out_of_bounds_behavior() {\n    run_test_actions([\n        TestAction::run(\"let a = new Uint8Array(2);\"),\n        TestAction::assert(\"a[10] === undefined\"),\n        TestAction::run(\"a[10] = 5;\"),\n        TestAction::assert_eq(\"a.length\", 2),\n    ]);\n}\n\n#[test]\nfn uint8array_numeric_coercion() {\n    run_test_actions([\n        TestAction::run(\"let a = new Uint8Array(1); a[0] = 256;\"),\n        TestAction::assert_eq(\"a[0]\", 0),\n        TestAction::run(\"a[0] = -1;\"),\n        TestAction::assert_eq(\"a[0]\", 255),\n    ]);\n}\n\n#[test]\nfn float32array_storage() {\n    run_test_actions([\n        TestAction::run(\"let f = new Float32Array(1); f[0] = 1.5;\"),\n        TestAction::assert_eq(\"f[0]\", 1.5),\n    ]);\n}\n\n#[test]\nfn float32array_nan_behavior() {\n    run_test_actions([\n        TestAction::run(\"let f = new Float32Array(1); f[0] = 'hello';\"),\n        TestAction::assert(\"Number.isNaN(f[0])\"),\n    ]);\n}\n\n#[test]\nfn float32_precision_behavior() {\n    run_test_actions([\n        TestAction::run(\"let f = new Float32Array(1); f[0] = 1.337;\"),\n        TestAction::assert(\"Math.fround(1.337) === f[0]\"),\n    ]);\n}\n\n#[test]\nfn typedarray_prototype_set() {\n    run_test_actions([\n        TestAction::run(\"let a = new Uint8Array(4); a.set([1, 2], 1);\"),\n        TestAction::assert_eq(\"a[1]\", 1),\n        TestAction::assert_eq(\"a[2]\", 2),\n    ]);\n}\n\n#[test]\nfn typedarray_prototype_fill() {\n    run_test_actions([\n        TestAction::run(\"let a = new Uint8Array(3); a.fill(7);\"),\n        TestAction::assert_eq(\"a[2]\", 7),\n    ]);\n}\n\n#[test]\nfn typedarray_prototype_subarray_shared_memory() {\n    run_test_actions([\n        TestAction::run(\n            \"\n        let a = new Uint8Array([1, 2, 3, 4]);\n        let b = a.subarray(1, 3);\n        b[0] = 99;\n        \",\n        ),\n        TestAction::assert_eq(\"a[1]\", 99),\n        TestAction::assert_eq(\"b[0]\", 99),\n    ]);\n}\n\n#[test]\nfn typedarray_conversion_number() {\n    run_test_actions([\n        TestAction::run(\"let a = new Int8Array([1, -1, 127]);\"),\n        TestAction::run(\"let b = new Float64Array(a);\"),\n        TestAction::assert_eq(\"b.length\", 3),\n        TestAction::assert_eq(\"b[0]\", 1),\n        TestAction::assert_eq(\"b[1]\", -1),\n        TestAction::assert_eq(\"b[2]\", 127),\n    ]);\n}\n\n#[test]\nfn typedarray_conversion_bigint() {\n    run_test_actions([\n        TestAction::run(\"let a = new BigInt64Array([1n, -1n]);\"),\n        TestAction::run(\"let b = new BigUint64Array(a);\"),\n        TestAction::assert_eq(\"b.length\", 2),\n        TestAction::assert(\"b[0] === 1n\"),\n        TestAction::assert(\"b[1] === 0xffffffffffffffffn\"),\n    ]);\n}\n\n#[test]\nfn typedarray_conversion_clamped() {\n    run_test_actions([\n        TestAction::run(\"let a = new Float64Array([255.5, 256.1, -0.5]);\"),\n        TestAction::run(\"let b = new Uint8ClampedArray(a);\"),\n        TestAction::assert_eq(\"b[0]\", 255),\n        TestAction::assert_eq(\"b[1]\", 255),\n        TestAction::assert_eq(\"b[2]\", 0),\n    ]);\n}\n\n#[test]\nfn typedarray_conversion_mismatch_throws() {\n    run_test_actions([\n        TestAction::run(\"let a = new Int8Array([1]);\"),\n        TestAction::assert_native_error(\n            \"new BigInt64Array(a)\",\n            JsNativeErrorKind::Type,\n            \"Cannot initialize typed array from different content type\",\n        ),\n        TestAction::run(\"let b = new BigInt64Array([1n]);\"),\n        TestAction::assert_native_error(\n            \"new Int8Array(b)\",\n            JsNativeErrorKind::Type,\n            \"Cannot initialize typed array from different content type\",\n        ),\n    ]);\n}\n"
  },
  {
    "path": "core/engine/src/builtins/uri/consts.rs",
    "content": "//! URI handling function constants\n//!\n//! This module contains a few constants used to handle decoding and encoding for URI handling\n//! functions. They make it easier and more performant to compare different ranges and code points.\n\nuse std::ops::RangeInclusive;\n\n/// A range containing all the lowercase `uriAlpha` code points.\n///\n/// More information:\n///  - [ECMAScript reference][spec]\n///\n/// [spec]: https://tc39.es/ecma262/#prod-uriAlpha\nconst URI_ALPHA_LOWER: RangeInclusive<u16> = b'a' as u16..=b'z' as u16;\n\n/// A range containing all the uppercase `uriAlpha` code points.\n///\n/// More information:\n///  - [ECMAScript reference][spec]\n///\n/// [spec]: https://tc39.es/ecma262/#prod-uriAlpha\nconst URI_ALPHA_UPPER: RangeInclusive<u16> = b'A' as u16..=b'Z' as u16;\n\n/// A range containing all the `DecimalDigit` code points.\n///\n/// More information:\n///  - [ECMAScript reference][spec]\n///\n/// [spec]: https://tc39.es/ecma262/#prod-DecimalDigit\nconst DECIMAL_DIGIT: RangeInclusive<u16> = b'0' as u16..=b'9' as u16;\n\n/// An array containing all the `uriMark` code points.\n///\n/// More information:\n///  - [ECMAScript reference][spec]\n///\n/// [spec]: https://tc39.es/ecma262/#prod-uriMark\nconst URI_MARK: [u16; 9] = [\n    b'-' as u16,\n    b'_' as u16,\n    b'.' as u16,\n    b'!' as u16,\n    b'~' as u16,\n    b'*' as u16,\n    b'\\'' as u16,\n    b'(' as u16,\n    b')' as u16,\n];\n\n/// An array containing all the `uriReserved` code points.\n///\n/// More information:\n///  - [ECMAScript reference][spec]\n///\n/// [spec]: https://tc39.es/ecma262/#prod-uriReserved\nconst URI_RESERVED: [u16; 10] = [\n    b';' as u16,\n    b'/' as u16,\n    b'?' as u16,\n    b':' as u16,\n    b'@' as u16,\n    b'&' as u16,\n    b'=' as u16,\n    b'+' as u16,\n    b'$' as u16,\n    b',' as u16,\n];\n\n/// The number sign (`#`) symbol as a UTF-16 code potint.\nconst NUMBER_SIGN: u16 = b'#' as u16;\n\n/// Constant with all the unescaped URI characters.\n///\n/// Contains `uriAlpha`, `DecimalDigit` and `uriMark`.\n///\n/// More information:\n///  - [ECMAScript reference][spec]\n///\n/// [spec]: https://tc39.es/ecma262/#prod-uriUnescaped\npub(super) fn is_uri_unescaped(code_point: u16) -> bool {\n    URI_ALPHA_LOWER.contains(&code_point)\n        || URI_ALPHA_UPPER.contains(&code_point)\n        || DECIMAL_DIGIT.contains(&code_point)\n        || URI_MARK.contains(&code_point)\n}\n\n/// Constant with all the reserved URI characters, plus the number sign symbol (`#`).\n///\n/// More information:\n///  - [ECMAScript reference][spec]\n///\n/// [spec]: https://tc39.es/ecma262/#prod-uriReserved\npub(super) fn is_uri_reserved_or_number_sign(code_point: u16) -> bool {\n    code_point == NUMBER_SIGN || URI_RESERVED.contains(&code_point)\n}\n\n/// Constant with all the reserved and unescaped URI characters, plus the number sign symbol (`#`).\n///\n/// More information:\n///  - [`uriReserved` in ECMAScript spec][uri_reserved]\n///  - [`uriUnescaped` in ECMAScript spec][uri_unescaped]\n///\n/// [uri_reserved]: https://tc39.es/ecma262/#prod-uriReserved\n/// [uri_unescaped]: https://tc39.es/ecma262/#prod-uriUnescaped\npub(super) fn is_uri_reserved_or_uri_unescaped_or_number_sign(code_point: u16) -> bool {\n    code_point == NUMBER_SIGN || is_uri_unescaped(code_point) || URI_RESERVED.contains(&code_point)\n}\n"
  },
  {
    "path": "core/engine/src/builtins/uri/mod.rs",
    "content": "//! Boa's implementation of ECMAScript's URI Handling Functions.\n//!\n//! Uniform Resource Identifiers, or URIs, are Strings that identify resources (e.g. web pages or\n//! files) and transport protocols by which to access them (e.g. HTTP or FTP) on the Internet. The\n//! ECMAScript language itself does not provide any support for using URIs except for functions\n//! that encode and decode URIs as described in 19.2.6.2, 19.2.6.3, 19.2.6.4 and 19.2.6.5\n//!\n//! More information:\n//!  - [ECMAScript reference][spec]\n//!\n//! [spec]: https://tc39.es/ecma262/#sec-uri-handling-functions\n\nmod consts;\n\nuse boa_gc::{Finalize, Trace};\n\nuse self::consts::{\n    is_uri_reserved_or_number_sign, is_uri_reserved_or_uri_unescaped_or_number_sign,\n    is_uri_unescaped,\n};\n\nuse super::{BuiltInBuilder, BuiltInObject, IntrinsicObject};\nuse crate::{\n    Context, JsArgs, JsExpect, JsNativeError, JsResult, JsString, JsValue,\n    context::intrinsics::Intrinsics,\n    js_string,\n    object::{JsFunction, JsObject},\n    realm::Realm,\n    string::{CodePoint, StaticJsStrings},\n};\n\n/// Intrinsics for the [`URI Handling Functions`][spec].\n///\n/// [spec]: https://tc39.es/ecma262/multipage/global-object.html#sec-uri-handling-functions\n#[derive(Debug, Trace, Finalize)]\npub struct UriFunctions {\n    /// %decodeURI%\n    decode_uri: JsFunction,\n\n    /// %decodeURI%\n    decode_uri_component: JsFunction,\n\n    /// %encodeURI%\n    encode_uri: JsFunction,\n\n    /// %encodeURIcomponent%\n    encode_uri_component: JsFunction,\n}\n\nimpl Default for UriFunctions {\n    fn default() -> Self {\n        Self {\n            decode_uri: JsFunction::empty_intrinsic_function(false),\n            decode_uri_component: JsFunction::empty_intrinsic_function(false),\n            encode_uri: JsFunction::empty_intrinsic_function(false),\n            encode_uri_component: JsFunction::empty_intrinsic_function(false),\n        }\n    }\n}\n\nimpl UriFunctions {\n    pub(crate) fn decode_uri(&self) -> JsFunction {\n        self.decode_uri.clone()\n    }\n\n    pub(crate) fn decode_uri_component(&self) -> JsFunction {\n        self.decode_uri_component.clone()\n    }\n\n    pub(crate) fn encode_uri(&self) -> JsFunction {\n        self.encode_uri.clone()\n    }\n\n    pub(crate) fn encode_uri_component(&self) -> JsFunction {\n        self.encode_uri_component.clone()\n    }\n}\n\n/// URI Handling Functions\n#[derive(Debug, Clone, Copy)]\npub(crate) struct DecodeUri;\n\nimpl IntrinsicObject for DecodeUri {\n    fn init(realm: &Realm) {\n        BuiltInBuilder::callable_with_intrinsic::<Self>(realm, decode_uri)\n            .name(Self::NAME)\n            .length(1)\n            .build();\n    }\n    fn get(intrinsics: &Intrinsics) -> JsObject {\n        intrinsics.objects().uri_functions().decode_uri().into()\n    }\n}\n\nimpl BuiltInObject for DecodeUri {\n    const NAME: JsString = StaticJsStrings::DECODE_URI;\n}\n\npub(crate) struct DecodeUriComponent;\n\nimpl IntrinsicObject for DecodeUriComponent {\n    fn init(realm: &Realm) {\n        BuiltInBuilder::callable_with_intrinsic::<Self>(realm, decode_uri_component)\n            .name(Self::NAME)\n            .length(1)\n            .build();\n    }\n    fn get(intrinsics: &Intrinsics) -> JsObject {\n        intrinsics\n            .objects()\n            .uri_functions()\n            .decode_uri_component()\n            .into()\n    }\n}\n\nimpl BuiltInObject for DecodeUriComponent {\n    const NAME: JsString = StaticJsStrings::DECODE_URI_COMPONENT;\n}\n\npub(crate) struct EncodeUri;\n\nimpl IntrinsicObject for EncodeUri {\n    fn init(realm: &Realm) {\n        BuiltInBuilder::callable_with_intrinsic::<Self>(realm, encode_uri)\n            .name(Self::NAME)\n            .length(1)\n            .build();\n    }\n    fn get(intrinsics: &Intrinsics) -> JsObject {\n        intrinsics.objects().uri_functions().encode_uri().into()\n    }\n}\n\nimpl BuiltInObject for EncodeUri {\n    const NAME: JsString = StaticJsStrings::ENCODE_URI;\n}\npub(crate) struct EncodeUriComponent;\n\nimpl IntrinsicObject for EncodeUriComponent {\n    fn init(realm: &Realm) {\n        BuiltInBuilder::callable_with_intrinsic::<Self>(realm, encode_uri_component)\n            .name(Self::NAME)\n            .length(1)\n            .build();\n    }\n    fn get(intrinsics: &Intrinsics) -> JsObject {\n        intrinsics\n            .objects()\n            .uri_functions()\n            .encode_uri_component()\n            .into()\n    }\n}\n\nimpl BuiltInObject for EncodeUriComponent {\n    const NAME: JsString = StaticJsStrings::ENCODE_URI_COMPONENT;\n}\n\n/// Builtin JavaScript `decodeURI ( encodedURI )` function.\n///\n/// This function computes a new version of a URI in which each escape sequence and UTF-8\n/// encoding of the sort that might be introduced by the `encodeURI` function is replaced with\n/// the UTF-16 encoding of the code points that it represents. Escape sequences that could not\n/// have been introduced by `encodeURI` are not replaced.\n///\n/// More information:\n///  - [ECMAScript reference][spec]\n///  - [MDN documentation][mdn]\n///\n/// [spec]: https://tc39.es/ecma262/#sec-decodeuri-encodeduri\n/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/decodeURI\npub(crate) fn decode_uri(\n    _: &JsValue,\n    args: &[JsValue],\n    context: &mut Context,\n) -> JsResult<JsValue> {\n    let encoded_uri = args.get_or_undefined(0);\n\n    // 1. Let uriString be ? ToString(encodedURI).\n    let uri_string = encoded_uri.to_string(context)?;\n\n    // 2. Let reservedURISet be a String containing one instance of each code unit valid in uriReserved plus \"#\".\n    let reserved_uri_set = is_uri_reserved_or_number_sign;\n\n    // 3. Return ? Decode(uriString, reservedURISet).\n    Ok(JsValue::from(decode(&uri_string, reserved_uri_set)?))\n}\n/// Builtin JavaScript `decodeURIComponent ( encodedURIComponent )` function.\n///\n/// This function computes a new version of a URI in which each escape sequence and UTF-8\n/// encoding of the sort that might be introduced by the `encodeURIComponent` function is\n/// replaced with the UTF-16 encoding of the code points that it represents.\n///\n/// More information:\n///  - [ECMAScript reference][spec]\n///  - [MDN documentation][mdn]\n///\n/// [spec]: https://tc39.es/ecma262/#sec-decodeuricomponent-encodeduricomponent\n/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/decodeURIComponent\npub(crate) fn decode_uri_component(\n    _: &JsValue,\n    args: &[JsValue],\n    context: &mut Context,\n) -> JsResult<JsValue> {\n    let encoded_uri_component = args.get_or_undefined(0);\n\n    // 1. Let componentString be ? ToString(encodedURIComponent).\n    let component_string = encoded_uri_component.to_string(context)?;\n\n    // 2. Let reservedURIComponentSet be the empty String.\n    let reserved_uri_component_set = |_: u16| false;\n\n    // 3. Return ? Decode(componentString, reservedURIComponentSet).\n    Ok(JsValue::from(decode(\n        &component_string,\n        reserved_uri_component_set,\n    )?))\n}\n\n/// Builtin JavaScript `encodeURI ( uri )` function.\n///\n/// This function computes a new version of a UTF-16 encoded (6.1.4) URI in which each instance\n/// of certain code points is replaced by one, two, three, or four escape sequences\n/// representing the UTF-8 encoding of the code points.\n///\n/// More information:\n///  - [ECMAScript reference][spec]\n///  - [MDN documentation][mdn]\n///\n/// [spec]: https://tc39.es/ecma262/#sec-encodeuri-uri\n/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/encodeURI\npub(crate) fn encode_uri(\n    _: &JsValue,\n    args: &[JsValue],\n    context: &mut Context,\n) -> JsResult<JsValue> {\n    let uri = args.get_or_undefined(0);\n\n    // 1. Let uriString be ? ToString(uri).\n    let uri_string = uri.to_string(context)?;\n\n    // 2. Let unescapedURISet be a String containing one instance of each code unit valid in uriReserved and uriUnescaped plus \"#\".\n    let unescaped_uri_set = is_uri_reserved_or_uri_unescaped_or_number_sign;\n\n    // 3. Return ? Encode(uriString, unescapedURISet).\n    Ok(JsValue::from(encode(&uri_string, unescaped_uri_set)?))\n}\n\n/// Builtin JavaScript `encodeURIComponent ( uriComponent )` function.\n///\n/// This function computes a new version of a UTF-16 encoded (6.1.4) URI in which each instance\n/// of certain code points is replaced by one, two, three, or four escape sequences\n/// representing the UTF-8 encoding of the code point.\n///\n/// More information:\n///  - [ECMAScript reference][spec]\n///  - [MDN documentation][mdn]\n///\n/// [spec]: https://tc39.es/ecma262/#sec-encodeuricomponent-uricomponent\n/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/encodeURIComponent\npub(crate) fn encode_uri_component(\n    _: &JsValue,\n    args: &[JsValue],\n    context: &mut Context,\n) -> JsResult<JsValue> {\n    let uri_component = args.get_or_undefined(0);\n\n    // 1. Let componentString be ? ToString(uriComponent).\n    let component_string = uri_component.to_string(context)?;\n\n    // 2. Let unescapedURIComponentSet be a String containing one instance of each code unit valid in uriUnescaped.\n    let unescaped_uri_component_set = is_uri_unescaped;\n\n    // 3. Return ? Encode(componentString, unescapedURIComponentSet).\n    Ok(JsValue::from(encode(\n        &component_string,\n        unescaped_uri_component_set,\n    )?))\n}\n\n/// The `Encode ( string, unescapedSet )` abstract operation\n///\n/// The abstract operation Encode takes arguments `string` (a String) and `unescapedSet` (a String)\n/// and returns either a normal completion containing a String or a throw completion. It performs\n/// URI encoding and escaping.\n///\n/// More information:\n///  - [ECMAScript reference][spec]\n///\n/// [spec]: https://tc39.es/ecma262/#sec-encode\nfn encode<F>(string: &JsString, unescaped_set: F) -> JsResult<JsString>\nwhere\n    F: Fn(u16) -> bool,\n{\n    // 1. Let strLen be the length of string.\n    let str_len = string.len();\n\n    // 2. Let R be the empty String.\n    let mut r = Vec::with_capacity(str_len);\n\n    // 3. Let k be 0.\n    let mut k = 0;\n    // 4. Repeat,\n    loop {\n        // a. If k = strLen, return R.\n        if k == str_len {\n            return Ok(js_string!(&r[..]));\n        }\n\n        // b. Let C be the code unit at index k within string.\n        let c = string.code_unit_at(k).js_expect(\"Bounds were verified\")?;\n\n        // c. If C is in unescapedSet, then\n        if unescaped_set(c) {\n            // i. Set k to k + 1.\n            k += 1;\n\n            // ii. Set R to the string-concatenation of R and C.\n            r.push(c);\n        } else {\n            // d. Else,\n            // i. Let cp be CodePointAt(string, k).\n            let cp = string.code_point_at(k);\n\n            // ii. If cp.[[IsUnpairedSurrogate]] is true, throw a URIError exception.\n            let CodePoint::Unicode(ch) = cp else {\n                return Err(JsNativeError::uri()\n                    .with_message(\"trying to encode an invalid string\")\n                    .into());\n            };\n\n            // iii. Set k to k + cp.[[CodeUnitCount]].\n            k += cp.code_unit_count();\n\n            // iv. Let Octets be the List of octets resulting by applying the UTF-8 transformation\n            //     to cp.[[CodePoint]].\n            let mut buff = [0_u8; 4]; // Will never be more than 4 bytes\n\n            let octets = ch.encode_utf8(&mut buff);\n\n            // v. For each element octet of Octets, do\n            for octet in octets.bytes() {\n                // 1. Set R to the string-concatenation of:\n                //    R\n                //    \"%\"\n                //    the String representation of octet, formatted as a two-digit uppercase\n                //    hexadecimal number, padded to the left with a zero if necessary\n                r.extend(format!(\"%{octet:0>2X}\").encode_utf16());\n            }\n        }\n    }\n}\n\n/// The `Decode ( string, reservedSet )` abstract operation.\n///\n/// The abstract operation Decode takes arguments `string` (a String) and `reservedSet` (a String)\n/// and returns either a normal completion containing a String or a throw completion. It performs\n/// URI unescaping and decoding.\n///\n/// More information:\n///  - [ECMAScript reference][spec]\n///\n/// [spec]: https://tc39.es/ecma262/#sec-decode\n#[allow(clippy::many_single_char_names)]\nfn decode<F>(string: &JsString, reserved_set: F) -> JsResult<JsString>\nwhere\n    F: Fn(u16) -> bool,\n{\n    // 1. Let strLen be the length of string.\n    let str_len = string.len();\n    // 2. Let R be the empty String.\n    let mut r = Vec::with_capacity(str_len);\n\n    let mut octets = Vec::with_capacity(4);\n\n    // 3. Let k be 0.\n    let mut k = 0;\n    // 4. Repeat,\n    loop {\n        // a. If k = strLen, return R.\n        // b. Let C be the code unit at index k within string.\n        let Some(c) = string.code_unit_at(k) else {\n            return Ok(js_string!(&r[..]));\n        };\n\n        // c. If C is not the code unit 0x0025 (PERCENT SIGN), then\n        #[allow(clippy::if_not_else)]\n        let s = if c != 0x0025_u16 {\n            // i. Let S be the String value containing only the code unit C.\n            Vec::from([c])\n        } else {\n            // d. Else,\n            // i. Let start be k.\n            let start = k;\n\n            // ii. If k + 2 ≥ strLen, throw a URIError exception.\n            if k + 2 >= str_len {\n                return Err(JsNativeError::uri()\n                    .with_message(\"invalid escape character found\")\n                    .into());\n            }\n\n            // iii. If the code units at index (k + 1) and (k + 2) within string do not represent\n            // hexadecimal digits, throw a URIError exception.\n            // iv. Let B be the 8-bit value represented by the two hexadecimal digits at index (k + 1) and (k + 2).\n\n            // SAFETY: the indices have been verified as valid already.\n            let (high, low) = unsafe {\n                (\n                    string.code_unit_at(k + 1).unwrap_unchecked(),\n                    string.code_unit_at(k + 2).unwrap_unchecked(),\n                )\n            };\n            let b = decode_hex_byte(high, low).ok_or_else(|| {\n                JsNativeError::uri().with_message(\"invalid hexadecimal digit found\")\n            })?;\n\n            // v. Set k to k + 2.\n            k += 2;\n\n            // vi. Let n be the number of leading 1 bits in B.\n            let n = b.leading_ones() as usize;\n\n            // vii. If n = 0, then\n            if n == 0 {\n                // 1. Let C be the code unit whose value is B.\n                let c = u16::from(b);\n\n                // 2. If C is not in reservedSet, then\n                if !reserved_set(c) {\n                    // a. Let S be the String value containing only the code unit C.\n                    Vec::from([c])\n                } else {\n                    // 3. Else,\n                    // a. Let S be the substring of string from start to k + 1.\n                    string.get_expect(start..=k).to_vec()\n                }\n            } else {\n                // viii. Else,\n                // 1. If n = 1 or n > 4, throw a URIError exception.\n                if n == 1 || n > 4 {\n                    return Err(JsNativeError::uri()\n                        .with_message(\"invalid escaped character found\")\n                        .into());\n                }\n\n                // 2. If k + (3 × (n - 1)) ≥ strLen, throw a URIError exception.\n                if k + (3 * (n - 1)) >= str_len {\n                    return Err(JsNativeError::uri()\n                        .with_message(\"non-terminated escape character found\")\n                        .into());\n                }\n\n                // 3. Let Octets be « B ».\n                octets.push(b);\n\n                // 4. Let j be 1.\n                // 5. Repeat, while j < n,\n                for _j in 1..n {\n                    // a. Set k to k + 1.\n                    k += 1;\n\n                    // b. If the code unit at index k within string is not the code unit 0x0025 (PERCENT SIGN), throw a URIError exception.\n                    if string.code_unit_at(k) != Some(0x0025) {\n                        return Err(JsNativeError::uri()\n                            .with_message(\"escape characters must be preceded with a % sign\")\n                            .into());\n                    }\n\n                    // c. If the code units at index (k + 1) and (k + 2) within string do not represent hexadecimal digits, throw a URIError exception.\n                    // d. Let B be the 8-bit value represented by the two hexadecimal digits at index (k + 1) and (k + 2).\n                    // SAFETY: the indices have been verified as valid already.\n                    let (high, low) = unsafe {\n                        (\n                            string.code_unit_at(k + 1).unwrap_unchecked(),\n                            string.code_unit_at(k + 2).unwrap_unchecked(),\n                        )\n                    };\n                    let b = decode_hex_byte(high, low).ok_or_else(|| {\n                        JsNativeError::uri().with_message(\"invalid hexadecimal digit found\")\n                    })?;\n\n                    // e. Set k to k + 2.\n                    k += 2;\n\n                    // f. Append B to Octets.\n                    octets.push(b);\n\n                    // g. Set j to j + 1.\n                }\n\n                // 6. Assert: The length of Octets is n.\n                assert_eq!(octets.len(), n);\n\n                // 7. If Octets does not contain a valid UTF-8 encoding of a Unicode code point, throw a URIError exception.\n                match std::str::from_utf8(&octets) {\n                    Err(_) => {\n                        return Err(JsNativeError::uri()\n                            .with_message(\"invalid UTF-8 encoding found\")\n                            .into());\n                    }\n                    Ok(v) => {\n                        // 8. Let V be the code point obtained by applying the UTF-8 transformation to Octets, that is, from a List of octets into a 21-bit value.\n\n                        // 9. Let S be UTF16EncodeCodePoint(V).\n                        // utf16_encode_codepoint(v)\n                        let s = v.encode_utf16().collect::<Vec<_>>();\n                        octets.clear();\n                        s\n                    }\n                }\n            }\n        };\n\n        // e. Set R to the string-concatenation of R and S.\n        r.extend_from_slice(&s);\n\n        // f. Set k to k + 1.\n        k += 1;\n    }\n}\n\n/// Decodes a byte from two unicode code units.\nfn decode_hex_byte(high: u16, low: u16) -> Option<u8> {\n    match (\n        char::from_u32(u32::from(high)),\n        char::from_u32(u32::from(low)),\n    ) {\n        (Some(high), Some(low)) => match (high.to_digit(16), low.to_digit(16)) {\n            (Some(high), Some(low)) => Some(((high as u8) << 4) + low as u8),\n            _ => None,\n        },\n        _ => None,\n    }\n}\n\n#[cfg(test)]\nmod tests {\n    use super::*;\n    use crate::JsNativeErrorKind;\n\n    /// Checks that the `decode_byte()` function works as expected.\n    #[test]\n    fn decode_byte() {\n        // Sunny day tests\n        assert_eq!(\n            decode_hex_byte(u16::from(b'2'), u16::from(b'0')).unwrap(),\n            0x20\n        );\n        assert_eq!(\n            decode_hex_byte(u16::from(b'2'), u16::from(b'A')).unwrap(),\n            0x2A\n        );\n        assert_eq!(\n            decode_hex_byte(u16::from(b'3'), u16::from(b'C')).unwrap(),\n            0x3C\n        );\n        assert_eq!(\n            decode_hex_byte(u16::from(b'4'), u16::from(b'0')).unwrap(),\n            0x40\n        );\n        assert_eq!(\n            decode_hex_byte(u16::from(b'7'), u16::from(b'E')).unwrap(),\n            0x7E\n        );\n        assert_eq!(\n            decode_hex_byte(u16::from(b'0'), u16::from(b'0')).unwrap(),\n            0x00\n        );\n\n        // Rainy day tests\n        assert!(decode_hex_byte(u16::from(b'-'), u16::from(b'0')).is_none());\n        assert!(decode_hex_byte(u16::from(b'f'), u16::from(b'~')).is_none());\n        assert!(decode_hex_byte(u16::from(b'A'), 0_u16).is_none());\n        assert!(decode_hex_byte(u16::from(b'%'), u16::from(b'&')).is_none());\n\n        assert!(decode_hex_byte(0xFACD_u16, u16::from(b'-')).is_none());\n        assert!(decode_hex_byte(u16::from(b'-'), 0xA0FD_u16).is_none());\n    }\n\n    #[test]\n    fn decode_uri_reports_uri_error_on_incomplete_sequence() {\n        // \"%E7%9A%8\" is an incomplete 3-byte UTF-8 sequence and must error with Uri kind\n        let s = js_string!(\"%E7%9A%8\");\n        let err = decode(&s, |_| false).expect_err(\"should error on incomplete escape\");\n        let native = err.as_native().expect(\"error should be native\");\n        assert!(matches!(native.kind(), JsNativeErrorKind::Uri));\n    }\n\n    #[test]\n    fn decode_preserves_non_bmp_characters() {\n        let s = js_string!(\"\\u{1F600}\");\n        let decoded = decode(&s, |_| false).expect(\"decode should succeed\");\n        assert_eq!(decoded, s);\n    }\n\n    #[test]\n    fn decode_preserves_non_bmp_characters_for_decode_uri_set() {\n        let s = js_string!(\"\\u{1F600}\");\n        let decoded = decode(&s, is_uri_reserved_or_number_sign).expect(\"decode should succeed\");\n        assert_eq!(decoded, s);\n    }\n}\n"
  },
  {
    "path": "core/engine/src/builtins/weak/mod.rs",
    "content": "//! Boa's implementation of ECMAScript's `WeakRef` object.\n\nmod weak_ref;\n\npub(crate) use weak_ref::WeakRef;\n"
  },
  {
    "path": "core/engine/src/builtins/weak/weak_ref.rs",
    "content": "use boa_gc::{Finalize, Trace, WeakGc};\n\nuse crate::{\n    Context, JsArgs, JsNativeError, JsResult, JsString, JsValue,\n    builtins::{BuiltInBuilder, BuiltInConstructor, BuiltInObject, IntrinsicObject},\n    context::intrinsics::{Intrinsics, StandardConstructor, StandardConstructors},\n    js_string,\n    object::{ErasedVTableObject, JsObject, internal_methods::get_prototype_from_constructor},\n    property::Attribute,\n    realm::Realm,\n    string::StaticJsStrings,\n    symbol::JsSymbol,\n};\n\n/// Boa's implementation of ECMAScript's `WeakRef` builtin object.\n///\n/// The `WeakRef` is a way to refer to a target object without rooting the target and thus preserving it in garbage\n/// collection. A `WeakRef` will allow the user to dereference the target as long as the target object has not been\n/// collected by the garbage collector.\n///\n/// More Information:\n///  - [ECMAScript Reference][spec]\n///\n/// [spec]: https://tc39.es/ecma262/#sec-weak-ref-objects\n#[derive(Debug, Clone, Trace, Finalize)]\npub(crate) struct WeakRef;\n\nimpl IntrinsicObject for WeakRef {\n    fn get(intrinsics: &Intrinsics) -> JsObject {\n        Self::STANDARD_CONSTRUCTOR(intrinsics.constructors()).constructor()\n    }\n\n    fn init(realm: &Realm) {\n        BuiltInBuilder::from_standard_constructor::<Self>(realm)\n            .property(\n                JsSymbol::to_string_tag(),\n                js_string!(\"WeakRef\"),\n                Attribute::CONFIGURABLE,\n            )\n            .method(Self::deref, js_string!(\"deref\"), 0)\n            .build();\n    }\n}\n\nimpl BuiltInObject for WeakRef {\n    const NAME: JsString = StaticJsStrings::WEAK_REF;\n\n    const ATTRIBUTE: Attribute = Attribute::WRITABLE.union(Attribute::CONFIGURABLE);\n}\n\nimpl BuiltInConstructor for WeakRef {\n    /// The amount of arguments the `WeakRef` constructor takes.\n    const CONSTRUCTOR_ARGUMENTS: usize = 1;\n    const PROTOTYPE_STORAGE_SLOTS: usize = 2;\n    const CONSTRUCTOR_STORAGE_SLOTS: usize = 0;\n\n    const STANDARD_CONSTRUCTOR: fn(&StandardConstructors) -> &StandardConstructor =\n        StandardConstructors::weak_ref;\n\n    /// Constructor [`WeakRef ( target )`][cons]\n    ///\n    /// [cons]: https://tc39.es/ecma262/#sec-weak-ref-target\n    fn constructor(\n        new_target: &JsValue,\n        args: &[JsValue],\n        context: &mut Context,\n    ) -> JsResult<JsValue> {\n        // If NewTarget is undefined, throw a TypeError exception.\n        if new_target.is_undefined() {\n            return Err(JsNativeError::typ()\n                .with_message(\"WeakRef: cannot call constructor without `new`\")\n                .into());\n        }\n\n        // 2. If target is not an Object, throw a TypeError exception.\n        let target = args.first().and_then(JsValue::as_object).ok_or_else(|| {\n            JsNativeError::typ().with_message(format!(\n                \"WeakRef: expected target argument of type `object`, got target of type `{}`\",\n                args.get_or_undefined(0).type_of()\n            ))\n        })?;\n\n        // 3. Let weakRef be ? OrdinaryCreateFromConstructor(NewTarget, \"%WeakRef.prototype%\", « [[WeakRefTarget]] »).\n        // 5. Set weakRef.[[WeakRefTarget]] to target.\n        let prototype =\n            get_prototype_from_constructor(new_target, StandardConstructors::weak_ref, context)?;\n        let weak_ref = JsObject::from_proto_and_data_with_shared_shape(\n            context.root_shape(),\n            prototype,\n            WeakGc::new(target.inner()),\n        );\n\n        // 4. Perform AddToKeptObjects(target).\n        context.kept_alive.push(target.clone());\n\n        // 6. Return weakRef.\n        Ok(weak_ref.into())\n    }\n}\n\nimpl WeakRef {\n    /// Method [`WeakRef.prototype.deref ( )`][spec].\n    ///\n    /// If the referenced object hasn't been collected, this method promotes a `WeakRef` into a\n    /// proper [`JsObject`], or returns `undefined` otherwise.\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-weak-ref.prototype.deref\n    pub(crate) fn deref(this: &JsValue, _: &[JsValue], context: &mut Context) -> JsResult<JsValue> {\n        // 1. Let weakRef be the this value.\n        // 2. Perform ? RequireInternalSlot(weakRef, [[WeakRefTarget]]).\n        let object = this.as_object();\n        let weak_ref = object\n            .as_ref()\n            .and_then(JsObject::downcast_ref::<WeakGc<ErasedVTableObject>>)\n            .ok_or_else(|| {\n                JsNativeError::typ().with_message(\n                    \"WeakRef.prototype.deref: expected `this` to be a `WeakRef` object\",\n                )\n            })?;\n\n        // 3. Return WeakRefDeref(weakRef).\n\n        // `WeakRefDeref`\n        // https://tc39.es/ecma262/multipage/managing-memory.html#sec-weakrefderef\n        // 1. Let target be weakRef.[[WeakRefTarget]].\n        // 2. If target is not empty, then\n        if let Some(object) = weak_ref.upgrade() {\n            let object = JsObject::from(object);\n\n            // a. Perform AddToKeptObjects(target).\n            context.kept_alive.push(object.clone());\n\n            // b. Return target.\n            Ok(object.into())\n        } else {\n            // 3. Return undefined.\n            Ok(JsValue::undefined())\n        }\n    }\n}\n\n#[cfg(test)]\nmod tests {\n    use indoc::indoc;\n\n    use crate::{JsNativeErrorKind, JsValue, TestAction, run_test_actions};\n\n    #[test]\n    fn weak_ref_collected() {\n        run_test_actions([\n            TestAction::assert_with_op(\n                indoc! {r#\"\n                    var ptr;\n                    {\n                        let obj = {a: 5, b: 6};\n                        ptr = new WeakRef(obj);\n                    }\n                    ptr.deref()\n                \"#},\n                |v, _| v.is_object(),\n            ),\n            TestAction::inspect_context(|context| {\n                context.clear_kept_objects();\n                boa_gc::force_collect();\n            }),\n            TestAction::assert_eq(\"ptr.deref()\", JsValue::undefined()),\n        ]);\n    }\n\n    #[test]\n    fn weak_ref_no_new() {\n        run_test_actions([TestAction::assert_native_error(\n            \"WeakRef({})\",\n            JsNativeErrorKind::Type,\n            \"WeakRef: cannot call constructor without `new`\",\n        )]);\n    }\n\n    #[test]\n    fn weak_ref_primitive_target_rejection() {\n        run_test_actions([\n            TestAction::assert_native_error(\n                \"new WeakRef(42)\",\n                JsNativeErrorKind::Type,\n                \"WeakRef: expected target argument of type `object`, got target of type `number`\",\n            ),\n            TestAction::assert_native_error(\n                \"new WeakRef('str')\",\n                JsNativeErrorKind::Type,\n                \"WeakRef: expected target argument of type `object`, got target of type `string`\",\n            ),\n            TestAction::assert_native_error(\n                \"new WeakRef(true)\",\n                JsNativeErrorKind::Type,\n                \"WeakRef: expected target argument of type `object`, got target of type `boolean`\",\n            ),\n            TestAction::assert_native_error(\n                \"new WeakRef(null)\",\n                JsNativeErrorKind::Type,\n                \"WeakRef: expected target argument of type `object`, got target of type `object`\",\n            ),\n            TestAction::assert_native_error(\n                \"new WeakRef(undefined)\",\n                JsNativeErrorKind::Type,\n                \"WeakRef: expected target argument of type `object`, got target of type `undefined`\",\n            ),\n            TestAction::assert_native_error(\n                \"new WeakRef(Symbol())\",\n                JsNativeErrorKind::Type,\n                \"WeakRef: expected target argument of type `object`, got target of type `symbol`\",\n            ),\n        ]);\n    }\n\n    #[test]\n    fn weak_ref_deref_behavior() {\n        run_test_actions([\n            TestAction::run(indoc! {r#\"\n                var obj = { x: 1 };\n                var ref = new WeakRef(obj);\n            \"#}),\n            TestAction::assert(\"ref.deref() === obj\"),\n            TestAction::assert(\"ref.deref() === ref.deref()\"),\n        ]);\n    }\n\n    #[test]\n    fn weak_ref_deref_invalid_this() {\n        run_test_actions([TestAction::assert_native_error(\n            \"WeakRef.prototype.deref.call({})\",\n            JsNativeErrorKind::Type,\n            \"WeakRef.prototype.deref: expected `this` to be a `WeakRef` object\",\n        )]);\n    }\n\n    #[test]\n    fn weak_ref_length() {\n        run_test_actions([\n            TestAction::assert_eq(\"WeakRef.length\", 1),\n            TestAction::assert_eq(\"WeakRef.prototype.deref.length\", 0),\n        ]);\n    }\n}\n"
  },
  {
    "path": "core/engine/src/builtins/weak_map/mod.rs",
    "content": "//! Boa's implementation of ECMAScript's `WeakMap` builtin object.\n//!\n//! More information:\n//!  - [ECMAScript reference][spec]\n//!  - [MDN documentation][mdn]\n//!\n//! [spec]: https://tc39.es/ecma262/#sec-weakmap-objects\n//! [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/WeakMap\n\nuse crate::{\n    Context, JsArgs, JsResult, JsString, JsValue,\n    builtins::{\n        BuiltInBuilder, BuiltInConstructor, BuiltInObject, IntrinsicObject,\n        map::add_entries_from_iterable,\n    },\n    context::intrinsics::{Intrinsics, StandardConstructor, StandardConstructors},\n    js_error, js_string,\n    object::{ErasedVTableObject, JsObject, internal_methods::get_prototype_from_constructor},\n    property::Attribute,\n    realm::Realm,\n    string::StaticJsStrings,\n    symbol::JsSymbol,\n};\nuse boa_gc::{Finalize, Trace};\n\npub(crate) type NativeWeakMap = boa_gc::WeakMap<ErasedVTableObject, JsValue>;\n\n#[derive(Debug, Trace, Finalize)]\npub(crate) struct WeakMap;\n\n#[cfg(test)]\nmod tests;\n\nimpl IntrinsicObject for WeakMap {\n    fn get(intrinsics: &Intrinsics) -> JsObject {\n        Self::STANDARD_CONSTRUCTOR(intrinsics.constructors()).constructor()\n    }\n\n    fn init(realm: &Realm) {\n        BuiltInBuilder::from_standard_constructor::<Self>(realm)\n            .property(\n                JsSymbol::to_string_tag(),\n                Self::NAME,\n                Attribute::READONLY | Attribute::NON_ENUMERABLE | Attribute::CONFIGURABLE,\n            )\n            .method(Self::delete, js_string!(\"delete\"), 1)\n            .method(Self::get, js_string!(\"get\"), 1)\n            .method(Self::has, js_string!(\"has\"), 1)\n            .method(Self::set, js_string!(\"set\"), 2)\n            .method(Self::get_or_insert, js_string!(\"getOrInsert\"), 2)\n            .method(\n                Self::get_or_insert_computed,\n                js_string!(\"getOrInsertComputed\"),\n                2,\n            )\n            .build();\n    }\n}\n\nimpl BuiltInObject for WeakMap {\n    const NAME: JsString = StaticJsStrings::WEAK_MAP;\n\n    const ATTRIBUTE: Attribute = Attribute::WRITABLE.union(Attribute::CONFIGURABLE);\n}\n\nimpl BuiltInConstructor for WeakMap {\n    /// The amount of arguments the `WeakMap` constructor takes.\n    const CONSTRUCTOR_ARGUMENTS: usize = 0;\n    const PROTOTYPE_STORAGE_SLOTS: usize = 7;\n    const CONSTRUCTOR_STORAGE_SLOTS: usize = 0;\n\n    const STANDARD_CONSTRUCTOR: fn(&StandardConstructors) -> &StandardConstructor =\n        StandardConstructors::weak_map;\n\n    /// `WeakMap ( [ iterable ] )`\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///  - [MDN documentation][mdn]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-weakmap-iterable\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/WeakMap/WeakMap\n    fn constructor(\n        new_target: &JsValue,\n        args: &[JsValue],\n        context: &mut Context,\n    ) -> JsResult<JsValue> {\n        // 1. If NewTarget is undefined, throw a TypeError exception.\n        if new_target.is_undefined() {\n            return Err(js_error!(TypeError: \"WeakMap: cannot call constructor without `new`\"));\n        }\n\n        // 2. Let map be ? OrdinaryCreateFromConstructor(NewTarget, \"%WeakMap.prototype%\", « [[WeakMapData]] »).\n        // 3. Set map.[[WeakMapData]] to a new empty List.\n        let prototype =\n            get_prototype_from_constructor(new_target, StandardConstructors::weak_map, context)?;\n        let map = JsObject::from_proto_and_data_with_shared_shape(\n            context.root_shape(),\n            prototype,\n            NativeWeakMap::new(),\n        )\n        .upcast();\n\n        // 4. If iterable is either undefined or null, return map.\n        let iterable = args.get_or_undefined(0);\n        if iterable.is_null_or_undefined() {\n            return Ok(map.into());\n        }\n\n        // 5. Let adder be ? Get(map, \"set\").\n        // 6. If IsCallable(adder) is false, throw a TypeError exception.\n        let adder = map\n            .get(js_string!(\"set\"), context)?\n            .as_function()\n            .ok_or_else(|| js_error!(TypeError: \"WeakMap: 'add' is not a function\"))?;\n\n        // 7. Return ? AddEntriesFromIterable(map, iterable, adder).\n        add_entries_from_iterable(&map, iterable, &adder, context)\n    }\n}\n\nimpl WeakMap {\n    /// `WeakMap.prototype.delete ( key )`\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///  - [MDN documentation][mdn]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-weakmap.prototype.delete\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/WeakMap/delete\n    pub(crate) fn delete(\n        this: &JsValue,\n        args: &[JsValue],\n        _context: &mut Context,\n    ) -> JsResult<JsValue> {\n        // 1. Let M be the this value.\n        // 2. Perform ? RequireInternalSlot(M, [[WeakMapData]]).\n        let object = this.as_object();\n        let mut map = object\n            .as_ref()\n            .and_then(JsObject::downcast_mut::<NativeWeakMap>)\n            .ok_or_else(|| {\n                js_error!(TypeError:\n                    \"WeakMap.prototype.delete: expected 'this' to be a WeakMap object\",\n                )\n            })?;\n\n        // 3. Let entries be M.[[WeakMapData]].\n        // 4. If key is not an Object, return false.\n        let Some(key) = args.get_or_undefined(0).as_object() else {\n            return Ok(false.into());\n        };\n\n        // 5. For each Record { [[Key]], [[Value]] } p of entries, do\n        // a. If p.[[Key]] is not empty and SameValue(p.[[Key]], key) is true, then\n        // i. Set p.[[Key]] to empty.\n        // ii. Set p.[[Value]] to empty.\n        // iii. Return true.\n        // 6. Return false.\n        Ok(map.remove(key.inner()).into())\n    }\n\n    /// `WeakMap.prototype.get ( key )`\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///  - [MDN documentation][mdn]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-weakmap.prototype.get\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/WeakMap/get\n    pub(crate) fn get(\n        this: &JsValue,\n        args: &[JsValue],\n        _context: &mut Context,\n    ) -> JsResult<JsValue> {\n        // 1. Let M be the this value.\n        // 2. Perform ? RequireInternalSlot(M, [[WeakMapData]]).\n        let object = this.as_object();\n        let map = object\n            .as_ref()\n            .and_then(JsObject::downcast_ref::<NativeWeakMap>)\n            .ok_or_else(|| {\n                js_error!(TypeError:\n                    \"WeakMap.prototype.get: expected 'this' to be a WeakMap object\")\n            })?;\n\n        // 3. Let entries be M.[[WeakMapData]].\n        // 4. If key is not an Object, return undefined.\n        let Some(key) = args.get_or_undefined(0).as_object() else {\n            return Ok(JsValue::undefined());\n        };\n\n        // 5. For each Record { [[Key]], [[Value]] } p of entries, do\n        // a. If p.[[Key]] is not empty and SameValue(p.[[Key]], key) is true, return p.[[Value]].\n        // 6. Return undefined.\n        if let Some(entry) = map.get(key.inner())\n            && let Some(val) = entry.value()\n        {\n            Ok(val.clone())\n        } else {\n            Ok(JsValue::undefined())\n        }\n    }\n\n    /// `WeakMap.prototype.has ( key )`\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///  - [MDN documentation][mdn]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-weakmap.prototype.has\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/WeakMap/has\n    pub(crate) fn has(\n        this: &JsValue,\n        args: &[JsValue],\n        _context: &mut Context,\n    ) -> JsResult<JsValue> {\n        // 1. Let M be the this value.\n        // 2. Perform ? RequireInternalSlot(M, [[WeakMapData]]).\n        let object = this.as_object();\n        let map = object\n            .as_ref()\n            .and_then(JsObject::downcast_ref::<NativeWeakMap>)\n            .ok_or_else(|| {\n                js_error!(TypeError:\n                    \"WeakMap.prototype.has: expected 'this' to be a WeakMap object\")\n            })?;\n\n        // 3. Let entries be M.[[WeakMapData]].\n        // 4. If key is not an Object, return false.\n        let Some(key) = args.get_or_undefined(0).as_object() else {\n            return Ok(false.into());\n        };\n\n        // 5. For each Record { [[Key]], [[Value]] } p of entries, do\n        // a. If p.[[Key]] is not empty and SameValue(p.[[Key]], key) is true, return true.\n        // 6. Return false.\n        Ok(map.contains_key(key.inner()).into())\n    }\n\n    /// `WeakMap.prototype.set ( key, value )`\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///  - [MDN documentation][mdn]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-weakmap.prototype.set\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/WeakMap/set\n    pub(crate) fn set(\n        this: &JsValue,\n        args: &[JsValue],\n        _context: &mut Context,\n    ) -> JsResult<JsValue> {\n        // 1. Let M be the this value.\n        // 2. Perform ? RequireInternalSlot(M, [[WeakMapData]]).\n        let object = this.as_object();\n        let mut map = object\n            .as_ref()\n            .and_then(JsObject::downcast_mut::<NativeWeakMap>)\n            .ok_or_else(|| {\n                js_error!(TypeError:\n                    \"WeakMap.prototype.set: expected 'this' to be a WeakMap object\")\n            })?;\n\n        // 3. Let entries be M.[[WeakMapData]].\n        // 4. If key is not an Object, throw a TypeError exception.\n        let key = args.get_or_undefined(0);\n        let Some(key) = key.as_object() else {\n            return Err(js_error!(TypeError:\n                \"WeakMap.set: expected target argument of type `object`, got target of type `{}`\",\n                key.type_of()\n            ));\n        };\n\n        // 5. For each Record { [[Key]], [[Value]] } p of entries, do\n        // a. If p.[[Key]] is not empty and SameValue(p.[[Key]], key) is true, then\n        // i. Set p.[[Value]] to value.\n        // ii. Return M.\n        // 6. Let p be the Record { [[Key]]: key, [[Value]]: value }.\n        // 7. Append p to entries.\n        map.insert(key.inner(), args.get_or_undefined(1).clone());\n\n        // 8. Return M.\n        Ok(this.clone())\n    }\n\n    /// `WeakMap.prototype.getOrInsert ( key, value )`\n    ///\n    /// Given a key and a value, returns the existing value if it exists; otherwise inserts the\n    /// provided default value and returns that value.\n    ///\n    /// More information:\n    ///  - [Upsert proposal reference][spec]\n    ///  - [MDN documentation][mdn]\n    ///\n    /// [spec]: https://tc39.es/proposal-upsert/#sec-weakmap.prototype.getOrInsert\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/WeakMap/getOrInsert\n    pub(crate) fn get_or_insert(\n        this: &JsValue,\n        args: &[JsValue],\n        _context: &mut Context,\n    ) -> JsResult<JsValue> {\n        // 1. Let M be the this value.\n        // 2. Perform ? RequireInternalSlot(M, [[WeakMapData]]).\n        let object = this.as_object();\n        let map = object\n            .and_then(|obj| obj.clone().downcast::<NativeWeakMap>().ok())\n            .ok_or_else(|| {\n                js_error!(TypeError:\n                    \"WeakMap.prototype.getOrInsert: expected 'this' to be a WeakMap object\",\n                )\n            })?;\n\n        // 3. If CanBeHeldWeakly(key) is false, throw a TypeError exception.\n        // TODO: Implement proper CanBeHeldWeakly once available. For now, only\n        //       objects are accepted as keys; symbols should be allowed in the\n        //       future according to the proposal.\n        let key_val = args.get_or_undefined(0);\n        let Some(key) = key_val.as_object() else {\n            return Err(js_error!(TypeError:\n                \"WeakMap.getOrInsert: expected target argument of type `object`, got target of type `{}`\",\n                key_val.type_of()\n            ));\n        };\n\n        // 4. For each Record { [[Key]], [[Value]] } p of M.[[WeakMapData]]\n        if let Some(existing) = map.borrow().data().get(key.inner())\n            && let Some(value) = existing.value()\n        {\n            // a. If p.[[Key]] is not empty and SameValue(p.[[Key]], key) is true, return p.[[Value]].\n            return Ok(value.clone());\n        }\n\n        // 5-6. Insert the new record with provided value and return it.\n        let value = args.get_or_undefined(1).clone();\n        map.borrow_mut()\n            .data_mut()\n            .insert(key.inner(), value.clone());\n        Ok(value)\n    }\n\n    /// `WeakMap.prototype.getOrInsertComputed ( key, callback )`\n    ///\n    /// If the key exists, returns the existing value. Otherwise computes a new value by calling\n    /// `callback` with the key, inserts it into the `WeakMap`, and returns it.\n    ///\n    /// More information:\n    ///  - [Upsert proposal reference][spec]\n    ///  - [MDN documentation][mdn]\n    ///\n    /// [spec]: https://tc39.es/proposal-upsert/#sec-weakmap.prototype.getOrInsertComputed\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/WeakMap/getOrInsertComputed\n    pub(crate) fn get_or_insert_computed(\n        this: &JsValue,\n        args: &[JsValue],\n        context: &mut Context,\n    ) -> JsResult<JsValue> {\n        // 1. Let M be the this value.\n        // 2. Perform ? RequireInternalSlot(M, [[WeakMapData]]).\n        let object = this.as_object();\n        let map = object\n            .and_then(|obj| obj.clone().downcast::<NativeWeakMap>().ok())\n            .ok_or_else(|| {\n                js_error!(TypeError:\n                    \"WeakMap.prototype.getOrInsertComputed: expected 'this' to be a WeakMap object\",\n                )\n            })?;\n\n        // 3. If CanBeHeldWeakly(key) is false, throw a TypeError exception.\n        // TODO: Implement proper CanBeHeldWeakly once available. For now, only\n        //       objects are accepted as keys; symbols should be allowed in the\n        //       future according to the proposal.\n        let key_value = args.get_or_undefined(0).clone();\n        let Some(key_obj) = key_value.as_object() else {\n            return Err(js_error!(TypeError:\n                \"WeakMap.getOrInsertComputed: expected target argument of type `object`, got target of type `{}`\",\n                key_value.type_of()\n            ));\n        };\n\n        // 4. If IsCallable(callback) is false, throw a TypeError exception.\n        let Some(callback_fn) = args.get_or_undefined(1).as_callable() else {\n            return Err(js_error!(TypeError:\n                \"Method WeakMap.prototype.getOrInsertComputed called with non-callable callback function\"\n            ));\n        };\n\n        // 5. For each Record { [[Key]], [[Value]] } p of M.[[WeakMapData]]\n        if let Some(existing) = map.borrow().data().get(key_obj.inner())\n            && let Some(value) = existing.value()\n        {\n            // a. If p.[[Key]] is not empty and SameValue(p.[[Key]], key) is true, return p.[[Value]].\n            return Ok(value.clone());\n        }\n\n        // 6. Let value be ? Call(callback, undefined, « key »).\n        // 7. NOTE: The WeakMap may have been modified during execution of callback.\n        let value = callback_fn.call(\n            &JsValue::undefined(),\n            std::slice::from_ref(&key_value),\n            context,\n        )?;\n\n        // 8-10. Insert or update the entry and return value.\n        map.borrow_mut()\n            .data_mut()\n            .insert(key_obj.inner(), value.clone());\n        Ok(value)\n    }\n}\n"
  },
  {
    "path": "core/engine/src/builtins/weak_map/tests.rs",
    "content": "use crate::{JsNativeErrorKind, TestAction, run_test_actions};\nuse boa_macros::js_str;\n\n#[test]\nfn get_or_insert_inserts_on_miss() {\n    run_test_actions([\n        TestAction::run(\"let wm = new WeakMap(); let k = {};\"),\n        TestAction::assert_eq(\"wm.getOrInsert(k, 42)\", 42),\n        TestAction::assert(\"wm.has(k)\"),\n        TestAction::assert_eq(\"wm.get(k)\", 42),\n    ]);\n}\n\n#[test]\nfn get_or_insert_returns_existing_on_hit() {\n    run_test_actions([\n        TestAction::run(\"let k = {}; let wm = new WeakMap([[k, 99]]);\"),\n        TestAction::assert_eq(\"wm.getOrInsert(k, 123)\", 99),\n        TestAction::assert_eq(\"wm.get(k)\", 99), // unchanged\n    ]);\n}\n\n#[test]\nfn get_or_insert_computed_requires_callable() {\n    run_test_actions([TestAction::assert_native_error(\n        \"new WeakMap().getOrInsertComputed({}, undefined)\",\n        JsNativeErrorKind::Type,\n        \"Method WeakMap.prototype.getOrInsertComputed called with non-callable callback function\",\n    )]);\n}\n\n#[test]\nfn get_or_insert_requires_object_key() {\n    run_test_actions([TestAction::assert_native_error(\n        \"new WeakMap().getOrInsert('x', 1)\",\n        JsNativeErrorKind::Type,\n        \"WeakMap.getOrInsert: expected target argument of type `object`, got target of type `string`\",\n    )]);\n}\n\n#[test]\nfn get_or_insert_computed_requires_object_key() {\n    run_test_actions([TestAction::assert_native_error(\n        \"new WeakMap().getOrInsertComputed('x', () => 1)\",\n        JsNativeErrorKind::Type,\n        \"WeakMap.getOrInsertComputed: expected target argument of type `object`, got target of type `string`\",\n    )]);\n}\n\n#[test]\nfn get_or_insert_computed_not_called_on_hit() {\n    run_test_actions([\n        TestAction::run(\"const k = {}; const wm = new WeakMap([[k, 7]]); let calls = 0;\"),\n        TestAction::assert_eq(\n            \"wm.getOrInsertComputed(k, (key) => { calls++; return 1; })\",\n            7,\n        ),\n        TestAction::assert_eq(\"calls\", 0),\n        TestAction::assert_eq(\"wm.get(k)\", 7),\n    ]);\n}\n\n#[test]\nfn get_or_insert_computed_this_is_undefined_and_key_forwarded() {\n    run_test_actions([\n        TestAction::run(\n            r#\"\n            const wm = new WeakMap();\n            const k = {};\n            let seenThis, seenKey;\n            const v = wm.getOrInsertComputed(k, function(x) { 'use strict'; seenThis = this; seenKey = x; return 'ok'; });\n        \"#,\n        ),\n        // `this` inside callback is undefined\n        TestAction::assert(\"seenThis === undefined\"),\n        // key argument is forwarded as the same object\n        TestAction::assert(\"seenKey === k\"),\n        TestAction::assert_eq(\"v\", js_str!(\"ok\")),\n        TestAction::assert(\"wm.has(k)\"),\n        TestAction::assert_eq(\"wm.get(k)\", js_str!(\"ok\")),\n    ]);\n}\n\n#[test]\nfn get_or_insert_computed_overwrites_race() {\n    run_test_actions([\n        TestAction::run(\n            r#\"\n            const wm = new WeakMap();\n            const k = {};\n            const v = wm.getOrInsertComputed(k, function(x) { wm.set(k, 'other'); return 'computed'; });\n        \"#,\n        ),\n        TestAction::assert_eq(\"v\", js_str!(\"computed\")),\n        TestAction::assert_eq(\"wm.get(k)\", js_str!(\"computed\")),\n    ]);\n}\n\n#[test]\nfn get_or_insert_this_not_weakmap() {\n    run_test_actions([TestAction::assert_native_error(\n        \"WeakMap.prototype.getOrInsert.call({}, {}, 1)\",\n        JsNativeErrorKind::Type,\n        \"WeakMap.prototype.getOrInsert: expected 'this' to be a WeakMap object\",\n    )]);\n}\n\n#[test]\nfn get_or_insert_computed_this_not_weakmap() {\n    run_test_actions([TestAction::assert_native_error(\n        \"WeakMap.prototype.getOrInsertComputed.call({}, {}, x => x)\",\n        JsNativeErrorKind::Type,\n        \"WeakMap.prototype.getOrInsertComputed: expected 'this' to be a WeakMap object\",\n    )]);\n}\n\n#[test]\nfn weakmap_set_and_get() {\n    run_test_actions([\n        TestAction::run(\n            r#\"\n            const wm = new WeakMap();\n            const obj = {};\n            wm.set(obj, 42);\n        \"#,\n        ),\n        TestAction::assert_eq(\"wm.get(obj)\", 42),\n    ]);\n}\n\n#[test]\nfn weakmap_overwrite_value() {\n    run_test_actions([\n        TestAction::run(\n            r#\"\n            const wm = new WeakMap();\n            const obj = {};\n            wm.set(obj, 1);\n            wm.set(obj, 2);\n        \"#,\n        ),\n        TestAction::assert_eq(\"wm.get(obj)\", 2),\n    ]);\n}\n\n#[test]\nfn weakmap_has() {\n    run_test_actions([\n        TestAction::run(\n            r#\"\n            const wm = new WeakMap();\n            const obj = {};\n            wm.set(obj, 10);\n        \"#,\n        ),\n        TestAction::assert(\"wm.has(obj)\"),\n    ]);\n}\n\n#[test]\nfn weakmap_delete() {\n    run_test_actions([\n        TestAction::run(\n            r#\"\n            const wm = new WeakMap();\n            const obj = {};\n            wm.set(obj, 1);\n        \"#,\n        ),\n        TestAction::assert(\"wm.delete(obj)\"),\n    ]);\n}\n\n#[test]\nfn weakmap_delete_twice() {\n    run_test_actions([\n        TestAction::run(\n            r#\"\n            const wm = new WeakMap();\n            const obj = {};\n            wm.set(obj, 1);\n            wm.delete(obj);\n        \"#,\n        ),\n        TestAction::assert(\"!wm.delete(obj)\"),\n    ]);\n}\n\n#[test]\nfn weakmap_get_missing_key() {\n    run_test_actions([\n        TestAction::run(\n            r#\"\n            const wm = new WeakMap();\n            const result = wm.get({});\n        \"#,\n        ),\n        TestAction::assert(\"result === undefined\"),\n    ]);\n}\n\n#[test]\nfn weakmap_multiple_keys() {\n    run_test_actions([\n        TestAction::run(\n            r#\"\n            const wm = new WeakMap();\n            const a = {};\n            const b = {};\n            wm.set(a, 1);\n            wm.set(b, 2);\n        \"#,\n        ),\n        TestAction::assert_eq(\"wm.get(a) + wm.get(b)\", 3),\n    ]);\n}\n\n#[test]\nfn weakmap_set_returns_this() {\n    run_test_actions([\n        TestAction::run(\n            r#\"\n            const wm = new WeakMap();\n            const obj = {};\n        \"#,\n        ),\n        TestAction::assert(\"wm.set(obj, 1) === wm\"),\n    ]);\n}\n\n#[test]\nfn weakmap_set_rejects_number() {\n    run_test_actions([\n        TestAction::run(\"const wm = new WeakMap();\"),\n        TestAction::assert_native_error(\n            \"wm.set(42, 'value')\",\n            JsNativeErrorKind::Type,\n            \"WeakMap.set: expected target argument of type `object`, got target of type `number`\",\n        ),\n    ]);\n}\n\n#[test]\nfn weakmap_set_rejects_string() {\n    run_test_actions([\n        TestAction::run(\"const wm = new WeakMap();\"),\n        TestAction::assert_native_error(\n            \"wm.set('string', 'value')\",\n            JsNativeErrorKind::Type,\n            \"WeakMap.set: expected target argument of type `object`, got target of type `string`\",\n        ),\n    ]);\n}\n\n#[test]\nfn weakmap_set_rejects_boolean() {\n    run_test_actions([\n        TestAction::run(\"const wm = new WeakMap();\"),\n        TestAction::assert_native_error(\n            \"wm.set(true, 'value')\",\n            JsNativeErrorKind::Type,\n            \"WeakMap.set: expected target argument of type `object`, got target of type `boolean`\",\n        ),\n    ]);\n}\n\n#[test]\nfn weakmap_set_rejects_null() {\n    run_test_actions([\n        TestAction::run(\"const wm = new WeakMap();\"),\n        TestAction::assert_native_error(\n            \"wm.set(null, 'value')\",\n            JsNativeErrorKind::Type,\n            \"WeakMap.set: expected target argument of type `object`, got target of type `object`\",\n        ),\n    ]);\n}\n\n#[test]\nfn weakmap_set_rejects_undefined() {\n    run_test_actions([\n        TestAction::run(\"const wm = new WeakMap();\"),\n        TestAction::assert_native_error(\n            \"wm.set(undefined, 'value')\",\n            JsNativeErrorKind::Type,\n            \"WeakMap.set: expected target argument of type `object`, got target of type `undefined`\",\n        ),\n    ]);\n}\n\n#[test]\nfn weakmap_set_rejects_symbol() {\n    run_test_actions([\n        TestAction::run(\"const wm = new WeakMap();\"),\n        TestAction::assert_native_error(\n            \"wm.set(Symbol('sim'), 'value')\",\n            JsNativeErrorKind::Type,\n            \"WeakMap.set: expected target argument of type `object`, got target of type `symbol`\",\n        ),\n    ]);\n}\n"
  },
  {
    "path": "core/engine/src/builtins/weak_set/mod.rs",
    "content": "//! Boa's implementation of ECMAScript's `WeakSet` builtin object.\n//!\n//! More information:\n//!  - [ECMAScript reference][spec]\n//!  - [MDN documentation][mdn]\n//!\n//! [spec]: https://tc39.es/ecma262/#sec-weakset-objects\n//! [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/WeakSet\n\nuse crate::{\n    Context, JsArgs, JsResult, JsString, JsValue,\n    builtins::{BuiltInBuilder, BuiltInConstructor, BuiltInObject, IntrinsicObject},\n    context::intrinsics::{Intrinsics, StandardConstructor, StandardConstructors},\n    js_error, js_string,\n    object::{ErasedVTableObject, JsObject, internal_methods::get_prototype_from_constructor},\n    property::Attribute,\n    realm::Realm,\n    string::StaticJsStrings,\n    symbol::JsSymbol,\n};\nuse boa_gc::{Finalize, Trace};\n\nuse super::iterable::IteratorHint;\n\npub(crate) type NativeWeakSet = boa_gc::WeakMap<ErasedVTableObject, ()>;\n\n#[derive(Debug, Trace, Finalize)]\npub(crate) struct WeakSet;\n\nimpl IntrinsicObject for WeakSet {\n    fn get(intrinsics: &Intrinsics) -> JsObject {\n        Self::STANDARD_CONSTRUCTOR(intrinsics.constructors()).constructor()\n    }\n\n    fn init(realm: &Realm) {\n        BuiltInBuilder::from_standard_constructor::<Self>(realm)\n            .property(\n                JsSymbol::to_string_tag(),\n                Self::NAME,\n                Attribute::READONLY | Attribute::NON_ENUMERABLE | Attribute::CONFIGURABLE,\n            )\n            .method(Self::add, js_string!(\"add\"), 1)\n            .method(Self::delete, js_string!(\"delete\"), 1)\n            .method(Self::has, js_string!(\"has\"), 1)\n            .build();\n    }\n}\n\nimpl BuiltInObject for WeakSet {\n    const NAME: JsString = StaticJsStrings::WEAK_SET;\n\n    const ATTRIBUTE: Attribute = Attribute::WRITABLE.union(Attribute::CONFIGURABLE);\n}\n\nimpl BuiltInConstructor for WeakSet {\n    /// The amount of arguments the `WeakSet` constructor takes.\n    const CONSTRUCTOR_ARGUMENTS: usize = 0;\n    const PROTOTYPE_STORAGE_SLOTS: usize = 4;\n    const CONSTRUCTOR_STORAGE_SLOTS: usize = 0;\n\n    const STANDARD_CONSTRUCTOR: fn(&StandardConstructors) -> &StandardConstructor =\n        StandardConstructors::weak_set;\n\n    /// `WeakSet ( [ iterable ] )`\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///  - [MDN documentation][mdn]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-weakset-iterable\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/WeakSet/WeakSet\n    fn constructor(\n        new_target: &JsValue,\n        args: &[JsValue],\n        context: &mut Context,\n    ) -> JsResult<JsValue> {\n        // 1. If NewTarget is undefined, throw a TypeError exception.\n        if new_target.is_undefined() {\n            return Err(js_error!(TypeError: \"WeakSet: cannot call constructor without `new`\"));\n        }\n\n        // 2. Let set be ? OrdinaryCreateFromConstructor(NewTarget, \"%WeakSet.prototype%\", « [[WeakSetData]] »).\n        // 3. Set set.[[WeakSetData]] to a new empty List.\n        let prototype =\n            get_prototype_from_constructor(new_target, StandardConstructors::weak_set, context)?;\n        let weak_set = JsObject::from_proto_and_data_with_shared_shape(\n            context.root_shape(),\n            prototype,\n            NativeWeakSet::new(),\n        )\n        .upcast();\n\n        // 4. If iterable is either undefined or null, return set.\n        let iterable = args.get_or_undefined(0);\n        if iterable.is_null_or_undefined() {\n            return Ok(weak_set.into());\n        }\n\n        // 5. Let adder be ? Get(set, \"add\").\n        let adder = weak_set.get(js_string!(\"add\"), context)?;\n\n        // 6. If IsCallable(adder) is false, throw a TypeError exception.\n        let adder = adder\n            .as_callable()\n            .ok_or_else(|| js_error!(TypeError: \"WeakSet: 'add' is not a function\"))?;\n\n        // 7. Let iteratorRecord be ? GetIterator(iterable, sync).\n        let mut iterator_record = iterable.clone().get_iterator(IteratorHint::Sync, context)?;\n\n        // 8. Repeat,\n        //     a. Let next be ? IteratorStepValue(iteratorRecord).\n        while let Some(next) = iterator_record.step_value(context)? {\n            //     c. Let status be Completion(Call(adder, set, « next »)).\n            if let Err(status) = adder.call(&weak_set.clone().into(), &[next], context) {\n                //     d. IfAbruptCloseIterator(status, iteratorRecord).\n                return iterator_record.close(Err(status), context);\n            }\n        }\n\n        //     b. If next is done, return set.\n        Ok(weak_set.into())\n    }\n}\n\nimpl WeakSet {\n    /// `WeakSet.prototype.add( value )`\n    ///\n    /// The `add()` method appends a new object to the end of a `WeakSet` object.\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///  - [MDN documentation][mdn]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-weakset.prototype.add\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/WeakSet/add\n    pub(crate) fn add(\n        this: &JsValue,\n        args: &[JsValue],\n        _context: &mut Context,\n    ) -> JsResult<JsValue> {\n        // 1. Let S be the this value.\n        // 2. Perform ? RequireInternalSlot(S, [[WeakSetData]]).\n        let object = this.as_object();\n        let mut set = object\n            .as_ref()\n            .and_then(JsObject::downcast_mut::<NativeWeakSet>)\n            .ok_or_else(|| {\n                js_error!(TypeError:\n                    \"WeakSet.prototype.add: expected 'this' to be a WeakSet object\")\n            })?;\n\n        // 3. If Type(value) is not Object, throw a TypeError exception.\n        let value = args.get_or_undefined(0);\n        let Some(value) = value.as_object() else {\n            return Err(js_error!(TypeError:\n                \"WeakSet.add: expected target argument of type `object`, got target of type `{}`\",\n                value.type_of()\n            ));\n        };\n\n        // 4. Let entries be the List that is S.[[WeakSetData]].\n        // 5. For each element e of entries, do\n        if set.contains_key(value.inner()) {\n            // a. If e is not empty and SameValue(e, value) is true, then\n            // i. Return S.\n            return Ok(this.clone());\n        }\n\n        // 6. Append value as the last element of entries.\n        set.insert(value.inner(), ());\n\n        // 7. Return S.\n        Ok(this.clone())\n    }\n\n    /// `WeakSet.prototype.delete( value )`\n    ///\n    /// The `delete()` method removes the specified element from a `WeakSet` object.\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///  - [MDN documentation][mdn]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-weakset.prototype.delete\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/WeakSet/delete\n    pub(crate) fn delete(\n        this: &JsValue,\n        args: &[JsValue],\n        _context: &mut Context,\n    ) -> JsResult<JsValue> {\n        // 1. Let S be the this value.\n        // 2. Perform ? RequireInternalSlot(S, [[WeakSetData]]).\n        let object = this.as_object();\n        let mut set = object\n            .as_ref()\n            .and_then(JsObject::downcast_mut::<NativeWeakSet>)\n            .ok_or_else(|| {\n                js_error!(TypeError:\n                    \"WeakSet.prototype.delete: expected 'this' to be a WeakSet object\",\n                )\n            })?;\n\n        // 3. If Type(value) is not Object, return false.\n        let value = args.get_or_undefined(0);\n        let Some(value) = value.as_object() else {\n            return Ok(false.into());\n        };\n\n        // 4. Let entries be the List that is S.[[WeakSetData]].\n        // 5. For each element e of entries, do\n        // a. If e is not empty and SameValue(e, value) is true, then\n        // i. Replace the element of entries whose value is e with an element whose value is empty.\n        // ii. Return true.\n        // 6. Return false.\n        Ok(set.remove(value.inner()).into())\n    }\n\n    /// `WeakSet.prototype.has( value )`\n    ///\n    /// The `has()` method returns a boolean indicating whether an object exists in a `WeakSet` or not.\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///  - [MDN documentation][mdn]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-weakset.prototype.has\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/WeakSet/has\n    pub(crate) fn has(\n        this: &JsValue,\n        args: &[JsValue],\n        _context: &mut Context,\n    ) -> JsResult<JsValue> {\n        // 1. Let S be the this value.\n        // 2. Perform ? RequireInternalSlot(S, [[WeakSetData]]).\n        let object = this.as_object();\n        let set = object\n            .as_ref()\n            .and_then(JsObject::downcast_ref::<NativeWeakSet>)\n            .ok_or_else(|| {\n                js_error!(TypeError:\n                    \"WeakSet.prototype.has: expected 'this' to be a WeakSet object\")\n            })?;\n\n        // 3. Let entries be the List that is S.[[WeakSetData]].\n        // 4. If Type(value) is not Object, return false.\n        let value = args.get_or_undefined(0);\n        let Some(value) = value.as_object() else {\n            return Ok(false.into());\n        };\n\n        // 5. For each element e of entries, do\n        // a. If e is not empty and SameValue(e, value) is true, return true.\n        // 6. Return false.\n        Ok(set.contains_key(value.inner()).into())\n    }\n}\n\n#[cfg(test)]\nmod tests;\n"
  },
  {
    "path": "core/engine/src/builtins/weak_set/tests.rs",
    "content": "use crate::{JsNativeErrorKind, TestAction, run_test_actions};\n\n#[test]\nfn weakset_add_and_has() {\n    run_test_actions([\n        TestAction::run(\"var ws = new WeakSet(); var obj = {}; ws.add(obj);\"),\n        TestAction::assert(\"ws.has(obj)\"),\n    ]);\n}\n\n#[test]\nfn weakset_add_chaining() {\n    run_test_actions([\n        TestAction::run(\"var ws = new WeakSet(); var obj = {};\"),\n        TestAction::assert(\"ws.add(obj) === ws\"),\n    ]);\n}\n\n#[test]\nfn weakset_delete_behavior() {\n    run_test_actions([\n        TestAction::run(\"var ws = new WeakSet(); var obj = {}; ws.add(obj);\"),\n        TestAction::assert(\"ws.delete(obj) === true\"),\n        TestAction::assert(\"ws.delete(obj) === false\"),\n        TestAction::assert(\"ws.has(obj) === false\"),\n    ]);\n}\n\n#[test]\nfn weakset_add_primitive_rejection() {\n    run_test_actions([\n        TestAction::run(\"var ws = new WeakSet();\"),\n        TestAction::assert_native_error(\n            \"ws.add(1);\",\n            JsNativeErrorKind::Type,\n            \"WeakSet.add: expected target argument of type `object`, got target of type `number`\",\n        ),\n        TestAction::assert_native_error(\n            \"ws.add('x');\",\n            JsNativeErrorKind::Type,\n            \"WeakSet.add: expected target argument of type `object`, got target of type `string`\",\n        ),\n        TestAction::assert_native_error(\n            \"ws.add(true);\",\n            JsNativeErrorKind::Type,\n            \"WeakSet.add: expected target argument of type `object`, got target of type `boolean`\",\n        ),\n        TestAction::assert_native_error(\n            \"ws.add(null);\",\n            JsNativeErrorKind::Type,\n            \"WeakSet.add: expected target argument of type `object`, got target of type `object`\",\n        ),\n        TestAction::assert_native_error(\n            \"ws.add(undefined);\",\n            JsNativeErrorKind::Type,\n            \"WeakSet.add: expected target argument of type `object`, got target of type `undefined`\",\n        ),\n        TestAction::assert_native_error(\n            \"ws.add(Symbol('id'));\",\n            JsNativeErrorKind::Type,\n            \"WeakSet.add: expected target argument of type `object`, got target of type `symbol`\",\n        ),\n    ]);\n}\n\n#[test]\nfn weakset_has_primitive_returns_false() {\n    run_test_actions([\n        TestAction::run(\"var ws = new WeakSet();\"),\n        TestAction::assert(\"ws.has(1) === false\"),\n        TestAction::assert(\"ws.has('x') === false\"),\n        TestAction::assert(\"ws.has(null) === false\"),\n    ]);\n}\n\n#[test]\nfn weakset_delete_primitive_returns_false() {\n    run_test_actions([\n        TestAction::run(\"var ws = new WeakSet();\"),\n        TestAction::assert(\"ws.delete(1) === false\"),\n        TestAction::assert(\"ws.delete('x') === false\"),\n        TestAction::assert(\"ws.delete(null) === false\"),\n    ]);\n}\n\n#[test]\nfn weakset_add_duplicate() {\n    run_test_actions([\n        TestAction::run(\"var ws = new WeakSet(); var obj = {}; ws.add(obj);\"),\n        TestAction::assert(\"ws.has(obj) === true\"),\n        TestAction::run(\"ws.add(obj);\"),\n        TestAction::assert(\"ws.has(obj) === true\"),\n    ]);\n}\n"
  },
  {
    "path": "core/engine/src/bytecompiler/class.rs",
    "content": "use super::{BindingAccessOpcode, ByteCompiler, Literal, Register, ToJsString};\nuse crate::{\n    js_string,\n    vm::{CodeBlock, CodeBlockFlags, opcode::BindingOpcode},\n};\nuse boa_ast::{\n    Expression,\n    expression::Identifier,\n    function::{\n        ClassDeclaration, ClassElement, ClassElementName, ClassExpression, FormalParameterList,\n        FunctionExpression,\n    },\n    property::{MethodDefinitionKind, PropertyName},\n    scope::Scope,\n};\nuse boa_gc::Gc;\nuse boa_interner::Sym;\nuse thin_vec::ThinVec;\n\n// Static class elements that are initialized at a later time in the class creation.\nenum StaticElement {\n    // A static class block with it's function code.\n    StaticBlock(Gc<CodeBlock>),\n\n    // A static class field with it's function code, an optional name index and the information if the function is an anonymous function.\n    StaticField {\n        code: Gc<CodeBlock>,\n        name_index: StaticFieldName,\n        is_anonymous_function: bool,\n    },\n}\n\nenum StaticFieldName {\n    PrivateName(u32),\n    Index(u32),\n    Register(Register),\n}\n\n/// Describes the complete specification of a class.\n#[derive(Debug, Clone, Copy, PartialEq)]\npub(crate) struct ClassSpec<'a> {\n    name: Option<Identifier>,\n    super_ref: Option<&'a Expression>,\n    constructor: Option<&'a FunctionExpression>,\n    elements: &'a [ClassElement],\n    has_binding_identifier: bool,\n    name_scope: Option<&'a Scope>,\n}\n\nimpl<'a> From<&'a ClassDeclaration> for ClassSpec<'a> {\n    fn from(class: &'a ClassDeclaration) -> Self {\n        Self {\n            name: Some(class.name()),\n            super_ref: class.super_ref(),\n            constructor: class.constructor(),\n            elements: class.elements(),\n            has_binding_identifier: true,\n            name_scope: Some(class.name_scope()),\n        }\n    }\n}\n\nimpl<'a> From<&'a ClassExpression> for ClassSpec<'a> {\n    fn from(class: &'a ClassExpression) -> Self {\n        Self {\n            name: class.name(),\n            super_ref: class.super_ref(),\n            constructor: class.constructor(),\n            elements: class.elements(),\n            has_binding_identifier: class.name().is_some(),\n            name_scope: class.name_scope(),\n        }\n    }\n}\n\nimpl ByteCompiler<'_> {\n    /// This function compiles a class declaration or expression.\n    ///\n    /// The compilation of a class declaration and expression is mostly equal.\n    /// A class declaration binds the resulting class object to it's identifier.\n    /// A class expression leaves the resulting class object on the stack for following operations.\n    pub(crate) fn compile_class(&mut self, class: ClassSpec<'_>, dst: Option<&Register>) {\n        // 11.2.2 Strict Mode Code - <https://tc39.es/ecma262/#sec-strict-mode-code>\n        //  - All parts of a ClassDeclaration or a ClassExpression are strict mode code.\n        let strict = self.strict();\n        self.code_block_flags |= CodeBlockFlags::STRICT;\n\n        let class_name = class\n            .name\n            .map_or(Sym::EMPTY_STRING, Identifier::sym)\n            .to_js_string(self.interner());\n\n        let outer_scope = self.push_declarative_scope(class.name_scope);\n\n        // The new span is not the same as the parent `ByteCompiler` have.\n        let spanned_source_text = self.spanned_source_text.clone_only_source();\n        let mut compiler = ByteCompiler::new(\n            class_name.clone(),\n            true,\n            self.json_parse,\n            self.variable_scope.clone(),\n            self.lexical_scope.clone(),\n            false,\n            false,\n            self.interner,\n            self.in_with,\n            spanned_source_text,\n            self.source_path.clone(),\n        );\n\n        compiler.code_block_flags |= CodeBlockFlags::IS_CLASS_CONSTRUCTOR;\n\n        let value = compiler.register_allocator.alloc();\n        if let Some(expr) = &class.constructor {\n            compiler.code_block_flags |= CodeBlockFlags::HAS_FUNCTION_SCOPE;\n            let _ = compiler.push_scope(expr.scopes().function_scope());\n\n            compiler.length = expr.parameters().length();\n            compiler.params = expr.parameters().clone();\n            compiler.parameter_scope = expr.scopes().parameter_scope();\n\n            compiler.function_declaration_instantiation(\n                expr.body(),\n                expr.parameters(),\n                false,\n                true,\n                false,\n                expr.scopes(),\n            );\n\n            {\n                let mut compiler = compiler.position_guard(expr);\n                compiler.compile_statement_list(expr.body().statement_list(), false, false);\n            }\n\n            compiler.bytecode.emit_store_undefined(value.variable());\n        } else if class.super_ref.is_some() {\n            // We push an empty, unused function scope since the compiler expects a function scope.\n            compiler.code_block_flags |= CodeBlockFlags::HAS_FUNCTION_SCOPE;\n            let _ = compiler.push_scope(&Scope::new(compiler.lexical_scope.clone(), true));\n            compiler.bytecode.emit_super_call_derived();\n            compiler.pop_into_register(&value);\n            compiler.bytecode.emit_bind_this_value(value.variable());\n        } else {\n            // We push an empty, unused function scope since the compiler expects a function scope.\n            compiler.code_block_flags |= CodeBlockFlags::HAS_FUNCTION_SCOPE;\n            let _ = compiler.push_scope(&Scope::new(compiler.lexical_scope.clone(), true));\n            compiler.bytecode.emit_store_undefined(value.variable());\n        }\n        compiler.bytecode.emit_set_accumulator(value.variable());\n        compiler.register_allocator.dealloc(value);\n\n        // 17. If ClassHeritageopt is present, set F.[[ConstructorKind]] to derived.\n        compiler.code_block_flags.set(\n            CodeBlockFlags::IS_DERIVED_CONSTRUCTOR,\n            class.super_ref.is_some(),\n        );\n\n        let code = Gc::new(compiler.finish());\n        let index = self.push_function_to_constants(code);\n\n        let class_register = self.register_allocator.alloc();\n        self.emit_get_function(&class_register, index);\n\n        let prototype_register = self.register_allocator.alloc();\n\n        if let Some(node) = class.super_ref {\n            self.compile_expr(node, &prototype_register);\n            self.bytecode.emit_store_class_prototype(\n                prototype_register.variable(),\n                class_register.variable(),\n                prototype_register.variable(),\n            );\n        } else {\n            self.bytecode\n                .emit_store_undefined(prototype_register.variable());\n        }\n\n        let proto_register = self.register_allocator.alloc();\n\n        self.bytecode.emit_set_class_prototype(\n            proto_register.variable(),\n            prototype_register.variable(),\n            class_register.variable(),\n        );\n        self.register_allocator.dealloc(prototype_register);\n\n        let mut name_indices = ThinVec::new();\n        for element in class.elements {\n            match element {\n                ClassElement::MethodDefinition(m) => {\n                    if let ClassElementName::PrivateName(name) = m.name() {\n                        let index = self.get_or_insert_private_name(*name);\n                        name_indices.push(index);\n                    }\n                }\n                ClassElement::PrivateFieldDefinition(field)\n                | ClassElement::PrivateStaticFieldDefinition(field) => {\n                    let index = self.get_or_insert_private_name(*field.name());\n                    name_indices.push(index);\n                }\n                _ => {}\n            }\n        }\n        self.bytecode\n            .emit_push_private_environment(class_register.variable(), name_indices);\n\n        let mut static_elements = Vec::new();\n\n        if let Some(scope) = class.name_scope {\n            let binding = scope.get_identifier_reference(class_name.clone());\n            let index = self.insert_binding(binding);\n            self.emit_binding_access(\n                BindingAccessOpcode::PutLexicalValue,\n                &index,\n                &class_register,\n            );\n        }\n\n        for element in class.elements {\n            match element {\n                ClassElement::MethodDefinition(m) => match m.name() {\n                    ClassElementName::PropertyName(PropertyName::Literal(name)) => {\n                        let index = self.get_or_insert_name(name.sym());\n                        let method = self.method(m.into());\n\n                        let object_register = if m.is_static() {\n                            &class_register\n                        } else {\n                            &proto_register\n                        };\n\n                        match (m.is_static(), m.kind()) {\n                            (true, MethodDefinitionKind::Get) => {\n                                self.bytecode.emit_define_class_static_getter_by_name(\n                                    method.variable(),\n                                    object_register.variable(),\n                                    index.into(),\n                                );\n                            }\n                            (true, MethodDefinitionKind::Set) => {\n                                self.bytecode.emit_define_class_static_setter_by_name(\n                                    method.variable(),\n                                    object_register.variable(),\n                                    index.into(),\n                                );\n                            }\n                            (true, _) => {\n                                self.bytecode.emit_define_class_static_method_by_name(\n                                    method.variable(),\n                                    object_register.variable(),\n                                    index.into(),\n                                );\n                            }\n                            (false, MethodDefinitionKind::Get) => {\n                                self.bytecode.emit_define_class_getter_by_name(\n                                    method.variable(),\n                                    object_register.variable(),\n                                    index.into(),\n                                );\n                            }\n                            (false, MethodDefinitionKind::Set) => {\n                                self.bytecode.emit_define_class_setter_by_name(\n                                    method.variable(),\n                                    object_register.variable(),\n                                    index.into(),\n                                );\n                            }\n                            (false, _) => {\n                                self.bytecode.emit_define_class_method_by_name(\n                                    method.variable(),\n                                    object_register.variable(),\n                                    index.into(),\n                                );\n                            }\n                        }\n\n                        self.register_allocator.dealloc(method);\n                    }\n                    ClassElementName::PropertyName(PropertyName::Computed(name)) => {\n                        let key = self.register_allocator.alloc();\n                        self.compile_expr(name, &key);\n                        self.bytecode\n                            .emit_to_property_key(key.variable(), key.variable());\n                        let method = self.method(m.into());\n                        let object_register = if m.is_static() {\n                            &class_register\n                        } else {\n                            &proto_register\n                        };\n\n                        match (m.is_static(), m.kind()) {\n                            (true, MethodDefinitionKind::Get) => {\n                                self.bytecode.emit_define_class_static_getter_by_value(\n                                    method.variable(),\n                                    key.variable(),\n                                    object_register.variable(),\n                                );\n                            }\n                            (true, MethodDefinitionKind::Set) => {\n                                self.bytecode.emit_define_class_static_setter_by_value(\n                                    method.variable(),\n                                    key.variable(),\n                                    object_register.variable(),\n                                );\n                            }\n                            (true, _) => {\n                                self.bytecode.emit_define_class_static_method_by_value(\n                                    method.variable(),\n                                    key.variable(),\n                                    object_register.variable(),\n                                );\n                            }\n                            (false, MethodDefinitionKind::Get) => {\n                                self.bytecode.emit_define_class_getter_by_value(\n                                    method.variable(),\n                                    key.variable(),\n                                    object_register.variable(),\n                                );\n                            }\n                            (false, MethodDefinitionKind::Set) => {\n                                self.bytecode.emit_define_class_setter_by_value(\n                                    method.variable(),\n                                    key.variable(),\n                                    object_register.variable(),\n                                );\n                            }\n                            (false, _) => {\n                                self.bytecode.emit_define_class_method_by_value(\n                                    method.variable(),\n                                    key.variable(),\n                                    object_register.variable(),\n                                );\n                            }\n                        }\n\n                        self.register_allocator.dealloc(method);\n                        self.register_allocator.dealloc(key);\n                    }\n                    ClassElementName::PrivateName(name) => {\n                        let index = self.get_or_insert_private_name(*name);\n                        let method = self.method(m.into());\n                        match (m.is_static(), m.kind()) {\n                            (true, MethodDefinitionKind::Get) => {\n                                self.bytecode.emit_set_private_getter(\n                                    class_register.variable(),\n                                    method.variable(),\n                                    index.into(),\n                                );\n                            }\n                            (true, MethodDefinitionKind::Set) => {\n                                self.bytecode.emit_set_private_setter(\n                                    class_register.variable(),\n                                    method.variable(),\n                                    index.into(),\n                                );\n                            }\n                            (true, _) => {\n                                self.bytecode.emit_set_private_method(\n                                    class_register.variable(),\n                                    method.variable(),\n                                    index.into(),\n                                );\n                            }\n                            (false, MethodDefinitionKind::Get) => {\n                                self.bytecode.emit_push_class_private_getter(\n                                    class_register.variable(),\n                                    method.variable(),\n                                    index.into(),\n                                );\n                            }\n                            (false, MethodDefinitionKind::Set) => {\n                                self.bytecode.emit_push_class_private_setter(\n                                    class_register.variable(),\n                                    method.variable(),\n                                    index.into(),\n                                );\n                            }\n                            (false, _) => {\n                                self.bytecode.emit_push_class_private_method(\n                                    class_register.variable(),\n                                    proto_register.variable(),\n                                    method.variable(),\n                                    index.into(),\n                                );\n                            }\n                        }\n                        self.register_allocator.dealloc(method);\n                    }\n                },\n                ClassElement::FieldDefinition(field) => {\n                    let name = self.register_allocator.alloc();\n                    match field.name() {\n                        PropertyName::Literal(ident) => {\n                            self.emit_store_literal(\n                                Literal::String(\n                                    self.interner()\n                                        .resolve_expect(ident.sym())\n                                        .into_common(false),\n                                ),\n                                &name,\n                            );\n                        }\n                        PropertyName::Computed(expr) => {\n                            self.compile_expr(expr, &name);\n                        }\n                    }\n                    let mut field_compiler = ByteCompiler::new(\n                        js_string!(),\n                        true,\n                        self.json_parse,\n                        self.variable_scope.clone(),\n                        self.lexical_scope.clone(),\n                        false,\n                        false,\n                        self.interner,\n                        self.in_with,\n                        self.spanned_source_text.clone_only_source(),\n                        self.source_path.clone(),\n                    );\n\n                    // Function environment\n                    field_compiler.code_block_flags |= CodeBlockFlags::HAS_FUNCTION_SCOPE;\n                    let _ = field_compiler.push_scope(field.scope());\n                    let value = field_compiler.register_allocator.alloc();\n                    let is_anonymous_function = if let Some(node) = &field.initializer() {\n                        field_compiler.compile_expr(node, &value);\n                        node.is_anonymous_function_definition()\n                    } else {\n                        field_compiler\n                            .bytecode\n                            .emit_store_undefined(value.variable());\n                        false\n                    };\n\n                    field_compiler\n                        .bytecode\n                        .emit_set_accumulator(value.variable());\n                    field_compiler.register_allocator.dealloc(value);\n\n                    field_compiler.code_block_flags |= CodeBlockFlags::IN_CLASS_FIELD_INITIALIZER;\n\n                    let code = Gc::new(field_compiler.finish());\n                    let index = self.push_function_to_constants(code);\n\n                    let dst = self.register_allocator.alloc();\n                    self.emit_get_function(&dst, index);\n                    self.bytecode.emit_push_class_field(\n                        class_register.variable(),\n                        name.variable(),\n                        dst.variable(),\n                        is_anonymous_function.into(),\n                    );\n                    self.register_allocator.dealloc(name);\n                    self.register_allocator.dealloc(dst);\n                }\n                ClassElement::PrivateFieldDefinition(field) => {\n                    let name_index = self.get_or_insert_private_name(*field.name());\n                    let mut field_compiler = ByteCompiler::new(\n                        class_name.clone(),\n                        true,\n                        self.json_parse,\n                        self.variable_scope.clone(),\n                        self.lexical_scope.clone(),\n                        false,\n                        false,\n                        self.interner,\n                        self.in_with,\n                        self.spanned_source_text.clone_only_source(),\n                        self.source_path.clone(),\n                    );\n                    field_compiler.code_block_flags |= CodeBlockFlags::HAS_FUNCTION_SCOPE;\n                    let _ = field_compiler.push_scope(field.scope());\n                    let value = field_compiler.register_allocator.alloc();\n                    if let Some(node) = field.initializer() {\n                        field_compiler.compile_expr(node, &value);\n                    } else {\n                        field_compiler\n                            .bytecode\n                            .emit_store_undefined(value.variable());\n                    }\n                    field_compiler\n                        .bytecode\n                        .emit_set_accumulator(value.variable());\n                    field_compiler.register_allocator.dealloc(value);\n\n                    field_compiler.code_block_flags |= CodeBlockFlags::IN_CLASS_FIELD_INITIALIZER;\n\n                    let code = Gc::new(field_compiler.finish());\n                    let index = self.push_function_to_constants(code);\n                    let dst = self.register_allocator.alloc();\n                    self.emit_get_function(&dst, index);\n                    self.bytecode.emit_push_class_field_private(\n                        class_register.variable(),\n                        dst.variable(),\n                        name_index.into(),\n                    );\n                    self.register_allocator.dealloc(dst);\n                }\n                ClassElement::StaticFieldDefinition(field) => {\n                    let name_index = match field.name() {\n                        PropertyName::Literal(name) => {\n                            StaticFieldName::Index(self.get_or_insert_name(name.sym()))\n                        }\n                        PropertyName::Computed(name) => {\n                            let name_register = self.register_allocator.alloc();\n                            self.compile_expr(name, &name_register);\n                            StaticFieldName::Register(name_register)\n                        }\n                    };\n                    let mut field_compiler = ByteCompiler::new(\n                        class_name.clone(),\n                        true,\n                        self.json_parse,\n                        self.variable_scope.clone(),\n                        self.lexical_scope.clone(),\n                        false,\n                        false,\n                        self.interner,\n                        self.in_with,\n                        self.spanned_source_text.clone_only_source(),\n                        self.source_path.clone(),\n                    );\n                    field_compiler.code_block_flags |= CodeBlockFlags::HAS_FUNCTION_SCOPE;\n                    let _ = field_compiler.push_scope(field.scope());\n                    let value = field_compiler.register_allocator.alloc();\n                    let is_anonymous_function = if let Some(node) = &field.initializer() {\n                        field_compiler.compile_expr(node, &value);\n                        node.is_anonymous_function_definition()\n                    } else {\n                        field_compiler\n                            .bytecode\n                            .emit_store_undefined(value.variable());\n                        false\n                    };\n\n                    field_compiler\n                        .bytecode\n                        .emit_set_accumulator(value.variable());\n                    field_compiler.register_allocator.dealloc(value);\n\n                    field_compiler.code_block_flags |= CodeBlockFlags::IN_CLASS_FIELD_INITIALIZER;\n\n                    let code = field_compiler.finish();\n                    let code = Gc::new(code);\n\n                    static_elements.push(StaticElement::StaticField {\n                        code,\n                        name_index,\n                        is_anonymous_function,\n                    });\n                }\n                ClassElement::PrivateStaticFieldDefinition(field) => {\n                    let name_index = self.get_or_insert_private_name(*field.name());\n                    let mut field_compiler = ByteCompiler::new(\n                        class_name.clone(),\n                        true,\n                        self.json_parse,\n                        self.variable_scope.clone(),\n                        self.lexical_scope.clone(),\n                        false,\n                        false,\n                        self.interner,\n                        self.in_with,\n                        self.spanned_source_text.clone_only_source(),\n                        self.source_path.clone(),\n                    );\n                    field_compiler.code_block_flags |= CodeBlockFlags::HAS_FUNCTION_SCOPE;\n                    let _ = field_compiler.push_scope(field.scope());\n                    let value = field_compiler.register_allocator.alloc();\n                    let is_anonymous_function = if let Some(node) = &field.initializer() {\n                        field_compiler.compile_expr(node, &value);\n                        node.is_anonymous_function_definition()\n                    } else {\n                        field_compiler\n                            .bytecode\n                            .emit_store_undefined(value.variable());\n                        false\n                    };\n\n                    field_compiler\n                        .bytecode\n                        .emit_set_accumulator(value.variable());\n                    field_compiler.register_allocator.dealloc(value);\n\n                    field_compiler.code_block_flags |= CodeBlockFlags::IN_CLASS_FIELD_INITIALIZER;\n\n                    let code = field_compiler.finish();\n                    let code = Gc::new(code);\n\n                    static_elements.push(StaticElement::StaticField {\n                        code,\n                        name_index: StaticFieldName::PrivateName(name_index),\n                        is_anonymous_function,\n                    });\n                }\n                ClassElement::StaticBlock(block) => {\n                    let mut compiler = ByteCompiler::new(\n                        Sym::EMPTY_STRING.to_js_string(self.interner()),\n                        true,\n                        false,\n                        self.variable_scope.clone(),\n                        self.lexical_scope.clone(),\n                        false,\n                        false,\n                        self.interner,\n                        self.in_with,\n                        self.spanned_source_text.clone_only_source(),\n                        self.source_path.clone(),\n                    );\n                    compiler.code_block_flags |= CodeBlockFlags::HAS_FUNCTION_SCOPE;\n                    let _ = compiler.push_scope(block.scopes().function_scope());\n\n                    compiler.function_declaration_instantiation(\n                        block.statements(),\n                        &FormalParameterList::default(),\n                        false,\n                        true,\n                        false,\n                        block.scopes(),\n                    );\n\n                    {\n                        let mut compiler = compiler.position_guard(block.statements());\n                        compiler.compile_statement_list(\n                            block.statements().statement_list(),\n                            false,\n                            false,\n                        );\n                    }\n\n                    let code = Gc::new(compiler.finish());\n                    static_elements.push(StaticElement::StaticBlock(code));\n                }\n            }\n        }\n\n        for element in static_elements {\n            match element {\n                StaticElement::StaticBlock(code) => {\n                    let index = self.push_function_to_constants(code);\n                    let function = self.register_allocator.alloc();\n                    self.emit_get_function(&function, index);\n                    self.bytecode\n                        .emit_set_home_object(function.variable(), class_register.variable());\n                    self.push_from_register(&class_register);\n                    self.push_from_register(&function);\n                    self.register_allocator.dealloc(function);\n                    self.bytecode.emit_call(0u32.into());\n                    self.bytecode.emit_pop();\n                }\n                StaticElement::StaticField {\n                    code,\n                    name_index,\n                    is_anonymous_function,\n                } => {\n                    let index = self.push_function_to_constants(code);\n                    let function = self.register_allocator.alloc();\n                    self.emit_get_function(&function, index);\n                    self.bytecode\n                        .emit_set_home_object(function.variable(), class_register.variable());\n                    self.push_from_register(&class_register);\n                    self.push_from_register(&function);\n                    self.register_allocator.dealloc(function);\n                    self.bytecode.emit_call(0u32.into());\n                    let value = self.register_allocator.alloc();\n                    self.pop_into_register(&value);\n                    match name_index {\n                        StaticFieldName::PrivateName(name) => {\n                            self.bytecode.emit_define_private_field(\n                                class_register.variable(),\n                                value.variable(),\n                                name.into(),\n                            );\n                        }\n                        StaticFieldName::Index(name) => {\n                            self.bytecode.emit_define_own_property_by_name(\n                                class_register.variable(),\n                                value.variable(),\n                                name.into(),\n                            );\n                        }\n                        StaticFieldName::Register(key) => {\n                            if is_anonymous_function {\n                                self.bytecode\n                                    .emit_to_property_key(key.variable(), key.variable());\n                                self.bytecode.emit_set_function_name(\n                                    value.variable(),\n                                    key.variable(),\n                                    0u32.into(),\n                                );\n                            }\n                            self.bytecode.emit_define_own_property_by_value(\n                                value.variable(),\n                                key.variable(),\n                                class_register.variable(),\n                            );\n                            self.register_allocator.dealloc(key);\n                        }\n                    }\n\n                    self.register_allocator.dealloc(value);\n                }\n            }\n        }\n\n        self.register_allocator.dealloc(proto_register);\n\n        self.pop_declarative_scope(outer_scope);\n        self.bytecode.emit_pop_private_environment();\n\n        if let Some(dst) = dst {\n            self.bytecode\n                .emit_move(dst.variable(), class_register.variable());\n        } else {\n            self.emit_binding(BindingOpcode::InitVar, class_name, &class_register);\n        }\n\n        self.register_allocator.dealloc(class_register);\n\n        // NOTE: Reset strict mode to before class declaration/expression evaluation.\n        self.code_block_flags.set(CodeBlockFlags::STRICT, strict);\n    }\n}\n"
  },
  {
    "path": "core/engine/src/bytecompiler/declaration/declaration_pattern.rs",
    "content": "use crate::{\n    bytecompiler::{Access, ByteCompiler, Literal, Register, ToJsString},\n    vm::opcode::BindingOpcode,\n};\nuse boa_ast::{\n    pattern::{ArrayPatternElement, ObjectPatternElement, Pattern},\n    property::PropertyName,\n};\nuse thin_vec::ThinVec;\n\nimpl ByteCompiler<'_> {\n    pub(crate) fn compile_declaration_pattern_impl(\n        &mut self,\n        pattern: &Pattern,\n        def: BindingOpcode,\n        object: &Register,\n    ) {\n        match pattern {\n            Pattern::Object(pattern) => {\n                self.bytecode\n                    .emit_value_not_null_or_undefined(object.variable());\n\n                let mut excluded_keys_registers = Vec::new();\n                let rest_exits = pattern.has_rest();\n\n                for binding in pattern.bindings() {\n                    use ObjectPatternElement::{\n                        AssignmentPropertyAccess, AssignmentRestPropertyAccess, Pattern,\n                        RestProperty, SingleName,\n                    };\n\n                    match binding {\n                        //  SingleNameBinding : BindingIdentifier Initializer[opt]\n                        SingleName {\n                            ident,\n                            name,\n                            default_init,\n                        } => {\n                            let dst = self.register_allocator.alloc();\n\n                            match name {\n                                PropertyName::Literal(ident) => {\n                                    self.emit_get_property_by_name(&dst, None, object, ident.sym());\n                                    let key = self.register_allocator.alloc();\n                                    self.emit_store_literal(\n                                        Literal::String(\n                                            self.interner()\n                                                .resolve_expect(ident.sym())\n                                                .into_common(false),\n                                        ),\n                                        &key,\n                                    );\n                                    excluded_keys_registers.push(key);\n                                }\n                                PropertyName::Computed(node) => {\n                                    let key = self.register_allocator.alloc();\n                                    self.compile_expr(node, &key);\n                                    if rest_exits {\n                                        self.bytecode.emit_get_property_by_value_push(\n                                            dst.variable(),\n                                            key.variable(),\n                                            object.variable(),\n                                            object.variable(),\n                                        );\n                                        excluded_keys_registers.push(key);\n                                    } else {\n                                        self.bytecode.emit_get_property_by_value(\n                                            dst.variable(),\n                                            key.variable(),\n                                            object.variable(),\n                                            object.variable(),\n                                        );\n                                        self.register_allocator.dealloc(key);\n                                    }\n                                }\n                            }\n\n                            if let Some(init) = default_init {\n                                let skip = self.jump_if_not_undefined(&dst);\n                                self.compile_expr(init, &dst);\n                                self.patch_jump(skip);\n                            }\n\n                            self.emit_binding(def, ident.to_js_string(self.interner()), &dst);\n                            self.register_allocator.dealloc(dst);\n                        }\n                        //  BindingRestProperty : ... BindingIdentifier\n                        RestProperty { ident } => {\n                            let value = self.register_allocator.alloc();\n                            self.bytecode.emit_store_empty_object(value.variable());\n                            let mut excluded_keys =\n                                ThinVec::with_capacity(excluded_keys_registers.len());\n                            for r in &excluded_keys_registers {\n                                excluded_keys.push(r.variable());\n                            }\n                            self.bytecode.emit_copy_data_properties(\n                                value.variable(),\n                                object.variable(),\n                                excluded_keys,\n                            );\n                            while let Some(r) = excluded_keys_registers.pop() {\n                                self.register_allocator.dealloc(r);\n                            }\n                            self.emit_binding(def, ident.to_js_string(self.interner()), &value);\n                            self.register_allocator.dealloc(value);\n                        }\n                        AssignmentRestPropertyAccess { access } => {\n                            let value = self.register_allocator.alloc();\n                            self.bytecode.emit_store_empty_object(value.variable());\n                            let mut excluded_keys =\n                                ThinVec::with_capacity(excluded_keys_registers.len());\n                            for r in &excluded_keys_registers {\n                                excluded_keys.push(r.variable());\n                            }\n                            self.bytecode.emit_copy_data_properties(\n                                value.variable(),\n                                object.variable(),\n                                excluded_keys,\n                            );\n                            while let Some(r) = excluded_keys_registers.pop() {\n                                self.register_allocator.dealloc(r);\n                            }\n                            self.access_set(Access::Property { access }, |_| &value);\n                            self.register_allocator.dealloc(value);\n                        }\n                        AssignmentPropertyAccess {\n                            name,\n                            access,\n                            default_init,\n                        } => {\n                            let key = self.register_allocator.alloc();\n                            match &name {\n                                PropertyName::Literal(ident) => {\n                                    let key = self.register_allocator.alloc();\n                                    self.emit_store_literal(\n                                        Literal::String(\n                                            self.interner()\n                                                .resolve_expect(ident.sym())\n                                                .into_common(false),\n                                        ),\n                                        &key,\n                                    );\n                                    excluded_keys_registers.push(key);\n                                }\n                                PropertyName::Computed(node) => {\n                                    self.compile_expr(node, &key);\n                                    self.bytecode\n                                        .emit_to_property_key(key.variable(), key.variable());\n                                }\n                            }\n\n                            let dst = self.register_allocator.alloc();\n                            self.access_set(\n                                Access::Property { access },\n                                |compiler: &mut ByteCompiler<'_>| {\n                                    match name {\n                                        PropertyName::Literal(ident) => {\n                                            compiler.emit_get_property_by_name(\n                                                &dst,\n                                                None,\n                                                object,\n                                                ident.sym(),\n                                            );\n                                            compiler.register_allocator.dealloc(key);\n                                        }\n                                        PropertyName::Computed(_) => {\n                                            if rest_exits {\n                                                compiler.bytecode.emit_get_property_by_value_push(\n                                                    dst.variable(),\n                                                    key.variable(),\n                                                    object.variable(),\n                                                    object.variable(),\n                                                );\n                                                excluded_keys_registers.push(key);\n                                            } else {\n                                                compiler.bytecode.emit_get_property_by_value(\n                                                    dst.variable(),\n                                                    key.variable(),\n                                                    object.variable(),\n                                                    object.variable(),\n                                                );\n                                                compiler.register_allocator.dealloc(key);\n                                            }\n                                        }\n                                    }\n\n                                    if let Some(init) = default_init {\n                                        let skip = compiler.jump_if_not_undefined(&dst);\n                                        compiler.compile_expr(init, &dst);\n                                        compiler.patch_jump(skip);\n                                    }\n\n                                    &dst\n                                },\n                            );\n                            self.register_allocator.dealloc(dst);\n                        }\n                        Pattern {\n                            name,\n                            pattern,\n                            default_init,\n                        } => {\n                            let dst = self.register_allocator.alloc();\n\n                            match name {\n                                PropertyName::Literal(ident) => {\n                                    self.emit_get_property_by_name(&dst, None, object, ident.sym());\n                                }\n                                PropertyName::Computed(node) => {\n                                    let key = self.register_allocator.alloc();\n                                    self.compile_expr(node, &key);\n                                    self.bytecode.emit_get_property_by_value(\n                                        dst.variable(),\n                                        key.variable(),\n                                        object.variable(),\n                                        object.variable(),\n                                    );\n                                    self.register_allocator.dealloc(key);\n                                }\n                            }\n\n                            if let Some(init) = default_init {\n                                let skip = self.jump_if_not_undefined(&dst);\n                                self.compile_expr(init, &dst);\n                                self.patch_jump(skip);\n                            }\n                            self.compile_declaration_pattern(pattern, def, &dst);\n                            self.register_allocator.dealloc(dst);\n                        }\n                    }\n                }\n\n                while let Some(r) = excluded_keys_registers.pop() {\n                    self.register_allocator.dealloc(r);\n                }\n            }\n            Pattern::Array(pattern) => {\n                self.bytecode\n                    .emit_value_not_null_or_undefined(object.variable());\n                self.bytecode.emit_get_iterator(object.variable());\n\n                let handler_index = self.push_handler();\n                for element in pattern.bindings() {\n                    self.compile_array_pattern_element(element, def);\n                }\n\n                let no_exception_thrown = self.jump();\n                self.patch_handler(handler_index);\n\n                let has_exception = self.register_allocator.alloc();\n                let exception = self.register_allocator.alloc();\n                self.bytecode\n                    .emit_maybe_exception(has_exception.variable(), exception.variable());\n\n                let iterator_close_handler = self.push_handler();\n                self.iterator_close(false);\n                self.patch_handler(iterator_close_handler);\n\n                let jump = self.jump_if_false(&has_exception);\n                self.register_allocator.dealloc(has_exception);\n\n                self.bytecode.emit_throw(exception.variable());\n                self.register_allocator.dealloc(exception);\n\n                self.patch_jump(jump);\n                self.bytecode.emit_re_throw();\n\n                self.patch_jump(no_exception_thrown);\n\n                self.iterator_close(false);\n            }\n        }\n    }\n\n    fn compile_array_pattern_element(&mut self, element: &ArrayPatternElement, def: BindingOpcode) {\n        use ArrayPatternElement::{\n            Elision, Pattern, PatternRest, PropertyAccess, PropertyAccessRest, SingleName,\n            SingleNameRest,\n        };\n\n        match element {\n            // ArrayBindingPattern : [ Elision ]\n            Elision => {\n                self.bytecode.emit_iterator_next();\n            }\n            // SingleNameBinding : BindingIdentifier Initializer[opt]\n            SingleName {\n                ident,\n                default_init,\n            } => {\n                self.bytecode.emit_iterator_next();\n                let value = self.register_allocator.alloc();\n                self.bytecode.emit_iterator_done(value.variable());\n                self.if_else(\n                    &value,\n                    |compiler| compiler.bytecode.emit_store_undefined(value.variable()),\n                    |compiler| compiler.bytecode.emit_iterator_value(value.variable()),\n                );\n\n                if let Some(init) = default_init {\n                    let skip = self.jump_if_not_undefined(&value);\n                    self.compile_expr(init, &value);\n                    self.patch_jump(skip);\n                }\n\n                self.emit_binding(def, ident.to_js_string(self.interner()), &value);\n                self.register_allocator.dealloc(value);\n            }\n            PropertyAccess {\n                access,\n                default_init,\n            } => {\n                let value = self.register_allocator.alloc();\n                self.access_set(Access::Property { access }, |compiler| {\n                    compiler.bytecode.emit_iterator_next();\n                    compiler.bytecode.emit_iterator_done(value.variable());\n                    compiler.if_else(\n                        &value,\n                        |compiler| compiler.bytecode.emit_store_undefined(value.variable()),\n                        |compiler| compiler.bytecode.emit_iterator_value(value.variable()),\n                    );\n\n                    if let Some(init) = default_init {\n                        let skip = compiler.jump_if_not_undefined(&value);\n                        compiler.compile_expr(init, &value);\n                        compiler.patch_jump(skip);\n                    }\n\n                    &value\n                });\n                self.register_allocator.dealloc(value);\n            }\n            // BindingElement : BindingPattern Initializer[opt]\n            Pattern {\n                pattern,\n                default_init,\n            } => {\n                self.bytecode.emit_iterator_next();\n                let value = self.register_allocator.alloc();\n                self.bytecode.emit_iterator_done(value.variable());\n                self.if_else(\n                    &value,\n                    |compiler| compiler.bytecode.emit_store_undefined(value.variable()),\n                    |compiler| compiler.bytecode.emit_iterator_value(value.variable()),\n                );\n\n                if let Some(init) = default_init {\n                    let skip = self.jump_if_not_undefined(&value);\n                    self.compile_expr(init, &value);\n                    self.patch_jump(skip);\n                }\n                self.compile_declaration_pattern(pattern, def, &value);\n                self.register_allocator.dealloc(value);\n            }\n            // BindingRestElement : ... BindingIdentifier\n            SingleNameRest { ident } => {\n                let value = self.register_allocator.alloc();\n                self.bytecode.emit_iterator_to_array(value.variable());\n                self.emit_binding(def, ident.to_js_string(self.interner()), &value);\n                self.register_allocator.dealloc(value);\n            }\n            PropertyAccessRest { access } => {\n                let value = self.register_allocator.alloc();\n                self.access_set(Access::Property { access }, |compiler| {\n                    compiler.bytecode.emit_iterator_to_array(value.variable());\n                    &value\n                });\n                self.register_allocator.dealloc(value);\n            }\n            // BindingRestElement : ... BindingPattern\n            PatternRest { pattern } => {\n                let value = self.register_allocator.alloc();\n                self.bytecode.emit_iterator_to_array(value.variable());\n                self.compile_declaration_pattern(pattern, def, &value);\n                self.register_allocator.dealloc(value);\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "core/engine/src/bytecompiler/declaration/mod.rs",
    "content": "mod declaration_pattern;\n"
  },
  {
    "path": "core/engine/src/bytecompiler/declarations.rs",
    "content": "use super::{BindingAccessOpcode, ToJsString};\nuse crate::{\n    Context, JsNativeError, JsResult, SpannedSourceText,\n    bytecompiler::{ByteCompiler, FunctionCompiler, FunctionSpec, NodeKind},\n    vm::{CallFrame, GlobalFunctionBinding, opcode::BindingOpcode},\n};\nuse boa_ast::{\n    Script,\n    declaration::Binding,\n    function::{FormalParameterList, FunctionBody},\n    operations::{\n        LexicallyScopedDeclaration, VarScopedDeclaration, all_private_identifiers_valid,\n        bound_names, lexically_declared_names, lexically_scoped_declarations, var_declared_names,\n        var_scoped_declarations,\n    },\n    scope::{FunctionScopes, Scope},\n    scope_analyzer::EvalDeclarationBindings,\n    visitor::NodeRef,\n};\nuse boa_interner::{JStrRef, Sym};\n\n#[cfg(feature = \"annex-b\")]\nuse boa_ast::operations::annex_b_function_declarations_names;\n\n/// `GlobalDeclarationInstantiation ( script, env )`\n///\n/// This diverges from the specification by separating the context from the compilation process.\n/// Many steps are skipped that are done during bytecode compilation.\n///\n/// More information:\n///  - [ECMAScript reference][spec]\n///\n/// [spec]: https://tc39.es/ecma262/#sec-globaldeclarationinstantiation\n#[cfg(not(feature = \"annex-b\"))]\n#[allow(clippy::unnecessary_wraps)]\n#[allow(clippy::ptr_arg)]\npub(crate) fn global_declaration_instantiation_context(\n    _annex_b_function_names: &mut Vec<Sym>,\n    _script: &Script,\n    _env: &Scope,\n    _context: &mut Context,\n) -> JsResult<()> {\n    Ok(())\n}\n\n/// `GlobalDeclarationInstantiation ( script, env )`\n///\n/// This diverges from the specification by separating the context from the compilation process.\n/// Many steps are skipped that are done during bytecode compilation.\n///\n/// More information:\n///  - [ECMAScript reference][spec]\n///\n/// [spec]: https://tc39.es/ecma262/#sec-globaldeclarationinstantiation\n#[cfg(feature = \"annex-b\")]\npub(crate) fn global_declaration_instantiation_context(\n    annex_b_function_names: &mut Vec<Sym>,\n    script: &Script,\n    env: &Scope,\n    context: &mut Context,\n) -> JsResult<()> {\n    // SKIP: 1. Let lexNames be the LexicallyDeclaredNames of script.\n    // SKIP: 2. Let varNames be the VarDeclaredNames of script.\n    // SKIP: 3. For each element name of lexNames, do\n    // SKIP: 4. For each element name of varNames, do\n\n    // 5. Let varDeclarations be the VarScopedDeclarations of script.\n    // Note: VarScopedDeclarations for a Script node is TopLevelVarScopedDeclarations.\n    let var_declarations = var_scoped_declarations(script);\n\n    // SKIP: 6. Let functionsToInitialize be a new empty List.\n\n    // 7. Let declaredFunctionNames be a new empty List.\n    let mut declared_function_names = Vec::new();\n\n    // 8. For each element d of varDeclarations, in reverse List order, do\n    for declaration in var_declarations.iter().rev() {\n        // a. If d is not either a VariableDeclaration, a ForBinding, or a BindingIdentifier, then\n        // a.i. Assert: d is either a FunctionDeclaration, a GeneratorDeclaration, an AsyncFunctionDeclaration, or an AsyncGeneratorDeclaration.\n        // a.ii. NOTE: If there are multiple function declarations for the same name, the last declaration is used.\n        // a.iii. Let fn be the sole element of the BoundNames of d.\n        let name = match declaration {\n            VarScopedDeclaration::FunctionDeclaration(f) => f.name(),\n            VarScopedDeclaration::GeneratorDeclaration(f) => f.name(),\n            VarScopedDeclaration::AsyncFunctionDeclaration(f) => f.name(),\n            VarScopedDeclaration::AsyncGeneratorDeclaration(f) => f.name(),\n            VarScopedDeclaration::VariableDeclaration(_) => continue,\n        };\n\n        // a.iv. If declaredFunctionNames does not contain fn, then\n        if !declared_function_names.contains(&name.sym()) {\n            // SKIP: 1. Let fnDefinable be ? env.CanDeclareGlobalFunction(fn).\n            // SKIP: 2. If fnDefinable is false, throw a TypeError exception.\n            // 3. Append fn to declaredFunctionNames.\n            declared_function_names.push(name.sym());\n\n            // SKIP: 4. Insert d as the first element of functionsToInitialize.\n        }\n    }\n\n    // // 9. Let declaredVarNames be a new empty List.\n    let mut declared_var_names = Vec::new();\n\n    // 10. For each element d of varDeclarations, do\n    //     a. If d is either a VariableDeclaration, a ForBinding, or a BindingIdentifier, then\n    for declaration in var_declarations {\n        let VarScopedDeclaration::VariableDeclaration(declaration) = declaration else {\n            continue;\n        };\n\n        // i. For each String vn of the BoundNames of d, do\n        for name in bound_names(&declaration) {\n            // 1. If declaredFunctionNames does not contain vn, then\n            if !declared_function_names.contains(&name) {\n                // SKIP: a. Let vnDefinable be ? env.CanDeclareGlobalVar(vn).\n                // SKIP: b. If vnDefinable is false, throw a TypeError exception.\n                // c. If declaredVarNames does not contain vn, then\n                if !declared_var_names.contains(&name) {\n                    // i. Append vn to declaredVarNames.\n                    declared_var_names.push(name);\n                }\n            }\n        }\n    }\n\n    // 11. NOTE: No abnormal terminations occur after this algorithm step if the global object is an ordinary object.\n    //     However, if the global object is a Proxy exotic object it may exhibit behaviours\n    //     that cause abnormal terminations in some of the following steps.\n\n    // 12. NOTE: Annex B.3.2.2 adds additional steps at this point.\n    // 12. Perform the following steps:\n    // a. Let strict be IsStrict of script.\n    // b. If strict is false, then\n    if !script.strict() {\n        let lex_names = lexically_declared_names(script);\n\n        // i. Let declaredFunctionOrVarNames be the list-concatenation of declaredFunctionNames and declaredVarNames.\n        // ii. For each FunctionDeclaration f that is directly contained in the StatementList of a Block, CaseClause,\n        //     or DefaultClause Contained within script, do\n        for f in annex_b_function_declarations_names(script) {\n            // 1. Let F be StringValue of the BindingIdentifier of f.\n            // 2. If replacing the FunctionDeclaration f with a VariableStatement that has F as a BindingIdentifier\n            //    would not produce any Early Errors for script, then\n            if !lex_names.contains(&f) {\n                let f_string = f.to_js_string(context.interner());\n\n                // a. If env.HasLexicalDeclaration(F) is false, then\n                if !env.has_lex_binding(&f_string) {\n                    // i. Let fnDefinable be ? env.CanDeclareGlobalVar(F).\n                    let fn_definable = context.can_declare_global_function(&f_string)?;\n\n                    // ii. If fnDefinable is true, then\n                    if fn_definable {\n                        // i. NOTE: A var binding for F is only instantiated here if it is neither\n                        //          a VarDeclaredName nor the name of another FunctionDeclaration.\n                        // ii. If declaredFunctionOrVarNames does not contain F, then\n                        if !declared_function_names.contains(&f) && !declared_var_names.contains(&f)\n                        {\n                            // i. Perform ? env.CreateGlobalVarBinding(F, false).\n                            context.create_global_var_binding(f_string, false)?;\n\n                            // ii. Append F to declaredFunctionOrVarNames.\n                            declared_function_names.push(f);\n                        }\n                        // iii. When the FunctionDeclaration f is evaluated, perform the following\n                        //      steps in place of the FunctionDeclaration Evaluation algorithm provided in 15.2.6:\n                        //     i. Let genv be the running execution context's VariableEnvironment.\n                        //     ii. Let benv be the running execution context's LexicalEnvironment.\n                        //     iii. Let fobj be ! benv.GetBindingValue(F, false).\n                        //     iv. Perform ? genv.SetMutableBinding(F, fobj, false).\n                        //     v. Return unused.\n                        annex_b_function_names.push(f);\n                    }\n                }\n            }\n        }\n    }\n\n    // SKIP: 13. Let lexDeclarations be the LexicallyScopedDeclarations of script.\n    // SKIP: 14. Let privateEnv be null.\n    // SKIP: 15. For each element d of lexDeclarations, do\n    // SKIP: 16. For each Parse Node f of functionsToInitialize, do\n    // SKIP: 17. For each String vn of declaredVarNames, do\n\n    // 18. Return unused.\n    Ok(())\n}\n\n/// `EvalDeclarationInstantiation ( body, varEnv, lexEnv, privateEnv, strict )`\n///\n/// This diverges from the specification by separating the context from the compilation process.\n/// Many steps are skipped that are done during bytecode compilation.\n///\n/// More information:\n///  - [ECMAScript reference][spec]\n///\n/// [spec]: https://tc39.es/ecma262/#sec-evaldeclarationinstantiation\npub(crate) fn prepare_eval_declaration_instantiation(\n    #[allow(unused, clippy::ptr_arg)] annex_b_function_names: &mut Vec<Sym>,\n    body: &Script,\n    #[allow(unused)] strict: bool,\n    #[allow(unused)] var_env: &Scope,\n    #[allow(unused)] lex_env: &Scope,\n    context: &mut Context,\n) -> JsResult<()> {\n    // SKIP: 3. If strict is false, then\n\n    // 4. Let privateIdentifiers be a new empty List.\n    // 5. Let pointer be privateEnv.\n    // 6. Repeat, while pointer is not null,\n    //     a. For each Private Name binding of pointer.[[Names]], do\n    //         i. If privateIdentifiers does not contain binding.[[Description]],\n    //            append binding.[[Description]] to privateIdentifiers.\n    //     b. Set pointer to pointer.[[OuterPrivateEnvironment]].\n    let private_identifiers = context.vm.frame().environments.private_name_descriptions();\n    let private_identifiers = private_identifiers\n        .into_iter()\n        .map(|ident| {\n            // TODO: Replace JStrRef with JsStr this would eliminate the to_vec call.\n            let ident = ident.to_vec();\n            context\n                .interner()\n                .get(JStrRef::Utf16(&ident))\n                .expect(\"string should be in interner\")\n        })\n        .collect();\n\n    // 7. If AllPrivateIdentifiersValid of body with argument privateIdentifiers is false, throw a SyntaxError exception.\n    if !all_private_identifiers_valid(body, private_identifiers) {\n        return Err(JsNativeError::syntax()\n            .with_message(\"invalid private identifier\")\n            .into());\n    }\n\n    // 2. Let varDeclarations be the VarScopedDeclarations of body.\n    #[cfg(feature = \"annex-b\")]\n    let var_declarations = var_scoped_declarations(body);\n\n    // SKIP: 8. Let functionsToInitialize be a new empty List.\n\n    // 9. Let declaredFunctionNames be a new empty List.\n    #[cfg(feature = \"annex-b\")]\n    let mut declared_function_names = Vec::new();\n\n    // 10. For each element d of varDeclarations, in reverse List order, do\n    #[cfg(feature = \"annex-b\")]\n    for declaration in var_declarations.iter().rev() {\n        // a. If d is not either a VariableDeclaration, a ForBinding, or a BindingIdentifier, then\n        // a.i. Assert: d is either a FunctionDeclaration, a GeneratorDeclaration, an AsyncFunctionDeclaration, or an AsyncGeneratorDeclaration.\n        // a.ii. NOTE: If there are multiple function declarations for the same name, the last declaration is used.\n        // a.iii. Let fn be the sole element of the BoundNames of d.\n        let name = match &declaration {\n            VarScopedDeclaration::FunctionDeclaration(f) => f.name(),\n            VarScopedDeclaration::GeneratorDeclaration(f) => f.name(),\n            VarScopedDeclaration::AsyncFunctionDeclaration(f) => f.name(),\n            VarScopedDeclaration::AsyncGeneratorDeclaration(f) => f.name(),\n            VarScopedDeclaration::VariableDeclaration(_) => continue,\n        };\n\n        // a.iv. If declaredFunctionNames does not contain fn, then\n        if !declared_function_names.contains(&name.sym()) {\n            // SKIP: 1. If varEnv is a Global Environment Record, then\n\n            // 2. Append fn to declaredFunctionNames.\n            declared_function_names.push(name.sym());\n\n            // SKIP: 3. Insert d as the first element of functionsToInitialize.\n        }\n    }\n\n    // 11. NOTE: Annex B.3.2.3 adds additional steps at this point.\n    // 11. If strict is false, then\n    #[cfg(feature = \"annex-b\")]\n    if !strict {\n        let lexically_declared_names = lexically_declared_names(body);\n\n        // a. Let declaredFunctionOrVarNames be the list-concatenation of declaredFunctionNames and declaredVarNames.\n        // b. For each FunctionDeclaration f that is directly contained in the StatementList\n        //    of a Block, CaseClause, or DefaultClause Contained within body, do\n        for f in annex_b_function_declarations_names(body) {\n            // i. Let F be StringValue of the BindingIdentifier of f.\n            // ii. If replacing the FunctionDeclaration f with a VariableStatement that has F\n            //     as a BindingIdentifier would not produce any Early Errors for body, then\n            if !lexically_declared_names.contains(&f) {\n                // 1. Let bindingExists be false.\n                let mut binding_exists = false;\n\n                // 2. Let thisEnv be lexEnv.\n                let mut this_env = lex_env;\n\n                // 3. Assert: The following loop will terminate.\n                // 4. Repeat, while thisEnv is not varEnv,\n                while this_env.scope_index() != lex_env.scope_index() {\n                    let f = f.to_js_string(context.interner());\n\n                    // a. If thisEnv is not an Object Environment Record, then\n                    // i. If ! thisEnv.HasBinding(F) is true, then\n                    if this_env.has_binding(&f) {\n                        // i. Let bindingExists be true.\n                        binding_exists = true;\n                        break;\n                    }\n\n                    // b. Set thisEnv to thisEnv.[[OuterEnv]].\n                    if let Some(outer) = this_env.outer() {\n                        this_env = outer;\n                    } else {\n                        break;\n                    }\n                }\n\n                // 5. If bindingExists is false and varEnv is a Global Environment Record, then\n                let fn_definable = if !binding_exists && var_env.is_global() {\n                    let f = f.to_js_string(context.interner());\n\n                    // a. If varEnv.HasLexicalDeclaration(F) is false, then\n                    // b. Else,\n                    if var_env.has_lex_binding(&f) {\n                        // i. Let fnDefinable be false.\n                        false\n                    } else {\n                        // i. Let fnDefinable be ? varEnv.CanDeclareGlobalVar(F).\n                        context.can_declare_global_var(&f)?\n                    }\n                }\n                // 6. Else,\n                else {\n                    // a. Let fnDefinable be true.\n                    true\n                };\n\n                // 7. If bindingExists is false and fnDefinable is true, then\n                if !binding_exists && fn_definable {\n                    // a. If declaredFunctionOrVarNames does not contain F, then\n                    if !declared_function_names.contains(&f) {\n                        // i. If varEnv is a Global Environment Record, then\n                        if var_env.is_global() {\n                            let f = f.to_js_string(context.interner());\n\n                            // i. Perform ? varEnv.CreateGlobalVarBinding(F, true).\n                            context.create_global_var_binding(f, true)?;\n                        }\n\n                        // SKIP: ii. Else,\n                        // SKIP: iii. Append F to declaredFunctionOrVarNames.\n                    }\n\n                    // b. When the FunctionDeclaration f is evaluated, perform the following steps\n                    //    in place of the FunctionDeclaration Evaluation algorithm provided in 15.2.6:\n                    //     i. Let genv be the running execution context's VariableEnvironment.\n                    //     ii. Let benv be the running execution context's LexicalEnvironment.\n                    //     iii. Let fobj be ! benv.GetBindingValue(F, false).\n                    //     iv. Perform ? genv.SetMutableBinding(F, fobj, false).\n                    //     v. Return unused.\n                    annex_b_function_names.push(f);\n                }\n            }\n        }\n    }\n\n    // SKIP: 12. Let declaredVarNames be a new empty List.\n    // SKIP: 13. For each element d of varDeclarations, do\n    // SKIP: 14. NOTE: No abnormal terminations occur after this algorithm step unless varEnv is a\n    //           Global Environment Record and the global object is a Proxy exotic object.\n    // SKIP: 15. Let lexDeclarations be the LexicallyScopedDeclarations of body.\n    // SKIP: 16. For each element d of lexDeclarations, do\n    // SKIP: 17. For each Parse Node f of functionsToInitialize, do\n    // SKIP: 18. For each String vn of declaredVarNames, do\n\n    // 19. Return unused.\n    Ok(())\n}\n\nimpl ByteCompiler<'_> {\n    /// `GlobalDeclarationInstantiation ( script, env )`\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-globaldeclarationinstantiation\n    pub(crate) fn global_declaration_instantiation(&mut self, script: &Script) {\n        // 1. Let lexNames be the LexicallyDeclaredNames of script.\n        let lex_names = lexically_declared_names(script);\n\n        // 2. Let varNames be the VarDeclaredNames of script.\n        // 3. For each element name of lexNames, do\n        for name in lex_names {\n            let name = name.to_js_string(self.interner());\n\n            // c. Let hasRestrictedGlobal be ? env.HasRestrictedGlobalProperty(name).\n            // d. If hasRestrictedGlobal is true, throw a SyntaxError exception.\n            // Done in `Context::global_declaration_instantiation`\n            let index = self.get_or_insert_string(name);\n            self.global_lexs.push(index);\n        }\n        // 4. For each element name of varNames, do\n        //    a. If HasLexicalDeclaration(env, name) is true, throw a SyntaxError exception.\n        // The scope analyzer already does this check for us.\n\n        // 5. Let varDeclarations be the VarScopedDeclarations of script.\n        // Note: VarScopedDeclarations for a Script node is TopLevelVarScopedDeclarations.\n        let var_declarations = var_scoped_declarations(script);\n\n        // 6. Let functionsToInitialize be a new empty List.\n        let mut functions_to_initialize = Vec::new();\n\n        // 7. Let declaredFunctionNames be a new empty List.\n        let mut declared_function_names = Vec::new();\n\n        // 8. For each element d of varDeclarations, in reverse List order, do\n        for declaration in var_declarations.iter().rev() {\n            // a. If d is not either a VariableDeclaration, a ForBinding, or a BindingIdentifier, then\n            // a.i. Assert: d is either a FunctionDeclaration, a GeneratorDeclaration, an AsyncFunctionDeclaration, or an AsyncGeneratorDeclaration.\n            // a.ii. NOTE: If there are multiple function declarations for the same name, the last declaration is used.\n            // a.iii. Let fn be the sole element of the BoundNames of d.\n            let name = match declaration {\n                VarScopedDeclaration::FunctionDeclaration(f) => f.name(),\n                VarScopedDeclaration::GeneratorDeclaration(f) => f.name(),\n                VarScopedDeclaration::AsyncFunctionDeclaration(f) => f.name(),\n                VarScopedDeclaration::AsyncGeneratorDeclaration(f) => f.name(),\n                VarScopedDeclaration::VariableDeclaration(_) => continue,\n            };\n\n            // a.iv. If declaredFunctionNames does not contain fn, then\n            if !declared_function_names.contains(&name.sym()) {\n                // 1. Let fnDefinable be ? env.CanDeclareGlobalFunction(fn).\n                // 2. If fnDefinable is false, throw a TypeError exception.\n                // Done in `Context::global_declaration_instantiation`.\n                // The names checked here are the same names from the functions\n                // in `functions_to_initialize`, but in reverse order, so we can\n                // reuse `global_fns` for this check.\n\n                // 3. Append fn to declaredFunctionNames.\n                declared_function_names.push(name.sym());\n\n                // 4. Insert d as the first element of functionsToInitialize.\n                functions_to_initialize.push(declaration.clone());\n            }\n        }\n\n        functions_to_initialize.reverse();\n\n        // 9. Let declaredVarNames be a new empty List.\n        let mut declared_var_names = Vec::new();\n\n        // 10. For each element d of varDeclarations, do\n        //     a. If d is either a VariableDeclaration, a ForBinding, or a BindingIdentifier, then\n        for declaration in var_declarations {\n            let VarScopedDeclaration::VariableDeclaration(declaration) = declaration else {\n                continue;\n            };\n\n            // i. For each String vn of the BoundNames of d, do\n            for name in bound_names(&declaration) {\n                // 1. If declaredFunctionNames does not contain vn, then\n                if !declared_function_names.contains(&name) {\n                    // a. Let vnDefinable be ? env.CanDeclareGlobalVar(vn).\n                    // b. If vnDefinable is false, throw a TypeError exception.\n                    // Done in `Context::global_declaration_instantiation`\n                    // The names checked here are the same names from the functions\n                    // in `declared_var_names`, so we can reuse `global_vars`\n                    // for this check.\n\n                    // c. If declaredVarNames does not contain vn, then\n                    if !declared_var_names.contains(&name) {\n                        // i. Append vn to declaredVarNames.\n                        declared_var_names.push(name);\n                    }\n                }\n            }\n        }\n\n        // 11. NOTE: No abnormal terminations occur after this algorithm step if the\n        //     global object is an ordinary object. However, if the global object is\n        //     a Proxy exotic object it may exhibit behaviours that cause abnormal\n        //     terminations in some of the following steps.\n\n        // Steps 13-15 are covered by the scope analyzer.\n\n        // 16. For each Parse Node f of functionsToInitialize, do\n        for function in functions_to_initialize {\n            // a. Let fn be the sole element of the BoundNames of f.\n            let (name, generator, r#async, parameters, body, scopes, contains_direct_eval) =\n                match &function {\n                    VarScopedDeclaration::FunctionDeclaration(f) => (\n                        f.name(),\n                        false,\n                        false,\n                        f.parameters(),\n                        f.body(),\n                        f.scopes().clone(),\n                        f.contains_direct_eval(),\n                    ),\n                    VarScopedDeclaration::GeneratorDeclaration(f) => (\n                        f.name(),\n                        true,\n                        false,\n                        f.parameters(),\n                        f.body(),\n                        f.scopes().clone(),\n                        f.contains_direct_eval(),\n                    ),\n                    VarScopedDeclaration::AsyncFunctionDeclaration(f) => (\n                        f.name(),\n                        false,\n                        true,\n                        f.parameters(),\n                        f.body(),\n                        f.scopes().clone(),\n                        f.contains_direct_eval(),\n                    ),\n                    VarScopedDeclaration::AsyncGeneratorDeclaration(f) => (\n                        f.name(),\n                        true,\n                        true,\n                        f.parameters(),\n                        f.body(),\n                        f.scopes().clone(),\n                        f.contains_direct_eval(),\n                    ),\n                    VarScopedDeclaration::VariableDeclaration(_) => continue,\n                };\n\n            let func_span = function.linear_span();\n            let spanned_source_text = SpannedSourceText::new(self.source_text(), func_span);\n\n            let code = FunctionCompiler::new(spanned_source_text)\n                .name(name.sym().to_js_string(self.interner()))\n                .generator(generator)\n                .r#async(r#async)\n                .strict(self.strict())\n                .in_with(self.in_with)\n                .source_path(self.source_path.clone())\n                .compile(\n                    parameters,\n                    body,\n                    self.variable_scope.clone(),\n                    self.lexical_scope.clone(),\n                    &scopes,\n                    contains_direct_eval,\n                    self.interner,\n                );\n\n            // Ensures global functions are printed when generating the global flowgraph.\n            let name_index = self.get_or_insert_name(name.sym());\n            let function_index = self.push_function_to_constants(code);\n\n            // b. Let fo be InstantiateFunctionObject of f with arguments env and privateEnv.\n            // c. Perform ? env.CreateGlobalFunctionBinding(fn, fo, false).\n            // Done in `Context::global_declaration_instantiation`\n            self.global_fns.push(GlobalFunctionBinding {\n                name_index,\n                function_index,\n            });\n        }\n\n        // 17 is done in `Context::global_declaration_instantiation\n\n        // 17. For each String vn of declaredVarNames, do\n        for var in declared_var_names {\n            let index = self.get_or_insert_name(var);\n            self.global_vars.push(index);\n\n            // a. Perform ? env.CreateGlobalVarBinding(vn, false).\n            // Done in `Context::global_declaration_instantiation`\n        }\n\n        // 18. Return unused.\n    }\n\n    /// `BlockDeclarationInstantiation ( code, env )`\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-blockdeclarationinstantiation\n    pub(crate) fn block_declaration_instantiation<'a, N>(&mut self, block: &'a N)\n    where\n        &'a N: Into<NodeRef<'a>>,\n    {\n        // 1. Let declarations be the LexicallyScopedDeclarations of code.\n        let declarations = lexically_scoped_declarations(block);\n\n        // Note: Not sure if the spec is wrong here or if our implementation just differs too much,\n        //       but we need 3.a to be finished for all declarations before 3.b can be done.\n\n        // b. If d is either a FunctionDeclaration, a GeneratorDeclaration, an AsyncFunctionDeclaration, or an AsyncGeneratorDeclaration, then\n        //     i. Let fn be the sole element of the BoundNames of d.\n        //     ii. Let fo be InstantiateFunctionObject of d with arguments env and privateEnv.\n        //     iii. Perform ! env.InitializeBinding(fn, fo). NOTE: This step is replaced in section B.3.2.6.\n        // TODO: Support B.3.2.6.\n        for d in declarations {\n            match d {\n                LexicallyScopedDeclaration::FunctionDeclaration(function) => {\n                    let dst = self.register_allocator.alloc();\n                    self.function_with_binding(function.into(), NodeKind::Declaration, &dst);\n                    self.register_allocator.dealloc(dst);\n                }\n                LexicallyScopedDeclaration::GeneratorDeclaration(function) => {\n                    let dst = self.register_allocator.alloc();\n                    self.function_with_binding(function.into(), NodeKind::Declaration, &dst);\n                    self.register_allocator.dealloc(dst);\n                }\n                LexicallyScopedDeclaration::AsyncFunctionDeclaration(function) => {\n                    let dst = self.register_allocator.alloc();\n                    self.function_with_binding(function.into(), NodeKind::Declaration, &dst);\n                    self.register_allocator.dealloc(dst);\n                }\n                LexicallyScopedDeclaration::AsyncGeneratorDeclaration(function) => {\n                    let dst = self.register_allocator.alloc();\n                    self.function_with_binding(function.into(), NodeKind::Declaration, &dst);\n                    self.register_allocator.dealloc(dst);\n                }\n                _ => {}\n            }\n        }\n\n        // 4. Return unused.\n    }\n\n    /// `EvalDeclarationInstantiation ( body, varEnv, lexEnv, privateEnv, strict )`\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-evaldeclarationinstantiation\n    pub(crate) fn eval_declaration_instantiation(\n        &mut self,\n        body: &Script,\n        #[allow(\n            unused_variables,\n            reason = \"only used when the `annex-b` feature is enabled\"\n        )]\n        strict: bool,\n        var_env: &Scope,\n        bindings: EvalDeclarationBindings,\n    ) {\n        // 2. Let varDeclarations be the VarScopedDeclarations of body.\n        let var_declarations = var_scoped_declarations(body);\n\n        // SKIP: 3. If strict is false, then\n        // covered by the scope analyzer.\n\n        // NOTE: These steps depend on the current environment state are done before bytecode compilation,\n        //       in `eval_declaration_instantiation_context`.\n        //\n        // SKIP: 4. Let privateIdentifiers be a new empty List.\n        // SKIP: 5. Let pointer be privateEnv.\n        // SKIP: 6. Repeat, while pointer is not null,\n        //           a. For each Private Name binding of pointer.[[Names]], do\n        //               i. If privateIdentifiers does not contain binding.[[Description]],\n        //                  append binding.[[Description]] to privateIdentifiers.\n        //           b. Set pointer to pointer.[[OuterPrivateEnvironment]].\n        // SKIP: 7. If AllPrivateIdentifiersValid of body with argument privateIdentifiers is false, throw a SyntaxError exception.\n\n        // 8. Let functionsToInitialize be a new empty List.\n        let mut functions_to_initialize = Vec::new();\n\n        // 9. Let declaredFunctionNames be a new empty List.\n        let mut declared_function_names = Vec::new();\n\n        // 10. For each element d of varDeclarations, in reverse List order, do\n        for declaration in var_declarations.iter().rev() {\n            // a. If d is not either a VariableDeclaration, a ForBinding, or a BindingIdentifier, then\n            // a.i. Assert: d is either a FunctionDeclaration, a GeneratorDeclaration, an AsyncFunctionDeclaration, or an AsyncGeneratorDeclaration.\n            // a.ii. NOTE: If there are multiple function declarations for the same name, the last declaration is used.\n            // a.iii. Let fn be the sole element of the BoundNames of d.\n            let name = match &declaration {\n                VarScopedDeclaration::FunctionDeclaration(f) => f.name(),\n                VarScopedDeclaration::GeneratorDeclaration(f) => f.name(),\n                VarScopedDeclaration::AsyncFunctionDeclaration(f) => f.name(),\n                VarScopedDeclaration::AsyncGeneratorDeclaration(f) => f.name(),\n                VarScopedDeclaration::VariableDeclaration(_) => continue,\n            };\n            // a.iv. If declaredFunctionNames does not contain fn, then\n            if !declared_function_names.contains(&name.sym()) {\n                // 1. If varEnv is a Global Environment Record, then\n                //    a. Let fnDefinable be ? varEnv.CanDeclareGlobalFunction(fn).\n                //    b. If fnDefinable is false, throw a TypeError exception.\n                //    Done in `Context::eval_declaration_instantiation`\n                //    The names checked here are the same names from the functions\n                //    in `functions_to_initialize`, but in reverse order, so we can\n                //    reuse `global_fns` for this check.\n\n                // 2. Append fn to declaredFunctionNames.\n                declared_function_names.push(name.sym());\n\n                // 3. Insert d as the first element of functionsToInitialize.\n                functions_to_initialize.push(declaration.clone());\n            }\n        }\n\n        functions_to_initialize.reverse();\n\n        // 11. NOTE: Annex B.3.2.3 adds additional steps at this point.\n        // 11. If strict is false, then\n        #[cfg(feature = \"annex-b\")]\n        if !strict {\n            // NOTE: This diviates from the specification, we split the first part of defining the annex-b names\n            //       in `eval_declaration_instantiation_context`, because it depends on the context.\n            if !var_env.is_global() {\n                for binding in bindings.new_annex_b_function_names {\n                    // i. Let bindingExists be ! varEnv.HasBinding(F).\n                    // ii. If bindingExists is false, then\n                    // i. Perform ! varEnv.CreateMutableBinding(F, true).\n                    // ii. Perform ! varEnv.InitializeBinding(F, undefined).\n\n                    use crate::vm::CallFrame;\n                    let index = self.insert_binding(binding);\n                    self.emit_binding_access(\n                        BindingAccessOpcode::DefInitVar,\n                        &index,\n                        &CallFrame::undefined_register(),\n                    );\n                }\n            }\n        }\n\n        // 12. Let declaredVarNames be a new empty List.\n        let mut declared_var_names = Vec::new();\n\n        // 13. For each element d of varDeclarations, do\n        for declaration in var_declarations {\n            // a. If d is either a VariableDeclaration, a ForBinding, or a BindingIdentifier, then\n            let VarScopedDeclaration::VariableDeclaration(declaration) = declaration else {\n                continue;\n            };\n\n            // a.i. For each String vn of the BoundNames of d, do\n            for name in bound_names(&declaration) {\n                // 1. If declaredFunctionNames does not contain vn, then\n                if !declared_function_names.contains(&name) {\n                    // a. If varEnv is a Global Environment Record, then\n                    //    i. Let vnDefinable be ? varEnv.CanDeclareGlobalVar(vn).\n                    //    ii. If vnDefinable is false, throw a TypeError exception.\n                    //    Done in `Context::eval_declaration_instantiation`\n                    //    The names checked here are the same names from the functions\n                    //    in `declared_var_names`, so we can reuse `global_vars`\n                    //    for this check.\n\n                    // b. If declaredVarNames does not contain vn, then\n                    if !declared_var_names.contains(&name) {\n                        // i. Append vn to declaredVarNames.\n                        declared_var_names.push(name);\n                    }\n                }\n            }\n        }\n\n        // 14. NOTE: No abnormal terminations occur after this algorithm step unless varEnv is a\n        //           Global Environment Record and the global object is a Proxy exotic object.\n\n        // 15. Let lexDeclarations be the LexicallyScopedDeclarations of body.\n        // 16. For each element d of lexDeclarations, do\n\n        // 17. For each Parse Node f of functionsToInitialize, do\n        for function in functions_to_initialize {\n            // a. Let fn be the sole element of the BoundNames of f.\n            let (name, generator, r#async, parameters, body, scopes, contains_direct_eval) =\n                match &function {\n                    VarScopedDeclaration::FunctionDeclaration(f) => (\n                        f.name(),\n                        false,\n                        false,\n                        f.parameters(),\n                        f.body(),\n                        f.scopes().clone(),\n                        f.contains_direct_eval(),\n                    ),\n                    VarScopedDeclaration::GeneratorDeclaration(f) => (\n                        f.name(),\n                        true,\n                        false,\n                        f.parameters(),\n                        f.body(),\n                        f.scopes().clone(),\n                        f.contains_direct_eval(),\n                    ),\n                    VarScopedDeclaration::AsyncFunctionDeclaration(f) => (\n                        f.name(),\n                        false,\n                        true,\n                        f.parameters(),\n                        f.body(),\n                        f.scopes().clone(),\n                        f.contains_direct_eval(),\n                    ),\n                    VarScopedDeclaration::AsyncGeneratorDeclaration(f) => (\n                        f.name(),\n                        true,\n                        true,\n                        f.parameters(),\n                        f.body(),\n                        f.scopes().clone(),\n                        f.contains_direct_eval(),\n                    ),\n                    VarScopedDeclaration::VariableDeclaration(_) => {\n                        continue;\n                    }\n                };\n\n            let func_span = function.linear_span();\n            let spanned_source_text = SpannedSourceText::new(self.source_text(), func_span);\n\n            let code = FunctionCompiler::new(spanned_source_text)\n                .name(name.sym().to_js_string(self.interner()))\n                .generator(generator)\n                .r#async(r#async)\n                .strict(self.strict())\n                .in_with(self.in_with)\n                .name_scope(None)\n                .compile(\n                    parameters,\n                    body,\n                    self.variable_scope.clone(),\n                    self.lexical_scope.clone(),\n                    &scopes,\n                    contains_direct_eval,\n                    self.interner,\n                );\n\n            // b. Let fo be InstantiateFunctionObject of f with arguments lexEnv and privateEnv.\n            let index = self.push_function_to_constants(code);\n\n            // c. If varEnv is a Global Environment Record, then\n            if var_env.is_global() {\n                // i. Perform ? varEnv.CreateGlobalFunctionBinding(fn, fo, true).\n                // Done in `Context::eval_declaration_instantiation`\n                let name_index = self.get_or_insert_name(name.sym());\n                self.global_fns.push(GlobalFunctionBinding {\n                    name_index,\n                    function_index: index,\n                });\n            }\n            // d. Else,\n            else {\n                let dst = self.register_allocator.alloc();\n                self.emit_get_function(&dst, index);\n\n                // i. Let bindingExists be ! varEnv.HasBinding(fn).\n                let (binding, binding_exists) = bindings\n                    .new_function_names\n                    .get(&name)\n                    .expect(\"binding must exist\");\n\n                // ii. If bindingExists is false, then\n                // iii. Else,\n                if *binding_exists {\n                    // 1. Perform ! varEnv.SetMutableBinding(fn, fo, false).\n                    let index = self.insert_binding(binding.clone());\n                    self.emit_binding_access(BindingAccessOpcode::SetName, &index, &dst);\n                } else {\n                    // 1. NOTE: The following invocation cannot return an abrupt completion because of the validation preceding step 14.\n                    // 2. Perform ! varEnv.CreateMutableBinding(fn, true).\n                    // 3. Perform ! varEnv.InitializeBinding(fn, fo).\n                    let index = self.insert_binding(binding.clone());\n                    self.emit_binding_access(BindingAccessOpcode::DefInitVar, &index, &dst);\n                }\n                self.register_allocator.dealloc(dst);\n            }\n        }\n\n        // 18. For each String vn of declaredVarNames, do\n        for name in declared_var_names {\n            // a. If varEnv is a Global Environment Record, then\n            if var_env.is_global() {\n                let index = self.get_or_insert_name(name);\n\n                // i. Perform ? varEnv.CreateGlobalVarBinding(vn, true).\n                // Done in `Context::eval_declaration_instantiation`\n                self.global_vars.push(index);\n            }\n        }\n        // 18.b\n        for binding in bindings.new_var_names {\n            // i. Let bindingExists be ! varEnv.HasBinding(vn).\n            // ii. If bindingExists is false, then\n            // 1. NOTE: The following invocation cannot return an abrupt completion because of the validation preceding step 14.\n            // 2. Perform ! varEnv.CreateMutableBinding(vn, true).\n            // 3. Perform ! varEnv.InitializeBinding(vn, undefined).\n            let index = self.insert_binding(binding);\n            self.emit_binding_access(\n                BindingAccessOpcode::DefInitVar,\n                &index,\n                &CallFrame::undefined_register(),\n            );\n        }\n\n        // 19. Return unused.\n    }\n\n    /// `FunctionDeclarationInstantiation ( func, argumentsList )`\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-functiondeclarationinstantiation\n    pub(crate) fn function_declaration_instantiation(\n        &mut self,\n        body: &FunctionBody,\n        formals: &FormalParameterList,\n        arrow: bool,\n        strict: bool,\n        generator: bool,\n        scopes: &FunctionScopes,\n    ) {\n        // 1. Let calleeContext be the running execution context.\n        // 2. Let code be func.[[ECMAScriptCode]].\n        // 3. Let strict be func.[[Strict]].\n        // 4. Let formals be func.[[FormalParameters]].\n\n        // 5. Let parameterNames be the BoundNames of formals.\n        let mut parameter_names = bound_names(formals);\n\n        // 6. If parameterNames has any duplicate entries, let hasDuplicates be true. Otherwise, let hasDuplicates be false.\n        // let has_duplicates = formals.has_duplicates();\n\n        // 7. Let simpleParameterList be IsSimpleParameterList of formals.\n        // let simple_parameter_list = formals.is_simple();\n\n        // 8. Let hasParameterExpressions be ContainsExpression of formals.\n        let has_parameter_expressions = formals.has_expressions();\n\n        // 9. Let varNames be the VarDeclaredNames of code.\n        let var_names = var_declared_names(body);\n\n        // 10. Let varDeclarations be the VarScopedDeclarations of code.\n        let var_declarations = var_scoped_declarations(body);\n\n        // 11. Let lexicalNames be the LexicallyDeclaredNames of code.\n        let lexical_names = lexically_declared_names(body);\n\n        // 12. Let functionNames be a new empty List.\n        let mut function_names = Vec::new();\n\n        // 13. Let functionsToInitialize be a new empty List.\n        let mut functions_to_initialize = Vec::new();\n\n        // 14. For each element d of varDeclarations, in reverse List order, do\n        for declaration in var_declarations.iter().rev() {\n            // a. If d is neither a VariableDeclaration nor a ForBinding nor a BindingIdentifier, then\n            // a.i. Assert: d is either a FunctionDeclaration, a GeneratorDeclaration, an AsyncFunctionDeclaration, or an AsyncGeneratorDeclaration.\n            // a.ii. Let fn be the sole element of the BoundNames of d.\n            let (name, function) = match declaration {\n                VarScopedDeclaration::FunctionDeclaration(f) => (f.name(), FunctionSpec::from(f)),\n                VarScopedDeclaration::GeneratorDeclaration(f) => (f.name(), FunctionSpec::from(f)),\n                VarScopedDeclaration::AsyncFunctionDeclaration(f) => {\n                    (f.name(), FunctionSpec::from(f))\n                }\n                VarScopedDeclaration::AsyncGeneratorDeclaration(f) => {\n                    (f.name(), FunctionSpec::from(f))\n                }\n                VarScopedDeclaration::VariableDeclaration(_) => continue,\n            };\n\n            // a.iii. If functionNames does not contain fn, then\n            if !function_names.contains(&name.sym()) {\n                // 1. Insert fn as the first element of functionNames.\n                function_names.push(name.sym());\n\n                // 2. NOTE: If there are multiple function declarations for the same name, the last declaration is used.\n                // 3. Insert d as the first element of functionsToInitialize.\n                functions_to_initialize.push(function);\n            }\n        }\n\n        function_names.reverse();\n        functions_to_initialize.reverse();\n\n        // 15. Let argumentsObjectNeeded be true.\n        let mut arguments_object_needed = true;\n\n        let arguments = Sym::ARGUMENTS;\n\n        // 16. If func.[[ThisMode]] is lexical, then\n        // 17. Else if parameterNames contains \"arguments\", then\n        if arrow || parameter_names.contains(&arguments) {\n            // 16.a. NOTE: Arrow functions never have an arguments object.\n            // 16.b. Set argumentsObjectNeeded to false.\n            // 17.a. Set argumentsObjectNeeded to false.\n            arguments_object_needed = false;\n        }\n        // 18. Else if hasParameterExpressions is false, then\n        else if !has_parameter_expressions {\n            //a. If functionNames contains \"arguments\" or lexicalNames contains \"arguments\", then\n            if function_names.contains(&arguments) || lexical_names.contains(&arguments) {\n                // i. Set argumentsObjectNeeded to false.\n                arguments_object_needed = false;\n            }\n        }\n\n        if arguments_object_needed {\n            arguments_object_needed = scopes.arguments_object_accessed();\n        }\n\n        // 19-20\n        drop(self.push_declarative_scope(scopes.parameters_eval_scope()));\n\n        let scope = self.lexical_scope.clone();\n\n        // 22. If argumentsObjectNeeded is true, then\n        //\n        // NOTE(HalidOdat): Has been moved up, so \"arguments\" gets registered as\n        //     the first binding in the environment with index 0.\n        if arguments_object_needed {\n            let arguments = arguments.to_js_string(self.interner());\n\n            // a. If strict is true or simpleParameterList is false, then\n            let value = self.register_allocator.alloc();\n            if strict || !formals.is_simple() {\n                // i. Let ao be CreateUnmappedArgumentsObject(argumentsList).\n                self.bytecode\n                    .emit_create_unmapped_arguments_object(value.variable());\n            }\n            // b. Else,\n            else {\n                // i. NOTE: A mapped argument object is only provided for non-strict functions\n                //          that don't have a rest parameter, any parameter\n                //          default value initializers, or any destructured parameters.\n                // ii. Let ao be CreateMappedArgumentsObject(func, formals, argumentsList, env).\n                self.bytecode\n                    .emit_create_mapped_arguments_object(value.variable());\n                self.emitted_mapped_arguments_object_opcode = true;\n            }\n\n            // e. Perform ! env.InitializeBinding(\"arguments\", ao).\n            self.emit_binding(BindingOpcode::InitLexical, arguments, &value);\n            self.register_allocator.dealloc(value);\n        }\n\n        // 22. If argumentsObjectNeeded is true, then\n        if arguments_object_needed {\n            // MOVED: a-e.\n            //\n            // NOTE(HalidOdat): Has been moved up, see comment above.\n\n            // f. Let parameterBindings be the list-concatenation of parameterNames and « \"arguments\" ».\n            parameter_names.push(arguments);\n        }\n\n        // 23. Else,\n        //     a. Let parameterBindings be parameterNames.\n        let parameter_bindings = parameter_names.clone();\n\n        // 24. Let iteratorRecord be CreateListIteratorRecord(argumentsList).\n        // 25. If hasDuplicates is true, then\n        //    a. Perform ? IteratorBindingInitialization of formals with arguments iteratorRecord and undefined.\n        // 26. Else,\n        //    a. Perform ? IteratorBindingInitialization of formals with arguments iteratorRecord and env.\n        for (i, parameter) in formals.as_ref().iter().enumerate() {\n            let value = self.register_allocator.alloc();\n            if parameter.is_rest_param() {\n                self.bytecode.emit_rest_parameter_init(value.variable());\n            } else {\n                self.bytecode\n                    .emit_get_argument((i as u32).into(), value.variable());\n            }\n\n            match parameter.variable().binding() {\n                Binding::Identifier(ident) => {\n                    let ident = ident.to_js_string(self.interner());\n                    if let Some(init) = parameter.variable().init() {\n                        let skip = self.jump_if_not_undefined(&value);\n                        self.compile_expr(init, &value);\n                        self.patch_jump(skip);\n                    }\n                    self.emit_binding(BindingOpcode::InitLexical, ident, &value);\n                }\n                Binding::Pattern(pattern) => {\n                    if let Some(init) = parameter.variable().init() {\n                        let skip = self.jump_if_not_undefined(&value);\n                        self.compile_expr(init, &value);\n                        self.patch_jump(skip);\n                    }\n                    self.compile_declaration_pattern(pattern, BindingOpcode::InitLexical, &value);\n                }\n            }\n            self.register_allocator.dealloc(value);\n        }\n\n        if generator {\n            if self.is_async() {\n                self.bytecode.emit_async_generator();\n            } else {\n                self.bytecode.emit_generator();\n            }\n            self.bytecode.emit_pop();\n        }\n\n        // 27. If hasParameterExpressions is false, then\n        // 28. Else,\n        #[allow(unused_variables, unused_mut)]\n        let (mut instantiated_var_names, mut variable_scope) =\n            if let Some(scope) = scopes.parameters_scope() {\n                // a. NOTE: A separate Environment Record is needed to ensure that closures created by\n                //          expressions in the formal parameter list do not have\n                //          visibility of declarations in the function body.\n                // b. Let varEnv be NewDeclarativeEnvironment(env).\n                // c. Set the VariableEnvironment of calleeContext to varEnv.\n                drop(self.push_declarative_scope(Some(scope)));\n\n                let mut variable_scope = self.lexical_scope.clone();\n\n                // d. Let instantiatedVarNames be a new empty List.\n                let mut instantiated_var_names = Vec::new();\n\n                // e. For each element n of varNames, do\n                for n in var_names {\n                    // i. If instantiatedVarNames does not contain n, then\n                    if !instantiated_var_names.contains(&n) {\n                        // 1. Append n to instantiatedVarNames.\n                        instantiated_var_names.push(n);\n\n                        let n_string = n.to_js_string(self.interner());\n\n                        // 2. Perform ! varEnv.CreateMutableBinding(n, false).\n                        let binding = variable_scope\n                            .get_binding_reference(&n_string)\n                            .expect(\"must have binding\");\n\n                        let value = self.register_allocator.alloc();\n                        // 3. If parameterBindings does not contain n, or if functionNames contains n, then\n                        if !parameter_bindings.contains(&n) || function_names.contains(&n) {\n                            // a. Let initialValue be undefined.\n                            self.bytecode.emit_store_undefined(value.variable());\n                        }\n                        // 4. Else,\n                        else {\n                            // a. Let initialValue be ! env.GetBindingValue(n, false).\n                            let binding = scope\n                                .get_binding_reference(&n_string)\n                                .expect(\"must have binding\");\n                            let index = self.get_binding(&binding);\n                            self.emit_binding_access(BindingAccessOpcode::GetName, &index, &value);\n                        }\n\n                        // 5. Perform ! varEnv.InitializeBinding(n, initialValue).\n                        let index = self.insert_binding(binding);\n\n                        // TODO: What?\n                        self.bytecode.emit_store_undefined(value.variable());\n                        self.emit_binding_access(BindingAccessOpcode::DefInitVar, &index, &value);\n                        self.register_allocator.dealloc(value);\n\n                        // 6. NOTE: A var with the same name as a formal parameter initially has\n                        //          the same value as the corresponding initialized parameter.\n                    }\n                }\n\n                (instantiated_var_names, variable_scope)\n            } else {\n                // a. NOTE: Only a single Environment Record is needed for the parameters and top-level vars.\n                // b. Let instantiatedVarNames be a copy of the List parameterBindings.\n                let mut instantiated_var_names = parameter_bindings;\n\n                // c. For each element n of varNames, do\n                for n in var_names {\n                    // i. If instantiatedVarNames does not contain n, then\n                    if !instantiated_var_names.contains(&n) {\n                        // 1. Append n to instantiatedVarNames.\n                        instantiated_var_names.push(n);\n\n                        let n = n.to_js_string(self.interner());\n\n                        // 2. Perform ! env.CreateMutableBinding(n, false).\n                        // 3. Perform ! env.InitializeBinding(n, undefined).\n                        let binding = scope.get_binding_reference(&n).expect(\"binding must exist\");\n                        let index = self.insert_binding(binding);\n                        self.emit_binding_access(\n                            BindingAccessOpcode::DefInitVar,\n                            &index,\n                            &CallFrame::undefined_register(),\n                        );\n                    }\n                }\n\n                // d. Let varEnv be env.\n                (instantiated_var_names, scope)\n            };\n\n        // 29. NOTE: Annex B.3.2.1 adds additional steps at this point.\n        // 29. If strict is false, then\n        #[cfg(feature = \"annex-b\")]\n        if !strict {\n            // a. For each FunctionDeclaration f that is directly contained in the StatementList\n            //    of a Block, CaseClause, or DefaultClause, do\n            for f in annex_b_function_declarations_names(body) {\n                // i. Let F be StringValue of the BindingIdentifier of f.\n                // ii. If replacing the FunctionDeclaration f with a VariableStatement that has F\n                //     as a BindingIdentifier would not produce any Early Errors\n                //     for func and parameterNames does not contain F, then\n                if !lexical_names.contains(&f) && !parameter_names.contains(&f) {\n                    // 1. NOTE: A var binding for F is only instantiated here if it is neither a\n                    //    VarDeclaredName, the name of a formal parameter, or another FunctionDeclaration.\n\n                    // 2. If initializedBindings does not contain F and F is not \"arguments\", then\n                    if !instantiated_var_names.contains(&f) && f != arguments {\n                        let f_string = f.to_js_string(self.interner());\n\n                        // a. Perform ! varEnv.CreateMutableBinding(F, false).\n                        // b. Perform ! varEnv.InitializeBinding(F, undefined).\n                        let binding = variable_scope\n                            .get_binding_reference(&f_string)\n                            .expect(\"binding must exist\");\n                        let index = self.insert_binding(binding);\n                        self.emit_binding_access(\n                            BindingAccessOpcode::DefInitVar,\n                            &index,\n                            &CallFrame::undefined_register(),\n                        );\n\n                        // c. Append F to instantiatedVarNames.\n                        instantiated_var_names.push(f);\n                    }\n\n                    // 3. When the FunctionDeclaration f is evaluated, perform the following steps\n                    //    in place of the FunctionDeclaration Evaluation algorithm provided in 15.2.6:\n                    //     a. Let fenv be the running execution context's VariableEnvironment.\n                    //     b. Let benv be the running execution context's LexicalEnvironment.\n                    //     c. Let fobj be ! benv.GetBindingValue(F, false).\n                    //     d. Perform ! fenv.SetMutableBinding(F, fobj, false).\n                    //     e. Return unused.\n                    self.annex_b_function_names.push(f);\n                }\n            }\n        }\n\n        // 30-31\n        drop(self.push_declarative_scope(scopes.lexical_scope()));\n\n        // 35. Let privateEnv be the PrivateEnvironment of calleeContext.\n        // 36. For each Parse Node f of functionsToInitialize, do\n        for function in functions_to_initialize {\n            // a. Let fn be the sole element of the BoundNames of f.\n            // b. Let fo be InstantiateFunctionObject of f with arguments lexEnv and privateEnv.\n            // c. Perform ! varEnv.SetMutableBinding(fn, fo, false).\n            let dst = self.register_allocator.alloc();\n            self.function_with_binding(function, NodeKind::Declaration, &dst);\n            self.register_allocator.dealloc(dst);\n        }\n\n        // 37. Return unused.\n    }\n}\n"
  },
  {
    "path": "core/engine/src/bytecompiler/env.rs",
    "content": "use super::ByteCompiler;\nuse crate::vm::Constant;\nuse boa_ast::scope::Scope;\n\nimpl ByteCompiler<'_> {\n    /// Push either a new declarative or function scope on the environment stack.\n    #[must_use]\n    pub(crate) fn push_scope(&mut self, scope: &Scope) -> u32 {\n        self.current_open_environments_count += 1;\n\n        let index = self.constants.len() as u32;\n        self.constants.push(Constant::Scope(scope.clone()));\n\n        if scope.is_function() {\n            self.variable_scope = scope.clone();\n        }\n\n        self.lexical_scope = scope.clone();\n\n        index\n    }\n\n    /// Push a declarative scope.\n    ///\n    /// Returns the outer scope.\n    #[must_use]\n    pub(crate) fn push_declarative_scope(&mut self, scope: Option<&Scope>) -> Option<Scope> {\n        let mut scope = scope?.clone();\n        if !scope.all_bindings_local() {\n            self.current_open_environments_count += 1;\n            let index = self.constants.len() as u32;\n            self.constants.push(Constant::Scope(scope.clone()));\n            self.bytecode.emit_push_scope(index.into());\n        }\n        std::mem::swap(&mut self.lexical_scope, &mut scope);\n        Some(scope)\n    }\n\n    /// Pop a declarative scope.\n    pub(crate) fn pop_declarative_scope(&mut self, scope: Option<Scope>) {\n        if let Some(mut scope) = scope {\n            std::mem::swap(&mut self.lexical_scope, &mut scope);\n            if !scope.all_bindings_local() {\n                self.current_open_environments_count -= 1;\n                self.bytecode.emit_pop_environment();\n            }\n        }\n    }\n\n    /// Pops the top scope.\n    pub(crate) fn pop_scope(&mut self) {\n        self.current_open_environments_count -= 1;\n    }\n}\n"
  },
  {
    "path": "core/engine/src/bytecompiler/expression/assign.rs",
    "content": "use crate::{\n    bytecompiler::{Access, BindingAccessOpcode, ByteCompiler, Label, Register, ToJsString},\n    vm::opcode::BindingOpcode,\n};\nuse boa_ast::{\n    Expression,\n    expression::{\n        access::{PropertyAccess, PropertyAccessField},\n        operator::{Assign, assign::AssignOp},\n    },\n    scope::BindingLocatorError,\n};\n\nimpl ByteCompiler<'_> {\n    pub(crate) fn compile_assign(&mut self, assign: &Assign, dst: &Register) {\n        let mut compiler = self.position_guard(assign);\n\n        if assign.op() == AssignOp::Assign {\n            match Access::from_assign_target(assign.lhs()) {\n                Ok(access) => {\n                    compiler.access_set(access, |compiler| {\n                        compiler.compile_expr(assign.rhs(), dst);\n                        dst\n                    });\n                }\n                Err(pattern) => {\n                    compiler.compile_expr(assign.rhs(), dst);\n                    compiler.compile_declaration_pattern(pattern, BindingOpcode::SetName, dst);\n                }\n            }\n        } else {\n            let access = Access::from_assign_target(assign.lhs())\n                .expect(\"patterns should throw early errors on complex assignment operators\");\n\n            let short_circuit = matches!(\n                assign.op(),\n                AssignOp::BoolAnd | AssignOp::BoolOr | AssignOp::Coalesce\n            );\n\n            let emit = |compiler: &mut Self,\n                        dst: &Register,\n                        expr: &Expression,\n                        op: AssignOp|\n             -> Option<Label> {\n                if short_circuit {\n                    let next = compiler.next_opcode_location();\n                    match op {\n                        AssignOp::BoolAnd => compiler\n                            .bytecode\n                            .emit_logical_and(Self::DUMMY_ADDRESS, dst.variable()),\n                        AssignOp::BoolOr => compiler\n                            .bytecode\n                            .emit_logical_or(Self::DUMMY_ADDRESS, dst.variable()),\n                        AssignOp::Coalesce => compiler\n                            .bytecode\n                            .emit_coalesce(Self::DUMMY_ADDRESS, dst.variable()),\n                        _ => unreachable!(),\n                    }\n                    compiler.compile_expr(expr, dst);\n                    Some(Label { index: next })\n                } else {\n                    let rhs = compiler.register_allocator.alloc();\n                    compiler.compile_expr(expr, &rhs);\n                    match op {\n                        AssignOp::Add => compiler.bytecode.emit_add(\n                            dst.variable(),\n                            dst.variable(),\n                            rhs.variable(),\n                        ),\n                        AssignOp::Sub => compiler.bytecode.emit_sub(\n                            dst.variable(),\n                            dst.variable(),\n                            rhs.variable(),\n                        ),\n                        AssignOp::Mul => compiler.bytecode.emit_mul(\n                            dst.variable(),\n                            dst.variable(),\n                            rhs.variable(),\n                        ),\n                        AssignOp::Div => compiler.bytecode.emit_div(\n                            dst.variable(),\n                            dst.variable(),\n                            rhs.variable(),\n                        ),\n                        AssignOp::Mod => compiler.bytecode.emit_mod(\n                            dst.variable(),\n                            dst.variable(),\n                            rhs.variable(),\n                        ),\n                        AssignOp::Exp => compiler.bytecode.emit_pow(\n                            dst.variable(),\n                            dst.variable(),\n                            rhs.variable(),\n                        ),\n                        AssignOp::And => compiler.bytecode.emit_bit_and(\n                            dst.variable(),\n                            dst.variable(),\n                            rhs.variable(),\n                        ),\n                        AssignOp::Or => compiler.bytecode.emit_bit_or(\n                            dst.variable(),\n                            dst.variable(),\n                            rhs.variable(),\n                        ),\n                        AssignOp::Xor => compiler.bytecode.emit_bit_xor(\n                            dst.variable(),\n                            dst.variable(),\n                            rhs.variable(),\n                        ),\n                        AssignOp::Shl => compiler.bytecode.emit_shift_left(\n                            dst.variable(),\n                            dst.variable(),\n                            rhs.variable(),\n                        ),\n                        AssignOp::Shr => compiler.bytecode.emit_shift_right(\n                            dst.variable(),\n                            dst.variable(),\n                            rhs.variable(),\n                        ),\n                        AssignOp::Ushr => compiler.bytecode.emit_unsigned_shift_right(\n                            dst.variable(),\n                            dst.variable(),\n                            rhs.variable(),\n                        ),\n                        _ => unreachable!(),\n                    }\n                    compiler.register_allocator.dealloc(rhs);\n                    None\n                }\n            };\n\n            let early_exit;\n\n            match access {\n                Access::Variable { name } => {\n                    let name = name.to_js_string(compiler.interner());\n\n                    let binding = compiler\n                        .lexical_scope\n                        .get_identifier_reference(name.clone());\n                    let is_lexical = binding.is_lexical();\n                    let index = compiler.get_binding(&binding);\n\n                    if is_lexical {\n                        compiler.emit_binding_access(BindingAccessOpcode::GetName, &index, dst);\n                    } else {\n                        compiler.emit_binding_access(\n                            BindingAccessOpcode::GetNameAndLocator,\n                            &index,\n                            dst,\n                        );\n                    }\n\n                    early_exit = emit(&mut compiler, dst, assign.rhs(), assign.op());\n\n                    if is_lexical {\n                        match compiler.lexical_scope.set_mutable_binding(name.clone()) {\n                            Ok(binding) => {\n                                let index = compiler.insert_binding(binding);\n                                compiler.emit_binding_access(\n                                    BindingAccessOpcode::SetName,\n                                    &index,\n                                    dst,\n                                );\n                            }\n                            Err(BindingLocatorError::MutateImmutable) => {\n                                let index = compiler.get_or_insert_string(name);\n                                compiler.bytecode.emit_throw_mutate_immutable(index.into());\n                            }\n                            Err(BindingLocatorError::Silent) => {}\n                        }\n                    } else {\n                        compiler.emit_binding_access(\n                            BindingAccessOpcode::SetNameByLocator,\n                            &index,\n                            dst,\n                        );\n                    }\n                }\n                Access::Property { access } => match access {\n                    PropertyAccess::Simple(access) => match access.field() {\n                        PropertyAccessField::Const(name) => {\n                            let object = compiler.register_allocator.alloc();\n                            compiler.compile_expr(access.target(), &object);\n\n                            compiler.emit_get_property_by_name(dst, None, &object, name.sym());\n\n                            early_exit = emit(&mut compiler, dst, assign.rhs(), assign.op());\n\n                            compiler.emit_set_property_by_name(dst, None, &object, name.sym());\n\n                            compiler.register_allocator.dealloc(object);\n                        }\n                        PropertyAccessField::Expr(expr) => {\n                            let object = compiler.register_allocator.alloc();\n                            compiler.compile_expr(access.target(), &object);\n\n                            let key = compiler.register_allocator.alloc();\n                            compiler.compile_expr(expr, &key);\n\n                            compiler.bytecode.emit_get_property_by_value_push(\n                                dst.variable(),\n                                key.variable(),\n                                object.variable(),\n                                object.variable(),\n                            );\n\n                            early_exit = emit(&mut compiler, dst, assign.rhs(), assign.op());\n\n                            compiler.bytecode.emit_set_property_by_value(\n                                dst.variable(),\n                                key.variable(),\n                                object.variable(),\n                                object.variable(),\n                            );\n\n                            compiler.register_allocator.dealloc(key);\n                            compiler.register_allocator.dealloc(object);\n                        }\n                    },\n                    PropertyAccess::Private(access) => {\n                        let index = compiler.get_or_insert_private_name(access.field());\n\n                        let object = compiler.register_allocator.alloc();\n                        compiler.compile_expr(access.target(), &object);\n\n                        compiler.bytecode.emit_get_private_field(\n                            dst.variable(),\n                            object.variable(),\n                            index.into(),\n                        );\n\n                        early_exit = emit(&mut compiler, dst, assign.rhs(), assign.op());\n\n                        compiler.bytecode.emit_set_private_field(\n                            dst.variable(),\n                            object.variable(),\n                            index.into(),\n                        );\n\n                        compiler.register_allocator.dealloc(object);\n                    }\n                    PropertyAccess::Super(access) => match access.field() {\n                        PropertyAccessField::Const(name) => {\n                            let object = compiler.register_allocator.alloc();\n                            let receiver = compiler.register_allocator.alloc();\n                            compiler.super_(&receiver, &object);\n\n                            compiler.emit_get_property_by_name(\n                                dst,\n                                Some(&receiver),\n                                &object,\n                                name.sym(),\n                            );\n\n                            early_exit = emit(&mut compiler, dst, assign.rhs(), assign.op());\n\n                            compiler.emit_set_property_by_name(\n                                dst,\n                                Some(&receiver),\n                                &object,\n                                name.sym(),\n                            );\n\n                            compiler.register_allocator.dealloc(receiver);\n                            compiler.register_allocator.dealloc(object);\n                        }\n                        PropertyAccessField::Expr(expr) => {\n                            let object = compiler.register_allocator.alloc();\n                            let receiver = compiler.register_allocator.alloc();\n                            compiler.super_(&receiver, &object);\n\n                            let key = compiler.register_allocator.alloc();\n                            compiler.compile_expr(expr, &key);\n\n                            compiler.bytecode.emit_get_property_by_value_push(\n                                dst.variable(),\n                                key.variable(),\n                                receiver.variable(),\n                                object.variable(),\n                            );\n\n                            early_exit = emit(&mut compiler, dst, assign.rhs(), assign.op());\n\n                            compiler.bytecode.emit_set_property_by_value(\n                                dst.variable(),\n                                key.variable(),\n                                receiver.variable(),\n                                object.variable(),\n                            );\n\n                            compiler.register_allocator.dealloc(key);\n                            compiler.register_allocator.dealloc(receiver);\n                            compiler.register_allocator.dealloc(object);\n                        }\n                    },\n                },\n                Access::This => unreachable!(),\n            }\n\n            if let Some(early_exit) = early_exit {\n                let skip = compiler.jump();\n                compiler.patch_jump(early_exit);\n                compiler.patch_jump(skip);\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "core/engine/src/bytecompiler/expression/binary.rs",
    "content": "use crate::{\n    bytecompiler::{ByteCompiler, Label, Register},\n    vm::opcode::RegisterOperand,\n};\nuse boa_ast::{\n    Expression,\n    expression::operator::{\n        Binary, BinaryInPrivate,\n        binary::{ArithmeticOp, BinaryOp, BitwiseOp, LogicalOp, RelationalOp},\n    },\n};\n\nimpl ByteCompiler<'_> {\n    pub(crate) fn compile_binary(&mut self, binary: &Binary, dst: &Register) {\n        match binary.op() {\n            BinaryOp::Arithmetic(op) => {\n                self.compile_expr_operand(binary.lhs(), |self_, lhs| {\n                    self_.compile_binary_arithmetic(op, binary.rhs(), dst, lhs);\n                });\n            }\n            BinaryOp::Bitwise(op) => {\n                self.compile_expr_operand(binary.lhs(), |self_, lhs| {\n                    self_.compile_binary_bitwise(op, binary.rhs(), dst, lhs);\n                });\n            }\n            BinaryOp::Relational(op) => {\n                self.compile_expr_operand(binary.lhs(), |self_, lhs| {\n                    self_.compile_binary_relational(op, binary.rhs(), dst, lhs);\n                });\n            }\n            BinaryOp::Logical(op) => {\n                self.compile_expr(binary.lhs(), dst);\n                let exit = self.next_opcode_location();\n                match op {\n                    LogicalOp::And => self\n                        .bytecode\n                        .emit_logical_and(Self::DUMMY_ADDRESS, dst.variable()),\n                    LogicalOp::Or => self\n                        .bytecode\n                        .emit_logical_or(Self::DUMMY_ADDRESS, dst.variable()),\n                    LogicalOp::Coalesce => self\n                        .bytecode\n                        .emit_coalesce(Self::DUMMY_ADDRESS, dst.variable()),\n                }\n                self.compile_expr(binary.rhs(), dst);\n                self.patch_jump(Label { index: exit });\n            }\n            BinaryOp::Comma => {\n                // Evaluate LHS for side effects, then RHS is the result.\n                self.compile_expr_operand(binary.lhs(), |_, _| {});\n                self.compile_expr(binary.rhs(), dst);\n            }\n        }\n    }\n\n    fn compile_binary_arithmetic(\n        &mut self,\n        op: ArithmeticOp,\n        rhs_expr: &Expression,\n        dst: &Register,\n        lhs: RegisterOperand,\n    ) {\n        self.compile_expr_operand(rhs_expr, |self_, rhs| {\n            let bytecode = &mut self_.bytecode;\n            match op {\n                ArithmeticOp::Add => bytecode.emit_add(dst.variable(), lhs, rhs),\n                ArithmeticOp::Sub => bytecode.emit_sub(dst.variable(), lhs, rhs),\n                ArithmeticOp::Div => bytecode.emit_div(dst.variable(), lhs, rhs),\n                ArithmeticOp::Mul => bytecode.emit_mul(dst.variable(), lhs, rhs),\n                ArithmeticOp::Exp => bytecode.emit_pow(dst.variable(), lhs, rhs),\n                ArithmeticOp::Mod => bytecode.emit_mod(dst.variable(), lhs, rhs),\n            }\n        });\n    }\n\n    fn compile_binary_bitwise(\n        &mut self,\n        op: BitwiseOp,\n        rhs_expr: &Expression,\n        dst: &Register,\n        lhs: RegisterOperand,\n    ) {\n        self.compile_expr_operand(rhs_expr, |self_, rhs| {\n            let bytecode = &mut self_.bytecode;\n            match op {\n                BitwiseOp::And => bytecode.emit_bit_and(dst.variable(), lhs, rhs),\n                BitwiseOp::Or => bytecode.emit_bit_or(dst.variable(), lhs, rhs),\n                BitwiseOp::Xor => bytecode.emit_bit_xor(dst.variable(), lhs, rhs),\n                BitwiseOp::Shl => {\n                    bytecode.emit_shift_left(dst.variable(), lhs, rhs);\n                }\n                BitwiseOp::Shr => {\n                    bytecode.emit_shift_right(dst.variable(), lhs, rhs);\n                }\n                BitwiseOp::UShr => {\n                    bytecode.emit_unsigned_shift_right(dst.variable(), lhs, rhs);\n                }\n            }\n        });\n    }\n\n    fn compile_binary_relational(\n        &mut self,\n        op: RelationalOp,\n        rhs_expr: &Expression,\n        dst: &Register,\n        lhs: RegisterOperand,\n    ) {\n        self.compile_expr_operand(rhs_expr, |self_, rhs| {\n            let bytecode = &mut self_.bytecode;\n            match op {\n                RelationalOp::Equal => bytecode.emit_eq(dst.variable(), lhs, rhs),\n                RelationalOp::NotEqual => {\n                    bytecode.emit_not_eq(dst.variable(), lhs, rhs);\n                }\n                RelationalOp::StrictEqual => {\n                    bytecode.emit_strict_eq(dst.variable(), lhs, rhs);\n                }\n                RelationalOp::StrictNotEqual => {\n                    bytecode.emit_strict_not_eq(dst.variable(), lhs, rhs);\n                }\n                RelationalOp::GreaterThan => {\n                    bytecode.emit_greater_than(dst.variable(), lhs, rhs);\n                }\n                RelationalOp::GreaterThanOrEqual => {\n                    bytecode.emit_greater_than_or_eq(dst.variable(), lhs, rhs);\n                }\n                RelationalOp::LessThan => {\n                    bytecode.emit_less_than(dst.variable(), lhs, rhs);\n                }\n                RelationalOp::LessThanOrEqual => {\n                    bytecode.emit_less_than_or_eq(dst.variable(), lhs, rhs);\n                }\n                RelationalOp::In => bytecode.emit_in(dst.variable(), lhs, rhs),\n                RelationalOp::InstanceOf => {\n                    bytecode.emit_instance_of(dst.variable(), lhs, rhs);\n                }\n            }\n        });\n    }\n\n    pub(crate) fn compile_binary_in_private(&mut self, binary: &BinaryInPrivate, dst: &Register) {\n        let index = self.get_or_insert_private_name(*binary.lhs());\n        self.compile_expr(binary.rhs(), dst);\n        self.bytecode\n            .emit_in_private(dst.variable(), index.into(), dst.variable());\n    }\n}\n"
  },
  {
    "path": "core/engine/src/bytecompiler/expression/mod.rs",
    "content": "mod assign;\nmod binary;\nmod object_literal;\nmod unary;\nmod update;\n\nuse std::ops::Deref;\n\nuse super::{Access, CallResultDest, Callable, NodeKind, Register, ToJsString};\nuse crate::{\n    bytecompiler::{ByteCompiler, Literal},\n    vm::{CallFrame, GeneratorResumeKind},\n};\nuse boa_ast::{\n    Expression,\n    expression::{\n        ImportPhase,\n        access::{PropertyAccess, PropertyAccessField},\n        literal::{\n            Literal as AstLiteral, LiteralKind as AstLiteralKind, TemplateElement, TemplateLiteral,\n        },\n        operator::Conditional,\n    },\n};\nuse thin_vec::ThinVec;\n\nimpl ByteCompiler<'_> {\n    fn compile_literal(&mut self, lit: &AstLiteral, dst: &Register) {\n        match lit.kind() {\n            AstLiteralKind::String(v) => {\n                self.emit_store_literal(Literal::String(v.to_js_string(self.interner())), dst);\n            }\n            AstLiteralKind::Int(v) => self.emit_store_integer(*v, dst),\n            AstLiteralKind::Num(v) => self.emit_store_rational(*v, dst),\n            AstLiteralKind::BigInt(v) => {\n                self.emit_store_literal(Literal::BigInt(v.clone().into()), dst);\n            }\n            AstLiteralKind::Bool(true) => self.bytecode.emit_store_true(dst.variable()),\n            AstLiteralKind::Bool(false) => self.bytecode.emit_store_false(dst.variable()),\n            AstLiteralKind::Null => self.bytecode.emit_store_null(dst.variable()),\n            AstLiteralKind::Undefined => self.bytecode.emit_store_undefined(dst.variable()),\n        }\n    }\n\n    fn compile_conditional(&mut self, op: &Conditional, dst: &Register) {\n        self.compile_expr(op.condition(), dst);\n        self.if_else(\n            dst,\n            |compiler| compiler.compile_expr(op.if_true(), dst),\n            |compiler| compiler.compile_expr(op.if_false(), dst),\n        );\n    }\n\n    fn compile_template_literal(&mut self, template_literal: &TemplateLiteral, dst: &Register) {\n        let mut registers = Vec::with_capacity(template_literal.elements().len());\n        for element in template_literal.elements() {\n            let value = self.register_allocator.alloc();\n            match element {\n                TemplateElement::String(s) => {\n                    self.emit_store_literal(\n                        Literal::String(s.to_js_string(self.interner())),\n                        &value,\n                    );\n                }\n                TemplateElement::Expr(expr) => {\n                    self.compile_expr(expr, &value);\n                }\n            }\n            registers.push(value);\n        }\n\n        let mut values = ThinVec::with_capacity(registers.len());\n        for reg in &registers {\n            values.push(reg.variable());\n        }\n        self.bytecode.emit_concat_to_string(dst.variable(), values);\n        for reg in registers {\n            self.register_allocator.dealloc(reg);\n        }\n    }\n\n    pub(crate) fn compile_expr_impl(&mut self, expr: &Expression, dst: &Register) {\n        match expr {\n            Expression::Literal(lit) => self.compile_literal(lit, dst),\n            Expression::RegExpLiteral(regexp) => {\n                let pattern_index = self.get_or_insert_name(regexp.pattern());\n                let flags_index = self.get_or_insert_name(regexp.flags());\n                self.bytecode.emit_store_regexp(\n                    dst.variable(),\n                    pattern_index.into(),\n                    flags_index.into(),\n                );\n            }\n            Expression::Unary(unary) => self.compile_unary(unary, dst),\n            Expression::Update(update) => self.compile_update(update, dst, false),\n            Expression::Binary(binary) => self.compile_binary(binary, dst),\n            Expression::BinaryInPrivate(binary) => self.compile_binary_in_private(binary, dst),\n            Expression::Assign(assign) => self.compile_assign(assign, dst),\n            Expression::ObjectLiteral(object) => self.compile_object_literal(object, dst),\n            Expression::Identifier(name) => self.access_get(Access::Variable { name: *name }, dst),\n            Expression::PropertyAccess(access) => self.access_get(Access::Property { access }, dst),\n            Expression::Conditional(op) => self.compile_conditional(op, dst),\n            Expression::ArrayLiteral(literal) => {\n                let value = self.register_allocator.alloc();\n\n                self.bytecode.emit_store_new_array(dst.variable());\n\n                for element in literal.as_ref() {\n                    if let Some(element) = element {\n                        self.compile_expr(element, &value);\n                        if let Expression::Spread(_) = element {\n                            self.bytecode.emit_get_iterator(value.variable());\n                            self.bytecode.emit_push_iterator_to_array(dst.variable());\n                        } else {\n                            self.bytecode\n                                .emit_push_value_to_array(value.variable(), dst.variable());\n                        }\n                    } else {\n                        self.bytecode.emit_push_elision_to_array(dst.variable());\n                    }\n                }\n                self.register_allocator.dealloc(value);\n            }\n            Expression::This(_this) => self.access_get(Access::This, dst),\n            Expression::Spread(spread) => self.compile_expr(spread.target(), dst),\n            Expression::FunctionExpression(function) => {\n                self.function_with_binding(function.into(), NodeKind::Expression, dst);\n            }\n            Expression::ArrowFunction(function) => {\n                self.function_with_binding(function.into(), NodeKind::Expression, dst);\n            }\n            Expression::AsyncArrowFunction(function) => {\n                self.function_with_binding(function.into(), NodeKind::Expression, dst);\n            }\n            Expression::GeneratorExpression(function) => {\n                self.function_with_binding(function.into(), NodeKind::Expression, dst);\n            }\n            Expression::AsyncFunctionExpression(function) => {\n                self.function_with_binding(function.into(), NodeKind::Expression, dst);\n            }\n            Expression::AsyncGeneratorExpression(function) => {\n                self.function_with_binding(function.into(), NodeKind::Expression, dst);\n            }\n            Expression::Call(call) => {\n                self.call(Callable::Call(call), CallResultDest::Register(dst));\n            }\n            Expression::New(new) => {\n                self.call(Callable::New(new), CallResultDest::Register(dst));\n            }\n            Expression::TemplateLiteral(template_literal) => {\n                self.compile_template_literal(template_literal, dst);\n            }\n            Expression::Await(expr) => {\n                self.compile_expr(expr.target(), dst);\n                self.bytecode.emit_await(dst.variable());\n                let resume_kind = self.register_allocator.alloc();\n                self.pop_into_register(&resume_kind);\n                self.pop_into_register(dst);\n                self.generator_next(dst, &resume_kind);\n                self.register_allocator.dealloc(resume_kind);\n            }\n            Expression::Yield(r#yield) => {\n                if let Some(expr) = r#yield.target() {\n                    self.compile_expr(expr, dst);\n                } else {\n                    self.bytecode.emit_store_undefined(dst.variable());\n                }\n\n                if !r#yield.delegate() {\n                    self.r#yield(dst);\n                    return;\n                }\n\n                // need to delegate to an inner iterator\n\n                if self.is_async() {\n                    self.bytecode.emit_get_async_iterator(dst.variable());\n                } else {\n                    self.bytecode.emit_get_iterator(dst.variable());\n                }\n\n                let resume_kind = self.register_allocator.alloc();\n                let is_return = self.register_allocator.alloc();\n                self.bytecode.emit_store_undefined(dst.variable());\n                self.emit_resume_kind(GeneratorResumeKind::Normal, &resume_kind);\n\n                let start_address = self.next_opcode_location();\n\n                let (return_method_undefined, throw_method_undefined) =\n                    self.generator_delegate_next(dst, &resume_kind, &is_return);\n\n                if self.is_async() {\n                    self.bytecode.emit_await(dst.variable());\n                    self.pop_into_register(&resume_kind);\n                    self.pop_into_register(dst);\n                } else {\n                    self.emit_resume_kind(GeneratorResumeKind::Normal, &resume_kind);\n                }\n\n                let (resume_return, resume_exit) =\n                    self.generator_delegate_resume(dst, &resume_kind, &is_return);\n\n                if self.is_async() {\n                    self.bytecode.emit_iterator_value(dst.variable());\n                    self.async_generator_yield(dst, &resume_kind);\n                } else {\n                    self.bytecode.emit_iterator_result(dst.variable());\n                    self.bytecode.emit_generator_yield(dst.variable());\n                    self.pop_into_register(&resume_kind);\n                    self.pop_into_register(dst);\n                }\n                self.bytecode.emit_jump(start_address);\n\n                self.register_allocator.dealloc(resume_kind);\n                self.register_allocator.dealloc(is_return);\n\n                self.patch_jump(return_method_undefined);\n                self.patch_jump(resume_return);\n\n                if self.is_async() {\n                    self.bytecode.emit_await(dst.variable());\n                    self.bytecode.emit_pop();\n                } else {\n                    self.push_from_register(dst);\n                }\n                self.close_active_iterators();\n\n                self.r#return(true);\n\n                self.patch_jump(throw_method_undefined);\n\n                self.iterator_close(self.is_async());\n                self.emit_type_error(\"iterator does not have a throw method\");\n\n                self.patch_jump(resume_exit);\n            }\n            Expression::TaggedTemplate(template) => {\n                let this = self.register_allocator.alloc();\n                let function = self.register_allocator.alloc();\n\n                match template.tag() {\n                    Expression::PropertyAccess(PropertyAccess::Simple(access)) => {\n                        self.compile_expr(access.target(), &this);\n                        match access.field() {\n                            PropertyAccessField::Const(ident) => {\n                                self.emit_get_property_by_name(&function, None, &this, ident.sym());\n                            }\n                            PropertyAccessField::Expr(field) => {\n                                let key = self.register_allocator.alloc();\n                                self.compile_expr(field, &key);\n                                self.bytecode.emit_get_property_by_value(\n                                    function.variable(),\n                                    key.variable(),\n                                    this.variable(),\n                                    this.variable(),\n                                );\n                                self.register_allocator.dealloc(key);\n                            }\n                        }\n                    }\n                    Expression::PropertyAccess(PropertyAccess::Private(access)) => {\n                        let index = self.get_or_insert_private_name(access.field());\n                        self.compile_expr(access.target(), &this);\n                        self.bytecode.emit_get_private_field(\n                            function.variable(),\n                            this.variable(),\n                            index.into(),\n                        );\n                    }\n                    expr => {\n                        self.bytecode.emit_store_undefined(this.variable());\n                        self.compile_expr(expr, &function);\n                    }\n                }\n\n                self.push_from_register(&this);\n                self.push_from_register(&function);\n\n                self.register_allocator.dealloc(this);\n                self.register_allocator.dealloc(function);\n\n                let site = template.identifier();\n                let count = template.cookeds().len() as u32;\n                let jump_label = self.template_lookup(dst, site);\n\n                let mut part_registers = Vec::with_capacity(count as usize * 2);\n\n                for (cooked, raw) in template.cookeds().iter().zip(template.raws()) {\n                    let value = self.register_allocator.alloc();\n                    if let Some(cooked) = cooked {\n                        self.emit_store_literal(\n                            Literal::String(cooked.to_js_string(self.interner())),\n                            &value,\n                        );\n                    } else {\n                        self.bytecode.emit_store_undefined(value.variable());\n                    }\n                    part_registers.push(value);\n                    let value = self.register_allocator.alloc();\n                    self.emit_store_literal(\n                        Literal::String(raw.to_js_string(self.interner())),\n                        &value,\n                    );\n                    part_registers.push(value);\n                }\n\n                let mut values = ThinVec::with_capacity(count as usize * 2);\n                for r in &part_registers {\n                    values.push(r.index());\n                }\n                self.bytecode\n                    .emit_template_create(site, dst.variable(), values);\n                for r in part_registers {\n                    self.register_allocator.dealloc(r);\n                }\n\n                self.patch_jump(jump_label);\n                self.push_from_register(dst);\n\n                for expr in template.exprs() {\n                    self.compile_expr_to_stack(expr);\n                }\n\n                self.bytecode\n                    .emit_call((template.exprs().len() as u32 + 1).into());\n                self.pop_into_register(dst);\n            }\n            Expression::ClassExpression(class) => {\n                self.compile_class(class.deref().into(), Some(dst));\n            }\n            Expression::SuperCall(super_call) => {\n                let value = self.register_allocator.alloc();\n\n                self.bytecode.emit_get_function_object(value.variable());\n                self.bytecode.emit_get_prototype(value.variable());\n\n                self.push_from_register(&CallFrame::undefined_register());\n                self.push_from_register(&value);\n                self.register_allocator.dealloc(value);\n\n                let contains_spread = super_call\n                    .arguments()\n                    .iter()\n                    .any(|arg| matches!(arg, Expression::Spread(_)));\n\n                if contains_spread {\n                    let array = self.register_allocator.alloc();\n                    let value = self.register_allocator.alloc();\n\n                    self.bytecode.emit_store_new_array(array.variable());\n\n                    for arg in super_call.arguments() {\n                        self.compile_expr(arg, &value);\n                        if let Expression::Spread(_) = arg {\n                            self.bytecode.emit_get_iterator(value.variable());\n                            self.bytecode.emit_push_iterator_to_array(array.variable());\n                        } else {\n                            self.bytecode\n                                .emit_push_value_to_array(value.variable(), array.variable());\n                        }\n                    }\n\n                    self.push_from_register(&array);\n\n                    self.register_allocator.dealloc(value);\n                    self.register_allocator.dealloc(array);\n                } else {\n                    for arg in super_call.arguments() {\n                        self.compile_expr_to_stack(arg);\n                    }\n                }\n\n                if contains_spread {\n                    self.bytecode.emit_super_call_spread();\n                } else {\n                    self.bytecode\n                        .emit_super_call((super_call.arguments().len() as u32).into());\n                }\n                self.pop_into_register(dst);\n                self.bytecode.emit_bind_this_value(dst.variable());\n            }\n            Expression::ImportCall(import) => {\n                self.compile_expr(import.specifier(), dst);\n                let options = self.register_allocator.alloc();\n                if let Some(opts) = import.options() {\n                    self.compile_expr(opts, &options);\n                } else {\n                    self.bytecode.emit_store_undefined(options.variable());\n                }\n\n                let phase: u32 = match import.phase() {\n                    ImportPhase::Evaluation => 0,\n                    ImportPhase::Defer => 1,\n                    ImportPhase::Source => 2,\n                };\n                self.bytecode\n                    .emit_import_call(dst.variable(), options.variable(), phase.into());\n                self.register_allocator.dealloc(options);\n            }\n            Expression::NewTarget(_new_target) => {\n                self.bytecode.emit_new_target(dst.variable());\n            }\n            Expression::ImportMeta(_import_meta) => {\n                self.bytecode.emit_import_meta(dst.variable());\n            }\n            Expression::Optional(opt) => {\n                let this = self.register_allocator.alloc();\n                self.compile_optional_preserve_this(opt, &this, dst);\n                self.register_allocator.dealloc(this);\n            }\n            Expression::Parenthesized(parenthesized) => {\n                self.compile_expr(parenthesized.expression(), dst);\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "core/engine/src/bytecompiler/expression/object_literal.rs",
    "content": "use crate::bytecompiler::{Access, ByteCompiler, FunctionSpec, MethodKind, Register};\nuse boa_ast::{\n    Expression,\n    expression::literal::{ObjectLiteral, PropertyDefinition},\n    property::{MethodDefinitionKind, PropertyName},\n};\nuse boa_interner::Sym;\nuse thin_vec::ThinVec;\n\nimpl ByteCompiler<'_> {\n    pub(crate) fn compile_object_literal(&mut self, literal: &ObjectLiteral, dst: &Register) {\n        self.bytecode.emit_store_empty_object(dst.variable());\n\n        for property in literal.properties() {\n            match property {\n                PropertyDefinition::IdentifierReference(ident) => {\n                    let value = self.register_allocator.alloc();\n                    self.access_get(Access::Variable { name: *ident }, &value);\n                    let index = self.get_or_insert_name(ident.sym());\n                    self.bytecode.emit_define_own_property_by_name(\n                        dst.variable(),\n                        value.variable(),\n                        index.into(),\n                    );\n                    self.register_allocator.dealloc(value);\n                }\n                PropertyDefinition::Property(name, expr) => match name {\n                    PropertyName::Literal(name) => {\n                        let value = self.register_allocator.alloc();\n                        self.compile_expr(expr, &value);\n                        if *name == Sym::__PROTO__ && !self.json_parse {\n                            self.bytecode\n                                .emit_set_prototype(dst.variable(), value.variable());\n                        } else {\n                            let index = self.get_or_insert_name(name.sym());\n                            self.bytecode.emit_define_own_property_by_name(\n                                dst.variable(),\n                                value.variable(),\n                                index.into(),\n                            );\n                        }\n                        self.register_allocator.dealloc(value);\n                    }\n                    PropertyName::Computed(name_node) => {\n                        let key = self.register_allocator.alloc();\n                        self.compile_expr(name_node, &key);\n                        self.bytecode\n                            .emit_to_property_key(key.variable(), key.variable());\n                        let function = self.register_allocator.alloc();\n                        self.compile_expr(expr, &function);\n                        if expr.is_anonymous_function_definition() {\n                            self.bytecode.emit_set_function_name(\n                                function.variable(),\n                                key.variable(),\n                                0u32.into(),\n                            );\n                        }\n                        self.bytecode.emit_define_own_property_by_value(\n                            function.variable(),\n                            key.variable(),\n                            dst.variable(),\n                        );\n                        self.register_allocator.dealloc(key);\n                        self.register_allocator.dealloc(function);\n                    }\n                },\n                PropertyDefinition::MethodDefinition(m) => {\n                    let kind = match m.kind() {\n                        MethodDefinitionKind::Get => MethodKind::Get,\n                        MethodDefinitionKind::Set => MethodKind::Set,\n                        _ => MethodKind::Ordinary,\n                    };\n                    match m.name() {\n                        PropertyName::Literal(name) => {\n                            let method = self.object_method(m.into(), kind);\n                            self.bytecode\n                                .emit_set_home_object(method.variable(), dst.variable());\n                            let index = self.get_or_insert_name(name.sym());\n                            match kind {\n                                MethodKind::Get => self.bytecode.emit_set_property_getter_by_name(\n                                    dst.variable(),\n                                    method.variable(),\n                                    index.into(),\n                                ),\n                                MethodKind::Set => self.bytecode.emit_set_property_setter_by_name(\n                                    dst.variable(),\n                                    method.variable(),\n                                    index.into(),\n                                ),\n                                MethodKind::Ordinary => {\n                                    self.bytecode.emit_define_own_property_by_name(\n                                        dst.variable(),\n                                        method.variable(),\n                                        index.into(),\n                                    );\n                                }\n                            }\n                            self.register_allocator.dealloc(method);\n                        }\n                        PropertyName::Computed(name_node) => {\n                            self.compile_object_literal_computed_method(\n                                name_node,\n                                m.into(),\n                                kind,\n                                dst,\n                            );\n                        }\n                    }\n                }\n                PropertyDefinition::SpreadObject(expr) => {\n                    let source = self.register_allocator.alloc();\n                    self.compile_expr(expr, &source);\n                    self.bytecode.emit_copy_data_properties(\n                        dst.variable(),\n                        source.variable(),\n                        ThinVec::new(),\n                    );\n                    self.register_allocator.dealloc(source);\n                }\n                PropertyDefinition::CoverInitializedName(_, _) => {\n                    unreachable!(\"invalid assignment pattern in object literal\")\n                }\n            }\n        }\n    }\n\n    fn compile_object_literal_computed_method(\n        &mut self,\n        expr: &Expression,\n        function: FunctionSpec<'_>,\n        kind: MethodKind,\n        object: &Register,\n    ) {\n        let key = self.register_allocator.alloc();\n        self.compile_expr(expr, &key);\n\n        self.bytecode\n            .emit_to_property_key(key.variable(), key.variable());\n\n        let method = self.object_method(function, kind);\n        let value: u32 = match kind {\n            MethodKind::Get => 1,\n            MethodKind::Set => 2,\n            MethodKind::Ordinary => 0,\n        };\n\n        self.bytecode\n            .emit_set_function_name(method.variable(), key.variable(), value.into());\n        self.bytecode\n            .emit_set_home_object(method.variable(), object.variable());\n\n        match kind {\n            MethodKind::Get => self.bytecode.emit_set_property_getter_by_value(\n                method.variable(),\n                key.variable(),\n                object.variable(),\n            ),\n            MethodKind::Set => self.bytecode.emit_set_property_setter_by_value(\n                method.variable(),\n                key.variable(),\n                object.variable(),\n            ),\n            MethodKind::Ordinary => self.bytecode.emit_define_own_property_by_value(\n                method.variable(),\n                key.variable(),\n                object.variable(),\n            ),\n        }\n\n        self.register_allocator.dealloc(key);\n        self.register_allocator.dealloc(method);\n    }\n}\n"
  },
  {
    "path": "core/engine/src/bytecompiler/expression/unary.rs",
    "content": "use crate::bytecompiler::{Access, BindingAccessOpcode, ByteCompiler, Register, ToJsString};\nuse boa_ast::Expression;\nuse boa_ast::expression::operator::{Unary, unary::UnaryOp};\n\nimpl ByteCompiler<'_> {\n    pub(crate) fn compile_unary(&mut self, unary: &Unary, dst: &Register) {\n        match unary.op() {\n            UnaryOp::Delete => {\n                let mut compiler = self.position_guard(unary);\n\n                if let Some(access) = Access::from_expression(unary.target()) {\n                    compiler.access_delete(access, dst);\n                } else if let Expression::Optional(opt) = unary.target() {\n                    compiler.compile_optional_delete(opt, dst);\n                } else {\n                    compiler.compile_expr(unary.target(), dst);\n                    compiler.bytecode.emit_store_true(dst.variable());\n                }\n            }\n            UnaryOp::Minus => {\n                self.compile_expr(unary.target(), dst);\n                self.bytecode.emit_neg(dst.variable());\n            }\n            UnaryOp::Plus => {\n                self.compile_expr(unary.target(), dst);\n                self.bytecode.emit_pos(dst.variable());\n            }\n            UnaryOp::Not => {\n                self.compile_expr(unary.target(), dst);\n                self.bytecode.emit_logical_not(dst.variable());\n            }\n            UnaryOp::Tilde => {\n                self.compile_expr(unary.target(), dst);\n                self.bytecode.emit_bit_not(dst.variable());\n            }\n            UnaryOp::TypeOf => {\n                match unary.target().flatten() {\n                    Expression::Identifier(identifier) => {\n                        let identifier = identifier.to_js_string(self.interner());\n                        let binding = self.lexical_scope.get_identifier_reference(identifier);\n                        let index = self.get_binding(&binding);\n                        self.emit_binding_access(\n                            BindingAccessOpcode::GetNameOrUndefined,\n                            &index,\n                            dst,\n                        );\n                    }\n                    expr => self.compile_expr(expr, dst),\n                }\n                self.bytecode.emit_type_of(dst.variable());\n            }\n            UnaryOp::Void => {\n                self.compile_expr(unary.target(), dst);\n                self.bytecode.emit_store_undefined(dst.variable());\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "core/engine/src/bytecompiler/expression/update.rs",
    "content": "use crate::bytecompiler::{\n    Access, BindingAccessOpcode, BindingKind, ByteCompiler, Register, ToJsString,\n};\nuse boa_ast::{\n    expression::{\n        access::{PropertyAccess, PropertyAccessField},\n        operator::{Update, update::UpdateOp},\n    },\n    scope::BindingLocatorError,\n};\n\nimpl ByteCompiler<'_> {\n    pub(crate) fn compile_update(&mut self, update: &Update, dst: &Register, discard: bool) {\n        let mut compiler = self.position_guard(update);\n        let increment = matches!(\n            update.op(),\n            UpdateOp::IncrementPost | UpdateOp::IncrementPre\n        );\n        let post = matches!(\n            update.op(),\n            UpdateOp::IncrementPost | UpdateOp::DecrementPost\n        );\n\n        match Access::from_update_target(update.target()) {\n            Access::Variable { name } => {\n                let name = name.to_js_string(compiler.interner());\n                let binding = compiler\n                    .lexical_scope\n                    .get_identifier_reference(name.clone());\n                let is_lexical = binding.is_lexical();\n                let index = compiler.get_binding(&binding);\n\n                // Fast path: for mutable local bindings with (post/pre)-increment/decrement,\n                // use the local register directly to avoid unnecessary Move instructions.\n                //\n                // Pre-increment (++i):\n                //   Inc(dst, local); Move(local, dst) → 2 ops\n                //\n                // Post-increment (i++):\n                //   Move(dst, local); Inc(local, local) → 2 ops\n                //\n                // Inc(local, local) works because Inc writes new to dst AFTER old to src,\n                // so when dst==src the new value wins.\n                //\n                // Skip for const bindings — they must fall through to emit ThrowMutateImmutable.\n                if is_lexical\n                    && compiler\n                        .lexical_scope\n                        .set_mutable_binding(name.clone())\n                        .is_ok()\n                    && let BindingKind::Local(Some(local_reg)) = &index\n                {\n                    let local_op = (*local_reg).into();\n\n                    if discard {\n                        // Result unused — just increment in-place.\n                        if increment {\n                            compiler.bytecode.emit_inc(local_op, local_op);\n                        } else {\n                            compiler.bytecode.emit_dec(local_op, local_op);\n                        }\n                        return;\n                    }\n\n                    if post {\n                        // Save old value to dst (post-increment returns old value).\n                        compiler.bytecode.emit_move(dst.variable(), local_op);\n                        // Increment in-place.\n                        if increment {\n                            compiler.bytecode.emit_inc(local_op, local_op);\n                        } else {\n                            compiler.bytecode.emit_dec(local_op, local_op);\n                        }\n                    } else {\n                        if increment {\n                            compiler.bytecode.emit_inc(dst.variable(), local_op);\n                        } else {\n                            compiler.bytecode.emit_dec(dst.variable(), local_op);\n                        }\n                        // Write the new value back to the local register.\n                        compiler.bytecode.emit_move(local_op, dst.variable());\n                    }\n                    return;\n                }\n\n                if is_lexical {\n                    compiler.emit_binding_access(BindingAccessOpcode::GetName, &index, dst);\n                } else {\n                    compiler.emit_binding_access(\n                        BindingAccessOpcode::GetNameAndLocator,\n                        &index,\n                        dst,\n                    );\n                }\n\n                let value = compiler.register_allocator.alloc();\n                if increment {\n                    compiler.bytecode.emit_inc(value.variable(), dst.variable());\n                } else {\n                    compiler.bytecode.emit_dec(value.variable(), dst.variable());\n                }\n\n                if is_lexical {\n                    match compiler.lexical_scope.set_mutable_binding(name.clone()) {\n                        Ok(binding) => {\n                            let index = compiler.insert_binding(binding);\n                            compiler.emit_binding_access(\n                                BindingAccessOpcode::SetName,\n                                &index,\n                                &value,\n                            );\n                        }\n                        Err(BindingLocatorError::MutateImmutable) => {\n                            let index = compiler.get_or_insert_string(name);\n                            compiler.bytecode.emit_throw_mutate_immutable(index.into());\n                        }\n                        Err(BindingLocatorError::Silent) => {}\n                    }\n                } else {\n                    compiler.emit_binding_access(\n                        BindingAccessOpcode::SetNameByLocator,\n                        &index,\n                        &value,\n                    );\n                }\n                if !post && !discard {\n                    compiler\n                        .bytecode\n                        .emit_move(dst.variable(), value.variable());\n                }\n\n                compiler.register_allocator.dealloc(value);\n            }\n            Access::Property { access } => match access {\n                PropertyAccess::Simple(access) => {\n                    let object = compiler.register_allocator.alloc();\n                    compiler.compile_expr(access.target(), &object);\n\n                    match access.field() {\n                        PropertyAccessField::Const(ident) => {\n                            compiler.emit_get_property_by_name(dst, None, &object, ident.sym());\n                            let value = compiler.register_allocator.alloc();\n                            if increment {\n                                compiler.bytecode.emit_inc(value.variable(), dst.variable());\n                            } else {\n                                compiler.bytecode.emit_dec(value.variable(), dst.variable());\n                            }\n\n                            compiler.emit_set_property_by_name(&value, None, &object, ident.sym());\n\n                            if !post {\n                                compiler\n                                    .bytecode\n                                    .emit_move(dst.variable(), value.variable());\n                            }\n\n                            compiler.register_allocator.dealloc(object);\n                            compiler.register_allocator.dealloc(value);\n                        }\n                        PropertyAccessField::Expr(expr) => {\n                            let key = compiler.register_allocator.alloc();\n                            compiler.compile_expr(expr, &key);\n\n                            compiler.bytecode.emit_get_property_by_value_push(\n                                dst.variable(),\n                                key.variable(),\n                                object.variable(),\n                                object.variable(),\n                            );\n\n                            let value = compiler.register_allocator.alloc();\n                            if increment {\n                                compiler.bytecode.emit_inc(value.variable(), dst.variable());\n                            } else {\n                                compiler.bytecode.emit_dec(value.variable(), dst.variable());\n                            }\n\n                            compiler.bytecode.emit_set_property_by_value(\n                                value.variable(),\n                                key.variable(),\n                                object.variable(),\n                                object.variable(),\n                            );\n\n                            if !post {\n                                compiler\n                                    .bytecode\n                                    .emit_move(dst.variable(), value.variable());\n                            }\n\n                            compiler.register_allocator.dealloc(key);\n                            compiler.register_allocator.dealloc(object);\n                            compiler.register_allocator.dealloc(value);\n                        }\n                    }\n                }\n                PropertyAccess::Private(access) => {\n                    let index = compiler.get_or_insert_private_name(access.field());\n\n                    let object = compiler.register_allocator.alloc();\n                    compiler.compile_expr(access.target(), &object);\n\n                    compiler.bytecode.emit_get_private_field(\n                        dst.variable(),\n                        object.variable(),\n                        index.into(),\n                    );\n\n                    let value = compiler.register_allocator.alloc();\n                    if increment {\n                        compiler.bytecode.emit_inc(value.variable(), dst.variable());\n                    } else {\n                        compiler.bytecode.emit_dec(value.variable(), dst.variable());\n                    }\n                    compiler.bytecode.emit_set_private_field(\n                        value.variable(),\n                        object.variable(),\n                        index.into(),\n                    );\n\n                    if !post {\n                        compiler\n                            .bytecode\n                            .emit_move(dst.variable(), value.variable());\n                    }\n\n                    compiler.register_allocator.dealloc(value);\n                    compiler.register_allocator.dealloc(object);\n                }\n                PropertyAccess::Super(access) => match access.field() {\n                    PropertyAccessField::Const(ident) => {\n                        let object = compiler.register_allocator.alloc();\n                        let receiver = compiler.register_allocator.alloc();\n                        compiler.super_(&receiver, &object);\n\n                        compiler.emit_get_property_by_name(\n                            dst,\n                            Some(&receiver),\n                            &object,\n                            ident.sym(),\n                        );\n\n                        let value = compiler.register_allocator.alloc();\n                        if increment {\n                            compiler.bytecode.emit_inc(value.variable(), dst.variable());\n                        } else {\n                            compiler.bytecode.emit_dec(value.variable(), dst.variable());\n                        }\n\n                        compiler.emit_set_property_by_name(\n                            &value,\n                            Some(&receiver),\n                            &object,\n                            ident.sym(),\n                        );\n                        if !post {\n                            compiler\n                                .bytecode\n                                .emit_move(dst.variable(), value.variable());\n                        }\n\n                        compiler.register_allocator.dealloc(receiver);\n                        compiler.register_allocator.dealloc(object);\n                        compiler.register_allocator.dealloc(value);\n                    }\n                    PropertyAccessField::Expr(expr) => {\n                        let object = compiler.register_allocator.alloc();\n                        let receiver = compiler.register_allocator.alloc();\n                        compiler.super_(&receiver, &object);\n\n                        let key = compiler.register_allocator.alloc();\n                        compiler.compile_expr(expr, &key);\n\n                        compiler.bytecode.emit_get_property_by_value(\n                            dst.variable(),\n                            key.variable(),\n                            receiver.variable(),\n                            object.variable(),\n                        );\n                        if increment {\n                            compiler.bytecode.emit_inc(dst.variable(), dst.variable());\n                        } else {\n                            compiler.bytecode.emit_dec(dst.variable(), dst.variable());\n                        }\n                        compiler.bytecode.emit_set_property_by_value(\n                            dst.variable(),\n                            key.variable(),\n                            receiver.variable(),\n                            object.variable(),\n                        );\n\n                        compiler.register_allocator.dealloc(receiver);\n                        compiler.register_allocator.dealloc(object);\n                        compiler.register_allocator.dealloc(key);\n                    }\n                },\n            },\n            Access::This => unreachable!(),\n        }\n    }\n}\n"
  },
  {
    "path": "core/engine/src/bytecompiler/function.rs",
    "content": "use crate::{\n    JsString, SpannedSourceText,\n    builtins::function::ThisMode,\n    bytecompiler::ByteCompiler,\n    js_string,\n    vm::{CodeBlock, CodeBlockFlags, source_info::SourcePath},\n};\nuse boa_ast::{\n    function::{FormalParameterList, FunctionBody},\n    scope::{FunctionScopes, Scope},\n};\nuse boa_gc::Gc;\nuse boa_interner::Interner;\n\n/// `FunctionCompiler` is used to compile AST functions to bytecode.\n#[derive(Debug, Clone)]\n#[allow(clippy::struct_excessive_bools)]\npub(crate) struct FunctionCompiler {\n    name: JsString,\n    generator: bool,\n    r#async: bool,\n    strict: bool,\n    arrow: bool,\n    method: bool,\n    in_with: bool,\n    force_function_scope: bool,\n    name_scope: Option<Scope>,\n    spanned_source_text: SpannedSourceText,\n    source_path: SourcePath,\n}\n\nimpl FunctionCompiler {\n    /// Create a new `FunctionCompiler`.\n    pub(crate) fn new(spanned_source_text: SpannedSourceText) -> Self {\n        Self {\n            name: js_string!(),\n            generator: false,\n            r#async: false,\n            strict: false,\n            arrow: false,\n            method: false,\n            in_with: false,\n            force_function_scope: false,\n            name_scope: None,\n            spanned_source_text,\n            source_path: SourcePath::None,\n        }\n    }\n\n    /// Set the name of the function.\n    pub(crate) fn name<N>(mut self, name: N) -> Self\n    where\n        N: Into<Option<JsString>>,\n    {\n        let name = name.into();\n        if let Some(name) = name {\n            self.name = name;\n        }\n        self\n    }\n\n    /// Indicate if the function is an arrow function.\n    pub(crate) const fn arrow(mut self, arrow: bool) -> Self {\n        self.arrow = arrow;\n        self\n    }\n    /// Indicate if the function is a method function.\n    pub(crate) const fn method(mut self, method: bool) -> Self {\n        self.method = method;\n        self\n    }\n    /// Indicate if the function is a generator function.\n    pub(crate) const fn generator(mut self, generator: bool) -> Self {\n        self.generator = generator;\n        self\n    }\n\n    /// Indicate if the function is an async function.\n    pub(crate) const fn r#async(mut self, r#async: bool) -> Self {\n        self.r#async = r#async;\n        self\n    }\n\n    /// Indicate if the function is in a strict context.\n    pub(crate) const fn strict(mut self, strict: bool) -> Self {\n        self.strict = strict;\n        self\n    }\n\n    /// Provide the name scope of the function.\n    pub(crate) fn name_scope(mut self, name_scope: Option<Scope>) -> Self {\n        self.name_scope = name_scope;\n        self\n    }\n\n    /// Indicate if the function is in a `with` statement.\n    pub(crate) const fn in_with(mut self, in_with: bool) -> Self {\n        self.in_with = in_with;\n        self\n    }\n\n    /// Indicate if the function is in a `with` statement.\n    pub(crate) const fn force_function_scope(mut self, force_function_scope: bool) -> Self {\n        self.force_function_scope = force_function_scope;\n        self\n    }\n\n    /// Set source map file path.\n    pub(crate) fn source_path(mut self, source_path: SourcePath) -> Self {\n        self.source_path = source_path;\n        self\n    }\n\n    /// Compile a function statement list and it's parameters into bytecode.\n    #[allow(clippy::too_many_arguments)]\n    pub(crate) fn compile(\n        mut self,\n        parameters: &FormalParameterList,\n        body: &FunctionBody,\n        variable_environment: Scope,\n        lexical_environment: Scope,\n        scopes: &FunctionScopes,\n        contains_direct_eval: bool,\n        interner: &mut Interner,\n    ) -> Gc<CodeBlock> {\n        self.strict = self.strict || body.strict();\n\n        let length = parameters.length();\n\n        let mut compiler = ByteCompiler::new(\n            self.name,\n            self.strict,\n            false,\n            variable_environment,\n            lexical_environment,\n            self.r#async,\n            self.generator,\n            interner,\n            self.in_with,\n            self.spanned_source_text,\n            self.source_path,\n        );\n\n        compiler.length = length;\n        compiler.code_block_flags.set(\n            CodeBlockFlags::HAS_PROTOTYPE_PROPERTY,\n            !self.arrow && !self.method && !self.r#async && !self.generator,\n        );\n\n        if self.arrow {\n            compiler.this_mode = ThisMode::Lexical;\n        }\n\n        if let Some(scope) = self.name_scope\n            && !scope.all_bindings_local()\n        {\n            compiler.code_block_flags |= CodeBlockFlags::HAS_BINDING_IDENTIFIER;\n            let _ = compiler.push_scope(&scope);\n        }\n\n        if contains_direct_eval || !scopes.function_scope().all_bindings_local() {\n            compiler.code_block_flags |= CodeBlockFlags::HAS_FUNCTION_SCOPE;\n        } else if !self.arrow {\n            compiler.code_block_flags.set(\n                CodeBlockFlags::HAS_FUNCTION_SCOPE,\n                self.force_function_scope || scopes.requires_function_scope(),\n            );\n        }\n\n        if compiler.code_block_flags.has_function_scope() {\n            let _ = compiler.push_scope(scopes.function_scope());\n        } else {\n            compiler.variable_scope = scopes.function_scope().clone();\n            compiler.lexical_scope = scopes.function_scope().clone();\n        }\n\n        // Taken from:\n        //  - 15.9.3 Runtime Semantics: EvaluateAsyncConciseBody: <https://tc39.es/ecma262/#sec-runtime-semantics-evaluateasyncconcisebody>\n        //  - 15.8.4 Runtime Semantics: EvaluateAsyncFunctionBody: <https://tc39.es/ecma262/#sec-runtime-semantics-evaluateasyncfunctionbody>\n        //\n        // Note: In `EvaluateAsyncGeneratorBody` unlike the async non-generator functions we don't handle exceptions thrown by\n        // `FunctionDeclarationInstantiation` (so they are propagated).\n        //\n        // See: 15.6.2 Runtime Semantics: EvaluateAsyncGeneratorBody: https://tc39.es/ecma262/#sec-runtime-semantics-evaluateasyncgeneratorbody\n        if compiler.is_async() && !compiler.is_generator() {\n            // 1. Let promiseCapability be ! NewPromiseCapability(%Promise%).\n            compiler.bytecode.emit_create_promise_capability();\n\n            // 2. Let declResult be Completion(FunctionDeclarationInstantiation(functionObject, argumentsList)).\n            //\n            // Note: We push an exception handler so we catch exceptions that are thrown by the\n            // `FunctionDeclarationInstantiation` abstract function.\n            //\n            // Patched in `ByteCompiler::finish()`.\n            compiler.async_handler = Some(compiler.push_handler());\n        }\n\n        compiler.function_declaration_instantiation(\n            body,\n            parameters,\n            self.arrow,\n            self.strict,\n            self.generator,\n            scopes,\n        );\n\n        // Taken from:\n        // - 27.6.3.2 AsyncGeneratorStart ( generator, generatorBody ): <https://tc39.es/ecma262/#sec-asyncgeneratorstart>\n        //\n        // Note: We do handle exceptions thrown by generator body in `AsyncGeneratorStart`.\n        if compiler.is_generator() {\n            assert!(compiler.async_handler.is_none());\n\n            if compiler.is_async() {\n                // Patched in `ByteCompiler::finish()`.\n                compiler.async_handler = Some(compiler.push_handler());\n            }\n        }\n\n        {\n            let mut compiler = compiler.position_guard(body);\n            compiler.compile_statement_list(body.statement_list(), false, false);\n        }\n\n        compiler.params = parameters.clone();\n        compiler.parameter_scope = scopes.parameter_scope();\n\n        let code = compiler.finish();\n\n        Gc::new(code)\n    }\n}\n"
  },
  {
    "path": "core/engine/src/bytecompiler/generator.rs",
    "content": "use thin_vec::thin_vec;\n\nuse crate::{\n    bytecompiler::{ByteCompiler, Label, Register},\n    js_string,\n    vm::GeneratorResumeKind,\n};\n\nimpl ByteCompiler<'_> {\n    pub(crate) fn generator_next(&mut self, value: &Register, resume_kind: &Register) {\n        // NOTE: +4 to jump past the index operand.\n        let jump_table_index = self.next_opcode_location() + size_of::<u32>() as u32;\n        self.bytecode.emit_jump_table(\n            resume_kind.index(),\n            thin_vec![\n                Self::DUMMY_ADDRESS, // GeneratorResumeKind::Normal\n                Self::DUMMY_ADDRESS, // GeneratorResumeKind::Throw\n                                     // GeneratorResumeKind::Return is the default case\n            ],\n        );\n        // Return branch\n        self.bytecode.emit_set_accumulator(value.variable());\n        self.bytecode.emit_re_throw();\n\n        // Normal branch\n        let normal = self.next_opcode_location();\n        let end = self.jump();\n\n        // Throw branch\n        let throw = self.next_opcode_location();\n        self.bytecode.emit_throw(value.variable());\n\n        self.patch_jump(end);\n        self.bytecode\n            .patch_jump_table(jump_table_index, &[normal, throw]);\n    }\n\n    pub(crate) fn generator_delegate_next(\n        &mut self,\n        value: &Register,\n        resume_kind: &Register,\n        is_return: &Register,\n    ) -> (Label, Label) {\n        let iterator = self.register_allocator.alloc();\n        let next = self.register_allocator.alloc();\n        self.bytecode\n            .emit_iterator_pop(iterator.variable(), next.variable());\n\n        // NOTE: +4 to jump past the index operand.\n        let jump_table_index = self.next_opcode_location() + size_of::<u32>() as u32;\n        self.bytecode.emit_jump_table(\n            resume_kind.index(),\n            thin_vec![\n                Self::DUMMY_ADDRESS, // GeneratorResumeKind::Normal\n                Self::DUMMY_ADDRESS, // GeneratorResumeKind::Throw\n                                     // GeneratorResumeKind::Return is the default case\n            ],\n        );\n\n        // GeneratorResumeKind::Return\n        let name_index = self.get_or_insert_string(js_string!(\"return\"));\n        self.bytecode\n            .emit_move(is_return.variable(), iterator.variable());\n\n        self.bytecode\n            .emit_get_method(is_return.variable(), name_index.into());\n\n        // If the return method is unavailable, jump outside the resume.\n        let return_method_undefined = self.jump_if_null_or_undefined(is_return);\n\n        // Return method is available. Call and set `is_return = true` and\n        // `value = returned value`.\n        self.push_from_register(&iterator);\n        self.push_from_register(is_return);\n        self.push_from_register(value);\n        self.bytecode.emit_call(1u8.into());\n        self.bytecode.emit_store_true(is_return.variable());\n        self.pop_into_register(value);\n        let return_jump = self.jump();\n\n        // GeneratorResumeKind::Normal\n        let normal = self.next_opcode_location();\n        self.push_from_register(&iterator);\n        self.push_from_register(&next);\n        self.push_from_register(value);\n        self.bytecode.emit_call(1u8.into());\n        self.bytecode.emit_store_false(is_return.variable());\n        self.pop_into_register(value);\n        let normal_jump = self.jump();\n\n        // GeneratorResumeKind::Throw\n        let throw = self.next_opcode_location();\n        let name_index = self.get_or_insert_string(js_string!(\"throw\"));\n        self.bytecode\n            .emit_move(is_return.variable(), iterator.variable());\n        self.bytecode\n            .emit_get_method(is_return.variable(), name_index.into());\n\n        let skip_push = self.jump_if_not_undefined(is_return);\n        self.bytecode\n            .emit_iterator_push(iterator.variable(), next.variable());\n        // If the return method is unavailable, jump outside the resume.\n        let throw_method_undefined = self.jump();\n\n        self.patch_jump(skip_push);\n\n        // Throw method is available. Call and set `is_return = true` and\n        // `value = returned value`.\n        self.push_from_register(&iterator);\n        self.push_from_register(is_return);\n        self.push_from_register(value);\n        self.bytecode.emit_call(1u8.into());\n        self.bytecode.emit_store_false(is_return.variable());\n        self.pop_into_register(value);\n\n        self.bytecode\n            .patch_jump_table(jump_table_index, &[normal, throw]);\n        self.patch_jump(return_jump);\n        self.patch_jump(normal_jump);\n\n        self.bytecode\n            .emit_iterator_push(iterator.variable(), next.variable());\n        self.register_allocator.dealloc(iterator);\n        self.register_allocator.dealloc(next);\n\n        (return_method_undefined, throw_method_undefined)\n    }\n\n    pub(crate) fn generator_delegate_resume(\n        &mut self,\n        value: &Register,\n        resume_kind: &Register,\n        is_return: &Register,\n    ) -> (Label, Label) {\n        let not_throw = self.jump_if_not_resume_kind(GeneratorResumeKind::Throw, resume_kind);\n\n        // resume_kind is throw. Pop the active iterator and raise the error.\n        self.bytecode\n            .emit_iterator_pop(resume_kind.variable(), resume_kind.variable());\n        self.bytecode.emit_throw(value.variable());\n        self.patch_jump(not_throw);\n\n        // resume_kind is not throw. Try to update the iterator result\n        self.bytecode.emit_iterator_update_result(value.variable());\n\n        // `value` should contain a boolean with `iterator.done()` at this point.\n        // skip pop if the iterator is not done yet\n        let skip_pop = self.jump_if_false(value);\n\n        self.bytecode.emit_iterator_value(value.variable());\n        self.bytecode\n            .emit_iterator_pop(resume_kind.variable(), resume_kind.variable());\n        let return_gen = self.jump_if_true(is_return);\n        let exit = self.jump();\n\n        self.patch_jump(skip_pop);\n\n        (return_gen, exit)\n    }\n}\n"
  },
  {
    "path": "core/engine/src/bytecompiler/jump_control.rs",
    "content": "//! `JumpControlInfo` tracks relevant jump information used during compilation.\n//!\n//! Primarily, jump control tracks information related to the compilation of [iteration\n//! statements][iteration spec], [switch statements][switch spec], [try statements][try spec],\n//! and [labelled statements][labelled spec].\n//!\n//! [iteration spec]: https://tc39.es/ecma262/#sec-iteration-statements\n//! [switch spec]: https://tc39.es/ecma262/#sec-switch-statement\n//! [try spec]: https://tc39.es/ecma262/#sec-try-statement\n//! [labelled spec]: https://tc39.es/ecma262/#sec-labelled-statements\n\nuse super::Register;\nuse crate::{\n    bytecompiler::{ByteCompiler, Label},\n    vm::{CallFrame, Handler, opcode::Address},\n};\nuse bitflags::bitflags;\nuse boa_interner::Sym;\nuse thin_vec::thin_vec;\n\n/// An actions to be performed for the local control flow.\n#[derive(Debug, Clone, Copy)]\npub(crate) enum JumpRecordAction {\n    /// Places a [`crate::vm::opcode::Opcode::Jump`], transfers to a specified [`JumpControlInfo`] to be handled when it gets popped.\n    Transfer {\n        /// [`JumpControlInfo`] index to be transferred.\n        index: u32,\n    },\n\n    /// Places [`crate::vm::opcode::Opcode::PopEnvironment`] opcodes, `count` times.\n    PopEnvironments { count: u32 },\n\n    /// Closes the an iterator.\n    CloseIterator { r#async: bool },\n\n    /// Handles finally, this needs to be done if we are in the try or catch section of a try statement that\n    /// has a finally block.\n    ///\n    /// It places push integer value [`crate::vm::opcode::Opcode`] as well as [`crate::vm::opcode::Opcode::StoreFalse`], which means don't [`ReThrow`](crate::vm::opcode::Opcode::ReThrow).\n    ///\n    /// The integer is an index used to jump. See [`crate::vm::opcode::Opcode::JumpTable`]. This is needed because the following code:\n    ///\n    /// ```JavaScript\n    /// do {\n    ///     try {\n    ///         if (cond) {\n    ///             continue;\n    ///         }\n    ///\n    ///         break;\n    ///     } finally {\n    ///         // Must execute the finally, even if `continue` is executed or `break` is executed.\n    ///     }\n    /// } while (true)\n    /// ```\n    ///\n    /// Both `continue` and `break` must go through the finally, but the `continue` goes to the beginning of the loop,\n    /// and the `break` goes to the end of the loop, this is solved by having a jump table (See [`crate::vm::opcode::Opcode::JumpTable`])\n    /// at the end of finally (It is constructed in [`ByteCompiler::pop_try_with_finally_control_info()`]).\n    HandleFinally {\n        /// Jump table index.\n        index: u32,\n        /// Register for the flag that indicated if the finally block needs to re throw.\n        finally_throw_flag: u32,\n        /// Register for the index in the jump table.\n        finally_throw_index: u32,\n    },\n}\n\n/// Local Control flow type.\n#[derive(Debug, Clone, Copy, PartialEq, Eq)]\npub(crate) enum JumpRecordKind {\n    Break,\n    Continue,\n    Return { return_value_on_stack: bool },\n}\n\n/// This represents a local control flow handling. See [`JumpRecordKind`] for types.\n#[derive(Debug, Clone)]\npub(crate) struct JumpRecord {\n    kind: JumpRecordKind,\n    label: Label,\n    actions: Vec<JumpRecordAction>,\n}\n\nimpl JumpRecord {\n    pub(crate) const fn new(kind: JumpRecordKind, actions: Vec<JumpRecordAction>) -> Self {\n        Self {\n            kind,\n            label: ByteCompiler::DUMMY_LABEL,\n            actions,\n        }\n    }\n\n    /// Performs the [`JumpRecordAction`]s.\n    pub(crate) fn perform_actions(\n        mut self,\n        start_address: Address,\n        compiler: &mut ByteCompiler<'_>,\n    ) {\n        while let Some(action) = self.actions.pop() {\n            match action {\n                JumpRecordAction::Transfer { index } => {\n                    self.label = compiler.jump();\n                    compiler.jump_info[index as usize].jumps.push(self);\n\n                    // Don't continue actions, let the delegate jump control info handle it!\n                    return;\n                }\n                JumpRecordAction::PopEnvironments { count } => {\n                    for _ in 0..count {\n                        compiler.bytecode.emit_pop_environment();\n                    }\n                }\n                JumpRecordAction::HandleFinally {\n                    index: value,\n                    finally_throw_flag,\n                    finally_throw_index,\n                } => {\n                    let index = value as i32;\n                    compiler\n                        .bytecode\n                        .emit_store_false(finally_throw_flag.into());\n                    compiler.emit_store_integer_with_index(index, finally_throw_index.into());\n                }\n                JumpRecordAction::CloseIterator { r#async } => {\n                    compiler.iterator_close(r#async);\n                }\n            }\n        }\n\n        // If there are no actions left, finalize the jump record.\n        match self.kind {\n            JumpRecordKind::Break => compiler.patch_jump(self.label),\n            JumpRecordKind::Continue => compiler.patch_jump_with_target(self.label, start_address),\n            JumpRecordKind::Return {\n                return_value_on_stack,\n            } => {\n                if return_value_on_stack {\n                    let value = compiler.register_allocator.alloc();\n                    compiler.pop_into_register(&value);\n                    compiler.bytecode.emit_set_accumulator(value.variable());\n                    compiler.register_allocator.dealloc(value);\n                }\n\n                match (compiler.is_async(), compiler.is_generator()) {\n                    // Taken from:\n                    //  - 27.6.3.2 AsyncGeneratorStart ( generator, generatorBody ): https://tc39.es/ecma262/#sec-asyncgeneratorstart\n                    //\n                    // Note: If we are returning we have to close the async generator function.\n                    (true, true) => compiler.bytecode.emit_async_generator_close(),\n\n                    // Taken from:\n                    //  - 27.7.5.2 AsyncBlockStart ( promiseCapability, asyncBody, asyncContext ): <https://tc39.es/ecma262/#sec-asyncblockstart>\n                    //\n                    // Note: If there is promise capability resolve or reject it based on pending exception.\n                    (true, false) => {\n                        let has_exception = compiler.register_allocator.alloc();\n                        let exception = compiler.register_allocator.alloc();\n\n                        compiler\n                            .bytecode\n                            .emit_maybe_exception(has_exception.variable(), exception.variable());\n\n                        // Pushes `undefined` to the stack, which acts as the\n                        // `this` value of the call.\n                        compiler.push_from_register(&CallFrame::undefined_register());\n\n                        compiler.if_else_with_dealloc(\n                            has_exception,\n                            |compiler| {\n                                // has_exception == true, so we need to call `reject`\n                                // with the current exception.\n\n                                compiler.push_from_register(\n                                    &CallFrame::promise_capability_reject_register(),\n                                );\n                                compiler.push_from_register(&exception);\n                                compiler.register_allocator.dealloc(exception);\n                                compiler.bytecode.emit_call(1u8.into());\n                            },\n                            |compiler| {\n                                // has_exception == false, call `resolve` normally.\n\n                                compiler.push_from_register(\n                                    &CallFrame::promise_capability_resolve_register(),\n                                );\n\n                                {\n                                    let value = compiler.register_allocator.alloc();\n                                    compiler\n                                        .bytecode\n                                        .emit_set_register_from_accumulator(value.variable());\n                                    compiler.push_from_register(&value);\n                                    compiler.register_allocator.dealloc(value);\n                                }\n                                compiler.bytecode.emit_call(1u8.into());\n                            },\n                        );\n\n                        // Finally, set the accumulator value to the promise from the\n                        // promise capability\n                        compiler.bytecode.emit_set_accumulator(\n                            (CallFrame::PROMISE_CAPABILITY_PROMISE_REGISTER_INDEX as u32).into(),\n                        );\n                    }\n                    (false, false) => {\n                        // TODO: We can omit checking for return, when constructing for functions,\n                        // that cannot be constructed, like arrow functions.\n                        compiler.bytecode.emit_check_return();\n                    }\n                    (false, true) => {}\n                }\n\n                compiler.bytecode.emit_return();\n            }\n        }\n    }\n}\n\n/// Boa's `ByteCompiler` jump information tracking struct.\n#[derive(Debug)]\npub(crate) struct JumpControlInfo {\n    label: Option<Sym>,\n    start_address: Address,\n    pub(crate) flags: JumpControlInfoFlags,\n    pub(crate) jumps: Vec<JumpRecord>,\n    current_open_environments_count: u32,\n    pub(crate) finally_throw: Option<(u32, u32)>,\n}\n\nbitflags! {\n    /// A bitflag that contains the type flags and relevant booleans for `JumpControlInfo`.\n    #[derive(Default, Debug, Clone, Copy)]\n    pub(crate) struct JumpControlInfoFlags: u8 {\n        const LOOP = 0b0000_0001;\n        const SWITCH = 0b0000_0010;\n\n        /// A try statement with a finally block.\n        ///\n        /// We emit special instructions to handle [`JumpRecord`]s in [`ByteCompiler::pop_try_with_finally_control_info()`].\n        const TRY_WITH_FINALLY = 0b0000_0100;\n\n        /// Are we in the finally block of the try statement?\n        const IN_FINALLY = 0b0000_1000;\n\n        const LABELLED = 0b0001_0000;\n        const ITERATOR_LOOP = 0b0010_0000;\n        const FOR_AWAIT_OF_LOOP = 0b0100_0000;\n\n        /// Is the statement compiled with use_expr set to true.\n        ///\n        /// This bitflag is inherited if the previous [`JumpControlInfo`].\n        const USE_EXPR = 0b1000_0000;\n    }\n}\n\n/// ---- `JumpControlInfo` Creation Methods ----\nimpl JumpControlInfo {\n    fn new(current_open_environments_count: u32) -> Self {\n        Self {\n            label: None,\n            start_address: ByteCompiler::DUMMY_ADDRESS,\n            flags: JumpControlInfoFlags::default(),\n            jumps: Vec::new(),\n            current_open_environments_count,\n            finally_throw: None,\n        }\n    }\n\n    pub(crate) const fn with_label(mut self, label: Option<Sym>) -> Self {\n        self.label = label;\n        self\n    }\n\n    pub(crate) const fn with_start_address(mut self, address: Address) -> Self {\n        self.start_address = address;\n        self\n    }\n\n    pub(crate) fn with_loop_flag(mut self, value: bool) -> Self {\n        self.flags.set(JumpControlInfoFlags::LOOP, value);\n        self\n    }\n\n    pub(crate) fn with_switch_flag(mut self, value: bool) -> Self {\n        self.flags.set(JumpControlInfoFlags::SWITCH, value);\n        self\n    }\n\n    pub(crate) fn with_try_with_finally_flag(mut self, flag: &Register, index: &Register) -> Self {\n        self.finally_throw = Some((flag.index(), index.index()));\n        self\n    }\n\n    pub(crate) fn with_labelled_block_flag(mut self, value: bool) -> Self {\n        self.flags.set(JumpControlInfoFlags::LABELLED, value);\n        self\n    }\n\n    pub(crate) fn with_iterator_loop(mut self, value: bool) -> Self {\n        self.flags.set(JumpControlInfoFlags::ITERATOR_LOOP, value);\n        self\n    }\n\n    pub(crate) fn with_for_await_of_loop(mut self, value: bool) -> Self {\n        self.flags\n            .set(JumpControlInfoFlags::FOR_AWAIT_OF_LOOP, value);\n        self\n    }\n}\n\n/// ---- `JumpControlInfo` const fn methods ----\nimpl JumpControlInfo {\n    pub(crate) const fn label(&self) -> Option<Sym> {\n        self.label\n    }\n\n    pub(crate) const fn start_address(&self) -> Address {\n        self.start_address\n    }\n\n    pub(crate) const fn is_loop(&self) -> bool {\n        self.flags.contains(JumpControlInfoFlags::LOOP)\n    }\n\n    pub(crate) const fn is_switch(&self) -> bool {\n        self.flags.contains(JumpControlInfoFlags::SWITCH)\n    }\n\n    pub(crate) const fn is_try_with_finally_block(&self) -> bool {\n        self.finally_throw.is_some()\n    }\n\n    pub(crate) const fn is_labelled(&self) -> bool {\n        self.flags.contains(JumpControlInfoFlags::LABELLED)\n    }\n\n    pub(crate) const fn in_finally(&self) -> bool {\n        self.flags.contains(JumpControlInfoFlags::IN_FINALLY)\n    }\n\n    pub(crate) const fn use_expr(&self) -> bool {\n        self.flags.contains(JumpControlInfoFlags::USE_EXPR)\n    }\n\n    pub(crate) const fn iterator_loop(&self) -> bool {\n        self.flags.contains(JumpControlInfoFlags::ITERATOR_LOOP)\n    }\n\n    pub(crate) const fn for_await_of_loop(&self) -> bool {\n        self.flags.contains(JumpControlInfoFlags::FOR_AWAIT_OF_LOOP)\n    }\n}\n\n/// ---- `JumpControlInfo` interaction methods ----\nimpl JumpControlInfo {\n    /// Sets the `label` field of `JumpControlInfo`.\n    pub(crate) fn set_label(&mut self, label: Option<Sym>) {\n        assert!(self.label.is_none());\n        self.label = label;\n    }\n\n    /// Sets the `start_address` field of `JumpControlInfo`.\n    pub(crate) fn set_start_address(&mut self, start_address: Address) {\n        self.start_address = start_address;\n    }\n}\n\n// `JumpControlInfo` related methods that are implemented on `ByteCompiler`.\nimpl ByteCompiler<'_> {\n    /// Pushes a generic `JumpControlInfo` onto `ByteCompiler`\n    ///\n    /// Default `JumpControlInfoKind` is `JumpControlInfoKind::Loop`\n    pub(crate) fn push_empty_loop_jump_control(&mut self, use_expr: bool) {\n        let new_info =\n            JumpControlInfo::new(self.current_open_environments_count).with_loop_flag(true);\n        self.push_control_info(new_info, use_expr);\n    }\n\n    pub(crate) fn current_jump_control_mut(&mut self) -> Option<&mut JumpControlInfo> {\n        self.jump_info.last_mut()\n    }\n\n    pub(crate) fn push_control_info(&mut self, mut info: JumpControlInfo, use_expr: bool) {\n        info.flags.set(JumpControlInfoFlags::USE_EXPR, use_expr);\n\n        if let Some(last) = self.jump_info.last() {\n            // Inherits the `JumpControlInfoFlags::USE_EXPR` flag.\n            info.flags |= last.flags & JumpControlInfoFlags::USE_EXPR;\n        }\n\n        self.jump_info.push(info);\n    }\n\n    /// Pushes an exception [`Handler`].\n    ///\n    /// Must be patched with [`Self::patch_handler()`].\n    #[must_use]\n    pub(crate) fn push_handler(&mut self) -> u32 {\n        let handler_index = self.handlers.len() as u32;\n        let start_address = self.next_opcode_location();\n\n        let environment_count = self.current_open_environments_count;\n        self.handlers.push(Handler {\n            start: start_address,\n            end: Self::DUMMY_ADDRESS,\n            environment_count,\n        });\n\n        handler_index\n    }\n\n    pub(crate) fn patch_handler(&mut self, handler_index: u32) {\n        let handler_index = handler_index as usize;\n        let handler_address = self.next_opcode_location();\n\n        assert_eq!(\n            self.handlers[handler_index].end,\n            Self::DUMMY_ADDRESS,\n            \"handler already set\"\n        );\n        assert!(\n            handler_address >= self.handlers[handler_index].start,\n            \"handler end is before that start\"\n        );\n\n        self.handlers[handler_index].end = handler_address;\n    }\n\n    /// Does the jump control info have the `use_expr` flag set to true.\n    ///\n    /// See [`JumpControlInfoFlags`].\n    pub(crate) fn jump_control_info_has_use_expr(&self) -> bool {\n        if let Some(last) = self.jump_info.last() {\n            return last.use_expr();\n        }\n\n        false\n    }\n\n    // ---- Labelled Statement JumpControlInfo methods ---- //\n\n    /// Pushes a `LabelledStatement`'s `JumpControlInfo` onto the `jump_info` stack.\n    pub(crate) fn push_labelled_control_info(\n        &mut self,\n        label: Sym,\n        start_address: Address,\n        use_expr: bool,\n    ) {\n        let new_info = JumpControlInfo::new(self.current_open_environments_count)\n            .with_labelled_block_flag(true)\n            .with_label(Some(label))\n            .with_start_address(start_address);\n\n        self.push_control_info(new_info, use_expr);\n    }\n\n    /// Pops and handles the info for a label's `JumpControlInfo`\n    ///\n    /// # Panic\n    ///  - Will panic if `jump_info` stack is empty.\n    ///  - Will panic if popped `JumpControlInfo` is not for a `LabelledStatement`.\n    pub(crate) fn pop_labelled_control_info(&mut self) {\n        assert!(!self.jump_info.is_empty());\n        let info = self.jump_info.pop().expect(\"no jump information found\");\n\n        assert!(info.is_labelled());\n        assert!(info.label().is_some());\n\n        let start_address = info.start_address();\n        for jump_record in info.jumps {\n            jump_record.perform_actions(start_address, self);\n        }\n    }\n    // ---- `IterationStatement`'s `JumpControlInfo` methods ---- //\n\n    /// Pushes an `WhileStatement`, `ForStatement` or `DoWhileStatement`'s `JumpControlInfo` on to the `jump_info` stack.\n    pub(crate) fn push_loop_control_info(\n        &mut self,\n        label: Option<Sym>,\n        start_address: Address,\n        use_expr: bool,\n    ) {\n        let new_info = JumpControlInfo::new(self.current_open_environments_count)\n            .with_loop_flag(true)\n            .with_label(label)\n            .with_start_address(start_address);\n\n        self.push_control_info(new_info, use_expr);\n    }\n\n    /// Pushes a `ForInOfStatement`'s `JumpControlInfo` on to the `jump_info` stack.\n    pub(crate) fn push_loop_control_info_for_of_in_loop(\n        &mut self,\n        label: Option<Sym>,\n        start_address: Address,\n        use_expr: bool,\n    ) {\n        let new_info = JumpControlInfo::new(self.current_open_environments_count)\n            .with_loop_flag(true)\n            .with_label(label)\n            .with_start_address(start_address)\n            .with_iterator_loop(true);\n\n        self.push_control_info(new_info, use_expr);\n    }\n\n    pub(crate) fn push_loop_control_info_for_await_of_loop(\n        &mut self,\n        label: Option<Sym>,\n        start_address: Address,\n        use_expr: bool,\n    ) {\n        let new_info = JumpControlInfo::new(self.current_open_environments_count)\n            .with_loop_flag(true)\n            .with_label(label)\n            .with_start_address(start_address)\n            .with_iterator_loop(true)\n            .with_for_await_of_loop(true);\n\n        self.push_control_info(new_info, use_expr);\n    }\n\n    /// Pops and handles the info for a loop control block's `JumpControlInfo`\n    ///\n    /// # Panic\n    ///  - Will panic if `jump_info` stack is empty.\n    ///  - Will panic if popped `JumpControlInfo` is not for a loop block.\n    pub(crate) fn pop_loop_control_info(&mut self) {\n        assert!(!self.jump_info.is_empty());\n        let info = self.jump_info.pop().expect(\"no jump information found\");\n\n        assert!(info.is_loop());\n\n        let start_address = info.start_address();\n        for jump_record in info.jumps {\n            jump_record.perform_actions(start_address, self);\n        }\n    }\n\n    // ---- `SwitchStatement` `JumpControlInfo` methods ---- //\n\n    /// Pushes a `SwitchStatement`'s `JumpControlInfo` on to the `jump_info` stack.\n    pub(crate) fn push_switch_control_info(\n        &mut self,\n        label: Option<Sym>,\n        start_address: Address,\n        use_expr: bool,\n    ) {\n        let new_info = JumpControlInfo::new(self.current_open_environments_count)\n            .with_switch_flag(true)\n            .with_label(label)\n            .with_start_address(start_address);\n\n        self.push_control_info(new_info, use_expr);\n    }\n\n    /// Pops and handles the info for a switch block's `JumpControlInfo`\n    ///\n    /// # Panic\n    ///  - Will panic if `jump_info` stack is empty.\n    ///  - Will panic if popped `JumpControlInfo` is not for a switch block.\n    pub(crate) fn pop_switch_control_info(&mut self) {\n        assert!(!self.jump_info.is_empty());\n        let info = self.jump_info.pop().expect(\"no jump information found\");\n\n        assert!(info.is_switch());\n\n        let start_address = info.start_address();\n        for jump_record in info.jumps {\n            jump_record.perform_actions(start_address, self);\n        }\n    }\n\n    // ---- `TryStatement`'s `JumpControlInfo` methods ---- //\n\n    /// Pushes a `TryStatement`'s `JumpControlInfo` onto the `jump_info` stack.\n    pub(crate) fn push_try_with_finally_control_info(\n        &mut self,\n        flag: &Register,\n        index: &Register,\n        use_expr: bool,\n    ) {\n        let new_info = JumpControlInfo::new(self.current_open_environments_count)\n            .with_try_with_finally_flag(flag, index);\n\n        self.push_control_info(new_info, use_expr);\n    }\n\n    /// Pops and handles the info for a try statement with a finally block.\n    ///\n    /// # Panic\n    ///  - Will panic if popped `JumpControlInfo` is not for a try block.\n    pub(crate) fn pop_try_with_finally_control_info(&mut self, finally_start: Address) {\n        assert!(!self.jump_info.is_empty());\n        let info = self.jump_info.pop().expect(\"no jump information found\");\n\n        assert!(info.is_try_with_finally_block());\n\n        if info.jumps.is_empty() {\n            return;\n        }\n\n        let (_, finally_throw_index) = info.finally_throw.expect(\"try with finally\");\n\n        for JumpRecord { label, .. } in &info.jumps {\n            self.patch_jump_with_target(*label, finally_start);\n        }\n\n        // NOTE: +4 to jump past the index operand.\n        let jump_table_index = self.next_opcode_location() + size_of::<u32>() as u32;\n        self.bytecode.emit_jump_table(\n            finally_throw_index,\n            thin_vec![Self::DUMMY_ADDRESS; info.jumps.len()],\n        );\n\n        // We are assuming any indices outside our jump table will fallback\n        // to executing the next available op. Since we kinda control the jump\n        // table index here, this doesn't matter too much, but we _could_ also\n        // throw a PanicError on the next instruction.\n\n        let mut patch_jumps = Vec::with_capacity(info.jumps.len());\n        // Handle breaks/continue/returns in a finally block\n        for i in 0..info.jumps.len() {\n            patch_jumps.push(self.next_opcode_location());\n\n            let jump_record = info.jumps[i].clone();\n            jump_record.perform_actions(Self::DUMMY_ADDRESS, self);\n        }\n\n        self.bytecode\n            .patch_jump_table(jump_table_index, &patch_jumps);\n    }\n\n    pub(crate) fn jump_info_open_environment_count(&self, index: usize) -> u32 {\n        let current = &self.jump_info[index];\n        if let Some(next) = self.jump_info.get(index + 1) {\n            return next.current_open_environments_count - current.current_open_environments_count;\n        }\n\n        self.current_open_environments_count - current.current_open_environments_count\n    }\n}\n"
  },
  {
    "path": "core/engine/src/bytecompiler/mod.rs",
    "content": "//! Boa's bytecode compiler.\n//!\n//! The bytecompiler is responsible for lowering ECMAScript AST nodes into\n//! executable bytecode for the virtual machine.\n//!\n//! It walks the parsed AST and emits instructions using the bytecode emitter,\n//! producing code blocks that are later executed by the VM.\n//!\n//! This module provides the necessary functionality to compile JavaScript code into bytecode.\n\nmod class;\nmod declaration;\nmod declarations;\nmod env;\nmod expression;\nmod function;\nmod generator;\nmod jump_control;\nmod module;\nmod register;\nmod statement;\nmod utils;\n\nuse std::{\n    borrow::{Borrow, BorrowMut},\n    cell::Cell,\n    ops::{Deref, DerefMut},\n};\n\nuse crate::{\n    JsBigInt, JsStr, JsString, SourceText, SpannedSourceText,\n    builtins::function::{ThisMode, arguments::MappedArguments},\n    js_string,\n    vm::{\n        CallFrame, CodeBlock, CodeBlockFlags, Constant, GeneratorResumeKind, GlobalFunctionBinding,\n        Handler, InlineCache,\n        opcode::{Address, BindingOpcode, BytecodeEmitter, RegisterOperand},\n        source_info::{SourceInfo, SourceMap, SourceMapBuilder, SourcePath},\n    },\n};\nuse boa_ast::{\n    Declaration, Expression, LinearSpan, Position, Spanned, Statement, StatementList,\n    StatementListItem,\n    declaration::{Binding, LexicalDeclaration, VarDeclaration},\n    expression::{\n        Call, Identifier, New, Optional, OptionalOperationKind,\n        access::{PropertyAccess, PropertyAccessField},\n        literal::ObjectMethodDefinition,\n        operator::{\n            Binary,\n            assign::AssignTarget,\n            binary::{BinaryOp, RelationalOp},\n            update::UpdateTarget,\n        },\n    },\n    function::{\n        ArrowFunction, AsyncArrowFunction, AsyncFunctionDeclaration, AsyncFunctionExpression,\n        AsyncGeneratorDeclaration, AsyncGeneratorExpression, ClassMethodDefinition,\n        FormalParameterList, FunctionBody, FunctionDeclaration, FunctionExpression,\n        GeneratorDeclaration, GeneratorExpression, PrivateName,\n    },\n    operations::returns_value,\n    pattern::Pattern,\n    property::MethodDefinitionKind,\n    scope::{BindingLocator, BindingLocatorError, FunctionScopes, IdentifierReference, Scope},\n};\nuse boa_gc::Gc;\nuse boa_interner::{Interner, Sym};\nuse boa_macros::js_str;\nuse boa_string::StaticJsStrings;\nuse rustc_hash::FxHashMap;\nuse thin_vec::ThinVec;\n\npub(crate) use declarations::{\n    global_declaration_instantiation_context, prepare_eval_declaration_instantiation,\n};\npub(crate) use function::FunctionCompiler;\npub(crate) use jump_control::JumpControlInfo;\npub(crate) use register::*;\n\npub(crate) trait ToJsString {\n    fn to_js_string(&self, interner: &Interner) -> JsString;\n}\n\nimpl ToJsString for Sym {\n    #[allow(clippy::cast_possible_truncation)]\n    fn to_js_string(&self, interner: &Interner) -> JsString {\n        let utf16 = interner.resolve_expect(*self).utf16();\n        if interner.is_latin1(*self) {\n            let bytes: Vec<u8> = utf16.iter().map(|&c| c as u8).collect();\n            js_string!(JsStr::latin1(&bytes))\n        } else {\n            js_string!(utf16)\n        }\n    }\n}\n\nimpl ToJsString for Identifier {\n    fn to_js_string(&self, interner: &Interner) -> JsString {\n        self.sym().to_js_string(interner)\n    }\n}\n\n/// Describes how a node has been defined in the source code.\n#[derive(Debug, Clone, Copy, PartialEq)]\npub(crate) enum NodeKind {\n    Declaration,\n    Expression,\n}\n\n/// Describes the type of a function.\n#[derive(Debug, Clone, Copy, PartialEq)]\npub(crate) enum FunctionKind {\n    Ordinary,\n    Arrow,\n    AsyncArrow,\n    Async,\n    Generator,\n    AsyncGenerator,\n}\n\nimpl FunctionKind {\n    pub(crate) const fn is_arrow(self) -> bool {\n        matches!(self, Self::Arrow | Self::AsyncArrow)\n    }\n\n    pub(crate) const fn is_async(self) -> bool {\n        matches!(self, Self::Async | Self::AsyncGenerator | Self::AsyncArrow)\n    }\n\n    pub(crate) const fn is_generator(self) -> bool {\n        matches!(self, Self::Generator | Self::AsyncGenerator)\n    }\n}\n\n/// Describes the complete specification of a function node.\n#[derive(Debug, Clone, Copy)]\npub(crate) struct FunctionSpec<'a> {\n    pub(crate) kind: FunctionKind,\n    pub(crate) name: Option<Identifier>,\n    parameters: &'a FormalParameterList,\n    body: &'a FunctionBody,\n    pub(crate) scopes: &'a FunctionScopes,\n    pub(crate) name_scope: Option<&'a Scope>,\n    linear_span: Option<LinearSpan>,\n    pub(crate) contains_direct_eval: bool,\n}\n\nimpl PartialEq for FunctionSpec<'_> {\n    fn eq(&self, other: &Self) -> bool {\n        // all fields except `linear_span`\n        self.kind == other.kind\n            && self.name == other.name\n            && self.parameters == other.parameters\n            && self.body == other.body\n            && self.scopes == other.scopes\n            && self.name_scope == other.name_scope\n    }\n}\n\nimpl<'a> From<&'a FunctionDeclaration> for FunctionSpec<'a> {\n    fn from(function: &'a FunctionDeclaration) -> Self {\n        FunctionSpec {\n            kind: FunctionKind::Ordinary,\n            name: Some(function.name()),\n            parameters: function.parameters(),\n            body: function.body(),\n            scopes: function.scopes(),\n            name_scope: None,\n            linear_span: Some(function.linear_span()),\n            contains_direct_eval: function.contains_direct_eval(),\n        }\n    }\n}\n\nimpl<'a> From<&'a GeneratorDeclaration> for FunctionSpec<'a> {\n    fn from(function: &'a GeneratorDeclaration) -> Self {\n        FunctionSpec {\n            kind: FunctionKind::Generator,\n            name: Some(function.name()),\n            parameters: function.parameters(),\n            body: function.body(),\n            scopes: function.scopes(),\n            name_scope: None,\n            linear_span: Some(function.linear_span()),\n            contains_direct_eval: function.contains_direct_eval(),\n        }\n    }\n}\n\nimpl<'a> From<&'a AsyncFunctionDeclaration> for FunctionSpec<'a> {\n    fn from(function: &'a AsyncFunctionDeclaration) -> Self {\n        FunctionSpec {\n            kind: FunctionKind::Async,\n            name: Some(function.name()),\n            parameters: function.parameters(),\n            body: function.body(),\n            scopes: function.scopes(),\n            name_scope: None,\n            linear_span: Some(function.linear_span()),\n            contains_direct_eval: function.contains_direct_eval(),\n        }\n    }\n}\n\nimpl<'a> From<&'a AsyncGeneratorDeclaration> for FunctionSpec<'a> {\n    fn from(function: &'a AsyncGeneratorDeclaration) -> Self {\n        FunctionSpec {\n            kind: FunctionKind::AsyncGenerator,\n            name: Some(function.name()),\n            parameters: function.parameters(),\n            body: function.body(),\n            scopes: function.scopes(),\n            name_scope: None,\n            linear_span: Some(function.linear_span()),\n            contains_direct_eval: function.contains_direct_eval(),\n        }\n    }\n}\n\nimpl<'a> From<&'a FunctionExpression> for FunctionSpec<'a> {\n    fn from(function: &'a FunctionExpression) -> Self {\n        FunctionSpec {\n            kind: FunctionKind::Ordinary,\n            name: function.name(),\n            parameters: function.parameters(),\n            body: function.body(),\n            scopes: function.scopes(),\n            name_scope: function.name_scope(),\n            linear_span: function.linear_span(),\n            contains_direct_eval: function.contains_direct_eval(),\n        }\n    }\n}\n\nimpl<'a> From<&'a ArrowFunction> for FunctionSpec<'a> {\n    fn from(function: &'a ArrowFunction) -> Self {\n        FunctionSpec {\n            kind: FunctionKind::Arrow,\n            name: function.name(),\n            parameters: function.parameters(),\n            body: function.body(),\n            scopes: function.scopes(),\n            name_scope: None,\n            linear_span: Some(function.linear_span()),\n            contains_direct_eval: function.contains_direct_eval(),\n        }\n    }\n}\n\nimpl<'a> From<&'a AsyncArrowFunction> for FunctionSpec<'a> {\n    fn from(function: &'a AsyncArrowFunction) -> Self {\n        FunctionSpec {\n            kind: FunctionKind::AsyncArrow,\n            name: function.name(),\n            parameters: function.parameters(),\n            body: function.body(),\n            scopes: function.scopes(),\n            name_scope: None,\n            linear_span: Some(function.linear_span()),\n            contains_direct_eval: function.contains_direct_eval(),\n        }\n    }\n}\n\nimpl<'a> From<&'a AsyncFunctionExpression> for FunctionSpec<'a> {\n    fn from(function: &'a AsyncFunctionExpression) -> Self {\n        FunctionSpec {\n            kind: FunctionKind::Async,\n            name: function.name(),\n            parameters: function.parameters(),\n            body: function.body(),\n            scopes: function.scopes(),\n            name_scope: function.name_scope(),\n            linear_span: Some(function.linear_span()),\n            contains_direct_eval: function.contains_direct_eval(),\n        }\n    }\n}\n\nimpl<'a> From<&'a GeneratorExpression> for FunctionSpec<'a> {\n    fn from(function: &'a GeneratorExpression) -> Self {\n        FunctionSpec {\n            kind: FunctionKind::Generator,\n            name: function.name(),\n            parameters: function.parameters(),\n            body: function.body(),\n            scopes: function.scopes(),\n            name_scope: function.name_scope(),\n            linear_span: Some(function.linear_span()),\n            contains_direct_eval: function.contains_direct_eval(),\n        }\n    }\n}\n\nimpl<'a> From<&'a AsyncGeneratorExpression> for FunctionSpec<'a> {\n    fn from(function: &'a AsyncGeneratorExpression) -> Self {\n        FunctionSpec {\n            kind: FunctionKind::AsyncGenerator,\n            name: function.name(),\n            parameters: function.parameters(),\n            body: function.body(),\n            scopes: function.scopes(),\n            name_scope: function.name_scope(),\n            linear_span: Some(function.linear_span()),\n            contains_direct_eval: function.contains_direct_eval(),\n        }\n    }\n}\n\nimpl<'a> From<&'a ClassMethodDefinition> for FunctionSpec<'a> {\n    fn from(method: &'a ClassMethodDefinition) -> Self {\n        let kind = match method.kind() {\n            MethodDefinitionKind::Generator => FunctionKind::Generator,\n            MethodDefinitionKind::AsyncGenerator => FunctionKind::AsyncGenerator,\n            MethodDefinitionKind::Async => FunctionKind::Async,\n            _ => FunctionKind::Ordinary,\n        };\n\n        FunctionSpec {\n            kind,\n            name: None,\n            parameters: method.parameters(),\n            body: method.body(),\n            scopes: method.scopes(),\n            name_scope: None,\n            linear_span: Some(method.linear_span()),\n            contains_direct_eval: method.contains_direct_eval(),\n        }\n    }\n}\n\nimpl<'a> From<&'a ObjectMethodDefinition> for FunctionSpec<'a> {\n    fn from(method: &'a ObjectMethodDefinition) -> Self {\n        let kind = match method.kind() {\n            MethodDefinitionKind::Generator => FunctionKind::Generator,\n            MethodDefinitionKind::AsyncGenerator => FunctionKind::AsyncGenerator,\n            MethodDefinitionKind::Async => FunctionKind::Async,\n            _ => FunctionKind::Ordinary,\n        };\n\n        FunctionSpec {\n            kind,\n            name: method.name().literal(),\n            parameters: method.parameters(),\n            body: method.body(),\n            scopes: method.scopes(),\n            name_scope: None,\n            linear_span: Some(method.linear_span()),\n            contains_direct_eval: method.contains_direct_eval(),\n        }\n    }\n}\n\n#[derive(Debug, Clone, Copy)]\npub(crate) enum MethodKind {\n    Get,\n    Set,\n    Ordinary,\n}\n\n/// Represents a callable expression, like `f()` or `new Cl()`\n#[derive(Debug, Clone, Copy)]\nenum Callable<'a> {\n    Call(&'a Call),\n    New(&'a New),\n}\n\n#[derive(Debug, Clone, PartialEq, Eq, Hash)]\nenum Literal {\n    String(JsString),\n    BigInt(JsBigInt),\n}\n\n#[must_use]\n#[derive(Debug, Clone, Copy, PartialEq, Eq)]\npub(crate) struct Label {\n    index: Address,\n}\n\n/// A loop-invariant operand that was hoisted out of a loop condition.\n///\n/// When a loop condition like `i < 10` has a literal operand, we can compile\n/// the constant (`10`) into a register once before the loop, rather than\n/// re-emitting a `PushInt8`/`PushInt32` on every iteration.\npub(crate) struct HoistedOperand {\n    /// The register holding the pre-compiled constant value.\n    pub(crate) register: Register,\n    /// `true` if this operand is the RHS of the comparison (e.g. `10` in `i < 10`).\n    pub(crate) is_rhs: bool,\n}\n\n#[derive(Debug, Clone, Copy)]\n#[allow(variant_size_differences)]\nenum Access<'a> {\n    Variable { name: Identifier },\n    Property { access: &'a PropertyAccess },\n    This,\n}\n\nimpl Access<'_> {\n    const fn from_assign_target(target: &AssignTarget) -> Result<Access<'_>, &Pattern> {\n        match target {\n            AssignTarget::Identifier(ident) => Ok(Access::Variable { name: *ident }),\n            AssignTarget::Access(access) => Ok(Access::Property { access }),\n            AssignTarget::Pattern(pat) => Err(pat),\n        }\n    }\n\n    const fn from_expression(expr: &Expression) -> Option<Access<'_>> {\n        match expr {\n            Expression::Identifier(name) => Some(Access::Variable { name: *name }),\n            Expression::PropertyAccess(access) => Some(Access::Property { access }),\n            Expression::This(_this) => Some(Access::This),\n            Expression::Parenthesized(expr) => Self::from_expression(expr.expression()),\n            _ => None,\n        }\n    }\n\n    const fn from_update_target(target: &UpdateTarget) -> Access<'_> {\n        match target {\n            UpdateTarget::Identifier(name) => Access::Variable { name: *name },\n            UpdateTarget::PropertyAccess(access) => Access::Property { access },\n        }\n    }\n}\n\n#[derive(Copy, Clone, Debug)]\npub(crate) enum BindingAccessOpcode {\n    PutLexicalValue,\n    DefInitVar,\n    SetName,\n    SetNameByLocator,\n    GetName,\n    GetNameAndLocator,\n    GetNameOrUndefined,\n    DeleteName,\n    GetLocator,\n    DefVar,\n}\n\n/// Manages the source position scope, push on creation, pop on drop.\npub(crate) struct SourcePositionGuard<'a, 'b> {\n    compiler: &'a mut ByteCompiler<'b>,\n}\nimpl<'a, 'b> SourcePositionGuard<'a, 'b> {\n    fn new(compiler: &'a mut ByteCompiler<'b>, position: Position) -> Self {\n        compiler.push_source_position(position);\n        Self { compiler }\n    }\n}\nimpl Drop for SourcePositionGuard<'_, '_> {\n    fn drop(&mut self) {\n        self.pop_source_position();\n    }\n}\nimpl<'a> Deref for SourcePositionGuard<'_, 'a> {\n    type Target = ByteCompiler<'a>;\n    fn deref(&self) -> &Self::Target {\n        self.compiler\n    }\n}\nimpl DerefMut for SourcePositionGuard<'_, '_> {\n    fn deref_mut(&mut self) -> &mut Self::Target {\n        self.compiler\n    }\n}\nimpl<'a> Borrow<ByteCompiler<'a>> for SourcePositionGuard<'_, 'a> {\n    fn borrow(&self) -> &ByteCompiler<'a> {\n        self.compiler\n    }\n}\nimpl<'a> BorrowMut<ByteCompiler<'a>> for SourcePositionGuard<'_, 'a> {\n    fn borrow_mut(&mut self) -> &mut ByteCompiler<'a> {\n        self.compiler\n    }\n}\n\n/// The [`ByteCompiler`] is used to compile ECMAScript AST from [`boa_ast`] to bytecode.\n#[derive(Debug)]\n#[allow(clippy::struct_excessive_bools)]\npub struct ByteCompiler<'ctx> {\n    /// Name of this function.\n    pub(crate) function_name: JsString,\n\n    /// The number of arguments expected.\n    pub(crate) length: u32,\n\n    pub(crate) register_allocator: RegisterAllocator,\n\n    /// `[[ThisMode]]`\n    pub(crate) this_mode: ThisMode,\n\n    /// Parameters passed to this function.\n    pub(crate) params: FormalParameterList,\n\n    /// Scope of the function parameters.\n    pub(crate) parameter_scope: Scope,\n\n    /// Bytecode\n    pub(crate) bytecode: BytecodeEmitter,\n\n    pub(crate) source_map_builder: SourceMapBuilder,\n    pub(crate) source_path: SourcePath,\n\n    pub(crate) constants: ThinVec<Constant>,\n\n    /// Locators for all bindings in the codeblock.\n    pub(crate) bindings: Vec<BindingLocator>,\n\n    pub(crate) local_binding_registers: FxHashMap<IdentifierReference, u32>,\n\n    /// The current variable scope.\n    pub(crate) variable_scope: Scope,\n\n    /// The current lexical scope.\n    pub(crate) lexical_scope: Scope,\n\n    pub(crate) current_open_environments_count: u32,\n    code_block_flags: CodeBlockFlags,\n    handlers: ThinVec<Handler>,\n    pub(crate) ic: Vec<InlineCache>,\n    literals_map: FxHashMap<Literal, u32>,\n    names_map: FxHashMap<Sym, u32>,\n    bindings_map: FxHashMap<BindingLocator, u32>,\n\n    /// Cache of non-local `const` binding values in persistent registers.\n    /// Avoids repeated `GetName` environment lookups for immutable bindings.\n    const_binding_cache: FxHashMap<BindingLocator, u32>,\n\n    jump_info: Vec<JumpControlInfo>,\n\n    /// Used to handle exception throws that escape the async function types.\n    ///\n    /// Async functions and async generator functions, need to be closed and resolved.\n    pub(crate) async_handler: Option<u32>,\n    json_parse: bool,\n\n    /// Whether the function is in a `with` statement.\n    pub(crate) in_with: bool,\n\n    /// Used to determine if a we emitted a `CreateUnmappedArgumentsObject` opcode\n    pub(crate) emitted_mapped_arguments_object_opcode: bool,\n\n    pub(crate) interner: &'ctx mut Interner,\n    spanned_source_text: SpannedSourceText,\n\n    pub(crate) global_lexs: Vec<u32>,\n    pub(crate) global_fns: Vec<GlobalFunctionBinding>,\n    pub(crate) global_vars: Vec<u32>,\n\n    #[cfg(feature = \"annex-b\")]\n    pub(crate) annex_b_function_names: Vec<Sym>,\n}\n\npub(crate) enum BindingKind {\n    Stack(u32),\n    Local(Option<u32>),\n    Global(u32),\n}\n\n/// Where the call result should go.\n#[derive(Copy, Clone)]\nenum CallResultDest<'a> {\n    /// Pop result into the given register.\n    Register(&'a Register),\n    /// Discard the result (emit Pop).\n    Discard,\n    /// Leave the result on the stack.\n    Stack,\n}\n\nimpl<'ctx> ByteCompiler<'ctx> {\n    /// Represents a placeholder address that will be patched later.\n    const DUMMY_ADDRESS: Address = Address::new(u32::MAX);\n    const DUMMY_LABEL: Label = Label {\n        index: Address::new(u32::MAX),\n    };\n\n    /// Creates a new [`ByteCompiler`].\n    #[inline]\n    #[allow(clippy::too_many_arguments)]\n    #[allow(clippy::fn_params_excessive_bools)]\n    pub(crate) fn new(\n        name: JsString,\n        strict: bool,\n        json_parse: bool,\n        variable_scope: Scope,\n        lexical_scope: Scope,\n        is_async: bool,\n        is_generator: bool,\n        interner: &'ctx mut Interner,\n        in_with: bool,\n        spanned_source_text: SpannedSourceText,\n        source_path: SourcePath,\n    ) -> ByteCompiler<'ctx> {\n        let mut code_block_flags = CodeBlockFlags::empty();\n        code_block_flags.set(CodeBlockFlags::STRICT, strict);\n        code_block_flags.set(CodeBlockFlags::IS_ASYNC, is_async);\n        code_block_flags.set(CodeBlockFlags::IS_GENERATOR, is_generator);\n        code_block_flags |= CodeBlockFlags::HAS_PROTOTYPE_PROPERTY;\n\n        let mut register_allocator = RegisterAllocator::default();\n        let undefined_register = register_allocator.alloc_persistent();\n        debug_assert_eq!(\n            undefined_register.index(),\n            CallFrame::undefined_register().index()\n        );\n        if is_async {\n            let promise_register = register_allocator.alloc_persistent();\n            let resolve_register = register_allocator.alloc_persistent();\n            let reject_register = register_allocator.alloc_persistent();\n\n            debug_assert_eq!(\n                promise_register.index(),\n                CallFrame::promise_capability_promise_register().index()\n            );\n            debug_assert_eq!(\n                resolve_register.index(),\n                CallFrame::promise_capability_resolve_register().index()\n            );\n            debug_assert_eq!(\n                reject_register.index(),\n                CallFrame::promise_capability_reject_register().index()\n            );\n\n            if is_generator {\n                let async_function_object_register = register_allocator.alloc_persistent();\n                debug_assert_eq!(\n                    async_function_object_register.index(),\n                    CallFrame::async_generator_object_register().index()\n                );\n            }\n        }\n\n        Self {\n            function_name: name,\n            length: 0,\n            bytecode: BytecodeEmitter::new(),\n            source_map_builder: SourceMapBuilder::default(),\n            constants: ThinVec::default(),\n            bindings: Vec::default(),\n            local_binding_registers: FxHashMap::default(),\n            this_mode: ThisMode::Global,\n            params: FormalParameterList::default(),\n            parameter_scope: Scope::default(),\n            current_open_environments_count: 0,\n\n            register_allocator,\n            code_block_flags,\n            handlers: ThinVec::default(),\n            ic: Vec::default(),\n\n            literals_map: FxHashMap::default(),\n            names_map: FxHashMap::default(),\n            bindings_map: FxHashMap::default(),\n            const_binding_cache: FxHashMap::default(),\n            jump_info: Vec::new(),\n            async_handler: None,\n            json_parse,\n            variable_scope,\n            lexical_scope,\n            interner,\n            spanned_source_text,\n            source_path,\n\n            #[cfg(feature = \"annex-b\")]\n            annex_b_function_names: Vec::new(),\n            in_with,\n            emitted_mapped_arguments_object_opcode: false,\n\n            global_lexs: Vec::new(),\n            global_fns: Vec::new(),\n            global_vars: Vec::new(),\n        }\n    }\n\n    pub(crate) fn source_text(&self) -> SourceText {\n        self.spanned_source_text.source_text()\n    }\n\n    pub(crate) const fn strict(&self) -> bool {\n        self.code_block_flags.contains(CodeBlockFlags::STRICT)\n    }\n\n    pub(crate) const fn is_async(&self) -> bool {\n        self.code_block_flags.contains(CodeBlockFlags::IS_ASYNC)\n    }\n\n    pub(crate) const fn is_generator(&self) -> bool {\n        self.code_block_flags.contains(CodeBlockFlags::IS_GENERATOR)\n    }\n\n    pub(crate) const fn is_async_generator(&self) -> bool {\n        self.is_async() && self.is_generator()\n    }\n\n    pub(crate) fn interner(&self) -> &Interner {\n        self.interner\n    }\n\n    fn get_or_insert_literal(&mut self, literal: Literal) -> u32 {\n        if let Some(index) = self.literals_map.get(&literal) {\n            return *index;\n        }\n\n        let value = match literal.clone() {\n            Literal::String(value) => Constant::String(value),\n            Literal::BigInt(value) => Constant::BigInt(value),\n        };\n\n        let index = self.constants.len() as u32;\n        self.constants.push(value);\n        self.literals_map.insert(literal, index);\n        index\n    }\n\n    fn get_or_insert_name(&mut self, name: Sym) -> u32 {\n        if let Some(index) = self.names_map.get(&name) {\n            return *index;\n        }\n\n        let index = self.constants.len() as u32;\n        let string = name.to_js_string(self.interner());\n        self.constants.push(Constant::String(string));\n        self.names_map.insert(name, index);\n        index\n    }\n\n    fn get_or_insert_string(&mut self, value: JsString) -> u32 {\n        self.get_or_insert_literal(Literal::String(value))\n    }\n\n    #[inline]\n    fn get_or_insert_private_name(&mut self, name: PrivateName) -> u32 {\n        self.get_or_insert_name(name.description())\n    }\n\n    // TODO: Make this return `Option<BindingKind>` instead of making BindingKind::Local\n    //       inner field optional.\n    #[inline]\n    pub(crate) fn get_binding(&mut self, binding: &IdentifierReference) -> BindingKind {\n        if binding.is_global_object() {\n            if let Some(index) = self.bindings_map.get(&binding.locator()) {\n                return BindingKind::Global(*index);\n            }\n\n            let index = self.bindings.len() as u32;\n            self.bindings.push(binding.locator().clone());\n            self.bindings_map.insert(binding.locator(), index);\n            return BindingKind::Global(index);\n        }\n\n        if binding.local() {\n            return BindingKind::Local(self.local_binding_registers.get(binding).copied());\n        }\n\n        if let Some(index) = self.bindings_map.get(&binding.locator()) {\n            return BindingKind::Stack(*index);\n        }\n\n        let index = self.bindings.len() as u32;\n        self.bindings.push(binding.locator().clone());\n        self.bindings_map.insert(binding.locator(), index);\n        BindingKind::Stack(index)\n    }\n\n    #[inline]\n    pub(crate) fn insert_binding(&mut self, binding: IdentifierReference) -> BindingKind {\n        if binding.is_global_object() {\n            if let Some(index) = self.bindings_map.get(&binding.locator()) {\n                return BindingKind::Global(*index);\n            }\n\n            let index = self.bindings.len() as u32;\n            self.bindings.push(binding.locator().clone());\n            self.bindings_map.insert(binding.locator(), index);\n            return BindingKind::Global(index);\n        }\n\n        if binding.local() {\n            return BindingKind::Local(Some(\n                *self\n                    .local_binding_registers\n                    .entry(binding)\n                    .or_insert_with(|| self.register_allocator.alloc_persistent().index()),\n            ));\n        }\n\n        if let Some(index) = self.bindings_map.get(&binding.locator()) {\n            return BindingKind::Stack(*index);\n        }\n\n        let index = self.bindings.len() as u32;\n        self.bindings.push(binding.locator().clone());\n        self.bindings_map.insert(binding.locator(), index);\n        BindingKind::Stack(index)\n    }\n\n    #[inline]\n    #[must_use]\n    pub(crate) fn push_function_to_constants(&mut self, function: Gc<CodeBlock>) -> u32 {\n        let index = self.constants.len() as u32;\n        self.constants.push(Constant::Function(function));\n        index\n    }\n\n    fn emit_binding(&mut self, opcode: BindingOpcode, name: JsString, value: &Register) {\n        match opcode {\n            BindingOpcode::Var => {\n                let binding = self.variable_scope.get_identifier_reference(name);\n                if !binding.locator().is_global() {\n                    let index = self.insert_binding(binding);\n                    self.emit_binding_access(BindingAccessOpcode::DefVar, &index, value);\n                }\n            }\n            BindingOpcode::InitVar => match self.lexical_scope.set_mutable_binding(name.clone()) {\n                Ok(binding) => {\n                    let index = self.insert_binding(binding);\n                    self.emit_binding_access(BindingAccessOpcode::DefInitVar, &index, value);\n                }\n                Err(BindingLocatorError::MutateImmutable) => {\n                    let index = self.get_or_insert_string(name);\n                    self.bytecode.emit_throw_mutate_immutable(index.into());\n                }\n                Err(BindingLocatorError::Silent) => {}\n            },\n            BindingOpcode::InitLexical => {\n                let binding = self.lexical_scope.get_identifier_reference(name);\n                let index = self.insert_binding(binding);\n                self.emit_binding_access(BindingAccessOpcode::PutLexicalValue, &index, value);\n            }\n            BindingOpcode::SetName => match self.lexical_scope.set_mutable_binding(name.clone()) {\n                Ok(binding) => {\n                    let index = self.insert_binding(binding);\n                    self.emit_binding_access(BindingAccessOpcode::SetName, &index, value);\n                }\n                Err(BindingLocatorError::MutateImmutable) => {\n                    let index = self.get_or_insert_string(name);\n                    self.bytecode.emit_throw_mutate_immutable(index.into());\n                }\n                Err(BindingLocatorError::Silent) => {}\n            },\n        }\n    }\n\n    fn next_opcode_location(&mut self) -> Address {\n        self.bytecode.next_opcode_location()\n    }\n\n    pub(crate) fn position_guard(\n        &mut self,\n        spanned: impl Spanned,\n    ) -> SourcePositionGuard<'_, 'ctx> {\n        SourcePositionGuard::new(self, spanned.span().start())\n    }\n\n    pub(crate) fn push_source_position<T>(&mut self, position: T)\n    where\n        T: Into<Option<Position>>,\n    {\n        let start_pc = self.next_opcode_location();\n        self.source_map_builder\n            .push_source_position(start_pc.as_u32(), position.into());\n    }\n\n    pub(crate) fn pop_source_position(&mut self) {\n        let start_pc = self.next_opcode_location();\n        self.source_map_builder\n            .pop_source_position(start_pc.as_u32());\n    }\n\n    pub(crate) fn emit_get_function(&mut self, dst: &Register, index: u32) {\n        self.bytecode\n            .emit_get_function(dst.variable(), index.into());\n    }\n\n    fn pop_into_register(&mut self, dst: &Register) {\n        self.bytecode.emit_pop_into_register(dst.variable());\n    }\n\n    pub(crate) fn push_from_register(&mut self, src: &Register) {\n        self.bytecode.emit_push_from_register(src.variable());\n    }\n\n    pub(crate) fn emit_binding_access(\n        &mut self,\n        opcode: BindingAccessOpcode,\n        binding: &BindingKind,\n        value: &Register,\n    ) {\n        match binding {\n            BindingKind::Global(index) => match opcode {\n                BindingAccessOpcode::SetNameByLocator => {\n                    self.bytecode.emit_set_name_by_locator(value.variable());\n                }\n                BindingAccessOpcode::GetName => {\n                    let ic_index = self.ic.len() as u32;\n                    let name = self.bindings[*index as usize].name().clone();\n                    self.ic.push(InlineCache::new(name));\n                    self.bytecode.emit_get_name_global(\n                        value.variable(),\n                        (*index).into(),\n                        ic_index.into(),\n                    );\n                }\n                BindingAccessOpcode::GetLocator => self.bytecode.emit_get_locator((*index).into()),\n                BindingAccessOpcode::DefVar => self.bytecode.emit_def_var((*index).into()),\n                BindingAccessOpcode::PutLexicalValue => self\n                    .bytecode\n                    .emit_put_lexical_value(value.variable(), (*index).into()),\n                BindingAccessOpcode::DefInitVar => self\n                    .bytecode\n                    .emit_def_init_var(value.variable(), (*index).into()),\n                BindingAccessOpcode::SetName => self\n                    .bytecode\n                    .emit_set_name(value.variable(), (*index).into()),\n                BindingAccessOpcode::GetNameAndLocator => self\n                    .bytecode\n                    .emit_get_name_and_locator(value.variable(), (*index).into()),\n                BindingAccessOpcode::GetNameOrUndefined => self\n                    .bytecode\n                    .emit_get_name_or_undefined(value.variable(), (*index).into()),\n                BindingAccessOpcode::DeleteName => self\n                    .bytecode\n                    .emit_delete_name(value.variable(), (*index).into()),\n            },\n            BindingKind::Stack(index) => match opcode {\n                BindingAccessOpcode::SetNameByLocator => {\n                    self.bytecode.emit_set_name_by_locator(value.variable());\n                }\n                BindingAccessOpcode::GetLocator => self.bytecode.emit_get_locator((*index).into()),\n                BindingAccessOpcode::DefVar => self.bytecode.emit_def_var((*index).into()),\n                BindingAccessOpcode::PutLexicalValue => self\n                    .bytecode\n                    .emit_put_lexical_value(value.variable(), (*index).into()),\n                BindingAccessOpcode::DefInitVar => self\n                    .bytecode\n                    .emit_def_init_var(value.variable(), (*index).into()),\n                BindingAccessOpcode::SetName => self\n                    .bytecode\n                    .emit_set_name(value.variable(), (*index).into()),\n                BindingAccessOpcode::GetName => self\n                    .bytecode\n                    .emit_get_name(value.variable(), (*index).into()),\n                BindingAccessOpcode::GetNameAndLocator => self\n                    .bytecode\n                    .emit_get_name_and_locator(value.variable(), (*index).into()),\n                BindingAccessOpcode::GetNameOrUndefined => self\n                    .bytecode\n                    .emit_get_name_or_undefined(value.variable(), (*index).into()),\n                BindingAccessOpcode::DeleteName => self\n                    .bytecode\n                    .emit_delete_name(value.variable(), (*index).into()),\n            },\n            BindingKind::Local(None) => {\n                let error_msg = self.get_or_insert_literal(Literal::String(js_string!(\n                    \"access of uninitialized binding\"\n                )));\n                self.bytecode\n                    .emit_throw_new_reference_error(error_msg.into());\n            }\n            BindingKind::Local(Some(index)) => match opcode {\n                BindingAccessOpcode::GetName\n                | BindingAccessOpcode::GetNameOrUndefined\n                | BindingAccessOpcode::GetNameAndLocator => {\n                    self.bytecode.emit_move(value.variable(), (*index).into());\n                }\n                BindingAccessOpcode::GetLocator | BindingAccessOpcode::DefVar => {}\n                BindingAccessOpcode::SetName\n                | BindingAccessOpcode::DefInitVar\n                | BindingAccessOpcode::PutLexicalValue\n                | BindingAccessOpcode::SetNameByLocator => {\n                    self.bytecode.emit_move((*index).into(), value.variable());\n                }\n                BindingAccessOpcode::DeleteName => self.bytecode.emit_store_false(value.variable()),\n            },\n        }\n    }\n\n    fn emit_get_property_by_name(\n        &mut self,\n        dst: &Register,\n        receiver: Option<&Register>,\n        value: &Register,\n        ident: Sym,\n    ) {\n        let ic_index = self.ic.len() as u32;\n\n        let name_index = self.get_or_insert_name(ident);\n        let Constant::String(ref name) = self.constants[name_index as usize].clone() else {\n            unreachable!(\"there should be a string at index\")\n        };\n        self.ic.push(InlineCache::new(name.clone()));\n\n        if let Some(receiver) = receiver {\n            self.bytecode.emit_get_property_by_name_with_this(\n                dst.variable(),\n                receiver.variable(),\n                value.variable(),\n                ic_index.into(),\n            );\n        } else if name == &StaticJsStrings::LENGTH {\n            self.bytecode.emit_get_length_property(\n                dst.variable(),\n                value.variable(),\n                ic_index.into(),\n            );\n        } else {\n            self.bytecode.emit_get_property_by_name(\n                dst.variable(),\n                value.variable(),\n                ic_index.into(),\n            );\n        }\n    }\n\n    fn emit_set_property_by_name(\n        &mut self,\n        value: &Register,\n        receiver: Option<&Register>,\n        object: &Register,\n        ident: Sym,\n    ) {\n        let ic_index = self.ic.len() as u32;\n\n        let name_index = self.get_or_insert_name(ident);\n        let Constant::String(ref name) = self.constants[name_index as usize].clone() else {\n            unreachable!(\"there should be a string at index\")\n        };\n        self.ic.push(InlineCache::new(name.clone()));\n\n        if let Some(receiver) = receiver {\n            self.bytecode.emit_set_property_by_name_with_this(\n                value.variable(),\n                receiver.variable(),\n                object.variable(),\n                ic_index.into(),\n            );\n        } else {\n            self.bytecode.emit_set_property_by_name(\n                value.variable(),\n                object.variable(),\n                ic_index.into(),\n            );\n        }\n    }\n\n    fn emit_type_error(&mut self, message: &str) {\n        let error_msg = self.get_or_insert_literal(Literal::String(js_string!(message)));\n        self.bytecode.emit_throw_new_type_error(error_msg.into());\n    }\n\n    fn emit_store_integer(&mut self, value: i32, dst: &Register) {\n        self.emit_store_integer_with_index(value, dst.variable());\n    }\n\n    fn emit_store_integer_with_index(&mut self, value: i32, dst: RegisterOperand) {\n        match value {\n            0 => self.bytecode.emit_store_zero(dst),\n            1 => self.bytecode.emit_store_one(dst),\n            x if i32::from(x as i8) == x => self.bytecode.emit_store_int8(dst, x as i8),\n            x if i32::from(x as i16) == x => {\n                self.bytecode.emit_store_int16(dst, x as i16);\n            }\n            x => self.bytecode.emit_store_int32(dst, x),\n        }\n    }\n\n    fn emit_store_literal(&mut self, literal: Literal, dst: &Register) {\n        let index = self.get_or_insert_literal(literal);\n        self.bytecode\n            .emit_store_literal(dst.variable(), index.into());\n    }\n\n    fn emit_store_rational(&mut self, value: f64, dst: &Register) {\n        if value.is_nan() {\n            return self.bytecode.emit_store_nan(dst.variable());\n        }\n\n        if value.is_infinite() {\n            if value.is_sign_positive() {\n                return self.bytecode.emit_store_positive_infinity(dst.variable());\n            }\n            return self.bytecode.emit_store_negative_infinity(dst.variable());\n        }\n\n        // Check if the f64 value can fit in an i32.\n        if f64::from(value as i32).to_bits() == value.to_bits() {\n            self.emit_store_integer(value as i32, dst);\n        } else {\n            let f32_value = value as f32;\n\n            #[allow(clippy::float_cmp)]\n            if f64::from(f32_value) == value {\n                self.bytecode.emit_store_float(dst.variable(), f32_value);\n            } else {\n                self.bytecode.emit_store_double(dst.variable(), value);\n            }\n        }\n    }\n\n    fn jump(&mut self) -> Label {\n        let index = self.next_opcode_location();\n        self.bytecode.emit_jump(Self::DUMMY_ADDRESS);\n        Label { index }\n    }\n\n    pub(crate) fn jump_if_true(&mut self, value: &Register) -> Label {\n        let index = self.next_opcode_location();\n        self.bytecode\n            .emit_jump_if_true(Self::DUMMY_ADDRESS, value.variable());\n        Label { index }\n    }\n\n    pub(crate) fn if_else(\n        &mut self,\n        bool: &Register,\n        true_case: impl FnOnce(&mut ByteCompiler<'_>),\n        false_case: impl FnOnce(&mut ByteCompiler<'_>),\n    ) {\n        let jump_false = self.jump_if_false(bool);\n\n        // if true, jump to end to avoid running the code for the `else`\n        true_case(self);\n        let jump_to_end = self.jump();\n\n        // if false, we should be already at the end so no need to do anything.\n        self.patch_jump(jump_false);\n        false_case(self);\n        self.patch_jump(jump_to_end);\n    }\n\n    /// Generates the `if-else` pattern.\n    ///\n    /// This will also deallocate the `bool` register.\n    pub(crate) fn if_else_with_dealloc(\n        &mut self,\n        bool: Register,\n        true_case: impl FnOnce(&mut ByteCompiler<'_>),\n        false_case: impl FnOnce(&mut ByteCompiler<'_>),\n    ) {\n        let jump_false = self.jump_if_false(&bool);\n        self.register_allocator.dealloc(bool);\n\n        // if true, jump to end to avoid running the code for the `else`\n        true_case(self);\n        let jump_to_end = self.jump();\n\n        // if false, we should be already at the end so no need to do anything.\n        self.patch_jump(jump_false);\n        false_case(self);\n        self.patch_jump(jump_to_end);\n    }\n\n    pub(crate) fn jump_if_false(&mut self, value: &Register) -> Label {\n        let index = self.next_opcode_location();\n        self.bytecode\n            .emit_jump_if_false(Self::DUMMY_ADDRESS, value.variable());\n        Label { index }\n    }\n\n    /// Compile a condition expression and emit a conditional jump.\n    ///\n    /// When the condition is a relational comparison (`<`, `<=`, `>`, `>=`),\n    /// emits a single fused comparison+branch opcode instead of separate\n    /// `LessThan` + `JumpIfFalse` instructions.\n    ///\n    /// If `hoisted` is provided, the pre-compiled register is used for one\n    /// operand of the comparison, avoiding a constant reload on every iteration.\n    pub(crate) fn compile_condition_and_branch(\n        &mut self,\n        condition: &Expression,\n        hoisted: Option<&HoistedOperand>,\n    ) -> Label {\n        if let Expression::Binary(binary) = condition\n            && let BinaryOp::Relational(op) = binary.op()\n            && let Some(label) = self.try_fused_comparison_branch(op, binary, hoisted)\n        {\n            return label;\n        }\n        // Fallback: compile expr + jump_if_false\n        let value = self.register_allocator.alloc();\n        self.compile_expr(condition, &value);\n        let label = self.jump_if_false(&value);\n        self.register_allocator.dealloc(value);\n        label\n    }\n\n    /// Try to hoist a constant operand from a loop condition into a register\n    /// that is loaded once before the loop, rather than on every iteration.\n    ///\n    /// Returns `Some(HoistedOperand)` when one side of a relational comparison\n    /// is a literal value (e.g. `i < 10` hoists `10`).\n    pub(crate) fn try_hoist_loop_condition(\n        &mut self,\n        condition: Option<&Expression>,\n    ) -> Option<HoistedOperand> {\n        let condition = condition?;\n        let Expression::Binary(binary) = condition else {\n            return None;\n        };\n        let BinaryOp::Relational(op) = binary.op() else {\n            return None;\n        };\n        match op {\n            RelationalOp::LessThan\n            | RelationalOp::LessThanOrEqual\n            | RelationalOp::GreaterThan\n            | RelationalOp::GreaterThanOrEqual => {}\n            _ => return None,\n        }\n        // Prefer hoisting RHS (most common pattern: `i < 10`)\n        if self.is_loop_invariant(binary.rhs()) {\n            let reg = self.register_allocator.alloc();\n            self.compile_expr(binary.rhs(), &reg);\n            return Some(HoistedOperand {\n                register: reg,\n                is_rhs: true,\n            });\n        }\n        // Try LHS (less common: `0 < i`)\n        if self.is_loop_invariant(binary.lhs()) {\n            let reg = self.register_allocator.alloc();\n            self.compile_expr(binary.lhs(), &reg);\n            return Some(HoistedOperand {\n                register: reg,\n                is_rhs: false,\n            });\n        }\n        None\n    }\n\n    /// Returns `true` if the expression is loop-invariant and would benefit\n    /// from being hoisted out of a loop (compiled once before the loop rather\n    /// than on every iteration).\n    ///\n    /// An expression qualifies if it is:\n    /// - A literal value (always immutable, always emits code)\n    /// - A `const` identifier that is NOT already optimized by\n    ///   [`compile_expr_operand`](Self::compile_expr_operand) (i.e., not local\n    ///   and not in `const_binding_cache`), since those are already handled\n    ///   with zero instructions inside the loop.\n    fn is_loop_invariant(&self, expr: &Expression) -> bool {\n        match expr {\n            Expression::Literal(_) => true,\n            Expression::Identifier(name) => {\n                let name = self.resolve_identifier_expect(*name);\n                let binding = self.lexical_scope.get_identifier_reference(name.clone());\n                // Local bindings already use persistent registers directly\n                // in compile_expr_operand — hoisting would add a redundant Move.\n                if binding.local() {\n                    return false;\n                }\n                // Cached const bindings are already handled by compile_expr_operand\n                // without emitting code — hoisting would add a redundant Move.\n                if !self.in_with && self.const_binding_cache.contains_key(&binding.locator()) {\n                    return false;\n                }\n                // Only hoist if the binding is immutable (const).\n                // Mutable bindings (let, var) can change between iterations.\n                matches!(self.lexical_scope.is_binding_mutable(&name), Some(false))\n            }\n            _ => false,\n        }\n    }\n\n    fn try_fused_comparison_branch(\n        &mut self,\n        op: RelationalOp,\n        binary: &Binary,\n        hoisted: Option<&HoistedOperand>,\n    ) -> Option<Label> {\n        use crate::vm::opcode::BytecodeEmitter;\n\n        let emit_fn: fn(&mut BytecodeEmitter, Address, RegisterOperand, RegisterOperand) = match op\n        {\n            RelationalOp::LessThan => BytecodeEmitter::emit_jump_if_not_less_than,\n            RelationalOp::LessThanOrEqual => BytecodeEmitter::emit_jump_if_not_less_than_or_equal,\n            RelationalOp::GreaterThan => BytecodeEmitter::emit_jump_if_not_greater_than,\n            RelationalOp::GreaterThanOrEqual => {\n                BytecodeEmitter::emit_jump_if_not_greater_than_or_equal\n            }\n            _ => return None,\n        };\n        let mut label_index = Address::new(0);\n        match hoisted {\n            Some(h) if h.is_rhs => {\n                let rhs = h.register.variable();\n                self.compile_expr_operand(binary.lhs(), |compiler, lhs| {\n                    label_index = compiler.next_opcode_location();\n                    emit_fn(&mut compiler.bytecode, Self::DUMMY_ADDRESS, lhs, rhs);\n                });\n            }\n            Some(h) => {\n                let lhs = h.register.variable();\n                self.compile_expr_operand(binary.rhs(), |compiler, rhs| {\n                    label_index = compiler.next_opcode_location();\n                    emit_fn(&mut compiler.bytecode, Self::DUMMY_ADDRESS, lhs, rhs);\n                });\n            }\n            None => {\n                self.compile_expr_operand(binary.lhs(), |compiler, lhs| {\n                    compiler.compile_expr_operand(binary.rhs(), |compiler, rhs| {\n                        label_index = compiler.next_opcode_location();\n                        emit_fn(&mut compiler.bytecode, Self::DUMMY_ADDRESS, lhs, rhs);\n                    });\n                });\n            }\n        }\n        Some(Label { index: label_index })\n    }\n\n    pub(crate) fn jump_if_null_or_undefined(&mut self, value: &Register) -> Label {\n        let index = self.next_opcode_location();\n        self.bytecode\n            .emit_jump_if_null_or_undefined(Self::DUMMY_ADDRESS, value.variable());\n        Label { index }\n    }\n\n    pub(crate) fn jump_if_not_undefined(&mut self, value: &Register) -> Label {\n        let index = self.next_opcode_location();\n        self.bytecode\n            .emit_jump_if_not_undefined(Self::DUMMY_ADDRESS, value.variable());\n        Label { index }\n    }\n\n    pub(crate) fn jump_if_neq(&mut self, lhs: &Register, rhs: &Register) -> Label {\n        let index = self.next_opcode_location();\n        self.bytecode\n            .emit_jump_if_not_equal(Self::DUMMY_ADDRESS, lhs.variable(), rhs.variable());\n        Label { index }\n    }\n\n    pub(crate) fn case(&mut self, value: &Register, condition: &Register) -> Label {\n        let index = self.next_opcode_location();\n        self.bytecode\n            .emit_case(Self::DUMMY_ADDRESS, value.variable(), condition.variable());\n        Label { index }\n    }\n\n    pub(crate) fn template_lookup(&mut self, dst: &Register, site: u64) -> Label {\n        let index = self.next_opcode_location();\n        self.bytecode\n            .emit_template_lookup(Self::DUMMY_ADDRESS, site, dst.variable());\n        Label { index }\n    }\n\n    fn emit_resume_kind(&mut self, resume_kind: GeneratorResumeKind, dst: &Register) {\n        self.emit_store_integer(resume_kind as i32, dst);\n    }\n\n    fn jump_if_not_resume_kind(\n        &mut self,\n        resume_kind: GeneratorResumeKind,\n        value: &Register,\n    ) -> Label {\n        let r1 = self.register_allocator.alloc();\n        self.emit_store_integer((resume_kind as u8).into(), &r1);\n\n        let index = self.next_opcode_location();\n        self.bytecode\n            .emit_jump_if_not_equal(Self::DUMMY_ADDRESS, r1.variable(), value.variable());\n        self.register_allocator.dealloc(r1);\n        Label { index }\n    }\n\n    #[track_caller]\n    pub(crate) fn patch_jump_with_target(&mut self, label: Label, target: Address) {\n        self.bytecode.patch_jump(label.index, target);\n    }\n\n    fn patch_jump(&mut self, label: Label) {\n        let target = self.next_opcode_location();\n        self.patch_jump_with_target(label, target);\n    }\n\n    fn resolve_identifier_expect(&self, identifier: Identifier) -> JsString {\n        identifier.to_js_string(self.interner())\n    }\n\n    fn super_(&mut self, this: &Register, super_: &Register) {\n        // Reuse super to avoid allocating a register just to get the function object.\n        self.bytecode.emit_this(this.variable());\n        self.bytecode.emit_get_function_object(super_.variable());\n        self.bytecode.emit_get_home_object(super_.variable());\n\n        let r1 = self.register_allocator.alloc();\n        self.bytecode.emit_store_null(r1.variable());\n        // jump to evaluation if `super` is already a valid home object.\n        let skip_move = self.jump_if_neq(&r1, super_);\n        self.bytecode.emit_move(r1.variable(), this.variable());\n        self.bytecode.emit_is_object(r1.variable());\n\n        // If `this` is also not an object, then `super` should be left as `null`.\n        // Jump to the end in this case because `super` is already `null`.\n        let skip_eval = self.jump_if_false(&r1);\n        self.register_allocator.dealloc(r1);\n\n        // `this` is an object, so replace `super` with it and get its\n        // prototype.\n        self.bytecode.emit_move(super_.variable(), this.variable());\n        self.patch_jump(skip_move);\n\n        self.bytecode.emit_get_prototype(super_.variable());\n\n        self.patch_jump(skip_eval);\n    }\n\n    fn access_get(&mut self, access: Access<'_>, dst: &Register) {\n        match access {\n            Access::Variable { name } => {\n                let name = self.resolve_identifier_expect(name);\n                let binding = self.lexical_scope.get_identifier_reference(name);\n                let index = self.get_binding(&binding);\n                if !self.in_with\n                    && let Some(&cached_reg) = self.const_binding_cache.get(&binding.locator())\n                {\n                    self.bytecode.emit_move(dst.variable(), cached_reg.into());\n                    return;\n                }\n                self.emit_binding_access(BindingAccessOpcode::GetName, &index, dst);\n            }\n            Access::Property { access } => match access {\n                PropertyAccess::Simple(access) => {\n                    let mut compiler = self.position_guard(access.field());\n\n                    let object = compiler.register_allocator.alloc();\n                    compiler.compile_expr(access.target(), &object);\n\n                    match access.field() {\n                        PropertyAccessField::Const(ident) => {\n                            compiler.emit_get_property_by_name(dst, None, &object, ident.sym());\n                        }\n                        PropertyAccessField::Expr(expr) => {\n                            let key = compiler.register_allocator.alloc();\n                            compiler.compile_expr(expr, &key);\n                            compiler.bytecode.emit_get_property_by_value(\n                                dst.variable(),\n                                key.variable(),\n                                object.variable(),\n                                object.variable(),\n                            );\n                            compiler.register_allocator.dealloc(key);\n                        }\n                    }\n                    compiler.register_allocator.dealloc(object);\n                }\n                PropertyAccess::Private(access) => {\n                    let mut compiler = self.position_guard(access.field());\n\n                    let index = compiler.get_or_insert_private_name(access.field());\n                    let object = compiler.register_allocator.alloc();\n                    compiler.compile_expr(access.target(), &object);\n                    compiler.bytecode.emit_get_private_field(\n                        dst.variable(),\n                        object.variable(),\n                        index.into(),\n                    );\n                    compiler.register_allocator.dealloc(object);\n                }\n                PropertyAccess::Super(access) => {\n                    let mut compiler = self.position_guard(access.field());\n\n                    let value = compiler.register_allocator.alloc();\n                    let receiver = compiler.register_allocator.alloc();\n                    compiler.super_(&receiver, &value);\n\n                    match access.field() {\n                        PropertyAccessField::Const(ident) => {\n                            compiler.emit_get_property_by_name(\n                                dst,\n                                Some(&receiver),\n                                &value,\n                                ident.sym(),\n                            );\n                        }\n                        PropertyAccessField::Expr(expr) => {\n                            let key = compiler.register_allocator.alloc();\n                            compiler.compile_expr(expr, &key);\n                            compiler.bytecode.emit_get_property_by_value(\n                                dst.variable(),\n                                key.variable(),\n                                receiver.variable(),\n                                value.variable(),\n                            );\n                            compiler.register_allocator.dealloc(key);\n                        }\n                    }\n                    compiler.register_allocator.dealloc(receiver);\n                    compiler.register_allocator.dealloc(value);\n                }\n            },\n            Access::This => {\n                self.bytecode.emit_this(dst.variable());\n            }\n        }\n    }\n\n    fn access_set<'a, F>(&mut self, access: Access<'_>, expr_fn: F)\n    where\n        F: FnOnce(&mut ByteCompiler<'_>) -> &'a Register,\n    {\n        match access {\n            Access::Variable { name } => {\n                let name = self.resolve_identifier_expect(name);\n                let binding = self.lexical_scope.get_identifier_reference(name.clone());\n                let is_lexical = binding.is_lexical();\n                let index = self.get_binding(&binding);\n\n                let value = self.register_allocator.alloc();\n                if !is_lexical {\n                    self.emit_binding_access(BindingAccessOpcode::GetLocator, &index, &value);\n                }\n                self.register_allocator.dealloc(value);\n\n                let value = expr_fn(self);\n\n                if is_lexical {\n                    match self.lexical_scope.set_mutable_binding(name.clone()) {\n                        Ok(binding) => {\n                            let index = self.insert_binding(binding);\n                            self.emit_binding_access(BindingAccessOpcode::SetName, &index, value);\n                        }\n                        Err(BindingLocatorError::MutateImmutable) => {\n                            let index = self.get_or_insert_string(name);\n                            self.bytecode.emit_throw_mutate_immutable(index.into());\n                        }\n                        Err(BindingLocatorError::Silent) => {}\n                    }\n                } else {\n                    self.emit_binding_access(BindingAccessOpcode::SetNameByLocator, &index, value);\n                }\n            }\n            Access::Property { access } => match access {\n                PropertyAccess::Simple(access) => match access.field() {\n                    PropertyAccessField::Const(name) => {\n                        let object = self.register_allocator.alloc();\n                        self.compile_expr(access.target(), &object);\n                        let value = expr_fn(self);\n                        self.emit_set_property_by_name(value, None, &object, name.sym());\n                        self.register_allocator.dealloc(object);\n                    }\n                    PropertyAccessField::Expr(expr) => {\n                        let object = self.register_allocator.alloc();\n                        self.compile_expr(access.target(), &object);\n\n                        let key = self.register_allocator.alloc();\n                        self.compile_expr(expr, &key);\n\n                        let value = expr_fn(self);\n\n                        self.bytecode.emit_set_property_by_value(\n                            value.variable(),\n                            key.variable(),\n                            object.variable(),\n                            object.variable(),\n                        );\n\n                        self.register_allocator.dealloc(object);\n                        self.register_allocator.dealloc(key);\n                    }\n                },\n                PropertyAccess::Private(access) => {\n                    let index = self.get_or_insert_private_name(access.field());\n\n                    let object = self.register_allocator.alloc();\n                    self.compile_expr(access.target(), &object);\n\n                    let value = expr_fn(self);\n\n                    self.bytecode.emit_set_private_field(\n                        value.variable(),\n                        object.variable(),\n                        index.into(),\n                    );\n\n                    self.register_allocator.dealloc(object);\n                }\n                PropertyAccess::Super(access) => match access.field() {\n                    PropertyAccessField::Const(name) => {\n                        let object = self.register_allocator.alloc();\n                        let receiver = self.register_allocator.alloc();\n                        self.super_(&receiver, &object);\n\n                        let value = expr_fn(self);\n\n                        self.emit_set_property_by_name(value, Some(&receiver), &object, name.sym());\n\n                        self.register_allocator.dealloc(receiver);\n                        self.register_allocator.dealloc(object);\n                    }\n                    PropertyAccessField::Expr(expr) => {\n                        let object = self.register_allocator.alloc();\n                        let receiver = self.register_allocator.alloc();\n                        self.super_(&receiver, &object);\n\n                        let key = self.register_allocator.alloc();\n                        self.compile_expr(expr, &key);\n\n                        let value = expr_fn(self);\n\n                        self.bytecode.emit_set_property_by_value(\n                            value.variable(),\n                            key.variable(),\n                            receiver.variable(),\n                            object.variable(),\n                        );\n\n                        self.register_allocator.dealloc(key);\n                        self.register_allocator.dealloc(receiver);\n                        self.register_allocator.dealloc(object);\n                    }\n                },\n            },\n            Access::This => {\n                // In non-strict code, assignment to `this` is a no-op per spec; we still evaluate\n                // the RHS for side effects (e.g. `this = foo()` must call foo()).\n                let _ = expr_fn(self);\n            }\n        }\n    }\n\n    fn access_delete(&mut self, access: Access<'_>, dst: &Register) {\n        match access {\n            Access::Property { access } => match access {\n                PropertyAccess::Simple(access) => match access.field() {\n                    PropertyAccessField::Const(name) => {\n                        let index = self.get_or_insert_name(name.sym());\n                        self.compile_expr(access.target(), dst);\n                        self.bytecode\n                            .emit_delete_property_by_name(dst.variable(), index.into());\n                    }\n                    PropertyAccessField::Expr(expr) => {\n                        self.compile_expr(access.target(), dst);\n                        let key = self.register_allocator.alloc();\n                        self.compile_expr(expr, &key);\n                        self.bytecode\n                            .emit_delete_property_by_value(dst.variable(), key.variable());\n                        self.register_allocator.dealloc(key);\n                    }\n                },\n                PropertyAccess::Super(_) => self.bytecode.emit_delete_super_throw(),\n                PropertyAccess::Private(_) => {\n                    unreachable!(\"deleting private properties should always throw early errors.\")\n                }\n            },\n            Access::Variable { name } => {\n                let name = name.to_js_string(self.interner());\n                let binding = self.lexical_scope.get_identifier_reference(name);\n                let index = self.get_binding(&binding);\n                self.emit_binding_access(BindingAccessOpcode::DeleteName, &index, dst);\n            }\n            Access::This => self.bytecode.emit_store_true(dst.variable()),\n        }\n    }\n\n    /// Compile a [`StatementList`].\n    pub fn compile_statement_list(&mut self, list: &StatementList, use_expr: bool, block: bool) {\n        if use_expr || self.jump_control_info_has_use_expr() {\n            let mut use_expr_index = 0;\n            for (i, statement) in list.statements().iter().enumerate() {\n                match statement {\n                    StatementListItem::Statement(statement) => match statement.as_ref() {\n                        Statement::Break(_) | Statement::Continue(_) => break,\n                        Statement::Empty | Statement::Var(_) => {}\n                        Statement::Block(block) if !returns_value(block) => {}\n                        _ => use_expr_index = i,\n                    },\n                    StatementListItem::Declaration(_) => {}\n                }\n            }\n\n            for (i, item) in list.statements().iter().enumerate() {\n                self.compile_stmt_list_item(item, i == use_expr_index, block);\n            }\n        } else {\n            for item in list.statements() {\n                self.compile_stmt_list_item(item, false, block);\n            }\n        }\n    }\n\n    /// Compile an [`Expression`].\n    #[inline]\n    pub(crate) fn compile_expr(&mut self, expr: &Expression, dst: &'_ Register) {\n        self.compile_expr_impl(expr, dst);\n    }\n\n    /// Compile an expression purely for its side effects, discarding the result.\n    ///\n    /// This avoids allocating a register and emitting a store for the result\n    /// when it's not needed (e.g., expression statements, for-loop updates).\n    pub(crate) fn compile_expr_for_side_effects(&mut self, expr: &Expression) {\n        match expr {\n            Expression::Call(call) => {\n                self.call(Callable::Call(call), CallResultDest::Discard);\n            }\n            Expression::New(new) => {\n                self.call(Callable::New(new), CallResultDest::Discard);\n            }\n            Expression::Update(update) => {\n                let tmp = self.register_allocator.alloc();\n                self.compile_update(update, &tmp, true);\n                self.register_allocator.dealloc(tmp);\n            }\n            Expression::Parenthesized(parenthesized) => {\n                self.compile_expr_for_side_effects(parenthesized.expression());\n            }\n            _ => {\n                let tmp = self.register_allocator.alloc();\n                self.compile_expr(expr, &tmp);\n                self.register_allocator.dealloc(tmp);\n            }\n        }\n    }\n\n    /// Compile an expression and leave the result on the stack.\n    ///\n    /// For call/new expressions, this avoids the `PopIntoRegister` + `PushFromRegister`\n    /// round-trip by leaving the call result directly on the stack.\n    pub(crate) fn compile_expr_to_stack(&mut self, expr: &Expression) {\n        match expr {\n            Expression::Call(call) => {\n                self.call(Callable::Call(call), CallResultDest::Stack);\n            }\n            Expression::New(new) => {\n                self.call(Callable::New(new), CallResultDest::Stack);\n            }\n            Expression::Parenthesized(parenthesized) => {\n                self.compile_expr_to_stack(parenthesized.expression());\n            }\n            _ => {\n                let tmp = self.register_allocator.alloc();\n                self.compile_expr(expr, &tmp);\n                self.push_from_register(&tmp);\n                self.register_allocator.dealloc(tmp);\n            }\n        }\n    }\n\n    /// Compile an expression and return the operand where the result lives.\n    ///\n    /// For local variable references, this returns the local's persistent register\n    /// directly without emitting a `Move` instruction. For all other expressions,\n    /// it allocates a temporary register and compiles into it.\n    ///\n    /// The `inner_fn` passed in will be called before the register get deallocated.\n    pub(crate) fn compile_expr_operand(\n        &mut self,\n        expr: &Expression,\n        inner_fn: impl FnOnce(&mut Self, RegisterOperand),\n    ) {\n        if let Expression::Identifier(name) = expr {\n            let name = self.resolve_identifier_expect(*name);\n            let binding = self.lexical_scope.get_identifier_reference(name);\n            let index = self.get_binding(&binding);\n            if let BindingKind::Local(Some(local_reg)) = &index {\n                inner_fn(self, RegisterOperand::from(*local_reg));\n                return;\n            }\n            if !self.in_with\n                && let Some(&cached_reg) = self.const_binding_cache.get(&binding.locator())\n            {\n                inner_fn(self, cached_reg.into());\n                return;\n            }\n        }\n        let reg = self.register_allocator.alloc();\n        self.compile_expr(expr, &reg);\n        let op = reg.variable();\n        inner_fn(self, op);\n        self.register_allocator.dealloc(reg);\n    }\n\n    /// Compile a property access expression, prepending `this` to the property value in the stack.\n    ///\n    /// This compiles the access in a way that the state of the stack after executing the property\n    /// access becomes `...rest, this, value`. where `...rest` is the rest of the stack, `this` is the\n    /// `this` value of the access, and `value` is the final result of the access.\n    ///\n    /// This is mostly useful for optional chains with calls (`a.b?.()`) and for regular chains\n    /// with calls (`a.b()`), since both of them must have `a` be the value of `this` for the function\n    /// call `b()`, but a regular compilation of the access would lose the `this` value after accessing\n    /// `b`.\n    fn compile_access_preserve_this(\n        &mut self,\n        access: &PropertyAccess,\n        this: &Register,\n        dst: &Register,\n    ) {\n        match access {\n            PropertyAccess::Simple(access) => {\n                self.compile_expr(access.target(), this);\n\n                match access.field() {\n                    PropertyAccessField::Const(ident) => {\n                        self.emit_get_property_by_name(dst, None, this, ident.sym());\n                    }\n                    PropertyAccessField::Expr(field) => {\n                        let key = self.register_allocator.alloc();\n                        self.compile_expr(field, &key);\n                        self.bytecode.emit_get_property_by_value(\n                            dst.variable(),\n                            key.variable(),\n                            this.variable(),\n                            this.variable(),\n                        );\n                        self.register_allocator.dealloc(key);\n                    }\n                }\n            }\n            PropertyAccess::Private(access) => {\n                self.compile_expr(access.target(), this);\n\n                let index = self.get_or_insert_private_name(access.field());\n                self.bytecode\n                    .emit_get_private_field(dst.variable(), this.variable(), index.into());\n            }\n            PropertyAccess::Super(access) => {\n                let object = self.register_allocator.alloc();\n                self.super_(this, &object);\n\n                match access.field() {\n                    PropertyAccessField::Const(ident) => {\n                        self.emit_get_property_by_name(dst, Some(this), &object, ident.sym());\n                    }\n                    PropertyAccessField::Expr(expr) => {\n                        let key = self.register_allocator.alloc();\n                        self.compile_expr(expr, &key);\n                        self.bytecode.emit_get_property_by_value(\n                            dst.variable(),\n                            key.variable(),\n                            this.variable(),\n                            object.variable(),\n                        );\n                        self.register_allocator.dealloc(key);\n                    }\n                }\n                self.register_allocator.dealloc(object);\n            }\n        }\n    }\n\n    /// Compile an optional chain expression, prepending `this` to the property value in the stack.\n    ///\n    /// This compiles the access in a way that the state of the stack after executing the optional\n    /// chain becomes `...rest, this, value`. where `...rest` is the rest of the stack, `this` is the\n    /// `this` value of the chain, and `value` is the result of the chain.\n    ///\n    /// This is mostly useful for inner optional chains with external calls (`(a?.b)()`), because the\n    /// external call is not in the optional chain, and compiling an optional chain in the usual way\n    /// would only return the result of the chain without preserving the `this` value. In other words,\n    /// `this` would be set to `undefined` for that call, which is incorrect since `a` should be the\n    /// `this` value of the call.\n    fn compile_optional_preserve_this(\n        &mut self,\n        optional: &Optional,\n        this: &Register,\n        value: &Register,\n    ) {\n        let mut jumps = Vec::with_capacity(optional.chain().len());\n\n        match optional.target().flatten() {\n            Expression::PropertyAccess(access) => {\n                self.compile_access_preserve_this(access, this, value);\n            }\n            Expression::Optional(opt) => self.compile_optional_preserve_this(opt, this, value),\n            expr => {\n                self.bytecode.emit_store_undefined(this.variable());\n                self.compile_expr(expr, value);\n            }\n        }\n\n        jumps.push(self.jump_if_null_or_undefined(value));\n\n        let (first, rest) = optional\n            .chain()\n            .split_first()\n            .expect(\"chain must have at least one element\");\n        assert!(first.shorted());\n\n        self.compile_optional_item_kind(first.kind(), this, value);\n\n        for item in rest {\n            if item.shorted() {\n                jumps.push(self.jump_if_null_or_undefined(value));\n            }\n            self.compile_optional_item_kind(item.kind(), this, value);\n        }\n\n        let skip_undef = self.jump();\n\n        for label in jumps {\n            self.patch_jump(label);\n            self.bytecode.emit_store_undefined(value.variable());\n        }\n\n        self.patch_jump(skip_undef);\n    }\n\n    /// Compile a `delete` expression on an optional chain.\n    ///\n    /// Follows the same short-circuit logic as `compile_optional_preserve_this`, but\n    /// emits delete opcodes for the final link in the chain instead of get opcodes.\n    /// When the chain short-circuits (base is null/undefined), returns `true` per spec.\n    pub(crate) fn compile_optional_delete(&mut self, optional: &Optional, dst: &Register) {\n        let value = self.register_allocator.alloc();\n        let this = self.register_allocator.alloc();\n\n        let mut jumps = Vec::with_capacity(optional.chain().len());\n\n        // Compile the target expression (base of the chain).\n        match optional.target().flatten() {\n            Expression::PropertyAccess(access) => {\n                self.compile_access_preserve_this(access, &this, &value);\n            }\n            Expression::Optional(opt) => {\n                self.compile_optional_preserve_this(opt, &this, &value);\n            }\n            expr => {\n                self.bytecode.emit_store_undefined(this.variable());\n                self.compile_expr(expr, &value);\n            }\n        }\n\n        jumps.push(self.jump_if_null_or_undefined(&value));\n\n        let chain = optional.chain();\n        let chain_len = chain.len();\n\n        let (first, rest) = chain\n            .split_first()\n            .expect(\"chain must have at least one element\");\n        assert!(first.shorted());\n\n        if chain_len == 1 {\n            // The only item is also the last — emit delete.\n            self.compile_optional_delete_final(first.kind(), &this, &value, dst);\n        } else {\n            // Not the last — emit a regular get.\n            self.compile_optional_item_kind(first.kind(), &this, &value);\n\n            for (i, item) in rest.iter().enumerate() {\n                if item.shorted() {\n                    jumps.push(self.jump_if_null_or_undefined(&value));\n                }\n                if i == rest.len() - 1 {\n                    // Last item — emit delete.\n                    self.compile_optional_delete_final(item.kind(), &this, &value, dst);\n                } else {\n                    self.compile_optional_item_kind(item.kind(), &this, &value);\n                }\n            }\n        }\n\n        let skip_true = self.jump();\n\n        // Short-circuit path: delete on null/undefined base returns true per spec.\n        for label in jumps {\n            self.patch_jump(label);\n        }\n        self.bytecode.emit_store_true(dst.variable());\n\n        self.patch_jump(skip_true);\n\n        self.register_allocator.dealloc(this);\n        self.register_allocator.dealloc(value);\n    }\n\n    /// Emit delete opcodes for the final operation in an optional chain.\n    fn compile_optional_delete_final(\n        &mut self,\n        kind: &OptionalOperationKind,\n        this: &Register,\n        value: &Register,\n        dst: &Register,\n    ) {\n        match kind {\n            OptionalOperationKind::SimplePropertyAccess { field } => {\n                self.bytecode.emit_move(this.variable(), value.variable());\n                match field {\n                    PropertyAccessField::Const(name) => {\n                        let index = self.get_or_insert_name(name.sym());\n                        self.bytecode.emit_move(dst.variable(), value.variable());\n                        self.bytecode\n                            .emit_delete_property_by_name(dst.variable(), index.into());\n                    }\n                    PropertyAccessField::Expr(expr) => {\n                        self.bytecode.emit_move(dst.variable(), value.variable());\n                        let key = self.register_allocator.alloc();\n                        self.compile_expr(expr, &key);\n                        self.bytecode\n                            .emit_delete_property_by_value(dst.variable(), key.variable());\n                        self.register_allocator.dealloc(key);\n                    }\n                }\n            }\n            OptionalOperationKind::PrivatePropertyAccess { .. } => {\n                unreachable!(\"deleting private properties should always throw early errors.\")\n            }\n            OptionalOperationKind::Call { .. } => {\n                // `delete obj?.method()` — evaluate the call, then return true.\n                self.compile_optional_item_kind(kind, this, value);\n                self.bytecode.emit_store_true(dst.variable());\n            }\n        }\n    }\n\n    /// Compile a single operation in an optional chain.\n    ///\n    /// On successful compilation, the state of the stack on execution will become `...rest, this, value`,\n    /// where `this` is the target of the property access (`undefined` on calls), and `value` is the\n    /// result of executing the action.\n    /// For example, in the expression `a?.b.c()`, after compiling and executing:\n    ///\n    /// - `a?.b`, the state of the stack will become `...rest, a, b`.\n    /// - `b.c`, the state of the stack will become `...rest, b, c`.\n    /// - `c()`, the state of the stack will become `...rest, undefined, c()`.\n    ///\n    /// # Requirements\n    /// - This should only be called after verifying that the previous value of the chain\n    ///   is not null or undefined (if the operator `?.` was used).\n    /// - This assumes that the state of the stack before compiling is `...rest, this, value`,\n    ///   since the operation compiled by this function could be a call.\n    fn compile_optional_item_kind(\n        &mut self,\n        kind: &OptionalOperationKind,\n        this: &Register,\n        value: &Register,\n    ) {\n        match kind {\n            OptionalOperationKind::SimplePropertyAccess { field } => {\n                self.bytecode.emit_move(this.variable(), value.variable());\n                match field {\n                    PropertyAccessField::Const(name) => {\n                        self.emit_get_property_by_name(value, None, value, name.sym());\n                    }\n                    PropertyAccessField::Expr(expr) => {\n                        let key = self.register_allocator.alloc();\n                        self.compile_expr(expr, &key);\n                        self.bytecode.emit_get_property_by_value(\n                            value.variable(),\n                            key.variable(),\n                            value.variable(),\n                            value.variable(),\n                        );\n                        self.register_allocator.dealloc(key);\n                    }\n                }\n            }\n            OptionalOperationKind::PrivatePropertyAccess { field } => {\n                self.bytecode.emit_move(this.variable(), value.variable());\n                let index = self.get_or_insert_private_name(*field);\n                self.bytecode.emit_get_private_field(\n                    value.variable(),\n                    value.variable(),\n                    index.into(),\n                );\n            }\n            OptionalOperationKind::Call { args } => {\n                self.push_from_register(this);\n                self.push_from_register(value);\n\n                let args = &**args;\n                let contains_spread = args.iter().any(|arg| matches!(arg, Expression::Spread(_)));\n\n                if contains_spread {\n                    let array = self.register_allocator.alloc();\n                    let value = self.register_allocator.alloc();\n\n                    self.bytecode.emit_store_new_array(array.variable());\n\n                    for arg in args {\n                        self.compile_expr(arg, &value);\n                        if let Expression::Spread(_) = arg {\n                            self.bytecode.emit_get_iterator(value.variable());\n                            self.bytecode.emit_push_iterator_to_array(array.variable());\n                        } else {\n                            self.bytecode\n                                .emit_push_value_to_array(value.variable(), array.variable());\n                        }\n                    }\n\n                    self.push_from_register(&array);\n\n                    self.register_allocator.dealloc(value);\n                    self.register_allocator.dealloc(array);\n\n                    self.bytecode.emit_call_spread();\n                } else {\n                    for arg in args {\n                        self.compile_expr_to_stack(arg);\n                    }\n                    self.bytecode.emit_call((args.len() as u32).into());\n                }\n\n                self.pop_into_register(value);\n                self.bytecode.emit_store_undefined(this.variable());\n            }\n        }\n    }\n\n    /// Compile a [`VarDeclaration`].\n    fn compile_var_decl(&mut self, decl: &VarDeclaration) {\n        for variable in decl.0.as_ref() {\n            match variable.binding() {\n                Binding::Identifier(ident) => {\n                    let ident = ident.to_js_string(self.interner());\n                    if let Some(expr) = variable.init() {\n                        let binding = self.lexical_scope.get_identifier_reference(ident.clone());\n                        let index = self.insert_binding(binding);\n                        let value = self.register_allocator.alloc();\n                        self.emit_binding_access(BindingAccessOpcode::GetLocator, &index, &value);\n                        self.compile_expr(expr, &value);\n                        self.emit_binding_access(\n                            BindingAccessOpcode::SetNameByLocator,\n                            &index,\n                            &value,\n                        );\n                        self.register_allocator.dealloc(value);\n                    } else {\n                        let value = self.register_allocator.alloc();\n                        self.emit_binding(BindingOpcode::Var, ident, &value);\n                        self.register_allocator.dealloc(value);\n                    }\n                }\n                Binding::Pattern(pattern) => {\n                    let value = self.register_allocator.alloc();\n                    if let Some(init) = variable.init() {\n                        self.compile_expr(init, &value);\n                    } else {\n                        self.bytecode.emit_store_undefined(value.variable());\n                    }\n                    self.compile_declaration_pattern(pattern, BindingOpcode::InitVar, &value);\n                    self.register_allocator.dealloc(value);\n                }\n            }\n        }\n    }\n\n    /// Compile a [`LexicalDeclaration`].\n    fn compile_lexical_decl(&mut self, decl: &LexicalDeclaration) {\n        match decl {\n            LexicalDeclaration::Let(decls) => {\n                for variable in decls.as_ref() {\n                    match variable.binding() {\n                        Binding::Identifier(ident) => {\n                            let ident = ident.to_js_string(self.interner());\n                            let binding = self.lexical_scope.get_identifier_reference(ident);\n                            if binding.local() {\n                                // Pre-allocate the persistent register but do NOT\n                                // insert into local_binding_registers yet, so that\n                                // TDZ checks still fire during initializer compilation.\n                                let reg = self.register_allocator.alloc_persistent();\n                                if let Some(init) = variable.init() {\n                                    self.compile_expr(init, &reg);\n                                } else {\n                                    self.bytecode.emit_store_undefined(reg.variable());\n                                }\n                                self.local_binding_registers.insert(binding, reg.index());\n                            } else {\n                                let value = self.register_allocator.alloc();\n                                if let Some(init) = variable.init() {\n                                    self.compile_expr(init, &value);\n                                } else {\n                                    self.bytecode.emit_store_undefined(value.variable());\n                                }\n                                let binding_kind = self.insert_binding(binding);\n                                self.emit_binding_access(\n                                    BindingAccessOpcode::PutLexicalValue,\n                                    &binding_kind,\n                                    &value,\n                                );\n                                self.register_allocator.dealloc(value);\n                            }\n                        }\n                        Binding::Pattern(pattern) => {\n                            let value = self.register_allocator.alloc();\n                            if let Some(init) = variable.init() {\n                                self.compile_expr(init, &value);\n                            } else {\n                                self.bytecode.emit_store_undefined(value.variable());\n                            }\n                            self.compile_declaration_pattern(\n                                pattern,\n                                BindingOpcode::InitLexical,\n                                &value,\n                            );\n                            self.register_allocator.dealloc(value);\n                        }\n                    }\n                }\n            }\n            LexicalDeclaration::Const(decls) => {\n                for variable in decls.as_ref() {\n                    match variable.binding() {\n                        Binding::Identifier(ident) => {\n                            let ident = ident.to_js_string(self.interner());\n                            let init = variable\n                                .init()\n                                .expect(\"const declaration must have initializer\");\n                            let binding =\n                                self.lexical_scope.get_identifier_reference(ident.clone());\n                            if binding.local() {\n                                let reg = self.register_allocator.alloc_persistent();\n                                self.compile_expr(init, &reg);\n                                self.local_binding_registers.insert(binding, reg.index());\n                            } else {\n                                let value = self.register_allocator.alloc();\n                                self.compile_expr(init, &value);\n                                let binding_kind = self.insert_binding(binding.clone());\n                                self.emit_binding_access(\n                                    BindingAccessOpcode::PutLexicalValue,\n                                    &binding_kind,\n                                    &value,\n                                );\n                                // Cache non-local const bindings in a persistent register\n                                // so subsequent reads avoid GetName environment lookups.\n                                let cache_reg = self.register_allocator.alloc_persistent();\n                                self.bytecode\n                                    .emit_move(cache_reg.variable(), value.variable());\n                                self.const_binding_cache\n                                    .insert(binding.locator(), cache_reg.index());\n                                self.register_allocator.dealloc(value);\n                            }\n                        }\n                        Binding::Pattern(pattern) => {\n                            let value = self.register_allocator.alloc();\n                            if let Some(init) = variable.init() {\n                                self.compile_expr(init, &value);\n                            } else {\n                                self.bytecode.emit_store_undefined(value.variable());\n                            }\n                            self.compile_declaration_pattern(\n                                pattern,\n                                BindingOpcode::InitLexical,\n                                &value,\n                            );\n                            self.register_allocator.dealloc(value);\n                        }\n                    }\n                }\n            }\n            LexicalDeclaration::Using(decls) => {\n                // For each using declaration, we need to:\n                // 1. Evaluate the initializer\n                // 2. Add the resource to the disposal stack\n                // 3. Bind the variable\n                for variable in decls.as_ref() {\n                    match variable.binding() {\n                        Binding::Identifier(ident) => {\n                            let ident = ident.to_js_string(self.interner());\n                            let value = self.register_allocator.alloc();\n\n                            if let Some(init) = variable.init() {\n                                self.compile_expr(init, &value);\n                            } else {\n                                self.bytecode.emit_store_undefined(value.variable());\n                            }\n\n                            // TODO(@abhinavs1920): Add resource to disposal stack\n                            // For now, we just bind the variable like a let declaration\n                            // Full implementation will add: AddDisposableResource opcode\n\n                            self.emit_binding(BindingOpcode::InitLexical, ident, &value);\n                            self.register_allocator.dealloc(value);\n                        }\n                        Binding::Pattern(pattern) => {\n                            let value = self.register_allocator.alloc();\n\n                            if let Some(init) = variable.init() {\n                                self.compile_expr(init, &value);\n                            } else {\n                                self.bytecode.emit_store_undefined(value.variable());\n                            }\n\n                            // TODO: Same as above\n\n                            self.compile_declaration_pattern(\n                                pattern,\n                                BindingOpcode::InitLexical,\n                                &value,\n                            );\n                            self.register_allocator.dealloc(value);\n                        }\n                    }\n                }\n            }\n            LexicalDeclaration::AwaitUsing(decls) => {\n                for variable in decls.as_ref() {\n                    match variable.binding() {\n                        Binding::Identifier(ident) => {\n                            let ident = ident.to_js_string(self.interner());\n                            let value = self.register_allocator.alloc();\n\n                            if let Some(init) = variable.init() {\n                                self.compile_expr(init, &value);\n                            } else {\n                                self.bytecode.emit_store_undefined(value.variable());\n                            }\n\n                            // TODO: Add resource to async disposal stack\n                            // For now, we just bind the variable like a let declaration\n                            // Full implementation will add: AddAsyncDisposableResource opcode\n\n                            self.emit_binding(BindingOpcode::InitLexical, ident, &value);\n                            self.register_allocator.dealloc(value);\n                        }\n                        Binding::Pattern(pattern) => {\n                            let value = self.register_allocator.alloc();\n\n                            if let Some(init) = variable.init() {\n                                self.compile_expr(init, &value);\n                            } else {\n                                self.bytecode.emit_store_undefined(value.variable());\n                            }\n\n                            // TODO: SAME\n                            self.compile_declaration_pattern(\n                                pattern,\n                                BindingOpcode::InitLexical,\n                                &value,\n                            );\n                            self.register_allocator.dealloc(value);\n                        }\n                    }\n                }\n            }\n        }\n    }\n\n    /// Compile a [`StatementListItem`].\n    fn compile_stmt_list_item(&mut self, item: &StatementListItem, use_expr: bool, block: bool) {\n        match item {\n            StatementListItem::Statement(stmt) => {\n                self.compile_stmt(stmt, use_expr, false);\n            }\n            StatementListItem::Declaration(decl) => self.compile_decl(decl, block),\n        }\n    }\n\n    /// Compile a [`Declaration`].\n    #[allow(unused_variables)]\n    pub fn compile_decl(&mut self, decl: &Declaration, block: bool) {\n        match decl {\n            #[cfg(feature = \"annex-b\")]\n            Declaration::FunctionDeclaration(function) if block => {\n                let name = function.name();\n                if self.annex_b_function_names.contains(&name.sym()) {\n                    let name = name.to_js_string(self.interner());\n                    let binding = self.lexical_scope.get_identifier_reference(name.clone());\n                    let index = self.get_binding(&binding);\n\n                    let value = self.register_allocator.alloc();\n                    self.emit_binding_access(BindingAccessOpcode::GetName, &index, &value);\n                    match self.variable_scope.set_mutable_binding_var(name.clone()) {\n                        Ok(binding) => {\n                            let index = self.get_binding(&binding);\n                            self.emit_binding_access(BindingAccessOpcode::SetName, &index, &value);\n                        }\n                        Err(BindingLocatorError::MutateImmutable) => {\n                            let index = self.get_or_insert_string(name);\n                            self.bytecode.emit_throw_mutate_immutable(index.into());\n                        }\n                        Err(BindingLocatorError::Silent) => {}\n                    }\n                    self.register_allocator.dealloc(value);\n                }\n            }\n            Declaration::ClassDeclaration(class) => self.compile_class(class.as_ref().into(), None),\n            Declaration::Lexical(lexical) => self.compile_lexical_decl(lexical),\n            _ => {}\n        }\n    }\n\n    /// Compiles a function AST Node into bytecode, and returns its index into\n    /// the `functions` array.\n    pub(crate) fn function(&mut self, function: FunctionSpec<'_>) -> u32 {\n        let (generator, r#async, arrow) = (\n            function.kind.is_generator(),\n            function.kind.is_async(),\n            function.kind.is_arrow(),\n        );\n\n        let FunctionSpec {\n            name,\n            parameters,\n            body,\n            scopes,\n            name_scope,\n            linear_span,\n            ..\n        } = function;\n\n        let name = if let Some(name) = name {\n            Some(name.sym().to_js_string(self.interner()))\n        } else {\n            Some(js_string!())\n        };\n\n        let spanned_source_text = SpannedSourceText::new(self.source_text(), linear_span);\n\n        let code = FunctionCompiler::new(spanned_source_text)\n            .name(name)\n            .generator(generator)\n            .r#async(r#async)\n            .strict(self.strict())\n            .arrow(arrow)\n            .in_with(self.in_with)\n            .name_scope(name_scope.cloned())\n            .source_path(self.source_path.clone())\n            .compile(\n                parameters,\n                body,\n                self.variable_scope.clone(),\n                self.lexical_scope.clone(),\n                scopes,\n                function.contains_direct_eval,\n                self.interner,\n            );\n\n        self.push_function_to_constants(code)\n    }\n\n    /// Compiles a function AST Node into bytecode, setting its corresponding binding or\n    /// pushing it to the stack if necessary.\n    pub(crate) fn function_with_binding(\n        &mut self,\n        function: FunctionSpec<'_>,\n        node_kind: NodeKind,\n        dst: &Register,\n    ) {\n        let name = function.name;\n        let index = self.function(function);\n        self.emit_get_function(dst, index);\n        match node_kind {\n            NodeKind::Declaration => {\n                self.emit_binding(\n                    BindingOpcode::InitVar,\n                    name.expect(\"function declaration must have a name\")\n                        .to_js_string(self.interner()),\n                    dst,\n                );\n            }\n            NodeKind::Expression => {}\n        }\n    }\n\n    /// Compile an object method AST Node into bytecode.\n    pub(crate) fn object_method(\n        &mut self,\n        function: FunctionSpec<'_>,\n        kind: MethodKind,\n    ) -> Register {\n        let (generator, r#async, arrow) = (\n            function.kind.is_generator(),\n            function.kind.is_async(),\n            function.kind.is_arrow(),\n        );\n        let FunctionSpec {\n            name,\n            parameters,\n            body,\n            scopes,\n            name_scope,\n            linear_span,\n            ..\n        } = function;\n\n        let name = if let Some(name) = name {\n            let name = name.sym().to_js_string(self.interner());\n            match kind {\n                MethodKind::Ordinary => Some(name),\n                MethodKind::Get => Some(js_string!(js_str!(\"get \"), &name)),\n                MethodKind::Set => Some(js_string!(js_str!(\"set \"), &name)),\n            }\n        } else {\n            Some(js_string!())\n        };\n\n        let spanned_source_text = SpannedSourceText::new(self.source_text(), linear_span);\n\n        let code = FunctionCompiler::new(spanned_source_text)\n            .name(name)\n            .generator(generator)\n            .r#async(r#async)\n            .strict(self.strict())\n            .arrow(arrow)\n            .method(true)\n            .in_with(self.in_with)\n            .name_scope(name_scope.cloned())\n            .source_path(self.source_path.clone())\n            .compile(\n                parameters,\n                body,\n                self.variable_scope.clone(),\n                self.lexical_scope.clone(),\n                scopes,\n                function.contains_direct_eval,\n                self.interner,\n            );\n\n        let index = self.push_function_to_constants(code);\n        let dst = self.register_allocator.alloc();\n        self.emit_get_function(&dst, index);\n        dst\n    }\n\n    /// Compile a class method AST Node into bytecode.\n    fn method(&mut self, function: FunctionSpec<'_>) -> Register {\n        let (generator, r#async, arrow) = (\n            function.kind.is_generator(),\n            function.kind.is_async(),\n            function.kind.is_arrow(),\n        );\n        let FunctionSpec {\n            name,\n            parameters,\n            body,\n            scopes,\n            linear_span,\n            ..\n        } = function;\n\n        let name = if let Some(name) = name {\n            Some(name.sym().to_js_string(self.interner()))\n        } else {\n            Some(js_string!())\n        };\n\n        let spanned_source_text = SpannedSourceText::new(self.source_text(), linear_span);\n\n        let code = FunctionCompiler::new(spanned_source_text)\n            .name(name)\n            .generator(generator)\n            .r#async(r#async)\n            .strict(true)\n            .arrow(arrow)\n            .method(true)\n            .in_with(self.in_with)\n            .name_scope(function.name_scope.cloned())\n            .source_path(self.source_path.clone())\n            .compile(\n                parameters,\n                body,\n                self.variable_scope.clone(),\n                self.lexical_scope.clone(),\n                scopes,\n                function.contains_direct_eval,\n                self.interner,\n            );\n\n        let index = self.push_function_to_constants(code);\n        let dst = self.register_allocator.alloc();\n        self.emit_get_function(&dst, index);\n        dst\n    }\n\n    fn call(&mut self, callable: Callable<'_>, dst: CallResultDest<'_>) {\n        #[derive(PartialEq)]\n        enum CallKind {\n            CallEval,\n            Call,\n            New,\n        }\n\n        let (call, mut kind) = match callable {\n            Callable::Call(call) => (call, CallKind::Call),\n            Callable::New(new) => (new.call(), CallKind::New),\n        };\n\n        match call.function().flatten() {\n            Expression::PropertyAccess(access) if kind == CallKind::Call => {\n                let this = self.register_allocator.alloc();\n                let dst = self.register_allocator.alloc();\n                self.compile_access_preserve_this(access, &this, &dst);\n                self.push_from_register(&this);\n                self.push_from_register(&dst);\n                self.register_allocator.dealloc(this);\n                self.register_allocator.dealloc(dst);\n            }\n            Expression::Optional(opt) if kind == CallKind::Call => {\n                let this = self.register_allocator.alloc();\n                let dst = self.register_allocator.alloc();\n                self.compile_optional_preserve_this(opt, &this, &dst);\n                self.push_from_register(&this);\n                self.push_from_register(&dst);\n                self.register_allocator.dealloc(this);\n                self.register_allocator.dealloc(dst);\n            }\n            expr if kind == CallKind::Call => {\n                if let Expression::Identifier(ident) = expr {\n                    if ident.sym() == Sym::EVAL {\n                        kind = CallKind::CallEval;\n                    }\n\n                    if self.in_with {\n                        let name = self.resolve_identifier_expect(*ident);\n                        let binding = self.lexical_scope.get_identifier_reference(name);\n                        let index = self.get_binding(&binding);\n                        let index = match index {\n                            BindingKind::Global(index) | BindingKind::Stack(index) => index,\n                            BindingKind::Local(_) => {\n                                unreachable!(\"with binding cannot be local\")\n                            }\n                        };\n                        let value = self.register_allocator.alloc();\n                        self.bytecode\n                            .emit_this_for_object_environment_name(value.variable(), index.into());\n                        self.push_from_register(&value);\n                        self.register_allocator.dealloc(value);\n                    } else {\n                        self.push_from_register(&CallFrame::undefined_register());\n                    }\n                } else {\n                    self.push_from_register(&CallFrame::undefined_register());\n                }\n\n                self.compile_expr_to_stack(expr);\n            }\n            expr => {\n                let value = self.register_allocator.alloc();\n                self.compile_expr(expr, &value);\n                self.push_from_register(&CallFrame::undefined_register());\n                self.push_from_register(&value);\n                self.register_allocator.dealloc(value);\n            }\n        }\n\n        let mut compiler = self.position_guard(call);\n\n        let contains_spread = call\n            .args()\n            .iter()\n            .any(|arg| matches!(arg, Expression::Spread(_)));\n\n        if contains_spread {\n            let array = compiler.register_allocator.alloc();\n            let value = compiler.register_allocator.alloc();\n\n            compiler.bytecode.emit_store_new_array(array.variable());\n\n            for arg in call.args() {\n                compiler.compile_expr(arg, &value);\n                if let Expression::Spread(_) = arg {\n                    compiler.bytecode.emit_get_iterator(value.variable());\n                    compiler\n                        .bytecode\n                        .emit_push_iterator_to_array(array.variable());\n                } else {\n                    compiler\n                        .bytecode\n                        .emit_push_value_to_array(value.variable(), array.variable());\n                }\n            }\n\n            compiler.push_from_register(&array);\n\n            compiler.register_allocator.dealloc(array);\n            compiler.register_allocator.dealloc(value);\n        } else {\n            for arg in call.args() {\n                compiler.compile_expr_to_stack(arg);\n            }\n        }\n\n        match kind {\n            CallKind::CallEval => {\n                let scope_index = compiler.constants.len() as u32;\n                let lexical_scope = compiler.lexical_scope.clone();\n                compiler.constants.push(Constant::Scope(lexical_scope));\n                if contains_spread {\n                    compiler.bytecode.emit_call_eval_spread(scope_index.into());\n                } else {\n                    compiler\n                        .bytecode\n                        .emit_call_eval((call.args().len() as u32).into(), scope_index.into());\n                }\n            }\n            CallKind::Call if contains_spread => compiler.bytecode.emit_call_spread(),\n            CallKind::Call => {\n                compiler\n                    .bytecode\n                    .emit_call((call.args().len() as u32).into());\n            }\n            CallKind::New if contains_spread => compiler.bytecode.emit_new_spread(),\n            CallKind::New => compiler\n                .bytecode\n                .emit_new((call.args().len() as u32).into()),\n        }\n        match dst {\n            CallResultDest::Register(dst) => compiler.pop_into_register(dst),\n            CallResultDest::Discard => compiler.bytecode.emit_pop(),\n            CallResultDest::Stack => {} // leave result on stack\n        }\n    }\n\n    /// Finish compiling code with the [`ByteCompiler`] and return the generated [`CodeBlock`].\n    #[inline]\n    #[must_use]\n    #[allow(clippy::missing_const_for_fn)]\n    pub fn finish(mut self) -> CodeBlock {\n        // Push return at the end of the function compilation.\n        if let Some(async_handler) = self.async_handler {\n            self.patch_handler(async_handler);\n        }\n        self.r#return(false);\n\n        let final_bytecode_len = self.next_opcode_location();\n\n        let mapped_arguments_binding_indices = if self.emitted_mapped_arguments_object_opcode {\n            MappedArguments::binding_indices(&self.params, &self.parameter_scope, self.interner)\n        } else {\n            ThinVec::default()\n        };\n\n        let register_count = self.register_allocator.finish();\n\n        let source_map_entries = self.source_map_builder.build(final_bytecode_len.as_u32());\n\n        CodeBlock {\n            length: self.length,\n            register_count,\n            this_mode: self.this_mode,\n            parameter_length: self.params.as_ref().len() as u32,\n            mapped_arguments_binding_indices,\n            bytecode: self.bytecode.into_bytecode(),\n            constants: self.constants,\n            bindings: self.bindings.into_boxed_slice(),\n            handlers: self.handlers,\n            flags: Cell::new(self.code_block_flags),\n            ic: self.ic.into_boxed_slice(),\n            source_info: SourceInfo::new(\n                SourceMap::new(source_map_entries, self.source_path),\n                self.function_name,\n                self.spanned_source_text,\n            ),\n            global_lexs: self.global_lexs.into_boxed_slice(),\n            global_fns: self.global_fns.into_boxed_slice(),\n            global_vars: self.global_vars.into_boxed_slice(),\n            debug_id: CodeBlock::get_next_codeblock_id(),\n            #[cfg(feature = \"trace\")]\n            traced: Cell::new(false),\n        }\n    }\n\n    fn compile_declaration_pattern(\n        &mut self,\n        pattern: &Pattern,\n        def: BindingOpcode,\n        object: &Register,\n    ) {\n        self.compile_declaration_pattern_impl(pattern, def, object);\n    }\n}\n"
  },
  {
    "path": "core/engine/src/bytecompiler/module.rs",
    "content": "use super::{ByteCompiler, Literal, ToJsString};\nuse crate::vm::opcode::BindingOpcode;\nuse boa_ast::{ModuleItem, ModuleItemList, declaration::ExportDeclaration};\nuse boa_interner::Sym;\n\nimpl ByteCompiler<'_> {\n    /// Compiles a [`ModuleItemList`].\n    #[inline]\n    pub fn compile_module_item_list(&mut self, list: &ModuleItemList) {\n        for node in list.items() {\n            self.compile_module_item(node);\n        }\n    }\n\n    /// Compiles a [`ModuleItem`].\n    #[inline]\n    pub fn compile_module_item(&mut self, item: &ModuleItem) {\n        match item {\n            ModuleItem::StatementListItem(stmt) => {\n                self.compile_stmt_list_item(stmt, false, false);\n            }\n            ModuleItem::ImportDeclaration(_) => {\n                // ModuleItem : ImportDeclaration\n\n                // 1. Return empty.\n            }\n            ModuleItem::ExportDeclaration(export) => {\n                #[allow(clippy::match_same_arms)]\n                match export.as_ref() {\n                    ExportDeclaration::ReExport { .. } | ExportDeclaration::List(_) => {\n                        // ExportDeclaration :\n                        //    export ExportFromClause FromClause ;\n                        //    export NamedExports ;\n                        //        1. Return empty.\n                    }\n                    ExportDeclaration::DefaultFunctionDeclaration(_)\n                    | ExportDeclaration::DefaultGeneratorDeclaration(_)\n                    | ExportDeclaration::DefaultAsyncFunctionDeclaration(_)\n                    | ExportDeclaration::DefaultAsyncGeneratorDeclaration(_) => {\n                        // Already instantiated in `initialize_environment`.\n                    }\n                    ExportDeclaration::VarStatement(var) => self.compile_var_decl(var),\n                    ExportDeclaration::Declaration(decl) => self.compile_decl(decl, false),\n                    ExportDeclaration::DefaultClassDeclaration(cl) => {\n                        self.compile_class(cl.as_ref().into(), None);\n                    }\n                    ExportDeclaration::DefaultAssignmentExpression(expr) => {\n                        let function = self.register_allocator.alloc();\n                        self.compile_expr(expr, &function);\n\n                        if expr.is_anonymous_function_definition() {\n                            let default = self\n                                .interner()\n                                .resolve_expect(Sym::DEFAULT)\n                                .into_common(false);\n                            let key = self.register_allocator.alloc();\n                            self.emit_store_literal(Literal::String(default), &key);\n                            self.bytecode.emit_set_function_name(\n                                function.variable(),\n                                key.variable(),\n                                0u32.into(),\n                            );\n                            self.register_allocator.dealloc(key);\n                        }\n\n                        let name = Sym::DEFAULT_EXPORT.to_js_string(self.interner());\n                        self.emit_binding(BindingOpcode::InitLexical, name, &function);\n                        self.register_allocator.dealloc(function);\n                    }\n                }\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "core/engine/src/bytecompiler/register.rs",
    "content": "use crate::vm::opcode::RegisterOperand;\nuse std::mem::forget;\n\nbitflags::bitflags! {\n    #[derive(Debug, Default, Clone, Copy)]\n    struct RegisterFlags: u8 {\n        /// Whether the register is still in use (not deallocated).\n        const USED       = 0b0000_0001;\n\n        const PERSISTENT = 0b0000_0010;\n    }\n}\n\nimpl RegisterFlags {\n    fn is_used(self) -> bool {\n        self.contains(Self::USED)\n    }\n    fn is_persistent(self) -> bool {\n        self.contains(Self::PERSISTENT)\n    }\n}\n\n/// An entry in the [`RegisterAllocator`].\n#[derive(Debug, Default)]\npub(crate) struct RegisterEntry {\n    flags: RegisterFlags,\n}\n\n/// Represent a VM register.\n///\n/// This is intended to be passed by reference or to be moved, dropping this is a bug,\n/// it should only be dropped though the [`RegisterAllocator::dealloc()`] method.\n/// This doesn't apply to persistent registers.\n///\n/// A [`Register`] is index into the register allocator,\n/// as well as an index into the per-frame register file on each [`CallFrame`](crate::vm::CallFrame).\n#[derive(Debug)]\npub(crate) struct Register {\n    index: u32,\n    flags: RegisterFlags,\n}\n\nimpl Register {\n    /// The index of the [`Register`].\n    pub(crate) fn index(&self) -> u32 {\n        self.index\n    }\n\n    /// The index of the [`Register`] as a [`RegisterOperand`].\n    pub(crate) fn variable(&self) -> RegisterOperand {\n        self.index.into()\n    }\n\n    pub(crate) fn persistent(index: u32) -> Self {\n        Self {\n            index,\n            flags: RegisterFlags::PERSISTENT,\n        }\n    }\n}\n\nimpl Drop for Register {\n    /// This method should never be called.\n    /// It is used to detect when a register has not been deallocated.\n    fn drop(&mut self) {\n        if self.flags.is_persistent() {\n            return;\n        }\n\n        // Prevent double panic.\n        if std::thread::panicking() {\n            return;\n        }\n\n        unreachable!(\"forgot to deallocate a register!\")\n    }\n}\n\n#[derive(Debug, Default)]\npub(crate) struct RegisterAllocator {\n    registers: Vec<RegisterEntry>,\n}\n\nimpl RegisterAllocator {\n    pub(crate) fn alloc(&mut self) -> Register {\n        if let Some((i, register)) = self\n            .registers\n            .iter_mut()\n            .enumerate()\n            .find(|(_, reg)| !reg.flags.is_used())\n        {\n            assert!(!register.flags.is_persistent());\n\n            register.flags |= RegisterFlags::USED;\n            return Register {\n                index: i as u32,\n                flags: register.flags,\n            };\n        }\n\n        let flags = RegisterFlags::USED;\n\n        let index = self.registers.len() as u32;\n        self.registers.push(RegisterEntry { flags });\n\n        Register { index, flags }\n    }\n\n    pub(crate) fn alloc_persistent(&mut self) -> Register {\n        let mut reg = self.alloc();\n\n        let index = reg.index();\n\n        let register = &mut self.registers[index as usize];\n\n        register.flags |= RegisterFlags::PERSISTENT;\n\n        reg.flags = register.flags;\n        reg\n    }\n\n    #[track_caller]\n    pub(crate) fn dealloc(&mut self, reg: Register) {\n        assert!(\n            !reg.flags.is_persistent(),\n            \"Trying to deallocate a persistent register\"\n        );\n\n        let register = &mut self.registers[reg.index as usize];\n\n        assert!(\n            register.flags.is_used(),\n            \"Cannot deallocate unused variable\"\n        );\n        register.flags.set(RegisterFlags::USED, false);\n\n        // NOTE: We should not drop it since, dropping it used to detect bugs.\n        forget(reg);\n    }\n\n    pub(crate) fn finish(self) -> u32 {\n        for register in &self.registers {\n            debug_assert!(\n                !register.flags.is_used()\n                    || (register.flags.is_used() && register.flags.is_persistent())\n            );\n        }\n\n        self.registers.len() as u32\n    }\n}\n"
  },
  {
    "path": "core/engine/src/bytecompiler/statement/block.rs",
    "content": "use crate::bytecompiler::ByteCompiler;\nuse boa_ast::statement::Block;\n\nimpl ByteCompiler<'_> {\n    /// Compile a [`Block`] `boa_ast` node\n    pub(crate) fn compile_block(&mut self, block: &Block, use_expr: bool) {\n        let scope = self.push_declarative_scope(block.scope());\n        self.block_declaration_instantiation(block);\n        self.compile_statement_list(block.statement_list(), use_expr, true);\n        self.pop_declarative_scope(scope);\n    }\n}\n"
  },
  {
    "path": "core/engine/src/bytecompiler/statement/break.rs",
    "content": "use crate::bytecompiler::{\n    ByteCompiler,\n    jump_control::{JumpRecord, JumpRecordAction, JumpRecordKind},\n};\nuse boa_ast::statement::Break;\n\nimpl ByteCompiler<'_> {\n    /// Compile a [`Break`] `boa_ast` node\n    pub(crate) fn compile_break(&mut self, node: Break, _use_expr: bool) {\n        let actions = self.break_jump_record_actions(node);\n\n        JumpRecord::new(JumpRecordKind::Break, actions).perform_actions(Self::DUMMY_ADDRESS, self);\n    }\n\n    fn break_jump_record_actions(&self, node: Break) -> Vec<JumpRecordAction> {\n        let mut actions = Vec::default();\n        for (i, info) in self.jump_info.iter().enumerate().rev() {\n            let count = self.jump_info_open_environment_count(i);\n            actions.push(JumpRecordAction::PopEnvironments { count });\n\n            if !info.in_finally()\n                && let Some((finally_throw_flag, finally_throw_index)) = info.finally_throw\n            {\n                actions.push(JumpRecordAction::HandleFinally {\n                    index: info.jumps.len() as u32,\n                    finally_throw_flag,\n                    finally_throw_index,\n                });\n                actions.push(JumpRecordAction::Transfer { index: i as u32 });\n            }\n\n            if let Some(label) = node.label() {\n                if info.label() == Some(label) {\n                    actions.push(JumpRecordAction::Transfer { index: i as u32 });\n                    break;\n                }\n            } else if info.is_loop() || info.is_switch() {\n                actions.push(JumpRecordAction::Transfer { index: i as u32 });\n                break;\n            }\n        }\n\n        actions.reverse();\n        actions\n    }\n}\n"
  },
  {
    "path": "core/engine/src/bytecompiler/statement/continue.rs",
    "content": "use crate::bytecompiler::{\n    ByteCompiler,\n    jump_control::{JumpRecord, JumpRecordAction, JumpRecordKind},\n};\nuse boa_ast::statement::Continue;\n\nimpl ByteCompiler<'_> {\n    #[allow(clippy::unnecessary_wraps)]\n    pub(crate) fn compile_continue(&mut self, node: Continue, _use_expr: bool) {\n        let actions = self.continue_jump_record_actions(node);\n\n        JumpRecord::new(JumpRecordKind::Continue, actions)\n            .perform_actions(Self::DUMMY_ADDRESS, self);\n    }\n\n    fn continue_jump_record_actions(&self, node: Continue) -> Vec<JumpRecordAction> {\n        let mut actions = Vec::default();\n        for (i, info) in self.jump_info.iter().enumerate().rev() {\n            let count = self.jump_info_open_environment_count(i);\n            actions.push(JumpRecordAction::PopEnvironments { count });\n\n            if !info.in_finally()\n                && let Some((finally_throw_flag, finally_throw_index)) = info.finally_throw\n            {\n                actions.push(JumpRecordAction::HandleFinally {\n                    index: info.jumps.len() as u32,\n                    finally_throw_flag,\n                    finally_throw_index,\n                });\n                actions.push(JumpRecordAction::Transfer { index: i as u32 });\n            }\n\n            if let Some(label) = node.label() {\n                if info.label() == Some(label) {\n                    actions.push(JumpRecordAction::Transfer { index: i as u32 });\n                    break;\n                }\n            } else if info.is_loop() {\n                actions.push(JumpRecordAction::Transfer { index: i as u32 });\n                break;\n            }\n\n            if info.iterator_loop() {\n                actions.push(JumpRecordAction::CloseIterator {\n                    r#async: info.for_await_of_loop(),\n                });\n            }\n        }\n\n        actions.reverse();\n        actions\n    }\n}\n"
  },
  {
    "path": "core/engine/src/bytecompiler/statement/if.rs",
    "content": "use crate::bytecompiler::ByteCompiler;\nuse boa_ast::statement::If;\n\nimpl ByteCompiler<'_> {\n    pub(crate) fn compile_if(&mut self, node: &If, use_expr: bool) {\n        let value = self.register_allocator.alloc();\n        self.compile_expr(node.cond(), &value);\n        self.if_else_with_dealloc(\n            value,\n            |compiler| {\n                compiler.compile_stmt(node.body(), use_expr, true);\n            },\n            |compiler| {\n                if let Some(else_body) = node.else_node() {\n                    compiler.compile_stmt(else_body, use_expr, true);\n                }\n            },\n        );\n    }\n}\n"
  },
  {
    "path": "core/engine/src/bytecompiler/statement/labelled.rs",
    "content": "use crate::bytecompiler::{ByteCompiler, NodeKind};\nuse boa_ast::{\n    Statement,\n    statement::{Labelled, LabelledItem},\n};\n\nimpl ByteCompiler<'_> {\n    /// Compile a [`Labelled`] `boa_ast` node\n    pub(crate) fn compile_labelled(&mut self, labelled: &Labelled, use_expr: bool) {\n        let labelled_loc = self.next_opcode_location();\n        self.push_labelled_control_info(labelled.label(), labelled_loc, use_expr);\n\n        match labelled.item() {\n            LabelledItem::Statement(stmt) => match stmt {\n                Statement::ForLoop(for_loop) => {\n                    self.compile_for_loop(for_loop, Some(labelled.label()), use_expr);\n                }\n                Statement::ForInLoop(for_in_loop) => {\n                    self.compile_for_in_loop(for_in_loop, Some(labelled.label()), use_expr);\n                }\n                Statement::ForOfLoop(for_of_loop) => {\n                    self.compile_for_of_loop(for_of_loop, Some(labelled.label()), use_expr);\n                }\n                Statement::WhileLoop(while_loop) => {\n                    self.compile_while_loop(while_loop, Some(labelled.label()), use_expr);\n                }\n                Statement::DoWhileLoop(do_while_loop) => {\n                    self.compile_do_while_loop(do_while_loop, Some(labelled.label()), use_expr);\n                }\n                stmt => self.compile_stmt(stmt, use_expr, true),\n            },\n            LabelledItem::FunctionDeclaration(f) => {\n                let dst = self.register_allocator.alloc();\n                self.function_with_binding(f.into(), NodeKind::Declaration, &dst);\n                self.register_allocator.dealloc(dst);\n            }\n        }\n\n        self.pop_labelled_control_info();\n    }\n}\n"
  },
  {
    "path": "core/engine/src/bytecompiler/statement/loop.rs",
    "content": "use boa_ast::{\n    declaration::Binding,\n    operations::bound_names,\n    scope::BindingLocatorError,\n    statement::{\n        DoWhileLoop, ForInLoop, ForLoop, ForOfLoop, WhileLoop,\n        iteration::{ForLoopInitializer, IterableLoopInitializer},\n    },\n};\nuse boa_interner::Sym;\n\nuse crate::{\n    bytecompiler::{Access, BindingAccessOpcode, ByteCompiler, ToJsString},\n    vm::opcode::BindingOpcode,\n};\n\nimpl ByteCompiler<'_> {\n    pub(crate) fn compile_for_loop(\n        &mut self,\n        for_loop: &ForLoop,\n        label: Option<Sym>,\n        use_expr: bool,\n    ) {\n        let mut let_binding_indices = None;\n        let mut outer_scope_local = None;\n        let mut outer_scope = None;\n\n        if let Some(init) = for_loop.init() {\n            match init {\n                ForLoopInitializer::Expression(expr) => {\n                    let value = self.register_allocator.alloc();\n                    self.compile_expr(expr, &value);\n                    self.register_allocator.dealloc(value);\n                }\n                ForLoopInitializer::Var(decl) => {\n                    self.compile_var_decl(decl);\n                }\n                ForLoopInitializer::Lexical(decl) => {\n                    let scope_index = if decl.scope().all_bindings_local() {\n                        outer_scope_local = Some(self.lexical_scope.clone());\n                        self.lexical_scope = decl.scope().clone();\n                        None\n                    } else {\n                        outer_scope = Some(self.lexical_scope.clone());\n                        let scope_index = self.push_scope(decl.scope());\n                        self.bytecode.emit_push_scope(scope_index.into());\n                        Some(scope_index)\n                    };\n\n                    let names = bound_names(decl.declaration());\n                    if decl.declaration().is_const() {\n                    } else {\n                        let mut indices = Vec::with_capacity(names.len());\n                        for name in &names {\n                            let name = name.to_js_string(self.interner());\n                            let binding = decl\n                                .scope()\n                                .get_binding_reference(&name)\n                                .expect(\"binding must exist\");\n                            let index = self.insert_binding(binding);\n                            indices.push(index);\n                        }\n                        let_binding_indices = Some((indices, scope_index));\n                    }\n                    self.compile_lexical_decl(decl.declaration());\n                }\n            }\n        }\n\n        self.push_empty_loop_jump_control(use_expr);\n\n        // Hoist loop-invariant constants from the condition (e.g. `10` in `i < 10`)\n        // so the value is loaded into a register once, not on every iteration.\n        let hoisted = self.try_hoist_loop_condition(for_loop.condition());\n\n        // Per-iteration binding copy: for `for (let i = ...)`, each iteration needs\n        // a fresh binding per the spec (important for closures). When the scope requires\n        // a runtime environment (scope_index is Some), we must pop/push the environment\n        // and copy bindings. When all bindings are local (scope_index is None), the\n        // GetName/PutLexicalValue pair is a no-op (Move to temp, Move back) and can be skipped.\n        if let Some((let_binding_indices, Some(scope_index))) = &let_binding_indices {\n            let mut values = Vec::with_capacity(let_binding_indices.len());\n            for index in let_binding_indices {\n                let value = self.register_allocator.alloc();\n                self.emit_binding_access(BindingAccessOpcode::GetName, index, &value);\n                values.push((index, value));\n            }\n\n            self.bytecode.emit_pop_environment();\n            self.bytecode.emit_push_scope((*scope_index).into());\n\n            for (index, value) in values {\n                self.emit_binding_access(BindingAccessOpcode::PutLexicalValue, index, &value);\n                self.register_allocator.dealloc(value);\n            }\n        }\n\n        let initial_jump = self.jump();\n        let start_address = self.next_opcode_location();\n\n        self.current_jump_control_mut()\n            .expect(\"jump_control must exist as it was just pushed\")\n            .set_label(label);\n        self.current_jump_control_mut()\n            .expect(\"jump_control must exist as it was just pushed\")\n            .set_start_address(start_address);\n\n        if let Some((let_binding_indices, Some(scope_index))) = &let_binding_indices {\n            let mut values = Vec::with_capacity(let_binding_indices.len());\n            for index in let_binding_indices {\n                let value = self.register_allocator.alloc();\n                self.emit_binding_access(BindingAccessOpcode::GetName, index, &value);\n                values.push((index, value));\n            }\n\n            self.bytecode.emit_pop_environment();\n            self.bytecode.emit_push_scope((*scope_index).into());\n\n            for (index, value) in values {\n                self.emit_binding_access(BindingAccessOpcode::PutLexicalValue, index, &value);\n                self.register_allocator.dealloc(value);\n            }\n        }\n\n        self.bytecode.emit_increment_loop_iteration();\n\n        if let Some(final_expr) = for_loop.final_expr() {\n            self.compile_expr_for_side_effects(final_expr);\n        }\n\n        self.patch_jump(initial_jump);\n\n        let exit = for_loop\n            .condition()\n            .map(|condition| self.compile_condition_and_branch(condition, hoisted.as_ref()));\n\n        self.compile_stmt(for_loop.body(), use_expr, true);\n\n        self.bytecode.emit_jump(start_address);\n\n        if let Some(exit) = exit {\n            self.patch_jump(exit);\n        }\n        self.pop_loop_control_info();\n\n        if let Some(hoisted) = hoisted {\n            self.register_allocator.dealloc(hoisted.register);\n        }\n\n        if let Some(outer_scope_local) = outer_scope_local {\n            self.lexical_scope = outer_scope_local;\n        }\n        self.pop_declarative_scope(outer_scope);\n    }\n\n    pub(crate) fn compile_for_in_loop(\n        &mut self,\n        for_in_loop: &ForInLoop,\n        label: Option<Sym>,\n        use_expr: bool,\n    ) {\n        // Handle https://tc39.es/ecma262/#prod-annexB-ForInOfStatement\n        if let IterableLoopInitializer::Var(var) = for_in_loop.initializer()\n            && let Binding::Identifier(ident) = var.binding()\n            && let Some(init) = var.init()\n        {\n            let ident = ident.to_js_string(self.interner());\n            let value = self.register_allocator.alloc();\n            self.compile_expr(init, &value);\n            self.emit_binding(BindingOpcode::InitVar, ident, &value);\n            self.register_allocator.dealloc(value);\n        }\n        let outer_scope = self.push_declarative_scope(for_in_loop.target_scope());\n        let value = self.register_allocator.alloc();\n        self.compile_expr(for_in_loop.target(), &value);\n        self.pop_declarative_scope(outer_scope);\n\n        let early_exit = self.jump_if_null_or_undefined(&value);\n\n        self.bytecode.emit_create_for_in_iterator(value.variable());\n\n        self.register_allocator.dealloc(value);\n\n        let start_address = self.next_opcode_location();\n        self.push_loop_control_info_for_of_in_loop(label, start_address, use_expr);\n        self.bytecode.emit_increment_loop_iteration();\n\n        self.bytecode.emit_iterator_next();\n\n        let done_reg = self.register_allocator.alloc();\n        self.bytecode.emit_iterator_done(done_reg.variable());\n        let exit = self.jump_if_true(&done_reg);\n        self.register_allocator.dealloc(done_reg);\n\n        let outer_scope = self.push_declarative_scope(for_in_loop.scope());\n\n        // For let/const with a local identifier binding, emit iterator_value\n        // directly into the binding's persistent register to avoid a Move.\n        if let IterableLoopInitializer::Let(Binding::Identifier(ident))\n        | IterableLoopInitializer::Const(Binding::Identifier(ident)) = for_in_loop.initializer()\n            && let ident = ident.to_js_string(self.interner())\n            && let binding = self.lexical_scope.get_identifier_reference(ident)\n            && binding.local()\n        {\n            let reg = self.register_allocator.alloc_persistent();\n            self.local_binding_registers.insert(binding, reg.index());\n            self.bytecode.emit_iterator_value(reg.variable());\n        } else {\n            let value = self.register_allocator.alloc();\n            self.bytecode.emit_iterator_value(value.variable());\n\n            match for_in_loop.initializer() {\n                IterableLoopInitializer::Identifier(ident) => {\n                    let ident = ident.to_js_string(self.interner());\n                    self.emit_binding(BindingOpcode::InitVar, ident, &value);\n                }\n                IterableLoopInitializer::Access(access) => {\n                    self.access_set(Access::Property { access }, |_| &value);\n                }\n                IterableLoopInitializer::Var(declaration) => match declaration.binding() {\n                    Binding::Identifier(ident) => {\n                        let ident = ident.to_js_string(self.interner());\n                        self.emit_binding(BindingOpcode::InitVar, ident, &value);\n                    }\n                    Binding::Pattern(pattern) => {\n                        self.compile_declaration_pattern(pattern, BindingOpcode::InitVar, &value);\n                    }\n                },\n                IterableLoopInitializer::Let(declaration)\n                | IterableLoopInitializer::Const(declaration) => match declaration {\n                    Binding::Identifier(ident) => {\n                        let ident = ident.to_js_string(self.interner());\n                        self.emit_binding(BindingOpcode::InitLexical, ident, &value);\n                    }\n                    Binding::Pattern(pattern) => {\n                        self.compile_declaration_pattern(\n                            pattern,\n                            BindingOpcode::InitLexical,\n                            &value,\n                        );\n                    }\n                },\n                IterableLoopInitializer::Pattern(pattern) => {\n                    self.compile_declaration_pattern(pattern, BindingOpcode::SetName, &value);\n                }\n            }\n\n            self.register_allocator.dealloc(value);\n        }\n\n        self.compile_stmt(for_in_loop.body(), use_expr, true);\n        self.pop_declarative_scope(outer_scope);\n\n        self.bytecode.emit_jump(start_address);\n\n        self.patch_jump(exit);\n        self.pop_loop_control_info();\n\n        self.iterator_close(false);\n        self.patch_jump(early_exit);\n    }\n\n    pub(crate) fn compile_for_of_loop(\n        &mut self,\n        for_of_loop: &ForOfLoop,\n        label: Option<Sym>,\n        use_expr: bool,\n    ) {\n        let outer_scope = self.push_declarative_scope(for_of_loop.iterable_scope());\n        let object = self.register_allocator.alloc();\n        self.compile_expr(for_of_loop.iterable(), &object);\n        self.pop_declarative_scope(outer_scope);\n\n        if for_of_loop.r#await() {\n            self.bytecode.emit_get_async_iterator(object.variable());\n        } else {\n            self.bytecode.emit_get_iterator(object.variable());\n        }\n\n        self.register_allocator.dealloc(object);\n\n        let start_address = self.next_opcode_location();\n        if for_of_loop.r#await() {\n            self.push_loop_control_info_for_await_of_loop(label, start_address, use_expr);\n        } else {\n            self.push_loop_control_info_for_of_in_loop(label, start_address, use_expr);\n        }\n        self.bytecode.emit_increment_loop_iteration();\n\n        self.bytecode.emit_iterator_next();\n        if for_of_loop.r#await() {\n            let value = self.register_allocator.alloc();\n            self.bytecode.emit_iterator_result(value.variable());\n            self.bytecode.emit_await(value.variable());\n            let resume_kind = self.register_allocator.alloc();\n            self.pop_into_register(&resume_kind);\n            self.pop_into_register(&value);\n\n            self.bytecode\n                .emit_iterator_finish_async_next(resume_kind.variable(), value.variable());\n            self.generator_next(&value, &resume_kind);\n            self.register_allocator.dealloc(value);\n            self.register_allocator.dealloc(resume_kind);\n        }\n\n        let done_reg = self.register_allocator.alloc();\n        self.bytecode.emit_iterator_done(done_reg.variable());\n        let exit = self.jump_if_true(&done_reg);\n        self.register_allocator.dealloc(done_reg);\n\n        let outer_scope = self.push_declarative_scope(for_of_loop.scope());\n\n        // For let/const with a local identifier binding, emit iterator_value\n        // directly into the binding's persistent register to avoid a Move.\n        let handler_index = if let IterableLoopInitializer::Let(Binding::Identifier(ident))\n        | IterableLoopInitializer::Const(Binding::Identifier(ident)) =\n            for_of_loop.initializer()\n            && let ident = ident.to_js_string(self.interner())\n            && let ident = self.lexical_scope.get_identifier_reference(ident)\n            && ident.local()\n        {\n            let reg = self.register_allocator.alloc_persistent();\n            self.local_binding_registers.insert(ident, reg.index());\n            self.bytecode.emit_iterator_value(reg.variable());\n\n            self.push_handler()\n        } else {\n            let value = self.register_allocator.alloc();\n            self.bytecode.emit_iterator_value(value.variable());\n            let handler_index = self.push_handler();\n            match for_of_loop.initializer() {\n                IterableLoopInitializer::Identifier(ident) => {\n                    let ident = ident.to_js_string(self.interner());\n                    match self.lexical_scope.set_mutable_binding(ident.clone()) {\n                        Ok(binding) => {\n                            let index = self.insert_binding(binding);\n                            self.emit_binding_access(\n                                BindingAccessOpcode::DefInitVar,\n                                &index,\n                                &value,\n                            );\n                        }\n                        Err(BindingLocatorError::MutateImmutable) => {\n                            let index = self.get_or_insert_string(ident);\n                            self.bytecode.emit_throw_mutate_immutable(index.into());\n                        }\n                        Err(BindingLocatorError::Silent) => {}\n                    }\n                }\n                IterableLoopInitializer::Access(access) => {\n                    self.access_set(Access::Property { access }, |_| &value);\n                }\n                IterableLoopInitializer::Var(declaration) => {\n                    // ignore initializers since those aren't allowed on for-of loops.\n                    assert!(declaration.init().is_none());\n                    match declaration.binding() {\n                        Binding::Identifier(ident) => {\n                            let ident = ident.to_js_string(self.interner());\n                            self.emit_binding(BindingOpcode::InitVar, ident, &value);\n                        }\n                        Binding::Pattern(pattern) => {\n                            self.compile_declaration_pattern(\n                                pattern,\n                                BindingOpcode::InitVar,\n                                &value,\n                            );\n                        }\n                    }\n                }\n                IterableLoopInitializer::Let(declaration)\n                | IterableLoopInitializer::Const(declaration) => match declaration {\n                    Binding::Identifier(ident) => {\n                        let ident = ident.to_js_string(self.interner());\n                        self.emit_binding(BindingOpcode::InitLexical, ident, &value);\n                    }\n                    Binding::Pattern(pattern) => {\n                        self.compile_declaration_pattern(\n                            pattern,\n                            BindingOpcode::InitLexical,\n                            &value,\n                        );\n                    }\n                },\n                IterableLoopInitializer::Pattern(pattern) => {\n                    self.compile_declaration_pattern(pattern, BindingOpcode::SetName, &value);\n                }\n            }\n\n            self.register_allocator.dealloc(value);\n            handler_index\n        };\n\n        self.compile_stmt(for_of_loop.body(), use_expr, true);\n\n        {\n            let exit = self.jump();\n            self.patch_handler(handler_index);\n\n            let error = self.register_allocator.alloc();\n            self.bytecode.emit_exception(error.variable());\n\n            // NOTE: Capture throw of the iterator close and ignore it.\n            let handler_index = self.push_handler();\n            self.iterator_close(for_of_loop.r#await());\n            self.patch_handler(handler_index);\n\n            self.bytecode.emit_throw(error.variable());\n            self.register_allocator.dealloc(error);\n            self.patch_jump(exit);\n        }\n\n        self.pop_declarative_scope(outer_scope);\n        self.bytecode.emit_jump(start_address);\n\n        self.patch_jump(exit);\n        self.pop_loop_control_info();\n\n        self.iterator_close(for_of_loop.r#await());\n    }\n\n    pub(crate) fn compile_while_loop(\n        &mut self,\n        while_loop: &WhileLoop,\n        label: Option<Sym>,\n        use_expr: bool,\n    ) {\n        // Hoist loop-invariant constants from the condition.\n        let hoisted = self.try_hoist_loop_condition(Some(while_loop.condition()));\n\n        let start_address = self.next_opcode_location();\n        self.bytecode.emit_increment_loop_iteration();\n        self.push_loop_control_info(label, start_address, use_expr);\n\n        let exit = self.compile_condition_and_branch(while_loop.condition(), hoisted.as_ref());\n\n        self.compile_stmt(while_loop.body(), use_expr, true);\n\n        self.bytecode.emit_jump(start_address);\n\n        self.patch_jump(exit);\n        self.pop_loop_control_info();\n\n        if let Some(hoisted) = hoisted {\n            self.register_allocator.dealloc(hoisted.register);\n        }\n    }\n\n    pub(crate) fn compile_do_while_loop(\n        &mut self,\n        do_while_loop: &DoWhileLoop,\n        label: Option<Sym>,\n        use_expr: bool,\n    ) {\n        // Hoist loop-invariant constants from the condition.\n        let hoisted = self.try_hoist_loop_condition(Some(do_while_loop.cond()));\n\n        let initial_label = self.jump();\n\n        let start_address = self.next_opcode_location();\n\n        self.push_loop_control_info(label, start_address, use_expr);\n\n        let condition_label_address = self.next_opcode_location();\n        self.bytecode.emit_increment_loop_iteration();\n\n        let exit = self.compile_condition_and_branch(do_while_loop.cond(), hoisted.as_ref());\n\n        self.patch_jump(initial_label);\n\n        self.compile_stmt(do_while_loop.body(), use_expr, true);\n\n        self.bytecode.emit_jump(condition_label_address);\n        self.patch_jump(exit);\n\n        self.pop_loop_control_info();\n\n        if let Some(hoisted) = hoisted {\n            self.register_allocator.dealloc(hoisted.register);\n        }\n    }\n}\n"
  },
  {
    "path": "core/engine/src/bytecompiler/statement/mod.rs",
    "content": "use super::jump_control::{JumpRecord, JumpRecordAction, JumpRecordKind};\nuse crate::{bytecompiler::ByteCompiler, vm::CallFrame};\nuse boa_ast::Statement;\n\nmod block;\nmod r#break;\nmod r#continue;\nmod r#if;\nmod labelled;\nmod r#loop;\nmod switch;\nmod r#try;\nmod with;\n\nimpl ByteCompiler<'_> {\n    /// Compiles a [`Statement`] `boa_ast` node.\n    pub fn compile_stmt(&mut self, node: &Statement, use_expr: bool, root_statement: bool) {\n        match node {\n            Statement::Var(var) => self.compile_var_decl(var),\n            Statement::If(node) => self.compile_if(node, use_expr),\n            Statement::ForLoop(for_loop) => {\n                self.compile_for_loop(for_loop, None, use_expr);\n            }\n            Statement::ForInLoop(for_in_loop) => {\n                self.compile_for_in_loop(for_in_loop, None, use_expr);\n            }\n            Statement::ForOfLoop(for_of_loop) => {\n                self.compile_for_of_loop(for_of_loop, None, use_expr);\n            }\n            Statement::WhileLoop(while_loop) => {\n                self.compile_while_loop(while_loop, None, use_expr);\n            }\n            Statement::DoWhileLoop(do_while_loop) => {\n                self.compile_do_while_loop(do_while_loop, None, use_expr);\n            }\n            Statement::Block(block) => {\n                self.compile_block(block, use_expr);\n            }\n            Statement::Labelled(labelled) => {\n                self.compile_labelled(labelled, use_expr);\n            }\n            Statement::Continue(node) => {\n                if root_statement && (use_expr || self.jump_control_info_has_use_expr()) {\n                    self.bytecode\n                        .emit_set_accumulator(CallFrame::undefined_register().variable());\n                }\n                self.compile_continue(*node, use_expr);\n            }\n            Statement::Break(node) => {\n                if root_statement && (use_expr || self.jump_control_info_has_use_expr()) {\n                    self.bytecode\n                        .emit_set_accumulator(CallFrame::undefined_register().variable());\n                }\n                self.compile_break(*node, use_expr);\n            }\n            Statement::Throw(throw) => {\n                let mut compiler = self.position_guard(throw.target());\n\n                let error = compiler.register_allocator.alloc();\n                compiler.compile_expr(throw.target(), &error);\n                compiler.bytecode.emit_throw(error.variable());\n                compiler.register_allocator.dealloc(error);\n            }\n            Statement::Switch(switch) => {\n                self.compile_switch(switch, use_expr);\n            }\n            Statement::Return(ret) => {\n                if let Some(expr) = ret.target() {\n                    if self.is_async_generator() {\n                        let value = self.register_allocator.alloc();\n                        self.compile_expr(expr, &value);\n                        self.bytecode.emit_await(value.variable());\n                        let resume_kind = self.register_allocator.alloc();\n                        self.pop_into_register(&resume_kind);\n                        self.pop_into_register(&value);\n                        self.generator_next(&value, &resume_kind);\n                        self.register_allocator.dealloc(resume_kind);\n                        self.push_from_register(&value);\n                        self.register_allocator.dealloc(value);\n                    } else {\n                        self.compile_expr_to_stack(expr);\n                    }\n                } else {\n                    self.push_from_register(&CallFrame::undefined_register());\n                }\n\n                self.r#return(true);\n            }\n            Statement::Try(t) => self.compile_try(t, use_expr),\n            Statement::Expression(expr) => {\n                if use_expr {\n                    let value = self.register_allocator.alloc();\n                    self.compile_expr(expr, &value);\n                    self.bytecode.emit_set_accumulator(value.variable());\n                    self.register_allocator.dealloc(value);\n                } else {\n                    self.compile_expr_for_side_effects(expr);\n                }\n            }\n            Statement::With(with) => self.compile_with(with, use_expr),\n            Statement::Empty | Statement::Debugger => {}\n        }\n    }\n\n    pub(crate) fn r#return(&mut self, return_value_on_stack: bool) {\n        let actions = self.return_jump_record_actions();\n\n        JumpRecord::new(\n            JumpRecordKind::Return {\n                return_value_on_stack,\n            },\n            actions,\n        )\n        .perform_actions(Self::DUMMY_ADDRESS, self);\n    }\n\n    fn return_jump_record_actions(&self) -> Vec<JumpRecordAction> {\n        let mut actions = Vec::default();\n        for (i, info) in self.jump_info.iter().enumerate().rev() {\n            let count = self.jump_info_open_environment_count(i);\n            actions.push(JumpRecordAction::PopEnvironments { count });\n\n            if !info.in_finally()\n                && let Some((finally_throw_flag, finally_throw_index)) = info.finally_throw\n            {\n                actions.push(JumpRecordAction::HandleFinally {\n                    index: info.jumps.len() as u32,\n                    finally_throw_flag,\n                    finally_throw_index,\n                });\n                actions.push(JumpRecordAction::Transfer { index: i as u32 });\n            }\n\n            if info.iterator_loop() {\n                actions.push(JumpRecordAction::CloseIterator {\n                    r#async: info.for_await_of_loop(),\n                });\n            }\n        }\n\n        actions.reverse();\n        actions\n    }\n}\n"
  },
  {
    "path": "core/engine/src/bytecompiler/statement/switch.rs",
    "content": "use crate::bytecompiler::ByteCompiler;\nuse boa_ast::statement::Switch;\n\nimpl ByteCompiler<'_> {\n    /// Compile a [`Switch`] `boa_ast` node\n    pub(crate) fn compile_switch(&mut self, switch: &Switch, use_expr: bool) {\n        let value = self.register_allocator.alloc();\n        self.compile_expr(switch.val(), &value);\n        let outer_scope = self.push_declarative_scope(switch.scope());\n\n        self.block_declaration_instantiation(switch);\n\n        let start_address = self.next_opcode_location();\n        self.push_switch_control_info(None, start_address, use_expr);\n\n        let mut labels = Vec::with_capacity(switch.cases().len());\n\n        let condition = self.register_allocator.alloc();\n\n        for case in switch.cases() {\n            // If it does not have a condition it is the default case.\n            let label = if let Some(cond) = case.condition() {\n                self.compile_expr(cond, &condition);\n                self.case(&value, &condition)\n            } else {\n                Self::DUMMY_LABEL\n            };\n\n            labels.push(label);\n        }\n\n        self.register_allocator.dealloc(condition);\n        self.register_allocator.dealloc(value);\n\n        let default_label = self.jump();\n        let mut default_label_set = false;\n\n        for (label, case) in labels.into_iter().zip(switch.cases()) {\n            // Check if it's the default case.\n            let label = if label == Self::DUMMY_LABEL {\n                default_label_set = true;\n                default_label\n            } else {\n                label\n            };\n            self.patch_jump(label);\n\n            self.compile_statement_list(case.body(), use_expr, true);\n        }\n\n        if !default_label_set {\n            self.patch_jump(default_label);\n        }\n\n        self.pop_switch_control_info();\n        self.pop_declarative_scope(outer_scope);\n    }\n}\n"
  },
  {
    "path": "core/engine/src/bytecompiler/statement/try.rs",
    "content": "use crate::{\n    bytecompiler::{ByteCompiler, Register, ToJsString, jump_control::JumpControlInfoFlags},\n    vm::{CallFrame, opcode::BindingOpcode},\n};\nuse boa_ast::{\n    Statement, StatementListItem,\n    declaration::Binding,\n    statement::{Block, Catch, Finally, Try},\n};\n\nenum TryVariant<'a> {\n    Catch(&'a Catch),\n    Finally((&'a Finally, Register, Register)),\n    CatchFinally((&'a Catch, &'a Finally, Register, Register)),\n}\n\nimpl TryVariant<'_> {\n    fn finally_re_throw_register(&self) -> Option<(&Register, &Register)> {\n        match self {\n            TryVariant::Catch(_) => None,\n            TryVariant::Finally((_, f, i)) | TryVariant::CatchFinally((_, _, f, i)) => Some((f, i)),\n        }\n    }\n}\n\nimpl ByteCompiler<'_> {\n    /// Compile try statement.\n    pub(crate) fn compile_try(&mut self, t: &Try, use_expr: bool) {\n        let variant = match (t.catch(), t.finally()) {\n            (Some(catch), Some(finally)) => {\n                let finally_re_throw = self.register_allocator.alloc();\n                let finally_jump_index = self.register_allocator.alloc();\n\n                self.bytecode.emit_store_true(finally_re_throw.variable());\n                self.bytecode.emit_store_zero(finally_jump_index.variable());\n\n                self.push_try_with_finally_control_info(\n                    &finally_re_throw,\n                    &finally_jump_index,\n                    use_expr,\n                );\n                TryVariant::CatchFinally((catch, finally, finally_re_throw, finally_jump_index))\n            }\n            (Some(catch), None) => TryVariant::Catch(catch),\n            (None, Some(finally)) => {\n                let finally_re_throw = self.register_allocator.alloc();\n                let finally_jump_index = self.register_allocator.alloc();\n\n                self.bytecode.emit_store_true(finally_re_throw.variable());\n                self.bytecode.emit_store_zero(finally_jump_index.variable());\n\n                self.push_try_with_finally_control_info(\n                    &finally_re_throw,\n                    &finally_jump_index,\n                    use_expr,\n                );\n                TryVariant::Finally((finally, finally_re_throw, finally_jump_index))\n            }\n            (None, None) => unreachable!(\"try statement must have either catch or finally\"),\n        };\n\n        let try_handler = self.push_handler();\n\n        // Compile try block\n        self.compile_block(t.block(), use_expr);\n\n        if let Some((finally_re_throw, _)) = variant.finally_re_throw_register() {\n            self.bytecode.emit_store_false(finally_re_throw.variable());\n        }\n\n        let finally = self.jump();\n\n        self.patch_handler(try_handler);\n\n        match variant {\n            TryVariant::Catch(c) => {\n                let error = self.register_allocator.alloc();\n                self.bytecode.emit_exception(error.variable());\n                self.compile_catch_stmt(c, &error, use_expr);\n                self.register_allocator.dealloc(error);\n                self.patch_jump(finally);\n            }\n            TryVariant::CatchFinally((c, f, finally_re_throw, finally_jump_index)) => {\n                let catch_handler = self.push_handler();\n                let error = self.register_allocator.alloc();\n                self.bytecode.emit_exception(error.variable());\n                self.compile_catch_stmt(c, &error, use_expr);\n                self.bytecode.emit_store_false(finally_re_throw.variable());\n\n                let no_throw = self.jump();\n                self.patch_handler(catch_handler);\n                self.bytecode.emit_store_true(finally_re_throw.variable());\n\n                self.patch_jump(no_throw);\n                self.patch_jump(finally);\n\n                let finally_start = self.next_opcode_location();\n                self.jump_info\n                    .last_mut()\n                    .expect(\"there should be a try block\")\n                    .flags |= JumpControlInfoFlags::IN_FINALLY;\n                self.compile_finally_stmt(f);\n                self.register_allocator.dealloc(error);\n                let do_not_throw_exit = self.jump_if_false(&finally_re_throw);\n                self.bytecode.emit_re_throw();\n                self.patch_jump(do_not_throw_exit);\n                self.pop_try_with_finally_control_info(finally_start);\n                self.register_allocator.dealloc(finally_re_throw);\n                self.register_allocator.dealloc(finally_jump_index);\n            }\n            TryVariant::Finally((f, finally_re_throw, finally_jump_index))\n                if self.is_generator() =>\n            {\n                let catch_handler = self.push_handler();\n                let error = self.register_allocator.alloc();\n                self.bytecode.emit_exception(error.variable());\n                // Is this a generator `return()` empty exception?\n                //\n                // This is false because when the `Exception` opcode is executed,\n                // it rethrows the empty exception, so if we reached this section,\n                // that means it's not an `return()` generator exception.\n                let re_throw_generator = self.register_allocator.alloc();\n                self.bytecode\n                    .emit_store_false(re_throw_generator.variable());\n\n                // Should we rethrow the exception?\n                self.bytecode.emit_store_true(finally_re_throw.variable());\n\n                let no_throw = self.jump();\n                self.patch_handler(catch_handler);\n                self.bytecode.emit_store_true(re_throw_generator.variable());\n\n                self.patch_jump(no_throw);\n                self.patch_jump(finally);\n\n                let finally_start = self.next_opcode_location();\n                self.jump_info\n                    .last_mut()\n                    .expect(\"there should be a try block\")\n                    .flags |= JumpControlInfoFlags::IN_FINALLY;\n                self.compile_finally_stmt(f);\n                let do_not_throw_exit = self.jump_if_false(&finally_re_throw);\n                let is_generator_exit = self.jump_if_true(&re_throw_generator);\n                self.bytecode.emit_throw(error.variable());\n                self.register_allocator.dealloc(error);\n                self.patch_jump(is_generator_exit);\n                self.bytecode.emit_re_throw();\n                self.patch_jump(do_not_throw_exit);\n                self.register_allocator.dealloc(re_throw_generator);\n                self.pop_try_with_finally_control_info(finally_start);\n                self.register_allocator.dealloc(finally_re_throw);\n                self.register_allocator.dealloc(finally_jump_index);\n            }\n            TryVariant::Finally((f, finally_re_throw, finally_jump_index)) => {\n                let catch_handler = self.push_handler();\n                let error = self.register_allocator.alloc();\n                self.bytecode.emit_exception(error.variable());\n                self.bytecode.emit_store_true(finally_re_throw.variable());\n\n                let no_throw = self.jump();\n                self.patch_handler(catch_handler);\n\n                self.patch_jump(no_throw);\n                self.patch_jump(finally);\n\n                let finally_start = self.next_opcode_location();\n                self.jump_info\n                    .last_mut()\n                    .expect(\"there should be a try block\")\n                    .flags |= JumpControlInfoFlags::IN_FINALLY;\n                self.compile_finally_stmt(f);\n                let do_not_throw_exit = self.jump_if_false(&finally_re_throw);\n                self.bytecode.emit_throw(error.variable());\n                self.register_allocator.dealloc(error);\n                self.patch_jump(do_not_throw_exit);\n                self.pop_try_with_finally_control_info(finally_start);\n                self.register_allocator.dealloc(finally_re_throw);\n                self.register_allocator.dealloc(finally_jump_index);\n            }\n        }\n    }\n\n    pub(crate) fn compile_catch_stmt(&mut self, catch: &Catch, error: &Register, use_expr: bool) {\n        let outer_scope = self.push_declarative_scope(Some(catch.scope()));\n\n        if let Some(binding) = catch.parameter() {\n            match binding {\n                Binding::Identifier(ident) => {\n                    let ident = ident.to_js_string(self.interner());\n                    self.emit_binding(BindingOpcode::InitLexical, ident, error);\n                }\n                Binding::Pattern(pattern) => {\n                    self.compile_declaration_pattern(pattern, BindingOpcode::InitLexical, error);\n                }\n            }\n        }\n\n        self.compile_catch_finally_block(catch.block(), use_expr);\n\n        self.pop_declarative_scope(outer_scope);\n    }\n\n    pub(crate) fn compile_finally_stmt(&mut self, finally: &Finally) {\n        // TODO: We could probably remove the Get/SetAccumulatorFromStack if we check that there is no break/continues statements.\n        let value = self.register_allocator.alloc();\n        self.bytecode\n            .emit_set_register_from_accumulator(value.variable());\n        self.compile_catch_finally_block(finally.block(), false);\n        self.bytecode.emit_set_accumulator(value.variable());\n        self.register_allocator.dealloc(value);\n    }\n\n    /// Compile a catch or finally block.\n    ///\n    /// If the block contains a break or continue as the first statement,\n    /// the return value is set to undefined.\n    /// See the [ECMAScript reference][spec] for more information.\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-try-statement-runtime-semantics-evaluation\n    fn compile_catch_finally_block(&mut self, block: &Block, use_expr: bool) {\n        let mut b = block;\n\n        while let Some(statement) = b.statement_list().first() {\n            match statement {\n                StatementListItem::Statement(statement) => match statement.as_ref() {\n                    Statement::Break(_) | Statement::Continue(_) => {\n                        self.bytecode\n                            .emit_set_accumulator(CallFrame::undefined_register().variable());\n                        break;\n                    }\n                    Statement::Block(block) => b = block,\n                    _ => break,\n                },\n                StatementListItem::Declaration(_) => break,\n            }\n        }\n\n        self.compile_block(block, use_expr);\n    }\n}\n"
  },
  {
    "path": "core/engine/src/bytecompiler/statement/with.rs",
    "content": "use crate::bytecompiler::ByteCompiler;\nuse boa_ast::statement::With;\n\nimpl ByteCompiler<'_> {\n    /// Compile a [`With`] `boa_ast` node\n    pub(crate) fn compile_with(&mut self, with: &With, use_expr: bool) {\n        let object = self.register_allocator.alloc();\n        self.compile_expr(with.expression(), &object);\n\n        let outer_scope = self.lexical_scope.clone();\n        let _ = self.push_scope(with.scope());\n        self.bytecode\n            .emit_push_object_environment(object.variable());\n        self.register_allocator.dealloc(object);\n\n        let in_with = self.in_with;\n        self.in_with = true;\n        self.compile_stmt(with.statement(), use_expr, true);\n        self.in_with = in_with;\n\n        self.pop_scope();\n        self.lexical_scope = outer_scope;\n        self.bytecode.emit_pop_environment();\n    }\n}\n"
  },
  {
    "path": "core/engine/src/bytecompiler/utils.rs",
    "content": "use super::{ByteCompiler, Literal, Register};\nuse crate::{js_string, vm::GeneratorResumeKind};\n\nimpl ByteCompiler<'_> {\n    /// Closes an iterator\n    ///\n    /// This is equivalent to the [`IteratorClose`][iter] and [`AsyncIteratorClose`][async]\n    /// operations.\n    ///\n    /// Iterator Stack:\n    ///  - iterator **=>** \\<empty\\>\n    ///\n    /// [iter]: https://tc39.es/ecma262/#sec-iteratorclose\n    /// [async]: https://tc39.es/ecma262/#sec-asynciteratorclose\n    pub(super) fn iterator_close(&mut self, async_: bool) {\n        let value = self.register_allocator.alloc();\n        let called = self.register_allocator.alloc();\n        self.bytecode\n            .emit_iterator_return(value.variable(), called.variable());\n\n        // `iterator` didn't have a `return` method, is already done or is not on the iterator stack.\n        let early_exit = self.jump_if_false(&called);\n        self.register_allocator.dealloc(called);\n\n        if async_ {\n            self.bytecode.emit_await(value.variable());\n            let resume_kind = self.register_allocator.alloc();\n            self.pop_into_register(&resume_kind);\n            self.pop_into_register(&value);\n            self.generator_next(&value, &resume_kind);\n            self.register_allocator.dealloc(resume_kind);\n        }\n\n        self.bytecode.emit_is_object(value.variable());\n        let skip_throw = self.jump_if_true(&value);\n\n        self.register_allocator.dealloc(value);\n\n        let error_msg = self.get_or_insert_literal(Literal::String(js_string!(\n            \"inner result was not an object\"\n        )));\n        self.bytecode.emit_throw_new_type_error(error_msg.into());\n\n        self.patch_jump(skip_throw);\n        self.patch_jump(early_exit);\n    }\n\n    /// Closes all active iterators in the current [`CallFrame`][crate::vm::CallFrame].\n    pub(super) fn close_active_iterators(&mut self) {\n        let start = self.next_opcode_location();\n\n        let empty = self.register_allocator.alloc();\n        self.bytecode.emit_iterator_stack_empty(empty.variable());\n        let exit = self.jump_if_true(&empty);\n        self.register_allocator.dealloc(empty);\n\n        self.iterator_close(self.is_async_generator());\n        self.bytecode.emit_jump(start);\n        self.patch_jump(exit);\n    }\n\n    /// Yields from the current generator.\n    ///\n    /// This is equivalent to the [`Yield ( value )`][yield] operation from the spec.\n    ///\n    /// stack:\n    /// - value **=>** received\n    ///\n    /// [yield]: https://tc39.es/ecma262/#sec-yield\n    pub(super) fn r#yield(&mut self, value: &Register) {\n        let resume_kind = self.register_allocator.alloc();\n\n        // 1. Let generatorKind be GetGeneratorKind().\n        if self.is_async() {\n            // 2. If generatorKind is async, return ? AsyncGeneratorYield(? Await(value)).\n            self.bytecode.emit_await(value.variable());\n            self.pop_into_register(&resume_kind);\n            self.pop_into_register(value);\n            self.generator_next(value, &resume_kind);\n            self.async_generator_yield(value, &resume_kind);\n        } else {\n            // 3. Otherwise, return ? GeneratorYield(CreateIterResultObject(value, false)).\n            self.bytecode\n                .emit_create_iterator_result(value.variable(), false.into());\n            self.bytecode.emit_generator_yield(value.variable());\n            self.pop_into_register(&resume_kind);\n            self.pop_into_register(value);\n        }\n\n        self.generator_next(value, &resume_kind);\n        self.register_allocator.dealloc(resume_kind);\n    }\n\n    /// Yields from the current async generator.\n    ///\n    /// This is equivalent to the [`AsyncGeneratorYield ( value )`][async_yield] operation from the spec.\n    ///\n    /// stack:\n    /// - value **=>** `resume_kind`, received\n    ///\n    /// [async_yield]: https://tc39.es/ecma262/#sec-asyncgeneratoryield\n    pub(super) fn async_generator_yield(&mut self, value: &Register, resume_kind: &Register) {\n        self.bytecode.emit_async_generator_yield(value.variable());\n        self.pop_into_register(resume_kind);\n        self.pop_into_register(value);\n\n        let non_return_resume =\n            self.jump_if_not_resume_kind(GeneratorResumeKind::Return, resume_kind);\n\n        self.bytecode.emit_await(value.variable());\n        self.pop_into_register(resume_kind);\n        self.pop_into_register(value);\n\n        let non_normal_resume =\n            self.jump_if_not_resume_kind(GeneratorResumeKind::Normal, resume_kind);\n\n        self.emit_resume_kind(GeneratorResumeKind::Return, resume_kind);\n\n        self.patch_jump(non_normal_resume);\n        self.patch_jump(non_return_resume);\n    }\n}\n"
  },
  {
    "path": "core/engine/src/class.rs",
    "content": "//! Traits and structs for implementing native classes.\n//!\n//! Native classes are implemented through the [`Class`][class-trait] trait.\n//!\n//! # Examples\n//!\n//! ```\n//! # use boa_engine::{\n//! #    NativeFunction,\n//! #    property::Attribute,\n//! #    class::{Class, ClassBuilder},\n//! #    Context, JsResult, JsValue,\n//! #    JsArgs, Source, JsObject, js_str, js_string,\n//! #    JsNativeError, JsData,\n//! # };\n//! # use boa_gc::{Finalize, Trace};\n//! #\n//! // Can also be a struct containing `Trace` types.\n//! #[derive(Debug, Trace, Finalize, JsData)]\n//! enum Animal {\n//!     Cat,\n//!     Dog,\n//!     Other,\n//! }\n//!\n//! impl Class for Animal {\n//!     // we set the binding name of this function to be `\"Animal\"`.\n//!     const NAME: &'static str = \"Animal\";\n//!\n//!     // We set the length to `2` since we accept 2 arguments in the constructor.\n//!     const LENGTH: usize = 2;\n//!\n//!     // This is what is called when we do `new Animal()` to construct the inner data of the class.\n//!     // `_new_target` is the target of the `new` invocation, in this case the `Animal` constructor\n//!     // object.\n//!     fn data_constructor(_new_target: &JsValue, args: &[JsValue], context: &mut Context) -> JsResult<Self> {\n//!         // This is equivalent to `String(arg)`.\n//!         let kind = args.get_or_undefined(0).to_string(context)?;\n//!\n//!         let animal = match kind.to_std_string_escaped().as_str() {\n//!             \"cat\" => Self::Cat,\n//!             \"dog\" => Self::Dog,\n//!             _ => Self::Other,\n//!         };\n//!\n//!         Ok(animal)\n//!     }\n//!\n//!     // This is also called on instance construction, but it receives the object wrapping the\n//!     // native data as its `instance` argument.\n//!     fn object_constructor(\n//!         instance: &JsObject<Self>,\n//!         args: &[JsValue],\n//!         context: &mut Context,\n//!     ) -> JsResult<()> {\n//!         let age = args.get_or_undefined(1).to_number(context)?;\n//!\n//!         // Roughly equivalent to `this.age = Number(age)`.\n//!         instance.clone().upcast().set(js_string!(\"age\"), age, true, context)?;\n//!\n//!         Ok(())\n//!     }\n//!\n//!     /// This is where the class object is initialized.\n//!     fn init(class: &mut ClassBuilder) -> JsResult<()> {\n//!         class.method(\n//!             js_string!(\"speak\"),\n//!             0,\n//!             NativeFunction::from_fn_ptr(|this, _args, _ctx| {\n//!                 if let Some(object) = this.as_object() {\n//!                     if let Some(animal) = object.downcast_ref::<Animal>() {\n//!                         return Ok(match &*animal {\n//!                             Self::Cat => js_string!(\"meow\"),\n//!                             Self::Dog => js_string!(\"woof\"),\n//!                             Self::Other => js_string!(r\"¯\\_(ツ)_/¯\"),\n//!                         }.into());\n//!                     }\n//!                 }\n//!                 Err(JsNativeError::typ().with_message(\"invalid this for class method\").into())\n//!             }),\n//!         );\n//!         Ok(())\n//!     }\n//! }\n//!\n//! fn main() {\n//!     let mut context = Context::default();\n//!\n//!     context.register_global_class::<Animal>().unwrap();\n//!\n//!     let result = context.eval(Source::from_bytes(r#\"\n//!         let pet = new Animal(\"dog\", 3);\n//!\n//!         `My pet is ${pet.age} years old. Right, buddy? - ${pet.speak()}!`\n//!     \"#)).unwrap();\n//!\n//!     assert_eq!(\n//!         result.as_string().unwrap(),\n//!         js_str!(\"My pet is 3 years old. Right, buddy? - woof!\")\n//!     );\n//! }\n//! ```\n//!\n//! [class-trait]: ./trait.Class.html\n\nuse crate::{\n    Context, JsResult, JsValue,\n    context::intrinsics::StandardConstructor,\n    error::JsNativeError,\n    native_function::NativeFunction,\n    object::{ConstructorBuilder, FunctionBinding, JsFunction, JsObject, NativeObject, PROTOTYPE},\n    property::{Attribute, PropertyDescriptor, PropertyKey},\n};\n\n/// Native class.\n///\n/// See the [module-level documentation][self] for more details.\npub trait Class: NativeObject + Sized {\n    /// The binding name of this class.\n    const NAME: &'static str;\n    /// The amount of arguments this class' constructor takes. Default is `0`.\n    const LENGTH: usize = 0;\n    /// The property attributes of this class' constructor in the global object.\n    /// Default is `writable`, `enumerable`, `configurable`.\n    const ATTRIBUTES: Attribute = Attribute::all();\n\n    /// Initializes the properties and methods of this class.\n    fn init(class: &mut ClassBuilder<'_>) -> JsResult<()>;\n\n    /// Creates the internal data for an instance of this class.\n    fn data_constructor(\n        new_target: &JsValue,\n        args: &[JsValue],\n        context: &mut Context,\n    ) -> JsResult<Self>;\n\n    /// Initializes the properties of the constructed object for an instance of this class.\n    ///\n    /// Useful to initialize additional properties for the constructed object that aren't\n    /// stored inside the native data.\n    #[allow(unused_variables)] // Saves work when IDEs autocomplete trait impls.\n    fn object_constructor(\n        instance: &JsObject<Self>,\n        args: &[JsValue],\n        context: &mut Context,\n    ) -> JsResult<()> {\n        Ok(())\n    }\n\n    /// Creates a new [`JsObject`] with its internal data set to the result of calling\n    /// [`Class::data_constructor`] and [`Class::object_constructor`].\n    ///\n    /// # Errors\n    ///\n    /// - Throws an error if `new_target` is undefined.\n    /// - Throws an error if this class is not registered in `new_target`'s realm.\n    ///   See [`Context::register_global_class`].\n    ///\n    /// <div class=\"warning\">\n    /// Overriding this method could be useful for certain usages, but incorrectly implementing this\n    /// could lead to weird errors like missing inherited methods or incorrect internal data.\n    /// </div>\n    fn construct(\n        new_target: &JsValue,\n        args: &[JsValue],\n        context: &mut Context,\n    ) -> JsResult<JsObject> {\n        if new_target.is_undefined() {\n            return Err(JsNativeError::typ()\n                .with_message(format!(\n                    \"cannot call constructor of native class `{}` without new\",\n                    Self::NAME\n                ))\n                .into());\n        }\n\n        let prototype = 'proto: {\n            let realm = if let Some(constructor) = new_target.as_object() {\n                if let Some(proto) = constructor.get(PROTOTYPE, context)?.as_object() {\n                    break 'proto proto.clone();\n                }\n                constructor.get_function_realm(context)?\n            } else {\n                context.realm().clone()\n            };\n            realm\n                .get_class::<Self>()\n                .ok_or_else(|| {\n                    JsNativeError::typ().with_message(format!(\n                        \"could not find native class `{}` in the map of registered classes\",\n                        Self::NAME\n                    ))\n                })?\n                .prototype()\n        };\n\n        let data = Self::data_constructor(new_target, args, context)?;\n\n        let object =\n            JsObject::from_proto_and_data_with_shared_shape(context.root_shape(), prototype, data);\n\n        Self::object_constructor(&object, args, context)?;\n\n        Ok(object.upcast())\n    }\n\n    /// Constructs an instance of this class from its inner native data.\n    ///\n    /// Note that the default implementation won't call [`Class::data_constructor`], but it will\n    /// call [`Class::object_constructor`] with no arguments.\n    ///\n    /// # Errors\n    /// - Throws an error if this class is not registered in the context's realm. See\n    ///   [`Context::register_global_class`].\n    ///\n    /// <div class=\"warning\">\n    /// Overriding this method could be useful for certain usages, but incorrectly implementing this\n    /// could lead to weird errors like missing inherited methods or incorrect internal data.\n    /// </div>\n    fn from_data(data: Self, context: &mut Context) -> JsResult<JsObject> {\n        let prototype = context\n            .get_global_class::<Self>()\n            .ok_or_else(|| {\n                JsNativeError::typ().with_message(format!(\n                    \"could not find native class `{}` in the map of registered classes\",\n                    Self::NAME\n                ))\n            })?\n            .prototype();\n\n        let object =\n            JsObject::from_proto_and_data_with_shared_shape(context.root_shape(), prototype, data);\n\n        Self::object_constructor(&object, &[], context)?;\n\n        Ok(object.upcast())\n    }\n}\n\n/// Class builder which allows adding methods and static methods to the class.\n#[derive(Debug)]\npub struct ClassBuilder<'ctx> {\n    builder: ConstructorBuilder<'ctx>,\n}\n\nimpl<'ctx> ClassBuilder<'ctx> {\n    /// Create a new `ClassBuilder` from a [`Class`] type.\n    pub fn new<T>(context: &'ctx mut Context) -> Self\n    where\n        T: Class,\n    {\n        let mut builder = ConstructorBuilder::new(\n            context,\n            NativeFunction::from_fn_ptr(|t, a, c| T::construct(t, a, c).map(JsValue::from)),\n        );\n        builder.name(T::NAME);\n        builder.length(T::LENGTH);\n        Self { builder }\n    }\n\n    /// Create the [`StandardConstructor`] from this class builder.\n    #[must_use]\n    pub fn build(self) -> StandardConstructor {\n        self.builder.build()\n    }\n\n    /// Add a method to the class.\n    ///\n    /// It is added to `prototype`.\n    pub fn method<N>(&mut self, name: N, length: usize, function: NativeFunction) -> &mut Self\n    where\n        N: Into<FunctionBinding>,\n    {\n        self.builder.method(function, name, length);\n        self\n    }\n\n    /// Add a static method to the class.\n    ///\n    /// It is added to class object itself.\n    pub fn static_method<N>(\n        &mut self,\n        name: N,\n        length: usize,\n        function: NativeFunction,\n    ) -> &mut Self\n    where\n        N: Into<FunctionBinding>,\n    {\n        self.builder.static_method(function, name, length);\n        self\n    }\n\n    /// Add a data property to the class, with the specified attribute.\n    ///\n    /// It is added to `prototype`.\n    pub fn property<K, V>(&mut self, key: K, value: V, attribute: Attribute) -> &mut Self\n    where\n        K: Into<PropertyKey>,\n        V: Into<JsValue>,\n    {\n        self.builder.property(key, value, attribute);\n        self\n    }\n\n    /// Add a static data property to the class, with the specified attribute.\n    ///\n    /// It is added to class object itself.\n    pub fn static_property<K, V>(&mut self, key: K, value: V, attribute: Attribute) -> &mut Self\n    where\n        K: Into<PropertyKey>,\n        V: Into<JsValue>,\n    {\n        self.builder.static_property(key, value, attribute);\n        self\n    }\n\n    /// Add an accessor property to the class, with the specified attribute.\n    ///\n    /// It is added to `prototype`.\n    pub fn accessor<K>(\n        &mut self,\n        key: K,\n        get: Option<JsFunction>,\n        set: Option<JsFunction>,\n        attribute: Attribute,\n    ) -> &mut Self\n    where\n        K: Into<PropertyKey>,\n    {\n        self.builder.accessor(key, get, set, attribute);\n        self\n    }\n\n    /// Add a static accessor property to the class, with the specified attribute.\n    ///\n    /// It is added to class object itself.\n    pub fn static_accessor<K>(\n        &mut self,\n        key: K,\n        get: Option<JsFunction>,\n        set: Option<JsFunction>,\n        attribute: Attribute,\n    ) -> &mut Self\n    where\n        K: Into<PropertyKey>,\n    {\n        self.builder.static_accessor(key, get, set, attribute);\n        self\n    }\n\n    /// Add a property descriptor to the class, with the specified attribute.\n    ///\n    /// It is added to `prototype`.\n    pub fn property_descriptor<K, P>(&mut self, key: K, property: P) -> &mut Self\n    where\n        K: Into<PropertyKey>,\n        P: Into<PropertyDescriptor>,\n    {\n        self.builder.property_descriptor(key, property);\n        self\n    }\n\n    /// Add a static property descriptor to the class, with the specified attribute.\n    ///\n    /// It is added to class object itself.\n    pub fn static_property_descriptor<K, P>(&mut self, key: K, property: P) -> &mut Self\n    where\n        K: Into<PropertyKey>,\n        P: Into<PropertyDescriptor>,\n    {\n        self.builder.static_property_descriptor(key, property);\n        self\n    }\n\n    /// Return the current context.\n    #[inline]\n    pub fn context(&mut self) -> &mut Context {\n        self.builder.context()\n    }\n}\n"
  },
  {
    "path": "core/engine/src/context/hooks.rs",
    "content": "use crate::{\n    Context, JsResult, JsString, JsValue,\n    builtins::{Promise, promise::OperationType},\n    context::intrinsics::Intrinsics,\n    job::JobCallback,\n    object::{JsFunction, JsObject},\n    realm::Realm,\n};\nuse time::{OffsetDateTime, UtcOffset};\n\n/// [`Host Hooks`] customizable by the host code or engine.\n///\n/// Every hook contains on its `Requirements` section the spec requirements\n/// that the hook must abide to for spec compliance.\n///\n/// # Usage\n///\n/// Implement the trait for a custom struct (maybe with additional state), overriding the methods that\n/// need to be redefined:\n///\n/// ```\n/// use boa_engine::{\n///     JsNativeError, JsResult, JsString, Source,\n///     context::{Context, ContextBuilder, HostHooks},\n///     realm::Realm,\n/// };\n/// use std::rc::Rc;\n///\n/// struct Hooks;\n///\n/// impl HostHooks for Hooks {\n///     fn ensure_can_compile_strings(\n///         &self,\n///         _realm: Realm,\n///         _parameters: &[JsString],\n///         _body: &JsString,\n///         _direct: bool,\n///         _context: &mut Context,\n///     ) -> JsResult<()> {\n///         Err(JsNativeError::typ()\n///             .with_message(\"eval calls not available\")\n///             .into())\n///     }\n/// }\n///\n/// let context = &mut ContextBuilder::new()\n///     .host_hooks(Rc::new(Hooks))\n///     .build()\n///     .unwrap();\n/// let result = context.eval(Source::from_bytes(r#\"eval(\"let a = 5\")\"#));\n/// assert!(\n///     result\n///         .unwrap_err()\n///         .to_string()\n///         .starts_with(\"TypeError: eval calls not available\")\n/// );\n/// ```\n///\n/// [`Host Hooks`]: https://tc39.es/ecma262/#sec-host-hooks-summary\npub trait HostHooks {\n    /// [`HostMakeJobCallback ( callback )`][spec]\n    ///\n    /// # Requirements\n    ///\n    /// - It must return a `JobCallback` Record whose `[[Callback]]` field is `callback`.\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-hostmakejobcallback\n    fn make_job_callback(&self, callback: JsFunction, _context: &mut Context) -> JobCallback {\n        // The default implementation of HostMakeJobCallback performs the following steps when called:\n\n        // 1. Return the JobCallback Record { [[Callback]]: callback, [[HostDefined]]: empty }.\n        JobCallback::new(callback, ())\n    }\n\n    /// [`HostCallJobCallback ( jobCallback, V, argumentsList )`][spec]\n    ///\n    /// # Requirements\n    ///\n    /// - It must perform and return the result of `Call(jobCallback.[[Callback]], V, argumentsList)`.\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-hostcalljobcallback\n    #[cfg_attr(feature = \"native-backtrace\", track_caller)]\n    fn call_job_callback(\n        &self,\n        job: &JobCallback,\n        this: &JsValue,\n        args: &[JsValue],\n        context: &mut Context,\n    ) -> JsResult<JsValue> {\n        // The default implementation of HostCallJobCallback performs the following steps when called:\n\n        // 1. Assert: IsCallable(jobCallback.[[Callback]]) is true.\n        // already asserted by `Call`.\n        // 2. Return ? Call(jobCallback.[[Callback]], V, argumentsList).\n        job.callback().call(this, args, context)\n    }\n\n    /// [`HostPromiseRejectionTracker ( promise, operation )`][spec]\n    ///\n    /// # Requirements\n    ///\n    /// - It must complete normally (i.e. not return an abrupt completion). This is already\n    ///   ensured by the return type.\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-host-promise-rejection-tracker\n    fn promise_rejection_tracker(\n        &self,\n        _promise: &JsObject<Promise>,\n        _operation: OperationType,\n        _context: &mut Context,\n    ) {\n        // The default implementation of HostPromiseRejectionTracker is to return unused.\n    }\n\n    /// [`HostEnsureCanCompileStrings ( calleeRealm, parameterStrings, bodyString, direct )`][spec]\n    ///\n    /// # Requirements\n    ///\n    /// - If the returned Completion Record is a normal completion, it must be a normal completion\n    ///   containing unused. This is already ensured by the return type.\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-hostensurecancompilestrings\n    fn ensure_can_compile_strings(\n        &self,\n        _realm: Realm,\n        _parameters: &[JsString],\n        _body: &JsString,\n        _direct: bool,\n        _context: &mut Context,\n    ) -> JsResult<()> {\n        // The default implementation of HostEnsureCanCompileStrings is to return NormalCompletion(unused).\n        Ok(())\n    }\n\n    /// [`HostHasSourceTextAvailable ( func )`][spec]\n    ///\n    /// # Requirements\n    ///\n    /// - It must be deterministic with respect to its parameters. Each time it is called with a\n    ///   specific `func` as its argument, it must return the same result.\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-hosthassourcetextavailable\n    fn has_source_text_available(&self, _function: &JsFunction, _context: &mut Context) -> bool {\n        // The default implementation of HostHasSourceTextAvailable is to return true.\n        true\n    }\n\n    /// [`HostEnsureCanAddPrivateElement ( O )`][spec]\n    ///\n    /// # Requirements\n    ///\n    /// - If `O` is not a host-defined exotic object, this abstract operation must return\n    ///   `NormalCompletion(unused)` and perform no other steps.\n    /// - Any two calls of this abstract operation with the same argument must return the same kind\n    ///   of *Completion Record*.\n    /// - This abstract operation should only be overridden by ECMAScript hosts that are web browsers.\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-hostensurecanaddprivateelement\n    fn ensure_can_add_private_element(\n        &self,\n        _o: &JsObject,\n        _context: &mut Context,\n    ) -> JsResult<()> {\n        Ok(())\n    }\n\n    /// Creates the global object of a new [`Context`] from the initial intrinsics.\n    ///\n    /// Equivalent to the step 7 of [`InitializeHostDefinedRealm ( )`][ihdr].\n    ///\n    /// [ihdr]: https://tc39.es/ecma262/#sec-initializehostdefinedrealm\n    fn create_global_object(&self, intrinsics: &Intrinsics) -> JsObject {\n        JsObject::with_object_proto(intrinsics)\n    }\n\n    /// Creates the global `this` of a new [`Context`] from the initial intrinsics.\n    ///\n    /// Equivalent to the step 8 of [`InitializeHostDefinedRealm ( )`][ihdr].\n    ///\n    /// [ihdr]: https://tc39.es/ecma262/#sec-initializehostdefinedrealm\n    fn create_global_this(&self, _intrinsics: &Intrinsics) -> Option<JsObject> {\n        None\n    }\n\n    /// Gets the current UTC time of the host, in milliseconds since epoch.\n    ///\n    /// Defaults to using [`OffsetDateTime::now_utc`] on all targets,\n    /// which can cause panics if the target doesn't support [`SystemTime::now`][time].\n    ///\n    /// [time]: std::time::SystemTime::now\n    #[deprecated(\n        since = \"0.21.0\",\n        note = \"Use `context.clock().now().millis_since_epoch()` instead\"\n    )]\n    fn utc_now(&self) -> i64 {\n        let now = OffsetDateTime::now_utc();\n        now.unix_timestamp() * 1000 + i64::from(now.millisecond())\n    }\n\n    /// Returns the offset of the local timezone to the `utc` timezone in seconds.\n    fn local_timezone_offset_seconds(&self, unix_time_seconds: i64) -> i32 {\n        OffsetDateTime::from_unix_timestamp(unix_time_seconds)\n            .ok()\n            .and_then(|t| UtcOffset::local_offset_at(t).ok())\n            .map_or(0, UtcOffset::whole_seconds)\n    }\n\n    /// Gets the maximum size in bits that can be allocated for an `ArrayBuffer` or a\n    /// `SharedArrayBuffer`.\n    ///\n    /// This hook will be called before any buffer allocation, which allows to dynamically change\n    /// the maximum size at runtime. By default, this is set to 1.5GiB per the recommendations of the\n    /// [specification]:\n    ///\n    /// > If a host is multi-tenanted (i.e. it runs many ECMAScript applications simultaneously),\n    /// > such as a web browser, and its implementations choose to implement in-place growth by reserving\n    /// > virtual memory, we recommend that both 32-bit and 64-bit implementations throw for values of\n    /// > \"`maxByteLength`\" ≥ 1GiB to 1.5GiB. This is to reduce the likelihood a single application can\n    /// > exhaust the virtual memory address space and to reduce interoperability risk.\n    ///\n    ///\n    /// [specification]: https://tc39.es/ecma262/#sec-resizable-arraybuffer-guidelines\n    fn max_buffer_size(&self, _context: &mut Context) -> u64 {\n        1_610_612_736 // 1.5 GiB\n    }\n}\n\n/// Default implementation of [`HostHooks`], which doesn't carry any state.\n#[derive(Debug, Clone, Copy)]\npub struct DefaultHooks;\n\nimpl HostHooks for DefaultHooks {}\n"
  },
  {
    "path": "core/engine/src/context/icu.rs",
    "content": "use std::{cell::OnceCell, fmt::Debug};\n\nuse icu_casemap::CaseMapper;\nuse icu_locale::{LocaleCanonicalizer, LocaleExpander};\nuse icu_normalizer::{ComposingNormalizer, DecomposingNormalizer};\nuse icu_provider::prelude::*;\nuse serde::Deserialize;\nuse thiserror::Error;\nuse yoke::Yokeable;\nuse zerofrom::ZeroFrom;\n\nuse crate::{JsError, JsNativeError, builtins::string::StringNormalizers};\n\n/// Error thrown when the engine cannot initialize the ICU4X utilities from a data provider.\n#[derive(Debug, Error, Copy, Clone)]\npub enum IcuError {\n    /// Failed to create the internationalization utilities.\n    #[error(\"could not construct the internationalization utilities\")]\n    DataError(#[from] DataError),\n}\n\nimpl From<IcuError> for JsNativeError {\n    #[cfg_attr(feature = \"native-backtrace\", track_caller)]\n    fn from(value: IcuError) -> Self {\n        JsNativeError::typ().with_message(value.to_string())\n    }\n}\n\nimpl From<IcuError> for JsError {\n    #[cfg_attr(feature = \"native-backtrace\", track_caller)]\n    fn from(value: IcuError) -> Self {\n        JsNativeError::from(value).into()\n    }\n}\n\n/// Custom [`DataProvider`] for `Intl` that caches some utilities.\npub(crate) struct IntlProvider {\n    inner_provider: Box<dyn DynamicDryDataProvider<BufferMarker>>,\n    locale_canonicalizer: OnceCell<LocaleCanonicalizer>,\n    locale_expander: OnceCell<LocaleExpander>,\n    string_normalizers: OnceCell<StringNormalizers>,\n    case_mapper: OnceCell<CaseMapper>,\n}\n\nimpl<M> DataProvider<M> for IntlProvider\nwhere\n    M: DataMarker + 'static,\n    for<'de> <M::DataStruct as Yokeable<'de>>::Output: Deserialize<'de> + Clone,\n    M::DataStruct: ZeroFrom<'static, M::DataStruct>,\n{\n    fn load(&self, req: DataRequest<'_>) -> Result<DataResponse<M>, DataError> {\n        self.inner_provider\n            .as_deserializing()\n            .load_data(M::INFO, req)\n    }\n}\n\nimpl<M> DryDataProvider<M> for IntlProvider\nwhere\n    M: DataMarker + 'static,\n    for<'de> <M::DataStruct as Yokeable<'de>>::Output: Deserialize<'de> + Clone,\n    M::DataStruct: ZeroFrom<'static, M::DataStruct>,\n{\n    fn dry_load(&self, req: DataRequest<'_>) -> Result<DataResponseMetadata, DataError> {\n        self.inner_provider.dry_load_data(M::INFO, req)\n    }\n}\n\nimpl Debug for IntlProvider {\n    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {\n        f.debug_struct(\"Icu\")\n            .field(\"locale_canonicalizer\", &self.locale_canonicalizer)\n            .field(\"locale_expander\", &self.locale_expander)\n            .field(\"string_normalizers\", &self.string_normalizers)\n            .field(\"string_normalizercase_mapper\", &self.case_mapper)\n            .finish_non_exhaustive()\n    }\n}\n\nimpl IntlProvider {\n    /// Creates a new [`IntlProvider`] from a [`DynamicDryDataProvider<BufferMarker>`].\n    ///\n    /// # Errors\n    ///\n    /// Returns an error if any of the tools required cannot be constructed.\n    pub(crate) fn try_new_buffer(\n        provider: impl DynamicDryDataProvider<BufferMarker> + 'static,\n    ) -> IntlProvider {\n        Self {\n            locale_canonicalizer: OnceCell::new(),\n            locale_expander: OnceCell::new(),\n            string_normalizers: OnceCell::new(),\n            case_mapper: OnceCell::new(),\n            inner_provider: Box::new(provider),\n        }\n    }\n\n    /// Gets the [`LocaleCanonicalizer`] tool.\n    pub(crate) fn locale_canonicalizer(&self) -> Result<&LocaleCanonicalizer, IcuError> {\n        if let Some(lc) = self.locale_canonicalizer.get() {\n            return Ok(lc);\n        }\n        let expander = self.locale_expander()?.clone();\n        let lc = LocaleCanonicalizer::try_new_with_expander_with_buffer_provider(\n            &self.inner_provider,\n            expander,\n        )?;\n        Ok(self.locale_canonicalizer.get_or_init(|| lc))\n    }\n\n    /// Gets the [`LocaleExpander`] tool.\n    pub(crate) fn locale_expander(&self) -> Result<&LocaleExpander, IcuError> {\n        if let Some(le) = self.locale_expander.get() {\n            return Ok(le);\n        }\n\n        let le = LocaleExpander::try_new_extended_with_buffer_provider(&self.inner_provider)?;\n        Ok(self.locale_expander.get_or_init(|| le))\n    }\n\n    /// Gets the [`StringNormalizers`] tools.\n    pub(crate) fn string_normalizers(&self) -> Result<&StringNormalizers, IcuError> {\n        if let Some(sn) = self.string_normalizers.get() {\n            return Ok(sn);\n        }\n        let sn = StringNormalizers {\n            nfc: ComposingNormalizer::try_new_nfc_with_buffer_provider(&self.inner_provider)?,\n            nfkc: ComposingNormalizer::try_new_nfkc_with_buffer_provider(&self.inner_provider)?,\n            nfd: DecomposingNormalizer::try_new_nfd_with_buffer_provider(&self.inner_provider)?,\n            nfkd: DecomposingNormalizer::try_new_nfkd_with_buffer_provider(&self.inner_provider)?,\n        };\n        Ok(self.string_normalizers.get_or_init(|| sn))\n    }\n\n    /// Gets the [`CaseMapper`] tool.\n    pub(crate) fn case_mapper(&self) -> Result<&CaseMapper, IcuError> {\n        if let Some(cm) = self.case_mapper.get() {\n            return Ok(cm);\n        }\n        let cm = CaseMapper::try_new_with_buffer_provider(&self.inner_provider)?;\n\n        Ok(self.case_mapper.get_or_init(|| cm))\n    }\n\n    /// Gets the inner provider.\n    pub(crate) fn erased_provider(&self) -> &dyn DynamicDryDataProvider<BufferMarker> {\n        &self.inner_provider\n    }\n}\n"
  },
  {
    "path": "core/engine/src/context/intrinsics.rs",
    "content": "//! Data structures that contain intrinsic objects and constructors.\n\nuse boa_gc::{Finalize, Trace};\n\nuse crate::{\n    JsSymbol,\n    builtins::{Array, OrdinaryObject, iterable::IteratorPrototypes, uri::UriFunctions},\n    js_string,\n    object::{\n        CONSTRUCTOR, JsFunction, JsObject, Object, PROTOTYPE,\n        internal_methods::immutable_prototype::IMMUTABLE_PROTOTYPE_EXOTIC_INTERNAL_METHODS,\n        shape::{RootShape, shared_shape::template::ObjectTemplate},\n    },\n    property::{Attribute, PropertyKey},\n};\n\n#[cfg(feature = \"intl\")]\nuse crate::builtins::intl::Intl;\n\n/// The intrinsic objects and constructors.\n///\n/// `Intrinsics` is internally stored using a `Gc`, which makes it cheapily cloneable\n/// for multiple references to the same set of intrinsic objects.\n#[derive(Debug, Trace, Finalize)]\npub struct Intrinsics {\n    /// Cached standard constructors\n    pub(super) constructors: StandardConstructors,\n    /// Cached intrinsic objects\n    pub(super) objects: IntrinsicObjects,\n    /// Cached object templates.\n    pub(super) templates: ObjectTemplates,\n}\n\nimpl Intrinsics {\n    /// Creates a new set of uninitialized intrinsics.\n    ///\n    /// Creates all the required empty objects for every intrinsic in this realm.\n    ///\n    /// To initialize all the intrinsics with their spec properties, see [`Realm::initialize`].\n    ///\n    /// [`Realm::initialize`]: crate::realm::Realm::initialize\n    pub(crate) fn uninit(root_shape: &RootShape) -> Option<Self> {\n        let constructors = StandardConstructors::default();\n        let templates = ObjectTemplates::new(root_shape, &constructors);\n\n        Some(Self {\n            constructors,\n            objects: IntrinsicObjects::uninit()?,\n            templates,\n        })\n    }\n\n    /// Return the cached intrinsic objects.\n    #[inline]\n    #[must_use]\n    pub const fn objects(&self) -> &IntrinsicObjects {\n        &self.objects\n    }\n\n    /// Return the cached standard constructors.\n    #[inline]\n    #[must_use]\n    pub const fn constructors(&self) -> &StandardConstructors {\n        &self.constructors\n    }\n\n    pub(crate) const fn templates(&self) -> &ObjectTemplates {\n        &self.templates\n    }\n}\n\n/// Stores a constructor (such as `Object`) and its corresponding prototype.\n#[derive(Debug, Trace, Finalize, Clone)]\npub struct StandardConstructor {\n    constructor: JsFunction,\n    prototype: JsObject,\n}\n\nimpl Default for StandardConstructor {\n    fn default() -> Self {\n        Self {\n            constructor: JsFunction::empty_intrinsic_function(true),\n            prototype: JsObject::with_null_proto(),\n        }\n    }\n}\n\nimpl StandardConstructor {\n    /// Creates a new `StandardConstructor` from the constructor and the prototype.\n    pub(crate) fn new(constructor: JsFunction, prototype: JsObject) -> Self {\n        Self {\n            constructor,\n            prototype,\n        }\n    }\n\n    /// Build a constructor with a defined prototype.\n    fn with_prototype(prototype: JsObject) -> Self {\n        Self {\n            constructor: JsFunction::empty_intrinsic_function(true),\n            prototype,\n        }\n    }\n\n    /// Return the prototype of the constructor object.\n    ///\n    /// This is the same as `Object.prototype`, `Array.prototype`, etc.\n    #[inline]\n    #[must_use]\n    pub fn prototype(&self) -> JsObject {\n        self.prototype.clone()\n    }\n\n    /// Return the constructor object.\n    ///\n    /// This is the same as `Object`, `Array`, etc.\n    #[inline]\n    #[must_use]\n    pub fn constructor(&self) -> JsObject {\n        self.constructor.clone().into()\n    }\n}\n\n/// Cached core standard constructors.\n#[derive(Debug, Trace, Finalize)]\npub struct StandardConstructors {\n    object: StandardConstructor,\n    proxy: StandardConstructor,\n    date: StandardConstructor,\n    function: StandardConstructor,\n    async_function: StandardConstructor,\n    generator_function: StandardConstructor,\n    async_generator_function: StandardConstructor,\n    array: StandardConstructor,\n    bigint: StandardConstructor,\n    number: StandardConstructor,\n    boolean: StandardConstructor,\n    string: StandardConstructor,\n    regexp: StandardConstructor,\n    symbol: StandardConstructor,\n    error: StandardConstructor,\n    type_error: StandardConstructor,\n    reference_error: StandardConstructor,\n    range_error: StandardConstructor,\n    syntax_error: StandardConstructor,\n    eval_error: StandardConstructor,\n    uri_error: StandardConstructor,\n    aggregate_error: StandardConstructor,\n    map: StandardConstructor,\n    set: StandardConstructor,\n    typed_array: StandardConstructor,\n    typed_int8_array: StandardConstructor,\n    typed_uint8_array: StandardConstructor,\n    typed_uint8clamped_array: StandardConstructor,\n    typed_int16_array: StandardConstructor,\n    typed_uint16_array: StandardConstructor,\n    typed_int32_array: StandardConstructor,\n    typed_uint32_array: StandardConstructor,\n    typed_bigint64_array: StandardConstructor,\n    typed_biguint64_array: StandardConstructor,\n    #[cfg(feature = \"float16\")]\n    typed_float16_array: StandardConstructor,\n    typed_float32_array: StandardConstructor,\n    typed_float64_array: StandardConstructor,\n    array_buffer: StandardConstructor,\n    shared_array_buffer: StandardConstructor,\n    data_view: StandardConstructor,\n    date_time_format: StandardConstructor,\n    promise: StandardConstructor,\n    weak_ref: StandardConstructor,\n    weak_map: StandardConstructor,\n    weak_set: StandardConstructor,\n    iterator: StandardConstructor,\n    finalization_registry: StandardConstructor,\n    #[cfg(feature = \"intl\")]\n    collator: StandardConstructor,\n    #[cfg(feature = \"intl\")]\n    list_format: StandardConstructor,\n    #[cfg(feature = \"intl\")]\n    locale: StandardConstructor,\n    #[cfg(feature = \"intl\")]\n    segmenter: StandardConstructor,\n    #[cfg(feature = \"intl\")]\n    plural_rules: StandardConstructor,\n    #[cfg(feature = \"intl\")]\n    number_format: StandardConstructor,\n    #[cfg(feature = \"temporal\")]\n    instant: StandardConstructor,\n    #[cfg(feature = \"temporal\")]\n    plain_date_time: StandardConstructor,\n    #[cfg(feature = \"temporal\")]\n    plain_date: StandardConstructor,\n    #[cfg(feature = \"temporal\")]\n    plain_time: StandardConstructor,\n    #[cfg(feature = \"temporal\")]\n    plain_year_month: StandardConstructor,\n    #[cfg(feature = \"temporal\")]\n    plain_month_day: StandardConstructor,\n    #[cfg(feature = \"temporal\")]\n    time_zone: StandardConstructor,\n    #[cfg(feature = \"temporal\")]\n    duration: StandardConstructor,\n    #[cfg(feature = \"temporal\")]\n    zoned_date_time: StandardConstructor,\n    #[cfg(feature = \"temporal\")]\n    calendar: StandardConstructor,\n}\n\nimpl Default for StandardConstructors {\n    fn default() -> Self {\n        Self {\n            object: StandardConstructor::with_prototype(JsObject::from_object_and_vtable(\n                Object::<OrdinaryObject>::default(),\n                &IMMUTABLE_PROTOTYPE_EXOTIC_INTERNAL_METHODS,\n            )),\n            async_generator_function: StandardConstructor::default(),\n            proxy: StandardConstructor::default(),\n            date: StandardConstructor::default(),\n            function: StandardConstructor {\n                constructor: JsFunction::empty_intrinsic_function(true),\n                prototype: JsFunction::empty_intrinsic_function(false).into(),\n            },\n            async_function: StandardConstructor::default(),\n            generator_function: StandardConstructor::default(),\n            array: StandardConstructor::with_prototype(JsObject::from_proto_and_data(None, Array)),\n            bigint: StandardConstructor::default(),\n            number: StandardConstructor::with_prototype(JsObject::from_proto_and_data(None, 0.0)),\n            boolean: StandardConstructor::with_prototype(JsObject::from_proto_and_data(\n                None, false,\n            )),\n            string: StandardConstructor::with_prototype(JsObject::from_proto_and_data(\n                None,\n                js_string!(),\n            )),\n            regexp: StandardConstructor::default(),\n            symbol: StandardConstructor::default(),\n            error: StandardConstructor::default(),\n            type_error: StandardConstructor::default(),\n            reference_error: StandardConstructor::default(),\n            range_error: StandardConstructor::default(),\n            syntax_error: StandardConstructor::default(),\n            eval_error: StandardConstructor::default(),\n            uri_error: StandardConstructor::default(),\n            aggregate_error: StandardConstructor::default(),\n            map: StandardConstructor::default(),\n            set: StandardConstructor::default(),\n            typed_array: StandardConstructor::default(),\n            typed_int8_array: StandardConstructor::default(),\n            typed_uint8_array: StandardConstructor::default(),\n            typed_uint8clamped_array: StandardConstructor::default(),\n            typed_int16_array: StandardConstructor::default(),\n            typed_uint16_array: StandardConstructor::default(),\n            typed_int32_array: StandardConstructor::default(),\n            typed_uint32_array: StandardConstructor::default(),\n            typed_bigint64_array: StandardConstructor::default(),\n            typed_biguint64_array: StandardConstructor::default(),\n            #[cfg(feature = \"float16\")]\n            typed_float16_array: StandardConstructor::default(),\n            typed_float32_array: StandardConstructor::default(),\n            typed_float64_array: StandardConstructor::default(),\n            array_buffer: StandardConstructor::default(),\n            shared_array_buffer: StandardConstructor::default(),\n            data_view: StandardConstructor::default(),\n            date_time_format: StandardConstructor::default(),\n            promise: StandardConstructor::default(),\n            weak_ref: StandardConstructor::default(),\n            weak_map: StandardConstructor::default(),\n            weak_set: StandardConstructor::default(),\n            iterator: StandardConstructor::default(),\n            finalization_registry: StandardConstructor::default(),\n            #[cfg(feature = \"intl\")]\n            collator: StandardConstructor::default(),\n            #[cfg(feature = \"intl\")]\n            list_format: StandardConstructor::default(),\n            #[cfg(feature = \"intl\")]\n            locale: StandardConstructor::default(),\n            #[cfg(feature = \"intl\")]\n            segmenter: StandardConstructor::default(),\n            #[cfg(feature = \"intl\")]\n            plural_rules: StandardConstructor::default(),\n            #[cfg(feature = \"intl\")]\n            number_format: StandardConstructor::default(),\n            #[cfg(feature = \"temporal\")]\n            instant: StandardConstructor::default(),\n            #[cfg(feature = \"temporal\")]\n            plain_date_time: StandardConstructor::default(),\n            #[cfg(feature = \"temporal\")]\n            plain_date: StandardConstructor::default(),\n            #[cfg(feature = \"temporal\")]\n            plain_time: StandardConstructor::default(),\n            #[cfg(feature = \"temporal\")]\n            plain_year_month: StandardConstructor::default(),\n            #[cfg(feature = \"temporal\")]\n            plain_month_day: StandardConstructor::default(),\n            #[cfg(feature = \"temporal\")]\n            time_zone: StandardConstructor::default(),\n            #[cfg(feature = \"temporal\")]\n            duration: StandardConstructor::default(),\n            #[cfg(feature = \"temporal\")]\n            zoned_date_time: StandardConstructor::default(),\n            #[cfg(feature = \"temporal\")]\n            calendar: StandardConstructor::default(),\n        }\n    }\n}\n\nimpl StandardConstructors {\n    /// Returns the `AsyncGeneratorFunction` constructor.\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-asyncgeneratorfunction-constructor\n    #[inline]\n    #[must_use]\n    pub const fn async_generator_function(&self) -> &StandardConstructor {\n        &self.async_generator_function\n    }\n\n    /// Returns the `Object` constructor.\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-object-constructor\n    #[inline]\n    #[must_use]\n    pub const fn object(&self) -> &StandardConstructor {\n        &self.object\n    }\n\n    /// Returns the `Proxy` constructor.\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-proxy-constructor\n    #[inline]\n    #[must_use]\n    pub const fn proxy(&self) -> &StandardConstructor {\n        &self.proxy\n    }\n\n    /// Returns the `Date` constructor.\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-date-constructor\n    #[inline]\n    #[must_use]\n    pub const fn date(&self) -> &StandardConstructor {\n        &self.date\n    }\n\n    /// Returns the `Function` constructor.\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-function-constructor\n    #[inline]\n    #[must_use]\n    pub const fn function(&self) -> &StandardConstructor {\n        &self.function\n    }\n\n    /// Returns the `AsyncFunction` constructor.\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-async-function-constructor\n    #[inline]\n    #[must_use]\n    pub const fn async_function(&self) -> &StandardConstructor {\n        &self.async_function\n    }\n\n    /// Returns the `GeneratorFunction` constructor.\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-generatorfunction-constructor\n    #[inline]\n    #[must_use]\n    pub const fn generator_function(&self) -> &StandardConstructor {\n        &self.generator_function\n    }\n\n    /// Returns the `Array` constructor.\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-array-constructor\n    #[inline]\n    #[must_use]\n    pub const fn array(&self) -> &StandardConstructor {\n        &self.array\n    }\n\n    /// Returns the `BigInt` constructor.\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-bigint-constructor\n    #[inline]\n    #[must_use]\n    pub const fn bigint(&self) -> &StandardConstructor {\n        &self.bigint\n    }\n\n    /// Returns the `Number` constructor.\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-number-constructor\n    #[inline]\n    #[must_use]\n    pub const fn number(&self) -> &StandardConstructor {\n        &self.number\n    }\n\n    /// Returns the `Boolean` constructor.\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-boolean-constructor\n    #[inline]\n    #[must_use]\n    pub const fn boolean(&self) -> &StandardConstructor {\n        &self.boolean\n    }\n\n    /// Returns the `String` constructor.\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-string-constructor\n    #[inline]\n    #[must_use]\n    pub const fn string(&self) -> &StandardConstructor {\n        &self.string\n    }\n\n    /// Returns the `RegExp` constructor.\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-regexp-constructor\n    #[inline]\n    #[must_use]\n    pub const fn regexp(&self) -> &StandardConstructor {\n        &self.regexp\n    }\n\n    /// Returns the `Symbol` constructor.\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-symbol-constructor\n    #[inline]\n    #[must_use]\n    pub const fn symbol(&self) -> &StandardConstructor {\n        &self.symbol\n    }\n\n    /// Returns the `Error` constructor.\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-error-constructor\n    #[inline]\n    #[must_use]\n    pub const fn error(&self) -> &StandardConstructor {\n        &self.error\n    }\n\n    /// Returns the `ReferenceError` constructor.\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-native-error-types-used-in-this-standard-referenceerror\n    #[inline]\n    #[must_use]\n    pub const fn reference_error(&self) -> &StandardConstructor {\n        &self.reference_error\n    }\n\n    /// Returns the `TypeError` constructor.\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-native-error-types-used-in-this-standard-typeerror\n    #[inline]\n    #[must_use]\n    pub const fn type_error(&self) -> &StandardConstructor {\n        &self.type_error\n    }\n\n    /// Returns the `RangeError` constructor.\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-native-error-types-used-in-this-standard-rangeerror\n    #[inline]\n    #[must_use]\n    pub const fn range_error(&self) -> &StandardConstructor {\n        &self.range_error\n    }\n\n    /// Returns the `SyntaxError` constructor.\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-native-error-types-used-in-this-standard-syntaxerror\n    #[inline]\n    #[must_use]\n    pub const fn syntax_error(&self) -> &StandardConstructor {\n        &self.syntax_error\n    }\n\n    /// Returns the `EvalError` constructor.\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-native-error-types-used-in-this-standard-evalerror\n    #[inline]\n    #[must_use]\n    pub const fn eval_error(&self) -> &StandardConstructor {\n        &self.eval_error\n    }\n\n    /// Returns the `URIError` constructor.\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-native-error-types-used-in-this-standard-urierror\n    #[inline]\n    #[must_use]\n    pub const fn uri_error(&self) -> &StandardConstructor {\n        &self.uri_error\n    }\n\n    /// Returns the `AggregateError` constructor.\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-aggregate-error-constructor\n    #[inline]\n    #[must_use]\n    pub const fn aggregate_error(&self) -> &StandardConstructor {\n        &self.aggregate_error\n    }\n\n    /// Returns the `Map` constructor.\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-map-constructor\n    #[inline]\n    #[must_use]\n    pub const fn map(&self) -> &StandardConstructor {\n        &self.map\n    }\n\n    /// Returns the `Set` constructor.\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-set-constructor\n    #[inline]\n    #[must_use]\n    pub const fn set(&self) -> &StandardConstructor {\n        &self.set\n    }\n\n    /// Returns the `TypedArray` constructor.\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-typedarray-constructors\n    #[inline]\n    #[must_use]\n    pub const fn typed_array(&self) -> &StandardConstructor {\n        &self.typed_array\n    }\n\n    /// Returns the `Int8Array` constructor.\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-typedarray-constructors\n    #[inline]\n    #[must_use]\n    pub const fn typed_int8_array(&self) -> &StandardConstructor {\n        &self.typed_int8_array\n    }\n\n    /// Returns the `Uint8Array` constructor.\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-typedarray-constructors\n    #[inline]\n    #[must_use]\n    pub const fn typed_uint8_array(&self) -> &StandardConstructor {\n        &self.typed_uint8_array\n    }\n\n    /// Returns the `Uint8ClampedArray` constructor.\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-typedarray-constructors\n    #[inline]\n    #[must_use]\n    pub const fn typed_uint8clamped_array(&self) -> &StandardConstructor {\n        &self.typed_uint8clamped_array\n    }\n\n    /// Returns the `Int16Array` constructor.\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-typedarray-constructors\n    #[inline]\n    #[must_use]\n    pub const fn typed_int16_array(&self) -> &StandardConstructor {\n        &self.typed_int16_array\n    }\n\n    /// Returns the `Uint16Array` constructor.\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-typedarray-constructors\n    #[inline]\n    #[must_use]\n    pub const fn typed_uint16_array(&self) -> &StandardConstructor {\n        &self.typed_uint16_array\n    }\n\n    /// Returns the `Uint32Array` constructor.\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-typedarray-constructors\n    #[inline]\n    #[must_use]\n    pub const fn typed_uint32_array(&self) -> &StandardConstructor {\n        &self.typed_uint32_array\n    }\n\n    /// Returns the `Int32Array` constructor.\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-typedarray-constructors\n    #[inline]\n    #[must_use]\n    pub const fn typed_int32_array(&self) -> &StandardConstructor {\n        &self.typed_int32_array\n    }\n\n    /// Returns the `BigInt64Array` constructor.\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-typedarray-constructors\n    #[inline]\n    #[must_use]\n    pub const fn typed_bigint64_array(&self) -> &StandardConstructor {\n        &self.typed_bigint64_array\n    }\n\n    /// Returns the `BigUint64Array` constructor.\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-typedarray-constructors\n    #[inline]\n    #[must_use]\n    pub const fn typed_biguint64_array(&self) -> &StandardConstructor {\n        &self.typed_biguint64_array\n    }\n\n    /// Returns the `Float16Array` constructor.\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-typedarray-constructors\n    #[cfg(feature = \"float16\")]\n    #[inline]\n    #[must_use]\n    pub const fn typed_float16_array(&self) -> &StandardConstructor {\n        &self.typed_float16_array\n    }\n\n    /// Returns the `Float32Array` constructor.\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-typedarray-constructors\n    #[inline]\n    #[must_use]\n    pub const fn typed_float32_array(&self) -> &StandardConstructor {\n        &self.typed_float32_array\n    }\n\n    /// Returns the `Float64Array` constructor.\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-typedarray-constructors\n    #[inline]\n    #[must_use]\n    pub const fn typed_float64_array(&self) -> &StandardConstructor {\n        &self.typed_float64_array\n    }\n\n    /// Returns the `ArrayBuffer` constructor.\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-arraybuffer-constructor\n    #[inline]\n    #[must_use]\n    pub const fn array_buffer(&self) -> &StandardConstructor {\n        &self.array_buffer\n    }\n\n    /// Returns the `SharedArrayBuffer` constructor.\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-sharedarraybuffer-constructor\n    #[inline]\n    #[must_use]\n    pub const fn shared_array_buffer(&self) -> &StandardConstructor {\n        &self.shared_array_buffer\n    }\n\n    /// Returns the `DataView` constructor.\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-dataview-constructor\n    #[inline]\n    #[must_use]\n    pub const fn data_view(&self) -> &StandardConstructor {\n        &self.data_view\n    }\n\n    /// Returns the `Intl.DateTimeFormat` constructor.\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///\n    /// [spec]: https://tc39.es/ecma402/#sec-intl-datetimeformat-constructor\n    #[inline]\n    #[must_use]\n    pub const fn date_time_format(&self) -> &StandardConstructor {\n        &self.date_time_format\n    }\n\n    /// Returns the `Promise` constructor.\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-promise-constructor\n    #[inline]\n    #[must_use]\n    pub const fn promise(&self) -> &StandardConstructor {\n        &self.promise\n    }\n\n    /// Returns the `WeakRef` constructor.\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-weak-ref-constructor\n    #[inline]\n    #[must_use]\n    pub const fn weak_ref(&self) -> &StandardConstructor {\n        &self.weak_ref\n    }\n\n    /// Returns the `WeakMap` constructor.\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-weakmap-constructor\n    #[inline]\n    #[must_use]\n    pub const fn weak_map(&self) -> &StandardConstructor {\n        &self.weak_map\n    }\n\n    /// Returns the `WeakSet` constructor.\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-weakset-constructor\n    #[inline]\n    #[must_use]\n    pub const fn weak_set(&self) -> &StandardConstructor {\n        &self.weak_set\n    }\n\n    /// Returns the `Iterator` constructor.\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///\n    /// [spec]: https://tc39.es/proposal-iterator-helpers/#sec-iterator-constructor\n    #[inline]\n    #[must_use]\n    pub const fn iterator(&self) -> &StandardConstructor {\n        &self.iterator\n    }\n\n    /// Returns the `FinalizationRegistry` constructor.\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-finalization-registry-constructor\n    #[inline]\n    #[must_use]\n    pub const fn finalization_registry(&self) -> &StandardConstructor {\n        &self.finalization_registry\n    }\n\n    /// Returns the `Intl.Collator` constructor.\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///\n    /// [spec]: https://tc39.es/ecma402/#sec-intl.collator\n    #[inline]\n    #[must_use]\n    #[cfg(feature = \"intl\")]\n    pub const fn collator(&self) -> &StandardConstructor {\n        &self.collator\n    }\n\n    /// Returns the `Intl.ListFormat` constructor.\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///\n    /// [spec]: https://tc39.es/ecma402/#sec-Intl.ListFormat\n    #[inline]\n    #[must_use]\n    #[cfg(feature = \"intl\")]\n    pub const fn list_format(&self) -> &StandardConstructor {\n        &self.list_format\n    }\n\n    /// Returns the `Intl.Locale` constructor.\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///\n    /// [spec]: https://tc39.es/ecma402/#sec-Intl.Locale\n    #[inline]\n    #[must_use]\n    #[cfg(feature = \"intl\")]\n    pub const fn locale(&self) -> &StandardConstructor {\n        &self.locale\n    }\n\n    /// Returns the `Intl.Segmenter` constructor.\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///\n    /// [spec]: https://tc39.es/ecma402/#sec-intl.segmenter\n    #[inline]\n    #[must_use]\n    #[cfg(feature = \"intl\")]\n    pub const fn segmenter(&self) -> &StandardConstructor {\n        &self.segmenter\n    }\n\n    /// Returns the `Intl.PluralRules` constructor.\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///\n    /// [spec]: https://tc39.es/ecma402/#sec-intl.pluralrules\n    #[inline]\n    #[must_use]\n    #[cfg(feature = \"intl\")]\n    pub const fn plural_rules(&self) -> &StandardConstructor {\n        &self.plural_rules\n    }\n\n    /// Returns the `Intl.NumberFormat` constructor.\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///\n    /// [spec]: https://tc39.es/ecma402/#sec-intl.numberformat\n    #[inline]\n    #[must_use]\n    #[cfg(feature = \"intl\")]\n    pub const fn number_format(&self) -> &StandardConstructor {\n        &self.number_format\n    }\n\n    /// Returns the `Temporal.Instant` constructor.\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///\n    /// [spec]: https://tc39.es/proposal-temporal/#sec-temporal-instant-constructor\n    #[inline]\n    #[must_use]\n    #[cfg(feature = \"temporal\")]\n    pub const fn instant(&self) -> &StandardConstructor {\n        &self.instant\n    }\n\n    /// Returns the `Temporal.PlainDateTime` constructor.\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///\n    /// [spec]: https://tc39.es/proposal-temporal/#sec-temporal-plaindatetime-constructor\n    #[inline]\n    #[must_use]\n    #[cfg(feature = \"temporal\")]\n    pub const fn plain_date_time(&self) -> &StandardConstructor {\n        &self.plain_date_time\n    }\n\n    /// Returns the `Temporal.PlainDate` constructor.\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///\n    /// [spec]: https://tc39.es/proposal-temporal/#sec-temporal-plaindate-constructor\n    #[inline]\n    #[must_use]\n    #[cfg(feature = \"temporal\")]\n    pub const fn plain_date(&self) -> &StandardConstructor {\n        &self.plain_date\n    }\n\n    /// Returns the `Temporal.PlainTime` constructor.\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///\n    /// [spec]: https://tc39.es/proposal-temporal/#sec-temporal-plaintime-constructor\n    #[inline]\n    #[must_use]\n    #[cfg(feature = \"temporal\")]\n    pub const fn plain_time(&self) -> &StandardConstructor {\n        &self.plain_time\n    }\n\n    /// Returns the `Temporal.PlainYearMonth` constructor.\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///\n    /// [spec]: https://tc39.es/proposal-temporal/#sec-temporal-plainyearmonth-constructor\n    #[inline]\n    #[must_use]\n    #[cfg(feature = \"temporal\")]\n    pub const fn plain_year_month(&self) -> &StandardConstructor {\n        &self.plain_year_month\n    }\n\n    /// Returns the `Temporal.PlainMonthDay` constructor.\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///\n    /// [spec]: https://tc39.es/proposal-temporal/#sec-temporal-plainmonthday-constructor\n    #[inline]\n    #[must_use]\n    #[cfg(feature = \"temporal\")]\n    pub const fn plain_month_day(&self) -> &StandardConstructor {\n        &self.plain_month_day\n    }\n\n    /// Returns the `Temporal.TimeZone` constructor.\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///\n    /// [spec]: https://tc39.es/proposal-temporal/#sec-temporal-timezone-constructor\n    #[inline]\n    #[must_use]\n    #[cfg(feature = \"temporal\")]\n    pub const fn time_zone(&self) -> &StandardConstructor {\n        &self.time_zone\n    }\n\n    /// Returns the `Temporal.Duration` constructor.\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///\n    /// [spec]: https://tc39.es/proposal-temporal/#sec-temporal-duration-constructor\n    #[inline]\n    #[must_use]\n    #[cfg(feature = \"temporal\")]\n    pub const fn duration(&self) -> &StandardConstructor {\n        &self.duration\n    }\n\n    /// Returns the `Temporal.ZonedDateTime` constructor.\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///\n    /// [spec]: https://tc39.es/proposal-temporal/#sec-temporal-zoneddatetime-constructor\n    #[inline]\n    #[must_use]\n    #[cfg(feature = \"temporal\")]\n    pub const fn zoned_date_time(&self) -> &StandardConstructor {\n        &self.zoned_date_time\n    }\n\n    /// Returns the `Temporal.Calendar` constructor.\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///\n    /// [spec]: https://tc39.es/proposal-temporal/#sec-temporal-calendar-constructor\n    #[inline]\n    #[must_use]\n    #[cfg(feature = \"temporal\")]\n    pub const fn calendar(&self) -> &StandardConstructor {\n        &self.calendar\n    }\n}\n\n/// Cached intrinsic objects\n#[derive(Debug, Trace, Finalize)]\npub struct IntrinsicObjects {\n    /// [`%Reflect%`](https://tc39.es/ecma262/#sec-reflect)\n    reflect: JsObject,\n\n    /// [`%Math%`](https://tc39.es/ecma262/#sec-math)\n    math: JsObject,\n\n    /// [`%JSON%`](https://tc39.es/ecma262/#sec-json)\n    json: JsObject,\n\n    /// [`%ThrowTypeError%`](https://tc39.es/ecma262/#sec-%throwtypeerror%)\n    throw_type_error: JsFunction,\n\n    /// [`%Array.prototype.values%`](https://tc39.es/ecma262/#sec-array.prototype.values)\n    array_prototype_values: JsFunction,\n\n    /// [`%Array.prototype.toString%`](https://tc39.es/ecma262/#sec-array.prototype.tostring)\n    array_prototype_to_string: JsFunction,\n\n    /// Cached iterator prototypes.\n    iterator_prototypes: IteratorPrototypes,\n\n    /// [`%GeneratorFunction.prototype.prototype%`](https://tc39.es/ecma262/#sec-properties-of-generator-prototype)\n    generator: JsObject,\n\n    /// [`%AsyncGeneratorFunction.prototype.prototype%`](https://tc39.es/ecma262/#sec-properties-of-asyncgenerator-prototype)\n    async_generator: JsObject,\n\n    /// [`%Atomics%`](https://tc39.es/ecma262/#sec-atomics)\n    atomics: JsObject,\n\n    /// [`%eval%`](https://tc39.es/ecma262/#sec-eval-x)\n    eval: JsFunction,\n\n    /// URI related functions\n    uri_functions: UriFunctions,\n\n    /// [`%isFinite%`](https://tc39.es/ecma262/#sec-isfinite-number)\n    is_finite: JsFunction,\n\n    /// [`%isNaN%`](https://tc39.es/ecma262/#sec-isnan-number)\n    is_nan: JsFunction,\n\n    /// [`%parseFloat%`](https://tc39.es/ecma262/#sec-parsefloat-string)\n    parse_float: JsFunction,\n\n    /// [`%parseInt%`](https://tc39.es/ecma262/#sec-parseint-string-radix)\n    parse_int: JsFunction,\n\n    /// [`%escape%`](https://tc39.es/ecma262/#sec-escape-string)\n    #[cfg(feature = \"annex-b\")]\n    escape: JsFunction,\n\n    /// [`%unescape%`](https://tc39.es/ecma262/#sec-unescape-string)\n    #[cfg(feature = \"annex-b\")]\n    unescape: JsFunction,\n\n    /// [`%Intl%`](https://tc39.es/ecma402/#intl-object)\n    #[cfg(feature = \"intl\")]\n    intl: JsObject<Intl>,\n\n    /// [`%SegmentsPrototype%`](https://tc39.es/ecma402/#sec-%segmentsprototype%-object)\n    #[cfg(feature = \"intl\")]\n    segments_prototype: JsObject,\n\n    /// [`%Temporal%`](https://tc39.es/proposal-temporal/#sec-temporal-objects)\n    #[cfg(feature = \"temporal\")]\n    temporal: JsObject,\n\n    /// [`%Temporal.Now%`](https://tc39.es/proposal-temporal/#sec-temporal-now-object)\n    #[cfg(feature = \"temporal\")]\n    now: JsObject,\n}\n\nimpl IntrinsicObjects {\n    /// Creates a new set of uninitialized intrinsic objects.\n    ///\n    /// Creates all the required empty objects for every intrinsic object in this realm.\n    ///\n    /// To initialize all the intrinsic objects with their spec properties, see [`Realm::initialize`].\n    ///\n    /// [`Realm::initialize`]: crate::realm::Realm::initialize\n    #[allow(clippy::unnecessary_wraps)]\n    pub(crate) fn uninit() -> Option<Self> {\n        Some(Self {\n            reflect: JsObject::with_null_proto(),\n            math: JsObject::with_null_proto(),\n            json: JsObject::with_null_proto(),\n            throw_type_error: JsFunction::empty_intrinsic_function(false),\n            array_prototype_values: JsFunction::empty_intrinsic_function(false),\n            array_prototype_to_string: JsFunction::empty_intrinsic_function(false),\n            iterator_prototypes: IteratorPrototypes::default(),\n            generator: JsObject::with_null_proto(),\n            async_generator: JsObject::with_null_proto(),\n            atomics: JsObject::with_null_proto(),\n            eval: JsFunction::empty_intrinsic_function(false),\n            uri_functions: UriFunctions::default(),\n            is_finite: JsFunction::empty_intrinsic_function(false),\n            is_nan: JsFunction::empty_intrinsic_function(false),\n            parse_float: JsFunction::empty_intrinsic_function(false),\n            parse_int: JsFunction::empty_intrinsic_function(false),\n            #[cfg(feature = \"annex-b\")]\n            escape: JsFunction::empty_intrinsic_function(false),\n            #[cfg(feature = \"annex-b\")]\n            unescape: JsFunction::empty_intrinsic_function(false),\n            #[cfg(feature = \"intl\")]\n            intl: JsObject::new_unique(None, Intl::new()?),\n            #[cfg(feature = \"intl\")]\n            segments_prototype: JsObject::with_null_proto(),\n            #[cfg(feature = \"temporal\")]\n            temporal: JsObject::with_null_proto(),\n            #[cfg(feature = \"temporal\")]\n            now: JsObject::with_null_proto(),\n        })\n    }\n\n    /// Gets the [`%ThrowTypeError%`][spec] intrinsic function.\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-%throwtypeerror%\n    #[inline]\n    #[must_use]\n    pub fn throw_type_error(&self) -> JsFunction {\n        self.throw_type_error.clone()\n    }\n\n    /// Gets the [`%Array.prototype.values%`][spec] intrinsic function.\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-array.prototype.values\n    #[inline]\n    #[must_use]\n    pub fn array_prototype_values(&self) -> JsFunction {\n        self.array_prototype_values.clone()\n    }\n\n    /// Gets the [`%Array.prototype.toString%`][spec] intrinsic function.\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-array.prototype.tostring\n    #[inline]\n    #[must_use]\n    pub fn array_prototype_to_string(&self) -> JsFunction {\n        self.array_prototype_to_string.clone()\n    }\n\n    /// Gets the cached iterator prototypes.\n    #[inline]\n    #[must_use]\n    pub const fn iterator_prototypes(&self) -> &IteratorPrototypes {\n        &self.iterator_prototypes\n    }\n\n    /// Gets the [`%GeneratorFunction.prototype.prototype%`][spec] intrinsic object.\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-generator-objects\n    #[inline]\n    #[must_use]\n    pub fn generator(&self) -> JsObject {\n        self.generator.clone()\n    }\n\n    /// Gets the [`%AsyncGeneratorFunction.prototype.prototype%`][spec] intrinsic object.\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-asyncgenerator-objects\n    #[inline]\n    #[must_use]\n    pub fn async_generator(&self) -> JsObject {\n        self.async_generator.clone()\n    }\n\n    /// Gets the [`%Atomics%`][spec] intrinsic object.\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-atomics\n    #[inline]\n    #[must_use]\n    pub fn atomics(&self) -> JsObject {\n        self.atomics.clone()\n    }\n\n    /// Gets the [`%eval%`][spec] intrinsic function.\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-eval-x\n    #[inline]\n    #[must_use]\n    pub fn eval(&self) -> JsFunction {\n        self.eval.clone()\n    }\n\n    /// Gets the URI intrinsic functions.\n    #[inline]\n    #[must_use]\n    pub const fn uri_functions(&self) -> &UriFunctions {\n        &self.uri_functions\n    }\n\n    /// Gets the [`%Reflect%`][spec] intrinsic object.\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-reflect\n    #[inline]\n    #[must_use]\n    pub fn reflect(&self) -> JsObject {\n        self.reflect.clone()\n    }\n\n    /// Gets the [`%Math%`][spec] intrinsic object.\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-math\n    #[inline]\n    #[must_use]\n    pub fn math(&self) -> JsObject {\n        self.math.clone()\n    }\n\n    /// Gets the [`%JSON%`][spec] intrinsic object.\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-json\n    #[inline]\n    #[must_use]\n    pub fn json(&self) -> JsObject {\n        self.json.clone()\n    }\n\n    /// Gets the [`%isFinite%`][spec] intrinsic function.\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-isfinite-number\n    #[inline]\n    #[must_use]\n    pub fn is_finite(&self) -> JsFunction {\n        self.is_finite.clone()\n    }\n\n    /// Gets the [`%isNaN%`][spec] intrinsic function.\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-isnan-number\n    #[inline]\n    #[must_use]\n    pub fn is_nan(&self) -> JsFunction {\n        self.is_nan.clone()\n    }\n\n    /// Gets the [`%parseFloat%`][spec] intrinsic function.\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-parsefloat-string\n    #[inline]\n    #[must_use]\n    pub fn parse_float(&self) -> JsFunction {\n        self.parse_float.clone()\n    }\n\n    /// Gets the [`%parseInt%`][spec] intrinsic function.\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-parseint-string-radix\n    #[inline]\n    #[must_use]\n    pub fn parse_int(&self) -> JsFunction {\n        self.parse_int.clone()\n    }\n\n    /// Gets the [`%escape%`][spec] intrinsic function.\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-escape-string\n    #[must_use]\n    #[cfg(feature = \"annex-b\")]\n    #[inline]\n    pub fn escape(&self) -> JsFunction {\n        self.escape.clone()\n    }\n\n    /// Gets the [`%unescape%`][spec] intrinsic function.\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-unescape-string\n    #[must_use]\n    #[cfg(feature = \"annex-b\")]\n    #[inline]\n    pub fn unescape(&self) -> JsFunction {\n        self.unescape.clone()\n    }\n\n    /// Gets the [`%Intl%`][spec] intrinsic object.\n    ///\n    /// [spec]: https://tc39.es/ecma402/#intl-object\n    #[must_use]\n    #[cfg(feature = \"intl\")]\n    #[inline]\n    pub fn intl(&self) -> JsObject<Intl> {\n        self.intl.clone()\n    }\n\n    /// Gets the [`%SegmentsPrototype%`][spec] intrinsic object.\n    ///\n    /// [spec]: https://tc39.es/ecma402/#sec-%segmentsprototype%-object\n    #[must_use]\n    #[cfg(feature = \"intl\")]\n    pub fn segments_prototype(&self) -> JsObject {\n        self.segments_prototype.clone()\n    }\n\n    /// Gets the [`%Temporal%`][spec] intrinsic object.\n    ///\n    /// [spec]: https://tc39.es/proposal-temporal/#sec-temporal-objects\n    #[cfg(feature = \"temporal\")]\n    #[must_use]\n    #[inline]\n    pub fn temporal(&self) -> JsObject {\n        self.temporal.clone()\n    }\n\n    /// Gets the [`%Temporal.Now%`][spec] intrinsic object.\n    ///\n    /// [spec]: https://tc39.es/proposal-temporal/#sec-temporal-now-object\n    #[cfg(feature = \"temporal\")]\n    #[must_use]\n    #[inline]\n    pub fn now(&self) -> JsObject {\n        self.now.clone()\n    }\n}\n\n/// Contains commonly used [`ObjectTemplate`]s.\n#[derive(Debug, Trace, Finalize)]\npub(crate) struct ObjectTemplates {\n    iterator_result: ObjectTemplate,\n    ordinary_object: ObjectTemplate,\n    array: ObjectTemplate,\n    number: ObjectTemplate,\n    string: ObjectTemplate,\n    symbol: ObjectTemplate,\n    bigint: ObjectTemplate,\n    boolean: ObjectTemplate,\n\n    regexp: ObjectTemplate,\n    regexp_without_proto: ObjectTemplate,\n\n    unmapped_arguments: ObjectTemplate,\n    mapped_arguments: ObjectTemplate,\n\n    function_with_prototype: ObjectTemplate,\n    function_prototype: ObjectTemplate,\n\n    function: ObjectTemplate,\n    async_function: ObjectTemplate,\n    generator_function: ObjectTemplate,\n    async_generator_function: ObjectTemplate,\n\n    function_without_proto: ObjectTemplate,\n    function_with_prototype_without_proto: ObjectTemplate,\n\n    namespace: ObjectTemplate,\n\n    with_resolvers: ObjectTemplate,\n\n    wait_async: ObjectTemplate,\n}\n\nimpl ObjectTemplates {\n    pub(crate) fn new(root_shape: &RootShape, constructors: &StandardConstructors) -> Self {\n        let root_shape = root_shape.shape();\n\n        // pre-initialize used shapes.\n        let ordinary_object =\n            ObjectTemplate::with_prototype(root_shape, constructors.object().prototype());\n        let mut array = ObjectTemplate::new(root_shape);\n        let length_property_key: PropertyKey = js_string!(\"length\").into();\n        array.property(\n            length_property_key.clone(),\n            Attribute::WRITABLE | Attribute::PERMANENT | Attribute::NON_ENUMERABLE,\n        );\n        array.set_prototype(constructors.array().prototype());\n\n        let number = ObjectTemplate::with_prototype(root_shape, constructors.number().prototype());\n        let symbol = ObjectTemplate::with_prototype(root_shape, constructors.symbol().prototype());\n        let bigint = ObjectTemplate::with_prototype(root_shape, constructors.bigint().prototype());\n        let boolean =\n            ObjectTemplate::with_prototype(root_shape, constructors.boolean().prototype());\n        let mut string = ObjectTemplate::new(root_shape);\n        string.property(\n            length_property_key.clone(),\n            Attribute::READONLY | Attribute::PERMANENT | Attribute::NON_ENUMERABLE,\n        );\n        string.set_prototype(constructors.string().prototype());\n\n        let mut regexp_without_proto = ObjectTemplate::new(root_shape);\n        regexp_without_proto.property(js_string!(\"lastIndex\").into(), Attribute::WRITABLE);\n\n        let mut regexp = regexp_without_proto.clone();\n        regexp.set_prototype(constructors.regexp().prototype());\n\n        let name_property_key: PropertyKey = js_string!(\"name\").into();\n        let mut function = ObjectTemplate::new(root_shape);\n        function.property(\n            length_property_key.clone(),\n            Attribute::READONLY | Attribute::CONFIGURABLE | Attribute::NON_ENUMERABLE,\n        );\n        function.property(\n            name_property_key,\n            Attribute::READONLY | Attribute::CONFIGURABLE | Attribute::NON_ENUMERABLE,\n        );\n\n        let function_without_proto = function.clone();\n        let mut async_function = function.clone();\n        let mut function_with_prototype = function.clone();\n\n        function_with_prototype.property(\n            PROTOTYPE.into(),\n            Attribute::WRITABLE | Attribute::PERMANENT | Attribute::NON_ENUMERABLE,\n        );\n        let mut generator_function = function_with_prototype.clone();\n        let mut async_generator_function = function_with_prototype.clone();\n\n        let function_with_prototype_without_proto = function_with_prototype.clone();\n\n        function.set_prototype(constructors.function().prototype());\n        function_with_prototype.set_prototype(constructors.function().prototype());\n        async_function.set_prototype(constructors.async_function().prototype());\n        generator_function.set_prototype(constructors.generator_function().prototype());\n        async_generator_function.set_prototype(constructors.async_generator_function().prototype());\n\n        let mut function_prototype = ordinary_object.clone();\n        function_prototype.property(\n            CONSTRUCTOR.into(),\n            Attribute::WRITABLE | Attribute::CONFIGURABLE | Attribute::NON_ENUMERABLE,\n        );\n\n        let mut unmapped_arguments = ordinary_object.clone();\n\n        // 4. Perform DefinePropertyOrThrow(obj, \"length\", PropertyDescriptor { [[Value]]: 𝔽(len),\n        // [[Writable]]: true, [[Enumerable]]: false, [[Configurable]]: true }).\n        unmapped_arguments.property(\n            length_property_key,\n            Attribute::WRITABLE | Attribute::NON_ENUMERABLE | Attribute::CONFIGURABLE,\n        );\n\n        // 7. Perform ! DefinePropertyOrThrow(obj, @@iterator, PropertyDescriptor {\n        // [[Value]]: %Array.prototype.values%, [[Writable]]: true, [[Enumerable]]: false,\n        // [[Configurable]]: true }).\n        unmapped_arguments.property(\n            JsSymbol::iterator().into(),\n            Attribute::WRITABLE | Attribute::NON_ENUMERABLE | Attribute::CONFIGURABLE,\n        );\n\n        let mut mapped_arguments = unmapped_arguments.clone();\n\n        // 8. Perform ! DefinePropertyOrThrow(obj, \"callee\", PropertyDescriptor {\n        // [[Get]]: %ThrowTypeError%, [[Set]]: %ThrowTypeError%, [[Enumerable]]: false,\n        // [[Configurable]]: false }).\n        unmapped_arguments.accessor(\n            js_string!(\"callee\").into(),\n            true,\n            true,\n            Attribute::NON_ENUMERABLE | Attribute::PERMANENT,\n        );\n\n        // 21. Perform ! DefinePropertyOrThrow(obj, \"callee\", PropertyDescriptor {\n        // [[Value]]: func, [[Writable]]: true, [[Enumerable]]: false, [[Configurable]]: true }).\n        mapped_arguments.property(\n            js_string!(\"callee\").into(),\n            Attribute::WRITABLE | Attribute::NON_ENUMERABLE | Attribute::CONFIGURABLE,\n        );\n\n        let mut iterator_result = ordinary_object.clone();\n        iterator_result.property(\n            js_string!(\"value\").into(),\n            Attribute::WRITABLE | Attribute::CONFIGURABLE | Attribute::ENUMERABLE,\n        );\n        iterator_result.property(\n            js_string!(\"done\").into(),\n            Attribute::WRITABLE | Attribute::CONFIGURABLE | Attribute::ENUMERABLE,\n        );\n\n        let mut namespace = ObjectTemplate::new(root_shape);\n        namespace.property(JsSymbol::to_string_tag().into(), Attribute::empty());\n\n        let with_resolvers = {\n            let mut with_resolvers = ordinary_object.clone();\n\n            with_resolvers\n                // 4. Perform ! CreateDataPropertyOrThrow(obj, \"promise\", promiseCapability.[[Promise]]).\n                .property(js_string!(\"promise\").into(), Attribute::all())\n                // 5. Perform ! CreateDataPropertyOrThrow(obj, \"resolve\", promiseCapability.[[Resolve]]).\n                .property(js_string!(\"resolve\").into(), Attribute::all())\n                // 6. Perform ! CreateDataPropertyOrThrow(obj, \"reject\", promiseCapability.[[Reject]]).\n                .property(js_string!(\"reject\").into(), Attribute::all());\n\n            with_resolvers\n        };\n\n        let wait_async = {\n            let mut obj = ordinary_object.clone();\n\n            obj.property(js_string!(\"async\").into(), Attribute::all())\n                .property(js_string!(\"value\").into(), Attribute::all());\n\n            obj\n        };\n\n        Self {\n            iterator_result,\n            ordinary_object,\n            array,\n            number,\n            string,\n            symbol,\n            bigint,\n            boolean,\n            regexp,\n            regexp_without_proto,\n            unmapped_arguments,\n            mapped_arguments,\n            function_with_prototype,\n            function_prototype,\n            function,\n            async_function,\n            generator_function,\n            async_generator_function,\n            function_without_proto,\n            function_with_prototype_without_proto,\n            namespace,\n            with_resolvers,\n            wait_async,\n        }\n    }\n\n    /// Cached iterator result template.\n    ///\n    /// Transitions:\n    ///\n    /// 1. `__proto__`: `Object.prototype`\n    /// 2. `\"done\"`: (`WRITABLE`, `CONFIGURABLE`, `ENUMERABLE`)\n    /// 3. `\"value\"`: (`WRITABLE`, `CONFIGURABLE`, `ENUMERABLE`)\n    pub(crate) const fn iterator_result(&self) -> &ObjectTemplate {\n        &self.iterator_result\n    }\n\n    /// Cached ordinary object template.\n    ///\n    /// Transitions:\n    ///\n    /// 1. `__proto__`: `Object.prototype`\n    pub(crate) const fn ordinary_object(&self) -> &ObjectTemplate {\n        &self.ordinary_object\n    }\n\n    /// Cached array object template.\n    ///\n    /// Transitions:\n    ///\n    /// 1. `\"length\"`: (`WRITABLE`, `PERMANENT`,`NON_ENUMERABLE`)\n    /// 2. `__proto__`: `Array.prototype`\n    pub(crate) const fn array(&self) -> &ObjectTemplate {\n        &self.array\n    }\n\n    /// Cached number object template.\n    ///\n    /// Transitions:\n    ///\n    /// 1. `__proto__`: `Number.prototype`\n    pub(crate) const fn number(&self) -> &ObjectTemplate {\n        &self.number\n    }\n\n    /// Cached string object template.\n    ///\n    /// Transitions:\n    ///\n    /// 1. `\"length\"`: (`READONLY`, `PERMANENT`,`NON_ENUMERABLE`)\n    /// 2. `__proto__`: `String.prototype`\n    pub(crate) const fn string(&self) -> &ObjectTemplate {\n        &self.string\n    }\n\n    /// Cached symbol object template.\n    ///\n    /// Transitions:\n    ///\n    /// 1. `__proto__`: `Symbol.prototype`\n    pub(crate) const fn symbol(&self) -> &ObjectTemplate {\n        &self.symbol\n    }\n\n    /// Cached bigint object template.\n    ///\n    /// Transitions:\n    ///\n    /// 1. `__proto__`: `BigInt.prototype`\n    pub(crate) const fn bigint(&self) -> &ObjectTemplate {\n        &self.bigint\n    }\n\n    /// Cached boolean object template.\n    ///\n    /// Transitions:\n    ///\n    /// 1. `__proto__`: `Boolean.prototype`\n    pub(crate) const fn boolean(&self) -> &ObjectTemplate {\n        &self.boolean\n    }\n\n    /// Cached regexp object template.\n    ///\n    /// Transitions:\n    ///\n    /// 1. `\"lastIndex\"`: (`WRITABLE` , `PERMANENT`,`NON_ENUMERABLE`)\n    pub(crate) const fn regexp(&self) -> &ObjectTemplate {\n        &self.regexp\n    }\n\n    /// Cached regexp object template without `__proto__` template.\n    ///\n    /// Transitions:\n    ///\n    /// 1. `\"lastIndex\"`: (`WRITABLE` , `PERMANENT`,`NON_ENUMERABLE`)\n    /// 2. `__proto__`: `RegExp.prototype`\n    pub(crate) const fn regexp_without_proto(&self) -> &ObjectTemplate {\n        &self.regexp_without_proto\n    }\n\n    /// Cached unmapped arguments object template.\n    ///\n    /// Transitions:\n    ///\n    /// 1. `__proto__`: `Object.prototype`\n    /// 2. `\"length\"`: (`WRITABLE`, `NON_ENUMERABLE`, `CONFIGURABLE`)\n    /// 3. `@@iterator`: (`WRITABLE`, `NON_ENUMERABLE`, `CONFIGURABLE`)\n    /// 4. `get/set` `\"callee\"`: (`NON_ENUMERABLE`, `PERMANENT`)\n    pub(crate) const fn unmapped_arguments(&self) -> &ObjectTemplate {\n        &self.unmapped_arguments\n    }\n\n    /// Cached mapped arguments object template.\n    ///\n    /// Transitions:\n    ///\n    /// 1. `__proto__`: `Object.prototype`\n    /// 2. `\"length\"`: (`WRITABLE`, `NON_ENUMERABLE`, `CONFIGURABLE`)\n    /// 3. `@@iterator`: (`WRITABLE`, `NON_ENUMERABLE`, `CONFIGURABLE`)\n    /// 4. `\"callee\"`: (`WRITABLE`, `NON_ENUMERABLE`, `CONFIGURABLE`)\n    pub(crate) const fn mapped_arguments(&self) -> &ObjectTemplate {\n        &self.mapped_arguments\n    }\n\n    /// Cached function object with `\"prototype\"` property template.\n    ///\n    /// Transitions:\n    ///\n    /// 1. `\"length\"`: (`READONLY`, `NON_ENUMERABLE`, `CONFIGURABLE`)\n    /// 2. `\"name\"`: (`READONLY`, `NON_ENUMERABLE`, `CONFIGURABLE`)\n    /// 3. `\"prototype\"`: (`WRITABLE`, `PERMANENT`, `NON_ENUMERABLE`)\n    /// 4. `__proto__`: `Function.prototype`\n    pub(crate) const fn function_with_prototype(&self) -> &ObjectTemplate {\n        &self.function_with_prototype\n    }\n\n    /// Cached constructor function object template.\n    ///\n    /// Transitions:\n    ///\n    /// 1. `__proto__`: `Object.prototype`\n    /// 2. `\"constructor\"`: (`WRITABLE`, `CONFIGURABLE`, `NON_ENUMERABLE`)\n    pub(crate) const fn function_prototype(&self) -> &ObjectTemplate {\n        &self.function_prototype\n    }\n\n    /// Cached function object property template.\n    ///\n    /// Transitions:\n    ///\n    /// 1. `\"length\"`: (`READONLY`, `NON_ENUMERABLE`, `CONFIGURABLE`)\n    /// 2. `\"name\"`: (`READONLY`, `NON_ENUMERABLE`, `CONFIGURABLE`)\n    /// 3. `__proto__`: `Function.prototype`\n    pub(crate) const fn function(&self) -> &ObjectTemplate {\n        &self.function\n    }\n\n    /// Cached function object property template.\n    ///\n    /// Transitions:\n    ///\n    /// 1. `\"length\"`: (`READONLY`, `NON_ENUMERABLE`, `CONFIGURABLE`)\n    /// 2. `\"name\"`: (`READONLY`, `NON_ENUMERABLE`, `CONFIGURABLE`)\n    /// 3. `__proto__`: `AsyncFunction.prototype`\n    pub(crate) const fn async_function(&self) -> &ObjectTemplate {\n        &self.async_function\n    }\n\n    /// Cached function object property template.\n    ///\n    /// Transitions:\n    ///\n    /// 1. `\"length\"`: (`READONLY`, `NON_ENUMERABLE`, `CONFIGURABLE`)\n    /// 2. `\"name\"`: (`READONLY`, `NON_ENUMERABLE`, `CONFIGURABLE`)\n    /// 3. `\"prototype\"`: (`WRITABLE`, `PERMANENT`, `NON_ENUMERABLE`)\n    /// 4. `__proto__`: `GeneratorFunction.prototype`\n    pub(crate) const fn generator_function(&self) -> &ObjectTemplate {\n        &self.generator_function\n    }\n\n    /// Cached function object property template.\n    ///\n    /// Transitions:\n    ///\n    /// 1. `\"length\"`: (`READONLY`, `NON_ENUMERABLE`, `CONFIGURABLE`)\n    /// 2. `\"name\"`: (`READONLY`, `NON_ENUMERABLE`, `CONFIGURABLE`)\n    /// 3. `\"prototype\"`: (`WRITABLE`, `PERMANENT`, `NON_ENUMERABLE`)\n    /// 4. `__proto__`: `AsyncGeneratorFunction.prototype`\n    pub(crate) const fn async_generator_function(&self) -> &ObjectTemplate {\n        &self.async_generator_function\n    }\n\n    /// Cached function object without `__proto__` template.\n    ///\n    /// Transitions:\n    ///\n    /// 1. `\"length\"`: (`READONLY`, `NON_ENUMERABLE`, `CONFIGURABLE`)\n    /// 2. `\"name\"`: (`READONLY`, `NON_ENUMERABLE`, `CONFIGURABLE`)\n    pub(crate) const fn function_without_proto(&self) -> &ObjectTemplate {\n        &self.function_without_proto\n    }\n\n    /// Cached function object with `\"prototype\"` and without `__proto__` template.\n    ///\n    /// Transitions:\n    ///\n    /// 1. `\"length\"`: (`READONLY`, `NON_ENUMERABLE`, `CONFIGURABLE`)\n    /// 2. `\"name\"`: (`READONLY`, `NON_ENUMERABLE`, `CONFIGURABLE`)\n    /// 3. `\"prototype\"`: (`WRITABLE`, `PERMANENT`, `NON_ENUMERABLE`)\n    pub(crate) const fn function_with_prototype_without_proto(&self) -> &ObjectTemplate {\n        &self.function_with_prototype_without_proto\n    }\n\n    /// Cached namespace object template.\n    ///\n    /// Transitions:\n    ///\n    /// 1. `@@toStringTag`: (`READONLY`, `NON_ENUMERABLE`, `PERMANENT`)\n    pub(crate) const fn namespace(&self) -> &ObjectTemplate {\n        &self.namespace\n    }\n\n    /// Cached object from the `Promise.withResolvers` method.\n    ///\n    /// Transitions:\n    ///\n    /// 1. `__proto__`: `Object.prototype`\n    /// 2. `\"promise\"`: (`WRITABLE`, `ENUMERABLE`, `CONFIGURABLE`)\n    /// 3. `\"resolve\"`: (`WRITABLE`, `ENUMERABLE`, `CONFIGURABLE`)\n    /// 4. `\"reject\"`: (`WRITABLE`, `ENUMERABLE`, `CONFIGURABLE`)\n    pub(crate) const fn with_resolvers(&self) -> &ObjectTemplate {\n        &self.with_resolvers\n    }\n\n    /// Cached object from the `Atomics.waitAsync` method.\n    ///\n    /// Transitions:\n    ///\n    /// 1. `__proto__`: `Object.prototype`\n    /// 2. `\"async\"`: (`WRITABLE`, `ENUMERABLE`, `CONFIGURABLE`)\n    /// 3. `\"value\"`: (`WRITABLE`, `ENUMERABLE`, `CONFIGURABLE`)\n    pub(crate) const fn wait_async(&self) -> &ObjectTemplate {\n        &self.wait_async\n    }\n}\n"
  },
  {
    "path": "core/engine/src/context/mod.rs",
    "content": "//! The ECMAScript context.\n\nuse std::{cell::Cell, path::Path, rc::Rc};\n\nuse boa_ast::StatementList;\nuse boa_interner::Interner;\nuse boa_parser::source::ReadChar;\npub use hooks::{DefaultHooks, HostHooks};\n#[cfg(feature = \"intl\")]\npub use icu::IcuError;\nuse intrinsics::Intrinsics;\n#[cfg(any(feature = \"temporal\", feature = \"intl\"))]\nuse temporal_rs::provider::TimeZoneProvider;\n#[cfg(any(feature = \"temporal\", feature = \"intl\"))]\nuse timezone_provider::experimental_tzif::ZeroCompiledTzdbProvider;\n\nuse crate::job::Job;\nuse crate::js_error;\nuse crate::module::DynModuleLoader;\nuse crate::vm::{CodeBlock, RuntimeLimits, create_function_object_fast};\nuse crate::{\n    HostDefined, JsNativeError, JsResult, JsString, JsValue, NativeObject, Source, builtins,\n    class::{Class, ClassBuilder},\n    job::{JobExecutor, SimpleJobExecutor},\n    js_string,\n    module::{IdleModuleLoader, ModuleLoader, SimpleModuleLoader},\n    native_function::NativeFunction,\n    object::{FunctionObjectBuilder, JsObject, shape::RootShape},\n    optimizer::{Optimizer, OptimizerOptions, OptimizerStatistics},\n    property::{Attribute, PropertyDescriptor, PropertyKey},\n    realm::Realm,\n    script::Script,\n    vm::{ActiveRunnable, CallFrame, Vm},\n};\n\nuse self::intrinsics::StandardConstructor;\n\npub mod time;\nuse crate::context::time::StdClock;\npub use time::Clock;\n\nmod hooks;\n#[cfg(feature = \"intl\")]\npub(crate) mod icu;\npub mod intrinsics;\n\nthread_local! {\n    static CANNOT_BLOCK_COUNTER: Cell<u64> = const { Cell::new(0) };\n}\n\n/// ECMAScript context. It is the primary way to interact with the runtime.\n///\n/// `Context`s constructed in a thread share the same runtime, therefore it\n/// is possible to share objects from one context to another context, but they\n/// have to be in the same thread.\n///\n/// # Examples\n///\n/// ## Execute Function of Script File\n///\n/// ```rust\n/// use boa_engine::{\n///     Context, Source, js_string,\n///     object::ObjectInitializer,\n///     property::{Attribute, PropertyDescriptor},\n/// };\n///\n/// let script = r#\"\n///     function test(arg1) {\n///         if(arg1 != null) {\n///             return arg1.x;\n///         }\n///         return 112233;\n///     }\n/// \"#;\n///\n/// let mut context = Context::default();\n///\n/// // Populate the script definition to the context.\n/// context.eval(Source::from_bytes(script)).unwrap();\n///\n/// // Create an object that can be used in eval calls.\n/// let arg = ObjectInitializer::new(&mut context)\n///     .property(js_string!(\"x\"), 12, Attribute::READONLY)\n///     .build();\n/// context\n///     .register_global_property(js_string!(\"arg\"), arg, Attribute::all())\n///     .expect(\"property shouldn't exist\");\n///\n/// let value = context.eval(Source::from_bytes(\"test(arg)\")).unwrap();\n///\n/// assert_eq!(value.as_number(), Some(12.0))\n/// ```\npub struct Context {\n    /// String interner in the context.\n    interner: Interner,\n\n    /// Execute in strict mode,\n    strict: bool,\n\n    /// Number of instructions remaining before a forced exit\n    #[cfg(feature = \"fuzz\")]\n    pub(crate) instructions_remaining: usize,\n\n    pub(crate) vm: Vm,\n\n    pub(crate) kept_alive: Vec<JsObject>,\n\n    can_block: bool,\n\n    #[cfg(any(feature = \"temporal\", feature = \"intl\"))]\n    timezone_provider: Box<dyn TimeZoneProvider>,\n\n    /// Intl data provider.\n    #[cfg(feature = \"intl\")]\n    intl_provider: icu::IntlProvider,\n\n    host_hooks: Rc<dyn HostHooks>,\n\n    clock: Rc<dyn Clock>,\n\n    job_executor: Rc<dyn JobExecutor>,\n\n    module_loader: Rc<dyn DynModuleLoader>,\n\n    optimizer_options: OptimizerOptions,\n    root_shape: RootShape,\n\n    /// Unique identifier for each parser instance used during the context lifetime.\n    parser_identifier: u32,\n\n    data: HostDefined,\n}\n\nimpl std::fmt::Debug for Context {\n    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {\n        let mut debug = f.debug_struct(\"Context\");\n\n        debug\n            .field(\"realm\", &self.vm.frame().realm)\n            .field(\"interner\", &self.interner)\n            .field(\"vm\", &self.vm)\n            .field(\"strict\", &self.strict)\n            .field(\"job_executor\", &\"JobExecutor\")\n            .field(\"hooks\", &\"HostHooks\")\n            .field(\"clock\", &\"Clock\")\n            .field(\"module_loader\", &\"ModuleLoader\")\n            .field(\"optimizer_options\", &self.optimizer_options);\n\n        #[cfg(feature = \"intl\")]\n        debug.field(\"intl_provider\", &self.intl_provider);\n\n        // TODO: Support TimeZoneProvider debug names\n        #[cfg(feature = \"temporal\")]\n        debug.field(\"timezone_provider\", &\"TimeZoneProvider\");\n\n        debug.finish_non_exhaustive()\n    }\n}\n\nimpl Drop for Context {\n    fn drop(&mut self) {\n        if !self.can_block {\n            CANNOT_BLOCK_COUNTER.set(CANNOT_BLOCK_COUNTER.get() - 1);\n        }\n    }\n}\n\nimpl Default for Context {\n    fn default() -> Self {\n        ContextBuilder::default()\n            .build()\n            .expect(\"Building the default context should not fail\")\n    }\n}\n\n// ==== Public API ====\nimpl Context {\n    /// Create a new [`ContextBuilder`] to specify the [`Interner`] and/or\n    /// the icu data provider.\n    #[must_use]\n    pub fn builder() -> ContextBuilder {\n        ContextBuilder::default()\n    }\n\n    /// Evaluates the given source by compiling down to bytecode, then interpreting the\n    /// bytecode into a value.\n    ///\n    /// # Examples\n    /// ```\n    /// # use boa_engine::{Context, Source};\n    /// let mut context = Context::default();\n    ///\n    /// let source = Source::from_bytes(\"1 + 3\");\n    /// let value = context.eval(source).unwrap();\n    ///\n    /// assert!(value.is_number());\n    /// assert_eq!(value.as_number().unwrap(), 4.0);\n    /// ```\n    ///\n    /// Note that this won't run any scheduled promise jobs; you need to call [`Context::run_jobs`]\n    /// on the context or [`JobExecutor::run_jobs`] on the provided queue to run them.\n    #[allow(clippy::unit_arg, dropping_copy_types)]\n    pub fn eval<R: ReadChar>(&mut self, src: Source<'_, R>) -> JsResult<JsValue> {\n        Script::parse(src, None, self)?.evaluate(self)\n    }\n\n    /// Applies optimizations to the [`StatementList`] inplace.\n    pub fn optimize_statement_list(\n        &mut self,\n        statement_list: &mut StatementList,\n    ) -> OptimizerStatistics {\n        let mut optimizer = Optimizer::new(self);\n        optimizer.apply(statement_list)\n    }\n\n    /// Register a global property.\n    ///\n    /// It will return an error if the property is already defined.\n    ///\n    /// # Example\n    /// ```\n    /// use boa_engine::{\n    ///     Context, js_string,\n    ///     object::ObjectInitializer,\n    ///     property::{Attribute, PropertyDescriptor},\n    /// };\n    ///\n    /// let mut context = Context::default();\n    ///\n    /// context\n    ///     .register_global_property(\n    ///         js_string!(\"myPrimitiveProperty\"),\n    ///         10,\n    ///         Attribute::all(),\n    ///     )\n    ///     .expect(\"property shouldn't exist\");\n    ///\n    /// let object = ObjectInitializer::new(&mut context)\n    ///     .property(js_string!(\"x\"), 0, Attribute::all())\n    ///     .property(js_string!(\"y\"), 1, Attribute::all())\n    ///     .build();\n    /// context\n    ///     .register_global_property(\n    ///         js_string!(\"myObjectProperty\"),\n    ///         object,\n    ///         Attribute::all(),\n    ///     )\n    ///     .expect(\"property shouldn't exist\");\n    /// ```\n    pub fn register_global_property<K, V>(\n        &mut self,\n        key: K,\n        value: V,\n        attribute: Attribute,\n    ) -> JsResult<()>\n    where\n        K: Into<PropertyKey>,\n        V: Into<JsValue>,\n    {\n        self.global_object().define_property_or_throw(\n            key,\n            PropertyDescriptor::builder()\n                .value(value)\n                .writable(attribute.writable())\n                .enumerable(attribute.enumerable())\n                .configurable(attribute.configurable()),\n            self,\n        )?;\n        Ok(())\n    }\n\n    /// Register a global native callable.\n    ///\n    /// The function will be both `constructable` (call with `new <name>()`) and `callable` (call\n    /// with `<name>()`).\n    ///\n    /// The function will be bound to the global object with `writable`, `non-enumerable`\n    /// and `configurable` attributes. The same as when you create a function in JavaScript.\n    ///\n    /// # Note\n    ///\n    /// If you wish to only create the function object without binding it to the global object, you\n    /// can use the [`FunctionObjectBuilder`] API.\n    pub fn register_global_callable(\n        &mut self,\n        name: JsString,\n        length: usize,\n        body: NativeFunction,\n    ) -> JsResult<()> {\n        let function = FunctionObjectBuilder::new(self.realm(), body)\n            .name(name.clone())\n            .length(length)\n            .constructor(true)\n            .build();\n\n        self.global_object().define_property_or_throw(\n            name,\n            PropertyDescriptor::builder()\n                .value(function)\n                .writable(true)\n                .enumerable(false)\n                .configurable(true),\n            self,\n        )?;\n        Ok(())\n    }\n\n    /// Register a global native function that is not a constructor.\n    ///\n    /// The function will be bound to the global object with `writable`, `non-enumerable`\n    /// and `configurable` attributes. The same as when you create a function in JavaScript.\n    ///\n    /// # Note\n    ///\n    /// The difference to [`Context::register_global_callable`] is, that the function will not be\n    /// `constructable`. Usage of the function as a constructor will produce a `TypeError`.\n    pub fn register_global_builtin_callable(\n        &mut self,\n        name: JsString,\n        length: usize,\n        body: NativeFunction,\n    ) -> JsResult<()> {\n        let function = FunctionObjectBuilder::new(self.realm(), body)\n            .name(name.clone())\n            .length(length)\n            .constructor(false)\n            .build();\n\n        self.global_object().define_property_or_throw(\n            name,\n            PropertyDescriptor::builder()\n                .value(function)\n                .writable(true)\n                .enumerable(false)\n                .configurable(true),\n            self,\n        )?;\n        Ok(())\n    }\n\n    /// Registers a global class `C` in the currently active realm.\n    ///\n    /// Errors if the class has already been registered.\n    ///\n    /// # Example\n    /// ```ignore\n    /// #[derive(Debug, Trace, Finalize)]\n    /// struct MyClass;\n    ///\n    /// impl Class for MyClass {\n    ///    // ...\n    /// }\n    ///\n    /// context.register_global_class::<MyClass>()?;\n    /// ```\n    pub fn register_global_class<C: Class>(&mut self) -> JsResult<()> {\n        if self.realm().has_class::<C>() {\n            return Err(JsNativeError::typ()\n                .with_message(\"cannot register a class twice\")\n                .into());\n        }\n\n        let mut class_builder = ClassBuilder::new::<C>(self);\n        C::init(&mut class_builder)?;\n\n        let class = class_builder.build();\n        let property = PropertyDescriptor::builder()\n            .value(class.constructor())\n            .writable(C::ATTRIBUTES.writable())\n            .enumerable(C::ATTRIBUTES.enumerable())\n            .configurable(C::ATTRIBUTES.configurable());\n\n        self.global_object()\n            .define_property_or_throw(js_string!(C::NAME), property, self)?;\n        self.realm().register_class::<C>(class);\n\n        Ok(())\n    }\n\n    /// Removes the global class `C` from the currently active realm, returning the constructor\n    /// and prototype of the class if `C` was registered.\n    ///\n    /// # Note\n    ///\n    /// This makes the constructor return an error on further calls, but note that this won't protect\n    /// static properties from being accessed within variables that stored the constructor before being\n    /// unregistered.  If you need that functionality, you can use a static accessor that first checks\n    /// if the class is registered ([`Context::has_global_class`]) before returning the static value.\n    ///\n    /// # Example\n    /// ```ignore\n    /// #[derive(Debug, Trace, Finalize)]\n    /// struct MyClass;\n    ///\n    /// impl Class for MyClass {\n    ///    // ...\n    /// }\n    ///\n    /// context.register_global_class::<MyClass>()?;\n    /// // ... code\n    /// context.unregister_global_class::<MyClass>()?;\n    /// ```\n    pub fn unregister_global_class<C: Class>(&mut self) -> JsResult<Option<StandardConstructor>> {\n        self.global_object()\n            .delete_property_or_throw(js_string!(C::NAME), self)?;\n        Ok(self.realm().unregister_class::<C>())\n    }\n\n    /// Checks if the currently active realm has the global class `C` registered.\n    #[must_use]\n    pub fn has_global_class<C: Class>(&self) -> bool {\n        self.realm().has_class::<C>()\n    }\n\n    /// Gets the constructor and prototype of the global class `C` if the currently active realm has\n    /// that class registered.\n    #[must_use]\n    pub fn get_global_class<C: Class>(&self) -> Option<StandardConstructor> {\n        self.realm().get_class::<C>()\n    }\n\n    /// Gets the string interner.\n    #[inline]\n    #[must_use]\n    pub const fn interner(&self) -> &Interner {\n        &self.interner\n    }\n\n    /// Gets a mutable reference to the string interner.\n    #[inline]\n    pub fn interner_mut(&mut self) -> &mut Interner {\n        &mut self.interner\n    }\n\n    /// Returns the global object.\n    #[inline]\n    #[must_use]\n    pub fn global_object(&self) -> JsObject {\n        self.vm.frame().realm.global_object().clone()\n    }\n\n    /// Returns the currently active intrinsic constructors and objects.\n    #[inline]\n    #[must_use]\n    pub fn intrinsics(&self) -> &Intrinsics {\n        self.vm.frame().realm.intrinsics()\n    }\n\n    /// Returns the amount of remaining instructions to be executed\n    #[cfg(feature = \"fuzz\")]\n    #[inline]\n    #[must_use]\n    pub fn instructions_remaining(&self) -> usize {\n        self.instructions_remaining\n    }\n\n    /// Returns the currently active realm.\n    #[inline]\n    #[must_use]\n    pub fn realm(&self) -> &Realm {\n        &self.vm.frame().realm\n    }\n\n    /// Set the value of trace on the context\n    #[cfg(feature = \"trace\")]\n    #[inline]\n    pub fn set_trace(&mut self, trace: bool) {\n        self.vm.trace = trace;\n    }\n\n    /// Get optimizer options.\n    #[inline]\n    #[must_use]\n    pub const fn optimizer_options(&self) -> OptimizerOptions {\n        self.optimizer_options\n    }\n    /// Enable or disable optimizations\n    #[inline]\n    pub fn set_optimizer_options(&mut self, optimizer_options: OptimizerOptions) {\n        self.optimizer_options = optimizer_options;\n    }\n\n    /// Changes the strictness mode of the context.\n    #[inline]\n    pub fn strict(&mut self, strict: bool) {\n        self.strict = strict;\n    }\n\n    /// Enqueues a [`Job`] on the [`JobExecutor`].\n    #[inline]\n    pub fn enqueue_job(&mut self, job: Job) {\n        self.job_executor().enqueue_job(job, self);\n    }\n\n    /// Runs all the jobs with the provided job executor.\n    #[inline]\n    pub fn run_jobs(&mut self) -> JsResult<()> {\n        self.job_executor().run_jobs(self)\n    }\n\n    /// Abstract operation [`ClearKeptObjects`][clear].\n    ///\n    /// Clears all objects maintained alive by calls to the [`AddToKeptObjects`][add] abstract\n    /// operation, used within the [`WeakRef`][weak] constructor.\n    ///\n    /// [clear]: https://tc39.es/ecma262/multipage/executable-code-and-execution-contexts.html#sec-clear-kept-objects\n    /// [add]: https://tc39.es/ecma262/multipage/executable-code-and-execution-contexts.html#sec-addtokeptobjects\n    /// [weak]: https://tc39.es/ecma262/multipage/managing-memory.html#sec-weak-ref-objects\n    #[inline]\n    pub fn clear_kept_objects(&mut self) {\n        self.kept_alive.clear();\n    }\n\n    /// Retrieves the current stack trace of the context.\n    ///\n    /// The stack trace is returned ordered with the most recent frames first.\n    #[inline]\n    pub fn stack_trace(&self) -> impl Iterator<Item = &CallFrame> {\n        // The first frame is always a dummy frame (see `Vm` implementation for more details),\n        // so skip the dummy frame and return the reversed list so that the most recent frames are first.\n        self.vm.frames.iter().skip(1).rev()\n    }\n\n    /// Replaces the currently active realm with `realm`, and returns the old realm.\n    #[inline]\n    pub fn enter_realm(&mut self, realm: Realm) -> Realm {\n        std::mem::replace(&mut self.vm.frame_mut().realm, realm)\n    }\n\n    /// Create a new Realm with the default global bindings.\n    pub fn create_realm(&mut self) -> JsResult<Realm> {\n        let realm = Realm::create(self.host_hooks.as_ref(), &self.root_shape)?;\n\n        let old_realm = self.enter_realm(realm);\n\n        builtins::set_default_global_bindings(self)?;\n\n        Ok(self.enter_realm(old_realm))\n    }\n\n    /// Get the [`RootShape`].\n    #[inline]\n    #[must_use]\n    pub const fn root_shape(&self) -> &RootShape {\n        &self.root_shape\n    }\n\n    /// Gets the host hooks.\n    #[inline]\n    #[must_use]\n    pub fn host_hooks(&self) -> Rc<dyn HostHooks> {\n        self.host_hooks.clone()\n    }\n\n    /// Gets the internal clock.\n    #[inline]\n    #[must_use]\n    pub fn clock(&self) -> &dyn Clock {\n        self.clock.as_ref()\n    }\n\n    /// Gets the current job executor, or `None` if the current job executor\n    /// is not a `T`.\n    #[inline]\n    #[must_use]\n    pub fn downcast_job_executor<T: 'static>(&self) -> Option<Rc<T>> {\n        Rc::downcast(self.job_executor.clone()).ok()\n    }\n\n    /// Gets the current module loader, or `None` if the current module loader\n    /// is not a `T`.\n    #[must_use]\n    pub fn downcast_module_loader<T: 'static>(&self) -> Option<Rc<T>> {\n        Rc::downcast(self.module_loader.clone()).ok()\n    }\n\n    /// Get the [`RuntimeLimits`].\n    #[inline]\n    #[must_use]\n    pub const fn runtime_limits(&self) -> RuntimeLimits {\n        self.vm.runtime_limits\n    }\n\n    /// Set the [`RuntimeLimits`].\n    #[inline]\n    pub fn set_runtime_limits(&mut self, runtime_limits: RuntimeLimits) {\n        self.vm.runtime_limits = runtime_limits;\n    }\n\n    /// Get a mutable reference to the [`RuntimeLimits`].\n    #[inline]\n    pub fn runtime_limits_mut(&mut self) -> &mut RuntimeLimits {\n        &mut self.vm.runtime_limits\n    }\n\n    /// Returns `true` if this context can be suspended by an `Atomics.wait` call.\n    #[inline]\n    #[must_use]\n    pub fn can_block(&self) -> bool {\n        self.can_block\n    }\n\n    /// Insert a type into the context-specific [`HostDefined`] field.\n    #[inline]\n    pub fn insert_data<T: NativeObject>(&mut self, value: T) -> Option<Box<T>> {\n        self.data.insert(value)\n    }\n\n    /// Check if the context-specific [`HostDefined`] has type T.\n    #[inline]\n    #[must_use]\n    pub fn has_data<T: NativeObject>(&self) -> bool {\n        self.data.has::<T>()\n    }\n\n    /// Remove type T from the context-specific [`HostDefined`], if it exists.\n    #[inline]\n    pub fn remove_data<T: NativeObject>(&mut self) -> Option<Box<T>> {\n        self.data.remove::<T>()\n    }\n\n    /// Get type T from the context-specific [`HostDefined`], if it exists.\n    #[inline]\n    #[must_use]\n    pub fn get_data<T: NativeObject>(&self) -> Option<&T> {\n        self.data.get::<T>()\n    }\n}\n\n// ==== Private API ====\n\nimpl Context {\n    /// Gets the current job executor.\n    pub(crate) fn job_executor(&self) -> Rc<dyn JobExecutor> {\n        self.job_executor.clone()\n    }\n\n    /// Gets the current module loader.\n    pub(crate) fn module_loader(&self) -> Rc<dyn DynModuleLoader> {\n        self.module_loader.clone()\n    }\n\n    /// Swaps the currently active realm with `realm`.\n    pub(crate) fn swap_realm(&mut self, realm: &mut Realm) {\n        std::mem::swap(&mut self.vm.frame_mut().realm, realm);\n    }\n\n    /// Increment and get the parser identifier.\n    pub(crate) fn next_parser_identifier(&mut self) -> u32 {\n        self.parser_identifier += 1;\n        self.parser_identifier\n    }\n\n    /// `CanDeclareGlobalFunction ( N )`\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-candeclareglobalfunction\n    pub(crate) fn can_declare_global_function(&mut self, name: &JsString) -> JsResult<bool> {\n        // 1. Let ObjRec be envRec.[[ObjectRecord]].\n        // 2. Let globalObject be ObjRec.[[BindingObject]].\n        let global_object = self.realm().global_object().clone();\n\n        // 3. Let existingProp be ? globalObject.[[GetOwnProperty]](N).\n        let name = name.clone().into();\n        let existing_prop = global_object.__get_own_property__(&name, &mut self.into())?;\n\n        // 4. If existingProp is undefined, return ? IsExtensible(globalObject).\n        let Some(existing_prop) = existing_prop else {\n            return global_object.is_extensible(self);\n        };\n\n        // 5. If existingProp.[[Configurable]] is true, return true.\n        if existing_prop.configurable() == Some(true) {\n            return Ok(true);\n        }\n\n        // 6. If IsDataDescriptor(existingProp) is true and existingProp has attribute values { [[Writable]]: true, [[Enumerable]]: true }, return true.\n        if existing_prop.is_data_descriptor()\n            && existing_prop.writable() == Some(true)\n            && existing_prop.enumerable() == Some(true)\n        {\n            return Ok(true);\n        }\n\n        // 7. Return false.\n        Ok(false)\n    }\n\n    /// `CanDeclareGlobalVar ( N )`\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-candeclareglobalvar\n    pub(crate) fn can_declare_global_var(&mut self, name: &JsString) -> JsResult<bool> {\n        // 1. Let ObjRec be envRec.[[ObjectRecord]].\n        // 2. Let globalObject be ObjRec.[[BindingObject]].\n        let global_object = self.realm().global_object().clone();\n\n        // 3. Let hasProperty be ? HasOwnProperty(globalObject, N).\n        let has_property = global_object.has_own_property(name.clone(), self)?;\n\n        // 4. If hasProperty is true, return true.\n        if has_property {\n            return Ok(true);\n        }\n\n        // 5. Return ? IsExtensible(globalObject).\n        global_object.is_extensible(self)\n    }\n\n    /// `CreateGlobalVarBinding ( N, D )`\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-createglobalvarbinding\n    pub(crate) fn create_global_var_binding(\n        &mut self,\n        name: JsString,\n        configurable: bool,\n    ) -> JsResult<()> {\n        // 1. Let ObjRec be envRec.[[ObjectRecord]].\n        // 2. Let globalObject be ObjRec.[[BindingObject]].\n        let global_object = self.realm().global_object().clone();\n\n        // 3. Let hasProperty be ? HasOwnProperty(globalObject, N).\n        let has_property = global_object.has_own_property(name.clone(), self)?;\n\n        // 4. Let extensible be ? IsExtensible(globalObject).\n        let extensible = global_object.is_extensible(self)?;\n\n        // 5. If hasProperty is false and extensible is true, then\n        if !has_property && extensible {\n            // a. Perform ? ObjRec.CreateMutableBinding(N, D).\n            // b. Perform ? ObjRec.InitializeBinding(N, undefined).\n            global_object.define_property_or_throw(\n                name,\n                PropertyDescriptor::builder()\n                    .value(JsValue::undefined())\n                    .writable(true)\n                    .enumerable(true)\n                    .configurable(configurable)\n                    .build(),\n                self,\n            )?;\n        }\n\n        // 6. If envRec.[[VarNames]] does not contain N, then\n        //     a. Append N to envRec.[[VarNames]].\n        // 7. Return unused.\n        Ok(())\n    }\n\n    /// `CreateGlobalFunctionBinding ( N, V, D )`\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-createglobalfunctionbinding\n    pub(crate) fn create_global_function_binding(\n        &mut self,\n        name: JsString,\n        function: JsObject,\n        configurable: bool,\n    ) -> JsResult<()> {\n        // 1. Let ObjRec be envRec.[[ObjectRecord]].\n        // 2. Let globalObject be ObjRec.[[BindingObject]].\n        let global_object = self.realm().global_object().clone();\n\n        // 3. Let existingProp be ? globalObject.[[GetOwnProperty]](N).\n        let existing_prop =\n            global_object.__get_own_property__(&name.clone().into(), &mut self.into())?;\n\n        // 4. If existingProp is undefined or existingProp.[[Configurable]] is true, then\n        let desc = if existing_prop.is_none()\n            || existing_prop.and_then(|p| p.configurable()) == Some(true)\n        {\n            // a. Let desc be the PropertyDescriptor { [[Value]]: V, [[Writable]]: true, [[Enumerable]]: true, [[Configurable]]: D }.\n            PropertyDescriptor::builder()\n                .value(function.clone())\n                .writable(true)\n                .enumerable(true)\n                .configurable(configurable)\n                .build()\n        }\n        // 5. Else,\n        else {\n            // a. Let desc be the PropertyDescriptor { [[Value]]: V }.\n            PropertyDescriptor::builder()\n                .value(function.clone())\n                .build()\n        };\n\n        // 6. Perform ? DefinePropertyOrThrow(globalObject, N, desc).\n        global_object.define_property_or_throw(name.clone(), desc, self)?;\n\n        // 7. Perform ? Set(globalObject, N, V, false).\n        global_object.set(name, function, false, self)?;\n\n        // 8. If envRec.[[VarNames]] does not contain N, then\n        //     a. Append N to envRec.[[VarNames]].\n        // 9. Return unused.\n        Ok(())\n    }\n\n    /// `HasRestrictedGlobalProperty ( N )`\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-hasrestrictedglobalproperty\n    pub(crate) fn has_restricted_global_property(&mut self, name: &JsString) -> JsResult<bool> {\n        // 1. Let ObjRec be envRec.[[ObjectRecord]].\n        // 2. Let globalObject be ObjRec.[[BindingObject]].\n        let global_object = self.realm().global_object().clone();\n\n        // 3. Let existingProp be ? globalObject.[[GetOwnProperty]](N).\n        let name = name.clone().into();\n        let existing_prop = global_object.__get_own_property__(&name, &mut self.into())?;\n\n        // 4. If existingProp is undefined, return false.\n        let Some(existing_prop) = existing_prop else {\n            return Ok(false);\n        };\n\n        // 5. If existingProp.[[Configurable]] is true, return false.\n        if existing_prop.configurable() == Some(true) {\n            return Ok(false);\n        }\n\n        // 6. Return true.\n        Ok(true)\n    }\n\n    /// Returns `true` if this context is in strict mode.\n    pub(crate) const fn is_strict(&self) -> bool {\n        self.strict\n    }\n\n    /// `9.4.1 GetActiveScriptOrModule ( )`\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-getactivescriptormodule\n    #[must_use]\n    pub fn get_active_script_or_module(&self) -> Option<ActiveRunnable> {\n        // 1. If the execution context stack is empty, return null.\n        // 2. Let ec be the topmost execution context on the execution context stack whose ScriptOrModule component is not null.\n        // 3. If no such execution context exists, return null. Otherwise, return ec's ScriptOrModule.\n        if let Some(active_runnable) = &self.vm.frame().active_runnable {\n            return Some(active_runnable.clone());\n        }\n\n        self.vm\n            .frames\n            .iter()\n            .rev()\n            .find_map(|frame| frame.active_runnable.clone())\n    }\n\n    /// Get `active function object`\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#active-function-object\n    pub(crate) fn active_function_object(&self) -> Option<JsObject> {\n        if self.vm.native_active_function.is_some() {\n            return self.vm.native_active_function.clone();\n        }\n\n        self.vm.stack.get_function(self.vm.frame())\n    }\n\n    /// Creates all globals required to evaluate `codeblock`.\n    ///\n    /// This is the common path of the instantiations:\n    /// - `EvalDeclarationInstantiation ( body, varEnv, lexEnv, privateEnv, strict )`\n    /// - `GlobalDeclarationInstantiation ( script, env )`\n    fn create_globals(\n        &mut self,\n        codeblock: &CodeBlock,\n        configurable_globals: bool,\n    ) -> JsResult<()> {\n        // 8. For each element d of varDeclarations, in reverse List order, do\n        //    a. If d is not either a VariableDeclaration, a ForBinding, or a BindingIdentifier, then\n        //       i. Assert: d is either a FunctionDeclaration, a GeneratorDeclaration, an AsyncFunctionDeclaration, or an AsyncGeneratorDeclaration.\n        //       ii. NOTE: If there are multiple function declarations for the same name, the last declaration is used.\n        //       iii. Let fn be the sole element of the BoundNames of d.\n        for fun in codeblock.global_fns.iter().rev() {\n            // ...\n            // 1. Let fnDefinable be ? env.CanDeclareGlobalFunction(fn).\n            // 2. If fnDefinable is false, throw a TypeError exception.\n            let name = codeblock.constant_string(fun.name_index as usize);\n            if !self.can_declare_global_function(&name)? {\n                return Err(js_error!(TypeError: \"cannot declare global function\"));\n            }\n        }\n\n        // 10. For each element d of varDeclarations, do\n        for global_var in &codeblock.global_vars {\n            // ...\n            // a. Let vnDefinable be ? env.CanDeclareGlobalVar(vn).\n            // b. If vnDefinable is false, throw a TypeError exception.\n            let name = codeblock.constant_string(*global_var as usize);\n            if !self.can_declare_global_var(&name)? {\n                return Err(js_error!(TypeError: \"cannot declare global variable\"));\n            }\n        }\n\n        // 16. For each Parse Node f of functionsToInitialize, do\n        for fun in &codeblock.global_fns {\n            // ...\n            // c. Perform ? env.CreateGlobalFunctionBinding(fn, fo, false).\n            let function = create_function_object_fast(\n                codeblock.constant_function(fun.function_index as usize),\n                self,\n            );\n            let name = codeblock.constant_string(fun.name_index as usize);\n            self.create_global_function_binding(name, function, configurable_globals)?;\n        }\n\n        // 17. For each String vn of declaredVarNames, do\n        for global_declared_var in &codeblock.global_vars {\n            // a. Perform ? env.CreateGlobalVarBinding(vn, false).\n            let name = codeblock.constant_string(*global_declared_var as usize);\n            self.create_global_var_binding(name, configurable_globals)?;\n        }\n\n        Ok(())\n    }\n\n    /// `GlobalDeclarationInstantiation ( script, env )`\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-globaldeclarationinstantiation\n    pub(crate) fn global_declaration_instantiation(\n        &mut self,\n        codeblock: &CodeBlock,\n    ) -> JsResult<()> {\n        // 3. For each element name of lexNames, do\n        for global_lex in &codeblock.global_lexs {\n            // c. Let hasRestrictedGlobal be ? env.HasRestrictedGlobalProperty(name).\n            // d. If hasRestrictedGlobal is true, throw a SyntaxError exception.\n            let name = codeblock.constant_string(*global_lex as usize);\n            if self.has_restricted_global_property(&name)? {\n                return Err(\n                    js_error!(SyntaxError: \"cannot redefine non-configurable global property\"),\n                );\n            }\n        }\n\n        self.create_globals(codeblock, false)\n    }\n\n    /// `EvalDeclarationInstantiation ( body, varEnv, lexEnv, privateEnv, strict )`\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-evaldeclarationinstantiation\n    pub(crate) fn eval_declaration_instantiation(&mut self, codeblock: &CodeBlock) -> JsResult<()> {\n        self.create_globals(codeblock, true)\n    }\n}\n\nimpl Context {\n    /// Creates a `ContextCleanupGuard` that executes some cleanup after being dropped.\n    pub(crate) fn guard<F>(&mut self, cleanup: F) -> ContextCleanupGuard<'_, F>\n    where\n        F: FnOnce(&mut Context) + 'static,\n    {\n        ContextCleanupGuard::new(self, cleanup)\n    }\n\n    /// Get the Intl data provider.\n    #[cfg(feature = \"intl\")]\n    pub(crate) const fn intl_provider(&self) -> &icu::IntlProvider {\n        &self.intl_provider\n    }\n\n    /// Get the Time Zone Provider\n    #[cfg(feature = \"temporal\")]\n    pub(crate) fn timezone_provider(&self) -> &dyn TimeZoneProvider {\n        self.timezone_provider.as_ref()\n    }\n}\n\n/// Builder for the [`Context`] type.\n///\n/// This builder allows custom initialization of the [`Interner`] within\n/// the context.\n#[derive(Default)]\npub struct ContextBuilder {\n    interner: Option<Interner>,\n    host_hooks: Option<Rc<dyn HostHooks>>,\n    clock: Option<Rc<dyn Clock>>,\n    job_executor: Option<Rc<dyn JobExecutor>>,\n    module_loader: Option<Rc<dyn DynModuleLoader>>,\n    can_block: bool,\n    #[cfg(feature = \"intl\")]\n    icu: Option<icu::IntlProvider>,\n    #[cfg(feature = \"temporal\")]\n    timezone_provider: Option<Box<dyn TimeZoneProvider>>,\n    #[cfg(feature = \"fuzz\")]\n    instructions_remaining: usize,\n}\n\nimpl std::fmt::Debug for ContextBuilder {\n    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {\n        #[derive(Clone, Copy, Debug)]\n        struct JobExecutor;\n        #[derive(Clone, Copy, Debug)]\n        struct HostHooks;\n        #[derive(Clone, Copy, Debug)]\n        struct Clock;\n        #[derive(Clone, Copy, Debug)]\n        struct ModuleLoader;\n\n        let mut out = f.debug_struct(\"ContextBuilder\");\n\n        out.field(\"interner\", &self.interner)\n            .field(\"host_hooks\", &self.host_hooks.as_ref().map(|_| HostHooks))\n            .field(\"clock\", &self.clock.as_ref().map(|_| Clock))\n            .field(\n                \"job_executor\",\n                &self.job_executor.as_ref().map(|_| JobExecutor),\n            )\n            .field(\n                \"module_loader\",\n                &self.module_loader.as_ref().map(|_| ModuleLoader),\n            )\n            .field(\"can_block\", &self.can_block);\n\n        #[cfg(feature = \"intl\")]\n        out.field(\"icu\", &self.icu);\n\n        #[cfg(feature = \"temporal\")]\n        out.field(\n            \"timezone_provider\",\n            &self.timezone_provider.as_ref().map(|_| \"TimeZoneProvider\"),\n        );\n\n        #[cfg(feature = \"fuzz\")]\n        out.field(\"instructions_remaining\", &self.instructions_remaining);\n\n        out.finish()\n    }\n}\n\nimpl ContextBuilder {\n    /// Creates a new [`ContextBuilder`] with a default empty [`Interner`]\n    /// and a default `BoaProvider` if the `intl` feature is enabled.\n    #[must_use]\n    pub fn new() -> Self {\n        Self::default()\n    }\n\n    /// Initializes the context [`Interner`] to the provided interner.\n    ///\n    /// This is useful when you want to initialize an [`Interner`] with\n    /// a collection of words before parsing.\n    #[must_use]\n    #[allow(clippy::missing_const_for_fn)]\n    pub fn interner(mut self, interner: Interner) -> Self {\n        self.interner = Some(interner);\n        self\n    }\n\n    /// Provides a [`BufferProvider`] data provider to the [`Context`].\n    ///\n    /// This function is only available if the `intl` feature is enabled.\n    ///\n    /// # Additional considerations\n    ///\n    /// If the data was generated using `icu_datagen`, make sure that the deduplication strategy is\n    /// not set to [`Maximal`]. Otherwise, `icu_datagen` will delete base locales such as \"en\" from\n    /// the list of supported locales if the required data for \"en\" is the same as \"und\".\n    /// We recommend [`RetainBaseLanguages`] as a nice default, which will only deduplicate locales\n    /// if the deduplication target is not \"und\".\n    ///\n    /// # Errors\n    ///\n    /// This returns `Err` if the provided provider doesn't have the required locale information\n    /// to construct common tools used through `Intl`. Note that this doesn't\n    /// mean that the provider will successfully construct all `Intl` services; that check is made\n    /// until the creation of an instance of a service.\n    ///\n    /// [`Maximal`]: https://docs.rs/icu_datagen/latest/icu_datagen/enum.DeduplicationStrategy.html#variant.Maximal\n    /// [`RetainBaseLanguages`]: https://docs.rs/icu_datagen/latest/icu_datagen/enum.DeduplicationStrategy.html#variant.RetainBaseLanguages\n    /// [`BufferProvider`]: icu_provider::buf::BufferProvider\n    #[cfg(feature = \"intl\")]\n    pub fn icu_buffer_provider<\n        T: icu_provider::prelude::DynamicDryDataProvider<icu_provider::prelude::BufferMarker>\n            + 'static,\n    >(\n        mut self,\n        provider: T,\n    ) -> Result<Self, IcuError> {\n        self.icu = Some(icu::IntlProvider::try_new_buffer(provider));\n        Ok(self)\n    }\n\n    /// Set the [`timezone_provider::provider::TimeZoneProvider`] that should be used to source\n    /// time zone data.\n    ///\n    /// ## Default\n    ///\n    /// If no time zone provider is provided, a compiled time zone provider will be used\n    /// which includes the time zone data in the binary. This may increase binary sizes\n    /// by up to 200 Kb.\n    #[cfg(feature = \"temporal\")]\n    #[must_use]\n    pub fn timezone_provider<T: TimeZoneProvider + 'static>(mut self, provider: T) -> Self {\n        self.timezone_provider = Some(Box::new(provider));\n        self\n    }\n\n    /// Initializes the [`HostHooks`] for the context.\n    ///\n    /// [`Host Hooks`]: https://tc39.es/ecma262/#sec-host-hooks-summary\n    #[must_use]\n    pub fn host_hooks<H: HostHooks + 'static>(mut self, host_hooks: Rc<H>) -> Self {\n        self.host_hooks = Some(host_hooks);\n        self\n    }\n\n    /// Initializes the [`Clock`] for the context.\n    #[must_use]\n    pub fn clock<C: Clock + 'static>(mut self, clock: Rc<C>) -> Self {\n        self.clock = Some(clock);\n        self\n    }\n\n    /// Initializes the [`JobExecutor`] for the context.\n    #[must_use]\n    pub fn job_executor<Q: JobExecutor + 'static>(mut self, job_executor: Rc<Q>) -> Self {\n        self.job_executor = Some(job_executor);\n        self\n    }\n\n    /// Initializes the [`ModuleLoader`] for the context.\n    #[must_use]\n    pub fn module_loader<M: ModuleLoader + 'static>(mut self, module_loader: Rc<M>) -> Self {\n        self.module_loader = Some(module_loader);\n        self\n    }\n\n    /// [`AgentCanSuspend ( )`][spec] aka `[[CanBlock]]`\n    ///\n    /// Defines if this context can be suspended by calls to the [`Atomics.wait`][wait] function.\n    ///\n    /// # Note\n    ///\n    /// By the specification, multiple agents cannot share the same thread if any of them has its\n    /// `[[CanBlock]]` field set to true. The builder will verify at build time that all contexts on\n    /// the current thread fulfill this requisite.\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-agentcansuspend\n    /// [wait]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Atomics/wait\n    #[must_use]\n    pub const fn can_block(mut self, can_block: bool) -> Self {\n        self.can_block = can_block;\n        self\n    }\n\n    /// Specifies the number of instructions remaining to the [`Context`].\n    ///\n    /// This function is only available if the `fuzz` feature is enabled.\n    #[cfg(feature = \"fuzz\")]\n    #[must_use]\n    pub const fn instructions_remaining(mut self, instructions_remaining: usize) -> Self {\n        self.instructions_remaining = instructions_remaining;\n        self\n    }\n\n    /// Builds a new [`Context`] with the provided parameters, and defaults\n    /// all missing parameters to their default values.\n    // TODO: try to use a custom error here, since most of the `JsError` APIs\n    // require having a `Context` in the first place.\n    pub fn build(self) -> JsResult<Context> {\n        if self.can_block {\n            if CANNOT_BLOCK_COUNTER.get() > 0 {\n                return Err(JsNativeError::typ()\n                    .with_message(\n                        \"a context that can block must be the only active context in its current thread\",\n                    )\n                    .into());\n            }\n        } else {\n            CANNOT_BLOCK_COUNTER.set(CANNOT_BLOCK_COUNTER.get() + 1);\n        }\n\n        let root_shape = RootShape::default();\n\n        let host_hooks = self.host_hooks.unwrap_or(Rc::new(DefaultHooks));\n        let clock = self.clock.unwrap_or_else(|| Rc::new(StdClock::new()));\n        let realm = Realm::create(host_hooks.as_ref(), &root_shape)?;\n        let vm = Vm::new(realm);\n\n        let module_loader: Rc<dyn DynModuleLoader> = if let Some(loader) = self.module_loader {\n            loader\n        } else if let Ok(loader) = SimpleModuleLoader::new(Path::new(\".\")) {\n            Rc::new(loader)\n        } else {\n            Rc::new(IdleModuleLoader)\n        };\n\n        let job_executor = self\n            .job_executor\n            .unwrap_or_else(|| Rc::new(SimpleJobExecutor::new()));\n\n        let mut context = Context {\n            interner: self.interner.unwrap_or_default(),\n            vm,\n            strict: false,\n            #[cfg(feature = \"temporal\")]\n            timezone_provider: if let Some(provider) = self.timezone_provider {\n                provider\n            } else {\n                Box::new(ZeroCompiledTzdbProvider::default())\n            },\n            #[cfg(feature = \"intl\")]\n            intl_provider: if let Some(icu) = self.icu {\n                icu\n            } else {\n                cfg_if::cfg_if! {\n                    if #[cfg(feature = \"intl_bundled\")] {\n                        icu::IntlProvider::try_new_buffer(boa_icu_provider::buffer())\n                    } else {\n                        return Err(JsNativeError::typ()\n                            .with_message(\"missing Intl provider for context\")\n                            .into()\n                        );\n                    }\n                }\n            },\n            #[cfg(feature = \"fuzz\")]\n            instructions_remaining: self.instructions_remaining,\n            kept_alive: Vec::new(),\n            host_hooks,\n            clock,\n            job_executor,\n            module_loader,\n            optimizer_options: OptimizerOptions::OPTIMIZE_ALL,\n            root_shape,\n            parser_identifier: 0,\n            can_block: self.can_block,\n            data: HostDefined::default(),\n        };\n\n        builtins::set_default_global_bindings(&mut context)?;\n\n        Ok(context)\n    }\n}\n\n/// A cleanup guard for a [`Context`] that is executed when dropped.\n#[derive(Debug)]\npub(crate) struct ContextCleanupGuard<'a, F>\nwhere\n    F: FnOnce(&mut Context) + 'static,\n{\n    context: &'a mut Context,\n    cleanup: Option<F>,\n}\n\nimpl<'a, F> ContextCleanupGuard<'a, F>\nwhere\n    F: FnOnce(&mut Context) + 'static,\n{\n    /// Creates a new `ContextCleanupGuard` from the current context and its cleanup operation.\n    pub(crate) fn new(context: &'a mut Context, cleanup: F) -> Self {\n        Self {\n            context,\n            cleanup: Some(cleanup),\n        }\n    }\n}\n\nimpl<F> std::ops::Deref for ContextCleanupGuard<'_, F>\nwhere\n    F: FnOnce(&mut Context) + 'static,\n{\n    type Target = Context;\n\n    fn deref(&self) -> &Self::Target {\n        self.context\n    }\n}\n\nimpl<F> std::ops::DerefMut for ContextCleanupGuard<'_, F>\nwhere\n    F: FnOnce(&mut Context) + 'static,\n{\n    fn deref_mut(&mut self) -> &mut Self::Target {\n        self.context\n    }\n}\n\nimpl<F> Drop for ContextCleanupGuard<'_, F>\nwhere\n    F: FnOnce(&mut Context) + 'static,\n{\n    fn drop(&mut self) {\n        if let Some(cleanup) = self.cleanup.take() {\n            cleanup(self.context);\n        }\n    }\n}\n"
  },
  {
    "path": "core/engine/src/context/time.rs",
    "content": "//! Clock related types and functions.\n\nuse crate::sys::time::Instant;\n\n/// A monotonic instant in time, in the Boa engine.\n///\n/// This type is guaranteed to be monotonic, i.e. if two instants\n/// are compared, the later one will always be greater than the\n/// earlier one.\n///\n/// This mirrors the behavior of [`std::time::Instant`] and represents\n/// a measurement of elapsed time relative to an arbitrary starting point.\n/// It is NOT tied to wall-clock time or the Unix epoch, and system clock\n/// adjustments will not affect it.\n///\n/// This should not be used to keep dates or times, but only to\n/// measure monotonic time progression in the engine.\n#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq, PartialOrd, Ord)]\npub struct JsInstant {\n    /// The duration of time since an arbitrary starting point.\n    inner: std::time::Duration,\n}\n\nimpl JsInstant {\n    /// Creates a new `JsInstant` from the given number of seconds and nanoseconds.\n    #[must_use]\n    pub fn new(secs: u64, nanos: u32) -> Self {\n        let inner = std::time::Duration::new(secs, nanos);\n        Self::new_unchecked(inner)\n    }\n\n    /// Creates a new `JsInstant` from an unchecked duration since the Unix epoch.\n    #[must_use]\n    fn new_unchecked(inner: std::time::Duration) -> Self {\n        Self { inner }\n    }\n\n    /// Returns the number of milliseconds since the clock's starting point.\n    ///\n    /// Note: This is NOT a Unix timestamp. It represents elapsed time\n    /// since an arbitrary starting point and is only meaningful for\n    /// measuring durations and comparing instants.\n    #[must_use]\n    pub fn millis_since_epoch(&self) -> u64 {\n        self.inner.as_millis() as u64\n    }\n\n    /// Returns the number of nanoseconds since the clock's starting point.\n    ///\n    /// Note: This is NOT a Unix timestamp. It represents elapsed time\n    /// since an arbitrary starting point and is only meaningful for\n    /// measuring durations and comparing instants.\n    #[must_use]\n    pub fn nanos_since_epoch(&self) -> u128 {\n        self.inner.as_nanos()\n    }\n}\n\n/// A duration of time, inside the Boa engine.\n#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq, PartialOrd, Ord)]\npub struct JsDuration {\n    inner: std::time::Duration,\n}\n\nimpl JsDuration {\n    /// Creates a new `JsDuration` from the given number of milliseconds.\n    #[must_use]\n    pub fn from_millis(millis: u64) -> Self {\n        Self {\n            inner: std::time::Duration::from_millis(millis),\n        }\n    }\n\n    /// Returns the number of milliseconds in this duration.\n    #[must_use]\n    pub fn as_millis(&self) -> u64 {\n        self.inner.as_millis() as u64\n    }\n\n    /// Returns the number of seconds in this duration.\n    #[must_use]\n    pub fn as_secs(&self) -> u64 {\n        self.inner.as_secs()\n    }\n\n    /// Returns the number of nanoseconds in this duration.\n    #[must_use]\n    pub fn as_nanos(&self) -> u128 {\n        self.inner.as_nanos()\n    }\n}\n\nimpl From<std::time::Duration> for JsDuration {\n    fn from(duration: std::time::Duration) -> Self {\n        Self { inner: duration }\n    }\n}\n\nimpl From<JsDuration> for std::time::Duration {\n    fn from(duration: JsDuration) -> Self {\n        duration.inner\n    }\n}\n\nmacro_rules! impl_duration_ops {\n    ($($trait:ident $trait_fn:ident),*) => {\n        $(\n            impl std::ops::$trait for JsDuration {\n                type Output = JsDuration;\n\n                #[inline]\n                fn $trait_fn(self, rhs: JsDuration) -> Self::Output {\n                    Self {\n                        inner: std::ops::$trait::$trait_fn(self.inner, rhs.inner)\n                    }\n                }\n            }\n            impl std::ops::$trait<JsDuration> for JsInstant {\n                type Output = JsInstant;\n\n                #[inline]\n                fn $trait_fn(self, rhs: JsDuration) -> Self::Output {\n                    Self {\n                        inner: std::ops::$trait::$trait_fn(self.inner, rhs.inner)\n                    }\n                }\n            }\n        )*\n    };\n}\n\nimpl_duration_ops!(Add add, Sub sub);\n\nimpl std::ops::Sub for JsInstant {\n    type Output = JsDuration;\n\n    #[inline]\n    fn sub(self, rhs: JsInstant) -> Self::Output {\n        JsDuration {\n            // saturating preserves the behaviour of std's Instant.\n            inner: self.inner.saturating_sub(rhs.inner),\n        }\n    }\n}\n\n/// Implement a clock that can be used to measure time.\npub trait Clock {\n    /// Returns the current monotonic time.\n    ///\n    /// Implementers must ensure this is monotonic and should be used for measuring\n    /// durations and scheduling timeouts. The engine assumes monotonicity.\n    fn now(&self) -> JsInstant;\n\n    /// Returns the current wall-clock time in milliseconds since the Unix epoch.\n    ///\n    /// This is NOT monotonic and can go backward if the system clock is adjusted.\n    /// It should only be used for `Date` objects and other wall-clock time needs.\n    fn system_time_millis(&self) -> i64;\n}\n\n/// A clock that uses the standard monotonic clock.\n///\n/// This clock is based on the `instant` crate's `Instant` type, which provides\n/// cross-platform monotonic time, including WASM support via `performance.now()`.\n/// Time measurements are relative to an arbitrary starting point\n/// (the first call to `now()`) and are not affected by system clock adjustments.\n///\n/// This ensures that time never goes backward, which is critical for\n/// maintaining the invariants of [`JsInstant`].\n#[derive(Debug, Clone, Copy)]\npub struct StdClock {\n    /// The base instant from which all measurements are relative.\n    base: Instant,\n}\n\nimpl Default for StdClock {\n    fn default() -> Self {\n        Self::new()\n    }\n}\n\nimpl StdClock {\n    /// Creates a new `StdClock` with the current instant as the base.\n    #[must_use]\n    pub fn new() -> Self {\n        Self {\n            base: Instant::now(),\n        }\n    }\n}\n\nimpl Clock for StdClock {\n    fn now(&self) -> JsInstant {\n        let elapsed = self.base.elapsed();\n        JsInstant::new_unchecked(elapsed)\n    }\n\n    fn system_time_millis(&self) -> i64 {\n        let now = std::time::SystemTime::now();\n        let duration = now\n            .duration_since(std::time::UNIX_EPOCH)\n            .expect(\"System clock is before Unix epoch\");\n        duration.as_millis() as i64\n    }\n}\n\n/// A clock that uses a fixed time, useful for testing. The internal time is in milliseconds.\n///\n/// This clock will always return the same time, unless it is moved forward manually. It cannot\n/// be moved backward or set to a specific time.\n#[derive(Debug, Clone, Default)]\npub struct FixedClock(std::cell::RefCell<u64>);\n\nimpl FixedClock {\n    /// Creates a new `FixedClock` from the given number of milliseconds since the Unix epoch.\n    #[must_use]\n    pub fn from_millis(millis: u64) -> Self {\n        Self(std::cell::RefCell::new(millis))\n    }\n\n    /// Move the clock forward by the given number of milliseconds.\n    pub fn forward(&self, millis: u64) {\n        *self.0.borrow_mut() += millis;\n    }\n}\n\nimpl Clock for FixedClock {\n    fn now(&self) -> JsInstant {\n        let millis = *self.0.borrow();\n        JsInstant::new_unchecked(std::time::Duration::new(\n            millis / 1000,\n            ((millis % 1000) * 1_000_000) as u32,\n        ))\n    }\n\n    fn system_time_millis(&self) -> i64 {\n        *self.0.borrow() as i64\n    }\n}\n\n#[test]\nfn basic() {\n    let clock = StdClock::new();\n    let now = clock.now();\n    // Since we're using a relative clock, values are always >= 0 by type\n    let _millis = now.millis_since_epoch();\n    let _nanos = now.nanos_since_epoch();\n\n    let duration = JsDuration::from_millis(1000);\n    let later = now + duration;\n    assert!(later > now);\n\n    // Only subtract if we have enough time elapsed\n    let duration_small = JsDuration::from_millis(100);\n    let later_small = now + duration_small;\n    let earlier = later_small - duration_small;\n    assert_eq!(earlier, now);\n\n    let diff = later - now;\n    assert_eq!(diff.as_millis(), 1000);\n\n    let fixed = FixedClock::from_millis(0);\n    let now2 = fixed.now();\n    assert_eq!(now2.millis_since_epoch(), 0);\n\n    fixed.forward(1000);\n    let now3 = fixed.now();\n    assert_eq!(now3.millis_since_epoch(), 1000);\n    assert!(now3 > now2);\n\n    // End of time.\n    fixed.forward(u64::MAX - 1000);\n    let now4 = fixed.now();\n    assert_eq!(now4.millis_since_epoch(), u64::MAX);\n    assert!(now4 > now3);\n}\n\n#[test]\nfn monotonic_behavior() {\n    let clock = StdClock::new();\n\n    // Verify that time always moves forward\n    let t1 = clock.now();\n    std::thread::sleep(std::time::Duration::from_millis(1));\n    let t2 = clock.now();\n    std::thread::sleep(std::time::Duration::from_millis(1));\n    let t3 = clock.now();\n\n    // Time must always increase\n    assert!(t2 > t1, \"Time must move forward\");\n    assert!(t3 > t2, \"Time must continue moving forward\");\n    assert!(t3 > t1, \"Time must be transitive\");\n\n    // Verify that elapsed time is reasonable\n    let elapsed = t3 - t1;\n    assert!(elapsed.as_millis() >= 2, \"At least 2ms should have elapsed\");\n}\n\n#[test]\nfn clock_independence() {\n    // Each clock instance has its own base instant\n    let clock1 = StdClock::new();\n    std::thread::sleep(std::time::Duration::from_millis(10));\n    let clock2 = StdClock::new();\n\n    let t1 = clock1.now();\n    let t2 = clock2.now();\n\n    // clock1 started earlier, so it should show more elapsed time\n    assert!(t1.millis_since_epoch() >= t2.millis_since_epoch());\n}\n"
  },
  {
    "path": "core/engine/src/environments/mod.rs",
    "content": "//! Boa's implementation of ECMAScript's `Environment Records`.\n//!\n//! Environments contain the bindings of identifiers to their values.\n//! The implementation differs from the methods defined by the specification,\n//! but the resulting behavior should be the same.\n//!\n//! To make the runtime more performant, environment specific behavior is split\n//! between bytecode compilation and the runtime.\n//! While the association of identifiers to values seems like a natural fit for a hashmap,\n//! lookups of the values at runtime are very expensive.\n//! Environments can also have outer environments.\n//! In the worst case, there are as many hashmap lookups, as there are environments.\n//!\n//! To avoid these costs, hashmaps are not used at runtime.\n//! At runtime, environments are represented as fixed size lists of binding values.\n//! The positions of the bindings in these lists is determined at bytecode compile time.\n//!\n//! A binding is uniquely identified by two indices:\n//!  - An environment index, that identifies the environment in which the binding exists\n//!  - A binding index, that identifies the binding in the environment\n//!\n//! More information:\n//!  - [ECMAScript reference][spec]\n//!\n//! [spec]: https://tc39.es/ecma262/#sec-environment-records\n\nmod runtime;\n\npub(crate) use runtime::{\n    DeclarativeEnvironment, Environment, EnvironmentStack, FunctionSlots, PrivateEnvironment,\n    SavedEnvironments, ThisBindingStatus,\n};\n\n#[cfg(test)]\nmod tests;\n"
  },
  {
    "path": "core/engine/src/environments/runtime/declarative/function.rs",
    "content": "use boa_ast::scope::Scope;\nuse boa_gc::{Finalize, GcRefCell, Trace, custom_trace};\n\nuse crate::{JsNativeError, JsObject, JsResult, JsValue, builtins::function::OrdinaryFunction};\n\n#[derive(Debug, Trace, Finalize)]\npub(crate) struct FunctionEnvironment {\n    bindings: GcRefCell<Vec<Option<JsValue>>>,\n    slots: Box<FunctionSlots>,\n\n    // Safety: Nothing in `Scope` needs tracing.\n    #[unsafe_ignore_trace]\n    scope: Scope,\n}\n\nimpl FunctionEnvironment {\n    /// Creates a new `FunctionEnvironment`.\n    pub(crate) fn new(bindings_count: u32, slots: FunctionSlots, scope: Scope) -> Self {\n        Self {\n            bindings: GcRefCell::new(vec![None; bindings_count as usize]),\n            slots: Box::new(slots),\n            scope,\n        }\n    }\n\n    /// Gets the slots of this function environment.\n    pub(crate) const fn slots(&self) -> &FunctionSlots {\n        &self.slots\n    }\n\n    /// Gets the compile time environment of this function environment.\n    pub(crate) const fn compile(&self) -> &Scope {\n        &self.scope\n    }\n\n    /// Gets the binding value from the environment by it's index.\n    ///\n    /// # Panics\n    ///\n    /// Panics if the binding value is out of range or not initialized.\n    #[track_caller]\n    pub(crate) fn get(&self, index: u32) -> Option<JsValue> {\n        self.bindings.borrow()[index as usize].clone()\n    }\n\n    /// Sets the binding value from the environment by index.\n    ///\n    /// # Panics\n    ///\n    /// Panics if the binding value is out of range.\n    #[track_caller]\n    pub(crate) fn set(&self, index: u32, value: JsValue) {\n        self.bindings.borrow_mut()[index as usize] = Some(value);\n    }\n\n    /// Gets the bindings of this poisonable environment.\n    pub(crate) const fn bindings(&self) -> &GcRefCell<Vec<Option<JsValue>>> {\n        &self.bindings\n    }\n\n    /// `BindThisValue`\n    ///\n    /// Sets the given value as the `this` binding of the environment.\n    /// Returns `false` if the `this` binding has already been initialized.\n    ///\n    /// More information:\n    ///  - [ECMAScript specification][spec]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-bindthisvalue\n    pub(crate) fn bind_this_value(&self, value: JsObject) -> JsResult<()> {\n        let mut this = self.slots.this.borrow_mut();\n        match &*this {\n            ThisBindingStatus::Lexical => {\n                unreachable!(\"1. Assert: envRec.[[ThisBindingStatus]] is not lexical.\")\n            }\n            ThisBindingStatus::Initialized(_) => {\n                // 2. If envRec.[[ThisBindingStatus]] is initialized, throw a ReferenceError exception.\n                return Err(JsNativeError::reference()\n                    .with_message(\"cannot reinitialize `this` binding\")\n                    .into());\n            }\n            ThisBindingStatus::Uninitialized => {\n                // 3. Set envRec.[[ThisValue]] to V.\n                // 4. Set envRec.[[ThisBindingStatus]] to initialized.\n                *this = ThisBindingStatus::Initialized(value.into());\n            }\n        }\n\n        // 5. Return V.\n        Ok(())\n    }\n\n    /// `HasSuperBinding`\n    ///\n    /// Returns `true` if the environment has a `super` binding.\n    ///\n    /// More information:\n    ///  - [ECMAScript specification][spec]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-function-environment-records-hassuperbinding\n    ///\n    /// # Panics\n    ///\n    /// Panics if the function object of the environment is not a function.\n    #[track_caller]\n    pub(crate) fn has_super_binding(&self) -> bool {\n        // 1.If envRec.[[ThisBindingStatus]] is lexical, return false.\n        if matches!(&*self.slots.this.borrow(), ThisBindingStatus::Lexical) {\n            return false;\n        }\n\n        // 2. If envRec.[[FunctionObject]].[[HomeObject]] is undefined, return false; otherwise, return true.\n        self.slots\n            .function_object\n            .downcast_ref::<OrdinaryFunction>()\n            .expect(\"function object must be function\")\n            .get_home_object()\n            .is_some()\n    }\n\n    /// `HasThisBinding`\n    ///\n    /// Returns `true` if the environment has a `this` binding.\n    ///\n    /// More information:\n    ///  - [ECMAScript specification][spec]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-function-environment-records-hasthisbinding\n    pub(crate) fn has_this_binding(&self) -> bool {\n        // 1. If envRec.[[ThisBindingStatus]] is lexical, return false; otherwise, return true.\n        !matches!(&*self.slots.this.borrow(), ThisBindingStatus::Lexical)\n    }\n\n    /// `GetThisBinding`\n    ///\n    /// Returns the `this` binding of the current environment.\n    ///\n    /// Differs slightly from the spec where lexical this (arrow functions) doesn't get asserted,\n    /// but instead is returned as `None`.\n    ///\n    /// More information:\n    ///  - [ECMAScript specification][spec]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-function-environment-records-getthisbinding\n    pub(crate) fn get_this_binding(&self) -> JsResult<Option<JsValue>> {\n        match &*self.slots.this.borrow() {\n            ThisBindingStatus::Lexical => Ok(None),\n            // 2. If envRec.[[ThisBindingStatus]] is uninitialized, throw a ReferenceError exception.\n            ThisBindingStatus::Uninitialized => Err(JsNativeError::reference()\n                .with_message(\n                    \"Must call super constructor in derived \\\n                    class before accessing 'this' or returning from derived constructor\",\n                )\n                .into()),\n            // 3. Return envRec.[[ThisValue]].\n            ThisBindingStatus::Initialized(this) => Ok(Some(this.clone())),\n        }\n    }\n}\n\n/// Describes the status of a `this` binding in function environments.\n#[derive(Clone, Debug, Finalize)]\npub(crate) enum ThisBindingStatus {\n    /// Function doesn't have a `this` binding. (arrow functions and async arrow functions)\n    Lexical,\n    /// Function has a `this` binding, but is uninitialized. (derived constructors)\n    Uninitialized,\n    /// Function has an initialized `this` binding. (base constructors and most callable objects)\n    Initialized(JsValue),\n}\n\nunsafe impl Trace for ThisBindingStatus {\n    custom_trace!(this, mark, {\n        match this {\n            Self::Initialized(obj) => mark(obj),\n            Self::Lexical | Self::Uninitialized => {}\n        }\n    });\n}\n\n/// Holds the internal slots of a function environment.\n#[derive(Clone, Debug, Trace, Finalize)]\npub(crate) struct FunctionSlots {\n    /// The `[[ThisValue]]` and `[[ThisBindingStatus]]`  internal slots.\n    this: GcRefCell<ThisBindingStatus>,\n\n    /// The `[[FunctionObject]]` internal slot.\n    function_object: JsObject,\n\n    /// The `[[NewTarget]]` internal slot.\n    new_target: Option<JsObject>,\n}\n\nimpl FunctionSlots {\n    /// Creates a new `FunctionSluts`.\n    pub(crate) fn new(\n        this: ThisBindingStatus,\n        function_object: JsObject,\n        new_target: Option<JsObject>,\n    ) -> Self {\n        Self {\n            this: GcRefCell::new(this),\n            function_object,\n            new_target,\n        }\n    }\n\n    /// Returns the value of the `[[FunctionObject]]` internal slot.\n    pub(crate) const fn function_object(&self) -> &JsObject {\n        &self.function_object\n    }\n\n    /// Returns the value of the `[[NewTarget]]` internal slot.\n    pub(crate) const fn new_target(&self) -> Option<&JsObject> {\n        self.new_target.as_ref()\n    }\n}\n"
  },
  {
    "path": "core/engine/src/environments/runtime/declarative/global.rs",
    "content": "use crate::JsValue;\nuse boa_gc::{Finalize, GcRefCell, Trace};\n\n#[derive(Debug, Trace, Finalize)]\npub(crate) struct GlobalEnvironment {\n    bindings: GcRefCell<Vec<Option<JsValue>>>,\n}\n\nimpl GlobalEnvironment {\n    /// Creates a new `GlobalEnvironment`.\n    pub(crate) fn new() -> Self {\n        Self {\n            bindings: GcRefCell::new(Vec::new()),\n        }\n    }\n\n    /// Gets the binding value from the environment by it's index.\n    ///\n    /// # Panics\n    ///\n    /// Panics if the binding value is out of range or not initialized.\n    #[track_caller]\n    pub(crate) fn get(&self, index: u32) -> Option<JsValue> {\n        self.bindings.borrow()[index as usize].clone()\n    }\n\n    /// Sets the binding value from the environment by index.\n    ///\n    /// # Panics\n    ///\n    /// Panics if the binding value is out of range.\n    #[track_caller]\n    pub(crate) fn set(&self, index: u32, value: JsValue) {\n        self.bindings.borrow_mut()[index as usize] = Some(value);\n    }\n\n    /// Gets the bindings of this poisonable environment.\n    pub(crate) const fn bindings(&self) -> &GcRefCell<Vec<Option<JsValue>>> {\n        &self.bindings\n    }\n}\n"
  },
  {
    "path": "core/engine/src/environments/runtime/declarative/lexical.rs",
    "content": "use boa_gc::{Finalize, GcRefCell, Trace};\n\nuse crate::JsValue;\n\n#[derive(Debug, Trace, Finalize)]\npub(crate) struct LexicalEnvironment {\n    bindings: GcRefCell<Vec<Option<JsValue>>>,\n}\n\nimpl LexicalEnvironment {\n    /// Creates a new `LexicalEnvironment`.\n    pub(crate) fn new(bindings: u32) -> Self {\n        Self {\n            bindings: GcRefCell::new(vec![None; bindings as usize]),\n        }\n    }\n\n    /// Gets the binding value from the environment by it's index.\n    ///\n    /// # Panics\n    ///\n    /// Panics if the binding value is out of range or not initialized.\n    #[track_caller]\n    pub(crate) fn get(&self, index: u32) -> Option<JsValue> {\n        self.bindings.borrow()[index as usize].clone()\n    }\n\n    /// Sets the binding value from the environment by index.\n    ///\n    /// # Panics\n    ///\n    /// Panics if the binding value is out of range.\n    #[track_caller]\n    pub(crate) fn set(&self, index: u32, value: JsValue) {\n        self.bindings.borrow_mut()[index as usize] = Some(value);\n    }\n\n    /// Gets the bindings of this poisonable environment.\n    #[expect(dead_code)]\n    pub(crate) const fn bindings(&self) -> &GcRefCell<Vec<Option<JsValue>>> {\n        &self.bindings\n    }\n}\n"
  },
  {
    "path": "core/engine/src/environments/runtime/declarative/mod.rs",
    "content": "mod function;\nmod global;\nmod lexical;\nmod module;\n\npub(crate) use function::{FunctionEnvironment, FunctionSlots, ThisBindingStatus};\npub(crate) use global::GlobalEnvironment;\npub(crate) use lexical::LexicalEnvironment;\npub(crate) use module::ModuleEnvironment;\n\nuse crate::{JsResult, JsValue};\nuse boa_gc::{Finalize, Trace};\nuse std::cell::Cell;\n\n/// A declarative environment holds binding values at runtime.\n///\n/// Bindings are stored in a fixed size list of optional values.\n/// If a binding is not initialized, the value is `None`.\n///\n/// Optionally, an environment can hold a `this` value.\n/// The `this` value is present only if the environment is a function environment.\n///\n/// Code evaluation at runtime (e.g. the `eval` built-in function) can add\n/// bindings to existing, compiled function environments.\n/// This makes it impossible to determine the location of all bindings at compile time.\n/// To dynamically check for added bindings at runtime, a reference to the\n/// corresponding compile time environment is needed.\n///\n/// Checking all environments for potential added bindings at runtime on every get/set\n/// would offset the performance improvement of determining binding locations at compile time.\n/// To minimize this, each environment holds a `poisoned` flag.\n/// If bindings where added at runtime, the current environment and all inner environments\n/// are marked as poisoned.\n/// All poisoned environments have to be checked for added bindings.\n/// Module Environments are never poisoned as they run in strict mode.\n#[derive(Debug, Trace, Finalize)]\npub(crate) struct DeclarativeEnvironment {\n    kind: DeclarativeEnvironmentKind,\n    #[unsafe_ignore_trace]\n    poisoned: Cell<bool>,\n    with: bool,\n}\n\nimpl DeclarativeEnvironment {\n    /// Creates a new global `DeclarativeEnvironment`.\n    pub(crate) fn global() -> Self {\n        Self {\n            kind: DeclarativeEnvironmentKind::Global(GlobalEnvironment::new()),\n            poisoned: Cell::new(false),\n            with: false,\n        }\n    }\n\n    /// Creates a new `DeclarativeEnvironment` from its kind and compile environment.\n    pub(crate) fn new(kind: DeclarativeEnvironmentKind, poisoned: bool, with: bool) -> Self {\n        Self {\n            kind,\n            poisoned: Cell::new(poisoned),\n            with,\n        }\n    }\n\n    /// Returns a reference to the kind of the environment.\n    pub(crate) const fn kind(&self) -> &DeclarativeEnvironmentKind {\n        &self.kind\n    }\n\n    /// Returns whether this environment is a function environment.\n    pub(crate) fn is_function(&self) -> bool {\n        matches!(self.kind(), DeclarativeEnvironmentKind::Function(_))\n    }\n\n    /// Gets the binding value from the environment by index.\n    ///\n    /// # Panics\n    ///\n    /// Panics if the binding value is out of range or not initialized.\n    #[track_caller]\n    pub(crate) fn get(&self, index: u32) -> Option<JsValue> {\n        self.kind.get(index)\n    }\n\n    /// Sets the binding value from the environment by index.\n    ///\n    /// # Panics\n    ///\n    /// Panics if the binding value is out of range.\n    #[track_caller]\n    pub(crate) fn set(&self, index: u32, value: JsValue) {\n        self.kind.set(index, value);\n    }\n\n    /// `GetThisBinding`\n    ///\n    /// Returns the `this` binding of this environment.\n    ///\n    /// More information:\n    ///  - [ECMAScript specification][spec]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-function-environment-records-getthisbinding\n    pub(crate) fn get_this_binding(&self) -> JsResult<Option<JsValue>> {\n        self.kind.get_this_binding()\n    }\n\n    /// `HasThisBinding`\n    ///\n    /// Returns if the environment has a `this` binding.\n    ///\n    /// More information:\n    ///  - [ECMAScript specification][spec]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-function-environment-records-hasthisbinding\n    pub(crate) fn has_this_binding(&self) -> bool {\n        self.kind.has_this_binding()\n    }\n\n    /// Returns `true` if this environment is poisoned.\n    pub(crate) fn poisoned(&self) -> bool {\n        self.poisoned.get()\n    }\n\n    /// Returns `true` if this environment is inside a `with` environment.\n    pub(crate) fn with(&self) -> bool {\n        self.with\n    }\n\n    /// Poisons this environment for future binding searches.\n    pub(crate) fn poison(&self) {\n        self.poisoned.set(true);\n    }\n\n    /// Extends the environment with the bindings from the compile time environment.\n    pub(crate) fn extend_from_compile(&self) {\n        if let Some(env) = self.kind().as_function() {\n            let compile_bindings_number = env.compile().num_bindings() as usize;\n            let mut bindings = env.bindings().borrow_mut();\n            if compile_bindings_number > bindings.len() {\n                bindings.resize(compile_bindings_number, None);\n            }\n        }\n    }\n}\n\n/// The kind of the declarative environment.\n#[derive(Debug, Trace, Finalize)]\npub(crate) enum DeclarativeEnvironmentKind {\n    /// Only stores lexical bindings.\n    Lexical(LexicalEnvironment),\n    /// Stores lexical bindings, global var bindings and the global this.\n    Global(GlobalEnvironment),\n    /// Stores lexical bindings, var bindings and the `FunctionSlots` of the function environment.\n    Function(FunctionEnvironment),\n    /// Stores module bindings, which include references to bindings on other environments.\n    Module(ModuleEnvironment),\n}\n\nimpl DeclarativeEnvironmentKind {\n    /// Unwraps the inner function environment if possible. Returns `None` otherwise.\n    pub(crate) const fn as_function(&self) -> Option<&FunctionEnvironment> {\n        if let Self::Function(fun) = &self {\n            Some(fun)\n        } else {\n            None\n        }\n    }\n\n    /// Unwraps the inner global environment if possible. Returns `None` otherwise.\n    pub(crate) const fn as_global(&self) -> Option<&GlobalEnvironment> {\n        if let Self::Global(fun) = &self {\n            Some(fun)\n        } else {\n            None\n        }\n    }\n\n    /// Unwraps the inner module environment if possible. Returns `None` otherwise.\n    pub(crate) const fn as_module(&self) -> Option<&ModuleEnvironment> {\n        if let Self::Module(module) = &self {\n            Some(module)\n        } else {\n            None\n        }\n    }\n\n    /// Get the binding value from the environment by it's index.\n    ///\n    /// # Panics\n    ///\n    /// Panics if the binding value is out of range or not initialized.\n    #[track_caller]\n    pub(crate) fn get(&self, index: u32) -> Option<JsValue> {\n        match self {\n            Self::Lexical(inner) => inner.get(index),\n            Self::Global(inner) => inner.get(index),\n            Self::Function(inner) => inner.get(index),\n            Self::Module(inner) => inner.get(index),\n        }\n    }\n\n    /// Sets the binding value from the environment by index.\n    ///\n    /// # Panics\n    ///\n    /// Panics if the binding value is out of range.\n    #[track_caller]\n    pub(crate) fn set(&self, index: u32, value: JsValue) {\n        match self {\n            Self::Lexical(inner) => inner.set(index, value),\n            Self::Global(inner) => inner.set(index, value),\n            Self::Function(inner) => inner.set(index, value),\n            Self::Module(inner) => inner.set(index, value),\n        }\n    }\n\n    /// `GetThisBinding`\n    ///\n    /// Returns the `this` binding of this environment.\n    ///\n    /// More information:\n    ///  - [ECMAScript specification][spec]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-function-environment-records-getthisbinding\n    pub(crate) fn get_this_binding(&self) -> JsResult<Option<JsValue>> {\n        match self {\n            Self::Lexical(_) | Self::Global(_) => Ok(None),\n            Self::Function(f) => f.get_this_binding(),\n            Self::Module(_) => Ok(Some(JsValue::undefined())),\n        }\n    }\n\n    /// `HasThisBinding`\n    ///\n    /// Returns if the environment has a `this` binding.\n    ///\n    /// More information:\n    ///  - [ECMAScript specification][spec]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-function-environment-records-hasthisbinding\n    pub(crate) fn has_this_binding(&self) -> bool {\n        match self {\n            Self::Lexical(_) => false,\n            Self::Function(f) => f.has_this_binding(),\n            Self::Global(_) | Self::Module(_) => true,\n        }\n    }\n}\n"
  },
  {
    "path": "core/engine/src/environments/runtime/declarative/module.rs",
    "content": "use std::cell::RefCell;\n\nuse boa_ast::scope::Scope;\nuse boa_gc::{Finalize, GcRefCell, Trace};\n\nuse crate::{JsString, JsValue, module::Module};\n\n/// Type of accessor used to access an indirect binding.\n#[derive(Debug, Clone)]\nenum BindingAccessor {\n    Identifier(JsString),\n    Index(u32),\n}\n\n/// An indirect reference to a binding inside an environment.\n#[derive(Clone, Debug, Trace, Finalize)]\nstruct IndirectBinding {\n    module: Module,\n    #[unsafe_ignore_trace]\n    accessor: RefCell<BindingAccessor>,\n}\n\n/// The type of binding a [`ModuleEnvironment`] can contain.\n#[derive(Clone, Debug, Trace, Finalize)]\nenum BindingType {\n    Direct(Option<JsValue>),\n    Indirect(IndirectBinding),\n}\n\n/// A [**Module Environment Record**][spec].\n///\n/// Module environments allow referencing bindings inside other environments, in addition\n/// to the usual declarative environment functionality.\n///\n///\n/// [spec]: https://tc39.es/ecma262/#sec-module-environment-records\n#[derive(Debug, Trace, Finalize)]\npub(crate) struct ModuleEnvironment {\n    bindings: GcRefCell<Vec<BindingType>>,\n\n    // Safety: Nothing in CompileTimeEnvironment needs tracing.\n    #[unsafe_ignore_trace]\n    compile: Scope,\n}\n\nimpl ModuleEnvironment {\n    /// Creates a new `LexicalEnvironment`.\n    pub(crate) fn new(bindings: u32, compile: Scope) -> Self {\n        Self {\n            bindings: GcRefCell::new(vec![BindingType::Direct(None); bindings as usize]),\n            compile,\n        }\n    }\n\n    /// Gets the compile time environment of this module environment.\n    pub(crate) const fn compile(&self) -> &Scope {\n        &self.compile\n    }\n\n    /// Get the binding value from the environment by it's index.\n    ///\n    /// # Panics\n    ///\n    /// Panics if the binding value is out of range or not initialized.\n    #[track_caller]\n    pub(crate) fn get(&self, index: u32) -> Option<JsValue> {\n        let bindings = self.bindings.borrow();\n\n        match &bindings[index as usize] {\n            BindingType::Direct(v) => v.clone(),\n            BindingType::Indirect(IndirectBinding { module, accessor }) => {\n                let env = module.environment()?;\n\n                match &*accessor.clone().borrow() {\n                    BindingAccessor::Identifier(name) => {\n                        let index = env\n                            .kind()\n                            .as_module()\n                            .expect(\"must be module environment\")\n                            .compile()\n                            .get_binding(name)\n                            .expect(\"linking must ensure the binding exists\");\n\n                        let value = env.get(index.binding_index())?;\n\n                        *accessor.borrow_mut() = BindingAccessor::Index(index.binding_index());\n\n                        Some(value)\n                    }\n                    BindingAccessor::Index(index) => env.get(*index),\n                }\n            }\n        }\n    }\n\n    /// Sets the binding value from the environment by index.\n    ///\n    /// # Panics\n    ///\n    /// Panics if the binding value is out of range.\n    #[track_caller]\n    pub(crate) fn set(&self, index: u32, value: JsValue) {\n        let mut bindings = self.bindings.borrow_mut();\n\n        match &mut bindings[index as usize] {\n            BindingType::Direct(v) => *v = Some(value),\n            BindingType::Indirect(_) => {\n                panic!(\"cannot modify indirect references to other environments\")\n            }\n        }\n    }\n\n    /// Creates an indirect binding reference to another environment binding.\n    ///\n    /// # Panics\n    ///\n    /// Panics if the binding value is out of range.\n    #[track_caller]\n    pub(crate) fn set_indirect(&self, index: u32, target_module: Module, target_binding: JsString) {\n        let mut bindings = self.bindings.borrow_mut();\n\n        bindings[index as usize] = BindingType::Indirect(IndirectBinding {\n            module: target_module,\n            accessor: RefCell::new(BindingAccessor::Identifier(target_binding)),\n        });\n    }\n}\n"
  },
  {
    "path": "core/engine/src/environments/runtime/mod.rs",
    "content": "use crate::{\n    Context, JsResult, JsString, JsSymbol, JsValue,\n    object::{JsObject, PrivateName},\n};\nuse boa_ast::scope::{BindingLocator, BindingLocatorScope, Scope};\nuse boa_gc::{Finalize, Gc, Trace};\nuse thin_vec::ThinVec;\n\nmod declarative;\nmod private;\n\nuse self::declarative::ModuleEnvironment;\npub(crate) use self::{\n    declarative::{\n        DeclarativeEnvironment, DeclarativeEnvironmentKind, FunctionEnvironment, FunctionSlots,\n        LexicalEnvironment, ThisBindingStatus,\n    },\n    private::PrivateEnvironment,\n};\n\n/// A single node in the environment chain.\n///\n/// Each node holds one [`Environment`] and a pointer to its parent.\n/// The chain is immutable from the perspective of other holders — pushing\n/// creates a new tip node, leaving existing nodes untouched. This makes\n/// cloning the chain O(1) (a single `Gc` ref-count bump).\n#[derive(Clone, Debug, Trace, Finalize)]\npub(crate) struct EnvironmentNode {\n    env: Environment,\n    parent: Option<Gc<EnvironmentNode>>,\n}\n\n/// The environment stack holds all environments at runtime.\n///\n/// Implemented as a singly-linked list of [`EnvironmentNode`]s, where each\n/// node points to its parent (the previous environment). Cloning is O(1) —\n/// just a reference-count bump on the tip pointer plus small scalar copies.\n///\n/// The global declarative environment is NOT stored here — it lives in the\n/// [`crate::realm::Realm`] and is accessed via `frame.realm.environment()`.\n/// This avoids a `Gc` clone on every function call.\n#[derive(Clone, Debug, Trace, Finalize)]\npub(crate) struct EnvironmentStack {\n    /// The tip (most recently pushed) environment in the chain.\n    tip: Option<Gc<EnvironmentNode>>,\n\n    /// Number of environments in the chain (not counting global).\n    #[unsafe_ignore_trace]\n    depth: u32,\n\n    private_stack: ThinVec<Gc<PrivateEnvironment>>,\n}\n\n/// Saved environment state for `pop_to_global` / `restore_from_saved`.\n/// Used by indirect `eval` and `Function.prototype.toString` recompilation.\npub(crate) struct SavedEnvironments {\n    tip: Option<Gc<EnvironmentNode>>,\n    depth: u32,\n}\n\n/// A runtime environment.\n#[derive(Clone, Debug, Trace, Finalize)]\npub(crate) enum Environment {\n    Declarative(Gc<DeclarativeEnvironment>),\n    Object(JsObject),\n}\n\nimpl Environment {\n    /// Returns the declarative environment if it is one.\n    pub(crate) const fn as_declarative(&self) -> Option<&Gc<DeclarativeEnvironment>> {\n        match self {\n            Self::Declarative(env) => Some(env),\n            Self::Object(_) => None,\n        }\n    }\n}\n\nimpl EnvironmentStack {\n    /// Create a new environment stack.\n    pub(crate) fn new() -> Self {\n        Self {\n            tip: None,\n            depth: 0,\n            private_stack: ThinVec::new(),\n        }\n    }\n\n    /// Gets the next outer function environment.\n    pub(crate) fn outer_function_environment(&self) -> Option<(Gc<DeclarativeEnvironment>, Scope)> {\n        for (env, _) in self.iter_from_tip() {\n            if let Some(decl) = env.as_declarative()\n                && let Some(function_env) = decl.kind().as_function()\n            {\n                return Some((decl.clone(), function_env.compile().clone()));\n            }\n        }\n        None\n    }\n\n    /// Save all current environments and clear the stack.\n    /// Used by indirect eval and `Function.prototype.toString` recompilation.\n    pub(crate) fn pop_to_global(&mut self) -> SavedEnvironments {\n        SavedEnvironments {\n            tip: self.tip.take(),\n            depth: std::mem::replace(&mut self.depth, 0),\n        }\n    }\n\n    /// Restore environments from a previous `pop_to_global` call.\n    pub(crate) fn restore_from_saved(&mut self, saved: SavedEnvironments) {\n        self.tip = saved.tip;\n        self.depth = saved.depth;\n    }\n\n    /// Get the number of current environments (not counting global).\n    #[inline]\n    pub(crate) fn len(&self) -> usize {\n        self.depth as usize\n    }\n\n    /// Get the environment at the given absolute index (0-based from root).\n    ///\n    /// Index 0 is the deepest (first pushed) environment in the chain.\n    /// Index `len() - 1` is the tip (most recently pushed).\n    #[inline]\n    pub(crate) fn get(&self, index: usize) -> Option<&Environment> {\n        let depth = self.depth as usize;\n        if index >= depth {\n            return None;\n        }\n        let steps = depth - 1 - index;\n        let mut current = self.tip.as_deref()?;\n        for _ in 0..steps {\n            current = current.parent.as_deref()?;\n        }\n        Some(&current.env)\n    }\n\n    /// Iterate from tip (most recent) toward root (oldest).\n    /// Yields `(&Environment, absolute_index)`.\n    fn iter_from_tip(&self) -> EnvironmentChainIter<'_> {\n        EnvironmentChainIter {\n            current: self.tip.as_deref(),\n            index: self.depth,\n        }\n    }\n\n    /// Get the tip (most recently pushed) environment.\n    #[inline]\n    fn last(&self) -> Option<&Environment> {\n        self.tip.as_deref().map(|node| &node.env)\n    }\n\n    /// Truncate current environments to the given depth.\n    pub(crate) fn truncate(&mut self, len: usize) {\n        while self.depth as usize > len {\n            let node = self.tip.as_ref().expect(\"depth > 0 implies tip is Some\");\n            self.tip = node.parent.clone();\n            self.depth -= 1;\n        }\n    }\n\n    /// `GetThisEnvironment`\n    ///\n    /// Returns the environment that currently provides a `this` biding.\n    ///\n    /// More information:\n    ///  - [ECMAScript specification][spec]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-getthisenvironment\n    pub(crate) fn get_this_environment<'a>(\n        &'a self,\n        global: &'a Gc<DeclarativeEnvironment>,\n    ) -> &'a DeclarativeEnvironmentKind {\n        for (env, _) in self.iter_from_tip() {\n            if let Some(decl) = env.as_declarative().filter(|decl| decl.has_this_binding()) {\n                return decl.kind();\n            }\n        }\n\n        global.kind()\n    }\n\n    /// `GetThisBinding`\n    ///\n    /// Returns the current `this` binding of the environment.\n    /// Note: If the current environment is the global environment, this function returns `Ok(None)`.\n    ///\n    /// More information:\n    ///  - [ECMAScript specification][spec]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-function-environment-records-getthisbinding\n    pub(crate) fn get_this_binding(&self) -> JsResult<Option<JsValue>> {\n        for (env, _) in self.iter_from_tip() {\n            if let Environment::Declarative(decl) = env\n                && let Some(this) = decl.get_this_binding()?\n            {\n                return Ok(Some(this));\n            }\n        }\n\n        Ok(None)\n    }\n\n    /// Push a new object environment on the environments stack.\n    pub(crate) fn push_object(&mut self, object: JsObject) {\n        self.push_env(Environment::Object(object));\n    }\n\n    /// Push a lexical environment on the environments stack and return it's index.\n    pub(crate) fn push_lexical(\n        &mut self,\n        bindings_count: u32,\n        global: &Gc<DeclarativeEnvironment>,\n    ) -> u32 {\n        let (poisoned, with) = self.compute_poisoned_with(global);\n\n        let index = self.depth;\n\n        self.push_env(Environment::Declarative(Gc::new(\n            DeclarativeEnvironment::new(\n                DeclarativeEnvironmentKind::Lexical(LexicalEnvironment::new(bindings_count)),\n                poisoned,\n                with,\n            ),\n        )));\n\n        index\n    }\n\n    /// Push a function environment on the environments stack.\n    pub(crate) fn push_function(\n        &mut self,\n        scope: Scope,\n        function_slots: FunctionSlots,\n        global: &Gc<DeclarativeEnvironment>,\n    ) {\n        let num_bindings = scope.num_bindings_non_local();\n\n        let (poisoned, with) = self.compute_poisoned_with(global);\n\n        self.push_env(Environment::Declarative(Gc::new(\n            DeclarativeEnvironment::new(\n                DeclarativeEnvironmentKind::Function(FunctionEnvironment::new(\n                    num_bindings,\n                    function_slots,\n                    scope,\n                )),\n                poisoned,\n                with,\n            ),\n        )));\n    }\n\n    /// Push a module environment on the environments stack.\n    pub(crate) fn push_module(&mut self, scope: Scope) {\n        let num_bindings = scope.num_bindings_non_local();\n        self.push_env(Environment::Declarative(Gc::new(\n            DeclarativeEnvironment::new(\n                DeclarativeEnvironmentKind::Module(ModuleEnvironment::new(num_bindings, scope)),\n                false,\n                false,\n            ),\n        )));\n    }\n\n    /// Pop environment from the environments stack.\n    #[track_caller]\n    pub(crate) fn pop(&mut self) {\n        let node = self\n            .tip\n            .as_ref()\n            .expect(\"cannot pop empty environment chain\");\n        self.tip = node.parent.clone();\n        self.depth -= 1;\n    }\n\n    /// Get the most outer environment.\n    pub(crate) fn current_declarative_ref<'a>(\n        &'a self,\n        global: &'a Gc<DeclarativeEnvironment>,\n    ) -> Option<&'a Gc<DeclarativeEnvironment>> {\n        if let Some(env) = self.last() {\n            env.as_declarative()\n        } else {\n            Some(global)\n        }\n    }\n\n    /// Mark that there may be added bindings from the current environment to the next function\n    /// environment.\n    pub(crate) fn poison_until_last_function(&mut self, global: &Gc<DeclarativeEnvironment>) {\n        for (env, _) in self.iter_from_tip() {\n            if let Some(decl) = env.as_declarative() {\n                decl.poison();\n                if decl.is_function() {\n                    return;\n                }\n            }\n        }\n        global.poison();\n    }\n\n    /// Set the value of a lexical binding.\n    ///\n    /// # Panics\n    ///\n    /// Panics if the environment or binding index are out of range.\n    #[track_caller]\n    pub(crate) fn put_lexical_value(\n        &mut self,\n        environment: BindingLocatorScope,\n        binding_index: u32,\n        value: JsValue,\n        global: &Gc<DeclarativeEnvironment>,\n    ) {\n        let env = match environment {\n            BindingLocatorScope::GlobalObject | BindingLocatorScope::GlobalDeclarative => global,\n            BindingLocatorScope::Stack(index) => self\n                .get(index as usize)\n                .and_then(Environment::as_declarative)\n                .expect(\"must be declarative environment\"),\n        };\n        env.set(binding_index, value);\n    }\n\n    /// Set the value of a binding if it is uninitialized.\n    ///\n    /// # Panics\n    ///\n    /// Panics if the environment or binding index are out of range.\n    #[track_caller]\n    pub(crate) fn put_value_if_uninitialized(\n        &mut self,\n        environment: BindingLocatorScope,\n        binding_index: u32,\n        value: JsValue,\n        global: &Gc<DeclarativeEnvironment>,\n    ) {\n        let env = match environment {\n            BindingLocatorScope::GlobalObject | BindingLocatorScope::GlobalDeclarative => global,\n            BindingLocatorScope::Stack(index) => self\n                .get(index as usize)\n                .and_then(Environment::as_declarative)\n                .expect(\"must be declarative environment\"),\n        };\n        if env.get(binding_index).is_none() {\n            env.set(binding_index, value);\n        }\n    }\n\n    /// Push a private environment to the private environment stack.\n    pub(crate) fn push_private(&mut self, environment: Gc<PrivateEnvironment>) {\n        self.private_stack.push(environment);\n    }\n\n    /// Pop a private environment from the private environment stack.\n    pub(crate) fn pop_private(&mut self) {\n        self.private_stack.pop();\n    }\n\n    /// `ResolvePrivateIdentifier ( privEnv, identifier )`\n    ///\n    /// More information:\n    ///  - [ECMAScript specification][spec]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-resolve-private-identifier\n    pub(crate) fn resolve_private_identifier(&self, identifier: JsString) -> Option<PrivateName> {\n        for environment in self.private_stack.iter().rev() {\n            if environment.descriptions().contains(&identifier) {\n                return Some(PrivateName::new(identifier, environment.id()));\n            }\n        }\n        None\n    }\n\n    /// Return all private name descriptions in all private environments.\n    pub(crate) fn private_name_descriptions(&self) -> Vec<&JsString> {\n        let mut names = Vec::new();\n        for environment in self.private_stack.iter().rev() {\n            for name in environment.descriptions() {\n                if !names.contains(&name) {\n                    names.push(name);\n                }\n            }\n        }\n        names\n    }\n\n    /// Indicate if the current environment stack has an object environment.\n    pub(crate) fn has_object_environment(&self) -> bool {\n        self.iter_from_tip()\n            .any(|(env, _)| matches!(env, Environment::Object(_)))\n    }\n\n    /// Create an `EnvironmentStack` snapshot suitable for storing in a closure.\n    ///\n    /// With the linked-list implementation, this is just a clone since\n    /// cloning is O(1) — a single ref-count bump on the tip pointer.\n    pub(crate) fn snapshot_for_closure(&self) -> EnvironmentStack {\n        self.clone()\n    }\n\n    // ---- Private helpers ----\n\n    /// Push an environment onto the chain.\n    fn push_env(&mut self, env: Environment) {\n        self.tip = Some(Gc::new(EnvironmentNode {\n            env,\n            parent: self.tip.take(),\n        }));\n        self.depth += 1;\n    }\n\n    /// Compute the `(poisoned, with)` flags for a new environment.\n    fn compute_poisoned_with(&self, global: &Gc<DeclarativeEnvironment>) -> (bool, bool) {\n        let with = if let Some(env) = self.last() {\n            env.as_declarative().is_none()\n        } else {\n            false\n        };\n\n        let environment = self\n            .iter_from_tip()\n            .find_map(|(env, _)| env.as_declarative())\n            .unwrap_or(global);\n        (environment.poisoned(), with || environment.with())\n    }\n}\n\n/// Iterator from tip (most recent) toward root (oldest).\n/// Yields `(&Environment, absolute_index)`.\nstruct EnvironmentChainIter<'a> {\n    current: Option<&'a EnvironmentNode>,\n    index: u32,\n}\n\nimpl<'a> Iterator for EnvironmentChainIter<'a> {\n    type Item = (&'a Environment, u32);\n\n    fn next(&mut self) -> Option<Self::Item> {\n        let node = self.current?;\n        self.index = self\n            .index\n            .checked_sub(1)\n            .expect(\"iterator advanced past root\");\n        self.current = node.parent.as_deref();\n        Some((&node.env, self.index))\n    }\n}\n\nimpl Context {\n    /// Gets the corresponding runtime binding of the provided `BindingLocator`, modifying\n    /// its indexes in place.\n    ///\n    /// This readjusts a `BindingLocator` to the correct binding if a `with` environment or\n    /// `eval` call modified the compile-time bindings.\n    ///\n    /// Only use if the binding origin is unknown or comes from a `var` declaration. Lexical bindings\n    /// are completely removed of runtime checks because the specification guarantees that runtime\n    /// semantics cannot add or remove lexical bindings.\n    pub(crate) fn find_runtime_binding(&mut self, locator: &mut BindingLocator) -> JsResult<()> {\n        let global = self.vm.frame().realm.environment();\n        if let Some(env) = self.vm.frame().environments.current_declarative_ref(global)\n            && !env.with()\n            && !env.poisoned()\n        {\n            return Ok(());\n        }\n\n        let (global, min_index) = match locator.scope() {\n            BindingLocatorScope::GlobalObject | BindingLocatorScope::GlobalDeclarative => (true, 0),\n            BindingLocatorScope::Stack(index) => (false, index),\n        };\n        let max_index = self.vm.frame().environments.len() as u32;\n\n        for index in (min_index..max_index).rev() {\n            match self.environment_expect(index) {\n                Environment::Declarative(env) => {\n                    if env.poisoned() {\n                        if let Some(env) = env.kind().as_function()\n                            && let Some(b) = env.compile().get_binding(locator.name())\n                        {\n                            locator.set_scope(b.scope());\n                            locator.set_binding_index(b.binding_index());\n                            return Ok(());\n                        }\n                    } else if !env.with() {\n                        return Ok(());\n                    }\n                }\n                Environment::Object(o) => {\n                    let o = o.clone();\n                    let key = locator.name().clone();\n                    if o.has_property(key.clone(), self)? {\n                        if let Some(unscopables) = o.get(JsSymbol::unscopables(), self)?.as_object()\n                            && unscopables.get(key.clone(), self)?.to_boolean()\n                        {\n                            continue;\n                        }\n                        locator.set_scope(BindingLocatorScope::Stack(index));\n                        return Ok(());\n                    }\n                }\n            }\n        }\n\n        if global\n            && self.realm().environment().poisoned()\n            && let Some(b) = self.realm().scope().get_binding(locator.name())\n        {\n            locator.set_scope(b.scope());\n            locator.set_binding_index(b.binding_index());\n        }\n\n        Ok(())\n    }\n\n    /// Finds the object environment that contains the binding and returns the `this` value of the object environment.\n    pub(crate) fn this_from_object_environment_binding(\n        &mut self,\n        locator: &BindingLocator,\n    ) -> JsResult<Option<JsObject>> {\n        let global = self.vm.frame().realm.environment();\n        if let Some(env) = self.vm.frame().environments.current_declarative_ref(global)\n            && !env.with()\n        {\n            return Ok(None);\n        }\n\n        let min_index = match locator.scope() {\n            BindingLocatorScope::GlobalObject | BindingLocatorScope::GlobalDeclarative => 0,\n            BindingLocatorScope::Stack(index) => index,\n        };\n        let max_index = self.vm.frame().environments.len() as u32;\n\n        for index in (min_index..max_index).rev() {\n            match self.environment_expect(index) {\n                Environment::Declarative(env) => {\n                    if env.poisoned() {\n                        if let Some(env) = env.kind().as_function()\n                            && env.compile().get_binding(locator.name()).is_some()\n                        {\n                            break;\n                        }\n                    } else if !env.with() {\n                        break;\n                    }\n                }\n                Environment::Object(o) => {\n                    let o = o.clone();\n                    let key = locator.name().clone();\n                    if o.has_property(key.clone(), self)? {\n                        if let Some(unscopables) = o.get(JsSymbol::unscopables(), self)?.as_object()\n                            && unscopables.get(key.clone(), self)?.to_boolean()\n                        {\n                            continue;\n                        }\n                        return Ok(Some(o));\n                    }\n                }\n            }\n        }\n\n        Ok(None)\n    }\n\n    /// Checks if the binding pointed by `locator` is initialized.\n    ///\n    /// # Panics\n    ///\n    /// Panics if the environment or binding index are out of range.\n    pub(crate) fn is_initialized_binding(&mut self, locator: &BindingLocator) -> JsResult<bool> {\n        match locator.scope() {\n            BindingLocatorScope::GlobalObject => {\n                let key = locator.name().clone();\n                let obj = self.global_object();\n                obj.has_property(key, self)\n            }\n            BindingLocatorScope::GlobalDeclarative => {\n                let env = self.vm.frame().realm.environment();\n                Ok(env.get(locator.binding_index()).is_some())\n            }\n            BindingLocatorScope::Stack(index) => match self.environment_expect(index) {\n                Environment::Declarative(env) => Ok(env.get(locator.binding_index()).is_some()),\n                Environment::Object(obj) => {\n                    let key = locator.name().clone();\n                    let obj = obj.clone();\n                    obj.has_property(key, self)\n                }\n            },\n        }\n    }\n\n    /// Get the value of a binding.\n    ///\n    /// # Panics\n    ///\n    /// Panics if the environment or binding index are out of range.\n    #[track_caller]\n    pub(crate) fn get_binding(&mut self, locator: &BindingLocator) -> JsResult<Option<JsValue>> {\n        match locator.scope() {\n            BindingLocatorScope::GlobalObject => {\n                let key = locator.name().clone();\n                let obj = self.global_object();\n                obj.try_get(key, self)\n            }\n            BindingLocatorScope::GlobalDeclarative => {\n                let env = self.vm.frame().realm.environment();\n                Ok(env.get(locator.binding_index()))\n            }\n            BindingLocatorScope::Stack(index) => match self.environment_expect(index) {\n                Environment::Declarative(env) => Ok(env.get(locator.binding_index())),\n                Environment::Object(obj) => {\n                    let key = locator.name().clone();\n                    let obj = obj.clone();\n                    obj.get(key, self).map(Some)\n                }\n            },\n        }\n    }\n\n    /// Sets the value of a binding.\n    ///\n    /// # Panics\n    ///\n    /// Panics if the environment or binding index are out of range.\n    #[track_caller]\n    pub(crate) fn set_binding(\n        &mut self,\n        locator: &BindingLocator,\n        value: JsValue,\n        strict: bool,\n    ) -> JsResult<()> {\n        match locator.scope() {\n            BindingLocatorScope::GlobalObject => {\n                let key = locator.name().clone();\n                let obj = self.global_object();\n                obj.set(key, value, strict, self)?;\n            }\n            BindingLocatorScope::GlobalDeclarative => {\n                let env = self.vm.frame().realm.environment();\n                env.set(locator.binding_index(), value);\n            }\n            BindingLocatorScope::Stack(index) => match self.environment_expect(index) {\n                Environment::Declarative(decl) => {\n                    decl.set(locator.binding_index(), value);\n                }\n                Environment::Object(obj) => {\n                    let key = locator.name().clone();\n                    let obj = obj.clone();\n                    obj.set(key, value, strict, self)?;\n                }\n            },\n        }\n        Ok(())\n    }\n\n    /// Deletes a binding if it exists.\n    ///\n    /// Returns `true` if the binding was deleted.\n    ///\n    /// # Panics\n    ///\n    /// Panics if the environment or binding index are out of range.\n    pub(crate) fn delete_binding(&mut self, locator: &BindingLocator) -> JsResult<bool> {\n        match locator.scope() {\n            BindingLocatorScope::GlobalObject => {\n                let key = locator.name().clone();\n                let obj = self.global_object();\n                obj.__delete__(&key.into(), &mut self.into())\n            }\n            BindingLocatorScope::GlobalDeclarative => Ok(false),\n            BindingLocatorScope::Stack(index) => match self.environment_expect(index) {\n                Environment::Declarative(_) => Ok(false),\n                Environment::Object(obj) => {\n                    let key = locator.name().clone();\n                    let obj = obj.clone();\n                    obj.__delete__(&key.into(), &mut self.into())\n                }\n            },\n        }\n    }\n\n    /// Return the environment at the given index.\n    ///\n    /// # Panics\n    ///\n    /// Panics if the `index` is out of range.\n    pub(crate) fn environment_expect(&self, index: u32) -> &Environment {\n        self.vm\n            .frame()\n            .environments\n            .get(index as usize)\n            .expect(\"environment index must be in range\")\n    }\n}\n"
  },
  {
    "path": "core/engine/src/environments/runtime/private.rs",
    "content": "use boa_gc::{Finalize, Trace, empty_trace};\n\nuse crate::JsString;\n\n/// Private runtime environment.\n#[derive(Clone, Debug, Finalize)]\npub(crate) struct PrivateEnvironment {\n    /// The unique identifier of the private names.\n    id: usize,\n\n    /// The `[[Description]]` internal slot of the private names.\n    descriptions: Vec<JsString>,\n}\n\n// Safety: PrivateEnvironment does not contain any objects that need to be traced.\nunsafe impl Trace for PrivateEnvironment {\n    empty_trace!();\n}\n\nimpl PrivateEnvironment {\n    /// Creates a new `PrivateEnvironment`.\n    pub(crate) fn new(id: usize, descriptions: Vec<JsString>) -> Self {\n        Self { id, descriptions }\n    }\n\n    /// Gets the id of this private environment.\n    pub(crate) const fn id(&self) -> usize {\n        self.id\n    }\n\n    /// Gets the descriptions of this private environment.\n    pub(crate) fn descriptions(&self) -> &[JsString] {\n        &self.descriptions\n    }\n}\n"
  },
  {
    "path": "core/engine/src/environments/tests.rs",
    "content": "use crate::{JsNativeErrorKind, TestAction, run_test_actions};\nuse indoc::indoc;\n\n#[test]\nfn let_is_block_scoped() {\n    run_test_actions([TestAction::assert_native_error(\n        indoc! {r#\"\n            {\n              let bar = \"bar\";\n            }\n            bar;\n        \"#},\n        JsNativeErrorKind::Reference,\n        \"bar is not defined\",\n    )]);\n}\n\n#[test]\nfn const_is_block_scoped() {\n    run_test_actions([TestAction::assert_native_error(\n        indoc! {r#\"\n            {\n            const bar = \"bar\";\n            }\n            bar;\n        \"#},\n        JsNativeErrorKind::Reference,\n        \"bar is not defined\",\n    )]);\n}\n\n#[test]\nfn var_not_block_scoped() {\n    run_test_actions([TestAction::assert(indoc! {r#\"\n            {\n              var bar = \"bar\";\n            }\n            bar == \"bar\";\n        \"#})]);\n}\n\n#[test]\nfn functions_use_declaration_scope() {\n    run_test_actions([TestAction::assert_native_error(\n        indoc! {r#\"\n            function foo() {\n                bar;\n            }\n            {\n                let bar = \"bar\";\n                foo();\n            }\n        \"#},\n        JsNativeErrorKind::Reference,\n        \"bar is not defined\",\n    )]);\n}\n\n#[test]\nfn set_outer_var_in_block_scope() {\n    run_test_actions([TestAction::assert(indoc! {r#\"\n            var bar;\n            {\n                bar = \"foo\";\n            }\n            bar == \"foo\";\n        \"#})]);\n}\n\n#[test]\nfn set_outer_let_in_block_scope() {\n    run_test_actions([TestAction::assert(indoc! {r#\"\n            let bar;\n            {\n                bar = \"foo\";\n            }\n            bar == \"foo\";\n        \"#})]);\n}\n"
  },
  {
    "path": "core/engine/src/error/mod.rs",
    "content": "//! Error-related types and conversions.\n\n#[cfg(test)]\nmod tests;\n\nuse crate::{\n    Context, JsResult, JsString, JsValue,\n    builtins::{\n        Array,\n        error::{Error, ErrorKind},\n    },\n    js_string,\n    object::JsObject,\n    property::PropertyDescriptor,\n    realm::Realm,\n    vm::{\n        NativeSourceInfo,\n        shadow_stack::{Backtrace, ShadowEntry},\n    },\n};\nuse boa_gc::{Finalize, Trace, custom_trace};\nuse std::{\n    borrow::Cow,\n    error,\n    fmt::{self},\n};\nuse thiserror::Error;\n\n/// Create an error object from a value or string literal. Optionally the\n/// first argument of the macro can be a type of error (such as `TypeError`,\n/// `RangeError` or `InternalError`).\n///\n/// Can be used with an expression that converts into `JsValue` or a format\n/// string with arguments.\n///\n/// # Native Errors\n///\n/// The only native error that is not buildable using this macro is\n/// `AggregateError`, which requires multiple error objects available at\n/// construction.\n///\n/// [`InternalError`][mdn] is non-standard and unsupported in Boa.\n///\n/// All other native error types can be created from the macro using their\n/// JavaScript name followed by a colon, like:\n///\n/// ```ignore\n/// js_error!(TypeError: \"hello world\");\n/// ```\n///\n/// # Examples\n///\n/// ```\n/// # use boa_engine::{js_str, Context, JsValue};\n/// use boa_engine::js_error;\n/// let context = &mut Context::default();\n///\n/// let error = js_error!(\"error!\");\n/// assert!(error.as_opaque().is_some());\n/// assert_eq!(\n///     error.as_opaque().unwrap().to_string(context).unwrap(),\n///     \"error!\"\n/// );\n///\n/// let error = js_error!(\"error: {}\", 5);\n/// assert_eq!(\n///     error.as_opaque().unwrap().to_string(context).unwrap(),\n///     \"error: 5\"\n/// );\n///\n/// // Non-string literals must be used as an expression.\n/// let error = js_error!({ true });\n/// assert_eq!(error.as_opaque().unwrap(), &JsValue::from(true));\n/// ```\n///\n/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/InternalError\n#[macro_export]\nmacro_rules! js_error {\n    (Error: $value: literal) => {\n        $crate::JsError::from_native(\n            $crate::JsNativeError::ERROR.with_message($value)\n        )\n    };\n    (Error: $value: literal $(, $args: expr)* $(,)?) => {\n        $crate::JsError::from_native(\n            $crate::JsNativeError::ERROR\n                .with_message(format!($value $(, $args)*))\n        )\n    };\n\n    (TypeError: $value: literal) => {\n        $crate::JsError::from_native(\n            $crate::JsNativeError::TYP.with_message($value)\n        )\n    };\n    (TypeError: $value: literal $(, $args: expr)* $(,)?) => {\n        $crate::JsError::from_native(\n            $crate::JsNativeError::TYP\n                .with_message(format!($value $(, $args)*))\n        )\n    };\n\n    (SyntaxError: $value: literal) => {\n        $crate::JsError::from_native(\n            $crate::JsNativeError::SYNTAX.with_message($value)\n        )\n    };\n    (SyntaxError: $value: literal $(, $args: expr)* $(,)?) => {\n        $crate::JsError::from_native(\n            $crate::JsNativeError::SYNTAX.with_message(format!($value $(, $args)*))\n        )\n    };\n\n    (RangeError: $value: literal) => {\n        $crate::JsError::from_native(\n            $crate::JsNativeError::RANGE.with_message($value)\n        )\n    };\n    (RangeError: $value: literal $(, $args: expr)* $(,)?) => {\n        $crate::JsError::from_native(\n            $crate::JsNativeError::RANGE.with_message(format!($value $(, $args)*))\n        )\n    };\n\n    (EvalError: $value: literal) => {\n        $crate::JsError::from_native(\n            $crate::JsNativeError::EVAL.with_message($value)\n        )\n    };\n    (EvalError: $value: literal $(, $args: expr)* $(,)?) => {\n        $crate::JsError::from_native(\n            $crate::JsNativeError::EVAL.with_message(format!($value $(, $args)*))\n        )\n    };\n\n    (ReferenceError: $value: literal) => {\n        $crate::JsError::from_native(\n            $crate::JsNativeError::REFERENCE.with_message($value)\n        )\n    };\n    (ReferenceError: $value: literal $(, $args: expr)* $(,)?) => {\n        $crate::JsError::from_native(\n            $crate::JsNativeError::REFERENCE.with_message(format!($value $(, $args)*))\n        )\n    };\n\n    (URIError: $value: literal) => {\n        $crate::JsError::from_native(\n            $crate::JsNativeError::URI.with_message($value)\n        )\n    };\n    (URIError: $value: literal $(, $args: expr)* $(,)?) => {\n        $crate::JsError::from_native(\n            $crate::JsNativeError::URI.with_message(format!($value $(, $args)*))\n        )\n    };\n\n    ($value: literal) => {\n        $crate::JsError::from_opaque($crate::JsValue::from(\n            $crate::js_string!($value)\n        ))\n    };\n    ($value: expr) => {\n        $crate::JsError::from_opaque(\n            $crate::JsValue::from($value)\n        )\n    };\n    ($value: literal $(, $args: expr)* $(,)?) => {\n        $crate::JsError::from_opaque($crate::JsValue::from(\n            $crate::JsString::from(format!($value $(, $args)*))\n        ))\n    };\n}\n\n/// The error type returned by all operations related\n/// to the execution of Javascript code.\n///\n/// This is essentially an enum that can store either [`JsNativeError`]s (for ideal\n/// native errors)  or opaque [`JsValue`]s, since Javascript allows throwing any valid\n/// `JsValue`.\n///\n/// The implementation doesn't provide a [`From`] conversion\n/// for `JsValue`. This is with the intent of encouraging the usage of proper\n/// `JsNativeError`s instead of plain `JsValue`s. However, if you\n/// do need a proper opaque error, you can construct one using the\n/// [`JsError::from_opaque`] method.\n///\n/// # Examples\n///\n/// ```rust\n/// # use boa_engine::{JsError, JsNativeError, JsNativeErrorKind, JsValue, js_string};\n/// let cause = JsError::from_opaque(js_string!(\"error!\").into());\n///\n/// assert!(cause.as_opaque().is_some());\n/// assert_eq!(\n///     cause.as_opaque().unwrap(),\n///     &JsValue::from(js_string!(\"error!\"))\n/// );\n///\n/// let native_error: JsError = JsNativeError::typ()\n///     .with_message(\"invalid type!\")\n///     .with_cause(cause)\n///     .into();\n///\n/// let native = native_error.as_native().unwrap();\n/// let kind = native.kind();\n/// assert!(matches!(kind, JsNativeErrorKind::Type));\n/// ```\n#[derive(Debug, Clone, Trace, Finalize)]\n#[boa_gc(unsafe_no_drop)]\npub struct JsError {\n    inner: Repr,\n\n    pub(crate) backtrace: Option<Backtrace>,\n}\n\nimpl Eq for JsError {}\nimpl PartialEq for JsError {\n    /// The backtrace information is ignored.\n    #[inline]\n    fn eq(&self, other: &Self) -> bool {\n        // NOTE: We want to ignore stack trace, since that is\n        //       meta-information about the error.\n        self.inner == other.inner\n    }\n}\n\n/// Internal representation of a [`JsError`].\n///\n/// `JsError` is represented by an opaque enum because it restricts\n/// matching against `JsError` without calling `try_native` first.\n/// This allows us to provide a safe API for `Error` objects that extracts\n/// their info as a native `Rust` type ([`JsNativeError`]).\n///\n/// This should never be used outside of this module. If that's not the case,\n/// you should add methods to either `JsError` or `JsNativeError` to\n/// represent that special use case.\n#[derive(Debug, Clone, PartialEq, Eq, Trace, Finalize)]\n#[boa_gc(unsafe_no_drop)]\nenum Repr {\n    Opaque(JsValue),\n    Native(Box<JsNativeError>),\n    Engine(EngineError),\n}\n\nimpl error::Error for JsError {\n    fn source(&self) -> Option<&(dyn error::Error + 'static)> {\n        match &self.inner {\n            Repr::Native(err) => err.source(),\n            Repr::Opaque(_) => None,\n            Repr::Engine(err) => err.source(),\n        }\n    }\n}\n\n/// The error type returned by the [`JsError::try_native`] method.\n#[derive(Debug, Clone, Error)]\npub enum TryNativeError {\n    /// A property of the error object has an invalid type.\n    #[error(\"invalid type of property `{0}`\")]\n    InvalidPropertyType(&'static str),\n\n    /// The message of the error object could not be decoded.\n    #[error(\"property `message` cannot contain unpaired surrogates\")]\n    InvalidMessageEncoding,\n\n    /// The constructor property of the error object was invalid.\n    #[error(\"invalid `constructor` property of Error object\")]\n    InvalidConstructor,\n\n    /// A property of the error object is not accessible.\n    #[error(\"could not access property `{property}`\")]\n    InaccessibleProperty {\n        /// The name of the property that could not be accessed.\n        property: &'static str,\n\n        /// The source error.\n        source: JsError,\n    },\n\n    /// An inner error of an aggregate error is not accessible.\n    #[error(\"could not get element `{index}` of property `errors`\")]\n    InvalidErrorsIndex {\n        /// The index of the error that could not be accessed.\n        index: u64,\n\n        /// The source error.\n        source: JsError,\n    },\n\n    /// The error value is not an error object.\n    #[error(\"opaque error of type `{:?}` is not an Error object\", .0.get_type())]\n    NotAnErrorObject(JsValue),\n\n    /// The original realm of the error object was inaccessible.\n    #[error(\"could not access realm of Error object\")]\n    InaccessibleRealm {\n        /// The source error.\n        source: JsError,\n    },\n    /// The error was an engine error.\n    #[error(\"could not convert engine error into a native error\")]\n    EngineError {\n        /// The source error.\n        source: EngineError,\n    },\n}\n\n/// Runtime limit related errors.\n#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Error, Trace, Finalize)]\n#[boa_gc(empty_trace)]\npub enum RuntimeLimitError {\n    /// Error for reaching the iteration loops limit.\n    #[error(\"reached the maximum number of iteration loops on this execution\")]\n    LoopIteration,\n    /// Error for reaching the maximum amount of recursive calls.\n    #[error(\"reached the maximum number of recursive calls on this execution\")]\n    Recursion,\n    /// Error for reaching the maximum stack size\n    #[error(\"reached the maximum stack size on this execution\")]\n    StackSize,\n}\n\n/// Internal panic error.\n#[derive(Debug, Clone, Error, Eq, PartialEq, Trace, Finalize)]\n#[boa_gc(unsafe_no_drop)]\n#[error(\"{message}\")]\n#[must_use]\npub struct PanicError {\n    /// The original panic message providing context about what went wrong.\n    message: Box<str>,\n    /// The source error of this panic, if applicable.\n    source: Option<Box<JsError>>,\n}\n\nimpl PanicError {\n    /// Creates a `PanicError` error from a panic message.\n    pub fn new<S: Into<Box<str>>>(message: S) -> Self {\n        PanicError {\n            message: message.into(),\n            source: None,\n        }\n    }\n\n    /// Sets the source error of this `PanicError`.\n    pub fn with_source<E: Into<JsError>>(mut self, source: E) -> Self {\n        self.source = Some(Box::new(source.into()));\n        self\n    }\n\n    /// Gets the message of this `PanicError`.\n    #[must_use]\n    pub fn message(&self) -> &str {\n        &self.message\n    }\n}\n\nimpl From<PanicError> for JsError {\n    fn from(err: PanicError) -> Self {\n        EngineError::from(err).into()\n    }\n}\n\n/// Engine error that cannot be caught from within ECMAScript code.\n#[derive(Debug, Clone, Error, Eq, PartialEq, Trace, Finalize)]\n#[boa_gc(unsafe_no_drop)]\n#[allow(variant_size_differences)]\npub enum EngineError {\n    /// Error thrown when no instructions remain. Only used in a fuzzing context.\n    #[cfg(feature = \"fuzz\")]\n    #[error(\"NoInstructionsRemainError: instruction budget was exhausted\")]\n    NoInstructionsRemain,\n\n    /// Error thrown when a runtime limit is exceeded.\n    #[error(\"RuntimeLimitError: {0}\")]\n    RuntimeLimit(#[from] RuntimeLimitError),\n\n    /// Error thrown when an internal panic condition is encountered.\n    #[error(\"EnginePanic: {0}\")]\n    Panic(#[from] PanicError),\n}\n\nimpl EngineError {\n    /// Converts this error into its thread-safe, erased version.\n    ///\n    /// Even though this operation is lossy, converting into an `ErasedEngineError`\n    /// is useful since it implements `Send` and `Sync`, making it compatible with\n    /// error reporting frameworks such as `anyhow`, `eyre` or `miette`.\n    fn into_erased(self, context: &mut Context) -> ErasedEngineError {\n        match self {\n            #[cfg(feature = \"fuzz\")]\n            EngineError::NoInstructionsRemain => ErasedEngineError::NoInstructionsRemain,\n            EngineError::RuntimeLimit(err) => ErasedEngineError::RuntimeLimit(err),\n            EngineError::Panic(err) => ErasedEngineError::Panic(ErasedPanicError {\n                message: err.message,\n                source: err.source.map(|err| Box::new(err.into_erased(context))),\n            }),\n        }\n    }\n}\n\nimpl JsError {\n    /// Creates a new `JsError` from a native error `err`.\n    ///\n    /// # Examples\n    ///\n    /// ```rust\n    /// # use boa_engine::{JsError, JsNativeError};\n    /// let error = JsError::from_native(JsNativeError::syntax());\n    ///\n    /// assert!(error.as_native().is_some());\n    /// ```\n    #[must_use]\n    pub fn from_native(err: JsNativeError) -> Self {\n        Self {\n            inner: Repr::Native(Box::new(err)),\n            backtrace: None,\n        }\n    }\n\n    /// Creates a new `JsError` from a Rust standard error `err`.\n    /// This will create a new `JsNativeError` with the message of the standard error.\n    ///\n    /// # Examples\n    ///\n    /// ```\n    /// # use boa_engine::JsError;\n    /// let error = std::io::Error::new(std::io::ErrorKind::Other, \"oh no!\");\n    /// let js_error: JsError = JsError::from_rust(error);\n    ///\n    /// assert_eq!(js_error.as_native().unwrap().message(), \"oh no!\");\n    /// assert!(js_error.as_native().unwrap().cause().is_none());\n    /// ```\n    #[must_use]\n    pub fn from_rust(err: impl error::Error) -> Self {\n        let mut native_err = JsNativeError::error().with_message(err.to_string());\n        if let Some(source) = err.source() {\n            native_err = native_err.with_cause(Self::from_rust(source));\n        }\n\n        Self::from_native(native_err)\n    }\n\n    /// Creates a new `JsError` from an opaque error `value`.\n    ///\n    /// # Examples\n    ///\n    /// ```rust\n    /// # use boa_engine::JsError;\n    /// let error = JsError::from_opaque(5.0f64.into());\n    ///\n    /// assert!(error.as_opaque().is_some());\n    /// ```\n    #[must_use]\n    pub fn from_opaque(value: JsValue) -> Self {\n        // Recover the backtrace from the Error object if present,\n        // so it survives the JsError → JsValue → JsError round-trip.\n        let backtrace = value.as_object().and_then(|obj| {\n            let error = obj.downcast_ref::<Error>()?;\n            error.backtrace.0.clone()\n        });\n        Self {\n            inner: Repr::Opaque(value),\n            backtrace,\n        }\n    }\n\n    /// Converts the error to an opaque `JsValue` error\n    ///\n    /// Unwraps the inner `JsValue` if the error is already an opaque error.\n    ///\n    /// # Errors\n    ///\n    /// Returns the original error if `self` was an engine error.\n    ///\n    /// # Examples\n    ///\n    /// ```rust\n    /// # use boa_engine::{Context, JsError, JsNativeError};\n    /// # use boa_engine::builtins::error::Error;\n    /// # use boa_engine::error::{EngineError, RuntimeLimitError};\n    /// let context = &mut Context::default();\n    /// let error: JsError =\n    ///     JsNativeError::eval().with_message(\"invalid script\").into();\n    /// let error_val = error.into_opaque(context).unwrap();\n    ///\n    /// assert!(error_val.as_object().unwrap().is::<Error>());\n    ///\n    /// let error: JsError =\n    ///     EngineError::RuntimeLimit(RuntimeLimitError::Recursion).into();\n    ///\n    /// assert!(error.into_opaque(context).is_err());\n    /// ```\n    pub fn into_opaque(self, context: &mut Context) -> JsResult<JsValue> {\n        match self.inner {\n            Repr::Native(e) => {\n                let obj = e.into_opaque(context);\n                // Store the backtrace in the Error object so it survives the\n                // JsError → JsValue → JsError round-trip through promise\n                // rejection.\n                if let Some(backtrace) = self.backtrace\n                    && let Some(mut error) = obj.downcast_mut::<Error>()\n                {\n                    error.backtrace = IgnoreEq(Some(backtrace));\n                }\n                Ok(obj.into())\n            }\n            Repr::Opaque(v) => {\n                // Store the backtrace in the Error object for opaque errors\n                // too (e.g. explicit `throw new Error(...)`).\n                if let Some(backtrace) = self.backtrace\n                    && let Some(obj) = v.as_object()\n                    && let Some(mut error) = obj.downcast_mut::<Error>()\n                    && error.backtrace.0.is_none()\n                {\n                    error.backtrace = IgnoreEq(Some(backtrace));\n                }\n                Ok(v.clone())\n            }\n            Repr::Engine(_) => Err(self),\n        }\n    }\n\n    /// Unwraps the inner error if this contains a native error.\n    /// Otherwise, inspects the opaque error and tries to extract the\n    /// necessary information to construct a native error similar to the provided\n    /// opaque error. If the conversion fails, returns a [`TryNativeError`]\n    /// with the cause of the failure.\n    ///\n    /// # Note 1\n    ///\n    /// This method won't try to make any conversions between JS types.\n    /// In other words, for this conversion to succeed:\n    /// - `message` **MUST** be a `JsString` value.\n    /// - `errors` (in the case of `AggregateError`s) **MUST** be an `Array` object.\n    ///\n    /// # Note 2\n    ///\n    /// This operation should be considered a lossy conversion, since it\n    /// won't store any additional properties of the opaque\n    /// error, other than `message`, `cause` and `errors` (in the case of\n    /// `AggregateError`s). If you cannot afford a lossy conversion, clone\n    /// the object before calling [`from_opaque`][JsError::from_opaque]\n    /// to preserve its original properties.\n    ///\n    /// # Examples\n    ///\n    /// ```rust\n    /// # use boa_engine::{Context, JsError, JsNativeError, JsNativeErrorKind};\n    /// let context = &mut Context::default();\n    ///\n    /// // create a new, opaque Error object\n    /// let error: JsError = JsNativeError::typ().with_message(\"type error!\").into();\n    /// let error_val = error.into_opaque(context).unwrap();\n    ///\n    /// // then, try to recover the original\n    /// let error = JsError::from_opaque(error_val).try_native(context).unwrap();\n    ///\n    /// assert!(matches!(error.kind(), JsNativeErrorKind::Type));\n    /// assert_eq!(error.message(), \"type error!\");\n    /// ```\n    pub fn try_native(&self, context: &mut Context) -> Result<JsNativeError, TryNativeError> {\n        match &self.inner {\n            Repr::Engine(e) => Err(TryNativeError::EngineError { source: e.clone() }),\n            Repr::Native(e) => Ok(e.as_ref().clone()),\n            Repr::Opaque(val) => {\n                let obj = val\n                    .as_object()\n                    .ok_or_else(|| TryNativeError::NotAnErrorObject(val.clone()))?;\n                let error_data: Error = obj\n                    .downcast_ref::<Error>()\n                    .ok_or_else(|| TryNativeError::NotAnErrorObject(val.clone()))?\n                    .clone();\n\n                let try_get_property = |key: JsString, name, context: &mut Context| {\n                    obj.try_get(key, context)\n                        .map_err(|e| TryNativeError::InaccessibleProperty {\n                            property: name,\n                            source: e,\n                        })\n                };\n\n                let message = if let Some(msg) =\n                    try_get_property(js_string!(\"message\"), \"message\", context)?\n                {\n                    Cow::Owned(\n                        msg.as_string()\n                            .as_ref()\n                            .map(JsString::to_std_string)\n                            .transpose()\n                            .map_err(|_| TryNativeError::InvalidMessageEncoding)?\n                            .ok_or(TryNativeError::InvalidPropertyType(\"message\"))?,\n                    )\n                } else {\n                    Cow::Borrowed(\"\")\n                };\n\n                let cause = try_get_property(js_string!(\"cause\"), \"cause\", context)?;\n\n                let position = error_data.position.clone();\n                let kind = match error_data.tag {\n                    ErrorKind::Error => JsNativeErrorKind::Error,\n                    ErrorKind::Eval => JsNativeErrorKind::Eval,\n                    ErrorKind::Type => JsNativeErrorKind::Type,\n                    ErrorKind::Range => JsNativeErrorKind::Range,\n                    ErrorKind::Reference => JsNativeErrorKind::Reference,\n                    ErrorKind::Syntax => JsNativeErrorKind::Syntax,\n                    ErrorKind::Uri => JsNativeErrorKind::Uri,\n                    ErrorKind::Aggregate => {\n                        let errors = obj.get(js_string!(\"errors\"), context).map_err(|e| {\n                            TryNativeError::InaccessibleProperty {\n                                property: \"errors\",\n                                source: e,\n                            }\n                        })?;\n                        let mut error_list = Vec::new();\n                        match errors.as_object() {\n                            Some(errors) if errors.is_array() => {\n                                let length = errors.length_of_array_like(context).map_err(|e| {\n                                    TryNativeError::InaccessibleProperty {\n                                        property: \"errors.length\",\n                                        source: e,\n                                    }\n                                })?;\n                                error_list.reserve(length as usize);\n                                for i in 0..length {\n                                    error_list.push(Self::from_opaque(\n                                        errors.get(i, context).map_err(|e| {\n                                            TryNativeError::InvalidErrorsIndex {\n                                                index: i,\n                                                source: e,\n                                            }\n                                        })?,\n                                    ));\n                                }\n                            }\n                            _ => return Err(TryNativeError::InvalidPropertyType(\"errors\")),\n                        }\n\n                        JsNativeErrorKind::Aggregate(error_list)\n                    }\n                };\n\n                let realm = try_get_property(js_string!(\"constructor\"), \"constructor\", context)?\n                    .as_ref()\n                    .and_then(JsValue::as_constructor)\n                    .ok_or(TryNativeError::InvalidConstructor)?\n                    .get_function_realm(context)\n                    .map_err(|err| TryNativeError::InaccessibleRealm { source: err })?;\n\n                Ok(JsNativeError {\n                    kind,\n                    message,\n                    cause: cause.map(|v| Box::new(Self::from_opaque(v))),\n                    realm: Some(realm),\n                    position,\n                })\n            }\n        }\n    }\n\n    /// Gets the inner [`JsValue`] if the error is an opaque error,\n    /// or `None` otherwise.\n    ///\n    /// # Examples\n    ///\n    /// ```rust\n    /// # use boa_engine::{JsError, JsNativeError};\n    /// let error: JsError = JsNativeError::reference()\n    ///     .with_message(\"variable not found!\")\n    ///     .into();\n    ///\n    /// assert!(error.as_opaque().is_none());\n    ///\n    /// let error = JsError::from_opaque(256u32.into());\n    ///\n    /// assert!(error.as_opaque().is_some());\n    /// ```\n    #[must_use]\n    pub const fn as_opaque(&self) -> Option<&JsValue> {\n        match self.inner {\n            Repr::Native(_) | Repr::Engine(_) => None,\n            Repr::Opaque(ref v) => Some(v),\n        }\n    }\n\n    /// Gets the inner [`JsNativeError`] if the error is a native\n    /// error, or `None` otherwise.\n    ///\n    /// # Examples\n    ///\n    /// ```rust\n    /// # use boa_engine::{JsError, JsNativeError, JsValue};\n    /// let error: JsError =\n    ///     JsNativeError::error().with_message(\"Unknown error\").into();\n    ///\n    /// assert!(error.as_native().is_some());\n    ///\n    /// let error = JsError::from_opaque(JsValue::undefined());\n    ///\n    /// assert!(error.as_native().is_none());\n    /// ```\n    #[must_use]\n    pub const fn as_native(&self) -> Option<&JsNativeError> {\n        match &self.inner {\n            Repr::Native(e) => Some(e),\n            Repr::Opaque(_) | Repr::Engine(_) => None,\n        }\n    }\n\n    /// Gets the inner [`EngineError`] if the error is an engine\n    /// error, or `None` otherwise.\n    #[must_use]\n    pub const fn as_engine(&self) -> Option<&EngineError> {\n        match &self.inner {\n            Repr::Opaque(_) | Repr::Native(_) => None,\n            Repr::Engine(err) => Some(err),\n        }\n    }\n\n    /// Converts this error into its thread-safe, erased version.\n    ///\n    /// Even though this operation is lossy, converting into a `JsErasedError`\n    /// is useful since it implements `Send` and `Sync`, making it compatible with\n    /// error reporting frameworks such as `anyhow`, `eyre` or `miette`.\n    ///\n    /// # Examples\n    ///\n    /// ```rust\n    /// # use boa_engine::{js_string, Context, JsError, JsNativeError, JsSymbol, JsValue};\n    /// # use std::error::Error;\n    /// let context = &mut Context::default();\n    /// let cause = JsError::from_opaque(JsSymbol::new(Some(js_string!(\"error!\"))).unwrap().into());\n    ///\n    /// let native_error: JsError = JsNativeError::typ()\n    ///     .with_message(\"invalid type!\")\n    ///     .with_cause(cause)\n    ///     .into();\n    ///\n    /// let erased_error = native_error.into_erased(context);\n    ///\n    /// assert_eq!(erased_error.to_string(), \"TypeError: invalid type!\");\n    ///\n    /// let send_sync_error: Box<dyn Error + Send + Sync> = Box::new(erased_error);\n    ///\n    /// assert_eq!(\n    ///     send_sync_error.source().unwrap().to_string(),\n    ///     \"Symbol(error!)\"\n    /// );\n    /// ```\n    pub fn into_erased(self, context: &mut Context) -> JsErasedError {\n        let native = match self.try_native(context) {\n            Ok(native) => native,\n            Err(TryNativeError::EngineError { source }) => {\n                return JsErasedError {\n                    inner: ErasedRepr::Engine(source.into_erased(context)),\n                };\n            }\n            Err(_) => {\n                return JsErasedError {\n                    inner: ErasedRepr::Opaque(Cow::Owned(self.to_string())),\n                };\n            }\n        };\n\n        let JsNativeError {\n            kind,\n            message,\n            cause,\n            ..\n        } = native;\n\n        let cause = cause.map(|err| Box::new(err.into_erased(context)));\n\n        let kind = match kind {\n            JsNativeErrorKind::Aggregate(errors) => JsErasedNativeErrorKind::Aggregate(\n                errors\n                    .into_iter()\n                    .map(|err| err.into_erased(context))\n                    .collect(),\n            ),\n            JsNativeErrorKind::Error => JsErasedNativeErrorKind::Error,\n            JsNativeErrorKind::Eval => JsErasedNativeErrorKind::Eval,\n            JsNativeErrorKind::Range => JsErasedNativeErrorKind::Range,\n            JsNativeErrorKind::Reference => JsErasedNativeErrorKind::Reference,\n            JsNativeErrorKind::Syntax => JsErasedNativeErrorKind::Syntax,\n            JsNativeErrorKind::Type => JsErasedNativeErrorKind::Type,\n            JsNativeErrorKind::Uri => JsErasedNativeErrorKind::Uri,\n        };\n\n        JsErasedError {\n            inner: ErasedRepr::Native(JsErasedNativeError {\n                kind,\n                message,\n                cause,\n            }),\n        }\n    }\n\n    /// Injects a realm on the `realm` field of a native error.\n    ///\n    /// This is a no-op if the error is not native or if the `realm` field of the error is already\n    /// set.\n    pub(crate) fn inject_realm(mut self, realm: Realm) -> Self {\n        match &mut self.inner {\n            Repr::Native(err) if err.realm.is_none() => {\n                err.realm = Some(realm);\n            }\n            _ => {}\n        }\n        self\n    }\n\n    /// Is the [`JsError`] catchable in JavaScript.\n    #[inline]\n    pub(crate) const fn is_catchable(&self) -> bool {\n        self.as_engine().is_none()\n    }\n}\n\nimpl From<boa_parser::Error> for JsError {\n    #[cfg_attr(feature = \"native-backtrace\", track_caller)]\n    fn from(err: boa_parser::Error) -> Self {\n        Self::from(JsNativeError::from(err))\n    }\n}\n\nimpl From<JsNativeError> for JsError {\n    fn from(error: JsNativeError) -> Self {\n        Self {\n            inner: Repr::Native(Box::new(error)),\n            backtrace: None,\n        }\n    }\n}\n\nimpl From<EngineError> for JsError {\n    fn from(value: EngineError) -> Self {\n        Self {\n            inner: Repr::Engine(value),\n            backtrace: None,\n        }\n    }\n}\n\nimpl From<RuntimeLimitError> for JsError {\n    fn from(value: RuntimeLimitError) -> Self {\n        EngineError::from(value).into()\n    }\n}\n\nimpl fmt::Display for JsError {\n    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {\n        match &self.inner {\n            Repr::Native(e) => e.fmt(f)?,\n            Repr::Engine(e) => e.fmt(f)?,\n            Repr::Opaque(v) => v.display().fmt(f)?,\n        }\n\n        if let Some(shadow_stack) = &self.backtrace {\n            for entry in shadow_stack.iter().rev() {\n                write!(f, \"\\n    at \")?;\n                match entry {\n                    ShadowEntry::Native {\n                        function_name,\n                        source_info,\n                    } => {\n                        if let Some(function_name) = function_name {\n                            write!(f, \"{}\", function_name.to_std_string_escaped())?;\n                        } else {\n                            f.write_str(\"<anonymous>\")?;\n                        }\n\n                        if let Some(loc) = source_info.as_location() {\n                            write!(\n                                f,\n                                \" (native at {}:{}:{})\",\n                                loc.file(),\n                                loc.line(),\n                                loc.column()\n                            )?;\n                        } else {\n                            f.write_str(\" (native)\")?;\n                        }\n                    }\n                    ShadowEntry::Bytecode { pc, source_info } => {\n                        let has_function_name = !source_info.function_name().is_empty();\n                        if has_function_name {\n                            write!(f, \"{}\", source_info.function_name().to_std_string_escaped(),)?;\n                        } else {\n                            f.write_str(\"<anonymous>\")?;\n                        }\n\n                        f.write_str(\" (\")?;\n                        source_info.map().path().fmt(f)?;\n\n                        if let Some(position) = source_info.map().find(*pc) {\n                            write!(\n                                f,\n                                \":{}:{}\",\n                                position.line_number(),\n                                position.column_number()\n                            )?;\n                        } else {\n                            f.write_str(\":?:?\")?;\n                        }\n                        f.write_str(\")\")?;\n                    }\n                }\n            }\n        }\n        Ok(())\n    }\n}\n\n/// Helper struct that ignores equality operator.\n#[derive(Debug, Clone, Finalize)]\npub(crate) struct IgnoreEq<T>(pub(crate) T);\n\nimpl<T> Eq for IgnoreEq<T> {}\n\nimpl<T> PartialEq for IgnoreEq<T> {\n    #[inline]\n    fn eq(&self, _: &Self) -> bool {\n        true\n    }\n}\n\nimpl<T> std::hash::Hash for IgnoreEq<T> {\n    fn hash<H: std::hash::Hasher>(&self, _state: &mut H) {}\n}\n\nimpl<T> From<T> for IgnoreEq<T> {\n    #[inline]\n    fn from(value: T) -> Self {\n        Self(value)\n    }\n}\n\n/// Native representation of an ideal `Error` object from Javascript.\n///\n/// This representation is more space efficient than its [`JsObject`] equivalent,\n/// since it doesn't need to create a whole new `JsObject` to be instantiated.\n/// Prefer using this over [`JsError`] when you don't need to throw\n/// plain [`JsValue`]s as errors, or when you need to inspect the error type\n/// of a `JsError`.\n///\n/// # Examples\n///\n/// ```rust\n/// # use boa_engine::{JsNativeError, JsNativeErrorKind};\n/// let native_error = JsNativeError::uri().with_message(\"cannot decode uri\");\n///\n/// match native_error.kind() {\n///     JsNativeErrorKind::Uri => { /* handle URI error*/ }\n///     _ => unreachable!(),\n/// }\n///\n/// assert_eq!(native_error.message(), \"cannot decode uri\");\n/// ```\n#[derive(Clone, Finalize, Error, PartialEq, Eq)]\npub struct JsNativeError {\n    /// The kind of native error (e.g. `TypeError`, `SyntaxError`, etc.)\n    kind: JsNativeErrorKind,\n    message: Cow<'static, str>,\n    #[source]\n    cause: Option<Box<JsError>>,\n    realm: Option<Realm>,\n    position: IgnoreEq<Option<ShadowEntry>>,\n}\n\nimpl fmt::Display for JsNativeError {\n    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {\n        write!(f, \"{}\", self.kind)?;\n\n        let message = self.message.trim();\n        if !message.is_empty() {\n            write!(f, \": {message}\")?;\n        }\n\n        if let Some(position) = &self.position.0 {\n            position.fmt(f)?;\n        }\n\n        Ok(())\n    }\n}\n\n// SAFETY: just mirroring the default derive to allow destructuring.\nunsafe impl Trace for JsNativeError {\n    custom_trace!(this, mark, {\n        mark(&this.kind);\n        mark(&this.cause);\n        mark(&this.realm);\n    });\n}\n\nimpl fmt::Debug for JsNativeError {\n    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {\n        f.debug_struct(\"JsNativeError\")\n            .field(\"kind\", &self.kind)\n            .field(\"message\", &self.message)\n            .field(\"cause\", &self.cause)\n            .finish_non_exhaustive()\n    }\n}\n\nimpl JsNativeError {\n    /// Default `AggregateError` kind `JsNativeError`.\n    pub const AGGREGATE: Self = Self::aggregate(Vec::new());\n    /// Default `Error` kind `JsNativeError`.\n    pub const ERROR: Self = Self::error();\n    /// Default `EvalError` kind `JsNativeError`.\n    pub const EVAL: Self = Self::eval();\n    /// Default `RangeError` kind `JsNativeError`.\n    pub const RANGE: Self = Self::range();\n    /// Default `ReferenceError` kind `JsNativeError`.\n    pub const REFERENCE: Self = Self::reference();\n    /// Default `SyntaxError` kind `JsNativeError`.\n    pub const SYNTAX: Self = Self::syntax();\n    /// Default `error` kind `JsNativeError`.\n    pub const TYP: Self = Self::typ();\n    /// Default `UriError` kind `JsNativeError`.\n    pub const URI: Self = Self::uri();\n\n    /// Returns the kind of this native error.\n    #[must_use]\n    #[inline]\n    pub const fn kind(&self) -> &JsNativeErrorKind {\n        &self.kind\n    }\n\n    /// Creates a new `JsNativeError` from its `kind`, `message` and (optionally) its `cause`.\n    #[cfg_attr(feature = \"native-backtrace\", track_caller)]\n    const fn new(\n        kind: JsNativeErrorKind,\n        message: Cow<'static, str>,\n        cause: Option<Box<JsError>>,\n    ) -> Self {\n        if let Some(cause) = &cause\n            && cause.as_engine().is_some()\n        {\n            panic!(\"engine errors cannot be used as the cause of another error\");\n        }\n        Self {\n            kind,\n            message,\n            cause,\n            realm: None,\n            position: IgnoreEq(Some(ShadowEntry::Native {\n                function_name: None,\n                source_info: NativeSourceInfo::caller(),\n            })),\n        }\n    }\n\n    /// Creates a new `JsNativeError` of kind `AggregateError` from a list of [`JsError`]s, with\n    /// empty `message` and undefined `cause`.\n    ///\n    /// # Examples\n    ///\n    /// ```rust\n    /// # use boa_engine::{JsNativeError, JsNativeErrorKind};\n    /// let inner_errors = vec![\n    ///     JsNativeError::typ().into(),\n    ///     JsNativeError::syntax().into()\n    /// ];\n    /// let error = JsNativeError::aggregate(inner_errors);\n    ///\n    /// assert!(matches!(\n    ///     error.kind(),\n    ///     JsNativeErrorKind::Aggregate(errors) if errors.len() == 2\n    /// ));\n    /// ```\n    #[must_use]\n    #[inline]\n    #[cfg_attr(feature = \"native-backtrace\", track_caller)]\n    pub const fn aggregate(errors: Vec<JsError>) -> Self {\n        Self::new(\n            JsNativeErrorKind::Aggregate(errors),\n            Cow::Borrowed(\"\"),\n            None,\n        )\n    }\n\n    /// Check if it's a [`JsNativeErrorKind::Aggregate`].\n    #[must_use]\n    #[inline]\n    pub const fn is_aggregate(&self) -> bool {\n        matches!(self.kind, JsNativeErrorKind::Aggregate(_))\n    }\n\n    /// Creates a new `JsNativeError` of kind `Error`, with empty `message` and undefined `cause`.\n    ///\n    /// # Examples\n    ///\n    /// ```rust\n    /// # use boa_engine::{JsNativeError, JsNativeErrorKind};\n    /// let error = JsNativeError::error();\n    ///\n    /// assert!(matches!(error.kind(), JsNativeErrorKind::Error));\n    /// ```\n    #[must_use]\n    #[inline]\n    #[cfg_attr(feature = \"native-backtrace\", track_caller)]\n    pub const fn error() -> Self {\n        Self::new(JsNativeErrorKind::Error, Cow::Borrowed(\"\"), None)\n    }\n\n    /// Check if it's a [`JsNativeErrorKind::Error`].\n    #[must_use]\n    #[inline]\n    pub const fn is_error(&self) -> bool {\n        matches!(self.kind, JsNativeErrorKind::Error)\n    }\n\n    /// Creates a new `JsNativeError` of kind `EvalError`, with empty `message` and undefined `cause`.\n    ///\n    /// # Examples\n    ///\n    /// ```rust\n    /// # use boa_engine::{JsNativeError, JsNativeErrorKind};\n    /// let error = JsNativeError::eval();\n    ///\n    /// assert!(matches!(error.kind(), JsNativeErrorKind::Eval));\n    /// ```\n    #[must_use]\n    #[inline]\n    #[cfg_attr(feature = \"native-backtrace\", track_caller)]\n    pub const fn eval() -> Self {\n        Self::new(JsNativeErrorKind::Eval, Cow::Borrowed(\"\"), None)\n    }\n\n    /// Check if it's a [`JsNativeErrorKind::Eval`].\n    #[must_use]\n    #[inline]\n    pub const fn is_eval(&self) -> bool {\n        matches!(self.kind, JsNativeErrorKind::Eval)\n    }\n\n    /// Creates a new `JsNativeError` of kind `RangeError`, with empty `message` and undefined `cause`.\n    ///\n    /// # Examples\n    ///\n    /// ```rust\n    /// # use boa_engine::{JsNativeError, JsNativeErrorKind};\n    /// let error = JsNativeError::range();\n    ///\n    /// assert!(matches!(error.kind(), JsNativeErrorKind::Range));\n    /// ```\n    #[must_use]\n    #[inline]\n    #[cfg_attr(feature = \"native-backtrace\", track_caller)]\n    pub const fn range() -> Self {\n        Self::new(JsNativeErrorKind::Range, Cow::Borrowed(\"\"), None)\n    }\n\n    /// Check if it's a [`JsNativeErrorKind::Range`].\n    #[must_use]\n    #[inline]\n    pub const fn is_range(&self) -> bool {\n        matches!(self.kind, JsNativeErrorKind::Range)\n    }\n\n    /// Creates a new `JsNativeError` of kind `ReferenceError`, with empty `message` and undefined `cause`.\n    ///\n    /// # Examples\n    ///\n    /// ```rust\n    /// # use boa_engine::{JsNativeError, JsNativeErrorKind};\n    /// let error = JsNativeError::reference();\n    ///\n    /// assert!(matches!(error.kind(), JsNativeErrorKind::Reference));\n    /// ```\n    #[must_use]\n    #[inline]\n    #[cfg_attr(feature = \"native-backtrace\", track_caller)]\n    pub const fn reference() -> Self {\n        Self::new(JsNativeErrorKind::Reference, Cow::Borrowed(\"\"), None)\n    }\n\n    /// Check if it's a [`JsNativeErrorKind::Reference`].\n    #[must_use]\n    #[inline]\n    pub const fn is_reference(&self) -> bool {\n        matches!(self.kind, JsNativeErrorKind::Reference)\n    }\n\n    /// Creates a new `JsNativeError` of kind `SyntaxError`, with empty `message` and undefined `cause`.\n    ///\n    /// # Examples\n    ///\n    /// ```rust\n    /// # use boa_engine::{JsNativeError, JsNativeErrorKind};\n    /// let error = JsNativeError::syntax();\n    ///\n    /// assert!(matches!(error.kind(), JsNativeErrorKind::Syntax));\n    /// ```\n    #[must_use]\n    #[inline]\n    #[cfg_attr(feature = \"native-backtrace\", track_caller)]\n    pub const fn syntax() -> Self {\n        Self::new(JsNativeErrorKind::Syntax, Cow::Borrowed(\"\"), None)\n    }\n\n    /// Check if it's a [`JsNativeErrorKind::Syntax`].\n    #[must_use]\n    #[inline]\n    pub const fn is_syntax(&self) -> bool {\n        matches!(self.kind, JsNativeErrorKind::Syntax)\n    }\n\n    /// Creates a new `JsNativeError` of kind `TypeError`, with empty `message` and undefined `cause`.\n    ///\n    /// # Examples\n    ///\n    /// ```rust\n    /// # use boa_engine::{JsNativeError, JsNativeErrorKind};\n    /// let error = JsNativeError::typ();\n    ///\n    /// assert!(matches!(error.kind(), JsNativeErrorKind::Type));\n    /// ```\n    #[must_use]\n    #[inline]\n    #[cfg_attr(feature = \"native-backtrace\", track_caller)]\n    pub const fn typ() -> Self {\n        Self::new(JsNativeErrorKind::Type, Cow::Borrowed(\"\"), None)\n    }\n\n    /// Check if it's a [`JsNativeErrorKind::Type`].\n    #[must_use]\n    #[inline]\n    pub const fn is_type(&self) -> bool {\n        matches!(self.kind, JsNativeErrorKind::Type)\n    }\n\n    /// Creates a new `JsNativeError` of kind `UriError`, with empty `message` and undefined `cause`.\n    ///\n    /// # Examples\n    ///\n    /// ```rust\n    /// # use boa_engine::{JsNativeError, JsNativeErrorKind};\n    /// let error = JsNativeError::uri();\n    ///\n    /// assert!(matches!(error.kind(), JsNativeErrorKind::Uri));\n    /// ```\n    #[must_use]\n    #[inline]\n    #[cfg_attr(feature = \"native-backtrace\", track_caller)]\n    pub const fn uri() -> Self {\n        Self::new(JsNativeErrorKind::Uri, Cow::Borrowed(\"\"), None)\n    }\n\n    /// Check if it's a [`JsNativeErrorKind::Uri`].\n    #[must_use]\n    #[inline]\n    pub const fn is_uri(&self) -> bool {\n        matches!(self.kind, JsNativeErrorKind::Uri)\n    }\n\n    /// Sets the message of this error.\n    ///\n    /// # Examples\n    ///\n    /// ```rust\n    /// # use boa_engine::JsNativeError;\n    /// let error = JsNativeError::range().with_message(\"number too large\");\n    ///\n    /// assert_eq!(error.message(), \"number too large\");\n    /// ```\n    #[must_use]\n    #[inline]\n    pub fn with_message<S>(mut self, message: S) -> Self\n    where\n        S: Into<Cow<'static, str>>,\n    {\n        self.message = message.into();\n        self\n    }\n\n    /// Sets the cause of this error.\n    ///\n    /// # Examples\n    ///\n    /// ```rust\n    /// # use boa_engine::JsNativeError;\n    /// let cause = JsNativeError::syntax();\n    /// let error = JsNativeError::error().with_cause(cause);\n    ///\n    /// assert!(error.cause().unwrap().as_native().is_some());\n    /// ```\n    ///\n    /// # Panics\n    ///\n    /// Panics if `cause` is an uncatchable error (i.e. an engine error).\n    #[must_use]\n    #[inline]\n    pub fn with_cause<V>(mut self, cause: V) -> Self\n    where\n        V: Into<JsError>,\n    {\n        let err = cause.into();\n        assert!(\n            err.is_catchable(),\n            \"uncatchable errors cannot be used as the cause of another error\",\n        );\n\n        self.cause = Some(Box::new(err));\n        self\n    }\n\n    /// Gets the `message` of this error.\n    ///\n    /// This is equivalent to the [`NativeError.prototype.message`][spec]\n    /// property.\n    ///\n    /// [spec]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Error/message\n    ///\n    /// # Examples\n    ///\n    /// ```rust\n    /// # use boa_engine::JsNativeError;\n    /// let error = JsNativeError::range().with_message(\"number too large\");\n    ///\n    /// assert_eq!(error.message(), \"number too large\");\n    /// ```\n    #[must_use]\n    #[inline]\n    pub fn message(&self) -> &str {\n        &self.message\n    }\n\n    /// Gets the `cause` of this error.\n    ///\n    /// This is equivalent to the [`NativeError.prototype.cause`][spec]\n    /// property.\n    ///\n    /// [spec]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Error/cause\n    ///\n    /// # Examples\n    ///\n    /// ```rust\n    /// # use boa_engine::JsNativeError;\n    /// let cause = JsNativeError::syntax();\n    /// let error = JsNativeError::error().with_cause(cause);\n    ///\n    /// assert!(error.cause().unwrap().as_native().is_some());\n    /// ```\n    #[must_use]\n    #[inline]\n    pub fn cause(&self) -> Option<&JsError> {\n        self.cause.as_deref()\n    }\n\n    /// Converts this native error to its opaque representation as a [`JsObject`].\n    ///\n    /// # Examples\n    ///\n    /// ```rust\n    /// # use boa_engine::{Context, JsError, JsNativeError, js_string};\n    /// # use boa_engine::builtins::error::Error;\n    /// let context = &mut Context::default();\n    ///\n    /// let error = JsNativeError::error().with_message(\"error!\");\n    /// let error_obj = error.into_opaque(context);\n    ///\n    /// assert!(error_obj.is::<Error>());\n    /// assert_eq!(\n    ///     error_obj.get(js_string!(\"message\"), context).unwrap(),\n    ///     js_string!(\"error!\").into()\n    /// )\n    /// ```\n    #[inline]\n    pub fn into_opaque(self, context: &mut Context) -> JsObject {\n        let Self {\n            kind,\n            message,\n            cause,\n            realm,\n            position,\n        } = self;\n        let constructors = realm.as_ref().map_or_else(\n            || context.intrinsics().constructors(),\n            |realm| realm.intrinsics().constructors(),\n        );\n        let (prototype, tag) = match kind {\n            JsNativeErrorKind::Aggregate(_) => (\n                constructors.aggregate_error().prototype(),\n                ErrorKind::Aggregate,\n            ),\n            JsNativeErrorKind::Error => (constructors.error().prototype(), ErrorKind::Error),\n            JsNativeErrorKind::Eval => (constructors.eval_error().prototype(), ErrorKind::Eval),\n            JsNativeErrorKind::Range => (constructors.range_error().prototype(), ErrorKind::Range),\n            JsNativeErrorKind::Reference => (\n                constructors.reference_error().prototype(),\n                ErrorKind::Reference,\n            ),\n            JsNativeErrorKind::Syntax => {\n                (constructors.syntax_error().prototype(), ErrorKind::Syntax)\n            }\n            JsNativeErrorKind::Type => (constructors.type_error().prototype(), ErrorKind::Type),\n            JsNativeErrorKind::Uri => (constructors.uri_error().prototype(), ErrorKind::Uri),\n        };\n\n        let o = JsObject::from_proto_and_data_with_shared_shape(\n            context.root_shape(),\n            prototype,\n            Error::with_shadow_entry(tag, position.0.clone()),\n        )\n        .upcast();\n\n        o.create_non_enumerable_data_property_or_throw(\n            js_string!(\"message\"),\n            js_string!(message.as_ref()),\n            context,\n        );\n\n        if let Some(cause) = cause {\n            o.create_non_enumerable_data_property_or_throw(\n                js_string!(\"cause\"),\n                cause\n                    .into_opaque(context)\n                    .expect(\"engine errors cannot be the cause of another error\"),\n                context,\n            );\n        }\n\n        if let JsNativeErrorKind::Aggregate(errors) = kind {\n            let errors = errors\n                .into_iter()\n                .map(|e| {\n                    e.into_opaque(context)\n                        .expect(\"engine errors cannot be the cause of another error\")\n                })\n                .collect::<Vec<_>>();\n            let errors = Array::create_array_from_list(errors, context);\n            o.define_property_or_throw(\n                js_string!(\"errors\"),\n                PropertyDescriptor::builder()\n                    .configurable(true)\n                    .enumerable(false)\n                    .writable(true)\n                    .value(errors),\n                context,\n            )\n            .expect(\"The spec guarantees this succeeds for a newly created object \");\n        }\n        o\n    }\n\n    /// Sets the realm of this error.\n    pub(crate) fn with_realm(mut self, realm: Realm) -> Self {\n        self.realm = Some(realm);\n        self\n    }\n}\n\nimpl From<boa_parser::Error> for JsNativeError {\n    #[cfg_attr(feature = \"native-backtrace\", track_caller)]\n    fn from(err: boa_parser::Error) -> Self {\n        Self::syntax().with_message(err.to_string())\n    }\n}\n\n/// The list of possible error types a [`JsNativeError`] can be.\n///\n/// More information:\n/// - [ECMAScript reference][spec]\n/// - [MDN documentation][mdn]\n///\n/// [spec]: https://tc39.es/ecma262/#sec-error-objects\n/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Error\n#[derive(Debug, Clone, Finalize, PartialEq, Eq)]\n#[non_exhaustive]\npub enum JsNativeErrorKind {\n    /// A collection of errors wrapped in a single error.\n    ///\n    /// More information:\n    /// - [ECMAScript reference][spec]\n    /// - [MDN documentation][mdn]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-aggregate-error-objects\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/AggregateError\n    Aggregate(Vec<JsError>),\n    /// A generic error. Commonly used as the base for custom exceptions.\n    ///\n    /// More information:\n    /// - [ECMAScript reference][spec]\n    /// - [MDN documentation][mdn]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-error-constructor\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Error\n    Error,\n    /// An error related to the global function [`eval()`][eval].\n    ///\n    /// More information:\n    /// - [ECMAScript reference][spec]\n    /// - [MDN documentation][mdn]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-native-error-types-used-in-this-standard-evalerror\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/EvalError\n    /// [eval]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/eval\n    Eval,\n    /// An error thrown when a value is outside its valid range.\n    ///\n    /// More information:\n    /// - [ECMAScript reference][spec]\n    /// - [MDN documentation][mdn]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-native-error-types-used-in-this-standard-rangeerror\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RangeError\n    Range,\n    /// An error representing an invalid de-reference of a variable.\n    ///\n    /// More information:\n    /// - [ECMAScript reference][spec]\n    /// - [MDN documentation][mdn]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-native-error-types-used-in-this-standard-referenceerror\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/ReferenceError\n    Reference,\n    /// An error representing an invalid syntax in the Javascript language.\n    ///\n    /// More information:\n    /// - [ECMAScript reference][spec]\n    /// - [MDN documentation][mdn]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-native-error-types-used-in-this-standard-syntaxerror\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/SyntaxError\n    Syntax,\n    /// An error thrown when a variable or argument is not of a valid type.\n    ///\n    /// More information:\n    /// - [ECMAScript reference][spec]\n    /// - [MDN documentation][mdn]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-native-error-types-used-in-this-standard-typeerror\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/TypeError\n    Type,\n    /// An error thrown when the [`encodeURI()`][e_uri] and [`decodeURI()`][d_uri] functions receive\n    /// invalid parameters.\n    ///\n    /// More information:\n    /// - [ECMAScript reference][spec]\n    /// - [MDN documentation][mdn]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-native-error-types-used-in-this-standard-urierror\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/URIError\n    /// [e_uri]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/encodeURI\n    /// [d_uri]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/decodeURI\n    Uri,\n}\n\n// SAFETY: just mirroring the default derive to allow destructuring.\nunsafe impl Trace for JsNativeErrorKind {\n    custom_trace!(\n        this,\n        mark,\n        match &this {\n            Self::Aggregate(errors) => mark(errors),\n            Self::Error\n            | Self::Eval\n            | Self::Range\n            | Self::Reference\n            | Self::Syntax\n            | Self::Type\n            | Self::Uri => {}\n        }\n    );\n}\n\nimpl PartialEq<ErrorKind> for JsNativeErrorKind {\n    fn eq(&self, other: &ErrorKind) -> bool {\n        matches!(\n            (self, other),\n            (Self::Aggregate(_), ErrorKind::Aggregate)\n                | (Self::Error, ErrorKind::Error)\n                | (Self::Eval, ErrorKind::Eval)\n                | (Self::Range, ErrorKind::Range)\n                | (Self::Reference, ErrorKind::Reference)\n                | (Self::Syntax, ErrorKind::Syntax)\n                | (Self::Type, ErrorKind::Type)\n                | (Self::Uri, ErrorKind::Uri)\n        )\n    }\n}\n\nimpl fmt::Display for JsNativeErrorKind {\n    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {\n        match self {\n            Self::Aggregate(_) => \"AggregateError\",\n            Self::Error => \"Error\",\n            Self::Eval => \"EvalError\",\n            Self::Range => \"RangeError\",\n            Self::Reference => \"ReferenceError\",\n            Self::Syntax => \"SyntaxError\",\n            Self::Type => \"TypeError\",\n            Self::Uri => \"UriError\",\n        }\n        .fmt(f)\n    }\n}\n\n/// Erased version of [`PanicError`].\n///\n/// This is mainly useful to convert a `PanicError` into an `ErasedPanicError` that also\n/// implements `Send + Sync`, which makes it compatible with error reporting tools\n/// such as `anyhow`, `eyre` or `miette`.\n///\n/// Generally, the conversion from `PanicError` to `ErasedPanicError` is unidirectional,\n/// since any `JsError` that is a [`JsValue`] is converted to its string representation\n/// instead. This will lose information if that value was an object, a symbol or a big int.\n#[derive(Debug, Clone, Error, Eq, PartialEq, Trace, Finalize)]\n#[error(\"{message}\")]\n#[must_use]\npub struct ErasedPanicError {\n    message: Box<str>,\n    source: Option<Box<JsErasedError>>,\n}\n\nimpl ErasedPanicError {\n    /// Gets the message of this `ErasedPanicError`.\n    #[must_use]\n    pub fn message(&self) -> &str {\n        &self.message\n    }\n}\n\n/// Erased version of [`EngineError`].\n///\n/// This is mainly useful to convert an `EngineError` into an `ErasedEngineError` that also\n/// implements `Send + Sync`, which makes it compatible with error reporting tools\n/// such as `anyhow`, `eyre` or `miette`.\n///\n/// Generally, the conversion from `EngineError` to `ErasedEngineError` is unidirectional,\n/// since any `JsError` that is a [`JsValue`] is converted to its string representation\n/// instead. This will lose information if that value was an object, a symbol or a big int.\n#[derive(Debug, Clone, Error, Eq, PartialEq, Trace, Finalize)]\n#[allow(variant_size_differences)]\npub enum ErasedEngineError {\n    /// Error thrown when no instructions remain. Only used in a fuzzing context.\n    #[cfg(feature = \"fuzz\")]\n    #[error(\"NoInstructionsRemainError: instruction budget was exhausted\")]\n    NoInstructionsRemain,\n\n    /// Error thrown when a runtime limit is exceeded.\n    #[error(\"RuntimeLimitError: {0}\")]\n    RuntimeLimit(#[from] RuntimeLimitError),\n\n    /// Error thrown when an internal panic condition is encountered.\n    #[error(\"EnginePanic: {0}\")]\n    Panic(#[from] ErasedPanicError),\n}\n\n/// Erased version of [`JsError`].\n///\n/// This is mainly useful to convert a `JsError` into an `Error` that also\n/// implements `Send + Sync`, which makes it compatible with error reporting tools\n/// such as `anyhow`, `eyre` or `miette`.\n///\n/// Generally, the conversion from `JsError` to `JsErasedError` is unidirectional,\n/// since any `JsError` that is a [`JsValue`] is converted to its string representation\n/// instead. This will lose information if that value was an object, a symbol or a big int.\n#[derive(Debug, Clone, Trace, Finalize, PartialEq, Eq)]\npub struct JsErasedError {\n    inner: ErasedRepr,\n}\n\n#[derive(Debug, Clone, Trace, Finalize, PartialEq, Eq)]\nenum ErasedRepr {\n    Native(JsErasedNativeError),\n    Opaque(Cow<'static, str>),\n    Engine(ErasedEngineError),\n}\n\nimpl fmt::Display for JsErasedError {\n    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {\n        match &self.inner {\n            ErasedRepr::Native(e) => e.fmt(f),\n            ErasedRepr::Opaque(v) => v.fmt(f),\n            ErasedRepr::Engine(e) => e.fmt(f),\n        }\n    }\n}\n\nimpl error::Error for JsErasedError {\n    fn source(&self) -> Option<&(dyn error::Error + 'static)> {\n        match &self.inner {\n            ErasedRepr::Native(err) => err.source(),\n            ErasedRepr::Opaque(_) => None,\n            ErasedRepr::Engine(err) => err.source(),\n        }\n    }\n}\n\nimpl JsErasedError {\n    /// Gets the inner [`str`] if the error is an opaque error,\n    /// or `None` otherwise.\n    #[must_use]\n    pub fn as_opaque(&self) -> Option<&str> {\n        match &self.inner {\n            ErasedRepr::Native(_) | ErasedRepr::Engine(_) => None,\n            ErasedRepr::Opaque(v) => Some(v),\n        }\n    }\n\n    /// Gets the inner [`JsErasedNativeError`] if the error is a native\n    /// error, or `None` otherwise.\n    #[must_use]\n    pub const fn as_native(&self) -> Option<&JsErasedNativeError> {\n        match &self.inner {\n            ErasedRepr::Native(e) => Some(e),\n            ErasedRepr::Opaque(_) | ErasedRepr::Engine(_) => None,\n        }\n    }\n\n    /// Gets the inner [`ErasedEngineError`] if the error is an engine\n    /// error, or `None` otherwise.\n    #[must_use]\n    pub const fn as_engine(&self) -> Option<&ErasedEngineError> {\n        match &self.inner {\n            ErasedRepr::Engine(e) => Some(e),\n            ErasedRepr::Opaque(_) | ErasedRepr::Native(_) => None,\n        }\n    }\n}\n\n/// Erased version of [`JsNativeError`].\n#[derive(Debug, Clone, Trace, Finalize, Error, PartialEq, Eq)]\npub struct JsErasedNativeError {\n    /// The kind of native error (e.g. `TypeError`, `SyntaxError`, etc.)\n    pub kind: JsErasedNativeErrorKind,\n    message: Cow<'static, str>,\n    #[source]\n    cause: Option<Box<JsErasedError>>,\n}\n\nimpl fmt::Display for JsErasedNativeError {\n    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {\n        write!(f, \"{}\", self.kind)?;\n\n        let message = self.message.trim();\n        if !message.is_empty() {\n            write!(f, \": {message}\")?;\n        }\n\n        Ok(())\n    }\n}\n\n/// Erased version of [`JsNativeErrorKind`]\n#[derive(Debug, Clone, Trace, Finalize, PartialEq, Eq)]\n#[non_exhaustive]\npub enum JsErasedNativeErrorKind {\n    /// A collection of errors wrapped in a single error.\n    ///\n    /// More information:\n    /// - [ECMAScript reference][spec]\n    /// - [MDN documentation][mdn]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-aggregate-error-objects\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/AggregateError\n    Aggregate(Vec<JsErasedError>),\n    /// A generic error. Commonly used as the base for custom exceptions.\n    ///\n    /// More information:\n    /// - [ECMAScript reference][spec]\n    /// - [MDN documentation][mdn]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-error-constructor\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Error\n    Error,\n    /// An error related to the global function [`eval()`][eval].\n    ///\n    /// More information:\n    /// - [ECMAScript reference][spec]\n    /// - [MDN documentation][mdn]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-native-error-types-used-in-this-standard-evalerror\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/EvalError\n    /// [eval]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/eval\n    Eval,\n    /// An error thrown when a value is outside its valid range.\n    ///\n    /// More information:\n    /// - [ECMAScript reference][spec]\n    /// - [MDN documentation][mdn]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-native-error-types-used-in-this-standard-rangeerror\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RangeError\n    Range,\n    /// An error representing an invalid de-reference of a variable.\n    ///\n    /// More information:\n    /// - [ECMAScript reference][spec]\n    /// - [MDN documentation][mdn]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-native-error-types-used-in-this-standard-referenceerror\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/ReferenceError\n    Reference,\n    /// An error representing an invalid syntax in the Javascript language.\n    ///\n    /// More information:\n    /// - [ECMAScript reference][spec]\n    /// - [MDN documentation][mdn]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-native-error-types-used-in-this-standard-syntaxerror\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/SyntaxError\n    Syntax,\n    /// An error thrown when a variable or argument is not of a valid type.\n    ///\n    /// More information:\n    /// - [ECMAScript reference][spec]\n    /// - [MDN documentation][mdn]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-native-error-types-used-in-this-standard-typeerror\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/TypeError\n    Type,\n    /// An error thrown when the [`encodeURI()`][e_uri] and [`decodeURI()`][d_uri] functions receive\n    /// invalid parameters.\n    ///\n    /// More information:\n    /// - [ECMAScript reference][spec]\n    /// - [MDN documentation][mdn]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-native-error-types-used-in-this-standard-urierror\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/URIError\n    /// [e_uri]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/encodeURI\n    /// [d_uri]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/decodeURI\n    Uri,\n\n    /// Error thrown when a runtime limit is exceeded.\n    RuntimeLimit,\n}\n\nimpl fmt::Display for JsErasedNativeErrorKind {\n    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {\n        match &self {\n            Self::Aggregate(errors) => {\n                return write!(f, \"AggregateError(error count: {})\", errors.len());\n            }\n            Self::Error => \"Error\",\n            Self::Eval => \"EvalError\",\n            Self::Range => \"RangeError\",\n            Self::Reference => \"ReferenceError\",\n            Self::Syntax => \"SyntaxError\",\n            Self::Type => \"TypeError\",\n            Self::Uri => \"UriError\",\n            Self::RuntimeLimit => \"RuntimeLimit\",\n        }\n        .fmt(f)\n    }\n}\n"
  },
  {
    "path": "core/engine/src/error/tests.rs",
    "content": "use std::path::Path;\n\nuse crate::{\n    Context, JsError, Source,\n    builtins::promise::PromiseState,\n    module::Module,\n    vm::{shadow_stack::ShadowEntry, source_info::SourcePath},\n};\nuse indoc::indoc;\n\n/// Helper to extract backtrace entries from a rejected module promise.\nfn get_backtrace_from_rejection(\n    context: &mut Context,\n    js_code: &[u8],\n    path: &str,\n) -> Vec<ShadowEntry> {\n    let source = Source::from_bytes(js_code).with_path(Path::new(path));\n    let module = Module::parse(source, None, context).unwrap();\n    let promise = module.load_link_evaluate(context);\n    context.run_jobs().unwrap();\n\n    match promise.state() {\n        PromiseState::Rejected(err) => {\n            let js_error = JsError::from_opaque(err);\n            js_error\n                .backtrace\n                .as_ref()\n                .expect(\"error should have a backtrace\")\n                .iter()\n                .cloned()\n                .collect()\n        }\n        PromiseState::Fulfilled(_) => panic!(\"Module should have thrown an error\"),\n        PromiseState::Pending => panic!(\"Module evaluation should not be pending\"),\n    }\n}\n\n/// Assert that a `ShadowEntry::Bytecode` frame matches expected function name, path, line, and column.\n#[track_caller]\nfn assert_bytecode_frame(\n    entry: &ShadowEntry,\n    expected_fn: &str,\n    expected_path: &Path,\n    expected_line: u32,\n    expected_col: u32,\n) {\n    match entry {\n        ShadowEntry::Bytecode { pc, source_info } => {\n            assert_eq!(\n                source_info.function_name().to_std_string_escaped(),\n                expected_fn,\n                \"function name mismatch\"\n            );\n            assert_eq!(\n                source_info.map().path(),\n                &SourcePath::Path(expected_path.into()),\n                \"path mismatch\"\n            );\n            let pos = source_info\n                .map()\n                .find(*pc)\n                .expect(\"should have a source position\");\n            assert_eq!(pos.line_number(), expected_line, \"line number mismatch\");\n            assert_eq!(pos.column_number(), expected_col, \"column number mismatch\");\n        }\n        ShadowEntry::Native { .. } => panic!(\"expected Bytecode frame, got Native\"),\n    }\n}\n\n/// Assert that a `ShadowEntry` is a `Native` frame.\n#[track_caller]\nfn assert_native_frame(entry: &ShadowEntry) {\n    assert!(\n        matches!(entry, ShadowEntry::Native { .. }),\n        \"expected Native frame, got Bytecode\"\n    );\n}\n\n/// Test that errors caught by internal handlers (e.g. async module evaluation)\n/// preserve their backtrace through promise rejection (`JsError` -> `JsValue` -> `JsError`).\n#[test]\nfn backtrace_preserved_through_promise_rejection() {\n    let mut context = Context::default();\n    let entries = get_backtrace_from_rejection(\n        &mut context,\n        indoc! {br#\"\n            let x = undefined;\n            x()\n        \"#},\n        \"test.js\",\n    );\n\n    let path = Path::new(\"test.js\");\n\n    // Backtrace stored bottom-up: [Native (call site), Bytecode (<main>)]\n    assert_eq!(entries.len(), 2, \"expected 2 backtrace entries\");\n    assert_native_frame(&entries[0]);\n    assert_bytecode_frame(&entries[1], \"<main>\", path, 2, 2);\n}\n\n/// Test that nested call frames produce a full backtrace through the\n/// promise rejection round-trip.\n#[test]\nfn nested_backtrace_preserved_through_promise_rejection() {\n    let mut context = Context::default();\n    let entries = get_backtrace_from_rejection(\n        &mut context,\n        indoc! {br#\"\n            function foo() {\n                function baz() {\n                    import.meta.non_existent()\n                }\n                baz()\n            }\n\n            foo()\n        \"#},\n        \"test.js\",\n    );\n\n    let path = Path::new(\"test.js\");\n\n    // Backtrace stored bottom-up: [Native, <main>, foo, baz]\n    assert_eq!(entries.len(), 4, \"expected 4 backtrace entries\");\n    assert_native_frame(&entries[0]);\n    assert_bytecode_frame(&entries[1], \"<main>\", path, 8, 4);\n    assert_bytecode_frame(&entries[2], \"foo\", path, 5, 8);\n    assert_bytecode_frame(&entries[3], \"baz\", path, 3, 33);\n}\n\n/// Test that an explicit `throw new Error(...)` inside a module also preserves\n/// the backtrace through the promise rejection round-trip.\n#[test]\nfn explicit_throw_backtrace_preserved_through_promise_rejection() {\n    let mut context = Context::default();\n    let entries = get_backtrace_from_rejection(\n        &mut context,\n        indoc! {br#\"\n            function foo() {\n                throw new Error(\"test\")\n            }\n            foo()\n        \"#},\n        \"test.js\",\n    );\n\n    let path = Path::new(\"test.js\");\n\n    // Backtrace stored bottom-up: [Native, <main>, foo]\n    assert_eq!(entries.len(), 3, \"expected 3 backtrace entries\");\n    assert_native_frame(&entries[0]);\n    assert_bytecode_frame(&entries[1], \"<main>\", path, 4, 4);\n    assert_bytecode_frame(&entries[2], \"foo\", path, 2, 11);\n}\n\n/// Sanity check: `context.eval()` errors include a backtrace (relates to\n/// <https://github.com/boa-dev/boa/discussions/4475>).\n#[test]\nfn eval_error_has_backtrace() {\n    let mut context = Context::default();\n    let code = indoc! {br#\"\n        const a = 0;\n        iWillCauseAnError\n        const b = a + 1;\n    \"#};\n    let source = Source::from_reader(code.as_slice(), Some(Path::new(\"test.js\")));\n    match context.eval(source) {\n        Ok(_) => panic!(\"Should have thrown a ReferenceError\"),\n        Err(e) => {\n            assert!(e.backtrace.is_some(), \"eval error should have a backtrace\");\n            let entries: Vec<_> = e.backtrace.as_ref().unwrap().iter().collect();\n            assert!(\n                !entries.is_empty(),\n                \"backtrace should have at least one entry\"\n            );\n        }\n    }\n}\n"
  },
  {
    "path": "core/engine/src/host_defined.rs",
    "content": "use std::any::TypeId;\n\nuse boa_macros::{Finalize, Trace};\nuse hashbrown::hash_map::HashMap;\n\nuse crate::object::NativeObject;\n\n/// This represents a `ECMAScript` specification \\[`HostDefined`\\] field.\n///\n/// This allows storing types which are mapped by their [`TypeId`].\n#[derive(Default, Trace, Finalize)]\n#[allow(missing_debug_implementations)]\npub struct HostDefined {\n    // INVARIANT: All key-value pairs `(id, obj)` satisfy:\n    //  `id == TypeId::of::<T>() && obj.is::<T>()`\n    // for some type `T : NativeObject`.\n    types: HashMap<TypeId, Box<dyn NativeObject>>,\n}\n\n// TODO: Track https://github.com/rust-lang/rust/issues/65991 and\n// https://github.com/rust-lang/rust/issues/90850 to remove this\n// when those are stabilized.\nfn downcast_boxed_native_object_unchecked<T: NativeObject>(obj: Box<dyn NativeObject>) -> Box<T> {\n    let raw: *mut dyn NativeObject = Box::into_raw(obj);\n\n    // SAFETY: We know that `obj` is of type `T` (due to the INVARIANT of `HostDefined`).\n    // See `HostDefined::insert`, `HostDefined::insert_default` and `HostDefined::remove`.\n    unsafe { Box::from_raw(raw.cast::<T>()) }\n}\n\nimpl HostDefined {\n    /// Insert a type into the [`HostDefined`].\n    #[track_caller]\n    pub fn insert_default<T: NativeObject + Default>(&mut self) -> Option<Box<T>> {\n        self.types\n            .insert(TypeId::of::<T>(), Box::<T>::default())\n            .map(downcast_boxed_native_object_unchecked)\n    }\n\n    /// Insert a type into the [`HostDefined`].\n    #[track_caller]\n    pub fn insert<T: NativeObject>(&mut self, value: T) -> Option<Box<T>> {\n        self.types\n            .insert(TypeId::of::<T>(), Box::new(value))\n            .map(downcast_boxed_native_object_unchecked)\n    }\n\n    /// Check if the [`HostDefined`] has type T.\n    #[must_use]\n    #[track_caller]\n    pub fn has<T: NativeObject>(&self) -> bool {\n        self.types.contains_key(&TypeId::of::<T>())\n    }\n\n    /// Remove type T from [`HostDefined`], if it exists.\n    ///\n    /// Returns [`Some`] with the object if it exits, [`None`] otherwise.\n    #[track_caller]\n    pub fn remove<T: NativeObject>(&mut self) -> Option<Box<T>> {\n        self.types\n            .remove(&TypeId::of::<T>())\n            .map(downcast_boxed_native_object_unchecked)\n    }\n\n    /// Get type T from [`HostDefined`], if it exists.\n    #[track_caller]\n    pub fn get<T: NativeObject>(&self) -> Option<&T> {\n        self.types\n            .get(&TypeId::of::<T>())\n            .map(Box::as_ref)\n            .and_then(<dyn NativeObject>::downcast_ref::<T>)\n    }\n\n    /// Get type T from [`HostDefined`], if it exists.\n    #[track_caller]\n    pub fn get_mut<T: NativeObject>(&mut self) -> Option<&mut T> {\n        self.types\n            .get_mut(&TypeId::of::<T>())\n            .map(Box::as_mut)\n            .and_then(<dyn NativeObject>::downcast_mut::<T>)\n    }\n\n    /// Get a tuple of types from [`HostDefined`], returning `None` for the types that are not on the map.\n    #[track_caller]\n    pub fn get_many_mut<T, const SIZE: usize>(&mut self) -> T::NativeTupleMutRef<'_>\n    where\n        T: NativeTuple<SIZE>,\n    {\n        let ids = T::as_type_ids();\n        let refs: [&TypeId; SIZE] = std::array::from_fn(|i| &ids[i]);\n\n        T::mut_ref_from_anys(self.types.get_disjoint_mut(refs))\n    }\n\n    /// Clears all the objects.\n    #[track_caller]\n    pub fn clear(&mut self) {\n        self.types.clear();\n    }\n}\n\n/// This trait represents a tuple of [`NativeObject`]s capable of being\n/// used in [`HostDefined`].\n///\n/// This allows accessing multiple types from [`HostDefined`] at once.\npub trait NativeTuple<const SIZE: usize> {\n    type NativeTupleMutRef<'a>;\n\n    fn as_type_ids() -> [TypeId; SIZE];\n\n    fn mut_ref_from_anys(\n        anys: [Option<&'_ mut Box<dyn NativeObject>>; SIZE],\n    ) -> Self::NativeTupleMutRef<'_>;\n}\n\nmacro_rules! impl_native_tuple {\n    ($size:literal $(,$name:ident)* ) => {\n        impl<$($name: NativeObject,)*> NativeTuple<$size> for ($($name,)*) {\n            type NativeTupleMutRef<'a> = ($(Option<&'a mut $name>,)*);\n\n            fn as_type_ids() -> [TypeId; $size] {\n                [$(TypeId::of::<$name>(),)*]\n            }\n\n            #[allow(unused_variables, unused_mut, clippy::unused_unit)]\n            fn mut_ref_from_anys(\n                anys: [Option<&'_ mut Box<dyn NativeObject>>; $size],\n            ) -> Self::NativeTupleMutRef<'_> {\n                let mut anys = anys.into_iter();\n                ($(\n                    anys.next().flatten().and_then(|v| v.downcast_mut::<$name>()),\n                )*)\n            }\n        }\n    }\n}\n\nimpl_native_tuple!(0);\nimpl_native_tuple!(1, A);\nimpl_native_tuple!(2, A, B);\nimpl_native_tuple!(3, A, B, C);\nimpl_native_tuple!(4, A, B, C, D);\nimpl_native_tuple!(5, A, B, C, D, E);\nimpl_native_tuple!(6, A, B, C, D, E, F);\nimpl_native_tuple!(7, A, B, C, D, E, F, G);\nimpl_native_tuple!(8, A, B, C, D, E, F, G, H);\nimpl_native_tuple!(9, A, B, C, D, E, F, G, H, I);\nimpl_native_tuple!(10, A, B, C, D, E, F, G, H, I, J);\nimpl_native_tuple!(11, A, B, C, D, E, F, G, H, I, J, K);\nimpl_native_tuple!(12, A, B, C, D, E, F, G, H, I, J, K, L);\n"
  },
  {
    "path": "core/engine/src/interop/into_js_arguments.rs",
    "content": "use boa_engine::value::TryFromJs;\nuse boa_engine::{Context, JsNativeError, JsObject, JsResult, JsValue, NativeObject};\nuse boa_gc::{GcRef, GcRefMut};\nuse std::ops::Deref;\n\n/// Create a Rust value from a JS argument. This trait is used to\n/// convert arguments from JS to Rust types. It allows support\n/// for optional arguments or rest arguments.\npub trait TryFromJsArgument<'a>: Sized {\n    /// Try to convert a JS argument into a Rust value, returning the\n    /// value and the rest of the arguments to be parsed.\n    ///\n    /// # Errors\n    /// Any parsing errors that may occur during the conversion.\n    fn try_from_js_argument(\n        this: &'a JsValue,\n        rest: &'a [JsValue],\n        context: &mut Context,\n    ) -> JsResult<(Self, &'a [JsValue])>;\n}\n\nimpl<'a, T: TryFromJs> TryFromJsArgument<'a> for T {\n    fn try_from_js_argument(\n        _: &'a JsValue,\n        rest: &'a [JsValue],\n        context: &mut Context,\n    ) -> JsResult<(Self, &'a [JsValue])> {\n        match rest.split_first() {\n            Some((first, rest)) => Ok((first.try_js_into(context)?, rest)),\n            None => T::try_from_js(&JsValue::undefined(), context).map(|v| (v, rest)),\n        }\n    }\n}\n\n/// An argument that would be ignored in a JS function. This is equivalent of typing\n/// `()` in Rust functions argument, but more explicit.\n#[derive(Debug, Clone, Copy)]\npub struct Ignore;\n\nimpl<'a> TryFromJsArgument<'a> for Ignore {\n    fn try_from_js_argument(\n        _this: &'a JsValue,\n        rest: &'a [JsValue],\n        _: &mut Context,\n    ) -> JsResult<(Self, &'a [JsValue])> {\n        Ok((Ignore, &rest[1..]))\n    }\n}\n\n/// An argument that when used in a JS function will empty the list\n/// of JS arguments as `JsValue`s. This can be used for having the\n/// rest of the arguments in a function. It should be the last\n/// argument of your function, before the `Context` argument if any.\n///\n/// For example,\n/// ```\n/// # use boa_engine::{Context, JsValue, IntoJsFunctionCopied};\n/// # use boa_engine::interop::JsRest;\n/// # let mut context = Context::default();\n/// let sums = (|args: JsRest, context: &mut Context| -> i32 {\n///     args.iter()\n///         .map(|i| i.try_js_into::<i32>(context).unwrap())\n///         .sum::<i32>()\n/// })\n/// .into_js_function_copied(&mut context);\n///\n/// let result = sums\n///     .call(\n///         &JsValue::undefined(),\n///         &[JsValue::from(1), JsValue::from(2), JsValue::from(3)],\n///         &mut context,\n///     )\n///     .unwrap();\n/// assert_eq!(result, JsValue::new(6));\n/// ```\n#[derive(Debug, Clone)]\npub struct JsRest<'a>(pub &'a [JsValue]);\n\n#[allow(unused)]\nimpl<'a> JsRest<'a> {\n    /// Consumes the `JsRest` and returns the inner list of `JsValue`.\n    #[must_use]\n    pub fn into_inner(self) -> &'a [JsValue] {\n        self.0\n    }\n\n    /// Transforms the `JsRest` into a `Vec<JsValue>`.\n    #[must_use]\n    pub fn to_vec(self) -> Vec<JsValue> {\n        self.0.to_vec()\n    }\n\n    /// Returns an iterator over the inner list of `JsValue`.\n    pub fn iter(&self) -> impl Iterator<Item = &JsValue> {\n        self.0.iter()\n    }\n\n    /// Returns the length of the inner list of `JsValue`.\n    #[must_use]\n    pub fn len(&self) -> usize {\n        self.0.len()\n    }\n\n    /// Returns `true` if the inner list of `JsValue` is empty.\n    #[must_use]\n    pub fn is_empty(&self) -> bool {\n        self.0.is_empty()\n    }\n}\n\nimpl<'a> From<&'a [JsValue]> for JsRest<'a> {\n    fn from(values: &'a [JsValue]) -> Self {\n        Self(values)\n    }\n}\n\nimpl<'a> IntoIterator for JsRest<'a> {\n    type Item = &'a JsValue;\n    type IntoIter = std::slice::Iter<'a, JsValue>;\n\n    fn into_iter(self) -> Self::IntoIter {\n        self.into_inner().iter()\n    }\n}\n\n/// An argument that when used in a JS function will capture all\n/// the arguments that can be converted to `T`. The first argument\n/// that cannot be converted to `T` will stop the conversion.\n///\n/// For example,\n/// ```\n/// # use boa_engine::{Context, JsValue, IntoJsFunctionCopied};\n/// # use boa_engine::interop::JsAll;\n/// # let mut context = Context::default();\n/// let sums = (|args: JsAll<i32>, context: &mut Context| -> i32 {\n///     args.iter().sum()\n/// })\n/// .into_js_function_copied(&mut context);\n///\n/// let result = sums\n///     .call(\n///         &JsValue::undefined(),\n///         &[\n///             JsValue::from(1),\n///             JsValue::from(2),\n///             JsValue::from(3),\n///             JsValue::from(true),\n///             JsValue::from(4),\n///         ],\n///         &mut context,\n///     )\n///     .unwrap();\n/// assert_eq!(result, JsValue::new(6));\n/// ```\n#[derive(Debug, Clone)]\npub struct JsAll<T: TryFromJs>(pub Vec<T>);\n\nimpl<T: TryFromJs> JsAll<T> {\n    /// Consumes the `JsAll` and returns the inner list of `T`.\n    #[must_use]\n    pub fn into_inner(self) -> Vec<T> {\n        self.0\n    }\n\n    /// Returns an iterator over the inner list of `T`.\n    pub fn iter(&self) -> impl Iterator<Item = &T> {\n        self.0.iter()\n    }\n\n    /// Returns a mutable iterator over the inner list of `T`.\n    pub fn iter_mut(&mut self) -> impl Iterator<Item = &mut T> {\n        self.0.iter_mut()\n    }\n\n    /// Returns the length of the inner list of `T`.\n    #[must_use]\n    pub fn len(&self) -> usize {\n        self.0.len()\n    }\n\n    /// Returns `true` if the inner list of `T` is empty.\n    #[must_use]\n    pub fn is_empty(&self) -> bool {\n        self.0.is_empty()\n    }\n}\n\nimpl<'a, T: TryFromJs> TryFromJsArgument<'a> for JsAll<T> {\n    fn try_from_js_argument(\n        _this: &'a JsValue,\n        mut rest: &'a [JsValue],\n        context: &mut Context,\n    ) -> JsResult<(Self, &'a [JsValue])> {\n        let mut values = Vec::new();\n\n        while !rest.is_empty() {\n            match rest[0].try_js_into(context) {\n                Ok(value) => {\n                    values.push(value);\n                    rest = &rest[1..];\n                }\n                Err(_) => break,\n            }\n        }\n        Ok((JsAll(values), rest))\n    }\n}\n\n/// Captures the `this` value in a JS function. Although this can be\n/// specified multiple times as argument, it will always be filled\n/// with clone of the same value.\n#[derive(Debug, Clone)]\npub struct JsThis<T: TryFromJs>(pub T);\n\nimpl<'a, T: TryFromJs> TryFromJsArgument<'a> for JsThis<T> {\n    fn try_from_js_argument(\n        this: &'a JsValue,\n        rest: &'a [JsValue],\n        context: &mut Context,\n    ) -> JsResult<(Self, &'a [JsValue])> {\n        Ok((JsThis(this.try_js_into(context)?), rest))\n    }\n}\n\nimpl<T: TryFromJs> Deref for JsThis<T> {\n    type Target = T;\n\n    fn deref(&self) -> &Self::Target {\n        &self.0\n    }\n}\n\n/// Captures a class instance from the `this` value in a JS function. The class\n/// will be a non-mutable reference of Rust type `T`, if it is an instance of `T`.\n///\n/// To have more flexibility on the parsing of the `this` value, you can use the\n/// [`JsThis`] capture instead.\n#[derive(Debug, Clone)]\npub struct JsClass<T: NativeObject> {\n    inner: JsObject<T>,\n}\n\nimpl<T: NativeObject> JsClass<T> {\n    /// Returns the inner object directly.\n    #[must_use]\n    pub fn inner(&self) -> JsObject<T> {\n        self.inner.clone()\n    }\n\n    /// Borrow a reference to the class instance of type `T`.\n    ///\n    /// # Panics\n    ///\n    /// Panics if the object is currently borrowed.\n    ///\n    /// This does not panic if the type is wrong, as the type is checked\n    /// during the construction of the `JsClass` instance.\n    #[must_use]\n    pub fn borrow(&self) -> GcRef<'_, T> {\n        GcRef::map(self.inner.borrow(), |obj| obj.data())\n    }\n\n    /// Borrow a mutable reference to the class instance of type `T`.\n    ///\n    /// # Panics\n    ///\n    /// Panics if the object is currently mutably borrowed.\n    #[must_use]\n    pub fn borrow_mut(&self) -> GcRefMut<'_, T> {\n        GcRefMut::map(self.inner.borrow_mut(), |obj| obj.data_mut())\n    }\n}\n\nimpl<T: NativeObject + Clone> JsClass<T> {\n    /// Clones the inner class instance.\n    ///\n    /// # Panics\n    ///\n    /// Panics if the inner object is currently borrowed mutably.\n    #[must_use]\n    pub fn clone_inner(&self) -> T {\n        self.inner.borrow().data().clone()\n    }\n}\n\nimpl<'a, T: NativeObject + 'static> TryFromJsArgument<'a> for JsClass<T> {\n    fn try_from_js_argument(\n        this: &'a JsValue,\n        rest: &'a [JsValue],\n        _context: &mut Context,\n    ) -> JsResult<(Self, &'a [JsValue])> {\n        let inner = this\n            .as_object()\n            .and_then(|o| o.clone().downcast::<T>().ok())\n            .ok_or_else(|| JsNativeError::typ().with_message(\"invalid this for class method\"))?;\n\n        Ok((JsClass { inner }, rest))\n    }\n}\n\n/// Captures a [`ContextData`] data from the [`Context`] as a JS function argument,\n/// based on its type.\n///\n/// The host defined type must implement [`Clone`], otherwise the borrow\n/// checker would not be able to ensure the safety of the context while\n/// making the function call. Because of this, it is recommended to use\n/// types that are cheap to clone.\n///\n/// For example,\n/// ```\n/// # use boa_engine::{Context, Finalize, JsData, JsValue, Trace, IntoJsFunctionCopied};\n/// # use boa_engine::interop::ContextData;\n///\n/// #[derive(Clone, Debug, Finalize, JsData, Trace)]\n/// struct CustomHostDefinedStruct {\n///     #[unsafe_ignore_trace]\n///     pub counter: usize,\n/// }\n/// let mut context = Context::default();\n/// context.insert_data(CustomHostDefinedStruct { counter: 123 });\n/// let f = (|ContextData(host): ContextData<CustomHostDefinedStruct>| host.counter + 1)\n///     .into_js_function_copied(&mut context);\n///\n/// assert_eq!(\n///     f.call(&JsValue::undefined(), &[], &mut context),\n///     Ok(JsValue::new(124))\n/// );\n/// ```\n#[derive(Debug, Clone)]\npub struct ContextData<T: Clone>(pub T);\n\nimpl<'a, T: NativeObject + Clone> TryFromJsArgument<'a> for ContextData<T> {\n    fn try_from_js_argument(\n        _this: &'a JsValue,\n        rest: &'a [JsValue],\n        context: &mut Context,\n    ) -> JsResult<(Self, &'a [JsValue])> {\n        match context.get_data::<T>() {\n            Some(value) => Ok((ContextData(value.clone()), rest)),\n            None => Err(JsNativeError::typ()\n                .with_message(\"Context data not found\")\n                .into()),\n        }\n    }\n}\n"
  },
  {
    "path": "core/engine/src/interop/into_js_function_impls.rs",
    "content": "//! Implementations of the `IntoJsFunction` trait for various function signatures.\n\nuse super::private::IntoJsFunctionSealed;\nuse super::{IntoJsFunctionCopied, UnsafeIntoJsFunction};\nuse crate::interop::{JsRest, TryFromJsArgument};\nuse crate::{Context, JsError, NativeFunction, TryIntoJsResult, js_string};\nuse std::cell::RefCell;\n\n/// A token to represent the context argument in the function signature.\n/// This should not be used directly and has no external meaning.\n#[derive(Debug, Copy, Clone)]\npub struct ContextArgToken;\n\nmacro_rules! impl_into_js_function {\n    ($($id: ident: $t: ident),*) => {\n        impl<$($t,)* R, T> IntoJsFunctionSealed<($($t,)*), R> for T\n        where\n            $($t: for<'a> TryFromJsArgument<'a> + 'static,)*\n            R: TryIntoJsResult,\n            T: FnMut($($t,)*) -> R + 'static\n        {}\n\n        impl<$($t,)* R, T> IntoJsFunctionSealed<($($t,)* ContextArgToken,), R> for T\n        where\n            $($t: for<'a> TryFromJsArgument<'a> + 'static,)*\n            R: TryIntoJsResult,\n            T: FnMut($($t,)* &mut Context) -> R + 'static\n        {}\n\n        impl<$($t,)* R, T> IntoJsFunctionSealed<($($t,)* JsRest<'_>,), R> for T\n        where\n            $($t: for<'a> TryFromJsArgument<'a> + 'static,)*\n            R: TryIntoJsResult,\n            T: FnMut($($t,)* JsRest<'_>) -> R + 'static\n        {}\n\n        impl<$($t,)* R, T> IntoJsFunctionSealed<($($t,)* JsRest<'_>, ContextArgToken), R> for T\n        where\n            $($t: for<'a> TryFromJsArgument<'a> + 'static,)*\n            R: TryIntoJsResult,\n            T: FnMut($($t,)* JsRest<'_>, &mut Context) -> R + 'static\n        {}\n\n        impl<$($t,)* R, T> UnsafeIntoJsFunction<($($t,)*), R> for T\n        where\n            $($t: for<'a> TryFromJsArgument<'a> + 'static,)*\n            R: TryIntoJsResult,\n            T: FnMut($($t,)*) -> R + 'static,\n        {\n            #[allow(unused_variables)]\n            unsafe fn into_js_function_unsafe(self, _context: &mut Context) -> NativeFunction {\n                let s = RefCell::new(self);\n                unsafe {\n                    NativeFunction::from_closure(move |this, args, ctx| {\n                        let rest = args;\n                        $(\n                            let ($id, rest) = $t::try_from_js_argument(this, rest, ctx)?;\n                        )*\n                        match s.try_borrow_mut() {\n                            Ok(mut r) => r( $($id,)* ).try_into_js_result(ctx),\n                            Err(_) => {\n                                Err(JsError::from_opaque(js_string!(\"recursive calls to this function not supported\").into()))\n                            }\n                        }\n                    })\n                }\n            }\n        }\n\n        impl<$($t,)* R, T> UnsafeIntoJsFunction<($($t,)* JsRest<'_>,), R> for T\n        where\n            $($t: for<'a> TryFromJsArgument<'a> + 'static,)*\n            R: TryIntoJsResult,\n            T: FnMut($($t,)* JsRest<'_>) -> R + 'static,\n        {\n            #[allow(unused_variables)]\n            unsafe fn into_js_function_unsafe(self, _context: &mut Context) -> NativeFunction {\n                let s = RefCell::new(self);\n                unsafe {\n                    NativeFunction::from_closure(move |this, args, ctx| {\n                        let rest = args;\n                        $(\n                            let ($id, rest) = $t::try_from_js_argument(this, rest, ctx)?;\n                        )*\n                        match s.try_borrow_mut() {\n                            Ok(mut r) => r( $($id,)* rest.into() ).try_into_js_result(ctx),\n                            Err(_) => {\n                                Err(JsError::from_opaque(js_string!(\"recursive calls to this function not supported\").into()))\n                            }\n                        }\n                    })\n                }\n            }\n        }\n\n        impl<$($t,)* R, T> UnsafeIntoJsFunction<($($t,)* ContextArgToken,), R> for T\n        where\n            $($t: for<'a> TryFromJsArgument<'a> + 'static,)*\n            R: TryIntoJsResult,\n            T: FnMut($($t,)* &mut Context) -> R + 'static,\n        {\n            #[allow(unused_variables)]\n            unsafe fn into_js_function_unsafe(self, _context: &mut Context) -> NativeFunction {\n                let s = RefCell::new(self);\n                unsafe {\n                    NativeFunction::from_closure(move |this, args, ctx| {\n                        let rest = args;\n                        $(\n                            let ($id, rest) = $t::try_from_js_argument(this, rest, ctx)?;\n                        )*\n                        let r = s.borrow_mut()( $($id,)* ctx);\n                        r.try_into_js_result(ctx)\n                    })\n                }\n            }\n        }\n\n        impl<$($t,)* R, T> UnsafeIntoJsFunction<($($t,)* JsRest<'_>, ContextArgToken), R> for T\n        where\n            $($t: for<'a> TryFromJsArgument<'a> + 'static,)*\n            R: TryIntoJsResult,\n            T: FnMut($($t,)* JsRest<'_>, &mut Context) -> R + 'static,\n        {\n            #[allow(unused_variables)]\n            unsafe fn into_js_function_unsafe(self, _context: &mut Context) -> NativeFunction {\n                let s = RefCell::new(self);\n                unsafe {\n                    NativeFunction::from_closure(move |this, args, ctx| {\n                        let rest = args;\n                        $(\n                            let ($id, rest) = $t::try_from_js_argument(this, rest, ctx)?;\n                        )*\n                        let r = s.borrow_mut()( $($id,)* rest.into(), ctx);\n                        r.try_into_js_result(ctx)\n                    })\n                }\n            }\n        }\n\n        // Safe versions for `Fn(..) -> ...`.\n        impl<$($t,)* R, T> IntoJsFunctionCopied<($($t,)*), R> for T\n        where\n            $($t: for<'a> TryFromJsArgument<'a> + 'static,)*\n            R: TryIntoJsResult,\n            T: Fn($($t,)*) -> R + 'static + Copy,\n        {\n            #[allow(unused_variables)]\n            fn into_js_function_copied(self, _context: &mut Context) -> NativeFunction {\n                let s = self;\n                NativeFunction::from_copy_closure(move |this, args, ctx| {\n                    let rest = args;\n                    $(\n                        let ($id, rest) = $t::try_from_js_argument(this, rest, ctx)?;\n                    )*\n                    let r = s( $($id,)* );\n                    r.try_into_js_result(ctx)\n                })\n            }\n        }\n\n        impl<$($t,)* R, T> IntoJsFunctionCopied<($($t,)* JsRest<'_>,), R> for T\n        where\n            $($t: for<'a> TryFromJsArgument<'a> + 'static,)*\n            R: TryIntoJsResult,\n            T: Fn($($t,)* JsRest<'_>) -> R + 'static + Copy,\n        {\n            #[allow(unused_variables)]\n            fn into_js_function_copied(self, _context: &mut Context) -> NativeFunction {\n                let s = self;\n                NativeFunction::from_copy_closure(move |this, args, ctx| {\n                    let rest = args;\n                    $(\n                        let ($id, rest) = $t::try_from_js_argument(this, rest, ctx)?;\n                    )*\n                    let r = s( $($id,)* rest.into() );\n                    r.try_into_js_result(ctx)\n                })\n            }\n        }\n\n        impl<$($t,)* R, T> IntoJsFunctionCopied<($($t,)* ContextArgToken,), R> for T\n        where\n            $($t: for<'a> TryFromJsArgument<'a> + 'static,)*\n            R: TryIntoJsResult,\n            T: Fn($($t,)* &mut Context) -> R + 'static + Copy,\n        {\n            #[allow(unused_variables)]\n            fn into_js_function_copied(self, _context: &mut Context) -> NativeFunction {\n                let s = self;\n                NativeFunction::from_copy_closure(move |this, args, ctx| {\n                    let rest = args;\n                    $(\n                        let ($id, rest) = $t::try_from_js_argument(this, rest, ctx)?;\n                    )*\n                    let r = s( $($id,)* ctx);\n                    r.try_into_js_result(ctx)\n                })\n            }\n        }\n\n        impl<$($t,)* R, T> IntoJsFunctionCopied<($($t,)* JsRest<'_>, ContextArgToken), R> for T\n        where\n            $($t: for<'a> TryFromJsArgument<'a> + 'static,)*\n            R: TryIntoJsResult,\n            T: Fn($($t,)* JsRest<'_>, &mut Context) -> R + 'static + Copy,\n        {\n            #[allow(unused_variables)]\n            fn into_js_function_copied(self, _context: &mut Context) -> NativeFunction {\n                let s = self;\n                NativeFunction::from_copy_closure(move |this, args, ctx| {\n                    let rest = args;\n                    $(\n                        let ($id, rest) = $t::try_from_js_argument(this, rest, ctx)?;\n                    )*\n                    let r = s( $($id,)* rest.into(), ctx);\n                    r.try_into_js_result(ctx)\n                })\n            }\n        }\n    };\n}\n\n// Currently implemented up to 12 arguments. The empty argument list\n// is implemented separately above.\n// Consider that JsRest and JsThis are part of this list, but Context\n// is not, as it is a special specialization of the template.\nimpl_into_js_function!();\nimpl_into_js_function!(a: A);\nimpl_into_js_function!(a: A, b: B);\nimpl_into_js_function!(a: A, b: B, c: C);\nimpl_into_js_function!(a: A, b: B, c: C, d: D);\nimpl_into_js_function!(a: A, b: B, c: C, d: D, e: E);\nimpl_into_js_function!(a: A, b: B, c: C, d: D, e: E, f: F);\nimpl_into_js_function!(a: A, b: B, c: C, d: D, e: E, f: F, g: G);\nimpl_into_js_function!(a: A, b: B, c: C, d: D, e: E, f: F, g: G, h: H);\nimpl_into_js_function!(a: A, b: B, c: C, d: D, e: E, f: F, g: G, h: H, i: I);\nimpl_into_js_function!(a: A, b: B, c: C, d: D, e: E, f: F, g: G, h: H, i: I, j: J);\nimpl_into_js_function!(a: A, b: B, c: C, d: D, e: E, f: F, g: G, h: H, i: I, j: J, k: K);\nimpl_into_js_function!(a: A, b: B, c: C, d: D, e: E, f: F, g: G, h: H, i: I, j: J, k: K, l: L);\n"
  },
  {
    "path": "core/engine/src/interop/mod.rs",
    "content": "//! Interop module containing traits and types to ease integration between Boa\n//! and Rust.\n\n/// Internal module only.\npub(crate) mod private {\n    /// A sealed trait to prevent users from implementing the `IntoJsModuleFunction`\n    /// and `IntoJsFunctionUnsafe` traits to their own types.\n    pub trait IntoJsFunctionSealed<A, R> {}\n}\n\n/// A trait to convert a type into a JS function.\n/// This trait does not require the implementing type to be `Copy`, which\n/// can lead to undefined behaviour if it contains Garbage Collected objects.\n///\n/// This trait is implemented for functions with various signatures.\n///\n/// For example:\n/// ```\n/// # use boa_engine::{UnsafeIntoJsFunction, Context, JsValue, NativeFunction};\n/// # let mut context = Context::default();\n/// let f = |a: i32, b: i32| a + b;\n/// let f = unsafe { f.into_js_function_unsafe(&mut context) };\n/// let result = f\n///     .call(\n///         &JsValue::undefined(),\n///         &[JsValue::from(1), JsValue::from(2)],\n///         &mut context,\n///     )\n///     .unwrap();\n/// assert_eq!(result, JsValue::new(3));\n/// ```\n///\n/// Since the `IntoJsFunctionUnsafe` trait is implemented for `FnMut`, you can\n/// also use closures directly:\n/// ```\n/// # use boa_engine::{UnsafeIntoJsFunction, Context, JsValue, NativeFunction};\n/// # use std::cell::RefCell;\n/// # use std::rc::Rc;\n/// # let mut context = Context::default();\n/// let mut x = Rc::new(RefCell::new(0));\n/// // Because NativeFunction takes ownership of the closure,\n/// // the compiler cannot be certain it won't outlive `x`, so\n/// // we need to create a `Rc<RefCell>` and share it.\n/// let f = unsafe {\n///     let x = x.clone();\n///     move |a: i32| *x.borrow_mut() += a\n/// };\n/// let f = unsafe { f.into_js_function_unsafe(&mut context) };\n/// f.call(&JsValue::undefined(), &[JsValue::from(1)], &mut context)\n///     .unwrap();\n/// f.call(&JsValue::undefined(), &[JsValue::from(4)], &mut context)\n///     .unwrap();\n/// assert_eq!(*x.borrow(), 5);\n/// ```\npub trait UnsafeIntoJsFunction<Args, Ret>: private::IntoJsFunctionSealed<Args, Ret> {\n    /// Converts the type into a JS function.\n    ///\n    /// # Safety\n    /// This function is unsafe to ensure the callee knows the risks of using this trait.\n    /// The implementing type must not contain any garbage collected objects.\n    unsafe fn into_js_function_unsafe(self, context: &mut Context) -> NativeFunction;\n}\n\n/// The safe equivalent of the [`UnsafeIntoJsFunction`] trait.\n/// This can only be used on closures that have the `Copy` trait.\n///\n/// Since this function is implemented for `Fn(...)` closures, we can use\n/// it directly when defining a function:\n/// ```\n/// # use boa_engine::{IntoJsFunctionCopied, Context, JsValue, NativeFunction};\n/// # let mut context = Context::default();\n/// let f = |a: i32, b: i32| a + b;\n/// let f = f.into_js_function_copied(&mut context);\n/// let result = f\n///     .call(\n///         &JsValue::undefined(),\n///         &[JsValue::from(1), JsValue::from(2)],\n///         &mut context,\n///     )\n///     .unwrap();\n/// assert_eq!(result, JsValue::new(3));\n/// ```\npub trait IntoJsFunctionCopied<Args, Ret>: private::IntoJsFunctionSealed<Args, Ret> + Copy {\n    /// Converts the type into a JS function.\n    fn into_js_function_copied(self, context: &mut Context) -> NativeFunction;\n}\n\nmod into_js_arguments;\nuse crate::{Context, NativeFunction};\npub use into_js_arguments::*;\n\n// Implement `IntoJsFunction` for functions with a various list of\n// arguments.\nmod into_js_function_impls;\n"
  },
  {
    "path": "core/engine/src/job.rs",
    "content": "//! Boa's API to create and customize `ECMAScript` jobs and job queues.\n//!\n//! [`Job`] is an ECMAScript [Job], or a closure that runs an `ECMAScript` computation when\n//! there's no other computation running. The module defines several type of jobs:\n//! - [`PromiseJob`] for Promise related jobs.\n//! - [`TimeoutJob`] for jobs that run after a certain amount of time.\n//! - [`NativeAsyncJob`] for jobs that support [`Future`].\n//! - [`NativeJob`] for generic jobs that aren't related to Promises.\n//!\n//! [`JobCallback`] is an ECMAScript [`JobCallback`] record, containing an `ECMAScript` function\n//! that is executed when a promise is either fulfilled or rejected.\n//!\n//! [`JobExecutor`] is a trait encompassing the required functionality for a job executor; this allows\n//! implementing custom event loops, custom handling of Jobs or other fun things.\n//! This trait is also accompanied by two implementors of the trait:\n//! - [`IdleJobExecutor`], which is an executor that does nothing, and the default executor if no executor is\n//!   provided. Useful for hosts that want to disable promises.\n//! - [`SimpleJobExecutor`], which is a simple FIFO queue that runs all jobs to completion, bailing\n//!   on the first error encountered. This simple executor will block on any async job queued.\n//!\n//! ## [`Trace`]?\n//!\n//! Most of the types defined in this module don't implement `Trace`. This is because most jobs can only\n//! be run once, and putting a `JobExecutor` on a garbage collected object is not allowed.\n//!\n//! In addition to that, not implementing `Trace` makes it so that the garbage collector can consider\n//! any captured variables inside jobs as roots, since you cannot store jobs within a [`Gc`].\n//!\n//! [Job]: https://tc39.es/ecma262/#sec-jobs\n//! [JobCallback]: https://tc39.es/ecma262/#sec-jobcallback-records\n//! [`Gc`]: boa_gc::Gc\n\nuse crate::context::time::{JsDuration, JsInstant};\nuse crate::sys::time;\nuse crate::{\n    Context, JsResult, JsValue,\n    object::{JsFunction, NativeObject},\n    realm::Realm,\n};\nuse boa_gc::{Finalize, Trace};\nuse futures_concurrency::future::FutureGroup;\nuse futures_lite::{StreamExt, future};\nuse portable_atomic::AtomicBool;\nuse std::any::Any;\nuse std::cell::Cell;\nuse std::collections::BTreeMap;\nuse std::mem;\nuse std::rc::Rc;\nuse std::sync::Arc;\nuse std::sync::atomic::Ordering;\nuse std::{cell::RefCell, collections::VecDeque, fmt::Debug, future::Future, pin::Pin};\n\n/// An ECMAScript [Job Abstract Closure].\n///\n/// This is basically a synchronous task that needs to be run to progress [`Promise`] objects,\n/// or unblock threads waiting on [`Atomics.waitAsync`].\n///\n/// [Job]: https://tc39.es/ecma262/#sec-jobs\n/// [`Promise`]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise\n/// [`Atomics.waitAsync`]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Atomics/waitAsync\npub struct NativeJob {\n    #[allow(clippy::type_complexity)]\n    f: Box<dyn FnOnce(&mut Context) -> JsResult<JsValue>>,\n    realm: Option<Realm>,\n}\n\nimpl Debug for NativeJob {\n    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {\n        f.debug_struct(\"NativeJob\").finish_non_exhaustive()\n    }\n}\n\nimpl NativeJob {\n    /// Creates a new `NativeJob` from a closure.\n    pub fn new<F>(f: F) -> Self\n    where\n        F: FnOnce(&mut Context) -> JsResult<JsValue> + 'static,\n    {\n        Self {\n            f: Box::new(f),\n            realm: None,\n        }\n    }\n\n    /// Creates a new `NativeJob` from a closure and an execution realm.\n    pub fn with_realm<F>(f: F, realm: Realm) -> Self\n    where\n        F: FnOnce(&mut Context) -> JsResult<JsValue> + 'static,\n    {\n        Self {\n            f: Box::new(f),\n            realm: Some(realm),\n        }\n    }\n\n    /// Gets a reference to the execution realm of the job.\n    #[must_use]\n    pub const fn realm(&self) -> Option<&Realm> {\n        self.realm.as_ref()\n    }\n\n    /// Calls the native job with the specified [`Context`].\n    ///\n    /// # Note\n    ///\n    /// If the native job has an execution realm defined, this sets the running execution\n    /// context to the realm's before calling the inner closure, and resets it after execution.\n    pub fn call(self, context: &mut Context) -> JsResult<JsValue> {\n        // If realm is not null, each time job is invoked the implementation must perform\n        // implementation-defined steps such that execution is prepared to evaluate ECMAScript\n        // code at the time of job's invocation.\n        if let Some(realm) = self.realm {\n            let old_realm = context.enter_realm(realm);\n\n            // Let scriptOrModule be GetActiveScriptOrModule() at the time HostEnqueuePromiseJob is\n            // invoked. If realm is not null, each time job is invoked the implementation must\n            // perform implementation-defined steps such that scriptOrModule is the active script or\n            // module at the time of job's invocation.\n            let result = (self.f)(context);\n\n            context.enter_realm(old_realm);\n\n            result\n        } else {\n            (self.f)(context)\n        }\n    }\n}\n\n/// Flag that can only be set once.\n#[derive(Debug, Clone)]\npub(crate) struct OnceFlag(Rc<Cell<bool>>);\n\nimpl OnceFlag {\n    /// Creates a new `OnceFlag`.\n    pub(crate) fn new() -> Self {\n        Self(Rc::new(Cell::new(false)))\n    }\n\n    /// Sets this `OnceFlag` to `true`.\n    pub(crate) fn set(&self) {\n        self.0.set(true);\n    }\n\n    /// Returns `true` if this `OnceFlag` has been set, or `false` otherwise.\n    pub(crate) fn is_set(&self) -> bool {\n        self.0.get()\n    }\n}\n\n/// An ECMAScript [Job] that runs after a certain amount of time.\n///\n/// This represents the [HostEnqueueTimeoutJob] operation from the specification.\n///\n/// [HostEnqueueTimeoutJob]: https://tc39.es/ecma262/#sec-hostenqueuetimeoutjob\n#[derive(Debug)]\npub struct TimeoutJob {\n    /// The distance in milliseconds in the future when the job should run.\n    /// This will be added to the current time when the job is enqueued.\n    timeout: JsDuration,\n    /// The job to run after the time has passed.\n    job: NativeJob,\n    /// Signals if the timeout job was cancelled.\n    cancelled: OnceFlag,\n    /// Signals that this job is recurring. A recurring job shouldn't be\n    /// awaited for when considering whether a run of the event loop is\n    /// done.\n    recurring: bool,\n}\n\nimpl TimeoutJob {\n    /// Create a new `TimeoutJob` with a timeout and a job.\n    #[must_use]\n    pub fn new(job: NativeJob, timeout_in_millis: u64) -> Self {\n        Self {\n            timeout: JsDuration::from_millis(timeout_in_millis),\n            job,\n            cancelled: OnceFlag::new(),\n            recurring: false,\n        }\n    }\n\n    /// Create a new `TimeoutJob` that is marked as recurring.\n    #[must_use]\n    pub fn recurring(job: NativeJob, timeout_in_millis: u64) -> Self {\n        Self {\n            timeout: JsDuration::from_millis(timeout_in_millis),\n            job,\n            cancelled: OnceFlag::new(),\n            recurring: true,\n        }\n    }\n\n    /// Creates a new `TimeoutJob` from a closure and a timeout as [`std::time::Duration`].\n    #[must_use]\n    pub fn from_duration<F>(f: F, timeout: impl Into<JsDuration>) -> Self\n    where\n        F: FnOnce(&mut Context) -> JsResult<JsValue> + 'static,\n    {\n        Self::new(NativeJob::new(f), timeout.into().as_millis())\n    }\n\n    /// Creates a new `TimeoutJob` from a closure, a timeout, and an execution realm.\n    #[must_use]\n    pub fn with_realm<F>(f: F, realm: Realm, timeout: time::Duration) -> Self\n    where\n        F: FnOnce(&mut Context) -> JsResult<JsValue> + 'static,\n    {\n        Self::new(NativeJob::with_realm(f, realm), timeout.as_millis() as u64)\n    }\n\n    /// Calls the native job with the specified [`Context`].\n    ///\n    /// # Note\n    ///\n    /// If the native job has an execution realm defined, this sets the running execution\n    /// context to the realm's before calling the inner closure, and resets it after execution.\n    pub fn call(self, context: &mut Context) -> JsResult<JsValue> {\n        self.job.call(context)\n    }\n\n    /// Returns the timeout value in milliseconds since epoch.\n    #[inline]\n    #[must_use]\n    pub fn timeout(&self) -> JsDuration {\n        self.timeout\n    }\n\n    /// Returns `true` if the timeout was cancelled, and its execution can be skipped.\n    #[inline]\n    #[must_use]\n    pub fn is_cancelled(&self) -> bool {\n        self.cancelled.is_set()\n    }\n\n    /// Returns the `OnceFlag` to cancel this timeout job.\n    pub(crate) fn cancelled_flag(&self) -> OnceFlag {\n        self.cancelled.clone()\n    }\n\n    /// Returns `true` if the job is recurring (meaning it happens regularly).\n    #[must_use]\n    pub fn is_recurring(&self) -> bool {\n        self.recurring\n    }\n}\n\n/// An ECMAScript Generic [Job].\n///\n/// This represents the [HostEnqueueGenericJob] operation from the specification, which\n/// enqueues a job that is just like a [`PromiseJob`], but unconstrained in relation\n/// to priority and ordering.\n///\n/// [HostEnqueueGenericJob]: https://tc39.es/ecma262/#sec-hostenqueuegenericjob\npub struct GenericJob(NativeJob);\n\nimpl Debug for GenericJob {\n    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {\n        f.debug_struct(\"GenericJob\").finish_non_exhaustive()\n    }\n}\n\nimpl GenericJob {\n    /// Creates a new `GenericJob` from a closure and an execution realm.\n    pub fn new<F>(f: F, realm: Realm) -> Self\n    where\n        F: FnOnce(&mut Context) -> JsResult<JsValue> + 'static,\n    {\n        Self(NativeJob::with_realm(f, realm))\n    }\n\n    /// Gets a reference to the execution realm of the job.\n    #[must_use]\n    pub const fn realm(&self) -> &Realm {\n        self.0\n            .realm\n            .as_ref()\n            .expect(\"all generic jobs must have an execution realm\")\n    }\n\n    /// Calls the `GenericJob` with the specified [`Context`], setting the execution\n    /// context to the job's realm before calling the inner closure, and resets it after execution.\n    pub fn call(self, context: &mut Context) -> JsResult<JsValue> {\n        self.0.call(context)\n    }\n}\n\n/// The [`Future`] job returned by a [`NativeAsyncJob`] operation.\npub type BoxedFuture<'a> = Pin<Box<dyn Future<Output = JsResult<JsValue>> + 'a>>;\n\n/// An ECMAScript [Job] that can be run asynchronously.\n///\n/// This is an additional type of job that is not defined by the specification, enabling running `Future` tasks\n/// created by ECMAScript code in an easier way.\n#[allow(clippy::type_complexity)]\npub struct NativeAsyncJob {\n    f: Box<dyn for<'a> FnOnce(&'a RefCell<&mut Context>) -> BoxedFuture<'a>>,\n    realm: Option<Realm>,\n}\n\nimpl Debug for NativeAsyncJob {\n    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {\n        f.debug_struct(\"NativeAsyncJob\")\n            .field(\"f\", &\"Closure\")\n            .finish()\n    }\n}\n\nimpl NativeAsyncJob {\n    /// Creates a new `NativeAsyncJob` from an async closure.\n    pub fn new<F>(f: F) -> Self\n    where\n        F: AsyncFnOnce(&RefCell<&mut Context>) -> JsResult<JsValue> + 'static,\n    {\n        Self {\n            f: Box::new(move |ctx| Box::pin(async move { f(ctx).await })),\n            realm: None,\n        }\n    }\n\n    /// Creates a new `NativeAsyncJob` from an async closure and an execution realm.\n    pub fn with_realm<F>(f: F, realm: Realm) -> Self\n    where\n        F: AsyncFnOnce(&RefCell<&mut Context>) -> JsResult<JsValue> + 'static,\n    {\n        Self {\n            f: Box::new(move |ctx| Box::pin(async move { f(ctx).await })),\n            realm: Some(realm),\n        }\n    }\n\n    /// Gets a reference to the execution realm of the job.\n    #[must_use]\n    pub const fn realm(&self) -> Option<&Realm> {\n        self.realm.as_ref()\n    }\n\n    /// Calls the native async job with the specified [`Context`].\n    ///\n    /// # Note\n    ///\n    /// If the native async job has an execution realm defined, this sets the running execution\n    /// context to the realm's before calling the inner closure, and resets it after execution.\n    pub fn call<'a, 'b>(\n        self,\n        context: &'a RefCell<&'b mut Context>,\n        // We can make our users assume `Unpin` because `self.f` is already boxed, so we shouldn't\n        // need pin at all.\n    ) -> impl Future<Output = JsResult<JsValue>> + Unpin + use<'a, 'b> {\n        // If realm is not null, each time job is invoked the implementation must perform\n        // implementation-defined steps such that execution is prepared to evaluate ECMAScript\n        // code at the time of job's invocation.\n        let realm = self.realm;\n\n        let mut future = if let Some(realm) = &realm {\n            let old_realm = context.borrow_mut().enter_realm(realm.clone());\n\n            // Let scriptOrModule be GetActiveScriptOrModule() at the time HostEnqueuePromiseJob is\n            // invoked. If realm is not null, each time job is invoked the implementation must\n            // perform implementation-defined steps such that scriptOrModule is the active script or\n            // module at the time of job's invocation.\n            let result = (self.f)(context);\n\n            context.borrow_mut().enter_realm(old_realm);\n            result\n        } else {\n            (self.f)(context)\n        };\n\n        std::future::poll_fn(move |cx| {\n            // We need to do the same dance again since the inner code could assume we're still\n            // on the same realm.\n            if let Some(realm) = &realm {\n                let old_realm = context.borrow_mut().enter_realm(realm.clone());\n\n                let poll_result = future.as_mut().poll(cx);\n\n                context.borrow_mut().enter_realm(old_realm);\n                poll_result\n            } else {\n                future.as_mut().poll(cx)\n            }\n        })\n    }\n}\n\n/// An ECMAScript [Job Abstract Closure] executing code related to [`Promise`] objects.\n///\n/// This represents the [`HostEnqueuePromiseJob`] operation from the specification.\n///\n/// ### [Requirements]\n///\n/// - If realm is not null, each time job is invoked the implementation must perform implementation-defined\n///   steps such that execution is prepared to evaluate ECMAScript code at the time of job's invocation.\n/// - Let `scriptOrModule` be [`GetActiveScriptOrModule()`] at the time `HostEnqueuePromiseJob` is invoked.\n///   If realm is not null, each time job is invoked the implementation must perform implementation-defined steps\n///   such that `scriptOrModule` is the active script or module at the time of job's invocation.\n/// - Jobs must run in the same order as the `HostEnqueuePromiseJob` invocations that scheduled them.\n///\n/// Of all the requirements, Boa guarantees the first two by its internal implementation of `NativeJob`, meaning\n/// implementations of [`JobExecutor`] must only guarantee that jobs are run in the same order as they're enqueued.\n///\n/// [`Promise`]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise\n/// [`HostEnqueuePromiseJob`]: https://tc39.es/ecma262/#sec-hostenqueuepromisejob\n/// [Job Abstract Closure]: https://tc39.es/ecma262/#sec-jobs\n/// [Requirements]: https://tc39.es/ecma262/multipage/executable-code-and-execution-contexts.html#sec-hostenqueuepromisejob\n/// [`GetActiveScriptOrModule()`]: https://tc39.es/ecma262/multipage/executable-code-and-execution-contexts.html#sec-getactivescriptormodule\npub struct PromiseJob(NativeJob);\n\nimpl Debug for PromiseJob {\n    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {\n        f.debug_struct(\"PromiseJob\").finish_non_exhaustive()\n    }\n}\n\nimpl PromiseJob {\n    /// Creates a new `PromiseJob` from a closure.\n    pub fn new<F>(f: F) -> Self\n    where\n        F: FnOnce(&mut Context) -> JsResult<JsValue> + 'static,\n    {\n        Self(NativeJob::new(f))\n    }\n\n    /// Creates a new `PromiseJob` from a closure and an execution realm.\n    pub fn with_realm<F>(f: F, realm: Realm) -> Self\n    where\n        F: FnOnce(&mut Context) -> JsResult<JsValue> + 'static,\n    {\n        Self(NativeJob::with_realm(f, realm))\n    }\n\n    /// Gets a reference to the execution realm of the `PromiseJob`.\n    #[must_use]\n    pub const fn realm(&self) -> Option<&Realm> {\n        self.0.realm()\n    }\n\n    /// Calls the `PromiseJob` with the specified [`Context`].\n    ///\n    /// # Note\n    ///\n    /// If the job has an execution realm defined, this sets the running execution\n    /// context to the realm's before calling the inner closure, and resets it after execution.\n    pub fn call(self, context: &mut Context) -> JsResult<JsValue> {\n        self.0.call(context)\n    }\n}\n\n/// [`JobCallback`][spec] records.\n///\n/// [spec]: https://tc39.es/ecma262/#sec-jobcallback-records\n#[derive(Trace, Finalize)]\npub struct JobCallback {\n    callback: JsFunction,\n    host_defined: Box<dyn NativeObject>,\n}\n\nimpl Debug for JobCallback {\n    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {\n        f.debug_struct(\"JobCallback\")\n            .field(\"callback\", &self.callback)\n            .field(\"host_defined\", &\"dyn NativeObject\")\n            .finish()\n    }\n}\n\nimpl JobCallback {\n    /// Creates a new `JobCallback`.\n    #[inline]\n    pub fn new<T: NativeObject>(callback: JsFunction, host_defined: T) -> Self {\n        Self {\n            callback,\n            host_defined: Box::new(host_defined),\n        }\n    }\n\n    /// Gets the inner callback of the job.\n    #[inline]\n    #[must_use]\n    pub const fn callback(&self) -> &JsFunction {\n        &self.callback\n    }\n\n    /// Gets a reference to the host defined additional field as an [`NativeObject`] trait object.\n    #[inline]\n    #[must_use]\n    pub fn host_defined(&self) -> &dyn NativeObject {\n        &*self.host_defined\n    }\n\n    /// Gets a mutable reference to the host defined additional field as an [`NativeObject`] trait object.\n    #[inline]\n    pub fn host_defined_mut(&mut self) -> &mut dyn NativeObject {\n        &mut *self.host_defined\n    }\n}\n\n/// A job that needs to be handled by a [`JobExecutor`].\n///\n/// # Requirements\n///\n/// The specification defines many types of jobs, but all of them must adhere to a set of requirements:\n///\n/// - At some future point in time, when there is no running execution context and the execution\n///   context stack is empty, the implementation must:\n///     - Perform any host-defined preparation steps.\n///     - Invoke the Job Abstract Closure.\n///     - Perform any host-defined cleanup steps, after which the execution context stack must be empty.\n/// - Only one Job may be actively undergoing evaluation at any point in time.\n/// - Once evaluation of a Job starts, it must run to completion before evaluation of any other Job starts.\n/// - The Abstract Closure must return a normal completion, implementing its own handling of errors.\n///\n/// Boa is a little bit flexible on the last requirement, since it allows jobs to return either\n/// values or errors, but the rest of the requirements must be followed for all conformant implementations.\n///\n/// Additionally, each job type can have additional requirements that must also be followed in addition\n/// to the previous ones.\n#[non_exhaustive]\n#[derive(Debug)]\npub enum Job {\n    /// A `Promise`-related job.\n    ///\n    /// See [`PromiseJob`] for more information.\n    PromiseJob(PromiseJob),\n    /// A [`Future`]-related job.\n    ///\n    /// See [`NativeAsyncJob`] for more information.\n    AsyncJob(NativeAsyncJob),\n    /// A generic job that is to be executed after a number of milliseconds.\n    ///\n    /// See [`TimeoutJob`] for more information.\n    TimeoutJob(TimeoutJob),\n    /// A generic job.\n    ///\n    /// See [`GenericJob`] for more information.\n    GenericJob(GenericJob),\n    /// A job that will eventually cleanup a `FinalizationRegistry`.\n    ///\n    /// This job differs slightly from the [spec]; originally it's defined\n    /// as being enqueued exactly when a `FinalizationRegistry` needs to call\n    /// `FinalizationRegistry::cleanup`, but here it's defined as an async\n    /// job that suspends execution until it receives a signal from the engine\n    /// that the `FinalizationRegistry` needs to be cleaned up.\n    ///\n    /// # Execution\n    ///\n    /// As described on the [spec's section about execution][execution],\n    ///\n    /// > Because calling `HostEnqueueFinalizationRegistryCleanupJob` is optional,\n    /// > registered objects in a `FinalizationRegistry` do not necessarily hold\n    /// > that `FinalizationRegistry` live. Implementations may omit `FinalizationRegistry`\n    /// > callbacks for any reason, e.g., if the `FinalizationRegistry` itself becomes\n    /// > dead, or if the application is shutting down.\n    ///\n    /// For this reason, it is recommended to exclude `FinalizationRegistry` cleanup\n    /// jobs from any condition that exits from [`JobExecutor::run_jobs`].\n    ///\n    /// By the same token, it is recommended to execute `FinalizationRegistry` cleanup\n    /// jobs separately from all other enqueued [`NativeAsyncJob`]s, prioritizing the\n    /// execution of all other jobs if possible.\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-weakref-host-hooks\n    /// [execution]: https://tc39.es/ecma262/#sec-weakref-execution\n    FinalizationRegistryCleanupJob(NativeAsyncJob),\n}\n\nimpl From<NativeAsyncJob> for Job {\n    fn from(native_async_job: NativeAsyncJob) -> Self {\n        Job::AsyncJob(native_async_job)\n    }\n}\n\nimpl From<PromiseJob> for Job {\n    fn from(promise_job: PromiseJob) -> Self {\n        Job::PromiseJob(promise_job)\n    }\n}\n\nimpl From<TimeoutJob> for Job {\n    fn from(job: TimeoutJob) -> Self {\n        Job::TimeoutJob(job)\n    }\n}\n\nimpl From<GenericJob> for Job {\n    fn from(job: GenericJob) -> Self {\n        Job::GenericJob(job)\n    }\n}\n\n/// An executor of `ECMAscript` [Jobs].\n///\n/// This is the main API that allows creating custom event loops.\n///\n/// [Jobs]: https://tc39.es/ecma262/#sec-jobs\npub trait JobExecutor: Any {\n    /// Enqueues a `Job` on the executor.\n    ///\n    /// This method combines all the host-defined job enqueueing operations into a single method.\n    /// See the [spec] for more information on the requirements that each operation must follow.\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-jobs\n    fn enqueue_job(self: Rc<Self>, job: Job, context: &mut Context);\n\n    /// Runs all jobs in the executor.\n    fn run_jobs(self: Rc<Self>, context: &mut Context) -> JsResult<()>;\n\n    /// Asynchronously runs all jobs in the executor.\n    ///\n    /// By default forwards to [`JobExecutor::run_jobs`]. Implementors using async should override this\n    /// with a proper algorithm to run jobs asynchronously.\n    #[expect(async_fn_in_trait, reason = \"all our APIs are single-threaded\")]\n    async fn run_jobs_async(self: Rc<Self>, context: &RefCell<&mut Context>) -> JsResult<()>\n    where\n        Self: Sized,\n    {\n        self.run_jobs(&mut context.borrow_mut())\n    }\n}\n\n/// A job executor that does nothing.\n///\n/// This executor is mostly useful if you want to disable the promise capabilities of the engine. This\n/// can be done by passing it to the [`ContextBuilder`]:\n///\n/// ```\n/// use boa_engine::{\n///     context::ContextBuilder,\n///     job::{IdleJobExecutor, JobExecutor},\n/// };\n/// use std::rc::Rc;\n///\n/// let executor = Rc::new(IdleJobExecutor);\n/// let context = ContextBuilder::new().job_executor(executor).build();\n/// ```\n///\n/// [`ContextBuilder`]: crate::context::ContextBuilder\n#[derive(Debug, Clone, Copy)]\npub struct IdleJobExecutor;\n\nimpl JobExecutor for IdleJobExecutor {\n    fn enqueue_job(self: Rc<Self>, _: Job, _: &mut Context) {}\n\n    fn run_jobs(self: Rc<Self>, _: &mut Context) -> JsResult<()> {\n        Ok(())\n    }\n}\n\n/// A simple FIFO executor that bails on the first error.\n///\n/// This is the default job executor for the [`Context`], but it is mostly pretty limited\n/// for a custom event loop.\n///\n/// To disable running promise jobs on the engine, see [`IdleJobExecutor`].\n#[derive(Default)]\npub struct SimpleJobExecutor {\n    promise_jobs: RefCell<VecDeque<PromiseJob>>,\n    async_jobs: RefCell<VecDeque<NativeAsyncJob>>,\n    finalization_registry_jobs: RefCell<VecDeque<NativeAsyncJob>>,\n    timeout_jobs: RefCell<BTreeMap<JsInstant, Vec<TimeoutJob>>>,\n    generic_jobs: RefCell<VecDeque<GenericJob>>,\n    stop: Arc<AtomicBool>,\n}\n\nimpl SimpleJobExecutor {\n    fn clear(&self) {\n        self.promise_jobs.borrow_mut().clear();\n        self.async_jobs.borrow_mut().clear();\n        self.timeout_jobs.borrow_mut().clear();\n        self.generic_jobs.borrow_mut().clear();\n    }\n}\n\nimpl Debug for SimpleJobExecutor {\n    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {\n        f.debug_struct(\"SimpleJobExecutor\").finish_non_exhaustive()\n    }\n}\n\nimpl SimpleJobExecutor {\n    /// Creates a new `SimpleJobExecutor`.\n    #[must_use]\n    pub fn new() -> Self {\n        Self::default()\n    }\n\n    /// Gets the cancellation token for this executor.\n    ///\n    /// Setting the signal to `true` will exit the inner event loop and\n    /// stop executing any pending jobs.\n    pub fn get_cancellation_token(&self) -> Arc<AtomicBool> {\n        Arc::clone(&self.stop)\n    }\n\n    pub(crate) fn is_empty(&self) -> bool {\n        self.promise_jobs.borrow().is_empty()\n            && self.async_jobs.borrow().is_empty()\n            && self.generic_jobs.borrow().is_empty()\n            && self.timeout_jobs.borrow().is_empty()\n    }\n}\n\nimpl JobExecutor for SimpleJobExecutor {\n    fn enqueue_job(self: Rc<Self>, job: Job, context: &mut Context) {\n        match job {\n            Job::PromiseJob(p) => self.promise_jobs.borrow_mut().push_back(p),\n            Job::AsyncJob(a) => self.async_jobs.borrow_mut().push_back(a),\n            Job::TimeoutJob(t) => {\n                let now = context.clock().now();\n                self.timeout_jobs\n                    .borrow_mut()\n                    .entry(now + t.timeout())\n                    .or_default()\n                    .push(t);\n            }\n            Job::GenericJob(g) => self.generic_jobs.borrow_mut().push_back(g),\n            Job::FinalizationRegistryCleanupJob(fr) => {\n                self.finalization_registry_jobs.borrow_mut().push_back(fr);\n            }\n        }\n    }\n\n    fn run_jobs(self: Rc<Self>, context: &mut Context) -> JsResult<()> {\n        future::block_on(self.run_jobs_async(&RefCell::new(context)))\n    }\n\n    async fn run_jobs_async(self: Rc<Self>, context: &RefCell<&mut Context>) -> JsResult<()>\n    where\n        Self: Sized,\n    {\n        let mut group = FutureGroup::new();\n        let mut fr_group = FutureGroup::new();\n        loop {\n            if self.stop.load(Ordering::Relaxed) {\n                self.stop.store(false, Ordering::Relaxed);\n                self.clear();\n                return Ok(());\n            }\n\n            for job in mem::take(&mut *self.async_jobs.borrow_mut()) {\n                group.insert(job.call(context));\n            }\n\n            for job in mem::take(&mut *self.finalization_registry_jobs.borrow_mut()) {\n                fr_group.insert(job.call(context));\n            }\n\n            // Dispatch all past-due timeout jobs before the termination check.\n            {\n                let now = context.borrow().clock().now();\n                let jobs_to_run = {\n                    let mut timeout_jobs = self.timeout_jobs.borrow_mut();\n                    let mut jobs_to_keep = timeout_jobs.split_off(&now);\n                    jobs_to_keep.retain(|_, jobs| {\n                        jobs.retain(|job| !job.is_cancelled());\n                        !jobs.is_empty()\n                    });\n                    mem::replace(&mut *timeout_jobs, jobs_to_keep)\n                };\n\n                for jobs in jobs_to_run.into_values() {\n                    for job in jobs {\n                        if !job.is_cancelled()\n                            && let Err(err) = job.call(&mut context.borrow_mut())\n                        {\n                            self.clear();\n                            return Err(err);\n                        }\n                    }\n                }\n            }\n\n            if self.is_empty() && group.is_empty() {\n                match future::poll_once(fr_group.next()).await.flatten() {\n                    Some(Err(err)) => {\n                        self.clear();\n                        return Err(err);\n                    }\n                    _ if !self.is_empty() => {}\n                    _ => break,\n                }\n            }\n\n            if let Some(Err(err)) = future::poll_once(group.next()).await.flatten() {\n                self.clear();\n                return Err(err);\n            }\n\n            let jobs = mem::take(&mut *self.promise_jobs.borrow_mut());\n            for job in jobs {\n                if let Err(err) = job.call(&mut context.borrow_mut()) {\n                    self.clear();\n                    return Err(err);\n                }\n            }\n\n            let jobs = mem::take(&mut *self.generic_jobs.borrow_mut());\n            for job in jobs {\n                if let Err(err) = job.call(&mut context.borrow_mut()) {\n                    self.clear();\n                    return Err(err);\n                }\n            }\n            context.borrow_mut().clear_kept_objects();\n            future::yield_now().await;\n        }\n\n        Ok(())\n    }\n}\n"
  },
  {
    "path": "core/engine/src/lib.rs",
    "content": "//! Boa's **`boa_engine`** crate implements ECMAScript's standard library of builtin objects\n//! and an ECMAScript context, bytecompiler, and virtual machine for code execution.\n//!\n//! # Example usage\n//!\n//! You can find multiple examples of the usage of Boa in the [`boa_examples`][examples] crate. In\n//! order to use Boa in your project, you will need to add the `boa_engine` crate to your\n//! `Cargo.toml` file. You will need to use a [`Source`] structure to handle the JavaScript code\n//! to execute, and a [`Context`] structure to execute the code:\n//!\n//! ```\n//! use boa_engine::{Context, Source};\n//!\n//! let js_code = r#\"\n//!     let two = 1 + 1;\n//!     let definitely_not_four = two + \"2\";\n//!\n//!     definitely_not_four\n//! \"#;\n//!\n//! // Instantiate the execution context\n//! let mut context = Context::default();\n//!\n//! // Parse the source code\n//! match context.eval(Source::from_bytes(js_code)) {\n//!     Ok(res) => {\n//!         println!(\n//!             \"{}\",\n//!             res.to_string(&mut context).unwrap().to_std_string_escaped()\n//!         );\n//!     }\n//!     Err(e) => {\n//!         // Pretty print the error\n//!         eprintln!(\"Uncaught {e}\");\n//!         # panic!(\"There was an error in boa_engine's introduction example.\");\n//!     }\n//! };\n//! ```\n//!\n//! # Crate Features\n//!\n//!  - **serde** - Enables serialization and deserialization of the AST (Abstract Syntax Tree).\n//!  - **profiler** - Enables profiling with measureme (this is mostly internal).\n//!  - **intl** - Enables `boa`'s [ECMA-402 Internationalization API][ecma-402] (`Intl` object)\n//!\n//! [ecma-402]: https://tc39.es/ecma402\n//! [examples]: https://github.com/boa-dev/boa/tree/main/examples\n#![doc = include_str!(\"../ABOUT.md\")]\n#![doc(\n    html_logo_url = \"https://raw.githubusercontent.com/boa-dev/boa/main/assets/logo_black.svg\",\n    html_favicon_url = \"https://raw.githubusercontent.com/boa-dev/boa/main/assets/logo_black.svg\"\n)]\n#![cfg_attr(test, allow(clippy::needless_raw_string_hashes))] // Makes strings a bit more copy-pastable\n#![cfg_attr(not(test), forbid(clippy::unwrap_used))]\n#![allow(\n    // Currently throws a false positive regarding dependencies that are only used in benchmarks.\n    unused_crate_dependencies,\n    clippy::module_name_repetitions,\n    clippy::redundant_pub_crate,\n    clippy::too_many_lines,\n    clippy::cognitive_complexity,\n    clippy::missing_errors_doc,\n    clippy::let_unit_value,\n    clippy::option_if_let_else,\n\n    // It may be worth to look if we can fix the issues highlighted by these lints.\n    clippy::cast_possible_truncation,\n    clippy::cast_sign_loss,\n    clippy::cast_precision_loss,\n    clippy::cast_possible_wrap,\n\n    // Add temporarily - Needs addressing\n    clippy::missing_panics_doc,\n)]\n\nextern crate self as boa_engine;\n#[cfg(not(target_has_atomic = \"ptr\"))]\ncompile_error!(\"Boa requires a lock free `AtomicUsize` in order to work properly.\");\n\npub use boa_ast as ast;\npub use boa_gc as gc;\npub use boa_interner as interner;\npub use boa_parser as parser;\n\npub mod bigint;\npub mod builtins;\npub mod bytecompiler;\npub mod class;\npub mod context;\npub mod environments;\npub mod error;\npub mod interop;\npub mod job;\npub mod module;\npub mod native_function;\npub mod object;\n/// ECMAScript Abstract Syntax Tree (AST) optimizer.\npub mod optimizer;\npub mod property;\npub mod realm;\npub mod script;\npub mod string;\npub mod symbol;\npub mod value;\npub mod vm;\n\nmod host_defined;\nmod sys;\n\nmod spanned_source_text;\nuse spanned_source_text::SourceText;\npub use spanned_source_text::SpannedSourceText;\n\n#[cfg(test)]\nmod tests;\n\n/// A convenience module that re-exports the most commonly-used Boa APIs\npub mod prelude {\n    pub use crate::{\n        bigint::JsBigInt,\n        context::Context,\n        error::{EngineError, JsError, JsNativeError, JsNativeErrorKind, RuntimeLimitError},\n        host_defined::HostDefined,\n        interop::{IntoJsFunctionCopied, UnsafeIntoJsFunction},\n        module::{IntoJsModule, Module},\n        native_function::NativeFunction,\n        object::{JsData, JsObject, NativeObject},\n        script::Script,\n        string::{JsStr, JsString},\n        symbol::JsSymbol,\n        value::{JsValue, JsVariant, js_object, js_value},\n    };\n    pub use boa_gc::{Finalize, Trace};\n    pub use boa_macros::{JsData, js_str};\n    pub use boa_parser::Source;\n}\n\n#[doc(inline)]\npub use boa_macros::{boa_class, boa_module, embed_module_inner as __embed_module_inner};\n\nuse crate::error::PanicError;\nuse std::result::Result as StdResult;\n\n// Export things to root level\n#[doc(inline)]\npub use prelude::*;\n\n#[doc(inline)]\npub use boa_parser::Source;\n\n/// The result of a Javascript expression is represented like this so it can succeed (`Ok`) or fail (`Err`)\npub type JsResult<T> = StdResult<T, JsError>;\n\n/// Create a [`JsResult`] from a Rust value. This trait is used to\n/// convert Rust types to JS types, including [`JsResult`] of\n/// Rust values and [`JsValue`]s.\n///\n/// This trait is implemented for any that can be converted into a [`JsValue`].\npub trait TryIntoJsResult {\n    /// Try to convert a Rust value into a `JsResult<JsValue>`.\n    ///\n    /// # Errors\n    /// Any parsing errors that may occur during the conversion, or any\n    /// error that happened during the call to a function.\n    fn try_into_js_result(self, context: &mut Context) -> JsResult<JsValue>;\n}\n\nmod try_into_js_result_impls;\n\n/// A utility trait to make working with function arguments easier.\npub trait JsArgs {\n    /// Utility function to `get` a parameter from a `[JsValue]` or default to\n    /// `JsValue::undefined()` if `get` returns `None`.\n    ///\n    /// Call this if you are thinking of calling something similar to\n    /// `args.get(n).cloned().unwrap_or_default()` or\n    /// `args.get(n).unwrap_or(&undefined)`.\n    ///\n    /// This returns a reference for efficiency, in case you only need to call methods of `JsValue`.\n    fn get_or_undefined(&self, index: usize) -> &JsValue;\n}\n\nimpl JsArgs for [JsValue] {\n    fn get_or_undefined(&self, index: usize) -> &JsValue {\n        const UNDEFINED: &JsValue = &JsValue::undefined();\n        self.get(index).unwrap_or(UNDEFINED)\n    }\n}\n\n/// Utility trait to \"expect\" a `JsResult`, but returning a `PanicError` instead of panicking.\n#[allow(dead_code)]\npub(crate) trait JsExpect<V> {\n    /// \"expects\" a `JsResult`, wrapping the error with a `PanicError`.\n    fn js_expect<S: Into<Box<str>>>(self, msg: S) -> StdResult<V, PanicError>;\n}\n\nimpl<V> JsExpect<V> for JsResult<V> {\n    fn js_expect<S: Into<Box<str>>>(self, msg: S) -> StdResult<V, PanicError> {\n        self.map_err(|err| PanicError::new(msg).with_source(err))\n    }\n}\n\nimpl<V> JsExpect<V> for Option<V> {\n    fn js_expect<S: Into<Box<str>>>(self, msg: S) -> StdResult<V, PanicError> {\n        self.ok_or_else(|| PanicError::new(msg))\n    }\n}\n\n#[cfg(test)]\nuse std::{borrow::Cow, pin::Pin};\n\n#[cfg(test)]\ntype PinBoxFuture<'a> = Pin<Box<dyn Future<Output = ()> + 'a>>;\n\n/// A test action executed in a test function.\n#[cfg(test)]\nstruct TestAction(Inner);\n\n#[cfg(test)]\nenum Inner {\n    RunHarness,\n    Run {\n        source: Cow<'static, str>,\n    },\n    InspectContext {\n        op: fn(&mut Context),\n    },\n    InspectContextAsync {\n        op: Box<dyn for<'a> FnOnce(&'a mut Context) -> PinBoxFuture<'a>>,\n    },\n    Assert {\n        source: Cow<'static, str>,\n    },\n    AssertEq {\n        source: Cow<'static, str>,\n        expected: JsValue,\n    },\n    AssertWithOp {\n        source: Cow<'static, str>,\n        op: fn(JsValue, &mut Context) -> bool,\n    },\n    AssertOpaqueError {\n        source: Cow<'static, str>,\n        expected: JsValue,\n    },\n    AssertNativeError {\n        source: Cow<'static, str>,\n        kind: JsNativeErrorKind,\n        message: &'static str,\n    },\n    AssertContext {\n        op: fn(&mut Context) -> bool,\n    },\n    AssertEngineError {\n        source: Cow<'static, str>,\n        error: EngineError,\n    },\n}\n\n#[cfg(test)]\nimpl TestAction {\n    /// Evaluates some utility functions used in tests.\n    const fn run_harness() -> Self {\n        Self(Inner::RunHarness)\n    }\n\n    /// Runs `source`, panicking if the execution throws.\n    fn run(source: impl Into<Cow<'static, str>>) -> Self {\n        Self(Inner::Run {\n            source: source.into(),\n        })\n    }\n\n    /// Executes `op` with the currently active context.\n    ///\n    /// Useful to make custom assertions that must be done from Rust code.\n    fn inspect_context(op: fn(&mut Context)) -> Self {\n        Self(Inner::InspectContext { op })\n    }\n\n    /// Executes `op` with the currently active context in an async environment.\n    pub(crate) fn inspect_context_async(op: impl AsyncFnOnce(&mut Context) + 'static) -> Self {\n        Self(Inner::InspectContextAsync {\n            op: Box::new(move |ctx| Box::pin(op(ctx))),\n        })\n    }\n\n    /// Asserts that evaluating `source` returns the `true` value.\n    fn assert(source: impl Into<Cow<'static, str>>) -> Self {\n        Self(Inner::Assert {\n            source: source.into(),\n        })\n    }\n\n    /// Asserts that the script returns `expected` when evaluating `source`.\n    fn assert_eq(source: impl Into<Cow<'static, str>>, expected: impl Into<JsValue>) -> Self {\n        Self(Inner::AssertEq {\n            source: source.into(),\n            expected: expected.into(),\n        })\n    }\n\n    /// Asserts that calling `op` with the value obtained from evaluating `source` returns `true`.\n    ///\n    /// Useful to check properties of the obtained value that cannot be checked from JS code.\n    fn assert_with_op(\n        source: impl Into<Cow<'static, str>>,\n        op: fn(JsValue, &mut Context) -> bool,\n    ) -> Self {\n        Self(Inner::AssertWithOp {\n            source: source.into(),\n            op,\n        })\n    }\n\n    /// Asserts that evaluating `source` throws the opaque error `value`.\n    fn assert_opaque_error(\n        source: impl Into<Cow<'static, str>>,\n        value: impl Into<JsValue>,\n    ) -> Self {\n        Self(Inner::AssertOpaqueError {\n            source: source.into(),\n            expected: value.into(),\n        })\n    }\n\n    /// Asserts that evaluating `source` throws a native error of `kind` and `message`.\n    fn assert_native_error(\n        source: impl Into<Cow<'static, str>>,\n        kind: JsNativeErrorKind,\n        message: &'static str,\n    ) -> Self {\n        Self(Inner::AssertNativeError {\n            source: source.into(),\n            kind,\n            message,\n        })\n    }\n\n    /// Asserts that evaluating `source` throws a runtime limit error.\n    fn assert_runtime_limit_error(\n        source: impl Into<Cow<'static, str>>,\n        error: RuntimeLimitError,\n    ) -> Self {\n        Self(Inner::AssertEngineError {\n            source: source.into(),\n            error: error.into(),\n        })\n    }\n\n    /// Asserts that calling `op` with the currently executing context returns `true`.\n    fn assert_context(op: fn(&mut Context) -> bool) -> Self {\n        Self(Inner::AssertContext { op })\n    }\n}\n\n/// Executes a list of test actions on a new, default context.\n#[cfg(test)]\n#[track_caller]\nfn run_test_actions(actions: impl IntoIterator<Item = TestAction>) {\n    let mut context = Context::builder();\n    if cfg!(miri) {\n        // Do not use OS APIs when running with Miri to avoid escaping the\n        // isolated sandbox.\n\n        use std::rc::Rc;\n\n        use crate::{context::time::FixedClock, module::IdleModuleLoader};\n\n        context = context\n            .clock(Rc::new(FixedClock::from_millis(65535)))\n            .module_loader(Rc::new(IdleModuleLoader));\n    }\n    run_test_actions_with(actions, &mut context.build().unwrap());\n}\n\n/// Executes a list of test actions on the provided context.\n#[cfg(test)]\n#[track_caller]\nfn run_test_actions_with(actions: impl IntoIterator<Item = TestAction>, context: &mut Context) {\n    #[track_caller]\n    fn forward_val(context: &mut Context, source: &str) -> JsResult<JsValue> {\n        context.eval(Source::from_bytes(source))\n    }\n\n    #[track_caller]\n    fn fmt_test(source: &str, test: usize) -> String {\n        format!(\n            \"\\n\\nTest case {test}: \\n```\\n{}\\n```\",\n            textwrap::indent(source, \"    \")\n        )\n    }\n\n    // Some unwrapping patterns look weird because they're replaceable\n    // by simpler patterns like `unwrap_or_else` or `unwrap_err\n    let mut i = 1;\n    for action in actions.into_iter().map(|a| a.0) {\n        match action {\n            Inner::RunHarness => {\n                // add utility functions for testing\n                // TODO: extract to a file\n                forward_val(\n                    context,\n                    r#\"\n                        function equals(a, b) {\n                            if (Array.isArray(a) && Array.isArray(b)) {\n                                return arrayEquals(a, b);\n                            }\n                            return a === b;\n                        }\n                        function arrayEquals(a, b) {\n                            return Array.isArray(a) &&\n                                Array.isArray(b) &&\n                                a.length === b.length &&\n                                a.every((val, index) => equals(val, b[index]));\n                        }\n                    \"#,\n                )\n                .expect(\"failed to evaluate test harness\");\n            }\n            Inner::Run { source } => {\n                if let Err(e) = forward_val(context, &source) {\n                    panic!(\"{}\\nUncaught {e}\", fmt_test(&source, i));\n                }\n            }\n            Inner::InspectContext { op } => {\n                op(context);\n            }\n            Inner::InspectContextAsync { op } => futures_lite::future::block_on(op(context)),\n            Inner::Assert { source } => {\n                let val = match forward_val(context, &source) {\n                    Err(e) => panic!(\"{}\\nUncaught {e}\", fmt_test(&source, i)),\n                    Ok(v) => v,\n                };\n                let Some(val) = val.as_boolean() else {\n                    panic!(\n                        \"{}\\nTried to assert with the non-boolean value `{}`\",\n                        fmt_test(&source, i),\n                        val.display()\n                    )\n                };\n                assert!(val, \"{}\", fmt_test(&source, i));\n                i += 1;\n            }\n            Inner::AssertEq { source, expected } => {\n                let val = match forward_val(context, &source) {\n                    Err(e) => panic!(\"{}\\nUncaught {e}\", fmt_test(&source, i)),\n                    Ok(v) => v,\n                };\n                assert_eq!(val, expected, \"{}\", fmt_test(&source, i));\n                i += 1;\n            }\n            Inner::AssertWithOp { source, op } => {\n                let val = match forward_val(context, &source) {\n                    Err(e) => panic!(\"{}\\nUncaught {e}\", fmt_test(&source, i)),\n                    Ok(v) => v,\n                };\n                assert!(op(val, context), \"{}\", fmt_test(&source, i));\n                i += 1;\n            }\n            Inner::AssertOpaqueError { source, expected } => {\n                let err = match forward_val(context, &source) {\n                    Ok(v) => panic!(\n                        \"{}\\nExpected error, got value `{}`\",\n                        fmt_test(&source, i),\n                        v.display()\n                    ),\n                    Err(e) => e,\n                };\n                let Some(err) = err.as_opaque() else {\n                    panic!(\n                        \"{}\\nExpected opaque error, got native error `{}`\",\n                        fmt_test(&source, i),\n                        err\n                    )\n                };\n\n                assert_eq!(err, &expected, \"{}\", fmt_test(&source, i));\n                i += 1;\n            }\n            Inner::AssertNativeError {\n                source,\n                kind,\n                message,\n            } => {\n                let err = match forward_val(context, &source) {\n                    Ok(v) => panic!(\n                        \"{}\\nExpected error, got value `{}`\",\n                        fmt_test(&source, i),\n                        v.display()\n                    ),\n                    Err(e) => e,\n                };\n                let native = match err.try_native(context) {\n                    Ok(err) => err,\n                    Err(e) => panic!(\n                        \"{}\\nCouldn't obtain a native error: {e}\",\n                        fmt_test(&source, i)\n                    ),\n                };\n\n                assert_eq!(native.kind(), &kind, \"{}\", fmt_test(&source, i));\n                assert_eq!(native.message(), message, \"{}\", fmt_test(&source, i));\n                i += 1;\n            }\n            Inner::AssertEngineError { source, error } => {\n                let err = match forward_val(context, &source) {\n                    Ok(v) => panic!(\n                        \"{}\\nExpected error, got value `{}`\",\n                        fmt_test(&source, i),\n                        v.display()\n                    ),\n                    Err(e) => e,\n                };\n\n                let Some(err) = err.as_engine() else {\n                    panic!(\n                        \"{}\\nExpected a native error, got {err}\",\n                        fmt_test(&source, i)\n                    )\n                };\n\n                assert_eq!(*err, error);\n                i += 1;\n            }\n            Inner::AssertContext { op } => {\n                assert!(op(context), \"Test case {i}\");\n                i += 1;\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "core/engine/src/module/loader/embedded.rs",
    "content": "//! Embedded module loader. Creates a `ModuleLoader` instance that contains\n//! files embedded in the binary at build time.\n\nuse std::cell::RefCell;\nuse std::collections::HashMap;\nuse std::path::Path;\nuse std::rc::Rc;\n\nuse boa_engine::module::{ModuleLoader, Referrer};\nuse boa_engine::{Context, JsNativeError, JsResult, JsString, Module, Source};\n\n/// Create a module loader that embeds files from the filesystem at build\n/// time. This is useful for bundling assets with the binary.\n///\n/// By default will error if the total file size exceeds 1MB. This can be\n/// changed by specifying the `max_size` parameter.\n///\n/// The embedded module will only contain files that have the `.js`, `.mjs`,\n/// or `.cjs` extension.\n#[macro_export]\nmacro_rules! embed_module {\n    ($($x: expr),*) => {\n        $crate::module::embedded::EmbeddedModuleLoader::from_iter(\n            $crate::__embed_module_inner!($($x),*),\n        )\n    };\n}\n\n#[derive(Debug, Clone)]\nenum EmbeddedModuleEntry {\n    Source(CompressType, JsString, &'static [u8]),\n    Module(Module),\n}\n\nimpl EmbeddedModuleEntry {\n    fn from_source(compress_type: CompressType, path: JsString, source: &'static [u8]) -> Self {\n        Self::Source(compress_type, path, source)\n    }\n\n    fn cache(&mut self, context: &mut Context) -> JsResult<&Module> {\n        if let Self::Source(compress, path, source) = self {\n            let mut bytes: &[u8] = match compress {\n                CompressType::None => source,\n\n                #[cfg(feature = \"embedded_lz4\")]\n                CompressType::Lz4 => &lz4_flex::decompress_size_prepended(source)\n                    .map_err(|e| boa_engine::js_error!(\"Could not decompress module: {}\", e))?,\n            };\n            let path = path.to_std_string_escaped();\n            let source = Source::from_reader(&mut bytes, Some(Path::new(&path)));\n            match Module::parse(source, None, context) {\n                Ok(module) => {\n                    *self = Self::Module(module);\n                }\n                Err(err) => {\n                    return Err(err);\n                }\n            }\n        }\n\n        match self {\n            Self::Module(module) => Ok(module),\n            EmbeddedModuleEntry::Source(_, _, _) => unreachable!(),\n        }\n    }\n\n    fn as_module(&self) -> Option<&Module> {\n        match self {\n            Self::Module(module) => Some(module),\n            Self::Source(_, _, _) => None,\n        }\n    }\n}\n\n/// The type of compression used, if any.\n#[derive(Debug, Copy, Clone)]\npub enum CompressType {\n    /// No compression used.\n    None,\n\n    #[cfg(feature = \"embedded_lz4\")]\n    /// LZ4 compression.\n    Lz4,\n}\n\nimpl TryFrom<&str> for CompressType {\n    type Error = &'static str;\n\n    fn try_from(value: &str) -> Result<Self, Self::Error> {\n        match value {\n            \"none\" => Ok(Self::None),\n            #[cfg(feature = \"embedded_lz4\")]\n            \"lz4\" => Ok(Self::Lz4),\n            _ => Err(\"Invalid compression type\"),\n        }\n    }\n}\n\n/// The resulting type of creating an embedded module loader.\n#[derive(Debug, Default, Clone)]\n#[allow(clippy::module_name_repetitions)]\npub struct EmbeddedModuleLoader {\n    map: HashMap<JsString, RefCell<EmbeddedModuleEntry>>,\n}\n\nimpl EmbeddedModuleLoader {\n    /// Get a module if it has been parsed and created. If the module is not found or\n    /// was not loaded, this will return `None`.\n    #[must_use]\n    pub fn get_module(&self, name: &JsString) -> Option<Module> {\n        self.map\n            .get(name)\n            .and_then(|module| module.borrow().as_module().cloned())\n    }\n}\n\nimpl FromIterator<(&'static str, &'static str, &'static [u8])> for EmbeddedModuleLoader {\n    fn from_iter<T: IntoIterator<Item = (&'static str, &'static str, &'static [u8])>>(\n        iter: T,\n    ) -> Self {\n        Self {\n            map: iter\n                .into_iter()\n                .map(|(compress_type, path, source)| {\n                    let p = JsString::from(path);\n                    (\n                        p.clone(),\n                        RefCell::new(EmbeddedModuleEntry::from_source(\n                            compress_type.try_into().expect(\"Invalid compress type\"),\n                            p,\n                            source,\n                        )),\n                    )\n                })\n                .collect(),\n        }\n    }\n}\n\nimpl ModuleLoader for EmbeddedModuleLoader {\n    fn load_imported_module(\n        self: Rc<Self>,\n        referrer: Referrer,\n        request: boa_engine::module::ModuleRequest,\n        context: &RefCell<&mut Context>,\n    ) -> impl Future<Output = JsResult<Module>> {\n        let result = (|| {\n            let specifier_path = boa_engine::module::resolve_module_specifier(\n                None,\n                request.specifier(),\n                referrer.path(),\n                &mut context.borrow_mut(),\n            )\n            .map_err(|e| {\n                JsNativeError::typ()\n                    .with_message(format!(\n                        \"could not resolve module specifier `{}`\",\n                        request.specifier().display_escaped()\n                    ))\n                    .with_cause(e)\n            })?;\n\n            let module = self\n                .map\n                .get(&JsString::from(specifier_path.to_string_lossy().as_ref()))\n                .ok_or_else(|| {\n                    JsNativeError::typ().with_message(format!(\n                        \"could not find module `{}`\",\n                        request.specifier().display_escaped()\n                    ))\n                })?;\n\n            let mut embedded = module.borrow_mut();\n            embedded.cache(&mut context.borrow_mut()).cloned()\n        })();\n\n        async { result }\n    }\n}\n"
  },
  {
    "path": "core/engine/src/module/loader/mod.rs",
    "content": "use std::any::Any;\nuse std::cell::RefCell;\nuse std::path::{Component, Path, PathBuf};\nuse std::rc::Rc;\n\nuse dynify::dynify;\nuse rustc_hash::FxHashMap;\n\nuse boa_gc::GcRefCell;\nuse boa_parser::Source;\n\nuse crate::script::Script;\nuse crate::{\n    Context, JsError, JsNativeError, JsResult, JsString, js_error, js_string, object::JsObject,\n    realm::Realm, vm::ActiveRunnable,\n};\n\nuse super::Module;\nuse crate::module::{ImportAttribute, ModuleRequest};\n\npub mod embedded;\n\n/// Resolves paths from the referrer and the specifier, normalize the paths and ensure the path\n/// is within a base. If the base is empty, that last verification will be skipped.\n///\n/// The returned specifier is a resolved absolute path that is guaranteed to be\n/// a descendant of `base`. All path component that are either empty or `.` and\n/// `..` have been resolved.\n///\n/// # Errors\n/// This predicate will return an error if the specifier is relative but the referrer\n/// does not have a path, or if the resolved path is outside `base`.\n///\n/// # Examples\n/// ```\n/// #[cfg(target_family = \"unix\")]\n/// # {\n/// # use std::path::Path;\n/// # use boa_engine::{Context, js_string};\n/// # use boa_engine::module::resolve_module_specifier;\n/// assert_eq!(\n///     resolve_module_specifier(\n///         Some(Path::new(\"/base\")),\n///         &js_string!(\"../a.js\"),\n///         Some(Path::new(\"/base/hello/ref.js\")),\n///         &mut Context::default()\n///     ),\n///     Ok(\"/base/a.js\".into())\n/// );\n/// # }\n/// ```\npub fn resolve_module_specifier(\n    base: Option<&Path>,\n    specifier: &JsString,\n    referrer: Option<&Path>,\n    _context: &mut Context,\n) -> JsResult<PathBuf> {\n    let base_path = base.map_or_else(|| PathBuf::from(\"\"), PathBuf::from);\n    let referrer_dir = referrer.and_then(|p| p.parent());\n\n    let specifier = specifier.to_std_string_escaped();\n\n    // On Windows, also replace `/` with `\\`. JavaScript imports use `/` as path separator.\n    #[cfg(target_family = \"windows\")]\n    let specifier = cow_utils::CowUtils::cow_replace(&*specifier, '/', \"\\\\\");\n\n    let short_path = Path::new(&*specifier);\n\n    // In ECMAScript, a path is considered relative if it starts with\n    // `./` or `../`. In Rust it's any path that start with `/`.\n    let is_relative = short_path.starts_with(\".\") || short_path.starts_with(\"..\");\n\n    let long_path = if is_relative {\n        if let Some(r_path) = referrer_dir {\n            base_path.join(r_path).join(short_path)\n        } else {\n            return Err(JsError::from_opaque(\n                js_string!(\"relative path without referrer\").into(),\n            ));\n        }\n    } else {\n        base_path.join(&*specifier)\n    };\n\n    if long_path.is_relative() && base.is_some() {\n        return Err(JsError::from_opaque(\n            js_string!(\"resolved path is relative\").into(),\n        ));\n    }\n\n    // Normalize the path. We cannot use `canonicalize` here because it will fail\n    // if the path doesn't exist.\n    let path = long_path\n        .components()\n        .filter(|c| c != &Component::CurDir || c == &Component::Normal(\"\".as_ref()))\n        .try_fold(PathBuf::new(), |mut acc, c| {\n            if c == Component::ParentDir {\n                if acc.as_os_str().is_empty() {\n                    return Err(JsError::from_opaque(\n                        js_string!(\"path is outside the module root\").into(),\n                    ));\n                }\n                acc.pop();\n            } else {\n                acc.push(c);\n            }\n            Ok(acc)\n        })?;\n\n    if path.starts_with(&base_path) {\n        Ok(path)\n    } else {\n        Err(JsError::from_opaque(\n            js_string!(\"path is outside the module root\").into(),\n        ))\n    }\n}\n\n/// The referrer from which a load request of a module originates.\n#[derive(Debug, Clone)]\npub enum Referrer {\n    /// A [**Source Text Module Record**](https://tc39.es/ecma262/#sec-source-text-module-records).\n    Module(Module),\n    /// A [**Realm**](https://tc39.es/ecma262/#sec-code-realms).\n    Realm(Realm),\n    /// A [**Script Record**](https://tc39.es/ecma262/#sec-script-records)\n    Script(Script),\n}\n\nimpl Referrer {\n    /// Gets the path of the referrer, if it has one.\n    #[must_use]\n    pub fn path(&self) -> Option<&Path> {\n        match self {\n            Self::Module(module) => module.path(),\n            Self::Realm(_) => None,\n            Self::Script(script) => script.path(),\n        }\n    }\n}\n\nimpl From<ActiveRunnable> for Referrer {\n    fn from(value: ActiveRunnable) -> Self {\n        match value {\n            ActiveRunnable::Script(script) => Self::Script(script),\n            ActiveRunnable::Module(module) => Self::Module(module),\n        }\n    }\n}\n\n/// Module loading related host hooks.\n///\n/// This trait allows to customize the behaviour of the engine on module load requests and\n/// `import.meta` requests.\n#[dynify]\npub trait ModuleLoader: Any {\n    /// Host hook [`HostLoadImportedModule ( referrer, specifier, hostDefined, payload )`][spec].\n    ///\n    /// This hook allows to customize the module loading functionality of the engine. Technically,\n    /// this should call the [`FinishLoadingImportedModule`][finish] operation, but this simpler API just provides\n    /// an async method that replaces `FinishLoadingImportedModule`.\n    ///\n    /// # Requirements\n    ///\n    /// - The host environment must perform `FinishLoadingImportedModule(referrer, specifier, payload, result)`,\n    ///   where result is either a normal completion containing the loaded Module Record or a throw\n    ///   completion, either synchronously or asynchronously. This is replaced by simply returning from\n    ///   the method.\n    /// - If this operation is called multiple times with the same `(referrer, specifier)` pair and\n    ///   it performs FinishLoadingImportedModule(referrer, specifier, payload, result) where result\n    ///   is a normal completion, then it must perform\n    ///   `FinishLoadingImportedModule(referrer, specifier, payload, result)` with the same result each\n    ///   time.\n    /// - The operation must treat payload as an opaque value to be passed through to\n    ///   `FinishLoadingImportedModule`. (can be ignored)\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-HostLoadImportedModule\n    /// [finish]: https://tc39.es/ecma262/#sec-FinishLoadingImportedModule\n    #[allow(async_fn_in_trait, reason = \"all our APIs are single-threaded\")]\n    async fn load_imported_module(\n        self: Rc<Self>,\n        referrer: Referrer,\n        request: ModuleRequest,\n        context: &RefCell<&mut Context>,\n    ) -> JsResult<Module>;\n\n    /// Host hooks [`HostGetImportMetaProperties ( moduleRecord )`][meta] and\n    /// [`HostFinalizeImportMeta ( importMeta, moduleRecord )`][final].\n    ///\n    /// This unifies both APIs into a single hook that can be overridden on both cases.\n    /// The most common usage is to add properties to `import_meta` and return, but this also\n    /// allows modifying the import meta object in more exotic ways before exposing it to ECMAScript\n    /// code.\n    ///\n    /// The default implementation of `HostGetImportMetaProperties` is to return a new empty List.\n    ///\n    /// [meta]: https://tc39.es/ecma262/#sec-hostgetimportmetaproperties\n    /// [final]: https://tc39.es/ecma262/#sec-hostfinalizeimportmeta\n    #[allow(unused_variables, reason = \"this should be overridden by implementors\")]\n    fn init_import_meta(\n        self: Rc<Self>,\n        import_meta: &JsObject,\n        module: &Module,\n        context: &mut Context,\n    ) {\n    }\n}\n\n/// A module loader that throws when trying to load any modules.\n///\n/// Useful to disable the module system on platforms that don't have a filesystem, for example.\n#[derive(Debug, Clone, Copy)]\npub struct IdleModuleLoader;\n\nimpl ModuleLoader for IdleModuleLoader {\n    async fn load_imported_module(\n        self: Rc<Self>,\n        _referrer: Referrer,\n        _request: ModuleRequest,\n        _context: &RefCell<&mut Context>,\n    ) -> JsResult<Module> {\n        Err(JsNativeError::typ()\n            .with_message(\"module resolution is disabled for this context\")\n            .into())\n    }\n}\n\n/// A module loader that uses a map of specifier -> Module to resolve.\n/// If the module was not registered, it will not be resolved.\n///\n/// A resolution relative to the referrer is performed when loading a\n/// module.\n#[derive(Default, Debug, Clone)]\npub struct MapModuleLoader {\n    inner: RefCell<FxHashMap<PathBuf, Module>>,\n}\n\nimpl MapModuleLoader {\n    /// Creates an empty map module loader.\n    #[must_use]\n    #[inline]\n    pub fn new() -> Self {\n        Self::default()\n    }\n\n    /// Insert or replace a mapping in the inner map, returning any previous module\n    /// if there was one.\n    #[inline]\n    pub fn insert(&self, specifier: impl AsRef<str>, module: Module) -> Option<Module> {\n        self.inner\n            .borrow_mut()\n            .insert(PathBuf::from(specifier.as_ref()), module)\n    }\n\n    /// Clear the map.\n    pub fn clear(&self) {\n        self.inner.borrow_mut().clear();\n    }\n}\n\nimpl FromIterator<(String, Module)> for MapModuleLoader {\n    fn from_iter<T: IntoIterator<Item = (String, Module)>>(iter: T) -> Self {\n        Self {\n            inner: RefCell::new(\n                iter.into_iter()\n                    .map(|(k, v)| (PathBuf::from(k), v))\n                    .collect(),\n            ),\n        }\n    }\n}\n\nimpl ModuleLoader for MapModuleLoader {\n    fn load_imported_module(\n        self: Rc<Self>,\n        referrer: Referrer,\n        request: ModuleRequest,\n        context: &RefCell<&mut Context>,\n    ) -> impl Future<Output = JsResult<Module>> {\n        let result = (|| {\n            let path = resolve_module_specifier(\n                None,\n                request.specifier(),\n                referrer.path(),\n                &mut context.borrow_mut(),\n            )?;\n            if let Some(module) = self.inner.borrow().get(&path) {\n                Ok(module.clone())\n            } else {\n                Err(js_error!(TypeError: \"Module could not be found.\"))\n            }\n        })();\n\n        async { result }\n    }\n}\n\n/// A simple module loader that loads modules relative to a root path.\n///\n/// # Note\n///\n/// This loader only works by using the type methods [`SimpleModuleLoader::insert`] and\n/// [`SimpleModuleLoader::get`]. The utility methods on [`ModuleLoader`] don't work at the moment,\n/// but we'll unify both APIs in the future.\n#[derive(Debug)]\npub struct SimpleModuleLoader {\n    root: PathBuf,\n    module_map: GcRefCell<FxHashMap<PathBuf, Module>>,\n}\n\nimpl SimpleModuleLoader {\n    /// Creates a new `SimpleModuleLoader` from a root module path.\n    pub fn new<P: AsRef<Path>>(root: P) -> JsResult<Self> {\n        if cfg!(target_family = \"wasm\") {\n            return Err(JsNativeError::typ()\n                .with_message(\"cannot resolve a relative path in Wasm targets\")\n                .into());\n        }\n        let root = root.as_ref();\n        let absolute = root.canonicalize().map_err(|e| {\n            JsNativeError::typ()\n                .with_message(format!(\"could not set module root `{}`\", root.display()))\n                .with_cause(JsError::from_opaque(js_string!(e.to_string()).into()))\n        })?;\n        Ok(Self {\n            root: absolute,\n            module_map: GcRefCell::default(),\n        })\n    }\n\n    /// Inserts a new module onto the module map.\n    #[inline]\n    pub fn insert(&self, path: PathBuf, module: Module) {\n        self.module_map.borrow_mut().insert(path, module);\n    }\n\n    /// Inserts a new module onto the module map with the given attributes.\n    ///\n    /// This is an alias for `insert` in this implementation, as it ignores attributes.\n    #[inline]\n    pub fn insert_with_attributes(\n        &self,\n        path: PathBuf,\n        _attributes: &[ImportAttribute],\n        module: Module,\n    ) {\n        self.insert(path, module);\n    }\n\n    /// Gets a module from its original path.\n    #[inline]\n    pub fn get(&self, path: &Path) -> Option<Module> {\n        self.module_map.borrow().get(path).cloned()\n    }\n\n    /// Gets a module from its original path and attributes.\n    ///\n    /// This is an alias for `get` in this implementation, as it ignores attributes.\n    #[inline]\n    pub fn get_with_attributes(\n        &self,\n        path: &Path,\n        _attributes: &[ImportAttribute],\n    ) -> Option<Module> {\n        self.get(path)\n    }\n}\n\nimpl ModuleLoader for SimpleModuleLoader {\n    fn load_imported_module(\n        self: Rc<Self>,\n        referrer: Referrer,\n        request: ModuleRequest,\n        context: &RefCell<&mut Context>,\n    ) -> impl Future<Output = JsResult<Module>> {\n        let result = (|| {\n            let short_path = request.specifier().to_std_string_escaped();\n            let path = resolve_module_specifier(\n                Some(&self.root),\n                request.specifier(),\n                referrer.path(),\n                &mut context.borrow_mut(),\n            )?;\n\n            if let Some(module) = self.get_with_attributes(&path, request.attributes()) {\n                return Ok(module);\n            }\n\n            // Check for import attribute type\n            let mut module_type = None;\n            let type_key = js_string!(\"type\");\n            for attr in request.attributes() {\n                if attr.key() == &type_key {\n                    module_type = Some(attr.value());\n                }\n            }\n\n            if path\n                .extension()\n                .is_some_and(|ext| ext.to_string_lossy() == \"json\")\n            {\n                let is_json_type = module_type.is_some_and(|t| t == &js_string!(\"json\"));\n                if !is_json_type {\n                    return Err(JsNativeError::typ()\n                        .with_message(format!(\n                            \"module `{short_path}` needs an import attribute of type \\\"json\\\"\"\n                        ))\n                        .into());\n                }\n            }\n            let module = if let Some(ty) = module_type {\n                // Handle different module types based on the type attribute\n                match ty.to_std_string_escaped().as_str() {\n                    \"json\" => {\n                        // Load and parse as JSON module\n                        let json_content = std::fs::read_to_string(&path).map_err(|err| {\n                            JsNativeError::typ()\n                                .with_message(format!(\"could not open file `{short_path}`\"))\n                                .with_cause(JsError::from_opaque(\n                                    js_string!(err.to_string()).into(),\n                                ))\n                        })?;\n                        let json_string = js_string!(json_content.as_str());\n                        Module::parse_json(json_string, &mut context.borrow_mut()).map_err(\n                            |err| {\n                                JsNativeError::syntax()\n                                    .with_message(format!(\n                                        \"could not parse JSON module `{short_path}`\"\n                                    ))\n                                    .with_cause(err)\n                            },\n                        )?\n                    }\n                    other => {\n                        // Unknown module type\n                        return Err(JsNativeError::typ()\n                            .with_message(format!(\n                                \"unsupported module type `{other}` for module `{short_path}`\"\n                            ))\n                            .into());\n                    }\n                }\n            } else {\n                // No type attribute, load as regular JavaScript module\n                let source = Source::from_filepath(&path).map_err(|err| {\n                    JsNativeError::typ()\n                        .with_message(format!(\"could not open file `{short_path}`\"))\n                        .with_cause(JsError::from_opaque(js_string!(err.to_string()).into()))\n                })?;\n                Module::parse(source, None, &mut context.borrow_mut()).map_err(|err| {\n                    JsNativeError::syntax()\n                        .with_message(format!(\"could not parse module `{short_path}`\"))\n                        .with_cause(err)\n                })?\n            };\n\n            self.insert_with_attributes(path, request.attributes(), module.clone());\n            Ok(module)\n        })();\n\n        async { result }\n    }\n}\n\n#[cfg(test)]\nmod tests {\n    use std::path::PathBuf;\n\n    use test_case::test_case;\n\n    use super::*;\n\n    // Tests on Windows and Linux are different because of the path separator and the definition\n    // of absolute paths.\n    #[rustfmt::skip]\n    #[cfg(target_family = \"unix\")]\n    #[test_case(Some(\"/hello/ref.js\"),      \"a.js\",             Ok(\"/base/a.js\"))]\n    #[test_case(Some(\"/base/ref.js\"),       \"./b.js\",           Ok(\"/base/b.js\"))]\n    #[test_case(Some(\"/base/other/ref.js\"), \"./c.js\",           Ok(\"/base/other/c.js\"))]\n    #[test_case(Some(\"/base/other/ref.js\"), \"../d.js\",          Ok(\"/base/d.js\"))]\n    #[test_case(Some(\"/base/ref.js\"),        \"e.js\",            Ok(\"/base/e.js\"))]\n    #[test_case(Some(\"/base/ref.js\"),        \"./f.js\",          Ok(\"/base/f.js\"))]\n    #[test_case(Some(\"./ref.js\"),           \"./g.js\",           Ok(\"/base/g.js\"))]\n    #[test_case(Some(\"./other/ref.js\"),     \"./other/h.js\",     Ok(\"/base/other/other/h.js\"))]\n    #[test_case(Some(\"./other/ref.js\"),     \"./other/../h1.js\", Ok(\"/base/other/h1.js\"))]\n    #[test_case(Some(\"./other/ref.js\"),     \"./../h2.js\",       Ok(\"/base/h2.js\"))]\n    #[test_case(None,                       \"./i.js\",           Err(()))]\n    #[test_case(None,                       \"j.js\",             Ok(\"/base/j.js\"))]\n    #[test_case(None,                       \"other/k.js\",       Ok(\"/base/other/k.js\"))]\n    #[test_case(None,                       \"other/../../l.js\", Err(()))]\n    #[test_case(Some(\"/base/ref.js\"),       \"other/../../m.js\", Err(()))]\n    #[test_case(None,                       \"../n.js\",          Err(()))]\n    fn resolve_test(ref_path: Option<&str>, spec: &str, expected: Result<&str, ()>) {\n        let base = PathBuf::from(\"/base\");\n\n        let mut context = Context::default();\n        let spec = js_string!(spec);\n        let ref_path = ref_path.map(PathBuf::from);\n\n        let actual = resolve_module_specifier(\n            Some(&base),\n            &spec,\n            ref_path.as_deref(),\n            &mut context,\n        );\n        assert_eq!(actual.map_err(|_| ()), expected.map(PathBuf::from));\n    }\n\n    // This tests the same cases as the previous test, but without a base path.\n    #[rustfmt::skip]\n    #[cfg(target_family = \"unix\")]\n    #[test_case(Some(\"hello/ref.js\"),       \"a.js\",             Ok(\"a.js\"))]\n    #[test_case(Some(\"base/ref.js\"),        \"./b.js\",           Ok(\"base/b.js\"))]\n    #[test_case(Some(\"base/other/ref.js\"),  \"./c.js\",           Ok(\"base/other/c.js\"))]\n    #[test_case(Some(\"base/other/ref.js\"),  \"../d.js\",          Ok(\"base/d.js\"))]\n    #[test_case(Some(\"base/ref.js\"),        \"e.js\",             Ok(\"e.js\"))]\n    #[test_case(Some(\"base/ref.js\"),        \"./f.js\",           Ok(\"base/f.js\"))]\n    #[test_case(Some(\"./ref.js\"),           \"./g.js\",           Ok(\"g.js\"))]\n    #[test_case(Some(\"./other/ref.js\"),     \"./other/h.js\",     Ok(\"other/other/h.js\"))]\n    #[test_case(Some(\"./other/ref.js\"),     \"./other/../h1.js\", Ok(\"other/h1.js\"))]\n    #[test_case(Some(\"./other/ref.js\"),     \"./../h2.js\",       Ok(\"h2.js\"))]\n    #[test_case(None,                       \"./i.js\",           Err(()))]\n    #[test_case(None,                       \"j.js\",             Ok(\"j.js\"))]\n    #[test_case(None,                       \"other/k.js\",       Ok(\"other/k.js\"))]\n    #[test_case(None,                       \"other/../../l.js\", Err(()))]\n    #[test_case(Some(\"/base/ref.js\"),       \"other/../../m.js\", Err(()))]\n    #[test_case(None,                       \"../n.js\",          Err(()))]\n    fn resolve_test_no_base(ref_path: Option<&str>, spec: &str, expected: Result<&str, ()>) {\n        let mut context = Context::default();\n        let spec = js_string!(spec);\n        let ref_path = ref_path.map(PathBuf::from);\n\n        let actual = resolve_module_specifier(\n            None,\n            &spec,\n            ref_path.as_deref(),\n            &mut context,\n        );\n        assert_eq!(actual.map_err(|_| ()), expected.map(PathBuf::from));\n    }\n\n    #[rustfmt::skip]\n    #[cfg(target_family = \"windows\")]\n    #[test_case(Some(\"a:\\\\hello\\\\ref.js\"),       \"a.js\",                Ok(\"a:\\\\base\\\\a.js\"))]\n    #[test_case(Some(\"a:\\\\base\\\\ref.js\"),        \"./b.js\",              Ok(\"a:\\\\base\\\\b.js\"))]\n    #[test_case(Some(\"a:\\\\base\\\\other\\\\ref.js\"), \"./c.js\",              Ok(\"a:\\\\base\\\\other\\\\c.js\"))]\n    #[test_case(Some(\"a:\\\\base\\\\other\\\\ref.js\"), \"../d.js\",             Ok(\"a:\\\\base\\\\d.js\"))]\n    #[test_case(Some(\"a:\\\\base\\\\ref.js\"),        \"e.js\",                Ok(\"a:\\\\base\\\\e.js\"))]\n    #[test_case(Some(\"a:\\\\base\\\\ref.js\"),        \"./f.js\",              Ok(\"a:\\\\base\\\\f.js\"))]\n    #[test_case(Some(\".\\\\ref.js\"),               \"./g.js\",              Ok(\"a:\\\\base\\\\g.js\"))]\n    #[test_case(Some(\".\\\\other\\\\ref.js\"),        \"./other/h.js\",        Ok(\"a:\\\\base\\\\other\\\\other\\\\h.js\"))]\n    #[test_case(Some(\".\\\\other\\\\ref.js\"),        \"./other/../h1.js\",    Ok(\"a:\\\\base\\\\other\\\\h1.js\"))]\n    #[test_case(Some(\".\\\\other\\\\ref.js\"),        \"./../h2.js\",          Ok(\"a:\\\\base\\\\h2.js\"))]\n    #[test_case(None,                            \"./i.js\",              Err(()))]\n    #[test_case(None,                            \"j.js\",                Ok(\"a:\\\\base\\\\j.js\"))]\n    #[test_case(None,                            \"other/k.js\",          Ok(\"a:\\\\base\\\\other\\\\k.js\"))]\n    #[test_case(None,                            \"other/../../l.js\",    Err(()))]\n    #[test_case(Some(\"\\\\base\\\\ref.js\"),          \"other/../../m.js\",    Err(()))]\n    #[test_case(None,                            \"../n.js\",             Err(()))]\n    fn resolve_test(ref_path: Option<&str>, spec: &str, expected: Result<&str, ()>) {\n        let base = PathBuf::from(\"a:\\\\base\");\n\n        let mut context = Context::default();\n        let spec = js_string!(spec);\n        let ref_path = ref_path.map(PathBuf::from);\n\n        let actual = resolve_module_specifier(\n            Some(&base),\n            &spec,\n            ref_path.as_deref(),\n            &mut context,\n        );\n        assert_eq!(actual.map_err(|_| ()), expected.map(PathBuf::from));\n    }\n}\n"
  },
  {
    "path": "core/engine/src/module/mod.rs",
    "content": "//! Boa's implementation of the ECMAScript's module system.\n//!\n//! This module contains the [`Module`] type, which represents an [**Abstract Module Record**][module],\n//! a [`ModuleLoader`] trait for custom module loader implementations, and [`SimpleModuleLoader`],\n//! the default `ModuleLoader` for [`Context`] which can be used for most simple usecases.\n//!\n//! Every module roughly follows the same lifecycle:\n//! - Parse using [`Module::parse`].\n//! - Load all its dependencies using [`Module::load`].\n//! - Link its dependencies together using [`Module::link`].\n//! - Evaluate the module and its dependencies using [`Module::evaluate`].\n//!\n//! The [`ModuleLoader`] trait allows customizing the \"load\" step on the lifecycle\n//! of a module, which allows doing things like fetching modules from urls, having multiple\n//! \"modpaths\" from where to import modules, or using Rust futures to avoid blocking the main thread\n//! on loads.\n//!\n//! More information:\n//!  - [ECMAScript reference][spec]\n//!\n//! [spec]: https://tc39.es/ecma262/#sec-modules\n//! [module]: https://tc39.es/ecma262/#sec-abstract-module-records\n\nuse std::cell::{Cell, RefCell};\nuse std::collections::HashSet;\nuse std::hash::Hash;\nuse std::path::{Path, PathBuf};\nuse std::rc::Rc;\n\nuse rustc_hash::FxHashSet;\n\nuse boa_ast::declaration::ImportAttribute as AstImportAttribute;\nuse boa_engine::js_string;\nuse boa_engine::property::PropertyKey;\nuse boa_engine::value::TryFromJs;\nuse boa_gc::{Finalize, Gc, GcRefCell, Trace};\nuse boa_interner::Interner;\nuse boa_parser::source::ReadChar;\nuse boa_parser::{Parser, Source};\n\npub use loader::*;\npub use namespace::ModuleNamespace;\nuse source::SourceTextModule;\npub use synthetic::{SyntheticModule, SyntheticModuleInitializer};\n\nuse crate::bytecompiler::ToJsString;\nuse crate::object::TypedJsFunction;\nuse crate::spanned_source_text::SourceText;\nuse crate::{\n    Context, HostDefined, JsError, JsNativeError, JsResult, JsString, JsValue, NativeFunction,\n    builtins,\n    builtins::promise::{PromiseCapability, PromiseState},\n    environments::DeclarativeEnvironment,\n    object::{JsObject, JsPromise},\n    realm::Realm,\n};\n\nmod loader;\nmod namespace;\nmod source;\nmod synthetic;\n\n/// Import attribute.\n///\n/// [spec]: https://tc39.es/ecma262/#table-importattribute-fields\n#[derive(Debug, Clone, PartialEq, Eq, Hash, Trace, Finalize)]\npub struct ImportAttribute {\n    key: JsString,\n    value: JsString,\n}\n\nimpl ImportAttribute {\n    /// Creates a new import attribute.\n    #[must_use]\n    pub fn new(key: JsString, value: JsString) -> Self {\n        Self { key, value }\n    }\n\n    /// Gets the attribute key.\n    #[must_use]\n    pub fn key(&self) -> &JsString {\n        &self.key\n    }\n\n    /// Gets the attribute value.\n    #[must_use]\n    pub fn value(&self) -> &JsString {\n        &self.value\n    }\n}\n\n/// A module request with optional import attributes.\n///\n/// Represents a module specifier and its associated import attributes.\n/// According to the [ECMAScript specification][spec], the module cache key\n/// should be (referrer, specifier, attributes).\n///\n/// [spec]: https://tc39.es/ecma262/#sec-modulerequest-record\n#[derive(Debug, Clone, PartialEq, Eq, Hash, Trace, Finalize)]\npub struct ModuleRequest {\n    specifier: JsString,\n    attributes: Box<[ImportAttribute]>,\n}\n\nimpl ModuleRequest {\n    /// Creates a new module request from a specifier and attributes.\n    #[must_use]\n    pub fn new(specifier: JsString, mut attributes: Box<[ImportAttribute]>) -> Self {\n        // Sort attributes by key to ensure canonical cache keys.\n        attributes.sort_unstable_by(|k1, k2| k1.key.cmp(&k2.key));\n        Self {\n            specifier,\n            attributes,\n        }\n    }\n\n    /// Creates a new module request from only a specifier with no attributes.\n    #[must_use]\n    pub fn from_specifier(specifier: JsString) -> Self {\n        Self {\n            specifier,\n            attributes: Box::default(),\n        }\n    }\n\n    /// Creates a new module request from an AST specifier and attributes.\n    #[must_use]\n    pub(crate) fn from_ast(\n        specifier: JsString,\n        attributes: &[AstImportAttribute],\n        interner: &Interner,\n    ) -> Self {\n        let attributes = attributes\n            .iter()\n            .map(|attr| {\n                ImportAttribute::new(\n                    attr.key().to_js_string(interner),\n                    attr.value().to_js_string(interner),\n                )\n            })\n            .collect::<Vec<_>>()\n            .into_boxed_slice();\n        Self::new(specifier, attributes)\n    }\n\n    /// Gets the module specifier.\n    #[must_use]\n    pub fn specifier(&self) -> &JsString {\n        &self.specifier\n    }\n\n    /// Gets the import attributes as key-value pairs.\n    #[must_use]\n    pub fn attributes(&self) -> &[ImportAttribute] {\n        &self.attributes\n    }\n\n    /// Gets the value of a specific attribute by key.\n    #[must_use]\n    pub fn get_attribute(&self, key: &str) -> Option<&JsString> {\n        self.attributes\n            .iter()\n            .find(|attr| attr.key == key)\n            .map(|attr| &attr.value)\n    }\n}\n\n/// ECMAScript's [**Abstract module record**][spec].\n///\n/// [spec]: https://tc39.es/ecma262/#sec-abstract-module-records\n#[derive(Clone, Trace, Finalize)]\npub struct Module {\n    inner: Gc<ModuleRepr>,\n}\n\nimpl std::fmt::Debug for Module {\n    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {\n        f.debug_struct(\"Module\")\n            .field(\"realm\", &self.inner.realm.addr())\n            .field(\"namespace\", &self.inner.namespace)\n            .field(\"kind\", &self.inner.kind)\n            .finish()\n    }\n}\n\n#[derive(Trace, Finalize)]\nstruct ModuleRepr {\n    realm: Realm,\n    namespace: GcRefCell<Option<JsObject>>,\n    kind: ModuleKind,\n    host_defined: HostDefined,\n    path: Option<PathBuf>,\n}\n\n/// The kind of a [`Module`].\n#[derive(Debug, Trace, Finalize)]\npub(crate) enum ModuleKind {\n    /// A [**Source Text Module Record**](https://tc39.es/ecma262/#sec-source-text-module-records)\n    SourceText(Box<SourceTextModule>),\n    /// A [**Synthetic Module Record**](https://tc39.es/proposal-json-modules/#sec-synthetic-module-records)\n    Synthetic(Box<SyntheticModule>),\n}\n\nimpl ModuleKind {\n    /// Returns the inner `SourceTextModule`.\n    pub(crate) fn as_source_text(&self) -> Option<&SourceTextModule> {\n        match self {\n            ModuleKind::SourceText(src) => Some(src),\n            ModuleKind::Synthetic(_) => None,\n        }\n    }\n}\n\n/// Return value of the [`Module::resolve_export`] operation.\n///\n/// Indicates how to access a specific export in a module.\n#[derive(Debug, Clone)]\npub(crate) struct ResolvedBinding {\n    module: Module,\n    binding_name: BindingName,\n}\n\n/// The local name of the resolved binding within its containing module.\n///\n/// Note that a resolved binding can resolve to a single binding inside a module (`export var a = 1\"`)\n/// or to a whole module namespace (`export * as ns from \"mod.js\"`).\n#[derive(Debug, Clone)]\npub(crate) enum BindingName {\n    /// A local binding.\n    Name(JsString),\n    /// The whole namespace of the containing module.\n    Namespace,\n}\n\nimpl ResolvedBinding {\n    /// Gets the module from which the export resolved.\n    pub(crate) const fn module(&self) -> &Module {\n        &self.module\n    }\n\n    /// Consumes `self` and returns the module from which the export resolved.\n    pub(crate) fn into_module(self) -> Module {\n        self.module\n    }\n\n    /// Gets a reference to the binding associated with the resolved export.\n    pub(crate) const fn binding_name(&self) -> &BindingName {\n        &self.binding_name\n    }\n}\n\n#[derive(Debug, Clone)]\nstruct GraphLoadingState {\n    capability: PromiseCapability,\n    loading: Cell<bool>,\n    pending_modules: Cell<usize>,\n    visited: RefCell<HashSet<Module>>,\n}\n\n#[derive(Debug, Clone, Copy)]\npub(crate) enum ResolveExportError {\n    NotFound,\n    Ambiguous,\n}\n\nimpl Module {\n    /// Abstract operation [`ParseModule ( sourceText, realm, hostDefined )`][spec].\n    ///\n    /// Parses the provided `src` as an ECMAScript module, returning an error if parsing fails.\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-parsemodule\n    pub fn parse<R: ReadChar>(\n        src: Source<'_, R>,\n        realm: Option<Realm>,\n        context: &mut Context,\n    ) -> JsResult<Self> {\n        let path = src.path().map(Path::to_path_buf);\n        let realm = realm.unwrap_or_else(|| context.realm().clone());\n\n        let mut parser = Parser::new(src);\n        parser.set_identifier(context.next_parser_identifier());\n        let (module, source) =\n            parser.parse_module_with_source(realm.scope(), context.interner_mut())?;\n\n        let source_text = SourceText::new(source);\n        let src = SourceTextModule::new(module, context.interner(), source_text, path.clone());\n\n        Ok(Self {\n            inner: Gc::new(ModuleRepr {\n                realm,\n                namespace: GcRefCell::default(),\n                kind: ModuleKind::SourceText(Box::new(src)),\n                host_defined: HostDefined::default(),\n                path,\n            }),\n        })\n    }\n\n    /// Abstract operation [`CreateSyntheticModule ( exportNames, evaluationSteps, realm )`][spec].\n    ///\n    /// Creates a new Synthetic Module from its list of exported names, its evaluation steps and\n    /// optionally a root realm.\n    ///\n    /// [spec]: https://tc39.es/proposal-json-modules/#sec-createsyntheticmodule\n    #[inline]\n    pub fn synthetic(\n        export_names: &[JsString],\n        evaluation_steps: SyntheticModuleInitializer,\n        path: Option<PathBuf>,\n        realm: Option<Realm>,\n        context: &mut Context,\n    ) -> Self {\n        let names = export_names.iter().cloned().collect();\n        let realm = realm.unwrap_or_else(|| context.realm().clone());\n        let synth = SyntheticModule::new(names, evaluation_steps);\n\n        Self {\n            inner: Gc::new(ModuleRepr {\n                realm,\n                namespace: GcRefCell::default(),\n                kind: ModuleKind::Synthetic(Box::new(synth)),\n                host_defined: HostDefined::default(),\n                path,\n            }),\n        }\n    }\n\n    /// Create a [`Module`] from a `JsValue`, exporting that value as the default export.\n    /// This will clone the module everytime it is initialized.\n    pub fn from_value_as_default(value: JsValue, context: &mut Context) -> Self {\n        Module::synthetic(\n            &[js_string!(\"default\")],\n            SyntheticModuleInitializer::from_copy_closure_with_captures(\n                move |m, value, _ctx| {\n                    m.set_export(&js_string!(\"default\"), value.clone())?;\n                    Ok(())\n                },\n                value,\n            ),\n            None,\n            None,\n            context,\n        )\n    }\n\n    /// Create a module that exports a single JSON value as the default export, from its\n    /// JSON string.\n    ///\n    /// # Specification\n    /// This is a custom extension to the ECMAScript specification. The current proposal\n    /// for JSON modules is being considered in <https://github.com/tc39/proposal-json-modules>\n    /// and might differ from this implementation.\n    ///\n    /// This method is provided as a convenience for hosts to create JSON modules.\n    ///\n    /// # Errors\n    /// This will return an error if the JSON string is invalid or cannot be converted.\n    pub fn parse_json(json: JsString, context: &mut Context) -> JsResult<Self> {\n        let value = builtins::Json::parse(&JsValue::undefined(), &[json.into()], context)?;\n        Ok(Self::from_value_as_default(value, context))\n    }\n\n    /// Gets the realm of this `Module`.\n    #[inline]\n    #[must_use]\n    pub fn realm(&self) -> &Realm {\n        &self.inner.realm\n    }\n\n    /// Returns the [`ECMAScript specification`][spec] defined [`\\[\\[HostDefined\\]\\]`][`HostDefined`] field of the [`Module`].\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-abstract-module-records\n    #[inline]\n    #[must_use]\n    pub fn host_defined(&self) -> &HostDefined {\n        &self.inner.host_defined\n    }\n\n    /// Gets the kind of this `Module`.\n    pub(crate) fn kind(&self) -> &ModuleKind {\n        &self.inner.kind\n    }\n\n    /// Gets the declarative environment of this `Module`.\n    pub(crate) fn environment(&self) -> Option<Gc<DeclarativeEnvironment>> {\n        match self.kind() {\n            ModuleKind::SourceText(src) => src.environment(),\n            ModuleKind::Synthetic(syn) => syn.environment(),\n        }\n    }\n\n    /// Abstract method [`LoadRequestedModules ( [ hostDefined ] )`][spec].\n    ///\n    /// Prepares the module for linking by loading all its module dependencies. Returns a `JsPromise`\n    /// that will resolve when the loading process either completes or fails.\n    ///\n    /// [spec]: https://tc39.es/ecma262/#table-abstract-methods-of-module-records\n    #[allow(clippy::missing_panics_doc)]\n    #[inline]\n    pub fn load(&self, context: &mut Context) -> JsPromise {\n        match self.kind() {\n            ModuleKind::SourceText(_) => {\n                // Concrete method [`LoadRequestedModules ( [ hostDefined ] )`][spec].\n                //\n                // [spec]: https://tc39.es/ecma262/#sec-LoadRequestedModules\n                // 1. If hostDefined is not present, let hostDefined be empty.\n\n                // 2. Let pc be ! NewPromiseCapability(%Promise%).\n                let pc = PromiseCapability::new(\n                    &context.intrinsics().constructors().promise().constructor(),\n                    context,\n                )\n                .expect(\n                    \"capability creation must always succeed when using the `%Promise%` intrinsic\",\n                );\n\n                // 4. Perform InnerModuleLoading(state, module).\n                self.inner_load(\n                    // 3. Let state be the GraphLoadingState Record {\n                    //     [[IsLoading]]: true, [[PendingModulesCount]]: 1, [[Visited]]: « »,\n                    //     [[PromiseCapability]]: pc, [[HostDefined]]: hostDefined\n                    // }.\n                    &Rc::new(GraphLoadingState {\n                        capability: pc.clone(),\n                        loading: Cell::new(true),\n                        pending_modules: Cell::new(1),\n                        visited: RefCell::default(),\n                    }),\n                    context,\n                );\n\n                // 5. Return pc.[[Promise]].\n                JsPromise::from_object(pc.promise().clone())\n                    .expect(\"promise created from the %Promise% intrinsic is always native\")\n            }\n            ModuleKind::Synthetic(_) => SyntheticModule::load(context),\n        }\n    }\n\n    /// Abstract operation [`InnerModuleLoading`][spec].\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-InnerModuleLoading\n    fn inner_load(&self, state: &Rc<GraphLoadingState>, context: &mut Context) {\n        // 1. Assert: state.[[IsLoading]] is true.\n        assert!(state.loading.get());\n\n        if let ModuleKind::SourceText(src) = self.kind() {\n            // continues on `inner_load\n            src.inner_load(self, state, context);\n            if !state.loading.get() {\n                return;\n            }\n        }\n\n        // 3. Assert: state.[[PendingModulesCount]] ≥ 1.\n        assert!(state.pending_modules.get() >= 1);\n\n        // 4. Set state.[[PendingModulesCount]] to state.[[PendingModulesCount]] - 1.\n        state.pending_modules.set(state.pending_modules.get() - 1);\n        // 5. If state.[[PendingModulesCount]] = 0, then\n\n        if state.pending_modules.get() == 0 {\n            // a. Set state.[[IsLoading]] to false.\n            state.loading.set(false);\n            // b. For each Cyclic Module Record loaded of state.[[Visited]], do\n            //    i. If loaded.[[Status]] is new, set loaded.[[Status]] to unlinked.\n            // By default, all modules start on `unlinked`.\n\n            // c. Perform ! Call(state.[[PromiseCapability]].[[Resolve]], undefined, « undefined »).\n            state\n                .capability\n                .resolve()\n                .call(&JsValue::undefined(), &[], context)\n                .expect(\"marking a module as loaded should not fail\");\n        }\n        // 6. Return unused.\n    }\n\n    /// Abstract method [`GetExportedNames([exportStarSet])`][spec].\n    ///\n    /// Returns a list of all the names exported from this module.\n    ///\n    /// # Note\n    ///\n    /// This must only be called if the [`JsPromise`] returned by [`Module::load`] has fulfilled.\n    ///\n    /// [spec]: https://tc39.es/ecma262/#table-abstract-methods-of-module-records\n    fn get_exported_names(\n        &self,\n        export_star_set: &mut Vec<Module>,\n        interner: &Interner,\n    ) -> FxHashSet<JsString> {\n        match self.kind() {\n            ModuleKind::SourceText(src) => src.get_exported_names(self, export_star_set, interner),\n            ModuleKind::Synthetic(synth) => synth.get_exported_names(),\n        }\n    }\n\n    /// Abstract method [`ResolveExport(exportName [, resolveSet])`][spec].\n    ///\n    /// Returns the corresponding local binding of a binding exported by this module.\n    /// The spec requires that this operation must be idempotent; calling this multiple times\n    /// with the same `export_name` and `resolve_set` should always return the same result.\n    ///\n    /// # Note\n    ///\n    /// This must only be called if the [`JsPromise`] returned by [`Module::load`] has fulfilled.\n    ///\n    /// [spec]: https://tc39.es/ecma262/#table-abstract-methods-of-module-records\n    #[allow(clippy::mutable_key_type)]\n    pub(crate) fn resolve_export(\n        &self,\n        export_name: &JsString,\n        resolve_set: &mut FxHashSet<(Self, JsString)>,\n        interner: &Interner,\n    ) -> Result<ResolvedBinding, ResolveExportError> {\n        match self.kind() {\n            ModuleKind::SourceText(src) => {\n                src.resolve_export(self, export_name, resolve_set, interner)\n            }\n            ModuleKind::Synthetic(synth) => synth.resolve_export(self, export_name),\n        }\n    }\n\n    /// Abstract method [`Link() `][spec].\n    ///\n    /// Prepares this module for evaluation by resolving all its module dependencies and initializing\n    /// its environment.\n    ///\n    /// # Note\n    ///\n    /// This must only be called if the [`JsPromise`] returned by [`Module::load`] has fulfilled.\n    ///\n    /// [spec]: https://tc39.es/ecma262/#table-abstract-methods-of-module-records\n    #[allow(clippy::missing_panics_doc)]\n    #[inline]\n    pub fn link(&self, context: &mut Context) -> JsResult<()> {\n        match self.kind() {\n            ModuleKind::SourceText(src) => src.link(self, context),\n            ModuleKind::Synthetic(synth) => {\n                synth.link(self, context);\n                Ok(())\n            }\n        }\n    }\n\n    /// Abstract operation [`InnerModuleLinking ( module, stack, index )`][spec].\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-InnerModuleLinking\n    fn inner_link(\n        &self,\n        stack: &mut Vec<Module>,\n        index: usize,\n        context: &mut Context,\n    ) -> JsResult<usize> {\n        match self.kind() {\n            ModuleKind::SourceText(src) => src.inner_link(self, stack, index, context),\n            // If module is not a Cyclic Module Record, then\n            ModuleKind::Synthetic(synth) => {\n                // a. Perform ? module.Link().\n                synth.link(self, context);\n                // b. Return index.\n                Ok(index)\n            }\n        }\n    }\n\n    /// Abstract method [`Evaluate()`][spec].\n    ///\n    /// Evaluates this module, returning a promise for the result of the evaluation of this module\n    /// and its dependencies.\n    /// If the promise is rejected, hosts are expected to handle the promise rejection and rethrow\n    /// the evaluation error.\n    ///\n    /// # Note\n    ///\n    /// This must only be called if the [`Module::link`] method finished successfully.\n    ///\n    /// [spec]: https://tc39.es/ecma262/#table-abstract-methods-of-module-records\n    #[inline]\n    pub fn evaluate(&self, context: &mut Context) -> JsResult<JsPromise> {\n        match self.kind() {\n            ModuleKind::SourceText(src) => src.evaluate(self, context),\n            ModuleKind::Synthetic(synth) => synth.evaluate(self, context),\n        }\n    }\n\n    /// Abstract operation [`InnerModuleLinking ( module, stack, index )`][spec].\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-InnerModuleLinking\n    fn inner_evaluate(\n        &self,\n        stack: &mut Vec<Module>,\n        index: usize,\n        context: &mut Context,\n    ) -> JsResult<usize> {\n        match self.kind() {\n            ModuleKind::SourceText(src) => src.inner_evaluate(self, stack, index, None, context),\n            // 1. If module is not a Cyclic Module Record, then\n            ModuleKind::Synthetic(synth) => {\n                // a. Let promise be ! module.Evaluate().\n                let promise: JsPromise = synth.evaluate(self, context)?;\n                let state = promise.state();\n                match state {\n                    PromiseState::Pending => {\n                        unreachable!(\"b. Assert: promise.[[PromiseState]] is not pending.\")\n                    }\n                    // d. Return index.\n                    PromiseState::Fulfilled(_) => Ok(index),\n                    // c. If promise.[[PromiseState]] is rejected, then\n                    //    i. Return ThrowCompletion(promise.[[PromiseResult]]).\n                    PromiseState::Rejected(err) => Err(JsError::from_opaque(err)),\n                }\n            }\n        }\n    }\n\n    /// Loads, links and evaluates this module, returning a promise that will resolve after the module\n    /// finishes its lifecycle.\n    ///\n    /// # Examples\n    /// ```\n    /// # use std::{path::Path, rc::Rc};\n    /// # use boa_engine::{Context, Source, Module, JsValue};\n    /// # use boa_engine::builtins::promise::PromiseState;\n    /// # use boa_engine::module::{ModuleLoader, SimpleModuleLoader};\n    /// let loader = Rc::new(SimpleModuleLoader::new(Path::new(\".\")).unwrap());\n    /// let mut context = &mut Context::builder()\n    ///     .module_loader(loader.clone())\n    ///     .build()\n    ///     .unwrap();\n    ///\n    /// let source = Source::from_bytes(\"1 + 3\");\n    ///\n    /// let module = Module::parse(source, None, context).unwrap();\n    ///\n    /// loader.insert(Path::new(\"main.mjs\").to_path_buf(), module.clone());\n    ///\n    /// let promise = module.load_link_evaluate(context);\n    ///\n    /// context.run_jobs().unwrap();\n    ///\n    /// assert_eq!(\n    ///     promise.state(),\n    ///     PromiseState::Fulfilled(JsValue::undefined())\n    /// );\n    /// ```\n    #[allow(dropping_copy_types)]\n    #[inline]\n    pub fn load_link_evaluate(&self, context: &mut Context) -> JsPromise {\n        self.load(context)\n            .then(\n                Some(\n                    NativeFunction::from_copy_closure_with_captures(\n                        |_, _, module, context| {\n                            module.link(context)?;\n                            Ok(JsValue::undefined())\n                        },\n                        self.clone(),\n                    )\n                    .to_js_function(context.realm()),\n                ),\n                None,\n                context,\n            )\n            .expect(\"`then` cannot fail for a native `JsPromise`\")\n            .then(\n                Some(\n                    NativeFunction::from_copy_closure_with_captures(\n                        |_, _, module, context| Ok(module.evaluate(context)?.into()),\n                        self.clone(),\n                    )\n                    .to_js_function(context.realm()),\n                ),\n                None,\n                context,\n            )\n            .expect(\"`then` cannot fail for a native `JsPromise`\")\n    }\n\n    /// Abstract operation [`GetModuleNamespace ( module )`][spec].\n    ///\n    /// Gets the [**Module Namespace Object**][ns] that represents this module's exports.\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-getmodulenamespace\n    /// [ns]: https://tc39.es/ecma262/#sec-module-namespace-exotic-objects\n    pub fn namespace(&self, context: &mut Context) -> JsObject {\n        // 1. Assert: If module is a Cyclic Module Record, then module.[[Status]] is not new or unlinked.\n        // 2. Let namespace be module.[[Namespace]].\n        // 3. If namespace is empty, then\n        // 4. Return namespace.\n        self.inner\n            .namespace\n            .borrow_mut()\n            .get_or_insert_with(|| {\n                // a. Let exportedNames be module.GetExportedNames().\n                let exported_names =\n                    self.get_exported_names(&mut Vec::default(), context.interner());\n\n                // b. Let unambiguousNames be a new empty List.\n                let unambiguous_names = exported_names\n                    .into_iter()\n                    // c. For each element name of exportedNames, do\n                    .filter_map(|name| {\n                        // i. Let resolution be module.ResolveExport(name).\n                        // ii. If resolution is a ResolvedBinding Record, append name to unambiguousNames.\n                        self.resolve_export(&name, &mut HashSet::default(), context.interner())\n                            .ok()\n                            .map(|_| name)\n                    })\n                    .collect();\n\n                //     d. Set namespace to ModuleNamespaceCreate(module, unambiguousNames).\n                ModuleNamespace::create(self.clone(), unambiguous_names, context)\n            })\n            .clone()\n    }\n\n    /// Get an exported value from the module.\n    #[inline]\n    pub fn get_value<K>(&self, name: K, context: &mut Context) -> JsResult<JsValue>\n    where\n        K: Into<PropertyKey>,\n    {\n        let namespace = self.namespace(context);\n        namespace.get(name, context)\n    }\n\n    /// Get an exported function, typed, from the module.\n    #[inline]\n    #[allow(clippy::needless_pass_by_value)]\n    pub fn get_typed_fn<A, R>(\n        &self,\n        name: JsString,\n        context: &mut Context,\n    ) -> JsResult<TypedJsFunction<A, R>>\n    where\n        A: crate::object::TryIntoJsArguments,\n        R: TryFromJs,\n    {\n        let func = self.get_value(name.clone(), context)?;\n        let func = func.as_function().ok_or_else(|| {\n            JsNativeError::typ().with_message(format!(\"{name:?} is not a function\"))\n        })?;\n        Ok(func.typed())\n    }\n\n    /// Returns the path of the module, if it was created from a file or assigned.\n    #[must_use]\n    pub fn path(&self) -> Option<&Path> {\n        self.inner.path.as_deref()\n    }\n}\n\nimpl PartialEq for Module {\n    #[inline]\n    fn eq(&self, other: &Self) -> bool {\n        Gc::ptr_eq(&self.inner, &other.inner)\n    }\n}\n\nimpl Eq for Module {}\n\nimpl Hash for Module {\n    #[inline]\n    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {\n        std::ptr::hash(self.inner.as_ref(), state);\n    }\n}\n\n/// A trait to convert a type into a JS module.\npub trait IntoJsModule {\n    /// Converts the type into a JS module.\n    fn into_js_module(self, context: &mut Context) -> Module;\n}\n\nimpl<T: IntoIterator<Item = (JsString, NativeFunction)> + Clone> IntoJsModule for T {\n    fn into_js_module(self, context: &mut Context) -> Module {\n        let (names, fns): (Vec<_>, Vec<_>) = self.into_iter().unzip();\n        let exports = names.clone();\n\n        Module::synthetic(\n            exports.as_slice(),\n            unsafe {\n                SyntheticModuleInitializer::from_closure(move |module, context| {\n                    for (name, f) in names.iter().zip(fns.iter()) {\n                        module\n                            .set_export(name, f.clone().to_js_function(context.realm()).into())?;\n                    }\n                    Ok(())\n                })\n            },\n            None,\n            None,\n            context,\n        )\n    }\n}\n\n#[test]\n#[allow(clippy::missing_panics_doc)]\nfn into_js_module() {\n    use boa_engine::interop::{ContextData, JsRest};\n    use boa_engine::{\n        Context, IntoJsFunctionCopied, JsValue, Module, Source, UnsafeIntoJsFunction, js_string,\n    };\n    use boa_gc::{Gc, GcRefCell};\n    use std::cell::RefCell;\n    use std::rc::Rc;\n\n    type ResultType = Gc<GcRefCell<JsValue>>;\n\n    let loader = Rc::new(MapModuleLoader::default());\n    let mut context = Context::builder()\n        .module_loader(loader.clone())\n        .build()\n        .unwrap();\n\n    let foo_count = Rc::new(RefCell::new(0));\n    let bar_count = Rc::new(RefCell::new(0));\n    let dad_count = Rc::new(RefCell::new(0));\n\n    context.insert_data(Gc::new(GcRefCell::new(JsValue::undefined())));\n\n    let module = unsafe {\n        vec![\n            (\n                js_string!(\"foo\"),\n                {\n                    let counter = foo_count.clone();\n                    move || {\n                        *counter.borrow_mut() += 1;\n\n                        *counter.borrow()\n                    }\n                }\n                .into_js_function_unsafe(&mut context),\n            ),\n            (\n                js_string!(\"bar\"),\n                UnsafeIntoJsFunction::into_js_function_unsafe(\n                    {\n                        let counter = bar_count.clone();\n                        move |i: i32| {\n                            *counter.borrow_mut() += i;\n                        }\n                    },\n                    &mut context,\n                ),\n            ),\n            (\n                js_string!(\"dad\"),\n                UnsafeIntoJsFunction::into_js_function_unsafe(\n                    {\n                        let counter = dad_count.clone();\n                        move |args: JsRest<'_>, context: &mut Context| {\n                            *counter.borrow_mut() += args\n                                .into_iter()\n                                .map(|i| i.try_js_into::<i32>(context).unwrap())\n                                .sum::<i32>();\n                        }\n                    },\n                    &mut context,\n                ),\n            ),\n            (\n                js_string!(\"send\"),\n                (move |value: JsValue, ContextData(result): ContextData<ResultType>| {\n                    *result.borrow_mut() = value;\n                })\n                .into_js_function_copied(&mut context),\n            ),\n        ]\n    }\n    .into_js_module(&mut context);\n\n    loader.insert(\"test\", module);\n\n    let source = Source::from_bytes(\n        r\"\n            import * as test from 'test';\n            let result = test.foo();\n            test.foo();\n            for (let i = 1; i <= 5; i++) {\n                test.bar(i);\n            }\n            for (let i = 1; i < 5; i++) {\n                test.dad(1, 2, 3);\n            }\n\n            test.send(result);\n        \",\n    );\n    let root_module = Module::parse(source, None, &mut context).unwrap();\n\n    let promise_result = root_module.load_link_evaluate(&mut context);\n    context.run_jobs().unwrap();\n\n    // Checking if the final promise didn't return an error.\n    assert!(\n        promise_result.state().as_fulfilled().is_some(),\n        \"module didn't execute successfully! Promise: {:?}\",\n        promise_result.state()\n    );\n\n    let result = context.get_data::<ResultType>().unwrap().borrow().clone();\n\n    assert_eq!(*foo_count.borrow(), 2);\n    assert_eq!(*bar_count.borrow(), 15);\n    assert_eq!(*dad_count.borrow(), 24);\n    assert_eq!(result.try_js_into(&mut context), Ok(1u32));\n}\n\n#[test]\nfn can_throw_exception() {\n    use boa_engine::{\n        Context, IntoJsFunctionCopied, JsError, JsResult, JsValue, Module, Source, js_string,\n    };\n    use std::rc::Rc;\n\n    let loader = Rc::new(MapModuleLoader::default());\n    let mut context = Context::builder()\n        .module_loader(loader.clone())\n        .build()\n        .unwrap();\n\n    let module = vec![(\n        js_string!(\"doTheThrow\"),\n        IntoJsFunctionCopied::into_js_function_copied(\n            |message: JsValue| -> JsResult<()> { Err(JsError::from_opaque(message)) },\n            &mut context,\n        ),\n    )]\n    .into_js_module(&mut context);\n\n    loader.insert(\"test\", module);\n\n    let source = Source::from_bytes(\n        r\"\n            import * as test from 'test';\n            try {\n                test.doTheThrow('javascript');\n            } catch(e) {\n                throw 'from ' + e;\n            }\n        \",\n    );\n    let root_module = Module::parse(source, None, &mut context).unwrap();\n\n    let promise_result = root_module.load_link_evaluate(&mut context);\n    context.run_jobs().unwrap();\n\n    // Checking if the final promise didn't return an error.\n    assert_eq!(\n        promise_result.state().as_rejected(),\n        Some(&js_string!(\"from javascript\").into())\n    );\n}\n\n#[test]\nfn test_module_request_attribute_sorting() {\n    let request1 = ModuleRequest::new(\n        js_string!(\"specifier\"),\n        Box::new([\n            ImportAttribute::new(js_string!(\"key2\"), js_string!(\"val2\")),\n            ImportAttribute::new(js_string!(\"key1\"), js_string!(\"val1\")),\n        ]),\n    );\n\n    let request2 = ModuleRequest::new(\n        js_string!(\"specifier\"),\n        Box::new([\n            ImportAttribute::new(js_string!(\"key1\"), js_string!(\"val1\")),\n            ImportAttribute::new(js_string!(\"key2\"), js_string!(\"val2\")),\n        ]),\n    );\n\n    assert_eq!(request1, request2);\n    assert_eq!(request1.attributes()[0].key(), &js_string!(\"key1\"));\n    assert_eq!(request1.attributes()[1].key(), &js_string!(\"key2\"));\n}\n"
  },
  {
    "path": "core/engine/src/module/namespace.rs",
    "content": "use std::hash::BuildHasherDefault;\n\nuse indexmap::IndexSet;\nuse rustc_hash::{FxHashMap, FxHasher};\n\nuse boa_gc::{Finalize, Trace};\n\nuse crate::object::internal_methods::immutable_prototype::immutable_prototype_exotic_set_prototype_of;\nuse crate::object::internal_methods::{\n    InternalMethodPropertyContext, InternalObjectMethods, ORDINARY_INTERNAL_METHODS,\n    ordinary_define_own_property, ordinary_delete, ordinary_get, ordinary_get_own_property,\n    ordinary_has_property, ordinary_own_property_keys, ordinary_try_get,\n};\nuse crate::object::{JsData, JsPrototype};\nuse crate::property::{PropertyDescriptor, PropertyKey};\nuse crate::{Context, JsExpect, JsResult, JsString, JsValue, js_string, object::JsObject};\nuse crate::{JsNativeError, Module};\n\nuse super::{BindingName, ResolvedBinding};\n\n/// Module namespace exotic object.\n///\n/// Exposes the bindings exported by a [`Module`] to be accessed from ECMAScript code.\n#[derive(Debug, Trace, Finalize)]\npub struct ModuleNamespace {\n    module: Module,\n    #[unsafe_ignore_trace]\n    exports: IndexSet<JsString, BuildHasherDefault<FxHasher>>,\n    /// Cached binding resolutions for each export name.\n    /// Populated once during namespace creation; bindings are immutable after linking.\n    ///\n    /// SAFETY: Every `Module` inside a `ResolvedBinding` is a transitive dependency\n    /// of the parent `module` field (which IS traced). Those modules are reachable\n    /// through `SourceTextModule::loaded_modules` / `SyntheticModule`, so tracing\n    /// them again here would be redundant. Skipping the trace avoids walking the\n    /// entire hashmap on every GC cycle. This is a performance optimization:\n    /// our GC already ignores pointers that were already traced, but this avoids\n    /// a lookup to check if the pointer is alive. The logic is correct either way.\n    #[unsafe_ignore_trace]\n    resolved_bindings: FxHashMap<JsString, ResolvedBinding>,\n}\n\nimpl JsData for ModuleNamespace {\n    fn internal_methods(&self) -> &'static InternalObjectMethods {\n        static METHODS: InternalObjectMethods = InternalObjectMethods {\n            __get_prototype_of__: module_namespace_exotic_get_prototype_of,\n            __set_prototype_of__: module_namespace_exotic_set_prototype_of,\n            __is_extensible__: module_namespace_exotic_is_extensible,\n            __prevent_extensions__: module_namespace_exotic_prevent_extensions,\n            __get_own_property__: module_namespace_exotic_get_own_property,\n            __define_own_property__: module_namespace_exotic_define_own_property,\n            __has_property__: module_namespace_exotic_has_property,\n            __try_get__: module_namespace_exotic_try_get,\n            __get__: module_namespace_exotic_get,\n            __set__: module_namespace_exotic_set,\n            __delete__: module_namespace_exotic_delete,\n            __own_property_keys__: module_namespace_exotic_own_property_keys,\n            ..ORDINARY_INTERNAL_METHODS\n        };\n\n        &METHODS\n    }\n}\n\nimpl ModuleNamespace {\n    /// Abstract operation [`ModuleNamespaceCreate ( module, exports )`][spec].\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-modulenamespacecreate\n    pub(crate) fn create(module: Module, names: Vec<JsString>, context: &mut Context) -> JsObject {\n        // 1. Assert: module.[[Namespace]] is empty.\n        // ignored since this is ensured by `Module::namespace`.\n\n        // 6. Let sortedExports be a List whose elements are the elements of exports ordered as if an Array of the same values had been sorted using %Array.prototype.sort% using undefined as comparefn.\n        let mut exports = names.into_iter().collect::<IndexSet<_, _>>();\n        exports.sort();\n\n        // Pre-resolve all export bindings and cache them.\n        // After linking, ResolveExport results are stable (per spec),\n        // so we can safely cache them to avoid repeated graph traversals.\n        let mut resolved_bindings = FxHashMap::default();\n        for name in &exports {\n            if let Ok(binding) = module.resolve_export(\n                name,\n                &mut rustc_hash::FxHashSet::default(),\n                context.interner(),\n            ) {\n                resolved_bindings.insert(name.clone(), binding);\n            }\n        }\n\n        // 2. Let internalSlotsList be the internal slots listed in Table 32.\n        // 3. Let M be MakeBasicObject(internalSlotsList).\n        // 4. Set M's essential internal methods to the definitions specified in 10.4.6.\n        // 5. Set M.[[Module]] to module.\n        // 7. Set M.[[Exports]] to sortedExports.\n        // 8. Create own properties of M corresponding to the definitions in 28.3.\n\n        // 9. Set module.[[Namespace]] to M.\n        // Ignored because this is done by `Module::namespace`\n\n        // 10. Return M.\n        context.intrinsics().templates().namespace().create(\n            Self {\n                module,\n                exports,\n                resolved_bindings,\n            },\n            vec![js_string!(\"Module\").into()],\n        )\n    }\n\n    /// Gets the export names of the Module Namespace object.\n    pub(crate) const fn exports(&self) -> &IndexSet<JsString, BuildHasherDefault<FxHasher>> {\n        &self.exports\n    }\n\n    /// Gets the module associated with this namespace.\n    pub(crate) const fn module(&self) -> &Module {\n        &self.module\n    }\n\n    /// Gets a cached resolved binding for the given export name.\n    pub(crate) fn get_resolved_binding(&self, name: &JsString) -> Option<&ResolvedBinding> {\n        self.resolved_bindings.get(name)\n    }\n}\n\n/// [`[[GetPrototypeOf]] ( )`][spec].\n///\n/// [spec]: https://tc39.es/ecma262/#sec-module-namespace-exotic-objects-getprototypeof\n#[allow(clippy::unnecessary_wraps)]\nfn module_namespace_exotic_get_prototype_of(\n    _: &JsObject,\n    _: &mut Context,\n) -> JsResult<JsPrototype> {\n    // 1. Return null.\n    Ok(None)\n}\n\n/// [`[[SetPrototypeOf]] ( V )`][spec].\n///\n/// [spec]: https://tc39.es/ecma262/#sec-module-namespace-exotic-objects-setprototypeof-v\n#[allow(clippy::unnecessary_wraps)]\nfn module_namespace_exotic_set_prototype_of(\n    obj: &JsObject,\n    val: JsPrototype,\n    context: &mut Context,\n) -> JsResult<bool> {\n    // 1. Return ! SetImmutablePrototype(O, V).\n    Ok(\n        immutable_prototype_exotic_set_prototype_of(obj, val, context)\n            .js_expect(\"this must not fail per the spec\")?,\n    )\n}\n\n/// [`[[IsExtensible]] ( )`][spec].\n///\n/// [spec]: https://tc39.es/ecma262/#sec-module-namespace-exotic-objects-isextensible\n#[allow(clippy::unnecessary_wraps)]\nfn module_namespace_exotic_is_extensible(_: &JsObject, _: &mut Context) -> JsResult<bool> {\n    // 1. Return false.\n    Ok(false)\n}\n\n/// [`[[PreventExtensions]] ( )`][spec].\n///\n/// [spec]: https://tc39.es/ecma262/#sec-module-namespace-exotic-objects-preventextensions\n#[allow(clippy::unnecessary_wraps)]\nfn module_namespace_exotic_prevent_extensions(_: &JsObject, _: &mut Context) -> JsResult<bool> {\n    Ok(true)\n}\n\n/// [`[[GetOwnProperty]] ( P )`][spec]\n///\n/// [spec]: https://tc39.es/ecma262/#sec-module-namespace-exotic-objects-getownproperty-p\nfn module_namespace_exotic_get_own_property(\n    obj: &JsObject,\n    key: &PropertyKey,\n    context: &mut InternalMethodPropertyContext<'_>,\n) -> JsResult<Option<PropertyDescriptor>> {\n    // 1. If P is a Symbol, return OrdinaryGetOwnProperty(O, P).\n    let key = match key {\n        PropertyKey::Symbol(_) => return ordinary_get_own_property(obj, key, context),\n        PropertyKey::Index(idx) => js_string!(format!(\"{}\", idx.get())),\n        PropertyKey::String(s) => s.clone(),\n    };\n\n    {\n        let obj = obj\n            .downcast_ref::<ModuleNamespace>()\n            .js_expect(\"internal method can only be called on module namespace objects\")?;\n        // 2. Let exports be O.[[Exports]].\n        let exports = obj.exports();\n\n        // 3. If exports does not contain P, return undefined.\n        if !exports.contains(&key) {\n            return Ok(None);\n        }\n    }\n\n    // 4. Let value be ? O.[[Get]](P, O).\n    let value = obj.get(key, context)?;\n\n    // 5. Return PropertyDescriptor { [[Value]]: value, [[Writable]]: true, [[Enumerable]]: true, [[Configurable]]: false }.\n    Ok(Some(\n        PropertyDescriptor::builder()\n            .value(value)\n            .writable(true)\n            .enumerable(true)\n            .configurable(false)\n            .build(),\n    ))\n}\n\n/// [`[[DefineOwnProperty]] ( P, Desc )`][spec]\n///\n/// [spec]: https://tc39.es/ecma262/#sec-module-namespace-exotic-objects-defineownproperty-p-desc\nfn module_namespace_exotic_define_own_property(\n    obj: &JsObject,\n    key: &PropertyKey,\n    desc: PropertyDescriptor,\n    context: &mut InternalMethodPropertyContext<'_>,\n) -> JsResult<bool> {\n    // 1. If P is a Symbol, return ! OrdinaryDefineOwnProperty(O, P, Desc).\n    if let PropertyKey::Symbol(_) = key {\n        return ordinary_define_own_property(obj, key, desc, context);\n    }\n\n    // 2. Let current be ? O.[[GetOwnProperty]](P).\n    let Some(current) = obj.__get_own_property__(key, context)? else {\n        // 3. If current is undefined, return false.\n        return Ok(false);\n    };\n\n    // 4. If Desc has a [[Configurable]] field and Desc.[[Configurable]] is true, return false.\n    // 5. If Desc has an [[Enumerable]] field and Desc.[[Enumerable]] is false, return false.\n    // 6. If IsAccessorDescriptor(Desc) is true, return false.\n    // 7. If Desc has a [[Writable]] field and Desc.[[Writable]] is false, return false.\n    if desc.configurable() == Some(true)\n        || desc.enumerable() == Some(false)\n        || desc.is_accessor_descriptor()\n        || desc.writable() == Some(false)\n    {\n        return Ok(false);\n    }\n\n    // 8. If Desc has a [[Value]] field, return SameValue(Desc.[[Value]], current.[[Value]]).\n    // 9. Return true.\n    Ok(desc.value().is_none_or(|v| v == current.expect_value()))\n}\n\n/// [`[[HasProperty]] ( P )`][spec]\n///\n/// [spec]: https://tc39.es/ecma262/#sec-module-namespace-exotic-objects-hasproperty-p\nfn module_namespace_exotic_has_property(\n    obj: &JsObject,\n    key: &PropertyKey,\n    context: &mut InternalMethodPropertyContext<'_>,\n) -> JsResult<bool> {\n    // 1. If P is a Symbol, return ! OrdinaryHasProperty(O, P).\n    let key = match key {\n        PropertyKey::Symbol(_) => return ordinary_has_property(obj, key, context),\n        PropertyKey::Index(idx) => js_string!(format!(\"{}\", idx.get())),\n        PropertyKey::String(s) => s.clone(),\n    };\n\n    let obj = obj\n        .downcast_ref::<ModuleNamespace>()\n        .js_expect(\"internal method can only be called on module namespace objects\")?;\n\n    // 2. Let exports be O.[[Exports]].\n    let exports = obj.exports();\n\n    // 3. If exports contains P, return true.\n    // 4. Return false.\n    Ok(exports.contains(&key))\n}\n\n/// Internal optimization method for `Module Namespace` exotic objects.\n///\n/// This method combines the internal methods `[[HasProperty]]` and `[[Get]]`.\n///\n/// More information:\n///  - [ECMAScript reference HasProperty][spec0]\n///  - [ECMAScript reference Get][spec1]\n///\n/// [spec0]: https://tc39.es/ecma262/#sec-module-namespace-exotic-objects-hasproperty-p\n/// [spec1]: https://tc39.es/ecma262/#sec-module-namespace-exotic-objects-get-p-receiver\nfn module_namespace_exotic_try_get(\n    obj: &JsObject,\n    key: &PropertyKey,\n    receiver: JsValue,\n    context: &mut InternalMethodPropertyContext<'_>,\n) -> JsResult<Option<JsValue>> {\n    // 1. If P is a Symbol, then\n    //     a. Return ! OrdinaryGet(O, P, Receiver).\n    let key = match key {\n        PropertyKey::Symbol(_) => return ordinary_try_get(obj, key, receiver, context),\n        PropertyKey::Index(idx) => js_string!(format!(\"{}\", idx.get())),\n        PropertyKey::String(s) => s.clone(),\n    };\n\n    let obj = obj\n        .downcast_ref::<ModuleNamespace>()\n        .js_expect(\"internal method can only be called on module namespace objects\")?;\n\n    // 2. Let exports be O.[[Exports]].\n    let exports = obj.exports();\n\n    // 3. If exports does not contain P, return undefined.\n    let Some(export_name) = exports.get(&key).cloned() else {\n        return Ok(None);\n    };\n\n    // 4. Let m be O.[[Module]].\n    let module = obj.module();\n\n    // 5. Let binding be m.ResolveExport(P).\n    // 6. Assert: binding is a ResolvedBinding Record.\n    // Use the pre-resolved cache when available; fall back to a fresh\n    // ResolveExport call on cache miss for robustness.\n    let fallback;\n    let binding = if let Some(b) = obj.get_resolved_binding(&export_name) {\n        b\n    } else {\n        fallback = module\n            .resolve_export(\n                &export_name,\n                &mut rustc_hash::FxHashSet::default(),\n                context.interner(),\n            )\n            .expect(\"6. Assert: binding is a ResolvedBinding Record.\");\n        &fallback\n    };\n\n    // 7. Let targetModule be binding.[[Module]].\n    // 8. Assert: targetModule is not undefined.\n    let target_module = binding.module();\n\n    if let BindingName::Name(name) = binding.binding_name() {\n        // 10. Let targetEnv be targetModule.[[Environment]].\n        let Some(env) = target_module.environment() else {\n            // 11. If targetEnv is empty, throw a ReferenceError exception.\n            let import = export_name.to_std_string_escaped();\n            return Err(JsNativeError::reference()\n                .with_message(format!(\n                    \"cannot get import `{import}` from an uninitialized module\"\n                ))\n                .into());\n        };\n\n        let locator = env\n            .kind()\n            .as_module()\n            .js_expect(\"must be module environment\")?\n            .compile()\n            .get_binding(name)\n            .js_expect(\"checked before that the name was reachable\")?;\n\n        // 12. Return ? targetEnv.GetBindingValue(binding.[[BindingName]], true).\n        env.get(locator.binding_index()).map(Some).ok_or_else(|| {\n            let import = export_name.to_std_string_escaped();\n\n            JsNativeError::reference()\n                .with_message(format!(\"cannot get uninitialized import `{import}`\"))\n                .into()\n        })\n    } else {\n        // 9. If binding.[[BindingName]] is namespace, then\n        //     a. Return GetModuleNamespace(targetModule).\n        Ok(Some(target_module.namespace(context).into()))\n    }\n}\n\n/// [`[[Get]] ( P, Receiver )`][spec]\n///\n/// [spec]: https://tc39.es/ecma262/#sec-module-namespace-exotic-objects-get-p-receiver\nfn module_namespace_exotic_get(\n    obj: &JsObject,\n    key: &PropertyKey,\n    receiver: JsValue,\n    context: &mut InternalMethodPropertyContext<'_>,\n) -> JsResult<JsValue> {\n    // 1. If P is a Symbol, then\n    //     a. Return ! OrdinaryGet(O, P, Receiver).\n    let key = match key {\n        PropertyKey::Symbol(_) => return ordinary_get(obj, key, receiver, context),\n        PropertyKey::Index(idx) => js_string!(format!(\"{}\", idx.get())),\n        PropertyKey::String(s) => s.clone(),\n    };\n\n    let obj = obj\n        .downcast_ref::<ModuleNamespace>()\n        .js_expect(\"internal method can only be called on module namespace objects\")?;\n\n    // 2. Let exports be O.[[Exports]].\n    let exports = obj.exports();\n    // 3. If exports does not contain P, return undefined.\n    let Some(export_name) = exports.get(&key).cloned() else {\n        return Ok(JsValue::undefined());\n    };\n\n    // 4. Let m be O.[[Module]].\n    let module = obj.module();\n\n    // 5. Let binding be m.ResolveExport(P).\n    // 6. Assert: binding is a ResolvedBinding Record.\n    // Use the pre-resolved cache when available; fall back to a fresh\n    // ResolveExport call on cache miss for robustness.\n    let fallback;\n    let binding = if let Some(b) = obj.get_resolved_binding(&export_name) {\n        b\n    } else {\n        fallback = module\n            .resolve_export(\n                &export_name,\n                &mut rustc_hash::FxHashSet::default(),\n                context.interner(),\n            )\n            .expect(\"6. Assert: binding is a ResolvedBinding Record.\");\n        &fallback\n    };\n\n    // 7. Let targetModule be binding.[[Module]].\n    // 8. Assert: targetModule is not undefined.\n    let target_module = binding.module();\n\n    if let BindingName::Name(name) = binding.binding_name() {\n        // 10. Let targetEnv be targetModule.[[Environment]].\n        let Some(env) = target_module.environment() else {\n            // 11. If targetEnv is empty, throw a ReferenceError exception.\n            let import = export_name.to_std_string_escaped();\n            return Err(JsNativeError::reference()\n                .with_message(format!(\n                    \"cannot get import `{import}` from an uninitialized module\"\n                ))\n                .into());\n        };\n\n        let locator = env\n            .kind()\n            .as_module()\n            .js_expect(\"must be module environment\")?\n            .compile()\n            .get_binding(name)\n            .js_expect(\"checked before that the name was reachable\")?;\n\n        // 12. Return ? targetEnv.GetBindingValue(binding.[[BindingName]], true).\n        env.get(locator.binding_index()).ok_or_else(|| {\n            let import = export_name.to_std_string_escaped();\n\n            JsNativeError::reference()\n                .with_message(format!(\"cannot get uninitialized import `{import}`\"))\n                .into()\n        })\n    } else {\n        // 9. If binding.[[BindingName]] is namespace, then\n        //     a. Return GetModuleNamespace(targetModule).\n        Ok(target_module.namespace(context).into())\n    }\n}\n\n/// [`[[Set]] ( P, V, Receiver )`][spec].\n///\n/// [spec]: https://tc39.es/ecma262/#sec-module-namespace-exotic-objects-set-p-v-receiver\n#[allow(clippy::needless_pass_by_value, clippy::unnecessary_wraps)]\nfn module_namespace_exotic_set(\n    _obj: &JsObject,\n    _key: PropertyKey,\n    _value: JsValue,\n    _receiver: JsValue,\n    _context: &mut InternalMethodPropertyContext<'_>,\n) -> JsResult<bool> {\n    // 1. Return false.\n    Ok(false)\n}\n\n/// [`[[Delete]] ( P )`][spec].\n///\n/// [spec]: https://tc39.es/ecma262/#sec-module-namespace-exotic-objects-delete-p\nfn module_namespace_exotic_delete(\n    obj: &JsObject,\n    key: &PropertyKey,\n    context: &mut InternalMethodPropertyContext<'_>,\n) -> JsResult<bool> {\n    // 1. If P is a Symbol, then\n    //     a. Return ! OrdinaryDelete(O, P).\n    let key = match key {\n        PropertyKey::Symbol(_) => return ordinary_delete(obj, key, context),\n        PropertyKey::Index(idx) => js_string!(format!(\"{}\", idx.get())),\n        PropertyKey::String(s) => s.clone(),\n    };\n\n    let obj = obj\n        .downcast_ref::<ModuleNamespace>()\n        .js_expect(\"internal method can only be called on module namespace objects\")?;\n\n    // 2. Let exports be O.[[Exports]].\n    let exports = obj.exports();\n\n    // 3. If exports contains P, return false.\n    // 4. Return true.\n    Ok(!exports.contains(&key))\n}\n\n/// [`[[OwnPropertyKeys]] ( )`][spec].\n///\n/// [spec]: https://tc39.es/ecma262/#sec-module-namespace-exotic-objects-ownpropertykeys\nfn module_namespace_exotic_own_property_keys(\n    obj: &JsObject,\n    context: &mut Context,\n) -> JsResult<Vec<PropertyKey>> {\n    // 2. Let symbolKeys be OrdinaryOwnPropertyKeys(O).\n    let symbol_keys = ordinary_own_property_keys(obj, context)?;\n\n    let obj = obj\n        .downcast_ref::<ModuleNamespace>()\n        .js_expect(\"internal method can only be called on module namespace objects\")?;\n\n    // 1. Let exports be O.[[Exports]].\n    let exports = obj.exports();\n\n    // 3. Return the list-concatenation of exports and symbolKeys.\n    Ok(exports\n        .iter()\n        .map(|k| PropertyKey::String(k.clone()))\n        .chain(symbol_keys)\n        .collect())\n}\n"
  },
  {
    "path": "core/engine/src/module/source.rs",
    "content": "use std::{\n    cell::{Cell, RefCell},\n    collections::HashSet,\n    hash::BuildHasherDefault,\n    mem::MaybeUninit,\n    path::PathBuf,\n    rc::Rc,\n};\n\nuse boa_ast::{\n    declaration::{\n        ExportEntry, ImportEntry, ImportName, IndirectExportEntry, LocalExportEntry,\n        ReExportImportName,\n    },\n    operations::{\n        ContainsSymbol, LexicallyScopedDeclaration, bound_names, contains,\n        lexically_scoped_declarations, var_scoped_declarations,\n    },\n    scope::BindingLocator,\n};\nuse boa_gc::{Finalize, Gc, GcRefCell, Trace};\nuse boa_interner::Interner;\nuse boa_macros::js_str;\nuse dynify::Dynify;\nuse indexmap::IndexSet;\nuse rustc_hash::{FxHashMap, FxHashSet, FxHasher};\n\nuse crate::{\n    Context, JsArgs, JsError, JsExpect, JsNativeError, JsObject, JsResult, JsString, JsValue,\n    NativeFunction, SpannedSourceText,\n    builtins::{Promise, promise::PromiseCapability},\n    bytecompiler::{BindingAccessOpcode, ByteCompiler, FunctionSpec, ToJsString},\n    environments::{DeclarativeEnvironment, EnvironmentStack},\n    job::NativeAsyncJob,\n    js_string,\n    module::ModuleKind,\n    object::{FunctionObjectBuilder, JsPromise},\n    realm::Realm,\n    vm::{\n        ActiveRunnable, CallFrame, CallFrameFlags, CodeBlock, CompletionRecord,\n        create_function_object_fast,\n    },\n};\n\nuse super::{\n    BindingName, GraphLoadingState, Module, Referrer, ResolveExportError, ResolvedBinding,\n    SourceText,\n};\n\n/// Information for the [**Depth-first search**] algorithm used in the\n/// [`Module::link`] and [`Module::evaluate`] methods.\n#[derive(Clone, Copy, Debug, Trace, Finalize)]\n#[boa_gc(empty_trace)]\npub(super) struct DfsInfo {\n    dfs_index: usize,\n    dfs_ancestor_index: usize,\n}\n\n/// Current status of a [`SourceTextModule`].\n///\n/// Roughly corresponds to the `[[Status]]` field of [**Cyclic Module Records**][cyclic],\n/// but with a state machine-like design for better correctness.\n///\n/// [cyclic]: https://tc39.es/ecma262/#table-cyclic-module-fields\n#[derive(Debug, Trace, Finalize)]\n#[boa_gc(unsafe_no_drop)]\nenum ModuleStatus {\n    Unlinked {\n        #[unsafe_ignore_trace]\n        source: boa_ast::Module,\n        source_text: SourceText,\n    },\n    Linking {\n        #[unsafe_ignore_trace]\n        source: boa_ast::Module,\n        source_text: SourceText,\n        info: DfsInfo,\n    },\n    PreLinked {\n        environment: Gc<DeclarativeEnvironment>,\n        context: SourceTextContext,\n        info: DfsInfo,\n    },\n    Linked {\n        environment: Gc<DeclarativeEnvironment>,\n        context: SourceTextContext,\n        info: DfsInfo,\n    },\n    Evaluating {\n        environment: Gc<DeclarativeEnvironment>,\n        context: SourceTextContext,\n        top_level_capability: Option<PromiseCapability>,\n        cycle_root: Module,\n        info: DfsInfo,\n        async_eval_index: Option<usize>,\n    },\n    EvaluatingAsync {\n        environment: Gc<DeclarativeEnvironment>,\n        context: SourceTextContext,\n        top_level_capability: Option<PromiseCapability>,\n        cycle_root: Module,\n        async_eval_index: usize,\n        pending_async_dependencies: usize,\n    },\n    Evaluated {\n        environment: Gc<DeclarativeEnvironment>,\n        top_level_capability: Option<PromiseCapability>,\n        cycle_root: Module,\n        error: Option<JsError>,\n    },\n}\n\nimpl ModuleStatus {\n    /// Transition from one state to another, taking the current state by value to move data\n    /// between states.\n    fn transition<F>(&mut self, f: F)\n    where\n        F: FnOnce(Self) -> Self,\n    {\n        *self = f(std::mem::replace(\n            self,\n            ModuleStatus::Unlinked {\n                source: boa_ast::Module::default(),\n                source_text: SourceText::default(),\n            },\n        ));\n    }\n\n    /// Gets the current index info of the module within the dependency graph, or `None` if the\n    /// module is not in a state executing the dfs algorithm.\n    const fn dfs_info(&self) -> Option<&DfsInfo> {\n        match self {\n            Self::Unlinked { .. } | Self::EvaluatingAsync { .. } | Self::Evaluated { .. } => None,\n            Self::Linking { info, .. }\n            | Self::PreLinked { info, .. }\n            | Self::Linked { info, .. }\n            | Self::Evaluating { info, .. } => Some(info),\n        }\n    }\n\n    /// Gets a mutable reference to the current index info of the module within the dependency graph,\n    /// or `None` if the module is not in a state executing the dfs algorithm.\n    fn dfs_info_mut(&mut self) -> Option<&mut DfsInfo> {\n        match self {\n            Self::Unlinked { .. } | Self::EvaluatingAsync { .. } | Self::Evaluated { .. } => None,\n            Self::Linking { info, .. }\n            | Self::PreLinked { info, .. }\n            | Self::Linked { info, .. }\n            | Self::Evaluating { info, .. } => Some(info),\n        }\n    }\n\n    /// If this module is the top module being evaluated and is in the evaluating state, gets its top\n    /// level capability.\n    const fn top_level_capability(&self) -> Option<&PromiseCapability> {\n        match &self {\n            Self::Unlinked { .. }\n            | Self::Linking { .. }\n            | Self::PreLinked { .. }\n            | Self::Linked { .. } => None,\n            Self::Evaluating {\n                top_level_capability,\n                ..\n            }\n            | Self::EvaluatingAsync {\n                top_level_capability,\n                ..\n            }\n            | Self::Evaluated {\n                top_level_capability,\n                ..\n            } => top_level_capability.as_ref(),\n        }\n    }\n\n    /// If this module is in the evaluated state, gets its `error` field.\n    const fn evaluation_error(&self) -> Option<&JsError> {\n        match &self {\n            Self::Evaluated { error, .. } => error.as_ref(),\n            _ => None,\n        }\n    }\n\n    /// If this module is in the evaluating state, gets its cycle root.\n    const fn cycle_root(&self) -> Option<&Module> {\n        match &self {\n            Self::Evaluating { cycle_root, .. }\n            | Self::EvaluatingAsync { cycle_root, .. }\n            | Self::Evaluated { cycle_root, .. } => Some(cycle_root),\n            _ => None,\n        }\n    }\n\n    /// Gets the declarative environment from the module status.\n    fn environment(&self) -> Option<Gc<DeclarativeEnvironment>> {\n        match self {\n            ModuleStatus::Unlinked { .. } | ModuleStatus::Linking { .. } => None,\n            ModuleStatus::PreLinked { environment, .. }\n            | ModuleStatus::Linked { environment, .. }\n            | ModuleStatus::Evaluating { environment, .. }\n            | ModuleStatus::EvaluatingAsync { environment, .. }\n            | ModuleStatus::Evaluated { environment, .. } => Some(environment.clone()),\n        }\n    }\n\n    /// If this module is in the unlinked or linking states, gets its source.\n    fn source(&self) -> Option<(&boa_ast::Module, &SourceText)> {\n        match self {\n            ModuleStatus::Unlinked {\n                source,\n                source_text,\n            }\n            | ModuleStatus::Linking {\n                source,\n                source_text,\n                ..\n            } => Some((source, source_text)),\n            _ => None,\n        }\n    }\n}\n\n/// The execution context of a [`SourceTextModule`].\n///\n/// Stores the required context data that needs to be in place before executing the\n/// inner code of the module.\n#[derive(Clone, Trace, Finalize)]\n#[boa_gc(unsafe_no_drop)]\nstruct SourceTextContext {\n    codeblock: Gc<CodeBlock>,\n    environments: EnvironmentStack,\n    realm: Realm,\n}\n\nimpl std::fmt::Debug for SourceTextContext {\n    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {\n        f.debug_struct(\"SourceTextContext\")\n            .field(\"codeblock\", &self.codeblock)\n            .field(\"environments\", &self.environments)\n            .field(\"realm\", &self.realm.addr())\n            .finish()\n    }\n}\n\n/// ECMAScript's [**Source Text Module Records**][spec].\n///\n/// [spec]: https://tc39.es/ecma262/#sec-source-text-module-records\n#[derive(Trace, Finalize)]\npub(crate) struct SourceTextModule {\n    status: GcRefCell<ModuleStatus>,\n    loaded_modules: GcRefCell<FxHashMap<super::ModuleRequest, Module>>,\n    async_parent_modules: GcRefCell<Vec<Module>>,\n    import_meta: GcRefCell<Option<JsObject>>,\n    #[unsafe_ignore_trace]\n    code: ModuleCode,\n}\n\nimpl std::fmt::Debug for SourceTextModule {\n    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {\n        f.debug_struct(\"SourceTextModule\")\n            .field(\"status\", &self.status)\n            .field(\"loaded_modules\", &self.loaded_modules)\n            .field(\"async_parent_modules\", &self.async_parent_modules)\n            .field(\"import_meta\", &self.import_meta)\n            .finish_non_exhaustive()\n    }\n}\n\n#[derive(Debug)]\nstruct ModuleCode {\n    has_tla: bool,\n    requested_modules: IndexSet<super::ModuleRequest, BuildHasherDefault<FxHasher>>,\n    path: Option<PathBuf>,\n    import_entries: Vec<ImportEntry>,\n    local_export_entries: Vec<LocalExportEntry>,\n    indirect_export_entries: Vec<IndirectExportEntry>,\n    star_export_entries: Vec<super::ModuleRequest>,\n}\n\nstruct ModuleRequestsVisitor<'a> {\n    interner: &'a Interner,\n    requests: IndexSet<super::ModuleRequest, BuildHasherDefault<FxHasher>>,\n}\n\nimpl<'ast> boa_ast::visitor::Visitor<'ast> for ModuleRequestsVisitor<'_> {\n    type BreakTy = std::convert::Infallible;\n\n    fn visit_import_declaration(\n        &mut self,\n        node: &'ast boa_ast::declaration::ImportDeclaration,\n    ) -> std::ops::ControlFlow<Self::BreakTy> {\n        let specifier = node.specifier().sym().to_js_string(self.interner);\n        self.requests.insert(super::ModuleRequest::from_ast(\n            specifier,\n            node.attributes(),\n            self.interner,\n        ));\n        std::ops::ControlFlow::Continue(())\n    }\n\n    fn visit_export_declaration(\n        &mut self,\n        node: &'ast boa_ast::declaration::ExportDeclaration,\n    ) -> std::ops::ControlFlow<Self::BreakTy> {\n        if let boa_ast::declaration::ExportDeclaration::ReExport {\n            specifier,\n            attributes,\n            ..\n        } = node\n        {\n            let spec = specifier.sym().to_js_string(self.interner);\n            self.requests.insert(super::ModuleRequest::from_ast(\n                spec,\n                attributes,\n                self.interner,\n            ));\n        }\n        std::ops::ControlFlow::Continue(())\n    }\n\n    fn visit_statement_list_item(\n        &mut self,\n        _: &'ast boa_ast::StatementListItem,\n    ) -> std::ops::ControlFlow<Self::BreakTy> {\n        std::ops::ControlFlow::Continue(())\n    }\n}\n\nimpl SourceTextModule {\n    /// Creates a new `SourceTextModule` from a parsed `ModuleSource`.\n    ///\n    /// Contains part of the abstract operation [`ParseModule`][parse].\n    ///\n    /// [parse]: https://tc39.es/ecma262/#sec-parsemodule\n    pub(super) fn new(\n        source: boa_ast::Module,\n        interner: &Interner,\n        source_text: SourceText,\n        path: Option<PathBuf>,\n    ) -> Self {\n        // 3. Let requestedModules be the ModuleRequests of body.\n        let requested_modules = {\n            use boa_ast::visitor::Visitor;\n\n            let mut visitor = ModuleRequestsVisitor {\n                interner,\n                requests: IndexSet::default(),\n            };\n            let _ = visitor.visit_module(&source);\n            visitor.requests\n        };\n        // 4. Let importEntries be ImportEntries of body.\n        let import_entries = source.items().import_entries();\n\n        // 5. Let importedBoundNames be ImportedLocalNames(importEntries).\n        // Can be ignored because this is just a simple `Iter::map`\n\n        // 6. Let indirectExportEntries be a new empty List.\n        let mut indirect_export_entries = Vec::new();\n        // 7. Let localExportEntries be a new empty List.\n        let mut local_export_entries = Vec::new();\n        // 8. Let starExportEntries be a new empty List.\n        let mut star_export_entries = Vec::new();\n\n        // 10. For each ExportEntry Record ee of exportEntries, do\n        for ee in source.items().export_entries() {\n            match ee {\n                // a. If ee.[[ModuleRequest]] is null, then\n                ExportEntry::Ordinary(entry) => {\n                    // ii. Else,\n                    //     1. Let ie be the element of importEntries whose [[LocalName]] is ee.[[LocalName]].\n                    if let Some((module, import, attrs)) =\n                        import_entries.iter().find_map(|ie| match ie.import_name() {\n                            ImportName::Name(name) if ie.local_name() == entry.local_name() => {\n                                Some((ie.module_request(), name, ie.attributes()))\n                            }\n                            _ => None,\n                        })\n                    {\n                        // 3. Else,\n                        //    a. NOTE: This is a re-export of a single name.\n                        //    b. Append the ExportEntry Record { [[ModuleRequest]]: ie.[[ModuleRequest]],\n                        //       [[ImportName]]: ie.[[ImportName]], [[LocalName]]: null,\n                        //       [[ExportName]]: ee.[[ExportName]] } to indirectExportEntries.\n                        indirect_export_entries.push(IndirectExportEntry::new(\n                            module,\n                            ReExportImportName::Name(import),\n                            entry.export_name(),\n                            attrs.to_vec().into_boxed_slice(),\n                        ));\n                    } else {\n                        // i. If importedBoundNames does not contain ee.[[LocalName]], then\n                        //    1. Append ee to localExportEntries.\n\n                        //    2. If ie.[[ImportName]] is namespace-object, then\n                        //       a. NOTE: This is a re-export of an imported module namespace object.\n                        //       b. Append ee to localExportEntries.\n                        local_export_entries.push(entry);\n                    }\n                }\n                // b. Else if ee.[[ImportName]] is all-but-default, then\n                ExportEntry::StarReExport {\n                    module_request,\n                    attributes,\n                } => {\n                    // i. Assert: ee.[[ExportName]] is null.\n                    // ii. Append ee to starExportEntries.\n                    let spec = module_request.to_js_string(interner);\n                    star_export_entries.push(super::ModuleRequest::from_ast(\n                        spec,\n                        &attributes,\n                        interner,\n                    ));\n                }\n                // c. Else,\n                //    i. Append ee to indirectExportEntries.\n                ExportEntry::ReExport(entry) => indirect_export_entries.push(entry),\n            }\n        }\n\n        // 11. Let async be body Contains await.\n        let has_tla = contains(&source, ContainsSymbol::AwaitExpression);\n\n        // 12. Return Source Text Module Record {\n        //     [[Realm]]: realm, [[Environment]]: empty, [[Namespace]]: empty, [[CycleRoot]]: empty,\n        //     [[HasTLA]]: async, [[AsyncEvaluation]]: false, [[TopLevelCapability]]: empty,\n        //     [[AsyncParentModules]]: « », [[PendingAsyncDependencies]]: empty,\n        //     [[Status]]: new, [[EvaluationError]]: empty, [[HostDefined]]: hostDefined,\n        //     [[ECMAScriptCode]]: body, [[Context]]: empty, [[ImportMeta]]: empty,\n        //     [[RequestedModules]]: requestedModules, [[LoadedModules]]: « »,\n        //     [[ImportEntries]]: importEntries, [[LocalExportEntries]]: localExportEntries,\n        //     [[IndirectExportEntries]]: indirectExportEntries,\n        //     [[StarExportEntries]]: starExportEntries,\n        //     [[DFSIndex]]: empty, [[DFSAncestorIndex]]: empty\n        // }.\n        // Most of this can be ignored, since `Status` takes care of the remaining state.\n        Self {\n            status: GcRefCell::new(ModuleStatus::Unlinked {\n                source,\n                source_text,\n            }),\n            loaded_modules: GcRefCell::default(),\n            async_parent_modules: GcRefCell::default(),\n            import_meta: GcRefCell::default(),\n            code: ModuleCode {\n                has_tla,\n                requested_modules,\n                path,\n                import_entries,\n                local_export_entries,\n                indirect_export_entries,\n                star_export_entries,\n            },\n        }\n    }\n\n    /// Abstract operation [`InnerModuleLoading`][spec].\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-InnerModuleLoading\n    pub(super) fn inner_load(\n        &self,\n        module_self: &Module,\n        state: &Rc<GraphLoadingState>,\n        context: &mut Context,\n    ) {\n        /// Loads the module of an import.\n        ///\n        /// This combines the operations:\n        /// - [`HostLoadImportedModule(module, required, state.[[HostDefined]], state)`][load]\n        /// - [`FinishLoadingImportedModule ( referrer, specifier, payload, result )`][finish]\n        /// - [`ContinueModuleLoading ( state, moduleCompletion )`][continue]\n        ///\n        /// [load]: https://tc39.es/ecma262/#sec-HostLoadImportedModule\n        /// [finish]: https://tc39.es/ecma262/#sec-FinishLoadingImportedModule\n        /// [continue]: https://tc39.es/ecma262/#sec-ContinueModuleLoading\n        async fn finish_loading_imported_module(\n            request: super::ModuleRequest,\n            src: Module,\n            state: Rc<GraphLoadingState>,\n            context: &RefCell<&mut Context>,\n        ) -> JsResult<()> {\n            let loader = context.borrow().module_loader();\n            let fut = loader.load_imported_module(\n                Referrer::Module(src.clone()),\n                request.clone(),\n                context,\n            );\n            let mut stack = [MaybeUninit::<u8>::uninit(); 16];\n            let mut heap = Vec::<MaybeUninit<u8>>::new();\n            let completion = fut.init2(&mut stack, &mut heap).await;\n\n            // FinishLoadingImportedModule ( referrer, specifier, payload, result )\n            // https://tc39.es/ecma262/#sec-FinishLoadingImportedModule\n\n            // 1. If result is a normal completion, then\n            if let Ok(loaded) = &completion {\n                let ModuleKind::SourceText(src) = src.kind() else {\n                    unreachable!(\"captured src must be a source text module\");\n                };\n                // a. If referrer.[[LoadedModules]] contains a Record whose [[Specifier]] is specifier, then\n                // b. Else,\n                //    i. Append the Record { [[Specifier]]: specifier, [[Module]]: result.[[Value]] } to referrer.[[LoadedModules]].\n                let mut loaded_modules = src.loaded_modules.borrow_mut();\n                let entry = loaded_modules\n                    .entry(request)\n                    .or_insert_with(|| loaded.clone());\n\n                //    i. Assert: That Record's [[Module]] is result.[[Value]].\n                assert_eq!(entry, loaded);\n            }\n\n            // 2. If payload is a GraphLoadingState Record, then\n            //    a. Perform ContinueModuleLoading(payload, result).\n\n            // Abstract operation `ContinueModuleLoading ( state, moduleCompletion )`.\n            //\n            // https://tc39.es/ecma262/#sec-ContinueModuleLoading\n\n            // 1. If state.[[IsLoading]] is false, return unused.\n            if !state.loading.get() {\n                return Ok(());\n            }\n\n            // 2. If moduleCompletion is a normal completion, then\n            match completion {\n                Ok(m) => {\n                    // a. Perform InnerModuleLoading(state, moduleCompletion.[[Value]]).\n                    m.inner_load(&state, &mut context.borrow_mut());\n                }\n                // 3. Else,\n                Err(err) => {\n                    // a. Set state.[[IsLoading]] to false.\n                    state.loading.set(false);\n\n                    let err = err.into_opaque(&mut context.borrow_mut())?;\n\n                    // b. Perform ! Call(state.[[PromiseCapability]].[[Reject]], undefined, « moduleCompletion.[[Value]] »).\n                    state\n                        .capability\n                        .reject()\n                        .call(&JsValue::undefined(), &[err], &mut context.borrow_mut())\n                        .js_expect(\"cannot fail for the default reject function\")?;\n                }\n            }\n\n            // 4. Return unused.\n            Ok(())\n        }\n\n        // 2. If module is a Cyclic Module Record, module.[[Status]] is new, and state.[[Visited]] does not contain\n        //    module, then\n        // a. Append module to state.[[Visited]].\n        if matches!(&*self.status.borrow(), ModuleStatus::Unlinked { .. })\n            && state.visited.borrow_mut().insert(module_self.clone())\n        {\n            // b. Let requestedModulesCount be the number of elements in module.[[RequestedModules]].\n            let requested = &self.code.requested_modules;\n            // c. Set state.[[PendingModulesCount]] to state.[[PendingModulesCount]] + requestedModulesCount.\n            state\n                .pending_modules\n                .set(state.pending_modules.get() + requested.len());\n            // d. For each String required of module.[[RequestedModules]], do\n            for required in requested {\n                // i. If module.[[LoadedModules]] contains a Record whose [[Specifier]] is required, then\n                let loaded = self.loaded_modules.borrow().get(required).cloned();\n                if let Some(loaded) = loaded {\n                    // 1. Let record be that Record.\n                    // 2. Perform InnerModuleLoading(state, record.[[Module]]).\n                    loaded.inner_load(state, context);\n                } else {\n                    //    ii. Else,\n                    //       1. Perform HostLoadImportedModule(module, required, state.[[HostDefined]], state).\n                    //       2. NOTE: HostLoadImportedModule will call FinishLoadingImportedModule, which re-enters\n                    //          the graph loading process through ContinueModuleLoading.\n                    let request = required.clone();\n                    let src = module_self.clone();\n                    let state = state.clone();\n                    let async_job = NativeAsyncJob::with_realm(\n                        async move |context| {\n                            finish_loading_imported_module(request, src, state, context).await?;\n                            Ok(JsValue::undefined())\n                        },\n                        context.realm().clone(),\n                    );\n                    context.enqueue_job(async_job.into());\n                }\n                // iii. If state.[[IsLoading]] is false, return unused.\n                if !state.loading.get() {\n                    return;\n                }\n            }\n        }\n    }\n\n    /// Concrete method [`GetExportedNames ( [ exportStarSet ] )`][spec].\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-getexportednames\n    pub(super) fn get_exported_names(\n        &self,\n        module_self: &Module,\n        export_star_set: &mut Vec<Module>,\n        interner: &Interner,\n    ) -> FxHashSet<JsString> {\n        // 1. Assert: module.[[Status]] is not new.\n        // 2. If exportStarSet is not present, set exportStarSet to a new empty List.\n\n        // 3. If exportStarSet contains module, then\n        if export_star_set.contains(module_self) {\n            // a. Assert: We've reached the starting point of an export * circularity.\n            // b. Return a new empty List.\n            return FxHashSet::default();\n        }\n\n        // 4. Append module to exportStarSet.\n        export_star_set.push(module_self.clone());\n\n        // 5. Let exportedNames be a new empty List.\n        let mut exported_names = FxHashSet::default();\n\n        // 6. For each ExportEntry Record e of module.[[LocalExportEntries]], do\n        for e in &self.code.local_export_entries {\n            // a. Assert: module provides the direct binding for this export.\n            // b. Append e.[[ExportName]] to exportedNames.\n            let name = e.export_name().to_js_string(interner);\n            exported_names.insert(name);\n        }\n\n        // 7. For each ExportEntry Record e of module.[[IndirectExportEntries]], do\n        for e in &self.code.indirect_export_entries {\n            // a. Assert: module imports a specific binding for this export.\n            // b. Append e.[[ExportName]] to exportedNames.\n            let name = e.export_name().to_js_string(interner);\n            exported_names.insert(name);\n        }\n\n        // 8. For each ExportEntry Record e of module.[[StarExportEntries]], do\n        for e in &self.code.star_export_entries {\n            // a. Let requestedModule be GetImportedModule(module, e.[[ModuleRequest]]).\n            let requested_module = self.loaded_modules.borrow()[e].clone();\n\n            // b. Let starNames be requestedModule.GetExportedNames(exportStarSet).\n            // c. For each element n of starNames, do\n            for n in requested_module.get_exported_names(export_star_set, interner) {\n                // i. If SameValue(n, \"default\") is false, then\n                if n != js_str!(\"default\") {\n                    // 1. If exportedNames does not contain n, then\n                    //    a. Append n to exportedNames.\n                    exported_names.insert(n);\n                }\n            }\n        }\n\n        // 9. Return exportedNames.\n        exported_names\n    }\n\n    /// Concrete method [`ResolveExport ( exportName [ , resolveSet ] )`][spec]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-resolveexport\n    #[allow(clippy::mutable_key_type)]\n    pub(super) fn resolve_export(\n        &self,\n        module_self: &Module,\n        export_name: &JsString,\n        resolve_set: &mut FxHashSet<(Module, JsString)>,\n        interner: &Interner,\n    ) -> Result<ResolvedBinding, ResolveExportError> {\n        // 1. Assert: module.[[Status]] is not new.\n        // 2. If resolveSet is not present, set resolveSet to a new empty List.\n        // 3. For each Record { [[Module]], [[ExportName]] } r of resolveSet, do\n        //    a. If module and r.[[Module]] are the same Module Record and SameValue(exportName, r.[[ExportName]]) is true, then\n        let value = (module_self.clone(), export_name.clone());\n        if resolve_set.contains(&value) {\n            //   i. Assert: This is a circular import request.\n            //   ii. Return null.\n            return Err(ResolveExportError::NotFound);\n        }\n\n        // 4. Append the Record { [[Module]]: module, [[ExportName]]: exportName } to resolveSet.\n        resolve_set.insert(value);\n\n        // 5. For each ExportEntry Record e of module.[[LocalExportEntries]], do\n        for e in &self.code.local_export_entries {\n            // a. If SameValue(exportName, e.[[ExportName]]) is true, then\n            if export_name == &e.export_name().to_js_string(interner) {\n                // i. Assert: module provides the direct binding for this export.\n                // ii. Return ResolvedBinding Record { [[Module]]: module, [[BindingName]]: e.[[LocalName]] }.\n                return Ok(ResolvedBinding {\n                    module: module_self.clone(),\n                    binding_name: BindingName::Name(e.local_name().to_js_string(interner)),\n                });\n            }\n        }\n\n        // 6. For each ExportEntry Record e of module.[[IndirectExportEntries]], do\n        for e in &self.code.indirect_export_entries {\n            // a. If SameValue(exportName, e.[[ExportName]]) is true, then\n            if export_name == &e.export_name().to_js_string(interner) {\n                // i. Let importedModule be GetImportedModule(module, e.[[ModuleRequest]]).\n                let module_request = super::ModuleRequest::from_ast(\n                    e.module_request().to_js_string(interner),\n                    e.attributes(),\n                    interner,\n                );\n                let imported_module = self.loaded_modules.borrow()[&module_request].clone();\n                return match e.import_name() {\n                    // ii. If e.[[ImportName]] is all, then\n                    //    1. Assert: module does not provide the direct binding for this export.\n                    //    2. Return ResolvedBinding Record { [[Module]]: importedModule, [[BindingName]]: namespace }.\n                    ReExportImportName::Star => Ok(ResolvedBinding {\n                        module: imported_module,\n                        binding_name: BindingName::Namespace,\n                    }),\n                    // iii. Else,\n                    //    1. Assert: module imports a specific binding for this export.\n                    //    2. Return importedModule.ResolveExport(e.[[ImportName]], resolveSet).\n                    ReExportImportName::Name(name) => {\n                        let name = name.to_js_string(interner);\n                        imported_module.resolve_export(&name, resolve_set, interner)\n                    }\n                };\n            }\n        }\n\n        // 7. If SameValue(exportName, \"default\") is true, then\n        if export_name == &js_str!(\"default\") {\n            // a. Assert: A default export was not explicitly defined by this module.\n            // b. Return null.\n            // c. NOTE: A default export cannot be provided by an export * from \"mod\" declaration.\n            return Err(ResolveExportError::NotFound);\n        }\n\n        // 8. Let starResolution be null.\n        let mut star_resolution: Option<ResolvedBinding> = None;\n\n        // 9. For each ExportEntry Record e of module.[[StarExportEntries]], do\n        for e in &self.code.star_export_entries {\n            // a. Let importedModule be GetImportedModule(module, e.[[ModuleRequest]]).\n            let imported_module = self.loaded_modules.borrow()[e].clone();\n            // b. Let resolution be importedModule.ResolveExport(exportName, resolveSet).\n            let resolution =\n                match imported_module.resolve_export(export_name, resolve_set, interner) {\n                    // d. If resolution is not null, then\n                    Ok(resolution) => resolution,\n                    // c. If resolution is ambiguous, return ambiguous.\n                    Err(e @ ResolveExportError::Ambiguous) => return Err(e),\n                    Err(ResolveExportError::NotFound) => continue,\n                };\n\n            // i. Assert: resolution is a ResolvedBinding Record.\n            if let Some(star_resolution) = &star_resolution {\n                // iii. Else,\n                //    1. Assert: There is more than one * import that includes the requested name.\n                //    2. If resolution.[[Module]] and starResolution.[[Module]] are not the same Module Record,\n                //       return ambiguous.\n                if resolution.module() != star_resolution.module() {\n                    return Err(ResolveExportError::Ambiguous);\n                }\n                // 3. If resolution.[[BindingName]] is not starResolution.[[BindingName]] and either\n                //    resolution.[[BindingName]] or starResolution.[[BindingName]] is namespace,\n                //    return ambiguous.\n                // 4. If resolution.[[BindingName]] is a String, starResolution.[[BindingName]] is a\n                //    String, and SameValue(resolution.[[BindingName]], starResolution.[[BindingName]])\n                //    is false, return ambiguous.\n                match (resolution.binding_name(), star_resolution.binding_name()) {\n                    (BindingName::Namespace, BindingName::Name(_))\n                    | (BindingName::Name(_), BindingName::Namespace) => {\n                        // See step 3 above.\n                        return Err(ResolveExportError::Ambiguous);\n                    }\n                    (BindingName::Name(res), BindingName::Name(star)) if res != star => {\n                        // See step 4 above.\n                        return Err(ResolveExportError::Ambiguous);\n                    }\n                    _ => {}\n                }\n            } else {\n                // ii. If starResolution is null, then\n                //    1. Set starResolution to resolution.\n                star_resolution = Some(resolution);\n            }\n        }\n\n        // 10. Return starResolution.\n        star_resolution.ok_or(ResolveExportError::NotFound)\n    }\n\n    /// Concrete method [`Link ( )`][spec].\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-moduledeclarationlinking\n    pub(super) fn link(&self, module_self: &Module, context: &mut Context) -> JsResult<()> {\n        // 1. Assert: module.[[Status]] is one of unlinked, linked, evaluating-async, or evaluated.\n        debug_assert!(matches!(\n            &*self.status.borrow(),\n            ModuleStatus::Unlinked { .. }\n                | ModuleStatus::Linked { .. }\n                | ModuleStatus::EvaluatingAsync { .. }\n                | ModuleStatus::Evaluated { .. }\n        ));\n\n        // 2. Let stack be a new empty List.\n        let mut stack = Vec::new();\n\n        // 3. Let result be Completion(InnerModuleLinking(module, stack, 0)).\n        // 4. If result is an abrupt completion, then\n        if let Err(err) = self.inner_link(module_self, &mut stack, 0, context) {\n            // a. For each Cyclic Module Record m of stack, do\n            for m in stack.iter().filter_map(|cmr| cmr.kind().as_source_text()) {\n                // i. Assert: m.[[Status]] is linking.\n                // ii. Set m.[[Status]] to unlinked.\n                m.status.borrow_mut().transition(|status| match status {\n                    ModuleStatus::Linking {\n                        source,\n                        source_text,\n                        ..\n                    } => ModuleStatus::Unlinked {\n                        source,\n                        source_text,\n                    },\n                    _ => unreachable!(\"i. Assert: m.[[Status]] is linking.\"),\n                });\n            }\n            // b. Assert: module.[[Status]] is unlinked.\n            debug_assert!(matches!(\n                &*self.status.borrow(),\n                ModuleStatus::Unlinked { .. }\n            ));\n            // c. Return ? result.\n            return Err(err);\n        }\n\n        // 5. Assert: module.[[Status]] is one of linked, evaluating-async, or evaluated.\n        debug_assert!(matches!(\n            &*self.status.borrow(),\n            ModuleStatus::Linked { .. }\n                | ModuleStatus::EvaluatingAsync { .. }\n                | ModuleStatus::Evaluated { .. }\n        ));\n        // 6. Assert: stack is empty.\n        assert!(stack.is_empty());\n\n        // 7. Return unused.\n        Ok(())\n    }\n\n    /// Abstract operation [`InnerModuleLinking ( module, stack, index )`][spec].\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-InnerModuleLinking\n    pub(super) fn inner_link(\n        &self,\n        module_self: &Module,\n        stack: &mut Vec<Module>,\n        mut index: usize,\n        context: &mut Context,\n    ) -> JsResult<usize> {\n        // 2. If module.[[Status]] is one of linking, linked, evaluating-async, or evaluated, then\n        if matches!(\n            &*self.status.borrow(),\n            ModuleStatus::Linking { .. }\n                | ModuleStatus::PreLinked { .. }\n                | ModuleStatus::Linked { .. }\n                | ModuleStatus::EvaluatingAsync { .. }\n                | ModuleStatus::Evaluated { .. }\n        ) {\n            // a. Return index.\n            return Ok(index);\n        }\n\n        // 4. Set module.[[Status]] to linking.\n        // 5. Set module.[[DFSIndex]] to index.\n        // 6. Set module.[[DFSAncestorIndex]] to index.\n        self.status.borrow_mut().transition(|status| match status {\n            ModuleStatus::Unlinked {\n                source,\n                source_text,\n            } => ModuleStatus::Linking {\n                source,\n                source_text,\n                info: DfsInfo {\n                    dfs_index: index,\n                    dfs_ancestor_index: index,\n                },\n            },\n            _ => unreachable!(\"3. Assert: module.[[Status]] is unlinked.\"),\n        });\n\n        // 7. Set index to index + 1.\n        index += 1;\n\n        // 8. Append module to stack.\n        stack.push(module_self.clone());\n\n        // 9. For each String required of module.[[RequestedModules]], do\n\n        for required in &self.code.requested_modules {\n            // a. Let requiredModule be GetImportedModule(module, required).\n            let required_module = self.loaded_modules.borrow()[required].clone();\n\n            // b. Set index to ? InnerModuleLinking(requiredModule, stack, index).\n            index = required_module.inner_link(stack, index, context)?;\n            // c. If requiredModule is a Cyclic Module Record, then\n            if let ModuleKind::SourceText(required_module_src) = required_module.kind() {\n                // i. Assert: requiredModule.[[Status]] is one of linking, linked, evaluating-async, or evaluated.\n                // ii. Assert: requiredModule.[[Status]] is linking if and only if stack contains requiredModule.\n                debug_assert!(match &*required_module_src.status.borrow() {\n                    ModuleStatus::PreLinked { .. }\n                    | ModuleStatus::Linked { .. }\n                    | ModuleStatus::EvaluatingAsync { .. }\n                    | ModuleStatus::Evaluated { .. } => true,\n                    ModuleStatus::Linking { .. } if stack.contains(&required_module) => true,\n                    _ => false,\n                });\n\n                // iii. If requiredModule.[[Status]] is linking, then\n                let required_index = if let ModuleStatus::Linking {\n                    info:\n                        DfsInfo {\n                            dfs_ancestor_index, ..\n                        },\n                    ..\n                } = &*required_module_src.status.borrow()\n                {\n                    // 1. Set module.[[DFSAncestorIndex]] to\n                    //    min(module.[[DFSAncestorIndex]], requiredModule.[[DFSAncestorIndex]]).\n\n                    Some(*dfs_ancestor_index)\n                } else {\n                    None\n                };\n\n                if let Some(required_index) = required_index {\n                    let mut status = self.status.borrow_mut();\n\n                    let DfsInfo {\n                        dfs_ancestor_index, ..\n                    } = status\n                        .dfs_info_mut()\n                        .js_expect(\"should be on the linking state\")?;\n                    *dfs_ancestor_index = usize::min(*dfs_ancestor_index, required_index);\n                }\n            }\n        }\n\n        // 10. Perform ? module.InitializeEnvironment().\n        self.initialize_environment(module_self, context)?;\n\n        // 11. Assert: module occurs exactly once in stack.\n        debug_assert_eq!(\n            stack.iter().filter(|module| *module == module_self).count(),\n            1\n        );\n        // 12. Assert: module.[[DFSAncestorIndex]] ≤ module.[[DFSIndex]].\n        debug_assert!({\n            let DfsInfo {\n                dfs_ancestor_index,\n                dfs_index,\n            } = self\n                .status\n                .borrow()\n                .dfs_info()\n                .copied()\n                .expect(\"should be linking\");\n            dfs_ancestor_index <= dfs_index\n        });\n\n        let info = self.status.borrow().dfs_info().copied();\n        match info {\n            // 13. If module.[[DFSAncestorIndex]] = module.[[DFSIndex]], then\n\n            //     a. Let done be false.\n            //     b. Repeat, while done is false,\n            Some(info) if info.dfs_ancestor_index == info.dfs_index => loop {\n                //    i. Let requiredModule be the last element of stack.\n                //    ii. Remove the last element of stack.\n                let last = stack.pop().js_expect(\"should have at least one element\")?;\n                let ModuleKind::SourceText(last_src) = last.kind() else {\n                    unreachable!(\"iii. Assert: requiredModule is a Cyclic Module Record.\")\n                };\n\n                //    iv. Set requiredModule.[[Status]] to linked.\n                last_src\n                    .status\n                    .borrow_mut()\n                    .transition(|current| match current {\n                        ModuleStatus::PreLinked {\n                            info,\n                            context,\n                            environment,\n                        } => ModuleStatus::Linked {\n                            info,\n                            context,\n                            environment,\n                        },\n                        _ => {\n                            unreachable!(\n                                \"can only transition to `Linked` from the `PreLinked` state\"\n                            )\n                        }\n                    });\n\n                //    v. If requiredModule and module are the same Module Record, set done to true.\n                if &last == module_self {\n                    break;\n                }\n            },\n            _ => {}\n        }\n\n        // 14. Return index.\n        Ok(index)\n    }\n\n    /// Concrete method [`Evaluate ( )`][spec].\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-moduleevaluation\n    pub(super) fn evaluate(\n        &self,\n        module_self: &Module,\n        context: &mut Context,\n    ) -> JsResult<JsPromise> {\n        // 1. Assert: This call to Evaluate is not happening at the same time as another call to Evaluate within the surrounding agent.\n        let (module, promise) = {\n            match &*self.status.borrow() {\n                ModuleStatus::Unlinked { .. }\n                | ModuleStatus::Linking { .. }\n                | ModuleStatus::PreLinked { .. }\n                | ModuleStatus::Evaluating { .. } => {\n                    unreachable!(\n                        \"2. Assert: module.[[Status]] is one of linked, evaluating-async, or evaluated.\"\n                    )\n                }\n                ModuleStatus::Linked { .. } => (module_self.clone(), None),\n                // 3. If module.[[Status]] is either evaluating-async or evaluated, set module to module.[[CycleRoot]].\n                ModuleStatus::EvaluatingAsync {\n                    cycle_root,\n                    top_level_capability,\n                    ..\n                }\n                | ModuleStatus::Evaluated {\n                    cycle_root,\n                    top_level_capability,\n                    ..\n                } => (\n                    cycle_root.clone(),\n                    top_level_capability\n                        .as_ref()\n                        .map(|cap| {\n                            JsPromise::from_object(cap.promise().clone()).js_expect(\n                                \"promise created from the %Promise% intrinsic is always native\",\n                            )\n                        })\n                        .transpose()?,\n                ),\n            }\n        };\n\n        // 4. If module.[[TopLevelCapability]] is not empty, then\n        if let Some(promise) = promise {\n            // a. Return module.[[TopLevelCapability]].[[Promise]].\n            return Ok(promise);\n        }\n\n        // 5. Let stack be a new empty List.\n        let mut stack = Vec::new();\n\n        // 6. Let capability be ! NewPromiseCapability(%Promise%).\n        // 7. Set module.[[TopLevelCapability]] to capability.\n        let capability = PromiseCapability::new(\n            &context.intrinsics().constructors().promise().constructor(),\n            context,\n        )\n        .js_expect(\n            \"capability creation must always succeed when using the `%Promise%` intrinsic\",\n        )?;\n\n        // 8. Let result be Completion(InnerModuleEvaluation(module, stack, 0)).\n        let ModuleKind::SourceText(module_src) = module.kind() else {\n            unreachable!(\"module must be a source text module\");\n        };\n        let result =\n            module_src.inner_evaluate(&module, &mut stack, 0, Some(capability.clone()), context);\n\n        match result {\n            Ok(_) => {\n                // 10. Else,\n                //     a. Assert: module.[[Status]] is either evaluating-async or evaluated.\n                assert!(match &*module_src.status.borrow() {\n                    ModuleStatus::EvaluatingAsync { .. } => true,\n                    // b. Assert: module.[[EvaluationError]] is empty.\n                    ModuleStatus::Evaluated { error, .. } if error.is_none() => true,\n                    _ => false,\n                });\n\n                //     c. If module.[[AsyncEvaluation]] is false, then\n                if matches!(&*module_src.status.borrow(), ModuleStatus::Evaluated { .. }) {\n                    //    i. Assert: module.[[Status]] is evaluated.\n                    //    ii. Perform ! Call(capability.[[Resolve]], undefined, « undefined »).\n                    capability\n                        .resolve()\n                        .call(&JsValue::undefined(), &[], context)\n                        .js_expect(\"cannot fail for the default resolve function\")?;\n                }\n\n                //     d. Assert: stack is empty.\n                assert!(stack.is_empty());\n            }\n            // 9. If result is an abrupt completion, then\n            Err(err) => {\n                // a. For each Cyclic Module Record m of stack, do\n                for m in stack.iter().filter_map(|cmr| cmr.kind().as_source_text()) {\n                    m.status.borrow_mut().transition(|current| match current {\n                        // i. Assert: m.[[Status]] is evaluating.\n                        ModuleStatus::Evaluating {\n                            environment,\n                            top_level_capability,\n                            cycle_root,\n                            ..\n                        } | ModuleStatus::EvaluatingAsync {\n                            environment,\n                            top_level_capability,\n                            cycle_root,\n                            ..\n                        } => {\n                            // ii. Set m.[[Status]] to evaluated.\n                            // iii. Set m.[[EvaluationError]] to result.\n                            ModuleStatus::Evaluated {\n                                environment,\n                                top_level_capability,\n                                cycle_root,\n                                error: Some(err.clone()),\n                            }\n                        },\n                        _ => panic!(\n                            \"can only transition to `Evaluated` from the `Evaluating` or `EvaluatingAsync states\"\n                        ),\n                    });\n                }\n                // b. Assert: module.[[Status]] is evaluated.\n                // c. Assert: module.[[EvaluationError]] is result.\n                assert!(\n                    matches!(&*module_src.status.borrow(), ModuleStatus::Evaluated { error, .. } if error.is_some())\n                );\n\n                // d. Perform ! Call(capability.[[Reject]], undefined, « result.[[Value]] »).\n                capability\n                    .reject()\n                    .call(&JsValue::undefined(), &[err.into_opaque(context)?], context)\n                    .js_expect(\"cannot fail for the default reject function\")?;\n            }\n        }\n\n        // 11. Return capability.[[Promise]].\n        Ok(JsPromise::from_object(capability.promise().clone())\n            .js_expect(\"promise created from the %Promise% intrinsic is always native\")?)\n    }\n\n    /// Abstract operation [`InnerModuleEvaluation ( module, stack, index )`][spec]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-innermoduleevaluation\n    pub(super) fn inner_evaluate(\n        &self,\n        module_self: &Module,\n        stack: &mut Vec<Module>,\n        mut index: usize,\n        capability: Option<PromiseCapability>,\n        context: &mut Context,\n    ) -> JsResult<usize> {\n        /// Gets the next evaluation index of an async module.\n        ///\n        /// Returns an error if there's no more available indices.\n        fn get_async_eval_index() -> JsResult<usize> {\n            thread_local! {\n                static ASYNC_EVAL_QUEUE_INDEX: Cell<usize> = const { Cell::new(0) };\n            }\n\n            ASYNC_EVAL_QUEUE_INDEX\n                .with(|idx| {\n                    let next = idx.get().checked_add(1)?;\n                    Some(idx.replace(next))\n                })\n                .ok_or_else(|| {\n                    JsNativeError::range()\n                        .with_message(\"exceeded the maximum number of async modules\")\n                        .into()\n                })\n        }\n\n        // 2. If module.[[Status]] is either evaluating-async or evaluated, then\n        match &*self.status.borrow() {\n            // 3. If module.[[Status]] is evaluating, return index.\n            ModuleStatus::Evaluating { .. } | ModuleStatus::EvaluatingAsync { .. } => {\n                return Ok(index);\n            }\n            //     a. If module.[[EvaluationError]] is empty, return index.\n            //     b. Otherwise, return ? module.[[EvaluationError]].\n            ModuleStatus::Evaluated { error, .. } => return error.clone().map_or(Ok(index), Err),\n            ModuleStatus::Linked { .. } => {\n                // 4. Assert: module.[[Status]] is linked.\n                // evaluate a linked module\n            }\n            _ => unreachable!(\n                \"2. Assert: module.[[Status]] is one of linked, evaluating-async, or evaluated.\"\n            ),\n        }\n\n        // 5. Set module.[[Status]] to evaluating.\n        // 6. Set module.[[DFSIndex]] to index.\n        // 7. Set module.[[DFSAncestorIndex]] to index.\n        // 8. Set module.[[PendingAsyncDependencies]] to 0.\n        self.status.borrow_mut().transition(|status| match status {\n            ModuleStatus::Linked {\n                environment,\n                context,\n                ..\n            } => ModuleStatus::Evaluating {\n                environment,\n                context,\n                top_level_capability: capability,\n                cycle_root: module_self.clone(),\n                info: DfsInfo {\n                    dfs_index: index,\n                    dfs_ancestor_index: index,\n                },\n                async_eval_index: None,\n            },\n            _ => unreachable!(\"already asserted that this state is `Linked`. \"),\n        });\n\n        // 9. Set index to index + 1.\n        index += 1;\n\n        let mut pending_async_dependencies = 0;\n        // 10. Append module to stack.\n        stack.push(module_self.clone());\n\n        // 11. For each String required of module.[[RequestedModules]], do\n        for required in &self.code.requested_modules {\n            // a. Let requiredModule be GetImportedModule(module, required).\n            let required_module = self.loaded_modules.borrow()[required].clone();\n            // b. Set index to ? InnerModuleEvaluation(requiredModule, stack, index).\n            index = required_module.inner_evaluate(stack, index, context)?;\n\n            // c. If requiredModule is a Cyclic Module Record, then\n            if let ModuleKind::SourceText(required_module_src) = required_module.kind() {\n                // i. Assert: requiredModule.[[Status]] is one of evaluating, evaluating-async, or evaluated.\n                // ii. Assert: requiredModule.[[Status]] is evaluating if and only if stack contains requiredModule.\n                debug_assert!(match &*required_module_src.status.borrow() {\n                    ModuleStatus::EvaluatingAsync { .. } | ModuleStatus::Evaluated { .. } => true,\n                    ModuleStatus::Evaluating { .. } if stack.contains(&required_module) => true,\n                    _ => false,\n                });\n\n                let (required_module, async_eval, req_info) = match &*required_module_src\n                    .status\n                    .borrow()\n                {\n                    // iii. If requiredModule.[[Status]] is evaluating, then\n                    ModuleStatus::Evaluating {\n                        info,\n                        async_eval_index,\n                        ..\n                    } => {\n                        // 1. Set module.[[DFSAncestorIndex]] to min(module.[[DFSAncestorIndex]], requiredModule.[[DFSAncestorIndex]]).\n                        (\n                            required_module.clone(),\n                            async_eval_index.is_some(),\n                            Some(*info),\n                        )\n                    }\n                    // iv. Else,\n                    ModuleStatus::EvaluatingAsync { cycle_root, .. }\n                    | ModuleStatus::Evaluated { cycle_root, .. } => {\n                        // 1. Set requiredModule to requiredModule.[[CycleRoot]].\n                        let ModuleKind::SourceText(cycle_root_src) = cycle_root.kind() else {\n                            unreachable!(\"cycle_root must be a source text module\");\n                        };\n\n                        // 2. Assert: requiredModule.[[Status]] is either evaluating-async or evaluated.\n                        match &*cycle_root_src.status.borrow() {\n                            ModuleStatus::EvaluatingAsync { .. } => {\n                                (cycle_root.clone(), true, None)\n                            }\n                            // 3. If requiredModule.[[EvaluationError]] is not empty, return ? requiredModule.[[EvaluationError]].\n                            ModuleStatus::Evaluated {\n                                error: Some(error), ..\n                            } => return Err(error.clone()),\n                            ModuleStatus::Evaluated { .. } => (cycle_root.clone(), false, None),\n                            _ => unreachable!(\n                                \"2. Assert: requiredModule.[[Status]] is either evaluating-async or evaluated.\"\n                            ),\n                        }\n                    }\n                    _ => unreachable!(\n                        \"i. Assert: requiredModule.[[Status]] is one of evaluating, evaluating-async, or evaluated.\"\n                    ),\n                };\n\n                let ModuleKind::SourceText(required_module) = required_module.kind() else {\n                    unreachable!(\"required_module must be a source text module\");\n                };\n\n                if let Some(req_info) = req_info {\n                    let mut status = self.status.borrow_mut();\n                    let info = status\n                        .dfs_info_mut()\n                        .js_expect(\"self should still be in the evaluating state\")?;\n                    info.dfs_ancestor_index =\n                        usize::min(info.dfs_ancestor_index, req_info.dfs_ancestor_index);\n                }\n\n                // v. If requiredModule.[[AsyncEvaluation]] is true, then\n                if async_eval {\n                    // 1. Set module.[[PendingAsyncDependencies]] to module.[[PendingAsyncDependencies]] + 1.\n                    pending_async_dependencies += 1;\n                    // 2. Append module to requiredModule.[[AsyncParentModules]].\n                    required_module\n                        .async_parent_modules\n                        .borrow_mut()\n                        .push(module_self.clone());\n                }\n            }\n        }\n\n        // 12. If module.[[PendingAsyncDependencies]] > 0 or module.[[HasTLA]] is true, then\n        if pending_async_dependencies > 0 || self.code.has_tla {\n            // a. Assert: module.[[AsyncEvaluation]] is false and was never previously set to true.\n            {\n                let ModuleStatus::Evaluating {\n                    async_eval_index, ..\n                } = &mut *self.status.borrow_mut()\n                else {\n                    unreachable!(\"self should still be in the evaluating state\")\n                };\n\n                // b. Set module.[[AsyncEvaluation]] to true.\n                // c. NOTE: The order in which module records have their [[AsyncEvaluation]] fields transition to true is significant. (See 16.2.1.5.3.4.)\n                *async_eval_index = Some(get_async_eval_index()?);\n            }\n\n            //     d. If module.[[PendingAsyncDependencies]] = 0, perform ExecuteAsyncModule(module).\n            if pending_async_dependencies == 0 {\n                self.execute_async(module_self, context);\n            }\n        } else {\n            // 13. Else,\n            //    a. Perform ? module.ExecuteModule().\n            self.execute(module_self, None, context)?;\n        }\n\n        let dfs_info = self.status.borrow().dfs_info().copied().js_expect(\n            \"haven't transitioned from the `Evaluating` state, so it should have its dfs info\",\n        )?;\n\n        // 14. Assert: module occurs exactly once in stack.\n        debug_assert_eq!(stack.iter().filter(|m| *m == module_self).count(), 1);\n        // 15. Assert: module.[[DFSAncestorIndex]] ≤ module.[[DFSIndex]].\n        assert!(dfs_info.dfs_ancestor_index <= dfs_info.dfs_index);\n\n        // 16. If module.[[DFSAncestorIndex]] = module.[[DFSIndex]], then\n        if dfs_info.dfs_ancestor_index == dfs_info.dfs_index {\n            // a. Let done be false.\n            // b. Repeat, while done is false,\n            loop {\n                // i. Let requiredModule be the last element of stack.\n                // ii. Remove the last element of stack.\n                let required_module = stack\n                    .pop()\n                    .js_expect(\"should at least have `self` in the stack\")?;\n                let is_self = module_self == &required_module;\n\n                let ModuleKind::SourceText(required_module_src) = required_module.kind() else {\n                    unreachable!(\"iii. Assert: requiredModule is a Cyclic Module Record.\");\n                };\n                required_module_src.status.borrow_mut().transition(|current| match current {\n                ModuleStatus::Evaluating {\n                            environment,\n                            top_level_capability,\n                            cycle_root,\n                            async_eval_index,\n                            context,\n                            ..\n                        } => if let Some(async_eval_index) = async_eval_index {\n                            // v. Otherwise, set requiredModule.[[Status]] to evaluating-async.\n                            ModuleStatus::EvaluatingAsync {\n                                environment,\n                                top_level_capability,\n                                // vii. Set requiredModule.[[CycleRoot]] to module.\n                                cycle_root: if is_self {\n                                    cycle_root\n                                } else {\n                                    module_self.clone()\n                                },\n                                async_eval_index,\n                                pending_async_dependencies,\n                                context\n                            }\n                        } else {\n                            // iv. If requiredModule.[[AsyncEvaluation]] is false, set requiredModule.[[Status]] to evaluated.\n                            ModuleStatus::Evaluated {\n                                environment,\n                                top_level_capability,\n                                cycle_root: if is_self {\n                                    cycle_root\n                                } else {\n                                    module_self.clone()\n                                },\n                                error: None,\n                            }\n                        }\n                        _ => unreachable!(\n                            \"should only transition to `Evaluated` or `EvaluatingAsync` from the `Evaluating` state\"\n                        )\n                    }\n                );\n\n                // vi. If requiredModule and module are the same Module Record, set done to true.\n                if is_self {\n                    break;\n                }\n            }\n        }\n\n        // 17. Return index.\n        Ok(index)\n    }\n\n    /// Abstract operation [`ExecuteAsyncModule ( module )`][spec].\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-execute-async-module\n    fn execute_async(&self, module_self: &Module, context: &mut Context) {\n        // 1. Assert: module.[[Status]] is either evaluating or evaluating-async.\n        debug_assert!(matches!(\n            &*self.status.borrow(),\n            ModuleStatus::Evaluating { .. } | ModuleStatus::EvaluatingAsync { .. }\n        ));\n        // 2. Assert: module.[[HasTLA]] is true.\n        debug_assert!(self.code.has_tla);\n\n        // 3. Let capability be ! NewPromiseCapability(%Promise%).\n        let capability = PromiseCapability::new(\n            &context.intrinsics().constructors().promise().constructor(),\n            context,\n        )\n        .expect(\"cannot fail for the %Promise% intrinsic\");\n        let promise = capability\n            .promise\n            .clone()\n            .downcast::<Promise>()\n            .expect(\"%Promise% constructor must always return a `Promise` object\");\n\n        // 4. Let fulfilledClosure be a new Abstract Closure with no parameters that captures module and performs the following steps when called:\n        // 5. Let onFulfilled be CreateBuiltinFunction(fulfilledClosure, 0, \"\", « »).\n        let on_fulfilled = FunctionObjectBuilder::new(\n            context.realm(),\n            NativeFunction::from_copy_closure_with_captures(\n                |_, _, module, context| {\n                    //     a. Perform AsyncModuleExecutionFulfilled(module).\n                    async_module_execution_fulfilled(module, context)?;\n                    //     b. Return undefined.\n                    Ok(JsValue::undefined())\n                },\n                module_self.clone(),\n            ),\n        )\n        .build();\n\n        // 6. Let rejectedClosure be a new Abstract Closure with parameters (error) that captures module and performs the following steps when called:\n        // 7. Let onRejected be CreateBuiltinFunction(rejectedClosure, 0, \"\", « »).\n        let on_rejected = FunctionObjectBuilder::new(\n            context.realm(),\n            NativeFunction::from_copy_closure_with_captures(\n                |_, args, module, context| {\n                    let error = JsError::from_opaque(args.get_or_undefined(0).clone());\n                    // a. Perform AsyncModuleExecutionRejected(module, error).\n                    async_module_execution_rejected(module, error, context)?;\n                    // b. Return undefined.\n                    Ok(JsValue::undefined())\n                },\n                module_self.clone(),\n            ),\n        )\n        .build();\n\n        // 8. Perform PerformPromiseThen(capability.[[Promise]], onFulfilled, onRejected).\n        Promise::perform_promise_then(\n            &promise,\n            Some(on_fulfilled),\n            Some(on_rejected),\n            None,\n            context,\n        );\n\n        // 9. Perform ! module.ExecuteModule(capability).\n        // 10. Return unused.\n        self.execute(module_self, Some(capability), context)\n            .expect(\"async modules cannot directly throw\");\n    }\n\n    /// Abstract operation [`GatherAvailableAncestors ( module, execList )`][spec].\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-gather-available-ancestors\n    #[allow(clippy::mutable_key_type)]\n    fn gather_available_ancestors(&self, exec_list: &mut FxHashSet<Module>) {\n        // 1. For each Cyclic Module Record m of module.[[AsyncParentModules]], do\n        let parents = std::mem::take(&mut *self.async_parent_modules.borrow_mut());\n        for m in parents {\n            let ModuleKind::SourceText(m_src) = m.kind() else {\n                continue;\n            };\n\n            if exec_list.contains(&m) {\n                continue;\n            }\n\n            {\n                let m_status = m_src.status.borrow();\n                let Some(cycle_root) = m_status\n                    .cycle_root()\n                    .and_then(|root| root.kind().as_source_text())\n                else {\n                    continue;\n                };\n\n                if cycle_root.status.borrow().evaluation_error().is_some() {\n                    continue;\n                }\n            }\n\n            // a. If execList does not contain m and m.[[CycleRoot]].[[EvaluationError]] is empty, then\n\n            let (deps, has_tla) = {\n                // i. Assert: m.[[Status]] is evaluating-async.\n                // ii. Assert: m.[[EvaluationError]] is empty.\n                // iii. Assert: m.[[AsyncEvaluation]] is true.\n                let ModuleStatus::EvaluatingAsync {\n                    pending_async_dependencies,\n                    ..\n                } = &mut *m_src.status.borrow_mut()\n                else {\n                    unreachable!(\"i. Assert: m.[[Status]] is evaluating-async.\");\n                };\n                // iv. Assert: m.[[PendingAsyncDependencies]] > 0.\n                assert!(*pending_async_dependencies > 0);\n\n                // v. Set m.[[PendingAsyncDependencies]] to m.[[PendingAsyncDependencies]] - 1.\n                *pending_async_dependencies -= 1;\n                (*pending_async_dependencies, m_src.code.has_tla)\n            };\n\n            // vi. If m.[[PendingAsyncDependencies]] = 0, then\n            if deps == 0 {\n                // 1. Append m to execList.\n                exec_list.insert(m.clone());\n                // 2. If m.[[HasTLA]] is false, perform GatherAvailableAncestors(m, execList).\n                if !has_tla {\n                    m_src.gather_available_ancestors(exec_list);\n                }\n            }\n        }\n        // 2. Return unused.\n    }\n\n    /// Abstract operation [`InitializeEnvironment ( )`][spec].\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-source-text-module-record-initialize-environment\n    fn initialize_environment(&self, module_self: &Module, context: &mut Context) -> JsResult<()> {\n        #[derive(Debug)]\n        enum ImportBinding {\n            Namespace {\n                locator: BindingLocator,\n                module: Module,\n            },\n            Single {\n                locator: BindingLocator,\n                export_locator: ResolvedBinding,\n            },\n        }\n\n        {\n            // 1. For each ExportEntry Record e of module.[[IndirectExportEntries]], do\n            for e in &self.code.indirect_export_entries {\n                // a. Let resolution be module.ResolveExport(e.[[ExportName]]).\n                module_self\n                    .resolve_export(\n                        &e.export_name().to_js_string(context.interner()),\n                        &mut HashSet::default(),\n                        context.interner(),\n                    )\n                    // b. If resolution is either null or ambiguous, throw a SyntaxError exception.\n                    .map_err(|err| match err {\n                        ResolveExportError::NotFound => {\n                            JsNativeError::syntax().with_message(format!(\n                                \"could not find export `{}`\",\n                                context.interner().resolve_expect(e.export_name())\n                            ))\n                        }\n                        ResolveExportError::Ambiguous => {\n                            JsNativeError::syntax().with_message(format!(\n                                \"could not resolve ambiguous export `{}`\",\n                                context.interner().resolve_expect(e.export_name())\n                            ))\n                        }\n                    })?;\n                // c. Assert: resolution is a ResolvedBinding Record.\n            }\n        }\n\n        // 2. Assert: All named exports from module are resolvable.\n        // 3. Let realm be module.[[Realm]].\n        // 4. Assert: realm is not undefined.\n        let realm = module_self.realm().clone();\n\n        let status = self.status.borrow();\n        let (source, source_text) = status\n            .source()\n            .js_expect(\"module can only initialize its environment in the linking phase\")?;\n\n        // 5. Let env be NewModuleEnvironment(realm.[[GlobalEnv]]).\n        // 6. Set module.[[Environment]] to env.\n        let env = source.scope().clone();\n\n        let spanned_source_text = SpannedSourceText::new_source_only(source_text.clone());\n        let mut compiler = ByteCompiler::new(\n            js_string!(\"<main>\"),\n            true,\n            false,\n            source.scope().clone(),\n            source.scope().clone(),\n            self.code.has_tla,\n            false,\n            context.interner_mut(),\n            false,\n            spanned_source_text,\n            self.code.path.clone().into(),\n        );\n\n        compiler.async_handler = self.code.has_tla.then(|| compiler.push_handler());\n\n        let mut imports = Vec::new();\n\n        let (codeblock, functions) = {\n            // 7. For each ImportEntry Record in of module.[[ImportEntries]], do\n            for entry in &self.code.import_entries {\n                // a. Let importedModule be GetImportedModule(module, in.[[ModuleRequest]]).\n                let module_request = super::ModuleRequest::from_ast(\n                    entry.module_request().to_js_string(compiler.interner()),\n                    entry.attributes(),\n                    compiler.interner(),\n                );\n                let imported_module = self.loaded_modules.borrow()[&module_request].clone();\n\n                if let ImportName::Name(name) = entry.import_name() {\n                    let name = name.to_js_string(compiler.interner());\n                    // c. Else,\n                    //    i. Let resolution be importedModule.ResolveExport(in.[[ImportName]]).\n                    let resolution = imported_module\n                        .resolve_export(&name, &mut HashSet::default(), compiler.interner())\n                        // ii. If resolution is either null or ambiguous, throw a SyntaxError exception.\n                        .map_err(|err| match err {\n                            ResolveExportError::NotFound => JsNativeError::syntax().with_message(\n                                format!(\"could not find export `{}`\", name.to_std_string_escaped()),\n                            ),\n                            ResolveExportError::Ambiguous => {\n                                JsNativeError::syntax().with_message(format!(\n                                    \"could not resolve ambiguous export `{}`\",\n                                    name.to_std_string_escaped()\n                                ))\n                            }\n                        })?;\n\n                    // 2. Perform ! env.CreateImmutableBinding(in.[[LocalName]], true).\n                    // 3. Perform ! env.InitializeBinding(in.[[LocalName]], namespace).\n                    let local_name = entry.local_name().to_js_string(compiler.interner());\n                    let locator = env\n                        .get_binding(&local_name)\n                        .js_expect(\"binding must exist\")?;\n\n                    if let BindingName::Name(_) = resolution.binding_name() {\n                        // 1. Perform env.CreateImportBinding(in.[[LocalName]], resolution.[[Module]],\n                        //    resolution.[[BindingName]]).\n                        //    deferred to initialization below\n                        imports.push(ImportBinding::Single {\n                            locator,\n                            export_locator: resolution,\n                        });\n                    } else {\n                        // 1. Let namespace be GetModuleNamespace(resolution.[[Module]]).\n                        // deferred to initialization below\n                        imports.push(ImportBinding::Namespace {\n                            locator,\n                            module: resolution.into_module(),\n                        });\n                    }\n                } else {\n                    // b. If in.[[ImportName]] is namespace-object, then\n                    //    ii. Perform ! env.CreateImmutableBinding(in.[[LocalName]], true).\n                    //    iii. Perform ! env.InitializeBinding(in.[[LocalName]], namespace).\n                    let name = entry.local_name().to_js_string(compiler.interner());\n                    let locator = env.get_binding(&name).js_expect(\"binding must exist\")?;\n\n                    //    i. Let namespace be GetModuleNamespace(importedModule).\n                    //       deferred to initialization below\n                    imports.push(ImportBinding::Namespace {\n                        locator,\n                        module: imported_module.clone(),\n                    });\n                }\n            }\n\n            // 18. Let code be module.[[ECMAScriptCode]].\n            // 19. Let varDeclarations be the VarScopedDeclarations of code.\n            let var_declarations = var_scoped_declarations(source);\n            // 20. Let declaredVarNames be a new empty List.\n            let mut declared_var_names = Vec::new();\n            // 21. For each element d of varDeclarations, do\n            for var in var_declarations {\n                // a. For each element dn of the BoundNames of d, do\n                for name in var.bound_names() {\n                    let name = name.to_js_string(compiler.interner());\n\n                    // i. If declaredVarNames does not contain dn, then\n                    if !declared_var_names.contains(&name) {\n                        // 1. Perform ! env.CreateMutableBinding(dn, false).\n                        // 2. Perform ! env.InitializeBinding(dn, undefined).\n                        let binding = env\n                            .get_binding_reference(&name)\n                            .js_expect(\"binding must exist\")?;\n                        let index = compiler.insert_binding(binding);\n                        compiler.emit_binding_access(\n                            BindingAccessOpcode::DefInitVar,\n                            &index,\n                            &CallFrame::undefined_register(),\n                        );\n\n                        // 3. Append dn to declaredVarNames.\n                        declared_var_names.push(name);\n                    }\n                }\n            }\n\n            // 22. Let lexDeclarations be the LexicallyScopedDeclarations of code.\n            // 23. Let privateEnv be null.\n            let lex_declarations = lexically_scoped_declarations(source);\n            let mut functions = Vec::new();\n            // 24. For each element d of lexDeclarations, do\n            for declaration in lex_declarations {\n                // ii. Else,\n                // a. For each element dn of the BoundNames of d, do\n                // 1. Perform ! env.CreateMutableBinding(dn, false).\n                //\n                // iii. If d is either a FunctionDeclaration, a GeneratorDeclaration, an\n                //      AsyncFunctionDeclaration, or an AsyncGeneratorDeclaration, then\n                // 1. Let fo be InstantiateFunctionObject of d with arguments env and privateEnv.\n                // 2. Perform ! env.InitializeBinding(dn, fo).\n                //\n                // deferred to below.\n                let (spec, locator): (FunctionSpec<'_>, _) = match declaration {\n                    LexicallyScopedDeclaration::FunctionDeclaration(f) => {\n                        let name = bound_names(f)[0].to_js_string(compiler.interner());\n                        let locator = env.get_binding(&name).js_expect(\"binding must exist\")?;\n\n                        (f.into(), locator)\n                    }\n                    LexicallyScopedDeclaration::GeneratorDeclaration(g) => {\n                        let name = bound_names(g)[0].to_js_string(compiler.interner());\n                        let locator = env.get_binding(&name).js_expect(\"binding must exist\")?;\n\n                        (g.into(), locator)\n                    }\n                    LexicallyScopedDeclaration::AsyncFunctionDeclaration(af) => {\n                        let name = bound_names(af)[0].to_js_string(compiler.interner());\n                        let locator = env.get_binding(&name).js_expect(\"binding must exist\")?;\n\n                        (af.into(), locator)\n                    }\n                    LexicallyScopedDeclaration::AsyncGeneratorDeclaration(ag) => {\n                        let name = bound_names(ag)[0].to_js_string(compiler.interner());\n                        let locator = env.get_binding(&name).js_expect(\"binding must exist\")?;\n\n                        (ag.into(), locator)\n                    }\n                    LexicallyScopedDeclaration::ClassDeclaration(_)\n                    | LexicallyScopedDeclaration::LexicalDeclaration(_)\n                    | LexicallyScopedDeclaration::AssignmentExpression(_) => {\n                        continue;\n                    }\n                };\n\n                functions.push((spec, locator));\n            }\n\n            // Should compile after initializing bindings first to ensure inner calls\n            // are correctly resolved to the outer functions instead of as global bindings.\n            let functions = functions\n                .into_iter()\n                .map(|(spec, locator)| (compiler.function(spec), locator))\n                .collect::<Vec<_>>();\n\n            compiler.compile_module_item_list(source.items());\n\n            (Gc::new(compiler.finish()), functions)\n        };\n\n        // 8. Let moduleContext be a new ECMAScript code execution context.\n        let mut envs = EnvironmentStack::new();\n        envs.push_module(source.scope().clone());\n        drop(status);\n\n        // 9. Set the Function of moduleContext to null.\n        // 10. Assert: module.[[Realm]] is not undefined.\n        // 11. Set the Realm of moduleContext to module.[[Realm]].\n        // 12. Set the ScriptOrModule of moduleContext to module.\n        // 13. Set the VariableEnvironment of moduleContext to module.[[Environment]].\n        // 14. Set the LexicalEnvironment of moduleContext to module.[[Environment]].\n        // 15. Set the PrivateEnvironment of moduleContext to null.\n        let call_frame = CallFrame::new(\n            codeblock.clone(),\n            Some(ActiveRunnable::Module(module_self.clone())),\n            envs,\n            realm.clone(),\n        );\n        context\n            .vm\n            .push_frame_with_stack(call_frame, JsValue::undefined(), JsValue::null());\n\n        // 17. Push moduleContext onto the execution context stack; moduleContext is now the running execution context.\n\n        // deferred initialization of import bindings\n        for import in imports {\n            match import {\n                ImportBinding::Namespace { locator, module } => {\n                    // i. Let namespace be GetModuleNamespace(importedModule).\n                    let namespace = module.namespace(context);\n                    {\n                        let frame = context.vm.frame_mut();\n                        let global = frame.realm.environment();\n                        frame.environments.put_lexical_value(\n                            locator.scope(),\n                            locator.binding_index(),\n                            namespace.into(),\n                            global,\n                        );\n                    }\n                }\n                ImportBinding::Single {\n                    locator,\n                    export_locator,\n                } => match export_locator.binding_name() {\n                    BindingName::Name(name) => {\n                        let frame = context.vm.frame();\n                        frame\n                            .environments\n                            .current_declarative_ref(frame.realm.environment())\n                            .js_expect(\"must be declarative\")?\n                            .kind()\n                            .as_module()\n                            .js_expect(\"last environment should be the module env\")?\n                            .set_indirect(\n                                locator.binding_index(),\n                                export_locator.module().clone(),\n                                name.clone(),\n                            );\n                    }\n                    BindingName::Namespace => {\n                        let namespace = export_locator.module.namespace(context);\n                        let frame = context.vm.frame_mut();\n                        let global = frame.realm.environment();\n                        frame.environments.put_lexical_value(\n                            locator.scope(),\n                            locator.binding_index(),\n                            namespace.into(),\n                            global,\n                        );\n                    }\n                },\n            }\n        }\n\n        // deferred initialization of function exports\n        for (index, locator) in functions {\n            let code = codeblock.constant_function(index as usize);\n\n            let function = create_function_object_fast(code, context);\n\n            {\n                let frame = context.vm.frame_mut();\n                let global = frame.realm.environment();\n                frame.environments.put_lexical_value(\n                    locator.scope(),\n                    locator.binding_index(),\n                    function.into(),\n                    global,\n                );\n            }\n        }\n\n        // 25. Remove moduleContext from the execution context stack.\n        let frame = context\n            .vm\n            .pop_frame()\n            .js_expect(\"There should be a call frame\")?;\n\n        let env = frame\n            .environments\n            .current_declarative_ref(frame.realm.environment())\n            .cloned()\n            .js_expect(\"frame must have a declarative environment\")?;\n\n        // 16. Set module.[[Context]] to moduleContext.\n        self.status.borrow_mut().transition(|state| match state {\n            ModuleStatus::Linking { info, .. } => ModuleStatus::PreLinked {\n                environment: env,\n                info,\n                context: SourceTextContext {\n                    codeblock,\n                    environments: frame.environments.clone(),\n                    realm,\n                },\n            },\n            _ => unreachable!(\n                \"should only transition to the `PreLinked` state from the `Linking` state\"\n            ),\n        });\n\n        // 26. Return unused.\n        Ok(())\n    }\n\n    /// Abstract operation [`ExecuteModule ( [ capability ] )`][spec].\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-source-text-module-record-execute-module\n    fn execute(\n        &self,\n        module_self: &Module,\n        capability: Option<PromiseCapability>,\n        context: &mut Context,\n    ) -> JsResult<()> {\n        // 1. Let moduleContext be a new ECMAScript code execution context.\n        let SourceTextContext {\n            codeblock,\n            environments,\n            realm,\n        } = match &*self.status.borrow() {\n            ModuleStatus::Evaluating { context, .. }\n            | ModuleStatus::EvaluatingAsync { context, .. } => context.clone(),\n            _ => unreachable!(\"`execute` should only be called for evaluating modules.\"),\n        };\n\n        // 2. Set the Function of moduleContext to null.\n        // 3. Set the Realm of moduleContext to module.[[Realm]].\n        // 4. Set the ScriptOrModule of moduleContext to module.\n        // 5. Assert: module has been linked and declarations in its module environment have been instantiated.\n        // 6. Set the VariableEnvironment of moduleContext to module.[[Environment]].\n        // 7. Set the LexicalEnvironment of moduleContext to module.[[Environment]].\n        let env_fp = environments.len() as u32;\n        let callframe = CallFrame::new(\n            codeblock,\n            Some(ActiveRunnable::Module(module_self.clone())),\n            environments,\n            realm,\n        )\n        .with_env_fp(env_fp)\n        .with_flags(CallFrameFlags::EXIT_EARLY);\n\n        // 8. Suspend the running execution context.\n        context\n            .vm\n            .push_frame_with_stack(callframe, JsValue::undefined(), JsValue::null());\n\n        if let Some(capability) = capability {\n            context.vm.set_promise_capability(capability)?;\n        }\n\n        // 9. If module.[[HasTLA]] is false, then\n        //    a. Assert: capability is not present.\n        //    b. Push moduleContext onto the execution context stack; moduleContext is now the running execution context.\n        //    c. Let result be Completion(Evaluation of module.[[ECMAScriptCode]]).\n        //    d. Suspend moduleContext and remove it from the execution context stack.\n        //    e. Resume the context that is now on the top of the execution context stack as the running execution context.\n        // 10. Else,\n        //    a. Assert: capability is a PromiseCapability Record.\n        //    b. Perform AsyncBlockStart(capability, module.[[ECMAScriptCode]], moduleContext).\n        let result = context.run();\n\n        context.vm.pop_frame();\n\n        //     f. If result is an abrupt completion, then\n        if let CompletionRecord::Throw(err) = result {\n            //    i. Return ? result.\n            Err(err)\n        } else {\n            // 11. Return unused.\n            Ok(())\n        }\n    }\n\n    /// Gets the loaded modules of this module.\n    pub(crate) fn loaded_modules(&self) -> &GcRefCell<FxHashMap<super::ModuleRequest, Module>> {\n        &self.loaded_modules\n    }\n\n    /// Gets the import meta object of this module, or initializes\n    /// it using the provided callback.\n    pub(crate) fn import_meta(&self) -> &GcRefCell<Option<JsObject>> {\n        &self.import_meta\n    }\n\n    /// Gets the declarative environment of this module.\n    pub(crate) fn environment(&self) -> Option<Gc<DeclarativeEnvironment>> {\n        self.status.borrow().environment()\n    }\n}\n\n/// Abstract operation [`AsyncModuleExecutionFulfilled ( module )`][spec].\n///\n/// [spec]: https://tc39.es/ecma262/#sec-async-module-execution-fulfilled\n#[allow(clippy::mutable_key_type)]\nfn async_module_execution_fulfilled(module: &Module, context: &mut Context) -> JsResult<()> {\n    let ModuleKind::SourceText(module_src) = module.kind() else {\n        unreachable!(\"async executed module must be a source text module\");\n    };\n\n    // 1. If module.[[Status]] is evaluated, then\n    if let ModuleStatus::Evaluated { error, .. } = &*module_src.status.borrow() {\n        //     a. Assert: module.[[EvaluationError]] is not empty.\n        assert!(error.is_some());\n        //     b. Return unused.\n        return Ok(());\n    }\n\n    // 2. Assert: module.[[Status]] is evaluating-async.\n    // 3. Assert: module.[[AsyncEvaluation]] is true.\n    // 4. Assert: module.[[EvaluationError]] is empty.\n    // 5. Set module.[[AsyncEvaluation]] to false.\n    // 6. Set module.[[Status]] to evaluated.\n    module_src\n        .status\n        .borrow_mut()\n        .transition(|status| match status {\n            ModuleStatus::EvaluatingAsync {\n                environment,\n                top_level_capability,\n                cycle_root,\n                ..\n            } => ModuleStatus::Evaluated {\n                environment,\n                top_level_capability,\n                cycle_root,\n                error: None,\n            },\n            _ => unreachable!(),\n        });\n\n    // 7. If module.[[TopLevelCapability]] is not empty, then\n    if let Some(cap) = module_src.status.borrow().top_level_capability() {\n        // a. Assert: module.[[CycleRoot]] is module.\n        debug_assert_eq!(module_src.status.borrow().cycle_root(), Some(module));\n\n        // b. Perform ! Call(module.[[TopLevelCapability]].[[Resolve]], undefined, « undefined »).\n        cap.resolve()\n            .call(&JsValue::undefined(), &[], context)\n            .js_expect(\"default `resolve` function cannot fail\")?;\n    }\n\n    // 8. Let execList be a new empty List.\n    let mut ancestors = FxHashSet::default();\n\n    // 9. Perform GatherAvailableAncestors(module, execList).\n    module_src.gather_available_ancestors(&mut ancestors);\n\n    // 11. Assert: All elements of sortedExecList have their [[AsyncEvaluation]] field set to true, [[PendingAsyncDependencies]] field set to 0, and [[EvaluationError]] field set to empty.\n    let mut ancestors = ancestors.into_iter().collect::<Vec<_>>();\n\n    // 10. Let sortedExecList be a List whose elements are the elements of execList, in the order in which they had their [[AsyncEvaluation]] fields set to true in InnerModuleEvaluation.\n    ancestors.sort_by_cached_key(|m| {\n        let ModuleKind::SourceText(m_src) = m.kind() else {\n            unreachable!(\"ancestors must only be source text modules\");\n        };\n\n        let ModuleStatus::EvaluatingAsync {\n            async_eval_index, ..\n        } = &*m_src.status.borrow()\n        else {\n            unreachable!(\"GatherAvailableAncestors: i. Assert: m.[[Status]] is evaluating-async.\");\n        };\n\n        *async_eval_index\n    });\n\n    // 12. For each Cyclic Module Record m of sortedExecList, do\n    for m in ancestors {\n        let ModuleKind::SourceText(m_src) = m.kind() else {\n            continue;\n        };\n\n        // a. If m.[[Status]] is evaluated, then\n        if let ModuleStatus::Evaluated { error, .. } = &*m_src.status.borrow() {\n            // i. Assert: m.[[EvaluationError]] is not empty.\n            assert!(error.is_some());\n            continue;\n        }\n\n        // b. Else if m.[[HasTLA]] is true, then\n        let has_tla = m_src.code.has_tla;\n        if has_tla {\n            // i. Perform ExecuteAsyncModule(m).\n            m_src.execute_async(&m, context);\n        } else {\n            // c. Else,\n            //    i. Let result be m.ExecuteModule().\n            let result = m_src.execute(&m, None, context);\n\n            //    ii. If result is an abrupt completion, then\n            if let Err(e) = result {\n                //    1. Perform AsyncModuleExecutionRejected(m, result.[[Value]]).\n                async_module_execution_rejected(module, e, context)?;\n            } else {\n                // iii. Else,\n                //    1. Set m.[[Status]] to evaluated.\n                m_src.status.borrow_mut().transition(|status| match status {\n                    ModuleStatus::EvaluatingAsync {\n                        environment,\n                        top_level_capability,\n                        cycle_root,\n                        ..\n                    } => ModuleStatus::Evaluated {\n                        environment,\n                        top_level_capability,\n                        cycle_root,\n                        error: None,\n                    },\n                    _ => unreachable!(),\n                });\n\n                let status = m_src.status.borrow();\n                // 2. If m.[[TopLevelCapability]] is not empty, then\n                if let Some(cap) = status.top_level_capability() {\n                    // a. Assert: m.[[CycleRoot]] is m.\n                    assert_eq!(status.cycle_root(), Some(&m));\n\n                    // b. Perform ! Call(m.[[TopLevelCapability]].[[Resolve]], undefined, « undefined »).\n                    cap.resolve()\n                        .call(&JsValue::undefined(), &[], context)\n                        .js_expect(\"default `resolve` function cannot fail\")?;\n                }\n            }\n        }\n    }\n    // 13. Return unused.\n    Ok(())\n}\n\n/// Abstract operation [`AsyncModuleExecutionRejected ( module, error )`][spec].\n///\n/// [spec]: https://tc39.es/ecma262/#sec-async-module-execution-rejected\nfn async_module_execution_rejected(\n    module: &Module,\n    error: JsError,\n    context: &mut Context,\n) -> JsResult<()> {\n    let ModuleKind::SourceText(module_src) = module.kind() else {\n        unreachable!(\"async executed module must be a source text module\");\n    };\n    // 1. If module.[[Status]] is evaluated, then\n    if let ModuleStatus::Evaluated { error, .. } = &*module_src.status.borrow() {\n        //     a. Assert: module.[[EvaluationError]] is not empty.\n        assert!(error.is_some());\n        //     b. Return unused.\n        return Ok(());\n    }\n\n    // 2. Assert: module.[[Status]] is evaluating-async.\n    // 3. Assert: module.[[AsyncEvaluation]] is true.\n    // 4. Assert: module.[[EvaluationError]] is empty.\n    // 5. Set module.[[EvaluationError]] to ThrowCompletion(error).\n    // 6. Set module.[[Status]] to evaluated.\n    module_src\n        .status\n        .borrow_mut()\n        .transition(|status| match status {\n            ModuleStatus::EvaluatingAsync {\n                environment,\n                top_level_capability,\n                cycle_root,\n                ..\n            } => ModuleStatus::Evaluated {\n                environment,\n                top_level_capability,\n                cycle_root,\n                error: Some(error.clone()),\n            },\n            _ => unreachable!(),\n        });\n\n    // 7. For each Cyclic Module Record m of module.[[AsyncParentModules]], do\n    for m in std::mem::take(&mut *module_src.async_parent_modules.borrow_mut()) {\n        // a. Perform AsyncModuleExecutionRejected(m, error).\n        async_module_execution_rejected(&m, error.clone(), context)?;\n    }\n\n    let status = module_src.status.borrow();\n    // 8. If module.[[TopLevelCapability]] is not empty, then\n    if let Some(cap) = status.top_level_capability() {\n        // a. Assert: module.[[CycleRoot]] is module.\n        assert_eq!(status.cycle_root(), Some(module));\n\n        // b. Perform ! Call(module.[[TopLevelCapability]].[[Reject]], undefined, « error »).\n        cap.reject()\n            .call(\n                &JsValue::undefined(),\n                &[error.into_opaque(context)?],\n                context,\n            )\n            .js_expect(\"default `reject` function cannot fail\")?;\n    }\n    // 9. Return unused.\n    Ok(())\n}\n"
  },
  {
    "path": "core/engine/src/module/synthetic.rs",
    "content": "use boa_ast::scope::Scope;\nuse boa_gc::{Finalize, Gc, GcRefCell, Trace};\nuse rustc_hash::FxHashSet;\n\nuse super::{BindingName, ResolveExportError, ResolvedBinding};\nuse crate::{\n    Context, JsExpect, JsNativeError, JsResult, JsString, JsValue, Module, SpannedSourceText,\n    builtins::promise::ResolvingFunctions,\n    bytecompiler::ByteCompiler,\n    class::{Class, ClassBuilder},\n    environments::{DeclarativeEnvironment, EnvironmentStack},\n    js_string,\n    object::JsPromise,\n    vm::{ActiveRunnable, CallFrame, CodeBlock, source_info::SourcePath},\n};\n\ntrait TraceableCallback: Trace {\n    fn call(&self, module: &SyntheticModule, context: &mut Context) -> JsResult<()>;\n}\n\n#[derive(Trace, Finalize)]\nstruct Callback<F, T>\nwhere\n    F: Fn(&SyntheticModule, &T, &mut Context) -> JsResult<()>,\n    T: Trace,\n{\n    // SAFETY: `SyntheticModuleInitializer`'s safe API ensures only `Copy` closures are stored; its unsafe API,\n    // on the other hand, explains the invariants to hold in order for this to be safe, shifting\n    // the responsibility to the caller.\n    #[unsafe_ignore_trace]\n    f: F,\n    captures: T,\n}\n\nimpl<F, T> TraceableCallback for Callback<F, T>\nwhere\n    F: Fn(&SyntheticModule, &T, &mut Context) -> JsResult<()>,\n    T: Trace,\n{\n    fn call(&self, module: &SyntheticModule, context: &mut Context) -> JsResult<()> {\n        (self.f)(module, &self.captures, context)\n    }\n}\n\n/// The initializing steps of a [`SyntheticModule`].\n///\n/// # Caveats\n///\n/// By limitations of the Rust language, the garbage collector currently cannot inspect closures\n/// in order to trace their captured variables. This means that only [`Copy`] closures are 100% safe\n/// to use. All other closures can also be stored in a `NativeFunction`, albeit by using an `unsafe`\n/// API, but note that passing closures implicitly capturing traceable types could cause\n/// **Undefined Behaviour**.\n#[derive(Clone, Trace, Finalize)]\npub struct SyntheticModuleInitializer {\n    inner: Gc<dyn TraceableCallback>,\n}\n\nimpl std::fmt::Debug for SyntheticModuleInitializer {\n    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {\n        f.debug_struct(\"SyntheticModuleInitializer\")\n            .finish_non_exhaustive()\n    }\n}\n\nimpl SyntheticModuleInitializer {\n    /// Creates a `SyntheticModuleInitializer` from a [`Copy`] closure.\n    pub fn from_copy_closure<F>(closure: F) -> Self\n    where\n        F: Fn(&SyntheticModule, &mut Context) -> JsResult<()> + Copy + 'static,\n    {\n        // SAFETY: The `Copy` bound ensures there are no traceable types inside the closure.\n        unsafe { Self::from_closure(closure) }\n    }\n\n    /// Creates a `SyntheticModuleInitializer` from a [`Copy`] closure and a list of traceable captures.\n    pub fn from_copy_closure_with_captures<F, T>(closure: F, captures: T) -> Self\n    where\n        F: Fn(&SyntheticModule, &T, &mut Context) -> JsResult<()> + Copy + 'static,\n        T: Trace + 'static,\n    {\n        // SAFETY: The `Copy` bound ensures there are no traceable types inside the closure.\n        unsafe { Self::from_closure_with_captures(closure, captures) }\n    }\n\n    /// Creates a new `SyntheticModuleInitializer` from a closure.\n    ///\n    /// # Safety\n    ///\n    /// Passing a closure that contains a captured variable that needs to be traced by the garbage\n    /// collector could cause an use after free, memory corruption or other kinds of **Undefined\n    /// Behaviour**. See <https://github.com/Manishearth/rust-gc/issues/50> for a technical explanation\n    /// on why that is the case.\n    pub unsafe fn from_closure<F>(closure: F) -> Self\n    where\n        F: Fn(&SyntheticModule, &mut Context) -> JsResult<()> + 'static,\n    {\n        // SAFETY: The caller must ensure the invariants of the closure hold.\n        unsafe {\n            Self::from_closure_with_captures(\n                move |module, (), context| closure(module, context),\n                (),\n            )\n        }\n    }\n\n    /// Create a new `SyntheticModuleInitializer` from a closure and a list of traceable captures.\n    ///\n    /// # Safety\n    ///\n    /// Passing a closure that contains a captured variable that needs to be traced by the garbage\n    /// collector could cause an use after free, memory corruption or other kinds of **Undefined\n    /// Behaviour**. See <https://github.com/Manishearth/rust-gc/issues/50> for a technical explanation\n    /// on why that is the case.\n    pub unsafe fn from_closure_with_captures<F, T>(closure: F, captures: T) -> Self\n    where\n        F: Fn(&SyntheticModule, &T, &mut Context) -> JsResult<()> + 'static,\n        T: Trace + 'static,\n    {\n        // Hopefully, this unsafe operation will be replaced by the `CoerceUnsized` API in the\n        // future: https://github.com/rust-lang/rust/issues/18598\n        let ptr = Gc::into_raw(Gc::new(Callback {\n            f: closure,\n            captures,\n        }));\n\n        // SAFETY: The pointer returned by `into_raw` is only used to coerce to a trait object,\n        // meaning this is safe.\n        unsafe {\n            Self {\n                inner: Gc::from_raw(ptr),\n            }\n        }\n    }\n\n    /// Calls this `SyntheticModuleInitializer`, forwarding the arguments to the corresponding function.\n    #[inline]\n    pub(crate) fn call(&self, module: &SyntheticModule, context: &mut Context) -> JsResult<()> {\n        self.inner.call(module, context)\n    }\n}\n\n/// Current status of a [`SyntheticModule`].\n#[derive(Debug, Trace, Finalize, Default)]\n#[boa_gc(unsafe_no_drop)]\nenum ModuleStatus {\n    #[default]\n    Unlinked,\n    Linked {\n        environment: Gc<DeclarativeEnvironment>,\n        eval_context: (EnvironmentStack, Gc<CodeBlock>),\n    },\n    Evaluated {\n        environment: Gc<DeclarativeEnvironment>,\n        promise: JsPromise,\n    },\n}\n\nimpl ModuleStatus {\n    /// Transition from one state to another, taking the current state by value to move data\n    /// between states.\n    fn transition<F>(&mut self, f: F)\n    where\n        F: FnOnce(Self) -> Self,\n    {\n        *self = f(std::mem::take(self));\n    }\n}\n\n/// ECMAScript's [**Synthetic Module Records**][spec].\n///\n/// [spec]: https://tc39.es/proposal-json-modules/#sec-synthetic-module-records\n#[derive(Trace, Finalize)]\npub struct SyntheticModule {\n    #[unsafe_ignore_trace]\n    export_names: FxHashSet<JsString>,\n    eval_steps: SyntheticModuleInitializer,\n    state: GcRefCell<ModuleStatus>,\n}\n\nimpl std::fmt::Debug for SyntheticModule {\n    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {\n        f.debug_struct(\"SyntheticModule\")\n            .field(\"export_names\", &self.export_names)\n            .field(\"eval_steps\", &self.eval_steps)\n            .finish_non_exhaustive()\n    }\n}\n\nimpl SyntheticModule {\n    /// Abstract operation [`SetSyntheticModuleExport ( module, exportName, exportValue )`][spec].\n    ///\n    /// Sets or changes the exported value for `exportName` in the synthetic module.\n    ///\n    /// # Note\n    ///\n    /// The default export corresponds to the name `\"default\"`, but note that it needs to\n    /// be passed to the list of exported names in [`Module::synthetic`] beforehand.\n    ///\n    /// [spec]: https://tc39.es/proposal-json-modules/#sec-createsyntheticmodule\n    pub fn set_export(&self, export_name: &JsString, export_value: JsValue) -> JsResult<()> {\n        let env = self.environment().ok_or_else(|| {\n            JsNativeError::typ().with_message(format!(\n                \"cannot set name `{}` in an unlinked synthetic module\",\n                export_name.to_std_string_escaped()\n            ))\n        })?;\n        let locator = env\n            .kind()\n            .as_module()\n            .js_expect(\"must be module environment\")?\n            .compile()\n            .get_binding(export_name)\n            .ok_or_else(|| {\n                JsNativeError::reference().with_message(format!(\n                    \"cannot set name `{}` which was not included in the list of exports\",\n                    export_name.to_std_string_escaped()\n                ))\n            })?;\n        env.set(locator.binding_index(), export_value);\n\n        Ok(())\n    }\n\n    /// Sets or changes the exported value for `C::NAME` in the synthetic module\n    /// to the Class's constructor.\n    pub fn export_class<C: Class>(&self, context: &mut Context) -> JsResult<()> {\n        self.export_named_class::<C>(&JsString::from(C::NAME), context)\n    }\n\n    /// Sets or changes the exported value for `export_name` in the synthetic module\n    /// to the Class's constructor.\n    pub fn export_named_class<C: Class>(\n        &self,\n        export_name: &JsString,\n        context: &mut Context,\n    ) -> JsResult<()> {\n        let mut class_builder = ClassBuilder::new::<C>(context);\n        C::init(&mut class_builder)?;\n\n        let class = class_builder.build();\n\n        self.set_export(export_name, class.constructor().into())?;\n        Ok(())\n    }\n\n    /// Creates a new synthetic module.\n    pub(super) fn new(names: FxHashSet<JsString>, eval_steps: SyntheticModuleInitializer) -> Self {\n        Self {\n            export_names: names,\n            eval_steps,\n            state: GcRefCell::default(),\n        }\n    }\n\n    /// Concrete method [`LoadRequestedModules ( )`][spec].\n    ///\n    /// [spec]: https://tc39.es/proposal-json-modules/#sec-smr-LoadRequestedModules\n    pub(super) fn load(context: &mut Context) -> JsPromise {\n        JsPromise::resolve(JsValue::undefined(), context)\n            .expect(\"default resolve functions cannot throw and must return a promise\")\n    }\n\n    /// Concrete method [`GetExportedNames ( [ exportStarSet ] )`][spec].\n    ///\n    /// [spec]: https://tc39.es/proposal-json-modules/#sec-smr-getexportednames\n    pub(super) fn get_exported_names(&self) -> FxHashSet<JsString> {\n        // 1. Return module.[[ExportNames]].\n        self.export_names.clone()\n    }\n\n    /// Concrete method [`ResolveExport ( exportName )`][spec]\n    ///\n    /// [spec]: https://tc39.es/proposal-json-modules/#sec-smr-resolveexport\n    #[allow(clippy::mutable_key_type)]\n    pub(super) fn resolve_export(\n        &self,\n        module_self: &Module,\n        export_name: &JsString,\n    ) -> Result<ResolvedBinding, ResolveExportError> {\n        if self.export_names.contains(export_name) {\n            // 2. Return ResolvedBinding Record { [[Module]]: module, [[BindingName]]: exportName }.\n            Ok(ResolvedBinding {\n                module: module_self.clone(),\n                binding_name: BindingName::Name(export_name.clone()),\n            })\n        } else {\n            // 1. If module.[[ExportNames]] does not contain exportName, return null.\n            Err(ResolveExportError::NotFound)\n        }\n    }\n\n    /// Concrete method [`Link ( )`][spec].\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-moduledeclarationlinking\n    pub(super) fn link(&self, module_self: &Module, context: &mut Context) {\n        if !matches!(&*self.state.borrow(), ModuleStatus::Unlinked) {\n            // Already linked and/or evaluated.\n            return;\n        }\n\n        // 1. Let realm be module.[[Realm]].\n        // 2. Let env be NewModuleEnvironment(realm.[[GlobalEnv]]).\n        // 3. Set module.[[Environment]] to env.\n        let global_env = module_self.realm().environment().clone();\n        let global_scope = module_self.realm().scope().clone();\n        let module_scope = Scope::new(global_scope, true);\n\n        // TODO: A bit of a hack to be able to pass the currently active runnable without an\n        // available codeblock to execute.\n        let compiler = ByteCompiler::new(\n            js_string!(\"<synthetic>\"),\n            true,\n            false,\n            module_scope.clone(),\n            module_scope.clone(),\n            false,\n            false,\n            context.interner_mut(),\n            false,\n            // A synthetic module does not contain `SourceText`\n            SpannedSourceText::new_empty(),\n            SourcePath::None,\n        );\n\n        // 4. For each String exportName in module.[[ExportNames]], do\n        let exports = self\n            .export_names\n            .iter()\n            .map(|name| {\n                //     a. Perform ! env.CreateMutableBinding(exportName, false).\n                module_scope.create_mutable_binding(name.clone(), false)\n            })\n            .collect::<Vec<_>>();\n\n        module_scope.escape_all_bindings();\n\n        let cb = Gc::new(compiler.finish());\n\n        let mut envs = EnvironmentStack::new();\n        envs.push_module(module_scope);\n\n        for locator in exports {\n            //     b. Perform ! env.InitializeBinding(exportName, undefined).\n            envs.put_lexical_value(\n                locator.scope(),\n                locator.binding_index(),\n                JsValue::undefined(),\n                &global_env,\n            );\n        }\n\n        let env = envs\n            .current_declarative_ref(&global_env)\n            .cloned()\n            .expect(\"should have the module environment\");\n\n        self.state\n            .borrow_mut()\n            .transition(|_| ModuleStatus::Linked {\n                environment: env,\n                eval_context: (envs, cb),\n            });\n\n        // 5. Return unused.\n    }\n\n    /// Concrete method [`Evaluate ( )`][spec].\n    ///\n    /// [spec]: https://tc39.es/proposal-json-modules/#sec-smr-Evaluate\n    pub(super) fn evaluate(\n        &self,\n        module_self: &Module,\n        context: &mut Context,\n    ) -> JsResult<JsPromise> {\n        let (environments, codeblock) = match &*self.state.borrow() {\n            ModuleStatus::Unlinked => {\n                let (promise, ResolvingFunctions { reject, .. }) = JsPromise::new_pending(context);\n                reject\n                    .call(\n                        &JsValue::undefined(),\n                        &[JsNativeError::typ()\n                            .with_message(\"cannot evaluate unlinked synthetic module\")\n                            .into_opaque(context)\n                            .into()],\n                        context,\n                    )\n                    .js_expect(\"native resolving functions cannot throw\")?;\n                return Ok(promise);\n            }\n            ModuleStatus::Linked { eval_context, .. } => eval_context.clone(),\n            ModuleStatus::Evaluated { promise, .. } => return Ok(promise.clone()),\n        };\n        // 1. Let moduleContext be a new ECMAScript code execution context.\n\n        let realm = module_self.realm().clone();\n\n        let env_fp = environments.len() as u32;\n        let callframe = CallFrame::new(\n            codeblock,\n            // 4. Set the ScriptOrModule of moduleContext to module.\n            Some(ActiveRunnable::Module(module_self.clone())),\n            // 5. Set the VariableEnvironment of moduleContext to module.[[Environment]].\n            // 6. Set the LexicalEnvironment of moduleContext to module.[[Environment]].\n            environments,\n            // 3. Set the Realm of moduleContext to module.[[Realm]].\n            realm,\n        )\n        .with_env_fp(env_fp);\n\n        // 2. Set the Function of moduleContext to null.\n        // 7. Suspend the currently running execution context.\n        // 8. Push moduleContext on to the execution context stack; moduleContext is now the running execution context.\n        context\n            .vm\n            .push_frame_with_stack(callframe, JsValue::undefined(), JsValue::null());\n\n        // 9. Let steps be module.[[EvaluationSteps]].\n        // 10. Let result be Completion(steps(module)).\n        let result = self.eval_steps.call(self, context);\n\n        // 11. Suspend moduleContext and remove it from the execution context stack.\n        // 12. Resume the context that is now on the top of the execution context stack as the running execution context.\n        let frame = context\n            .vm\n            .pop_frame()\n            .js_expect(\"there should be a frame\")?;\n        context.vm.stack.truncate_to_frame(&frame);\n\n        // 13. Let pc be ! NewPromiseCapability(%Promise%).\n        let (promise, ResolvingFunctions { resolve, reject }) = JsPromise::new_pending(context);\n\n        match result {\n            // 15. Perform ! pc.[[Resolve]](result).\n            Ok(()) => resolve.call(&JsValue::undefined(), &[], context),\n            // 14. IfAbruptRejectPromise(result, pc).\n            Err(err) => reject.call(&JsValue::undefined(), &[err.into_opaque(context)?], context),\n        }\n        .js_expect(\"default resolving functions cannot throw\")?;\n\n        self.state.borrow_mut().transition(|state| match state {\n            ModuleStatus::Linked { environment, .. } => ModuleStatus::Evaluated {\n                environment,\n                promise: promise.clone(),\n            },\n            _ => unreachable!(\"checks above ensure the module is linked\"),\n        });\n\n        // 16. Return pc.[[Promise]].\n        Ok(promise)\n    }\n\n    pub(crate) fn environment(&self) -> Option<Gc<DeclarativeEnvironment>> {\n        match &*self.state.borrow() {\n            ModuleStatus::Unlinked => None,\n            ModuleStatus::Linked { environment, .. }\n            | ModuleStatus::Evaluated { environment, .. } => Some(environment.clone()),\n        }\n    }\n}\n"
  },
  {
    "path": "core/engine/src/native_function/continuation.rs",
    "content": "use boa_gc::{Finalize, Gc, Trace};\n\nuse crate::{Context, JsResult, JsValue};\n\n#[derive(Trace, Finalize)]\n#[boa_gc(unsafe_no_drop)]\npub(crate) enum CoroutineState {\n    Yielded(JsValue),\n    Done,\n}\n\ntrait TraceableCoroutine: Trace {\n    fn call(&self, value: JsResult<JsValue>, context: &mut Context) -> JsResult<CoroutineState>;\n}\n\n#[derive(Trace, Finalize)]\nstruct Coroutine<F, T>\nwhere\n    F: Fn(JsResult<JsValue>, &T, &mut Context) -> JsResult<CoroutineState>,\n    T: Trace,\n{\n    // SAFETY: `NativeCoroutine`'s safe API ensures only `Copy` closures are stored; its unsafe API,\n    // on the other hand, explains the invariants to hold in order for this to be safe, shifting\n    // the responsibility to the caller.\n    #[unsafe_ignore_trace]\n    f: F,\n    captures: T,\n}\n\nimpl<F, T> TraceableCoroutine for Coroutine<F, T>\nwhere\n    F: Fn(JsResult<JsValue>, &T, &mut Context) -> JsResult<CoroutineState>,\n    T: Trace,\n{\n    fn call(&self, result: JsResult<JsValue>, context: &mut Context) -> JsResult<CoroutineState> {\n        (self.f)(result, &self.captures, context)\n    }\n}\n\n/// A callable Rust coroutine that can be used to await promises.\n///\n/// # Caveats\n///\n/// By limitations of the Rust language, the garbage collector currently cannot inspect closures\n/// in order to trace their captured variables. This means that only [`Copy`] closures are 100% safe\n/// to use. All other closures can also be stored in a `NativeCoroutine`, albeit by using an `unsafe`\n/// API, but note that passing closures implicitly capturing traceable types could cause\n/// **Undefined Behaviour**.\n#[derive(Clone, Trace, Finalize)]\npub(crate) struct NativeCoroutine {\n    inner: Gc<dyn TraceableCoroutine>,\n}\n\nimpl std::fmt::Debug for NativeCoroutine {\n    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {\n        f.debug_struct(\"NativeCoroutine\").finish_non_exhaustive()\n    }\n}\n\nimpl NativeCoroutine {\n    /// Creates a `NativeCoroutine` from a `Copy` closure and a list of traceable captures.\n    pub(crate) fn from_copy_closure_with_captures<F, T>(closure: F, captures: T) -> Self\n    where\n        F: Fn(JsResult<JsValue>, &T, &mut Context) -> JsResult<CoroutineState> + Copy + 'static,\n        T: Trace + 'static,\n    {\n        // SAFETY: The `Copy` bound ensures there are no traceable types inside the closure.\n        unsafe { Self::from_closure_with_captures(closure, captures) }\n    }\n\n    /// Create a new `NativeCoroutine` from a closure and a list of traceable captures.\n    ///\n    /// # Safety\n    ///\n    /// Passing a closure that contains a captured variable that needs to be traced by the garbage\n    /// collector could cause an use after free, memory corruption or other kinds of **Undefined\n    /// Behaviour**. See <https://github.com/Manishearth/rust-gc/issues/50> for a technical explanation\n    /// on why that is the case.\n    pub(crate) unsafe fn from_closure_with_captures<F, T>(closure: F, captures: T) -> Self\n    where\n        F: Fn(JsResult<JsValue>, &T, &mut Context) -> JsResult<CoroutineState> + 'static,\n        T: Trace + 'static,\n    {\n        // Hopefully, this unsafe operation will be replaced by the `CoerceUnsized` API in the\n        // future: https://github.com/rust-lang/rust/issues/18598\n        let ptr = Gc::into_raw(Gc::new(Coroutine {\n            f: closure,\n            captures,\n        }));\n        // SAFETY: The pointer returned by `into_raw` is only used to coerce to a trait object,\n        // meaning this is safe.\n        unsafe {\n            Self {\n                inner: Gc::from_raw(ptr),\n            }\n        }\n    }\n\n    /// Calls this `NativeCoroutine`, forwarding the arguments to the corresponding function.\n    #[inline]\n    pub(crate) fn call(\n        &self,\n        result: JsResult<JsValue>,\n        context: &mut Context,\n    ) -> JsResult<CoroutineState> {\n        self.inner.call(result, context)\n    }\n}\n"
  },
  {
    "path": "core/engine/src/native_function/mod.rs",
    "content": "//! Boa's wrappers for native Rust functions to be compatible with ECMAScript calls.\n//!\n//! [`NativeFunction`] is the main type of this module, providing APIs to create native callables\n//! from native Rust functions and closures.\n\nuse std::cell::RefCell;\n\nuse boa_gc::{Finalize, Gc, Trace, custom_trace};\nuse boa_string::JsString;\n\nuse crate::job::NativeAsyncJob;\nuse crate::object::internal_methods::InternalMethodCallContext;\nuse crate::value::JsVariant;\nuse crate::{\n    Context, JsNativeError, JsObject, JsResult, JsValue,\n    builtins::{OrdinaryObject, function::ConstructorKind},\n    context::intrinsics::StandardConstructors,\n    object::{\n        FunctionObjectBuilder, JsData, JsFunction, JsPromise,\n        internal_methods::{\n            CallValue, InternalObjectMethods, ORDINARY_INTERNAL_METHODS,\n            get_prototype_from_constructor,\n        },\n    },\n    realm::Realm,\n};\n\n#[cfg(feature = \"experimental\")]\nmod continuation;\n\n#[cfg(feature = \"experimental\")]\npub(crate) use continuation::{CoroutineState, NativeCoroutine};\n\n/// The required signature for all native built-in function pointers.\n///\n/// # Arguments\n///\n/// - The first argument represents the `this` variable of every ECMAScript function.\n///\n/// - The second argument represents the list of all arguments passed to the function.\n///\n/// - The last argument is the engine [`Context`].\npub type NativeFunctionPointer = fn(&JsValue, &[JsValue], &mut Context) -> JsResult<JsValue>;\n\ntrait TraceableClosure: Trace {\n    fn call(&self, this: &JsValue, args: &[JsValue], context: &mut Context) -> JsResult<JsValue>;\n}\n\n#[derive(Trace, Finalize)]\nstruct Closure<F, T>\nwhere\n    F: Fn(&JsValue, &[JsValue], &T, &mut Context) -> JsResult<JsValue>,\n    T: Trace,\n{\n    // SAFETY: `NativeFunction`'s safe API ensures only `Copy` closures are stored; its unsafe API,\n    // on the other hand, explains the invariants to hold in order for this to be safe, shifting\n    // the responsibility to the caller.\n    #[unsafe_ignore_trace]\n    f: F,\n    captures: T,\n}\n\nimpl<F, T> TraceableClosure for Closure<F, T>\nwhere\n    F: Fn(&JsValue, &[JsValue], &T, &mut Context) -> JsResult<JsValue>,\n    T: Trace,\n{\n    fn call(&self, this: &JsValue, args: &[JsValue], context: &mut Context) -> JsResult<JsValue> {\n        (self.f)(this, args, &self.captures, context)\n    }\n}\n\n#[derive(Clone, Debug, Finalize)]\n/// The data of an object containing a `NativeFunction`.\npub struct NativeFunctionObject {\n    /// The rust function.\n    pub(crate) f: NativeFunction,\n\n    /// JavaScript name of the function.\n    pub(crate) name: JsString,\n\n    /// The kind of the function constructor if it is a constructor.\n    pub(crate) constructor: Option<ConstructorKind>,\n\n    /// The [`Realm`] in which the function is defined, or `None` if the realm is uninitialized.\n    pub(crate) realm: Option<Realm>,\n}\n\n// SAFETY: this traces all fields that need to be traced by the GC.\nunsafe impl Trace for NativeFunctionObject {\n    custom_trace!(this, mark, {\n        mark(&this.f);\n        mark(&this.realm);\n    });\n}\n\nimpl JsData for NativeFunctionObject {\n    fn internal_methods(&self) -> &'static InternalObjectMethods {\n        static FUNCTION: InternalObjectMethods = InternalObjectMethods {\n            __call__: native_function_call,\n            ..ORDINARY_INTERNAL_METHODS\n        };\n\n        static CONSTRUCTOR: InternalObjectMethods = InternalObjectMethods {\n            __call__: native_function_call,\n            __construct__: native_function_construct,\n            ..ORDINARY_INTERNAL_METHODS\n        };\n\n        if self.constructor.is_some() {\n            &CONSTRUCTOR\n        } else {\n            &FUNCTION\n        }\n    }\n}\n\n/// A callable Rust function that can be invoked by the engine.\n///\n/// `NativeFunction` functions are divided in two:\n/// - Function pointers a.k.a common functions (see [`NativeFunctionPointer`]).\n/// - Closure functions that can capture the current environment.\n///\n/// # Caveats\n///\n/// By limitations of the Rust language, the garbage collector currently cannot inspect closures\n/// in order to trace their captured variables. This means that only [`Copy`] closures are 100% safe\n/// to use. All other closures can also be stored in a `NativeFunction`, albeit by using an `unsafe`\n/// API, but note that passing closures implicitly capturing traceable types could cause\n/// **Undefined Behaviour**.\n#[derive(Clone, Finalize)]\npub struct NativeFunction {\n    inner: Inner,\n}\n\n#[derive(Clone)]\nenum Inner {\n    PointerFn(NativeFunctionPointer),\n    Closure(Gc<dyn TraceableClosure>),\n}\n\n// Manual implementation because deriving `Trace` triggers the `single_use_lifetimes` lint.\n// SAFETY: Only closures can contain `Trace` captures, so this implementation is safe.\nunsafe impl Trace for NativeFunction {\n    custom_trace!(this, mark, {\n        if let Inner::Closure(c) = &this.inner {\n            mark(c);\n        }\n    });\n}\n\nimpl std::fmt::Debug for NativeFunction {\n    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {\n        f.debug_struct(\"NativeFunction\").finish_non_exhaustive()\n    }\n}\n\nimpl NativeFunction {\n    /// Creates a `NativeFunction` from a function pointer.\n    #[inline]\n    pub fn from_fn_ptr(function: NativeFunctionPointer) -> Self {\n        Self {\n            inner: Inner::PointerFn(function),\n        }\n    }\n\n    /// Creates a `NativeFunction` from a function returning a [`Future`]-like.\n    ///\n    /// The returned `NativeFunction` will return an ECMAScript `Promise` that will be fulfilled\n    /// or rejected when the returned `Future` completes.\n    ///\n    /// If you only need to convert a `Future`-like into a [`JsPromise`], see\n    /// [`JsPromise::from_async_fn`].\n    ///\n    /// # Examples\n    ///\n    /// ```\n    /// # use std::cell::RefCell;\n    /// # use boa_engine::{\n    /// #   JsValue,\n    /// #   Context,\n    /// #   JsResult,\n    /// #   NativeFunction,\n    /// #   JsArgs,\n    /// # };\n    /// async fn test(\n    ///     _this: &JsValue,\n    ///     args: &[JsValue],\n    ///     context: &RefCell<&mut Context>,\n    /// ) -> JsResult<JsValue> {\n    ///     let arg = args.get_or_undefined(0).clone();\n    ///     std::future::ready(()).await;\n    ///     let value = arg.to_u32(&mut context.borrow_mut())?;\n    ///     Ok(JsValue::from(value * 2))\n    /// }\n    /// NativeFunction::from_async_fn(test);\n    /// ```\n    pub fn from_async_fn<F>(f: F) -> Self\n    where\n        F: AsyncFn(&JsValue, &[JsValue], &RefCell<&mut Context>) -> JsResult<JsValue> + 'static,\n        F: Copy,\n    {\n        Self::from_copy_closure(move |this, args, context| {\n            let (promise, resolvers) = JsPromise::new_pending(context);\n            let this = this.clone();\n            let args = args.to_vec();\n\n            context.enqueue_job(\n                NativeAsyncJob::new(async move |context| {\n                    let result = f(&this, &args, context).await;\n\n                    let context = &mut context.borrow_mut();\n                    match result {\n                        Ok(v) => resolvers.resolve.call(&JsValue::undefined(), &[v], context),\n                        Err(e) => {\n                            let e = e.into_opaque(context)?;\n                            resolvers.reject.call(&JsValue::undefined(), &[e], context)\n                        }\n                    }\n                })\n                .into(),\n            );\n\n            Ok(promise.into())\n        })\n    }\n\n    /// Creates a `NativeFunction` from a `Copy` closure.\n    pub fn from_copy_closure<F>(closure: F) -> Self\n    where\n        F: Fn(&JsValue, &[JsValue], &mut Context) -> JsResult<JsValue> + Copy + 'static,\n    {\n        // SAFETY: The `Copy` bound ensures there are no traceable types inside the closure.\n        unsafe { Self::from_closure(closure) }\n    }\n\n    /// Creates a `NativeFunction` from a `Copy` closure and a list of traceable captures.\n    pub fn from_copy_closure_with_captures<F, T>(closure: F, captures: T) -> Self\n    where\n        F: Fn(&JsValue, &[JsValue], &T, &mut Context) -> JsResult<JsValue> + Copy + 'static,\n        T: Trace + 'static,\n    {\n        // SAFETY: The `Copy` bound ensures there are no traceable types inside the closure.\n        unsafe { Self::from_closure_with_captures(closure, captures) }\n    }\n\n    /// Creates a new `NativeFunction` from a closure.\n    ///\n    /// # Safety\n    ///\n    /// Passing a closure that contains a captured variable that needs to be traced by the garbage\n    /// collector could cause an use after free, memory corruption or other kinds of **Undefined\n    /// Behaviour**. See <https://github.com/Manishearth/rust-gc/issues/50> for a technical explanation\n    /// on why that is the case.\n    pub unsafe fn from_closure<F>(closure: F) -> Self\n    where\n        F: Fn(&JsValue, &[JsValue], &mut Context) -> JsResult<JsValue> + 'static,\n    {\n        // SAFETY: The caller must ensure the invariants of the closure hold.\n        unsafe {\n            Self::from_closure_with_captures(\n                move |this, args, (), context| closure(this, args, context),\n                (),\n            )\n        }\n    }\n\n    /// Create a new `NativeFunction` from a closure and a list of traceable captures.\n    ///\n    /// # Safety\n    ///\n    /// Passing a closure that contains a captured variable that needs to be traced by the garbage\n    /// collector could cause an use after free, memory corruption or other kinds of **Undefined\n    /// Behaviour**. See <https://github.com/Manishearth/rust-gc/issues/50> for a technical explanation\n    /// on why that is the case.\n    pub unsafe fn from_closure_with_captures<F, T>(closure: F, captures: T) -> Self\n    where\n        F: Fn(&JsValue, &[JsValue], &T, &mut Context) -> JsResult<JsValue> + 'static,\n        T: Trace + 'static,\n    {\n        // Hopefully, this unsafe operation will be replaced by the `CoerceUnsized` API in the\n        // future: https://github.com/rust-lang/rust/issues/18598\n        let ptr = Gc::into_raw(Gc::new(Closure {\n            f: closure,\n            captures,\n        }));\n        // SAFETY: The pointer returned by `into_raw` is only used to coerce to a trait object,\n        // meaning this is safe.\n        unsafe {\n            Self {\n                inner: Inner::Closure(Gc::from_raw(ptr)),\n            }\n        }\n    }\n\n    /// Calls this `NativeFunction`, forwarding the arguments to the corresponding function.\n    #[inline]\n    pub fn call(\n        &self,\n        this: &JsValue,\n        args: &[JsValue],\n        context: &mut Context,\n    ) -> JsResult<JsValue> {\n        match self.inner {\n            Inner::PointerFn(f) => f(this, args, context),\n            Inner::Closure(ref c) => c.call(this, args, context),\n        }\n    }\n\n    /// Converts this `NativeFunction` into a `JsFunction` without setting its name or length.\n    ///\n    /// Useful to create functions that will only be used once, such as callbacks.\n    #[must_use]\n    pub fn to_js_function(self, realm: &Realm) -> JsFunction {\n        FunctionObjectBuilder::new(realm, self).build()\n    }\n}\n\n/// Call this object.\n///\n/// # Panics\n///\n/// Panics if the object is currently mutably borrowed.\n// <https://tc39.es/ecma262/#sec-built-in-function-objects-call-thisargument-argumentslist>\npub(crate) fn native_function_call(\n    obj: &JsObject,\n    argument_count: usize,\n    context: &mut InternalMethodCallContext<'_>,\n) -> JsResult<CallValue> {\n    let args = context\n        .vm\n        .stack\n        .calling_convention_pop_arguments(argument_count);\n    let _func = context.vm.stack.pop();\n    let this = context.vm.stack.pop();\n\n    // We technically don't need this since native functions don't push any new frames to the\n    // vm, but we'll eventually have to combine the native stack with the vm stack.\n    context.check_runtime_limits()?;\n    let this_function_object = obj.clone();\n\n    let NativeFunctionObject {\n        f: function,\n        name,\n        constructor,\n        realm,\n    } = obj\n        .downcast_ref::<NativeFunctionObject>()\n        .expect(\"the object should be a native function object\")\n        .clone();\n\n    let pc = context.vm.frame().pc;\n    let native_source_info = context.native_source_info();\n    context\n        .vm\n        .shadow_stack\n        .push_native(pc, name, native_source_info);\n\n    let mut realm = realm.unwrap_or_else(|| context.realm().clone());\n\n    context.swap_realm(&mut realm);\n    context.vm.native_active_function = Some(this_function_object);\n\n    let result = if constructor.is_some() {\n        function.call(&JsValue::undefined(), &args, context)\n    } else {\n        function.call(&this, &args, context)\n    }\n    .map_err(|err| err.inject_realm(context.realm().clone()));\n\n    context.vm.native_active_function = None;\n    context.swap_realm(&mut realm);\n\n    context.vm.shadow_stack.pop();\n\n    context.vm.stack.push(result?);\n\n    Ok(CallValue::Complete)\n}\n\n/// Construct an instance of this object with the specified arguments.\n///\n/// # Panics\n///\n/// Panics if the object is currently mutably borrowed.\n// <https://tc39.es/ecma262/#sec-built-in-function-objects-construct-argumentslist-newtarget>\nfn native_function_construct(\n    obj: &JsObject,\n    argument_count: usize,\n    context: &mut InternalMethodCallContext<'_>,\n) -> JsResult<CallValue> {\n    // We technically don't need this since native functions don't push any new frames to the\n    // vm, but we'll eventually have to combine the native stack with the vm stack.\n    context.check_runtime_limits()?;\n    let this_function_object = obj.clone();\n\n    let NativeFunctionObject {\n        f: function,\n        name,\n        constructor,\n        realm,\n    } = obj\n        .downcast_ref::<NativeFunctionObject>()\n        .expect(\"the object should be a native function object\")\n        .clone();\n\n    let pc = context.vm.frame().pc;\n    let native_source_info = context.native_source_info();\n    context\n        .vm\n        .shadow_stack\n        .push_native(pc, name, native_source_info);\n\n    let mut realm = realm.unwrap_or_else(|| context.realm().clone());\n\n    context.swap_realm(&mut realm);\n    context.vm.native_active_function = Some(this_function_object);\n\n    let new_target = context.vm.stack.pop();\n    let args = context\n        .vm\n        .stack\n        .calling_convention_pop_arguments(argument_count);\n    let _func = context.vm.stack.pop();\n    let _this = context.vm.stack.pop();\n\n    let result = function\n        .call(&new_target, &args, context)\n        .map_err(|err| err.inject_realm(context.realm().clone()))\n        .and_then(|v| match v.variant() {\n            JsVariant::Object(o) => Ok(o.clone()),\n            val => {\n                if constructor.expect(\"must be a constructor\").is_base() || val.is_undefined() {\n                    let prototype = get_prototype_from_constructor(\n                        &new_target,\n                        StandardConstructors::object,\n                        context,\n                    )?;\n                    Ok(JsObject::from_proto_and_data_with_shared_shape(\n                        context.root_shape(),\n                        prototype,\n                        OrdinaryObject,\n                    )\n                    .upcast())\n                } else {\n                    Err(JsNativeError::typ()\n                        .with_message(\"derived constructor can only return an Object or undefined\")\n                        .into())\n                }\n            }\n        });\n\n    context.vm.native_active_function = None;\n    context.swap_realm(&mut realm);\n\n    context.vm.shadow_stack.pop();\n\n    context.vm.stack.push(result?);\n\n    Ok(CallValue::Complete)\n}\n"
  },
  {
    "path": "core/engine/src/object/builtins/jsarray.rs",
    "content": "//! A Rust API wrapper for Boa's `Array` Builtin ECMAScript Object\nuse crate::{\n    Context, JsExpect, JsResult, JsString, JsValue,\n    builtins::Array,\n    error::JsNativeError,\n    object::{JsFunction, JsObject},\n    value::{IntoOrUndefined, TryFromJs},\n};\nuse boa_gc::{Finalize, Trace};\nuse std::ops::Deref;\n\n/// `JsArray` provides a wrapper for Boa's implementation of the JavaScript `Array` object.\n#[derive(Debug, Clone, Trace, Finalize)]\npub struct JsArray {\n    inner: JsObject,\n}\n\nimpl JsArray {\n    /// Create a new empty array.\n    ///\n    /// # Errors\n    ///\n    /// Returns a `PanicError` if creating the array fails due to an engine bug.\n    #[inline]\n    pub fn new(context: &mut Context) -> JsResult<Self> {\n        let inner = Array::array_create(0, None, context)\n            .js_expect(\"creating an empty array with the default prototype must not fail\")?;\n\n        Ok(Self { inner })\n    }\n\n    /// Create an array from a `IntoIterator<Item = JsValue>` convertible object.\n    pub fn from_iter<I>(elements: I, context: &mut Context) -> Self\n    where\n        I: IntoIterator<Item = JsValue>,\n    {\n        Self {\n            inner: Array::create_array_from_list(elements, context),\n        }\n    }\n\n    /// Create a [`JsArray`] from a [`JsObject`], if the object is not an array throw a `TypeError`.\n    ///\n    /// This does not clone the fields of the array, it only does a shallow clone of the object.\n    #[inline]\n    pub fn from_object(object: JsObject) -> JsResult<Self> {\n        if object.is_array() {\n            Ok(Self { inner: object })\n        } else {\n            Err(JsNativeError::typ()\n                .with_message(\"object is not an Array\")\n                .into())\n        }\n    }\n\n    /// Get the length of the array.\n    ///\n    /// Same as `array.length` in JavaScript.\n    #[inline]\n    pub fn length(&self, context: &mut Context) -> JsResult<u64> {\n        self.inner.length_of_array_like(context)\n    }\n\n    /// Check if the array is empty, i.e. the `length` is zero.\n    #[inline]\n    pub fn is_empty(&self, context: &mut Context) -> JsResult<bool> {\n        self.inner.length_of_array_like(context).map(|len| len == 0)\n    }\n\n    /// Push an element to the array.\n    pub fn push<T>(&self, value: T, context: &mut Context) -> JsResult<JsValue>\n    where\n        T: Into<JsValue>,\n    {\n        self.push_items(&[value.into()], context)\n    }\n\n    /// Pushes a slice of elements to the array.\n    #[inline]\n    pub fn push_items(&self, items: &[JsValue], context: &mut Context) -> JsResult<JsValue> {\n        Array::push(&self.inner.clone().into(), items, context)\n    }\n\n    /// Pops an element from the array.\n    #[inline]\n    pub fn pop(&self, context: &mut Context) -> JsResult<JsValue> {\n        Array::pop(&self.inner.clone().into(), &[], context)\n    }\n\n    /// Calls `Array.prototype.at()`.\n    pub fn at<T>(&self, index: T, context: &mut Context) -> JsResult<JsValue>\n    where\n        T: Into<i64>,\n    {\n        Array::at(&self.inner.clone().into(), &[index.into().into()], context)\n    }\n\n    /// Calls `Array.prototype.shift()`.\n    #[inline]\n    pub fn shift(&self, context: &mut Context) -> JsResult<JsValue> {\n        Array::shift(&self.inner.clone().into(), &[], context)\n    }\n\n    /// Calls `Array.prototype.unshift()`.\n    #[inline]\n    pub fn unshift(&self, items: &[JsValue], context: &mut Context) -> JsResult<JsValue> {\n        Array::unshift(&self.inner.clone().into(), items, context)\n    }\n\n    /// Calls `Array.prototype.reverse()`.\n    #[inline]\n    pub fn reverse(&self, context: &mut Context) -> JsResult<Self> {\n        Array::reverse(&self.inner.clone().into(), &[], context)?;\n        Ok(self.clone())\n    }\n\n    /// Calls `Array.prototype.concat()`.\n    #[inline]\n    pub fn concat(&self, items: &[JsValue], context: &mut Context) -> JsResult<Self> {\n        let object = Array::concat(&self.inner.clone().into(), items, context)?\n            .as_object()\n            .js_expect(\"Array.prototype.concat should always return object\")?;\n\n        Self::from_object(object)\n    }\n\n    /// Calls `Array.prototype.join()`.\n    #[inline]\n    pub fn join(&self, separator: Option<JsString>, context: &mut Context) -> JsResult<JsString> {\n        let result = Array::join(\n            &self.inner.clone().into(),\n            &[separator.into_or_undefined()],\n            context,\n        )?;\n        result\n            .as_string()\n            .js_expect(\"Array.prototype.join always returns string\")\n            .map_err(Into::into)\n    }\n\n    /// Calls `Array.prototype.fill()`.\n    pub fn fill<T>(\n        &self,\n        value: T,\n        start: Option<u32>,\n        end: Option<u32>,\n        context: &mut Context,\n    ) -> JsResult<Self>\n    where\n        T: Into<JsValue>,\n    {\n        Array::fill(\n            &self.inner.clone().into(),\n            &[\n                value.into(),\n                start.into_or_undefined(),\n                end.into_or_undefined(),\n            ],\n            context,\n        )?;\n        Ok(self.clone())\n    }\n\n    /// Calls `Array.prototype.indexOf()`.\n    pub fn index_of<T>(\n        &self,\n        search_element: T,\n        from_index: Option<u32>,\n        context: &mut Context,\n    ) -> JsResult<Option<u32>>\n    where\n        T: Into<JsValue>,\n    {\n        let index = Array::index_of(\n            &self.inner.clone().into(),\n            &[search_element.into(), from_index.into_or_undefined()],\n            context,\n        )?\n        .as_number()\n        .js_expect(\"Array.prototype.indexOf should always return number\")?;\n\n        #[allow(clippy::float_cmp)]\n        if index == -1.0 {\n            Ok(None)\n        } else {\n            Ok(Some(index as u32))\n        }\n    }\n\n    /// Calls `Array.prototype.lastIndexOf()`.\n    pub fn last_index_of<T>(\n        &self,\n        search_element: T,\n        from_index: Option<u32>,\n        context: &mut Context,\n    ) -> JsResult<Option<u32>>\n    where\n        T: Into<JsValue>,\n    {\n        let index = Array::last_index_of(\n            &self.inner.clone().into(),\n            &[search_element.into(), from_index.into_or_undefined()],\n            context,\n        )?\n        .as_number()\n        .js_expect(\"Array.prototype.lastIndexOf should always return number\")?;\n\n        #[allow(clippy::float_cmp)]\n        if index == -1.0 {\n            Ok(None)\n        } else {\n            Ok(Some(index as u32))\n        }\n    }\n\n    /// Calls `Array.prototype.find()`.\n    #[inline]\n    pub fn find(\n        &self,\n        predicate: JsFunction,\n        this_arg: Option<JsValue>,\n        context: &mut Context,\n    ) -> JsResult<JsValue> {\n        Array::find(\n            &self.inner.clone().into(),\n            &[predicate.into(), this_arg.into_or_undefined()],\n            context,\n        )\n    }\n\n    /// Calls `Array.prototype.filter()`.\n    #[inline]\n    pub fn filter(\n        &self,\n        callback: JsFunction,\n        this_arg: Option<JsValue>,\n        context: &mut Context,\n    ) -> JsResult<Self> {\n        let object = Array::filter(\n            &self.inner.clone().into(),\n            &[callback.into(), this_arg.into_or_undefined()],\n            context,\n        )?\n        .as_object()\n        .js_expect(\"Array.prototype.filter should always return object\")?;\n\n        Self::from_object(object)\n    }\n\n    /// Calls `Array.prototype.map()`.\n    #[inline]\n    pub fn map(\n        &self,\n        callback: JsFunction,\n        this_arg: Option<JsValue>,\n        context: &mut Context,\n    ) -> JsResult<Self> {\n        let object = Array::map(\n            &self.inner.clone().into(),\n            &[callback.into(), this_arg.into_or_undefined()],\n            context,\n        )?\n        .as_object()\n        .js_expect(\"Array.prototype.map should always return object\")?;\n\n        Self::from_object(object)\n    }\n\n    /// Calls `Array.prototype.every()`.\n    #[inline]\n    pub fn every(\n        &self,\n        callback: JsFunction,\n        this_arg: Option<JsValue>,\n        context: &mut Context,\n    ) -> JsResult<bool> {\n        let result = Array::every(\n            &self.inner.clone().into(),\n            &[callback.into(), this_arg.into_or_undefined()],\n            context,\n        )?\n        .as_boolean()\n        .js_expect(\"Array.prototype.every should always return boolean\")?;\n\n        Ok(result)\n    }\n\n    /// Calls `Array.prototype.some()`.\n    #[inline]\n    pub fn some(\n        &self,\n        callback: JsFunction,\n        this_arg: Option<JsValue>,\n        context: &mut Context,\n    ) -> JsResult<bool> {\n        let result = Array::some(\n            &self.inner.clone().into(),\n            &[callback.into(), this_arg.into_or_undefined()],\n            context,\n        )?\n        .as_boolean()\n        .js_expect(\"Array.prototype.some should always return boolean\")?;\n\n        Ok(result)\n    }\n\n    /// Calls `Array.prototype.sort()`.\n    #[inline]\n    pub fn sort(&self, compare_fn: Option<JsFunction>, context: &mut Context) -> JsResult<Self> {\n        Array::sort(\n            &self.inner.clone().into(),\n            &[compare_fn.into_or_undefined()],\n            context,\n        )?;\n\n        Ok(self.clone())\n    }\n\n    /// Calls `Array.prototype.slice()`.\n    #[inline]\n    pub fn slice(\n        &self,\n        start: Option<u32>,\n        end: Option<u32>,\n        context: &mut Context,\n    ) -> JsResult<Self> {\n        let object = Array::slice(\n            &self.inner.clone().into(),\n            &[start.into_or_undefined(), end.into_or_undefined()],\n            context,\n        )?\n        .as_object()\n        .js_expect(\"Array.prototype.slice should always return object\")?;\n\n        Self::from_object(object)\n    }\n\n    /// Calls `Array.prototype.values()`.\n    #[inline]\n    pub fn values(&self, context: &mut Context) -> JsResult<JsValue> {\n        Array::values(&self.inner.clone().into(), &[], context)\n    }\n\n    /// Calls `Array.prototype.splice()`.\n    ///\n    /// Removes and/or inserts elements from the array, returning the removed elements.\n    ///\n    /// # Examples\n    ///\n    /// ```\n    /// # use boa_engine::{Context, JsValue};\n    /// # use boa_engine::object::builtins::JsArray;\n    /// let context = &mut Context::default();\n    /// let array = JsArray::from_iter([1, 2, 3].map(JsValue::from), context);\n    ///\n    /// // Insert elements at index 1 without removing\n    /// let removed = array.splice(\n    ///     1,\n    ///     Some(0),\n    ///     &[JsValue::from(10), JsValue::from(20)],\n    ///     context,\n    /// ).unwrap();\n    ///\n    /// assert_eq!(array.length(context).unwrap(), 5);\n    /// assert_eq!(removed.length(context).unwrap(), 0);\n    /// ```\n    #[inline]\n    pub fn splice(\n        &self,\n        start: u32,\n        delete_count: Option<u32>,\n        items: &[JsValue],\n        context: &mut Context,\n    ) -> JsResult<Self> {\n        let start = JsValue::from(start);\n        let delete_count = delete_count.map(JsValue::from);\n        let object = Array::splice_internal(\n            &self.inner.clone().into(),\n            Some(&start),\n            delete_count.as_ref(),\n            items,\n            context,\n        )?\n        .as_object()\n        .js_expect(\"Array.prototype.splice should always return object\")?;\n\n        Self::from_object(object)\n    }\n\n    /// Calls `Array.prototype.reduce()`.\n    #[inline]\n    pub fn reduce(\n        &self,\n        callback: JsFunction,\n        initial_value: Option<JsValue>,\n        context: &mut Context,\n    ) -> JsResult<JsValue> {\n        Array::reduce(\n            &self.inner.clone().into(),\n            &[callback.into(), initial_value.into_or_undefined()],\n            context,\n        )\n    }\n\n    /// Calls `Array.prototype.reduceRight()`.\n    #[inline]\n    pub fn reduce_right(\n        &self,\n        callback: JsFunction,\n        initial_value: Option<JsValue>,\n        context: &mut Context,\n    ) -> JsResult<JsValue> {\n        Array::reduce_right(\n            &self.inner.clone().into(),\n            &[callback.into(), initial_value.into_or_undefined()],\n            context,\n        )\n    }\n\n    /// Calls `Array.prototype.toReversed`.\n    #[inline]\n    pub fn to_reversed(&self, context: &mut Context) -> JsResult<Self> {\n        let array = Array::to_reversed(&self.inner.clone().into(), &[], context)?;\n\n        Ok(Self {\n            inner: array\n                .as_object()\n                .js_expect(\"`to_reversed` must always return an `Array` on success\")?,\n        })\n    }\n\n    /// Calls `Array.prototype.toSorted`.\n    #[inline]\n    pub fn to_sorted(\n        &self,\n        compare_fn: Option<JsFunction>,\n        context: &mut Context,\n    ) -> JsResult<Self> {\n        let array = Array::to_sorted(\n            &self.inner.clone().into(),\n            &[compare_fn.into_or_undefined()],\n            context,\n        )?;\n\n        Ok(Self {\n            inner: array\n                .as_object()\n                .js_expect(\"`to_sorted` must always return an `Array` on success\")?,\n        })\n    }\n\n    /// Calls `Array.prototype.with`.\n    #[inline]\n    pub fn with(&self, index: u64, value: JsValue, context: &mut Context) -> JsResult<Self> {\n        let array = Array::with(&self.inner.clone().into(), &[index.into(), value], context)?;\n\n        Ok(Self {\n            inner: array\n                .as_object()\n                .js_expect(\"`with` must always return an `Array` on success\")?,\n        })\n    }\n}\n\nimpl From<JsArray> for JsObject {\n    #[inline]\n    fn from(o: JsArray) -> Self {\n        o.inner.clone()\n    }\n}\n\nimpl From<JsArray> for JsValue {\n    #[inline]\n    fn from(o: JsArray) -> Self {\n        o.inner.clone().into()\n    }\n}\n\nimpl Deref for JsArray {\n    type Target = JsObject;\n\n    #[inline]\n    fn deref(&self) -> &Self::Target {\n        &self.inner\n    }\n}\n\nimpl TryFromJs for JsArray {\n    fn try_from_js(value: &JsValue, _context: &mut Context) -> JsResult<Self> {\n        if let Some(o) = value.as_object() {\n            Self::from_object(o.clone())\n        } else {\n            Err(JsNativeError::typ()\n                .with_message(\"value is not an Array object\")\n                .into())\n        }\n    }\n}\n#[cfg(test)]\nmod tests {\n    use super::*;\n\n    #[test]\n    fn splice_remove() {\n        let context = &mut Context::default();\n        let array = JsArray::from_iter([1, 2, 3].map(JsValue::from), context);\n\n        let removed = array.splice(1, Some(1), &[], context).unwrap();\n\n        assert_eq!(array.length(context).unwrap(), 2);\n        assert_eq!(removed.length(context).unwrap(), 1);\n    }\n\n    #[test]\n    fn splice_insert() {\n        let context = &mut Context::default();\n        let array = JsArray::from_iter([1, 2, 3].map(JsValue::from), context);\n\n        let removed = array\n            .splice(1, Some(0), &[JsValue::from(10), JsValue::from(20)], context)\n            .unwrap();\n\n        assert_eq!(array.length(context).unwrap(), 5);\n        assert_eq!(removed.length(context).unwrap(), 0);\n    }\n\n    #[test]\n    fn splice_replace() {\n        let context = &mut Context::default();\n        let array = JsArray::from_iter([1, 2, 3].map(JsValue::from), context);\n\n        let removed = array\n            .splice(1, Some(1), &[JsValue::from(99)], context)\n            .unwrap();\n\n        assert_eq!(array.length(context).unwrap(), 3);\n        assert_eq!(removed.length(context).unwrap(), 1);\n    }\n\n    #[test]\n    fn splice_from_start() {\n        let context = &mut Context::default();\n        let array = JsArray::from_iter([1, 2, 3].map(JsValue::from), context);\n\n        let removed = array.splice(0, Some(1), &[], context).unwrap();\n\n        assert_eq!(array.length(context).unwrap(), 2);\n        assert_eq!(removed.length(context).unwrap(), 1);\n    }\n\n    #[test]\n    fn splice_empty_array() {\n        let context = &mut Context::default();\n        let array = JsArray::new(context).unwrap();\n\n        let removed = array.splice(0, Some(0), &[], context).unwrap();\n\n        assert_eq!(array.length(context).unwrap(), 0);\n        assert_eq!(removed.length(context).unwrap(), 0);\n    }\n}\n"
  },
  {
    "path": "core/engine/src/object/builtins/jsarraybuffer.rs",
    "content": "//! A Rust API wrapper for Boa's `ArrayBuffer` Builtin ECMAScript Object\nuse crate::{\n    Context, JsResult, JsValue,\n    builtins::array_buffer::ArrayBuffer,\n    context::intrinsics::StandardConstructors,\n    error::JsNativeError,\n    object::{JsObject, internal_methods::get_prototype_from_constructor},\n    value::TryFromJs,\n};\nuse boa_gc::{Finalize, GcRef, GcRefMut, Trace};\nuse std::ops::Deref;\n\n#[doc(inline)]\npub use crate::builtins::array_buffer::AlignedVec;\n\n/// `JsArrayBuffer` provides a wrapper for Boa's implementation of the ECMAScript `ArrayBuffer` object\n#[derive(Debug, Clone, Trace, Finalize)]\n#[boa_gc(unsafe_no_drop)]\npub struct JsArrayBuffer {\n    inner: JsObject<ArrayBuffer>,\n}\n\nimpl From<JsArrayBuffer> for JsObject<ArrayBuffer> {\n    #[inline]\n    fn from(value: JsArrayBuffer) -> Self {\n        value.inner\n    }\n}\n\nimpl From<JsObject<ArrayBuffer>> for JsArrayBuffer {\n    #[inline]\n    fn from(value: JsObject<ArrayBuffer>) -> Self {\n        Self { inner: value }\n    }\n}\n\n// TODO: Add constructors that also take the `detach_key` as argument.\nimpl JsArrayBuffer {\n    /// Create a new array buffer with byte length.\n    ///\n    /// ```\n    /// # use boa_engine::{\n    /// # object::builtins::JsArrayBuffer,\n    /// # Context, JsResult, JsValue\n    /// # };\n    /// # fn main() -> JsResult<()> {\n    /// # // Initialize context\n    /// # let context = &mut Context::default();\n    /// // Creates a blank array buffer of n bytes\n    /// let array_buffer = JsArrayBuffer::new(4, context)?;\n    ///\n    /// assert_eq!(\n    ///     array_buffer.detach(&JsValue::undefined())?.as_slice(),\n    ///     &[0u8, 0, 0, 0]\n    /// );\n    ///\n    /// # Ok(())\n    /// # }\n    /// ```\n    #[inline]\n    pub fn new(byte_length: usize, context: &mut Context) -> JsResult<Self> {\n        let inner = ArrayBuffer::allocate(\n            &context\n                .intrinsics()\n                .constructors()\n                .array_buffer()\n                .constructor()\n                .into(),\n            byte_length as u64,\n            None,\n            context,\n        )?;\n\n        Ok(Self { inner })\n    }\n\n    /// Create a new array buffer from byte block.\n    ///\n    /// This uses the passed byte block as the internal storage, it does not clone it!\n    ///\n    /// The `byte_length` will be set to `byte_block.len()`.\n    ///\n    /// ```\n    /// # use boa_engine::{\n    /// # object::builtins::{JsArrayBuffer, AlignedVec},\n    /// # Context, JsResult, JsValue,\n    /// # };\n    /// # fn main() -> JsResult<()> {\n    /// # // Initialize context\n    /// # let context = &mut Context::default();\n    ///\n    /// // Create a buffer from a chunk of data\n    /// let data_block = AlignedVec::from_iter(0, 0..5);\n    /// let array_buffer = JsArrayBuffer::from_byte_block(data_block, context)?;\n    ///\n    /// assert_eq!(\n    ///     array_buffer.detach(&JsValue::undefined())?.as_slice(),\n    ///     &[0u8, 1, 2, 3, 4]\n    /// );\n    /// # Ok(())\n    /// # }\n    /// ```\n    pub fn from_byte_block(byte_block: AlignedVec<u8>, context: &mut Context) -> JsResult<Self> {\n        let constructor = context\n            .intrinsics()\n            .constructors()\n            .array_buffer()\n            .constructor()\n            .into();\n\n        // 1. Let obj be ? OrdinaryCreateFromConstructor(constructor, \"%ArrayBuffer.prototype%\", « [[ArrayBufferData]], [[ArrayBufferByteLength]], [[ArrayBufferDetachKey]] »).\n        let prototype = get_prototype_from_constructor(\n            &constructor,\n            StandardConstructors::array_buffer,\n            context,\n        )?;\n\n        // 2. Let block be ? CreateByteDataBlock(byteLength).\n        //\n        // NOTE: We skip step 2. because we already have the block\n        // that is passed to us as a function argument.\n        let block = byte_block;\n\n        // 3. Set obj.[[ArrayBufferData]] to block.\n        // 4. Set obj.[[ArrayBufferByteLength]] to byteLength.\n        let obj = JsObject::new(\n            context.root_shape(),\n            prototype,\n            ArrayBuffer::from_data(block, JsValue::undefined()),\n        );\n\n        Ok(Self { inner: obj })\n    }\n\n    /// Set a maximum length for the underlying array buffer.\n    #[inline]\n    #[must_use]\n    pub fn with_max_byte_length(self, max_byte_len: u64) -> Self {\n        self.inner\n            .borrow_mut()\n            .data_mut()\n            .set_max_byte_length(max_byte_len);\n        self\n    }\n\n    /// Create a [`JsArrayBuffer`] from a [`JsObject`], if the object is not an array buffer throw a `TypeError`.\n    ///\n    /// This does not clone the fields of the array buffer, it only does a shallow clone of the object.\n    #[inline]\n    pub fn from_object(object: JsObject) -> JsResult<Self> {\n        object\n            .downcast::<ArrayBuffer>()\n            .map(|inner| Self { inner })\n            .map_err(|_| {\n                JsNativeError::typ()\n                    .with_message(\"object is not an ArrayBuffer\")\n                    .into()\n            })\n    }\n\n    /// Returns the byte length of the array buffer.\n    ///\n    /// ```\n    /// # use boa_engine::{\n    /// # object::builtins::{JsArrayBuffer, AlignedVec},\n    /// # Context, JsResult,\n    /// # };\n    /// # fn main() -> JsResult<()> {\n    /// # // Initialize context\n    /// # let context = &mut Context::default();\n    /// // Create a buffer from a chunk of data\n    /// let data_block = AlignedVec::from_iter(0, 0..5);\n    /// let array_buffer = JsArrayBuffer::from_byte_block(data_block, context)?;\n    ///\n    /// // Take the inner buffer\n    /// let buffer_length = array_buffer.byte_length();\n    ///\n    /// assert_eq!(buffer_length, 5);\n    /// # Ok(())\n    /// # }\n    ///  ```\n    #[inline]\n    #[must_use]\n    pub fn byte_length(&self) -> usize {\n        self.inner.borrow().data().len()\n    }\n\n    /// Take the inner `ArrayBuffer`'s `array_buffer_data` field and replace it with `None`\n    ///\n    /// # Note\n    ///\n    /// This tries to detach the pre-existing `JsArrayBuffer`, meaning the original detach\n    /// key is required. By default, the key is set to `undefined`.\n    ///\n    /// ```\n    /// # use boa_engine::{\n    /// # object::builtins::{JsArrayBuffer, AlignedVec},\n    /// # Context, JsResult, JsValue\n    /// # };\n    /// # fn main() -> JsResult<()> {\n    /// # // Initialize context\n    /// # let context = &mut Context::default();\n    /// // Create a buffer from a chunk of data\n    /// let data_block = AlignedVec::from_iter(0, 0..5);\n    /// let array_buffer = JsArrayBuffer::from_byte_block(data_block, context)?;\n    ///\n    /// // Take the inner buffer\n    /// let internal_buffer = array_buffer.detach(&JsValue::undefined())?;\n    ///\n    /// assert_eq!(internal_buffer.as_slice(), &[0u8, 1, 2, 3, 4]);\n    ///\n    /// // Anymore interaction with the buffer will return an error\n    /// let detached_err = array_buffer.detach(&JsValue::undefined());\n    /// assert!(detached_err.is_err());\n    /// # Ok(())\n    /// # }\n    /// ```\n    #[inline]\n    pub fn detach(&self, detach_key: &JsValue) -> JsResult<AlignedVec<u8>> {\n        self.inner\n            .borrow_mut()\n            .data_mut()\n            .detach(detach_key)?\n            .ok_or_else(|| {\n                JsNativeError::typ()\n                    .with_message(\"ArrayBuffer was already detached\")\n                    .into()\n            })\n    }\n\n    /// Get an immutable reference to the [`JsArrayBuffer`]'s data.\n    ///\n    /// Returns `None` if detached.\n    ///\n    /// ```\n    /// # use boa_engine::{\n    /// # object::builtins::{JsArrayBuffer, AlignedVec},\n    /// # Context, JsResult, JsValue,\n    /// # };\n    /// # fn main() -> JsResult<()> {\n    /// # // Initialize context\n    /// # let context = &mut Context::default();\n    /// // Create a buffer from a chunk of data\n    /// let data_block = AlignedVec::from_iter(0, 0..5);\n    /// let array_buffer = JsArrayBuffer::from_byte_block(data_block, context)?;\n    ///\n    /// // Get a reference to the data.\n    /// let internal_buffer = array_buffer.data();\n    ///\n    /// assert_eq!(internal_buffer.as_deref(), Some(&[0u8, 1, 2, 3, 4][..]));\n    /// # Ok(())\n    /// # }\n    /// ```\n    #[inline]\n    #[must_use]\n    pub fn data(&self) -> Option<GcRef<'_, [u8]>> {\n        GcRef::try_map(self.inner.borrow(), |o| o.data().bytes())\n    }\n\n    /// Copies the contents of this [`JsArrayBuffer`] into a new [`Vec<u8>`].\n    ///\n    /// Returns `None` if the buffer has been detached.\n    ///\n    /// See also [`crate::object::builtins::JsUint8Array::to_vec`] and\n    /// [`crate::object::builtins::JsSharedArrayBuffer::to_vec`].\n    ///\n    /// # Example\n    ///\n    /// ```\n    /// # use boa_engine::object::builtins::{AlignedVec, JsArrayBuffer};\n    /// # use boa_engine::{Context, JsResult, JsValue};\n    /// # fn main() -> JsResult<()> {\n    /// let context = &mut Context::default();\n    /// let data = AlignedVec::from_iter(0, [1u8, 2, 3, 4]);\n    /// let buffer = JsArrayBuffer::from_byte_block(data, context)?;\n    /// assert_eq!(buffer.to_vec(), Some(vec![1u8, 2, 3, 4]));\n    ///\n    /// buffer.detach(&JsValue::undefined())?;\n    /// assert_eq!(buffer.to_vec(), None);\n    /// # Ok(())\n    /// # }\n    /// ```\n    #[inline]\n    #[must_use]\n    pub fn to_vec(&self) -> Option<Vec<u8>> {\n        self.data().map(|data| data.to_vec())\n    }\n\n    /// Get a mutable reference to the [`JsArrayBuffer`]'s data.\n    ///\n    /// Returns `None` if detached.\n    ///\n    /// ```\n    /// # use boa_engine::{\n    /// # object::builtins::{JsArrayBuffer, AlignedVec},\n    /// # Context, JsResult, JsValue\n    /// # };\n    /// # fn main() -> JsResult<()> {\n    /// # // Initialize context\n    /// # let context = &mut Context::default();\n    /// // Create a buffer from a chunk of data\n    /// let data_block = AlignedVec::from_iter(0, 0..5);\n    /// let array_buffer = JsArrayBuffer::from_byte_block(data_block, context)?;\n    ///\n    /// // Get a reference to the data.\n    /// let mut internal_buffer = array_buffer\n    ///     .data_mut()\n    ///     .expect(\"the buffer should not be detached\");\n    ///\n    /// internal_buffer.fill(10);\n    ///\n    /// assert_eq!(&*internal_buffer, &[10u8, 10, 10, 10, 10]);\n    /// # Ok(())\n    /// # }\n    /// ```\n    #[inline]\n    #[must_use]\n    pub fn data_mut(&self) -> Option<GcRefMut<'_, [u8]>> {\n        GcRefMut::try_map(self.inner.borrow_mut(), |o| o.data_mut().bytes_mut())\n    }\n}\n\nimpl From<JsArrayBuffer> for JsObject {\n    #[inline]\n    fn from(o: JsArrayBuffer) -> Self {\n        o.inner.upcast()\n    }\n}\n\nimpl From<JsArrayBuffer> for JsValue {\n    #[inline]\n    fn from(o: JsArrayBuffer) -> Self {\n        o.inner.upcast().into()\n    }\n}\n\nimpl Deref for JsArrayBuffer {\n    type Target = JsObject<ArrayBuffer>;\n\n    #[inline]\n    fn deref(&self) -> &Self::Target {\n        &self.inner\n    }\n}\n\nimpl TryFromJs for JsArrayBuffer {\n    fn try_from_js(value: &JsValue, _context: &mut Context) -> JsResult<Self> {\n        if let Some(o) = value.as_object() {\n            Self::from_object(o.clone())\n        } else {\n            Err(JsNativeError::typ()\n                .with_message(\"value is not an ArrayBuffer object\")\n                .into())\n        }\n    }\n}\n\n#[cfg(test)]\nmod tests {\n    use super::*;\n    use crate::{Context, JsValue};\n\n    #[test]\n    fn array_buffer_to_vec_roundtrip_and_detach() {\n        let context = &mut Context::default();\n\n        let data = AlignedVec::from_iter(0, [1u8, 2, 3, 4, 5]);\n        let buffer = JsArrayBuffer::from_byte_block(data, context).unwrap();\n\n        assert_eq!(buffer.to_vec(), Some(vec![1u8, 2, 3, 4, 5]));\n\n        buffer.detach(&JsValue::undefined()).unwrap();\n        assert_eq!(buffer.to_vec(), None);\n    }\n\n    #[test]\n    fn array_buffer_to_vec_empty() {\n        let context = &mut Context::default();\n\n        let data = AlignedVec::from_iter(0, []);\n        let buffer = JsArrayBuffer::from_byte_block(data, context).unwrap();\n\n        assert_eq!(buffer.to_vec(), Some(Vec::new()));\n    }\n}\n"
  },
  {
    "path": "core/engine/src/object/builtins/jsasyncgenerator.rs",
    "content": "//! A Rust API wrapper for Boa's `AsyncGenerator` Builtin ECMAScript Object\nuse super::JsPromise;\nuse crate::{\n    Context, JsNativeError, JsResult, JsValue, builtins::async_generator::AsyncGenerator,\n    object::JsObject, value::TryFromJs,\n};\nuse boa_gc::{Finalize, Trace};\nuse std::ops::Deref;\n\n/// `JsAsyncGenerator` provides a wrapper for Boa's implementation of the ECMAScript `AsyncGenerator` builtin object.\n#[derive(Debug, Clone, Trace, Finalize)]\npub struct JsAsyncGenerator {\n    inner: JsObject,\n}\n\nimpl JsAsyncGenerator {\n    /// Creates a `JsAsyncGenerator` from an async generator `JsObject`.\n    ///\n    /// More information:\n    ///  - [MDN documentation][mdn]\n    ///\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/AsyncGenerator\n    #[inline]\n    pub fn from_object(object: JsObject) -> JsResult<Self> {\n        if object.is::<AsyncGenerator>() {\n            Ok(Self { inner: object })\n        } else {\n            Err(JsNativeError::typ()\n                .with_message(\"object is not an AsyncGenerator\")\n                .into())\n        }\n    }\n\n    /// Calls `AsyncGenerator.prototype.next()`.\n    ///\n    /// More information:\n    ///  - [MDN documentation][mdn]\n    ///\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/AsyncGenerator/next\n    pub fn next<T>(&self, value: T, context: &mut Context) -> JsResult<JsPromise>\n    where\n        T: Into<JsValue>,\n    {\n        let value = AsyncGenerator::next(&self.inner.clone().into(), &[value.into()], context)?;\n        let obj = value\n            .as_object()\n            .ok_or_else(|| {\n                JsNativeError::typ().with_message(\"async generator did not return a Promise\")\n            })?\n            .clone();\n        JsPromise::from_object(obj)\n    }\n\n    /// Calls `AsyncGenerator.prototype.return()`.\n    ///\n    /// More information:\n    ///  - [MDN documentation][mdn]\n    ///\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/AsyncGenerator/return\n    pub fn r#return<T>(&self, value: T, context: &mut Context) -> JsResult<JsPromise>\n    where\n        T: Into<JsValue>,\n    {\n        let value = AsyncGenerator::r#return(&self.inner.clone().into(), &[value.into()], context)?;\n        let obj = value\n            .as_object()\n            .ok_or_else(|| {\n                JsNativeError::typ().with_message(\"async generator did not return a Promise\")\n            })?\n            .clone();\n        JsPromise::from_object(obj)\n    }\n\n    /// Calls `AsyncGenerator.prototype.throw()`.\n    ///\n    /// More information:\n    ///  - [MDN documentation][mdn]\n    ///\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/AsyncGenerator/throw\n    pub fn throw<T>(&self, value: T, context: &mut Context) -> JsResult<JsPromise>\n    where\n        T: Into<JsValue>,\n    {\n        let value = AsyncGenerator::throw(&self.inner.clone().into(), &[value.into()], context)?;\n        let obj = value\n            .as_object()\n            .ok_or_else(|| {\n                JsNativeError::typ().with_message(\"async generator did not return a Promise\")\n            })?\n            .clone();\n        JsPromise::from_object(obj)\n    }\n}\n\nimpl From<JsAsyncGenerator> for JsObject {\n    #[inline]\n    fn from(o: JsAsyncGenerator) -> Self {\n        o.inner.clone()\n    }\n}\n\nimpl From<JsAsyncGenerator> for JsValue {\n    #[inline]\n    fn from(o: JsAsyncGenerator) -> Self {\n        o.inner.clone().into()\n    }\n}\n\nimpl Deref for JsAsyncGenerator {\n    type Target = JsObject;\n\n    #[inline]\n    fn deref(&self) -> &Self::Target {\n        &self.inner\n    }\n}\n\nimpl TryFromJs for JsAsyncGenerator {\n    fn try_from_js(value: &JsValue, _context: &mut Context) -> JsResult<Self> {\n        if let Some(o) = value.as_object() {\n            Self::from_object(o.clone())\n        } else {\n            Err(JsNativeError::typ()\n                .with_message(\"value is not an AsyncGenerator object\")\n                .into())\n        }\n    }\n}\n"
  },
  {
    "path": "core/engine/src/object/builtins/jsdataview.rs",
    "content": "//! A Rust API wrapper for Boa's `DataView` Builtin ECMAScript Object\nuse crate::{\n    Context, JsExpect, JsNativeError, JsResult, JsValue,\n    builtins::{DataView, array_buffer::BufferObject},\n    object::{JsArrayBuffer, JsObject},\n    value::TryFromJs,\n};\n\nuse boa_gc::{Finalize, Trace};\nuse std::ops::Deref;\n\n/// `JsDataView` provides a wrapper for Boa's implementation of the ECMAScript `DataView` object\n///\n/// # Examples\n/// ```\n/// # use boa_engine::{\n/// #     object::builtins::{JsArrayBuffer, JsDataView},\n/// #     Context, JsValue, JsResult,\n/// # };\n/// # fn main() -> JsResult<()> {\n/// // Create a new context and ArrayBuffer\n/// let context = &mut Context::default();\n/// let array_buffer = JsArrayBuffer::new(4, context)?;\n///\n/// // Create a new Dataview from pre-existing ArrayBuffer\n/// let data_view =\n///     JsDataView::from_js_array_buffer(array_buffer, None, None, context)?;\n///\n/// # Ok(())\n/// # }\n/// ```\n#[derive(Debug, Clone, Trace, Finalize)]\n#[boa_gc(unsafe_no_drop)]\npub struct JsDataView {\n    inner: JsObject<DataView>,\n}\n\nimpl From<JsDataView> for JsObject<DataView> {\n    #[inline]\n    fn from(value: JsDataView) -> Self {\n        value.inner\n    }\n}\n\nimpl From<JsObject<DataView>> for JsDataView {\n    #[inline]\n    fn from(value: JsObject<DataView>) -> Self {\n        Self { inner: value }\n    }\n}\n\nimpl JsDataView {\n    /// Create a new `JsDataView` object from an existing `JsArrayBuffer`.\n    pub fn from_js_array_buffer(\n        buffer: JsArrayBuffer,\n        offset: Option<u64>,\n        byte_len: Option<u64>,\n        context: &mut Context,\n    ) -> JsResult<Self> {\n        let offset = offset.unwrap_or_default();\n\n        let (buf_byte_len, is_fixed_len) = {\n            let buffer = buffer.borrow();\n            let buffer = buffer.data();\n\n            // 4. If IsDetachedBuffer(buffer) is true, throw a TypeError exception.\n            let Some(slice) = buffer.bytes() else {\n                return Err(JsNativeError::typ()\n                    .with_message(\"ArrayBuffer is detached\")\n                    .into());\n            };\n\n            // 5. Let bufferByteLength be ArrayBufferByteLength(buffer, seq-cst).\n            let buf_len = slice.len() as u64;\n\n            // 6. If offset > bufferByteLength, throw a RangeError exception.\n            if offset > buf_len {\n                return Err(JsNativeError::range()\n                    .with_message(\"Start offset is outside the bounds of the buffer\")\n                    .into());\n            }\n\n            // 7. Let bufferIsFixedLength be IsFixedLengthArrayBuffer(buffer).\n            (buf_len, buffer.is_fixed_len())\n        };\n\n        // 8. If byteLength is undefined, then\n        let view_byte_len = if let Some(byte_len) = byte_len {\n            // 9. Else,\n            //     a. Let viewByteLength be ? ToIndex(byteLength).\n            //     b. If offset + viewByteLength > bufferByteLength, throw a RangeError exception.\n            if offset + byte_len > buf_byte_len {\n                return Err(JsNativeError::range()\n                    .with_message(\"Invalid data view length\")\n                    .into());\n            }\n\n            Some(byte_len)\n        } else {\n            // a. If bufferIsFixedLength is true, then\n            //     i. Let viewByteLength be bufferByteLength - offset.\n            // b. Else,\n            //     i. Let viewByteLength be auto.\n            is_fixed_len.then_some(buf_byte_len - offset)\n        };\n\n        // 10. Let O be ? OrdinaryCreateFromConstructor(NewTarget, \"%DataView.prototype%\",\n        //     « [[DataView]], [[ViewedArrayBuffer]], [[ByteLength]], [[ByteOffset]] »).\n        let prototype = context.intrinsics().constructors().data_view().prototype();\n\n        // 11. If IsDetachedBuffer(buffer) is true, throw a TypeError exception.\n        // 12. Set bufferByteLength to ArrayBufferByteLength(buffer, seq-cst).\n        let Some(buf_byte_len) = buffer.borrow().data().bytes().map(|s| s.len() as u64) else {\n            return Err(JsNativeError::typ()\n                .with_message(\"ArrayBuffer is detached\")\n                .into());\n        };\n\n        // 13. If offset > bufferByteLength, throw a RangeError exception.\n        if offset > buf_byte_len {\n            return Err(JsNativeError::range()\n                .with_message(\"DataView offset outside of buffer array bounds\")\n                .into());\n        }\n\n        // 14. If byteLength is not undefined, then\n        //     a. If offset + viewByteLength > bufferByteLength, throw a RangeError exception.\n        if let Some(view_byte_len) = view_byte_len\n            && byte_len.is_some()\n            && offset + view_byte_len > buf_byte_len\n        {\n            return Err(JsNativeError::range()\n                .with_message(\"DataView offset outside of buffer array bounds\")\n                .into());\n        }\n\n        let obj = JsObject::new(\n            context.root_shape(),\n            prototype,\n            DataView {\n                // 15. Set O.[[ViewedArrayBuffer]] to buffer.\n                viewed_array_buffer: BufferObject::Buffer(buffer.into()),\n                // 16. Set O.[[ByteLength]] to viewByteLength.\n                byte_length: view_byte_len,\n                // 17. Set O.[[ByteOffset]] to offset.\n                byte_offset: offset,\n            },\n        );\n\n        // 18. Return O.\n        Ok(Self { inner: obj })\n    }\n\n    /// Create a new `JsDataView` object from an existing object.\n    #[inline]\n    pub fn from_object(object: JsObject) -> JsResult<Self> {\n        object\n            .downcast::<DataView>()\n            .map(|inner| Self { inner })\n            .map_err(|_| {\n                JsNativeError::typ()\n                    .with_message(\"object is not a DataView\")\n                    .into()\n            })\n    }\n\n    /// Returns the `viewed_array_buffer` field for [`JsDataView`]\n    #[inline]\n    pub fn buffer(&self, context: &mut Context) -> JsResult<JsValue> {\n        DataView::get_buffer(&self.inner.clone().upcast().into(), &[], context)\n    }\n\n    /// Returns the `byte_length` property of [`JsDataView`] as a u64 integer\n    #[inline]\n    pub fn byte_length(&self, context: &mut Context) -> JsResult<u64> {\n        DataView::get_byte_length(&self.inner.clone().upcast().into(), &[], context).and_then(|v| {\n            v.as_number()\n                .js_expect(\"value should be a number\")\n                .map(|n| n as u64)\n                .map_err(Into::into)\n        })\n    }\n\n    /// Returns the `byte_offset` field property of [`JsDataView`] as a u64 integer\n    #[inline]\n    pub fn byte_offset(&self, context: &mut Context) -> JsResult<u64> {\n        DataView::get_byte_offset(&self.inner.clone().upcast().into(), &[], context).and_then(|v| {\n            v.as_number()\n                .js_expect(\"byte_offset value must be a number\")\n                .map(|n| n as u64)\n                .map_err(Into::into)\n        })\n    }\n\n    /// Returns a signed 64-bit integer at the specified offset from the start of the [`JsDataView`]\n    #[inline]\n    pub fn get_big_int64(\n        &self,\n        byte_offset: usize,\n        is_little_endian: bool,\n        context: &mut Context,\n    ) -> JsResult<i64> {\n        DataView::get_big_int64(\n            &self.inner.clone().upcast().into(),\n            &[byte_offset.into(), is_little_endian.into()],\n            context,\n        )\n        .and_then(|v| {\n            v.as_number()\n                .js_expect(\"value must be a number\")\n                .map(|n| n as i64)\n                .map_err(Into::into)\n        })\n    }\n\n    /// Returns an unsigned 64-bit integer at the specified offset from the start of the [`JsDataView`]\n    #[inline]\n    pub fn get_big_uint64(\n        &self,\n        byte_offset: usize,\n        is_little_endian: bool,\n        context: &mut Context,\n    ) -> JsResult<u64> {\n        DataView::get_big_uint64(\n            &self.inner.clone().upcast().into(),\n            &[byte_offset.into(), is_little_endian.into()],\n            context,\n        )\n        .and_then(|v| {\n            v.as_number()\n                .js_expect(\"value must be a number\")\n                .map(|n| n as u64)\n                .map_err(Into::into)\n        })\n    }\n\n    /// Returns a signed 32-bit float integer at the specified offset from the start of the [`JsDataView`]\n    #[inline]\n    pub fn get_float32(\n        &self,\n        byte_offset: usize,\n        is_little_endian: bool,\n        context: &mut Context,\n    ) -> JsResult<f32> {\n        DataView::get_float32(\n            &self.inner.clone().upcast().into(),\n            &[byte_offset.into(), is_little_endian.into()],\n            context,\n        )\n        .and_then(|v| {\n            v.as_number()\n                .js_expect(\"value must be a number\")\n                .map(|n| n as f32)\n                .map_err(Into::into)\n        })\n    }\n\n    /// Returns a signed 64-bit float integer at the specified offset from the start of the [`JsDataView`]\n    #[inline]\n    pub fn get_float64(\n        &self,\n        byte_offset: usize,\n        is_little_endian: bool,\n        context: &mut Context,\n    ) -> JsResult<f64> {\n        DataView::get_float64(\n            &self.inner.clone().upcast().into(),\n            &[byte_offset.into(), is_little_endian.into()],\n            context,\n        )\n        .and_then(|v| {\n            v.as_number()\n                .js_expect(\"value must be a number\")\n                .map_err(Into::into)\n        })\n    }\n\n    /// Returns a signed 8-bit integer at the specified offset from the start of the [`JsDataView`]\n    #[inline]\n    pub fn get_int8(\n        &self,\n        byte_offset: usize,\n        is_little_endian: bool,\n        context: &mut Context,\n    ) -> JsResult<i8> {\n        DataView::get_int8(\n            &self.inner.clone().upcast().into(),\n            &[byte_offset.into(), is_little_endian.into()],\n            context,\n        )\n        .and_then(|v| {\n            v.as_number()\n                .js_expect(\"value must be a number\")\n                .map(|n| n as i8)\n                .map_err(Into::into)\n        })\n    }\n\n    /// Returns a signed 16-bit integer at the specified offset from the start of the [`JsDataView`]\n    #[inline]\n    pub fn get_int16(\n        &self,\n        byte_offset: usize,\n        is_little_endian: bool,\n        context: &mut Context,\n    ) -> JsResult<i16> {\n        DataView::get_int16(\n            &self.inner.clone().upcast().into(),\n            &[byte_offset.into(), is_little_endian.into()],\n            context,\n        )\n        .and_then(|v| {\n            v.as_number()\n                .js_expect(\"value must be a number\")\n                .map(|n| n as i16)\n                .map_err(Into::into)\n        })\n    }\n\n    /// Returns a signed 32-bit integer at the specified offset from the start of the [`JsDataView`]\n    #[inline]\n    pub fn get_int32(\n        &self,\n        byte_offset: usize,\n        is_little_endian: bool,\n        context: &mut Context,\n    ) -> JsResult<i32> {\n        DataView::get_int32(\n            &self.inner.clone().upcast().into(),\n            &[byte_offset.into(), is_little_endian.into()],\n            context,\n        )\n        .and_then(|v| {\n            v.as_number()\n                .js_expect(\"value must be a number\")\n                .map(|n| n as i32)\n                .map_err(Into::into)\n        })\n    }\n\n    /// Returns an unsigned 8-bit integer at the specified offset from the start of the [`JsDataView`]\n    #[inline]\n    pub fn get_uint8(\n        &self,\n        byte_offset: usize,\n        is_little_endian: bool,\n        context: &mut Context,\n    ) -> JsResult<u8> {\n        DataView::get_uint8(\n            &self.inner.clone().upcast().into(),\n            &[byte_offset.into(), is_little_endian.into()],\n            context,\n        )\n        .and_then(|v| {\n            v.as_number()\n                .js_expect(\"value must be a number\")\n                .map(|n| n as u8)\n                .map_err(Into::into)\n        })\n    }\n\n    /// Returns an unsigned 16-bit integer at the specified offset from the start of the [`JsDataView`]\n    #[inline]\n    pub fn get_unit16(\n        &self,\n        byte_offset: usize,\n        is_little_endian: bool,\n        context: &mut Context,\n    ) -> JsResult<u16> {\n        DataView::get_uint16(\n            &self.inner.clone().upcast().into(),\n            &[byte_offset.into(), is_little_endian.into()],\n            context,\n        )\n        .and_then(|v| {\n            v.as_number()\n                .js_expect(\"value must be a number\")\n                .map(|n| n as u16)\n                .map_err(Into::into)\n        })\n    }\n\n    /// Returns an unsigned 32-bit integer at the specified offset from the start of the [`JsDataView`]\n    #[inline]\n    pub fn get_uint32(\n        &self,\n        byte_offset: usize,\n        is_little_endian: bool,\n        context: &mut Context,\n    ) -> JsResult<u32> {\n        DataView::get_uint32(\n            &self.inner.clone().upcast().into(),\n            &[byte_offset.into(), is_little_endian.into()],\n            context,\n        )\n        .and_then(|v| {\n            v.as_number()\n                .js_expect(\"value must be a number\")\n                .map(|n| n as u32)\n                .map_err(Into::into)\n        })\n    }\n\n    /// Sets a signed 64-bit integer at the specified offset from the start of the [`JsDataView`]\n    #[inline]\n    pub fn set_big_int64(\n        &self,\n        byte_offset: usize,\n        value: i64,\n        is_little_endian: bool,\n        context: &mut Context,\n    ) -> JsResult<JsValue> {\n        DataView::set_big_int64(\n            &self.inner.clone().upcast().into(),\n            &[byte_offset.into(), value.into(), is_little_endian.into()],\n            context,\n        )\n    }\n\n    /// Sets an unsigned 64-bit integer at the specified offset from the start of the [`JsDataView`]\n    #[inline]\n    pub fn set_big_uint64(\n        &self,\n        byte_offset: usize,\n        value: u64,\n        is_little_endian: bool,\n        context: &mut Context,\n    ) -> JsResult<JsValue> {\n        DataView::set_big_uint64(\n            &self.inner.clone().upcast().into(),\n            &[byte_offset.into(), value.into(), is_little_endian.into()],\n            context,\n        )\n    }\n\n    /// Sets a signed 32-bit integer at the specified offset from the start of the [`JsDataView`]\n    #[inline]\n    pub fn set_float32(\n        &self,\n        byte_offset: usize,\n        value: f32,\n        is_little_endian: bool,\n        context: &mut Context,\n    ) -> JsResult<JsValue> {\n        DataView::set_float32(\n            &self.inner.clone().upcast().into(),\n            &[byte_offset.into(), value.into(), is_little_endian.into()],\n            context,\n        )\n    }\n\n    /// Sets a signed 64-bit integer at the specified offset from the start of the [`JsDataView`]\n    #[inline]\n    pub fn set_float64(\n        &self,\n        byte_offset: usize,\n        value: f64,\n        is_little_endian: bool,\n        context: &mut Context,\n    ) -> JsResult<JsValue> {\n        DataView::set_float64(\n            &self.inner.clone().upcast().into(),\n            &[byte_offset.into(), value.into(), is_little_endian.into()],\n            context,\n        )\n    }\n\n    /// Sets a signed 8-bit integer at the specified offset from the start of the [`JsDataView`]\n    #[inline]\n    pub fn set_int8(\n        &self,\n        byte_offset: usize,\n        value: i8,\n        is_little_endian: bool,\n        context: &mut Context,\n    ) -> JsResult<JsValue> {\n        DataView::set_int8(\n            &self.inner.clone().upcast().into(),\n            &[byte_offset.into(), value.into(), is_little_endian.into()],\n            context,\n        )\n    }\n\n    /// Sets a signed 16-bit integer at the specified offset from the start of the [`JsDataView`]\n    #[inline]\n    pub fn set_int16(\n        &self,\n        byte_offset: usize,\n        value: i16,\n        is_little_endian: bool,\n        context: &mut Context,\n    ) -> JsResult<JsValue> {\n        DataView::set_int16(\n            &self.inner.clone().upcast().into(),\n            &[byte_offset.into(), value.into(), is_little_endian.into()],\n            context,\n        )\n    }\n\n    /// Sets a signed 32-bit integer at the specified offset from the start of the [`JsDataView`]\n    #[inline]\n    pub fn set_int32(\n        &self,\n        byte_offset: usize,\n        value: i32,\n        is_little_endian: bool,\n        context: &mut Context,\n    ) -> JsResult<JsValue> {\n        DataView::set_int32(\n            &self.inner.clone().upcast().into(),\n            &[byte_offset.into(), value.into(), is_little_endian.into()],\n            context,\n        )\n    }\n\n    /// Sets an unsigned 8-bit integer at the specified offset from the start of the [`JsDataView`]\n    #[inline]\n    pub fn set_uint8(\n        &self,\n        byte_offset: usize,\n        value: u8,\n        is_little_endian: bool,\n        context: &mut Context,\n    ) -> JsResult<JsValue> {\n        DataView::set_uint8(\n            &self.inner.clone().upcast().into(),\n            &[byte_offset.into(), value.into(), is_little_endian.into()],\n            context,\n        )\n    }\n\n    /// Sets an unsigned 16-bit integer at the specified offset from the start of the [`JsDataView`]\n    #[inline]\n    pub fn set_unit16(\n        &self,\n        byte_offset: usize,\n        value: u16,\n        is_little_endian: bool,\n        context: &mut Context,\n    ) -> JsResult<JsValue> {\n        DataView::set_uint16(\n            &self.inner.clone().upcast().into(),\n            &[byte_offset.into(), value.into(), is_little_endian.into()],\n            context,\n        )\n    }\n\n    /// Sets an unsigned 32-bit integer at the specified offset from the start of the [`JsDataView`]\n    #[inline]\n    pub fn set_unit32(\n        &self,\n        byte_offset: usize,\n        value: u32,\n        is_little_endian: bool,\n        context: &mut Context,\n    ) -> JsResult<JsValue> {\n        DataView::set_uint32(\n            &self.inner.clone().upcast().into(),\n            &[byte_offset.into(), value.into(), is_little_endian.into()],\n            context,\n        )\n    }\n}\n\nimpl From<JsDataView> for JsObject {\n    #[inline]\n    fn from(o: JsDataView) -> Self {\n        o.inner.upcast()\n    }\n}\n\nimpl From<JsDataView> for JsValue {\n    #[inline]\n    fn from(o: JsDataView) -> Self {\n        o.inner.upcast().into()\n    }\n}\n\nimpl Deref for JsDataView {\n    type Target = JsObject<DataView>;\n\n    #[inline]\n    fn deref(&self) -> &Self::Target {\n        &self.inner\n    }\n}\n\nimpl TryFromJs for JsDataView {\n    fn try_from_js(value: &JsValue, _context: &mut Context) -> JsResult<Self> {\n        if let Some(o) = value.as_object() {\n            Self::from_object(o.clone())\n        } else {\n            Err(JsNativeError::typ()\n                .with_message(\"value is not a DataView object\")\n                .into())\n        }\n    }\n}\n"
  },
  {
    "path": "core/engine/src/object/builtins/jsdate.rs",
    "content": "//! A Rust API wrapper for Boa's `Date` ECMAScript Builtin Object.\n\nuse crate::{\n    Context, JsNativeError, JsResult, JsValue, builtins::Date, object::JsObject, value::TryFromJs,\n};\nuse boa_gc::{Finalize, Trace};\nuse std::ops::Deref;\nuse time::{OffsetDateTime, format_description::well_known::Rfc3339};\n\n/// `JsDate` is a wrapper for JavaScript `JsDate` builtin object\n///\n/// # Example\n///\n/// Create a `JsDate` object and set date to December 4 1995\n///\n/// ```\n/// use boa_engine::{\n///     Context, JsResult, JsValue, js_string, object::builtins::JsDate,\n/// };\n///\n/// fn main() -> JsResult<()> {\n///     // JS mutable Context\n///     let context = &mut Context::default();\n///\n///     let date = JsDate::new(context);\n///\n///     date.set_full_year(&[1995.into(), 11.into(), 4.into()], context)?;\n///\n///     assert_eq!(\n///         date.to_date_string(context)?,\n///         JsValue::from(js_string!(\"Mon Dec 04 1995\"))\n///     );\n///\n///     Ok(())\n/// }\n/// ```\n#[derive(Debug, Clone, Trace, Finalize)]\npub struct JsDate {\n    inner: JsObject,\n}\n\nimpl JsDate {\n    /// Create a new `Date` object with universal time.\n    #[inline]\n    pub fn new(context: &mut Context) -> Self {\n        let prototype = context.intrinsics().constructors().date().prototype();\n        let now = Date::utc_now(context);\n        let inner =\n            JsObject::from_proto_and_data_with_shared_shape(context.root_shape(), prototype, now)\n                .upcast();\n\n        Self { inner }\n    }\n\n    /// Create a new `JsDate` object from an existing object.\n    #[inline]\n    pub fn from_object(object: JsObject) -> JsResult<Self> {\n        if object.is::<Date>() {\n            Ok(Self { inner: object })\n        } else {\n            Err(JsNativeError::typ()\n                .with_message(\"object is not a Date\")\n                .into())\n        }\n    }\n\n    /// Return a `Number` representing the milliseconds elapsed since the UNIX epoch.\n    ///\n    /// Same as JavaScript's `Date.now()`\n    #[inline]\n    pub fn now(context: &mut Context) -> JsResult<JsValue> {\n        Date::now(&JsValue::null(), &[JsValue::null()], context)\n    }\n\n    // DEBUG: Uses RFC3339 internally therefore could match es6 spec of ISO8601  <========\n    /// Parse a `String` representation of date.\n    /// String should be ISO 8601 format.\n    /// Returns the `Number` of milliseconds since UNIX epoch if `String`\n    /// is valid, else return a `NaN`.\n    ///\n    /// Same as JavaScript's `Date.parse(value)`.\n    #[inline]\n    pub fn parse(value: JsValue, context: &mut Context) -> JsResult<JsValue> {\n        Date::parse(&JsValue::null(), &[value], context)\n    }\n\n    /// Takes a [year, month, day, hour, minute, second, millisecond]\n    /// Return a `Number` representing the milliseconds elapsed since the UNIX epoch.\n    ///\n    /// Same as JavaScript's `Date.UTC()`\n    #[inline]\n    pub fn utc(values: &[JsValue], context: &mut Context) -> JsResult<JsValue> {\n        Date::utc(&JsValue::null(), values, context)\n    }\n\n    /// Returns the day of the month(1-31) for the specified date\n    /// according to local time.\n    ///\n    /// Same as JavaScript's `Date.prototype.getDate()`.\n    #[inline]\n    pub fn get_date(&self, context: &mut Context) -> JsResult<JsValue> {\n        Date::get_date::<true>(&self.inner.clone().into(), &[JsValue::null()], context)\n    }\n\n    /// Returns the day of the week (0–6) for the specified date\n    /// according to local time.\n    ///\n    /// Same as JavaScript's `Date.prototype.getDay()`.\n    #[inline]\n    pub fn get_day(&self, context: &mut Context) -> JsResult<JsValue> {\n        Date::get_day::<true>(&self.inner.clone().into(), &[JsValue::null()], context)\n    }\n\n    /// Returns the year (4 digits for 4-digit years) of the specified date\n    /// according to local time.\n    ///\n    /// Same as JavaScript's `Date.prototype.getFullYear()`.\n    #[inline]\n    pub fn get_full_year(&self, context: &mut Context) -> JsResult<JsValue> {\n        Date::get_full_year::<true>(&self.inner.clone().into(), &[JsValue::null()], context)\n    }\n\n    /// Returns the hour (0–23) in the specified date according to local time.\n    ///\n    /// Same as JavaScript's `Date.prototype.getHours()`.\n    #[inline]\n    pub fn get_hours(&self, context: &mut Context) -> JsResult<JsValue> {\n        Date::get_hours::<true>(&self.inner.clone().into(), &[JsValue::null()], context)\n    }\n\n    /// Returns the milliseconds (0–999) in the specified date according\n    /// to local time.\n    ///\n    /// Same as JavaScript's `Date.prototype.getMilliseconds()`.\n    #[inline]\n    pub fn get_milliseconds(&self, context: &mut Context) -> JsResult<JsValue> {\n        Date::get_milliseconds::<true>(&self.inner.clone().into(), &[JsValue::null()], context)\n    }\n\n    /// Returns the minutes (0–59) in the specified date according to local time.\n    ///\n    /// Same as JavaScript's `Date.prototype.getMinutes()`.\n    #[inline]\n    pub fn get_minutes(&self, context: &mut Context) -> JsResult<JsValue> {\n        Date::get_minutes::<true>(&self.inner.clone().into(), &[JsValue::null()], context)\n    }\n\n    /// Returns the month (0–11) in the specified date according to local time.\n    ///\n    /// Same as JavaScript's `Date.prototype.getMonth()`.\n    #[inline]\n    pub fn get_month(&self, context: &mut Context) -> JsResult<JsValue> {\n        Date::get_month::<true>(&self.inner.clone().into(), &[JsValue::null()], context)\n    }\n\n    /// Returns the seconds (0–59) in the specified date according to local time.\n    ///\n    /// Same as JavaScript's `Date.prototype.getSeconds()`.\n    #[inline]\n    pub fn get_seconds(&self, context: &mut Context) -> JsResult<JsValue> {\n        Date::get_seconds::<true>(&self.inner.clone().into(), &[JsValue::null()], context)\n    }\n\n    /// Returns the numeric value of the specified date as the number\n    /// of milliseconds since UNIX epoch.\n    /// Negative values are returned for prior times.\n    ///\n    /// Same as JavaScript's `Date.prototype.getTime()`.\n    #[inline]\n    pub fn get_time(&self, context: &mut Context) -> JsResult<JsValue> {\n        Date::get_time(&self.inner.clone().into(), &[JsValue::null()], context)\n    }\n\n    /// Returns the time-zone offset in minutes for the current locale.\n    ///\n    /// Same as JavaScript's `Date.prototype.getTimezoneOffset()`.\n    #[inline]\n    pub fn get_timezone_offset(&self, context: &mut Context) -> JsResult<JsValue> {\n        Date::get_timezone_offset(&self.inner.clone().into(), &[JsValue::null()], context)\n    }\n\n    /// Returns the day (date) of the month (1–31) in the specified\n    /// date according to universal time.\n    ///\n    /// Same as JavaScript's `Date.prototype.getUTCDate()`.\n    #[inline]\n    pub fn get_utc_date(&self, context: &mut Context) -> JsResult<JsValue> {\n        Date::get_date::<false>(&self.inner.clone().into(), &[JsValue::null()], context)\n    }\n\n    /// Returns the day of the week (0–6) in the specified\n    /// date according to universal time.\n    ///\n    /// Same as JavaScript's `Date.prototype.getUTCDay()`.\n    #[inline]\n    pub fn get_utc_day(&self, context: &mut Context) -> JsResult<JsValue> {\n        Date::get_day::<false>(&self.inner.clone().into(), &[JsValue::null()], context)\n    }\n\n    /// Returns the year (4 digits for 4-digit years) in the specified\n    /// date according to universal time.\n    ///\n    /// Same as JavaScript's `Date.prototype.getUTCFullYear()`.\n    #[inline]\n    pub fn get_utc_full_year(&self, context: &mut Context) -> JsResult<JsValue> {\n        Date::get_full_year::<false>(&self.inner.clone().into(), &[JsValue::null()], context)\n    }\n\n    /// Returns the hours (0–23) in the specified date according\n    /// to universal time.\n    ///\n    /// Same as JavaScript's `Date.prototype.getUTCHours()`.\n    #[inline]\n    pub fn get_utc_hours(&self, context: &mut Context) -> JsResult<JsValue> {\n        Date::get_hours::<false>(&self.inner.clone().into(), &[JsValue::null()], context)\n    }\n\n    /// Returns the milliseconds (0–999) in the specified date\n    /// according to universal time.\n    ///\n    /// Same as JavaScript's `Date.prototype.getUTCMilliseconds()`.\n    #[inline]\n    pub fn get_utc_milliseconds(&self, context: &mut Context) -> JsResult<JsValue> {\n        Date::get_milliseconds::<false>(&self.inner.clone().into(), &[JsValue::null()], context)\n    }\n\n    /// Returns the minutes (0–59) in the specified date according\n    /// to universal time.\n    ///\n    /// Same as JavaScript's `Date.prototype.getUTCMinutes()`.\n    #[inline]\n    pub fn get_utc_minutes(&self, context: &mut Context) -> JsResult<JsValue> {\n        Date::get_minutes::<false>(&self.inner.clone().into(), &[JsValue::null()], context)\n    }\n\n    /// Returns the month (0–11) in the specified date according\n    /// to universal time.\n    ///\n    /// Same as JavaScript's `Date.prototype.getUTCMonth()`.\n    #[inline]\n    pub fn get_utc_month(&self, context: &mut Context) -> JsResult<JsValue> {\n        Date::get_month::<false>(&self.inner.clone().into(), &[JsValue::null()], context)\n    }\n\n    /// Returns the seconds (0–59) in the specified date according\n    /// to universal time.\n    ///\n    /// Same as JavaScript's `Date.prototype.getUTCSeconds()`.\n    #[inline]\n    pub fn get_utc_seconds(&self, context: &mut Context) -> JsResult<JsValue> {\n        Date::get_seconds::<false>(&self.inner.clone().into(), &[JsValue::null()], context)\n    }\n\n    /// Sets the day of the month for a specified date according\n    /// to local time.\n    /// Takes a `month_value`.\n    /// Return a `Number` representing the milliseconds elapsed between\n    /// the UNIX epoch and the given date.\n    ///\n    /// Same as JavaScript's `Date.prototype.setDate()`.\n    pub fn set_date<T>(&self, value: T, context: &mut Context) -> JsResult<JsValue>\n    where\n        T: Into<JsValue>,\n    {\n        Date::set_date::<true>(&self.inner.clone().into(), &[value.into()], context)\n    }\n\n    /// Sets the full year (e.g. 4 digits for 4-digit years) for a\n    /// specified date according to local time.\n    /// Takes [`year_value`, `month_value`, `date_value`]\n    /// Return a `Number` representing the milliseconds elapsed between\n    /// the UNIX epoch and updated date.\n    ///\n    /// Same as JavaScript's `Date.prototype.setFullYear()`.\n    #[inline]\n    pub fn set_full_year(&self, values: &[JsValue], context: &mut Context) -> JsResult<JsValue> {\n        Date::set_full_year::<true>(&self.inner.clone().into(), values, context)\n    }\n\n    /// Sets the hours for a specified date according to local time.\n    /// Takes [`hours_value`, `minutes_value`, `seconds_value`, `ms_value`]\n    /// Return a `Number` representing the milliseconds elapsed between\n    /// the UNIX epoch and the updated date.\n    ///\n    /// Same as JavaScript's `Date.prototype.setHours()`.\n    #[inline]\n    pub fn set_hours(&self, values: &[JsValue], context: &mut Context) -> JsResult<JsValue> {\n        Date::set_hours::<true>(&self.inner.clone().into(), values, context)\n    }\n\n    /// Sets the milliseconds for a specified date according to local time.\n    /// Takes a `milliseconds_value`\n    /// Return a `Number` representing the milliseconds elapsed between\n    /// the UNIX epoch and updated date.\n    ///\n    /// Same as JavaScript's `Date.prototype.setMilliseconds()`.\n    pub fn set_milliseconds<T>(&self, value: T, context: &mut Context) -> JsResult<JsValue>\n    where\n        T: Into<JsValue>,\n    {\n        Date::set_milliseconds::<true>(&self.inner.clone().into(), &[value.into()], context)\n    }\n\n    /// Sets the minutes for a specified date according to local time.\n    /// Takes [`minutes_value`, `seconds_value`, `ms_value`]\n    /// Return a `Number` representing the milliseconds elapsed between\n    /// the UNIX epoch and the updated date.\n    ///\n    /// Same as JavaScript's `Date.prototype.setMinutes()`.\n    #[inline]\n    pub fn set_minutes(&self, values: &[JsValue], context: &mut Context) -> JsResult<JsValue> {\n        Date::set_minutes::<true>(&self.inner.clone().into(), values, context)\n    }\n\n    /// Sets the month for a specified date according to local time.\n    /// Takes [`month_value`, `day_value`]\n    /// Return a `Number` representing the milliseconds elapsed between\n    /// the UNIX epoch and the updated date.\n    ///\n    /// Same as JavaScript's `Date.prototype.setMonth()`.\n    #[inline]\n    pub fn set_month(&self, values: &[JsValue], context: &mut Context) -> JsResult<JsValue> {\n        Date::set_month::<true>(&self.inner.clone().into(), values, context)\n    }\n\n    /// Sets the seconds for a specified date according to local time.\n    /// Takes [`seconds_value`, `ms_value`]\n    /// Return a `Number` representing the milliseconds elapsed between\n    /// the UNIX epoch and the updated date.\n    ///\n    /// Same as JavaScript's `Date.prototype.setSeconds()`.\n    #[inline]\n    pub fn set_seconds(&self, values: &[JsValue], context: &mut Context) -> JsResult<JsValue> {\n        Date::set_seconds::<true>(&self.inner.clone().into(), values, context)\n    }\n\n    /// Sets the Date object to the time represented by a number\n    /// of milliseconds since UNIX epoch.\n    /// Takes number of milliseconds since UNIX epoch.\n    /// Use negative numbers for times prior.\n    /// Return a `Number` representing the milliseconds elapsed between\n    /// the UNIX epoch and the updated date.\n    ///\n    /// Same as JavaScript's `Date.prototype.setTime()`.\n    pub fn set_time<T>(&self, value: T, context: &mut Context) -> JsResult<JsValue>\n    where\n        T: Into<JsValue>,\n    {\n        Date::set_time(&self.inner.clone().into(), &[value.into()], context)\n    }\n\n    /// Sets the day of the month for a specified date according\n    /// to universal time.\n    /// Takes a `month_value`.\n    /// Return a `Number` representing the milliseconds elapsed between\n    /// the UNIX epoch and the updated date.\n    ///\n    /// Same as JavaScript's `Date.prototype.setUTCDate()`.\n    pub fn set_utc_date<T>(&self, value: T, context: &mut Context) -> JsResult<JsValue>\n    where\n        T: Into<JsValue>,\n    {\n        Date::set_date::<false>(&self.inner.clone().into(), &[value.into()], context)\n    }\n\n    /// Sets the full year (e.g. 4 digits for 4-digit years) for a\n    /// specified date according to universal time.\n    /// Takes [`year_value`, `month_value`, `date_value`]\n    /// Return a `Number` representing the milliseconds elapsed between\n    /// the UNIX epoch and the updated date.\n    ///\n    /// Same as JavaScript's `Date.prototype.setUTCFullYear()`.\n    #[inline]\n    pub fn set_utc_full_year(\n        &self,\n        values: &[JsValue],\n        context: &mut Context,\n    ) -> JsResult<JsValue> {\n        Date::set_full_year::<false>(&self.inner.clone().into(), values, context)\n    }\n\n    /// Sets the hours for a specified date according to universal time.\n    /// Takes [`hours_value`, `minutes_value`, `seconds_value`, `ms_value`]\n    /// Return a `Number` representing the milliseconds elapsed between\n    /// the UNIX epoch and the updated dated.\n    ///\n    /// Same as JavaScript's `Date.prototype.setUTCHours()`.\n    #[inline]\n    pub fn set_utc_hours(&self, values: &[JsValue], context: &mut Context) -> JsResult<JsValue> {\n        Date::set_hours::<false>(&self.inner.clone().into(), values, context)\n    }\n\n    /// Sets the milliseconds for a specified date according to universal time.\n    /// Takes a `milliseconds_value`\n    /// Return a `Number` representing the milliseconds elapsed between\n    /// the UNIX epoch and the updated date.\n    ///\n    /// Same as JavaScript's `Date.prototype.setUTCMilliseconds()`.\n    pub fn set_utc_milliseconds<T>(&self, value: T, context: &mut Context) -> JsResult<JsValue>\n    where\n        T: Into<JsValue>,\n    {\n        Date::set_milliseconds::<false>(&self.inner.clone().into(), &[value.into()], context)\n    }\n\n    /// Sets the minutes for a specified date according to universal time.\n    /// Takes [`minutes_value`, `seconds_value`, `ms_value`]\n    /// Return a `Number` representing the milliseconds elapsed between\n    /// the UNIX epoch and the updated date.\n    ///\n    /// Same as JavaScript's `Date.prototype.setUTCMinutes()`.\n    #[inline]\n    pub fn set_utc_minutes(&self, values: &[JsValue], context: &mut Context) -> JsResult<JsValue> {\n        Date::set_minutes::<false>(&self.inner.clone().into(), values, context)\n    }\n\n    /// Sets the month for a specified date according to universal time.\n    /// Takes [`month_value`, `day_value`]\n    /// Return a `Number` representing the milliseconds elapsed between\n    /// the UNIX epoch and the updated date.\n    ///\n    /// Same as JavaScript's `Date.prototype.setUTCMonth()`.\n    #[inline]\n    pub fn set_utc_month(&self, values: &[JsValue], context: &mut Context) -> JsResult<JsValue> {\n        Date::set_month::<false>(&self.inner.clone().into(), values, context)\n    }\n\n    /// Sets the seconds for a specified date according to universal time.\n    /// Takes [`seconds_value`, `ms_value`]\n    /// Return a `Number` representing the milliseconds elapsed between\n    /// the UNIX epoch and the updated date.\n    ///\n    /// Same as JavaScript's `Date.prototype.setUTCSeconds()`.\n    #[inline]\n    pub fn set_utc_seconds(&self, values: &[JsValue], context: &mut Context) -> JsResult<JsValue> {\n        Date::set_seconds::<false>(&self.inner.clone().into(), values, context)\n    }\n\n    /// Returns the \"date\" portion of the Date as a human-readable string.\n    ///\n    /// Same as JavaScript's `Date.prototype.toDateString()`.\n    #[inline]\n    pub fn to_date_string(&self, context: &mut Context) -> JsResult<JsValue> {\n        Date::to_date_string(&self.inner.clone().into(), &[JsValue::null()], context)\n    }\n\n    /// DEPRECATED: This feature is no longer recommended.\n    /// USE: `to_utc_string()` instead.\n    /// Returns a string representing the Date based on the GMT timezone.\n    ///\n    /// Same as JavaScript's legacy `Date.prototype.toGMTString()`\n    #[deprecated]\n    #[inline]\n    pub fn to_gmt_string(&self, context: &mut Context) -> JsResult<JsValue> {\n        Date::to_utc_string(&self.inner.clone().into(), &[JsValue::null()], context)\n    }\n\n    /// Returns the given date in the ISO 8601 format according to universal\n    /// time.\n    ///\n    /// Same as JavaScript's `Date.prototype.toISOString()`.\n    #[inline]\n    pub fn to_iso_string(&self, context: &mut Context) -> JsResult<JsValue> {\n        Date::to_iso_string(&self.inner.clone().into(), &[JsValue::null()], context)\n    }\n\n    /// Returns a string representing the Date using `to_iso_string()`.\n    ///\n    /// Same as JavaScript's `Date.prototype.toJSON()`.\n    #[inline]\n    pub fn to_json(&self, context: &mut Context) -> JsResult<JsValue> {\n        Date::to_json(&self.inner.clone().into(), &[JsValue::null()], context)\n    }\n\n    /// Returns a string representing the date portion of the given Date instance\n    /// according to language-specific conventions.\n    /// Takes [locales, options]\n    ///\n    /// Same as JavaScript's `Date.prototype.toLocaleDateString()`.\n    #[inline]\n    pub fn to_local_date_string(\n        &self,\n        values: &[JsValue],\n        context: &mut Context,\n    ) -> JsResult<JsValue> {\n        Date::to_locale_date_string(&self.inner.clone().into(), values, context)\n    }\n\n    /// Returns a string representing the given date according to language-specific conventions.\n    /// Takes [locales, options]\n    ///\n    /// Same as JavaScript's `Date.prototype.toLocaleDateString()`.\n    #[inline]\n    pub fn to_locale_string(&self, values: &[JsValue], context: &mut Context) -> JsResult<JsValue> {\n        Date::to_locale_string(&self.inner.clone().into(), values, context)\n    }\n\n    /// Returns the \"time\" portion of the Date as human-readable string.\n    ///\n    /// Same as JavaScript's `Date.prototype.toTimeString()`.\n    #[inline]\n    pub fn to_locale_time_string(\n        &self,\n        values: &[JsValue],\n        context: &mut Context,\n    ) -> JsResult<JsValue> {\n        Date::to_locale_time_string(&self.inner.clone().into(), values, context)\n    }\n\n    /// Returns a string representing the specified Date object.\n    ///\n    /// Same as JavaScript's `Date.prototype.toString()`.\n    #[inline]\n    pub fn to_string(&self, context: &mut Context) -> JsResult<JsValue> {\n        Date::to_string(&self.inner.clone().into(), &[JsValue::null()], context)\n    }\n\n    /// Returns the \"time\" portion of the Date as human-readable string.\n    ///\n    /// Same as JavaScript's `Date.prototype.toTimeString()`.\n    #[inline]\n    pub fn to_time_string(&self, context: &mut Context) -> JsResult<JsValue> {\n        Date::to_time_string(&self.inner.clone().into(), &[JsValue::null()], context)\n    }\n\n    /// Returns a string representing the given date using the UTC time zone.\n    ///\n    /// Same as JavaScript's `Date.prototype.toUTCString()`.\n    #[inline]\n    pub fn to_utc_string(&self, context: &mut Context) -> JsResult<JsValue> {\n        Date::to_utc_string(&self.inner.clone().into(), &[JsValue::null()], context)\n    }\n\n    /// Returns the primitive value pf Date object.\n    ///\n    /// Same as JavaScript's `Date.prototype.valueOf()`.\n    #[inline]\n    pub fn value_of(&self, context: &mut Context) -> JsResult<JsValue> {\n        Date::value_of(&self.inner.clone().into(), &[JsValue::null()], context)\n    }\n\n    /// Utility create a `Date` object from RFC3339 string\n    pub fn new_from_parse(value: &JsValue, context: &mut Context) -> JsResult<Self> {\n        let prototype = context.intrinsics().constructors().date().prototype();\n        let string = value\n            .to_string(context)?\n            .to_std_string()\n            .map_err(|_| JsNativeError::typ().with_message(\"unpaired surrogate on date string\"))?;\n        let t = OffsetDateTime::parse(&string, &Rfc3339)\n            .map_err(|err| JsNativeError::typ().with_message(err.to_string()))?;\n        let date_time = Date::new((t.unix_timestamp() * 1000 + i64::from(t.millisecond())) as f64);\n\n        Ok(Self {\n            inner: JsObject::from_proto_and_data_with_shared_shape(\n                context.root_shape(),\n                prototype,\n                date_time,\n            )\n            .upcast(),\n        })\n    }\n}\n\nimpl From<JsDate> for JsObject {\n    #[inline]\n    fn from(o: JsDate) -> Self {\n        o.inner.clone()\n    }\n}\n\nimpl From<JsDate> for JsValue {\n    #[inline]\n    fn from(o: JsDate) -> Self {\n        o.inner.clone().into()\n    }\n}\n\nimpl Deref for JsDate {\n    type Target = JsObject;\n\n    #[inline]\n    fn deref(&self) -> &Self::Target {\n        &self.inner\n    }\n}\n\nimpl TryFromJs for JsDate {\n    fn try_from_js(value: &JsValue, _context: &mut Context) -> JsResult<Self> {\n        if let Some(o) = value.as_object() {\n            Self::from_object(o.clone())\n        } else {\n            Err(JsNativeError::typ()\n                .with_message(\"value is not a Date object\")\n                .into())\n        }\n    }\n}\n"
  },
  {
    "path": "core/engine/src/object/builtins/jsfinalization_registry.rs",
    "content": "use std::ops::Deref;\n\nuse boa_gc::{Finalize, Trace};\n\nuse crate::{\n    builtins::finalization_registry::FinalizationRegistry, object::JsObjectType, realm::Realm,\n    value::TryFromJs, Context, JsNativeError, JsObject, JsResult, JsValue,\n};\n\n/// `JsFinalizationRegistry` provides a wrapper for Boa's implementation of the ECMAScript\n/// [`FinalizationRegistry`] object.\n///\n/// [`FinalizationRegistry`]: https://tc39.es/ecma262/#sec-finalization-registry-objects\n#[derive(Debug, Clone, Trace, Finalize)]\npub struct JsFinalizationRegistry {\n    inner: JsObject,\n}\n\nimpl JsFinalizationRegistry {\n    /// Creates a [`JsFinalizationRegistry`] from a [`JsObject`], erroring if the object is not\n    /// of the required kind.\n    #[inline]\n    pub fn from_object(object: JsObject) -> JsResult<Self> {\n        if object.is::<FinalizationRegistry>() {\n            Ok(Self { inner: object })\n        } else {\n            Err(JsNativeError::typ()\n                .with_message(\"object is not a TypedArray\")\n                .into())\n        }\n    }\n\n    /// Gets the `[[Realm]]` slot of this [`FinalizationRegistry`].\n    pub fn realm(&self) -> Realm {\n        self.downcast_ref::<FinalizationRegistry>()\n            .expect(\"must be a `FinalizationRegistry\")\n            .realm\n            .clone()\n    }\n\n    /// Returns `true` if this finalization registry has unreachable cells.\n    pub(crate) fn needs_cleanup(&self) -> bool {\n        self.downcast_ref::<FinalizationRegistry>()\n            .expect(\"must be a `FinalizationRegistry\")\n            .needs_cleanup\n            .get()\n    }\n\n    /// Clears the `needs_cleanup` flag from the registry.\n    pub(crate) fn clear_needs_cleanup(&self) {\n        self.downcast_ref::<FinalizationRegistry>()\n            .expect(\"must be a `FinalizationRegistry\")\n            .needs_cleanup\n            .set(false)\n    }\n\n    /// Abstract operation [`CleanupFinalizationRegistry ( finalizationRegistry )`][spec].\n    ///\n    /// Cleans up all the cells of the finalization registry that are determined to be\n    /// unreachable by the garbage collector.\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-cleanup-finalization-registry\n    pub fn cleanup(&self, context: &mut Context) -> JsResult<()> {\n        FinalizationRegistry::cleanup(&self, context)\n    }\n\n    /// Creates a new [`JsFinalizationRegistry`] from an object, without checking if the object is\n    /// a finalization registry.\n    pub(crate) fn from_object_unchecked(object: JsObject) -> Self {\n        Self { inner: object }\n    }\n}\n\nimpl From<JsFinalizationRegistry> for JsObject {\n    #[inline]\n    fn from(o: JsFinalizationRegistry) -> Self {\n        o.inner.clone()\n    }\n}\n\nimpl From<JsFinalizationRegistry> for JsValue {\n    #[inline]\n    fn from(o: JsFinalizationRegistry) -> Self {\n        o.inner.clone().into()\n    }\n}\n\nimpl Deref for JsFinalizationRegistry {\n    type Target = JsObject;\n\n    #[inline]\n    fn deref(&self) -> &Self::Target {\n        &self.inner\n    }\n}\n\nimpl JsObjectType for JsFinalizationRegistry {}\n\nimpl TryFromJs for JsFinalizationRegistry {\n    fn try_from_js(value: &JsValue, _context: &mut Context) -> JsResult<Self> {\n        match value {\n            JsValue::Object(o) => Self::from_object(o.clone()),\n            _ => Err(JsNativeError::typ()\n                .with_message(\"value is not a FinalizationRegistry object\")\n                .into()),\n        }\n    }\n}\n"
  },
  {
    "path": "core/engine/src/object/builtins/jsfunction.rs",
    "content": "//! A Rust API wrapper for Boa's `Function` Builtin ECMAScript Object\nuse crate::js_string;\nuse crate::{\n    Context, JsNativeError, JsResult, JsValue, NativeFunction, TryIntoJsResult,\n    builtins::function::ConstructorKind, native_function::NativeFunctionObject, object::JsObject,\n    value::TryFromJs,\n};\nuse boa_gc::{Finalize, Trace};\nuse std::marker::PhantomData;\nuse std::ops::Deref;\n\n/// A trait for converting a tuple of Rust values into a vector of `JsValue`,\n/// to be used as arguments for a JavaScript function.\npub trait TryIntoJsArguments {\n    /// Convert a tuple of Rust values into a vector of `JsValue`.\n    /// This is automatically implemented for tuples that implement\n    /// `TryIntoJsResult`.\n    fn into_js_args(self, cx: &mut Context) -> JsResult<Vec<JsValue>>;\n}\n\nmacro_rules! impl_try_into_js_args {\n    ($($n: ident: $t: ident),*) => {\n        impl<$($t),*> TryIntoJsArguments for ($($t,)*) where $($t: TryIntoJsResult),* {\n            fn into_js_args(self, cx: &mut Context) -> JsResult<Vec<JsValue>> {\n                let ($($n,)*) = self;\n                Ok(vec![$($n.try_into_js_result(cx)?),*])\n            }\n        }\n    };\n}\n\nimpl_try_into_js_args!(a: A);\nimpl_try_into_js_args!(a: A, b: B);\nimpl_try_into_js_args!(a: A, b: B, c: C);\nimpl_try_into_js_args!(a: A, b: B, c: C, d: D);\nimpl_try_into_js_args!(a: A, b: B, c: C, d: D, e: E);\n\n/// A JavaScript `Function` rust object, typed. This adds types to\n/// a JavaScript exported function, allowing for type checking and\n/// type conversion in Rust. Those types must convert to a [`JsValue`]\n/// but will not be verified at runtime (since JavaScript doesn't\n/// actually have strong typing).\n///\n/// To create this type, use the [`JsFunction::typed`] method.\n#[derive(Debug, Clone, Trace, Finalize)]\npub struct TypedJsFunction<A: TryIntoJsArguments, R: TryFromJs> {\n    inner: JsFunction,\n    _args: PhantomData<A>,\n    _ret: PhantomData<R>,\n}\n\nimpl<A: TryIntoJsArguments, R: TryFromJs> TypedJsFunction<A, R> {\n    /// Transforms this typed function back into a regular `JsFunction`.\n    #[must_use]\n    pub fn into_inner(self) -> JsFunction {\n        self.inner.clone()\n    }\n\n    /// Get the inner `JsFunction` without consuming this object.\n    #[must_use]\n    pub fn as_js_function(&self) -> &JsFunction {\n        &self.inner\n    }\n\n    /// Call the function with the given arguments.\n    #[inline]\n    #[cfg_attr(feature = \"native-backtrace\", track_caller)]\n    pub fn call(&self, context: &mut Context, args: A) -> JsResult<R> {\n        self.call_with_this(&JsValue::undefined(), context, args)\n    }\n\n    /// Call the function with the given argument and `this`.\n    #[inline]\n    #[cfg_attr(feature = \"native-backtrace\", track_caller)]\n    pub fn call_with_this(&self, this: &JsValue, context: &mut Context, args: A) -> JsResult<R> {\n        let arguments = args.into_js_args(context)?;\n        let result = self.inner.call(this, &arguments, context)?;\n        R::try_from_js(&result, context)\n    }\n}\n\nimpl<A: TryIntoJsArguments, R: TryFromJs> TryFromJs for TypedJsFunction<A, R> {\n    fn try_from_js(value: &JsValue, _context: &mut Context) -> JsResult<Self> {\n        if let Some(o) = value.as_object() {\n            JsFunction::from_object(o.clone())\n                .ok_or_else(|| {\n                    JsNativeError::typ()\n                        .with_message(\"object is not a function\")\n                        .into()\n                })\n                .map(JsFunction::typed)\n        } else {\n            Err(JsNativeError::typ()\n                .with_message(\"value is not a Function object\")\n                .into())\n        }\n    }\n}\n\nimpl<A: TryIntoJsArguments, R: TryFromJs> From<TypedJsFunction<A, R>> for JsValue {\n    #[inline]\n    fn from(o: TypedJsFunction<A, R>) -> Self {\n        o.into_inner().into()\n    }\n}\n\nimpl<A: TryIntoJsArguments, R: TryFromJs> From<TypedJsFunction<A, R>> for JsFunction {\n    fn from(value: TypedJsFunction<A, R>) -> Self {\n        value.inner.clone()\n    }\n}\n\n/// JavaScript `Function` rust object.\n#[derive(Debug, Clone, Trace, Finalize)]\npub struct JsFunction {\n    inner: JsObject,\n}\n\nimpl JsFunction {\n    /// Creates a new `JsFunction` from an object, without checking if the object is callable.\n    pub(crate) fn from_object_unchecked(object: JsObject) -> Self {\n        Self { inner: object }\n    }\n\n    /// Creates a new, empty intrinsic function object with only its function internal methods set.\n    ///\n    /// Mainly used to initialize objects before a [`Context`] is available to do so.\n    ///\n    /// [`Context`]: crate::Context\n    pub(crate) fn empty_intrinsic_function(constructor: bool) -> Self {\n        Self {\n            inner: JsObject::from_proto_and_data(\n                None,\n                NativeFunctionObject {\n                    f: NativeFunction::from_fn_ptr(|_, _, _| Ok(JsValue::undefined())),\n                    name: js_string!(),\n                    constructor: constructor.then_some(ConstructorKind::Base),\n                    realm: None,\n                },\n            ),\n        }\n    }\n\n    /// Creates a [`JsFunction`] from a [`JsObject`], or returns `None` if the object is not a function.\n    ///\n    /// This does not clone the fields of the function, it only does a shallow clone of the object.\n    #[inline]\n    #[must_use]\n    pub fn from_object(object: JsObject) -> Option<Self> {\n        object\n            .is_callable()\n            .then(|| Self::from_object_unchecked(object))\n    }\n\n    /// Creates a `TypedJsFunction` from a `JsFunction`.\n    #[inline]\n    #[must_use]\n    pub fn typed<A: TryIntoJsArguments, R: TryFromJs>(self) -> TypedJsFunction<A, R> {\n        TypedJsFunction {\n            inner: self,\n            _args: PhantomData,\n            _ret: PhantomData,\n        }\n    }\n}\n\nimpl From<JsFunction> for JsObject {\n    #[inline]\n    fn from(o: JsFunction) -> Self {\n        o.inner.clone()\n    }\n}\n\nimpl From<JsFunction> for JsValue {\n    #[inline]\n    fn from(o: JsFunction) -> Self {\n        o.inner.clone().into()\n    }\n}\n\nimpl Deref for JsFunction {\n    type Target = JsObject;\n\n    #[inline]\n    fn deref(&self) -> &Self::Target {\n        &self.inner\n    }\n}\n\nimpl TryFromJs for JsFunction {\n    fn try_from_js(value: &JsValue, _context: &mut Context) -> JsResult<Self> {\n        if let Some(o) = value.as_object() {\n            Self::from_object(o.clone()).ok_or_else(|| {\n                JsNativeError::typ()\n                    .with_message(\"object is not a function\")\n                    .into()\n            })\n        } else {\n            Err(JsNativeError::typ()\n                .with_message(\"value is not a Function object\")\n                .into())\n        }\n    }\n}\n"
  },
  {
    "path": "core/engine/src/object/builtins/jsgenerator.rs",
    "content": "//! A Rust API wrapper for Boa's `Generator` Builtin ECMAScript Object\nuse crate::{\n    Context, JsNativeError, JsResult, JsValue, builtins::generator::Generator, object::JsObject,\n    value::TryFromJs,\n};\n\nuse boa_gc::{Finalize, Trace};\nuse std::ops::Deref;\n\n/// `JsGenerator` provides a wrapper for Boa's implementation of the ECMAScript `Generator` builtin object\n#[derive(Debug, Clone, Trace, Finalize)]\npub struct JsGenerator {\n    inner: JsObject,\n}\n\nimpl JsGenerator {\n    /// Creates a `JsGenerator` from a generator `JsObject`\n    #[inline]\n    pub fn from_object(object: JsObject) -> JsResult<Self> {\n        if object.is::<Generator>() {\n            Ok(Self { inner: object })\n        } else {\n            Err(JsNativeError::typ()\n                .with_message(\"object is not a Generator\")\n                .into())\n        }\n    }\n\n    /// Calls `Generator.prototype.next()`\n    ///\n    /// This method returns an object with the properties `done` and `value`\n    pub fn next<T>(&self, value: T, context: &mut Context) -> JsResult<JsValue>\n    where\n        T: Into<JsValue>,\n    {\n        Generator::next(&self.inner.clone().into(), &[value.into()], context)\n    }\n\n    /// Calls `Generator.prototype.return()`\n    ///\n    /// This method returns the given value and finishes the generator\n    pub fn r#return<T>(&self, value: T, context: &mut Context) -> JsResult<JsValue>\n    where\n        T: Into<JsValue>,\n    {\n        Generator::r#return(&self.inner.clone().into(), &[value.into()], context)\n    }\n\n    /// Calls `Generator.prototype.throw()`\n    ///\n    /// This method resumes the execution of a generator by throwing an error and returning an\n    /// an object with the properties `done` and `value`\n    pub fn throw<T>(&self, value: T, context: &mut Context) -> JsResult<JsValue>\n    where\n        T: Into<JsValue>,\n    {\n        Generator::throw(&self.inner.clone().into(), &[value.into()], context)\n    }\n}\n\nimpl From<JsGenerator> for JsObject {\n    #[inline]\n    fn from(o: JsGenerator) -> Self {\n        o.inner.clone()\n    }\n}\n\nimpl From<JsGenerator> for JsValue {\n    #[inline]\n    fn from(o: JsGenerator) -> Self {\n        o.inner.clone().into()\n    }\n}\n\nimpl Deref for JsGenerator {\n    type Target = JsObject;\n\n    #[inline]\n    fn deref(&self) -> &Self::Target {\n        &self.inner\n    }\n}\n\nimpl TryFromJs for JsGenerator {\n    fn try_from_js(value: &JsValue, _context: &mut Context) -> JsResult<Self> {\n        if let Some(o) = value.as_object() {\n            Self::from_object(o.clone())\n        } else {\n            Err(JsNativeError::typ()\n                .with_message(\"value is not a Generator object\")\n                .into())\n        }\n    }\n}\n"
  },
  {
    "path": "core/engine/src/object/builtins/jsgeneratorfunction.rs",
    "content": "//! A Rust API wrapper for Boa's `GeneratorFunction` Builtin ECMAScript Object\nuse crate::{\n    Context, JsNativeError, JsResult, JsValue,\n    builtins::function::OrdinaryFunction,\n    object::{JsObject, builtins::JsGenerator},\n    value::TryFromJs,\n};\nuse boa_gc::{Finalize, Trace};\nuse std::ops::Deref;\n\n/// `JsGeneratorFunction` provides a wrapper for Boa's implementation of the ECMAScript `GeneratorFunction` builtin object.\n#[derive(Debug, Clone, Trace, Finalize)]\npub struct JsGeneratorFunction {\n    inner: JsObject,\n}\n\nimpl JsGeneratorFunction {\n    /// Creates a `JsGeneratorFunction` from a generator function `JsObject`.\n    ///\n    /// More information:\n    ///  - [MDN documentation][mdn]\n    ///\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/GeneratorFunction\n    #[inline]\n    pub fn from_object(object: JsObject) -> JsResult<Self> {\n        if object\n            .downcast_ref::<OrdinaryFunction>()\n            .is_some_and(|f| f.code.is_generator() && !f.code.is_async())\n        {\n            Ok(Self { inner: object })\n        } else {\n            Err(JsNativeError::typ()\n                .with_message(\"object is not a GeneratorFunction\")\n                .into())\n        }\n    }\n\n    /// Calls the generator function and returns a new `JsGenerator` object.\n    ///\n    /// More information:\n    ///  - [MDN documentation][mdn]\n    ///\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/GeneratorFunction\n    pub fn call(\n        &self,\n        this: &JsValue,\n        args: &[JsValue],\n        context: &mut Context,\n    ) -> JsResult<JsGenerator> {\n        let value = self.inner.call(this, args, context)?;\n        let obj = value\n            .as_object()\n            .ok_or_else(|| {\n                JsNativeError::typ().with_message(\"generator function did not return an object\")\n            })?\n            .clone();\n        JsGenerator::from_object(obj)\n    }\n}\n\nimpl From<JsGeneratorFunction> for JsObject {\n    #[inline]\n    fn from(o: JsGeneratorFunction) -> Self {\n        o.inner.clone()\n    }\n}\n\nimpl From<JsGeneratorFunction> for JsValue {\n    #[inline]\n    fn from(o: JsGeneratorFunction) -> Self {\n        o.inner.clone().into()\n    }\n}\n\nimpl Deref for JsGeneratorFunction {\n    type Target = JsObject;\n    #[inline]\n    fn deref(&self) -> &Self::Target {\n        &self.inner\n    }\n}\n\nimpl TryFromJs for JsGeneratorFunction {\n    fn try_from_js(value: &JsValue, _context: &mut Context) -> JsResult<Self> {\n        if let Some(o) = value.as_object() {\n            Self::from_object(o.clone())\n        } else {\n            Err(JsNativeError::typ()\n                .with_message(\"value is not a GeneratorFunction object\")\n                .into())\n        }\n    }\n}\n"
  },
  {
    "path": "core/engine/src/object/builtins/jsmap.rs",
    "content": "//! A Rust API wrapper for Boa's `Map` Builtin ECMAScript Object\nuse crate::{\n    Context, JsResult, JsValue,\n    builtins::{\n        Map,\n        iterable::IteratorHint,\n        map::{add_entries_from_iterable, ordered_map::OrderedMap},\n    },\n    error::JsNativeError,\n    js_string,\n    object::{JsFunction, JsMapIterator, JsObject},\n    value::TryFromJs,\n};\n\nuse boa_gc::{Finalize, Trace};\nuse std::ops::Deref;\n\n/// `JsMap` provides a wrapper for Boa's implementation of the ECMAScript `Map` object.\n///\n/// # Examples\n///\n/// Create a `JsMap` and set a new entry\n/// ```\n/// # use boa_engine::{\n/// #  object::builtins::JsMap,\n/// #  Context, JsValue, JsResult, js_string\n/// # };\n/// # fn main() -> JsResult<()> {\n/// // Create default `Context`\n/// let context = &mut Context::default();\n///\n/// // Create a new empty `JsMap`.\n/// let map = JsMap::new(context);\n///\n/// // Set key-value pairs for the `JsMap`.\n/// map.set(js_string!(\"Key-1\"), js_string!(\"Value-1\"), context)?;\n/// map.set(js_string!(\"Key-2\"), 10, context)?;\n///\n/// assert_eq!(map.get_size(context)?, 2.into());\n/// # Ok(())\n/// # }\n/// ```\n///\n/// Create a `JsMap` from a `JsArray`\n/// ```\n/// # use boa_engine::{\n/// #    object::builtins::{JsArray, JsMap},\n/// #    Context, JsValue, JsResult, js_string\n/// # };\n/// # fn main() -> JsResult<()> {\n/// // Create a default `Context`\n/// let context = &mut Context::default();\n///\n/// // Create an array of two `[key, value]` pairs\n/// let js_array = JsArray::new(context)?;\n///\n/// // Create a `[key, value]` pair of JsValues\n/// let vec_one: Vec<JsValue> = vec![\n///     js_string!(\"first-key\").into(),\n///     js_string!(\"first-value\").into()\n/// ];\n///\n/// // We create an push our `[key, value]` pair onto our array as a `JsArray`\n/// js_array.push(JsArray::from_iter(vec_one, context), context)?;\n///\n/// // Create a `JsMap` from the `JsArray` using it's iterable property.\n/// let js_iterable_map = JsMap::from_js_iterable(&js_array.into(), context)?;\n///\n/// assert_eq!(\n///     js_iterable_map.get(js_string!(\"first-key\"), context)?,\n///     js_string!(\"first-value\").into()\n/// );\n///\n/// # Ok(())\n/// }\n/// ```\n#[derive(Debug, Clone, Trace, Finalize)]\npub struct JsMap {\n    inner: JsObject,\n}\n\nimpl JsMap {\n    /// Creates a new empty [`JsMap`] object.\n    ///\n    /// # Example\n    /// ```\n    /// # use boa_engine::{\n    /// #    object::builtins::JsMap,\n    /// #    Context, JsValue,\n    /// # };\n    /// # // Create a new context.\n    /// # let context = &mut Context::default();\n    /// // Create a new empty `JsMap`.\n    /// let map = JsMap::new(context);\n    /// ```\n    #[inline]\n    pub fn new(context: &mut Context) -> Self {\n        let map = Self::create_map(context);\n        Self { inner: map }\n    }\n\n    /// Create a new [`JsMap`] object from a [`JsObject`] that has an `@@Iterator` field.\n    ///\n    /// # Examples\n    /// ```\n    /// # use boa_engine::{\n    /// #    object::builtins::{JsArray, JsMap},\n    /// #    Context, JsResult, JsValue, js_string\n    /// # };\n    /// # fn main() -> JsResult<()> {\n    /// # // Create a default `Context`\n    /// # let context = &mut Context::default();\n    /// // Create an array of two `[key, value]` pairs\n    /// let js_array = JsArray::new(context)?;\n    ///\n    /// // Create a `[key, value]` pair of JsValues and add it to the `JsArray` as a `JsArray`\n    /// let vec_one: Vec<JsValue> = vec![js_string!(\"first-key\").into(), js_string!(\"first-value\").into()];\n    /// js_array.push(JsArray::from_iter(vec_one, context), context)?;\n    ///\n    /// // Create a `JsMap` from the `JsArray` using it's iterable property.\n    /// let js_iterable_map = JsMap::from_js_iterable(&js_array.into(), context)?;\n    ///\n    /// # Ok(())\n    /// # }\n    /// ```\n    pub fn from_js_iterable(iterable: &JsValue, context: &mut Context) -> JsResult<Self> {\n        // Create a new map object.\n        let map = Self::create_map(context);\n\n        // Let adder be Get(map, \"set\") per spec. This action should not fail with default map.\n        let adder = map\n            .get(js_string!(\"set\"), context)?\n            .as_function()\n            .ok_or_else(|| {\n                JsNativeError::typ().with_message(\"property `set` on new `Map` must be callable\")\n            })?;\n\n        let _completion_record = add_entries_from_iterable(&map, iterable, &adder, context)?;\n\n        Ok(Self { inner: map })\n    }\n\n    /// Creates a [`JsMap`] from a valid [`JsObject`], or returns a `TypeError` if the provided object is not a [`JsMap`]\n    ///\n    /// # Examples\n    ///\n    /// ### Valid Example - returns a `JsMap` object\n    /// ```\n    /// # use boa_engine::{\n    /// #    builtins::map::ordered_map::OrderedMap,\n    /// #    object::{builtins::JsMap, JsObject},\n    /// #    Context, JsValue, JsResult,\n    /// # };\n    /// # fn main() -> JsResult<()> {\n    /// # let context = &mut Context::default();\n    /// // `some_object` can be any JavaScript `Map` object.\n    /// let some_object = JsObject::from_proto_and_data(\n    ///     context.intrinsics().constructors().map().prototype(),\n    ///     OrderedMap::<JsValue>::new(),\n    /// );\n    ///\n    /// // Create `JsMap` object with incoming object.\n    /// let js_map = JsMap::from_object(some_object)?;\n    /// # Ok(())\n    /// # }\n    /// ```\n    ///\n    /// ### Invalid Example - returns a `TypeError` with the message \"object is not a Map\"\n    /// ```\n    /// # use boa_engine::{\n    /// #    object::{JsObject, builtins::{JsArray, JsMap}},\n    /// #    Context, JsResult, JsValue,\n    /// # };\n    /// # fn main() -> JsResult<()> {\n    /// # let context = &mut Context::default();\n    /// let some_object = JsArray::new(context)?;\n    ///\n    /// // `some_object` is an Array object, not a map object\n    /// assert!(JsMap::from_object(some_object.into()).is_err());\n    /// # Ok(())\n    /// # }\n    /// ```\n    #[inline]\n    pub fn from_object(object: JsObject) -> JsResult<Self> {\n        if object.is::<OrderedMap<JsValue>>() {\n            Ok(Self { inner: object })\n        } else {\n            Err(JsNativeError::typ()\n                .with_message(\"object is not a Map\")\n                .into())\n        }\n    }\n\n    // Utility function to generate the default `Map` object.\n    fn create_map(context: &mut Context) -> JsObject {\n        // Get default Map prototype\n        let prototype = context.intrinsics().constructors().map().prototype();\n\n        // Create a default map object with [[MapData]] as a new empty list\n        JsObject::from_proto_and_data_with_shared_shape(\n            context.root_shape(),\n            prototype,\n            <OrderedMap<JsValue>>::new(),\n        )\n        .upcast()\n    }\n\n    /// Returns a new [`JsMapIterator`] object that yields the `[key, value]` pairs within the [`JsMap`] in insertion order.\n    #[inline]\n    pub fn entries(&self, context: &mut Context) -> JsResult<JsMapIterator> {\n        let iterator_record = Map::entries(&self.inner.clone().into(), &[], context)?\n            .get_iterator(IteratorHint::Sync, context)?;\n        let map_iterator_object = iterator_record.iterator();\n        JsMapIterator::from_object(map_iterator_object.clone())\n    }\n\n    /// Returns a new [`JsMapIterator`] object that yields the `key` for each element within the [`JsMap`] in insertion order.\n    #[inline]\n    pub fn keys(&self, context: &mut Context) -> JsResult<JsMapIterator> {\n        let iterator_record = Map::keys(&self.inner.clone().into(), &[], context)?\n            .get_iterator(IteratorHint::Sync, context)?;\n        let map_iterator_object = iterator_record.iterator();\n        JsMapIterator::from_object(map_iterator_object.clone())\n    }\n\n    /// Inserts a new entry into the [`JsMap`] object\n    ///\n    /// # Example\n    ///\n    /// ```\n    /// # use boa_engine::{\n    /// #    object::builtins::JsMap,\n    /// #    Context, JsValue, JsResult, js_string\n    /// # };\n    /// # fn main() -> JsResult<()> {\n    /// # let context = &mut Context::default();\n    /// let js_map = JsMap::new(context);\n    ///\n    /// js_map.set(js_string!(\"foo\"), js_string!(\"bar\"), context)?;\n    /// js_map.set(2, 4, context)?;\n    ///\n    /// assert_eq!(\n    ///     js_map.get(js_string!(\"foo\"), context)?,\n    ///     js_string!(\"bar\").into()\n    /// );\n    /// assert_eq!(js_map.get(2, context)?, 4.into());\n    /// # Ok(())\n    /// # }\n    /// ```\n    pub fn set<K, V>(&self, key: K, value: V, context: &mut Context) -> JsResult<JsValue>\n    where\n        K: Into<JsValue>,\n        V: Into<JsValue>,\n    {\n        Map::set(\n            &self.inner.clone().into(),\n            &[key.into(), value.into()],\n            context,\n        )\n    }\n\n    /// Gets the size of the [`JsMap`] object.\n    ///\n    /// # Example\n    ///\n    /// ```\n    /// # use boa_engine::{\n    /// #    object::builtins::JsMap,\n    /// #    Context, JsValue, JsResult, js_string\n    /// # };\n    /// # fn main() -> JsResult<()> {\n    /// # let context = &mut Context::default();\n    /// let js_map = JsMap::new(context);\n    ///\n    /// js_map.set(js_string!(\"foo\"), js_string!(\"bar\"), context)?;\n    ///\n    /// let map_size = js_map.get_size(context)?;\n    ///\n    /// assert_eq!(map_size, 1.into());\n    /// # Ok(())\n    /// # }\n    /// ```\n    #[inline]\n    pub fn get_size(&self, context: &mut Context) -> JsResult<JsValue> {\n        Map::get_size(&self.inner.clone().into(), &[], context)\n    }\n\n    /// Removes element from [`JsMap`] with a matching `key` value.\n    ///\n    /// # Example\n    ///\n    /// ```\n    /// # use boa_engine::{\n    /// #    object::builtins::JsMap,\n    /// #    Context, JsValue, JsResult, js_string\n    /// # };\n    /// # fn main() -> JsResult<()> {\n    /// # let context = &mut Context::default();\n    /// let js_map = JsMap::new(context);\n    /// js_map.set(js_string!(\"foo\"), js_string!(\"bar\"), context)?;\n    /// js_map.set(js_string!(\"hello\"), js_string!(\"world\"), context)?;\n    ///\n    /// js_map.delete(js_string!(\"foo\"), context)?;\n    ///\n    /// assert_eq!(js_map.get_size(context)?, 1.into());\n    /// assert_eq!(\n    ///     js_map.get(js_string!(\"foo\"), context)?,\n    ///     JsValue::undefined()\n    /// );\n    /// # Ok(())\n    /// # }\n    /// ```\n    pub fn delete<T>(&self, key: T, context: &mut Context) -> JsResult<JsValue>\n    where\n        T: Into<JsValue>,\n    {\n        Map::delete(&self.inner.clone().into(), &[key.into()], context)\n    }\n\n    /// Gets the value associated with the specified key within the [`JsMap`], or `undefined` if the key does not exist.\n    ///\n    /// # Example\n    ///\n    /// ```\n    /// # use boa_engine::{\n    /// #    object::builtins::JsMap,\n    /// #    Context, JsValue, JsResult, js_string\n    /// # };\n    /// # fn main() -> JsResult<()> {\n    /// # let context = &mut Context::default();\n    /// let js_map = JsMap::new(context);\n    /// js_map.set(js_string!(\"foo\"), js_string!(\"bar\"), context)?;\n    ///\n    /// let retrieved_value = js_map.get(js_string!(\"foo\"), context)?;\n    ///\n    /// assert_eq!(retrieved_value, js_string!(\"bar\").into());\n    /// # Ok(())\n    /// # }\n    /// ```\n    pub fn get<T>(&self, key: T, context: &mut Context) -> JsResult<JsValue>\n    where\n        T: Into<JsValue>,\n    {\n        Map::get(&self.inner.clone().into(), &[key.into()], context)\n    }\n\n    /// Removes all entries from the [`JsMap`].\n    ///\n    /// # Example\n    ///\n    /// ```\n    /// # use boa_engine::{\n    /// #    object::builtins::JsMap,\n    /// #    Context, JsValue, JsResult, js_string\n    /// # };\n    /// # fn main() -> JsResult<()> {\n    /// # let context = &mut Context::default();\n    /// let js_map = JsMap::new(context);\n    /// js_map.set(js_string!(\"foo\"), js_string!(\"bar\"), context)?;\n    /// js_map.set(js_string!(\"hello\"), js_string!(\"world\"), context)?;\n    ///\n    /// js_map.clear(context)?;\n    ///\n    /// assert_eq!(js_map.get_size(context)?, 0.into());\n    /// # Ok(())\n    /// # }\n    /// ```\n    #[inline]\n    pub fn clear(&self, context: &mut Context) -> JsResult<JsValue> {\n        Map::clear(&self.inner.clone().into(), &[], context)\n    }\n\n    /// Checks if [`JsMap`] has an entry with the provided `key` value.\n    ///\n    /// # Example\n    ///\n    /// ```\n    /// # use boa_engine::{\n    /// #    object::builtins::JsMap,\n    /// #    Context, JsValue, JsResult, js_string\n    /// # };\n    /// # fn main() -> JsResult<()> {\n    /// # let context = &mut Context::default();\n    /// let js_map = JsMap::new(context);\n    /// js_map.set(js_string!(\"foo\"), js_string!(\"bar\"), context)?;\n    ///\n    /// let has_key = js_map.has(js_string!(\"foo\"), context)?;\n    ///\n    /// assert_eq!(has_key, true.into());\n    /// # Ok(())\n    /// # }\n    /// ```\n    pub fn has<T>(&self, key: T, context: &mut Context) -> JsResult<JsValue>\n    where\n        T: Into<JsValue>,\n    {\n        Map::has(&self.inner.clone().into(), &[key.into()], context)\n    }\n\n    /// Executes the provided callback function for each key-value pair within the [`JsMap`].\n    #[inline]\n    pub fn for_each(\n        &self,\n        callback: JsFunction,\n        this_arg: JsValue,\n        context: &mut Context,\n    ) -> JsResult<JsValue> {\n        Map::for_each(\n            &self.inner.clone().into(),\n            &[callback.into(), this_arg],\n            context,\n        )\n    }\n\n    /// Executes the provided callback function for each key-value pair within the [`JsMap`].\n    #[inline]\n    pub fn for_each_native<F>(&self, f: F) -> JsResult<()>\n    where\n        F: FnMut(JsValue, JsValue) -> JsResult<()>,\n    {\n        let this = self.inner.clone().into();\n        Map::for_each_native(&this, f)\n    }\n\n    /// Returns a new [`JsMapIterator`] object that yields the `value` for each element within the [`JsMap`] in insertion order.\n    #[inline]\n    pub fn values(&self, context: &mut Context) -> JsResult<JsMapIterator> {\n        let iterator_record = Map::values(&self.inner.clone().into(), &[], context)?\n            .get_iterator(IteratorHint::Sync, context)?;\n        let map_iterator_object = iterator_record.iterator();\n        JsMapIterator::from_object(map_iterator_object.clone())\n    }\n}\n\nimpl From<JsMap> for JsObject {\n    #[inline]\n    fn from(o: JsMap) -> Self {\n        o.inner.clone()\n    }\n}\n\nimpl From<JsMap> for JsValue {\n    #[inline]\n    fn from(o: JsMap) -> Self {\n        o.inner.clone().into()\n    }\n}\n\nimpl Deref for JsMap {\n    type Target = JsObject;\n\n    #[inline]\n    fn deref(&self) -> &Self::Target {\n        &self.inner\n    }\n}\n\nimpl TryFromJs for JsMap {\n    fn try_from_js(value: &JsValue, _context: &mut Context) -> JsResult<Self> {\n        if let Some(o) = value.as_object() {\n            Self::from_object(o.clone())\n        } else {\n            Err(JsNativeError::typ()\n                .with_message(\"value is not a Map object\")\n                .into())\n        }\n    }\n}\n"
  },
  {
    "path": "core/engine/src/object/builtins/jsmap_iterator.rs",
    "content": "//! A Rust API wrapper for Boa's `MapIterator` Builtin ECMAScript Object\nuse crate::{\n    Context, JsResult, JsValue, builtins::map::MapIterator, error::JsNativeError, object::JsObject,\n    value::TryFromJs,\n};\n\nuse boa_gc::{Finalize, Trace};\nuse std::ops::Deref;\n\n/// `JsMapIterator` provides a wrapper for Boa's implementation of the ECMAScript `MapIterator` object.\n#[derive(Debug, Clone, Finalize, Trace)]\npub struct JsMapIterator {\n    inner: JsObject,\n}\n\nimpl JsMapIterator {\n    /// Create a [`JsMapIterator`] from a [`JsObject`]. If object is not a `MapIterator`, throw `TypeError`\n    #[inline]\n    pub fn from_object(object: JsObject) -> JsResult<Self> {\n        if object.is::<MapIterator>() {\n            Ok(Self { inner: object })\n        } else {\n            Err(JsNativeError::typ()\n                .with_message(\"object is not a MapIterator\")\n                .into())\n        }\n    }\n\n    /// Advances the `JsMapIterator` and gets the next result in the `JsMap`\n    pub fn next(&self, context: &mut Context) -> JsResult<JsValue> {\n        MapIterator::next(&self.inner.clone().into(), &[], context)\n    }\n}\n\nimpl From<JsMapIterator> for JsObject {\n    #[inline]\n    fn from(o: JsMapIterator) -> Self {\n        o.inner.clone()\n    }\n}\n\nimpl From<JsMapIterator> for JsValue {\n    #[inline]\n    fn from(o: JsMapIterator) -> Self {\n        o.inner.clone().into()\n    }\n}\n\nimpl Deref for JsMapIterator {\n    type Target = JsObject;\n\n    #[inline]\n    fn deref(&self) -> &Self::Target {\n        &self.inner\n    }\n}\n\nimpl TryFromJs for JsMapIterator {\n    fn try_from_js(value: &JsValue, _context: &mut Context) -> JsResult<Self> {\n        if let Some(o) = value.as_object() {\n            Self::from_object(o.clone())\n        } else {\n            Err(JsNativeError::typ()\n                .with_message(\"value is not a MapIterator object\")\n                .into())\n        }\n    }\n}\n"
  },
  {
    "path": "core/engine/src/object/builtins/jspromise.rs",
    "content": "//! A Rust API wrapper for Boa's promise Builtin ECMAScript Object\n\nuse super::{JsArray, JsFunction};\nuse crate::value::TryIntoJs;\nuse crate::{\n    Context, JsArgs, JsError, JsExpect, JsNativeError, JsResult, JsValue, NativeFunction,\n    builtins::{\n        Promise,\n        promise::{PromiseState, ResolvingFunctions},\n    },\n    job::NativeAsyncJob,\n    object::JsObject,\n    value::TryFromJs,\n};\nuse boa_gc::{Finalize, Gc, GcRefCell, Trace};\nuse std::cell::RefCell;\nuse std::{future::Future, pin::Pin, task};\n\n/// An ECMAScript [promise] object.\n///\n/// Known as the concurrency primitive of ECMAScript, this is the main struct used to manipulate,\n/// chain and inspect `Promises` from Rust code.\n///\n/// # Examples\n///\n/// ```\n/// # use boa_engine::{\n/// #     builtins::promise::PromiseState,\n/// #     js_string,\n/// #     object::{builtins::JsPromise, FunctionObjectBuilder},\n/// #     property::Attribute,\n/// #     Context, JsArgs, JsError, JsValue, NativeFunction,\n/// # };\n/// # use std::error::Error;\n/// # fn main() -> Result<(), Box<dyn Error>> {\n/// let context = &mut Context::default();\n///\n/// context.register_global_property(\n///     js_string!(\"finally\"),\n///     false,\n///     Attribute::all(),\n/// );\n///\n/// let promise = JsPromise::new(\n///     |resolvers, context| {\n///         let result = js_string!(\"hello world!\").into();\n///         resolvers.resolve.call(\n///             &JsValue::undefined(),\n///             &[result],\n///             context,\n///         )?;\n///         Ok(JsValue::undefined())\n///     },\n///     context,\n/// )?;\n///\n/// let promise = promise\n///     .then(\n///         Some(\n///             NativeFunction::from_fn_ptr(|_, args, _| {\n///                 Err(JsError::from_opaque(args.get_or_undefined(0).clone())\n///                     .into())\n///             })\n///             .to_js_function(context.realm()),\n///         ),\n///         None,\n///         context,\n///     )?\n///     .catch(\n///         NativeFunction::from_fn_ptr(|_, args, _| {\n///             Ok(args.get_or_undefined(0).clone())\n///         })\n///         .to_js_function(context.realm()),\n///         context,\n///     )?\n///     .finally(\n///         NativeFunction::from_fn_ptr(|_, _, context| {\n///             context.global_object().clone().set(\n///                 js_string!(\"finally\"),\n///                 JsValue::from(true),\n///                 true,\n///                 context,\n///             )?;\n///             Ok(JsValue::undefined())\n///         })\n///         .to_js_function(context.realm()),\n///         context,\n///     )?;\n///\n/// context.run_jobs();\n///\n/// assert_eq!(\n///     promise.state(),\n///     PromiseState::Fulfilled(js_string!(\"hello world!\").into())\n/// );\n///\n/// assert_eq!(\n///     context\n///         .global_object()\n///         .clone()\n///         .get(js_string!(\"finally\"), context)?,\n///     JsValue::from(true)\n/// );\n///\n/// # Ok(())\n/// # }\n/// ```\n///\n/// [promise]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise\n#[derive(Debug, Clone, Trace, Finalize)]\npub struct JsPromise {\n    inner: JsObject<Promise>,\n}\n\nimpl JsPromise {\n    /// Creates a new promise object from an executor function.\n    ///\n    /// It is equivalent to calling the [`Promise()`] constructor, which makes it share the same\n    /// execution semantics as the constructor:\n    /// - The executor function `executor` is called synchronously just after the promise is created.\n    /// - The executor return value is ignored.\n    /// - Any error thrown within the execution of `executor` will call the `reject` function\n    ///   of the newly created promise, unless either `resolve` or `reject` were already called\n    ///   beforehand.\n    ///\n    /// `executor` receives as an argument the [`ResolvingFunctions`] needed to settle the promise,\n    /// which can be done by either calling the `resolve` function or the `reject` function.\n    ///\n    /// # Examples\n    ///\n    /// ```\n    /// # use std::error::Error;\n    /// # use boa_engine::{\n    /// #    object::builtins::JsPromise,\n    /// #    builtins::promise::PromiseState,\n    /// #    Context, JsValue, js_string\n    /// # };\n    /// # fn main() -> Result<(), Box<dyn Error>> {\n    /// let context = &mut Context::default();\n    ///\n    /// let promise = JsPromise::new(\n    ///     |resolvers, context| {\n    ///         let result = js_string!(\"hello world\").into();\n    ///         resolvers.resolve.call(\n    ///             &JsValue::undefined(),\n    ///             &[result],\n    ///             context,\n    ///         )?;\n    ///         Ok(JsValue::undefined())\n    ///     },\n    ///     context,\n    /// )?;\n    ///\n    /// context.run_jobs();\n    ///\n    /// assert_eq!(\n    ///     promise.state(),\n    ///     PromiseState::Fulfilled(js_string!(\"hello world\").into())\n    /// );\n    /// # Ok(())\n    /// # }\n    /// ```\n    ///\n    /// [`Promise()`]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/Promise\n    pub fn new<F>(executor: F, context: &mut Context) -> JsResult<Self>\n    where\n        F: FnOnce(&ResolvingFunctions, &mut Context) -> JsResult<JsValue>,\n    {\n        let promise = JsObject::from_proto_and_data_with_shared_shape(\n            context.root_shape(),\n            context.intrinsics().constructors().promise().prototype(),\n            Promise::new(),\n        );\n        let resolvers = Promise::create_resolving_functions(&promise, context);\n\n        if let Err(e) = executor(&resolvers, context) {\n            let e = e.into_opaque(context)?;\n            resolvers\n                .reject\n                .call(&JsValue::undefined(), &[e], context)\n                .js_expect(\"default `reject` function cannot throw\")?;\n        }\n\n        Ok(Self { inner: promise })\n    }\n\n    /// Creates a new pending promise and returns it and its associated `ResolvingFunctions`.\n    ///\n    /// This can be useful when you want to manually settle a promise from Rust code, instead of\n    /// running an `executor` function that automatically settles the promise on creation\n    /// (see [`JsPromise::new`]).\n    ///\n    /// # Examples\n    ///\n    /// ```\n    /// # use std::error::Error;\n    /// # use boa_engine::{\n    /// #    object::builtins::JsPromise,\n    /// #    builtins::promise::PromiseState,\n    /// #    Context, JsValue\n    /// # };\n    /// # fn main() -> Result<(), Box<dyn Error>> {\n    /// let context = &mut Context::default();\n    ///\n    /// let (promise, resolvers) = JsPromise::new_pending(context);\n    ///\n    /// assert_eq!(promise.state(), PromiseState::Pending);\n    ///\n    /// resolvers\n    ///     .reject\n    ///     .call(&JsValue::undefined(), &[5.into()], context)?;\n    ///\n    /// assert_eq!(promise.state(), PromiseState::Rejected(5.into()));\n    ///\n    /// # Ok(())\n    /// # }\n    /// ```\n    #[inline]\n    pub fn new_pending(context: &mut Context) -> (Self, ResolvingFunctions) {\n        let promise = JsObject::from_proto_and_data_with_shared_shape(\n            context.root_shape(),\n            context.intrinsics().constructors().promise().prototype(),\n            Promise::new(),\n        );\n        let resolvers = Promise::create_resolving_functions(&promise, context);\n\n        (promise.into(), resolvers)\n    }\n\n    /// Wraps an existing object with the `JsPromise` interface, returning `Err` if the object\n    /// is not a valid promise.\n    ///\n    /// # Examples\n    ///\n    /// ```\n    /// # use std::error::Error;\n    /// # use boa_engine::{\n    /// #    object::builtins::JsPromise,\n    /// #    builtins::promise::PromiseState,\n    /// #    Context, JsObject, JsValue, Source\n    /// # };\n    /// # fn main() -> Result<(), Box<dyn Error>> {\n    /// let context = &mut Context::default();\n    ///\n    /// let promise = context.eval(Source::from_bytes(\n    ///     \"new Promise((resolve, reject) => resolve())\",\n    /// ))?;\n    /// let promise = promise.as_object().unwrap();\n    ///\n    /// let promise = JsPromise::from_object(promise)?;\n    ///\n    /// assert_eq!(\n    ///     promise.state(),\n    ///     PromiseState::Fulfilled(JsValue::undefined())\n    /// );\n    ///\n    /// assert!(JsPromise::from_object(JsObject::with_null_proto()).is_err());\n    ///\n    /// # Ok(())\n    /// # }\n    /// ```\n    #[inline]\n    pub fn from_object(object: JsObject) -> JsResult<Self> {\n        let Ok(inner) = object.downcast::<Promise>() else {\n            return Err(JsNativeError::typ()\n                .with_message(\"`object` is not a Promise\")\n                .into());\n        };\n        Ok(Self { inner })\n    }\n\n    /// Creates a new `JsPromise` from a [`Future`]-like.\n    ///\n    /// If you want to convert a Rust async function into an ECMAScript async function, see\n    /// [`NativeFunction::from_async_fn`][async_fn].\n    ///\n    /// # Examples\n    ///\n    /// ```\n    /// use std::error::Error;\n    /// # use std::cell::RefCell;\n    /// # use boa_engine::{\n    /// #    object::builtins::JsPromise,\n    /// #    builtins::promise::PromiseState,\n    /// #    Context, JsResult, JsValue\n    /// # };\n    /// async fn f(_: &RefCell<&mut Context>) -> JsResult<JsValue> {\n    ///     Ok(JsValue::null())\n    /// }\n    /// let context = &mut Context::default();\n    ///\n    /// let promise = JsPromise::from_async_fn(f, context);\n    ///\n    /// context.run_jobs();\n    ///\n    /// assert_eq!(promise.state(), PromiseState::Fulfilled(JsValue::null()));\n    /// ```\n    ///\n    /// [async_fn]: crate::native_function::NativeFunction::from_async_fn\n    pub fn from_async_fn<F>(f: F, context: &mut Context) -> Self\n    where\n        F: AsyncFnOnce(&RefCell<&mut Context>) -> JsResult<JsValue> + 'static,\n    {\n        let (promise, resolvers) = Self::new_pending(context);\n\n        context.enqueue_job(\n            NativeAsyncJob::new(async move |context| {\n                let result = f(context).await;\n\n                let context = &mut context.borrow_mut();\n                match result {\n                    Ok(v) => resolvers.resolve.call(&JsValue::undefined(), &[v], context),\n                    Err(e) => {\n                        let e = e.into_opaque(context)?;\n                        resolvers.reject.call(&JsValue::undefined(), &[e], context)\n                    }\n                }\n            })\n            .into(),\n        );\n\n        promise\n    }\n\n    /// Creates a new `JsPromise` from a `Result<T, JsError>`, where `T` is the fulfilled value of\n    /// the promise, and `JsError` is the rejection reason. This is a simpler way to create a\n    /// promise that is either fulfilled or rejected based on the result of a computation.\n    ///\n    /// # Examples\n    ///\n    /// ```\n    /// # use std::error::Error;\n    /// # use boa_engine::{\n    /// #    object::builtins::JsPromise,\n    /// #    builtins::promise::PromiseState,\n    /// #    Context, JsResult, JsString, js_string, js_error\n    /// # };\n    /// # fn main() -> Result<(), Box<dyn Error>> {\n    /// let context = &mut Context::default();\n    ///\n    /// fn do_thing(success: bool) -> JsResult<JsString> {\n    ///     success\n    ///         .then(|| js_string!(\"resolved!\"))\n    ///         .ok_or(js_error!(\"rejected!\"))\n    /// }\n    ///\n    /// let promise = JsPromise::from_result(do_thing(true), context)?;\n    /// assert_eq!(\n    ///     promise.state(),\n    ///     PromiseState::Fulfilled(js_string!(\"resolved!\").into())\n    /// );\n    ///\n    /// let promise = JsPromise::from_result(do_thing(false), context)?;\n    /// assert_eq!(\n    ///     promise.state(),\n    ///     PromiseState::Rejected(js_string!(\"rejected!\").into())\n    /// );\n    /// # Ok(())\n    /// # }\n    /// ```\n    pub fn from_result<V: Into<JsValue>, E: Into<JsError>>(\n        value: Result<V, E>,\n        context: &mut Context,\n    ) -> JsResult<Self> {\n        match value {\n            Ok(v) => Self::resolve(v, context),\n            Err(e) => Self::reject(e, context),\n        }\n    }\n\n    /// Resolves a `JsValue` into a `JsPromise`.\n    ///\n    /// Equivalent to the [`Promise.resolve()`] static method.\n    ///\n    /// This function is mainly used to wrap a plain `JsValue` into a fulfilled promise, but it can\n    /// also flatten nested layers of [thenables], which essentially converts them into native\n    /// promises.\n    ///\n    /// # Examples\n    ///\n    /// ```\n    /// # use std::error::Error;\n    /// # use boa_engine::{\n    /// #    object::builtins::JsPromise,\n    /// #    builtins::promise::PromiseState,\n    /// #    Context, js_string\n    /// # };\n    /// # fn main() -> Result<(), Box<dyn Error>> {\n    /// let context = &mut Context::default();\n    ///\n    /// let promise = JsPromise::resolve(js_string!(\"resolved!\"), context)?;\n    ///\n    /// assert_eq!(\n    ///     promise.state(),\n    ///     PromiseState::Fulfilled(js_string!(\"resolved!\").into())\n    /// );\n    /// # Ok(())\n    /// # }\n    /// ```\n    ///\n    /// [`Promise.resolve()`]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/resolve\n    /// [thenables]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise#thenables\n    pub fn resolve<V: Into<JsValue>>(value: V, context: &mut Context) -> JsResult<Self> {\n        Promise::promise_resolve(\n            &context.intrinsics().constructors().promise().constructor(),\n            value.into(),\n            context,\n        )\n        .and_then(Self::from_object)\n        .js_expect(\"default resolve functions cannot throw and must return a promise\")\n        .map_err(Into::into)\n    }\n\n    /// Creates a `JsPromise` that is rejected with the reason `error`.\n    ///\n    /// Equivalent to the [`Promise.reject`] static method.\n    ///\n    /// `JsPromise::reject` is pretty similar to [`JsPromise::resolve`], with the difference that\n    /// it always wraps `error` into a rejected promise, even if `error` is a promise or a [thenable].\n    ///\n    /// # Examples\n    ///\n    /// ```\n    /// # use std::error::Error;\n    /// # use boa_engine::{\n    /// #    object::builtins::JsPromise,\n    /// #    builtins::promise::PromiseState,\n    /// #    Context, js_string, JsError\n    /// # };\n    /// # fn main() -> Result<(), Box<dyn Error>> {\n    /// let context = &mut Context::default();\n    ///\n    /// let promise = JsPromise::reject(\n    ///     JsError::from_opaque(js_string!(\"oops!\").into()),\n    ///     context,\n    /// )?;\n    ///\n    /// assert_eq!(\n    ///     promise.state(),\n    ///     PromiseState::Rejected(js_string!(\"oops!\").into())\n    /// );\n    /// # Ok(())\n    /// # }\n    /// ```\n    ///\n    /// [`Promise.reject`]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/reject\n    /// [thenable]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise#thenables\n    pub fn reject<E: Into<JsError>>(error: E, context: &mut Context) -> JsResult<Self> {\n        let error = error.into();\n        assert!(\n            error.is_catchable(),\n            \"cannot create a reject function from an uncatchable error\"\n        );\n\n        Promise::promise_reject(\n            &context.intrinsics().constructors().promise().constructor(),\n            error,\n            context,\n        )\n        .and_then(Self::from_object)\n        .js_expect(\"default resolve functions cannot throw and must return a promise\")\n        .map_err(Into::into)\n    }\n\n    /// Gets the current state of the promise.\n    ///\n    /// # Examples\n    ///\n    /// ```\n    /// # use std::error::Error;\n    /// # use boa_engine::{\n    /// #    object::builtins::JsPromise,\n    /// #    builtins::promise::PromiseState,\n    /// #    Context\n    /// # };\n    /// let context = &mut Context::default();\n    ///\n    /// let promise = JsPromise::new_pending(context).0;\n    ///\n    /// assert_eq!(promise.state(), PromiseState::Pending);\n    /// ```\n    #[inline]\n    #[must_use]\n    pub fn state(&self) -> PromiseState {\n        self.inner.borrow().data().state().clone()\n    }\n\n    /// Schedules callback functions to run when the promise settles.\n    ///\n    /// Equivalent to the [`Promise.prototype.then`] method.\n    ///\n    /// The return value is a promise that is always pending on return, regardless of the current\n    /// state of the original promise. Two handlers can be provided as callbacks to be executed when\n    /// the original promise settles:\n    ///\n    /// - If the original promise is fulfilled, `on_fulfilled` is called with the fulfillment value\n    ///   of the original promise.\n    /// - If the original promise is rejected, `on_rejected` is called with the rejection reason\n    ///   of the original promise.\n    ///\n    /// The return value of the handlers can be used to mutate the state of the created promise. If\n    /// the callback:\n    ///\n    /// - returns a value: the created promise gets fulfilled with the returned value.\n    /// - doesn't return: the created promise gets fulfilled with undefined.\n    /// - throws: the created promise gets rejected with the thrown error as its value.\n    /// - returns a fulfilled promise: the created promise gets fulfilled with that promise's value as its value.\n    /// - returns a rejected promise: the created promise gets rejected with that promise's value as its value.\n    /// - returns another pending promise: the created promise remains pending but becomes settled with that\n    ///   promise's value as its value immediately after that promise becomes settled.\n    ///\n    /// # Examples\n    ///\n    /// ```\n    /// # use std::error::Error;\n    /// # use boa_engine::{\n    /// #     builtins::promise::PromiseState,\n    /// #     js_string,\n    /// #     object::{builtins::JsPromise, FunctionObjectBuilder},\n    /// #     Context, JsArgs, JsError, JsValue, NativeFunction,\n    /// # };\n    /// # fn main() -> Result<(), Box<dyn Error>> {\n    /// let context = &mut Context::default();\n    ///\n    /// let promise = JsPromise::new(\n    ///     |resolvers, context| {\n    ///         resolvers.resolve.call(\n    ///             &JsValue::undefined(),\n    ///             &[255.255.into()],\n    ///             context,\n    ///         )?;\n    ///         Ok(JsValue::undefined())\n    ///     },\n    ///     context,\n    /// )?\n    /// .then(\n    ///     Some(\n    ///         NativeFunction::from_fn_ptr(|_, args, context| {\n    ///             args.get_or_undefined(0)\n    ///                 .to_string(context)\n    ///                 .map(JsValue::from)\n    ///         })\n    ///         .to_js_function(context.realm()),\n    ///     ),\n    ///     None,\n    ///     context,\n    /// )?;\n    ///\n    /// context.run_jobs();\n    ///\n    /// assert_eq!(\n    ///     promise.state(),\n    ///     PromiseState::Fulfilled(js_string!(\"255.255\").into())\n    /// );\n    /// # Ok(())\n    /// # }\n    /// ```\n    ///\n    /// [`Promise.prototype.then`]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/then\n    #[inline]\n    #[allow(clippy::return_self_not_must_use)] // Could just be used to add handlers on an existing promise\n    pub fn then(\n        &self,\n        on_fulfilled: Option<JsFunction>,\n        on_rejected: Option<JsFunction>,\n        context: &mut Context,\n    ) -> JsResult<Self> {\n        Promise::inner_then(self, on_fulfilled, on_rejected, context)\n            .and_then(Self::from_object)\n            .js_expect(\"`inner_then` cannot fail for native `JsPromise`\")\n            .map_err(Into::into)\n    }\n\n    /// Schedules a callback to run when the promise is rejected.\n    ///\n    /// Equivalent to the [`Promise.prototype.catch`] method.\n    ///\n    /// This is essentially a shortcut for calling [`promise.then(None, Some(function))`][then], which\n    /// only handles the error case and leaves the fulfilled case untouched.\n    ///\n    /// # Examples\n    ///\n    /// ```\n    /// # use std::error::Error;\n    /// # use boa_engine::{\n    /// #     js_string,\n    /// #     builtins::promise::PromiseState,\n    /// #     object::{builtins::JsPromise, FunctionObjectBuilder},\n    /// #     Context, JsArgs, JsNativeError, JsValue, NativeFunction,\n    /// # };\n    /// # fn main() -> Result<(), Box<dyn Error>> {\n    /// let context = &mut Context::default();\n    ///\n    /// let promise = JsPromise::new(\n    ///     |resolvers, context| {\n    ///         let error = JsNativeError::typ().with_message(\"thrown\");\n    ///         let error = error.into_opaque(context);\n    ///         resolvers.reject.call(\n    ///             &JsValue::undefined(),\n    ///             &[error.into()],\n    ///             context,\n    ///         )?;\n    ///         Ok(JsValue::undefined())\n    ///     },\n    ///     context,\n    /// )?\n    /// .catch(\n    ///     NativeFunction::from_fn_ptr(|_, args, context| {\n    ///         args.get_or_undefined(0)\n    ///             .to_string(context)\n    ///             .map(JsValue::from)\n    ///     })\n    ///     .to_js_function(context.realm()),\n    ///     context,\n    /// )?;\n    ///\n    /// context.run_jobs();\n    ///\n    /// assert_eq!(\n    ///     promise.state(),\n    ///     PromiseState::Fulfilled(js_string!(\"TypeError: thrown\").into())\n    /// );\n    /// # Ok(())\n    /// # }\n    /// ```\n    ///\n    /// [`Promise.prototype.catch`]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/catch\n    /// [then]: JsPromise::then\n    #[inline]\n    #[allow(clippy::return_self_not_must_use)] // Could just be used to add a handler on an existing promise\n    pub fn catch(&self, on_rejected: JsFunction, context: &mut Context) -> JsResult<Self> {\n        self.then(None, Some(on_rejected), context)\n    }\n\n    /// Schedules a callback to run when the promise is rejected.\n    ///\n    /// Equivalent to the [`Promise.prototype.finally()`] method.\n    ///\n    /// While this could be seen as a shortcut for calling [`promise.then(Some(function), Some(function))`][then],\n    /// it has slightly different semantics than `then`:\n    /// - `on_finally` doesn't receive any argument, unlike `on_fulfilled` and `on_rejected`.\n    /// - `finally()` is transparent; a call like `Promise.resolve(\"first\").finally(() => \"second\")`\n    ///   returns a promise fulfilled with the value `\"first\"`, which would return `\"second\"` if `finally`\n    ///   was a shortcut of `then`.\n    ///\n    /// # Examples\n    ///\n    /// ```\n    /// # use std::error::Error;\n    /// # use boa_engine::{\n    /// #     object::{builtins::JsPromise, FunctionObjectBuilder},\n    /// #     property::Attribute,\n    /// #     Context, JsNativeError, JsValue, NativeFunction, js_string\n    /// # };\n    /// # fn main() -> Result<(), Box<dyn Error>> {\n    /// let context = &mut Context::default();\n    ///\n    /// context.register_global_property(\n    ///     js_string!(\"finally\"),\n    ///     false,\n    ///     Attribute::all(),\n    /// )?;\n    ///\n    /// let promise = JsPromise::new(\n    ///     |resolvers, context| {\n    ///         let error = JsNativeError::typ().with_message(\"thrown\");\n    ///         let error = error.into_opaque(context);\n    ///         resolvers.reject.call(\n    ///             &JsValue::undefined(),\n    ///             &[error.into()],\n    ///             context,\n    ///         )?;\n    ///         Ok(JsValue::undefined())\n    ///     },\n    ///     context,\n    /// )?\n    /// .finally(\n    ///     NativeFunction::from_fn_ptr(|_, _, context| {\n    ///         context.global_object().clone().set(\n    ///             js_string!(\"finally\"),\n    ///             JsValue::from(true),\n    ///             true,\n    ///             context,\n    ///         )?;\n    ///         Ok(JsValue::undefined())\n    ///     })\n    ///     .to_js_function(context.realm()),\n    ///     context,\n    /// )?;\n    ///\n    /// context.run_jobs();\n    ///\n    /// assert_eq!(\n    ///     context\n    ///         .global_object()\n    ///         .clone()\n    ///         .get(js_string!(\"finally\"), context)?,\n    ///     JsValue::from(true)\n    /// );\n    ///\n    /// # Ok(())\n    /// # }\n    /// ```\n    ///\n    /// [`Promise.prototype.finally()`]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/finally\n    /// [then]: JsPromise::then\n    #[inline]\n    #[allow(clippy::return_self_not_must_use)] // Could just be used to add a handler on an existing promise\n    pub fn finally(&self, on_finally: JsFunction, context: &mut Context) -> JsResult<Self> {\n        let (then, catch) = Promise::then_catch_finally_closures(\n            context.intrinsics().constructors().promise().constructor(),\n            on_finally,\n            context,\n        );\n        Promise::inner_then(self, Some(then), Some(catch), context)\n            .and_then(Self::from_object)\n            .js_expect(\"`inner_then` cannot fail for native `JsPromise`\")\n            .map_err(Into::into)\n    }\n\n    /// Waits for a list of promises to settle with fulfilled values, rejecting the aggregate promise\n    /// when any of the inner promises is rejected.\n    ///\n    /// Equivalent to the [`Promise.all`] static method.\n    ///\n    /// # Examples\n    ///\n    /// ```\n    /// # use std::error::Error;\n    /// # use boa_engine::{\n    /// #     js_string,\n    /// #     object::builtins::{JsArray, JsPromise},\n    /// #     Context, JsNativeError, JsValue,\n    /// # };\n    /// # fn main() -> Result<(), Box<dyn Error>> {\n    /// let context = &mut Context::default();\n    ///\n    /// let promise1 = JsPromise::all(\n    ///     [\n    ///         JsPromise::resolve(0, context)?,\n    ///         JsPromise::resolve(2, context)?,\n    ///         JsPromise::resolve(4, context)?,\n    ///     ],\n    ///     context,\n    /// )?;\n    ///\n    /// let promise2 = JsPromise::all(\n    ///     [\n    ///         JsPromise::resolve(1, context)?,\n    ///         JsPromise::reject(JsNativeError::typ(), context)?,\n    ///         JsPromise::resolve(3, context)?,\n    ///     ],\n    ///     context,\n    /// )?;\n    ///\n    /// context.run_jobs();\n    ///\n    /// let array = promise1\n    ///     .state()\n    ///     .as_fulfilled()\n    ///     .and_then(JsValue::as_object)\n    ///     .unwrap()\n    ///     .clone();\n    /// let array = JsArray::from_object(array)?;\n    /// assert_eq!(array.at(0, context)?, 0.into());\n    /// assert_eq!(array.at(1, context)?, 2.into());\n    /// assert_eq!(array.at(2, context)?, 4.into());\n    ///\n    /// let error = promise2.state().as_rejected().unwrap().clone();\n    /// assert_eq!(error.to_string(context)?, js_string!(\"TypeError\"));\n    ///\n    /// # Ok(())\n    /// # }\n    /// ```\n    ///\n    /// [`Promise.all`]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/all\n    pub fn all<I>(promises: I, context: &mut Context) -> JsResult<Self>\n    where\n        I: IntoIterator<Item = Self>,\n    {\n        let promises = JsArray::from_iter(promises.into_iter().map(JsValue::from), context);\n\n        let c = &context\n            .intrinsics()\n            .constructors()\n            .promise()\n            .constructor()\n            .into();\n\n        let value = Promise::all(c, &[promises.into()], context)\n            .js_expect(\"Promise.all cannot fail with the default `%Promise%` constructor\")?;\n\n        let object = value\n            .as_object()\n            .js_expect(\"`Promise.all` always returns an object on success\")?;\n\n        Self::from_object(object.clone())\n            .js_expect(\"`Promise::all` with the default `%Promise%` constructor always returns a native `JsPromise`\").map_err(Into::into)\n    }\n\n    /// Waits for a list of promises to settle, fulfilling with an array of the outcomes of every\n    /// promise.\n    ///\n    /// Equivalent to the [`Promise.allSettled`] static method.\n    ///\n    /// # Examples\n    ///\n    /// ```\n    /// # use std::error::Error;\n    /// # use boa_engine::{\n    /// #     js_string,\n    /// #     object::builtins::{JsArray, JsPromise},\n    /// #     Context, JsNativeError, JsValue,\n    /// # };\n    /// # fn main() -> Result<(), Box<dyn Error>> {\n    /// let context = &mut Context::default();\n    ///\n    /// let promise = JsPromise::all_settled(\n    ///     [\n    ///         JsPromise::resolve(1, context)?,\n    ///         JsPromise::reject(JsNativeError::typ(), context)?,\n    ///         JsPromise::resolve(3, context)?,\n    ///     ],\n    ///     context,\n    /// )?;\n    ///\n    /// context.run_jobs();\n    ///\n    /// let array = promise\n    ///     .state()\n    ///     .as_fulfilled()\n    ///     .and_then(JsValue::as_object)\n    ///     .unwrap()\n    ///     .clone();\n    /// let array = JsArray::from_object(array)?;\n    ///\n    /// let a = array.at(0, context)?.as_object().unwrap().clone();\n    /// assert_eq!(\n    ///     a.get(js_string!(\"status\"), context)?,\n    ///     js_string!(\"fulfilled\").into()\n    /// );\n    /// assert_eq!(a.get(js_string!(\"value\"), context)?, 1.into());\n    ///\n    /// let b = array.at(1, context)?.as_object().unwrap().clone();\n    /// assert_eq!(\n    ///     b.get(js_string!(\"status\"), context)?,\n    ///     js_string!(\"rejected\").into()\n    /// );\n    /// assert_eq!(\n    ///     b.get(js_string!(\"reason\"), context)?.to_string(context)?,\n    ///     js_string!(\"TypeError\")\n    /// );\n    ///\n    /// let c = array.at(2, context)?.as_object().unwrap().clone();\n    /// assert_eq!(\n    ///     c.get(js_string!(\"status\"), context)?,\n    ///     js_string!(\"fulfilled\").into()\n    /// );\n    /// assert_eq!(c.get(js_string!(\"value\"), context)?, 3.into());\n    ///\n    /// # Ok(())\n    /// # }\n    /// ```\n    ///\n    /// [`Promise.allSettled`]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/allSettled\n    pub fn all_settled<I>(promises: I, context: &mut Context) -> JsResult<Self>\n    where\n        I: IntoIterator<Item = Self>,\n    {\n        let promises = JsArray::from_iter(promises.into_iter().map(JsValue::from), context);\n\n        let c = &context\n            .intrinsics()\n            .constructors()\n            .promise()\n            .constructor()\n            .into();\n\n        let value = Promise::all_settled(c, &[promises.into()], context).js_expect(\n            \"`Promise.all_settled` cannot fail with the default `%Promise%` constructor\",\n        )?;\n\n        let object = value\n            .as_object()\n            .js_expect(\"`Promise.all_settled` always returns an object on success\")?;\n\n        Self::from_object(object.clone())\n            .js_expect(\"`Promise::all_settled` with the default `%Promise%` constructor always returns a native `JsPromise`\").map_err(Into::into)\n    }\n\n    /// Returns the first promise that fulfills from a list of promises.\n    ///\n    /// Equivalent to the [`Promise.any`] static method.\n    ///\n    /// If after settling all promises in `promises` there isn't a fulfilled promise, the returned\n    /// promise will be rejected with an `AggregatorError` containing the rejection values of every\n    /// promise; this includes the case where `promises` is an empty iterator.\n    ///\n    /// # Examples\n    ///\n    /// ```\n    /// # use std::error::Error;\n    /// # use boa_engine::{\n    /// #     builtins::promise::PromiseState,\n    /// #     js_string,\n    /// #     object::builtins::JsPromise,\n    /// #     Context, JsNativeError,\n    /// # };\n    /// # fn main() -> Result<(), Box<dyn Error>> {\n    /// let context = &mut Context::default();\n    ///\n    /// let promise = JsPromise::any(\n    ///     [\n    ///         JsPromise::reject(JsNativeError::syntax(), context)?,\n    ///         JsPromise::reject(JsNativeError::typ(), context)?,\n    ///         JsPromise::resolve(js_string!(\"fulfilled\"), context)?,\n    ///         JsPromise::reject(JsNativeError::range(), context)?,\n    ///     ],\n    ///     context,\n    /// )?;\n    ///\n    /// context.run_jobs();\n    ///\n    /// assert_eq!(\n    ///     promise.state(),\n    ///     PromiseState::Fulfilled(js_string!(\"fulfilled\").into())\n    /// );\n    /// # Ok(())\n    /// # }\n    /// ```\n    ///\n    /// [`Promise.any`]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/any\n    pub fn any<I>(promises: I, context: &mut Context) -> JsResult<Self>\n    where\n        I: IntoIterator<Item = Self>,\n    {\n        let promises = JsArray::from_iter(promises.into_iter().map(JsValue::from), context);\n\n        let c = &context\n            .intrinsics()\n            .constructors()\n            .promise()\n            .constructor()\n            .into();\n\n        let value = Promise::any(c, &[promises.into()], context)\n            .js_expect(\"`Promise.any` cannot fail with the default `%Promise%` constructor\")?;\n\n        let object = value\n            .as_object()\n            .js_expect(\"`Promise.any` always returns an object on success\")?;\n\n        Self::from_object(object.clone())\n            .js_expect(\"`Promise::any` with the default `%Promise%` constructor always returns a native `JsPromise`\").map_err(Into::into)\n    }\n\n    /// Returns the first promise that settles from a list of promises.\n    ///\n    /// Equivalent to the [`Promise.race`] static method.\n    ///\n    /// If the provided iterator is empty, the returned promise will remain on the pending state\n    /// forever.\n    ///\n    /// # Examples\n    ///\n    /// ```\n    /// # use std::error::Error;\n    /// # use boa_engine::{\n    /// #     builtins::promise::PromiseState,\n    /// #     js_string,\n    /// #     object::builtins::JsPromise,\n    /// #     Context, JsValue,\n    /// # };\n    /// # fn main() -> Result<(), Box<dyn Error>> {\n    /// let context = &mut Context::default();\n    ///\n    /// let (a, resolvers_a) = JsPromise::new_pending(context);\n    /// let (b, resolvers_b) = JsPromise::new_pending(context);\n    /// let (c, resolvers_c) = JsPromise::new_pending(context);\n    ///\n    /// let promise = JsPromise::race([a, b, c], context)?;\n    ///\n    /// resolvers_b\n    ///     .reject\n    ///     .call(&JsValue::undefined(), &[], context)?;\n    /// resolvers_a\n    ///     .resolve\n    ///     .call(&JsValue::undefined(), &[5.into()], context)?;\n    /// resolvers_c.reject.call(\n    ///     &JsValue::undefined(),\n    ///     &[js_string!(\"c error\").into()],\n    ///     context,\n    /// )?;\n    ///\n    /// context.run_jobs();\n    ///\n    /// assert_eq!(\n    ///     promise.state(),\n    ///     PromiseState::Rejected(JsValue::undefined())\n    /// );\n    ///\n    /// # Ok(())\n    /// # }\n    /// ```\n    ///\n    /// [`Promise.race`]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/race\n    pub fn race<I>(promises: I, context: &mut Context) -> JsResult<Self>\n    where\n        I: IntoIterator<Item = Self>,\n    {\n        let promises = JsArray::from_iter(promises.into_iter().map(JsValue::from), context);\n\n        let c = &context\n            .intrinsics()\n            .constructors()\n            .promise()\n            .constructor()\n            .into();\n\n        let value = Promise::race(c, &[promises.into()], context)\n            .js_expect(\"`Promise.race` cannot fail with the default `%Promise%` constructor\")?;\n\n        let object = value\n            .as_object()\n            .js_expect(\"`Promise.race` always returns an object on success\")?;\n\n        Self::from_object(object.clone())\n            .js_expect(\"`Promise::race` with the default `%Promise%` constructor always returns a native `JsPromise`\").map_err(Into::into)\n    }\n\n    /// Creates a `JsFuture` from this `JsPromise`.\n    ///\n    /// The returned `JsFuture` implements [`Future`], which means it can be `await`ed within Rust's\n    /// async contexts (async functions and async blocks).\n    ///\n    /// # Examples\n    ///\n    /// ```\n    /// # use std::error::Error;\n    /// # use boa_engine::{\n    /// #     builtins::promise::PromiseState,\n    /// #     object::builtins::JsPromise,\n    /// #     Context, JsValue, JsError\n    /// # };\n    /// # use futures_lite::future;\n    /// # fn main() -> Result<(), Box<dyn Error>> {\n    /// let context = &mut Context::default();\n    ///\n    /// let (promise, resolvers) = JsPromise::new_pending(context);\n    /// let promise_future = promise.into_js_future(context)?;\n    ///\n    /// let future1 = async move { promise_future.await };\n    ///\n    /// let future2 = async move {\n    ///     resolvers\n    ///         .resolve\n    ///         .call(&JsValue::undefined(), &[10.into()], context)?;\n    ///     context.run_jobs();\n    ///     Ok::<(), JsError>(())\n    /// };\n    ///\n    /// let (result1, result2) = future::block_on(future::zip(future1, future2));\n    ///\n    /// assert_eq!(result1, Ok(JsValue::from(10)));\n    /// assert_eq!(result2, Ok(()));\n    ///\n    /// # Ok(())\n    /// # }\n    /// ```\n    pub fn into_js_future(self, context: &mut Context) -> JsResult<JsFuture> {\n        // Mostly based from:\n        // https://docs.rs/wasm-bindgen-futures/0.4.37/src/wasm_bindgen_futures/lib.rs.html#109-168\n\n        fn finish(state: &GcRefCell<Inner>, val: JsResult<JsValue>) {\n            let task = {\n                let mut state = state.borrow_mut();\n\n                // The engine ensures both `resolve` and `reject` are called only once,\n                // and only one of them.\n                debug_assert!(state.result.is_none());\n\n                // Store the received value into the state shared by the resolving functions\n                // and the `JsFuture` itself. This will be accessed when the executor polls\n                // the `JsFuture` again.\n                state.result = Some(val);\n                state.task.take()\n            };\n\n            // `task` could be `None` if the `JsPromise` was already fulfilled before polling\n            // the `JsFuture`.\n            if let Some(task) = task {\n                task.wake();\n            }\n        }\n\n        let state = Gc::new(GcRefCell::new(Inner {\n            result: None,\n            task: None,\n        }));\n\n        let resolve = {\n            let state = state.clone();\n\n            NativeFunction::from_copy_closure_with_captures(\n                move |_, args, state, _| {\n                    finish(state, Ok(args.get_or_undefined(0).clone()));\n                    Ok(JsValue::undefined())\n                },\n                state,\n            )\n        };\n\n        let reject = {\n            let state = state.clone();\n\n            NativeFunction::from_copy_closure_with_captures(\n                move |_, args, state, _| {\n                    let err = JsError::from_opaque(args.get_or_undefined(0).clone());\n                    finish(state, Err(err));\n                    Ok(JsValue::undefined())\n                },\n                state,\n            )\n        };\n\n        drop(self.then(\n            Some(resolve.to_js_function(context.realm())),\n            Some(reject.to_js_function(context.realm())),\n            context,\n        )?);\n\n        Ok(JsFuture { inner: state })\n    }\n\n    /// Run jobs until this promise is resolved or rejected. This could\n    /// result in an infinite loop if the promise is never resolved or\n    /// rejected (e.g. with a [`boa_engine::job::JobExecutor`] that does\n    /// not prioritize properly). If you need more control over how\n    /// the promise handles timing out, consider using\n    /// [`Context::run_jobs`] directly.\n    ///\n    /// Returns [`Result::Ok`] if the promise resolved, or [`Result::Err`]\n    /// if the promise was rejected. If the promise was already resolved,\n    /// [`Context::run_jobs`] is guaranteed to not be executed.\n    ///\n    /// # Examples\n    ///\n    /// ```\n    /// # use boa_engine::{Context, JsArgs, JsValue, NativeFunction};\n    /// # use boa_engine::object::builtins::{JsFunction, JsPromise};\n    /// # fn main() -> Result<(), Box<dyn std::error::Error>> {\n    /// let context = &mut Context::default();\n    ///\n    /// let p1 = JsPromise::new(\n    ///     |fns, context| {\n    ///         fns.resolve\n    ///             .call(&JsValue::undefined(), &[JsValue::new(1)], context)\n    ///     },\n    ///     context,\n    /// )?;\n    /// let p2 = p1.then(\n    ///     Some(\n    ///         NativeFunction::from_fn_ptr(|_, args, context| {\n    ///             assert_eq!(*args.get_or_undefined(0), JsValue::new(1));\n    ///             Ok(JsValue::new(2))\n    ///         })\n    ///         .to_js_function(context.realm()),\n    ///     ),\n    ///     None,\n    ///     context,\n    /// )?;\n    ///\n    /// assert_eq!(p2.await_blocking(context), Ok(JsValue::new(2)));\n    /// # Ok(())\n    /// # }\n    /// ```\n    ///\n    /// This will not panic as `run_jobs()` is not executed.\n    /// ```\n    /// # use boa_engine::{Context, JsValue, NativeFunction};\n    /// # use boa_engine::object::builtins::JsPromise;\n    ///\n    /// # fn main() -> Result<(), Box<dyn std::error::Error>> {\n    /// let context = &mut Context::default();\n    /// let p1 = JsPromise::new(\n    ///     |fns, context| fns.resolve.call(&JsValue::undefined(), &[], context),\n    ///     context,\n    /// )?\n    /// .then(\n    ///     Some(\n    ///         NativeFunction::from_fn_ptr(|_, _, _| {\n    ///             panic!(\"This will not happen.\");\n    ///         })\n    ///         .to_js_function(context.realm()),\n    ///     ),\n    ///     None,\n    ///     context,\n    /// )?;\n    /// let p2 = JsPromise::resolve(1, context)?;\n    ///\n    /// assert_eq!(p2.await_blocking(context), Ok(JsValue::new(1)));\n    /// // Uncommenting the following line would panic.\n    /// // context.run_jobs();\n    /// # Ok(())\n    /// # }\n    /// ```\n    pub fn await_blocking(&self, context: &mut Context) -> Result<JsValue, JsError> {\n        loop {\n            match self.state() {\n                PromiseState::Pending => {\n                    context.run_jobs()?;\n                }\n                PromiseState::Fulfilled(f) => break Ok(f),\n                PromiseState::Rejected(r) => break Err(JsError::from_opaque(r)),\n            }\n        }\n    }\n\n    #[cfg(feature = \"experimental\")]\n    pub(crate) fn await_native(\n        &self,\n        continuation: crate::native_function::NativeCoroutine,\n        context: &mut Context,\n    ) {\n        use crate::{\n            builtins::{async_generator::AsyncGenerator, generator::GeneratorContext},\n            js_string,\n            object::FunctionObjectBuilder,\n        };\n        use std::cell::Cell;\n\n        // Clone the stack since we split it.\n        let stack = context.vm.stack.clone();\n        let gen_ctx = GeneratorContext::from_current(context, None);\n        context.vm.stack = stack;\n\n        // 3. Let fulfilledClosure be a new Abstract Closure with parameters (value) that captures asyncContext and performs the following steps when called:\n        // 4. Let onFulfilled be CreateBuiltinFunction(fulfilledClosure, 1, \"\", « »).\n        let on_fulfilled = FunctionObjectBuilder::new(\n            context.realm(),\n            NativeFunction::from_copy_closure_with_captures(\n                |_this, args, captures, context| {\n                    // a. Let prevContext be the running execution context.\n                    // b. Suspend prevContext.\n                    // c. Push asyncContext onto the execution context stack; asyncContext is now the running execution context.\n                    // d. Resume the suspended evaluation of asyncContext using NormalCompletion(value) as the result of the operation that suspended it.\n                    let continuation = &captures.0;\n                    let mut r#gen = captures.1.take().js_expect(\"should only run once\")?;\n\n                    // NOTE: We need to get the object before resuming, since it could clear the stack.\n                    let async_generator = r#gen.async_generator_object()?;\n\n                    std::mem::swap(&mut context.vm.stack, &mut r#gen.stack);\n                    let frame = r#gen\n                        .call_frame\n                        .take()\n                        .js_expect(\"should have a call frame\")?;\n                    let fp = frame.fp;\n                    let rp = frame.rp;\n                    context.vm.push_frame(frame);\n                    context.vm.frame_mut().fp = fp;\n                    context.vm.frame_mut().rp = rp;\n\n                    if let crate::native_function::CoroutineState::Yielded(value) =\n                        continuation.call(Ok(args.get_or_undefined(0).clone()), context)?\n                    {\n                        JsPromise::resolve(value, context)?\n                            .await_native(continuation.clone(), context);\n                    }\n\n                    std::mem::swap(&mut context.vm.stack, &mut r#gen.stack);\n                    r#gen.call_frame = context.vm.pop_frame();\n                    assert!(r#gen.call_frame.is_some());\n\n                    if let Some(async_generator) = async_generator {\n                        async_generator\n                            .downcast_mut::<AsyncGenerator>()\n                            .js_expect(\"must be async generator\")?\n                            .context = Some(r#gen);\n                    }\n\n                    // e. Assert: When we reach this step, asyncContext has already been removed from the execution context stack and prevContext is the currently running execution context.\n                    // f. Return undefined.\n                    Ok(JsValue::undefined())\n                },\n                (continuation.clone(), Cell::new(Some(gen_ctx))),\n            ),\n        )\n        .name(js_string!())\n        .length(1)\n        .build();\n\n        let stack = context.vm.stack.clone();\n        let gen_ctx = GeneratorContext::from_current(context, None);\n        context.vm.stack = stack;\n\n        // 5. Let rejectedClosure be a new Abstract Closure with parameters (reason) that captures asyncContext and performs the following steps when called:\n        // 6. Let onRejected be CreateBuiltinFunction(rejectedClosure, 1, \"\", « »).\n        let on_rejected = FunctionObjectBuilder::new(\n            context.realm(),\n            NativeFunction::from_copy_closure_with_captures(\n                |_this, args, captures, context| {\n                    // a. Let prevContext be the running execution context.\n                    // b. Suspend prevContext.\n                    // c. Push asyncContext onto the execution context stack; asyncContext is now the running execution context.\n                    // d. Resume the suspended evaluation of asyncContext using ThrowCompletion(reason) as the result of the operation that suspended it.\n                    // e. Assert: When we reach this step, asyncContext has already been removed from the execution context stack and prevContext is the currently running execution context.\n                    // f. Return undefined.\n                    let continuation = &captures.0;\n                    let mut r#gen = captures.1.take().js_expect(\"should only run once\")?;\n\n                    // NOTE: We need to get the object before resuming, since it could clear the stack.\n                    let async_generator = r#gen.async_generator_object()?;\n\n                    std::mem::swap(&mut context.vm.stack, &mut r#gen.stack);\n                    let frame = r#gen\n                        .call_frame\n                        .take()\n                        .js_expect(\"should have a call frame\")?;\n                    let fp = frame.fp;\n                    let rp = frame.rp;\n                    context.vm.push_frame(frame);\n                    context.vm.frame_mut().fp = fp;\n                    context.vm.frame_mut().rp = rp;\n\n                    if let crate::native_function::CoroutineState::Yielded(value) = continuation\n                        .call(\n                            Err(JsError::from_opaque(args.get_or_undefined(0).clone())),\n                            context,\n                        )?\n                    {\n                        JsPromise::resolve(value, context)?\n                            .await_native(continuation.clone(), context);\n                    }\n\n                    std::mem::swap(&mut context.vm.stack, &mut r#gen.stack);\n                    r#gen.call_frame = context.vm.pop_frame();\n                    assert!(r#gen.call_frame.is_some());\n\n                    if let Some(async_generator) = async_generator {\n                        async_generator\n                            .downcast_mut::<AsyncGenerator>()\n                            .js_expect(\"must be async generator\")?\n                            .context = Some(r#gen);\n                    }\n\n                    Ok(JsValue::undefined())\n                },\n                (continuation, Cell::new(Some(gen_ctx))),\n            ),\n        )\n        .name(js_string!())\n        .length(1)\n        .build();\n\n        // 7. Perform PerformPromiseThen(promise, onFulfilled, onRejected).\n        Promise::perform_promise_then(\n            &self.inner,\n            Some(on_fulfilled),\n            Some(on_rejected),\n            None,\n            context,\n        );\n    }\n}\n\nimpl From<JsObject<Promise>> for JsPromise {\n    fn from(value: JsObject<Promise>) -> Self {\n        Self { inner: value }\n    }\n}\n\nimpl From<JsPromise> for JsObject {\n    #[inline]\n    fn from(o: JsPromise) -> Self {\n        o.inner.clone().upcast()\n    }\n}\n\nimpl From<JsPromise> for JsValue {\n    #[inline]\n    fn from(o: JsPromise) -> Self {\n        o.inner.clone().into()\n    }\n}\n\nimpl std::ops::Deref for JsPromise {\n    type Target = JsObject<Promise>;\n\n    #[inline]\n    fn deref(&self) -> &Self::Target {\n        &self.inner\n    }\n}\n\nimpl TryFromJs for JsPromise {\n    fn try_from_js(value: &JsValue, _context: &mut Context) -> JsResult<Self> {\n        if let Some(o) = value.as_object() {\n            Self::from_object(o.clone())\n        } else {\n            Err(JsNativeError::typ()\n                .with_message(\"value is not a Promise object\")\n                .into())\n        }\n    }\n}\n\nimpl TryIntoJs for JsPromise {\n    fn try_into_js(&self, _: &mut Context) -> JsResult<JsValue> {\n        Ok(self.clone().into())\n    }\n}\n\n/// A Rust's `Future` that becomes ready when a `JsPromise` fulfills.\n///\n/// This type allows `await`ing `JsPromise`s inside Rust's async contexts, which makes interfacing\n/// between promises and futures a bit easier.\n///\n/// The only way to construct an instance of `JsFuture` is by calling [`JsPromise::into_js_future`].\npub struct JsFuture {\n    inner: Gc<GcRefCell<Inner>>,\n}\n\nimpl std::fmt::Debug for JsFuture {\n    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {\n        f.debug_struct(\"JsFuture\").finish_non_exhaustive()\n    }\n}\n\n#[derive(Trace, Finalize)]\nstruct Inner {\n    result: Option<JsResult<JsValue>>,\n    #[unsafe_ignore_trace]\n    task: Option<task::Waker>,\n}\n\n// Taken from:\n// https://docs.rs/wasm-bindgen-futures/0.4.37/src/wasm_bindgen_futures/lib.rs.html#171-187\nimpl Future for JsFuture {\n    type Output = JsResult<JsValue>;\n\n    fn poll(self: Pin<&mut Self>, cx: &mut task::Context<'_>) -> task::Poll<Self::Output> {\n        let mut inner = self.inner.borrow_mut();\n\n        if let Some(result) = inner.result.take() {\n            return task::Poll::Ready(result);\n        }\n\n        inner.task = Some(cx.waker().clone());\n        task::Poll::Pending\n    }\n}\n"
  },
  {
    "path": "core/engine/src/object/builtins/jsproxy.rs",
    "content": "//! A Rust API wrapper for the `Proxy` Builtin ECMAScript Object\nuse super::JsFunction;\nuse crate::{\n    Context, JsExpect, JsNativeError, JsResult, JsValue,\n    builtins::Proxy,\n    js_string,\n    native_function::{NativeFunction, NativeFunctionPointer},\n    object::{FunctionObjectBuilder, JsObject},\n    value::TryFromJs,\n};\nuse boa_gc::{Finalize, Trace};\n\n/// `JsProxy` provides a wrapper for Boa's implementation of the ECMAScript `Proxy` object\n///\n/// This is a wrapper type for the [`Proxy`][proxy] API that allows customizing\n/// essential behaviour for an object, like [property accesses][get] or the\n/// [`delete`][delete] operator.\n///\n/// The only way to construct this type is to use the [`JsProxyBuilder`] type; also\n/// accessible from [`JsProxy::builder`].\n///\n/// [get]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Proxy/Proxy/get\n/// [delete]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Proxy/Proxy/deleteProperty\n/// [proxy]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Proxy\n#[derive(Debug, Clone, Trace, Finalize)]\npub struct JsProxy {\n    inner: JsObject,\n}\n\nimpl JsProxy {\n    /// Creates a new [`JsProxyBuilder`] to easily construct a [`JsProxy`].\n    pub fn builder(target: JsObject) -> JsProxyBuilder {\n        JsProxyBuilder::new(target)\n    }\n\n    /// Create a [`JsProxy`] from a [`JsObject`], if the object is not a `Proxy` throw a\n    /// `TypeError`.\n    #[inline]\n    pub fn from_object(object: JsObject) -> JsResult<Self> {\n        if object.is::<Proxy>() {\n            Ok(Self { inner: object })\n        } else {\n            Err(JsNativeError::typ()\n                .with_message(\"object is not a Proxy\")\n                .into())\n        }\n    }\n}\n\nimpl From<JsProxy> for JsObject {\n    #[inline]\n    fn from(o: JsProxy) -> Self {\n        o.inner.clone()\n    }\n}\n\nimpl From<JsProxy> for JsValue {\n    #[inline]\n    fn from(o: JsProxy) -> Self {\n        o.inner.clone().into()\n    }\n}\n\nimpl std::ops::Deref for JsProxy {\n    type Target = JsObject;\n\n    #[inline]\n    fn deref(&self) -> &Self::Target {\n        &self.inner\n    }\n}\n\nimpl TryFromJs for JsProxy {\n    fn try_from_js(value: &JsValue, _context: &mut Context) -> JsResult<Self> {\n        if let Some(o) = value.as_object() {\n            Self::from_object(o.clone())\n        } else {\n            Err(JsNativeError::typ()\n                .with_message(\"value is not a Proxy object\")\n                .into())\n        }\n    }\n}\n\n/// `JsRevocableProxy` provides a wrapper for `JsProxy` that can be disabled.\n///\n/// Safe interface for the [`Proxy.revocable`][revocable] method that creates a\n/// proxy that can be disabled using the [`JsRevocableProxy::revoke`] method.\n/// The internal proxy is accessible using the [`Deref`][`std::ops::Deref`] operator.\n///\n/// The only way to construct this type is to use the [`JsProxyBuilder`] type; also\n/// accessible from [`JsProxy::builder`]; with the [`JsProxyBuilder::build_revocable`]\n/// method.\n///\n/// [proxy]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Proxy\n/// [revocable]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Proxy/revocable\n#[derive(Debug, Trace, Finalize)]\npub struct JsRevocableProxy {\n    proxy: JsProxy,\n    revoker: JsFunction,\n}\n\nimpl JsRevocableProxy {\n    /// Disables the traps of the internal `proxy` object, essentially\n    /// making it unusable and throwing `TypeError`s for all the traps.\n    #[inline]\n    pub fn revoke(self, context: &mut Context) -> JsResult<()> {\n        self.revoker.call(&JsValue::undefined(), &[], context)?;\n        Ok(())\n    }\n}\n\nimpl std::ops::Deref for JsRevocableProxy {\n    type Target = JsProxy;\n\n    #[inline]\n    fn deref(&self) -> &Self::Target {\n        &self.proxy\n    }\n}\n\n/// Utility builder to create [`JsProxy`] objects from native functions.\n///\n/// This builder can be used when you need to create [`Proxy`] objects\n/// from Rust instead of Javascript, which should generate faster\n/// trap functions than its Javascript counterparts.\n#[must_use]\n#[derive(Clone)]\npub struct JsProxyBuilder {\n    target: JsObject,\n    apply: Option<NativeFunctionPointer>,\n    construct: Option<NativeFunctionPointer>,\n    define_property: Option<NativeFunctionPointer>,\n    delete_property: Option<NativeFunctionPointer>,\n    get: Option<NativeFunctionPointer>,\n    get_own_property_descriptor: Option<NativeFunctionPointer>,\n    get_prototype_of: Option<NativeFunctionPointer>,\n    has: Option<NativeFunctionPointer>,\n    is_extensible: Option<NativeFunctionPointer>,\n    own_keys: Option<NativeFunctionPointer>,\n    prevent_extensions: Option<NativeFunctionPointer>,\n    set: Option<NativeFunctionPointer>,\n    set_prototype_of: Option<NativeFunctionPointer>,\n}\n\nimpl std::fmt::Debug for JsProxyBuilder {\n    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {\n        #[derive(Debug)]\n        struct NativeFunction;\n        f.debug_struct(\"ProxyBuilder\")\n            .field(\"target\", &self.target)\n            .field(\"apply\", &self.apply.map(|_| NativeFunction))\n            .field(\"construct\", &self.construct.map(|_| NativeFunction))\n            .field(\n                \"define_property\",\n                &self.define_property.map(|_| NativeFunction),\n            )\n            .field(\n                \"delete_property\",\n                &self.delete_property.map(|_| NativeFunction),\n            )\n            .field(\"get\", &self.get.map(|_| NativeFunction))\n            .field(\n                \"get_own_property_descriptor\",\n                &self.get_own_property_descriptor.map(|_| NativeFunction),\n            )\n            .field(\n                \"get_prototype_of\",\n                &self.get_prototype_of.map(|_| NativeFunction),\n            )\n            .field(\"has\", &self.has.map(|_| NativeFunction))\n            .field(\"is_extensible\", &self.is_extensible.map(|_| NativeFunction))\n            .field(\"own_keys\", &self.own_keys.map(|_| NativeFunction))\n            .field(\n                \"prevent_extensions\",\n                &self.prevent_extensions.map(|_| NativeFunction),\n            )\n            .field(\"set\", &self.set.map(|_| NativeFunction))\n            .field(\n                \"set_prototype_of\",\n                &self.set_prototype_of.map(|_| NativeFunction),\n            )\n            .finish()\n    }\n}\n\nimpl JsProxyBuilder {\n    /// Create a new `ProxyBuilder` with every trap set to `undefined`.\n    #[inline]\n    pub fn new(target: JsObject) -> Self {\n        Self {\n            target,\n            apply: None,\n            construct: None,\n            define_property: None,\n            delete_property: None,\n            get: None,\n            get_own_property_descriptor: None,\n            get_prototype_of: None,\n            has: None,\n            is_extensible: None,\n            own_keys: None,\n            prevent_extensions: None,\n            set: None,\n            set_prototype_of: None,\n        }\n    }\n\n    /// Set the `apply` proxy trap to the specified native function.\n    ///\n    /// More information:\n    ///\n    /// - [MDN documentation][mdn]\n    ///\n    /// # Note\n    ///\n    /// If the `target` object is not a function, then `apply` will be ignored\n    /// when trying to call the proxy, which will throw a type error.\n    ///\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Proxy/Proxy/apply\n    #[inline]\n    pub fn apply(mut self, apply: NativeFunctionPointer) -> Self {\n        self.apply = Some(apply);\n        self\n    }\n\n    /// Set the `construct` proxy trap to the specified native function.\n    ///\n    /// More information:\n    ///\n    /// - [MDN documentation][mdn]\n    ///\n    /// # Note\n    ///\n    /// If the `target` object is not a constructor, then `construct` will be ignored\n    /// when trying to construct an object using the proxy, which will throw a type error.\n    ///\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Proxy/Proxy/construct\n    #[inline]\n    pub fn construct(mut self, construct: NativeFunctionPointer) -> Self {\n        self.construct = Some(construct);\n        self\n    }\n\n    /// Set the `defineProperty` proxy trap to the specified native function.\n    ///\n    /// More information:\n    ///\n    /// - [MDN documentation][mdn]\n    ///\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Proxy/Proxy/defineProperty\n    #[inline]\n    pub fn define_property(mut self, define_property: NativeFunctionPointer) -> Self {\n        self.define_property = Some(define_property);\n        self\n    }\n\n    /// Set the `deleteProperty` proxy trap to the specified native function.\n    ///\n    /// More information:\n    ///\n    /// - [MDN documentation][mdn]\n    ///\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Proxy/Proxy/deleteProperty\n    #[inline]\n    pub fn delete_property(mut self, delete_property: NativeFunctionPointer) -> Self {\n        self.delete_property = Some(delete_property);\n        self\n    }\n\n    /// Set the `get` proxy trap to the specified native function.\n    ///\n    /// More information:\n    ///\n    /// - [MDN documentation][mdn]\n    ///\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Proxy/Proxy/get\n    #[inline]\n    pub fn get(mut self, get: NativeFunctionPointer) -> Self {\n        self.get = Some(get);\n        self\n    }\n\n    /// Set the `getOwnPropertyDescriptor` proxy trap to the specified native function.\n    ///\n    /// More information:\n    ///\n    /// - [MDN documentation][mdn]\n    ///\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Proxy/Proxy/getOwnPropertyDescriptor\n    #[inline]\n    pub fn get_own_property_descriptor(\n        mut self,\n        get_own_property_descriptor: NativeFunctionPointer,\n    ) -> Self {\n        self.get_own_property_descriptor = Some(get_own_property_descriptor);\n        self\n    }\n\n    /// Set the `getPrototypeOf` proxy trap to the specified native function.\n    ///\n    /// More information:\n    ///\n    /// - [MDN documentation][mdn]\n    ///\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Proxy/Proxy/getPrototypeOf\n    #[inline]\n    pub fn get_prototype_of(mut self, get_prototype_of: NativeFunctionPointer) -> Self {\n        self.get_prototype_of = Some(get_prototype_of);\n        self\n    }\n\n    /// Set the `has` proxy trap to the specified native function.\n    ///\n    /// More information:\n    ///\n    /// - [MDN documentation][mdn]\n    ///\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Proxy/Proxy/has\n    #[inline]\n    pub fn has(mut self, has: NativeFunctionPointer) -> Self {\n        self.has = Some(has);\n        self\n    }\n\n    /// Set the `isExtensible` proxy trap to the specified native function.\n    ///\n    /// More information:\n    ///\n    /// - [MDN documentation][mdn]\n    ///\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Proxy/Proxy/isExtensible\n    #[inline]\n    pub fn is_extensible(mut self, is_extensible: NativeFunctionPointer) -> Self {\n        self.is_extensible = Some(is_extensible);\n        self\n    }\n\n    /// Set the `ownKeys` proxy trap to the specified native function.\n    ///\n    /// More information:\n    ///\n    /// - [MDN documentation][mdn]\n    ///\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Proxy/Proxy/ownKeys\n    #[inline]\n    pub fn own_keys(mut self, own_keys: NativeFunctionPointer) -> Self {\n        self.own_keys = Some(own_keys);\n        self\n    }\n\n    /// Set the `preventExtensions` proxy trap to the specified native function.\n    ///\n    /// More information:\n    ///\n    /// - [MDN documentation][mdn]\n    ///\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Proxy/Proxy/preventExtensions\n    #[inline]\n    pub fn prevent_extensions(mut self, prevent_extensions: NativeFunctionPointer) -> Self {\n        self.prevent_extensions = Some(prevent_extensions);\n        self\n    }\n\n    /// Set the `set` proxy trap to the specified native function.\n    ///\n    /// More information:\n    ///\n    /// - [MDN documentation][mdn]\n    ///\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Proxy/Proxy/set\n    #[inline]\n    pub fn set(mut self, set: NativeFunctionPointer) -> Self {\n        self.set = Some(set);\n        self\n    }\n\n    /// Set the `setPrototypeOf` proxy trap to the specified native function.\n    ///\n    /// More information:\n    ///\n    /// - [MDN documentation][mdn]\n    ///\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Proxy/Proxy/setPrototypeOf\n    #[inline]\n    pub fn set_prototype_of(mut self, set_prototype_of: NativeFunctionPointer) -> Self {\n        self.set_prototype_of = Some(set_prototype_of);\n        self\n    }\n\n    /// Build a [`JsObject`] of kind [`Proxy`].\n    ///\n    /// Equivalent to the `Proxy ( target, handler )` constructor, but returns a\n    /// [`JsObject`] in case there's a need to manipulate the returned object\n    /// inside Rust code.\n    pub fn build(self, context: &mut Context) -> JsResult<JsProxy> {\n        let handler = JsObject::with_object_proto(context.intrinsics());\n\n        if let Some(apply) = self.apply {\n            let f = FunctionObjectBuilder::new(context.realm(), NativeFunction::from_fn_ptr(apply))\n                .length(3)\n                .build();\n            handler\n                .create_data_property_or_throw(js_string!(\"apply\"), f, context)\n                .js_expect(\"new object should be writable\")?;\n        }\n        if let Some(construct) = self.construct {\n            let f =\n                FunctionObjectBuilder::new(context.realm(), NativeFunction::from_fn_ptr(construct))\n                    .length(3)\n                    .build();\n            handler\n                .create_data_property_or_throw(js_string!(\"construct\"), f, context)\n                .js_expect(\"new object should be writable\")?;\n        }\n        if let Some(define_property) = self.define_property {\n            let f = FunctionObjectBuilder::new(\n                context.realm(),\n                NativeFunction::from_fn_ptr(define_property),\n            )\n            .length(3)\n            .build();\n            handler\n                .create_data_property_or_throw(js_string!(\"defineProperty\"), f, context)\n                .js_expect(\"new object should be writable\")?;\n        }\n        if let Some(delete_property) = self.delete_property {\n            let f = FunctionObjectBuilder::new(\n                context.realm(),\n                NativeFunction::from_fn_ptr(delete_property),\n            )\n            .length(2)\n            .build();\n            handler\n                .create_data_property_or_throw(js_string!(\"deleteProperty\"), f, context)\n                .js_expect(\"new object should be writable\")?;\n        }\n        if let Some(get) = self.get {\n            let f = FunctionObjectBuilder::new(context.realm(), NativeFunction::from_fn_ptr(get))\n                .length(3)\n                .build();\n            handler\n                .create_data_property_or_throw(js_string!(\"get\"), f, context)\n                .js_expect(\"new object should be writable\")?;\n        }\n        if let Some(get_own_property_descriptor) = self.get_own_property_descriptor {\n            let f = FunctionObjectBuilder::new(\n                context.realm(),\n                NativeFunction::from_fn_ptr(get_own_property_descriptor),\n            )\n            .length(2)\n            .build();\n            handler\n                .create_data_property_or_throw(js_string!(\"getOwnPropertyDescriptor\"), f, context)\n                .js_expect(\"new object should be writable\")?;\n        }\n        if let Some(get_prototype_of) = self.get_prototype_of {\n            let f = FunctionObjectBuilder::new(\n                context.realm(),\n                NativeFunction::from_fn_ptr(get_prototype_of),\n            )\n            .length(1)\n            .build();\n            handler\n                .create_data_property_or_throw(js_string!(\"getPrototypeOf\"), f, context)\n                .js_expect(\"new object should be writable\")?;\n        }\n        if let Some(has) = self.has {\n            let f = FunctionObjectBuilder::new(context.realm(), NativeFunction::from_fn_ptr(has))\n                .length(2)\n                .build();\n            handler\n                .create_data_property_or_throw(js_string!(\"has\"), f, context)\n                .js_expect(\"new object should be writable\")?;\n        }\n        if let Some(is_extensible) = self.is_extensible {\n            let f = FunctionObjectBuilder::new(\n                context.realm(),\n                NativeFunction::from_fn_ptr(is_extensible),\n            )\n            .length(1)\n            .build();\n            handler\n                .create_data_property_or_throw(js_string!(\"isExtensible\"), f, context)\n                .js_expect(\"new object should be writable\")?;\n        }\n        if let Some(own_keys) = self.own_keys {\n            let f =\n                FunctionObjectBuilder::new(context.realm(), NativeFunction::from_fn_ptr(own_keys))\n                    .length(1)\n                    .build();\n            handler\n                .create_data_property_or_throw(js_string!(\"ownKeys\"), f, context)\n                .js_expect(\"new object should be writable\")?;\n        }\n        if let Some(prevent_extensions) = self.prevent_extensions {\n            let f = FunctionObjectBuilder::new(\n                context.realm(),\n                NativeFunction::from_fn_ptr(prevent_extensions),\n            )\n            .length(1)\n            .build();\n            handler\n                .create_data_property_or_throw(js_string!(\"preventExtensions\"), f, context)\n                .js_expect(\"new object should be writable\")?;\n        }\n        if let Some(set) = self.set {\n            let f = FunctionObjectBuilder::new(context.realm(), NativeFunction::from_fn_ptr(set))\n                .length(4)\n                .build();\n            handler\n                .create_data_property_or_throw(js_string!(\"set\"), f, context)\n                .js_expect(\"new object should be writable\")?;\n        }\n        if let Some(set_prototype_of) = self.set_prototype_of {\n            let f = FunctionObjectBuilder::new(\n                context.realm(),\n                NativeFunction::from_fn_ptr(set_prototype_of),\n            )\n            .length(2)\n            .build();\n            handler\n                .create_data_property_or_throw(js_string!(\"setPrototypeOf\"), f, context)\n                .js_expect(\"new object should be writable\")?;\n        }\n\n        let proxy = JsObject::from_proto_and_data_with_shared_shape(\n            context.root_shape(),\n            context.intrinsics().constructors().object().prototype(),\n            Proxy::new(self.target, handler),\n        )\n        .upcast();\n\n        Ok(JsProxy { inner: proxy })\n    }\n\n    /// Builds a [`JsObject`] of kind [`Proxy`] and a [`JsFunction`] that, when\n    /// called, disables the proxy of the object.\n    ///\n    /// Equivalent to the `Proxy.revocable ( target, handler )` static method,\n    /// but returns a [`JsObject`] for the proxy and a [`JsFunction`] for the\n    /// revoker in case there's a need to manipulate the returned objects\n    /// inside Rust code.\n    pub fn build_revocable(self, context: &mut Context) -> JsResult<JsRevocableProxy> {\n        let proxy = self.build(context)?;\n        let revoker = Proxy::revoker(proxy.inner.clone(), context);\n\n        Ok(JsRevocableProxy { proxy, revoker })\n    }\n}\n"
  },
  {
    "path": "core/engine/src/object/builtins/jsregexp.rs",
    "content": "//! A Rust API wrapper for Boa's `RegExp` Builtin ECMAScript Object\nuse crate::{\n    Context, JsExpect, JsNativeError, JsResult, JsValue,\n    builtins::RegExp,\n    error::PanicError,\n    object::{JsArray, JsObject},\n    value::TryFromJs,\n};\n\nuse boa_gc::{Finalize, Trace};\nuse std::ops::Deref;\n\n/// `JsRegExp` provides a wrapper for Boa's implementation of the ECMAScript `RegExp` builtin object\n///\n/// # Examples\n///\n/// Create a `JsRegExp` and run RegExp.prototype.test( String )\n///\n/// ```\n/// # use boa_engine::{\n/// #  object::builtins::JsRegExp,\n/// #  Context, JsValue, JsResult,js_string\n/// # };\n/// # fn main() -> JsResult<()> {\n/// // Initialize the `Context`\n/// let context = &mut Context::default();\n///\n/// // Create a new RegExp with pattern and flags\n/// let regexp = JsRegExp::new(js_string!(\"foo\"), js_string!(\"gi\"), context)?;\n///\n/// let test_result = regexp.test(js_string!(\"football\"), context)?;\n/// assert!(test_result);\n///\n/// let to_string = regexp.to_string(context)?;\n/// assert_eq!(to_string, String::from(\"/foo/gi\"));\n/// # Ok(())\n/// # }\n/// ```\n#[derive(Debug, Clone, Trace, Finalize)]\npub struct JsRegExp {\n    inner: JsObject,\n}\n\nimpl JsRegExp {\n    /// Create a new `JsRegExp` object\n    /// ```\n    /// # use boa_engine::{\n    /// #  object::builtins::JsRegExp,\n    /// #  Context, JsValue, JsResult, js_string\n    /// # };\n    /// # fn main() -> JsResult<()> {\n    /// // Initialize the `Context`\n    /// let context = &mut Context::default();\n    ///\n    /// // Create a new RegExp with pattern and flags\n    /// let regexp = JsRegExp::new(js_string!(\"foo\"), js_string!(\"gi\"), context)?;\n    /// # Ok(())\n    /// # }\n    /// ```\n    pub fn new<S>(pattern: S, flags: S, context: &mut Context) -> JsResult<Self>\n    where\n        S: Into<JsValue>,\n    {\n        let regexp = RegExp::initialize(None, &pattern.into(), &flags.into(), context)?\n            .as_object()\n            .js_expect(\"RegExp::initialize must return a RegExp object\")?\n            .clone();\n\n        Ok(Self { inner: regexp })\n    }\n\n    /// Create a `JsRegExp` from a regular expression `JsObject`\n    #[inline]\n    pub fn from_object(object: JsObject) -> JsResult<Self> {\n        if object.is::<RegExp>() {\n            Ok(Self { inner: object })\n        } else {\n            Err(JsNativeError::typ()\n                .with_message(\"object is not a RegExp\")\n                .into())\n        }\n    }\n\n    /// Returns a boolean value for whether the `d` flag is present in `JsRegExp` flags\n    #[inline]\n    pub fn has_indices(&self, context: &mut Context) -> JsResult<bool> {\n        RegExp::get_has_indices(&self.inner.clone().into(), &[], context).and_then(|v| {\n            v.as_boolean()\n                .js_expect(\"value must be a bool\")\n                .map_err(Into::into)\n        })\n    }\n\n    /// Returns a boolean value for whether the `g` flag is present in `JsRegExp` flags\n    #[inline]\n    pub fn global(&self, context: &mut Context) -> JsResult<bool> {\n        RegExp::get_global(&self.inner.clone().into(), &[], context).and_then(|v| {\n            v.as_boolean()\n                .js_expect(\"value must be a bool\")\n                .map_err(Into::into)\n        })\n    }\n\n    /// Returns a boolean value for whether the `i` flag is present in `JsRegExp` flags\n    #[inline]\n    pub fn ignore_case(&self, context: &mut Context) -> JsResult<bool> {\n        RegExp::get_ignore_case(&self.inner.clone().into(), &[], context).and_then(|v| {\n            v.as_boolean()\n                .js_expect(\"value must be a bool\")\n                .map_err(Into::into)\n        })\n    }\n\n    /// Returns a boolean value for whether the `m` flag is present in `JsRegExp` flags\n    #[inline]\n    pub fn multiline(&self, context: &mut Context) -> JsResult<bool> {\n        RegExp::get_multiline(&self.inner.clone().into(), &[], context).and_then(|v| {\n            v.as_boolean()\n                .js_expect(\"value must be a bool\")\n                .map_err(Into::into)\n        })\n    }\n\n    /// Returns a boolean value for whether the `s` flag is present in `JsRegExp` flags\n    #[inline]\n    pub fn dot_all(&self, context: &mut Context) -> JsResult<bool> {\n        RegExp::get_dot_all(&self.inner.clone().into(), &[], context).and_then(|v| {\n            v.as_boolean()\n                .js_expect(\"value must be a bool\")\n                .map_err(Into::into)\n        })\n    }\n\n    /// Returns a boolean value for whether the `u` flag is present in `JsRegExp` flags\n    #[inline]\n    pub fn unicode(&self, context: &mut Context) -> JsResult<bool> {\n        RegExp::get_unicode(&self.inner.clone().into(), &[], context).and_then(|v| {\n            v.as_boolean()\n                .js_expect(\"value must be a bool\")\n                .map_err(Into::into)\n        })\n    }\n\n    /// Returns a boolean value for whether the `y` flag is present in `JsRegExp` flags\n    #[inline]\n    pub fn sticky(&self, context: &mut Context) -> JsResult<bool> {\n        RegExp::get_sticky(&self.inner.clone().into(), &[], context).and_then(|v| {\n            v.as_boolean()\n                .js_expect(\"value must be a bool\")\n                .map_err(Into::into)\n        })\n    }\n\n    /// Returns the flags of `JsRegExp` as a string\n    /// ```\n    /// # use boa_engine::{\n    /// #  object::builtins::JsRegExp,\n    /// #  Context, JsValue, JsResult, js_string\n    /// # };\n    /// # fn main() -> JsResult<()> {\n    /// # let context = &mut Context::default();\n    /// let regexp = JsRegExp::new(js_string!(\"foo\"), js_string!(\"gi\"), context)?;\n    ///\n    /// let flags = regexp.flags(context)?;\n    /// assert_eq!(flags, String::from(\"gi\"));\n    /// # Ok(())\n    /// # }\n    /// ```\n    #[inline]\n    pub fn flags(&self, context: &mut Context) -> JsResult<String> {\n        RegExp::get_flags(&self.inner.clone().into(), &[], context).and_then(|v| {\n            v.as_string()\n                .js_expect(\"value must be string\")?\n                .to_std_string()\n                .map_err(|e| PanicError::new(e.to_string()).into())\n        })\n    }\n\n    /// Returns the source pattern of `JsRegExp` as a string\n    /// ```\n    /// # use boa_engine::{\n    /// #  object::builtins::JsRegExp,\n    /// #  Context, JsValue, JsResult, js_string\n    /// # };\n    /// # fn main() -> JsResult<()> {\n    /// # let context = &mut Context::default();\n    /// let regexp = JsRegExp::new(js_string!(\"foo\"), js_string!(\"gi\"), context)?;\n    ///\n    /// let src = regexp.source(context)?;\n    /// assert_eq!(src, String::from(\"foo\"));\n    /// # Ok(())\n    /// # }\n    /// ```\n    #[inline]\n    pub fn source(&self, context: &mut Context) -> JsResult<String> {\n        RegExp::get_source(&self.inner.clone().into(), &[], context).and_then(|v| {\n            v.as_string()\n                .js_expect(\"value must be string\")?\n                .to_std_string()\n                .map_err(|e| PanicError::new(e.to_string()).into())\n        })\n    }\n\n    /// Executes a search for a match between `JsRegExp` and the provided string\n    /// ```\n    /// # use boa_engine::{\n    /// #  object::builtins::JsRegExp,\n    /// #  Context, JsValue, JsResult, js_string\n    /// # };\n    /// # fn main() -> JsResult<()> {\n    /// # let context = &mut Context::default();\n    /// let regexp = JsRegExp::new(js_string!(\"foo\"), js_string!(\"gi\"), context)?;\n    ///\n    /// let test_result = regexp.test(js_string!(\"football\"), context)?;\n    /// assert!(test_result);\n    /// # Ok(())\n    /// # }\n    /// ```\n    pub fn test<S>(&self, search_string: S, context: &mut Context) -> JsResult<bool>\n    where\n        S: Into<JsValue>,\n    {\n        RegExp::test(&self.inner.clone().into(), &[search_string.into()], context).and_then(|v| {\n            v.as_boolean()\n                .js_expect(\"value must be a bool\")\n                .map_err(Into::into)\n        })\n    }\n\n    /// Executes a search for a match in a specified string\n    ///\n    /// Returns a `JsArray` containing matched value and updates the `lastIndex` property, or `None`\n    pub fn exec<S>(&self, search_string: S, context: &mut Context) -> JsResult<Option<JsArray>>\n    where\n        S: Into<JsValue>,\n    {\n        RegExp::exec(&self.inner.clone().into(), &[search_string.into()], context).and_then(|v| {\n            if v.is_null() {\n                Ok(None)\n            } else {\n                let obj = v.to_object(context).js_expect(\"value must be an array\")?;\n                let array = JsArray::from_object(obj)\n                    .js_expect(\"from_object must not fail if value is an array object\")?;\n                Ok(Some(array))\n            }\n        })\n    }\n\n    /// Return a string representing the regular expression.\n    /// ```\n    /// # use boa_engine::{\n    /// #  object::builtins::JsRegExp,\n    /// #  Context, JsValue, JsResult, js_string\n    /// # };\n    /// # fn main() -> JsResult<()> {\n    /// # let context = &mut Context::default();\n    /// let regexp = JsRegExp::new(js_string!(\"foo\"), js_string!(\"gi\"), context)?;\n    ///\n    /// let to_string = regexp.to_string(context)?;\n    /// assert_eq!(to_string, \"/foo/gi\");\n    /// # Ok(())\n    /// # }\n    /// ```\n    #[inline]\n    pub fn to_string(&self, context: &mut Context) -> JsResult<String> {\n        RegExp::to_string(&self.inner.clone().into(), &[], context).and_then(|v| {\n            v.as_string()\n                .js_expect(\"value must be a string\")?\n                .to_std_string()\n                .map_err(|e| PanicError::new(e.to_string()).into())\n        })\n    }\n}\n\nimpl From<JsRegExp> for JsObject {\n    #[inline]\n    fn from(o: JsRegExp) -> Self {\n        o.inner.clone()\n    }\n}\n\nimpl From<JsRegExp> for JsValue {\n    #[inline]\n    fn from(o: JsRegExp) -> Self {\n        o.inner.clone().into()\n    }\n}\n\nimpl Deref for JsRegExp {\n    type Target = JsObject;\n\n    #[inline]\n    fn deref(&self) -> &Self::Target {\n        &self.inner\n    }\n}\n\nimpl TryFromJs for JsRegExp {\n    fn try_from_js(value: &JsValue, _context: &mut Context) -> JsResult<Self> {\n        if let Some(o) = value.as_object() {\n            Self::from_object(o.clone())\n        } else {\n            Err(JsNativeError::typ()\n                .with_message(\"value is not a RegExp object\")\n                .into())\n        }\n    }\n}\n"
  },
  {
    "path": "core/engine/src/object/builtins/jsset.rs",
    "content": "//! A Rust API wrapper for the `Set` Builtin ECMAScript Object\nuse std::ops::Deref;\n\nuse boa_gc::{Finalize, Trace};\n\nuse crate::{\n    Context, JsResult, JsValue,\n    builtins::{\n        Set, canonicalize_keyed_collection_key, iterable::IteratorHint,\n        set::ordered_set::OrderedSet,\n    },\n    error::JsNativeError,\n    object::{JsFunction, JsObject, JsSetIterator},\n    value::TryFromJs,\n};\n\n/// `JsSet` provides a wrapper for Boa's implementation of the ECMAScript `Set` object.\n#[derive(Debug, Clone, Trace, Finalize)]\npub struct JsSet {\n    inner: JsObject<OrderedSet>,\n}\n\nimpl JsSet {\n    /// Create a new empty set.\n    ///\n    /// Doesn't matches JavaScript `new Set()` as it doesn't takes an iterator\n    /// similar to Rust initialization.\n    #[inline]\n    pub fn new(context: &mut Context) -> Self {\n        Self {\n            inner: JsObject::from_proto_and_data_with_shared_shape(\n                context.root_shape(),\n                context.intrinsics().constructors().set().prototype(),\n                OrderedSet::new(),\n            ),\n        }\n    }\n\n    /// Returns the size of the `Set` as an integer.\n    ///\n    /// Same as JavaScript's `set.size`.\n    #[inline]\n    #[must_use]\n    pub fn size(&self) -> usize {\n        self.inner.borrow().data().len()\n    }\n\n    /// Appends value to the Set object.\n    /// Returns the Set object with added value.\n    ///\n    /// Same as JavaScript's `set.add(value)`.\n    pub fn add<T>(&self, value: T, context: &mut Context) -> JsResult<JsValue>\n    where\n        T: Into<JsValue>,\n    {\n        self.add_items(&[value.into()], context)\n    }\n\n    /// Adds slice as a single element.\n    /// Returns the Set object with added slice.\n    ///\n    /// Same as JavaScript's `set.add([\"one\", \"two\", \"three\"])`\n    #[inline]\n    pub fn add_items(&self, items: &[JsValue], context: &mut Context) -> JsResult<JsValue> {\n        Set::add(&self.inner.clone().into(), items, context)\n    }\n\n    /// Removes all elements from the Set object.\n    ///\n    /// Same as JavaScript's `set.clear()`.\n    #[inline]\n    pub fn clear(&self) {\n        self.inner.borrow_mut().data_mut().clear();\n    }\n\n    /// Removes the element associated to the value.\n    /// Returns a boolean asserting whether an element was\n    /// successfully removed or not.\n    ///\n    /// Same as JavaScript's `set.delete(value)`.\n    pub fn delete<T>(&self, value: T) -> bool\n    where\n        T: Into<JsValue>,\n    {\n        self.borrow_mut()\n            .data_mut()\n            .delete(&canonicalize_keyed_collection_key(value.into()))\n    }\n\n    /// Returns a boolean asserting whether an element is present\n    /// with the given value in the Set object or not.\n    ///\n    /// Same as JavaScript's `set.has(value)`.\n    #[must_use]\n    pub fn has<T>(&self, value: T) -> bool\n    where\n        T: Into<JsValue>,\n    {\n        self.borrow()\n            .data()\n            .contains(&canonicalize_keyed_collection_key(value.into()))\n    }\n\n    /// Returns a new iterator object that yields the values\n    /// for each element in the Set object in insertion order.\n    ///\n    /// Same as JavaScript's `set.values()`.\n    #[inline]\n    pub fn values(&self, context: &mut Context) -> JsResult<JsSetIterator> {\n        let iterator_object = Set::values(&self.inner.clone().into(), &[JsValue::null()], context)?\n            .get_iterator(IteratorHint::Sync, context)?;\n\n        JsSetIterator::from_object(iterator_object.iterator().clone())\n    }\n\n    /// Alias for `Set.prototype.values()`\n    /// Returns a new iterator object that yields the values\n    /// for each element in the Set object in insertion order.\n    ///\n    /// Same as JavaScript's `set.keys()`.\n    #[inline]\n    pub fn keys(&self, context: &mut Context) -> JsResult<JsSetIterator> {\n        let iterator_object = Set::values(&self.inner.clone().into(), &[JsValue::null()], context)?\n            .get_iterator(IteratorHint::Sync, context)?;\n\n        JsSetIterator::from_object(iterator_object.iterator().clone())\n    }\n\n    /// Calls callbackFn once for each value present in the Set object,\n    /// in insertion order.\n    /// Returns `Undefined`.\n    ///\n    /// Same as JavaScript's `set.forEach(values)`.\n    #[inline]\n    pub fn for_each(\n        &self,\n        callback: JsFunction,\n        this_arg: JsValue,\n        context: &mut Context,\n    ) -> JsResult<JsValue> {\n        Set::for_each(\n            &self.inner.clone().into(),\n            &[callback.into(), this_arg],\n            context,\n        )\n    }\n\n    /// Executes the provided callback function for each key-value pair within the [`JsSet`].\n    #[inline]\n    pub fn for_each_native<F>(&self, f: F) -> JsResult<()>\n    where\n        F: FnMut(JsValue) -> JsResult<()>,\n    {\n        let this = self.inner.clone().into();\n        Set::for_each_native(&this, f)\n    }\n\n    /// Utility: Creates `JsSet` from `JsObject`, otherwise returns the original object as an `Err`.\n    #[inline]\n    pub fn from_object(object: JsObject) -> Result<Self, JsObject> {\n        object.downcast::<OrderedSet>().map(|o| Self { inner: o })\n    }\n\n    /// Utility: Creates a `JsSet` from a `<IntoIterator<Item = JsValue>` convertible object.\n    pub fn from_iter<I>(elements: I, context: &mut Context) -> Self\n    where\n        I: IntoIterator<Item = JsValue>,\n    {\n        let elements = elements.into_iter();\n\n        // Create empty Set\n        let mut set = OrderedSet::with_capacity(elements.size_hint().0);\n\n        // For each element e of elements, do\n        for elem in elements {\n            let elem = canonicalize_keyed_collection_key(elem);\n            set.add(elem);\n        }\n\n        Self {\n            inner: JsObject::from_proto_and_data_with_shared_shape(\n                context.root_shape(),\n                context.intrinsics().constructors().set().prototype(),\n                set,\n            ),\n        }\n    }\n}\n\nimpl From<JsObject<OrderedSet>> for JsSet {\n    fn from(value: JsObject<OrderedSet>) -> Self {\n        Self { inner: value }\n    }\n}\n\nimpl From<JsSet> for JsObject {\n    #[inline]\n    fn from(o: JsSet) -> Self {\n        o.inner.clone().upcast()\n    }\n}\n\nimpl From<JsSet> for JsValue {\n    #[inline]\n    fn from(o: JsSet) -> Self {\n        o.inner.clone().into()\n    }\n}\n\nimpl Deref for JsSet {\n    type Target = JsObject<OrderedSet>;\n    #[inline]\n    fn deref(&self) -> &Self::Target {\n        &self.inner\n    }\n}\n\nimpl TryFromJs for JsSet {\n    fn try_from_js(value: &JsValue, _context: &mut Context) -> JsResult<Self> {\n        if let Some(o) = value.as_object()\n            && let Ok(set) = Self::from_object(o.clone())\n        {\n            Ok(set)\n        } else {\n            Err(JsNativeError::typ()\n                .with_message(\"value is not a Set object\")\n                .into())\n        }\n    }\n}\n"
  },
  {
    "path": "core/engine/src/object/builtins/jsset_iterator.rs",
    "content": "//! A Rust API wrapper for Boa's `SetIterator` Builtin ECMAScript Object\nuse std::ops::Deref;\n\nuse boa_gc::{Finalize, Trace};\n\nuse crate::{\n    Context, JsResult, JsValue, builtins::set::SetIterator, error::JsNativeError, object::JsObject,\n    value::TryFromJs,\n};\n\n/// `JsSetIterator` provides a wrapper for Boa's implementation of the ECMAScript `SetIterator` object\n#[derive(Debug, Clone, Finalize, Trace)]\npub struct JsSetIterator {\n    inner: JsObject,\n}\n\nimpl JsSetIterator {\n    /// Create a `JsSetIterator` from a `JsObject`.\n    /// If object is not a `SetIterator`, throw `TypeError`.\n    pub fn from_object(object: JsObject) -> JsResult<Self> {\n        if object.is::<SetIterator>() {\n            Ok(Self { inner: object })\n        } else {\n            Err(JsNativeError::typ()\n                .with_message(\"object is not a SetIterator\")\n                .into())\n        }\n    }\n    /// Advances the `JsSetIterator` and gets the next result in the `JsSet`.\n    pub fn next(&self, context: &mut Context) -> JsResult<JsValue> {\n        SetIterator::next(&self.inner.clone().into(), &[JsValue::null()], context)\n    }\n}\n\nimpl From<JsSetIterator> for JsObject {\n    #[inline]\n    fn from(o: JsSetIterator) -> Self {\n        o.inner.clone()\n    }\n}\n\nimpl From<JsSetIterator> for JsValue {\n    #[inline]\n    fn from(o: JsSetIterator) -> Self {\n        o.inner.clone().into()\n    }\n}\n\nimpl Deref for JsSetIterator {\n    type Target = JsObject;\n\n    #[inline]\n    fn deref(&self) -> &Self::Target {\n        &self.inner\n    }\n}\n\nimpl TryFromJs for JsSetIterator {\n    fn try_from_js(value: &JsValue, _context: &mut Context) -> JsResult<Self> {\n        if let Some(o) = value.as_object() {\n            Self::from_object(o.clone())\n        } else {\n            Err(JsNativeError::typ()\n                .with_message(\"value is not a SetIterator object\")\n                .into())\n        }\n    }\n}\n"
  },
  {
    "path": "core/engine/src/object/builtins/jssharedarraybuffer.rs",
    "content": "//! A Rust API wrapper for Boa's `SharedArrayBuffer` Builtin ECMAScript Object\nuse crate::{\n    Context, JsResult, JsValue,\n    builtins::array_buffer::{SharedArrayBuffer, utils::SliceRef},\n    error::JsNativeError,\n    object::JsObject,\n    value::TryFromJs,\n};\nuse boa_gc::{Finalize, Trace};\nuse std::{ops::Deref, sync::atomic::Ordering};\n\n/// `JsSharedArrayBuffer` provides a wrapper for Boa's implementation of the ECMAScript `ArrayBuffer` object\n#[derive(Debug, Clone, Trace, Finalize)]\n#[boa_gc(unsafe_no_drop)]\npub struct JsSharedArrayBuffer {\n    inner: JsObject<SharedArrayBuffer>,\n}\n\nimpl From<JsSharedArrayBuffer> for JsObject<SharedArrayBuffer> {\n    #[inline]\n    fn from(value: JsSharedArrayBuffer) -> Self {\n        value.inner\n    }\n}\n\nimpl From<JsObject<SharedArrayBuffer>> for JsSharedArrayBuffer {\n    #[inline]\n    fn from(value: JsObject<SharedArrayBuffer>) -> Self {\n        JsSharedArrayBuffer { inner: value }\n    }\n}\n\nimpl JsSharedArrayBuffer {\n    /// Creates a new [`JsSharedArrayBuffer`] with `byte_length` bytes of allocated space.\n    #[inline]\n    pub fn new(byte_length: usize, context: &mut Context) -> JsResult<Self> {\n        let inner = SharedArrayBuffer::allocate(\n            &context\n                .intrinsics()\n                .constructors()\n                .shared_array_buffer()\n                .constructor()\n                .into(),\n            byte_length as u64,\n            None,\n            context,\n        )?;\n\n        Ok(Self { inner })\n    }\n\n    /// Creates a [`JsSharedArrayBuffer`] from a shared raw buffer.\n    #[inline]\n    pub fn from_buffer(buffer: SharedArrayBuffer, context: &mut Context) -> Self {\n        let proto = context\n            .intrinsics()\n            .constructors()\n            .shared_array_buffer()\n            .prototype();\n\n        let inner = JsObject::new(context.root_shape(), proto, buffer);\n\n        Self { inner }\n    }\n\n    /// Creates a [`JsSharedArrayBuffer`] from a [`JsObject`], throwing a `TypeError` if the object\n    /// is not a shared array buffer.\n    ///\n    /// This does not clone the fields of the shared array buffer, it only does a shallow clone of\n    /// the object.\n    #[inline]\n    pub fn from_object(object: JsObject) -> JsResult<Self> {\n        object\n            .downcast::<SharedArrayBuffer>()\n            .map(|inner| Self { inner })\n            .map_err(|_| {\n                JsNativeError::typ()\n                    .with_message(\"object is not a SharedArrayBuffer\")\n                    .into()\n            })\n    }\n\n    /// Returns the byte length of the array buffer.\n    #[inline]\n    #[must_use]\n    pub fn byte_length(&self) -> usize {\n        self.borrow().data().len(Ordering::SeqCst)\n    }\n\n    /// Copies the contents of this [`JsSharedArrayBuffer`] into a new [`Vec<u8>`].\n    ///\n    /// Each byte is loaded with `SeqCst` ordering into the returned buffer.\n    /// GC-safe and safe for concurrent access within Boa's memory model.\n    ///\n    /// # Example\n    ///\n    /// ```\n    /// # use boa_engine::{Context, JsResult, object::builtins::JsSharedArrayBuffer};\n    /// # fn main() -> JsResult<()> {\n    /// let context = &mut Context::default();\n    /// let sab = JsSharedArrayBuffer::new(64, context)?;\n    /// let bytes = sab.to_vec();\n    /// assert_eq!(bytes.len(), 64);\n    /// # Ok(())\n    /// # }\n    /// ```\n    #[must_use]\n    pub fn to_vec(&self) -> Vec<u8> {\n        let obj = self.borrow();\n        let src = obj.data().bytes(Ordering::SeqCst);\n        let src = SliceRef::AtomicSlice(src);\n        src.to_vec()\n    }\n\n    /// Gets the raw buffer of this `JsSharedArrayBuffer`.\n    #[inline]\n    #[must_use]\n    pub fn inner(&self) -> SharedArrayBuffer {\n        self.borrow().data().clone()\n    }\n}\n\nimpl From<JsSharedArrayBuffer> for JsObject {\n    #[inline]\n    fn from(o: JsSharedArrayBuffer) -> Self {\n        o.inner.upcast()\n    }\n}\n\nimpl From<JsSharedArrayBuffer> for JsValue {\n    #[inline]\n    fn from(o: JsSharedArrayBuffer) -> Self {\n        o.inner.upcast().into()\n    }\n}\n\nimpl Deref for JsSharedArrayBuffer {\n    type Target = JsObject<SharedArrayBuffer>;\n\n    #[inline]\n    fn deref(&self) -> &Self::Target {\n        &self.inner\n    }\n}\n\nimpl TryFromJs for JsSharedArrayBuffer {\n    fn try_from_js(value: &JsValue, _context: &mut Context) -> JsResult<Self> {\n        if let Some(o) = value.as_object() {\n            Self::from_object(o.clone())\n        } else {\n            Err(JsNativeError::typ()\n                .with_message(\"value is not a SharedArrayBuffer object\")\n                .into())\n        }\n    }\n}\n\n#[cfg(test)]\nmod tests {\n    use super::*;\n\n    #[test]\n    fn shared_array_buffer_to_vec_roundtrip() {\n        let context = &mut Context::default();\n        let len = 64;\n        let sab = JsSharedArrayBuffer::new(len, context).unwrap();\n        assert_eq!(sab.byte_length(), len);\n\n        // Write a pattern at multiple indices and ensure `to_vec` observes it.\n        let inner = sab.inner();\n        let atoms = inner.bytes(Ordering::SeqCst);\n        atoms[0].store(1, Ordering::SeqCst);\n        atoms[1].store(2, Ordering::SeqCst);\n        atoms[len - 1].store(255, Ordering::SeqCst);\n\n        let bytes = sab.to_vec();\n        assert_eq!(bytes.len(), len);\n        assert_eq!(bytes[0], 1);\n        assert_eq!(bytes[1], 2);\n        assert_eq!(bytes[len - 1], 255);\n    }\n\n    #[test]\n    fn shared_array_buffer_to_vec_zero_length() {\n        let context = &mut Context::default();\n        let sab = JsSharedArrayBuffer::new(0, context).unwrap();\n        assert_eq!(sab.byte_length(), 0);\n\n        let bytes = sab.to_vec();\n        assert!(bytes.is_empty());\n    }\n}\n"
  },
  {
    "path": "core/engine/src/object/builtins/jstypedarray.rs",
    "content": "//! Rust API wrappers for the `TypedArray` Builtin ECMAScript Objects\nuse crate::{\n    Context, JsExpect, JsResult, JsString, JsValue,\n    builtins::{\n        BuiltInConstructor,\n        array_buffer::AlignedVec,\n        typed_array::{BuiltinTypedArray, TypedArray, TypedArrayKind},\n    },\n    error::JsNativeError,\n    object::{JsArrayBuffer, JsFunction, JsObject, JsSharedArrayBuffer},\n    value::{IntoOrUndefined, TryFromJs},\n};\nuse boa_gc::{Finalize, Trace};\nuse std::ops::Deref;\nuse std::sync::atomic::Ordering;\n\n/// `JsTypedArray` provides a wrapper for Boa's implementation of the ECMAScript `TypedArray`\n/// builtin object.\n#[derive(Debug, Clone, Trace, Finalize)]\npub struct JsTypedArray {\n    inner: JsObject,\n}\n\nimpl JsTypedArray {\n    /// Create a [`JsTypedArray`] from a [`JsObject`], if the object is not a typed array throw a\n    /// `TypeError`.\n    ///\n    /// This does not clone the fields of the typed array, it only does a shallow clone of the\n    /// object.\n    #[inline]\n    pub fn from_object(object: JsObject) -> JsResult<Self> {\n        if object.is::<TypedArray>() {\n            Ok(Self { inner: object })\n        } else {\n            Err(JsNativeError::typ()\n                .with_message(\"object is not a TypedArray\")\n                .into())\n        }\n    }\n\n    /// Return the kind of typed array this is. This can be used in conjunction with\n    /// [`js_typed_array_from_kind`] to create a typed array of the same kind.\n    #[inline]\n    #[must_use]\n    pub fn kind(&self) -> Option<TypedArrayKind> {\n        self.inner\n            .downcast_ref::<TypedArray>()\n            .map(|arr| arr.kind())\n    }\n\n    /// Get the length of the array.\n    ///\n    /// Same as `array.length` in JavaScript.\n    ///\n    /// # Errors\n    ///\n    /// Returns an error if the length value is not a number.\n    #[inline]\n    pub fn length(&self, context: &mut Context) -> JsResult<usize> {\n        Ok(\n            BuiltinTypedArray::length(&self.inner.clone().into(), &[], context)?\n                .as_number()\n                .js_expect(\"length should return a number\")? as usize,\n        )\n    }\n\n    /// Check if the array is empty, i.e. the `length` is zero.\n    #[inline]\n    pub fn is_empty(&self, context: &mut Context) -> JsResult<bool> {\n        Ok(self.length(context)? == 0)\n    }\n\n    /// Calls `TypedArray.prototype.at()`.\n    pub fn at<T>(&self, index: T, context: &mut Context) -> JsResult<JsValue>\n    where\n        T: Into<i64>,\n    {\n        BuiltinTypedArray::at(&self.inner.clone().into(), &[index.into().into()], context)\n    }\n\n    /// Returns the `ArrayBuffer` referenced by this typed array at construction time.\n    ///\n    /// Calls `TypedArray.prototype.buffer()`.\n    ///\n    /// # Examples\n    ///\n    /// ```\n    /// # use boa_engine::{js_string, JsResult, object::{builtins::{JsUint8Array, JsArrayBuffer}}, property::{PropertyKey}, JsValue, Context};\n    /// # fn main() -> JsResult<()> {\n    ///\n    /// let context = &mut Context::default();\n    /// let array_buffer8 = JsArrayBuffer::new(8, context)?;\n    /// let array = JsUint8Array::from_array_buffer(array_buffer8, context)?;\n    /// assert_eq!(\n    ///     array.buffer(context)?.as_object().unwrap().get(PropertyKey::String(js_string!(\"byteLength\")), context).unwrap(),\n    ///     JsValue::new(8)\n    /// );\n    /// # Ok(())\n    /// # }\n    /// ```\n    #[inline]\n    pub fn buffer(&self, context: &mut Context) -> JsResult<JsValue> {\n        BuiltinTypedArray::buffer(&self.inner.clone().into(), &[], context)\n    }\n\n    /// Returns `TypedArray.prototype.byteLength`.\n    ///\n    /// # Errors\n    ///\n    /// Returns an error if the byteLength value is not a number.\n    #[inline]\n    pub fn byte_length(&self, context: &mut Context) -> JsResult<usize> {\n        Ok(\n            BuiltinTypedArray::byte_length(&self.inner.clone().into(), &[], context)?\n                .as_number()\n                .js_expect(\"byteLength should return a number\")? as usize,\n        )\n    }\n\n    /// Returns `TypedArray.prototype.byteOffset`.\n    ///\n    /// # Errors\n    ///\n    /// Returns an error if the byteOffset value is not a number.\n    #[inline]\n    pub fn byte_offset(&self, context: &mut Context) -> JsResult<usize> {\n        Ok(\n            BuiltinTypedArray::byte_offset(&self.inner.clone().into(), &[], context)?\n                .as_number()\n                .js_expect(\"byteLength should return a number\")? as usize,\n        )\n    }\n\n    /// Function that created the instance object. It is the hidden `TypedArray` constructor function,\n    /// but each typed array subclass also defines its own constructor property.\n    ///\n    /// Returns `TypedArray.prototype.constructor`.\n    ///\n    /// # Examples\n    ///\n    /// ```\n    /// # use boa_engine::{JsResult, object::{builtins::JsUint8Array}, JsNativeError, Context};\n    /// # fn main() -> JsResult<()> {\n    ///\n    /// let context = &mut Context::default();\n    /// let array = JsUint8Array::from_iter(vec![1, 2, 3, 4, 5], context)?;\n    /// assert_eq!(\n    ///     Err(JsNativeError::typ()\n    ///         .with_message(\"the TypedArray constructor should never be called directly\")\n    ///         .into()),\n    ///     array.constructor(context)\n    /// );\n    ///\n    /// # Ok(())\n    /// # }\n    /// ```\n    #[inline]\n    pub fn constructor(&self, context: &mut Context) -> JsResult<JsValue> {\n        BuiltinTypedArray::constructor(&self.inner.clone().into(), &[], context)\n    }\n\n    /// Shallow copies part of this typed array to another location in the same typed\n    /// array and returns this typed array without modifying its length.\n    ///\n    /// Returns `TypedArray.prototype.copyWithin()`.\n    ///\n    /// # Examples\n    ///\n    /// ```\n    /// # use boa_engine::{JsResult, JsValue, object::{builtins::{JsUint8Array}}, Context};\n    /// # fn main() -> JsResult<()> {\n    ///\n    /// let context = &mut Context::default();\n    /// let array = JsUint8Array::from_iter(vec![1u8, 2u8, 3u8, 4u8, 5u8, 6u8, 7u8, 8u8], context)?;\n    /// array.copy_within(3, 1, Some(3), context)?;\n    /// assert_eq!(array.get(0, context)?, JsValue::new(1.0));\n    /// assert_eq!(array.get(1, context)?, JsValue::new(2.0));\n    /// assert_eq!(array.get(2, context)?, JsValue::new(3.0));\n    /// assert_eq!(array.get(3, context)?, JsValue::new(2.0));\n    /// assert_eq!(array.get(4, context)?, JsValue::new(3.0));\n    /// assert_eq!(array.get(5, context)?, JsValue::new(6.0));\n    /// assert_eq!(array.get(6, context)?, JsValue::new(7.0));\n    /// assert_eq!(array.get(7, context)?, JsValue::new(8.0));\n    ///\n    /// # Ok(())\n    /// # }\n    /// ```\n    #[inline]\n    pub fn copy_within<T>(\n        &self,\n        target: T,\n        start: u64,\n        end: Option<u64>,\n        context: &mut Context,\n    ) -> JsResult<Self>\n    where\n        T: Into<JsValue>,\n    {\n        let object = BuiltinTypedArray::copy_within(\n            &self.inner.clone().into(),\n            &[target.into(), start.into(), end.into_or_undefined()],\n            context,\n        )?;\n\n        Ok(Self {\n            inner: object\n                .as_object()\n                .js_expect(\"`copyWithin` must always return a `TypedArray` on success\")?,\n        })\n    }\n\n    /// Calls `TypedArray.prototype.fill()`.\n    pub fn fill<T>(\n        &self,\n        value: T,\n        start: Option<usize>,\n        end: Option<usize>,\n        context: &mut Context,\n    ) -> JsResult<Self>\n    where\n        T: Into<JsValue>,\n    {\n        BuiltinTypedArray::fill(\n            &self.inner.clone().into(),\n            &[\n                value.into(),\n                start.into_or_undefined(),\n                end.into_or_undefined(),\n            ],\n            context,\n        )?;\n        Ok(self.clone())\n    }\n\n    /// Calls `TypedArray.prototype.every()`.\n    pub fn every(\n        &self,\n        predicate: JsFunction,\n        this_arg: Option<JsValue>,\n        context: &mut Context,\n    ) -> JsResult<bool> {\n        let result = BuiltinTypedArray::every(\n            &self.inner.clone().into(),\n            &[predicate.into(), this_arg.into_or_undefined()],\n            context,\n        )?\n        .as_boolean()\n        .js_expect(\"TypedArray.prototype.every should always return boolean\")?;\n\n        Ok(result)\n    }\n\n    /// Calls `TypedArray.prototype.some()`.\n    #[inline]\n    pub fn some(\n        &self,\n        callback: JsFunction,\n        this_arg: Option<JsValue>,\n        context: &mut Context,\n    ) -> JsResult<bool> {\n        let result = BuiltinTypedArray::some(\n            &self.inner.clone().into(),\n            &[callback.into(), this_arg.into_or_undefined()],\n            context,\n        )?\n        .as_boolean()\n        .js_expect(\"TypedArray.prototype.some should always return boolean\")?;\n\n        Ok(result)\n    }\n\n    /// Calls `TypedArray.prototype.sort()`.\n    #[inline]\n    pub fn sort(&self, compare_fn: Option<JsFunction>, context: &mut Context) -> JsResult<Self> {\n        BuiltinTypedArray::sort(\n            &self.inner.clone().into(),\n            &[compare_fn.into_or_undefined()],\n            context,\n        )?;\n\n        Ok(self.clone())\n    }\n\n    /// Returns a new typed array on the same `ArrayBuffer` store and with the same element\n    /// types as for this typed array.\n    /// The begin offset is inclusive and the end offset is exclusive.\n    ///\n    /// Calls `TypedArray.prototype.subarray()`.\n    ///\n    /// # Examples\n    ///\n    /// ```\n    /// # use boa_engine::{JsResult, object::{builtins::JsUint8Array}, JsValue, Context};\n    /// # fn main() -> JsResult<()> {\n    ///\n    /// let context = &mut Context::default();\n    /// let array = JsUint8Array::from_iter(vec![1u8, 2u8, 3u8, 4u8, 5u8, 6u8, 7u8, 8u8], context)?;\n    /// let subarray2_6 = array.subarray(2, 6, context)?;\n    /// assert_eq!(subarray2_6.length(context)?, 4);\n    /// assert_eq!(subarray2_6.get(0, context)?, JsValue::new(3.0));\n    /// assert_eq!(subarray2_6.get(1, context)?, JsValue::new(4.0));\n    /// assert_eq!(subarray2_6.get(2, context)?, JsValue::new(5.0));\n    /// assert_eq!(subarray2_6.get(3, context)?, JsValue::new(6.0));\n    /// let subarray4_6 = array.subarray(-4, 6, context)?;\n    /// assert_eq!(subarray4_6.length(context)?, 2);\n    /// assert_eq!(subarray4_6.get(0, context)?, JsValue::new(5.0));\n    /// assert_eq!(subarray4_6.get(1, context)?, JsValue::new(6.0));\n    ///\n    /// # Ok(())\n    /// # }\n    /// ```\n    #[inline]\n    pub fn subarray(&self, begin: i64, end: i64, context: &mut Context) -> JsResult<Self> {\n        let subarray = BuiltinTypedArray::subarray(\n            &self.inner.clone().into(),\n            &[begin.into(), end.into()],\n            context,\n        )?;\n\n        Ok(Self {\n            inner: subarray\n                .as_object()\n                .js_expect(\"`subarray` must always return a `TypedArray` on success\")?,\n        })\n    }\n\n    /// Calls `TypedArray.prototype.toLocaleString()`\n    #[inline]\n    pub fn to_locale_string(\n        &self,\n        reserved1: Option<JsValue>,\n        reserved2: Option<JsValue>,\n        context: &mut Context,\n    ) -> JsResult<JsValue> {\n        BuiltinTypedArray::to_locale_string(\n            &self.inner.clone().into(),\n            &[reserved1.into_or_undefined(), reserved2.into_or_undefined()],\n            context,\n        )\n    }\n\n    /// Calls `TypedArray.prototype.filter()`.\n    #[inline]\n    pub fn filter(\n        &self,\n        callback: JsFunction,\n        this_arg: Option<JsValue>,\n        context: &mut Context,\n    ) -> JsResult<Self> {\n        let object = BuiltinTypedArray::filter(\n            &self.inner.clone().into(),\n            &[callback.into(), this_arg.into_or_undefined()],\n            context,\n        )?;\n\n        Ok(Self {\n            inner: object\n                .as_object()\n                .js_expect(\"`filter` must always return a `TypedArray` on success\")?,\n        })\n    }\n\n    /// Calls `TypedArray.prototype.map()`.\n    #[inline]\n    pub fn map(\n        &self,\n        callback: JsFunction,\n        this_arg: Option<JsValue>,\n        context: &mut Context,\n    ) -> JsResult<Self> {\n        let object = BuiltinTypedArray::map(\n            &self.inner.clone().into(),\n            &[callback.into(), this_arg.into_or_undefined()],\n            context,\n        )?;\n\n        Ok(Self {\n            inner: object\n                .as_object()\n                .js_expect(\"`map` must always return a `TypedArray` on success\")?,\n        })\n    }\n\n    /// Calls `TypedArray.prototype.reduce()`.\n    #[inline]\n    pub fn reduce(\n        &self,\n        callback: JsFunction,\n        initial_value: Option<JsValue>,\n        context: &mut Context,\n    ) -> JsResult<JsValue> {\n        BuiltinTypedArray::reduce(\n            &self.inner.clone().into(),\n            &[callback.into(), initial_value.into_or_undefined()],\n            context,\n        )\n    }\n\n    /// Calls `TypedArray.prototype.reduceRight()`.\n    #[inline]\n    pub fn reduce_right(\n        &self,\n        callback: JsFunction,\n        initial_value: Option<JsValue>,\n        context: &mut Context,\n    ) -> JsResult<JsValue> {\n        BuiltinTypedArray::reduceright(\n            &self.inner.clone().into(),\n            &[callback.into(), initial_value.into_or_undefined()],\n            context,\n        )\n    }\n\n    /// Calls `TypedArray.prototype.reverse()`.\n    #[inline]\n    pub fn reverse(&self, context: &mut Context) -> JsResult<Self> {\n        BuiltinTypedArray::reverse(&self.inner.clone().into(), &[], context)?;\n        Ok(self.clone())\n    }\n\n    /// Stores multiple values in the typed array, reading input values from a specified array.\n    ///\n    /// Returns `TypedArray.prototype.set()`.\n    ///\n    ///\n    /// # Examples\n    ///\n    /// ```\n    /// # use boa_engine::{JsResult, object::{builtins::{JsUint8Array, JsArray, JsArrayBuffer}}, JsValue, Context};\n    /// # fn main() -> JsResult<()> {\n    ///\n    /// let context = &mut Context::default();\n    /// let array_buffer8 = JsArrayBuffer::new(8, context)?;\n    /// let initialized8_array = JsUint8Array::from_array_buffer(array_buffer8, context)?;\n    /// initialized8_array.set_values(\n    ///   JsArray::from_iter(vec![JsValue::new(1), JsValue::new(2)], context).into(),\n    ///   Some(3),\n    ///   context,\n    /// )?;\n    /// assert_eq!(initialized8_array.get(0, context)?, JsValue::new(0));\n    /// assert_eq!(initialized8_array.get(1, context)?, JsValue::new(0));\n    /// assert_eq!(initialized8_array.get(2, context)?, JsValue::new(0));\n    /// assert_eq!(initialized8_array.get(3, context)?, JsValue::new(1.0));\n    /// assert_eq!(initialized8_array.get(4, context)?, JsValue::new(2.0));\n    /// assert_eq!(initialized8_array.get(5, context)?, JsValue::new(0));\n    /// assert_eq!(initialized8_array.get(6, context)?, JsValue::new(0));\n    /// assert_eq!(initialized8_array.get(7, context)?, JsValue::new(0));\n    /// assert_eq!(initialized8_array.get(8, context)?, JsValue::undefined());\n    ///\n    /// # Ok(())\n    /// # }\n    /// ```\n    #[inline]\n    pub fn set_values(\n        &self,\n        source: JsValue,\n        offset: Option<u64>,\n        context: &mut Context,\n    ) -> JsResult<JsValue> {\n        BuiltinTypedArray::set(\n            &self.inner.clone().into(),\n            &[source, offset.into_or_undefined()],\n            context,\n        )\n    }\n\n    /// Calls `TypedArray.prototype.slice()`.\n    #[inline]\n    pub fn slice(\n        &self,\n        start: Option<usize>,\n        end: Option<usize>,\n        context: &mut Context,\n    ) -> JsResult<Self> {\n        let object = BuiltinTypedArray::slice(\n            &self.inner.clone().into(),\n            &[start.into_or_undefined(), end.into_or_undefined()],\n            context,\n        )?;\n\n        Ok(Self {\n            inner: object\n                .as_object()\n                .js_expect(\"`slice` must always return a `TypedArray` on success\")?,\n        })\n    }\n\n    /// Calls `TypedArray.prototype.find()`.\n    #[inline]\n    pub fn find(\n        &self,\n        predicate: JsFunction,\n        this_arg: Option<JsValue>,\n        context: &mut Context,\n    ) -> JsResult<JsValue> {\n        BuiltinTypedArray::find(\n            &self.inner.clone().into(),\n            &[predicate.into(), this_arg.into_or_undefined()],\n            context,\n        )\n    }\n\n    /// Returns the index of the first element in an array that satisfies the\n    /// provided testing function.\n    /// If no elements satisfy the testing function, `JsResult::Ok(None)` is returned.\n    ///\n    /// Calls `TypedArray.prototype.findIndex()`.\n    ///\n    /// # Examples\n    ///\n    /// ```\n    /// # use boa_engine::{JsResult, object::{builtins::JsUint8Array, FunctionObjectBuilder}, NativeFunction, JsValue, Context};\n    /// # fn main() -> JsResult<()> {\n    /// let context = &mut Context::default();\n    /// let data: Vec<u8> = (0..=255).collect();\n    /// let array = JsUint8Array::from_iter(data, context)?;\n    ///\n    /// let greater_than_10_predicate = FunctionObjectBuilder::new(\n    ///     context.realm(),\n    ///     NativeFunction::from_fn_ptr(|_this, args, _context| {\n    ///         let element = args\n    ///             .first()\n    ///             .cloned()\n    ///             .unwrap_or_default()\n    ///             .as_number()\n    ///             .expect(\"error at number conversion\");\n    ///         Ok(JsValue::from(element > 10.0))\n    ///     }),\n    /// )\n    /// .build();\n    /// assert_eq!(\n    ///     array.find_index(greater_than_10_predicate, None, context),\n    ///     Ok(Some(11))\n    /// );\n    ///\n    /// # Ok(())\n    /// # }\n    /// ```\n    #[inline]\n    pub fn find_index(\n        &self,\n        predicate: JsFunction,\n        this_arg: Option<JsValue>,\n        context: &mut Context,\n    ) -> JsResult<Option<u64>> {\n        let index = BuiltinTypedArray::find_index(\n            &self.inner.clone().into(),\n            &[predicate.into(), this_arg.into_or_undefined()],\n            context,\n        )?\n        .as_number()\n        .js_expect(\"TypedArray.prototype.findIndex() should always return number\")?;\n\n        if index >= 0.0 {\n            Ok(Some(index as u64))\n        } else {\n            Ok(None)\n        }\n    }\n\n    /// Iterates the typed array in reverse order and returns the value of\n    /// the first element that satisfies the provided testing function.\n    /// If no elements satisfy the testing function, `JsResult::Ok(None)` is returned.\n    ///\n    /// Calls `TypedArray.prototype.findLast()`.\n    ///\n    /// # Examples\n    ///\n    /// ```\n    /// # use boa_engine::{JsResult, object::{builtins::JsUint8Array, FunctionObjectBuilder}, NativeFunction, JsValue, Context};\n    /// # fn main() -> JsResult<()> {\n    /// let context = &mut Context::default();\n    /// let data: Vec<u8> = (0..=255).collect();\n    /// let array = JsUint8Array::from_iter(data, context)?;\n    ///\n    /// let lower_than_200_predicate = FunctionObjectBuilder::new(\n    ///     context.realm(),\n    ///     NativeFunction::from_fn_ptr(|_this, args, _context| {\n    ///         let element = args\n    ///             .first()\n    ///             .cloned()\n    ///             .unwrap_or_default()\n    ///             .as_number()\n    ///             .expect(\"error at number conversion\");\n    ///         Ok(JsValue::from(element < 200.0))\n    ///     }),\n    /// )\n    /// .build();\n    /// assert_eq!(\n    ///     array.find_last(lower_than_200_predicate.clone(), None, context),\n    ///     Ok(JsValue::new(199))\n    /// );\n    ///\n    /// # Ok(())\n    /// # }\n    /// ```\n    #[inline]\n    pub fn find_last(\n        &self,\n        predicate: JsFunction,\n        this_arg: Option<JsValue>,\n        context: &mut Context,\n    ) -> JsResult<JsValue> {\n        BuiltinTypedArray::find_last(\n            &self.inner.clone().into(),\n            &[predicate.into(), this_arg.into_or_undefined()],\n            context,\n        )\n    }\n\n    /// Iterates the typed array in reverse order and returns the index of\n    /// the first element that satisfies the provided testing function.\n    /// If no elements satisfy the testing function, `JsResult::OK(None)` is returned.\n    ///\n    /// Calls `TypedArray.prototype.findLastIndex()`.\n    ///\n    /// # Examples\n    ///\n    /// ```\n    /// # use boa_engine::{JsResult, object::{builtins::JsUint8Array, FunctionObjectBuilder}, NativeFunction, JsValue, Context};\n    /// # fn main() -> JsResult<()> {\n    /// let context = &mut Context::default();\n    /// let data: Vec<u8> = (0..=255).collect();\n    /// let array = JsUint8Array::from_iter(data, context)?;\n    ///\n    /// let lower_than_200_predicate = FunctionObjectBuilder::new(\n    ///     context.realm(),\n    ///     NativeFunction::from_fn_ptr(|_this, args, _context| {\n    ///         let element = args\n    ///             .first()\n    ///             .cloned()\n    ///             .unwrap_or_default()\n    ///             .as_number()\n    ///             .expect(\"error at number conversion\");\n    ///         Ok(JsValue::from(element < 200.0))\n    ///     }),\n    /// )\n    /// .build();\n    /// assert_eq!(\n    ///     array.find_last(lower_than_200_predicate.clone(), None, context),\n    ///     Ok(JsValue::new(199))\n    /// );\n    ///\n    /// # Ok(())\n    /// # }\n    /// ```\n    #[inline]\n    pub fn find_last_index(\n        &self,\n        predicate: JsFunction,\n        this_arg: Option<JsValue>,\n        context: &mut Context,\n    ) -> JsResult<Option<u64>> {\n        let index = BuiltinTypedArray::find_last_index(\n            &self.inner.clone().into(),\n            &[predicate.into(), this_arg.into_or_undefined()],\n            context,\n        )?\n        .as_number()\n        .js_expect(\"TypedArray.prototype.findLastIndex() should always return number\")?;\n\n        if index >= 0.0 {\n            Ok(Some(index as u64))\n        } else {\n            Ok(None)\n        }\n    }\n\n    /// Executes a provided function once for each typed array element.\n    ///\n    /// Calls `TypedArray.prototype.forEach()`.\n    ///\n    /// # Examples\n    ///\n    /// ```\n    /// # use boa_gc::{Gc, GcRefCell};\n    /// # use boa_engine::{JsResult, object::{builtins::JsUint8Array, FunctionObjectBuilder}, NativeFunction, JsValue, Context};\n    /// # fn main() -> JsResult<()> {\n    /// let context = &mut Context::default();\n    /// let array = JsUint8Array::from_iter(vec![1, 2, 3, 4, 5], context)?;\n    /// let num_to_modify = Gc::new(GcRefCell::new(0u8));\n    ///\n    /// let js_function = FunctionObjectBuilder::new(\n    ///     context.realm(),\n    ///     NativeFunction::from_copy_closure_with_captures(\n    ///         |_, args, captures, inner_context| {\n    ///             let element = args\n    ///                 .first()\n    ///                 .cloned()\n    ///                 .unwrap_or_default()\n    ///                 .to_uint8(inner_context)\n    ///                 .expect(\"error at number conversion\");\n    ///             *captures.borrow_mut() += element;\n    ///             Ok(JsValue::undefined())\n    ///         },\n    ///         Gc::clone(&num_to_modify),\n    ///     ),\n    /// )\n    /// .build();\n    ///\n    /// array.for_each(js_function, None, context);\n    /// let borrow = *num_to_modify.borrow();\n    /// assert_eq!(borrow, 15u8);\n    ///\n    /// # Ok(())\n    /// # }\n    /// ```\n    #[inline]\n    pub fn for_each(\n        &self,\n        callback: JsFunction,\n        this_arg: Option<JsValue>,\n        context: &mut Context,\n    ) -> JsResult<JsValue> {\n        BuiltinTypedArray::for_each(\n            &self.inner.clone().into(),\n            &[callback.into(), this_arg.into_or_undefined()],\n            context,\n        )\n    }\n\n    /// Determines whether a typed array includes a certain value among its entries,\n    /// returning true or false as appropriate.\n    ///\n    /// Calls `TypedArray.prototype.includes()`.\n    ///\n    /// # Examples\n    ///\n    /// ```\n    /// # use boa_engine::{JsResult, object::{builtins::JsUint8Array}, JsValue, Context};\n    /// # fn main() -> JsResult<()> {\n    ///\n    /// let context = &mut Context::default();\n    /// let data: Vec<u8> = (0..=255).collect();\n    /// let array = JsUint8Array::from_iter(data, context)?;\n    ///\n    /// assert_eq!(array.includes(JsValue::new(2), None, context), Ok(true));\n    /// let empty_array = JsUint8Array::from_iter(vec![], context)?;\n    /// assert_eq!(\n    ///     empty_array.includes(JsValue::new(2), None, context),\n    ///     Ok(false)\n    /// );\n    ///\n    /// # Ok(())\n    /// # }\n    /// ```\n    #[inline]\n    pub fn includes<T>(\n        &self,\n        search_element: T,\n        from_index: Option<u64>,\n        context: &mut Context,\n    ) -> JsResult<bool>\n    where\n        T: Into<JsValue>,\n    {\n        let result = BuiltinTypedArray::includes(\n            &self.inner.clone().into(),\n            &[search_element.into(), from_index.into_or_undefined()],\n            context,\n        )?\n        .as_boolean()\n        .js_expect(\"TypedArray.prototype.includes should always return boolean\")?;\n\n        Ok(result)\n    }\n\n    /// Calls `TypedArray.prototype.indexOf()`.\n    pub fn index_of<T>(\n        &self,\n        search_element: T,\n        from_index: Option<usize>,\n        context: &mut Context,\n    ) -> JsResult<Option<usize>>\n    where\n        T: Into<JsValue>,\n    {\n        let index = BuiltinTypedArray::index_of(\n            &self.inner.clone().into(),\n            &[search_element.into(), from_index.into_or_undefined()],\n            context,\n        )?\n        .as_number()\n        .js_expect(\"TypedArray.prototype.indexOf should always return number\")?;\n\n        #[allow(clippy::float_cmp)]\n        if index == -1.0 {\n            Ok(None)\n        } else {\n            Ok(Some(index as usize))\n        }\n    }\n\n    /// Calls `TypedArray.prototype.lastIndexOf()`.\n    pub fn last_index_of<T>(\n        &self,\n        search_element: T,\n        from_index: Option<usize>,\n        context: &mut Context,\n    ) -> JsResult<Option<usize>>\n    where\n        T: Into<JsValue>,\n    {\n        let index = BuiltinTypedArray::last_index_of(\n            &self.inner.clone().into(),\n            &[search_element.into(), from_index.into_or_undefined()],\n            context,\n        )?\n        .as_number()\n        .js_expect(\"TypedArray.prototype.lastIndexOf should always return number\")?;\n\n        #[allow(clippy::float_cmp)]\n        if index == -1.0 {\n            Ok(None)\n        } else {\n            Ok(Some(index as usize))\n        }\n    }\n\n    /// Calls `TypedArray.prototype.join()`.\n    #[inline]\n    pub fn join(&self, separator: Option<JsString>, context: &mut Context) -> JsResult<JsString> {\n        BuiltinTypedArray::join(\n            &self.inner.clone().into(),\n            &[separator.into_or_undefined()],\n            context,\n        )\n        .and_then(|x| {\n            x.as_string()\n                .js_expect(\"TypedArray.prototype.join always returns string\")\n                .map_err(Into::into)\n        })\n    }\n\n    /// Calls `TypedArray.prototype.toReversed ( )`.\n    #[inline]\n    pub fn to_reversed(&self, context: &mut Context) -> JsResult<Self> {\n        let array = BuiltinTypedArray::to_reversed(&self.inner.clone().into(), &[], context)?;\n\n        Ok(Self {\n            inner: array\n                .as_object()\n                .js_expect(\"`to_reversed` must always return a `TypedArray` on success\")?,\n        })\n    }\n\n    /// Calls `TypedArray.prototype.toSorted ( comparefn )`.\n    #[inline]\n    pub fn to_sorted(\n        &self,\n        compare_fn: Option<JsFunction>,\n        context: &mut Context,\n    ) -> JsResult<Self> {\n        let array = BuiltinTypedArray::to_sorted(\n            &self.inner.clone().into(),\n            &[compare_fn.into_or_undefined()],\n            context,\n        )?;\n\n        Ok(Self {\n            inner: array\n                .as_object()\n                .js_expect(\"`to_sorted` must always return a `TypedArray` on success\")?,\n        })\n    }\n\n    /// Calls `TypedArray.prototype.with ( index, value )`.\n    #[inline]\n    pub fn with(&self, index: u64, value: JsValue, context: &mut Context) -> JsResult<Self> {\n        let array =\n            BuiltinTypedArray::with(&self.inner.clone().into(), &[index.into(), value], context)?;\n\n        Ok(Self {\n            inner: array\n                .as_object()\n                .js_expect(\"`with` must always return a `TypedArray` on success\")?,\n        })\n    }\n\n    /// Calls `TypedArray.prototype.entries()`.\n    #[inline]\n    pub fn entries(&self, context: &mut Context) -> JsResult<JsValue> {\n        BuiltinTypedArray::entries(&self.inner.clone().into(), &[], context)\n    }\n\n    /// Calls `TypedArray.prototype.keys()`.\n    #[inline]\n    pub fn keys(&self, context: &mut Context) -> JsResult<JsValue> {\n        BuiltinTypedArray::keys(&self.inner.clone().into(), &[], context)\n    }\n\n    /// Calls `TypedArray.prototype.values()`.\n    #[inline]\n    pub fn values(&self, context: &mut Context) -> JsResult<JsValue> {\n        BuiltinTypedArray::values(&self.inner.clone().into(), &[], context)\n    }\n\n    /// Calls `TypedArray.prototype[@@iterator]()`.\n    #[inline]\n    pub fn iterator(&self, context: &mut Context) -> JsResult<JsValue> {\n        BuiltinTypedArray::values(&self.inner.clone().into(), &[], context)\n    }\n\n    /// Calls `TypedArray.prototype.toString()`.\n    #[inline]\n    pub fn to_string(&self, context: &mut Context) -> JsResult<JsString> {\n        // TypedArray.prototype.toString is the same as Array.prototype.toString\n        let result = crate::builtins::Array::to_string(&self.inner.clone().into(), &[], context)?;\n        result\n            .as_string()\n            .js_expect(\"Array.prototype.toString always returns string\")\n            .map_err(Into::into)\n    }\n\n    /// It is a getter that returns the same string as the typed array constructor's name.\n    /// It returns `Ok(JsValue::Undefined)` if the this value is not one of the typed array subclasses.\n    ///\n    /// Returns `TypedArray.prototype.toStringTag()`.\n    ///\n    /// # Examples\n    ///\n    /// ```\n    /// # use boa_engine::{JsResult, js_string, object::{builtins::{JsUint8Array}}, Context};\n    /// # fn main() -> JsResult<()> {\n    ///\n    /// let context = &mut Context::default();\n    /// let array = JsUint8Array::from_iter(vec![1u8, 2u8, 3u8, 4u8, 5u8, 6u8, 7u8, 8u8], context)?;\n    /// let tag = array.to_string_tag(context)?.to_string(context)?;\n    /// assert_eq!(tag, js_string!(\"Uint8Array\"));\n    ///\n    /// # Ok(())\n    /// # }\n    /// ```\n    #[inline]\n    pub fn to_string_tag(&self, context: &mut Context) -> JsResult<JsValue> {\n        BuiltinTypedArray::to_string_tag(&self.inner.clone().into(), &[], context)\n    }\n}\n\nimpl From<JsTypedArray> for JsObject {\n    #[inline]\n    fn from(o: JsTypedArray) -> Self {\n        o.inner.clone()\n    }\n}\n\nimpl From<JsTypedArray> for JsValue {\n    #[inline]\n    fn from(o: JsTypedArray) -> Self {\n        o.inner.clone().into()\n    }\n}\n\nimpl Deref for JsTypedArray {\n    type Target = JsObject;\n\n    #[inline]\n    fn deref(&self) -> &Self::Target {\n        &self.inner\n    }\n}\n\nimpl TryFromJs for JsTypedArray {\n    fn try_from_js(value: &JsValue, _context: &mut Context) -> JsResult<Self> {\n        if let Some(o) = value.as_object() {\n            Self::from_object(o.clone())\n        } else {\n            Err(JsNativeError::typ()\n                .with_message(\"value is not a TypedArray object\")\n                .into())\n        }\n    }\n}\n\nmacro_rules! JsTypedArrayType {\n    (\n        $name:ident,\n        $constructor_function:ident,\n        $kind:expr,\n        $constructor_object:ident,\n        $value_to_elem:ident,\n        $element:ty\n    ) => {\n\n        #[doc = concat!(\n            \"`\", stringify!($name),\n            \"` provides a wrapper for Boa's implementation of the ECMAScript `\",\n            stringify!($constructor_function) ,\"` builtin object.\"\n        )]\n        #[derive(Debug, Clone, Trace, Finalize)]\n        pub struct $name {\n            inner: JsTypedArray,\n        }\n\n        impl $name {\n            #[doc = concat!(\"Creates a `\", stringify!($name),\n                \"` using a [`JsObject`]. It will make sure that the object is of the correct kind.\"\n            )]\n            #[inline]\n            pub fn from_object(object: JsObject) -> JsResult<Self> {\n                let is_kind = if let Some(int) = object.downcast_ref::<TypedArray>() {\n                    int.kind() == $kind\n                } else {\n                    false\n                };\n                if is_kind {\n                    Ok(Self {\n                        inner: JsTypedArray {\n                            inner: object.into(),\n                        },\n                    })\n                } else {\n                    Err(JsNativeError::typ()\n                        .with_message(\"object is not a TypedArray\")\n                        .into())\n                }\n            }\n\n            /// Create the typed array from a [`JsArrayBuffer`].\n            pub fn from_array_buffer(\n                array_buffer: JsArrayBuffer,\n                context: &mut Context,\n            ) -> JsResult<Self> {\n                let new_target = context\n                    .intrinsics()\n                    .constructors()\n                    .$constructor_object()\n                    .constructor()\n                    .into();\n                let object = crate::builtins::typed_array::$constructor_function::constructor(\n                    &new_target,\n                    &[array_buffer.into()],\n                    context,\n                )?\n                .as_object()\n                .js_expect(\"object\")?\n                .clone();\n\n                Ok(Self {\n                    inner: JsTypedArray {\n                        inner: object.into(),\n                    },\n                })\n            }\n\n            /// Create the typed array from a [`JsSharedArrayBuffer`].\n            pub fn from_shared_array_buffer(\n                buffer: JsSharedArrayBuffer,\n                context: &mut Context,\n            ) -> JsResult<Self> {\n                let new_target = context\n                    .intrinsics()\n                    .constructors()\n                    .$constructor_object()\n                    .constructor()\n                    .into();\n                let object = crate::builtins::typed_array::$constructor_function::constructor(\n                    &new_target,\n                    &[buffer.into()],\n                    context,\n                )?\n                .as_object()\n                .js_expect(\"object\")?\n                .clone();\n\n                Ok(Self {\n                    inner: JsTypedArray {\n                        inner: object.into(),\n                    },\n                })\n            }\n\n            /// Create the typed array from an iterator.\n            pub fn from_iter<I>(elements: I, context: &mut Context) -> JsResult<Self>\n            where\n                I: IntoIterator<Item = $element>,\n            {\n                let bytes = AlignedVec::from_iter(\n                    0,\n                    elements\n                    .into_iter()\n                    .flat_map(<$element>::to_ne_bytes)\n                );\n                let array_buffer = JsArrayBuffer::from_byte_block(bytes, context)?;\n                let new_target = context\n                    .intrinsics()\n                    .constructors()\n                    .$constructor_object()\n                    .constructor()\n                    .into();\n                let object = crate::builtins::typed_array::$constructor_function::constructor(\n                    &new_target,\n                    &[array_buffer.into()],\n                    context,\n                )?\n                .as_object()\n                .js_expect(\"object\")?\n                .clone();\n\n                Ok(Self {\n                    inner: JsTypedArray {\n                        inner: object.into(),\n                    },\n                })\n            }\n\n            /// Create an iterator over the typed array's elements.\n            pub fn iter<'a>(&'a self, context: &'a mut Context) -> impl Iterator<Item = $element> + 'a {\n                let length = self.length(context).unwrap_or(0);\n                let mut index = 0;\n                std::iter::from_fn(move || {\n                    if index < length {\n                        let value = self.get(index, context).ok()?;\n                        index += 1;\n                        value.$value_to_elem(context).ok()\n                    } else {\n                        None\n                    }\n                })\n            }\n        }\n\n        impl From<$name> for JsObject {\n            #[inline]\n            fn from(o: $name) -> Self {\n                o.inner\n                    .inner\n                    .clone()\n            }\n        }\n\n        impl From<$name> for JsValue {\n            #[inline]\n            fn from(o: $name) -> Self {\n                o.inner.inner.clone().into()\n            }\n        }\n\n        impl Deref for $name {\n            type Target = JsTypedArray;\n\n            #[inline]\n            fn deref(&self) -> &Self::Target {\n                &self.inner\n            }\n        }\n\n        impl TryFromJs for $name {\n            fn try_from_js(value: &JsValue, _context: &mut Context) -> JsResult<Self> {\n                if let Some(o) = value.as_object() {\n                    Self::from_object(o.clone())\n                } else {\n                    Err(JsNativeError::typ()\n                        .with_message(concat!(\n                            \"value is not a \",\n                            stringify!($constructor_function),\n                            \" object\"\n                        ))\n                        .into())\n                }\n            }\n        }\n    };\n}\n\nJsTypedArrayType!(\n    JsUint8Array,\n    Uint8Array,\n    TypedArrayKind::Uint8,\n    typed_uint8_array,\n    to_uint8,\n    u8\n);\n\nimpl JsUint8Array {\n    /// Copies the viewed byte range of this `Uint8Array` into a new `Vec<u8>`.\n    ///\n    /// Works with both `ArrayBuffer` and `SharedArrayBuffer` backing storage. The buffer must not be\n    /// detached; otherwise a `TypeError` is returned.\n    ///\n    /// # Example\n    ///\n    /// ```\n    /// # use boa_engine::{Context, JsResult, object::builtins::JsUint8Array};\n    /// # fn main() -> JsResult<()> {\n    ///\n    /// let context = &mut Context::default();\n    /// let data: Vec<u8> = (0..=255).collect();\n    /// let array = JsUint8Array::from_iter(data.clone(), context)?;\n    /// let bytes = array.to_vec(context)?;\n    /// assert_eq!(bytes, data);\n    ///\n    /// # Ok(())\n    /// # }\n    /// ```\n    #[inline]\n    pub fn to_vec(&self, _context: &mut Context) -> JsResult<Vec<u8>> {\n        let this_val = self.inner.inner.clone().into();\n        let (obj, buf_byte_len) = TypedArray::validate(&this_val, Ordering::SeqCst)?;\n        let vec = {\n            let array = obj.borrow();\n            let ta = array.data();\n            let buffer = ta.viewed_array_buffer().as_buffer();\n            let Some(slice) = buffer.bytes(Ordering::SeqCst) else {\n                return Err(JsNativeError::typ()\n                    .with_message(\"typed array buffer is detached or out of bounds\")\n                    .into());\n            };\n            if ta.is_out_of_bounds(slice.len()) {\n                return Err(JsNativeError::typ()\n                    .with_message(\"typed array is outside the bounds of its inner buffer\")\n                    .into());\n            }\n            let byte_offset = ta.byte_offset() as usize;\n            let byte_len = ta.byte_length(buf_byte_len) as usize;\n            slice.subslice(byte_offset..byte_offset + byte_len).to_vec()\n        };\n        Ok(vec)\n    }\n}\n\nJsTypedArrayType!(\n    JsUint8ClampedArray,\n    Uint8ClampedArray,\n    TypedArrayKind::Uint8Clamped,\n    typed_uint8clamped_array,\n    to_uint8_clamp,\n    u8\n);\nJsTypedArrayType!(\n    JsInt8Array,\n    Int8Array,\n    TypedArrayKind::Int8,\n    typed_int8_array,\n    to_int8,\n    i8\n);\nJsTypedArrayType!(\n    JsUint16Array,\n    Uint16Array,\n    TypedArrayKind::Uint16,\n    typed_uint16_array,\n    to_uint16,\n    u16\n);\nJsTypedArrayType!(\n    JsInt16Array,\n    Int16Array,\n    TypedArrayKind::Int16,\n    typed_int16_array,\n    to_int16,\n    i16\n);\nJsTypedArrayType!(\n    JsUint32Array,\n    Uint32Array,\n    TypedArrayKind::Uint32,\n    typed_uint32_array,\n    to_u32,\n    u32\n);\nJsTypedArrayType!(\n    JsInt32Array,\n    Int32Array,\n    TypedArrayKind::Int32,\n    typed_int32_array,\n    to_i32,\n    i32\n);\n#[cfg(feature = \"float16\")]\nJsTypedArrayType!(\n    JsFloat16Array,\n    Float16Array,\n    TypedArrayKind::Float16,\n    typed_float16_array,\n    to_f16,\n    float16::f16\n);\nJsTypedArrayType!(\n    JsBigInt64Array,\n    BigInt64Array,\n    TypedArrayKind::BigInt64,\n    typed_bigint64_array,\n    to_big_int64,\n    i64\n);\nJsTypedArrayType!(\n    JsBigUint64Array,\n    BigUint64Array,\n    TypedArrayKind::BigUint64,\n    typed_biguint64_array,\n    to_big_uint64,\n    u64\n);\nJsTypedArrayType!(\n    JsFloat32Array,\n    Float32Array,\n    TypedArrayKind::Float32,\n    typed_float32_array,\n    to_f32,\n    f32\n);\nJsTypedArrayType!(\n    JsFloat64Array,\n    Float64Array,\n    TypedArrayKind::Float64,\n    typed_float64_array,\n    to_number,\n    f64\n);\n\n/// Create a [`JsTypedArray`] from a [`TypedArrayKind`] and a backing [`JsArrayBuffer`].\n#[inline]\npub fn js_typed_array_from_kind(\n    kind: TypedArrayKind,\n    inner: JsArrayBuffer,\n    context: &mut Context,\n) -> JsResult<JsValue> {\n    match kind {\n        TypedArrayKind::Int8 => JsInt8Array::from_array_buffer(inner, context).map(Into::into),\n        TypedArrayKind::Uint8 => JsUint8Array::from_array_buffer(inner, context).map(Into::into),\n        TypedArrayKind::Uint8Clamped => {\n            JsUint8ClampedArray::from_array_buffer(inner, context).map(Into::into)\n        }\n        TypedArrayKind::Int16 => JsInt16Array::from_array_buffer(inner, context).map(Into::into),\n        TypedArrayKind::Uint16 => JsUint16Array::from_array_buffer(inner, context).map(Into::into),\n        TypedArrayKind::Int32 => JsInt32Array::from_array_buffer(inner, context).map(Into::into),\n        TypedArrayKind::Uint32 => JsUint32Array::from_array_buffer(inner, context).map(Into::into),\n        TypedArrayKind::BigInt64 => {\n            JsBigInt64Array::from_array_buffer(inner, context).map(Into::into)\n        }\n        TypedArrayKind::BigUint64 => {\n            JsBigUint64Array::from_array_buffer(inner, context).map(Into::into)\n        }\n        #[cfg(feature = \"float16\")]\n        TypedArrayKind::Float16 => {\n            JsFloat16Array::from_array_buffer(inner, context).map(Into::into)\n        }\n        TypedArrayKind::Float32 => {\n            JsFloat32Array::from_array_buffer(inner, context).map(Into::into)\n        }\n        TypedArrayKind::Float64 => {\n            JsFloat64Array::from_array_buffer(inner, context).map(Into::into)\n        }\n    }\n}\n\n#[test]\nfn typed_iterators_uint8() {\n    let context = &mut Context::default();\n    let vec = vec![1u8, 2, 3, 4, 5, 6, 7, 8];\n\n    let array = JsUint8Array::from_iter(vec.clone(), context).unwrap();\n    let vec2 = array.iter(context).collect::<Vec<_>>();\n    assert_eq!(vec, vec2);\n}\n\n#[test]\nfn uint8_array_to_vec_roundtrip() {\n    let context = &mut Context::default();\n    let data: Vec<u8> = (0..=255).collect();\n    let array = JsUint8Array::from_iter(data.clone(), context).unwrap();\n    let bytes = array.to_vec(context).unwrap();\n    assert_eq!(bytes, data);\n}\n\n#[test]\nfn typed_iterators_uint32() {\n    let context = &mut Context::default();\n    let vec = vec![1u32, 2, 0xFFFF, 4, 0xFF12_3456, 6, 7, 8];\n\n    let array = JsUint32Array::from_iter(vec.clone(), context).unwrap();\n    let vec2 = array.iter(context).collect::<Vec<_>>();\n    assert_eq!(vec, vec2);\n}\n\n#[test]\nfn typed_iterators_f32() {\n    let context = &mut Context::default();\n    let vec = vec![0.1f32, 0.2, 0.3, 0.4, 1.1, 9.99999];\n\n    let array = JsFloat32Array::from_iter(vec.clone(), context).unwrap();\n    let vec2 = array.iter(context).collect::<Vec<_>>();\n    assert_eq!(vec, vec2);\n}\n\n#[test]\nfn typed_array_to_string() {\n    let context = &mut Context::default();\n    let vec = vec![1u8, 2, 3];\n    let array = JsUint8Array::from_iter(vec, context).unwrap();\n    assert_eq!(\n        array.to_string(context).unwrap(),\n        crate::js_string!(\"1,2,3\")\n    );\n}\n\n#[test]\nfn typed_array_entries() {\n    let context = &mut Context::default();\n    let vec = vec![1u8, 2];\n    let array = JsUint8Array::from_iter(vec, context).unwrap();\n    let entries = array.entries(context).unwrap();\n    let mut entries_vec = Vec::new();\n    let next_str = crate::js_string!(\"next\");\n    loop {\n        let next_fn = entries\n            .as_object()\n            .unwrap()\n            .get(next_str.clone(), context)\n            .unwrap();\n        let result = next_fn\n            .as_object()\n            .unwrap()\n            .call(&entries, &[], context)\n            .unwrap();\n        if result\n            .as_object()\n            .unwrap()\n            .get(crate::js_string!(\"done\"), context)\n            .unwrap()\n            .to_boolean()\n        {\n            break;\n        }\n        entries_vec.push(\n            result\n                .as_object()\n                .unwrap()\n                .get(crate::js_string!(\"value\"), context)\n                .unwrap(),\n        );\n    }\n    assert_eq!(entries_vec.len(), 2);\n}\n\n#[test]\nfn typed_array_keys() {\n    let context = &mut Context::default();\n    let vec = vec![1u8, 2];\n    let array = JsUint8Array::from_iter(vec, context).unwrap();\n    let keys = array.keys(context).unwrap();\n    let mut keys_vec = Vec::new();\n    let next_str = crate::js_string!(\"next\");\n    loop {\n        let next_fn = keys\n            .as_object()\n            .unwrap()\n            .get(next_str.clone(), context)\n            .unwrap();\n        let result = next_fn\n            .as_object()\n            .unwrap()\n            .call(&keys, &[], context)\n            .unwrap();\n        if result\n            .as_object()\n            .unwrap()\n            .get(crate::js_string!(\"done\"), context)\n            .unwrap()\n            .to_boolean()\n        {\n            break;\n        }\n        keys_vec.push(\n            result\n                .as_object()\n                .unwrap()\n                .get(crate::js_string!(\"value\"), context)\n                .unwrap(),\n        );\n    }\n    assert_eq!(keys_vec, vec![JsValue::new(0), JsValue::new(1)]);\n}\n\n#[test]\nfn typed_array_values() {\n    let context = &mut Context::default();\n    let vec = vec![1u8, 2];\n    let array = JsUint8Array::from_iter(vec, context).unwrap();\n    let values = array.values(context).unwrap();\n    let mut values_vec = Vec::new();\n    let next_str = crate::js_string!(\"next\");\n    loop {\n        let next_fn = values\n            .as_object()\n            .unwrap()\n            .get(next_str.clone(), context)\n            .unwrap();\n        let result = next_fn\n            .as_object()\n            .unwrap()\n            .call(&values, &[], context)\n            .unwrap();\n        if result\n            .as_object()\n            .unwrap()\n            .get(crate::js_string!(\"done\"), context)\n            .unwrap()\n            .to_boolean()\n        {\n            break;\n        }\n        values_vec.push(\n            result\n                .as_object()\n                .unwrap()\n                .get(crate::js_string!(\"value\"), context)\n                .unwrap(),\n        );\n    }\n    assert_eq!(values_vec, vec![JsValue::new(1), JsValue::new(2)]);\n}\n\n#[test]\nfn typed_array_iterator() {\n    let context = &mut Context::default();\n    let array = JsUint8Array::from_iter(vec![1u8, 2], context).unwrap();\n    let values = array.iterator(context).unwrap();\n    let mut values_vec = Vec::new();\n    let next_str = crate::js_string!(\"next\");\n    loop {\n        let next_fn = values\n            .as_object()\n            .unwrap()\n            .get(next_str.clone(), context)\n            .unwrap();\n        let result = next_fn\n            .as_object()\n            .unwrap()\n            .call(&values, &[], context)\n            .unwrap();\n        if result\n            .as_object()\n            .unwrap()\n            .get(crate::js_string!(\"done\"), context)\n            .unwrap()\n            .to_boolean()\n        {\n            break;\n        }\n        values_vec.push(\n            result\n                .as_object()\n                .unwrap()\n                .get(crate::js_string!(\"value\"), context)\n                .unwrap(),\n        );\n    }\n    assert_eq!(values_vec, vec![JsValue::new(1), JsValue::new(2)]);\n}\n\n#[test]\nfn typed_array_to_reversed() {\n    let context = &mut Context::default();\n    let array = JsUint8Array::from_iter(vec![3u8, 1, 2], context).unwrap();\n\n    let reversed = array.to_reversed(context).unwrap();\n\n    // New array has reversed order\n    assert_eq!(reversed.at(0i64, context).unwrap(), JsValue::new(2));\n    assert_eq!(reversed.at(1i64, context).unwrap(), JsValue::new(1));\n    assert_eq!(reversed.at(2i64, context).unwrap(), JsValue::new(3));\n\n    // Original is unchanged\n    assert_eq!(array.at(0i64, context).unwrap(), JsValue::new(3));\n    assert_eq!(array.at(1i64, context).unwrap(), JsValue::new(1));\n    assert_eq!(array.at(2i64, context).unwrap(), JsValue::new(2));\n}\n\n#[test]\nfn typed_array_to_sorted() {\n    let context = &mut Context::default();\n    let array = JsUint8Array::from_iter(vec![3u8, 1, 2], context).unwrap();\n\n    let sorted = array.to_sorted(None, context).unwrap();\n\n    // New array is sorted\n    assert_eq!(sorted.at(0i64, context).unwrap(), JsValue::new(1));\n    assert_eq!(sorted.at(1i64, context).unwrap(), JsValue::new(2));\n    assert_eq!(sorted.at(2i64, context).unwrap(), JsValue::new(3));\n\n    // Original is unchanged\n    assert_eq!(array.at(0i64, context).unwrap(), JsValue::new(3));\n    assert_eq!(array.at(1i64, context).unwrap(), JsValue::new(1));\n    assert_eq!(array.at(2i64, context).unwrap(), JsValue::new(2));\n}\n\n#[test]\nfn typed_array_to_locale_string() {\n    let context = &mut Context::default();\n    let array = JsUint8Array::from_iter(vec![1u8, 2, 3], context).unwrap();\n\n    let result = array.to_locale_string(None, None, context).unwrap();\n\n    let result_str = result\n        .as_string()\n        .expect(\"toLocaleString should return a string\");\n    assert!(\n        result_str.to_std_string_escaped().contains('1'),\n        \"result should contain element values\"\n    );\n}\n"
  },
  {
    "path": "core/engine/src/object/builtins/jsweakmap.rs",
    "content": "//! A Rust API wrapper for the `WeakMap` Builtin ECMAScript Object\nuse std::ops::Deref;\n\nuse boa_gc::{Finalize, Trace};\n\nuse crate::{\n    Context, JsResult, JsValue,\n    builtins::weak_map::{NativeWeakMap, WeakMap},\n    error::JsNativeError,\n    object::JsObject,\n    value::TryFromJs,\n};\n\n/// `JsWeakMap` provides a wrapper for Boa's implementation of the ECMAScript `WeakMap` object.\n#[derive(Debug, Clone, Trace, Finalize)]\npub struct JsWeakMap {\n    inner: JsObject,\n}\n\nimpl JsWeakMap {\n    /// Creates a new empty `WeakMap`.\n    ///\n    /// More information:\n    ///  - [MDN documentation][mdn]\n    ///\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/WeakMap/WeakMap\n    #[inline]\n    pub fn new(context: &mut Context) -> Self {\n        Self {\n            inner: JsObject::from_proto_and_data_with_shared_shape(\n                context.root_shape(),\n                context.intrinsics().constructors().weak_map().prototype(),\n                NativeWeakMap::new(),\n            )\n            .upcast(),\n        }\n    }\n\n    /// Returns the value associated with the specified key in the `WeakMap`,\n    /// or `undefined` if the key is not present.\n    ///\n    /// More information:\n    ///  - [MDN documentation][mdn]\n    ///\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/WeakMap/get\n    #[inline]\n    pub fn get(&self, key: &JsObject, context: &mut Context) -> JsResult<JsValue> {\n        WeakMap::get(&self.inner.clone().into(), &[key.clone().into()], context)\n    }\n\n    /// Inserts a key-value pair into the `WeakMap`.\n    ///\n    /// More information:\n    ///  - [MDN documentation][mdn]\n    ///\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/WeakMap/set\n    #[inline]\n    pub fn set(&self, key: &JsObject, value: JsValue, context: &mut Context) -> JsResult<JsValue> {\n        WeakMap::set(\n            &self.inner.clone().into(),\n            &[key.clone().into(), value],\n            context,\n        )\n    }\n\n    /// Returns `true` if the specified key exists in the `WeakMap`.\n    ///\n    /// More information:\n    ///  - [MDN documentation][mdn]\n    ///\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/WeakMap/has\n    #[inline]\n    pub fn has(&self, key: &JsObject, context: &mut Context) -> JsResult<bool> {\n        WeakMap::has(&self.inner.clone().into(), &[key.clone().into()], context)\n            .map(|v| v.as_boolean().unwrap_or(false))\n    }\n\n    /// Removes the element associated with the specified key.\n    /// Returns `true` if the element existed, `false` otherwise.\n    ///\n    /// More information:\n    ///  - [MDN documentation][mdn]\n    ///\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/WeakMap/delete\n    #[inline]\n    pub fn delete(&self, key: &JsObject, context: &mut Context) -> JsResult<bool> {\n        WeakMap::delete(&self.inner.clone().into(), &[key.clone().into()], context)\n            .map(|v| v.as_boolean().unwrap_or(false))\n    }\n\n    /// Returns the value associated with the key if it exists; otherwise inserts\n    /// the provided default value and returns it.\n    ///\n    /// More information:\n    ///  - [MDN documentation][mdn]\n    ///\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/WeakMap/getOrInsert\n    #[inline]\n    pub fn get_or_insert(\n        &self,\n        key: &JsObject,\n        default: JsValue,\n        context: &mut Context,\n    ) -> JsResult<JsValue> {\n        WeakMap::get_or_insert(\n            &self.inner.clone().into(),\n            &[key.clone().into(), default],\n            context,\n        )\n    }\n\n    /// Returns the value associated with the key if it exists; otherwise calls\n    /// the provided callback with the key, inserts the result, and returns it.\n    ///\n    /// More information:\n    ///  - [MDN documentation][mdn]\n    ///\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/WeakMap/getOrInsertComputed\n    #[inline]\n    pub fn get_or_insert_computed(\n        &self,\n        key: &JsObject,\n        callback: JsValue,\n        context: &mut Context,\n    ) -> JsResult<JsValue> {\n        WeakMap::get_or_insert_computed(\n            &self.inner.clone().into(),\n            &[key.clone().into(), callback],\n            context,\n        )\n    }\n\n    /// Creates a `JsWeakMap` from a `JsObject`, or returns the original object as `Err`\n    /// if it is not a `WeakMap`.\n    #[inline]\n    pub fn from_object(object: JsObject) -> Result<Self, JsObject> {\n        if object.downcast_ref::<NativeWeakMap>().is_some() {\n            Ok(Self { inner: object })\n        } else {\n            Err(object)\n        }\n    }\n}\n\nimpl From<JsWeakMap> for JsObject {\n    #[inline]\n    fn from(o: JsWeakMap) -> Self {\n        o.inner.clone()\n    }\n}\n\nimpl From<JsWeakMap> for JsValue {\n    #[inline]\n    fn from(o: JsWeakMap) -> Self {\n        o.inner.clone().into()\n    }\n}\n\nimpl Deref for JsWeakMap {\n    type Target = JsObject;\n    #[inline]\n    fn deref(&self) -> &Self::Target {\n        &self.inner\n    }\n}\n\nimpl TryFromJs for JsWeakMap {\n    fn try_from_js(value: &JsValue, _context: &mut Context) -> JsResult<Self> {\n        if let Some(o) = value.as_object()\n            && let Ok(weak_map) = Self::from_object(o.clone())\n        {\n            Ok(weak_map)\n        } else {\n            Err(JsNativeError::typ()\n                .with_message(\"value is not a WeakMap object\")\n                .into())\n        }\n    }\n}\n"
  },
  {
    "path": "core/engine/src/object/builtins/jsweakset.rs",
    "content": "//! A Rust API wrapper for the `WeakSet` Builtin ECMAScript Object\nuse std::ops::Deref;\n\nuse boa_gc::{Finalize, Trace};\n\nuse crate::{\n    Context, JsResult, JsValue,\n    builtins::weak_set::{NativeWeakSet, WeakSet},\n    error::JsNativeError,\n    object::JsObject,\n    value::TryFromJs,\n};\n\n/// `JsWeakSet` provides a wrapper for Boa's implementation of the ECMAScript `WeakSet` object.\n#[derive(Debug, Clone, Trace, Finalize)]\npub struct JsWeakSet {\n    inner: JsObject,\n}\n\nimpl JsWeakSet {\n    /// Creates a new empty `WeakSet`.\n    ///\n    /// More information:\n    ///  - [MDN documentation][mdn]\n    ///\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/WeakSet/WeakSet\n    #[inline]\n    pub fn new(context: &mut Context) -> Self {\n        Self {\n            inner: JsObject::from_proto_and_data_with_shared_shape(\n                context.root_shape(),\n                context.intrinsics().constructors().weak_set().prototype(),\n                NativeWeakSet::new(),\n            )\n            .upcast(),\n        }\n    }\n\n    /// Adds the given object to the `WeakSet`.\n    /// Returns the `JsWeakSet` itself.\n    ///\n    /// More information:\n    ///  - [MDN documentation][mdn]\n    ///\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/WeakSet/add\n    #[inline]\n    pub fn add(&self, value: &JsObject, context: &mut Context) -> JsResult<Self> {\n        WeakSet::add(&self.inner.clone().into(), &[value.clone().into()], context)?;\n        Ok(self.clone())\n    }\n\n    /// Removes the given object from the `WeakSet`.\n    /// Returns `true` if the element existed, `false` otherwise.\n    ///\n    /// More information:\n    ///  - [MDN documentation][mdn]\n    ///\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/WeakSet/delete\n    #[inline]\n    pub fn delete(&self, value: &JsObject, context: &mut Context) -> JsResult<bool> {\n        WeakSet::delete(&self.inner.clone().into(), &[value.clone().into()], context)\n            .map(|v| v.as_boolean().unwrap_or(false))\n    }\n\n    /// Returns `true` if the given object exists in the `WeakSet`.\n    ///\n    /// More information:\n    ///  - [MDN documentation][mdn]\n    ///\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/WeakSet/has\n    #[inline]\n    pub fn has(&self, value: &JsObject, context: &mut Context) -> JsResult<bool> {\n        WeakSet::has(&self.inner.clone().into(), &[value.clone().into()], context)\n            .map(|v| v.as_boolean().unwrap_or(false))\n    }\n\n    /// Creates a `JsWeakSet` from a `JsObject`, or returns the original object as `Err`\n    /// if it is not a `WeakSet`.\n    #[inline]\n    pub fn from_object(object: JsObject) -> Result<Self, JsObject> {\n        if object.downcast_ref::<NativeWeakSet>().is_some() {\n            Ok(Self { inner: object })\n        } else {\n            Err(object)\n        }\n    }\n}\n\nimpl From<JsWeakSet> for JsObject {\n    #[inline]\n    fn from(o: JsWeakSet) -> Self {\n        o.inner.clone()\n    }\n}\n\nimpl From<JsWeakSet> for JsValue {\n    #[inline]\n    fn from(o: JsWeakSet) -> Self {\n        o.inner.clone().into()\n    }\n}\n\nimpl Deref for JsWeakSet {\n    type Target = JsObject;\n    #[inline]\n    fn deref(&self) -> &Self::Target {\n        &self.inner\n    }\n}\n\nimpl TryFromJs for JsWeakSet {\n    fn try_from_js(value: &JsValue, _context: &mut Context) -> JsResult<Self> {\n        if let Some(o) = value.as_object()\n            && let Ok(weak_set) = Self::from_object(o.clone())\n        {\n            Ok(weak_set)\n        } else {\n            Err(JsNativeError::typ()\n                .with_message(\"value is not a WeakSet object\")\n                .into())\n        }\n    }\n}\n"
  },
  {
    "path": "core/engine/src/object/builtins/mod.rs",
    "content": "//! All Rust API wrappers for Boa's ECMAScript objects.\n//!\n//! The structs available in this module provide functionality to interact with native ECMAScript objects from Rust.\n\nmod jsarray;\nmod jsarraybuffer;\nmod jsasyncgenerator;\nmod jsdataview;\nmod jsdate;\nmod jsfunction;\nmod jsgenerator;\nmod jsgeneratorfunction;\nmod jsmap;\nmod jsmap_iterator;\nmod jspromise;\nmod jsproxy;\nmod jsregexp;\nmod jsset;\nmod jsset_iterator;\nmod jssharedarraybuffer;\nmod jstypedarray;\nmod jsweakmap;\nmod jsweakset;\n\npub use jsarray::*;\npub use jsarraybuffer::*;\npub use jsasyncgenerator::JsAsyncGenerator;\npub use jsdataview::*;\npub use jsdate::*;\npub use jsfunction::*;\npub use jsgenerator::*;\npub use jsgeneratorfunction::JsGeneratorFunction;\npub use jsmap::*;\npub use jsmap_iterator::*;\npub use jspromise::*;\npub use jsproxy::{JsProxy, JsProxyBuilder, JsRevocableProxy};\npub use jsregexp::JsRegExp;\npub use jsset::*;\npub use jsset_iterator::*;\npub use jssharedarraybuffer::*;\npub use jstypedarray::*;\npub use jsweakmap::JsWeakMap;\npub use jsweakset::JsWeakSet;\n"
  },
  {
    "path": "core/engine/src/object/datatypes.rs",
    "content": "use std::{\n    any::TypeId,\n    borrow::Cow,\n    cell::Cell,\n    collections::{BTreeMap, BTreeSet, BinaryHeap, HashMap, HashSet, LinkedList, VecDeque},\n    hash::{BuildHasher, Hash},\n    marker::PhantomData,\n    num::{\n        NonZeroI8, NonZeroI16, NonZeroI32, NonZeroI64, NonZeroI128, NonZeroIsize, NonZeroU8,\n        NonZeroU16, NonZeroU32, NonZeroU64, NonZeroU128, NonZeroUsize,\n    },\n    path::{Path, PathBuf},\n    rc::Rc,\n    sync::atomic,\n};\n\nuse boa_gc::{Ephemeron, Finalize, Gc, GcRefCell, Trace, WeakGc, WeakMap};\n\nuse super::internal_methods::{InternalObjectMethods, ORDINARY_INTERNAL_METHODS};\n\n/// Represents a type that can be stored inside a `JsObject`.\n///\n/// This can be automatically derived using a macro.\n///\n/// # Example\n///\n/// ```\n/// use boa_engine::{Finalize, JsData, JsObject, Trace};\n///\n/// #[derive(Trace, Finalize, JsData)]\n/// struct CustomStruct {\n///     #[unsafe_ignore_trace]\n///     counter: usize,\n/// }\n///\n/// let object =\n///     JsObject::from_proto_and_data(None, CustomStruct { counter: 5 });\n///\n/// assert_eq!(object.downcast_ref::<CustomStruct>().unwrap().counter, 5);\n/// ```\npub trait JsData {\n    #[doc(hidden)]\n    fn internal_methods(&self) -> &'static InternalObjectMethods\n    where\n        Self: Sized, // Avoids adding this method to `NativeObject`'s vtable.\n    {\n        &ORDINARY_INTERNAL_METHODS\n    }\n}\n\nmacro_rules! default_impls {\n    ($($T:ty),*$(,)?) => {\n        $(\n            impl JsData for $T {}\n        )*\n    }\n}\n\ndefault_impls![\n    (),\n    bool,\n    isize,\n    usize,\n    i8,\n    u8,\n    i16,\n    u16,\n    i32,\n    u32,\n    i64,\n    u64,\n    i128,\n    u128,\n    f32,\n    f64,\n    char,\n    TypeId,\n    String,\n    Path,\n    PathBuf,\n    NonZeroIsize,\n    NonZeroUsize,\n    NonZeroI8,\n    NonZeroU8,\n    NonZeroI16,\n    NonZeroU16,\n    NonZeroI32,\n    NonZeroU32,\n    NonZeroI64,\n    NonZeroU64,\n    NonZeroI128,\n    NonZeroU128,\n];\n\n#[cfg(target_has_atomic = \"8\")]\ndefault_impls![atomic::AtomicBool, atomic::AtomicI8, atomic::AtomicU8];\n\n#[cfg(target_has_atomic = \"16\")]\ndefault_impls![atomic::AtomicI16, atomic::AtomicU16];\n\n#[cfg(target_has_atomic = \"32\")]\ndefault_impls![atomic::AtomicI32, atomic::AtomicU32];\n\n#[cfg(target_has_atomic = \"64\")]\ndefault_impls![atomic::AtomicI64, atomic::AtomicU64];\n\n#[cfg(target_has_atomic = \"ptr\")]\ndefault_impls![atomic::AtomicIsize, atomic::AtomicUsize];\n\nimpl<T, const N: usize> JsData for [T; N] {}\n\nmacro_rules! fn_one {\n    ($ty:ty $(,$args:ident)*) => {\n        impl<Ret $(,$args)*> JsData for $ty {}\n    }\n}\n\nmacro_rules! fn_impls {\n    () => {\n        fn_one!(extern \"Rust\" fn () -> Ret);\n        fn_one!(extern \"C\" fn () -> Ret);\n        fn_one!(unsafe extern \"Rust\" fn () -> Ret);\n        fn_one!(unsafe extern \"C\" fn () -> Ret);\n    };\n    ($($args:ident),*) => {\n        fn_one!(extern \"Rust\" fn ($($args),*) -> Ret, $($args),*);\n        fn_one!(extern \"C\" fn ($($args),*) -> Ret, $($args),*);\n        fn_one!(extern \"C\" fn ($($args),*, ...) -> Ret, $($args),*);\n        fn_one!(unsafe extern \"Rust\" fn ($($args),*) -> Ret, $($args),*);\n        fn_one!(unsafe extern \"C\" fn ($($args),*) -> Ret, $($args),*);\n        fn_one!(unsafe extern \"C\" fn ($($args),*, ...) -> Ret, $($args),*);\n    }\n}\n\nmacro_rules! tuple_impls {\n    () => {}; // This case is handled above, by default_impls!().\n    ($($args:ident),*) => {\n        impl<$($args),*> JsData for ($($args,)*) {}\n    }\n}\n\nmacro_rules! type_arg_tuple_based_impls {\n    ($(($($args:ident),*);)*) => {\n        $(\n            fn_impls!($($args),*);\n            tuple_impls!($($args),*);\n        )*\n    }\n}\n\ntype_arg_tuple_based_impls![\n    ();\n    (A);\n    (A, B);\n    (A, B, C);\n    (A, B, C, D);\n    (A, B, C, D, E);\n    (A, B, C, D, E, F);\n    (A, B, C, D, E, F, G);\n    (A, B, C, D, E, F, G, H);\n    (A, B, C, D, E, F, G, H, I);\n    (A, B, C, D, E, F, G, H, I, J);\n    (A, B, C, D, E, F, G, H, I, J, K);\n    (A, B, C, D, E, F, G, H, I, J, K, L);\n];\n\nimpl<T: ?Sized> JsData for Box<T> {}\n\nimpl<T: ?Sized> JsData for Rc<T> {}\n\nimpl<T> JsData for Vec<T> {}\n\nimpl<T> JsData for thin_vec::ThinVec<T> {}\n\nimpl<T> JsData for Option<T> {}\n\nimpl<T, E> JsData for Result<T, E> {}\n\nimpl<T: Ord> JsData for BinaryHeap<T> {}\n\nimpl<K, V> JsData for BTreeMap<K, V> {}\n\nimpl<T> JsData for BTreeSet<T> {}\n\nimpl<K: Eq + Hash, V, S: BuildHasher> JsData for hashbrown::hash_map::HashMap<K, V, S> {}\n\nimpl<K: Eq + Hash, V, S: BuildHasher> JsData for HashMap<K, V, S> {}\n\nimpl<T: Eq + Hash, S: BuildHasher> JsData for HashSet<T, S> {}\n\nimpl<T: Eq + Hash> JsData for LinkedList<T> {}\n\nimpl<T> JsData for PhantomData<T> {}\n\nimpl<T> JsData for VecDeque<T> {}\n\nimpl<T: ToOwned + ?Sized> JsData for Cow<'static, T> {}\n\nimpl<T> JsData for Cell<Option<T>> {}\n\n#[cfg(feature = \"intl\")]\ndefault_impls!(icu_locale::Locale);\n\nimpl<T: Trace + ?Sized> JsData for Gc<T> {}\n\nimpl<T: Trace + ?Sized> JsData for WeakGc<T> {}\n\nimpl<T: Trace + ?Sized, V: Trace> JsData for Ephemeron<T, V> {}\n\nimpl<T: Trace + ?Sized> JsData for GcRefCell<T> {}\n\nimpl<K: Trace + ?Sized, V: Trace> JsData for WeakMap<K, V> {}\n\n/// Wrapper type to enforce consistent alignment for all [`JsData`] types.\n///\n/// Ensures alignment is exactly 8 bytes:\n/// - Minimum alignment is set via `#[repr(align(8))]`.\n/// - Maximum alignment is enforced at compile time with a const assertion.\n///\n///\n/// Use [`ObjectData::new`] to construct safely. Inner data is accessible via [`AsRef`] and [`AsMut`].\n#[derive(Debug, Finalize, Trace)]\n// SAFETY: This does not implement drop, so this is safe.\n#[boa_gc(unsafe_no_drop)]\n#[repr(C, align(8))]\n#[non_exhaustive]\npub(crate) struct ObjectData<T: ?Sized> {\n    // MUST BE PRIVATE, should not be constructed directly. i.e. { data: ... }\n    // Because we want to trigger the compile-time const assertion below.\n    //\n    // It is fine if we have as_ref/as_mut to it or any access.\n    data: T,\n}\n\nimpl<T: Default> Default for ObjectData<T> {\n    #[inline]\n    fn default() -> Self {\n        Self::new(T::default())\n    }\n}\n\nstatic_assertions::const_assert!(align_of::<Box<()>>() <= 8);\n\nimpl<T> ObjectData<T> {\n    const OBJECT_DATA_ALIGNMENT_REQUIREMENT: () = assert!(\n        align_of::<T>() <= 8,\n        \"Alignment of JsData must be <= 8, consider wrapping the data in a Box<T>.\"\n    );\n\n    pub(crate) fn new(value: T) -> Self {\n        // force assertion to trigger when we instantiate `ObjectData<T>::new`.\n        let () = Self::OBJECT_DATA_ALIGNMENT_REQUIREMENT;\n\n        Self { data: value }\n    }\n}\n\nimpl<T: ?Sized> AsRef<T> for ObjectData<T> {\n    #[inline]\n    fn as_ref(&self) -> &T {\n        &self.data\n    }\n}\n\nimpl<T: ?Sized> AsMut<T> for ObjectData<T> {\n    #[inline]\n    fn as_mut(&mut self) -> &mut T {\n        &mut self.data\n    }\n}\n"
  },
  {
    "path": "core/engine/src/object/internal_methods/immutable_prototype.rs",
    "content": "use crate::{\n    Context, JsResult,\n    object::{JsObject, JsPrototype},\n};\n\nuse super::{InternalObjectMethods, ORDINARY_INTERNAL_METHODS};\n\n/// Definitions of the internal object methods for [**Immutable Prototype Exotic Objects**][spec].\n///\n/// [spec]: https://tc39.es/ecma262/#sec-immutable-prototype-exotic-objects\npub(crate) static IMMUTABLE_PROTOTYPE_EXOTIC_INTERNAL_METHODS: InternalObjectMethods =\n    InternalObjectMethods {\n        __set_prototype_of__: immutable_prototype_exotic_set_prototype_of,\n        ..ORDINARY_INTERNAL_METHODS\n    };\n\n/// [`[[SetPrototypeOf]] ( V )`][spec].\n///\n/// [spec]: https://tc39.es/ecma262/#sec-immutable-prototype-exotic-objects-setprototypeof-v\n#[allow(clippy::needless_pass_by_value)]\npub(crate) fn immutable_prototype_exotic_set_prototype_of(\n    obj: &JsObject,\n    val: JsPrototype,\n    context: &mut Context,\n) -> JsResult<bool> {\n    // 1. Return ? SetImmutablePrototype(O, V).\n\n    // inlined since other implementations can just use `set_prototype_of` directly.\n\n    // SetImmutablePrototype ( O, V )\n    // <https://tc39.es/ecma262/#sec-set-immutable-prototype>\n\n    // 1. Let current be ? O.[[GetPrototypeOf]]().\n    let current = obj.__get_prototype_of__(context)?;\n\n    // 2. If SameValue(V, current) is true, return true.\n    // 3. Return false.\n    Ok(val == current)\n}\n"
  },
  {
    "path": "core/engine/src/object/internal_methods/mod.rs",
    "content": "//! This module defines the object internal methods.\n//!\n//! More information:\n//!  - [ECMAScript reference][spec]\n//!\n//! [spec]: https://tc39.es/ecma262/#sec-ordinary-object-internal-methods-and-internal-slots\n\nuse std::ops::{Deref, DerefMut};\n\nuse super::{\n    JsPrototype, PROTOTYPE,\n    shape::slot::{Slot, SlotAttributes},\n};\nuse crate::{\n    Context, JsNativeError, JsResult,\n    context::intrinsics::{StandardConstructor, StandardConstructors},\n    object::JsObject,\n    property::{DescriptorKind, PropertyDescriptor, PropertyKey},\n    value::JsValue,\n    vm::source_info::NativeSourceInfo,\n};\n\npub(crate) mod immutable_prototype;\npub(crate) mod string;\n\n/// A lightweight wrapper around [`Context`] used in [`InternalObjectMethods`].\n#[derive(Debug)]\npub(crate) struct InternalMethodPropertyContext<'ctx> {\n    context: &'ctx mut Context,\n    slot: Slot,\n}\n\nimpl<'ctx> InternalMethodPropertyContext<'ctx> {\n    /// Create a new [`InternalMethodPropertyContext`].\n    pub(crate) fn new(context: &'ctx mut Context) -> Self {\n        Self {\n            context,\n            slot: Slot::new(),\n        }\n    }\n\n    /// Gets the [`Slot`] associated with this [`InternalMethodPropertyContext`].\n    #[inline]\n    pub(crate) fn slot(&mut self) -> &mut Slot {\n        &mut self.slot\n    }\n}\n\nimpl Deref for InternalMethodPropertyContext<'_> {\n    type Target = Context;\n\n    #[inline]\n    fn deref(&self) -> &Self::Target {\n        self.context\n    }\n}\n\nimpl DerefMut for InternalMethodPropertyContext<'_> {\n    #[inline]\n    fn deref_mut(&mut self) -> &mut Self::Target {\n        self.context\n    }\n}\n\nimpl<'context> From<&'context mut Context> for InternalMethodPropertyContext<'context> {\n    #[inline]\n    fn from(context: &'context mut Context) -> Self {\n        Self::new(context)\n    }\n}\n\n/// A lightweight wrapper around [`Context`] used in internal call methods.\n#[derive(Debug)]\npub(crate) struct InternalMethodCallContext<'ctx> {\n    context: &'ctx mut Context,\n    native_source_info: NativeSourceInfo,\n}\n\nimpl<'ctx> InternalMethodCallContext<'ctx> {\n    /// Create a new [`InternalMethodCallContext`].\n    #[inline]\n    #[cfg_attr(feature = \"native-backtrace\", track_caller)]\n    pub(crate) fn new(context: &'ctx mut Context) -> Self {\n        Self {\n            context,\n            native_source_info: NativeSourceInfo::caller(),\n        }\n    }\n\n    /// Create a new [`InternalMethodCallContext`].\n    #[inline]\n    pub(crate) fn with_native_source_info(\n        context: &'ctx mut Context,\n        native_source_info: NativeSourceInfo,\n    ) -> Self {\n        Self {\n            context,\n            native_source_info,\n        }\n    }\n\n    #[inline]\n    pub(crate) fn context(&mut self) -> &mut Context {\n        self.context\n    }\n\n    #[inline]\n    pub(crate) fn native_source_info(&self) -> NativeSourceInfo {\n        self.native_source_info\n    }\n}\n\nimpl Deref for InternalMethodCallContext<'_> {\n    type Target = Context;\n\n    #[inline]\n    fn deref(&self) -> &Self::Target {\n        self.context\n    }\n}\n\nimpl DerefMut for InternalMethodCallContext<'_> {\n    #[inline]\n    fn deref_mut(&mut self) -> &mut Self::Target {\n        self.context\n    }\n}\n\nimpl<'context> From<&'context mut Context> for InternalMethodCallContext<'context> {\n    #[inline]\n    fn from(context: &'context mut Context) -> Self {\n        Self::new(context)\n    }\n}\n\nimpl JsObject {\n    /// Internal method `[[GetPrototypeOf]]`\n    ///\n    /// Return either the prototype of this object or null.\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-ordinary-object-internal-methods-and-internal-slots-getprototypeof\n    #[track_caller]\n    pub(crate) fn __get_prototype_of__(&self, context: &mut Context) -> JsResult<JsPrototype> {\n        (self.vtable().__get_prototype_of__)(self, context)\n    }\n\n    /// Internal method `[[SetPrototypeOf]]`\n    ///\n    /// Set the property of a specified object to another object or `null`.\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-ordinary-object-internal-methods-and-internal-slots-setprototypeof-v\n    pub(crate) fn __set_prototype_of__(\n        &self,\n        val: JsPrototype,\n        context: &mut Context,\n    ) -> JsResult<bool> {\n        (self.vtable().__set_prototype_of__)(self, val, context)\n    }\n\n    /// Internal method `[[IsExtensible]]`\n    ///\n    /// Check if the object is extensible.\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-ordinary-object-internal-methods-and-internal-slots-isextensible\n    pub(crate) fn __is_extensible__(&self, context: &mut Context) -> JsResult<bool> {\n        (self.vtable().__is_extensible__)(self, context)\n    }\n\n    /// Internal method `[[PreventExtensions]]`\n    ///\n    /// Disable extensibility for this object.\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-ordinary-object-internal-methods-and-internal-slots-preventextensions\n    pub(crate) fn __prevent_extensions__(&self, context: &mut Context) -> JsResult<bool> {\n        (self.vtable().__prevent_extensions__)(self, context)\n    }\n\n    /// Internal method `[[GetOwnProperty]]`\n    ///\n    /// Get the specified property of this object.\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-ordinary-object-internal-methods-and-internal-slots-getownproperty-p\n    pub(crate) fn __get_own_property__(\n        &self,\n        key: &PropertyKey,\n        context: &mut InternalMethodPropertyContext<'_>,\n    ) -> JsResult<Option<PropertyDescriptor>> {\n        (self.vtable().__get_own_property__)(self, key, context)\n    }\n\n    /// Internal method `[[DefineOwnProperty]]`\n    ///\n    /// Define a new property of this object.\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-ordinary-object-internal-methods-and-internal-slots-defineownproperty-p-desc\n    pub(crate) fn __define_own_property__(\n        &self,\n        key: &PropertyKey,\n        desc: PropertyDescriptor,\n        context: &mut InternalMethodPropertyContext<'_>,\n    ) -> JsResult<bool> {\n        (self.vtable().__define_own_property__)(self, key, desc, context)\n    }\n\n    /// Internal method `[[hasProperty]]`.\n    ///\n    /// Check if the object or its prototype has the required property.\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-ordinary-object-internal-methods-and-internal-slots-hasproperty-p\n    pub(crate) fn __has_property__(\n        &self,\n        key: &PropertyKey,\n        context: &mut InternalMethodPropertyContext<'_>,\n    ) -> JsResult<bool> {\n        (self.vtable().__has_property__)(self, key, context)\n    }\n\n    /// Internal optimization method.\n    ///\n    /// This method combines the internal methods `[[hasProperty]]` and `[[Get]]`.\n    ///\n    /// More information:\n    ///  - [ECMAScript reference hasProperty][spec0]\n    ///  - [ECMAScript reference get][spec1]\n    ///\n    /// [spec0]: https://tc39.es/ecma262/#sec-ordinary-object-internal-methods-and-internal-slots-hasproperty-p\n    /// [spec1]: https://tc39.es/ecma262/#sec-ordinary-object-internal-methods-and-internal-slots-get-p-receiver\n    pub(crate) fn __try_get__(\n        &self,\n        key: &PropertyKey,\n        receiver: JsValue,\n        context: &mut InternalMethodPropertyContext<'_>,\n    ) -> JsResult<Option<JsValue>> {\n        (self.vtable().__try_get__)(self, key, receiver, context)\n    }\n\n    /// Internal method `[[Get]]`\n    ///\n    /// Get the specified property of this object or its prototype.\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-ordinary-object-internal-methods-and-internal-slots-get-p-receiver\n    pub(crate) fn __get__(\n        &self,\n        key: &PropertyKey,\n        receiver: JsValue,\n        context: &mut InternalMethodPropertyContext<'_>,\n    ) -> JsResult<JsValue> {\n        (self.vtable().__get__)(self, key, receiver, context)\n    }\n\n    /// Internal method `[[Set]]`\n    ///\n    /// Set the specified property of this object or its prototype to the provided value.\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-ordinary-object-internal-methods-and-internal-slots-set-p-v-receiver\n    pub(crate) fn __set__(\n        &self,\n        key: PropertyKey,\n        value: JsValue,\n        receiver: JsValue,\n        context: &mut InternalMethodPropertyContext<'_>,\n    ) -> JsResult<bool> {\n        (self.vtable().__set__)(self, key, value, receiver, context)\n    }\n\n    /// Internal method `[[Delete]]`\n    ///\n    /// Delete the specified own property of this object.\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-ordinary-object-internal-methods-and-internal-slots-delete-p\n    pub(crate) fn __delete__(\n        &self,\n        key: &PropertyKey,\n        context: &mut InternalMethodPropertyContext<'_>,\n    ) -> JsResult<bool> {\n        (self.vtable().__delete__)(self, key, context)\n    }\n\n    /// Internal method `[[OwnPropertyKeys]]`\n    ///\n    /// Get all the keys of the properties of this object.\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-ordinary-object-internal-methods-and-internal-slots-ownpropertykeys\n    #[track_caller]\n    pub(crate) fn __own_property_keys__(\n        &self,\n        context: &mut Context,\n    ) -> JsResult<Vec<PropertyKey>> {\n        (self.vtable().__own_property_keys__)(self, context)\n    }\n\n    /// Internal method `[[Call]]`\n    ///\n    /// The caller must ensure that the following values are pushed on the stack.\n    ///\n    /// Stack: `this, function, arg0, arg1, ..., argN`\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-ecmascript-function-objects-call-thisargument-argumentslist\n    #[track_caller]\n    pub(crate) fn __call__(&self, argument_count: usize) -> CallValue {\n        CallValue::Pending {\n            func: self.vtable().__call__,\n            object: self.clone(),\n            argument_count,\n            native_source_info: NativeSourceInfo::caller(),\n        }\n    }\n\n    /// Internal method `[[Construct]]`\n    ///\n    /// The caller must ensure that the following values are pushed on the stack.\n    ///\n    /// Stack: `function, arg0, arg1, ..., argN, new.target`\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-ecmascript-function-objects-construct-argumentslist-newtarget\n    #[track_caller]\n    pub(crate) fn __construct__(&self, argument_count: usize) -> CallValue {\n        CallValue::Pending {\n            func: self.vtable().__construct__,\n            object: self.clone(),\n            argument_count,\n            native_source_info: NativeSourceInfo::caller(),\n        }\n    }\n}\n\n/// Definitions of the internal object methods for ordinary objects.\n///\n/// If you want to implement an exotic object, create a new `static InternalObjectMethods`\n/// overriding the desired internal methods with the definitions of the spec\n/// and set all other methods to the default ordinary values, if necessary.\n///\n/// E.g. `string::STRING_EXOTIC_INTERNAL_METHODS`\n///\n/// Then, reference this static in the creation phase of an `ObjectData`.\n///\n/// E.g. `ObjectData::string`\npub(crate) const ORDINARY_INTERNAL_METHODS: InternalObjectMethods = InternalObjectMethods {\n    __get_prototype_of__: ordinary_get_prototype_of,\n    __set_prototype_of__: ordinary_set_prototype_of,\n    __is_extensible__: ordinary_is_extensible,\n    __prevent_extensions__: ordinary_prevent_extensions,\n    __get_own_property__: ordinary_get_own_property,\n    __define_own_property__: ordinary_define_own_property,\n    __has_property__: ordinary_has_property,\n    __try_get__: ordinary_try_get,\n    __get__: ordinary_get,\n    __set__: ordinary_set,\n    __delete__: ordinary_delete,\n    __own_property_keys__: ordinary_own_property_keys,\n    __call__: non_existent_call,\n    __construct__: non_existent_construct,\n};\n\n/// The internal representation of the internal methods of a `JsObject`.\n///\n/// This struct allows us to dynamically dispatch exotic objects with their\n/// exclusive definitions of the internal methods, without having to\n/// resort to `dyn Object`.\n///\n/// For a guide on how to implement exotic internal methods, see `ORDINARY_INTERNAL_METHODS`.\n#[derive(Debug, Clone, Copy)]\n#[allow(clippy::type_complexity, clippy::struct_field_names)]\npub struct InternalObjectMethods {\n    pub(crate) __get_prototype_of__: fn(&JsObject, &mut Context) -> JsResult<JsPrototype>,\n    pub(crate) __set_prototype_of__: fn(&JsObject, JsPrototype, &mut Context) -> JsResult<bool>,\n    pub(crate) __is_extensible__: fn(&JsObject, &mut Context) -> JsResult<bool>,\n    pub(crate) __prevent_extensions__: fn(&JsObject, &mut Context) -> JsResult<bool>,\n    pub(crate) __get_own_property__: fn(\n        &JsObject,\n        &PropertyKey,\n        &mut InternalMethodPropertyContext<'_>,\n    ) -> JsResult<Option<PropertyDescriptor>>,\n    pub(crate) __define_own_property__: fn(\n        &JsObject,\n        &PropertyKey,\n        PropertyDescriptor,\n        &mut InternalMethodPropertyContext<'_>,\n    ) -> JsResult<bool>,\n    pub(crate) __has_property__:\n        fn(&JsObject, &PropertyKey, &mut InternalMethodPropertyContext<'_>) -> JsResult<bool>,\n    pub(crate) __get__: fn(\n        &JsObject,\n        &PropertyKey,\n        JsValue,\n        &mut InternalMethodPropertyContext<'_>,\n    ) -> JsResult<JsValue>,\n    pub(crate) __try_get__: fn(\n        &JsObject,\n        &PropertyKey,\n        JsValue,\n        &mut InternalMethodPropertyContext<'_>,\n    ) -> JsResult<Option<JsValue>>,\n    pub(crate) __set__: fn(\n        &JsObject,\n        PropertyKey,\n        JsValue,\n        JsValue,\n        &mut InternalMethodPropertyContext<'_>,\n    ) -> JsResult<bool>,\n    pub(crate) __delete__:\n        fn(&JsObject, &PropertyKey, &mut InternalMethodPropertyContext<'_>) -> JsResult<bool>,\n    pub(crate) __own_property_keys__:\n        fn(&JsObject, context: &mut Context) -> JsResult<Vec<PropertyKey>>,\n    pub(crate) __call__: fn(\n        &JsObject,\n        argument_count: usize,\n        context: &mut InternalMethodCallContext<'_>,\n    ) -> JsResult<CallValue>,\n    pub(crate) __construct__: fn(\n        &JsObject,\n        argument_count: usize,\n        context: &mut InternalMethodCallContext<'_>,\n    ) -> JsResult<CallValue>,\n}\n\n/// The return value of an internal method (`[[Call]]` or `[[Construct]]`).\n///\n/// This is done to avoid recursion.\n#[allow(variant_size_differences)]\npub(crate) enum CallValue {\n    /// Calling is ready, the frames have been setup.\n    ///\n    /// Requires calling [`Context::run()`].\n    Ready,\n\n    /// Further processing is needed.\n    Pending {\n        func: fn(\n            &JsObject,\n            argument_count: usize,\n            context: &mut InternalMethodCallContext<'_>,\n        ) -> JsResult<CallValue>,\n        object: JsObject,\n        argument_count: usize,\n        native_source_info: NativeSourceInfo,\n    },\n\n    /// The value has been computed and is the first element on the stack.\n    Complete,\n}\n\nimpl CallValue {\n    /// Resolves the [`CallValue`], and return if the value is complete.\n    #[cfg_attr(feature = \"native-backtrace\", track_caller)]\n    pub(crate) fn resolve(mut self, context: &mut Context) -> JsResult<bool> {\n        loop {\n            match self {\n                CallValue::Ready => return Ok(false),\n                CallValue::Complete => return Ok(true),\n                CallValue::Pending {\n                    func,\n                    object,\n                    argument_count,\n                    native_source_info,\n                } => {\n                    self = func(\n                        &object,\n                        argument_count,\n                        &mut InternalMethodCallContext::with_native_source_info(\n                            context,\n                            native_source_info,\n                        ),\n                    )?;\n                }\n            }\n        }\n    }\n}\n\n/// Abstract operation `OrdinaryGetPrototypeOf`.\n///\n/// More information:\n///  - [ECMAScript reference][spec]\n///\n/// [spec]: https://tc39.es/ecma262/#sec-ordinarygetprototypeof\n#[allow(clippy::unnecessary_wraps)]\npub(crate) fn ordinary_get_prototype_of(\n    obj: &JsObject,\n    _context: &mut Context,\n) -> JsResult<JsPrototype> {\n    // 1. Return O.[[Prototype]].\n    Ok(obj.prototype().clone())\n}\n\n/// Abstract operation `OrdinarySetPrototypeOf`.\n///\n/// More information:\n///  - [ECMAScript reference][spec]\n///\n/// [spec]: https://tc39.es/ecma262/#sec-ordinarysetprototypeof\n#[allow(clippy::unnecessary_wraps)]\npub(crate) fn ordinary_set_prototype_of(\n    obj: &JsObject,\n    val: JsPrototype,\n    _: &mut Context,\n) -> JsResult<bool> {\n    // 1. Assert: Either Type(V) is Object or Type(V) is Null.\n    // 2. Let current be O.[[Prototype]].\n    let current = obj.prototype();\n\n    // 3. If SameValue(V, current) is true, return true.\n    if val == current {\n        return Ok(true);\n    }\n\n    // 4. Let extensible be O.[[Extensible]].\n    // 5. If extensible is false, return false.\n    if !obj.extensible() {\n        return Ok(false);\n    }\n\n    // 6. Let p be V.\n    let mut p = val.clone();\n\n    // 7. Let done be false.\n    // 8. Repeat, while done is false,\n    // a. If p is null, set done to true.\n    while let Some(proto) = p {\n        // b. Else if SameValue(p, O) is true, return false.\n        if &proto == obj {\n            return Ok(false);\n        }\n        // c. Else,\n        // i. If p.[[GetPrototypeOf]] is not the ordinary object internal method defined\n        // in 10.1.1, set done to true.\n        else if proto.vtable().__get_prototype_of__ as usize\n            != ordinary_get_prototype_of as *const () as usize\n        {\n            break;\n        }\n        // ii. Else, set p to p.[[Prototype]].\n        p = proto.prototype();\n    }\n\n    // 9. Set O.[[Prototype]] to V.\n    obj.set_prototype(val);\n\n    // 10. Return true.\n    Ok(true)\n}\n\n/// Abstract operation `OrdinaryIsExtensible`.\n///\n/// More information:\n///  - [ECMAScript reference][spec]\n///\n/// [spec]: https://tc39.es/ecma262/#sec-ordinaryisextensible\n#[allow(clippy::unnecessary_wraps)]\npub(crate) fn ordinary_is_extensible(obj: &JsObject, _context: &mut Context) -> JsResult<bool> {\n    // 1. Return O.[[Extensible]].\n    Ok(obj.borrow().extensible)\n}\n\n/// Abstract operation `OrdinaryPreventExtensions`.\n///\n/// More information:\n///  - [ECMAScript reference][spec]\n///\n/// [spec]: https://tc39.es/ecma262/#sec-ordinarypreventextensions\n#[allow(clippy::unnecessary_wraps)]\npub(crate) fn ordinary_prevent_extensions(\n    obj: &JsObject,\n    _context: &mut Context,\n) -> JsResult<bool> {\n    // 1. Set O.[[Extensible]] to false.\n    obj.borrow_mut().extensible = false;\n\n    // 2. Return true.\n    Ok(true)\n}\n\n/// Abstract operation `OrdinaryGetOwnProperty`.\n///\n/// More information:\n///  - [ECMAScript reference][spec]\n///\n/// [spec]: https://tc39.es/ecma262/#sec-ordinarygetownproperty\n#[allow(clippy::unnecessary_wraps)]\npub(crate) fn ordinary_get_own_property(\n    obj: &JsObject,\n    key: &PropertyKey,\n    context: &mut InternalMethodPropertyContext<'_>,\n) -> JsResult<Option<PropertyDescriptor>> {\n    // 1. Assert: IsPropertyKey(P) is true.\n    // 2. If O does not have an own property with key P, return undefined.\n    // 3. Let D be a newly created Property Descriptor with no fields.\n    // 4. Let X be O's own property whose key is P.\n    // 5. If X is a data property, then\n    //      a. Set D.[[Value]] to the value of X's [[Value]] attribute.\n    //      b. Set D.[[Writable]] to the value of X's [[Writable]] attribute.\n    // 6. Else,\n    //      a. Assert: X is an accessor property.\n    //      b. Set D.[[Get]] to the value of X's [[Get]] attribute.\n    //      c. Set D.[[Set]] to the value of X's [[Set]] attribute.\n    // 7. Set D.[[Enumerable]] to the value of X's [[Enumerable]] attribute.\n    // 8. Set D.[[Configurable]] to the value of X's [[Configurable]] attribute.\n    // 9. Return D.\n    Ok(obj.borrow().properties.get_with_slot(key, context.slot()))\n}\n\n/// Abstract operation `OrdinaryDefineOwnProperty`.\n///\n/// More information:\n///  - [ECMAScript reference][spec]\n///\n/// [spec]: https://tc39.es/ecma262/#sec-ordinarydefineownproperty\npub(crate) fn ordinary_define_own_property(\n    obj: &JsObject,\n    key: &PropertyKey,\n    desc: PropertyDescriptor,\n    context: &mut InternalMethodPropertyContext<'_>,\n) -> JsResult<bool> {\n    // 1. Let current be ? O.[[GetOwnProperty]](P).\n    let current = obj.__get_own_property__(key, context)?;\n\n    // 2. Let extensible be ? IsExtensible(O).\n    let extensible = obj.__is_extensible__(context)?;\n\n    // 3. Return ValidateAndApplyPropertyDescriptor(O, P, extensible, Desc, current).\n    Ok(validate_and_apply_property_descriptor(\n        Some((obj, key)),\n        extensible,\n        desc,\n        current,\n        context.slot(),\n    ))\n}\n\n/// Abstract operation `OrdinaryHasProperty`.\n///\n/// More information:\n///  - [ECMAScript reference][spec]\n///\n/// [spec]: https://tc39.es/ecma262/#sec-ordinaryhasproperty\npub(crate) fn ordinary_has_property(\n    obj: &JsObject,\n    key: &PropertyKey,\n    context: &mut InternalMethodPropertyContext<'_>,\n) -> JsResult<bool> {\n    // 1. Assert: IsPropertyKey(P) is true.\n    // 2. Let hasOwn be ? O.[[GetOwnProperty]](P).\n    // 3. If hasOwn is not undefined, return true.\n    if obj.__get_own_property__(key, context)?.is_some() {\n        Ok(true)\n    } else {\n        // 4. Let parent be ? O.[[GetPrototypeOf]]().\n        let parent = obj.__get_prototype_of__(context)?;\n\n        context.slot().set_not_cacheable_if_already_prototype();\n        context.slot().attributes |= SlotAttributes::PROTOTYPE;\n\n        parent\n            // 5. If parent is not null, then\n            // a. Return ? parent.[[HasProperty]](P).\n            // 6. Return false.\n            .map_or(Ok(false), |obj| obj.__has_property__(key, context))\n    }\n}\n\n/// Abstract operation `OrdinaryGet`.\n///\n/// More information:\n///  - [ECMAScript reference][spec]\n///\n/// [spec]: https://tc39.es/ecma262/#sec-ordinaryget\npub(crate) fn ordinary_get(\n    obj: &JsObject,\n    key: &PropertyKey,\n    receiver: JsValue,\n    context: &mut InternalMethodPropertyContext<'_>,\n) -> JsResult<JsValue> {\n    // 1. Assert: IsPropertyKey(P) is true.\n    // 2. Let desc be ? O.[[GetOwnProperty]](P).\n    match obj.__get_own_property__(key, context)? {\n        // If desc is undefined, then\n        None => {\n            // a. Let parent be ? O.[[GetPrototypeOf]]().\n            if let Some(parent) = obj.__get_prototype_of__(context)? {\n                context.slot().set_not_cacheable_if_already_prototype();\n                context.slot().attributes |= SlotAttributes::PROTOTYPE;\n\n                // c. Return ? parent.[[Get]](P, Receiver).\n                parent.__get__(key, receiver, context)\n            }\n            // b. If parent is null, return undefined.\n            else {\n                Ok(JsValue::undefined())\n            }\n        }\n        Some(ref desc) => {\n            match desc.kind() {\n                // 4. If IsDataDescriptor(desc) is true, return desc.[[Value]].\n                DescriptorKind::Data {\n                    value: Some(value), ..\n                } => Ok(value.clone()),\n                // 5. Assert: IsAccessorDescriptor(desc) is true.\n                // 6. Let getter be desc.[[Get]].\n                DescriptorKind::Accessor { get: Some(get), .. } if !get.is_undefined() => {\n                    // 8. Return ? Call(getter, Receiver).\n                    get.call(&receiver, &[], context)\n                }\n                // 7. If getter is undefined, return undefined.\n                _ => Ok(JsValue::undefined()),\n            }\n        }\n    }\n}\n\n/// Abstract optimization operation.\n///\n/// This operation combines the abstract operations `OrdinaryHasProperty` and `OrdinaryGet`.\n///\n/// More information:\n///  - [ECMAScript reference OrdinaryHasProperty][spec0]\n///  - [ECMAScript reference OrdinaryGet][spec1]\n///\n/// [spec0]: https://tc39.es/ecma262/#sec-ordinaryhasproperty\n/// [spec1]: https://tc39.es/ecma262/#sec-ordinaryget\npub(crate) fn ordinary_try_get(\n    obj: &JsObject,\n    key: &PropertyKey,\n    receiver: JsValue,\n    context: &mut InternalMethodPropertyContext<'_>,\n) -> JsResult<Option<JsValue>> {\n    // 1. Assert: IsPropertyKey(P) is true.\n    // 2. Let desc be ? O.[[GetOwnProperty]](P).\n    match obj.__get_own_property__(key, context)? {\n        // If desc is undefined, then\n        None => {\n            // a. Let parent be ? O.[[GetPrototypeOf]]().\n            if let Some(parent) = obj.__get_prototype_of__(context)? {\n                context.slot().set_not_cacheable_if_already_prototype();\n                context.slot().attributes |= SlotAttributes::PROTOTYPE;\n\n                // c. Return ? parent.[[Get]](P, Receiver).\n                parent.__try_get__(key, receiver, context)\n            }\n            // b. If parent is null, return undefined.\n            else {\n                Ok(None)\n            }\n        }\n        Some(ref desc) => {\n            match desc.kind() {\n                // 4. If IsDataDescriptor(desc) is true, return desc.[[Value]].\n                DescriptorKind::Data {\n                    value: Some(value), ..\n                } => Ok(Some(value.clone())),\n                // 5. Assert: IsAccessorDescriptor(desc) is true.\n                // 6. Let getter be desc.[[Get]].\n                DescriptorKind::Accessor { get: Some(get), .. } if !get.is_undefined() => {\n                    // 8. Return ? Call(getter, Receiver).\n                    get.call(&receiver, &[], context).map(Some)\n                }\n                // 7. If getter is undefined, return undefined.\n                _ => Ok(Some(JsValue::undefined())),\n            }\n        }\n    }\n}\n\n/// Abstract operation `OrdinarySet`.\n///\n/// More information:\n///  - [ECMAScript reference][spec]\n///\n/// [spec]: https://tc39.es/ecma262/#sec-ordinaryset\npub(crate) fn ordinary_set(\n    obj: &JsObject,\n    key: PropertyKey,\n    value: JsValue,\n    receiver: JsValue,\n    context: &mut InternalMethodPropertyContext<'_>,\n) -> JsResult<bool> {\n    // 1. Assert: IsPropertyKey(P) is true.\n    // 2. Let ownDesc be ? O.[[GetOwnProperty]](P).\n    // 3. Return OrdinarySetWithOwnDescriptor(O, P, V, Receiver, ownDesc).\n\n    // OrdinarySetWithOwnDescriptor ( O, P, V, Receiver, ownDesc )\n    // https://tc39.es/ecma262/multipage/ordinary-and-exotic-objects-behaviours.html#sec-ordinarysetwithowndescriptor\n\n    let mut has_own_desc = false;\n\n    // 1. Assert: IsPropertyKey(P) is true.\n    let own_desc = if let Some(desc) = obj.__get_own_property__(&key, context)? {\n        has_own_desc = true;\n        desc\n    }\n    // 2. If ownDesc is undefined, then\n    // a. Let parent be ? O.[[GetPrototypeOf]]().\n    // b. If parent is not null, then\n    else if let Some(parent) = obj.__get_prototype_of__(context)? {\n        context.slot().set_not_cacheable_if_already_prototype();\n        context.slot().attributes |= SlotAttributes::PROTOTYPE;\n\n        // i. Return ? parent.[[Set]](P, V, Receiver).\n        return parent.__set__(key, value, receiver, context);\n    }\n    // c. Else,\n    else {\n        // It's not on prototype chain.\n        context\n            .slot()\n            .attributes\n            .remove(SlotAttributes::PROTOTYPE | SlotAttributes::NOT_CACHEABLE);\n\n        // i. Set ownDesc to the PropertyDescriptor { [[Value]]: undefined, [[Writable]]: true,\n        // [[Enumerable]]: true, [[Configurable]]: true }.\n        PropertyDescriptor::builder()\n            .value(JsValue::undefined())\n            .writable(true)\n            .enumerable(true)\n            .configurable(true)\n            .build()\n    };\n\n    // 3. If IsDataDescriptor(ownDesc) is true, then\n    if own_desc.is_data_descriptor() {\n        // a. If ownDesc.[[Writable]] is false, return false.\n        if !own_desc.expect_writable() {\n            return Ok(false);\n        }\n\n        // b. If Type(Receiver) is not Object, return false.\n        let Some(receiver) = receiver.as_object() else {\n            return Ok(false);\n        };\n\n        let obj_is_receiver = JsObject::equals(obj, &receiver);\n\n        // NOTE(HaledOdat): If the object and receiver are not the same then it's not inline cacheable for now.\n        context\n            .slot()\n            .attributes\n            .set(SlotAttributes::NOT_CACHEABLE, !obj_is_receiver);\n\n        // OPTIMIZATION: If obj and receiver are the same, there's no need to call [[GetOwnProperty]](P)\n        //              again because it was already performed above.\n        let existing_descriptor = if has_own_desc && obj_is_receiver {\n            Some(own_desc)\n        } else {\n            // c. Let existingDescriptor be ? Receiver.[[GetOwnProperty]](P).\n            receiver.__get_own_property__(&key, context)?\n        };\n\n        // d. If existingDescriptor is not undefined, then\n        if let Some(ref existing_desc) = existing_descriptor {\n            // i. If IsAccessorDescriptor(existingDescriptor) is true, return false.\n            if existing_desc.is_accessor_descriptor() {\n                return Ok(false);\n            }\n\n            // ii. If existingDescriptor.[[Writable]] is false, return false.\n            if !existing_desc.expect_writable() {\n                return Ok(false);\n            }\n\n            // iii. Let valueDesc be the PropertyDescriptor { [[Value]]: V }.\n            // iv. Return ? Receiver.[[DefineOwnProperty]](P, valueDesc).\n            return receiver.__define_own_property__(\n                &key,\n                PropertyDescriptor::builder().value(value).build(),\n                context,\n            );\n        }\n\n        // e. Else\n        // i. Assert: Receiver does not currently have a property P.\n        // ii. Return ? CreateDataProperty(Receiver, P, V).\n        return receiver.create_data_property_with_slot(key, value, context);\n    }\n\n    // 4. Assert: IsAccessorDescriptor(ownDesc) is true.\n    debug_assert!(own_desc.is_accessor_descriptor());\n\n    // 5. Let setter be ownDesc.[[Set]].\n    match own_desc.set() {\n        Some(set) if !set.is_undefined() => {\n            // 7. Perform ? Call(setter, Receiver, « V »).\n            set.call(&receiver, &[value], context)?;\n\n            // 8. Return true.\n            Ok(true)\n        }\n        // 6. If setter is undefined, return false.\n        _ => Ok(false),\n    }\n}\n\n/// Abstract operation `OrdinaryDelete`.\n///\n/// More information:\n///  - [ECMAScript reference][spec]\n///\n/// [spec]: https://tc39.es/ecma262/#sec-ordinarydelete\npub(crate) fn ordinary_delete(\n    obj: &JsObject,\n    key: &PropertyKey,\n    context: &mut InternalMethodPropertyContext<'_>,\n) -> JsResult<bool> {\n    // 1. Assert: IsPropertyKey(P) is true.\n    Ok(\n        // 2. Let desc be ? O.[[GetOwnProperty]](P).\n        match obj.__get_own_property__(key, context)? {\n            // 4. If desc.[[Configurable]] is true, then\n            Some(desc) if desc.expect_configurable() => {\n                // a. Remove the own property with name P from O.\n                obj.borrow_mut().remove(key);\n                // b. Return true.\n                true\n            }\n            // 5. Return false.\n            Some(_) => false,\n            // 3. If desc is undefined, return true.\n            None => true,\n        },\n    )\n}\n\n/// Abstract operation `OrdinaryOwnPropertyKeys`.\n///\n/// More information:\n///  - [ECMAScript reference][spec]\n///\n/// [spec]: https://tc39.es/ecma262/#sec-ordinaryownpropertykeys\n#[allow(clippy::unnecessary_wraps)]\npub(crate) fn ordinary_own_property_keys(\n    obj: &JsObject,\n    _context: &mut Context,\n) -> JsResult<Vec<PropertyKey>> {\n    // 1. Let keys be a new empty List.\n    let mut keys = Vec::new();\n\n    let ordered_indexes = {\n        let mut indexes: Vec<_> = obj.borrow().properties.index_property_keys().collect();\n        indexes.sort_unstable();\n        indexes\n    };\n\n    // 2. For each own property key P of O such that P is an array index, in ascending numeric index order, do\n    // a. Add P as the last element of keys.\n    keys.extend(ordered_indexes.into_iter().map(Into::into));\n\n    // 3. For each own property key P of O such that Type(P) is String and P is not an array index, in ascending chronological order of property creation, do\n    //     a. Add P as the last element of keys.\n    //\n    // 4. For each own property key P of O such that Type(P) is Symbol, in ascending chronological order of property creation, do\n    //     a. Add P as the last element of keys.\n    keys.extend(obj.borrow().properties.shape.keys());\n\n    // 5. Return keys.\n    Ok(keys)\n}\n\n/// Abstract operation `IsCompatiblePropertyDescriptor`\n///\n/// More information:\n///  - [ECMAScript reference][spec]\n///\n/// [spec]: https://tc39.es/ecma262/#sec-iscompatiblepropertydescriptor\npub(crate) fn is_compatible_property_descriptor(\n    extensible: bool,\n    desc: PropertyDescriptor,\n    current: Option<PropertyDescriptor>,\n) -> bool {\n    // 1. Return ValidateAndApplyPropertyDescriptor(undefined, undefined, Extensible, Desc, Current).\n    validate_and_apply_property_descriptor(None, extensible, desc, current, &mut Slot::new())\n}\n\n/// Abstract operation `ValidateAndApplyPropertyDescriptor`\n///\n/// More information:\n///  - [ECMAScript reference][spec]\n///\n/// [spec]: https://tc39.es/ecma262/#sec-validateandapplypropertydescriptor\npub(crate) fn validate_and_apply_property_descriptor(\n    obj_and_key: Option<(&JsObject, &PropertyKey)>,\n    extensible: bool,\n    desc: PropertyDescriptor,\n    current: Option<PropertyDescriptor>,\n    slot: &mut Slot,\n) -> bool {\n    // 1. Assert: If O is not undefined, then IsPropertyKey(P) is true.\n\n    let Some(mut current) = current else {\n        // 2. If current is undefined, then\n        // a. If extensible is false, return false.\n        if !extensible {\n            return false;\n        }\n\n        // b. Assert: extensible is true.\n\n        if let Some((obj, key)) = obj_and_key {\n            obj.borrow_mut().properties.insert_with_slot(\n                key,\n                // c. If IsGenericDescriptor(Desc) is true or IsDataDescriptor(Desc) is true, then\n                if desc.is_generic_descriptor() || desc.is_data_descriptor() {\n                    // i. If O is not undefined, create an own data property named P of\n                    // object O whose [[Value]], [[Writable]], [[Enumerable]], and\n                    // [[Configurable]] attribute values are described by Desc.\n                    // If the value of an attribute field of Desc is absent, the attribute\n                    // of the newly created property is set to its default value.\n                    desc.into_data_defaulted()\n                }\n                // d. Else,\n                else {\n                    // i. Assert: ! IsAccessorDescriptor(Desc) is true.\n\n                    // ii. If O is not undefined, create an own accessor property named P\n                    // of object O whose [[Get]], [[Set]], [[Enumerable]], and [[Configurable]]\n                    // attribute values are described by Desc. If the value of an attribute field\n                    // of Desc is absent, the attribute of the newly created property is set to\n                    // its default value.\n                    desc.into_accessor_defaulted()\n                },\n                slot,\n            );\n        }\n\n        // e. Return true.\n        return true;\n    };\n\n    // 3. If every field in Desc is absent, return true.\n    if desc.is_empty() {\n        return true;\n    }\n\n    // 4. If current.[[Configurable]] is false, then\n    if !current.expect_configurable() {\n        // a. If Desc.[[Configurable]] is present and its value is true, return false.\n        if matches!(desc.configurable(), Some(true)) {\n            return false;\n        }\n\n        // b. If Desc.[[Enumerable]] is present and ! SameValue(Desc.[[Enumerable]], current.[[Enumerable]])\n        // is false, return false.\n        if matches!(desc.enumerable(), Some(desc_enum) if desc_enum != current.expect_enumerable())\n        {\n            return false;\n        }\n    }\n\n    // 5. If ! IsGenericDescriptor(Desc) is true, then\n    if desc.is_generic_descriptor() {\n        // a. NOTE: No further validation is required.\n    }\n    // 6. Else if ! SameValue(! IsDataDescriptor(current), ! IsDataDescriptor(Desc)) is false, then\n    else if current.is_data_descriptor() != desc.is_data_descriptor() {\n        // a. If current.[[Configurable]] is false, return false.\n        if !current.expect_configurable() {\n            return false;\n        }\n\n        if obj_and_key.is_some() {\n            // b. If IsDataDescriptor(current) is true, then\n            if current.is_data_descriptor() {\n                // i. If O is not undefined, convert the property named P of object O from a data\n                // property to an accessor property. Preserve the existing values of the converted\n                // property's [[Configurable]] and [[Enumerable]] attributes and set the rest of\n                // the property's attributes to their default values.\n                current = current.into_accessor_defaulted();\n            }\n            // c. Else,\n            else {\n                // i. If O is not undefined, convert the property named P of object O from an\n                // accessor property to a data property. Preserve the existing values of the\n                // converted property's [[Configurable]] and [[Enumerable]] attributes and set\n                // the rest of the property's attributes to their default values.\n                current = current.into_data_defaulted();\n            }\n        }\n    }\n    // 7. Else if IsDataDescriptor(current) and IsDataDescriptor(Desc) are both true, then\n    else if current.is_data_descriptor() && desc.is_data_descriptor() {\n        // a. If current.[[Configurable]] is false and current.[[Writable]] is false, then\n        if !current.expect_configurable() && !current.expect_writable() {\n            // i. If Desc.[[Writable]] is present and Desc.[[Writable]] is true, return false.\n            if matches!(desc.writable(), Some(true)) {\n                return false;\n            }\n            // ii. If Desc.[[Value]] is present and SameValue(Desc.[[Value]], current.[[Value]]) is false, return false.\n            if matches!(desc.value(), Some(value) if !JsValue::same_value(value, current.expect_value()))\n            {\n                return false;\n            }\n            // iii. Return true.\n            return true;\n        }\n    }\n    // 8. Else,\n    // a. Assert: ! IsAccessorDescriptor(current) and ! IsAccessorDescriptor(Desc) are both true.\n    // b. If current.[[Configurable]] is false, then\n    else if !current.expect_configurable() {\n        // i. If Desc.[[Set]] is present and SameValue(Desc.[[Set]], current.[[Set]]) is false, return false.\n        if matches!(desc.set(), Some(set) if !JsValue::same_value(set, current.expect_set())) {\n            return false;\n        }\n\n        // ii. If Desc.[[Get]] is present and SameValue(Desc.[[Get]], current.[[Get]]) is false, return false.\n        if matches!(desc.get(), Some(get) if !JsValue::same_value(get, current.expect_get())) {\n            return false;\n        }\n        // iii. Return true.\n        return true;\n    }\n\n    // 9. If O is not undefined, then\n    if let Some((obj, key)) = obj_and_key {\n        // a. For each field of Desc that is present, set the corresponding attribute of the\n        // property named P of object O to the value of the field.\n        current.fill_with(desc);\n        obj.borrow_mut()\n            .properties\n            .insert_with_slot(key, current, slot);\n        slot.attributes |= SlotAttributes::FOUND;\n    }\n\n    // 10. Return true.\n    true\n}\n\n/// Abstract operation `GetPrototypeFromConstructor`\n///\n/// More information:\n///  - [ECMAScript reference][spec]\n///\n/// [spec]: https://tc39.es/ecma262/#sec-getprototypefromconstructor\n#[track_caller]\npub(crate) fn get_prototype_from_constructor<F>(\n    constructor: &JsValue,\n    default: F,\n    context: &mut Context,\n) -> JsResult<JsObject>\nwhere\n    F: FnOnce(&StandardConstructors) -> &StandardConstructor,\n{\n    // 1. Assert: intrinsicDefaultProto is this specification's name of an intrinsic\n    // object.\n    // The corresponding object must be an intrinsic that is intended to be used\n    // as the [[Prototype]] value of an object.\n    // 2. Let proto be ? Get(constructor, \"prototype\").\n    let realm = if let Some(constructor) = constructor.as_object() {\n        if let Some(proto) = constructor.get(PROTOTYPE, context)?.as_object() {\n            return Ok(proto.clone());\n        }\n        // 3. If Type(proto) is not Object, then\n        // a. Let realm be ? GetFunctionRealm(constructor).\n        constructor.get_function_realm(context)?\n    } else {\n        context.realm().clone()\n    };\n    // b. Set proto to realm's intrinsic object named intrinsicDefaultProto.\n    Ok(default(realm.intrinsics().constructors()).prototype())\n}\n\nfn non_existent_call(\n    _obj: &JsObject,\n    _argument_count: usize,\n    context: &mut InternalMethodCallContext<'_>,\n) -> JsResult<CallValue> {\n    Err(JsNativeError::typ()\n        .with_message(\"not a callable function\")\n        .with_realm(context.realm().clone())\n        .into())\n}\n\nfn non_existent_construct(\n    _obj: &JsObject,\n    _argument_count: usize,\n    context: &mut InternalMethodCallContext<'_>,\n) -> JsResult<CallValue> {\n    Err(JsNativeError::typ()\n        .with_message(\"not a constructor\")\n        .with_realm(context.realm().clone())\n        .into())\n}\n"
  },
  {
    "path": "core/engine/src/object/internal_methods/string.rs",
    "content": "use crate::{\n    Context, JsExpect, JsResult, JsString,\n    object::{JsData, JsObject},\n    property::{PropertyDescriptor, PropertyKey},\n};\n\nuse super::{InternalMethodPropertyContext, InternalObjectMethods, ORDINARY_INTERNAL_METHODS};\n\nimpl JsData for JsString {\n    fn internal_methods(&self) -> &'static InternalObjectMethods {\n        static METHODS: InternalObjectMethods = InternalObjectMethods {\n            __get_own_property__: string_exotic_get_own_property,\n            __define_own_property__: string_exotic_define_own_property,\n            __own_property_keys__: string_exotic_own_property_keys,\n            ..ORDINARY_INTERNAL_METHODS\n        };\n\n        &METHODS\n    }\n}\n\n/// Gets own property of 'String' exotic object\n///\n/// More information:\n///  - [ECMAScript reference][spec]\n///\n/// [spec]: https://tc39.es/ecma262/#sec-string-exotic-objects-getownproperty-p\npub(crate) fn string_exotic_get_own_property(\n    obj: &JsObject,\n    key: &PropertyKey,\n    context: &mut InternalMethodPropertyContext<'_>,\n) -> JsResult<Option<PropertyDescriptor>> {\n    // 1. Assert: IsPropertyKey(P) is true.\n    // 2. Let desc be OrdinaryGetOwnProperty(S, P).\n    let desc = super::ordinary_get_own_property(obj, key, context)?;\n\n    // 3. If desc is not undefined, return desc.\n    if desc.is_some() {\n        Ok(desc)\n    } else {\n        // 4. Return ! StringGetOwnProperty(S, P).\n        Ok(string_get_own_property(obj, key))\n    }\n}\n\n/// Defines own property of 'String' exotic object\n///\n/// More information:\n///  - [ECMAScript reference][spec]\n///\n/// [spec]: https://tc39.es/ecma262/#sec-string-exotic-objects-defineownproperty-p-desc\npub(crate) fn string_exotic_define_own_property(\n    obj: &JsObject,\n    key: &PropertyKey,\n    desc: PropertyDescriptor,\n    context: &mut InternalMethodPropertyContext<'_>,\n) -> JsResult<bool> {\n    // 1. Assert: IsPropertyKey(P) is true.\n    // 2. Let stringDesc be ! StringGetOwnProperty(S, P).\n    let string_desc = string_get_own_property(obj, key);\n\n    // 3. If stringDesc is not undefined, then\n    if let Some(string_desc) = string_desc {\n        // a. Let extensible be S.[[Extensible]].\n        let extensible = obj.borrow().extensible;\n        // b. Return ! IsCompatiblePropertyDescriptor(extensible, Desc, stringDesc).\n        Ok(super::is_compatible_property_descriptor(\n            extensible,\n            desc,\n            Some(string_desc),\n        ))\n    } else {\n        // 4. Return ! OrdinaryDefineOwnProperty(S, P, Desc).\n        super::ordinary_define_own_property(obj, key, desc, context)\n    }\n}\n\n/// Gets own property keys of 'String' exotic object\n///\n/// More information:\n///  - [ECMAScript reference][spec]\n///\n/// [spec]: https://tc39.es/ecma262/#sec-string-exotic-objects-ownpropertykeys\n#[allow(clippy::unnecessary_wraps)]\npub(crate) fn string_exotic_own_property_keys(\n    obj: &JsObject,\n    _context: &mut Context,\n) -> JsResult<Vec<PropertyKey>> {\n    // 2. Let str be O.[[StringData]].\n    // 3. Assert: Type(str) is String.\n    let string = obj\n        .downcast_ref::<JsString>()\n        .js_expect(\"string exotic method should only be callable from string objects\")?\n        .clone();\n    // 4. Let len be the length of str.\n    let len = string.len();\n\n    // 1. Let keys be a new empty List.\n    let mut keys = Vec::with_capacity(len);\n\n    // 5. For each integer i starting with 0 such that i < len, in ascending order, do\n    //      a. Add ! ToString(𝔽(i)) as the last element of keys.\n    keys.extend((0..len).map(Into::into));\n\n    // 6. For each own property key P of O such that P is an array index\n    // and ! ToIntegerOrInfinity(P) ≥ len, in ascending numeric index order, do\n    //      a. Add P as the last element of keys.\n    let mut remaining_indices: Vec<_> = obj\n        .borrow()\n        .properties\n        .index_property_keys()\n        .filter(|idx| (*idx as usize) >= len)\n        .collect();\n    remaining_indices.sort_unstable();\n    keys.extend(remaining_indices.into_iter().map(Into::into));\n\n    // 7. For each own property key P of O such that Type(P) is String and P is not\n    // an array index, in ascending chronological order of property creation, do\n    //      a. Add P as the last element of keys.\n\n    // 8. For each own property key P of O such that Type(P) is Symbol, in ascending\n    // chronological order of property creation, do\n    //      a. Add P as the last element of keys.\n    keys.extend(obj.borrow().properties.shape.keys());\n\n    // 9. Return keys.\n    Ok(keys)\n}\n\n/// `StringGetOwnProperty` abstract operation\n///\n/// More information:\n///  - [ECMAScript reference][spec]\n///\n/// [spec]: https://tc39.es/ecma262/#sec-stringgetownproperty\nfn string_get_own_property(obj: &JsObject, key: &PropertyKey) -> Option<PropertyDescriptor> {\n    // 1. Assert: S is an Object that has a [[StringData]] internal slot.\n    // 2. Assert: IsPropertyKey(P) is true.\n    // 3. If Type(P) is not String, return undefined.\n    // 4. Let index be ! CanonicalNumericIndexString(P).\n    // 5. If index is undefined, return undefined.\n    // 6. If IsIntegralNumber(index) is false, return undefined.\n    // 7. If index is -0𝔽, return undefined.\n    let pos = match key {\n        PropertyKey::Index(index) => index.get() as usize,\n        _ => return None,\n    };\n\n    // 8. Let str be S.[[StringData]].\n    // 9. Assert: Type(str) is String.\n    let string = obj\n        .downcast_ref::<JsString>()\n        .expect(\"string exotic method should only be callable from string objects\")\n        .clone();\n\n    // 10. Let len be the length of str.\n    // 11. If ℝ(index) < 0 or len ≤ ℝ(index), return undefined.\n    // 12. Let resultStr be the String value of length 1, containing one code unit from str, specifically the code unit at index ℝ(index).\n    let result_str = string.get(pos..=pos)?;\n\n    // 13. Return the PropertyDescriptor { [[Value]]: resultStr, [[Writable]]: false, [[Enumerable]]: true, [[Configurable]]: false }.\n    let desc = PropertyDescriptor::builder()\n        .value(result_str)\n        .writable(false)\n        .enumerable(true)\n        .configurable(false)\n        .build();\n\n    Some(desc)\n}\n"
  },
  {
    "path": "core/engine/src/object/jsobject.rs",
    "content": "//! This module implements the `JsObject` structure.\n//!\n//! The `JsObject` is a garbage collected Object.\n\nuse super::{\n    JsPrototype, NativeObject, Object, ObjectData, PrivateName, PropertyMap,\n    internal_methods::{InternalObjectMethods, ORDINARY_INTERNAL_METHODS},\n    shape::RootShape,\n};\nuse crate::{\n    Context, JsData, JsResult, JsString, JsSymbol, JsValue,\n    builtins::{\n        array::ARRAY_EXOTIC_INTERNAL_METHODS,\n        array_buffer::{ArrayBuffer, BufferObject, SharedArrayBuffer},\n        object::OrdinaryObject,\n    },\n    context::intrinsics::Intrinsics,\n    error::JsNativeError,\n    js_error, js_string,\n    property::{PropertyDescriptor, PropertyKey},\n    value::PreferredType,\n};\nuse boa_gc::{self, Finalize, Gc, GcRef, GcRefCell, GcRefMut, Trace};\nuse core::ptr::fn_addr_eq;\nuse std::collections::HashSet;\nuse std::{\n    cell::RefCell,\n    collections::HashMap,\n    error::Error,\n    fmt::{self, Debug, Display},\n    hash::Hash,\n    result::Result as StdResult,\n};\nuse thin_vec::ThinVec;\n\n#[cfg(not(feature = \"jsvalue-enum\"))]\nuse boa_gc::GcBox;\n\n#[cfg(not(feature = \"jsvalue-enum\"))]\nuse std::ptr::NonNull;\n\n/// A wrapper type for an immutably borrowed type T.\npub type Ref<'a, T> = GcRef<'a, T>;\n\n/// A wrapper type for a mutably borrowed type T.\npub type RefMut<'a, T> = GcRefMut<'a, T>;\n\npub(crate) type ErasedVTableObject = VTableObject<ErasedObjectData>;\n\n/// An `Object` with inner data set to `ErasedObjectData`.\npub type ErasedObject = Object<ErasedObjectData>;\n\n/// A erased object data type that must never be used directly.\n#[derive(Debug, Trace, Finalize)]\npub struct ErasedObjectData {}\n\nimpl JsData for ErasedObjectData {}\n\n/// Garbage collected `Object`.\n#[derive(Trace, Finalize)]\n#[boa_gc(unsafe_no_drop)]\npub struct JsObject<T: NativeObject = ErasedObjectData> {\n    inner: Gc<VTableObject<T>>,\n}\n\nimpl<T: NativeObject> Clone for JsObject<T> {\n    fn clone(&self) -> Self {\n        Self {\n            inner: self.inner.clone(),\n        }\n    }\n}\n\n/// An `Object` that has an additional `vtable` with its internal methods.\n// We have to skip implementing `Debug` for this because not using the\n// implementation of `Debug` for `JsObject` could easily cause stack overflows,\n// so we have to force our users to debug the `JsObject` instead.\n#[allow(missing_debug_implementations)]\n#[derive(Trace, Finalize)]\npub(crate) struct VTableObject<T: NativeObject + ?Sized> {\n    #[unsafe_ignore_trace]\n    vtable: &'static InternalObjectMethods,\n    object: GcRefCell<Object<T>>,\n}\n\nimpl JsObject {\n    /// Converts the `JsObject` into a raw pointer to its inner `GcBox<ErasedVTableObject>`.\n    #[cfg(not(feature = \"jsvalue-enum\"))]\n    pub(crate) fn into_raw(self) -> NonNull<GcBox<ErasedVTableObject>> {\n        Gc::into_raw(self.inner)\n    }\n\n    /// Creates a new `JsObject` from a raw pointer.\n    ///\n    /// # Safety\n    /// The caller must ensure that the pointer is valid and points to a `GcBox<ErasedVTableObject>`.\n    /// The pointer must not be null.\n    #[cfg(not(feature = \"jsvalue-enum\"))]\n    pub(crate) unsafe fn from_raw(raw: NonNull<GcBox<ErasedVTableObject>>) -> Self {\n        // SAFETY: The caller guaranteed the value to be a valid pointer to a `GcBox<ErasedVTableObject>`.\n        let inner = unsafe { Gc::from_raw(raw) };\n\n        JsObject { inner }\n    }\n\n    /// Creates a new ordinary object with its prototype set to the `Object` prototype.\n    ///\n    /// This is an alias for [`Self::with_object_proto`].\n    ///\n    /// # Examples\n    ///\n    /// ```\n    /// # use boa_engine::{Context, JsObject};\n    /// let context = &mut Context::default();\n    /// let obj = JsObject::default(context.intrinsics());\n    ///\n    /// assert!(obj.is_ordinary());\n    /// ```\n    #[inline]\n    #[must_use]\n    pub fn default(intrinsics: &Intrinsics) -> Self {\n        Self::with_object_proto(intrinsics)\n    }\n\n    /// Creates a new `JsObject` from its inner object and its vtable.\n    pub(crate) fn from_object_and_vtable<T: NativeObject>(\n        object: Object<T>,\n        vtable: &'static InternalObjectMethods,\n    ) -> Self {\n        let inner = Gc::new(VTableObject {\n            object: GcRefCell::new(object),\n            vtable,\n        });\n\n        JsObject { inner }.upcast()\n    }\n\n    /// Creates a new ordinary object with its prototype set to the `Object` prototype.\n    ///\n    /// This is equivalent to calling the specification's abstract operation\n    /// [`OrdinaryObjectCreate(%Object.prototype%)`][call].\n    ///\n    /// [call]: https://tc39.es/ecma262/#sec-ordinaryobjectcreate\n    ///\n    /// # Examples\n    ///\n    /// ```\n    /// # use boa_engine::{Context, JsObject};\n    /// let context = &mut Context::default();\n    /// let obj = JsObject::with_object_proto(context.intrinsics());\n    ///\n    /// assert!(obj.is_ordinary());\n    /// assert!(obj.prototype().is_some());\n    /// ```\n    #[inline]\n    #[must_use]\n    pub fn with_object_proto(intrinsics: &Intrinsics) -> Self {\n        Self::from_proto_and_data(\n            intrinsics.constructors().object().prototype(),\n            OrdinaryObject,\n        )\n    }\n\n    /// Creates a new ordinary object, with its prototype set to null.\n    ///\n    /// This is equivalent to calling the specification's abstract operation\n    /// [`OrdinaryObjectCreate(null)`][call].\n    ///\n    /// [call]: https://tc39.es/ecma262/#sec-ordinaryobjectcreate\n    ///\n    /// # Examples\n    ///\n    /// ```\n    /// # use boa_engine::JsObject;\n    /// let obj = JsObject::with_null_proto();\n    /// assert!(obj.prototype().is_none());\n    /// assert!(obj.is_ordinary());\n    /// ```\n    #[inline]\n    #[must_use]\n    pub fn with_null_proto() -> Self {\n        Self::from_proto_and_data(None, OrdinaryObject)\n    }\n\n    /// Creates a new object with the provided prototype and object data.\n    ///\n    /// This is equivalent to calling the specification's abstract operation [`OrdinaryObjectCreate`],\n    /// with the difference that the `additionalInternalSlotsList` parameter is determined by\n    /// the provided `data`.\n    ///\n    /// [`OrdinaryObjectCreate`]: https://tc39.es/ecma262/#sec-ordinaryobjectcreate\n    ///\n    /// # Examples\n    ///\n    /// ```\n    /// # use boa_engine::{Context, JsObject};\n    /// # use boa_engine::builtins::object::OrdinaryObject;\n    /// let context = &mut Context::default();\n    /// let obj = JsObject::from_proto_and_data(\n    ///     context.intrinsics().constructors().object().prototype(),\n    ///     OrdinaryObject,\n    /// );\n    ///\n    /// assert!(obj.is_ordinary());\n    /// assert!(obj.prototype().is_some());\n    ///\n    /// // Create an object with no prototype.\n    /// let null_obj = JsObject::from_proto_and_data(None, OrdinaryObject);\n    /// assert!(null_obj.prototype().is_none());\n    /// ```\n    pub fn from_proto_and_data<O: Into<Option<Self>>, T: NativeObject>(\n        prototype: O,\n        data: T,\n    ) -> Self {\n        let internal_methods = data.internal_methods();\n        let inner = Gc::new(VTableObject {\n            object: GcRefCell::new(Object {\n                data: ObjectData::new(data),\n                properties: PropertyMap::from_prototype_unique_shape(prototype.into()),\n                extensible: true,\n                private_elements: ThinVec::new(),\n            }),\n            vtable: internal_methods,\n        });\n\n        JsObject { inner }.upcast()\n    }\n\n    /// Creates a new object with the provided prototype and object data.\n    ///\n    /// This is equivalent to calling the specification's abstract operation [`OrdinaryObjectCreate`],\n    /// with the difference that the `additionalInternalSlotsList` parameter is determined by\n    /// the provided `data`.\n    ///\n    /// [`OrdinaryObjectCreate`]: https://tc39.es/ecma262/#sec-ordinaryobjectcreate\n    pub(crate) fn from_proto_and_data_with_shared_shape<O: Into<Option<Self>>, T: NativeObject>(\n        root_shape: &RootShape,\n        prototype: O,\n        data: T,\n    ) -> JsObject<T> {\n        let internal_methods = data.internal_methods();\n        let inner = Gc::new(VTableObject {\n            object: GcRefCell::new(Object {\n                data: ObjectData::new(data),\n                properties: PropertyMap::from_prototype_with_shared_shape(\n                    root_shape,\n                    prototype.into(),\n                ),\n                extensible: true,\n                private_elements: ThinVec::new(),\n            }),\n            vtable: internal_methods,\n        });\n\n        JsObject { inner }\n    }\n\n    /// Downcasts the object's inner data if the object is of type `T`.\n    ///\n    /// # Panics\n    ///\n    /// Panics if the object is currently mutably borrowed.\n    ///\n    /// # Examples\n    ///\n    /// ```\n    /// # use boa_engine::{JsObject, JsData, Trace, Finalize};\n    /// # use boa_engine::builtins::object::OrdinaryObject;\n    /// #[derive(Debug, Trace, Finalize, JsData)]\n    /// struct CustomStruct;\n    ///\n    /// let obj = JsObject::from_proto_and_data(None, OrdinaryObject);\n    ///\n    /// // Downcast consumes the object on success.\n    /// let typed = obj.downcast::<OrdinaryObject>();\n    /// assert!(typed.is_ok());\n    ///\n    /// // Downcast fails for a wrong type, returning the original object.\n    /// let obj = JsObject::from_proto_and_data(None, OrdinaryObject);\n    /// let result = obj.downcast::<CustomStruct>();\n    /// assert!(result.is_err());\n    /// ```\n    pub fn downcast<T: NativeObject>(self) -> Result<JsObject<T>, Self> {\n        if self.is::<T>() {\n            // SAFETY: We have verified that the object is of type `T`, so we can safely cast it.\n            let object = unsafe { self.downcast_unchecked::<T>() };\n\n            Ok(object)\n        } else {\n            Err(self)\n        }\n    }\n\n    /// Downcasts the object's inner data to `T` without verifying the inner type of `T`.\n    ///\n    /// # Safety\n    ///\n    /// For this cast to be sound, `self` must contain an instance of `T` inside its inner data.\n    #[must_use]\n    pub unsafe fn downcast_unchecked<T: NativeObject>(self) -> JsObject<T> {\n        // SAFETY: The caller guarantees `T` is the original inner data type of the underlying\n        // object.\n        // The pointer is guaranteed to be valid because we just created it.\n        // `VTableObject<ErasedObjectData>` and `VTableObject<T>` have the same size and alignment.\n        let inner = unsafe { Gc::cast_unchecked::<VTableObject<T>>(self.inner) };\n\n        JsObject { inner }\n    }\n\n    /// Downcasts a reference to the object,\n    /// if the object is of type `T`.\n    ///\n    /// # Panics\n    ///\n    /// Panics if the object is currently mutably borrowed.\n    ///\n    /// # Examples\n    ///\n    /// ```\n    /// # use boa_engine::{JsObject, JsData, Trace, Finalize};\n    /// # use boa_engine::builtins::object::OrdinaryObject;\n    /// #[derive(Debug, Trace, Finalize, JsData)]\n    /// struct CustomStruct;\n    ///\n    /// let obj = JsObject::from_proto_and_data(None, OrdinaryObject);\n    ///\n    /// // Downcast ref succeeds for the correct type.\n    /// assert!(obj.downcast_ref::<OrdinaryObject>().is_some());\n    ///\n    /// // Returns `None` for a wrong type.\n    /// assert!(obj.downcast_ref::<CustomStruct>().is_none());\n    /// ```\n    #[must_use]\n    #[track_caller]\n    pub fn downcast_ref<T: NativeObject>(&self) -> Option<Ref<'_, T>> {\n        if self.is::<T>() {\n            let obj = self.borrow();\n\n            // SAFETY: We have verified that the object is of type `T`, so we can safely cast it.\n            let obj = unsafe { GcRef::cast::<Object<T>>(obj) };\n\n            return Some(Ref::map(obj, |r| r.data()));\n        }\n        None\n    }\n\n    /// Downcasts a mutable reference to the object,\n    /// if the object is type native object type `T`.\n    ///\n    /// # Panics\n    ///\n    /// Panics if the object is currently borrowed.\n    ///\n    /// # Examples\n    ///\n    /// ```\n    /// # use boa_engine::{JsObject, JsData, Trace, Finalize};\n    /// # use boa_engine::builtins::object::OrdinaryObject;\n    /// #[derive(Debug, Trace, Finalize, JsData)]\n    /// struct CustomStruct;\n    ///\n    /// let obj = JsObject::from_proto_and_data(None, OrdinaryObject);\n    ///\n    /// // Downcast mut succeeds for the correct type.\n    /// assert!(obj.downcast_mut::<OrdinaryObject>().is_some());\n    ///\n    /// // Returns `None` for a wrong type.\n    /// assert!(obj.downcast_mut::<CustomStruct>().is_none());\n    /// ```\n    #[must_use]\n    #[track_caller]\n    pub fn downcast_mut<T: NativeObject>(&self) -> Option<RefMut<'_, T>> {\n        if self.is::<T>() {\n            let obj = self.borrow_mut();\n\n            // SAFETY: We have verified that the object is of type `T`, so we can safely cast it.\n            let obj = unsafe { GcRefMut::cast::<Object<T>>(obj) };\n\n            return Some(RefMut::map(obj, |c| c.data_mut()));\n        }\n        None\n    }\n\n    /// Checks if this object is an instance of a certain `NativeObject`.\n    ///\n    /// # Panics\n    ///\n    /// Panics if the object is currently mutably borrowed.\n    ///\n    /// # Examples\n    ///\n    /// ```\n    /// # use boa_engine::{JsObject, JsData, Trace, Finalize};\n    /// # use boa_engine::builtins::object::OrdinaryObject;\n    /// #[derive(Debug, Trace, Finalize, JsData)]\n    /// struct CustomStruct;\n    ///\n    /// let obj = JsObject::from_proto_and_data(None, OrdinaryObject);\n    ///\n    /// assert!(obj.is::<OrdinaryObject>());\n    /// assert!(!obj.is::<CustomStruct>());\n    /// ```\n    #[inline]\n    #[must_use]\n    #[track_caller]\n    pub fn is<T: NativeObject>(&self) -> bool {\n        Gc::is::<VTableObject<T>>(&self.inner)\n    }\n\n    /// Checks if it's an ordinary object.\n    ///\n    /// # Panics\n    ///\n    /// Panics if the object is currently mutably borrowed.\n    ///\n    /// # Examples\n    ///\n    /// ```\n    /// # use boa_engine::{Context, JsObject};\n    /// let context = &mut Context::default();\n    /// let obj = JsObject::with_object_proto(context.intrinsics());\n    ///\n    /// assert!(obj.is_ordinary());\n    /// ```\n    #[inline]\n    #[must_use]\n    #[track_caller]\n    pub fn is_ordinary(&self) -> bool {\n        self.is::<OrdinaryObject>()\n    }\n\n    /// Checks if it's an `Array` object.\n    ///\n    /// # Examples\n    ///\n    /// ```\n    /// # use boa_engine::{Context, JsObject, JsValue ,JsResult};\n    /// # use boa_engine::object::builtins::JsArray;\n    /// # fn main() -> JsResult<()> {\n    /// let context = &mut Context::default();\n    ///\n    /// let array = JsArray::new(context)?;\n    /// // A JsArray's inner JsObject is an array.\n    /// assert!(JsObject::from(array).is_array());\n    ///\n    /// // An ordinary object is not an array.\n    /// let obj = JsObject::with_object_proto(context.intrinsics());\n    /// assert!(!obj.is_array());\n    /// # Ok(())\n    /// # }\n    /// ```\n    #[inline]\n    #[must_use]\n    #[track_caller]\n    pub fn is_array(&self) -> bool {\n        std::ptr::eq(self.vtable(), &raw const ARRAY_EXOTIC_INTERNAL_METHODS)\n    }\n\n    /// The inner implementation of `deep_strict_equals`, which keeps a list of values we've\n    /// seen to avoid recursive objects.\n    pub(crate) fn deep_strict_equals_inner(\n        lhs: &Self,\n        rhs: &Self,\n        encounters: &mut HashSet<usize>,\n        context: &mut Context,\n    ) -> JsResult<bool> {\n        // Loop through all the keys and if one is not equal, return false.\n        fn key_loop(\n            lhs: &JsObject,\n            rhs: &JsObject,\n            encounters: &mut HashSet<usize>,\n            context: &mut Context,\n        ) -> JsResult<bool> {\n            let l_keys = lhs.own_property_keys(context)?;\n            let r_keys = rhs.own_property_keys(context)?;\n\n            if l_keys.len() != r_keys.len() {\n                return Ok(false);\n            }\n\n            for key in &l_keys {\n                let vl = lhs.get_property(key);\n                let vr = rhs.get_property(key);\n\n                match (vl, vr) {\n                    (None, None) => {}\n                    (Some(vl), Some(vr)) => match (vl.value(), vr.value()) {\n                        (None, None) => {}\n                        (Some(lv), Some(rv)) => {\n                            if !lv.deep_strict_equals_inner(rv, encounters, context)? {\n                                return Ok(false);\n                            }\n                        }\n                        _ => {\n                            return Ok(false);\n                        }\n                    },\n                    _ => {\n                        return Ok(false);\n                    }\n                }\n            }\n            Ok(true)\n        }\n\n        let addr_l = std::ptr::from_ref::<Self>(lhs) as usize;\n        let addr_r = std::ptr::from_ref::<Self>(rhs) as usize;\n\n        if addr_r == addr_l {\n            return Ok(true);\n        }\n\n        let contains_l = encounters.contains(&addr_l);\n        let contains_r = encounters.contains(&addr_r);\n        if contains_l || contains_r {\n            return Ok(false);\n        }\n\n        encounters.insert(addr_l);\n        encounters.insert(addr_r);\n\n        // Make sure we clean up after the recursion.\n        let result = key_loop(lhs, rhs, encounters, context);\n        encounters.remove(&addr_l);\n        encounters.remove(&addr_r);\n        result\n    }\n\n    /// Checks that all own property keys and values are equal (recursively).\n    ///\n    /// # Examples\n    ///\n    /// ```\n    /// # use boa_engine::{Context, JsObject, JsResult, js_string};\n    /// # fn main() -> JsResult<()> {\n    /// let context = &mut Context::default();\n    ///\n    /// let obj1 = JsObject::with_object_proto(context.intrinsics());\n    /// obj1.set(js_string!(\"key\"), 42, false, context)?;\n    ///\n    /// let obj2 = JsObject::with_object_proto(context.intrinsics());\n    /// obj2.set(js_string!(\"key\"), 42, false, context)?;\n    ///\n    /// assert!(JsObject::deep_strict_equals(&obj1, &obj2, context)?);\n    /// # Ok(())\n    /// # }\n    /// ```\n    #[inline]\n    pub fn deep_strict_equals(lhs: &Self, rhs: &Self, context: &mut Context) -> JsResult<bool> {\n        Self::deep_strict_equals_inner(lhs, rhs, &mut HashSet::new(), context)\n    }\n\n    /// The abstract operation `ToPrimitive` takes an input argument and an optional argument\n    /// `PreferredType`.\n    ///\n    /// <https://tc39.es/ecma262/#sec-toprimitive>\n    pub fn to_primitive(\n        &self,\n        context: &mut Context,\n        preferred_type: PreferredType,\n    ) -> JsResult<JsValue> {\n        // a. Let exoticToPrim be ? GetMethod(input, @@toPrimitive).\n        let Some(exotic_to_prim) = self.get_method(JsSymbol::to_primitive(), context)? else {\n            // c. If preferredType is not present, let preferredType be number.\n            let preferred_type = match preferred_type {\n                PreferredType::Default | PreferredType::Number => PreferredType::Number,\n                PreferredType::String => PreferredType::String,\n            };\n            return self.ordinary_to_primitive(context, preferred_type);\n        };\n\n        // b. If exoticToPrim is not undefined, then\n        //    i. If preferredType is not present, let hint be \"default\".\n        //    ii. Else if preferredType is string, let hint be \"string\".\n        //    iii. Else,\n        //        1. Assert: preferredType is number.\n        //        2. Let hint be \"number\".\n        let hint = match preferred_type {\n            PreferredType::Default => js_string!(\"default\"),\n            PreferredType::String => js_string!(\"string\"),\n            PreferredType::Number => js_string!(\"number\"),\n        }\n        .into();\n\n        //    iv. Let result be ? Call(exoticToPrim, input, « hint »).\n        let result = exotic_to_prim.call(&self.clone().into(), &[hint], context)?;\n\n        //    v. If Type(result) is not Object, return result.\n        //    vi. Throw a TypeError exception.\n        if result.is_object() {\n            Err(js_error!(\n                TypeError: \"method `[Symbol.toPrimitive]` cannot return an object\"\n            ))\n        } else {\n            Ok(result)\n        }\n    }\n\n    /// Converts an object to a primitive.\n    ///\n    /// Diverges from the spec to prevent a stack overflow when the object is recursive.\n    /// For example,\n    /// ```javascript\n    /// let a = [1];\n    /// a[1] = a;\n    /// console.log(a.toString()); // We print \"1,\"\n    /// ```\n    /// The spec doesn't mention what to do in this situation, but a naive implementation\n    /// would overflow the stack recursively calling `toString()`. We follow v8 and SpiderMonkey\n    /// instead by returning a default value for the given `hint` -- either `0.` or `\"\"`.\n    /// Example in v8: <https://repl.it/repls/IvoryCircularCertification#index.js>\n    ///\n    /// More information:\n    ///  - [ECMAScript][spec]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-ordinarytoprimitive\n    pub(crate) fn ordinary_to_primitive(\n        &self,\n        context: &mut Context,\n        hint: PreferredType,\n    ) -> JsResult<JsValue> {\n        // 1. Assert: Type(O) is Object.\n        //      Already is JsObject by type.\n        // 2. Assert: Type(hint) is String and its value is either \"string\" or \"number\".\n        debug_assert!(hint == PreferredType::String || hint == PreferredType::Number);\n\n        // Diverge from the spec here to make sure we aren't going to overflow the stack by converting\n        // a recursive structure\n        // We can follow v8 & SpiderMonkey's lead and return a default value for the hint in this situation\n        // (see https://repl.it/repls/IvoryCircularCertification#index.js)\n        let recursion_limiter = RecursionLimiter::new(self.as_ref());\n        if recursion_limiter.live {\n            // we're in a recursive object, bail\n            return Ok(match hint {\n                PreferredType::Number => JsValue::new(0),\n                PreferredType::String => JsValue::new(js_string!()),\n                PreferredType::Default => unreachable!(\"checked type hint in step 2\"),\n            });\n        }\n\n        // 3. If hint is \"string\", then\n        //    a. Let methodNames be « \"toString\", \"valueOf\" ».\n        // 4. Else,\n        //    a. Let methodNames be « \"valueOf\", \"toString\" ».\n        let method_names = if hint == PreferredType::String {\n            [js_string!(\"toString\"), js_string!(\"valueOf\")]\n        } else {\n            [js_string!(\"valueOf\"), js_string!(\"toString\")]\n        };\n\n        // 5. For each name in methodNames in List order, do\n        for name in method_names {\n            // a. Let method be ? Get(O, name).\n            let method = self.get(name, context)?;\n\n            // b. If IsCallable(method) is true, then\n            if let Some(method) = method.as_callable() {\n                // i. Let result be ? Call(method, O).\n                let result = method.call(&self.clone().into(), &[], context)?;\n\n                // ii. If Type(result) is not Object, return result.\n                if !result.is_object() {\n                    return Ok(result);\n                }\n            }\n        }\n\n        // 6. Throw a TypeError exception.\n        Err(JsNativeError::typ()\n            .with_message(\"cannot convert object to primitive value\")\n            .into())\n    }\n\n    /// The abstract operation `ToPropertyDescriptor`.\n    ///\n    /// More information:\n    /// - [ECMAScript reference][spec]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-topropertydescriptor\n    pub fn to_property_descriptor(&self, context: &mut Context) -> JsResult<PropertyDescriptor> {\n        // 1 is implemented on the method `to_property_descriptor` of value\n\n        // 2. Let desc be a new Property Descriptor that initially has no fields.\n        let mut desc = PropertyDescriptor::builder();\n\n        // 3. Let hasEnumerable be ? HasProperty(Obj, \"enumerable\").\n        // 4. If hasEnumerable is true, then ...\n        if let Some(enumerable) = self.try_get(js_string!(\"enumerable\"), context)? {\n            // a. Let enumerable be ! ToBoolean(? Get(Obj, \"enumerable\")).\n            // b. Set desc.[[Enumerable]] to enumerable.\n            desc = desc.enumerable(enumerable.to_boolean());\n        }\n\n        // 5. Let hasConfigurable be ? HasProperty(Obj, \"configurable\").\n        // 6. If hasConfigurable is true, then ...\n        if let Some(configurable) = self.try_get(js_string!(\"configurable\"), context)? {\n            // a. Let configurable be ! ToBoolean(? Get(Obj, \"configurable\")).\n            // b. Set desc.[[Configurable]] to configurable.\n            desc = desc.configurable(configurable.to_boolean());\n        }\n\n        // 7. Let hasValue be ? HasProperty(Obj, \"value\").\n        // 8. If hasValue is true, then ...\n        if let Some(value) = self.try_get(js_string!(\"value\"), context)? {\n            // a. Let value be ? Get(Obj, \"value\").\n            // b. Set desc.[[Value]] to value.\n            desc = desc.value(value);\n        }\n\n        // 9. Let hasWritable be ? HasProperty(Obj, ).\n        // 10. If hasWritable is true, then ...\n        if let Some(writable) = self.try_get(js_string!(\"writable\"), context)? {\n            // a. Let writable be ! ToBoolean(? Get(Obj, \"writable\")).\n            // b. Set desc.[[Writable]] to writable.\n            desc = desc.writable(writable.to_boolean());\n        }\n\n        // 11. Let hasGet be ? HasProperty(Obj, \"get\").\n        // 12. If hasGet is true, then\n        // 12.a. Let getter be ? Get(Obj, \"get\").\n        let get = if let Some(getter) = self.try_get(js_string!(\"get\"), context)? {\n            // b. If IsCallable(getter) is false and getter is not undefined, throw a TypeError exception.\n            // todo: extract IsCallable to be callable from Value\n            if !getter.is_undefined() && getter.as_object().is_none_or(|o| !o.is_callable()) {\n                return Err(JsNativeError::typ()\n                    .with_message(\"Property descriptor getter must be callable\")\n                    .into());\n            }\n            // c. Set desc.[[Get]] to getter.\n            Some(getter)\n        } else {\n            None\n        };\n\n        // 13. Let hasSet be ? HasProperty(Obj, \"set\").\n        // 14. If hasSet is true, then\n        // 14.a. Let setter be ? Get(Obj, \"set\").\n        let set = if let Some(setter) = self.try_get(js_string!(\"set\"), context)? {\n            // 14.b. If IsCallable(setter) is false and setter is not undefined, throw a TypeError exception.\n            // todo: extract IsCallable to be callable from Value\n            if !setter.is_undefined() && setter.as_object().is_none_or(|o| !o.is_callable()) {\n                return Err(JsNativeError::typ()\n                    .with_message(\"Property descriptor setter must be callable\")\n                    .into());\n            }\n            // 14.c. Set desc.[[Set]] to setter.\n            Some(setter)\n        } else {\n            None\n        };\n\n        // 15. If desc.[[Get]] is present or desc.[[Set]] is present, then ...\n        // a. If desc.[[Value]] is present or desc.[[Writable]] is present, throw a TypeError exception.\n        if get.as_ref().or(set.as_ref()).is_some() && desc.inner().is_data_descriptor() {\n            return Err(JsNativeError::typ()\n                .with_message(\n                    \"Invalid property descriptor.\\\nCannot both specify accessors and a value or writable attribute\",\n                )\n                .into());\n        }\n\n        desc = desc.maybe_get(get).maybe_set(set);\n\n        // 16. Return desc.\n        Ok(desc.build())\n    }\n\n    // Allow lint, false positive.\n    #[allow(clippy::assigning_clones)]\n    pub(crate) fn get_property(&self, key: &PropertyKey) -> Option<PropertyDescriptor> {\n        let mut obj = Some(self.clone());\n\n        while let Some(o) = obj {\n            if let Some(v) = o.borrow().properties.get(key) {\n                return Some(v);\n            }\n            obj = o.borrow().prototype().clone();\n        }\n        None\n    }\n\n    /// Casts to a `BufferObject` if the object is an `ArrayBuffer` or a `SharedArrayBuffer`.\n    #[inline]\n    pub(crate) fn into_buffer_object(self) -> Result<BufferObject, JsObject> {\n        match self.downcast::<ArrayBuffer>() {\n            Ok(buffer) => Ok(BufferObject::Buffer(buffer)),\n            Err(object) => object\n                .downcast::<SharedArrayBuffer>()\n                .map(BufferObject::SharedBuffer),\n        }\n    }\n}\n\nimpl<T: NativeObject> JsObject<T> {\n    /// Immutably borrows the `Object`.\n    ///\n    /// The borrow lasts until the returned `Ref` exits scope.\n    /// Multiple immutable borrows can be taken out at the same time.\n    ///\n    /// # Panics\n    ///\n    /// Panics if the object is currently mutably borrowed.\n    ///\n    /// # Examples\n    ///\n    /// ```\n    /// # use boa_engine::JsObject;\n    /// # use boa_engine::builtins::object::OrdinaryObject;\n    /// let obj = JsObject::from_proto_and_data(None, OrdinaryObject);\n    ///\n    /// // Multiple immutable borrows are allowed.\n    /// let borrowed = obj.borrow();\n    /// assert!(borrowed.prototype().is_none());\n    /// ```\n    #[inline]\n    #[must_use]\n    #[track_caller]\n    pub fn borrow(&self) -> Ref<'_, Object<T>> {\n        self.try_borrow().expect(\"Object already mutably borrowed\")\n    }\n\n    /// Mutably borrows the Object.\n    ///\n    /// The borrow lasts until the returned `RefMut` exits scope.\n    /// The object cannot be borrowed while this borrow is active.\n    ///\n    /// # Panics\n    /// Panics if the object is currently borrowed.\n    ///\n    /// # Examples\n    ///\n    /// ```\n    /// # use boa_engine::{Context, JsObject};\n    /// # use boa_engine::builtins::object::OrdinaryObject;\n    /// let context = &mut Context::default();\n    /// let obj = JsObject::from_proto_and_data(\n    ///     context.intrinsics().constructors().object().prototype(),\n    ///     OrdinaryObject,\n    /// );\n    ///\n    /// // Set the prototype to `None` via a mutable borrow.\n    /// obj.borrow_mut().set_prototype(None);\n    /// assert!(obj.prototype().is_none());\n    /// ```\n    #[inline]\n    #[must_use]\n    #[track_caller]\n    pub fn borrow_mut(&self) -> RefMut<'_, Object<T>> {\n        self.try_borrow_mut().expect(\"Object already borrowed\")\n    }\n\n    /// Immutably borrows the `Object`, returning an error if the value is currently mutably borrowed.\n    ///\n    /// The borrow lasts until the returned `GcCellRef` exits scope.\n    /// Multiple immutable borrows can be taken out at the same time.\n    ///\n    /// This is the non-panicking variant of [`borrow`](#method.borrow).\n    ///\n    /// # Examples\n    ///\n    /// ```\n    /// # use boa_engine::JsObject;\n    /// # use boa_engine::builtins::object::OrdinaryObject;\n    /// let obj = JsObject::from_proto_and_data(None, OrdinaryObject);\n    ///\n    /// // Non-panicking immutable borrow.\n    /// let result = obj.try_borrow();\n    /// assert!(result.is_ok());\n    /// ```\n    #[inline]\n    pub fn try_borrow(&self) -> StdResult<Ref<'_, Object<T>>, BorrowError> {\n        self.inner.object.try_borrow().map_err(|_| BorrowError)\n    }\n\n    /// Mutably borrows the object, returning an error if the value is currently borrowed.\n    ///\n    /// The borrow lasts until the returned `GcCellRefMut` exits scope.\n    /// The object be borrowed while this borrow is active.\n    ///\n    /// This is the non-panicking variant of [`borrow_mut`](#method.borrow_mut).\n    ///\n    /// # Examples\n    ///\n    /// ```\n    /// # use boa_engine::JsObject;\n    /// # use boa_engine::builtins::object::OrdinaryObject;\n    /// let obj = JsObject::from_proto_and_data(None, OrdinaryObject);\n    ///\n    /// // Non-panicking mutable borrow.\n    /// let result = obj.try_borrow_mut();\n    /// assert!(result.is_ok());\n    /// ```\n    #[inline]\n    pub fn try_borrow_mut(&self) -> StdResult<RefMut<'_, Object<T>>, BorrowMutError> {\n        self.inner\n            .object\n            .try_borrow_mut()\n            .map_err(|_| BorrowMutError)\n    }\n\n    /// Checks if the garbage collected memory is the same.\n    ///\n    /// # Examples\n    ///\n    /// ```\n    /// # use boa_engine::JsObject;\n    /// # use boa_engine::builtins::object::OrdinaryObject;\n    /// let obj = JsObject::from_proto_and_data(None, OrdinaryObject);\n    /// let clone = obj.clone();\n    ///\n    /// // A clone points to the same GC allocation.\n    /// assert!(JsObject::equals(&obj, &clone));\n    ///\n    /// // A separate object is different, even with identical data.\n    /// let other = JsObject::from_proto_and_data(None, OrdinaryObject);\n    /// assert!(!JsObject::equals(&obj, &other));\n    /// ```\n    #[must_use]\n    #[inline]\n    pub fn equals(lhs: &Self, rhs: &Self) -> bool {\n        Gc::ptr_eq(lhs.inner(), rhs.inner())\n    }\n\n    /// Get the prototype of the object.\n    ///\n    /// # Panics\n    ///\n    /// Panics if the object is currently mutably borrowed.\n    ///\n    /// # Examples\n    ///\n    /// ```\n    /// # use boa_engine::{Context, JsObject};\n    /// let context = &mut Context::default();\n    ///\n    /// let obj = JsObject::with_object_proto(context.intrinsics());\n    /// assert!(obj.prototype().is_some());\n    ///\n    /// let null_obj = JsObject::with_null_proto();\n    /// assert!(null_obj.prototype().is_none());\n    /// ```\n    #[inline]\n    #[must_use]\n    #[track_caller]\n    pub fn prototype(&self) -> JsPrototype {\n        self.borrow().prototype()\n    }\n\n    /// Get the extensibility of the object.\n    ///\n    /// # Panics\n    ///\n    /// Panics if the object is currently mutably borrowed.\n    pub(crate) fn extensible(&self) -> bool {\n        self.borrow().extensible\n    }\n\n    /// Set the prototype of the object.\n    ///\n    /// # Panics\n    ///\n    /// Panics if the object is currently mutably borrowed\n    ///\n    /// # Examples\n    ///\n    /// ```\n    /// # use boa_engine::{Context, JsObject};\n    /// let context = &mut Context::default();\n    /// let obj = JsObject::with_object_proto(context.intrinsics());\n    ///\n    /// assert!(obj.prototype().is_some());\n    ///\n    /// // Set the prototype to `None`.\n    /// obj.set_prototype(None);\n    /// assert!(obj.prototype().is_none());\n    /// ```\n    #[inline]\n    #[track_caller]\n    #[allow(clippy::must_use_candidate)]\n    pub fn set_prototype(&self, prototype: JsPrototype) -> bool {\n        self.borrow_mut().set_prototype(prototype)\n    }\n\n    /// Helper function for property insertion.\n    #[track_caller]\n    pub(crate) fn insert<K, P>(&self, key: K, property: P) -> bool\n    where\n        K: Into<PropertyKey>,\n        P: Into<PropertyDescriptor>,\n    {\n        self.borrow_mut().insert(key, property)\n    }\n\n    /// Inserts a field in the object `properties` without checking if it's writable.\n    ///\n    /// If a field was already in the object with the same name, than `true` is returned\n    /// with that field, otherwise `false` is returned.\n    pub fn insert_property<K, P>(&self, key: K, property: P) -> bool\n    where\n        K: Into<PropertyKey>,\n        P: Into<PropertyDescriptor>,\n    {\n        self.insert(key.into(), property)\n    }\n\n    /// It determines if Object is a callable function with a `[[Call]]` internal method.\n    ///\n    /// More information:\n    /// - [ECMAScript reference][spec]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-iscallable\n    #[inline]\n    #[must_use]\n    pub fn is_callable(&self) -> bool {\n        !fn_addr_eq(\n            self.inner.vtable.__call__,\n            ORDINARY_INTERNAL_METHODS.__call__,\n        )\n    }\n\n    /// It determines if Object is a function object with a `[[Construct]]` internal method.\n    ///\n    /// More information:\n    /// - [ECMAScript reference][spec]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-isconstructor\n    #[inline]\n    #[must_use]\n    pub fn is_constructor(&self) -> bool {\n        !fn_addr_eq(\n            self.inner.vtable.__construct__,\n            ORDINARY_INTERNAL_METHODS.__construct__,\n        )\n    }\n\n    pub(crate) fn vtable(&self) -> &'static InternalObjectMethods {\n        self.inner.vtable\n    }\n\n    pub(crate) fn inner(&self) -> &Gc<VTableObject<T>> {\n        &self.inner\n    }\n\n    pub(crate) fn from_inner(inner: Gc<VTableObject<T>>) -> Self {\n        Self { inner }\n    }\n\n    /// Create a new private name with this object as the unique identifier.\n    pub(crate) fn private_name(&self, description: JsString) -> PrivateName {\n        let ptr: *const _ = self.as_ref();\n        PrivateName::new(description, ptr.cast::<()>() as usize)\n    }\n}\n\nimpl<T: NativeObject> JsObject<T> {\n    /// Creates a new `JsObject` from its root shape, prototype, and data.\n    ///\n    /// Note that the returned object will not be erased to be convertible to a\n    /// `JsValue`. To erase the pointer, call [`JsObject::upcast`].\n    ///\n    /// # Examples\n    ///\n    /// ```\n    /// # use boa_engine::{Context, JsObject};\n    /// # use boa_engine::builtins::object::OrdinaryObject;\n    /// let context = &mut Context::default();\n    ///\n    /// let typed_obj = JsObject::new(\n    ///     context.root_shape(),\n    ///     context.intrinsics().constructors().object().prototype(),\n    ///     OrdinaryObject,\n    /// );\n    ///\n    /// // Upcast to an erased JsObject to use with JsValue.\n    /// let obj = typed_obj.upcast();\n    /// assert!(obj.is_ordinary());\n    /// ```\n    pub fn new<O: Into<Option<JsObject>>>(root_shape: &RootShape, prototype: O, data: T) -> Self {\n        let internal_methods = data.internal_methods();\n        let inner = Gc::new(VTableObject {\n            object: GcRefCell::new(Object {\n                data: ObjectData::new(data),\n                properties: PropertyMap::from_prototype_with_shared_shape(\n                    root_shape,\n                    prototype.into(),\n                ),\n                extensible: true,\n                private_elements: ThinVec::new(),\n            }),\n            vtable: internal_methods,\n        });\n\n        Self { inner }\n    }\n\n    /// Creates a new `JsObject` from prototype, and data.\n    ///\n    /// Note that the returned object will not be erased to be convertible to a\n    /// `JsValue`. To erase the pointer, call [`JsObject::upcast`].\n    ///\n    /// # Examples\n    ///\n    /// ```\n    /// # use boa_engine::JsObject;\n    /// # use boa_engine::builtins::object::OrdinaryObject;\n    /// let typed_obj = JsObject::new_unique(None, OrdinaryObject);\n    ///\n    /// // Upcast to an erased JsObject.\n    /// let obj = typed_obj.upcast();\n    /// assert!(obj.is_ordinary());\n    /// assert!(obj.prototype().is_none());\n    /// ```\n    pub fn new_unique<O: Into<Option<JsObject>>>(prototype: O, data: T) -> Self {\n        let internal_methods = data.internal_methods();\n        let inner = Gc::new(VTableObject {\n            object: GcRefCell::new(Object {\n                data: ObjectData::new(data),\n                properties: PropertyMap::from_prototype_unique_shape(prototype.into()),\n                extensible: true,\n                private_elements: ThinVec::new(),\n            }),\n            vtable: internal_methods,\n        });\n\n        Self { inner }\n    }\n\n    /// Upcasts this object's inner data from a specific type `T` to an erased type\n    /// `dyn NativeObject`.\n    ///\n    /// # Examples\n    ///\n    /// ```\n    /// # use boa_engine::JsObject;\n    /// # use boa_engine::builtins::object::OrdinaryObject;\n    /// // Create a typed JsObject<OrdinaryObject>.\n    /// let typed_obj = JsObject::new_unique(None, OrdinaryObject);\n    ///\n    /// // Upcast erases the type, producing an untyped JsObject.\n    /// let obj: JsObject = typed_obj.upcast();\n    /// assert!(obj.is_ordinary());\n    /// ```\n    #[must_use]\n    pub fn upcast(self) -> JsObject {\n        // SAFETY: The pointer is guaranteed to be valid.\n        // `VTableObject<ErasedObjectData>` and `VTableObject<T>` have the same size and alignment.\n        let inner = unsafe { Gc::cast_unchecked::<ErasedVTableObject>(self.inner) };\n\n        JsObject { inner }\n    }\n}\n\nimpl<T: NativeObject> AsRef<GcRefCell<Object<T>>> for JsObject<T> {\n    #[inline]\n    fn as_ref(&self) -> &GcRefCell<Object<T>> {\n        &self.inner.object\n    }\n}\n\nimpl<T: NativeObject> From<Gc<VTableObject<T>>> for JsObject<T> {\n    #[inline]\n    fn from(inner: Gc<VTableObject<T>>) -> Self {\n        Self { inner }\n    }\n}\n\nimpl<T: NativeObject> PartialEq for JsObject<T> {\n    fn eq(&self, other: &Self) -> bool {\n        Self::equals(self, other)\n    }\n}\n\nimpl<T: NativeObject> Eq for JsObject<T> {}\n\nimpl<T: NativeObject> Hash for JsObject<T> {\n    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {\n        std::ptr::hash(self.as_ref(), state);\n    }\n}\n\n/// An error returned by [`JsObject::try_borrow`](struct.JsObject.html#method.try_borrow).\n#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]\npub struct BorrowError;\n\nimpl Display for BorrowError {\n    #[inline]\n    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {\n        Display::fmt(\"Object already mutably borrowed\", f)\n    }\n}\n\nimpl Error for BorrowError {}\n\n/// An error returned by [`JsObject::try_borrow_mut`](struct.JsObject.html#method.try_borrow_mut).\n#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]\npub struct BorrowMutError;\n\nimpl Display for BorrowMutError {\n    #[inline]\n    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {\n        Display::fmt(\"Object already borrowed\", f)\n    }\n}\n\nimpl Error for BorrowMutError {}\n\n#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]\nenum RecursionValueState {\n    /// This value is \"live\": there's an active `RecursionLimiter` that hasn't been dropped.\n    Live,\n    /// This value has been seen before, but the recursion limiter has been dropped.\n    /// For example:\n    /// ```javascript\n    /// let b = [];\n    /// JSON.stringify([ // Create a recursion limiter for the root here\n    ///    b,            // state for b's &JsObject here is None\n    ///    b,            // state for b's &JsObject here is Visited\n    /// ]);\n    /// ```\n    Visited,\n}\n\n/// Prevents infinite recursion during `Debug::fmt`, `JSON.stringify`, and other conversions.\n/// This uses a thread local, so is not safe to use where the object graph will be traversed by\n/// multiple threads!\n#[derive(Debug)]\npub struct RecursionLimiter {\n    /// If this was the first `JsObject` in the tree.\n    top_level: bool,\n    /// The ptr being kept in the `HashSet`, so we can delete it when we drop.\n    ptr: usize,\n    /// If this `JsObject` has been visited before in the graph, but not in the current branch.\n    pub visited: bool,\n    /// If this `JsObject` has been visited in the current branch of the graph.\n    pub live: bool,\n}\n\nimpl Drop for RecursionLimiter {\n    fn drop(&mut self) {\n        if self.top_level {\n            // When the top level of the graph is dropped, we can free the entire map for the next traversal.\n            SEEN.with(|hm| hm.borrow_mut().clear());\n        } else if !self.live {\n            // This was the first RL for this object to become live, so it's no longer live now that it's dropped.\n            SEEN.with(|hm| {\n                hm.borrow_mut()\n                    .insert(self.ptr, RecursionValueState::Visited)\n            });\n        }\n    }\n}\n\nthread_local! {\n    /// The map of pointers to `JsObject` that have been visited during the current `Debug::fmt` graph,\n    /// and the current state of their RecursionLimiter (dropped or live -- see `RecursionValueState`)\n    static SEEN: RefCell<HashMap<usize, RecursionValueState>> = RefCell::new(HashMap::new());\n}\n\nimpl RecursionLimiter {\n    /// Determines if the specified `T` has been visited, and returns a struct that will free it when dropped.\n    ///\n    /// This is done by maintaining a thread-local hashset containing the pointers of `T` values that have been\n    /// visited. The first `T` visited will clear the hashset, while any others will check if they are contained\n    /// by the hashset.\n    pub fn new<T: ?Sized>(o: &T) -> Self {\n        let ptr: *const _ = o;\n        let ptr = ptr.cast::<()>() as usize;\n        let (top_level, visited, live) = SEEN.with(|hm| {\n            let mut hm = hm.borrow_mut();\n            let top_level = hm.is_empty();\n            let old_state = hm.insert(ptr, RecursionValueState::Live);\n\n            (\n                top_level,\n                old_state == Some(RecursionValueState::Visited),\n                old_state == Some(RecursionValueState::Live),\n            )\n        });\n\n        Self {\n            top_level,\n            ptr,\n            visited,\n            live,\n        }\n    }\n}\n\nimpl<T: NativeObject> Debug for JsObject<T> {\n    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {\n        let limiter = RecursionLimiter::new(self.as_ref());\n\n        // Typically, using `!limiter.live` would be good enough here.\n        // However, the JS object hierarchy involves quite a bit of repetition, and the sheer amount of data makes\n        // understanding the Debug output impossible; limiting the usefulness of it.\n        //\n        // Instead, we check if the object has appeared before in the entire graph. This means that objects will appear\n        // at most once, hopefully making things a bit clearer.\n        if !limiter.visited && !limiter.live {\n            let ptr: *const _ = self.as_ref();\n            let ptr = ptr.cast::<()>();\n            let obj = self.borrow();\n            let kind = obj.data().type_name_of_value();\n            if self.is_callable() {\n                let name_prop = obj\n                    .properties()\n                    .get(&PropertyKey::String(js_string!(\"name\")));\n                let name = match name_prop {\n                    None => JsString::default(),\n                    Some(prop) => prop\n                        .value()\n                        .and_then(JsValue::as_string)\n                        .unwrap_or_default(),\n                };\n\n                return f.write_fmt(format_args!(\"({:?}) {:?} 0x{:X}\", kind, name, ptr as usize));\n            }\n\n            f.write_fmt(format_args!(\"({:?}) 0x{:X}\", kind, ptr as usize))\n        } else {\n            f.write_str(\"{ ... }\")\n        }\n    }\n}\n"
  },
  {
    "path": "core/engine/src/object/mod.rs",
    "content": "//! Boa's representation of a JavaScript object and builtin object wrappers\n//!\n//! For the builtin object wrappers, please see [`object::builtins`][builtins] for implementors.\n\nuse datatypes::ObjectData;\npub use jsobject::{RecursionLimiter, Ref, RefMut};\npub use operations::IntegrityLevel;\npub use property_map::*;\nuse thin_vec::ThinVec;\n\nuse self::{internal_methods::ORDINARY_INTERNAL_METHODS, shape::Shape};\nuse crate::{\n    Context, JsString, JsSymbol, JsValue,\n    builtins::{OrdinaryObject, function::ConstructorKind},\n    context::intrinsics::StandardConstructor,\n    js_string,\n    native_function::{NativeFunction, NativeFunctionObject},\n    property::{Attribute, PropertyDescriptor, PropertyKey},\n    realm::Realm,\n    string::StaticJsStrings,\n};\n\nuse boa_gc::{Finalize, Trace};\nuse std::{\n    any::{Any, TypeId},\n    fmt::Debug,\n};\n\n#[cfg(test)]\nmod tests;\n\npub(crate) mod internal_methods;\n\npub mod builtins;\nmod datatypes;\nmod jsobject;\nmod operations;\nmod property_map;\n\npub mod shape;\n\npub(crate) use builtins::*;\n\npub use datatypes::JsData;\npub use jsobject::*;\n\n/// Const `constructor`, usually set on prototypes as a key to point to their respective constructor object.\npub const CONSTRUCTOR: JsString = js_string!(\"constructor\");\n\n/// Const `prototype`, usually set on constructors as a key to point to their respective prototype object.\npub const PROTOTYPE: JsString = js_string!(\"prototype\");\n\n/// A type alias for an object prototype.\n///\n/// A `None` values means that the prototype is the `null` value.\npub type JsPrototype = Option<JsObject>;\n\n/// The internal storage of an object's property values.\n///\n/// The [`shape::Shape`] contains the property names and attributes.\npub(crate) type ObjectStorage = Vec<JsValue>;\n\n/// This trait allows Rust types to be passed around as objects.\n///\n/// This is automatically implemented when a type implements `Any`, `Trace`, and `JsData`.\npub trait NativeObject: Any + Trace + JsData {\n    /// Convert the Rust type which implements `NativeObject` to a `&dyn Any`.\n    fn as_any(&self) -> &dyn Any;\n\n    /// Convert the Rust type which implements `NativeObject` to a `&mut dyn Any`.\n    fn as_mut_any(&mut self) -> &mut dyn Any;\n\n    /// Gets the type name of the value.\n    fn type_name_of_value(&self) -> &'static str;\n}\n\n// TODO: Use super trait casting in Rust 1.75\nimpl<T: Any + Trace + JsData> NativeObject for T {\n    fn as_any(&self) -> &dyn Any {\n        self\n    }\n\n    fn as_mut_any(&mut self) -> &mut dyn Any {\n        self\n    }\n\n    fn type_name_of_value(&self) -> &'static str {\n        fn name_of_val<T: ?Sized>(_val: &T) -> &'static str {\n            std::any::type_name::<T>()\n        }\n\n        name_of_val(self)\n    }\n}\n\n// TODO: Use super trait casting in Rust 1.75\nimpl dyn NativeObject {\n    /// Returns `true` if the inner type is the same as `T`.\n    #[inline]\n    pub fn is<T: NativeObject>(&self) -> bool {\n        // Get `TypeId` of the type this function is instantiated with.\n        let t = TypeId::of::<T>();\n\n        // Get `TypeId` of the type in the trait object (`self`).\n        let concrete = self.type_id();\n\n        // Compare both `TypeId`s on equality.\n        t == concrete\n    }\n\n    /// Returns some reference to the inner value if it is of type `T`, or\n    /// `None` if it isn't.\n    #[inline]\n    pub fn downcast_ref<T: NativeObject>(&self) -> Option<&T> {\n        if self.is::<T>() {\n            // SAFETY: just checked whether we are pointing to the correct type, and we can rely on\n            // that check for memory safety because we have implemented NativeObject for all types; no other\n            // impls can exist as they would conflict with our impl.\n            unsafe { Some(self.downcast_ref_unchecked()) }\n        } else {\n            None\n        }\n    }\n\n    /// Returns some mutable reference to the inner value if it is of type `T`, or\n    /// `None` if it isn't.\n    #[inline]\n    pub fn downcast_mut<T: NativeObject>(&mut self) -> Option<&mut T> {\n        if self.is::<T>() {\n            // SAFETY: Already checked if inner type is T, so this is safe.\n            unsafe { Some(self.downcast_mut_unchecked()) }\n        } else {\n            None\n        }\n    }\n\n    /// Returns a reference to the inner value as type `dyn T`.\n    ///\n    /// # Safety\n    ///\n    /// The contained value must be of type `T`. Calling this method\n    /// with the incorrect type is *undefined behavior*.\n    #[inline]\n    pub unsafe fn downcast_ref_unchecked<T: NativeObject>(&self) -> &T {\n        debug_assert!(self.is::<T>());\n        let ptr: *const dyn NativeObject = self;\n        // SAFETY: caller guarantees that T is the correct type\n        unsafe { &*ptr.cast::<T>() }\n    }\n\n    /// Returns a mutable reference to the inner value as type `dyn T`.\n    ///\n    /// # Safety\n    ///\n    /// The contained value must be of type `T`. Calling this method\n    /// with the incorrect type is *undefined behavior*.\n    #[inline]\n    pub unsafe fn downcast_mut_unchecked<T: NativeObject>(&mut self) -> &mut T {\n        debug_assert!(self.is::<T>());\n        // SAFETY: caller guarantees that T is the correct type\n        let ptr: *mut dyn NativeObject = self;\n        unsafe { &mut *ptr.cast::<T>() }\n    }\n}\n\n/// The internal representation of a JavaScript object.\n#[derive(Debug, Finalize, Trace)]\n// SAFETY: This does not implement drop, so this is safe.\n#[boa_gc(unsafe_no_drop)]\n// SAFETY: This type must use `#[repr(C)]` to prevent the compiler from reordering fields,\n//         as it is used for casting between types.\n#[repr(C)]\npub struct Object<T: ?Sized> {\n    /// The collection of properties contained in the object\n    pub(crate) properties: PropertyMap,\n    /// Whether it can have new properties added to it.\n    pub(crate) extensible: bool,\n    /// The `[[PrivateElements]]` internal slot.\n    private_elements: ThinVec<(PrivateName, PrivateElement)>,\n    /// The inner object data\n    data: ObjectData<T>,\n}\n\nimpl<T: Default> Default for Object<T> {\n    fn default() -> Self {\n        Self {\n            properties: PropertyMap::default(),\n            extensible: true,\n            private_elements: ThinVec::new(),\n            data: ObjectData::default(),\n        }\n    }\n}\n\n/// A Private Name.\n#[derive(Clone, Debug, PartialEq, Eq, Trace, Finalize)]\npub struct PrivateName {\n    /// The `[[Description]]` internal slot of the private name.\n    description: JsString,\n\n    /// The unique identifier of the private name.\n    id: usize,\n}\n\nimpl PrivateName {\n    /// Create a new private name.\n    pub(crate) const fn new(description: JsString, id: usize) -> Self {\n        Self { description, id }\n    }\n}\n\n/// The representation of private object elements.\n#[derive(Clone, Debug, Trace, Finalize)]\npub enum PrivateElement {\n    /// A private field.\n    Field(JsValue),\n\n    /// A private method.\n    Method(JsObject),\n\n    /// A private element accessor.\n    Accessor {\n        /// A getter function.\n        getter: Option<JsObject>,\n\n        /// A setter function.\n        setter: Option<JsObject>,\n    },\n}\n\nimpl<T: ?Sized> Object<T> {\n    /// Returns the shape of the object.\n    #[must_use]\n    pub const fn shape(&self) -> &Shape {\n        &self.properties.shape\n    }\n\n    /// Returns the data of the object.\n    #[inline]\n    #[must_use]\n    pub fn data(&self) -> &T {\n        self.data.as_ref()\n    }\n\n    /// Returns the data of the object.\n    #[inline]\n    #[must_use]\n    pub fn data_mut(&mut self) -> &mut T {\n        self.data.as_mut()\n    }\n\n    /// Gets the prototype instance of this object.\n    #[inline]\n    #[must_use]\n    pub fn prototype(&self) -> JsPrototype {\n        self.properties.shape.prototype()\n    }\n\n    /// Sets the prototype instance of the object.\n    ///\n    /// [More information][spec]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-invariants-of-the-essential-internal-methods\n    #[track_caller]\n    pub fn set_prototype<O: Into<JsPrototype>>(&mut self, prototype: O) -> bool {\n        let prototype = prototype.into();\n        if self.extensible {\n            self.properties.shape = self.properties.shape.change_prototype_transition(prototype);\n            true\n        } else {\n            // If target is non-extensible, [[SetPrototypeOf]] must return false\n            // unless V is the SameValue as the target's observed [[GetPrototypeOf]] value.\n            self.prototype() == prototype\n        }\n    }\n\n    /// Returns the properties of the object.\n    #[inline]\n    #[must_use]\n    pub const fn properties(&self) -> &PropertyMap {\n        &self.properties\n    }\n\n    #[inline]\n    pub(crate) fn properties_mut(&mut self) -> &mut PropertyMap {\n        &mut self.properties\n    }\n\n    /// Inserts a field in the object `properties` without checking if it's writable.\n    ///\n    /// If a field was already in the object with the same name, then `true` is returned\n    /// otherwise, `false` is returned.\n    pub(crate) fn insert<K, P>(&mut self, key: K, property: P) -> bool\n    where\n        K: Into<PropertyKey>,\n        P: Into<PropertyDescriptor>,\n    {\n        self.properties.insert(&key.into(), property.into())\n    }\n\n    /// Helper function for property removal without checking if it's configurable.\n    ///\n    /// Returns `true` if the property was removed, `false` otherwise.\n    #[inline]\n    pub(crate) fn remove(&mut self, key: &PropertyKey) -> bool {\n        self.properties.remove(key)\n    }\n\n    /// Append a private element to an object.\n    pub(crate) fn append_private_element(&mut self, name: PrivateName, element: PrivateElement) {\n        if let PrivateElement::Accessor { getter, setter } = &element {\n            for (key, value) in &mut self.private_elements {\n                if name == *key\n                    && let PrivateElement::Accessor {\n                        getter: existing_getter,\n                        setter: existing_setter,\n                    } = value\n                {\n                    if existing_getter.is_none() {\n                        existing_getter.clone_from(getter);\n                    }\n                    if existing_setter.is_none() {\n                        existing_setter.clone_from(setter);\n                    }\n                    return;\n                }\n            }\n        }\n\n        self.private_elements.push((name, element));\n    }\n}\n\n/// The functions binding.\n///\n/// Specifies what is the name of the function object (`name` property),\n/// and the binding name of the function object which can be different\n/// from the function name.\n///\n/// The only way to construct this is with the `From` trait.\n///\n/// There are two implementations:\n///  - From a single type `T` which implements `Into<FunctionBinding>` which sets the binding\n///    name and the function name to the same value.\n///  - From a tuple `(B: Into<PropertyKey>, N: Into<JsString>)`, where the `B` is the binding name\n///    and the `N` is the function name.\n#[derive(Debug, Clone)]\npub struct FunctionBinding {\n    pub(crate) binding: PropertyKey,\n    pub(crate) name: JsString,\n}\n\nimpl From<JsString> for FunctionBinding {\n    #[inline]\n    fn from(name: JsString) -> Self {\n        Self {\n            binding: name.clone().into(),\n            name,\n        }\n    }\n}\n\nimpl From<JsSymbol> for FunctionBinding {\n    #[inline]\n    fn from(binding: JsSymbol) -> Self {\n        Self {\n            name: binding.fn_name(),\n            binding: binding.into(),\n        }\n    }\n}\n\nimpl<B, N> From<(B, N)> for FunctionBinding\nwhere\n    B: Into<PropertyKey>,\n    N: Into<JsString>,\n{\n    fn from((binding, name): (B, N)) -> Self {\n        Self {\n            binding: binding.into(),\n            name: name.into(),\n        }\n    }\n}\n\n/// Builder for creating native function objects\n#[derive(Debug)]\npub struct FunctionObjectBuilder<'realm> {\n    realm: &'realm Realm,\n    function: NativeFunction,\n    constructor: Option<ConstructorKind>,\n    name: JsString,\n    length: usize,\n}\n\nimpl<'realm> FunctionObjectBuilder<'realm> {\n    /// Create a new `FunctionBuilder` for creating a native function.\n    #[inline]\n    #[must_use]\n    pub fn new(realm: &'realm Realm, function: NativeFunction) -> Self {\n        Self {\n            realm,\n            function,\n            constructor: None,\n            name: js_string!(),\n            length: 0,\n        }\n    }\n\n    /// Specify the name property of object function object.\n    ///\n    /// The default is `\"\"` (empty string).\n    #[must_use]\n    pub fn name<N>(mut self, name: N) -> Self\n    where\n        N: Into<JsString>,\n    {\n        self.name = name.into();\n        self\n    }\n\n    /// Specify the length property of object function object.\n    ///\n    /// How many arguments this function takes.\n    ///\n    /// The default is `0`.\n    #[inline]\n    #[must_use]\n    pub const fn length(mut self, length: usize) -> Self {\n        self.length = length;\n        self\n    }\n\n    /// Specify whether the object function object can be called with `new` keyword.\n    ///\n    /// The default is `false`.\n    #[must_use]\n    pub fn constructor(mut self, yes: bool) -> Self {\n        self.constructor = yes.then_some(ConstructorKind::Base);\n        self\n    }\n\n    /// Build the function object.\n    #[must_use]\n    pub fn build(self) -> JsFunction {\n        let object = self.realm.intrinsics().templates().function().create(\n            NativeFunctionObject {\n                f: self.function,\n                name: self.name.clone(),\n                constructor: self.constructor,\n                realm: Some(self.realm.clone()),\n            },\n            vec![self.length.into(), self.name.into()],\n        );\n\n        JsFunction::from_object_unchecked(object)\n    }\n}\n\n/// Builder for creating objects with properties.\n///\n/// # Examples\n///\n/// ```\n/// # use boa_engine::{\n/// #     Context,\n/// #     JsValue,\n/// #     NativeFunction,\n/// #     object::ObjectInitializer,\n/// #     property::Attribute,\n/// #     js_string,\n/// # };\n/// let mut context = Context::default();\n/// let object = ObjectInitializer::new(&mut context)\n///     .property(js_string!(\"hello\"), js_string!(\"world\"), Attribute::all())\n///     .property(1, 1, Attribute::all())\n///     .function(\n///         NativeFunction::from_fn_ptr(|_, _, _| Ok(JsValue::undefined())),\n///         js_string!(\"func\"),\n///         0,\n///     )\n///     .build();\n/// ```\n///\n/// The equivalent in JavaScript would be:\n/// ```text\n/// let object = {\n///     hello: \"world\",\n///     \"1\": 1,\n///     func: function() {}\n/// }\n/// ```\n#[derive(Debug)]\npub struct ObjectInitializer<'ctx> {\n    context: &'ctx mut Context,\n    object: JsObject,\n}\n\nimpl<'ctx> ObjectInitializer<'ctx> {\n    /// Create a new `ObjectBuilder`.\n    #[inline]\n    pub fn new(context: &'ctx mut Context) -> Self {\n        let object = JsObject::with_object_proto(context.intrinsics());\n        Self { context, object }\n    }\n\n    /// Create a new `ObjectBuilder` with custom [`NativeObject`] data.\n    pub fn with_native_data<T: NativeObject>(data: T, context: &'ctx mut Context) -> Self {\n        let object = JsObject::from_proto_and_data_with_shared_shape(\n            context.root_shape(),\n            context.intrinsics().constructors().object().prototype(),\n            data,\n        )\n        .upcast();\n        Self { context, object }\n    }\n\n    /// Create a new `ObjectBuilder` with custom [`NativeObject`] data and custom prototype.\n    pub fn with_native_data_and_proto<T: NativeObject>(\n        data: T,\n        proto: JsObject,\n        context: &'ctx mut Context,\n    ) -> Self {\n        let object =\n            JsObject::from_proto_and_data_with_shared_shape(context.root_shape(), proto, data)\n                .upcast();\n        Self { context, object }\n    }\n\n    /// Add a function to the object.\n    pub fn function<B>(&mut self, function: NativeFunction, binding: B, length: usize) -> &mut Self\n    where\n        B: Into<FunctionBinding>,\n    {\n        let binding = binding.into();\n        let function = FunctionObjectBuilder::new(self.context.realm(), function)\n            .name(binding.name)\n            .length(length)\n            .constructor(false)\n            .build();\n\n        self.object.borrow_mut().insert(\n            binding.binding,\n            PropertyDescriptor::builder()\n                .value(function)\n                .writable(true)\n                .enumerable(false)\n                .configurable(true),\n        );\n        self\n    }\n\n    /// Add a property to the object.\n    pub fn property<K, V>(&mut self, key: K, value: V, attribute: Attribute) -> &mut Self\n    where\n        K: Into<PropertyKey>,\n        V: Into<JsValue>,\n    {\n        let property = PropertyDescriptor::builder()\n            .value(value)\n            .writable(attribute.writable())\n            .enumerable(attribute.enumerable())\n            .configurable(attribute.configurable());\n        self.object.borrow_mut().insert(key, property);\n        self\n    }\n\n    /// Add new accessor property to the object.\n    ///\n    /// # Panics\n    ///\n    /// If both getter or setter are [`None`].\n    pub fn accessor<K>(\n        &mut self,\n        key: K,\n        get: Option<JsFunction>,\n        set: Option<JsFunction>,\n        attribute: Attribute,\n    ) -> &mut Self\n    where\n        K: Into<PropertyKey>,\n    {\n        // Accessors should have at least one function.\n        assert!(set.is_some() || get.is_some());\n\n        let property = PropertyDescriptor::builder()\n            .maybe_get(get)\n            .maybe_set(set)\n            .enumerable(attribute.enumerable())\n            .configurable(attribute.configurable());\n        self.object.borrow_mut().insert(key, property);\n        self\n    }\n\n    /// Build the object.\n    #[inline]\n    pub fn build(&mut self) -> JsObject {\n        self.object.clone()\n    }\n\n    /// Gets the context used to create the object.\n    #[inline]\n    pub fn context(&mut self) -> &mut Context {\n        self.context\n    }\n}\n\n/// Builder for creating constructors objects, like `Array`.\n#[derive(Debug)]\npub struct ConstructorBuilder<'ctx> {\n    context: &'ctx mut Context,\n    function: NativeFunction,\n    constructor_object: Object<OrdinaryObject>,\n    has_prototype_property: bool,\n    prototype: Object<OrdinaryObject>,\n    name: JsString,\n    length: usize,\n    callable: bool,\n    kind: Option<ConstructorKind>,\n    inherit: Option<JsPrototype>,\n    custom_prototype: Option<JsPrototype>,\n}\n\nimpl<'ctx> ConstructorBuilder<'ctx> {\n    /// Create a new `ConstructorBuilder`.\n    #[inline]\n    pub fn new(context: &'ctx mut Context, function: NativeFunction) -> ConstructorBuilder<'ctx> {\n        Self {\n            context,\n            function,\n            constructor_object: Object {\n                data: ObjectData::new(OrdinaryObject),\n                properties: PropertyMap::default(),\n                extensible: true,\n                private_elements: ThinVec::new(),\n            },\n            prototype: Object {\n                data: ObjectData::new(OrdinaryObject),\n                properties: PropertyMap::default(),\n                extensible: true,\n                private_elements: ThinVec::new(),\n            },\n            length: 0,\n            name: js_string!(),\n            callable: true,\n            kind: Some(ConstructorKind::Base),\n            inherit: None,\n            custom_prototype: None,\n            has_prototype_property: true,\n        }\n    }\n\n    /// Add new method to the constructors prototype.\n    pub fn method<B>(&mut self, function: NativeFunction, binding: B, length: usize) -> &mut Self\n    where\n        B: Into<FunctionBinding>,\n    {\n        let binding = binding.into();\n        let function = FunctionObjectBuilder::new(self.context.realm(), function)\n            .name(binding.name)\n            .length(length)\n            .constructor(false)\n            .build();\n\n        self.prototype.insert(\n            binding.binding,\n            PropertyDescriptor::builder()\n                .value(function)\n                .writable(true)\n                .enumerable(false)\n                .configurable(true),\n        );\n        self\n    }\n\n    /// Add new static method to the constructors object itself.\n    pub fn static_method<B>(\n        &mut self,\n        function: NativeFunction,\n        binding: B,\n        length: usize,\n    ) -> &mut Self\n    where\n        B: Into<FunctionBinding>,\n    {\n        let binding = binding.into();\n        let function = FunctionObjectBuilder::new(self.context.realm(), function)\n            .name(binding.name)\n            .length(length)\n            .constructor(false)\n            .build();\n\n        self.constructor_object.insert(\n            binding.binding,\n            PropertyDescriptor::builder()\n                .value(function)\n                .writable(true)\n                .enumerable(false)\n                .configurable(true),\n        );\n        self\n    }\n\n    /// Add new data property to the constructor's prototype.\n    pub fn property<K, V>(&mut self, key: K, value: V, attribute: Attribute) -> &mut Self\n    where\n        K: Into<PropertyKey>,\n        V: Into<JsValue>,\n    {\n        let property = PropertyDescriptor::builder()\n            .value(value)\n            .writable(attribute.writable())\n            .enumerable(attribute.enumerable())\n            .configurable(attribute.configurable());\n        self.prototype.insert(key, property);\n        self\n    }\n\n    /// Add new static data property to the constructor object itself.\n    pub fn static_property<K, V>(&mut self, key: K, value: V, attribute: Attribute) -> &mut Self\n    where\n        K: Into<PropertyKey>,\n        V: Into<JsValue>,\n    {\n        let property = PropertyDescriptor::builder()\n            .value(value)\n            .writable(attribute.writable())\n            .enumerable(attribute.enumerable())\n            .configurable(attribute.configurable());\n        self.constructor_object.insert(key, property);\n        self\n    }\n\n    /// Add new accessor property to the constructor's prototype.\n    pub fn accessor<K>(\n        &mut self,\n        key: K,\n        get: Option<JsFunction>,\n        set: Option<JsFunction>,\n        attribute: Attribute,\n    ) -> &mut Self\n    where\n        K: Into<PropertyKey>,\n    {\n        let property = PropertyDescriptor::builder()\n            .maybe_get(get)\n            .maybe_set(set)\n            .enumerable(attribute.enumerable())\n            .configurable(attribute.configurable());\n        self.prototype.insert(key, property);\n        self\n    }\n\n    /// Add new static accessor property to the constructor object itself.\n    pub fn static_accessor<K>(\n        &mut self,\n        key: K,\n        get: Option<JsFunction>,\n        set: Option<JsFunction>,\n        attribute: Attribute,\n    ) -> &mut Self\n    where\n        K: Into<PropertyKey>,\n    {\n        let property = PropertyDescriptor::builder()\n            .maybe_get(get)\n            .maybe_set(set)\n            .enumerable(attribute.enumerable())\n            .configurable(attribute.configurable());\n        self.constructor_object.insert(key, property);\n        self\n    }\n\n    /// Add new property to the constructor's prototype.\n    pub fn property_descriptor<K, P>(&mut self, key: K, property: P) -> &mut Self\n    where\n        K: Into<PropertyKey>,\n        P: Into<PropertyDescriptor>,\n    {\n        let property = property.into();\n        self.prototype.insert(key, property);\n        self\n    }\n\n    /// Add new static property to the constructor object itself.\n    pub fn static_property_descriptor<K, P>(&mut self, key: K, property: P) -> &mut Self\n    where\n        K: Into<PropertyKey>,\n        P: Into<PropertyDescriptor>,\n    {\n        let property = property.into();\n        self.constructor_object.insert(key, property);\n        self\n    }\n\n    /// Specify how many arguments the constructor function takes.\n    ///\n    /// Default is `0`.\n    #[inline]\n    pub fn length(&mut self, length: usize) -> &mut Self {\n        self.length = length;\n        self\n    }\n\n    /// Specify the name of the constructor function.\n    ///\n    /// Default is `\"[object]\"`\n    pub fn name<N>(&mut self, name: N) -> &mut Self\n    where\n        N: AsRef<str>,\n    {\n        self.name = name.as_ref().into();\n        self\n    }\n\n    /// Specify whether the constructor function can be called.\n    ///\n    /// Default is `true`\n    #[inline]\n    pub fn callable(&mut self, callable: bool) -> &mut Self {\n        self.callable = callable;\n        self\n    }\n\n    /// Specify whether the constructor function can be called with `new` keyword.\n    ///\n    /// Default is `true`\n    #[inline]\n    pub fn constructor(&mut self, constructor: bool) -> &mut Self {\n        self.kind = constructor.then_some(ConstructorKind::Base);\n        self\n    }\n\n    /// Specify the parent prototype which objects created by this constructor\n    /// inherit from.\n    ///\n    /// Default is `Object.prototype`\n    pub fn inherit<O: Into<JsPrototype>>(&mut self, prototype: O) -> &mut Self {\n        self.inherit = Some(prototype.into());\n        self\n    }\n\n    /// Specify the `[[Prototype]]` internal field of this constructor.\n    ///\n    /// Default is `Function.prototype`\n    pub fn custom_prototype<O: Into<JsPrototype>>(&mut self, prototype: O) -> &mut Self {\n        self.custom_prototype = Some(prototype.into());\n        self\n    }\n\n    /// Specify whether the constructor function has a 'prototype' property.\n    ///\n    /// Default is `true`\n    #[inline]\n    pub fn has_prototype_property(&mut self, has_prototype_property: bool) -> &mut Self {\n        self.has_prototype_property = has_prototype_property;\n        self\n    }\n\n    /// Return the current context.\n    #[inline]\n    pub fn context(&mut self) -> &mut Context {\n        self.context\n    }\n\n    /// Build the constructor function object.\n    #[must_use]\n    pub fn build(mut self) -> StandardConstructor {\n        let length = PropertyDescriptor::builder()\n            .value(self.length)\n            .writable(false)\n            .enumerable(false)\n            .configurable(true);\n        let name = PropertyDescriptor::builder()\n            .value(self.name.clone())\n            .writable(false)\n            .enumerable(false)\n            .configurable(true);\n\n        let prototype = {\n            if let Some(proto) = self.inherit.take() {\n                self.prototype.set_prototype(proto);\n            } else {\n                self.prototype.set_prototype(\n                    self.context\n                        .intrinsics()\n                        .constructors()\n                        .object()\n                        .prototype(),\n                );\n            }\n\n            JsObject::from_object_and_vtable(self.prototype, &ORDINARY_INTERNAL_METHODS)\n        };\n\n        let constructor = {\n            let data = NativeFunctionObject {\n                f: self.function,\n                name: self.name.clone(),\n                constructor: self.kind,\n                realm: Some(self.context.realm().clone()),\n            };\n            let internal_methods = data.internal_methods();\n            let mut constructor = Object {\n                properties: self.constructor_object.properties,\n                extensible: self.constructor_object.extensible,\n                private_elements: self.constructor_object.private_elements,\n                data: ObjectData::new(data),\n            };\n\n            constructor.insert(StaticJsStrings::LENGTH, length);\n            constructor.insert(js_string!(\"name\"), name);\n\n            if let Some(proto) = self.custom_prototype.take() {\n                constructor.set_prototype(proto);\n            } else {\n                constructor.set_prototype(\n                    self.context\n                        .intrinsics()\n                        .constructors()\n                        .function()\n                        .prototype(),\n                );\n            }\n\n            if self.has_prototype_property {\n                constructor.insert(\n                    PROTOTYPE,\n                    PropertyDescriptor::builder()\n                        .value(prototype.clone())\n                        .writable(false)\n                        .enumerable(false)\n                        .configurable(false),\n                );\n            }\n\n            JsObject::from_object_and_vtable(constructor, internal_methods)\n        };\n\n        {\n            let mut prototype = prototype.borrow_mut();\n            prototype.insert(\n                CONSTRUCTOR,\n                PropertyDescriptor::builder()\n                    .value(constructor.clone())\n                    .writable(true)\n                    .enumerable(false)\n                    .configurable(true),\n            );\n        }\n\n        StandardConstructor::new(JsFunction::from_object_unchecked(constructor), prototype)\n    }\n}\n"
  },
  {
    "path": "core/engine/src/object/operations.rs",
    "content": "use super::internal_methods::InternalMethodPropertyContext;\nuse crate::js_error;\nuse crate::value::JsVariant;\nuse crate::{\n    Context, JsExpect, JsResult, JsSymbol, JsValue,\n    builtins::{\n        Array, Proxy,\n        function::{BoundFunction, ClassFieldDefinition, OrdinaryFunction, set_function_name},\n    },\n    context::intrinsics::{StandardConstructor, StandardConstructors},\n    error::JsNativeError,\n    native_function::NativeFunctionObject,\n    object::{CONSTRUCTOR, JsObject, PROTOTYPE, PrivateElement, PrivateName},\n    property::{PropertyDescriptor, PropertyDescriptorBuilder, PropertyKey, PropertyNameKind},\n    realm::Realm,\n    string::StaticJsStrings,\n    value::Type,\n};\n\n/// Object integrity level.\n#[derive(Debug, Clone, Copy, PartialEq, Eq)]\npub enum IntegrityLevel {\n    /// Sealed object integrity level.\n    ///\n    /// Preventing new properties from being added to it and marking all existing\n    /// properties as non-configurable. Values of present properties can still be\n    /// changed as long as they are writable.\n    Sealed,\n\n    /// Frozen object integrity level\n    ///\n    /// A frozen object can no longer be changed; freezing an object prevents new\n    /// properties from being added to it, existing properties from being removed,\n    /// prevents changing the enumerability, configurability, or writability of\n    /// existing properties, and prevents the values of existing properties from\n    /// being changed. In addition, freezing an object also prevents its prototype\n    /// from being changed.\n    Frozen,\n}\n\nimpl IntegrityLevel {\n    /// Returns `true` if the integrity level is sealed.\n    #[must_use]\n    pub const fn is_sealed(&self) -> bool {\n        matches!(self, Self::Sealed)\n    }\n\n    /// Returns `true` if the integrity level is frozen.\n    #[must_use]\n    pub const fn is_frozen(&self) -> bool {\n        matches!(self, Self::Frozen)\n    }\n}\n\nimpl JsObject {\n    /// Check if object is extensible.\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-isextensible-o\n    #[inline]\n    pub fn is_extensible(&self, context: &mut Context) -> JsResult<bool> {\n        // 1. Return ? O.[[IsExtensible]]().\n        self.__is_extensible__(context)\n    }\n\n    /// Get property from object or throw.\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-get-o-p\n    pub fn get<K>(&self, key: K, context: &mut Context) -> JsResult<JsValue>\n    where\n        K: Into<PropertyKey>,\n    {\n        // 1. Assert: Type(O) is Object.\n        // 2. Assert: IsPropertyKey(P) is true.\n        // 3. Return ? O.[[Get]](P, O).\n        self.__get__(\n            &key.into(),\n            self.clone().into(),\n            &mut InternalMethodPropertyContext::new(context),\n        )\n    }\n\n    /// set property of object or throw if bool flag is passed.\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-set-o-p-v-throw\n    pub fn set<K, V>(&self, key: K, value: V, throw: bool, context: &mut Context) -> JsResult<bool>\n    where\n        K: Into<PropertyKey>,\n        V: Into<JsValue>,\n    {\n        let key = key.into();\n        // 1. Assert: Type(O) is Object.\n        // 2. Assert: IsPropertyKey(P) is true.\n        // 3. Assert: Type(Throw) is Boolean.\n        // 4. Let success be ? O.[[Set]](P, V, O).\n        let success = self.__set__(\n            key.clone(),\n            value.into(),\n            self.clone().into(),\n            &mut InternalMethodPropertyContext::new(context),\n        )?;\n        // 5. If success is false and Throw is true, throw a TypeError exception.\n        if !success && throw {\n            return Err(JsNativeError::typ()\n                .with_message(format!(\"cannot set non-writable property: {key}\"))\n                .into());\n        }\n        // 6. Return success.\n        Ok(success)\n    }\n\n    /// Create data property\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-createdataproperty\n    pub fn create_data_property<K, V>(\n        &self,\n        key: K,\n        value: V,\n        context: &mut Context,\n    ) -> JsResult<bool>\n    where\n        K: Into<PropertyKey>,\n        V: Into<JsValue>,\n    {\n        // 1. Assert: Type(O) is Object.\n        // 2. Assert: IsPropertyKey(P) is true.\n        // 3. Let newDesc be the PropertyDescriptor { [[Value]]: V, [[Writable]]: true, [[Enumerable]]: true, [[Configurable]]: true }.\n        let new_desc = PropertyDescriptor::builder()\n            .value(value)\n            .writable(true)\n            .enumerable(true)\n            .configurable(true);\n        // 4. Return ? O.[[DefineOwnProperty]](P, newDesc).\n        self.__define_own_property__(\n            &key.into(),\n            new_desc.into(),\n            &mut InternalMethodPropertyContext::new(context),\n        )\n    }\n\n    /// Create data property\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-createdataproperty\n    pub(crate) fn create_data_property_with_slot<K, V>(\n        &self,\n        key: K,\n        value: V,\n        context: &mut InternalMethodPropertyContext<'_>,\n    ) -> JsResult<bool>\n    where\n        K: Into<PropertyKey>,\n        V: Into<JsValue>,\n    {\n        // 1. Assert: Type(O) is Object.\n        // 2. Assert: IsPropertyKey(P) is true.\n        // 3. Let newDesc be the PropertyDescriptor { [[Value]]: V, [[Writable]]: true, [[Enumerable]]: true, [[Configurable]]: true }.\n        let new_desc = PropertyDescriptor::builder()\n            .value(value)\n            .writable(true)\n            .enumerable(true)\n            .configurable(true);\n        // 4. Return ? O.[[DefineOwnProperty]](P, newDesc).\n        self.__define_own_property__(&key.into(), new_desc.into(), context)\n    }\n\n    /// `10.2.8 DefineMethodProperty ( homeObject, key, closure, enumerable )`\n    ///\n    /// Defines a method property on an object with the specified attributes.\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-definemethodproperty\n    pub(crate) fn define_method_property<K, V>(\n        &self,\n        key: K,\n        value: V,\n        context: &mut InternalMethodPropertyContext<'_>,\n    ) -> JsResult<()>\n    where\n        K: Into<PropertyKey>,\n        V: Into<JsValue>,\n    {\n        // 1. Assert: homeObject is an ordinary, extensible object with no non-configurable properties.\n        // 2. If key is a Private Name, then\n        //    a. Return PrivateElement { [[Key]]: key, [[Kind]]: method, [[Value]]: closure }.\n        // 3. Else,\n        //    a. Let desc be the PropertyDescriptor { [[Value]]: closure, [[Writable]]: true, [[Enumerable]]: enumerable, [[Configurable]]: true }.\n        let new_desc = PropertyDescriptor::builder()\n            .value(value)\n            .writable(true)\n            .enumerable(false)\n            .configurable(true);\n\n        //    b. Perform ! DefinePropertyOrThrow(homeObject, key, desc).\n        self.__define_own_property__(&key.into(), new_desc.into(), context)?;\n\n        //    c. Return unused.\n        Ok(())\n    }\n\n    /// Create data property or throw\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-createdatapropertyorthrow\n    pub fn create_data_property_or_throw<K, V>(\n        &self,\n        key: K,\n        value: V,\n        context: &mut Context,\n    ) -> JsResult<bool>\n    where\n        K: Into<PropertyKey>,\n        V: Into<JsValue>,\n    {\n        let key = key.into();\n        // 1. Assert: Type(O) is Object.\n        // 2. Assert: IsPropertyKey(P) is true.\n        // 3. Let success be ? CreateDataProperty(O, P, V).\n        let success = self.create_data_property(key.clone(), value, context)?;\n        // 4. If success is false, throw a TypeError exception.\n        if !success {\n            return Err(JsNativeError::typ()\n                .with_message(format!(\"cannot redefine property: {key}\"))\n                .into());\n        }\n        // 5. Return success.\n        Ok(success)\n    }\n\n    /// Create non-enumerable data property or throw\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-createnonenumerabledatapropertyinfallibly\n    pub(crate) fn create_non_enumerable_data_property_or_throw<K, V>(\n        &self,\n        key: K,\n        value: V,\n        context: &mut Context,\n    ) where\n        K: Into<PropertyKey>,\n        V: Into<JsValue>,\n    {\n        // 1. Assert: O is an ordinary, extensible object with no non-configurable properties.\n\n        // 2. Let newDesc be the PropertyDescriptor {\n        //    [[Value]]: V,\n        //    [[Writable]]: true,\n        //    [[Enumerable]]: false,\n        //    [[Configurable]]: true\n        //  }.\n        let new_desc = PropertyDescriptorBuilder::new()\n            .value(value)\n            .writable(true)\n            .enumerable(false)\n            .configurable(true)\n            .build();\n\n        // 3. Perform ! DefinePropertyOrThrow(O, P, newDesc).\n        self.define_property_or_throw(key, new_desc, context)\n            .expect(\"should not fail according to spec\");\n\n        // 4. Return unused.\n    }\n\n    /// Define property or throw.\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-definepropertyorthrow\n    pub fn define_property_or_throw<K, P>(\n        &self,\n        key: K,\n        desc: P,\n        context: &mut Context,\n    ) -> JsResult<bool>\n    where\n        K: Into<PropertyKey>,\n        P: Into<PropertyDescriptor>,\n    {\n        let key = key.into();\n        // 1. Assert: Type(O) is Object.\n        // 2. Assert: IsPropertyKey(P) is true.\n        // 3. Let success be ? O.[[DefineOwnProperty]](P, desc).\n        let success = self.__define_own_property__(\n            &key,\n            desc.into(),\n            &mut InternalMethodPropertyContext::new(context),\n        )?;\n        // 4. If success is false, throw a TypeError exception.\n        if !success {\n            return Err(JsNativeError::typ()\n                .with_message(format!(\"cannot redefine property: {key}\"))\n                .into());\n        }\n        // 5. Return success.\n        Ok(success)\n    }\n\n    /// Defines the property or throws a `TypeError` if the operation fails.\n    ///\n    /// More information:\n    /// - [ECMAScript reference][spec]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-deletepropertyorthrow\n    pub fn delete_property_or_throw<K>(&self, key: K, context: &mut Context) -> JsResult<bool>\n    where\n        K: Into<PropertyKey>,\n    {\n        let key = key.into();\n        // 1. Assert: Type(O) is Object.\n        // 2. Assert: IsPropertyKey(P) is true.\n        // 3. Let success be ? O.[[Delete]](P).\n        let success = self.__delete__(&key, &mut InternalMethodPropertyContext::new(context))?;\n        // 4. If success is false, throw a TypeError exception.\n        if !success {\n            return Err(JsNativeError::typ()\n                .with_message(format!(\"cannot delete non-configurable property: {key}\"))\n                .into());\n        }\n        // 5. Return success.\n        Ok(success)\n    }\n\n    /// Check if object has property.\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-hasproperty\n    pub fn has_property<K>(&self, key: K, context: &mut Context) -> JsResult<bool>\n    where\n        K: Into<PropertyKey>,\n    {\n        // 1. Assert: Type(O) is Object.\n        // 2. Assert: IsPropertyKey(P) is true.\n        // 3. Return ? O.[[HasProperty]](P).\n\n        self.__has_property__(\n            &key.into(),\n            &mut InternalMethodPropertyContext::new(context),\n        )\n    }\n\n    /// Abstract optimization operation.\n    ///\n    /// Check if an object has a property and get it if it exists.\n    /// This operation combines the abstract operations `HasProperty` and `Get`.\n    ///\n    /// More information:\n    ///  - [ECMAScript reference HasProperty][spec0]\n    ///  - [ECMAScript reference Get][spec1]\n    ///\n    /// [spec0]: https://tc39.es/ecma262/#sec-hasproperty\n    /// [spec1]: https://tc39.es/ecma262/#sec-get-o-p\n    pub(crate) fn try_get<K>(&self, key: K, context: &mut Context) -> JsResult<Option<JsValue>>\n    where\n        K: Into<PropertyKey>,\n    {\n        self.__try_get__(\n            &key.into(),\n            self.clone().into(),\n            &mut InternalMethodPropertyContext::new(context),\n        )\n    }\n\n    /// Check if object has an own property.\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-hasownproperty\n    pub fn has_own_property<K>(&self, key: K, context: &mut Context) -> JsResult<bool>\n    where\n        K: Into<PropertyKey>,\n    {\n        let key = key.into();\n        // 1. Assert: Type(O) is Object.\n        // 2. Assert: IsPropertyKey(P) is true.\n        // 3. Let desc be ? O.[[GetOwnProperty]](P).\n        let desc =\n            self.__get_own_property__(&key, &mut InternalMethodPropertyContext::new(context))?;\n        // 4. If desc is undefined, return false.\n        // 5. Return true.\n        Ok(desc.is_some())\n    }\n\n    /// Get all the keys of the properties of this object.\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-ordinary-object-internal-methods-and-internal-slots-ownpropertykeys\n    pub fn own_property_keys(&self, context: &mut Context) -> JsResult<Vec<PropertyKey>> {\n        self.__own_property_keys__(context)\n    }\n\n    /// `Call ( F, V [ , argumentsList ] )`\n    ///\n    /// # Panics\n    ///\n    /// Panics if the object is currently mutably borrowed.\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-call\n    #[track_caller]\n    #[inline]\n    pub fn call(\n        &self,\n        this: &JsValue,\n        args: &[JsValue],\n        context: &mut Context,\n    ) -> JsResult<JsValue> {\n        // SKIP: 1. If argumentsList is not present, set argumentsList to a new empty List.\n        // SKIP: 2. If IsCallable(F) is false, throw a TypeError exception.\n        // NOTE(HalidOdat): For object's that are not callable we implement a special __call__ internal method\n        //                  that throws on call.\n\n        context.vm.stack.push(this.clone()); // this\n        context.vm.stack.push(self.clone()); // func\n        let argument_count = args.len();\n        context.vm.stack.calling_convention_push_arguments(args);\n\n        // 3. Return ? F.[[Call]](V, argumentsList).\n        let frame_index = context.vm.frames.len();\n        if self.__call__(argument_count).resolve(context)? {\n            return Ok(context.vm.stack.pop());\n        }\n\n        if frame_index + 1 == context.vm.frames.len() {\n            context.vm.frame_mut().set_exit_early(true);\n        } else {\n            context.vm.frames[frame_index + 1].set_exit_early(true);\n        }\n\n        context.vm.host_call_depth += 1;\n        let result = context.run().consume();\n        context.vm.host_call_depth = context.vm.host_call_depth.saturating_sub(1);\n\n        context.vm.pop_frame().js_expect(\"frame must exist\")?;\n\n        result\n    }\n\n    /// `Construct ( F [ , argumentsList [ , newTarget ] ] )`\n    ///\n    /// Construct an instance of this object with the specified arguments.\n    ///\n    /// # Panics\n    ///\n    /// Panics if the object is currently mutably borrowed.\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-construct\n    #[track_caller]\n    #[inline]\n    pub fn construct(\n        &self,\n        args: &[JsValue],\n        new_target: Option<&Self>,\n        context: &mut Context,\n    ) -> JsResult<Self> {\n        // 1. If newTarget is not present, set newTarget to F.\n        let new_target = new_target.unwrap_or(self);\n\n        context.vm.stack.push(JsValue::undefined());\n        context.vm.stack.push(self.clone()); // func\n        let argument_count = args.len();\n        context.vm.stack.calling_convention_push_arguments(args);\n        context.vm.stack.push(new_target.clone());\n\n        // 2. If argumentsList is not present, set argumentsList to a new empty List.\n        // 3. Return ? F.[[Construct]](argumentsList, newTarget).\n        let frame_index = context.vm.frames.len();\n\n        if self.__construct__(argument_count).resolve(context)? {\n            let result = context.vm.stack.pop();\n            return Ok(result\n                .as_object()\n                .js_expect(\"construct value should be an object\")?\n                .clone());\n        }\n\n        if frame_index + 1 == context.vm.frames.len() {\n            context.vm.frame_mut().set_exit_early(true);\n        } else {\n            context.vm.frames[frame_index + 1].set_exit_early(true);\n        }\n\n        context.vm.host_call_depth += 1;\n        let result = context.run().consume();\n        context.vm.host_call_depth = context.vm.host_call_depth.saturating_sub(1);\n\n        context.vm.pop_frame().js_expect(\"frame must exist\")?;\n\n        Ok(result?\n            .as_object()\n            .js_expect(\"should be an object\")?\n            .clone())\n    }\n\n    /// Make the object [`sealed`][IntegrityLevel::Sealed] or [`frozen`][IntegrityLevel::Frozen].\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-setintegritylevel\n    pub fn set_integrity_level(\n        &self,\n        level: IntegrityLevel,\n        context: &mut Context,\n    ) -> JsResult<bool> {\n        // 1. Assert: Type(O) is Object.\n        // 2. Assert: level is either sealed or frozen.\n\n        // 3. Let status be ? O.[[PreventExtensions]]().\n        let status =\n            self.__prevent_extensions__(&mut InternalMethodPropertyContext::new(context))?;\n        // 4. If status is false, return false.\n        if !status {\n            return Ok(false);\n        }\n\n        // 5. Let keys be ? O.[[OwnPropertyKeys]]().\n        let keys = self.__own_property_keys__(&mut InternalMethodPropertyContext::new(context))?;\n\n        match level {\n            // 6. If level is sealed, then\n            IntegrityLevel::Sealed => {\n                // a. For each element k of keys, do\n                for k in keys {\n                    // i. Perform ? DefinePropertyOrThrow(O, k, PropertyDescriptor { [[Configurable]]: false }).\n                    self.define_property_or_throw(\n                        k,\n                        PropertyDescriptor::builder().configurable(false).build(),\n                        context,\n                    )?;\n                }\n            }\n            // 7. Else,\n            //     a. Assert: level is frozen.\n            IntegrityLevel::Frozen => {\n                // b. For each element k of keys, do\n                for k in keys {\n                    // i. Let currentDesc be ? O.[[GetOwnProperty]](k).\n                    let current_desc = self.__get_own_property__(\n                        &k,\n                        &mut InternalMethodPropertyContext::new(context),\n                    )?;\n                    // ii. If currentDesc is not undefined, then\n                    if let Some(current_desc) = current_desc {\n                        // 1. If IsAccessorDescriptor(currentDesc) is true, then\n                        let desc = if current_desc.is_accessor_descriptor() {\n                            // a. Let desc be the PropertyDescriptor { [[Configurable]]: false }.\n                            PropertyDescriptor::builder().configurable(false).build()\n                        // 2. Else,\n                        } else {\n                            // a. Let desc be the PropertyDescriptor { [[Configurable]]: false, [[Writable]]: false }.\n                            PropertyDescriptor::builder()\n                                .configurable(false)\n                                .writable(false)\n                                .build()\n                        };\n                        // 3. Perform ? DefinePropertyOrThrow(O, k, desc).\n                        self.define_property_or_throw(k, desc, context)?;\n                    }\n                }\n            }\n        }\n\n        // 8. Return true.\n        Ok(true)\n    }\n\n    /// Check if the object is [`sealed`][IntegrityLevel::Sealed] or [`frozen`][IntegrityLevel::Frozen].\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-testintegritylevel\n    pub fn test_integrity_level(\n        &self,\n        level: IntegrityLevel,\n        context: &mut Context,\n    ) -> JsResult<bool> {\n        // 1. Assert: Type(O) is Object.\n        // 2. Assert: level is either sealed or frozen.\n\n        // 3. Let extensible be ? IsExtensible(O).\n        let extensible = self.is_extensible(context)?;\n\n        // 4. If extensible is true, return false.\n        if extensible {\n            return Ok(false);\n        }\n\n        // 5. NOTE: If the object is extensible, none of its properties are examined.\n        // 6. Let keys be ? O.[[OwnPropertyKeys]]().\n        let keys = self.__own_property_keys__(&mut InternalMethodPropertyContext::new(context))?;\n\n        // 7. For each element k of keys, do\n        for k in keys {\n            // a. Let currentDesc be ? O.[[GetOwnProperty]](k).\n            let current_desc =\n                self.__get_own_property__(&k, &mut InternalMethodPropertyContext::new(context))?;\n            // b. If currentDesc is not undefined, then\n            if let Some(current_desc) = current_desc {\n                // i. If currentDesc.[[Configurable]] is true, return false.\n                if current_desc.expect_configurable() {\n                    return Ok(false);\n                }\n                // ii. If level is frozen and IsDataDescriptor(currentDesc) is true, then\n                if level.is_frozen() && current_desc.is_data_descriptor() {\n                    // 1. If currentDesc.[[Writable]] is true, return false.\n                    if current_desc.expect_writable() {\n                        return Ok(false);\n                    }\n                }\n            }\n        }\n        // 8. Return true.\n        Ok(true)\n    }\n\n    /// Abstract operation [`LengthOfArrayLike ( obj )`][spec].\n    ///\n    /// Returns the value of the \"length\" property of an array-like object.\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-lengthofarraylike\n    pub(crate) fn length_of_array_like(&self, context: &mut Context) -> JsResult<u64> {\n        // 1. Assert: Type(obj) is Object.\n\n        // NOTE: This is an optimization, most of the cases that `LengthOfArrayLike` will be called\n        //       is for arrays. The \"length\" property of an array is stored in the first index.\n        if self.is_array() {\n            let borrowed_object = self.borrow();\n            // NOTE: using `to_u32` instead of `to_length` is an optimization,\n            //       since arrays are limited to [0, 2^32 - 1] range.\n            return borrowed_object.properties().storage[0]\n                .to_u32(context)\n                .map(u64::from);\n        }\n\n        // 2. Return ℝ(? ToLength(? Get(obj, \"length\"))).\n        self.get(StaticJsStrings::LENGTH, context)?\n            .to_length(context)\n    }\n\n    /// `7.3.22 SpeciesConstructor ( O, defaultConstructor )`\n    ///\n    /// The abstract operation `SpeciesConstructor` takes arguments `O` (an Object) and\n    /// `defaultConstructor` (a constructor). It is used to retrieve the constructor that should be\n    /// used to create new objects that are derived from `O`. `defaultConstructor` is the\n    /// constructor to use if a constructor `@@species` property cannot be found starting from `O`.\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-speciesconstructor\n    pub(crate) fn species_constructor<F>(\n        &self,\n        default_constructor: F,\n        context: &mut Context,\n    ) -> JsResult<Self>\n    where\n        F: FnOnce(&StandardConstructors) -> &StandardConstructor,\n    {\n        // 1. Assert: Type(O) is Object.\n\n        // 2. Let C be ? Get(O, \"constructor\").\n        let c = self.get(CONSTRUCTOR, context)?;\n\n        // 3. If C is undefined, return defaultConstructor.\n        if c.is_undefined() {\n            return Ok(default_constructor(context.intrinsics().constructors()).constructor());\n        }\n\n        // 4. If Type(C) is not Object, throw a TypeError exception.\n        let c = c.as_object().ok_or_else(|| {\n            JsNativeError::typ().with_message(\"property 'constructor' is not an object\")\n        })?;\n\n        // 5. Let S be ? Get(C, @@species).\n        let s = c.get(JsSymbol::species(), context)?;\n\n        // 6. If S is either undefined or null, return defaultConstructor.\n        if s.is_null_or_undefined() {\n            return Ok(default_constructor(context.intrinsics().constructors()).constructor());\n        }\n\n        // 7. If IsConstructor(S) is true, return S.\n        if let Some(s) = s.as_constructor() {\n            return Ok(s.clone());\n        }\n\n        // 8. Throw a TypeError exception.\n        Err(JsNativeError::typ()\n            .with_message(\"property 'constructor' is not a constructor\")\n            .into())\n    }\n\n    /// It is used to iterate over names of object's keys.\n    ///\n    /// More information:\n    /// - [ECMAScript reference][spec]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-enumerableownpropertynames\n    pub(crate) fn enumerable_own_property_names(\n        &self,\n        kind: PropertyNameKind,\n        context: &mut Context,\n    ) -> JsResult<Vec<JsValue>> {\n        // 1. Assert: Type(O) is Object.\n        // 2. Let ownKeys be ? O.[[OwnPropertyKeys]]().\n        let own_keys =\n            self.__own_property_keys__(&mut InternalMethodPropertyContext::new(context))?;\n        // 3. Let properties be a new empty List.\n        let mut properties = Vec::with_capacity(own_keys.len());\n\n        // 4. For each element key of ownKeys, do\n        for key in own_keys {\n            // a. If Type(key) is String, then\n            let key_str = match &key {\n                PropertyKey::String(s) => Some(s.clone()),\n                PropertyKey::Index(i) => Some(i.get().to_string().into()),\n                PropertyKey::Symbol(_) => None,\n            };\n\n            if let Some(key_str) = key_str {\n                // i. Let desc be ? O.[[GetOwnProperty]](key).\n                let desc = self\n                    .__get_own_property__(&key, &mut InternalMethodPropertyContext::new(context))?;\n                // ii. If desc is not undefined and desc.[[Enumerable]] is true, then\n                if let Some(desc) = desc\n                    && desc.expect_enumerable()\n                {\n                    match kind {\n                        // 1. If kind is key, append key to properties.\n                        PropertyNameKind::Key => properties.push(key_str.into()),\n                        // 2. Else,\n                        // a. Let value be ? Get(O, key).\n                        // b. If kind is value, append value to properties.\n                        PropertyNameKind::Value => {\n                            properties.push(self.get(key.clone(), context)?);\n                        }\n                        // c. Else,\n                        // i. Assert: kind is key+value.\n                        // ii. Let entry be ! CreateArrayFromList(« key, value »).\n                        // iii. Append entry to properties.\n                        PropertyNameKind::KeyAndValue => properties.push(\n                            Array::create_array_from_list(\n                                [key_str.into(), self.get(key.clone(), context)?],\n                                context,\n                            )\n                            .into(),\n                        ),\n                    }\n                }\n            }\n        }\n\n        // 5. Return properties.\n        Ok(properties)\n    }\n\n    /// Abstract operation `GetMethod ( V, P )`\n    ///\n    /// Retrieves the value of a specific property, when the value of the property is expected to be a function.\n    ///\n    /// More information:\n    /// - [ECMAScript reference][spec]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-getmethod\n    pub(crate) fn get_method<K>(&self, key: K, context: &mut Context) -> JsResult<Option<Self>>\n    where\n        K: Into<PropertyKey>,\n    {\n        // Note: The spec specifies this function for JsValue.\n        // It is implemented for JsObject for convenience.\n\n        // 1. Assert: IsPropertyKey(P) is true.\n        // 2. Let func be ? GetV(V, P).\n\n        match self\n            .__get__(\n                &key.into(),\n                self.clone().into(),\n                &mut InternalMethodPropertyContext::new(context),\n            )?\n            .variant()\n        {\n            // 3. If func is either undefined or null, return undefined.\n            JsVariant::Undefined | JsVariant::Null => Ok(None),\n            // 5. Return func.\n            JsVariant::Object(obj) if obj.is_callable() => Ok(Some(obj.clone())),\n            // 4. If IsCallable(func) is false, throw a TypeError exception.\n            _ => Err(JsNativeError::typ()\n                .with_message(\"value returned for property of object is not a function\")\n                .into()),\n        }\n    }\n\n    /// Abstract operation `IsArray ( argument )`\n    ///\n    /// Check if a value is an array.\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-isarray\n    pub(crate) fn is_array_abstract(&self) -> JsResult<bool> {\n        // Note: The spec specifies this function for JsValue.\n        // It is implemented for JsObject for convenience.\n\n        // 2. If argument is an Array exotic object, return true.\n        if self.is_array() {\n            return Ok(true);\n        }\n\n        // 3. If argument is a Proxy exotic object, then\n        if let Some(proxy) = self.downcast_ref::<Proxy>() {\n            // a. If argument.[[ProxyHandler]] is null, throw a TypeError exception.\n            // b. Let target be argument.[[ProxyTarget]].\n            let (target, _) = proxy.try_data()?;\n\n            // c. Return ? IsArray(target).\n            return target.is_array_abstract();\n        }\n\n        // 4. Return false.\n        Ok(false)\n    }\n\n    /// Abstract operation [`GetFunctionRealm`][spec].\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-getfunctionrealm\n    pub(crate) fn get_function_realm(&self, context: &mut Context) -> JsResult<Realm> {\n        if let Some(fun) = self.downcast_ref::<OrdinaryFunction>() {\n            return Ok(fun.realm().clone());\n        }\n\n        if let Some(f) = self.downcast_ref::<NativeFunctionObject>() {\n            return Ok(f.realm.clone().unwrap_or_else(|| context.realm().clone()));\n        }\n\n        if let Some(bound) = self.downcast_ref::<BoundFunction>() {\n            let fun = bound.target_function().clone();\n            return fun.get_function_realm(context);\n        }\n\n        if let Some(proxy) = self.downcast_ref::<Proxy>() {\n            let (fun, _) = proxy.try_data()?;\n            return fun.get_function_realm(context);\n        }\n\n        Ok(context.realm().clone())\n    }\n\n    /// `7.3.26 CopyDataProperties ( target, source, excludedItems )`\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-copydataproperties\n    pub fn copy_data_properties<K>(\n        &self,\n        source: &JsValue,\n        excluded_keys: Vec<K>,\n        context: &mut Context,\n    ) -> JsResult<()>\n    where\n        K: Into<PropertyKey>,\n    {\n        let context = &mut InternalMethodPropertyContext::new(context);\n\n        // 1. Assert: Type(target) is Object.\n        // 2. Assert: excludedItems is a List of property keys.\n        // 3. If source is undefined or null, return target.\n        if source.is_null_or_undefined() {\n            return Ok(());\n        }\n\n        // 4. Let from be ! ToObject(source).\n        let from = source\n            .to_object(context)\n            .expect(\"function ToObject should never complete abruptly here\");\n\n        // 5. Let keys be ? from.[[OwnPropertyKeys]]().\n        // 6. For each element nextKey of keys, do\n        let excluded_keys: Vec<PropertyKey> = excluded_keys.into_iter().map(Into::into).collect();\n        for key in from.__own_property_keys__(context)? {\n            // a. Let excluded be false.\n            let mut excluded = false;\n\n            // b. For each element e of excludedItems, do\n            for e in &excluded_keys {\n                // i. If SameValue(e, nextKey) is true, then\n                if *e == key {\n                    // 1. Set excluded to true.\n                    excluded = true;\n                    break;\n                }\n            }\n            // c. If excluded is false, then\n            if !excluded {\n                // i. Let desc be ? from.[[GetOwnProperty]](nextKey).\n                let desc = from.__get_own_property__(&key, context)?;\n\n                // ii. If desc is not undefined and desc.[[Enumerable]] is true, then\n                if let Some(desc) = desc\n                    && let Some(enumerable) = desc.enumerable()\n                    && enumerable\n                {\n                    // 1. Let propValue be ? Get(from, nextKey).\n                    let prop_value = from.__get__(&key, from.clone().into(), context)?;\n\n                    // 2. Perform ! CreateDataPropertyOrThrow(target, nextKey, propValue).\n                    self.create_data_property_or_throw(key, prop_value, context)\n                        .expect(\"CreateDataPropertyOrThrow should never complete abruptly here\");\n                }\n            }\n        }\n\n        // 7. Return target.\n        Ok(())\n    }\n\n    /// Abstract operation `PrivateElementFind ( O, P )`\n    ///\n    /// Get the private element from an object.\n    ///\n    /// More information:\n    ///  - [ECMAScript specification][spec]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-privateelementfind\n    #[allow(clippy::similar_names)]\n    pub(crate) fn private_element_find(\n        &self,\n        name: &PrivateName,\n        is_getter: bool,\n        is_setter: bool,\n    ) -> Option<PrivateElement> {\n        // 1. If O.[[PrivateElements]] contains a PrivateElement whose [[Key]] is P, then\n        for (key, value) in &self.borrow().private_elements {\n            if key == name {\n                // a. Let entry be that PrivateElement.\n                // b. Return entry.\n                if let PrivateElement::Accessor { getter, setter } = value {\n                    if getter.is_some() && is_getter || setter.is_some() && is_setter {\n                        return Some(value.clone());\n                    }\n                } else {\n                    return Some(value.clone());\n                }\n            }\n        }\n\n        // 2. Return empty.\n        None\n    }\n\n    /// Abstract operation `PrivateFieldAdd ( O, P, value )`\n    ///\n    /// Add private field to an object.\n    ///\n    /// More information:\n    ///  - [ECMAScript specification][spec]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-privatefieldadd\n    pub(crate) fn private_field_add(\n        &self,\n        name: &PrivateName,\n        value: JsValue,\n        context: &mut Context,\n    ) -> JsResult<()> {\n        // 1. If the host is a web browser, then\n        //    a. Perform ? HostEnsureCanAddPrivateElement(O).\n        context\n            .host_hooks()\n            .ensure_can_add_private_element(self, context)?;\n\n        // 2. If ? IsExtensible(O) is false, throw a TypeError exception.\n        // NOTE: From <https://tc39.es/proposal-nonextensible-applies-to-private/#sec-privatefieldadd>\n        if !self.is_extensible(context)? {\n            return Err(js_error!(\n                TypeError: \"cannot add private field to non-extensible class instance\"\n            ));\n        }\n\n        // 3. Let entry be PrivateElementFind(O, P).\n        let entry = self.private_element_find(name, false, false);\n\n        // 4. If entry is not empty, throw a TypeError exception.\n        if entry.is_some() {\n            return Err(js_error!(TypeError: \"private field already exists on prototype\"));\n        }\n\n        // 5. Append PrivateElement { [[Key]]: P, [[Kind]]: field, [[Value]]: value } to O.[[PrivateElements]].\n        self.borrow_mut()\n            .private_elements\n            .push((name.clone(), PrivateElement::Field(value)));\n\n        // 5. Return unused.\n        Ok(())\n    }\n\n    /// Abstract operation `PrivateMethodOrAccessorAdd ( O, method )`\n    ///\n    /// Add private method or accessor to an object.\n    ///\n    /// More information:\n    ///  - [ECMAScript specification][spec]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-privatemethodoraccessoradd\n    pub(crate) fn private_method_or_accessor_add(\n        &self,\n        name: &PrivateName,\n        method: &PrivateElement,\n        context: &mut Context,\n    ) -> JsResult<()> {\n        // 1. Assert: method.[[Kind]] is either method or accessor.\n        assert!(matches!(\n            method,\n            PrivateElement::Method(_) | PrivateElement::Accessor { .. }\n        ));\n\n        let (getter, setter) = if let PrivateElement::Accessor { getter, setter } = method {\n            (getter.is_some(), setter.is_some())\n        } else {\n            (false, false)\n        };\n\n        // 2. If the host is a web browser, then\n        // a. Perform ? HostEnsureCanAddPrivateElement(O).\n        context\n            .host_hooks()\n            .ensure_can_add_private_element(self, context)?;\n\n        // 3. If ? IsExtensible(O) is false, throw a TypeError exception.\n        // NOTE: From <https://tc39.es/proposal-nonextensible-applies-to-private/#sec-privatemethodoraccessoradd>\n        if !self.is_extensible(context)? {\n            return if getter || setter {\n                Err(js_error!(\n                    TypeError: \"cannot add private accessor to non-extensible class instance\"\n                ))\n            } else {\n                Err(js_error!(\n                    TypeError: \"cannot add private method to non-extensible class instance\"\n                ))\n            };\n        }\n\n        // 3. Let entry be PrivateElementFind(O, method.[[Key]]).\n        let entry = self.private_element_find(name, getter, setter);\n\n        // 4. If entry is not empty, throw a TypeError exception.\n        if entry.is_some() {\n            return if getter || setter {\n                Err(js_error!(\n                    TypeError: \"private accessor already exists on class instance\"\n                ))\n            } else {\n                Err(js_error!(\n                    TypeError: \"private method already exists on class instance\"\n                ))\n            };\n        }\n\n        // 5. Append method to O.[[PrivateElements]].\n        self.borrow_mut()\n            .append_private_element(name.clone(), method.clone());\n\n        // 6. Return unused.\n        Ok(())\n    }\n\n    /// Abstract operation `PrivateGet ( O, P )`\n    ///\n    /// Get the value of a private element.\n    ///\n    /// More information:\n    ///  - [ECMAScript specification][spec]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-privateget\n    pub(crate) fn private_get(\n        &self,\n        name: &PrivateName,\n        context: &mut Context,\n    ) -> JsResult<JsValue> {\n        // 1. Let entry be PrivateElementFind(O, P).\n        let entry = self.private_element_find(name, true, true);\n\n        match &entry {\n            // 2. If entry is empty, throw a TypeError exception.\n            None => Err(JsNativeError::typ()\n                .with_message(\"Private element does not exist on object\")\n                .into()),\n\n            // 3. If entry.[[Kind]] is field or method, then\n            // a. Return entry.[[Value]].\n            Some(PrivateElement::Field(value)) => Ok(value.clone()),\n            Some(PrivateElement::Method(value)) => Ok(value.clone().into()),\n\n            // 4. Assert: entry.[[Kind]] is accessor.\n            Some(PrivateElement::Accessor { getter, .. }) => {\n                // 5. If entry.[[Get]] is undefined, throw a TypeError exception.\n                // 6. Let getter be entry.[[Get]].\n                let getter = getter.as_ref().ok_or_else(|| {\n                    JsNativeError::typ()\n                        .with_message(\"private property was defined without a getter\")\n                })?;\n\n                // 7. Return ? Call(getter, O).\n                getter.call(&self.clone().into(), &[], context)\n            }\n        }\n    }\n\n    /// Abstract operation `PrivateSet ( O, P, value )`\n    ///\n    /// Set the value of a private element.\n    ///\n    /// More information:\n    ///  - [ECMAScript specification][spec]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-privateset\n    pub(crate) fn private_set(\n        &self,\n        name: &PrivateName,\n        value: JsValue,\n        context: &mut Context,\n    ) -> JsResult<()> {\n        // 1. Let entry be PrivateElementFind(O, P).\n        // Note: This function is inlined here for mutable access.\n        let mut object_mut = self.borrow_mut();\n        let entry = object_mut\n            .private_elements\n            .iter_mut()\n            .find_map(|(key, value)| if key == name { Some(value) } else { None });\n\n        match entry {\n            // 2. If entry is empty, throw a TypeError exception.\n            None => {\n                return Err(JsNativeError::typ()\n                    .with_message(\"Private element does not exist on object\")\n                    .into());\n            }\n\n            // 3. If entry.[[Kind]] is field, then\n            // a. Set entry.[[Value]] to value.\n            Some(PrivateElement::Field(field)) => {\n                *field = value;\n            }\n\n            // 4. Else if entry.[[Kind]] is method, then\n            // a. Throw a TypeError exception.\n            Some(PrivateElement::Method(_)) => {\n                return Err(JsNativeError::typ()\n                    .with_message(\"private method is not writable\")\n                    .into());\n            }\n\n            // 5. Else,\n            Some(PrivateElement::Accessor { setter, .. }) => {\n                // a. Assert: entry.[[Kind]] is accessor.\n                // b. If entry.[[Set]] is undefined, throw a TypeError exception.\n                // c. Let setter be entry.[[Set]].\n                let setter = setter.clone().ok_or_else(|| {\n                    JsNativeError::typ()\n                        .with_message(\"private property was defined without a setter\")\n                })?;\n\n                // d. Perform ? Call(setter, O, « value »).\n                drop(object_mut);\n                setter.call(&self.clone().into(), &[value], context)?;\n            }\n        }\n\n        // 6. Return unused.\n        Ok(())\n    }\n\n    /// Abstract operation `DefineField ( receiver, fieldRecord )`\n    ///\n    /// Define a field on an object.\n    ///\n    /// More information:\n    ///  - [ECMAScript specification][spec]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-definefield\n    pub(crate) fn define_field(\n        &self,\n        field_record: &ClassFieldDefinition,\n        context: &mut Context,\n    ) -> JsResult<()> {\n        // 2. Let initializer be fieldRecord.[[Initializer]].\n        let initializer = match field_record {\n            ClassFieldDefinition::Public(_, function, _)\n            | ClassFieldDefinition::Private(_, function) => function,\n        };\n\n        // 3. If initializer is not empty, then\n        // a. Let initValue be ? Call(initializer, receiver).\n        // 4. Else, let initValue be undefined.\n        let init_value = initializer.call(&self.clone().into(), &[], context)?;\n\n        match field_record {\n            // 1. Let fieldName be fieldRecord.[[Name]].\n            // 5. If fieldName is a Private Name, then\n            ClassFieldDefinition::Private(field_name, _) => {\n                // a. Perform ? PrivateFieldAdd(receiver, fieldName, initValue).\n                self.private_field_add(field_name, init_value, context)?;\n            }\n            // 1. Let fieldName be fieldRecord.[[Name]].\n            // 6. Else,\n            ClassFieldDefinition::Public(field_name, _, function_name) => {\n                if let Some(function_name) = function_name {\n                    set_function_name(\n                        &init_value\n                            .as_object()\n                            .js_expect(\"init value must be a function object\")?,\n                        function_name,\n                        None,\n                        context,\n                    )?;\n                }\n\n                // a. Assert: IsPropertyKey(fieldName) is true.\n                // b. Perform ? CreateDataPropertyOrThrow(receiver, fieldName, initValue).\n                self.create_data_property_or_throw(field_name.clone(), init_value, context)?;\n            }\n        }\n\n        // 7. Return unused.\n        Ok(())\n    }\n\n    /// Abstract operation `InitializeInstanceElements ( O, constructor )`\n    ///\n    /// Add private methods and fields from a class constructor to an object.\n    ///\n    /// More information:\n    ///  - [ECMAScript specification][spec]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-initializeinstanceelements\n    pub(crate) fn initialize_instance_elements(\n        &self,\n        constructor: &Self,\n        context: &mut Context,\n    ) -> JsResult<()> {\n        let constructor_function = constructor\n            .downcast_ref::<OrdinaryFunction>()\n            .js_expect(\"class constructor must be function object\")?;\n\n        // 1. Let methods be the value of constructor.[[PrivateMethods]].\n        // 2. For each PrivateElement method of methods, do\n        for (name, method) in constructor_function.get_private_methods() {\n            // a. Perform ? PrivateMethodOrAccessorAdd(O, method).\n            self.private_method_or_accessor_add(name, method, context)?;\n        }\n\n        // 3. Let fields be the value of constructor.[[Fields]].\n        // 4. For each element fieldRecord of fields, do\n        for field_record in constructor_function.get_fields() {\n            // a. Perform ? DefineField(O, fieldRecord).\n            self.define_field(field_record, context)?;\n        }\n\n        // 5. Return unused.\n        Ok(())\n    }\n\n    /// Abstract operation `Invoke ( V, P [ , argumentsList ] )`\n    ///\n    /// Calls a method property of an ECMAScript object.\n    ///\n    /// Equivalent to the [`JsValue::invoke`] method, but specialized for objects.\n    ///\n    /// More information:\n    /// - [ECMAScript reference][spec]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-invoke\n    pub(crate) fn invoke<K>(\n        &self,\n        key: K,\n        args: &[JsValue],\n        context: &mut Context,\n    ) -> JsResult<JsValue>\n    where\n        K: Into<PropertyKey>,\n    {\n        let this_value: JsValue = self.clone().into();\n\n        // 1. If argumentsList is not present, set argumentsList to a new empty List.\n        // 2. Let func be ? GetV(V, P).\n        let func = self.__get__(\n            &key.into(),\n            this_value.clone(),\n            &mut InternalMethodPropertyContext::new(context),\n        )?;\n\n        // 3. Return ? Call(func, V, argumentsList)\n        func.call(&this_value, args, context)\n    }\n}\n\nimpl JsValue {\n    /// Abstract operation `GetV ( V, P )`.\n    ///\n    /// Retrieves the value of a specific property of an ECMAScript language value. If the value is\n    /// not an object, the property lookup is performed using a wrapper object appropriate for the\n    /// type of the value.\n    ///\n    /// More information:\n    /// - [ECMAScript reference][spec]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-getv\n    pub(crate) fn get_v<K>(&self, key: K, context: &mut Context) -> JsResult<Self>\n    where\n        K: Into<PropertyKey>,\n    {\n        // 1. Let O be ? ToObject(V).\n        let o = self.to_object(context)?;\n\n        // 2. Return ? O.[[Get]](P, V).\n\n        o.__get__(\n            &key.into(),\n            self.clone(),\n            &mut InternalMethodPropertyContext::new(context),\n        )\n    }\n\n    /// Abstract operation `GetMethod ( V, P )`\n    ///\n    /// Retrieves the value of a specific property, when the value of the property is expected to be a function.\n    ///\n    /// More information:\n    /// - [ECMAScript reference][spec]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-getmethod\n    pub(crate) fn get_method<K>(&self, key: K, context: &mut Context) -> JsResult<Option<JsObject>>\n    where\n        K: Into<PropertyKey>,\n    {\n        // Note: The spec specifies this function for JsValue.\n        // The main part of the function is implemented for JsObject.\n        self.to_object(context)?.get_method(key, context)\n    }\n\n    /// It is used to create List value whose elements are provided by the indexed properties of\n    /// self.\n    ///\n    /// More information:\n    /// - [ECMAScript reference][spec]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-createlistfromarraylike\n    pub(crate) fn create_list_from_array_like(\n        &self,\n        element_types: &[Type],\n        context: &mut Context,\n    ) -> JsResult<Vec<Self>> {\n        // 1. If elementTypes is not present, set elementTypes to « Undefined, Null, Boolean, String, Symbol, Number, BigInt, Object ».\n        let types = if element_types.is_empty() {\n            &[\n                Type::Undefined,\n                Type::Null,\n                Type::Boolean,\n                Type::String,\n                Type::Symbol,\n                Type::Number,\n                Type::BigInt,\n                Type::Object,\n            ]\n        } else {\n            element_types\n        };\n\n        // 2. If Type(obj) is not Object, throw a TypeError exception.\n        let obj = self.as_object().ok_or_else(|| {\n            JsNativeError::typ().with_message(\"cannot create list from a primitive\")\n        })?;\n\n        // 3. Let len be ? LengthOfArrayLike(obj).\n        let len = obj.length_of_array_like(context)?;\n\n        // 4. Let list be a new empty List.\n        let mut list = Vec::with_capacity(len as usize);\n\n        // 5. Let index be 0.\n        // 6. Repeat, while index < len,\n        for index in 0..len {\n            // a. Let indexName be ! ToString(𝔽(index)).\n            // b. Let next be ? Get(obj, indexName).\n            let next = obj.get(index, context)?;\n            // c. If Type(next) is not an element of elementTypes, throw a TypeError exception.\n            if !types.contains(&next.get_type()) {\n                return Err(JsNativeError::typ().with_message(\"bad type\").into());\n            }\n            // d. Append next as the last element of list.\n            list.push(next.clone());\n            // e. Set index to index + 1.\n        }\n\n        // 7. Return list.\n        Ok(list)\n    }\n\n    /// Abstract operation [`Call ( F, V [ , argumentsList ] )`][call].\n    ///\n    /// Calls this value if the value is a callable object.\n    ///\n    /// # Note\n    ///\n    /// It is almost always better to try to obtain a callable object first with [`JsValue::as_callable`],\n    /// then calling [`JsObject::call`], since that allows reusing the unwrapped function for other\n    /// operations. This method is only an utility method for when the spec directly uses `Call`\n    /// without using the value as a proper object.\n    ///\n    /// [call]: https://tc39.es/ecma262/#sec-call\n    #[inline]\n    #[cfg_attr(feature = \"native-backtrace\", track_caller)]\n    pub(crate) fn call(&self, this: &Self, args: &[Self], context: &mut Context) -> JsResult<Self> {\n        let Some(object) = self.as_object() else {\n            return Err(JsNativeError::typ()\n                .with_message(format!(\n                    \"value with type `{}` is not callable\",\n                    self.type_of()\n                ))\n                .into());\n        };\n\n        object.call(this, args, context)\n    }\n\n    /// Abstract operation `( V, P [ , argumentsList ] )`\n    ///\n    /// Calls a method property of an ECMAScript language value.\n    ///\n    /// More information:\n    /// - [ECMAScript reference][spec]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-invoke\n    #[cfg_attr(feature = \"native-backtrace\", track_caller)]\n    pub(crate) fn invoke<K>(&self, key: K, args: &[Self], context: &mut Context) -> JsResult<Self>\n    where\n        K: Into<PropertyKey>,\n    {\n        // 1. If argumentsList is not present, set argumentsList to a new empty List.\n        // 2. Let func be ? GetV(V, P).\n        let func = self.get_v(key, context)?;\n\n        // 3. Return ? Call(func, V, argumentsList)\n        func.call(self, args, context)\n    }\n\n    /// Abstract operation `OrdinaryHasInstance ( C, O )`\n    ///\n    /// More information:\n    /// - [ECMAScript reference][spec]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-ordinaryhasinstance\n    pub fn ordinary_has_instance(\n        function: &Self,\n        object: &Self,\n        context: &mut Context,\n    ) -> JsResult<bool> {\n        // 1. If IsCallable(C) is false, return false.\n        let Some(function) = function.as_callable() else {\n            return Ok(false);\n        };\n\n        // 2. If C has a [[BoundTargetFunction]] internal slot, then\n        if let Some(bound_function) = function.downcast_ref::<BoundFunction>() {\n            // a. Let BC be C.[[BoundTargetFunction]].\n            // b. Return ? InstanceofOperator(O, BC).\n            return object.instance_of(&bound_function.target_function().clone().into(), context);\n        }\n\n        let Some(mut object) = object.as_object() else {\n            // 3. If Type(O) is not Object, return false.\n            return Ok(false);\n        };\n\n        // 4. Let P be ? Get(C, \"prototype\").\n        let prototype = function.get(PROTOTYPE, context)?;\n\n        // 5. If Type(P) is not Object, throw a TypeError exception.\n        let prototype = prototype.as_object().ok_or_else(|| {\n            JsNativeError::typ()\n                .with_message(\"function has non-object prototype in instanceof check\")\n        })?;\n\n        // 6. Repeat,\n        loop {\n            // a. Set O to ? O.[[GetPrototypeOf]]().\n            object = match object\n                .__get_prototype_of__(&mut InternalMethodPropertyContext::new(context))?\n            {\n                Some(obj) => obj,\n                // b. If O is null, return false.\n                None => return Ok(false),\n            };\n\n            // c. If SameValue(P, O) is true, return true.\n            if JsObject::equals(&object, &prototype) {\n                return Ok(true);\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "core/engine/src/object/property_map.rs",
    "content": "use super::{\n    JsPrototype, ObjectStorage, PropertyDescriptor, PropertyKey,\n    shape::{\n        ChangeTransitionAction, RootShape, Shape, UniqueShape,\n        property_table::PropertyTableInner,\n        shared_shape::TransitionKey,\n        slot::{Slot, SlotAttributes},\n    },\n};\nuse crate::value::JsVariant;\nuse crate::{JsValue, property::PropertyDescriptorBuilder};\nuse boa_gc::{Finalize, Trace};\nuse rustc_hash::FxHashMap;\nuse std::{collections::hash_map, iter::FusedIterator};\nuse thin_vec::ThinVec;\n\n/// This represents all the indexed properties.\n///\n/// The index properties can be stored in two storage methods:\n///\n/// ## Dense Storage\n///\n/// Dense storage holds a contiguous array of properties where the index in the array is the key of the property.\n/// These are known to be data descriptors with a value field, writable field set to `true`, configurable field set to `true`, enumerable field set to `true`.\n///\n/// Since we know the properties of the property descriptors (and they are all the same) we can omit it and just store only\n/// the value field and construct the data property descriptor on demand.\n///\n/// ## Sparse Storage\n///\n/// This storage is used as a backup if the element keys are not continuous or the property descriptors\n/// are not data descriptors with a value field, writable field set to `true`, configurable field set to `true`, enumerable field set to `true`.\n///\n/// This method uses more space, since we also have to store the property descriptors, not just the value.\n/// It is also slower because we need to do a hash lookup.\n#[derive(Debug, Trace, Finalize)]\npub enum IndexedProperties {\n    /// Dense [`i32`] storage.\n    DenseI32(ThinVec<i32>),\n\n    /// Dense [`f64`] storage.\n    DenseF64(ThinVec<f64>),\n\n    /// Dense [`JsValue`] storage.\n    DenseElement(ThinVec<JsValue>),\n\n    /// Sparse storage that only keeps element values.\n    SparseElement(Box<FxHashMap<u32, JsValue>>),\n\n    /// Sparse storage that keeps full property descriptors.\n    SparseProperty(Box<FxHashMap<u32, PropertyDescriptor>>),\n}\n\nimpl Default for IndexedProperties {\n    #[inline]\n    fn default() -> Self {\n        Self::DenseI32(ThinVec::new())\n    }\n}\n\nimpl IndexedProperties {\n    pub(crate) fn from_dense_js_value(elements: ThinVec<JsValue>) -> Self {\n        if elements.is_empty() {\n            return Self::default();\n        }\n        Self::DenseElement(elements)\n    }\n\n    #[inline]\n    fn property_simple_value(property: &PropertyDescriptor) -> Option<&JsValue> {\n        if property.writable().unwrap_or(false)\n            && property.enumerable().unwrap_or(false)\n            && property.configurable().unwrap_or(false)\n        {\n            property.value()\n        } else {\n            None\n        }\n    }\n\n    /// Get a property descriptor if it exists.\n    fn get(&self, key: u32) -> Option<PropertyDescriptor> {\n        let value = match self {\n            Self::DenseI32(vec) => vec.get(key as usize).copied()?.into(),\n            Self::DenseF64(vec) => vec.get(key as usize).copied()?.into(),\n            Self::DenseElement(vec) => vec.get(key as usize)?.clone(),\n            Self::SparseElement(map) => map.get(&key)?.clone(),\n            Self::SparseProperty(map) => return map.get(&key).cloned(),\n        };\n\n        Some(\n            PropertyDescriptorBuilder::new()\n                .writable(true)\n                .enumerable(true)\n                .configurable(true)\n                .value(value)\n                .build(),\n        )\n    }\n\n    /// Helper function for converting from a dense storage type to sparse storage type.\n    fn convert_dense_to_sparse<T>(vec: &mut ThinVec<T>) -> FxHashMap<u32, PropertyDescriptor>\n    where\n        T: Into<JsValue>,\n    {\n        let data = std::mem::take(vec);\n\n        data.into_iter()\n            .enumerate()\n            .map(|(index, value)| {\n                (\n                    index as u32,\n                    PropertyDescriptorBuilder::new()\n                        .writable(true)\n                        .enumerable(true)\n                        .configurable(true)\n                        .value(value.into())\n                        .build(),\n                )\n            })\n            .collect()\n    }\n\n    fn convert_dense_to_sparse_values<T>(vec: &mut ThinVec<T>) -> FxHashMap<u32, JsValue>\n    where\n        T: Into<JsValue>,\n    {\n        let data = std::mem::take(vec);\n\n        data.into_iter()\n            .enumerate()\n            .map(|(index, value)| (index as u32, value.into()))\n            .collect()\n    }\n\n    fn convert_to_sparse_and_insert(&mut self, key: u32, property: PropertyDescriptor) -> bool {\n        let mut descriptors = match self {\n            Self::DenseI32(vec) => Self::convert_dense_to_sparse(vec),\n            Self::DenseF64(vec) => Self::convert_dense_to_sparse(vec),\n            Self::DenseElement(vec) => Self::convert_dense_to_sparse(vec),\n            Self::SparseElement(map) => map\n                .iter()\n                .map(|(&index, value)| {\n                    (\n                        index,\n                        PropertyDescriptorBuilder::new()\n                            .writable(true)\n                            .enumerable(true)\n                            .configurable(true)\n                            .value(value.clone())\n                            .build(),\n                    )\n                })\n                .collect(),\n            Self::SparseProperty(map) => {\n                return map.insert(key, property).is_some();\n            }\n        };\n\n        let replaced = descriptors.insert(key, property).is_some();\n        *self = Self::SparseProperty(Box::new(descriptors));\n        replaced\n    }\n\n    /// Inserts a property descriptor with the specified key.\n    fn insert(&mut self, key: u32, property: PropertyDescriptor) -> bool {\n        let Some(value) = Self::property_simple_value(&property) else {\n            return self.convert_to_sparse_and_insert(key, property);\n        };\n\n        match self {\n            // Fast Path: contiguous array access without creating holes.\n            Self::DenseI32(vec) if key <= vec.len() as u32 => {\n                let len = vec.len() as u32;\n\n                if let Some(val) = value.as_i32() {\n                    // If the key is pointing one past the last element, we push it!\n                    //\n                    // Since the previous key is the current key - 1. Meaning that the elements are continuous.\n                    if key == len {\n                        vec.push(val);\n                        return false;\n                    }\n\n                    // If the key points at an already taken index, set it.\n                    vec[key as usize] = val;\n                    return true;\n                }\n\n                if let Some(num) = value.as_number() {\n                    let mut vec = vec.iter().copied().map(f64::from).collect::<ThinVec<_>>();\n\n                    // If the key is pointing one past the last element, we push it!\n                    //\n                    // Since the previous key is the current key - 1. Meaning that the elements are continuous.\n                    if key == len {\n                        vec.push(num);\n                        *self = Self::DenseF64(vec);\n                        return false;\n                    }\n\n                    // If the key points at an already taken index, set it.\n                    vec[key as usize] = num;\n                    *self = Self::DenseF64(vec);\n                    return true;\n                }\n\n                let mut vec = vec\n                    .iter()\n                    .copied()\n                    .map(JsValue::from)\n                    .collect::<ThinVec<JsValue>>();\n\n                // If the key is pointing one past the last element, we push it!\n                //\n                // Since the previous key is the current key - 1. Meaning that the elements are continuous.\n                if key == len {\n                    vec.push(value.clone());\n                    *self = Self::DenseElement(vec);\n                    return false;\n                }\n\n                // If the key points at an already taken index, set it.\n                vec[key as usize] = value.clone();\n                *self = Self::DenseElement(vec);\n                true\n            }\n            Self::DenseF64(vec) if key <= vec.len() as u32 => {\n                let len = vec.len() as u32;\n\n                if let Some(num) = value.as_number() {\n                    // If the key is pointing one past the last element, we push it!\n                    //\n                    // Since the previous key is the current key - 1. Meaning that the elements are continuous.\n                    if key == len {\n                        vec.push(num);\n                        return false;\n                    }\n\n                    // If the key points at an already taken index, swap and return it.\n                    vec[key as usize] = num;\n                    return true;\n                }\n\n                let mut vec = vec\n                    .iter()\n                    .copied()\n                    .map(JsValue::from)\n                    .collect::<ThinVec<JsValue>>();\n\n                // If the key is pointing one past the last element, we push it!\n                //\n                // Since the previous key is the current key - 1. Meaning that the elements are continuous.\n                if key == len {\n                    vec.push(value.clone());\n                    *self = Self::DenseElement(vec);\n                    return false;\n                }\n\n                // If the key points at an already taken index, set it.\n                vec[key as usize] = value.clone();\n                *self = Self::DenseElement(vec);\n                true\n            }\n            Self::DenseElement(vec) if key <= vec.len() as u32 => {\n                let len = vec.len() as u32;\n\n                // If the key is pointing one past the last element, we push it!\n                //\n                // Since the previous key is the current key - 1. Meaning that the elements are continuous.\n                if key == len {\n                    vec.push(value.clone());\n                    return false;\n                }\n\n                // If the key points at an already taken index, set it.\n                vec[key as usize] = value.clone();\n                true\n            }\n            // Creating a hole: transition dense storage into sparse element storage.\n            Self::DenseI32(vec) => {\n                let mut values = Self::convert_dense_to_sparse_values(vec);\n                let replaced = values.insert(key, value.clone()).is_some();\n                *self = Self::SparseElement(Box::new(values));\n                replaced\n            }\n            Self::DenseF64(vec) => {\n                let mut values = Self::convert_dense_to_sparse_values(vec);\n                let replaced = values.insert(key, value.clone()).is_some();\n                *self = Self::SparseElement(Box::new(values));\n                replaced\n            }\n            Self::DenseElement(vec) => {\n                let mut values = Self::convert_dense_to_sparse_values(vec);\n                let replaced = values.insert(key, value.clone()).is_some();\n                *self = Self::SparseElement(Box::new(values));\n                replaced\n            }\n            Self::SparseElement(map) => map.insert(key, value.clone()).is_some(),\n            Self::SparseProperty(map) => {\n                let descriptor = PropertyDescriptorBuilder::new()\n                    .value(value.clone())\n                    .writable(true)\n                    .enumerable(true)\n                    .configurable(true)\n                    .build();\n                map.insert(key, descriptor).is_some()\n            }\n        }\n    }\n\n    fn convert_to_sparse_and_remove(&mut self, key: u32) -> bool {\n        let mut descriptors = match self {\n            IndexedProperties::DenseI32(vec) => Self::convert_dense_to_sparse_values(vec),\n            IndexedProperties::DenseF64(vec) => Self::convert_dense_to_sparse_values(vec),\n            IndexedProperties::DenseElement(vec) => Self::convert_dense_to_sparse_values(vec),\n            IndexedProperties::SparseProperty(map) => return map.remove(&key).is_some(),\n            IndexedProperties::SparseElement(map) => {\n                return map.remove(&key).is_some();\n            }\n        };\n\n        let removed = descriptors.remove(&key).is_some();\n        *self = Self::SparseElement(Box::new(descriptors));\n        removed\n    }\n\n    /// Removes a property descriptor with the specified key.\n    fn remove(&mut self, key: u32) -> bool {\n        match self {\n            // Fast Paths: contiguous storage.\n            //\n            // If the key is pointing at the last element, then we pop it.\n            Self::DenseI32(vec) if (key + 1) == vec.len() as u32 => {\n                vec.pop();\n                true\n            }\n            // If the key is out of range then don't do anything.\n            Self::DenseI32(vec) if key >= vec.len() as u32 => false,\n            Self::DenseF64(vec) if (key + 1) == vec.len() as u32 => {\n                vec.pop();\n                true\n            }\n            Self::DenseF64(vec) if key >= vec.len() as u32 => false,\n            Self::DenseElement(vec) if (key + 1) == vec.len() as u32 => {\n                vec.pop();\n                true\n            }\n            Self::DenseElement(vec) if key >= vec.len() as u32 => false,\n            // Slow Paths: non-contiguous storage.\n            Self::SparseElement(map) => map.remove(&key).is_some(),\n            Self::SparseProperty(map) => map.remove(&key).is_some(),\n            _ => self.convert_to_sparse_and_remove(key),\n        }\n    }\n\n    /// Check if we contain the key to a property descriptor.\n    fn contains_key(&self, key: u32) -> bool {\n        match self {\n            Self::DenseI32(vec) => (0..vec.len() as u32).contains(&key),\n            Self::DenseF64(vec) => (0..vec.len() as u32).contains(&key),\n            Self::DenseElement(vec) => (0..vec.len() as u32).contains(&key),\n            Self::SparseElement(map) => map.contains_key(&key),\n            Self::SparseProperty(map) => map.contains_key(&key),\n        }\n    }\n\n    /// Pushes a value to the end of the dense indexed properties.\n    ///\n    /// Returns `true` if the push succeeded (storage is dense), `false` if\n    /// the storage is sparse and the caller should fall back to the slow path.\n    ///\n    /// Handles type transitions: `DenseI32` → `DenseF64` → `DenseElement`.\n    pub(crate) fn push_dense(&mut self, value: &JsValue) -> bool {\n        match self {\n            Self::DenseI32(vec) => {\n                if let Some(i) = value.as_i32() {\n                    vec.push(i);\n                } else if let Some(n) = value.as_number() {\n                    let mut new_vec: ThinVec<f64> = vec.iter().copied().map(f64::from).collect();\n                    new_vec.push(n);\n                    *self = Self::DenseF64(new_vec);\n                } else {\n                    let mut new_vec: ThinVec<JsValue> =\n                        vec.iter().copied().map(JsValue::from).collect();\n                    new_vec.push(value.clone());\n                    *self = Self::DenseElement(new_vec);\n                }\n                true\n            }\n            Self::DenseF64(vec) => {\n                if let Some(n) = value.as_number() {\n                    vec.push(n);\n                } else {\n                    let mut new_vec: ThinVec<JsValue> =\n                        vec.iter().copied().map(JsValue::from).collect();\n                    new_vec.push(value.clone());\n                    *self = Self::DenseElement(new_vec);\n                }\n                true\n            }\n            Self::DenseElement(vec) => {\n                vec.push(value.clone());\n                true\n            }\n            Self::SparseElement(_) | Self::SparseProperty(_) => false,\n        }\n    }\n\n    /// Transform this array into a sparse array if it isn't so already.\n    pub(crate) fn transform_to_sparse(&mut self) {\n        match &*self {\n            Self::DenseI32(v) => {\n                *self = Self::SparseElement(Box::new(\n                    v.into_iter()\n                        .copied()\n                        .enumerate()\n                        .map(|(i, v)| (i as u32, JsValue::from(v)))\n                        .collect(),\n                ));\n            }\n            Self::DenseF64(v) => {\n                *self = Self::SparseElement(Box::new(\n                    v.into_iter()\n                        .copied()\n                        .enumerate()\n                        .map(|(i, v)| (i as u32, JsValue::from(v)))\n                        .collect(),\n                ));\n            }\n            Self::DenseElement(v) => {\n                *self = Self::SparseElement(Box::new(\n                    v.into_iter()\n                        .enumerate()\n                        .map(|(i, v)| (i as u32, v.clone()))\n                        .collect(),\n                ));\n            }\n            _ => {}\n        }\n    }\n\n    fn iter(&self) -> IndexProperties<'_> {\n        match self {\n            Self::DenseI32(vec) => IndexProperties::DenseI32(vec.iter().enumerate()),\n            Self::DenseF64(vec) => IndexProperties::DenseF64(vec.iter().enumerate()),\n            Self::DenseElement(vec) => IndexProperties::DenseElement(vec.iter().enumerate()),\n            Self::SparseElement(map) => IndexProperties::SparseElement(map.iter()),\n            Self::SparseProperty(map) => IndexProperties::SparseProperty(map.iter()),\n        }\n    }\n\n    fn keys(&self) -> IndexPropertyKeys<'_> {\n        match self {\n            Self::DenseI32(vec) => IndexPropertyKeys::Dense(0..vec.len() as u32),\n            Self::DenseF64(vec) => IndexPropertyKeys::Dense(0..vec.len() as u32),\n            Self::DenseElement(vec) => IndexPropertyKeys::Dense(0..vec.len() as u32),\n            Self::SparseElement(map) => IndexPropertyKeys::SparseElement(map.keys()),\n            Self::SparseProperty(map) => IndexPropertyKeys::SparseProperty(map.keys()),\n        }\n    }\n\n    fn values(&self) -> IndexPropertyValues<'_> {\n        match self {\n            Self::DenseI32(vec) => IndexPropertyValues::DenseI32(vec.iter()),\n            Self::DenseF64(vec) => IndexPropertyValues::DenseF64(vec.iter()),\n            Self::DenseElement(vec) => IndexPropertyValues::DenseElement(vec.iter()),\n            Self::SparseElement(map) => IndexPropertyValues::SparseElement(map.values()),\n            Self::SparseProperty(map) => IndexPropertyValues::SparseProperty(map.values()),\n        }\n    }\n}\n\nimpl<'a> IntoIterator for &'a IndexedProperties {\n    type IntoIter = IndexProperties<'a>;\n    type Item = (u32, PropertyDescriptor);\n    fn into_iter(self) -> Self::IntoIter {\n        self.iter()\n    }\n}\n\n/// A [`PropertyMap`] contains all the properties of an object.\n///\n/// The property values are stored in different data structures based on keys.\n#[derive(Default, Debug, Trace, Finalize)]\npub struct PropertyMap {\n    /// Properties stored with integers as keys.\n    pub(crate) indexed_properties: IndexedProperties,\n\n    pub(crate) shape: Shape,\n    pub(crate) storage: ObjectStorage,\n}\n\nimpl PropertyMap {\n    /// Create a new [`PropertyMap`].\n    #[must_use]\n    #[inline]\n    pub fn new(shape: Shape, indexed_properties: IndexedProperties) -> Self {\n        Self {\n            indexed_properties,\n            shape,\n            storage: Vec::default(),\n        }\n    }\n\n    /// Construct a [`PropertyMap`] with the given prototype with a unique [`Shape`].\n    #[must_use]\n    #[inline]\n    pub fn from_prototype_unique_shape(prototype: JsPrototype) -> Self {\n        Self {\n            indexed_properties: IndexedProperties::default(),\n            shape: UniqueShape::new(prototype, PropertyTableInner::default()).into(),\n            storage: Vec::default(),\n        }\n    }\n\n    /// Construct a [`PropertyMap`] with the given prototype with a shared shape [`Shape`].\n    #[must_use]\n    #[inline]\n    pub fn from_prototype_with_shared_shape(\n        root_shape: &RootShape,\n        prototype: JsPrototype,\n    ) -> Self {\n        let shape = root_shape.shape().change_prototype_transition(prototype);\n        Self {\n            indexed_properties: IndexedProperties::default(),\n            shape: shape.into(),\n            storage: Vec::default(),\n        }\n    }\n\n    /// Get the property with the given key from the [`PropertyMap`].\n    #[must_use]\n    pub fn get(&self, key: &PropertyKey) -> Option<PropertyDescriptor> {\n        if let PropertyKey::Index(index) = key {\n            return self.indexed_properties.get(index.get());\n        }\n        if let Some(slot) = self.shape.lookup(key) {\n            return Some(self.get_storage(slot));\n        }\n\n        None\n    }\n\n    /// Get the property with the given key from the [`PropertyMap`].\n    #[must_use]\n    pub(crate) fn get_with_slot(\n        &self,\n        key: &PropertyKey,\n        out_slot: &mut Slot,\n    ) -> Option<PropertyDescriptor> {\n        if let PropertyKey::Index(index) = key {\n            return self.indexed_properties.get(index.get());\n        }\n        if let Some(slot) = self.shape.lookup(key) {\n            out_slot.index = slot.index;\n\n            // Remove all descriptor attributes, but keep inline caching bits.\n            out_slot.attributes = (out_slot.attributes & SlotAttributes::INLINE_CACHE_BITS)\n                | slot.attributes\n                | SlotAttributes::FOUND;\n            return Some(self.get_storage(slot));\n        }\n\n        None\n    }\n\n    /// Get the property with the given key from the [`PropertyMap`].\n    #[must_use]\n    pub(crate) fn get_storage(&self, Slot { index, attributes }: Slot) -> PropertyDescriptor {\n        let index = index as usize;\n        let mut builder = PropertyDescriptor::builder()\n            .configurable(attributes.contains(SlotAttributes::CONFIGURABLE))\n            .enumerable(attributes.contains(SlotAttributes::ENUMERABLE));\n        if attributes.is_accessor_descriptor() {\n            if attributes.has_get() {\n                builder = builder.get(self.storage[index].clone());\n            }\n            if attributes.has_set() {\n                builder = builder.set(self.storage[index + 1].clone());\n            }\n        } else {\n            builder = builder.writable(attributes.contains(SlotAttributes::WRITABLE));\n            builder = builder.value(self.storage[index].clone());\n        }\n        builder.build()\n    }\n\n    /// Insert the given property descriptor with the given key [`PropertyMap`].\n    pub fn insert(&mut self, key: &PropertyKey, property: PropertyDescriptor) -> bool {\n        let mut dummy_slot = Slot::new();\n        self.insert_with_slot(key, property, &mut dummy_slot)\n    }\n\n    /// Insert the given property descriptor with the given key [`PropertyMap`].\n    pub(crate) fn insert_with_slot(\n        &mut self,\n        key: &PropertyKey,\n        property: PropertyDescriptor,\n        out_slot: &mut Slot,\n    ) -> bool {\n        if let PropertyKey::Index(index) = key {\n            return self.indexed_properties.insert(index.get(), property);\n        }\n\n        let attributes = property.to_slot_attributes();\n\n        if let Some(slot) = self.shape.lookup(key) {\n            let index = slot.index as usize;\n\n            if slot.attributes != attributes {\n                let key = TransitionKey {\n                    property_key: key.clone(),\n                    attributes,\n                };\n                let transition = self.shape.change_attributes_transition(key);\n                self.shape = transition.shape;\n                match transition.action {\n                    ChangeTransitionAction::Nothing => {}\n                    ChangeTransitionAction::Remove => {\n                        self.storage.remove(slot.index as usize + 1);\n                    }\n                    ChangeTransitionAction::Insert => {\n                        // insert after index which is (index + 1).\n                        self.storage.insert(index, JsValue::undefined());\n                    }\n                }\n            }\n\n            if attributes.is_accessor_descriptor() {\n                if attributes.has_get() {\n                    self.storage[index] = property\n                        .get()\n                        .cloned()\n                        .map(JsValue::new)\n                        .unwrap_or_default();\n                }\n                if attributes.has_set() {\n                    self.storage[index + 1] = property\n                        .set()\n                        .cloned()\n                        .map(JsValue::new)\n                        .unwrap_or_default();\n                }\n            } else {\n                self.storage[index] = property.expect_value().clone();\n            }\n            out_slot.index = slot.index;\n            out_slot.attributes =\n                (out_slot.attributes & SlotAttributes::INLINE_CACHE_BITS) | attributes;\n            return true;\n        }\n\n        let transition_key = TransitionKey {\n            property_key: key.clone(),\n            attributes,\n        };\n        self.shape = self.shape.insert_property_transition(transition_key);\n\n        // Make Sure that if we are inserting, it has the correct slot index.\n        debug_assert_eq!(\n            self.shape.lookup(key),\n            Some(Slot {\n                index: self.storage.len() as u32,\n                attributes\n            })\n        );\n\n        out_slot.index = self.storage.len() as u32;\n        out_slot.attributes =\n            (out_slot.attributes & SlotAttributes::INLINE_CACHE_BITS) | attributes;\n\n        if attributes.is_accessor_descriptor() {\n            self.storage.push(\n                property\n                    .get()\n                    .cloned()\n                    .map(JsValue::new)\n                    .unwrap_or_default(),\n            );\n            self.storage.push(\n                property\n                    .set()\n                    .cloned()\n                    .map(JsValue::new)\n                    .unwrap_or_default(),\n            );\n        } else {\n            self.storage\n                .push(property.value().cloned().unwrap_or_default());\n        }\n\n        false\n    }\n\n    /// Remove the property with the given key from the [`PropertyMap`].\n    pub fn remove(&mut self, key: &PropertyKey) -> bool {\n        if let PropertyKey::Index(index) = key {\n            return self.indexed_properties.remove(index.get());\n        }\n        if let Some(slot) = self.shape.lookup(key) {\n            // shift all elements when removing.\n            if slot.attributes.is_accessor_descriptor() {\n                self.storage.remove(slot.index as usize + 1);\n            }\n            self.storage.remove(slot.index as usize);\n\n            self.shape = self.shape.remove_property_transition(key);\n            return true;\n        }\n\n        false\n    }\n\n    /// Overrides all the indexed properties, setting it to dense storage.\n    pub(crate) fn override_indexed_properties(&mut self, properties: ThinVec<JsValue>) {\n        self.indexed_properties = IndexedProperties::DenseElement(properties);\n    }\n\n    pub(crate) fn get_dense_property(&self, index: u32) -> Option<JsValue> {\n        let index = index as usize;\n        match &self.indexed_properties {\n            IndexedProperties::DenseI32(properties) => {\n                properties.get(index).copied().map(JsValue::from)\n            }\n            IndexedProperties::DenseF64(properties) => {\n                properties.get(index).copied().map(JsValue::from)\n            }\n            IndexedProperties::DenseElement(properties) => properties.get(index).cloned(),\n            IndexedProperties::SparseProperty(_) | IndexedProperties::SparseElement(_) => None,\n        }\n    }\n\n    pub(crate) fn set_dense_property(&mut self, index: u32, value: &JsValue) -> bool {\n        let index = index as usize;\n\n        match &mut self.indexed_properties {\n            IndexedProperties::DenseI32(properties) => {\n                let Some(element) = properties.get_mut(index) else {\n                    return false;\n                };\n\n                // If it can fit in a i32 and the truncated version is\n                // equal to the original then it is an integer.\n                let is_rational_integer = |n: f64| n.to_bits() == f64::from(n as i32).to_bits();\n\n                let value = match value.variant() {\n                    JsVariant::Integer32(n) => n,\n                    JsVariant::Float64(n) if is_rational_integer(n) => n as i32,\n                    JsVariant::Float64(value) => {\n                        let mut properties = properties\n                            .iter()\n                            .copied()\n                            .map(f64::from)\n                            .collect::<ThinVec<_>>();\n                        properties[index] = value;\n                        self.indexed_properties = IndexedProperties::DenseF64(properties);\n\n                        return true;\n                    }\n                    _ => {\n                        let mut properties = properties\n                            .iter()\n                            .copied()\n                            .map(JsValue::from)\n                            .collect::<ThinVec<_>>();\n                        properties[index] = value.clone();\n                        self.indexed_properties = IndexedProperties::DenseElement(properties);\n\n                        return true;\n                    }\n                };\n\n                *element = value;\n                true\n            }\n            IndexedProperties::DenseF64(properties) => {\n                let Some(element) = properties.get_mut(index) else {\n                    return false;\n                };\n\n                let Some(value) = value.as_number() else {\n                    let mut properties = properties\n                        .iter()\n                        .copied()\n                        .map(JsValue::from)\n                        .collect::<ThinVec<_>>();\n                    properties[index] = value.clone();\n                    self.indexed_properties = IndexedProperties::DenseElement(properties);\n                    return true;\n                };\n\n                *element = value;\n                true\n            }\n            IndexedProperties::DenseElement(properties) => {\n                let Some(element) = properties.get_mut(index) else {\n                    return false;\n                };\n                *element = value.clone();\n                true\n            }\n            IndexedProperties::SparseProperty(_) | IndexedProperties::SparseElement(_) => false,\n        }\n    }\n\n    /// Returns the vec of dense indexed properties if they exist.\n    pub(crate) fn to_dense_indexed_properties(&self) -> Option<ThinVec<JsValue>> {\n        match &self.indexed_properties {\n            IndexedProperties::DenseI32(properties) => {\n                Some(properties.iter().copied().map(JsValue::from).collect())\n            }\n            IndexedProperties::DenseF64(properties) => {\n                Some(properties.iter().copied().map(JsValue::from).collect())\n            }\n            IndexedProperties::DenseElement(properties) => Some(properties.clone()),\n            IndexedProperties::SparseProperty(_) | IndexedProperties::SparseElement(_) => None,\n        }\n    }\n\n    /// Returns the vec of dense indexed properties if they exist.\n    pub(crate) fn dense_indexed_properties_mut(&mut self) -> Option<&mut ThinVec<JsValue>> {\n        if let IndexedProperties::DenseElement(properties) = &mut self.indexed_properties {\n            Some(properties)\n        } else {\n            None\n        }\n    }\n\n    /// An iterator visiting all indexed key-value pairs in arbitrary order. The iterator element type is `(&'a u32, &'a Property)`.\n    ///\n    /// This iterator does not recurse down the prototype chain.\n    #[inline]\n    #[must_use]\n    pub fn index_properties(&self) -> IndexProperties<'_> {\n        self.indexed_properties.iter()\n    }\n\n    /// An iterator visiting all index keys in arbitrary order. The iterator element type is `&'a u32`.\n    ///\n    /// This iterator does not recurse down the prototype chain.\n    #[inline]\n    #[must_use]\n    pub fn index_property_keys(&self) -> IndexPropertyKeys<'_> {\n        self.indexed_properties.keys()\n    }\n\n    /// An iterator visiting all index values in arbitrary order. The iterator element type is `&'a Property`.\n    ///\n    /// This iterator does not recurse down the prototype chain.\n    #[inline]\n    #[must_use]\n    pub fn index_property_values(&self) -> IndexPropertyValues<'_> {\n        self.indexed_properties.values()\n    }\n\n    /// Returns `true` if the given key is contained in the [`PropertyMap`].\n    #[inline]\n    #[must_use]\n    pub fn contains_key(&self, key: &PropertyKey) -> bool {\n        if let PropertyKey::Index(index) = key {\n            return self.indexed_properties.contains_key(index.get());\n        }\n        if self.shape.lookup(key).is_some() {\n            return true;\n        }\n\n        false\n    }\n}\n\n/// An iterator over the indexed property entries of an `Object`.\n#[derive(Debug, Clone)]\npub enum IndexProperties<'a> {\n    /// An iterator over dense i32, Vec backed indexed property entries of an `Object`.\n    DenseI32(std::iter::Enumerate<std::slice::Iter<'a, i32>>),\n\n    /// An iterator over dense f64, Vec backed indexed property entries of an `Object`.\n    DenseF64(std::iter::Enumerate<std::slice::Iter<'a, f64>>),\n\n    /// An iterator over dense, Vec backed indexed property entries of an `Object`.\n    DenseElement(std::iter::Enumerate<std::slice::Iter<'a, JsValue>>),\n\n    /// An iterator over sparse, `HashMap` backed indexed property entries storing raw values.\n    SparseElement(hash_map::Iter<'a, u32, JsValue>),\n\n    /// An iterator over sparse, `HashMap` backed indexed property entries storing descriptors.\n    SparseProperty(hash_map::Iter<'a, u32, PropertyDescriptor>),\n}\n\nimpl Iterator for IndexProperties<'_> {\n    type Item = (u32, PropertyDescriptor);\n\n    fn next(&mut self) -> Option<Self::Item> {\n        let (index, value) = match self {\n            Self::DenseI32(vec) => vec\n                .next()\n                .map(|(index, value)| (index, JsValue::from(*value)))?,\n            Self::DenseF64(vec) => vec\n                .next()\n                .map(|(index, value)| (index, JsValue::from(*value)))?,\n            Self::DenseElement(vec) => vec.next().map(|(index, value)| (index, value.clone()))?,\n            Self::SparseProperty(map) => {\n                return map.next().map(|(index, value)| (*index, value.clone()));\n            }\n            Self::SparseElement(map) => map\n                .next()\n                .map(|(index, value)| (*index as usize, value.clone()))?,\n        };\n\n        Some((\n            index as u32,\n            PropertyDescriptorBuilder::new()\n                .writable(true)\n                .configurable(true)\n                .enumerable(true)\n                .value(value.clone())\n                .build(),\n        ))\n    }\n\n    #[inline]\n    fn size_hint(&self) -> (usize, Option<usize>) {\n        match self {\n            Self::DenseI32(vec) => vec.size_hint(),\n            Self::DenseF64(vec) => vec.size_hint(),\n            Self::DenseElement(vec) => vec.size_hint(),\n            Self::SparseProperty(map) => map.size_hint(),\n            Self::SparseElement(map) => map.size_hint(),\n        }\n    }\n}\n\nimpl ExactSizeIterator for IndexProperties<'_> {\n    #[inline]\n    fn len(&self) -> usize {\n        match self {\n            Self::DenseI32(vec) => vec.len(),\n            Self::DenseF64(vec) => vec.len(),\n            Self::DenseElement(vec) => vec.len(),\n            Self::SparseProperty(map) => map.len(),\n            Self::SparseElement(map) => map.len(),\n        }\n    }\n}\n\nimpl FusedIterator for IndexProperties<'_> {}\n\n/// An iterator over the index keys (`u32`) of an `Object`.\n#[derive(Debug, Clone)]\npub enum IndexPropertyKeys<'a> {\n    /// An iterator over dense, Vec backed indexed property entries of an `Object`.\n    Dense(std::ops::Range<u32>),\n\n    /// An iterator over sparse, `HashMap` backed indexed property entries of an `Object`.\n    SparseProperty(hash_map::Keys<'a, u32, PropertyDescriptor>),\n\n    /// An iterator over sparse, `HashMap` backed indexed property entries storing values.\n    SparseElement(hash_map::Keys<'a, u32, JsValue>),\n}\n\nimpl Iterator for IndexPropertyKeys<'_> {\n    type Item = u32;\n\n    #[inline]\n    fn next(&mut self) -> Option<Self::Item> {\n        match self {\n            Self::Dense(vec) => vec.next(),\n            Self::SparseProperty(map) => map.next().copied(),\n            Self::SparseElement(map) => map.next().copied(),\n        }\n    }\n\n    #[inline]\n    fn size_hint(&self) -> (usize, Option<usize>) {\n        match self {\n            Self::Dense(vec) => vec.size_hint(),\n            Self::SparseProperty(map) => map.size_hint(),\n            Self::SparseElement(map) => map.size_hint(),\n        }\n    }\n}\n\nimpl ExactSizeIterator for IndexPropertyKeys<'_> {\n    #[inline]\n    fn len(&self) -> usize {\n        match self {\n            Self::Dense(vec) => vec.len(),\n            Self::SparseProperty(map) => map.len(),\n            Self::SparseElement(map) => map.len(),\n        }\n    }\n}\n\nimpl FusedIterator for IndexPropertyKeys<'_> {}\n\n/// An iterator over the index values (`Property`) of an `Object`.\n#[derive(Debug, Clone)]\n#[allow(variant_size_differences)]\npub enum IndexPropertyValues<'a> {\n    /// An iterator over dense, Vec backed indexed property entries of an `Object`.\n    DenseI32(std::slice::Iter<'a, i32>),\n\n    /// An iterator over dense, Vec backed indexed property entries of an `Object`.\n    DenseF64(std::slice::Iter<'a, f64>),\n\n    /// An iterator over dense, Vec backed indexed property entries of an `Object`.\n    DenseElement(std::slice::Iter<'a, JsValue>),\n\n    /// An iterator over sparse, `HashMap` backed indexed property entries of an `Object`.\n    SparseProperty(hash_map::Values<'a, u32, PropertyDescriptor>),\n\n    /// An iterator over sparse, `HashMap` backed indexed property entries storing values.\n    SparseElement(hash_map::Values<'a, u32, JsValue>),\n}\n\nimpl Iterator for IndexPropertyValues<'_> {\n    type Item = PropertyDescriptor;\n\n    fn next(&mut self) -> Option<Self::Item> {\n        let value = match self {\n            Self::DenseI32(vec) => vec.next().copied()?.into(),\n            Self::DenseF64(vec) => vec.next().copied()?.into(),\n            Self::DenseElement(vec) => vec.next().cloned()?,\n            Self::SparseProperty(map) => return map.next().cloned(),\n            Self::SparseElement(map) => map.next().cloned()?,\n        };\n\n        Some(\n            PropertyDescriptorBuilder::new()\n                .writable(true)\n                .configurable(true)\n                .enumerable(true)\n                .value(value)\n                .build(),\n        )\n    }\n\n    #[inline]\n    fn size_hint(&self) -> (usize, Option<usize>) {\n        match self {\n            Self::DenseI32(vec) => vec.size_hint(),\n            Self::DenseF64(vec) => vec.size_hint(),\n            Self::DenseElement(vec) => vec.size_hint(),\n            Self::SparseProperty(map) => map.size_hint(),\n            Self::SparseElement(map) => map.size_hint(),\n        }\n    }\n}\n\nimpl ExactSizeIterator for IndexPropertyValues<'_> {\n    #[inline]\n    fn len(&self) -> usize {\n        match self {\n            Self::DenseI32(vec) => vec.len(),\n            Self::DenseF64(vec) => vec.len(),\n            Self::DenseElement(vec) => vec.len(),\n            Self::SparseProperty(map) => map.len(),\n            Self::SparseElement(map) => map.len(),\n        }\n    }\n}\n"
  },
  {
    "path": "core/engine/src/object/shape/mod.rs",
    "content": "//! Implements object shapes.\n\npub(crate) mod property_table;\nmod root_shape;\npub(crate) mod shared_shape;\npub(crate) mod slot;\npub(crate) mod unique_shape;\n\npub use root_shape::RootShape;\npub use shared_shape::SharedShape;\npub(crate) use unique_shape::UniqueShape;\n\nuse std::fmt::Debug;\n\nuse boa_gc::{Finalize, Trace};\n\nuse crate::property::PropertyKey;\n\nuse self::{\n    shared_shape::{TransitionKey, WeakSharedShape},\n    slot::Slot,\n    unique_shape::WeakUniqueShape,\n};\n\nuse super::JsPrototype;\n\n/// Action to be performed after a property attribute change\n//\n// Example: of { get/set x() { ... }, y: ... } into { x: ..., y: ... }\n//\n//                 0       1       2\n//    Storage: | get x | set x |   y   |\n//\n// We delete at position of x which is index 0 (it spans two elements) + 1:\n//\n//                 0      1\n//    Storage: |   x  |   y   |\npub(crate) enum ChangeTransitionAction {\n    /// Do nothing to storage.\n    Nothing,\n\n    /// Remove element at (index + 1) from storage.\n    Remove,\n\n    /// Insert element at (index + 1) into storage.\n    Insert,\n}\n\n/// The result of a change property attribute transition.\npub(crate) struct ChangeTransition<T> {\n    /// The shape after transition.\n    pub(crate) shape: T,\n\n    /// The needed action to be performed after transition to the object storage.\n    pub(crate) action: ChangeTransitionAction,\n}\n\n/// The internal representation of [`Shape`].\n#[derive(Debug, Trace, Finalize, Clone)]\nenum Inner {\n    Unique(UniqueShape),\n    Shared(SharedShape),\n}\n\n/// Represents the shape of an object.\n#[derive(Debug, Trace, Finalize, Clone)]\npub struct Shape {\n    inner: Inner,\n}\n\nimpl Default for Shape {\n    #[inline]\n    fn default() -> Self {\n        UniqueShape::default().into()\n    }\n}\n\nimpl Shape {\n    /// The max transition count of a [`SharedShape`] from the root node,\n    /// before the shape will be converted into a [`UniqueShape`]\n    ///\n    /// NOTE: This only applies to [`SharedShape`].\n    const TRANSITION_COUNT_MAX: u16 = 1024;\n\n    /// Returns `true` if it's a shared shape, `false` otherwise.\n    #[inline]\n    #[must_use]\n    pub const fn is_shared(&self) -> bool {\n        matches!(self.inner, Inner::Shared(_))\n    }\n\n    /// Returns `true` if it's a unique shape, `false` otherwise.\n    #[inline]\n    #[must_use]\n    pub const fn is_unique(&self) -> bool {\n        matches!(self.inner, Inner::Unique(_))\n    }\n\n    pub(crate) const fn as_unique(&self) -> Option<&UniqueShape> {\n        if let Inner::Unique(shape) = &self.inner {\n            return Some(shape);\n        }\n        None\n    }\n\n    /// Create an insert property transitions returning the new transitioned [`Shape`].\n    ///\n    /// NOTE: This assumes that there is no property with the given key!\n    pub(crate) fn insert_property_transition(&self, key: TransitionKey) -> Self {\n        match &self.inner {\n            Inner::Shared(shape) => {\n                let shape = shape.insert_property_transition(key);\n                if shape.transition_count() >= Self::TRANSITION_COUNT_MAX {\n                    return shape.to_unique().into();\n                }\n                shape.into()\n            }\n            Inner::Unique(shape) => shape.insert_property_transition(key).into(),\n        }\n    }\n\n    /// Create a change attribute property transitions returning [`ChangeTransition`] containing the new [`Shape`]\n    /// and actions to be performed\n    ///\n    /// NOTE: This assumes that there already is a property with the given key!\n    pub(crate) fn change_attributes_transition(\n        &self,\n        key: TransitionKey,\n    ) -> ChangeTransition<Self> {\n        match &self.inner {\n            Inner::Shared(shape) => {\n                let change_transition = shape.change_attributes_transition(key);\n                let shape =\n                    if change_transition.shape.transition_count() >= Self::TRANSITION_COUNT_MAX {\n                        change_transition.shape.to_unique().into()\n                    } else {\n                        change_transition.shape.into()\n                    };\n                ChangeTransition {\n                    shape,\n                    action: change_transition.action,\n                }\n            }\n            Inner::Unique(shape) => shape.change_attributes_transition(&key),\n        }\n    }\n\n    /// Remove a property property from the [`Shape`] returning the new transitioned [`Shape`].\n    ///\n    /// NOTE: This assumes that there already is a property with the given key!\n    pub(crate) fn remove_property_transition(&self, key: &PropertyKey) -> Self {\n        match &self.inner {\n            Inner::Shared(shape) => {\n                let shape = shape.remove_property_transition(key);\n                if shape.transition_count() >= Self::TRANSITION_COUNT_MAX {\n                    return shape.to_unique().into();\n                }\n                shape.into()\n            }\n            Inner::Unique(shape) => shape.remove_property_transition(key).into(),\n        }\n    }\n\n    /// Create a prototype transitions returning the new transitioned [`Shape`].\n    pub(crate) fn change_prototype_transition(&self, prototype: JsPrototype) -> Self {\n        match &self.inner {\n            Inner::Shared(shape) => {\n                let shape = shape.change_prototype_transition(prototype);\n                if shape.transition_count() >= Self::TRANSITION_COUNT_MAX {\n                    return shape.to_unique().into();\n                }\n                shape.into()\n            }\n            Inner::Unique(shape) => shape.change_prototype_transition(prototype).into(),\n        }\n    }\n\n    /// Get the [`JsPrototype`] of the [`Shape`].\n    #[must_use]\n    pub fn prototype(&self) -> JsPrototype {\n        match &self.inner {\n            Inner::Shared(shape) => shape.prototype(),\n            Inner::Unique(shape) => shape.prototype(),\n        }\n    }\n\n    /// Lookup a property in the shape\n    #[inline]\n    pub(crate) fn lookup(&self, key: &PropertyKey) -> Option<Slot> {\n        match &self.inner {\n            Inner::Shared(shape) => shape.lookup(key),\n            Inner::Unique(shape) => shape.lookup(key),\n        }\n    }\n\n    /// Returns the keys of the [`Shape`], in insertion order.\n    #[inline]\n    #[must_use]\n    pub fn keys(&self) -> Vec<PropertyKey> {\n        match &self.inner {\n            Inner::Shared(shape) => shape.keys(),\n            Inner::Unique(shape) => shape.keys(),\n        }\n    }\n\n    /// Return location in memory of the [`Shape`].\n    #[inline]\n    #[must_use]\n    pub fn to_addr_usize(&self) -> usize {\n        match &self.inner {\n            Inner::Shared(shape) => shape.to_addr_usize(),\n            Inner::Unique(shape) => shape.to_addr_usize(),\n        }\n    }\n}\n\nimpl From<UniqueShape> for Shape {\n    fn from(shape: UniqueShape) -> Self {\n        Self {\n            inner: Inner::Unique(shape),\n        }\n    }\n}\n\nimpl From<SharedShape> for Shape {\n    fn from(shape: SharedShape) -> Self {\n        Self {\n            inner: Inner::Shared(shape),\n        }\n    }\n}\n\n/// Represents a weak reaference to an object's [`Shape`].\n#[derive(Debug, Trace, Finalize, Clone, PartialEq)]\npub(crate) enum WeakShape {\n    Unique(WeakUniqueShape),\n    Shared(WeakSharedShape),\n}\n\nimpl WeakShape {\n    /// Return location in memory of the [`Shape`].\n    ///\n    /// Returns `0` if the shape has been freed.\n    #[inline]\n    #[must_use]\n    pub(crate) fn to_addr_usize(&self) -> usize {\n        self.upgrade().as_ref().map_or(0, Shape::to_addr_usize)\n    }\n\n    /// Return location in memory of the [`Shape`].\n    ///\n    /// Returns `0` if the shape has been freed.\n    #[inline]\n    #[must_use]\n    pub(crate) fn upgrade(&self) -> Option<Shape> {\n        match self {\n            WeakShape::Shared(shape) => Some(shape.upgrade()?.into()),\n            WeakShape::Unique(shape) => Some(shape.upgrade()?.into()),\n        }\n    }\n}\n\nimpl From<&Shape> for WeakShape {\n    fn from(value: &Shape) -> Self {\n        match &value.inner {\n            Inner::Shared(shape) => WeakShape::Shared(shape.into()),\n            Inner::Unique(shape) => WeakShape::Unique(shape.into()),\n        }\n    }\n}\n"
  },
  {
    "path": "core/engine/src/object/shape/property_table.rs",
    "content": "use crate::{\n    object::shape::slot::{Slot, SlotAttributes},\n    property::PropertyKey,\n};\nuse rustc_hash::{FxBuildHasher, FxHashMap};\nuse std::{cell::RefCell, rc::Rc};\n\n/// The internal representation of [`PropertyTable`].\n#[derive(Default, Debug, Clone)]\npub(crate) struct PropertyTableInner {\n    pub(crate) map: FxHashMap<PropertyKey, (u32, Slot)>,\n    pub(crate) keys: Vec<(PropertyKey, Slot)>,\n}\n\nimpl PropertyTableInner {\n    /// Returns a new table with a given minimum capacity.\n    pub(crate) fn with_capacity(capacity: usize) -> Self {\n        Self {\n            map: FxHashMap::with_capacity_and_hasher(capacity, FxBuildHasher),\n            keys: Vec::with_capacity(capacity),\n        }\n    }\n\n    /// Returns all the keys, in insertion order.\n    pub(crate) fn keys(&self) -> Vec<PropertyKey> {\n        self.keys_cloned_n(self.keys.len() as u32)\n    }\n\n    /// Returns `n` cloned keys, in insertion order.\n    pub(crate) fn keys_cloned_n(&self, n: u32) -> Vec<PropertyKey> {\n        let n = n as usize;\n\n        self.keys\n            .iter()\n            .take(n)\n            .map(|(key, _)| key)\n            .filter(|key| matches!(key, PropertyKey::String(_)))\n            .chain(\n                self.keys\n                    .iter()\n                    .take(n)\n                    .map(|(key, _)| key)\n                    .filter(|key| matches!(key, PropertyKey::Symbol(_))),\n            )\n            .cloned()\n            .collect()\n    }\n\n    /// Returns a new table with `n` cloned properties.\n    pub(crate) fn clone_count(&self, n: u32) -> Self {\n        let n = n as usize;\n\n        let mut keys = Vec::with_capacity(n);\n        let mut map = FxHashMap::default();\n\n        for (index, (key, slot)) in self.keys.iter().take(n).enumerate() {\n            keys.push((key.clone(), *slot));\n            map.insert(key.clone(), (index as u32, *slot));\n        }\n\n        Self { map, keys }\n    }\n\n    /// Insert a property entry into the table.\n    pub(crate) fn insert(&mut self, key: PropertyKey, attributes: SlotAttributes) {\n        let slot = Slot::from_previous(self.keys.last().map(|x| x.1), attributes);\n        let index = self.keys.len() as u32;\n        self.keys.push((key.clone(), slot));\n        let value = self.map.insert(key, (index, slot));\n        debug_assert!(value.is_none());\n    }\n}\n\n/// Represents an ordered property table, that maps [`PropertyTable`] to [`Slot`].\n///\n/// This is shared between [`crate::object::shape::SharedShape`].\n#[derive(Default, Debug, Clone)]\npub(crate) struct PropertyTable {\n    pub(super) inner: Rc<RefCell<PropertyTableInner>>,\n}\n\nimpl PropertyTable {\n    /// Creates a new `PropertyTable` with the specified capacity.\n    pub(crate) fn with_capacity(capacity: usize) -> Self {\n        Self {\n            inner: Rc::new(RefCell::new(PropertyTableInner::with_capacity(capacity))),\n        }\n    }\n\n    /// Returns the inner representation of a [`PropertyTable`].\n    pub(super) fn inner(&self) -> &RefCell<PropertyTableInner> {\n        &self.inner\n    }\n\n    /// Add a property to the [`PropertyTable`] or deep clone it,\n    /// if there already is a property or the property has attributes that are not the same.\n    pub(crate) fn add_property_deep_clone_if_needed(\n        &self,\n        key: PropertyKey,\n        attributes: SlotAttributes,\n        property_count: u32,\n    ) -> Self {\n        {\n            let mut inner = self.inner.borrow_mut();\n            if (property_count as usize) == inner.keys.len() && !inner.map.contains_key(&key) {\n                inner.insert(key, attributes);\n                return self.clone();\n            }\n        }\n\n        // property is already present need to make deep clone of property table.\n        let this = self.deep_clone(property_count);\n        {\n            let mut inner = this.inner.borrow_mut();\n            inner.insert(key, attributes);\n        }\n        this\n    }\n\n    /// Deep clone the [`PropertyTable`] in insertion order with the first n properties.\n    pub(crate) fn deep_clone(&self, n: u32) -> Self {\n        Self {\n            inner: Rc::new(RefCell::new(self.inner.borrow().clone_count(n))),\n        }\n    }\n\n    /// Deep clone the [`PropertyTable`].\n    pub(crate) fn deep_clone_all(&self) -> Self {\n        Self {\n            inner: Rc::new(RefCell::new((*self.inner.borrow()).clone())),\n        }\n    }\n\n    /// Change the attributes of a property.\n    pub(crate) fn set_attributes_at_index(\n        &self,\n        key: &PropertyKey,\n        property_attributes: SlotAttributes,\n    ) {\n        let mut inner = self.inner.borrow_mut();\n        let Some((index, slot)) = inner.map.get_mut(key) else {\n            unreachable!(\"There should already be a property!\")\n        };\n        slot.attributes = property_attributes;\n        let index = *index as usize;\n\n        inner.keys[index].1.attributes = property_attributes;\n    }\n\n    /// Get a property from the [`PropertyTable`].\n    ///\n    /// Panics:\n    ///\n    /// If it is not in the [`PropertyTable`].\n    pub(crate) fn get_expect(&self, key: &PropertyKey) -> Slot {\n        let inner = self.inner.borrow();\n        let Some((_, slot)) = inner.map.get(key) else {\n            unreachable!(\"There should already be a property!\")\n        };\n        *slot\n    }\n}\n"
  },
  {
    "path": "core/engine/src/object/shape/root_shape.rs",
    "content": "use boa_macros::{Finalize, Trace};\n\nuse super::SharedShape;\n\n/// This is a wrapper around [`SharedShape`] that ensures it's root shape.\n///\n/// Represent the root shape that [`SharedShape`] transitions start from.\n#[derive(Debug, Clone, Trace, Finalize)]\npub struct RootShape {\n    shape: SharedShape,\n}\n\nimpl Default for RootShape {\n    #[inline]\n    fn default() -> Self {\n        Self {\n            shape: SharedShape::root(),\n        }\n    }\n}\n\nimpl RootShape {\n    /// Gets the inner [`SharedShape`].\n    #[must_use]\n    pub const fn shape(&self) -> &SharedShape {\n        &self.shape\n    }\n}\n"
  },
  {
    "path": "core/engine/src/object/shape/shared_shape/forward_transition.rs",
    "content": "use std::fmt::Debug;\n\nuse boa_gc::{Finalize, Gc, GcRefCell, Trace, WeakGc};\nuse rustc_hash::FxHashMap;\n\nuse crate::object::JsPrototype;\n\nuse super::{Inner as SharedShapeInner, TransitionKey};\n\n/// Maps transition key type to a [`SharedShapeInner`] transition.\n#[derive(Debug, Trace, Finalize)]\nstruct TransitionMap<T: Debug + Trace + Finalize> {\n    map: FxHashMap<T, WeakGc<SharedShapeInner>>,\n\n    /// This counts the number of insertions after a prune operation.\n    insertion_count_since_prune: u8,\n}\n\nimpl<T: Debug + Trace + Finalize> Default for TransitionMap<T> {\n    fn default() -> Self {\n        Self {\n            map: FxHashMap::default(),\n            insertion_count_since_prune: 0,\n        }\n    }\n}\n\nimpl<T: Debug + Trace + Finalize> TransitionMap<T> {\n    fn get_and_increment_count(&mut self) -> u8 {\n        let result = self.insertion_count_since_prune;\n\n        // NOTE(HalidOdat): This is done so it overflows to 0, on every 256 insertion\n        // operations. Fulfills the prune condition every 256 insertions.\n        self.insertion_count_since_prune = self.insertion_count_since_prune.wrapping_add(1);\n\n        result\n    }\n}\n\n/// The internal representation of [`ForwardTransition`].\n#[derive(Default, Debug, Trace, Finalize)]\nstruct Inner {\n    properties: Option<Box<TransitionMap<TransitionKey>>>,\n    prototypes: Option<Box<TransitionMap<JsPrototype>>>,\n}\n\n/// Holds a forward reference to a previously created transition.\n///\n/// The reference is weak, therefore it can be garbage collected, if it's not in use.\n#[derive(Default, Debug, Trace, Finalize)]\npub(super) struct ForwardTransition {\n    inner: GcRefCell<Inner>,\n}\n\nimpl ForwardTransition {\n    /// Insert a property transition.\n    pub(super) fn insert_property(&self, key: TransitionKey, value: &Gc<SharedShapeInner>) {\n        let mut this = self.inner.borrow_mut();\n        let properties = this.properties.get_or_insert_with(Box::default);\n\n        if properties.get_and_increment_count() == u8::MAX {\n            properties.map.retain(|_, v| v.is_upgradable());\n        }\n\n        properties.map.insert(key, WeakGc::new(value));\n    }\n\n    /// Insert a prototype transition.\n    pub(super) fn insert_prototype(&self, key: JsPrototype, value: &Gc<SharedShapeInner>) {\n        let mut this = self.inner.borrow_mut();\n        let prototypes = this.prototypes.get_or_insert_with(Box::default);\n\n        if prototypes.get_and_increment_count() == u8::MAX {\n            prototypes.map.retain(|_, v| v.is_upgradable());\n        }\n\n        prototypes.map.insert(key, WeakGc::new(value));\n    }\n\n    /// Get a property transition, return [`None`] otherwise.\n    pub(super) fn get_property(&self, key: &TransitionKey) -> Option<WeakGc<SharedShapeInner>> {\n        let this = self.inner.borrow();\n        let transitions = this.properties.as_ref()?;\n        transitions.map.get(key).cloned()\n    }\n\n    /// Get a prototype transition, return [`None`] otherwise.\n    pub(super) fn get_prototype(&self, key: &JsPrototype) -> Option<WeakGc<SharedShapeInner>> {\n        let this = self.inner.borrow();\n        let transitions = this.prototypes.as_ref()?;\n        transitions.map.get(key).cloned()\n    }\n\n    /// Prunes the [`WeakGc`]s that have been garbage collected.\n    pub(super) fn prune_property_transitions(&self) {\n        let mut this = self.inner.borrow_mut();\n        let Some(transitions) = this.properties.as_deref_mut() else {\n            return;\n        };\n\n        transitions.insertion_count_since_prune = 0;\n        transitions.map.retain(|_, v| v.is_upgradable());\n    }\n\n    /// Prunes the [`WeakGc`]s that have been garbage collected.\n    pub(super) fn prune_prototype_transitions(&self) {\n        let mut this = self.inner.borrow_mut();\n        let Some(transitions) = this.prototypes.as_deref_mut() else {\n            return;\n        };\n\n        transitions.insertion_count_since_prune = 0;\n        transitions.map.retain(|_, v| v.is_upgradable());\n    }\n\n    #[cfg(test)]\n    pub(crate) fn property_transitions_count(&self) -> (usize, u8) {\n        let this = self.inner.borrow();\n        this.properties.as_ref().map_or((0, 0), |transitions| {\n            (\n                transitions.map.len(),\n                transitions.insertion_count_since_prune,\n            )\n        })\n    }\n\n    #[cfg(test)]\n    pub(crate) fn prototype_transitions_count(&self) -> (usize, u8) {\n        let this = self.inner.borrow();\n        this.prototypes.as_ref().map_or((0, 0), |transitions| {\n            (\n                transitions.map.len(),\n                transitions.insertion_count_since_prune,\n            )\n        })\n    }\n}\n"
  },
  {
    "path": "core/engine/src/object/shape/shared_shape/mod.rs",
    "content": "mod forward_transition;\npub(crate) mod template;\n\n#[cfg(test)]\nmod tests;\n\nuse std::{collections::hash_map::RandomState, hash::Hash};\n\nuse bitflags::bitflags;\nuse boa_gc::{Finalize, Gc, Trace, WeakGc, empty_trace};\nuse indexmap::IndexMap;\n\nuse crate::{JsObject, object::JsPrototype, property::PropertyKey};\n\nuse self::forward_transition::ForwardTransition;\n\nuse super::{\n    ChangeTransition, ChangeTransitionAction, Slot, UniqueShape, property_table::PropertyTable,\n    slot::SlotAttributes,\n};\n\n/// Represent a [`SharedShape`] property transition.\n#[derive(Debug, Finalize, Clone, PartialEq, Eq, Hash)]\npub(crate) struct TransitionKey {\n    pub(crate) property_key: PropertyKey,\n    pub(crate) attributes: SlotAttributes,\n}\n\n// SAFETY: Non of the member of this struct are garbage collected,\n//         so this should be fine.\nunsafe impl Trace for TransitionKey {\n    empty_trace!();\n}\n\nconst INSERT_PROPERTY_TRANSITION_TYPE: u8 = 0b0000_0000;\nconst CONFIGURE_PROPERTY_TRANSITION_TYPE: u8 = 0b0000_0001;\nconst PROTOTYPE_TRANSITION_TYPE: u8 = 0b0000_0010;\n\n// Reserved for future use!\n#[allow(unused)]\nconst RESERVED_TRANSITION_TYPE: u8 = 0b0000_0011;\n\nbitflags! {\n    /// Flags of a shape.\n    #[derive(Default, Debug, Clone, Copy, PartialEq, Eq, Finalize)]\n    pub struct ShapeFlags: u8 {\n        /// Represents the transition type of a [`SharedShape`].\n        const TRANSITION_TYPE = 0b0000_0011;\n    }\n}\n\nimpl ShapeFlags {\n    // NOTE: Remove type bits and set the new ones.\n    fn insert_property_transition_from(previous: Self) -> Self {\n        previous.difference(Self::TRANSITION_TYPE)\n            | Self::from_bits_retain(INSERT_PROPERTY_TRANSITION_TYPE)\n    }\n    fn configure_property_transition_from(previous: Self) -> Self {\n        previous.difference(Self::TRANSITION_TYPE)\n            | Self::from_bits_retain(CONFIGURE_PROPERTY_TRANSITION_TYPE)\n    }\n    fn prototype_transition_from(previous: Self) -> Self {\n        previous.difference(Self::TRANSITION_TYPE)\n            | Self::from_bits_retain(PROTOTYPE_TRANSITION_TYPE)\n    }\n\n    const fn is_insert_transition_type(self) -> bool {\n        self.intersection(Self::TRANSITION_TYPE).bits() == INSERT_PROPERTY_TRANSITION_TYPE\n    }\n    const fn is_prototype_transition_type(self) -> bool {\n        self.intersection(Self::TRANSITION_TYPE).bits() == PROTOTYPE_TRANSITION_TYPE\n    }\n}\n\n// SAFETY: Non of the member of this struct are garbage collected,\n//         so this should be fine.\nunsafe impl Trace for ShapeFlags {\n    empty_trace!();\n}\n\n/// The internal representation of a [`SharedShape`].\n#[derive(Debug, Trace, Finalize)]\nstruct Inner {\n    /// See [`ForwardTransition`].\n    forward_transitions: ForwardTransition,\n\n    /// The count of how many properties this [`SharedShape`] holds.\n    property_count: u32,\n\n    /// Instance prototype `__proto__`.\n    prototype: JsPrototype,\n\n    // SAFETY: This is safe because nothing in [`PropertyTable`]\n    //         needs tracing\n    #[unsafe_ignore_trace]\n    property_table: PropertyTable,\n\n    /// The previous shape in the transition chain.\n    ///\n    /// [`None`] if it is the root shape.\n    previous: Option<SharedShape>,\n\n    /// How many transitions have happened from the root node.\n    transition_count: u16,\n\n    /// Flags about the shape.\n    flags: ShapeFlags,\n}\n\n/// Represents a shared object shape.\n#[derive(Debug, Trace, Finalize, Clone)]\npub struct SharedShape {\n    inner: Gc<Inner>,\n}\n\nimpl SharedShape {\n    fn property_table(&self) -> &PropertyTable {\n        &self.inner.property_table\n    }\n    /// Return the property count that this shape owns in the [`PropertyTable`].\n    fn property_count(&self) -> u32 {\n        self.inner.property_count\n    }\n    /// Return the index to the property in the [`PropertyTable`].\n    fn property_index(&self) -> u32 {\n        self.inner.property_count.saturating_sub(1)\n    }\n    /// Getter for the transition count field.\n    #[must_use]\n    pub fn transition_count(&self) -> u16 {\n        self.inner.transition_count\n    }\n    /// Getter for the previous field.\n    #[must_use]\n    pub fn previous(&self) -> Option<&Self> {\n        self.inner.previous.as_ref()\n    }\n    /// Get the prototype of the shape.\n    #[must_use]\n    pub fn prototype(&self) -> JsPrototype {\n        self.inner.prototype.clone()\n    }\n    /// Get the property this [`SharedShape`] refers to.\n    pub(crate) fn property(&self) -> (PropertyKey, Slot) {\n        let inner = self.property_table().inner().borrow();\n        let (key, slot) = inner\n            .keys\n            .get(self.property_index() as usize)\n            .expect(\"There should be a property\");\n        (key.clone(), *slot)\n    }\n    /// Get the flags of the shape.\n    fn flags(&self) -> ShapeFlags {\n        self.inner.flags\n    }\n    /// Getter for the [`ForwardTransition`] field.\n    fn forward_transitions(&self) -> &ForwardTransition {\n        &self.inner.forward_transitions\n    }\n    /// Check if the shape has the given prototype.\n    #[must_use]\n    pub fn has_prototype(&self, prototype: &JsObject) -> bool {\n        self.inner.prototype.as_ref() == Some(prototype)\n    }\n\n    /// Create a new [`SharedShape`].\n    fn new(inner: Inner) -> Self {\n        Self {\n            inner: Gc::new(inner),\n        }\n    }\n\n    /// Create a root [`SharedShape`].\n    #[must_use]\n    pub(crate) fn root() -> Self {\n        Self::new(Inner {\n            forward_transitions: ForwardTransition::default(),\n            prototype: None,\n            property_count: 0,\n            // Most of the time the root shape initiates with between 1-4 properties.\n            property_table: PropertyTable::with_capacity(4),\n            previous: None,\n            flags: ShapeFlags::default(),\n            transition_count: 0,\n        })\n    }\n\n    /// Create a [`SharedShape`] change prototype transition.\n    pub(crate) fn change_prototype_transition(&self, prototype: JsPrototype) -> Self {\n        if let Some(shape) = self.forward_transitions().get_prototype(&prototype) {\n            if let Some(inner) = shape.upgrade() {\n                return Self { inner };\n            }\n\n            self.forward_transitions().prune_prototype_transitions();\n        }\n        let new_inner_shape = Inner {\n            forward_transitions: ForwardTransition::default(),\n            prototype: prototype.clone(),\n            property_table: self.property_table().clone(),\n            property_count: self.property_count(),\n            previous: Some(self.clone()),\n            transition_count: self.transition_count() + 1,\n            flags: ShapeFlags::prototype_transition_from(self.flags()),\n        };\n        let new_shape = Self::new(new_inner_shape);\n\n        self.forward_transitions()\n            .insert_prototype(prototype, &new_shape.inner);\n\n        new_shape\n    }\n\n    /// Create a [`SharedShape`] insert property transition.\n    pub(crate) fn insert_property_transition(&self, key: TransitionKey) -> Self {\n        // Check if we have already created such a transition, if so use it!\n        if let Some(shape) = self.forward_transitions().get_property(&key) {\n            if let Some(inner) = shape.upgrade() {\n                return Self { inner };\n            }\n\n            self.forward_transitions().prune_property_transitions();\n        }\n\n        let property_table = self.property_table().add_property_deep_clone_if_needed(\n            key.property_key.clone(),\n            key.attributes,\n            self.property_count(),\n        );\n        let new_inner_shape = Inner {\n            prototype: self.prototype(),\n            forward_transitions: ForwardTransition::default(),\n            property_table,\n            property_count: self.property_count() + 1,\n            previous: Some(self.clone()),\n            transition_count: self.transition_count() + 1,\n            flags: ShapeFlags::insert_property_transition_from(self.flags()),\n        };\n        let new_shape = Self::new(new_inner_shape);\n\n        self.forward_transitions()\n            .insert_property(key, &new_shape.inner);\n\n        new_shape\n    }\n\n    /// Create a [`SharedShape`] change prototype transition, returning [`ChangeTransition`].\n    pub(crate) fn change_attributes_transition(\n        &self,\n        key: TransitionKey,\n    ) -> ChangeTransition<Self> {\n        let slot = self.property_table().get_expect(&key.property_key);\n\n        // Check if we have already created such a transition, if so use it!\n        if let Some(shape) = self.forward_transitions().get_property(&key) {\n            if let Some(inner) = shape.upgrade() {\n                let action = if slot.attributes.width_match(key.attributes) {\n                    ChangeTransitionAction::Nothing\n                } else if slot.attributes.is_accessor_descriptor() {\n                    // Accessor property --> Data property\n                    ChangeTransitionAction::Remove\n                } else {\n                    // Data property --> Accessor property\n                    ChangeTransitionAction::Insert\n                };\n\n                return ChangeTransition {\n                    shape: Self { inner },\n                    action,\n                };\n            }\n\n            self.forward_transitions().prune_property_transitions();\n        }\n\n        // The attribute change transitions, didn't change from accessor to data property or vice-versa.\n        if slot.attributes.width_match(key.attributes) {\n            let property_table = self.property_table().deep_clone_all();\n            property_table.set_attributes_at_index(&key.property_key, key.attributes);\n            let inner_shape = Inner {\n                forward_transitions: ForwardTransition::default(),\n                prototype: self.prototype(),\n                property_table,\n                property_count: self.property_count(),\n                previous: Some(self.clone()),\n                transition_count: self.transition_count() + 1,\n                flags: ShapeFlags::configure_property_transition_from(self.flags()),\n            };\n            let shape = Self::new(inner_shape);\n\n            self.forward_transitions()\n                .insert_property(key, &shape.inner);\n\n            return ChangeTransition {\n                shape,\n                action: ChangeTransitionAction::Nothing,\n            };\n        }\n\n        // Rollback before the property has added.\n        let (mut base, prototype, transitions) = self.rollback_before(&key.property_key);\n\n        // Apply prototype transition, if it was found.\n        if let Some(prototype) = prototype {\n            base = base.change_prototype_transition(prototype);\n        }\n\n        // Apply this property.\n        base = base.insert_property_transition(key);\n\n        // Apply previous properties.\n        for (property_key, attributes) in transitions.into_iter().rev() {\n            let transition = TransitionKey {\n                property_key,\n                attributes,\n            };\n            base = base.insert_property_transition(transition);\n        }\n\n        // Determine action to be performed on the storage.\n        let action = if slot.attributes.is_accessor_descriptor() {\n            // Accessor property --> Data property\n            ChangeTransitionAction::Remove\n        } else {\n            // Data property --> Accessor property\n            ChangeTransitionAction::Insert\n        };\n\n        ChangeTransition {\n            shape: base,\n            action,\n        }\n    }\n\n    /// Rollback to shape before the insertion of the [`PropertyKey`] that is provided.\n    ///\n    /// This returns the shape before the insertion, if it sees a prototype transition it will return the latest one,\n    /// ignoring any others, [`None`] otherwise. It also will return the property transitions ordered from\n    /// latest to oldest that it sees.\n    ///\n    /// NOTE: In the transitions it does not include the property that we are rolling back.\n    ///\n    /// NOTE: The prototype transitions if it sees a property insert and then later an attribute change it will condense\n    /// into one property insert transition with the new attribute in the change attribute transition,\n    /// in the same place that the property was inserted initially.\n    //\n    // For example with the following chain:\n    //\n    //        INSERT(x)             INSERT(y)                INSERT(z)\n    // { }  ------------>  { x }  ------------>  { x, y }  ------------>  { x, y, z }\n    //\n    // Then we call rollback on `y`:\n    //\n    //        INSERT(x)             INSERT(y)                INSERT(z)\n    // { }  ------------>  { x }  ------------>  { x, y }  ------------>  { x, y, z }\n    //                       ^\n    //                       \\--- base (with array of transitions to be performed: INSERT(z),\n    //                                                 and protortype: None )\n    fn rollback_before(\n        &self,\n        key: &PropertyKey,\n    ) -> (\n        Self,\n        Option<JsPrototype>,\n        IndexMap<PropertyKey, SlotAttributes>,\n    ) {\n        let mut prototype = None;\n        let mut transitions: IndexMap<PropertyKey, SlotAttributes, RandomState> =\n            IndexMap::default();\n\n        let mut current = Some(self);\n        let base = loop {\n            let Some(current_shape) = current else {\n                unreachable!(\"The chain should have insert transition type!\")\n            };\n\n            // We only take the latest prototype change it, if it exists.\n            if current_shape.flags().is_prototype_transition_type() {\n                if prototype.is_none() {\n                    prototype = Some(current_shape.prototype().clone());\n                }\n\n                // Skip when it is a prototype transition.\n                current = current_shape.previous();\n                continue;\n            }\n\n            let (current_property_key, slot) = current_shape.property();\n\n            if current_shape.flags().is_insert_transition_type() && &current_property_key == key {\n                let base = if let Some(base) = current_shape.previous() {\n                    base.clone()\n                } else {\n                    // It's the root, because it doesn't have previous.\n                    current_shape.clone()\n                };\n                break base;\n            }\n\n            // Do not add property that we are trying to delete.\n            // this can happen if a configure was called after inserting it into the shape\n            if &current_property_key != key {\n                // Only take the latest changes to a property. To try to build a smaller tree.\n                transitions\n                    .entry(current_property_key)\n                    .or_insert(slot.attributes);\n            }\n\n            current = current_shape.previous();\n        };\n\n        (base, prototype, transitions)\n    }\n\n    /// Remove a property from [`SharedShape`], returning the new [`SharedShape`].\n    pub(crate) fn remove_property_transition(&self, key: &PropertyKey) -> Self {\n        let (mut base, prototype, transitions) = self.rollback_before(key);\n\n        // Apply prototype transition, if it was found.\n        if let Some(prototype) = prototype {\n            base = base.change_prototype_transition(prototype);\n        }\n\n        for (property_key, attributes) in transitions.into_iter().rev() {\n            let transition = TransitionKey {\n                property_key,\n                attributes,\n            };\n            base = base.insert_property_transition(transition);\n        }\n\n        base\n    }\n\n    /// Do a property lookup, returns [`None`] if property not found.\n    pub(crate) fn lookup(&self, key: &PropertyKey) -> Option<Slot> {\n        let property_count = self.property_count();\n        if property_count == 0 {\n            return None;\n        }\n\n        let property_table_inner = self.property_table().inner().borrow();\n        // Check if we are trying to access properties that belong to another shape.\n        if let Some((property_table_index, slot)) = property_table_inner.map.get(key)\n            && *property_table_index < self.property_count()\n        {\n            return Some(*slot);\n        }\n        None\n    }\n\n    /// Gets all keys first strings then symbols in creation order.\n    pub(crate) fn keys(&self) -> Vec<PropertyKey> {\n        let property_table = self.property_table().inner().borrow();\n        property_table.keys_cloned_n(self.property_count())\n    }\n\n    /// Returns a new [`UniqueShape`] with the properties of the [`SharedShape`].\n    pub(crate) fn to_unique(&self) -> UniqueShape {\n        UniqueShape::new(\n            self.prototype(),\n            self.property_table()\n                .inner()\n                .borrow()\n                .clone_count(self.property_count()),\n        )\n    }\n\n    /// Return location in memory of the [`SharedShape`].\n    pub(crate) fn to_addr_usize(&self) -> usize {\n        let ptr: *const _ = self.inner.as_ref();\n        ptr as usize\n    }\n}\n\n/// Represents a weak reference to [`SharedShape`].\n#[derive(Debug, Trace, Finalize, Clone, PartialEq)]\npub(crate) struct WeakSharedShape {\n    inner: WeakGc<Inner>,\n}\n\nimpl WeakSharedShape {\n    /// Upgrade returns a [`SharedShape`] pointer for the internal value if the pointer is still live,\n    /// or [`None`] if the value was already garbage collected.\n    #[inline]\n    #[must_use]\n    pub(crate) fn upgrade(&self) -> Option<SharedShape> {\n        Some(SharedShape {\n            inner: self.inner.upgrade()?,\n        })\n    }\n}\n\nimpl From<&SharedShape> for WeakSharedShape {\n    fn from(value: &SharedShape) -> Self {\n        WeakSharedShape {\n            inner: WeakGc::new(&value.inner),\n        }\n    }\n}\n"
  },
  {
    "path": "core/engine/src/object/shape/shared_shape/template.rs",
    "content": "use boa_gc::{Finalize, Trace};\nuse thin_vec::ThinVec;\n\nuse crate::{\n    JsValue,\n    object::{\n        IndexedProperties, JsObject, NativeObject, Object, ObjectData, PropertyMap,\n        shape::slot::SlotAttributes,\n    },\n    property::{Attribute, PropertyKey},\n};\n\nuse super::{SharedShape, TransitionKey};\n\n/// Represent a template of an objects properties and prototype.\n/// This is used to construct as many objects  as needed from a predefined [`SharedShape`].\n#[derive(Debug, Clone, Trace, Finalize)]\npub(crate) struct ObjectTemplate {\n    shape: SharedShape,\n}\n\nimpl ObjectTemplate {\n    /// Create a new [`ObjectTemplate`]\n    pub(crate) fn new(shape: &SharedShape) -> Self {\n        Self {\n            shape: shape.clone(),\n        }\n    }\n\n    /// Create and [`ObjectTemplate`] with a prototype.\n    pub(crate) fn with_prototype(shape: &SharedShape, prototype: JsObject) -> Self {\n        let shape = shape.change_prototype_transition(Some(prototype));\n        Self { shape }\n    }\n\n    /// Check if the shape has a specific, prototype.\n    pub(crate) fn has_prototype(&self, prototype: &JsObject) -> bool {\n        self.shape.has_prototype(prototype)\n    }\n\n    /// Set the prototype of the [`ObjectTemplate`].\n    ///\n    /// This assumes that the prototype has not been set yet.\n    pub(crate) fn set_prototype(&mut self, prototype: JsObject) -> &mut Self {\n        self.shape = self.shape.change_prototype_transition(Some(prototype));\n        self\n    }\n\n    /// Returns the inner shape of the [`ObjectTemplate`].\n    pub(crate) const fn shape(&self) -> &SharedShape {\n        &self.shape\n    }\n\n    /// Add a data property to the [`ObjectTemplate`].\n    ///\n    /// This assumes that the property with the given key was not previously set\n    /// and that it's a string or symbol.\n    pub(crate) fn property(&mut self, key: PropertyKey, attributes: Attribute) -> &mut Self {\n        debug_assert!(!matches!(&key, PropertyKey::Index(_)));\n\n        let attributes = SlotAttributes::from_bits_truncate(attributes.bits());\n        self.shape = self.shape.insert_property_transition(TransitionKey {\n            property_key: key,\n            attributes,\n        });\n        self\n    }\n\n    /// Add a accessor property to the [`ObjectTemplate`].\n    ///\n    /// This assumes that the property with the given key was not previously set\n    /// and that it's a string or symbol.\n    pub(crate) fn accessor(\n        &mut self,\n        key: PropertyKey,\n        get: bool,\n        set: bool,\n        attributes: Attribute,\n    ) -> &mut Self {\n        // TODO: We don't support indexed keys.\n        debug_assert!(!matches!(&key, PropertyKey::Index(_)));\n\n        let attributes = {\n            let mut result = SlotAttributes::empty();\n            result.set(\n                SlotAttributes::CONFIGURABLE,\n                attributes.contains(Attribute::CONFIGURABLE),\n            );\n            result.set(\n                SlotAttributes::ENUMERABLE,\n                attributes.contains(Attribute::ENUMERABLE),\n            );\n\n            result.set(SlotAttributes::GET, get);\n            result.set(SlotAttributes::SET, set);\n\n            result\n        };\n\n        self.shape = self.shape.insert_property_transition(TransitionKey {\n            property_key: key,\n            attributes,\n        });\n        self\n    }\n\n    /// Create an object from the [`ObjectTemplate`]\n    ///\n    /// The storage must match the properties provided.\n    pub(crate) fn create<T: NativeObject>(&self, data: T, storage: Vec<JsValue>) -> JsObject {\n        let internal_methods = data.internal_methods();\n\n        let mut object = Object {\n            data: ObjectData::new(data),\n            extensible: true,\n            properties: PropertyMap::new(self.shape.clone().into(), IndexedProperties::default()),\n            private_elements: ThinVec::new(),\n        };\n\n        object.properties.storage = storage;\n\n        JsObject::from_object_and_vtable(object, internal_methods)\n    }\n\n    /// Create an object from the [`ObjectTemplate`]\n    ///\n    /// The storage must match the properties provided. It does not apply to\n    /// the indexed properties.\n    pub(crate) fn create_with_indexed_properties<T: NativeObject>(\n        &self,\n        data: T,\n        storage: Vec<JsValue>,\n        indexed_properties: IndexedProperties,\n    ) -> JsObject {\n        let internal_methods = data.internal_methods();\n        let mut object = Object {\n            data: ObjectData::new(data),\n            extensible: true,\n            properties: PropertyMap::new(self.shape.clone().into(), indexed_properties),\n            private_elements: ThinVec::new(),\n        };\n\n        object.properties.storage = storage;\n\n        JsObject::from_object_and_vtable(object, internal_methods)\n    }\n}\n"
  },
  {
    "path": "core/engine/src/object/shape/shared_shape/tests.rs",
    "content": "use crate::{JsObject, JsSymbol, object::shape::slot::SlotAttributes, property::PropertyKey};\n\nuse super::{SharedShape, TransitionKey};\n\n#[test]\nfn test_prune_property_on_counter_limit() {\n    let shape = SharedShape::root();\n\n    for i in 0..255 {\n        assert_eq!(\n            shape.forward_transitions().property_transitions_count(),\n            (i, i as u8)\n        );\n\n        shape.insert_property_transition(TransitionKey {\n            property_key: PropertyKey::Symbol(JsSymbol::new(None).unwrap()),\n            attributes: SlotAttributes::all(),\n        });\n    }\n\n    assert_eq!(\n        shape.forward_transitions().property_transitions_count(),\n        (255, 255)\n    );\n\n    boa_gc::force_collect();\n\n    {\n        shape.insert_property_transition(TransitionKey {\n            property_key: PropertyKey::Symbol(JsSymbol::new(None).unwrap()),\n            attributes: SlotAttributes::all(),\n        });\n    }\n\n    assert_eq!(\n        shape.forward_transitions().property_transitions_count(),\n        (1, 0)\n    );\n\n    {\n        shape.insert_property_transition(TransitionKey {\n            property_key: PropertyKey::Symbol(JsSymbol::new(None).unwrap()),\n            attributes: SlotAttributes::all(),\n        });\n    }\n\n    assert_eq!(\n        shape.forward_transitions().property_transitions_count(),\n        (2, 1)\n    );\n\n    boa_gc::force_collect();\n\n    assert_eq!(\n        shape.forward_transitions().property_transitions_count(),\n        (2, 1)\n    );\n}\n\n#[test]\nfn test_prune_prototype_on_counter_limit() {\n    let shape = SharedShape::root();\n\n    assert_eq!(\n        shape.forward_transitions().prototype_transitions_count(),\n        (0, 0)\n    );\n\n    for i in 0..255 {\n        assert_eq!(\n            shape.forward_transitions().prototype_transitions_count(),\n            (i, i as u8)\n        );\n\n        shape.change_prototype_transition(Some(JsObject::with_null_proto()));\n    }\n\n    boa_gc::force_collect();\n\n    assert_eq!(\n        shape.forward_transitions().prototype_transitions_count(),\n        (255, 255)\n    );\n\n    {\n        shape.change_prototype_transition(Some(JsObject::with_null_proto()));\n    }\n\n    assert_eq!(\n        shape.forward_transitions().prototype_transitions_count(),\n        (1, 0)\n    );\n\n    {\n        shape.change_prototype_transition(Some(JsObject::with_null_proto()));\n    }\n\n    assert_eq!(\n        shape.forward_transitions().prototype_transitions_count(),\n        (2, 1)\n    );\n\n    boa_gc::force_collect();\n\n    assert_eq!(\n        shape.forward_transitions().prototype_transitions_count(),\n        (2, 1)\n    );\n}\n"
  },
  {
    "path": "core/engine/src/object/shape/slot.rs",
    "content": "use bitflags::bitflags;\npub(crate) type SlotIndex = u32;\n\nbitflags! {\n    /// Attributes of a slot.\n    #[derive(Default, Debug, Clone, Copy, PartialEq, Eq, Hash)]\n    pub(crate) struct SlotAttributes: u8 {\n        const WRITABLE = 0b0000_0001;\n        const ENUMERABLE = 0b0000_0010;\n        const CONFIGURABLE = 0b0000_0100;\n        const GET = 0b0000_1000;\n        const SET = 0b0001_0000;\n\n        const INLINE_CACHE_BITS = 0b1110_0000;\n        const PROTOTYPE    = 0b0010_0000;\n        const FOUND        = 0b0100_0000;\n        const NOT_CACHEABLE = 0b1000_0000;\n    }\n}\n\nimpl SlotAttributes {\n    pub(crate) const fn is_accessor_descriptor(self) -> bool {\n        self.contains(Self::GET) || self.contains(Self::SET)\n    }\n\n    pub(crate) const fn has_get(self) -> bool {\n        self.contains(Self::GET)\n    }\n    pub(crate) const fn has_set(self) -> bool {\n        self.contains(Self::SET)\n    }\n\n    /// Check if slot type width matches, this can only happens,\n    /// if they are both accessors, or both data properties.\n    pub(crate) const fn width_match(self, other: Self) -> bool {\n        self.is_accessor_descriptor() == other.is_accessor_descriptor()\n    }\n\n    /// Get the width of the slot.\n    pub(crate) fn width(self) -> u32 {\n        // accessor take 2 positions in the storage to accommodate for the `get` and `set` fields.\n        1 + u32::from(self.is_accessor_descriptor())\n    }\n\n    pub(crate) const fn is_cacheable(self) -> bool {\n        !self.contains(Self::NOT_CACHEABLE) && self.contains(Self::FOUND)\n    }\n\n    #[cfg(test)]\n    pub(crate) const fn in_prototype(self) -> bool {\n        self.contains(Self::PROTOTYPE)\n    }\n}\n\n/// Represents an [`u32`] index and it's slot attributes of an element in a object storage.\n///\n/// Slots can have different width depending on its attributes, accessors properties have width `2`,\n/// while data properties have width `1`.\n#[derive(Debug, Clone, Copy, PartialEq, Eq)]\npub(crate) struct Slot {\n    pub(crate) index: SlotIndex,\n    pub(crate) attributes: SlotAttributes,\n}\n\nimpl Slot {\n    pub(crate) const fn new() -> Self {\n        Self {\n            index: 0,\n            attributes: SlotAttributes::empty(),\n        }\n    }\n\n    pub(crate) const fn is_cacheable(self) -> bool {\n        self.attributes.is_cacheable()\n    }\n\n    #[cfg(test)]\n    pub(crate) const fn in_prototype(self) -> bool {\n        self.attributes.in_prototype()\n    }\n\n    /// Get the width of the slot.\n    pub(crate) fn width(self) -> u32 {\n        self.attributes.width()\n    }\n\n    /// Calculate next slot from previous one.\n    ///\n    /// This is needed because slots do not have the same width.\n    pub(crate) fn from_previous(\n        previous_slot: Option<Self>,\n        new_attributes: SlotAttributes,\n    ) -> Self {\n        // If there was no previous slot then return 0 as the index.\n        let Some(previous_slot) = previous_slot else {\n            return Self {\n                index: 0,\n                attributes: new_attributes,\n            };\n        };\n\n        Self {\n            index: previous_slot.index + previous_slot.width(),\n            attributes: new_attributes,\n        }\n    }\n\n    pub(crate) fn set_not_cacheable_if_already_prototype(&mut self) {\n        // NOTE(HalidOdat): This is a bit of a hack to avoid conditional branches.\n        //\n        // Equivalent to:\n        // if slot.attributes.contains(SlotAttributes::PROTOTYPE) {\n        //     slot.attributes |= SlotAttributes::NOT_CACHEABLE;\n        // }\n        //\n        self.attributes |= SlotAttributes::from_bits_retain(\n            (self.attributes.bits() & SlotAttributes::PROTOTYPE.bits()) << 2,\n        );\n    }\n}\n"
  },
  {
    "path": "core/engine/src/object/shape/unique_shape.rs",
    "content": "use std::{cell::RefCell, fmt::Debug};\n\nuse boa_gc::{Finalize, Gc, GcRefCell, Trace, WeakGc};\n\nuse crate::property::PropertyKey;\n\nuse super::{\n    ChangeTransition, ChangeTransitionAction, JsPrototype, Shape, Slot,\n    property_table::PropertyTableInner, shared_shape::TransitionKey,\n};\n\n/// The internal representation of [`UniqueShape`].\n#[derive(Default, Debug, Trace, Finalize)]\nstruct Inner {\n    /// The property table that maps a [`PropertyKey`] to a slot in the objects storage.\n    //\n    // SAFETY: This is safe because nothing in this field needs tracing.\n    #[unsafe_ignore_trace]\n    property_table: RefCell<PropertyTableInner>,\n\n    /// The prototype of the shape.\n    prototype: GcRefCell<JsPrototype>,\n}\n\n/// Represents a [`Shape`] that is not shared with any other object.\n///\n/// This is useful for objects that are inherently unique like,\n/// the builtin object.\n///\n/// Cloning this does a shallow clone.\n#[derive(Default, Debug, Clone, Trace, Finalize)]\npub(crate) struct UniqueShape {\n    inner: Gc<Inner>,\n}\n\nimpl UniqueShape {\n    /// Create a new [`UniqueShape`].\n    pub(crate) fn new(prototype: JsPrototype, property_table: PropertyTableInner) -> Self {\n        Self {\n            inner: Gc::new(Inner {\n                property_table: RefCell::new(property_table),\n                prototype: GcRefCell::new(prototype),\n            }),\n        }\n    }\n\n    pub(crate) fn override_internal(\n        &self,\n        property_table: PropertyTableInner,\n        prototype: JsPrototype,\n    ) {\n        *self.inner.property_table.borrow_mut() = property_table;\n        *self.inner.prototype.borrow_mut() = prototype;\n    }\n\n    /// Get the prototype of the [`UniqueShape`].\n    pub(crate) fn prototype(&self) -> JsPrototype {\n        self.inner.prototype.borrow().clone()\n    }\n\n    /// Get the property table of the [`UniqueShape`].\n    fn property_table(&self) -> &RefCell<PropertyTableInner> {\n        &self.inner.property_table\n    }\n\n    /// Inserts a new property into the [`UniqueShape`].\n    pub(crate) fn insert_property_transition(&self, key: TransitionKey) -> Self {\n        let mut property_table = self.property_table().borrow_mut();\n        property_table.insert(key.property_key, key.attributes);\n        self.clone()\n    }\n\n    /// Remove a property from the [`UniqueShape`].\n    ///\n    /// This will cause the current shape to be invalidated, and a new [`UniqueShape`] will be returned.\n    pub(crate) fn remove_property_transition(&self, key: &PropertyKey) -> Self {\n        let mut property_table = self.property_table().borrow_mut();\n        let Some((index, _attributes)) = property_table.map.remove(key) else {\n            return self.clone();\n        };\n\n        let index = index as usize;\n\n        // shift elements\n        property_table.keys.remove(index);\n\n        // The property that was deleted was not the last property added.\n        // Therefore we need to create a new unique shape,\n        // to invalidate any pointers to this shape i.e inline caches.\n        let mut property_table = std::mem::take(&mut *property_table);\n\n        // If it is not the last property that was deleted,\n        // then update all the property slots that are after it.\n        if index != property_table.keys.len() {\n            // Get the previous value before the value at index,\n            //\n            // NOTE: calling wrapping_sub when usize index is 0 will wrap into usize::MAX\n            //       which will return None, avoiding unneeded checks.\n            let mut previous_slot = property_table.keys.get(index.wrapping_sub(1)).map(|x| x.1);\n\n            // Update all slot positions\n            for (index, (key, slot)) in property_table.keys.iter_mut().enumerate().skip(index) {\n                *slot = Slot::from_previous(previous_slot, slot.attributes);\n\n                let Some((map_index, map_slot)) = property_table.map.get_mut(key) else {\n                    unreachable!(\"There should already be a property\")\n                };\n                *map_index = index as u32;\n                *map_slot = *slot;\n\n                previous_slot = Some(*slot);\n            }\n        }\n\n        let prototype = self.inner.prototype.borrow_mut().take();\n        Self::new(prototype, property_table)\n    }\n\n    /// Does a property lookup on the [`UniqueShape`] returning the [`Slot`] where it's\n    /// located or [`None`] otherwise.\n    pub(crate) fn lookup(&self, key: &PropertyKey) -> Option<Slot> {\n        let property_table = self.property_table().borrow();\n        if let Some((_, slot)) = property_table.map.get(key) {\n            return Some(*slot);\n        }\n\n        None\n    }\n\n    /// Change the attributes of a property from the [`UniqueShape`].\n    ///\n    /// This will cause the current shape to be invalidated, and a new [`UniqueShape`] will be returned.\n    ///\n    /// NOTE: This assumes that the property had already been inserted.\n    pub(crate) fn change_attributes_transition(\n        &self,\n        key: &TransitionKey,\n    ) -> ChangeTransition<Shape> {\n        let mut property_table = self.property_table().borrow_mut();\n        let Some((index, slot)) = property_table.map.get_mut(&key.property_key) else {\n            unreachable!(\"Attribute change can only happen on existing property\")\n        };\n\n        let index = *index as usize;\n\n        // If property does not change type, there is no need to shift.\n        if slot.attributes.width_match(key.attributes) {\n            slot.attributes = key.attributes;\n            property_table.keys[index].1.attributes = key.attributes;\n            // TODO: invalidate the pointer.\n            return ChangeTransition {\n                shape: self.clone().into(),\n                action: ChangeTransitionAction::Nothing,\n            };\n        }\n        slot.attributes = key.attributes;\n\n        let mut previous_slot = *slot;\n\n        property_table.keys[index].1.attributes = key.attributes;\n\n        let action = if key.attributes.is_accessor_descriptor() {\n            // Data --> Accessor\n            ChangeTransitionAction::Insert\n        } else {\n            // Accessor --> Data\n            ChangeTransitionAction::Remove\n        };\n\n        // The property that was deleted was not the last property added.\n        // Therefore we need to create a new unique shape,\n        // to invalidate any pointers to this shape i.e inline caches.\n        let mut property_table = std::mem::take(&mut *property_table);\n\n        // Update all slot positions, after the target property.\n        //\n        // Example: Change 2nd one from accessor to data\n        //\n        // | Idx: 0, DATA  | Idx: 1, ACCESSOR | Idx: 3, DATA |\n        //                         \\----- target\n        //\n        // Change attributes of target (given trough arguments).\n        //\n        // | Idx: 0, DATA  | Idx: 1, DATA | Idx: 3, DATA |\n        //                         \\----- target\n        //\n        // The target becomes the previous pointer and next is target_index + 1,\n        // continue until we reach the end.\n        //\n        // | Idx: 0, DATA  | Idx: 1, DATA | Idx: 3, DATA |\n        //           previous ----/               \\-------- next\n        //\n        // When next is out of range we quit, everything has been set.\n        //\n        // | Idx: 0, DATA  | Idx: 1, DATA | Idx: 2, DATA |    EMPTY   |\n        //                          previous ----/              \\-------- next\n        //\n        let next = index + 1;\n        for (key, slot) in property_table.keys.iter_mut().skip(next) {\n            *slot = Slot::from_previous(Some(previous_slot), slot.attributes);\n\n            let Some((_, map_slot)) = property_table.map.get_mut(key) else {\n                unreachable!(\"There should already be a property\")\n            };\n            *map_slot = *slot;\n\n            previous_slot = *slot;\n        }\n\n        let prototype = self.inner.prototype.borrow_mut().take();\n        let shape = Self::new(prototype, property_table);\n\n        ChangeTransition {\n            shape: shape.into(),\n            action,\n        }\n    }\n\n    /// Change the prototype of the [`UniqueShape`].\n    ///\n    /// This will cause the current shape to be invalidated, and a new [`UniqueShape`] will be returned.\n    pub(crate) fn change_prototype_transition(&self, prototype: JsPrototype) -> Self {\n        let mut property_table = self.inner.property_table.borrow_mut();\n\n        // We need to create a new unique shape,\n        // to invalidate any pointers to this shape i.e inline caches.\n        let property_table = std::mem::take(&mut *property_table);\n        Self::new(prototype, property_table)\n    }\n\n    /// Gets all keys first strings then symbols in creation order.\n    pub(crate) fn keys(&self) -> Vec<PropertyKey> {\n        self.property_table().borrow().keys()\n    }\n\n    /// Return location in memory of the [`UniqueShape`].\n    pub(crate) fn to_addr_usize(&self) -> usize {\n        let ptr: *const _ = self.inner.as_ref();\n        ptr as usize\n    }\n}\n\n/// Represents a weak reference to [`UniqueShape`].\n#[derive(Debug, Clone, Trace, Finalize, PartialEq)]\npub(crate) struct WeakUniqueShape {\n    inner: WeakGc<Inner>,\n}\n\nimpl WeakUniqueShape {\n    /// Upgrade returns a [`UniqueShape`] pointer for the internal value if the pointer is still live,\n    /// or [`None`] if the value was already garbage collected.\n    #[inline]\n    #[must_use]\n    pub(crate) fn upgrade(&self) -> Option<UniqueShape> {\n        Some(UniqueShape {\n            inner: self.inner.upgrade()?,\n        })\n    }\n}\n\nimpl From<&UniqueShape> for WeakUniqueShape {\n    fn from(value: &UniqueShape) -> Self {\n        WeakUniqueShape {\n            inner: WeakGc::new(&value.inner),\n        }\n    }\n}\n"
  },
  {
    "path": "core/engine/src/object/tests.rs",
    "content": "use crate::{JsNativeErrorKind, TestAction, run_test_actions};\nuse indoc::indoc;\n\n#[test]\nfn ordinary_has_instance_nonobject_prototype() {\n    run_test_actions([TestAction::assert_native_error(\n        indoc! {r#\"\n            function C() {}\n            C.prototype = 1\n            String instanceof C\n        \"#},\n        JsNativeErrorKind::Type,\n        \"function has non-object prototype in instanceof check\",\n    )]);\n}\n\n#[test]\nfn object_properties_return_order() {\n    run_test_actions([\n        TestAction::run_harness(),\n        TestAction::run(indoc! {r#\"\n                var o = {\n                    p1: 'v1',\n                    p2: 'v2',\n                    p3: 'v3',\n                };\n                o.p4 = 'v4';\n                o[2] = 'iv2';\n                o[0] = 'iv0';\n                o[1] = 'iv1';\n                delete o.p1;\n                delete o.p3;\n                o.p1 = 'v1';\n            \"#}),\n        TestAction::assert(r#\"arrayEquals(Object.keys(o), [ \"0\", \"1\", \"2\", \"p2\", \"p4\", \"p1\" ])\"#),\n        TestAction::assert(\n            r#\"arrayEquals(Object.values(o), [ \"iv0\", \"iv1\", \"iv2\", \"v2\", \"v4\", \"v1\" ])\"#,\n        ),\n    ]);\n}\n"
  },
  {
    "path": "core/engine/src/optimizer/mod.rs",
    "content": "pub(crate) mod pass;\npub(crate) mod walker;\n\nuse self::{\n    pass::{ConstantFolding, DeadCodeElimination, StrengthReduction},\n    walker::Walker,\n};\nuse crate::Context;\nuse bitflags::bitflags;\nuse boa_ast::{\n    Expression, StatementList,\n    statement::Statement,\n    visitor::{VisitWith, VisitorMut},\n};\nuse std::{fmt, ops::ControlFlow};\n\nbitflags! {\n    /// Optimizer options.\n    #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]\n    pub struct OptimizerOptions: u8 {\n        /// Print statistics to `stdout`.\n        const STATISTICS = 0b0000_0001;\n        /// Apply constant folding optimization.\n        const CONSTANT_FOLDING = 0b0000_0010;\n        /// Apply strength reduction.\n        const STRENGTH_REDUCTION = 0b0000_0100;\n        /// Apply dead code elimination.\n        const DEAD_CODE_ELIMINATION = 0b0000_1000;\n        /// Apply all optimizations.\n        const OPTIMIZE_ALL = Self::CONSTANT_FOLDING.bits()\n            | Self::STRENGTH_REDUCTION.bits()\n            | Self::DEAD_CODE_ELIMINATION.bits();\n    }\n}\n\n/// The action to be performed after an optimization step.\n#[derive(Debug)]\npub(crate) enum PassAction<T> {\n    /// Keep the node, do nothing.\n    Keep,\n    /// The node was modified inplace.\n    Modified,\n    /// Replace the node.\n    Replace(T),\n}\n\n/// Contains statistics about the optimizer execution.\n#[derive(Debug, Default, Clone, Copy)]\npub struct OptimizerStatistics {\n    /// Constant folding run count.\n    pub constant_folding_run_count: usize,\n    /// Constant folding pass count.\n    pub constant_folding_pass_count: usize,\n    /// Strength reduction run count.\n    pub strength_reduction_run_count: usize,\n    /// Strength reduction pass count.\n    pub strength_reduction_pass_count: usize,\n    /// Dead code elimination count.\n    pub dead_code_elimination_count: usize,\n}\n\nimpl fmt::Display for OptimizerStatistics {\n    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {\n        writeln!(f, \"Optimizer {{\")?;\n        writeln!(\n            f,\n            \"    constant folding: {} run(s), {} pass(es) ({} mutating, {} checking)\",\n            self.constant_folding_run_count,\n            self.constant_folding_pass_count,\n            self.constant_folding_pass_count\n                .saturating_sub(self.constant_folding_run_count),\n            self.constant_folding_run_count\n        )?;\n        writeln!(\n            f,\n            \"    strength reduction: {} run(s), {} pass(es)\",\n            self.strength_reduction_run_count, self.strength_reduction_pass_count,\n        )?;\n        writeln!(\n            f,\n            \"    dead code elimination: {} elimination(s)\",\n            self.dead_code_elimination_count,\n        )?;\n        writeln!(f, \"}}\")?;\n        Ok(())\n    }\n}\n\n/// This represents an AST optimizer.\n#[derive(Debug)]\npub(crate) struct Optimizer<'context> {\n    statistics: OptimizerStatistics,\n    context: &'context mut Context,\n}\n\nimpl<'context> Optimizer<'context> {\n    /// Create a optimizer.\n    pub(crate) fn new(context: &'context mut Context) -> Self {\n        Self {\n            statistics: OptimizerStatistics::default(),\n            context,\n        }\n    }\n\n    /// Run the constant folding optimization on an expression.\n    fn run_constant_folding_pass(&mut self, expr: &mut Expression) -> bool {\n        self.statistics.constant_folding_run_count += 1;\n\n        let mut has_changes = false;\n        for _ in 0..Self::MAX_PASS_ITERATIONS {\n            self.statistics.constant_folding_pass_count += 1;\n            let mut walker = Walker::new(|expr| -> PassAction<Expression> {\n                ConstantFolding::fold_expression(expr, self.context)\n            });\n            // NOTE: postoder traversal is optimal for constant folding,\n            // since it evaluates the tree bottom-up.\n            walker.walk_expression_postorder(expr);\n            if !walker.changed() {\n                break;\n            }\n            has_changes = true;\n        }\n        has_changes\n    }\n\n    /// Maximum number of iterations for optimization passes.\n    /// This prevents infinite loops if a pass has a bug that keeps producing changes.\n    const MAX_PASS_ITERATIONS: usize = 10;\n\n    /// Run the strength reduction optimization on an expression.\n    fn run_strength_reduction_pass(&mut self, expr: &mut Expression) -> bool {\n        self.statistics.strength_reduction_run_count += 1;\n\n        let mut has_changes = false;\n        for _ in 0..Self::MAX_PASS_ITERATIONS {\n            self.statistics.strength_reduction_pass_count += 1;\n            let mut walker = Walker::new(|expr| -> PassAction<Expression> {\n                StrengthReduction::reduce_expression(expr)\n            });\n            walker.walk_expression_postorder(expr);\n            if !walker.changed() {\n                break;\n            }\n            has_changes = true;\n        }\n        has_changes\n    }\n\n    fn run_all(&mut self, expr: &mut Expression) {\n        if self\n            .context\n            .optimizer_options()\n            .contains(OptimizerOptions::CONSTANT_FOLDING)\n        {\n            self.run_constant_folding_pass(expr);\n        }\n        if self\n            .context\n            .optimizer_options()\n            .contains(OptimizerOptions::STRENGTH_REDUCTION)\n        {\n            self.run_strength_reduction_pass(expr);\n        }\n    }\n\n    /// Apply optimizations inplace.\n    pub(crate) fn apply(&mut self, statement_list: &mut StatementList) -> OptimizerStatistics {\n        let _ = self.visit_statement_list_mut(statement_list);\n\n        #[allow(clippy::print_stdout)]\n        if self\n            .context\n            .optimizer_options()\n            .contains(OptimizerOptions::STATISTICS)\n        {\n            println!(\"{}\", self.statistics);\n        }\n        self.statistics\n    }\n}\n\nimpl<'ast> VisitorMut<'ast> for Optimizer<'_> {\n    type BreakTy = ();\n\n    fn visit_expression_mut(&mut self, node: &'ast mut Expression) -> ControlFlow<Self::BreakTy> {\n        self.run_all(node);\n        ControlFlow::Continue(())\n    }\n\n    fn visit_statement_mut(&mut self, node: &'ast mut Statement) -> ControlFlow<Self::BreakTy> {\n        // First, recurse into children so constant folding and\n        // strength reduction run on nested expressions.\n        node.visit_with_mut(self)?;\n\n        // Then apply dead code elimination if enabled.\n        if self\n            .context\n            .optimizer_options()\n            .contains(OptimizerOptions::DEAD_CODE_ELIMINATION)\n        {\n            match node {\n                Statement::If(if_stmt) => {\n                    if let PassAction::Replace(replacement) =\n                        DeadCodeElimination::try_eliminate_if(if_stmt)\n                    {\n                        *node = replacement;\n                        self.statistics.dead_code_elimination_count += 1;\n                    }\n                }\n                Statement::WhileLoop(while_loop) => {\n                    if let PassAction::Replace(replacement) =\n                        DeadCodeElimination::try_eliminate_while(while_loop)\n                    {\n                        *node = replacement;\n                        self.statistics.dead_code_elimination_count += 1;\n                    }\n                }\n                Statement::ForLoop(for_loop) => {\n                    if let PassAction::Replace(replacement) =\n                        DeadCodeElimination::try_eliminate_for(for_loop)\n                    {\n                        *node = replacement;\n                        self.statistics.dead_code_elimination_count += 1;\n                    }\n                }\n                _ => {}\n            }\n        }\n\n        ControlFlow::Continue(())\n    }\n}\n"
  },
  {
    "path": "core/engine/src/optimizer/pass/constant_folding.rs",
    "content": "use crate::value::JsVariant;\nuse crate::{\n    Context, JsBigInt, JsValue, builtins::Number, bytecompiler::ToJsString, optimizer::PassAction,\n    value::Numeric,\n};\nuse boa_ast::expression::literal::Literal;\nuse boa_ast::{\n    Expression, Spanned,\n    expression::{\n        literal::LiteralKind,\n        operator::{\n            Binary, Unary,\n            binary::{ArithmeticOp, BinaryOp, BitwiseOp, LogicalOp, RelationalOp},\n            unary::UnaryOp,\n        },\n    },\n};\nuse boa_interner::JStrRef;\n\nfn literal_to_js_value(literal: &Literal, context: &mut Context) -> JsValue {\n    match literal.kind() {\n        LiteralKind::String(v) => JsValue::new(v.to_js_string(context.interner())),\n        LiteralKind::Num(v) => JsValue::new(*v),\n        LiteralKind::Int(v) => JsValue::new(*v),\n        LiteralKind::BigInt(v) => JsValue::new(JsBigInt::new(v.clone())),\n        LiteralKind::Bool(v) => JsValue::new(*v),\n        LiteralKind::Null => JsValue::null(),\n        LiteralKind::Undefined => JsValue::undefined(),\n    }\n}\n\nfn js_value_to_literal_kind(value: &JsValue, context: &mut Context) -> LiteralKind {\n    match value.variant() {\n        JsVariant::Null => LiteralKind::Null,\n        JsVariant::Undefined => LiteralKind::Undefined,\n        JsVariant::Boolean(v) => LiteralKind::Bool(v),\n        JsVariant::String(v) => {\n            // TODO: Replace JStrRef with JsStr this would eliminate the to_vec call.\n            let v = v.to_vec();\n            LiteralKind::String(context.interner_mut().get_or_intern(JStrRef::Utf16(&v)))\n        }\n        JsVariant::Float64(v) => LiteralKind::Num(v),\n        JsVariant::Integer32(v) => LiteralKind::Int(v),\n        JsVariant::BigInt(v) => LiteralKind::BigInt(Box::new(v.as_inner().clone())),\n        JsVariant::Object(_) | JsVariant::Symbol(_) => {\n            unreachable!(\"value must not be an object or symbol\")\n        }\n    }\n}\n\n#[derive(Debug, Default)]\npub(crate) struct ConstantFolding {}\n\nimpl ConstantFolding {\n    pub(crate) fn fold_expression(\n        expr: &mut Expression,\n        context: &mut Context,\n    ) -> PassAction<Expression> {\n        match expr {\n            Expression::Unary(unary) => Self::constant_fold_unary_expr(unary, context),\n            Expression::Binary(binary) => Self::constant_fold_binary_expr(binary, context),\n            _ => PassAction::Keep,\n        }\n    }\n\n    fn constant_fold_unary_expr(\n        unary: &mut Unary,\n        context: &mut Context,\n    ) -> PassAction<Expression> {\n        let Expression::Literal(literal) = unary.target() else {\n            return PassAction::Keep;\n        };\n        let value = match (literal, unary.op()) {\n            (literal, UnaryOp::Minus) => literal_to_js_value(literal, context).neg(context),\n            (literal, UnaryOp::Plus) => literal_to_js_value(literal, context)\n                .to_number(context)\n                .map(JsValue::new),\n            (literal, UnaryOp::Not) => literal_to_js_value(literal, context)\n                .not()\n                .map(JsValue::new),\n            (literal, UnaryOp::Tilde) => Ok(\n                match literal_to_js_value(literal, context)\n                    .to_numeric(context)\n                    .expect(\"should not fail\")\n                {\n                    Numeric::Number(number) => Number::not(number).into(),\n                    Numeric::BigInt(bigint) => JsBigInt::not(&bigint).into(),\n                },\n            ),\n            (literal, UnaryOp::TypeOf) => Ok(JsValue::new(\n                literal_to_js_value(literal, context).js_type_of(),\n            )),\n            (_, UnaryOp::Delete) => {\n                return PassAction::Replace(Literal::new(true, unary.span()).into());\n            }\n            (_, UnaryOp::Void) => {\n                return PassAction::Replace(\n                    Literal::new(LiteralKind::Undefined, unary.span()).into(),\n                );\n            }\n        };\n\n        // If it fails then revert changes\n        let Ok(value) = value else {\n            return PassAction::Keep;\n        };\n\n        PassAction::Replace(Expression::Literal(Literal::new(\n            js_value_to_literal_kind(&value, context),\n            unary.span(),\n        )))\n    }\n\n    fn constant_fold_binary_expr(\n        binary: &mut Binary,\n        context: &mut Context,\n    ) -> PassAction<Expression> {\n        let Expression::Literal(lhs) = binary.lhs() else {\n            return PassAction::Keep;\n        };\n\n        // We know that the lhs is a literal (pure expression) therefore the following\n        // optimization can be done:\n        //\n        // (pure_expression, call()) --> call()\n        //\n        // We cannot optimize it if rhs is `eval` or function call, because it is considered an indirect call,\n        // which is not the same as direct call.\n        //\n        // The lhs will replace with `undefined`, to simplify it as much as possible:\n        //\n        // (complex_pure_expression, eval)                     --> (undefined, eval)\n        // (complex_pure_expression, Object.prototype.valueOf) --> (undefined, Object.prototype.valueOf)\n        if binary.op() == BinaryOp::Comma {\n            let span = binary.span();\n            if !matches!(binary.rhs(), Expression::Literal(_)) {\n                // If left-hand side is already undefined then just keep it,\n                // so we don't cause an infinite loop.\n                if let Expression::Literal(literal) = binary.lhs()\n                    && literal.is_undefined()\n                {\n                    return PassAction::Keep;\n                }\n\n                *binary.lhs_mut() = Literal::new(LiteralKind::Undefined, span).into();\n                return PassAction::Modified;\n            }\n\n            // We take rhs, by replacing with a dummy value.\n            let rhs = std::mem::replace(\n                binary.rhs_mut(),\n                Literal::new(LiteralKind::Undefined, span).into(),\n            );\n            return PassAction::Replace(rhs);\n        }\n\n        let lhs = literal_to_js_value(lhs, context);\n\n        let span = binary.span();\n\n        // Do the following optimizations if it's a logical binary expression:\n        //\n        // falsy              && call() --> falsy\n        // truthy             || call() --> truthy\n        // null/undefined     ?? call() --> call()\n        //\n        // The following **only** apply if the left-hand side is a pure expression (without side-effects):\n        //\n        // NOTE: The left-hand side is always pure because we check that it is a literal, above.\n        //\n        // falsy              || call() --> call()\n        // truthy             && call() --> call()\n        // non-null/undefined ?? call() --> non-null/undefined\n        if let BinaryOp::Logical(op) = binary.op() {\n            let expr = match op {\n                LogicalOp::And => {\n                    if lhs.to_boolean() {\n                        std::mem::replace(\n                            binary.rhs_mut(),\n                            Literal::new(LiteralKind::Undefined, span).into(),\n                        )\n                    } else {\n                        std::mem::replace(\n                            binary.lhs_mut(),\n                            Literal::new(LiteralKind::Undefined, span).into(),\n                        )\n                    }\n                }\n                LogicalOp::Or => {\n                    if lhs.to_boolean() {\n                        std::mem::replace(\n                            binary.lhs_mut(),\n                            Literal::new(LiteralKind::Undefined, span).into(),\n                        )\n                    } else {\n                        std::mem::replace(\n                            binary.rhs_mut(),\n                            Literal::new(LiteralKind::Undefined, span).into(),\n                        )\n                    }\n                }\n                LogicalOp::Coalesce => {\n                    if lhs.is_null_or_undefined() {\n                        std::mem::replace(\n                            binary.rhs_mut(),\n                            Literal::new(LiteralKind::Undefined, span).into(),\n                        )\n                    } else {\n                        std::mem::replace(\n                            binary.lhs_mut(),\n                            Literal::new(LiteralKind::Undefined, span).into(),\n                        )\n                    }\n                }\n            };\n            return PassAction::Replace(expr);\n        }\n\n        let Expression::Literal(rhs_literal) = binary.rhs() else {\n            return PassAction::Keep;\n        };\n\n        let rhs = literal_to_js_value(rhs_literal, context);\n\n        let value = match binary.op() {\n            BinaryOp::Arithmetic(op) => match op {\n                ArithmeticOp::Add => lhs.add(&rhs, context),\n                ArithmeticOp::Sub => lhs.sub(&rhs, context),\n                ArithmeticOp::Div => lhs.div(&rhs, context),\n                ArithmeticOp::Mul => lhs.mul(&rhs, context),\n                ArithmeticOp::Exp => lhs.pow(&rhs, context),\n                ArithmeticOp::Mod => lhs.rem(&rhs, context),\n            },\n            BinaryOp::Bitwise(op) => match op {\n                BitwiseOp::And => lhs.bitand(&rhs, context),\n                BitwiseOp::Or => lhs.bitor(&rhs, context),\n                BitwiseOp::Xor => lhs.bitxor(&rhs, context),\n                BitwiseOp::Shl => lhs.shl(&rhs, context),\n                BitwiseOp::Shr => lhs.shr(&rhs, context),\n                BitwiseOp::UShr => lhs.ushr(&rhs, context),\n            },\n            BinaryOp::Relational(op) => match op {\n                RelationalOp::In | RelationalOp::InstanceOf => return PassAction::Keep,\n                RelationalOp::Equal => lhs.equals(&rhs, context).map(JsValue::new),\n                RelationalOp::NotEqual => lhs.equals(&rhs, context).map(|x| !x).map(JsValue::new),\n                RelationalOp::StrictEqual => Ok(JsValue::new(lhs.strict_equals(&rhs))),\n                RelationalOp::StrictNotEqual => Ok(JsValue::new(!lhs.strict_equals(&rhs))),\n                RelationalOp::GreaterThan => lhs.gt(&rhs, context).map(JsValue::new),\n                RelationalOp::GreaterThanOrEqual => lhs.ge(&rhs, context).map(JsValue::new),\n                RelationalOp::LessThan => lhs.lt(&rhs, context).map(JsValue::new),\n                RelationalOp::LessThanOrEqual => lhs.le(&rhs, context).map(JsValue::new),\n            },\n            BinaryOp::Logical(_) => {\n                unreachable!(\"We already checked if it's a logical binary expression!\")\n            }\n            BinaryOp::Comma => unreachable!(\"We already checked if it's a comma expression!\"),\n        };\n\n        // If it fails then revert changes\n        let Ok(value) = value else {\n            return PassAction::Keep;\n        };\n\n        PassAction::Replace(\n            Literal::new(js_value_to_literal_kind(&value, context), binary.span()).into(),\n        )\n    }\n}\n"
  },
  {
    "path": "core/engine/src/optimizer/pass/dead_code_elimination.rs",
    "content": "use crate::optimizer::PassAction;\nuse boa_ast::{\n    Expression,\n    expression::literal::LiteralKind,\n    statement::{If, Statement},\n    visitor::{VisitWith, Visitor},\n};\nuse core::ops::ControlFlow;\n\n#[derive(Debug, Default)]\nstruct ContainsHoistedDeclarationsVisitor {\n    found: bool,\n}\n\nimpl<'ast> Visitor<'ast> for ContainsHoistedDeclarationsVisitor {\n    type BreakTy = ();\n\n    fn visit_var_declaration(\n        &mut self,\n        _: &'ast boa_ast::declaration::VarDeclaration,\n    ) -> ControlFlow<Self::BreakTy> {\n        self.found = true;\n        ControlFlow::Break(())\n    }\n\n    fn visit_function_declaration(\n        &mut self,\n        _: &'ast boa_ast::function::FunctionDeclaration,\n    ) -> ControlFlow<Self::BreakTy> {\n        self.found = true;\n        ControlFlow::Break(())\n    }\n\n    fn visit_generator_declaration(\n        &mut self,\n        _: &'ast boa_ast::function::GeneratorDeclaration,\n    ) -> ControlFlow<Self::BreakTy> {\n        self.found = true;\n        ControlFlow::Break(())\n    }\n\n    fn visit_async_function_declaration(\n        &mut self,\n        _: &'ast boa_ast::function::AsyncFunctionDeclaration,\n    ) -> ControlFlow<Self::BreakTy> {\n        self.found = true;\n        ControlFlow::Break(())\n    }\n\n    fn visit_async_generator_declaration(\n        &mut self,\n        _: &'ast boa_ast::function::AsyncGeneratorDeclaration,\n    ) -> ControlFlow<Self::BreakTy> {\n        self.found = true;\n        ControlFlow::Break(())\n    }\n}\n\n#[derive(Debug, Default)]\npub(crate) struct DeadCodeElimination;\n\nimpl DeadCodeElimination {\n    fn as_literal_bool(expr: &Expression) -> Option<bool> {\n        if let Expression::Literal(lit) = expr\n            && let LiteralKind::Bool(v) = lit.kind()\n        {\n            return Some(*v);\n        }\n        None\n    }\n\n    fn contains_hoisted_declarations(stmt: &Statement) -> bool {\n        let mut visitor = ContainsHoistedDeclarationsVisitor { found: false };\n        let _ = stmt.visit_with(&mut visitor);\n        visitor.found\n    }\n\n    pub(crate) fn try_eliminate_if(if_stmt: &If) -> PassAction<Statement> {\n        let Some(cond_value) = Self::as_literal_bool(if_stmt.cond()) else {\n            return PassAction::Keep;\n        };\n\n        if cond_value {\n            if let Some(alt) = if_stmt.else_node()\n                && Self::contains_hoisted_declarations(alt)\n            {\n                return PassAction::Keep;\n            }\n            PassAction::Replace(if_stmt.body().clone())\n        } else {\n            if Self::contains_hoisted_declarations(if_stmt.body()) {\n                return PassAction::Keep;\n            }\n            match if_stmt.else_node() {\n                Some(alt) => PassAction::Replace(alt.clone()),\n                None => PassAction::Replace(Statement::Empty),\n            }\n        }\n    }\n\n    pub(crate) fn try_eliminate_while(\n        while_loop: &boa_ast::statement::iteration::WhileLoop,\n    ) -> PassAction<Statement> {\n        let Some(cond_value) = Self::as_literal_bool(while_loop.condition()) else {\n            return PassAction::Keep;\n        };\n\n        if !cond_value {\n            if Self::contains_hoisted_declarations(while_loop.body()) {\n                return PassAction::Keep;\n            }\n            return PassAction::Replace(Statement::Empty);\n        }\n\n        PassAction::Keep\n    }\n\n    pub(crate) fn try_eliminate_for(\n        for_loop: &boa_ast::statement::iteration::ForLoop,\n    ) -> PassAction<Statement> {\n        let Some(condition) = for_loop.condition() else {\n            return PassAction::Keep;\n        };\n\n        let Some(cond_value) = Self::as_literal_bool(condition) else {\n            return PassAction::Keep;\n        };\n\n        if !cond_value {\n            if Self::contains_hoisted_declarations(for_loop.body()) {\n                return PassAction::Keep;\n            }\n\n            // If there's an initializer, it might have side-effects (e.g., `for (let i = doAuth();;)`).\n            // Removing the loop entirely could bypass those side effects. The safest action is to keep the loop.\n            if for_loop.init().is_some() {\n                return PassAction::Keep;\n            }\n\n            return PassAction::Replace(Statement::Empty);\n        }\n\n        PassAction::Keep\n    }\n}\n"
  },
  {
    "path": "core/engine/src/optimizer/pass/mod.rs",
    "content": "mod constant_folding;\nmod dead_code_elimination;\nmod strength_reduction;\n\npub(crate) use constant_folding::ConstantFolding;\npub(crate) use dead_code_elimination::DeadCodeElimination;\npub(crate) use strength_reduction::StrengthReduction;\n"
  },
  {
    "path": "core/engine/src/optimizer/pass/strength_reduction.rs",
    "content": "use crate::optimizer::PassAction;\nuse boa_ast::{\n    Expression, Spanned,\n    expression::operator::{\n        Binary,\n        binary::{ArithmeticOp, BinaryOp},\n    },\n};\n\n#[derive(Debug, Default)]\npub(crate) struct StrengthReduction;\n\nimpl StrengthReduction {\n    pub(crate) fn reduce_expression(expr: &mut Expression) -> PassAction<Expression> {\n        match expr {\n            Expression::Binary(binary) => Self::try_reduce_binary(binary),\n            _ => PassAction::Keep,\n        }\n    }\n\n    fn is_side_effect_free(expr: &Expression) -> bool {\n        matches!(expr, Expression::Literal(_) | Expression::Identifier(_))\n    }\n\n    fn as_literal_int(expr: &Expression) -> Option<i32> {\n        if let Expression::Literal(lit) = expr\n            && let boa_ast::expression::literal::LiteralKind::Int(v) = lit.kind()\n        {\n            return Some(*v);\n        }\n        None\n    }\n\n    fn try_reduce_binary(binary: &mut Binary) -> PassAction<Expression> {\n        match binary.op() {\n            BinaryOp::Arithmetic(ArithmeticOp::Exp) => Self::try_reduce_exp(binary),\n            BinaryOp::Arithmetic(ArithmeticOp::Div) => Self::try_reduce_div(binary),\n            _ => PassAction::Keep,\n        }\n    }\n\n    fn try_reduce_div(binary: &mut Binary) -> PassAction<Expression> {\n        if let Some(div_val) = Self::as_literal_int(binary.rhs())\n            && div_val == 2\n        {\n            let span = binary.span();\n            let lhs = std::mem::replace(\n                binary.lhs_mut(),\n                boa_ast::expression::literal::Literal::new(\n                    boa_ast::expression::literal::LiteralKind::Undefined,\n                    span,\n                )\n                .into(),\n            );\n\n            return PassAction::Replace(\n                Binary::new(\n                    BinaryOp::Arithmetic(ArithmeticOp::Mul),\n                    lhs,\n                    boa_ast::expression::literal::Literal::new(\n                        boa_ast::expression::literal::LiteralKind::Num(0.5),\n                        span,\n                    )\n                    .into(),\n                )\n                .into(),\n            );\n        }\n\n        PassAction::Keep\n    }\n\n    fn try_reduce_exp(binary: &mut Binary) -> PassAction<Expression> {\n        if let Some(exp_val) = Self::as_literal_int(binary.rhs())\n            && exp_val == 2\n            && Self::is_side_effect_free(binary.lhs())\n        {\n            let span = binary.span();\n            let lhs = std::mem::replace(\n                binary.lhs_mut(),\n                boa_ast::expression::literal::Literal::new(\n                    boa_ast::expression::literal::LiteralKind::Undefined,\n                    span,\n                )\n                .into(),\n            );\n            let lhs2 = lhs.clone();\n\n            return PassAction::Replace(\n                Binary::new(BinaryOp::Arithmetic(ArithmeticOp::Mul), lhs, lhs2).into(),\n            );\n        }\n\n        PassAction::Keep\n    }\n}\n"
  },
  {
    "path": "core/engine/src/optimizer/walker.rs",
    "content": "use super::PassAction;\nuse boa_ast::{\n    Expression,\n    visitor::{VisitWith, VisitorMut},\n};\nuse std::{convert::Infallible, ops::ControlFlow};\n\n/// The utility structure that traverses the AST.\npub(crate) struct Walker<F>\nwhere\n    F: FnMut(&mut Expression) -> PassAction<Expression>,\n{\n    /// The function to be applied to the node.\n    f: F,\n\n    /// Did a change happen while traversing.\n    changed: bool,\n}\n\nimpl<F> Walker<F>\nwhere\n    F: FnMut(&mut Expression) -> PassAction<Expression>,\n{\n    pub(crate) const fn new(f: F) -> Self {\n        Self { f, changed: false }\n    }\n\n    pub(crate) const fn changed(&self) -> bool {\n        self.changed\n    }\n\n    /// Walk the AST in postorder.\n    pub(crate) fn walk_expression_postorder(&mut self, expr: &mut Expression) {\n        let _ = self.visit_expression_mut(expr);\n    }\n}\n\nimpl<'ast, F> VisitorMut<'ast> for Walker<F>\nwhere\n    F: FnMut(&mut Expression) -> PassAction<Expression>,\n{\n    type BreakTy = Infallible;\n\n    /// Visits the tree in postorder.\n    fn visit_expression_mut(&mut self, expr: &'ast mut Expression) -> ControlFlow<Self::BreakTy> {\n        expr.visit_with_mut(self)?;\n\n        match (self.f)(expr) {\n            PassAction::Keep => {}\n            PassAction::Modified => self.changed = true,\n            PassAction::Replace(new) => {\n                *expr = new;\n                self.changed = true;\n            }\n        }\n\n        ControlFlow::Continue(())\n    }\n}\n"
  },
  {
    "path": "core/engine/src/property/attribute/mod.rs",
    "content": "//! This module implements the `Attribute` struct which contains the attributes for property descriptors.\n\nuse bitflags::bitflags;\n\n#[cfg(test)]\nmod tests;\n\nbitflags! {\n    /// This struct contains the property flags as described in the ECMAScript specification.\n    ///\n    /// It contains the following flags:\n    ///  - `[[Writable]]` (`WRITABLE`) - If `false`, attempts by ECMAScript code to change the property's\n    /// `[[Value]]` attribute using `[[Set]]` will not succeed.\n    ///  - `[[Enumerable]]` (`ENUMERABLE`) - If the property will be enumerated by a for-in enumeration.\n    ///  - `[[Configurable]]` (`CONFIGURABLE`) - If `false`, attempts to delete the property,\n    /// change the property to be an `accessor property`, or change its attributes (other than `[[Value]]`,\n    /// or changing `[[Writable]]` to `false`) will fail.\n    #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]\n    pub struct Attribute: u8 {\n        /// The `Writable` attribute decides whether the value associated with the property can be changed or not, from its initial value.\n        const WRITABLE = 0b0000_0001;\n\n        /// If the property can be enumerated by a `for-in` loop.\n        const ENUMERABLE = 0b0000_0010;\n\n        /// If the property descriptor can be changed later.\n        const CONFIGURABLE = 0b0000_0100;\n\n        /// The property is not writable.\n        const READONLY = 0b0000_0000;\n\n        /// The property can not be enumerated in a `for-in` loop.\n        const NON_ENUMERABLE = 0b0000_0000;\n\n        /// The property descriptor cannot be changed.\n        const PERMANENT = 0b0000_0000;\n    }\n}\n\nimpl Attribute {\n    /// Clear all flags.\n    #[inline]\n    pub fn clear(&mut self) {\n        *self.0.bits_mut() = 0;\n    }\n\n    /// Sets the `writable` flag.\n    #[inline]\n    pub fn set_writable(&mut self, value: bool) {\n        if value {\n            *self |= Self::WRITABLE;\n        } else {\n            *self |= *self & !Self::WRITABLE;\n        }\n    }\n\n    /// Gets the `writable` flag.\n    #[inline]\n    #[must_use]\n    pub const fn writable(self) -> bool {\n        self.contains(Self::WRITABLE)\n    }\n\n    /// Sets the `enumerable` flag.\n    #[inline]\n    pub fn set_enumerable(&mut self, value: bool) {\n        if value {\n            *self |= Self::ENUMERABLE;\n        } else {\n            *self |= *self & !Self::ENUMERABLE;\n        }\n    }\n\n    /// Gets the `enumerable` flag.\n    #[inline]\n    #[must_use]\n    pub const fn enumerable(self) -> bool {\n        self.contains(Self::ENUMERABLE)\n    }\n\n    /// Sets the `configurable` flag.\n    #[inline]\n    pub fn set_configurable(&mut self, value: bool) {\n        if value {\n            *self |= Self::CONFIGURABLE;\n        } else {\n            *self |= *self & !Self::CONFIGURABLE;\n        }\n    }\n\n    /// Gets the `configurable` flag.\n    #[inline]\n    #[must_use]\n    pub const fn configurable(self) -> bool {\n        self.contains(Self::CONFIGURABLE)\n    }\n}\n\nimpl Default for Attribute {\n    /// Returns the default flags according to the [ECMAScript specification][spec].\n    ///\n    /// [spec]: https://tc39.es/ecma262/#table-default-attribute-values\n    fn default() -> Self {\n        Self::READONLY | Self::NON_ENUMERABLE | Self::PERMANENT\n    }\n}\n"
  },
  {
    "path": "core/engine/src/property/attribute/tests.rs",
    "content": "use super::Attribute;\n\n#[test]\nfn writable() {\n    let attribute = Attribute::WRITABLE;\n\n    assert!(attribute.writable());\n}\n\n#[test]\nfn enumerable() {\n    let attribute = Attribute::ENUMERABLE;\n\n    assert!(attribute.enumerable());\n}\n\n#[test]\nfn configurable() {\n    let attribute = Attribute::CONFIGURABLE;\n\n    assert!(attribute.configurable());\n}\n\n#[test]\nfn writable_and_enumerable() {\n    let attribute = Attribute::WRITABLE | Attribute::ENUMERABLE;\n\n    assert!(attribute.writable());\n    assert!(attribute.enumerable());\n}\n\n#[test]\nfn enumerable_configurable() {\n    let attribute = Attribute::ENUMERABLE | Attribute::CONFIGURABLE;\n\n    assert!(!attribute.writable());\n\n    assert!(attribute.enumerable());\n    assert!(attribute.configurable());\n}\n\n#[test]\nfn writable_enumerable_configurable() {\n    let attribute = Attribute::WRITABLE | Attribute::ENUMERABLE | Attribute::CONFIGURABLE;\n\n    assert!(attribute.writable());\n    assert!(attribute.enumerable());\n    assert!(attribute.configurable());\n}\n\n#[test]\nfn default() {\n    let attribute = Attribute::default();\n\n    assert!(!attribute.writable());\n    assert!(!attribute.enumerable());\n    assert!(!attribute.configurable());\n}\n\n#[test]\nfn clear() {\n    let mut attribute = Attribute::default();\n\n    attribute.clear();\n\n    assert!(!attribute.writable());\n    assert!(!attribute.enumerable());\n    assert!(!attribute.configurable());\n\n    assert!(attribute.is_empty());\n}\n\n#[test]\nfn set_writable_to_true() {\n    let mut attribute = Attribute::default();\n\n    attribute.set_writable(true);\n\n    assert!(attribute.writable());\n    assert!(!attribute.enumerable());\n    assert!(!attribute.configurable());\n}\n\n#[test]\nfn set_writable_to_false() {\n    let mut attribute = Attribute::default();\n\n    attribute.set_writable(false);\n\n    assert!(!attribute.writable());\n    assert!(!attribute.enumerable());\n    assert!(!attribute.configurable());\n}\n\n#[test]\nfn set_enumerable_to_true() {\n    let mut attribute = Attribute::default();\n\n    attribute.set_enumerable(true);\n\n    assert!(!attribute.writable());\n    assert!(attribute.enumerable());\n    assert!(!attribute.configurable());\n}\n\n#[test]\nfn set_enumerable_to_false() {\n    let mut attribute = Attribute::default();\n\n    attribute.set_enumerable(false);\n\n    assert!(!attribute.writable());\n    assert!(!attribute.enumerable());\n    assert!(!attribute.configurable());\n}\n\n#[test]\nfn set_configurable_to_true() {\n    let mut attribute = Attribute::default();\n\n    attribute.set_configurable(true);\n\n    assert!(!attribute.writable());\n    assert!(!attribute.enumerable());\n    assert!(attribute.configurable());\n}\n\n#[test]\nfn set_configurable_to_false() {\n    let mut attribute = Attribute::default();\n\n    attribute.set_configurable(false);\n\n    assert!(!attribute.writable());\n    assert!(!attribute.enumerable());\n    assert!(!attribute.configurable());\n}\n"
  },
  {
    "path": "core/engine/src/property/mod.rs",
    "content": "//! Boa's implementation of ECMAScript's Property Descriptor.\n//!\n//! The Property Descriptor type is used to explain the manipulation and reification of `Object`\n//! property attributes. Values of the Property Descriptor type are Records. Each field's name is\n//! an attribute name and its value is a corresponding attribute value as specified in\n//! [6.1.7.1][section]. In addition, any field may be present or absent. The schema name used\n//! within this specification to tag literal descriptions of Property Descriptor records is\n//! `PropertyDescriptor`.\n//!\n//! More information:\n//!  - [MDN documentation][mdn]\n//!  - [ECMAScript reference][spec]\n//!\n//! [spec]: https://tc39.es/ecma262/#sec-property-descriptor-specification-type\n//! [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/defineProperty\n//! [section]: https://tc39.es/ecma262/#sec-property-attributes\n\nmod attribute;\nmod nonmaxu32;\n\nuse crate::{\n    JsString, JsSymbol, JsValue, js_string, object::shape::slot::SlotAttributes, string::JsStr,\n};\nuse boa_gc::{Finalize, Trace};\nuse std::{fmt, iter::FusedIterator};\n\npub use {attribute::Attribute, nonmaxu32::NonMaxU32};\n\n/// This represents an ECMAScript Property AKA The Property Descriptor.\n///\n/// Property descriptors present in objects come in three main flavors:\n///  - data descriptors\n///  - accessor descriptors\n///  - generic descriptor\n///\n/// A data Property Descriptor is one that includes any fields named either\n/// \\[\\[Value\\]\\] or \\[\\[Writable\\]\\].\n///\n/// An accessor Property Descriptor is one that includes any fields named either\n/// \\[\\[Get\\]\\] or \\[\\[Set\\]\\].\n///\n/// A generic Property Descriptor is a Property Descriptor value that is neither\n/// a data Property Descriptor nor an accessor Property Descriptor.\n///\n/// More information:\n/// - [MDN documentation][mdn]\n/// - [ECMAScript reference][spec]\n///\n/// [spec]: https://tc39.es/ecma262/#sec-property-descriptor-specification-type\n/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/defineProperty\n#[derive(Default, Debug, Clone, Trace, Finalize)]\npub struct PropertyDescriptor {\n    enumerable: Option<bool>,\n    configurable: Option<bool>,\n    kind: DescriptorKind,\n}\n\n/// `DescriptorKind` represents the different kinds of property descriptors.\n#[derive(Debug, Default, Clone, Trace, Finalize)]\npub enum DescriptorKind {\n    /// A data property descriptor.\n    Data {\n        /// The value of the property.\n        value: Option<JsValue>,\n\n        /// Whether the property is writable.\n        writable: Option<bool>,\n    },\n\n    /// An accessor property descriptor.\n    Accessor {\n        /// The getter of the property.\n        get: Option<JsValue>,\n\n        /// The setter of the property.\n        set: Option<JsValue>,\n    },\n\n    /// A generic property descriptor.\n    #[default]\n    Generic,\n}\n\nimpl PropertyDescriptor {\n    /// An accessor property descriptor is one that includes any fields named either `[[Get]]` or `[[Set]]`.\n    ///\n    /// More information:\n    /// - [ECMAScript reference][spec]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-isaccessordescriptor\n    #[inline]\n    #[must_use]\n    pub const fn is_accessor_descriptor(&self) -> bool {\n        matches!(self.kind, DescriptorKind::Accessor { .. })\n    }\n\n    /// A data property descriptor is one that includes any fields named either `[[Value]]` or `[[Writable]]`.\n    ///\n    /// More information:\n    /// - [ECMAScript reference][spec]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-isdatadescriptor\n    #[inline]\n    #[must_use]\n    pub const fn is_data_descriptor(&self) -> bool {\n        matches!(self.kind, DescriptorKind::Data { .. })\n    }\n\n    /// A generic property descriptor is one that is neither a data descriptor nor an accessor descriptor.\n    ///\n    /// More information:\n    /// - [ECMAScript reference][spec]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-isgenericdescriptor\n    #[inline]\n    #[must_use]\n    pub const fn is_generic_descriptor(&self) -> bool {\n        matches!(self.kind, DescriptorKind::Generic)\n    }\n\n    /// Returns if the property descriptor is empty.\n    #[inline]\n    #[must_use]\n    pub const fn is_empty(&self) -> bool {\n        self.is_generic_descriptor() && self.enumerable.is_none() && self.configurable.is_none()\n    }\n\n    /// Returns if the property descriptor is enumerable.\n    /// Returns `None` if the `enumerable` field is not set.\n    #[inline]\n    #[must_use]\n    pub const fn enumerable(&self) -> Option<bool> {\n        self.enumerable\n    }\n\n    /// Returns if the property descriptor is configurable.\n    /// Returns `None` if the `configurable` field is not set.\n    #[inline]\n    #[must_use]\n    pub const fn configurable(&self) -> Option<bool> {\n        self.configurable\n    }\n\n    /// Returns if the property descriptor is writable.\n    /// Returns `None` if the `writable` field is not set or the property descriptor is not a data descriptor.\n    #[inline]\n    #[must_use]\n    pub const fn writable(&self) -> Option<bool> {\n        match self.kind {\n            DescriptorKind::Data { writable, .. } => writable,\n            _ => None,\n        }\n    }\n\n    /// Returns the value of the property descriptor.\n    /// Returns `None` if the value is not set or the property descriptor is not a data descriptor.\n    #[inline]\n    #[must_use]\n    pub const fn value(&self) -> Option<&JsValue> {\n        match &self.kind {\n            DescriptorKind::Data { value, .. } => value.as_ref(),\n            _ => None,\n        }\n    }\n\n    /// Returns the getter of the property descriptor.\n    /// Returns `None` if the getter is not set or the property descriptor is not an accessor descriptor.\n    #[inline]\n    #[must_use]\n    pub const fn get(&self) -> Option<&JsValue> {\n        match &self.kind {\n            DescriptorKind::Accessor { get, .. } => get.as_ref(),\n            _ => None,\n        }\n    }\n\n    /// Returns the setter of the property descriptor.\n    /// Returns `None` if the setter is not set or the property descriptor is not an accessor descriptor.\n    #[inline]\n    #[must_use]\n    pub const fn set(&self) -> Option<&JsValue> {\n        match &self.kind {\n            DescriptorKind::Accessor { set, .. } => set.as_ref(),\n            _ => None,\n        }\n    }\n\n    /// Returns if the property descriptor is enumerable.\n    ///\n    /// # Panics\n    ///\n    /// Panics if the `enumerable` field is not set.\n    #[inline]\n    #[must_use]\n    pub fn expect_enumerable(&self) -> bool {\n        self.enumerable\n            .expect(\"[[enumerable]] field not in property descriptor\")\n    }\n\n    /// Returns if the property descriptor is configurable.\n    ///\n    /// # Panics\n    ///\n    /// Panics if the `configurable` field is not set.\n    #[inline]\n    #[must_use]\n    pub fn expect_configurable(&self) -> bool {\n        self.configurable\n            .expect(\"[[configurable]] field not in property descriptor\")\n    }\n\n    /// Returns if the property descriptor is writable.\n    ///\n    /// # Panics\n    ///\n    /// Panics if the `writable` field is not set.\n    #[inline]\n    #[must_use]\n    pub fn expect_writable(&self) -> bool {\n        self.writable()\n            .expect(\"[[writable]] field not in property descriptor\")\n    }\n\n    /// Returns the value of the property descriptor.\n    ///\n    /// # Panics\n    ///\n    /// Panics if the `value` field is not set.\n    #[inline]\n    #[must_use]\n    pub fn expect_value(&self) -> &JsValue {\n        self.value()\n            .expect(\"[[value]] field not in property descriptor\")\n    }\n\n    /// Returns the getter of the property descriptor.\n    ///\n    /// # Panics\n    ///\n    /// Panics if the `getter` field is not set.\n    #[inline]\n    #[must_use]\n    pub fn expect_get(&self) -> &JsValue {\n        self.get()\n            .expect(\"[[get]] field not in property descriptor\")\n    }\n\n    /// Returns the setter of the property descriptor.\n    ///\n    /// # Panics\n    ///\n    /// Panics if the `setter` field is not set.\n    #[inline]\n    #[must_use]\n    pub fn expect_set(&self) -> &JsValue {\n        self.set()\n            .expect(\"[[set]] field not in property descriptor\")\n    }\n\n    /// Returns the kind of the property descriptor.\n    #[inline]\n    #[must_use]\n    pub const fn kind(&self) -> &DescriptorKind {\n        &self.kind\n    }\n\n    /// Creates a new [`PropertyDescriptorBuilder`].\n    #[inline]\n    #[must_use]\n    pub fn builder() -> PropertyDescriptorBuilder {\n        PropertyDescriptorBuilder::new()\n    }\n\n    /// Creates an accessor property descriptor with default values.\n    #[inline]\n    #[must_use]\n    pub fn into_accessor_defaulted(mut self) -> Self {\n        match &mut self.kind {\n            DescriptorKind::Accessor { set, get } => {\n                if set.is_none() {\n                    *set = Some(JsValue::undefined());\n                }\n                if get.is_none() {\n                    *get = Some(JsValue::undefined());\n                }\n            }\n            _ => {\n                self.kind = DescriptorKind::Accessor {\n                    get: Some(JsValue::undefined()),\n                    set: Some(JsValue::undefined()),\n                };\n            }\n        }\n        self.configurable = self.configurable.or(Some(false));\n        self.enumerable = self.enumerable.or(Some(false));\n        self\n    }\n\n    /// Creates a data property descriptor with default values.\n    #[must_use]\n    pub fn into_data_defaulted(mut self) -> Self {\n        match &mut self.kind {\n            DescriptorKind::Data { value, writable } => {\n                if value.is_none() {\n                    *value = Some(JsValue::undefined());\n                }\n                if writable.is_none() {\n                    *writable = Some(false);\n                }\n            }\n            _ => {\n                self.kind = DescriptorKind::Data {\n                    value: Some(JsValue::undefined()),\n                    writable: Some(false),\n                };\n            }\n        }\n        self.configurable = self.configurable.or(Some(false));\n        self.enumerable = self.enumerable.or(Some(false));\n        self\n    }\n\n    /// Creates an generic property descriptor with default values.\n    #[inline]\n    #[must_use]\n    pub fn complete_property_descriptor(self) -> Self {\n        PropertyDescriptorBuilder { inner: self }\n            .complete_with_defaults()\n            .build()\n    }\n\n    /// Fills the fields of the `PropertyDescriptor` that are not set\n    /// with fields from the given `PropertyDescriptor`.\n    ///\n    /// # Panics\n    ///\n    /// Panics if the given `PropertyDescriptor` is not compatible with this one.\n    #[inline]\n    pub fn fill_with(&mut self, mut desc: Self) {\n        match (&mut self.kind, &mut desc.kind) {\n            (\n                DescriptorKind::Data { value, writable },\n                DescriptorKind::Data {\n                    value: desc_value,\n                    writable: desc_writable,\n                },\n            ) => {\n                if desc_value.is_some() {\n                    std::mem::swap(value, desc_value);\n                }\n                if desc_writable.is_some() {\n                    std::mem::swap(writable, desc_writable);\n                }\n            }\n            (\n                DescriptorKind::Accessor { get, set },\n                DescriptorKind::Accessor {\n                    get: desc_get,\n                    set: desc_set,\n                },\n            ) => {\n                if desc_get.is_some() {\n                    std::mem::swap(get, desc_get);\n                }\n                if desc_set.is_some() {\n                    std::mem::swap(set, desc_set);\n                }\n            }\n            (_, DescriptorKind::Generic) => {}\n            _ => panic!(\"Tried to fill a descriptor with an incompatible descriptor\"),\n        }\n\n        if let Some(enumerable) = desc.enumerable {\n            self.enumerable = Some(enumerable);\n        }\n        if let Some(configurable) = desc.configurable {\n            self.configurable = Some(configurable);\n        }\n    }\n\n    pub(crate) fn to_slot_attributes(&self) -> SlotAttributes {\n        let mut attributes = SlotAttributes::empty();\n        attributes.set(SlotAttributes::CONFIGURABLE, self.expect_configurable());\n        attributes.set(SlotAttributes::ENUMERABLE, self.expect_enumerable());\n        if self.is_data_descriptor() {\n            attributes.set(SlotAttributes::WRITABLE, self.expect_writable());\n        } else {\n            attributes.set(SlotAttributes::GET, self.get().is_some());\n            attributes.set(SlotAttributes::SET, self.set().is_some());\n        }\n        attributes\n    }\n}\n\n/// A builder for [`PropertyDescriptor`].\n#[derive(Default, Debug, Clone)]\npub struct PropertyDescriptorBuilder {\n    inner: PropertyDescriptor,\n}\n\nimpl PropertyDescriptorBuilder {\n    /// Creates a new [`PropertyDescriptorBuilder`].\n    #[must_use]\n    pub fn new() -> Self {\n        Self::default()\n    }\n\n    /// Sets the `value` field of the property descriptor.\n    #[must_use]\n    pub fn value<V: Into<JsValue>>(mut self, value: V) -> Self {\n        match self.inner.kind {\n            DescriptorKind::Data {\n                value: ref mut v, ..\n            } => *v = Some(value.into()),\n            // TODO: maybe panic when trying to convert accessor to data?\n            _ => {\n                self.inner.kind = DescriptorKind::Data {\n                    value: Some(value.into()),\n                    writable: None,\n                }\n            }\n        }\n        self\n    }\n\n    /// Sets the `writable` field of the property descriptor.\n    #[must_use]\n    pub fn writable(mut self, writable: bool) -> Self {\n        match self.inner.kind {\n            DescriptorKind::Data {\n                writable: ref mut w,\n                ..\n            } => *w = Some(writable),\n            // TODO: maybe panic when trying to convert accessor to data?\n            _ => {\n                self.inner.kind = DescriptorKind::Data {\n                    value: None,\n                    writable: Some(writable),\n                }\n            }\n        }\n        self\n    }\n\n    /// Sets the `get` field of the property descriptor.\n    #[must_use]\n    pub fn get<V: Into<JsValue>>(mut self, get: V) -> Self {\n        match self.inner.kind {\n            DescriptorKind::Accessor { get: ref mut g, .. } => *g = Some(get.into()),\n            // TODO: maybe panic when trying to convert data to accessor?\n            _ => {\n                self.inner.kind = DescriptorKind::Accessor {\n                    get: Some(get.into()),\n                    set: None,\n                }\n            }\n        }\n        self\n    }\n\n    /// Sets the `set` field of the property descriptor.\n    #[must_use]\n    pub fn set<V: Into<JsValue>>(mut self, set: V) -> Self {\n        match self.inner.kind {\n            DescriptorKind::Accessor { set: ref mut s, .. } => *s = Some(set.into()),\n            // TODO: maybe panic when trying to convert data to accessor?\n            _ => {\n                self.inner.kind = DescriptorKind::Accessor {\n                    set: Some(set.into()),\n                    get: None,\n                }\n            }\n        }\n        self\n    }\n\n    /// Optionally sets the `enumerable` field of the property descriptor.\n    #[must_use]\n    pub const fn maybe_enumerable(mut self, enumerable: Option<bool>) -> Self {\n        if let Some(enumerable) = enumerable {\n            self = self.enumerable(enumerable);\n        }\n        self\n    }\n\n    /// Optionally sets the `configurable` field of the property descriptor.\n    #[must_use]\n    pub const fn maybe_configurable(mut self, configurable: Option<bool>) -> Self {\n        if let Some(configurable) = configurable {\n            self = self.configurable(configurable);\n        }\n        self\n    }\n\n    /// Optionally sets the `value` field of the property descriptor.\n    #[must_use]\n    pub fn maybe_value<V: Into<JsValue>>(mut self, value: Option<V>) -> Self {\n        if let Some(value) = value {\n            self = self.value(value);\n        }\n        self\n    }\n\n    /// Optionally sets the `writable` field of the property descriptor.\n    #[must_use]\n    pub fn maybe_writable(mut self, writable: Option<bool>) -> Self {\n        if let Some(writable) = writable {\n            self = self.writable(writable);\n        }\n        self\n    }\n\n    /// Optionally sets the `get` field of the property descriptor.\n    #[must_use]\n    pub fn maybe_get<V: Into<JsValue>>(mut self, get: Option<V>) -> Self {\n        if let Some(get) = get {\n            self = self.get(get);\n        }\n        self\n    }\n\n    /// Optionally sets the `set` field of the property descriptor.\n    #[must_use]\n    pub fn maybe_set<V: Into<JsValue>>(mut self, set: Option<V>) -> Self {\n        if let Some(set) = set {\n            self = self.set(set);\n        }\n        self\n    }\n\n    /// Sets the `enumerable` field of the property descriptor.\n    #[must_use]\n    pub const fn enumerable(mut self, enumerable: bool) -> Self {\n        self.inner.enumerable = Some(enumerable);\n        self\n    }\n\n    /// Sets the `configurable` field of the property descriptor.\n    #[must_use]\n    pub const fn configurable(mut self, configurable: bool) -> Self {\n        self.inner.configurable = Some(configurable);\n        self\n    }\n\n    /// Fill any missing fields in the property descriptor.\n    #[must_use]\n    pub fn complete_with_defaults(mut self) -> Self {\n        match self.inner.kind {\n            DescriptorKind::Generic => {\n                self.inner.kind = DescriptorKind::Data {\n                    value: Some(JsValue::undefined()),\n                    writable: Some(false),\n                }\n            }\n            DescriptorKind::Data {\n                ref mut value,\n                ref mut writable,\n            } => {\n                if value.is_none() {\n                    *value = Some(JsValue::undefined());\n                }\n                if writable.is_none() {\n                    *writable = Some(false);\n                }\n            }\n            DescriptorKind::Accessor {\n                ref mut set,\n                ref mut get,\n            } => {\n                if set.is_none() {\n                    *set = Some(JsValue::undefined());\n                }\n                if get.is_none() {\n                    *get = Some(JsValue::undefined());\n                }\n            }\n        }\n        if self.inner.configurable.is_none() {\n            self.inner.configurable = Some(false);\n        }\n        if self.inner.enumerable.is_none() {\n            self.inner.enumerable = Some(false);\n        }\n        self\n    }\n\n    /// Returns a reference to the currently built [`PropertyDescriptor`].\n    #[must_use]\n    pub const fn inner(&self) -> &PropertyDescriptor {\n        &self.inner\n    }\n\n    /// Consumes the builder and returns the [`PropertyDescriptor`].\n    #[must_use]\n    #[allow(clippy::missing_const_for_fn)]\n    pub fn build(self) -> PropertyDescriptor {\n        self.inner\n    }\n}\n\nimpl From<PropertyDescriptorBuilder> for PropertyDescriptor {\n    fn from(builder: PropertyDescriptorBuilder) -> Self {\n        builder.build()\n    }\n}\n\n/// This abstracts away the need for `IsPropertyKey` by transforming the `PropertyKey`\n/// values into an enum with both valid types: String and Symbol\n///\n/// More information:\n/// - [ECMAScript reference][spec]\n///\n/// [spec]: https://tc39.es/ecma262/#sec-ispropertykey\n#[derive(Finalize, PartialEq, Debug, Clone, Eq, Hash)]\npub enum PropertyKey {\n    /// A string property key.\n    String(JsString),\n\n    /// A symbol property key.\n    Symbol(JsSymbol),\n\n    /// A numeric property key.\n    Index(NonMaxU32),\n}\n\n/// Utility function for parsing [`PropertyKey`].\nfn parse_u32_index<I, T>(mut input: I) -> Option<NonMaxU32>\nwhere\n    I: Iterator<Item = T> + ExactSizeIterator + FusedIterator,\n    T: Into<u16>,\n{\n    // min: 0             --> 1  char\n    // max: 4_294_967_296 --> 10 chars\n    //\n    // Max char range: [1, 10] inclusive.\n    const MAX_CHAR_COUNT: usize = 10;\n\n    const CHAR_ZERO: u16 = b'0' as u16;\n    const CHAR_NINE: u16 = b'9' as u16;\n\n    // Eliminate any string if it's greater than the max char count.\n    let len = input.len();\n    if len > MAX_CHAR_COUNT {\n        return None;\n    }\n\n    // Helper function, for converting character to digit [0, 9].\n    let to_digit = |c: u16| -> Option<u32> {\n        if matches!(c, CHAR_ZERO..=CHAR_NINE) {\n            Some(u32::from(c - CHAR_ZERO))\n        } else {\n            None\n        }\n    };\n\n    let byte = input.next()?.into();\n    if byte == CHAR_ZERO {\n        if len == 1 {\n            // SAFETY: `0` is not `u32::MAX`.\n            return unsafe { Some(NonMaxU32::new_unchecked(0)) };\n        }\n\n        // String \"012345\" is not a valid index.\n        return None;\n    }\n\n    let mut result = to_digit(byte)?;\n\n    // If the len is equal to max chars, then we need to do checked operations,\n    // in case of overflows. If less use unchecked versions.\n    if len == MAX_CHAR_COUNT {\n        for c in input {\n            result = result.checked_mul(10)?.checked_add(to_digit(c.into())?)?;\n        }\n\n        NonMaxU32::new(result)\n    } else {\n        for c in input {\n            result = result * 10 + to_digit(c.into())?;\n        }\n\n        // SAFETY: `result` cannot be `u32::MAX`,\n        //         because the length of the input is smaller than `MAX_CHAR_COUNT`.\n        unsafe { Some(NonMaxU32::new_unchecked(result)) }\n    }\n}\n\nimpl From<JsStr<'_>> for PropertyKey {\n    #[inline]\n    fn from(string: JsStr<'_>) -> Self {\n        parse_u32_index(string.iter()).map_or_else(|| Self::String(string.into()), Self::Index)\n    }\n}\n\nimpl From<JsString> for PropertyKey {\n    #[inline]\n    fn from(string: JsString) -> Self {\n        parse_u32_index(string.as_str().iter()).map_or(Self::String(string), Self::Index)\n    }\n}\n\nimpl From<JsSymbol> for PropertyKey {\n    #[inline]\n    fn from(symbol: JsSymbol) -> Self {\n        Self::Symbol(symbol)\n    }\n}\n\nimpl fmt::Display for PropertyKey {\n    #[inline]\n    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {\n        match self {\n            Self::String(string) => string.to_std_string_escaped().fmt(f),\n            Self::Symbol(symbol) => symbol.descriptive_string().to_std_string_escaped().fmt(f),\n            Self::Index(index) => index.get().fmt(f),\n        }\n    }\n}\n\nimpl From<&PropertyKey> for JsValue {\n    #[inline]\n    fn from(property_key: &PropertyKey) -> Self {\n        match property_key {\n            PropertyKey::String(string) => string.clone().into(),\n            PropertyKey::Symbol(symbol) => symbol.clone().into(),\n            PropertyKey::Index(index) => {\n                i32::try_from(index.get()).map_or_else(|_| Self::new(index.get()), Self::new)\n            }\n        }\n    }\n}\n\nimpl From<PropertyKey> for JsValue {\n    #[inline]\n    fn from(property_key: PropertyKey) -> Self {\n        match property_key {\n            PropertyKey::String(ref string) => string.clone().into(),\n            PropertyKey::Symbol(ref symbol) => symbol.clone().into(),\n            PropertyKey::Index(index) => js_string!(index.get()).into(),\n        }\n    }\n}\n\nimpl From<u8> for PropertyKey {\n    fn from(value: u8) -> Self {\n        // SAFETY: `u8` can never be `u32::MAX`.\n        unsafe { Self::Index(NonMaxU32::new_unchecked(value.into())) }\n    }\n}\n\nimpl From<u16> for PropertyKey {\n    fn from(value: u16) -> Self {\n        // SAFETY: `u16` can never be `u32::MAX`.\n        unsafe { Self::Index(NonMaxU32::new_unchecked(value.into())) }\n    }\n}\n\nimpl From<u32> for PropertyKey {\n    fn from(value: u32) -> Self {\n        NonMaxU32::new(value).map_or_else(|| Self::String(value.into()), Self::Index)\n    }\n}\n\nimpl From<usize> for PropertyKey {\n    fn from(value: usize) -> Self {\n        u32::try_from(value)\n            .ok()\n            .and_then(NonMaxU32::new)\n            .map_or_else(|| Self::String(value.into()), Self::Index)\n    }\n}\n\nimpl From<i64> for PropertyKey {\n    fn from(value: i64) -> Self {\n        u32::try_from(value)\n            .ok()\n            .and_then(NonMaxU32::new)\n            .map_or_else(|| Self::String(value.into()), Self::Index)\n    }\n}\n\nimpl From<u64> for PropertyKey {\n    fn from(value: u64) -> Self {\n        u32::try_from(value)\n            .ok()\n            .and_then(NonMaxU32::new)\n            .map_or_else(|| Self::String(value.into()), Self::Index)\n    }\n}\n\nimpl From<isize> for PropertyKey {\n    fn from(value: isize) -> Self {\n        u32::try_from(value)\n            .ok()\n            .and_then(NonMaxU32::new)\n            .map_or_else(|| Self::String(value.into()), Self::Index)\n    }\n}\n\nimpl From<i32> for PropertyKey {\n    fn from(value: i32) -> Self {\n        if !value.is_negative() {\n            // Safety: A positive i32 value fits in 31 bits, so it can never be u32::MAX.\n            return Self::Index(unsafe { NonMaxU32::new_unchecked(value as u32) });\n        }\n        Self::String(value.into())\n    }\n}\n\nimpl From<f64> for PropertyKey {\n    fn from(value: f64) -> Self {\n        use num_traits::cast::FromPrimitive;\n\n        u32::from_f64(value)\n            .and_then(NonMaxU32::new)\n            .map_or_else(|| Self::String(value.into()), Self::Index)\n    }\n}\n\nimpl PartialEq<[u16]> for PropertyKey {\n    fn eq(&self, other: &[u16]) -> bool {\n        match self {\n            Self::String(string) => string == other,\n            _ => false,\n        }\n    }\n}\n\n#[derive(Debug, Clone, Copy)]\npub(crate) enum PropertyNameKind {\n    Key,\n    Value,\n    KeyAndValue,\n}\n"
  },
  {
    "path": "core/engine/src/property/nonmaxu32.rs",
    "content": "/// An integer that is not `u32::MAX`.\n#[derive(PartialEq, Debug, Clone, Copy, Eq, Hash)]\npub struct NonMaxU32 {\n    inner: u32,\n}\n\nimpl NonMaxU32 {\n    /// Creates a non-max `u32`.\n    ///\n    /// # Safety\n    ///\n    /// The caller must ensure that the given value is not `u32::MAX`.\n    #[must_use]\n    pub const unsafe fn new_unchecked(inner: u32) -> Self {\n        debug_assert!(inner != u32::MAX);\n\n        Self { inner }\n    }\n\n    /// Creates a non-max `u32` if the given value is not `u32::MAX`.\n    #[must_use]\n    pub const fn new(inner: u32) -> Option<Self> {\n        if inner == u32::MAX {\n            return None;\n        }\n\n        // SAFETY: We checked that `inner` is not `u32::MAX`.\n        unsafe { Some(Self::new_unchecked(inner)) }\n    }\n\n    /// Returns the value as a primitive type.\n    #[must_use]\n    pub const fn get(&self) -> u32 {\n        self.inner\n    }\n}\n"
  },
  {
    "path": "core/engine/src/realm.rs",
    "content": "//! Boa's implementation of ECMAScript's `Realm Records`\n//!\n//! Conceptually, a realm consists of a set of intrinsic objects, an ECMAScript global environment,\n//! all of the ECMAScript code that is loaded within the scope of that global environment,\n//! and other associated state and resources.\n//!\n//! A realm is represented in this implementation as a Realm struct with the fields specified from the spec.\n\nuse std::any::TypeId;\n\nuse crate::{\n    Context, HostDefined, JsNativeError, JsObject, JsResult, JsString,\n    class::Class,\n    context::{\n        HostHooks,\n        intrinsics::{Intrinsics, StandardConstructor},\n    },\n    environments::DeclarativeEnvironment,\n    module::Module,\n    object::shape::RootShape,\n};\nuse boa_ast::scope::Scope;\nuse boa_engine::JsValue;\nuse boa_engine::property::{Attribute, PropertyDescriptor, PropertyKey};\nuse boa_gc::{Finalize, Gc, GcRef, GcRefCell, GcRefMut, Trace};\nuse rustc_hash::FxHashMap;\n\n/// Representation of a Realm.\n///\n/// In the specification these are called Realm Records.\n#[derive(Clone, Trace, Finalize)]\npub struct Realm {\n    inner: Gc<Inner>,\n}\n\nimpl Eq for Realm {}\n\nimpl PartialEq for Realm {\n    fn eq(&self, other: &Self) -> bool {\n        Gc::ptr_eq(&self.inner, &other.inner)\n    }\n}\n\nimpl std::fmt::Debug for Realm {\n    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {\n        f.debug_struct(\"Realm\")\n            .field(\"intrinsics\", &self.inner.intrinsics)\n            .field(\"environment\", &self.inner.environment)\n            .field(\"global_object\", &self.inner.global_object)\n            .field(\"global_this\", &self.inner.global_this)\n            .finish()\n    }\n}\n\n#[derive(Trace, Finalize)]\nstruct Inner {\n    intrinsics: Intrinsics,\n\n    /// The global declarative environment of this realm.\n    environment: Gc<DeclarativeEnvironment>,\n\n    /// The global scope of this realm.\n    /// This is directly related to the global declarative environment.\n    // Safety: Nothing in `Scope` needs tracing.\n    #[unsafe_ignore_trace]\n    scope: Scope,\n\n    global_object: JsObject,\n    global_this: JsObject,\n    template_map: GcRefCell<FxHashMap<u64, JsObject>>,\n    loaded_modules: GcRefCell<FxHashMap<JsString, Module>>,\n    host_classes: GcRefCell<FxHashMap<TypeId, StandardConstructor>>,\n\n    host_defined: GcRefCell<HostDefined>,\n}\n\nimpl Realm {\n    /// Create a new [`Realm`].\n    #[inline]\n    pub fn create(hooks: &dyn HostHooks, root_shape: &RootShape) -> JsResult<Self> {\n        let intrinsics = Intrinsics::uninit(root_shape).ok_or_else(|| {\n            JsNativeError::typ().with_message(\"failed to create the realm intrinsics\")\n        })?;\n\n        let global_object = hooks.create_global_object(&intrinsics);\n        let global_this = hooks\n            .create_global_this(&intrinsics)\n            .unwrap_or_else(|| global_object.clone());\n        let environment = Gc::new(DeclarativeEnvironment::global());\n        let scope = Scope::new_global();\n\n        let realm = Self {\n            inner: Gc::new(Inner {\n                intrinsics,\n                environment,\n                scope,\n                global_object,\n                global_this,\n                template_map: GcRefCell::default(),\n                loaded_modules: GcRefCell::default(),\n                host_classes: GcRefCell::default(),\n                host_defined: GcRefCell::default(),\n            }),\n        };\n\n        realm.initialize();\n\n        Ok(realm)\n    }\n\n    /// Gets the intrinsics of this `Realm`.\n    #[inline]\n    #[must_use]\n    pub fn intrinsics(&self) -> &Intrinsics {\n        &self.inner.intrinsics\n    }\n\n    /// Returns an immutable reference to the [`ECMAScript specification`][spec] defined\n    /// [`\\[\\[\\HostDefined]\\]`][`HostDefined`] field of the [`Realm`].\n    ///\n    /// [spec]: https://tc39.es/ecma262/#table-realm-record-fields\n    ///\n    /// # Panics\n    ///\n    /// Panics if [`HostDefined`] field is mutably borrowed.\n    #[inline]\n    #[must_use]\n    pub fn host_defined(&self) -> GcRef<'_, HostDefined> {\n        self.inner.host_defined.borrow()\n    }\n\n    /// Returns a mutable reference to [`ECMAScript specification`][spec] defined\n    /// [`\\[\\[\\HostDefined]\\]`][`HostDefined`] field of the [`Realm`].\n    ///\n    /// [spec]: https://tc39.es/ecma262/#table-realm-record-fields\n    ///\n    /// # Panics\n    ///\n    /// Panics if [`HostDefined`] field is borrowed.\n    #[inline]\n    #[must_use]\n    pub fn host_defined_mut(&self) -> GcRefMut<'_, HostDefined> {\n        self.inner.host_defined.borrow_mut()\n    }\n\n    /// Checks if this `Realm` has the class `C` registered into its class map.\n    #[must_use]\n    pub fn has_class<C: Class>(&self) -> bool {\n        self.inner\n            .host_classes\n            .borrow()\n            .contains_key(&TypeId::of::<C>())\n    }\n\n    /// Gets the constructor and prototype of the class `C` if it is registered in the class map.\n    #[must_use]\n    pub fn get_class<C: Class>(&self) -> Option<StandardConstructor> {\n        self.inner\n            .host_classes\n            .borrow()\n            .get(&TypeId::of::<C>())\n            .cloned()\n    }\n\n    pub(crate) fn environment(&self) -> &Gc<DeclarativeEnvironment> {\n        &self.inner.environment\n    }\n\n    /// Returns the scope of this realm.\n    #[must_use]\n    pub fn scope(&self) -> &Scope {\n        &self.inner.scope\n    }\n\n    pub(crate) fn global_object(&self) -> &JsObject {\n        &self.inner.global_object\n    }\n\n    pub(crate) fn global_this(&self) -> &JsObject {\n        &self.inner.global_this\n    }\n\n    pub(crate) fn loaded_modules(&self) -> &GcRefCell<FxHashMap<JsString, Module>> {\n        &self.inner.loaded_modules\n    }\n\n    /// Resizes the number of bindings on the global environment.\n    pub(crate) fn resize_global_env(&self) {\n        let binding_number = self.scope().num_bindings();\n        let env = self\n            .environment()\n            .kind()\n            .as_global()\n            .expect(\"Realm should only store global environments\");\n        let mut bindings = env.bindings().borrow_mut();\n\n        if bindings.len() < binding_number as usize {\n            bindings.resize(binding_number as usize, None);\n        }\n    }\n\n    pub(crate) fn push_template(&self, site: u64, template: JsObject) {\n        self.inner.template_map.borrow_mut().insert(site, template);\n    }\n\n    pub(crate) fn lookup_template(&self, site: u64) -> Option<JsObject> {\n        self.inner.template_map.borrow().get(&site).cloned()\n    }\n\n    /// Register a property on the global object of this realm.\n    ///\n    /// It will return an error if the property is already defined.\n    pub fn register_property<K, V>(\n        &self,\n        key: K,\n        value: V,\n        attribute: Attribute,\n        context: &mut Context,\n    ) -> JsResult<()>\n    where\n        K: Into<PropertyKey>,\n        V: Into<JsValue>,\n    {\n        self.global_object().define_property_or_throw(\n            key,\n            PropertyDescriptor::builder()\n                .value(value)\n                .writable(attribute.writable())\n                .enumerable(attribute.enumerable())\n                .configurable(attribute.configurable()),\n            context,\n        )?;\n        Ok(())\n    }\n\n    /// Register a class `C` in this realm.\n    pub fn register_class<C: Class>(&self, spec: StandardConstructor) {\n        self.inner\n            .host_classes\n            .borrow_mut()\n            .insert(TypeId::of::<C>(), spec);\n    }\n\n    /// Unregister a class `C` in this realm.\n    #[must_use]\n    pub fn unregister_class<C: Class>(&self) -> Option<StandardConstructor> {\n        self.inner\n            .host_classes\n            .borrow_mut()\n            .remove(&TypeId::of::<C>())\n    }\n\n    pub(crate) fn addr(&self) -> *const () {\n        let ptr: *const _ = &raw const *self.inner;\n        ptr.cast()\n    }\n}\n"
  },
  {
    "path": "core/engine/src/script.rs",
    "content": "//! Boa's implementation of ECMAScript's Scripts.\n//!\n//! This module contains the [`Script`] type, which represents a [**Script Record**][script].\n//!\n//! More information:\n//!  - [ECMAScript reference][spec]\n//!\n//! [spec]: https://tc39.es/ecma262/#sec-scripts\n//! [script]: https://tc39.es/ecma262/#sec-script-records\n\nuse std::path::{Path, PathBuf};\n\nuse rustc_hash::FxHashMap;\n\nuse boa_gc::{Finalize, Gc, GcRefCell, Trace};\nuse boa_parser::{Parser, Source, source::ReadChar};\n\nuse crate::{\n    Context, HostDefined, JsResult, JsString, JsValue, Module, SpannedSourceText,\n    bytecompiler::{ByteCompiler, global_declaration_instantiation_context},\n    environments::EnvironmentStack,\n    js_string,\n    realm::Realm,\n    spanned_source_text::SourceText,\n    vm::{ActiveRunnable, CallFrame, CallFrameFlags, CodeBlock},\n};\n\n/// ECMAScript's [**Script Record**][spec].\n///\n/// [spec]: https://tc39.es/ecma262/#sec-script-records\n#[derive(Clone, Trace, Finalize)]\npub struct Script {\n    inner: Gc<Inner>,\n}\n\nimpl std::fmt::Debug for Script {\n    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {\n        f.debug_struct(\"Script\")\n            .field(\"realm\", &self.inner.realm.addr())\n            .field(\"phase\", &self.inner.phase.borrow())\n            .field(\"loaded_modules\", &self.inner.loaded_modules)\n            .finish()\n    }\n}\n\n#[derive(Trace, Debug, Finalize)]\nenum ScriptPhase {\n    Ast(#[unsafe_ignore_trace] boa_ast::Script),\n    Codeblock(Gc<CodeBlock>),\n}\n\n#[derive(Trace, Finalize)]\nstruct Inner {\n    realm: Realm,\n    phase: GcRefCell<ScriptPhase>,\n    source_text: SourceText,\n    loaded_modules: GcRefCell<FxHashMap<JsString, Module>>,\n    host_defined: HostDefined,\n    path: Option<PathBuf>,\n}\n\nimpl Script {\n    /// Gets the realm of this script.\n    #[must_use]\n    pub fn realm(&self) -> &Realm {\n        &self.inner.realm\n    }\n\n    /// Returns the [`ECMAScript specification`][spec] defined [`\\[\\[HostDefined\\]\\]`][`HostDefined`] field of the [`Module`].\n    ///\n    /// [spec]: https://tc39.es/ecma262/#script-record\n    #[must_use]\n    pub fn host_defined(&self) -> &HostDefined {\n        &self.inner.host_defined\n    }\n\n    /// Gets the loaded modules of this script.\n    pub(crate) fn loaded_modules(&self) -> &GcRefCell<FxHashMap<JsString, Module>> {\n        &self.inner.loaded_modules\n    }\n\n    /// Abstract operation [`ParseScript ( sourceText, realm, hostDefined )`][spec].\n    ///\n    /// Parses the provided `src` as an ECMAScript script, returning an error if parsing fails.\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-parse-script\n    pub fn parse<R: ReadChar>(\n        src: Source<'_, R>,\n        realm: Option<Realm>,\n        context: &mut Context,\n    ) -> JsResult<Self> {\n        let path = src.path().map(Path::to_path_buf);\n        let mut parser = Parser::new(src);\n        parser.set_identifier(context.next_parser_identifier());\n        if context.is_strict() {\n            parser.set_strict();\n        }\n        let scope = context.realm().scope().clone();\n        let (mut code, source) = parser.parse_script_with_source(&scope, context.interner_mut())?;\n        if !context.optimizer_options().is_empty() {\n            context.optimize_statement_list(code.statements_mut());\n        }\n\n        let source_text = SourceText::new(source);\n\n        Ok(Self {\n            inner: Gc::new(Inner {\n                realm: realm.unwrap_or_else(|| context.realm().clone()),\n                phase: GcRefCell::new(ScriptPhase::Ast(code)),\n                source_text,\n                loaded_modules: GcRefCell::default(),\n                host_defined: HostDefined::default(),\n                path,\n            }),\n        })\n    }\n\n    /// Compiles the codeblock of this script.\n    ///\n    /// This is a no-op if this has been called previously.\n    pub fn codeblock(&self, context: &mut Context) -> JsResult<Gc<CodeBlock>> {\n        let cb = {\n            let phase = self.inner.phase.borrow();\n            let source = match &*phase {\n                ScriptPhase::Codeblock(codeblock) => return Ok(codeblock.clone()),\n                ScriptPhase::Ast(source) => source,\n            };\n\n            let mut annex_b_function_names = Vec::new();\n\n            global_declaration_instantiation_context(\n                &mut annex_b_function_names,\n                source,\n                self.inner.realm.scope(),\n                context,\n            )?;\n\n            let spanned_source_text = SpannedSourceText::new_source_only(self.get_source());\n\n            let mut compiler = ByteCompiler::new(\n                js_string!(\"<main>\"),\n                source.strict(),\n                false,\n                self.inner.realm.scope().clone(),\n                self.inner.realm.scope().clone(),\n                false,\n                false,\n                context.interner_mut(),\n                false,\n                spanned_source_text,\n                self.path().map(Path::to_owned).into(),\n            );\n\n            #[cfg(feature = \"annex-b\")]\n            {\n                compiler.annex_b_function_names = annex_b_function_names;\n            }\n\n            compiler.global_declaration_instantiation(source);\n            compiler.compile_statement_list(source.statements(), true, false);\n\n            Gc::new(compiler.finish())\n        };\n\n        *self.inner.phase.borrow_mut() = ScriptPhase::Codeblock(cb.clone());\n\n        Ok(cb)\n    }\n\n    /// Evaluates this script and returns its result.\n    ///\n    /// Note that this won't run any scheduled promise jobs; you need to call [`Context::run_jobs`]\n    /// on the context or [`JobExecutor::run_jobs`] on the provided queue to run them.\n    ///\n    /// [`JobExecutor::run_jobs`]: crate::job::JobExecutor::run_jobs\n    pub fn evaluate(&self, context: &mut Context) -> JsResult<JsValue> {\n        self.prepare_run(context)?;\n        let record = context.run();\n\n        context.vm.pop_frame();\n\n        record.consume()\n    }\n\n    /// Evaluates this script and returns its result, periodically yielding to the executor\n    /// in order to avoid blocking the current thread.\n    ///\n    /// This uses an implementation defined amount of \"clock cycles\" that need to pass before\n    /// execution is suspended. See [`Script::evaluate_async_with_budget`] if you want to also\n    /// customize this parameter.\n    #[allow(clippy::future_not_send)]\n    pub async fn evaluate_async(&self, context: &mut Context) -> JsResult<JsValue> {\n        self.evaluate_async_with_budget(context, 256).await\n    }\n\n    /// Evaluates this script and returns its result, yielding to the executor each time `budget`\n    /// number of \"clock cycles\" pass.\n    ///\n    /// Note that \"clock cycle\" is in quotation marks because we can't determine exactly how many\n    /// CPU clock cycles a VM instruction will take, but all instructions have a \"cost\" associated\n    /// with them that depends on their individual complexity. We'd recommend benchmarking with\n    /// different budget sizes in order to find the ideal yielding time for your application.\n    #[allow(clippy::future_not_send)]\n    pub async fn evaluate_async_with_budget(\n        &self,\n        context: &mut Context,\n        budget: u32,\n    ) -> JsResult<JsValue> {\n        self.prepare_run(context)?;\n\n        let record = context.run_async_with_budget(budget).await;\n\n        context.vm.pop_frame();\n\n        record.consume()\n    }\n\n    fn prepare_run(&self, context: &mut Context) -> JsResult<()> {\n        let codeblock = self.codeblock(context)?;\n\n        let global_env = EnvironmentStack::new();\n        context.vm.push_frame_with_stack(\n            CallFrame::new(\n                codeblock.clone(),\n                Some(ActiveRunnable::Script(self.clone())),\n                global_env,\n                self.inner.realm.clone(),\n            )\n            .with_env_fp(0)\n            .with_flags(CallFrameFlags::EXIT_EARLY),\n            JsValue::undefined(),\n            JsValue::null(),\n        );\n\n        self.realm().resize_global_env();\n\n        context\n            .global_declaration_instantiation(&codeblock)\n            .inspect_err(|_| {\n                context.vm.pop_frame();\n            })?;\n\n        Ok(())\n    }\n\n    pub(super) fn path(&self) -> Option<&Path> {\n        self.inner.path.as_deref()\n    }\n\n    pub(super) fn get_source(&self) -> SourceText {\n        self.inner.source_text.clone()\n    }\n}\n"
  },
  {
    "path": "core/engine/src/spanned_source_text.rs",
    "content": "use std::rc::Rc;\n\nuse boa_ast::LinearSpan;\nuse boa_gc::{Finalize, Trace};\n\nstruct Inner {\n    source_text: boa_ast::SourceText,\n}\nimpl Inner {\n    fn new(source_text: boa_ast::SourceText) -> Self {\n        Self { source_text }\n    }\n}\n\n#[derive(Default, Trace, Finalize, Clone)]\npub(crate) struct SourceText {\n    #[unsafe_ignore_trace]\n    source_text: Option<Rc<Inner>>,\n}\n\nimpl SourceText {\n    #[must_use]\n    pub(crate) fn new(source_text: boa_ast::SourceText) -> Self {\n        Self {\n            source_text: Some(Rc::new(Inner::new(source_text))),\n        }\n    }\n\n    fn new_empty() -> Self {\n        Self { source_text: None }\n    }\n\n    #[inline]\n    fn inner(&self) -> Option<&boa_ast::SourceText> {\n        self.source_text.as_ref().map(|x| &x.source_text)\n    }\n\n    fn is_empty(&self) -> bool {\n        self.source_text.is_none()\n    }\n}\n\n/// Contains pointer to source code and span of the object.\n#[derive(Default, Clone)]\npub struct SpannedSourceText {\n    source_text: SourceText,\n    span: Option<LinearSpan>,\n}\n\nimpl SpannedSourceText {\n    pub(crate) fn new(source_text: SourceText, span: Option<LinearSpan>) -> Self {\n        Self { source_text, span }\n    }\n\n    pub(crate) fn new_source_only(source_text: SourceText) -> Self {\n        Self {\n            source_text,\n            span: None,\n        }\n    }\n\n    pub(crate) fn new_empty() -> Self {\n        Self {\n            source_text: SourceText::new_empty(),\n            span: None,\n        }\n    }\n\n    /// Creates new [`SpannedSourceText`] with the same [`SourceText`] but without its span.\n    pub(crate) fn clone_only_source(&self) -> Self {\n        Self {\n            source_text: self.source_text.clone(),\n            span: None,\n        }\n    }\n\n    /// Returns the [`SourceText`].\n    #[must_use]\n    pub(crate) fn source_text(&self) -> SourceText {\n        self.source_text.clone()\n    }\n\n    /// Test if the span is empty.\n    #[inline]\n    #[must_use]\n    pub fn is_empty(&self) -> bool {\n        let span_is_empty = if let Some(x) = self.span {\n            x.is_empty()\n        } else {\n            true\n        };\n        span_is_empty || self.source_text.is_empty()\n    }\n\n    /// Gets inner code points.\n    #[must_use]\n    pub fn to_code_points(&self) -> Option<&[u16]> {\n        if let (Some(source_text), Some(span)) = (self.source_text.inner(), self.span) {\n            Some(source_text.get_code_points_from_span(span))\n        } else {\n            None\n        }\n    }\n}\n\nimpl std::fmt::Debug for SpannedSourceText {\n    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {\n        f.debug_struct(\"SourceTextSpanned\").finish()\n    }\n}\nimpl std::fmt::Debug for SourceText {\n    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {\n        f.debug_struct(\"SourceTextInner\").finish()\n    }\n}\n"
  },
  {
    "path": "core/engine/src/string.rs",
    "content": "//! This module contains the [`js_string`][crate::js_string] macro and the\n//! [`js_str`][crate::js_str] macro.\n//!\n//! The [`js_string`][crate::js_string] macro is used when you need to create a new [`JsString`],\n//! and the [`js_str`][crate::js_str] macro is used for const conversions of string literals to [`JsStr`].\n\n#[doc(inline)]\npub use boa_string::*;\n\n/// Utility macro to create a [`JsString`].\n///\n/// # Examples\n///\n/// You can call the macro without arguments to create an empty `JsString`:\n///\n/// ```\n/// use boa_engine::js_string;\n///\n/// let empty_str = js_string!();\n/// assert!(empty_str.is_empty());\n/// ```\n///\n///\n/// You can create a `JsString` from a string literal, which completely skips the runtime\n/// conversion from [`&str`] to <code>[&\\[u16\\]][slice]</code>:\n///\n/// ```\n/// # use boa_engine::js_string;\n/// let hw = js_string!(\"Hello, world!\");\n/// assert_eq!(&hw, \"Hello, world!\");\n/// ```\n///\n/// Any `&[u16]` slice is a valid `JsString`, including unpaired surrogates:\n///\n/// ```\n/// # use boa_engine::js_string;\n/// let array = js_string!(&[0xD8AFu16, 0x00A0, 0xD8FF, 0x00F0]);\n/// ```\n///\n/// You can also pass it any number of `&[u16]` as arguments to create a new `JsString` with\n/// the concatenation of every slice:\n///\n/// ```\n/// # use boa_engine::{js_string, js_str, JsStr};\n/// const NAME: JsStr<'_> = js_str!(\"human! \");\n/// let greeting = js_string!(\"Hello, \");\n/// let msg = js_string!(&greeting, NAME, js_str!(\"Nice to meet you!\"));\n///\n/// assert_eq!(&msg, \"Hello, human! Nice to meet you!\");\n/// ```\n#[macro_export]\n#[allow(clippy::module_name_repetitions)]\nmacro_rules! js_string {\n    () => {\n        $crate::string::JsString::default()\n    };\n    ($s:literal) => {const {\n        const LITERAL: $crate::string::StaticString = $crate::string::StaticString::new($crate::js_str!($s));\n        $crate::string::JsString::from_static(&LITERAL)\n    }};\n    ($s:expr) => {\n        $crate::string::JsString::from($s)\n    };\n    ( $x:expr, $y:expr ) => {\n        $crate::string::JsString::concat($crate::string::JsStr::from($x), $crate::string::JsStr::from($y))\n    };\n    ( $( $s:expr ),+ ) => {\n        $crate::string::JsString::concat_array(&[ $( $crate::string::JsStr::from($s) ),+ ])\n    };\n}\n\n#[allow(clippy::redundant_clone)]\n#[cfg(test)]\nmod tests {\n    use std::hash::{BuildHasher, BuildHasherDefault, Hash};\n\n    use crate::{JsStr, string::StaticJsStrings};\n\n    use super::JsString;\n    use boa_macros::{js_str, utf16};\n    use rustc_hash::FxHasher;\n\n    fn hash_value<T: Hash>(value: &T) -> u64 {\n        BuildHasherDefault::<FxHasher>::default().hash_one(value)\n    }\n\n    #[test]\n    fn empty() {\n        let s = js_string!();\n        assert_eq!(&s, utf16!(\"\"));\n    }\n\n    #[test]\n    fn refcount() {\n        let x = js_string!(\"Hello world\");\n        assert_eq!(x.refcount(), None);\n\n        let x = js_string!(\"你好\");\n        assert_eq!(x.refcount(), None);\n\n        let x = js_string!(\"Hello world\".to_string());\n        assert_eq!(x.refcount(), Some(1));\n\n        {\n            let y = x.clone();\n            assert_eq!(x.refcount(), Some(2));\n            assert_eq!(y.refcount(), Some(2));\n\n            {\n                let z = y.clone();\n                assert_eq!(x.refcount(), Some(3));\n                assert_eq!(y.refcount(), Some(3));\n                assert_eq!(z.refcount(), Some(3));\n            }\n\n            assert_eq!(x.refcount(), Some(2));\n            assert_eq!(y.refcount(), Some(2));\n        }\n\n        assert_eq!(x.refcount(), Some(1));\n    }\n\n    #[test]\n    fn static_refcount() {\n        let x = js_string!();\n        assert_eq!(x.refcount(), None);\n\n        {\n            let y = x.clone();\n            assert_eq!(x.refcount(), None);\n            assert_eq!(y.refcount(), None);\n        };\n\n        assert_eq!(x.refcount(), None);\n    }\n\n    #[test]\n    fn as_str() {\n        const HELLO: &[u16] = utf16!(\"Hello\");\n        let x = js_string!(HELLO);\n\n        assert_eq!(&x, HELLO);\n    }\n\n    #[test]\n    fn hash() {\n        const HELLOWORLD: JsStr<'_> = js_str!(\"Hello World!\");\n        let x = js_string!(HELLOWORLD);\n\n        assert_eq!(x.as_str(), HELLOWORLD);\n\n        assert!(HELLOWORLD.is_latin1());\n        assert!(x.as_str().is_latin1());\n\n        let s_hash = hash_value(&HELLOWORLD);\n        let x_hash = hash_value(&x);\n\n        assert_eq!(s_hash, x_hash);\n    }\n\n    #[test]\n    fn concat() {\n        const Y: &[u16] = utf16!(\", \");\n        const W: &[u16] = utf16!(\"!\");\n\n        let x = js_string!(\"hello\");\n        let z = js_string!(\"world\");\n\n        let xy = js_string!(&x, &JsString::from(Y));\n        assert_eq!(&xy, utf16!(\"hello, \"));\n        assert_eq!(xy.refcount(), Some(1));\n\n        let xyz = js_string!(&xy, &z);\n        assert_eq!(&xyz, utf16!(\"hello, world\"));\n        assert_eq!(xyz.refcount(), Some(1));\n\n        let xyzw = js_string!(&xyz, &JsString::from(W));\n        assert_eq!(&xyzw, utf16!(\"hello, world!\"));\n        assert_eq!(xyzw.refcount(), Some(1));\n    }\n\n    #[test]\n    fn trim_start_non_ascii_to_ascii() {\n        let s = \"\\u{2029}abc\";\n        let x = js_string!(s);\n\n        let y = js_string!(x.trim_start());\n\n        assert_eq!(&y, s.trim_start());\n    }\n\n    #[test]\n    fn conversion_to_known_static_js_string() {\n        const JS_STR_U8: &JsStr<'_> = &js_str!(\"length\");\n        const JS_STR_U16: &JsStr<'_> = &JsStr::utf16(utf16!(\"length\"));\n\n        assert!(JS_STR_U8.is_latin1());\n        assert!(!JS_STR_U16.is_latin1());\n\n        assert_eq!(JS_STR_U8, JS_STR_U8);\n        assert_eq!(JS_STR_U16, JS_STR_U16);\n\n        assert_eq!(JS_STR_U8, JS_STR_U16);\n        assert_eq!(JS_STR_U16, JS_STR_U8);\n\n        assert_eq!(hash_value(JS_STR_U8), hash_value(JS_STR_U16));\n\n        let string = StaticJsStrings::get_string(JS_STR_U8);\n\n        assert!(string.is_some());\n        assert!(string.unwrap().as_str().is_latin1());\n\n        let string = StaticJsStrings::get_string(JS_STR_U16);\n\n        assert!(string.is_some());\n        assert!(string.unwrap().as_str().is_latin1());\n    }\n}\n"
  },
  {
    "path": "core/engine/src/symbol.rs",
    "content": "//! Boa's implementation of ECMAScript's global `Symbol` object.\n//!\n//! The data type symbol is a primitive data type.\n//! The `Symbol()` function returns a value of type symbol, has static properties that expose\n//! several members of built-in objects, has static methods that expose the global symbol registry,\n//! and resembles a built-in object class, but is incomplete as a constructor because it does not\n//! support the syntax \"`new Symbol()`\".\n//!\n//! Every symbol value returned from `Symbol()` is unique.\n//!\n//! More information:\n//! - [MDN documentation][mdn]\n//! - [ECMAScript reference][spec]\n//!\n//! [spec]: https://tc39.es/ecma262/#sec-symbol-value\n//! [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Symbol\n\n#![deny(\n    unsafe_op_in_unsafe_fn,\n    clippy::undocumented_unsafe_blocks,\n    clippy::missing_safety_doc\n)]\n\nuse crate::{\n    js_string,\n    string::{JsString, StaticJsStrings},\n};\nuse boa_gc::{Finalize, Trace};\nuse tag_ptr::{Tagged, UnwrappedTagged};\n\nuse boa_macros::{JsData, js_str};\nuse num_enum::{IntoPrimitive, TryFromPrimitive};\n\nuse std::{\n    hash::{Hash, Hasher},\n    mem::ManuallyDrop,\n    ptr::NonNull,\n    sync::{Arc, atomic::Ordering},\n};\n\nuse portable_atomic::AtomicU64;\n\n/// Reserved number of symbols.\n///\n/// This is the maximum number of well known and internal engine symbols\n/// that can be defined.\nconst RESERVED_SYMBOL_HASHES: u64 = 127;\n\nfn get_id() -> Option<u64> {\n    // Symbol hash.\n    //\n    // For now this is an incremented u64 number.\n    static SYMBOL_HASH_COUNT: AtomicU64 = AtomicU64::new(RESERVED_SYMBOL_HASHES + 1);\n\n    SYMBOL_HASH_COUNT\n        .fetch_update(Ordering::Relaxed, Ordering::Relaxed, |value| {\n            value.checked_add(1)\n        })\n        .ok()\n}\n\n/// List of well known symbols.\n#[derive(Debug, Clone, Copy, TryFromPrimitive, IntoPrimitive)]\n#[repr(u8)]\nenum WellKnown {\n    AsyncIterator,\n    HasInstance,\n    IsConcatSpreadable,\n    Iterator,\n    Match,\n    MatchAll,\n    Replace,\n    Search,\n    Species,\n    Split,\n    ToPrimitive,\n    ToStringTag,\n    Unscopables,\n    Dispose,\n    AsyncDispose,\n}\n\nimpl WellKnown {\n    const fn description(self) -> JsString {\n        match self {\n            Self::AsyncIterator => StaticJsStrings::SYMBOL_ASYNC_ITERATOR,\n            Self::HasInstance => StaticJsStrings::SYMBOL_HAS_INSTANCE,\n            Self::IsConcatSpreadable => StaticJsStrings::SYMBOL_IS_CONCAT_SPREADABLE,\n            Self::Iterator => StaticJsStrings::SYMBOL_ITERATOR,\n            Self::Match => StaticJsStrings::SYMBOL_MATCH,\n            Self::MatchAll => StaticJsStrings::SYMBOL_MATCH_ALL,\n            Self::Replace => StaticJsStrings::SYMBOL_REPLACE,\n            Self::Search => StaticJsStrings::SYMBOL_SEARCH,\n            Self::Species => StaticJsStrings::SYMBOL_SPECIES,\n            Self::Split => StaticJsStrings::SYMBOL_SPLIT,\n            Self::ToPrimitive => StaticJsStrings::SYMBOL_TO_PRIMITIVE,\n            Self::ToStringTag => StaticJsStrings::SYMBOL_TO_STRING_TAG,\n            Self::Unscopables => StaticJsStrings::SYMBOL_UNSCOPABLES,\n            Self::Dispose => StaticJsStrings::SYMBOL_DISPOSE,\n            Self::AsyncDispose => StaticJsStrings::SYMBOL_ASYNC_DISPOSE,\n        }\n    }\n\n    const fn fn_name(self) -> JsString {\n        match self {\n            Self::AsyncIterator => StaticJsStrings::FN_SYMBOL_ASYNC_ITERATOR,\n            Self::HasInstance => StaticJsStrings::FN_SYMBOL_HAS_INSTANCE,\n            Self::IsConcatSpreadable => StaticJsStrings::FN_SYMBOL_IS_CONCAT_SPREADABLE,\n            Self::Iterator => StaticJsStrings::FN_SYMBOL_ITERATOR,\n            Self::Match => StaticJsStrings::FN_SYMBOL_MATCH,\n            Self::MatchAll => StaticJsStrings::FN_SYMBOL_MATCH_ALL,\n            Self::Replace => StaticJsStrings::FN_SYMBOL_REPLACE,\n            Self::Search => StaticJsStrings::FN_SYMBOL_SEARCH,\n            Self::Species => StaticJsStrings::FN_SYMBOL_SPECIES,\n            Self::Split => StaticJsStrings::FN_SYMBOL_SPLIT,\n            Self::ToPrimitive => StaticJsStrings::FN_SYMBOL_TO_PRIMITIVE,\n            Self::ToStringTag => StaticJsStrings::FN_SYMBOL_TO_STRING_TAG,\n            Self::Unscopables => StaticJsStrings::FN_SYMBOL_UNSCOPABLES,\n            Self::Dispose => StaticJsStrings::FN_SYMBOL_DISPOSE,\n            Self::AsyncDispose => StaticJsStrings::FN_SYMBOL_ASYNC_DISPOSE,\n        }\n    }\n\n    const fn hash(self) -> u64 {\n        self as u64\n    }\n\n    fn from_tag(tag: usize) -> Option<Self> {\n        Self::try_from_primitive(u8::try_from(tag).ok()?).ok()\n    }\n}\n\n/// The inner representation of a JavaScript symbol.\n#[derive(Debug, Clone)]\npub(crate) struct RawJsSymbol {\n    hash: u64,\n    // must be a `Box`, since this needs to be shareable between many threads.\n    description: Option<Box<[u16]>>,\n}\n\n/// This represents a JavaScript symbol primitive.\n#[derive(Trace, Finalize, JsData)]\n// Safety: JsSymbol does not contain any objects which needs to be traced,\n// so this is safe.\n#[boa_gc(unsafe_empty_trace)]\n#[allow(clippy::module_name_repetitions)]\npub struct JsSymbol {\n    repr: Tagged<RawJsSymbol>,\n}\n\n// SAFETY: `JsSymbol` uses `Arc` to do the reference counting, making this type thread-safe.\nunsafe impl Send for JsSymbol {}\n// SAFETY: `JsSymbol` uses `Arc` to do the reference counting, making this type thread-safe.\nunsafe impl Sync for JsSymbol {}\n\nmacro_rules! well_known_symbols {\n    ( $( $(#[$attr:meta])* ($name:ident, $variant:path) ),+$(,)? ) => {\n        $(\n            $(#[$attr])* #[must_use] pub const fn $name() -> JsSymbol {\n                JsSymbol {\n                    // the cast shouldn't matter since we only have 127 const symbols\n                    repr: Tagged::from_tag($variant.hash() as usize),\n                }\n            }\n        )+\n    };\n}\n\nimpl JsSymbol {\n    /// Creates a new symbol.\n    ///\n    /// Returns `None` if the maximum number of possible symbols has been reached (`u64::MAX`).\n    #[inline]\n    #[must_use]\n    pub fn new(description: Option<JsString>) -> Option<Self> {\n        let hash = get_id()?;\n        let arc = Arc::new(RawJsSymbol {\n            hash,\n            description: description.map(|s| s.iter().collect::<Vec<_>>().into_boxed_slice()),\n        });\n\n        Some(Self {\n            // SAFETY: Pointers returned by `Arc::into_raw` must be non-null.\n            repr: unsafe { Tagged::from_ptr(Arc::into_raw(arc).cast_mut()) },\n        })\n    }\n\n    /// Returns the `Symbol` description.\n    #[inline]\n    #[must_use]\n    pub fn description(&self) -> Option<JsString> {\n        match self.repr.unwrap() {\n            UnwrappedTagged::Ptr(ptr) => {\n                // SAFETY: `ptr` comes from `Arc`, which ensures the validity of the pointer\n                // as long as we correctly call `Arc::from_raw` on `Drop`.\n                unsafe { ptr.as_ref().description.as_ref().map(|v| js_string!(&**v)) }\n            }\n            UnwrappedTagged::Tag(tag) => {\n                // SAFETY: All tagged reprs always come from `WellKnown` itself, making\n                // this operation always safe.\n                let wk = unsafe { WellKnown::from_tag(tag).unwrap_unchecked() };\n                Some(wk.description())\n            }\n        }\n    }\n\n    /// Returns the `Symbol` as a function name.\n    ///\n    /// Equivalent to `[description]`, but returns the empty string if the symbol doesn't have a\n    /// description.\n    #[inline]\n    #[must_use]\n    pub fn fn_name(&self) -> JsString {\n        if let UnwrappedTagged::Tag(tag) = self.repr.unwrap() {\n            // SAFETY: All tagged reprs always come from `WellKnown` itself, making\n            // this operation always safe.\n            let wk = unsafe { WellKnown::from_tag(tag).unwrap_unchecked() };\n            return wk.fn_name();\n        }\n        self.description()\n            .map(|s| js_string!(js_str!(\"[\"), &s, js_str!(\"]\")))\n            .unwrap_or_default()\n    }\n\n    /// Returns the `Symbol`s hash.\n    ///\n    /// The hash is guaranteed to be unique.\n    #[inline]\n    #[must_use]\n    pub fn hash(&self) -> u64 {\n        match self.repr.unwrap() {\n            UnwrappedTagged::Ptr(ptr) => {\n                // SAFETY: `ptr` comes from `Arc`, which ensures the validity of the pointer\n                // as long as we correctly call `Arc::from_raw` on `Drop`.\n                unsafe { ptr.as_ref().hash }\n            }\n            UnwrappedTagged::Tag(tag) => {\n                // SAFETY: All tagged reprs always come from `WellKnown` itself, making\n                // this operation always safe.\n                unsafe { WellKnown::from_tag(tag).unwrap_unchecked().hash() }\n            }\n        }\n    }\n\n    /// Abstract operation `SymbolDescriptiveString ( sym )`\n    ///\n    /// More info:\n    /// - [ECMAScript reference][spec]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-symboldescriptivestring\n    #[must_use]\n    pub fn descriptive_string(&self) -> JsString {\n        self.description().as_ref().map_or_else(\n            || js_string!(\"Symbol()\"),\n            |desc| js_string!(js_str!(\"Symbol(\"), desc, js_str!(\")\")),\n        )\n    }\n\n    /// Consumes the [`JsSymbol`], returning a pointer to `RawJsSymbol`.\n    ///\n    /// To avoid a memory leak the pointer must be converted back to a `JsSymbol` using\n    /// [`JsSymbol::from_raw`].\n    #[inline]\n    #[must_use]\n    #[allow(unused, reason = \"only used in nan-boxed implementation of JsValue\")]\n    pub(crate) fn into_raw(self) -> NonNull<RawJsSymbol> {\n        ManuallyDrop::new(self).repr.as_inner_ptr()\n    }\n\n    /// Constructs a `JsSymbol` from a pointer to `RawJsSymbol`.\n    ///\n    /// The raw pointer must have been previously returned by a call to\n    /// [`JsSymbol::into_raw`].\n    ///\n    /// # Safety\n    ///\n    /// This function is unsafe because improper use may lead to memory unsafety,\n    /// even if the returned `JsSymbol` is never accessed.\n    #[inline]\n    #[must_use]\n    #[allow(unused, reason = \"only used in nan-boxed implementation of JsValue\")]\n    pub(crate) unsafe fn from_raw(ptr: NonNull<RawJsSymbol>) -> Self {\n        Self {\n            repr: Tagged::from_non_null(ptr),\n        }\n    }\n\n    well_known_symbols! {\n        /// Gets the static `JsSymbol` for `\"Symbol.asyncIterator\"`.\n        (async_iterator, WellKnown::AsyncIterator),\n        /// Gets the static `JsSymbol` for `\"Symbol.hasInstance\"`.\n        (has_instance, WellKnown::HasInstance),\n        /// Gets the static `JsSymbol` for `\"Symbol.isConcatSpreadable\"`.\n        (is_concat_spreadable, WellKnown::IsConcatSpreadable),\n        /// Gets the static `JsSymbol` for `\"Symbol.iterator\"`.\n        (iterator, WellKnown::Iterator),\n        /// Gets the static `JsSymbol` for `\"Symbol.match\"`.\n        (r#match, WellKnown::Match),\n        /// Gets the static `JsSymbol` for `\"Symbol.matchAll\"`.\n        (match_all, WellKnown::MatchAll),\n        /// Gets the static `JsSymbol` for `\"Symbol.replace\"`.\n        (replace, WellKnown::Replace),\n        /// Gets the static `JsSymbol` for `\"Symbol.search\"`.\n        (search, WellKnown::Search),\n        /// Gets the static `JsSymbol` for `\"Symbol.species\"`.\n        (species, WellKnown::Species),\n        /// Gets the static `JsSymbol` for `\"Symbol.split\"`.\n        (split, WellKnown::Split),\n        /// Gets the static `JsSymbol` for `\"Symbol.toPrimitive\"`.\n        (to_primitive, WellKnown::ToPrimitive),\n        /// Gets the static `JsSymbol` for `\"Symbol.toStringTag\"`.\n        (to_string_tag, WellKnown::ToStringTag),\n        /// Gets the static `JsSymbol` for `\"Symbol.unscopables\"`.\n        (unscopables, WellKnown::Unscopables),\n        /// Gets the static `JsSymbol` for `\"Symbol.dispose\"`.\n        (dispose, WellKnown::Dispose),\n        /// Gets the static `JsSymbol` for `\"Symbol.asyncDispose\"`.\n        (async_dispose, WellKnown::AsyncDispose),\n    }\n}\n\nimpl Clone for JsSymbol {\n    fn clone(&self) -> Self {\n        if let UnwrappedTagged::Ptr(ptr) = self.repr.unwrap() {\n            // SAFETY: the pointer returned by `self.repr` must be a valid pointer\n            // that came from an `Arc::into_raw` call.\n            unsafe {\n                let arc = Arc::from_raw(ptr.as_ptr().cast_const());\n                // Don't need the Arc since `self` is already a copyable pointer, just need to\n                // trigger the `clone` impl.\n                std::mem::forget(arc.clone());\n                std::mem::forget(arc);\n            }\n        }\n        Self { repr: self.repr }\n    }\n}\n\nimpl Drop for JsSymbol {\n    fn drop(&mut self) {\n        if let UnwrappedTagged::Ptr(ptr) = self.repr.unwrap() {\n            // SAFETY: the pointer returned by `self.repr` must be a valid pointer\n            // that came from an `Arc::into_raw` call.\n            unsafe { drop(Arc::from_raw(ptr.as_ptr().cast_const())) }\n        }\n    }\n}\n\nimpl std::fmt::Debug for JsSymbol {\n    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {\n        f.debug_struct(\"JsSymbol\")\n            .field(\"hash\", &self.hash())\n            .field(\"description\", &self.description())\n            .finish()\n    }\n}\n\nimpl std::fmt::Display for JsSymbol {\n    #[inline]\n    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {\n        match &self.description() {\n            Some(desc) => write!(f, \"Symbol({})\", desc.to_std_string_escaped()),\n            None => write!(f, \"Symbol()\"),\n        }\n    }\n}\n\nimpl Eq for JsSymbol {}\n\nimpl PartialEq for JsSymbol {\n    #[inline]\n    fn eq(&self, other: &Self) -> bool {\n        self.hash() == other.hash()\n    }\n}\n\nimpl PartialOrd for JsSymbol {\n    #[inline]\n    fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {\n        Some(self.cmp(other))\n    }\n}\n\nimpl Ord for JsSymbol {\n    #[inline]\n    fn cmp(&self, other: &Self) -> std::cmp::Ordering {\n        self.hash().cmp(&other.hash())\n    }\n}\n\nimpl Hash for JsSymbol {\n    fn hash<H: Hasher>(&self, state: &mut H) {\n        self.hash().hash(state);\n    }\n}\n\n#[cfg(test)]\nmod tests {\n    use boa_macros::js_str;\n\n    use crate::{\n        Context, JsObject, JsValue, TestAction, builtins::Json, run_test_actions, value::TryIntoJs,\n    };\n\n    use super::JsSymbol;\n    use std::collections::hash_set::HashSet;\n\n    #[test]\n    fn unique() {\n        let max_loop_iterations = 100;\n        let mut set: HashSet<JsSymbol> = HashSet::new();\n        for _ in 0..max_loop_iterations {\n            let symbol = JsSymbol::new(None);\n            if let Some(symbol) = symbol {\n                assert!(set.insert(symbol), \"JsSymbol already exists in the set\");\n            } else {\n                panic!(\"JsSymbol::new() failed when creating up to {max_loop_iterations} symbols\");\n            }\n        }\n    }\n\n    #[test]\n    fn hidden_in_enumeration() {\n        let mut context = Context::default();\n        let symbol1 = JsSymbol::new(None).unwrap();\n        let symbol2 = JsSymbol::new(None).unwrap();\n        let test_obj = JsObject::from_proto_and_data(None, ());\n        test_obj\n            .set(symbol1, js_str!(\"Can't see me\"), false, &mut context)\n            .unwrap();\n        test_obj\n            .set(js_str!(\"visible\"), true, false, &mut context)\n            .unwrap();\n        test_obj\n            .set(symbol2, js_str!(\"Still can't see me\"), false, &mut context)\n            .unwrap();\n        let values = test_obj\n            .enumerable_own_property_names(crate::property::PropertyNameKind::Value, &mut context)\n            .expect(\"Test data should be enumerable\");\n        assert!(\n            values.len() == 1,\n            \"Test data should have exactly one enumerable value, instead found {}\",\n            values.len()\n        );\n    }\n\n    #[test]\n    fn hidden_in_stringify() {\n        let mut context = Context::default();\n        let symbol = JsSymbol::new(None).unwrap();\n        let test_obj = JsObject::with_object_proto(context.intrinsics());\n        test_obj\n            .set(symbol, js_str!(\"This won't show up\"), false, &mut context)\n            .unwrap();\n        let json = test_obj\n            .try_into_js(&mut context)\n            .expect(\"try_into_js() failed\");\n        let json_str = Json::stringify(&JsValue::from(0), &[json], &mut context)\n            .expect(\"Json::stringify() failed\")\n            .as_string()\n            .expect(\"Json::stringify() did not return string\");\n        assert_eq!(js_str!(\"{}\"), json_str);\n    }\n    #[test]\n    fn type_conversions() {\n        run_test_actions([\n            TestAction::assert_eq(\n                r#\"\n            let symbol = Symbol(\"symbol\");\n            typeof symbol\n        \"#,\n                js_str!(\"symbol\"),\n            ),\n            TestAction::assert(\n                r#\"\n            symbol == Object(symbol)\n        \"#,\n            ),\n        ]);\n    }\n}\n"
  },
  {
    "path": "core/engine/src/sys/fallback/mod.rs",
    "content": "// Reexports `std::time` for all other platforms. This could cause panics on\n// platforms that don't support `Instant::now()`.\npub(crate) use std::time;\n"
  },
  {
    "path": "core/engine/src/sys/js/mod.rs",
    "content": "pub(crate) use getrandom as _;\npub(crate) use web_time as time;\n"
  },
  {
    "path": "core/engine/src/sys/mod.rs",
    "content": "// We could use `web-time` directly, but that would make it harder to add support\n// for other platforms in the future e.g. `no_std` targets.\n// We could also pull `web-time` and customize it for our target selection\ncfg_if::cfg_if! {\n    if #[cfg(all(\n        target_family = \"wasm\",\n        not(any(target_os = \"emscripten\", target_os = \"wasi\")),\n        feature = \"js\"\n    ))] {\n        mod js;\n        pub(crate) use self::js::*;\n    } else {\n        mod fallback;\n        pub(crate) use self::fallback::*;\n    }\n}\n"
  },
  {
    "path": "core/engine/src/tests/async_generator.rs",
    "content": "use crate::{\n    Context, JsValue, TestAction, builtins::promise::PromiseState, object::JsPromise,\n    run_test_actions,\n};\nuse boa_macros::js_str;\nuse indoc::indoc;\n\n#[track_caller]\nfn assert_promise_iter_value(\n    promise: &JsValue,\n    target: &JsValue,\n    done: bool,\n    context: &mut Context,\n) {\n    let promise = JsPromise::from_object(promise.as_object().unwrap().clone()).unwrap();\n    let PromiseState::Fulfilled(v) = promise.state() else {\n        panic!(\"promise was not fulfilled\");\n    };\n    let o = v.as_object().unwrap();\n    let value = o.get(js_str!(\"value\"), context).unwrap();\n    let d = o\n        .get(js_str!(\"done\"), context)\n        .unwrap()\n        .as_boolean()\n        .unwrap();\n    assert_eq!(&value, target);\n    assert_eq!(d, done);\n}\n\n#[test]\nfn return_on_then_infinite_loop() {\n    // Checks that calling `return` inside `then` only enters an infinite loop without\n    // crashing the engine.\n    run_test_actions([\n        TestAction::run(indoc! {r#\"\n            async function* f() {}\n            const g = f();\n            let count = 0;\n            Object.defineProperty(Object.prototype, \"then\", {\n                get: function() {\n                    if (count < 100) {\n                        count++;\n                        g.return();\n                    }\n                    return;\n                },\n            });\n            g.return();\n        \"#}),\n        TestAction::inspect_context(|ctx| ctx.run_jobs().unwrap()),\n        TestAction::assert_eq(\"count\", 100),\n    ]);\n}\n\n#[test]\nfn return_on_then_single() {\n    // Checks that calling `return` inside `then` once runs without panicking.\n    run_test_actions([\n        TestAction::run(indoc! {r#\"\n            async function* f() {}\n            const g = f();\n            let first = true;\n            Object.defineProperty(Object.prototype, \"then\", {\n                get: function() {\n                    if (first) {\n                        first = false;\n                        g.return();\n                    }\n                    return;\n                },\n            });\n            let ret = g.return()\n        \"#}),\n        TestAction::inspect_context(|ctx| ctx.run_jobs().unwrap()),\n        TestAction::assert_eq(\"first\", false),\n        TestAction::assert_with_op(\"ret\", |ret, context| {\n            assert_promise_iter_value(&ret, &JsValue::undefined(), true, context);\n            true\n        }),\n    ]);\n}\n\n#[test]\nfn return_on_then_queue() {\n    // Checks that calling `return` inside `then` doesn't mess with the request queue.\n    run_test_actions([\n        TestAction::run(indoc! {r#\"\n            async function* f() {\n                yield 1;\n                yield 2;\n            }\n            const g = f();\n            let count = 0;\n            Object.defineProperty(Object.prototype, \"then\", {\n                get: function() {\n                    if (count < 2) {\n                        count++;\n                        g.return();\n                    }\n                    return;\n                },\n            });\n            let first = g.next();\n            let second = g.next();\n            let ret = g.return();\n        \"#}),\n        TestAction::inspect_context(|ctx| ctx.run_jobs().unwrap()),\n        TestAction::assert_with_op(\"first\", |first, context| {\n            assert_promise_iter_value(&first, &JsValue::from(1), false, context);\n            true\n        }),\n        TestAction::assert_with_op(\"second\", |second, context| {\n            assert_promise_iter_value(&second, &JsValue::from(2), false, context);\n            true\n        }),\n        TestAction::assert_with_op(\"ret\", |ret, context| {\n            assert_promise_iter_value(&ret, &JsValue::undefined(), true, context);\n            true\n        }),\n        TestAction::assert_eq(\"count\", JsValue::from(2)),\n    ]);\n}\n"
  },
  {
    "path": "core/engine/src/tests/class.rs",
    "content": "use crate::{TestAction, run_test_actions};\nuse boa_macros::js_str;\nuse indoc::indoc;\n\n#[test]\nfn class_field_initializer_name_static() {\n    run_test_actions([\n        TestAction::run(indoc! {r#\"\n            class C {\n                static a = function() {};\n                static [\"b\"] = function() {};\n                static #c = function() {};\n                static c = this.#c\n            }\n        \"#}),\n        TestAction::assert_eq(\"C.a.name\", js_str!(\"a\")),\n        TestAction::assert_eq(\"C.b.name\", js_str!(\"b\")),\n        TestAction::assert_eq(\"C.c.name\", js_str!(\"#c\")),\n    ]);\n}\n\n#[test]\nfn class_field_initializer_name() {\n    run_test_actions([\n        TestAction::run(indoc! {r#\"\n            class C {\n                a = function() {};\n                [\"b\"] = function() {};\n                #c = function() {};\n                c = this.#c\n            }\n            let c = new C();\n        \"#}),\n        TestAction::assert_eq(\"c.a.name\", js_str!(\"a\")),\n        TestAction::assert_eq(\"c.b.name\", js_str!(\"b\")),\n        TestAction::assert_eq(\"c.c.name\", js_str!(\"#c\")),\n    ]);\n}\n\n#[test]\nfn class_superclass_from_regex_error() {\n    run_test_actions([TestAction::assert_native_error(\n        \"class A extends /=/ {}\",\n        crate::JsNativeErrorKind::Type,\n        \"superclass must be a constructor\",\n    )]);\n}\n\n// https://github.com/boa-dev/boa/issues/3055\n#[test]\nfn class_can_access_super_from_static_initializer() {\n    run_test_actions([\n        TestAction::run(indoc! {r#\"\n            class a {\n                static field = \"super field\";\n            }\n\n            class b extends a {\n                static #field = super.field;\n                static get field() {\n                    return this.#field;\n                }\n            }\n\n            class c extends a {\n                static field = super.field;\n            }\n\n        \"#}),\n        TestAction::assert_eq(\"a.field\", js_str!(\"super field\")),\n        TestAction::assert_eq(\"b.field\", js_str!(\"super field\")),\n        TestAction::assert_eq(\"c.field\", js_str!(\"super field\")),\n    ]);\n}\n\n// https://github.com/boa-dev/boa/issues/4400\n#[test]\nfn class_in_constructor() {\n    run_test_actions([\n        TestAction::run(indoc! {r#\"\n            class C {\n                constructor() {\n                    class D {}\n                    this.v = D.name.toString()\n                }\n            }\n            let c = new C()\n\n        \"#}),\n        TestAction::assert_eq(\"c.v\", js_str!(\"D\")),\n    ]);\n}\n\n// https://github.com/boa-dev/boa/issues/4555\n#[test]\nfn nested_class_in_class_expression_constructor() {\n    run_test_actions([TestAction::run(\n        \"new (class { constructor() { class D {} } })();\",\n    )]);\n}\n\n// https://github.com/boa-dev/boa/issues/4555\n#[test]\nfn nested_class_in_static_block() {\n    run_test_actions([TestAction::run(\"(class { static { class D {} } });\")]);\n}\n\n#[test]\nfn property_initializer_reference_escaped_variable() {\n    run_test_actions([\n        TestAction::run(indoc! {r#\"\n            function run() {\n                const x = \"D\";\n                class C {\n                    a = x;\n                    static b = x;\n                    #c = x;\n                    static #d = x;\n\n                    getC() { return this.#c }\n                    static getD() { return C.#d }\n                }\n                return C\n            }\n            var Z = run();\n            var z = new Z();\n        \"#}),\n        TestAction::assert_eq(\"z.a\", js_str!(\"D\")),\n        TestAction::assert_eq(\"Z.b\", js_str!(\"D\")),\n        TestAction::assert_eq(\"z.getC()\", js_str!(\"D\")),\n        TestAction::assert_eq(\"Z.getD()\", js_str!(\"D\")),\n    ]);\n}\n\n#[test]\nfn private_field_initializer_reference_non_escaped_variable() {\n    run_test_actions([\n        TestAction::run(indoc! {r#\"\n            function outer() {\n                let x = 1;\n                class C {\n                    #p = x;\n                    m() { return this.#p; }\n                }\n                return new C().m();\n            }\n        \"#}),\n        TestAction::assert_eq(\"outer()\", 1),\n    ]);\n}\n\n// https://github.com/boa-dev/boa/issues/4605\n#[test]\nfn class_boolean_literal_method_names() {\n    run_test_actions([\n        TestAction::run(indoc! {r#\"\n            class A {\n                true() { return 1; }\n                false() { return 2; }\n                null() { return 3; }\n            }\n            var a = new A();\n        \"#}),\n        TestAction::assert_eq(\"a.true()\", 1),\n        TestAction::assert_eq(\"a.false()\", 2),\n        TestAction::assert_eq(\"a.null()\", 3),\n    ]);\n}\n\n// https://github.com/boa-dev/boa/issues/4605\n#[test]\nfn class_boolean_literal_static_method_names() {\n    run_test_actions([\n        TestAction::run(indoc! {r#\"\n            class B {\n                static true() { return 10; }\n                static false() { return 20; }\n            }\n        \"#}),\n        TestAction::assert_eq(\"B.true()\", 10),\n        TestAction::assert_eq(\"B.false()\", 20),\n    ]);\n}\n\n// https://github.com/boa-dev/boa/issues/4605\n#[test]\nfn class_boolean_literal_getter_setter_names() {\n    run_test_actions([\n        TestAction::run(indoc! {r#\"\n            class C {\n                get true() { return this._true; }\n                set true(v) { this._true = v; }\n                get false() { return this._false; }\n                set false(v) { this._false = v; }\n            }\n            var c = new C();\n            c.true = 42;\n            c.false = 84;\n        \"#}),\n        TestAction::assert_eq(\"c.true\", 42),\n        TestAction::assert_eq(\"c.false\", 84),\n    ]);\n}\n"
  },
  {
    "path": "core/engine/src/tests/control_flow/loops.rs",
    "content": "use crate::{JsNativeErrorKind, TestAction, run_test_actions};\nuse boa_macros::js_str;\nuse indoc::indoc;\n\n#[test]\nfn do_while_loop() {\n    run_test_actions([\n        TestAction::assert_eq(\n            indoc! {r#\"\n                a = 0;\n                do {\n                    a += 1;\n                } while (a < 10);\n                a\n            \"#},\n            10,\n        ),\n        TestAction::assert_eq(\n            indoc! {r#\"\n                pow = 0;\n                b = 1;\n                do {\n                    pow += 1;\n                    b *= 2;\n                } while (pow < 8);\n                b\n            \"#},\n            256,\n        ),\n    ]);\n}\n\n#[test]\nfn do_while_loop_at_least_once() {\n    run_test_actions([TestAction::assert_eq(\n        indoc! {r#\"\n            a = 0;\n            do\n            {\n                a += 1;\n            }\n            while (false);\n            a\n        \"#},\n        1,\n    )]);\n}\n\n#[test]\nfn do_while_post_inc() {\n    run_test_actions([TestAction::assert_eq(\n        indoc! {r#\"\n            var i = 0;\n            do {} while(i++ < 10) i;\n        \"#},\n        11,\n    )]);\n}\n\n#[test]\nfn do_while_in_block() {\n    run_test_actions([TestAction::assert_eq(\n        indoc! {r#\"\n            {\n                var i = 0;\n                do {\n                    i += 1;\n                }\n                while(false);\n                i;\n            }\n        \"#},\n        1,\n    )]);\n}\n\n#[test]\nfn for_loop() {\n    run_test_actions([\n        TestAction::assert_eq(\n            indoc! {r#\"\n                {\n                    const a = ['h', 'e', 'l', 'l', 'o'];\n                    let b = '';\n                    for (let i = 0; i < a.length; i++) {\n                        b = b + a[i];\n                    }\n                    b\n                }\n            \"#},\n            js_str!(\"hello\"),\n        ),\n        TestAction::assert_eq(\n            indoc! {r#\"\n                {\n                    let a = 0;\n                    let i = 0;\n                    for (;i < 10;) {\n                        a = a + i;\n                        i++;\n                    }\n                    a\n                }\n            \"#},\n            45,\n        ),\n        TestAction::assert_eq(\n            indoc! {r#\"\n                {\n                    let a = 0\n                    for (;false;) {\n                        a++;\n                    }\n                    a\n                }\n            \"#},\n            0,\n        ),\n    ]);\n}\n\n#[test]\nfn for_loop_iteration_variable_does_not_leak() {\n    run_test_actions([TestAction::assert_native_error(\n        indoc! {r#\"\n            for (let i = 0;false;) {}\n            i\n        \"#},\n        JsNativeErrorKind::Reference,\n        \"i is not defined\",\n    )]);\n}\n\n#[test]\nfn test_invalid_break_target() {\n    run_test_actions([TestAction::assert_native_error(\n        indoc! {r#\"\n            while (false) {\n                break nonexistent;\n            }\n        \"#},\n        JsNativeErrorKind::Syntax,\n        \"undefined break target: nonexistent at line 1, col 1\",\n    )]);\n}\n\n#[test]\nfn try_break_finally_edge_cases() {\n    let scenario = r#\"\n        var a;\n        var b;\n        {\n            while (true) {\n                try {\n                    try {\n                        break;\n                    } catch(a) {\n                    } finally {\n                    }\n                } finally {\n                }\n            }\n        }\n\n        {\n            while (true) {\n                try {\n                    try {\n                        throw \"b\";\n                    } catch (b) {\n                        break;\n                    } finally {\n                        a = \"foo\"\n                    }\n                } finally {\n                }\n            }\n        }\n\n        {\n            while (true) {\n                try {\n                    try {\n                    } catch (c) {\n                    } finally {\n                        b = \"bar\"\n                        break;\n                    }\n                } finally {\n                }\n            }\n        }\n        a + b\n    \"#;\n\n    run_test_actions([TestAction::assert_eq(scenario, js_str!(\"foobar\"))]);\n}\n\n#[test]\nfn try_break_labels() {\n    let scenario = r#\"\n        {\n            var str = '';\n\n            outer: {\n                foo: {\n                    bar: {\n                        while (true) {\n                            try {\n                                try {\n                                    break;\n                                } catch(f) {\n                                } finally {\n                                    str = \"fin\";\n                                    break foo;\n                                    str += \"This won't execute\";\n                                }\n                            } finally {\n                                str = str + \"ally!\"\n                                break bar;\n                            }\n                        }\n                        str += \" oh no\";\n                    }\n                    str += \" :)\";\n                }\n            }\n            str\n        }\n    \"#;\n\n    run_test_actions([TestAction::assert_eq(scenario, js_str!(\"finally! :)\"))]);\n}\n\n#[test]\nfn break_nested_labels_loops_and_try() {\n    let scenario = r#\"\n        function nestedLabels(x) {\n            let str = \"\";\n            foo: {\n                spacer: {\n                    bar: {\n                        while(true) {\n                            try {\n                                try {\n                                    break spacer;\n                                } finally {\n                                    str = \"foo\";\n                                }\n                            } catch(h) {} finally {\n                                str += \"bar\"\n                                if (x === true) {\n                                    break foo;\n                                } else {\n                                    break bar;\n                                }\n                            }\n                        }\n                        str += \" broke-while\"\n                    }\n                    str += \" broke-bar\"\n                }\n                str += \" broke-spacer\"\n            }\n            str += \" broke-foo\";\n            return str\n        }\n    \"#;\n\n    run_test_actions([\n        TestAction::run(scenario),\n        TestAction::assert_eq(\"nestedLabels(true)\", js_str!(\"foobar broke-foo\")),\n        TestAction::assert_eq(\n            \"nestedLabels(false)\",\n            js_str!(\"foobar broke-bar broke-spacer broke-foo\"),\n        ),\n    ]);\n}\n\n#[test]\nfn break_environment_gauntlet() {\n    // test that break handles popping environments correctly.\n    let scenario = r#\"\n        let a = 0;\n\n        while(true) {\n            break;\n        }\n\n        while(true) break\n\n        do {break} while(true);\n\n        while (a < 3) {\n            let a = 1;\n            if (a == 1) {\n                break;\n            }\n\n            let b = 2;\n        }\n\n        {\n            b = 0;\n            do {\n                b = 2\n                if (b == 2) {\n                    break;\n                }\n                b++\n            } while( i < 3);\n\n            let c = 1;\n        }\n\n        {\n            for (let r = 0; r< 3; r++) {\n                if (r == 2) {\n                    break;\n                }\n            }\n        }\n\n        basic: for (let a = 0; a < 2; a++) {\n            break;\n        }\n\n        {\n            let result = true;\n            {\n                let x = 2;\n                L: {\n                    let x = 3;\n                    result &&= (x === 3);\n                    break L;\n                    result &&= (false);\n                }\n                result &&= (x === 2);\n            }\n            result;\n        }\n\n        {\n            var str = \"\";\n\n            far_outer: {\n                outer: for (let i = 0; i < 5; i++) {\n                    inner: for (let b = 5; b < 10; b++) {\n                        if (b === 7) {\n                            break far_outer;\n                        }\n                        str = str + b;\n                    }\n                    str = str + i;\n                }\n            }\n            str\n        }\n\n        {\n            for (let r = 0; r < 2; r++) {\n                str = str + r\n            }\n        }\n\n        {\n            let result = \"\";\n            lab_block: {\n                try {\n                    result = \"try_block\";\n                    break lab_block;\n                    result = \"did not break\"\n                } catch (err) {}\n            }\n            str = str + result\n            str\n        }\n    \"#;\n\n    run_test_actions([TestAction::assert_eq(scenario, js_str!(\"5601try_block\"))]);\n}\n\n#[test]\nfn while_loop_late_break() {\n    // Ordering with statement before the break.\n    run_test_actions([TestAction::assert_eq(\n        indoc! {r#\"\n            let a = 1;\n            while (a < 5) {\n                a++;\n                if (a == 3) {\n                    break;\n                }\n            }\n            a;\n        \"#},\n        3,\n    )]);\n}\n\n#[test]\nfn while_loop_early_break() {\n    // Ordering with statements after the break.\n    run_test_actions([TestAction::assert_eq(\n        indoc! {r#\"\n            let a = 1;\n            while (a < 5) {\n                if (a == 3) {\n                    break;\n                }\n                a++;\n            }\n            a;\n        \"#},\n        3,\n    )]);\n}\n\n#[test]\nfn for_loop_break() {\n    run_test_actions([TestAction::assert_eq(\n        indoc! {r#\"\n            let a = 1;\n            for (; a < 5; a++) {\n                if (a == 3) {\n                    break;\n                }\n            }\n            a;\n        \"#},\n        3,\n    )]);\n}\n\n#[test]\nfn for_loop_return() {\n    run_test_actions([TestAction::assert_eq(\n        indoc! {r#\"\n            function foo() {\n                for (let a = 1; a < 5; a++) {\n                    if (a == 3) {\n                        return a;\n                    }\n                }\n            }\n            foo();\n        \"#},\n        3,\n    )]);\n}\n\n#[test]\nfn do_loop_late_break() {\n    // Ordering with statement before the break.\n    run_test_actions([TestAction::assert_eq(\n        indoc! {r#\"\n            let a = 1;\n            do {\n                a++;\n                if (a == 3) {\n                    break;\n                }\n            } while (a < 5);\n            a;\n        \"#},\n        3,\n    )]);\n}\n\n#[test]\nfn do_loop_early_break() {\n    // Ordering with statements after the break.\n    run_test_actions([TestAction::assert_eq(\n        indoc! {r#\"\n            let a = 1;\n            do {\n                if (a == 3) {\n                    break;\n                }\n                a++;\n            } while (a < 5);\n            a;\n        \"#},\n        3,\n    )]);\n}\n\n#[test]\nfn break_out_of_inner_loop() {\n    run_test_actions([\n        TestAction::run(indoc! {r#\"\n                var a = 0, b = 0;\n                for (let i = 0; i < 2; i++) {\n                    a++;\n                    for (let j = 0; j < 10; j++) {\n                        b++;\n                        if (j == 3)\n                            break;\n                    }\n                    a++;\n                }\n            \"#}),\n        TestAction::assert_eq(\"a\", 4),\n        TestAction::assert_eq(\"b\", 8),\n    ]);\n}\n\n#[test]\nfn continue_inner_loop() {\n    run_test_actions([\n        TestAction::run(indoc! {r#\"\n                var a = 0, b = 0;\n                for (let i = 0; i < 2; i++) {\n                    a++;\n                    for (let j = 0; j < 10; j++) {\n                        if (j < 3)\n                            continue;\n                        b++;\n                    }\n                    a++;\n                }\n            \"#}),\n        TestAction::assert_eq(\"a\", 4),\n        TestAction::assert_eq(\"b\", 14),\n    ]);\n}\n\n#[test]\nfn for_loop_continue_out_of_switch() {\n    run_test_actions([\n        TestAction::run(indoc! {r#\"\n                var a = 0, b = 0, c = 0;\n                for (let i = 0; i < 3; i++) {\n                    a++;\n                    switch (i) {\n                        case 0:\n                            continue;\n                            c++;\n                        case 1:\n                            continue;\n                        case 5:\n                            c++;\n                    }\n                    b++;\n                }\n            \"#}),\n        TestAction::assert_eq(\"a\", 3),\n        TestAction::assert_eq(\"b\", 1),\n        TestAction::assert_eq(\"c\", 0),\n    ]);\n}\n\n#[test]\nfn while_loop_continue() {\n    run_test_actions([\n        TestAction::run(indoc! {r#\"\n                var i = 0, a = 0, b = 0;\n                while (i < 3) {\n                    i++;\n                    if (i < 2) {\n                        a++;\n                        continue;\n                    }\n                    b++;\n                }\n            \"#}),\n        TestAction::assert_eq(\"a\", 1),\n        TestAction::assert_eq(\"b\", 2),\n    ]);\n}\n\n#[test]\nfn do_while_loop_continue() {\n    run_test_actions([\n        TestAction::run(indoc! {r#\"\n                var i = 0, a = 0, b = 0;\n                do {\n                    i++;\n                    if (i < 2) {\n                        a++;\n                        continue;\n                    }\n                    b++;\n                } while (i < 3)\n            \"#}),\n        TestAction::assert_eq(\"a\", 1),\n        TestAction::assert_eq(\"b\", 2),\n    ]);\n}\n\n#[test]\nfn for_of_loop_declaration() {\n    run_test_actions([\n        TestAction::run(indoc! {r#\"\n                var result = 0;\n                for (i of [1, 2, 3]) {\n                    result = i;\n                }\n            \"#}),\n        TestAction::assert_eq(\"result\", 3),\n        TestAction::assert_eq(\"i\", 3),\n    ]);\n}\n\n#[test]\nfn for_of_loop_var() {\n    run_test_actions([\n        TestAction::run(indoc! {r#\"\n                var result = 0;\n                for (var i of [1, 2, 3]) {\n                    result = i;\n                }\n            \"#}),\n        TestAction::assert_eq(\"result\", 3),\n        TestAction::assert_eq(\"i\", 3),\n    ]);\n}\n\n#[test]\nfn for_of_loop_let() {\n    run_test_actions([\n        TestAction::run(indoc! {r#\"\n                var result = 0;\n                for (let i of [1, 2, 3]) {\n                    result = i;\n                }\n            \"#}),\n        TestAction::assert_eq(\"result\", 3),\n        TestAction::assert_native_error(\"i\", JsNativeErrorKind::Reference, \"i is not defined\"),\n    ]);\n}\n\n#[test]\nfn for_of_loop_const() {\n    run_test_actions([\n        TestAction::run(indoc! {r#\"\n                var result = 0;\n                for (let i of [1, 2, 3]) {\n                    result = i;\n                }\n            \"#}),\n        TestAction::assert_eq(\"result\", 3),\n        TestAction::assert_native_error(\"i\", JsNativeErrorKind::Reference, \"i is not defined\"),\n    ]);\n}\n\n#[test]\nfn for_of_loop_break() {\n    run_test_actions([\n        TestAction::run(indoc! {r#\"\n                var result = 0;\n                for (var i of [1, 2, 3]) {\n                    if (i > 1)\n                        break;\n                    result = i\n                }\n            \"#}),\n        TestAction::assert_eq(\"result\", 1),\n        TestAction::assert_eq(\"i\", 2),\n    ]);\n}\n\n#[test]\nfn for_of_loop_continue() {\n    run_test_actions([\n        TestAction::run(indoc! {r#\"\n                var result = 0;\n                for (var i of [1, 2, 3]) {\n                    if (i == 3)\n                        continue;\n                    result = i\n                }\n            \"#}),\n        TestAction::assert_eq(\"result\", 2),\n        TestAction::assert_eq(\"i\", 3),\n    ]);\n}\n\n#[test]\nfn for_of_loop_return() {\n    run_test_actions([\n        TestAction::run(indoc! {r#\"\n                function foo() {\n                    for (i of [1, 2, 3]) {\n                        if (i > 1)\n                            return i;\n                    }\n                }\n            \"#}),\n        TestAction::assert_eq(\"foo()\", 2),\n    ]);\n}\n\n#[test]\nfn for_loop_break_label() {\n    run_test_actions([TestAction::assert_eq(\n        indoc! {r#\"\n            var str = \"\";\n\n            outer: for (let i = 0; i < 5; i++) {\n                inner: for (let b = 0; b < 5; b++) {\n                    if (b === 2) {\n                    break outer;\n                    }\n                    str = str + b;\n                }\n                str = str + i;\n            }\n            str\n        \"#},\n        js_str!(\"01\"),\n    )]);\n}\n\n#[test]\nfn for_loop_continue_label() {\n    run_test_actions([TestAction::assert_eq(\n        indoc! {r#\"\n            var count = 0;\n            label: for (let x = 0; x < 10;) {\n                while (true) {\n                    x++;\n                    count++;\n                    continue label;\n                }\n            }\n            count\n        \"#},\n        10,\n    )]);\n}\n\n#[test]\nfn for_in_declaration() {\n    run_test_actions([\n        TestAction::run_harness(),\n        TestAction::run(indoc! {r#\"\n                let result = [];\n                let obj = { a: \"a\", b: 2};\n                var i;\n                for (i in obj) {\n                    result = result.concat([i]);\n                }\n            \"#}),\n        TestAction::assert(\"arrayEquals(result, ['a', 'b'])\"),\n    ]);\n}\n\n#[test]\nfn for_in_var_object() {\n    run_test_actions([\n        TestAction::run_harness(),\n        TestAction::run(indoc! {r#\"\n                let result = [];\n                let obj = { a: \"a\", b: 2};\n                for (var i in obj) {\n                    result = result.concat([i]);\n                }\n            \"#}),\n        TestAction::assert(\"arrayEquals(result, ['a', 'b'])\"),\n    ]);\n}\n\n#[test]\nfn for_in_var_array() {\n    run_test_actions([\n        TestAction::run_harness(),\n        TestAction::run(indoc! {r#\"\n                let result = [];\n                let arr = [\"a\", \"b\"];\n                for (var i in arr) {\n                    result = result.concat([i]);\n                }\n            \"#}),\n        TestAction::assert(\"arrayEquals(result, ['0', '1'])\"),\n    ]);\n}\n\n#[test]\nfn for_in_let_object() {\n    run_test_actions([\n        TestAction::run_harness(),\n        TestAction::run(indoc! {r#\"\n                let result = [];\n                let obj = { a: \"a\", b: 2};\n                for (let i in obj) {\n                    result = result.concat([i]);\n                }\n            \"#}),\n        TestAction::assert(\"arrayEquals(result, ['a', 'b'])\"),\n    ]);\n}\n\n#[test]\nfn for_in_const_array() {\n    run_test_actions([\n        TestAction::run_harness(),\n        TestAction::run(indoc! {r#\"\n                let result = [];\n                let arr = [\"a\", \"b\"];\n                for (const i in arr) {\n                    result = result.concat([i]);\n                }\n            \"#}),\n        TestAction::assert(\"arrayEquals(result, ['0', '1'])\"),\n    ]);\n}\n\n#[test]\nfn for_in_break_label() {\n    run_test_actions([TestAction::assert_eq(\n        indoc! {r#\"\n            var str = \"\";\n\n            outer: for (let i in [1, 2]) {\n                inner: for (let b in [2, 3, 4]) {\n                    if (b === \"1\") {\n                        break outer;\n                    }\n                    str = str + b;\n                }\n                str = str + i;\n            }\n            str\n        \"#},\n        js_str!(\"0\"),\n    )]);\n}\n\n#[test]\nfn for_in_continue_label() {\n    run_test_actions([TestAction::assert_eq(\n        indoc! {r#\"\n            var str = \"\";\n\n            outer: for (let i in [1, 2]) {\n                inner: for (let b in [2, 3, 4]) {\n                    if (b === \"1\") {\n                        continue outer;\n                    }\n                    str = str + b;\n                }\n                str = str + i;\n            }\n            str\n        \"#},\n        js_str!(\"00\"),\n    )]);\n}\n"
  },
  {
    "path": "core/engine/src/tests/control_flow/mod.rs",
    "content": "use boa_macros::js_str;\nuse indoc::indoc;\nmod loops;\n\nuse crate::{JsNativeErrorKind, TestAction, run_test_actions};\n\n#[test]\nfn test_invalid_break() {\n    run_test_actions([TestAction::assert_native_error(\n        \"break;\",\n        JsNativeErrorKind::Syntax,\n        \"illegal break statement at line 1, col 1\",\n    )]);\n}\n\n#[test]\nfn test_invalid_continue_target() {\n    run_test_actions([TestAction::assert_native_error(\n        indoc! {r#\"\n            while (false) {\n                continue nonexistent;\n            }\n        \"#},\n        JsNativeErrorKind::Syntax,\n        \"undefined continue target: nonexistent at line 1, col 1\",\n    )]);\n}\n\n#[test]\nfn test_invalid_continue() {\n    run_test_actions([TestAction::assert_native_error(\n        \"continue;\",\n        JsNativeErrorKind::Syntax,\n        \"illegal continue statement at line 1, col 1\",\n    )]);\n}\n\n#[test]\nfn test_labelled_block() {\n    run_test_actions([TestAction::assert(indoc! {r#\"\n            let result = true;\n            {\n                let x = 2;\n                L: {\n                    let x = 3;\n                    result &&= (x === 3);\n                    break L;\n                    result &&= (false);\n                }\n                result &&= (x === 2);\n            }\n            result;\n        \"#})]);\n}\n\n#[test]\nfn simple_try() {\n    run_test_actions([TestAction::assert_eq(\n        indoc! {r#\"\n            let a = 10;\n            try {\n                a = 20;\n            } catch {\n                a = 30;\n            }\n\n            a;\n        \"#},\n        20,\n    )]);\n}\n\n#[test]\nfn finally() {\n    run_test_actions([TestAction::assert_eq(\n        indoc! {r#\"\n            let a = 10;\n            try {\n                a = 20;\n            } finally {\n                a = 30;\n            }\n\n            a;\n        \"#},\n        30,\n    )]);\n}\n\n#[test]\nfn catch_finally() {\n    run_test_actions([TestAction::assert_eq(\n        indoc! {r#\"\n            let a = 10;\n            try {\n                a = 20;\n            } catch {\n                a = 40;\n            } finally {\n                a = 30;\n            }\n\n            a;\n        \"#},\n        30,\n    )]);\n}\n\n#[test]\nfn catch() {\n    run_test_actions([TestAction::assert_eq(\n        indoc! {r#\"\n            let a = 10;\n            try {\n                throw \"error\";\n            } catch {\n                a = 20;\n            }\n\n            a;\n        \"#},\n        20,\n    )]);\n}\n\n#[test]\nfn catch_binding() {\n    run_test_actions([TestAction::assert_eq(\n        indoc! {r#\"\n            let a = 10;\n            try {\n                throw 20;\n            } catch(err) {\n                a = err;\n            }\n\n            a;\n        \"#},\n        20,\n    )]);\n}\n\n#[test]\nfn catch_binding_pattern_object() {\n    run_test_actions([TestAction::assert_eq(\n        indoc! {r#\"\n            let a = 10;\n            try {\n                throw {\n                    n: 30,\n                };\n            } catch ({ n }) {\n                a = n;\n            }\n\n            a;\n        \"#},\n        30,\n    )]);\n}\n\n#[test]\nfn catch_binding_pattern_array() {\n    run_test_actions([TestAction::assert_eq(\n        indoc! {r#\"\n            let a = 10;\n            try {\n                throw [20, 30];\n            } catch ([, n]) {\n                a = n;\n            }\n\n            a;\n        \"#},\n        30,\n    )]);\n}\n\n#[test]\nfn catch_binding_finally() {\n    run_test_actions([TestAction::assert_eq(\n        indoc! {r#\"\n            let a = 10;\n            try {\n                throw 20;\n            } catch(err) {\n                a = err;\n            } finally {\n                a = 30;\n            }\n\n            a;\n        \"#},\n        30,\n    )]);\n}\n\n#[test]\nfn finally_with_loop_break() {\n    run_test_actions([TestAction::assert_eq(\n        indoc! {r#\"\n            try {\n              30;\n            }\n            catch {\n            } finally {\n              while(true) {\n                break;\n              }\n            }\n        \"#},\n        30,\n    )]);\n}\n\n#[test]\nfn single_case_switch() {\n    run_test_actions([TestAction::assert_eq(\n        indoc! {r#\"\n            let a = 10;\n            switch (a) {\n                case 10:\n                    a = 20;\n                    break;\n            }\n\n            a;\n        \"#},\n        20,\n    )]);\n}\n\n#[test]\nfn no_cases_switch() {\n    run_test_actions([TestAction::assert_eq(\n        indoc! {r#\"\n            let a = 10;\n            switch (a) {\n            }\n\n            a;\n        \"#},\n        10,\n    )]);\n}\n\n#[test]\nfn no_true_case_switch() {\n    run_test_actions([TestAction::assert_eq(\n        indoc! {r#\"\n            let a = 10;\n            switch (a) {\n                case 5:\n                    a = 15;\n                    break;\n            }\n\n            a;\n        \"#},\n        10,\n    )]);\n}\n\n#[test]\nfn two_case_switch() {\n    run_test_actions([TestAction::assert_eq(\n        indoc! {r#\"\n            let a = 10;\n            switch (a) {\n                case 5:\n                    a = 15;\n                    break;\n                case 10:\n                    a = 20;\n                    break;\n            }\n\n            a;\n        \"#},\n        20,\n    )]);\n}\n\n#[test]\nfn two_case_no_break_switch() {\n    run_test_actions([TestAction::assert_eq(\n        indoc! {r#\"\n            let a = 10;\n            let b = 10;\n\n            switch (a) {\n                case 10:\n                    a = 150;\n                case 20:\n                    b = 150;\n                    break;\n            }\n\n            a + b;\n        \"#},\n        300,\n    )]);\n}\n\n#[test]\nfn three_case_partial_fallthrough() {\n    run_test_actions([TestAction::assert_eq(\n        indoc! {r#\"\n            let a = 10;\n            let b = 10;\n\n            switch (a) {\n                case 10:\n                    a = 150;\n                case 20:\n                    b = 150;\n                    break;\n                case 15:\n                    b = 1000;\n                    break;\n            }\n\n            a + b;\n        \"#},\n        300,\n    )]);\n}\n\n#[test]\nfn default_taken_switch() {\n    run_test_actions([TestAction::assert_eq(\n        indoc! {r#\"\n            let a = 10;\n\n            switch (a) {\n                case 5:\n                    a = 150;\n                    break;\n                default:\n                    a = 70;\n            }\n\n            a;\n        \"#},\n        70,\n    )]);\n}\n\n#[test]\nfn default_not_taken_switch() {\n    run_test_actions([TestAction::assert_eq(\n        indoc! {r#\"\n            let a = 5;\n\n            switch (a) {\n                case 5:\n                    a = 150;\n                    break;\n                default:\n                    a = 70;\n            }\n\n            a;\n        \"#},\n        150,\n    )]);\n}\n\n#[test]\nfn string_switch() {\n    run_test_actions([TestAction::assert_eq(\n        indoc! {r#\"\n            let a = \"hello\";\n\n            switch (a) {\n                case \"hello\":\n                    a = \"world\";\n                    break;\n                default:\n                    a = \"hi\";\n            }\n\n            a;\n        \"#},\n        js_str!(\"world\"),\n    )]);\n}\n\n#[test]\nfn bigger_switch_example() {\n    run_test_actions([\n        TestAction::run(indoc! {r#\"\n                function f(a) {\n                    let b;\n\n                    switch (a) {\n                        case 0:\n                            b = \"Mon\";\n                            break;\n                        case 1:\n                            b = \"Tue\";\n                            break;\n                        case 2:\n                            b = \"Wed\";\n                            break;\n                        case 3:\n                            b = \"Thurs\";\n                            break;\n                        case 4:\n                            b = \"Fri\";\n                            break;\n                        case 5:\n                            b = \"Sat\";\n                            break;\n                        case 6:\n                            b = \"Sun\";\n                            break;\n                    }\n                    return b;\n                }\n            \"#}),\n        TestAction::assert_eq(\"f(0)\", js_str!(\"Mon\")),\n        TestAction::assert_eq(\"f(1)\", js_str!(\"Tue\")),\n        TestAction::assert_eq(\"f(2)\", js_str!(\"Wed\")),\n        TestAction::assert_eq(\"f(3)\", js_str!(\"Thurs\")),\n        TestAction::assert_eq(\"f(4)\", js_str!(\"Fri\")),\n        TestAction::assert_eq(\"f(5)\", js_str!(\"Sat\")),\n        TestAction::assert_eq(\"f(6)\", js_str!(\"Sun\")),\n    ]);\n}\n\n#[test]\nfn break_labelled_if_statement() {\n    run_test_actions([TestAction::assert_eq(\n        indoc! {r#\"\n            let result = \"\";\n            bar: if(true) {\n                result = \"foo\";\n                break bar;\n                result = 'this will not be executed';\n            }\n            result\n        \"#},\n        js_str!(\"foo\"),\n    )]);\n}\n\n#[test]\nfn break_labelled_try_statement() {\n    run_test_actions([TestAction::assert_eq(\n        indoc! {r#\"\n            let result = \"\"\n            one: try {\n                result = \"foo\";\n                break one;\n                result = \"did not break\"\n            } catch (err) {\n                console.log(err)\n            }\n            result\n        \"#},\n        js_str!(\"foo\"),\n    )]);\n}\n"
  },
  {
    "path": "core/engine/src/tests/env.rs",
    "content": "use boa_macros::js_str;\nuse indoc::indoc;\n\nuse crate::{JsNativeErrorKind, TestAction, run_test_actions};\n\n#[test]\n// https://github.com/boa-dev/boa/issues/2317\nfn fun_block_eval_2317() {\n    run_test_actions([\n        TestAction::assert_eq(\n            indoc! {r#\"\n                (function(y){\n                    {\n                        eval(\"var x = 'inner';\");\n                    }\n                    return y + x;\n                })(\"arg\");\n            \"#},\n            js_str!(\"arginner\"),\n        ),\n        TestAction::assert_eq(\n            indoc! {r#\"\n                (function(y = \"default\"){\n                    {\n                        eval(\"var x = 'inner';\");\n                    }\n                    return y + x;\n                })();\n            \"#},\n            js_str!(\"defaultinner\"),\n        ),\n    ]);\n}\n\n#[test]\n// https://github.com/boa-dev/boa/issues/2719\nfn with_env_not_panic() {\n    run_test_actions([TestAction::assert_native_error(\n        indoc! {r#\"\n            with({ p1:1,  }) {k[oa>>2]=d;}\n            {\n            let a12345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890 = 1,\n                b = \"\";\n            }\n        \"#},\n        JsNativeErrorKind::Reference,\n        \"k is not defined\",\n    )]);\n}\n\n#[test]\n// https://github.com/boa-dev/boa/issues/4350\nfn indirect_eval_function_var_binding_4350() {\n    run_test_actions([TestAction::assert_eq(\n        indoc! {r#\"\n            var t = [];\n\n            var s1 = `\n            function core() { t.push(1) }\n\n            core.prototype.a = function () { t.push(2) }\n            core.prototype.b = function () { t.push(3) }\n            `;\n            var s2 = `\n            function core() { t.push(1) }\n\n            core.prototype.a = function () { t.push(2) }\n            core.prototype.b = function () { t.push(3) }\n            var core = new core();\n            `;\n            var s3 = `\n            function core() { t.push(1) }\n            var core = new core();\n            `;\n\n            function run_ctx(s) {\n                (1,eval)(s);\n            }\n\n            function test() {\n                run_ctx(s1);\n                var core1 = new core();\n\n                run_ctx(s2);\n                var core2 = core;\n\n                run_ctx(s3);\n                var core3 = core;\n                return [core1, core2, core3].toString();\n            }\n\n            test();\n        \"#},\n        js_str!(\"[object Object],[object Object],[object Object]\"),\n    )]);\n}\n"
  },
  {
    "path": "core/engine/src/tests/function.rs",
    "content": "use crate::{JsNativeErrorKind, JsValue, TestAction, run_test_actions};\nuse boa_macros::js_str;\nuse indoc::indoc;\n\n#[test]\nfn function_declaration_returns_undefined() {\n    run_test_actions([TestAction::assert_eq(\n        \"function abc() {}\",\n        JsValue::undefined(),\n    )]);\n}\n\n#[test]\nfn empty_function_returns_undefined() {\n    run_test_actions([TestAction::assert_eq(\n        \"(function () {}) ()\",\n        JsValue::undefined(),\n    )]);\n}\n\n#[test]\nfn property_accessor_member_expression_dot_notation_on_function() {\n    run_test_actions([TestAction::assert_eq(\n        indoc! {r#\"\n            function asd () {};\n            asd.name;\n        \"#},\n        js_str!(\"asd\"),\n    )]);\n}\n\n#[test]\nfn property_accessor_member_expression_bracket_notation_on_function() {\n    run_test_actions([TestAction::assert_eq(\n        indoc! {r#\"\n            function asd () {};\n            asd['name'];\n        \"#},\n        js_str!(\"asd\"),\n    )]);\n}\n\n#[test]\nfn early_return() {\n    run_test_actions([\n        TestAction::assert(indoc! {r#\"\n                function early_return() {\n                    if (true) {\n                        return true;\n                    }\n                    return false;\n                }\n                early_return()\n            \"#}),\n        TestAction::assert_eq(\n            indoc! {r#\"\n                function nested_fnct() {\n                    return \"nested\";\n                }\n                function outer_fnct() {\n                    nested_fnct();\n                    return \"outer\";\n                }\n                outer_fnct()\n            \"#},\n            js_str!(\"outer\"),\n        ),\n    ]);\n}\n\n#[test]\nfn should_set_this_value() {\n    run_test_actions([\n        TestAction::run(indoc! {r#\"\n                function Foo() {\n                    this.a = \"a\";\n                    this.b = \"b\";\n                }\n\n                var bar = new Foo();\n            \"#}),\n        TestAction::assert_eq(\"bar.a\", js_str!(\"a\")),\n        TestAction::assert_eq(\"bar.b\", js_str!(\"b\")),\n    ]);\n}\n\n#[test]\nfn should_type_error_when_new_is_not_constructor() {\n    run_test_actions([TestAction::assert_native_error(\n        \"new ''()\",\n        JsNativeErrorKind::Type,\n        \"not a constructor\",\n    )]);\n}\n\n#[test]\nfn new_instance_should_point_to_prototype() {\n    // A new instance should point to a prototype object created with the constructor function\n    run_test_actions([\n        TestAction::run(indoc! {r#\"\n                function Foo() {}\n                var bar = new Foo();\n            \"#}),\n        TestAction::assert(\"Object.getPrototypeOf(bar) == Foo.prototype \"),\n    ]);\n}\n\n#[test]\nfn calling_function_with_unspecified_arguments() {\n    run_test_actions([TestAction::assert_eq(\n        indoc! {r#\"\n            function test(a, b) {\n                return b;\n            }\n\n            test(10)\n        \"#},\n        JsValue::undefined(),\n    )]);\n}\n\n#[test]\nfn not_a_function() {\n    run_test_actions([\n        TestAction::run(indoc! {r#\"\n                let a = {};\n                let b = true;\n            \"#}),\n        TestAction::assert_native_error(\"a()\", JsNativeErrorKind::Type, \"not a callable function\"),\n        TestAction::assert_native_error(\n            \"a.a()\",\n            JsNativeErrorKind::Type,\n            \"not a callable function\",\n        ),\n        TestAction::assert_native_error(\"b()\", JsNativeErrorKind::Type, \"not a callable function\"),\n    ]);\n}\n\n#[test]\nfn strict_mode_dup_func_parameters() {\n    // Checks that a function cannot contain duplicate parameter\n    // names in strict mode code as per https://tc39.es/ecma262/#sec-function-definitions-static-semantics-early-errors.\n    run_test_actions([TestAction::assert_native_error(\n        indoc! {r#\"\n            'use strict';\n            function f(a, b, b) {}\n        \"#},\n        JsNativeErrorKind::Syntax,\n        \"Duplicate parameter name not allowed in this context at line 2, col 12\",\n    )]);\n}\n\n#[test]\nfn duplicate_function_name() {\n    run_test_actions([TestAction::assert_eq(\n        indoc! {r#\"\n            function f () {}\n            function f () {return 12;}\n            f()\n        \"#},\n        12,\n    )]);\n}\n\n#[test]\nfn eval_out_of_scope() {\n    run_test_actions([\n        TestAction::run(indoc! {\n        r#\"\n            function f() {\n                var x = null;\n                return (function() {\n                    return eval(\"x\");\n                })();\n            }\n        \"#\n        }),\n        TestAction::assert_eq(\"f()\", JsValue::null()),\n    ]);\n}\n\n/// Regression test for issue #4531.\n/// `Function` constructor with nested function containing lexical bindings\n/// captured by a closure should not panic with \"must be declarative environment\".\n#[test]\nfn function_constructor_nested_lexical_binding() {\n    run_test_actions([TestAction::assert_eq(\n        indoc! {r#\"\n            var code = \"\\\n                function f() {\\\n                    const a = 42;\\\n                    return () => { return a; };\\\n                }\\\n                return f()();\\\n            \";\n            Function(code)();\n        \"#},\n        42,\n    )]);\n}\n"
  },
  {
    "path": "core/engine/src/tests/generators.rs",
    "content": "use crate::{JsValue, TestAction, run_test_actions};\nuse boa_macros::js_str;\nuse indoc::indoc;\n\n#[test]\nfn basic_yield() {\n    // Checks that yield produces the correct value with done: false,\n    // and that exhausting the generator returns undefined with done: true.\n    run_test_actions([\n        TestAction::run(indoc! {r#\"\n            function* gen() {\n                yield 1;\n                yield 2;\n                yield 3;\n            }\n            const g = gen();\n            let r1 = g.next();\n            let r2 = g.next();\n            let r3 = g.next();\n            let r4 = g.next();\n        \"#}),\n        TestAction::assert_eq(\"r1.value\", 1),\n        TestAction::assert_eq(\"r1.done\", false),\n        TestAction::assert_eq(\"r2.value\", 2),\n        TestAction::assert_eq(\"r2.done\", false),\n        TestAction::assert_eq(\"r3.value\", 3),\n        TestAction::assert_eq(\"r3.done\", false),\n        TestAction::assert_eq(\"r4.value\", JsValue::undefined()),\n        TestAction::assert_eq(\"r4.done\", true),\n    ]);\n}\n\n#[test]\nfn explicit_return() {\n    // Checks that `return <value>` inside a generator produces {value, done: true}\n    // and that subsequent .next() calls return {undefined, true}.\n    run_test_actions([\n        TestAction::run(indoc! {r#\"\n            function* gen() {\n                yield 1;\n                return 42;\n            }\n            const g = gen();\n            let r1 = g.next();\n            let r2 = g.next();\n            let r3 = g.next();\n        \"#}),\n        TestAction::assert_eq(\"r1.value\", 1),\n        TestAction::assert_eq(\"r1.done\", false),\n        TestAction::assert_eq(\"r2.value\", 42),\n        TestAction::assert_eq(\"r2.done\", true),\n        TestAction::assert_eq(\"r3.value\", JsValue::undefined()),\n        TestAction::assert_eq(\"r3.done\", true),\n    ]);\n}\n\n#[test]\nfn next_with_value() {\n    // Checks that the argument to .next(value) becomes the result of the\n    // yield expression inside the generator.\n    run_test_actions([\n        TestAction::run(indoc! {r#\"\n            function* gen() {\n                let a = yield 1;\n                let b = yield a + 10;\n                return a + b;\n            }\n            const g = gen();\n            let r1 = g.next();\n            let r2 = g.next(5);\n            let r3 = g.next(20);\n        \"#}),\n        TestAction::assert_eq(\"r1.value\", 1),\n        TestAction::assert_eq(\"r1.done\", false),\n        TestAction::assert_eq(\"r2.value\", 15),\n        TestAction::assert_eq(\"r2.done\", false),\n        TestAction::assert_eq(\"r3.value\", 25),\n        TestAction::assert_eq(\"r3.done\", true),\n    ]);\n}\n\n#[test]\nfn return_method() {\n    // Checks that calling .return(value) terminates the generator early\n    // and produces {value, done: true}.\n    run_test_actions([\n        TestAction::run(indoc! {r#\"\n            function* gen() {\n                yield 1;\n                yield 2;\n                yield 3;\n            }\n            const g = gen();\n            let r1 = g.next();\n            let r2 = g.return(99);\n            let r3 = g.next();\n        \"#}),\n        TestAction::assert_eq(\"r1.value\", 1),\n        TestAction::assert_eq(\"r1.done\", false),\n        TestAction::assert_eq(\"r2.value\", 99),\n        TestAction::assert_eq(\"r2.done\", true),\n        TestAction::assert_eq(\"r3.value\", JsValue::undefined()),\n        TestAction::assert_eq(\"r3.done\", true),\n    ]);\n}\n\n#[test]\nfn throw_method_uncaught() {\n    // Checks that calling .throw(error) propagates the error to the caller\n    // when the generator doesn't catch it, and marks the generator as completed.\n    run_test_actions([\n        TestAction::run(indoc! {r#\"\n            function* gen() {\n                yield 1;\n                yield 2;\n            }\n            const g = gen();\n            let r1 = g.next();\n            let threw = false;\n            try {\n                g.throw(new Error(\"boom\"));\n            } catch (e) {\n                threw = true;\n                var errMsg = e.message;\n            }\n            let r2 = g.next();\n        \"#}),\n        TestAction::assert_eq(\"r1.value\", 1),\n        TestAction::assert_eq(\"r1.done\", false),\n        TestAction::assert_eq(\"threw\", true),\n        TestAction::assert_eq(\"errMsg\", js_str!(\"boom\")),\n        TestAction::assert_eq(\"r2.value\", JsValue::undefined()),\n        TestAction::assert_eq(\"r2.done\", true),\n    ]);\n}\n\n#[test]\nfn throw_method_caught() {\n    // Checks that .throw(error) can be caught inside the generator via try/catch,\n    // allowing the generator to continue yielding.\n    run_test_actions([\n        TestAction::run(indoc! {r#\"\n            function* gen() {\n                let result;\n                try {\n                    result = yield 1;\n                } catch (e) {\n                    result = \"caught: \" + e.message;\n                }\n                yield result;\n                return \"done\";\n            }\n            const g = gen();\n            let r1 = g.next();\n            let r2 = g.throw(new Error(\"oops\"));\n            let r3 = g.next();\n        \"#}),\n        TestAction::assert_eq(\"r1.value\", 1),\n        TestAction::assert_eq(\"r1.done\", false),\n        TestAction::assert_eq(\"r2.value\", js_str!(\"caught: oops\")),\n        TestAction::assert_eq(\"r2.done\", false),\n        TestAction::assert_eq(\"r3.value\", js_str!(\"done\")),\n        TestAction::assert_eq(\"r3.done\", true),\n    ]);\n}\n\n#[test]\nfn exhausted_generator() {\n    // Checks that calling .next() on an already-completed generator\n    // always returns {value: undefined, done: true}.\n    run_test_actions([\n        TestAction::run(indoc! {r#\"\n            function* gen() {\n                yield 1;\n            }\n            const g = gen();\n            g.next();\n            g.next();\n            let r1 = g.next();\n            let r2 = g.next();\n            let r3 = g.next();\n        \"#}),\n        TestAction::assert_eq(\"r1.value\", JsValue::undefined()),\n        TestAction::assert_eq(\"r1.done\", true),\n        TestAction::assert_eq(\"r2.value\", JsValue::undefined()),\n        TestAction::assert_eq(\"r2.done\", true),\n        TestAction::assert_eq(\"r3.value\", JsValue::undefined()),\n        TestAction::assert_eq(\"r3.done\", true),\n    ]);\n}\n\n#[test]\nfn return_before_start() {\n    // Checks that calling .return() on a generator that hasn't started\n    // completes it immediately without executing any body code.\n    run_test_actions([\n        TestAction::run(indoc! {r#\"\n            let bodyRan = false;\n            function* gen() {\n                bodyRan = true;\n                yield 1;\n            }\n            const g = gen();\n            let r1 = g.return(42);\n            let r2 = g.next();\n        \"#}),\n        TestAction::assert_eq(\"bodyRan\", false),\n        TestAction::assert_eq(\"r1.value\", 42),\n        TestAction::assert_eq(\"r1.done\", true),\n        TestAction::assert_eq(\"r2.value\", JsValue::undefined()),\n        TestAction::assert_eq(\"r2.done\", true),\n    ]);\n}\n\n#[test]\nfn throw_before_start() {\n    // Checks that calling .throw() on a generator that hasn't started\n    // completes it immediately and propagates the error without executing any body code.\n    run_test_actions([\n        TestAction::run(indoc! {r#\"\n            let bodyRan = false;\n            function* gen() {\n                bodyRan = true;\n                yield 1;\n            }\n            const g = gen();\n            let threw = false;\n            try {\n                g.throw(new Error(\"early\"));\n            } catch (e) {\n                threw = true;\n                var errMsg = e.message;\n            }\n            let r1 = g.next();\n        \"#}),\n        TestAction::assert_eq(\"bodyRan\", false),\n        TestAction::assert_eq(\"threw\", true),\n        TestAction::assert_eq(\"errMsg\", js_str!(\"early\")),\n        TestAction::assert_eq(\"r1.value\", JsValue::undefined()),\n        TestAction::assert_eq(\"r1.done\", true),\n    ]);\n}\n"
  },
  {
    "path": "core/engine/src/tests/iterators.rs",
    "content": "use indoc::indoc;\n\nuse crate::{TestAction, run_test_actions};\n\n#[test]\nfn iterator_close_in_continue_before_jobs() {\n    run_test_actions([\n        TestAction::run_harness(),\n        TestAction::run(indoc! {r#\"\n            var actual = [];\n\n            var iter = {\n                [Symbol.iterator]() {\n                    return this;\n                },\n                next() {\n                    actual.push(\"call next\");\n                    return {\n                        done: false,\n                    };\n                },\n                get return() {\n                    actual.push(\"get return\");\n                    return function () {\n                        actual.push(\"return call\");\n                        return {\n                            done: true\n                        }\n                    }\n                }\n            };\n\n            Promise.resolve(0)\n                .then(() => actual.push(\"tick 1\"))\n                .then(() => actual.push(\"tick 2\"));\n\n            void async function f() {\n                actual.push(\"async fn start\");\n                let count = 0;\n                loop: while (count === 0) {\n                    count++;\n                    for (_ of iter) {\n                        continue loop;\n                    }\n                }\n                actual.push(\"async fn end\");\n            }();\n        \"#}),\n        TestAction::inspect_context(|ctx| ctx.run_jobs().unwrap()),\n        TestAction::assert(indoc! {r#\"\n            arrayEquals(\n                actual,\n                [\n                    \"async fn start\",\n                    \"call next\",\n                    \"get return\",\n                    \"return call\",\n                    \"async fn end\",\n                    \"tick 1\",\n                    \"tick 2\",\n                ]\n            )\n            \"#}),\n    ]);\n}\n\n#[test]\nfn async_iterator_close_in_continue_is_awaited() {\n    run_test_actions([\n        TestAction::run_harness(),\n        TestAction::run(indoc! {r#\"\n            var actual = [];\n\n            var asyncIter = {\n                [Symbol.asyncIterator]() {\n                    return this;\n                },\n                next() {\n                    actual.push(\"async call next\");\n                    return {\n                        done: false,\n                    };\n                },\n                get return() {\n                    actual.push(\"get async return\");\n                    return function () {\n                        actual.push(\"async return call\");\n                        return {\n                            done: true\n                        };\n                    }\n                }\n            };\n\n            Promise.resolve(0)\n                .then(() => actual.push(\"tick 1\"))\n                .then(() => actual.push(\"tick 2\"))\n                .then(() => actual.push(\"tick 3\"));\n\n            void async function f() {\n                actual.push(\"async fn start\");\n                let count = 0;\n                loop: while (count === 0) {\n                    count++;\n                    for await (__ of asyncIter) {\n                        continue loop;\n                    }\n                }\n                actual.push(\"async fn end\");\n            }();\n        \"#}),\n        TestAction::inspect_context(|ctx| ctx.run_jobs().unwrap()),\n        TestAction::assert(indoc! {r#\"\n            arrayEquals(\n                actual,\n                [\n                    \"async fn start\",\n                    \"async call next\",\n                    \"tick 1\",\n                    \"get async return\",\n                    \"async return call\",\n                    \"tick 2\",\n                    \"async fn end\",\n                    \"tick 3\"\n                ]\n            )\n        \"#}),\n    ]);\n}\n\n#[test]\nfn mixed_iterators_close_in_continue() {\n    run_test_actions([\n        TestAction::run_harness(),\n        TestAction::run(indoc! {r#\"\n            var actual = [];\n\n            var iter = {\n                [Symbol.iterator]() {\n                    return this;\n                },\n                next() {\n                    actual.push(\"call next\");\n                    return {\n                        done: false,\n                    };\n                },\n                get return() {\n                    actual.push(\"get return\");\n                    return function () {\n                        actual.push(\"return call\");\n                        return {\n                            done: true\n                        }\n                    }\n                }\n            };\n\n            var asyncIter = {\n                [Symbol.asyncIterator]() {\n                    return this;\n                },\n                next() {\n                    actual.push(\"async call next\");\n                    return {\n                        done: false,\n                    };\n                },\n                get return() {\n                    actual.push(\"get async return\");\n                    return function () {\n                        actual.push(\"async return call\");\n                        return {\n                            done: true\n                        };\n                    }\n                }\n            };\n\n            Promise.resolve(0)\n                .then(() => actual.push(\"tick 1\"))\n                .then(() => actual.push(\"tick 2\"))\n                .then(() => actual.push(\"tick 3\"));\n\n            void async function f() {\n                actual.push(\"async fn start\");\n                let count = 0;\n                loop: while (count === 0) {\n                    count++;\n                    for (_ of iter) {\n                        for await (__ of asyncIter) {\n                            continue loop;\n                        }\n                    }\n                }\n                actual.push(\"async fn end\");\n            }();\n        \"#}),\n        TestAction::inspect_context(|ctx| ctx.run_jobs().unwrap()),\n        TestAction::assert(indoc! {r#\"\n            arrayEquals(\n                actual,\n                [\n                    \"async fn start\",\n                    \"call next\",\n                    \"async call next\",\n                    \"tick 1\",\n                    \"get async return\",\n                    \"async return call\",\n                    \"tick 2\",\n                    \"get return\",\n                    \"return call\",\n                    \"async fn end\",\n                    \"tick 3\",\n                ]\n            )\n        \"#}),\n    ]);\n}\n"
  },
  {
    "path": "core/engine/src/tests/job.rs",
    "content": "use std::{\n    cell::{Cell, RefCell},\n    pin::pin,\n    rc::Rc,\n};\n\nuse futures_lite::future;\n\nuse crate::{\n    JsValue, TestAction,\n    context::{ContextBuilder, time::FixedClock},\n    job::{GenericJob, JobExecutor, NativeAsyncJob, SimpleJobExecutor},\n    run_test_actions_with,\n};\n\n#[test]\nfn test_async_job_not_blocking_event_loop() {\n    let clock = Rc::new(FixedClock::default());\n    let context = &mut ContextBuilder::default()\n        .clock(clock.clone())\n        .build()\n        .unwrap();\n\n    run_test_actions_with(\n        [TestAction::inspect_context_async(async move |ctx| {\n            let executor = ctx.downcast_job_executor::<SimpleJobExecutor>().unwrap();\n            let ctx = &RefCell::new(ctx);\n\n            let mut event_loop = pin!(future::poll_once(executor.run_jobs_async(ctx)));\n\n            // There are no jobs in our queue. Push\n            // an async job that will consistently yield to the executor.\n            ctx.borrow_mut().enqueue_job(\n                NativeAsyncJob::new(async |_| {\n                    loop {\n                        future::yield_now().await;\n                    }\n                })\n                .into(),\n            );\n\n            // Then, start the event loop\n            assert!(event_loop.as_mut().await.is_none());\n\n            let checker = Rc::new(Cell::new(false));\n            {\n                let checker = checker.clone();\n                // At this point, the event loop should have yielded again to the async executor.\n                // Thus, enqueue a generic job that should resolve in the next loop.\n                let realm = ctx.borrow().realm().clone();\n                ctx.borrow_mut().enqueue_job(\n                    GenericJob::new(\n                        move |_| {\n                            checker.set(true);\n                            Ok(JsValue::undefined())\n                        },\n                        realm,\n                    )\n                    .into(),\n                );\n            }\n\n            // Next iteration of the event loop\n            assert!(event_loop.as_mut().await.is_none());\n\n            // At this point, our generic job should have been executed.\n            assert!(checker.get());\n        })],\n        context,\n    );\n}\n"
  },
  {
    "path": "core/engine/src/tests/mod.rs",
    "content": "#![cfg(any(not(feature = \"intl\"), feature = \"intl_bundled\"))]\n\nuse boa_macros::js_str;\nuse indoc::indoc;\n\nmod async_generator;\nmod class;\nmod control_flow;\nmod env;\nmod function;\nmod generators;\nmod iterators;\nmod job;\nmod operators;\nmod promise;\nmod spread;\nmod to_string;\n\nuse crate::{JsNativeErrorKind, JsValue, TestAction, run_test_actions};\n\n#[test]\nfn length_correct_value_on_string_literal() {\n    run_test_actions([TestAction::assert_eq(\"'hello'.length\", 5)]);\n}\n\n#[test]\nfn empty_let_decl_undefined() {\n    run_test_actions([TestAction::assert_eq(\"let a; a\", JsValue::undefined())]);\n}\n\n#[test]\nfn semicolon_expression_stop() {\n    run_test_actions([TestAction::assert_eq(\n        indoc! {r#\"\n            var a = 1;\n            + 1;\n            a\n        \"#},\n        1,\n    )]);\n}\n\n#[test]\nfn empty_var_decl_undefined() {\n    run_test_actions([TestAction::assert_eq(\"var a; a\", JsValue::undefined())]);\n}\n\n#[test]\nfn identifier_on_global_object_undefined() {\n    run_test_actions([TestAction::assert_native_error(\n        \"bar;\",\n        JsNativeErrorKind::Reference,\n        \"bar is not defined\",\n    )]);\n}\n\n#[test]\nfn object_field_set() {\n    run_test_actions([TestAction::assert_eq(\n        indoc! {r#\"\n            let m = {};\n            m['key'] = 22;\n            m['key']\n        \"#},\n        22,\n    )]);\n}\n\n#[test]\nfn array_field_set() {\n    run_test_actions([\n        TestAction::run(\"let m;\"),\n        // element_changes\n        TestAction::assert_eq(\n            indoc! {r#\"\n                m = [1, 2, 3];\n                m[1] = 5;\n                m[1]\n            \"#},\n            5,\n        ),\n        // length changes\n        TestAction::assert_eq(\n            indoc! {r#\"\n                m = [1, 2, 3];\n                m[10] = 52;\n                m.length\n            \"#},\n            11,\n        ),\n        // negative_index_wont_affect_length\n        TestAction::assert_eq(\n            indoc! {r#\"\n                m = [1, 2, 3];\n                m[-11] = 5;\n                m.length\n            \"#},\n            3,\n        ),\n        // non_num_key_wont_affect_length\n        TestAction::assert_eq(\n            indoc! {r#\"\n                m = [1, 2, 3];\n                m[\"magic\"] = 5;\n                m.length\n            \"#},\n            3,\n        ),\n    ]);\n}\n\n#[test]\nfn var_decl_hoisting_simple() {\n    run_test_actions([TestAction::assert_eq(\"x = 5; var x; x\", 5)]);\n}\n\n#[test]\nfn var_decl_hoisting_with_initialization() {\n    run_test_actions([TestAction::assert_eq(\"x = 5; var x = 10; x\", 10)]);\n}\n\n#[test]\nfn var_decl_hoisting_2_variables_hoisting() {\n    run_test_actions([TestAction::assert_eq(\n        indoc! {r#\"\n            x = y;\n\n            var x = 10;\n            var y = 5;\n\n            x;\n        \"#},\n        10,\n    )]);\n}\n\n#[test]\nfn var_decl_hoisting_2_variables_hoisting_2() {\n    run_test_actions([TestAction::assert_eq(\n        indoc! {r#\"\n            var x = y;\n\n            var y = 5;\n            x;\n        \"#},\n        JsValue::undefined(),\n    )]);\n}\n\n#[test]\nfn var_decl_hoisting_2_variables_hoisting_3() {\n    run_test_actions([TestAction::assert_eq(\n        indoc! {r#\"\n            let y = x;\n            x = 5;\n\n            var x = 10;\n            y;\n        \"#},\n        JsValue::undefined(),\n    )]);\n}\n\n#[test]\nfn function_decl_hoisting() {\n    run_test_actions([\n        TestAction::assert_eq(\n            indoc! {r#\"\n                {\n                    let a = hello();\n                    function hello() { return 5 }\n\n                    a;\n                }\n            \"#},\n            5,\n        ),\n        TestAction::assert_eq(\n            indoc! {r#\"\n                {\n                    x = hello();\n\n                    function hello() { return 5 }\n                    var x;\n                    x;\n                }\n            \"#},\n            5,\n        ),\n        TestAction::assert_eq(\n            indoc! {r#\"\n                {\n                    hello = function() { return 5 }\n                    x = hello();\n\n                    x;\n                }\n            \"#},\n            5,\n        ),\n        TestAction::assert_eq(\n            indoc! {r#\"\n                {\n                    let x = b();\n\n                    function a() {return 5}\n                    function b() {return a()}\n\n                    x;\n                }\n            \"#},\n            5,\n        ),\n        TestAction::assert_eq(\n            indoc! {r#\"\n                {\n                    let x = b();\n\n                    function b() {return a()}\n                    function a() {return 5}\n\n                    x;\n                }\n            \"#},\n            5,\n        ),\n    ]);\n}\n\n#[test]\nfn check_this_binding_in_object_literal() {\n    run_test_actions([TestAction::assert_eq(\n        indoc! {r#\"\n            var foo = {\n                a: 3,\n                bar: function () { return this.a + 5 }\n            };\n\n            foo.bar()\n        \"#},\n        8,\n    )]);\n}\n\n#[test]\nfn array_creation_benchmark() {\n    run_test_actions([\n        TestAction::run_harness(),\n        TestAction::assert(indoc! {r#\"\n                const finalArr = [ \"p0\", \"p1\", \"p2\", \"p3\", \"p4\", \"p5\", \"p6\", \"p7\", \"p8\", \"p9\", \"p10\",\n                \"p11\", \"p12\", \"p13\", \"p14\", \"p15\", \"p16\", \"p17\", \"p18\", \"p19\", \"p20\",\n                \"p21\", \"p22\", \"p23\", \"p24\", \"p25\", \"p26\", \"p27\", \"p28\", \"p29\", \"p30\",\n                \"p31\", \"p32\", \"p33\", \"p34\", \"p35\", \"p36\", \"p37\", \"p38\", \"p39\", \"p40\",\n                \"p41\", \"p42\", \"p43\", \"p44\", \"p45\", \"p46\", \"p47\", \"p48\", \"p49\", \"p50\",\n                \"p51\", \"p52\", \"p53\", \"p54\", \"p55\", \"p56\", \"p57\", \"p58\", \"p59\", \"p60\",\n                \"p61\", \"p62\", \"p63\", \"p64\", \"p65\", \"p66\", \"p67\", \"p68\", \"p69\", \"p70\",\n                \"p71\", \"p72\", \"p73\", \"p74\", \"p75\", \"p76\", \"p77\", \"p78\", \"p79\", \"p80\",\n                \"p81\", \"p82\", \"p83\", \"p84\", \"p85\", \"p86\", \"p87\", \"p88\", \"p89\", \"p90\",\n                \"p91\", \"p92\", \"p93\", \"p94\", \"p95\", \"p96\", \"p97\", \"p98\", \"p99\", \"p100\",\n                \"p101\", \"p102\", \"p103\", \"p104\", \"p105\", \"p106\", \"p107\", \"p108\", \"p109\", \"p110\",\n                \"p111\", \"p112\", \"p113\", \"p114\", \"p115\", \"p116\", \"p117\", \"p118\", \"p119\", \"p120\",\n                \"p121\", \"p122\", \"p123\", \"p124\", \"p125\", \"p126\", \"p127\", \"p128\", \"p129\", \"p130\",\n                \"p131\", \"p132\", \"p133\", \"p134\", \"p135\", \"p136\", \"p137\", \"p138\", \"p139\", \"p140\",\n                \"p141\", \"p142\", \"p143\", \"p144\", \"p145\", \"p146\", \"p147\", \"p148\", \"p149\", \"p150\",\n                \"p151\", \"p152\", \"p153\", \"p154\", \"p155\", \"p156\", \"p157\", \"p158\", \"p159\", \"p160\",\n                \"p161\", \"p162\", \"p163\", \"p164\", \"p165\", \"p166\", \"p167\", \"p168\", \"p169\", \"p170\",\n                \"p171\", \"p172\", \"p173\", \"p174\", \"p175\", \"p176\", \"p177\", \"p178\", \"p179\", \"p180\",\n                \"p181\", \"p182\", \"p183\", \"p184\", \"p185\", \"p186\", \"p187\", \"p188\", \"p189\", \"p190\",\n                \"p191\", \"p192\", \"p193\", \"p194\", \"p195\", \"p196\", \"p197\", \"p198\", \"p199\", \"p200\",\n                \"p201\", \"p202\", \"p203\", \"p204\", \"p205\", \"p206\", \"p207\", \"p208\", \"p209\", \"p210\",\n                \"p211\", \"p212\", \"p213\", \"p214\", \"p215\", \"p216\", \"p217\", \"p218\", \"p219\", \"p220\",\n                \"p221\", \"p222\", \"p223\", \"p224\", \"p225\", \"p226\", \"p227\", \"p228\", \"p229\", \"p230\",\n                \"p231\", \"p232\", \"p233\", \"p234\", \"p235\", \"p236\", \"p237\", \"p238\", \"p239\", \"p240\",\n                \"p241\", \"p242\", \"p243\", \"p244\", \"p245\", \"p246\", \"p247\", \"p248\", \"p249\", \"p250\",\n                \"p251\", \"p252\", \"p253\", \"p254\", \"p255\", \"p256\", \"p257\", \"p258\", \"p259\", \"p260\",\n                \"p261\", \"p262\", \"p263\", \"p264\", \"p265\", \"p266\", \"p267\", \"p268\", \"p269\", \"p270\",\n                \"p271\", \"p272\", \"p273\", \"p274\", \"p275\", \"p276\", \"p277\", \"p278\", \"p279\", \"p280\",\n                \"p281\", \"p282\", \"p283\", \"p284\", \"p285\", \"p286\", \"p287\", \"p288\", \"p289\", \"p290\",\n                \"p291\", \"p292\", \"p293\", \"p294\", \"p295\", \"p296\", \"p297\", \"p298\", \"p299\", \"p300\",\n                \"p301\", \"p302\", \"p303\", \"p304\", \"p305\", \"p306\", \"p307\", \"p308\", \"p309\", \"p310\",\n                \"p311\", \"p312\", \"p313\", \"p314\", \"p315\", \"p316\", \"p317\", \"p318\", \"p319\", \"p320\",\n                \"p321\", \"p322\", \"p323\", \"p324\", \"p325\", \"p326\", \"p327\", \"p328\", \"p329\", \"p330\",\n                \"p331\", \"p332\", \"p333\", \"p334\", \"p335\", \"p336\", \"p337\", \"p338\", \"p339\", \"p340\",\n                \"p341\", \"p342\", \"p343\", \"p344\", \"p345\", \"p346\", \"p347\", \"p348\", \"p349\", \"p350\",\n                \"p351\", \"p352\", \"p353\", \"p354\", \"p355\", \"p356\", \"p357\", \"p358\", \"p359\", \"p360\",\n                \"p361\", \"p362\", \"p363\", \"p364\", \"p365\", \"p366\", \"p367\", \"p368\", \"p369\", \"p370\",\n                \"p371\", \"p372\", \"p373\", \"p374\", \"p375\", \"p376\", \"p377\", \"p378\", \"p379\", \"p380\",\n                \"p381\", \"p382\", \"p383\", \"p384\", \"p385\", \"p386\", \"p387\", \"p388\", \"p389\", \"p390\",\n                \"p391\", \"p392\", \"p393\", \"p394\", \"p395\", \"p396\", \"p397\", \"p398\", \"p399\", \"p400\",\n                \"p401\", \"p402\", \"p403\", \"p404\", \"p405\", \"p406\", \"p407\", \"p408\", \"p409\", \"p410\",\n                \"p411\", \"p412\", \"p413\", \"p414\", \"p415\", \"p416\", \"p417\", \"p418\", \"p419\", \"p420\",\n                \"p421\", \"p422\", \"p423\", \"p424\", \"p425\", \"p426\", \"p427\", \"p428\", \"p429\", \"p430\",\n                \"p431\", \"p432\", \"p433\", \"p434\", \"p435\", \"p436\", \"p437\", \"p438\", \"p439\", \"p440\",\n                \"p441\", \"p442\", \"p443\", \"p444\", \"p445\", \"p446\", \"p447\", \"p448\", \"p449\", \"p450\",\n                \"p451\", \"p452\", \"p453\", \"p454\", \"p455\", \"p456\", \"p457\", \"p458\", \"p459\", \"p460\",\n                \"p461\", \"p462\", \"p463\", \"p464\", \"p465\", \"p466\", \"p467\", \"p468\", \"p469\", \"p470\",\n                \"p471\", \"p472\", \"p473\", \"p474\", \"p475\", \"p476\", \"p477\", \"p478\", \"p479\", \"p480\",\n                \"p481\", \"p482\", \"p483\", \"p484\", \"p485\", \"p486\", \"p487\", \"p488\", \"p489\", \"p490\",\n                \"p491\", \"p492\", \"p493\", \"p494\", \"p495\", \"p496\", \"p497\", \"p498\", \"p499\", \"p500\" ];\n\n                let testArr = [];\n                for (let a = 0; a <= 500; a++) {\n                    testArr[a] = ('p' + a);\n                }\n\n                arrayEquals(testArr, finalArr)\n            \"#}),\n    ]);\n}\n\n#[test]\nfn array_pop_benchmark() {\n    run_test_actions([\n        TestAction::run_harness(),\n        TestAction::assert(indoc! {r#\"\n            let testArray = [83, 93, 27, 29, 2828, 234, 23, 56, 32, 56, 67, 77, 32,\n            45, 93, 17, 28, 83, 62, 99, 36, 28, 93, 27, 29, 2828,\n            234, 23, 56, 32, 56, 67, 77, 32, 45, 93, 17, 28, 83, 62,\n            99, 36, 28, 93, 27, 29, 2828, 234, 23, 56, 32, 56, 67,\n            77, 32, 45, 93, 17, 28, 83, 62, 99, 36, 28, 93, 27, 29,\n            2828, 234, 23, 56, 32, 56, 67, 77, 32, 45, 93, 17, 28,\n            83, 62, 99, 36, 28, 93, 27, 29, 2828, 234, 23, 56, 32,\n            56, 67, 77, 32, 45, 93, 17, 28, 83, 62, 99, 36, 28, 93,\n            27, 29, 2828, 234, 23, 56, 32, 56, 67, 77, 32, 45, 93,\n            17, 28, 83, 62, 99, 36, 28, 93, 27, 29, 2828, 234, 23,\n            56, 32, 56, 67, 77, 32, 45, 93, 17, 28, 83, 62, 99, 36,\n            28, 93, 27, 29, 2828, 234, 23, 56, 32, 56, 67, 77, 32,\n            45, 93, 17, 28, 83, 62, 99, 36, 28, 93, 27, 29, 2828, 234,\n            23, 56, 32, 56, 67, 77, 32, 45, 93, 17, 28, 83, 62, 99,\n            36, 28, 93, 27, 29, 2828, 234, 23, 56, 32, 56, 67, 77, 32,\n            45, 93, 17, 28, 83, 62, 99, 36, 28];\n\n            while (testArray.length > 0) {\n                testArray.pop();\n            }\n\n            arrayEquals(testArray, [])\n            \"#}),\n    ]);\n}\n\n#[test]\nfn number_object_access_benchmark() {\n    run_test_actions([TestAction::assert_eq(\n        indoc! {r#\"\n            new Number(\n                new Number(\n                    new Number(\n                        new Number(100).valueOf() - 10.5\n                    ).valueOf() + 100\n                ).valueOf() * 1.6\n            ).valueOf()\n        \"#},\n        303.2,\n    )]);\n}\n\n#[test]\nfn multiline_str_concat() {\n    run_test_actions([TestAction::assert_eq(\n        indoc! {r#\"\n            let a = 'hello ' +\n                    'world';\n            a\n        \"#},\n        js_str!(\"hello world\"),\n    )]);\n}\n\n#[test]\nfn result_of_empty_block() {\n    run_test_actions([TestAction::assert_eq(\"{}\", JsValue::undefined())]);\n}\n\n#[test]\nfn undefined_constant() {\n    run_test_actions([TestAction::assert_eq(\"undefined\", JsValue::undefined())]);\n}\n\n#[test]\nfn identifier_op() {\n    run_test_actions([TestAction::assert_native_error(\n        \"break = 1\",\n        JsNativeErrorKind::Syntax,\n        r#\"expected token 'identifier', got '=' in identifier parsing at line 1, col 7\"#,\n    )]);\n}\n\n#[test]\nfn strict_mode_octal() {\n    // Checks as per https://tc39.es/ecma262/#sec-literals-numeric-literals that 0 prefix\n    // octal number literal syntax is a syntax error in strict mode.\n    run_test_actions([TestAction::assert_native_error(\n        indoc! {r#\"\n            'use strict';\n            var n = 023;\n        \"#},\n        JsNativeErrorKind::Syntax,\n        \"implicit octal literals are not allowed in strict mode at line 2, col 9\",\n    )]);\n}\n\n#[test]\nfn strict_mode_with() {\n    // Checks as per https://tc39.es/ecma262/#sec-with-statement-static-semantics-early-errors\n    // that a with statement is an error in strict mode code.\n    run_test_actions([TestAction::assert_native_error(\n        indoc! {r#\"\n            'use strict';\n            function f(x, o) {\n                with (o) {\n                    console.log(x);\n                }\n            }\n        \"#},\n        JsNativeErrorKind::Syntax,\n        \"with statement not allowed in strict mode at line 3, col 5\",\n    )]);\n}\n\n#[test]\nfn strict_mode_reserved_name() {\n    // Checks that usage of a reserved keyword for an identifier name is\n    // an error in strict mode code as per https://tc39.es/ecma262/#sec-strict-mode-of-ecmascript.\n\n    let cases = [\n        (\n            \"var implements = 10;\",\n            \"unexpected token 'implements', strict reserved word cannot be an identifier at line 1, col 19\",\n        ),\n        (\n            \"var interface = 10;\",\n            \"unexpected token 'interface', strict reserved word cannot be an identifier at line 1, col 19\",\n        ),\n        (\n            \"var package = 10;\",\n            \"unexpected token 'package', strict reserved word cannot be an identifier at line 1, col 19\",\n        ),\n        (\n            \"var private = 10;\",\n            \"unexpected token 'private', strict reserved word cannot be an identifier at line 1, col 19\",\n        ),\n        (\n            \"var protected = 10;\",\n            \"unexpected token 'protected', strict reserved word cannot be an identifier at line 1, col 19\",\n        ),\n        (\n            \"var public = 10;\",\n            \"unexpected token 'public', strict reserved word cannot be an identifier at line 1, col 19\",\n        ),\n        (\n            \"var static = 10;\",\n            \"unexpected token 'static', strict reserved word cannot be an identifier at line 1, col 19\",\n        ),\n        (\n            \"var eval = 10;\",\n            \"binding identifier `eval` not allowed in strict mode at line 1, col 19\",\n        ),\n        (\n            \"var arguments = 10;\",\n            \"binding identifier `arguments` not allowed in strict mode at line 1, col 19\",\n        ),\n        (\n            \"var let = 10;\",\n            \"unexpected token 'let', strict reserved word cannot be an identifier at line 1, col 19\",\n        ),\n        (\n            \"var yield = 10;\",\n            \"unexpected token 'yield', strict reserved word cannot be an identifier at line 1, col 19\",\n        ),\n    ];\n\n    run_test_actions(cases.into_iter().map(|(case, msg)| {\n        TestAction::assert_native_error(\n            format!(\"'use strict'; {case}\"),\n            JsNativeErrorKind::Syntax,\n            msg,\n        )\n    }));\n}\n\n#[test]\nfn empty_statement() {\n    run_test_actions([TestAction::assert_eq(\n        indoc! {r#\"\n            ;;;let a = 10;;\n            if(a) ;\n            a\n        \"#},\n        10,\n    )]);\n}\n\n#[test]\nfn tagged_template() {\n    run_test_actions([\n        TestAction::run_harness(),\n        TestAction::assert(indoc! {r#\"\n                function tag(t, ...args) {\n                    let a = []\n                    a = a.concat([t[0], t[1], t[2]]);\n                    a = a.concat([t.raw[0], t.raw[1], t.raw[2]]);\n                    a = a.concat([args[0], args[1]]);\n                    return a\n                }\n                let a = 10;\n\n                arrayEquals(\n                    tag`result: ${a} \\x26 ${a+10}`,\n                    [ \"result: \", \" & \", \"\", \"result: \", \" \\\\x26 \", \"\", 10, 20 ]\n                )\n            \"#}),\n    ]);\n}\n\n#[test]\nfn template_literal() {\n    run_test_actions([TestAction::assert_eq(\n        indoc! {r#\"\n            let a = 10;\n            `result: ${a} and ${a+10}`;\n        \"#},\n        js_str!(\"result: 10 and 20\"),\n    )]);\n}\n\n#[test]\nfn null_bool_in_object_pattern() {\n    run_test_actions([\n        TestAction::run(indoc! {r#\"\n            let obj = {\n                null: 0,\n                true: 10,\n                false: 100\n            };\n\n            let { null: a, true: b, false: c } = obj;\n        \"#}),\n        TestAction::assert_eq(\"a\", 0),\n        TestAction::assert_eq(\"b\", 10),\n        TestAction::assert_eq(\"c\", 100),\n    ]);\n}\n\n// https://github.com/boa-dev/boa/pull/4372\n#[test]\nfn fix_index_access_in_finally_jump_table_in_loop() {\n    run_test_actions([\n        TestAction::run(indoc! {r#\"\n        do {\n            try {\n                throw 0; // can be any values\n\n                // This is unreachable, but it forces creation of HandleFinally jump record.\n                break;\n            } catch {\n                // It is important that we enter the finally with rethrow flag set to false,\n                // we do this by just catching the value and ignoring it.\n            } finally {\n                // The index register is released right after allocation, so we can override it\n                // by allocating the next register through an expression statement.\n                \"this is used as the index, which triggers unreachable code in JumpTable\"\n            }\n        } while (0);\n    \"#}),\n        TestAction::run(indoc! {r#\"\n        do {\n            try {\n                throw 0; // can be any values\n\n                // This is unreachable, but it forces creation of HandleFinally jump record.\n                continue;\n            } catch {\n                // It is important that we enter the finally with rethrow flag set to false,\n                // we do this by just catching the value and ignoring it.\n            } finally {\n                // The index register is released right after allocation, so we can override it\n                // by allocating the next register through an expression statement.\n                \"this is used as the index, which triggers unreachable code in JumpTable\"\n            }\n        } while (0);\n    \"#}),\n    ]);\n}\n\n// https://github.com/boa-dev/boa/pull/4372\n#[test]\nfn fix_index_access_in_finally_jump_table_in_function() {\n    run_test_actions([TestAction::run(indoc! {r#\"\n        (() => {\n            try {\n                throw 0; // can be any values\n\n                // This is unreachable, but it forces creation of HandleFinally jump record.\n                return;\n            } catch {\n                // It is important that we enter the finally with rethrow flag set to false,\n                // we do this by just catching the value and ignoring it.\n            } finally {\n                // The index register is released right after allocation, so we can override it\n                // by allocating the next register through an expression statement.\n                \"this is used as the index, which triggers unreachable code in JumpTable\"\n            }\n        })()\n    \"#})]);\n}\n"
  },
  {
    "path": "core/engine/src/tests/operators.rs",
    "content": "use crate::{JsNativeErrorKind, JsValue, TestAction, run_test_actions};\nuse boa_macros::js_str;\nuse indoc::indoc;\n\n#[test]\nfn property_accessor_member_expression_dot_notation_on_string_literal() {\n    run_test_actions([TestAction::assert_eq(\n        \"typeof 'asd'.matchAll\",\n        js_str!(\"function\"),\n    )]);\n}\n\n#[test]\nfn property_accessor_member_expression_bracket_notation_on_string_literal() {\n    run_test_actions([TestAction::assert_eq(\n        \"typeof 'asd'['matchAll']\",\n        js_str!(\"function\"),\n    )]);\n}\n\n#[test]\nfn short_circuit_evaluation() {\n    run_test_actions([\n        // OR operation\n        TestAction::assert(\"true || true\"),\n        TestAction::assert(\"true || false\"),\n        TestAction::assert(\"false || true\"),\n        TestAction::assert(\"!(false || false)\"),\n        // short circuiting OR.\n        TestAction::assert_eq(\n            indoc! {r#\"\n                function add_one_a(counter) {\n                    counter.value += 1;\n                    return true;\n                }\n                let counter_a = { value: 0 };\n                add_one_a(counter_a) || add_one_a(counter_a);\n                counter_a.value\n            \"#},\n            1,\n        ),\n        TestAction::assert_eq(\n            indoc! {r#\"\n                function add_one_b(counter) {\n                    counter.value += 1;\n                    return false;\n                }\n                let counter_b = { value: 0 };\n                add_one_b(counter_b) || add_one_b(counter_b);\n                counter_b.value\n            \"#},\n            2,\n        ),\n        // AND operation\n        TestAction::assert(\"true && true\"),\n        TestAction::assert(\"!(true && false)\"),\n        TestAction::assert(\"!(false && true)\"),\n        TestAction::assert(\"!(false && false)\"),\n        // short circuiting AND\n        TestAction::assert_eq(\n            indoc! {r#\"\n                function add_one_c(counter) {\n                    counter.value += 1;\n                    return true;\n                }\n                let counter_c = { value: 0 };\n                add_one_c(counter_c) && add_one_c(counter_c);\n                counter_c.value\n            \"#},\n            2,\n        ),\n        TestAction::assert_eq(\n            indoc! {r#\"\n                function add_one_d(counter) {\n                    counter.value += 1;\n                    return false;\n                }\n                let counter_d = { value: 0 };\n                add_one_d(counter_d) && add_one_d(counter_d);\n                counter_d.value\n            \"#},\n            1,\n        ),\n    ]);\n}\n\n#[test]\nfn tilde_operator() {\n    run_test_actions([\n        // float\n        TestAction::assert_eq(\"~(-1.2)\", 0),\n        // numeric\n        TestAction::assert_eq(\"~1789\", -1790),\n        // nan\n        TestAction::assert_eq(\"~NaN\", -1),\n        // object\n        TestAction::assert_eq(\"~{}\", -1),\n        // boolean true\n        TestAction::assert_eq(\"~true\", -2),\n        // boolean false\n        TestAction::assert_eq(\"~false\", -1),\n    ]);\n}\n\n#[test]\nfn assign_operator_precedence() {\n    run_test_actions([TestAction::assert_eq(\n        indoc! {r#\"\n            let a = 1;\n            a = a + 1;\n            a\n        \"#},\n        2,\n    )]);\n}\n\n#[test]\nfn unary_pre() {\n    run_test_actions([\n        TestAction::assert_eq(\"{ let a = 5; ++a; a }\", 6),\n        TestAction::assert_eq(\"{ let b = 5; --b; b }\", 4),\n        TestAction::assert_eq(\"{ const c = { a: 5 }; ++c.a; c['a'] }\", 6),\n        TestAction::assert_eq(\"{ const d = { a: 5 }; --d['a']; d.a }\", 4),\n        TestAction::assert_eq(\"{ let e = 5; ++e }\", 6),\n        TestAction::assert_eq(\"{ let f = 5; --f }\", 4),\n        TestAction::assert_eq(\"{ let g = 2147483647; ++g }\", 2_147_483_648_i64),\n        TestAction::assert_eq(\"{ let h = -2147483648; --h }\", -2_147_483_649_i64),\n        TestAction::assert_eq(\n            indoc! {r#\"\n                let i = {[Symbol.toPrimitive]() { return 123; }};\n                ++i\n            \"#},\n            124,\n        ),\n        TestAction::assert_eq(\n            indoc! {r#\"\n                let j = {[Symbol.toPrimitive]() { return 123; }};\n                ++j\n            \"#},\n            124,\n        ),\n    ]);\n}\n\n#[test]\nfn invalid_unary_access() {\n    run_test_actions([\n        TestAction::assert_native_error(\n            \"++[]\",\n            JsNativeErrorKind::Syntax,\n            \"Invalid left-hand side in assignment at line 1, col 1\",\n        ),\n        TestAction::assert_native_error(\n            \"[]++\",\n            JsNativeErrorKind::Syntax,\n            \"Invalid left-hand side in assignment at line 1, col 3\",\n        ),\n        TestAction::assert_native_error(\n            \"--[]\",\n            JsNativeErrorKind::Syntax,\n            \"Invalid left-hand side in assignment at line 1, col 1\",\n        ),\n        TestAction::assert_native_error(\n            \"[]--\",\n            JsNativeErrorKind::Syntax,\n            \"Invalid left-hand side in assignment at line 1, col 3\",\n        ),\n    ]);\n}\n\n#[test]\nfn unary_operations_on_this() {\n    // https://tc39.es/ecma262/#sec-assignment-operators-static-semantics-early-errors\n    run_test_actions([\n        TestAction::assert_native_error(\n            \"++this\",\n            JsNativeErrorKind::Syntax,\n            \"Invalid left-hand side in assignment at line 1, col 1\",\n        ),\n        TestAction::assert_native_error(\n            \"--this\",\n            JsNativeErrorKind::Syntax,\n            \"Invalid left-hand side in assignment at line 1, col 1\",\n        ),\n        TestAction::assert_native_error(\n            \"this++\",\n            JsNativeErrorKind::Syntax,\n            \"Invalid left-hand side in assignment at line 1, col 5\",\n        ),\n        TestAction::assert_native_error(\n            \"this--\",\n            JsNativeErrorKind::Syntax,\n            \"Invalid left-hand side in assignment at line 1, col 5\",\n        ),\n    ]);\n}\n\n#[test]\nfn typeofs() {\n    run_test_actions([\n        TestAction::assert_eq(\"typeof String()\", js_str!(\"string\")),\n        TestAction::assert_eq(\"typeof 5\", js_str!(\"number\")),\n        TestAction::assert_eq(\"typeof 0.5\", js_str!(\"number\")),\n        TestAction::assert_eq(\"typeof undefined\", js_str!(\"undefined\")),\n        TestAction::assert_eq(\"typeof true\", js_str!(\"boolean\")),\n        TestAction::assert_eq(\"typeof null\", js_str!(\"object\")),\n        TestAction::assert_eq(\"typeof {}\", js_str!(\"object\")),\n        TestAction::assert_eq(\"typeof Symbol()\", js_str!(\"symbol\")),\n        TestAction::assert_eq(\"typeof function(){}\", js_str!(\"function\")),\n    ]);\n}\n\n#[test]\nfn typeof_edge_cases() {\n    run_test_actions([\n        TestAction::assert_eq(\"typeof notDeclared\", js_str!(\"undefined\")),\n        TestAction::assert_eq(\n            indoc! {r#\"\n                function f() {\n                    return typeof x;\n                    var x = 1;\n                }\n                f();\n            \"#},\n            js_str!(\"undefined\"),\n        ),\n        TestAction::assert_native_error(\n            indoc! {r#\"\n                function f() {\n                    return typeof x;\n                    let x = 1;\n                }\n                f();\n            \"#},\n            JsNativeErrorKind::Reference,\n            \"access of uninitialized binding\",\n        ),\n        TestAction::assert_native_error(\n            indoc! {r#\"\n                function f() {\n                    return typeof x;\n                    const x = 1;\n                }\n                f();\n            \"#},\n            JsNativeErrorKind::Reference,\n            \"access of uninitialized binding\",\n        ),\n    ]);\n}\n\n#[test]\nfn unary_post() {\n    run_test_actions([\n        TestAction::assert_eq(\"{ let a = 5; a++; a }\", 6),\n        TestAction::assert_eq(\"{ let b = 5; b--; b }\", 4),\n        TestAction::assert_eq(\"{ const c = { a: 5 }; c.a++; c['a'] }\", 6),\n        TestAction::assert_eq(\"{ const d = { a: 5 }; d['a']--; d.a }\", 4),\n        TestAction::assert_eq(\"{ let e = 5; e++ }\", 5),\n        TestAction::assert_eq(\"{ let f = 5; f-- }\", 5),\n        TestAction::assert_eq(\"{ let g = 2147483647; g++; g }\", 2_147_483_648_i64),\n        TestAction::assert_eq(\"{ let h = -2147483648; h--; h }\", -2_147_483_649_i64),\n        TestAction::assert_eq(\n            indoc! {r#\"\n                let i = {[Symbol.toPrimitive]() { return 123; }};\n                i++\n            \"#},\n            123,\n        ),\n        TestAction::assert_eq(\n            indoc! {r#\"\n                let j = {[Symbol.toPrimitive]() { return 123; }};\n                j--\n            \"#},\n            123,\n        ),\n    ]);\n}\n\n#[test]\nfn unary_void() {\n    run_test_actions([\n        TestAction::assert_eq(\"{ const a = 0; void a }\", JsValue::undefined()),\n        TestAction::assert_eq(\n            indoc! {r#\"\n                let a = 0;\n                const test = () => a = 42;\n                const b = void test() + '';\n                a + b\n            \"#},\n            js_str!(\"42undefined\"),\n        ),\n    ]);\n}\n\n#[test]\nfn unary_delete() {\n    run_test_actions([\n        TestAction::assert(\"{ var a = 5; !(delete a) && a === 5 }\"),\n        TestAction::assert(\"{ const a = { b: 5 }; delete a.b && a.b === undefined }\"),\n        TestAction::assert(\"{ const a = { b: 5 }; delete a.c && a.b === 5 }\"),\n        TestAction::assert(\"{ const a = { b: 5 }; delete a['b'] && a.b === undefined }\"),\n        TestAction::assert(\"{ const a = { b: 5 }; !(delete a) }\"),\n        TestAction::assert(\"delete []\"),\n        TestAction::assert(\"delete function(){}\"),\n        TestAction::assert(\"delete delete delete 1\"),\n    ]);\n}\n\n#[test]\nfn delete_optional_chaining() {\n    run_test_actions([\n        // Basic: delete o?.prop actually removes the property descriptor.\n        TestAction::assert(indoc! {r#\"\n            var o = { a: 1 };\n            delete o?.a === true\n                && Object.getOwnPropertyDescriptor(o, \"a\") === undefined\n        \"#}),\n        // Chain: delete o?.a.b removes nested property descriptor, keeps sibling.\n        TestAction::assert(indoc! {r#\"\n            var o = { a: { b: 1, c: 2 } };\n            delete o?.a.b === true\n                && Object.getOwnPropertyDescriptor(o.a, \"b\") === undefined\n                && o.a.c === 2\n        \"#}),\n        // Multiple optional: delete o?.a?.b\n        TestAction::assert(indoc! {r#\"\n            var o = { a: { b: 1 } };\n            delete o?.a?.b === true\n                && Object.getOwnPropertyDescriptor(o.a, \"b\") === undefined\n        \"#}),\n        // Null base short-circuits to true without error.\n        TestAction::assert(\"delete null?.a === true\"),\n        // Undefined base short-circuits to true without error.\n        TestAction::assert(\"delete undefined?.a === true\"),\n        // Null in the middle with ?. short-circuits to true.\n        TestAction::assert(indoc! {r#\"\n            var o = { a: null };\n            delete o?.a?.b === true\n        \"#}),\n        // Computed property: delete o?.[\"key\"]\n        TestAction::assert(indoc! {r#\"\n            var o = { x: 1 };\n            delete o?.[\"x\"] === true\n                && Object.getOwnPropertyDescriptor(o, \"x\") === undefined\n        \"#}),\n        // Computed property in chain: delete o?.a[\"b\"]\n        TestAction::assert(indoc! {r#\"\n            var o = { a: { b: 1 } };\n            delete o?.a[\"b\"] === true\n                && Object.getOwnPropertyDescriptor(o.a, \"b\") === undefined\n        \"#}),\n        // Non-existent property returns true.\n        TestAction::assert(indoc! {r#\"\n            var o = { a: 1 };\n            delete o?.nonExistent === true\n                && Object.getOwnPropertyDescriptor(o, \"a\") !== undefined\n        \"#}),\n        // Delete from function return value.\n        TestAction::assert(indoc! {r#\"\n            function f() { return { x: 1 }; }\n            delete f()?.x === true\n        \"#}),\n        // Deeply nested optional chain.\n        TestAction::assert(indoc! {r#\"\n            var o = { a: { b: { c: { d: 1 } } } };\n            delete o?.a?.b?.c?.d === true\n                && Object.getOwnPropertyDescriptor(o.a.b.c, \"d\") === undefined\n        \"#}),\n    ]);\n}\n\n#[test]\nfn comma_operator() {\n    run_test_actions([\n        TestAction::assert_eq(\n            indoc! {r#\"\n                var a, b;\n                b = 10;\n                a = (b++, b);\n                a\n            \"#},\n            11,\n        ),\n        TestAction::assert_eq(\n            indoc! {r#\"\n                var a, b;\n                b = 10;\n                a = (b += 5, b /= 3, b - 3);\n                a\n            \"#},\n            2,\n        ),\n    ]);\n}\n\n#[test]\nfn assignment_to_non_assignable() {\n    // Relates to the behaviour described at\n    // https://tc39.es/ecma262/#sec-assignment-operators-static-semantics-early-errors\n    // Tests all assignment operators as per [spec] and [mdn]\n    //\n    // [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Expressions_and_Operators#Assignment\n    // [spec]: https://tc39.es/ecma262/#prod-AssignmentOperator\n\n    run_test_actions(\n        [\n            \"3 -= 5\", \"3 *= 5\", \"3 /= 5\", \"3 %= 5\", \"3 &= 5\", \"3 ^= 5\", \"3 |= 5\", \"3 += 5\", \"3 = 5\",\n        ]\n        .into_iter()\n        .map(|src| {\n            TestAction::assert_native_error(\n                src,\n                JsNativeErrorKind::Syntax,\n                \"Invalid left-hand side in assignment at line 1, col 3\",\n            )\n        }),\n    );\n}\n\n#[test]\nfn assignment_to_non_assignable_ctd() {\n    run_test_actions(\n        [\n            \"(()=>{})() -= 5\",\n            \"(()=>{})() *= 5\",\n            \"(()=>{})() /= 5\",\n            \"(()=>{})() %= 5\",\n            \"(()=>{})() &= 5\",\n            \"(()=>{})() ^= 5\",\n            \"(()=>{})() |= 5\",\n            \"(()=>{})() += 5\",\n            \"(()=>{})() = 5\",\n        ]\n        .into_iter()\n        .map(|src| {\n            TestAction::assert_native_error(\n                src,\n                JsNativeErrorKind::Syntax,\n                \"Invalid left-hand side in assignment at line 1, col 12\",\n            )\n        }),\n    );\n}\n\n#[test]\nfn multicharacter_assignment_to_non_assignable() {\n    // Relates to the behaviour described at\n    // https://tc39.es/ecma262/#sec-assignment-operators-static-semantics-early-errors\n    run_test_actions([\"3 **= 5\", \"3 <<= 5\", \"3 >>= 5\"].into_iter().map(|src| {\n        TestAction::assert_native_error(\n            src,\n            JsNativeErrorKind::Syntax,\n            \"Invalid left-hand side in assignment at line 1, col 3\",\n        )\n    }));\n}\n\n#[test]\nfn multicharacter_assignment_to_non_assignable_ctd() {\n    run_test_actions(\n        [\"(()=>{})() **= 5\", \"(()=>{})() <<= 5\", \"(()=>{})() >>= 5\"]\n            .into_iter()\n            .map(|src| {\n                TestAction::assert_native_error(\n                    src,\n                    JsNativeErrorKind::Syntax,\n                    \"Invalid left-hand side in assignment at line 1, col 12\",\n                )\n            }),\n    );\n}\n\n#[test]\nfn multicharacter_bitwise_assignment_to_non_assignable() {\n    run_test_actions(\n        [\"3 >>>= 5\", \"3 &&= 5\", \"3 ||= 5\", \"3 ??= 5\"]\n            .into_iter()\n            .map(|src| {\n                TestAction::assert_native_error(\n                    src,\n                    JsNativeErrorKind::Syntax,\n                    \"Invalid left-hand side in assignment at line 1, col 3\",\n                )\n            }),\n    );\n}\n\n#[test]\nfn multicharacter_bitwise_assignment_to_non_assignable_ctd() {\n    run_test_actions(\n        [\n            \"(()=>{})() >>>= 5\",\n            \"(()=>{})() &&= 5\",\n            \"(()=>{})() ||= 5\",\n            \"(()=>{})() ??= 5\",\n        ]\n        .into_iter()\n        .map(|src| {\n            TestAction::assert_native_error(\n                src,\n                JsNativeErrorKind::Syntax,\n                \"Invalid left-hand side in assignment at line 1, col 12\",\n            )\n        }),\n    );\n}\n\n#[test]\nfn assign_to_array_decl() {\n    run_test_actions([\n        TestAction::assert_native_error(\n            \"[1] = [2]\",\n            JsNativeErrorKind::Syntax,\n            \"Invalid left-hand side in assignment at line 1, col 5\",\n        ),\n        TestAction::assert_native_error(\n            \"[3, 5] = [7, 8]\",\n            JsNativeErrorKind::Syntax,\n            \"Invalid left-hand side in assignment at line 1, col 8\",\n        ),\n        TestAction::assert_native_error(\n            \"[6, 8] = [2]\",\n            JsNativeErrorKind::Syntax,\n            \"Invalid left-hand side in assignment at line 1, col 8\",\n        ),\n        TestAction::assert_native_error(\n            \"[6] = [2, 9]\",\n            JsNativeErrorKind::Syntax,\n            \"Invalid left-hand side in assignment at line 1, col 5\",\n        ),\n    ]);\n}\n\n#[test]\nfn assign_to_object_decl() {\n    run_test_actions([TestAction::assert_native_error(\n        \"{a: 3} = {a: 5};\",\n        JsNativeErrorKind::Syntax,\n        \"unexpected token '=', primary expression at line 1, col 8\",\n    )]);\n}\n\n#[test]\nfn assignmentoperator_lhs_not_defined() {\n    run_test_actions([TestAction::assert_native_error(\n        \"a += 5\",\n        JsNativeErrorKind::Reference,\n        \"a is not defined\",\n    )]);\n}\n\n#[test]\nfn assignmentoperator_rhs_throws_error() {\n    run_test_actions([TestAction::assert_native_error(\n        \"let a; a += b\",\n        JsNativeErrorKind::Reference,\n        \"b is not defined\",\n    )]);\n}\n\n#[test]\nfn instanceofoperator_rhs_not_object() {\n    run_test_actions([TestAction::assert_native_error(\n        \"let s = new String(); s instanceof 1\",\n        JsNativeErrorKind::Type,\n        \"right-hand side of 'instanceof' should be an object, got `number`\",\n    )]);\n}\n\n#[test]\nfn instanceofoperator_rhs_not_callable() {\n    run_test_actions([TestAction::assert_native_error(\n        \"let s = new String(); s instanceof {}\",\n        JsNativeErrorKind::Type,\n        \"right-hand side of 'instanceof' is not callable\",\n    )]);\n}\n\n#[test]\nfn logical_nullish_assignment() {\n    run_test_actions([\n        TestAction::assert_eq(\"{ let a = undefined; a ??= 10; a }\", 10),\n        TestAction::assert_eq(\"{ let a = 20; a ??= 10; a }\", 20),\n    ]);\n}\n\n#[test]\nfn logical_assignment() {\n    run_test_actions([\n        TestAction::assert(\"{ let a = false; a &&= 10; !a }\"),\n        TestAction::assert_eq(\"{ let a = 20; a &&= 10; a }\", 10),\n        TestAction::assert_eq(\"{ let a = null; a ||= 10; a }\", 10),\n        TestAction::assert_eq(\"{ let a = 20; a ||= 10; a }\", 20),\n    ]);\n}\n\n#[test]\nfn conditional_op() {\n    run_test_actions([TestAction::assert_eq(\"1 === 2 ? 'a' : 'b'\", js_str!(\"b\"))]);\n}\n\n#[test]\nfn delete_variable_in_strict() {\n    // Checks as per https://tc39.es/ecma262/#sec-delete-operator-static-semantics-early-errors\n    // that delete on a variable name is an error in strict mode code.\n    run_test_actions([TestAction::assert_native_error(\n        indoc! {r#\"\n            'use strict';\n            let x = 10;\n            delete x;\n        \"#},\n        JsNativeErrorKind::Syntax,\n        \"cannot delete variables in strict mode at line 3, col 1\",\n    )]);\n}\n\n#[test]\nfn delete_non_configurable() {\n    run_test_actions([TestAction::assert_native_error(\n        \"'use strict'; delete Boolean.prototype\",\n        JsNativeErrorKind::Type,\n        \"Cannot delete property\",\n    )]);\n}\n\n#[test]\nfn delete_non_configurable_in_function() {\n    run_test_actions([TestAction::assert_native_error(\n        indoc! {r#\"\n            function t() {\n                'use strict';\n                delete Boolean.prototype;\n            }\n            t()\n        \"#},\n        JsNativeErrorKind::Type,\n        \"Cannot delete property\",\n    )]);\n}\n\n#[test]\nfn delete_after_strict_function() {\n    run_test_actions([TestAction::assert_eq(\n        indoc! {r#\"\n            function t() {\n                'use strict';\n            }\n            t()\n            delete Boolean.prototype;\n        \"#},\n        false,\n    )]);\n}\n\n#[test]\nfn delete_in_function_global_strict() {\n    run_test_actions([TestAction::assert_native_error(\n        indoc! {r#\"\n            'use strict'\n            function a(){\n                delete Boolean.prototype;\n            }\n            a();\n        \"#},\n        JsNativeErrorKind::Type,\n        \"Cannot delete property\",\n    )]);\n}\n\n#[test]\nfn delete_in_function_in_strict_function() {\n    run_test_actions([TestAction::assert_eq(\n        indoc! {r#\"\n            function a(){\n                return delete Boolean.prototype;\n            }\n            function b(){\n                'use strict';\n                return a();\n            }\n            b();\n        \"#},\n        false,\n    )]);\n}\n\n#[test]\nfn delete_in_strict_function_returned() {\n    run_test_actions([TestAction::assert_native_error(\n        indoc! {r#\"\n            function a() {\n                'use strict';\n                return function () {\n                    delete Boolean.prototype;\n                }\n            }\n            a()();\n        \"#},\n        JsNativeErrorKind::Type,\n        \"Cannot delete property\",\n    )]);\n}\n\n#[test]\nfn ops_at_the_end() {\n    let msg = \"abrupt end\";\n\n    let mut actions = vec![TestAction::assert_eq(\"var a, b=3; a = b ++\", 3)];\n\n    let abrupt_op_sources = [\n        // there was a bug with different behavior with and without space at the end;\n        // so few lines are almost the same except for ending space\n        \"var a, b=3; a = b **\",\n        \"var a, b=3; a = b ** \",\n        \"var a, b=3; a = b *\",\n        \"var a, b=3; a = b * \",\n        \"var a, b=3; a /= b *\",\n        \"var a, b=3; a /= b * \",\n        \"var a, b=3; a = b /\",\n        \"var a, b=3; a = b / \",\n        \"var a, b=3; a = b +\",\n        \"var a, b=3; a = b -\",\n        \"var a, b=3; a = b ||\",\n        \"var a, b=3; a = b || \",\n        \"var a, b=3; a = b ==\",\n        \"var a, b=3; a = b ===\",\n    ];\n\n    for source in abrupt_op_sources {\n        actions.push(TestAction::assert_native_error(\n            source,\n            JsNativeErrorKind::Syntax,\n            msg,\n        ));\n    }\n\n    actions.push(TestAction::assert_eq(\"var a, b=3; a = b --\", 3));\n\n    run_test_actions(actions);\n}\n\n#[test]\nfn regex_slash_eq() {\n    run_test_actions([\n        TestAction::assert_eq(\"+/=/\", JsValue::nan()),\n        TestAction::assert_eq(\"var a = 5; /=/; a\", 5),\n        TestAction::assert_eq(\"x = () => /=/;\\n\\\"a=b\\\".match(x())[0]\", js_str!(\"=\")),\n    ]);\n}\n\nmod in_operator {\n    use super::*;\n\n    #[test]\n    fn property_in_object() {\n        run_test_actions([TestAction::assert(\"'a' in {a: 'x'}\")]);\n    }\n\n    #[test]\n    fn property_in_property_chain() {\n        run_test_actions([TestAction::assert(\"'toString' in {}\")]);\n    }\n\n    #[test]\n    fn property_not_in_object() {\n        run_test_actions([TestAction::assert(\"!('b' in {a: 'a'})\")]);\n    }\n\n    #[test]\n    fn number_in_array() {\n        // Note: this is valid because the LHS is converted to a prop key with ToPropertyKey\n        // and arrays are just fancy objects like {'0': 'a'}\n        run_test_actions([TestAction::assert(\"0 in ['a']\")]);\n    }\n\n    #[test]\n    fn symbol_in_object() {\n        run_test_actions([TestAction::assert(indoc! {r#\"\n                var sym = Symbol('hi');\n                sym in { [sym]: 'hello' }\n            \"#})]);\n    }\n\n    #[test]\n    fn should_type_error_when_rhs_not_object() {\n        run_test_actions([TestAction::assert_native_error(\n            \"'fail' in undefined\",\n            JsNativeErrorKind::Type,\n            \"right-hand side of 'in' should be an object, got `undefined`\",\n        )]);\n    }\n}\n\n/// `NaN` comparison, both via numeric literals (fast path) and\n/// via `Number` object properties (slow path through `abstract_relation`).\n///\n#[test]\nfn nan_comparisons() {\n    run_test_actions([\n        TestAction::assert(\"!(NaN == NaN)\"),\n        TestAction::assert(\"NaN != NaN\"),\n        TestAction::assert(\"!(NaN === NaN)\"),\n        TestAction::assert(\"NaN !== NaN\"),\n        TestAction::assert(\"!(NaN < 1)\"),\n        TestAction::assert(\"!(NaN > 1)\"),\n        TestAction::assert(\"!(NaN <= 1)\"),\n        TestAction::assert(\"!(NaN >= 1)\"),\n        TestAction::assert(\"!(1 < NaN)\"),\n        TestAction::assert(\"!(1 > NaN)\"),\n        TestAction::assert(\"!(1 <= NaN)\"),\n        TestAction::assert(\"!(1 >= NaN)\"),\n        TestAction::assert(\"!(NaN < NaN)\"),\n        TestAction::assert(\"!(NaN > NaN)\"),\n        TestAction::assert(\"!(NaN <= NaN)\"),\n        TestAction::assert(\"!(NaN >= NaN)\"),\n        TestAction::assert(\"!(NaN < Infinity)\"),\n        TestAction::assert(\"!(NaN > -Infinity)\"),\n        TestAction::assert(\"!(NaN < -Infinity)\"),\n        TestAction::assert(\"!(NaN > Infinity)\"),\n        TestAction::assert(\"!(new Number(NaN) < 1)\"),\n        TestAction::assert(\"!(new Number(NaN) > 1)\"),\n        TestAction::assert(\"!(new Number(NaN) <= 1)\"),\n        TestAction::assert(\"!(new Number(NaN) >= 1)\"),\n        TestAction::assert(\"!(1 < new Number(NaN))\"),\n        TestAction::assert(\"!(1 > new Number(NaN))\"),\n        TestAction::assert(\"!(1 <= new Number(NaN))\"),\n        TestAction::assert(\"!(1 >= new Number(NaN))\"),\n        TestAction::assert(\"Number.isNaN(NaN)\"),\n        TestAction::assert(\"Number.isNaN(0/0)\"),\n        TestAction::assert(\"!Number.isNaN(undefined)\"),\n        TestAction::assert(\"!Number.isNaN('NaN')\"),\n    ]);\n}\n\n#[test]\nfn bigint_mixed_type_operations() {\n    const MIXED_MSG: &str = \"cannot mix BigInt and other types, use explicit conversions\";\n    run_test_actions([\n        TestAction::assert_native_error(\"1n + 1\", JsNativeErrorKind::Type, MIXED_MSG),\n        TestAction::assert_native_error(\"1n - 1\", JsNativeErrorKind::Type, MIXED_MSG),\n        TestAction::assert_native_error(\"1n * 2\", JsNativeErrorKind::Type, MIXED_MSG),\n        TestAction::assert_native_error(\"4n / 2\", JsNativeErrorKind::Type, MIXED_MSG),\n        TestAction::assert_native_error(\"5n % 2\", JsNativeErrorKind::Type, MIXED_MSG),\n        TestAction::assert_native_error(\"2n ** 3\", JsNativeErrorKind::Type, MIXED_MSG),\n        TestAction::assert_native_error(\"1 + 1n\", JsNativeErrorKind::Type, MIXED_MSG),\n        TestAction::assert_eq(\"'hello' + 1n\", js_str!(\"hello1\")),\n        TestAction::assert_eq(\"1n + 'hello'\", js_str!(\"1hello\")),\n        TestAction::assert_native_error(\n            indoc! {r#\"\n                let numObj = { valueOf() { return 42; }, toString() { return \"42\"; } };\n                1n + numObj\n            \"#},\n            JsNativeErrorKind::Type,\n            MIXED_MSG,\n        ),\n        TestAction::assert(\"1n < 2\"),\n        TestAction::assert(\"!(1n > 2)\"),\n        TestAction::assert(\"1n <= 1\"),\n        TestAction::assert(\"2n >= 2\"),\n        TestAction::assert(\"(2n + 3n) === 5n\"),\n        TestAction::assert(\"(10n - 4n) === 6n\"),\n        TestAction::assert(\"(3n * 4n) === 12n\"),\n        TestAction::assert(\"(10n / 3n) === 3n\"),\n        TestAction::assert(\"(10n % 3n) === 1n\"),\n        TestAction::assert(\"(2n ** 10n) === 1024n\"),\n    ]);\n}\n\n#[test]\nfn ushr_bigint_type_error() {\n    run_test_actions([\n        TestAction::assert_native_error(\n            \"1n >>> 0n\",\n            JsNativeErrorKind::Type,\n            \"BigInts have no unsigned right shift, use >> instead\",\n        ),\n        TestAction::assert_native_error(\n            \"255n >>> 4n\",\n            JsNativeErrorKind::Type,\n            \"BigInts have no unsigned right shift, use >> instead\",\n        ),\n        TestAction::assert(\"(8n >> 2n) === 2n\"),\n        TestAction::assert(\"(-1n >> 63n) === -1n\"),\n        TestAction::assert(\"(1n << 4n) === 16n\"),\n    ]);\n}\n\n#[test]\nfn instanceof_custom_has_instance() {\n    run_test_actions([\n        TestAction::assert(indoc! {r#\"\n            class AlwaysTrue {\n                static [Symbol.hasInstance](_instance) {\n                    return true;\n                }\n            }\n            ({} instanceof AlwaysTrue)\n        \"#}),\n        TestAction::assert(indoc! {r#\"\n            class AlwaysFalse {\n                static [Symbol.hasInstance](_instance) {\n                    return false;\n                }\n            }\n            let obj = new AlwaysFalse();\n            !(obj instanceof AlwaysFalse)\n        \"#}),\n        TestAction::assert(indoc! {r#\"\n            const EvenNumber = {\n                [Symbol.hasInstance](value) {\n                    return typeof value === 'number' && value % 2 === 0;\n                }\n            };\n            (2 instanceof EvenNumber) && !(3 instanceof EvenNumber)\n        \"#}),\n        TestAction::assert_native_error(\n            indoc! {r#\"\n                class Throws {\n                    static [Symbol.hasInstance](_instance) {\n                        throw new TypeError(\"hasInstance threw\");\n                    }\n                }\n                ({} instanceof Throws)\n            \"#},\n            JsNativeErrorKind::Type,\n            \"hasInstance threw\",\n        ),\n        TestAction::assert_native_error(\n            \"let s = new String(); s instanceof {}\",\n            JsNativeErrorKind::Type,\n            \"right-hand side of 'instanceof' is not callable\",\n        ),\n        TestAction::assert_native_error(\n            \"42 instanceof 'not-a-constructor'\",\n            JsNativeErrorKind::Type,\n            \"right-hand side of 'instanceof' should be an object, got `string`\",\n        ),\n        TestAction::assert(indoc! {r#\"\n            const Truthy = {\n                [Symbol.hasInstance]() { return 1; }\n            };\n            ({} instanceof Truthy)\n        \"#}),\n        TestAction::assert(indoc! {r#\"\n            const Falsy = {\n                [Symbol.hasInstance]() { return 0; }\n            };\n            !({} instanceof Falsy)\n        \"#}),\n    ]);\n}\n"
  },
  {
    "path": "core/engine/src/tests/promise.rs",
    "content": "use indoc::indoc;\n\nuse crate::{TestAction, run_test_actions};\n\n#[test]\n#[allow(clippy::redundant_closure_for_method_calls)]\nfn issue_2658() {\n    run_test_actions([\n        TestAction::run(indoc! {\n            r#\"\n                    let result1;\n                    let result2;\n                    async function* agf(a) {\n                        for await (m of a) {\n                            yield m;\n                        }\n                    }\n                    iterTwo = {\n                        [Symbol.asyncIterator]() {\n                            return this;\n                        },\n                        next() {\n                            return {\n                                value: 5,\n                                done: false,\n                            };\n                        }\n                    };\n                    const genTwo = agf(iterTwo);\n                    genTwo.next().then(v => { result1 = v; });\n                    genTwo.next().then(v => { result2 = v; });\n                \"#\n        }),\n        TestAction::inspect_context(|ctx| ctx.run_jobs().unwrap()),\n        TestAction::assert(\"!result1.done\"),\n        TestAction::assert_eq(\"result1.value\", 5),\n        TestAction::assert(\"!result2.done\"),\n        TestAction::assert_eq(\"result2.value\", 5),\n    ]);\n}\n"
  },
  {
    "path": "core/engine/src/tests/spread.rs",
    "content": "use crate::{JsNativeErrorKind, JsValue, TestAction, run_test_actions};\nuse boa_macros::js_str;\nuse indoc::indoc;\n\n#[test]\nfn object_spread() {\n    run_test_actions([\n        TestAction::run(indoc! {r#\"\n                var b = {x: -1, z: -3}\n                var a = {x: 1, y: 2, ...b};\n            \"#}),\n        TestAction::assert_eq(\"a.x\", -1),\n        TestAction::assert_eq(\"a.y\", 2),\n        TestAction::assert_eq(\"a.z\", -3),\n    ]);\n}\n\n#[test]\nfn spread_with_arguments() {\n    run_test_actions([\n        TestAction::run(indoc! {r#\"\n                const a = [1, \"test\", 3, 4];\n                function foo(...a) {\n                    return arguments;\n                }\n\n                var result = foo(...a);\n            \"#}),\n        TestAction::assert_eq(\"result[0]\", 1),\n        TestAction::assert_eq(\"result[1]\", js_str!(\"test\")),\n        TestAction::assert_eq(\"result[2]\", 3),\n        TestAction::assert_eq(\"result[3]\", 4),\n    ]);\n}\n\n#[test]\nfn array_rest_with_arguments() {\n    run_test_actions([\n        TestAction::run_harness(),\n        TestAction::run(indoc! {r#\"\n                var b = [4, 5, 6]\n                var a = [1, 2, 3, ...b];\n            \"#}),\n        TestAction::assert(\"arrayEquals(a, [ 1, 2, 3, 4, 5, 6 ])\"),\n    ]);\n}\n\n#[test]\nfn spread_shallow_clone() {\n    run_test_actions([TestAction::assert(indoc! {r#\"\n            var a = { x: {} };\n            var aClone = { ...a };\n\n            a.x === aClone.x\n        \"#})]);\n}\n\n#[test]\nfn spread_merge() {\n    run_test_actions([\n        TestAction::run(indoc! {r#\"\n                var a = { x: 1, y: 2 };\n                var b = { x: -1, z: -3, ...a };\n            \"#}),\n        TestAction::assert_eq(\"b.x\", 1),\n        TestAction::assert_eq(\"b.y\", 2),\n        TestAction::assert_eq(\"b.z\", -3),\n    ]);\n}\n\n#[test]\nfn spread_overriding_properties() {\n    run_test_actions([\n        TestAction::run(indoc! {r#\"\n                var a = { x: 0, y: 0 };\n                var aWithOverrides = { ...a, ...{ x: 1, y: 2 } };\n            \"#}),\n        TestAction::assert_eq(\"aWithOverrides.x\", 1),\n        TestAction::assert_eq(\"aWithOverrides.y\", 2),\n    ]);\n}\n\n#[test]\nfn spread_getters_in_initializer() {\n    run_test_actions([TestAction::assert_eq(\n        indoc! {r#\"\n                var a = { x: 42 };\n                var aWithXGetter = { ...a, get x() { throw new Error('not thrown yet') } };\n            \"#},\n        JsValue::undefined(),\n    )]);\n}\n\n#[test]\nfn spread_getters_in_object() {\n    run_test_actions([TestAction::assert_native_error(\n        indoc! {r#\"\n            var a = { x: 42 };\n            var aWithXGetter = { ...a, ... { get x() { throw new Error('not thrown yet') } } };\n        \"#},\n        JsNativeErrorKind::Error,\n        \"not thrown yet\",\n    )]);\n}\n\n#[test]\nfn spread_setters() {\n    run_test_actions([TestAction::assert_eq(\n        \"var z = { set x(nexX) { throw new Error() }, ... { x: 1 } }\",\n        JsValue::undefined(),\n    )]);\n}\n\n#[test]\nfn spread_null_and_undefined_ignored() {\n    run_test_actions([\n        TestAction::run(\"var a = { ...null, ...undefined };\"),\n        TestAction::assert(\"!(undefined in a)\"),\n        TestAction::assert(\"!(null in a)\"),\n    ]);\n}\n\n#[test]\nfn spread_with_new() {\n    run_test_actions([TestAction::assert_eq(\n        indoc! {r#\"\n            function F(m) {\n                this.m = m;\n            }\n            function f(...args) {\n                return new F(...args);\n            }\n            f('message').m;\n        \"#},\n        js_str!(\"message\"),\n    )]);\n}\n\n#[test]\nfn spread_with_call() {\n    run_test_actions([TestAction::assert_eq(\n        indoc! {r#\"\n            function f(m) {\n                return m;\n            }\n            function g(...args) {\n                return f(...args);\n            }\n            g('message');\n        \"#},\n        js_str!(\"message\"),\n    )]);\n}\n"
  },
  {
    "path": "core/engine/src/tests/to_string.rs",
    "content": "#![allow(clippy::items_after_statements)]\n\nuse std::fmt::Debug;\n\nuse crate::{Context, value::TryFromJs};\nuse boa_parser::Source;\n\n/// We test nested `toString()` cases:\n/// * an ordinary within an ordinary // `ordinary_1` --> `ordinary_2`\n/// * an ordinary within an arrow // `ordinary_0` --> `arrow_3`\n/// * an arrow within an ordinary // `arrow_2` --> `ordinary_2`\n/// * an arrow within an arrow // `arrow_1` --> `arrow_3`\n///\n/// And also test top level `toString()` workability.\n///\n/// It use `format!(..)` to avoid copy-past & human mistakes\n#[test]\nfn test_ordinary_and_arrow_to_string() {\n    let ordinary_0 = \"function oa3(yy, xx) {\n        return xx * 3 + yy\n    }\";\n    let arrow_1 = r#\"(b) => b * 2\"#;\n    let arrow_2 = r#\"(b) => b * /* --- */ 4\"#;\n    let arrow_3 = format!(\n        r#\"(a) => {{\n        ia1 = {arrow_1}    ;\n        // \\\\ // \\\\ // \\\\ // \\\\ // \\\\ // \\\\ // \\\\\n        A    =    ia1 . toString(    );\n        {ordinary_0}\n        A1 = oa3.toString(  );\n        return ia1(a) - 1\n    }}\"#\n    );\n\n    let ordinary_1 = r#\"function oa1(yy, xx) {\n        return xx * 3 + yy\n    }\"#;\n    let ordinary_2 = format!(\n        \"function oa2(a, boba) {{\n        {ordinary_1}\n        B = oa1.toString();;;\n        C = oa2.toString();\n        ia2 = {arrow_2};\n        D = ia2.toString();\n        return oa1(a, 5) - boba;\n    }}\"\n    );\n\n    let code = format!(\n        \"unused_f = (b) => b * b      ;\n        var A;\n        var A1;\n        var B;\n        var C;\n        var D;\n        var E;\n        var F;\n\n        ia3 = {arrow_3};\n\n        {ordinary_2}\n\n        oa2(7, 2);\n        ia3(7);\n\n        const x = {{\n            a: A,\n            a1: A1,\n            b: B,\n            c: C,\n            d: D,\n            e: ia3.toString(),\n            f: oa2.toString(),\n        }};\n\n        x\"\n    );\n\n    #[derive(Debug, TryFromJs, PartialEq, Eq)]\n    struct Expected {\n        a: String,\n        a1: String,\n        b: String,\n        c: String,\n        d: String,\n        e: String,\n        f: String,\n    }\n    let expected = Expected {\n        a: arrow_1.into(),\n        a1: ordinary_0.into(),\n        b: ordinary_1.into(),\n        c: ordinary_2.clone(),\n        d: arrow_2.into(),\n        e: arrow_3,\n        f: ordinary_2,\n    };\n\n    assert_helper(&code, expected);\n}\n\n#[test]\nfn test_simple_generator_to_string() {\n    let inner_fn = \"function ff(yy, xx) {\n        return xx * 3 + yy\n    }\";\n    let generator = format!(\n        \"function* simpleGenerator() {{\n        {inner_fn}\n        A = ff.toString();\n\n        yield ff(1, 1);\n        yield ff(2, 2);\n        yield ff(3, 3);\n    }}\"\n    );\n\n    let code = format!(\n        \"\n        var A;\n        var B;\n\n        {generator}\n        B = simpleGenerator.toString();\n\n        const gen = simpleGenerator();\n        gen.next();\n        gen.next();\n\n        const x = {{\n            a: A,\n            b: B,\n        }};\n        x\"\n    );\n\n    #[derive(Debug, TryFromJs, PartialEq, Eq)]\n    struct Expected {\n        a: String,\n        b: String,\n    }\n    let expected = Expected {\n        a: inner_fn.into(),\n        b: generator,\n    };\n\n    assert_helper(&code, expected);\n}\n\n#[test]\nfn test_async_fn_to_string() {\n    let f = \"function resolveAfter2Seconds(x) {\n            return new Promise((resolve) => {\n                setTimeout(() => {\n                resolve(x);\n            }, 2000);\n        });\n    }\";\n    let async_fn = \"async function asyncCall() {\n        let r = await resolveAfter2Seconds(88);\n        return r\n    }\";\n\n    let code = format!(\n        \"\n        {f}\n        {async_fn}\n        const x = {{\n            a: asyncCall.toString(),\n        }};\n        x\"\n    );\n\n    let expected = ExpectedOne { a: async_fn.into() };\n    assert_helper(&code, expected);\n}\n\n#[test]\nfn test_async_generator_to_string() {\n    let async_gen = \"async function* asyncGenerator(n) {\n        let index = 1    ;\n        while (index <= n     ) {\n            await new Promise /*   0____0   */   (resolve => setTimeout(resolve, 250, index));\n            yield index++;\n        }\n    }\";\n\n    let code = format!(\n        \"\n        {async_gen}\n        const x = {{\n            a: asyncGenerator.toString(),\n        }};\n        x\"\n    );\n\n    let expected = ExpectedOne {\n        a: async_gen.into(),\n    };\n    assert_helper(&code, expected);\n}\n\n#[test]\nfn test_async_arrow_to_string() {\n    let async_arrow = \"async (a, b) => {\n        return new Promise(resolve => {\n            setTimeout(() => {\n                resolve(a + b);\n            }, 1000);\n        });\n    }\";\n    let func_expr = \"function(a) {\n        return a /* * */ * 2;\n    }\";\n    let async_func_expr = \"async function() {\n        return /* * */ 1;\n    }\";\n    let gen_expr = \"function*(a) {\n        yield a /* * */;\n        yield a + 1 /* * */;\n        yield a + 2 /* * */;\n    }\";\n    let async_gen_expr = \"async function*(a) {\n        yield await a /* * */;\n        yield await a + 1 /* * */;\n        yield await a + 2 /* * */;\n    }\";\n\n    let code = format!(\n        \"\n        const asyncArrowAdd = {async_arrow};\n        const funcExpr = {func_expr};\n        const asyncFnExpr = {async_func_expr}   ;\n        const genExpr = {gen_expr} ;\n        const asyncGenExpr = {async_gen_expr};\n\n        const x = {{\n            a: asyncArrowAdd.toString(),\n            b: funcExpr.toString(),\n            c: asyncFnExpr.toString(),\n            d: genExpr.toString(),\n            e: asyncGenExpr.toString(),\n        }};\n        x\"\n    );\n\n    #[derive(Debug, TryFromJs, PartialEq, Eq)]\n    struct Expected {\n        a: String,\n        b: String,\n        c: String,\n        d: String,\n        e: String,\n    }\n    let expected = Expected {\n        a: async_arrow.into(),\n        b: func_expr.into(),\n        c: async_func_expr.into(),\n        d: gen_expr.into(),\n        e: async_gen_expr.into(),\n    };\n\n    assert_helper(&code, expected);\n}\n\n#[test]\nfn test_class_methods_to_string() {\n    let calc_area = \"calcArea() {\n        return this.height * this.width;\n    }\";\n    let get_area = \"get area() {\n        return this.calcArea(/**/);\n    }\";\n    let sides = \"*sides() {\n        yield this.height;\n        yield this.width;\n        yield this.height;\n        yield this.width;\n    }\";\n\n    let code = format!(\n        \"\n        class Rectangle {{\n            constructor(height, width) {{\n                this.height = height;\n                this.width = width;\n            }}\n            {calc_area}\n            {get_area}\n            {sides}\n        }}\n\n        let r = new Rectangle(24, 42);\n        const descr = Object.getOwnPropertyDescriptor(Rectangle.prototype, 'area');\n        const x = {{\n            calc_area: r.calcArea.toString(),\n            sides: r.sides.toString(),\n            area_val: r.area.toString(),\n            area: descr.get.toString(),\n        }};\n        x\"\n    );\n\n    #[derive(Debug, TryFromJs, PartialEq, Eq)]\n    struct Expected {\n        calc_area: String,\n        sides: String,\n        area_val: String,\n        area: String,\n    }\n    let expected = Expected {\n        calc_area: calc_area.into(),\n        sides: sides.into(),\n        area_val: (24 * 42).to_string(),\n        area: get_area.into(),\n    };\n\n    assert_helper(&code, expected);\n}\n\n#[test]\nfn test_obj_methods_to_string() {\n    let get_undef = \"get p2() { /* / */ }\";\n    let ordinary = \"p3(a, b) { return a + b }\";\n    let set_p0 = \"set p4(a) { /* * */ p0 = a; }\";\n    let generator = \"*vroom_d() { yield 4; }\";\n    let async_generator = \"async* vroom_e() {     }\";\n\n    let code = format!(\n        \"\n        const x = {{\n            p0: 97,\n            p1: true,\n            {get_undef},\n            {ordinary},\n            {set_p0},\n            {generator},\n            {async_generator},\n        }};\n\n        let descr_a = Object.getOwnPropertyDescriptor(x, 'p2');\n        let descr_c = Object.getOwnPropertyDescriptor(x, 'p4');\n\n        const ret = {{\n            a: descr_a.get.toString(),\n            b: x.p3.toString(),\n            c: descr_c.set.toString(),\n            d: x.vroom_d.toString(),\n            e: x.vroom_e.toString(),\n        }};\n        ret\"\n    );\n\n    #[derive(Debug, TryFromJs, PartialEq, Eq)]\n    struct Expected {\n        a: String,\n        b: String,\n        c: String,\n        d: String,\n        e: String,\n    }\n    let expected = Expected {\n        a: get_undef.into(),\n        b: ordinary.into(),\n        c: set_p0.into(),\n        d: generator.into(),\n        e: async_generator.into(),\n    };\n\n    assert_helper(&code, expected);\n}\n\n#[test]\nfn test_eval_fn_to_string() {\n    let function_def = \"function f1(x) {\n        return 1  +  x * x\n    }\";\n    let code = format!(\n        \"\n        eval(`{function_def};    42`);\n\n        const ret = {{ a: f1.toString() }};\n        ret\"\n    );\n\n    #[derive(Debug, TryFromJs, PartialEq, Eq)]\n    struct Expected {\n        a: String,\n    }\n    let expected = Expected {\n        a: function_def.into(),\n    };\n\n    assert_helper(&code, expected);\n}\n\n#[test]\nfn test_static_to_string() {\n    let fn_def = \"function f3(x) {\n        return 3  +  x * x  +  3\n    }\";\n    let static_fn_def = format!(\n        \"staticFunc() {{\n                {fn_def};;;\n                return f3 . toString()\n            }}\"\n    );\n    let fn_in_static_block = \"function ff(x) { return \\\"f\\\" + \\\"f\\\" }\";\n\n    let code = format!(\n        \"\n        class TestClass {{\n            static {static_fn_def}\n\n            static str = \\\"\\\";\n            static {{\n                {fn_in_static_block};\n                this.str = ff.toString();\n            }}\n        }}\n\n        const ret = {{\n            a: TestClass.staticFunc(),\n            b: TestClass.staticFunc.toString(),\n            c: TestClass.str,\n        }};\n        ret\n        \"\n    );\n\n    #[derive(Debug, TryFromJs, PartialEq, Eq)]\n    struct Expected {\n        a: String,\n        b: String,\n        c: String,\n    }\n    let expected = Expected {\n        a: fn_def.into(),\n        b: static_fn_def,\n        c: fn_in_static_block.into(),\n    };\n\n    assert_helper(&code, expected);\n}\n\n#[derive(Debug, TryFromJs, PartialEq, Eq)]\nstruct ExpectedOne {\n    a: String,\n}\n\n#[allow(clippy::needless_pass_by_value)]\nfn assert_helper<T>(code: &str, expected: T)\nwhere\n    T: TryFromJs + Debug + Eq,\n{\n    let context = &mut Context::default();\n    let js = context.eval(Source::from_bytes(code)).unwrap();\n    let res = T::try_from_js(&js, context).unwrap();\n    assert_eq!(expected, res);\n}\n"
  },
  {
    "path": "core/engine/src/try_into_js_result_impls.rs",
    "content": "//! Declare implementations of [`TryIntoJsResult`] trait for various types.\n\nuse crate::value::TryIntoJs;\nuse crate::{Context, JsResult, JsValue, TryIntoJsResult};\n\nimpl<T> TryIntoJsResult for T\nwhere\n    T: TryIntoJs,\n{\n    fn try_into_js_result(self, ctx: &mut Context) -> JsResult<JsValue> {\n        self.try_into_js(ctx)\n    }\n}\n\nimpl<T> TryIntoJsResult for JsResult<T>\nwhere\n    T: TryIntoJsResult,\n{\n    fn try_into_js_result(self, cx: &mut Context) -> JsResult<JsValue> {\n        self.and_then(|value| value.try_into_js_result(cx))\n    }\n}\n"
  },
  {
    "path": "core/engine/src/value/conversions/convert.rs",
    "content": "//! Types and functions for applying JavaScript Convert rules to [`JsValue`] when\n//! converting. See <https://262.ecma-international.org/5.1/#sec-9> (Section 9) for\n//! conversion rules of JavaScript types.\n//!\n//! Some conversions are not specified in the spec (e.g. integer conversions),\n//! and we apply rules that make sense (e.g. converting to Number and rounding\n//! if necessary).\n\nuse boa_engine::JsNativeError;\nuse boa_gc::{Finalize, Trace};\n\nuse crate::value::TryFromJs;\nuse crate::{Context, JsData, JsResult, JsString, JsValue};\n\n/// A wrapper type that allows converting a `JsValue` to a specific type.\n/// This is useful when you want to convert a `JsValue` to a Rust type.\n///\n/// # Example\n/// Convert a string to number.\n/// ```\n/// # use boa_engine::{Context, js_string, JsValue};\n/// # use boa_engine::value::{Convert, TryFromJs};\n/// # let mut context = Context::default();\n/// let value = JsValue::from(js_string!(\"42\"));\n/// let Convert(converted): Convert<i32> =\n///     Convert::try_from_js(&value, &mut context).unwrap();\n///\n/// assert_eq!(converted, 42);\n/// ```\n///\n/// Convert a number to a bool.\n/// ```\n/// # use boa_engine::{Context, js_string, JsValue};\n/// # use boa_engine::value::{Convert, TryFromJs};\n/// # let mut context = Context::default();\n/// let Convert(conv0): Convert<bool> =\n///     Convert::try_from_js(&JsValue::new(0), &mut context).unwrap();\n/// let Convert(conv5): Convert<bool> =\n///     Convert::try_from_js(&JsValue::new(5), &mut context).unwrap();\n/// let Convert(conv_nan): Convert<bool> =\n///     Convert::try_from_js(&JsValue::new(f64::NAN), &mut context).unwrap();\n///\n/// assert_eq!(conv0, false);\n/// assert_eq!(conv5, true);\n/// assert_eq!(conv_nan, false);\n/// ```\n#[derive(Debug, Clone, PartialEq, Eq, Trace, Finalize, JsData)]\npub struct Convert<T: TryFromJs>(pub T);\n\nimpl<T: TryFromJs> From<T> for Convert<T> {\n    fn from(value: T) -> Self {\n        Self(value)\n    }\n}\n\nimpl<T: TryFromJs> AsRef<T> for Convert<T> {\n    fn as_ref(&self) -> &T {\n        &self.0\n    }\n}\n\nmacro_rules! decl_convert_to_int {\n    ($($ty:ty),*) => {\n        $(\n            impl TryFromJs for Convert<$ty> {\n                fn try_from_js(value: &JsValue, context: &mut Context) -> JsResult<Self> {\n                    value.to_numeric_number(context).and_then(|num| {\n                        if num.is_finite() {\n                            if num >= f64::from(<$ty>::MAX) {\n                                Err(JsNativeError::typ()\n                                    .with_message(\"cannot convert value to integer, it is too large\")\n                                    .into())\n                            } else if num <= f64::from(<$ty>::MIN) {\n                                Err(JsNativeError::typ()\n                                    .with_message(\"cannot convert value to integer, it is too small\")\n                                    .into())\n                                // Only round if it differs from the next integer by an epsilon\n                            } else if num.abs().fract() >= (1.0 - f64::EPSILON) {\n                                Ok(Convert(num.round() as $ty))\n                            } else {\n                                Ok(Convert(num as $ty))\n                            }\n                        } else if num.is_nan() {\n                            Err(JsNativeError::typ()\n                                .with_message(\"cannot convert NaN to integer\")\n                                .into())\n                        } else if num.is_infinite() {\n                            Err(JsNativeError::typ()\n                                .with_message(\"cannot convert Infinity to integer\")\n                                .into())\n                        } else {\n                            Err(JsNativeError::typ()\n                                .with_message(\"cannot convert non-finite number to integer\")\n                                .into())\n                        }\n                    })\n                }\n            }\n        )*\n    };\n}\n\ndecl_convert_to_int!(i8, i16, i32, u8, u16, u32);\n\nmacro_rules! decl_convert_to_float {\n    ($($ty:ty),*) => {\n        $(\n            impl TryFromJs for Convert<$ty> {\n                fn try_from_js(value: &JsValue, context: &mut Context) -> JsResult<Self> {\n                    value.to_numeric_number(context).and_then(|num| Ok(Convert(<$ty>::try_from(num).map_err(|_| {\n                        JsNativeError::typ()\n                            .with_message(\"cannot convert value to float\")\n                    })?)))\n                }\n            }\n        )*\n    };\n}\n\ndecl_convert_to_float!(f64);\n\nimpl TryFromJs for Convert<String> {\n    fn try_from_js(value: &JsValue, context: &mut Context) -> JsResult<Self> {\n        value\n            .to_string(context)\n            .and_then(|s| s.to_std_string().map_err(|_| JsNativeError::typ().into()))\n            .map(Convert)\n    }\n}\n\nimpl TryFromJs for Convert<JsString> {\n    fn try_from_js(value: &JsValue, context: &mut Context) -> JsResult<Self> {\n        value.to_string(context).map(Convert)\n    }\n}\n\nimpl TryFromJs for Convert<bool> {\n    fn try_from_js(value: &JsValue, _context: &mut Context) -> JsResult<Self> {\n        Ok(Self(value.to_boolean()))\n    }\n}\n"
  },
  {
    "path": "core/engine/src/value/conversions/either.rs",
    "content": "//! Implementation of [`TryFromJs`] for [`Either`].\n//!\n//! This will try to deserialize for the [`Either::Left`] type\n//! first, and if it fails will try the [`Either::Right`] type.\n//!\n//! Upon failure of both, the second failure will be returned.\n#![cfg(feature = \"either\")]\n\nuse crate::value::TryFromJs;\nuse boa_engine::{Context, JsResult, JsValue};\nuse either::Either;\n\nimpl<L, R> TryFromJs for Either<L, R>\nwhere\n    L: TryFromJs,\n    R: TryFromJs,\n{\n    #[inline]\n    fn try_from_js(value: &JsValue, context: &mut Context) -> JsResult<Self> {\n        L::try_from_js(value, context)\n            .map(Self::Left)\n            .or_else(|_| R::try_from_js(value, context).map(Self::Right))\n    }\n}\n\n#[test]\nfn either() {\n    let v = JsValue::new(123);\n    let mut context = Context::default();\n\n    assert_eq!(\n        Either::<i32, i32>::try_from_js(&v, &mut context),\n        Ok(Either::Left(123))\n    );\n    assert_eq!(\n        Either::<i32, String>::try_from_js(&v, &mut context),\n        Ok(Either::Left(123))\n    );\n    assert_eq!(\n        Either::<String, i32>::try_from_js(&v, &mut context),\n        Ok(Either::Right(123))\n    );\n    assert!(Either::<String, String>::try_from_js(&v, &mut context).is_err());\n}\n"
  },
  {
    "path": "core/engine/src/value/conversions/mod.rs",
    "content": "//! Conversions from JavaScript values into Rust values, and the other way around.\n\nuse super::{JsBigInt, JsObject, JsString, JsSymbol, JsValue};\nuse crate::NativeObject;\nuse crate::value::inner::InnerValue;\nuse crate::{js_string, string::JsStr};\n\nmod either;\npub(super) mod nullable;\nmod serde_json;\npub(super) mod try_from_js;\npub(super) mod try_into_js;\n\npub(super) mod convert;\n\nimpl From<JsStr<'_>> for JsValue {\n    fn from(value: JsStr<'_>) -> Self {\n        Self::from_inner(InnerValue::string(value.into()))\n    }\n}\n\nimpl From<JsString> for JsValue {\n    fn from(value: JsString) -> Self {\n        Self::from_inner(InnerValue::string(value))\n    }\n}\n\nimpl From<char> for JsValue {\n    #[inline]\n    fn from(value: char) -> Self {\n        let mut buf: [u16; 2] = [0; 2];\n\n        let out = value.encode_utf16(&mut buf);\n\n        Self::from(js_string!(&*out))\n    }\n}\n\nimpl From<JsSymbol> for JsValue {\n    #[inline]\n    fn from(value: JsSymbol) -> Self {\n        Self::from_inner(InnerValue::symbol(value))\n    }\n}\n\nimpl From<f32> for JsValue {\n    #[inline]\n    fn from(value: f32) -> Self {\n        Self::rational(f64::from(value))\n    }\n}\n\nimpl From<f64> for JsValue {\n    #[inline]\n    fn from(value: f64) -> Self {\n        Self::rational(value)\n    }\n}\n\nmacro_rules! impl_from_integer {\n    ( $( $type_:ty ),* ) => {\n        $(\n            impl From<$type_> for JsValue {\n                #[inline]\n                #[allow(clippy::cast_lossless)]\n                fn from(value: $type_) -> Self {\n\n                    i32::try_from(value)\n                        .map_or_else(\n                            |_| Self::rational(value as f64),\n                            |value| Self::from_inner(InnerValue::integer32(value)),\n                        )\n                }\n            }\n        )*\n    };\n}\n\nimpl_from_integer!(u8, i8, u16, i16, u32, i32, u64, i64, usize, isize);\n\nimpl From<JsBigInt> for JsValue {\n    #[inline]\n    fn from(value: JsBigInt) -> Self {\n        Self::from_inner(InnerValue::bigint(value))\n    }\n}\n\nimpl From<bool> for JsValue {\n    #[inline]\n    fn from(value: bool) -> Self {\n        Self::from_inner(InnerValue::boolean(value))\n    }\n}\n\nimpl<T: NativeObject> From<JsObject<T>> for JsValue {\n    #[inline]\n    fn from(object: JsObject<T>) -> Self {\n        Self::from_inner(InnerValue::object(object.upcast()))\n    }\n}\n\nimpl From<()> for JsValue {\n    #[inline]\n    #[allow(clippy::pedantic)] // didn't want to increase our MSRV for just a lint.\n    fn from((): ()) -> Self {\n        Self::null()\n    }\n}\n\n/// Converts an `Option<T>` into a `JsValue`.\n///\n/// It will convert the `None` variant to `JsValue::undefined()`, and the `Some()` variant into a\n/// `JsValue` using the `Into` trait.\npub(crate) trait IntoOrUndefined {\n    /// Converts an `Option<T>` into a `JsValue`.\n    fn into_or_undefined(self) -> JsValue;\n}\n\nimpl<T> IntoOrUndefined for Option<T>\nwhere\n    T: Into<JsValue>,\n{\n    #[inline]\n    fn into_or_undefined(self) -> JsValue {\n        match self {\n            Some(value) => value.into(),\n            None => JsValue::undefined(),\n        }\n    }\n}\n"
  },
  {
    "path": "core/engine/src/value/conversions/nullable/mod.rs",
    "content": "//! A value that can be `null` in JavaScript, but not `undefined`.\n\nuse crate::value::{TryFromJs, TryIntoJs};\nuse boa_engine::{Context, JsResult, JsValue};\n\n#[cfg(test)]\nmod tests;\n\n/// A value that can be `null` in JavaScript, but not `undefined`.\n///\n/// This is used to distinguish between values in JavaScript that can be\n/// `null` (i.e. [`Nullable<T>`]) vs. those of which that can be `undefined`\n/// (i.e. [`Option<T>`]).\n///\n/// [`Nullable<T>`] tries to be close in API surface to the standard\n/// [`Option<T>`], though with a much smaller API scope, and can be\n/// transformed into and from [`Option<T>`] for free.\n///\n/// ## Why a new type?\n/// There are in the standard 2 different values that are \"null coalescing\":\n/// 1. `undefined`, which is its own type (`typeof void 0 == \"undefined\"`),\n/// 2. `null`, which is different from `undefined` (`undefined !== null`) but\n///    is of type object (`typeof null === \"object\"`) for legacy reasons.\n///\n/// [`Option<T>`] sets up the first case, so we needed to add a new type for\n/// the second case.\n///\n/// ## Would it be bad to use `Option::<T>::None` as both undefined _or_ null?\n/// Many values in the standard can be `null` but not `undefined`, or the other\n/// way around. Coalescing these two into `Option::None` means that any\n/// conversions using boa's traits (such as `TryFromJs`) would result in\n/// indistinguishable values. The only way to respect the standard in this case\n/// would be to get `JsValue` and manually verify it's not undefined/null, then\n/// do the conversion. This new type makes this process much simpler.\n///\n/// It also means that there is an asymmetry between `JsValue` to [`Option<T>`]\n/// then [`Option<T>`] back to `JsValue`, leading to unintuitive errors. Having\n/// a type [`Nullable<T>`] that acts like [`Option<T>`] but (de-)serialize to\n/// `null` clarifies all usage.\n///\n/// ## How can I do `EitherNullOrUndefined<T>`?\n/// The best way is to use `Nullable<Option<T>>` and convert into [`Option<T>`]\n/// using [`Nullable::flatten()`]. Please note that the reverse\n/// (`Nullable<Option<T>>`) results in the same deserializing behaviour but\n/// does not implement `flatten()`.\n///\n/// Please note that JavaScript cannot make a distinction between [`Option<T>`] and\n/// `Option<Option<T>>`. This cannot be resolved using this type, as it suffers\n/// from the same limitation. There is no way to distinguish between `Null` and\n/// `NonNull(Null)`. `Nullable<Nullable<T>>` does not provide additional\n/// information.\n///\n/// # Examples\n/// ```\n/// # use boa_engine::{Context, JsValue};\n/// # use boa_engine::value::Nullable;\n/// # let context = &mut Context::default();\n/// let maybe_10: Nullable<u8> = JsValue::new(10).try_js_into(context).unwrap();\n/// assert_eq!(maybe_10, Nullable::NonNull(10u8));\n///\n/// let maybe_not: Nullable<u8> = JsValue::null().try_js_into(context).unwrap();\n/// assert_eq!(maybe_not, Nullable::Null);\n/// ```\n///\n/// ```\n/// # use boa_engine::{Context, JsResult, JsValue};\n/// # use boa_engine::value::Nullable;\n/// # let context = &mut Context::default();\n/// let mut v: JsResult<Nullable<Option<u8>>> =\n///     JsValue::undefined().try_js_into(context);\n/// assert_eq!(v, Ok(Nullable::NonNull(None)));\n///\n/// v = JsValue::null().try_js_into(context);\n/// assert_eq!(v, Ok(Nullable::Null));\n///\n/// v = JsValue::from(42).try_js_into(context);\n/// assert_eq!(v, Ok(Nullable::NonNull(Some(42))));\n/// assert_eq!(v.unwrap().flatten(), Some(42));\n/// ```\n#[derive(Debug, Copy, Clone, PartialOrd, PartialEq, Hash, Ord, Eq)]\npub enum Nullable<T> {\n    /// The value was not defined in JavaScript.\n    Null,\n\n    /// The value was defined in JavaScript.\n    NonNull(T),\n}\n\nimpl<T> Nullable<T> {\n    /// Returns `true` if this value is `Nullable::Null`.\n    pub const fn is_null(&self) -> bool {\n        matches!(self, Nullable::Null)\n    }\n\n    /// Returns `true` if this value is `Nullable::NotNull`.\n    pub const fn is_not_null(&self) -> bool {\n        matches!(self, Nullable::NonNull(_))\n    }\n\n    /// Returns an iterator over the possibly contained value.\n    ///\n    /// # Examples\n    ///\n    /// ```\n    /// # use boa_engine::value::Nullable;\n    /// let x = Nullable::NonNull(4);\n    /// assert_eq!(x.iter().next(), Some(&4));\n    ///\n    /// let x: Nullable<u32> = Nullable::Null;\n    /// assert_eq!(x.iter().next(), None);\n    /// ```\n    #[inline]\n    pub fn iter(&self) -> Iter<'_, T> {\n        self.into_iter()\n    }\n\n    /// Converts from `&Nullable<T>` to `Nullable<&T>`.\n    ///\n    /// # Examples\n    /// ```\n    /// # use boa_engine::value::Nullable;\n    /// let text: Nullable<String> = Nullable::NonNull(\"Hello, world!\".to_string());\n    ///\n    /// // First, cast `Nullable<String>` to `Nullable<&String>` with `as_ref`,\n    /// // then consume *that* with `map`, leaving `text` on the stack.\n    /// let text_length: Nullable<usize> = text.as_ref().map(|s| s.len());\n    /// println!(\"still can print text: {text:?}\");\n    /// ```\n    #[inline]\n    pub const fn as_ref(&self) -> Nullable<&T> {\n        match self {\n            Nullable::Null => Nullable::Null,\n            Nullable::NonNull(t) => Nullable::NonNull(t),\n        }\n    }\n\n    /// Maps a [`Nullable<T>`] to [`Nullable<U>`] by applying a function to a contained\n    /// value.\n    /// # Examples\n    ///\n    /// ```\n    /// # use boa_engine::value::Nullable;\n    /// let maybe_some_string = Nullable::NonNull(String::from(\"Hello, World!\"));\n    /// // `Nullable::map` takes self *by value*, consuming `maybe_some_string`\n    /// let maybe_some_len = maybe_some_string.map(|s| s.len());\n    /// assert_eq!(maybe_some_len, Nullable::NonNull(13));\n    ///\n    /// let x: Nullable<&str> = Nullable::Null;\n    /// assert_eq!(x.map(|s| s.len()), Nullable::Null);\n    /// ```\n    #[inline]\n    pub fn map<U, F>(self, f: F) -> Nullable<U>\n    where\n        F: FnOnce(T) -> U,\n    {\n        match self {\n            Nullable::NonNull(x) => Nullable::NonNull(f(x)),\n            Nullable::Null => Nullable::Null,\n        }\n    }\n\n    /// Returns the contained [`Nullable::NonNull`] value or a default.\n    ///\n    /// Consumes the `self` argument then, if [`Nullable::NonNull`], returns the contained\n    /// value, otherwise if [`Nullable::Null`], returns the [default value] for that\n    /// type.\n    ///\n    /// # Examples\n    ///\n    /// ```\n    /// # use boa_engine::value::Nullable;\n    /// let x: Nullable<u32> = Nullable::Null;\n    /// let y: Nullable<u32> = Nullable::NonNull(12);\n    ///\n    /// assert_eq!(x.unwrap_or_default(), 0);\n    /// assert_eq!(y.unwrap_or_default(), 12);\n    /// ```\n    ///\n    /// [default value]: Default::default\n    /// [`parse`]: str::parse\n    #[inline]\n    pub fn unwrap_or_default(self) -> T\n    where\n        T: Default,\n    {\n        match self {\n            Nullable::NonNull(x) => x,\n            Nullable::Null => T::default(),\n        }\n    }\n}\n\nimpl<'a, T> IntoIterator for &'a Nullable<T> {\n    type Item = &'a T;\n    type IntoIter = Iter<'a, T>;\n\n    #[inline]\n    fn into_iter(self) -> Iter<'a, T> {\n        Iter(match self {\n            Nullable::Null => None,\n            Nullable::NonNull(v) => Some(v),\n        })\n    }\n}\n\nimpl<T> IntoIterator for Nullable<T> {\n    type Item = T;\n    type IntoIter = IntoIter<T>;\n\n    /// Returns a consuming iterator over the possibly contained value.\n    ///\n    /// # Examples\n    ///\n    /// ```\n    /// # use boa_engine::value::Nullable;\n    /// let x = Nullable::NonNull(\"string\");\n    /// let v: Vec<&str> = x.into_iter().collect();\n    /// assert_eq!(v, [\"string\"]);\n    ///\n    /// let x = Nullable::Null;\n    /// let v: Vec<&str> = x.into_iter().collect();\n    /// assert!(v.is_empty());\n    /// ```\n    #[inline]\n    fn into_iter(self) -> IntoIter<T> {\n        IntoIter(self.into())\n    }\n}\n\nimpl<T: TryFromJs> TryFromJs for Nullable<T> {\n    fn try_from_js(value: &JsValue, context: &mut Context) -> JsResult<Self> {\n        if value.is_null() {\n            Ok(Nullable::Null)\n        } else {\n            T::try_from_js(value, context).map(Nullable::NonNull)\n        }\n    }\n}\n\nimpl<T: TryIntoJs> TryIntoJs for Nullable<T> {\n    fn try_into_js(&self, context: &mut Context) -> JsResult<JsValue> {\n        match self {\n            Nullable::Null => Ok(JsValue::null()),\n            Nullable::NonNull(t) => t.try_into_js(context),\n        }\n    }\n}\n\nimpl<T> From<Nullable<T>> for Option<T> {\n    #[inline]\n    fn from(value: Nullable<T>) -> Self {\n        match value {\n            Nullable::Null => None,\n            Nullable::NonNull(t) => Some(t),\n        }\n    }\n}\n\nimpl<T> From<Option<T>> for Nullable<T> {\n    #[inline]\n    fn from(value: Option<T>) -> Self {\n        match value {\n            None => Nullable::Null,\n            Some(t) => Nullable::NonNull(t),\n        }\n    }\n}\n\n/// An iterator over a [`Nullable`] value reference.\n#[derive(Debug)]\npub struct Iter<'a, A>(Option<&'a A>);\n\nimpl<'a, T: 'a> Iterator for Iter<'a, T> {\n    type Item = &'a T;\n\n    fn next(&mut self) -> Option<Self::Item> {\n        self.0.take()\n    }\n}\n\nimpl<'a, T: 'a> DoubleEndedIterator for Iter<'a, T> {\n    fn next_back(&mut self) -> Option<Self::Item> {\n        self.0.take()\n    }\n}\n\nimpl<A> ExactSizeIterator for Iter<'_, A> {\n    #[inline]\n    fn len(&self) -> usize {\n        usize::from(self.0.is_some())\n    }\n}\n\n/// An owning iterator over a [`Nullable`] value.\n#[derive(Debug)]\npub struct IntoIter<A>(Option<A>);\n\nimpl<A> Iterator for IntoIter<A> {\n    type Item = A;\n    fn next(&mut self) -> Option<Self::Item> {\n        self.0.take()\n    }\n}\n\nimpl<A> DoubleEndedIterator for IntoIter<A> {\n    fn next_back(&mut self) -> Option<A> {\n        self.0.take()\n    }\n}\n\nimpl<A> ExactSizeIterator for IntoIter<A> {\n    #[inline]\n    fn len(&self) -> usize {\n        usize::from(self.0.is_some())\n    }\n}\n\nimpl<T> Nullable<Option<T>> {\n    /// Converts from `Nullable<Option<T>>` to [`Option<T>`].\n    #[inline]\n    pub fn flatten(self) -> Option<T> {\n        match self {\n            Nullable::Null | Nullable::NonNull(None) => None,\n            Nullable::NonNull(Some(t)) => Some(t),\n        }\n    }\n}\n"
  },
  {
    "path": "core/engine/src/value/conversions/nullable/tests.rs",
    "content": "use crate::value::TryIntoJs;\nuse boa_engine::value::Nullable;\nuse boa_engine::{Context, JsResult, JsValue};\n\n#[test]\nfn not_null() {\n    let context = &mut Context::default();\n    let v: Nullable<i32> = JsValue::new(42)\n        .try_js_into(context)\n        .expect(\"Failed to convert value from js\");\n\n    assert!(!v.is_null());\n    assert!(v.is_not_null());\n    assert_eq!(v, Nullable::NonNull(42));\n\n    assert_eq!(v.try_into_js(context).unwrap(), JsValue::new(42));\n}\n\n#[test]\nfn null() {\n    let context = &mut Context::default();\n    let v: Nullable<i32> = JsValue::null()\n        .try_js_into(context)\n        .expect(\"Failed to convert value from js\");\n\n    assert!(v.is_null());\n    assert!(!v.is_not_null());\n    assert_eq!(v, Nullable::Null);\n\n    assert_eq!(v.try_into_js(context).unwrap(), JsValue::null());\n}\n\n#[test]\nfn invalid() {\n    let context = &mut Context::default();\n    let v: JsResult<Nullable<i32>> = JsValue::undefined().try_js_into(context);\n\n    assert!(v.is_err());\n}\n"
  },
  {
    "path": "core/engine/src/value/conversions/serde_json.rs",
    "content": "//! This module implements the conversions from and into [`serde_json::Value`].\n\nuse super::JsValue;\nuse crate::{\n    Context, JsResult, JsVariant,\n    builtins::Array,\n    error::JsNativeError,\n    js_string,\n    object::JsObject,\n    property::{PropertyDescriptor, PropertyKey},\n};\nuse serde_json::{Map, Value};\nuse std::collections::HashSet;\n\nimpl JsValue {\n    /// Converts a [`serde_json::Value`] to a `JsValue`.\n    ///\n    /// # Example\n    ///\n    /// ```\n    /// use boa_engine::{Context, JsValue};\n    ///\n    /// let data = r#\"\n    ///     {\n    ///         \"name\": \"John Doe\",\n    ///         \"age\": 43,\n    ///         \"phones\": [\n    ///             \"+44 1234567\",\n    ///             \"+44 2345678\"\n    ///         ]\n    ///      }\"#;\n    ///\n    /// let json: serde_json::Value = serde_json::from_str(data).unwrap();\n    ///\n    /// let mut context = Context::default();\n    /// let value = JsValue::from_json(&json, &mut context).unwrap();\n    /// #\n    /// # assert_eq!(Some(json), value.to_json(&mut context).unwrap());\n    /// ```\n    pub fn from_json(json: &Value, context: &mut Context) -> JsResult<Self> {\n        /// Biggest possible integer, as i64.\n        const MAX_INT: i64 = i32::MAX as i64;\n\n        /// Smallest possible integer, as i64.\n        const MIN_INT: i64 = i32::MIN as i64;\n\n        match json {\n            Value::Null => Ok(Self::null()),\n            Value::Bool(b) => Ok(Self::new(*b)),\n            Value::Number(num) => num\n                .as_i64()\n                .filter(|n| (MIN_INT..=MAX_INT).contains(n))\n                .map(|i| Self::new(i as i32))\n                .or_else(|| num.as_f64().map(Self::new))\n                .ok_or_else(|| {\n                    JsNativeError::typ()\n                        .with_message(format!(\"could not convert JSON number {num} to JsValue\"))\n                        .into()\n                }),\n            Value::String(string) => Ok(Self::from(js_string!(string.as_str()))),\n            Value::Array(vec) => {\n                let mut arr = Vec::with_capacity(vec.len());\n                for val in vec {\n                    arr.push(Self::from_json(val, context)?);\n                }\n                Ok(Array::create_array_from_list(arr, context).into())\n            }\n            Value::Object(obj) => {\n                let js_obj = JsObject::with_object_proto(context.intrinsics());\n                for (key, value) in obj {\n                    let property = PropertyDescriptor::builder()\n                        .value(Self::from_json(value, context)?)\n                        .writable(true)\n                        .enumerable(true)\n                        .configurable(true);\n                    js_obj\n                        .borrow_mut()\n                        .insert(js_string!(key.clone()), property);\n                }\n\n                Ok(js_obj.into())\n            }\n        }\n    }\n\n    /// Converts the `JsValue` to a [`serde_json::Value`].\n    ///\n    /// If the `JsValue` is `Undefined`, this method will return `None`.\n    /// Otherwise it will return the corresponding `serde_json::Value`.\n    ///\n    /// # Example\n    ///\n    /// ```\n    /// use boa_engine::{Context, JsValue};\n    ///\n    /// let data = r#\"\n    ///     {\n    ///         \"name\": \"John Doe\",\n    ///         \"age\": 43,\n    ///         \"phones\": [\n    ///             \"+44 1234567\",\n    ///             \"+44 2345678\"\n    ///         ]\n    ///      }\"#;\n    ///\n    /// let json: serde_json::Value = serde_json::from_str(data).unwrap();\n    ///\n    /// let mut context = Context::default();\n    /// let value = JsValue::from_json(&json, &mut context).unwrap();\n    ///\n    /// let back_to_json = value.to_json(&mut context).unwrap();\n    /// #\n    /// # assert_eq!(Some(json), back_to_json);\n    /// ```\n    pub fn to_json(&self, context: &mut Context) -> JsResult<Option<Value>> {\n        let mut seen_objects = HashSet::new();\n        self.to_json_inner(context, &mut seen_objects)\n    }\n\n    fn to_json_inner(\n        &self,\n        context: &mut Context,\n        seen_objects: &mut HashSet<JsObject>,\n    ) -> JsResult<Option<Value>> {\n        match self.variant() {\n            JsVariant::Null => Ok(Some(Value::Null)),\n            JsVariant::Undefined => Ok(None),\n            JsVariant::Boolean(b) => Ok(Some(Value::from(b))),\n            JsVariant::String(string) => Ok(Some(string.to_std_string_escaped().into())),\n            JsVariant::Float64(rat) => Ok(Some(Value::from(rat))),\n            JsVariant::Integer32(int) => Ok(Some(Value::from(int))),\n            JsVariant::BigInt(_bigint) => Err(JsNativeError::typ()\n                .with_message(\"cannot convert bigint to JSON\")\n                .into()),\n            JsVariant::Object(obj) => {\n                if seen_objects.contains(&obj) {\n                    return Err(JsNativeError::typ()\n                        .with_message(\"cyclic object value\")\n                        .into());\n                }\n                seen_objects.insert(obj.clone());\n                let mut value_by_prop_key = |property_key, context: &mut Context| {\n                    obj.borrow()\n                        .properties()\n                        .get(&property_key)\n                        .and_then(|x| {\n                            x.value()\n                                .map(|val| val.to_json_inner(context, seen_objects))\n                        })\n                        .unwrap_or(Ok(Some(Value::Null)))\n                };\n\n                if obj.is_array() {\n                    let len = obj.length_of_array_like(context)?;\n                    let mut arr = Vec::with_capacity(len as usize);\n\n                    for k in 0..len as u32 {\n                        let val = value_by_prop_key(k.into(), context)?;\n                        match val {\n                            Some(val) => arr.push(val),\n\n                            // Undefined in array. Substitute with null as Value doesn't support Undefined.\n                            None => arr.push(Value::Null),\n                        }\n                    }\n                    // Passing the object rather than its clone that was inserted to the set should be fine\n                    // as they hash to the same value and therefore HashSet can still remove the clone\n                    seen_objects.remove(&obj);\n                    Ok(Some(Value::Array(arr)))\n                } else {\n                    let mut map = Map::new();\n\n                    for index in obj.borrow().properties().index_property_keys() {\n                        let key = index.to_string();\n                        let value = value_by_prop_key(index.into(), context)?;\n                        if let Some(value) = value {\n                            map.insert(key, value);\n                        }\n                    }\n\n                    for property_key in obj.borrow().properties().shape.keys() {\n                        let key = match &property_key {\n                            PropertyKey::String(string) => string.to_std_string_escaped(),\n                            PropertyKey::Index(i) => i.get().to_string(),\n                            PropertyKey::Symbol(_sym) => {\n                                return Err(JsNativeError::typ()\n                                    .with_message(\"cannot convert Symbol to JSON\")\n                                    .into());\n                            }\n                        };\n                        let value = value_by_prop_key(property_key, context)?;\n                        if let Some(value) = value {\n                            map.insert(key, value);\n                        }\n                    }\n                    seen_objects.remove(&obj);\n                    Ok(Some(Value::Object(map)))\n                }\n            }\n            JsVariant::Symbol(_sym) => Err(JsNativeError::typ()\n                .with_message(\"cannot convert Symbol to JSON\")\n                .into()),\n        }\n    }\n}\n\n#[cfg(test)]\nmod tests {\n    use boa_macros::js_str;\n    use indoc::indoc;\n    use serde_json::json;\n\n    use crate::{\n        Context, JsObject, JsValue, TestAction, js_string, object::JsArray, run_test_actions,\n    };\n\n    #[test]\n    fn json_conversions() {\n        const DATA: &str = indoc! {r#\"\n            {\n                \"name\": \"John Doe\",\n                \"age\": 43,\n                \"minor\": false,\n                \"adult\": true,\n                \"extra\": {\n                    \"address\": null\n                },\n                \"phones\": [\n                    \"+44 1234567\",\n                    -45,\n                    {},\n                    true\n                ],\n                \"7.3\": \"random text\",\n                \"100\": 1000,\n                \"24\": 42\n            }\n        \"#};\n\n        run_test_actions([TestAction::inspect_context(|ctx| {\n            let json: serde_json::Value = serde_json::from_str(DATA).unwrap();\n            assert!(json.is_object());\n\n            let value = JsValue::from_json(&json, ctx).unwrap();\n            let obj = value.as_object().unwrap();\n            assert_eq!(\n                obj.get(js_str!(\"name\"), ctx).unwrap(),\n                js_str!(\"John Doe\").into()\n            );\n            assert_eq!(obj.get(js_str!(\"age\"), ctx).unwrap(), 43_i32.into());\n            assert_eq!(obj.get(js_str!(\"minor\"), ctx).unwrap(), false.into());\n            assert_eq!(obj.get(js_str!(\"adult\"), ctx).unwrap(), true.into());\n\n            assert_eq!(\n                obj.get(js_str!(\"7.3\"), ctx).unwrap(),\n                js_string!(\"random text\").into()\n            );\n            assert_eq!(obj.get(js_str!(\"100\"), ctx).unwrap(), 1000.into());\n            assert_eq!(obj.get(js_str!(\"24\"), ctx).unwrap(), 42.into());\n\n            {\n                let extra = obj.get(js_str!(\"extra\"), ctx).unwrap();\n                let extra = extra.as_object().unwrap();\n                assert!(extra.get(js_str!(\"address\"), ctx).unwrap().is_null());\n            }\n            {\n                let phones = obj.get(js_str!(\"phones\"), ctx).unwrap();\n                let phones = phones.as_object().unwrap();\n\n                let arr = JsArray::from_object(phones.clone()).unwrap();\n                assert_eq!(arr.at(0, ctx).unwrap(), js_str!(\"+44 1234567\").into());\n                assert_eq!(arr.at(1, ctx).unwrap(), JsValue::from(-45_i32));\n                assert!(arr.at(2, ctx).unwrap().is_object());\n                assert_eq!(arr.at(3, ctx).unwrap(), true.into());\n            }\n\n            assert_eq!(Some(json), value.to_json(ctx).unwrap());\n        })]);\n    }\n\n    #[test]\n    fn integer_ops_to_json() {\n        run_test_actions([\n            TestAction::assert_with_op(\"1000000 + 500\", |v, ctx| {\n                v.to_json(ctx).unwrap() == Some(json!(1_000_500))\n            }),\n            TestAction::assert_with_op(\"1000000 - 500\", |v, ctx| {\n                v.to_json(ctx).unwrap() == Some(json!(999_500))\n            }),\n            TestAction::assert_with_op(\"1000000 * 500\", |v, ctx| {\n                v.to_json(ctx).unwrap() == Some(json!(500_000_000))\n            }),\n            TestAction::assert_with_op(\"1000000 / 500\", |v, ctx| {\n                v.to_json(ctx).unwrap() == Some(json!(2_000))\n            }),\n            TestAction::assert_with_op(\"233894 % 500\", |v, ctx| {\n                v.to_json(ctx).unwrap() == Some(json!(394))\n            }),\n            TestAction::assert_with_op(\"36 ** 5\", |v, ctx| {\n                v.to_json(ctx).unwrap() == Some(json!(60_466_176))\n            }),\n        ]);\n    }\n\n    #[test]\n    fn to_json_cyclic() {\n        let mut context = Context::default();\n        let obj = JsObject::with_null_proto();\n        obj.create_data_property(js_string!(\"a\"), obj.clone(), &mut context)\n            .expect(\"should create data property\");\n\n        assert!(\n            JsValue::from(obj)\n                .to_json(&mut context)\n                .unwrap_err()\n                .to_string()\n                .starts_with(\"TypeError: cyclic object value\"),\n        );\n    }\n\n    #[test]\n    fn to_json_undefined() {\n        let mut context = Context::default();\n        let undefined_value = JsValue::undefined();\n        assert!(undefined_value.to_json(&mut context).unwrap().is_none());\n    }\n\n    #[test]\n    fn to_json_undefined_in_structure() {\n        let mut context = Context::default();\n        let object_with_undefined = {\n            // Defining the following structure:\n            // {\n            //     \"outer_a\": 1,\n            //     \"outer_b\": undefined,\n            //     \"outer_c\": [2, undefined, 3, { \"inner_a\": undefined }]\n            // }\n\n            let inner = JsObject::with_null_proto();\n            inner\n                .create_data_property(js_string!(\"inner_a\"), JsValue::undefined(), &mut context)\n                .expect(\"should add property\");\n\n            let array = JsArray::new(&mut context).expect(\"creating array in test must not fail\");\n            array.push(2, &mut context).expect(\"should push\");\n            array\n                .push(JsValue::undefined(), &mut context)\n                .expect(\"should push\");\n            array.push(3, &mut context).expect(\"should push\");\n            array.push(inner, &mut context).expect(\"should push\");\n\n            let outer = JsObject::with_null_proto();\n            outer\n                .create_data_property(js_string!(\"outer_a\"), JsValue::new(1), &mut context)\n                .expect(\"should add property\");\n            outer\n                .create_data_property(js_string!(\"outer_b\"), JsValue::undefined(), &mut context)\n                .expect(\"should add property\");\n            outer\n                .create_data_property(js_string!(\"outer_c\"), array, &mut context)\n                .expect(\"should add property\");\n\n            JsValue::from(outer)\n        };\n\n        assert_eq!(\n            Some(json!({\n                \"outer_a\": 1,\n                \"outer_c\": [2, null, 3, { }]\n            })),\n            object_with_undefined.to_json(&mut context).unwrap()\n        );\n    }\n}\n"
  },
  {
    "path": "core/engine/src/value/conversions/try_from_js/collections.rs",
    "content": "//! [`JsValue`] conversions for std collections.\n\nuse std::collections::{BTreeMap, HashMap};\nuse std::hash::Hash;\n\nuse crate::object::JsMap;\nuse crate::value::TryFromJs;\nuse crate::{Context, JsNativeError, JsResult, JsValue};\n\nimpl<K, V> TryFromJs for BTreeMap<K, V>\nwhere\n    K: TryFromJs + Ord,\n    V: TryFromJs,\n{\n    fn try_from_js(value: &JsValue, context: &mut Context) -> JsResult<Self> {\n        let Some(object) = value.as_object() else {\n            return Err(JsNativeError::typ()\n                .with_message(\"cannot convert value to a BTreeMap\")\n                .into());\n        };\n\n        // JsMap case\n        if let Ok(js_map) = JsMap::from_object(object.clone()) {\n            let mut map = Self::default();\n            js_map.for_each_native(|key, value| {\n                map.insert(\n                    K::try_from_js(&key, context)?,\n                    V::try_from_js(&value, context)?,\n                );\n                Ok(())\n            })?;\n            return Ok(map);\n        }\n\n        // key-valued JsObject case:\n        let keys = object.__own_property_keys__(context)?;\n\n        keys.into_iter()\n            .map(|key| {\n                let js_value = object.get(key.clone(), context)?;\n                let js_key: JsValue = key.into();\n\n                let key = K::try_from_js(&js_key, context)?;\n                let value = V::try_from_js(&js_value, context)?;\n\n                Ok((key, value))\n            })\n            .collect()\n    }\n}\n\nimpl<K, V, S> TryFromJs for HashMap<K, V, S>\nwhere\n    K: TryFromJs + Ord + Hash,\n    V: TryFromJs,\n    S: std::hash::BuildHasher + Default,\n{\n    fn try_from_js(value: &JsValue, context: &mut Context) -> JsResult<Self> {\n        let Some(object) = value.as_object() else {\n            return Err(JsNativeError::typ()\n                .with_message(\"cannot convert value to a BTreeMap\")\n                .into());\n        };\n\n        // JsMap case\n        if let Ok(js_map) = JsMap::from_object(object.clone()) {\n            let mut map = Self::default();\n            js_map.for_each_native(|key, value| {\n                map.insert(\n                    K::try_from_js(&key, context)?,\n                    V::try_from_js(&value, context)?,\n                );\n                Ok(())\n            })?;\n            return Ok(map);\n        }\n\n        // key-valued JsObject case:\n        let keys = object.__own_property_keys__(context)?;\n\n        keys.into_iter()\n            .map(|key| {\n                let js_value = object.get(key.clone(), context)?;\n                let js_key: JsValue = key.into();\n\n                let key = K::try_from_js(&js_key, context)?;\n                let value = V::try_from_js(&js_value, context)?;\n\n                Ok((key, value))\n            })\n            .collect()\n    }\n}\n"
  },
  {
    "path": "core/engine/src/value/conversions/try_from_js/tuples.rs",
    "content": "//! Implementation of [`TryFromJs`] for tuples.\n//!\n//! Tuples are converted from a JavaScript array, using similar semantics to `TypeScript` tuples:\n//!     - If the tuple is shorter than the array, the extra elements are ignored.\n//!     - If the tuple is longer than the array, the extra elements are `undefined`.\n//!     - If the array is empty, all elements are `undefined`.\n//!\n//! A tuple of size 0 (unit type) does not implement [`TryFromJs`].\n\nuse crate::value::JsValue;\nuse crate::{Context, JsResult};\n\nuse super::TryFromJs;\n\nmacro_rules! impl_try_from_js_for_tuples {\n    ($($name:ident),*) => {\n        impl<$($name: TryFromJs),*> TryFromJs for ($($name,)*) {\n            fn try_from_js(value: &JsValue, context: &mut Context) -> JsResult<Self> {\n                let vec: Vec<JsValue> = value.try_js_into(context)?;\n                let mut iter = vec.into_iter();\n\n                Ok((\n                    $(\n                        $name::try_from_js(&iter.next().unwrap_or_else(JsValue::undefined), context)?,\n                    )*\n                ))\n            }\n        }\n    };\n}\n\nimpl_try_from_js_for_tuples!(A);\nimpl_try_from_js_for_tuples!(A, B);\nimpl_try_from_js_for_tuples!(A, B, C);\nimpl_try_from_js_for_tuples!(A, B, C, D);\nimpl_try_from_js_for_tuples!(A, B, C, D, E);\nimpl_try_from_js_for_tuples!(A, B, C, D, E, F);\nimpl_try_from_js_for_tuples!(A, B, C, D, E, F, G);\nimpl_try_from_js_for_tuples!(A, B, C, D, E, F, G, H);\nimpl_try_from_js_for_tuples!(A, B, C, D, E, F, G, H, I);\nimpl_try_from_js_for_tuples!(A, B, C, D, E, F, G, H, I, J);\n"
  },
  {
    "path": "core/engine/src/value/conversions/try_from_js.rs",
    "content": "//! This module contains the [`TryFromJs`] trait, and conversions to basic Rust types.\n\nuse crate::{Context, JsBigInt, JsNativeError, JsObject, JsResult, JsString, JsValue, js_error};\nuse boa_string::StaticJsStrings;\nuse num_bigint::BigInt;\nuse num_traits::AsPrimitive;\n\nmod collections;\nmod tuples;\n\n/// This trait adds a fallible and efficient conversions from a [`JsValue`] to Rust types.\npub trait TryFromJs: Sized {\n    /// This function tries to convert a JavaScript value into `Self`.\n    fn try_from_js(value: &JsValue, context: &mut Context) -> JsResult<Self>;\n}\n\nimpl JsValue {\n    /// This function is the inverse of [`TryFromJs`]. It tries to convert a [`JsValue`] to a given\n    /// Rust type.\n    pub fn try_js_into<T>(&self, context: &mut Context) -> JsResult<T>\n    where\n        T: TryFromJs,\n    {\n        T::try_from_js(self, context)\n    }\n}\n\nimpl TryFromJs for bool {\n    fn try_from_js(value: &JsValue, _context: &mut Context) -> JsResult<Self> {\n        if let Some(b) = value.as_boolean() {\n            Ok(b)\n        } else {\n            Err(JsNativeError::typ()\n                .with_message(\"cannot convert value to a boolean\")\n                .into())\n        }\n    }\n}\n\nimpl TryFromJs for () {\n    fn try_from_js(_value: &JsValue, _context: &mut Context) -> JsResult<Self> {\n        Ok(())\n    }\n}\n\nimpl TryFromJs for String {\n    fn try_from_js(value: &JsValue, _context: &mut Context) -> JsResult<Self> {\n        if let Some(s) = value.as_string() {\n            s.to_std_string().map_err(|e| {\n                JsNativeError::typ()\n                    .with_message(format!(\"could not convert JsString to Rust string: {e}\"))\n                    .into()\n            })\n        } else {\n            Err(JsNativeError::typ()\n                .with_message(\"cannot convert value to a String\")\n                .into())\n        }\n    }\n}\n\nimpl TryFromJs for JsString {\n    fn try_from_js(value: &JsValue, _context: &mut Context) -> JsResult<Self> {\n        if let Some(s) = value.as_string() {\n            Ok(s.clone())\n        } else {\n            Err(JsNativeError::typ()\n                .with_message(\"cannot convert value to a JsString\")\n                .into())\n        }\n    }\n}\n\nimpl<T> TryFromJs for Option<T>\nwhere\n    T: TryFromJs,\n{\n    fn try_from_js(value: &JsValue, context: &mut Context) -> JsResult<Self> {\n        if value.is_undefined() {\n            Ok(None)\n        } else {\n            Ok(Some(T::try_from_js(value, context)?))\n        }\n    }\n}\n\nimpl<T> TryFromJs for Vec<T>\nwhere\n    T: TryFromJs,\n{\n    fn try_from_js(value: &JsValue, context: &mut Context) -> JsResult<Self> {\n        let Some(object) = &value.as_object() else {\n            return Err(JsNativeError::typ()\n                .with_message(\"cannot convert value to a Vec\")\n                .into());\n        };\n\n        let length = object.get(StaticJsStrings::LENGTH, context)?;\n        // If there's no length, return an error.\n        if length.is_null_or_undefined() {\n            return Err(js_error!(TypeError: \"Not an array\"));\n        }\n        let length = length.to_length(context)?;\n\n        let length = match usize::try_from(length) {\n            Ok(length) => length,\n            Err(e) => {\n                return Err(JsNativeError::typ()\n                    .with_message(format!(\"could not convert length to usize: {e}\"))\n                    .into());\n            }\n        };\n        let mut vec = Vec::with_capacity(length);\n        for i in 0..length {\n            let value = object.get(i, context)?;\n            vec.push(T::try_from_js(&value, context)?);\n        }\n\n        Ok(vec)\n    }\n}\n\nimpl TryFromJs for JsObject {\n    fn try_from_js(value: &JsValue, _context: &mut Context) -> JsResult<Self> {\n        if let Some(o) = value.as_object() {\n            Ok(o.clone())\n        } else {\n            Err(JsNativeError::typ()\n                .with_message(\"cannot convert value to a Object\")\n                .into())\n        }\n    }\n}\n\nimpl TryFromJs for JsBigInt {\n    fn try_from_js(value: &JsValue, _context: &mut Context) -> JsResult<Self> {\n        if let Some(b) = value.as_bigint() {\n            Ok(b.clone())\n        } else {\n            Err(JsNativeError::typ()\n                .with_message(\"cannot convert value to a BigInt\")\n                .into())\n        }\n    }\n}\n\nimpl TryFromJs for BigInt {\n    fn try_from_js(value: &JsValue, _context: &mut Context) -> JsResult<Self> {\n        if let Some(b) = value.as_bigint() {\n            Ok(b.as_inner().clone())\n        } else {\n            Err(JsNativeError::typ()\n                .with_message(\"cannot convert value to a BigInt\")\n                .into())\n        }\n    }\n}\n\nimpl TryFromJs for JsValue {\n    fn try_from_js(value: &JsValue, _context: &mut Context) -> JsResult<Self> {\n        Ok(value.clone())\n    }\n}\n\nimpl TryFromJs for f64 {\n    fn try_from_js(value: &JsValue, _context: &mut Context) -> JsResult<Self> {\n        if let Some(i) = value.0.as_integer32() {\n            Ok(f64::from(i))\n        } else if let Some(f) = value.0.as_float64() {\n            Ok(f)\n        } else {\n            Err(JsNativeError::typ()\n                .with_message(\"cannot convert value to a f64\")\n                .into())\n        }\n    }\n}\n\nfn from_f64<T>(v: f64) -> Option<T>\nwhere\n    T: AsPrimitive<f64>,\n    f64: AsPrimitive<T>,\n{\n    if <f64 as AsPrimitive<T>>::as_(v).as_().to_bits() == v.to_bits() {\n        return Some(v.as_());\n    }\n    None\n}\n\nmacro_rules! impl_try_from_js_integer {\n    ( $( $type: ty ),* ) => {\n        $(\n            impl TryFromJs for $type {\n                fn try_from_js(value: &JsValue, _context: &mut Context) -> JsResult<Self> {\n                    if let Some(i) = value.as_i32() {\n                        i.try_into().map_err(|e| {\n                            JsNativeError::typ()\n                                .with_message(format!(\n                                    concat!(\"cannot convert value to a \", stringify!($type), \": {}\"),\n                                    e)\n                                )\n                                .into()\n                        })\n                    } else if let Some(f) = value.as_number() {\n                        from_f64(f).ok_or_else(|| {\n                            JsNativeError::typ()\n                                .with_message(concat!(\"cannot convert value to a \", stringify!($type)))\n                                .into()\n                        })\n                    } else {\n                        Err(JsNativeError::typ()\n                            .with_message(concat!(\"cannot convert value to a \", stringify!($type)))\n                            .into())\n                    }\n                }\n            }\n        )*\n    }\n}\n\nimpl_try_from_js_integer!(i8, u8, i16, u16, i32, u32, i64, u64, usize, i128, u128);\n\n#[test]\nfn integer_floating_js_value_to_integer() {\n    let context = &mut Context::default();\n\n    assert_eq!(i8::try_from_js(&JsValue::from(4.0), context), Ok(4));\n    assert_eq!(u8::try_from_js(&JsValue::from(4.0), context), Ok(4));\n    assert_eq!(i16::try_from_js(&JsValue::from(4.0), context), Ok(4));\n    assert_eq!(u16::try_from_js(&JsValue::from(4.0), context), Ok(4));\n    assert_eq!(i32::try_from_js(&JsValue::from(4.0), context), Ok(4));\n    assert_eq!(u32::try_from_js(&JsValue::from(4.0), context), Ok(4));\n    assert_eq!(i64::try_from_js(&JsValue::from(4.0), context), Ok(4));\n    assert_eq!(u64::try_from_js(&JsValue::from(4.0), context), Ok(4));\n\n    // Floating with fractional part\n    let result = i32::try_from_js(&JsValue::from(4.000_000_000_000_001), context);\n    assert!(result.is_err());\n\n    // NaN\n    let result = i32::try_from_js(&JsValue::nan(), context);\n    assert!(result.is_err());\n\n    // +Infinity\n    let result = i32::try_from_js(&JsValue::positive_infinity(), context);\n    assert!(result.is_err());\n\n    // -Infinity\n    let result = i32::try_from_js(&JsValue::negative_infinity(), context);\n    assert!(result.is_err());\n}\n\n#[test]\nfn value_into_vec() {\n    use boa_engine::{TestAction, run_test_actions};\n    use indoc::indoc;\n\n    #[derive(Debug, PartialEq, Eq, boa_macros::TryFromJs)]\n    struct TestStruct {\n        inner: bool,\n        my_int: i16,\n        my_vec: Vec<String>,\n    }\n\n    run_test_actions([\n        TestAction::assert_with_op(\n            indoc! {r#\"\n            let value = {\n                inner: true,\n                my_int: 11,\n                my_vec: [\"a\", \"b\", \"c\"]\n            };\n            value\n        \"#},\n            |value, context| {\n                let value = TestStruct::try_from_js(&value, context);\n\n                match value {\n                    Ok(value) => {\n                        value\n                            == TestStruct {\n                                inner: true,\n                                my_int: 11,\n                                my_vec: vec![\"a\".to_string(), \"b\".to_string(), \"c\".to_string()],\n                            }\n                    }\n                    _ => false,\n                }\n            },\n        ),\n        TestAction::assert_with_op(\n            indoc!(\n                r#\"\n            let wrong = {\n                inner: false,\n                my_int: 22,\n                my_vec: [{}, \"e\", \"f\"]\n            };\n            wrong\"#\n            ),\n            |value, context| {\n                let Err(value) = TestStruct::try_from_js(&value, context) else {\n                    return false;\n                };\n                assert!(value.to_string().contains(\"TypeError\"));\n                true\n            },\n        ),\n    ]);\n}\n\n#[test]\nfn value_into_tuple() {\n    use boa_engine::{TestAction, run_test_actions};\n    use indoc::indoc;\n\n    run_test_actions([\n        TestAction::assert_with_op(indoc! {r#\" [42, \"hello\", true] \"#}, |value, context| {\n            type TestType = (i32, String, bool);\n            TestType::try_from_js(&value, context).unwrap() == (42, \"hello\".to_string(), true)\n        }),\n        TestAction::assert_with_op(indoc! {r#\" [42, \"hello\", true] \"#}, |value, context| {\n            type TestType = (i32, String, Option<bool>, Option<u8>);\n            TestType::try_from_js(&value, context).unwrap()\n                == (42, \"hello\".to_string(), Some(true), None)\n        }),\n        TestAction::assert_with_op(indoc! {r#\" [] \"#}, |value, context| {\n            type TestType = (\n                Option<bool>,\n                Option<bool>,\n                Option<bool>,\n                Option<bool>,\n                Option<bool>,\n                Option<bool>,\n                Option<bool>,\n                Option<bool>,\n                Option<bool>,\n                Option<bool>,\n            );\n            TestType::try_from_js(&value, context).unwrap()\n                == (None, None, None, None, None, None, None, None, None, None)\n        }),\n        TestAction::assert_with_op(indoc!(r#\"[42, \"hello\", {}]\"#), |value, context| {\n            type TestType = (i32, String, bool);\n            let Err(value) = TestType::try_from_js(&value, context) else {\n                return false;\n            };\n            assert!(value.to_string().contains(\"TypeError\"));\n            true\n        }),\n        TestAction::assert_with_op(indoc!(r#\"[42, \"hello\"]\"#), |value, context| {\n            type TestType = (i32, String, bool);\n            let Err(value) = TestType::try_from_js(&value, context) else {\n                return false;\n            };\n            assert!(value.to_string().contains(\"TypeError\"));\n            true\n        }),\n    ]);\n}\n\n#[test]\nfn value_into_map() {\n    use boa_engine::{TestAction, run_test_actions};\n    use indoc::indoc;\n\n    run_test_actions([\n        TestAction::assert_with_op(indoc! {r#\" ({ a: 1, b: 2, c: 3 }) \"#}, |value, context| {\n            let value = std::collections::BTreeMap::<String, i32>::try_from_js(&value, context);\n\n            match value {\n                Ok(value) => {\n                    value\n                        == vec![\n                            (\"a\".to_string(), 1),\n                            (\"b\".to_string(), 2),\n                            (\"c\".to_string(), 3),\n                        ]\n                        .into_iter()\n                        .collect::<std::collections::BTreeMap<String, i32>>()\n                }\n                _ => false,\n            }\n        }),\n        TestAction::assert_with_op(indoc! {r#\" ({ a: 1, b: 2, c: 3 }) \"#}, |value, context| {\n            let value = std::collections::HashMap::<String, i32>::try_from_js(&value, context);\n\n            match value {\n                Ok(value) => {\n                    value\n                        == std::collections::HashMap::from_iter(\n                            vec![\n                                (\"a\".to_string(), 1),\n                                (\"b\".to_string(), 2),\n                                (\"c\".to_string(), 3),\n                            ]\n                            .into_iter()\n                            .collect::<std::collections::BTreeMap<String, i32>>(),\n                        )\n                }\n                _ => false,\n            }\n        }),\n    ]);\n}\n\n#[test]\nfn js_map_into_rust_map() -> JsResult<()> {\n    use boa_engine::Source;\n    use std::collections::{BTreeMap, HashMap};\n\n    let js_code = \"new Map([['a', 1], ['b', 3], ['aboba', 42024]])\";\n    let mut context = Context::default();\n\n    let js_value = context.eval(Source::from_bytes(js_code))?;\n\n    let hash_map = HashMap::<String, i32>::try_from_js(&js_value, &mut context)?;\n    let btree_map = BTreeMap::<String, i32>::try_from_js(&js_value, &mut context)?;\n\n    let expect = [(\"a\".into(), 1), (\"aboba\".into(), 42024), (\"b\".into(), 3)];\n\n    let expected_hash_map: HashMap<String, _> = expect.iter().cloned().collect();\n    assert_eq!(expected_hash_map, hash_map);\n\n    let expected_btree_map: BTreeMap<String, _> = expect.iter().cloned().collect();\n    assert_eq!(expected_btree_map, btree_map);\n    Ok(())\n}\n"
  },
  {
    "path": "core/engine/src/value/conversions/try_into_js.rs",
    "content": "use crate::class::Class;\nuse crate::{Context, JsNativeError, JsResult, JsString, JsValue};\n\n/// This trait adds a conversions from a Rust Type into [`JsValue`].\npub trait TryIntoJs: Sized {\n    /// This function tries to convert a `Self` into [`JsValue`].\n    fn try_into_js(&self, context: &mut Context) -> JsResult<JsValue>;\n}\n\nimpl<T> TryIntoJs for T\nwhere\n    T: Class + Clone,\n{\n    fn try_into_js(&self, context: &mut Context) -> JsResult<JsValue> {\n        T::from_data(self.clone(), context).map(JsValue::from)\n    }\n}\n\nimpl TryIntoJs for bool {\n    fn try_into_js(&self, _context: &mut Context) -> JsResult<JsValue> {\n        Ok(JsValue::from(*self))\n    }\n}\n\nimpl TryIntoJs for &str {\n    fn try_into_js(&self, _context: &mut Context) -> JsResult<JsValue> {\n        Ok(JsValue::from(JsString::from(*self)))\n    }\n}\nimpl TryIntoJs for String {\n    fn try_into_js(&self, _context: &mut Context) -> JsResult<JsValue> {\n        Ok(JsValue::from(JsString::from(self.as_str())))\n    }\n}\n\nmacro_rules! impl_try_into_js_by_from {\n    ($t:ty) => {\n        impl TryIntoJs for $t {\n            fn try_into_js(&self, _context: &mut Context) -> JsResult<JsValue> {\n                Ok(JsValue::from(self.clone()))\n            }\n        }\n    };\n    [$($ts:ty),+] => {\n        $(impl_try_into_js_by_from!($ts);)+\n    }\n}\nimpl_try_into_js_by_from![i8, u8, i16, u16, i32, u32, f32, f64];\nimpl_try_into_js_by_from![\n    JsValue,\n    JsString,\n    crate::JsBigInt,\n    crate::JsObject,\n    crate::JsSymbol,\n    crate::object::JsArray,\n    crate::object::JsArrayBuffer,\n    crate::object::JsDataView,\n    crate::object::JsDate,\n    crate::object::JsFunction,\n    crate::object::JsGenerator,\n    crate::object::JsMapIterator,\n    crate::object::JsMap,\n    crate::object::JsSetIterator,\n    crate::object::JsSet,\n    crate::object::JsSharedArrayBuffer,\n    crate::object::JsInt8Array,\n    crate::object::JsInt16Array,\n    crate::object::JsInt32Array,\n    crate::object::JsUint8Array,\n    crate::object::JsUint16Array,\n    crate::object::JsUint32Array,\n    crate::object::JsFloat32Array,\n    crate::object::JsFloat64Array\n];\n\nconst MAX_SAFE_INTEGER_I64: i64 = (1 << 53) - 1;\nconst MIN_SAFE_INTEGER_I64: i64 = -MAX_SAFE_INTEGER_I64;\n\nfn err_outside_safe_range() -> crate::JsError {\n    JsNativeError::typ()\n        .with_message(\"cannot convert value into JsValue: the value is outside the safe range\")\n        .into()\n}\nfn convert_safe_i64(value: i64) -> JsValue {\n    i32::try_from(value).map_or(JsValue::from(value as f64), JsValue::new)\n}\n\nimpl TryIntoJs for i64 {\n    fn try_into_js(&self, _context: &mut Context) -> JsResult<JsValue> {\n        let value = *self;\n        if (MIN_SAFE_INTEGER_I64..MAX_SAFE_INTEGER_I64).contains(&value) {\n            Ok(convert_safe_i64(value))\n        } else {\n            Err(err_outside_safe_range())\n        }\n    }\n}\nimpl TryIntoJs for u64 {\n    fn try_into_js(&self, _context: &mut Context) -> JsResult<JsValue> {\n        let value = *self;\n        if (MAX_SAFE_INTEGER_I64 as u64) < value {\n            Err(err_outside_safe_range())\n        } else {\n            Ok(convert_safe_i64(value as i64))\n        }\n    }\n}\nimpl TryIntoJs for isize {\n    fn try_into_js(&self, _context: &mut Context) -> JsResult<JsValue> {\n        let value = *self as i64;\n        if (MIN_SAFE_INTEGER_I64..MAX_SAFE_INTEGER_I64).contains(&value) {\n            Ok(convert_safe_i64(value))\n        } else {\n            Err(err_outside_safe_range())\n        }\n    }\n}\nimpl TryIntoJs for usize {\n    fn try_into_js(&self, _context: &mut Context) -> JsResult<JsValue> {\n        let value = *self;\n        if (MAX_SAFE_INTEGER_I64 as usize) < value {\n            Err(err_outside_safe_range())\n        } else {\n            Ok(convert_safe_i64(value as i64))\n        }\n    }\n}\nimpl TryIntoJs for i128 {\n    fn try_into_js(&self, _context: &mut Context) -> JsResult<JsValue> {\n        let value = *self;\n        if value < i128::from(MIN_SAFE_INTEGER_I64) || i128::from(MAX_SAFE_INTEGER_I64) < value {\n            Err(err_outside_safe_range())\n        } else {\n            Ok(convert_safe_i64(value as i64))\n        }\n    }\n}\nimpl TryIntoJs for u128 {\n    fn try_into_js(&self, _context: &mut Context) -> JsResult<JsValue> {\n        let value = *self;\n        if (MAX_SAFE_INTEGER_I64 as u128) < value {\n            Err(err_outside_safe_range())\n        } else {\n            Ok(convert_safe_i64(value as i64))\n        }\n    }\n}\n\nimpl<T> TryIntoJs for Option<T>\nwhere\n    T: TryIntoJs,\n{\n    fn try_into_js(&self, context: &mut Context) -> JsResult<JsValue> {\n        match self {\n            Some(x) => x.try_into_js(context),\n            None => Ok(JsValue::undefined()),\n        }\n    }\n}\n\nimpl<T> TryIntoJs for Vec<T>\nwhere\n    T: TryIntoJs,\n{\n    fn try_into_js(&self, context: &mut Context) -> JsResult<JsValue> {\n        let arr = crate::object::JsArray::new(context)?;\n        for value in self {\n            let value = value.try_into_js(context)?;\n            arr.push(value, context)?;\n        }\n        Ok(arr.into())\n    }\n}\n\nmacro_rules! impl_try_into_js_for_tuples {\n    ($($names:ident : $ts:ident),+) => {\n        impl<$($ts: TryIntoJs,)+> TryIntoJs for ($($ts,)+) {\n            fn try_into_js(&self, context: &mut Context) -> JsResult<JsValue> {\n                let ($($names,)+) = self;\n                let arr = crate::object::JsArray::new(context)?;\n                $(arr.push($names.try_into_js(context)?, context)?;)+\n                Ok(arr.into())\n            }\n        }\n    };\n}\n\nimpl_try_into_js_for_tuples!(a: A);\nimpl_try_into_js_for_tuples!(a: A, b: B);\nimpl_try_into_js_for_tuples!(a: A, b: B, c: C);\nimpl_try_into_js_for_tuples!(a: A, b: B, c: C, d: D);\nimpl_try_into_js_for_tuples!(a: A, b: B, c: C, d: D, e: E);\nimpl_try_into_js_for_tuples!(a: A, b: B, c: C, d: D, e: E, f: F);\nimpl_try_into_js_for_tuples!(a: A, b: B, c: C, d: D, e: E, f: F, g: G);\nimpl_try_into_js_for_tuples!(a: A, b: B, c: C, d: D, e: E, f: F, g: G, h: H);\nimpl_try_into_js_for_tuples!(a: A, b: B, c: C, d: D, e: E, f: F, g: G, h: H, i: I);\nimpl_try_into_js_for_tuples!(a: A, b: B, c: C, d: D, e: E, f: F, g: G, h: H, i: I, j: J);\nimpl_try_into_js_for_tuples!(a: A, b: B, c: C, d: D, e: E, f: F, g: G, h: H, i: I, j: J, k: K);\n\nimpl TryIntoJs for () {\n    fn try_into_js(&self, _context: &mut Context) -> JsResult<JsValue> {\n        Ok(JsValue::null())\n    }\n}\n\nimpl<T, S> TryIntoJs for std::collections::HashSet<T, S>\nwhere\n    T: TryIntoJs,\n{\n    fn try_into_js(&self, context: &mut Context) -> JsResult<JsValue> {\n        let set = crate::object::JsSet::new(context);\n        for value in self {\n            let value = value.try_into_js(context)?;\n            set.add(value, context)?;\n        }\n        Ok(set.into())\n    }\n}\n\nimpl<K, V, S> TryIntoJs for std::collections::HashMap<K, V, S>\nwhere\n    K: TryIntoJs,\n    V: TryIntoJs,\n{\n    fn try_into_js(&self, context: &mut Context) -> JsResult<JsValue> {\n        let map = crate::object::JsMap::new(context);\n        for (key, value) in self {\n            let key = key.try_into_js(context)?;\n            let value = value.try_into_js(context)?;\n            map.set(key, value, context)?;\n        }\n        Ok(map.into())\n    }\n}\n\n#[cfg(test)]\nmod try_into_js_tests {\n    use crate::value::{TryFromJs, TryIntoJs};\n    use crate::{Context, JsResult};\n\n    #[test]\n    fn big_int_err() {\n        fn assert<T: TryIntoJs>(int: &T, context: &mut Context) {\n            let expect_err = int.try_into_js(context);\n            assert!(expect_err.is_err());\n        }\n\n        let mut context = Context::default();\n        let context = &mut context;\n\n        let int = (1 << 55) + 17i64;\n        assert(&int, context);\n\n        let int = (1 << 55) + 17u64;\n        assert(&int, context);\n\n        let int = (1 << 55) + 17u128;\n        assert(&int, context);\n\n        let int = (1 << 55) + 17i128;\n        assert(&int, context);\n    }\n\n    #[test]\n    fn int_tuple() -> JsResult<()> {\n        let mut context = Context::default();\n        let context = &mut context;\n\n        let tuple_initial = (\n            -42i8,\n            42u8,\n            1764i16,\n            7641u16,\n            -((1 << 27) + 13),\n            (1 << 27) + 72u32,\n            (1 << 49) + 1793i64,\n            (1 << 49) + 1793u64,\n            -((1 << 49) + 7193i128),\n            (1 << 49) + 9173u128,\n        );\n\n        // it will rewrite without reading, so it's just for auto type resolving.\n        #[allow(unused_assignments)]\n        let mut tuple_after_transform = tuple_initial;\n\n        let js_value = tuple_initial.try_into_js(context)?;\n        tuple_after_transform = TryFromJs::try_from_js(&js_value, context)?;\n\n        assert_eq!(tuple_initial, tuple_after_transform);\n        Ok(())\n    }\n\n    #[test]\n    fn string() -> JsResult<()> {\n        let mut context = Context::default();\n        let context = &mut context;\n\n        let s_init = \"String\".to_string();\n        let js_value = s_init.try_into_js(context)?;\n        let s: String = TryFromJs::try_from_js(&js_value, context)?;\n        assert_eq!(s_init, s);\n        Ok(())\n    }\n\n    #[test]\n    fn vec() -> JsResult<()> {\n        let mut context = Context::default();\n        let context = &mut context;\n\n        let vec_init = vec![(-4i64, 2u64), (15, 15), (32, 23)];\n        let js_value = vec_init.try_into_js(context)?;\n        println!(\"JsValue: {}\", js_value.display());\n        let vec: Vec<(i64, u64)> = TryFromJs::try_from_js(&js_value, context)?;\n        assert_eq!(vec_init, vec);\n        Ok(())\n    }\n}\n"
  },
  {
    "path": "core/engine/src/value/display/arguments.rs",
    "content": "use crate::builtins::function::arguments::MappedArguments;\nuse crate::property::DescriptorKind;\nuse crate::value::display::value;\nuse crate::{JsObject, JsValue, js_string};\nuse std::collections::HashSet;\nuse std::fmt::{self, Write};\n\nconst MAX_ARGUMENTS_TO_LOG: u32 = 100;\n\n/// Formats an Arguments object for display.\n///\n/// Always uses multiline output: (unless `print_children` is false or length is 0)\n/// ```text\n/// [Arguments] {\n///   0: \"first\",\n///   1: 42\n/// }\n/// ```\npub(super) fn log_arguments_to(\n    f: &mut fmt::Formatter<'_>,\n    x: &JsObject,\n    print_internals: bool,\n    print_children: bool,\n) -> fmt::Result {\n    let reported_len = x\n        .borrow()\n        .properties()\n        .get(&js_string!(\"length\").into())\n        .and_then(|d| d.value().cloned())\n        .and_then(|v| v.as_number())\n        .map_or(0u32, |n| n.max(0.0) as u32);\n\n    let len = reported_len.min(MAX_ARGUMENTS_TO_LOG);\n\n    if !print_children {\n        return write!(f, \"Arguments({reported_len})\");\n    }\n\n    if reported_len == 0 {\n        return f.write_str(\"[Arguments] {}\");\n    }\n\n    f.write_str(\"[Arguments] {\\n\")?;\n    for i in 0..len {\n        // For MappedArguments, prefer the live value from the environment parameter map.\n        // Named parameters are backed by environment bindings, not the stored property value,\n        // so reading properties().get(...).value() can return a stale initial value.\n        let mapped_value = x.downcast_ref::<MappedArguments>().and_then(|m| m.get(i));\n\n        write!(f, \"  {i}: \")?;\n\n        if let Some(v) = mapped_value {\n            write!(\n                f,\n                \"{}\",\n                CompactValue {\n                    value: &v,\n                    print_internals\n                }\n            )?;\n        } else {\n            let borrow = x.borrow();\n            if let Some(d) = borrow.properties().get(&i.into()) {\n                match d.kind() {\n                    DescriptorKind::Data { value, .. } => {\n                        if let Some(v) = value {\n                            write!(\n                                f,\n                                \"{}\",\n                                CompactValue {\n                                    value: v,\n                                    print_internals\n                                }\n                            )?;\n                        } else {\n                            f.write_str(\"undefined\")?;\n                        }\n                    }\n                    DescriptorKind::Accessor { get, set } => {\n                        let label = match (get.is_some(), set.is_some()) {\n                            (true, true) => \"[Getter/Setter]\",\n                            (true, false) => \"[Getter]\",\n                            (false, true) => \"[Setter]\",\n                            _ => \"[No Getter/Setter]\",\n                        };\n                        f.write_str(label)?;\n                    }\n                    DescriptorKind::Generic => f.write_str(\"undefined\")?,\n                }\n            } else {\n                f.write_str(\"<empty>\")?;\n            }\n        }\n\n        if i + 1 < len {\n            f.write_char(',')?;\n        }\n        f.write_char('\\n')?;\n    }\n    f.write_str(\"}\")\n}\n\nstruct CompactValue<'a> {\n    value: &'a JsValue,\n    print_internals: bool,\n}\n\nimpl fmt::Display for CompactValue<'_> {\n    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {\n        value::log_value_compact(f, self.value, 0, self.print_internals, &mut HashSet::new())\n    }\n}\n"
  },
  {
    "path": "core/engine/src/value/display/array.rs",
    "content": "use std::collections::HashSet;\nuse std::fmt;\n\nuse crate::{JsObject, JsValue, js_string, property::DescriptorKind};\n\npub(super) fn log_array_to(\n    f: &mut fmt::Formatter<'_>,\n    x: &JsObject,\n    print_internals: bool,\n    print_children: bool,\n) -> fmt::Result {\n    let len = x\n        .borrow()\n        .properties()\n        .get(&js_string!(\"length\").into())\n        .expect(\"array object must have 'length' property\")\n        .value()\n        .and_then(JsValue::as_number)\n        .map(|n| n.max(0.0) as u32)\n        .unwrap_or_default();\n\n    if print_children {\n        // Cap the number of elements we iterate over to avoid hangs on very large/sparse arrays.\n        const MAX_ELEMENTS_TO_PRINT: u32 = 1000;\n        let elems_to_print = len.min(MAX_ELEMENTS_TO_PRINT);\n\n        if elems_to_print == 0 {\n            return f.write_str(\"[]\");\n        }\n\n        f.write_str(\"[ \")?;\n        let mut first = true;\n\n        for i in 0..elems_to_print {\n            if first {\n                first = false;\n            } else {\n                f.write_str(\", \")?;\n            }\n\n            if let Some(desc) = x.borrow().properties().get(&i.into()) {\n                match desc.kind() {\n                    DescriptorKind::Data { value, .. } => {\n                        if let Some(value) = value {\n                            super::value::log_value_to(f, value, print_internals, false)?;\n                        } else {\n                            f.write_str(\"undefined\")?;\n                        }\n                    }\n                    DescriptorKind::Accessor { get, set } => {\n                        let display = match (get.is_some(), set.is_some()) {\n                            (true, true) => \"[Getter/Setter]\",\n                            (true, false) => \"[Getter]\",\n                            (false, true) => \"[Setter]\",\n                            _ => \"<empty>\",\n                        };\n                        f.write_str(display)?;\n                    }\n                    DescriptorKind::Generic => {\n                        unreachable!(\"found generic descriptor in array\")\n                    }\n                }\n            } else {\n                f.write_str(\"<empty>\")?;\n            }\n        }\n\n        if len > elems_to_print {\n            if !first {\n                f.write_str(\", \")?;\n            }\n            write!(f, \"... {} more items\", len - elems_to_print)?;\n        }\n        write!(f, \" ]\")\n    } else {\n        write!(f, \"Array({len})\")\n    }\n}\n\npub(super) fn log_array_compact(\n    f: &mut fmt::Formatter<'_>,\n    x: &JsObject,\n    depth: u32,\n    print_internals: bool,\n    encounters: &mut HashSet<usize>,\n) -> fmt::Result {\n    let len = x\n        .borrow()\n        .properties()\n        .get(&js_string!(\"length\").into())\n        .expect(\"array object must have 'length' property\")\n        .value()\n        .and_then(JsValue::as_number)\n        .map(|n| n.max(0.0) as u32)\n        .unwrap_or_default();\n\n    if len == 0 {\n        return f.write_str(\"[]\");\n    }\n\n    f.write_str(\"[ \")?;\n    let mut first = true;\n    for i in 0..len {\n        if first {\n            first = false;\n        } else {\n            f.write_str(\", \")?;\n        }\n        if let Some(desc) = x.borrow().properties().get(&i.into()) {\n            match desc.kind() {\n                DescriptorKind::Data { value, .. } => {\n                    if let Some(value) = value {\n                        super::value::log_value_compact(\n                            f,\n                            value,\n                            depth + 1,\n                            print_internals,\n                            encounters,\n                        )?;\n                    } else {\n                        f.write_str(\"undefined\")?;\n                    }\n                }\n                DescriptorKind::Accessor { get, set } => {\n                    let display = match (get.is_some(), set.is_some()) {\n                        (true, true) => \"[Getter/Setter]\",\n                        (true, false) => \"[Getter]\",\n                        (false, true) => \"[Setter]\",\n                        _ => \"<empty>\",\n                    };\n                    f.write_str(display)?;\n                }\n                DescriptorKind::Generic => {\n                    unreachable!(\"found generic descriptor in array\")\n                }\n            }\n        } else {\n            f.write_str(\"<empty>\")?;\n        }\n    }\n    f.write_str(\" ]\")\n}\n"
  },
  {
    "path": "core/engine/src/value/display/map.rs",
    "content": "use std::collections::HashSet;\nuse std::fmt;\n\nuse crate::{JsObject, JsValue, builtins::map::ordered_map::OrderedMap};\n\npub(super) fn log_map_to(\n    f: &mut fmt::Formatter<'_>,\n    v: &JsObject,\n    print_internals: bool,\n    print_children: bool,\n) -> fmt::Result {\n    let map = v\n        .downcast_ref::<OrderedMap<JsValue>>()\n        .expect(\"object must be a map\");\n    let size = map.len();\n\n    if size == 0 {\n        return f.write_str(\"Map(0)\");\n    }\n\n    if !print_children {\n        return write!(f, \"Map({size})\");\n    }\n\n    f.write_str(\"Map { \")?;\n    let mut first = true;\n    for (key, value) in map.iter() {\n        if !first {\n            f.write_str(\", \")?;\n        }\n        first = false;\n        super::value::log_value_to(f, key, print_internals, false)?;\n        f.write_str(\" \\u{2192} \")?;\n        super::value::log_value_to(f, value, print_internals, false)?;\n    }\n    f.write_str(\" }\")\n}\n\npub(super) fn log_map_compact(\n    f: &mut fmt::Formatter<'_>,\n    v: &JsObject,\n    depth: u32,\n    print_internals: bool,\n    encounters: &mut HashSet<usize>,\n) -> fmt::Result {\n    let map = v\n        .downcast_ref::<OrderedMap<JsValue>>()\n        .expect(\"object must be a map\");\n    let size = map.len();\n\n    if size == 0 {\n        return f.write_str(\"Map(0)\");\n    }\n\n    if depth >= super::value::COMPACT_DEPTH_LIMIT {\n        return write!(f, \"Map({size})\");\n    }\n\n    f.write_str(\"Map { \")?;\n    let mut first = true;\n    for (key, value) in map.iter() {\n        if !first {\n            f.write_str(\", \")?;\n        }\n        first = false;\n        super::value::log_value_compact(f, key, depth + 1, print_internals, encounters)?;\n        f.write_str(\" \\u{2192} \")?;\n        super::value::log_value_compact(f, value, depth + 1, print_internals, encounters)?;\n    }\n    f.write_str(\" }\")\n}\n"
  },
  {
    "path": "core/engine/src/value/display/mod.rs",
    "content": "use std::fmt::{self, Display};\n\nuse crate::JsValue;\n\nmod arguments;\nmod array;\nmod map;\nmod object;\nmod primitives;\nmod set;\nmod typed_array;\nmod value;\n\n/// This object is used for displaying a `Value`.\n#[derive(Debug, Clone, Copy)]\npub struct ValueDisplay<'value> {\n    pub(super) value: &'value JsValue,\n    pub(super) internals: bool,\n}\n\nimpl ValueDisplay<'_> {\n    /// Display internal information about value.\n    ///\n    /// By default this is `false`.\n    #[inline]\n    #[must_use]\n    pub const fn internals(mut self, yes: bool) -> Self {\n        self.internals = yes;\n        self\n    }\n}\n\nimpl Display for ValueDisplay<'_> {\n    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {\n        value::log_value_to(f, self.value, self.internals, true)\n    }\n}\n"
  },
  {
    "path": "core/engine/src/value/display/object.rs",
    "content": "use std::collections::HashSet;\nuse std::fmt::{self, Display, Write};\n\nuse crate::{\n    JsObject, JsString, JsValue, js_string,\n    property::{DescriptorKind, PropertyKey},\n};\n\nfn print_obj_value_internals(\n    f: &mut fmt::Formatter<'_>,\n    obj: &JsObject,\n    indent: usize,\n    encounters: &mut HashSet<usize>,\n) -> fmt::Result {\n    let object = obj.borrow();\n    write!(f, \"{:>indent$}__proto__: \", \"\")?;\n    if let Some(object) = object.prototype() {\n        log_object_to_internal(\n            f,\n            &object.clone().into(),\n            encounters,\n            indent.wrapping_add(4),\n            true,\n        )?;\n    } else {\n        write!(f, \"{}\", JsValue::null().display())?;\n    }\n    f.write_char(',')?;\n    f.write_char('\\n')\n}\n\nfn print_obj_value_props(\n    f: &mut fmt::Formatter<'_>,\n    obj: &JsObject,\n    indent: usize,\n    encounters: &mut HashSet<usize>,\n    print_internals: bool,\n) -> fmt::Result {\n    let mut keys: Vec<_> = obj\n        .borrow()\n        .properties()\n        .index_property_keys()\n        .map(PropertyKey::from)\n        .collect();\n    keys.extend(obj.borrow().properties().shape.keys());\n\n    let mut first = true;\n    for key in keys {\n        if first {\n            first = false;\n        } else {\n            f.write_char(',')?;\n            f.write_char('\\n')?;\n        }\n        let val = obj\n            .borrow()\n            .properties()\n            .get(&key)\n            .expect(\"There should be a value\");\n\n        write!(f, \"{:>width$}{}: \", \"\", key, width = indent)?;\n        if val.is_data_descriptor() {\n            let v = val.expect_value();\n            log_object_to_internal(f, v, encounters, indent.wrapping_add(4), print_internals)?;\n        } else {\n            let display = match (val.set().is_some(), val.get().is_some()) {\n                (true, true) => \"Getter & Setter\",\n                (true, false) => \"Setter\",\n                (false, true) => \"Getter\",\n                _ => \"No Getter/Setter\",\n            };\n            write!(f, \"{display}\")?;\n        }\n    }\n    f.write_char('\\n')?;\n    Ok(())\n}\n\npub(super) fn log_object_to_internal(\n    f: &mut fmt::Formatter<'_>,\n    data: &JsValue,\n    encounters: &mut HashSet<usize>,\n    indent: usize,\n    print_internals: bool,\n) -> fmt::Result {\n    if let Some(v) = data.as_object() {\n        // The in-memory address of the current object\n        let addr = std::ptr::from_ref(v.as_ref()).addr();\n\n        // We need not continue if this object has already been\n        // printed up the current chain\n        if encounters.contains(&addr) {\n            return f.write_str(\"[Cycle]\");\n        }\n\n        // Mark the current object as encountered\n        encounters.insert(addr);\n\n        if v.is::<crate::builtins::Array>() {\n            encounters.remove(&addr);\n            return super::array::log_array_to(f, &v, print_internals, false);\n        }\n\n        if v.is::<crate::builtins::typed_array::TypedArray>() {\n            encounters.remove(&addr);\n            return super::typed_array::log_typed_array(f, &v, true, print_internals);\n        }\n\n        let constructor_name = get_constructor_name_of(&v);\n        if let Some(name) = constructor_name {\n            write!(f, \"{} \", name.to_std_string_lossy())?;\n        }\n        f.write_str(\"{\\n\")?;\n\n        if print_internals {\n            print_obj_value_internals(f, &v, indent, encounters)?;\n        }\n        print_obj_value_props(f, &v, indent, encounters, print_internals)?;\n        write!(f, \"{:>indent$}}}\", \"\", indent = indent.saturating_sub(4))?;\n\n        // If the current object is referenced in a different branch,\n        // it will not cause an infinite printing loop, so it is safe to be printed again\n        encounters.remove(&addr);\n        Ok(())\n    } else {\n        // Every other type of data is printed with the display method\n        super::value::log_value_to(f, data, print_internals, false)\n    }\n}\n\n/// The constructor can be retrieved as `Object.getPrototypeOf(obj).constructor`.\n///\n/// Returns `None` if the constructor is `Object` as plain objects don't need a name.\nfn get_constructor_name_of(obj: &JsObject) -> Option<JsString> {\n    let prototype = obj.prototype()?;\n\n    // To neglect out plain object\n    // `Object.getPrototypeOf(Object.prototype)` => null.\n    // For user created `Object.create(Object.create(null))`,\n    // we also don't need to display its name.\n    prototype.prototype()?;\n\n    let constructor_property = prototype\n        .borrow()\n        .properties()\n        .get(&PropertyKey::from(js_string!(\"constructor\")))?;\n    let constructor = constructor_property.value()?;\n\n    let name = constructor\n        .as_object()?\n        .borrow()\n        .properties()\n        .get(&PropertyKey::from(js_string!(\"name\")))?\n        .value()?\n        .as_string()?;\n\n    Some(name)\n}\n\npub(super) fn log_plain_object_compact(\n    f: &mut fmt::Formatter<'_>,\n    obj: &JsObject,\n    depth: u32,\n    print_internals: bool,\n    encounters: &mut HashSet<usize>,\n) -> fmt::Result {\n    let addr = std::ptr::from_ref(obj.as_ref()).addr();\n    if encounters.contains(&addr) {\n        return f.write_str(\"[Circular *]\");\n    }\n    encounters.insert(addr);\n\n    let mut keys: Vec<_> = obj\n        .borrow()\n        .properties()\n        .index_property_keys()\n        .map(PropertyKey::from)\n        .collect();\n    keys.extend(obj.borrow().properties().shape.keys());\n\n    if keys.is_empty() {\n        encounters.remove(&addr);\n        return f.write_str(\"{}\");\n    }\n\n    f.write_str(\"{ \")?;\n    let mut first = true;\n    for key in &keys {\n        if first {\n            first = false;\n        } else {\n            f.write_str(\", \")?;\n        }\n        write!(f, \"{key}: \")?;\n\n        if let Some(val) = obj.borrow().properties().get(key) {\n            match val.kind() {\n                DescriptorKind::Data { value, .. } => {\n                    if let Some(value) = value {\n                        super::value::log_value_compact(\n                            f,\n                            value,\n                            depth + 1,\n                            print_internals,\n                            encounters,\n                        )?;\n                    } else {\n                        f.write_str(\"undefined\")?;\n                    }\n                }\n                DescriptorKind::Accessor { get, set } => {\n                    let display = match (get.is_some(), set.is_some()) {\n                        (true, true) => \"[Getter/Setter]\",\n                        (true, false) => \"[Getter]\",\n                        (false, true) => \"[Setter]\",\n                        _ => \"[No Getter/Setter]\",\n                    };\n                    f.write_str(display)?;\n                }\n                DescriptorKind::Generic => {\n                    f.write_str(\"undefined\")?;\n                }\n            }\n        }\n    }\n    f.write_str(\" }\")?;\n\n    encounters.remove(&addr);\n    Ok(())\n}\n\nimpl JsValue {\n    /// A helper function for specifically printing object values\n    #[must_use]\n    pub fn display_obj(&self, print_internals: bool) -> String {\n        struct DisplayObj<'a>(&'a JsValue, bool);\n        impl Display for DisplayObj<'_> {\n            fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {\n                log_object_to_internal(f, self.0, &mut HashSet::new(), 4, self.1)\n            }\n        }\n\n        DisplayObj(self, print_internals).to_string()\n    }\n}\n"
  },
  {
    "path": "core/engine/src/value/display/primitives.rs",
    "content": "use std::fmt;\n\n/// Formats a rational (floating-point) number for display.\n///\n/// This is different from the ECMAScript compliant number to string, in the printing of `-0`.\n///\n/// This function prints `-0` as `-0` instead of positive `0` as the specification says.\n/// This is done to make it easier for the user of the REPL to identify what is a `-0` vs `0`,\n/// since the REPL is not bound to the ECMAScript specification, we can do this.\npub(super) fn format_rational(v: f64, f: &mut fmt::Formatter<'_>) -> fmt::Result {\n    if v.is_sign_negative() && v == 0.0 {\n        f.write_str(\"-0\")\n    } else {\n        let mut buffer = ryu_js::Buffer::new();\n        f.write_str(buffer.format(v))\n    }\n}\n"
  },
  {
    "path": "core/engine/src/value/display/set.rs",
    "content": "use std::collections::HashSet;\nuse std::fmt;\n\nuse crate::{JsObject, builtins::set::ordered_set::OrderedSet};\n\npub(super) fn log_set_to(\n    f: &mut fmt::Formatter<'_>,\n    v: &JsObject,\n    print_internals: bool,\n    print_children: bool,\n) -> fmt::Result {\n    let set = v\n        .downcast_ref::<OrderedSet>()\n        .expect(\"object must be a set\");\n    let size = set.len();\n\n    if size == 0 {\n        return f.write_str(\"Set(0)\");\n    }\n\n    if !print_children {\n        return write!(f, \"Set({size})\");\n    }\n\n    f.write_str(\"Set { \")?;\n    let mut first = true;\n    for value in set.iter() {\n        if !first {\n            f.write_str(\", \")?;\n        }\n        first = false;\n        super::value::log_value_to(f, value, print_internals, false)?;\n    }\n    f.write_str(\" }\")\n}\n\npub(super) fn log_set_compact(\n    f: &mut fmt::Formatter<'_>,\n    v: &JsObject,\n    depth: u32,\n    print_internals: bool,\n    encounters: &mut HashSet<usize>,\n) -> fmt::Result {\n    let set = v\n        .downcast_ref::<OrderedSet>()\n        .expect(\"object must be a set\");\n    let size = set.len();\n\n    if size == 0 {\n        return f.write_str(\"Set(0)\");\n    }\n\n    if depth >= super::value::COMPACT_DEPTH_LIMIT {\n        return write!(f, \"Set({size})\");\n    }\n\n    f.write_str(\"Set { \")?;\n    let mut first = true;\n    for value in set.iter() {\n        if !first {\n            f.write_str(\", \")?;\n        }\n        first = false;\n        super::value::log_value_compact(f, value, depth + 1, print_internals, encounters)?;\n    }\n    f.write_str(\" }\")\n}\n"
  },
  {
    "path": "core/engine/src/value/display/typed_array.rs",
    "content": "use std::fmt;\nuse std::sync::atomic::Ordering;\n\nuse crate::{JsObject, JsValue, builtins::typed_array::TypedArray};\n\n/// Formats a `TypedArray` object for display, e.g. `Uint8Array(3) [ 1, 2, 3 ]`.\n///\n/// If `show_elements` is `false`, only the type name and length are printed (e.g. `Uint8Array(3)`).\npub(super) fn log_typed_array(\n    f: &mut fmt::Formatter<'_>,\n    obj: &JsObject,\n    show_elements: bool,\n    print_internals: bool,\n) -> fmt::Result {\n    let inner = obj\n        .downcast_ref::<TypedArray>()\n        .expect(\"must be a TypedArray object\");\n    let kind = inner.kind();\n    let type_name = kind.js_name().to_std_string_lossy();\n\n    let viewed_buf = inner.viewed_array_buffer();\n    let buf_ref = viewed_buf.as_buffer();\n    let Some(buf_bytes) = buf_ref.bytes(Ordering::Relaxed) else {\n        if show_elements {\n            return write!(f, \"{type_name}(0) []\");\n        }\n        return write!(f, \"{type_name}(0)\");\n    };\n    let buf_len = buf_bytes.len();\n    if inner.is_out_of_bounds(buf_len) {\n        if show_elements {\n            return write!(f, \"{type_name}(0) []\");\n        }\n        return write!(f, \"{type_name}(0)\");\n    }\n\n    let length = inner.array_length(buf_len);\n\n    if !show_elements {\n        return write!(f, \"{type_name}({length})\");\n    }\n\n    if length == 0 {\n        return write!(f, \"{type_name}(0) []\");\n    }\n\n    write!(f, \"{type_name}({length}) [ \")?;\n\n    let offset = inner.byte_offset() as usize;\n    let elem_size = kind.element_size() as usize;\n\n    for i in 0..length as usize {\n        if i > 0 {\n            f.write_str(\", \")?;\n        }\n        let byte_index = offset + i * elem_size;\n        // SAFETY: `array_length` verified all indices are within bounds.\n        // TypedArray invariants guarantee the buffer is correctly aligned at `byte_offset`.\n        let element = unsafe {\n            buf_bytes\n                .subslice(byte_index..)\n                .get_value(kind, Ordering::Relaxed)\n        };\n        let value: JsValue = element.into();\n        super::value::log_value_to(f, &value, print_internals, false)?;\n    }\n\n    f.write_str(\" ]\")\n}\n"
  },
  {
    "path": "core/engine/src/value/display/value.rs",
    "content": "use std::collections::HashSet;\nuse std::fmt::{self, Display};\n\nuse crate::{\n    JsError, JsString, JsValue, JsVariant,\n    builtins::{\n        Array, Promise,\n        error::Error,\n        function::{\n            OrdinaryFunction,\n            arguments::{MappedArguments, UnmappedArguments},\n        },\n        map::ordered_map::OrderedMap,\n        promise::PromiseState,\n        set::ordered_set::OrderedSet,\n        typed_array::TypedArray,\n        weak_map::NativeWeakMap,\n        weak_set::NativeWeakSet,\n    },\n    js_string,\n    property::{PropertyDescriptor, PropertyKey},\n};\n\n/// Maximum nesting depth before objects/arrays are collapsed\npub(super) const COMPACT_DEPTH_LIMIT: u32 = 2;\n\npub(crate) fn log_value_to(\n    f: &mut fmt::Formatter<'_>,\n    x: &JsValue,\n    print_internals: bool,\n    print_children: bool,\n) -> fmt::Result {\n    match x.variant() {\n        // We don't want to print private (compiler) or prototype properties\n        JsVariant::Object(v) => {\n            // Can use the private \"type\" field of an Object to match on\n            // which type of Object it represents for special printing\n            if let Some(s) = v.downcast_ref::<JsString>() {\n                write!(f, \"String {{ {:?} }}\", s.to_std_string_escaped())\n            } else if let Some(b) = v.downcast_ref::<bool>() {\n                write!(f, \"Boolean {{ {b} }}\")\n            } else if let Some(r) = v.downcast_ref::<f64>() {\n                f.write_str(\"Number { \")?;\n                super::primitives::format_rational(*r, f)?;\n                f.write_str(\" }\")\n            } else if v.is::<Array>() {\n                super::array::log_array_to(f, &v, print_internals, print_children)\n            } else if v.is::<UnmappedArguments>() || v.is::<MappedArguments>() {\n                super::arguments::log_arguments_to(f, &v, print_internals, print_children)\n            } else if v.downcast_ref::<OrderedMap<JsValue>>().is_some() {\n                super::map::log_map_to(f, &v, print_internals, print_children)\n            } else if v.downcast_ref::<OrderedSet>().is_some() {\n                super::set::log_set_to(f, &v, print_internals, print_children)\n            } else if v.downcast_ref::<NativeWeakMap>().is_some() {\n                f.write_str(\"WeakMap { <items unknown> }\")\n            } else if v.downcast_ref::<NativeWeakSet>().is_some() {\n                f.write_str(\"WeakSet { <items unknown> }\")\n            } else if v.is::<Error>() {\n                let name: std::borrow::Cow<'static, str> = v\n                    .get_property(&js_string!(\"name\").into())\n                    .as_ref()\n                    .and_then(PropertyDescriptor::value)\n                    .map_or_else(\n                        || \"<error>\".into(),\n                        |v| {\n                            v.as_string()\n                                .as_ref()\n                                .map_or_else(\n                                    || v.display().to_string(),\n                                    JsString::to_std_string_escaped,\n                                )\n                                .into()\n                        },\n                    );\n                let message = v\n                    .get_property(&js_string!(\"message\").into())\n                    .as_ref()\n                    .and_then(PropertyDescriptor::value)\n                    .map(|v| {\n                        v.as_string().as_ref().map_or_else(\n                            || v.display().to_string(),\n                            JsString::to_std_string_escaped,\n                        )\n                    })\n                    .unwrap_or_default();\n                if name.is_empty() {\n                    f.write_str(&message)?;\n                } else if message.is_empty() {\n                    f.write_str(name.as_ref())?;\n                } else {\n                    write!(f, \"{name}: {message}\")?;\n                }\n                let data = v\n                    .downcast_ref::<Error>()\n                    .expect(\"already checked object type\");\n\n                if let Some(position) = &data.position.0 {\n                    write!(f, \"{position}\")?;\n                }\n                Ok(())\n            } else if let Some(promise) = v.downcast_ref::<Promise>() {\n                f.write_str(\"Promise { \")?;\n                match promise.state() {\n                    PromiseState::Pending => f.write_str(\"<pending>\")?,\n                    PromiseState::Fulfilled(val) => Display::fmt(&val.display(), f)?,\n                    PromiseState::Rejected(reason) => {\n                        write!(f, \"<rejected> {}\", JsError::from_opaque(reason.clone()))?;\n                    }\n                }\n                f.write_str(\" }\")\n            } else if v.is::<TypedArray>() {\n                super::typed_array::log_typed_array(f, &v, print_children, print_internals)\n            } else if let Some(date) = v.downcast_ref::<crate::builtins::date::Date>() {\n                match date.to_iso_display() {\n                    Some(iso) => f.write_str(&iso),\n                    None => f.write_str(\"Invalid Date\"),\n                }\n            } else if let Some(regexp) = v.downcast_ref::<crate::builtins::regexp::RegExp>() {\n                let source =\n                    escape_regexp_source(&regexp.original_source().to_std_string_escaped());\n                let flags = regexp.original_flags().to_std_string_escaped();\n                write!(f, \"/{source}/{flags}\")\n            } else if v.is_callable() {\n                let name = v\n                    .get_property(&PropertyKey::from(js_string!(\"name\")))\n                    .and_then(|d| Some(d.value()?.as_string()?.to_std_string_escaped()));\n                let is_class = v\n                    .downcast_ref::<OrdinaryFunction>()\n                    .is_some_and(|f| f.code.is_class_constructor());\n\n                if is_class {\n                    match name {\n                        Some(name) if !name.is_empty() => write!(f, \"[class {name}]\"),\n                        _ => f.write_str(\"[class (anonymous)]\"),\n                    }\n                } else {\n                    match name {\n                        Some(name) if !name.is_empty() => write!(f, \"[Function: {name}]\"),\n                        _ => f.write_str(\"[Function (anonymous)]\"),\n                    }\n                }\n            } else {\n                Display::fmt(&x.display_obj(print_internals), f)\n            }\n        }\n        JsVariant::Null => write!(f, \"null\"),\n        JsVariant::Undefined => write!(f, \"undefined\"),\n        JsVariant::Boolean(v) => write!(f, \"{v}\"),\n        JsVariant::Symbol(symbol) => {\n            write!(f, \"{}\", symbol.descriptive_string().to_std_string_escaped())\n        }\n        JsVariant::String(v) => write!(f, \"{:?}\", v.to_std_string_escaped()),\n        JsVariant::Float64(v) => super::primitives::format_rational(v, f),\n        JsVariant::Integer32(v) => write!(f, \"{v}\"),\n        JsVariant::BigInt(num) => write!(f, \"{num}n\"),\n    }\n}\n\n/// Escapes characters in a regex source string so the result is valid inside `/.../` syntax.\n///\n/// Per `RegExp.prototype.toString` (ES2022 §21.2.5.14):\n/// - `/` → `\\/`  (avoids prematurely closing the literal)\n/// - Line terminators (`\\n`, `\\r`, U+2028, U+2029) are escaped so the literal\n///   does not span lines: `\\n` and `\\r` become `\\\\n`/`\\\\r`, U+2028/U+2029\n///   become `\\u2028`/`\\u2029`.\nfn escape_regexp_source(source: &str) -> String {\n    let mut out = String::with_capacity(source.len());\n    for ch in source.chars() {\n        match ch {\n            '/' => out.push_str(\"\\\\/\"),\n            '\\n' => out.push_str(\"\\\\n\"),\n            '\\r' => out.push_str(\"\\\\r\"),\n            '\\u{2028}' => out.push_str(\"\\\\u2028\"),\n            '\\u{2029}' => out.push_str(\"\\\\u2029\"),\n            c => out.push(c),\n        }\n    }\n    out\n}\n\n/// Formats a [`JsValue`] inline and compactly, collapsing deeply-nested objects.\n/// This method is used for printing arguments of a function to the console.\npub(super) fn log_value_compact(\n    f: &mut fmt::Formatter<'_>,\n    x: &JsValue,\n    depth: u32,\n    print_internals: bool,\n    encounters: &mut HashSet<usize>,\n) -> fmt::Result {\n    match x.variant() {\n        JsVariant::Object(v) => {\n            // Reuse the full formatter for cases that are identical in compact and non-compact modes.\n            if v.downcast_ref::<JsString>().is_some()\n                || v.downcast_ref::<bool>().is_some()\n                || v.downcast_ref::<f64>().is_some()\n            {\n                log_value_to(f, x, print_internals, false)\n            } else if v.is::<Array>() {\n                if depth >= COMPACT_DEPTH_LIMIT {\n                    f.write_str(\"[Array]\")\n                } else {\n                    super::array::log_array_compact(f, &v, depth, print_internals, encounters)\n                }\n            } else if v.is::<UnmappedArguments>() || v.is::<MappedArguments>() {\n                f.write_str(\"[Arguments]\")\n            } else if v.downcast_ref::<OrderedMap<JsValue>>().is_some() {\n                super::map::log_map_compact(f, &v, depth, print_internals, encounters)\n            } else if v.downcast_ref::<OrderedSet>().is_some() {\n                super::set::log_set_compact(f, &v, depth, print_internals, encounters)\n            } else if v.downcast_ref::<NativeWeakMap>().is_some() {\n                f.write_str(\"WeakMap { <items unknown> }\")\n            } else if v.downcast_ref::<NativeWeakSet>().is_some() {\n                f.write_str(\"WeakSet { <items unknown> }\")\n            } else if v.downcast_ref::<crate::builtins::date::Date>().is_some()\n                || v.downcast_ref::<crate::builtins::regexp::RegExp>()\n                    .is_some()\n            {\n                log_value_to(f, x, print_internals, false)\n            } else if v.is::<Error>() {\n                log_value_to(f, x, print_internals, true)\n            } else if let Some(promise) = v.downcast_ref::<Promise>() {\n                f.write_str(\"Promise { \")?;\n                match promise.state() {\n                    PromiseState::Pending => f.write_str(\"<pending>\")?,\n                    PromiseState::Fulfilled(val) => {\n                        log_value_compact(f, val, depth + 1, print_internals, encounters)?;\n                    }\n                    PromiseState::Rejected(reason) => {\n                        write!(f, \"<rejected> {}\", JsError::from_opaque(reason.clone()))?;\n                    }\n                }\n                f.write_str(\" }\")\n            } else if v.is::<TypedArray>() {\n                super::typed_array::log_typed_array(\n                    f,\n                    &v,\n                    depth < COMPACT_DEPTH_LIMIT,\n                    print_internals,\n                )\n            } else if v.is_callable() {\n                log_value_to(f, x, print_internals, false)\n            } else {\n                // Plain object\n                if depth >= COMPACT_DEPTH_LIMIT {\n                    f.write_str(\"[Object]\")\n                } else {\n                    super::object::log_plain_object_compact(\n                        f,\n                        &v,\n                        depth,\n                        print_internals,\n                        encounters,\n                    )\n                }\n            }\n        }\n        // All non-object variants are formatted the same in compact and non-compact modes.\n        _ => log_value_to(f, x, print_internals, false),\n    }\n}\n"
  },
  {
    "path": "core/engine/src/value/equality.rs",
    "content": "use super::{JsBigInt, JsObject, JsResult, JsValue, PreferredType};\n#[cfg(feature = \"annex-b\")]\nuse crate::builtins::is_html_dda::IsHTMLDDA;\nuse crate::{Context, JsVariant, builtins::Number};\nuse std::collections::HashSet;\n\nimpl JsValue {\n    /// Inner loop of the deep equality comparison, strict.\n    pub(crate) fn deep_strict_equals_inner(\n        &self,\n        other: &Self,\n        encounters: &mut HashSet<usize>,\n        context: &mut Context,\n    ) -> JsResult<bool> {\n        match (self.as_object(), other.as_object()) {\n            (None, None) => Ok(self.strict_equals(other)),\n            (Some(x), Some(y)) => JsObject::deep_strict_equals_inner(&x, &y, encounters, context),\n            _ => Ok(false),\n        }\n    }\n\n    /// Deep strict equality.\n    ///\n    /// If the value is an object/array, also compare the key-values.\n    /// It uses `strict_equals()` for non-object values.\n    pub fn deep_strict_equals(&self, other: &Self, context: &mut Context) -> JsResult<bool> {\n        self.deep_strict_equals_inner(other, &mut HashSet::new(), context)\n    }\n\n    /// Strict equality comparison.\n    ///\n    /// This method is executed when doing strict equality comparisons with the `===` operator.\n    /// For more information, check <https://tc39.es/ecma262/#sec-strict-equality-comparison>.\n    #[must_use]\n    pub fn strict_equals(&self, other: &Self) -> bool {\n        // 1. If Type(x) is different from Type(y), return false.\n        if self.get_type() != other.get_type() {\n            return false;\n        }\n\n        match (self.variant(), other.variant()) {\n            // 2. If Type(x) is Number or BigInt, then\n            //    a. Return ! Type(x)::equal(x, y).\n            (JsVariant::BigInt(x), JsVariant::BigInt(y)) => JsBigInt::equal(&x, &y),\n            (JsVariant::Float64(x), JsVariant::Float64(y)) => Number::equal(x, y),\n            (JsVariant::Float64(x), JsVariant::Integer32(y)) => Number::equal(x, f64::from(y)),\n            (JsVariant::Integer32(x), JsVariant::Float64(y)) => Number::equal(f64::from(x), y),\n            (JsVariant::Integer32(x), JsVariant::Integer32(y)) => x == y,\n\n            //Null has to be handled specially because \"typeof null\" returns object and if we managed\n            //this without a special case we would compare self and other as if they were actually\n            //objects which unfortunately fails\n            //Specification Link: https://tc39.es/ecma262/#sec-typeof-operator\n            (JsVariant::Null, JsVariant::Null) => true,\n\n            // 3. Return ! SameValueNonNumeric(x, y).\n            (_, _) => Self::same_value_non_numeric(self, other),\n        }\n    }\n\n    /// Abstract equality comparison.\n    ///\n    /// This method is executed when doing abstract equality comparisons with the `==` operator.\n    ///  For more information, check <https://tc39.es/ecma262/#sec-abstract-equality-comparison>\n    #[allow(clippy::float_cmp)]\n    pub fn equals(&self, other: &Self, context: &mut Context) -> JsResult<bool> {\n        // 1. If Type(x) is the same as Type(y), then\n        //     a. Return the result of performing Strict Equality Comparison x === y.\n        if self.get_type() == other.get_type() {\n            return Ok(self.strict_equals(other));\n        }\n\n        Ok(match (self.variant(), other.variant()) {\n            // 2. If x is null and y is undefined, return true.\n            // 3. If x is undefined and y is null, return true.\n            (JsVariant::Null, JsVariant::Undefined) | (JsVariant::Undefined, JsVariant::Null) => {\n                true\n            }\n\n            // B.3.6.2: Objects with [[IsHTMLDDA]] are loosely equal to null and undefined.\n            #[cfg(feature = \"annex-b\")]\n            (JsVariant::Object(ref obj), JsVariant::Null | JsVariant::Undefined)\n                if obj.is::<IsHTMLDDA>() =>\n            {\n                true\n            }\n            #[cfg(feature = \"annex-b\")]\n            (JsVariant::Null | JsVariant::Undefined, JsVariant::Object(ref obj))\n                if obj.is::<IsHTMLDDA>() =>\n            {\n                true\n            }\n\n            // 3. If Type(x) is Number and Type(y) is String, return the result of the comparison x == ! ToNumber(y).\n            // 4. If Type(x) is String and Type(y) is Number, return the result of the comparison ! ToNumber(x) == y.\n            //\n            // https://github.com/rust-lang/rust/issues/54883\n            (\n                JsVariant::Integer32(_) | JsVariant::Float64(_),\n                JsVariant::String(_) | JsVariant::Boolean(_),\n            )\n            | (JsVariant::String(_), JsVariant::Integer32(_) | JsVariant::Float64(_)) => {\n                let x = self.to_number(context)?;\n                let y = other.to_number(context)?;\n                Number::equal(x, y)\n            }\n\n            // 6. If Type(x) is BigInt and Type(y) is String, then\n            //    a. Let n be ! StringToBigInt(y).\n            //    b. If n is NaN, return false.\n            //    c. Return the result of the comparison x == n.\n            (JsVariant::BigInt(a), JsVariant::String(b)) => JsBigInt::from_js_string(&b) == Some(a),\n\n            // 7. If Type(x) is String and Type(y) is BigInt, return the result of the comparison y == x.\n            (JsVariant::String(a), JsVariant::BigInt(b)) => JsBigInt::from_js_string(&a) == Some(b),\n\n            // 8. If Type(x) is Boolean, return the result of the comparison ! ToNumber(x) == y.\n            (JsVariant::Boolean(x), _) => {\n                return other.equals(&JsValue::new(i32::from(x)), context);\n            }\n\n            // 9. If Type(y) is Boolean, return the result of the comparison x == ! ToNumber(y).\n            (_, JsVariant::Boolean(y)) => return self.equals(&JsValue::new(i32::from(y)), context),\n\n            // 10. If Type(x) is either String, Number, BigInt, or Symbol and Type(y) is Object, return the result\n            // of the comparison x == ? ToPrimitive(y).\n            (\n                JsVariant::Object(_),\n                JsVariant::String(_)\n                | JsVariant::Float64(_)\n                | JsVariant::Integer32(_)\n                | JsVariant::BigInt(_)\n                | JsVariant::Symbol(_),\n            ) => {\n                let primitive = self.to_primitive(context, PreferredType::Default)?;\n                return Ok(primitive\n                    .equals(other, context)\n                    .expect(\"should not fail according to spec\"));\n            }\n\n            // 11. If Type(x) is Object and Type(y) is either String, Number, BigInt, or Symbol, return the result\n            // of the comparison ? ToPrimitive(x) == y.\n            (\n                JsVariant::String(_)\n                | JsVariant::Float64(_)\n                | JsVariant::Integer32(_)\n                | JsVariant::BigInt(_)\n                | JsVariant::Symbol(_),\n                JsVariant::Object(_),\n            ) => {\n                let primitive = other.to_primitive(context, PreferredType::Default)?;\n                return Ok(primitive\n                    .equals(self, context)\n                    .expect(\"should not fail according to spec\"));\n            }\n\n            // 12. If Type(x) is BigInt and Type(y) is Number, or if Type(x) is Number and Type(y) is BigInt, then\n            //    a. If x or y are any of NaN, +∞, or -∞, return false.\n            //    b. If the mathematical value of x is equal to the mathematical value of y, return true; otherwise return false.\n            (JsVariant::BigInt(a), JsVariant::Float64(b)) => a == b,\n            (JsVariant::Float64(a), JsVariant::BigInt(b)) => a == b,\n            (JsVariant::BigInt(a), JsVariant::Integer32(b)) => a == b,\n            (JsVariant::Integer32(a), JsVariant::BigInt(b)) => a == b,\n\n            // 13. Return false.\n            _ => false,\n        })\n    }\n\n    /// Abstract non-equality comparison.\n    ///\n    /// This method is executed when doing abstract equality comparisons with the `!=` operator.\n    /// It uses [`Self::equals`] to perform the comparison and negates the result.\n    pub fn not_equals(&self, other: &Self, context: &mut Context) -> JsResult<bool> {\n        Ok(!self.equals(other, context)?)\n    }\n\n    /// The internal comparison abstract operation SameValue(x, y),\n    /// where x and y are ECMAScript language values, produces true or false.\n    ///\n    /// More information:\n    ///  - [ECMAScript][spec]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-samevalue\n    #[must_use]\n    pub fn same_value(x: &Self, y: &Self) -> bool {\n        // 1. If Type(x) is different from Type(y), return false.\n        if x.get_type() != y.get_type() {\n            return false;\n        }\n\n        match (x.variant(), y.variant()) {\n            // 2. If Type(x) is Number or BigInt, then\n            //    a. Return ! Type(x)::SameValue(x, y).\n            (JsVariant::BigInt(x), JsVariant::BigInt(y)) => JsBigInt::same_value(&x, &y),\n            (JsVariant::Float64(x), JsVariant::Float64(y)) => Number::same_value(x, y),\n            (JsVariant::Float64(x), JsVariant::Integer32(y)) => Number::same_value(x, f64::from(y)),\n            (JsVariant::Integer32(x), JsVariant::Float64(y)) => Number::same_value(f64::from(x), y),\n            (JsVariant::Integer32(x), JsVariant::Integer32(y)) => x == y,\n\n            // 3. Return ! SameValueNonNumeric(x, y).\n            (_, _) => Self::same_value_non_numeric(x, y),\n        }\n    }\n\n    /// The internal comparison abstract operation `SameValueZero(x, y)`,\n    /// where `x` and `y` are ECMAScript language values, produces `true` or `false`.\n    ///\n    /// `SameValueZero` differs from `SameValue` only in its treatment of `+0` and `-0`.\n    ///\n    /// More information:\n    ///  - [ECMAScript][spec]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-samevaluezero\n    #[must_use]\n    pub fn same_value_zero(x: &Self, y: &Self) -> bool {\n        if x.get_type() != y.get_type() {\n            return false;\n        }\n\n        match (x.variant(), y.variant()) {\n            // 2. If Type(x) is Number or BigInt, then\n            //    a. Return ! Type(x)::SameValueZero(x, y).\n            (JsVariant::BigInt(x), JsVariant::BigInt(y)) => JsBigInt::same_value_zero(&x, &y),\n\n            (JsVariant::Float64(x), JsVariant::Float64(y)) => Number::same_value_zero(x, y),\n            (JsVariant::Float64(x), JsVariant::Integer32(y)) => {\n                Number::same_value_zero(x, f64::from(y))\n            }\n            (JsVariant::Integer32(x), JsVariant::Float64(y)) => {\n                Number::same_value_zero(f64::from(x), y)\n            }\n            (JsVariant::Integer32(x), JsVariant::Integer32(y)) => x == y,\n\n            // 3. Return ! SameValueNonNumeric(x, y).\n            (_, _) => Self::same_value_non_numeric(x, y),\n        }\n    }\n\n    fn same_value_non_numeric(x: &Self, y: &Self) -> bool {\n        debug_assert!(x.get_type() == y.get_type());\n        match (x.variant(), y.variant()) {\n            (JsVariant::Null, JsVariant::Null) | (JsVariant::Undefined, JsVariant::Undefined) => {\n                true\n            }\n            (JsVariant::String(x), JsVariant::String(y)) => x == y,\n            (JsVariant::Boolean(x), JsVariant::Boolean(y)) => x == y,\n            (JsVariant::Object(x), JsVariant::Object(y)) => JsObject::equals(&x, &y),\n            (JsVariant::Symbol(x), JsVariant::Symbol(y)) => x == y,\n            _ => false,\n        }\n    }\n}\n"
  },
  {
    "path": "core/engine/src/value/hash.rs",
    "content": "use super::JsValue;\nuse crate::JsVariant;\nuse crate::builtins::Number;\nuse std::hash::{Hash, Hasher};\n\nimpl PartialEq for JsValue {\n    fn eq(&self, other: &Self) -> bool {\n        Self::same_value_zero(self, other)\n    }\n}\n\nimpl Eq for JsValue {}\n\n#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]\nstruct UndefinedHashable;\n\n#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]\nstruct NullHashable;\n\n#[derive(Debug, Clone, Copy)]\nstruct RationalHashable(f64);\n\nimpl PartialEq for RationalHashable {\n    #[inline]\n    fn eq(&self, other: &Self) -> bool {\n        Number::same_value(self.0, other.0)\n    }\n}\n\nimpl Eq for RationalHashable {}\n\nimpl Hash for RationalHashable {\n    fn hash<H: Hasher>(&self, state: &mut H) {\n        self.0.to_bits().hash(state);\n    }\n}\n\nimpl Hash for JsValue {\n    fn hash<H: Hasher>(&self, state: &mut H) {\n        match self.variant() {\n            JsVariant::Undefined => UndefinedHashable.hash(state),\n            JsVariant::Null => NullHashable.hash(state),\n            JsVariant::String(string) => string.hash(state),\n            JsVariant::Boolean(boolean) => boolean.hash(state),\n            JsVariant::Integer32(integer) => RationalHashable(f64::from(integer)).hash(state),\n            JsVariant::BigInt(bigint) => bigint.hash(state),\n            JsVariant::Float64(rational) => RationalHashable(rational).hash(state),\n            JsVariant::Symbol(symbol) => Hash::hash(&symbol, state),\n            JsVariant::Object(object) => object.hash(state),\n        }\n    }\n}\n"
  },
  {
    "path": "core/engine/src/value/inner/legacy.rs",
    "content": "//! This `[JsValue]` inner type is an opaque enum implementing the same\n//! interface as the `NanBoxedValue` type, but using an enum instead of\n//! a 64-bits float.\n\n#[cfg(feature = \"annex-b\")]\nuse crate::builtins::is_html_dda::IsHTMLDDA;\nuse crate::{JsBigInt, JsObject, JsSymbol, value::Type};\nuse boa_engine::JsVariant;\nuse boa_gc::{Finalize, Trace, custom_trace};\nuse boa_string::JsString;\n\n#[derive(Clone, Debug)]\npub(crate) enum EnumBasedValue {\n    Undefined,\n    Null,\n    Boolean(bool),\n    Integer32(i32),\n    Float64(f64),\n    BigInt(JsBigInt),\n    Object(JsObject),\n    Symbol(JsSymbol),\n    String(JsString),\n}\n\nimpl Finalize for EnumBasedValue {\n    fn finalize(&self) {\n        if let Some(o) = self.as_object() {\n            o.finalize();\n        }\n    }\n}\n\n#[allow(unsafe_op_in_unsafe_fn)]\nunsafe impl Trace for EnumBasedValue {\n    custom_trace! {this, mark, {\n        if let Some(o) = this.as_object() {\n            mark(&o);\n        }\n    }}\n}\n\nimpl EnumBasedValue {\n    /// Returns a `InnerValue` from a Null.\n    #[must_use]\n    #[inline]\n    pub(crate) const fn null() -> Self {\n        Self::Null\n    }\n\n    /// Returns a `InnerValue` from an undefined.\n    #[must_use]\n    #[inline]\n    pub(crate) const fn undefined() -> Self {\n        Self::Undefined\n    }\n\n    /// Returns a `InnerValue` from a 64-bits float. If the float is `NaN`,\n    /// it will be reduced to a canonical `NaN` representation.\n    #[must_use]\n    #[inline]\n    pub(crate) const fn float64(value: f64) -> Self {\n        Self::Float64(value)\n    }\n\n    /// Returns a `InnerValue` from a 32-bits integer.\n    #[must_use]\n    #[inline]\n    pub(crate) const fn integer32(value: i32) -> Self {\n        Self::Integer32(value)\n    }\n\n    /// Returns a `InnerValue` from a boolean.\n    #[must_use]\n    #[inline]\n    pub(crate) const fn boolean(value: bool) -> Self {\n        Self::Boolean(value)\n    }\n\n    /// Returns a `InnerValue` from a boxed `[JsBigInt]`.\n    #[must_use]\n    #[inline]\n    pub(crate) fn bigint(value: JsBigInt) -> Self {\n        Self::BigInt(value)\n    }\n\n    /// Returns a `InnerValue` from a boxed `[JsObject]`.\n    #[must_use]\n    #[inline]\n    pub(crate) fn object(value: JsObject) -> Self {\n        Self::Object(value)\n    }\n\n    /// Returns a `InnerValue` from a boxed `[JsSymbol]`.\n    #[must_use]\n    #[inline]\n    pub(crate) fn symbol(value: JsSymbol) -> Self {\n        Self::Symbol(value)\n    }\n\n    /// Returns a `InnerValue` from a boxed `[JsString]`.\n    #[must_use]\n    #[inline]\n    pub(crate) fn string(value: JsString) -> Self {\n        Self::String(value)\n    }\n\n    /// Returns true if a value is undefined.\n    #[must_use]\n    #[inline]\n    pub(crate) const fn is_undefined(&self) -> bool {\n        matches!(self, Self::Undefined)\n    }\n\n    /// Returns true if a value is null.\n    #[must_use]\n    #[inline]\n    pub(crate) const fn is_null(&self) -> bool {\n        matches!(self, Self::Null)\n    }\n\n    /// Returns true if a value is null or undefined.\n    #[must_use]\n    #[inline]\n    pub(crate) const fn is_null_or_undefined(&self) -> bool {\n        matches!(self, Self::Null | Self::Undefined)\n    }\n\n    /// Returns true if a value is a boolean.\n    #[must_use]\n    #[inline]\n    pub(crate) const fn is_bool(&self) -> bool {\n        matches!(self, Self::Boolean(_))\n    }\n\n    /// Returns true if a value is a 64-bits float.\n    #[must_use]\n    #[inline]\n    pub(crate) const fn is_float64(&self) -> bool {\n        matches!(self, Self::Float64(_))\n    }\n\n    /// Returns true if a value is negative zero (`-0`).\n    #[must_use]\n    #[inline]\n    pub(crate) const fn is_negative_zero(&self) -> bool {\n        matches!(self, Self::Float64(value) if value.to_bits() == (-0f64).to_bits())\n    }\n\n    /// Returns true if a value is a 32-bits integer.\n    #[must_use]\n    #[inline]\n    pub(crate) const fn is_integer32(&self) -> bool {\n        matches!(self, Self::Integer32(_))\n    }\n\n    /// Returns true if a value is a `[JsBigInt]`. A `NaN` will not match here.\n    #[must_use]\n    #[inline]\n    pub(crate) const fn is_bigint(&self) -> bool {\n        matches!(self, Self::BigInt(_))\n    }\n\n    /// Returns true if a value is a boxed Object.\n    #[must_use]\n    #[inline]\n    pub(crate) const fn is_object(&self) -> bool {\n        matches!(self, Self::Object(_))\n    }\n\n    /// Returns true if a value is a boxed Symbol.\n    #[must_use]\n    #[inline]\n    pub(crate) const fn is_symbol(&self) -> bool {\n        matches!(self, Self::Symbol(_))\n    }\n\n    /// Returns true if a value is a boxed String.\n    #[must_use]\n    #[inline]\n    pub(crate) const fn is_string(&self) -> bool {\n        matches!(self, Self::String(_))\n    }\n\n    /// Returns the [`Type`] of this value without cloning the inner value.\n    #[must_use]\n    #[inline]\n    pub(crate) const fn get_type(&self) -> Type {\n        match self {\n            Self::Float64(_) | Self::Integer32(_) => Type::Number,\n            Self::String(_) => Type::String,\n            Self::Boolean(_) => Type::Boolean,\n            Self::Symbol(_) => Type::Symbol,\n            Self::Null => Type::Null,\n            Self::Undefined => Type::Undefined,\n            Self::BigInt(_) => Type::BigInt,\n            Self::Object(_) => Type::Object,\n        }\n    }\n\n    /// Returns the value as an f64 if it is a float.\n    #[must_use]\n    #[inline]\n    pub(crate) const fn as_float64(&self) -> Option<f64> {\n        match self {\n            Self::Float64(value) => Some(*value),\n            _ => None,\n        }\n    }\n\n    /// Returns the value as an i32 if it is an integer.\n    #[must_use]\n    #[inline]\n    pub(crate) const fn as_integer32(&self) -> Option<i32> {\n        match self {\n            Self::Integer32(value) => Some(*value),\n            _ => None,\n        }\n    }\n\n    /// Returns the value as a boolean if it is a boolean.\n    #[must_use]\n    #[inline]\n    pub(crate) const fn as_bool(&self) -> Option<bool> {\n        match self {\n            Self::Boolean(value) => Some(*value),\n            _ => None,\n        }\n    }\n\n    /// Returns the value as a boxed `[JsBigInt]`.\n    #[must_use]\n    #[inline]\n    pub(crate) fn as_bigint(&self) -> Option<JsBigInt> {\n        match self {\n            Self::BigInt(value) => Some(value.clone()),\n            _ => None,\n        }\n    }\n\n    /// Returns the value as a boxed `[JsObject]`.\n    #[must_use]\n    #[inline]\n    pub(crate) fn as_object(&self) -> Option<JsObject> {\n        match self {\n            Self::Object(value) => Some(value.clone()),\n            _ => None,\n        }\n    }\n\n    /// Returns the value as a boxed `[JsSymbol]`.\n    #[must_use]\n    #[inline]\n    pub(crate) fn as_symbol(&self) -> Option<JsSymbol> {\n        match self {\n            Self::Symbol(value) => Some(value.clone()),\n            _ => None,\n        }\n    }\n\n    /// Returns the value as a boxed `[JsString]`.\n    #[must_use]\n    #[inline]\n    pub(crate) fn as_string(&self) -> Option<JsString> {\n        match self {\n            Self::String(value) => Some(value.clone()),\n            _ => None,\n        }\n    }\n\n    /// Converts the value to a boolean without cloning pointer types.\n    #[must_use]\n    #[inline]\n    pub(crate) fn to_boolean(&self) -> bool {\n        match self {\n            Self::Symbol(_) => true,\n            // Objects are truthy, unless they have [[IsHTMLDDA]] (Annex B §B.3.6.1).\n            #[cfg(feature = \"annex-b\")]\n            Self::Object(obj) if obj.is::<IsHTMLDDA>() => false,\n            Self::Object(_) => true,\n            Self::Null | Self::Undefined => false,\n            Self::Integer32(n) => *n != 0,\n            Self::Boolean(v) => *v,\n            Self::String(s) => !s.is_empty(),\n            Self::BigInt(n) => !n.is_zero(),\n            Self::Float64(n) => *n != 0.0 && !n.is_nan(),\n        }\n    }\n\n    /// Returns the `[JsVariant]` of this inner value.\n    #[must_use]\n    #[inline]\n    pub(crate) fn as_variant(&self) -> JsVariant {\n        match self {\n            Self::Undefined => JsVariant::Undefined,\n            Self::Null => JsVariant::Null,\n            Self::Boolean(v) => JsVariant::Boolean(*v),\n            Self::Integer32(v) => JsVariant::Integer32(*v),\n            Self::Float64(v) => JsVariant::Float64(*v),\n            Self::BigInt(v) => JsVariant::BigInt(v.clone()),\n            Self::Object(v) => JsVariant::Object(v.clone()),\n            Self::Symbol(v) => JsVariant::Symbol(v.clone()),\n            Self::String(v) => JsVariant::String(v.clone()),\n        }\n    }\n}\n"
  },
  {
    "path": "core/engine/src/value/inner/nan_boxed.rs",
    "content": "//! A NaN-boxed inner value for JavaScript values.\n//!\n//! This [`JsValue`] is a float using `NaN` values to represent an inner\n//! JavaScript value.\n//!\n//! # Assumptions\n//!\n//! This implementation makes exactly two architecture assumptions. Everything\n//! else is independent of the arch where it's run.\n//!\n//! The first assumption is easy to verify: JavaScript numbers must be 64 bits\n//! IEEE-754, which is guaranteed by Rust and JavaScript implementations.\n//!\n//! The second assumption is that pointers are 48 bits maximum. This is a bit\n//! more complex to verify, but it is a safe assumption for all current\n//! architectures. The only exception is RISC-V and Intel processors that\n//! enable 5-level paging extensions.\n//!\n//! This is clarified here: <https://en.m.wikipedia.org/wiki/64-bit_computing>:\n//!\n//! > not all 64-bit instruction sets support full 64-bit virtual memory\n//! > addresses; x86-64 and AArch64 for example, support only 48 bits of\n//! > virtual address, with the remaining 16 bits of the virtual address\n//! > required to be all zeros (000...) or all ones (111...), and several\n//! > 64-bit instruction sets support fewer than 64 bits of physical\n//! > memory address.\n//!\n//! ALL 32 bits architectures are compatible, of course, as their pointers\n//! are 32 bits.\n//!\n//! Wasm with MEMORY64 (which is very rare) follows the pointer structure\n//! of its host architecture.\n//! For more info, see\n//! <https://spidermonkey.dev/blog/2025/01/15/is-memory64-actually-worth-using.html>\n//!\n//! This leaves RISC-V and processes that enable 5-level paging extensions\n//! on Intel (<https://en.m.wikipedia.org/wiki/Intel_5-level_paging>).\n//!\n//! We could feature gate on RISC-V, but it's not worth it. The only\n//! RISC-V processors that support 64-bit are the ones that support 64-bit\n//! virtual memory addresses. So it's a safe assumption.\n//!\n//! There is no way to feature gate on 5-level paging as it's a software\n//! trigger.\n//!\n//! There is a software assertion in the code that will panic if the pointer\n//! uses more than 48 bits.\n//!\n//! # Design\n//!\n//! This [`JsValue`] inner type is a NaN-boxed value, which is a 64-bits value\n//! that can represent any JavaScript value. If the integer is a non-NaN value,\n//! it will be stored as a 64-bits float. If it is a `f64::NAN` value, it will\n//! be stored as a quiet `NaN` value. Subnormal numbers are regular float.\n//!\n//! For any other type of values, the value will be stored as a 51-bits non-zero\n//! integer.\n//!\n//! In short, the memory layout of a NaN-boxed value is as follows:\n//!\n//! | Type of           | Bit Layout | Comment |\n//! |-------------------|------------|---------|\n//! | `+Infinity`       | `7FF0:0000:0000:0000`    | |\n//! | `-Infinity`       | `FFF0:0000:0000:0000`    | |\n//! | `NAN` (quiet)     | `7FF8:0000:0000:0000`    | |\n//! | `Integer32`       | `7FF9:0000:IIII:IIII`    | 32-bits integer. |\n//! | `False`           | `7FFA:0000:0000:0000`    | |\n//! | `True`            | `7FFA:0000:0000:0001`    | |\n//! | `Null`            | `7FFB:0000:0000:0000`    | |\n//! | `Undefined`       | `7FFB:0000:0000:0001`    | |\n//! | `Object`          | `7FFC:PPPP:PPPP:PPPP`    | 48-bits pointer. Assumes non-null pointer. |\n//! | `String`          | `7FFD:PPPP:PPPP:PPPP`    | 48-bits pointer. Assumes non-null pointer. |\n//! | `Symbol`          | `7FFE:PPPP:PPPP:PPPP`    | 48-bits pointer. Assumes non-null pointer. |\n//! | `BigInt`          | `7FFF:PPPP:PPPP:PPPP`    | 48-bits pointer. Assumes non-null pointer. |\n//! | `Float64`         | Any other values.        | |\n//!\n//! Another way to visualize this is by looking at the bit layout of a NaN-boxed\n//! value:\n//! ```text\n//!                           ....--<| The type of inner value is represented by this.\n//!                           |..|   | 11?? - Pointer, where ?? is the subtype of pointer:\n//!                           |..|   |        b00 - Object, b01 - String,\n//!                           |..|   |        b10 - Symbol, b11 - BigInt.\n//!                           |..|   | 10?? - Non-pointer, where ?? is the subtype:\n//!                           |..|   |        b01 - Integer32, b10 - Boolean,\n//!                           |..|   |        b11 - Other\n//!                           vvvv\n//! bit index: 63   59   55   51   47   43   39   35   31 .. 3  0\n//!            0000 0000 0000 0000 0000 0000 0000 0000 0000 .. 0000\n//! +Inf       0111 1111 1111 0000 0000 0000 0000 0000 0000 .. 0000\n//! -Inf       1111 1111 1111 0000 0000 0000 0000 0000 0000 .. 0000\n//! NaN (q)    0111 1111 1111 1000 0000 0000 0000 0000 0000 .. 0000\n//! Integer32  0111 1111 1111 1001 0000 0000 0000 0000 IIII .. IIII\n//! False      0111 1111 1111 1010 0000 0000 0000 0000 0000 .. 0000\n//! True       0111 1111 1111 1010 0000 0000 0000 0000 0000 .. 0001\n//! Null       0111 1111 1111 1011 0000 0000 0000 0000 0000 .. 0000\n//! Undefined  0111 1111 1111 1011 0000 0000 0000 0000 0000 .. 0001\n//! Object     0111 1111 1111 1100 PPPP PPPP PPPP PPPP PPPP .. PPPP\n//! String     0111 1111 1111 1101 PPPP PPPP PPPP PPPP PPPP .. PPPP\n//! Symbol     0111 1111 1111 1110 PPPP PPPP PPPP PPPP PPPP .. PPPP\n//! BigInt     0111 1111 1111 1111 PPPP PPPP PPPP PPPP PPPP .. PPPP\n//! Float64    Any other value.\n//! ```\n//!\n//! The pointers are assumed to never be NULL, and as such no clash\n//! with regular NAN should happen.\n#![allow(clippy::inline_always)]\n\n#[cfg(feature = \"annex-b\")]\nuse crate::builtins::is_html_dda::IsHTMLDDA;\nuse crate::{\n    JsBigInt, JsObject, JsSymbol, JsVariant, bigint::RawBigInt, object::ErasedVTableObject,\n    symbol::RawJsSymbol, value::Type,\n};\nuse boa_gc::{Finalize, GcBox, Trace, custom_trace};\nuse boa_string::JsString;\nuse core::fmt;\nuse static_assertions::const_assert;\nuse std::{\n    mem::{self, ManuallyDrop},\n    ptr::{self, NonNull},\n};\n\nconst _NAN_BOX_COMPAT_CHECK: () = const {\n    // We can only NaN-box pointers that are 32 or 64 bits.\n    assert!(\n        size_of::<usize>() == size_of::<u64>() || size_of::<usize>() == size_of::<u32>(),\n        \"this platform is not compatible with a nan-boxed `JsValueInner`\\n\\\n        enable the `jsvalue-enum` feature to use the enum-based `JsValueInner`\"\n    );\n\n    // We cannot NaN-box pointers that are not 4-bytes aligned.\n    assert!(\n        align_of::<*mut ()>() >= 4,\n        \"this platform is not compatible with a nan-boxed `JsValueInner`\\n\\\n        enable the `jsvalue-enum` feature to use the enum-based `JsValueInner`\"\n    );\n};\n\n/// Internal module for bit masks and constants.\n///\n/// All bit magic is done here.\nmod bits {\n    use std::ptr::NonNull;\n\n    /// The mask for the bits that indicate if the value is a NaN-value.\n    const MASK_NAN: u64 = 0x7FF0_0000_0000_0000;\n\n    /// The mask for the bits that indicate the kind of the value.\n    pub(super) const MASK_KIND: u64 = MASK_NAN | 0xF_0000_0000_0000;\n\n    // The tag bits for the different kinds of values.\n    const TAG_INF: u64 = 0x0_0000_0000_0000;\n    const TAG_NAN: u64 = 0x8_0000_0000_0000;\n    const TAG_INT32: u64 = 0x9_0000_0000_0000;\n    const TAG_BOOLEAN: u64 = 0xA_0000_0000_0000;\n    const TAG_OTHER: u64 = 0xB_0000_0000_0000;\n    const TAG_OBJECT: u64 = 0xC_0000_0000_0000;\n    const TAG_STRING: u64 = 0xD_0000_0000_0000;\n    const TAG_SYMBOL: u64 = 0xE_0000_0000_0000;\n    const TAG_BIGINT: u64 = 0xF_0000_0000_0000;\n\n    // The masks for the different kinds of tag bits.\n    pub(super) const MASK_INT32: u64 = MASK_NAN | TAG_INT32;\n    pub(super) const MASK_BOOLEAN: u64 = MASK_NAN | TAG_BOOLEAN;\n    pub(super) const MASK_OTHER: u64 = MASK_NAN | TAG_OTHER;\n    pub(super) const MASK_OBJECT: u64 = MASK_NAN | TAG_OBJECT;\n    pub(super) const MASK_STRING: u64 = MASK_NAN | TAG_STRING;\n    pub(super) const MASK_SYMBOL: u64 = MASK_NAN | TAG_SYMBOL;\n    pub(super) const MASK_BIGINT: u64 = MASK_NAN | TAG_BIGINT;\n\n    // The masks for the different kinds of values.\n    const MASK_INT32_VALUE: u64 = 0xFFFF_FFFF;\n    const MASK_POINTER_VALUE: u64 = 0x0000_FFFF_FFFF_FFFF;\n    const MASK_BOOLEAN_VALUE: u64 = 1;\n\n    /// The constant null value.\n    pub(super) const VALUE_NULL: u64 = MASK_OTHER;\n\n    /// The constant undefined value.\n    pub(super) const VALUE_UNDEFINED: u64 = MASK_OTHER | 1;\n\n    /// The constant false value.\n    pub(super) const VALUE_FALSE: u64 = MASK_BOOLEAN;\n\n    /// The constant true value.\n    pub(super) const VALUE_TRUE: u64 = MASK_BOOLEAN | 1;\n\n    // The constant `-0` value.\n    pub(super) const VALUE_NEGATIVE_ZERO: u64 = (-0f64).to_bits();\n\n    /// Checks that a value is a valid boolean (either true or false).\n    #[inline(always)]\n    pub(super) const fn is_bool(value: u64) -> bool {\n        value & MASK_KIND == MASK_BOOLEAN\n    }\n\n    /// Checks that a value is a valid float, not a tagged nan boxed value.\n    #[inline(always)]\n    pub(super) const fn is_float(value: u64) -> bool {\n        (value & MASK_NAN != MASK_NAN)\n            || (value & MASK_KIND) == (MASK_NAN | TAG_INF)\n            || (value & MASK_KIND) == (MASK_NAN | TAG_NAN)\n    }\n\n    /// Checks that a value is a negative zero (`-0`).\n    #[inline(always)]\n    pub(super) const fn is_negative_zero(value: u64) -> bool {\n        value == VALUE_NEGATIVE_ZERO\n    }\n\n    /// Checks that a value is a valid integer32.\n    #[inline(always)]\n    pub(super) const fn is_integer32(value: u64) -> bool {\n        value & MASK_KIND == MASK_INT32\n    }\n\n    /// Checks that a value is a valid `BigInt`.\n    #[inline(always)]\n    pub(super) const fn is_bigint(value: u64) -> bool {\n        value & MASK_KIND == MASK_BIGINT\n    }\n\n    /// Checks that a value is a valid Object.\n    #[inline(always)]\n    pub(super) const fn is_object(value: u64) -> bool {\n        value & MASK_KIND == MASK_OBJECT\n    }\n\n    /// Checks that a value is a valid Symbol.\n    #[inline(always)]\n    pub(super) const fn is_symbol(value: u64) -> bool {\n        value & MASK_KIND == MASK_SYMBOL\n    }\n\n    /// Checks that a value is a valid String.\n    #[inline(always)]\n    pub(super) const fn is_string(value: u64) -> bool {\n        value & MASK_KIND == MASK_STRING\n    }\n\n    /// Returns a tagged u64 of a 64-bits float.\n    #[inline(always)]\n    pub(super) const fn tag_f64(value: f64) -> u64 {\n        if value.is_nan() {\n            // Reduce any NAN to a canonical NAN representation.\n            f64::NAN.to_bits()\n        } else {\n            value.to_bits()\n        }\n    }\n\n    /// Returns a tagged u64 of a 32-bits integer.\n    #[inline(always)]\n    pub(super) const fn tag_i32(value: i32) -> u64 {\n        value as u64 & MASK_INT32_VALUE | MASK_INT32\n    }\n\n    /// Returns a i32-bits from a tagged integer.\n    #[inline(always)]\n    pub(super) const fn untag_i32(value: u64) -> i32 {\n        value as i32\n    }\n\n    /// Returns a tagged u64 of a boolean.\n    #[inline(always)]\n    pub(super) const fn tag_bool(value: bool) -> u64 {\n        value as u64 | MASK_BOOLEAN\n    }\n\n    /// Returns a boolean from a tagged value.\n    #[inline(always)]\n    pub(super) const fn untag_bool(value: u64) -> bool {\n        value & MASK_BOOLEAN_VALUE != 0\n    }\n\n    #[inline(always)]\n    pub(super) fn tag_pointer<T>(ptr: NonNull<T>, type_mask: u64) -> u64 {\n        // Mark this as `cold` since on well-behaved platforms\n        // this will never be called.\n        #[cold]\n        #[inline(never)]\n        fn unsupported_platform() {\n            panic!(\n                \"this platform is not compatible with a nan-boxed `JsValueInner`\\n\\\n                enable the `jsvalue-enum` feature to use the enum-based `JsValueInner`\"\n            )\n        }\n\n        let value = ptr.addr().get() as u64;\n        let value_masked: u64 = value & MASK_POINTER_VALUE;\n\n        // Assert alignment and location of the pointer.\n        // TODO: we may want to have nan-boxing be the default only on\n        // certain platforms, and make it an unsafe operation\n        // to manually enable the implementation.\n        if value_masked != value {\n            unsupported_platform();\n        }\n\n        value_masked | type_mask\n    }\n\n    /// Returns the pointer address of the inner value.\n    #[inline(always)]\n    pub(super) const fn untag_pointer(value: u64) -> usize {\n        (value & MASK_POINTER_VALUE) as usize\n    }\n}\n\n// Verify that all const values and masks are nan.\nconst_assert!(f64::from_bits(bits::VALUE_UNDEFINED).is_nan());\nconst_assert!(f64::from_bits(bits::VALUE_NULL).is_nan());\nconst_assert!(f64::from_bits(bits::VALUE_FALSE).is_nan());\nconst_assert!(f64::from_bits(bits::VALUE_TRUE).is_nan());\nconst_assert!(f64::from_bits(bits::MASK_INT32).is_nan());\nconst_assert!(f64::from_bits(bits::MASK_BOOLEAN).is_nan());\nconst_assert!(f64::from_bits(bits::MASK_OTHER).is_nan());\nconst_assert!(f64::from_bits(bits::MASK_OBJECT).is_nan());\nconst_assert!(f64::from_bits(bits::MASK_STRING).is_nan());\nconst_assert!(f64::from_bits(bits::MASK_SYMBOL).is_nan());\nconst_assert!(f64::from_bits(bits::MASK_BIGINT).is_nan());\n\n/// A NaN-boxed [`JsValue`]'s inner.\npub(crate) struct NanBoxedValue {\n    #[cfg(target_pointer_width = \"32\")]\n    half: u32,\n    ptr: *mut (),\n}\n\nimpl fmt::Debug for NanBoxedValue {\n    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {\n        match self.as_variant() {\n            JsVariant::Undefined => f.debug_tuple(\"Undefined\").finish(),\n            JsVariant::Null => f.debug_tuple(\"Null\").finish(),\n            JsVariant::Boolean(b) => f.debug_tuple(\"Boolean\").field(&b).finish(),\n            JsVariant::Float64(n) => f.debug_tuple(\"Float64\").field(&n).finish(),\n            JsVariant::Integer32(n) => f.debug_tuple(\"Integer32\").field(&n).finish(),\n            JsVariant::BigInt(n) => f.debug_tuple(\"BigInt\").field(&n).finish(),\n            JsVariant::Object(n) => f.debug_tuple(\"Object\").field(&n).finish(),\n            JsVariant::Symbol(n) => f.debug_tuple(\"Symbol\").field(&n).finish(),\n            JsVariant::String(n) => f.debug_tuple(\"String\").field(&n).finish(),\n        }\n    }\n}\n\nimpl Finalize for NanBoxedValue {\n    fn finalize(&self) {\n        if let Some(o) = self.as_object() {\n            o.finalize();\n        }\n    }\n}\n\n#[allow(unsafe_op_in_unsafe_fn)]\nunsafe impl Trace for NanBoxedValue {\n    custom_trace! {this, mark, {\n        if let Some(o) = this.as_object() {\n            mark(&o);\n        }\n    }}\n}\n\nimpl Clone for NanBoxedValue {\n    #[inline(always)]\n    fn clone(&self) -> Self {\n        // This way of inlining ensures the compiler\n        // knows it can convert the match into a\n        // jump table.\n        match self.value() & bits::MASK_KIND {\n            bits::MASK_OBJECT => unsafe {\n                mem::forget((*self.as_object_unchecked()).clone());\n            },\n            bits::MASK_STRING => unsafe {\n                mem::forget((*self.as_string_unchecked()).clone());\n            },\n            bits::MASK_SYMBOL => unsafe {\n                mem::forget((*self.as_symbol_unchecked()).clone());\n            },\n            bits::MASK_BIGINT => unsafe {\n                mem::forget((*self.as_bigint_unchecked()).clone());\n            },\n            _ => {\n                // Rest of the variants don't need additional handling.\n            }\n        }\n\n        Self {\n            #[cfg(target_pointer_width = \"32\")]\n            half: self.half,\n            ptr: self.ptr,\n        }\n    }\n}\n\nimpl NanBoxedValue {\n    /// Creates a new `NanBoxedValue` from an u64 value without checking the validity\n    /// of the value.\n    #[must_use]\n    #[inline(always)]\n    const fn from_inner_unchecked(inner: u64) -> Self {\n        Self {\n            #[cfg(target_pointer_width = \"32\")]\n            half: (inner >> 32) as u32,\n            ptr: ptr::without_provenance_mut(inner as usize),\n        }\n    }\n\n    /// Creates a new `NanBoxedValue` from a pointer to an object-like and the tagged address of that\n    /// pointer.\n    ///\n    /// This preserves the provenance of the original pointer.\n    fn from_object_like<T>(ptr: NonNull<T>, addr: u64) -> Self {\n        Self {\n            #[cfg(target_pointer_width = \"32\")]\n            half: (addr >> 32) as u32,\n            ptr: ptr.cast::<()>().as_ptr().with_addr(addr as usize),\n        }\n    }\n\n    /// Returns the value contained within this structure as a `u64`.\n    #[must_use]\n    #[inline(always)]\n    fn value(&self) -> u64 {\n        let value = self.ptr.addr() as u64;\n\n        #[cfg(target_pointer_width = \"32\")]\n        let value = ((self.half as u64) << 32) | value;\n\n        value\n    }\n\n    /// Returns a `InnerValue` from a Null.\n    #[must_use]\n    #[inline(always)]\n    pub(crate) const fn null() -> Self {\n        Self::from_inner_unchecked(bits::VALUE_NULL)\n    }\n\n    /// Returns a `InnerValue` from an undefined.\n    #[must_use]\n    #[inline(always)]\n    pub(crate) const fn undefined() -> Self {\n        Self::from_inner_unchecked(bits::VALUE_UNDEFINED)\n    }\n\n    /// Returns a `InnerValue` from a 64-bits float. If the float is `NaN`,\n    /// it will be reduced to a canonical `NaN` representation.\n    #[must_use]\n    #[inline(always)]\n    pub(crate) const fn float64(value: f64) -> Self {\n        Self::from_inner_unchecked(bits::tag_f64(value))\n    }\n\n    /// Returns a `InnerValue` from a 32-bits integer.\n    #[must_use]\n    #[inline(always)]\n    pub(crate) const fn integer32(value: i32) -> Self {\n        Self::from_inner_unchecked(bits::tag_i32(value))\n    }\n\n    /// Returns a `InnerValue` from a boolean.\n    #[must_use]\n    #[inline(always)]\n    pub(crate) const fn boolean(value: bool) -> Self {\n        Self::from_inner_unchecked(bits::tag_bool(value))\n    }\n\n    /// Returns a `InnerValue` from a boxed [`JsBigInt`].\n    #[must_use]\n    #[inline(always)]\n    pub(crate) fn bigint(value: JsBigInt) -> Self {\n        let ptr = value.into_raw();\n        let addr = bits::tag_pointer(ptr, bits::MASK_BIGINT);\n        Self::from_object_like(ptr, addr)\n    }\n\n    /// Returns a `InnerValue` from a boxed [`JsObject`].\n    #[must_use]\n    #[inline(always)]\n    pub(crate) fn object(value: JsObject) -> Self {\n        let ptr = value.into_raw();\n        let addr = bits::tag_pointer(ptr, bits::MASK_OBJECT);\n        Self::from_object_like(ptr, addr)\n    }\n\n    /// Returns a `InnerValue` from a boxed [`JsSymbol`].\n    #[must_use]\n    #[inline(always)]\n    pub(crate) fn symbol(value: JsSymbol) -> Self {\n        let ptr = value.into_raw();\n        let addr = bits::tag_pointer(ptr, bits::MASK_SYMBOL);\n        Self::from_object_like(ptr, addr)\n    }\n\n    /// Returns a `InnerValue` from a boxed [`JsString`].\n    #[must_use]\n    #[inline(always)]\n    pub(crate) fn string(value: JsString) -> Self {\n        let ptr = value.into_raw();\n        let addr = bits::tag_pointer(ptr, bits::MASK_STRING);\n        Self::from_object_like(ptr, addr)\n    }\n\n    /// Returns true if a value is undefined.\n    #[must_use]\n    #[inline(always)]\n    pub(crate) fn is_undefined(&self) -> bool {\n        self.value() == bits::VALUE_UNDEFINED\n    }\n\n    /// Returns true if a value is null.\n    #[must_use]\n    #[inline(always)]\n    pub(crate) fn is_null(&self) -> bool {\n        self.value() == bits::VALUE_NULL\n    }\n\n    /// Returns true if a value is null or undefined.\n    #[must_use]\n    #[inline(always)]\n    pub(crate) fn is_null_or_undefined(&self) -> bool {\n        self.value() & bits::MASK_KIND == bits::MASK_OTHER\n    }\n\n    /// Returns true if a value is a boolean.\n    #[must_use]\n    #[inline(always)]\n    pub(crate) fn is_bool(&self) -> bool {\n        bits::is_bool(self.value())\n    }\n\n    /// Returns true if a value is a 64-bits float.\n    #[must_use]\n    #[inline(always)]\n    pub(crate) fn is_float64(&self) -> bool {\n        bits::is_float(self.value())\n    }\n\n    /// Returns true if a value is negative zero (`-0.0`).\n    #[must_use]\n    #[inline(always)]\n    pub(crate) fn is_negative_zero(&self) -> bool {\n        bits::is_negative_zero(self.value())\n    }\n\n    /// Returns true if a value is a 32-bits integer.\n    #[must_use]\n    #[inline(always)]\n    pub(crate) fn is_integer32(&self) -> bool {\n        bits::is_integer32(self.value())\n    }\n\n    /// Returns true if a value is a [`JsBigInt`]. A `NaN` will not match here.\n    #[must_use]\n    #[inline(always)]\n    pub(crate) fn is_bigint(&self) -> bool {\n        bits::is_bigint(self.value())\n    }\n\n    /// Returns true if a value is a boxed Object.\n    #[must_use]\n    #[inline(always)]\n    pub(crate) fn is_object(&self) -> bool {\n        bits::is_object(self.value())\n    }\n\n    /// Returns true if a value is a boxed Symbol.\n    #[must_use]\n    #[inline(always)]\n    pub(crate) fn is_symbol(&self) -> bool {\n        bits::is_symbol(self.value())\n    }\n\n    /// Returns true if a value is a boxed String.\n    #[must_use]\n    #[inline(always)]\n    pub(crate) fn is_string(&self) -> bool {\n        bits::is_string(self.value())\n    }\n\n    /// Returns the [`Type`] of this value using only the tag bits,\n    /// without extracting or cloning the inner value.\n    #[must_use]\n    #[inline(always)]\n    pub(crate) fn get_type(&self) -> Type {\n        match self.value() & bits::MASK_KIND {\n            bits::MASK_OBJECT => Type::Object,\n            bits::MASK_STRING => Type::String,\n            bits::MASK_SYMBOL => Type::Symbol,\n            bits::MASK_BIGINT => Type::BigInt,\n            bits::MASK_BOOLEAN => Type::Boolean,\n            bits::MASK_OTHER => match self.value() {\n                bits::VALUE_NULL => Type::Null,\n                _ => Type::Undefined,\n            },\n            // Same arm as below.\n            // bits::MASK_INT32 => Type::Number,\n            _ => Type::Number, // Float64\n        }\n    }\n\n    /// Returns the value as a f64 if it is a float.\n    #[must_use]\n    #[inline(always)]\n    pub(crate) fn as_float64(&self) -> Option<f64> {\n        if self.is_float64() {\n            Some(f64::from_bits(self.value()))\n        } else {\n            None\n        }\n    }\n\n    /// Returns the value as an i32 if it is an integer.\n    #[must_use]\n    #[inline(always)]\n    pub(crate) fn as_integer32(&self) -> Option<i32> {\n        if self.is_integer32() {\n            Some(bits::untag_i32(self.value()))\n        } else {\n            None\n        }\n    }\n\n    /// Returns the value as a boolean if it is a boolean.\n    #[must_use]\n    #[inline(always)]\n    pub(crate) fn as_bool(&self) -> Option<bool> {\n        match self.value() {\n            bits::VALUE_FALSE => Some(false),\n            bits::VALUE_TRUE => Some(true),\n            _ => None,\n        }\n    }\n\n    /// Returns the value as a boxed [`JsBigInt`].\n    #[must_use]\n    #[inline(always)]\n    pub(crate) fn as_bigint(&self) -> Option<JsBigInt> {\n        if self.is_bigint() {\n            // SAFETY: the inner address must hold a valid, non-null JsBigInt.\n            unsafe { Some((*self.as_bigint_unchecked()).clone()) }\n        } else {\n            None\n        }\n    }\n\n    /// Returns the value as a [`JsBigInt`] without checking the inner tag.\n    ///\n    /// # Safety\n    ///\n    /// The inner value must be a valid `JsBigInt`.\n    #[must_use]\n    #[inline(always)]\n    unsafe fn as_bigint_unchecked(&self) -> ManuallyDrop<JsBigInt> {\n        let addr = bits::untag_pointer(self.value());\n        // SAFETY: This is guaranteed by the caller.\n        unsafe {\n            ManuallyDrop::new(JsBigInt::from_raw(\n                self.ptr.with_addr(addr).cast::<RawBigInt>().cast_const(),\n            ))\n        }\n    }\n\n    /// Returns the value as a boxed [`JsObject`].\n    #[must_use]\n    #[inline(always)]\n    pub(crate) fn as_object(&self) -> Option<JsObject> {\n        if self.is_object() {\n            // SAFETY: the inner address must hold a valid, non-null JsObject.\n            unsafe { Some((*self.as_object_unchecked()).clone()) }\n        } else {\n            None\n        }\n    }\n\n    /// Returns the value as a boxed [`JsObject`] without checking the inner tag.\n    ///\n    /// # Safety\n    ///\n    /// The inner value must be a valid `JsObject`.\n    #[must_use]\n    #[inline(always)]\n    unsafe fn as_object_unchecked(&self) -> ManuallyDrop<JsObject> {\n        let addr = bits::untag_pointer(self.value());\n        // SAFETY: This is guaranteed by the caller.\n        unsafe {\n            ManuallyDrop::new(JsObject::from_raw(NonNull::new_unchecked(\n                self.ptr.with_addr(addr).cast::<GcBox<ErasedVTableObject>>(),\n            )))\n        }\n    }\n\n    /// Returns the value as a [`JsSymbol`].\n    #[must_use]\n    #[inline(always)]\n    pub(crate) fn as_symbol(&self) -> Option<JsSymbol> {\n        if self.is_symbol() {\n            // SAFETY: the inner address must hold a valid, non-null JsSymbol.\n            unsafe { Some((*self.as_symbol_unchecked()).clone()) }\n        } else {\n            None\n        }\n    }\n\n    /// Returns the value as a [`JsSymbol`] without checking the inner tag.\n    ///\n    /// # Safety\n    ///\n    /// The inner value must be a valid `JsSymbol`.\n    #[must_use]\n    #[inline(always)]\n    unsafe fn as_symbol_unchecked(&self) -> ManuallyDrop<JsSymbol> {\n        let addr = bits::untag_pointer(self.value());\n        // SAFETY: This is guaranteed by the caller.\n        unsafe {\n            ManuallyDrop::new(JsSymbol::from_raw(NonNull::new_unchecked(\n                self.ptr.with_addr(addr).cast::<RawJsSymbol>(),\n            )))\n        }\n    }\n\n    /// Returns the value as a boxed [`JsString`].\n    #[must_use]\n    #[inline(always)]\n    pub(crate) fn as_string(&self) -> Option<JsString> {\n        if self.is_string() {\n            // SAFETY: the inner address must hold a valid, non-null JsString.\n            unsafe { Some((*self.as_string_unchecked()).clone()) }\n        } else {\n            None\n        }\n    }\n\n    /// Returns the value as a [`JsString`] without checking the inner tag.\n    ///\n    /// # Safety\n    ///\n    /// The inner value must be a valid `JsString`.\n    #[must_use]\n    #[inline(always)]\n    unsafe fn as_string_unchecked(&self) -> ManuallyDrop<JsString> {\n        let addr = bits::untag_pointer(self.value());\n        // SAFETY: the inner address must hold a valid, non-null JsString.\n        unsafe {\n            ManuallyDrop::new(JsString::from_raw(NonNull::new_unchecked(\n                self.ptr.with_addr(addr).cast(),\n            )))\n        }\n    }\n\n    /// Converts the value to a boolean without cloning pointer types.\n    ///\n    /// Objects and Symbols are always truthy. For `String` and `BigInt`,\n    /// the pointer is temporarily reconstructed via [`ManuallyDrop`] to\n    /// call `is_empty()` / `is_zero()` without touching the refcount.\n    #[must_use]\n    #[inline(always)]\n    pub(crate) fn to_boolean(&self) -> bool {\n        match self.value() & bits::MASK_KIND {\n            // Symbols are always truthy.\n            bits::MASK_SYMBOL => true,\n            // Objects are truthy, unless they have [[IsHTMLDDA]] (Annex B §B.3.6.1).\n            bits::MASK_OBJECT => {\n                #[cfg(feature = \"annex-b\")]\n                {\n                    // SAFETY: tag confirmed this is an Object.\n                    if unsafe { self.as_object_unchecked().is::<IsHTMLDDA>() } {\n                        return false;\n                    }\n                }\n                true\n            }\n            // Null and Undefined are always falsy.\n            bits::MASK_OTHER => false,\n            bits::MASK_INT32 => bits::untag_i32(self.value()) != 0,\n            bits::MASK_BOOLEAN => bits::untag_bool(self.value()),\n            bits::MASK_STRING => {\n                // SAFETY: tag confirmed this is a String.\n                unsafe { !self.as_string_unchecked().is_empty() }\n            }\n            bits::MASK_BIGINT => {\n                // SAFETY: tag confirmed this is a BigInt.\n                unsafe { !self.as_bigint_unchecked().is_zero() }\n            }\n            // Float64: falsy if 0.0, -0.0, or NaN.\n            _ => {\n                let f = f64::from_bits(self.value());\n                f != 0.0 && !f.is_nan()\n            }\n        }\n    }\n\n    /// Returns the [`JsVariant`] of this inner value.\n    #[must_use]\n    #[inline(always)]\n    pub(crate) fn as_variant(&self) -> JsVariant {\n        match self.value() & bits::MASK_KIND {\n            bits::MASK_OBJECT => {\n                JsVariant::Object(unsafe { (*self.as_object_unchecked()).clone() })\n            }\n            bits::MASK_STRING => {\n                JsVariant::String(unsafe { (*self.as_string_unchecked()).clone() })\n            }\n            bits::MASK_SYMBOL => {\n                JsVariant::Symbol(unsafe { (*self.as_symbol_unchecked()).clone() })\n            }\n            bits::MASK_BIGINT => {\n                JsVariant::BigInt(unsafe { (*self.as_bigint_unchecked()).clone() })\n            }\n            bits::MASK_INT32 => JsVariant::Integer32(bits::untag_i32(self.value())),\n            bits::MASK_BOOLEAN => JsVariant::Boolean(bits::untag_bool(self.value())),\n            bits::MASK_OTHER => match self.value() {\n                bits::VALUE_NULL => JsVariant::Null,\n                _ => JsVariant::Undefined,\n            },\n            _ => JsVariant::Float64(f64::from_bits(self.value())),\n        }\n    }\n}\n\nimpl Drop for NanBoxedValue {\n    #[inline(always)]\n    fn drop(&mut self) {\n        match self.value() & bits::MASK_KIND {\n            bits::MASK_OBJECT => {\n                unsafe { ManuallyDrop::into_inner(self.as_object_unchecked()) };\n            }\n            bits::MASK_STRING => {\n                unsafe { ManuallyDrop::into_inner(self.as_string_unchecked()) };\n            }\n            bits::MASK_SYMBOL => {\n                unsafe { ManuallyDrop::into_inner(self.as_symbol_unchecked()) };\n            }\n            bits::MASK_BIGINT => {\n                unsafe { ManuallyDrop::into_inner(self.as_bigint_unchecked()) };\n            }\n            _ => {}\n        }\n    }\n}\n\n#[cfg(test)]\nmacro_rules! assert_type {\n    (@@is $value: ident, $u: literal, $n: literal, $b: literal, $i: literal, $f: literal, $bi: literal, $s: literal, $o: literal, $sy: literal) => {\n        assert_eq!($u  != 0, $value.is_undefined());\n        assert_eq!($n  != 0, $value.is_null());\n        assert_eq!($b  != 0, $value.is_bool());\n        assert_eq!($i  != 0, $value.is_integer32());\n        assert_eq!($f  != 0, $value.is_float64());\n        assert_eq!($bi != 0, $value.is_bigint());\n        assert_eq!($s  != 0, $value.is_string());\n        assert_eq!($o  != 0, $value.is_object());\n        assert_eq!($sy != 0, $value.is_symbol());\n    };\n    (@@as $value: ident, $u: literal, $n: literal, $b: literal, $i: literal, $f: literal, $bi: literal, $s: literal, $o: literal, $sy: literal) => {\n        if $b  == 0 { assert_eq!($value.as_bool(), None); }\n        if $i  == 0 { assert_eq!($value.as_integer32(), None); }\n        if $f  == 0 { assert_eq!($value.as_float64(), None); }\n        if $bi == 0 { assert_eq!($value.as_bigint(), None); }\n        if $s  == 0 { assert_eq!($value.as_string(), None); }\n        if $o  == 0 { assert_eq!($value.as_object(), None); }\n        if $sy == 0 { assert_eq!($value.as_symbol(), None); }\n    };\n    ($value: ident is undefined) => {\n        assert_type!(@@is $value, 1, 0, 0, 0, 0, 0, 0, 0, 0);\n        assert_eq!($value.as_variant(), JsVariant::Undefined);\n    };\n    ($value: ident is null) => {\n        assert_type!(@@is $value, 0, 1, 0, 0, 0, 0, 0, 0, 0);\n        assert_eq!($value.as_variant(), JsVariant::Null);\n    };\n    ($value: ident is bool($scalar: ident)) => {\n        assert_type!(@@is $value, 0, 0, 1, 0, 0, 0, 0, 0, 0);\n        assert_type!(@@as $value, 0, 0, 1, 0, 0, 0, 0, 0, 0);\n        assert_eq!(Some($scalar), $value.as_bool());\n        assert_eq!($value.as_variant(), JsVariant::Boolean($scalar));\n    };\n    ($value: ident is integer($scalar: ident)) => {\n        assert_type!(@@is $value, 0, 0, 0, 1, 0, 0, 0, 0, 0);\n        assert_type!(@@as $value, 0, 0, 0, 1, 0, 0, 0, 0, 0);\n        assert_eq!(Some($scalar), $value.as_integer32());\n        assert_eq!($value.as_variant(), JsVariant::Integer32($scalar));\n    };\n    ($value: ident is float($scalar: ident)) => {\n        assert_type!(@@is $value, 0, 0, 0, 0, 1, 0, 0, 0, 0);\n        assert_type!(@@as $value, 0, 0, 0, 0, 1, 0, 0, 0, 0);\n        assert_eq!(Some($scalar), $value.as_float64());\n        // Verify parity.\n        assert_eq!(Some(1.0 / $scalar), $value.as_float64().map(|f| 1.0 / f));\n        assert_eq!($value.as_variant(), JsVariant::Float64($scalar));\n\n        // Verify that the clone is still the same.\n        let new_value = $value.clone();\n\n        assert_eq!(Some($scalar), new_value.as_float64());\n        assert_eq!($value.as_float64(), new_value.as_float64());\n        // Verify parity.\n        assert_eq!(Some(1.0 / $scalar), new_value.as_float64().map(|f| 1.0 / f));\n        assert_eq!(new_value.as_variant(), JsVariant::Float64($scalar));\n\n        let JsVariant::Float64(new_scalar) = new_value.as_variant() else {\n            panic!(\"Expected Float64, got {:?}\", new_value.as_variant());\n        };\n        assert_eq!(Some(new_scalar), new_value.as_float64());\n        assert_eq!($value.as_float64(), new_value.as_float64());\n        // Verify parity.\n        assert_eq!(Some(1.0 / new_scalar), new_value.as_float64().map(|f| 1.0 / f));\n        assert_eq!(new_value.as_variant(), JsVariant::Float64(new_scalar));\n    };\n    ($value: ident is nan) => {\n        assert_type!(@@is $value, 0, 0, 0, 0, 1, 0, 0, 0, 0);\n        assert_type!(@@as $value, 0, 0, 0, 0, 1, 0, 0, 0, 0);\n        assert!($value.as_float64().unwrap().is_nan());\n        assert!(matches!($value.as_variant(), JsVariant::Float64(f) if f.is_nan()));\n    };\n    ($value: ident is bigint($scalar: ident)) => {\n        assert_type!(@@is $value, 0, 0, 0, 0, 0, 1, 0, 0, 0);\n        assert_type!(@@as $value, 0, 0, 0, 0, 0, 1, 0, 0, 0);\n        assert_eq!(Some(&$scalar), $value.as_bigint().as_ref());\n        assert_eq!($value.as_variant(), JsVariant::BigInt($scalar));\n    };\n    ($value: ident is object($scalar: ident)) => {\n        assert_type!(@@is $value, 0, 0, 0, 0, 0, 0, 0, 1, 0);\n        assert_type!(@@as $value, 0, 0, 0, 0, 0, 0, 0, 1, 0);\n        assert_eq!(Some(&$scalar), $value.as_object().as_ref());\n        assert_eq!($value.as_variant(), JsVariant::Object($scalar));\n    };\n    ($value: ident is symbol($scalar: ident)) => {\n        assert_type!(@@is $value, 0, 0, 0, 0, 0, 0, 0, 0, 1);\n        assert_type!(@@as $value, 0, 0, 0, 0, 0, 0, 0, 0, 1);\n        assert_eq!(Some(&$scalar), $value.as_symbol().as_ref());\n        assert_eq!($value.as_variant(), JsVariant::Symbol($scalar));\n    };\n    ($value: ident is string($scalar: ident)) => {\n        assert_type!(@@is $value, 0, 0, 0, 0, 0, 0, 1, 0, 0);\n        assert_type!(@@as $value, 0, 0, 0, 0, 0, 0, 1, 0, 0);\n        assert_eq!(Some(&$scalar), $value.as_string().as_ref());\n        assert_eq!($value.as_variant(), JsVariant::String($scalar));\n    };\n}\n\n#[test]\nfn null() {\n    let v = NanBoxedValue::null();\n    assert_type!(v is null);\n}\n\n#[test]\nfn undefined() {\n    let v = NanBoxedValue::undefined();\n    assert_type!(v is undefined);\n}\n\n#[test]\nfn boolean() {\n    let v = NanBoxedValue::boolean(true);\n    assert_type!(v is bool(true));\n\n    let v = NanBoxedValue::boolean(false);\n    assert_type!(v is bool(false));\n}\n\n#[test]\nfn integer() {\n    fn assert_integer(i: i32) {\n        let v = NanBoxedValue::integer32(i);\n        assert_type!(v is integer(i));\n    }\n\n    assert_integer(0);\n    assert_integer(1);\n    assert_integer(-1);\n    assert_integer(42);\n    assert_integer(-42);\n    assert_integer(i32::MAX);\n    assert_integer(i32::MIN);\n    assert_integer(i32::MAX - 1);\n    assert_integer(i32::MIN + 1);\n}\n\n#[test]\n#[allow(clippy::float_cmp)]\nfn float() {\n    fn assert_float(f: f64) {\n        let v = NanBoxedValue::float64(f);\n        assert_type!(v is float(f));\n    }\n\n    assert_float(0.0);\n    assert_float(-0.0);\n    assert_float(0.1 + 0.2);\n    assert_float(-42.123);\n    assert_float(f64::INFINITY);\n    assert_float(f64::NEG_INFINITY);\n\n    // Some edge cases around zeroes.\n    let neg_zero = NanBoxedValue::float64(-0.0);\n    assert!(neg_zero.as_float64().unwrap().is_sign_negative());\n    assert_eq!(0.0f64, neg_zero.as_float64().unwrap());\n\n    let pos_zero = NanBoxedValue::float64(0.0);\n    assert!(!pos_zero.as_float64().unwrap().is_sign_negative());\n    assert_eq!(0.0f64, pos_zero.as_float64().unwrap());\n\n    assert_eq!(pos_zero.as_float64(), neg_zero.as_float64());\n\n    let nan = NanBoxedValue::float64(f64::NAN);\n    assert_type!(nan is nan);\n}\n\n#[test]\nfn bigint() {\n    let bigint = JsBigInt::from(42);\n    let v = NanBoxedValue::bigint(bigint.clone());\n    assert_type!(v is bigint(bigint));\n}\n\n#[test]\nfn object() {\n    let object = JsObject::with_null_proto();\n    let v = NanBoxedValue::object(object.clone());\n    assert_type!(v is object(object));\n}\n\n#[test]\nfn string() {\n    let str = crate::js_string!(\"Hello World\");\n    let v = NanBoxedValue::string(str.clone());\n    assert_type!(v is string(str));\n}\n\n#[test]\nfn symbol() {\n    let sym = JsSymbol::new(Some(JsString::from(\"Hello World\"))).unwrap();\n    let v = NanBoxedValue::symbol(sym.clone());\n    assert_type!(v is symbol(sym));\n\n    let sym = JsSymbol::new(None).unwrap();\n    let v = NanBoxedValue::symbol(sym.clone());\n    assert_type!(v is symbol(sym));\n}\n"
  },
  {
    "path": "core/engine/src/value/inner.rs",
    "content": "//! Module implementing the operations for the inner value of a `[super::JsValue]`.\nuse cfg_if::cfg_if;\n\ncfg_if!(\n    if #[cfg(feature = \"jsvalue-enum\")] {\n        mod legacy;\n        pub(crate) use legacy::EnumBasedValue as InnerValue;\n    } else {\n        mod nan_boxed;\n        pub(crate) use nan_boxed::NanBoxedValue as InnerValue;\n    }\n);\n"
  },
  {
    "path": "core/engine/src/value/integer.rs",
    "content": "use num_traits::{AsPrimitive, FromPrimitive};\nuse std::cmp::Ordering;\n\n/// Represents the result of the `ToIntegerOrInfinity` operation\n#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]\npub enum IntegerOrInfinity {\n    /// Positive infinity.\n    PositiveInfinity,\n\n    /// An integer.\n    Integer(i64),\n\n    /// Negative infinity.\n    NegativeInfinity,\n}\n\nimpl IntegerOrInfinity {\n    /// Clamps an `IntegerOrInfinity` between two `i64`, effectively converting\n    /// it to an i64.\n    ///\n    /// # Panics\n    ///\n    /// Panics if `min > max`.\n    #[must_use]\n    pub fn clamp_finite<I: Ord + AsPrimitive<i64> + FromPrimitive>(self, min: I, max: I) -> I {\n        assert!(min <= max);\n        match self {\n            Self::Integer(i) => {\n                I::from_i64(i.clamp(min.as_(), max.as_())).expect(\"`i` should already be clamped\")\n            }\n            Self::PositiveInfinity => max,\n            Self::NegativeInfinity => min,\n        }\n    }\n\n    /// Gets the wrapped `i64` if the variant is an `Integer`.\n    #[must_use]\n    pub const fn as_integer(self) -> Option<i64> {\n        match self {\n            Self::Integer(i) => Some(i),\n            _ => None,\n        }\n    }\n}\n\nimpl From<f64> for IntegerOrInfinity {\n    fn from(number: f64) -> Self {\n        // `ToIntegerOrInfinity ( argument )`\n        if number.is_nan() || number == 0.0 {\n            // 2. If number is NaN, +0𝔽, or -0𝔽, return 0.\n            Self::Integer(0)\n        } else if number == f64::INFINITY {\n            // 3. If number is +∞𝔽, return +∞.\n            Self::PositiveInfinity\n        } else if number == f64::NEG_INFINITY {\n            // 4. If number is -∞𝔽, return -∞.\n            Self::NegativeInfinity\n        } else {\n            // 5. Let integer be floor(abs(ℝ(number))).\n            // 6. If number < +0𝔽, set integer to -integer.\n            let integer = number.abs().floor().copysign(number) as i64;\n\n            // 7. Return integer.\n            Self::Integer(integer)\n        }\n    }\n}\n\nimpl PartialEq<i64> for IntegerOrInfinity {\n    fn eq(&self, other: &i64) -> bool {\n        match self {\n            Self::Integer(i) => i == other,\n            _ => false,\n        }\n    }\n}\n\nimpl PartialEq<IntegerOrInfinity> for i64 {\n    fn eq(&self, other: &IntegerOrInfinity) -> bool {\n        other.eq(self)\n    }\n}\n\nimpl PartialOrd<i64> for IntegerOrInfinity {\n    fn partial_cmp(&self, other: &i64) -> Option<Ordering> {\n        match self {\n            Self::PositiveInfinity => Some(Ordering::Greater),\n            Self::Integer(i) => i.partial_cmp(other),\n            Self::NegativeInfinity => Some(Ordering::Less),\n        }\n    }\n}\n\nimpl PartialOrd<IntegerOrInfinity> for i64 {\n    fn partial_cmp(&self, other: &IntegerOrInfinity) -> Option<Ordering> {\n        other.partial_cmp(self).map(Ordering::reverse)\n    }\n}\n\n#[cfg(test)]\nmod tests {\n    use super::*;\n\n    #[test]\n    fn test_eq() {\n        let int: i64 = 42;\n        let int_or_inf = IntegerOrInfinity::Integer(10);\n        assert!(int != int_or_inf);\n        assert!(int_or_inf != int);\n\n        let int: i64 = 10;\n        assert!(int == int_or_inf);\n        assert!(int_or_inf == int);\n    }\n\n    #[test]\n    fn test_ord() {\n        let int: i64 = 42;\n\n        let int_or_inf = IntegerOrInfinity::Integer(10);\n        assert!(int_or_inf < int);\n        assert!(int > int_or_inf);\n\n        let int_or_inf = IntegerOrInfinity::Integer(100);\n        assert!(int_or_inf > int);\n        assert!(int < int_or_inf);\n\n        let int_or_inf = IntegerOrInfinity::PositiveInfinity;\n        assert!(int_or_inf > int);\n        assert!(int < int_or_inf);\n\n        let int_or_inf = IntegerOrInfinity::NegativeInfinity;\n        assert!(int_or_inf < int);\n        assert!(int > int_or_inf);\n    }\n}\n"
  },
  {
    "path": "core/engine/src/value/mod.rs",
    "content": "//! Boa's ECMAScript Value implementation.\n//!\n//! Javascript values, utility methods and conversion between Javascript values and Rust values.\n\nuse num_bigint::BigInt;\nuse num_integer::Integer;\nuse num_traits::{ToPrimitive, Zero};\nuse std::{ops::Sub, sync::LazyLock};\n\nuse boa_gc::{Finalize, Trace};\n#[doc(inline)]\npub use boa_macros::TryFromJs;\npub use boa_macros::TryIntoJs;\n#[doc(inline)]\npub use conversions::convert::Convert;\n#[doc(inline)]\npub use conversions::nullable::Nullable;\n\npub(crate) use self::conversions::IntoOrUndefined;\n#[doc(inline)]\npub use self::{\n    conversions::try_from_js::TryFromJs, conversions::try_into_js::TryIntoJs,\n    display::ValueDisplay, integer::IntegerOrInfinity, operations::*, r#type::Type,\n    variant::JsVariant,\n};\nuse crate::builtins::RegExp;\nuse crate::object::{JsFunction, JsPromise, JsRegExp};\nuse crate::{\n    Context, JsBigInt, JsResult, JsString,\n    builtins::{\n        Number, Promise,\n        number::{f64_to_int32, f64_to_uint32},\n    },\n    error::JsNativeError,\n    js_string,\n    object::JsObject,\n    property::{PropertyDescriptor, PropertyKey},\n    symbol::JsSymbol,\n};\n\nmod conversions;\npub(crate) mod display;\nmod equality;\nmod hash;\nmod inner;\nmod integer;\nmod operations;\nmod r#type;\nmod variant;\n\n#[cfg(test)]\nmod tests;\n\nstatic TWO_E_64: LazyLock<BigInt> = LazyLock::new(|| {\n    const TWO_E_64: u128 = 2u128.pow(64);\n    BigInt::from(TWO_E_64)\n});\n\nstatic TWO_E_63: LazyLock<BigInt> = LazyLock::new(|| {\n    const TWO_E_63: u128 = 2u128.pow(63);\n    BigInt::from(TWO_E_63)\n});\n\n/// The `js_value!` macro creates a `JsValue` instance based on a JSON-like DSL.\n///\n/// ```\n/// # use boa_engine::{js_string, js_value, Context, JsValue};\n/// # let context = &mut Context::default();\n/// assert_eq!(js_value!( 1 ), JsValue::from(1));\n/// assert_eq!(js_value!( false ), JsValue::from(false));\n/// // Objects and arrays cannot be compared with simple equality.\n/// // To create arrays and objects, the context needs to be passed in.\n/// assert_eq!(js_value!([ 1, 2, 3 ], context).display().to_string(), \"[ 1, 2, 3 ]\");\n/// assert_eq!(\n///   js_value!({\n///     // Comments are allowed inside.\n///     \"key\": (js_string!(\"value\"))\n///   }, context).display().to_string(),\n///   \"{\\n    key: \\\"value\\\"\\n}\",\n/// );\n/// ```\npub use boa_macros::js_object;\n\n/// Create a `JsObject` object from a simpler DSL that resembles JSON.\n///\n/// ```\n/// # use boa_engine::{js_string, js_object, Context, JsValue};\n/// # let context = &mut Context::default();\n/// let value = js_object!({\n///   // Comments are allowed inside. String literals will always be transformed to `JsString`.\n///   \"key\": \"value\",\n///   // Identifiers will be used as keys, like in JavaScript.\n///   alsoKey: 1,\n///   // Expressions surrounded by brackets will be expressed, like in JavaScript.\n///   // Note that in this case, the unit value is represented by `null`.\n///   [1 + 2]: (),\n/// }, context);\n///\n/// assert_eq!(\n///     JsValue::from(value).display().to_string(),\n///     \"{\\n    3: null,\\n    key: \\\"value\\\",\\n    alsoKey: 1\\n}\"\n/// );\n/// ```\npub use boa_macros::js_value;\n\n/// A generic JavaScript value. This can be any ECMAScript language valid value.\n///\n/// This is a wrapper around the actual value, which is stored in an opaque type.\n/// This allows for internal changes to the value without affecting the public API.\n///\n/// ```\n/// # use boa_engine::{js_string, Context, JsValue};\n/// let mut context = Context::default();\n/// let value = JsValue::new(3);\n/// assert_eq!(value.to_string(&mut context), Ok(js_string!(\"3\")));\n/// ```\n#[derive(Finalize, Debug, Clone, Trace)]\npub struct JsValue(inner::InnerValue);\n\nimpl JsValue {\n    /// Create a new [`JsValue`] from an inner value.\n    #[inline]\n    const fn from_inner(inner: inner::InnerValue) -> Self {\n        Self(inner)\n    }\n\n    /// Create a new [`JsValue`].\n    ///\n    /// # Examples\n    ///\n    /// ```\n    /// use boa_engine::JsValue;\n    ///\n    /// let integer = JsValue::new(42);\n    /// assert_eq!(integer.as_number(), Some(42.0));\n    ///\n    /// let float = JsValue::new(3.14);\n    /// assert_eq!(float.as_number(), Some(3.14));\n    ///\n    /// let boolean = JsValue::new(true);\n    /// assert_eq!(boolean.as_boolean(), Some(true));\n    /// ```\n    #[inline]\n    #[must_use]\n    pub fn new<T>(value: T) -> Self\n    where\n        T: Into<Self>,\n    {\n        value.into()\n    }\n\n    /// Return the variant of this value.\n    ///\n    /// This can be used to match on the underlying type of the value.\n    ///\n    /// # Examples\n    ///\n    /// ```\n    /// use boa_engine::{JsValue, value::JsVariant};\n    ///\n    /// let value = JsValue::new(42);\n    /// match value.variant() {\n    ///     JsVariant::Integer32(n) => assert_eq!(n, 42),\n    ///     _ => unreachable!(),\n    /// }\n    ///\n    /// let value = JsValue::undefined();\n    /// assert!(matches!(value.variant(), JsVariant::Undefined));\n    /// ```\n    #[inline]\n    #[must_use]\n    pub fn variant(&self) -> JsVariant {\n        self.0.as_variant()\n    }\n\n    /// Creates a new `undefined` value.\n    ///\n    /// # Examples\n    ///\n    /// ```\n    /// use boa_engine::JsValue;\n    ///\n    /// let value = JsValue::undefined();\n    /// assert!(value.is_undefined());\n    /// assert!(value.is_null_or_undefined());\n    /// assert_eq!(value.display().to_string(), \"undefined\");\n    /// ```\n    #[inline]\n    #[must_use]\n    pub const fn undefined() -> Self {\n        Self::from_inner(inner::InnerValue::undefined())\n    }\n\n    /// Creates a new `null` value.\n    ///\n    /// # Examples\n    ///\n    /// ```\n    /// use boa_engine::JsValue;\n    ///\n    /// let value = JsValue::null();\n    /// assert!(value.is_null());\n    /// assert!(value.is_null_or_undefined());\n    /// assert_eq!(value.display().to_string(), \"null\");\n    /// ```\n    #[inline]\n    #[must_use]\n    pub const fn null() -> Self {\n        Self::from_inner(inner::InnerValue::null())\n    }\n\n    /// Creates a new number with `NaN` value.\n    ///\n    /// # Examples\n    ///\n    /// ```\n    /// use boa_engine::JsValue;\n    ///\n    /// let value = JsValue::nan();\n    /// assert!(value.is_number());\n    /// // NaN is not equal to itself.\n    /// assert!(value.as_number().unwrap().is_nan());\n    /// assert_eq!(value.display().to_string(), \"NaN\");\n    /// ```\n    #[inline]\n    #[must_use]\n    pub const fn nan() -> Self {\n        Self::from_inner(inner::InnerValue::float64(f64::NAN))\n    }\n\n    /// Creates a new number with `Infinity` value.\n    ///\n    /// # Examples\n    ///\n    /// ```\n    /// use boa_engine::JsValue;\n    ///\n    /// let value = JsValue::positive_infinity();\n    /// assert!(value.is_number());\n    /// assert_eq!(value.as_number(), Some(f64::INFINITY));\n    /// assert_eq!(value.display().to_string(), \"Infinity\");\n    /// ```\n    #[inline]\n    #[must_use]\n    pub const fn positive_infinity() -> Self {\n        Self::from_inner(inner::InnerValue::float64(f64::INFINITY))\n    }\n\n    /// Creates a new number with `-Infinity` value.\n    ///\n    /// # Examples\n    ///\n    /// ```\n    /// use boa_engine::JsValue;\n    ///\n    /// let value = JsValue::negative_infinity();\n    /// assert!(value.is_number());\n    /// assert_eq!(value.as_number(), Some(f64::NEG_INFINITY));\n    /// assert_eq!(value.display().to_string(), \"-Infinity\");\n    /// ```\n    #[inline]\n    #[must_use]\n    pub const fn negative_infinity() -> Self {\n        Self::from_inner(inner::InnerValue::float64(f64::NEG_INFINITY))\n    }\n\n    /// Creates a new number from a float.\n    ///\n    /// # Examples\n    ///\n    /// ```\n    /// use boa_engine::JsValue;\n    ///\n    /// let value = JsValue::rational(3.14);\n    /// assert!(value.is_number());\n    /// assert_eq!(value.as_number(), Some(3.14));\n    ///\n    /// // Can also represent special float values.\n    /// let neg_zero = JsValue::rational(-0.0);\n    /// assert!(neg_zero.is_number());\n    /// ```\n    // #[inline]\n    #[must_use]\n    pub fn rational(rational: f64) -> Self {\n        Self::from_inner(inner::InnerValue::float64(rational))\n    }\n\n    /// Returns true if the value is an object.\n    ///\n    /// # Examples\n    ///\n    /// ```\n    /// use boa_engine::{JsValue, object::JsObject};\n    ///\n    /// let obj = JsValue::new(JsObject::with_null_proto());\n    /// assert!(obj.is_object());\n    ///\n    /// let number = JsValue::new(42);\n    /// assert!(!number.is_object());\n    /// ```\n    #[inline]\n    #[must_use]\n    pub fn is_object(&self) -> bool {\n        self.0.is_object()\n    }\n\n    /// Returns the object if the value is object, otherwise `None`.\n    ///\n    /// # Examples\n    ///\n    /// ```\n    /// use boa_engine::{JsValue, object::JsObject};\n    ///\n    /// let obj = JsValue::new(JsObject::with_null_proto());\n    /// assert!(obj.as_object().is_some());\n    ///\n    /// let number = JsValue::new(42);\n    /// assert!(number.as_object().is_none());\n    /// ```\n    #[inline]\n    #[must_use]\n    pub fn as_object(&self) -> Option<JsObject> {\n        self.0.as_object()\n    }\n\n    /// Consumes the value and return the inner object if it was an object.\n    ///\n    /// # Examples\n    ///\n    /// ```\n    /// use boa_engine::{JsValue, object::JsObject};\n    ///\n    /// let obj = JsValue::new(JsObject::with_null_proto());\n    /// let inner = obj.into_object();\n    /// assert!(inner.is_some());\n    ///\n    /// let number = JsValue::new(42);\n    /// assert!(number.into_object().is_none());\n    /// ```\n    #[inline]\n    #[must_use]\n    pub fn into_object(self) -> Option<JsObject> {\n        self.0.as_object()\n    }\n\n    /// It determines if the value is a callable function with a `[[Call]]` internal method.\n    ///\n    /// More information:\n    /// - [ECMAScript reference][spec]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-iscallable\n    ///\n    /// # Examples\n    ///\n    /// ```\n    /// use boa_engine::{Context, JsValue, NativeFunction};\n    ///\n    /// let context = &mut Context::default();\n    /// let native_fn = NativeFunction::from_copy_closure(|_, _, _| Ok(JsValue::undefined()));\n    /// let js_value = JsValue::from(native_fn.to_js_function(context.realm()));\n    /// assert!(js_value.is_callable());\n    ///\n    /// let number = JsValue::new(42);\n    /// assert!(!number.is_callable());\n    /// ```\n    #[inline]\n    #[must_use]\n    pub fn is_callable(&self) -> bool {\n        self.as_object().as_ref().is_some_and(JsObject::is_callable)\n    }\n\n    /// Returns the callable value if the value is callable, otherwise `None`.\n    ///\n    /// # Examples\n    ///\n    /// ```\n    /// use boa_engine::{Context, JsValue, NativeFunction};\n    ///\n    /// let context = &mut Context::default();\n    /// let native_fn = NativeFunction::from_copy_closure(|_, _, _| Ok(JsValue::undefined()));\n    /// let js_value = JsValue::from(native_fn.to_js_function(context.realm()));\n    /// assert!(js_value.as_callable().is_some());\n    ///\n    /// let number = JsValue::new(42);\n    /// assert!(number.as_callable().is_none());\n    /// ```\n    #[inline]\n    #[must_use]\n    pub fn as_callable(&self) -> Option<JsObject> {\n        self.as_object().filter(JsObject::is_callable)\n    }\n\n    /// Returns a [`JsFunction`] if the value is callable, otherwise `None`.\n    /// This is equivalent to `JsFunction::from_object(value.as_callable()?)`.\n    ///\n    /// # Examples\n    ///\n    /// ```\n    /// use boa_engine::{Context, JsValue, NativeFunction};\n    ///\n    /// let context = &mut Context::default();\n    /// let native_fn = NativeFunction::from_copy_closure(|_, _, _| Ok(JsValue::undefined()));\n    /// let js_value = JsValue::from(native_fn.to_js_function(context.realm()));\n    /// assert!(js_value.as_function().is_some());\n    ///\n    /// let number = JsValue::new(42);\n    /// assert!(number.as_function().is_none());\n    /// ```\n    #[inline]\n    #[must_use]\n    pub fn as_function(&self) -> Option<JsFunction> {\n        self.as_callable().and_then(JsFunction::from_object)\n    }\n\n    /// Returns true if the value is a constructor object.\n    ///\n    /// # Examples\n    ///\n    /// ```\n    /// use boa_engine::{Context, JsValue, Source};\n    ///\n    /// let mut context = Context::default();\n    /// // Classes and regular functions are constructors.\n    /// let class = context.eval(Source::from_bytes(b\"(class {})\")).unwrap();\n    /// assert!(class.is_constructor());\n    ///\n    /// // Arrow functions are not constructors.\n    /// let arrow = context.eval(Source::from_bytes(b\"(() => {})\")).unwrap();\n    /// assert!(!arrow.is_constructor());\n    /// ```\n    #[inline]\n    #[must_use]\n    pub fn is_constructor(&self) -> bool {\n        self.as_object()\n            .as_ref()\n            .is_some_and(JsObject::is_constructor)\n    }\n\n    /// Returns the constructor if the value is a constructor, otherwise `None`.\n    ///\n    /// # Examples\n    ///\n    /// ```\n    /// use boa_engine::{Context, JsValue, Source};\n    ///\n    /// let mut context = Context::default();\n    /// let class = context.eval(Source::from_bytes(b\"(class {})\")).unwrap();\n    /// assert!(class.as_constructor().is_some());\n    ///\n    /// let number = JsValue::new(42);\n    /// assert!(number.as_constructor().is_none());\n    /// ```\n    #[inline]\n    #[must_use]\n    pub fn as_constructor(&self) -> Option<JsObject> {\n        self.as_object().filter(JsObject::is_constructor)\n    }\n\n    /// Returns true if the value is a promise object.\n    ///\n    /// # Examples\n    ///\n    /// ```\n    /// use boa_engine::{Context, JsValue, object::builtins::JsPromise};\n    ///\n    /// let context = &mut Context::default();\n    /// let (promise, _) = JsPromise::new_pending(context);\n    /// let js_value = JsValue::from(promise);\n    /// assert!(js_value.is_promise());\n    ///\n    /// let number = JsValue::new(42);\n    /// assert!(!number.is_promise());\n    /// ```\n    #[inline]\n    #[must_use]\n    pub fn is_promise(&self) -> bool {\n        self.as_object().is_some_and(|obj| obj.is::<Promise>())\n    }\n\n    /// Returns the value as an object if the value is a promise, otherwise `None`.\n    #[inline]\n    #[must_use]\n    pub(crate) fn as_promise_object(&self) -> Option<JsObject<Promise>> {\n        self.as_object()\n            .and_then(|obj| obj.downcast::<Promise>().ok())\n    }\n\n    /// Returns the value as a promise if the value is a promise, otherwise `None`.\n    ///\n    /// # Examples\n    ///\n    /// ```\n    /// use boa_engine::{Context, JsValue, object::builtins::JsPromise};\n    ///\n    /// let context = &mut Context::default();\n    /// let (promise, _) = JsPromise::new_pending(context);\n    /// let js_value = JsValue::from(promise);\n    /// assert!(js_value.as_promise().is_some());\n    ///\n    /// let number = JsValue::new(42);\n    /// assert!(number.as_promise().is_none());\n    /// ```\n    #[inline]\n    #[must_use]\n    pub fn as_promise(&self) -> Option<JsPromise> {\n        self.as_promise_object().map(JsPromise::from)\n    }\n\n    /// Returns true if the value is a regular expression object.\n    ///\n    /// # Examples\n    ///\n    /// ```\n    /// use boa_engine::{Context, JsValue, js_string, object::builtins::JsRegExp};\n    ///\n    /// let context = &mut Context::default();\n    /// let regexp = JsRegExp::new(js_string!(\"abc\"), js_string!(\"g\"), context).unwrap();\n    /// let js_value = JsValue::from(regexp);\n    /// assert!(js_value.is_regexp());\n    ///\n    /// let string = JsValue::new(js_string!(\"abc\"));\n    /// assert!(!string.is_regexp());\n    /// ```\n    #[inline]\n    #[must_use]\n    pub fn is_regexp(&self) -> bool {\n        self.as_object().is_some_and(|obj| obj.is::<RegExp>())\n    }\n\n    /// Returns the value as a regular expression if the value is a regexp, otherwise `None`.\n    ///\n    /// # Examples\n    ///\n    /// ```\n    /// use boa_engine::{Context, JsValue, js_string, object::builtins::JsRegExp};\n    ///\n    /// let context = &mut Context::default();\n    /// let regexp = JsRegExp::new(js_string!(\"abc\"), js_string!(\"g\"), context).unwrap();\n    /// let js_value = JsValue::from(regexp);\n    /// assert!(js_value.as_regexp().is_some());\n    ///\n    /// let number = JsValue::new(42);\n    /// assert!(number.as_regexp().is_none());\n    /// ```\n    #[inline]\n    #[must_use]\n    pub fn as_regexp(&self) -> Option<JsRegExp> {\n        self.as_object()\n            .filter(|obj| obj.is::<RegExp>())\n            .and_then(|o| JsRegExp::from_object(o).ok())\n    }\n\n    /// Returns true if the value is a symbol.\n    ///\n    /// # Examples\n    ///\n    /// ```\n    /// use boa_engine::{JsValue, JsSymbol};\n    ///\n    /// let sym = JsValue::new(JsSymbol::new(None).unwrap());\n    /// assert!(sym.is_symbol());\n    ///\n    /// let string = JsValue::new(boa_engine::js_string!(\"hello\"));\n    /// assert!(!string.is_symbol());\n    /// ```\n    #[inline]\n    #[must_use]\n    pub fn is_symbol(&self) -> bool {\n        self.0.is_symbol()\n    }\n\n    /// Returns the symbol if the value is a symbol, otherwise `None`.\n    ///\n    /// # Examples\n    ///\n    /// ```\n    /// use boa_engine::{JsValue, JsSymbol};\n    ///\n    /// let sym = JsValue::new(JsSymbol::new(None).unwrap());\n    /// assert!(sym.as_symbol().is_some());\n    ///\n    /// let number = JsValue::new(42);\n    /// assert!(number.as_symbol().is_none());\n    /// ```\n    #[inline]\n    #[must_use]\n    pub fn as_symbol(&self) -> Option<JsSymbol> {\n        self.0.as_symbol()\n    }\n\n    /// Returns true if the value is undefined.\n    ///\n    /// # Examples\n    ///\n    /// ```\n    /// use boa_engine::JsValue;\n    ///\n    /// assert!(JsValue::undefined().is_undefined());\n    /// assert!(!JsValue::null().is_undefined());\n    /// assert!(!JsValue::new(0).is_undefined());\n    /// ```\n    #[inline]\n    #[must_use]\n    pub fn is_undefined(&self) -> bool {\n        self.0.is_undefined()\n    }\n\n    /// Returns true if the value is null.\n    ///\n    /// # Examples\n    ///\n    /// ```\n    /// use boa_engine::JsValue;\n    ///\n    /// assert!(JsValue::null().is_null());\n    /// assert!(!JsValue::undefined().is_null());\n    /// assert!(!JsValue::new(0).is_null());\n    /// ```\n    #[inline]\n    #[must_use]\n    pub fn is_null(&self) -> bool {\n        self.0.is_null()\n    }\n\n    /// Returns true if the value is null or undefined.\n    ///\n    /// # Examples\n    ///\n    /// ```\n    /// use boa_engine::JsValue;\n    ///\n    /// assert!(JsValue::null().is_null_or_undefined());\n    /// assert!(JsValue::undefined().is_null_or_undefined());\n    /// assert!(!JsValue::new(0).is_null_or_undefined());\n    /// assert!(!JsValue::new(false).is_null_or_undefined());\n    /// ```\n    #[inline]\n    #[must_use]\n    pub fn is_null_or_undefined(&self) -> bool {\n        self.0.is_null_or_undefined()\n    }\n\n    /// Returns the number if the value is a finite integral Number value, otherwise `None`.\n    ///\n    /// More information:\n    /// - [ECMAScript reference][spec]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-isintegralnumber\n    ///\n    /// # Examples\n    ///\n    /// ```\n    /// use boa_engine::JsValue;\n    ///\n    /// // Integers are returned directly.\n    /// assert_eq!(JsValue::new(42).as_i32(), Some(42));\n    ///\n    /// // Floats that are whole numbers also succeed.\n    /// assert_eq!(JsValue::new(5.0).as_i32(), Some(5));\n    ///\n    /// // Non-integral floats return None.\n    /// assert_eq!(JsValue::new(3.14).as_i32(), None);\n    ///\n    /// // Non-number types return None.\n    /// assert_eq!(JsValue::new(true).as_i32(), None);\n    /// ```\n    #[inline]\n    #[must_use]\n    #[allow(clippy::float_cmp)]\n    pub fn as_i32(&self) -> Option<i32> {\n        if let Some(integer) = self.0.as_integer32() {\n            return Some(integer);\n        }\n\n        if let Some(rational) = self.0.as_float64() {\n            let int_val = rational as i32;\n            // Use bitwise comparison to handle -0.0 correctly\n            if rational.to_bits() == f64::from(int_val).to_bits() {\n                return Some(int_val);\n            }\n        }\n        None\n    }\n\n    /// Returns true if the value is a number.\n    ///\n    /// # Examples\n    ///\n    /// ```\n    /// use boa_engine::JsValue;\n    ///\n    /// assert!(JsValue::new(42).is_number());\n    /// assert!(JsValue::new(3.14).is_number());\n    /// assert!(JsValue::nan().is_number());\n    ///\n    /// assert!(!JsValue::new(true).is_number());\n    /// assert!(!JsValue::undefined().is_number());\n    /// ```\n    #[inline]\n    #[must_use]\n    pub fn is_number(&self) -> bool {\n        self.0.is_integer32() || self.0.is_float64()\n    }\n\n    /// Returns true if the value is a negative zero (`-0`).\n    #[inline]\n    #[must_use]\n    pub(crate) fn is_negative_zero(&self) -> bool {\n        self.0.is_negative_zero()\n    }\n\n    /// Returns the number if the value is a number, otherwise `None`.\n    ///\n    /// # Examples\n    ///\n    /// ```\n    /// use boa_engine::JsValue;\n    ///\n    /// assert_eq!(JsValue::new(42).as_number(), Some(42.0));\n    /// assert_eq!(JsValue::new(3.14).as_number(), Some(3.14));\n    ///\n    /// // Non-number types return None.\n    /// assert_eq!(JsValue::null().as_number(), None);\n    /// assert_eq!(JsValue::new(true).as_number(), None);\n    /// ```\n    #[inline]\n    #[must_use]\n    pub fn as_number(&self) -> Option<f64> {\n        match self.variant() {\n            JsVariant::Integer32(i) => Some(f64::from(i)),\n            JsVariant::Float64(f) => Some(f),\n            _ => None,\n        }\n    }\n\n    /// Returns true if the value is a string.\n    ///\n    /// # Examples\n    ///\n    /// ```\n    /// use boa_engine::{JsValue, js_string};\n    ///\n    /// let s = JsValue::new(js_string!(\"hello\"));\n    /// assert!(s.is_string());\n    ///\n    /// let number = JsValue::new(42);\n    /// assert!(!number.is_string());\n    /// ```\n    #[inline]\n    #[must_use]\n    pub fn is_string(&self) -> bool {\n        self.0.is_string()\n    }\n\n    /// Returns the string if the value is a string, otherwise `None`.\n    ///\n    /// # Examples\n    ///\n    /// ```\n    /// use boa_engine::{JsValue, js_string};\n    ///\n    /// let s = JsValue::new(js_string!(\"hello\"));\n    /// assert_eq!(s.as_string().map(|s| s == js_string!(\"hello\")), Some(true));\n    ///\n    /// let number = JsValue::new(42);\n    /// assert!(number.as_string().is_none());\n    /// ```\n    #[inline]\n    #[must_use]\n    pub fn as_string(&self) -> Option<JsString> {\n        self.0.as_string()\n    }\n\n    /// Returns true if the value is a boolean.\n    ///\n    /// # Examples\n    ///\n    /// ```\n    /// use boa_engine::JsValue;\n    ///\n    /// assert!(JsValue::new(true).is_boolean());\n    /// assert!(JsValue::new(false).is_boolean());\n    ///\n    /// assert!(!JsValue::new(1).is_boolean());\n    /// assert!(!JsValue::null().is_boolean());\n    /// ```\n    #[inline]\n    #[must_use]\n    pub fn is_boolean(&self) -> bool {\n        self.0.is_bool()\n    }\n\n    /// Returns the boolean if the value is a boolean, otherwise `None`.\n    ///\n    /// # Examples\n    ///\n    /// ```\n    /// use boa_engine::JsValue;\n    ///\n    /// assert_eq!(JsValue::new(true).as_boolean(), Some(true));\n    /// assert_eq!(JsValue::new(false).as_boolean(), Some(false));\n    ///\n    /// // Non-boolean types return None, even \"truthy\" or \"falsy\" ones.\n    /// assert_eq!(JsValue::new(1).as_boolean(), None);\n    /// assert_eq!(JsValue::new(0).as_boolean(), None);\n    /// ```\n    #[inline]\n    #[must_use]\n    pub fn as_boolean(&self) -> Option<bool> {\n        self.0.as_bool()\n    }\n\n    /// Returns true if the value is a bigint.\n    ///\n    /// # Examples\n    ///\n    /// ```\n    /// use boa_engine::{JsValue, JsBigInt};\n    ///\n    /// let big = JsValue::new(JsBigInt::from(42));\n    /// assert!(big.is_bigint());\n    ///\n    /// // Regular numbers are not bigints.\n    /// let number = JsValue::new(42);\n    /// assert!(!number.is_bigint());\n    /// ```\n    #[inline]\n    #[must_use]\n    pub fn is_bigint(&self) -> bool {\n        self.0.is_bigint()\n    }\n\n    /// Returns a `BigInt` if the value is a `BigInt` primitive.\n    ///\n    /// # Examples\n    ///\n    /// ```\n    /// use boa_engine::{JsValue, JsBigInt};\n    ///\n    /// let big = JsValue::new(JsBigInt::from(100));\n    /// assert!(big.as_bigint().is_some());\n    ///\n    /// let number = JsValue::new(100);\n    /// assert!(number.as_bigint().is_none());\n    /// ```\n    #[inline]\n    #[must_use]\n    pub fn as_bigint(&self) -> Option<JsBigInt> {\n        self.0.as_bigint()\n    }\n\n    /// Converts the value to a `bool` type.\n    ///\n    /// More information:\n    ///  - [ECMAScript][spec]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-toboolean\n    ///\n    /// # Examples\n    ///\n    /// ```\n    /// use boa_engine::{JsValue, js_string};\n    ///\n    /// // Numbers: 0 and NaN are false, everything else is true.\n    /// assert!(!JsValue::new(0).to_boolean());\n    /// assert!(!JsValue::nan().to_boolean());\n    /// assert!(JsValue::new(1).to_boolean());\n    /// assert!(JsValue::new(-1).to_boolean());\n    ///\n    /// // Strings: empty string is false, non-empty is true.\n    /// assert!(!JsValue::new(js_string!(\"\")).to_boolean());\n    /// assert!(JsValue::new(js_string!(\"hello\")).to_boolean());\n    ///\n    /// // null and undefined are always false.\n    /// assert!(!JsValue::null().to_boolean());\n    /// assert!(!JsValue::undefined().to_boolean());\n    ///\n    /// // Booleans pass through.\n    /// assert!(JsValue::new(true).to_boolean());\n    /// assert!(!JsValue::new(false).to_boolean());\n    /// ```\n    #[must_use]\n    #[inline]\n    pub fn to_boolean(&self) -> bool {\n        self.0.to_boolean()\n    }\n\n    /// The abstract operation `ToPrimitive` takes an input argument and an optional argument\n    /// `PreferredType`.\n    ///\n    /// <https://tc39.es/ecma262/#sec-toprimitive>\n    #[inline]\n    pub fn to_primitive(\n        &self,\n        context: &mut Context,\n        preferred_type: PreferredType,\n    ) -> JsResult<Self> {\n        // 1. Assert: input is an ECMAScript language value. (always a value not need to check)\n        // 2. If Type(input) is Object, then\n        if let Some(o) = self.as_object() {\n            return o.to_primitive(context, preferred_type);\n        }\n\n        // 3. Return input.\n        Ok(self.clone())\n    }\n\n    /// `7.1.13 ToBigInt ( argument )`\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-tobigint\n    pub fn to_bigint(&self, context: &mut Context) -> JsResult<JsBigInt> {\n        match self.variant() {\n            JsVariant::Null => Err(JsNativeError::typ()\n                .with_message(\"cannot convert null to a BigInt\")\n                .into()),\n            JsVariant::Undefined => Err(JsNativeError::typ()\n                .with_message(\"cannot convert undefined to a BigInt\")\n                .into()),\n            JsVariant::String(string) => JsBigInt::from_js_string(&string).map_or_else(\n                || {\n                    Err(JsNativeError::syntax()\n                        .with_message(format!(\n                            \"cannot convert string '{}' to bigint primitive\",\n                            string.to_std_string_escaped()\n                        ))\n                        .into())\n                },\n                Ok,\n            ),\n            JsVariant::Boolean(true) => Ok(JsBigInt::one()),\n            JsVariant::Boolean(false) => Ok(JsBigInt::zero()),\n            JsVariant::Integer32(_) | JsVariant::Float64(_) => Err(JsNativeError::typ()\n                .with_message(\"cannot convert Number to a BigInt\")\n                .into()),\n            JsVariant::BigInt(b) => Ok(b),\n            JsVariant::Object(o) => o\n                .to_primitive(context, PreferredType::Number)?\n                .to_bigint(context),\n            JsVariant::Symbol(_) => Err(JsNativeError::typ()\n                .with_message(\"cannot convert Symbol to a BigInt\")\n                .into()),\n        }\n    }\n\n    /// Returns an object that implements `Display`.\n    ///\n    /// By default, the internals are not shown, but they can be toggled\n    /// with [`ValueDisplay::internals`] method.\n    ///\n    /// # Examples\n    ///\n    /// ```\n    /// use boa_engine::JsValue;\n    ///\n    /// let value = JsValue::new(3);\n    ///\n    /// println!(\"{}\", value.display());\n    /// ```\n    #[must_use]\n    #[inline]\n    pub const fn display(&self) -> ValueDisplay<'_> {\n        ValueDisplay {\n            value: self,\n            internals: false,\n        }\n    }\n\n    /// Converts the value to a string.\n    ///\n    /// This function is equivalent to `String(value)` in JavaScript.\n    pub fn to_string(&self, context: &mut Context) -> JsResult<JsString> {\n        match self.variant() {\n            JsVariant::Null => Ok(js_string!(\"null\")),\n            JsVariant::Undefined => Ok(js_string!(\"undefined\")),\n            JsVariant::Boolean(true) => Ok(js_string!(\"true\")),\n            JsVariant::Boolean(false) => Ok(js_string!(\"false\")),\n            JsVariant::Float64(rational) => Ok(JsString::from(rational)),\n            JsVariant::Integer32(integer) => Ok(JsString::from(integer)),\n            JsVariant::String(string) => Ok(string),\n            JsVariant::Symbol(_) => Err(JsNativeError::typ()\n                .with_message(\"can't convert symbol to string\")\n                .into()),\n            JsVariant::BigInt(bigint) => Ok(bigint.to_string().into()),\n            JsVariant::Object(o) => o\n                .to_primitive(context, PreferredType::String)?\n                .to_string(context),\n        }\n    }\n\n    /// Converts the value to an Object.\n    ///\n    /// This function is equivalent to `Object(value)` in JavaScript.\n    ///\n    /// See: <https://tc39.es/ecma262/#sec-toobject>\n    pub fn to_object(&self, context: &mut Context) -> JsResult<JsObject> {\n        match self.variant() {\n            JsVariant::Undefined | JsVariant::Null => Err(JsNativeError::typ()\n                .with_message(\"cannot convert 'null' or 'undefined' to object\")\n                .into()),\n            JsVariant::Boolean(boolean) => Ok(context\n                .intrinsics()\n                .templates()\n                .boolean()\n                .create(boolean, Vec::default())),\n            JsVariant::Integer32(integer) => Ok(context\n                .intrinsics()\n                .templates()\n                .number()\n                .create(f64::from(integer), Vec::default())),\n            JsVariant::Float64(rational) => Ok(context\n                .intrinsics()\n                .templates()\n                .number()\n                .create(rational, Vec::default())),\n            JsVariant::String(string) => {\n                let len = string.len();\n                Ok(context\n                    .intrinsics()\n                    .templates()\n                    .string()\n                    .create(string, vec![len.into()]))\n            }\n            JsVariant::Symbol(symbol) => Ok(context\n                .intrinsics()\n                .templates()\n                .symbol()\n                .create(symbol, Vec::default())),\n            JsVariant::BigInt(bigint) => Ok(context\n                .intrinsics()\n                .templates()\n                .bigint()\n                .create(bigint, Vec::default())),\n            JsVariant::Object(jsobject) => Ok(jsobject),\n        }\n    }\n\n    pub(crate) fn base_class(&self, context: &Context) -> JsResult<JsObject> {\n        let constructors = context.intrinsics().constructors();\n        match self.variant() {\n            JsVariant::Undefined | JsVariant::Null => Err(JsNativeError::typ()\n                .with_message(\"cannot convert 'null' or 'undefined' to object\")\n                .into()),\n            JsVariant::Boolean(_) => Ok(constructors.boolean().prototype()),\n            JsVariant::Integer32(_) | JsVariant::Float64(_) => {\n                Ok(constructors.number().prototype())\n            }\n            JsVariant::String(_) => Ok(constructors.string().prototype()),\n            JsVariant::Symbol(_) => Ok(constructors.symbol().prototype()),\n            JsVariant::BigInt(_) => Ok(constructors.bigint().prototype()),\n            JsVariant::Object(object) => Ok(object.clone()),\n        }\n    }\n\n    /// Converts the value to a `PropertyKey`, that can be used as a key for properties.\n    ///\n    /// See <https://tc39.es/ecma262/#sec-topropertykey>\n    pub fn to_property_key(&self, context: &mut Context) -> JsResult<PropertyKey> {\n        match self.variant() {\n            // fast path\n            //\n            // The compiler will surely make this a jump table, but in case it\n            // doesn't, we put the \"expected\" property key types first\n            // (integer, string, symbol), then the rest of the variants.\n            JsVariant::Integer32(integer) => Ok(integer.into()),\n            JsVariant::String(string) => Ok(string.into()),\n            JsVariant::Symbol(symbol) => Ok(symbol.into()),\n\n            // We also inline the call to `to_string`, removing the\n            // double match against `self.variant()`.\n            JsVariant::Float64(float) => Ok(JsString::from(float).into()),\n            JsVariant::Undefined => Ok(js_string!(\"undefined\").into()),\n            JsVariant::Null => Ok(js_string!(\"null\").into()),\n            JsVariant::Boolean(true) => Ok(js_string!(\"true\").into()),\n            JsVariant::Boolean(false) => Ok(js_string!(\"false\").into()),\n            JsVariant::BigInt(bigint) => Ok(JsString::from(bigint.to_string()).into()),\n\n            // slow path\n            // Cannot infinitely recurse since it is guaranteed that `to_primitive` returns a non-object\n            // value or errors.\n            JsVariant::Object(o) => o\n                .to_primitive(context, PreferredType::String)?\n                .to_property_key(context),\n        }\n    }\n\n    /// It returns value converted to a numeric value of type `Number` or `BigInt`.\n    ///\n    /// See: <https://tc39.es/ecma262/#sec-tonumeric>\n    pub fn to_numeric(&self, context: &mut Context) -> JsResult<Numeric> {\n        // 1. Let primValue be ? ToPrimitive(value, number).\n        let primitive = self.to_primitive(context, PreferredType::Number)?;\n\n        // 2. If primValue is a BigInt, return primValue.\n        if let Some(bigint) = primitive.as_bigint() {\n            return Ok(bigint.into());\n        }\n\n        // 3. Return ? ToNumber(primValue).\n        Ok(primitive.to_number(context)?.into())\n    }\n\n    /// Converts a value to an integral 32-bit unsigned integer.\n    ///\n    /// This function is equivalent to `value | 0` in JavaScript\n    ///\n    /// See: <https://tc39.es/ecma262/#sec-touint32>\n    pub fn to_u32(&self, context: &mut Context) -> JsResult<u32> {\n        // This is the fast path, if the value is Integer we can just return it.\n        if let Some(number) = self.0.as_integer32()\n            && let Ok(number) = u32::try_from(number)\n        {\n            return Ok(number);\n        }\n        let number = self.to_number(context)?;\n\n        Ok(f64_to_uint32(number))\n    }\n\n    /// Converts a value to an integral 32-bit signed integer.\n    ///\n    /// See: <https://tc39.es/ecma262/#sec-toint32>\n    pub fn to_i32(&self, context: &mut Context) -> JsResult<i32> {\n        // This is the fast path, if the value is Integer we can just return it.\n        if let Some(number) = self.0.as_integer32() {\n            return Ok(number);\n        }\n        let number = self.to_number(context)?;\n\n        Ok(f64_to_int32(number))\n    }\n\n    /// `7.1.10 ToInt8 ( argument )`\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-toint8\n    pub fn to_int8(&self, context: &mut Context) -> JsResult<i8> {\n        // 1. Let number be ? ToNumber(argument).\n        let number = self.to_number(context)?;\n\n        // 2. If number is NaN, +0𝔽, -0𝔽, +∞𝔽, or -∞𝔽, return +0𝔽.\n        if number.is_nan() || number.is_zero() || number.is_infinite() {\n            return Ok(0);\n        }\n\n        // 3. Let int be the mathematical value whose sign is the sign of number and whose magnitude is floor(abs(ℝ(number))).\n        let int = number.abs().floor().copysign(number) as i64;\n\n        // 4. Let int8bit be int modulo 2^8.\n        let int_8_bit = int % 2i64.pow(8);\n\n        // 5. If int8bit ≥ 2^7, return 𝔽(int8bit - 2^8); otherwise return 𝔽(int8bit).\n        if int_8_bit >= 2i64.pow(7) {\n            Ok((int_8_bit - 2i64.pow(8)) as i8)\n        } else {\n            Ok(int_8_bit as i8)\n        }\n    }\n\n    /// `7.1.11 ToUint8 ( argument )`\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-touint8\n    pub fn to_uint8(&self, context: &mut Context) -> JsResult<u8> {\n        // 1. Let number be ? ToNumber(argument).\n        let number = self.to_number(context)?;\n\n        // 2. If number is NaN, +0𝔽, -0𝔽, +∞𝔽, or -∞𝔽, return +0𝔽.\n        if number.is_nan() || number.is_zero() || number.is_infinite() {\n            return Ok(0);\n        }\n\n        // 3. Let int be the mathematical value whose sign is the sign of number and whose magnitude is floor(abs(ℝ(number))).\n        let int = number.abs().floor().copysign(number) as i64;\n\n        // 4. Let int8bit be int modulo 2^8.\n        let int_8_bit = int % 2i64.pow(8);\n\n        // 5. Return 𝔽(int8bit).\n        Ok(int_8_bit as u8)\n    }\n\n    /// `7.1.12 ToUint8Clamp ( argument )`\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-touint8clamp\n    pub fn to_uint8_clamp(&self, context: &mut Context) -> JsResult<u8> {\n        // 1. Let number be ? ToNumber(argument).\n        let number = self.to_number(context)?;\n\n        // 2. If number is NaN, return +0𝔽.\n        if number.is_nan() {\n            return Ok(0);\n        }\n\n        // 3. If ℝ(number) ≤ 0, return +0𝔽.\n        if number <= 0.0 {\n            return Ok(0);\n        }\n\n        // 4. If ℝ(number) ≥ 255, return 255𝔽.\n        if number >= 255.0 {\n            return Ok(255);\n        }\n\n        // 5. Let f be floor(ℝ(number)).\n        let f = number.floor();\n\n        // 6. If f + 0.5 < ℝ(number), return 𝔽(f + 1).\n        if f + 0.5 < number {\n            return Ok(f as u8 + 1);\n        }\n\n        // 7. If ℝ(number) < f + 0.5, return 𝔽(f).\n        if number < f + 0.5 {\n            return Ok(f as u8);\n        }\n\n        // 8. If f is odd, return 𝔽(f + 1).\n        if !(f as u8).is_multiple_of(2) {\n            return Ok(f as u8 + 1);\n        }\n\n        // 9. Return 𝔽(f).\n        Ok(f as u8)\n    }\n\n    /// `7.1.8 ToInt16 ( argument )`\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-toint16\n    pub fn to_int16(&self, context: &mut Context) -> JsResult<i16> {\n        // 1. Let number be ? ToNumber(argument).\n        let number = self.to_number(context)?;\n\n        // 2. If number is NaN, +0𝔽, -0𝔽, +∞𝔽, or -∞𝔽, return +0𝔽.\n        if number.is_nan() || number.is_zero() || number.is_infinite() {\n            return Ok(0);\n        }\n\n        // 3. Let int be the mathematical value whose sign is the sign of number and whose magnitude is floor(abs(ℝ(number))).\n        let int = number.abs().floor().copysign(number) as i64;\n\n        // 4. Let int16bit be int modulo 2^16.\n        let int_16_bit = int % 2i64.pow(16);\n\n        // 5. If int16bit ≥ 2^15, return 𝔽(int16bit - 2^16); otherwise return 𝔽(int16bit).\n        if int_16_bit >= 2i64.pow(15) {\n            Ok((int_16_bit - 2i64.pow(16)) as i16)\n        } else {\n            Ok(int_16_bit as i16)\n        }\n    }\n\n    /// `7.1.9 ToUint16 ( argument )`\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-touint16\n    pub fn to_uint16(&self, context: &mut Context) -> JsResult<u16> {\n        // 1. Let number be ? ToNumber(argument).\n        let number = self.to_number(context)?;\n\n        // 2. If number is NaN, +0𝔽, -0𝔽, +∞𝔽, or -∞𝔽, return +0𝔽.\n        if number.is_nan() || number.is_zero() || number.is_infinite() {\n            return Ok(0);\n        }\n\n        // 3. Let int be the mathematical value whose sign is the sign of number and whose magnitude is floor(abs(ℝ(number))).\n        let int = number.abs().floor().copysign(number) as i64;\n\n        // 4. Let int16bit be int modulo 2^16.\n        let int_16_bit = int % 2i64.pow(16);\n\n        // 5. Return 𝔽(int16bit).\n        Ok(int_16_bit as u16)\n    }\n\n    /// `7.1.15 ToBigInt64 ( argument )`\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-tobigint64\n    pub fn to_big_int64(&self, context: &mut Context) -> JsResult<i64> {\n        // 1. Let n be ? ToBigInt(argument).\n        let n = self.to_bigint(context)?;\n\n        // 2. Let int64bit be ℝ(n) modulo 2^64.\n        let int64_bit = n.as_inner().mod_floor(&TWO_E_64);\n\n        // 3. If int64bit ≥ 2^63, return ℤ(int64bit - 2^64); otherwise return ℤ(int64bit).\n        let value = if int64_bit >= *TWO_E_63 {\n            int64_bit.sub(&*TWO_E_64)\n        } else {\n            int64_bit\n        };\n\n        Ok(value\n            .to_i64()\n            .expect(\"should be within the range of `i64` by the mod operation\"))\n    }\n\n    /// `7.1.16 ToBigUint64 ( argument )`\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-tobiguint64\n    pub fn to_big_uint64(&self, context: &mut Context) -> JsResult<u64> {\n        // 1. Let n be ? ToBigInt(argument).\n        let n = self.to_bigint(context)?;\n\n        // 2. Let int64bit be ℝ(n) modulo 2^64.\n        // 3. Return ℤ(int64bit).\n        Ok(n.as_inner()\n            .mod_floor(&TWO_E_64)\n            .to_u64()\n            .expect(\"should be within the range of `u64` by the mod operation\"))\n    }\n\n    /// Converts a value to a non-negative integer if it is a valid integer index value.\n    ///\n    /// See: <https://tc39.es/ecma262/#sec-toindex>\n    pub fn to_index(&self, context: &mut Context) -> JsResult<u64> {\n        // 1. If value is undefined, then\n        if self.is_undefined() {\n            // a. Return 0.\n            return Ok(0);\n        }\n\n        // 2. Else,\n        // a. Let integer be ? ToIntegerOrInfinity(value).\n        let integer = self.to_integer_or_infinity(context)?;\n\n        // b. Let clamped be ! ToLength(𝔽(integer)).\n        let clamped = integer.clamp_finite(0, Number::MAX_SAFE_INTEGER as i64);\n\n        // c. If ! SameValue(𝔽(integer), clamped) is false, throw a RangeError exception.\n        if integer != clamped {\n            return Err(JsNativeError::range()\n                .with_message(\"Index must be between 0 and  2^53 - 1\")\n                .into());\n        }\n\n        // d. Assert: 0 ≤ integer ≤ 2^53 - 1.\n        debug_assert!(0 <= clamped && clamped <= Number::MAX_SAFE_INTEGER as i64);\n\n        // e. Return integer.\n        Ok(clamped as u64)\n    }\n\n    /// Converts argument to an integer suitable for use as the length of an array-like object.\n    ///\n    /// See: <https://tc39.es/ecma262/#sec-tolength>\n    pub fn to_length(&self, context: &mut Context) -> JsResult<u64> {\n        // 1. Let len be ? ToInteger(argument).\n        // 2. If len ≤ +0, return +0.\n        // 3. Return min(len, 2^53 - 1).\n        Ok(self\n            .to_integer_or_infinity(context)?\n            .clamp_finite(0, Number::MAX_SAFE_INTEGER as i64) as u64)\n    }\n\n    /// Abstract operation `ToIntegerOrInfinity ( argument )`\n    ///\n    /// This method converts a `Value` to an integer representing its `Number` value with\n    /// fractional part truncated, or to +∞ or -∞ when that `Number` value is infinite.\n    ///\n    /// More information:\n    /// - [ECMAScript reference][spec]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-tointegerorinfinity\n    pub fn to_integer_or_infinity(&self, context: &mut Context) -> JsResult<IntegerOrInfinity> {\n        // 1. Let number be ? ToNumber(argument).\n        let number = self.to_number(context)?;\n\n        // Continues on `IntegerOrInfinity::from::<f64>`\n        Ok(IntegerOrInfinity::from(number))\n    }\n\n    /// Converts a value to a double precision floating point.\n    ///\n    /// This function is equivalent to the unary `+` operator (`+value`) in JavaScript\n    ///\n    /// See: <https://tc39.es/ecma262/#sec-tonumber>\n    pub fn to_number(&self, context: &mut Context) -> JsResult<f64> {\n        match self.variant() {\n            JsVariant::Null => Ok(0.0),\n            JsVariant::Undefined => Ok(f64::NAN),\n            JsVariant::Boolean(b) => Ok(if b { 1.0 } else { 0.0 }),\n            JsVariant::String(string) => Ok(string.to_number()),\n            JsVariant::Float64(number) => Ok(number),\n            JsVariant::Integer32(integer) => Ok(f64::from(integer)),\n            JsVariant::Symbol(_) => Err(JsNativeError::typ()\n                .with_message(\"argument must not be a symbol\")\n                .into()),\n            JsVariant::BigInt(_) => Err(JsNativeError::typ()\n                .with_message(\"argument must not be a bigint\")\n                .into()),\n            JsVariant::Object(_) => {\n                let primitive = self.to_primitive(context, PreferredType::Number)?;\n                primitive.to_number(context)\n            }\n        }\n    }\n\n    /// Converts a value to a 16-bit floating point.\n    #[cfg(feature = \"float16\")]\n    pub fn to_f16(&self, context: &mut Context) -> JsResult<float16::f16> {\n        self.to_number(context).map(float16::f16::from_f64)\n    }\n\n    /// Converts a value to a 32 bit floating point.\n    pub fn to_f32(&self, context: &mut Context) -> JsResult<f32> {\n        self.to_number(context).map(|n| n as f32)\n    }\n\n    /// This is a more specialized version of `to_numeric`, including `BigInt`.\n    ///\n    /// This function is equivalent to `Number(value)` in JavaScript\n    ///\n    /// See: <https://tc39.es/ecma262/#sec-tonumeric>\n    pub fn to_numeric_number(&self, context: &mut Context) -> JsResult<f64> {\n        let primitive = self.to_primitive(context, PreferredType::Number)?;\n        if let Some(bigint) = primitive.as_bigint() {\n            return Ok(bigint.to_f64());\n        }\n        primitive.to_number(context)\n    }\n\n    /// Check if the `Value` can be converted to an `Object`\n    ///\n    /// The abstract operation `RequireObjectCoercible` takes argument argument.\n    /// It throws an error if argument is a value that cannot be converted to an Object using `ToObject`.\n    /// It is defined by [Table 15][table]\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///\n    /// [table]: https://tc39.es/ecma262/#table-14\n    /// [spec]: https://tc39.es/ecma262/#sec-requireobjectcoercible\n    ///\n    /// # Examples\n    ///\n    /// ```\n    /// use boa_engine::JsValue;\n    ///\n    /// // Most values are object-coercible.\n    /// assert!(JsValue::new(42).require_object_coercible().is_ok());\n    /// assert!(JsValue::new(true).require_object_coercible().is_ok());\n    ///\n    /// // null and undefined are not.\n    /// assert!(JsValue::null().require_object_coercible().is_err());\n    /// assert!(JsValue::undefined().require_object_coercible().is_err());\n    /// ```\n    #[inline]\n    pub fn require_object_coercible(&self) -> JsResult<&Self> {\n        if self.is_null_or_undefined() {\n            Err(JsNativeError::typ()\n                .with_message(\"cannot convert null or undefined to Object\")\n                .into())\n        } else {\n            Ok(self)\n        }\n    }\n\n    /// The abstract operation `ToPropertyDescriptor`.\n    ///\n    /// More information:\n    /// - [ECMAScript reference][spec]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-topropertydescriptor\n    #[inline]\n    pub fn to_property_descriptor(&self, context: &mut Context) -> JsResult<PropertyDescriptor> {\n        // 1. If Type(Obj) is not Object, throw a TypeError exception.\n        self.as_object()\n            .ok_or_else(|| {\n                JsNativeError::typ()\n                    .with_message(\"Cannot construct a property descriptor from a non-object\")\n                    .into()\n            })\n            .and_then(|obj| obj.to_property_descriptor(context))\n    }\n\n    /// `typeof` operator. Returns a string representing the type of the\n    /// given ECMA Value.\n    ///\n    /// More information:\n    /// - [ECMAScript reference][spec]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-typeof-operator\n    ///\n    /// # Examples\n    ///\n    /// ```\n    /// use boa_engine::{JsValue, js_string, JsSymbol};\n    ///\n    /// assert_eq!(JsValue::undefined().type_of(), \"undefined\");\n    /// assert_eq!(JsValue::null().type_of(), \"object\");\n    /// assert_eq!(JsValue::new(true).type_of(), \"boolean\");\n    /// assert_eq!(JsValue::new(42).type_of(), \"number\");\n    /// assert_eq!(JsValue::new(js_string!(\"hi\")).type_of(), \"string\");\n    /// assert_eq!(JsValue::new(JsSymbol::new(None).unwrap()).type_of(), \"symbol\");\n    /// ```\n    #[must_use]\n    pub fn type_of(&self) -> &'static str {\n        self.variant().type_of()\n    }\n\n    /// Same as [`JsValue::type_of`], but returning a [`JsString`] instead.\n    ///\n    /// # Examples\n    ///\n    /// ```\n    /// use boa_engine::{JsValue, js_string};\n    ///\n    /// assert_eq!(JsValue::new(42).js_type_of(), js_string!(\"number\"));\n    /// assert_eq!(JsValue::new(true).js_type_of(), js_string!(\"boolean\"));\n    /// assert_eq!(JsValue::undefined().js_type_of(), js_string!(\"undefined\"));\n    /// ```\n    #[must_use]\n    pub fn js_type_of(&self) -> JsString {\n        self.variant().js_type_of()\n    }\n\n    /// Maps a `JsValue` into `Option<T>` where T is the result of an\n    /// operation on a defined value. If the value is `JsValue::undefined`,\n    /// then `JsValue::map` will return None.\n    ///\n    /// # Example\n    ///\n    /// ```\n    /// use boa_engine::{Context, JsValue};\n    ///\n    /// let mut context = Context::default();\n    ///\n    /// let defined_value = JsValue::from(5);\n    /// let undefined = JsValue::undefined();\n    ///\n    /// let defined_result = defined_value\n    ///     .map(|v| v.add(&JsValue::from(5), &mut context))\n    ///     .transpose()\n    ///     .unwrap();\n    /// let undefined_result = undefined\n    ///     .map(|v| v.add(&JsValue::from(5), &mut context))\n    ///     .transpose()\n    ///     .unwrap();\n    ///\n    /// assert_eq!(defined_result, Some(JsValue::from(10u8)));\n    /// assert_eq!(undefined_result, None);\n    /// ```\n    #[inline]\n    #[must_use]\n    pub fn map<T, F>(&self, f: F) -> Option<T>\n    where\n        F: FnOnce(&JsValue) -> T,\n    {\n        if self.is_undefined() {\n            return None;\n        }\n        Some(f(self))\n    }\n\n    /// Maps a `JsValue` into `T` where T is the result of an\n    /// operation on a defined value. If the value is `JsValue::undefined`,\n    /// then `JsValue::map` will return the provided default value.\n    ///\n    /// # Example\n    ///\n    /// ```\n    /// use boa_engine::{Context, JsValue};\n    ///\n    /// let mut context = Context::default();\n    ///\n    /// let defined_value = JsValue::from(5);\n    /// let undefined = JsValue::undefined();\n    ///\n    /// let defined_result = defined_value\n    ///     .map_or(Ok(JsValue::new(true)), |v| {\n    ///         v.add(&JsValue::from(5), &mut context)\n    ///     })\n    ///     .unwrap();\n    /// let undefined_result = undefined\n    ///     .map_or(Ok(JsValue::new(true)), |v| {\n    ///         v.add(&JsValue::from(5), &mut context)\n    ///     })\n    ///     .unwrap();\n    ///\n    /// assert_eq!(defined_result, JsValue::new(10));\n    /// assert_eq!(undefined_result, JsValue::new(true));\n    /// ```\n    #[inline]\n    #[must_use]\n    pub fn map_or<T, F>(&self, default: T, f: F) -> T\n    where\n        F: FnOnce(&JsValue) -> T,\n    {\n        if self.is_undefined() {\n            return default;\n        }\n        f(self)\n    }\n\n    /// Abstract operation `IsArray ( argument )`\n    ///\n    /// Check if a value is an array.\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-isarray\n    pub(crate) fn is_array(&self) -> JsResult<bool> {\n        // Note: The spec specifies this function for JsValue.\n        // The main part of the function is implemented for JsObject.\n\n        // 1. If Type(argument) is not Object, return false.\n        self.as_object()\n            .as_ref()\n            .map_or(Ok(false), JsObject::is_array_abstract)\n    }\n}\n\nimpl Default for JsValue {\n    fn default() -> Self {\n        Self::undefined()\n    }\n}\n\n/// The preferred type to convert an object to a primitive `Value`.\n#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]\npub enum PreferredType {\n    /// Prefer to convert to a `String` primitive.\n    String,\n\n    /// Prefer to convert to a `Number` primitive.\n    Number,\n\n    /// Do not prefer a type to convert to.\n    Default,\n}\n\n/// Numeric value which can be of two types `Number`, `BigInt`.\n#[derive(Debug, Clone, PartialEq, PartialOrd)]\npub enum Numeric {\n    /// Double precision floating point number.\n    Number(f64),\n    /// `BigInt` an integer of arbitrary size.\n    BigInt(JsBigInt),\n}\n\nimpl From<f64> for Numeric {\n    #[inline]\n    fn from(value: f64) -> Self {\n        Self::Number(value)\n    }\n}\n\nimpl From<f32> for Numeric {\n    #[inline]\n    fn from(value: f32) -> Self {\n        Self::Number(value.into())\n    }\n}\n\nimpl From<i64> for Numeric {\n    #[inline]\n    fn from(value: i64) -> Self {\n        Self::BigInt(value.into())\n    }\n}\n\nimpl From<i32> for Numeric {\n    #[inline]\n    fn from(value: i32) -> Self {\n        Self::Number(value.into())\n    }\n}\n\nimpl From<i16> for Numeric {\n    #[inline]\n    fn from(value: i16) -> Self {\n        Self::Number(value.into())\n    }\n}\n\nimpl From<i8> for Numeric {\n    #[inline]\n    fn from(value: i8) -> Self {\n        Self::Number(value.into())\n    }\n}\n\nimpl From<u64> for Numeric {\n    #[inline]\n    fn from(value: u64) -> Self {\n        Self::BigInt(value.into())\n    }\n}\n\nimpl From<u32> for Numeric {\n    #[inline]\n    fn from(value: u32) -> Self {\n        Self::Number(value.into())\n    }\n}\n\nimpl From<u16> for Numeric {\n    #[inline]\n    fn from(value: u16) -> Self {\n        Self::Number(value.into())\n    }\n}\n\nimpl From<u8> for Numeric {\n    #[inline]\n    fn from(value: u8) -> Self {\n        Self::Number(value.into())\n    }\n}\n\nimpl From<JsBigInt> for Numeric {\n    #[inline]\n    fn from(value: JsBigInt) -> Self {\n        Self::BigInt(value)\n    }\n}\n\nimpl From<Numeric> for JsValue {\n    fn from(value: Numeric) -> Self {\n        match value {\n            Numeric::Number(number) => Self::new(number),\n            Numeric::BigInt(bigint) => Self::new(bigint),\n        }\n    }\n}\n"
  },
  {
    "path": "core/engine/src/value/operations.rs",
    "content": "use crate::{\n    Context, JsBigInt, JsResult, JsValue, JsVariant,\n    builtins::{\n        Number,\n        number::{f64_to_int32, f64_to_uint32},\n    },\n    error::JsNativeError,\n    js_string,\n    value::{JsSymbol, Numeric, PreferredType},\n};\n\nimpl JsValue {\n    /// Perform the binary `+` operator on the value and return the result.\n    pub fn add(&self, other: &Self, context: &mut Context) -> JsResult<Self> {\n        Ok(match (self.variant(), other.variant()) {\n            // Fast path:\n            // Numeric add\n            (JsVariant::Integer32(x), JsVariant::Integer32(y)) => x\n                .checked_add(y)\n                .map_or_else(|| Self::new(f64::from(x) + f64::from(y)), Self::new),\n            (JsVariant::Float64(x), JsVariant::Float64(y)) => Self::new(x + y),\n            (JsVariant::Integer32(x), JsVariant::Float64(y)) => Self::new(f64::from(x) + y),\n            (JsVariant::Float64(x), JsVariant::Integer32(y)) => Self::new(x + f64::from(y)),\n            (JsVariant::BigInt(x), JsVariant::BigInt(y)) => Self::new(JsBigInt::add(&x, &y)),\n\n            // String concat\n            (JsVariant::String(x), JsVariant::String(y)) => Self::from(js_string!(&x, &y)),\n\n            // Slow path:\n            (_, _) => {\n                let x = self.to_primitive(context, PreferredType::Default)?;\n                let y = other.to_primitive(context, PreferredType::Default)?;\n                match (x.variant(), y.variant()) {\n                    (JsVariant::String(x), _) => Self::from(js_string!(&x, &y.to_string(context)?)),\n                    (_, JsVariant::String(y)) => Self::from(js_string!(&x.to_string(context)?, &y)),\n                    (_, _) => {\n                        match (x.to_numeric(context)?, y.to_numeric(context)?) {\n                            (Numeric::Number(x), Numeric::Number(y)) => Self::new(x + y),\n                            (Numeric::BigInt(ref x), Numeric::BigInt(ref y)) => {\n                                Self::new(JsBigInt::add(x, y))\n                            }\n                            (_, _) => return Err(JsNativeError::typ()\n                                .with_message(\n                                    \"cannot mix BigInt and other types, use explicit conversions\",\n                                )\n                                .into()),\n                        }\n                    }\n                }\n            }\n        })\n    }\n\n    /// Perform the binary `-` operator on the value and return the result.\n    pub fn sub(&self, other: &Self, context: &mut Context) -> JsResult<Self> {\n        Ok(match (self.variant(), other.variant()) {\n            // Fast path:\n            (JsVariant::Integer32(x), JsVariant::Integer32(y)) => x\n                .checked_sub(y)\n                .map_or_else(|| Self::new(f64::from(x) - f64::from(y)), Self::new),\n            (JsVariant::Float64(x), JsVariant::Float64(y)) => Self::new(x - y),\n            (JsVariant::Integer32(x), JsVariant::Float64(y)) => Self::new(f64::from(x) - y),\n            (JsVariant::Float64(x), JsVariant::Integer32(y)) => Self::new(x - f64::from(y)),\n\n            (JsVariant::BigInt(x), JsVariant::BigInt(y)) => Self::new(JsBigInt::sub(&x, &y)),\n\n            // Slow path:\n            (_, _) => match (self.to_numeric(context)?, other.to_numeric(context)?) {\n                (Numeric::Number(a), Numeric::Number(b)) => Self::new(a - b),\n                (Numeric::BigInt(ref x), Numeric::BigInt(ref y)) => Self::new(JsBigInt::sub(x, y)),\n                (_, _) => {\n                    return Err(JsNativeError::typ()\n                        .with_message(\"cannot mix BigInt and other types, use explicit conversions\")\n                        .into());\n                }\n            },\n        })\n    }\n\n    /// Perform the binary `*` operator on the value and return the result.\n    pub fn mul(&self, other: &Self, context: &mut Context) -> JsResult<Self> {\n        Ok(match (self.variant(), other.variant()) {\n            // Fast path:\n            (JsVariant::Integer32(x), JsVariant::Integer32(y)) => x\n                .checked_mul(y)\n                // Check for the special case of `0 * -N` which must produce `-0.0`\n                .filter(|v| *v != 0 || i32::min(x, y) >= 0)\n                .map_or_else(|| Self::new(f64::from(x) * f64::from(y)), Self::new),\n            (JsVariant::Float64(x), JsVariant::Float64(y)) => Self::new(x * y),\n            (JsVariant::Integer32(x), JsVariant::Float64(y)) => Self::new(f64::from(x) * y),\n            (JsVariant::Float64(x), JsVariant::Integer32(y)) => Self::new(x * f64::from(y)),\n\n            (JsVariant::BigInt(x), JsVariant::BigInt(y)) => Self::new(JsBigInt::mul(&x, &y)),\n\n            // Slow path:\n            (_, _) => match (self.to_numeric(context)?, other.to_numeric(context)?) {\n                (Numeric::Number(a), Numeric::Number(b)) => Self::new(a * b),\n                (Numeric::BigInt(ref x), Numeric::BigInt(ref y)) => Self::new(JsBigInt::mul(x, y)),\n                (_, _) => {\n                    return Err(JsNativeError::typ()\n                        .with_message(\"cannot mix BigInt and other types, use explicit conversions\")\n                        .into());\n                }\n            },\n        })\n    }\n\n    /// Perform the binary `/` operator on the value and return the result.\n    pub fn div(&self, other: &Self, context: &mut Context) -> JsResult<Self> {\n        Ok(match (self.variant(), other.variant()) {\n            // Fast path:\n            (JsVariant::Integer32(x), JsVariant::Integer32(y)) => x\n                .checked_div(y)\n                .filter(|div| y * div == x)\n                .map_or_else(|| Self::new(f64::from(x) / f64::from(y)), Self::new),\n            (JsVariant::Float64(x), JsVariant::Float64(y)) => Self::new(x / y),\n            (JsVariant::Integer32(x), JsVariant::Float64(y)) => Self::new(f64::from(x) / y),\n            (JsVariant::Float64(x), JsVariant::Integer32(y)) => Self::new(x / f64::from(y)),\n\n            (JsVariant::BigInt(x), JsVariant::BigInt(y)) => {\n                if y.is_zero() {\n                    return Err(JsNativeError::range()\n                        .with_message(\"BigInt division by zero\")\n                        .into());\n                }\n                Self::new(JsBigInt::div(&x, &y))\n            }\n\n            // Slow path:\n            (_, _) => match (self.to_numeric(context)?, other.to_numeric(context)?) {\n                (Numeric::Number(a), Numeric::Number(b)) => Self::new(a / b),\n                (Numeric::BigInt(ref x), Numeric::BigInt(ref y)) => {\n                    if y.is_zero() {\n                        return Err(JsNativeError::range()\n                            .with_message(\"BigInt division by zero\")\n                            .into());\n                    }\n                    Self::new(JsBigInt::div(x, y))\n                }\n                (_, _) => {\n                    return Err(JsNativeError::typ()\n                        .with_message(\"cannot mix BigInt and other types, use explicit conversions\")\n                        .into());\n                }\n            },\n        })\n    }\n\n    /// Perform the binary `%` operator on the value and return the result.\n    pub fn rem(&self, other: &Self, context: &mut Context) -> JsResult<Self> {\n        Ok(match (self.variant(), other.variant()) {\n            // Fast path:\n            (JsVariant::Integer32(x), JsVariant::Integer32(y)) => {\n                if y == 0 {\n                    Self::nan()\n                } else {\n                    match x % y {\n                        rem if rem == 0 && x < 0 => Self::new(-0.0),\n                        rem => Self::new(rem),\n                    }\n                }\n            }\n            (JsVariant::Float64(x), JsVariant::Float64(y)) => Self::new((x % y).copysign(x)),\n            (JsVariant::Integer32(x), JsVariant::Float64(y)) => {\n                let x = f64::from(x);\n                Self::new((x % y).copysign(x))\n            }\n\n            (JsVariant::Float64(x), JsVariant::Integer32(y)) => {\n                Self::new((x % f64::from(y)).copysign(x))\n            }\n\n            (JsVariant::BigInt(x), JsVariant::BigInt(y)) => {\n                if y.is_zero() {\n                    return Err(JsNativeError::range()\n                        .with_message(\"BigInt division by zero\")\n                        .into());\n                }\n                Self::new(JsBigInt::rem(&x, &y))\n            }\n\n            // Slow path:\n            (_, _) => match (self.to_numeric(context)?, other.to_numeric(context)?) {\n                (Numeric::Number(a), Numeric::Number(b)) => Self::new(a % b),\n                (Numeric::BigInt(ref x), Numeric::BigInt(ref y)) => {\n                    if y.is_zero() {\n                        return Err(JsNativeError::range()\n                            .with_message(\"BigInt division by zero\")\n                            .into());\n                    }\n                    Self::new(JsBigInt::rem(x, y))\n                }\n                (_, _) => {\n                    return Err(JsNativeError::typ()\n                        .with_message(\"cannot mix BigInt and other types, use explicit conversions\")\n                        .into());\n                }\n            },\n        })\n    }\n\n    /// Perform the binary `**` operator on the value and return the result.\n    // NOTE: There are some cases in the spec where we have to compare floats\n    #[allow(clippy::float_cmp)]\n    pub fn pow(&self, other: &Self, context: &mut Context) -> JsResult<Self> {\n        Ok(match (self.variant(), other.variant()) {\n            // Fast path:\n            (JsVariant::Integer32(x), JsVariant::Integer32(y)) => u32::try_from(y)\n                .ok()\n                .and_then(|y| x.checked_pow(y))\n                .map_or_else(|| Self::new(f64::from(x).powi(y)), Self::new),\n            (JsVariant::Float64(x), JsVariant::Float64(y)) => {\n                if x.abs() == 1.0 && y.is_infinite() {\n                    Self::nan()\n                } else {\n                    Self::new(x.powf(y))\n                }\n            }\n            (JsVariant::Integer32(x), JsVariant::Float64(y)) => {\n                if x.wrapping_abs() == 1 && y.is_infinite() {\n                    Self::nan()\n                } else {\n                    Self::new(f64::from(x).powf(y))\n                }\n            }\n            (JsVariant::Float64(x), JsVariant::Integer32(y)) => Self::new(x.powi(y)),\n            (JsVariant::BigInt(a), JsVariant::BigInt(b)) => Self::new(JsBigInt::pow(&a, &b)?),\n\n            // Slow path:\n            (_, _) => match (self.to_numeric(context)?, other.to_numeric(context)?) {\n                (Numeric::Number(a), Numeric::Number(b)) => {\n                    if a.abs() == 1.0 && b.is_infinite() {\n                        Self::nan()\n                    } else {\n                        Self::new(a.powf(b))\n                    }\n                }\n                (Numeric::BigInt(ref a), Numeric::BigInt(ref b)) => Self::new(JsBigInt::pow(a, b)?),\n                (_, _) => {\n                    return Err(JsNativeError::typ()\n                        .with_message(\"cannot mix BigInt and other types, use explicit conversions\")\n                        .into());\n                }\n            },\n        })\n    }\n\n    /// Perform the binary `&` operator on the value and return the result.\n    pub fn bitand(&self, other: &Self, context: &mut Context) -> JsResult<Self> {\n        Ok(match (self.variant(), other.variant()) {\n            // Fast path:\n            (JsVariant::Integer32(x), JsVariant::Integer32(y)) => Self::new(x & y),\n            (JsVariant::Float64(x), JsVariant::Float64(y)) => {\n                Self::new(f64_to_int32(x) & f64_to_int32(y))\n            }\n            (JsVariant::Integer32(x), JsVariant::Float64(y)) => Self::new(x & f64_to_int32(y)),\n            (JsVariant::Float64(x), JsVariant::Integer32(y)) => Self::new(f64_to_int32(x) & y),\n\n            (JsVariant::BigInt(x), JsVariant::BigInt(y)) => Self::new(JsBigInt::bitand(&x, &y)),\n\n            // Slow path:\n            (_, _) => match (self.to_numeric(context)?, other.to_numeric(context)?) {\n                (Numeric::Number(a), Numeric::Number(b)) => {\n                    Self::new(f64_to_int32(a) & f64_to_int32(b))\n                }\n                (Numeric::BigInt(ref x), Numeric::BigInt(ref y)) => {\n                    Self::new(JsBigInt::bitand(x, y))\n                }\n                (_, _) => {\n                    return Err(JsNativeError::typ()\n                        .with_message(\"cannot mix BigInt and other types, use explicit conversions\")\n                        .into());\n                }\n            },\n        })\n    }\n\n    /// Perform the binary `|` operator on the value and return the result.\n    pub fn bitor(&self, other: &Self, context: &mut Context) -> JsResult<Self> {\n        Ok(match (self.variant(), other.variant()) {\n            // Fast path:\n            (JsVariant::Integer32(x), JsVariant::Integer32(y)) => Self::new(x | y),\n            (JsVariant::Float64(x), JsVariant::Float64(y)) => {\n                Self::new(f64_to_int32(x) | f64_to_int32(y))\n            }\n            (JsVariant::Integer32(x), JsVariant::Float64(y)) => Self::new(x | f64_to_int32(y)),\n            (JsVariant::Float64(x), JsVariant::Integer32(y)) => Self::new(f64_to_int32(x) | y),\n\n            (JsVariant::BigInt(x), JsVariant::BigInt(y)) => Self::new(JsBigInt::bitor(&x, &y)),\n\n            // Slow path:\n            (_, _) => match (self.to_numeric(context)?, other.to_numeric(context)?) {\n                (Numeric::Number(a), Numeric::Number(b)) => {\n                    Self::new(f64_to_int32(a) | f64_to_int32(b))\n                }\n                (Numeric::BigInt(ref x), Numeric::BigInt(ref y)) => {\n                    Self::new(JsBigInt::bitor(x, y))\n                }\n                (_, _) => {\n                    return Err(JsNativeError::typ()\n                        .with_message(\"cannot mix BigInt and other types, use explicit conversions\")\n                        .into());\n                }\n            },\n        })\n    }\n\n    /// Perform the binary `^` operator on the value and return the result.\n    pub fn bitxor(&self, other: &Self, context: &mut Context) -> JsResult<Self> {\n        Ok(match (self.variant(), other.variant()) {\n            // Fast path:\n            (JsVariant::Integer32(x), JsVariant::Integer32(y)) => Self::new(x ^ y),\n            (JsVariant::Float64(x), JsVariant::Float64(y)) => {\n                Self::new(f64_to_int32(x) ^ f64_to_int32(y))\n            }\n            (JsVariant::Integer32(x), JsVariant::Float64(y)) => Self::new(x ^ f64_to_int32(y)),\n            (JsVariant::Float64(x), JsVariant::Integer32(y)) => Self::new(f64_to_int32(x) ^ y),\n\n            (JsVariant::BigInt(x), JsVariant::BigInt(y)) => Self::new(JsBigInt::bitxor(&x, &y)),\n\n            // Slow path:\n            (_, _) => match (self.to_numeric(context)?, other.to_numeric(context)?) {\n                (Numeric::Number(a), Numeric::Number(b)) => {\n                    Self::new(f64_to_int32(a) ^ f64_to_int32(b))\n                }\n                (Numeric::BigInt(ref x), Numeric::BigInt(ref y)) => {\n                    Self::new(JsBigInt::bitxor(x, y))\n                }\n                (_, _) => {\n                    return Err(JsNativeError::typ()\n                        .with_message(\"cannot mix BigInt and other types, use explicit conversions\")\n                        .into());\n                }\n            },\n        })\n    }\n\n    /// Perform the binary `<<` operator on the value and return the result.\n    pub fn shl(&self, other: &Self, context: &mut Context) -> JsResult<Self> {\n        Ok(match (self.variant(), other.variant()) {\n            // Fast path:\n            (JsVariant::Integer32(x), JsVariant::Integer32(y)) => {\n                Self::new(x.wrapping_shl(y as u32))\n            }\n            (JsVariant::Float64(x), JsVariant::Float64(y)) => {\n                Self::new(f64_to_int32(x).wrapping_shl(f64_to_uint32(y)))\n            }\n            (JsVariant::Integer32(x), JsVariant::Float64(y)) => {\n                Self::new(x.wrapping_shl(f64_to_uint32(y)))\n            }\n            (JsVariant::Float64(x), JsVariant::Integer32(y)) => {\n                Self::new(f64_to_int32(x).wrapping_shl(y as u32))\n            }\n\n            (JsVariant::BigInt(a), JsVariant::BigInt(b)) => {\n                Self::new(JsBigInt::shift_left(&a, &b)?)\n            }\n\n            // Slow path:\n            (_, _) => match (self.to_numeric(context)?, other.to_numeric(context)?) {\n                (Numeric::Number(x), Numeric::Number(y)) => {\n                    Self::new(f64_to_int32(x).wrapping_shl(f64_to_uint32(y)))\n                }\n                (Numeric::BigInt(ref x), Numeric::BigInt(ref y)) => {\n                    Self::new(JsBigInt::shift_left(x, y)?)\n                }\n                (_, _) => {\n                    return Err(JsNativeError::typ()\n                        .with_message(\"cannot mix BigInt and other types, use explicit conversions\")\n                        .into());\n                }\n            },\n        })\n    }\n\n    /// Perform the binary `>>` operator on the value and return the result.\n    pub fn shr(&self, other: &Self, context: &mut Context) -> JsResult<Self> {\n        Ok(match (self.variant(), other.variant()) {\n            // Fast path:\n            (JsVariant::Integer32(x), JsVariant::Integer32(y)) => {\n                Self::new(x.wrapping_shr(y as u32))\n            }\n            (JsVariant::Float64(x), JsVariant::Float64(y)) => {\n                Self::new(f64_to_int32(x).wrapping_shr(f64_to_uint32(y)))\n            }\n            (JsVariant::Integer32(x), JsVariant::Float64(y)) => {\n                Self::new(x.wrapping_shr(f64_to_uint32(y)))\n            }\n            (JsVariant::Float64(x), JsVariant::Integer32(y)) => {\n                Self::new(f64_to_int32(x).wrapping_shr(y as u32))\n            }\n\n            (JsVariant::BigInt(a), JsVariant::BigInt(b)) => {\n                Self::new(JsBigInt::shift_right(&a, &b)?)\n            }\n\n            // Slow path:\n            (_, _) => match (self.to_numeric(context)?, other.to_numeric(context)?) {\n                (Numeric::Number(x), Numeric::Number(y)) => {\n                    Self::new(f64_to_int32(x).wrapping_shr(f64_to_uint32(y)))\n                }\n                (Numeric::BigInt(ref x), Numeric::BigInt(ref y)) => {\n                    Self::new(JsBigInt::shift_right(x, y)?)\n                }\n                (_, _) => {\n                    return Err(JsNativeError::typ()\n                        .with_message(\"cannot mix BigInt and other types, use explicit conversions\")\n                        .into());\n                }\n            },\n        })\n    }\n\n    /// Perform the binary `>>>` operator on the value and return the result.\n    pub fn ushr(&self, other: &Self, context: &mut Context) -> JsResult<Self> {\n        Ok(match (self.variant(), other.variant()) {\n            // Fast path:\n            (JsVariant::Integer32(x), JsVariant::Integer32(y)) => {\n                Self::new((x as u32).wrapping_shr(y as u32))\n            }\n            (JsVariant::Float64(x), JsVariant::Float64(y)) => {\n                Self::new(f64_to_uint32(x).wrapping_shr(f64_to_uint32(y)))\n            }\n            (JsVariant::Integer32(x), JsVariant::Float64(y)) => {\n                Self::new((x as u32).wrapping_shr(f64_to_uint32(y)))\n            }\n            (JsVariant::Float64(x), JsVariant::Integer32(y)) => {\n                Self::new(f64_to_uint32(x).wrapping_shr(y as u32))\n            }\n\n            // Slow path:\n            (_, _) => match (self.to_numeric(context)?, other.to_numeric(context)?) {\n                (Numeric::Number(x), Numeric::Number(y)) => {\n                    Self::new(f64_to_uint32(x).wrapping_shr(f64_to_uint32(y)))\n                }\n                (Numeric::BigInt(_), Numeric::BigInt(_)) => {\n                    return Err(JsNativeError::typ()\n                        .with_message(\"BigInts have no unsigned right shift, use >> instead\")\n                        .into());\n                }\n                (_, _) => {\n                    return Err(JsNativeError::typ()\n                        .with_message(\"cannot mix BigInt and other types, use explicit conversions\")\n                        .into());\n                }\n            },\n        })\n    }\n\n    /// Abstract operation `InstanceofOperator ( V, target )`\n    ///\n    /// More information:\n    /// - [ECMAScript reference][spec]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-instanceofoperator\n    pub fn instance_of(&self, target: &Self, context: &mut Context) -> JsResult<bool> {\n        // 1. If Type(target) is not Object, throw a TypeError exception.\n        if !target.is_object() {\n            return Err(JsNativeError::typ()\n                .with_message(format!(\n                    \"right-hand side of 'instanceof' should be an object, got `{}`\",\n                    target.type_of()\n                ))\n                .into());\n        }\n\n        // 2. Let instOfHandler be ? GetMethod(target, @@hasInstance).\n        match target.get_method(JsSymbol::has_instance(), context)? {\n            // 3. If instOfHandler is not undefined, then\n            Some(instance_of_handler) => {\n                // a. Return ! ToBoolean(? Call(instOfHandler, target, « V »)).\n                Ok(instance_of_handler\n                    .call(target, std::slice::from_ref(self), context)?\n                    .to_boolean())\n            }\n            None if target.is_callable() => {\n                // 5. Return ? OrdinaryHasInstance(target, V).\n                Self::ordinary_has_instance(target, self, context)\n            }\n            None => {\n                // 4. If IsCallable(target) is false, throw a TypeError exception.\n                Err(JsNativeError::typ()\n                    .with_message(\"right-hand side of 'instanceof' is not callable\")\n                    .into())\n            }\n        }\n    }\n\n    /// Returns the negated value.\n    pub fn neg(&self, context: &mut Context) -> JsResult<Self> {\n        Ok(match self.variant() {\n            JsVariant::Symbol(_) | JsVariant::Undefined => Self::new(f64::NAN),\n            JsVariant::Object(_) => Self::new(\n                self.to_numeric_number(context)\n                    .map_or(f64::NAN, std::ops::Neg::neg),\n            ),\n            JsVariant::String(str) => Self::new(-str.to_number()),\n            JsVariant::Float64(num) => Self::new(-num),\n            JsVariant::Integer32(0) | JsVariant::Boolean(false) | JsVariant::Null => {\n                Self::new(-0.0)\n            }\n            JsVariant::Integer32(num) => Self::new(-num),\n            JsVariant::Boolean(true) => Self::new(-1),\n            JsVariant::BigInt(x) => Self::new(JsBigInt::neg(&x)),\n        })\n    }\n\n    /// Returns the negated boolean value.\n    #[inline]\n    pub fn not(&self) -> JsResult<bool> {\n        Ok(!self.to_boolean())\n    }\n\n    /// Abstract relational comparison\n    ///\n    /// The comparison `x < y`, where `x` and `y` are values, produces `true`, `false`,\n    /// or `undefined` (which indicates that at least one operand is `NaN`).\n    ///\n    /// In addition to `x` and `y` the algorithm takes a Boolean flag named `LeftFirst` as a parameter.\n    /// The flag is used to control the order in which operations with potentially visible side-effects\n    /// are performed upon `x` and `y`. It is necessary because ECMAScript specifies left to right evaluation\n    /// of expressions. The default value of `LeftFirst` is `true` and indicates that the `x` parameter\n    /// corresponds to an expression that occurs to the left of the `y` parameter's corresponding expression.\n    ///\n    /// If `LeftFirst` is `false`, the reverse is the case and operations must be performed upon `y` before `x`.\n    ///\n    /// More Information:\n    ///  - [ECMAScript reference][spec]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-abstract-relational-comparison\n    pub fn abstract_relation(\n        &self,\n        other: &Self,\n        left_first: bool,\n        context: &mut Context,\n    ) -> JsResult<AbstractRelation> {\n        Ok(match (self.variant(), other.variant()) {\n            // Fast path (for some common operations):\n            (JsVariant::Integer32(x), JsVariant::Integer32(y)) => (x < y).into(),\n            (JsVariant::Integer32(x), JsVariant::Float64(y)) => Number::less_than(f64::from(x), y),\n            (JsVariant::Float64(x), JsVariant::Integer32(y)) => Number::less_than(x, f64::from(y)),\n            (JsVariant::Float64(x), JsVariant::Float64(y)) => Number::less_than(x, y),\n            (JsVariant::BigInt(x), JsVariant::BigInt(y)) => (x < y).into(),\n\n            // Slow path:\n            (_, _) => {\n                let (px, py) = if left_first {\n                    let px = self.to_primitive(context, PreferredType::Number)?;\n                    let py = other.to_primitive(context, PreferredType::Number)?;\n                    (px, py)\n                } else {\n                    // NOTE: The order of evaluation needs to be reversed to preserve left to right evaluation.\n                    let py = other.to_primitive(context, PreferredType::Number)?;\n                    let px = self.to_primitive(context, PreferredType::Number)?;\n                    (px, py)\n                };\n\n                match (px.variant(), py.variant()) {\n                    (JsVariant::String(x), JsVariant::String(y)) => (x < y).into(),\n                    (JsVariant::BigInt(x), JsVariant::String(y)) => JsBigInt::from_js_string(&y)\n                        .map_or(AbstractRelation::Undefined, |y| (x < y).into()),\n                    (JsVariant::String(x), JsVariant::BigInt(y)) => JsBigInt::from_js_string(&x)\n                        .map_or(AbstractRelation::Undefined, |x| (x < y).into()),\n                    (_, _) => match (px.to_numeric(context)?, py.to_numeric(context)?) {\n                        (Numeric::Number(x), Numeric::Number(y)) => Number::less_than(x, y),\n                        (Numeric::BigInt(ref x), Numeric::BigInt(ref y)) => (x < y).into(),\n                        (Numeric::BigInt(ref x), Numeric::Number(y)) => {\n                            if y.is_nan() {\n                                return Ok(AbstractRelation::Undefined);\n                            }\n                            if y.is_infinite() {\n                                return Ok(y.is_sign_positive().into());\n                            }\n\n                            if let Ok(y) = JsBigInt::try_from(y) {\n                                return Ok((x < &y).into());\n                            }\n\n                            (x.to_f64() < y).into()\n                        }\n                        (Numeric::Number(x), Numeric::BigInt(ref y)) => {\n                            if x.is_nan() {\n                                return Ok(AbstractRelation::Undefined);\n                            }\n                            if x.is_infinite() {\n                                return Ok(x.is_sign_negative().into());\n                            }\n\n                            if let Ok(x) = JsBigInt::try_from(x) {\n                                return Ok((&x < y).into());\n                            }\n\n                            (x < y.to_f64()).into()\n                        }\n                    },\n                }\n            }\n        })\n    }\n\n    /// The less than operator (`<`) returns `true` if the left operand is less than the right operand,\n    /// and `false` otherwise.\n    ///\n    /// More Information:\n    ///  - [MDN documentation][mdn]\n    ///  - [ECMAScript reference][spec]\n    ///\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Less_than\n    /// [spec]: https://tc39.es/ecma262/#sec-relational-operators-runtime-semantics-evaluation\n    #[inline]\n    pub fn lt(&self, other: &Self, context: &mut Context) -> JsResult<bool> {\n        match self.abstract_relation(other, true, context)? {\n            AbstractRelation::True => Ok(true),\n            AbstractRelation::False | AbstractRelation::Undefined => Ok(false),\n        }\n    }\n\n    /// The less than or equal operator (`<=`) returns `true` if the left operand is less than\n    /// or equal to the right operand, and `false` otherwise.\n    ///\n    /// More Information:\n    ///  - [MDN documentation][mdn]\n    ///  - [ECMAScript reference][spec]\n    ///\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Less_than_or_equal\n    /// [spec]: https://tc39.es/ecma262/#sec-relational-operators-runtime-semantics-evaluation\n    #[inline]\n    pub fn le(&self, other: &Self, context: &mut Context) -> JsResult<bool> {\n        match other.abstract_relation(self, false, context)? {\n            AbstractRelation::False => Ok(true),\n            AbstractRelation::True | AbstractRelation::Undefined => Ok(false),\n        }\n    }\n\n    /// The greater than operator (`>`) returns `true` if the left operand is greater than\n    /// the right operand, and `false` otherwise.\n    ///\n    /// More Information:\n    ///  - [MDN documentation][mdn]\n    ///  - [ECMAScript reference][spec]\n    ///\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Greater_than\n    /// [spec]: https://tc39.es/ecma262/#sec-relational-operators-runtime-semantics-evaluation\n    #[inline]\n    pub fn gt(&self, other: &Self, context: &mut Context) -> JsResult<bool> {\n        match other.abstract_relation(self, false, context)? {\n            AbstractRelation::True => Ok(true),\n            AbstractRelation::False | AbstractRelation::Undefined => Ok(false),\n        }\n    }\n\n    /// The greater than or equal operator (`>=`) returns `true` if the left operand is greater than\n    /// or equal to the right operand, and `false` otherwise.\n    ///\n    /// More Information:\n    ///  - [MDN documentation][mdn]\n    ///  - [ECMAScript reference][spec]\n    ///\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Greater_than_or_equal\n    /// [spec]: https://tc39.es/ecma262/#sec-relational-operators-runtime-semantics-evaluation\n    #[inline]\n    pub fn ge(&self, other: &Self, context: &mut Context) -> JsResult<bool> {\n        match self.abstract_relation(other, true, context)? {\n            AbstractRelation::False => Ok(true),\n            AbstractRelation::True | AbstractRelation::Undefined => Ok(false),\n        }\n    }\n\n    // ==== Numeric Fast Paths ====\n    //\n    // These methods provide optimized paths for binary operations at the opcode\n    // handler level. They use `self.0.as_integer32()` and `self.as_number_cheap()`\n    // (pure bit operations) instead of `variant()`, which avoids cloning pointer\n    // types (Object, String, Symbol, BigInt) for non-numeric values.\n    //\n    // Returns `Some(result)` if both operands are numeric, `None` otherwise\n    // (caller should fall back to the full method with type coercion).\n\n    /// Converts the value to a number if possible, using fast bit operations. This is\n    /// used for the fast operations to allow mixed i32/f64 values.\n    #[inline]\n    fn as_number_cheap(&self) -> Option<f64> {\n        if let Some(i) = self.0.as_integer32() {\n            Some(f64::from(i))\n        } else {\n            self.0.as_float64()\n        }\n    }\n\n    /// Fast path for the binary `+` operator (numeric only).\n    #[inline]\n    #[allow(clippy::float_cmp)]\n    pub(crate) fn add_fast(&self, other: &Self) -> Option<Self> {\n        if let (Some(x), Some(y)) = (self.0.as_integer32(), other.0.as_integer32()) {\n            return Some(\n                x.checked_add(y)\n                    .map_or_else(|| Self::new(f64::from(x) + f64::from(y)), Self::new),\n            );\n        }\n        let x = self.as_number_cheap()?;\n        let y = other.as_number_cheap()?;\n        Some(Self::new(x + y))\n    }\n\n    /// Fast path for the binary `-` operator.\n    #[inline]\n    pub(crate) fn sub_fast(&self, other: &Self) -> Option<Self> {\n        if let (Some(x), Some(y)) = (self.0.as_integer32(), other.0.as_integer32()) {\n            return Some(\n                x.checked_sub(y)\n                    .map_or_else(|| Self::new(f64::from(x) - f64::from(y)), Self::new),\n            );\n        }\n        let x = self.as_number_cheap()?;\n        let y = other.as_number_cheap()?;\n        Some(Self::new(x - y))\n    }\n\n    /// Fast path for the binary `*` operator.\n    #[inline]\n    pub(crate) fn mul_fast(&self, other: &Self) -> Option<Self> {\n        if let (Some(x), Some(y)) = (self.0.as_integer32(), other.0.as_integer32()) {\n            return Some(\n                x.checked_mul(y)\n                    .filter(|v| *v != 0 || i32::min(x, y) >= 0)\n                    .map_or_else(|| Self::new(f64::from(x) * f64::from(y)), Self::new),\n            );\n        }\n        let x = self.as_number_cheap()?;\n        let y = other.as_number_cheap()?;\n        Some(Self::new(x * y))\n    }\n\n    /// Fast path for the binary `/` operator.\n    #[inline]\n    #[allow(clippy::float_cmp)]\n    pub(crate) fn div_fast(&self, other: &Self) -> Option<Self> {\n        if let (Some(x), Some(y)) = (self.0.as_integer32(), other.0.as_integer32()) {\n            return Some(\n                x.checked_div(y)\n                    .filter(|div| y * div == x)\n                    .map_or_else(|| Self::new(f64::from(x) / f64::from(y)), Self::new),\n            );\n        }\n        let x = self.as_number_cheap()?;\n        let y = other.as_number_cheap()?;\n        Some(Self::new(x / y))\n    }\n\n    /// Fast path for the binary `%` operator.\n    #[inline]\n    pub(crate) fn rem_fast(&self, other: &Self) -> Option<Self> {\n        if let (Some(x), Some(y)) = (self.0.as_integer32(), other.0.as_integer32()) {\n            if y == 0 {\n                return Some(Self::nan());\n            }\n            return Some(match x % y {\n                rem if rem == 0 && x < 0 => Self::new(-0.0),\n                rem => Self::new(rem),\n            });\n        }\n        let x = self.as_number_cheap()?;\n        let y = other.as_number_cheap()?;\n        Some(Self::new((x % y).copysign(x)))\n    }\n\n    /// Fast path for the binary `**` operator.\n    #[inline]\n    #[allow(clippy::float_cmp)]\n    pub(crate) fn pow_fast(&self, other: &Self) -> Option<Self> {\n        if let (Some(x), Some(y)) = (self.0.as_integer32(), other.0.as_integer32()) {\n            return Some(\n                u32::try_from(y)\n                    .ok()\n                    .and_then(|y| x.checked_pow(y))\n                    .map_or_else(|| Self::new(f64::from(x).powi(y)), Self::new),\n            );\n        }\n        let x = self.as_number_cheap()?;\n        let y = other.as_number_cheap()?;\n        if x.abs() == 1.0 && y.is_infinite() {\n            Some(Self::nan())\n        } else {\n            Some(Self::new(x.powf(y)))\n        }\n    }\n\n    /// Fast path for the binary `&` operator (i32 only).\n    #[inline]\n    pub(crate) fn bitand_fast(&self, other: &Self) -> Option<Self> {\n        let x = self.0.as_integer32()?;\n        let y = other.0.as_integer32()?;\n        Some(Self::new(x & y))\n    }\n\n    /// Fast path for the binary `|` operator (i32 only).\n    #[inline]\n    pub(crate) fn bitor_fast(&self, other: &Self) -> Option<Self> {\n        let x = self.0.as_integer32()?;\n        let y = other.0.as_integer32()?;\n        Some(Self::new(x | y))\n    }\n\n    /// Fast path for the binary `^` operator (i32 only).\n    #[inline]\n    pub(crate) fn bitxor_fast(&self, other: &Self) -> Option<Self> {\n        let x = self.0.as_integer32()?;\n        let y = other.0.as_integer32()?;\n        Some(Self::new(x ^ y))\n    }\n\n    /// Fast path for the binary `<<` operator (i32 only).\n    #[inline]\n    pub(crate) fn shl_fast(&self, other: &Self) -> Option<Self> {\n        let x = self.0.as_integer32()?;\n        let y = other.0.as_integer32()?;\n        Some(Self::new(x.wrapping_shl(y as u32)))\n    }\n\n    /// Fast path for the binary `>>` operator (i32 only).\n    #[inline]\n    pub(crate) fn shr_fast(&self, other: &Self) -> Option<Self> {\n        let x = self.0.as_integer32()?;\n        let y = other.0.as_integer32()?;\n        Some(Self::new(x.wrapping_shr(y as u32)))\n    }\n\n    /// Fast path for the binary `>>>` operator (i32 only).\n    #[inline]\n    pub(crate) fn ushr_fast(&self, other: &Self) -> Option<Self> {\n        let x = self.0.as_integer32()?;\n        let y = other.0.as_integer32()?;\n        Some(Self::new((x as u32).wrapping_shr(y as u32)))\n    }\n\n    /// Fast path for the `<` operator.\n    #[inline]\n    pub(crate) fn lt_fast(&self, other: &Self) -> Option<bool> {\n        if let (Some(x), Some(y)) = (self.0.as_integer32(), other.0.as_integer32()) {\n            return Some(x < y);\n        }\n        let x = self.as_number_cheap()?;\n        let y = other.as_number_cheap()?;\n        Some(x < y)\n    }\n\n    /// Fast path for the `<=` operator.\n    #[inline]\n    pub(crate) fn le_fast(&self, other: &Self) -> Option<bool> {\n        if let (Some(x), Some(y)) = (self.0.as_integer32(), other.0.as_integer32()) {\n            return Some(x <= y);\n        }\n        let x = self.as_number_cheap()?;\n        let y = other.as_number_cheap()?;\n        Some(x <= y)\n    }\n\n    /// Fast path for the `>` operator.\n    #[inline]\n    pub(crate) fn gt_fast(&self, other: &Self) -> Option<bool> {\n        if let (Some(x), Some(y)) = (self.0.as_integer32(), other.0.as_integer32()) {\n            return Some(x > y);\n        }\n        let x = self.as_number_cheap()?;\n        let y = other.as_number_cheap()?;\n        Some(x > y)\n    }\n\n    /// Fast path for the `>=` operator.\n    #[inline]\n    pub(crate) fn ge_fast(&self, other: &Self) -> Option<bool> {\n        if let (Some(x), Some(y)) = (self.0.as_integer32(), other.0.as_integer32()) {\n            return Some(x >= y);\n        }\n        let x = self.as_number_cheap()?;\n        let y = other.as_number_cheap()?;\n        Some(x >= y)\n    }\n\n    /// Fast path for the `==` operator (numeric only).\n    #[inline]\n    #[allow(clippy::float_cmp)]\n    pub(crate) fn equals_fast(&self, other: &Self) -> Option<Self> {\n        if let (Some(x), Some(y)) = (self.0.as_integer32(), other.0.as_integer32()) {\n            return Some(Self::new(x == y));\n        }\n        let x = self.as_number_cheap()?;\n        let y = other.as_number_cheap()?;\n        Some(Self::new(x == y))\n    }\n\n    /// Fast path for the `!=` operator (numeric only).\n    #[inline]\n    #[allow(clippy::float_cmp)]\n    pub(crate) fn not_equals_fast(&self, other: &Self) -> Option<Self> {\n        if let (Some(x), Some(y)) = (self.0.as_integer32(), other.0.as_integer32()) {\n            return Some(Self::new(x != y));\n        }\n        let x = self.as_number_cheap()?;\n        let y = other.as_number_cheap()?;\n        Some(Self::new(x != y))\n    }\n}\n\n/// The result of the [Abstract Relational Comparison][arc].\n///\n/// Comparison `x < y`, where `x` and `y` are values.\n/// It produces `true`, `false`, or `undefined`\n/// (which indicates that at least one operand is `NaN`).\n///\n/// [arc]: https://tc39.es/ecma262/#sec-abstract-relational-comparison\n#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]\npub enum AbstractRelation {\n    /// `x` is less than `y`\n    True,\n    /// `x` is **not** less than `y`\n    False,\n    /// Indicates that at least one operand is `NaN`\n    Undefined,\n}\n\nimpl From<bool> for AbstractRelation {\n    #[inline]\n    fn from(value: bool) -> Self {\n        if value { Self::True } else { Self::False }\n    }\n}\n"
  },
  {
    "path": "core/engine/src/value/tests.rs",
    "content": "use boa_macros::js_str;\nuse indoc::indoc;\n\nuse super::*;\nuse crate::object::internal_methods::InternalMethodPropertyContext;\nuse crate::{TestAction, js_string, run_test_actions};\n\nuse std::collections::hash_map::DefaultHasher;\nuse std::hash::{Hash, Hasher};\n\n#[test]\nfn string_to_value() {\n    let s = String::from(\"Hello\");\n    let v = JsValue::new(js_string!(s));\n    assert!(v.is_string());\n    assert!(!v.is_null());\n}\n\n#[test]\nfn undefined() {\n    let u = JsValue::undefined();\n    assert_eq!(u.get_type(), Type::Undefined);\n    assert_eq!(u.display().to_string(), \"undefined\");\n}\n\n#[test]\nfn get_set_field() {\n    run_test_actions([TestAction::assert_context(|ctx| {\n        let obj = &JsObject::with_object_proto(ctx.intrinsics());\n        // Create string and convert it to a Value\n        let s = JsValue::new(js_str!(\"bar\"));\n        obj.set(js_str!(\"foo\"), s, false, ctx).unwrap();\n        obj.get(js_str!(\"foo\"), ctx).unwrap() == JsValue::new(js_str!(\"bar\"))\n    })]);\n}\n\n#[test]\nfn integer_is_true() {\n    assert!(JsValue::new(1).to_boolean());\n    assert!(!JsValue::new(0).to_boolean());\n    assert!(JsValue::new(-1).to_boolean());\n}\n\n#[test]\nfn number_is_true() {\n    assert!(JsValue::new(1.0).to_boolean());\n    assert!(JsValue::new(0.1).to_boolean());\n    assert!(!JsValue::new(0.0).to_boolean());\n    assert!(!JsValue::new(-0.0).to_boolean());\n    assert!(JsValue::new(-1.0).to_boolean());\n    assert!(!JsValue::nan().to_boolean());\n}\n\n// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Equality_comparisons_and_sameness\n#[test]\nfn abstract_equality_comparison() {\n    run_test_actions([\n        TestAction::assert(\"undefined == undefined\"),\n        TestAction::assert(\"null == null\"),\n        TestAction::assert(\"true == true\"),\n        TestAction::assert(\"false == false\"),\n        TestAction::assert(\"'foo' == 'foo'\"),\n        TestAction::assert(\"0 == 0\"),\n        TestAction::assert(\"+0 == -0\"),\n        TestAction::assert(\"+0 == 0\"),\n        TestAction::assert(\"-0 == 0\"),\n        TestAction::assert(\"0 == false\"),\n        TestAction::assert(\"-0 == -false\"),\n        TestAction::assert(\"-0 == -null\"),\n        TestAction::assert(\"-1 == -true\"),\n        TestAction::assert(\"'' == false\"),\n        TestAction::assert(\"'' == 0\"),\n        TestAction::assert(\"'17' == 17\"),\n        TestAction::assert(\"[1,2] == '1,2'\"),\n        TestAction::assert(\"new String('foo') == 'foo'\"),\n        TestAction::assert(\"null == undefined\"),\n        TestAction::assert(\"undefined == null\"),\n        TestAction::assert(\"null != false\"),\n        TestAction::assert(\"[] == ![]\"),\n        TestAction::assert(\"a = { foo: 'bar' }; b = { foo: 'bar'}; a != b\"),\n        TestAction::assert(\"new String('foo') != new String('foo')\"),\n        TestAction::assert(\"0 != null\"),\n        TestAction::assert(\"0 == '-0'\"),\n        TestAction::assert(\"0 == '+0'\"),\n        TestAction::assert(\"'+0' == 0\"),\n        TestAction::assert(\"'-0' == 0\"),\n        TestAction::assert(\"0 != NaN\"),\n        TestAction::assert(\"'foo' != NaN\"),\n        TestAction::assert(\"NaN != NaN\"),\n        TestAction::assert(\"Number.POSITIVE_INFINITY === Number.POSITIVE_INFINITY\"),\n        TestAction::assert(\"Number.NEGATIVE_INFINITY === Number.NEGATIVE_INFINITY\"),\n    ]);\n}\n\n/// Helper function to get the hash of a `Value`.\nfn hash_value(value: &JsValue) -> u64 {\n    let mut hasher = DefaultHasher::new();\n    value.hash(&mut hasher);\n    hasher.finish()\n}\n\n#[allow(clippy::redundant_clone)]\n#[test]\nfn hash_undefined() {\n    let value1 = JsValue::undefined();\n    let value_clone = value1.clone();\n    assert_eq!(value1, value_clone);\n\n    let value2 = JsValue::undefined();\n    assert_eq!(value1, value2);\n\n    assert_eq!(hash_value(&value1), hash_value(&value_clone));\n    assert_eq!(hash_value(&value2), hash_value(&value_clone));\n}\n\n#[test]\nfn hash_rational() {\n    let value1 = JsValue::new(1.0);\n    let value2 = JsValue::new(1.0);\n    assert_eq!(value1, value2);\n    assert_eq!(hash_value(&value1), hash_value(&value2));\n\n    let nan = JsValue::nan();\n    assert_eq!(nan, nan);\n    assert_eq!(hash_value(&nan), hash_value(&nan));\n    assert_ne!(hash_value(&nan), hash_value(&JsValue::new(1.0)));\n}\n\n#[test]\nfn hash_object() {\n    let object1 = JsValue::new(JsObject::with_null_proto());\n    assert_eq!(object1, object1);\n    assert_eq!(object1, object1.clone());\n\n    let object2 = JsValue::new(JsObject::with_null_proto());\n    assert_ne!(object1, object2);\n\n    assert_eq!(hash_value(&object1), hash_value(&object1.clone()));\n    assert_ne!(hash_value(&object1), hash_value(&object2));\n}\n\n#[test]\nfn get_types() {\n    run_test_actions([\n        TestAction::assert_with_op(\"undefined\", |value, _| value.get_type() == Type::Undefined),\n        TestAction::assert_with_op(\"1\", |value, _| value.get_type() == Type::Number),\n        TestAction::assert_with_op(\"1.5\", |value, _| value.get_type() == Type::Number),\n        TestAction::assert_with_op(\"BigInt(\\\"123442424242424424242424242\\\")\", |value, _| {\n            value.get_type() == Type::BigInt\n        }),\n        TestAction::assert_with_op(\"true\", |value, _| value.get_type() == Type::Boolean),\n        TestAction::assert_with_op(\"false\", |value, _| value.get_type() == Type::Boolean),\n        TestAction::assert_with_op(\"function foo() {console.log(\\\"foo\\\");}\", |value, _| {\n            value.get_type() == Type::Undefined\n        }),\n        TestAction::assert_with_op(\"null\", |value, _| value.get_type() == Type::Null),\n        TestAction::assert_with_op(\"var x = {arg: \\\"hi\\\", foo: \\\"hello\\\"}; x\", |value, _| {\n            value.get_type() == Type::Object\n        }),\n        TestAction::assert_with_op(\"\\\"Hi\\\"\", |value, _| value.get_type() == Type::String),\n        TestAction::assert_with_op(\"Symbol()\", |value, _| value.get_type() == Type::Symbol),\n    ]);\n}\n\n#[test]\nfn float_display() {\n    let f64_to_str = |f| JsValue::new(f).display().to_string();\n\n    assert_eq!(f64_to_str(f64::NAN), \"NaN\");\n    assert_eq!(f64_to_str(0.0), \"0\");\n    assert_eq!(f64_to_str(f64::INFINITY), \"Infinity\");\n    assert_eq!(f64_to_str(f64::NEG_INFINITY), \"-Infinity\");\n    assert_eq!(f64_to_str(90.12), \"90.12\");\n    assert_eq!(\n        f64_to_str(111_111_111_111_111_111_111.0),\n        \"111111111111111110000\"\n    );\n    assert_eq!(\n        f64_to_str(1_111_111_111_111_111_111_111.0),\n        \"1.1111111111111111e+21\"\n    );\n\n    assert_eq!(f64_to_str(-90.12), \"-90.12\");\n\n    assert_eq!(\n        f64_to_str(-111_111_111_111_111_111_111.0),\n        \"-111111111111111110000\"\n    );\n    assert_eq!(\n        f64_to_str(-1_111_111_111_111_111_111_111.0),\n        \"-1.1111111111111111e+21\"\n    );\n\n    assert_eq!(f64_to_str(0.000_000_1), \"1e-7\");\n    assert_eq!(f64_to_str(0.000_001), \"0.000001\");\n    assert_eq!(f64_to_str(0.000_000_2), \"2e-7\");\n    assert_eq!(f64_to_str(-0.000_000_1), \"-1e-7\");\n\n    assert_eq!(f64_to_str(3e50), \"3e+50\");\n}\n\n#[test]\nfn string_length_is_not_enumerable() {\n    run_test_actions([TestAction::assert_context(|context| {\n        let object = JsValue::new(js_string!(\"foo\")).to_object(context).unwrap();\n        let length_desc = object\n            .__get_own_property__(\n                &PropertyKey::from(js_string!(\"length\")),\n                &mut InternalMethodPropertyContext::new(context),\n            )\n            .unwrap()\n            .unwrap();\n        !length_desc.expect_enumerable()\n    })]);\n}\n\n#[test]\nfn string_length_is_in_utf16_codeunits() {\n    run_test_actions([TestAction::assert_context(|context| {\n        // 😀 is one Unicode code point, but 2 UTF-16 code units\n        let object = JsValue::new(js_string!(\"😀\")).to_object(context).unwrap();\n\n        let length_desc = object\n            .__get_own_property__(\n                &PropertyKey::from(js_string!(\"length\")),\n                &mut InternalMethodPropertyContext::new(context),\n            )\n            .unwrap()\n            .unwrap();\n        length_desc\n            .expect_value()\n            .to_integer_or_infinity(context)\n            .unwrap()\n            == IntegerOrInfinity::Integer(2)\n    })]);\n}\n\n#[test]\nfn add_number_and_number() {\n    run_test_actions([TestAction::assert_eq(\"1 + 2\", 3)]);\n}\n\n#[test]\nfn add_number_and_string() {\n    run_test_actions([TestAction::assert_eq(\n        \"1 + \\\" + 2 = 3\\\"\",\n        js_str!(\"1 + 2 = 3\"),\n    )]);\n}\n\n#[test]\nfn add_string_and_string() {\n    run_test_actions([TestAction::assert_eq(\n        \"\\\"Hello\\\" + \\\", world\\\"\",\n        js_str!(\"Hello, world\"),\n    )]);\n}\n\n#[test]\nfn add_number_object_and_number() {\n    run_test_actions([TestAction::assert_eq(\"new Number(10) + 6\", 16)]);\n}\n\n#[test]\nfn add_number_object_and_string_object() {\n    run_test_actions([TestAction::assert_eq(\n        \"new Number(10) + new String(\\\"0\\\")\",\n        js_str!(\"100\"),\n    )]);\n}\n\n#[test]\nfn sub_number_and_number() {\n    run_test_actions([TestAction::assert_eq(\"1 - 999\", -998)]);\n}\n\n#[test]\nfn sub_number_object_and_number_object() {\n    run_test_actions([TestAction::assert_eq(\n        \"new Number(1) - new Number(999)\",\n        -998,\n    )]);\n}\n\n#[test]\nfn sub_string_and_number_object() {\n    run_test_actions([TestAction::assert_eq(\"'Hello' - new Number(999)\", f64::NAN)]);\n}\n\n#[test]\nfn div_by_zero() {\n    run_test_actions([TestAction::assert_eq(\"1 / 0\", f64::INFINITY)]);\n}\n\n#[test]\nfn rem_by_zero() {\n    run_test_actions([TestAction::assert_eq(\"1 % 0\", f64::NAN)]);\n}\n\n#[test]\nfn bitand_integer_and_integer() {\n    run_test_actions([TestAction::assert_eq(\"0xFFFF & 0xFF\", 255)]);\n}\n\n#[test]\nfn bitand_integer_and_rational() {\n    run_test_actions([TestAction::assert_eq(\"0xFFFF & 255.5\", 255)]);\n}\n\n#[test]\nfn bitand_rational_and_rational() {\n    run_test_actions([TestAction::assert_eq(\"255.772 & 255.5\", 255)]);\n}\n\n#[test]\n#[allow(clippy::float_cmp)]\nfn pow_number_and_number() {\n    run_test_actions([TestAction::assert_eq(\"3 ** 3\", 27.0)]);\n}\n\n#[test]\nfn pow_number_and_string() {\n    run_test_actions([TestAction::assert_eq(\"3 ** 'Hello'\", f64::NAN)]);\n}\n\n#[test]\nfn assign_pow_number_and_string() {\n    run_test_actions([TestAction::assert_eq(\n        indoc! {r\"\n            let a = 3;\n            a **= 'Hello'\n            a\n        \"},\n        f64::NAN,\n    )]);\n}\n\n#[test]\nfn display_string() {\n    let s = String::from(\"Hello\");\n    let v = JsValue::new(js_string!(s));\n    assert_eq!(v.display().to_string(), \"\\\"Hello\\\"\");\n}\n\n#[test]\nfn display_array_string() {\n    run_test_actions([TestAction::assert_with_op(\"[\\\"Hello\\\"]\", |v, _| {\n        v.display().to_string() == \"[ \\\"Hello\\\" ]\"\n    })]);\n}\n\n#[test]\nfn display_boolean_object() {\n    run_test_actions([TestAction::assert_with_op(\n        indoc! {r#\"\n            let bool = new Boolean(0);\n            bool\n        \"#},\n        |v, _| v.display().to_string() == \"Boolean { false }\",\n    )]);\n}\n\n#[test]\nfn display_number_object() {\n    run_test_actions([TestAction::assert_with_op(\n        indoc! {r#\"\n            let num = new Number(3.14);\n            num\n        \"#},\n        |v, _| v.display().to_string() == \"Number { 3.14 }\",\n    )]);\n}\n\n#[test]\nfn display_negative_zero_object() {\n    run_test_actions([TestAction::assert_with_op(\n        indoc! {r#\"\n            let num = new Number(-0);\n            num\n        \"#},\n        |v, _| v.display().to_string() == \"Number { -0 }\",\n    )]);\n}\n\n#[test]\nfn debug_object() {\n    // We don't care about the contents of the debug display (it is *debug* after all). In the\n    // commit that this test was added, this would cause a stack overflow, so executing\n    // `Debug::fmt` is the assertion.\n    //\n    // However, we want to make sure that no data is being left in the internal hashset, so\n    // executing the formatting twice should result in the same output.\n    run_test_actions([TestAction::assert_with_op(\n        \"new Array([new Date()])\",\n        |v, _| format!(\"{v:?}\") == format!(\"{v:?}\"),\n    )]);\n}\n\n#[test]\nfn display_object() {\n    const DISPLAY: &str = indoc! {r#\"\n        {\n            a: \"a\"\n        }\"#\n    };\n    run_test_actions([TestAction::assert_with_op(\"({a: 'a'})\", |v, _| {\n        v.display().to_string() == DISPLAY\n    })]);\n}\n\n/// Ensure indentation is always 4 and that the property key is not right-align with\n/// the brackets.\n#[test]\nfn display_object_indent() {\n    const DISPLAY: &str = indoc! {r#\"\n        {\n            a: \"a\",\n            hello: \"world\"\n        }\"#\n    };\n    run_test_actions([TestAction::assert_with_op(\n        \"({a: 'a', hello: 'world'})\",\n        |v, _| v.display().to_string() == DISPLAY,\n    )]);\n}\n\n#[test]\nfn to_integer_or_infinity() {\n    run_test_actions([TestAction::inspect_context(|ctx| {\n        assert_eq!(\n            JsValue::undefined().to_integer_or_infinity(ctx).unwrap(),\n            IntegerOrInfinity::Integer(0)\n        );\n        assert_eq!(\n            JsValue::nan().to_integer_or_infinity(ctx).unwrap(),\n            IntegerOrInfinity::Integer(0)\n        );\n        assert_eq!(\n            JsValue::new(0.0).to_integer_or_infinity(ctx).unwrap(),\n            IntegerOrInfinity::Integer(0)\n        );\n        assert_eq!(\n            JsValue::new(-0.0).to_integer_or_infinity(ctx).unwrap(),\n            IntegerOrInfinity::Integer(0)\n        );\n        assert_eq!(\n            JsValue::new(f64::INFINITY)\n                .to_integer_or_infinity(ctx)\n                .unwrap(),\n            IntegerOrInfinity::PositiveInfinity\n        );\n        assert_eq!(\n            JsValue::new(f64::NEG_INFINITY)\n                .to_integer_or_infinity(ctx)\n                .unwrap(),\n            IntegerOrInfinity::NegativeInfinity\n        );\n        assert_eq!(\n            JsValue::new(10).to_integer_or_infinity(ctx).unwrap(),\n            IntegerOrInfinity::Integer(10)\n        );\n        assert_eq!(\n            JsValue::new(11.0).to_integer_or_infinity(ctx).unwrap(),\n            IntegerOrInfinity::Integer(11)\n        );\n        assert_eq!(\n            JsValue::new(js_str!(\"12\"))\n                .to_integer_or_infinity(ctx)\n                .unwrap(),\n            IntegerOrInfinity::Integer(12)\n        );\n        assert_eq!(\n            JsValue::new(true).to_integer_or_infinity(ctx).unwrap(),\n            IntegerOrInfinity::Integer(1)\n        );\n    })]);\n}\n\n#[test]\nfn test_accessors() {\n    run_test_actions([\n        TestAction::run(indoc! {r#\"\n                let arr = [];\n                let a = { get b() { return \"c\" }, set b(value) { arr = arr.concat([value]) }} ;\n                a.b = \"a\";\n            \"#}),\n        TestAction::assert_eq(\"a.b\", js_str!(\"c\")),\n        TestAction::assert_eq(\"arr[0]\", js_str!(\"a\")),\n    ]);\n}\n\n#[test]\nfn to_primitive() {\n    run_test_actions([\n        TestAction::run(indoc! {r#\"\n                let a = {};\n                a[Symbol.toPrimitive] = function() {\n                    return 42;\n                };\n                let primitive = a + 0;\n            \"#}),\n        TestAction::assert_eq(\"primitive\", 42),\n    ]);\n}\n\n#[test]\nfn object_to_property_key() {\n    let source = r#\"\n        let obj = {};\n\n        let to_primitive_42 = {\n            [Symbol.toPrimitive]() {\n                return 42;\n            }\n        };\n        obj[to_primitive_42] = 1;\n\n        let to_primitive_true = {\n            [Symbol.toPrimitive]() {\n                return true;\n            }\n        };\n        obj[to_primitive_true] = 2;\n\n        let to_primitive_str = {\n            [Symbol.toPrimitive]() {\n                return \"str1\";\n            }\n        };\n        obj[to_primitive_str] = 3;\n\n        let mysymbol = Symbol(\"test\");\n        let to_primitive_symbol = {\n            [Symbol.toPrimitive]() {\n                return mysymbol;\n            }\n        };\n        obj[to_primitive_symbol] = 4;\n\n        let to_str = {\n            toString: function() {\n                return \"str2\";\n            }\n        };\n        obj[to_str] = 5;\n    \"#;\n    run_test_actions([\n        TestAction::run(source),\n        TestAction::assert_eq(\"obj[42]\", 1),\n        TestAction::assert_eq(\"obj[true]\", 2),\n        TestAction::assert_eq(\"obj['str1']\", 3),\n        TestAction::assert_eq(\"obj[mysymbol]\", 4),\n        TestAction::assert_eq(\"obj['str2']\", 5),\n    ]);\n}\n\n#[test]\nfn to_index() {\n    run_test_actions([TestAction::inspect_context(|ctx| {\n        assert_eq!(JsValue::undefined().to_index(ctx).unwrap(), 0);\n        assert!(JsValue::new(-1).to_index(ctx).is_err());\n    })]);\n}\n\n#[test]\nfn to_length() {\n    run_test_actions([TestAction::inspect_context(|ctx| {\n        assert_eq!(JsValue::new(f64::NAN).to_length(ctx).unwrap(), 0);\n        assert_eq!(JsValue::new(f64::NEG_INFINITY).to_length(ctx).unwrap(), 0);\n        assert_eq!(\n            JsValue::new(f64::INFINITY).to_length(ctx).unwrap(),\n            Number::MAX_SAFE_INTEGER as u64\n        );\n        assert_eq!(JsValue::new(0.0).to_length(ctx).unwrap(), 0);\n        assert_eq!(JsValue::new(-0.0).to_length(ctx).unwrap(), 0);\n        assert_eq!(JsValue::new(20.9).to_length(ctx).unwrap(), 20);\n        assert_eq!(JsValue::new(-20.9).to_length(ctx).unwrap(), 0);\n        assert_eq!(\n            JsValue::new(100_000_000_000.0).to_length(ctx).unwrap(),\n            100_000_000_000\n        );\n        assert_eq!(\n            JsValue::new(4_010_101_101.0).to_length(ctx).unwrap(),\n            4_010_101_101\n        );\n    })]);\n}\n\n#[test]\nfn to_int32() {\n    run_test_actions([TestAction::inspect_context(|ctx| {\n        macro_rules! check_to_int32 {\n            ($from:expr => $to:expr) => {\n                assert_eq!(JsValue::new($from).to_i32(ctx).unwrap(), $to);\n            };\n        }\n\n        check_to_int32!(f64::NAN => 0);\n        check_to_int32!(f64::NEG_INFINITY => 0);\n        check_to_int32!(f64::INFINITY => 0);\n        check_to_int32!(0 => 0);\n        check_to_int32!(-0.0 => 0);\n\n        check_to_int32!(20.9 => 20);\n        check_to_int32!(-20.9 => -20);\n\n        check_to_int32!(Number::MIN_VALUE => 0);\n        check_to_int32!(-Number::MIN_VALUE => 0);\n        check_to_int32!(0.1 => 0);\n        check_to_int32!(-0.1 => 0);\n        check_to_int32!(1 => 1);\n        check_to_int32!(1.1 => 1);\n        check_to_int32!(-1 => -1);\n        check_to_int32!(0.6 => 0);\n        check_to_int32!(1.6 => 1);\n        check_to_int32!(-0.6 => 0);\n        check_to_int32!(-1.6 => -1);\n\n        check_to_int32!(2_147_483_647.0 => 2_147_483_647);\n        check_to_int32!(2_147_483_648.0 => -2_147_483_648);\n        check_to_int32!(2_147_483_649.0 => -2_147_483_647);\n\n        check_to_int32!(4_294_967_295.0 => -1);\n        check_to_int32!(4_294_967_296.0 => 0);\n        check_to_int32!(4_294_967_297.0 => 1);\n\n        check_to_int32!(-2_147_483_647.0 => -2_147_483_647);\n        check_to_int32!(-2_147_483_648.0 => -2_147_483_648);\n        check_to_int32!(-2_147_483_649.0 => 2_147_483_647);\n\n        check_to_int32!(-4_294_967_295.0 => 1);\n        check_to_int32!(-4_294_967_296.0 => 0);\n        check_to_int32!(-4_294_967_297.0 => -1);\n\n        check_to_int32!(2_147_483_648.25 => -2_147_483_648);\n        check_to_int32!(2_147_483_648.5 => -2_147_483_648);\n        check_to_int32!(2_147_483_648.75 => -2_147_483_648);\n        check_to_int32!(4_294_967_295.25 => -1);\n        check_to_int32!(4_294_967_295.5 => -1);\n        check_to_int32!(4_294_967_295.75 => -1);\n        check_to_int32!(3_000_000_000.25 => -1_294_967_296);\n        check_to_int32!(3_000_000_000.5 => -1_294_967_296);\n        check_to_int32!(3_000_000_000.75 => -1_294_967_296);\n\n        check_to_int32!(-2_147_483_648.25 => -2_147_483_648);\n        check_to_int32!(-2_147_483_648.5 => -2_147_483_648);\n        check_to_int32!(-2_147_483_648.75 => -2_147_483_648);\n        check_to_int32!(-4_294_967_295.25 => 1);\n        check_to_int32!(-4_294_967_295.5 => 1);\n        check_to_int32!(-4_294_967_295.75 => 1);\n        check_to_int32!(-3_000_000_000.25 => 1_294_967_296);\n        check_to_int32!(-3_000_000_000.5 => 1_294_967_296);\n        check_to_int32!(-3_000_000_000.75 => 1_294_967_296);\n\n        let base = 18_446_744_073_709_552_000.0; // approx. 2^64\n        check_to_int32!(base + 0.0 => 0);\n        check_to_int32!(base + 1117.0 => 0);\n        check_to_int32!(base + 2234.0 => 4096);\n        check_to_int32!(base + 3351.0 => 4096);\n        check_to_int32!(base + 4468.0 => 4096);\n        check_to_int32!(base + 5585.0 => 4096);\n        check_to_int32!(base + 6702.0 => 8192);\n        check_to_int32!(base + 7819.0 => 8192);\n        check_to_int32!(base + 8936.0 => 8192);\n        check_to_int32!(base + 10053.0 => 8192);\n        check_to_int32!(base + 11170.0 => 12288);\n        check_to_int32!(base + 12287.0 => 12288);\n        check_to_int32!(base + 13404.0 => 12288);\n        check_to_int32!(base + 14521.0 => 16384);\n        check_to_int32!(base + 15638.0 => 16384);\n        check_to_int32!(base + 16755.0 => 16384);\n        check_to_int32!(base + 17872.0 => 16384);\n        check_to_int32!(base + 18989.0 => 20480);\n        check_to_int32!(base + 20106.0 => 20480);\n        check_to_int32!(base + 21223.0 => 20480);\n        check_to_int32!(base + 22340.0 => 20480);\n        check_to_int32!(base + 23457.0 => 24576);\n        check_to_int32!(base + 24574.0 => 24576);\n        check_to_int32!(base + 25691.0 => 24576);\n        check_to_int32!(base + 26808.0 => 28672);\n        check_to_int32!(base + 27925.0 => 28672);\n        check_to_int32!(base + 29042.0 => 28672);\n        check_to_int32!(base + 30159.0 => 28672);\n        check_to_int32!(base + 31276.0 => 32768);\n\n        // bignum is the highest number with bit 31 set.\n        let bignum = f64::from_bits(0x452f_ffff_ffff_ffff);\n        check_to_int32!(bignum => -2_147_483_648);\n        check_to_int32!(-bignum => -2_147_483_648);\n        check_to_int32!(2.0 * bignum => 0);\n        check_to_int32!(-(2.0 * bignum) => 0);\n        check_to_int32!(bignum - 2_147_483_648.0 => 0);\n        check_to_int32!(-(bignum - 2_147_483_648.0) => 0);\n\n        // max_fraction is largest number below 1.\n        let max_fraction = 1.0f64.next_down();\n        check_to_int32!(max_fraction => 0);\n        check_to_int32!(-max_fraction => 0);\n    })]);\n}\n\n#[test]\nfn to_string() {\n    run_test_actions([TestAction::inspect_context(|ctx| {\n        assert_eq!(&JsValue::null().to_string(ctx).unwrap(), \"null\");\n        assert_eq!(&JsValue::undefined().to_string(ctx).unwrap(), \"undefined\");\n        assert_eq!(&JsValue::new(55).to_string(ctx).unwrap(), \"55\");\n        assert_eq!(&JsValue::new(55.0).to_string(ctx).unwrap(), \"55\");\n        assert_eq!(\n            JsValue::new(js_str!(\"hello\")).to_string(ctx).unwrap(),\n            js_str!(\"hello\")\n        );\n    })]);\n}\n\n#[test]\nfn to_bigint() {\n    run_test_actions([TestAction::inspect_context(|ctx| {\n        assert!(JsValue::null().to_bigint(ctx).is_err());\n        assert!(JsValue::undefined().to_bigint(ctx).is_err());\n        assert!(JsValue::new(55).to_bigint(ctx).is_err());\n        assert!(JsValue::new(10.0).to_bigint(ctx).is_err());\n        assert!(JsValue::new(js_str!(\"100\")).to_bigint(ctx).is_ok());\n    })]);\n}\n\n#[test]\nfn pad_end() {\n    run_test_actions([\n        TestAction::assert_eq(\"'abc'.padEnd(10, false)\", js_string!(\"abcfalsefa\")),\n        TestAction::assert_eq(\"'abc'.padEnd(10, true)\", js_string!(\"abctruetru\")),\n        TestAction::assert_eq(\"'abc'.padEnd(10, null)\", js_string!(\"abcnullnul\")),\n        TestAction::assert_eq(\"'abc'.padEnd(10, 0)\", js_string!(\"abc0000000\")),\n        TestAction::assert_eq(\"'abc'.padEnd(10, -0)\", js_string!(\"abc0000000\")),\n        TestAction::assert_eq(\"'abc'.padEnd(10, NaN)\", js_string!(\"abcNaNNaNN\")),\n    ]);\n}\n\n/// Test cyclic conversions that previously caused stack overflows\n/// Relevant mitigation for these are in `JsObject::ordinary_to_primitive` and\n/// `JsObject::to_json`\nmod cyclic_conversions {\n    use crate::JsNativeErrorKind;\n\n    use super::*;\n\n    #[test]\n    fn to_json_cyclic() {\n        run_test_actions([TestAction::assert_native_error(\n            indoc! {r#\"\n                let a = [];\n                a[0] = a;\n                JSON.stringify(a)\n            \"#},\n            JsNativeErrorKind::Type,\n            \"cyclic object value\",\n        )]);\n    }\n\n    #[test]\n    fn to_json_noncyclic() {\n        run_test_actions([TestAction::assert_eq(\n            indoc! {r#\"\n                let b = [];\n                let a = [b, b];\n                JSON.stringify(a)\n            \"#},\n            js_str!(\"[[],[]]\"),\n        )]);\n    }\n\n    // These tests don't throw errors. Instead we mirror Chrome / Firefox behavior for these\n    // conversions\n\n    #[test]\n    fn to_string_cyclic() {\n        run_test_actions([TestAction::assert_eq(\n            indoc! {r#\"\n                let a = [];\n                a[0] = a;\n                a.toString()\n            \"#},\n            js_string!(),\n        )]);\n    }\n\n    #[test]\n    fn to_number_cyclic() {\n        run_test_actions([TestAction::assert_eq(\n            indoc! {r#\"\n                let a = [];\n                a[0] = a;\n                +a\n            \"#},\n            0.0,\n        )]);\n    }\n\n    #[test]\n    fn to_boolean_cyclic() {\n        // this already worked before the mitigation, but we don't want to cause a regression\n        run_test_actions([TestAction::assert(indoc! {r#\"\n                let a = [];\n                a[0] = a;\n                !!a\n            \"#})]);\n    }\n\n    #[test]\n    fn to_bigint_cyclic() {\n        run_test_actions([TestAction::assert_eq(\n            indoc! {r#\"\n                let a = [];\n                a[0] = a;\n                BigInt(a)\n            \"#},\n            JsBigInt::new(0),\n        )]);\n    }\n\n    #[test]\n    fn to_u32_cyclic() {\n        run_test_actions([TestAction::assert_eq(\n            indoc! {r#\"\n                    let a = [];\n                    a[0] = a;\n                    a | 0\n                \"#},\n            0.0,\n        )]);\n    }\n}\n\nmod abstract_relational_comparison {\n    #![allow(clippy::bool_assert_comparison)]\n\n    use super::*;\n\n    #[test]\n    fn number_less_than_number() {\n        run_test_actions([\n            TestAction::assert(\"1 < 2\"),\n            TestAction::assert(\"!(2 < 2)\"),\n            TestAction::assert(\"!(3 < 2)\"),\n            TestAction::assert(\"2 < 2.5\"),\n            TestAction::assert(\"!(2.5 < 2)\"),\n        ]);\n    }\n\n    #[test]\n    fn string_less_than_number() {\n        run_test_actions([\n            TestAction::assert(\"'1' < 2\"),\n            TestAction::assert(\"!('2' < 2)\"),\n            TestAction::assert(\"!('3' < 2)\"),\n            TestAction::assert(\"'2' < 2.5\"),\n            TestAction::assert(\"!('2.5' < 2.5)\"),\n        ]);\n    }\n\n    #[test]\n    fn number_less_than_string() {\n        run_test_actions([\n            TestAction::assert(\"1 < '2'\"),\n            TestAction::assert(\"!(2 < '2')\"),\n            TestAction::assert(\"!(3 < '2')\"),\n            TestAction::assert(\"2 < '2.5'\"),\n            TestAction::assert(\"!(2.5 < '2')\"),\n        ]);\n    }\n\n    #[test]\n    fn number_object_less_than_number() {\n        run_test_actions([\n            TestAction::assert(\"new Number(1) < 2\"),\n            TestAction::assert(\"!(new Number(2) < 2)\"),\n            TestAction::assert(\"!(new Number(3) < 2)\"),\n            TestAction::assert(\"new Number(2) < 2.5\"),\n            TestAction::assert(\"!(new Number(2.5) < 2)\"),\n        ]);\n    }\n\n    #[test]\n    fn number_object_less_than_number_object() {\n        run_test_actions([\n            TestAction::assert(\"new Number(1) < new Number(2)\"),\n            TestAction::assert(\"!(new Number(2) < new Number(2))\"),\n            TestAction::assert(\"!(new Number(3) < new Number(2))\"),\n            TestAction::assert(\"new Number(2) < new Number(2.5)\"),\n            TestAction::assert(\"!(new Number(2.5) < new Number(2))\"),\n        ]);\n    }\n\n    #[test]\n    fn string_less_than_string() {\n        run_test_actions([\n            TestAction::assert(\"!('hello' < 'hello')\"),\n            TestAction::assert(\"'hell' < 'hello'\"),\n            TestAction::assert(\"'hello, world' < 'world'\"),\n            TestAction::assert(\"'aa' < 'ab'\"),\n        ]);\n    }\n\n    #[test]\n    fn string_object_less_than_string() {\n        run_test_actions([\n            TestAction::assert(\"!(new String('hello') < 'hello')\"),\n            TestAction::assert(\"new String('hell') < 'hello'\"),\n            TestAction::assert(\"new String('hello, world') < 'world'\"),\n            TestAction::assert(\"new String('aa') < 'ab'\"),\n        ]);\n    }\n\n    #[test]\n    fn string_object_less_than_string_object() {\n        run_test_actions([\n            TestAction::assert(\"!(new String('hello') < new String('hello'))\"),\n            TestAction::assert(\"new String('hell') < new String('hello')\"),\n            TestAction::assert(\"new String('hello, world') < new String('world')\"),\n            TestAction::assert(\"new String('aa') < new String('ab')\"),\n        ]);\n    }\n\n    #[test]\n    fn bigint_less_than_number() {\n        run_test_actions([\n            TestAction::assert(\"1n < 10\"),\n            TestAction::assert(\"!(10n < 10)\"),\n            TestAction::assert(\"!(100n < 10)\"),\n            TestAction::assert(\"10n < 10.9\"),\n        ]);\n    }\n\n    #[test]\n    fn number_less_than_bigint() {\n        run_test_actions([\n            TestAction::assert(\"!(10 < 1n)\"),\n            TestAction::assert(\"!(1 < 1n)\"),\n            TestAction::assert(\"!(-1 < -1n)\"),\n            TestAction::assert(\"-1.9 < -1n\"),\n        ]);\n    }\n\n    #[test]\n    fn negative_infinity_less_than_bigint() {\n        run_test_actions([\n            TestAction::assert(\"-Infinity < -10000000000n\"),\n            TestAction::assert(\"-Infinity < (-1n << 100n)\"),\n        ]);\n    }\n\n    #[test]\n    fn bigint_less_than_infinity() {\n        run_test_actions([\n            TestAction::assert(\"!(1000n < NaN)\"),\n            TestAction::assert(\"!((1n << 100n) < NaN)\"),\n        ]);\n    }\n\n    #[test]\n    fn nan_less_than_bigint() {\n        run_test_actions([\n            TestAction::assert(\"!(NaN < -10000000000n)\"),\n            TestAction::assert(\"!(NaN < (-1n << 100n))\"),\n        ]);\n    }\n\n    #[test]\n    fn bigint_less_than_nan() {\n        run_test_actions([\n            TestAction::assert(\"1000n < Infinity\"),\n            TestAction::assert(\"(1n << 100n) < Infinity\"),\n        ]);\n    }\n\n    #[test]\n    fn bigint_less_than_string() {\n        run_test_actions([\n            TestAction::assert(\"!(1000n < '1000')\"),\n            TestAction::assert(\"1000n < '2000'\"),\n            TestAction::assert(\"!(1n < '-1')\"),\n            TestAction::assert(\"!(2n < '-1')\"),\n            TestAction::assert(\"!(-100n < 'InvalidBigInt')\"),\n        ]);\n    }\n\n    #[test]\n    fn string_less_than_bigint() {\n        run_test_actions([\n            TestAction::assert(\"!('1000' < 1000n)\"),\n            TestAction::assert(\"!('2000' < 1000n)\"),\n            TestAction::assert(\"'500' < 1000n\"),\n            TestAction::assert(\"'-1' < 1n\"),\n            TestAction::assert(\"'-1' < 2n\"),\n            TestAction::assert(\"!('InvalidBigInt' < -100n)\"),\n        ]);\n    }\n\n    // -------------------------------------------\n\n    #[test]\n    fn number_less_than_or_equal_number() {\n        run_test_actions([\n            TestAction::assert(\"1 <= 2\"),\n            TestAction::assert(\"2 <= 2\"),\n            TestAction::assert(\"!(3 <= 2)\"),\n            TestAction::assert(\"2 <= 2.5\"),\n            TestAction::assert(\"!(2.5 <= 2)\"),\n        ]);\n    }\n\n    #[test]\n    fn string_less_than_or_equal_number() {\n        run_test_actions([\n            TestAction::assert(\"'1' <= 2\"),\n            TestAction::assert(\"'2' <= 2\"),\n            TestAction::assert(\"!('3' <= 2)\"),\n            TestAction::assert(\"'2' <= 2.5\"),\n            TestAction::assert(\"!('2.5' <= 2)\"),\n        ]);\n    }\n\n    #[test]\n    fn number_less_than_or_equal_string() {\n        run_test_actions([\n            TestAction::assert(\"1 <= '2'\"),\n            TestAction::assert(\"2 <= '2'\"),\n            TestAction::assert(\"!(3 <= '2')\"),\n            TestAction::assert(\"2 <= '2.5'\"),\n            TestAction::assert(\"!(2.5 <= '2')\"),\n        ]);\n    }\n\n    #[test]\n    fn number_object_less_than_or_equal_number() {\n        run_test_actions([\n            TestAction::assert(\"new Number(1) <= '2'\"),\n            TestAction::assert(\"new Number(2) <= '2'\"),\n            TestAction::assert(\"!(new Number(3) <= '2')\"),\n            TestAction::assert(\"new Number(2) <= '2.5'\"),\n            TestAction::assert(\"!(new Number(2.5) <= '2')\"),\n        ]);\n    }\n\n    #[test]\n    fn number_object_less_than_number_or_equal_object() {\n        run_test_actions([\n            TestAction::assert(\"new Number(1) <= new Number(2)\"),\n            TestAction::assert(\"new Number(2) <= new Number(2)\"),\n            TestAction::assert(\"!(new Number(3) <= new Number(2))\"),\n            TestAction::assert(\"new Number(2) <= new Number(2.5)\"),\n            TestAction::assert(\"!(new Number(2.5) <= new Number(2))\"),\n        ]);\n    }\n\n    #[test]\n    fn string_less_than_or_equal_string() {\n        run_test_actions([\n            TestAction::assert(\"'hello' <= 'hello'\"),\n            TestAction::assert(\"'hell' <= 'hello'\"),\n            TestAction::assert(\"'hello, world' <= 'world'\"),\n            TestAction::assert(\"'aa' <= 'ab'\"),\n        ]);\n    }\n\n    #[test]\n    fn string_object_less_than_or_equal_string() {\n        run_test_actions([\n            TestAction::assert(\"new String('hello') <= 'hello'\"),\n            TestAction::assert(\"new String('hell') <= 'hello'\"),\n            TestAction::assert(\"new String('hello, world') <= 'world'\"),\n            TestAction::assert(\"new String('aa') <= 'ab'\"),\n        ]);\n    }\n\n    #[test]\n    fn string_object_less_than_string_or_equal_object() {\n        run_test_actions([\n            TestAction::assert(\"new String('hello') <= new String('hello')\"),\n            TestAction::assert(\"new String('hell') <= new String('hello')\"),\n            TestAction::assert(\"new String('hello, world') <= new String('world')\"),\n            TestAction::assert(\"new String('aa') <= new String('ab')\"),\n        ]);\n    }\n\n    #[test]\n    fn bigint_less_than_or_equal_number() {\n        run_test_actions([\n            TestAction::assert(\"1n <= 10\"),\n            TestAction::assert(\"10n <= 10\"),\n            TestAction::assert(\"!(100n <= 10)\"),\n            TestAction::assert(\"10n <= 10.9\"),\n        ]);\n    }\n\n    #[test]\n    fn number_less_than_or_equal_bigint() {\n        run_test_actions([\n            TestAction::assert(\"!(10 <= 1n)\"),\n            TestAction::assert(\"1 <= 1n\"),\n            TestAction::assert(\"-1 <= -1n\"),\n            TestAction::assert(\"-1.9 <= -1n\"),\n        ]);\n    }\n\n    #[test]\n    fn negative_infinity_less_than_or_equal_bigint() {\n        run_test_actions([\n            TestAction::assert(\"-Infinity <= -10000000000n\"),\n            TestAction::assert(\"-Infinity <= (-1n << 100n)\"),\n        ]);\n    }\n\n    #[test]\n    fn bigint_less_than_or_equal_infinity() {\n        run_test_actions([\n            TestAction::assert(\"!(1000n <= NaN)\"),\n            TestAction::assert(\"!((1n << 100n) <= NaN)\"),\n        ]);\n    }\n\n    #[test]\n    fn nan_less_than_or_equal_bigint() {\n        run_test_actions([\n            TestAction::assert(\"!(NaN <= -10000000000n)\"),\n            TestAction::assert(\"!(NaN <= (-1n << 100n))\"),\n        ]);\n    }\n\n    #[test]\n    fn bigint_less_than_or_equal_nan() {\n        run_test_actions([\n            TestAction::assert(\"1000n <= Infinity\"),\n            TestAction::assert(\"(1n << 100n) <= Infinity\"),\n        ]);\n    }\n\n    #[test]\n    fn bigint_less_than_or_equal_string() {\n        run_test_actions([\n            TestAction::assert(\"1000n <= '1000'\"),\n            TestAction::assert(\"1000n <= '2000'\"),\n            TestAction::assert(\"!(1n <= '-1')\"),\n            TestAction::assert(\"!(2n <= '-1')\"),\n            TestAction::assert(\"!(-100n <= 'InvalidBigInt')\"),\n        ]);\n    }\n\n    #[test]\n    fn string_less_than_or_equal_bigint() {\n        run_test_actions([\n            TestAction::assert(\"'1000' <= 1000n\"),\n            TestAction::assert(\"!('2000' <= 1000n)\"),\n            TestAction::assert(\"'500' <= 1000n\"),\n            TestAction::assert(\"'-1' <= 1n\"),\n            TestAction::assert(\"'-1' <= 2n\"),\n            TestAction::assert(\"!('InvalidBigInt' <= -100n)\"),\n        ]);\n    }\n\n    // -------------------------------------------\n\n    #[test]\n    fn number_greater_than_number() {\n        run_test_actions([\n            TestAction::assert(\"!(1 > 2)\"),\n            TestAction::assert(\"!(2 > 2)\"),\n            TestAction::assert(\"3 > 2\"),\n            TestAction::assert(\"!(2 > 2.5)\"),\n            TestAction::assert(\"2.5 > 2\"),\n        ]);\n    }\n\n    #[test]\n    fn string_greater_than_number() {\n        run_test_actions([\n            TestAction::assert(\"!('1' > 2)\"),\n            TestAction::assert(\"!('2' > 2)\"),\n            TestAction::assert(\"'3' > 2\"),\n            TestAction::assert(\"!('2' > 2.5)\"),\n            TestAction::assert(\"'2.5' > 2\"),\n        ]);\n    }\n\n    #[test]\n    fn number_less_greater_string() {\n        run_test_actions([\n            TestAction::assert(\"!(1 > '2')\"),\n            TestAction::assert(\"!(2 > '2')\"),\n            TestAction::assert(\"3 > '2'\"),\n            TestAction::assert(\"!(2 > '2.5')\"),\n            TestAction::assert(\"2.5 > '2'\"),\n        ]);\n    }\n\n    #[test]\n    fn number_object_greater_than_number() {\n        run_test_actions([\n            TestAction::assert(\"!(new Number(1) > '2')\"),\n            TestAction::assert(\"!(new Number(2) > '2')\"),\n            TestAction::assert(\"new Number(3) > '2'\"),\n            TestAction::assert(\"!(new Number(2) > '2.5')\"),\n            TestAction::assert(\"new Number(2.5) > '2'\"),\n        ]);\n    }\n\n    #[test]\n    fn number_object_greater_than_number_object() {\n        run_test_actions([\n            TestAction::assert(\"!(new Number(1) > new Number(2))\"),\n            TestAction::assert(\"!(new Number(2) > new Number(2))\"),\n            TestAction::assert(\"3 > new Number(2)\"),\n            TestAction::assert(\"!(new Number(2) > new Number(2.5))\"),\n            TestAction::assert(\"new Number(2.5) > new Number(2)\"),\n        ]);\n    }\n\n    #[test]\n    fn string_greater_than_string() {\n        run_test_actions([\n            TestAction::assert(\"!('hello' > 'hello')\"),\n            TestAction::assert(\"!('hell' > 'hello')\"),\n            TestAction::assert(\"!('hello, world' > 'world')\"),\n            TestAction::assert(\"!('aa' > 'ab')\"),\n            TestAction::assert(\"'ab' > 'aa'\"),\n        ]);\n    }\n\n    #[test]\n    fn string_object_greater_than_string() {\n        run_test_actions([\n            TestAction::assert(\"!(new String('hello') > 'hello')\"),\n            TestAction::assert(\"!(new String('hell') > 'hello')\"),\n            TestAction::assert(\"!(new String('hello, world') > 'world')\"),\n            TestAction::assert(\"!(new String('aa') > 'ab')\"),\n            TestAction::assert(\"new String('ab') > 'aa'\"),\n        ]);\n    }\n\n    #[test]\n    fn string_object_greater_than_string_object() {\n        run_test_actions([\n            TestAction::assert(\"!(new String('hello') > new String('hello'))\"),\n            TestAction::assert(\"!(new String('hell') > new String('hello'))\"),\n            TestAction::assert(\"!(new String('hello, world') > new String('world'))\"),\n            TestAction::assert(\"!(new String('aa') > new String('ab'))\"),\n            TestAction::assert(\"new String('ab') > new String('aa')\"),\n        ]);\n    }\n\n    #[test]\n    fn bigint_greater_than_number() {\n        run_test_actions([\n            TestAction::assert(\"!(1n > 10)\"),\n            TestAction::assert(\"!(10n > 10)\"),\n            TestAction::assert(\"100n > 10\"),\n            TestAction::assert(\"!(10n > 10.9)\"),\n        ]);\n    }\n\n    #[test]\n    fn number_greater_than_bigint() {\n        run_test_actions([\n            TestAction::assert(\"10 > 1n\"),\n            TestAction::assert(\"!(1 > 1n)\"),\n            TestAction::assert(\"!(-1 > -1n)\"),\n            TestAction::assert(\"!(-1.9 > -1n)\"),\n        ]);\n    }\n\n    #[test]\n    fn negative_infinity_greater_than_bigint() {\n        run_test_actions([\n            TestAction::assert(\"!(-Infinity > -10000000000n)\"),\n            TestAction::assert(\"!(-Infinity > (-1n << 100n))\"),\n        ]);\n    }\n\n    #[test]\n    fn bigint_greater_than_infinity() {\n        run_test_actions([\n            TestAction::assert(\"!(1000n > NaN)\"),\n            TestAction::assert(\"!((1n << 100n) > NaN)\"),\n        ]);\n    }\n\n    #[test]\n    fn nan_greater_than_bigint() {\n        run_test_actions([\n            TestAction::assert(\"!(NaN > -10000000000n)\"),\n            TestAction::assert(\"!(NaN > (-1n << 100n))\"),\n        ]);\n    }\n\n    #[test]\n    fn bigint_greater_than_nan() {\n        run_test_actions([\n            TestAction::assert(\"!(1000n > Infinity)\"),\n            TestAction::assert(\"!((1n << 100n) > Infinity)\"),\n        ]);\n    }\n\n    #[test]\n    fn bigint_greater_than_string() {\n        run_test_actions([\n            TestAction::assert(\"!(1000n > '1000')\"),\n            TestAction::assert(\"!(1000n > '2000')\"),\n            TestAction::assert(\"1n > '-1'\"),\n            TestAction::assert(\"2n > '-1'\"),\n            TestAction::assert(\"!(-100n > 'InvalidBigInt')\"),\n        ]);\n    }\n\n    #[test]\n    fn string_greater_than_bigint() {\n        run_test_actions([\n            TestAction::assert(\"!('1000' > 1000n)\"),\n            TestAction::assert(\"'2000' > 1000n\"),\n            TestAction::assert(\"!('500' > 1000n)\"),\n            TestAction::assert(\"!('-1' > 1n)\"),\n            TestAction::assert(\"!('-1' > 2n)\"),\n            TestAction::assert(\"!('InvalidBigInt' > -100n)\"),\n        ]);\n    }\n\n    // ----------------------------------------------\n\n    #[test]\n    fn number_greater_than_or_equal_number() {\n        run_test_actions([\n            TestAction::assert(\"!(1 >= 2)\"),\n            TestAction::assert(\"2 >= 2\"),\n            TestAction::assert(\"3 >= 2\"),\n            TestAction::assert(\"!(2 >= 2.5)\"),\n            TestAction::assert(\"2.5 >= 2\"),\n        ]);\n    }\n\n    #[test]\n    fn string_greater_than_or_equal_number() {\n        run_test_actions([\n            TestAction::assert(\"!('1' >= 2)\"),\n            TestAction::assert(\"'2' >= 2\"),\n            TestAction::assert(\"'3' >= 2\"),\n            TestAction::assert(\"!('2' >= 2.5)\"),\n            TestAction::assert(\"'2.5' >= 2\"),\n        ]);\n    }\n\n    #[test]\n    fn number_less_greater_or_equal_string() {\n        run_test_actions([\n            TestAction::assert(\"!(1 >= '2')\"),\n            TestAction::assert(\"2 >= '2'\"),\n            TestAction::assert(\"3 >= '2'\"),\n            TestAction::assert(\"!(2 >= '2.5')\"),\n            TestAction::assert(\"2.5 >= '2'\"),\n        ]);\n    }\n\n    #[test]\n    fn number_object_greater_than_or_equal_number() {\n        run_test_actions([\n            TestAction::assert(\"!(new Number(1) >= '2')\"),\n            TestAction::assert(\"new Number(2) >= '2'\"),\n            TestAction::assert(\"new Number(3) >= '2'\"),\n            TestAction::assert(\"!(new Number(2) >= '2.5')\"),\n            TestAction::assert(\"new Number(2.5) >= '2'\"),\n        ]);\n    }\n\n    #[test]\n    fn number_object_greater_than_or_equal_number_object() {\n        run_test_actions([\n            TestAction::assert(\"!(new Number(1) >= new Number(2))\"),\n            TestAction::assert(\"new Number(2) >= new Number(2)\"),\n            TestAction::assert(\"new Number(3) >= new Number(2)\"),\n            TestAction::assert(\"!(new Number(2) >= new Number(2.5))\"),\n            TestAction::assert(\"new Number(2.5) >= new Number(2)\"),\n        ]);\n    }\n\n    #[test]\n    fn string_greater_than_or_equal_string() {\n        run_test_actions([\n            TestAction::assert(\"'hello' >= 'hello'\"),\n            TestAction::assert(\"!('hell' >= 'hello')\"),\n            TestAction::assert(\"!('hello, world' >= 'world')\"),\n            TestAction::assert(\"!('aa' >= 'ab')\"),\n            TestAction::assert(\"'ab' >= 'aa'\"),\n        ]);\n    }\n\n    #[test]\n    fn string_object_greater_or_equal_than_string() {\n        run_test_actions([\n            TestAction::assert(\"new String('hello') >= 'hello'\"),\n            TestAction::assert(\"!(new String('hell') >= 'hello')\"),\n            TestAction::assert(\"!(new String('hello, world') >= 'world')\"),\n            TestAction::assert(\"!(new String('aa') >= 'ab')\"),\n            TestAction::assert(\"new String('ab') >= 'aa'\"),\n        ]);\n    }\n\n    #[test]\n    fn string_object_greater_than_or_equal_string_object() {\n        run_test_actions([\n            TestAction::assert(\"new String('hello') >= new String('hello')\"),\n            TestAction::assert(\"!(new String('hell') >= new String('hello'))\"),\n            TestAction::assert(\"!(new String('hello, world') >= new String('world'))\"),\n            TestAction::assert(\"!(new String('aa') >= new String('ab'))\"),\n            TestAction::assert(\"new String('ab') >= new String('aa')\"),\n        ]);\n    }\n\n    #[test]\n    fn bigint_greater_than_or_equal_number() {\n        run_test_actions([\n            TestAction::assert(\"!(1n >= 10)\"),\n            TestAction::assert(\"10n >= 10\"),\n            TestAction::assert(\"100n >= 10\"),\n            TestAction::assert(\"!(10n >= 10.9)\"),\n        ]);\n    }\n\n    #[test]\n    fn number_greater_than_or_equal_bigint() {\n        run_test_actions([\n            TestAction::assert(\"10 >= 1n\"),\n            TestAction::assert(\"1 >= 1n\"),\n            TestAction::assert(\"-1 >= -1n\"),\n            TestAction::assert(\"!(-1.9 >= -1n)\"),\n        ]);\n    }\n\n    #[test]\n    fn negative_infinity_greater_or_equal_than_bigint() {\n        run_test_actions([\n            TestAction::assert(\"!(-Infinity >= -10000000000n)\"),\n            TestAction::assert(\"!(-Infinity >= (-1n << 100n))\"),\n        ]);\n    }\n\n    #[test]\n    fn bigint_greater_than_or_equal_infinity() {\n        run_test_actions([\n            TestAction::assert(\"!(1000n >= NaN)\"),\n            TestAction::assert(\"!((1n << 100n) >= NaN)\"),\n        ]);\n    }\n\n    #[test]\n    fn nan_greater_than_or_equal_bigint() {\n        run_test_actions([\n            TestAction::assert(\"!(NaN >= -10000000000n)\"),\n            TestAction::assert(\"!(NaN >= (-1n << 100n))\"),\n        ]);\n    }\n\n    #[test]\n    fn bigint_greater_than_or_equal_nan() {\n        run_test_actions([\n            TestAction::assert(\"!(1000n >= Infinity)\"),\n            TestAction::assert(\"!((1n << 100n) >= Infinity)\"),\n        ]);\n    }\n\n    #[test]\n    fn bigint_greater_than_or_equal_string() {\n        run_test_actions([\n            TestAction::assert(\"1000n >= '1000'\"),\n            TestAction::assert(\"!(1000n >= '2000')\"),\n            TestAction::assert(\"1n >= '-1'\"),\n            TestAction::assert(\"2n >= '-1'\"),\n            TestAction::assert(\"!(-100n >= 'InvalidBigInt')\"),\n        ]);\n    }\n\n    #[test]\n    fn string_greater_than_or_equal_bigint() {\n        run_test_actions([\n            TestAction::assert(\"'1000' >= 1000n\"),\n            TestAction::assert(\"'2000' >= 1000n\"),\n            TestAction::assert(\"!('500' >= 1000n)\"),\n            TestAction::assert(\"!('-1' >= 1n)\"),\n            TestAction::assert(\"!('-1' >= 2n)\"),\n            TestAction::assert(\"!('InvalidBigInt' >= -100n)\"),\n        ]);\n    }\n}\n\nmod js_value_macro {\n    use crate::value::TryIntoJs;\n    use crate::{JsValue, TestAction, js_string, run_test_actions};\n    use boa_engine::{Context, JsResult, js_value};\n    use boa_string::JsString;\n    use std::ops::Neg;\n\n    #[test]\n    fn primitive_values() {\n        run_test_actions([\n            TestAction::assert_eq(\"true\", js_value!(true)),\n            TestAction::assert_eq(\"false\", js_value!(false)),\n            TestAction::assert_eq(\"1\", js_value!(1)),\n            TestAction::assert_eq(\"0xFFFF_FFFF\", js_value!(4_294_967_295u32)),\n            TestAction::assert_eq(\"1.5\", js_value!(1.5)),\n            TestAction::assert_eq(\"Infinity\", js_value!(f32::INFINITY)),\n            TestAction::assert_eq(\"-Infinity\", js_value!(f32::INFINITY.neg())),\n        ]);\n    }\n\n    #[test]\n    fn arrays() {\n        run_test_actions([\n            TestAction::assert_with_op(\"[1, 2, 3]\", |value, context| {\n                let v = js_value!([1, 2, 3], context);\n                value.deep_strict_equals(&v, context).unwrap()\n            }),\n            TestAction::assert_with_op(\"[1, [2], 3]\", |value, context| {\n                value\n                    .deep_strict_equals(&js_value!([1, [2], 3], context), context)\n                    .unwrap()\n            }),\n            TestAction::assert_with_op(\"[1, [2], [], [[false]], 3]\", |value, context| {\n                value\n                    .deep_strict_equals(&js_value!([1, [2], [], [[false]], 3], context), context)\n                    .unwrap()\n            }),\n        ]);\n    }\n\n    #[test]\n    fn objects() {\n        run_test_actions([TestAction::assert_with_op(\n            r#\"({ \"hello\": 1, \"world\": null })\"#,\n            |value, context| {\n                value\n                    .deep_strict_equals(&js_value!({ \"hello\": 1, \"world\": () }, context), context)\n                    .unwrap()\n            },\n        )]);\n    }\n\n    #[test]\n    fn vars() {\n        run_test_actions([TestAction::assert_with_op(\n            r#\"({ \"hello\": 1, \"world\": null })\"#,\n            |value, context| {\n                let hello = JsValue::from(1);\n                let world = JsValue::null();\n\n                value\n                    .deep_strict_equals(\n                        &js_value!({ \"hello\": hello, \"world\": world }, context),\n                        context,\n                    )\n                    .expect(\"No error should happen.\")\n            },\n        )]);\n    }\n\n    #[test]\n    fn complex() {\n        run_test_actions([TestAction::assert_with_op(\n            r#\"({ \"hello\": [{ \"foo\": [1, []] }], \"world\": { \"bar\": false } })\"#,\n            |value, context| {\n                let bar = false;\n                let other = js_value!({\n                    \"hello\": [{ \"foo\": [1, []] }],\n                    // Allow comments\n                    \"world\": { \"bar\": bar },\n                }, context);\n\n                value\n                    .deep_strict_equals(&other, context)\n                    .expect(\"No error should happen here.\")\n            },\n        )]);\n    }\n\n    #[test]\n    fn context() {\n        run_test_actions([TestAction::assert_with_op(\n            r#\"({ \"hello\": [{ \"foo\": [1, []] }], \"world\": { \"bar\": false } })\"#,\n            |value, ctx| {\n                let bar = JsValue::from(false);\n                let other = js_value!({\n                    \"hello\": [{ \"foo\": [1, []] }],\n                    // Allow comments\n                    \"world\": { \"bar\": bar },\n                }, ctx);\n\n                value\n                    .deep_strict_equals(&other, ctx)\n                    .expect(\"No error should happen here.\")\n            },\n        )]);\n    }\n\n    #[test]\n    fn into_jsvalue() {\n        struct Test {\n            field_1: JsString,\n            field_2: u32,\n        }\n\n        impl TryIntoJs for Test {\n            fn try_into_js(&self, context: &mut Context) -> JsResult<JsValue> {\n                Ok(js_value!({\n                    \"fOne\": self.field_1.clone(),\n                    \"field2\": self.field_2\n                }, context))\n            }\n        }\n\n        let t = Test {\n            field_1: js_string!(\"Hello\"),\n            field_2: 42,\n        };\n        let context = &mut Context::default();\n        let o = t\n            .try_into_js(context)\n            .expect(\"Failed to convert value to js.\");\n\n        assert_eq!(\n            format!(\"{}\", o.display()),\n            \"{\\n    fOne: \\\"Hello\\\",\\n    field2: 42\\n}\"\n        );\n    }\n}\n"
  },
  {
    "path": "core/engine/src/value/type.rs",
    "content": "use super::JsValue;\n\n/// Possible types of values as defined at <https://tc39.es/ecma262/#sec-typeof-operator>.\n#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]\npub enum Type {\n    /// The \"undefined\" type.\n    Undefined,\n\n    /// The \"null\" type.\n    Null,\n\n    /// The \"boolean\" type.\n    Boolean,\n\n    /// The \"number\" type.\n    Number,\n\n    /// The \"string\" type.\n    String,\n\n    /// The \"symbol\" type.\n    Symbol,\n\n    /// The \"bigint\" type.\n    BigInt,\n\n    /// The \"object\" type.\n    Object,\n}\n\nimpl JsValue {\n    /// Get the type of a value\n    ///\n    /// This is the abstract operation Type(v), as described in\n    /// <https://tc39.es/ecma262/multipage/ecmascript-data-types-and-values.html#sec-ecmascript-language-types>.\n    ///\n    /// Check [`JsValue::type_of`] if you need to call the `typeof` operator.\n    #[must_use]\n    #[inline]\n    pub fn get_type(&self) -> Type {\n        self.0.get_type()\n    }\n}\n"
  },
  {
    "path": "core/engine/src/value/variant.rs",
    "content": "#[cfg(feature = \"annex-b\")]\nuse crate::builtins::is_html_dda::IsHTMLDDA;\nuse crate::{JsBigInt, JsObject, JsSymbol, JsValue};\nuse boa_engine::js_string;\nuse boa_string::JsString;\n\n/// A non-mutable variant of a `JsValue`.\n/// Represents either a primitive value ([`bool`], [`f64`], [`i32`]) or a reference\n/// to a heap allocated value ([`JsString`], [`JsSymbol`]).\n#[derive(Debug, PartialEq)]\npub enum JsVariant {\n    /// `null` - A null value, for when a value doesn't exist.\n    Null,\n    /// `undefined` - An undefined value, for when a field or index doesn't exist.\n    Undefined,\n    /// `boolean` - A `true` / `false` value, for if a certain criteria is met.\n    Boolean(bool),\n    /// `String` - A UTF-16 string, such as `\"Hello, world\"`.\n    String(JsString),\n    /// `Number` - A 64-bit floating point number, such as `3.1415` or `Infinity`.\n    /// This is the default representation of a number. If a number can be represented\n    /// as an integer, it will be stored as an `Integer` variant instead.\n    Float64(f64),\n    /// `Number` - A 32-bit integer, such as `42`.\n    Integer32(i32),\n    /// `BigInt` - holds any arbitrary large signed integer.\n    BigInt(JsBigInt),\n    /// `Object` - An object, such as `Math`, represented by a binary tree of string keys to Javascript values.\n    Object(JsObject),\n    /// `Symbol` - A Symbol Primitive type.\n    Symbol(JsSymbol),\n}\n\nimpl From<JsVariant> for JsValue {\n    fn from(value: JsVariant) -> Self {\n        match value {\n            JsVariant::Null => JsValue::null(),\n            JsVariant::Undefined => JsValue::undefined(),\n            JsVariant::Boolean(b) => JsValue::new(b),\n            JsVariant::String(s) => JsValue::new(s),\n            JsVariant::Float64(f) => JsValue::new(f),\n            JsVariant::Integer32(i) => JsValue::new(i),\n            JsVariant::BigInt(b) => JsValue::new(b),\n            JsVariant::Object(o) => JsValue::new(o),\n            JsVariant::Symbol(s) => JsValue::new(s),\n        }\n    }\n}\n\nimpl JsVariant {\n    /// Check if the variant is an `undefined` value.\n    #[inline]\n    #[must_use]\n    pub fn is_undefined(&self) -> bool {\n        matches!(self, JsVariant::Undefined)\n    }\n\n    /// `typeof` operator. Returns a string representing the type of the\n    /// given ECMA Value.\n    ///\n    /// More information:\n    /// - [ECMAScript reference][spec]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-typeof-operator\n    #[must_use]\n    pub fn type_of(&self) -> &'static str {\n        match self {\n            JsVariant::Float64(_) | JsVariant::Integer32(_) => \"number\",\n            JsVariant::String(_) => \"string\",\n            JsVariant::Boolean(_) => \"boolean\",\n            JsVariant::Symbol(_) => \"symbol\",\n            JsVariant::Null => \"object\",\n            JsVariant::Undefined => \"undefined\",\n            JsVariant::BigInt(_) => \"bigint\",\n            JsVariant::Object(object) => {\n                #[cfg(feature = \"annex-b\")]\n                if object.is::<IsHTMLDDA>() {\n                    return \"undefined\";\n                }\n                if object.is_callable() {\n                    \"function\"\n                } else {\n                    \"object\"\n                }\n            }\n        }\n    }\n\n    /// Same as [`JsVariant::type_of`], but returning a [`JsString`] instead.\n    #[must_use]\n    pub fn js_type_of(&self) -> JsString {\n        match self {\n            JsVariant::Float64(_) | JsVariant::Integer32(_) => js_string!(\"number\"),\n            JsVariant::String(_) => js_string!(\"string\"),\n            JsVariant::Boolean(_) => js_string!(\"boolean\"),\n            JsVariant::Symbol(_) => js_string!(\"symbol\"),\n            JsVariant::Null => js_string!(\"object\"),\n            JsVariant::Undefined => js_string!(\"undefined\"),\n            JsVariant::BigInt(_) => js_string!(\"bigint\"),\n            JsVariant::Object(object) => {\n                #[cfg(feature = \"annex-b\")]\n                if object.is::<IsHTMLDDA>() {\n                    return js_string!(\"undefined\");\n                }\n                if object.is_callable() {\n                    js_string!(\"function\")\n                } else {\n                    js_string!(\"object\")\n                }\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "core/engine/src/vm/call_frame/mod.rs",
    "content": "//! `CallFrame`\n//!\n//! This module will provides everything needed to implement the `CallFrame`\n\nuse super::ActiveRunnable;\nuse crate::{\n    JsValue,\n    builtins::iterable::IteratorRecord,\n    bytecompiler::Register,\n    environments::EnvironmentStack,\n    realm::Realm,\n    vm::{CodeBlock, SourcePath},\n};\nuse boa_ast::Position;\nuse boa_ast::scope::BindingLocator;\nuse boa_gc::{Finalize, Gc, Trace};\nuse boa_string::JsString;\nuse thin_vec::ThinVec;\n\nbitflags::bitflags! {\n    /// Flags associated with a [`CallFrame`].\n    #[derive(Debug, Default, Clone, Copy)]\n    pub(crate) struct CallFrameFlags: u8 {\n        /// When we return from this [`CallFrame`] to stop execution and\n        /// return from [`crate::Context::run()`], and leave the remaining [`CallFrame`]s unchanged.\n        const EXIT_EARLY = 0b0000_0001;\n\n        /// Was this [`CallFrame`] created from the `__construct__()` internal object method?\n        const CONSTRUCT = 0b0000_0010;\n\n        /// Does this [`CallFrame`] need to push registers on [`Vm::push_frame()`].\n        const REGISTERS_ALREADY_PUSHED = 0b0000_0100;\n\n        /// If the `this` value has been cached.\n        const THIS_VALUE_CACHED = 0b0000_1000;\n    }\n}\n\n#[derive(Debug, Clone, Eq, PartialEq)]\npub struct CallFrameLocation {\n    pub function_name: JsString,\n    pub path: SourcePath,\n    pub position: Option<Position>,\n}\n\n/// A `CallFrame` holds the state of a function call.\n#[derive(Clone, Debug, Finalize, Trace)]\npub struct CallFrame {\n    pub(crate) code_block: Gc<CodeBlock>,\n    pub(crate) pc: u32,\n    /// The frame pointer, points to the start of this frame's data in the stack\n    /// (i.e., the `this` value position).\n    pub(crate) fp: u32,\n    pub(crate) argument_count: u32,\n    pub(crate) env_fp: u32,\n\n    /// The register pointer, points to the start of this frame's registers on the stack.\n    pub(crate) rp: u32,\n\n    // Iterators and their `[[Done]]` flags that must be closed when an abrupt completion is thrown.\n    pub(crate) iterators: ThinVec<IteratorRecord>,\n\n    // The stack of bindings being updated.\n    // SAFETY: Nothing in `BindingLocator` requires tracing, so this is safe.\n    #[unsafe_ignore_trace]\n    pub(crate) binding_stack: ThinVec<BindingLocator>,\n\n    /// How many iterations a loop has done.\n    pub(crate) loop_iteration_count: u64,\n\n    /// `[[ScriptOrModule]]`\n    pub(crate) active_runnable: Option<ActiveRunnable>,\n\n    /// \\[\\[Environment\\]\\]\n    pub(crate) environments: EnvironmentStack,\n\n    /// \\[\\[Realm\\]\\]\n    pub(crate) realm: Realm,\n\n    // SAFETY: Nothing in `CallFrameFlags` requires tracing, so this is safe.\n    #[unsafe_ignore_trace]\n    pub(crate) flags: CallFrameFlags,\n}\n\n/// ---- `CallFrame` public API ----\nimpl CallFrame {\n    /// Retrieves the [`CodeBlock`] of this call frame.\n    #[inline]\n    #[must_use]\n    pub const fn code_block(&self) -> &Gc<CodeBlock> {\n        &self.code_block\n    }\n\n    /// Retrieves a tuple of `(`[`JsString`]`, `[`SourcePath`]`, `[`Position`]`)` to know the\n    /// location of the call frame.\n    #[inline]\n    #[must_use]\n    pub fn position(&self) -> CallFrameLocation {\n        let source_info = &self.code_block.source_info;\n        CallFrameLocation {\n            function_name: source_info.function_name().clone(),\n            path: source_info.map().path().clone(),\n            position: source_info.map().find(self.pc),\n        }\n    }\n}\n\n/// ---- `CallFrame` creation methods ----\nimpl CallFrame {\n    pub(crate) const FUNCTION_PROLOGUE: u32 = 2;\n    pub(crate) const UNDEFINED_REGISTER_INDEX: usize = 0;\n    pub(crate) const PROMISE_CAPABILITY_PROMISE_REGISTER_INDEX: usize = 1;\n    pub(crate) const PROMISE_CAPABILITY_RESOLVE_REGISTER_INDEX: usize = 2;\n    pub(crate) const PROMISE_CAPABILITY_REJECT_REGISTER_INDEX: usize = 3;\n    pub(crate) const ASYNC_GENERATOR_OBJECT_REGISTER_INDEX: usize = 4;\n\n    pub(crate) fn undefined_register() -> Register {\n        Register::persistent(Self::UNDEFINED_REGISTER_INDEX as u32)\n    }\n\n    pub(crate) fn promise_capability_promise_register() -> Register {\n        Register::persistent(Self::PROMISE_CAPABILITY_PROMISE_REGISTER_INDEX as u32)\n    }\n\n    pub(crate) fn promise_capability_resolve_register() -> Register {\n        Register::persistent(Self::PROMISE_CAPABILITY_RESOLVE_REGISTER_INDEX as u32)\n    }\n\n    pub(crate) fn promise_capability_reject_register() -> Register {\n        Register::persistent(Self::PROMISE_CAPABILITY_REJECT_REGISTER_INDEX as u32)\n    }\n\n    pub(crate) fn async_generator_object_register() -> Register {\n        Register::persistent(Self::ASYNC_GENERATOR_OBJECT_REGISTER_INDEX as u32)\n    }\n\n    /// Creates a new `CallFrame` with the provided `CodeBlock`.\n    pub(crate) fn new(\n        code_block: Gc<CodeBlock>,\n        active_runnable: Option<ActiveRunnable>,\n        environments: EnvironmentStack,\n        realm: Realm,\n    ) -> Self {\n        Self {\n            pc: 0,\n            fp: 0,\n            env_fp: 0,\n            argument_count: 0,\n            rp: 0,\n            iterators: ThinVec::new(),\n            binding_stack: ThinVec::new(),\n            code_block,\n            loop_iteration_count: 0,\n            active_runnable,\n            environments,\n            realm,\n            flags: CallFrameFlags::empty(),\n        }\n    }\n\n    /// Updates a `CallFrame`'s `argument_count` field with the value provided.\n    pub(crate) fn with_argument_count(mut self, count: u32) -> Self {\n        self.argument_count = count;\n        self\n    }\n\n    /// Updates a `CallFrame`'s `env_fp` field with the value provided.\n    pub(crate) fn with_env_fp(mut self, env_fp: u32) -> Self {\n        self.env_fp = env_fp;\n        self\n    }\n\n    /// Updates a `CallFrame`'s `flags` field with the value provided.\n    pub(crate) fn with_flags(mut self, flags: CallFrameFlags) -> Self {\n        self.flags = flags;\n        self\n    }\n\n    /// Returns the index of `this` in the stack.\n    pub(crate) fn this_index(&self) -> usize {\n        self.fp as usize\n    }\n\n    /// Returns the index of the function in the stack.\n    pub(crate) fn function_index(&self) -> usize {\n        self.fp as usize + 1\n    }\n\n    /// Returns the range of the arguments in the stack.\n    pub(crate) fn arguments_range(&self) -> std::ops::Range<usize> {\n        let start = self.fp as usize + Self::FUNCTION_PROLOGUE as usize;\n        start..start + self.argument_count as usize\n    }\n\n    /// Returns the frame pointer of this `CallFrame`.\n    pub(crate) fn frame_pointer(&self) -> usize {\n        self.fp as usize\n    }\n\n    /// Does this have the [`CallFrameFlags::EXIT_EARLY`] flag.\n    pub(crate) fn exit_early(&self) -> bool {\n        self.flags.contains(CallFrameFlags::EXIT_EARLY)\n    }\n\n    /// Set the [`CallFrameFlags::EXIT_EARLY`] flag.\n    pub(crate) fn set_exit_early(&mut self, early_exit: bool) {\n        self.flags.set(CallFrameFlags::EXIT_EARLY, early_exit);\n    }\n\n    /// Does this have the [`CallFrameFlags::CONSTRUCT`] flag.\n    pub(crate) fn construct(&self) -> bool {\n        self.flags.contains(CallFrameFlags::CONSTRUCT)\n    }\n\n    /// Does this [`CallFrame`] need to push registers on [`super::Vm::push_frame()`].\n    pub(crate) fn registers_already_pushed(&self) -> bool {\n        self.flags\n            .contains(CallFrameFlags::REGISTERS_ALREADY_PUSHED)\n    }\n\n    /// Does this [`CallFrame`] have a cached `this` value.\n    ///\n    /// The cached value is placed in the `this` position.\n    pub(crate) fn has_this_value_cached(&self) -> bool {\n        self.flags.contains(CallFrameFlags::THIS_VALUE_CACHED)\n    }\n}\n\n/// Indicates how a generator function that has been called/resumed should return.\n#[derive(Copy, Clone, Debug, PartialEq, Eq, Default)]\n#[repr(u8)]\n#[allow(missing_docs)]\npub enum GeneratorResumeKind {\n    #[default]\n    Normal = 0,\n    Throw,\n    Return,\n}\n\nimpl From<GeneratorResumeKind> for JsValue {\n    fn from(value: GeneratorResumeKind) -> Self {\n        Self::new(value as u8)\n    }\n}\n\nimpl JsValue {\n    /// Convert value to [`GeneratorResumeKind`].\n    ///\n    /// # Panics\n    ///\n    /// If not a integer type or not in the range `0..=2`.\n    #[track_caller]\n    pub(crate) fn to_generator_resume_kind(&self) -> GeneratorResumeKind {\n        if let Some(value) = self.as_i32() {\n            match value {\n                0 => return GeneratorResumeKind::Normal,\n                1 => return GeneratorResumeKind::Throw,\n                2 => return GeneratorResumeKind::Return,\n                _ => unreachable!(\"generator kind must be an integer between 1..=2, got {value}\"),\n            }\n        }\n\n        unreachable!(\"generator kind must be an integer type\")\n    }\n}\n"
  },
  {
    "path": "core/engine/src/vm/code_block.rs",
    "content": "//! `CodeBlock`\n//!\n//! This module is for the `CodeBlock` which implements a function representation in the VM\n\nuse crate::{\n    Context, JsBigInt, JsString, JsValue, SpannedSourceText,\n    builtins::{\n        OrdinaryObject,\n        function::{OrdinaryFunction, ThisMode},\n    },\n    object::JsObject,\n};\nuse bitflags::bitflags;\nuse boa_ast::scope::{BindingLocator, Scope};\nuse boa_gc::{Finalize, Gc, Trace, empty_trace};\nuse itertools::Itertools;\nuse std::{cell::Cell, fmt::Display, fmt::Write as _};\nuse thin_vec::ThinVec;\n\nuse super::{\n    InlineCache,\n    opcode::{Address, Bytecode, Instruction, InstructionIterator},\n    source_info::{SourceInfo, SourceMap, SourcePath},\n};\n\nbitflags! {\n    /// Flags for [`CodeBlock`].\n    #[derive(Clone, Copy, Debug, Finalize)]\n    pub(crate) struct CodeBlockFlags: u16 {\n        /// Is this function in strict mode.\n        const STRICT = 0b0000_0001;\n\n        /// Indicates if the function is an expression and has a binding identifier.\n        const HAS_BINDING_IDENTIFIER = 0b0000_0010;\n\n        /// The `[[IsClassConstructor]]` internal slot.\n        const IS_CLASS_CONSTRUCTOR = 0b0000_0100;\n\n        /// The `[[ClassFieldInitializerName]]` internal slot.\n        const IN_CLASS_FIELD_INITIALIZER = 0b0000_1000;\n\n        /// `[[ConstructorKind]]`\n        const IS_DERIVED_CONSTRUCTOR = 0b0001_0000;\n\n        const IS_ASYNC = 0b0010_0000;\n        const IS_GENERATOR = 0b0100_0000;\n\n        /// Arrow and method functions don't have `\"prototype\"` property.\n        const HAS_PROTOTYPE_PROPERTY = 0b1000_0000;\n\n        /// If the function requires a function scope.\n        const HAS_FUNCTION_SCOPE = 0b1_0000_0000;\n\n        /// Trace instruction execution to `stdout`.\n        #[cfg(feature = \"trace\")]\n        const TRACEABLE = 0b1000_0000_0000_0000;\n    }\n}\n\nimpl CodeBlockFlags {\n    /// Check if the [`CodeBlock`] has a function scope.\n    #[must_use]\n    pub(crate) fn has_function_scope(self) -> bool {\n        self.contains(Self::HAS_FUNCTION_SCOPE)\n    }\n}\n\n// SAFETY: Nothing in CodeBlockFlags needs tracing, so this is safe.\nunsafe impl Trace for CodeBlockFlags {\n    empty_trace!();\n}\n\n/// This represents a range in the code that handles exception throws.\n///\n/// When a throw happens, we search for handler in the [`CodeBlock`] using\n/// the [`CodeBlock::find_handler()`] method.\n///\n/// If any exception happens and gets caught by this handler, the `pc` will be set to `end` of the\n/// [`Handler`] and remove any environments or stack values that where pushed after the handler.\n#[derive(Debug, Clone, Copy)]\npub(crate) struct Handler {\n    pub(crate) start: Address,\n    pub(crate) end: Address,\n    pub(crate) environment_count: u32,\n}\n\nimpl Handler {\n    /// Get the handler address.\n    pub(crate) const fn handler(&self) -> Address {\n        self.end\n    }\n\n    /// Check if the provided `pc` is contained in the handler range.\n    pub(crate) const fn contains(&self, pc: u32) -> bool {\n        pc < self.end.as_u32() && pc >= self.start.as_u32()\n    }\n}\n\n#[derive(Clone, Debug, Trace, Finalize)]\npub(crate) enum Constant {\n    /// Property field names and private names `[[description]]`s.\n    String(JsString),\n    Function(Gc<CodeBlock>),\n    BigInt(#[unsafe_ignore_trace] JsBigInt),\n\n    /// Declarative or function scope.\n    // Safety: Nothing in `Scope` needs tracing, so this is safe.\n    Scope(#[unsafe_ignore_trace] Scope),\n}\n\n/// Binding information about a global function.\n#[derive(Copy, Clone, Debug, Trace, Finalize)]\n#[boa_gc(empty_trace)]\npub(crate) struct GlobalFunctionBinding {\n    /// The index of the global function's name in the constants array.\n    pub(crate) name_index: u32,\n    /// The index of the global function in the constants array\n    pub(crate) function_index: u32,\n}\n\n/// The internal representation of a JavaScript function.\n///\n/// A `CodeBlock` is generated for each function compiled by the\n/// [`ByteCompiler`](crate::bytecompiler::ByteCompiler). It stores the bytecode and the other\n/// attributes of the function.\n#[derive(Clone, Debug, Trace, Finalize)]\npub struct CodeBlock {\n    #[unsafe_ignore_trace]\n    pub(crate) flags: Cell<CodeBlockFlags>,\n\n    /// The number of arguments expected.\n    pub(crate) length: u32,\n\n    pub(crate) parameter_length: u32,\n\n    pub(crate) register_count: u32,\n\n    /// `[[ThisMode]]`\n    pub(crate) this_mode: ThisMode,\n\n    /// Used for constructing a `MappedArguments` object.\n    #[unsafe_ignore_trace]\n    pub(crate) mapped_arguments_binding_indices: ThinVec<Option<u32>>,\n\n    /// Bytecode\n    #[unsafe_ignore_trace]\n    pub(crate) bytecode: Bytecode,\n\n    pub(crate) constants: ThinVec<Constant>,\n\n    /// Locators for all bindings in the codeblock.\n    #[unsafe_ignore_trace]\n    pub(crate) bindings: Box<[BindingLocator]>,\n\n    /// Exception [`Handler`]s.\n    #[unsafe_ignore_trace]\n    pub(crate) handlers: ThinVec<Handler>,\n\n    /// inline caching\n    pub(crate) ic: Box<[InlineCache]>,\n\n    /// Bytecode to source code mapping.\n    pub(crate) source_info: SourceInfo,\n\n    pub(crate) global_lexs: Box<[u32]>,\n    pub(crate) global_fns: Box<[GlobalFunctionBinding]>,\n    pub(crate) global_vars: Box<[u32]>,\n\n    // Used for identifying anonymous functions in compiled output and call frames.\n    pub(crate) debug_id: u64,\n\n    #[cfg(feature = \"trace\")]\n    #[unsafe_ignore_trace]\n    pub(crate) traced: Cell<bool>,\n}\n\n/// ---- `CodeBlock` public API ----\nimpl CodeBlock {\n    /// Creates a new `CodeBlock`.\n    #[must_use]\n    pub fn new(name: JsString, length: u32, strict: bool) -> Self {\n        let mut flags = CodeBlockFlags::empty();\n        flags.set(CodeBlockFlags::STRICT, strict);\n        Self {\n            bytecode: Bytecode::default(),\n            constants: ThinVec::default(),\n            bindings: Box::default(),\n            flags: Cell::new(flags),\n            length,\n            register_count: 0,\n            this_mode: ThisMode::Global,\n            mapped_arguments_binding_indices: ThinVec::new(),\n            parameter_length: 0,\n            handlers: ThinVec::default(),\n            ic: Box::default(),\n            source_info: SourceInfo::new(\n                SourceMap::new(Box::default(), SourcePath::None),\n                name,\n                SpannedSourceText::new_empty(),\n            ),\n            global_lexs: Box::default(),\n            global_fns: Box::default(),\n            global_vars: Box::default(),\n            debug_id: CodeBlock::get_next_codeblock_id(),\n            #[cfg(feature = \"trace\")]\n            traced: Cell::new(false),\n        }\n    }\n\n    /// Retrieves the name associated with this code block.\n    #[must_use]\n    pub fn name(&self) -> &JsString {\n        self.source_info.function_name()\n    }\n\n    /// Retrieves the path of this code block.\n    #[must_use]\n    pub fn path(&self) -> &SourcePath {\n        self.source_info.map().path()\n    }\n\n    /// Check if the function is traced.\n    #[cfg(feature = \"trace\")]\n    pub(crate) fn traceable(&self) -> bool {\n        self.flags.get().contains(CodeBlockFlags::TRACEABLE)\n    }\n    /// Enable or disable instruction tracing to `stdout`.\n    #[cfg(feature = \"trace\")]\n    #[inline]\n    pub fn set_traceable(&self, value: bool) {\n        let mut flags = self.flags.get();\n        flags.set(CodeBlockFlags::TRACEABLE, value);\n        self.flags.set(flags);\n    }\n\n    /// Check if the function is a class constructor.\n    pub(crate) fn is_class_constructor(&self) -> bool {\n        self.flags\n            .get()\n            .contains(CodeBlockFlags::IS_CLASS_CONSTRUCTOR)\n    }\n\n    /// Check if the function is in strict mode.\n    pub(crate) fn strict(&self) -> bool {\n        self.flags.get().contains(CodeBlockFlags::STRICT)\n    }\n\n    /// Indicates if the function is an expression and has a binding identifier.\n    pub(crate) fn has_binding_identifier(&self) -> bool {\n        self.flags\n            .get()\n            .contains(CodeBlockFlags::HAS_BINDING_IDENTIFIER)\n    }\n\n    /// Does this function have the `[[ClassFieldInitializerName]]` internal slot set to non-empty value.\n    pub(crate) fn in_class_field_initializer(&self) -> bool {\n        self.flags\n            .get()\n            .contains(CodeBlockFlags::IN_CLASS_FIELD_INITIALIZER)\n    }\n\n    /// Returns true if this function is a derived constructor.\n    pub(crate) fn is_derived_constructor(&self) -> bool {\n        self.flags\n            .get()\n            .contains(CodeBlockFlags::IS_DERIVED_CONSTRUCTOR)\n    }\n\n    /// Returns true if this function an async function.\n    pub(crate) fn is_async(&self) -> bool {\n        self.flags.get().contains(CodeBlockFlags::IS_ASYNC)\n    }\n\n    /// Returns true if this function an generator function.\n    pub(crate) fn is_generator(&self) -> bool {\n        self.flags.get().contains(CodeBlockFlags::IS_GENERATOR)\n    }\n\n    /// Returns true if this function a async generator function.\n    pub(crate) fn is_async_generator(&self) -> bool {\n        self.flags\n            .get()\n            .contains(CodeBlockFlags::IS_ASYNC | CodeBlockFlags::IS_GENERATOR)\n    }\n\n    /// Returns true if this function an async function.\n    pub(crate) fn is_ordinary(&self) -> bool {\n        !self.is_async() && !self.is_generator()\n    }\n\n    /// Returns true if this function has the `\"prototype\"` property when function object is created.\n    pub(crate) fn has_prototype_property(&self) -> bool {\n        self.flags\n            .get()\n            .contains(CodeBlockFlags::HAS_PROTOTYPE_PROPERTY)\n    }\n\n    /// Returns true if this function requires a function scope.\n    pub(crate) fn has_function_scope(&self) -> bool {\n        self.flags.get().has_function_scope()\n    }\n\n    /// Find exception [`Handler`] in the code block given the current program counter (`pc`).\n    #[inline]\n    pub(crate) fn find_handler(&self, pc: u32) -> Option<(usize, &Handler)> {\n        self.handlers\n            .iter()\n            .enumerate()\n            .rev()\n            .find(|(_, handler)| handler.contains(pc))\n    }\n\n    /// Get the [`JsString`] constant from the [`CodeBlock`].\n    ///\n    /// # Panics\n    ///\n    /// If the type of the [`Constant`] is not [`Constant::String`].\n    /// Or `index` is greater or equal to length of `constants`.\n    pub(crate) fn constant_string(&self, index: usize) -> JsString {\n        if let Some(Constant::String(value)) = self.constants.get(index) {\n            return value.clone();\n        }\n\n        panic!(\"expected string constant at index {index}\")\n    }\n\n    /// Get the function ([`Gc<CodeBlock>`]) constant from the [`CodeBlock`].\n    ///\n    /// # Panics\n    ///\n    /// If the type of the [`Constant`] is not [`Constant::Function`].\n    /// Or `index` is greater or equal to length of `constants`.\n    pub(crate) fn constant_function(&self, index: usize) -> Gc<Self> {\n        if let Some(Constant::Function(value)) = self.constants.get(index) {\n            return value.clone();\n        }\n\n        panic!(\"expected function constant at index {index}\")\n    }\n\n    /// Get the [`Scope`] constant from the [`CodeBlock`].\n    ///\n    /// # Panics\n    ///\n    /// If the type of the [`Constant`] is not [`Constant::Scope`].\n    /// Or `index` is greater or equal to length of `constants`.\n    pub(crate) fn constant_scope(&self, index: usize) -> Scope {\n        if let Some(Constant::Scope(value)) = self.constants.get(index) {\n            return value.clone();\n        }\n\n        panic!(\"expected scope constant at index {index}\")\n    }\n\n    pub(crate) fn source_info(&self) -> &SourceInfo {\n        &self.source_info\n    }\n\n    pub(crate) fn get_next_codeblock_id() -> u64 {\n        thread_local! {\n            static CODEBLOCK_ID_COUNTER: Cell<u64> = const { Cell::new(0) };\n        }\n\n        CODEBLOCK_ID_COUNTER.with(|c| {\n            let id = c.get();\n            c.set(id + 1);\n            id\n        })\n    }\n}\n\n/// ---- `CodeBlock` private API ----\nimpl CodeBlock {\n    /// Get the operands after the `Opcode` pointed to by `pc` as a `String`.\n    /// Modifies the `pc` to point to the next instruction.\n    ///\n    /// Returns an empty `String` if no operands are present.\n    pub(crate) fn instruction_operands(&self, instruction: &Instruction) -> String {\n        match instruction {\n            Instruction::SetRegisterFromAccumulator { dst }\n            | Instruction::PopIntoRegister { dst }\n            | Instruction::StoreZero { dst }\n            | Instruction::StoreOne { dst }\n            | Instruction::StoreNan { dst }\n            | Instruction::StorePositiveInfinity { dst }\n            | Instruction::StoreNegativeInfinity { dst }\n            | Instruction::StoreNull { dst }\n            | Instruction::StoreTrue { dst }\n            | Instruction::StoreFalse { dst }\n            | Instruction::StoreUndefined { dst }\n            | Instruction::Exception { dst }\n            | Instruction::This { dst }\n            | Instruction::NewTarget { dst }\n            | Instruction::ImportMeta { dst }\n            | Instruction::CreateMappedArgumentsObject { dst }\n            | Instruction::CreateUnmappedArgumentsObject { dst }\n            | Instruction::RestParameterInit { dst }\n            | Instruction::StoreNewArray { dst } => format!(\"dst:{dst}\"),\n            Instruction::Add { lhs, rhs, dst }\n            | Instruction::Sub { lhs, rhs, dst }\n            | Instruction::Div { lhs, rhs, dst }\n            | Instruction::Mul { lhs, rhs, dst }\n            | Instruction::Mod { lhs, rhs, dst }\n            | Instruction::Pow { lhs, rhs, dst }\n            | Instruction::ShiftRight { lhs, rhs, dst }\n            | Instruction::ShiftLeft { lhs, rhs, dst }\n            | Instruction::UnsignedShiftRight { lhs, rhs, dst }\n            | Instruction::BitOr { lhs, rhs, dst }\n            | Instruction::BitAnd { lhs, rhs, dst }\n            | Instruction::BitXor { lhs, rhs, dst }\n            | Instruction::In { lhs, rhs, dst }\n            | Instruction::Eq { lhs, rhs, dst }\n            | Instruction::StrictEq { lhs, rhs, dst }\n            | Instruction::NotEq { lhs, rhs, dst }\n            | Instruction::StrictNotEq { lhs, rhs, dst }\n            | Instruction::GreaterThan { lhs, rhs, dst }\n            | Instruction::GreaterThanOrEq { lhs, rhs, dst }\n            | Instruction::LessThan { lhs, rhs, dst }\n            | Instruction::LessThanOrEq { lhs, rhs, dst }\n            | Instruction::InstanceOf { lhs, rhs, dst } => {\n                format!(\"lhs:{lhs}, rhs:{rhs}, dst:{dst}\")\n            }\n            Instruction::InPrivate { dst, index, rhs } => {\n                format!(\"rhs:{rhs}, index:{index}, dst:{dst}\")\n            }\n            Instruction::Inc { src, dst }\n            | Instruction::Dec { src, dst }\n            | Instruction::Move { src, dst }\n            | Instruction::ToPropertyKey { src, dst } => {\n                format!(\"src:{src}, dst:{dst}\")\n            }\n            Instruction::SetFunctionName {\n                function,\n                name,\n                prefix,\n            } => {\n                format!(\n                    \"function:{function}, name:{name}, prefix:{}\",\n                    match u32::from(*prefix) {\n                        0 => \"prefix:\",\n                        1 => \"prefix: get\",\n                        2 => \"prefix: set\",\n                        _ => unreachable!(),\n                    }\n                )\n            }\n            Instruction::StoreInt8 { value, dst } => {\n                format!(\"value:{value}, dst:{dst}\")\n            }\n            Instruction::StoreInt16 { value, dst } => {\n                format!(\"value:{value}, dst:{dst}\")\n            }\n            Instruction::StoreInt32 { value, dst } => {\n                format!(\"value:{value}, dst:{dst}\")\n            }\n            Instruction::StoreFloat { value, dst } => {\n                format!(\"value:{value}, dst:{dst}\")\n            }\n            Instruction::StoreDouble { value, dst } => {\n                format!(\"value:{value}, dst:{dst}\")\n            }\n            Instruction::StoreLiteral { index, dst }\n            | Instruction::ThisForObjectEnvironmentName { index, dst }\n            | Instruction::GetFunction { index, dst }\n            | Instruction::GetArgument { index, dst } => {\n                format!(\"index:{index}, dst:{dst}\")\n            }\n            Instruction::ThrowNewTypeError { message }\n            | Instruction::ThrowNewReferenceError { message } => format!(\"message:{message}\"),\n            Instruction::StoreRegexp {\n                pattern_index,\n                flags_index,\n                dst,\n            } => {\n                format!(\"pattern:{pattern_index}, flags:{flags_index}, dst:{dst}\")\n            }\n            Instruction::Jump { address } => format!(\"address:{address}\"),\n            Instruction::JumpIfTrue { address, value }\n            | Instruction::JumpIfFalse { address, value }\n            | Instruction::JumpIfNotUndefined { address, value }\n            | Instruction::JumpIfNullOrUndefined { address, value }\n            | Instruction::LogicalAnd { address, value }\n            | Instruction::LogicalOr { address, value }\n            | Instruction::Coalesce { address, value } => {\n                format!(\"value:{value}, address:{address}\")\n            }\n            Instruction::JumpIfNotLessThan { address, lhs, rhs }\n            | Instruction::JumpIfNotLessThanOrEqual { address, lhs, rhs }\n            | Instruction::JumpIfNotGreaterThan { address, lhs, rhs }\n            | Instruction::JumpIfNotGreaterThanOrEqual { address, lhs, rhs }\n            | Instruction::JumpIfNotEqual { address, lhs, rhs } => {\n                format!(\"lhs:{lhs}, rhs:{rhs}, address:{address}\")\n            }\n            Instruction::Case {\n                address,\n                value,\n                condition,\n            } => {\n                format!(\"value:{value}, condition:{condition}, address:{address}\")\n            }\n            Instruction::CallEval {\n                argument_count,\n                scope_index,\n            } => {\n                format!(\"argument_count:{argument_count}, scope_index:{scope_index}\")\n            }\n            Instruction::CallEvalSpread { scope_index }\n            | Instruction::PushScope { scope_index } => {\n                format!(\"scope_index:{scope_index}\")\n            }\n            Instruction::Call { argument_count }\n            | Instruction::New { argument_count }\n            | Instruction::SuperCall { argument_count } => {\n                format!(\"argument_count:{argument_count}\")\n            }\n            Instruction::DefVar { binding_index } | Instruction::GetLocator { binding_index } => {\n                format!(\"binding_index:{binding_index}\")\n            }\n            Instruction::DefInitVar { src, binding_index }\n            | Instruction::PutLexicalValue { src, binding_index }\n            | Instruction::SetName { src, binding_index } => {\n                format!(\"src:{src}, binding_index:{binding_index}\")\n            }\n            Instruction::GetName { dst, binding_index }\n            | Instruction::GetNameAndLocator { dst, binding_index }\n            | Instruction::GetNameOrUndefined { dst, binding_index }\n            | Instruction::DeleteName { dst, binding_index } => {\n                format!(\"dst:{dst}, binding_index:{binding_index}\")\n            }\n            Instruction::GetNameGlobal {\n                dst,\n                binding_index,\n                ic_index,\n            } => {\n                format!(\"dst:{dst}, binding_index:{binding_index}, ic_index:{ic_index}\")\n            }\n            Instruction::DefineOwnPropertyByName {\n                object,\n                value,\n                name_index,\n            }\n            | Instruction::SetPropertyGetterByName {\n                object,\n                value,\n                name_index,\n            }\n            | Instruction::SetPropertySetterByName {\n                object,\n                value,\n                name_index,\n            }\n            | Instruction::DefinePrivateField {\n                object,\n                value,\n                name_index,\n            }\n            | Instruction::SetPrivateMethod {\n                object,\n                value,\n                name_index,\n            }\n            | Instruction::SetPrivateSetter {\n                object,\n                value,\n                name_index,\n            }\n            | Instruction::SetPrivateGetter {\n                object,\n                value,\n                name_index,\n            }\n            | Instruction::PushClassPrivateGetter {\n                object,\n                value,\n                name_index,\n            }\n            | Instruction::PushClassPrivateSetter {\n                object,\n                value,\n                name_index,\n            }\n            | Instruction::DefineClassStaticMethodByName {\n                object,\n                value,\n                name_index,\n            }\n            | Instruction::DefineClassMethodByName {\n                object,\n                value,\n                name_index,\n            }\n            | Instruction::DefineClassStaticGetterByName {\n                object,\n                value,\n                name_index,\n            }\n            | Instruction::DefineClassGetterByName {\n                object,\n                value,\n                name_index,\n            }\n            | Instruction::DefineClassStaticSetterByName {\n                object,\n                value,\n                name_index,\n            }\n            | Instruction::DefineClassSetterByName {\n                object,\n                value,\n                name_index,\n            }\n            | Instruction::SetPrivateField {\n                object,\n                value,\n                name_index,\n            }\n            | Instruction::PushClassFieldPrivate {\n                object,\n                value,\n                name_index,\n            } => {\n                format!(\"object:{object}, value:{value}, name_index:{name_index}\")\n            }\n            Instruction::GetPrivateField {\n                dst,\n                object,\n                name_index,\n            } => {\n                format!(\"dst:{dst}, object:{object}, name_index:{name_index}\")\n            }\n            Instruction::PushClassPrivateMethod {\n                object,\n                proto,\n                value,\n                name_index,\n            } => {\n                format!(\"object:{object}, proto:{proto}, value:{value}, name_index:{name_index}\")\n            }\n            Instruction::ThrowMutateImmutable { index } => {\n                format!(\"index:{index}\")\n            }\n            Instruction::DeletePropertyByName { object, name_index }\n            | Instruction::GetMethod { object, name_index } => {\n                format!(\"object:{object}, name_index:{name_index}\")\n            }\n            Instruction::GetLengthProperty {\n                dst,\n                value,\n                ic_index,\n            }\n            | Instruction::GetPropertyByName {\n                dst,\n                value,\n                ic_index,\n            } => {\n                let ic = &self.ic[u32::from(*ic_index) as usize];\n                format!(\"dst:{dst}, value:{value}, ic:{ic}\",)\n            }\n            Instruction::GetPropertyByNameWithThis {\n                dst,\n                receiver,\n                value,\n                ic_index,\n            } => {\n                let ic = &self.ic[u32::from(*ic_index) as usize];\n                format!(\"dst:{dst}, receiver:{receiver}, value:{value}, ic:{ic}\",)\n            }\n            Instruction::SetPropertyByName {\n                value,\n                object,\n                ic_index,\n            } => {\n                let ic = &self.ic[u32::from(*ic_index) as usize];\n                format!(\"object:{object}, value:{value}, ic:{ic}\",)\n            }\n            Instruction::SetPropertyByNameWithThis {\n                value,\n                receiver,\n                object,\n                ic_index,\n            } => {\n                let ic = &self.ic[u32::from(*ic_index) as usize];\n                format!(\"object:{object}, receiver:{receiver}, value:{value}, ic:{ic}\")\n            }\n            Instruction::GetPropertyByValue {\n                dst,\n                key,\n                receiver,\n                object,\n            }\n            | Instruction::GetPropertyByValuePush {\n                dst,\n                key,\n                receiver,\n                object,\n            } => {\n                format!(\"dst:{dst}, object:{object}, receiver:{receiver}, key:{key}\")\n            }\n            Instruction::SetPropertyByValue {\n                value,\n                key,\n                receiver,\n                object,\n            } => {\n                format!(\"object:{object}, receiver:{receiver}, key:{key}, value:{value}\")\n            }\n            Instruction::DefineOwnPropertyByValue { value, key, object }\n            | Instruction::DefineClassStaticMethodByValue { value, key, object }\n            | Instruction::DefineClassMethodByValue { value, key, object }\n            | Instruction::SetPropertyGetterByValue { value, key, object }\n            | Instruction::DefineClassStaticGetterByValue { value, key, object }\n            | Instruction::DefineClassGetterByValue { value, key, object }\n            | Instruction::SetPropertySetterByValue { value, key, object }\n            | Instruction::DefineClassStaticSetterByValue { value, key, object }\n            | Instruction::DefineClassSetterByValue { value, key, object } => {\n                format!(\"object:{object}, key:{key}, value:{value}\")\n            }\n            Instruction::DeletePropertyByValue { key, object } => {\n                format!(\"object:{object}, key:{key}\")\n            }\n            Instruction::CreateIteratorResult { value, done } => {\n                format!(\"value:{value}, done:{done}\")\n            }\n            Instruction::StoreClassPrototype {\n                dst,\n                class,\n                superclass,\n            } => {\n                format!(\"dst:{dst}, class:{class}, superclass:{superclass}\")\n            }\n            Instruction::SetClassPrototype {\n                dst,\n                prototype,\n                class,\n            } => {\n                format!(\"dst:{dst}, prototype:{prototype}, class:{class}\")\n            }\n            Instruction::SetHomeObject { function, home } => {\n                format!(\"function:{function}, home:{home}\")\n            }\n            Instruction::GetHomeObject { function } => {\n                format!(\"function:{function}\")\n            }\n            Instruction::SetPrototype { object, prototype } => {\n                format!(\"object:{object}, prototype:{prototype}\")\n            }\n            Instruction::GetPrototype { object } => {\n                format!(\"object:{object}\")\n            }\n            Instruction::PushValueToArray { value, array } => {\n                format!(\"value:{value}, array:{array}\")\n            }\n            Instruction::PushElisionToArray { array }\n            | Instruction::PushIteratorToArray { array } => {\n                format!(\"array:{array}\")\n            }\n            Instruction::TypeOf { value }\n            | Instruction::LogicalNot { value }\n            | Instruction::Pos { value }\n            | Instruction::Neg { value }\n            | Instruction::IsObject { value }\n            | Instruction::BindThisValue { value }\n            | Instruction::BitNot { value } => {\n                format!(\"value:{value}\")\n            }\n            Instruction::ImportCall {\n                specifier,\n                options,\n                phase,\n            } => {\n                let phase_str = match u32::from(*phase) {\n                    0 => \"evaluation\",\n                    1 => \"defer\",\n                    2 => \"source\",\n                    _ => \"unknown\",\n                };\n                format!(\"specifier:{specifier}, options:{options}, phase:{phase_str}\")\n            }\n            Instruction::PushClassField {\n                object,\n                name,\n                value,\n                is_anonymous_function,\n            } => {\n                format!(\n                    \"object:{object}, value:{value}, name:{name}, is_anonymous_function:{is_anonymous_function}\"\n                )\n            }\n            Instruction::MaybeException {\n                has_exception,\n                exception,\n            } => {\n                format!(\"has_exception:{has_exception}, exception:{exception}\")\n            }\n            Instruction::SetAccumulator { src }\n            | Instruction::PushFromRegister { src }\n            | Instruction::Throw { src }\n            | Instruction::SetNameByLocator { src }\n            | Instruction::PushObjectEnvironment { src }\n            | Instruction::CreateForInIterator { src }\n            | Instruction::GetIterator { src }\n            | Instruction::GetAsyncIterator { src }\n            | Instruction::ValueNotNullOrUndefined { src }\n            | Instruction::GeneratorYield { src }\n            | Instruction::AsyncGeneratorYield { src }\n            | Instruction::Await { src } => {\n                format!(\"src:{src}\")\n            }\n            Instruction::IteratorPush { iterator, next }\n            | Instruction::IteratorPop { iterator, next } => {\n                format!(\"iterator:{iterator}, next:{next}\")\n            }\n            Instruction::IteratorUpdateResult { result } => {\n                format!(\"result:{result}\")\n            }\n            Instruction::IteratorDone { dst }\n            | Instruction::IteratorValue { dst }\n            | Instruction::IteratorResult { dst }\n            | Instruction::IteratorToArray { dst }\n            | Instruction::IteratorStackEmpty { dst }\n            | Instruction::StoreEmptyObject { dst } => {\n                format!(\"dst:{dst}\")\n            }\n            Instruction::IteratorFinishAsyncNext { resume_kind, value } => {\n                format!(\"resume_kind:{resume_kind}, value:{value}\")\n            }\n            Instruction::IteratorReturn { value, called } => {\n                format!(\"value:{value}, called:{called}\")\n            }\n            Instruction::PushPrivateEnvironment {\n                class,\n                name_indices,\n            } => {\n                format!(\"class:{class}, names:{name_indices:?}\")\n            }\n            Instruction::TemplateLookup { address, site, dst } => {\n                format!(\"address:{address}, site:{site}, dst:{dst}\")\n            }\n            Instruction::JumpTable { index, addresses } => {\n                format!(\n                    \"index:{index}, jump_table:({})\",\n                    addresses.iter().format(\", \")\n                )\n            }\n            Instruction::ConcatToString { dst, values } => {\n                format!(\"dst:{dst}, values:{values:?}\")\n            }\n            Instruction::CopyDataProperties {\n                object,\n                source,\n                excluded_keys,\n            } => {\n                format!(\"object:{object}, source:{source}, excluded_keys:{excluded_keys:?}\")\n            }\n            Instruction::TemplateCreate { site, dst, values } => {\n                format!(\"site:{site}, dst:{dst}, values:{values:?}\")\n            }\n            Instruction::GetFunctionObject { function_object } => {\n                format!(\"function_object:{function_object}\")\n            }\n            Instruction::Pop\n            | Instruction::DeleteSuperThrow\n            | Instruction::ReThrow\n            | Instruction::CheckReturn\n            | Instruction::Return\n            | Instruction::AsyncGeneratorClose\n            | Instruction::CreatePromiseCapability\n            | Instruction::PopEnvironment\n            | Instruction::IncrementLoopIteration\n            | Instruction::IteratorNext\n            | Instruction::SuperCallDerived\n            | Instruction::CallSpread\n            | Instruction::NewSpread\n            | Instruction::SuperCallSpread\n            | Instruction::PopPrivateEnvironment\n            | Instruction::Generator\n            | Instruction::AsyncGenerator => String::new(),\n            Instruction::Reserved1\n            | Instruction::Reserved2\n            | Instruction::Reserved3\n            | Instruction::Reserved4\n            | Instruction::Reserved5\n            | Instruction::Reserved6\n            | Instruction::Reserved7\n            | Instruction::Reserved8\n            | Instruction::Reserved9\n            | Instruction::Reserved10\n            | Instruction::Reserved11\n            | Instruction::Reserved12\n            | Instruction::Reserved13\n            | Instruction::Reserved14\n            | Instruction::Reserved15\n            | Instruction::Reserved16\n            | Instruction::Reserved17\n            | Instruction::Reserved18\n            | Instruction::Reserved19\n            | Instruction::Reserved20\n            | Instruction::Reserved21\n            | Instruction::Reserved22\n            | Instruction::Reserved23\n            | Instruction::Reserved24\n            | Instruction::Reserved25\n            | Instruction::Reserved26\n            | Instruction::Reserved27\n            | Instruction::Reserved28\n            | Instruction::Reserved29\n            | Instruction::Reserved30\n            | Instruction::Reserved31\n            | Instruction::Reserved32\n            | Instruction::Reserved33\n            | Instruction::Reserved34\n            | Instruction::Reserved35\n            | Instruction::Reserved36\n            | Instruction::Reserved37\n            | Instruction::Reserved38\n            | Instruction::Reserved39\n            | Instruction::Reserved40\n            | Instruction::Reserved41\n            | Instruction::Reserved42\n            | Instruction::Reserved43\n            | Instruction::Reserved44\n            | Instruction::Reserved45\n            | Instruction::Reserved46\n            | Instruction::Reserved47\n            | Instruction::Reserved48\n            | Instruction::Reserved49\n            | Instruction::Reserved50\n            | Instruction::Reserved51\n            | Instruction::Reserved52\n            | Instruction::Reserved53\n            | Instruction::Reserved54\n            | Instruction::Reserved55\n            | Instruction::Reserved56\n            | Instruction::Reserved57\n            | Instruction::Reserved58\n            | Instruction::Reserved59\n            | Instruction::Reserved60 => unreachable!(\"Reserved opcodes are unreachable\"),\n        }\n    }\n}\n\nimpl Display for CodeBlock {\n    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {\n        let name = self.name();\n        writeln!(\n            f,\n            \"{:-^80}\",\n            format!(\n                \" Compiled Output: {} \",\n                if name.is_empty() {\n                    format!(\"[anon#{}]\", self.debug_id)\n                } else {\n                    format!(\"'{}'\", name.to_std_string_escaped())\n                }\n            ),\n        )?;\n        writeln!(\n            f,\n            \"Location     Handler      Opcode                            Operands\"\n        )?;\n        let mut iterator = InstructionIterator::new(&self.bytecode);\n        while let Some((instruction_start_pc, opcode, instruction)) = iterator.next() {\n            let opcode = opcode.as_str();\n            let operands = self.instruction_operands(&instruction);\n            let pc = iterator.pc();\n            let handler = if let Some((i, handler)) = self.find_handler(instruction_start_pc as u32)\n            {\n                let border_char = if instruction_start_pc as u32 == u32::from(handler.start) {\n                    '>'\n                } else if pc as u32 == u32::from(handler.end) {\n                    '<'\n                } else {\n                    ' '\n                };\n                format!(\"{border_char}{i:2}: {}\", handler.handler())\n            } else {\n                \"           \".to_string()\n            };\n            writeln!(\n                f,\n                \"  {instruction_start_pc:>06x}    {handler}     {opcode:<32}  {operands}\",\n            )?;\n        }\n        writeln!(\n            f,\n            \"\\nRegister Count: {}, Flags: {:?}\",\n            self.register_count,\n            self.flags.get()\n        )?;\n        f.write_str(\"Constants:\")?;\n        if self.constants.is_empty() {\n            f.write_str(\" <empty>\\n\")?;\n        } else {\n            f.write_char('\\n')?;\n            for (i, value) in self.constants.iter().enumerate() {\n                write!(f, \"    {i:04}: \")?;\n                match value {\n                    Constant::String(v) => {\n                        writeln!(\n                            f,\n                            \"[STRING] \\\"{}\\\"\",\n                            v.to_std_string_escaped().escape_debug()\n                        )?;\n                    }\n                    Constant::BigInt(v) => writeln!(f, \"[BIGINT] {v}n\")?,\n                    Constant::Function(code) => writeln!(\n                        f,\n                        \"[FUNCTION] name: '{}' (length: {})\",\n                        code.name().to_std_string_escaped(),\n                        code.length\n                    )?,\n                    Constant::Scope(v) => {\n                        writeln!(\n                            f,\n                            \"[SCOPE] index: {}, bindings: {}\",\n                            v.scope_index(),\n                            v.num_bindings()\n                        )?;\n                    }\n                }\n            }\n        }\n        f.write_str(\"Bindings:\")?;\n        if self.bindings.is_empty() {\n            f.write_str(\" <empty>\\n\")?;\n        } else {\n            f.write_char('\\n')?;\n            for (i, binding_locator) in self.bindings.iter().enumerate() {\n                writeln!(\n                    f,\n                    \"    {i:04}: {}, scope: {:?}\",\n                    binding_locator.name().to_std_string_escaped(),\n                    binding_locator.scope()\n                )?;\n            }\n        }\n        f.write_str(\"Handlers:\")?;\n        if self.handlers.is_empty() {\n            f.write_str(\" <empty>\\n\")?;\n        } else {\n            f.write_char('\\n')?;\n            for (i, handler) in self.handlers.iter().enumerate() {\n                writeln!(\n                    f,\n                    \"    {i:04}: Range: [{:04}, {:04}): Handler: {:04}, Environment: {:02}\",\n                    handler.start,\n                    handler.end,\n                    handler.handler(),\n                    handler.environment_count,\n                )?;\n            }\n        }\n        f.write_str(\"Source Map:\")?;\n        if self.source_info().map().entries().is_empty() {\n            f.write_str(\" <empty>\\n\")?;\n        } else {\n            f.write_char('\\n')?;\n\n            let bytecode_len = self.bytecode.bytes.len() as u32;\n            for (i, handler) in self.source_info().map().entries().windows(2).enumerate() {\n                let current = handler[0];\n                let next = handler.get(1);\n\n                write!(\n                    f,\n                    \"    {i:04}: {:?}: \",\n                    current.pc..next.map_or(bytecode_len, |entry| entry.pc),\n                )?;\n\n                if let Some(position) = current.position {\n                    writeln!(\n                        f,\n                        \"({}, {})\",\n                        position.line_number(),\n                        position.column_number()\n                    )?;\n                } else {\n                    f.write_str(\"unknown\")?;\n                }\n            }\n        }\n        Ok(())\n    }\n}\n\n/// Creates a new function object.\n///\n/// This is used in cases that the prototype is not known if it's [`None`] or [`Some`].\n///\n/// If the prototype given is [`None`] it will use [`create_function_object_fast`]. Otherwise\n/// it will construct the function from template objects that have all the fields except the\n/// prototype, and will perform a prototype transition change to set the prototype.\n///\n/// This is slower than direct object template construction that is done in [`create_function_object_fast`].\npub(crate) fn create_function_object(\n    code: Gc<CodeBlock>,\n    prototype: JsObject,\n    context: &mut Context,\n) -> JsObject {\n    let name: JsValue = code.name().clone().into();\n    let length: JsValue = code.length.into();\n\n    let script_or_module = context.get_active_script_or_module();\n\n    let is_async = code.is_async();\n    let is_generator = code.is_generator();\n    let function = OrdinaryFunction::new(\n        code,\n        context.vm.frame().environments.snapshot_for_closure(),\n        script_or_module,\n        context.realm().clone(),\n    );\n\n    let templates = context.intrinsics().templates();\n\n    let (mut template, storage, constructor_prototype) = if is_generator {\n        let prototype = JsObject::from_proto_and_data_with_shared_shape(\n            context.root_shape(),\n            if is_async {\n                context.intrinsics().objects().async_generator()\n            } else {\n                context.intrinsics().objects().generator()\n            },\n            OrdinaryObject,\n        );\n\n        (\n            templates.function_with_prototype_without_proto().clone(),\n            vec![length, name, prototype.into()],\n            None,\n        )\n    } else if is_async {\n        (\n            templates.function_without_proto().clone(),\n            vec![length, name],\n            None,\n        )\n    } else {\n        let constructor_prototype = templates\n            .function_prototype()\n            .create(OrdinaryObject, vec![JsValue::undefined()]);\n\n        let template = templates.function_with_prototype_without_proto();\n\n        (\n            template.clone(),\n            vec![length, name, constructor_prototype.clone().into()],\n            Some(constructor_prototype),\n        )\n    };\n\n    template.set_prototype(prototype);\n\n    let constructor = template.create(function, storage);\n\n    if let Some(constructor_prototype) = &constructor_prototype {\n        constructor_prototype.borrow_mut().properties_mut().storage[0] = constructor.clone().into();\n    }\n    constructor\n}\n\n/// Creates a new function object.\n///\n/// This is preferred over [`create_function_object`] if prototype is [`None`],\n/// because it constructs the function from a pre-initialized object template,\n/// with all the properties and prototype set.\npub(crate) fn create_function_object_fast(code: Gc<CodeBlock>, context: &mut Context) -> JsObject {\n    let name: JsValue = code.name().clone().into();\n    let length: JsValue = code.length.into();\n\n    let script_or_module = context.get_active_script_or_module();\n\n    let is_async = code.is_async();\n    let is_generator = code.is_generator();\n    let has_prototype_property = code.has_prototype_property();\n    let function = OrdinaryFunction::new(\n        code,\n        context.vm.frame().environments.snapshot_for_closure(),\n        script_or_module,\n        context.realm().clone(),\n    );\n\n    if is_generator {\n        let prototype = JsObject::from_proto_and_data_with_shared_shape(\n            context.root_shape(),\n            if is_async {\n                context.intrinsics().objects().async_generator()\n            } else {\n                context.intrinsics().objects().generator()\n            },\n            OrdinaryObject,\n        );\n        let template = if is_async {\n            context.intrinsics().templates().async_generator_function()\n        } else {\n            context.intrinsics().templates().generator_function()\n        };\n\n        template.create(function, vec![length, name, prototype.into()])\n    } else if is_async {\n        context\n            .intrinsics()\n            .templates()\n            .async_function()\n            .create(function, vec![length, name])\n    } else if !has_prototype_property {\n        context\n            .intrinsics()\n            .templates()\n            .function()\n            .create(function, vec![length, name])\n    } else {\n        let prototype = context\n            .intrinsics()\n            .templates()\n            .function_prototype()\n            .create(OrdinaryObject, vec![JsValue::undefined()]);\n\n        let constructor = context\n            .intrinsics()\n            .templates()\n            .function_with_prototype()\n            .create(function, vec![length, name, prototype.clone().into()]);\n\n        prototype.borrow_mut().properties_mut().storage[0] = constructor.clone().into();\n\n        constructor\n    }\n}\n"
  },
  {
    "path": "core/engine/src/vm/completion_record.rs",
    "content": "//! An implementation of a `CompletionRecord` for Boa's VM.\n\n#![allow(clippy::inline_always)]\n\nuse crate::{Context, JsError, JsResult, JsValue};\nuse boa_gc::{Finalize, Trace, custom_trace};\nuse std::ops::ControlFlow;\n\n/// An implementation of the ECMAScript's `CompletionRecord` [specification] for\n/// Boa's VM output Completion and Result.\n///\n/// [specification]: https://tc39.es/ecma262/#sec-completion-record-specification-type\n#[derive(Debug, Clone, Finalize)]\npub(crate) enum CompletionRecord {\n    Normal(JsValue),\n    Return(JsValue),\n    Throw(JsError),\n}\n\n// SAFETY: this matches all possible variants and traces\n// their inner contents, which makes this safe.\nunsafe impl Trace for CompletionRecord {\n    custom_trace!(this, mark, {\n        match this {\n            Self::Normal(v) => mark(v),\n            Self::Return(r) => mark(r),\n            Self::Throw(th) => mark(th),\n        }\n    });\n}\n\n// ---- `CompletionRecord` methods ----\nimpl CompletionRecord {\n    pub(crate) const fn is_throw_completion(&self) -> bool {\n        matches!(self, Self::Throw(_))\n    }\n\n    /// This function will consume the current `CompletionRecord` and return a `JsResult<JsValue>`\n    // NOTE: rustc bug around evaluating destructors that prevents this from being a const function.\n    // Related issue(s):\n    //   - https://github.com/rust-lang/rust-clippy/issues/4041\n    //   - https://github.com/rust-lang/rust/issues/60964\n    //   - https://github.com/rust-lang/rust/issues/73255\n    #[allow(clippy::missing_const_for_fn)]\n    pub(crate) fn consume(self) -> JsResult<JsValue> {\n        match self {\n            Self::Throw(error) => Err(error),\n            Self::Normal(value) | Self::Return(value) => Ok(value),\n        }\n    }\n}\n\npub(crate) trait IntoCompletionRecord {\n    fn into_completion_record(self, context: &mut Context) -> ControlFlow<CompletionRecord>;\n}\n\nimpl IntoCompletionRecord for () {\n    #[inline(always)]\n    fn into_completion_record(self, _: &mut Context) -> ControlFlow<CompletionRecord> {\n        ControlFlow::Continue(())\n    }\n}\n\nimpl IntoCompletionRecord for JsError {\n    #[inline(always)]\n    fn into_completion_record(self, context: &mut Context) -> ControlFlow<CompletionRecord> {\n        context.handle_error(self)\n    }\n}\n\nimpl IntoCompletionRecord for JsResult<()> {\n    #[inline(always)]\n    fn into_completion_record(self, context: &mut Context) -> ControlFlow<CompletionRecord> {\n        match self {\n            Ok(()) => ControlFlow::Continue(()),\n            Err(err) => context.handle_error(err),\n        }\n    }\n}\n\nimpl IntoCompletionRecord for ControlFlow<CompletionRecord> {\n    #[inline(always)]\n    fn into_completion_record(self, _: &mut Context) -> ControlFlow<CompletionRecord> {\n        self\n    }\n}\n"
  },
  {
    "path": "core/engine/src/vm/flowgraph/color.rs",
    "content": "use std::fmt::Display;\n\n/// Represents the color of a node or edge.\n#[derive(Debug, Clone, Copy, PartialEq, Eq)]\npub enum Color {\n    /// Represents the default color.\n    None,\n    /// Represents the color red.\n    Red,\n    /// Represents the color green.\n    Green,\n    /// Represents the color blue.\n    Blue,\n    /// Represents the color yellow.\n    Yellow,\n    /// Represents the color purple.\n    Purple,\n    /// Represents a RGB color.\n    Rgb {\n        /// Red.\n        r: u8,\n        /// Green.\n        g: u8,\n        /// Blue.\n        b: u8,\n    },\n}\n\nimpl Color {\n    /// Function for converting HSV to RGB color format.\n    #[allow(clippy::many_single_char_names)]\n    #[must_use]\n    pub fn hsv_to_rgb(h: f64, s: f64, v: f64) -> Self {\n        let h_i = (h * 6.0) as i64;\n        let f = h.mul_add(6.0, -h_i as f64);\n        let p = v * (1.0 - s);\n        let q = v * f.mul_add(-s, 1.0);\n        let t = v * (1.0 - f).mul_add(-s, 1.0);\n\n        let (r, g, b) = match h_i {\n            0 => (v, t, p),\n            1 => (q, v, p),\n            2 => (p, v, t),\n            3 => (p, q, v),\n            4 => (t, p, v),\n            5 => (v, p, q),\n            _ => unreachable!(),\n        };\n\n        let r = (r * 256.0) as u8;\n        let g = (g * 256.0) as u8;\n        let b = (b * 256.0) as u8;\n\n        Self::Rgb { r, g, b }\n    }\n\n    /// This function takes a random value and converts it to\n    /// a pleasant to look at RGB color.\n    #[inline]\n    #[must_use]\n    pub fn from_random_number(mut random: f64) -> Self {\n        const GOLDEN_RATIO_CONJUGATE: f64 = 0.618_033_988_749_895;\n        random += GOLDEN_RATIO_CONJUGATE;\n        random %= 1.0;\n\n        Self::hsv_to_rgb(random, 0.7, 0.95)\n    }\n\n    /// Check if the color is [`Self::None`].\n    #[inline]\n    #[must_use]\n    pub fn is_none(&self) -> bool {\n        *self == Self::None\n    }\n}\n\nimpl Display for Color {\n    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {\n        match self {\n            Self::None => f.write_str(\"\"),\n            Self::Red => f.write_str(\"red\"),\n            Self::Green => f.write_str(\"green\"),\n            Self::Blue => f.write_str(\"blue\"),\n            Self::Yellow => f.write_str(\"yellow\"),\n            Self::Purple => f.write_str(\"purple\"),\n            Self::Rgb { r, g, b } => write!(f, \"#{r:02X}{b:02X}{g:02X}\"),\n        }\n    }\n}\n"
  },
  {
    "path": "core/engine/src/vm/flowgraph/edge.rs",
    "content": "use crate::vm::flowgraph::Color;\n\n/// Represents the edge (connection) style.\n#[derive(Debug, Clone, Copy)]\npub enum EdgeStyle {\n    /// Represents a solid line.\n    Line,\n    /// Represents a dotted line.\n    Dotted,\n    /// Represents a dashed line.\n    Dashed,\n}\n\n/// Represents the edge type.\n#[derive(Debug, Clone, Copy)]\npub enum EdgeType {\n    /// Represents no decoration on the edge line.\n    None,\n    /// Represents arrow edge type.\n    Arrow,\n}\n\n/// Represents an edge/connection in the flowgraph.\n#[derive(Debug, Clone)]\npub struct Edge {\n    /// The location of the source node.\n    pub(super) from: usize,\n    /// The location of the destination node.\n    pub(super) to: usize,\n    /// The label on top of the edge.\n    pub(super) label: Option<Box<str>>,\n    /// The color of the line.\n    pub(super) color: Color,\n    /// The style of the line.\n    pub(super) style: EdgeStyle,\n    /// The type of the line.\n    pub(super) type_: EdgeType,\n}\n\nimpl Edge {\n    /// Construct a new edge.\n    pub(super) const fn new(\n        from: usize,\n        to: usize,\n        label: Option<Box<str>>,\n        color: Color,\n        style: EdgeStyle,\n    ) -> Self {\n        Self {\n            from,\n            to,\n            label,\n            color,\n            style,\n            type_: EdgeType::Arrow,\n        }\n    }\n\n    /// Set the type of the edge.\n    #[inline]\n    pub fn set_type(&mut self, type_: EdgeType) {\n        self.type_ = type_;\n    }\n}\n"
  },
  {
    "path": "core/engine/src/vm/flowgraph/graph.rs",
    "content": "use crate::vm::flowgraph::{Color, Edge, EdgeStyle, EdgeType, Node, NodeShape};\nuse std::{collections::hash_map::RandomState, fmt::Write as _, hash::BuildHasher};\n\n/// This represents the direction of flow in the flowgraph.\n#[derive(Debug, Clone, Copy)]\npub enum Direction {\n    /// Represents a top to bottom direction.\n    TopToBottom,\n\n    /// Represents a bottom to top direction.\n    BottomToTop,\n\n    /// Represents a left to right direction.\n    LeftToRight,\n\n    /// Represents a right to left direction.\n    RightToLeft,\n}\n\n/// Represents a sub-graph in the graph.\n///\n/// Sub-graphs can be nested.\n#[derive(Debug, Clone)]\npub struct SubGraph {\n    /// The label on the sub-graph.\n    label: String,\n    /// The nodes it contains.\n    nodes: Vec<Node>,\n    /// The edges/connections in contains.\n    edges: Vec<Edge>,\n    /// The direction of flow in the sub-graph.\n    direction: Direction,\n\n    /// The sub-graphs this graph contains.\n    subgraphs: Vec<SubGraph>,\n}\n\nimpl SubGraph {\n    /// Construct a new subgraph.\n    #[inline]\n    fn new(label: String) -> Self {\n        Self {\n            label,\n            nodes: Vec::default(),\n            edges: Vec::default(),\n            direction: Direction::TopToBottom,\n            subgraphs: Vec::default(),\n        }\n    }\n\n    /// Set the label of the subgraph.\n    #[inline]\n    pub fn set_label(&mut self, label: String) {\n        self.label = label;\n    }\n\n    /// Set the direction of the subgraph.\n    #[inline]\n    pub fn set_direction(&mut self, direction: Direction) {\n        self.direction = direction;\n    }\n\n    /// Add a node to the subgraph.\n    #[inline]\n    pub fn add_node(&mut self, location: usize, shape: NodeShape, label: Box<str>, color: Color) {\n        let node = Node::new(location, shape, label, color);\n        self.nodes.push(node);\n    }\n\n    /// Add an edge to the subgraph.\n    #[inline]\n    pub fn add_edge(\n        &mut self,\n        from: usize,\n        to: usize,\n        label: Option<Box<str>>,\n        color: Color,\n        style: EdgeStyle,\n    ) -> &mut Edge {\n        let edge = Edge::new(from, to, label, color, style);\n        self.edges.push(edge);\n        self.edges.last_mut().expect(\"Already pushed edge\")\n    }\n\n    /// Create a subgraph in this subgraph.\n    #[inline]\n    pub fn subgraph(&mut self, label: String) -> &mut Self {\n        self.subgraphs.push(Self::new(label));\n        let result = self\n            .subgraphs\n            .last_mut()\n            .expect(\"We just pushed a subgraph\");\n        result.set_direction(self.direction);\n        result\n    }\n\n    /// Format into the graphviz format.\n    fn graphviz_format(&self, result: &mut String, prefix: &str) {\n        let label = format!(\"{}\", RandomState::new().hash_one(&self.label));\n        let _ = writeln!(result, \"\\tsubgraph cluster_{prefix}_{label} {{\");\n        result.push_str(\"\\t\\tstyle = filled;\\n\");\n        let _ = writeln!(\n            result,\n            \"\\t\\tlabel = \\\"{}\\\";\",\n            if self.label.is_empty() {\n                \"Anonymous Function\"\n            } else {\n                self.label.as_ref()\n            }\n        );\n\n        let _ = writeln!(\n            result,\n            \"\\t\\t{prefix}_{label}_start [label=\\\"Start\\\",shape=Mdiamond,style=filled,color=green]\"\n        );\n        if !self.nodes.is_empty() {\n            let _ = writeln!(result, \"\\t\\t{prefix}_{label}_start -> {prefix}_{label}_i_0\");\n        }\n\n        for node in &self.nodes {\n            let shape = match node.shape {\n                NodeShape::None => \"\",\n                NodeShape::Record => \", shape=record\",\n                NodeShape::Diamond => \", shape=diamond\",\n            };\n            let color = format!(\",style=filled,color=\\\"{}\\\"\", node.color);\n            let _ = writeln!(\n                result,\n                \"\\t\\t{prefix}_{}_i_{}[label=\\\"{:04}: {}\\\"{shape}{color}];\",\n                label, node.location, node.location, node.label\n            );\n        }\n\n        for edge in &self.edges {\n            let color = format!(\",color=\\\"{}\\\"\", edge.color);\n            let style = match (edge.style, edge.type_) {\n                (EdgeStyle::Line, EdgeType::None) => \",dir=none\",\n                (EdgeStyle::Line, EdgeType::Arrow) => \"\",\n                (EdgeStyle::Dotted, EdgeType::None) => \",style=dotted,dir=none\",\n                (EdgeStyle::Dotted, EdgeType::Arrow) => \",style=dotted\",\n                (EdgeStyle::Dashed, EdgeType::None) => \",style=dashed,dir=none\",\n                (EdgeStyle::Dashed, EdgeType::Arrow) => \",style=dashed,\",\n            };\n            let _ = writeln!(\n                result,\n                \"\\t\\t{prefix}_{}_i_{} -> {prefix}_{}_i_{} [label=\\\"{}\\\", len=f{style}{color}];\",\n                label,\n                edge.from,\n                label,\n                edge.to,\n                edge.label.as_deref().unwrap_or(\"\")\n            );\n        }\n        for (index, subgraph) in self.subgraphs.iter().enumerate() {\n            let prefix = format!(\"{prefix}_F{index}\");\n            subgraph.graphviz_format(result, &prefix);\n        }\n        result.push_str(\"\\t}\\n\");\n    }\n\n    /// Format into the mermaid format.\n    fn mermaid_format(&self, result: &mut String, prefix: &str) {\n        let label = format!(\"{}\", RandomState::new().hash_one(&self.label));\n        let rankdir = match self.direction {\n            Direction::TopToBottom => \"TB\",\n            Direction::BottomToTop => \"BT\",\n            Direction::LeftToRight => \"LR\",\n            Direction::RightToLeft => \"RL\",\n        };\n        let _ = writeln!(\n            result,\n            \"  subgraph {prefix}_{}[\\\"{}\\\"]\",\n            label,\n            if self.label.is_empty() {\n                \"Anonymous Function\"\n            } else {\n                self.label.as_ref()\n            }\n        );\n        let _ = writeln!(result, \"  direction {rankdir}\");\n        let _ = writeln!(result, \"  {prefix}_{label}_start{{Start}}\");\n        let _ = writeln!(result, \"  style {prefix}_{label}_start fill:green\");\n        if !self.nodes.is_empty() {\n            let _ = writeln!(result, \"  {prefix}_{label}_start --> {prefix}_{label}_i_0\");\n        }\n\n        for node in &self.nodes {\n            let (shape_begin, shape_end) = match node.shape {\n                NodeShape::None | NodeShape::Record => ('[', ']'),\n                NodeShape::Diamond => ('{', '}'),\n            };\n            let _ = writeln!(\n                result,\n                \"  {prefix}_{}_i_{}{shape_begin}\\\"{:04}: {}\\\"{shape_end}\",\n                label, node.location, node.location, node.label\n            );\n            if !node.color.is_none() {\n                let _ = writeln!(\n                    result,\n                    \"  style {prefix}_{}_i_{} fill:{}\",\n                    label, node.location, node.color\n                );\n            }\n        }\n\n        for (index, edge) in self.edges.iter().enumerate() {\n            let style = match (edge.style, edge.type_) {\n                (EdgeStyle::Line, EdgeType::None) => \"---\",\n                (EdgeStyle::Line, EdgeType::Arrow) => \"-->\",\n                (EdgeStyle::Dotted | EdgeStyle::Dashed, EdgeType::None) => \"-.-\",\n                (EdgeStyle::Dotted | EdgeStyle::Dashed, EdgeType::Arrow) => \"-.->\",\n            };\n            let _ = writeln!(\n                result,\n                \"  {prefix}_{}_i_{} {style}| {}| {prefix}_{}_i_{}\",\n                label,\n                edge.from,\n                edge.label.as_deref().unwrap_or(\"\"),\n                label,\n                edge.to,\n            );\n\n            if !edge.color.is_none() {\n                let _ = writeln!(\n                    result,\n                    \"  linkStyle {} stroke:{}, stroke-width: 4px\",\n                    index + 1,\n                    edge.color\n                );\n            }\n        }\n        for (index, subgraph) in self.subgraphs.iter().enumerate() {\n            let prefix = format!(\"{prefix}_F{index}\");\n            subgraph.mermaid_format(result, &prefix);\n        }\n        result.push_str(\"  end\\n\");\n    }\n}\n\n/// This represents the main graph that other [`SubGraph`]s can be nested in.\n#[derive(Debug)]\npub struct Graph {\n    subgraphs: Vec<SubGraph>,\n    direction: Direction,\n}\n\nimpl Graph {\n    /// Construct a [`Graph`]\n    #[inline]\n    #[must_use]\n    pub fn new(direction: Direction) -> Self {\n        Self {\n            subgraphs: Vec::default(),\n            direction,\n        }\n    }\n\n    /// Create a [`SubGraph`] in this [`Graph`].\n    #[inline]\n    pub fn subgraph(&mut self, label: String) -> &mut SubGraph {\n        self.subgraphs.push(SubGraph::new(label));\n        let result = self\n            .subgraphs\n            .last_mut()\n            .expect(\"We just pushed a subgraph\");\n        result.set_direction(self.direction);\n        result\n    }\n\n    /// Output the graph into the graphviz format.\n    #[must_use]\n    pub fn to_graphviz_format(&self) -> String {\n        let mut result = String::new();\n        result += \"digraph {\\n\";\n        result += \"\\tnode [shape=record];\\n\";\n\n        let rankdir = match self.direction {\n            Direction::TopToBottom => \"TB\",\n            Direction::BottomToTop => \"BT\",\n            Direction::LeftToRight => \"LR\",\n            Direction::RightToLeft => \"RL\",\n        };\n        let _ = writeln!(result, \"\\trankdir={rankdir};\");\n\n        for subgraph in &self.subgraphs {\n            subgraph.graphviz_format(&mut result, \"\");\n        }\n        result += \"}\\n\";\n        result\n    }\n\n    /// Output the graph into the mermaid format.\n    #[must_use]\n    pub fn to_mermaid_format(&self) -> String {\n        let mut result = String::new();\n        let rankdir = match self.direction {\n            Direction::TopToBottom => \"TD\",\n            Direction::BottomToTop => \"DT\",\n            Direction::LeftToRight => \"LR\",\n            Direction::RightToLeft => \"RL\",\n        };\n        let _ = writeln!(result, \"graph {rankdir}\");\n\n        for subgraph in &self.subgraphs {\n            subgraph.mermaid_format(&mut result, \"\");\n        }\n        result += \"\\n\";\n        result\n    }\n}\n"
  },
  {
    "path": "core/engine/src/vm/flowgraph/mod.rs",
    "content": "//! This module is responsible for generating the vm instruction flowgraph.\n\nuse crate::vm::CodeBlock;\n\nmod color;\nmod edge;\nmod graph;\nmod node;\n\nuse boa_macros::js_str;\npub use color::*;\npub use edge::*;\npub use graph::*;\npub use node::*;\n\nuse super::{\n    Constant,\n    opcode::{Instruction, InstructionIterator},\n};\n\nimpl CodeBlock {\n    /// Output the [`CodeBlock`] VM instructions into a [`Graph`].\n    #[allow(clippy::match_same_arms)]\n    pub fn to_graph(&self, graph: &mut SubGraph) {\n        // Have to remove any invalid graph chars like `<` or `>`.\n        let name = if self.name() == &js_str!(\"<main>\") {\n            \"__main__\".to_string()\n        } else {\n            self.name().to_std_string_escaped()\n        };\n\n        graph.set_label(name);\n\n        let mut iterator = InstructionIterator::new(&self.bytecode);\n        while let Some((previous_pc, opcode, instruction)) = iterator.next() {\n            let opcode_str = opcode.as_str();\n\n            let label = format!(\"{opcode_str} {}\", self.instruction_operands(&instruction));\n\n            let pc = iterator.pc();\n\n            match instruction {\n                Instruction::StrictEq { .. }\n                | Instruction::StrictNotEq { .. }\n                | Instruction::SetRegisterFromAccumulator { .. }\n                | Instruction::Move { .. }\n                | Instruction::PopIntoRegister { .. }\n                | Instruction::PushFromRegister { .. }\n                | Instruction::Add { .. }\n                | Instruction::Sub { .. }\n                | Instruction::Div { .. }\n                | Instruction::Mul { .. }\n                | Instruction::Mod { .. }\n                | Instruction::Pow { .. }\n                | Instruction::ShiftRight { .. }\n                | Instruction::ShiftLeft { .. }\n                | Instruction::UnsignedShiftRight { .. }\n                | Instruction::BitOr { .. }\n                | Instruction::BitAnd { .. }\n                | Instruction::BitXor { .. }\n                | Instruction::BitNot { .. }\n                | Instruction::In { .. }\n                | Instruction::Eq { .. }\n                | Instruction::NotEq { .. }\n                | Instruction::GreaterThan { .. }\n                | Instruction::GreaterThanOrEq { .. }\n                | Instruction::LessThan { .. }\n                | Instruction::LessThanOrEq { .. }\n                | Instruction::InstanceOf { .. }\n                | Instruction::SetAccumulator { .. }\n                | Instruction::SetFunctionName { .. }\n                | Instruction::Inc { .. }\n                | Instruction::Dec { .. }\n                | Instruction::CreateIteratorResult { .. }\n                | Instruction::Generator\n                | Instruction::AsyncGenerator\n                | Instruction::StoreInt8 { .. }\n                | Instruction::StoreInt16 { .. }\n                | Instruction::StoreInt32 { .. }\n                | Instruction::StoreFloat { .. }\n                | Instruction::StoreDouble { .. }\n                | Instruction::StoreLiteral { .. }\n                | Instruction::StoreRegexp { .. } => {\n                    graph.add_node(previous_pc, NodeShape::None, label.into(), Color::None);\n                    graph.add_edge(previous_pc, pc, None, Color::None, EdgeStyle::Line);\n                }\n                Instruction::Jump { address } => {\n                    graph.add_node(previous_pc, NodeShape::Diamond, label.into(), Color::None);\n                    graph.add_edge(\n                        previous_pc,\n                        address.as_u32() as usize,\n                        None,\n                        Color::None,\n                        EdgeStyle::Line,\n                    );\n                }\n                Instruction::JumpIfFalse { address, .. }\n                | Instruction::JumpIfTrue { address, .. }\n                | Instruction::JumpIfNotUndefined { address, .. }\n                | Instruction::JumpIfNullOrUndefined { address, .. }\n                | Instruction::JumpIfNotLessThan { address, .. }\n                | Instruction::JumpIfNotLessThanOrEqual { address, .. }\n                | Instruction::JumpIfNotGreaterThan { address, .. }\n                | Instruction::JumpIfNotGreaterThanOrEqual { address, .. }\n                | Instruction::JumpIfNotEqual { address, .. } => {\n                    graph.add_node(previous_pc, NodeShape::Diamond, label.into(), Color::None);\n                    graph.add_edge(\n                        previous_pc,\n                        address.as_u32() as usize,\n                        Some(\"YES\".into()),\n                        Color::Green,\n                        EdgeStyle::Line,\n                    );\n                    graph.add_edge(\n                        previous_pc,\n                        pc,\n                        Some(\"NO\".into()),\n                        Color::Red,\n                        EdgeStyle::Line,\n                    );\n                }\n                Instruction::TemplateLookup { .. } | Instruction::TemplateCreate { .. } => {\n                    graph.add_node(previous_pc, NodeShape::None, label.into(), Color::Red);\n                    graph.add_edge(previous_pc, pc, None, Color::None, EdgeStyle::Line);\n                }\n                Instruction::LogicalAnd { address, .. }\n                | Instruction::LogicalOr { address, .. }\n                | Instruction::Coalesce { address, .. } => {\n                    graph.add_node(previous_pc, NodeShape::None, label.into(), Color::None);\n                    graph.add_edge(previous_pc, pc, None, Color::None, EdgeStyle::Line);\n                    graph.add_edge(\n                        previous_pc,\n                        address.as_u32() as usize,\n                        Some(\"SHORT CIRCUIT\".into()),\n                        Color::Red,\n                        EdgeStyle::Line,\n                    );\n                }\n                Instruction::Case { address, .. } => {\n                    graph.add_node(previous_pc, NodeShape::None, label.into(), Color::None);\n                    graph.add_edge(\n                        previous_pc,\n                        pc,\n                        Some(\"NO\".into()),\n                        Color::Red,\n                        EdgeStyle::Line,\n                    );\n                    graph.add_edge(\n                        previous_pc,\n                        address.as_u32() as usize,\n                        Some(\"YES\".into()),\n                        Color::Green,\n                        EdgeStyle::Line,\n                    );\n                }\n                Instruction::CallEval { .. }\n                | Instruction::Call { .. }\n                | Instruction::New { .. }\n                | Instruction::SuperCall { .. }\n                | Instruction::ConcatToString { .. }\n                | Instruction::GetArgument { .. } => {\n                    graph.add_node(previous_pc, NodeShape::None, label.into(), Color::None);\n                    graph.add_edge(previous_pc, pc, None, Color::None, EdgeStyle::Line);\n                }\n                Instruction::CopyDataProperties { .. } => {\n                    graph.add_node(previous_pc, NodeShape::None, label.into(), Color::None);\n                    graph.add_edge(previous_pc, pc, None, Color::None, EdgeStyle::Line);\n                }\n                Instruction::PushScope { .. } => {\n                    let random = rand::random();\n\n                    graph.add_node(\n                        previous_pc,\n                        NodeShape::None,\n                        label.into(),\n                        Color::from_random_number(random),\n                    );\n                    graph.add_edge(previous_pc, pc, None, Color::None, EdgeStyle::Line);\n                }\n                Instruction::PopEnvironment => {\n                    graph.add_node(previous_pc, NodeShape::None, label.into(), Color::None);\n                    graph.add_edge(previous_pc, pc, None, Color::None, EdgeStyle::Line);\n                }\n                Instruction::GetFunction { .. } => {\n                    graph.add_node(previous_pc, NodeShape::None, label.into(), Color::None);\n                    graph.add_edge(previous_pc, pc, None, Color::None, EdgeStyle::Line);\n                }\n                Instruction::DefVar { .. }\n                | Instruction::DefInitVar { .. }\n                | Instruction::PutLexicalValue { .. }\n                | Instruction::GetName { .. }\n                | Instruction::GetNameGlobal { .. }\n                | Instruction::GetLocator { .. }\n                | Instruction::GetNameAndLocator { .. }\n                | Instruction::GetNameOrUndefined { .. }\n                | Instruction::SetName { .. }\n                | Instruction::DeleteName { .. } => {\n                    graph.add_node(previous_pc, NodeShape::None, label.into(), Color::None);\n                    graph.add_edge(previous_pc, pc, None, Color::None, EdgeStyle::Line);\n                }\n                Instruction::GetPropertyByNameWithThis { .. }\n                | Instruction::GetLengthProperty { .. }\n                | Instruction::GetPropertyByName { .. }\n                | Instruction::GetPropertyByValue { .. }\n                | Instruction::GetPropertyByValuePush { .. }\n                | Instruction::SetPropertyByName { .. }\n                | Instruction::SetPropertyByNameWithThis { .. }\n                | Instruction::DefineOwnPropertyByName { .. }\n                | Instruction::DefineClassStaticMethodByName { .. }\n                | Instruction::DefineClassMethodByName { .. }\n                | Instruction::SetPropertyGetterByName { .. }\n                | Instruction::DefineClassStaticGetterByName { .. }\n                | Instruction::DefineClassGetterByName { .. }\n                | Instruction::SetPropertySetterByName { .. }\n                | Instruction::DefineClassStaticSetterByName { .. }\n                | Instruction::DefineClassSetterByName { .. }\n                | Instruction::SetPrivateField { .. }\n                | Instruction::DefinePrivateField { .. }\n                | Instruction::SetPrivateMethod { .. }\n                | Instruction::SetPrivateSetter { .. }\n                | Instruction::SetPrivateGetter { .. }\n                | Instruction::GetPrivateField { .. }\n                | Instruction::DeletePropertyByName { .. }\n                | Instruction::PushClassFieldPrivate { .. }\n                | Instruction::PushClassPrivateGetter { .. }\n                | Instruction::PushClassPrivateSetter { .. }\n                | Instruction::PushClassPrivateMethod { .. }\n                | Instruction::InPrivate { .. }\n                | Instruction::ThrowMutateImmutable { .. } => {\n                    graph.add_node(previous_pc, NodeShape::None, label.into(), Color::None);\n                    graph.add_edge(previous_pc, pc, None, Color::None, EdgeStyle::Line);\n                }\n                Instruction::ThrowNewTypeError { .. }\n                | Instruction::ThrowNewReferenceError { .. } => {\n                    graph.add_node(previous_pc, NodeShape::None, label.into(), Color::None);\n                    if let Some((i, handler)) = self.find_handler(previous_pc as u32) {\n                        graph.add_edge(\n                            previous_pc,\n                            handler.handler().as_u32() as usize,\n                            Some(format!(\"Handler {i:2}: CAUGHT\").into()),\n                            Color::None,\n                            EdgeStyle::Line,\n                        );\n                    }\n                }\n                Instruction::Throw { .. } | Instruction::ReThrow => {\n                    if let Some((i, handler)) = self.find_handler(previous_pc as u32) {\n                        graph.add_node(previous_pc, NodeShape::Record, label.into(), Color::None);\n                        graph.add_edge(\n                            previous_pc,\n                            handler.handler().as_u32() as usize,\n                            Some(format!(\"Handler {i:2}: CAUGHT\").into()),\n                            Color::None,\n                            EdgeStyle::Line,\n                        );\n                    } else {\n                        graph.add_node(previous_pc, NodeShape::Diamond, label.into(), Color::None);\n                    }\n                }\n                Instruction::PushPrivateEnvironment { .. } => {\n                    graph.add_node(previous_pc, NodeShape::None, label.into(), Color::None);\n                    graph.add_edge(previous_pc, pc, None, Color::None, EdgeStyle::Line);\n                }\n                Instruction::JumpTable {\n                    index: _,\n                    addresses,\n                } => {\n                    graph.add_node(previous_pc, NodeShape::None, label.into(), Color::None);\n\n                    graph.add_edge(\n                        previous_pc,\n                        pc,\n                        Some(\"DEFAULT\".into()),\n                        Color::None,\n                        EdgeStyle::Line,\n                    );\n\n                    for (i, address) in addresses.iter().enumerate() {\n                        graph.add_edge(\n                            previous_pc,\n                            address.as_u32() as usize,\n                            Some(format!(\"{i}\").into()),\n                            Color::None,\n                            EdgeStyle::Line,\n                        );\n                    }\n                }\n                Instruction::Pop\n                | Instruction::StoreZero { .. }\n                | Instruction::StoreOne { .. }\n                | Instruction::StoreNan { .. }\n                | Instruction::StorePositiveInfinity { .. }\n                | Instruction::StoreNegativeInfinity { .. }\n                | Instruction::StoreNull { .. }\n                | Instruction::StoreTrue { .. }\n                | Instruction::StoreFalse { .. }\n                | Instruction::StoreUndefined { .. }\n                | Instruction::StoreEmptyObject { .. }\n                | Instruction::StoreClassPrototype { .. }\n                | Instruction::SetClassPrototype { .. }\n                | Instruction::SetHomeObject { .. }\n                | Instruction::GetHomeObject { .. }\n                | Instruction::TypeOf { .. }\n                | Instruction::LogicalNot { .. }\n                | Instruction::Pos { .. }\n                | Instruction::Neg { .. }\n                | Instruction::SetPropertyByValue { .. }\n                | Instruction::DefineOwnPropertyByValue { .. }\n                | Instruction::DefineClassStaticMethodByValue { .. }\n                | Instruction::DefineClassMethodByValue { .. }\n                | Instruction::SetPropertyGetterByValue { .. }\n                | Instruction::DefineClassStaticGetterByValue { .. }\n                | Instruction::DefineClassGetterByValue { .. }\n                | Instruction::SetPropertySetterByValue { .. }\n                | Instruction::DefineClassStaticSetterByValue { .. }\n                | Instruction::DefineClassSetterByValue { .. }\n                | Instruction::DeletePropertyByValue { .. }\n                | Instruction::DeleteSuperThrow\n                | Instruction::GetMethod { .. }\n                | Instruction::ToPropertyKey { .. }\n                | Instruction::This { .. }\n                | Instruction::ThisForObjectEnvironmentName { .. }\n                | Instruction::GetFunctionObject { .. }\n                | Instruction::IncrementLoopIteration\n                | Instruction::CreateForInIterator { .. }\n                | Instruction::GetIterator { .. }\n                | Instruction::GetAsyncIterator { .. }\n                | Instruction::IteratorNext\n                | Instruction::IteratorPop { .. }\n                | Instruction::IteratorPush { .. }\n                | Instruction::IteratorUpdateResult { .. }\n                | Instruction::IteratorFinishAsyncNext { .. }\n                | Instruction::IteratorValue { .. }\n                | Instruction::IteratorResult { .. }\n                | Instruction::IteratorDone { .. }\n                | Instruction::IteratorToArray { .. }\n                | Instruction::IteratorReturn { .. }\n                | Instruction::IteratorStackEmpty { .. }\n                | Instruction::ValueNotNullOrUndefined { .. }\n                | Instruction::RestParameterInit { .. }\n                | Instruction::PushValueToArray { .. }\n                | Instruction::PushElisionToArray { .. }\n                | Instruction::PushIteratorToArray { .. }\n                | Instruction::StoreNewArray { .. }\n                | Instruction::GeneratorYield { .. }\n                | Instruction::AsyncGeneratorYield { .. }\n                | Instruction::AsyncGeneratorClose\n                | Instruction::CreatePromiseCapability\n                | Instruction::PushClassField { .. }\n                | Instruction::SuperCallDerived\n                | Instruction::Await { .. }\n                | Instruction::NewTarget { .. }\n                | Instruction::ImportMeta { .. }\n                | Instruction::CallEvalSpread { .. }\n                | Instruction::CallSpread\n                | Instruction::NewSpread\n                | Instruction::SuperCallSpread\n                | Instruction::SetPrototype { .. }\n                | Instruction::GetPrototype { .. }\n                | Instruction::IsObject { .. }\n                | Instruction::SetNameByLocator { .. }\n                | Instruction::PushObjectEnvironment { .. }\n                | Instruction::PopPrivateEnvironment\n                | Instruction::ImportCall { .. }\n                | Instruction::Exception { .. }\n                | Instruction::MaybeException { .. }\n                | Instruction::CheckReturn\n                | Instruction::BindThisValue { .. }\n                | Instruction::CreateMappedArgumentsObject { .. }\n                | Instruction::CreateUnmappedArgumentsObject { .. } => {\n                    graph.add_node(previous_pc, NodeShape::None, label.into(), Color::None);\n                    graph.add_edge(previous_pc, pc, None, Color::None, EdgeStyle::Line);\n                }\n                Instruction::Return => {\n                    graph.add_node(previous_pc, NodeShape::Diamond, label.into(), Color::Red);\n                }\n                Instruction::Reserved1\n                | Instruction::Reserved2\n                | Instruction::Reserved3\n                | Instruction::Reserved4\n                | Instruction::Reserved5\n                | Instruction::Reserved6\n                | Instruction::Reserved7\n                | Instruction::Reserved8\n                | Instruction::Reserved9\n                | Instruction::Reserved10\n                | Instruction::Reserved11\n                | Instruction::Reserved12\n                | Instruction::Reserved13\n                | Instruction::Reserved14\n                | Instruction::Reserved15\n                | Instruction::Reserved16\n                | Instruction::Reserved17\n                | Instruction::Reserved18\n                | Instruction::Reserved19\n                | Instruction::Reserved20\n                | Instruction::Reserved21\n                | Instruction::Reserved22\n                | Instruction::Reserved23\n                | Instruction::Reserved24\n                | Instruction::Reserved25\n                | Instruction::Reserved26\n                | Instruction::Reserved27\n                | Instruction::Reserved28\n                | Instruction::Reserved29\n                | Instruction::Reserved30\n                | Instruction::Reserved31\n                | Instruction::Reserved32\n                | Instruction::Reserved33\n                | Instruction::Reserved34\n                | Instruction::Reserved35\n                | Instruction::Reserved36\n                | Instruction::Reserved37\n                | Instruction::Reserved38\n                | Instruction::Reserved39\n                | Instruction::Reserved40\n                | Instruction::Reserved41\n                | Instruction::Reserved42\n                | Instruction::Reserved43\n                | Instruction::Reserved44\n                | Instruction::Reserved45\n                | Instruction::Reserved46\n                | Instruction::Reserved47\n                | Instruction::Reserved48\n                | Instruction::Reserved49\n                | Instruction::Reserved50\n                | Instruction::Reserved51\n                | Instruction::Reserved52\n                | Instruction::Reserved53\n                | Instruction::Reserved54\n                | Instruction::Reserved55\n                | Instruction::Reserved56\n                | Instruction::Reserved57\n                | Instruction::Reserved58\n                | Instruction::Reserved59\n                | Instruction::Reserved60 => unreachable!(\"Reserved opcodes are unreachable\"),\n            }\n        }\n\n        for constant in &self.constants {\n            if let Constant::Function(function) = constant {\n                let subgraph = graph.subgraph(String::new());\n                function.to_graph(subgraph);\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "core/engine/src/vm/flowgraph/node.rs",
    "content": "use crate::vm::flowgraph::Color;\n\n/// Represents the shape of a node in the flowgraph.\n#[derive(Debug, Clone, Copy)]\npub enum NodeShape {\n    /// Represents the default shape used in the graph.\n    None,\n    /// Represents a rectangular node shape.\n    Record,\n    /// Represents a diamond node shape.\n    Diamond,\n}\n\n/// This represents a node in the flowgraph.\n#[derive(Debug, Clone)]\npub struct Node {\n    /// The opcode location.\n    pub(super) location: usize,\n    /// The shape of the opcode.\n    pub(super) shape: NodeShape,\n    /// The label/contents of the node.\n    pub(super) label: Box<str>,\n    /// The background color of the node.\n    pub(super) color: Color,\n}\n\nimpl Node {\n    /// Construct a new node.\n    pub(super) fn new(location: usize, shape: NodeShape, label: Box<str>, color: Color) -> Self {\n        Self {\n            location,\n            shape,\n            label,\n            color,\n        }\n    }\n}\n"
  },
  {
    "path": "core/engine/src/vm/inline_cache/mod.rs",
    "content": "use arrayvec::ArrayVec;\nuse itertools::Itertools;\nuse std::{cell::Cell, fmt};\n\nuse boa_gc::GcRefCell;\nuse boa_macros::{Finalize, Trace};\n\nuse crate::{\n    JsString,\n    object::shape::{Shape, WeakShape, slot::Slot},\n};\n\n#[cfg(test)]\nmod tests;\n\npub(crate) const PIC_CAPACITY: usize = 4;\n\n/// A cached shape-to-slot mapping for a polymorphic inline cache.\n#[derive(Clone, Debug, Trace, Finalize)]\npub(crate) struct CacheEntry {\n    /// A weak reference is kept to the shape to avoid the shape preventing deallocation.\n    pub(crate) shape: WeakShape,\n    #[unsafe_ignore_trace]\n    pub(crate) slot: Slot,\n}\n\n/// An inline cache entry for a property access.\n#[derive(Clone, Debug, Trace, Finalize)]\npub(crate) struct InlineCache {\n    /// The property that is accessed.\n    pub(crate) name: JsString,\n\n    /// Multiple cached shape-to-slot entries.\n    pub(crate) entries: GcRefCell<ArrayVec<CacheEntry, PIC_CAPACITY>>,\n\n    /// Whether this access site has seen too many shapes and should no longer be cached.\n    #[unsafe_ignore_trace]\n    pub(crate) megamorphic: Cell<bool>,\n}\n\nimpl fmt::Display for InlineCache {\n    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {\n        write!(f, \"(name:{} entries:\", self.name.display_escaped())?;\n\n        if self.megamorphic.get() {\n            return write!(f, \"(megamorphic))\");\n        }\n\n        let entries = self.entries.borrow();\n        let entries = entries.iter().map(|e| e.shape.to_addr_usize()).format(\", \");\n\n        write!(f, \"({entries:#x}))\")\n    }\n}\n\nimpl InlineCache {\n    pub(crate) fn new(name: JsString) -> Self {\n        Self {\n            name,\n            entries: GcRefCell::new(ArrayVec::new()),\n            megamorphic: Cell::new(false),\n        }\n    }\n\n    pub(crate) fn set(&self, shape: &Shape, slot: Slot) {\n        if self.megamorphic.get() {\n            return;\n        }\n\n        let mut entries = self.entries.borrow_mut();\n\n        // Add a new entry if there's space.\n        if entries\n            .try_push(CacheEntry {\n                shape: shape.into(),\n                slot,\n            })\n            .is_err()\n        {\n            // Polymorphic cache is full, transition to megamorphic.\n            self.megamorphic.set(true);\n            entries.clear();\n        }\n    }\n\n    /// Returns the cached `(Shape, Slot)` if a matching shape exists in the inline cache.\n    ///\n    /// Opportunistically cleans up stale weak shape references during lookup.\n    pub(crate) fn get(&self, shape: &Shape) -> Option<(Shape, Slot)> {\n        if self.megamorphic.get() {\n            return None;\n        }\n\n        let mut entries = self.entries.borrow_mut();\n        let mut i = 0;\n        let mut result = None;\n        let shape_addr = shape.to_addr_usize();\n\n        while i < entries.len() {\n            if let Some(upgraded) = entries[i].shape.upgrade() {\n                if upgraded.to_addr_usize() == shape_addr {\n                    result = Some((upgraded, entries[i].slot));\n                    break;\n                }\n                i += 1;\n            } else {\n                // Opportunistically clean up stale weak shapes.\n                entries.swap_remove(i);\n            }\n        }\n\n        result\n    }\n}\n"
  },
  {
    "path": "core/engine/src/vm/inline_cache/tests.rs",
    "content": "use boa_gc::Gc;\nuse boa_parser::Source;\n\nuse crate::{\n    Context, JsObject, JsResult, JsValue,\n    builtins::{OrdinaryObject, function::OrdinaryFunction},\n    js_string,\n    object::{\n        ObjectInitializer, internal_methods::InternalMethodPropertyContext,\n        shape::slot::SlotAttributes,\n    },\n    property::{Attribute, PropertyDescriptor, PropertyKey},\n    vm::CodeBlock,\n};\n\n#[test]\nfn get_own_property_internal_method() {\n    let context = &mut Context::default();\n\n    let o = context\n        .intrinsics()\n        .templates()\n        .ordinary_object()\n        .create(OrdinaryObject, Vec::default());\n\n    let property: PropertyKey = js_string!(\"prop\").into();\n    let value = 100;\n\n    o.set(property.clone(), value, true, context)\n        .expect(\"should not fail\");\n\n    let context = &mut InternalMethodPropertyContext::new(context);\n\n    assert_eq!(context.slot().index, 0);\n    assert_eq!(context.slot().attributes, SlotAttributes::empty());\n\n    o.__get_own_property__(&property, context)\n        .expect(\"should not fail\");\n\n    assert!(\n        !context.slot().in_prototype(),\n        \"Since it's an owned property, the prototype bit should not be set\"\n    );\n\n    assert!(\n        context.slot().is_cacheable(),\n        \"Since it's an owned property, this should be cacheable\"\n    );\n\n    let shape = o.borrow().shape().clone();\n\n    let slot = shape.lookup(&property);\n\n    assert!(slot.is_some(), \"the property should be found in the object\");\n\n    let slot = slot.expect(\"the property should be found in the object\");\n\n    assert_eq!(context.slot().index, slot.index);\n}\n\n#[test]\nfn get_internal_method() {\n    let context = &mut Context::default();\n\n    let o = context\n        .intrinsics()\n        .templates()\n        .ordinary_object()\n        .create(OrdinaryObject, Vec::default());\n\n    let property: PropertyKey = js_string!(\"prop\").into();\n    let value = 100;\n\n    o.set(property.clone(), value, true, context)\n        .expect(\"should not fail\");\n\n    let context = &mut InternalMethodPropertyContext::new(context);\n\n    assert_eq!(context.slot().index, 0);\n    assert_eq!(context.slot().attributes, SlotAttributes::empty());\n\n    o.__get__(&property, o.clone().into(), context)\n        .expect(\"should not fail\");\n\n    assert!(\n        !context.slot().in_prototype(),\n        \"Since it's an owned property, the prototype bit should not be set\"\n    );\n\n    assert!(\n        context.slot().is_cacheable(),\n        \"Since it's an owned property, this should be cacheable\"\n    );\n\n    let shape = o.borrow().shape().clone();\n\n    let slot = shape.lookup(&property);\n\n    assert!(slot.is_some(), \"the property should be found in the object\");\n\n    let slot = slot.expect(\"the property should be found in the object\");\n\n    assert_eq!(context.slot().index, slot.index);\n}\n\n#[test]\nfn get_internal_method_in_prototype() {\n    let context = &mut Context::default();\n\n    let o = context\n        .intrinsics()\n        .templates()\n        .ordinary_object()\n        .create(OrdinaryObject, Vec::default());\n\n    let property: PropertyKey = js_string!(\"prop\").into();\n    let value = 100;\n\n    let prototype = context.intrinsics().constructors().object().prototype();\n\n    prototype\n        .set(property.clone(), value, true, context)\n        .expect(\"should not fail\");\n\n    let context = &mut InternalMethodPropertyContext::new(context);\n\n    assert_eq!(context.slot().index, 0);\n    assert_eq!(context.slot().attributes, SlotAttributes::empty());\n\n    o.__get__(&property, o.clone().into(), context)\n        .expect(\"should not fail\");\n\n    assert!(\n        context.slot().in_prototype(),\n        \"Since it's an prototype property, the prototype bit should not be set\"\n    );\n\n    assert!(\n        context.slot().is_cacheable(),\n        \"Since it's an prototype property, this should be cacheable\"\n    );\n\n    let shape = prototype.borrow().shape().clone();\n\n    let slot = shape.lookup(&property);\n\n    assert!(slot.is_some(), \"the property should be found in the object\");\n\n    let slot = slot.expect(\"the property should be found in the object\");\n\n    assert_eq!(context.slot().index, slot.index);\n}\n\n#[test]\nfn define_own_property_internal_method_non_existent_property() {\n    let context = &mut Context::default();\n\n    let o = context\n        .intrinsics()\n        .templates()\n        .ordinary_object()\n        .create(OrdinaryObject, Vec::default());\n\n    let property: PropertyKey = js_string!(\"prop\").into();\n    let value = 100;\n\n    o.set(property.clone(), value, true, context)\n        .expect(\"should not fail\");\n\n    let context = &mut InternalMethodPropertyContext::new(context);\n\n    assert_eq!(context.slot().index, 0);\n    assert_eq!(context.slot().attributes, SlotAttributes::empty());\n\n    o.__define_own_property__(\n        &property,\n        PropertyDescriptor::builder()\n            .value(value)\n            .writable(true)\n            .configurable(true)\n            .enumerable(true)\n            .build(),\n        context,\n    )\n    .expect(\"should not fail\");\n\n    assert!(\n        !context.slot().in_prototype(),\n        \"Since it's an owned property, the prototype bit should not be set\"\n    );\n\n    assert!(\n        context.slot().is_cacheable(),\n        \"Since it's an owned property, this should be cacheable\"\n    );\n\n    let shape = o.borrow().shape().clone();\n\n    let slot = shape.lookup(&property);\n\n    assert!(slot.is_some(), \"the property should be found in the object\");\n\n    let slot = slot.expect(\"the property should be found in the object\");\n\n    assert_eq!(context.slot().index, slot.index);\n}\n\n#[test]\nfn define_own_property_internal_method_existing_property_property() {\n    let context = &mut Context::default();\n\n    let o = context\n        .intrinsics()\n        .templates()\n        .ordinary_object()\n        .create(OrdinaryObject, Vec::default());\n\n    let property: PropertyKey = js_string!(\"prop\").into();\n    let value = 100;\n\n    o.set(property.clone(), value, true, context)\n        .expect(\"should not fail\");\n\n    o.__define_own_property__(\n        &property,\n        PropertyDescriptor::builder()\n            .value(value)\n            .writable(true)\n            .configurable(true)\n            .enumerable(true)\n            .build(),\n        &mut context.into(),\n    )\n    .expect(\"should not fail\");\n\n    let context = &mut InternalMethodPropertyContext::new(context);\n\n    assert_eq!(context.slot().index, 0);\n    assert_eq!(context.slot().attributes, SlotAttributes::empty());\n\n    o.__define_own_property__(\n        &property,\n        PropertyDescriptor::builder()\n            .value(value + 100)\n            .writable(true)\n            .configurable(true)\n            .enumerable(true)\n            .build(),\n        context,\n    )\n    .expect(\"should not fail\");\n\n    assert!(\n        !context.slot().in_prototype(),\n        \"Since it's an owned property, the prototype bit should not be set\"\n    );\n\n    assert!(\n        context.slot().is_cacheable(),\n        \"Since it's an owned property, this should be cacheable\"\n    );\n\n    let shape = o.borrow().shape().clone();\n\n    let slot = shape.lookup(&property);\n\n    assert!(slot.is_some(), \"the property should be found in the object\");\n\n    let slot = slot.expect(\"the property should be found in the object\");\n\n    assert_eq!(context.slot().index, slot.index);\n}\n\n#[test]\nfn set_internal_method() {\n    let context = &mut Context::default();\n\n    let o = context\n        .intrinsics()\n        .templates()\n        .ordinary_object()\n        .create(OrdinaryObject, Vec::default());\n\n    let property: PropertyKey = js_string!(\"prop\").into();\n    let value = 100;\n\n    o.set(property.clone(), value, true, context)\n        .expect(\"should not fail\");\n\n    let context = &mut InternalMethodPropertyContext::new(context);\n\n    assert_eq!(context.slot().index, 0);\n    assert_eq!(context.slot().attributes, SlotAttributes::empty());\n\n    o.__set__(property.clone(), value.into(), o.clone().into(), context)\n        .expect(\"should not fail\");\n\n    assert!(\n        !context.slot().in_prototype(),\n        \"Since it's an owned property, the prototype bit should not be set\"\n    );\n\n    assert!(\n        context.slot().is_cacheable(),\n        \"Since it's an owned property, this should be cacheable\"\n    );\n\n    let shape = o.borrow().shape().clone();\n\n    let slot = shape.lookup(&property);\n\n    assert!(slot.is_some(), \"the property should be found in the object\");\n\n    let slot = slot.expect(\"the property should be found in the object\");\n\n    assert_eq!(context.slot().index, slot.index);\n}\n\nfn get_codeblock(value: &JsValue) -> Option<(JsObject, Gc<CodeBlock>)> {\n    let object = value.as_object()?.clone();\n    let code = object.downcast_ref::<OrdinaryFunction>()?.code.clone();\n\n    Some((object, code))\n}\n\n#[test]\nfn set_property_by_name_set_inline_cache_on_property_load() -> JsResult<()> {\n    let context = &mut Context::default();\n    let function = context.eval(Source::from_bytes(\"(function (o) { return o.test; })\"))?;\n    let (function, code) = get_codeblock(&function).unwrap();\n\n    assert_eq!(code.ic.len(), 1);\n    assert_eq!(code.ic[0].entries.borrow().len(), 0);\n\n    let o = ObjectInitializer::new(context)\n        .property(js_string!(\"test\"), 0, Attribute::all())\n        .build();\n    let o_shape = o.borrow().shape().clone();\n\n    function.call(&JsValue::undefined(), &[o.clone().into()], context)?;\n\n    assert_eq!(code.ic[0].entries.borrow().len(), 1);\n    assert_eq!(\n        code.ic[0].entries.borrow()[0]\n            .shape\n            .upgrade()\n            .unwrap()\n            .to_addr_usize(),\n        o_shape.to_addr_usize()\n    );\n\n    Ok(())\n}\n\n#[test]\nfn get_property_by_name_set_inline_cache_on_property_load() -> JsResult<()> {\n    let context = &mut Context::default();\n    let function = context.eval(Source::from_bytes(\"(function (o) { o.test = 30; })\"))?;\n    let (function, code) = get_codeblock(&function).unwrap();\n\n    assert_eq!(code.ic.len(), 1);\n    assert_eq!(code.ic[0].entries.borrow().len(), 0);\n\n    let o = ObjectInitializer::new(context)\n        .property(js_string!(\"test\"), 0, Attribute::all())\n        .build();\n    let o_shape = o.borrow().shape().clone();\n\n    function.call(&JsValue::undefined(), &[o.clone().into()], context)?;\n\n    assert_eq!(code.ic[0].entries.borrow().len(), 1);\n    assert_eq!(\n        code.ic[0].entries.borrow()[0]\n            .shape\n            .upgrade()\n            .unwrap()\n            .to_addr_usize(),\n        o_shape.to_addr_usize()\n    );\n\n    Ok(())\n}\n\n#[test]\nfn test_polymorphic_inline_cache() -> JsResult<()> {\n    let context = &mut Context::default();\n    let function = context.eval(Source::from_bytes(\"(function (o) { return o.test; })\"))?;\n    let (function, code) = get_codeblock(&function).unwrap();\n\n    assert_eq!(code.ic.len(), 1);\n    assert_eq!(code.ic[0].entries.borrow().len(), 0);\n    assert!(!code.ic[0].megamorphic.get());\n\n    let shapes = vec![\n        ObjectInitializer::new(context)\n            .property(js_string!(\"test\"), 1, Attribute::all())\n            .build(),\n        ObjectInitializer::new(context)\n            .property(js_string!(\"a\"), 2, Attribute::all())\n            .property(js_string!(\"test\"), 3, Attribute::all())\n            .build(),\n        ObjectInitializer::new(context)\n            .property(js_string!(\"b\"), 4, Attribute::all())\n            .property(js_string!(\"test\"), 5, Attribute::all())\n            .build(),\n        ObjectInitializer::new(context)\n            .property(js_string!(\"c\"), 6, Attribute::all())\n            .property(js_string!(\"test\"), 7, Attribute::all())\n            .build(),\n    ];\n\n    for o in &shapes {\n        function.call(&JsValue::undefined(), &[o.clone().into()], context)?;\n    }\n\n    assert_eq!(code.ic[0].entries.borrow().len(), 4);\n    assert!(!code.ic[0].megamorphic.get());\n\n    Ok(())\n}\n\n#[test]\nfn test_megamorphic_inline_cache() -> JsResult<()> {\n    let context = &mut Context::default();\n    let function = context.eval(Source::from_bytes(\"(function (o) { return o.test; })\"))?;\n    let (function, code) = get_codeblock(&function).unwrap();\n\n    let shapes = vec![\n        ObjectInitializer::new(context)\n            .property(js_string!(\"test\"), 1, Attribute::all())\n            .build(),\n        ObjectInitializer::new(context)\n            .property(js_string!(\"a\"), 1, Attribute::all())\n            .property(js_string!(\"test\"), 1, Attribute::all())\n            .build(),\n        ObjectInitializer::new(context)\n            .property(js_string!(\"b\"), 1, Attribute::all())\n            .property(js_string!(\"test\"), 1, Attribute::all())\n            .build(),\n        ObjectInitializer::new(context)\n            .property(js_string!(\"c\"), 1, Attribute::all())\n            .property(js_string!(\"test\"), 1, Attribute::all())\n            .build(),\n        ObjectInitializer::new(context)\n            .property(js_string!(\"d\"), 1, Attribute::all())\n            .property(js_string!(\"test\"), 1, Attribute::all())\n            .build(),\n    ];\n\n    for o in &shapes {\n        function.call(&JsValue::undefined(), &[o.clone().into()], context)?;\n    }\n\n    assert_eq!(code.ic[0].entries.borrow().len(), 0);\n    assert!(code.ic[0].megamorphic.get());\n\n    // Regression check: repeated miss should remain empty\n    let o6 = ObjectInitializer::new(context)\n        .property(js_string!(\"e\"), 1, Attribute::all())\n        .property(js_string!(\"test\"), 1, Attribute::all())\n        .build();\n    function.call(&JsValue::undefined(), &[o6.clone().into()], context)?;\n    assert_eq!(code.ic[0].entries.borrow().len(), 0);\n    assert!(code.ic[0].megamorphic.get());\n\n    Ok(())\n}\n"
  },
  {
    "path": "core/engine/src/vm/mod.rs",
    "content": "//! Boa's ECMAScript Virtual Machine\n//!\n//! The Virtual Machine (VM) handles generating instructions, then executing them.\n//! This module will provide an instruction set for the AST to use, various traits,\n//! plus an interpreter to execute those instructions\n\nuse crate::{\n    Context, JsError, JsExpect, JsNativeError, JsObject, JsResult, JsString, JsValue, Module,\n    builtins::promise::{PromiseCapability, ResolvingFunctions},\n    environments::EnvironmentStack,\n    error::RuntimeLimitError,\n    object::JsFunction,\n    realm::Realm,\n    script::Script,\n    vm::opcode::{OPCODE_HANDLERS, OPCODE_HANDLERS_BUDGET},\n};\nuse boa_gc::{Finalize, Gc, Trace, custom_trace};\nuse shadow_stack::ShadowStack;\nuse std::{future::Future, ops::ControlFlow, path::Path, pin::Pin, task};\n\n#[cfg(feature = \"trace\")]\nuse crate::sys::time::Instant;\n\n#[cfg(feature = \"trace\")]\nuse std::fmt::Write as _;\n\n#[allow(unused_imports)]\npub(crate) use opcode::{Instruction, InstructionIterator, Opcode};\n\npub(crate) use {\n    call_frame::CallFrameFlags,\n    code_block::{\n        CodeBlockFlags, Constant, Handler, create_function_object, create_function_object_fast,\n    },\n    completion_record::CompletionRecord,\n    inline_cache::InlineCache,\n};\n\npub use runtime_limits::RuntimeLimits;\npub use {\n    call_frame::{CallFrame, GeneratorResumeKind},\n    code_block::CodeBlock,\n    source_info::{NativeSourceInfo, SourcePath},\n};\n\npub(crate) use code_block::GlobalFunctionBinding;\n\nmod call_frame;\nmod code_block;\nmod completion_record;\nmod inline_cache;\nmod runtime_limits;\n\npub(crate) mod opcode;\npub(crate) mod shadow_stack;\npub(crate) mod source_info;\n\n#[cfg(feature = \"flowgraph\")]\npub mod flowgraph;\n\n#[cfg(test)]\nmod tests;\n\n/// Virtual Machine.\n#[derive(Debug)]\npub struct Vm {\n    /// The call frame stack.\n    ///\n    /// The current frame is always the last element. A dummy frame is always\n    /// present at position 0 so the stack is never empty.\n    pub(crate) frames: Vec<CallFrame>,\n\n    pub(crate) stack: Stack,\n\n    pub(crate) return_value: JsValue,\n\n    /// When an error is thrown, the pending exception is set.\n    ///\n    /// If we throw an empty exception ([`None`]), this means that `return()` was called on a generator,\n    /// propagating though the exception handlers and executing the finally code (if any).\n    ///\n    /// See [`ReThrow`](crate::vm::Opcode::ReThrow) and [`ReThrow`](crate::vm::Opcode::Exception) opcodes.\n    ///\n    /// This eliminates the conversion between [`crate::JsNativeError`] and [`crate::JsValue`] if not needed.\n    pub(crate) pending_exception: Option<JsError>,\n    pub(crate) runtime_limits: RuntimeLimits,\n\n    /// This is used to assign a native (rust) function as the active function,\n    /// because we don't push a frame for them.\n    pub(crate) native_active_function: Option<JsObject>,\n\n    /// Number of nested host calls that re-enter the VM via `Context::run()`.\n    ///\n    /// This is incremented by high-level host entry points such as\n    /// [`JsObject::call`](crate::object::JsObject::call) and\n    /// [`JsObject::construct`](crate::object::JsObject::construct).\n    pub(crate) host_call_depth: usize,\n\n    pub(crate) shadow_stack: ShadowStack,\n\n    #[cfg(feature = \"trace\")]\n    pub(crate) trace: bool,\n    #[cfg(feature = \"trace\")]\n    pub(crate) current_frame: Option<*const CallFrame>,\n}\n\n/// The stack holds the [`JsValue`]s for the calling convention and registers.\n///\n/// The stack is persistent across frames.\n/// It's addressing is relative to the frame pointer (`fp`) in each [`CallFrame`].\n///\n/// The stack stores the following elements:\n/// - The function prologue\n///   - The `this` value of the function\n///   - The function object itself\n/// - The arguments of the function\n/// - The register file for the frame\n/// - Some manually pushed values like the return value of a function.\n///\n/// ```text\n///  Stack: | this | func | arg1 | ... | argN | reg0 | reg1 | ... | regK |\n///           ▲                                  ▲\n///           └─ fp                              └─ rp\n/// ```\n#[derive(Clone, Debug, Trace, Finalize)]\npub(crate) struct Stack {\n    stack: Vec<JsValue>,\n}\n\nimpl Stack {\n    /// Creates a new stack with the given capacity.\n    fn new(capacity: usize) -> Self {\n        Self {\n            stack: Vec::with_capacity(capacity),\n        }\n    }\n\n    /// Get a register value by index, relative to the given frame's `rp`.\n    pub(crate) fn get_register(&self, frame: &CallFrame, index: usize) -> Option<&JsValue> {\n        self.stack.get(frame.rp as usize + index)\n    }\n\n    /// Set a register value by index, relative to the given frame's `rp`.\n    pub(crate) fn set_register(&mut self, frame: &CallFrame, index: usize, value: JsValue) {\n        self.stack[frame.rp as usize + index] = value;\n    }\n\n    /// Truncate the stack to the given frame.\n    pub(crate) fn truncate_to_frame(&mut self, frame: &CallFrame) {\n        self.stack.truncate(frame.frame_pointer());\n    }\n\n    /// Split the stack at the given frame.\n    pub(crate) fn split_off_frame(&mut self, frame: &CallFrame) -> Self {\n        let frame_pointer = frame.frame_pointer();\n        Self {\n            stack: self.stack.split_off(frame_pointer),\n        }\n    }\n\n    /// Get the `this` value of the given frame.\n    pub(crate) fn get_this(&self, frame: &CallFrame) -> JsValue {\n        self.stack[frame.this_index()].clone()\n    }\n\n    /// Set the `this` value of the given frame.\n    pub(crate) fn set_this(&mut self, frame: &CallFrame, this: JsValue) {\n        self.stack[frame.this_index()] = this;\n    }\n\n    /// Get the function object of the given frame.\n    pub(crate) fn get_function(&self, frame: &CallFrame) -> Option<JsObject> {\n        if let Some(object) = self.stack[frame.function_index()].as_object() {\n            return Some(object.clone());\n        }\n        None\n    }\n\n    /// Get the function arguments of the given frame.\n    pub(crate) fn get_arguments(&self, frame: &CallFrame) -> &[JsValue] {\n        &self.stack[frame.arguments_range()]\n    }\n\n    /// Get a single function argument of the given frame by index.\n    pub(crate) fn get_argument(&self, frame: &CallFrame, index: usize) -> Option<&JsValue> {\n        self.get_arguments(frame).get(index)\n    }\n\n    /// Get the rest arguments of the given frame.\n    pub(crate) fn pop_rest_arguments(&mut self, frame: &CallFrame) -> Option<Vec<JsValue>> {\n        let argument_count = frame.argument_count as usize;\n        let param_count = frame.code_block().parameter_length as usize;\n        if argument_count < param_count {\n            return None;\n        }\n        let args_start = frame.fp as usize + CallFrame::FUNCTION_PROLOGUE as usize;\n        let args_end = args_start + argument_count;\n        let rest_count = argument_count - param_count + 1;\n\n        Some(\n            self.stack\n                .drain((args_end - rest_count)..args_end)\n                .collect(),\n        )\n    }\n\n    /// Push a value on the stack.\n    pub(crate) fn push<T>(&mut self, value: T)\n    where\n        T: Into<JsValue>,\n    {\n        self.stack.push(value.into());\n    }\n\n    /// Pop a value off the stack.\n    ///\n    /// # Panics\n    ///\n    /// If there is nothing to pop, then this will panic.\n    #[track_caller]\n    pub(crate) fn pop(&mut self) -> JsValue {\n        self.stack.pop().expect(\"stack was empty\")\n    }\n\n    /// Pop the function arguments according to the calling convention.\n    /// This will pop the last `argument_count` values from the stack.\n    pub(crate) fn calling_convention_pop_arguments(\n        &mut self,\n        argument_count: usize,\n    ) -> Vec<JsValue> {\n        let index = self.stack.len() - argument_count;\n        self.stack.split_off(index)\n    }\n\n    /// Push the function arguments according to the calling convention.\n    /// This will push the given values onto the stack.\n    pub(crate) fn calling_convention_push_arguments(&mut self, values: &[JsValue]) {\n        self.stack.extend_from_slice(values);\n    }\n\n    /// Get the function object at the top of the stack according to the calling convention.\n    #[track_caller]\n    pub(crate) fn calling_convention_get_function(&self, argument_count: usize) -> &JsValue {\n        let index = self.stack.len() - 1 - argument_count;\n        self.stack\n            .get(index)\n            .expect(\"invalid calling convention function index\")\n    }\n\n    /// Set the function object value at the top of the stack according to the calling convention.\n    #[track_caller]\n    pub(crate) fn calling_convention_set_function(\n        &mut self,\n        argument_count: usize,\n        function: JsValue,\n    ) {\n        let index = self.stack.len() - 1 - argument_count;\n        self.stack[index] = function;\n    }\n\n    /// Set the `this` value at the top of the stack according to the calling convention.\n    #[track_caller]\n    pub(crate) fn calling_convention_set_this(&mut self, argument_count: usize, function: JsValue) {\n        let index = self.stack.len() - 2 - argument_count;\n        self.stack[index] = function;\n    }\n\n    /// Insert the function arguments at the top of the stack according to the calling convention.\n    /// This will insert the given values at the position of the function arguments.\n    pub(crate) fn calling_convention_insert_arguments(\n        &mut self,\n        existing_argument_count: usize,\n        arguments: &[JsValue],\n    ) {\n        let index = self.stack.len() - existing_argument_count;\n        self.stack.splice(index..index, arguments.iter().cloned());\n    }\n\n    #[cfg(feature = \"trace\")]\n    /// Display the stack trace of the current frame.\n    fn display_trace(&self, frame: &CallFrame, frame_count: usize) -> String {\n        let mut string = String::from(\"[ \");\n        for (i, (j, value)) in self.stack.iter().enumerate().rev().enumerate() {\n            match value {\n                value if value.is_callable() => string.push_str(\"[function]\"),\n                value if value.is_object() => string.push_str(\"[object]\"),\n                value => string.push_str(&value.display().to_string()),\n            }\n\n            if frame.frame_pointer() == j {\n                let _ = write!(string, \" |{frame_count}|\");\n            } else if i + 1 != self.stack.len() {\n                string.push(',');\n            }\n\n            string.push(' ');\n        }\n\n        string.push(']');\n        string\n    }\n}\n\n/// Active runnable in the current vm context.\n#[derive(Debug, Clone, Finalize)]\npub enum ActiveRunnable {\n    /// A [**Script Record**](https://tc39.es/ecma262/#sec-script-records)\n    Script(Script),\n    /// A [**Source Text Module Record**](https://tc39.es/ecma262/#sec-source-text-module-records).\n    Module(Module),\n}\n\nunsafe impl Trace for ActiveRunnable {\n    custom_trace!(this, mark, {\n        match this {\n            Self::Script(script) => mark(script),\n            Self::Module(module) => mark(module),\n        }\n    });\n}\n\nimpl ActiveRunnable {\n    /// Gets the path of the runnable, if it has one.\n    #[must_use]\n    pub fn path(&self) -> Option<&Path> {\n        match self {\n            Self::Script(script) => script.path(),\n            Self::Module(module) => module.path(),\n        }\n    }\n}\n\nimpl Vm {\n    /// Creates a new virtual machine.\n    pub(crate) fn new(realm: Realm) -> Self {\n        let mut frames = Vec::with_capacity(16);\n        frames.push(CallFrame::new(\n            Gc::new(CodeBlock::new(JsString::default(), 0, true)),\n            None,\n            EnvironmentStack::new(),\n            realm,\n        ));\n        Self {\n            frames,\n            stack: Stack::new(1024),\n            return_value: JsValue::undefined(),\n            pending_exception: None,\n            runtime_limits: RuntimeLimits::default(),\n            native_active_function: None,\n            host_call_depth: 0,\n            shadow_stack: ShadowStack::default(),\n            #[cfg(feature = \"trace\")]\n            trace: false,\n            #[cfg(feature = \"trace\")]\n            current_frame: None,\n        }\n    }\n\n    #[track_caller]\n    #[inline]\n    pub(crate) fn set_register(&mut self, index: usize, value: JsValue) {\n        let rp = self.frame().rp as usize;\n        debug_assert!(\n            rp + index < self.stack.stack.len(),\n            \"register index out of bounds: rp {rp}, index {index}, stack len {}\",\n            self.stack.stack.len()\n        );\n        // SAFETY: Register indices are determined by the bytecode compiler and are\n        // guaranteed to be within the register bounds for well-formed bytecode. The\n        // debug_assert above catches any compiler bugs during development.\n        unsafe {\n            *self.stack.stack.get_unchecked_mut(rp + index) = value;\n        }\n    }\n\n    #[track_caller]\n    #[inline]\n    pub(crate) fn get_register(&self, index: usize) -> &JsValue {\n        let rp = self.frame().rp as usize;\n        debug_assert!(\n            rp + index < self.stack.stack.len(),\n            \"register index out of bounds: rp {rp}, index {index}, stack len {}\",\n            self.stack.stack.len()\n        );\n        // SAFETY: Register indices are determined by the bytecode compiler and are\n        // guaranteed to be within the register bounds for well-formed bytecode. The\n        // debug_assert above catches any compiler bugs during development.\n        unsafe { self.stack.stack.get_unchecked(rp + index) }\n    }\n\n    /// Takes the value from a register, replacing it with `undefined`.\n    ///\n    /// Use this instead of `get_register().clone()` when the register value is\n    /// consumed and won't be read again, to avoid unnecessary Gc refcount increments.\n    #[track_caller]\n    #[inline]\n    pub(crate) fn take_register(&mut self, index: usize) -> JsValue {\n        let rp = self.frame().rp as usize;\n        debug_assert!(\n            rp + index < self.stack.stack.len(),\n            \"register index out of bounds: rp {rp}, index {index}, stack len {}\",\n            self.stack.stack.len()\n        );\n        // SAFETY: Register indices are determined by the bytecode compiler and are\n        // guaranteed to be within the register bounds for well-formed bytecode. The\n        // debug_assert above catches any compiler bugs during development.\n        unsafe { std::mem::take(self.stack.stack.get_unchecked_mut(rp + index)) }\n    }\n\n    /// Set the promise capability for the current frame.\n    #[track_caller]\n    pub(crate) fn set_promise_capability(\n        &mut self,\n        promise_capability: PromiseCapability,\n    ) -> JsResult<()> {\n        #[cfg(debug_assertions)]\n        {\n            if !self.frame().code_block().is_async() {\n                return Err(crate::error::PanicError::new(\n                    \"only async functions and modules with a top-level-await \\\n                    can have a promise capability\",\n                )\n                .into());\n            }\n        }\n\n        let rp = self.frame().rp as usize;\n        self.stack.stack[rp + CallFrame::PROMISE_CAPABILITY_PROMISE_REGISTER_INDEX] =\n            promise_capability.promise.into();\n        self.stack.stack[rp + CallFrame::PROMISE_CAPABILITY_RESOLVE_REGISTER_INDEX] =\n            promise_capability.functions.resolve.into();\n        self.stack.stack[rp + CallFrame::PROMISE_CAPABILITY_REJECT_REGISTER_INDEX] =\n            promise_capability.functions.reject.into();\n\n        Ok(())\n    }\n\n    /// Get the promise capability for the current frame.\n    #[track_caller]\n    pub(crate) fn get_promise_capability(&self) -> JsResult<PromiseCapability> {\n        #[cfg(debug_assertions)]\n        if !self.frame().code_block().is_async() {\n            return Err(crate::error::PanicError::new(\n                \"cannot get promise capability from non-async code\",\n            )\n            .into());\n        }\n\n        let rp = self.frame().rp as usize;\n        let promise = self\n            .stack\n            .stack\n            .get(rp + CallFrame::PROMISE_CAPABILITY_PROMISE_REGISTER_INDEX)\n            .and_then(JsValue::as_object)\n            .js_expect(\"registers must have a promise capability\")?;\n        let resolve = self\n            .stack\n            .stack\n            .get(rp + CallFrame::PROMISE_CAPABILITY_RESOLVE_REGISTER_INDEX)\n            .and_then(JsValue::as_object)\n            .and_then(JsFunction::from_object)\n            .js_expect(\"registers must have a resolve function\")?;\n        let reject = self\n            .stack\n            .stack\n            .get(rp + CallFrame::PROMISE_CAPABILITY_REJECT_REGISTER_INDEX)\n            .and_then(JsValue::as_object)\n            .and_then(JsFunction::from_object)\n            .js_expect(\"registers must have a reject function\")?;\n\n        Ok(PromiseCapability {\n            promise,\n            functions: ResolvingFunctions { resolve, reject },\n        })\n    }\n\n    /// Get the async generator object for the current frame.\n    #[track_caller]\n    pub(crate) fn async_generator_object(&self) -> Option<JsObject> {\n        if !self.frame().code_block().is_async_generator() {\n            return None;\n        }\n\n        let rp = self.frame().rp as usize;\n        self.stack\n            .stack\n            .get(rp + CallFrame::ASYNC_GENERATOR_OBJECT_REGISTER_INDEX)\n            .expect(\"registers must have an async generator object\")\n            .as_object()\n    }\n\n    /// Retrieves the VM frame.\n    ///\n    /// NOTE: When you need a `&CallFrame` alongside a mutable borrow of another\n    /// `Vm` field (e.g. `stack`), use `self.vm.frames.last().expect(\"frame must exist\")` instead\n    /// so that the borrow checker can split the borrows.\n    #[track_caller]\n    #[inline]\n    pub(crate) fn frame(&self) -> &CallFrame {\n        // SAFETY: `frames` always contains at least the dummy frame.\n        unsafe { self.frames.last().unwrap_unchecked() }\n    }\n\n    /// Retrieves the VM frame mutably.\n    ///\n    /// NOTE: When you need a `&mut CallFrame` alongside a mutable borrow of another\n    /// `Vm` field (e.g. `stack`), use `self.vm.frames.last_mut().expect(\"frame must exist\")` instead\n    /// so that the borrow checker can split the borrows.\n    #[track_caller]\n    #[inline]\n    pub(crate) fn frame_mut(&mut self) -> &mut CallFrame {\n        // SAFETY: `frames` always contains at least the dummy frame.\n        unsafe { self.frames.last_mut().unwrap_unchecked() }\n    }\n\n    pub(crate) fn push_frame(&mut self, mut frame: CallFrame) {\n        // NOTE: We need to check if we already pushed the registers,\n        //       since generator-like functions push the same call\n        //       frame with pre-built stack and registers (fp and rp already set).\n        if !frame.registers_already_pushed() {\n            let current_stack_length = self.stack.stack.len() as u32;\n            frame.fp = current_stack_length - frame.argument_count - CallFrame::FUNCTION_PROLOGUE;\n\n            let register_count = frame.code_block.register_count as usize;\n            frame.rp = self.stack.stack.len() as u32;\n            self.stack.stack.resize(\n                self.stack.stack.len() + register_count,\n                JsValue::undefined(),\n            );\n        }\n\n        // Keep carrying the last active runnable in case the current callframe\n        // yields.\n        if frame.active_runnable.is_none() {\n            let current = self.frame();\n            frame.active_runnable.clone_from(&current.active_runnable);\n        }\n\n        let current_pc = self.frame().pc;\n        self.shadow_stack\n            .push_bytecode(current_pc, frame.code_block().source_info.clone());\n\n        self.frames.push(frame);\n    }\n\n    pub(crate) fn push_frame_with_stack(\n        &mut self,\n        frame: CallFrame,\n        this: JsValue,\n        function: JsValue,\n    ) {\n        self.stack.push(this);\n        self.stack.push(function);\n\n        self.push_frame(frame);\n    }\n\n    pub(crate) fn pop_frame(&mut self) -> Option<CallFrame> {\n        // Don't pop the dummy frame (index 0).\n        if self.frames.len() <= 1 {\n            return None;\n        }\n        self.shadow_stack.pop();\n        self.frames.pop()\n    }\n\n    /// Handles an exception thrown at position `pc`.\n    ///\n    /// Returns `true` if the exception was handled, `false` otherwise.\n    #[inline]\n    pub(crate) fn handle_exception_at(&mut self, pc: u32) -> bool {\n        let frame = self.frame_mut();\n        let Some((_, handler)) = frame.code_block().find_handler(pc) else {\n            return false;\n        };\n\n        let catch_address = handler.handler();\n        let environment_sp = frame.env_fp + handler.environment_count;\n\n        // Go to handler location.\n        frame.pc = u32::from(catch_address);\n\n        self.frame_mut()\n            .environments\n            .truncate(environment_sp as usize);\n\n        true\n    }\n\n    pub(crate) fn get_return_value(&self) -> JsValue {\n        self.return_value.clone()\n    }\n\n    pub(crate) fn set_return_value(&mut self, value: JsValue) {\n        self.return_value = value;\n    }\n\n    pub(crate) fn take_return_value(&mut self) -> JsValue {\n        std::mem::take(&mut self.return_value)\n    }\n}\n\n#[allow(clippy::print_stdout)]\n#[cfg(feature = \"trace\")]\nimpl Context {\n    const COLUMN_WIDTH: usize = 26;\n    const TIME_COLUMN_WIDTH: usize = Self::COLUMN_WIDTH / 2;\n    const OPCODE_COLUMN_WIDTH: usize = Self::COLUMN_WIDTH;\n    const OPERAND_COLUMN_WIDTH: usize = Self::COLUMN_WIDTH;\n    const NUMBER_OF_COLUMNS: usize = 4;\n\n    pub(crate) fn trace_call_frame(&self) {\n        let frame = self.vm.frame();\n        let msg = if self.vm.frames.is_empty() {\n            \" VM Start \".to_string()\n        } else {\n            format!(\n                \" Call Frame '{}'{} \",\n                frame.code_block().name().to_std_string_escaped(),\n                if frame.code_block().name().is_empty() {\n                    format!(\" [anon#{}]\", frame.code_block().debug_id)\n                } else {\n                    String::new()\n                }\n            )\n        };\n\n        // Only print a functions compiled output if it has not been printed already\n        if !frame.code_block.traced.get() {\n            println!(\"{}\", frame.code_block);\n            frame.code_block.traced.set(true);\n        }\n        println!(\n            \"{msg:-^width$}\",\n            width = Self::COLUMN_WIDTH * Self::NUMBER_OF_COLUMNS - 10\n        );\n        println!(\n            \"{:<TIME_COLUMN_WIDTH$} {:<OPCODE_COLUMN_WIDTH$} {:<OPERAND_COLUMN_WIDTH$} Stack\\n\",\n            \"Time\",\n            \"Opcode\",\n            \"Operands\",\n            TIME_COLUMN_WIDTH = Self::TIME_COLUMN_WIDTH,\n            OPCODE_COLUMN_WIDTH = Self::OPCODE_COLUMN_WIDTH,\n            OPERAND_COLUMN_WIDTH = Self::OPERAND_COLUMN_WIDTH,\n        );\n    }\n\n    fn trace_execute_instruction<F>(\n        &mut self,\n        f: F,\n        opcode: Opcode,\n    ) -> ControlFlow<CompletionRecord>\n    where\n        F: FnOnce(&mut Context, Opcode) -> ControlFlow<CompletionRecord>,\n    {\n        if self.vm.current_frame != Some(self.vm.frame()) {\n            println!();\n            self.trace_call_frame();\n            self.vm.current_frame = Some(self.vm.frame());\n        }\n        let frame = self.vm.frame();\n        let (instruction, _) = frame\n            .code_block\n            .bytecode\n            .next_instruction(frame.pc as usize);\n        let operands = self\n            .vm\n            .frame()\n            .code_block()\n            .instruction_operands(&instruction);\n\n        let instant = Instant::now();\n        let result = self.execute_instruction(f, opcode);\n        let duration = instant.elapsed();\n\n        let stack = self\n            .vm\n            .stack\n            .display_trace(self.vm.frame(), self.vm.frames.len() - 1);\n\n        println!(\n            \"{:<TIME_COLUMN_WIDTH$} {:<OPCODE_COLUMN_WIDTH$} {operands:<OPERAND_COLUMN_WIDTH$} {stack}\",\n            format!(\"{}μs\", duration.as_micros()),\n            format!(\"{}\", opcode.as_str()),\n            TIME_COLUMN_WIDTH = Self::TIME_COLUMN_WIDTH,\n            OPCODE_COLUMN_WIDTH = Self::OPCODE_COLUMN_WIDTH,\n            OPERAND_COLUMN_WIDTH = Self::OPERAND_COLUMN_WIDTH,\n        );\n\n        result\n    }\n}\n\nimpl Context {\n    fn execute_instruction<F>(&mut self, f: F, opcode: Opcode) -> ControlFlow<CompletionRecord>\n    where\n        F: FnOnce(&mut Context, Opcode) -> ControlFlow<CompletionRecord>,\n    {\n        f(self, opcode)\n    }\n\n    fn execute_one<F>(&mut self, f: F, opcode: Opcode) -> ControlFlow<CompletionRecord>\n    where\n        F: FnOnce(&mut Context, Opcode) -> ControlFlow<CompletionRecord>,\n    {\n        #[cfg(feature = \"fuzz\")]\n        {\n            use crate::error::EngineError;\n            if self.instructions_remaining == 0 {\n                return ControlFlow::Break(CompletionRecord::Throw(\n                    EngineError::NoInstructionsRemain.into(),\n                ));\n            }\n            self.instructions_remaining -= 1;\n        }\n\n        #[cfg(feature = \"trace\")]\n        if self.vm.trace || self.vm.frame().code_block.traceable() {\n            self.trace_execute_instruction(f, opcode)\n        } else {\n            self.execute_instruction(f, opcode)\n        }\n\n        #[cfg(not(feature = \"trace\"))]\n        self.execute_instruction(f, opcode)\n    }\n\n    fn handle_error(&mut self, mut err: JsError) -> ControlFlow<CompletionRecord> {\n        // Capture the backtrace early, before any exception handler check,\n        // so that errors caught by internal handlers (e.g. async module\n        // evaluation) still carry source position information.\n        if err.backtrace.is_none() {\n            err.backtrace = Some(\n                self.vm\n                    .shadow_stack\n                    .take(self.vm.runtime_limits.backtrace_limit(), self.vm.frame().pc),\n            );\n        }\n\n        // If we hit the execution step limit, bubble up the error to the\n        // (Rust) caller instead of trying to handle as an exception.\n        if !err.is_catchable() {\n            let mut frame = None;\n            let mut env_fp = self.vm.frame().environments.len();\n            loop {\n                if self.vm.frame().exit_early() {\n                    break;\n                }\n\n                env_fp = self.vm.frame().env_fp as usize;\n\n                let Some(f) = self.vm.pop_frame() else {\n                    break;\n                };\n                frame = Some(f);\n            }\n            self.vm.frame_mut().environments.truncate(env_fp);\n            if let Some(frame) = frame {\n                self.vm.stack.truncate_to_frame(&frame);\n            }\n            return ControlFlow::Break(CompletionRecord::Throw(err));\n        }\n\n        // Note: -1 because we increment after fetching the opcode.\n        let pc = self.vm.frame().pc.saturating_sub(1);\n        if self.vm.handle_exception_at(pc) {\n            self.vm.pending_exception = Some(err);\n            return ControlFlow::Continue(());\n        }\n\n        // Inject realm before crossing the function boundary\n        let err = err.inject_realm(self.realm().clone());\n\n        self.vm.pending_exception = Some(err);\n        self.handle_throw()\n    }\n\n    fn handle_return(&mut self) -> ControlFlow<CompletionRecord> {\n        let exit_early = self.vm.frame().exit_early();\n        let frame = self.vm.frames.last().expect(\"frame must exist\");\n        self.vm.stack.truncate_to_frame(frame);\n\n        let result = self.vm.take_return_value();\n        if exit_early {\n            return ControlFlow::Break(CompletionRecord::Return(result));\n        }\n\n        self.vm.stack.push(result);\n        self.vm.pop_frame().expect(\"frame must exist\");\n        ControlFlow::Continue(())\n    }\n\n    fn handle_yield(&mut self) -> ControlFlow<CompletionRecord> {\n        let result = self.vm.take_return_value();\n        if self.vm.frame().exit_early() {\n            return ControlFlow::Break(CompletionRecord::Normal(result));\n        }\n\n        self.vm.stack.push(result);\n        self.vm.pop_frame().expect(\"frame must exist\");\n        ControlFlow::Continue(())\n    }\n\n    fn handle_throw(&mut self) -> ControlFlow<CompletionRecord> {\n        if self\n            .vm\n            .pending_exception\n            .as_ref()\n            .is_some_and(|err| err.backtrace.is_none())\n        {\n            let pc = self.vm.frames.last().expect(\"frame must exist\").pc;\n            let limit = self.vm.runtime_limits.backtrace_limit();\n            let backtrace = self.vm.shadow_stack.take(limit, pc);\n            self.vm\n                .pending_exception\n                .as_mut()\n                .expect(\"pending exception must exist\")\n                .backtrace = Some(backtrace);\n        }\n\n        let mut env_fp = self.vm.frame().env_fp;\n        if self.vm.frame().exit_early() {\n            self.vm.frame_mut().environments.truncate(env_fp as usize);\n            let frame = self.vm.frames.last().expect(\"frame must exist\");\n            self.vm.stack.truncate_to_frame(frame);\n            return ControlFlow::Break(CompletionRecord::Throw(\n                self.vm\n                    .pending_exception\n                    .take()\n                    .expect(\"Err must exist for a CompletionType::Throw\"),\n            ));\n        }\n\n        let mut frame = self.vm.pop_frame().expect(\"frame must exist\");\n\n        loop {\n            env_fp = self.vm.frame().env_fp;\n            let pc = self.vm.frame().pc;\n            let exit_early = self.vm.frame().exit_early();\n\n            if self.vm.handle_exception_at(pc) {\n                return ControlFlow::Continue(());\n            }\n\n            if exit_early {\n                return ControlFlow::Break(CompletionRecord::Throw(\n                    self.vm\n                        .pending_exception\n                        .take()\n                        .expect(\"Err must exist for a CompletionType::Throw\"),\n                ));\n            }\n\n            let Some(f) = self.vm.pop_frame() else {\n                break;\n            };\n            frame = f;\n        }\n        self.vm.frame_mut().environments.truncate(env_fp as usize);\n        self.vm.stack.truncate_to_frame(&frame);\n        ControlFlow::Continue(())\n    }\n\n    /// Runs the current frame to completion, yielding to the caller each time `budget`\n    /// \"clock cycles\" have passed.\n    #[allow(clippy::future_not_send)]\n    pub(crate) async fn run_async_with_budget(&mut self, budget: u32) -> CompletionRecord {\n        let mut runtime_budget: u32 = budget;\n\n        while let Some(byte) = self\n            .vm\n            .frame()\n            .code_block\n            .bytecode\n            .bytes\n            .get(self.vm.frame().pc as usize)\n        {\n            let opcode = Opcode::decode(*byte);\n\n            match self.execute_one(\n                |context, opcode| {\n                    let frame = context.vm.frame();\n                    let pc = frame.pc as usize;\n\n                    OPCODE_HANDLERS_BUDGET[opcode as usize](context, pc, &mut runtime_budget)\n                },\n                opcode,\n            ) {\n                ControlFlow::Continue(()) => {}\n                ControlFlow::Break(value) => return value,\n            }\n\n            if runtime_budget == 0 {\n                runtime_budget = budget;\n                yield_now().await;\n            }\n        }\n\n        CompletionRecord::Throw(JsError::from_native(JsNativeError::error()))\n    }\n\n    pub(crate) fn run(&mut self) -> CompletionRecord {\n        while let Some(byte) = self\n            .vm\n            .frame()\n            .code_block\n            .bytecode\n            .bytes\n            .get(self.vm.frame().pc as usize)\n        {\n            let opcode = Opcode::decode(*byte);\n\n            match self.execute_one(\n                |context, opcode| {\n                    let frame = context.vm.frame();\n                    let pc = frame.pc as usize;\n\n                    OPCODE_HANDLERS[opcode as usize](context, pc)\n                },\n                opcode,\n            ) {\n                ControlFlow::Continue(()) => {}\n                ControlFlow::Break(value) => return value,\n            }\n        }\n\n        CompletionRecord::Throw(JsError::from_native(JsNativeError::error()))\n    }\n\n    /// Checks if we haven't exceeded the defined runtime limits.\n    pub(crate) fn check_runtime_limits(&self) -> JsResult<()> {\n        // Must throw if the number of recursive calls exceeds the defined limit.\n        //\n        // `host_call_depth` accounts for nested host calls that re-enter the VM by invoking\n        // `Context::run()` recursively (for example, accessor calls).\n        // Subtract 1 to exclude the dummy frame at index 0.\n        let recursion_depth = (self.vm.frames.len() - 1).saturating_add(self.vm.host_call_depth);\n        if self.vm.runtime_limits.recursion_limit() <= recursion_depth {\n            return Err(RuntimeLimitError::Recursion.into());\n        }\n        // Must throw if the stack size exceeds the defined maximum length.\n        if self.vm.runtime_limits.stack_size_limit() <= self.vm.stack.stack.len() {\n            return Err(RuntimeLimitError::StackSize.into());\n        }\n\n        Ok(())\n    }\n}\n\n/// Yields once to the executor.\nfn yield_now() -> impl Future<Output = ()> {\n    struct YieldNow(bool);\n\n    impl Future for YieldNow {\n        type Output = ();\n\n        fn poll(mut self: Pin<&mut Self>, cx: &mut task::Context<'_>) -> task::Poll<Self::Output> {\n            if self.0 {\n                task::Poll::Ready(())\n            } else {\n                self.0 = true;\n                cx.waker().wake_by_ref();\n                task::Poll::Pending\n            }\n        }\n    }\n\n    YieldNow(false)\n}\n"
  },
  {
    "path": "core/engine/src/vm/opcode/args.rs",
    "content": "use thin_vec::ThinVec;\n\nuse super::{Address, IndexOperand, RegisterOperand};\n\n/// A trait for types that can be read from a byte slice.\n///\n/// # Safety\n///\n/// - The implementor must ensure that the type can be safely read from a byte slice.\npub(super) unsafe trait Readable: Copy + Sized {}\n\nunsafe impl Readable for u8 {}\nunsafe impl Readable for i8 {}\nunsafe impl Readable for u16 {}\nunsafe impl Readable for i16 {}\nunsafe impl Readable for u32 {}\nunsafe impl Readable for i32 {}\nunsafe impl Readable for u64 {}\nunsafe impl Readable for f32 {}\nunsafe impl Readable for f64 {}\nunsafe impl Readable for (u8, u8) {}\nunsafe impl Readable for (u8, i8) {}\nunsafe impl Readable for (u16, u16) {}\nunsafe impl Readable for (u16, i16) {}\nunsafe impl Readable for (u32, u32) {}\nunsafe impl Readable for (u32, i32) {}\nunsafe impl Readable for (u8, u8, u8) {}\nunsafe impl Readable for (u16, u16, u16) {}\nunsafe impl Readable for (u32, u32, u32) {}\nunsafe impl Readable for (u8, u8, u8, u8) {}\nunsafe impl Readable for (u16, u16, u16, u16) {}\nunsafe impl Readable for (u32, u32, u32, u32) {}\nunsafe impl Readable for (u32, u32, u32, u32, u32) {}\n\n#[inline(always)]\n#[track_caller]\n/// Read a value of type T from the byte slice at the given offset.\npub(super) fn read<T: Readable>(bytes: &[u8], offset: usize) -> (T, usize) {\n    let new_offset = offset + size_of::<T>();\n\n    assert!(bytes.len() >= new_offset, \"buffer too small to read type T\");\n\n    // Safety: The assertion above ensures that the slice is large enough to read T.\n    let result = unsafe { read_unchecked(bytes, offset) };\n\n    (result, new_offset)\n}\n\n#[inline(always)]\n#[track_caller]\n/// Read a value of type T from the byte slice at the given offset.\n///\n/// # Safety\n///\n/// - The caller must ensure that the byte slice is large enough to contain a value of type T at the given offset.\nunsafe fn read_unchecked<T: Readable>(bytes: &[u8], offset: usize) -> T {\n    unsafe { bytes.as_ptr().add(offset).cast::<T>().read_unaligned() }\n}\n\npub(crate) trait Argument: Sized + std::fmt::Debug {\n    /// Encode the argument into a byte slice\n    fn encode(self, bytes: &mut Vec<u8>);\n\n    /// Decode the argument from a byte slice\n    /// Returns the decoded argument and the new position after reading\n    fn decode(bytes: &[u8], pos: usize) -> (Self, usize);\n}\n\n#[inline(always)]\nfn write_u8(bytes: &mut Vec<u8>, value: u8) {\n    bytes.extend_from_slice(&value.to_le_bytes());\n}\n\n#[inline(always)]\nfn write_i8(bytes: &mut Vec<u8>, value: i8) {\n    bytes.extend_from_slice(&value.to_le_bytes());\n}\n\n#[inline(always)]\nfn write_u16(bytes: &mut Vec<u8>, value: u16) {\n    bytes.extend_from_slice(&value.to_le_bytes());\n}\n\n#[inline(always)]\nfn write_i16(bytes: &mut Vec<u8>, value: i16) {\n    bytes.extend_from_slice(&value.to_le_bytes());\n}\n\n#[inline(always)]\nfn write_u32(bytes: &mut Vec<u8>, value: u32) {\n    bytes.extend_from_slice(&value.to_le_bytes());\n}\n\n#[inline(always)]\nfn write_i32(bytes: &mut Vec<u8>, value: i32) {\n    bytes.extend_from_slice(&value.to_le_bytes());\n}\n\n#[inline(always)]\nfn write_u64(bytes: &mut Vec<u8>, value: u64) {\n    bytes.extend_from_slice(&value.to_le_bytes());\n}\n\nfn write_f32(bytes: &mut Vec<u8>, value: f32) {\n    bytes.extend_from_slice(&value.to_bits().to_le_bytes());\n}\n\nfn write_f64(bytes: &mut Vec<u8>, value: f64) {\n    bytes.extend_from_slice(&value.to_bits().to_le_bytes());\n}\n\nimpl<T: Argument> Argument for ThinVec<T> {\n    fn encode(self, bytes: &mut Vec<u8>) {\n        write_u32(bytes, self.len() as u32);\n        for arg in self {\n            arg.encode(bytes);\n        }\n    }\n\n    fn decode(bytes: &[u8], pos: usize) -> (Self, usize) {\n        let (len, mut pos) = read::<u32>(bytes, pos);\n        let total_len = len as usize;\n        let mut result = ThinVec::with_capacity(total_len);\n        for _ in 0..total_len {\n            let (arg, new_pos) = T::decode(bytes, pos);\n            result.push(arg);\n            pos = new_pos;\n        }\n        (result, pos)\n    }\n}\n\nimpl Argument for () {\n    fn encode(self, _: &mut Vec<u8>) {}\n\n    fn decode(_: &[u8], pos: usize) -> (Self, usize) {\n        ((), pos)\n    }\n}\n\nimpl Argument for IndexOperand {\n    fn encode(self, bytes: &mut Vec<u8>) {\n        write_u32(bytes, self.0);\n    }\n\n    fn decode(bytes: &[u8], pos: usize) -> (Self, usize) {\n        let (arg1, pos) = read::<u32>(bytes, pos);\n        (arg1.into(), pos)\n    }\n}\n\nimpl Argument for RegisterOperand {\n    fn encode(self, bytes: &mut Vec<u8>) {\n        write_u32(bytes, self.0);\n    }\n\n    fn decode(bytes: &[u8], pos: usize) -> (Self, usize) {\n        let (arg1, pos) = read::<u32>(bytes, pos);\n        (Self::new(arg1), pos)\n    }\n}\n\nimpl Argument for Address {\n    #[inline(always)]\n    fn encode(self, bytes: &mut Vec<u8>) {\n        write_u32(bytes, self.0);\n    }\n\n    #[inline(always)]\n    fn decode(bytes: &[u8], pos: usize) -> (Self, usize) {\n        let (value, pos) = read::<u32>(bytes, pos);\n        (Self::new(value), pos)\n    }\n}\n\nmacro_rules! impl_argument_for_tuple {\n    ($( $i: ident  $t: ident ),*) => {\n        impl<$( $t: Argument, )*> Argument for ($( $t, )*) {\n            #[inline(always)]\n            fn encode(self, bytes: &mut Vec<u8>) {\n                let ($($i, )*) = self;\n                $( $i.encode(bytes); )*\n            }\n\n            #[inline(always)]\n            fn decode(bytes: &[u8], pos: usize) -> (Self, usize) {\n                $( let ($i, pos) = $t::decode(bytes, pos); )*\n                (($($i,)*), pos)\n            }\n        }\n    };\n}\n\nimpl_argument_for_tuple!(a A);\nimpl_argument_for_tuple!(a A, b B);\nimpl_argument_for_tuple!(a A, b B, c C);\nimpl_argument_for_tuple!(a A, b B, c C, d D);\nimpl_argument_for_tuple!(a A, b B, c C, d D, e E);\n\nmacro_rules! impl_argument_for_int {\n    ($( $t: ty )*) => {\n        $(\n        impl Argument for $t {\n            #[inline(always)]\n            fn encode(self, bytes: &mut Vec<u8>) {\n                paste::paste! {\n                    [<write_ $t>](bytes, self);\n                }\n            }\n\n            #[inline(always)]\n            fn decode(bytes: &[u8], pos: usize) -> (Self, usize) {\n                read::<$t>(bytes, pos)\n            }\n        }\n        )*\n    };\n}\n\nimpl_argument_for_int!(u8 u16 u32 u64 i8 i16 i32 f32 f64);\n\n#[cfg(test)]\nmod tests {\n    use super::{Address, Argument, IndexOperand, RegisterOperand};\n    use std::mem::size_of;\n    use thin_vec::ThinVec;\n\n    fn round_trip<T: Argument + PartialEq + Clone>(value: &T) {\n        let mut bytes = Vec::new();\n        value.clone().encode(&mut bytes);\n        let (decoded, pos) = T::decode(&bytes, 0);\n        assert_eq!(decoded, *value);\n        assert_eq!(pos, bytes.len());\n    }\n\n    fn round_trip_eq<T: Argument + Clone, F: Fn(&T, &T) -> bool>(value: &T, eq: F) {\n        let mut bytes = Vec::new();\n        value.clone().encode(&mut bytes);\n        let (decoded, pos) = T::decode(&bytes, 0);\n        assert!(eq(&decoded, value));\n        assert_eq!(pos, bytes.len());\n    }\n\n    #[test]\n    fn test_unit_round_trip() {\n        round_trip(&());\n    }\n\n    #[test]\n    fn test_address_round_trip() {\n        round_trip_eq(&Address::new(0), |a, b| u32::from(*a) == u32::from(*b));\n        round_trip_eq(&Address::new(0x1234_5678), |a, b| {\n            u32::from(*a) == u32::from(*b)\n        });\n    }\n\n    #[test]\n    fn test_register_operand_round_trip() {\n        round_trip_eq(&RegisterOperand::new(0), |a, b| {\n            u32::from(*a) == u32::from(*b)\n        });\n        round_trip_eq(&RegisterOperand::new(255), |a, b| {\n            u32::from(*a) == u32::from(*b)\n        });\n    }\n\n    #[test]\n    fn test_varying_operand_round_trip() {\n        round_trip_eq(&IndexOperand::new(0), |a, b| u32::from(*a) == u32::from(*b));\n        round_trip_eq(&IndexOperand::new(0xFFFF_FFFF), |a, b| {\n            u32::from(*a) == u32::from(*b)\n        });\n    }\n\n    #[test]\n    fn test_primitive_round_trips() {\n        round_trip(&0u8);\n        round_trip(&255u8);\n        round_trip(&0i8);\n        round_trip(&(-128i8));\n        round_trip(&0u16);\n        round_trip(&0xFFFFu16);\n        round_trip(&(-32768i16));\n        round_trip(&0u32);\n        round_trip(&0xFFFF_FFFFu32);\n        round_trip(&0i32);\n        round_trip(&i32::MIN);\n        round_trip(&0u64);\n        round_trip(&0xFFFF_FFFF_FFFF_FFFFu64);\n        round_trip(&0.0f32);\n        round_trip(&1.5f32);\n        round_trip(&0.0f64);\n        round_trip(&1.5f64);\n    }\n\n    #[test]\n    fn test_tuple_round_trips() {\n        round_trip(&(0u8, 1u8));\n        round_trip(&(0u32, 1u32));\n        let tuple = (Address::new(0), RegisterOperand::new(1));\n        round_trip_eq(&tuple, |a, b| {\n            u32::from(a.0) == u32::from(b.0) && u32::from(a.1) == u32::from(b.1)\n        });\n    }\n\n    #[test]\n    fn test_thin_vec_round_trip() {\n        let v: ThinVec<u32> = ThinVec::new();\n        round_trip(&v);\n        let v: ThinVec<u32> = [1u32, 2, 3].into_iter().collect();\n        round_trip(&v);\n        let v: ThinVec<RegisterOperand> = [RegisterOperand::new(0), RegisterOperand::new(1)]\n            .into_iter()\n            .collect();\n        round_trip_eq(&v, |a, b| {\n            a.len() == b.len()\n                && a.iter()\n                    .zip(b.iter())\n                    .all(|(x, y)| u32::from(*x) == u32::from(*y))\n        });\n    }\n\n    #[test]\n    fn test_encoded_size_matches_type_size() {\n        let mut bytes = Vec::new();\n        Address::new(0xDEAD_BEEF).encode(&mut bytes);\n        assert_eq!(bytes.len(), size_of::<u32>());\n\n        bytes.clear();\n        (0u64).encode(&mut bytes);\n        assert_eq!(bytes.len(), size_of::<u64>());\n\n        bytes.clear();\n        (0.0f64).encode(&mut bytes);\n        assert_eq!(bytes.len(), size_of::<f64>());\n    }\n\n    #[test]\n    #[should_panic(expected = \"buffer too small\")]\n    fn decode_truncated_buffer_panics() {\n        let bytes = [0u8; 2];\n        let _ = u32::decode(&bytes, 0);\n    }\n}\n"
  },
  {
    "path": "core/engine/src/vm/opcode/arguments.rs",
    "content": "use super::{Operation, RegisterOperand};\nuse crate::{\n    Context,\n    builtins::function::arguments::{MappedArguments, UnmappedArguments},\n};\n\n/// `CreateMappedArgumentsObject` implements the Opcode Operation for `Opcode::CreateMappedArgumentsObject`\n///\n/// Operation:\n///  - Create a mapped arguments object and store it in a register.\n#[derive(Debug, Clone, Copy)]\npub(crate) struct CreateMappedArgumentsObject;\n\nimpl CreateMappedArgumentsObject {\n    #[inline(always)]\n    pub(super) fn operation(value: RegisterOperand, context: &mut Context) {\n        let frame = context.vm.frame();\n        let function_object = context\n            .vm\n            .stack\n            .get_function(context.vm.frame())\n            .expect(\"there should be a function object\");\n        let code = frame.code_block().clone();\n        let args = context.vm.stack.get_arguments(context.vm.frame());\n        let env = {\n            let frame = context.vm.frame();\n            frame\n                .environments\n                .current_declarative_ref(frame.realm.environment())\n                .expect(\"must be declarative\")\n                .clone()\n        };\n        let arguments = MappedArguments::new(\n            &function_object,\n            &code.mapped_arguments_binding_indices,\n            args,\n            &env,\n            context,\n        );\n        context.vm.set_register(value.into(), arguments.into());\n    }\n}\n\nimpl Operation for CreateMappedArgumentsObject {\n    const NAME: &'static str = \"CreateMappedArgumentsObject\";\n    const INSTRUCTION: &'static str = \"INST - CreateMappedArgumentsObject\";\n    const COST: u8 = 8;\n}\n\n/// `CreateUnmappedArgumentsObject` implements the Opcode Operation for `Opcode::CreateUnmappedArgumentsObject`\n///\n/// Operation:\n///  - Create an unmapped arguments object and store it in a register.\n#[derive(Debug, Clone, Copy)]\npub(crate) struct CreateUnmappedArgumentsObject;\n\nimpl CreateUnmappedArgumentsObject {\n    #[inline(always)]\n    pub(super) fn operation(dst: RegisterOperand, context: &mut Context) {\n        let args = context.vm.stack.get_arguments(context.vm.frame()).to_vec();\n        let arguments = UnmappedArguments::new(&args, context);\n        context.vm.set_register(dst.into(), arguments.into());\n    }\n}\n\nimpl Operation for CreateUnmappedArgumentsObject {\n    const NAME: &'static str = \"CreateUnmappedArgumentsObject\";\n    const INSTRUCTION: &'static str = \"INST - CreateUnmappedArgumentsObject\";\n    const COST: u8 = 4;\n}\n"
  },
  {
    "path": "core/engine/src/vm/opcode/await/mod.rs",
    "content": "use super::RegisterOperand;\nuse crate::{\n    Context, JsArgs, JsExpect, JsResult, JsValue,\n    builtins::{\n        Promise, async_generator::AsyncGenerator, generator::GeneratorContext,\n        promise::PromiseCapability,\n    },\n    js_string,\n    native_function::NativeFunction,\n    object::FunctionObjectBuilder,\n    vm::{CompletionRecord, GeneratorResumeKind, opcode::Operation},\n};\nuse boa_gc::Gc;\nuse std::{cell::Cell, ops::ControlFlow};\n\n/// `Await` implements the Opcode Operation for `Opcode::Await`\n///\n/// Operation:\n///  - Stops the current Async function and schedules it to resume later.\n#[derive(Debug, Clone, Copy)]\npub(crate) struct Await;\n\nimpl Await {\n    #[inline(always)]\n    pub(super) fn operation(\n        value: RegisterOperand,\n        context: &mut Context,\n    ) -> ControlFlow<CompletionRecord> {\n        let value = context.vm.get_register(value.into());\n\n        // 2. Let promise be ? PromiseResolve(%Promise%, value).\n        let promise = match Promise::promise_resolve(\n            &context.intrinsics().constructors().promise().constructor(),\n            value.clone(),\n            context,\n        ) {\n            Ok(promise) => promise\n                .downcast::<Promise>()\n                .expect(\"%Promise% constructor must return a `Promise` object\"),\n            Err(err) => return context.handle_error(err),\n        };\n\n        let return_value = context\n            .vm\n            .get_promise_capability()\n            .ok()\n            .map(|cap| JsValue::from(cap.promise))\n            .unwrap_or_default();\n\n        let r#gen = GeneratorContext::from_current(context, None);\n\n        let captures = Gc::new(Cell::new(Some(r#gen)));\n\n        // 3. Let fulfilledClosure be a new Abstract Closure with parameters (value) that captures asyncContext and performs the following steps when called:\n        // 4. Let onFulfilled be CreateBuiltinFunction(fulfilledClosure, 1, \"\", « »).\n        let on_fulfilled = FunctionObjectBuilder::new(\n            context.realm(),\n            NativeFunction::from_copy_closure_with_captures(\n                |_this, args, captures, context| {\n                    // a. Let prevContext be the running execution context.\n                    // b. Suspend prevContext.\n                    // c. Push asyncContext onto the execution context stack; asyncContext is now the running execution context.\n                    // d. Resume the suspended evaluation of asyncContext using NormalCompletion(value) as the result of the operation that suspended it.\n                    let mut r#gen = captures.take().expect(\"should only run once\");\n\n                    // NOTE: We need to get the object before resuming, since it could clear the stack.\n                    let async_generator = r#gen.async_generator_object()?;\n\n                    r#gen.resume(\n                        Some(args.get_or_undefined(0).clone()),\n                        GeneratorResumeKind::Normal,\n                        context,\n                    );\n\n                    if let Some(async_generator) = async_generator {\n                        async_generator\n                            .downcast_mut::<AsyncGenerator>()\n                            .expect(\"must be async generator\")\n                            .context = Some(r#gen);\n                    }\n\n                    // e. Assert: When we reach this step, asyncContext has already been removed from the execution context stack and prevContext is the currently running execution context.\n                    // f. Return undefined.\n                    Ok(JsValue::undefined())\n                },\n                captures.clone(),\n            ),\n        )\n        .name(js_string!())\n        .length(1)\n        .build();\n\n        // 5. Let rejectedClosure be a new Abstract Closure with parameters (reason) that captures asyncContext and performs the following steps when called:\n        // 6. Let onRejected be CreateBuiltinFunction(rejectedClosure, 1, \"\", « »).\n        let on_rejected = FunctionObjectBuilder::new(\n            context.realm(),\n            NativeFunction::from_copy_closure_with_captures(\n                |_this, args, captures, context| {\n                    // a. Let prevContext be the running execution context.\n                    // b. Suspend prevContext.\n                    // c. Push asyncContext onto the execution context stack; asyncContext is now the running execution context.\n                    // d. Resume the suspended evaluation of asyncContext using ThrowCompletion(reason) as the result of the operation that suspended it.\n                    // e. Assert: When we reach this step, asyncContext has already been removed from the execution context stack and prevContext is the currently running execution context.\n                    // f. Return undefined.\n                    let mut r#gen = captures.take().expect(\"should only run once\");\n\n                    // NOTE: We need to get the object before resuming, since it could clear the stack.\n                    let async_generator = r#gen.async_generator_object()?;\n\n                    r#gen.resume(\n                        Some(args.get_or_undefined(0).clone()),\n                        GeneratorResumeKind::Throw,\n                        context,\n                    );\n\n                    if let Some(async_generator) = async_generator {\n                        async_generator\n                            .downcast_mut::<AsyncGenerator>()\n                            .expect(\"must be async generator\")\n                            .context = Some(r#gen);\n                    }\n\n                    Ok(JsValue::undefined())\n                },\n                captures,\n            ),\n        )\n        .name(js_string!())\n        .length(1)\n        .build();\n\n        // 7. Perform PerformPromiseThen(promise, onFulfilled, onRejected).\n        Promise::perform_promise_then(\n            &promise,\n            Some(on_fulfilled),\n            Some(on_rejected),\n            None,\n            context,\n        );\n\n        context.vm.set_return_value(return_value);\n        context.handle_yield()\n    }\n}\n\nimpl Operation for Await {\n    const NAME: &'static str = \"Await\";\n    const INSTRUCTION: &'static str = \"INST - Await\";\n    const COST: u8 = 5;\n}\n\n/// `CreatePromiseCapability` implements the Opcode Operation for `Opcode::CreatePromiseCapability`\n///\n/// Operation:\n///  - Create a promise capacity for an async function.\n#[derive(Debug, Clone, Copy)]\npub(crate) struct CreatePromiseCapability;\n\nimpl CreatePromiseCapability {\n    #[inline(always)]\n    pub(super) fn operation((): (), context: &mut Context) -> JsResult<()> {\n        let promise_capability = PromiseCapability::new(\n            &context.intrinsics().constructors().promise().constructor(),\n            context,\n        )\n        .js_expect(\"cannot fail per spec\")?;\n\n        context.vm.set_promise_capability(promise_capability)\n    }\n}\n\nimpl Operation for CreatePromiseCapability {\n    const NAME: &'static str = \"CreatePromiseCapability\";\n    const INSTRUCTION: &'static str = \"INST - CreatePromiseCapability\";\n    const COST: u8 = 8;\n}\n"
  },
  {
    "path": "core/engine/src/vm/opcode/binary_ops/logical.rs",
    "content": "use crate::{\n    Context,\n    vm::opcode::{Address, Operation, RegisterOperand},\n};\n\n/// `LogicalAnd` implements the Opcode Operation for `Opcode::LogicalAnd`\n///\n/// Operation:\n///  - Binary logical `&&` operation\n#[derive(Debug, Clone, Copy)]\npub(crate) struct LogicalAnd;\n\nimpl LogicalAnd {\n    #[inline(always)]\n    pub(crate) fn operation((exit, lhs): (Address, RegisterOperand), context: &mut Context) {\n        let lhs = context.vm.get_register(lhs.into());\n        if !lhs.to_boolean() {\n            context.vm.frame_mut().pc = u32::from(exit);\n        }\n    }\n}\n\nimpl Operation for LogicalAnd {\n    const NAME: &'static str = \"LogicalAnd\";\n    const INSTRUCTION: &'static str = \"INST - LogicalAnd\";\n    const COST: u8 = 1;\n}\n\n/// `LogicalOr` implements the Opcode Operation for `Opcode::LogicalOr`\n///\n/// Operation:\n///  - Binary logical `||` operation\n#[derive(Debug, Clone, Copy)]\npub(crate) struct LogicalOr;\n\nimpl LogicalOr {\n    #[inline(always)]\n    pub(crate) fn operation((exit, lhs): (Address, RegisterOperand), context: &mut Context) {\n        let lhs = context.vm.get_register(lhs.into());\n        if lhs.to_boolean() {\n            context.vm.frame_mut().pc = u32::from(exit);\n        }\n    }\n}\n\nimpl Operation for LogicalOr {\n    const NAME: &'static str = \"LogicalOr\";\n    const INSTRUCTION: &'static str = \"INST - LogicalOr\";\n    const COST: u8 = 1;\n}\n\n/// `Coalesce` implements the Opcode Operation for `Opcode::Coalesce`\n///\n/// Operation:\n///  - Binary logical `||` operation\n#[derive(Debug, Clone, Copy)]\npub(crate) struct Coalesce;\n\nimpl Coalesce {\n    #[inline(always)]\n    pub(crate) fn operation((exit, lhs): (Address, RegisterOperand), context: &mut Context) {\n        let lhs = context.vm.get_register(lhs.into());\n        if !lhs.is_null_or_undefined() {\n            context.vm.frame_mut().pc = u32::from(exit);\n        }\n    }\n}\n\nimpl Operation for Coalesce {\n    const NAME: &'static str = \"Coalesce\";\n    const INSTRUCTION: &'static str = \"INST - Coalesce\";\n    const COST: u8 = 1;\n}\n"
  },
  {
    "path": "core/engine/src/vm/opcode/binary_ops/macro_defined.rs",
    "content": "use crate::{\n    Context, JsResult, JsValue,\n    vm::opcode::{Operation, RegisterOperand},\n};\n\nmacro_rules! implement_bin_ops {\n    ($name:ident, $op:ident, $doc_string:literal $(, $fast_fn: ident)?) => {\n        #[doc= concat!(\"`\", stringify!($name), \"` implements the `OpCode` Operation for `Opcode::\", stringify!($name), \"`\\n\")]\n        #[doc= \"\\n\"]\n        #[doc=\"Operation:\\n\"]\n        #[doc= concat!(\" - \", $doc_string)]\n        #[derive(Debug, Clone, Copy)]\n        pub(crate) struct $name;\n\n        impl $name {\n            #[inline]\n            pub(crate) fn operation(\n                (dst, lhs, rhs): (RegisterOperand, RegisterOperand, RegisterOperand),\n                context: &mut Context,\n            ) -> JsResult<()> {\n                let lhs = context.vm.get_register(lhs.into());\n                let rhs = context.vm.get_register(rhs.into());\n\n                $(\n                // Fast path: try numeric operation without cloning.\n                if let Some(value) = JsValue::$fast_fn(lhs, rhs) {\n                    context.vm.set_register(dst.into(), value.into());\n                    return Ok(());\n                }\n                )?\n\n                // Slow path: clone and use full method with type coercion.\n                let lhs = lhs.clone();\n                let rhs = rhs.clone();\n                let value = lhs.$op(&rhs, context)?;\n                context.vm.set_register(dst.into(), value.into());\n                Ok(())\n            }\n        }\n\n        impl Operation for $name {\n            const NAME: &'static str = stringify!($name);\n            const INSTRUCTION: &'static str = stringify!(\"INST - \" + $name);\n            const COST: u8 = 2;\n        }\n    };\n}\n\nimplement_bin_ops!(Add, add, \"Binary `+` operator.\", add_fast);\nimplement_bin_ops!(Sub, sub, \"Binary `-` operator.\", sub_fast);\nimplement_bin_ops!(Mul, mul, \"Binary `*` operator.\", mul_fast);\nimplement_bin_ops!(Div, div, \"Binary `/` operator.\", div_fast);\nimplement_bin_ops!(Pow, pow, \"Binary `**` operator.\", pow_fast);\nimplement_bin_ops!(Mod, rem, \"Binary `%` operator.\", rem_fast);\nimplement_bin_ops!(BitAnd, bitand, \"Binary `&` operator.\", bitand_fast);\nimplement_bin_ops!(BitOr, bitor, \"Binary `|` operator.\", bitor_fast);\nimplement_bin_ops!(BitXor, bitxor, \"Binary `^` operator.\", bitxor_fast);\nimplement_bin_ops!(ShiftLeft, shl, \"Binary `<<` operator.\", shl_fast);\nimplement_bin_ops!(ShiftRight, shr, \"Binary `>>` operator.\", shr_fast);\nimplement_bin_ops!(\n    UnsignedShiftRight,\n    ushr,\n    \"Binary `>>>` operator.\",\n    ushr_fast\n);\nimplement_bin_ops!(Eq, equals, \"Binary `==` operator.\", equals_fast);\nimplement_bin_ops!(NotEq, not_equals, \"Binary `!=` operator.\", not_equals_fast);\nimplement_bin_ops!(GreaterThan, gt, \"Binary `>` operator.\", gt_fast);\nimplement_bin_ops!(GreaterThanOrEq, ge, \"Binary `>=` operator.\", ge_fast);\nimplement_bin_ops!(LessThan, lt, \"Binary `<` operator.\", lt_fast);\nimplement_bin_ops!(LessThanOrEq, le, \"Binary `<=` operator.\", le_fast);\nimplement_bin_ops!(InstanceOf, instance_of, \"Binary `instanceof` operator.\");\n"
  },
  {
    "path": "core/engine/src/vm/opcode/binary_ops/mod.rs",
    "content": "use super::{IndexOperand, RegisterOperand};\nuse crate::{Context, JsResult, error::JsNativeError, vm::opcode::Operation};\n\npub(crate) mod logical;\npub(crate) mod macro_defined;\n\npub(crate) use logical::*;\npub(crate) use macro_defined::*;\n\n/// `StrictEq` implements the Opcode Operation for `Opcode::StrictEq`\n///\n/// Operation:\n///  - Binary `===` operation\n#[derive(Debug, Clone, Copy)]\npub(crate) struct StrictEq;\n\nimpl StrictEq {\n    #[inline(always)]\n    pub(super) fn operation(\n        (dst, lhs, rhs): (RegisterOperand, RegisterOperand, RegisterOperand),\n        context: &mut Context,\n    ) {\n        let lhs = context.vm.get_register(lhs.into());\n        let rhs = context.vm.get_register(rhs.into());\n        let value = lhs.strict_equals(rhs);\n        context.vm.set_register(dst.into(), value.into());\n    }\n}\n\nimpl Operation for StrictEq {\n    const NAME: &'static str = \"StrictEq\";\n    const INSTRUCTION: &'static str = \"INST - StrictEq\";\n    const COST: u8 = 2;\n}\n\n/// `StrictNotEq` implements the Opcode Operation for `Opcode::StrictNotEq`\n///\n/// Operation:\n///  - Binary `!==` operation\n#[derive(Debug, Clone, Copy)]\npub(crate) struct StrictNotEq;\n\nimpl StrictNotEq {\n    #[inline(always)]\n    pub(super) fn operation(\n        (dst, lhs, rhs): (RegisterOperand, RegisterOperand, RegisterOperand),\n        context: &mut Context,\n    ) {\n        let lhs = context.vm.get_register(lhs.into());\n        let rhs = context.vm.get_register(rhs.into());\n        let value = !lhs.strict_equals(rhs);\n        context.vm.set_register(dst.into(), value.into());\n    }\n}\n\nimpl Operation for StrictNotEq {\n    const NAME: &'static str = \"StrictNotEq\";\n    const INSTRUCTION: &'static str = \"INST - StrictNotEq\";\n    const COST: u8 = 2;\n}\n\n/// `In` implements the Opcode Operation for `Opcode::In`\n///\n/// Operation:\n///  - Binary `in` operation\n#[derive(Debug, Clone, Copy)]\npub(crate) struct In;\n\nimpl In {\n    #[inline(always)]\n    pub(super) fn operation(\n        (dst, lhs, rhs): (RegisterOperand, RegisterOperand, RegisterOperand),\n        context: &mut Context,\n    ) -> JsResult<()> {\n        let rhs = context.vm.get_register(rhs.into()).clone();\n        let Some(rhs) = rhs.as_object() else {\n            return Err(JsNativeError::typ()\n                .with_message(format!(\n                    \"right-hand side of 'in' should be an object, got `{}`\",\n                    rhs.type_of()\n                ))\n                .into());\n        };\n        let lhs = context.vm.get_register(lhs.into()).clone();\n        let key = lhs.to_property_key(context)?;\n        let value = rhs.has_property(key, context)?;\n        context.vm.set_register(dst.into(), value.into());\n        Ok(())\n    }\n}\n\nimpl Operation for In {\n    const NAME: &'static str = \"In\";\n    const INSTRUCTION: &'static str = \"INST - In\";\n    const COST: u8 = 3;\n}\n\n/// `InPrivate` implements the Opcode Operation for `Opcode::InPrivate`\n///\n/// Operation:\n///  - Binary `in` operation for private names.\n#[derive(Debug, Clone, Copy)]\npub(crate) struct InPrivate;\n\nimpl InPrivate {\n    #[inline(always)]\n    pub(super) fn operation(\n        (dst, index, rhs): (RegisterOperand, IndexOperand, RegisterOperand),\n        context: &mut Context,\n    ) -> JsResult<()> {\n        let name = context\n            .vm\n            .frame()\n            .code_block()\n            .constant_string(index.into());\n        let rhs = context.vm.get_register(rhs.into());\n\n        let Some(rhs) = rhs.as_object() else {\n            return Err(JsNativeError::typ()\n                .with_message(format!(\n                    \"right-hand side of 'in' should be an object, got `{}`\",\n                    rhs.type_of()\n                ))\n                .into());\n        };\n\n        let name = context\n            .vm\n            .frame()\n            .environments\n            .resolve_private_identifier(name)\n            .expect(\"private name must be in environment\");\n\n        let value = rhs.private_element_find(&name, true, true).is_some();\n\n        context.vm.set_register(dst.into(), value.into());\n        Ok(())\n    }\n}\n\nimpl Operation for InPrivate {\n    const NAME: &'static str = \"InPrivate\";\n    const INSTRUCTION: &'static str = \"INST - InPrivate\";\n    const COST: u8 = 4;\n}\n"
  },
  {
    "path": "core/engine/src/vm/opcode/call/mod.rs",
    "content": "use std::{cell::RefCell, mem::MaybeUninit};\n\nuse boa_string::JsString;\nuse dynify::Dynify;\n\nuse super::{IndexOperand, RegisterOperand};\nuse crate::{\n    Context, JsError, JsObject, JsResult, JsValue, NativeFunction,\n    builtins::{Promise, promise::PromiseCapability},\n    error::JsNativeError,\n    job::NativeAsyncJob,\n    module::{ImportAttribute, ModuleKind, ModuleRequest, Referrer},\n    object::FunctionObjectBuilder,\n    vm::opcode::Operation,\n};\n\n/// `CallEval` implements the Opcode Operation for `Opcode::CallEval`\n///\n/// Operation:\n///  - Call a function named \"eval\".\n#[derive(Debug, Clone, Copy)]\npub(crate) struct CallEval;\n\nimpl CallEval {\n    #[inline(always)]\n    pub(super) fn operation(\n        (argument_count, scope_index): (IndexOperand, IndexOperand),\n        context: &mut Context,\n    ) -> JsResult<()> {\n        let func = context\n            .vm\n            .stack\n            .calling_convention_get_function(argument_count.into());\n\n        let Some(object) = func.as_object() else {\n            return Err(JsNativeError::typ()\n                .with_message(\"not a callable function\")\n                .into());\n        };\n\n        // Taken from `13.3.6.1 Runtime Semantics: Evaluation`\n        //            `CallExpression : CoverCallExpressionAndAsyncArrowHead`\n        //\n        // <https://tc39.es/ecma262/#sec-function-calls-runtime-semantics-evaluation>\n        //\n        // 6. If ref is a Reference Record, IsPropertyReference(ref) is false, and ref.[[ReferencedName]] is \"eval\", then\n        //     a. If SameValue(func, %eval%) is true, then\n        let eval = context.intrinsics().objects().eval();\n        if JsObject::equals(&object, &eval) {\n            let arguments = context\n                .vm\n                .stack\n                .calling_convention_pop_arguments(argument_count.into());\n            let _func = context.vm.stack.pop();\n            let _this = context.vm.stack.pop();\n            if let Some(x) = arguments.first() {\n                // i. Let argList be ? ArgumentListEvaluation of arguments.\n                // ii. If argList has no elements, return undefined.\n                // iii. Let evalArg be the first element of argList.\n                // iv. If the source text matched by this CallExpression is strict mode code,\n                //     let strictCaller be true. Otherwise let strictCaller be false.\n                // v. Return ? PerformEval(evalArg, strictCaller, true).\n                let strict = context.vm.frame().code_block.strict();\n                let scope = context\n                    .vm\n                    .frame()\n                    .code_block()\n                    .constant_scope(scope_index.into());\n                let result = crate::builtins::eval::Eval::perform_eval(\n                    x,\n                    true,\n                    Some(scope),\n                    strict,\n                    context,\n                )?;\n                context.vm.stack.push(result);\n            } else {\n                // NOTE: This is a deviation from the spec, to optimize the case when we dont pass anything to `eval`.\n                context.vm.stack.push(JsValue::undefined());\n            }\n\n            return Ok(());\n        }\n\n        object.__call__(argument_count.into()).resolve(context)?;\n        Ok(())\n    }\n}\n\nimpl Operation for CallEval {\n    const NAME: &'static str = \"CallEval\";\n    const INSTRUCTION: &'static str = \"INST - CallEval\";\n    const COST: u8 = 5;\n}\n\n/// `CallEvalSpread` implements the Opcode Operation for `Opcode::CallEvalSpread`\n///\n/// Operation:\n///  - Call a function named \"eval\" where the arguments contain spreads.\n#[derive(Debug, Clone, Copy)]\npub(crate) struct CallEvalSpread;\n\nimpl CallEvalSpread {\n    #[inline(always)]\n    pub(super) fn operation(index: IndexOperand, context: &mut Context) -> JsResult<()> {\n        // Get the arguments that are stored as an array object on the stack.\n        let arguments_array = context.vm.stack.pop();\n        let arguments_array_object = arguments_array\n            .as_object()\n            .expect(\"arguments array in call spread function must be an object\");\n        let arguments = arguments_array_object\n            .borrow()\n            .properties()\n            .to_dense_indexed_properties()\n            .expect(\"arguments array in call spread function must be dense\");\n\n        let func = context.vm.stack.calling_convention_get_function(0);\n\n        let Some(object) = func.as_object() else {\n            return Err(JsNativeError::typ()\n                .with_message(\"not a callable function\")\n                .into());\n        };\n        // Taken from `13.3.6.1 Runtime Semantics: Evaluation`\n        //            `CallExpression : CoverCallExpressionAndAsyncArrowHead`\n        //\n        // <https://tc39.es/ecma262/#sec-function-calls-runtime-semantics-evaluation>\n        //\n        // 6. If ref is a Reference Record, IsPropertyReference(ref) is false, and ref.[[ReferencedName]] is \"eval\", then\n        //     a. If SameValue(func, %eval%) is true, then\n        let eval = context.intrinsics().objects().eval();\n        if JsObject::equals(&object, &eval) {\n            let _func = context.vm.stack.pop();\n            let _this = context.vm.stack.pop();\n            if let Some(x) = arguments.first() {\n                // i. Let argList be ? ArgumentListEvaluation of arguments.\n                // ii. If argList has no elements, return undefined.\n                // iii. Let evalArg be the first element of argList.\n                // iv. If the source text matched by this CallExpression is strict mode code,\n                //     let strictCaller be true. Otherwise let strictCaller be false.\n                // v. Return ? PerformEval(evalArg, strictCaller, true).\n                let strict = context.vm.frame().code_block.strict();\n                let scope = context.vm.frame().code_block().constant_scope(index.into());\n                let result = crate::builtins::eval::Eval::perform_eval(\n                    x,\n                    true,\n                    Some(scope),\n                    strict,\n                    context,\n                )?;\n                context.vm.stack.push(result);\n            } else {\n                // NOTE: This is a deviation from the spec, to optimize the case when we dont pass anything to `eval`.\n                context.vm.stack.push(JsValue::undefined());\n            }\n\n            return Ok(());\n        }\n\n        let argument_count = arguments.len();\n        context\n            .vm\n            .stack\n            .calling_convention_push_arguments(&arguments);\n\n        object.__call__(argument_count).resolve(context)?;\n        Ok(())\n    }\n}\n\nimpl Operation for CallEvalSpread {\n    const NAME: &'static str = \"CallEvalSpread\";\n    const INSTRUCTION: &'static str = \"INST - CallEvalSpread\";\n    const COST: u8 = 5;\n}\n\n/// `Call` implements the Opcode Operation for `Opcode::Call`\n///\n/// Operation:\n///  - Call a function\n#[derive(Debug, Clone, Copy)]\npub(crate) struct Call;\n\nimpl Call {\n    #[inline(always)]\n    pub(super) fn operation(argument_count: IndexOperand, context: &mut Context) -> JsResult<()> {\n        let func = context\n            .vm\n            .stack\n            .calling_convention_get_function(argument_count.into());\n\n        let Some(object) = func.as_object() else {\n            return Err(Self::handle_not_callable());\n        };\n\n        object.__call__(argument_count.into()).resolve(context)?;\n\n        Ok(())\n    }\n\n    #[cold]\n    #[inline(never)]\n    fn handle_not_callable() -> JsError {\n        JsNativeError::typ()\n            .with_message(\"not a callable function\")\n            .into()\n    }\n}\n\nimpl Operation for Call {\n    const NAME: &'static str = \"Call\";\n    const INSTRUCTION: &'static str = \"INST - Call\";\n    const COST: u8 = 3;\n}\n\n#[derive(Debug, Clone, Copy)]\npub(crate) struct CallSpread;\n\nimpl CallSpread {\n    #[inline(always)]\n    pub(super) fn operation((): (), context: &mut Context) -> JsResult<()> {\n        // Get the arguments that are stored as an array object on the stack.\n        let arguments_array = context.vm.stack.pop();\n        let arguments_array_object = arguments_array\n            .as_object()\n            .expect(\"arguments array in call spread function must be an object\");\n        let arguments = arguments_array_object\n            .borrow()\n            .properties()\n            .to_dense_indexed_properties()\n            .expect(\"arguments array in call spread function must be dense\");\n\n        let argument_count = arguments.len();\n        context\n            .vm\n            .stack\n            .calling_convention_push_arguments(&arguments);\n\n        let func = context\n            .vm\n            .stack\n            .calling_convention_get_function(argument_count);\n\n        let Some(object) = func.as_object() else {\n            return Err(JsNativeError::typ()\n                .with_message(\"not a callable function\")\n                .into());\n        };\n\n        object.__call__(argument_count).resolve(context)?;\n        Ok(())\n    }\n}\n\nimpl Operation for CallSpread {\n    const NAME: &'static str = \"CallSpread\";\n    const INSTRUCTION: &'static str = \"INST - CallSpread\";\n    const COST: u8 = 3;\n}\n\n/// Parses the import attributes from the options object.\nfn parse_import_attributes(\n    specifier: JsString,\n    options: &JsValue,\n    context: &mut Context,\n) -> JsResult<ModuleRequest> {\n    // Taken from `EvaluateImportCall`\n    //\n    // <https://tc39.es/ecma262/#sec-evaluate-import-call>\n\n    // 1. Let attributes be a new empty List.\n    let mut attributes = Vec::new();\n\n    // 2. If options is not undefined, then\n    if !options.is_undefined() {\n        // a. If Type(options) is not Object, throw a TypeError exception.\n        let Some(options_obj) = options.as_object() else {\n            return Err(JsNativeError::typ()\n                .with_message(\"import options must be an object or undefined\")\n                .into());\n        };\n\n        // b. Let attributesObj be ? Get(options, \"with\").\n        let attributes_obj = options_obj.get(crate::js_str!(\"with\"), context)?;\n\n        // c. If attributesObj is not undefined, then\n        if !attributes_obj.is_undefined() {\n            // i. If Type(attributesObj) is not Object, throw a TypeError exception.\n            let Some(attributes_obj) = attributes_obj.as_object() else {\n                return Err(JsNativeError::typ()\n                    .with_message(\"the 'with' option must be an object\")\n                    .into());\n            };\n\n            // ii. Let entries be ? EnumerableOwnProperties(attributesObj, \"key+value\").\n            let entries = attributes_obj.enumerable_own_property_names(\n                crate::property::PropertyNameKind::KeyAndValue,\n                context,\n            )?;\n\n            // iii. For each entry in entries, do\n            attributes.reserve(entries.len());\n            for entry in entries {\n                let entry = entry\n                    .as_object()\n                    .expect(\"entry from EnumerableOwnProperties must be an object\");\n\n                // 1. Let key be entry.[[Key]].\n                let key = entry.get(0, context)?;\n                let key_str = key\n                    .as_string()\n                    .expect(\"key from EnumerableOwnProperties must be a string\")\n                    .clone();\n\n                // 2. Let value be entry.[[Value]].\n                let value = entry.get(1, context)?;\n\n                // 3. If Type(value) is not String, throw a TypeError exception.\n                let Some(value_str) = value.as_string() else {\n                    return Err(JsNativeError::typ()\n                        .with_message(\"import attribute value must be a string\")\n                        .into());\n                };\n                let value_str = value_str.clone();\n\n                // 4. Append the Record { [[Key]]: key, [[Value]]: value } to attributes.\n                attributes.push(ImportAttribute::new(key_str, value_str));\n            }\n        }\n    }\n\n    // 3. Return the Record { [[Specifier]]: specifier, [[Attributes]]: attributes }.\n    Ok(ModuleRequest::new(specifier, attributes.into_boxed_slice()))\n}\n\n/// Loads the module of a dynamic import. This combines the operations:\n/// - [`HostLoadImportedModule(referrer, specifierString, empty, promiseCapability).`][load]\n/// - [`FinishLoadingImportedModule ( referrer, specifier, payload, result )`][finish]\n/// - [`ContinueDynamicImport ( promiseCapability, moduleCompletion )`][continue]\n///\n/// [load]: https://tc39.es/ecma262/#sec-HostLoadImportedModule\n/// [finish]: https://tc39.es/ecma262/#sec-FinishLoadingImportedModule\n/// [continue]: https://tc39.es/ecma262/#sec-ContinueDynamicImport\nasync fn load_dyn_import(\n    referrer: Referrer,\n    request: ModuleRequest,\n    cap: PromiseCapability,\n    phase: u32,\n    context: &RefCell<&mut Context>,\n) -> JsResult<()> {\n    let loader = context.borrow().module_loader();\n    let fut = loader.load_imported_module(referrer.clone(), request.clone(), context);\n    let mut stack = [MaybeUninit::<u8>::uninit(); 16];\n    let mut heap = Vec::<MaybeUninit<u8>>::new();\n    let completion = fut.init2(&mut stack, &mut heap).await;\n\n    // `ContinueDynamicImport ( promiseCapability, moduleCompletion )`\n    // https://tc39.es/ecma262/#sec-ContinueDynamicImport\n\n    // `FinishLoadingImportedModule ( referrer, specifier, payload, result )`\n    // https://tc39.es/ecma262/#sec-FinishLoadingImportedModule\n\n    let module = match completion {\n        // 1. If moduleCompletion is an abrupt completion, then\n        Err(err) => {\n            // a. Perform ! Call(promiseCapability.[[Reject]], undefined, « moduleCompletion.[[Value]] »).\n            let err = err.into_opaque(&mut context.borrow_mut())?;\n            cap.reject()\n                .call(&JsValue::undefined(), &[err], &mut context.borrow_mut())\n                .expect(\"default `reject` function cannot throw\");\n\n            // b. Return unused.\n            return Ok(());\n        }\n        Ok(m) => m,\n    };\n\n    // 1. If result is a normal completion, then\n    match referrer {\n        Referrer::Module(mod_ref) => {\n            let ModuleKind::SourceText(src) = mod_ref.kind() else {\n                panic!(\"referrer cannot be a synthetic module\");\n            };\n\n            let mut loaded_modules = src.loaded_modules().borrow_mut();\n\n            //     a. If referrer.[[LoadedModules]] contains a Record whose [[Specifier]] is specifier, then\n            //     b. Else,\n            //         i. Append the Record { [[Specifier]]: specifier, [[Module]]: result.[[Value]] } to referrer.[[LoadedModules]].\n            let entry = loaded_modules\n                .entry(request)\n                .or_insert_with(|| module.clone());\n\n            //         i. Assert: That Record's [[Module]] is result.[[Value]].\n            debug_assert_eq!(&module, entry);\n\n            // Same steps apply to referrers below\n        }\n        Referrer::Realm(realm) => {\n            let mut loaded_modules = realm.loaded_modules().borrow_mut();\n            let entry = loaded_modules\n                .entry(request.specifier().clone())\n                .or_insert_with(|| module.clone());\n            debug_assert_eq!(&module, entry);\n        }\n        Referrer::Script(script) => {\n            let mut loaded_modules = script.loaded_modules().borrow_mut();\n            let entry = loaded_modules\n                .entry(request.specifier().clone())\n                .or_insert_with(|| module.clone());\n            debug_assert_eq!(&module, entry);\n        }\n    }\n\n    // When the `experimental` feature is disabled, reject any non-evaluation phase.\n    #[cfg(not(feature = \"experimental\"))]\n    if phase != 0 {\n        let err = JsNativeError::syntax()\n            .with_message(\"import.defer() and import.source() require the 'experimental' feature\")\n            .into();\n        let err = JsError::into_opaque(err, &mut context.borrow_mut())?;\n        cap.reject()\n            .call(&JsValue::undefined(), &[err], &mut context.borrow_mut())\n            .expect(\"default `reject` function cannot throw\");\n        return Ok(());\n    }\n\n    // TODO: For source phase (phase == 2), implement GetModuleSource()\n    // 16.2.1.7.2 GetModuleSource ( )\n    // Source Text Module Record provides a GetModuleSource implementation\n    // that always returns an abrupt completion indicating that a source phase import is not available.\n    // 1. Throw a SyntaxError exception.\n    #[cfg(feature = \"experimental\")]\n    if phase == 2 {\n        let err = JsNativeError::syntax()\n            .with_message(\"source phase import is not available for this module\")\n            .into();\n        let err = JsError::into_opaque(err, &mut context.borrow_mut())?;\n        cap.reject()\n            .call(&JsValue::undefined(), &[err], &mut context.borrow_mut())\n            .expect(\"default `reject` function cannot throw\");\n        return Ok(());\n    }\n\n    // 2. Let module be moduleCompletion.[[Value]].\n    // 3. Let loadPromise be module.LoadRequestedModules().\n    let load = module.load(&mut context.borrow_mut());\n\n    // 4. Let rejectedClosure be a new Abstract Closure with parameters (reason) that captures promiseCapability and performs the following steps when called:\n    // 5. Let onRejected be CreateBuiltinFunction(rejectedClosure, 1, \"\", « »).\n    let on_rejected = FunctionObjectBuilder::new(\n        context.borrow().realm(),\n        NativeFunction::from_copy_closure_with_captures(\n            |_, args, cap, context| {\n                //     a. Perform ! Call(promiseCapability.[[Reject]], undefined, « reason »).\n                cap.reject()\n                    .call(&JsValue::undefined(), args, context)\n                    .expect(\"default `reject` function cannot throw\");\n\n                //     b. Return unused.\n                Ok(JsValue::undefined())\n            },\n            cap.clone(),\n        ),\n    )\n    .build();\n\n    // 6. Let linkAndEvaluateClosure be a new Abstract Closure with no parameters that captures module, promiseCapability, and onRejected and performs the following steps when called:\n    // 7. Let linkAndEvaluate be CreateBuiltinFunction(linkAndEvaluateClosure, 0, \"\", « »).\n    let link_evaluate = FunctionObjectBuilder::new(\n        context.borrow().realm(),\n        NativeFunction::from_copy_closure_with_captures(\n            |_, _, (module, cap, on_rejected), context| {\n                // a. Let link be Completion(module.Link()).\n                // b. If link is an abrupt completion, then\n                if let Err(e) = module.link(context) {\n                    // i. Perform ! Call(promiseCapability.[[Reject]], undefined, « link.[[Value]] »).\n                    let e = e.into_opaque(context)?;\n                    cap.reject()\n                        .call(&JsValue::undefined(), &[e], context)\n                        .expect(\"default `reject` function cannot throw\");\n                    // ii. Return unused.\n                    return Ok(JsValue::undefined());\n                }\n\n                // c. Let evaluatePromise be module.Evaluate().\n                let evaluate = module.evaluate(context)?;\n\n                // d. Let fulfilledClosure be a new Abstract Closure with no parameters that captures module and promiseCapability and performs the following steps when called:\n                // e. Let onFulfilled be CreateBuiltinFunction(fulfilledClosure, 0, \"\", « »).\n                let fulfill = FunctionObjectBuilder::new(\n                    context.realm(),\n                    NativeFunction::from_copy_closure_with_captures(\n                        |_, _, (module, cap), context| {\n                            // i. Let namespace be GetModuleNamespace(module).\n                            let namespace = module.namespace(context);\n\n                            // ii. Perform ! Call(promiseCapability.[[Resolve]], undefined, « namespace »).\n                            cap.resolve()\n                                .call(&JsValue::undefined(), &[namespace.into()], context)\n                                .expect(\"default `resolve` function cannot throw\");\n\n                            // iii. Return unused.\n                            Ok(JsValue::undefined())\n                        },\n                        (module.clone(), cap.clone()),\n                    ),\n                )\n                .build();\n\n                // f. Perform PerformPromiseThen(evaluatePromise, onFulfilled, onRejected).\n                Promise::perform_promise_then(\n                    &evaluate,\n                    Some(fulfill),\n                    Some(on_rejected.clone()),\n                    None,\n                    context,\n                );\n\n                // g. Return unused.\n                Ok(JsValue::undefined())\n            },\n            (module.clone(), cap.clone(), on_rejected.clone()),\n        ),\n    )\n    .build();\n\n    // 8. Perform PerformPromiseThen(loadPromise, linkAndEvaluate, onRejected).\n    Promise::perform_promise_then(\n        &load,\n        Some(link_evaluate),\n        Some(on_rejected),\n        None,\n        &mut context.borrow_mut(),\n    );\n\n    // 9. Return unused.\n    Ok(())\n}\n\n/// `ImportCall` implements the Opcode Operation for `Opcode::ImportCall`\n///\n/// Operation:\n///  - Dynamically imports a module\n#[derive(Debug, Clone, Copy)]\npub(crate) struct ImportCall;\n\nimpl ImportCall {\n    #[inline(always)]\n    pub(super) fn operation(\n        (specifier_op, options_op, phase_op): (RegisterOperand, RegisterOperand, IndexOperand),\n        context: &mut Context,\n    ) -> JsResult<()> {\n        // Import Calls\n        // Runtime Semantics: Evaluation\n        // https://tc39.es/ecma262/#sec-import-call-runtime-semantics-evaluation\n\n        let phase: u32 = phase_op.into();\n\n        // 1. Let referrer be GetActiveScriptOrModule().\n        // 2. If referrer is null, set referrer to the current Realm Record.\n        let referrer = context\n            .get_active_script_or_module()\n            .map_or_else(|| Referrer::Realm(context.realm().clone()), Into::into);\n\n        // 3. Let argRef be ? Evaluation of AssignmentExpression.\n        // 4. Let specifier be ? GetValue(argRef).\n        let specifier = context.vm.get_register(specifier_op.into()).clone();\n\n        // Get options if provided\n        let options = context.vm.get_register(options_op.into()).clone();\n\n        // 5. Let promiseCapability be ! NewPromiseCapability(%Promise%).\n        let cap = PromiseCapability::new(\n            &context.intrinsics().constructors().promise().constructor(),\n            context,\n        )\n        .expect(\"operation cannot fail for the %Promise% intrinsic\");\n        let promise = cap.promise().clone();\n\n        // 6. Let specifierString be Completion(ToString(specifier)).\n        let specifier_str = match specifier.to_string(context) {\n            Ok(s) => s,\n            // 7. IfAbruptRejectPromise(specifierString, promiseCapability).\n            Err(err) => {\n                let err = err.into_opaque(context)?;\n                cap.reject().call(&JsValue::undefined(), &[err], context)?;\n                context.vm.set_register(specifier_op.into(), promise.into());\n                return Ok(());\n            }\n        };\n\n        let request = match parse_import_attributes(specifier_str, &options, context) {\n            Ok(req) => req,\n            Err(err) => {\n                let err = err.into_opaque(context)?;\n                cap.reject().call(&JsValue::undefined(), &[err], context)?;\n                context.vm.set_register(specifier_op.into(), promise.into());\n                return Ok(());\n            }\n        };\n\n        // 8. Perform HostLoadImportedModule(referrer, specifierString, empty, promiseCapability).\n        let job = NativeAsyncJob::with_realm(\n            async move |context| {\n                load_dyn_import(referrer, request, cap, phase, context).await?;\n                Ok(JsValue::undefined())\n            },\n            context.realm().clone(),\n        );\n        context.enqueue_job(job.into());\n\n        // 9. Return promiseCapability.[[Promise]].\n        context.vm.set_register(specifier_op.into(), promise.into());\n\n        Ok(())\n    }\n}\n\nimpl Operation for ImportCall {\n    const NAME: &'static str = \"ImportCall\";\n    const INSTRUCTION: &'static str = \"INST - ImportCall\";\n    const COST: u8 = 15;\n}\n"
  },
  {
    "path": "core/engine/src/vm/opcode/concat/mod.rs",
    "content": "use super::RegisterOperand;\nuse crate::{Context, JsResult, JsString, vm::opcode::Operation};\nuse thin_vec::ThinVec;\n\n/// `ConcatToString` implements the Opcode Operation for `Opcode::ConcatToString`\n///\n/// Operation:\n///  - Concat multiple stack objects into a string.\n#[derive(Debug, Clone, Copy)]\npub(crate) struct ConcatToString;\n\nimpl ConcatToString {\n    #[inline(always)]\n    pub(super) fn operation(\n        (string, values): (RegisterOperand, ThinVec<RegisterOperand>),\n\n        context: &mut Context,\n    ) -> JsResult<()> {\n        let mut strings = Vec::with_capacity(values.len());\n        for value in values {\n            let val = context.vm.get_register(value.into()).clone();\n            strings.push(val.to_string(context)?);\n        }\n        let s = JsString::concat_array(&strings.iter().map(JsString::as_str).collect::<Vec<_>>());\n        context.vm.set_register(string.into(), s.into());\n        Ok(())\n    }\n}\n\nimpl Operation for ConcatToString {\n    const NAME: &'static str = \"ConcatToString\";\n    const INSTRUCTION: &'static str = \"INST - ConcatToString\";\n    const COST: u8 = 6;\n}\n"
  },
  {
    "path": "core/engine/src/vm/opcode/control_flow/jump.rs",
    "content": "use crate::{\n    Context, JsResult,\n    vm::opcode::{Address, Operation, RegisterOperand},\n};\nuse thin_vec::ThinVec;\n\n/// `Jump` implements the Opcode Operation for `Opcode::Jump`\n///\n/// Operation:\n///  - Unconditional jump to address.\n#[derive(Debug, Clone, Copy)]\npub(crate) struct Jump;\n\nimpl Jump {\n    #[inline(always)]\n    pub(crate) fn operation(address: Address, context: &mut Context) {\n        context.vm.frame_mut().pc = u32::from(address);\n    }\n}\n\nimpl Operation for Jump {\n    const NAME: &'static str = \"Jump\";\n    const INSTRUCTION: &'static str = \"INST - Jump\";\n    const COST: u8 = 1;\n}\n\n// `JumpIfTrue` implements the Opcode Operation for `Opcode::JumpIfTrue`\n///\n/// Operation:\n///  - Conditional jump to address.\n#[derive(Debug, Clone, Copy)]\npub(crate) struct JumpIfTrue;\n\nimpl JumpIfTrue {\n    #[inline(always)]\n    pub(crate) fn operation((address, value): (Address, RegisterOperand), context: &mut Context) {\n        let value = context.vm.get_register(value.into());\n        if value.to_boolean() {\n            context.vm.frame_mut().pc = u32::from(address);\n        }\n    }\n}\n\nimpl Operation for JumpIfTrue {\n    const NAME: &'static str = \"JumpIfTrue\";\n    const INSTRUCTION: &'static str = \"INST - JumpIfTrue\";\n    const COST: u8 = 1;\n}\n\n/// `JumpIfFalse` implements the Opcode Operation for `Opcode::JumpIfFalse`\n///\n/// Operation:\n///  - Conditional jump to address.\n#[derive(Debug, Clone, Copy)]\npub(crate) struct JumpIfFalse;\n\nimpl JumpIfFalse {\n    #[inline(always)]\n    pub(crate) fn operation((address, value): (Address, RegisterOperand), context: &mut Context) {\n        let value = context.vm.get_register(value.into());\n        if !value.to_boolean() {\n            context.vm.frame_mut().pc = u32::from(address);\n        }\n    }\n}\n\nimpl Operation for JumpIfFalse {\n    const NAME: &'static str = \"JumpIfFalse\";\n    const INSTRUCTION: &'static str = \"INST - JumpIfFalse\";\n    const COST: u8 = 1;\n}\n\n/// `JumpIfNotUndefined` implements the Opcode Operation for `Opcode::JumpIfNotUndefined`\n///\n/// Operation:\n///  - Conditional jump to address.\n#[derive(Debug, Clone, Copy)]\npub(crate) struct JumpIfNotUndefined;\n\nimpl JumpIfNotUndefined {\n    #[inline(always)]\n    pub(crate) fn operation((address, value): (Address, RegisterOperand), context: &mut Context) {\n        let value = context.vm.get_register(value.into());\n        if !value.is_undefined() {\n            context.vm.frame_mut().pc = u32::from(address);\n        }\n    }\n}\n\nimpl Operation for JumpIfNotUndefined {\n    const NAME: &'static str = \"JumpIfNotUndefined\";\n    const INSTRUCTION: &'static str = \"INST - JumpIfNotUndefined\";\n    const COST: u8 = 1;\n}\n\n/// `JumpIfNullOrUndefined` implements the Opcode Operation for `Opcode::JumpIfNullOrUndefined`\n///\n/// Operation:\n///  - Conditional jump to address.\n#[derive(Debug, Clone, Copy)]\npub(crate) struct JumpIfNullOrUndefined;\n\nimpl JumpIfNullOrUndefined {\n    #[inline(always)]\n    pub(crate) fn operation((address, value): (Address, RegisterOperand), context: &mut Context) {\n        let value = context.vm.get_register(value.into());\n        if value.is_null_or_undefined() {\n            context.vm.frame_mut().pc = u32::from(address);\n        }\n    }\n}\n\nimpl Operation for JumpIfNullOrUndefined {\n    const NAME: &'static str = \"JumpIfNullOrUndefined\";\n    const INSTRUCTION: &'static str = \"INST - JumpIfNullOrUndefined\";\n    const COST: u8 = 1;\n}\n\n/// `JumpIfNotLessThan` implements the Opcode Operation for `Opcode::JumpIfNotLessThan`\n///\n/// Operation:\n///  - Fused `<` comparison + conditional jump. Jumps if `!(lhs < rhs)`.\n#[derive(Debug, Clone, Copy)]\npub(crate) struct JumpIfNotLessThan;\n\nimpl JumpIfNotLessThan {\n    #[inline(always)]\n    pub(crate) fn operation(\n        (address, lhs, rhs): (Address, RegisterOperand, RegisterOperand),\n        context: &mut Context,\n    ) -> JsResult<()> {\n        let lhs = context.vm.get_register(lhs.into());\n        let rhs = context.vm.get_register(rhs.into());\n        if let Some(result) = lhs.lt_fast(rhs) {\n            if !result {\n                context.vm.frame_mut().pc = u32::from(address);\n            }\n            return Ok(());\n        }\n        let lhs = lhs.clone();\n        let rhs = rhs.clone();\n        if !lhs.lt(&rhs, context)? {\n            context.vm.frame_mut().pc = u32::from(address);\n        }\n        Ok(())\n    }\n}\n\nimpl Operation for JumpIfNotLessThan {\n    const NAME: &'static str = \"JumpIfNotLessThan\";\n    const INSTRUCTION: &'static str = \"INST - JumpIfNotLessThan\";\n    const COST: u8 = 2;\n}\n\n/// `JumpIfNotLessThanOrEqual` implements the Opcode Operation for `Opcode::JumpIfNotLessThanOrEqual`\n///\n/// Operation:\n///  - Fused `<=` comparison + conditional jump. Jumps if `!(lhs <= rhs)`.\n#[derive(Debug, Clone, Copy)]\npub(crate) struct JumpIfNotLessThanOrEqual;\n\nimpl JumpIfNotLessThanOrEqual {\n    #[inline(always)]\n    pub(crate) fn operation(\n        (address, lhs, rhs): (Address, RegisterOperand, RegisterOperand),\n        context: &mut Context,\n    ) -> JsResult<()> {\n        let lhs = context.vm.get_register(lhs.into());\n        let rhs = context.vm.get_register(rhs.into());\n        if let Some(result) = lhs.le_fast(rhs) {\n            if !result {\n                context.vm.frame_mut().pc = u32::from(address);\n            }\n            return Ok(());\n        }\n        let lhs = lhs.clone();\n        let rhs = rhs.clone();\n        if !lhs.le(&rhs, context)? {\n            context.vm.frame_mut().pc = u32::from(address);\n        }\n        Ok(())\n    }\n}\n\nimpl Operation for JumpIfNotLessThanOrEqual {\n    const NAME: &'static str = \"JumpIfNotLessThanOrEqual\";\n    const INSTRUCTION: &'static str = \"INST - JumpIfNotLessThanOrEqual\";\n    const COST: u8 = 2;\n}\n\n/// `JumpIfNotGreaterThan` implements the Opcode Operation for `Opcode::JumpIfNotGreaterThan`\n///\n/// Operation:\n///  - Fused `>` comparison + conditional jump. Jumps if `!(lhs > rhs)`.\n#[derive(Debug, Clone, Copy)]\npub(crate) struct JumpIfNotGreaterThan;\n\nimpl JumpIfNotGreaterThan {\n    #[inline(always)]\n    pub(crate) fn operation(\n        (address, lhs, rhs): (Address, RegisterOperand, RegisterOperand),\n        context: &mut Context,\n    ) -> JsResult<()> {\n        let lhs = context.vm.get_register(lhs.into());\n        let rhs = context.vm.get_register(rhs.into());\n        if let Some(result) = lhs.gt_fast(rhs) {\n            if !result {\n                context.vm.frame_mut().pc = u32::from(address);\n            }\n            return Ok(());\n        }\n        let lhs = lhs.clone();\n        let rhs = rhs.clone();\n        if !lhs.gt(&rhs, context)? {\n            context.vm.frame_mut().pc = u32::from(address);\n        }\n        Ok(())\n    }\n}\n\nimpl Operation for JumpIfNotGreaterThan {\n    const NAME: &'static str = \"JumpIfNotGreaterThan\";\n    const INSTRUCTION: &'static str = \"INST - JumpIfNotGreaterThan\";\n    const COST: u8 = 2;\n}\n\n/// `JumpIfNotGreaterThanOrEqual` implements the Opcode Operation for `Opcode::JumpIfNotGreaterThanOrEqual`\n///\n/// Operation:\n///  - Fused `>=` comparison + conditional jump. Jumps if `!(lhs >= rhs)`.\n#[derive(Debug, Clone, Copy)]\npub(crate) struct JumpIfNotGreaterThanOrEqual;\n\nimpl JumpIfNotGreaterThanOrEqual {\n    #[inline(always)]\n    pub(crate) fn operation(\n        (address, lhs, rhs): (Address, RegisterOperand, RegisterOperand),\n        context: &mut Context,\n    ) -> JsResult<()> {\n        let lhs = context.vm.get_register(lhs.into());\n        let rhs = context.vm.get_register(rhs.into());\n        if let Some(result) = lhs.ge_fast(rhs) {\n            if !result {\n                context.vm.frame_mut().pc = u32::from(address);\n            }\n            return Ok(());\n        }\n        let lhs = lhs.clone();\n        let rhs = rhs.clone();\n        if !lhs.ge(&rhs, context)? {\n            context.vm.frame_mut().pc = u32::from(address);\n        }\n        Ok(())\n    }\n}\n\nimpl Operation for JumpIfNotGreaterThanOrEqual {\n    const NAME: &'static str = \"JumpIfNotGreaterThanOrEqual\";\n    const INSTRUCTION: &'static str = \"INST - JumpIfNotGreaterThanOrEqual\";\n    const COST: u8 = 2;\n}\n\n/// `JumpIfNotEqual` implements the Opcode Operation for `Opcode::JumpIfNotEqual`\n///\n/// Operation:\n///  - Conditional jump to address.\n#[derive(Debug, Clone, Copy)]\npub(crate) struct JumpIfNotEqual;\n\nimpl JumpIfNotEqual {\n    #[inline(always)]\n    pub(crate) fn operation(\n        (address, lhs, rhs): (Address, RegisterOperand, RegisterOperand),\n        context: &mut Context,\n    ) {\n        let lhs = context.vm.get_register(lhs.into());\n        let rhs = context.vm.get_register(rhs.into());\n        if lhs != rhs {\n            context.vm.frame_mut().pc = u32::from(address);\n        }\n    }\n}\n\nimpl Operation for JumpIfNotEqual {\n    const NAME: &'static str = \"JumpIfNotEqual\";\n    const INSTRUCTION: &'static str = \"INST - JumpIfNotEqual\";\n    const COST: u8 = 1;\n}\n\n/// `JumpTable` implements the Opcode Operation for `Opcode::JumpTable`\n///\n/// Operation:\n///  - Conditional jump to address.\n#[derive(Debug, Clone, Copy)]\npub(crate) struct JumpTable;\n\nimpl JumpTable {\n    #[inline(always)]\n    pub(crate) fn operation((index, addresses): (u32, ThinVec<Address>), context: &mut Context) {\n        let value = context.vm.get_register(index as usize);\n        let Some(offset) = value.as_i32().map(|i| i as usize) else {\n            return;\n        };\n\n        let Some(pc) = addresses.get(offset).copied() else {\n            return;\n        };\n\n        context.vm.frame_mut().pc = u32::from(pc);\n    }\n}\n\nimpl Operation for JumpTable {\n    const NAME: &'static str = \"JumpTable\";\n    const INSTRUCTION: &'static str = \"INST - JumpTable\";\n    const COST: u8 = 5;\n}\n"
  },
  {
    "path": "core/engine/src/vm/opcode/control_flow/mod.rs",
    "content": "pub(crate) mod jump;\npub(crate) mod r#return;\npub(crate) mod throw;\n\npub(crate) use jump::*;\npub(crate) use r#return::*;\npub(crate) use throw::*;\n"
  },
  {
    "path": "core/engine/src/vm/opcode/control_flow/return.rs",
    "content": "use std::ops::ControlFlow;\n\nuse crate::{\n    Context, JsNativeError,\n    vm::{\n        CompletionRecord,\n        opcode::{Operation, RegisterOperand},\n    },\n};\n\n/// `Return` implements the Opcode Operation for `Opcode::Return`\n///\n/// Operation:\n///  - Return from a function.\n#[derive(Debug, Clone, Copy)]\npub(crate) struct Return;\n\nimpl Return {\n    #[inline(always)]\n    pub(crate) fn operation((): (), context: &mut Context) -> ControlFlow<CompletionRecord> {\n        context.handle_return()\n    }\n}\n\nimpl Operation for Return {\n    const NAME: &'static str = \"Return\";\n    const INSTRUCTION: &'static str = \"INST - Return\";\n    const COST: u8 = 4;\n}\n\n/// `CheckReturn` implements the Opcode Operation for `Opcode::CheckReturn`\n///\n/// Operation:\n///  - Check return from a function.\n#[derive(Debug, Clone, Copy)]\npub(crate) struct CheckReturn;\n\nimpl CheckReturn {\n    #[inline(always)]\n    pub(crate) fn operation((): (), context: &mut Context) -> ControlFlow<CompletionRecord> {\n        let frame = context.vm.frame();\n        if !frame.construct() {\n            return ControlFlow::Continue(());\n        }\n        let this = &context.vm.stack.get_this(frame);\n        let result = context.vm.take_return_value();\n\n        let result = if result.is_object() {\n            result\n        } else if !this.is_undefined() {\n            this.clone()\n        } else if !result.is_undefined() {\n            context.vm.pending_exception = Some(\n                // Avoid setting the realm here, since it needs to be set by the parent\n                // execution context.\n                JsNativeError::typ()\n                    .with_message(\"derived constructor can only return an Object or undefined\")\n                    .into(),\n            );\n            return context.handle_throw();\n        } else {\n            let frame = context.vm.frame();\n            if frame.has_this_value_cached() {\n                this.clone()\n            } else {\n                match context.vm.frame().environments.get_this_binding() {\n                    Err(err) => {\n                        // Avoid setting the realm here, since it needs to be set by the parent\n                        // execution context.\n                        context.vm.pending_exception = Some(err);\n                        return context.handle_throw();\n                    }\n                    Ok(Some(this)) => this,\n                    Ok(None) => context.realm().global_this().clone().into(),\n                }\n            }\n        };\n\n        context.vm.set_return_value(result);\n        ControlFlow::Continue(())\n    }\n}\n\nimpl Operation for CheckReturn {\n    const NAME: &'static str = \"CheckReturn\";\n    const INSTRUCTION: &'static str = \"INST - CheckReturn\";\n    const COST: u8 = 3;\n}\n\n/// `SetAccumulator` implements the Opcode Operation for `Opcode::SetAccumulator`\n///\n/// Operation:\n///  - Sets the accumulator value, which is the implicit return value of a function.\n#[derive(Debug, Clone, Copy)]\npub(crate) struct SetAccumulator;\n\nimpl SetAccumulator {\n    #[inline(always)]\n    pub(crate) fn operation(register: RegisterOperand, context: &mut Context) {\n        let value = context.vm.get_register(register.into());\n        context.vm.set_return_value(value.clone());\n    }\n}\n\nimpl Operation for SetAccumulator {\n    const NAME: &'static str = \"SetAccumulator\";\n    const INSTRUCTION: &'static str = \"INST - SetAccumulator\";\n    const COST: u8 = 2;\n}\n\n/// `Move` implements the Opcode Operation for `Opcode::Move`\n///\n/// Operation:\n///  - Sets the accumulator value, which is the implicit return value of a function.\n#[derive(Debug, Clone, Copy)]\npub(crate) struct Move;\n\nimpl Move {\n    #[inline(always)]\n    pub(crate) fn operation((dst, src): (RegisterOperand, RegisterOperand), context: &mut Context) {\n        let value = context.vm.get_register(src.into());\n        context.vm.set_register(dst.into(), value.clone());\n    }\n}\n\nimpl Operation for Move {\n    const NAME: &'static str = \"Move\";\n    const INSTRUCTION: &'static str = \"INST - Move\";\n    const COST: u8 = 2;\n}\n\n/// `PopIntoRegister` implements the Opcode Operation for `Opcode::PopIntoRegister`.\n///\n/// Operation:\n///  - Pop a value from the stack and store it in a register.\n#[derive(Debug, Clone, Copy)]\npub(crate) struct PopIntoRegister;\n\nimpl PopIntoRegister {\n    #[inline(always)]\n    pub(crate) fn operation(dst: RegisterOperand, context: &mut Context) {\n        let value = context.vm.stack.pop();\n        context.vm.set_register(dst.into(), value);\n    }\n}\n\nimpl Operation for PopIntoRegister {\n    const NAME: &'static str = \"PopIntoRegister\";\n    const INSTRUCTION: &'static str = \"INST - PopIntoRegister\";\n    const COST: u8 = 2;\n}\n\n/// `PushFromRegister` implements the Opcode Operation for `Opcode::PushFromRegister`.\n///\n/// Operation:\n///  - Read a value from a register and push it onto the stack.\n#[derive(Debug, Clone, Copy)]\npub(crate) struct PushFromRegister;\n\nimpl PushFromRegister {\n    #[inline(always)]\n    pub(crate) fn operation(dst: RegisterOperand, context: &mut Context) {\n        let value = context.vm.get_register(dst.into());\n        context.vm.stack.push(value.clone());\n    }\n}\n\nimpl Operation for PushFromRegister {\n    const NAME: &'static str = \"PushFromRegister\";\n    const INSTRUCTION: &'static str = \"INST - PushFromRegister\";\n    const COST: u8 = 2;\n}\n\n/// `SetRegisterFromAccumulator` implements the Opcode Operation for `Opcode::SetRegisterFromAccumulator`\n///\n/// Operation:\n///  - Sets the accumulator value, which is the implicit return value of a function.\n#[derive(Debug, Clone, Copy)]\npub(crate) struct SetRegisterFromAccumulator;\n\nimpl SetRegisterFromAccumulator {\n    #[inline(always)]\n    pub(crate) fn operation(register: RegisterOperand, context: &mut Context) {\n        context\n            .vm\n            .set_register(register.into(), context.vm.get_return_value());\n    }\n}\n\nimpl Operation for SetRegisterFromAccumulator {\n    const NAME: &'static str = \"SetRegisterFromAccumulator\";\n    const INSTRUCTION: &'static str = \"INST - SetRegisterFromAccumulator\";\n    const COST: u8 = 2;\n}\n"
  },
  {
    "path": "core/engine/src/vm/opcode/control_flow/throw.rs",
    "content": "use std::ops::ControlFlow;\n\nuse crate::{\n    Context, JsError, JsNativeError, JsResult,\n    vm::{\n        CompletionRecord,\n        opcode::{IndexOperand, Operation, RegisterOperand},\n    },\n};\n\n/// `Throw` implements the Opcode Operation for `Opcode::Throw`\n///\n/// Operation:\n///  - Throw exception.\n#[derive(Debug, Clone, Copy)]\npub(crate) struct Throw;\n\nimpl Throw {\n    #[inline(always)]\n    pub(crate) fn operation(\n        value: RegisterOperand,\n        context: &mut Context,\n    ) -> ControlFlow<CompletionRecord> {\n        let value = context.vm.get_register(value.into());\n        let error = JsError::from_opaque(value.clone());\n        context.vm.pending_exception = Some(error);\n\n        // Note: -1 because we increment after fetching the opcode.\n        let pc = context.vm.frame().pc - 1;\n        if context.vm.handle_exception_at(pc) {\n            return ControlFlow::Continue(());\n        }\n\n        context.handle_throw()\n    }\n}\n\nimpl Operation for Throw {\n    const NAME: &'static str = \"Throw\";\n    const INSTRUCTION: &'static str = \"INST - Throw\";\n    const COST: u8 = 6;\n}\n\n/// `ReThrow` implements the Opcode Operation for `Opcode::ReThrow`\n///\n/// Operation:\n///  - Rethrow the pending exception.\n#[derive(Debug, Clone, Copy)]\npub(crate) struct ReThrow;\n\nimpl ReThrow {\n    #[inline(always)]\n    pub(crate) fn operation((): (), context: &mut Context) -> ControlFlow<CompletionRecord> {\n        // Note: -1 because we increment after fetching the opcode.\n        let pc = context.vm.frame().pc.saturating_sub(1);\n        if context.vm.handle_exception_at(pc) {\n            return ControlFlow::Continue(());\n        }\n\n        // Note: If we are rethowing and there is no pending error,\n        //       this means that return was called on the generator.\n        //\n        // Note: If we reached this stage then we there is no handler to handle this,\n        //       so return (only for generators).\n        if context.vm.pending_exception.is_none() {\n            return context.handle_return();\n        }\n\n        context.handle_throw()\n    }\n}\n\nimpl Operation for ReThrow {\n    const NAME: &'static str = \"ReThrow\";\n    const INSTRUCTION: &'static str = \"INST - ReThrow\";\n    const COST: u8 = 2;\n}\n\n/// `Exception` implements the Opcode Operation for `Opcode::Exception`\n///\n/// Operation:\n///  - Get the thrown exception and store it in dst.\n#[derive(Debug, Clone, Copy)]\npub(crate) struct Exception;\n\nimpl Exception {\n    #[inline(always)]\n    pub(crate) fn operation(\n        dst: RegisterOperand,\n        context: &mut Context,\n    ) -> ControlFlow<CompletionRecord> {\n        if let Some(error) = context.vm.pending_exception.take() {\n            let error = match error.into_opaque(context) {\n                Ok(e) => e,\n                Err(e) => return context.handle_error(e),\n            };\n            context.vm.set_register(dst.into(), error);\n            return ControlFlow::Continue(());\n        }\n\n        // If there is no pending error, this means that `return()` was called\n        // on the generator, so we rethrow this (empty) error until there is no handler to handle it.\n        // This is done to run the finally code.\n        //\n        // This should be unreachable for regular functions.\n        ReThrow::operation((), context)\n    }\n}\n\nimpl Operation for Exception {\n    const NAME: &'static str = \"Exception\";\n    const INSTRUCTION: &'static str = \"INST - Exception\";\n    const COST: u8 = 2;\n}\n\n/// `MaybeException` implements the Opcode Operation for `Opcode::MaybeException`\n///\n/// Operation:\n///  - Get the thrown pending exception if it's set and push `true`, otherwise push only `false`.\n#[derive(Debug, Clone, Copy)]\npub(crate) struct MaybeException;\n\nimpl MaybeException {\n    #[inline(always)]\n    pub(crate) fn operation(\n        (has_exception, exception): (RegisterOperand, RegisterOperand),\n        context: &mut Context,\n    ) -> JsResult<()> {\n        if let Some(error) = context.vm.pending_exception.take() {\n            let error = error.into_opaque(context)?;\n            context.vm.set_register(exception.into(), error);\n            context.vm.set_register(has_exception.into(), true.into());\n        } else {\n            context.vm.set_register(has_exception.into(), false.into());\n        }\n        Ok(())\n    }\n}\n\nimpl Operation for MaybeException {\n    const NAME: &'static str = \"MaybeException\";\n    const INSTRUCTION: &'static str = \"INST - MaybeException\";\n    const COST: u8 = 3;\n}\n\n/// `ThrowNewTypeError` implements the Opcode Operation for `Opcode::ThrowNewTypeError`\n///\n/// Operation:\n///  - Throws a `TypeError` exception.\n#[derive(Debug, Clone, Copy)]\npub(crate) struct ThrowNewTypeError;\n\nimpl ThrowNewTypeError {\n    #[inline(always)]\n    pub(crate) fn operation(index: IndexOperand, context: &mut Context) -> JsError {\n        let msg = context\n            .vm\n            .frame()\n            .code_block()\n            .constant_string(index.into());\n        let msg = msg\n            .to_std_string()\n            .expect(\"throw message must be an ASCII string\");\n        JsNativeError::typ().with_message(msg).into()\n    }\n}\n\nimpl Operation for ThrowNewTypeError {\n    const NAME: &'static str = \"ThrowNewTypeError\";\n    const INSTRUCTION: &'static str = \"INST - ThrowNewTypeError\";\n    const COST: u8 = 2;\n}\n\n/// `ThrowNewReferenceError` implements the Opcode Operation for `Opcode::ThrowNewReferenceError`\n///\n/// Operation:\n///  - Throws a `ReferenceError` exception.\n#[derive(Debug, Clone, Copy)]\npub(crate) struct ThrowNewReferenceError;\n\nimpl ThrowNewReferenceError {\n    #[inline(always)]\n    pub(crate) fn operation(index: IndexOperand, context: &mut Context) -> JsError {\n        let msg = context\n            .vm\n            .frame()\n            .code_block()\n            .constant_string(index.into());\n        let msg = msg\n            .to_std_string()\n            .expect(\"throw message must be an ASCII string\");\n        JsNativeError::reference().with_message(msg).into()\n    }\n}\n\nimpl Operation for ThrowNewReferenceError {\n    const NAME: &'static str = \"ThrowNewReferenceError\";\n    const INSTRUCTION: &'static str = \"INST - ThrowNewReferenceError\";\n    const COST: u8 = 2;\n}\n"
  },
  {
    "path": "core/engine/src/vm/opcode/copy/mod.rs",
    "content": "use super::RegisterOperand;\nuse crate::{Context, JsResult, vm::opcode::Operation};\nuse thin_vec::ThinVec;\n\n/// `CopyDataProperties` implements the Opcode Operation for `Opcode::CopyDataProperties`\n///\n/// Operation:\n///  - Copy all properties of one object to another object.\n#[derive(Debug, Clone, Copy)]\npub(crate) struct CopyDataProperties;\n\nimpl CopyDataProperties {\n    #[inline(always)]\n    pub(super) fn operation(\n        (object, source, keys): (RegisterOperand, RegisterOperand, ThinVec<RegisterOperand>),\n        context: &mut Context,\n    ) -> JsResult<()> {\n        let object = context.vm.get_register(object.into()).clone();\n        let source = context.vm.get_register(source.into()).clone();\n        let mut excluded_keys = Vec::with_capacity(keys.len());\n        for key in keys {\n            let key = context.vm.get_register(key.into()).clone();\n            excluded_keys.push(\n                key.to_property_key(context)\n                    .expect(\"key must be property key\"),\n            );\n        }\n        let object = object.as_object().expect(\"not an object\");\n        object.copy_data_properties(&source, excluded_keys, context)?;\n        Ok(())\n    }\n}\n\nimpl Operation for CopyDataProperties {\n    const NAME: &'static str = \"CopyDataProperties\";\n    const INSTRUCTION: &'static str = \"INST - CopyDataProperties\";\n    const COST: u8 = 6;\n}\n"
  },
  {
    "path": "core/engine/src/vm/opcode/define/class/getter.rs",
    "content": "use boa_macros::js_str;\n\nuse crate::{\n    Context, JsResult,\n    builtins::function::{OrdinaryFunction, set_function_name},\n    object::internal_methods::InternalMethodPropertyContext,\n    property::PropertyDescriptor,\n    vm::opcode::{IndexOperand, Operation, RegisterOperand},\n};\n\n/// `DefineClassStaticGetterByName` implements the Opcode Operation for `Opcode::DefineClassStaticGetterByName`\n///\n/// Operation:\n///  - Defines a class getter by name.\n#[derive(Debug, Clone, Copy)]\npub(crate) struct DefineClassStaticGetterByName;\n\nimpl DefineClassStaticGetterByName {\n    #[inline(always)]\n    pub(crate) fn operation(\n        (function, class, index): (RegisterOperand, RegisterOperand, IndexOperand),\n        context: &mut Context,\n    ) -> JsResult<()> {\n        let function = context.vm.get_register(function.into()).clone();\n        let class = context.vm.get_register(class.into()).clone();\n        let class = class.as_object().expect(\"class must be object\");\n        let key = context\n            .vm\n            .frame()\n            .code_block()\n            .constant_string(index.into())\n            .into();\n        {\n            let function_object = function\n                .as_object()\n                .expect(\"method must be function object\");\n            set_function_name(&function_object, &key, Some(js_str!(\"get\")), context)?;\n            function_object\n                .downcast_mut::<OrdinaryFunction>()\n                .expect(\"method must be function object\")\n                .set_home_object(class.clone());\n        }\n        let set = class\n            .__get_own_property__(&key, &mut InternalMethodPropertyContext::new(context))?\n            .as_ref()\n            .and_then(PropertyDescriptor::set)\n            .cloned();\n        class.__define_own_property__(\n            &key,\n            PropertyDescriptor::builder()\n                .maybe_get(Some(function.clone()))\n                .maybe_set(set)\n                .enumerable(false)\n                .configurable(true)\n                .build(),\n            &mut InternalMethodPropertyContext::new(context),\n        )?;\n        Ok(())\n    }\n}\n\nimpl Operation for DefineClassStaticGetterByName {\n    const NAME: &'static str = \"DefineClassStaticGetterByName\";\n    const INSTRUCTION: &'static str = \"INST - DefineClassStaticGetterByName\";\n    const COST: u8 = 6;\n}\n\n/// `DefineClassGetterByName` implements the Opcode Operation for `Opcode::DefineClassGetterByName`\n///\n/// Operation:\n///  - Defines a class getter by name.\n#[derive(Debug, Clone, Copy)]\npub(crate) struct DefineClassGetterByName;\n\nimpl DefineClassGetterByName {\n    #[inline(always)]\n    pub(crate) fn operation(\n        (function, class_proto, index): (RegisterOperand, RegisterOperand, IndexOperand),\n        context: &mut Context,\n    ) -> JsResult<()> {\n        let function = context.vm.get_register(function.into()).clone();\n        let class_proto = context.vm.get_register(class_proto.into()).clone();\n        let class_proto = class_proto.as_object().expect(\"class must be object\");\n        let key = context\n            .vm\n            .frame()\n            .code_block()\n            .constant_string(index.into())\n            .into();\n        {\n            let function_object = function\n                .as_object()\n                .expect(\"method must be function object\");\n            set_function_name(&function_object, &key, Some(js_str!(\"get\")), context)?;\n            function_object\n                .downcast_mut::<OrdinaryFunction>()\n                .expect(\"method must be function object\")\n                .set_home_object(class_proto.clone());\n        }\n        let set = class_proto\n            .__get_own_property__(&key, &mut InternalMethodPropertyContext::new(context))?\n            .as_ref()\n            .and_then(PropertyDescriptor::set)\n            .cloned();\n        class_proto.__define_own_property__(\n            &key,\n            PropertyDescriptor::builder()\n                .maybe_get(Some(function.clone()))\n                .maybe_set(set)\n                .enumerable(false)\n                .configurable(true)\n                .build(),\n            &mut InternalMethodPropertyContext::new(context),\n        )?;\n        Ok(())\n    }\n}\n\nimpl Operation for DefineClassGetterByName {\n    const NAME: &'static str = \"DefineClassGetterByName\";\n    const INSTRUCTION: &'static str = \"INST - DefineClassGetterByName\";\n    const COST: u8 = 6;\n}\n\n/// `DefineClassStaticGetterByValue` implements the Opcode Operation for `Opcode::DefineClassStaticGetterByValue`\n///\n/// Operation:\n///  - Defines a class getter by value.\n#[derive(Debug, Clone, Copy)]\npub(crate) struct DefineClassStaticGetterByValue;\n\nimpl DefineClassStaticGetterByValue {\n    #[inline(always)]\n    pub(crate) fn operation(\n        (function, key, class): (RegisterOperand, RegisterOperand, RegisterOperand),\n        context: &mut Context,\n    ) -> JsResult<()> {\n        let function = context.vm.get_register(function.into()).clone();\n        let key = context.vm.get_register(key.into()).clone();\n        let class = context.vm.get_register(class.into()).clone();\n        let class = class.as_object().expect(\"class must be object\");\n        let key = key\n            .to_property_key(context)\n            .expect(\"property key must already be valid\");\n        {\n            let function_object = function\n                .as_object()\n                .expect(\"method must be function object\");\n            set_function_name(&function_object, &key, Some(js_str!(\"get\")), context)?;\n            function_object\n                .downcast_mut::<OrdinaryFunction>()\n                .expect(\"method must be function object\")\n                .set_home_object(class.clone());\n        }\n\n        let set = class\n            .__get_own_property__(&key, &mut InternalMethodPropertyContext::new(context))?\n            .as_ref()\n            .and_then(PropertyDescriptor::set)\n            .cloned();\n        class.define_property_or_throw(\n            key,\n            PropertyDescriptor::builder()\n                .maybe_get(Some(function.clone()))\n                .maybe_set(set)\n                .enumerable(false)\n                .configurable(true)\n                .build(),\n            context,\n        )?;\n        Ok(())\n    }\n}\n\nimpl Operation for DefineClassStaticGetterByValue {\n    const NAME: &'static str = \"DefineClassStaticGetterByValue\";\n    const INSTRUCTION: &'static str = \"INST - DefineClassStaticGetterByValue\";\n    const COST: u8 = 6;\n}\n\n/// `DefineClassGetterByValue` implements the Opcode Operation for `Opcode::DefineClassGetterByValue`\n///\n/// Operation:\n///  - Defines a class getter by value.\n#[derive(Debug, Clone, Copy)]\npub(crate) struct DefineClassGetterByValue;\n\nimpl DefineClassGetterByValue {\n    #[inline(always)]\n    pub(crate) fn operation(\n        (function, key, class_proto): (RegisterOperand, RegisterOperand, RegisterOperand),\n        context: &mut Context,\n    ) -> JsResult<()> {\n        let function = context.vm.get_register(function.into()).clone();\n        let key = context.vm.get_register(key.into()).clone();\n        let class_proto = context.vm.get_register(class_proto.into()).clone();\n        let class_proto = class_proto.as_object().expect(\"class must be object\");\n        let key = key\n            .to_property_key(context)\n            .expect(\"property key must already be valid\");\n        {\n            let function_object = function\n                .as_object()\n                .expect(\"method must be function object\");\n            set_function_name(&function_object, &key, Some(js_str!(\"get\")), context)?;\n            function_object\n                .downcast_mut::<OrdinaryFunction>()\n                .expect(\"method must be function object\")\n                .set_home_object(class_proto.clone());\n        }\n        let set = class_proto\n            .__get_own_property__(&key, &mut InternalMethodPropertyContext::new(context))?\n            .as_ref()\n            .and_then(PropertyDescriptor::set)\n            .cloned();\n        class_proto.__define_own_property__(\n            &key,\n            PropertyDescriptor::builder()\n                .maybe_get(Some(function.clone()))\n                .maybe_set(set)\n                .enumerable(false)\n                .configurable(true)\n                .build(),\n            &mut InternalMethodPropertyContext::new(context),\n        )?;\n        Ok(())\n    }\n}\n\nimpl Operation for DefineClassGetterByValue {\n    const NAME: &'static str = \"DefineClassGetterByValue\";\n    const INSTRUCTION: &'static str = \"INST - DefineClassGetterByValue\";\n    const COST: u8 = 6;\n}\n"
  },
  {
    "path": "core/engine/src/vm/opcode/define/class/method.rs",
    "content": "use crate::{\n    Context, JsResult,\n    builtins::function::{OrdinaryFunction, set_function_name},\n    object::internal_methods::InternalMethodPropertyContext,\n    property::PropertyDescriptor,\n    vm::opcode::{IndexOperand, Operation, RegisterOperand},\n};\n\n/// `DefineClassStaticMethodByName` implements the Opcode Operation for `Opcode::DefineClassStaticMethodByName`\n///\n/// Operation:\n///  - Defines a class method by name.\n#[derive(Debug, Clone, Copy)]\npub(crate) struct DefineClassStaticMethodByName;\n\nimpl DefineClassStaticMethodByName {\n    #[inline(always)]\n    pub(crate) fn operation(\n        (function, class, index): (RegisterOperand, RegisterOperand, IndexOperand),\n        context: &mut Context,\n    ) -> JsResult<()> {\n        let function = context.vm.get_register(function.into()).clone();\n        let class = context.vm.get_register(class.into()).clone();\n        let class = class.as_object().expect(\"class must be object\");\n        let key = context\n            .vm\n            .frame()\n            .code_block()\n            .constant_string(index.into())\n            .into();\n        {\n            let function_object = function\n                .as_object()\n                .expect(\"method must be function object\");\n            set_function_name(&function_object, &key, None, context)?;\n            function_object\n                .downcast_mut::<OrdinaryFunction>()\n                .expect(\"method must be function object\")\n                .set_home_object(class.clone());\n        }\n\n        class.define_method_property(\n            key,\n            function.clone(),\n            &mut InternalMethodPropertyContext::new(context),\n        )?;\n        Ok(())\n    }\n}\n\nimpl Operation for DefineClassStaticMethodByName {\n    const NAME: &'static str = \"DefineClassStaticMethodByName\";\n    const INSTRUCTION: &'static str = \"INST - DefineClassStaticMethodByName\";\n    const COST: u8 = 6;\n}\n\n/// `DefineClassMethodByName` implements the Opcode Operation for `Opcode::DefineClassMethodByName`\n///\n/// Operation:\n///  - Defines a class method by name.\n#[derive(Debug, Clone, Copy)]\npub(crate) struct DefineClassMethodByName;\n\nimpl DefineClassMethodByName {\n    #[inline(always)]\n    pub(crate) fn operation(\n        (function, class_proto, index): (RegisterOperand, RegisterOperand, IndexOperand),\n        context: &mut Context,\n    ) -> JsResult<()> {\n        let function = context.vm.get_register(function.into()).clone();\n        let class_proto = context.vm.get_register(class_proto.into()).clone();\n        let class_proto = class_proto.as_object().expect(\"class must be object\");\n        let key = context\n            .vm\n            .frame()\n            .code_block()\n            .constant_string(index.into())\n            .into();\n        {\n            let function_object = function\n                .as_object()\n                .expect(\"method must be function object\");\n            set_function_name(&function_object, &key, None, context)?;\n            function_object\n                .downcast_mut::<OrdinaryFunction>()\n                .expect(\"method must be function object\")\n                .set_home_object(class_proto.clone());\n        }\n\n        class_proto.define_method_property(\n            key,\n            function.clone(),\n            &mut InternalMethodPropertyContext::new(context),\n        )?;\n        Ok(())\n    }\n}\n\nimpl Operation for DefineClassMethodByName {\n    const NAME: &'static str = \"DefineClassMethodByName\";\n    const INSTRUCTION: &'static str = \"INST - DefineClassMethodByName\";\n    const COST: u8 = 6;\n}\n\n/// `DefineClassStaticMethodByValue` implements the Opcode Operation for `Opcode::DefineClassStaticMethodByValue`\n///\n/// Operation:\n///  - Defines a class method by value.\n#[derive(Debug, Clone, Copy)]\npub(crate) struct DefineClassStaticMethodByValue;\n\nimpl DefineClassStaticMethodByValue {\n    #[inline(always)]\n    pub(crate) fn operation(\n        (function, key, class): (RegisterOperand, RegisterOperand, RegisterOperand),\n        context: &mut Context,\n    ) -> JsResult<()> {\n        let function = context.vm.get_register(function.into()).clone();\n        let key = context.vm.get_register(key.into()).clone();\n        let class = context.vm.get_register(class.into()).clone();\n        let class = class.as_object().expect(\"class must be object\");\n        let key = key\n            .to_property_key(context)\n            .expect(\"property key must already be valid\");\n        {\n            let function_object = function\n                .as_object()\n                .expect(\"method must be function object\");\n            set_function_name(&function_object, &key, None, context)?;\n            function_object\n                .downcast_mut::<OrdinaryFunction>()\n                .expect(\"method must be function object\")\n                .set_home_object(class.clone());\n        }\n\n        class.define_property_or_throw(\n            key,\n            PropertyDescriptor::builder()\n                .value(function.clone())\n                .writable(true)\n                .enumerable(false)\n                .configurable(true)\n                .build(),\n            context,\n        )?;\n        Ok(())\n    }\n}\n\nimpl Operation for DefineClassStaticMethodByValue {\n    const NAME: &'static str = \"DefineClassStaticMethodByValue\";\n    const INSTRUCTION: &'static str = \"INST - DefineClassStaticMethodByValue\";\n    const COST: u8 = 6;\n}\n\n/// `DefineClassMethodByValue` implements the Opcode Operation for `Opcode::DefineClassMethodByValue`\n///\n/// Operation:\n///  - Defines a class method by value.\n#[derive(Debug, Clone, Copy)]\npub(crate) struct DefineClassMethodByValue;\n\nimpl DefineClassMethodByValue {\n    #[inline(always)]\n    pub(crate) fn operation(\n        (function, key, class_proto): (RegisterOperand, RegisterOperand, RegisterOperand),\n        context: &mut Context,\n    ) -> JsResult<()> {\n        let function = context.vm.get_register(function.into()).clone();\n        let key = context.vm.get_register(key.into()).clone();\n        let class_proto = context.vm.get_register(class_proto.into()).clone();\n        let class_proto = class_proto.as_object().expect(\"class must be object\");\n        let key = key\n            .to_property_key(context)\n            .expect(\"property key must already be valid\");\n        {\n            let function_object = function\n                .as_object()\n                .expect(\"method must be function object\");\n            set_function_name(&function_object, &key, None, context)?;\n            function_object\n                .downcast_mut::<OrdinaryFunction>()\n                .expect(\"method must be function object\")\n                .set_home_object(class_proto.clone());\n        }\n\n        class_proto.define_method_property(\n            key,\n            function.clone(),\n            &mut InternalMethodPropertyContext::new(context),\n        )?;\n        Ok(())\n    }\n}\n\nimpl Operation for DefineClassMethodByValue {\n    const NAME: &'static str = \"DefineClassMethodByValue\";\n    const INSTRUCTION: &'static str = \"INST - DefineClassMethodByValue\";\n    const COST: u8 = 6;\n}\n"
  },
  {
    "path": "core/engine/src/vm/opcode/define/class/mod.rs",
    "content": "pub(crate) mod getter;\npub(crate) mod method;\npub(crate) mod setter;\n\npub(crate) use getter::*;\npub(crate) use method::*;\npub(crate) use setter::*;\n"
  },
  {
    "path": "core/engine/src/vm/opcode/define/class/setter.rs",
    "content": "use boa_macros::js_str;\n\nuse crate::{\n    Context, JsResult,\n    builtins::function::{OrdinaryFunction, set_function_name},\n    object::internal_methods::InternalMethodPropertyContext,\n    property::PropertyDescriptor,\n    vm::opcode::{IndexOperand, Operation, RegisterOperand},\n};\n\n/// `DefineClassStaticSetterByName` implements the Opcode Operation for `Opcode::DefineClassStaticSetterByName`\n///\n/// Operation:\n///  - Defines a class setter by name.\n#[derive(Debug, Clone, Copy)]\npub(crate) struct DefineClassStaticSetterByName;\n\nimpl DefineClassStaticSetterByName {\n    #[inline(always)]\n    pub(crate) fn operation(\n        (function, class, index): (RegisterOperand, RegisterOperand, IndexOperand),\n        context: &mut Context,\n    ) -> JsResult<()> {\n        let function = context.vm.get_register(function.into()).clone();\n        let class = context.vm.get_register(class.into()).clone();\n        let class = class.as_object().expect(\"class must be object\");\n        let key = context\n            .vm\n            .frame()\n            .code_block()\n            .constant_string(index.into())\n            .into();\n        {\n            let function_object = function\n                .as_object()\n                .expect(\"method must be function object\");\n            set_function_name(&function_object, &key, Some(js_str!(\"set\")), context)?;\n            function_object\n                .downcast_mut::<OrdinaryFunction>()\n                .expect(\"method must be function object\")\n                .set_home_object(class.clone());\n        }\n        let get = class\n            .__get_own_property__(&key, &mut InternalMethodPropertyContext::new(context))?\n            .as_ref()\n            .and_then(PropertyDescriptor::get)\n            .cloned();\n\n        class.__define_own_property__(\n            &key,\n            PropertyDescriptor::builder()\n                .maybe_set(Some(function.clone()))\n                .maybe_get(get)\n                .enumerable(false)\n                .configurable(true)\n                .build(),\n            &mut InternalMethodPropertyContext::new(context),\n        )?;\n        Ok(())\n    }\n}\n\nimpl Operation for DefineClassStaticSetterByName {\n    const NAME: &'static str = \"DefineClassStaticSetterByName\";\n    const INSTRUCTION: &'static str = \"INST - DefineClassStaticSetterByName\";\n    const COST: u8 = 6;\n}\n\n/// `DefineClassSetterByName` implements the Opcode Operation for `Opcode::DefineClassSetterByName`\n///\n/// Operation:\n///  - Defines a class setter by name.\n#[derive(Debug, Clone, Copy)]\npub(crate) struct DefineClassSetterByName;\n\nimpl DefineClassSetterByName {\n    #[inline(always)]\n    pub(crate) fn operation(\n        (function, class_proto, index): (RegisterOperand, RegisterOperand, IndexOperand),\n        context: &mut Context,\n    ) -> JsResult<()> {\n        let function = context.vm.get_register(function.into()).clone();\n        let class_proto = context.vm.get_register(class_proto.into()).clone();\n        let class_proto = class_proto.as_object().expect(\"class must be object\");\n        let key = context\n            .vm\n            .frame()\n            .code_block()\n            .constant_string(index.into())\n            .into();\n        {\n            let function_object = function\n                .as_object()\n                .expect(\"method must be function object\");\n            set_function_name(&function_object, &key, Some(js_str!(\"set\")), context)?;\n            function_object\n                .downcast_mut::<OrdinaryFunction>()\n                .expect(\"method must be function object\")\n                .set_home_object(class_proto.clone());\n        }\n        let get = class_proto\n            .__get_own_property__(&key, &mut InternalMethodPropertyContext::new(context))?\n            .as_ref()\n            .and_then(PropertyDescriptor::get)\n            .cloned();\n\n        class_proto.__define_own_property__(\n            &key,\n            PropertyDescriptor::builder()\n                .maybe_set(Some(function.clone()))\n                .maybe_get(get)\n                .enumerable(false)\n                .configurable(true)\n                .build(),\n            &mut InternalMethodPropertyContext::new(context),\n        )?;\n\n        Ok(())\n    }\n}\n\nimpl Operation for DefineClassSetterByName {\n    const NAME: &'static str = \"DefineClassSetterByName\";\n    const INSTRUCTION: &'static str = \"INST - DefineClassSetterByName\";\n    const COST: u8 = 6;\n}\n\n/// `DefineClassStaticSetterByValue` implements the Opcode Operation for `Opcode::DefineClassStaticSetterByValue`\n///\n/// Operation:\n///  - Defines a class setter by value.\n#[derive(Debug, Clone, Copy)]\npub(crate) struct DefineClassStaticSetterByValue;\n\nimpl DefineClassStaticSetterByValue {\n    #[inline(always)]\n    pub(crate) fn operation(\n        (function, key, class): (RegisterOperand, RegisterOperand, RegisterOperand),\n        context: &mut Context,\n    ) -> JsResult<()> {\n        let function = context.vm.get_register(function.into()).clone();\n        let key = context.vm.get_register(key.into()).clone();\n        let class = context.vm.get_register(class.into()).clone();\n        let class = class.as_object().expect(\"class must be object\");\n        let key = key\n            .to_property_key(context)\n            .expect(\"property key must already be valid\");\n        {\n            let function_object = function\n                .as_object()\n                .expect(\"method must be function object\");\n            set_function_name(&function_object, &key, Some(js_str!(\"set\")), context)?;\n            function_object\n                .downcast_mut::<OrdinaryFunction>()\n                .expect(\"method must be function object\")\n                .set_home_object(class.clone());\n        }\n        let get = class\n            .__get_own_property__(&key, &mut InternalMethodPropertyContext::new(context))?\n            .as_ref()\n            .and_then(PropertyDescriptor::get)\n            .cloned();\n\n        class.define_property_or_throw(\n            key,\n            PropertyDescriptor::builder()\n                .maybe_set(Some(function.clone()))\n                .maybe_get(get)\n                .enumerable(false)\n                .configurable(true)\n                .build(),\n            context,\n        )?;\n\n        Ok(())\n    }\n}\n\nimpl Operation for DefineClassStaticSetterByValue {\n    const NAME: &'static str = \"DefineClassStaticSetterByValue\";\n    const INSTRUCTION: &'static str = \"INST - DefineClassStaticSetterByValue\";\n    const COST: u8 = 6;\n}\n\n/// `DefineClassSetterByValue` implements the Opcode Operation for `Opcode::DefineClassSetterByValue`\n///\n/// Operation:\n///  - Defines a class setter by value.\n#[derive(Debug, Clone, Copy)]\npub(crate) struct DefineClassSetterByValue;\n\nimpl DefineClassSetterByValue {\n    #[inline(always)]\n    pub(crate) fn operation(\n        (function, key, class_proto): (RegisterOperand, RegisterOperand, RegisterOperand),\n        context: &mut Context,\n    ) -> JsResult<()> {\n        let function = context.vm.get_register(function.into()).clone();\n        let key = context.vm.get_register(key.into()).clone();\n        let class_proto = context.vm.get_register(class_proto.into()).clone();\n        let class_proto = class_proto.as_object().expect(\"class must be object\");\n        let key = key\n            .to_property_key(context)\n            .expect(\"property key must already be valid\");\n        {\n            let function_object = function\n                .as_object()\n                .expect(\"method must be function object\");\n            set_function_name(&function_object, &key, Some(js_str!(\"set\")), context)?;\n            function_object\n                .downcast_mut::<OrdinaryFunction>()\n                .expect(\"method must be function object\")\n                .set_home_object(class_proto.clone());\n        }\n        let get = class_proto\n            .__get_own_property__(&key, &mut InternalMethodPropertyContext::new(context))?\n            .as_ref()\n            .and_then(PropertyDescriptor::get)\n            .cloned();\n\n        class_proto.__define_own_property__(\n            &key,\n            PropertyDescriptor::builder()\n                .maybe_set(Some(function.clone()))\n                .maybe_get(get)\n                .enumerable(false)\n                .configurable(true)\n                .build(),\n            &mut InternalMethodPropertyContext::new(context),\n        )?;\n\n        Ok(())\n    }\n}\n\nimpl Operation for DefineClassSetterByValue {\n    const NAME: &'static str = \"DefineClassSetterByValue\";\n    const INSTRUCTION: &'static str = \"INST - DefineClassSetterByValue\";\n    const COST: u8 = 6;\n}\n"
  },
  {
    "path": "core/engine/src/vm/opcode/define/mod.rs",
    "content": "use super::{IndexOperand, RegisterOperand};\nuse crate::{Context, JsResult, JsValue, vm::opcode::Operation};\n\npub(crate) mod class;\npub(crate) mod own_property;\n\npub(crate) use class::*;\npub(crate) use own_property::*;\n\n/// `DefVar` implements the Opcode Operation for `Opcode::DefVar`\n///\n/// Operation:\n///  - Declare `var` type variable.\n#[derive(Debug, Clone, Copy)]\npub(crate) struct DefVar;\n\nimpl DefVar {\n    #[inline(always)]\n    pub(super) fn operation(index: IndexOperand, context: &mut Context) {\n        // TODO: spec specifies to return `empty` on empty vars, but we're trying to initialize.\n        let binding_locator = context.vm.frame().code_block.bindings[usize::from(index)].clone();\n\n        {\n            let frame = context.vm.frame_mut();\n            let global = frame.realm.environment();\n            frame.environments.put_value_if_uninitialized(\n                binding_locator.scope(),\n                binding_locator.binding_index(),\n                JsValue::undefined(),\n                global,\n            );\n        }\n    }\n}\n\nimpl Operation for DefVar {\n    const NAME: &'static str = \"DefVar\";\n    const INSTRUCTION: &'static str = \"INST - DefVar\";\n    const COST: u8 = 3;\n}\n\n/// `DefInitVar` implements the Opcode Operation for `Opcode::DefInitVar`\n///\n/// Operation:\n///  - Declare and initialize a function argument.\n#[derive(Debug, Clone, Copy)]\npub(crate) struct DefInitVar;\n\nimpl DefInitVar {\n    #[inline(always)]\n    pub(super) fn operation(\n        (value, index): (RegisterOperand, IndexOperand),\n        context: &mut Context,\n    ) -> JsResult<()> {\n        let value = context.vm.get_register(value.into()).clone();\n        let frame = context.vm.frame();\n        let strict = frame.code_block.strict();\n        let mut binding_locator = frame.code_block.bindings[usize::from(index)].clone();\n        context.find_runtime_binding(&mut binding_locator)?;\n        context.set_binding(&binding_locator, value.clone(), strict)?;\n\n        Ok(())\n    }\n}\n\nimpl Operation for DefInitVar {\n    const NAME: &'static str = \"DefInitVar\";\n    const INSTRUCTION: &'static str = \"INST - DefInitVar\";\n    const COST: u8 = 3;\n}\n\n/// `PutLexicalValue` implements the Opcode Operation for `Opcode::PutLexicalValue`\n///\n/// Operation:\n///  - Initialize a lexical binding.\n#[derive(Debug, Clone, Copy)]\npub(crate) struct PutLexicalValue;\n\nimpl PutLexicalValue {\n    #[inline(always)]\n    pub(super) fn operation(\n        (value, index): (RegisterOperand, IndexOperand),\n        context: &mut Context,\n    ) {\n        let value = context.vm.get_register(value.into()).clone();\n        let binding_locator = context.vm.frame().code_block.bindings[usize::from(index)].clone();\n        {\n            let frame = context.vm.frame_mut();\n            let global = frame.realm.environment();\n            frame.environments.put_lexical_value(\n                binding_locator.scope(),\n                binding_locator.binding_index(),\n                value,\n                global,\n            );\n        }\n    }\n}\n\nimpl Operation for PutLexicalValue {\n    const NAME: &'static str = \"PutLexicalValue\";\n    const INSTRUCTION: &'static str = \"INST - PutLexicalValue\";\n    const COST: u8 = 3;\n}\n"
  },
  {
    "path": "core/engine/src/vm/opcode/define/own_property.rs",
    "content": "use crate::{\n    Context, JsNativeError, JsResult,\n    object::internal_methods::InternalMethodPropertyContext,\n    property::PropertyDescriptor,\n    vm::opcode::{IndexOperand, Operation, RegisterOperand},\n};\n\n/// `DefineOwnPropertyByName` implements the Opcode Operation for `Opcode::DefineOwnPropertyByName`\n///\n/// Operation:\n///  - Defines a own property of an object by name.\n#[derive(Debug, Clone, Copy)]\npub(crate) struct DefineOwnPropertyByName;\n\nimpl DefineOwnPropertyByName {\n    #[inline(always)]\n    pub(crate) fn operation(\n        (object, value, index): (RegisterOperand, RegisterOperand, IndexOperand),\n        context: &mut Context,\n    ) -> JsResult<()> {\n        let object = context.vm.get_register(object.into()).clone();\n        let value = context.vm.get_register(value.into()).clone();\n        let name = context\n            .vm\n            .frame()\n            .code_block()\n            .constant_string(index.into());\n        let object = object.to_object(context)?;\n        object.__define_own_property__(\n            &name.into(),\n            PropertyDescriptor::builder()\n                .value(value.clone())\n                .writable(true)\n                .enumerable(true)\n                .configurable(true)\n                .build(),\n            &mut InternalMethodPropertyContext::new(context),\n        )?;\n        Ok(())\n    }\n}\n\nimpl Operation for DefineOwnPropertyByName {\n    const NAME: &'static str = \"DefineOwnPropertyByName\";\n    const INSTRUCTION: &'static str = \"INST - DefineOwnPropertyByName\";\n    const COST: u8 = 4;\n}\n\n/// `DefineOwnPropertyByValue` implements the Opcode Operation for `Opcode::DefineOwnPropertyByValue`\n///\n/// Operation:\n///  - Defines a own property of an object by value.\n#[derive(Debug, Clone, Copy)]\npub(crate) struct DefineOwnPropertyByValue;\n\nimpl DefineOwnPropertyByValue {\n    #[inline(always)]\n    pub(crate) fn operation(\n        (value, key, object): (RegisterOperand, RegisterOperand, RegisterOperand),\n        context: &mut Context,\n    ) -> JsResult<()> {\n        let value = context.vm.get_register(value.into()).clone();\n        let key = context.vm.get_register(key.into()).clone();\n        let object = context.vm.get_register(object.into()).clone();\n        let object = object.to_object(context)?;\n        let key = key.to_property_key(context)?;\n        let success = object.__define_own_property__(\n            &key,\n            PropertyDescriptor::builder()\n                .value(value.clone())\n                .writable(true)\n                .enumerable(true)\n                .configurable(true)\n                .build(),\n            &mut InternalMethodPropertyContext::new(context),\n        )?;\n        if !success {\n            return Err(JsNativeError::typ()\n                .with_message(\"failed to defined own property\")\n                .into());\n        }\n        Ok(())\n    }\n}\n\nimpl Operation for DefineOwnPropertyByValue {\n    const NAME: &'static str = \"DefineOwnPropertyByValue\";\n    const INSTRUCTION: &'static str = \"INST - DefineOwnPropertyByValue\";\n    const COST: u8 = 4;\n}\n"
  },
  {
    "path": "core/engine/src/vm/opcode/delete/mod.rs",
    "content": "use crate::{\n    Context, JsError, JsResult, error::JsNativeError,\n    object::internal_methods::InternalMethodPropertyContext, vm::opcode::Operation,\n};\n\nuse super::{IndexOperand, RegisterOperand};\n\n/// `DeletePropertyByName` implements the Opcode Operation for `Opcode::DeletePropertyByName`\n///\n/// Operation:\n///  - Deletes a property by name of an object\n#[derive(Debug, Clone, Copy)]\npub(crate) struct DeletePropertyByName;\n\nimpl DeletePropertyByName {\n    #[inline(always)]\n    pub(super) fn operation(\n        (object_register, index): (RegisterOperand, IndexOperand),\n        context: &mut Context,\n    ) -> JsResult<()> {\n        let object = context.vm.take_register(object_register.into());\n        let object = object.to_object(context)?;\n        let code_block = context.vm.frame().code_block();\n        let key = code_block.constant_string(index.into()).into();\n        let strict = code_block.strict();\n\n        let result = object.__delete__(&key, &mut InternalMethodPropertyContext::new(context))?;\n        if !result && strict {\n            return Err(JsNativeError::typ()\n                .with_message(\"Cannot delete property\")\n                .into());\n        }\n        context\n            .vm\n            .set_register(object_register.into(), result.into());\n        Ok(())\n    }\n}\n\nimpl Operation for DeletePropertyByName {\n    const NAME: &'static str = \"DeletePropertyByName\";\n    const INSTRUCTION: &'static str = \"INST - DeletePropertyByName\";\n    const COST: u8 = 3;\n}\n\n/// `DeletePropertyByValue` implements the Opcode Operation for `Opcode::DeletePropertyByValue`\n///\n/// Operation:\n///  - Deletes a property by value of an object\n#[derive(Debug, Clone, Copy)]\npub(crate) struct DeletePropertyByValue;\n\nimpl DeletePropertyByValue {\n    #[inline(always)]\n    pub(super) fn operation(\n        (object_register, key): (RegisterOperand, RegisterOperand),\n        context: &mut Context,\n    ) -> JsResult<()> {\n        let object = context.vm.get_register(object_register.into()).clone();\n        let key = context.vm.get_register(key.into()).clone();\n        let object = object.to_object(context)?;\n        let property_key = key.to_property_key(context)?;\n\n        let result = object.__delete__(\n            &property_key,\n            &mut InternalMethodPropertyContext::new(context),\n        )?;\n        if !result && context.vm.frame().code_block().strict() {\n            return Err(JsNativeError::typ()\n                .with_message(\"Cannot delete property\")\n                .into());\n        }\n        context\n            .vm\n            .set_register(object_register.into(), result.into());\n        Ok(())\n    }\n}\n\nimpl Operation for DeletePropertyByValue {\n    const NAME: &'static str = \"DeletePropertyByValue\";\n    const INSTRUCTION: &'static str = \"INST - DeletePropertyByValue\";\n    const COST: u8 = 3;\n}\n\n/// `DeleteName` implements the Opcode Operation for `Opcode::DeleteName`\n///\n/// Operation:\n///  - Deletes a property by value of an object\n#[derive(Debug, Clone, Copy)]\npub(crate) struct DeleteName;\n\nimpl DeleteName {\n    #[inline(always)]\n    pub(super) fn operation(\n        (value, index): (RegisterOperand, IndexOperand),\n        context: &mut Context,\n    ) -> JsResult<()> {\n        let mut binding_locator =\n            context.vm.frame().code_block.bindings[usize::from(index)].clone();\n        context.find_runtime_binding(&mut binding_locator)?;\n        let deleted = context.delete_binding(&binding_locator)?;\n        context.vm.set_register(value.into(), deleted.into());\n        Ok(())\n    }\n}\n\nimpl Operation for DeleteName {\n    const NAME: &'static str = \"DeleteName\";\n    const INSTRUCTION: &'static str = \"INST - DeleteName\";\n    const COST: u8 = 3;\n}\n\n/// `DeleteSuperThrow` implements the Opcode Operation for `Opcode::DeleteSuperThrow`\n///\n/// Operation:\n///  - Throws an error when trying to delete a property of `super`.\n#[derive(Debug, Clone, Copy)]\npub(crate) struct DeleteSuperThrow;\n\nimpl DeleteSuperThrow {\n    #[inline(always)]\n    pub(super) fn operation((): (), _: &mut Context) -> JsError {\n        JsNativeError::reference()\n            .with_message(\"cannot delete a property of `super`\")\n            .into()\n    }\n}\n\nimpl Operation for DeleteSuperThrow {\n    const NAME: &'static str = \"DeleteSuperThrow\";\n    const INSTRUCTION: &'static str = \"INST - DeleteSuperThrow\";\n    const COST: u8 = 2;\n}\n"
  },
  {
    "path": "core/engine/src/vm/opcode/environment/mod.rs",
    "content": "use super::{IndexOperand, RegisterOperand};\nuse crate::{\n    Context, JsExpect, JsResult, JsValue,\n    error::JsNativeError,\n    object::internal_methods::InternalMethodPropertyContext,\n    vm::{CallFrameFlags, opcode::Operation},\n};\n\n/// `GetFunctionObject` implements the Opcode Operation for `Opcode::GetFunctionObject`\n///\n/// Operation:\n///  - Gets the function object of the current Function environment.\n///\n/// Registers (out):\n///  - `function_object`: `JsObject`.\n#[derive(Debug, Clone, Copy)]\npub(crate) struct GetFunctionObject;\n\nimpl GetFunctionObject {\n    #[inline(always)]\n    pub(super) fn operation(\n        function_object: RegisterOperand,\n        context: &mut Context,\n    ) -> JsResult<()> {\n        let env = {\n            let frame = context.vm.frame();\n            frame\n                .environments\n                .get_this_environment(frame.realm.environment())\n                .as_function()\n                .js_expect(\"must be in a function environment\")?\n        };\n\n        let function_object_v = env.slots().function_object().clone().into();\n\n        context\n            .vm\n            .set_register(function_object.into(), function_object_v);\n\n        Ok(())\n    }\n}\n\nimpl Operation for GetFunctionObject {\n    const NAME: &'static str = \"GetFunctionObject\";\n    const INSTRUCTION: &'static str = \"INST - GetFunctionObject\";\n    const COST: u8 = 4;\n}\n\n/// `This` implements the Opcode Operation for `Opcode::This`\n///\n/// Operation:\n///  - Pushes `this` value.\n#[derive(Debug, Clone, Copy)]\npub(crate) struct This;\n\nimpl This {\n    #[inline(always)]\n    pub(super) fn operation(dst: RegisterOperand, context: &mut Context) -> JsResult<()> {\n        if context.vm.frame().has_this_value_cached() {\n            let this = context.vm.stack.get_this(context.vm.frame());\n            context.vm.set_register(dst.into(), this);\n            return Ok(());\n        }\n\n        let this = context\n            .vm\n            .frame()\n            .environments\n            .get_this_binding()?\n            .unwrap_or(context.realm().global_this().clone().into());\n        context.vm.frame_mut().flags |= CallFrameFlags::THIS_VALUE_CACHED;\n        context.vm.stack.set_this(\n            context.vm.frames.last().expect(\"frame must exist\"),\n            this.clone(),\n        );\n        context.vm.set_register(dst.into(), this);\n        Ok(())\n    }\n}\n\nimpl Operation for This {\n    const NAME: &'static str = \"This\";\n    const INSTRUCTION: &'static str = \"INST - This\";\n    const COST: u8 = 1;\n}\n\n/// `ThisForObjectEnvironmentName` implements the Opcode Operation for `Opcode::ThisForObjectEnvironmentName`\n///\n/// Operation:\n///  - Pushes `this` value that is related to the object environment of the given binding.\n#[derive(Debug, Clone, Copy)]\npub(crate) struct ThisForObjectEnvironmentName;\n\nimpl ThisForObjectEnvironmentName {\n    #[inline(always)]\n    pub(super) fn operation(\n        (dst, index): (RegisterOperand, IndexOperand),\n        context: &mut Context,\n    ) -> JsResult<()> {\n        let binding_locator = context.vm.frame().code_block.bindings[usize::from(index)].clone();\n        let this = context\n            .this_from_object_environment_binding(&binding_locator)?\n            .map_or(JsValue::undefined(), Into::into);\n        context.vm.set_register(dst.into(), this);\n        Ok(())\n    }\n}\n\nimpl Operation for ThisForObjectEnvironmentName {\n    const NAME: &'static str = \"ThisForObjectEnvironmentName\";\n    const INSTRUCTION: &'static str = \"INST - ThisForObjectEnvironmentName\";\n    const COST: u8 = 1;\n}\n\n/// `SuperCall` implements the Opcode Operation for `Opcode::SuperCall`\n///\n/// Operation:\n///  - Execute the `super()` method.\n#[derive(Debug, Clone, Copy)]\npub(crate) struct SuperCall;\n\nimpl SuperCall {\n    #[inline(always)]\n    pub(super) fn operation(argument_count: IndexOperand, context: &mut Context) -> JsResult<()> {\n        let super_constructor = context\n            .vm\n            .stack\n            .calling_convention_get_function(argument_count.into())\n            .clone();\n\n        let Some(super_constructor) = super_constructor.as_constructor() else {\n            return Err(JsNativeError::typ()\n                .with_message(\"super constructor object must be constructor\")\n                .into());\n        };\n\n        let this_env = {\n            let frame = context.vm.frame();\n            frame\n                .environments\n                .get_this_environment(frame.realm.environment())\n                .as_function()\n                .expect(\"super call must be in function environment\")\n        };\n\n        let new_target = this_env\n            .slots()\n            .new_target()\n            .expect(\"must have new.target\")\n            .clone();\n\n        context.vm.stack.push(new_target);\n\n        super_constructor\n            .__construct__(argument_count.into())\n            .resolve(context)?;\n        Ok(())\n    }\n}\n\nimpl Operation for SuperCall {\n    const NAME: &'static str = \"SuperCall\";\n    const INSTRUCTION: &'static str = \"INST - SuperCall\";\n    const COST: u8 = 3;\n}\n\n/// `SuperCallSpread` implements the Opcode Operation for `Opcode::SuperCallSpread`\n///\n/// Operation:\n///  - Execute the `super()` method where the arguments contain spreads.\n#[derive(Debug, Clone, Copy)]\npub(crate) struct SuperCallSpread;\n\nimpl SuperCallSpread {\n    #[inline(always)]\n    pub(super) fn operation((): (), context: &mut Context) -> JsResult<()> {\n        // Get the arguments that are stored as an array object on the stack.\n        let arguments_array = context.vm.stack.pop();\n        let arguments_array_object = arguments_array\n            .as_object()\n            .expect(\"arguments array in call spread function must be an object\");\n        let arguments = arguments_array_object\n            .borrow()\n            .properties()\n            .to_dense_indexed_properties()\n            .expect(\"arguments array in call spread function must be dense\");\n\n        let super_constructor = context.vm.stack.pop();\n\n        let Some(super_constructor) = super_constructor.as_constructor() else {\n            return Err(JsNativeError::typ()\n                .with_message(\"super constructor object must be constructor\")\n                .into());\n        };\n\n        context.vm.stack.push(super_constructor.clone());\n\n        context\n            .vm\n            .stack\n            .calling_convention_push_arguments(&arguments);\n\n        let this_env = {\n            let frame = context.vm.frame();\n            frame\n                .environments\n                .get_this_environment(frame.realm.environment())\n                .as_function()\n                .expect(\"super call must be in function environment\")\n        };\n\n        let new_target = this_env\n            .slots()\n            .new_target()\n            .expect(\"must have new.target\")\n            .clone();\n\n        context.vm.stack.push(new_target);\n\n        super_constructor\n            .__construct__(arguments.len())\n            .resolve(context)?;\n        Ok(())\n    }\n}\n\nimpl Operation for SuperCallSpread {\n    const NAME: &'static str = \"SuperCallSpread\";\n    const INSTRUCTION: &'static str = \"INST - SuperCallSpread\";\n    const COST: u8 = 3;\n}\n\n/// `SuperCallDerived` implements the Opcode Operation for `Opcode::SuperCallDerived`\n///\n/// Operation:\n///  - Execute the `super()` method when no constructor of the class is defined.\n#[derive(Debug, Clone, Copy)]\npub(crate) struct SuperCallDerived;\n\nimpl SuperCallDerived {\n    #[inline(always)]\n    pub(super) fn operation((): (), context: &mut Context) -> JsResult<()> {\n        let this_env = {\n            let frame = context.vm.frame();\n            frame\n                .environments\n                .get_this_environment(frame.realm.environment())\n                .as_function()\n                .expect(\"super call must be in function environment\")\n        };\n        let new_target = this_env\n            .slots()\n            .new_target()\n            .expect(\"must have new target\")\n            .clone();\n        let active_function = this_env.slots().function_object().clone();\n        let super_constructor = active_function\n            .__get_prototype_of__(&mut InternalMethodPropertyContext::new(context))\n            .expect(\"function object must have prototype\")\n            .expect(\"function object must have prototype\");\n\n        if !super_constructor.is_constructor() {\n            return Err(JsNativeError::typ()\n                .with_message(\"super constructor object must be constructor\")\n                .into());\n        }\n\n        context.vm.stack.push(JsValue::undefined());\n        context.vm.stack.push(super_constructor.clone());\n        for argument in Vec::from(context.vm.stack.get_arguments(context.vm.frame())) {\n            context.vm.stack.push(argument.clone());\n        }\n        context.vm.stack.push(new_target);\n\n        super_constructor\n            .__construct__(context.vm.frame().argument_count as usize)\n            .resolve(context)?;\n        Ok(())\n    }\n}\n\nimpl Operation for SuperCallDerived {\n    const NAME: &'static str = \"SuperCallDerived\";\n    const INSTRUCTION: &'static str = \"INST - SuperCallDerived\";\n    const COST: u8 = 3;\n}\n\n/// `BindThisValue` implements the Opcode Operation for `Opcode::BindThisValue`\n///\n/// Operation:\n///  - Binds `this` value and initializes the instance elements.\n#[derive(Debug, Clone, Copy)]\npub(crate) struct BindThisValue;\n\nimpl BindThisValue {\n    #[inline(always)]\n    pub(super) fn operation(value: RegisterOperand, context: &mut Context) -> JsResult<()> {\n        // Taken from `SuperCall : super Arguments` steps 7-12.\n        //\n        // <https://tc39.es/ecma262/#sec-super-keyword-runtime-semantics-evaluation>\n\n        let result = context\n            .vm\n            .get_register(value.into())\n            .as_object()\n            .expect(\"construct result should be an object\")\n            .clone();\n\n        // 7. Let thisER be GetThisEnvironment().\n        let this_env = {\n            let frame = context.vm.frame();\n            frame\n                .environments\n                .get_this_environment(frame.realm.environment())\n                .as_function()\n                .expect(\"super call must be in function environment\")\n        };\n\n        // 8. Perform ? thisER.BindThisValue(result).\n        this_env.bind_this_value(result.clone())?;\n\n        // 9. Let F be thisER.[[FunctionObject]].\n        // SKIP: 10. Assert: F is an ECMAScript function object.\n        let active_function = this_env.slots().function_object().clone();\n\n        // 11. Perform ? InitializeInstanceElements(result, F).\n        result.initialize_instance_elements(&active_function, context)?;\n\n        // 12. Return result.\n        context.vm.set_register(value.into(), result.into());\n        Ok(())\n    }\n}\n\nimpl Operation for BindThisValue {\n    const NAME: &'static str = \"BindThisValue\";\n    const INSTRUCTION: &'static str = \"INST - BindThisValue\";\n    const COST: u8 = 6;\n}\n"
  },
  {
    "path": "core/engine/src/vm/opcode/function.rs",
    "content": "use crate::{\n    Context, JsExpect, JsResult, JsValue,\n    builtins::function::OrdinaryFunction,\n    vm::opcode::{IndexOperand, Operation, RegisterOperand},\n};\n\n/// `SetHomeObject` implements the Opcode Operation for `Opcode::SetHomeObject`\n///\n/// Operation:\n///  - Set home object internal slot of a function object.\n#[derive(Debug, Clone, Copy)]\npub(crate) struct SetHomeObject;\n\nimpl SetHomeObject {\n    #[inline(always)]\n    pub(crate) fn operation(\n        (function, home): (RegisterOperand, RegisterOperand),\n        context: &mut Context,\n    ) -> JsResult<()> {\n        let function = context.vm.get_register(function.into());\n        let home = context.vm.get_register(home.into());\n\n        function\n            .as_object()\n            .js_expect(\"must be object\")?\n            .downcast_mut::<OrdinaryFunction>()\n            .js_expect(\"must be function object\")?\n            .set_home_object(home.as_object().js_expect(\"must be object\")?.clone());\n\n        Ok(())\n    }\n}\n\nimpl Operation for SetHomeObject {\n    const NAME: &'static str = \"SetHomeObject\";\n    const INSTRUCTION: &'static str = \"INST - SetHomeObject\";\n    const COST: u8 = 4;\n}\n\n/// `GetHomeObject` implements the Opcode Operation for `Opcode::GetHomeObject`\n///\n/// Operation:\n///  - Get the home object internal slot of a function object (null if not set).\n///\n/// Registers (in):\n///  - `function`: `JsObject<OrdinaryFunction>`\n///\n/// Registers (out):\n///  - `home`: `JsObject` or `null` if the home object is not set.\n#[derive(Debug, Clone, Copy)]\npub(crate) struct GetHomeObject;\n\nimpl GetHomeObject {\n    #[inline(always)]\n    pub(crate) fn operation(function: RegisterOperand, context: &mut Context) -> JsResult<()> {\n        let function_v = context.vm.get_register(function.into());\n\n        let home_object = function_v\n            .as_object()\n            .js_expect(\"must be object\")?\n            .downcast_ref::<OrdinaryFunction>()\n            .js_expect(\"must be function object\")?\n            .get_home_object()\n            .map_or_else(JsValue::null, |o| o.clone().into());\n\n        context.vm.set_register(function.into(), home_object);\n        Ok(())\n    }\n}\n\nimpl Operation for GetHomeObject {\n    const NAME: &'static str = \"GetHomeObject\";\n    const INSTRUCTION: &'static str = \"INST - GetHomeObject\";\n    const COST: u8 = 4;\n}\n\n/// `GetMethod` implements the Opcode Operation for `Opcode::GetMethod`\n///\n/// Operation:\n///  - Get a method of an object (undefined if not in the object).\n///\n/// Operands:\n///  - name_index: constant `JsString`\n///\n/// Registers (inout)\n///  - object: `JsObject`, and the operation will set it to the method or\n///    to `undefined` if the object does not have the specified name.\n#[derive(Debug, Clone, Copy)]\npub(crate) struct GetMethod;\n\nimpl GetMethod {\n    #[inline(always)]\n    pub(crate) fn operation(\n        (object, name_index): (RegisterOperand, IndexOperand),\n        context: &mut Context,\n    ) -> JsResult<()> {\n        let function_val = context.vm.take_register(object.into());\n        let code_block = context.vm.frame().code_block();\n        let key = code_block.constant_string(name_index.into());\n\n        let method = function_val.get_method(key, context)?;\n\n        context.vm.set_register(\n            object.into(),\n            method.map_or_else(JsValue::undefined, JsValue::from),\n        );\n\n        Ok(())\n    }\n}\n\nimpl Operation for GetMethod {\n    const NAME: &'static str = \"GetMethod\";\n    const INSTRUCTION: &'static str = \"INST - GetMethod\";\n    const COST: u8 = 3;\n}\n"
  },
  {
    "path": "core/engine/src/vm/opcode/generator/mod.rs",
    "content": "pub(crate) mod yield_stm;\n\nuse crate::{\n    Context, JsObject, JsResult,\n    builtins::{\n        async_generator::{AsyncGenerator as NativeAsyncGenerator, AsyncGeneratorState},\n        generator::{Generator as NativeGenerator, GeneratorContext, GeneratorState},\n    },\n    object::PROTOTYPE,\n    vm::{CompletionRecord, opcode::Operation},\n};\nuse std::{collections::VecDeque, ops::ControlFlow};\n\npub(crate) use yield_stm::*;\n\n/// `Generator` implements the Opcode Operation for `Opcode::Generator`\n///\n/// Operation:\n///  - Creates the Generator object and yields.\n#[derive(Debug, Clone, Copy)]\npub(crate) struct Generator;\n\nimpl Generator {\n    #[inline(always)]\n    pub(super) fn operation((): (), context: &mut Context) -> ControlFlow<CompletionRecord> {\n        let active_function = context.vm.stack.get_function(context.vm.frame());\n        let this_function_object =\n            active_function.expect(\"active function should be set to the generator\");\n\n        let proto = this_function_object\n            .get(PROTOTYPE, context)\n            .expect(\"generator must have a prototype property\")\n            .as_object()\n            .unwrap_or_else(|| context.intrinsics().objects().generator());\n\n        let generator = JsObject::from_proto_and_data_with_shared_shape(\n            context.root_shape(),\n            proto,\n            NativeGenerator {\n                state: GeneratorState::Completed,\n            },\n        );\n\n        let gen_ctx = GeneratorContext::from_current(context, None);\n\n        generator.borrow_mut().data_mut().state =\n            GeneratorState::SuspendedStart { context: gen_ctx };\n\n        context.vm.set_return_value(generator.upcast().into());\n        context.handle_yield()\n    }\n}\n\nimpl Operation for Generator {\n    const NAME: &'static str = \"Generator\";\n    const INSTRUCTION: &'static str = \"INST - Generator\";\n    const COST: u8 = 4;\n}\n\n/// `AsyncGenerator` implements the Opcode Operation for `Opcode::AsyncGenerator`\n///\n/// Operation:\n///  - Creates the AsyncGenerator object and yields.\n#[derive(Debug, Clone, Copy)]\npub(crate) struct AsyncGenerator;\n\nimpl AsyncGenerator {\n    #[inline(always)]\n    pub(super) fn operation((): (), context: &mut Context) -> ControlFlow<CompletionRecord> {\n        let active_function = context.vm.stack.get_function(context.vm.frame());\n        let this_function_object =\n            active_function.expect(\"active function should be set to the generator\");\n\n        let proto = this_function_object\n            .get(PROTOTYPE, context)\n            .expect(\"generator must have a prototype property\")\n            .as_object()\n            .unwrap_or_else(|| context.intrinsics().objects().async_generator());\n\n        let generator = JsObject::from_proto_and_data_with_shared_shape(\n            context.root_shape(),\n            proto,\n            NativeAsyncGenerator {\n                state: AsyncGeneratorState::SuspendedStart,\n                context: None,\n                queue: VecDeque::new(),\n            },\n        );\n\n        let gen_ctx = GeneratorContext::from_current(context, Some(generator.clone().upcast()));\n        generator.borrow_mut().data_mut().context = Some(gen_ctx);\n\n        context.vm.set_return_value(generator.upcast().into());\n        context.handle_yield()\n    }\n}\n\nimpl Operation for AsyncGenerator {\n    const NAME: &'static str = \"AsyncGenerator\";\n    const INSTRUCTION: &'static str = \"INST - AsyncGenerator\";\n    const COST: u8 = 4;\n}\n\n/// `AsyncGeneratorClose` implements the Opcode Operation for `Opcode::AsyncGeneratorClose`\n///\n/// Operation:\n///  - Close an async generator function.\n#[derive(Debug, Clone, Copy)]\npub(crate) struct AsyncGeneratorClose;\n\nimpl AsyncGeneratorClose {\n    #[inline(always)]\n    pub(super) fn operation((): (), context: &mut Context) -> JsResult<()> {\n        // Step 3.e-g in [AsyncGeneratorStart](https://tc39.es/ecma262/#sec-asyncgeneratorstart)\n        let generator = context\n            .vm\n            .async_generator_object()\n            .expect(\"There should be a object\")\n            .downcast::<NativeAsyncGenerator>()\n            .expect(\"must be async generator\");\n\n        let mut r#gen = generator.borrow_mut();\n\n        // e. Assert: If we return here, the async generator either threw an exception or performed either an implicit or explicit return.\n        // f. Remove acGenContext from the execution context stack and restore the execution context that is at the top of the execution context stack as the running execution context.\n\n        // g. Set acGenerator.[[AsyncGeneratorState]] to draining-queue.\n        r#gen.data_mut().state = AsyncGeneratorState::DrainingQueue;\n\n        // h. If result is a normal completion, set result to NormalCompletion(undefined).\n        // i. If result is a return completion, set result to NormalCompletion(result.[[Value]]).\n        let return_value = context.vm.take_return_value();\n\n        let result = context\n            .vm\n            .pending_exception\n            .take()\n            .map_or(Ok(return_value), Err);\n\n        drop(r#gen);\n\n        // j. Perform AsyncGeneratorCompleteStep(acGenerator, result, true).\n        NativeAsyncGenerator::complete_step(&generator, result, true, None, context)?;\n        // k. Perform AsyncGeneratorDrainQueue(acGenerator).\n        NativeAsyncGenerator::drain_queue(&generator, context)?;\n\n        // l. Return undefined.\n        Ok(())\n    }\n}\n\nimpl Operation for AsyncGeneratorClose {\n    const NAME: &'static str = \"AsyncGeneratorClose\";\n    const INSTRUCTION: &'static str = \"INST - AsyncGeneratorClose\";\n    const COST: u8 = 8;\n}\n"
  },
  {
    "path": "core/engine/src/vm/opcode/generator/yield_stm.rs",
    "content": "use std::ops::ControlFlow;\n\nuse crate::{\n    Context, JsValue,\n    builtins::async_generator::{AsyncGenerator, AsyncGeneratorState},\n    vm::{\n        CompletionRecord, GeneratorResumeKind,\n        opcode::{Operation, RegisterOperand},\n    },\n};\n\n/// `GeneratorYield` implements the Opcode Operation for `Opcode::GeneratorYield`\n///\n/// Operation:\n///  - Yield from the current generator execution.\n#[derive(Debug, Clone, Copy)]\npub(crate) struct GeneratorYield;\n\nimpl GeneratorYield {\n    #[inline(always)]\n    pub(crate) fn operation(\n        value: RegisterOperand,\n        context: &mut Context,\n    ) -> ControlFlow<CompletionRecord> {\n        let value = context.vm.get_register(value.into());\n        context.vm.set_return_value(value.clone());\n        context.handle_yield()\n    }\n}\n\nimpl Operation for GeneratorYield {\n    const NAME: &'static str = \"GeneratorYield\";\n    const INSTRUCTION: &'static str = \"INST - GeneratorYield\";\n    const COST: u8 = 1;\n}\n\n/// `AsyncGeneratorYield` implements the Opcode Operation for `Opcode::AsyncGeneratorYield`\n///\n/// Operation:\n///  - Yield from the current async generator execution.\n#[derive(Debug, Clone, Copy)]\npub(crate) struct AsyncGeneratorYield;\n\nimpl AsyncGeneratorYield {\n    #[inline(always)]\n    pub(crate) fn operation(\n        value: RegisterOperand,\n        context: &mut Context,\n    ) -> ControlFlow<CompletionRecord> {\n        // AsyncGeneratorYield ( value )\n        // https://tc39.es/ecma262/#sec-asyncgeneratoryield\n\n        // 1. Let genContext be the running execution context.\n        // 2. Assert: genContext is the execution context of a generator.\n        // 3. Let generator be the value of the Generator component of genContext.\n        // 4. Assert: GetGeneratorKind() is async.\n        let async_generator_object = context\n            .vm\n            .async_generator_object()\n            .expect(\"`AsyncGeneratorYield` must only be called inside async generators\");\n        let async_generator_object = async_generator_object\n            .downcast::<AsyncGenerator>()\n            .expect(\"must be async generator object\");\n\n        // 5. Let completion be NormalCompletion(value).\n        let value = context.vm.get_register(value.into());\n        let completion = Ok(value.clone());\n\n        // TODO: 6. Assert: The execution context stack has at least two elements.\n        // TODO: 7. Let previousContext be the second to top element of the execution context stack.\n        // TODO: 8. Let previousRealm be previousContext's Realm.\n        // 9. Perform AsyncGeneratorCompleteStep(generator, completion, false, previousRealm).\n        if let Err(err) =\n            AsyncGenerator::complete_step(&async_generator_object, completion, false, None, context)\n        {\n            return context.handle_error(err);\n        }\n\n        let mut r#gen = async_generator_object.borrow_mut();\n\n        // 10. Let queue be generator.[[AsyncGeneratorQueue]].\n        // 11. If queue is not empty, then\n        //     a. NOTE: Execution continues without suspending the generator.\n        //     b. Let toYield be the first element of queue.\n        if let Some(next) = r#gen.data().queue.front() {\n            // c. Let resumptionValue be Completion(toYield.[[Completion]]).\n            let resume_kind = match next.completion.clone() {\n                CompletionRecord::Normal(val) => {\n                    context.vm.stack.push(val);\n                    GeneratorResumeKind::Normal\n                }\n                CompletionRecord::Return(val) => {\n                    context.vm.stack.push(val);\n                    GeneratorResumeKind::Return\n                }\n                CompletionRecord::Throw(err) => {\n                    let err = match err.into_opaque(context) {\n                        Ok(e) => e,\n                        Err(e) => return context.handle_error(e),\n                    };\n                    context.vm.stack.push(err);\n                    GeneratorResumeKind::Throw\n                }\n            };\n\n            context.vm.stack.push(resume_kind);\n\n            // d. Return ? AsyncGeneratorUnwrapYieldResumption(resumptionValue).\n            return ControlFlow::Continue(());\n        }\n\n        // 12. Else,\n\n        //     a. Set generator.[[AsyncGeneratorState]] to suspended-yield.\n        r#gen.data_mut().state = AsyncGeneratorState::SuspendedYield;\n\n        //     TODO: b. Remove genContext from the execution context stack and restore the execution context that is at the top of the execution context stack as the running execution context.\n        //     TODO: c. Let callerContext be the running execution context.\n        //     d. Resume callerContext passing undefined. If genContext is ever resumed again, let resumptionValue be the Completion Record with which it is resumed.\n        //     e. Assert: If control reaches here, then genContext is the running execution context again.\n        //     f. Return ? AsyncGeneratorUnwrapYieldResumption(resumptionValue).\n        context.vm.set_return_value(JsValue::undefined());\n        context.handle_yield()\n    }\n}\n\nimpl Operation for AsyncGeneratorYield {\n    const NAME: &'static str = \"AsyncGeneratorYield\";\n    const INSTRUCTION: &'static str = \"INST - AsyncGeneratorYield\";\n    const COST: u8 = 8;\n}\n"
  },
  {
    "path": "core/engine/src/vm/opcode/get/argument.rs",
    "content": "use crate::{\n    Context,\n    vm::opcode::{IndexOperand, Operation, RegisterOperand},\n};\n\n/// `GetArgument` implements the Opcode Operation for `Opcode::GetArgument`\n///\n/// Operation:\n///  - Get i-th argument of the current frame.\n#[derive(Debug, Clone, Copy)]\npub(crate) struct GetArgument;\n\nimpl GetArgument {\n    #[inline(always)]\n    pub(crate) fn operation((index, dst): (IndexOperand, RegisterOperand), context: &mut Context) {\n        let value = context\n            .vm\n            .stack\n            .get_argument(context.vm.frame(), index.into())\n            .cloned()\n            .unwrap_or_default();\n        context.vm.set_register(dst.into(), value);\n    }\n}\n\nimpl Operation for GetArgument {\n    const NAME: &'static str = \"GetArgument\";\n    const INSTRUCTION: &'static str = \"INST - GetArgument\";\n    const COST: u8 = 2;\n}\n"
  },
  {
    "path": "core/engine/src/vm/opcode/get/function.rs",
    "content": "use crate::{\n    Context,\n    vm::{\n        code_block::create_function_object_fast,\n        opcode::{IndexOperand, Operation, RegisterOperand},\n    },\n};\n\n/// `GetFunction` implements the Opcode Operation for `Opcode::GetFunction`\n///\n/// Operation:\n///  - Get function from the pre-compiled inner functions.\n#[derive(Debug, Clone, Copy)]\npub(crate) struct GetFunction;\n\nimpl GetFunction {\n    #[inline(always)]\n    pub(crate) fn operation((dst, index): (RegisterOperand, IndexOperand), context: &mut Context) {\n        let code = context\n            .vm\n            .frame()\n            .code_block()\n            .constant_function(index.into());\n        let function = create_function_object_fast(code, context);\n        context.vm.set_register(dst.into(), function.into());\n    }\n}\n\nimpl Operation for GetFunction {\n    const NAME: &'static str = \"GetFunction\";\n    const INSTRUCTION: &'static str = \"INST - GetFunction\";\n    const COST: u8 = 3;\n}\n"
  },
  {
    "path": "core/engine/src/vm/opcode/get/mod.rs",
    "content": "pub(crate) mod argument;\npub(crate) mod function;\npub(crate) mod name;\npub(crate) mod private;\npub(crate) mod property;\n\npub(crate) use argument::*;\npub(crate) use function::*;\npub(crate) use name::*;\npub(crate) use private::*;\npub(crate) use property::*;\n"
  },
  {
    "path": "core/engine/src/vm/opcode/get/name.rs",
    "content": "use crate::{\n    Context, JsResult, JsValue,\n    error::JsNativeError,\n    object::{internal_methods::InternalMethodPropertyContext, shape::slot::SlotAttributes},\n    property::PropertyKey,\n    vm::opcode::{IndexOperand, Operation, RegisterOperand},\n};\n\n/// `GetName` implements the Opcode Operation for `Opcode::GetName`\n///\n/// Operation:\n///  - Find a binding on the environment chain and store its value in dst.\n#[derive(Debug, Clone, Copy)]\npub(crate) struct GetName;\n\nimpl GetName {\n    #[inline(always)]\n    pub(crate) fn operation(\n        (value, index): (RegisterOperand, IndexOperand),\n        context: &mut Context,\n    ) -> JsResult<()> {\n        let mut binding_locator =\n            context.vm.frame().code_block.bindings[usize::from(index)].clone();\n        context.find_runtime_binding(&mut binding_locator)?;\n        let result = context.get_binding(&binding_locator)?.ok_or_else(|| {\n            let name = binding_locator.name().to_std_string_escaped();\n            JsNativeError::reference().with_message(format!(\"{name} is not defined\"))\n        })?;\n        context.vm.set_register(value.into(), result);\n        Ok(())\n    }\n}\n\nimpl Operation for GetName {\n    const NAME: &'static str = \"GetName\";\n    const INSTRUCTION: &'static str = \"INST - GetName\";\n    const COST: u8 = 4;\n}\n\n/// `GetNameGlobal` implements the Opcode Operation for `Opcode::GetNameGlobal`\n///\n/// Operation:\n///  - Find a binding in the global object and store its value in dst.\n#[derive(Debug, Clone, Copy)]\npub(crate) struct GetNameGlobal;\n\nimpl GetNameGlobal {\n    #[inline(always)]\n    pub(crate) fn operation(\n        (dst, index, ic_index): (RegisterOperand, IndexOperand, IndexOperand),\n        context: &mut Context,\n    ) -> JsResult<()> {\n        let mut binding_locator =\n            context.vm.frame().code_block.bindings[usize::from(index)].clone();\n        context.find_runtime_binding(&mut binding_locator)?;\n\n        if binding_locator.is_global() {\n            let object = context.global_object();\n\n            let ic = &context.vm.frame().code_block().ic[usize::from(ic_index)];\n\n            let object_borrowed = object.borrow();\n            if let Some((shape, slot)) = ic.get(object_borrowed.shape()) {\n                let mut result = if slot.attributes.contains(SlotAttributes::PROTOTYPE) {\n                    let prototype = shape.prototype().expect(\"prototype should have value\");\n                    let prototype = prototype.borrow();\n                    prototype.properties().storage[slot.index as usize].clone()\n                } else {\n                    object_borrowed.properties().storage[slot.index as usize].clone()\n                };\n\n                drop(object_borrowed);\n                if slot.attributes.has_get() && result.is_object() {\n                    result = result.as_object().expect(\"should contain getter\").call(\n                        &object.clone().into(),\n                        &[],\n                        context,\n                    )?;\n                }\n                context.vm.set_register(dst.into(), result);\n                return Ok(());\n            }\n\n            drop(object_borrowed);\n\n            let key: PropertyKey = ic.name.clone().into();\n\n            let context = &mut InternalMethodPropertyContext::new(context);\n            let Some(result) = object.__try_get__(&key, object.clone().into(), context)? else {\n                let name = binding_locator.name().to_std_string_escaped();\n                return Err(JsNativeError::reference()\n                    .with_message(format!(\"{name} is not defined\"))\n                    .into());\n            };\n\n            // Cache the property.\n            let slot = *context.slot();\n            if slot.is_cacheable() {\n                let ic = &context.vm.frame().code_block.ic[usize::from(ic_index)];\n                let object_borrowed = object.borrow();\n                let shape = object_borrowed.shape();\n                ic.set(shape, slot);\n            }\n\n            context.vm.set_register(dst.into(), result);\n            return Ok(());\n        }\n\n        let result = context.get_binding(&binding_locator)?.ok_or_else(|| {\n            let name = binding_locator.name().to_std_string_escaped();\n            JsNativeError::reference().with_message(format!(\"{name} is not defined\"))\n        })?;\n\n        context.vm.set_register(dst.into(), result);\n        Ok(())\n    }\n}\n\nimpl Operation for GetNameGlobal {\n    const NAME: &'static str = \"GetNameGlobal\";\n    const INSTRUCTION: &'static str = \"INST - GetNameGlobal\";\n    const COST: u8 = 4;\n}\n\n/// `GetLocator` implements the Opcode Operation for `Opcode::GetLocator`\n///\n/// Operation:\n///  - Find a binding on the environment and set the `current_binding` of the current frame.\n#[derive(Debug, Clone, Copy)]\npub(crate) struct GetLocator;\n\nimpl GetLocator {\n    #[inline(always)]\n    pub(crate) fn operation(index: IndexOperand, context: &mut Context) -> JsResult<()> {\n        let mut binding_locator =\n            context.vm.frame().code_block.bindings[usize::from(index)].clone();\n        context.find_runtime_binding(&mut binding_locator)?;\n\n        context.vm.frame_mut().binding_stack.push(binding_locator);\n\n        Ok(())\n    }\n}\n\nimpl Operation for GetLocator {\n    const NAME: &'static str = \"GetLocator\";\n    const INSTRUCTION: &'static str = \"INST - GetLocator\";\n    const COST: u8 = 4;\n}\n\n/// `GetNameAndLocator` implements the Opcode Operation for `Opcode::GetNameAndLocator`\n///\n/// Operation:\n///  - Find a binding on the environment chain and store its value in dst, setting the\n///    `current_binding` of the current frame.\n#[derive(Debug, Clone, Copy)]\npub(crate) struct GetNameAndLocator;\n\nimpl GetNameAndLocator {\n    #[inline(always)]\n    pub(crate) fn operation(\n        (value, index): (RegisterOperand, IndexOperand),\n        context: &mut Context,\n    ) -> JsResult<()> {\n        let mut binding_locator =\n            context.vm.frame().code_block.bindings[usize::from(index)].clone();\n        context.find_runtime_binding(&mut binding_locator)?;\n        let result = context.get_binding(&binding_locator)?.ok_or_else(|| {\n            let name = binding_locator.name().to_std_string_escaped();\n            JsNativeError::reference().with_message(format!(\"{name} is not defined\"))\n        })?;\n\n        context.vm.frame_mut().binding_stack.push(binding_locator);\n        context.vm.set_register(value.into(), result);\n        Ok(())\n    }\n}\n\nimpl Operation for GetNameAndLocator {\n    const NAME: &'static str = \"GetNameAndLocator\";\n    const INSTRUCTION: &'static str = \"INST - GetNameAndLocator\";\n    const COST: u8 = 4;\n}\n\n/// `GetNameOrUndefined` implements the Opcode Operation for `Opcode::GetNameOrUndefined`\n///\n/// Operation:\n///  - Find a binding on the environment chain and store its value in dst. If the binding does not exist, store undefined.\n#[derive(Debug, Clone, Copy)]\npub(crate) struct GetNameOrUndefined;\n\nimpl GetNameOrUndefined {\n    #[inline(always)]\n    pub(crate) fn operation(\n        (value, index): (RegisterOperand, IndexOperand),\n        context: &mut Context,\n    ) -> JsResult<()> {\n        let mut binding_locator =\n            context.vm.frame().code_block.bindings[usize::from(index)].clone();\n\n        let is_global = binding_locator.is_global();\n\n        context.find_runtime_binding(&mut binding_locator)?;\n\n        let result = if let Some(value) = context.get_binding(&binding_locator)? {\n            value\n        } else if is_global {\n            JsValue::undefined()\n        } else {\n            let name = binding_locator.name().to_std_string_escaped();\n            return Err(JsNativeError::reference()\n                .with_message(format!(\"{name} is not defined\"))\n                .into());\n        };\n\n        context.vm.set_register(value.into(), result);\n        Ok(())\n    }\n}\n\nimpl Operation for GetNameOrUndefined {\n    const NAME: &'static str = \"GetNameOrUndefined\";\n    const INSTRUCTION: &'static str = \"INST - GetNameOrUndefined\";\n    const COST: u8 = 4;\n}\n"
  },
  {
    "path": "core/engine/src/vm/opcode/get/private.rs",
    "content": "use crate::{\n    Context, JsResult,\n    vm::opcode::{IndexOperand, Operation, RegisterOperand},\n};\n\n/// `GetPrivateField` implements the Opcode Operation for `Opcode::GetPrivateField`\n///\n/// Operation:\n///  - Get a private property by name from an object and store it in dst.\n#[derive(Debug, Clone, Copy)]\npub(crate) struct GetPrivateField;\n\nimpl GetPrivateField {\n    #[inline(always)]\n    pub(crate) fn operation(\n        (dst, object, index): (RegisterOperand, RegisterOperand, IndexOperand),\n        context: &mut Context,\n    ) -> JsResult<()> {\n        let name = context\n            .vm\n            .frame()\n            .code_block()\n            .constant_string(index.into());\n        let object = context.vm.get_register(object.into()).clone();\n        let object = object.to_object(context)?;\n        let name = context\n            .vm\n            .frame()\n            .environments\n            .resolve_private_identifier(name)\n            .expect(\"private name must be in environment\");\n\n        let result = object.private_get(&name, context)?;\n        context.vm.set_register(dst.into(), result);\n        Ok(())\n    }\n}\n\nimpl Operation for GetPrivateField {\n    const NAME: &'static str = \"GetPrivateField\";\n    const INSTRUCTION: &'static str = \"INST - GetPrivateField\";\n    const COST: u8 = 4;\n}\n"
  },
  {
    "path": "core/engine/src/vm/opcode/get/property.rs",
    "content": "use boa_string::StaticJsStrings;\n\nuse crate::{\n    Context, JsResult, JsValue, js_string,\n    object::{internal_methods::InternalMethodPropertyContext, shape::slot::SlotAttributes},\n    property::PropertyKey,\n    vm::opcode::{IndexOperand, Operation, RegisterOperand},\n};\n\nfn get_by_name<const LENGTH: bool>(\n    (dst, object, receiver, index): (RegisterOperand, &JsValue, &JsValue, IndexOperand),\n    context: &mut Context,\n) -> JsResult<()> {\n    if LENGTH {\n        if let Some(object) = object.as_object()\n            && object.is_array()\n        {\n            let value = object.borrow().properties().storage[0].clone();\n            context.vm.set_register(dst.into(), value);\n            return Ok(());\n        } else if let Some(string) = object.as_string() {\n            // NOTE: Since we’re using the prototype returned directly by `base_class()`,\n            //       we need to handle string primitives separately due to the\n            //       string exotic internal methods.\n            context\n                .vm\n                .set_register(dst.into(), (string.len() as u32).into());\n            return Ok(());\n        }\n    }\n\n    // OPTIMIZATION:\n    //    Instead of calling `to_object()`, which creates a temporary wrapper object for primitive\n    //    values (e.g., numbers, strings, booleans) just to query their prototype chain.\n    //\n    //    To prevent the creation of a temporary JsObject, we directly retrieve the prototype that\n    //    `to_object()` would produce, such as `Number.prototype`, `String.prototype`, etc.\n    let object = object.base_class(context)?;\n\n    let ic = &context.vm.frame().code_block().ic[usize::from(index)];\n    let object_borrowed = object.borrow();\n    if let Some((shape, slot)) = ic.get(object_borrowed.shape()) {\n        let mut result = if slot.attributes.contains(SlotAttributes::PROTOTYPE) {\n            let prototype = shape.prototype().expect(\"prototype should have value\");\n            let prototype = prototype.borrow();\n            prototype.properties().storage[slot.index as usize].clone()\n        } else {\n            object_borrowed.properties().storage[slot.index as usize].clone()\n        };\n\n        drop(object_borrowed);\n        if slot.attributes.has_get() && result.is_object() {\n            result =\n                result\n                    .as_object()\n                    .expect(\"should contain getter\")\n                    .call(receiver, &[], context)?;\n        }\n        context.vm.set_register(dst.into(), result);\n        return Ok(());\n    }\n\n    drop(object_borrowed);\n\n    let key: PropertyKey = ic.name.clone().into();\n\n    let context = &mut InternalMethodPropertyContext::new(context);\n    let result = object.__get__(&key, receiver.clone(), context)?;\n\n    // Cache the property.\n    let slot = *context.slot();\n    if slot.is_cacheable() {\n        let ic = &context.vm.frame().code_block.ic[usize::from(index)];\n        let object_borrowed = object.borrow();\n        let shape = object_borrowed.shape();\n        ic.set(shape, slot);\n    }\n\n    context.vm.set_register(dst.into(), result);\n    Ok(())\n}\n\nfn get_by_value<const PUSH_KEY: bool>(\n    (dst, key, receiver, object): (\n        RegisterOperand,\n        RegisterOperand,\n        RegisterOperand,\n        RegisterOperand,\n    ),\n    context: &mut Context,\n) -> JsResult<()> {\n    let key_value = context.vm.get_register(key.into()).clone();\n    let base = context.vm.get_register(object.into()).clone();\n    let object = base.base_class(context)?;\n    let key_value = key_value.to_property_key(context)?;\n\n    // Fast Path\n    //\n    // NOTE: Since we’re using the prototype returned directly by `base_class()`,\n    //       we need to handle string primitives separately due to the\n    //       string exotic internal methods.\n    match &key_value {\n        PropertyKey::Index(index) => {\n            if object.is_array() {\n                let object_borrowed = object.borrow();\n                if let Some(element) = object_borrowed.properties().get_dense_property(index.get())\n                {\n                    if PUSH_KEY {\n                        context.vm.set_register(key.into(), key_value.into());\n                    }\n\n                    context.vm.set_register(dst.into(), element);\n                    return Ok(());\n                }\n            } else if let Some(string) = base.as_string() {\n                let value = string\n                    .code_unit_at(index.get() as usize)\n                    .map_or_else(JsValue::undefined, |char| {\n                        js_string!([char].as_slice()).into()\n                    });\n\n                if PUSH_KEY {\n                    context.vm.set_register(key.into(), key_value.into());\n                }\n                context.vm.set_register(dst.into(), value);\n                return Ok(());\n            }\n        }\n        PropertyKey::String(string) if *string == StaticJsStrings::LENGTH => {\n            if let Some(string) = base.as_string() {\n                let value = string.len().into();\n\n                if PUSH_KEY {\n                    context.vm.set_register(key.into(), key_value.into());\n                }\n                context.vm.set_register(dst.into(), value);\n                return Ok(());\n            }\n        }\n        _ => {}\n    }\n\n    let receiver = context.vm.get_register(receiver.into());\n\n    // Slow path:\n    let result = object.__get__(\n        &key_value,\n        receiver.clone(),\n        &mut InternalMethodPropertyContext::new(context),\n    )?;\n\n    if PUSH_KEY {\n        context.vm.set_register(key.into(), key_value.into());\n    }\n    context.vm.set_register(dst.into(), result);\n    Ok(())\n}\n\n#[derive(Debug, Clone, Copy)]\npub(crate) struct GetLengthProperty;\n\nimpl GetLengthProperty {\n    #[inline(always)]\n    pub(crate) fn operation(\n        (dst, object, index): (RegisterOperand, RegisterOperand, IndexOperand),\n        context: &mut Context,\n    ) -> JsResult<()> {\n        let object = context.vm.get_register(object.into()).clone();\n        get_by_name::<true>((dst, &object, &object, index), context)\n    }\n}\n\nimpl Operation for GetLengthProperty {\n    const NAME: &'static str = \"GetLengthProperty\";\n    const INSTRUCTION: &'static str = \"INST - GetLengthProperty\";\n    const COST: u8 = 4;\n}\n\n/// `GetPropertyByName` implements the Opcode Operation for `Opcode::GetPropertyByName`\n///\n/// Operation:\n///  - Get a property by name from an object.\n#[derive(Debug, Clone, Copy)]\npub(crate) struct GetPropertyByName;\n\nimpl GetPropertyByName {\n    #[inline(always)]\n    pub(crate) fn operation(\n        (dst, object, index): (RegisterOperand, RegisterOperand, IndexOperand),\n        context: &mut Context,\n    ) -> JsResult<()> {\n        let object = context.vm.get_register(object.into()).clone();\n        get_by_name::<false>((dst, &object, &object, index), context)\n    }\n}\n\nimpl Operation for GetPropertyByName {\n    const NAME: &'static str = \"GetPropertyByName\";\n    const INSTRUCTION: &'static str = \"INST - GetPropertyByName\";\n    const COST: u8 = 4;\n}\n\n/// `GetPropertyByNameWithThis` implements the Opcode Operation for `Opcode::GetPropertyByNameWithThis`\n///\n/// Operation:\n///  - Get a property by name from an object with this.\n#[derive(Debug, Clone, Copy)]\npub(crate) struct GetPropertyByNameWithThis;\n\nimpl GetPropertyByNameWithThis {\n    #[inline(always)]\n    pub(crate) fn operation(\n        (dst, receiver, value, index): (\n            RegisterOperand,\n            RegisterOperand,\n            RegisterOperand,\n            IndexOperand,\n        ),\n        context: &mut Context,\n    ) -> JsResult<()> {\n        let receiver = context.vm.get_register(receiver.into()).clone();\n        let object = context.vm.get_register(value.into()).clone();\n        get_by_name::<false>((dst, &object, &receiver, index), context)\n    }\n}\n\nimpl Operation for GetPropertyByNameWithThis {\n    const NAME: &'static str = \"GetPropertyByNameWithThis\";\n    const INSTRUCTION: &'static str = \"INST - GetPropertyByNameWithThis\";\n    const COST: u8 = 4;\n}\n\n/// `GetPropertyByValue` implements the Opcode Operation for `Opcode::GetPropertyByValue`\n///\n/// Operation:\n///  - Get a property by value from an object and store it in dst.\n#[derive(Debug, Clone, Copy)]\npub(crate) struct GetPropertyByValue;\n\nimpl GetPropertyByValue {\n    #[inline(always)]\n    pub(crate) fn operation(\n        args: (\n            RegisterOperand,\n            RegisterOperand,\n            RegisterOperand,\n            RegisterOperand,\n        ),\n        context: &mut Context,\n    ) -> JsResult<()> {\n        get_by_value::<false>(args, context)\n    }\n}\n\nimpl Operation for GetPropertyByValue {\n    const NAME: &'static str = \"GetPropertyByValue\";\n    const INSTRUCTION: &'static str = \"INST - GetPropertyByValue\";\n    const COST: u8 = 4;\n}\n\n/// `GetPropertyByValuePush` implements the Opcode Operation for `Opcode::GetPropertyByValuePush`\n///\n/// Operation:\n///  - Get a property by value from an object and store the key and value in registers.\n#[derive(Debug, Clone, Copy)]\npub(crate) struct GetPropertyByValuePush;\n\nimpl GetPropertyByValuePush {\n    #[inline(always)]\n    pub(crate) fn operation(\n        args: (\n            RegisterOperand,\n            RegisterOperand,\n            RegisterOperand,\n            RegisterOperand,\n        ),\n        context: &mut Context,\n    ) -> JsResult<()> {\n        get_by_value::<true>(args, context)\n    }\n}\n\nimpl Operation for GetPropertyByValuePush {\n    const NAME: &'static str = \"GetPropertyByValuePush\";\n    const INSTRUCTION: &'static str = \"INST - GetPropertyByValuePush\";\n    const COST: u8 = 4;\n}\n"
  },
  {
    "path": "core/engine/src/vm/opcode/iteration/for_in.rs",
    "content": "use crate::{\n    Context, JsResult, JsValue,\n    builtins::{iterable::IteratorRecord, object::for_in_iterator::ForInIterator},\n    vm::opcode::{Operation, RegisterOperand},\n};\n\n/// `CreateForInIterator` implements the Opcode Operation for `Opcode::CreateForInIterator`\n///\n/// Operation:\n///  - Creates a new `ForInIterator` for the provided object.\n#[derive(Debug, Clone, Copy)]\npub(crate) struct CreateForInIterator;\n\nimpl CreateForInIterator {\n    #[inline(always)]\n    pub(crate) fn operation(value: RegisterOperand, context: &mut Context) -> JsResult<()> {\n        let object = context.vm.get_register(value.into()).clone();\n        let object = object.to_object(context)?;\n        let (iterator, next_method) =\n            ForInIterator::create_for_in_iterator(JsValue::new(object), context);\n\n        context\n            .vm\n            .frame_mut()\n            .iterators\n            .push(IteratorRecord::new(iterator, next_method));\n\n        Ok(())\n    }\n}\n\nimpl Operation for CreateForInIterator {\n    const NAME: &'static str = \"CreateForInIterator\";\n    const INSTRUCTION: &'static str = \"INST - CreateForInIterator\";\n    const COST: u8 = 4;\n}\n"
  },
  {
    "path": "core/engine/src/vm/opcode/iteration/get.rs",
    "content": "use crate::{\n    Context, JsResult,\n    builtins::iterable::IteratorHint,\n    vm::opcode::{Operation, RegisterOperand},\n};\n\n/// `GetIterator` implements the Opcode Operation for `Opcode::GetIterator`\n///\n/// Operation:\n///  - Initialize an iterator\n#[derive(Debug, Clone, Copy)]\npub(crate) struct GetIterator;\n\nimpl GetIterator {\n    #[inline(always)]\n    pub(crate) fn operation(value: RegisterOperand, context: &mut Context) -> JsResult<()> {\n        let value = context.vm.get_register(value.into()).clone();\n        let iterator = value.get_iterator(IteratorHint::Sync, context)?;\n        context.vm.frame_mut().iterators.push(iterator);\n        Ok(())\n    }\n}\n\nimpl Operation for GetIterator {\n    const NAME: &'static str = \"GetIterator\";\n    const INSTRUCTION: &'static str = \"INST - GetIterator\";\n    const COST: u8 = 6;\n}\n\n/// `GetAsyncIterator` implements the Opcode Operation for `Opcode::GetAsyncIterator`\n///\n/// Operation:\n///  - Initialize an async iterator.\n#[derive(Debug, Clone, Copy)]\npub(crate) struct GetAsyncIterator;\n\nimpl GetAsyncIterator {\n    #[inline(always)]\n    pub(crate) fn operation(value: RegisterOperand, context: &mut Context) -> JsResult<()> {\n        let value = context.vm.get_register(value.into()).clone();\n        let iterator = value.get_iterator(IteratorHint::Async, context)?;\n        context.vm.frame_mut().iterators.push(iterator);\n        Ok(())\n    }\n}\n\nimpl Operation for GetAsyncIterator {\n    const NAME: &'static str = \"GetAsyncIterator\";\n    const INSTRUCTION: &'static str = \"INST - GetAsyncIterator\";\n    const COST: u8 = 6;\n}\n"
  },
  {
    "path": "core/engine/src/vm/opcode/iteration/iterator.rs",
    "content": "use crate::{\n    Context, JsExpect, JsResult,\n    builtins::{\n        Array,\n        iterable::{IteratorRecord, create_iter_result_object},\n    },\n    js_string,\n    vm::{\n        GeneratorResumeKind,\n        opcode::{IndexOperand, Operation, RegisterOperand},\n    },\n};\n\n/// `IteratorPop` implements the Opcode Operation for `Opcode::IteratorPop`\n///\n/// Operation:\n///  - Pops the last iterator on the iterators stack.\n///\n/// Registers (out):\n///  - iterator: `JsObject`.\n///  - next: `JsValue`.\n#[derive(Debug, Clone, Copy)]\npub(crate) struct IteratorPop;\n\nimpl IteratorPop {\n    #[inline(always)]\n    pub(crate) fn operation(\n        (iterator, next): (RegisterOperand, RegisterOperand),\n        context: &mut Context,\n    ) -> JsResult<()> {\n        let iterator_record = context\n            .vm\n            .frame_mut()\n            .iterators\n            .pop()\n            .js_expect(\"iterator stack should have at least an iterator\")?;\n\n        context\n            .vm\n            .set_register(iterator.into(), iterator_record.iterator().clone().into());\n        context\n            .vm\n            .set_register(next.into(), iterator_record.next_method().clone());\n\n        Ok(())\n    }\n}\n\nimpl Operation for IteratorPop {\n    const NAME: &'static str = \"IteratorPop\";\n    const INSTRUCTION: &'static str = \"INST - IteratorPop\";\n    const COST: u8 = 3;\n}\n\n/// `IteratorPush` implements the Opcode Operation for `Opcode::IteratorPush`\n///\n/// Operation:\n///  - Pushes an iterator on the iterators stack\n///\n/// Registers (in):\n///  - iterator: `JsObject`.\n///  - next: `JsValue`.\n#[derive(Debug, Clone, Copy)]\npub(crate) struct IteratorPush;\n\nimpl IteratorPush {\n    #[inline(always)]\n    pub(crate) fn operation(\n        (iterator, next): (RegisterOperand, RegisterOperand),\n        context: &mut Context,\n    ) -> JsResult<()> {\n        let iterator = context\n            .vm\n            .get_register(iterator.into())\n            .as_object()\n            .js_expect(\"iterator should be an object\")?;\n        let next = context.vm.get_register(next.into()).clone();\n\n        context\n            .vm\n            .frame_mut()\n            .iterators\n            .push(IteratorRecord::new(iterator, next));\n\n        Ok(())\n    }\n}\n\nimpl Operation for IteratorPush {\n    const NAME: &'static str = \"IteratorPush\";\n    const INSTRUCTION: &'static str = \"INST - IteratorPush\";\n    const COST: u8 = 3;\n}\n\n/// `IteratorUpdateResult` implements the Opcode Operation for `Opcode::IteratorUpdateResult`\n///\n/// Operation:\n///  - Updates the result of the currently active iterator.\n///\n/// Registers (inout):\n///  - result: `JsValue` (in), `bool` (out) with the `done` value of the iterator.\n#[derive(Debug, Clone, Copy)]\npub(crate) struct IteratorUpdateResult;\n\nimpl IteratorUpdateResult {\n    #[inline(always)]\n    pub(crate) fn operation(result: RegisterOperand, context: &mut Context) -> JsResult<()> {\n        let mut iterator = context\n            .vm\n            .frame_mut()\n            .iterators\n            .pop()\n            .js_expect(\"iterator stack should have at least an iterator\")?;\n        let result_v = context.vm.take_register(result.into());\n        iterator.update_result(result_v, context)?;\n        context\n            .vm\n            .set_register(result.into(), iterator.done().into());\n        context.vm.frame_mut().iterators.push(iterator);\n\n        Ok(())\n    }\n}\n\nimpl Operation for IteratorUpdateResult {\n    const NAME: &'static str = \"IteratorUpdateResult\";\n    const INSTRUCTION: &'static str = \"INST - IteratorUpdateResult\";\n    const COST: u8 = 2;\n}\n\n/// `IteratorNext` implements the Opcode Operation for `Opcode::IteratorNext`\n///\n/// Operation:\n///  - Calls the `next` method of `iterator`, updating its record with the next value.\n#[derive(Debug, Clone, Copy)]\npub(crate) struct IteratorNext;\n\nimpl IteratorNext {\n    #[inline(always)]\n    pub(crate) fn operation((): (), context: &mut Context) -> JsResult<()> {\n        let mut iterator = context\n            .vm\n            .frame_mut()\n            .iterators\n            .pop()\n            .expect(\"iterator stack should have at least an iterator\");\n\n        iterator.step(context)?;\n\n        context.vm.frame_mut().iterators.push(iterator);\n\n        Ok(())\n    }\n}\n\nimpl Operation for IteratorNext {\n    const NAME: &'static str = \"IteratorNext\";\n    const INSTRUCTION: &'static str = \"INST - IteratorNext\";\n    const COST: u8 = 6;\n}\n\n/// `IteratorFinishAsyncNext` implements the Opcode Operation for `Opcode::IteratorFinishAsyncNext`.\n///\n/// Operation:\n///  - Finishes the call to `Opcode::IteratorNext` within a `for await` loop by setting the current\n///    result of the current iterator.\n#[derive(Debug, Clone, Copy)]\npub(crate) struct IteratorFinishAsyncNext;\n\nimpl IteratorFinishAsyncNext {\n    #[inline(always)]\n    pub(crate) fn operation(\n        (resume_kind, value): (RegisterOperand, RegisterOperand),\n        context: &mut Context,\n    ) -> JsResult<()> {\n        let mut iterator = context\n            .vm\n            .frame_mut()\n            .iterators\n            .pop()\n            .expect(\"iterator on the call frame must exist\");\n\n        let resume_kind = context\n            .vm\n            .get_register(resume_kind.into())\n            .to_generator_resume_kind();\n\n        if matches!(resume_kind, GeneratorResumeKind::Throw) {\n            // If after awaiting the `next` call the iterator returned an error, it can be considered\n            // as poisoned, meaning we can remove it from the iterator stack to avoid calling\n            // cleanup operations on it.\n            return Ok(());\n        }\n\n        let value = context.vm.get_register(value.into());\n        iterator.update_result(value.clone(), context)?;\n        context.vm.frame_mut().iterators.push(iterator);\n        Ok(())\n    }\n}\n\nimpl Operation for IteratorFinishAsyncNext {\n    const NAME: &'static str = \"IteratorFinishAsyncNext\";\n    const INSTRUCTION: &'static str = \"INST - IteratorFinishAsyncNext\";\n    const COST: u8 = 5;\n}\n\n/// `IteratorResult` implements the Opcode Operation for `Opcode::IteratorResult`\n///\n/// Operation:\n///  - Gets the last iteration result of the current iterator record.\n#[derive(Debug, Clone, Copy)]\npub(crate) struct IteratorResult;\n\nimpl IteratorResult {\n    #[inline(always)]\n    pub(crate) fn operation(value: RegisterOperand, context: &mut Context) {\n        let last_result = context\n            .vm\n            .frame()\n            .iterators\n            .last()\n            .expect(\"iterator on the call frame must exist\")\n            .last_result()\n            .object()\n            .clone();\n        context.vm.set_register(value.into(), last_result.into());\n    }\n}\n\nimpl Operation for IteratorResult {\n    const NAME: &'static str = \"IteratorResult\";\n    const INSTRUCTION: &'static str = \"INST - IteratorResult\";\n    const COST: u8 = 3;\n}\n\n/// `IteratorValue` implements the Opcode Operation for `Opcode::IteratorValue`\n///\n/// Operation:\n///  - Gets the `value` property of the current iterator record.\n#[derive(Debug, Clone, Copy)]\npub(crate) struct IteratorValue;\n\nimpl IteratorValue {\n    #[inline(always)]\n    pub(crate) fn operation(value: RegisterOperand, context: &mut Context) -> JsResult<()> {\n        let mut iterator = context\n            .vm\n            .frame_mut()\n            .iterators\n            .pop()\n            .expect(\"iterator on the call frame must exist\");\n\n        let iter_value = iterator.value(context)?;\n        context.vm.set_register(value.into(), iter_value);\n\n        context.vm.frame_mut().iterators.push(iterator);\n\n        Ok(())\n    }\n}\n\nimpl Operation for IteratorValue {\n    const NAME: &'static str = \"IteratorValue\";\n    const INSTRUCTION: &'static str = \"INST - IteratorValue\";\n    const COST: u8 = 5;\n}\n\n/// `IteratorDone` implements the Opcode Operation for `Opcode::IteratorDone`\n///\n/// Operation:\n///  - Returns `true` if the current iterator is done, or `false` otherwise\n#[derive(Debug, Clone, Copy)]\npub(crate) struct IteratorDone;\n\nimpl IteratorDone {\n    #[inline(always)]\n    pub(crate) fn operation(done: RegisterOperand, context: &mut Context) {\n        let value = context\n            .vm\n            .frame()\n            .iterators\n            .last()\n            .expect(\"iterator on the call frame must exist\")\n            .done();\n        context.vm.set_register(done.into(), value.into());\n    }\n}\n\nimpl Operation for IteratorDone {\n    const NAME: &'static str = \"IteratorDone\";\n    const INSTRUCTION: &'static str = \"INST - IteratorDone\";\n    const COST: u8 = 3;\n}\n\n/// `IteratorReturn` implements the Opcode Operation for `Opcode::IteratorReturn`\n///\n/// Operation:\n///  - Calls `return` on the current iterator and returns the result.\n#[derive(Debug, Clone, Copy)]\npub(crate) struct IteratorReturn;\n\nimpl IteratorReturn {\n    #[inline(always)]\n    pub(crate) fn operation(\n        (value, called): (RegisterOperand, RegisterOperand),\n        context: &mut Context,\n    ) -> JsResult<()> {\n        let Some(record) = context.vm.frame_mut().iterators.pop() else {\n            context.vm.set_register(called.into(), false.into());\n            return Ok(());\n        };\n\n        if record.done() {\n            context.vm.set_register(called.into(), false.into());\n            return Ok(());\n        }\n\n        let Some(ret) = record\n            .iterator()\n            .get_method(js_string!(\"return\"), context)?\n        else {\n            context.vm.set_register(called.into(), false.into());\n            return Ok(());\n        };\n\n        let old_return_value = context.vm.get_return_value();\n\n        let return_value = ret.call(&record.iterator().clone().into(), &[], context)?;\n\n        context.vm.set_return_value(old_return_value);\n\n        context.vm.set_register(value.into(), return_value);\n        context.vm.set_register(called.into(), true.into());\n\n        Ok(())\n    }\n}\n\nimpl Operation for IteratorReturn {\n    const NAME: &'static str = \"IteratorReturn\";\n    const INSTRUCTION: &'static str = \"INST - IteratorReturn\";\n    const COST: u8 = 8;\n}\n\n/// `IteratorToArray` implements the Opcode Operation for `Opcode::IteratorToArray`\n///\n/// Operation:\n///  - Consume the iterator and construct and array with all the values.\n#[derive(Debug, Clone, Copy)]\npub(crate) struct IteratorToArray;\n\nimpl IteratorToArray {\n    #[inline(always)]\n    pub(crate) fn operation(array: RegisterOperand, context: &mut Context) -> JsResult<()> {\n        let mut iterator = context\n            .vm\n            .frame_mut()\n            .iterators\n            .pop()\n            .expect(\"iterator on the call frame must exist\");\n\n        let mut values = Vec::new();\n\n        loop {\n            let done = match iterator.step(context) {\n                Ok(done) => done,\n                Err(err) => {\n                    context.vm.frame_mut().iterators.push(iterator);\n                    return Err(err);\n                }\n            };\n\n            if done {\n                break;\n            }\n\n            match iterator.value(context) {\n                Ok(value) => values.push(value),\n                Err(err) => {\n                    context.vm.frame_mut().iterators.push(iterator);\n                    return Err(err);\n                }\n            }\n        }\n\n        context.vm.frame_mut().iterators.push(iterator);\n        let result = Array::create_array_from_list(values, context);\n        context.vm.set_register(array.into(), result.into());\n        Ok(())\n    }\n}\n\nimpl Operation for IteratorToArray {\n    const NAME: &'static str = \"IteratorToArray\";\n    const INSTRUCTION: &'static str = \"INST - IteratorToArray\";\n    const COST: u8 = 8;\n}\n\n/// `IteratorStackEmpty` implements the Opcode Operation for `Opcode::IteratorStackEmpty`\n///\n/// Operation:\n/// - Store `true` in dst if the iterator stack is empty.\n#[derive(Debug, Clone, Copy)]\npub(crate) struct IteratorStackEmpty;\n\nimpl IteratorStackEmpty {\n    #[inline(always)]\n    pub(crate) fn operation(empty: RegisterOperand, context: &mut Context) {\n        let is_empty = context.vm.frame().iterators.is_empty();\n        context.vm.set_register(empty.into(), is_empty.into());\n    }\n}\n\nimpl Operation for IteratorStackEmpty {\n    const NAME: &'static str = \"IteratorStackEmpty\";\n    const INSTRUCTION: &'static str = \"INST - IteratorStackEmpty\";\n    const COST: u8 = 1;\n}\n\n/// `CreateIteratorResult` implements the Opcode Operation for `Opcode::CreateIteratorResult`\n///\n/// Operation:\n/// -  Creates a new iterator result object\n#[derive(Debug, Clone, Copy)]\npub(crate) struct CreateIteratorResult;\n\nimpl CreateIteratorResult {\n    #[inline(always)]\n    pub(crate) fn operation((value, done): (RegisterOperand, IndexOperand), context: &mut Context) {\n        let done = u32::from(done) != 0;\n        let val = context.vm.take_register(value.into());\n        let result = create_iter_result_object(val, done, context);\n        context.vm.set_register(value.into(), result);\n    }\n}\n\nimpl Operation for CreateIteratorResult {\n    const NAME: &'static str = \"CreateIteratorResult\";\n    const INSTRUCTION: &'static str = \"INST - CreateIteratorResult\";\n    const COST: u8 = 3;\n}\n"
  },
  {
    "path": "core/engine/src/vm/opcode/iteration/loop_ops.rs",
    "content": "use crate::error::RuntimeLimitError;\nuse crate::{Context, JsResult, vm::opcode::Operation};\n\n/// `IncrementLoopIteration` implements the Opcode Operation for `Opcode::IncrementLoopIteration`.\n///\n/// Operation:\n///  - Increment loop iteration count.\n#[derive(Debug, Clone, Copy)]\npub(crate) struct IncrementLoopIteration;\n\nimpl IncrementLoopIteration {\n    #[inline(always)]\n    pub(crate) fn operation((): (), context: &mut Context) -> JsResult<()> {\n        let max = context.vm.runtime_limits.loop_iteration_limit();\n        let frame = context.vm.frame_mut();\n        let previous_iteration_count = frame.loop_iteration_count;\n\n        if previous_iteration_count > max {\n            return Err(RuntimeLimitError::LoopIteration.into());\n        }\n\n        frame.loop_iteration_count = previous_iteration_count.wrapping_add(1);\n        Ok(())\n    }\n}\n\nimpl Operation for IncrementLoopIteration {\n    const NAME: &'static str = \"IncrementLoopIteration\";\n    const INSTRUCTION: &'static str = \"INST - IncrementLoopIteration\";\n    const COST: u8 = 3;\n}\n"
  },
  {
    "path": "core/engine/src/vm/opcode/iteration/mod.rs",
    "content": "pub(crate) mod for_in;\npub(crate) mod get;\npub(crate) mod iterator;\npub(crate) mod loop_ops;\n\npub(crate) use for_in::*;\npub(crate) use get::*;\npub(crate) use iterator::*;\npub(crate) use loop_ops::*;\n"
  },
  {
    "path": "core/engine/src/vm/opcode/meta/mod.rs",
    "content": "use super::RegisterOperand;\nuse crate::{\n    Context, JsObject, JsValue,\n    module::ModuleKind,\n    vm::{ActiveRunnable, opcode::Operation},\n};\nuse std::unreachable;\n\n/// `NewTarget` implements the Opcode Operation for `Opcode::NewTarget`\n///\n/// Operation:\n///  - Store the current new target in dst.\n#[derive(Debug, Clone, Copy)]\npub(crate) struct NewTarget;\n\nimpl NewTarget {\n    #[inline(always)]\n    pub(super) fn operation(dst: RegisterOperand, context: &mut Context) {\n        let new_target = if let Some(new_target) = {\n            let frame = context.vm.frame();\n            frame\n                .environments\n                .get_this_environment(frame.realm.environment())\n                .as_function()\n                .and_then(|env| env.slots().new_target().cloned())\n        } {\n            new_target.into()\n        } else {\n            JsValue::undefined()\n        };\n        context.vm.set_register(dst.into(), new_target);\n    }\n}\n\nimpl Operation for NewTarget {\n    const NAME: &'static str = \"NewTarget\";\n    const INSTRUCTION: &'static str = \"INST - NewTarget\";\n    const COST: u8 = 2;\n}\n\n/// `ImportMeta` implements the Opcode Operation for `Opcode::ImportMeta`\n///\n/// Operation:\n///  - Store the current `import.meta` in dst.\n#[derive(Debug, Clone, Copy)]\npub(crate) struct ImportMeta;\n\nimpl ImportMeta {\n    #[inline(always)]\n    pub(super) fn operation(dst: RegisterOperand, context: &mut Context) {\n        // Meta Properties\n        //\n        // ImportMeta : import . meta\n        //\n        // https://tc39.es/ecma262/#sec-meta-properties\n\n        // 1. Let module be GetActiveScriptOrModule().\n\n        let Some(ActiveRunnable::Module(module)) = context.get_active_script_or_module() else {\n            unreachable!(\"2. Assert: module is a Source Text Module Record.\");\n        };\n\n        let ModuleKind::SourceText(src) = module.kind() else {\n            unreachable!(\"2. Assert: module is a Source Text Module Record.\");\n        };\n\n        // 3. Let importMeta be module.[[ImportMeta]].\n        // 4. If importMeta is empty, then\n        // 5. Else,\n        //     a. Assert: importMeta is an Object.\n        let import_meta = src\n            .import_meta()\n            .borrow_mut()\n            .get_or_insert_with(|| {\n                // a. Set importMeta to OrdinaryObjectCreate(null).\n                let import_meta = JsObject::with_null_proto();\n\n                // b. Let importMetaValues be HostGetImportMetaProperties(module).\n                // c. For each Record { [[Key]], [[Value]] } p of importMetaValues, do\n                //     i. Perform ! CreateDataPropertyOrThrow(importMeta, p.[[Key]], p.[[Value]]).\n                // d. Perform HostFinalizeImportMeta(importMeta, module).\n                context\n                    .module_loader()\n                    .init_import_meta(&import_meta, &module, context);\n\n                // e. Set module.[[ImportMeta]] to importMeta.\n                import_meta\n            })\n            .clone();\n\n        //     b. Return importMeta.\n        //     f. Return importMeta.\n        context.vm.set_register(dst.into(), import_meta.into());\n    }\n}\n\nimpl Operation for ImportMeta {\n    const NAME: &'static str = \"ImportMeta\";\n    const INSTRUCTION: &'static str = \"INST - ImportMeta\";\n    const COST: u8 = 6;\n}\n"
  },
  {
    "path": "core/engine/src/vm/opcode/mod.rs",
    "content": "#![allow(clippy::inline_always)]\n#![allow(clippy::doc_markdown)]\nuse crate::{\n    Context,\n    vm::{completion_record::CompletionRecord, completion_record::IntoCompletionRecord},\n};\nuse args::{Argument, read};\nuse std::ops::ControlFlow;\nuse thin_vec::ThinVec;\n\nmod args;\n\n// Operation modules\nmod arguments;\nmod r#await;\nmod binary_ops;\nmod call;\nmod concat;\nmod control_flow;\nmod copy;\nmod define;\nmod delete;\nmod environment;\nmod function;\nmod generator;\nmod get;\nmod iteration;\nmod meta;\nmod new;\nmod nop;\nmod object;\nmod pop;\nmod push;\nmod rest_parameter;\nmod set;\nmod switch;\nmod templates;\nmod to;\nmod unary_ops;\nmod value;\n\n// Operation structs\n#[doc(inline)]\npub(crate) use arguments::*;\n#[doc(inline)]\npub(crate) use r#await::*;\n#[doc(inline)]\npub(crate) use binary_ops::*;\n#[doc(inline)]\npub(crate) use call::*;\n#[doc(inline)]\npub(crate) use concat::*;\n#[doc(inline)]\npub(crate) use control_flow::*;\n#[doc(inline)]\npub(crate) use copy::*;\n#[doc(inline)]\npub(crate) use define::*;\n#[doc(inline)]\npub(crate) use delete::*;\n#[doc(inline)]\npub(crate) use environment::*;\n#[doc(inline)]\npub(crate) use function::*;\n#[doc(inline)]\npub(crate) use generator::*;\n#[doc(inline)]\npub(crate) use get::*;\n#[doc(inline)]\npub(crate) use iteration::*;\n#[doc(inline)]\npub(crate) use meta::*;\n#[doc(inline)]\npub(crate) use new::*;\n#[doc(inline)]\npub(crate) use nop::*;\n#[doc(inline)]\npub(crate) use object::*;\n#[doc(inline)]\npub(crate) use pop::*;\n#[doc(inline)]\npub(crate) use push::*;\n#[doc(inline)]\npub(crate) use rest_parameter::*;\n#[doc(inline)]\npub(crate) use set::*;\n#[doc(inline)]\npub(crate) use switch::*;\n#[doc(inline)]\npub(crate) use templates::*;\n#[doc(inline)]\npub(crate) use to::*;\n#[doc(inline)]\npub(crate) use unary_ops::*;\n#[doc(inline)]\npub(crate) use value::*;\n\n/// Specific opcodes for bindings.\n///\n/// This separate enum exists to make matching exhaustive where needed.\n#[derive(Clone, Copy, Debug)]\npub(crate) enum BindingOpcode {\n    Var,\n    InitVar,\n    InitLexical,\n    SetName,\n}\n\n/// The `Operation` trait implements the execution code along with the\n/// identifying Name and Instruction value for an Boa Opcode.\n///\n/// This trait should be implemented for a struct that corresponds with\n/// any arm of the `OpCode` enum.\npub(crate) trait Operation {\n    const NAME: &'static str;\n    #[allow(unused)] // TODO: need to double check usage.\n    const INSTRUCTION: &'static str;\n    const COST: u8;\n}\n\n/// The compile time representation of bytecode instructions.\n#[derive(Debug)]\npub(crate) struct BytecodeEmitter {\n    bytes: Vec<u8>,\n}\n\nimpl BytecodeEmitter {\n    /// Create a new [`BytecodeEmitter`] instance.\n    pub(crate) fn new() -> Self {\n        Self { bytes: Vec::new() }\n    }\n\n    /// Convert the [`BytecodeEmitter`] into a [`Bytecode`] instance.\n    pub(crate) fn into_bytecode(self) -> Bytecode {\n        Bytecode {\n            bytes: self.bytes.into_boxed_slice(),\n        }\n    }\n\n    /// Get the location of the next opcode in the bytecode.\n    pub(crate) fn next_opcode_location(&self) -> Address {\n        Address::new(self.bytes.len() as u32)\n    }\n\n    /// Patch the jump instruction at the given label with the given address.\n    pub(crate) fn patch_jump(&mut self, label: Address, patch: Address) {\n        let pos = u32::from(label) as usize;\n        let bytes = u32::from(patch).to_le_bytes();\n        self.bytes[pos + 1] = bytes[0];\n        self.bytes[pos + 2] = bytes[1];\n        self.bytes[pos + 3] = bytes[2];\n        self.bytes[pos + 4] = bytes[3];\n    }\n\n    /// Patch the jump instruction at the given label with jump table addresses.\n    pub(crate) fn patch_jump_table(&mut self, label: Address, patch: &[Address]) {\n        let length_offset = u32::from(label) as usize + 1;\n\n        let (length, first_offset) = read::<u32>(&self.bytes, length_offset);\n        assert_eq!(length as usize, patch.len());\n\n        // Write patched address values.\n        for (i, value) in patch.iter().enumerate() {\n            let offset = first_offset + i * size_of::<u32>();\n            self.bytes[offset..offset + size_of::<u32>()]\n                .copy_from_slice(&u32::from(*value).to_le_bytes());\n        }\n    }\n}\n\n#[derive(Clone, Debug, Default)]\n/// The bytecode representation of a codeblock.\npub(crate) struct Bytecode {\n    pub(crate) bytes: Box<[u8]>,\n}\n\n#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]\n/// An address is a bytecode offset, displayed as hexadecimal.\npub(crate) struct Address(u32);\n\nimpl Address {\n    /// Create a new [`Address`] from a u32 value.\n    pub(crate) const fn new(value: u32) -> Self {\n        Self(value)\n    }\n\n    /// Returns the inner `u32` value.\n    pub(crate) const fn as_u32(self) -> u32 {\n        self.0\n    }\n}\n\nimpl From<Address> for u32 {\n    fn from(addr: Address) -> Self {\n        addr.0\n    }\n}\n\nimpl From<u32> for Address {\n    fn from(value: u32) -> Self {\n        Self::new(value)\n    }\n}\n\nimpl std::ops::Add<u32> for Address {\n    type Output = Self;\n\n    fn add(self, rhs: u32) -> Self {\n        Self::new(self.0 + rhs)\n    }\n}\n\nimpl std::fmt::Display for Address {\n    #[inline]\n    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {\n        write!(f, \"{:06x}\", self.0)\n    }\n}\n\n#[derive(Debug, Clone, Copy)]\n/// A register operand is a register index used in bytecode instructions.\npub(crate) struct RegisterOperand(u32);\n\nimpl RegisterOperand {\n    /// Create a new [`RegisterOperand`] from a u32 value.\n    pub(crate) fn new(value: u32) -> Self {\n        Self(value)\n    }\n}\n\nimpl From<RegisterOperand> for u32 {\n    fn from(value: RegisterOperand) -> Self {\n        value.0\n    }\n}\n\nimpl From<RegisterOperand> for usize {\n    fn from(value: RegisterOperand) -> Self {\n        value.0 as usize\n    }\n}\n\nimpl From<u8> for RegisterOperand {\n    fn from(value: u8) -> Self {\n        Self::new(value.into())\n    }\n}\n\nimpl From<u16> for RegisterOperand {\n    fn from(value: u16) -> Self {\n        Self::new(value.into())\n    }\n}\n\nimpl From<u32> for RegisterOperand {\n    fn from(value: u32) -> Self {\n        Self::new(value)\n    }\n}\n\nimpl std::fmt::Display for RegisterOperand {\n    #[inline]\n    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {\n        write!(f, \"r{:02}\", self.0)\n    }\n}\n\n#[derive(Debug, Clone, Copy)]\n/// A index operand is e.g. an index into the constant pool\npub(crate) struct IndexOperand(u32);\n\nimpl IndexOperand {\n    /// Create a new [`IndexOperand`] from a u32 value.\n    pub(crate) fn new(value: u32) -> Self {\n        Self(value)\n    }\n}\n\nimpl From<IndexOperand> for u32 {\n    fn from(value: IndexOperand) -> Self {\n        value.0\n    }\n}\n\nimpl From<IndexOperand> for usize {\n    fn from(value: IndexOperand) -> Self {\n        value.0 as usize\n    }\n}\n\nimpl From<bool> for IndexOperand {\n    fn from(value: bool) -> Self {\n        Self::new(value.into())\n    }\n}\n\nimpl From<u8> for IndexOperand {\n    fn from(value: u8) -> Self {\n        Self::new(value.into())\n    }\n}\n\nimpl From<u16> for IndexOperand {\n    fn from(value: u16) -> Self {\n        Self::new(value.into())\n    }\n}\n\nimpl From<u32> for IndexOperand {\n    fn from(value: u32) -> Self {\n        Self::new(value)\n    }\n}\n\nimpl std::fmt::Display for IndexOperand {\n    #[inline]\n    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {\n        write!(f, \"{}\", self.0)\n    }\n}\n\nimpl Opcode {\n    fn encode(self) -> u8 {\n        self as u8\n    }\n\n    pub(crate) fn decode(instruction: u8) -> Self {\n        Self::from(instruction)\n    }\n}\n\nfn encode_instruction<A: Argument>(opcode: Opcode, args: A, bytes: &mut Vec<u8>) {\n    bytes.push(opcode.encode());\n    args.encode(bytes);\n}\n\nmacro_rules! generate_opcodes {\n    (\n        $(\n            $(#[$comment:ident $($args:tt)*])*\n            $Variant:ident $({\n                $(\n                    $(#[$fieldinner:ident $($fieldargs:tt)*])*\n                    $FieldName:ident : $FieldType:ty\n                ),*\n                $(,)?\n            })? $(=> $mapping:ident)?\n        ),*\n        $(,)?\n    ) => {\n        /// The opcodes of the vm.\n        #[derive(Debug, Clone, Copy, PartialEq, Eq)]\n        #[repr(u8)]\n        pub(crate) enum Opcode {\n            $(\n                $(#[$comment $($args)*])*\n                $Variant\n            ),*\n        }\n\n        impl From<u8> for Opcode {\n            #[inline]\n            #[allow(non_upper_case_globals)]\n            fn from(value: u8) -> Self {\n                $(\n                    const $Variant: u8 = Opcode::$Variant as u8;\n                )*\n                match value {\n                    $($Variant => Self::$Variant),*\n                }\n            }\n        }\n\n        impl Opcode {\n            pub(crate) fn as_str(&self) -> &'static str {\n                match self {\n                    $(Self::$Variant => $Variant::NAME),*\n                }\n            }\n        }\n\n        impl BytecodeEmitter {\n            $(\n                paste::paste! {\n                    #[allow(unused)]\n                    pub(crate) fn [<emit_ $Variant:snake>](&mut self $( $(, $FieldName: $FieldType)* )? ) {\n                        encode_instruction(\n                            Opcode::$Variant,\n                            ($($($FieldName),*)?),\n                            &mut self.bytes,\n                        );\n                    }\n                }\n            )*\n        }\n\n        type OpcodeHandler = fn(&mut Context, usize) -> ControlFlow<CompletionRecord>;\n\n        pub(crate) const OPCODE_HANDLERS: [OpcodeHandler; 256] = {\n            [\n                $(\n                    paste::paste! { [<handle_ $Variant:snake>] },\n                )*\n            ]\n        };\n\n        type OpcodeHandlerBudget = fn(&mut Context, usize, &mut u32) -> ControlFlow<CompletionRecord>;\n\n        pub(crate) const OPCODE_HANDLERS_BUDGET: [OpcodeHandlerBudget; 256] = {\n            [\n                $(\n                    paste::paste! { [<handle_ $Variant:snake _budget>] },\n                )*\n            ]\n        };\n\n        $(\n            paste::paste! {\n                #[inline(always)]\n                #[allow(unused_parens)]\n                fn [<handle_ $Variant:snake>](context: &mut Context, pc: usize) -> ControlFlow<CompletionRecord> {\n                    let bytes = &context.vm.frame().code_block.bytecode.bytes;\n                    let (args, next_pc) = <($($($FieldType),*)?)>::decode(bytes, pc + 1);\n                    context.vm.frame_mut().pc = next_pc as u32;\n                    let result = $Variant::operation(args, context);\n                    IntoCompletionRecord::into_completion_record(result, context)\n                }\n            }\n        )*\n\n        $(\n            paste::paste! {\n                #[inline(always)]\n                #[allow(unused_parens)]\n                fn [<handle_ $Variant:snake _budget>](context: &mut Context, pc: usize, budget: &mut u32) -> ControlFlow<CompletionRecord> {\n                    *budget = budget.saturating_sub(u32::from($Variant::COST));\n                    let bytes = &context.vm.frame().code_block.bytecode.bytes;\n                    let (args, next_pc) = <($($($FieldType),*)?)>::decode(bytes, pc + 1);\n                    context.vm.frame_mut().pc = next_pc as u32;\n                    let result = $Variant::operation(args, context);\n                    IntoCompletionRecord::into_completion_record(result, context)\n                }\n            }\n        )*\n\n        $(\n            $(\n                struct $Variant {}\n                impl $Variant {\n                    #[allow(unused_parens)]\n                    #[allow(unused_variables)]\n                    #[inline(always)]\n                    fn operation(args: (), context: &mut Context) {\n                        $mapping::operation(args, context)\n                    }\n                }\n\n                impl Operation for $Variant {\n                    const NAME: &'static str = \"Reserved\";\n                    const INSTRUCTION: &'static str = \"INST - Reserved\";\n                    const COST: u8 = 0;\n                }\n            )?\n        )*\n\n        pub(crate) enum Instruction {\n            $(\n                $Variant $({\n                    $(\n                        $(#[$fieldinner $($fieldargs)*])*\n                        $FieldName : $FieldType\n                    ),*\n                })?\n            ),*\n        }\n\n        impl Bytecode {\n            #[allow(unused_parens)]\n            pub(crate) fn next_instruction(&self, pc: usize) -> (Instruction, usize) {\n                let bytes = &self.bytes;\n                let opcode = Opcode::decode(bytes[pc]);\n\n                match opcode {\n                    $(\n                        Opcode::$Variant => {\n                            let (($($($FieldName),*)?), read_size) = <($($($FieldType),*)?)>::decode(bytes, pc + 1);\n                            (Instruction::$Variant $({\n                                $(\n                                    $FieldName: $FieldName\n                                ),*\n                            })?, read_size)\n                        }\n                    ),*\n                }\n            }\n        }\n    }\n}\n\n/// Iterator over the instructions in the compact bytecode.\n// #[derive(Debug, Clone)]\npub(crate) struct InstructionIterator<'bytecode> {\n    bytes: &'bytecode Bytecode,\n    pc: usize,\n}\n\n// TODO: see if this can be exposed on all features.\n// #[allow(unused)]\nimpl<'bytecode> InstructionIterator<'bytecode> {\n    /// Create a new [`InstructionIterator`] from bytecode array.\n    #[inline]\n    #[must_use]\n    pub(crate) const fn new(bytes: &'bytecode Bytecode) -> Self {\n        Self { bytes, pc: 0 }\n    }\n\n    /// Get the current program counter.\n    #[inline]\n    #[must_use]\n    pub(crate) const fn pc(&self) -> usize {\n        self.pc\n    }\n}\n\nimpl Iterator for InstructionIterator<'_> {\n    type Item = (usize, Opcode, Instruction);\n\n    #[inline]\n    fn next(&mut self) -> Option<Self::Item> {\n        let start_pc = self.pc;\n        if self.pc >= self.bytes.bytes.len() {\n            return None;\n        }\n\n        let bytes = &self.bytes.bytes;\n        let opcode = Opcode::decode(bytes[self.pc]);\n        // Get instruction and determine how much to advance pc\n        let (instruction, read_size) = self.bytes.next_instruction(self.pc);\n        self.pc = read_size;\n        Some((start_pc, opcode, instruction))\n    }\n}\n\ngenerate_opcodes! {\n    /// Pop the top value from the stack.\n    ///\n    /// - Stack: value **=>**\n    Pop,\n\n    /// Store integer `0` in dst.\n    ///\n    /// - Registers:\n    ///   - Output: dst\n    StoreZero { dst: RegisterOperand },\n\n    /// Store integer `1` in dst.\n    ///\n    /// - Registers:\n    ///   - Output: dst\n    StoreOne { dst: RegisterOperand },\n\n    /// Store `i8` value in dst.\n    ///\n    /// - Operands:\n    ///   - value: `i8`\n    /// - Registers:\n    ///   - Output: dst\n    StoreInt8 { dst: RegisterOperand, value: i8 },\n\n    /// Store `i16` value in dst.\n    ///\n    /// - Operands:\n    ///   - value: `i16`\n    /// - Registers:\n    ///   - Output: dst\n    StoreInt16 { dst: RegisterOperand, value: i16 },\n\n    /// Store `i32` value in dst.\n    ///\n    /// - Operands:\n    ///   - value: `i32`\n    /// - Registers:\n    ///   - Output: dst\n    StoreInt32 { dst: RegisterOperand, value: i32 },\n\n    /// Store `f32` value in dst.\n    ///\n    /// - Operands:\n    ///   - value: `f32`\n    /// - Registers:\n    ///   - Output: dst\n    StoreFloat { dst: RegisterOperand, value: f32 },\n\n    /// Store `f64` value in dst.\n    ///\n    /// - Operands:\n    ///   - value: `f64`\n    /// - Registers:\n    ///   - Output: dst\n    StoreDouble { dst: RegisterOperand, value: f64 },\n\n    /// Store `NaN` in dst.\n    ///\n    /// - Registers:\n    ///   - Output: dst\n    StoreNan { dst: RegisterOperand },\n\n    /// Store `Infinity` in dst.\n    ///\n    /// - Registers:\n    ///   - Output: dst\n    StorePositiveInfinity { dst: RegisterOperand },\n\n    /// Store `-Infinity` in dst.\n    ///\n    /// - Registers:\n    ///   - Output: dst\n    StoreNegativeInfinity { dst: RegisterOperand },\n\n    /// Store `null` in dst.\n    ///\n    /// - Registers:\n    ///   - Output: dst\n    StoreNull { dst: RegisterOperand },\n\n    /// Store `true` in dst.\n    ///\n    /// - Registers:\n    ///   - Output: dst\n    StoreTrue { dst: RegisterOperand },\n\n    /// Store `false` in dst.\n    ///\n    /// - Registers:\n    ///   - Output: dst\n    StoreFalse { dst: RegisterOperand },\n\n    /// Store `undefined` in dst.\n    ///\n    /// - Registers:\n    ///   - Output: dst\n    StoreUndefined { dst: RegisterOperand },\n\n    /// Store literal value in dst.\n    ///\n    /// Like strings and bigints. The index operand is used to index into the `literals`\n    /// array to get the value.\n    ///\n    /// - Operands:\n    ///   - index: `IndexOperand`\n    /// - Registers:\n    ///   - Output: dst\n    StoreLiteral { dst: RegisterOperand, index: IndexOperand },\n\n    /// Store regexp value in dst.\n    ///\n    /// - Operands:\n    ///   - pattern_index: `IndexOperand`\n    ///   - flags: `IndexOperand`\n    /// - Registers:\n    ///   - Output: dst\n    StoreRegexp { dst: RegisterOperand, pattern_index: IndexOperand, flags_index: IndexOperand },\n\n    /// Store empty object `{}` in dst.\n    ///\n    /// - Registers:\n    ///   - Output: dst\n    StoreEmptyObject { dst: RegisterOperand },\n\n    /// Get the prototype of a superclass and store it in dst.\n    ///\n    /// Additionally this sets the `[[prototype]]` of the class and the `DERIVED` flag.\n    ///\n    /// - Registers:\n    ///   - Input: class, superclass\n    ///   - Output: dst\n    StoreClassPrototype {\n        dst: RegisterOperand,\n        class: RegisterOperand,\n        superclass: RegisterOperand\n    },\n\n    /// Set the prototype of a class object.\n    ///\n    /// - Registers:\n    ///   - Input: class, prototype\n    ///   - Output: dst\n    SetClassPrototype {\n        dst: RegisterOperand,\n        prototype: RegisterOperand,\n        class: RegisterOperand\n    },\n\n    /// Set home object internal slot of an object literal method.\n    ///\n    /// - Registers:\n    ///   - Input: function, home\n    SetHomeObject {\n        function: RegisterOperand,\n        home: RegisterOperand\n    },\n\n    /// Get home object internal slot of an object literal method.\n    ///\n    /// - Registers (inout):\n    ///   - function:\n    ///     - in: `JsObject<OrdinaryFunction>`.\n    ///     - out: `JsObject` or `null` if the home object is not set.\n    GetHomeObject {\n        function: RegisterOperand,\n    },\n\n    /// Set the prototype of an object if the value is an object or null.\n    ///\n    /// - Registers (in):\n    ///   - object: `JsObject`.\n    ///   - prototype: `JsObject` or `null`\n    SetPrototype {\n        object: RegisterOperand,\n        prototype: RegisterOperand\n    },\n\n    /// Get the prototype of an object.\n    ///\n    /// - Registers (inout):\n    ///   - object:\n    ///     - in: `JsObject`.\n    ///     - out: `JsObject` or `null`.\n    GetPrototype {\n        object: RegisterOperand,\n    },\n\n    /// Store an empty array in dst.\n    ///\n    /// - Registers:\n    ///   - Output: dst\n    StoreNewArray { dst: RegisterOperand },\n\n    /// Push a value to an array.\n    ///\n    /// - Registers:\n    ///   - Input: array, value\n    PushValueToArray { value: RegisterOperand, array: RegisterOperand },\n\n    /// Push an empty element/hole to an array.\n    ///\n    /// - Registers:\n    ///   - Input: array\n    PushElisionToArray { array: RegisterOperand },\n\n    /// Push all iterator values to an array.\n    ///\n    /// - Registers:\n    ///   - Input: array\n    PushIteratorToArray { array: RegisterOperand },\n\n    /// Binary `+` operator.\n    ///\n    /// - Registers\n    ///   - Input: lhs, rhs\n    ///   - Output: dst\n    Add { dst: RegisterOperand, lhs: RegisterOperand, rhs: RegisterOperand },\n\n    /// Binary `-` operator.\n    ///\n    /// - Registers\n    ///   - Input: lhs, rhs\n    ///   - Output: dst\n    Sub { dst: RegisterOperand, lhs: RegisterOperand, rhs: RegisterOperand },\n\n    /// Binary `/` operator.\n    ///\n    /// - Registers\n    ///   - Input: lhs, rhs\n    ///   - Output: dst\n    Div { dst: RegisterOperand, lhs: RegisterOperand, rhs: RegisterOperand },\n\n    /// Binary `*` operator.\n    ///\n    /// - Registers\n    ///   - Input: lhs, rhs\n    ///   - Output: dst\n    Mul { dst: RegisterOperand, lhs: RegisterOperand, rhs: RegisterOperand },\n\n    /// Binary `%` operator.\n    ///\n    /// - Registers\n    ///   - Input: lhs, rhs\n    ///   - Output: dst\n    Mod { dst: RegisterOperand, lhs: RegisterOperand, rhs: RegisterOperand },\n\n    /// Binary `**` operator.\n    ///\n    /// - Registers\n    ///   - Input: lhs, rhs\n    ///   - Output: dst\n    Pow { dst: RegisterOperand, lhs: RegisterOperand, rhs: RegisterOperand },\n\n    /// Binary `>>` operator.\n    ///\n    /// - Registers\n    ///   - Input: lhs, rhs\n    ///   - Output: dst\n    ShiftRight { dst: RegisterOperand, lhs: RegisterOperand, rhs: RegisterOperand },\n\n    /// Binary `<<` operator.\n    ///\n    /// - Registers:\n    ///   - Input: lhs, rhs\n    ///   - Output: dst\n    ShiftLeft { dst: RegisterOperand, lhs: RegisterOperand, rhs: RegisterOperand },\n\n    /// Binary `>>>` operator.\n    ///\n    /// - Registers:\n    ///   - Input: lhs, rhs\n    ///   - Output: dst\n    UnsignedShiftRight { dst: RegisterOperand, lhs: RegisterOperand, rhs: RegisterOperand },\n\n    /// Binary bitwise `|` operator.\n    ///\n    /// - Registers:\n    ///   - Input: lhs, rhs\n    ///   - Output: dst\n    BitOr { dst: RegisterOperand, lhs: RegisterOperand, rhs: RegisterOperand },\n\n    /// Binary bitwise `&` operator.\n    ///\n    /// - Registers:\n    ///   - Input: lhs, rhs\n    ///   - Output: dst\n    BitAnd { dst: RegisterOperand, lhs: RegisterOperand, rhs: RegisterOperand },\n\n    /// Binary bitwise `^` operator.\n    ///\n    /// - Registers:\n    ///   - Input: lhs, rhs\n    ///   - Output: dst\n    BitXor { dst: RegisterOperand, lhs: RegisterOperand, rhs: RegisterOperand },\n\n    /// Unary bitwise `~` operator.\n    ///\n    /// - Registers:\n    ///   - Input: lhs, rhs\n    ///   - Output: dst\n    BitNot { value: RegisterOperand },\n\n    /// Binary `in` operator.\n    ///\n    /// - Registers:\n    ///   - Input: lhs, rhs\n    ///   - Output: dst\n    In { dst: RegisterOperand, lhs: RegisterOperand, rhs: RegisterOperand },\n\n    /// Binary `in` operator for private names.\n    ///\n    /// - Operands: index: `u32`\n    /// - Registers:\n    ///   - Input: rhs\n    ///   - Output: dst\n    InPrivate { dst: RegisterOperand, index: IndexOperand, rhs: RegisterOperand },\n\n    /// Binary `==` operator.\n    ///\n    /// - Registers:\n    ///   - Input: lhs, rhs\n    ///   - Output: dst\n    Eq { dst: RegisterOperand, lhs: RegisterOperand, rhs: RegisterOperand },\n\n    /// Binary `===` operator.\n    ///\n    /// - Registers:\n    ///   - Input: lhs, rhs\n    ///   - Output: dst\n    StrictEq { dst: RegisterOperand, lhs: RegisterOperand, rhs: RegisterOperand },\n\n    /// Binary `!=` operator.\n    ///\n    /// - Registers:\n    ///   - Input: lhs, rhs\n    ///   - Output: dst\n    NotEq { dst: RegisterOperand, lhs: RegisterOperand, rhs: RegisterOperand },\n\n    /// Binary `!==` operator.\n    ///\n    /// - Registers:\n    ///   - Input: lhs, rhs\n    ///   - Output: dst\n    StrictNotEq { dst: RegisterOperand, lhs: RegisterOperand, rhs: RegisterOperand },\n\n    /// Binary `>` operator.\n    ///\n    /// - Registers:\n    ///   - Input: lhs, rhs\n    ///   - Output: dst\n    GreaterThan { dst: RegisterOperand, lhs: RegisterOperand, rhs: RegisterOperand },\n\n    /// Binary `>=` operator.\n    ///\n    /// - Registers:\n    ///   - Input: lhs, rhs\n    ///   - Output: dst\n    GreaterThanOrEq { dst: RegisterOperand, lhs: RegisterOperand, rhs: RegisterOperand },\n\n    /// Binary `<` operator.\n    ///\n    /// - Registers:\n    ///   - Input: lhs, rhs\n    ///   - Output: dst\n    LessThan { dst: RegisterOperand, lhs: RegisterOperand, rhs: RegisterOperand },\n\n    /// Binary `<=` operator.\n    ///\n    /// - Registers:\n    ///   - Input: lhs, rhs\n    ///   - Output: dst\n    LessThanOrEq { dst: RegisterOperand, lhs: RegisterOperand, rhs: RegisterOperand },\n\n    /// Binary `instanceof` operator.\n    ///\n    /// - Registers:\n    ///   - Input: lhs, rhs\n    ///   - Output: dst\n    InstanceOf { dst: RegisterOperand, lhs: RegisterOperand, rhs: RegisterOperand },\n\n    /// Binary logical `&&` operator.\n    ///\n    /// This is a short-circuit operator, if the `lhs` value is `false`, then it jumps to `exit` address.\n    ///\n    /// - Operands:\n    ///   - address: `u32`\n    /// - Registers:\n    ///   - Input: value\n    ///   - Output: value\n    LogicalAnd { address: Address, value: RegisterOperand },\n\n    /// Binary logical `||` operator.\n    ///\n    /// This is a short-circuit operator, if the `lhs` value is `true`, then it jumps to `exit` address.\n    ///\n    /// - Operands:\n    ///   - address: `Address`\n    /// - Registers:\n    ///   - Input: value\n    ///   - Output: value\n    LogicalOr { address: Address, value: RegisterOperand },\n\n    /// Binary `??` operator.\n    ///\n    /// This is a short-circuit operator, if the `lhs` value is **not** `null` or `undefined`,\n    /// then it jumps to `exit` address.\n    ///\n    /// - Operands:\n    ///   - address: `Address`\n    /// - Registers:\n    ///   - Input: value\n    ///   - Output: value\n    Coalesce { address: Address, value: RegisterOperand },\n\n    /// Unary `typeof` operator.\n    ///\n    /// - Registers:\n    ///   - Input: value\n    ///   - Output: value\n    TypeOf { value: RegisterOperand },\n\n    /// Unary logical `!` operator.\n    ///\n    /// - Registers:\n    ///   - Input: value\n    ///   - Output: value\n    LogicalNot { value: RegisterOperand },\n\n    /// Unary `+` operator.\n    ///\n    /// - Registers:\n    ///   - Input: value\n    ///   - Output: value\n    Pos { value: RegisterOperand },\n\n    /// Unary `-` operator.\n    ///\n    /// - Registers:\n    ///   - Input: value\n    ///   - Output: value\n    Neg { value: RegisterOperand },\n\n    /// Unary `++` operator.\n    ///\n    /// - Registers:\n    ///   - Input: src\n    ///   - Output: dst\n    Inc { dst: RegisterOperand, src: RegisterOperand },\n\n    /// Unary `--` operator.\n    ///\n    /// - Registers:\n    ///   - Input: src\n    ///   - Output: dst\n    Dec { dst: RegisterOperand, src: RegisterOperand },\n\n    /// Declare `var` type variable.\n    ///\n    /// - Operands:\n    ///   - binding_index: `IndexOperand`\n    DefVar { binding_index: IndexOperand },\n\n    /// Declare and initialize `var` type variable.\n    ///\n    /// - Operands:\n    ///   - binding_index: `IndexOperand`\n    /// - Registers:\n    ///   - Input: src\n    DefInitVar { src: RegisterOperand, binding_index: IndexOperand },\n\n    /// Initialize a lexical binding.\n    ///\n    /// - Operands:\n    ///   - binding_index: `IndexOperand`\n    /// - Registers:\n    ///   - Input: src\n    PutLexicalValue { src: RegisterOperand, binding_index: IndexOperand },\n\n    /// Throws an error because the binding access is illegal.\n    ///\n    /// - Operands:\n    ///   -index: `IndexOperand`\n    ThrowMutateImmutable { index: IndexOperand },\n\n    /// Get i-th argument of the current frame.\n    ///\n    /// Returns `undefined` if `arguments.len()` < `index`.\n    ///\n    /// - Operands:\n    ///   - index: `IndexOperand`\n    /// - Registers:\n    ///   - Output: dst\n    GetArgument { index: IndexOperand, dst: RegisterOperand },\n\n    /// Find a binding on the environment chain and store its value in dst.\n    ///\n    /// - Operands:\n    ///   - binding_index: `IndexOperand`\n    /// - Registers:\n    ///   - Output: dst\n    GetName { dst: RegisterOperand, binding_index: IndexOperand },\n\n    /// Find a binding in the global object and store its value in dst.\n    ///\n    /// - Operands:\n    ///   - binding_index: `IndexOperand`\n    ///   - ic_index: `IndexOperand`\n    /// - Registers:\n    ///   - Output: dst\n    GetNameGlobal { dst: RegisterOperand, binding_index: IndexOperand, ic_index: IndexOperand },\n\n    /// Find a binding on the environment and set the `current_binding` of the current frame.\n    ///\n    /// - Operands:\n    ///   - binding_index: `IndexOperand`\n    GetLocator { binding_index: IndexOperand },\n\n    /// Find a binding on the environment chain and store its value in dst, and push its\n    /// `BindingLocator` to the `bindings_stack`.\n    ///\n    /// - Operands:\n    ///   - binding_index: `IndexOperand`\n    /// - Registers:\n    ///   - Output: dst\n    GetNameAndLocator { dst: RegisterOperand, binding_index: IndexOperand },\n\n    /// Find a binding on the environment chain and store its value in dst. If the binding does not exist, store undefined.\n    ///\n    /// - Operands:\n    ///   - binding_index: `IndexOperand`\n    /// - Registers:\n    ///   - Output: dst\n    GetNameOrUndefined { dst: RegisterOperand, binding_index: IndexOperand },\n\n    /// Find a binding on the environment chain and assign its value.\n    ///\n    /// - Operands:\n    ///   - binding_index: `IndexOperand`\n    /// - Registers:\n    ///   - Input: src\n    SetName { src: RegisterOperand, binding_index: IndexOperand },\n\n    /// Assigns a value to the binding pointed by the top of the `bindings_stack`.\n    ///\n    /// - Registers:\n    ///   - Input: src\n    SetNameByLocator { src: RegisterOperand },\n\n    /// Deletes a property of the global object.\n    ///\n    /// - Operands:\n    ///   - binding_index: `IndexOperand`\n    /// - Registers:\n    ///   - Output: dst\n    DeleteName { dst: RegisterOperand, binding_index: IndexOperand },\n\n    /// Gets a method from an object, or `undefined` if the method does not exist.\n    ///\n    /// Operands:\n    ///  - name_index: constant `JsString`.\n    ///\n    /// Registers (inout)\n    ///  - object: `JsObject` as input, `JsObject` or `undefined` as output.\n    GetMethod { object: RegisterOperand, name_index: IndexOperand },\n\n    /// Get the length property by name from an object.\n    ///\n    /// Like `object.name`\n    ///\n    /// - Operands:\n    ///   - ic_index: `IndexOperand`\n    /// - Registers:\n    ///   - Input: value\n    ///   - Output: dst\n    GetLengthProperty {\n        dst: RegisterOperand,\n        value: RegisterOperand,\n        ic_index: IndexOperand\n    },\n\n    /// Get a property by name from an object.\n    ///\n    /// Like `object.name`\n    ///\n    /// - Operands:\n    ///   - ic_index: `IndexOperand`\n    /// - Registers:\n    ///   - Input: value\n    ///   - Output: dst\n    GetPropertyByName {\n        dst: RegisterOperand,\n        value: RegisterOperand,\n        ic_index: IndexOperand\n    },\n\n    /// Get a property by name from an object.\n    ///\n    /// Like `object.name`\n    ///\n    /// - Operands:\n    ///   - ic_index: `IndexOperand`\n    /// - Registers:\n    ///   - Input: receiver, value\n    ///   - Output: dst\n    GetPropertyByNameWithThis {\n        dst: RegisterOperand,\n        receiver: RegisterOperand,\n        value: RegisterOperand,\n        ic_index: IndexOperand\n    },\n\n    /// Get a property by value from an object.\n    ///\n    /// Like `object[key]`\n    ///\n    /// - Registers:\n    ///   - Input: object, receiver, key\n    ///   - Output: dst\n    GetPropertyByValue {\n        dst: RegisterOperand,\n        key: RegisterOperand,\n        receiver: RegisterOperand,\n        object: RegisterOperand\n    },\n\n    /// Get a property by value from an object.\n    ///\n    /// Like `object[key]`\n    ///\n    /// - Registers:\n    ///   - Input: object, receiver\n    ///   - Output: dst, key\n    GetPropertyByValuePush {\n        dst: RegisterOperand,\n        key: RegisterOperand,\n        receiver: RegisterOperand,\n        object: RegisterOperand\n    },\n\n    /// Sets a property by name of an object.\n    ///\n    /// Like `object.name = value`\n    ///\n    /// - Operands:\n    ///   - ic_index: `IndexOperand`\n    /// - Registers:\n    ///   - Input: object, value\n    SetPropertyByName {\n        value: RegisterOperand,\n        object: RegisterOperand,\n        ic_index: IndexOperand\n    },\n\n    /// Sets a property by name of an object.\n    ///\n    /// Like `object.name = value`\n    ///\n    /// - Operands:\n    ///   - ic_index: `IndexOperand`\n    /// - Registers:\n    ///   - Input: object,receiver, value\n    SetPropertyByNameWithThis {\n        value: RegisterOperand,\n        receiver: RegisterOperand,\n        object: RegisterOperand,\n        ic_index: IndexOperand\n    },\n\n    /// Sets the name of a function object.\n    ///\n    /// This operation is corresponds to the `SetFunctionName` abstract operation in the [spec].\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-setfunctionname\n    ///\n    /// - Operands:\n    ///   - prefix\n    ///     - 0: no prefix\n    ///     - 1: \"get \"\n    ///     - 2: \"set \"\n    /// - Registers:\n    ///   - Input: function, name\n    SetFunctionName { function: RegisterOperand, name: RegisterOperand, prefix: IndexOperand },\n\n    /// Defines a own property of an object by name.\n    ///\n    /// - Operands:\n    ///   - name_index: `IndexOperand`\n    /// - Registers:\n    ///   - Input: object, value\n    DefineOwnPropertyByName { object: RegisterOperand, value: RegisterOperand, name_index: IndexOperand },\n\n    /// Defines a static class method by name.\n    ///\n    /// - Operands:\n    ///   - name_index: `IndexOperand`\n    /// - Registers:\n    ///   - Input: object, value\n    DefineClassStaticMethodByName {\n        value: RegisterOperand,\n        object: RegisterOperand,\n        name_index: IndexOperand\n    },\n\n    /// Defines a class method by name.\n    ///\n    /// - Operands:\n    ///   - name_index: `IndexOperand`\n    /// - Registers:\n    ///   - Input: object, value\n    DefineClassMethodByName {\n        value: RegisterOperand,\n        object: RegisterOperand,\n        name_index: IndexOperand\n    },\n\n    /// Sets a property by value of an object.\n    ///\n    /// Like `object[key] = value`\n    ///\n    /// - Registers:\n    ///   - Input: value, key, receiver, object\n    SetPropertyByValue {\n        value: RegisterOperand,\n        key: RegisterOperand,\n        receiver: RegisterOperand,\n        object: RegisterOperand\n    },\n\n    /// Defines a own property of an object by value.\n    ///\n    /// - Registers:\n    ///   - Input: object, key, value\n    DefineOwnPropertyByValue {\n        value: RegisterOperand,\n        key: RegisterOperand,\n        object: RegisterOperand\n    },\n\n    /// Defines a static class method by value.\n    ///\n    /// - Registers:\n    ///   - Input: object, key, value\n    DefineClassStaticMethodByValue {\n        value: RegisterOperand,\n        key: RegisterOperand,\n        object: RegisterOperand\n    },\n\n    /// Defines a class method by value.\n    ///\n    /// - Registers:\n    ///   - Input: object, key, value\n    DefineClassMethodByValue {\n        value: RegisterOperand,\n        key: RegisterOperand,\n        object: RegisterOperand\n    },\n\n    /// Sets a getter property by name of an object.\n    ///\n    /// Like `get name() value`\n    ///\n    /// - Operands:\n    ///   - name_index: `IndexOperand`\n    /// - Registers:\n    ///   - Input: object, value\n    SetPropertyGetterByName { object: RegisterOperand, value: RegisterOperand, name_index: IndexOperand },\n\n    /// Defines a static getter class method by name.\n    ///\n    /// Like `static get name() value`\n    ///\n    /// - Operands:\n    ///   - name_index: `IndexOperand`\n    /// - Registers:\n    ///   - Input: object, value\n    DefineClassStaticGetterByName {\n        value: RegisterOperand,\n        object: RegisterOperand,\n        name_index: IndexOperand\n    },\n\n    /// Defines a getter class method by name.\n    ///\n    /// Like `get name() value`\n    ///\n    /// - Operands:\n    ///   - name_index: `IndexOperand`\n    /// - Registers:\n    ///   - Input: object, value\n    DefineClassGetterByName {\n        value: RegisterOperand,\n        object: RegisterOperand,\n        name_index: IndexOperand\n    },\n\n    /// Sets a getter property by value of an object.\n    ///\n    /// Like `get [key]() value`\n    ///\n    /// - Registers:\n    ///   - Input: object, key, value\n    SetPropertyGetterByValue {\n        value: RegisterOperand,\n        key: RegisterOperand,\n        object: RegisterOperand\n    },\n\n    /// Defines a static getter class method by value.\n    ///\n    /// Like `static get [key]() value`\n    ///\n    /// - Registers:\n    ///   - Input: object, key, value\n    DefineClassStaticGetterByValue {\n        value: RegisterOperand,\n        key: RegisterOperand,\n        object: RegisterOperand\n    },\n\n    /// Defines a getter class method by value.\n    ///\n    /// Like `get [key]() value`\n    ///\n    /// - Registers:\n    ///   - Input: object, key, value\n    DefineClassGetterByValue {\n        value: RegisterOperand,\n        key: RegisterOperand,\n        object: RegisterOperand\n    },\n\n    /// Sets a setter property by name of an object.\n    ///\n    /// Like `set name() value`\n    ///\n    /// - Operands:\n    ///   - name_index: `IndexOperand`\n    /// - Registers:\n    ///   - Input: object, value\n    SetPropertySetterByName { object: RegisterOperand, value: RegisterOperand, name_index: IndexOperand },\n\n    /// Defines a static setter class method by name.\n    ///\n    /// Like `static set name() value`\n    ///\n    /// - Operands:\n    ///   - name_index: `IndexOperand`\n    /// - Registers:\n    ///   - Input: object, value\n    DefineClassStaticSetterByName {\n        value: RegisterOperand,\n        object: RegisterOperand,\n        name_index: IndexOperand\n    },\n\n    /// Defines a setter class method by name.\n    ///\n    /// Like `set name() value`\n    ///\n    /// - Operands:\n    ///   - name_index: `IndexOperand`\n    /// - Registers:\n    ///   - Input: object, value\n    DefineClassSetterByName {\n        value: RegisterOperand,\n        object: RegisterOperand,\n        name_index: IndexOperand\n    },\n\n    /// Sets a setter property by value of an object.\n    ///\n    /// Like `set [key]() value`\n    ///\n    /// - Registers:\n    ///   - Input: object, key, value\n    SetPropertySetterByValue {\n        value: RegisterOperand,\n        key: RegisterOperand,\n        object: RegisterOperand\n    },\n\n    /// Defines a static setter class method by value.\n    ///\n    /// Like `static set [key]() value`\n    ///\n    /// - Registers:\n    ///   - Input: object, key, value\n    DefineClassStaticSetterByValue {\n        value: RegisterOperand,\n        key: RegisterOperand,\n        object: RegisterOperand\n    },\n\n    /// Defines a setter class method by value.\n    ///\n    /// Like `set [key]() value`\n    ///\n    /// - Registers:\n    ///   - Input: object, key, value\n    DefineClassSetterByValue {\n        value: RegisterOperand,\n        key: RegisterOperand,\n        object: RegisterOperand\n    },\n\n    /// Set the value of a private property of an object by it's name.\n    ///\n    /// Like `obj.#name = value`\n    ///\n    /// - Operands:\n    ///   - name_index: `IndexOperand`\n    /// - Registers:\n    ///   - Input: object, value\n    SetPrivateField { value: RegisterOperand, object: RegisterOperand, name_index: IndexOperand },\n\n    /// Define a private property of a class constructor by it's name.\n    ///\n    /// Like `#name = value`\n    ///\n    /// - Operands:\n    ///   - name_index: `IndexOperand`\n    /// - Registers:\n    ///   - Input: object, value\n    DefinePrivateField { object: RegisterOperand, value: RegisterOperand, name_index: IndexOperand },\n\n    /// Set a private method of a class constructor by it's name.\n    ///\n    /// Like `#name() {}`\n    ///\n    /// - Operands:\n    ///   - name_index: `IndexOperand`\n    /// - Registers:\n    ///   - Input: object, value\n    SetPrivateMethod { object: RegisterOperand, value: RegisterOperand, name_index: IndexOperand },\n\n    /// Set a private setter property of a class constructor by it's name.\n    ///\n    /// Like `set #name() {}`\n    ///\n    /// - Operands:\n    ///   - name_index: `IndexOperand`\n    /// - Registers:\n    ///   - Input: object, value\n    SetPrivateSetter { object: RegisterOperand, value: RegisterOperand, name_index: IndexOperand },\n\n    /// Set a private getter property of a class constructor by it's name.\n    ///\n    /// Like `get #name() {}`\n    ///\n    /// - Operands:\n    ///   - name_index: `IndexOperand`\n    /// - Registers:\n    ///   - Input: object, value\n    SetPrivateGetter { object: RegisterOperand, value: RegisterOperand, name_index: IndexOperand },\n\n    /// Get a private property by name from an object and store it in dst.\n    ///\n    /// Like `object.#name`\n    ///\n    /// - Operands:\n    ///   - name_index: `IndexOperand`\n    /// - Registers:\n    ///   - Input: object\n    ///   - Output: dst\n    GetPrivateField { dst: RegisterOperand, object: RegisterOperand, name_index: IndexOperand },\n\n    /// Push a field to a class.\n    ///\n    /// - Operands:\n    ///   - name_index: `IndexOperand`\n    ///   - is_anonymous_function: `bool`\n    /// - Registers:\n    ///   - Input: object, value\n    PushClassField { object: RegisterOperand, name: RegisterOperand, value: RegisterOperand, is_anonymous_function: IndexOperand },\n\n    /// Push a private field to the class.\n    ///\n    /// - Operands:\n    ///   - name_index: `IndexOperand`\n    /// - Registers:\n    ///   - Input: object, value\n    PushClassFieldPrivate { object: RegisterOperand, value: RegisterOperand, name_index: IndexOperand },\n\n    /// Push a private getter to the class.\n    ///\n    /// - Operands:\n    ///   - name_index: `IndexOperand`\n    /// - Registers:\n    ///   - Input: object, value\n    PushClassPrivateGetter { object: RegisterOperand, value: RegisterOperand, name_index: IndexOperand },\n\n    /// Push a private setter to the class.\n    ///\n    /// - Operands:\n    ///   - name_index: `IndexOperand`\n    /// - Registers:\n    ///   - Input: object, value\n    PushClassPrivateSetter { object: RegisterOperand, value: RegisterOperand, name_index: IndexOperand },\n\n    /// Push a private method to the class.\n    ///\n    /// - Operands:\n    ///   - name_index: `IndexOperand`\n    /// - Registers:\n    ///   - Input: object, proto, value\n    PushClassPrivateMethod { object: RegisterOperand, proto: RegisterOperand, value: RegisterOperand, name_index: IndexOperand },\n\n    /// Deletes a property by name of an object.\n    ///\n    /// Like `delete object.key`\n    ///\n    /// - Operands:\n    ///   - name_index: `IndexOperand`\n    /// - Registers:\n    ///   - Input: object\n    DeletePropertyByName { object: RegisterOperand, name_index: IndexOperand },\n\n    /// Deletes a property by value of an object.\n    ///\n    /// Like `delete object[key]`\n    ///\n    /// - Registers:\n    ///   - Input: object, key\n    DeletePropertyByValue { object: RegisterOperand, key: RegisterOperand },\n\n    /// Throws an error when trying to delete a property of `super`\n    DeleteSuperThrow,\n\n    /// Copy all properties of one object to another object.\n    ///\n    /// - Registers:\n    ///   - Input: object, source, excluded_keys\n    CopyDataProperties { object: RegisterOperand, source: RegisterOperand, excluded_keys: ThinVec<RegisterOperand> },\n\n    /// Call ToPropertyKey on the value on the stack.\n    ///\n    /// - Registers:\n    ///   - Input: src\n    ///   - Output: dst\n    ToPropertyKey { src: RegisterOperand, dst: RegisterOperand },\n\n    /// Unconditional jump to address.\n    ///\n    /// - Operands:\n    ///   - address: `u32`\n    Jump { address: Address },\n\n    /// Conditional jump to address.\n    ///\n    /// If the value popped is [`truthy`][truthy] then jump to `address`.\n    ///\n    /// - Operands:\n    ///   - address: `Address`\n    /// - Registers (in):\n    ///   - `value`: `JsValue`\n    ///\n    /// [truthy]: https://developer.mozilla.org/en-US/docs/Glossary/Truthy\n    JumpIfTrue { address: Address, value: RegisterOperand },\n\n    /// Conditional jump to address.\n    ///\n    /// If the value popped is [`falsy`][falsy] then jump to `address`.\n    ///\n    /// - Operands:\n    ///   - address: `Address`\n    /// - Registers (in):\n    ///   - `value`: `JsValue`\n    ///\n    /// [falsy]: https://developer.mozilla.org/en-US/docs/Glossary/Falsy\n    JumpIfFalse { address: Address, value: RegisterOperand },\n\n    /// Conditional jump to address.\n    ///\n    /// If the value popped is not undefined jump to `address`.\n    ///\n    /// - Operands:\n    ///   - address: `Address`.\n    /// - Registers (in):\n    ///   - value: `JsValue`\n    JumpIfNotUndefined { address: Address, value: RegisterOperand },\n\n    /// Conditional jump to address.\n    ///\n    /// If the value popped is undefined jump to `address`.\n    ///\n    /// - Operands:\n    ///   - address: `Address`.\n    /// - Registers (in):\n    ///   - value: `JsValue`.\n    JumpIfNullOrUndefined { address: Address, value: RegisterOperand },\n\n    /// Fused `<` comparison + conditional jump.\n    ///\n    /// Jumps to `address` if `!(lhs < rhs)`.\n    ///\n    /// - Operands:\n    ///   - address: `u32`\n    /// - Registers:\n    ///   - Input: lhs, rhs\n    JumpIfNotLessThan { address: Address, lhs: RegisterOperand, rhs: RegisterOperand },\n\n    /// Fused `<=` comparison + conditional jump.\n    ///\n    /// Jumps to `address` if `!(lhs <= rhs)`.\n    ///\n    /// - Operands:\n    ///   - address: `u32`\n    /// - Registers:\n    ///   - Input: lhs, rhs\n    JumpIfNotLessThanOrEqual { address: Address, lhs: RegisterOperand, rhs: RegisterOperand },\n\n    /// Fused `>` comparison + conditional jump.\n    ///\n    /// Jumps to `address` if `!(lhs > rhs)`.\n    ///\n    /// - Operands:\n    ///   - address: `u32`\n    /// - Registers:\n    ///   - Input: lhs, rhs\n    JumpIfNotGreaterThan { address: Address, lhs: RegisterOperand, rhs: RegisterOperand },\n\n    /// Fused `>=` comparison + conditional jump.\n    ///\n    /// Jumps to `address` if `!(lhs >= rhs)`.\n    ///\n    /// - Operands:\n    ///   - address: `u32`\n    /// - Registers:\n    ///   - Input: lhs, rhs\n    JumpIfNotGreaterThanOrEqual { address: Address, lhs: RegisterOperand, rhs: RegisterOperand },\n\n    /// Conditional jump to address.\n    ///\n    /// Jump to `address` if two values are not equal.\n    ///\n    /// - Operands:\n    ///   - address: `Address`\n    /// - Registers (in):\n    ///   - lhs: `JsValue`.\n    ///   - rhs: `JsValue`.\n    JumpIfNotEqual { address: Address, lhs: RegisterOperand, rhs: RegisterOperand },\n\n    /// Jump table that jumps depending on top value of the stack.\n    ///\n    /// This is used to handle special cases when we call `continue`, `break` or `return` in a try block,\n    /// that has finally block.\n    ///\n    /// Operands: index: Register, count: `u32`, address: `Address` * count\n    JumpTable { index: u32, addresses: ThinVec<Address> },\n\n    /// Throw exception.\n    ///\n    /// This sets pending exception and searches for an exception handler.\n    ///\n    /// - Registers:\n    ///   - Input: src\n    Throw { src: RegisterOperand },\n\n    /// Rethrow thrown exception.\n    ///\n    /// This is also used to handle generator `return()` call, we throw an empty exception, by setting pending exception to [`None`],\n    /// propagating it and calling finally code until there is no exception handler left, in that case we consume the empty exception and return\n    /// from the generator.\n    ReThrow,\n\n    /// Get the thrown pending exception (if it's set) and store it in dst.\n    ///\n    /// If there is no pending exception, which can happen if we are handling `return()` call on generator,\n    /// then we rethrow the empty exception. See [`Opcode::ReThrow`].\n    ///\n    /// - Registers:\n    ///   - Output: dst\n    Exception { dst: RegisterOperand },\n\n    /// Get the thrown pending exception if it's set and store `true` in `has_exception`, otherwise store `false`.\n    ///\n    /// - Registers:\n    ///   - Output: exception, has_exception\n    MaybeException { has_exception: RegisterOperand, exception: RegisterOperand },\n\n    /// Throw a new `TypeError` exception\n    ///\n    /// - Operands:\n    ///   - message: `IndexOperand`\n    ThrowNewTypeError { message: IndexOperand },\n\n    /// Throw a new `ReferenceError` exception\n    ///\n    /// - Operands:\n    ///   - message: `IndexOperand`\n    ThrowNewReferenceError { message: IndexOperand },\n\n    /// Gets the function object of the current function environment\n    ///\n    /// - Registers (out):\n    ///   - function_object: `JsObject`.\n    GetFunctionObject { function_object: RegisterOperand },\n\n    /// Pushes `this` value\n    ///\n    /// - Registers:\n    ///   - Output: dst\n    This { dst: RegisterOperand },\n\n    /// Pushes `this` value that is related to the object environment of the given binding\n    ///\n    /// - Operands:\n    ///   - index: `IndexOperand`\n    /// - Registers:\n    ///   - Output: dst\n    ThisForObjectEnvironmentName { dst: RegisterOperand, index: IndexOperand },\n\n    /// Execute the `super()` method.\n    ///\n    /// - Operands:\n    ///   - argument_count: `IndexOperand`\n    /// - Stack: super_constructor, argument_1, ... argument_n **=>**\n    SuperCall { argument_count: IndexOperand },\n\n    /// Execute the `super()` method where the arguments contain spreads.\n    ///\n    /// Operands:\n    ///\n    /// Stack: super_constructor, arguments_array **=>**\n    SuperCallSpread,\n\n    /// Execute the `super()` method when no constructor of the class is defined.\n    ///\n    /// Operands:\n    ///\n    /// Stack: argument_n, ... argument_1 **=>**\n    SuperCallDerived,\n\n    /// Binds `this` value and initializes the instance elements.\n    ///\n    /// Performs steps 7-12 of [`SuperCall: super Arguments`][spec]\n    ///\n    /// - Registers:\n    ///   - Input: value\n    ///   - Output: value\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-super-keyword-runtime-semantics-evaluation\n    BindThisValue { value: RegisterOperand },\n\n    /// Dynamically import a module.\n    ///\n    /// - Operands:\n    ///   - phase: `IndexOperand` (0 = evaluation, 1 = defer, 2 = source)\n    /// - Registers:\n    ///   - Input: specifier, options\n    ///   - Output: specifier\n    ImportCall { specifier: RegisterOperand, options: RegisterOperand, phase: IndexOperand },\n\n    /// Strict equal compare two register values,\n    /// if true jumps to address.\n    /// - Operands:\n    ///   - address: `Address`\n    /// - Registers:\n    ///   - Input: value, condition\n    Case { address: Address, value: RegisterOperand, condition: RegisterOperand },\n\n    /// Get function from the pre-compiled inner functions.\n    ///\n    /// - Operands:\n    ///   - index: `IndexOperand`\n    /// - Registers:\n    ///   - Output: dst\n    GetFunction { dst: RegisterOperand, index: IndexOperand },\n\n    /// Call a function named \"eval\".\n    ///\n    /// - Operands:\n    ///   - argument_count: `IndexOperand`\n    ///   - scope_index: `IndexOperand`\n    /// - Stack: this, func, argument_1, ... argument_n **=>** result\n    CallEval { argument_count: IndexOperand, scope_index: IndexOperand },\n\n    /// Call a function named \"eval\" where the arguments contain spreads.\n    ///\n    /// - Operands:\n    ///   - scope_index: `IndexOperand`\n    /// - Stack: Stack: this, func, arguments_array **=>** result\n    CallEvalSpread { scope_index: IndexOperand },\n\n    /// Call a function.\n    ///\n    /// - Operands:\n    ///   - argument_count: `IndexOperand`\n    /// - Stack: this, func, argument_1, ... argument_n **=>** result\n    Call { argument_count: IndexOperand },\n\n    /// Call a function where the arguments contain spreads.\n    ///\n    /// Operands:\n    ///\n    /// Stack: this, func, arguments_array **=>** result\n    CallSpread,\n\n    /// Call construct on a function.\n    ///\n    /// - Operands:\n    ///   - argument_count: `IndexOperand`\n    /// - Stack: this, func, argument_1, ... argument_n **=>** result\n    New { argument_count: IndexOperand },\n\n    /// Call construct on a function where the arguments contain spreads.\n    ///\n    /// Operands:\n    ///\n    /// Stack: arguments_array, func **=>** result\n    NewSpread,\n\n    /// Check return from a function.\n    CheckReturn,\n\n    /// Return from a function.\n    Return,\n\n    /// Close an async generator function.\n    AsyncGeneratorClose,\n\n    /// Creates the Generator object and yields.\n    ///\n    /// - Stack: **=>** resume_kind\n    Generator,\n\n    /// Creates the AsyncGenerator object and yields.\n    ///\n    /// - Stack: **=>** resume_kind\n    AsyncGenerator,\n\n    /// Set return value of a function.\n    ///\n    /// - Registers:\n    ///   - Input: src\n    SetAccumulator { src: RegisterOperand },\n\n    // Set return value of a function.\n    ///\n    /// - Registers:\n    ///   - Output: dst\n    SetRegisterFromAccumulator { dst: RegisterOperand },\n\n    /// Move value of operand `src` to register `dst`.\n    ///\n    /// - Registers:\n    ///   - Input: src\n    ///   - Output: dst\n    Move { dst: RegisterOperand, src: RegisterOperand },\n\n    /// Pop value from the stack and push to register `dst`\n    ///\n    /// - Registers:\n    ///   - Output: dst\n    PopIntoRegister { dst: RegisterOperand },\n\n    /// Copy value at register `src` and push it on the stack.\n    ///\n    /// - Registers:\n    ///   - Input: src\n    PushFromRegister { src: RegisterOperand },\n\n    /// Push a declarative environment.\n    ///\n    /// - Operands:\n    ///   - scope_index: `IndexOperand`\n    PushScope { scope_index: IndexOperand },\n\n    /// Push an object environment.\n    ///\n    /// - Registers:\n    ///   - Input: src\n    PushObjectEnvironment { src: RegisterOperand },\n\n    /// Pop the current environment.\n    PopEnvironment,\n\n    /// Increment loop iteration count.\n    ///\n    /// Used for limiting the loop iteration.\n    IncrementLoopIteration,\n\n    /// Creates the ForInIterator of an object.\n    ///\n    /// - Registers:\n    ///   - Input: src\n    CreateForInIterator { src: RegisterOperand },\n\n    /// Gets the iterator of an object.\n    ///\n    /// - Registers:\n    ///   - Input: src\n    /// - Iterator Stack: **=>** `iterator`\n    GetIterator { src: RegisterOperand },\n\n    /// Gets the async iterator of an object.\n    ///\n    /// - Registers:\n    ///   - Input: src\n    /// - Iterator Stack: **=>** `iterator`\n    GetAsyncIterator { src: RegisterOperand },\n\n    /// Pop an iterator from the iterators stack\n    /// - Registers (out)\n    ///   - iterator: `JsObject`.\n    ///   - next: `JsValue`.\n    IteratorPop { iterator: RegisterOperand, next: RegisterOperand },\n\n    /// Pushes an iterator on the iterators stack\n    /// - Registers (in)\n    ///   - iterator: `JsObject`.\n    ///   - next: `JsValue`.\n    IteratorPush { iterator: RegisterOperand, next: RegisterOperand },\n\n    /// Calls the `next` method of `iterator`, updating its record with the next value.\n    ///\n    /// - Iterator Stack: `iterator` **=>** `iterator`\n    IteratorNext,\n\n    /// Updates the result of the currently active iterator.\n    /// - Registers (inout)\n    ///  - result: `JsValue` (in), `bool` (out) with the `done` value of the iterator.\n    IteratorUpdateResult { result: RegisterOperand },\n\n    /// Returns `true` if the current iterator is done, or `false` otherwise\n    ///\n    /// - Registers:\n    ///   - Output: dst\n    /// - Iterator Stack: `iterator` **=>** `iterator`\n    IteratorDone { dst: RegisterOperand },\n\n    /// Finishes the call to `Opcode::IteratorNext` within a `for await` loop by setting the current\n    /// result of the current iterator.\n    ///\n    /// - Registers:\n    ///   - Input: resume_kind, value\n    /// - Iterator Stack: `iterator` **=>** `iterator`\n    IteratorFinishAsyncNext { resume_kind: RegisterOperand, value: RegisterOperand },\n\n    /// Gets the `value` property of the current iterator record.\n    ///\n    /// - Registers:\n    ///   - Output: dst\n    /// - Iterator Stack: `iterator` **=>** `iterator`\n    IteratorValue { dst: RegisterOperand },\n\n    /// Gets the last iteration result of the current iterator record.\n    ///\n    /// - Registers:\n    ///   - Output: dst\n    /// - Iterator Stack: `iterator` **=>** `iterator`\n    IteratorResult { dst: RegisterOperand },\n\n    /// Consume the iterator and construct and array with all the values.\n    ///\n    /// - Registers:\n    ///   - Output: dst\n    /// - Iterator Stack: `iterator` **=>** `iterator`\n    IteratorToArray { dst: RegisterOperand },\n\n    /// Store `true` in dst if the iterator stack is empty.\n    ///\n    /// - Registers:\n    ///   - Output: dst\n    /// - Iterator Stack: **=>**\n    IteratorStackEmpty { dst: RegisterOperand },\n\n    /// Creates a new iterator result object.\n    ///\n    /// - Operands:\n    ///   - done: `bool` (codified as u8 with `0` -> `false` and `!0` -> `true`)\n    /// - Registers:\n    ///   - Input: value\n    ///   - Output: value\n    CreateIteratorResult { value: RegisterOperand, done: IndexOperand },\n\n    /// Calls `return` on the current iterator and returns the result.\n    ///\n    /// - Registers:\n    ///   - Output: value, called\n    /// - Iterator Stack: `iterator` **=>**\n    IteratorReturn { value: RegisterOperand, called: RegisterOperand },\n\n    /// Concat multiple stack objects into a string.\n    ///\n    /// - Registers:\n    ///   - Input: values\n    ///   - Output: dst\n    ConcatToString { dst: RegisterOperand, values: ThinVec<RegisterOperand> },\n\n    /// Require the stack value to be neither null nor undefined.\n    ///\n    /// - Registers:\n    ///   - Input: src\n    ValueNotNullOrUndefined { src: RegisterOperand },\n\n    /// Initialize the rest parameter value of a function from the remaining arguments.\n    ///\n    /// - Stack: `argument_1` .. `argument_n` **=>**\n    /// - Registers:\n    ///   - Output: dst\n    RestParameterInit { dst: RegisterOperand },\n\n    /// Yields from the current generator execution.\n    ///\n    /// - Registers:\n    ///   - Input: src\n    ///   - Output: resume_kind, received\n    GeneratorYield { src: RegisterOperand },\n\n    /// Yields from the current async generator execution.\n    ///\n    /// - Registers:\n    ///   - Input: src\n    ///   - Output: resume_kind, received\n    AsyncGeneratorYield { src: RegisterOperand },\n\n    /// Create a promise capacity for an async function, if not already set.\n    CreatePromiseCapability,\n\n    /// Stops the current async function and schedules it to resume later.\n    ///\n    /// - Registers:\n    ///   - Input: src\n    ///   - Output: resume_kind, received\n    Await { src: RegisterOperand },\n\n    /// Store the current new target in dst.\n    ///\n    /// - Registers:\n    ///   - Output: dst\n    NewTarget { dst: RegisterOperand },\n\n    /// Store the current `import.meta` in dst.\n    ///\n    /// - Registers:\n    ///   - Output: dst\n    ImportMeta { dst: RegisterOperand },\n\n    /// Store `true` in the register if the value is an object, or `false` otherwise.\n    ///\n    /// - Registers:\n    ///   - Input: value\n    ///   - Output: value\n    IsObject { value: RegisterOperand },\n\n    /// Lookup if a tagged template object is cached and skip the creation if it is.\n    ///\n    /// - Operands:\n    ///   - address: `u32`\n    ///   - site: `u64`\n    /// - Registers:\n    ///   - Output: dst\n    TemplateLookup { address: Address, site: u64, dst: RegisterOperand },\n\n    /// Create a new tagged template object and cache it.\n    ///\n    /// - Operands:\n    ///   - site: `u64`\n    /// - Registers:\n    ///   - Inputs: values\n    ///   - Output: dst\n    TemplateCreate { site: u64, dst: RegisterOperand, values: ThinVec<u32> },\n\n    /// Push a private environment.\n    ///\n    /// Operands: count: `u32`, count * name_index: `u32`\n    ///\n    /// - Registers:\n    ///   - Input: class, name_indices\n    PushPrivateEnvironment { class: RegisterOperand, name_indices: ThinVec<u32> },\n\n    /// Pop a private environment.\n    PopPrivateEnvironment,\n\n    /// Creates a mapped `arguments` object.\n    ///\n    /// Performs [`10.4.4.7 CreateMappedArgumentsObject ( func, formals, argumentsList, env )`]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-createunmappedargumentsobject\n    ///\n    /// - Registers:\n    ///   - Output: dst\n    CreateMappedArgumentsObject { dst: RegisterOperand },\n\n    /// Creates an unmapped `arguments` object.\n    ///\n    /// Performs: [`10.4.4.6 CreateUnmappedArgumentsObject ( argumentsList )`]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-createmappedargumentsobject\n    ///\n    /// - Registers:\n    ///   - Output: dst\n    CreateUnmappedArgumentsObject { dst: RegisterOperand },\n\n    /// Reserved [`Opcode`].\n    Reserved1 => Reserved,\n    /// Reserved [`Opcode`].\n    Reserved2 => Reserved,\n    /// Reserved [`Opcode`].\n    Reserved3 => Reserved,\n    /// Reserved [`Opcode`].\n    Reserved4 => Reserved,\n    /// Reserved [`Opcode`].\n    Reserved5 => Reserved,\n    /// Reserved [`Opcode`].\n    Reserved6 => Reserved,\n    /// Reserved [`Opcode`].\n    Reserved7 => Reserved,\n    /// Reserved [`Opcode`].\n    Reserved8 => Reserved,\n    /// Reserved [`Opcode`].\n    Reserved9 => Reserved,\n    /// Reserved [`Opcode`].\n    Reserved10 => Reserved,\n    /// Reserved [`Opcode`].\n    Reserved11 => Reserved,\n    /// Reserved [`Opcode`].\n    Reserved12 => Reserved,\n    /// Reserved [`Opcode`].\n    Reserved13 => Reserved,\n    /// Reserved [`Opcode`].\n    Reserved14 => Reserved,\n    /// Reserved [`Opcode`].\n    Reserved15 => Reserved,\n    /// Reserved [`Opcode`].\n    Reserved16 => Reserved,\n    /// Reserved [`Opcode`].\n    Reserved17 => Reserved,\n    /// Reserved [`Opcode`].\n    Reserved18 => Reserved,\n    /// Reserved [`Opcode`].\n    Reserved19 => Reserved,\n    /// Reserved [`Opcode`].\n    Reserved20 => Reserved,\n    /// Reserved [`Opcode`].\n    Reserved21 => Reserved,\n    /// Reserved [`Opcode`].\n    Reserved22 => Reserved,\n    /// Reserved [`Opcode`].\n    Reserved23 => Reserved,\n    /// Reserved [`Opcode`].\n    Reserved24 => Reserved,\n    /// Reserved [`Opcode`].\n    Reserved25 => Reserved,\n    /// Reserved [`Opcode`].\n    Reserved26 => Reserved,\n    /// Reserved [`Opcode`].\n    Reserved27 => Reserved,\n    /// Reserved [`Opcode`].\n    Reserved28 => Reserved,\n    /// Reserved [`Opcode`].\n    Reserved29 => Reserved,\n    /// Reserved [`Opcode`].\n    Reserved30 => Reserved,\n    /// Reserved [`Opcode`].\n    Reserved31 => Reserved,\n    /// Reserved [`Opcode`].\n    Reserved32 => Reserved,\n    /// Reserved [`Opcode`].\n    Reserved33 => Reserved,\n    /// Reserved [`Opcode`].\n    Reserved34 => Reserved,\n    /// Reserved [`Opcode`].\n    Reserved35 => Reserved,\n    /// Reserved [`Opcode`].\n    Reserved36 => Reserved,\n    /// Reserved [`Opcode`].\n    Reserved37 => Reserved,\n    /// Reserved [`Opcode`].\n    Reserved38 => Reserved,\n    /// Reserved [`Opcode`].\n    Reserved39 => Reserved,\n    /// Reserved [`Opcode`].\n    Reserved40 => Reserved,\n    /// Reserved [`Opcode`].\n    Reserved41 => Reserved,\n    /// Reserved [`Opcode`].\n    Reserved42 => Reserved,\n    /// Reserved [`Opcode`].\n    Reserved43 => Reserved,\n    /// Reserved [`Opcode`].\n    Reserved44 => Reserved,\n    /// Reserved [`Opcode`].\n    Reserved45 => Reserved,\n    /// Reserved [`Opcode`].\n    Reserved46 => Reserved,\n    /// Reserved [`Opcode`].\n    Reserved47 => Reserved,\n    /// Reserved [`Opcode`].\n    Reserved48 => Reserved,\n    /// Reserved [`Opcode`].\n    Reserved49 => Reserved,\n    /// Reserved [`Opcode`].\n    Reserved50 => Reserved,\n    /// Reserved [`Opcode`].\n    Reserved51 => Reserved,\n    /// Reserved [`Opcode`].\n    Reserved52 => Reserved,\n    /// Reserved [`Opcode`].\n    Reserved53 => Reserved,\n    /// Reserved [`Opcode`].\n    Reserved54 => Reserved,\n    /// Reserved [`Opcode`].\n    Reserved55 => Reserved,\n    /// Reserved [`Opcode`].\n    Reserved56 => Reserved,\n    /// Reserved [`Opcode`].\n    Reserved57 => Reserved,\n    /// Reserved [`Opcode`].\n    Reserved58 => Reserved,\n    /// Reserved [`Opcode`].\n    Reserved59 => Reserved,\n    /// Reserved [`Opcode`].\n    Reserved60 => Reserved,\n}\n"
  },
  {
    "path": "core/engine/src/vm/opcode/new/mod.rs",
    "content": "use super::IndexOperand;\nuse crate::{Context, JsResult, error::JsNativeError, vm::opcode::Operation};\n\n/// `New` implements the Opcode Operation for `Opcode::New`\n///\n/// Operation:\n///  - Call construct on a function.\n#[derive(Debug, Clone, Copy)]\npub(crate) struct New;\n\nimpl New {\n    #[inline(always)]\n    pub(super) fn operation(argument_count: IndexOperand, context: &mut Context) -> JsResult<()> {\n        let func = context\n            .vm\n            .stack\n            .calling_convention_get_function(argument_count.into());\n\n        let cons = func\n            .as_object()\n            .ok_or_else(|| JsNativeError::typ().with_message(\"not a constructor\"))?\n            .clone();\n\n        context.vm.stack.push(cons.clone()); // Push new.target\n\n        cons.__construct__(argument_count.into()).resolve(context)?;\n        Ok(())\n    }\n}\n\nimpl Operation for New {\n    const NAME: &'static str = \"New\";\n    const INSTRUCTION: &'static str = \"INST - New\";\n    const COST: u8 = 3;\n}\n\n/// `NewSpread` implements the Opcode Operation for `Opcode::NewSpread`\n///\n/// Operation:\n///  - Call construct on a function where the arguments contain spreads.\n#[derive(Debug, Clone, Copy)]\npub(crate) struct NewSpread;\n\nimpl NewSpread {\n    #[inline(always)]\n    pub(super) fn operation((): (), context: &mut Context) -> JsResult<()> {\n        // Get the arguments that are stored as an array object on the stack.\n        let arguments_array = context.vm.stack.pop();\n        let arguments_array_object = arguments_array\n            .as_object()\n            .expect(\"arguments array in call spread function must be an object\");\n        let arguments = arguments_array_object\n            .borrow()\n            .properties()\n            .to_dense_indexed_properties()\n            .expect(\"arguments array in call spread function must be dense\");\n\n        let func = context.vm.stack.pop();\n\n        let cons = func\n            .as_object()\n            .ok_or_else(|| JsNativeError::typ().with_message(\"not a constructor\"))?\n            .clone();\n\n        let argument_count = arguments.len();\n        context.vm.stack.push(func);\n        context\n            .vm\n            .stack\n            .calling_convention_push_arguments(&arguments);\n        context.vm.stack.push(cons.clone()); // Push new.target\n\n        cons.__construct__(argument_count).resolve(context)?;\n        Ok(())\n    }\n}\n\nimpl Operation for NewSpread {\n    const NAME: &'static str = \"NewSpread\";\n    const INSTRUCTION: &'static str = \"INST - NewSpread\";\n    const COST: u8 = 3;\n}\n"
  },
  {
    "path": "core/engine/src/vm/opcode/nop/mod.rs",
    "content": "use crate::{Context, vm::opcode::Operation};\n\n/// `Reserved` implements the Opcode Operation for `Opcode::Reserved`\n///\n/// Operation:\n///  - Panics, this should be unreachable.\n#[derive(Debug, Clone, Copy)]\npub(crate) struct Reserved;\n\nimpl Reserved {\n    #[inline(always)]\n    pub(crate) fn operation((): (), _: &mut Context) {\n        unreachable!(\"Reserved opcodes are unreachable!\")\n    }\n}\n\nimpl Operation for Reserved {\n    const NAME: &'static str = \"Reserved\";\n    const INSTRUCTION: &'static str = \"INST - Reserved\";\n    const COST: u8 = 0;\n}\n"
  },
  {
    "path": "core/engine/src/vm/opcode/object.rs",
    "content": "use crate::{\n    Context, JsExpect, JsResult, JsValue,\n    object::internal_methods::InternalMethodPropertyContext,\n    vm::opcode::{Operation, RegisterOperand},\n};\n\n/// `SetPrototype` implements the Opcode Operation for `Opcode::SetPrototype`\n///\n/// Operation:\n///  - Sets the prototype of an object.\n#[derive(Debug, Clone, Copy)]\npub(crate) struct SetPrototype;\n\nimpl SetPrototype {\n    #[inline(always)]\n    pub(crate) fn operation(\n        (object, value): (RegisterOperand, RegisterOperand),\n        context: &mut Context,\n    ) -> JsResult<()> {\n        let object = context.vm.get_register(object.into()).clone();\n        let value = context.vm.get_register(value.into());\n\n        let prototype = if let Some(prototype) = value.as_object() {\n            Some(prototype.clone())\n        } else if value.is_null() {\n            None\n        } else {\n            return Ok(());\n        };\n\n        let object = object.as_object().js_expect(\"object is not an object\")?;\n        object\n            .__set_prototype_of__(prototype, &mut InternalMethodPropertyContext::new(context))\n            .js_expect(\"cannot fail per spec\")?;\n\n        Ok(())\n    }\n}\n\nimpl Operation for SetPrototype {\n    const NAME: &'static str = \"SetPrototype\";\n    const INSTRUCTION: &'static str = \"INST - SetPrototype\";\n    const COST: u8 = 4;\n}\n\n/// `GetPrototype` implements the Opcode Operation for `Opcode::GetPrototype`\n///\n/// Operation:\n///  - Gets the prototype of an object.\n#[derive(Debug, Clone, Copy)]\npub(crate) struct GetPrototype;\n\nimpl GetPrototype {\n    #[inline(always)]\n    pub(crate) fn operation(object: RegisterOperand, context: &mut Context) -> JsResult<()> {\n        let object_val = context\n            .vm\n            .get_register(object.into())\n            .as_object()\n            .js_expect(\"object register is not an object\")?;\n\n        let proto_object = object_val\n            .__get_prototype_of__(context)?\n            .map_or_else(JsValue::null, JsValue::from);\n\n        context.vm.set_register(object.into(), proto_object);\n        Ok(())\n    }\n}\n\nimpl Operation for GetPrototype {\n    const NAME: &'static str = \"GetPrototype\";\n    const INSTRUCTION: &'static str = \"INST - GetPrototype\";\n    const COST: u8 = 4;\n}\n"
  },
  {
    "path": "core/engine/src/vm/opcode/pop/mod.rs",
    "content": "use crate::{Context, vm::opcode::Operation};\n\n/// `Pop` implements the Opcode Operation for `Opcode::Pop`\n///\n/// Operation:\n///  - Pop the top value from the stack.\n#[derive(Debug, Clone, Copy)]\npub(crate) struct Pop;\n\nimpl Pop {\n    #[inline(always)]\n    pub(super) fn operation((): (), context: &mut Context) {\n        let _val = context.vm.stack.pop();\n    }\n}\n\nimpl Operation for Pop {\n    const NAME: &'static str = \"Pop\";\n    const INSTRUCTION: &'static str = \"INST - Pop\";\n    const COST: u8 = 1;\n}\n\n/// `PopEnvironment` implements the Opcode Operation for `Opcode::PopEnvironment`\n///\n/// Operation:\n///  - Pop the current environment.\n#[derive(Debug, Clone, Copy)]\npub(crate) struct PopEnvironment;\n\nimpl PopEnvironment {\n    #[inline(always)]\n    pub(super) fn operation((): (), context: &mut Context) {\n        context.vm.frame_mut().environments.pop();\n    }\n}\n\nimpl Operation for PopEnvironment {\n    const NAME: &'static str = \"PopEnvironment\";\n    const INSTRUCTION: &'static str = \"INST - PopEnvironment\";\n    const COST: u8 = 1;\n}\n"
  },
  {
    "path": "core/engine/src/vm/opcode/push/array.rs",
    "content": "use crate::{\n    Context, JsResult, JsValue,\n    builtins::Array,\n    string::StaticJsStrings,\n    vm::opcode::{Operation, RegisterOperand},\n};\n\n/// `StoreNewArray` implements the Opcode Operation for `Opcode::StoreNewArray`\n///\n/// Operation:\n///  - Store an empty array in dst.\n#[derive(Debug, Clone, Copy)]\npub(crate) struct StoreNewArray;\n\nimpl StoreNewArray {\n    #[inline(always)]\n    pub(crate) fn operation(array: RegisterOperand, context: &mut Context) {\n        let value = context\n            .intrinsics()\n            .templates()\n            .array()\n            .create(Array, Vec::from([JsValue::new(0)]));\n        context.vm.set_register(array.into(), value.into());\n    }\n}\n\nimpl Operation for StoreNewArray {\n    const NAME: &'static str = \"StoreNewArray\";\n    const INSTRUCTION: &'static str = \"INST - StoreNewArray\";\n    const COST: u8 = 3;\n}\n\n/// `PushValueToArray` implements the Opcode Operation for `Opcode::PushValueToArray`\n///\n/// Operation:\n///  - Push a value to an array.\n#[derive(Debug, Clone, Copy)]\npub(crate) struct PushValueToArray;\n\nimpl PushValueToArray {\n    #[inline(always)]\n    pub(crate) fn operation(\n        (value, array): (RegisterOperand, RegisterOperand),\n        context: &mut Context,\n    ) {\n        let value = context.vm.get_register(value.into()).clone();\n        let o = context\n            .vm\n            .get_register(array.into())\n            .as_object()\n            .expect(\"should be an object\");\n\n        // Fast path: push directly to dense indexed storage.\n        {\n            let mut o_mut = o.borrow_mut();\n            let len = o_mut.properties().storage[0].as_i32();\n            if let Some(len) = len\n                && o_mut.properties_mut().indexed_properties.push_dense(&value)\n            {\n                o_mut.properties_mut().storage[0] = JsValue::new(len + 1);\n                return;\n            }\n        }\n\n        // Slow path: fall through to the generic property machinery.\n        let len = o\n            .length_of_array_like(context)\n            .expect(\"should have 'length' property\");\n        o.create_data_property_or_throw(len, value, context)\n            .expect(\"should be able to create new data property\");\n    }\n}\n\nimpl Operation for PushValueToArray {\n    const NAME: &'static str = \"PushValueToArray\";\n    const INSTRUCTION: &'static str = \"INST - PushValueToArray\";\n    const COST: u8 = 3;\n}\n\n/// `PushElisionToArray` implements the Opcode Operation for `Opcode::PushElisionToArray`\n///\n/// Operation:\n///  - Push an empty element/hole to an array.\n#[derive(Debug, Clone, Copy)]\npub(crate) struct PushElisionToArray;\n\nimpl PushElisionToArray {\n    #[inline(always)]\n    pub(crate) fn operation(array: RegisterOperand, context: &mut Context) -> JsResult<()> {\n        let array = context.vm.get_register(array.into()).clone();\n        let o = array.as_object().expect(\"should always be an object\");\n        let len = o\n            .length_of_array_like(context)\n            .expect(\"arrays should always have a 'length' property\");\n        o.set(StaticJsStrings::LENGTH, len + 1, true, context)?;\n        o.borrow_mut()\n            .properties_mut()\n            .indexed_properties\n            .transform_to_sparse();\n        Ok(())\n    }\n}\n\nimpl Operation for PushElisionToArray {\n    const NAME: &'static str = \"PushElisionToArray\";\n    const INSTRUCTION: &'static str = \"INST - PushElisionToArray\";\n    const COST: u8 = 3;\n}\n\n/// `PushIteratorToArray` implements the Opcode Operation for `Opcode::PushIteratorToArray`\n///\n/// Operation:\n///  - Push all iterator values to an array.\n#[derive(Debug, Clone, Copy)]\npub(crate) struct PushIteratorToArray;\n\nimpl PushIteratorToArray {\n    #[inline(always)]\n    pub(crate) fn operation(array: RegisterOperand, context: &mut Context) -> JsResult<()> {\n        let array = context.vm.get_register(array.into()).clone();\n        let mut iterator = context\n            .vm\n            .frame_mut()\n            .iterators\n            .pop()\n            .expect(\"iterator stack should have at least an iterator\");\n        while let Some(next) = iterator.step_value(context)? {\n            Array::push(&array, &[next], context)?;\n        }\n        Ok(())\n    }\n}\n\nimpl Operation for PushIteratorToArray {\n    const NAME: &'static str = \"PushIteratorToArray\";\n    const INSTRUCTION: &'static str = \"INST - PushIteratorToArray\";\n    const COST: u8 = 8;\n}\n"
  },
  {
    "path": "core/engine/src/vm/opcode/push/class/field.rs",
    "content": "use crate::{\n    Context, JsResult,\n    builtins::function::OrdinaryFunction,\n    object::JsFunction,\n    vm::opcode::{IndexOperand, Operation, RegisterOperand},\n};\n\n/// `PushClassField` implements the Opcode Operation for `Opcode::PushClassField`\n///\n/// Operation:\n///  - Push a field to a class.\n#[derive(Debug, Clone, Copy)]\npub(crate) struct PushClassField;\n\nimpl PushClassField {\n    #[inline(always)]\n    pub(crate) fn operation(\n        (class, name, function, is_anonymous_function): (\n            RegisterOperand,\n            RegisterOperand,\n            RegisterOperand,\n            IndexOperand,\n        ),\n        context: &mut Context,\n    ) -> JsResult<()> {\n        let class = context.vm.get_register(class.into()).clone();\n        let name = context.vm.get_register(name.into()).clone();\n        let function = context.vm.get_register(function.into()).clone();\n        let is_anonymous_function = u32::from(is_anonymous_function) != 0;\n\n        let name = name.to_property_key(context)?;\n        let function = function\n            .as_object()\n            .expect(\"field value must be function object\");\n        let class = class.as_object().expect(\"class must be function object\");\n\n        function\n            .downcast_mut::<OrdinaryFunction>()\n            .expect(\"field value must be function object\")\n            .set_home_object(class.clone());\n\n        class\n            .downcast_mut::<OrdinaryFunction>()\n            .expect(\"class must be function object\")\n            .push_field(\n                name.clone(),\n                JsFunction::from_object_unchecked(function.clone()),\n                if is_anonymous_function {\n                    Some(name)\n                } else {\n                    None\n                },\n            );\n        Ok(())\n    }\n}\n\nimpl Operation for PushClassField {\n    const NAME: &'static str = \"PushClassField\";\n    const INSTRUCTION: &'static str = \"INST - PushClassField\";\n    const COST: u8 = 6;\n}\n\n/// `PushClassFieldPrivate` implements the Opcode Operation for `Opcode::PushClassFieldPrivate`\n///\n/// Operation:\n///  - Push a private field to the class.\n#[derive(Debug, Clone, Copy)]\npub(crate) struct PushClassFieldPrivate;\n\nimpl PushClassFieldPrivate {\n    #[inline(always)]\n    pub(crate) fn operation(\n        (class, function, index): (RegisterOperand, RegisterOperand, IndexOperand),\n        context: &mut Context,\n    ) {\n        let class = context.vm.get_register(class.into());\n        let function = context.vm.get_register(function.into());\n        let name = context\n            .vm\n            .frame()\n            .code_block()\n            .constant_string(index.into());\n\n        let function = function\n            .as_object()\n            .expect(\"field value must be function object\");\n        let class = class.as_object().expect(\"class must be function object\");\n\n        function\n            .downcast_mut::<OrdinaryFunction>()\n            .expect(\"field value must be function object\")\n            .set_home_object(class.clone());\n\n        class\n            .downcast_mut::<OrdinaryFunction>()\n            .expect(\"class must be function object\")\n            .push_field_private(\n                class.private_name(name),\n                JsFunction::from_object_unchecked(function.clone()),\n            );\n    }\n}\n\nimpl Operation for PushClassFieldPrivate {\n    const NAME: &'static str = \"PushClassFieldPrivate\";\n    const INSTRUCTION: &'static str = \"INST - PushClassFieldPrivate\";\n    const COST: u8 = 3;\n}\n"
  },
  {
    "path": "core/engine/src/vm/opcode/push/class/mod.rs",
    "content": "use crate::{\n    Context, JsResult, JsValue,\n    error::JsNativeError,\n    object::PROTOTYPE,\n    vm::opcode::{Operation, RegisterOperand},\n};\n\npub(crate) mod field;\npub(crate) mod private;\n\npub(crate) use field::*;\npub(crate) use private::*;\n\n/// `StoreClassPrototype` implements the Opcode Operation for `Opcode::StoreClassPrototype`\n///\n/// Operation:\n///  - Get the prototype of a superclass and store it in dst.\n#[derive(Debug, Clone, Copy)]\npub(crate) struct StoreClassPrototype;\n\nimpl StoreClassPrototype {\n    #[inline(always)]\n    pub(crate) fn operation(\n        (dst, class, superclass): (RegisterOperand, RegisterOperand, RegisterOperand),\n        context: &mut Context,\n    ) -> JsResult<()> {\n        let class = context.vm.get_register(class.into()).clone();\n        let superclass = context.vm.get_register(superclass.into()).clone();\n\n        // // Taken from `15.7.14 Runtime Semantics: ClassDefinitionEvaluation`:\n        // <https://tc39.es/ecma262/#sec-runtime-semantics-classdefinitionevaluation>\n        //\n        // 8. Else\n        //     f. If superclass is null, then\n        let (proto_parent, constructor_parent) = if superclass.is_null() {\n            // i. Let protoParent be null.\n            // ii. Let constructorParent be %Function.prototype%.\n            //\n            // NOTE(HalidOdat): We set constructorParent to None, it is resolved in `SetClassPrototype` opcode.\n            (JsValue::null(), None)\n\n        // h. Else,\n        } else if let Some(superclass) = superclass.as_constructor() {\n            // i. Let protoParent be ? Get(superclass, \"prototype\").\n            let proto = superclass.get(PROTOTYPE, context)?;\n\n            // ii. If protoParent is not an Object and protoParent is not null, throw a TypeError exception.\n            if !proto.is_object() && !proto.is_null() {\n                return Err(JsNativeError::typ()\n                    .with_message(\"superclass prototype must be an object or null\")\n                    .into());\n            }\n\n            // iii. Let constructorParent be superclass.\n            (proto, Some(superclass.clone()))\n\n        // g. Else if IsConstructor(superclass) is false, then\n        } else {\n            // i. Throw a TypeError exception.\n            return Err(JsNativeError::typ()\n                .with_message(\"superclass must be a constructor\")\n                .into());\n        };\n\n        let class_object = class.as_object().expect(\"class must be object\");\n\n        if let Some(constructor_parent) = constructor_parent {\n            class_object.set_prototype(Some(constructor_parent));\n        }\n\n        context.vm.set_register(dst.into(), proto_parent);\n        Ok(())\n    }\n}\n\nimpl Operation for StoreClassPrototype {\n    const NAME: &'static str = \"StoreClassPrototype\";\n    const INSTRUCTION: &'static str = \"INST - StoreClassPrototype\";\n    const COST: u8 = 6;\n}\n"
  },
  {
    "path": "core/engine/src/vm/opcode/push/class/private.rs",
    "content": "use crate::{\n    Context,\n    builtins::function::OrdinaryFunction,\n    js_str, js_string,\n    object::{PrivateElement, internal_methods::InternalMethodPropertyContext},\n    property::PropertyDescriptor,\n    vm::opcode::{IndexOperand, Operation, RegisterOperand},\n};\n\n/// `PushClassPrivateMethod` implements the Opcode Operation for `Opcode::PushClassPrivateMethod`\n///\n/// Operation:\n///  - Push a private method to the class.\n#[derive(Debug, Clone, Copy)]\npub(crate) struct PushClassPrivateMethod;\n\nimpl PushClassPrivateMethod {\n    #[inline(always)]\n    pub(crate) fn operation(\n        (object, prototype, value, index): (\n            RegisterOperand,\n            RegisterOperand,\n            RegisterOperand,\n            IndexOperand,\n        ),\n        context: &mut Context,\n    ) {\n        let object = context.vm.get_register(object.into()).clone();\n        let prototype = context.vm.get_register(prototype.into()).clone();\n        let value = context.vm.get_register(value.into()).clone();\n        let name = context\n            .vm\n            .frame()\n            .code_block()\n            .constant_string(index.into());\n\n        let value = value.as_callable().expect(\"method must be callable\");\n        let prototype = prototype\n            .as_object()\n            .expect(\"class_prototype must be function object\");\n        let object = object.as_object().expect(\"class must be function object\");\n\n        let name_string = js_string!(js_str!(\"#\"), &name);\n        let desc = PropertyDescriptor::builder()\n            .value(name_string)\n            .writable(false)\n            .enumerable(false)\n            .configurable(true)\n            .build();\n        value\n            .__define_own_property__(\n                &js_string!(\"name\").into(),\n                desc,\n                &mut InternalMethodPropertyContext::new(context),\n            )\n            .expect(\"failed to set name property on private method\");\n        value\n            .downcast_mut::<OrdinaryFunction>()\n            .expect(\"method must be function object\")\n            .set_home_object(prototype.clone());\n\n        object\n            .downcast_mut::<OrdinaryFunction>()\n            .expect(\"class must be function object\")\n            .push_private_method(\n                object.private_name(name),\n                PrivateElement::Method(value.clone()),\n            );\n    }\n}\n\nimpl Operation for PushClassPrivateMethod {\n    const NAME: &'static str = \"PushClassPrivateMethod\";\n    const INSTRUCTION: &'static str = \"INST - PushClassPrivateMethod\";\n    const COST: u8 = 6;\n}\n\n/// `PushClassPrivateGetter` implements the Opcode Operation for `Opcode::PushClassPrivateGetter`\n///\n/// Operation:\n///  - Push a private getter to the class.\n#[derive(Debug, Clone, Copy)]\npub(crate) struct PushClassPrivateGetter;\n\nimpl PushClassPrivateGetter {\n    #[inline(always)]\n    pub(crate) fn operation(\n        (object, value, index): (RegisterOperand, RegisterOperand, IndexOperand),\n        context: &mut Context,\n    ) {\n        let object = context.vm.get_register(object.into());\n        let value = context.vm.get_register(value.into());\n        let name = context\n            .vm\n            .frame()\n            .code_block()\n            .constant_string(index.into());\n\n        let value = value.as_callable().expect(\"getter must be callable\");\n        let object = object.as_object().expect(\"class must be function object\");\n\n        object\n            .downcast_mut::<OrdinaryFunction>()\n            .expect(\"class must be function object\")\n            .push_private_method(\n                object.private_name(name),\n                PrivateElement::Accessor {\n                    getter: Some(value.clone()),\n                    setter: None,\n                },\n            );\n    }\n}\n\nimpl Operation for PushClassPrivateGetter {\n    const NAME: &'static str = \"PushClassPrivateGetter\";\n    const INSTRUCTION: &'static str = \"INST - PushClassPrivateGetter\";\n    const COST: u8 = 6;\n}\n\n/// `PushClassPrivateSetter` implements the Opcode Operation for `Opcode::PushClassPrivateSetter`\n///\n/// Operation:\n///  - Push a private setter to the class.\n#[derive(Debug, Clone, Copy)]\npub(crate) struct PushClassPrivateSetter;\n\nimpl PushClassPrivateSetter {\n    #[inline(always)]\n    pub(crate) fn operation(\n        (object, value, index): (RegisterOperand, RegisterOperand, IndexOperand),\n        context: &mut Context,\n    ) {\n        let object = context.vm.get_register(object.into());\n        let value = context.vm.get_register(value.into());\n        let name = context\n            .vm\n            .frame()\n            .code_block()\n            .constant_string(index.into());\n\n        let value = value.as_callable().expect(\"getter must be callable\");\n        let object = object.as_object().expect(\"class must be function object\");\n\n        object\n            .downcast_mut::<OrdinaryFunction>()\n            .expect(\"class must be function object\")\n            .push_private_method(\n                object.private_name(name),\n                PrivateElement::Accessor {\n                    getter: None,\n                    setter: Some(value.clone()),\n                },\n            );\n    }\n}\n\nimpl Operation for PushClassPrivateSetter {\n    const NAME: &'static str = \"PushClassPrivateSetter\";\n    const INSTRUCTION: &'static str = \"INST - PushClassPrivateSetter\";\n    const COST: u8 = 6;\n}\n"
  },
  {
    "path": "core/engine/src/vm/opcode/push/environment.rs",
    "content": "use crate::{\n    Context, JsResult,\n    builtins::function::OrdinaryFunction,\n    environments::PrivateEnvironment,\n    vm::opcode::{IndexOperand, Operation, RegisterOperand},\n};\nuse boa_gc::Gc;\nuse thin_vec::ThinVec;\n\n/// `PushScope` implements the Opcode Operation for `Opcode::PushScope`\n///\n/// Operation:\n///  - Push a declarative environment\n#[derive(Debug, Clone, Copy)]\npub(crate) struct PushScope;\n\nimpl PushScope {\n    #[inline(always)]\n    pub(crate) fn operation(index: IndexOperand, context: &mut Context) {\n        let scope = context.vm.frame().code_block().constant_scope(index.into());\n        let frame = context.vm.frame_mut();\n        let global = frame.realm.environment();\n        frame\n            .environments\n            .push_lexical(scope.num_bindings_non_local(), global);\n    }\n}\n\nimpl Operation for PushScope {\n    const NAME: &'static str = \"PushScope\";\n    const INSTRUCTION: &'static str = \"INST - PushScope\";\n    const COST: u8 = 3;\n}\n\n/// `PushObjectEnvironment` implements the Opcode Operation for `Opcode::PushObjectEnvironment`\n///\n/// Operation:\n///  - Push an object environment\n#[derive(Debug, Clone, Copy)]\npub(crate) struct PushObjectEnvironment;\n\nimpl PushObjectEnvironment {\n    #[inline(always)]\n    pub(crate) fn operation(value: RegisterOperand, context: &mut Context) -> JsResult<()> {\n        let object = context.vm.get_register(value.into()).clone();\n        let object = object.to_object(context)?;\n        context.vm.frame_mut().environments.push_object(object);\n        Ok(())\n    }\n}\n\nimpl Operation for PushObjectEnvironment {\n    const NAME: &'static str = \"PushObjectEnvironment\";\n    const INSTRUCTION: &'static str = \"INST - PushObjectEnvironment\";\n    const COST: u8 = 3;\n}\n\n/// `PushPrivateEnvironment` implements the Opcode Operation for `Opcode::PushPrivateEnvironment`\n///\n/// Operation:\n///  - Push a private environment.\n#[derive(Debug, Clone, Copy)]\npub(crate) struct PushPrivateEnvironment;\n\nimpl PushPrivateEnvironment {\n    #[inline(always)]\n    pub(crate) fn operation(\n        (class, name_indices): (RegisterOperand, ThinVec<u32>),\n        context: &mut Context,\n    ) {\n        let class = context.vm.get_register(class.into());\n        let class = class.as_object().expect(\"should be a object\");\n        let mut names = Vec::with_capacity(name_indices.len());\n        for index in name_indices {\n            let name = context\n                .vm\n                .frame()\n                .code_block()\n                .constant_string(index as usize);\n            names.push(name);\n        }\n\n        let ptr: *const _ = class.as_ref();\n        let environment = Gc::new(PrivateEnvironment::new(ptr.cast::<()>() as usize, names));\n\n        class\n            .downcast_mut::<OrdinaryFunction>()\n            .expect(\"class object must be function\")\n            .push_private_environment(environment.clone());\n        context\n            .vm\n            .frame_mut()\n            .environments\n            .push_private(environment);\n    }\n}\n\nimpl Operation for PushPrivateEnvironment {\n    const NAME: &'static str = \"PushPrivateEnvironment\";\n    const INSTRUCTION: &'static str = \"INST - PushPrivateEnvironment\";\n    const COST: u8 = 5;\n}\n\n/// `PopPrivateEnvironment` implements the Opcode Operation for `Opcode::PopPrivateEnvironment`\n///\n/// Operation:\n///  - Pop a private environment.\n#[derive(Debug, Clone, Copy)]\npub(crate) struct PopPrivateEnvironment;\n\nimpl PopPrivateEnvironment {\n    #[inline(always)]\n    pub(crate) fn operation((): (), context: &mut Context) {\n        context.vm.frame_mut().environments.pop_private();\n    }\n}\n\nimpl Operation for PopPrivateEnvironment {\n    const NAME: &'static str = \"PopPrivateEnvironment\";\n    const INSTRUCTION: &'static str = \"INST - PopPrivateEnvironment\";\n    const COST: u8 = 1;\n}\n"
  },
  {
    "path": "core/engine/src/vm/opcode/push/literal.rs",
    "content": "use crate::{\n    Context, JsResult, JsValue,\n    object::JsRegExp,\n    vm::{\n        Constant,\n        opcode::{IndexOperand, Operation, RegisterOperand},\n    },\n};\n\n/// `StoreLiteral` implements the Opcode Operation for `Opcode::StoreLiteral`\n///\n/// Operation:\n///  - Store literal value in dst.\n#[derive(Debug, Clone, Copy)]\npub(crate) struct StoreLiteral;\n\nimpl StoreLiteral {\n    #[inline(always)]\n    pub(crate) fn operation((dst, index): (RegisterOperand, IndexOperand), context: &mut Context) {\n        let constant = &context.vm.frame().code_block().constants[usize::from(index)];\n        let value: JsValue = match constant {\n            Constant::BigInt(v) => v.clone().into(),\n            Constant::String(v) => v.clone().into(),\n            _ => unreachable!(\"constant should be a string or bigint\"),\n        };\n        context.vm.set_register(dst.into(), value);\n    }\n}\n\nimpl Operation for StoreLiteral {\n    const NAME: &'static str = \"StoreLiteral\";\n    const INSTRUCTION: &'static str = \"INST - StoreLiteral\";\n    const COST: u8 = 1;\n}\n\n/// `StoreRegexp` implements the Opcode Operation for `Opcode::StoreRegexp`\n///\n/// Operation:\n///  - Store regexp value in dst.\n#[derive(Debug, Clone, Copy)]\npub(crate) struct StoreRegexp;\n\nimpl StoreRegexp {\n    #[inline(always)]\n    pub(crate) fn operation(\n        (dst, pattern_index, flags_index): (RegisterOperand, IndexOperand, IndexOperand),\n        context: &mut Context,\n    ) -> JsResult<()> {\n        let code_block = context.vm.frame().code_block();\n        let pattern = code_block.constant_string(pattern_index.into());\n        let flags = code_block.constant_string(flags_index.into());\n        let regexp = JsRegExp::new(pattern, flags, context)?;\n        context.vm.set_register(dst.into(), regexp.into());\n        Ok(())\n    }\n}\n\nimpl Operation for StoreRegexp {\n    const NAME: &'static str = \"StoreRegexp\";\n    const INSTRUCTION: &'static str = \"INST - StoreRegexp\";\n    const COST: u8 = 5;\n}\n"
  },
  {
    "path": "core/engine/src/vm/opcode/push/mod.rs",
    "content": "use crate::{\n    Context, JsValue,\n    vm::opcode::{Operation, RegisterOperand},\n};\n\npub(crate) mod array;\npub(crate) mod class;\npub(crate) mod environment;\npub(crate) mod literal;\npub(crate) mod numbers;\npub(crate) mod object;\n\npub(crate) use array::*;\npub(crate) use class::*;\npub(crate) use environment::*;\npub(crate) use literal::*;\npub(crate) use numbers::*;\npub(crate) use object::*;\n\nmacro_rules! implement_store_generics {\n    ($name:ident, $push_value:expr, $doc_string:literal) => {\n        #[doc= concat!(\"`\", stringify!($name), \"` implements the `OpCode` Operation for `Opcode::\", stringify!($name), \"`\\n\")]\n        #[doc= \"\\n\"]\n        #[doc=\"Operation:\\n\"]\n        #[doc= concat!(\" - \", $doc_string)]\n        #[derive(Debug, Clone, Copy)]\n        pub(crate) struct $name;\n\n        impl $name {\n            #[inline(always)]\n            pub(super) fn operation(dst: RegisterOperand,  context: &mut Context) {\n                context.vm.set_register(dst.into(), $push_value.into());\n            }\n        }\n\n        impl Operation for $name {\n            const NAME: &'static str = stringify!($name);\n            const INSTRUCTION: &'static str = stringify!(\"INST - \" + $name);\n            const COST: u8 = 1;\n        }\n    };\n}\n\nimplement_store_generics!(\n    StoreUndefined,\n    JsValue::undefined(),\n    \"Store `undefined` in dst.\"\n);\nimplement_store_generics!(StoreNull, JsValue::null(), \"Store `null` in dst.\");\nimplement_store_generics!(StoreTrue, true, \"Store `true` in dst.\");\nimplement_store_generics!(StoreFalse, false, \"Store `false` in dst.\");\nimplement_store_generics!(StoreZero, 0, \"Store integer `0` in dst.\");\nimplement_store_generics!(StoreOne, 1, \"Store integer `1` in dst.\");\nimplement_store_generics!(StoreNan, JsValue::nan(), \"Store `NaN` in dst.\");\nimplement_store_generics!(\n    StorePositiveInfinity,\n    JsValue::positive_infinity(),\n    \"Store `Infinity` in dst.\"\n);\nimplement_store_generics!(\n    StoreNegativeInfinity,\n    JsValue::negative_infinity(),\n    \"Store `-Infinity` in dst.\"\n);\n"
  },
  {
    "path": "core/engine/src/vm/opcode/push/numbers.rs",
    "content": "use crate::{\n    Context,\n    vm::opcode::{Operation, RegisterOperand},\n};\n\nmacro_rules! implement_store_numbers_with_conversion {\n    ($name:ident, $num_type:ty, $doc_string:literal) => {\n        #[doc= concat!(\"`\", stringify!($name), \"` implements the `OpCode` Operation for `Opcode::\", stringify!($name), \"`\\n\")]\n        #[doc= \"\\n\"]\n        #[doc=\"Operation:\\n\"]\n        #[doc= concat!(\" - \", $doc_string)]\n        #[derive(Debug, Clone, Copy)]\n        pub(crate) struct $name;\n\n        impl $name {\n            #[inline(always)]\n            pub(crate) fn operation((dst, value): (RegisterOperand, $num_type),  context: &mut Context) {\n                context.vm.set_register(dst.into(), i32::from(value).into());\n            }\n        }\n\n        impl Operation for $name {\n            const NAME: &'static str = stringify!($name);\n            const INSTRUCTION: &'static str = stringify!(\"INST - \" + $name);\n            const COST: u8 = 1;\n        }\n    };\n}\n\nmacro_rules! implement_store_numbers_no_conversion {\n    ($name:ident, $num_type:ty, $doc_string:literal) => {\n        #[doc= concat!(\"`\", stringify!($name), \"` implements the `OpCode` Operation for `Opcode::\", stringify!($name), \"`\\n\")]\n        #[doc= \"\\n\"]\n        #[doc=\"Operation:\\n\"]\n        #[doc= concat!(\" - \", $doc_string)]\n        #[derive(Debug, Clone, Copy)]\n        pub(crate) struct $name;\n\n        impl $name {\n            #[inline(always)]\n            pub(crate) fn operation((dst, value): (RegisterOperand, $num_type),  context: &mut Context) {\n                context.vm.set_register(dst.into(), value.into());\n            }\n        }\n\n        impl Operation for $name {\n            const NAME: &'static str = stringify!($name);\n            const INSTRUCTION: &'static str = stringify!(\"INST - \" + $name);\n            const COST: u8 = 1;\n        }\n    };\n}\n\nimplement_store_numbers_with_conversion!(StoreInt8, i8, \"Store `i8` value in dst\");\nimplement_store_numbers_with_conversion!(StoreInt16, i16, \"Store `i16` value in dst\");\n\nimplement_store_numbers_no_conversion!(StoreInt32, i32, \"Store `i32` value in dst\");\nimplement_store_numbers_no_conversion!(StoreFloat, f32, \"Store `f32` value in dst\");\nimplement_store_numbers_no_conversion!(StoreDouble, f64, \"Store `f64` value in dst\");\n"
  },
  {
    "path": "core/engine/src/vm/opcode/push/object.rs",
    "content": "use crate::{\n    Context,\n    builtins::OrdinaryObject,\n    vm::opcode::{Operation, RegisterOperand},\n};\n\n/// `StoreEmptyObject` implements the Opcode Operation for `Opcode::StoreEmptyObject`\n///\n/// Operation:\n///  - Store empty object `{}` in dst.\n#[derive(Debug, Clone, Copy)]\npub(crate) struct StoreEmptyObject;\n\nimpl StoreEmptyObject {\n    #[inline(always)]\n    pub(crate) fn operation(dst: RegisterOperand, context: &mut Context) {\n        let o = context\n            .intrinsics()\n            .templates()\n            .ordinary_object()\n            .create(OrdinaryObject, Vec::default());\n        context.vm.set_register(dst.into(), o.into());\n    }\n}\n\nimpl Operation for StoreEmptyObject {\n    const NAME: &'static str = \"StoreEmptyObject\";\n    const INSTRUCTION: &'static str = \"INST - StoreEmptyObject\";\n    const COST: u8 = 1;\n}\n"
  },
  {
    "path": "core/engine/src/vm/opcode/rest_parameter/mod.rs",
    "content": "use super::RegisterOperand;\nuse crate::{Context, builtins::Array, vm::opcode::Operation};\n\n/// `RestParameterInit` implements the Opcode Operation for `Opcode::RestParameterInit`\n///\n/// Operation:\n///  - Initialize the rest parameter value of a function from the remaining arguments.\n#[derive(Debug, Clone, Copy)]\npub(crate) struct RestParameterInit;\n\nimpl RestParameterInit {\n    #[inline(always)]\n    pub(super) fn operation(dst: RegisterOperand, context: &mut Context) {\n        let array = if let Some(rest) = context\n            .vm\n            .stack\n            .pop_rest_arguments(context.vm.frames.last().expect(\"frame must exist\"))\n        {\n            let rest_count = rest.len() as u32;\n            let array = Array::create_array_from_list(rest, context);\n            context.vm.frame_mut().argument_count -= rest_count;\n            // Adjust rp since draining rest arguments shifted the register slots down.\n            context.vm.frame_mut().rp -= rest_count;\n            array\n        } else {\n            Array::array_create(0, None, context).expect(\"could not create an empty array\")\n        };\n        context.vm.set_register(dst.into(), array.into());\n    }\n}\n\nimpl Operation for RestParameterInit {\n    const NAME: &'static str = \"RestParameterInit\";\n    const INSTRUCTION: &'static str = \"INST - RestParameterInit\";\n    const COST: u8 = 6;\n}\n"
  },
  {
    "path": "core/engine/src/vm/opcode/set/class_prototype.rs",
    "content": "use crate::value::JsVariant;\nuse crate::vm::opcode::RegisterOperand;\nuse crate::{\n    Context,\n    builtins::{OrdinaryObject, function::OrdinaryFunction},\n    object::{CONSTRUCTOR, JsObject, PROTOTYPE, internal_methods::InternalMethodPropertyContext},\n    property::PropertyDescriptorBuilder,\n    vm::opcode::Operation,\n};\n\n/// `SetClassProtoType` implements the Opcode Operation for `Opcode::SetClassPrototype`\n///\n/// Operation:\n///  - Set the prototype of a class object.\n#[derive(Debug, Clone, Copy)]\npub(crate) struct SetClassPrototype;\n\nimpl SetClassPrototype {\n    #[inline(always)]\n    pub(crate) fn operation(\n        (dst, prototype, class): (RegisterOperand, RegisterOperand, RegisterOperand),\n        context: &mut Context,\n    ) {\n        let prototype = context.vm.get_register(prototype.into());\n        let prototype = match prototype.variant() {\n            JsVariant::Object(proto) => Some(proto.clone()),\n            JsVariant::Null => None,\n            JsVariant::Undefined => Some(context.intrinsics().constructors().object().prototype()),\n            _ => unreachable!(),\n        };\n\n        // 9.Let proto be OrdinaryObjectCreate(protoParent).\n        let proto = JsObject::from_proto_and_data_with_shared_shape(\n            context.root_shape(),\n            prototype,\n            OrdinaryObject,\n        )\n        .upcast();\n        let class = context.vm.get_register(class.into()).clone();\n\n        {\n            let class_object = class.as_object().expect(\"class must be object\");\n            class_object\n                .define_property_or_throw(\n                    PROTOTYPE,\n                    PropertyDescriptorBuilder::new()\n                        .value(proto.clone())\n                        .writable(false)\n                        .enumerable(false)\n                        .configurable(false),\n                    context,\n                )\n                .expect(\"cannot fail per spec\");\n            class_object\n                .downcast_mut::<OrdinaryFunction>()\n                .expect(\"class must be function object\")\n                .set_home_object(proto.clone());\n        }\n\n        proto\n            .__define_own_property__(\n                &CONSTRUCTOR.into(),\n                PropertyDescriptorBuilder::new()\n                    .value(class.clone())\n                    .writable(true)\n                    .enumerable(false)\n                    .configurable(true)\n                    .build(),\n                &mut InternalMethodPropertyContext::new(context),\n            )\n            .expect(\"cannot fail per spec\");\n\n        context.vm.set_register(dst.into(), proto.into());\n    }\n}\n\nimpl Operation for SetClassPrototype {\n    const NAME: &'static str = \"SetClassPrototype\";\n    const INSTRUCTION: &'static str = \"INST - SetClassPrototype\";\n    const COST: u8 = 6;\n}\n"
  },
  {
    "path": "core/engine/src/vm/opcode/set/mod.rs",
    "content": "pub(crate) mod class_prototype;\npub(crate) mod name;\npub(crate) mod private;\npub(crate) mod property;\n\npub(crate) use class_prototype::*;\npub(crate) use name::*;\npub(crate) use private::*;\npub(crate) use property::*;\n"
  },
  {
    "path": "core/engine/src/vm/opcode/set/name.rs",
    "content": "use boa_ast::scope::{BindingLocator, BindingLocatorScope};\n\nuse crate::{\n    Context, JsError, JsNativeError, JsResult,\n    environments::Environment,\n    vm::opcode::{IndexOperand, Operation, RegisterOperand},\n};\n\n/// `ThrowMutateImmutable` implements the Opcode Operation for `Opcode::ThrowMutateImmutable`\n///\n/// Operation:\n///  - Throws an error because the binding access is illegal.\n#[derive(Debug, Clone, Copy)]\npub(crate) struct ThrowMutateImmutable;\n\nimpl ThrowMutateImmutable {\n    #[inline(always)]\n    pub(crate) fn operation(index: IndexOperand, context: &mut Context) -> JsError {\n        let name = context\n            .vm\n            .frame()\n            .code_block()\n            .constant_string(index.into());\n\n        JsNativeError::typ()\n            .with_message(format!(\n                \"cannot mutate an immutable binding '{}'\",\n                name.to_std_string_escaped()\n            ))\n            .into()\n    }\n}\n\nimpl Operation for ThrowMutateImmutable {\n    const NAME: &'static str = \"ThrowMutateImmutable\";\n    const INSTRUCTION: &'static str = \"INST - ThrowMutateImmutable\";\n    const COST: u8 = 2;\n}\n\n/// `SetName` implements the Opcode Operation for `Opcode::SetName`\n///\n/// Operation:\n///  - Find a binding on the environment chain and assign its value.\n#[derive(Debug, Clone, Copy)]\npub(crate) struct SetName;\n\nimpl SetName {\n    #[inline(always)]\n    pub(crate) fn operation(\n        (value, index): (RegisterOperand, IndexOperand),\n        context: &mut Context,\n    ) -> JsResult<()> {\n        let value = context.vm.get_register(value.into()).clone();\n        let code_block = context.vm.frame().code_block();\n        let mut binding_locator = code_block.bindings[usize::from(index)].clone();\n        let strict = code_block.strict();\n\n        context.find_runtime_binding(&mut binding_locator)?;\n\n        verify_initialized(&binding_locator, context)?;\n\n        context.set_binding(&binding_locator, value.clone(), strict)?;\n\n        Ok(())\n    }\n}\n\nimpl Operation for SetName {\n    const NAME: &'static str = \"SetName\";\n    const INSTRUCTION: &'static str = \"INST - SetName\";\n    const COST: u8 = 4;\n}\n\n/// `SetNameByLocator` implements the Opcode Operation for `Opcode::SetNameByLocator`\n///\n/// Operation:\n///  - Assigns a value to the binding pointed by the `current_binding` of the current frame.\n#[derive(Debug, Clone, Copy)]\npub(crate) struct SetNameByLocator;\n\nimpl SetNameByLocator {\n    #[inline(always)]\n    pub(crate) fn operation(value: RegisterOperand, context: &mut Context) -> JsResult<()> {\n        let frame = context.vm.frame_mut();\n        let strict = frame.code_block.strict();\n        let binding_locator = frame\n            .binding_stack\n            .pop()\n            .expect(\"locator should have been popped before\");\n        let value = context.vm.get_register(value.into()).clone();\n\n        verify_initialized(&binding_locator, context)?;\n\n        context.set_binding(&binding_locator, value.clone(), strict)?;\n\n        Ok(())\n    }\n}\n\nimpl Operation for SetNameByLocator {\n    const NAME: &'static str = \"SetNameByLocator\";\n    const INSTRUCTION: &'static str = \"INST - SetNameByLocator\";\n    const COST: u8 = 4;\n}\n\n/// Checks that the binding pointed by `locator` exists and is initialized.\nfn verify_initialized(locator: &BindingLocator, context: &mut Context) -> JsResult<()> {\n    if !context.is_initialized_binding(locator)? {\n        let key = locator.name();\n        let strict = context.vm.frame().code_block.strict();\n\n        let message = match locator.scope() {\n            BindingLocatorScope::GlobalObject if strict => Some(format!(\n                \"cannot assign to uninitialized global property `{}`\",\n                key.to_std_string_escaped()\n            )),\n            BindingLocatorScope::GlobalObject => None,\n            BindingLocatorScope::GlobalDeclarative => Some(format!(\n                \"cannot assign to uninitialized binding `{}`\",\n                key.to_std_string_escaped()\n            )),\n            BindingLocatorScope::Stack(index) => match context.environment_expect(index) {\n                Environment::Declarative(_) => Some(format!(\n                    \"cannot assign to uninitialized binding `{}`\",\n                    key.to_std_string_escaped()\n                )),\n                Environment::Object(_) if strict => Some(format!(\n                    \"cannot assign to uninitialized property `{}`\",\n                    key.to_std_string_escaped()\n                )),\n                Environment::Object(_) => None,\n            },\n        };\n\n        if let Some(message) = message {\n            return Err(JsNativeError::reference().with_message(message).into());\n        }\n    }\n\n    Ok(())\n}\n"
  },
  {
    "path": "core/engine/src/vm/opcode/set/private.rs",
    "content": "use crate::{\n    Context, JsResult, js_str, js_string,\n    object::PrivateElement,\n    property::PropertyDescriptor,\n    vm::opcode::{IndexOperand, Operation, RegisterOperand},\n};\n\n/// `SetPrivateField` implements the Opcode Operation for `Opcode::SetPrivateField`\n///\n/// Operation:\n///  - Assign the value of a private property of an object by it's name.\n#[derive(Debug, Clone, Copy)]\npub(crate) struct SetPrivateField;\n\nimpl SetPrivateField {\n    #[inline(always)]\n    pub(crate) fn operation(\n        (value, object, index): (RegisterOperand, RegisterOperand, IndexOperand),\n        context: &mut Context,\n    ) -> JsResult<()> {\n        let name = context\n            .vm\n            .frame()\n            .code_block()\n            .constant_string(index.into());\n        let value = context.vm.get_register(value.into()).clone();\n        let object = context.vm.get_register(object.into()).clone();\n        let base_obj = object.to_object(context)?;\n        let name = context\n            .vm\n            .frame()\n            .environments\n            .resolve_private_identifier(name)\n            .expect(\"private name must be in environment\");\n\n        base_obj.private_set(&name, value.clone(), context)?;\n        Ok(())\n    }\n}\n\nimpl Operation for SetPrivateField {\n    const NAME: &'static str = \"SetPrivateField\";\n    const INSTRUCTION: &'static str = \"INST - SetPrivateField\";\n    const COST: u8 = 4;\n}\n\n/// `DefinePrivateField` implements the Opcode Operation for `Opcode::DefinePrivateField`\n///\n/// Operation:\n///  - Set a private property of a class constructor by it's name.\n#[derive(Debug, Clone, Copy)]\npub(crate) struct DefinePrivateField;\n\nimpl DefinePrivateField {\n    #[inline(always)]\n    pub(crate) fn operation(\n        (object, value, index): (RegisterOperand, RegisterOperand, IndexOperand),\n        context: &mut Context,\n    ) -> JsResult<()> {\n        let object = context.vm.get_register(object.into()).clone();\n        let value = context.vm.get_register(value.into()).clone();\n        let name = context\n            .vm\n            .frame()\n            .code_block()\n            .constant_string(index.into());\n\n        let object = object\n            .as_object()\n            .expect(\"class prototype must be an object\");\n\n        let name = object.private_name(name);\n        object.private_field_add(&name, value, context)?;\n        Ok(())\n    }\n}\n\nimpl Operation for DefinePrivateField {\n    const NAME: &'static str = \"DefinePrivateField\";\n    const INSTRUCTION: &'static str = \"INST - DefinePrivateField\";\n    const COST: u8 = 4;\n}\n\n/// `SetPrivateMethod` implements the Opcode Operation for `Opcode::SetPrivateMethod`\n///\n/// Operation:\n///  - Set a private method of a class constructor by it's name.\n#[derive(Debug, Clone, Copy)]\npub(crate) struct SetPrivateMethod;\n\nimpl SetPrivateMethod {\n    #[inline(always)]\n    pub(crate) fn operation(\n        (object, value, index): (RegisterOperand, RegisterOperand, IndexOperand),\n        context: &mut Context,\n    ) {\n        let object = context.vm.get_register(object.into()).clone();\n        let value = context.vm.get_register(value.into()).clone();\n        let name = context\n            .vm\n            .frame()\n            .code_block()\n            .constant_string(index.into());\n\n        let value = value.as_callable().expect(\"method must be callable\");\n        let object = object\n            .as_object()\n            .expect(\"class prototype must be an object\");\n\n        let name_string = js_string!(js_str!(\"#\"), &name);\n        let desc = PropertyDescriptor::builder()\n            .value(name_string)\n            .writable(false)\n            .enumerable(false)\n            .configurable(true)\n            .build();\n        value\n            .__define_own_property__(&js_string!(\"name\").into(), desc, &mut context.into())\n            .expect(\"failed to set name property on private method\");\n\n        object.borrow_mut().append_private_element(\n            object.private_name(name),\n            PrivateElement::Method(value.clone()),\n        );\n    }\n}\n\nimpl Operation for SetPrivateMethod {\n    const NAME: &'static str = \"SetPrivateMethod\";\n    const INSTRUCTION: &'static str = \"INST - SetPrivateMethod\";\n    const COST: u8 = 4;\n}\n\n/// `SetPrivateSetter` implements the Opcode Operation for `Opcode::SetPrivateSetter`\n///\n/// Operation:\n///  - Set a private setter property of a class constructor by it's name.\n#[derive(Debug, Clone, Copy)]\npub(crate) struct SetPrivateSetter;\n\nimpl SetPrivateSetter {\n    #[inline(always)]\n    pub(crate) fn operation(\n        (object, value, index): (RegisterOperand, RegisterOperand, IndexOperand),\n        context: &mut Context,\n    ) {\n        let object = context.vm.get_register(object.into());\n        let value = context.vm.get_register(value.into());\n        let name = context\n            .vm\n            .frame()\n            .code_block()\n            .constant_string(index.into());\n\n        let value = value.as_callable().expect(\"setter must be callable\");\n        let object = object\n            .as_object()\n            .expect(\"class prototype must be an object\");\n\n        object.borrow_mut().append_private_element(\n            object.private_name(name),\n            PrivateElement::Accessor {\n                getter: None,\n                setter: Some(value.clone()),\n            },\n        );\n    }\n}\n\nimpl Operation for SetPrivateSetter {\n    const NAME: &'static str = \"SetPrivateSetter\";\n    const INSTRUCTION: &'static str = \"INST - SetPrivateSetter\";\n    const COST: u8 = 4;\n}\n\n/// `SetPrivateGetter` implements the Opcode Operation for `Opcode::SetPrivateGetter`\n///\n/// Operation:\n///  - Set a private getter property of a class constructor by it's name.\n#[derive(Debug, Clone, Copy)]\npub(crate) struct SetPrivateGetter;\n\nimpl SetPrivateGetter {\n    #[inline(always)]\n    pub(crate) fn operation(\n        (object, value, index): (RegisterOperand, RegisterOperand, IndexOperand),\n        context: &mut Context,\n    ) {\n        let object = context.vm.get_register(object.into());\n        let value = context.vm.get_register(value.into());\n        let name = context\n            .vm\n            .frame()\n            .code_block()\n            .constant_string(index.into());\n\n        let value = value.as_callable().expect(\"getter must be callable\");\n        let object = object\n            .as_object()\n            .expect(\"class prototype must be an object\");\n\n        object.borrow_mut().append_private_element(\n            object.private_name(name),\n            PrivateElement::Accessor {\n                getter: Some(value.clone()),\n                setter: None,\n            },\n        );\n    }\n}\n\nimpl Operation for SetPrivateGetter {\n    const NAME: &'static str = \"SetPrivateGetter\";\n    const INSTRUCTION: &'static str = \"INST - SetPrivateGetter\";\n    const COST: u8 = 4;\n}\n"
  },
  {
    "path": "core/engine/src/vm/opcode/set/property.rs",
    "content": "use crate::JsExpect;\nuse crate::JsValue;\nuse crate::value::JsVariant;\nuse crate::vm::opcode::{IndexOperand, RegisterOperand};\nuse crate::{\n    Context, JsNativeError, JsResult,\n    builtins::function::set_function_name,\n    object::{internal_methods::InternalMethodPropertyContext, shape::slot::SlotAttributes},\n    property::{PropertyDescriptor, PropertyKey},\n    vm::opcode::Operation,\n};\nuse boa_macros::js_str;\n\nfn set_by_name(\n    value: RegisterOperand,\n    value_object: &JsValue,\n    receiver: &JsValue,\n    index: IndexOperand,\n    context: &mut Context,\n) -> JsResult<()> {\n    let value = context.vm.get_register(value.into()).clone();\n\n    let object = value_object.to_object(context)?;\n\n    let ic = &context.vm.frame().code_block().ic[usize::from(index)];\n\n    let object_borrowed = object.borrow();\n    if let Some((shape, slot)) = ic.get(object_borrowed.shape()) {\n        let slot_index = slot.index as usize;\n\n        if slot.attributes.is_accessor_descriptor() {\n            let result = if slot.attributes.contains(SlotAttributes::PROTOTYPE) {\n                let prototype = shape.prototype().expect(\"prototype should have value\");\n                let prototype = prototype.borrow();\n\n                prototype.properties().storage[slot_index + 1].clone()\n            } else {\n                object_borrowed.properties().storage[slot_index + 1].clone()\n            };\n\n            drop(object_borrowed);\n            if slot.attributes.has_set() && result.is_object() {\n                result.as_object().expect(\"should contain getter\").call(\n                    receiver,\n                    std::slice::from_ref(&value),\n                    context,\n                )?;\n            }\n        } else if slot.attributes.contains(SlotAttributes::PROTOTYPE) {\n            let prototype = shape.prototype().expect(\"prototype should have value\");\n            let mut prototype = prototype.borrow_mut();\n\n            prototype.properties_mut().storage[slot_index] = value.clone();\n        } else {\n            drop(object_borrowed);\n            let mut object_borrowed = object.borrow_mut();\n            object_borrowed.properties_mut().storage[slot_index] = value.clone();\n        }\n        return Ok(());\n    }\n    drop(object_borrowed);\n\n    let name: PropertyKey = ic.name.clone().into();\n\n    let context = &mut InternalMethodPropertyContext::new(context);\n    let succeeded = object.__set__(name.clone(), value.clone(), receiver.clone(), context)?;\n    if !succeeded && context.vm.frame().code_block.strict() {\n        return Err(JsNativeError::typ()\n            .with_message(format!(\"cannot set non-writable property: {name}\"))\n            .into());\n    }\n\n    // Cache the property.\n    let slot = *context.slot();\n    if succeeded && slot.is_cacheable() {\n        let ic = &context.vm.frame().code_block.ic[usize::from(index)];\n        let object_borrowed = object.borrow();\n        let shape = object_borrowed.shape();\n        ic.set(shape, slot);\n    }\n\n    Ok(())\n}\n\n/// `SetPropertyByName` implements the Opcode Operation for `Opcode::SetPropertyByName`\n///\n/// Operation:\n///  - Sets a property by name of an object.\n#[derive(Debug, Clone, Copy)]\npub(crate) struct SetPropertyByName;\n\nimpl SetPropertyByName {\n    #[inline(always)]\n    pub(crate) fn operation(\n        (value, object, index): (RegisterOperand, RegisterOperand, IndexOperand),\n        context: &mut Context,\n    ) -> JsResult<()> {\n        let object = context.vm.get_register(object.into()).clone();\n        set_by_name(value, &object, &object, index, context)\n    }\n}\n\nimpl Operation for SetPropertyByNameWithThis {\n    const NAME: &'static str = \"SetPropertyByNameWithThis\";\n    const INSTRUCTION: &'static str = \"INST - SetPropertyByNameWithThis\";\n    const COST: u8 = 4;\n}\n\n/// `SetPropertyByNameWithThis` implements the Opcode Operation for `Opcode::SetPropertyByNameWithThis`\n///\n/// Operation:\n///  - Sets a property by name of an object.\n#[derive(Debug, Clone, Copy)]\npub(crate) struct SetPropertyByNameWithThis;\n\nimpl SetPropertyByNameWithThis {\n    #[inline(always)]\n    pub(crate) fn operation(\n        (value, receiver, object, index): (\n            RegisterOperand,\n            RegisterOperand,\n            RegisterOperand,\n            IndexOperand,\n        ),\n        context: &mut Context,\n    ) -> JsResult<()> {\n        let value_object = context.vm.get_register(object.into()).clone();\n        let receiver = context.vm.get_register(receiver.into()).clone();\n        set_by_name(value, &value_object, &receiver, index, context)\n    }\n}\n\nimpl Operation for SetPropertyByName {\n    const NAME: &'static str = \"SetPropertyByName\";\n    const INSTRUCTION: &'static str = \"INST - SetPropertyByName\";\n    const COST: u8 = 4;\n}\n\n/// `SetPropertyByValue` implements the Opcode Operation for `Opcode::SetPropertyByValue`\n///\n/// Operation:\n///  - Sets a property by value of an object.\n#[derive(Debug, Clone, Copy)]\npub(crate) struct SetPropertyByValue;\n\nimpl SetPropertyByValue {\n    #[inline(always)]\n    pub(crate) fn operation(\n        (value, key, receiver, object): (\n            RegisterOperand,\n            RegisterOperand,\n            RegisterOperand,\n            RegisterOperand,\n        ),\n        context: &mut Context,\n    ) -> JsResult<()> {\n        let value = context.vm.get_register(value.into()).clone();\n        let key = context.vm.get_register(key.into()).clone();\n        let receiver = context.vm.get_register(receiver.into()).clone();\n        let object = context.vm.get_register(object.into()).clone();\n        let object = object.to_object(context)?;\n\n        let key = key.to_property_key(context)?;\n\n        // Fast Path:\n        'fast_path: {\n            if object.is_array()\n                && let PropertyKey::Index(index) = &key\n            {\n                let mut object_borrowed = object.borrow_mut();\n\n                // Cannot modify if not extensible.\n                if !object_borrowed.extensible {\n                    break 'fast_path;\n                }\n\n                if object_borrowed\n                    .properties_mut()\n                    .set_dense_property(index.get(), &value)\n                {\n                    return Ok(());\n                }\n            }\n        }\n\n        // Slow path:\n        let succeeded = object.__set__(\n            key.clone(),\n            value.clone(),\n            receiver.clone(),\n            &mut context.into(),\n        )?;\n        if !succeeded && context.vm.frame().code_block.strict() {\n            return Err(JsNativeError::typ()\n                .with_message(format!(\"cannot set non-writable property: {key}\"))\n                .into());\n        }\n\n        Ok(())\n    }\n}\n\nimpl Operation for SetPropertyByValue {\n    const NAME: &'static str = \"SetPropertyByValue\";\n    const INSTRUCTION: &'static str = \"INST - SetPropertyByValue\";\n    const COST: u8 = 4;\n}\n\n/// `SetPropertyGetterByName` implements the Opcode Operation for `Opcode::SetPropertyGetterByName`\n///\n/// Operation:\n///  - Sets a getter property by name of an object.\n#[derive(Debug, Clone, Copy)]\npub(crate) struct SetPropertyGetterByName;\n\nimpl SetPropertyGetterByName {\n    #[inline(always)]\n    pub(crate) fn operation(\n        (object, value, index): (RegisterOperand, RegisterOperand, IndexOperand),\n        context: &mut Context,\n    ) -> JsResult<()> {\n        let object = context.vm.get_register(object.into()).clone();\n        let value = context.vm.get_register(value.into()).clone();\n        let name = context\n            .vm\n            .frame()\n            .code_block()\n            .constant_string(index.into())\n            .into();\n\n        let object = object.to_object(context)?;\n        let set = object\n            .__get_own_property__(&name, &mut InternalMethodPropertyContext::new(context))?\n            .as_ref()\n            .and_then(PropertyDescriptor::set)\n            .cloned();\n        object.__define_own_property__(\n            &name,\n            PropertyDescriptor::builder()\n                .maybe_get(Some(value.clone()))\n                .maybe_set(set)\n                .enumerable(true)\n                .configurable(true)\n                .build(),\n            &mut InternalMethodPropertyContext::new(context),\n        )?;\n        Ok(())\n    }\n}\n\nimpl Operation for SetPropertyGetterByName {\n    const NAME: &'static str = \"SetPropertyGetterByName\";\n    const INSTRUCTION: &'static str = \"INST - SetPropertyGetterByName\";\n    const COST: u8 = 4;\n}\n\n/// `SetPropertyGetterByValue` implements the Opcode Operation for `Opcode::SetPropertyGetterByValue`\n///\n/// Operation:\n///  - Sets a getter property by value of an object.\n#[derive(Debug, Clone, Copy)]\npub(crate) struct SetPropertyGetterByValue;\n\nimpl SetPropertyGetterByValue {\n    #[inline(always)]\n    pub(crate) fn operation(\n        (value, key, object): (RegisterOperand, RegisterOperand, RegisterOperand),\n        context: &mut Context,\n    ) -> JsResult<()> {\n        let value = context.vm.get_register(value.into()).clone();\n        let key = context.vm.get_register(key.into()).clone();\n        let object = context.vm.get_register(object.into()).clone();\n        let object = object.to_object(context)?;\n        let name = key.to_property_key(context)?;\n\n        let set = object\n            .__get_own_property__(&name, &mut InternalMethodPropertyContext::new(context))?\n            .as_ref()\n            .and_then(PropertyDescriptor::set)\n            .cloned();\n        object.__define_own_property__(\n            &name,\n            PropertyDescriptor::builder()\n                .maybe_get(Some(value.clone()))\n                .maybe_set(set)\n                .enumerable(true)\n                .configurable(true)\n                .build(),\n            &mut InternalMethodPropertyContext::new(context),\n        )?;\n        Ok(())\n    }\n}\n\nimpl Operation for SetPropertyGetterByValue {\n    const NAME: &'static str = \"SetPropertyGetterByValue\";\n    const INSTRUCTION: &'static str = \"INST - SetPropertyGetterByValue\";\n    const COST: u8 = 4;\n}\n\n/// `SetPropertySetterByName` implements the Opcode Operation for `Opcode::SetPropertySetterByName`\n///\n/// Operation:\n///  - Sets a setter property by name of an object.\n#[derive(Debug, Clone, Copy)]\npub(crate) struct SetPropertySetterByName;\n\nimpl SetPropertySetterByName {\n    #[inline(always)]\n    pub(crate) fn operation(\n        (object, value, index): (RegisterOperand, RegisterOperand, IndexOperand),\n        context: &mut Context,\n    ) -> JsResult<()> {\n        let object = context.vm.get_register(object.into()).clone();\n        let value = context.vm.get_register(value.into()).clone();\n        let name = context\n            .vm\n            .frame()\n            .code_block()\n            .constant_string(index.into())\n            .into();\n\n        let object = object.to_object(context)?;\n\n        let get = object\n            .__get_own_property__(&name, &mut InternalMethodPropertyContext::new(context))?\n            .as_ref()\n            .and_then(PropertyDescriptor::get)\n            .cloned();\n        object.__define_own_property__(\n            &name,\n            PropertyDescriptor::builder()\n                .maybe_set(Some(value.clone()))\n                .maybe_get(get)\n                .enumerable(true)\n                .configurable(true)\n                .build(),\n            &mut InternalMethodPropertyContext::new(context),\n        )?;\n        Ok(())\n    }\n}\n\nimpl Operation for SetPropertySetterByName {\n    const NAME: &'static str = \"SetPropertySetterByName\";\n    const INSTRUCTION: &'static str = \"INST - SetPropertySetterByName\";\n    const COST: u8 = 4;\n}\n\n/// `SetPropertySetterByValue` implements the Opcode Operation for `Opcode::SetPropertySetterByValue`\n///\n/// Operation:\n///  - Sets a setter property by value of an object.\n#[derive(Debug, Clone, Copy)]\npub(crate) struct SetPropertySetterByValue;\n\nimpl SetPropertySetterByValue {\n    #[inline(always)]\n    pub(crate) fn operation(\n        (value, key, object): (RegisterOperand, RegisterOperand, RegisterOperand),\n        context: &mut Context,\n    ) -> JsResult<()> {\n        let value = context.vm.get_register(value.into()).clone();\n        let key = context.vm.get_register(key.into()).clone();\n        let object = context.vm.get_register(object.into()).clone();\n\n        let object = object.to_object(context)?;\n        let name = key.to_property_key(context)?;\n\n        let get = object\n            .__get_own_property__(&name, &mut InternalMethodPropertyContext::new(context))?\n            .as_ref()\n            .and_then(PropertyDescriptor::get)\n            .cloned();\n        object.__define_own_property__(\n            &name,\n            PropertyDescriptor::builder()\n                .maybe_set(Some(value.clone()))\n                .maybe_get(get)\n                .enumerable(true)\n                .configurable(true)\n                .build(),\n            &mut InternalMethodPropertyContext::new(context),\n        )?;\n        Ok(())\n    }\n}\n\nimpl Operation for SetPropertySetterByValue {\n    const NAME: &'static str = \"SetPropertySetterByValue\";\n    const INSTRUCTION: &'static str = \"INST - SetPropertySetterByValue\";\n    const COST: u8 = 4;\n}\n\n/// `SetFunctionName` implements the Opcode Operation for `Opcode::SetFunctionName`\n///\n/// Operation:\n///  - Sets the name of a function object.\n#[derive(Debug, Clone, Copy)]\npub(crate) struct SetFunctionName;\n\nimpl SetFunctionName {\n    #[inline(always)]\n    pub(crate) fn operation(\n        (function, name, prefix): (RegisterOperand, RegisterOperand, IndexOperand),\n        context: &mut Context,\n    ) -> JsResult<()> {\n        let function = context.vm.get_register(function.into()).clone();\n        let name = context.vm.get_register(name.into()).clone();\n        let name = match name.variant() {\n            JsVariant::String(name) => PropertyKey::from(name.clone()),\n            JsVariant::Symbol(name) => PropertyKey::from(name.clone()),\n            _ => unreachable!(),\n        };\n\n        let prefix = match u32::from(prefix) {\n            1 => Some(js_str!(\"get\")),\n            2 => Some(js_str!(\"set\")),\n            _ => None,\n        };\n\n        set_function_name(\n            &function\n                .as_object()\n                .js_expect(\"function is not an object\")?,\n            &name,\n            prefix,\n            context,\n        )?;\n        Ok(())\n    }\n}\n\nimpl Operation for SetFunctionName {\n    const NAME: &'static str = \"SetFunctionName\";\n    const INSTRUCTION: &'static str = \"INST - SetFunctionName\";\n    const COST: u8 = 4;\n}\n"
  },
  {
    "path": "core/engine/src/vm/opcode/switch/mod.rs",
    "content": "use super::RegisterOperand;\nuse crate::{\n    Context,\n    vm::opcode::{Address, Operation},\n};\n\n/// `Case` implements the Opcode Operation for `Opcode::Case`\n///\n/// Operation:\n///  - Pop the two values of the stack, strict equal compares the two values,\n///    if true jumps to address, otherwise push the second pop'ed value.\n#[derive(Debug, Clone, Copy)]\npub(crate) struct Case;\n\nimpl Case {\n    #[inline(always)]\n    pub(super) fn operation(\n        (address, value, condition): (Address, RegisterOperand, RegisterOperand),\n        context: &mut Context,\n    ) {\n        let value = context.vm.get_register(value.into());\n        let condition = context.vm.get_register(condition.into());\n        if value.strict_equals(condition) {\n            context.vm.frame_mut().pc = u32::from(address);\n        }\n    }\n}\n\nimpl Operation for Case {\n    const NAME: &'static str = \"Case\";\n    const INSTRUCTION: &'static str = \"INST - Case\";\n    const COST: u8 = 2;\n}\n"
  },
  {
    "path": "core/engine/src/vm/opcode/templates/mod.rs",
    "content": "use super::RegisterOperand;\nuse crate::{\n    Context,\n    builtins::array::Array,\n    js_string,\n    object::IntegrityLevel,\n    property::PropertyDescriptor,\n    vm::opcode::{Address, Operation},\n};\nuse thin_vec::ThinVec;\n\n/// `TemplateLookup` implements the Opcode Operation for `Opcode::TemplateLookup`\n///\n/// Operation:\n///  - Lookup if a tagged template object is cached and skip the creation if it is.\n#[derive(Debug, Clone, Copy)]\npub(crate) struct TemplateLookup;\n\nimpl TemplateLookup {\n    #[inline(always)]\n    pub(super) fn operation(\n        (jump, site, dst): (Address, u64, RegisterOperand),\n        context: &mut Context,\n    ) {\n        if let Some(template) = context.realm().lookup_template(site) {\n            context.vm.set_register(dst.into(), template.into());\n            context.vm.frame_mut().pc = u32::from(jump);\n        }\n    }\n}\n\nimpl Operation for TemplateLookup {\n    const NAME: &'static str = \"TemplateLookup\";\n    const INSTRUCTION: &'static str = \"INST - TemplateLookup\";\n    const COST: u8 = 3;\n}\n\n/// `TemplateCreate` implements the Opcode Operation for `Opcode::TemplateCreate`\n///\n/// Operation:\n///  - Create a new tagged template object and cache it.\n#[derive(Debug, Clone, Copy)]\npub(crate) struct TemplateCreate;\n\nimpl TemplateCreate {\n    #[inline(always)]\n    pub(super) fn operation(\n        (site, dst, values): (u64, RegisterOperand, ThinVec<u32>),\n        context: &mut Context,\n    ) {\n        let count = values.len() / 2;\n        let template =\n            Array::array_create(count as u64, None, context).expect(\"cannot fail per spec\");\n        let raw_obj =\n            Array::array_create(count as u64, None, context).expect(\"cannot fail per spec\");\n\n        let mut index = 0;\n        let mut cooked = true;\n        for value in values {\n            if cooked {\n                let cooked_value = context.vm.get_register(value as usize);\n                template\n                    .define_property_or_throw(\n                        index,\n                        PropertyDescriptor::builder()\n                            .value(cooked_value.clone())\n                            .writable(false)\n                            .enumerable(true)\n                            .configurable(false),\n                        context,\n                    )\n                    .expect(\"should not fail on new array\");\n            } else {\n                let raw_value = context.vm.get_register(value as usize);\n                raw_obj\n                    .define_property_or_throw(\n                        index,\n                        PropertyDescriptor::builder()\n                            .value(raw_value.clone())\n                            .writable(false)\n                            .enumerable(true)\n                            .configurable(false),\n                        context,\n                    )\n                    .expect(\"should not fail on new array\");\n                index += 1;\n            }\n\n            cooked = !cooked;\n        }\n\n        raw_obj\n            .set_integrity_level(IntegrityLevel::Frozen, context)\n            .expect(\"should never fail per spec\");\n        template\n            .define_property_or_throw(\n                js_string!(\"raw\"),\n                PropertyDescriptor::builder()\n                    .value(raw_obj)\n                    .writable(false)\n                    .enumerable(false)\n                    .configurable(false),\n                context,\n            )\n            .expect(\"should never fail per spec\");\n        template\n            .set_integrity_level(IntegrityLevel::Frozen, context)\n            .expect(\"should never fail per spec\");\n\n        context.realm().push_template(site, template.clone());\n\n        context.vm.set_register(dst.into(), template.into());\n    }\n}\n\nimpl Operation for TemplateCreate {\n    const NAME: &'static str = \"TemplateCreate\";\n    const INSTRUCTION: &'static str = \"INST - TemplateCreate\";\n    const COST: u8 = 6;\n}\n"
  },
  {
    "path": "core/engine/src/vm/opcode/to/mod.rs",
    "content": "use super::RegisterOperand;\nuse crate::{Context, JsResult, vm::opcode::Operation};\n\n/// `ToPropertyKey` implements the Opcode Operation for `Opcode::ToPropertyKey`\n///\n/// Operation:\n///  - Call `ToPropertyKey` on the value on the stack.\n#[derive(Debug, Clone, Copy)]\npub(crate) struct ToPropertyKey;\n\nimpl ToPropertyKey {\n    #[inline(always)]\n    pub(super) fn operation(\n        (value, dst): (RegisterOperand, RegisterOperand),\n        context: &mut Context,\n    ) -> JsResult<()> {\n        let value = context.vm.get_register(value.into()).clone();\n        let key = value.to_property_key(context)?;\n        context.vm.set_register(dst.into(), key.into());\n        Ok(())\n    }\n}\n\nimpl Operation for ToPropertyKey {\n    const NAME: &'static str = \"ToPropertyKey\";\n    const INSTRUCTION: &'static str = \"INST - ToPropertyKey\";\n    const COST: u8 = 2;\n}\n"
  },
  {
    "path": "core/engine/src/vm/opcode/unary_ops/decrement.rs",
    "content": "use crate::{\n    Context, JsBigInt, JsResult,\n    value::{JsValue, JsVariant, Numeric},\n    vm::opcode::{Operation, RegisterOperand},\n};\n\n/// `Dec` implements the Opcode Operation for `Opcode::Dec`\n///\n/// Operation:\n///  - Unary `--` operator.\n#[derive(Debug, Clone, Copy)]\npub(crate) struct Dec;\n\nimpl Dec {\n    #[inline(always)]\n    pub(crate) fn operation(\n        (dst, src): (RegisterOperand, RegisterOperand),\n        context: &mut Context,\n    ) -> JsResult<()> {\n        let value = context.vm.take_register(src.into());\n\n        let (numeric, value) = match value.variant() {\n            JsVariant::Integer32(number) if number > i32::MIN => {\n                (JsValue::from(number), JsValue::from(number - 1))\n            }\n            _ => match value.to_numeric(context)? {\n                Numeric::Number(number) => (JsValue::from(number), JsValue::from(number - 1f64)),\n                Numeric::BigInt(bigint) => (\n                    JsValue::from(bigint.clone()),\n                    JsValue::from(JsBigInt::sub(&bigint, &JsBigInt::one())),\n                ),\n            },\n        };\n        context.vm.set_register(src.into(), numeric);\n        context.vm.set_register(dst.into(), value);\n        Ok(())\n    }\n}\n\nimpl Operation for Dec {\n    const NAME: &'static str = \"Dec\";\n    const INSTRUCTION: &'static str = \"INST - Dec\";\n    const COST: u8 = 3;\n}\n"
  },
  {
    "path": "core/engine/src/vm/opcode/unary_ops/increment.rs",
    "content": "use crate::{\n    Context, JsBigInt, JsResult,\n    value::{JsValue, JsVariant, Numeric},\n    vm::opcode::{Operation, RegisterOperand},\n};\n\n/// `Inc` implements the Opcode Operation for `Opcode::Inc`\n///\n/// Operation:\n///  - Unary `++` operator.\n#[derive(Debug, Clone, Copy)]\npub(crate) struct Inc;\n\nimpl Inc {\n    #[inline(always)]\n    pub(crate) fn operation(\n        (dst, src): (RegisterOperand, RegisterOperand),\n        context: &mut Context,\n    ) -> JsResult<()> {\n        let value = context.vm.take_register(src.into());\n\n        let (numeric, value) = match value.variant() {\n            JsVariant::Integer32(number) if number < i32::MAX => {\n                (JsValue::from(number), JsValue::from(number + 1))\n            }\n            _ => match value.to_numeric(context)? {\n                Numeric::Number(number) => (JsValue::from(number), JsValue::from(number + 1f64)),\n                Numeric::BigInt(bigint) => (\n                    JsValue::from(bigint.clone()),\n                    JsValue::from(JsBigInt::add(&bigint, &JsBigInt::one())),\n                ),\n            },\n        };\n        context.vm.set_register(src.into(), numeric);\n        context.vm.set_register(dst.into(), value);\n        Ok(())\n    }\n}\n\nimpl Operation for Inc {\n    const NAME: &'static str = \"Inc\";\n    const INSTRUCTION: &'static str = \"INST - Inc\";\n    const COST: u8 = 3;\n}\n"
  },
  {
    "path": "core/engine/src/vm/opcode/unary_ops/logical.rs",
    "content": "use crate::{\n    Context,\n    vm::opcode::{Operation, RegisterOperand},\n};\n\n/// `LogicalNot` implements the Opcode Operation for `Opcode::LogicalNot`\n///\n/// Operation:\n///  - Unary logical `!` operator.\n#[derive(Debug, Clone, Copy)]\npub(crate) struct LogicalNot;\n\nimpl LogicalNot {\n    #[inline(always)]\n    pub(crate) fn operation(value: RegisterOperand, context: &mut Context) {\n        context.vm.set_register(\n            value.into(),\n            (!context.vm.get_register(value.into()).to_boolean()).into(),\n        );\n    }\n}\n\nimpl Operation for LogicalNot {\n    const NAME: &'static str = \"LogicalNot\";\n    const INSTRUCTION: &'static str = \"INST - LogicalNot\";\n    const COST: u8 = 1;\n}\n"
  },
  {
    "path": "core/engine/src/vm/opcode/unary_ops/mod.rs",
    "content": "use super::RegisterOperand;\nuse crate::{Context, JsBigInt, JsResult, builtins::Number, value::Numeric, vm::opcode::Operation};\nuse std::ops::Neg as StdNeg;\n\npub(crate) mod decrement;\npub(crate) mod increment;\npub(crate) mod logical;\n\npub(crate) use decrement::*;\npub(crate) use increment::*;\npub(crate) use logical::*;\n\n/// `TypeOf` implements the Opcode Operation for `Opcode::TypeOf`\n///\n/// Operation:\n///  - Unary `typeof` operator.\n#[derive(Debug, Clone, Copy)]\npub(crate) struct TypeOf;\n\nimpl TypeOf {\n    #[inline(always)]\n    pub(super) fn operation(value: RegisterOperand, context: &mut Context) {\n        context.vm.set_register(\n            value.into(),\n            context.vm.get_register(value.into()).js_type_of().into(),\n        );\n    }\n}\n\nimpl Operation for TypeOf {\n    const NAME: &'static str = \"TypeOf\";\n    const INSTRUCTION: &'static str = \"INST - TypeOf\";\n    const COST: u8 = 1;\n}\n\n/// `Pos` implements the Opcode Operation for `Opcode::Pos`\n///\n/// Operation:\n///  - Unary `+` operator.\n#[derive(Debug, Clone, Copy)]\npub(crate) struct Pos;\n\nimpl Pos {\n    #[inline(always)]\n    pub(super) fn operation(value: RegisterOperand, context: &mut Context) -> JsResult<()> {\n        let v = context\n            .vm\n            .get_register(value.into())\n            .clone()\n            .to_number(context)?\n            .into();\n        context.vm.set_register(value.into(), v);\n        Ok(())\n    }\n}\n\nimpl Operation for Pos {\n    const NAME: &'static str = \"Pos\";\n    const INSTRUCTION: &'static str = \"INST - Pos\";\n    const COST: u8 = 3;\n}\n\n/// `Neg` implements the Opcode Operation for `Opcode::Neg`\n///\n/// Operation:\n///  - Unary `-` operator.\n#[derive(Debug, Clone, Copy)]\npub(crate) struct Neg;\n\nimpl Neg {\n    #[inline(always)]\n    pub(super) fn operation(value: RegisterOperand, context: &mut Context) -> JsResult<()> {\n        match context\n            .vm\n            .get_register(value.into())\n            .clone()\n            .to_numeric(context)?\n        {\n            Numeric::Number(number) => context.vm.set_register(value.into(), number.neg().into()),\n            Numeric::BigInt(bigint) => context\n                .vm\n                .set_register(value.into(), JsBigInt::neg(&bigint).into()),\n        }\n        Ok(())\n    }\n}\n\nimpl Operation for Neg {\n    const NAME: &'static str = \"Neg\";\n    const INSTRUCTION: &'static str = \"INST - Neg\";\n    const COST: u8 = 3;\n}\n\n/// `BitNot` implements the Opcode Operation for `Opcode::BitNot`\n///\n/// Operation:\n///  - Unary bitwise `~` operator.\n#[derive(Debug, Clone, Copy)]\npub(crate) struct BitNot;\n\nimpl BitNot {\n    #[inline(always)]\n    pub(super) fn operation(value: RegisterOperand, context: &mut Context) -> JsResult<()> {\n        match context\n            .vm\n            .get_register(value.into())\n            .clone()\n            .to_numeric(context)?\n        {\n            Numeric::Number(number) => context\n                .vm\n                .set_register(value.into(), Number::not(number).into()),\n            Numeric::BigInt(bigint) => context\n                .vm\n                .set_register(value.into(), JsBigInt::not(&bigint).into()),\n        }\n        Ok(())\n    }\n}\n\nimpl Operation for BitNot {\n    const NAME: &'static str = \"BitNot\";\n    const INSTRUCTION: &'static str = \"INST - BitNot\";\n    const COST: u8 = 3;\n}\n"
  },
  {
    "path": "core/engine/src/vm/opcode/value/mod.rs",
    "content": "use super::RegisterOperand;\nuse crate::{Context, JsResult, error::JsNativeError, vm::opcode::Operation};\n\n/// `ValueNotNullOrUndefined` implements the Opcode Operation for `Opcode::ValueNotNullOrUndefined`\n///\n/// Operation:\n///  - Require the stack value to be neither null nor undefined.\n#[derive(Debug, Clone, Copy)]\npub(crate) struct ValueNotNullOrUndefined;\n\nimpl ValueNotNullOrUndefined {\n    #[inline(always)]\n    pub(super) fn operation(value: RegisterOperand, context: &mut Context) -> JsResult<()> {\n        let value = context.vm.get_register(value.into());\n        if value.is_null() {\n            return Err(JsNativeError::typ()\n                .with_message(\"Cannot destructure 'null' value\")\n                .into());\n        }\n        if value.is_undefined() {\n            return Err(JsNativeError::typ()\n                .with_message(\"Cannot destructure 'undefined' value\")\n                .into());\n        }\n        Ok(())\n    }\n}\n\nimpl Operation for ValueNotNullOrUndefined {\n    const NAME: &'static str = \"ValueNotNullOrUndefined\";\n    const INSTRUCTION: &'static str = \"INST - ValueNotNullOrUndefined\";\n    const COST: u8 = 2;\n}\n\n/// `IsObject` implements the Opcode Operation for `Opcode::IsObject`\n///\n/// Operation:\n///  - Store `true` in the register if the value is an object, or `false` otherwise.\n#[derive(Debug, Clone, Copy)]\npub(crate) struct IsObject;\n\nimpl IsObject {\n    #[inline(always)]\n    pub(super) fn operation(value: RegisterOperand, context: &mut Context) {\n        let is_object = context.vm.get_register(value.into()).is_object();\n        context.vm.set_register(value.into(), is_object.into());\n    }\n}\n\nimpl Operation for IsObject {\n    const NAME: &'static str = \"IsObject\";\n    const INSTRUCTION: &'static str = \"INST - IsObject\";\n    const COST: u8 = 1;\n}\n"
  },
  {
    "path": "core/engine/src/vm/runtime_limits.rs",
    "content": "/// Represents the limits of different runtime operations.\n#[derive(Debug, Clone, Copy)]\npub struct RuntimeLimits {\n    /// Max stack size before an error is thrown.\n    stack_size: usize,\n\n    /// Max loop iterations before an error is thrown.\n    loop_iteration: u64,\n\n    /// Max backtrace count in exception.\n    backtrace_limit: usize,\n\n    /// Max function recursion limit\n    recursion: usize,\n}\n\nimpl Default for RuntimeLimits {\n    #[inline]\n    fn default() -> Self {\n        Self {\n            loop_iteration: u64::MAX,\n            recursion: 512,\n            backtrace_limit: 50,\n            stack_size: 1024 * 10,\n        }\n    }\n}\n\nimpl RuntimeLimits {\n    /// Return the loop iteration limit.\n    ///\n    /// If the limit is exceeded in a loop it will throw an error.\n    ///\n    /// The limit value [`u64::MAX`] means that there is no limit.\n    #[inline]\n    #[must_use]\n    pub const fn loop_iteration_limit(&self) -> u64 {\n        self.loop_iteration\n    }\n\n    /// Set the loop iteration limit.\n    ///\n    /// If the limit is exceeded in a loop it will throw an error.\n    ///\n    /// Setting the limit to [`u64::MAX`] means that there is no limit.\n    #[inline]\n    pub fn set_loop_iteration_limit(&mut self, value: u64) {\n        self.loop_iteration = value;\n    }\n\n    /// Disable loop iteration limit.\n    #[inline]\n    pub fn disable_loop_iteration_limit(&mut self) {\n        self.loop_iteration = u64::MAX;\n    }\n\n    /// Get max backtrace limit for an exception.\n    ///\n    /// Default is 50.\n    #[inline]\n    #[must_use]\n    pub const fn backtrace_limit(&self) -> usize {\n        self.backtrace_limit\n    }\n\n    /// Set max backtrace limit for an exception.\n    #[inline]\n    pub fn set_backtrace_limit(&mut self, value: usize) {\n        self.backtrace_limit = value;\n    }\n\n    /// Get max stack size.\n    #[inline]\n    #[must_use]\n    pub const fn stack_size_limit(&self) -> usize {\n        self.stack_size\n    }\n\n    /// Set max stack size before an error is thrown.\n    #[inline]\n    pub fn set_stack_size_limit(&mut self, value: usize) {\n        self.stack_size = value;\n    }\n\n    /// Get recursion limit.\n    #[inline]\n    #[must_use]\n    pub const fn recursion_limit(&self) -> usize {\n        self.recursion\n    }\n\n    /// Set recursion limit before an error is thrown.\n    #[inline]\n    pub fn set_recursion_limit(&mut self, value: usize) {\n        self.recursion = value;\n    }\n}\n"
  },
  {
    "path": "core/engine/src/vm/shadow_stack.rs",
    "content": "use std::fmt::{Display, Write};\n\nuse boa_gc::{Finalize, Trace};\nuse boa_string::JsString;\nuse thin_vec::ThinVec;\n\nuse super::source_info::{NativeSourceInfo, SourceInfo};\n\n#[derive(Debug, Default, Clone, Trace, Finalize)]\npub(crate) struct Backtrace {\n    // SAFETY: Nothing in `ShadowEntry` requires trace, so this is safe.\n    #[unsafe_ignore_trace]\n    stack: ThinVec<ShadowEntry>,\n}\n\nimpl Backtrace {\n    pub(crate) fn iter(&self) -> impl DoubleEndedIterator<Item = &ShadowEntry> {\n        self.stack.iter()\n    }\n}\n\n#[derive(Debug, Clone)]\npub(crate) enum ShadowEntry {\n    Native {\n        function_name: Option<JsString>,\n        source_info: NativeSourceInfo,\n    },\n    Bytecode {\n        pc: u32,\n        source_info: SourceInfo,\n    },\n}\n\nimpl Display for ShadowEntry {\n    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {\n        match self {\n            ShadowEntry::Native {\n                function_name,\n                source_info,\n            } => {\n                if function_name.is_some() || source_info.as_location().is_some() {\n                    f.write_str(\" (native\")?;\n                    if let Some(function_name) = function_name {\n                        write!(f, \" {}\", function_name.to_std_string_escaped())?;\n                    }\n                    if let Some(location) = source_info.as_location() {\n                        write!(f, \" at {location}\")?;\n                    }\n                    f.write_char(')')?;\n                }\n            }\n            ShadowEntry::Bytecode { pc, source_info } => {\n                let path = source_info.map().path();\n                let position = source_info.map().find(*pc);\n\n                if path.is_some() || position.is_some() {\n                    write!(f, \" ({}\", source_info.map().path())?;\n\n                    if let Some(position) = position {\n                        write!(\n                            f,\n                            \":{}:{}\",\n                            position.line_number(),\n                            position.column_number()\n                        )?;\n                    }\n\n                    f.write_char(')')?;\n                }\n            }\n        }\n        Ok(())\n    }\n}\n\n#[derive(Debug, Default, Clone)]\npub(crate) struct ShadowStack {\n    stack: ThinVec<ShadowEntry>,\n}\n\nimpl ShadowStack {\n    pub(crate) fn push_native(\n        &mut self,\n        last_pc: u32,\n        function_name: JsString,\n        native_source_info: NativeSourceInfo,\n    ) {\n        // NOTE: pc points to the next opcode, so we offset by -1 to put it within range.\n        let last_pc = last_pc.saturating_sub(1);\n\n        match self.stack.last_mut() {\n            Some(ShadowEntry::Bytecode { pc, .. }) => *pc = last_pc,\n            Some(ShadowEntry::Native { source_info, .. }) => *source_info = native_source_info,\n            _ => {}\n        }\n        self.stack.push(ShadowEntry::Native {\n            function_name: Some(function_name),\n            source_info: native_source_info,\n        });\n    }\n\n    pub(crate) fn push_bytecode(&mut self, last_pc: u32, source_info: SourceInfo) {\n        // NOTE: pc points to the next opcode, so we offset by -1 to put it within range.\n        let last_pc = last_pc.saturating_sub(1);\n\n        if let Some(ShadowEntry::Bytecode { pc, .. }) = self.stack.last_mut() {\n            *pc = last_pc;\n        }\n        self.stack\n            .push(ShadowEntry::Bytecode { pc: 0, source_info });\n    }\n\n    pub(crate) fn pop(&mut self) -> Option<ShadowEntry> {\n        self.stack.pop()\n    }\n\n    pub(crate) fn take(&self, n: usize, last_pc: u32) -> Backtrace {\n        let mut stack = self\n            .stack\n            .iter()\n            .rev()\n            .take(n)\n            .rev()\n            .cloned()\n            .collect::<ThinVec<_>>();\n\n        if let Some(ShadowEntry::Bytecode { pc, .. }) = stack.last_mut() {\n            // NOTE: pc points to the next opcode, so we offset by -1 to put it within range.\n            *pc = last_pc.saturating_sub(1);\n        }\n        Backtrace { stack }\n    }\n\n    pub(crate) fn caller_position(&self) -> Option<ShadowEntry> {\n        // NOTE: We push the function that is currently execution, so the second last is the caller.\n        let index = self.stack.len().checked_sub(2)?;\n        self.stack.get(index).cloned()\n    }\n\n    #[cfg(feature = \"native-backtrace\")]\n    pub(crate) fn patch_last_native(&mut self, new_source_info: NativeSourceInfo) {\n        let Some(ShadowEntry::Native { source_info, .. }) = self.stack.last_mut() else {\n            return;\n        };\n        *source_info = new_source_info;\n    }\n}\n"
  },
  {
    "path": "core/engine/src/vm/source_info/builder/mod.rs",
    "content": "use std::{cmp::Ordering, ops::Range};\n\nuse boa_ast::Position;\nuse itertools::Itertools;\n\nuse crate::vm::source_info::Entry;\n\n#[cfg(test)]\nmod tests;\n\n#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)]\nstruct EntryRange {\n    start: u32,\n    end: u32,\n    position: Option<Position>,\n}\n\nimpl EntryRange {\n    fn range(&self) -> Range<u32> {\n        self.start..self.end\n    }\n\n    fn is_empty(&self) -> bool {\n        self.range().is_empty()\n    }\n}\n\n#[derive(Debug, Default)]\npub(crate) struct SourceMapBuilder {\n    entries: Vec<EntryRange>,\n    stack: Vec<u32>,\n}\n\nimpl SourceMapBuilder {\n    pub(crate) fn build(self, final_pc: u32) -> Box<[Entry]> {\n        assert!(self.stack.is_empty(), \"forgot to pop source scope\");\n        let end_entry = self\n            .entries\n            .last()\n            .copied()\n            .map(|entry| EntryRange {\n                start: entry.end,\n                end: final_pc,\n                position: None,\n            })\n            .unwrap_or_default();\n\n        self.entries\n            .into_iter()\n            .chain(std::iter::once(end_entry))\n            .filter(|entry| !entry.is_empty())\n            .dedup_by(|a, b| a.position == b.position)\n            .map(|entry| Entry {\n                pc: entry.start,\n                position: entry.position,\n            })\n            .collect::<Box<[_]>>()\n    }\n\n    pub(crate) fn push_source_position(&mut self, start_pc: u32, position: Option<Position>) {\n        let index = self.entries.len() as u32;\n        self.entries.push(EntryRange {\n            start: start_pc,\n            end: u32::MAX,\n            position,\n        });\n        self.stack.push(index);\n    }\n\n    // TODO: document implementation range flattening.\n    pub(crate) fn pop_source_position(&mut self, current_start_pc: u32) {\n        let Some(index) = self.stack.pop().map(|index| index as usize) else {\n            panic!(\"popped more than pushed\");\n        };\n\n        self.entries[index].end = current_start_pc;\n\n        if self.entries[index].range().is_empty() {\n            return;\n        }\n\n        let Some(parent) = self.stack.last().copied().map(|index| index as usize) else {\n            return;\n        };\n\n        let ordering = self.entries[parent].start.cmp(&self.entries[index].start);\n\n        let new_parent_index = match ordering {\n            Ordering::Equal => {\n                self.entries.swap(parent, index);\n                let (parent, index) = (index, parent);\n\n                self.entries[parent].start = self.entries[index].end;\n\n                parent\n            }\n            Ordering::Less => {\n                let old_end = self.entries[parent].end;\n                assert_eq!(old_end, u32::MAX, \"parent end position should not be set\");\n\n                self.entries[parent].end = self.entries[index].start;\n\n                let new_index = self.entries.len();\n                self.entries.push(EntryRange {\n                    start: self.entries[index].end,\n                    end: u32::MAX,\n                    position: self.entries[parent].position,\n                });\n                new_index\n            }\n            Ordering::Greater => {\n                unreachable!(\"Parent source scope cannot be greater than child scope\")\n            }\n        };\n\n        if let Some(parent) = self.stack.last_mut() {\n            *parent = new_parent_index as u32;\n        }\n    }\n}\n"
  },
  {
    "path": "core/engine/src/vm/source_info/builder/tests.rs",
    "content": "use crate::vm::source_info::{\n    Entry,\n    builder::{EntryRange, SourceMapBuilder},\n};\n\nfn entries(mut builder: SourceMapBuilder) -> Vec<EntryRange> {\n    builder.entries.retain(|entry| !entry.is_empty());\n    // println!(\"\\n{:#?}\", builder.entries);\n    builder.entries.clone()\n}\n\n#[test]\nfn empty() {\n    let builder = SourceMapBuilder::default();\n\n    assert_eq!(entries(builder), Vec::<EntryRange>::new());\n}\n\n#[test]\nfn single_source_non_overlapping() {\n    let mut builder = SourceMapBuilder::default();\n    builder.push_source_position(0, Some((1, 1).into()));\n    builder.pop_source_position(10);\n\n    assert_eq!(\n        entries(builder),\n        vec![EntryRange {\n            start: 0,\n            end: 10,\n            position: Some((1, 1).into())\n        }]\n    );\n}\n\n#[test]\nfn single_source_overlapping() {\n    let mut builder = SourceMapBuilder::default();\n    builder.push_source_position(0, Some((1, 1).into()));\n    builder.pop_source_position(0);\n\n    assert_eq!(entries(builder), Vec::<EntryRange>::new());\n}\n\n#[test]\nfn multiple_source_non_overlapping() {\n    let mut builder = SourceMapBuilder::default();\n    builder.push_source_position(0, Some((1, 1).into()));\n    {\n        builder.push_source_position(2, Some((3, 1).into()));\n        builder.pop_source_position(4);\n    }\n    builder.pop_source_position(10);\n\n    assert_eq!(\n        entries(builder),\n        vec![\n            EntryRange {\n                start: 0,\n                end: 2,\n                position: Some((1, 1).into())\n            },\n            EntryRange {\n                start: 2,\n                end: 4,\n                position: Some((3, 1).into())\n            },\n            EntryRange {\n                start: 4,\n                end: 10,\n                position: Some((1, 1).into())\n            },\n        ]\n    );\n}\n\n#[test]\nfn multiple_source_full_overlapping() {\n    let mut builder = SourceMapBuilder::default();\n    builder.push_source_position(0, Some((1, 1).into()));\n    {\n        builder.push_source_position(0, Some((3, 1).into()));\n        builder.pop_source_position(0);\n    }\n    builder.pop_source_position(0);\n\n    assert_eq!(entries(builder), Vec::<EntryRange>::new(),);\n}\n\n#[test]\nfn multiple_source_inner_overlapping() {\n    let mut builder = SourceMapBuilder::default();\n    builder.push_source_position(0, Some((1, 1).into()));\n    {\n        builder.push_source_position(3, Some((3, 1).into()));\n        builder.pop_source_position(3);\n    }\n    builder.pop_source_position(10);\n\n    assert_eq!(\n        entries(builder),\n        vec![EntryRange {\n            start: 0,\n            end: 10,\n            position: Some((1, 1).into())\n        }]\n    );\n}\n\n#[test]\nfn multiple_source_multiple_inner_non_overlapping() {\n    let mut builder = SourceMapBuilder::default();\n    builder.push_source_position(0, Some((1, 1).into()));\n    {\n        builder.push_source_position(3, Some((3, 1).into()));\n        builder.pop_source_position(5);\n\n        builder.push_source_position(5, Some((5, 1).into()));\n        builder.pop_source_position(7);\n    }\n    builder.pop_source_position(10);\n\n    assert_eq!(\n        entries(builder),\n        vec![\n            EntryRange {\n                start: 0,\n                end: 3,\n                position: Some((1, 1).into())\n            },\n            EntryRange {\n                start: 3,\n                end: 5,\n                position: Some((3, 1).into())\n            },\n            EntryRange {\n                start: 5,\n                end: 7,\n                position: Some((5, 1).into())\n            },\n            EntryRange {\n                start: 7,\n                end: 10,\n                position: Some((1, 1).into())\n            }\n        ]\n    );\n}\n\n#[test]\nfn multiple_source_multiple_inner_non_overlapping_with_gap() {\n    let mut builder = SourceMapBuilder::default();\n    builder.push_source_position(0, Some((1, 1).into()));\n    {\n        builder.push_source_position(3, Some((3, 1).into()));\n        builder.pop_source_position(4);\n\n        builder.push_source_position(5, Some((5, 1).into()));\n        builder.pop_source_position(7);\n    }\n    builder.pop_source_position(10);\n\n    assert_eq!(\n        entries(builder),\n        vec![\n            EntryRange {\n                start: 0,\n                end: 3,\n                position: Some((1, 1).into())\n            },\n            EntryRange {\n                start: 3,\n                end: 4,\n                position: Some((3, 1).into())\n            },\n            EntryRange {\n                start: 4,\n                end: 5,\n                position: Some((1, 1).into())\n            },\n            EntryRange {\n                start: 5,\n                end: 7,\n                position: Some((5, 1).into())\n            },\n            EntryRange {\n                start: 7,\n                end: 10,\n                position: Some((1, 1).into())\n            }\n        ]\n    );\n}\n\n#[test]\nfn multiple_source_outer_overlapping() {\n    let mut builder = SourceMapBuilder::default();\n    builder.push_source_position(0, Some((1, 1).into()));\n    {\n        builder.push_source_position(0, Some((3, 1).into()));\n        builder.pop_source_position(10);\n    }\n    builder.pop_source_position(10);\n\n    assert_eq!(\n        entries(builder),\n        vec![EntryRange {\n            start: 0,\n            end: 10,\n            position: Some((3, 1).into())\n        }]\n    );\n}\n\n#[test]\nfn finish_does_not_insert_none_position_if_len_is_equal() {\n    let mut builder = SourceMapBuilder::default();\n    builder.push_source_position(0, Some((1, 1).into()));\n    builder.pop_source_position(10);\n\n    assert_eq!(\n        builder.build(10),\n        vec![Entry {\n            pc: 0,\n            position: Some((1, 1).into()),\n        }]\n        .into()\n    );\n}\n\n#[test]\nfn finish_inserts_none_position_if_len_is_not_equal() {\n    let mut builder = SourceMapBuilder::default();\n    builder.push_source_position(0, Some((1, 1).into()));\n    builder.pop_source_position(10);\n\n    assert_eq!(\n        builder.build(20),\n        vec![\n            Entry {\n                pc: 0,\n                position: Some((1, 1).into()),\n            },\n            Entry {\n                pc: 10,\n                position: None,\n            }\n        ]\n        .into()\n    );\n}\n\n#[test]\nfn finish_full_consecutive_duplicates() {\n    let mut builder = SourceMapBuilder::default();\n    builder.push_source_position(0, Some((1, 1).into()));\n    {\n        builder.push_source_position(2, Some((1, 1).into()));\n        {\n            builder.push_source_position(4, Some((1, 1).into()));\n            builder.pop_source_position(6);\n        }\n        builder.pop_source_position(8);\n    }\n    builder.pop_source_position(10);\n\n    assert_eq!(\n        builder.build(20),\n        vec![\n            Entry {\n                pc: 0,\n                position: Some((1, 1).into()),\n            },\n            Entry {\n                pc: 10,\n                position: None,\n            }\n        ]\n        .into()\n    );\n}\n\n#[test]\nfn finish_outer_consecutive_duplicates() {\n    let mut builder = SourceMapBuilder::default();\n    builder.push_source_position(0, Some((1, 1).into()));\n    {\n        builder.push_source_position(2, Some((1, 1).into()));\n        {\n            builder.push_source_position(4, Some((2, 1).into()));\n            builder.pop_source_position(6);\n        }\n        builder.pop_source_position(8);\n    }\n    builder.pop_source_position(10);\n\n    assert_eq!(\n        builder.build(20),\n        vec![\n            Entry {\n                pc: 0,\n                position: Some((1, 1).into()),\n            },\n            Entry {\n                pc: 4,\n                position: Some((2, 1).into()),\n            },\n            Entry {\n                pc: 6,\n                position: Some((1, 1).into()),\n            },\n            Entry {\n                pc: 10,\n                position: None,\n            }\n        ]\n        .into()\n    );\n}\n\n#[test]\nfn finish_inner_consecutive_duplicates() {\n    let mut builder = SourceMapBuilder::default();\n    builder.push_source_position(0, Some((2, 1).into()));\n    {\n        builder.push_source_position(2, Some((1, 1).into()));\n        {\n            builder.push_source_position(4, Some((1, 1).into()));\n            builder.pop_source_position(6);\n        }\n        builder.pop_source_position(8);\n    }\n    builder.pop_source_position(10);\n\n    assert_eq!(\n        builder.build(20),\n        vec![\n            Entry {\n                pc: 0,\n                position: Some((2, 1).into()),\n            },\n            Entry {\n                pc: 2,\n                position: Some((1, 1).into()),\n            },\n            Entry {\n                pc: 8,\n                position: Some((2, 1).into()),\n            },\n            Entry {\n                pc: 10,\n                position: None,\n            }\n        ]\n        .into()\n    );\n}\n"
  },
  {
    "path": "core/engine/src/vm/source_info/mod.rs",
    "content": "use std::{\n    fmt::Display,\n    path::{Path, PathBuf},\n    rc::Rc,\n};\n\nuse boa_ast::Position;\n\nmod builder;\n\nuse boa_gc::{Finalize, Trace};\nuse boa_string::JsString;\npub(crate) use builder::SourceMapBuilder;\n\nuse crate::SpannedSourceText;\n\n#[cfg(test)]\nmod tests;\n\n/// Source information.\n#[derive(Debug, Default, Clone, Finalize, Trace)]\n// SAFETY: Nothing in Inner needs tracing, so this is safe.\n#[boa_gc(unsafe_empty_trace)]\npub(crate) struct SourceInfo {\n    inner: Rc<Inner>,\n}\n\nimpl SourceInfo {\n    pub(crate) fn new(\n        source_map: SourceMap,\n        function_name: JsString,\n        source_text_spanned: SpannedSourceText,\n    ) -> Self {\n        Self {\n            inner: Rc::new(Inner {\n                map: source_map,\n                function_name,\n                text_spanned: source_text_spanned,\n            }),\n        }\n    }\n\n    pub(crate) fn map(&self) -> &SourceMap {\n        &self.inner.map\n    }\n\n    pub(crate) fn function_name(&self) -> &JsString {\n        &self.inner.function_name\n    }\n\n    pub(crate) fn text_spanned(&self) -> &SpannedSourceText {\n        &self.inner.text_spanned\n    }\n}\n\n#[derive(Debug, Default, Clone)]\nstruct Inner {\n    map: SourceMap,\n    function_name: JsString,\n\n    text_spanned: SpannedSourceText,\n}\n\n/// Bytecode to source code mapping.\n#[derive(Debug, Default, Clone)]\npub(crate) struct SourceMap {\n    entries: Box<[Entry]>,\n    path: SourcePath,\n}\n\nimpl SourceMap {\n    pub(crate) fn new(entries: Box<[Entry]>, path: SourcePath) -> Self {\n        Self { entries, path }\n    }\n\n    pub(crate) fn entries(&self) -> &[Entry] {\n        &self.entries\n    }\n\n    pub(crate) fn find(&self, pc: u32) -> Option<Position> {\n        find_entry(self.entries(), pc)\n    }\n\n    pub(crate) fn path(&self) -> &SourcePath {\n        &self.path\n    }\n}\n\nfn find_entry(entries: &[Entry], pc: u32) -> Option<Position> {\n    let first = entries.first()?;\n\n    if pc < first.pc() {\n        return None;\n    }\n\n    let mut low = 0;\n    let mut high = entries.len() - 1;\n\n    while low <= high {\n        let mid = low.midpoint(high);\n        let entry = &entries[mid];\n        let start = entry.pc;\n\n        let end = entries.get(mid + 1).map_or(u32::MAX, |entry| entry.pc);\n\n        if pc < start {\n            high = mid;\n        } else if pc >= end {\n            low = mid + 1;\n        } else {\n            return entry.position();\n        }\n    }\n\n    // Since the last element defines the start of the end of the range,\n    // therefore we return the last element's position.\n    entries.last().and_then(Entry::position)\n}\n\n// TODO: The line number increments slower than column,\n//       maybe we can take advantage of this, for memory optimization?\n#[derive(Debug, Clone, Copy, PartialEq, Eq)]\npub(crate) struct Entry {\n    /// Represents the start of a bytecode range that falls under the given position.\n    ///\n    /// The end of the range is the pc of the next entry.\n    /// If the entry is the last, the end of the range is [`u32::MAX`].\n    pub(crate) pc: u32,\n\n    /// Source code [`Position`].\n    pub(crate) position: Option<Position>,\n}\n\nimpl Entry {\n    pub(crate) const fn pc(&self) -> u32 {\n        self.pc\n    }\n\n    pub(crate) const fn position(&self) -> Option<Position> {\n        self.position\n    }\n}\n\n/// The Path and type of [`boa_engine::Source`]. This applies to functions and objects.\n#[derive(Debug, Default, Clone, PartialEq, Eq)]\npub enum SourcePath {\n    /// There was no source associated with this call.\n    #[default]\n    None,\n    /// The source is from an `eval()` call.\n    // TODO: Could add more information, like path in which the eval is located.\n    Eval,\n    /// The source is from a `JSON.parse()` call.\n    // TODO: Could add more information, like path in which the JSON.parse is located.\n    Json,\n    /// The source is from a file or module. This contains the [`Path`] of the\n    /// module (e.g. the absolute file path).\n    Path(Rc<Path>),\n}\n\nimpl From<Option<PathBuf>> for SourcePath {\n    fn from(value: Option<PathBuf>) -> Self {\n        match value {\n            None => Self::None,\n            Some(path) => Self::Path(path.into()),\n        }\n    }\n}\n\nimpl From<Option<Rc<Path>>> for SourcePath {\n    fn from(value: Option<Rc<Path>>) -> Self {\n        match value {\n            None => Self::None,\n            Some(path) => Self::Path(path),\n        }\n    }\n}\n\nimpl Display for SourcePath {\n    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {\n        match self {\n            SourcePath::None => f.write_str(\"unknown at \"),\n            SourcePath::Eval => f.write_str(\"eval at \"),\n            SourcePath::Json => f.write_str(\"json at \"),\n            SourcePath::Path(path) => write!(f, \"{}\", path.display()),\n        }\n    }\n}\n\nimpl SourcePath {\n    pub(crate) fn is_none(&self) -> bool {\n        matches!(self, Self::None)\n    }\n\n    pub(crate) fn is_some(&self) -> bool {\n        !self.is_none()\n    }\n}\n\n/// A struct containing information about native source code.\n///\n/// # Note\n///\n/// If the `native-backtrace` feature is not enabled the this becomes [zero sized type][zst].\n///\n/// [zst]: https://doc.rust-lang.org/nomicon/exotic-sizes.html#zero-sized-types-zsts\n#[derive(Debug, Clone, Copy)]\npub struct NativeSourceInfo {\n    #[cfg(feature = \"native-backtrace\")]\n    inner: &'static std::panic::Location<'static>,\n\n    #[cfg(not(feature = \"native-backtrace\"))]\n    inner: std::marker::PhantomData<()>,\n}\n\nimpl NativeSourceInfo {\n    /// Returns the source location of the caller of this function.\n    ///\n    /// If that function’s caller is annotated with `#[track_caller]`, then its call\n    /// location will be returned, and so on up the stack to the first call within\n    /// a non-tracked function body.\n    #[inline]\n    #[must_use]\n    #[cfg_attr(feature = \"native-backtrace\", track_caller)]\n    pub const fn caller() -> Self {\n        Self {\n            #[cfg(feature = \"native-backtrace\")]\n            inner: std::panic::Location::caller(),\n\n            #[cfg(not(feature = \"native-backtrace\"))]\n            inner: std::marker::PhantomData,\n        }\n    }\n\n    /// Return a [`std::panic::Location`].\n    ///\n    /// # Note\n    ///\n    /// If the `native-backtrace` feature is not enabled, then this always returns [`None`].\n    #[inline]\n    #[must_use]\n    pub const fn as_location(self) -> Option<&'static std::panic::Location<'static>> {\n        #[cfg(feature = \"native-backtrace\")]\n        return Some(self.inner);\n\n        #[cfg(not(feature = \"native-backtrace\"))]\n        return None;\n    }\n}\n"
  },
  {
    "path": "core/engine/src/vm/source_info/tests.rs",
    "content": "use crate::vm::source_info::find_entry;\n\nuse super::Entry;\n\n#[test]\nfn find_empty() {\n    let entries = &[];\n\n    assert_eq!(find_entry(entries, 0), None);\n}\n\n#[test]\nfn find_unit_ranges() {\n    let entries = &[\n        Entry {\n            pc: 0,\n            position: Some((1, 1).into()),\n        },\n        Entry {\n            pc: 1,\n            position: Some((2, 1).into()),\n        },\n        Entry {\n            pc: 2,\n            position: Some((3, 1).into()),\n        },\n        Entry {\n            pc: 3,\n            position: Some((4, 1).into()),\n        },\n    ];\n\n    assert_eq!(find_entry(entries, 0), Some((1, 1).into()));\n    assert_eq!(find_entry(entries, 1), Some((2, 1).into()));\n    assert_eq!(find_entry(entries, 2), Some((3, 1).into()));\n    assert_eq!(find_entry(entries, 3), Some((4, 1).into()));\n\n    assert_eq!(find_entry(entries, 4), Some((4, 1).into()));\n    assert_eq!(find_entry(entries, u32::MAX), Some((4, 1).into()));\n}\n\n#[test]\nfn find_before_first_entry() {\n    let entries = &[Entry {\n        pc: 10,\n        position: Some((1, 1).into()),\n    }];\n\n    assert_eq!(find_entry(entries, 0), None);\n    assert_eq!(find_entry(entries, 5), None);\n    assert_eq!(find_entry(entries, 10), Some((1, 1).into()));\n}\n\n#[test]\nfn find_past_last_entry() {\n    let entries = &[Entry {\n        pc: 0,\n        position: Some((1, 1).into()),\n    }];\n\n    assert_eq!(find_entry(entries, 0), Some((1, 1).into()));\n    assert_eq!(find_entry(entries, 10), Some((1, 1).into()));\n    assert_eq!(find_entry(entries, u32::MAX), Some((1, 1).into()));\n}\n\n#[test]\nfn find_wide_spaced_ranges_odd() {\n    let entries = &[\n        Entry {\n            pc: 0,\n            position: Some((1, 1).into()),\n        },\n        Entry {\n            pc: 10,\n            position: Some((2, 1).into()),\n        },\n        Entry {\n            pc: 20,\n            position: Some((3, 1).into()),\n        },\n        Entry {\n            pc: 30,\n            position: Some((4, 1).into()),\n        },\n    ];\n\n    assert_eq!(find_entry(entries, 0), Some((1, 1).into()));\n    assert_eq!(find_entry(entries, 5), Some((1, 1).into()));\n    assert_eq!(find_entry(entries, 9), Some((1, 1).into()));\n\n    assert_eq!(find_entry(entries, 10), Some((2, 1).into()));\n    assert_eq!(find_entry(entries, 15), Some((2, 1).into()));\n    assert_eq!(find_entry(entries, 19), Some((2, 1).into()));\n\n    assert_eq!(find_entry(entries, 20), Some((3, 1).into()));\n    assert_eq!(find_entry(entries, 25), Some((3, 1).into()));\n    assert_eq!(find_entry(entries, 29), Some((3, 1).into()));\n\n    assert_eq!(find_entry(entries, 30), Some((4, 1).into()));\n    assert_eq!(find_entry(entries, 35), Some((4, 1).into()));\n    assert_eq!(find_entry(entries, 39), Some((4, 1).into()));\n}\n\n#[test]\nfn find_wide_spaced_ranges_even() {\n    let entries = &[\n        Entry {\n            pc: 0,\n            position: Some((1, 1).into()),\n        },\n        Entry {\n            pc: 10,\n            position: Some((2, 1).into()),\n        },\n        Entry {\n            pc: 20,\n            position: Some((3, 1).into()),\n        },\n        Entry {\n            pc: 30,\n            position: Some((4, 1).into()),\n        },\n        Entry {\n            pc: 40,\n            position: Some((5, 1).into()),\n        },\n    ];\n\n    assert_eq!(find_entry(entries, 0), Some((1, 1).into()));\n    assert_eq!(find_entry(entries, 5), Some((1, 1).into()));\n    assert_eq!(find_entry(entries, 9), Some((1, 1).into()));\n\n    assert_eq!(find_entry(entries, 10), Some((2, 1).into()));\n    assert_eq!(find_entry(entries, 15), Some((2, 1).into()));\n    assert_eq!(find_entry(entries, 19), Some((2, 1).into()));\n\n    assert_eq!(find_entry(entries, 20), Some((3, 1).into()));\n    assert_eq!(find_entry(entries, 25), Some((3, 1).into()));\n    assert_eq!(find_entry(entries, 29), Some((3, 1).into()));\n\n    assert_eq!(find_entry(entries, 30), Some((4, 1).into()));\n    assert_eq!(find_entry(entries, 35), Some((4, 1).into()));\n    assert_eq!(find_entry(entries, 39), Some((4, 1).into()));\n\n    assert_eq!(find_entry(entries, 40), Some((5, 1).into()));\n    assert_eq!(find_entry(entries, 45), Some((5, 1).into()));\n    assert_eq!(find_entry(entries, 49), Some((5, 1).into()));\n}\n\n#[test]\nfn find_with_single_entry() {\n    let entries = &[\n        Entry {\n            pc: 22,\n            position: Some((1, 1).into()),\n        },\n        Entry {\n            pc: 33,\n            position: None,\n        },\n    ];\n\n    assert_eq!(find_entry(entries, 0), None);\n    assert_eq!(find_entry(entries, 10), None);\n    assert_eq!(find_entry(entries, 21), None);\n\n    assert_eq!(find_entry(entries, 22), Some((1, 1).into()));\n    assert_eq!(find_entry(entries, 30), Some((1, 1).into()));\n    assert_eq!(find_entry(entries, 32), Some((1, 1).into()));\n\n    assert_eq!(find_entry(entries, 33), None);\n    assert_eq!(find_entry(entries, u32::MAX), None);\n}\n"
  },
  {
    "path": "core/engine/src/vm/tests.rs",
    "content": "use crate::error::RuntimeLimitError;\nuse crate::vm::CallFrame;\nuse crate::vm::call_frame::CallFrameLocation;\nuse crate::vm::source_info::SourcePath;\nuse crate::{\n    Context, JsNativeErrorKind, JsValue, NativeFunction, TestAction, js_string,\n    property::Attribute, run_test_actions, run_test_actions_with,\n};\nuse boa_ast::Position;\nuse boa_macros::js_str;\nuse boa_parser::Source;\nuse indoc::indoc;\n\n#[test]\nfn typeof_string() {\n    run_test_actions([TestAction::assert_eq(\n        indoc! {r#\"\n            const a = \"hello\";\n            typeof a;\n        \"#},\n        js_str!(\"string\"),\n    )]);\n}\n\n#[test]\nfn typeof_number() {\n    run_test_actions([TestAction::assert_eq(\n        indoc! {r#\"\n            let a = 1234;\n            typeof a;\n        \"#},\n        js_str!(\"number\"),\n    )]);\n}\n\n#[test]\nfn basic_op() {\n    run_test_actions([TestAction::assert_eq(\n        indoc! {r#\"\n            const a = 1;\n            const b = 2;\n            a + b\n        \"#},\n        3,\n    )]);\n}\n\n#[test]\nfn position() {\n    let context = &mut Context::default();\n    context\n        .register_global_callable(\n            js_string!(\"check_stack\"),\n            2,\n            NativeFunction::from_copy_closure(|_, _, context| {\n                let frame = context.stack_trace().collect::<Vec<&CallFrame>>();\n\n                assert_eq!(frame.len(), 4);\n                assert_eq!(\n                    frame[0].position(),\n                    CallFrameLocation {\n                        function_name: js_string!(\"myOtherFunction\"),\n                        path: SourcePath::None,\n                        position: Some(Position::new(2, 16))\n                    }\n                );\n                assert_eq!(\n                    frame[1].position(),\n                    CallFrameLocation {\n                        function_name: js_string!(\"<eval>\"),\n                        path: SourcePath::Eval,\n                        position: Some(Position::new(1, 16))\n                    }\n                );\n                assert_eq!(\n                    frame[2].position(),\n                    CallFrameLocation {\n                        function_name: js_string!(\"myFunction\"),\n                        path: SourcePath::None,\n                        position: Some(Position::new(5, 9))\n                    }\n                );\n                assert_eq!(\n                    frame[3].position(),\n                    CallFrameLocation {\n                        function_name: js_string!(\"<main>\"),\n                        path: SourcePath::None,\n                        position: Some(Position::new(8, 11))\n                    }\n                );\n                Ok(JsValue::undefined())\n            }),\n        )\n        .expect(\"Could not register function\");\n    run_test_actions_with(\n        [TestAction::run(indoc! {r#\"\n            const myOtherFunction = () => {\n                check_stack();\n            };\n            function myFunction() {\n                eval(\"myOtherFunction()\");\n            }\n\n            myFunction();\n        \"#})],\n        context,\n    );\n}\n\n#[test]\nfn try_catch_finally_from_init() {\n    // the initialisation of the array here emits a PopOnReturnAdd op\n    //\n    // here we test that the stack is not popped more than intended due to multiple catches in the\n    // same function, which could lead to VM stack corruption\n    run_test_actions([TestAction::assert_opaque_error(\n        indoc! {r#\"\n            try {\n                [(() => {throw \"h\";})()];\n            } catch (x) {\n                throw \"h\";\n            } finally {\n            }\n        \"#},\n        js_str!(\"h\"),\n    )]);\n}\n\n#[test]\nfn multiple_catches() {\n    // see explanation on `try_catch_finally_from_init`\n    run_test_actions([TestAction::assert_eq(\n        indoc! {r#\"\n            try {\n                try {\n                    [(() => {throw \"h\";})()];\n                } catch (x) {\n                    throw \"h\";\n                }\n            } catch (y) {\n            }\n        \"#},\n        JsValue::undefined(),\n    )]);\n}\n\n#[test]\nfn use_last_expr_try_block() {\n    run_test_actions([TestAction::assert_eq(\n        indoc! {r#\"\n            try {\n                19;\n                7.5;\n                \"Hello!\";\n            } catch (y) {\n                14;\n                \"Bye!\"\n            }\n        \"#},\n        js_str!(\"Hello!\"),\n    )]);\n}\n\n#[test]\nfn use_last_expr_catch_block() {\n    run_test_actions([TestAction::assert_eq(\n        indoc! {r#\"\n            try {\n                throw Error(\"generic error\");\n                19;\n                7.5;\n            } catch (y) {\n                14;\n                \"Hello!\";\n            }\n        \"#},\n        js_str!(\"Hello!\"),\n    )]);\n}\n\n#[test]\nfn no_use_last_expr_finally_block() {\n    run_test_actions([TestAction::assert_eq(\n        indoc! {r#\"\n            try {\n            } catch (y) {\n            } finally {\n                \"Unused\";\n            }\n        \"#},\n        JsValue::undefined(),\n    )]);\n}\n\n#[test]\nfn finally_block_binding_env() {\n    run_test_actions([TestAction::assert_eq(\n        indoc! {r#\"\n            let buf = \"Hey hey\";\n            try {\n            } catch (y) {\n            } finally {\n                let x = \" people\";\n                buf += x;\n            }\n            buf\n        \"#},\n        js_str!(\"Hey hey people\"),\n    )]);\n}\n\n#[test]\nfn run_super_method_in_object() {\n    run_test_actions([TestAction::assert_eq(\n        indoc! {r#\"\n            let proto = {\n                m() { return \"super\"; }\n            };\n            let obj = {\n                v() { return super.m(); }\n            };\n            Object.setPrototypeOf(obj, proto);\n            obj.v();\n        \"#},\n        js_str!(\"super\"),\n    )]);\n}\n\n#[test]\nfn get_reference_by_super() {\n    run_test_actions([TestAction::assert_eq(\n        indoc! {r#\"\n            var fromA, fromB;\n            var A = { fromA: 'a', fromB: 'a' };\n            var B = { fromB: 'b' };\n            Object.setPrototypeOf(B, A);\n            var obj = {\n                fromA: 'c',\n                fromB: 'c',\n                method() {\n                    fromA = (() => { return super.fromA; })();\n                    fromB = (() => { return super.fromB; })();\n                }\n            };\n            Object.setPrototypeOf(obj, B);\n            obj.method();\n            fromA + fromB\n        \"#},\n        js_str!(\"ab\"),\n    )]);\n}\n\n#[test]\nfn super_call_constructor_null() {\n    run_test_actions([TestAction::assert_native_error(\n        indoc! {r#\"\n            class A extends Object {\n                constructor() {\n                    Object.setPrototypeOf(A, null);\n                    super(A);\n                }\n            }\n            new A();\n        \"#},\n        JsNativeErrorKind::Type,\n        \"super constructor object must be constructor\",\n    )]);\n}\n\n#[test]\nfn super_call_get_constructor_before_arguments_execution() {\n    run_test_actions([TestAction::assert(indoc! {r#\"\n        class A extends Object {\n            constructor() {\n                super(Object.setPrototypeOf(A, null));\n            }\n        }\n        new A() instanceof A;\n    \"#})]);\n}\n\n#[test]\nfn order_of_execution_in_assignment() {\n    run_test_actions([\n        TestAction::run(indoc! {r#\"\n                let i = 0;\n                let array = [[]];\n\n                array[i++][i++] = i++;\n            \"#}),\n        TestAction::assert_eq(\"i\", 3),\n        TestAction::assert_eq(\"array.length\", 1),\n        TestAction::assert_eq(\"array[0].length\", 2),\n    ]);\n}\n\n#[test]\nfn order_of_execution_in_assignment_with_comma_expressions() {\n    run_test_actions([TestAction::assert_eq(\n        indoc! {r#\"\n            let result = \"\";\n            function f(i) {\n                result += i;\n            }\n            let a = [[]];\n            (f(1), a)[(f(2), 0)][(f(3), 0)] = (f(4), 123);\n            result\n        \"#},\n        js_str!(\"1234\"),\n    )]);\n}\n\n#[test]\nfn loop_runtime_limit() {\n    run_test_actions([\n        TestAction::assert_eq(\n            indoc! {r#\"\n                for (let i = 0; i < 20; ++i) { }\n            \"#},\n            JsValue::undefined(),\n        ),\n        TestAction::inspect_context(|context| {\n            context.runtime_limits_mut().set_loop_iteration_limit(10);\n        }),\n        TestAction::assert_runtime_limit_error(\n            indoc! {r#\"\n                for (let i = 0; i < 20; ++i) { }\n            \"#},\n            RuntimeLimitError::LoopIteration,\n        ),\n        TestAction::assert_eq(\n            indoc! {r#\"\n                for (let i = 0; i < 10; ++i) { }\n            \"#},\n            JsValue::undefined(),\n        ),\n        TestAction::assert_runtime_limit_error(\n            indoc! {r#\"\n                while (1) { }\n            \"#},\n            RuntimeLimitError::LoopIteration,\n        ),\n    ]);\n}\n\n#[test]\nfn recursion_runtime_limit() {\n    run_test_actions([\n        TestAction::run(indoc! {r#\"\n            function factorial(n) {\n                if (n == 0) {\n                    return 1;\n                }\n\n                return n * factorial(n - 1);\n            }\n        \"#}),\n        TestAction::assert_eq(\"factorial(8)\", JsValue::new(40_320)),\n        TestAction::assert_eq(\"factorial(11)\", JsValue::new(39_916_800)),\n        TestAction::inspect_context(|context| {\n            context.runtime_limits_mut().set_recursion_limit(10);\n        }),\n        TestAction::assert_runtime_limit_error(\"factorial(11)\", RuntimeLimitError::Recursion),\n        TestAction::assert_eq(\"factorial(8)\", JsValue::new(40_320)),\n        TestAction::assert_runtime_limit_error(\n            indoc! {r#\"\n                function x() {\n                    x()\n                }\n\n                x()\n            \"#},\n            RuntimeLimitError::Recursion,\n        ),\n    ]);\n}\n\n#[test]\nfn arguments_object_constructor_valid_index() {\n    run_test_actions([TestAction::assert_eq(\n        indoc! {r#\"\n            let args;\n            function F(a = 1) {\n                args = arguments;\n            }\n            new F();\n            typeof args\n        \"#},\n        js_str!(\"object\"),\n    )]);\n}\n\n#[test]\nfn empty_return_values() {\n    run_test_actions([\n        TestAction::run(indoc! {r#\"do {{}} while (false);\"#}),\n        TestAction::run(indoc! {r#\"do try {{}} catch {} while (false);\"#}),\n        TestAction::run(indoc! {r#\"do {} while (false);\"#}),\n        TestAction::run(indoc! {r#\"do try {{}{}} catch {} while (false);\"#}),\n        TestAction::run(indoc! {r#\"do {{}{}} while (false);\"#}),\n        TestAction::run(indoc! {r#\"do {;{}} while (false);\"#}),\n        TestAction::run(indoc! {r#\"do {e: {}} while (false);\"#}),\n        TestAction::run(indoc! {r#\"do {e: ;} while (false);\"#}),\n        TestAction::run(indoc! {r#\"do { break } while (false);\"#}),\n        TestAction::run(indoc! {r#\"while (true) a: break\"#}),\n        TestAction::run(indoc! {r#\"while (true) a: {\"a\"; break};\"#}),\n        TestAction::run(indoc! {r#\"do {\"a\";{}} while (false);\"#}),\n        TestAction::run(indoc! {r#\"\n            switch (false) {\n                default: {}\n            }\n        \"#}),\n        TestAction::run(indoc! {r#\"\n            switch (false) {\n                default: {}{}\n            }\n        \"#}),\n        TestAction::run(indoc! {r#\"\n            switch (false) {\n                default: ;{}{}\n            }\n        \"#}),\n    ]);\n}\n\n#[test]\nfn truncate_environments_on_non_caught_native_error() {\n    let source = \"with (new Proxy({}, {has: p => false})) {a}\";\n    run_test_actions([\n        TestAction::assert_native_error(source, JsNativeErrorKind::Reference, \"a is not defined\"),\n        TestAction::assert_native_error(source, JsNativeErrorKind::Reference, \"a is not defined\"),\n    ]);\n}\n\n#[test]\nfn super_construction_with_parameter_expression() {\n    run_test_actions([\n        TestAction::run(indoc! {r#\"\n            class Person {\n                constructor(name) {\n                    this.name = name;\n                }\n            }\n\n            class Student extends Person {\n                constructor(name = 'unknown') {\n                    super(name);\n                }\n            }\n        \"#}),\n        TestAction::assert_eq(\"new Student().name\", js_str!(\"unknown\")),\n        TestAction::assert_eq(\"new Student('Jack').name\", js_str!(\"Jack\")),\n    ]);\n}\n\n#[test]\nfn cross_context_function_call() {\n    let context1 = &mut Context::default();\n    let result = context1.eval(Source::from_bytes(indoc! {r\"\n        var global = 100;\n\n        (function x() {\n            return global;\n        })\n    \"}));\n\n    assert!(result.is_ok());\n    let result = result.unwrap();\n    assert!(result.is_callable());\n\n    let context2 = &mut Context::default();\n\n    context2\n        .register_global_property(js_string!(\"func\"), result, Attribute::all())\n        .unwrap();\n\n    let result = context2.eval(Source::from_bytes(\"func()\"));\n\n    assert_eq!(result, Ok(JsValue::new(100)));\n}\n\n// See: https://github.com/boa-dev/boa/issues/1848\n#[test]\nfn long_object_chain_gc_trace_stack_overflow() {\n    run_test_actions([\n        TestAction::run(indoc! {r#\"\n            let old = {};\n            for (let i = 0; i < 100000; i++) {\n                old = { old };\n            }\n        \"#}),\n        TestAction::inspect_context(|_| boa_gc::force_collect()),\n    ]);\n}\n\n// See: https://github.com/boa-dev/boa/issues/4515\n#[test]\nfn recursion_in_async_gen_throws_uncatchable_error() {\n    run_test_actions([\n        TestAction::inspect_context(|context| {\n            context.runtime_limits_mut().set_recursion_limit(128);\n        }),\n        TestAction::assert_runtime_limit_error(\n            indoc! {r#\"\n                async function* f() {}\n                f().return({\n                  get then() {\n                    this.then;\n                  },\n                });\n            \"#},\n            RuntimeLimitError::Recursion,\n        ),\n    ]);\n}\n\n#[test]\nfn recursion_in_setter_throws_uncatchable_error() {\n    run_test_actions([\n        TestAction::inspect_context(|context| {\n            context.runtime_limits_mut().set_recursion_limit(128);\n        }),\n        TestAction::assert_runtime_limit_error(\n            indoc! {r#\"\n                const obj = {\n                  set x(value) {\n                    this.x = value;\n                  },\n                };\n                obj.x = 1;\n            \"#},\n            RuntimeLimitError::Recursion,\n        ),\n    ]);\n}\n"
  },
  {
    "path": "core/engine/tests/assets/dir1/file1_1.js",
    "content": "import { file1_2 } from \"./file1_2.js\";\n\nexport function file1_1() {\n  return \"file1_1\" + \".\" + file1_2();\n}\n"
  },
  {
    "path": "core/engine/tests/assets/dir1/file1_2.js",
    "content": "export function file1_2() {\n  return \"file1_2\";\n}\n"
  },
  {
    "path": "core/engine/tests/assets/file1.js",
    "content": "import { file1_1 } from \"./dir1/file1_1.js\";\n\nexport function file1() {\n  return \"file1\" + \"..\" + file1_1();\n}\n"
  },
  {
    "path": "core/engine/tests/assets/gcd.js",
    "content": "/**\n * Calculate the greatest common divisor of two numbers.\n * @param {number} a\n * @param {number} b\n * @returns {number|*} The greatest common divisor of {a} and {b}.\n * @throws {TypeError} If either {a} or {b} is not finite.\n */\nexport function gcd(a, b) {\n  a = +a;\n  b = +b;\n  if (!Number.isFinite(a) || !Number.isFinite(b)) {\n    throw new TypeError(\"Invalid input\");\n  }\n\n  // Euclidean algorithm\n  function inner_gcd(a, b) {\n    while (b !== 0) {\n      let t = b;\n      b = a % b;\n      a = t;\n    }\n    return a;\n  }\n\n  return inner_gcd(a, b);\n}\n"
  },
  {
    "path": "core/engine/tests/gcd.rs",
    "content": "#![allow(unused_crate_dependencies)]\n//! A test that mimics the GCD example from wasmtime.\n//! See: <https://docs.wasmtime.dev/examples-rust-gcd.html#gcdrs>.\n//! This is a good point to discuss and improve on the usability\n//! of the [`boa_engine`] API.\n\n// You can execute this example with `cargo run --example gcd`\n\nuse boa_engine::{Context, Module, js_string};\nuse boa_parser::Source;\nuse std::path::PathBuf;\n\n#[test]\nfn gcd() {\n    let assets_dir =\n        PathBuf::from(std::env::var(\"CARGO_MANIFEST_DIR\").unwrap()).join(\"tests/assets\");\n\n    // Create the engine.\n    let context = &mut Context::default();\n\n    // Load the JavaScript code.\n    let gcd_path = assets_dir.join(\"gcd.js\");\n    let source = Source::from_filepath(&gcd_path).unwrap();\n    let module = Module::parse(source, None, context).unwrap();\n    module\n        .load_link_evaluate(context)\n        .await_blocking(context)\n        .unwrap();\n\n    let js_gcd = module\n        .get_typed_fn::<(i32, i32), i32>(js_string!(\"gcd\"), context)\n        .unwrap();\n\n    assert_eq!(js_gcd.call(context, (6, 9)), Ok(3));\n    assert_eq!(js_gcd.call(context, (9, 6)), Ok(3));\n}\n"
  },
  {
    "path": "core/engine/tests/imports.rs",
    "content": "#![allow(unused_crate_dependencies, missing_docs)]\n\nuse std::path::PathBuf;\nuse std::rc::Rc;\n\nuse boa_engine::builtins::promise::PromiseState;\nuse boa_engine::module::SimpleModuleLoader;\nuse boa_engine::{Context, JsValue, Source, js_string};\n\n/// Test that relative imports work with the simple module loader.\n#[test]\nfn subdirectories() {\n    let assets_dir =\n        PathBuf::from(std::env::var(\"CARGO_MANIFEST_DIR\").unwrap()).join(\"tests/assets\");\n\n    let loader = Rc::new(SimpleModuleLoader::new(assets_dir).unwrap());\n    let mut context = Context::builder()\n        .module_loader(loader.clone())\n        .build()\n        .unwrap();\n\n    let source = Source::from_bytes(b\"export { file1 } from 'file1.js';\");\n    let module = boa_engine::Module::parse(source, None, &mut context).unwrap();\n    let result = module.load_link_evaluate(&mut context);\n\n    context.run_jobs().unwrap();\n    match result.state() {\n        PromiseState::Pending => {}\n        PromiseState::Fulfilled(v) => {\n            assert!(v.is_undefined());\n\n            let foo_value = module\n                .namespace(&mut context)\n                .get(js_string!(\"file1\"), &mut context)\n                .unwrap()\n                .as_callable()\n                .unwrap()\n                .call(&JsValue::undefined(), &[], &mut context)\n                .unwrap();\n\n            assert_eq!(foo_value, js_string!(\"file1..file1_1.file1_2\").into());\n        }\n        PromiseState::Rejected(reason) => {\n            panic!(\"Module failed to load: {}\", reason.display());\n        }\n    }\n}\n"
  },
  {
    "path": "core/engine/tests/macros.rs",
    "content": "//! Tests for the macros in this crate.\n\n#![allow(unused_crate_dependencies)]\n\nuse boa_engine::value::TryFromJs;\nuse boa_engine::{Context, JsResult, JsValue, Source, js_string};\nuse boa_string::JsString;\n\n#[test]\nfn try_from_js_derive() {\n    #[derive(Debug, TryFromJs, Eq, PartialEq)]\n    struct TryFromJsTest {\n        a: JsString,\n        #[boa(rename = \"bBB\")]\n        b: i32,\n        #[boa(from_js_with = \"check_tfj_called\")]\n        c: i32,\n    }\n\n    fn check_tfj_called(value: &JsValue, context: &mut Context) -> JsResult<i32> {\n        let v = value.to_i32(context)?;\n        Ok(v / 2)\n    }\n\n    let mut context = Context::default();\n    let obj = context\n        .eval(Source::from_bytes(br#\"({ a: \"hello\", bBB: 42, c: 120 })\"#))\n        .unwrap();\n\n    let result = TryFromJsTest::try_from_js(&obj, &mut context).unwrap();\n    assert_eq!(\n        result,\n        TryFromJsTest {\n            a: js_string!(\"hello\"),\n            b: 42,\n            c: 60\n        }\n    );\n}\n"
  },
  {
    "path": "core/engine/tests/module.rs",
    "content": "#![allow(unused_crate_dependencies, missing_docs)]\n\nuse std::cell::RefCell;\nuse std::rc::Rc;\n\nuse boa_engine::builtins::promise::PromiseState;\nuse boa_engine::module::{ModuleLoader, Referrer};\nuse boa_engine::{Context, JsResult, JsString, Module, Source, js_string};\n\n#[test]\nfn test_json_module_from_str() {\n    struct TestModuleLoader(JsString);\n    impl ModuleLoader for TestModuleLoader {\n        async fn load_imported_module(\n            self: Rc<Self>,\n            _referrer: Referrer,\n            request: boa_engine::module::ModuleRequest,\n            context: &RefCell<&mut Context>,\n        ) -> JsResult<Module> {\n            assert_eq!(request.specifier().to_std_string_escaped(), \"basic\");\n            let src = self.0.clone();\n\n            Ok(Module::parse_json(src, &mut context.borrow_mut()).unwrap())\n        }\n    }\n\n    let json_string = js_string!(r#\"{\"key\":\"value\",\"other\":123}\"#);\n    let mut context = Context::builder()\n        .module_loader(Rc::new(TestModuleLoader(json_string.clone())))\n        .build()\n        .unwrap();\n\n    let source = Source::from_bytes(\n        b\"\n        import basic_json from 'basic';\n        export let json = basic_json;\n    \",\n    );\n\n    let module = Module::parse(source, None, &mut context).unwrap();\n    let promise = module.load_link_evaluate(&mut context);\n    context.run_jobs().unwrap();\n\n    match promise.state() {\n        PromiseState::Pending => {}\n        PromiseState::Fulfilled(v) => {\n            assert!(v.is_undefined());\n        }\n        PromiseState::Rejected(e) => {\n            panic!(\"Unexpected error: {:?}\", e.to_string(&mut context).unwrap());\n        }\n    }\n\n    let json = module\n        .namespace(&mut context)\n        .get(js_string!(\"json\"), &mut context)\n        .unwrap();\n\n    assert_eq!(\n        JsString::from(json.to_json(&mut context).unwrap().unwrap().to_string()),\n        json_string\n    );\n}\n\n#[test]\nfn test_json_module_dynamic_import() {\n    struct TestModuleLoader(JsString);\n    impl ModuleLoader for TestModuleLoader {\n        async fn load_imported_module(\n            self: Rc<Self>,\n            _referrer: Referrer,\n            request: boa_engine::module::ModuleRequest,\n            context: &RefCell<&mut Context>,\n        ) -> JsResult<Module> {\n            assert_eq!(request.specifier().to_std_string_escaped(), \"basic\");\n\n            // Verify attributes were passed correctly\n            let type_attr = request\n                .get_attribute(\"type\")\n                .expect(\"should have type attribute\");\n            assert_eq!(type_attr.to_std_string_escaped(), \"json\");\n\n            let src = self.0.clone();\n            Ok(Module::parse_json(src, &mut context.borrow_mut()).unwrap())\n        }\n    }\n\n    let json_content = js_string!(r#\"{\"key\":\"value\",\"other\":123}\"#);\n    let mut context = Context::builder()\n        .module_loader(Rc::new(TestModuleLoader(json_content.clone())))\n        .build()\n        .unwrap();\n\n    let source = Source::from_bytes(\n        b\"\n        export let p = import('basic', { with: { type: 'json' } });\n    \",\n    );\n\n    let module = Module::parse(source, None, &mut context).unwrap();\n    let promise = module.load_link_evaluate(&mut context);\n    context.run_jobs().unwrap();\n\n    match promise.state() {\n        PromiseState::Fulfilled(_) => {}\n        _ => panic!(\"Module evaluation failed\"),\n    }\n\n    // Get the exported promise 'p'\n    let p = module\n        .namespace(&mut context)\n        .get(js_string!(\"p\"), &mut context)\n        .unwrap();\n\n    let p_obj = p.as_promise().unwrap();\n    context.run_jobs().unwrap();\n\n    match p_obj.state() {\n        PromiseState::Fulfilled(module_ns) => {\n            let default_export = module_ns\n                .as_object()\n                .unwrap()\n                .get(js_string!(\"default\"), &mut context)\n                .unwrap();\n\n            assert_eq!(\n                JsString::from(\n                    default_export\n                        .to_json(&mut context)\n                        .unwrap()\n                        .unwrap()\n                        .to_string()\n                ),\n                json_content\n            );\n        }\n        PromiseState::Rejected(e) => {\n            panic!(\n                \"Dynamic import failed: {:?}\",\n                e.to_string(&mut context).unwrap()\n            );\n        }\n        PromiseState::Pending => panic!(\"Dynamic import is still pending\"),\n    }\n}\n\n#[test]\nfn test_json_module_static_import_with_attributes() {\n    struct TestModuleLoader(JsString);\n    impl ModuleLoader for TestModuleLoader {\n        async fn load_imported_module(\n            self: Rc<Self>,\n            _referrer: Referrer,\n            request: boa_engine::module::ModuleRequest,\n            context: &RefCell<&mut Context>,\n        ) -> JsResult<Module> {\n            assert_eq!(request.specifier().to_std_string_escaped(), \"basic\");\n\n            let type_attr = request\n                .get_attribute(\"type\")\n                .expect(\"should have type attribute\");\n            assert_eq!(type_attr.to_std_string_escaped(), \"json\");\n\n            let src = self.0.clone();\n            Ok(Module::parse_json(src, &mut context.borrow_mut()).unwrap())\n        }\n    }\n\n    let json_string = js_string!(r#\"{\"static\":\"import\"}\"#);\n    let mut context = Context::builder()\n        .module_loader(Rc::new(TestModuleLoader(json_string.clone())))\n        .build()\n        .unwrap();\n\n    let source = Source::from_bytes(\n        b\"\n        import json from 'basic' with { type: 'json' };\n        export let value = json;\n    \",\n    );\n\n    let module = Module::parse(source, None, &mut context).unwrap();\n    let promise = module.load_link_evaluate(&mut context);\n    context.run_jobs().unwrap();\n\n    assert_eq!(\n        promise.state(),\n        PromiseState::Fulfilled(boa_engine::JsValue::undefined())\n    );\n\n    let value = module\n        .namespace(&mut context)\n        .get(js_string!(\"value\"), &mut context)\n        .unwrap();\n\n    assert_eq!(\n        JsString::from(value.to_json(&mut context).unwrap().unwrap().to_string()),\n        json_string\n    );\n}\n\n#[test]\nfn test_json_module_reexport_with_attributes() {\n    struct TestModuleLoader(JsString);\n    impl ModuleLoader for TestModuleLoader {\n        async fn load_imported_module(\n            self: Rc<Self>,\n            _referrer: Referrer,\n            request: boa_engine::module::ModuleRequest,\n            context: &RefCell<&mut Context>,\n        ) -> JsResult<Module> {\n            assert_eq!(request.specifier().to_std_string_escaped(), \"basic\");\n\n            let type_attr = request\n                .get_attribute(\"type\")\n                .expect(\"should have type attribute\");\n            assert_eq!(type_attr.to_std_string_escaped(), \"json\");\n\n            let src = self.0.clone();\n            Ok(Module::parse_json(src, &mut context.borrow_mut()).unwrap())\n        }\n    }\n\n    let json_string = js_string!(r#\"{\"re\":\"export\"}\"#);\n    let mut context = Context::builder()\n        .module_loader(Rc::new(TestModuleLoader(json_string.clone())))\n        .build()\n        .unwrap();\n\n    let source = Source::from_bytes(\n        b\"\n        export { default as json } from 'basic' with { type: 'json' };\n    \",\n    );\n\n    let module = Module::parse(source, None, &mut context).unwrap();\n    let promise = module.load_link_evaluate(&mut context);\n    context.run_jobs().unwrap();\n\n    assert_eq!(\n        promise.state(),\n        PromiseState::Fulfilled(boa_engine::JsValue::undefined())\n    );\n\n    let json = module\n        .namespace(&mut context)\n        .get(js_string!(\"json\"), &mut context)\n        .unwrap();\n\n    assert_eq!(\n        JsString::from(json.to_json(&mut context).unwrap().unwrap().to_string()),\n        json_string\n    );\n}\n\n#[test]\nfn test_dynamic_import_invalid_options() {\n    struct TestModuleLoader;\n    impl ModuleLoader for TestModuleLoader {\n        async fn load_imported_module(\n            self: Rc<Self>,\n            _referrer: Referrer,\n            _request: boa_engine::module::ModuleRequest,\n            _context: &RefCell<&mut Context>,\n        ) -> JsResult<Module> {\n            panic!(\"Module loading should not be triggered for invalid options\");\n        }\n    }\n\n    let mut context = Context::builder()\n        .module_loader(Rc::new(TestModuleLoader))\n        .build()\n        .unwrap();\n\n    let source = Source::from_bytes(\n        b\"\n        export let p = import('basic', 'invalid-option-string');\n    \",\n    );\n\n    let module = Module::parse(source, None, &mut context).unwrap();\n    let promise = module.load_link_evaluate(&mut context);\n    context.run_jobs().unwrap();\n\n    match promise.state() {\n        PromiseState::Fulfilled(_) => {}\n        _ => panic!(\"Module evaluation failed\"),\n    }\n\n    // Get the exported promise 'p'\n    let p = module\n        .namespace(&mut context)\n        .get(js_string!(\"p\"), &mut context)\n        .unwrap();\n\n    let p_obj = p.as_promise().unwrap();\n    context.run_jobs().unwrap();\n\n    match p_obj.state() {\n        PromiseState::Rejected(e) => {\n            let error = e.as_object().unwrap();\n            let name = error.get(js_string!(\"name\"), &mut context).unwrap();\n            assert_eq!(name.as_string().unwrap(), js_string!(\"TypeError\"));\n        }\n        state => panic!(\"Dynamic import should be rejected with TypeError, got {state:?}\"),\n    }\n}\n\n#[test]\nfn test_dynamic_import_non_string_attribute_value() {\n    struct TestModuleLoader;\n    impl ModuleLoader for TestModuleLoader {\n        async fn load_imported_module(\n            self: Rc<Self>,\n            _referrer: Referrer,\n            _request: boa_engine::module::ModuleRequest,\n            _context: &RefCell<&mut Context>,\n        ) -> JsResult<Module> {\n            panic!(\"Module loading should not be triggered for invalid attribute values\");\n        }\n    }\n\n    let mut context = Context::builder()\n        .module_loader(Rc::new(TestModuleLoader))\n        .build()\n        .unwrap();\n\n    let source = Source::from_bytes(\n        b\"\n        export let p = import('basic', { with: { type: 123 } });\n    \",\n    );\n\n    let module = Module::parse(source, None, &mut context).unwrap();\n    let promise = module.load_link_evaluate(&mut context);\n    context.run_jobs().unwrap();\n\n    match promise.state() {\n        PromiseState::Fulfilled(_) => {}\n        _ => panic!(\"Module evaluation failed\"),\n    }\n\n    let p = module\n        .namespace(&mut context)\n        .get(js_string!(\"p\"), &mut context)\n        .unwrap();\n\n    let p_obj = p.as_promise().unwrap();\n    context.run_jobs().unwrap();\n\n    match p_obj.state() {\n        PromiseState::Rejected(e) => {\n            let error = e.as_object().unwrap();\n            let name = error.get(js_string!(\"name\"), &mut context).unwrap();\n            assert_eq!(name.as_string().unwrap(), js_string!(\"TypeError\"));\n            let message = error.get(js_string!(\"message\"), &mut context).unwrap();\n            assert_eq!(\n                message.as_string().unwrap(),\n                js_string!(\"import attribute value must be a string\")\n            );\n        }\n        state => panic!(\"Dynamic import should be rejected with TypeError, got {state:?}\"),\n    }\n}\n\n#[test]\nfn test_dynamic_import_symbol_key() {\n    struct TestModuleLoader(JsString);\n    impl ModuleLoader for TestModuleLoader {\n        async fn load_imported_module(\n            self: Rc<Self>,\n            _referrer: Referrer,\n            request: boa_engine::module::ModuleRequest,\n            context: &RefCell<&mut Context>,\n        ) -> JsResult<Module> {\n            assert_eq!(request.specifier().to_std_string_escaped(), \"basic\");\n\n            // Verify attributes were passed correctly (symbol key should be ignored)\n            assert!(request.get_attribute(\"type\").is_none());\n\n            let src = self.0.clone();\n            Ok(Module::parse_json(src, &mut context.borrow_mut()).unwrap())\n        }\n    }\n\n    let json_content = js_string!(r#\"{\"ignore\":\"symbol\"}\"#);\n    let mut context = Context::builder()\n        .module_loader(Rc::new(TestModuleLoader(json_content.clone())))\n        .build()\n        .unwrap();\n\n    let source = Source::from_bytes(\n        b\"\n        let sym = Symbol('type');\n        export let p = import('basic', { with: { [sym]: 'json' } });\n    \",\n    );\n\n    let module = Module::parse(source, None, &mut context).unwrap();\n    let promise = module.load_link_evaluate(&mut context);\n    context.run_jobs().unwrap();\n\n    match promise.state() {\n        PromiseState::Fulfilled(_) => {}\n        _ => panic!(\"Module evaluation failed\"),\n    }\n\n    let p = module\n        .namespace(&mut context)\n        .get(js_string!(\"p\"), &mut context)\n        .unwrap();\n\n    let p_obj = p.as_promise().unwrap();\n    context.run_jobs().unwrap();\n\n    match p_obj.state() {\n        PromiseState::Fulfilled(module_ns) => {\n            let default_export = module_ns\n                .as_object()\n                .unwrap()\n                .get(js_string!(\"default\"), &mut context)\n                .unwrap();\n\n            assert_eq!(\n                JsString::from(\n                    default_export\n                        .to_json(&mut context)\n                        .unwrap()\n                        .unwrap()\n                        .to_string()\n                ),\n                json_content\n            );\n        }\n        PromiseState::Rejected(e) => {\n            panic!(\n                \"Dynamic import failed: {:?}\",\n                e.to_string(&mut context).unwrap()\n            );\n        }\n        PromiseState::Pending => panic!(\"Dynamic import is still pending\"),\n    }\n}\n"
  },
  {
    "path": "core/gc/ABOUT.md",
    "content": "# About Boa\n\nBoa is an open-source, experimental ECMAScript Engine written in Rust for\nlexing, parsing and executing ECMAScript/JavaScript. Currently, Boa supports some\nof the [language][boa-conformance]. More information can be viewed at [Boa's\nwebsite][boa-web].\n\nTry out the most recent release with Boa's live demo\n[playground][boa-playground].\n\n## Boa Crates\n\n- [**`boa_cli`**][cli] - Boa's CLI && REPL implementation\n- [**`boa_ast`**][ast] - Boa's ECMAScript Abstract Syntax Tree.\n- [**`boa_engine`**][engine] - Boa's implementation of ECMAScript builtin objects and execution.\n- [**`boa_gc`**][gc] - Boa's garbage collector.\n- [**`boa_icu_provider`**][icu] - Boa's ICU4X data provider.\n- [**`boa_interner`**][interner] - Boa's string interner.\n- [**`boa_macros`**][macros] - Boa's macros.\n- [**`boa_parser`**][parser] - Boa's lexer and parser.\n- [**`boa_runtime`**][runtime] - Boa's `WebAPI` features.\n- [**`boa_string`**][string] - Boa's ECMAScript string implementation.\n- [**`boa_wintertc`**][wintertc] - Boa's `WinterTC` (TC55) Minimum Common Web API implementation.\n- [**`tag_ptr`**][tag_ptr] - Utility library that enables a pointer to be associated with a tag of type `usize`.\n- [**`small_btree`**][small_btree] - Utility library that adds the `SmallBTreeMap` data structure.\n\n[boa-conformance]: https://boajs.dev/conformance\n[boa-web]: https://boajs.dev/\n[boa-playground]: https://boajs.dev/playground\n[ast]: https://docs.rs/boa_ast/latest/boa_ast/index.html\n[engine]: https://docs.rs/boa_engine/latest/boa_engine/index.html\n[gc]: https://docs.rs/boa_gc/latest/boa_gc/index.html\n[interner]: https://docs.rs/boa_interner/latest/boa_interner/index.html\n[parser]: https://docs.rs/boa_parser/latest/boa_parser/index.html\n[icu]: https://docs.rs/boa_icu_provider/latest/boa_icu_provider/index.html\n[runtime]: https://docs.rs/boa_runtime/latest/boa_runtime/index.html\n[string]: https://docs.rs/boa_string/latest/boa_string/index.html\n[wintertc]: https://docs.rs/boa_wintertc/latest/boa_wintertc/index.html\n[tag_ptr]: https://docs.rs/tag_ptr/latest/tag_ptr/index.html\n[small_btree]: https://docs.rs/small_btree/latest/small_btree/index.html\n[macros]: https://docs.rs/boa_macros/latest/boa_macros/index.html\n[cli]: https://crates.io/crates/boa_cli\n"
  },
  {
    "path": "core/gc/Cargo.toml",
    "content": "[package]\nname = \"boa_gc\"\ndescription = \"Garbage collector for the Boa JavaScript engine.\"\nkeywords = [\"javascript\", \"js\", \"garbage\", \"memory\"]\ncategories = [\"command-line-utilities\"]\nversion.workspace = true\nedition.workspace = true\nauthors.workspace = true\nlicense.workspace = true\nrepository.workspace = true\nrust-version.workspace = true\n\n[features]\n# Enable default implementations of trace and finalize for the thin-vec crate\nthin-vec = [\"dep:thin-vec\"]\n# Enable default implementations of trace and finalize for some `ICU4X` types\nicu = [\"dep:icu_locale_core\"]\n# Enable default implementations of trace and finalize for the `boa_string` crate\nboa_string = [\"dep:boa_string\"]\n# Enable default implementations of trace and finalize for the `either` crate\neither = [\"dep:either\"]\n# Enable default implementations of trace and finalize for the arrayvec crate\narrayvec = [\"dep:arrayvec\"]\n\n[dependencies]\nboa_macros.workspace = true\nhashbrown.workspace = true\n\nboa_string = { workspace = true, optional = true }\neither = { workspace = true, optional = true }\nthin-vec = { workspace = true, optional = true }\nicu_locale_core = { workspace = true, optional = true }\narrayvec = { workspace = true, optional = true }\n\n[lints]\nworkspace = true\n\n[package.metadata.docs.rs]\nall-features = true\n"
  },
  {
    "path": "core/gc/src/cell.rs",
    "content": "//! A garbage collected cell implementation\n\nuse crate::{\n    Tracer,\n    trace::{Finalize, Trace},\n};\nuse std::marker::PhantomData;\nuse std::ptr::NonNull;\nuse std::{\n    cell::{Cell, UnsafeCell},\n    cmp::Ordering,\n    fmt::{self, Debug, Display},\n    hash::Hash,\n    ops::{Deref, DerefMut},\n};\n\n/// `BorrowFlag` represent the internal state of a `GcCell` and\n/// keeps track of the number of current borrows.\n#[derive(Copy, Clone)]\nstruct BorrowFlag(usize);\n\n/// `BorrowState` represents the various states of a `BorrowFlag`\n///\n///  - Reading: the value is currently being read/borrowed.\n///  - Writing: the value is currently being written/borrowed mutably.\n///  - Unused: the value is currently unrooted.\n#[derive(Copy, Clone, Debug, Eq, PartialEq)]\nenum BorrowState {\n    Reading,\n    Writing,\n    Unused,\n}\n\nconst WRITING: usize = !0;\nconst UNUSED: usize = 0;\n\n/// The base borrow flag init is rooted, and has no outstanding borrows.\nconst BORROWFLAG_INIT: BorrowFlag = BorrowFlag(UNUSED);\n\nimpl BorrowFlag {\n    /// Check the current `BorrowState` of `BorrowFlag`.\n    const fn borrowed(self) -> BorrowState {\n        match self.0 {\n            UNUSED => BorrowState::Unused,\n            WRITING => BorrowState::Writing,\n            _ => BorrowState::Reading,\n        }\n    }\n\n    /// Set the `BorrowFlag`'s state to writing.\n    const fn set_writing(self) -> Self {\n        Self(self.0 | WRITING)\n    }\n\n    /// Increments the counter for a new borrow.\n    ///\n    /// # Panic\n    ///  - This method will panic if the current `BorrowState` is writing.\n    ///  - This method will panic after incrementing if the borrow count overflows.\n    #[inline]\n    fn add_reading(self) -> Self {\n        assert!(self.borrowed() != BorrowState::Writing);\n        let flags = Self(self.0 + 1);\n\n        // This will fail if the borrow count overflows, which shouldn't happen,\n        // but let's be safe\n        {\n            assert!(flags.borrowed() == BorrowState::Reading);\n        }\n        flags\n    }\n\n    /// Decrements the counter to remove a borrow.\n    ///\n    /// # Panic\n    ///  - This method will panic if the current `BorrowState` is not reading.\n    fn sub_reading(self) -> Self {\n        assert!(self.borrowed() == BorrowState::Reading);\n        Self(self.0 - 1)\n    }\n}\n\nimpl Debug for BorrowFlag {\n    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {\n        f.debug_struct(\"BorrowFlag\")\n            .field(\"State\", &self.borrowed())\n            .finish()\n    }\n}\n\n/// A mutable memory location with dynamically checked borrow rules\n/// that can be used inside of a garbage-collected pointer.\n///\n/// This object is a `RefCell` that can be used inside of a `Gc<T>`.\npub struct GcRefCell<T: ?Sized + 'static> {\n    borrow: Cell<BorrowFlag>,\n    cell: UnsafeCell<T>,\n}\n\nimpl<T> GcRefCell<T> {\n    /// Creates a new `GcCell` containing `value`.\n    pub const fn new(value: T) -> Self {\n        Self {\n            borrow: Cell::new(BORROWFLAG_INIT),\n            cell: UnsafeCell::new(value),\n        }\n    }\n\n    /// Consumes the `GcCell`, returning the wrapped value.\n    pub fn into_inner(self) -> T {\n        self.cell.into_inner()\n    }\n}\n\nimpl<T: ?Sized> GcRefCell<T> {\n    /// Immutably borrows the wrapped value.\n    ///\n    /// The borrow lasts until the returned `GcCellRef` exits scope.\n    /// Multiple immutable borrows can be taken out at the same time.\n    ///\n    /// # Panics\n    ///\n    /// Panics if the value is currently mutably borrowed.\n    pub fn borrow(&self) -> GcRef<'_, T> {\n        match self.try_borrow() {\n            Ok(value) => value,\n            Err(e) => panic!(\"{}\", e),\n        }\n    }\n\n    /// Mutably borrows the wrapped value.\n    ///\n    /// The borrow lasts until the returned `GcCellRefMut` exits scope.\n    /// The value cannot be borrowed while this borrow is active.\n    ///\n    /// # Panics\n    ///\n    /// Panics if the value is currently borrowed.\n    #[track_caller]\n    pub fn borrow_mut(&self) -> GcRefMut<'_, T> {\n        match self.try_borrow_mut() {\n            Ok(value) => value,\n            Err(e) => panic!(\"{}\", e),\n        }\n    }\n\n    /// Immutably borrows the wrapped value, returning an error if the value is currently mutably\n    /// borrowed.\n    ///\n    /// The borrow lasts until the returned `GcCellRef` exits scope. Multiple immutable borrows can be\n    /// taken out at the same time.\n    ///\n    /// This is the non-panicking variant of [`borrow`](#method.borrow).\n    ///\n    /// # Errors\n    ///\n    /// Returns an `Err` if the value is currently mutably borrowed.\n    pub fn try_borrow(&self) -> Result<GcRef<'_, T>, BorrowError> {\n        if self.borrow.get().borrowed() == BorrowState::Writing {\n            return Err(BorrowError);\n        }\n        self.borrow.set(self.borrow.get().add_reading());\n\n        // SAFETY: calling value on a rooted value may cause Undefined Behavior\n        unsafe {\n            Ok(GcRef {\n                borrow: BorrowGcRef {\n                    borrow: &self.borrow,\n                },\n                value: NonNull::new_unchecked(self.cell.get()),\n            })\n        }\n    }\n\n    /// Mutably borrows the wrapped value, returning an error if the value is currently borrowed.\n    ///\n    /// The borrow lasts until the returned `GcCellRefMut` exits scope.\n    /// The value cannot be borrowed while this borrow is active.\n    ///\n    /// This is the non-panicking variant of [`borrow_mut`](#method.borrow_mut).\n    ///\n    /// # Errors\n    ///\n    /// Returns an `Err` if the value is currently borrowed.\n    pub fn try_borrow_mut(&self) -> Result<GcRefMut<'_, T>, BorrowMutError> {\n        if self.borrow.get().borrowed() != BorrowState::Unused {\n            return Err(BorrowMutError);\n        }\n        self.borrow.set(self.borrow.get().set_writing());\n\n        // SAFETY: This is safe as the value is rooted if it was not previously rooted,\n        // so it cannot be dropped.\n        unsafe {\n            Ok(GcRefMut {\n                borrow: BorrowGcRefMut {\n                    borrow: &self.borrow,\n                },\n                value: NonNull::new_unchecked(self.cell.get()),\n                marker: PhantomData,\n            })\n        }\n    }\n}\n\n/// An error returned by [`GcCell::try_borrow`](struct.GcCell.html#method.try_borrow).\n#[derive(Debug, Clone, Copy, Eq, PartialEq, Ord, PartialOrd, Default, Hash)]\npub struct BorrowError;\n\nimpl Display for BorrowError {\n    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {\n        Display::fmt(\"GcCell<T> already mutably borrowed\", f)\n    }\n}\n\n/// An error returned by [`GcCell::try_borrow_mut`](struct.GcCell.html#method.try_borrow_mut).\n#[derive(Debug, Clone, Copy, Eq, PartialEq, Ord, PartialOrd, Default, Hash)]\npub struct BorrowMutError;\n\nimpl Display for BorrowMutError {\n    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {\n        Display::fmt(\"GcCell<T> already borrowed\", f)\n    }\n}\n\nimpl<T: Trace + ?Sized> Finalize for GcRefCell<T> {}\n\n// SAFETY: GcCell maintains its own BorrowState and rootedness. GcCell's implementation\n// focuses on only continuing Trace-based methods while the cell state is not written.\n// Implementing a Trace while the cell is being written to or incorrectly implementing Trace\n// on GcCell's value may cause Undefined Behavior\nunsafe impl<T: Trace + ?Sized> Trace for GcRefCell<T> {\n    unsafe fn trace(&self, tracer: &mut Tracer) {\n        match self.borrow.get().borrowed() {\n            BorrowState::Writing => (),\n            // SAFETY: Please see GcCell's Trace impl Safety note.\n            _ => unsafe { (*self.cell.get()).trace(tracer) },\n        }\n    }\n\n    unsafe fn trace_non_roots(&self) {\n        match self.borrow.get().borrowed() {\n            BorrowState::Writing => (),\n            // SAFETY: Please see GcCell's Trace impl Safety note.\n            _ => unsafe { (*self.cell.get()).trace_non_roots() },\n        }\n    }\n\n    fn run_finalizer(&self) {\n        Finalize::finalize(self);\n        match self.borrow.get().borrowed() {\n            BorrowState::Writing => (),\n            // SAFETY: Please see GcCell's Trace impl Safety note.\n            _ => unsafe { (*self.cell.get()).run_finalizer() },\n        }\n    }\n}\n\nstruct BorrowGcRef<'a> {\n    borrow: &'a Cell<BorrowFlag>,\n}\n\nimpl Drop for BorrowGcRef<'_> {\n    fn drop(&mut self) {\n        debug_assert!(self.borrow.get().borrowed() == BorrowState::Reading);\n        self.borrow.set(self.borrow.get().sub_reading());\n    }\n}\n\nimpl Clone for BorrowGcRef<'_> {\n    #[inline]\n    fn clone(&self) -> Self {\n        self.borrow.set(self.borrow.get().add_reading());\n        BorrowGcRef {\n            borrow: self.borrow,\n        }\n    }\n}\n\n/// A wrapper type for an immutably borrowed value from a `GcCell<T>`.\npub struct GcRef<'a, T: ?Sized + 'static> {\n    value: NonNull<T>,\n    borrow: BorrowGcRef<'a>,\n}\n\nimpl<'a, T: ?Sized> GcRef<'a, T> {\n    /// Casts to a `GcRef` of another type.\n    ///\n    /// # Safety\n    /// * The caller must ensure that `T` can be safely cast to `U`.\n    #[must_use]\n    pub unsafe fn cast<U>(orig: Self) -> GcRef<'a, U> {\n        let value = orig.value.cast::<U>();\n\n        GcRef {\n            borrow: orig.borrow,\n            value,\n        }\n    }\n\n    /// Copies a `GcCellRef`.\n    ///\n    /// The `GcCell` is already immutably borrowed, so this cannot fail.\n    ///\n    /// This is an associated function that needs to be used as\n    /// `GcCellRef::clone(...)`. A `Clone` implementation or a method\n    /// would interfere with the use of `c.borrow().clone()` to clone\n    /// the contents of a `GcCell`.\n    #[allow(clippy::should_implement_trait)]\n    #[must_use]\n    pub fn clone(orig: &GcRef<'a, T>) -> GcRef<'a, T> {\n        GcRef {\n            borrow: orig.borrow.clone(),\n            value: orig.value,\n        }\n    }\n\n    /// Tries to make a new `GcCellRef` from a component of the borrowed data, returning `None`\n    /// if the mapping function returns `None`.\n    ///\n    /// The `GcCell` is already immutably borrowed, so this cannot fail.\n    ///\n    /// This is an associated function that needs to be used as `GcCellRef::try_map(...)`.\n    /// A method would interfere with methods of the same name on the contents\n    /// of a `GcCellRef` used through `Deref`.\n    pub fn try_map<U, F>(orig: Self, f: F) -> Option<GcRef<'a, U>>\n    where\n        U: ?Sized,\n        F: FnOnce(&T) -> Option<&U>,\n    {\n        let value = NonNull::from(f(&*orig)?);\n\n        let ret = GcRef {\n            borrow: orig.borrow,\n            value,\n        };\n\n        Some(ret)\n    }\n\n    /// Makes a new `GcCellRef` from a component of the borrowed data.\n    ///\n    /// The `GcCell` is already immutably borrowed, so this cannot fail.\n    ///\n    /// This is an associated function that needs to be used as `GcCellRef::map(...)`.\n    /// A method would interfere with methods of the same name on the contents\n    /// of a `GcCellRef` used through `Deref`.\n    pub fn map<U, F>(orig: Self, f: F) -> GcRef<'a, U>\n    where\n        U: ?Sized,\n        F: FnOnce(&T) -> &U,\n    {\n        let value = NonNull::from(f(&*orig));\n\n        GcRef {\n            borrow: orig.borrow,\n            value,\n        }\n    }\n\n    /// Splits a `GcCellRef` into multiple `GcCellRef`s for different components of the borrowed data.\n    ///\n    /// The `GcCell` is already immutably borrowed, so this cannot fail.\n    ///\n    /// This is an associated function that needs to be used as `GcCellRef::map_split(...)`.\n    /// A method would interfere with methods of the same name on the contents of a `GcCellRef` used through `Deref`.\n    pub fn map_split<U, V, F>(orig: Self, f: F) -> (GcRef<'a, U>, GcRef<'a, V>)\n    where\n        U: ?Sized,\n        V: ?Sized,\n        F: FnOnce(&T) -> (&U, &V),\n    {\n        let (a, b) = f(&*orig);\n        let borrow = orig.borrow.clone();\n\n        (\n            GcRef {\n                borrow,\n                value: NonNull::from(a),\n            },\n            GcRef {\n                value: NonNull::from(b),\n                borrow: orig.borrow,\n            },\n        )\n    }\n}\n\nimpl<T: ?Sized> Deref for GcRef<'_, T> {\n    type Target = T;\n\n    fn deref(&self) -> &T {\n        unsafe { self.value.as_ref() }\n    }\n}\n\nimpl<T: ?Sized + Debug> Debug for GcRef<'_, T> {\n    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {\n        Debug::fmt(&**self, f)\n    }\n}\n\nimpl<T: ?Sized + Display> Display for GcRef<'_, T> {\n    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {\n        Display::fmt(&**self, f)\n    }\n}\n\nstruct BorrowGcRefMut<'a> {\n    borrow: &'a Cell<BorrowFlag>,\n}\n\nimpl Drop for BorrowGcRefMut<'_> {\n    fn drop(&mut self) {\n        debug_assert!(self.borrow.get().borrowed() == BorrowState::Writing);\n        self.borrow.set(BorrowFlag(UNUSED));\n    }\n}\n\n/// A wrapper type for a mutably borrowed value from a `GcCell<T>`.\npub struct GcRefMut<'a, T: ?Sized> {\n    // NB: we use a pointer instead of `&'a mut U` to avoid `noalias` violations, because\n    // a `GcRefMut` argument doesn't hold exclusivity for its whole scope, only until it\n    // drops.\n    value: NonNull<T>,\n    borrow: BorrowGcRefMut<'a>,\n    // `NonNull` is covariant over `T`, so we need to reintroduce invariance.\n    marker: PhantomData<&'a mut T>,\n}\n\nimpl<'a, T: ?Sized> GcRefMut<'a, T> {\n    /// Casts to a `GcRefMut` of another type.\n    ///\n    /// # Safety\n    /// * The caller must ensure that `U` can be safely cast to `V`.\n    #[must_use]\n    pub unsafe fn cast<V>(orig: Self) -> GcRefMut<'a, V> {\n        let value = orig.value.cast::<V>();\n\n        GcRefMut {\n            borrow: orig.borrow,\n            value,\n            marker: PhantomData,\n        }\n    }\n\n    /// Tries to make a new `GcCellRefMut` for a component of the borrowed data, returning `None`\n    /// if the mapping function returns `None`.\n    ///\n    /// The `GcCellRefMut` is already mutably borrowed, so this cannot fail.\n    ///\n    /// This is an associated function that needs to be used as\n    /// `GcCellRefMut::map(...)`. A method would interfere with methods of the same\n    /// name on the contents of a `GcCell` used through `Deref`.\n    pub fn try_map<V, F>(mut orig: GcRefMut<'a, T>, f: F) -> Option<GcRefMut<'a, V>>\n    where\n        V: ?Sized,\n        F: FnOnce(&mut T) -> Option<&mut V>,\n    {\n        let value = NonNull::from(f(&mut *orig)?);\n\n        let ret = GcRefMut {\n            borrow: orig.borrow,\n            value,\n            marker: PhantomData,\n        };\n\n        Some(ret)\n    }\n\n    /// Makes a new `GcCellRefMut` for a component of the borrowed data, e.g., an enum\n    /// variant.\n    ///\n    /// The `GcCellRefMut` is already mutably borrowed, so this cannot fail.\n    ///\n    /// This is an associated function that needs to be used as\n    /// `GcCellRefMut::map(...)`. A method would interfere with methods of the same\n    /// name on the contents of a `GcCell` used through `Deref`.\n    pub fn map<V, F>(mut orig: Self, f: F) -> GcRefMut<'a, V>\n    where\n        V: ?Sized,\n        F: FnOnce(&mut T) -> &mut V,\n    {\n        let value = NonNull::from(f(&mut *orig));\n\n        GcRefMut {\n            borrow: orig.borrow,\n            value,\n            marker: PhantomData,\n        }\n    }\n}\n\nimpl<T: ?Sized> Deref for GcRefMut<'_, T> {\n    type Target = T;\n\n    fn deref(&self) -> &T {\n        // SAFETY: the value is accessible as long as we hold our borrow.\n        unsafe { self.value.as_ref() }\n    }\n}\n\nimpl<T: ?Sized> DerefMut for GcRefMut<'_, T> {\n    fn deref_mut(&mut self) -> &mut T {\n        // SAFETY: the value is accessible as long as we hold our borrow.\n        unsafe { self.value.as_mut() }\n    }\n}\n\nimpl<T: Debug + ?Sized> Debug for GcRefMut<'_, T> {\n    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {\n        Debug::fmt(&**self, f)\n    }\n}\n\nimpl<T: Display + ?Sized> Display for GcRefMut<'_, T> {\n    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {\n        Display::fmt(&**self, f)\n    }\n}\n\n// SAFETY: GcCell<T> tracks it's `BorrowState` is `Writing`\nunsafe impl<T: ?Sized + Send> Send for GcRefCell<T> {}\n\nimpl<T: Trace + Clone> Clone for GcRefCell<T> {\n    fn clone(&self) -> Self {\n        Self::new(self.borrow().clone())\n    }\n}\n\nimpl<T: Default> Default for GcRefCell<T> {\n    fn default() -> Self {\n        Self::new(Default::default())\n    }\n}\n\n#[allow(clippy::inline_always)]\nimpl<T: ?Sized + PartialEq> PartialEq for GcRefCell<T> {\n    #[inline(always)]\n    fn eq(&self, other: &Self) -> bool {\n        *self.borrow() == *other.borrow()\n    }\n}\n\nimpl<T: ?Sized + Eq> Eq for GcRefCell<T> {}\n\n#[allow(clippy::inline_always)]\nimpl<T: ?Sized + PartialOrd> PartialOrd for GcRefCell<T> {\n    #[inline(always)]\n    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {\n        (*self.borrow()).partial_cmp(&*other.borrow())\n    }\n\n    #[inline(always)]\n    fn lt(&self, other: &Self) -> bool {\n        *self.borrow() < *other.borrow()\n    }\n\n    #[inline(always)]\n    fn le(&self, other: &Self) -> bool {\n        *self.borrow() <= *other.borrow()\n    }\n\n    #[inline(always)]\n    fn gt(&self, other: &Self) -> bool {\n        *self.borrow() > *other.borrow()\n    }\n\n    #[inline(always)]\n    fn ge(&self, other: &Self) -> bool {\n        *self.borrow() >= *other.borrow()\n    }\n}\n\nimpl<T: ?Sized + Ord> Ord for GcRefCell<T> {\n    fn cmp(&self, other: &Self) -> Ordering {\n        (*self.borrow()).cmp(&*other.borrow())\n    }\n}\n\nimpl<T: ?Sized + Debug> Debug for GcRefCell<T> {\n    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {\n        match self.borrow.get().borrowed() {\n            BorrowState::Unused | BorrowState::Reading => f\n                .debug_struct(\"GcCell\")\n                .field(\"flags\", &self.borrow.get())\n                .field(\"value\", &self.borrow())\n                .finish_non_exhaustive(),\n            BorrowState::Writing => f\n                .debug_struct(\"GcCell\")\n                .field(\"flags\", &self.borrow.get())\n                .field(\"value\", &\"<borrowed>\")\n                .finish_non_exhaustive(),\n        }\n    }\n}\n"
  },
  {
    "path": "core/gc/src/internals/ephemeron_box.rs",
    "content": "use crate::{Gc, GcBox, Tracer, trace::Trace};\nuse std::{cell::UnsafeCell, ptr::NonNull};\n\nuse super::GcHeader;\n\n/// The inner allocation of an [`Ephemeron`][crate::Ephemeron] pointer.\npub(crate) struct EphemeronBox<K: Trace + ?Sized + 'static, V: Trace + 'static> {\n    pub(crate) header: GcHeader,\n    data: UnsafeCell<Option<Data<K, V>>>,\n}\n\nstruct Data<K: Trace + ?Sized + 'static, V: Trace + 'static> {\n    key: NonNull<GcBox<K>>,\n    value: V,\n}\n\nimpl<K: Trace + ?Sized, V: Trace> EphemeronBox<K, V> {\n    /// Creates a new `EphemeronBox` that tracks `key` and has `value` as its inner data.\n    pub(crate) fn new(key: &Gc<K>, value: V) -> Self {\n        Self {\n            header: GcHeader::new(),\n            data: UnsafeCell::new(Some(Data {\n                key: key.inner_ptr(),\n                value,\n            })),\n        }\n    }\n\n    /// Creates a new `EphemeronBox` with its inner data in the invalidated state.\n    pub(crate) fn new_empty() -> Self {\n        Self {\n            header: GcHeader::new(),\n            data: UnsafeCell::new(None),\n        }\n    }\n\n    /// Returns a reference to the ephemeron's value or None.\n    ///\n    /// # Safety\n    ///\n    /// The caller must ensure there are no live mutable references to the ephemeron box's data\n    /// before calling this method.\n    pub(crate) unsafe fn value(&self) -> Option<&V> {\n        // SAFETY: the garbage collector ensures the ephemeron doesn't mutate until\n        // finalization.\n        let data = unsafe { &*self.data.get() };\n        data.as_ref().map(|data| &data.value)\n    }\n\n    /// Returns the pointer to the ephemeron's key or None.\n    ///\n    /// # Safety\n    ///\n    /// The caller must ensure there are no live mutable references to the ephemeron box's data\n    /// before calling this method.\n    pub(crate) unsafe fn key_ptr(&self) -> Option<NonNull<GcBox<K>>> {\n        // SAFETY: the garbage collector ensures the ephemeron doesn't mutate until\n        // finalization.\n        unsafe {\n            let data = &*self.data.get();\n            data.as_ref().map(|data| data.key)\n        }\n    }\n\n    /// Returns a reference to the ephemeron's key or None.\n    ///\n    /// # Safety\n    ///\n    /// The caller must ensure there are no live mutable references to the ephemeron box's data\n    /// before calling this method.\n    pub(crate) unsafe fn key(&self) -> Option<&GcBox<K>> {\n        // SAFETY: the garbage collector ensures the ephemeron doesn't mutate until\n        // finalization.\n        unsafe { self.key_ptr().map(|data| data.as_ref()) }\n    }\n\n    /// Marks this `EphemeronBox` as live.\n    ///\n    /// This doesn't mark the inner value of the ephemeron. [`ErasedEphemeronBox::trace`]\n    /// does this, and it's called by the garbage collector on demand.\n    pub(crate) unsafe fn mark(&self) {\n        self.header.mark();\n    }\n\n    /// Sets the inner data of the `EphemeronBox` to the specified key and value.\n    ///\n    /// # Safety\n    ///\n    /// The caller must ensure there are no live mutable references to the ephemeron box's data\n    /// before calling this method.\n    pub(crate) unsafe fn set(&self, key: &Gc<K>, value: V) {\n        // SAFETY: The caller must ensure setting the key and value of the ephemeron box is safe.\n        unsafe {\n            *self.data.get() = Some(Data {\n                key: key.inner_ptr(),\n                value,\n            });\n        }\n    }\n\n    #[inline]\n    pub(crate) fn inc_ref_count(&self) {\n        self.header.inc_ref_count();\n    }\n\n    #[inline]\n    pub(crate) fn dec_ref_count(&self) {\n        self.header.dec_ref_count();\n    }\n\n    #[inline]\n    pub(crate) fn inc_non_root_count(&self) {\n        self.header.inc_non_root_count();\n    }\n}\n\npub(crate) trait ErasedEphemeronBox {\n    /// Gets the header of the `EphemeronBox`.\n    fn header(&self) -> &GcHeader;\n\n    /// Traces through the `EphemeronBox`'s held value, but only if it's marked and its key is also\n    /// marked. Returns `true` if the ephemeron successfully traced through its value. This also\n    /// considers ephemerons that are marked but don't have their value anymore as\n    /// \"successfully traced\".\n    unsafe fn trace(&self, tracer: &mut Tracer) -> bool;\n\n    fn trace_non_roots(&self);\n\n    /// Runs the finalization logic of the `EphemeronBox`'s held value, if the key is still live,\n    /// and clears its contents.\n    fn finalize_and_clear(&self);\n}\n\nimpl<K: Trace + ?Sized, V: Trace> ErasedEphemeronBox for EphemeronBox<K, V> {\n    fn header(&self) -> &GcHeader {\n        &self.header\n    }\n\n    unsafe fn trace(&self, tracer: &mut Tracer) -> bool {\n        if !self.header.is_marked() {\n            return false;\n        }\n\n        // SAFETY: the garbage collector ensures the ephemeron doesn't mutate until\n        // finalization.\n        let data = unsafe { &*self.data.get() };\n        let Some(data) = data.as_ref() else {\n            return true;\n        };\n\n        // SAFETY: `key` comes from a `Gc`, and the garbage collector only invalidates\n        // `key` when it is unreachable, making `key` always valid.\n        let key = unsafe { data.key.as_ref() };\n\n        let is_key_marked = key.is_marked();\n\n        if is_key_marked {\n            // SAFETY: this is safe to call, since we want to trace all reachable objects\n            // from a marked ephemeron that holds a live `key`.\n            unsafe { data.value.trace(tracer) }\n        }\n\n        is_key_marked\n    }\n\n    fn trace_non_roots(&self) {\n        // SAFETY: Tracing always executes before collecting, meaning this cannot cause\n        // use after free.\n        unsafe {\n            if let Some(value) = self.value() {\n                value.trace_non_roots();\n            }\n        }\n    }\n\n    fn finalize_and_clear(&self) {\n        // SAFETY: the invariants of the garbage collector ensures this is only executed when\n        // there are no remaining references to the inner data.\n        unsafe { (*self.data.get()).take() };\n    }\n}\n"
  },
  {
    "path": "core/gc/src/internals/gc_box.rs",
    "content": "use crate::Trace;\n\nuse super::{DropFn, GcHeader, RunFinalizerFn, TraceFn, TraceNonRootsFn, VTable, vtable_of};\n\n/// A garbage collected allocation.\n#[derive(Debug)]\n#[repr(C)]\npub struct GcBox<T: Trace + ?Sized + 'static> {\n    pub(crate) header: GcHeader,\n    pub(crate) vtable: &'static VTable,\n    value: T,\n}\n\nimpl<T: Trace> GcBox<T> {\n    /// Returns a new `GcBox` with a rooted `GcBoxHeader`.\n    pub(crate) fn new(value: T) -> Self {\n        let vtable = vtable_of::<T>();\n        Self {\n            header: GcHeader::new(),\n            vtable,\n            value,\n        }\n    }\n}\n\nimpl<T: Trace + ?Sized> GcBox<T> {\n    /// Returns a reference to the `GcBox`'s value.\n    pub(crate) const fn value(&self) -> &T {\n        &self.value\n    }\n\n    /// Returns `true` if the header is marked.\n    pub(crate) fn is_marked(&self) -> bool {\n        self.header.is_marked()\n    }\n\n    #[inline]\n    pub(crate) fn inc_ref_count(&self) {\n        self.header.inc_ref_count();\n    }\n\n    #[inline]\n    pub(crate) fn dec_ref_count(&self) {\n        self.header.dec_ref_count();\n    }\n\n    #[inline]\n    pub(crate) fn inc_non_root_count(&self) {\n        self.header.inc_non_root_count();\n    }\n\n    pub(crate) fn reset_non_root_count(&self) {\n        self.header.reset_non_root_count();\n    }\n\n    /// Check if the gc object is rooted.\n    ///\n    /// # Note\n    ///\n    /// This only gives valid result if the we have run through the\n    /// tracing non roots phase.\n    pub(crate) fn is_rooted(&self) -> bool {\n        self.header.is_rooted()\n    }\n\n    pub(crate) fn trace_fn(&self) -> TraceFn {\n        self.vtable.trace_fn()\n    }\n\n    pub(crate) fn trace_non_roots_fn(&self) -> TraceNonRootsFn {\n        self.vtable.trace_non_roots_fn()\n    }\n\n    pub(crate) fn run_finalizer_fn(&self) -> RunFinalizerFn {\n        self.vtable.run_finalizer_fn()\n    }\n\n    pub(crate) fn drop_fn(&self) -> DropFn {\n        self.vtable.drop_fn()\n    }\n\n    pub(crate) fn size(&self) -> usize {\n        self.vtable.size()\n    }\n}\n"
  },
  {
    "path": "core/gc/src/internals/gc_header.rs",
    "content": "use std::{cell::Cell, fmt};\n\nconst MARK_MASK: u32 = 1 << (u32::BITS - 1);\nconst NON_ROOTS_MASK: u32 = !MARK_MASK;\nconst NON_ROOTS_MAX: u32 = NON_ROOTS_MASK;\n\n/// The `Gcheader` contains the `GcBox`'s and `EphemeronBox`'s current state for the `Collector`'s\n/// Mark/Sweep as well as a pointer to the next node in the heap.\n///\n/// `ref_count` is the number of Gc instances, and `non_root_count` is the number of\n/// Gc instances in the heap. `non_root_count` also includes Mark Flag bit.\n///\n/// The next node is set by the `Allocator` during initialization and by the\n/// `Collector` during the sweep phase.\npub(crate) struct GcHeader {\n    ref_count: Cell<u32>,\n    non_root_count: Cell<u32>,\n}\n\nimpl GcHeader {\n    /// Creates a new [`GcHeader`] with a root of 1 and next set to None.\n    pub(crate) fn new() -> Self {\n        Self {\n            ref_count: Cell::new(1),\n            non_root_count: Cell::new(0),\n        }\n    }\n\n    /// Returns the [`GcHeader`]'s current ref count.\n    pub(crate) fn ref_count(&self) -> u32 {\n        self.ref_count.get()\n    }\n\n    /// Returns the [`GcHeader`]'s current non-roots count\n    pub(crate) fn non_root_count(&self) -> u32 {\n        self.non_root_count.get() & NON_ROOTS_MASK\n    }\n\n    /// Increments [`GcHeader`]'s non-roots count.\n    pub(crate) fn inc_non_root_count(&self) {\n        let non_root_count = self.non_root_count.get() & NON_ROOTS_MASK;\n\n        // `non_root_count` must not exceed `ref_count`.\n        // This prevents `is_rooted()` from returning false on live objects,\n        // which would cause a UAF.\n        // `inc_ref_count` caps `ref_count` at `NON_ROOTS_MAX`, ensuring\n        // `ref_count` is always reachable.\n        if non_root_count < self.ref_count.get() {\n            self.non_root_count\n                .set(self.non_root_count.get().wrapping_add(1));\n        } else {\n            // Saturated: `non_root_count` has reached `ref_count`.\n            // The debug assertion below catches corrupted state (non_root_count > ref_count),\n            // which is unreachable through this function but can occur via direct field writes\n            // in unsafe code or tests.\n            debug_assert_eq!(\n                non_root_count,\n                self.ref_count.get(),\n                \"non_root_count exceeded ref_count: state corruption detected \\\n                 (only reachable via direct field writes that bypass the saturation cap)\"\n            );\n        }\n    }\n\n    /// Decreases [`GcHeader`]'s current non-roots count.\n    pub(crate) fn reset_non_root_count(&self) {\n        self.non_root_count\n            .set(self.non_root_count.get() & !NON_ROOTS_MASK);\n    }\n\n    /// Returns a bool for whether [`GcHeader`]'s mark bit is 1.\n    pub(crate) fn is_marked(&self) -> bool {\n        self.non_root_count.get() & MARK_MASK != 0\n    }\n\n    pub(crate) fn inc_ref_count(&self) {\n        // Mark this as `cold` since the ref count will\n        // (almost) never overflow.\n        #[cold]\n        #[inline(never)]\n        fn overflow_panic() {\n            panic!(\"too many references to a gc allocation\");\n        }\n\n        let count = self.ref_count.get().wrapping_add(1);\n\n        // `non_root_count` shares storage with the mark bit (using 31 bits).\n        // A `ref_count` > `NON_ROOTS_MAX` would make `is_rooted()` always true,\n        // leaking memory. Treat this as a hard error identically to `u32` wrap.\n        // Check before writing to maintain a clean `ref_count` on `catch_unwind`.\n        if count == 0 || count > NON_ROOTS_MAX {\n            overflow_panic();\n        }\n\n        self.ref_count.set(count);\n    }\n\n    pub(crate) fn dec_ref_count(&self) {\n        self.ref_count.set(self.ref_count.get() - 1);\n    }\n\n    /// Check if the gc object is rooted.\n    ///\n    /// # Note\n    ///\n    /// This only gives valid result if the we have run through the\n    /// tracing non roots phase.\n    pub(crate) fn is_rooted(&self) -> bool {\n        self.non_root_count() < self.ref_count()\n    }\n\n    /// Sets [`GcHeader`]'s mark bit to 1.\n    pub(crate) fn mark(&self) {\n        self.non_root_count\n            .set(self.non_root_count.get() | MARK_MASK);\n    }\n\n    /// Sets [`GcHeader`]'s mark bit to 0.\n    pub(crate) fn unmark(&self) {\n        self.non_root_count\n            .set(self.non_root_count.get() & !MARK_MASK);\n    }\n}\n\nimpl fmt::Debug for GcHeader {\n    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {\n        f.debug_struct(\"GcHeader\")\n            .field(\"marked\", &self.is_marked())\n            .field(\"ref_count\", &self.ref_count.get())\n            .field(\"non_root_count\", &self.non_root_count())\n            .finish_non_exhaustive()\n    }\n}\n\n#[cfg(test)]\nmod tests {\n    use super::*;\n\n    #[test]\n    fn mark_bit_preserved() {\n        let header = GcHeader::new();\n        header.mark();\n        assert!(header.is_marked());\n\n        header.inc_non_root_count();\n        assert!(header.is_marked());\n        assert_eq!(header.non_root_count(), 1);\n\n        header.inc_non_root_count();\n        assert!(header.is_marked());\n        assert_eq!(header.non_root_count(), 1);\n    }\n\n    #[test]\n    fn reset_preserves_mark() {\n        let header = GcHeader::new();\n        header.inc_non_root_count();\n        header.mark();\n\n        header.reset_non_root_count();\n        assert_eq!(header.non_root_count(), 0);\n        assert!(header.is_marked());\n    }\n\n    #[test]\n    #[should_panic(expected = \"too many references to a gc allocation\")]\n    fn inc_ref_panics() {\n        let header = GcHeader::new();\n        header.ref_count.set(NON_ROOTS_MAX);\n\n        header.inc_ref_count();\n    }\n\n    #[test]\n    fn is_rooted_before_saturation() {\n        let header = GcHeader::new();\n        header.inc_ref_count();\n\n        header.inc_non_root_count();\n        assert!(header.is_rooted());\n\n        header.inc_non_root_count();\n        assert!(!header.is_rooted());\n    }\n\n    #[test]\n    fn saturation_at_higher_ref_count() {\n        let header = GcHeader::new();\n        header.inc_ref_count();\n        header.inc_ref_count();\n\n        header.inc_non_root_count();\n        header.inc_non_root_count();\n        header.inc_non_root_count(); // saturates at ref_count\n        header.inc_non_root_count(); // no-op\n        assert_eq!(header.non_root_count(), 3);\n        assert!(!header.is_rooted());\n    }\n\n    #[test]\n    fn unmark_preserves_non_root_count() {\n        let header = GcHeader::new();\n        header.inc_ref_count();\n        header.inc_non_root_count();\n        header.mark();\n        header.unmark();\n        assert_eq!(header.non_root_count(), 1);\n    }\n\n    /// Verifies `debug_assert!` panics if `inc_non_root_count` exceeds `ref_count`.\n    #[test]\n    #[cfg(debug_assertions)]\n    #[should_panic(expected = \"non_root_count exceeded ref_count: state corruption detected\")]\n    fn debug_assert_fires_when_non_root_exceeds_ref_count() {\n        let header = GcHeader::new();\n        // Corrupt the state to bypass the cap.\n        header.non_root_count.set(2);\n        header.inc_non_root_count(); // triggers debug_assert_eq!\n    }\n}\n"
  },
  {
    "path": "core/gc/src/internals/mod.rs",
    "content": "mod ephemeron_box;\nmod gc_box;\nmod gc_header;\nmod vtable;\nmod weak_map_box;\n\npub(crate) use self::ephemeron_box::{EphemeronBox, ErasedEphemeronBox};\npub(crate) use self::gc_header::GcHeader;\npub(crate) use self::weak_map_box::{ErasedWeakMapBox, WeakMapBox};\npub(crate) use vtable::{DropFn, RunFinalizerFn, TraceFn, TraceNonRootsFn, VTable, vtable_of};\n\npub use self::gc_box::GcBox;\n"
  },
  {
    "path": "core/gc/src/internals/vtable.rs",
    "content": "use std::any::TypeId;\n\nuse crate::{GcBox, GcErasedPointer, Trace, Tracer};\n\n// Workaround: https://users.rust-lang.org/t/custom-vtables-with-integers/78508\npub(crate) const fn vtable_of<T: Trace + 'static>() -> &'static VTable {\n    trait HasVTable: Trace + Sized + 'static {\n        const VTABLE: &'static VTable;\n\n        unsafe fn trace_fn(this: GcErasedPointer, tracer: &mut Tracer) {\n            // SAFETY: The caller must ensure that the passed erased pointer is `GcBox<Self>`.\n            let value = unsafe { this.cast::<GcBox<Self>>().as_ref().value() };\n\n            // SAFETY: The implementor must ensure that `trace` is correctly implemented.\n            unsafe {\n                Trace::trace(value, tracer);\n            }\n        }\n\n        unsafe fn trace_non_roots_fn(this: GcErasedPointer) {\n            // SAFETY: The caller must ensure that the passed erased pointer is `GcBox<Self>`.\n            let value = unsafe { this.cast::<GcBox<Self>>().as_ref().value() };\n\n            // SAFETY: The implementor must ensure that `trace_non_roots` is correctly implemented.\n            unsafe {\n                Self::trace_non_roots(value);\n            }\n        }\n\n        unsafe fn run_finalizer_fn(this: GcErasedPointer) {\n            // SAFETY: The caller must ensure that the passed erased pointer is `GcBox<Self>`.\n            let value = unsafe { this.cast::<GcBox<Self>>().as_ref().value() };\n\n            Self::run_finalizer(value);\n        }\n\n        // SAFETY: The caller must ensure that the passed erased pointer is `GcBox<Self>`.\n        unsafe fn drop_fn(this: GcErasedPointer) {\n            // SAFETY: The caller must ensure that the passed erased pointer is `GcBox<Self>`.\n            let this = this.cast::<GcBox<Self>>();\n\n            // SAFETY: The caller must ensure the erased pointer is not dropped or deallocated.\n            let _value = unsafe { Box::from_raw(this.as_ptr()) };\n        }\n    }\n\n    impl<T: Trace + 'static> HasVTable for T {\n        const VTABLE: &'static VTable = &VTable {\n            trace_fn: T::trace_fn,\n            trace_non_roots_fn: T::trace_non_roots_fn,\n            run_finalizer_fn: T::run_finalizer_fn,\n            drop_fn: T::drop_fn,\n            type_id: TypeId::of::<T>(),\n            size: size_of::<GcBox<T>>(),\n        };\n    }\n\n    T::VTABLE\n}\n\npub(crate) type TraceFn = unsafe fn(this: GcErasedPointer, tracer: &mut Tracer);\npub(crate) type TraceNonRootsFn = unsafe fn(this: GcErasedPointer);\npub(crate) type RunFinalizerFn = unsafe fn(this: GcErasedPointer);\npub(crate) type DropFn = unsafe fn(this: GcErasedPointer);\n\n#[derive(Debug)]\npub(crate) struct VTable {\n    trace_fn: TraceFn,\n    trace_non_roots_fn: TraceNonRootsFn,\n    run_finalizer_fn: RunFinalizerFn,\n    drop_fn: DropFn,\n    type_id: TypeId,\n    size: usize,\n}\n\nimpl VTable {\n    pub(crate) fn trace_fn(&self) -> TraceFn {\n        self.trace_fn\n    }\n\n    pub(crate) fn trace_non_roots_fn(&self) -> TraceNonRootsFn {\n        self.trace_non_roots_fn\n    }\n\n    pub(crate) fn run_finalizer_fn(&self) -> RunFinalizerFn {\n        self.run_finalizer_fn\n    }\n\n    pub(crate) fn drop_fn(&self) -> DropFn {\n        self.drop_fn\n    }\n\n    pub(crate) const fn type_id(&self) -> TypeId {\n        self.type_id\n    }\n\n    pub(crate) fn size(&self) -> usize {\n        self.size\n    }\n}\n"
  },
  {
    "path": "core/gc/src/internals/weak_map_box.rs",
    "content": "use crate::{GcRefCell, Trace, Tracer, WeakGc, pointers::RawWeakMap};\n\n/// A box that is used to track [`WeakMap`][`crate::WeakMap`]s.\npub(crate) struct WeakMapBox<K: Trace + ?Sized + 'static, V: Trace + Sized + 'static> {\n    pub(crate) map: WeakGc<GcRefCell<RawWeakMap<K, V>>>,\n}\n\n/// A trait that is used to erase the type of a [`WeakMapBox`].\npub(crate) trait ErasedWeakMapBox {\n    /// Clear dead entries from the [`WeakMapBox`].\n    fn clear_dead_entries(&self);\n\n    /// Returns `true` if the [`WeakMapBox`] is live.\n    fn is_live(&self) -> bool;\n\n    /// Traces the weak reference inside of the [`WeakMapBox`] if the weak map is live.\n    unsafe fn trace(&self, tracer: &mut Tracer);\n}\n\nimpl<K: Trace + ?Sized, V: Trace> ErasedWeakMapBox for WeakMapBox<K, V> {\n    fn clear_dead_entries(&self) {\n        if let Some(map) = self.map.upgrade()\n            && let Ok(mut map) = map.try_borrow_mut()\n        {\n            map.clear_expired();\n        }\n    }\n\n    fn is_live(&self) -> bool {\n        self.map.upgrade().is_some()\n    }\n\n    unsafe fn trace(&self, tracer: &mut Tracer) {\n        if self.map.upgrade().is_some() {\n            // SAFETY: When the weak map is live, the weak reference should be traced.\n            unsafe { self.map.trace(tracer) }\n        }\n    }\n}\n"
  },
  {
    "path": "core/gc/src/lib.rs",
    "content": "//! Boa's **`boa_gc`** crate implements a garbage collector.\n//!\n//! # Crate Overview\n//! **`boa_gc`** is a mark-sweep garbage collector that implements a [`Trace`] and [`Finalize`] trait\n//! for garbage collected values.\n#![doc = include_str!(\"../ABOUT.md\")]\n#![doc(\n    html_logo_url = \"https://raw.githubusercontent.com/boa-dev/boa/main/assets/logo_black.svg\",\n    html_favicon_url = \"https://raw.githubusercontent.com/boa-dev/boa/main/assets/logo_black.svg\"\n)]\n#![cfg_attr(not(test), forbid(clippy::unwrap_used))]\n#![allow(\n    clippy::module_name_repetitions,\n    clippy::redundant_pub_crate,\n    clippy::let_unit_value\n)]\n\nextern crate self as boa_gc;\n\nmod cell;\nmod pointers;\nmod trace;\n\npub(crate) mod internals;\n\nuse internals::{EphemeronBox, ErasedEphemeronBox, ErasedWeakMapBox, WeakMapBox};\nuse pointers::{NonTraceable, RawWeakMap};\nuse std::{\n    cell::{Cell, RefCell},\n    mem,\n    ptr::NonNull,\n};\n\npub use crate::trace::{Finalize, Trace, Tracer};\npub use boa_macros::{Finalize, Trace};\npub use cell::{GcRef, GcRefCell, GcRefMut};\npub use internals::GcBox;\npub use pointers::{Ephemeron, Gc, GcErased, WeakGc, WeakMap};\n\ntype GcErasedPointer = NonNull<GcBox<NonTraceable>>;\ntype EphemeronPointer = NonNull<dyn ErasedEphemeronBox>;\ntype ErasedWeakMapBoxPointer = NonNull<dyn ErasedWeakMapBox>;\n\nthread_local!(static GC_DROPPING: Cell<bool> = const { Cell::new(false) });\nthread_local!(static BOA_GC: RefCell<BoaGc> = RefCell::new( BoaGc {\n    config: GcConfig::default(),\n    runtime: GcRuntimeData::default(),\n    strongs: Vec::default(),\n    weaks: Vec::default(),\n    weak_maps: Vec::default(),\n}));\n\n#[derive(Debug, Clone, Copy)]\nstruct GcConfig {\n    /// The threshold at which the garbage collector will trigger a collection.\n    threshold: usize,\n    /// The percentage of used space at which the garbage collector will trigger a collection.\n    used_space_percentage: usize,\n}\n\n// Setting the defaults to an arbitrary value currently.\n//\n// TODO: Add a configure later\nimpl Default for GcConfig {\n    fn default() -> Self {\n        Self {\n            // Start at 1MB, the nursary size for V8 is ~1-8MB and SM can be up to 16MB\n            threshold: 1_048_576,\n            used_space_percentage: 70,\n        }\n    }\n}\n\n#[derive(Default, Debug, Clone, Copy)]\nstruct GcRuntimeData {\n    collections: usize,\n    bytes_allocated: usize,\n}\n\n#[derive(Debug)]\nstruct BoaGc {\n    config: GcConfig,\n    runtime: GcRuntimeData,\n    strongs: Vec<GcErasedPointer>,\n    weaks: Vec<EphemeronPointer>,\n    weak_maps: Vec<ErasedWeakMapBoxPointer>,\n}\n\nimpl Drop for BoaGc {\n    fn drop(&mut self) {\n        Collector::dump(self);\n    }\n}\n\n// Whether or not the thread is currently in the sweep phase of garbage collection.\n// During this phase, attempts to dereference a `Gc<T>` pointer will trigger a panic.\n/// `DropGuard` flags whether the Collector is currently running `Collector::sweep()` or `Collector::dump()`\n///\n/// While the `DropGuard` is active, all `GcBox`s must not be dereferenced or accessed as it could cause Undefined Behavior\n#[derive(Debug, Clone)]\nstruct DropGuard;\n\nimpl DropGuard {\n    fn new() -> Self {\n        GC_DROPPING.with(|dropping| dropping.set(true));\n        Self\n    }\n}\n\nimpl Drop for DropGuard {\n    fn drop(&mut self) {\n        GC_DROPPING.with(|dropping| dropping.set(false));\n    }\n}\n\n/// Returns `true` if it is safe for a type to run [`Finalize::finalize`].\n#[must_use]\n#[inline]\npub fn finalizer_safe() -> bool {\n    GC_DROPPING.with(|dropping| !dropping.get())\n}\n\n/// The Allocator handles allocation of garbage collected values.\n///\n/// The allocator can trigger a garbage collection.\n#[derive(Debug, Clone, Copy)]\nstruct Allocator;\n\nimpl Allocator {\n    /// Allocate a new garbage collected value to the Garbage Collector's heap.\n    fn alloc_gc<T: Trace>(value: GcBox<T>) -> NonNull<GcBox<T>> {\n        let element_size = size_of_val::<GcBox<T>>(&value);\n        BOA_GC.with(|st| {\n            let mut gc = st.borrow_mut();\n\n            Self::manage_state(&mut gc);\n            // Safety: value cannot be a null pointer, since `Box` cannot return null pointers.\n            let ptr = unsafe { NonNull::new_unchecked(Box::into_raw(Box::new(value))) };\n            let erased: NonNull<GcBox<NonTraceable>> = ptr.cast();\n\n            gc.strongs.push(erased);\n            gc.runtime.bytes_allocated += element_size;\n\n            ptr\n        })\n    }\n\n    fn alloc_ephemeron<K: Trace + ?Sized, V: Trace>(\n        value: EphemeronBox<K, V>,\n    ) -> NonNull<EphemeronBox<K, V>> {\n        let element_size = size_of_val::<EphemeronBox<K, V>>(&value);\n        BOA_GC.with(|st| {\n            let mut gc = st.borrow_mut();\n\n            Self::manage_state(&mut gc);\n            // Safety: value cannot be a null pointer, since `Box` cannot return null pointers.\n            let ptr = unsafe { NonNull::new_unchecked(Box::into_raw(Box::new(value))) };\n            let erased: NonNull<dyn ErasedEphemeronBox> = ptr;\n\n            gc.weaks.push(erased);\n            gc.runtime.bytes_allocated += element_size;\n\n            ptr\n        })\n    }\n\n    fn alloc_weak_map<K: Trace + ?Sized, V: Trace>() -> WeakMap<K, V> {\n        let weak_map = WeakMap {\n            inner: Gc::new(GcRefCell::new(RawWeakMap::new())),\n        };\n        let weak = WeakGc::new(&weak_map.inner);\n\n        BOA_GC.with(|st| {\n            let mut gc = st.borrow_mut();\n\n            let weak_box = WeakMapBox { map: weak };\n\n            // Safety: value cannot be a null pointer, since `Box` cannot return null pointers.\n            let ptr = unsafe { NonNull::new_unchecked(Box::into_raw(Box::new(weak_box))) };\n            let erased: ErasedWeakMapBoxPointer = ptr;\n\n            gc.weak_maps.push(erased);\n\n            weak_map\n        })\n    }\n\n    fn manage_state(gc: &mut BoaGc) {\n        if gc.runtime.bytes_allocated > gc.config.threshold {\n            Collector::collect(gc);\n\n            // Post collection check\n            // If the allocated bytes are still above the threshold, increase the threshold.\n            if gc.runtime.bytes_allocated\n                > gc.config.threshold / 100 * gc.config.used_space_percentage\n            {\n                gc.config.threshold =\n                    gc.runtime.bytes_allocated / gc.config.used_space_percentage * 100;\n            }\n        }\n    }\n}\n\nstruct Unreachables {\n    strong: Vec<GcErasedPointer>,\n    weak: Vec<NonNull<dyn ErasedEphemeronBox>>,\n}\n\n/// This collector currently functions in four main phases\n///\n/// Mark -> Finalize -> Mark -> Sweep\n///\n/// 1. Mark nodes as reachable.\n/// 2. Finalize the unreachable nodes.\n/// 3. Mark again because `Finalize::finalize` can potentially resurrect dead nodes.\n/// 4. Sweep and drop all dead nodes.\n///\n/// A better approach in a more concurrent structure may be to reorder.\n///\n/// Mark -> Sweep -> Finalize\nstruct Collector;\n\nimpl Collector {\n    /// Run a collection on the full heap.\n    fn collect(gc: &mut BoaGc) {\n        gc.runtime.collections += 1;\n\n        Self::trace_non_roots(gc);\n\n        let mut tracer = Tracer::new();\n\n        let unreachables = Self::mark_heap(&mut tracer, &gc.strongs, &gc.weaks, &gc.weak_maps);\n\n        assert!(tracer.is_empty(), \"The queue should be empty\");\n\n        // Only finalize if there are any unreachable nodes.\n        if !unreachables.strong.is_empty() || !unreachables.weak.is_empty() {\n            // Finalize all the unreachable nodes.\n            // SAFETY: All passed pointers are valid, since we won't deallocate until `Self::sweep`.\n            unsafe { Self::finalize(unreachables) };\n\n            // Reuse the tracer's already allocated capacity.\n            let _final_unreachables =\n                Self::mark_heap(&mut tracer, &gc.strongs, &gc.weaks, &gc.weak_maps);\n        }\n\n        // SAFETY: The head of our linked list is always valid per the invariants of our GC.\n        unsafe {\n            Self::sweep(\n                &mut gc.strongs,\n                &mut gc.weaks,\n                &mut gc.runtime.bytes_allocated,\n            );\n        }\n\n        // Weak maps have to be cleared after the sweep, since the process dereferences GcBoxes.\n        gc.weak_maps.retain(|w| {\n            // SAFETY: The caller must ensure the validity of every node of `heap_start`.\n            let node_ref = unsafe { w.as_ref() };\n\n            if node_ref.is_live() {\n                node_ref.clear_dead_entries();\n\n                true\n            } else {\n                // SAFETY:\n                // The `Allocator` must always ensure its start node is a valid, non-null pointer that\n                // was allocated by `Box::from_raw(Box::new(..))`.\n                let _unmarked_node = unsafe { Box::from_raw(w.as_ptr()) };\n\n                false\n            }\n        });\n\n        gc.strongs.shrink_to(gc.strongs.len() >> 2);\n        gc.weaks.shrink_to(gc.weaks.len() >> 2);\n        gc.weak_maps.shrink_to(gc.weak_maps.len() >> 2);\n    }\n\n    fn trace_non_roots(gc: &BoaGc) {\n        // Count all the handles located in GC heap.\n        // Then, we can find whether there is a reference from other places, and they are the roots.\n        for node in &gc.strongs {\n            // SAFETY: node must be valid as this phase cannot drop any node.\n            let trace_non_roots_fn = unsafe { node.as_ref() }.trace_non_roots_fn();\n\n            // SAFETY: The function pointer is appropriate for this node type because we extract it from it's VTable.\n            unsafe {\n                trace_non_roots_fn(*node);\n            }\n        }\n\n        for eph in &gc.weaks {\n            // SAFETY: node must be valid as this phase cannot drop any node.\n            let eph_ref = unsafe { eph.as_ref() };\n            eph_ref.trace_non_roots();\n        }\n    }\n\n    /// Walk the heap and mark any nodes deemed reachable\n    fn mark_heap(\n        tracer: &mut Tracer,\n        strongs: &[GcErasedPointer],\n        weaks: &[EphemeronPointer],\n        weak_maps: &[ErasedWeakMapBoxPointer],\n    ) -> Unreachables {\n        // Walk the list, tracing and marking the nodes\n        let mut strong_dead = Vec::new();\n        let mut pending_ephemerons = Vec::new();\n\n        // === Preliminary mark phase ===\n        //\n        // 0. Get the naive list of possibly dead nodes.\n        for node in strongs {\n            // SAFETY: node must be valid as this phase cannot drop any node.\n            let node_ref = unsafe { node.as_ref() };\n            if node_ref.is_rooted() {\n                tracer.enqueue(*node);\n\n                // SAFETY: all nodes must be valid as this phase cannot drop any node.\n                unsafe {\n                    tracer.trace_until_empty();\n                }\n            } else if !node_ref.is_marked() {\n                strong_dead.push(*node);\n            }\n        }\n\n        // 0.1. Early return if there are no ephemerons in the GC\n        if weaks.is_empty() {\n            strong_dead.retain_mut(|node| {\n                // SAFETY: node must be valid as this phase cannot drop any node.\n                unsafe { !node.as_ref().is_marked() }\n            });\n            return Unreachables {\n                strong: strong_dead,\n                weak: Vec::new(),\n            };\n        }\n\n        // === Weak mark phase ===\n        //\n        //\n        // 1. Get the naive list of ephemerons that are supposedly dead or their key is dead and\n        // trace all the ephemerons that have roots and their keys are live. Also remove from\n        // this list the ephemerons that are marked but their value is dead.\n        for eph in weaks {\n            // SAFETY: node must be valid as this phase cannot drop any node.\n            let eph_ref = unsafe { eph.as_ref() };\n            let header = eph_ref.header();\n            if header.is_rooted() {\n                header.mark();\n            }\n            // SAFETY: the garbage collector ensures `eph_ref` always points to valid data.\n            if unsafe { !eph_ref.trace(tracer) } {\n                pending_ephemerons.push(*eph);\n            }\n\n            // SAFETY: all nodes must be valid as this phase cannot drop any node.\n            unsafe {\n                tracer.trace_until_empty();\n            }\n        }\n\n        // 2. Trace all the weak pointers in the live weak maps to make sure they do not get swept.\n        for w in weak_maps {\n            // SAFETY: node must be valid as this phase cannot drop any node.\n            let node_ref = unsafe { w.as_ref() };\n\n            // SAFETY: The garbage collector ensures that all nodes are valid.\n            unsafe { node_ref.trace(tracer) };\n\n            // SAFETY: all nodes must be valid as this phase cannot drop any node.\n            unsafe {\n                tracer.trace_until_empty();\n            }\n        }\n\n        // 3. Iterate through all pending ephemerons, removing the ones which have been successfully\n        // traced. If there are no changes in the pending ephemerons list, it means that there are no\n        // more reachable ephemerons from the remaining ephemeron values.\n        let mut previous_len = pending_ephemerons.len();\n        loop {\n            pending_ephemerons.retain_mut(|eph| {\n                // SAFETY: node must be valid as this phase cannot drop any node.\n                let eph_ref = unsafe { eph.as_ref() };\n                // SAFETY: the garbage collector ensures `eph_ref` always points to valid data.\n                let is_key_marked = unsafe { !eph_ref.trace(tracer) };\n\n                // SAFETY: all nodes must be valid as this phase cannot drop any node.\n                unsafe {\n                    tracer.trace_until_empty();\n                }\n\n                is_key_marked\n            });\n\n            if previous_len == pending_ephemerons.len() {\n                break;\n            }\n\n            previous_len = pending_ephemerons.len();\n        }\n\n        // 4. The remaining list should contain the ephemerons that are either unreachable or its key\n        // is dead. Cleanup the strong pointers since this procedure could have marked some more strong\n        // pointers.\n        strong_dead.retain_mut(|node| {\n            // SAFETY: node must be valid as this phase cannot drop any node.\n            unsafe { !node.as_ref().is_marked() }\n        });\n\n        Unreachables {\n            strong: strong_dead,\n            weak: pending_ephemerons,\n        }\n    }\n\n    /// # Safety\n    ///\n    /// Passing a `strong` or a `weak` vec with invalid pointers will result in Undefined Behaviour.\n    unsafe fn finalize(unreachables: Unreachables) {\n        for node in unreachables.strong {\n            // SAFETY: The caller must ensure all pointers inside `unreachables.strong` are valid.\n            let node_ref = unsafe { node.as_ref() };\n            let run_finalizer_fn = node_ref.run_finalizer_fn();\n\n            // SAFETY: The function pointer is appropriate for this node type because we extract it from it's VTable.\n            unsafe {\n                run_finalizer_fn(node);\n            }\n        }\n        for node in unreachables.weak {\n            // SAFETY: The caller must ensure all pointers inside `unreachables.weak` are valid.\n            let node = unsafe { node.as_ref() };\n            node.finalize_and_clear();\n        }\n    }\n\n    /// # Safety\n    ///\n    /// - Providing an invalid pointer in the `heap_start` or in any of the headers of each\n    ///   node will result in Undefined Behaviour.\n    /// - Providing a list of pointers that weren't allocated by `Box::into_raw(Box::new(..))`\n    ///   will result in Undefined Behaviour.\n    unsafe fn sweep(\n        strong: &mut Vec<GcErasedPointer>,\n        weak: &mut Vec<EphemeronPointer>,\n        total_allocated: &mut usize,\n    ) {\n        let _guard = DropGuard::new();\n\n        strong.retain(|node| {\n            // SAFETY: The caller must ensure the validity of every node of `heap_start`.\n            let node_ref = unsafe { node.as_ref() };\n            if node_ref.is_marked() {\n                node_ref.header.unmark();\n                node_ref.reset_non_root_count();\n\n                true\n            } else {\n                // SAFETY: The algorithm ensures only unmarked/unreachable pointers are dropped.\n                // The caller must ensure all pointers were allocated by `Box::into_raw(Box::new(..))`.\n                let drop_fn = node_ref.drop_fn();\n                let size = node_ref.size();\n                *total_allocated -= size;\n\n                // SAFETY: The function pointer is appropriate for this node type because we extract it from it's VTable.\n                unsafe {\n                    drop_fn(*node);\n                }\n\n                false\n            }\n        });\n\n        weak.retain(|eph| {\n            // SAFETY: The caller must ensure the validity of every node of `heap_start`.\n            let eph_ref = unsafe { eph.as_ref() };\n            let header = eph_ref.header();\n            if header.is_marked() {\n                header.unmark();\n                header.reset_non_root_count();\n\n                true\n            } else {\n                // SAFETY: The algorithm ensures only unmarked/unreachable pointers are dropped.\n                // The caller must ensure all pointers were allocated by `Box::into_raw(Box::new(..))`.\n                let unmarked_eph = unsafe { Box::from_raw(eph.as_ptr()) };\n                let unallocated_bytes = size_of_val(&*unmarked_eph);\n                *total_allocated -= unallocated_bytes;\n\n                false\n            }\n        });\n    }\n\n    // Clean up the heap when BoaGc is dropped\n    fn dump(gc: &mut BoaGc) {\n        // Weak maps have to be dropped first, since the process dereferences GcBoxes.\n        // This can be done without initializing a dropguard since no GcBox's are being dropped.\n        for node in mem::take(&mut gc.weak_maps) {\n            // SAFETY:\n            // The `Allocator` must always ensure its start node is a valid, non-null pointer that\n            // was allocated by `Box::from_raw(Box::new(..))`.\n            let _unmarked_node = unsafe { Box::from_raw(node.as_ptr()) };\n        }\n\n        // Not initializing a dropguard since this should only be invoked when BOA_GC is being dropped.\n        let _guard = DropGuard::new();\n\n        for node in mem::take(&mut gc.strongs) {\n            // SAFETY:\n            // The `Allocator` must always ensure its start node is a valid, non-null pointer that\n            // was allocated by `Box::from_raw(Box::new(..))`.\n            let drop_fn = unsafe { node.as_ref() }.drop_fn();\n\n            // SAFETY: The function pointer is appropriate for this node type because we extract it from it's VTable.\n            unsafe {\n                drop_fn(node);\n            }\n        }\n\n        for node in mem::take(&mut gc.weaks) {\n            // SAFETY:\n            // The `Allocator` must always ensure its start node is a valid, non-null pointer that\n            // was allocated by `Box::from_raw(Box::new(..))`.\n            let _unmarked_node = unsafe { Box::from_raw(node.as_ptr()) };\n        }\n    }\n}\n\n/// Forcefully runs a garbage collection of all inaccessible nodes.\npub fn force_collect() {\n    BOA_GC.with(|current| {\n        let mut gc = current.borrow_mut();\n\n        if gc.runtime.bytes_allocated > 0 {\n            Collector::collect(&mut gc);\n        }\n    });\n}\n\n#[cfg(test)]\nmod test;\n\n/// Returns `true` is any weak maps are currently allocated.\n#[cfg(test)]\n#[must_use]\npub fn has_weak_maps() -> bool {\n    BOA_GC.with(|current| {\n        let gc = current.borrow();\n\n        !gc.weak_maps.is_empty()\n    })\n}\n"
  },
  {
    "path": "core/gc/src/pointers/ephemeron.rs",
    "content": "#![allow(clippy::doc_link_with_quotes)]\n\nuse crate::{\n    Allocator, Gc, Tracer, finalizer_safe,\n    internals::EphemeronBox,\n    trace::{Finalize, Trace},\n};\nuse std::{ops::Deref, ptr::NonNull};\n\n/// A reference to the value held by an [`Ephemeron`].\n///\n/// This reference can be thought as a `(Gc<K>, &V)` pair, where the\n/// returned `Gc<K>` ensures that the reference `&V` is always valid until\n/// the `Gc<K>` gets dropped.\n#[derive(Debug)]\npub struct EphemeronValueRef<'a, K: Trace + ?Sized + 'static, V> {\n    // Only required to maintain the reference `&V` alive.\n    _key: Gc<K>,\n    value: &'a V,\n}\n\nimpl<K: Trace + ?Sized, V> Deref for EphemeronValueRef<'_, K, V> {\n    type Target = V;\n\n    fn deref(&self) -> &Self::Target {\n        self.value\n    }\n}\n\n/// A key-value pair where the value becomes inaccessible when the key is garbage collected.\n///\n/// You can read more about ephemerons on:\n/// - Racket's page about [**ephemerons**][eph], which gives a brief overview.\n/// - Barry Hayes' paper [\"_Ephemerons_: a new finalization mechanism\"][acm] which explains the topic\n///   in full detail.\n///\n///\n/// [eph]: https://docs.racket-lang.org/reference/ephemerons.html\n/// [acm]: https://dl.acm.org/doi/10.1145/263700.263733\n#[derive(Debug)]\npub struct Ephemeron<K: Trace + ?Sized + 'static, V: Trace + 'static> {\n    inner_ptr: NonNull<EphemeronBox<K, V>>,\n}\n\nimpl<K: Trace + ?Sized, V: Trace> Ephemeron<K, V> {\n    /// Creates a new `Ephemeron`.\n    #[must_use]\n    pub fn new(key: &Gc<K>, value: V) -> Self {\n        let inner_ptr = Allocator::alloc_ephemeron(EphemeronBox::new(key, value));\n        Self { inner_ptr }\n    }\n\n    /// Gets the stored key of this `Ephemeron`, or `None` if the key was already garbage collected.\n    #[inline]\n    #[must_use]\n    pub fn key(&self) -> Option<Gc<K>> {\n        // SAFETY: this is safe because `Ephemeron` is tracked to always point to a valid pointer\n        // `inner_ptr`.\n        let key_ptr = unsafe { self.inner_ptr.as_ref().key_ptr() }?;\n\n        // SAFETY: Returned pointer is valid, so this is safe.\n        unsafe {\n            key_ptr.as_ref().inc_ref_count();\n        }\n\n        // SAFETY: The gc pointer's reference count has been incremented, so this is safe.\n        Some(unsafe { Gc::from_raw(key_ptr) })\n    }\n\n    /// Gets the stored value of this `Ephemeron`, or `None` if the key was already garbage collected.\n    #[must_use]\n    pub fn value(&self) -> Option<EphemeronValueRef<'_, K, V>> {\n        let key = self.key()?;\n\n        // SAFETY: this is safe because `Ephemeron` is tracked to always point to a valid pointer\n        // `inner_ptr`.\n        let value = unsafe { self.inner_ptr.as_ref().value()? };\n\n        Some(EphemeronValueRef { _key: key, value })\n    }\n\n    /// Checks if the [`Ephemeron`] has a value.\n    #[must_use]\n    pub fn has_value(&self) -> bool {\n        // SAFETY: this is safe because `Ephemeron` is tracked to always point to a valid pointer\n        // `inner_ptr`.\n        unsafe { self.inner_ptr.as_ref().value().is_some() }\n    }\n\n    /// Returns `true` if the two `Ephemeron`s point to the same allocation.\n    #[must_use]\n    pub fn ptr_eq(this: &Self, other: &Self) -> bool {\n        std::ptr::addr_eq(this.inner(), other.inner())\n    }\n\n    pub(crate) fn inner_ptr(&self) -> NonNull<EphemeronBox<K, V>> {\n        assert!(finalizer_safe());\n        self.inner_ptr\n    }\n\n    pub(crate) fn inner(&self) -> &EphemeronBox<K, V> {\n        // SAFETY: Please see Gc::inner_ptr()\n        unsafe { self.inner_ptr().as_ref() }\n    }\n\n    /// Constructs an `Ephemeron<K, V>` from a raw pointer.\n    ///\n    /// # Safety\n    ///\n    /// This function is unsafe because improper use may lead to memory corruption, double-free,\n    /// or misbehaviour of the garbage collector.\n    #[must_use]\n    pub(crate) const unsafe fn from_raw(inner_ptr: NonNull<EphemeronBox<K, V>>) -> Self {\n        Self { inner_ptr }\n    }\n}\n\nimpl<K: Trace + ?Sized, V: Trace> Finalize for Ephemeron<K, V> {\n    fn finalize(&self) {\n        // SAFETY: inner_ptr should be alive when calling finalize.\n        // We don't call inner_ptr() to avoid overhead of calling finalizer_safe().\n        unsafe {\n            self.inner_ptr.as_ref().dec_ref_count();\n        }\n    }\n}\n\n// SAFETY: `Ephemeron`s trace implementation only marks its inner box because we want to stop\n// tracing through weakly held pointers.\nunsafe impl<K: Trace + ?Sized, V: Trace> Trace for Ephemeron<K, V> {\n    unsafe fn trace(&self, _tracer: &mut Tracer) {\n        // SAFETY: We need to mark the inner box of the `Ephemeron` since it is reachable\n        // from a root and this means it cannot be dropped.\n        unsafe {\n            self.inner().mark();\n        }\n    }\n\n    unsafe fn trace_non_roots(&self) {\n        self.inner().inc_non_root_count();\n    }\n\n    fn run_finalizer(&self) {\n        Finalize::finalize(self);\n    }\n}\n\nimpl<K: Trace + ?Sized, V: Trace> Clone for Ephemeron<K, V> {\n    fn clone(&self) -> Self {\n        let ptr = self.inner_ptr();\n        self.inner().inc_ref_count();\n        // SAFETY: `&self` is a valid Ephemeron pointer.\n        unsafe { Self::from_raw(ptr) }\n    }\n}\n\nimpl<K: Trace + ?Sized, V: Trace> Drop for Ephemeron<K, V> {\n    fn drop(&mut self) {\n        if finalizer_safe() {\n            Finalize::finalize(self);\n        }\n    }\n}\n"
  },
  {
    "path": "core/gc/src/pointers/gc.rs",
    "content": "use crate::{\n    Allocator, Ephemeron, GcErasedPointer, Tracer, WeakGc, custom_trace, finalizer_safe,\n    internals::{EphemeronBox, GcBox, VTable},\n    trace::{Finalize, Trace},\n};\nuse std::{\n    any::TypeId,\n    cmp::Ordering,\n    fmt::{self, Debug, Display},\n    hash::{Hash, Hasher},\n    marker::PhantomData,\n    ops::Deref,\n    ptr::NonNull,\n    rc::Rc,\n};\n\n/// Zero sized struct that is used to ensure that we do not call trace methods,\n/// call its finalization method or drop it.\n///\n/// This can only happen if we are accessing a [`GcErasedPointer`] directly which is a bug.\n/// Panics if any of it's methods are called.\n///\n/// Note: Accessing the [`crate::internals::GcHeader`] of [`GcErasedPointer`] is fine.\npub(crate) struct NonTraceable(());\n\nimpl Finalize for NonTraceable {\n    fn finalize(&self) {\n        unreachable!()\n    }\n}\n\nunsafe impl Trace for NonTraceable {\n    unsafe fn trace(&self, _tracer: &mut Tracer) {\n        unreachable!()\n    }\n    unsafe fn trace_non_roots(&self) {\n        unreachable!()\n    }\n    fn run_finalizer(&self) {\n        unreachable!()\n    }\n}\n\nimpl Drop for NonTraceable {\n    fn drop(&mut self) {\n        unreachable!()\n    }\n}\n\n/// A type erased [`Gc<T>`] pointer type.\n#[repr(transparent)]\npub struct GcErased {\n    inner: Gc<NonTraceable>,\n}\n\nimpl GcErased {\n    /// Convert a [`Gc<T>`] into a type erased [`GcErased`].\n    #[inline]\n    #[must_use]\n    pub fn new<T: Trace>(gc: Gc<T>) -> Self {\n        let inner_ptr = gc.inner_ptr;\n        std::mem::forget(gc);\n\n        Self {\n            inner: Gc {\n                inner_ptr: inner_ptr.cast(),\n                marker: PhantomData,\n            },\n        }\n    }\n\n    /// Returns `true` if the two [`GcErased`]s point to the same allocation.\n    #[must_use]\n    pub fn ptr_eq(this: &Self, other: &Self) -> bool {\n        Gc::ptr_eq(&this.inner, &other.inner)\n    }\n\n    /// Returns the [`TypeId`] of the inner type.\n    #[inline]\n    #[must_use]\n    pub fn type_id(&self) -> TypeId {\n        Gc::type_id(&self.inner)\n    }\n\n    /// Returns true if the inner type is the same as `T`.\n    #[inline]\n    #[must_use]\n    pub fn is<T: Trace + 'static>(&self) -> bool {\n        Gc::is::<T>(&self.inner)\n    }\n\n    /// Returns [`Some`] `Gc<T>` if it is of type `T`, or [`None`] if it isn’t.\n    #[inline]\n    #[must_use]\n    pub fn downcast<T: Trace + 'static>(self) -> Option<Gc<T>> {\n        Gc::downcast::<T>(self.inner)\n    }\n\n    /// Downcast the inner value of type `T`.\n    ///\n    /// # Safety\n    ///\n    /// The caller must ensure that the cast is valid.\n    #[inline]\n    #[must_use]\n    pub unsafe fn downcast_unchecked<T: Trace + 'static>(self) -> Gc<T> {\n        // SAFETY: It's the callers responsibility to make sure this is valid.\n        unsafe { Gc::cast_unchecked::<T>(self.inner) }\n    }\n\n    /// Returns reference to the inner value of type `T`.\n    ///\n    /// # Safety\n    ///\n    /// The caller must ensure that the cast is valid.\n    #[inline]\n    #[must_use]\n    pub unsafe fn downcast_ref_unchecked<T: Trace + 'static>(&self) -> &Gc<T> {\n        // SAFETY: It's the callers responsibility to make sure this is valid.\n        unsafe { Gc::cast_ref_unchecked::<T>(&self.inner) }\n    }\n}\n\nimpl Debug for GcErased {\n    #[inline]\n    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {\n        f.debug_struct(\"GcErased\")\n            .field(\"inner\", &self.inner.inner_ptr)\n            .finish()\n    }\n}\n\nimpl Finalize for GcErased {\n    fn finalize(&self) {}\n}\n\n// SAFETY: We only have one transparent field in GcErased that needs trace,\n//         so this is safe.\nunsafe impl Trace for GcErased {\n    custom_trace!(this, mark, {\n        mark(&this.inner);\n    });\n}\n\nimpl Clone for GcErased {\n    #[inline]\n    fn clone(&self) -> Self {\n        Self {\n            inner: self.inner.clone(),\n        }\n    }\n}\n\n/// A garbage-collected pointer type over an immutable value.\npub struct Gc<T: Trace + ?Sized + 'static> {\n    pub(crate) inner_ptr: NonNull<GcBox<T>>,\n    pub(crate) marker: PhantomData<Rc<T>>,\n}\n\nimpl<T: Trace + ?Sized> Gc<T> {\n    /// Constructs a new `Gc<T>` with the given value.\n    #[must_use]\n    pub fn new(value: T) -> Self\n    where\n        T: Sized,\n    {\n        // Create GcBox and allocate it to heap.\n        //\n        // Note: Allocator can cause Collector to run\n        let inner_ptr = Allocator::alloc_gc(GcBox::new(value));\n\n        Self {\n            inner_ptr,\n            marker: PhantomData,\n        }\n    }\n\n    /// Constructs a new `Gc<T>` while giving you a `WeakGc<T>` to the allocation, to allow\n    /// constructing a T which holds a weak pointer to itself.\n    ///\n    /// Since the new `Gc<T>` is not fully-constructed until `Gc<T>::new_cyclic` returns, calling\n    /// [`upgrade`][WeakGc::upgrade]  on the weak reference inside the closure will fail and result\n    /// in a `None` value.\n    #[must_use]\n    pub fn new_cyclic<F>(data_fn: F) -> Self\n    where\n        F: FnOnce(&WeakGc<T>) -> T,\n        T: Sized,\n    {\n        // SAFETY: The newly allocated ephemeron is only live here, meaning `Ephemeron` is the\n        // sole owner of the allocation after passing it to `from_raw`, making this operation safe.\n        let weak = unsafe {\n            Ephemeron::from_raw(Allocator::alloc_ephemeron(EphemeronBox::new_empty())).into()\n        };\n\n        let gc = Self::new(data_fn(&weak));\n\n        // SAFETY:\n        // - `as_mut`: `weak` is properly initialized by `alloc_ephemeron` and cannot escape the\n        //   `unsafe` block.\n        // - `set_kv`: `weak` is a newly created `EphemeronBox`, meaning it isn't possible to\n        //   collect it since `weak` is still live.\n        unsafe { weak.inner().inner_ptr().as_mut().set(&gc, ()) }\n\n        gc\n    }\n\n    /// Consumes the `Gc`, returning a wrapped raw pointer.\n    ///\n    /// To avoid a memory leak, the pointer must be converted back to a `Gc` using [`Gc::from_raw`].\n    #[must_use]\n    pub fn into_raw(this: Self) -> NonNull<GcBox<T>> {\n        let ptr = this.inner_ptr();\n        std::mem::forget(this);\n        ptr\n    }\n\n    /// Returns `true` if the two `Gc`s point to the same allocation.\n    #[must_use]\n    pub fn ptr_eq<U: Trace + ?Sized>(this: &Self, other: &Gc<U>) -> bool {\n        std::ptr::addr_eq(this.inner(), other.inner())\n    }\n\n    /// Constructs a `Gc<T>` from a raw pointer.\n    ///\n    /// The raw pointer must have been returned by a previous call to [`Gc<U>::into_raw`][Gc::into_raw]\n    /// where `U` must have the same size and alignment as `T`.\n    ///\n    /// # Safety\n    ///\n    /// This function is unsafe because improper use may lead to memory corruption, double-free,\n    /// or misbehaviour of the garbage collector.\n    #[must_use]\n    pub const unsafe fn from_raw(inner_ptr: NonNull<GcBox<T>>) -> Self {\n        Self {\n            inner_ptr,\n            marker: PhantomData,\n        }\n    }\n\n    pub(crate) fn as_erased_pointer(&self) -> GcErasedPointer {\n        self.inner_ptr.cast()\n    }\n\n    /// Return the [`TypeId`] of the `T`.\n    #[inline]\n    #[must_use]\n    pub fn type_id(this: &Self) -> TypeId {\n        this.vtable().type_id()\n    }\n\n    /// Returns true if the inner type is the same as `T`.\n    #[inline]\n    #[must_use]\n    pub fn is<U: Trace + 'static>(this: &Self) -> bool {\n        Gc::type_id(this) == TypeId::of::<U>()\n    }\n\n    /// Returns [`Some`] reference to the inner value if it is of type `T`, or [`None`] if it isn’t.\n    #[inline]\n    #[must_use]\n    pub fn downcast<U: Trace + 'static>(this: Self) -> Option<Gc<U>> {\n        if !Gc::is::<U>(&this) {\n            return None;\n        }\n\n        // SAFETY: We check that the type is correct above, so this is safe.\n        Some(unsafe { Gc::cast_unchecked::<U>(this) })\n    }\n\n    /// Returns reference to the inner value of type `T`.\n    ///\n    /// # Safety\n    ///\n    /// The caller must ensure that the cast is valid.\n    #[inline]\n    #[must_use]\n    pub unsafe fn cast_unchecked<U: Trace + 'static>(this: Self) -> Gc<U> {\n        let inner_ptr = this.inner_ptr.cast::<U>();\n        core::mem::forget(this); // Prevents double free.\n        Gc {\n            inner_ptr: inner_ptr.cast(),\n            marker: PhantomData,\n        }\n    }\n\n    /// Returns reference to the inner value of type `T`.\n    ///\n    /// # Safety\n    ///\n    /// The caller must ensure that the cast is valid.\n    #[inline]\n    #[must_use]\n    pub unsafe fn cast_ref_unchecked<U: Trace + 'static>(this: &Self) -> &Gc<U> {\n        // SAFETY: Casting a Gc<T> to a Gc<U> of any type is safe, as long as you don’t actually access it as a U.\n        //         The correct functions for T will still be called during tracing, finalization, and dropping.\n        unsafe { &(*(&raw const *this).cast::<Gc<U>>()) }\n    }\n}\n\nimpl<T: Trace + ?Sized> Gc<T> {\n    pub(crate) fn vtable(&self) -> &'static VTable {\n        // SAFETY: The inner pointer is valid at all times.\n        unsafe { self.inner_ptr.as_ref() }.vtable\n    }\n\n    #[inline(always)]\n    #[allow(clippy::inline_always)]\n    pub(crate) fn inner_ptr(&self) -> NonNull<GcBox<T>> {\n        debug_assert!(finalizer_safe());\n        self.inner_ptr\n    }\n\n    fn inner(&self) -> &GcBox<T> {\n        // SAFETY: Please see Gc::inner_ptr()\n        unsafe { self.inner_ptr().as_ref() }\n    }\n}\n\nimpl<T: Trace + ?Sized> Finalize for Gc<T> {\n    fn finalize(&self) {\n        // SAFETY: inner_ptr should be alive when calling finalize.\n        // We don't call inner_ptr() to avoid overhead of calling finalizer_safe().\n        unsafe {\n            self.inner_ptr.as_ref().dec_ref_count();\n        }\n    }\n}\n\n// SAFETY: `Gc` maintains it's own rootedness and implements all methods of\n// Trace. It is not possible to root an already rooted `Gc` and vice versa.\nunsafe impl<T: Trace + ?Sized> Trace for Gc<T> {\n    unsafe fn trace(&self, tracer: &mut Tracer) {\n        tracer.enqueue(self.as_erased_pointer());\n    }\n\n    unsafe fn trace_non_roots(&self) {\n        self.inner().inc_non_root_count();\n    }\n\n    fn run_finalizer(&self) {\n        Finalize::finalize(self);\n    }\n}\n\nimpl<T: Trace + ?Sized> Clone for Gc<T> {\n    fn clone(&self) -> Self {\n        let ptr = self.inner_ptr();\n        // SAFETY: though `ptr` doesn't come from a `into_raw` call, it essentially does the same,\n        // but it skips the call to `std::mem::forget` since we have a reference instead of an owned\n        // value.\n        unsafe {\n            ptr.as_ref().inc_ref_count();\n            Self::from_raw(ptr)\n        }\n    }\n}\n\nimpl<T: Trace + ?Sized> Deref for Gc<T> {\n    type Target = T;\n\n    fn deref(&self) -> &T {\n        self.inner().value()\n    }\n}\n\nimpl<T: Trace + ?Sized> Drop for Gc<T> {\n    fn drop(&mut self) {\n        if finalizer_safe() {\n            Finalize::finalize(self);\n        }\n    }\n}\n\nimpl<T: Trace + Default> Default for Gc<T> {\n    fn default() -> Self {\n        Self::new(Default::default())\n    }\n}\n\n#[allow(clippy::inline_always)]\nimpl<T: Trace + ?Sized + PartialEq> PartialEq for Gc<T> {\n    #[inline(always)]\n    fn eq(&self, other: &Self) -> bool {\n        **self == **other\n    }\n}\n\nimpl<T: Trace + ?Sized + Eq> Eq for Gc<T> {}\n\n#[allow(clippy::inline_always)]\nimpl<T: Trace + ?Sized + PartialOrd> PartialOrd for Gc<T> {\n    #[inline(always)]\n    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {\n        (**self).partial_cmp(&**other)\n    }\n\n    #[inline(always)]\n    fn lt(&self, other: &Self) -> bool {\n        **self < **other\n    }\n\n    #[inline(always)]\n    fn le(&self, other: &Self) -> bool {\n        **self <= **other\n    }\n\n    #[inline(always)]\n    fn gt(&self, other: &Self) -> bool {\n        **self > **other\n    }\n\n    #[inline(always)]\n    fn ge(&self, other: &Self) -> bool {\n        **self >= **other\n    }\n}\n\nimpl<T: Trace + ?Sized + Ord> Ord for Gc<T> {\n    fn cmp(&self, other: &Self) -> Ordering {\n        (**self).cmp(&**other)\n    }\n}\n\nimpl<T: Trace + ?Sized + Hash> Hash for Gc<T> {\n    fn hash<H: Hasher>(&self, state: &mut H) {\n        (**self).hash(state);\n    }\n}\n\nimpl<T: Trace + ?Sized + Display> Display for Gc<T> {\n    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {\n        Display::fmt(&**self, f)\n    }\n}\n\nimpl<T: Trace + ?Sized + Debug> Debug for Gc<T> {\n    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {\n        Debug::fmt(&**self, f)\n    }\n}\n\nimpl<T: Trace + ?Sized> fmt::Pointer for Gc<T> {\n    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {\n        fmt::Pointer::fmt(&self.inner(), f)\n    }\n}\n\nimpl<T: Trace + ?Sized> std::borrow::Borrow<T> for Gc<T> {\n    fn borrow(&self) -> &T {\n        self\n    }\n}\n\nimpl<T: Trace + ?Sized> AsRef<T> for Gc<T> {\n    fn as_ref(&self) -> &T {\n        self\n    }\n}\n"
  },
  {
    "path": "core/gc/src/pointers/mod.rs",
    "content": "//! Pointers represents the External types returned by the Boa Garbage Collector\n\nmod ephemeron;\nmod gc;\nmod weak;\nmod weak_map;\n\npub use ephemeron::Ephemeron;\npub use gc::{Gc, GcErased};\npub use weak::WeakGc;\npub use weak_map::WeakMap;\n\npub(crate) use gc::NonTraceable;\npub(crate) use weak_map::RawWeakMap;\n"
  },
  {
    "path": "core/gc/src/pointers/weak.rs",
    "content": "use crate::{Ephemeron, Finalize, Gc, Trace};\nuse std::hash::{Hash, Hasher};\n\n/// A weak reference to a [`Gc`].\n///\n/// This type allows keeping references to [`Gc`] managed values without keeping them alive for\n/// garbage collections. However, this also means [`WeakGc::upgrade`] could return `None` at any moment.\n#[derive(Debug, Trace, Finalize)]\n#[repr(transparent)]\npub struct WeakGc<T: Trace + ?Sized + 'static> {\n    inner: Ephemeron<T, ()>,\n}\n\nimpl<T: Trace + ?Sized> WeakGc<T> {\n    /// Creates a new weak pointer for a garbage collected value.\n    #[inline]\n    #[must_use]\n    pub fn new(value: &Gc<T>) -> Self {\n        Self {\n            inner: Ephemeron::new(value, ()),\n        }\n    }\n\n    /// Upgrade returns a `Gc` pointer for the internal value if the pointer is still live, or `None`\n    /// if the value was already garbage collected.\n    #[inline]\n    #[must_use]\n    pub fn upgrade(&self) -> Option<Gc<T>> {\n        self.inner.key()\n    }\n\n    /// Check if the [`WeakGc`] can be upgraded.\n    #[inline]\n    #[must_use]\n    pub fn is_upgradable(&self) -> bool {\n        self.inner.has_value()\n    }\n\n    #[must_use]\n    pub(crate) const fn inner(&self) -> &Ephemeron<T, ()> {\n        &self.inner\n    }\n}\n\nimpl<T: Trace> Clone for WeakGc<T> {\n    fn clone(&self) -> Self {\n        Self {\n            inner: self.inner.clone(),\n        }\n    }\n}\n\nimpl<T: Trace> From<Ephemeron<T, ()>> for WeakGc<T> {\n    fn from(inner: Ephemeron<T, ()>) -> Self {\n        Self { inner }\n    }\n}\n\nimpl<T: Trace> PartialEq for WeakGc<T> {\n    fn eq(&self, other: &Self) -> bool {\n        match (self.upgrade(), other.upgrade()) {\n            (Some(a), Some(b)) => std::ptr::eq(a.as_ref(), b.as_ref()),\n            _ => false,\n        }\n    }\n}\n\nimpl<T: Trace> Eq for WeakGc<T> {}\n\nimpl<T: Trace> Hash for WeakGc<T> {\n    fn hash<H: Hasher>(&self, state: &mut H) {\n        if let Some(obj) = self.upgrade() {\n            std::ptr::hash(obj.as_ref(), state);\n        } else {\n            std::ptr::hash(self, state);\n        }\n    }\n}\n"
  },
  {
    "path": "core/gc/src/pointers/weak_map.rs",
    "content": "// Implementation taken partly from https://docs.rs/hashbrown/0.14.0/src/hashbrown/lib.rs.html,\n// but with some adjustments to use `Ephemeron<K,V>` instead of `(K,V)`\n\nuse hashbrown::{\n    DefaultHashBuilder, HashTable, TryReserveError,\n    hash_table::{Entry as RawEntry, Iter as RawIter},\n};\n\nuse crate::{Allocator, Ephemeron, Finalize, Gc, GcRef, GcRefCell, Trace, custom_trace};\nuse std::{fmt, hash::BuildHasher, marker::PhantomData};\n\n/// A map that holds weak references to its keys and is traced by the garbage collector.\n#[derive(Clone, Debug, Default, Finalize)]\npub struct WeakMap<K: Trace + ?Sized + 'static, V: Trace + 'static> {\n    pub(crate) inner: Gc<GcRefCell<RawWeakMap<K, V>>>,\n}\n\nunsafe impl<K: Trace + ?Sized + 'static, V: Trace + 'static> Trace for WeakMap<K, V> {\n    custom_trace!(this, mark, {\n        mark(&this.inner);\n    });\n}\n\nimpl<K: Trace + ?Sized, V: Trace> WeakMap<K, V> {\n    /// Creates a new `WeakMap`.\n    #[must_use]\n    #[inline]\n    pub fn new() -> Self {\n        Allocator::alloc_weak_map()\n    }\n\n    /// Inserts a key-value pair into the map.\n    #[inline]\n    pub fn insert(&mut self, key: &Gc<K>, value: V) {\n        self.inner.borrow_mut().insert(key, value);\n    }\n\n    /// Removes a key from the map, returning `true` if the key was previously in\n    /// the map.\n    #[inline]\n    pub fn remove(&mut self, key: &Gc<K>) -> bool {\n        self.inner.borrow_mut().remove(key)\n    }\n\n    /// Returns `true` if the map contains a value for the specified key.\n    #[must_use]\n    #[inline]\n    pub fn contains_key(&self, key: &Gc<K>) -> bool {\n        self.inner.borrow().contains_key(key)\n    }\n\n    /// Returns a reference to the ephemeron corresponding to the key.\n    #[must_use]\n    #[inline]\n    pub fn get<'a>(&'a self, key: &Gc<K>) -> Option<GcRef<'a, Ephemeron<K, V>>> {\n        GcRef::try_map(self.inner.borrow(), |inner| inner.get(key))\n    }\n}\n\n/// A hash map where the bucket type is an <code>[Ephemeron]\\<K, V\\></code>.\n///\n/// This data structure allows associating a <code>[Gc]\\<K\\></code> with a value `V` that will be\n/// invalidated when the `Gc<K>` gets collected. In other words, all key entries on the map are weakly\n/// held.\npub(crate) struct RawWeakMap<K, V, S = DefaultHashBuilder>\nwhere\n    K: Trace + ?Sized + 'static,\n    V: Trace + 'static,\n{\n    hash_builder: S,\n    table: HashTable<Ephemeron<K, V>>,\n}\n\nimpl<K, V, S> Finalize for RawWeakMap<K, V, S>\nwhere\n    K: Trace + ?Sized + 'static,\n    V: Trace + 'static,\n{\n}\n\n// SAFETY: The implementation correctly marks all ephemerons inside the map.\nunsafe impl<K, V, S> Trace for RawWeakMap<K, V, S>\nwhere\n    K: Trace + ?Sized + 'static,\n    V: Trace + 'static,\n{\n    custom_trace!(this, mark, {\n        for eph in this.iter() {\n            mark(eph);\n        }\n    });\n}\n\nimpl<K, V, S> Default for RawWeakMap<K, V, S>\nwhere\n    S: Default,\n    K: Trace + ?Sized + 'static,\n    V: Trace + 'static,\n{\n    fn default() -> Self {\n        Self::with_hasher(Default::default())\n    }\n}\n\nimpl<K, V> RawWeakMap<K, V, DefaultHashBuilder>\nwhere\n    K: Trace + ?Sized + 'static,\n    V: Trace + 'static,\n{\n    /// Creates an empty `RawWeakMap`.\n    ///\n    /// The map is initially created with a capacity of 0, so it will not allocate until it\n    /// is first inserted into.\n    pub(crate) fn new() -> Self {\n        Self::default()\n    }\n\n    /// Creates an empty `RawWeakMap` with the specified capacity.\n    ///\n    /// The map will be able to hold at least `capacity` elements without reallocating.\n    /// If `capacity` is 0, the map will not allocate.\n    #[allow(unused)]\n    pub(crate) fn with_capacity(capacity: usize) -> Self {\n        Self::with_capacity_and_hasher(capacity, DefaultHashBuilder::default())\n    }\n}\n\nimpl<K, V, S> RawWeakMap<K, V, S>\nwhere\n    K: Trace + ?Sized + 'static,\n    V: Trace + 'static,\n{\n    /// Creates an empty `RawWeakMap` which will use the given hash builder to hash\n    /// keys.\n    ///\n    /// The map is initially created with a capacity of 0, so it will not allocate until it is first\n    /// inserted into.\n    pub(crate) const fn with_hasher(hash_builder: S) -> Self {\n        Self {\n            hash_builder,\n            table: HashTable::new(),\n        }\n    }\n\n    /// Creates an empty `RawWeakMap` with the specified capacity, using `hash_builder`\n    /// to hash the keys.\n    ///\n    /// The map will be able to hold at least `capacity` elements without reallocating.\n    /// If `capacity` is 0, the map will not allocate.\n    pub(crate) fn with_capacity_and_hasher(capacity: usize, hash_builder: S) -> Self {\n        Self {\n            hash_builder,\n            table: HashTable::with_capacity(capacity),\n        }\n    }\n\n    /// Returns a reference to the map's [`BuildHasher`].\n    #[allow(unused)]\n    pub(crate) const fn hasher(&self) -> &S {\n        &self.hash_builder\n    }\n\n    /// Returns the number of elements the map can hold without reallocating.\n    ///\n    /// This number is a lower bound; the map might be able to hold more, but is guaranteed to be\n    /// able to hold at least this many.\n    #[allow(unused)]\n    pub(crate) fn capacity(&self) -> usize {\n        self.table.capacity()\n    }\n\n    /// An iterator visiting all entries in arbitrary order.\n    /// The iterator element type is <code>[Ephemeron]<K, V></code>.\n    pub(crate) fn iter(&self) -> Iter<'_, K, V> {\n        Iter {\n            inner: self.table.iter(),\n            marker: PhantomData,\n        }\n    }\n\n    /// Returns the number of elements in the map.\n    ///\n    /// This is an upper bound; the map might contain some expired keys which haven't been\n    /// removed.\n    #[allow(unused)]\n    pub(crate) fn len(&self) -> usize {\n        self.table.len()\n    }\n\n    /// Returns `true` if the map contains no elements.\n    ///\n    /// This might return `false` if the map has expired keys that are still pending to be\n    /// cleaned up.\n    #[allow(unused)]\n    pub(crate) fn is_empty(&self) -> bool {\n        self.len() == 0\n    }\n\n    /// Retains only the elements specified by the predicate. Keeps the\n    /// allocated memory for reuse.\n    ///\n    /// In other words, remove all ephemerons <code>[Ephemeron]<K, V></code> such that\n    /// `f(&eph)` returns `false`.\n    /// The elements are visited in unsorted (and unspecified) order.\n    pub(crate) fn retain<F>(&mut self, mut f: F)\n    where\n        F: FnMut(&Ephemeron<K, V>) -> bool,\n    {\n        // SAFETY:\n        // - `item` is only used internally, which means it outlives self.\n        // - `item` pointer is not used after the call to `erase`.\n        self.table.retain(|item| f(item));\n    }\n\n    /// Clears the map, removing all key-value pairs. Keeps the allocated memory\n    /// for reuse.\n    #[allow(unused)]\n    pub(crate) fn clear(&mut self) {\n        self.table.clear();\n    }\n}\n\nimpl<K, V, S> RawWeakMap<K, V, S>\nwhere\n    K: Trace + ?Sized + 'static,\n    V: Trace + 'static,\n    S: BuildHasher,\n{\n    /// Reserves capacity for at least `additional` more elements to be inserted\n    /// in the `RawWeakMap`. The collection may reserve more space to avoid\n    /// frequent reallocations.\n    ///\n    /// # Panics\n    ///\n    /// Panics if the new capacity exceeds [`isize::MAX`] bytes and [`abort`](std::process::abort)\n    /// the program in case of allocation error. Use [`try_reserve`](RawWeakMap::try_reserve) instead\n    /// if you want to handle memory allocation failure.\n    #[allow(unused)]\n    pub(crate) fn reserve(&mut self, additional: usize) {\n        self.table\n            .reserve(additional, make_hasher(&self.hash_builder));\n    }\n\n    /// Tries to reserve capacity for at least `additional` more elements to be inserted\n    /// in the given `RawWeakMap<K,V>`. The collection may reserve more space to avoid\n    /// frequent reallocations.\n    ///\n    /// # Errors\n    ///\n    /// If the capacity overflows, or the allocator reports a failure, then an error\n    /// is returned.\n    #[allow(unused)]\n    pub(crate) fn try_reserve(&mut self, additional: usize) -> Result<(), TryReserveError> {\n        self.table\n            .try_reserve(additional, make_hasher(&self.hash_builder))\n    }\n\n    /// Shrinks the capacity of the map as much as possible. It will drop\n    /// down as much as possible while maintaining the internal rules\n    /// and possibly leaving some space in accordance with the resize policy.\n    #[allow(unused)]\n    pub(crate) fn shrink_to_fit(&mut self) {\n        self.table\n            .shrink_to(0, make_hasher::<_, V, S>(&self.hash_builder));\n    }\n\n    /// Shrinks the capacity of the map with a lower limit. It will drop\n    /// down no lower than the supplied limit while maintaining the internal rules\n    /// and possibly leaving some space in accordance with the resize policy.\n    ///\n    /// This function does nothing if the current capacity is smaller than the\n    /// supplied minimum capacity.\n    #[allow(unused)]\n    pub(crate) fn shrink_to(&mut self, min_capacity: usize) {\n        self.table\n            .shrink_to(min_capacity, make_hasher::<_, V, S>(&self.hash_builder));\n    }\n\n    /// Returns the ephemeron corresponding to the supplied key.\n    pub(crate) fn get(&self, k: &Gc<K>) -> Option<&Ephemeron<K, V>> {\n        if self.table.is_empty() {\n            None\n        } else {\n            let hash = make_hash_from_gc(&self.hash_builder, k);\n            self.table.find(hash, equivalent_key(k))\n        }\n    }\n\n    /// Returns `true` if the map contains a value for the specified key.\n    pub(crate) fn contains_key(&self, k: &Gc<K>) -> bool {\n        self.get(k).is_some()\n    }\n\n    // Inserts a key-value pair into the map.\n    ///\n    /// If the map did not have this key present, [`None`] is returned.\n    ///\n    /// If the map did have this key present, the value is updated, and the old\n    /// value is returned. The key is not updated.\n    pub(crate) fn insert(&mut self, k: &Gc<K>, v: V) -> Option<Ephemeron<K, V>> {\n        let hash = make_hash_from_gc(&self.hash_builder, k);\n        let hasher = make_hasher(&self.hash_builder);\n        let entry = self.table.entry(hash, equivalent_key(k), hasher);\n        let (old, slot) = match entry {\n            RawEntry::Occupied(occupied_entry) => {\n                let (v, slot) = occupied_entry.remove();\n                (Some(v), slot)\n            }\n            RawEntry::Vacant(vacant_entry) => (None, vacant_entry),\n        };\n\n        slot.insert(Ephemeron::new(k, v));\n        old\n    }\n\n    /// Removes a key from the map, returning `true` if the key\n    /// was previously in the map. Keeps the allocated memory for reuse.\n    pub(crate) fn remove(&mut self, k: &Gc<K>) -> bool {\n        let hash = make_hash_from_gc(&self.hash_builder, k);\n        if let Ok(entry) = self.table.find_entry(hash, equivalent_key(k)) {\n            entry.remove();\n            true\n        } else {\n            false\n        }\n    }\n\n    /// Clears all the expired keys in the map.\n    pub(crate) fn clear_expired(&mut self) {\n        self.retain(|eph| eph.value().is_some());\n    }\n}\n\npub(crate) struct Iter<'a, K, V>\nwhere\n    K: Trace + ?Sized + 'static,\n    V: Trace + 'static,\n{\n    inner: RawIter<'a, Ephemeron<K, V>>,\n    marker: PhantomData<&'a Ephemeron<K, V>>,\n}\n\nimpl<K, V> Clone for Iter<'_, K, V>\nwhere\n    K: Trace + ?Sized + 'static,\n    V: Trace + 'static,\n{\n    #[inline]\n    fn clone(&self) -> Self {\n        Iter {\n            inner: self.inner.clone(),\n            marker: PhantomData,\n        }\n    }\n}\n\nimpl<K, V> fmt::Debug for Iter<'_, K, V>\nwhere\n    K: Trace + ?Sized + 'static + fmt::Debug,\n    V: Trace + 'static + fmt::Debug,\n{\n    #[inline]\n    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {\n        f.debug_list().entries(self.clone()).finish()\n    }\n}\n\nimpl<'a, K, V> Iterator for Iter<'a, K, V>\nwhere\n    K: Trace + ?Sized + 'static,\n    V: Trace + 'static,\n{\n    type Item = &'a Ephemeron<K, V>;\n\n    #[inline]\n    fn next(&mut self) -> Option<Self::Item> {\n        self.inner.next()\n    }\n\n    #[inline]\n    fn size_hint(&self) -> (usize, Option<usize>) {\n        self.inner.size_hint()\n    }\n}\n\nimpl<K, V, S> fmt::Debug for RawWeakMap<K, V, S>\nwhere\n    K: fmt::Debug + ?Sized + Trace + Finalize,\n    V: fmt::Debug + Trace + Finalize,\n{\n    #[inline]\n    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {\n        self.iter().fmt(f)\n    }\n}\n\nfn make_hasher<K, V, S>(hash_builder: &S) -> impl Fn(&Ephemeron<K, V>) -> u64 + '_\nwhere\n    S: BuildHasher,\n    K: Trace + ?Sized + 'static,\n    V: Trace + 'static,\n{\n    move |val| make_hash_from_eph::<K, V, S>(hash_builder, val)\n}\n\nfn make_hash_from_eph<K, V, S>(hash_builder: &S, eph: &Ephemeron<K, V>) -> u64\nwhere\n    S: BuildHasher,\n    K: Trace + ?Sized + 'static,\n    V: Trace + 'static,\n{\n    use std::hash::Hasher;\n    let mut state = hash_builder.build_hasher();\n    // TODO: Is this true for custom hashers? if not, rewrite `key` to be safe.\n    // SAFETY: The return value of `key` is only used to hash it, which\n    // cannot trigger a garbage collection,\n    unsafe {\n        if let Some(val) = eph.inner().key() {\n            std::ptr::hash(val, &mut state);\n        } else {\n            std::ptr::hash(eph.inner_ptr().as_ptr(), &mut state);\n        }\n    }\n    state.finish()\n}\n\nfn make_hash_from_gc<K, S>(hash_builder: &S, gc: &Gc<K>) -> u64\nwhere\n    S: BuildHasher,\n    K: Trace + ?Sized + 'static,\n{\n    use std::hash::Hasher;\n    let mut state = hash_builder.build_hasher();\n    std::ptr::hash(gc.inner_ptr().as_ptr(), &mut state);\n    state.finish()\n}\n\nfn equivalent_key<K, V>(k: &Gc<K>) -> impl Fn(&Ephemeron<K, V>) -> bool + '_\nwhere\n    K: Trace + ?Sized + 'static,\n    V: Trace + 'static,\n{\n    // SAFETY: The return value of `key` is only used inside eq, which\n    // cannot trigger a garbage collection.\n    move |eph| unsafe {\n        eph.inner().key().is_some_and(|val| {\n            let val: *const _ = val;\n            std::ptr::eq(val, k.inner_ptr().as_ptr())\n        })\n    }\n}\n"
  },
  {
    "path": "core/gc/src/test/allocation.rs",
    "content": "mod miri {\n    use boa_macros::{Finalize, Trace};\n\n    use super::super::{Harness, run_test};\n    use crate::{Gc, GcBox, GcRefCell, force_collect};\n\n    #[test]\n    fn gc_basic_cell_allocation() {\n        run_test(|| {\n            let gc_cell = Gc::new(GcRefCell::new(16_u16));\n\n            force_collect();\n            Harness::assert_collections(1);\n            Harness::assert_bytes_allocated();\n            assert_eq!(*gc_cell.borrow_mut(), 16);\n        });\n    }\n\n    #[test]\n    fn gc_basic_pointer_alloc() {\n        run_test(|| {\n            let gc = Gc::new(16_u8);\n\n            force_collect();\n            Harness::assert_collections(1);\n            Harness::assert_bytes_allocated();\n            assert_eq!(*gc, 16);\n\n            drop(gc);\n            force_collect();\n            Harness::assert_collections(2);\n            Harness::assert_empty_gc();\n        });\n    }\n\n    #[test]\n    // Takes too long to finish in miri\n    #[cfg_attr(miri, ignore)]\n    fn gc_recursion() {\n        run_test(|| {\n            #[derive(Debug, Finalize, Trace)]\n            struct S {\n                i: usize,\n                next: Option<Gc<S>>,\n            }\n\n            const SIZE: usize = size_of::<GcBox<S>>();\n            const COUNT: usize = 1_000_000;\n\n            let mut root = Gc::new(S { i: 0, next: None });\n            for i in 1..COUNT {\n                root = Gc::new(S {\n                    i,\n                    next: Some(root),\n                });\n            }\n\n            Harness::assert_bytes_allocated();\n            Harness::assert_exact_bytes_allocated(SIZE * COUNT);\n\n            drop(root);\n            force_collect();\n            Harness::assert_empty_gc();\n        });\n    }\n}\n"
  },
  {
    "path": "core/gc/src/test/cell.rs",
    "content": "mod miri {\n    use super::super::run_test;\n    use crate::{Gc, GcRefCell};\n\n    #[test]\n    fn boa_borrow_mut_test() {\n        run_test(|| {\n            let v = Gc::new(GcRefCell::new(Vec::new()));\n\n            for _ in 1..=259 {\n                let cell = Gc::new(GcRefCell::new([0u8; 10]));\n                v.borrow_mut().push(cell);\n            }\n        });\n    }\n}\n"
  },
  {
    "path": "core/gc/src/test/erased.rs",
    "content": "mod miri {\n    use std::any::TypeId;\n\n    use boa_macros::{Finalize, Trace};\n\n    use super::super::run_test;\n    use crate::{Gc, GcBox, GcErased, force_collect, test::Harness};\n\n    #[test]\n    fn erased_gc() {\n        run_test(|| {\n            let value = vec![1, 2, 3];\n            let gc = Gc::new(value.clone());\n\n            assert_eq!(Gc::type_id(&gc), TypeId::of::<Vec<i32>>());\n\n            let erased = GcErased::new(gc.clone());\n\n            assert_eq!(erased.type_id(), TypeId::of::<Vec<i32>>());\n            assert!(erased.is::<Vec<i32>>());\n\n            assert_eq!(erased.clone().downcast::<i32>(), None);\n\n            let gc_from_erased = erased.downcast::<Vec<i32>>().unwrap();\n            assert_eq!(**gc_from_erased, value);\n\n            assert!(Gc::ptr_eq(&gc, &gc_from_erased));\n        });\n    }\n\n    #[test]\n    fn nested_erased_gc() {\n        #[derive(Debug, Trace, Finalize)]\n        struct List {\n            value: i32,\n            next: Option<GcErased>,\n        }\n\n        run_test(|| {\n            let mut root = GcErased::new(Gc::new(List {\n                value: 0,\n                next: None,\n            }));\n\n            for value in 1..100 {\n                root = GcErased::new(Gc::new(List {\n                    value,\n                    next: Some(root),\n                }));\n            }\n\n            Harness::assert_exact_bytes_allocated(100 * size_of::<GcBox<List>>());\n            force_collect();\n            Harness::assert_exact_bytes_allocated(100 * size_of::<GcBox<List>>());\n\n            let mut head = root.downcast::<List>();\n            for value in (0..100).rev() {\n                let head_unwrap = head.as_ref().unwrap();\n\n                assert_eq!(head_unwrap.value, value);\n\n                head = head_unwrap\n                    .next\n                    .clone()\n                    .and_then(GcErased::downcast::<List>);\n            }\n        });\n    }\n\n    #[test]\n    fn c_style_inheritance() {\n        #[repr(C)]\n        #[derive(Debug, Trace, Finalize, PartialEq, Eq)]\n        struct Base {\n            base_field: Vec<i32>,\n        }\n\n        #[repr(C)]\n        #[derive(Debug, Trace, Finalize, PartialEq, Eq)]\n        struct Derived {\n            base: Base,\n            derived_field: Vec<i64>,\n        }\n\n        run_test(|| {\n            let value = vec![1, 2, 3];\n            let derived = Gc::new(Derived {\n                base: Base {\n                    base_field: value.clone(),\n                },\n                derived_field: vec![4, 5, 6],\n            });\n\n            assert_eq!(Gc::type_id(&derived), TypeId::of::<Derived>());\n            assert!(Gc::is::<Derived>(&derived));\n\n            // SAFETY: The structs have #[repr(C)] so this is safe.\n            let base = unsafe { Gc::cast_unchecked::<Base>(derived.clone()) };\n\n            assert_eq!(Gc::type_id(&base), TypeId::of::<Derived>());\n            assert!(Gc::is::<Derived>(&base));\n\n            assert_eq!(base.base_field, value);\n            assert_eq!(base.base_field, derived.base.base_field);\n\n            assert!(Gc::ptr_eq(&base, &derived));\n\n            assert_eq!(Gc::downcast::<i32>(base.clone()), None);\n            assert_eq!(*Gc::downcast::<Derived>(base).unwrap(), *derived);\n        });\n    }\n}\n"
  },
  {
    "path": "core/gc/src/test/mod.rs",
    "content": "use crate::BOA_GC;\n\nmod allocation;\nmod cell;\nmod erased;\nmod std_types;\nmod weak;\nmod weak_map;\n\nstruct Harness;\n\nimpl Harness {\n    #[track_caller]\n    fn assert_collections(o: usize) {\n        let collections = BOA_GC.with(|current| {\n            let gc = current.borrow();\n            gc.runtime.collections\n        });\n\n        assert_eq!(collections, o);\n    }\n\n    #[track_caller]\n    fn assert_empty_gc() {\n        let (is_empty, bytes_allocated) = BOA_GC.with(|current| {\n            let gc = current.borrow();\n            (gc.strongs.is_empty(), gc.runtime.bytes_allocated)\n        });\n\n        assert!(is_empty);\n        assert_eq!(bytes_allocated, 0);\n    }\n\n    #[track_caller]\n    fn assert_bytes_allocated() {\n        let bytes_allocated = BOA_GC.with(|current| {\n            let gc = current.borrow();\n            gc.runtime.bytes_allocated\n        });\n\n        assert!(bytes_allocated > 0);\n    }\n\n    #[track_caller]\n    fn assert_exact_bytes_allocated(bytes: usize) {\n        let bytes_allocated = BOA_GC.with(|current| {\n            let gc = current.borrow();\n            gc.runtime.bytes_allocated\n        });\n        assert_eq!(bytes_allocated, bytes);\n    }\n}\n\n#[track_caller]\nfn run_test(test: impl FnOnce() + Send + 'static) {\n    let handle = std::thread::spawn(test);\n    handle.join().unwrap();\n}\n"
  },
  {
    "path": "core/gc/src/test/std_types.rs",
    "content": "use crate::{Trace, Tracer};\nuse std::path::PathBuf;\nuse std::time::Instant;\n\n#[test]\nfn test_simple_types_trace() {\n    let mut tracer = Tracer::new();\n    unsafe {\n        Instant::now().trace(&mut tracer);\n        PathBuf::from(\".\").trace(&mut tracer);\n    }\n    assert!(\n        tracer.is_empty(),\n        \"Simple types should not add anything to tracer\"\n    );\n}\n\n#[cfg(not(target_family = \"wasm\"))]\n#[test]\nfn test_file_trace() {\n    use std::fs::File;\n    if let Ok(file) = File::open(\"Cargo.toml\") {\n        let mut tracer = Tracer::new();\n        unsafe {\n            file.trace(&mut tracer);\n        }\n        assert!(\n            tracer.is_empty(),\n            \"File handle should not add anything to tracer\"\n        );\n    }\n}\n"
  },
  {
    "path": "core/gc/src/test/weak.rs",
    "content": "mod miri {\n    use std::{cell::Cell, rc::Rc};\n\n    use super::super::run_test;\n    use crate::{\n        Ephemeron, Finalize, Gc, GcBox, GcRefCell, Trace, WeakGc, force_collect,\n        internals::EphemeronBox, test::Harness,\n    };\n\n    #[test]\n    fn eph_weak_gc_test() {\n        run_test(|| {\n            let gc_value = Gc::new(3);\n\n            {\n                let cloned_gc = gc_value.clone();\n\n                let weak = WeakGc::new(&cloned_gc);\n\n                assert_eq!(*weak.upgrade().expect(\"Is live currently\"), 3);\n                drop(cloned_gc);\n                force_collect();\n                assert_eq!(*weak.upgrade().expect(\"WeakGc is still live here\"), 3);\n\n                drop(gc_value);\n                force_collect();\n\n                assert!(weak.upgrade().is_none());\n            }\n        });\n    }\n\n    #[test]\n    fn eph_ephemeron_test() {\n        run_test(|| {\n            let gc_value = Gc::new(3);\n\n            {\n                let cloned_gc = gc_value.clone();\n\n                let ephemeron = Ephemeron::new(&cloned_gc, String::from(\"Hello World!\"));\n\n                assert_eq!(\n                    *ephemeron.value().expect(\"Ephemeron is live\"),\n                    String::from(\"Hello World!\")\n                );\n                drop(cloned_gc);\n                force_collect();\n                assert_eq!(\n                    *ephemeron.value().expect(\"Ephemeron is still live here\"),\n                    String::from(\"Hello World!\")\n                );\n\n                drop(gc_value);\n                force_collect();\n\n                assert!(ephemeron.value().is_none());\n            }\n        });\n    }\n\n    #[test]\n    fn eph_allocation_chains() {\n        run_test(|| {\n            let gc_value = Gc::new(String::from(\"foo\"));\n\n            {\n                let cloned_gc = gc_value.clone();\n                let weak = WeakGc::new(&cloned_gc);\n                let wrap = Gc::new(weak);\n\n                assert_eq!(wrap.upgrade().as_deref().map(String::as_str), Some(\"foo\"));\n\n                let eph = Ephemeron::new(&wrap, 3);\n\n                drop(cloned_gc);\n                force_collect();\n                assert_eq!(wrap.upgrade().as_deref().map(String::as_str), Some(\"foo\"));\n                assert_eq!(&*eph.value().unwrap(), &3);\n\n                drop(gc_value);\n                force_collect();\n                assert!(wrap.upgrade().is_none());\n                assert_eq!(&*eph.value().unwrap(), &3);\n\n                drop(wrap);\n                force_collect();\n                assert!(eph.value().is_none());\n            }\n        });\n    }\n\n    #[test]\n    fn eph_basic_alloc_dump_test() {\n        run_test(|| {\n            let gc_value = Gc::new(String::from(\"gc here\"));\n            let _gc_two = Gc::new(\"hmmm\");\n\n            let eph = Ephemeron::new(&gc_value, 4);\n            let _fourth = Gc::new(\"tail\");\n\n            assert_eq!(&*eph.value().unwrap(), &4);\n        });\n    }\n\n    #[test]\n    fn eph_basic_upgrade_test() {\n        run_test(|| {\n            let init_gc = Gc::new(String::from(\"foo\"));\n\n            let weak = WeakGc::new(&init_gc);\n\n            let new_gc = weak.upgrade().expect(\"Weak is still live\");\n\n            drop(weak);\n            force_collect();\n\n            assert_eq!(*init_gc, *new_gc);\n        });\n    }\n\n    #[test]\n    fn eph_basic_clone_test() {\n        run_test(|| {\n            let init_gc = Gc::new(String::from(\"bar\"));\n\n            let weak = WeakGc::new(&init_gc);\n\n            let new_gc = weak.upgrade().expect(\"Weak is live\");\n            let new_weak = weak.clone();\n\n            drop(weak);\n            force_collect();\n\n            assert_eq!(*new_gc, *new_weak.upgrade().expect(\"weak should be live\"));\n            assert_eq!(\n                *init_gc,\n                *new_weak.upgrade().expect(\"weak_should be live still\")\n            );\n        });\n    }\n\n    #[test]\n    fn eph_self_referential() {\n        #[derive(Trace, Finalize, Clone)]\n        struct InnerCell {\n            inner: GcRefCell<Option<Ephemeron<InnerCell, TestCell>>>,\n        }\n        #[derive(Trace, Finalize, Clone)]\n        struct TestCell {\n            inner: Gc<InnerCell>,\n        }\n        run_test(|| {\n            let root = TestCell {\n                inner: Gc::new(InnerCell {\n                    inner: GcRefCell::new(None),\n                }),\n            };\n            let root_size = size_of::<GcBox<InnerCell>>();\n\n            Harness::assert_exact_bytes_allocated(root_size);\n\n            {\n                let eph_size = size_of::<EphemeronBox<InnerCell, TestCell>>();\n                // Generate a self-referential ephemeron\n                let eph = Ephemeron::new(&root.inner, root.clone());\n                *root.inner.inner.borrow_mut() = Some(eph.clone());\n\n                assert!(eph.value().is_some());\n                Harness::assert_exact_bytes_allocated(root_size + eph_size);\n            }\n\n            *root.inner.inner.borrow_mut() = None;\n\n            force_collect();\n\n            Harness::assert_exact_bytes_allocated(root_size);\n        });\n    }\n\n    #[test]\n    fn eph_self_referential_chain() {\n        #[derive(Trace, Finalize, Clone)]\n        struct TestCell {\n            inner: Gc<GcRefCell<Option<Ephemeron<u8, TestCell>>>>,\n        }\n        run_test(|| {\n            let root = Gc::new(GcRefCell::new(None));\n            let root_size = size_of::<GcBox<GcRefCell<Option<Ephemeron<u8, TestCell>>>>>();\n\n            Harness::assert_exact_bytes_allocated(root_size);\n\n            let watched = Gc::new(0);\n            let watched_size = size_of::<GcBox<u8>>();\n\n            {\n                let eph_size = size_of::<EphemeronBox<u8, TestCell>>();\n                // Generate a self-referential loop of weak and non-weak pointers\n                let chain1 = TestCell {\n                    inner: Gc::new(GcRefCell::new(None)),\n                };\n                let chain2 = TestCell {\n                    inner: Gc::new(GcRefCell::new(None)),\n                };\n\n                let eph_start = Ephemeron::new(&watched, chain1.clone());\n                let eph_chain2 = Ephemeron::new(&watched, chain2.clone());\n\n                *chain1.inner.borrow_mut() = Some(eph_chain2.clone());\n                *chain2.inner.borrow_mut() = Some(eph_start.clone());\n\n                *root.borrow_mut() = Some(eph_start.clone());\n\n                force_collect();\n\n                assert!(eph_start.value().is_some());\n                assert!(eph_chain2.value().is_some());\n                Harness::assert_exact_bytes_allocated(watched_size + 3 * root_size + 2 * eph_size);\n            }\n\n            *root.borrow_mut() = None;\n\n            force_collect();\n\n            drop(watched);\n\n            force_collect();\n\n            Harness::assert_exact_bytes_allocated(root_size);\n        });\n    }\n\n    #[test]\n    fn eph_finalizer() {\n        #[derive(Clone, Trace)]\n        struct S {\n            #[unsafe_ignore_trace]\n            inner: Rc<Cell<u8>>,\n        }\n\n        impl Finalize for S {\n            fn finalize(&self) {\n                self.inner.set(self.inner.get() + 1);\n            }\n        }\n\n        run_test(|| {\n            let val = S {\n                inner: Rc::new(Cell::new(0)),\n            };\n\n            let key = Gc::new(50u32);\n            let eph = Ephemeron::new(&key, val.clone());\n            assert!(eph.has_value());\n            // finalize hasn't been run\n            assert_eq!(val.inner.get(), 0);\n\n            drop(key);\n            force_collect();\n            assert!(!eph.has_value());\n            // finalize ran when collecting\n            assert_eq!(val.inner.get(), 1);\n        });\n    }\n\n    #[test]\n    fn eph_gc_finalizer() {\n        #[derive(Clone, Trace)]\n        struct S {\n            #[unsafe_ignore_trace]\n            inner: Rc<Cell<u8>>,\n        }\n\n        impl Finalize for S {\n            fn finalize(&self) {\n                self.inner.set(self.inner.get() + 1);\n            }\n        }\n\n        run_test(|| {\n            let val = S {\n                inner: Rc::new(Cell::new(0)),\n            };\n\n            let key = Gc::new(50u32);\n            let eph = Ephemeron::new(&key, Gc::new(val.clone()));\n            assert!(eph.has_value());\n            // finalize hasn't been run\n            assert_eq!(val.inner.get(), 0);\n\n            drop(key);\n            force_collect();\n            assert!(!eph.has_value());\n            // finalize ran when collecting\n            assert_eq!(val.inner.get(), 1);\n        });\n    }\n\n    #[test]\n    fn eph_strong_self_reference() {\n        type Inner = GcRefCell<(Option<TestCell>, Option<TestCell>)>;\n        #[derive(Trace, Finalize, Clone)]\n        struct TestCell {\n            inner: Gc<Inner>,\n        }\n        run_test(|| {\n            let root = TestCell {\n                inner: Gc::new(GcRefCell::new((None, None))),\n            };\n            let root_size = size_of::<GcBox<Inner>>();\n\n            Harness::assert_exact_bytes_allocated(root_size);\n\n            let watched = Gc::new(0);\n            let watched_size = size_of::<GcBox<i32>>();\n\n            {\n                let eph = Ephemeron::new(&watched, root.clone());\n                let eph_size = size_of::<EphemeronBox<Gc<i32>, TestCell>>();\n\n                root.inner.borrow_mut().0 = Some(root.clone());\n                root.inner.borrow_mut().1 = Some(root.clone());\n\n                force_collect();\n\n                assert!(eph.value().is_some());\n                Harness::assert_exact_bytes_allocated(root_size + eph_size + watched_size);\n            }\n\n            force_collect();\n\n            drop(watched);\n\n            force_collect();\n\n            Harness::assert_exact_bytes_allocated(root_size);\n        });\n    }\n}\n"
  },
  {
    "path": "core/gc/src/test/weak_map.rs",
    "content": "mod miri {\n    use super::super::run_test;\n    use crate::{Gc, WeakMap, force_collect, has_weak_maps};\n\n    #[test]\n    fn weak_map_basic() {\n        run_test(|| {\n            let key1 = Gc::new(String::from(\"key1\"));\n            let key2 = Gc::new(String::from(\"key2\"));\n            let key3 = Gc::new(String::from(\"key3\"));\n\n            assert!(!has_weak_maps());\n\n            let mut map = WeakMap::new();\n\n            assert!(has_weak_maps());\n\n            map.insert(&key1, ());\n            map.insert(&key2, ());\n            map.insert(&key3, ());\n\n            force_collect();\n            assert!(has_weak_maps());\n\n            assert!(map.contains_key(&key1));\n            assert!(map.contains_key(&key2));\n            assert!(map.contains_key(&key3));\n\n            drop(key1);\n\n            force_collect();\n            assert!(has_weak_maps());\n\n            assert!(map.contains_key(&key2));\n            assert!(map.contains_key(&key3));\n\n            drop(key2);\n\n            force_collect();\n            assert!(has_weak_maps());\n\n            assert!(map.contains_key(&key3));\n            assert!(has_weak_maps());\n\n            drop(key3);\n\n            assert!(has_weak_maps());\n\n            force_collect();\n            assert!(has_weak_maps());\n\n            drop(map);\n\n            force_collect();\n            assert!(!has_weak_maps());\n        });\n    }\n\n    #[test]\n    fn weak_map_multiple() {\n        run_test(|| {\n            let key1 = Gc::new(String::from(\"key1\"));\n            let key2 = Gc::new(String::from(\"key2\"));\n            let key3 = Gc::new(String::from(\"key3\"));\n\n            assert!(!has_weak_maps());\n\n            let mut map_1 = WeakMap::new();\n            let mut map_2 = WeakMap::new();\n\n            assert!(has_weak_maps());\n\n            map_1.insert(&key1, ());\n            map_1.insert(&key2, ());\n            map_2.insert(&key3, ());\n\n            force_collect();\n            assert!(has_weak_maps());\n\n            assert!(map_1.contains_key(&key1));\n            assert!(map_1.contains_key(&key2));\n            assert!(!map_1.contains_key(&key3));\n            assert!(!map_2.contains_key(&key1));\n            assert!(!map_2.contains_key(&key2));\n            assert!(map_2.contains_key(&key3));\n\n            force_collect();\n            assert!(has_weak_maps());\n\n            drop(key1);\n            drop(key2);\n\n            force_collect();\n            assert!(has_weak_maps());\n\n            assert!(!map_1.contains_key(&key3));\n            assert!(map_2.contains_key(&key3));\n\n            drop(key3);\n\n            force_collect();\n            assert!(has_weak_maps());\n\n            drop(map_1);\n\n            force_collect();\n            assert!(has_weak_maps());\n\n            drop(map_2);\n\n            force_collect();\n            assert!(!has_weak_maps());\n        });\n    }\n\n    #[test]\n    fn weak_map_key_live() {\n        run_test(|| {\n            let key = Gc::new(String::from(\"key\"));\n            let key_copy = key.clone();\n\n            let mut map = WeakMap::new();\n\n            map.insert(&key, ());\n\n            assert!(map.contains_key(&key));\n            assert!(map.contains_key(&key_copy));\n\n            assert!(map.remove(&key));\n\n            map.insert(&key, ());\n\n            drop(key);\n\n            force_collect();\n\n            assert!(map.contains_key(&key_copy));\n        });\n    }\n}\n"
  },
  {
    "path": "core/gc/src/trace.rs",
    "content": "use std::{\n    any::TypeId,\n    borrow::{Cow, ToOwned},\n    cell::{Cell, OnceCell},\n    collections::{BTreeMap, BTreeSet, BinaryHeap, HashMap, HashSet, LinkedList, VecDeque},\n    hash::{BuildHasher, Hash},\n    marker::PhantomData,\n    num::{\n        NonZeroI8, NonZeroI16, NonZeroI32, NonZeroI64, NonZeroI128, NonZeroIsize, NonZeroU8,\n        NonZeroU16, NonZeroU32, NonZeroU64, NonZeroU128, NonZeroUsize,\n    },\n    path::{Path, PathBuf},\n    rc::Rc,\n    sync::atomic,\n    time::{Instant, SystemTime},\n};\n\nuse crate::GcErasedPointer;\n\n/// A queue used to trace [`crate::Gc<T>`] non-recursively.\n#[doc(hidden)]\n#[allow(missing_debug_implementations)]\npub struct Tracer {\n    queue: VecDeque<GcErasedPointer>,\n}\n\nimpl Tracer {\n    pub(crate) fn new() -> Self {\n        Self {\n            queue: VecDeque::default(),\n        }\n    }\n\n    pub(crate) fn enqueue(&mut self, node: GcErasedPointer) {\n        self.queue.push_back(node);\n    }\n\n    /// Traces through all the queued nodes until the queue is empty.\n    ///\n    /// # Safety\n    ///\n    /// All the pointers inside of the queue must point to valid memory.\n    pub(crate) unsafe fn trace_until_empty(&mut self) {\n        while let Some(node) = self.queue.pop_front() {\n            let node_ref = unsafe { node.as_ref() };\n            if node_ref.is_marked() {\n                continue;\n            }\n            node_ref.header.mark();\n            let trace_fn = node_ref.trace_fn();\n\n            // SAFETY: The function pointer is appropriate for this node type because we extract it from it's VTable.\n            // Additionally, the node pointer is valid per the caller's guarantee.\n            unsafe { trace_fn(node, self) }\n        }\n    }\n\n    pub(crate) fn is_empty(&mut self) -> bool {\n        self.queue.is_empty()\n    }\n}\n\n/// Substitute for the [`Drop`] trait for garbage collected types.\npub trait Finalize {\n    /// Cleanup logic for a type.\n    fn finalize(&self) {}\n}\n\n/// The Trace trait, which needs to be implemented on garbage-collected objects.\n///\n/// # Safety\n///\n/// - An incorrect implementation of the trait can result in heap overflows, data corruption,\n///   use-after-free, or Undefined Behaviour in general.\n///\n/// - Calling any of the functions marked as `unsafe` outside of the context of the garbage collector\n///   can result in Undefined Behaviour.\npub unsafe trait Trace: Finalize {\n    /// Marks all contained `Gc`s.\n    ///\n    /// # Safety\n    ///\n    /// See [`Trace`].\n    unsafe fn trace(&self, tracer: &mut Tracer);\n\n    /// Trace handles located in GC heap, and mark them as non root.\n    ///\n    /// # Safety\n    ///\n    /// See [`Trace`].\n    unsafe fn trace_non_roots(&self);\n\n    /// Runs [`Finalize::finalize`] on this object and all\n    /// contained subobjects.\n    fn run_finalizer(&self);\n}\n\n/// Utility macro to define an empty implementation of [`Trace`].\n///\n/// Use this for marking types as not containing any `Trace` types.\n#[macro_export]\nmacro_rules! empty_trace {\n    () => {\n        #[inline]\n        unsafe fn trace(&self, _tracer: &mut $crate::Tracer) {}\n        #[inline]\n        unsafe fn trace_non_roots(&self) {}\n        #[inline]\n        fn run_finalizer(&self) {\n            $crate::Finalize::finalize(self)\n        }\n    };\n}\n\n/// Utility macro to manually implement [`Trace`] on a type.\n///\n/// You define a `this` parameter name and pass in a body, which should call `mark` on every\n/// traceable element inside the body. The mark implementation will automatically delegate to the\n/// correct method on the argument.\n///\n/// # Safety\n///\n/// Misusing the `mark` function may result in Undefined Behaviour.\n#[macro_export]\nmacro_rules! custom_trace {\n    ($this:ident, $marker:ident, $body:expr) => {\n        #[inline]\n        unsafe fn trace(&self, tracer: &mut $crate::Tracer) {\n            let mut $marker = |it: &dyn $crate::Trace| {\n                // SAFETY: The implementor must ensure that `trace` is correctly implemented.\n                unsafe {\n                    $crate::Trace::trace(it, tracer);\n                }\n            };\n            let $this = self;\n            $body\n        }\n        #[inline]\n        unsafe fn trace_non_roots(&self) {\n            fn $marker<T: $crate::Trace + ?Sized>(it: &T) {\n                // SAFETY: The implementor must ensure that `trace` is correctly implemented.\n                unsafe {\n                    $crate::Trace::trace_non_roots(it);\n                }\n            }\n            let $this = self;\n            $body\n        }\n        #[inline]\n        fn run_finalizer(&self) {\n            fn $marker<T: $crate::Trace + ?Sized>(it: &T) {\n                $crate::Trace::run_finalizer(it);\n            }\n            $crate::Finalize::finalize(self);\n            let $this = self;\n            $body\n        }\n    };\n}\n\nimpl<T: ?Sized> Finalize for &'static T {}\n// SAFETY: 'static references don't need to be traced, since they live indefinitely.\nunsafe impl<T: ?Sized> Trace for &'static T {\n    empty_trace!();\n}\n\nmacro_rules! simple_empty_finalize_trace {\n    ($($T:ty),*) => {\n        $(\n            impl Finalize for $T {}\n\n            // SAFETY:\n            // Primitive types and string types don't have inner nodes that need to be marked.\n            unsafe impl Trace for $T { empty_trace!(); }\n        )*\n    }\n}\n\nsimple_empty_finalize_trace![\n    (),\n    bool,\n    isize,\n    usize,\n    i8,\n    u8,\n    i16,\n    u16,\n    i32,\n    u32,\n    i64,\n    u64,\n    i128,\n    u128,\n    f32,\n    f64,\n    char,\n    TypeId,\n    String,\n    str,\n    Rc<str>,\n    Path,\n    PathBuf,\n    Instant,\n    SystemTime,\n    NonZeroIsize,\n    NonZeroUsize,\n    NonZeroI8,\n    NonZeroU8,\n    NonZeroI16,\n    NonZeroU16,\n    NonZeroI32,\n    NonZeroU32,\n    NonZeroI64,\n    NonZeroU64,\n    NonZeroI128,\n    NonZeroU128\n];\n\n#[cfg(not(target_family = \"wasm\"))]\nsimple_empty_finalize_trace![\n    std::fs::File,\n    std::fs::FileType,\n    std::net::TcpStream,\n    std::net::UdpSocket\n];\n\n#[cfg(target_has_atomic = \"8\")]\nsimple_empty_finalize_trace![atomic::AtomicBool, atomic::AtomicI8, atomic::AtomicU8];\n\n#[cfg(target_has_atomic = \"16\")]\nsimple_empty_finalize_trace![atomic::AtomicI16, atomic::AtomicU16];\n\n#[cfg(target_has_atomic = \"32\")]\nsimple_empty_finalize_trace![atomic::AtomicI32, atomic::AtomicU32];\n\n#[cfg(target_has_atomic = \"64\")]\nsimple_empty_finalize_trace![atomic::AtomicI64, atomic::AtomicU64];\n\n#[cfg(target_has_atomic = \"ptr\")]\nsimple_empty_finalize_trace![atomic::AtomicIsize, atomic::AtomicUsize];\n\nimpl<T: Trace, const N: usize> Finalize for [T; N] {}\n// SAFETY:\n// All elements inside the array are correctly marked.\nunsafe impl<T: Trace, const N: usize> Trace for [T; N] {\n    custom_trace!(this, mark, {\n        for v in this {\n            mark(v);\n        }\n    });\n}\n\nmacro_rules! fn_finalize_trace_one {\n    ($ty:ty $(,$args:ident)*) => {\n        impl<Ret $(,$args)*> Finalize for $ty {}\n        // SAFETY:\n        // Function pointers don't have inner nodes that need to be marked.\n        unsafe impl<Ret $(,$args)*> Trace for $ty { empty_trace!(); }\n    }\n}\nmacro_rules! fn_finalize_trace_group {\n    () => {\n        fn_finalize_trace_one!(extern \"Rust\" fn () -> Ret);\n        fn_finalize_trace_one!(extern \"C\" fn () -> Ret);\n        fn_finalize_trace_one!(unsafe extern \"Rust\" fn () -> Ret);\n        fn_finalize_trace_one!(unsafe extern \"C\" fn () -> Ret);\n    };\n    ($($args:ident),*) => {\n        fn_finalize_trace_one!(extern \"Rust\" fn ($($args),*) -> Ret, $($args),*);\n        fn_finalize_trace_one!(extern \"C\" fn ($($args),*) -> Ret, $($args),*);\n        fn_finalize_trace_one!(extern \"C\" fn ($($args),*, ...) -> Ret, $($args),*);\n        fn_finalize_trace_one!(unsafe extern \"Rust\" fn ($($args),*) -> Ret, $($args),*);\n        fn_finalize_trace_one!(unsafe extern \"C\" fn ($($args),*) -> Ret, $($args),*);\n        fn_finalize_trace_one!(unsafe extern \"C\" fn ($($args),*, ...) -> Ret, $($args),*);\n    }\n}\n\nmacro_rules! tuple_finalize_trace {\n    () => {}; // This case is handled above, by simple_finalize_empty_trace!().\n    ($($args:ident),*) => {\n        impl<$($args),*> Finalize for ($($args,)*) {}\n        // SAFETY:\n        // All elements inside the tuple are correctly marked.\n        unsafe impl<$($args: $crate::Trace),*> Trace for ($($args,)*) {\n            custom_trace!(this, mark, {\n                #[allow(non_snake_case, unused_unsafe, unused_mut)]\n                let mut avoid_lints = |&($(ref $args,)*): &($($args,)*)| {\n                    // SAFETY: The implementor must ensure a correct implementation.\n                    unsafe { $(mark($args);)* }\n                };\n                avoid_lints(this)\n            });\n        }\n    }\n}\n\nmacro_rules! type_arg_tuple_based_finalize_trace_impls {\n    ($(($($args:ident),*);)*) => {\n        $(\n            fn_finalize_trace_group!($($args),*);\n            tuple_finalize_trace!($($args),*);\n        )*\n    }\n}\n\ntype_arg_tuple_based_finalize_trace_impls![\n    ();\n    (A);\n    (A, B);\n    (A, B, C);\n    (A, B, C, D);\n    (A, B, C, D, E);\n    (A, B, C, D, E, F);\n    (A, B, C, D, E, F, G);\n    (A, B, C, D, E, F, G, H);\n    (A, B, C, D, E, F, G, H, I);\n    (A, B, C, D, E, F, G, H, I, J);\n    (A, B, C, D, E, F, G, H, I, J, K);\n    (A, B, C, D, E, F, G, H, I, J, K, L);\n];\n\nimpl<T: Trace + ?Sized> Finalize for Box<T> {}\n// SAFETY: The inner value of the `Box` is correctly marked.\nunsafe impl<T: Trace + ?Sized> Trace for Box<T> {\n    #[inline]\n    unsafe fn trace(&self, tracer: &mut Tracer) {\n        // SAFETY: The implementor must ensure that `trace` is correctly implemented.\n        unsafe {\n            Trace::trace(&**self, tracer);\n        }\n    }\n    #[inline]\n    unsafe fn trace_non_roots(&self) {\n        // SAFETY: The implementor must ensure that `trace_non_roots` is correctly implemented.\n        unsafe {\n            Trace::trace_non_roots(&**self);\n        }\n    }\n    #[inline]\n    fn run_finalizer(&self) {\n        Finalize::finalize(self);\n        Trace::run_finalizer(&**self);\n    }\n}\n\nimpl<T: Trace> Finalize for Box<[T]> {}\n// SAFETY: All the inner elements of the `Box` array are correctly marked.\nunsafe impl<T: Trace> Trace for Box<[T]> {\n    custom_trace!(this, mark, {\n        for e in &**this {\n            mark(e);\n        }\n    });\n}\n\nimpl<T: Trace> Finalize for Vec<T> {}\n// SAFETY: All the inner elements of the `Vec` are correctly marked.\nunsafe impl<T: Trace> Trace for Vec<T> {\n    custom_trace!(this, mark, {\n        for e in this {\n            mark(e);\n        }\n    });\n}\n\n#[cfg(feature = \"thin-vec\")]\nimpl<T: Trace> Finalize for thin_vec::ThinVec<T> {}\n\n#[cfg(feature = \"thin-vec\")]\n// SAFETY: All the inner elements of the `Vec` are correctly marked.\nunsafe impl<T: Trace> Trace for thin_vec::ThinVec<T> {\n    custom_trace!(this, mark, {\n        for e in this {\n            mark(e);\n        }\n    });\n}\n\n#[cfg(feature = \"arrayvec\")]\nimpl<T: Trace, const N: usize> Finalize for arrayvec::ArrayVec<T, N> {}\n\n#[cfg(feature = \"arrayvec\")]\n// SAFETY: All the inner elements of the `ArrayVec` are correctly marked.\nunsafe impl<T: Trace, const N: usize> Trace for arrayvec::ArrayVec<T, N> {\n    custom_trace!(this, mark, {\n        for e in this {\n            mark(e);\n        }\n    });\n}\n\nimpl<T: Trace> Finalize for Option<T> {}\n// SAFETY: The inner value of the `Option` is correctly marked.\nunsafe impl<T: Trace> Trace for Option<T> {\n    custom_trace!(this, mark, {\n        if let Some(ref v) = *this {\n            mark(v);\n        }\n    });\n}\n\nimpl<T: Trace, E: Trace> Finalize for Result<T, E> {}\n// SAFETY: Both inner values of the `Result` are correctly marked.\nunsafe impl<T: Trace, E: Trace> Trace for Result<T, E> {\n    custom_trace!(this, mark, {\n        match *this {\n            Ok(ref v) => mark(v),\n            Err(ref v) => mark(v),\n        }\n    });\n}\n\nimpl<T: Ord + Trace> Finalize for BinaryHeap<T> {}\n// SAFETY: All the elements of the `BinaryHeap` are correctly marked.\nunsafe impl<T: Ord + Trace> Trace for BinaryHeap<T> {\n    custom_trace!(this, mark, {\n        for v in this {\n            mark(v);\n        }\n    });\n}\n\nimpl<K: Trace, V: Trace> Finalize for BTreeMap<K, V> {}\n// SAFETY: All the elements of the `BTreeMap` are correctly marked.\nunsafe impl<K: Trace, V: Trace> Trace for BTreeMap<K, V> {\n    custom_trace!(this, mark, {\n        for (k, v) in this {\n            mark(k);\n            mark(v);\n        }\n    });\n}\n\nimpl<T: Trace> Finalize for BTreeSet<T> {}\n// SAFETY: All the elements of the `BTreeSet` are correctly marked.\nunsafe impl<T: Trace> Trace for BTreeSet<T> {\n    custom_trace!(this, mark, {\n        for v in this {\n            mark(v);\n        }\n    });\n}\n\nimpl<K: Eq + Hash + Trace, V: Trace, S: BuildHasher> Finalize\n    for hashbrown::hash_map::HashMap<K, V, S>\n{\n}\n// SAFETY: All the elements of the `HashMap` are correctly marked.\nunsafe impl<K: Eq + Hash + Trace, V: Trace, S: BuildHasher> Trace\n    for hashbrown::hash_map::HashMap<K, V, S>\n{\n    custom_trace!(this, mark, {\n        for (k, v) in this {\n            mark(k);\n            mark(v);\n        }\n    });\n}\n\nimpl<K: Eq + Hash + Trace, V: Trace, S: BuildHasher> Finalize for HashMap<K, V, S> {}\n// SAFETY: All the elements of the `HashMap` are correctly marked.\nunsafe impl<K: Eq + Hash + Trace, V: Trace, S: BuildHasher> Trace for HashMap<K, V, S> {\n    custom_trace!(this, mark, {\n        for (k, v) in this {\n            mark(k);\n            mark(v);\n        }\n    });\n}\n\nimpl<T: Eq + Hash + Trace, S: BuildHasher> Finalize for HashSet<T, S> {}\n// SAFETY: All the elements of the `HashSet` are correctly marked.\nunsafe impl<T: Eq + Hash + Trace, S: BuildHasher> Trace for HashSet<T, S> {\n    custom_trace!(this, mark, {\n        for v in this {\n            mark(v);\n        }\n    });\n}\n\nimpl<T: Eq + Hash + Trace> Finalize for LinkedList<T> {}\n// SAFETY: All the elements of the `LinkedList` are correctly marked.\nunsafe impl<T: Eq + Hash + Trace> Trace for LinkedList<T> {\n    custom_trace!(this, mark, {\n        #[allow(clippy::explicit_iter_loop)]\n        for v in this.iter() {\n            mark(v);\n        }\n    });\n}\n\nimpl<T> Finalize for PhantomData<T> {}\n// SAFETY: A `PhantomData` doesn't have inner data that needs to be marked.\nunsafe impl<T> Trace for PhantomData<T> {\n    empty_trace!();\n}\n\nimpl<T: Trace> Finalize for VecDeque<T> {}\n// SAFETY: All the elements of the `VecDeque` are correctly marked.\nunsafe impl<T: Trace> Trace for VecDeque<T> {\n    custom_trace!(this, mark, {\n        for v in this {\n            mark(v);\n        }\n    });\n}\n\nimpl<T: ToOwned + Trace + ?Sized> Finalize for Cow<'static, T> {}\n// SAFETY: 'static references don't need to be traced, since they live indefinitely, and the owned\n// variant is correctly marked.\nunsafe impl<T: ToOwned + Trace + ?Sized> Trace for Cow<'static, T>\nwhere\n    T::Owned: Trace,\n{\n    custom_trace!(this, mark, {\n        if let Cow::Owned(v) = this {\n            mark(v);\n        }\n    });\n}\n\nimpl<T: Trace> Finalize for Cell<Option<T>> {}\n// SAFETY: Taking and setting is done in a single action, and recursive traces should find a `None`\n// value instead of the original `T`, making this safe.\nunsafe impl<T: Trace> Trace for Cell<Option<T>> {\n    custom_trace!(this, mark, {\n        if let Some(v) = this.take() {\n            mark(&v);\n            this.set(Some(v));\n        }\n    });\n}\n\nimpl<T: Trace> Finalize for OnceCell<T> {}\n// SAFETY: We only trace the inner cell if the cell has a value.\nunsafe impl<T: Trace> Trace for OnceCell<T> {\n    custom_trace!(this, mark, {\n        if let Some(v) = this.get() {\n            mark(v);\n        }\n    });\n}\n\n#[cfg(feature = \"icu\")]\nmod icu {\n    use icu_locale_core::{LanguageIdentifier, Locale};\n\n    use crate::{Finalize, Trace};\n\n    impl Finalize for LanguageIdentifier {}\n\n    // SAFETY: `LanguageIdentifier` doesn't have any traceable data.\n    unsafe impl Trace for LanguageIdentifier {\n        empty_trace!();\n    }\n\n    impl Finalize for Locale {}\n\n    // SAFETY: `LanguageIdentifier` doesn't have any traceable data.\n    unsafe impl Trace for Locale {\n        empty_trace!();\n    }\n}\n\n#[cfg(feature = \"boa_string\")]\nmod boa_string_trace {\n    use crate::{Finalize, Trace};\n\n    // SAFETY: `boa_string::JsString` doesn't have any traceable data.\n    unsafe impl Trace for boa_string::JsString {\n        empty_trace!();\n    }\n\n    impl Finalize for boa_string::JsString {}\n}\n#[cfg(feature = \"either\")]\nmod either_trace {\n    use crate::{Finalize, Trace};\n\n    impl<L: Trace, R: Trace> Finalize for either::Either<L, R> {}\n\n    unsafe impl<L: Trace, R: Trace> Trace for either::Either<L, R> {\n        custom_trace!(this, mark, {\n            match this {\n                either::Either::Left(l) => mark(l),\n                either::Either::Right(r) => mark(r),\n            }\n        });\n    }\n}\n"
  },
  {
    "path": "core/icu_provider/ABOUT.md",
    "content": "# About Boa\n\nBoa is an open-source, experimental ECMAScript Engine written in Rust for\nlexing, parsing and executing ECMAScript/JavaScript. Currently, Boa supports some\nof the [language][boa-conformance]. More information can be viewed at [Boa's\nwebsite][boa-web].\n\nTry out the most recent release with Boa's live demo\n[playground][boa-playground].\n\n## Boa Crates\n\n- [**`boa_cli`**][cli] - Boa's CLI && REPL implementation\n- [**`boa_ast`**][ast] - Boa's ECMAScript Abstract Syntax Tree.\n- [**`boa_engine`**][engine] - Boa's implementation of ECMAScript builtin objects and execution.\n- [**`boa_gc`**][gc] - Boa's garbage collector.\n- [**`boa_icu_provider`**][icu] - Boa's ICU4X data provider.\n- [**`boa_interner`**][interner] - Boa's string interner.\n- [**`boa_macros`**][macros] - Boa's macros.\n- [**`boa_parser`**][parser] - Boa's lexer and parser.\n- [**`boa_runtime`**][runtime] - Boa's `WebAPI` features.\n- [**`boa_string`**][string] - Boa's ECMAScript string implementation.\n- [**`boa_wintertc`**][wintertc] - Boa's `WinterTC` (TC55) Minimum Common Web API implementation.\n- [**`tag_ptr`**][tag_ptr] - Utility library that enables a pointer to be associated with a tag of type `usize`.\n- [**`small_btree`**][small_btree] - Utility library that adds the `SmallBTreeMap` data structure.\n\n[boa-conformance]: https://boajs.dev/conformance\n[boa-web]: https://boajs.dev/\n[boa-playground]: https://boajs.dev/playground\n[ast]: https://docs.rs/boa_ast/latest/boa_ast/index.html\n[engine]: https://docs.rs/boa_engine/latest/boa_engine/index.html\n[gc]: https://docs.rs/boa_gc/latest/boa_gc/index.html\n[interner]: https://docs.rs/boa_interner/latest/boa_interner/index.html\n[parser]: https://docs.rs/boa_parser/latest/boa_parser/index.html\n[icu]: https://docs.rs/boa_icu_provider/latest/boa_icu_provider/index.html\n[runtime]: https://docs.rs/boa_runtime/latest/boa_runtime/index.html\n[string]: https://docs.rs/boa_string/latest/boa_string/index.html\n[wintertc]: https://docs.rs/boa_wintertc/latest/boa_wintertc/index.html\n[tag_ptr]: https://docs.rs/tag_ptr/latest/tag_ptr/index.html\n[small_btree]: https://docs.rs/small_btree/latest/small_btree/index.html\n[macros]: https://docs.rs/boa_macros/latest/boa_macros/index.html\n[cli]: https://crates.io/crates/boa_cli\n"
  },
  {
    "path": "core/icu_provider/Cargo.toml",
    "content": "[package]\nname = \"boa_icu_provider\"\ndescription = \"ICU4X data provider for the Boa JavaScript engine.\"\nkeywords = [\"javascript\", \"cldr\", \"unicode\"]\ncategories = [\"internationalization\", \"no-std\"]\nversion.workspace = true\nedition.workspace = true\nauthors.workspace = true\nlicense.workspace = true\nrepository.workspace = true\nrust-version.workspace = true\n\n[dependencies]\nicu_provider = { workspace = true, features = [\"sync\"] }\nicu_provider_blob.workspace = true\nicu_provider_adapters = { workspace = true, features = [\"serde\"] }\nicu_casemap = { workspace = true, features = [\"serde\", \"datagen\"] }\nicu_collator = { workspace = true, features = [\"serde\", \"datagen\"] }\nicu_datetime = { workspace = true, features = [\"serde\", \"datagen\"] }\nicu_time = { workspace = true, features = [\"serde\", \"datagen\"] }\nicu_decimal = { workspace = true, features = [\"serde\", \"datagen\"] }\nicu_list = { workspace = true, features = [\"serde\", \"datagen\"] }\nicu_locale = { workspace = true, features = [\"serde\", \"datagen\"] }\nicu_normalizer = { workspace = true, features = [\"serde\", \"datagen\"] }\nicu_plurals = { workspace = true, features = [\"serde\", \"datagen\", \"experimental\"] }\nicu_segmenter = { workspace = true, features = [\"serde\", \"datagen\"] }\nonce_cell = { workspace = true, default-features = false, features = [\"critical-section\"] }\npaste.workspace = true\n\n[features]\ndefault = [\"std\"]\nstd = [\"once_cell/std\"]\n\n[lints]\nworkspace = true\n\n[package.metadata.docs.rs]\nall-features = true\n"
  },
  {
    "path": "core/icu_provider/README.md",
    "content": "# boa_icu_provider\n\n`boa_icu_provider` defines the [ICU4X](https://github.com/unicode-org/icu4x) data provider\nused in the Boa engine to enable internationalization functionality.\n"
  },
  {
    "path": "core/icu_provider/src/lib.rs",
    "content": "//! Boa's **`boa_icu_provider`** exports the default data provider used by its `Intl` implementation.\n//!\n//! # Crate Overview\n//! This crate exports the function `buffer`, which contains an extensive dataset of locale data to\n//! enable `Intl` functionality in the engine. The set of locales included is precisely the [\"modern\"]\n//! subset of locales in the [Unicode Common Locale Data Repository][cldr].\n//!\n//! If you need to support the full set of locales, you can check out the [ICU4X guide] about\n//! generating custom data providers. Boa supports plugging any [`BufferProvider`]s\n//! generated by the tool.\n//!\n//! [\"modern\"]: https://github.com/unicode-org/cldr-json/tree/main/cldr-json/cldr-localenames-modern/main\n//! [cldr]: https://github.com/unicode-org/\n//! [ICU4X guide]: https://github.com/unicode-org/icu4x/blob/main/docs/tutorials/data_management.md\n//! [`BufferProvider`]: icu_provider::buf::BufferProvider\n#![doc = include_str!(\"../ABOUT.md\")]\n#![doc(\n    html_logo_url = \"https://raw.githubusercontent.com/boa-dev/boa/main/assets/logo_black.svg\",\n    html_favicon_url = \"https://raw.githubusercontent.com/boa-dev/boa/main/assets/logo_black.svg\"\n)]\n#![cfg_attr(not(feature = \"std\"), no_std)]\n\nextern crate alloc;\nuse core::fmt::Debug;\n\nuse icu_locale::LocaleFallbacker;\nuse icu_provider::prelude::*;\nuse icu_provider_adapters::{fallback::LocaleFallbackProvider, fork::MultiForkByMarkerProvider};\nuse icu_provider_blob::BlobDataProvider;\nuse once_cell::sync::{Lazy, OnceCell};\n\n/// A buffer provider that is lazily deserialized at the first data request.\n///\n/// The provider must specify the list of keys it supports, to avoid deserializing the\n/// buffer for unknown keys.\nstruct LazyBufferProvider {\n    provider: OnceCell<BlobDataProvider>,\n    bytes: &'static [u8],\n    valid_markers: &'static [DataMarkerInfo],\n}\n\nimpl Debug for LazyBufferProvider {\n    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {\n        f.debug_struct(\"LazyBufferProvider\")\n            .field(\"provider\", &self.provider)\n            .field(\"bytes\", &\"[...]\")\n            .field(\"valid_keys\", &self.valid_markers)\n            .finish()\n    }\n}\n\nimpl DynamicDataProvider<BufferMarker> for LazyBufferProvider {\n    fn load_data(\n        &self,\n        marker: DataMarkerInfo,\n        req: DataRequest<'_>,\n    ) -> Result<DataResponse<BufferMarker>, DataError> {\n        if !self.valid_markers.contains(&marker) {\n            return Err(DataErrorKind::MarkerNotFound.with_marker(marker));\n        }\n\n        let Ok(provider) = self\n            .provider\n            .get_or_try_init(|| BlobDataProvider::try_new_from_static_blob(self.bytes))\n        else {\n            return Err(DataErrorKind::Custom.with_str_context(\"invalid blob data provider\"));\n        };\n\n        provider.load_data(marker, req)\n    }\n}\n\nimpl DynamicDryDataProvider<BufferMarker> for LazyBufferProvider {\n    fn dry_load_data(\n        &self,\n        marker: DataMarkerInfo,\n        req: DataRequest<'_>,\n    ) -> Result<DataResponseMetadata, DataError> {\n        if !self.valid_markers.contains(&marker) {\n            return Err(DataErrorKind::MarkerNotFound.with_marker(marker));\n        }\n\n        let Ok(provider) = self\n            .provider\n            .get_or_try_init(|| BlobDataProvider::try_new_from_static_blob(self.bytes))\n        else {\n            return Err(DataErrorKind::Custom.with_str_context(\"invalid blob data provider\"));\n        };\n\n        provider.dry_load_data(marker, req)\n    }\n}\n\n/// A macro that creates a [`LazyBufferProvider`] from an icu4x crate.\nmacro_rules! provider_from_icu_crate {\n    ($service:path) => {\n        paste::paste! {\n            LazyBufferProvider {\n                provider: OnceCell::new(),\n                bytes: include_bytes!(concat!(\n                    env!(\"CARGO_MANIFEST_DIR\"),\n                    \"/data/\",\n                    stringify!($service),\n                    \".postcard\",\n                )),\n                valid_markers: $service::provider::MARKERS,\n            }\n        }\n    };\n}\n\n/// Boa's default buffer provider.\nstatic PROVIDER: Lazy<LocaleFallbackProvider<MultiForkByMarkerProvider<LazyBufferProvider>>> =\n    Lazy::new(|| {\n        let provider = MultiForkByMarkerProvider::new(alloc::vec![\n            provider_from_icu_crate!(icu_casemap),\n            provider_from_icu_crate!(icu_collator),\n            provider_from_icu_crate!(icu_datetime),\n            provider_from_icu_crate!(icu_time),\n            provider_from_icu_crate!(icu_decimal),\n            provider_from_icu_crate!(icu_list),\n            provider_from_icu_crate!(icu_locale),\n            provider_from_icu_crate!(icu_normalizer),\n            provider_from_icu_crate!(icu_plurals),\n            provider_from_icu_crate!(icu_segmenter),\n        ]);\n        let fallbacker = LocaleFallbacker::try_new_with_buffer_provider(&provider)\n            .expect(\"The statically compiled data file should be valid.\");\n        LocaleFallbackProvider::new(provider, fallbacker)\n    });\n\n#[derive(Debug)]\nstruct Wrapper<T: 'static>(&'static T);\n\nimpl<T> DynamicDataProvider<BufferMarker> for Wrapper<T>\nwhere\n    T: DynamicDataProvider<BufferMarker>,\n{\n    fn load_data(\n        &self,\n        marker: DataMarkerInfo,\n        req: DataRequest<'_>,\n    ) -> Result<DataResponse<BufferMarker>, DataError> {\n        self.0.load_data(marker, req)\n    }\n}\n\nimpl<T> DynamicDryDataProvider<BufferMarker> for Wrapper<T>\nwhere\n    T: DynamicDryDataProvider<BufferMarker>,\n{\n    fn dry_load_data(\n        &self,\n        marker: DataMarkerInfo,\n        req: DataRequest<'_>,\n    ) -> Result<DataResponseMetadata, DataError> {\n        self.0.dry_load_data(marker, req)\n    }\n}\n\n/// Gets the default data provider stored as a [`DynamicDryDataProvider<BufferMarker>`].\n///\n/// [`DynamicDryDataProvider<BufferMarker>`]: icu_provider::DynamicDryDataProvider\n#[must_use]\npub fn buffer() -> impl DynamicDryDataProvider<BufferMarker> {\n    Wrapper(&*PROVIDER)\n}\n"
  },
  {
    "path": "core/interner/ABOUT.md",
    "content": "# About Boa\n\nBoa is an open-source, experimental ECMAScript Engine written in Rust for\nlexing, parsing and executing ECMAScript/JavaScript. Currently, Boa supports some\nof the [language][boa-conformance]. More information can be viewed at [Boa's\nwebsite][boa-web].\n\nTry out the most recent release with Boa's live demo\n[playground][boa-playground].\n\n## Boa Crates\n\n- [**`boa_cli`**][cli] - Boa's CLI && REPL implementation\n- [**`boa_ast`**][ast] - Boa's ECMAScript Abstract Syntax Tree.\n- [**`boa_engine`**][engine] - Boa's implementation of ECMAScript builtin objects and execution.\n- [**`boa_gc`**][gc] - Boa's garbage collector.\n- [**`boa_icu_provider`**][icu] - Boa's ICU4X data provider.\n- [**`boa_interner`**][interner] - Boa's string interner.\n- [**`boa_macros`**][macros] - Boa's macros.\n- [**`boa_parser`**][parser] - Boa's lexer and parser.\n- [**`boa_runtime`**][runtime] - Boa's `WebAPI` features.\n- [**`boa_string`**][string] - Boa's ECMAScript string implementation.\n- [**`boa_wintertc`**][wintertc] - Boa's `WinterTC` (TC55) Minimum Common Web API implementation.\n- [**`tag_ptr`**][tag_ptr] - Utility library that enables a pointer to be associated with a tag of type `usize`.\n- [**`small_btree`**][small_btree] - Utility library that adds the `SmallBTreeMap` data structure.\n\n[boa-conformance]: https://boajs.dev/conformance\n[boa-web]: https://boajs.dev/\n[boa-playground]: https://boajs.dev/playground\n[ast]: https://docs.rs/boa_ast/latest/boa_ast/index.html\n[engine]: https://docs.rs/boa_engine/latest/boa_engine/index.html\n[gc]: https://docs.rs/boa_gc/latest/boa_gc/index.html\n[interner]: https://docs.rs/boa_interner/latest/boa_interner/index.html\n[parser]: https://docs.rs/boa_parser/latest/boa_parser/index.html\n[icu]: https://docs.rs/boa_icu_provider/latest/boa_icu_provider/index.html\n[runtime]: https://docs.rs/boa_runtime/latest/boa_runtime/index.html\n[string]: https://docs.rs/boa_string/latest/boa_string/index.html\n[wintertc]: https://docs.rs/boa_wintertc/latest/boa_wintertc/index.html\n[tag_ptr]: https://docs.rs/tag_ptr/latest/tag_ptr/index.html\n[small_btree]: https://docs.rs/small_btree/latest/small_btree/index.html\n[macros]: https://docs.rs/boa_macros/latest/boa_macros/index.html\n[cli]: https://crates.io/crates/boa_cli\n"
  },
  {
    "path": "core/interner/Cargo.toml",
    "content": "[package]\nname = \"boa_interner\"\ndescription = \"String interner for the Boa JavaScript engine.\"\nkeywords = [\"javascript\", \"js\", \"string\", \"interner\"]\ncategories = [\"data-structures\", \"no-std\"]\nversion.workspace = true\nedition.workspace = true\nauthors.workspace = true\nlicense.workspace = true\nrepository.workspace = true\nrust-version.workspace = true\n\n[features]\nserde = [\"dep:serde\"]\narbitrary = [\"dep:arbitrary\"]\n\n[dependencies]\nboa_macros.workspace = true\nboa_gc.workspace = true\nphf = { workspace = true, default-features = false, features = [\"macros\"] }\nrustc-hash = { workspace = true, default-features = false }\nstatic_assertions.workspace = true\nonce_cell = { workspace = true, features = [\"std\"]}\nindexmap.workspace = true\nserde = { workspace = true, features = [\"derive\"], optional = true }\narbitrary = { workspace = true, features = [\"derive\"], optional = true }\nhashbrown.workspace = true\n\n[lints]\nworkspace = true\n\n[package.metadata.docs.rs]\nall-features = true\n"
  },
  {
    "path": "core/interner/src/fixed_string.rs",
    "content": "use alloc::vec::Vec;\n\nuse crate::interned_str::InternedStr;\n\n#[derive(Debug)]\npub(super) struct FixedString<Char> {\n    inner: Vec<Char>,\n}\n\nimpl<Char> Default for FixedString<Char> {\n    fn default() -> Self {\n        Self {\n            inner: Vec::default(),\n        }\n    }\n}\n\nimpl<Char> FixedString<Char> {\n    /// Creates a new, pinned [`FixedString`].\n    pub(super) fn new(capacity: usize) -> Self {\n        Self {\n            inner: Vec::with_capacity(capacity),\n        }\n    }\n\n    /// Gets the maximum capacity of the [`FixedString`].\n    pub(super) fn capacity(&self) -> usize {\n        self.inner.capacity()\n    }\n\n    /// Returns `true` if the [`FixedString`] has length zero,\n    /// and `false` otherwise.\n    pub(super) fn is_empty(&self) -> bool {\n        self.inner.is_empty()\n    }\n}\n\nimpl<Char> FixedString<Char>\nwhere\n    Char: Clone,\n{\n    /// Tries to push `string` to the [`FixedString`], and returns\n    /// an [`InternedStr`] pointer to the stored `string`, or\n    /// `None` if the capacity is not enough to store `string`.\n    ///\n    /// # Safety\n    ///\n    /// The caller is responsible for ensuring `self` outlives the returned\n    /// [`InternedStr`].\n    pub(super) unsafe fn push(&mut self, string: &[Char]) -> Option<InternedStr<Char>> {\n        let capacity = self.inner.capacity();\n        (capacity >= self.inner.len() + string.len()).then(|| {\n            // SAFETY:\n            // The caller is responsible for extending the lifetime\n            // of `self` to outlive the return value.\n            unsafe { self.push_unchecked(string) }\n        })\n    }\n\n    /// Pushes `string` to the [`FixedString`], and returns\n    /// an [`InternedStr`] pointer to the stored `string`, without\n    /// checking if the total `capacity` is enough to store `string`,\n    /// and without checking if the string is correctly aligned.\n    ///\n    /// # Safety\n    ///\n    /// The caller is responsible for ensuring that `self` outlives the returned\n    /// [`InternedStr`] and that it has enough capacity to store `string` without\n    /// reallocating.\n    pub(super) unsafe fn push_unchecked(&mut self, string: &[Char]) -> InternedStr<Char> {\n        let old_len = self.inner.len();\n        self.inner.extend_from_slice(string);\n\n        // SAFETY: The caller is responsible for extending the lifetime\n        // of `self` to outlive the return value, and for ensuring\n        // the alignment of `string` is correct.\n        let ptr = &self.inner[old_len..self.inner.len()];\n        unsafe { InternedStr::new(ptr.into()) }\n    }\n}\n"
  },
  {
    "path": "core/interner/src/interned_str.rs",
    "content": "use core::{hash::Hash, ptr::NonNull};\n\n/// Wrapper for an interned str pointer, required to\n/// quickly check using a hash if a string is inside an [`Interner`][`super::Interner`].\n///\n/// # Safety\n///\n/// This struct could cause Undefined Behaviour on:\n/// - Use without ensuring the referenced memory is still allocated.\n/// - Construction of an [`InternedStr`] from an invalid [`NonNull<Char>`] pointer.\n/// - Construction of an [`InternedStr`] from a [`NonNull<Char>`] pointer\n///   without checking if the pointed memory of the [`NonNull<Char>`] outlives\n///   the [`InternedStr`].\n///\n/// In general, this should not be used outside of an [`Interner`][`super::Interner`].\n#[derive(Debug)]\npub(super) struct InternedStr<Char> {\n    ptr: NonNull<[Char]>,\n}\n\nimpl<Char> InternedStr<Char> {\n    /// Create a new interned string from the given `*const u8` pointer,\n    /// length and encoding kind\n    ///\n    /// # Safety\n    ///\n    /// Not maintaining the invariants specified on the struct definition\n    /// could cause Undefined Behaviour.\n    pub(super) const unsafe fn new(ptr: NonNull<[Char]>) -> Self {\n        Self { ptr }\n    }\n\n    /// Returns a shared reference to the underlying string.\n    ///\n    /// # Safety\n    ///\n    /// Not maintaining the invariants specified on the struct definition\n    /// could cause Undefined Behaviour.\n    pub(super) unsafe fn as_ref(&self) -> &[Char] {\n        // SAFETY:\n        // The caller must ensure `ptr` is still valid throughout the\n        // lifetime of `self`.\n        unsafe { self.ptr.as_ref() }\n    }\n}\n\nimpl<Char> Clone for InternedStr<Char> {\n    fn clone(&self) -> Self {\n        *self\n    }\n}\n\nimpl<Char> Copy for InternedStr<Char> {}\n\nimpl<Char> Eq for InternedStr<Char> where Char: Eq {}\n\nimpl<Char> PartialEq for InternedStr<Char>\nwhere\n    Char: PartialEq,\n{\n    fn eq(&self, other: &Self) -> bool {\n        // SAFETY: The caller must verify the invariants\n        // specified in the struct definition.\n        unsafe { self.as_ref() == other.as_ref() }\n    }\n}\n\nimpl<Char> Hash for InternedStr<Char>\nwhere\n    Char: Hash,\n{\n    fn hash<H: core::hash::Hasher>(&self, state: &mut H) {\n        // SAFETY:\n        // The caller must ensure `ptr` is still valid throughout the\n        // lifetime of `self`.\n        unsafe {\n            self.as_ref().hash(state);\n        }\n    }\n}\n"
  },
  {
    "path": "core/interner/src/lib.rs",
    "content": "//! Boa's **`boa_interner`** is a string interner for compiler performance.\n//!\n//! # Crate Overview\n//!\n//! The idea behind using a string interner is that in most of the code, strings such as\n//! identifiers and literals are often repeated. This causes extra burden when comparing them and\n//! storing them. A string interner stores a unique `usize` symbol for each string, making sure\n//! that there are no duplicates. This makes it much easier to compare, since it's just comparing\n//! to `usize`, and also it's easier to store, since instead of a heap-allocated string, you only\n//! need to store a `usize`. This reduces memory consumption and improves performance in the\n//! compiler.\n#![doc = include_str!(\"../ABOUT.md\")]\n#![doc(\n    html_logo_url = \"https://raw.githubusercontent.com/boa-dev/boa/main/assets/logo_black.svg\",\n    html_favicon_url = \"https://raw.githubusercontent.com/boa-dev/boa/main/assets/logo_black.svg\"\n)]\n#![cfg_attr(not(test), forbid(clippy::unwrap_used))]\n#![allow(\n    clippy::redundant_pub_crate,\n    // TODO deny once false positive is fixed (https://github.com/rust-lang/rust-clippy/issues/9626).\n    clippy::trait_duplication_in_bounds,\n    // Field names intentionally mirror the encoding type they store.\n    clippy::struct_field_names\n)]\n#![cfg_attr(not(feature = \"arbitrary\"), no_std)]\n\nextern crate alloc;\n\nmod fixed_string;\nmod interned_str;\nmod raw;\nmod sym;\n\n#[cfg(test)]\nmod tests;\n\nuse alloc::{borrow::Cow, format, string::String, vec::Vec};\nuse raw::RawInterner;\n\npub use sym::*;\n\n/// An enumeration of all slice types [`Interner`] can internally store.\n///\n/// This struct allows us to intern either `UTF-8` or `UTF-16` str references, which are the two\n/// encodings [`Interner`] can store.\n#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]\npub enum JStrRef<'a> {\n    /// A `UTF-8` string reference.\n    Utf8(&'a str),\n\n    /// A `UTF-16` string reference.\n    Utf16(&'a [u16]),\n}\n\nimpl<'a> From<&'a str> for JStrRef<'a> {\n    fn from(s: &'a str) -> Self {\n        JStrRef::Utf8(s)\n    }\n}\n\nimpl<'a> From<&'a [u16]> for JStrRef<'a> {\n    fn from(s: &'a [u16]) -> Self {\n        JStrRef::Utf16(s)\n    }\n}\n\nimpl<'a, const N: usize> From<&'a [u16; N]> for JStrRef<'a> {\n    fn from(s: &'a [u16; N]) -> Self {\n        JStrRef::Utf16(s)\n    }\n}\n\n/// A double reference to an interned string inside [`Interner`].\n///\n/// [`JSInternedStrRef::utf8`] returns an [`Option`], since not every `UTF-16` string is fully\n/// representable as a `UTF-8` string (because of unpaired surrogates). However, every `UTF-8`\n/// string is representable as a `UTF-16` string, so `JSInternedStrRef::utf8` returns a\n/// [<code>&\\[u16\\]</code>][core::slice].\n#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]\npub struct JSInternedStrRef<'a, 'b> {\n    utf8: Option<&'a str>,\n    utf16: &'b [u16],\n}\n\nimpl<'a, 'b> JSInternedStrRef<'a, 'b> {\n    /// Returns the inner reference to the interned string in `UTF-8` encoding.\n    /// if the string is not representable in `UTF-8`, returns [`None`]\n    ///\n    /// # Examples\n    ///\n    /// ```\n    /// use boa_interner::Interner;\n    ///\n    /// let mut interner = Interner::new();\n    /// let sym = interner.get_or_intern(\"hello\");\n    /// let interned = interner.resolve_expect(sym);\n    /// assert_eq!(interned.utf8(), Some(\"hello\"));\n    /// ```\n    #[inline]\n    #[must_use]\n    pub const fn utf8(&self) -> Option<&'a str> {\n        self.utf8\n    }\n\n    /// Returns the inner reference to the interned string in `UTF-16` encoding.\n    ///\n    /// # Examples\n    ///\n    /// ```\n    /// use boa_interner::Interner;\n    ///\n    /// let mut interner = Interner::new();\n    /// let sym = interner.get_or_intern(\"hello\");\n    /// let interned = interner.resolve_expect(sym);\n    /// let utf16: Vec<u16> = \"hello\".encode_utf16().collect();\n    /// assert_eq!(interned.utf16(), utf16.as_slice());\n    /// ```\n    #[inline]\n    #[must_use]\n    pub const fn utf16(&self) -> &'b [u16] {\n        self.utf16\n    }\n\n    /// Joins the result of both possible strings into a common type.\n    ///\n    /// If `self` is representable by a `UTF-8` string and the `prioritize_utf8` argument is set,\n    /// it will prioritize calling `f`, and will only call `g` if `self` is only representable by a\n    /// `UTF-16` string. Otherwise, it will directly call `g`.\n    ///\n    /// # Examples\n    ///\n    /// ```\n    /// use boa_interner::Interner;\n    ///\n    /// let mut interner = Interner::new();\n    /// let sym = interner.get_or_intern(\"hello\");\n    /// let interned = interner.resolve_expect(sym);\n    /// let result = interned.join(\n    ///     |utf8| utf8.to_uppercase(),\n    ///     |utf16| String::from_utf16_lossy(utf16).to_uppercase(),\n    ///     true,\n    /// );\n    /// assert_eq!(result, \"HELLO\");\n    /// ```\n    pub fn join<F, G, T>(self, f: F, g: G, prioritize_utf8: bool) -> T\n    where\n        F: FnOnce(&'a str) -> T,\n        G: FnOnce(&'b [u16]) -> T,\n    {\n        if prioritize_utf8 && let Some(str) = self.utf8 {\n            return f(str);\n        }\n        g(self.utf16)\n    }\n\n    /// Same as [`join`][`JSInternedStrRef::join`], but where you can pass an additional context.\n    ///\n    /// Useful when you have a `&mut Context` context that cannot be borrowed by both closures at\n    /// the same time.\n    ///\n    /// # Examples\n    ///\n    /// ```\n    /// use boa_interner::Interner;\n    ///\n    /// let mut interner = Interner::new();\n    /// let sym = interner.get_or_intern(\"hello\");\n    /// let interned = interner.resolve_expect(sym);\n    /// let mut output = String::new();\n    /// interned.join_with_context(\n    ///     |utf8, buf: &mut String| buf.push_str(&utf8.to_uppercase()),\n    ///     |utf16, buf: &mut String| buf.push_str(&String::from_utf16_lossy(utf16).to_uppercase()),\n    ///     &mut output,\n    ///     true,\n    /// );\n    /// assert_eq!(output, \"HELLO\");\n    /// ```\n    pub fn join_with_context<C, F, G, T>(self, f: F, g: G, ctx: C, prioritize_utf8: bool) -> T\n    where\n        F: FnOnce(&'a str, C) -> T,\n        G: FnOnce(&'b [u16], C) -> T,\n    {\n        if prioritize_utf8 && let Some(str) = self.utf8 {\n            return f(str, ctx);\n        }\n        g(self.utf16, ctx)\n    }\n\n    /// Converts both string types into a common type `C`.\n    ///\n    /// If `self` is representable by a `UTF-8` string and the `prioritize_utf8` argument is set, it\n    /// will prioritize converting its `UTF-8` representation first, and will only convert its\n    /// `UTF-16` representation if it is only representable by a `UTF-16` string. Otherwise, it will\n    /// directly convert its `UTF-16` representation.\n    ///\n    /// # Examples\n    ///\n    /// ```\n    /// use boa_interner::Interner;\n    ///\n    /// enum JsString<'a> {\n    ///     Utf8(&'a str),\n    ///     Utf16(&'a [u16]),\n    /// }\n    ///\n    /// impl<'a> From<&'a str> for JsString<'a> {\n    ///     fn from(s: &'a str) -> Self {\n    ///         JsString::Utf8(s)\n    ///     }\n    /// }\n    ///\n    /// impl<'a> From<&'a [u16]> for JsString<'a> {\n    ///     fn from(s: &'a [u16]) -> Self {\n    ///         JsString::Utf16(s)\n    ///     }\n    /// }\n    ///\n    /// let mut interner = Interner::new();\n    /// let sym = interner.get_or_intern(\"hello\");\n    /// let interned = interner.resolve_expect(sym);\n    /// let result: JsString<'_> = interned.into_common(true);\n    /// assert!(matches!(result, JsString::Utf8(\"hello\")));\n    /// ```\n    pub fn into_common<C>(self, prioritize_utf8: bool) -> C\n    where\n        C: From<&'a str> + From<&'b [u16]>,\n    {\n        self.join(Into::into, Into::into, prioritize_utf8)\n    }\n}\n\nimpl core::fmt::Display for JSInternedStrRef<'_, '_> {\n    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {\n        self.join_with_context(\n            core::fmt::Display::fmt,\n            |js, f| {\n                char::decode_utf16(js.iter().copied())\n                    .map(|r| match r {\n                        Ok(c) => String::from(c),\n                        Err(e) => format!(\"\\\\u{:04X}\", e.unpaired_surrogate()),\n                    })\n                    .collect::<String>()\n                    .fmt(f)\n            },\n            f,\n            true,\n        )\n    }\n}\n\n/// The string interner for Boa.\n#[derive(Debug, Default)]\npub struct Interner {\n    utf8_interner: RawInterner<u8>,\n    utf16_interner: RawInterner<u16>,\n    /// Latin1-encodability cache for dynamically-interned strings (all code units ≤ 0xFF).\n    latin1_flags: Vec<bool>,\n}\n\nimpl Interner {\n    /// Creates a new [`Interner`].\n    ///\n    /// # Examples\n    ///\n    /// ```\n    /// use boa_interner::Interner;\n    ///\n    /// let mut interner = Interner::new();\n    /// let sym = interner.get_or_intern(\"hello\");\n    /// assert!(interner.resolve(sym).is_some());\n    /// ```\n    #[inline]\n    #[must_use]\n    pub fn new() -> Self {\n        Self::default()\n    }\n\n    /// Creates a new [`Interner`] with the specified capacity.\n    ///\n    /// # Examples\n    ///\n    /// ```\n    /// use boa_interner::Interner;\n    ///\n    /// let mut interner = Interner::with_capacity(10);\n    /// let sym = interner.get_or_intern(\"hello\");\n    /// assert!(interner.resolve(sym).is_some());\n    /// ```\n    #[inline]\n    #[must_use]\n    pub fn with_capacity(capacity: usize) -> Self {\n        Self {\n            utf8_interner: RawInterner::with_capacity(capacity),\n            utf16_interner: RawInterner::with_capacity(capacity),\n            latin1_flags: Vec::with_capacity(capacity),\n        }\n    }\n\n    /// Returns the number of strings interned by the interner.\n    ///\n    /// # Examples\n    ///\n    /// ```\n    /// use boa_interner::Interner;\n    ///\n    /// let mut interner = Interner::new();\n    /// let initial_len = interner.len();\n    /// interner.get_or_intern(\"hello\");\n    /// assert_eq!(interner.len(), initial_len + 1);\n    /// ```\n    #[inline]\n    #[must_use]\n    pub fn len(&self) -> usize {\n        // `utf16_interner.len()` == `utf8_interner.len()`,\n        // so we can use any of them.\n        COMMON_STRINGS_UTF8.len() + self.utf16_interner.len()\n    }\n\n    /// Returns `true` if the [`Interner`] contains no interned strings.\n    ///\n    /// # Examples\n    ///\n    /// ```\n    /// use boa_interner::Interner;\n    ///\n    /// let interner = Interner::new();\n    /// assert!(!interner.is_empty());\n    /// ```\n    #[inline]\n    #[must_use]\n    pub fn is_empty(&self) -> bool {\n        COMMON_STRINGS_UTF8.is_empty() && self.utf16_interner.is_empty()\n    }\n\n    /// Returns the symbol for the given string if any.\n    ///\n    /// Can be used to query if a string has already been interned without interning.\n    ///\n    /// # Examples\n    ///\n    /// ```\n    /// use boa_interner::Interner;\n    ///\n    /// let mut interner = Interner::new();\n    /// assert!(interner.get(\"hello\").is_none());\n    /// interner.get_or_intern(\"hello\");\n    /// assert!(interner.get(\"hello\").is_some());\n    /// ```\n    pub fn get<'a, T>(&self, string: T) -> Option<Sym>\n    where\n        T: Into<JStrRef<'a>>,\n    {\n        let string = string.into();\n        Self::get_common(string).or_else(|| {\n            let index = match string {\n                JStrRef::Utf8(s) => self.utf8_interner.get(s.as_bytes()),\n                JStrRef::Utf16(s) => self.utf16_interner.get(s),\n            };\n            // SAFETY:\n            // `get_or_intern/get_or_intern_static` already have checks to avoid returning indices\n            // that could cause overflows, meaning the indices returned by\n            // `idx + 1 + COMMON_STRINGS_UTF8.len()` cannot cause overflows.\n            unsafe { index.map(|i| Sym::new_unchecked(i + 1 + COMMON_STRINGS_UTF8.len())) }\n        })\n    }\n\n    /// Interns the given string.\n    ///\n    /// Returns a symbol for resolution into the original string.\n    ///\n    /// # Panics\n    ///\n    /// If the interner already interns the maximum number of strings possible by the chosen symbol type.\n    ///\n    /// # Examples\n    ///\n    /// ```\n    /// use boa_interner::Interner;\n    ///\n    /// let mut interner = Interner::new();\n    /// let sym1 = interner.get_or_intern(\"hello\");\n    /// let sym2 = interner.get_or_intern(\"hello\");\n    /// assert_eq!(sym1, sym2);\n    /// let sym3 = interner.get_or_intern(\"world\");\n    /// assert_ne!(sym1, sym3);\n    /// ```\n    pub fn get_or_intern<'a, T>(&mut self, string: T) -> Sym\n    where\n        T: Into<JStrRef<'a>>,\n    {\n        let string = string.into();\n        self.get(string).unwrap_or_else(|| {\n            let (utf8, utf16) = match string {\n                JStrRef::Utf8(s) => (\n                    Some(Cow::Borrowed(s)),\n                    Cow::Owned(s.encode_utf16().collect()),\n                ),\n                JStrRef::Utf16(s) => (String::from_utf16(s).ok().map(Cow::Owned), Cow::Borrowed(s)),\n            };\n\n            // We need a way to check for the strings that can be interned by `utf16_interner` but\n            // not by `utf8_interner` (since there are some UTF-16 strings with surrogates that are\n            // not representable in UTF-8), so we use the sentinel value `\"\"` as a marker indicating\n            // that the `Sym` corresponding to that string is only available in `utf16_interner`.\n            //\n            // We don't need to worry about matches with `\"\"` inside `get`, because\n            // `COMMON_STRINGS_UTF8` filters all the empty strings before interning.\n            let index = if let Some(utf8) = utf8 {\n                self.utf8_interner.intern(utf8.as_bytes())\n            } else {\n                self.utf8_interner.intern_static(b\"\")\n            };\n\n            let utf16_index = self.utf16_interner.intern(&utf16);\n\n            assert_eq!(index, utf16_index);\n\n            self.latin1_flags.push(utf16.iter().all(|&c| c <= 0xFF));\n\n            index\n                .checked_add(1 + COMMON_STRINGS_UTF8.len())\n                .and_then(Sym::new)\n                .expect(\"Cannot intern new string: integer overflow\")\n        })\n    }\n\n    /// Interns the given `'static` string.\n    ///\n    /// Returns a symbol for resolution into the original string.\n    ///\n    /// # Note\n    ///\n    /// This is more efficient than [`Interner::get_or_intern`], since it avoids allocating space\n    /// for one `string` inside the [`Interner`], with the disadvantage that you need to provide\n    /// both the `UTF-8` and the `UTF-16` representation of the string.\n    ///\n    /// # Panics\n    ///\n    /// If the interner already interns the maximum number of strings possible by the chosen symbol type.\n    ///\n    /// # Examples\n    ///\n    /// ```\n    /// use boa_interner::Interner;\n    ///\n    /// static HELLO_UTF16: &[u16] = &[0x68, 0x65, 0x6C, 0x6C, 0x6F];\n    ///\n    /// let mut interner = Interner::new();\n    /// let sym1 = interner.get_or_intern_static(\"hello\", HELLO_UTF16);\n    /// let sym2 = interner.get_or_intern(\"hello\");\n    /// assert_eq!(sym1, sym2);\n    /// ```\n    pub fn get_or_intern_static(&mut self, utf8: &'static str, utf16: &'static [u16]) -> Sym {\n        // Uses the utf8 because it's quicker to check inside `COMMON_STRINGS_UTF8`\n        // (which is a perfect hash set) than to check inside `COMMON_STRINGS_UTF16`\n        // (which is a lazy static hash set).\n        self.get(utf8).unwrap_or_else(|| {\n            let index = self.utf8_interner.intern(utf8.as_bytes());\n            let utf16_index = self.utf16_interner.intern(utf16);\n\n            debug_assert_eq!(index, utf16_index);\n\n            self.latin1_flags.push(utf16.iter().all(|&c| c <= 0xFF));\n\n            index\n                .checked_add(1 + COMMON_STRINGS_UTF8.len())\n                .and_then(Sym::new)\n                .expect(\"Cannot intern new string: integer overflow\")\n        })\n    }\n\n    /// Returns the string for the given symbol if any.\n    ///\n    /// # Panics\n    ///\n    /// Panics if the size of both statics is not equal or the interners do\n    /// not have the same size\n    ///\n    /// # Examples\n    ///\n    /// ```\n    /// use boa_interner::Interner;\n    ///\n    /// let mut interner = Interner::new();\n    /// let sym = interner.get_or_intern(\"hello\");\n    /// let resolved = interner.resolve(sym);\n    /// assert!(resolved.is_some());\n    /// assert_eq!(resolved.unwrap().utf8(), Some(\"hello\"));\n    /// ```\n    #[must_use]\n    pub fn resolve(&self, symbol: Sym) -> Option<JSInternedStrRef<'_, '_>> {\n        let index = symbol.get() - 1;\n\n        if let Some(utf8) = COMMON_STRINGS_UTF8.index(index).copied() {\n            let utf16 = COMMON_STRINGS_UTF16\n                .get_index(index)\n                .copied()\n                .expect(\"The sizes of both statics must be equal\");\n            return Some(JSInternedStrRef {\n                utf8: Some(utf8),\n                utf16,\n            });\n        }\n\n        let index = index - COMMON_STRINGS_UTF8.len();\n\n        if let Some(utf16) = self.utf16_interner.index(index) {\n            let index = index - (self.utf16_interner.len() - self.utf8_interner.len());\n            // SAFETY:\n            // We only manipulate valid UTF-8 `str`s and convert them to `[u8]` for convenience,\n            // so converting back to a `str` is safe.\n            let utf8 = unsafe {\n                core::str::from_utf8_unchecked(\n                    self.utf8_interner\n                        .index(index)\n                        .expect(\"both interners must have the same size\"),\n                )\n            };\n            return Some(JSInternedStrRef {\n                utf8: if utf8.is_empty() { None } else { Some(utf8) },\n                utf16,\n            });\n        }\n\n        None\n    }\n\n    /// Returns the string for the given symbol.\n    ///\n    /// # Panics\n    ///\n    /// If the interner cannot resolve the given symbol.\n    ///\n    /// # Examples\n    ///\n    /// ```\n    /// use boa_interner::Interner;\n    ///\n    /// let mut interner = Interner::new();\n    /// let sym = interner.get_or_intern(\"hello\");\n    /// let resolved = interner.resolve_expect(sym);\n    /// assert_eq!(resolved.utf8(), Some(\"hello\"));\n    /// ```\n    #[inline]\n    #[must_use]\n    pub fn resolve_expect(&self, symbol: Sym) -> JSInternedStrRef<'_, '_> {\n        self.resolve(symbol).expect(\"string disappeared\")\n    }\n\n    /// Returns `true` if the string identified by `symbol` can be encoded as Latin1\n    /// (i.e. all code units are in the range `0x00..=0xFF`).\n    ///\n    /// This information is computed **once** when the string is first interned, so callers pay no\n    /// O(n) scanning cost beyond the initial intern call.\n    ///\n    /// # Examples\n    ///\n    /// ```\n    /// use boa_interner::Interner;\n    ///\n    /// let mut interner = Interner::new();\n    /// let ascii = interner.get_or_intern(\"hello\");\n    /// assert!(interner.is_latin1(ascii));\n    ///\n    /// let non_latin1: Vec<u16> = vec![0x4e2d, 0x6587]; // \"中文\"\n    /// let sym = interner.get_or_intern(non_latin1.as_slice());\n    /// assert!(!interner.is_latin1(sym));\n    /// ```\n    #[inline]\n    #[must_use]\n    pub fn is_latin1(&self, symbol: Sym) -> bool {\n        let index = symbol.get() - 1;\n        if index < COMMON_STRINGS_UTF8.len() {\n            return true;\n        }\n        let dynamic_index = index - COMMON_STRINGS_UTF8.len();\n        self.latin1_flags\n            .get(dynamic_index)\n            .copied()\n            .unwrap_or(false)\n    }\n\n    fn get_common(string: JStrRef<'_>) -> Option<Sym> {\n        match string {\n            JStrRef::Utf8(s) => COMMON_STRINGS_UTF8.get_index(s).map(|idx| {\n                // SAFETY: `idx >= 0`, since it's an `usize`, and `idx + 1 > 0`.\n                // In this case, we don't need to worry about overflows because we have a static\n                // assertion in place checking that `COMMON_STRINGS.len() < usize::MAX`.\n                unsafe { Sym::new_unchecked(idx + 1) }\n            }),\n            JStrRef::Utf16(s) => COMMON_STRINGS_UTF16.get_index_of(&s).map(|idx| {\n                // SAFETY: `idx >= 0`, since it's an `usize`, and `idx + 1 > 0`.\n                // In this case, we don't need to worry about overflows because we have a static\n                // assertion in place checking that `COMMON_STRINGS.len() < usize::MAX`.\n                unsafe { Sym::new_unchecked(idx + 1) }\n            }),\n        }\n    }\n}\n\n/// Implements the display formatting with indentation.\npub trait ToIndentedString {\n    /// Converts the element to a string using an interner, with the given indentation.\n    fn to_indented_string(&self, interner: &Interner, indentation: usize) -> String;\n}\n\n/// Converts a given element to a string using an interner.\npub trait ToInternedString {\n    /// Converts a given element to a string using an interner.\n    fn to_interned_string(&self, interner: &Interner) -> String;\n}\n\nimpl<T> ToInternedString for T\nwhere\n    T: ToIndentedString,\n{\n    fn to_interned_string(&self, interner: &Interner) -> String {\n        self.to_indented_string(interner, 0)\n    }\n}\n"
  },
  {
    "path": "core/interner/src/raw.rs",
    "content": "use crate::{fixed_string::FixedString, interned_str::InternedStr};\nuse alloc::vec::Vec;\nuse core::hash::{BuildHasherDefault, Hash};\nuse hashbrown::HashMap;\nuse rustc_hash::FxHasher;\n\ntype Map<T, U> = HashMap<T, U, BuildHasherDefault<FxHasher>>;\n\n/// Raw string interner, generic by a char type.\n#[derive(Debug)]\npub(super) struct RawInterner<Char> {\n    // COMMENT FOR DEVS:\n    // This interner works on the assumption that\n    // `head` won't ever be reallocated, since this could invalidate\n    // some of our stored pointers inside `spans`.\n    // This means that any operation on `head` and `full` should be carefully\n    // reviewed to not cause Undefined Behaviour.\n    // `intern` has a more thorough explanation on this.\n    //\n    // Also, if you want to implement `shrink_to_fit` (and friends),\n    // please check out https://github.com/Robbepop/string-interner/pull/47 first.\n    // This doesn't implement that method, since implementing it increases\n    // our memory footprint.\n    symbol_cache: Map<InternedStr<Char>, usize>,\n    spans: Vec<InternedStr<Char>>,\n    head: FixedString<Char>,\n    full: Vec<FixedString<Char>>,\n}\n\nimpl<Char> Default for RawInterner<Char> {\n    fn default() -> Self {\n        Self {\n            symbol_cache: Map::default(),\n            spans: Vec::default(),\n            head: FixedString::default(),\n            full: Vec::default(),\n        }\n    }\n}\n\nimpl<Char> RawInterner<Char> {\n    /// Creates a new `RawInterner` with the specified capacity.\n    pub(super) fn with_capacity(capacity: usize) -> Self {\n        Self {\n            symbol_cache: Map::default(),\n            spans: Vec::with_capacity(capacity),\n            head: FixedString::new(capacity),\n            full: Vec::new(),\n        }\n    }\n\n    /// Returns the number of strings interned by the interner.\n    pub(super) fn len(&self) -> usize {\n        self.spans.len()\n    }\n\n    /// Returns `true` if the interner contains no interned strings.\n    pub(super) fn is_empty(&self) -> bool {\n        self.spans.is_empty()\n    }\n}\n\nimpl<Char> RawInterner<Char>\nwhere\n    Char: Hash + Eq,\n{\n    /// Returns the index position for the given string if any.\n    ///\n    /// Can be used to query if a string has already been interned without interning.\n    pub(super) fn get(&self, string: &[Char]) -> Option<usize> {\n        // SAFETY:\n        // `string` is a valid slice that doesn't outlive the\n        // created `InternedStr`, so this is safe.\n        unsafe {\n            self.symbol_cache\n                .get(&InternedStr::new(string.into()))\n                .copied()\n        }\n    }\n\n    /// Interns the given `'static` string.\n    ///\n    /// Returns the index of `string` within the interner.\n    ///\n    /// # Note\n    ///\n    /// This is more efficient than [`RawInterner::intern`], since it\n    /// avoids storing `string` inside the interner.\n    ///\n    /// # Panics\n    ///\n    /// If the interner already interns the maximum number of strings possible\n    /// by the chosen symbol type.\n    pub(super) fn intern_static(&mut self, string: &'static [Char]) -> usize {\n        // SAFETY:\n        // A static string reference is always valid, meaning it cannot outlive\n        // the lifetime of the created `InternedStr`. This makes this\n        // operation safe.\n        let string = unsafe { InternedStr::new(string.into()) };\n\n        // SAFETY:\n        // A `InternedStr` created from a static reference\n        // cannot be invalidated by allocations and deallocations,\n        // so this is safe.\n        unsafe { self.next_index(string) }\n    }\n\n    /// Returns the string for the given index if any.\n    pub(super) fn index(&self, index: usize) -> Option<&[Char]> {\n        self.spans.get(index).map(|ptr|\n            // SAFETY: We always ensure the stored `InternedStr`s always\n            // reference memory inside `head` and `full`\n            unsafe {ptr.as_ref()})\n    }\n\n    /// Inserts a new string pointer into `spans` and returns its index.\n    ///\n    /// # Safety\n    ///\n    /// The caller must ensure `string` points to a valid\n    /// memory inside `head` (or only valid in the case of statics)\n    /// and that it won't be invalidated by allocations and deallocations.\n    unsafe fn next_index(&mut self, string: InternedStr<Char>) -> usize {\n        let next = self.len();\n        self.spans.push(string);\n        self.symbol_cache.insert(string, next);\n        next\n    }\n}\n\nimpl<Char> RawInterner<Char>\nwhere\n    Char: Hash + Eq + Clone,\n{\n    /// Interns the given string.\n    ///\n    /// Returns the index of `string` within the interner.\n    ///\n    /// # Panics\n    ///\n    /// If the interner already interns the maximum number of strings possible by the chosen symbol type.\n    pub(super) fn intern(&mut self, string: &[Char]) -> usize {\n        // SAFETY:\n        //\n        // Firstly, this interner works on the assumption that the allocated\n        // memory by `head` won't ever be moved from its position on the heap,\n        // which is an important point to understand why manipulating it like\n        // this is safe.\n        //\n        // `String` (which is simply a `Vec<u8>` with additional invariants)\n        // is essentially a pointer to heap memory that can be moved without\n        // any problems, since copying a pointer cannot invalidate the memory\n        // that it points to.\n        //\n        // However, `String` CAN be invalidated when pushing, extending or\n        // shrinking it, since all those operations reallocate on the heap.\n        //\n        // To prevent that, we HAVE to ensure the capacity will succeed without\n        // having to reallocate, and the only way to do that without invalidating\n        // any other alive `InternedStr` is to create a brand new `head` with\n        // enough capacity and push the old `head` to `full` to keep it alive\n        // throughout the lifetime of the whole interner.\n        //\n        // `FixedString` encapsulates this by only allowing checked `push`es\n        // to the internal string, but we still have to ensure the memory\n        // of `head` is not deallocated until the whole interner deallocates,\n        // which we can do by moving it inside the interner itself, specifically\n        // on the `full` vector, where every other old `head` also lives.\n        let interned_str = unsafe {\n            self.head.push(string).unwrap_or_else(|| {\n                let new_cap =\n                    (usize::max(self.head.capacity(), string.len()) + 1).next_power_of_two();\n                let new_head = FixedString::new(new_cap);\n                let old_head = core::mem::replace(&mut self.head, new_head);\n\n                // If the user creates an `Interner`\n                // with `Interner::with_capacity(BIG_NUMBER)` and\n                // the first interned string's length is bigger than `BIG_NUMBER`,\n                // `self.full.push(old_head)` would push a big, empty string of\n                // allocated size `BIG_NUMBER` into `full`.\n                // This prevents that case.\n                if !old_head.is_empty() {\n                    self.full.push(old_head);\n                }\n                self.head.push_unchecked(string)\n            })\n        };\n\n        // SAFETY: We are obtaining a pointer to the internal memory of\n        // `head`, which is alive through the whole life of the interner, so\n        // this is safe.\n        unsafe { self.next_index(interned_str) }\n    }\n}\n"
  },
  {
    "path": "core/interner/src/sym.rs",
    "content": "use boa_gc::{Finalize, Trace, empty_trace};\nuse boa_macros::static_syms;\nuse core::num::NonZeroUsize;\n\n/// The string symbol type for Boa.\n///\n/// This symbol type is internally a `NonZeroUsize`, which makes it pointer-width in size and it's\n/// optimized so that it can occupy 1 pointer width even in an `Option` type.\n#[cfg_attr(\n    feature = \"serde\",\n    derive(serde::Serialize, serde::Deserialize),\n    serde(transparent)\n)]\n#[cfg_attr(feature = \"arbitrary\", derive(arbitrary::Arbitrary))]\n#[allow(clippy::unsafe_derive_deserialize)]\n#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Finalize)]\npub struct Sym {\n    value: NonZeroUsize,\n}\n\n// SAFETY: `NonZeroUsize` is a constrained `usize`, and all primitive types don't need to be traced\n// by the garbage collector.\nunsafe impl Trace for Sym {\n    empty_trace!();\n}\n\nimpl Sym {\n    /// Creates a new [`Sym`] from the provided `value`, or returns `None` if `index` is zero.\n    pub(super) fn new(value: usize) -> Option<Self> {\n        NonZeroUsize::new(value).map(|value| Self { value })\n    }\n\n    /// Creates a new [`Sym`] from the provided `value`, without checking if `value` is not zero\n    ///\n    /// # Safety\n    ///\n    /// `value` must not be zero.\n    pub(super) const unsafe fn new_unchecked(value: usize) -> Self {\n        Self {\n            value:\n            // SAFETY: The caller must ensure the invariants of the function.\n            unsafe {\n                NonZeroUsize::new_unchecked(value)\n            },\n        }\n    }\n\n    /// Checks if this symbol is one of the [reserved identifiers][spec] of the ECMAScript\n    /// specification, excluding `await` and `yield`\n    ///\n    /// [spec]: https://tc39.es/ecma262/#prod-ReservedWord\n    #[inline]\n    #[must_use]\n    pub fn is_reserved_identifier(self) -> bool {\n        (Self::BREAK..=Self::WITH).contains(&self)\n    }\n\n    /// Checks if this symbol is one of the [strict reserved identifiers][spec] of the ECMAScript\n    /// specification.\n    ///\n    /// [spec]: https://tc39.es/ecma262/#prod-ReservedWord\n    #[inline]\n    #[must_use]\n    pub fn is_strict_reserved_identifier(self) -> bool {\n        (Self::IMPLEMENTS..=Self::YIELD).contains(&self)\n    }\n\n    /// Returns the internal value of the [`Sym`]\n    #[inline]\n    #[must_use]\n    pub const fn get(self) -> usize {\n        self.value.get()\n    }\n}\n\nstatic_syms! {\n    // Reserved identifiers\n    // See: <https://tc39.es/ecma262/#prod-ReservedWord>\n    // Note, they must all be together.\n    \"break\",\n    \"case\",\n    \"catch\",\n    \"class\",\n    \"const\",\n    \"continue\",\n    \"debugger\",\n    \"default\",\n    \"delete\",\n    \"do\",\n    \"else\",\n    \"enum\",\n    \"export\",\n    \"extends\",\n    \"false\",\n    \"finally\",\n    \"for\",\n    \"function\",\n    \"if\",\n    \"import\",\n    \"in\",\n    \"instanceof\",\n    \"new\",\n    \"null\",\n    \"return\",\n    \"super\",\n    \"switch\",\n    \"this\",\n    \"throw\",\n    \"true\",\n    \"try\",\n    \"typeof\",\n    \"var\",\n    \"void\",\n    \"while\",\n    \"with\",\n    // End reserved identifier\n\n    // strict reserved identifiers.\n    // See: <https://tc39.es/ecma262/#prod-Identifier>\n    // Note, they must all be together.\n    \"implements\",\n    \"interface\",\n    \"let\",\n    \"package\",\n    \"private\",\n    \"protected\",\n    \"public\",\n    \"static\",\n    \"yield\",\n    // End strict reserved identifiers\n\n    (\"\", EMPTY_STRING),\n    \"prototype\",\n    \"constructor\",\n    \"arguments\",\n    \"eval\",\n    \"RegExp\",\n    \"get\",\n    \"set\",\n    (\"<main>\", MAIN),\n    \"raw\",\n    \"anonymous\",\n    \"async\",\n    \"of\",\n    \"target\",\n    \"as\",\n    \"from\",\n    \"__proto__\",\n    \"name\",\n    \"await\",\n    (\"*default*\", DEFAULT_EXPORT),\n    \"meta\",\n    \"defer\",\n    \"source\",\n    \"using\",\n    \"dispose\",\n    \"asyncDispose\"\n}\n"
  },
  {
    "path": "core/interner/src/tests.rs",
    "content": "use crate::{COMMON_STRINGS_UTF8, COMMON_STRINGS_UTF16, Interner, Sym};\nuse boa_macros::utf16;\n\n#[track_caller]\nfn sym_from_usize(index: usize) -> Sym {\n    Sym::new(index).expect(\"Invalid NonZeroUsize\")\n}\n\n#[test]\nfn check_static_strings() {\n    let mut interner = Interner::default();\n\n    for (i, &str) in COMMON_STRINGS_UTF8.into_iter().enumerate() {\n        assert_eq!(interner.get_or_intern(str), sym_from_usize(i + 1));\n    }\n}\n\n#[test]\nfn check_new_string() {\n    let mut interner = Interner::default();\n\n    assert!(interner.get_or_intern(\"my test string\").get() > COMMON_STRINGS_UTF8.len());\n}\n\n#[test]\nfn check_resolve() {\n    let mut interner = Interner::default();\n\n    let utf_8_strings = [\"test string\", \"arguments\", \"hello\"];\n    let utf_8_strings = utf_8_strings.into_iter();\n    let utf_16_strings = [utf16!(\"TEST STRING\"), utf16!(\"ARGUMENTS\"), utf16!(\"HELLO\")];\n    let utf_16_strings = utf_16_strings.into_iter();\n\n    for (s8, s16) in utf_8_strings.zip(utf_16_strings) {\n        let sym = interner.get_or_intern(s8);\n        let resolved = interner.resolve(sym).unwrap();\n        assert_eq!(Some(s8), resolved.utf8());\n        let new_sym = interner.get_or_intern(s8);\n        assert_eq!(sym, new_sym);\n\n        let sym = interner.get_or_intern(s16);\n        let resolved = interner.resolve(sym).unwrap();\n        assert_eq!(s16, resolved.utf16());\n        let new_sym = interner.get_or_intern(s16);\n        assert_eq!(sym, new_sym);\n    }\n}\n\n#[test]\nfn check_static_resolve() {\n    let mut interner = Interner::default();\n\n    for (utf8, utf16) in COMMON_STRINGS_UTF8\n        .into_iter()\n        .copied()\n        .zip(COMMON_STRINGS_UTF16.iter().copied())\n        .chain(\n            [\n                (\"my test str\", utf16!(\"my test str\")),\n                (\"hello world\", utf16!(\"hello world\")),\n                (\";\", utf16!(\";\")),\n            ]\n            .into_iter(),\n        )\n    {\n        let sym = interner.get_or_intern_static(utf8, utf16);\n        let resolved = interner.resolve(sym).unwrap();\n        assert_eq!(Some(utf8), resolved.utf8());\n        assert_eq!(utf16, resolved.utf16());\n\n        let new_sym = interner.get_or_intern(utf8);\n\n        assert_eq!(sym, new_sym);\n    }\n}\n\n#[test]\nfn check_unpaired_surrogates() {\n    let mut interner = Interner::default();\n\n    let unp = &[\n        0xDC15u16, 0xDC19, 'h' as u16, 'e' as u16, 'l' as u16, 'l' as u16, 'o' as u16,\n    ];\n    let unp2 = &[\n        0xDC01u16, 'w' as u16, 'o' as u16, 'r' as u16, 0xDCF4, 'l' as u16, 'd' as u16,\n    ];\n\n    let sym = interner.get_or_intern(\"abc\");\n    let sym2 = interner.get_or_intern(\"def\");\n\n    let sym3 = interner.get_or_intern(unp);\n    let sym4 = interner.get_or_intern(utf16!(\"ghi\"));\n    let sym5 = interner.get_or_intern(unp2);\n\n    let sym6 = interner.get_or_intern(\"jkl\");\n\n    assert_eq!(interner.resolve_expect(sym).utf8(), Some(\"abc\"));\n    assert_eq!(interner.resolve_expect(sym).utf16(), utf16!(\"abc\"));\n\n    assert_eq!(interner.resolve_expect(sym2).utf8(), Some(\"def\"));\n    assert_eq!(interner.resolve_expect(sym2).utf16(), utf16!(\"def\"));\n\n    assert!(interner.resolve_expect(sym3).utf8().is_none());\n    assert_eq!(interner.resolve_expect(sym3).utf16(), unp);\n\n    assert_eq!(interner.resolve_expect(sym4).utf8(), Some(\"ghi\"));\n    assert_eq!(interner.resolve_expect(sym4).utf16(), utf16!(\"ghi\"));\n\n    assert!(interner.resolve_expect(sym5).utf8().is_none());\n    assert_eq!(interner.resolve_expect(sym5).utf16(), unp2);\n\n    assert_eq!(interner.resolve_expect(sym6).utf8(), Some(\"jkl\"));\n    assert_eq!(interner.resolve_expect(sym6).utf16(), utf16!(\"jkl\"));\n}\n\n#[test]\nfn check_empty_interner() {\n    let interner = Interner::default();\n\n    let sym = sym_from_usize(123); // Choose an arbitrary symbol\n\n    assert!(interner.resolve(sym).is_none());\n}\n\n#[test]\nfn check_capacity() {\n    let interner = Interner::with_capacity(100);\n\n    let sym = sym_from_usize(123); // Choose an arbitrary symbol\n\n    assert!(interner.resolve(sym).is_none());\n}\n\n#[test]\nfn check_is_latin1() {\n    static STATIC_STR: &str = \"static_latin1\";\n    static STATIC_UTF16: &[u16] = &[\n        's' as u16, 't' as u16, 'a' as u16, 't' as u16, 'i' as u16, 'c' as u16, '_' as u16,\n        'l' as u16, 'a' as u16, 't' as u16, 'i' as u16, 'n' as u16, '1' as u16,\n    ];\n\n    let mut interner = Interner::default();\n\n    // Common/static strings (e.g. keywords) are always Latin1.\n    let common_sym = interner.get_or_intern(\"break\");\n    assert!(interner.is_latin1(common_sym));\n\n    // Dynamic ASCII string.\n    let ascii_sym = interner.get_or_intern(\"hello_world\");\n    assert!(interner.is_latin1(ascii_sym));\n\n    // Dynamic non-ASCII but Latin1-encodable (U+0080..=U+00FF).\n    let latin1_sym = interner.get_or_intern(&[0x00E9u16, 0x00FC, 0x00F1][..]);\n    assert!(interner.is_latin1(latin1_sym));\n\n    // Dynamic non-Latin1 (code unit > 0xFF).\n    let non_latin1_sym = interner.get_or_intern(&[0x4E2Du16, 0x6587][..]);\n    assert!(!interner.is_latin1(non_latin1_sym));\n\n    // Boundary: U+00FF (last Latin1) and U+0100 (first non-Latin1).\n    let boundary_sym = interner.get_or_intern(&[0x00FFu16][..]);\n    assert!(interner.is_latin1(boundary_sym));\n    let boundary_non_sym = interner.get_or_intern(&[0x0100u16][..]);\n    assert!(!interner.is_latin1(boundary_non_sym));\n\n    // get_or_intern_static also caches correctly.\n    let static_sym = interner.get_or_intern_static(STATIC_STR, STATIC_UTF16);\n    assert!(interner.is_latin1(static_sym));\n}\n"
  },
  {
    "path": "core/macros/ABOUT.md",
    "content": "# About Boa\n\nBoa is an open-source, experimental ECMAScript Engine written in Rust for\nlexing, parsing and executing ECMAScript/JavaScript. Currently, Boa supports some\nof the [language][boa-conformance]. More information can be viewed at [Boa's\nwebsite][boa-web].\n\nTry out the most recent release with Boa's live demo\n[playground][boa-playground].\n\n## Boa Crates\n\n- [**`boa_cli`**][cli] - Boa's CLI && REPL implementation\n- [**`boa_ast`**][ast] - Boa's ECMAScript Abstract Syntax Tree.\n- [**`boa_engine`**][engine] - Boa's implementation of ECMAScript builtin objects and execution.\n- [**`boa_gc`**][gc] - Boa's garbage collector.\n- [**`boa_icu_provider`**][icu] - Boa's ICU4X data provider.\n- [**`boa_interner`**][interner] - Boa's string interner.\n- [**`boa_macros`**][macros] - Boa's macros.\n- [**`boa_parser`**][parser] - Boa's lexer and parser.\n- [**`boa_runtime`**][runtime] - Boa's `WebAPI` features.\n- [**`boa_string`**][string] - Boa's ECMAScript string implementation.\n- [**`boa_wintertc`**][wintertc] - Boa's `WinterTC` (TC55) Minimum Common Web API implementation.\n- [**`tag_ptr`**][tag_ptr] - Utility library that enables a pointer to be associated with a tag of type `usize`.\n- [**`small_btree`**][small_btree] - Utility library that adds the `SmallBTreeMap` data structure.\n\n[boa-conformance]: https://boajs.dev/conformance\n[boa-web]: https://boajs.dev/\n[boa-playground]: https://boajs.dev/playground\n[ast]: https://docs.rs/boa_ast/latest/boa_ast/index.html\n[engine]: https://docs.rs/boa_engine/latest/boa_engine/index.html\n[gc]: https://docs.rs/boa_gc/latest/boa_gc/index.html\n[interner]: https://docs.rs/boa_interner/latest/boa_interner/index.html\n[parser]: https://docs.rs/boa_parser/latest/boa_parser/index.html\n[icu]: https://docs.rs/boa_icu_provider/latest/boa_icu_provider/index.html\n[runtime]: https://docs.rs/boa_runtime/latest/boa_runtime/index.html\n[string]: https://docs.rs/boa_string/latest/boa_string/index.html\n[wintertc]: https://docs.rs/boa_wintertc/latest/boa_wintertc/index.html\n[tag_ptr]: https://docs.rs/tag_ptr/latest/tag_ptr/index.html\n[small_btree]: https://docs.rs/small_btree/latest/small_btree/index.html\n[macros]: https://docs.rs/boa_macros/latest/boa_macros/index.html\n[cli]: https://crates.io/crates/boa_cli\n"
  },
  {
    "path": "core/macros/Cargo.toml",
    "content": "[package]\nname = \"boa_macros\"\ndescription = \"Macros for the Boa JavaScript engine.\"\nversion.workspace = true\nedition.workspace = true\nauthors.workspace = true\nlicense.workspace = true\nrepository.workspace = true\nrust-version.workspace = true\n\n[lib]\nproc-macro = true\n\n[dependencies]\ncfg-if.workspace = true\ncow-utils.workspace = true\nlz4_flex = { workspace = true, optional = true }\nquote.workspace = true\nsyn = { workspace = true, features = [\"full\", \"visit-mut\"] }\nproc-macro2.workspace = true\nsynstructure.workspace = true\n\n[dev-dependencies]\ntest-case.workspace = true\n\n[lints]\nworkspace = true\n\n[package.metadata.docs.rs]\nall-features = true\n\n[features]\nembedded_lz4 = [\"lz4_flex\"]\n"
  },
  {
    "path": "core/macros/src/class.rs",
    "content": "use crate::utils::{\n    RenameScheme, SpannedResult, error, take_error_from_attrs, take_length_from_attrs,\n    take_name_value_attr, take_name_value_string, take_path_attr,\n};\nuse proc_macro::TokenStream;\nuse proc_macro2::{Span as Span2, TokenStream as TokenStream2};\nuse quote::quote;\nuse std::collections::BTreeMap;\nuse std::fmt::{Display, Formatter};\nuse syn::parse::{Parse, ParseStream};\nuse syn::punctuated::Punctuated;\nuse syn::spanned::Spanned;\nuse syn::visit_mut::VisitMut;\nuse syn::{\n    Attribute, ConstParam, Expr, FnArg, GenericParam, Ident, ImplItemFn, ItemImpl, LifetimeParam,\n    Lit, Meta, MetaNameValue, PatType, Receiver, ReturnType, Signature, Token, Type, TypeParam,\n};\n\n/// The name of a method or accessor, which can be either a string property key or a\n/// well-known symbol (e.g. `Symbol.iterator`).\n#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]\npub(crate) enum MethodName {\n    String(String),\n    Symbol(String),\n}\n\nimpl MethodName {\n    fn to_key_tokens(&self) -> TokenStream2 {\n        match self {\n            Self::String(s) => quote! { boa_engine::js_string!( #s ) },\n            Self::Symbol(sym) => {\n                let snake = camel_to_snake_case(sym);\n                let ident = Ident::new_raw(&snake, Span2::call_site());\n                quote! { boa_engine::JsSymbol:: #ident () }\n            }\n        }\n    }\n}\n\n/// Converts a `camelCase` string to `snake_case`.\n///\n/// The symbol name is forwarded to `JsSymbol::<snake_name>()` without\n/// validation; if the accessor does not exist the Rust compiler will report\n/// the error.\nfn camel_to_snake_case(s: &str) -> String {\n    let mut result = String::with_capacity(s.len() + 4);\n    for (i, ch) in s.chars().enumerate() {\n        if ch.is_ascii_uppercase() {\n            if i > 0 {\n                result.push('_');\n            }\n            result.push(ch.to_ascii_lowercase());\n        } else {\n            result.push(ch);\n        }\n    }\n    result\n}\n\n/// A function representation. Takes a function from the AST and remember its name, length and\n/// how its body should be in the output AST.\n/// There are three types of functions: Constructors, Methods and Accessors (setter/getter).\n///\n/// This is not an enum for simplicity. The body is dependent on how this was created.\npub(crate) struct Function {\n    /// The name of the function. Can be a string key or a well-known symbol.\n    name: MethodName,\n\n    /// The length of the function in JavaScript. Can be overridden with `#[boa(length = ...)]`.\n    length: usize,\n\n    /// The body of the function serialized. This depends highly on the type of function.\n    body: TokenStream2,\n\n    /// Whether a receiver was found.\n    is_static: bool,\n}\n\nimpl std::fmt::Debug for Function {\n    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {\n        f.debug_struct(\"Function\")\n            .field(\"name\", &self.name)\n            .field(\"length\", &self.length)\n            .field(\"is_static\", &self.is_static)\n            .field(\"body\", &self.body.to_string())\n            .finish()\n    }\n}\n\nimpl Display for MethodName {\n    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {\n        match self {\n            Self::String(s) => f.write_str(s),\n            Self::Symbol(s) => write!(f, \"[Symbol.{s}]\"),\n        }\n    }\n}\n\nimpl Function {\n    /// Serializes the `self` argument declaration and call.\n    fn arg_self_from_receiver(\n        receiver: &mut Receiver,\n        class_ty: &Type,\n    ) -> SpannedResult<(TokenStream2, TokenStream2)> {\n        let err = take_error_from_attrs(&mut receiver.attrs)?\n            .unwrap_or(\"Invalid class for type\".to_string());\n\n        // `&mut self`\n        let downcast = if receiver.mutability.is_some() {\n            quote! {\n                let object = this.as_object();\n                let self_ = &mut *object.as_ref().and_then(|o| o.downcast_mut::< #class_ty >())\n                    .ok_or( boa_engine::js_error!( #err ))?;\n            }\n        } else {\n            quote! {\n                let object = this.as_object();\n                let self_ = &*object.as_ref().and_then(|o| o.downcast_ref::< #class_ty >())\n                    .ok_or( boa_engine::js_error!( #err ))?;\n            }\n        };\n\n        Ok((downcast, quote! { self_ }))\n    }\n\n    /// Serializes an argument of form `pat: Type` into its declaration and call. Also returns\n    /// whether we should increment the length.\n    #[allow(clippy::unnecessary_wraps)]\n    fn arg_from_pat_type(\n        pat_type: &mut PatType,\n        i: usize,\n    ) -> SpannedResult<(bool, TokenStream2, TokenStream2)> {\n        let ty = pat_type.ty.as_ref();\n        let ident = Ident::new(&format!(\"boa_arg_{i}\"), Span2::call_site());\n\n        // Find out if it's a boa context.\n        let is_context = match ty {\n            Type::Reference(syn::TypeReference {\n                elem,\n                mutability: Some(_),\n                ..\n            }) => match elem.as_ref() {\n                Type::Path(syn::TypePath { qself: _, path }) => {\n                    if let Some(maybe_ctx) = path.segments.last() {\n                        maybe_ctx.ident == \"Context\"\n                    } else {\n                        false\n                    }\n                }\n                _ => take_path_attr(&mut pat_type.attrs, \"context\"),\n            },\n            _ => false,\n        };\n\n        if is_context {\n            Ok((true, quote! {}, quote! { context }))\n        } else {\n            Ok((\n                false,\n                quote! {\n                    let (#ident, rest): (#ty, &[boa_engine::JsValue]) =\n                        boa_engine::interop::TryFromJsArgument::try_from_js_argument( this, rest, context )?;\n                },\n                quote! { #ident },\n            ))\n        }\n    }\n\n    pub(crate) fn from_sig(\n        name: MethodName,\n        has_explicit_method: bool,\n        has_explicit_static: bool,\n        attrs: &mut Vec<Attribute>,\n        sig: &mut Signature,\n        class_ty: Option<&Type>,\n    ) -> SpannedResult<Self> {\n        // The amount of arguments that aren't really arguments in JavaScript,\n        // e.g. `self`, `&mut Context`, etc.\n        let mut not_param_count = 0;\n        let (args_decl, args_call): (Vec<TokenStream2>, Vec<TokenStream2>) = sig\n            .inputs\n            .iter_mut()\n            .enumerate()\n            .map(|(i, a)| match a {\n                FnArg::Receiver(receiver) => {\n                    not_param_count += 1;\n                    if let Some(cty) = class_ty {\n                        Self::arg_self_from_receiver(receiver, cty)\n                    } else {\n                        error(receiver, \"Invalid context for using a receiver.\")\n                    }\n                }\n                FnArg::Typed(ty) => {\n                    let (incr, decl, call) = Self::arg_from_pat_type(ty, i)?;\n                    if incr {\n                        not_param_count += 1;\n                    }\n                    Ok((decl, call))\n                }\n            })\n            .collect::<SpannedResult<_>>()?;\n\n        let length = take_length_from_attrs(attrs)?.unwrap_or(args_decl.len() - not_param_count);\n\n        let fn_name = &sig.ident;\n\n        let generics = if sig.generics.params.is_empty() {\n            quote! {}\n        } else {\n            let generics = sig\n                .generics\n                .params\n                .iter()\n                .map(|param| match param {\n                    GenericParam::Type(TypeParam { ident, .. })\n                    | GenericParam::Const(ConstParam { ident, .. }) => {\n                        quote! { #ident }\n                    }\n                    GenericParam::Lifetime(LifetimeParam { lifetime, .. }) => {\n                        quote! { #lifetime }\n                    }\n                })\n                .collect::<TokenStream2>();\n            quote! { :: < #generics > }\n        };\n\n        // A method is static if it has the `#[boa(static)]` attribute, or if it is\n        // not a method and doesn't contain `self`.\n        let is_static = has_explicit_static || !(has_explicit_method || not_param_count > 0);\n\n        // If this is a scoped function to a type (e.g. inside an `impl` block),\n        let scope = if class_ty.is_some() {\n            quote! { Self :: }\n        } else {\n            quote! {}\n        };\n\n        Ok(Self {\n            length,\n            name,\n            body: quote! {\n                |   this: &boa_engine::JsValue,\n                    args: &[boa_engine::JsValue],\n                    context: &mut boa_engine::Context\n                | -> boa_engine::JsResult<boa_engine::JsValue> {\n                    let rest = args;\n                    #(#args_decl)*\n\n                    let result = #scope #fn_name #generics ( #(#args_call),* );\n                    boa_engine::TryIntoJsResult::try_into_js_result(result, context)\n                }\n            },\n            is_static,\n        })\n    }\n\n    fn method(\n        name: MethodName,\n        has_explicit_method: bool,\n        has_explicit_static: bool,\n        fn_: &mut ImplItemFn,\n        class_ty: Option<&Type>,\n    ) -> SpannedResult<Self> {\n        if fn_.sig.asyncness.is_some() {\n            error(&fn_.sig.asyncness, \"Async methods are not supported.\")?;\n        }\n\n        if !fn_.sig.generics.params.is_empty() {\n            error(&fn_.sig.generics, \"Generic methods are not supported.\")?;\n        }\n\n        Self::from_sig(\n            name,\n            has_explicit_method,\n            has_explicit_static,\n            &mut fn_.attrs,\n            &mut fn_.sig,\n            class_ty,\n        )\n    }\n\n    fn getter(name: MethodName, fn_: &mut ImplItemFn, class_ty: &Type) -> SpannedResult<Self> {\n        Self::method(name, false, true, fn_, Some(class_ty))\n    }\n\n    fn setter(name: MethodName, fn_: &mut ImplItemFn, class_ty: &Type) -> SpannedResult<Self> {\n        Self::method(name, false, true, fn_, Some(class_ty))\n    }\n\n    fn constructor(fn_: &mut ImplItemFn, _class_ty: &Type) -> SpannedResult<Self> {\n        if fn_.sig.asyncness.is_some() {\n            error(&fn_.sig.asyncness, \"Async methods are not supported.\")?;\n        }\n\n        if !fn_.sig.generics.params.is_empty() {\n            error(&fn_.sig.generics, \"Generic methods are not supported.\")?;\n        }\n\n        let (args_decl, args_call): (Vec<TokenStream2>, Vec<TokenStream2>) = fn_\n            .sig\n            .inputs\n            .iter_mut()\n            .enumerate()\n            .map(|(i, a)| match a {\n                FnArg::Receiver(receiver) => error(receiver, \"Constructors cannot use 'self'\"),\n                FnArg::Typed(ty) => {\n                    let (_, decl, call) = Self::arg_from_pat_type(ty, i)?;\n                    Ok((decl, call))\n                }\n            })\n            .collect::<SpannedResult<_>>()?;\n\n        let length = take_length_from_attrs(&mut fn_.attrs)?.unwrap_or(args_decl.len());\n        let fn_name = &fn_.sig.ident;\n\n        // Does the function return Result<_> or JsResult<_>? If so, Into JsResult (to\n        // transform the error. If not, return Ok(_).\n        let return_statement = match &fn_.sig.output {\n            ReturnType::Default => quote! { Default::default() },\n            ReturnType::Type(_, ty) => {\n                if let Type::Path(path) = ty.as_ref() {\n                    let Some(t) = path.path.segments.last() else {\n                        return error(&fn_.sig.output, \"Cannot infer return type.\");\n                    };\n                    if t.ident == \"Self\" {\n                        quote! { Ok(result) }\n                    } else if t.ident == \"JsResult\" {\n                        quote! { result.into() }\n                    } else {\n                        return error(\n                            &fn_.sig.output,\n                            \"Invalid return type: constructors should return Self or JsResult<Self>.\",\n                        );\n                    }\n                } else {\n                    quote! { Ok(result) }\n                }\n            }\n        };\n\n        Ok(Self {\n            length,\n            name: MethodName::String(String::new()),\n            body: quote! {\n                let rest = args;\n                #(#args_decl)*\n\n                let result = Self:: #fn_name ( #(#args_call),* );\n                #return_statement\n            },\n            is_static: false,\n        })\n    }\n\n    pub(crate) fn body(&self) -> &TokenStream2 {\n        &self.body\n    }\n}\n\n#[derive(Debug, Default)]\nstruct Accessor {\n    getter: Option<Function>,\n    setter: Option<Function>,\n}\n\nimpl Accessor {\n    fn set_getter(\n        &mut self,\n        name: MethodName,\n        fn_: &mut ImplItemFn,\n        class_ty: &Type,\n    ) -> SpannedResult<()> {\n        if self.getter.is_some() {\n            error(fn_, format!(\"Getter for property {name} already declared.\"))\n        } else {\n            let getter = Function::getter(name, fn_, class_ty)?;\n            self.getter = Some(getter);\n            Ok(())\n        }\n    }\n\n    fn set_setter(\n        &mut self,\n        name: MethodName,\n        fn_: &mut ImplItemFn,\n        class_ty: &Type,\n    ) -> SpannedResult<()> {\n        if self.setter.is_some() {\n            error(fn_, format!(\"Setter for property {name} already declared.\"))\n        } else {\n            let setter = Function::setter(name, fn_, class_ty)?;\n            self.setter = Some(setter);\n            Ok(())\n        }\n    }\n\n    fn body(&self) -> TokenStream2 {\n        let Some(name) = self\n            .getter\n            .as_ref()\n            .map_or_else(|| self.setter.as_ref().map(|s| &s.name), |g| Some(&g.name))\n        else {\n            return quote! {};\n        };\n        let key_tokens = name.to_key_tokens();\n        let getter = if let Some(getter) = self.getter.as_ref() {\n            let body = getter.body.clone();\n            quote! {\n                Some(\n                    boa_engine::NativeFunction::from_fn_ptr( #body )\n                        .to_js_function(builder.context().realm())\n                )\n            }\n        } else {\n            quote! { None }\n        };\n        let setter = if let Some(setter) = self.setter.as_ref() {\n            let body = setter.body.clone();\n            quote! {\n                Some(\n                    boa_engine::NativeFunction::from_fn_ptr( #body )\n                        .to_js_function(builder.context().realm())\n                )\n            }\n        } else {\n            quote! { None }\n        };\n\n        quote! {\n            {\n                let g = #getter;\n                let s = #setter;\n                builder.accessor(\n                    #key_tokens,\n                    g,\n                    s,\n                    boa_engine::property::Attribute::CONFIGURABLE\n                        | boa_engine::property::Attribute::NON_ENUMERABLE,\n                );\n            }\n        }\n    }\n}\n\n/// A visitor for the `impl X { ... }` block. This will record all top-level functions\n/// and create endpoints for the JavaScript class.\n#[derive(Debug)]\nstruct ClassVisitor {\n    renaming: RenameScheme,\n\n    // The type name for this class.\n    type_: Type,\n\n    // Whether we detected a constructor while visiting.\n    constructor: Option<Function>,\n\n    // All static functions recorded.\n    statics: Vec<Function>,\n\n    // All methods recorded.\n    methods: Vec<Function>,\n\n    // All accessors (getters and/or setters) with their names.\n    accessors: BTreeMap<MethodName, Accessor>,\n\n    // All errors we found along the way.\n    errors: Option<syn::Error>,\n}\n\nimpl ClassVisitor {\n    fn new(renaming: RenameScheme, type_: Type) -> Self {\n        Self {\n            renaming,\n            type_,\n            constructor: None,\n            statics: Vec::new(),\n            methods: Vec::new(),\n            accessors: BTreeMap::default(),\n            errors: None,\n        }\n    }\n\n    fn name_of(&self, fn_: &mut ImplItemFn) -> SpannedResult<MethodName> {\n        if let Some(sym) = take_name_value_string(&mut fn_.attrs, \"symbol\")? {\n            return Ok(MethodName::Symbol(sym));\n        }\n\n        take_name_value_attr(&mut fn_.attrs, \"rename\").map_or_else(\n            || {\n                Ok(MethodName::String(\n                    self.renaming.rename(fn_.sig.ident.to_string()),\n                ))\n            },\n            |nv| match &nv {\n                Lit::Str(s) => Ok(MethodName::String(s.value())),\n                _ => error(&nv, \"Invalid attribute value literal\"),\n            },\n        )\n    }\n\n    fn method(\n        &mut self,\n        explicit_method: bool,\n        explicit_static: bool,\n        fn_: &mut ImplItemFn,\n    ) -> SpannedResult<()> {\n        let name = self.name_of(fn_)?;\n        let f = Function::method(\n            name,\n            explicit_method,\n            explicit_static,\n            fn_,\n            Some(&self.type_),\n        )?;\n\n        if f.is_static {\n            self.statics.push(f);\n        } else {\n            self.methods.push(f);\n        }\n\n        Ok(())\n    }\n\n    fn getter(&mut self, fn_: &mut ImplItemFn) -> SpannedResult<()> {\n        let name = self.name_of(fn_)?;\n        self.accessors\n            .entry(name.clone())\n            .or_default()\n            .set_getter(name, fn_, &self.type_)?;\n\n        Ok(())\n    }\n\n    fn setter(&mut self, fn_: &mut ImplItemFn) -> SpannedResult<()> {\n        let name = self.name_of(fn_)?;\n        self.accessors\n            .entry(name.clone())\n            .or_default()\n            .set_setter(name, fn_, &self.type_)?;\n        Ok(())\n    }\n\n    fn constructor(&mut self, fn_: &mut ImplItemFn) -> SpannedResult<()> {\n        self.constructor = Some(Function::constructor(fn_, &self.type_)?);\n        Ok(())\n    }\n\n    /// Add an error to list of errors we are recording along the way. Errors are handled\n    /// at the end of the process, so this combines all errors.\n    #[allow(clippy::needless_pass_by_value)]\n    fn error(&mut self, node: impl Spanned, message: impl Display) {\n        let error = syn::Error::new(node.span(), message);\n\n        match &mut self.errors {\n            None => {\n                self.errors = Some(error);\n            }\n            Some(e) => {\n                e.combine(error);\n            }\n        }\n    }\n\n    /// Serialize the `boa_engine::Class` implementation into a token stream.\n    fn serialize_class_impl(&self, class_ty: &Type, class_name: &str) -> TokenStream2 {\n        let arg_count = self.constructor.as_ref().map_or(0, |c| c.length);\n\n        let accessors = self.accessors.values().map(Accessor::body);\n\n        let builder_methods = self.methods.iter().map(|m| {\n            let key_tokens = m.name.to_key_tokens();\n            let length = m.length;\n            let body = &m.body;\n\n            quote! {\n                builder.method(\n                    #key_tokens,\n                    #length,\n                    boa_engine::NativeFunction::from_fn_ptr(\n                        #body\n                    )\n                );\n            }\n        });\n\n        let builder_statics = self.statics.iter().map(|m| {\n            let key_tokens = m.name.to_key_tokens();\n            let length = m.length;\n            let body = &m.body;\n\n            quote! {\n                builder.static_method(\n                    #key_tokens,\n                    #length,\n                    boa_engine::NativeFunction::from_fn_ptr(\n                        #body\n                    )\n                );\n            }\n        });\n\n        let constructor_body = self.constructor.as_ref().map_or_else(\n            || {\n                quote! {\n                    Ok(Default::default())\n                }\n            },\n            |c| c.body.clone(),\n        );\n\n        quote! {\n            impl boa_engine::class::Class for #class_ty {\n                const NAME: &'static str = #class_name;\n                const LENGTH: usize = #arg_count;\n\n                fn data_constructor(\n                    this: &boa_engine::JsValue,\n                    args: &[boa_engine::JsValue],\n                    context: &mut boa_engine::Context\n                ) -> boa_engine::JsResult<Self> {\n                    #constructor_body\n                }\n\n                fn init(builder: &mut boa_engine::class::ClassBuilder) -> boa_engine::JsResult<()> {\n                    // Add all statics.\n                    #(#builder_statics)*\n\n                    // Add all accessors.\n                    #(#accessors)*\n\n                    // Add all methods to the class.\n                    #(#builder_methods)*\n\n                    Ok(())\n                }\n            }\n        }\n    }\n}\n\nimpl VisitMut for ClassVisitor {\n    // Allow similar names as there are no better ways to name `getter` and `setter`.\n    #[allow(clippy::similar_names)]\n    fn visit_impl_item_fn_mut(&mut self, item: &mut ImplItemFn) {\n        // If there's a `boa` argument, parse it.\n        let has_ctor_attr = take_path_attr(&mut item.attrs, \"constructor\");\n        let has_getter_attr = take_path_attr(&mut item.attrs, \"getter\");\n        let has_setter_attr = take_path_attr(&mut item.attrs, \"setter\");\n        let has_method_attr = take_path_attr(&mut item.attrs, \"method\");\n        let has_static_attr = take_path_attr(&mut item.attrs, \"static\");\n\n        if has_getter_attr && let Err((span, msg)) = self.getter(item) {\n            self.error(span, msg);\n        }\n\n        if has_setter_attr && let Err((span, msg)) = self.setter(item) {\n            self.error(span, msg);\n        }\n\n        if has_ctor_attr && let Err((span, msg)) = self.constructor(item) {\n            self.error(span, msg);\n        }\n\n        // A function is a method if it has a `#[boa(method)]` attribute or has no\n        // method-type related attributes.\n        if (has_static_attr\n            || has_method_attr\n            || !(has_getter_attr || has_ctor_attr || has_setter_attr))\n            && let Err((span, msg)) = self.method(has_method_attr, has_static_attr, item)\n        {\n            self.error(span, msg);\n        }\n\n        syn::visit_mut::visit_impl_item_fn_mut(self, item);\n    }\n}\n\n#[derive(Debug)]\nstruct ClassArguments {\n    name: Option<String>,\n}\n\nimpl Parse for ClassArguments {\n    fn parse(input: ParseStream<'_>) -> syn::Result<Self> {\n        let args: Punctuated<Meta, Token![,]> = Punctuated::parse_terminated(input)?;\n        let mut name = None;\n\n        for arg in &args {\n            match arg {\n                Meta::NameValue(MetaNameValue {\n                    path,\n                    value: Expr::Lit(lit),\n                    ..\n                }) if path.is_ident(\"rename\") => {\n                    name = Some(match &lit.lit {\n                        Lit::Str(s) => Ok(s.value()),\n                        _ => Err(syn::Error::new(lit.span(), \"Expected a string literal\")),\n                    }?);\n                }\n                _ => return Err(syn::Error::new(arg.span(), \"Unrecognize argument.\")),\n            }\n        }\n\n        Ok(Self { name })\n    }\n}\n\npub(crate) fn class_impl(attr: TokenStream, input: TokenStream) -> TokenStream {\n    // Parse the attribute arguments.\n    let args = syn::parse_macro_input!(attr as ClassArguments);\n\n    // Parse the input.\n    let mut impl_ = syn::parse_macro_input!(input as ItemImpl);\n\n    let renaming = match RenameScheme::from_named_attrs(&mut impl_.attrs, \"rename_all\") {\n        Ok(Some(r)) => r,\n        Ok(None) => RenameScheme::CamelCase,\n        Err((span, msg)) => {\n            return syn::Error::new(span, msg).to_compile_error().into();\n        }\n    };\n\n    // Get all methods from the input.\n    let mut visitor = ClassVisitor::new(renaming, impl_.self_ty.as_ref().clone());\n    syn::visit_mut::visit_item_impl_mut(&mut visitor, &mut impl_);\n\n    if let Some(err) = visitor.errors {\n        return err.to_compile_error().into();\n    }\n\n    let Type::Path(pa) = impl_.self_ty.as_ref() else {\n        return syn::Error::new(impl_.span(), \"Impossible to find the name of the class.\")\n            .to_compile_error()\n            .into();\n    };\n    let Some(name) = args\n        .name\n        .or_else(|| pa.path.get_ident().map(ToString::to_string))\n    else {\n        return syn::Error::new(pa.span(), \"Impossible to find the name of the class.\")\n            .to_compile_error()\n            .into();\n    };\n\n    let class_impl = visitor.serialize_class_impl(&impl_.self_ty, &name);\n\n    let debug = take_path_attr(&mut impl_.attrs, \"debug\");\n\n    let tokens = quote! {\n        // Keep the original implementation as is.\n        // Add `#[allow(clippy::needless_pass_by_value)]`, as clippy can complaint when\n        // using passing-by-value but not using it in the body, except that we cannot\n        // convert the types if we pass by reference.\n        #[allow(clippy::needless_pass_by_value)]\n        #impl_\n\n        // The boa_engine::Class implementation.\n        #class_impl\n    };\n\n    #[allow(clippy::print_stderr)]\n    if debug {\n        eprintln!(\"---------\\n{tokens}\\n---------\\n\");\n    }\n\n    tokens.into()\n}\n"
  },
  {
    "path": "core/macros/src/embedded_module_loader.rs",
    "content": "//! Embedded module loader. Creates a `ModuleLoader` instance that contains\n//! files embedded in the binary at build time.\n\nuse proc_macro::TokenStream;\nuse quote::{ToTokens, TokenStreamExt, quote};\nuse std::fs;\nuse std::path::PathBuf;\nuse syn::parse::ParseStream;\nuse syn::punctuated::Punctuated;\nuse syn::{Ident, LitInt, LitStr, Token, parse::Parse};\n\n#[derive(Copy, Clone)]\nenum CompressType {\n    None,\n\n    #[cfg(feature = \"embedded_lz4\")]\n    Lz4,\n}\n\nimpl ToTokens for CompressType {\n    fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) {\n        match self {\n            CompressType::None => tokens.append(proc_macro2::Literal::string(\"none\")),\n            #[cfg(feature = \"embedded_lz4\")]\n            CompressType::Lz4 => tokens.append(proc_macro2::Literal::string(\"lz4\")),\n        }\n    }\n}\n\nimpl Parse for CompressType {\n    fn parse(input: ParseStream<'_>) -> syn::Result<Self> {\n        let ty = input.parse::<LitStr>()?;\n        match ty.value().as_ref() {\n            \"none\" => Ok(Self::None),\n\n            #[cfg(feature = \"lz4_flex\")]\n            \"lz4\" => Ok(Self::Lz4),\n\n            other => Err(input.error(format!(\"Invalid compression type: {other}\"))),\n        }\n    }\n}\n\nenum Argument {\n    Path(LitStr),\n    MaxSize(u64),\n    Compress(CompressType),\n}\n\nimpl Parse for Argument {\n    fn parse(input: ParseStream<'_>) -> syn::Result<Self> {\n        let lookahead = input.lookahead1();\n        // If it's a single literal string, use it as path.\n        if lookahead.peek(LitStr) {\n            return Ok(Self::Path(input.parse()?));\n        }\n\n        match input.parse::<Ident>()?.to_string().as_ref() {\n            \"path\" => {\n                let _sep = input.parse::<Token![,]>()?;\n                Ok(Self::Path(input.parse()?))\n            }\n            \"max_size\" => {\n                let _sep = input.parse::<Token![,]>()?;\n                let value: LitInt = input.parse()?;\n                Ok(Self::MaxSize(value.base10_parse()?))\n            }\n            \"compress\" => {\n                let lookahead = input.lookahead1();\n                if lookahead.peek(Token![=]) {\n                    let _sep = input.parse::<Token![=]>()?;\n                    Ok(Self::Compress(input.parse()?))\n                } else {\n                    cfg_if::cfg_if! {\n                        if #[cfg(feature = \"embedded_lz4\")] {\n                            Ok(Self::Compress(CompressType::Lz4))\n                        } else {\n                            Err(input.error(\"No compression available by default.\"))\n                        }\n                    }\n                }\n            }\n            other => Err(input.error(format!(\"Invalid argument name: {other}\"))),\n        }\n    }\n}\n\nstruct EmbedModuleMacroInput {\n    path: LitStr,\n    max_size: Option<u64>,\n    compress: CompressType,\n}\n\nimpl Parse for EmbedModuleMacroInput {\n    fn parse(input: ParseStream<'_>) -> syn::Result<Self> {\n        let tokens = input.fork();\n        let arguments = Punctuated::<Argument, Token![,]>::parse_terminated(input)?;\n\n        let mut path = None;\n        let mut max_size = None;\n        let mut compress = CompressType::None;\n        for arg in arguments {\n            match arg {\n                Argument::Path(p) => path = Some(p),\n                Argument::MaxSize(sz) => max_size = Some(sz),\n                Argument::Compress(t) => compress = t,\n            }\n        }\n\n        if let Some(path) = path {\n            Ok(Self {\n                path,\n                max_size,\n                compress,\n            })\n        } else {\n            Err(tokens.error(\"Must specify a path.\"))\n        }\n    }\n}\n\n/// List all the files readable from the given directory, recursively.\nfn find_all_files(dir: &mut fs::ReadDir, root: &PathBuf) -> Vec<PathBuf> {\n    let mut files = Vec::new();\n    for entry in dir {\n        let Ok(entry) = entry else {\n            continue;\n        };\n\n        let path = entry.path();\n        if path.is_dir() {\n            let Ok(mut sub_dir) = fs::read_dir(&path) else {\n                continue;\n            };\n            files.append(&mut find_all_files(&mut sub_dir, root));\n        } else if let Ok(path) = path.strip_prefix(root) {\n            files.push(path.to_path_buf());\n        }\n    }\n    files\n}\n\n/// Implementation of the `embed_module_inner!` macro.\n/// This should not be used directly. Use the `embed_module!` macro from the `boa_engine`\n/// crate instead.\npub(crate) fn embed_module_impl(input: TokenStream) -> TokenStream {\n    let manifest_dir = PathBuf::from(std::env::var(\"CARGO_MANIFEST_DIR\").unwrap_or_default());\n\n    let input = syn::parse_macro_input!(input as EmbedModuleMacroInput);\n\n    let root = manifest_dir.join(input.path.value());\n    let max_size = input.max_size.unwrap_or(u64::MAX);\n\n    let mut dir = match fs::read_dir(root.clone()) {\n        Ok(dir) => dir,\n        Err(e) => {\n            return syn::Error::new_spanned(\n                input.path.clone(),\n                format!(\"Error reading directory: {e}\"),\n            )\n            .to_compile_error()\n            .into();\n        }\n    };\n\n    let mut total = 0;\n    let files = find_all_files(&mut dir, &root);\n\n    let inner = match files.into_iter().try_fold(quote! {}, |acc, relative_path| {\n        let path = root.join(&relative_path);\n        let absolute_path = manifest_dir.join(&path).to_string_lossy().to_string();\n        let Some(relative_path) = relative_path.to_str() else {\n            return Err(syn::Error::new_spanned(\n                input.path.clone(),\n                \"Path has non-Unicode characters\",\n            ));\n        };\n        let relative_path = format!(\"{}{}\", std::path::MAIN_SEPARATOR, relative_path);\n\n        // Check the size.\n        let size = fs::metadata(&path)\n            .map_err(|e| {\n                syn::Error::new_spanned(input.path.clone(), format!(\"Error reading file size: {e}\"))\n            })?\n            .len();\n\n        total += size;\n        if total > max_size {\n            return Err(syn::Error::new_spanned(\n                input.path.clone(),\n                \"The total embedded file size exceeds the maximum size\",\n            ));\n        }\n\n        let bytes = fs::read_to_string(&absolute_path).map_err(|e| {\n            syn::Error::new_spanned(\n                input.path.clone(),\n                format!(\"Could not read {absolute_path}: {e}\"),\n            )\n        })?;\n\n        let compress = input.compress;\n        let bytes = match compress {\n            CompressType::None => bytes.as_bytes().to_vec(),\n\n            #[cfg(feature = \"embedded_lz4\")]\n            CompressType::Lz4 => lz4_flex::compress_prepend_size(bytes.as_bytes()),\n        };\n\n        Ok(quote! {\n            #acc\n\n            (\n                #compress,\n                #relative_path,\n                &[#(#bytes),*] as &[u8],\n            ),\n        })\n    }) {\n        Ok(inner) => inner,\n        Err(e) => return e.to_compile_error().into(),\n    };\n\n    let stream = quote! {\n        [\n            #inner\n        ]\n    };\n\n    stream.into()\n}\n"
  },
  {
    "path": "core/macros/src/lib.rs",
    "content": "//! Macros for the Boa JavaScript engine.\n#![doc = include_str!(\"../ABOUT.md\")]\n#![doc(\n    html_logo_url = \"https://raw.githubusercontent.com/boa-dev/boa/main/assets/logo_black.svg\",\n    html_favicon_url = \"https://raw.githubusercontent.com/boa-dev/boa/main/assets/logo_black.svg\"\n)]\n#![cfg_attr(not(test), forbid(clippy::unwrap_used))]\n\nuse crate::utils::RenameScheme;\nuse cow_utils::CowUtils;\nuse proc_macro::TokenStream;\nuse proc_macro2::Literal;\nuse quote::{ToTokens, quote};\nuse syn::{\n    Data, DeriveInput, Expr, ExprLit, Fields, FieldsNamed, Ident, Lit, LitStr, Token,\n    parse::{Parse, ParseStream},\n    parse_macro_input,\n    punctuated::Punctuated,\n};\nuse synstructure::{AddBounds, Structure, decl_derive};\n\nmod embedded_module_loader;\n\nmod class;\nmod module;\nmod utils;\nmod value;\n\n/// The `js_value!` macro creates a `JsValue` instance based on a JSON-like DSL.\n#[proc_macro]\npub fn js_value(input: TokenStream) -> TokenStream {\n    value::js_value_impl(proc_macro2::TokenStream::from(input)).into()\n}\n\n/// Create a `JsObject` object from a simpler DSL that resembles JSON.\n#[proc_macro]\npub fn js_object(input: TokenStream) -> TokenStream {\n    value::js_object_impl(proc_macro2::TokenStream::from(input)).into()\n}\n\n/// Implementation of the inner iterator of the `embed_module!` macro. All\n/// arguments are required.\n///\n/// # Warning\n/// This should not be used directly as is, and instead should be used through\n/// the `embed_module!` macro in `boa_engine` for convenience.\n#[proc_macro]\npub fn embed_module_inner(input: TokenStream) -> TokenStream {\n    embedded_module_loader::embed_module_impl(input)\n}\n\n/// `boa_class` proc macro attribute that applies to an `impl XYZ` block and\n/// add a `[boa_engine::JsClass]` implementation for it.\n///\n/// It will transform functions in the `impl ...` block as follow (by default, see\n/// below):\n/// 1. `fn some_method(&self, ...) -> ... {}` will be added as class methods with\n///    the name `some_method`, borrowing the object for the ref. This is dangerous\n///    if the function execute/eval JavaScript back (potentially leading to a\n///    `BorrowError`).\n/// 2. `fn some_method(&mut self, ...) -> ... {}` will be added as class methods,\n///    similar to the above but borrowing as mutable at runtime.\n/// 3. `fn some_method(...) -> ... {}` (no self mention) will be added as a\n///    static method.\n/// 4. `#[boa(constructor)] fn ...(...) -> Self {}` (or returning `JsResult<Self>`)\n///    will be used as the constructor of the class. If no constructor is declared,\n///    `Default::default()` will be used instead. If the `Default` trait is not\n///    defined for the type, an error will happen.\n/// 5. `#[boa(getter)]`\n///\n/// To change this behaviour, you can use the following attributes on the function\n/// declarations:\n/// 1. `#[boa(rename = \"...\")]` renames the function in JavaScript with the string.\n/// 2. `#[boa(getter)]` will declare a getter accessor.\n/// 2. `#[boa(setter)]` will declare a setter accessor.\n/// 3. `#[boa(static)]` will declare a static method.\n/// 4. `#[boa(method)]` will declare a method.\n/// 5. `#[boa(constructor)]` will declare a constructor.\n/// 6. `#[boa(length = 123)]` sets the length of the function in JavaScript (ie. its\n///    number of arguments accepted).\n///\n/// Multiple of those attributes can be added to a single method.\n///\n/// The top level `boa_class` supports the following:\n/// 1. `#[boa_class(rename = \"...\")]` sets the name of the class in JavaScript.\n/// 2. `#[boa(rename_all = \"camelCase\")]` will change the naming scheme of verbatim\n///    to using \"camelCase\" or \"none\".\n///\n/// # Warning\n/// This should not be used directly as is, and instead should be used through\n/// the `embed_module!` macro in `boa_engine` for convenience.\n#[proc_macro_attribute]\npub fn boa_class(attr: TokenStream, item: TokenStream) -> TokenStream {\n    class::class_impl(attr, item)\n}\n\n/// `boa_module` proc macro attribute for declaring a `boa_engine::Module` based\n/// on a Rust module. The original Rust module will also be exposed as is.\n///\n/// This macro exports two functions out of the existing module (and those\n/// functions must not exist in the declared module):\n///\n/// ## `boa_module(realm: Option<Realm>, context: &mut Context) -> JsResult<Module>`\n/// Create a JavaScript module from the rust module's content.\n///\n/// ## `boa_register(realm: Option<Realm>, context: &mut Context) -> JsResult<()>`\n/// Register the constants, classes and functions from the module in the global\n/// scope of the Realm (if specified) or the context (if no realm).\n#[proc_macro_attribute]\npub fn boa_module(attr: TokenStream, item: TokenStream) -> TokenStream {\n    module::module_impl(attr, item)\n}\n\nstruct Static {\n    literal: LitStr,\n    ident: Ident,\n}\n\nimpl Parse for Static {\n    fn parse(input: ParseStream<'_>) -> syn::Result<Self> {\n        let expr = Expr::parse(input)?;\n        match expr {\n            Expr::Tuple(expr) => {\n                let mut elems = expr.elems.iter().cloned();\n                let literal = elems\n                    .next()\n                    .ok_or_else(|| syn::Error::new_spanned(&expr, \"invalid empty tuple\"))?;\n                let ident = elems.next();\n                if elems.next().is_some() {\n                    return Err(syn::Error::new_spanned(\n                        &expr,\n                        \"invalid tuple with more than two elements\",\n                    ));\n                }\n                let Expr::Lit(ExprLit {\n                    lit: Lit::Str(literal),\n                    ..\n                }) = literal\n                else {\n                    return Err(syn::Error::new_spanned(\n                        literal,\n                        \"expected an UTF-8 string literal\",\n                    ));\n                };\n\n                let ident = if let Some(ident) = ident {\n                    syn::parse2::<Ident>(ident.into_token_stream())?\n                } else {\n                    Ident::new(&literal.value().cow_to_uppercase(), literal.span())\n                };\n\n                Ok(Self { literal, ident })\n            }\n            Expr::Lit(expr) => match expr.lit {\n                Lit::Str(str) => Ok(Self {\n                    ident: Ident::new(&str.value().cow_to_uppercase(), str.span()),\n                    literal: str,\n                }),\n                _ => Err(syn::Error::new_spanned(\n                    expr,\n                    \"expected an UTF-8 string literal\",\n                )),\n            },\n            _ => Err(syn::Error::new_spanned(\n                expr,\n                \"expected a string literal or a tuple expression\",\n            )),\n        }\n    }\n}\n\nstruct Syms(Vec<Static>);\n\nimpl Parse for Syms {\n    fn parse(input: ParseStream<'_>) -> syn::Result<Self> {\n        let parsed = Punctuated::<Static, Token![,]>::parse_terminated(input)?;\n        let literals = parsed.into_iter().collect();\n        Ok(Self(literals))\n    }\n}\n\n#[doc(hidden)]\n#[proc_macro]\npub fn static_syms(input: TokenStream) -> TokenStream {\n    let literals = parse_macro_input!(input as Syms).0;\n\n    let consts = literals.iter().enumerate().map(|(mut idx, lit)| {\n        let doc = format!(\n            \"Symbol for the \\\"{}\\\" string.\",\n            lit.literal\n                .value()\n                .cow_replace('<', r\"\\<\")\n                .cow_replace('>', r\"\\>\")\n                .cow_replace('*', r\"\\*\")\n        );\n        let ident = &lit.ident;\n        idx += 1;\n        quote! {\n            #[doc = #doc]\n            pub const #ident: Self = unsafe { Self::new_unchecked(#idx) };\n        }\n    });\n\n    let literals = literals.iter().map(|lit| &lit.literal).collect::<Vec<_>>();\n\n    let caches = quote! {\n        type Set<T> = ::indexmap::IndexSet<T, ::core::hash::BuildHasherDefault<::rustc_hash::FxHasher>>;\n\n        /// Ordered set of commonly used static `UTF-8` strings.\n        ///\n        /// # Note\n        ///\n        /// `COMMON_STRINGS_UTF8`, `COMMON_STRINGS_UTF16` and the constants\n        /// defined in [`Sym`] must always be in sync.\n        pub(super) static COMMON_STRINGS_UTF8: ::phf::OrderedSet<&'static str> = {\n            const COMMON_STRINGS: ::phf::OrderedSet<&'static str> = ::phf::phf_ordered_set! {\n                #(#literals),*\n            };\n            // A `COMMON_STRINGS` of size `usize::MAX` would cause an overflow on our `Interner`\n            ::static_assertions::const_assert!(COMMON_STRINGS.len() < usize::MAX);\n            COMMON_STRINGS\n        };\n\n        /// Ordered set of commonly used static `UTF-16` strings.\n        ///\n        /// # Note\n        ///\n        /// `COMMON_STRINGS_UTF8`, `COMMON_STRINGS_UTF16` and the constants\n        /// defined in [`Sym`] must always be in sync.\n        // FIXME: use phf when const expressions are allowed.\n        // <https://github.com/rust-phf/rust-phf/issues/188>\n        pub(super) static COMMON_STRINGS_UTF16: ::once_cell::sync::Lazy<Set<&'static [u16]>> =\n            ::once_cell::sync::Lazy::new(|| {\n                let mut set = Set::with_capacity_and_hasher(\n                    COMMON_STRINGS_UTF8.len(),\n                    ::core::hash::BuildHasherDefault::default()\n                );\n                #(\n                    set.insert(::boa_macros::utf16!(#literals));\n                )*\n                set\n            });\n    };\n\n    quote! {\n        impl Sym {\n            #(#consts)*\n        }\n        #caches\n    }\n    .into()\n}\n\n/// Construct a utf-16 array literal from a utf-8 [`str`] literal.\n#[proc_macro]\npub fn utf16(input: TokenStream) -> TokenStream {\n    let literal = parse_macro_input!(input as LitStr);\n    let utf8 = literal.value();\n    let utf16 = utf8.encode_utf16().collect::<Vec<_>>();\n    quote! {\n        [#(#utf16),*].as_slice()\n    }\n    .into()\n}\n\n/// Convert a utf8 string literal to a `JsString`\n#[proc_macro]\npub fn js_str(input: TokenStream) -> TokenStream {\n    let literal = parse_macro_input!(input as LitStr);\n\n    let mut is_latin1 = true;\n    let codepoints = literal\n        .value()\n        .encode_utf16()\n        .map(|x| {\n            if x > u8::MAX.into() {\n                is_latin1 = false;\n            }\n            Literal::u16_unsuffixed(x)\n        })\n        .collect::<Vec<_>>();\n    if is_latin1 {\n        quote! {\n            ::boa_engine::string::JsStr::latin1([#(#codepoints),*].as_slice())\n        }\n    } else {\n        quote! {\n            ::boa_engine::string::JsStr::utf16([#(#codepoints),*].as_slice())\n        }\n    }\n    .into()\n}\n\ndecl_derive! {\n    [Trace, attributes(boa_gc, unsafe_ignore_trace)] =>\n    /// Derive the `Trace` trait.\n    derive_trace\n}\n\n/// Derives the `Trace` trait.\n#[allow(clippy::too_many_lines)]\nfn derive_trace(mut s: Structure<'_>) -> proc_macro2::TokenStream {\n    struct EmptyTrace {\n        copy: bool,\n        drop: bool,\n    }\n\n    impl Parse for EmptyTrace {\n        fn parse(input: ParseStream<'_>) -> syn::Result<Self> {\n            let i: Ident = input.parse()?;\n\n            if i != \"empty_trace\" && i != \"unsafe_empty_trace\" && i != \"unsafe_no_drop\" {\n                let msg = format!(\n                    \"expected token \\\"empty_trace\\\", \\\"unsafe_empty_trace\\\" or \\\"unsafe_no_drop\\\", found {i:?}\"\n                );\n                return Err(syn::Error::new_spanned(i.clone(), msg));\n            }\n\n            Ok(Self {\n                copy: i == \"empty_trace\",\n                drop: i == \"empty_trace\" || i != \"unsafe_no_drop\",\n            })\n        }\n    }\n\n    let mut drop = true;\n\n    for attr in &s.ast().attrs {\n        if attr.path().is_ident(\"boa_gc\") {\n            let trace = match attr.parse_args::<EmptyTrace>() {\n                Ok(t) => t,\n                Err(e) => return e.into_compile_error(),\n            };\n\n            if trace.copy {\n                s.add_where_predicate(syn::parse_quote!(Self: Copy));\n            }\n\n            if !trace.drop {\n                drop = false;\n                continue;\n            }\n\n            return s.unsafe_bound_impl(\n                quote!(::boa_gc::Trace),\n                quote! {\n                    #[inline(always)]\n                    unsafe fn trace(&self, _tracer: &mut ::boa_gc::Tracer) {}\n                    #[inline(always)]\n                    unsafe fn trace_non_roots(&self) {}\n                    #[inline]\n                    fn run_finalizer(&self) {\n                        ::boa_gc::Finalize::finalize(self)\n                    }\n                },\n            );\n        }\n    }\n\n    s.filter(|bi| {\n        !bi.ast()\n            .attrs\n            .iter()\n            .any(|attr| attr.path().is_ident(\"unsafe_ignore_trace\"))\n    });\n    let trace_body = s.each(|bi| quote!(::boa_gc::Trace::trace(#bi, tracer)));\n    let trace_other_body = s.each(|bi| quote!(mark(#bi)));\n\n    s.add_bounds(AddBounds::Fields);\n    let trace_impl = s.unsafe_bound_impl(\n        quote!(::boa_gc::Trace),\n        quote! {\n            #[inline]\n            unsafe fn trace(&self, tracer: &mut ::boa_gc::Tracer) {\n                #[allow(dead_code)]\n                let mut mark = |it: &dyn ::boa_gc::Trace| {\n                    // SAFETY: The implementor must ensure that `trace` is correctly implemented.\n                    unsafe {\n                        ::boa_gc::Trace::trace(it, tracer);\n                    }\n                };\n                match *self { #trace_body }\n            }\n            #[inline]\n            unsafe fn trace_non_roots(&self) {\n                #[allow(dead_code)]\n                fn mark<T: ::boa_gc::Trace + ?Sized>(it: &T) {\n                    // SAFETY: The implementor must ensure that `trace_non_roots` is correctly implemented.\n                    unsafe {\n                        ::boa_gc::Trace::trace_non_roots(it);\n                    }\n                }\n                match *self { #trace_other_body }\n            }\n            #[inline]\n            fn run_finalizer(&self) {\n                ::boa_gc::Finalize::finalize(self);\n                #[allow(dead_code)]\n                fn mark<T: ::boa_gc::Trace + ?Sized>(it: &T) {\n                    unsafe {\n                        ::boa_gc::Trace::run_finalizer(it);\n                    }\n                }\n                match *self { #trace_other_body }\n            }\n        },\n    );\n\n    // We also implement drop to prevent unsafe drop implementations on this\n    // type and encourage people to use Finalize. This implementation will\n    // call `Finalize::finalize` if it is safe to do so.\n    let drop_impl = if drop {\n        s.unbound_impl(\n            quote!(::core::ops::Drop),\n            quote! {\n                #[allow(clippy::inline_always)]\n                #[inline(always)]\n                fn drop(&mut self) {\n                    if ::boa_gc::finalizer_safe() {\n                        ::boa_gc::Finalize::finalize(self);\n                    }\n                }\n            },\n        )\n    } else {\n        quote!()\n    };\n\n    quote! {\n        #trace_impl\n        #drop_impl\n    }\n}\n\ndecl_derive! {\n    [Finalize] =>\n    /// Derive the `Finalize` trait.\n    derive_finalize\n}\n\n/// Derives the `Finalize` trait.\n#[allow(clippy::needless_pass_by_value)]\nfn derive_finalize(s: Structure<'_>) -> proc_macro2::TokenStream {\n    s.unbound_impl(quote!(::boa_gc::Finalize), quote!())\n}\n\ndecl_derive! {\n    [JsData] =>\n    /// Derive the `JsData` trait.\n    derive_js_data\n}\n\n/// Derives the `JsData` trait.\n#[allow(clippy::needless_pass_by_value)]\nfn derive_js_data(s: Structure<'_>) -> proc_macro2::TokenStream {\n    s.unbound_impl(quote!(::boa_engine::JsData), quote!())\n}\n\n/// Derives the `TryFromJs` trait, with the `#[boa()]` attribute.\n///\n/// # Panics\n///\n/// It will panic if the user tries to derive the `TryFromJs` trait in an `enum` or a tuple struct.\n#[proc_macro_derive(TryFromJs, attributes(boa))]\npub fn derive_try_from_js(input: TokenStream) -> TokenStream {\n    // Parse the input tokens into a syntax tree\n    let input = parse_macro_input!(input as DeriveInput);\n\n    let Data::Struct(data) = input.data else {\n        panic!(\"you can only derive TryFromJs for structs\");\n    };\n\n    let Fields::Named(fields) = data.fields else {\n        panic!(\"you can only derive TryFromJs for named-field structs\")\n    };\n\n    let renaming = match RenameScheme::from_named_attrs(&mut input.attrs.clone(), \"rename_all\") {\n        Ok(renaming) => renaming.unwrap_or(RenameScheme::None),\n        Err((span, msg)) => return syn::Error::new(span, msg).to_compile_error().into(),\n    };\n\n    let conv = generate_conversion(fields, renaming).unwrap_or_else(to_compile_errors);\n\n    let type_name = input.ident;\n\n    // Build the output, possibly using quasi-quotation\n    let expanded = quote! {\n        impl ::boa_engine::value::TryFromJs for #type_name {\n            fn try_from_js(value: &boa_engine::JsValue, context: &mut boa_engine::Context)\n                -> boa_engine::JsResult<Self> {\n                let o = value.as_object().ok_or_else(|| ::boa_engine::JsError::from(\n                    ::boa_engine::JsNativeError::typ()\n                        .with_message(\"value is not an object\")\n                ))?;\n                #conv\n            }\n        }\n    };\n\n    // Hand the output tokens back to the compiler\n    expanded.into()\n}\n\n/// Generates the conversion field by field.\nfn generate_conversion(\n    fields: FieldsNamed,\n    rename: RenameScheme,\n) -> Result<proc_macro2::TokenStream, Vec<syn::Error>> {\n    use syn::spanned::Spanned;\n\n    let mut field_list = Vec::with_capacity(fields.named.len());\n    let mut final_fields = Vec::with_capacity(fields.named.len());\n\n    for field in fields.named {\n        let span = field.span();\n        let name = field.ident.ok_or_else(|| {\n            vec![syn::Error::new(\n                span,\n                \"you can only derive `TryFromJs` for named-field structs\",\n            )]\n        })?;\n\n        field_list.push(name.clone());\n\n        let mut from_js_with = None;\n        let mut field_name = rename.rename(format!(\"{name}\"));\n        if let Some(attr) = field\n            .attrs\n            .into_iter()\n            .find(|attr| attr.path().is_ident(\"boa\"))\n        {\n            attr.parse_nested_meta(|meta| {\n                if meta.path.is_ident(\"from_js_with\") {\n                    let value = meta.value()?;\n                    from_js_with = Some(value.parse::<LitStr>()?);\n                    Ok(())\n                } else if meta.path.is_ident(\"rename\") {\n                    let value = meta.value()?;\n                    field_name = value.parse::<LitStr>()?.value();\n                    Ok(())\n                } else {\n                    Err(meta.error(\n                        \"invalid syntax in the `#[boa()]` attribute. \\\n                              Note that this attribute only accepts the following syntax: \\\n                            `#[boa(from_js_with = \\\"fully::qualified::path\\\")]`\",\n                    ))\n                }\n            })\n            .map_err(|err| vec![err])?;\n        }\n\n        let error_str = format!(\"cannot get property {name} of value\");\n        final_fields.push(quote! {\n            let #name = match props.get(&::boa_engine::js_string!(#field_name).into()) {\n                Some(pd) => pd.value().ok_or_else(|| ::boa_engine::JsError::from(\n                        ::boa_engine::JsNativeError::typ().with_message(#error_str)\n                    ))?.clone().try_js_into(context)?,\n                None => ::boa_engine::JsValue::undefined().try_js_into(context)?,\n            };\n        });\n\n        if let Some(method) = from_js_with {\n            let ident = Ident::new(&method.value(), method.span());\n            final_fields.push(quote! {\n                let #name = #ident(&#name, context)?;\n            });\n        }\n    }\n\n    // TODO: this could possibly skip accessors. Consider using `JsObject::get` instead.\n    Ok(quote! {\n        let o = o.borrow();\n        let props = o.properties();\n        #(#final_fields)*\n        Ok(Self {\n            #(#field_list),*\n        })\n    })\n}\n\n/// Generates a list of compile errors.\n#[allow(clippy::needless_pass_by_value)]\nfn to_compile_errors(errors: Vec<syn::Error>) -> proc_macro2::TokenStream {\n    let compile_errors = errors.iter().map(syn::Error::to_compile_error);\n    quote!(#(#compile_errors)*)\n}\n\n/// Derives the `TryIntoJs` trait, with the `#[boa()]` attribute.\n///\n/// # Panics\n///\n/// It will panic if the user tries to derive the `TryIntoJs` trait in an `enum` or a tuple struct.\n#[proc_macro_derive(TryIntoJs, attributes(boa))]\npub fn derive_try_into_js(input: TokenStream) -> TokenStream {\n    // Parse the input tokens into a syntax tree\n    let input = parse_macro_input!(input as DeriveInput);\n\n    let Data::Struct(data) = input.data else {\n        panic!(\"you can only derive TryFromJs for structs\");\n    };\n    // TODO: Enums ?\n\n    let Fields::Named(fields) = data.fields else {\n        panic!(\"you can only derive TryFromJs for named-field structs\")\n    };\n\n    let renaming = match RenameScheme::from_named_attrs(&mut input.attrs.clone(), \"rename_all\") {\n        Ok(renaming) => renaming.unwrap_or(RenameScheme::None),\n        Err((span, msg)) => return syn::Error::new(span, msg).to_compile_error().into(),\n    };\n\n    let props = generate_obj_properties(fields, renaming)\n        .map_err(|err| vec![err])\n        .unwrap_or_else(to_compile_errors);\n\n    let type_name = input.ident;\n\n    // Build the output, possibly using quasi-quotation\n    let expanded = quote! {\n        impl ::boa_engine::value::TryIntoJs for #type_name {\n            fn try_into_js(&self, context: &mut boa_engine::Context) -> boa_engine::JsResult<boa_engine::JsValue> {\n                let obj = boa_engine::JsObject::default(context.intrinsics());\n                #props\n                boa_engine::JsResult::Ok(obj.into())\n            }\n        }\n    };\n\n    // Hand the output tokens back to the compiler\n    expanded.into()\n}\n\n/// Generates property creation for object.\nfn generate_obj_properties(\n    fields: FieldsNamed,\n    renaming: RenameScheme,\n) -> Result<proc_macro2::TokenStream, syn::Error> {\n    use syn::spanned::Spanned;\n\n    let mut prop_ctors = Vec::with_capacity(fields.named.len());\n\n    for field in fields.named {\n        let span = field.span();\n        let name = field.ident.ok_or_else(|| {\n            syn::Error::new(\n                span,\n                \"you can only derive `TryIntoJs` for named-field structs\",\n            )\n        })?;\n\n        let mut into_js_with = None;\n        let mut prop_key = renaming.rename(format!(\"{name}\"));\n        let mut skip = false;\n\n        for attr in field\n            .attrs\n            .into_iter()\n            .filter(|attr| attr.path().is_ident(\"boa\"))\n        {\n            attr.parse_nested_meta(|meta| {\n                if meta.path.is_ident(\"into_js_with\") {\n                    let value = meta.value()?;\n                    into_js_with = Some(value.parse::<LitStr>()?);\n                    Ok(())\n                } else if meta.path.is_ident(\"rename\") {\n                    let value = meta.value()?;\n                    prop_key = value.parse::<LitStr>()?.value();\n                    Ok(())\n                } else if meta.path.is_ident(\"skip\") & meta.input.is_empty() {\n                    skip = true;\n                    Ok(())\n                } else {\n                    Err(meta.error(\n                        \"invalid syntax in the `#[boa()]` attribute. \\\n                              Note that this attribute only accepts the following syntax: \\\n                            \\n* `#[boa(into_js_with = \\\"fully::qualified::path\\\")]`\\\n                            \\n* `#[boa(rename = \\\"jsPropertyName\\\")]` \\\n                            \\n* `#[boa(skip)]` \\\n                            \",\n                    ))\n                }\n            })?;\n        }\n\n        if skip {\n            continue;\n        }\n\n        let value = if let Some(into_js_with) = into_js_with {\n            let into_js_with = Ident::new(&into_js_with.value(), into_js_with.span());\n            quote! { #into_js_with(&self.#name, context)? }\n        } else {\n            quote! { boa_engine::value::TryIntoJs::try_into_js(&self.#name, context)? }\n        };\n        prop_ctors.push(quote! {\n            obj.create_data_property_or_throw(boa_engine::js_string!(#prop_key), #value, context)?;\n        });\n    }\n\n    Ok(quote! { #(#prop_ctors)* })\n}\n"
  },
  {
    "path": "core/macros/src/module.rs",
    "content": "use crate::class;\nuse crate::class::Function;\nuse crate::utils::{RenameScheme, SpannedResult, error, take_name_value_string, take_path_attr};\nuse proc_macro::TokenStream;\nuse proc_macro2::TokenStream as TokenStream2;\nuse quote::quote;\nuse syn::parse::{Parse, ParseStream};\nuse syn::spanned::Spanned;\nuse syn::{\n    Item, ItemConst, ItemEnum, ItemExternCrate, ItemFn, ItemForeignMod, ItemImpl, ItemMacro,\n    ItemMod, ItemStatic, ItemStruct, ItemTrait, ItemTraitAlias, ItemType, ItemUnion, ItemUse,\n};\n\n#[derive(Debug)]\nstruct ModuleArguments {}\n\nimpl Parse for ModuleArguments {\n    fn parse(_input: ParseStream<'_>) -> syn::Result<Self> {\n        Ok(Self {})\n    }\n}\n\nfn const_item(\n    c: &mut ItemConst,\n    renaming: RenameScheme,\n) -> SpannedResult<(String, TokenStream2, TokenStream2)> {\n    let ident = &c.ident;\n    let name = take_name_value_string(&mut c.attrs, \"rename\")?\n        .unwrap_or_else(|| renaming.rename(ident.to_string()));\n\n    Ok((\n        name.clone(),\n        quote! {\n            m.set_export( &boa_engine::js_string!( #name ), boa_engine::JsValue::from( #ident ) )?;\n        },\n        quote! {\n            if let Some(ref realm) = realm {\n                realm.register_property(\n                    boa_engine::js_string!( #name ),\n                    boa_engine::JsValue::from( #ident ),\n                    boa_engine::property::Attribute::all(),\n                    context,\n                )?;\n            } else {\n                context.register_global_property(\n                    boa_engine::js_string!( #name ),\n                    boa_engine::JsValue::from( #ident ),\n                    boa_engine::property::Attribute::all(),\n                )?;\n            }\n        },\n    ))\n}\n\nfn fn_item(\n    fn_: &mut ItemFn,\n    renaming: RenameScheme,\n) -> SpannedResult<(String, TokenStream2, TokenStream2)> {\n    let ident = &fn_.sig.ident;\n    let name = take_name_value_string(&mut fn_.attrs, \"rename\")?\n        .unwrap_or_else(|| renaming.rename(ident.to_string()));\n\n    if fn_.sig.asyncness.is_some() {\n        error(&fn_.sig.asyncness, \"Async methods are not supported.\")?;\n    }\n\n    let fn_ = Function::from_sig(\n        class::MethodName::String(name.clone()),\n        false,\n        false,\n        &mut fn_.attrs,\n        &mut fn_.sig,\n        None,\n    )?;\n    let fn_body = fn_.body();\n\n    Ok((\n        name.clone(),\n        quote! {\n            m.set_export(\n                &boa_engine::js_string!( #name ),\n                boa_engine::JsValue::from(\n                    boa_engine::NativeFunction::from_fn_ptr( #fn_body )\n                        .to_js_function(context.realm())\n                ),\n            )?;\n        },\n        quote! {\n            let function = #fn_body;\n            if let Some(ref realm) = realm {\n                realm.register_property(\n                    boa_engine::js_string!( #name ),\n                    boa_engine::JsValue::from(\n                        boa_engine::NativeFunction::from_fn_ptr( function )\n                            .to_js_function(context.realm())\n                    ),\n                    boa_engine::property::Attribute::all(),\n                    context,\n                )?;\n            } else {\n                context.register_global_property(\n                    boa_engine::js_string!( #name ),\n                    boa_engine::JsValue::from(\n                        boa_engine::NativeFunction::from_fn_ptr( function )\n                            .to_js_function(context.realm())\n                    ),\n                    boa_engine::property::Attribute::all(),\n                )?;\n            }\n        },\n    ))\n}\n\nfn type_item(\n    ty: &mut ItemType,\n    renaming: RenameScheme,\n) -> SpannedResult<(String, TokenStream2, TokenStream2)> {\n    let ident = &ty.ident;\n    let name = take_name_value_string(&mut ty.attrs, \"rename\")?\n        .unwrap_or_else(|| renaming.rename(ident.to_string()));\n    let path = ty.ty.as_ref();\n\n    Ok((\n        name.clone(),\n        quote! {\n            m.export_named_class::< #path >(&boa_engine::js_string!(#name), context)?;\n        },\n        quote! {\n            if let Some(ref realm) = realm {\n                let mut class_builder = boa_engine::class::ClassBuilder::new::<#path>(context);\n                <#path as boa_engine::class::Class>::init(&mut class_builder)?;\n                let class = class_builder.build();\n                realm.register_class::<#path>(class);\n            } else {\n                context.register_global_class::<#path>()?;\n            }\n        },\n    ))\n}\n\npub(crate) fn module_impl(attr: TokenStream, input: TokenStream) -> TokenStream {\n    // Parse the attribute arguments.\n    let args = syn::parse_macro_input!(attr as ModuleArguments);\n\n    // Parse the input.\n    let mod_ = syn::parse_macro_input!(input as ItemMod);\n\n    match module_impl_impl(args, mod_) {\n        Ok(tokens) => tokens.into(),\n        Err((span, msg)) => syn::Error::new(span, msg).to_compile_error().into(),\n    }\n}\n\n// Allow too many lines as this is a giant match with local variables. The logic is still\n// fairly straightforward.\n#[allow(clippy::too_many_lines)]\nfn module_impl_impl(_args: ModuleArguments, mut mod_: ItemMod) -> SpannedResult<TokenStream2> {\n    let renaming = RenameScheme::from_named_attrs(&mut mod_.attrs, \"rename_all\")?\n        .unwrap_or(RenameScheme::CamelCase);\n    let class_renaming = RenameScheme::from_named_attrs(&mut mod_.attrs, \"rename_all_class\")?\n        .unwrap_or(RenameScheme::PascalCase);\n\n    // Iterate through all top-level content. If the module is empty, still\n    // iterate to create an empty JS module.\n    let mut original_module_decl = quote! {};\n    let mut module_fn = quote! {};\n    let mut global_fn = quote! {};\n    let mut module_exports = quote! {};\n    let mut generics = vec![];\n\n    for item in mod_.content.map_or_else(Vec::new, |c| c.1).as_mut_slice() {\n        // Check for skip attributes.\n        match item {\n            Item::Const(ItemConst { attrs, .. })\n            | Item::Enum(ItemEnum { attrs, .. })\n            | Item::ExternCrate(ItemExternCrate { attrs, .. })\n            | Item::Fn(ItemFn { attrs, .. })\n            | Item::ForeignMod(ItemForeignMod { attrs, .. })\n            | Item::Impl(ItemImpl { attrs, .. })\n            | Item::Macro(ItemMacro { attrs, .. })\n            | Item::Mod(ItemMod { attrs, .. })\n            | Item::Static(ItemStatic { attrs, .. })\n            | Item::Struct(ItemStruct { attrs, .. })\n            | Item::Trait(ItemTrait { attrs, .. })\n            | Item::TraitAlias(ItemTraitAlias { attrs, .. })\n            | Item::Type(ItemType { attrs, .. })\n            | Item::Union(ItemUnion { attrs, .. })\n            | Item::Use(ItemUse { attrs, .. }) => {\n                if take_path_attr(attrs, \"skip\") {\n                    original_module_decl = quote! {\n                        #original_module_decl\n                        #item\n                    };\n                    continue;\n                }\n            }\n            _ => {}\n        }\n\n        let result = match item {\n            Item::Const(c) => const_item(c, renaming),\n            Item::Fn(f) => {\n                generics.extend(f.sig.generics.params.iter().cloned());\n                fn_item(f, renaming)\n            }\n            Item::Use(_) => {\n                // Skip use statements. These are valid but ignored.\n                original_module_decl = quote! {\n                    #original_module_decl\n                    #item\n                };\n                continue;\n            }\n            Item::Type(ty) => type_item(ty, class_renaming),\n            _ => Err((\n                item.span(),\n                \"Invalid boa_module top-level item.\".to_string(),\n            )),\n        };\n        let (export_name, export_decl, register_global) = result?;\n\n        module_fn = quote! {\n            #module_fn\n            #export_decl\n        };\n        global_fn = quote! {\n            #global_fn\n            #register_global\n        };\n        module_exports = quote! {\n            #module_exports\n            boa_engine::js_string!( #export_name ),\n        };\n        original_module_decl = quote! {\n            #[allow(clippy::needless_pass_by_value)]\n            #original_module_decl\n\n            #[allow(unused)]\n            #item\n        }\n    }\n\n    let debug = take_path_attr(&mut mod_.attrs, \"debug\");\n    let vis = mod_.vis;\n    let name = mod_.ident;\n    let attrs = mod_.attrs;\n    let safety = mod_.unsafety;\n\n    let generics = quote! {\n        <#(#generics),*>\n    };\n\n    let tokens = quote! {\n        #(#attrs)*\n        #vis #safety mod #name {\n            #original_module_decl\n\n            pub(super) fn boa_register #generics (\n                realm: Option<boa_engine::realm::Realm>,\n                context: &mut boa_engine::Context,\n            ) -> boa_engine::JsResult<()> {\n                #global_fn\n                Ok(())\n            }\n\n            pub(super) fn boa_module #generics (\n                realm: Option<boa_engine::realm::Realm>,\n                context: &mut boa_engine::Context,\n            ) -> boa_engine::Module {\n                boa_engine::Module::synthetic(\n                    &[ #module_exports ],\n                    boa_engine::module::SyntheticModuleInitializer::from_copy_closure(\n                        |m, context| {\n                            #module_fn\n                            Ok(())\n                        }\n                    ),\n                    None,\n                    realm,\n                    context,\n                )\n            }\n        }\n    };\n\n    #[allow(clippy::print_stderr)]\n    if debug {\n        eprintln!(\"---------\\n{tokens}\\n---------\\n\");\n    }\n\n    Ok(tokens)\n}\n"
  },
  {
    "path": "core/macros/src/utils.rs",
    "content": "use proc_macro2::{Ident, Span as Span2};\nuse quote::ToTokens;\nuse std::fmt::Display;\nuse std::str::FromStr;\nuse syn::ext::IdentExt;\nuse syn::spanned::Spanned;\nuse syn::{Attribute, Expr, ExprLit, Lit, MetaNameValue};\n\npub(crate) type SpannedResult<T> = Result<T, (Span2, String)>;\n\n/// A function to make it easier to return error messages.\npub(crate) fn error<T>(span: &impl Spanned, message: impl Display) -> SpannedResult<T> {\n    Err((span.span(), message.to_string()))\n}\n\n/// Look (and remove from AST) a `path` version of the attribute `boa`, e.g. `#[boa(something)]`.\npub(crate) fn take_path_attr(attrs: &mut Vec<Attribute>, name: &str) -> bool {\n    if let Some((i, _)) = attrs\n        .iter()\n        .enumerate()\n        .filter(|(_, a)| a.path().is_ident(\"boa\"))\n        .filter_map(|(i, a)| a.meta.require_list().ok().map(|nv| (i, nv)))\n        .filter_map(|(i, m)| m.parse_args_with(Ident::parse_any).ok().map(|p| (i, p)))\n        .find(|(_, path)| path == name)\n    {\n        attrs.remove(i);\n        true\n    } else {\n        false\n    }\n}\n\n/// Look (and remove from AST) for a `#[boa(rename = ...)]` attribute, where `...`\n/// is a literal. The validation of the literal's type should be done separately.\npub(crate) fn take_name_value_attr(attrs: &mut Vec<Attribute>, name: &str) -> Option<Lit> {\n    if let Some((i, lit)) = attrs\n        .iter()\n        .enumerate()\n        .filter(|(_, a)| a.meta.path().is_ident(\"boa\"))\n        .filter_map(|(i, a)| a.meta.require_list().ok().map(|nv| (i, nv)))\n        .filter_map(|(i, a)| {\n            syn::parse2::<MetaNameValue>(a.tokens.to_token_stream())\n                .ok()\n                .map(|nv| (i, nv))\n        })\n        .filter(|(_, nv)| nv.path.is_ident(name))\n        .find_map(|(i, nv)| match &nv.value {\n            Expr::Lit(ExprLit { lit, .. }) => Some((i, lit.clone())),\n            _ => None,\n        })\n    {\n        attrs.remove(i);\n        Some(lit)\n    } else {\n        None\n    }\n}\n\n/// Take the length name-value from the list of attributes.\npub(crate) fn take_length_from_attrs(attrs: &mut Vec<Attribute>) -> SpannedResult<Option<usize>> {\n    match take_name_value_attr(attrs, \"length\") {\n        None => Ok(None),\n        Some(lit) => match lit {\n            Lit::Int(int) if int.base10_parse::<usize>().is_ok() => int\n                .base10_parse::<usize>()\n                .map(Some)\n                .map_err(|e| (int.span(), format!(\"Invalid literal: {e}\"))),\n            l => error(&l, \"Invalid literal type. Was expecting a number\")?,\n        },\n    }\n}\n\npub(crate) fn take_name_value_string(\n    attrs: &mut Vec<Attribute>,\n    name: &str,\n) -> SpannedResult<Option<String>> {\n    match take_name_value_attr(attrs, name) {\n        None => Ok(None),\n        Some(lit) => match lit {\n            Lit::Str(s) => Ok(Some(s.value())),\n            l => Err((\n                l.span(),\n                \"Invalid literal type. Was expecting a string\".to_string(),\n            )),\n        },\n    }\n}\n\n/// Take the last `#[boa(error = \"...\")]` statement if found, remove it from the list\n/// of attributes, and return the literal string.\npub(crate) fn take_error_from_attrs(attrs: &mut Vec<Attribute>) -> SpannedResult<Option<String>> {\n    take_name_value_string(attrs, \"error\")\n}\n\n#[derive(Copy, Clone, Debug, Default)]\npub(crate) enum RenameScheme {\n    #[default]\n    None,\n    CamelCase,\n    PascalCase,\n}\n\nimpl FromStr for RenameScheme {\n    type Err = String;\n\n    fn from_str(s: &str) -> Result<Self, Self::Err> {\n        if s.eq_ignore_ascii_case(\"none\") {\n            Ok(Self::None)\n        } else if s.eq_ignore_ascii_case(\"camelcase\") {\n            Ok(Self::CamelCase)\n        } else if s.eq_ignore_ascii_case(\"pascalcase\") {\n            Ok(Self::PascalCase)\n        } else {\n            Err(format!(\n                r#\"Invalid rename scheme: {s:?}. Accepted values are \"none\" or \"camelCase\".\"#\n            ))\n        }\n    }\n}\n\nimpl RenameScheme {\n    pub(crate) fn from_named_attrs(\n        attrs: &mut Vec<Attribute>,\n        name: &str,\n    ) -> SpannedResult<Option<Self>> {\n        match take_name_value_attr(attrs, name) {\n            None => Ok(None),\n            Some(Lit::Str(lit_str)) => Self::from_str(lit_str.value().as_str())\n                .map_err(|e| (lit_str.span(), e))\n                .map(Some),\n            Some(lit) => Err((\n                lit.span(),\n                \"Invalid attribute value literal, expected a string.\".to_string(),\n            )),\n        }\n    }\n\n    fn camel_case(s: &str) -> String {\n        #[derive(Debug, PartialEq)]\n        enum State {\n            First,\n            Middle,\n            NextOfUpper,\n            NextOfContinuedUpper(char),\n            NextOfSepMark,\n            Other,\n        }\n\n        let mut result = String::with_capacity(s.len());\n        let mut state = State::First;\n\n        for ch in s.chars() {\n            let is_upper = ch.is_ascii_uppercase();\n            let is_lower = ch.is_ascii_lowercase();\n\n            match (&state, is_upper, is_lower) {\n                (State::First | State::Middle, true, false) => {\n                    state = State::NextOfUpper;\n                    result.push(ch.to_ascii_lowercase());\n                }\n                (State::First | State::Middle, false, true) => {\n                    state = State::Other;\n                    result.push(ch);\n                }\n                (State::First, false, false) => {}\n                (State::NextOfUpper, true, false) => {\n                    state = State::NextOfContinuedUpper(ch);\n                }\n                (State::NextOfUpper, false, true) => {\n                    state = State::Middle;\n                    result.push(ch);\n                }\n                (State::NextOfContinuedUpper(last), true, false) => {\n                    result.push(last.to_ascii_lowercase());\n                    state = State::NextOfContinuedUpper(ch);\n                }\n                (State::NextOfContinuedUpper(last), false, true) => {\n                    result.push(last.to_ascii_uppercase());\n                    state = State::Middle;\n                    result.push(ch);\n                }\n                (State::NextOfContinuedUpper(last), false, false) => {\n                    result.push(last.to_ascii_lowercase());\n                    state = State::NextOfSepMark;\n                }\n                (State::NextOfSepMark, true, false) => {\n                    state = State::NextOfUpper;\n                    result.push(ch);\n                }\n                (State::NextOfSepMark, false, true) | (State::Other, true, false) => {\n                    state = State::NextOfUpper;\n                    result.push(ch.to_ascii_uppercase());\n                }\n                (State::Other, false, true) => {\n                    result.push(ch);\n                }\n                (_, false, false) => {\n                    state = State::NextOfSepMark;\n                }\n                (_, _, _) => {}\n            }\n        }\n\n        if let State::NextOfContinuedUpper(last) = state {\n            result.push(last.to_ascii_lowercase());\n        }\n\n        result\n    }\n\n    fn pascal_case(s: &str) -> String {\n        let mut result = Self::camel_case(s);\n        if let Some(ch) = result.get_mut(..1) {\n            ch.make_ascii_uppercase();\n        }\n        result\n    }\n\n    pub(crate) fn rename(self, s: String) -> String {\n        match self {\n            Self::None => s,\n            Self::CamelCase => Self::camel_case(&s),\n            Self::PascalCase => Self::pascal_case(&s),\n        }\n    }\n}\n\n#[cfg(test)]\nmod tests {\n    use super::RenameScheme;\n    use test_case::test_case;\n\n    #[rustfmt::skip]\n    #[test_case(\"HelloWorld\", \"helloWorld\" ; \"camel_case_1\")]\n    #[test_case(\"Hello_World\", \"helloWorld\" ; \"camel_case_2\")]\n    #[test_case(\"hello_world\", \"helloWorld\" ; \"camel_case_3\")]\n    #[test_case(\"__hello_world__\", \"helloWorld\" ; \"camel_case_4\")]\n    #[test_case(\"HELLOWorld\", \"helloWorld\" ; \"camel_case_5\")]\n    #[test_case(\"helloWORLD\", \"helloWorld\" ; \"camel_case_6\")]\n    #[test_case(\"HELLO_WORLD\", \"helloWorld\" ; \"camel_case_7\")]\n    #[test_case(\"hello_beautiful_world\", \"helloBeautifulWorld\" ; \"camel_case_8\")]\n    #[test_case(\"helloBeautifulWorld\", \"helloBeautifulWorld\" ; \"camel_case_9\")]\n    #[test_case(\"switch_to_term\", \"switchToTerm\" ; \"camel_case_10\")]\n    #[test_case(\"_a_b_c_\", \"aBC\" ; \"camel_case_11\")]\n    fn camel_case(input: &str, expected: &str) {\n        assert_eq!(RenameScheme::camel_case(input).as_str(), expected);\n    }\n\n    #[rustfmt::skip]\n    #[test_case(\"HelloWorld\", \"HelloWorld\" ; \"pascal_case_1\")]\n    #[test_case(\"Hello_World\", \"HelloWorld\" ; \"pascal_case_2\")]\n    #[test_case(\"hello_world\", \"HelloWorld\" ; \"pascal_case_3\")]\n    #[test_case(\"__hello_world__\", \"HelloWorld\" ; \"pascal_case_4\")]\n    #[test_case(\"HELLOWorld\", \"HelloWorld\" ; \"pascal_case_5\")]\n    #[test_case(\"helloWORLD\", \"HelloWorld\" ; \"pascal_case_6\")]\n    #[test_case(\"HELLO_WORLD\", \"HelloWorld\" ; \"pascal_case_7\")]\n    fn pascal_case(input: &str, expected: &str) {\n        assert_eq!(RenameScheme::pascal_case(input).as_str(), expected);\n    }\n}\n"
  },
  {
    "path": "core/macros/src/value.rs",
    "content": "use proc_macro2::{Span, TokenStream};\nuse quote::quote;\nuse syn::parse::{Parse, ParseStream};\nuse syn::punctuated::Punctuated;\nuse syn::token::{Brace, Bracket};\nuse syn::{Expr, Ident, LitStr, Token, braced, bracketed, parse2};\n\n/// The key can be an identifier (which will be stringified), or an actual string\n/// literal.\nenum Key {\n    Bracketed(Expr),\n    Ident(Ident),\n    StringLiteral(LitStr),\n}\n\nimpl Parse for Key {\n    fn parse(input: ParseStream<'_>) -> syn::Result<Self> {\n        if input.peek(Bracket) {\n            let content;\n            let _bracket = bracketed!(content in input);\n            Ok(Key::Bracketed(content.parse()?))\n        } else if input.peek(Ident) {\n            Ok(Key::Ident(input.parse()?))\n        } else if input.peek(LitStr) {\n            Ok(Key::StringLiteral(input.parse()?))\n        } else {\n            Err(input.error(\"Expected a field name\"))\n        }\n    }\n}\n\n/// A value, which itself can recursively be an object, an array, a literal or\n/// an expression.\nenum Value {\n    Object(Object),\n    Array(Array),\n    String(LitStr),\n    Expr(Expr),\n}\n\nimpl Value {\n    fn output(&self, context: Option<&Ident>) -> syn::Result<TokenStream> {\n        match self {\n            Value::Object(o) => o.output(context).map(|o| {\n                quote! {\n                    ::boa_engine::JsValue::from( #o )\n                }\n            }),\n            Value::Array(a) => a.output(context),\n            Value::String(str) => Ok(quote! {\n                ::boa_engine::JsValue::from( ::boa_macros::js_str!( #str ) )\n            }),\n            Value::Expr(e) => Ok(quote! {\n                ::boa_engine::JsValue::from( #e )\n            }),\n        }\n    }\n}\n\nimpl Parse for Value {\n    fn parse(input: ParseStream<'_>) -> syn::Result<Self> {\n        if input.peek(Brace) {\n            Ok(Self::Object(input.parse()?))\n        } else if input.peek(Bracket) {\n            Ok(Self::Array(input.parse()?))\n        } else if input.peek(LitStr) {\n            Ok(Self::String(input.parse()?))\n        } else {\n            Ok(Self::Expr(input.parse()?))\n        }\n    }\n}\n\n/// An object is built of multiple key-value pairs.\nstruct KeyValue {\n    key: Key,\n    _colon: Token![:],\n    value: Value,\n}\n\nimpl Parse for KeyValue {\n    fn parse(input: ParseStream<'_>) -> syn::Result<Self> {\n        Ok(Self {\n            key: input.parse()?,\n            _colon: input.parse()?,\n            value: input.parse()?,\n        })\n    }\n}\n\n/// An object declaration.\nstruct Object {\n    _brace: Brace,\n    fields: Punctuated<KeyValue, Token![,]>,\n}\n\nimpl Object {\n    fn output(&self, context: Option<&Ident>) -> syn::Result<TokenStream> {\n        let Some(c_ident) = context else {\n            return Err(syn::Error::new(\n                Span::call_site(),\n                \"Need to specify a context identifier.\",\n            ));\n        };\n\n        let fields: Vec<TokenStream> = self\n            .fields\n            .iter()\n            .map(|field| match &field.key {\n                Key::Bracketed(expr) => (\n                    quote! {\n                        ::boa_engine::property::PropertyKey::from( #expr )\n                    },\n                    &field.value,\n                ),\n                Key::Ident(ident) => {\n                    let ident = ident.to_string();\n                    (quote! { ::boa_engine::js_string!( #ident ) }, &field.value)\n                }\n                Key::StringLiteral(literal) => (\n                    quote! { ::boa_engine::js_string!( #literal ) },\n                    &field.value,\n                ),\n            })\n            .map(|(key, value)| {\n                let value = value.output(context)?;\n\n                Ok(quote! {\n                    let boa_value = {\n                        #[allow(unused)]\n                        let #c_ident = obj.context();\n                        #value\n                    };\n                    let mut obj = obj.property( #key, boa_value, boa_engine::property::Attribute::all() );\n                })\n            })\n            .collect::<syn::Result<_>>()?;\n\n        Ok(quote! {\n            {\n                let mut obj = ::boa_engine::object::ObjectInitializer::new( #c_ident );\n                {\n                    #(#fields)*\n                    obj.build()\n                }\n            }\n        })\n    }\n}\n\nimpl Parse for Object {\n    fn parse(input: ParseStream<'_>) -> syn::Result<Self> {\n        let content;\n        let brace = braced!(content in input);\n\n        Ok(Self {\n            _brace: brace,\n            fields: Punctuated::parse_terminated(&content)?,\n        })\n    }\n}\n\n/// An array declaration.\nstruct Array {\n    _bracket: Bracket,\n    elems: Punctuated<Value, Token![,]>,\n}\n\nimpl Array {\n    fn output(&self, context: Option<&Ident>) -> syn::Result<TokenStream> {\n        let items: Vec<TokenStream> = self\n            .elems\n            .iter()\n            .map(|item| item.output(context))\n            .collect::<syn::Result<_>>()?;\n\n        let Some(c_ident) = context else {\n            return Err(syn::Error::new(\n                Span::call_site(),\n                \"Need to specify a context identifier.\",\n            ));\n        };\n\n        Ok(quote! {\n            ::boa_engine::JsValue::from(\n                ::boa_engine::object::builtins::JsArray::from_iter( [ #(#items),* ], #c_ident )\n            )\n        })\n    }\n}\n\nimpl Parse for Array {\n    fn parse(input: ParseStream<'_>) -> syn::Result<Self> {\n        let content;\n        let bracket = bracketed!(content in input);\n        Ok(Self {\n            _bracket: bracket,\n            elems: Punctuated::parse_terminated(&content)?,\n        })\n    }\n}\n\n/// The result of parsing the full `js_value!()` macro arguments.\nstruct JsValue {\n    value: Value,\n    context_ident: Option<Ident>,\n}\n\nimpl JsValue {\n    fn output(&self) -> TokenStream {\n        self.value\n            .output(self.context_ident.as_ref())\n            .unwrap_or_else(|err| err.to_compile_error())\n    }\n}\n\nimpl Parse for JsValue {\n    fn parse(input: ParseStream<'_>) -> syn::Result<Self> {\n        let value = input.parse()?;\n        let mut context_ident = None;\n\n        if !input.is_empty() {\n            input.parse::<Token![,]>()?;\n            context_ident = Some(input.parse()?);\n        }\n\n        Ok(Self {\n            value,\n            context_ident,\n        })\n    }\n}\n\n/// The result of parsing the `js_object!()` arguments.\nstruct JsObject {\n    value: Object,\n    context_ident: Option<Ident>,\n}\n\nimpl JsObject {\n    fn output(&self) -> TokenStream {\n        self.value\n            .output(self.context_ident.as_ref())\n            .unwrap_or_else(|err| err.to_compile_error())\n    }\n}\n\nimpl Parse for JsObject {\n    fn parse(input: ParseStream<'_>) -> syn::Result<Self> {\n        let value = input.parse()?;\n        let mut context_ident = None;\n\n        if !input.is_empty() {\n            input.parse::<Token![,]>()?;\n            context_ident = Some(input.parse()?);\n        }\n\n        Ok(Self {\n            value,\n            context_ident,\n        })\n    }\n}\n\n#[allow(clippy::needless_pass_by_value)]\npub(crate) fn js_object_impl(input: TokenStream) -> TokenStream {\n    parse2::<JsObject>(input).map_or_else(|e| e.to_compile_error(), |v| v.output())\n}\n\n#[allow(clippy::needless_pass_by_value)]\npub(crate) fn js_value_impl(input: TokenStream) -> TokenStream {\n    parse2::<JsValue>(input).map_or_else(|e| e.to_compile_error(), |v| v.output())\n}\n"
  },
  {
    "path": "core/macros/tests/str.rs",
    "content": "//! Tests for the macros in this crate.\n\n#![allow(unused_crate_dependencies)]\n\nuse boa_macros::utf16;\n\n#[test]\nfn literal() {\n    let utf16 = utf16!(\"hello!\");\n    let manual = \"hello!\".encode_utf16().collect::<Vec<_>>();\n    assert_eq!(manual, utf16);\n}\n\n#[test]\nfn utf16() {\n    let utf16 = utf16!(\"hello!😁😁😁\");\n    let manual = \"hello!😁😁😁\".encode_utf16().collect::<Vec<_>>();\n    assert_eq!(manual, utf16);\n}\n"
  },
  {
    "path": "core/parser/ABOUT.md",
    "content": "# About Boa\n\nBoa is an open-source, experimental ECMAScript Engine written in Rust for\nlexing, parsing and executing ECMAScript/JavaScript. Currently, Boa supports some\nof the [language][boa-conformance]. More information can be viewed at [Boa's\nwebsite][boa-web].\n\nTry out the most recent release with Boa's live demo\n[playground][boa-playground].\n\n## Boa Crates\n\n- [**`boa_cli`**][cli] - Boa's CLI && REPL implementation\n- [**`boa_ast`**][ast] - Boa's ECMAScript Abstract Syntax Tree.\n- [**`boa_engine`**][engine] - Boa's implementation of ECMAScript builtin objects and execution.\n- [**`boa_gc`**][gc] - Boa's garbage collector.\n- [**`boa_icu_provider`**][icu] - Boa's ICU4X data provider.\n- [**`boa_interner`**][interner] - Boa's string interner.\n- [**`boa_macros`**][macros] - Boa's macros.\n- [**`boa_parser`**][parser] - Boa's lexer and parser.\n- [**`boa_runtime`**][runtime] - Boa's `WebAPI` features.\n- [**`boa_string`**][string] - Boa's ECMAScript string implementation.\n- [**`boa_wintertc`**][wintertc] - Boa's `WinterTC` (TC55) Minimum Common Web API implementation.\n- [**`tag_ptr`**][tag_ptr] - Utility library that enables a pointer to be associated with a tag of type `usize`.\n- [**`small_btree`**][small_btree] - Utility library that adds the `SmallBTreeMap` data structure.\n\n[boa-conformance]: https://boajs.dev/conformance\n[boa-web]: https://boajs.dev/\n[boa-playground]: https://boajs.dev/playground\n[ast]: https://docs.rs/boa_ast/latest/boa_ast/index.html\n[engine]: https://docs.rs/boa_engine/latest/boa_engine/index.html\n[gc]: https://docs.rs/boa_gc/latest/boa_gc/index.html\n[interner]: https://docs.rs/boa_interner/latest/boa_interner/index.html\n[parser]: https://docs.rs/boa_parser/latest/boa_parser/index.html\n[icu]: https://docs.rs/boa_icu_provider/latest/boa_icu_provider/index.html\n[runtime]: https://docs.rs/boa_runtime/latest/boa_runtime/index.html\n[string]: https://docs.rs/boa_string/latest/boa_string/index.html\n[wintertc]: https://docs.rs/boa_wintertc/latest/boa_wintertc/index.html\n[tag_ptr]: https://docs.rs/tag_ptr/latest/tag_ptr/index.html\n[small_btree]: https://docs.rs/small_btree/latest/small_btree/index.html\n[macros]: https://docs.rs/boa_macros/latest/boa_macros/index.html\n[cli]: https://crates.io/crates/boa_cli\n"
  },
  {
    "path": "core/parser/Cargo.toml",
    "content": "[package]\nname = \"boa_parser\"\ndescription = \"ECMAScript parser for the Boa JavaScript engine.\"\nkeywords = [\"javascript\", \"js\", \"syntax\", \"parser\"]\ncategories = [\"parser-implementations\", \"compilers\"]\nversion.workspace = true\nedition.workspace = true\nauthors.workspace = true\nlicense.workspace = true\nrepository.workspace = true\nrust-version.workspace = true\n\n[dependencies]\nboa_interner.workspace = true\nboa_macros.workspace = true\nboa_ast.workspace = true\nrustc-hash = { workspace = true, features = [\"std\"] }\nfast-float2.workspace = true\nnum-traits.workspace = true\nbitflags.workspace = true\nnum-bigint.workspace = true\nregress.workspace = true\nicu_properties.workspace = true\n\n[dev-dependencies]\nindoc.workspace = true\nstrum.workspace = true\n\n[features]\nannex-b = []\n\n[lints]\nworkspace = true\n\n[package.metadata.docs.rs]\nall-features = true\n"
  },
  {
    "path": "core/parser/src/error/mod.rs",
    "content": "//! Error and result implementation for the parser.\n\n#[cfg(test)]\nmod tests;\n\nuse crate::lexer::Error as LexError;\nuse boa_ast::{Position, Span};\nuse std::fmt;\n\n/// Result of a parsing operation.\npub type ParseResult<T> = Result<T, Error>;\n\n/// Adds context to a parser error.\npub(crate) trait ErrorContext {\n    /// Sets the context of the error, if possible.\n    fn set_context(self, context: &'static str) -> Self;\n\n    /// Gets the context of the error, if any.\n    fn context(&self) -> Option<&'static str>;\n}\n\nimpl<T> ErrorContext for ParseResult<T> {\n    fn set_context(self, context: &'static str) -> Self {\n        self.map_err(|e| e.set_context(context))\n    }\n\n    fn context(&self) -> Option<&'static str> {\n        self.as_ref().err().and_then(ErrorContext::context)\n    }\n}\n\nimpl ErrorContext for Error {\n    fn set_context(self, new_context: &'static str) -> Self {\n        match self {\n            Self::Expected {\n                expected,\n                found,\n                span,\n                ..\n            } => Self::expected(expected, found, span, new_context),\n            e => e,\n        }\n    }\n\n    fn context(&self) -> Option<&'static str> {\n        if let Self::Expected { context, .. } = self {\n            Some(context)\n        } else {\n            None\n        }\n    }\n}\n\nimpl From<LexError> for Error {\n    #[inline]\n    fn from(e: LexError) -> Self {\n        Self::lex(e)\n    }\n}\n\n/// An enum which represents errors encountered during parsing an expression\n#[derive(Debug)]\npub enum Error {\n    /// When it expected a certain kind of token, but got another as part of something\n    Expected {\n        /// The token(s) that were expected.\n        expected: Box<[String]>,\n\n        /// The token that was not expected.\n        found: Box<str>,\n\n        /// The parsing context in which the error occurred.\n        context: &'static str,\n\n        /// Position of the source code where the error occurred.\n        span: Span,\n    },\n\n    /// When a token is unexpected\n    Unexpected {\n        /// The error message.\n        message: Box<str>,\n\n        /// The token that was not expected.\n        found: Box<str>,\n\n        /// Position of the source code where the error occurred.\n        span: Span,\n    },\n\n    /// When there is an abrupt end to the parsing\n    AbruptEnd,\n\n    /// A lexing error.\n    Lex {\n        /// The error that occurred during lexing.\n        err: LexError,\n    },\n\n    /// A scope analysis error.\n    ScopeAnalysis {\n        /// The error that occurred during scope analysis.\n        err: &'static str,\n    },\n\n    /// Catch all General Error\n    General {\n        /// The error message.\n        message: Box<str>,\n\n        /// Position of the source code where the error occurred.\n        position: Position,\n    },\n}\n\nimpl Error {\n    /// Creates an `Expected` parsing error.\n    pub(crate) fn expected<E, F>(expected: E, found: F, span: Span, context: &'static str) -> Self\n    where\n        E: Into<Box<[String]>>,\n        F: Into<Box<str>>,\n    {\n        let expected = expected.into();\n        debug_assert_ne!(expected.len(), 0);\n\n        Self::Expected {\n            expected,\n            found: found.into(),\n            span,\n            context,\n        }\n    }\n\n    /// Creates an `Unexpected` parsing error.\n    pub(crate) fn unexpected<F, C>(found: F, span: Span, message: C) -> Self\n    where\n        F: Into<Box<str>>,\n        C: Into<Box<str>>,\n    {\n        Self::Unexpected {\n            found: found.into(),\n            span,\n            message: message.into(),\n        }\n    }\n\n    /// Creates a `ScopeAnalysis` parsing error.\n    pub(crate) fn scope_analysis(err: &'static str) -> Self {\n        Self::ScopeAnalysis { err }\n    }\n\n    /// Creates a \"general\" parsing error.\n    pub(crate) fn general<S, P>(message: S, position: P) -> Self\n    where\n        S: Into<Box<str>>,\n        P: Into<Position>,\n    {\n        Self::General {\n            message: message.into(),\n            position: position.into(),\n        }\n    }\n\n    /// Creates a \"general\" parsing error with the specific error message for a misplaced function declaration.\n    pub(crate) fn misplaced_function_declaration(position: Position, strict: bool) -> Self {\n        Self::General {\n            message: format!(\n                \"{}functions can only be declared at the top level or inside a block.\",\n                if strict { \"in strict mode code, \" } else { \"\" }\n            )\n            .into(),\n            position,\n        }\n    }\n\n    /// Creates a \"general\" parsing error with the specific error message for a wrong function declaration with label.\n    pub(crate) fn wrong_labelled_function_declaration(position: Position) -> Self {\n        Self::General {\n            message: \"labelled functions can only be declared at the top level or inside a block\"\n                .into(),\n            position,\n        }\n    }\n\n    /// Creates a parsing error from a lexing error.\n    pub(crate) const fn lex(e: LexError) -> Self {\n        Self::Lex { err: e }\n    }\n}\n\nimpl fmt::Display for Error {\n    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {\n        match self {\n            Self::Expected {\n                expected,\n                found,\n                span,\n                ..\n            } => {\n                write!(f, \"expected \")?;\n                match &**expected {\n                    [single] => write!(f, \"token '{single}'\")?,\n                    expected => {\n                        write!(f, \"one of \")?;\n                        for (i, token) in expected.iter().enumerate() {\n                            let prefix = if i == 0 {\n                                \"\"\n                            } else if i == expected.len() - 1 {\n                                \" or \"\n                            } else {\n                                \", \"\n                            };\n                            write!(f, \"{prefix}'{token}'\")?;\n                        }\n                    }\n                }\n                if let Some(context) = self.context() {\n                    write!(\n                        f,\n                        \", got '{found}' in {context} at line {}, col {}\",\n                        span.start().line_number(),\n                        span.start().column_number()\n                    )\n                } else {\n                    write!(\n                        f,\n                        \", got '{found}' at line {}, col {}\",\n                        span.start().line_number(),\n                        span.start().column_number()\n                    )\n                }\n            }\n            Self::Unexpected {\n                found,\n                span,\n                message,\n            } => write!(\n                f,\n                \"unexpected token '{found}', {message} at line {}, col {}\",\n                span.start().line_number(),\n                span.start().column_number()\n            ),\n            Self::AbruptEnd => f.write_str(\"abrupt end\"),\n            Self::General { message, position } => write!(\n                f,\n                \"{message} at line {}, col {}\",\n                position.line_number(),\n                position.column_number()\n            ),\n            Self::Lex { err } => err.fmt(f),\n            Self::ScopeAnalysis { err } => write!(f, \"invalid scope analysis: {err}\"),\n        }\n    }\n}\n\nimpl std::error::Error for Error {}\n"
  },
  {
    "path": "core/parser/src/error/tests.rs",
    "content": "use super::*;\n\n#[test]\nfn context() {\n    let result: ParseResult<String> = ParseResult::Err(Error::expected(\n        [\"testing\".to_owned()],\n        \"nottesting\",\n        Span::new(Position::new(1, 1), Position::new(1, 1)),\n        \"before\",\n    ));\n\n    assert_eq!(result.context(), Some(\"before\"));\n\n    let result = result.set_context(\"after\");\n\n    assert_eq!(result.context(), Some(\"after\"));\n\n    let error = result.unwrap_err();\n    if let Error::Expected {\n        expected,\n        found,\n        span,\n        context,\n    } = error\n    {\n        assert_eq!(expected.as_ref(), &[\"testing\".to_owned()]);\n        assert_eq!(found, \"nottesting\".into());\n        assert_eq!(span, Span::new(Position::new(1, 1), Position::new(1, 1)));\n        assert_eq!(context, \"after\");\n    } else {\n        unreachable!();\n    }\n\n    let err = Error::AbruptEnd;\n    assert!(err.context().is_none());\n    let err = err.set_context(\"ignored\");\n    assert!(err.context().is_none());\n}\n\n#[test]\nfn from_lex_error() {\n    let lex_err = LexError::syntax(\"testing\", Position::new(1, 1));\n    let parse_err: Error = lex_err.into();\n\n    assert!(matches!(parse_err, Error::Lex { .. }));\n\n    let lex_err = LexError::syntax(\"testing\", Position::new(1, 1));\n    let parse_err = Error::lex(lex_err);\n\n    assert!(matches!(parse_err, Error::Lex { .. }));\n}\n\n#[test]\nfn misplaced_function_declaration() {\n    let err = Error::misplaced_function_declaration(Position::new(1, 1), false);\n    if let Error::General { message, position } = err {\n        assert_eq!(\n            message.as_ref(),\n            \"functions can only be declared at the top level or inside a block.\"\n        );\n        assert_eq!(position, Position::new(1, 1));\n    } else {\n        unreachable!()\n    }\n\n    let err = Error::misplaced_function_declaration(Position::new(1, 1), true);\n    if let Error::General { message, position } = err {\n        assert_eq!(\n            message.as_ref(),\n            \"in strict mode code, functions can only be declared at the top level or inside a block.\"\n        );\n        assert_eq!(position, Position::new(1, 1));\n    } else {\n        unreachable!()\n    }\n}\n\n#[test]\nfn wrong_labelled_function_declaration() {\n    let err = Error::wrong_labelled_function_declaration(Position::new(1, 1));\n    if let Error::General { message, position } = err {\n        assert_eq!(\n            message.as_ref(),\n            \"labelled functions can only be declared at the top level or inside a block\"\n        );\n        assert_eq!(position, Position::new(1, 1));\n    } else {\n        unreachable!()\n    }\n}\n\n#[test]\nfn display() {\n    let err = Error::expected(\n        [\"testing\".to_owned()],\n        \"nottesting\",\n        Span::new(Position::new(1, 1), Position::new(1, 1)),\n        \"context\",\n    );\n    assert_eq!(\n        err.to_string(),\n        \"expected token 'testing', got 'nottesting' in context at line 1, col 1\"\n    );\n\n    let err = Error::expected(\n        [\"testing\".to_owned(), \"more\".to_owned()],\n        \"nottesting\",\n        Span::new(Position::new(1, 1), Position::new(1, 3)),\n        \"context\",\n    );\n    assert_eq!(\n        err.to_string(),\n        \"expected one of 'testing' or 'more', got 'nottesting' in context at line 1, col 1\"\n    );\n\n    let err = Error::expected(\n        [\"testing\".to_owned(), \"more\".to_owned(), \"tokens\".to_owned()],\n        \"nottesting\",\n        Span::new(Position::new(1, 1), Position::new(1, 3)),\n        \"context\",\n    );\n    assert_eq!(\n        err.to_string(),\n        \"expected one of 'testing', 'more' or 'tokens', got 'nottesting' in context at line 1, col 1\"\n    );\n\n    let err = Error::expected(\n        [\n            \"testing\".to_owned(),\n            \"more\".to_owned(),\n            \"tokens\".to_owned(),\n            \"extra\".to_owned(),\n        ],\n        \"nottesting\",\n        Span::new(Position::new(1, 1), Position::new(1, 3)),\n        \"context\",\n    );\n    assert_eq!(\n        err.to_string(),\n        \"expected one of 'testing', 'more', 'tokens' or 'extra', got 'nottesting' in context at line 1, col 1\"\n    );\n\n    let err = Error::unexpected(\n        \"nottesting\",\n        Span::new(Position::new(1, 1), Position::new(1, 3)),\n        \"error message\",\n    );\n    assert_eq!(\n        err.to_string(),\n        \"unexpected token 'nottesting', error message at line 1, col 1\"\n    );\n\n    let err = Error::general(\"this is a general error message\", Position::new(1, 1));\n    assert_eq!(\n        err.to_string(),\n        \"this is a general error message at line 1, col 1\"\n    );\n\n    let err = Error::AbruptEnd;\n    assert_eq!(err.to_string(), \"abrupt end\");\n\n    let lex_err = LexError::syntax(\"testing\", Position::new(1, 1));\n    let err = Error::lex(lex_err);\n\n    assert_eq!(err.to_string(), \"testing at line 1, col 1\");\n}\n"
  },
  {
    "path": "core/parser/src/lexer/comment.rs",
    "content": "//! Boa's lexing for ECMAScript comments.\n\nuse crate::lexer::{Cursor, Error, Token, TokenKind, Tokenizer};\nuse crate::source::ReadChar;\nuse boa_ast::PositionGroup;\nuse boa_interner::Interner;\n\n/// Lexes a single line comment.\n///\n/// Assumes that the initial '//' is already consumed.\n///\n/// More information:\n///  - [ECMAScript reference][spec]\n///  - [MDN documentation][mdn]\n///\n/// [spec]: https://tc39.es/ecma262/#sec-comments\n/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Lexical_grammar\npub(super) struct SingleLineComment;\n\nimpl<R> Tokenizer<R> for SingleLineComment {\n    fn lex(\n        &mut self,\n        cursor: &mut Cursor<R>,\n        start_pos: PositionGroup,\n        _interner: &mut Interner,\n    ) -> Result<Token, Error>\n    where\n        R: ReadChar,\n    {\n        // Skip either to the end of the line or to the end of the input\n        while let Some(ch) = cursor.peek_char()? {\n            let tried_ch = char::try_from(ch);\n            match tried_ch {\n                Ok(c) if c == '\\r' || c == '\\n' || c == '\\u{2028}' || c == '\\u{2029}' => break,\n                _ => {}\n            }\n            cursor.next_char().expect(\"Comment character vanished\");\n        }\n        Ok(Token::new_by_position_group(\n            TokenKind::Comment,\n            start_pos,\n            cursor.pos_group(),\n        ))\n    }\n}\n\n/// Lexes a block (multi-line) comment.\n///\n/// Assumes that the initial '/*' is already consumed.\n///\n/// More information:\n///  - [ECMAScript reference][spec]\n///  - [MDN documentation][mdn]\n///\n/// [spec]: https://tc39.es/ecma262/#sec-comments\n/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Lexical_grammar\npub(super) struct MultiLineComment;\n\nimpl<R> Tokenizer<R> for MultiLineComment {\n    fn lex(\n        &mut self,\n        cursor: &mut Cursor<R>,\n        start_pos: PositionGroup,\n        _interner: &mut Interner,\n    ) -> Result<Token, Error>\n    where\n        R: ReadChar,\n    {\n        let mut new_line = false;\n        while let Some(ch) = cursor.next_char()? {\n            let tried_ch = char::try_from(ch);\n            match tried_ch {\n                Ok(c) if c == '*' && cursor.next_if(0x2F /* / */)? => {\n                    return Ok(Token::new_by_position_group(\n                        if new_line {\n                            TokenKind::LineTerminator\n                        } else {\n                            TokenKind::Comment\n                        },\n                        start_pos,\n                        cursor.pos_group(),\n                    ));\n                }\n                Ok(c) if c == '\\r' || c == '\\n' || c == '\\u{2028}' || c == '\\u{2029}' => {\n                    new_line = true;\n                }\n                _ => {}\n            }\n        }\n\n        Err(Error::syntax(\n            \"unterminated multiline comment\",\n            cursor.pos(),\n        ))\n    }\n}\n\n/// Lexes a first line Hashbang comment\n///\n/// More information:\n///  - [ECMAScript reference][spec]\n///\n/// [spec]: https://tc39.es/ecma262/#sec-ecmascript-language-lexical-grammar\npub(super) struct HashbangComment;\n\nimpl<R> Tokenizer<R> for HashbangComment {\n    fn lex(\n        &mut self,\n        cursor: &mut Cursor<R>,\n        start_pos: PositionGroup,\n        _interner: &mut Interner,\n    ) -> Result<Token, Error>\n    where\n        R: ReadChar,\n    {\n        while let Some(ch) = cursor.next_char()? {\n            let tried_ch = char::try_from(ch);\n            match tried_ch {\n                Ok(c) if c == '\\r' || c == '\\n' || c == '\\u{2028}' || c == '\\u{2029}' => break,\n                _ => {}\n            }\n        }\n\n        Ok(Token::new_by_position_group(\n            TokenKind::Comment,\n            start_pos,\n            cursor.pos_group(),\n        ))\n    }\n}\n"
  },
  {
    "path": "core/parser/src/lexer/cursor.rs",
    "content": "//! Boa's lexer cursor that manages the input byte stream.\n\nuse crate::source::{ReadChar, UTF8Input};\nuse boa_ast::{LinearPosition, Position, PositionGroup, SourceText};\nuse std::io::{self, Error, ErrorKind};\n\n/// Cursor over the source code.\n#[derive(Debug)]\npub(super) struct Cursor<R> {\n    iter: R,\n    pos: Position,\n    module: bool,\n    strict: bool,\n    peeked: [Option<u32>; 4],\n    source_collector: SourceText,\n}\n\nimpl<R> Cursor<R> {\n    /// Gets the current position of the cursor in the source code.\n    #[inline]\n    pub(super) fn pos_group(&self) -> PositionGroup {\n        PositionGroup::new(self.pos, self.linear_pos())\n    }\n\n    /// Gets the current position of the cursor in the source code.\n    #[inline]\n    pub(super) const fn pos(&self) -> Position {\n        self.pos\n    }\n\n    /// Gets the current linear position of the cursor in the source code.\n    #[inline]\n    pub(super) fn linear_pos(&self) -> LinearPosition {\n        self.source_collector.cur_linear_position()\n    }\n\n    pub(super) fn take_source(&mut self) -> SourceText {\n        let replace_with = SourceText::with_capacity(0);\n        std::mem::replace(&mut self.source_collector, replace_with)\n    }\n\n    /// Advances the position to the next column.\n    fn next_column(&mut self) {\n        let current_line = self.pos.line_number();\n        let next_column = self.pos.column_number() + 1;\n        self.pos = Position::new(current_line, next_column);\n    }\n\n    /// Advances the position to the next line.\n    fn next_line(&mut self) {\n        let next_line = self.pos.line_number() + 1;\n        self.pos = Position::new(next_line, 1);\n    }\n\n    /// Returns if strict mode is currently active.\n    pub(super) const fn strict(&self) -> bool {\n        self.strict\n    }\n\n    /// Sets the current strict mode.\n    pub(super) fn set_strict(&mut self, strict: bool) {\n        self.strict = strict;\n    }\n\n    /// Returns if the module mode is currently active.\n    pub(super) const fn module(&self) -> bool {\n        self.module\n    }\n\n    /// Sets the current goal symbol to module.\n    pub(super) fn set_module(&mut self, module: bool) {\n        self.module = module;\n        self.strict = module;\n    }\n}\n\nimpl<R: ReadChar> Cursor<R> {\n    /// Creates a new Lexer cursor.\n    pub(super) fn new(inner: R) -> Self {\n        Self {\n            iter: inner,\n            pos: Position::new(1, 1),\n            strict: false,\n            module: false,\n            peeked: [None; 4],\n            source_collector: SourceText::default(),\n        }\n    }\n\n    /// Peeks the next n bytes, the maximum number of peeked bytes is 4 (n <= 4).\n    pub(super) fn peek_n(&mut self, n: u8) -> Result<&[Option<u32>; 4], Error> {\n        let peeked = self.peeked.iter().filter(|c| c.is_some()).count();\n        let needs_peek = n as usize - peeked;\n\n        for i in 0..needs_peek {\n            let next = self.iter.next_char()?;\n            self.peeked[i + peeked] = next;\n        }\n\n        Ok(&self.peeked)\n    }\n\n    /// Peeks the next UTF-8 character in u32 code point.\n    pub(super) fn peek_char(&mut self) -> Result<Option<u32>, Error> {\n        if let Some(c) = self.peeked[0] {\n            return Ok(Some(c));\n        }\n\n        let next = self.iter.next_char()?;\n        self.peeked[0] = next;\n        Ok(next)\n    }\n\n    pub(super) fn next_if(&mut self, c: u32) -> io::Result<bool> {\n        if self.peek_char()? == Some(c) {\n            self.next_char()?;\n            Ok(true)\n        } else {\n            Ok(false)\n        }\n    }\n\n    /// Applies the predicate to the next character and returns the result.\n    /// Returns false if the next character is not a valid ascii or there is no next character.\n    /// Otherwise returns the result from the predicate on the ascii in char\n    ///\n    /// The buffer is not incremented.\n    pub(super) fn next_is_ascii_pred<F>(&mut self, pred: &F) -> io::Result<bool>\n    where\n        F: Fn(char) -> bool,\n    {\n        Ok(match self.peek_char()? {\n            Some(byte) if (0..=0x7F).contains(&byte) =>\n            {\n                #[allow(clippy::cast_possible_truncation)]\n                pred(char::from(byte as u8))\n            }\n            Some(_) | None => false,\n        })\n    }\n\n    /// Fills the buffer with all bytes until the stop byte is found.\n    /// Returns error when reaching the end of the buffer.\n    ///\n    /// Note that all bytes up until the stop byte are added to the buffer, including the byte right before.\n    pub(super) fn take_until(&mut self, stop: u32, buf: &mut Vec<u32>) -> io::Result<()> {\n        loop {\n            if self.next_if(stop)? {\n                return Ok(());\n            } else if let Some(c) = self.next_char()? {\n                buf.push(c);\n            } else {\n                return Err(Error::new(\n                    ErrorKind::UnexpectedEof,\n                    format!(\"Unexpected end of file when looking for character {stop}\"),\n                ));\n            }\n        }\n    }\n\n    /// Fills a mutable slice up to the ends while characters are alphabetic. Returns\n    /// the number of characters read, or `N+1` if the buffer was filled but there were\n    /// still characters after.\n    pub(super) fn take_array_alphabetic<const N: usize>(\n        &mut self,\n        arr: &mut [u32; N],\n    ) -> io::Result<usize> {\n        for (i, out) in arr.iter_mut().enumerate() {\n            match self.peek_char()? {\n                // A..Z | a..z\n                Some(0x41..=0x5A | 0x61..=0x7A) => {\n                    *out = self.next_char()?.expect(\"Already checked.\");\n                }\n                _ => return Ok(i),\n            }\n        }\n        // Check the next character and return N+1 if it's alphabetic.\n        match self.peek_char() {\n            // A..Z | a..z\n            Ok(Some(0x41..=0x5A | 0x61..=0x7A)) => Ok(N + 1),\n            _ => Ok(N),\n        }\n    }\n\n    /// Retrieves the next UTF-8 character.\n    pub(crate) fn next_char(&mut self) -> Result<Option<u32>, Error> {\n        let ch = if let Some(c) = self.peeked[0] {\n            self.peeked[0] = None;\n            self.peeked.rotate_left(1);\n            Some(c)\n        } else {\n            self.iter.next_char()?\n        };\n\n        if let Some(ch) = ch {\n            self.source_collector.collect_code_point(ch);\n        }\n\n        match ch {\n            Some(0xD) => {\n                // Try to take a newline if it's next, for windows \"\\r\\n\" newlines\n                // Otherwise, treat as a Mac OS9 bare '\\r' newline\n                if self.peek_char()? == Some(0xA) {\n                    self.peeked[0] = None;\n                    self.peeked.rotate_left(1);\n                    self.source_collector.collect_code_point(0xA);\n                }\n                self.next_line();\n            }\n            // '\\n' | '\\u{2028}' | '\\u{2029}'\n            Some(0xA | 0x2028 | 0x2029) => self.next_line(),\n            Some(_) => self.next_column(),\n            _ => {}\n        }\n\n        Ok(ch)\n    }\n}\n\nimpl<'a> From<&'a [u8]> for Cursor<UTF8Input<&'a [u8]>> {\n    fn from(input: &'a [u8]) -> Self {\n        Self::new(UTF8Input::new(input))\n    }\n}\n"
  },
  {
    "path": "core/parser/src/lexer/error.rs",
    "content": "//! This module contains the errors used by the lexer.\n//!\n//! More information:\n//!  - [ECMAScript reference][spec]\n//!\n//! [spec]: https://tc39.es/ecma262/#sec-native-error-types-used-in-this-standard\n\nuse boa_ast::Position;\nuse std::{error, fmt, io};\n\n/// An error that occurred during the lexing.\n#[derive(Debug)]\npub enum Error {\n    /// An IO error is raised to indicate an issue when the lexer is reading data that isn't\n    /// related to the sourcecode itself.\n    IO(io::Error),\n\n    /// Indicates a parsing error due to the presence, or lack of, one or more characters.\n    ///\n    /// More information:\n    /// - [ECMAScript reference][spec]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-native-error-types-used-in-this-standard-syntaxerror\n    Syntax(Box<str>, Position),\n}\n\nimpl From<io::Error> for Error {\n    #[inline]\n    fn from(err: io::Error) -> Self {\n        Self::IO(err)\n    }\n}\n\nimpl Error {\n    /// Creates a new syntax error.\n    #[inline]\n    pub(crate) fn syntax<M, P>(err: M, pos: P) -> Self\n    where\n        M: Into<Box<str>>,\n        P: Into<Position>,\n    {\n        Self::Syntax(err.into(), pos.into())\n    }\n\n    /// Creates an \"unexpected\" syntax error (found X, with message).\n    #[inline]\n    pub(crate) fn unexpected<F, M, P>(found: F, message: M, pos: P) -> Self\n    where\n        F: fmt::Display,\n        M: Into<Box<str>>,\n        P: Into<Position>,\n    {\n        Self::Syntax(\n            format!(\"unexpected {found}, {}\", message.into().as_ref()).into(),\n            pos.into(),\n        )\n    }\n}\n\nimpl fmt::Display for Error {\n    #[inline]\n    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {\n        match self {\n            Self::IO(e) => e.fmt(f),\n            Self::Syntax(e, pos) => write!(\n                f,\n                \"{e} at line {}, col {}\",\n                pos.line_number(),\n                pos.column_number()\n            ),\n        }\n    }\n}\n\nimpl error::Error for Error {\n    #[inline]\n    fn source(&self) -> Option<&(dyn error::Error + 'static)> {\n        match self {\n            Self::IO(err) => Some(err),\n            Self::Syntax(_, _) => None,\n        }\n    }\n}\n\n#[cfg(test)]\nmod tests {\n    use super::*;\n    use std::{error::Error as _, io};\n\n    #[test]\n    fn syntax() {\n        let err = Error::syntax(\"testing\", Position::new(1, 1));\n        if let Error::Syntax(err, pos) = err {\n            assert_eq!(err.as_ref(), \"testing\");\n            assert_eq!(pos, Position::new(1, 1));\n        } else {\n            unreachable!()\n        }\n\n        let err = Error::syntax(\"testing\", Position::new(1, 1));\n        assert_eq!(err.to_string(), \"testing at line 1, col 1\");\n        assert!(err.source().is_none());\n    }\n\n    #[test]\n    fn io() {\n        let custom_error = io::Error::other(\"I/O error\");\n        let err = custom_error.into();\n        if let Error::IO(err) = err {\n            assert_eq!(err.to_string(), \"I/O error\");\n        } else {\n            unreachable!()\n        }\n\n        let custom_error = io::Error::other(\"I/O error\");\n        let err: Error = custom_error.into();\n        assert_eq!(err.to_string(), \"I/O error\");\n        err.source().map_or_else(\n            || unreachable!(),\n            |io_err| {\n                assert_eq!(io_err.to_string(), \"I/O error\");\n            },\n        );\n    }\n}\n"
  },
  {
    "path": "core/parser/src/lexer/identifier.rs",
    "content": "//! This module implements lexing for identifiers (foo, myvar, etc.) used in ECMAScript.\n\nuse crate::lexer::{\n    Cursor, Error, StringLiteral, Token, TokenKind, Tokenizer, token::ContainsEscapeSequence,\n};\nuse crate::source::ReadChar;\nuse boa_ast::PositionGroup;\nuse boa_interner::Interner;\nuse icu_properties::props::{IdContinue, IdStart};\nuse icu_properties::{CodePointSetData, CodePointSetDataBorrowed};\n/// Identifier lexing.\n///\n/// More information:\n///  - [ECMAScript reference][spec]\n///  - [MDN documentation][mdn]\n///\n/// [spec]: https://tc39.es/ecma262/#prod-Identifier\n/// [mdn]: https://developer.mozilla.org/en-US/docs/Glossary/Identifier\n#[derive(Debug, Clone, Copy)]\npub(super) struct Identifier {\n    init: char,\n}\n\nimpl Identifier {\n    /// Creates a new identifier/keyword lexer.\n    pub(super) const fn new(init: char) -> Self {\n        Self { init }\n    }\n\n    /// Checks if a character is `IdentifierStart` as per ECMAScript standards.\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-names-and-keywords\n    pub(super) fn is_identifier_start(ch: u32) -> bool {\n        const ID_START: CodePointSetDataBorrowed<'static> = CodePointSetData::new::<IdStart>();\n        matches!(ch, 0x0024 /* $ */ | 0x005F /* _ */) || ID_START.contains32(ch)\n    }\n\n    /// Checks if a character is `IdentifierPart` as per ECMAScript standards.\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-names-and-keywords\n    fn is_identifier_part(ch: u32) -> bool {\n        const ID_CONTINUE: CodePointSetDataBorrowed<'static> =\n            CodePointSetData::new::<IdContinue>();\n        matches!(\n            ch,\n            0x0024 /* $ */ | 0x005F /* _ */ | 0x200C /* <ZWNJ> */ | 0x200D /* <ZWJ> */\n        ) || ID_CONTINUE.contains32(ch)\n    }\n}\n\nimpl<R> Tokenizer<R> for Identifier {\n    fn lex(\n        &mut self,\n        cursor: &mut Cursor<R>,\n        start_pos: PositionGroup,\n        interner: &mut Interner,\n    ) -> Result<Token, Error>\n    where\n        R: ReadChar,\n    {\n        let (identifier_name, contains_escaped_chars) =\n            Self::take_identifier_name(cursor, start_pos, self.init)?;\n\n        let token_kind = match identifier_name.parse() {\n            Ok(keyword) => TokenKind::Keyword((keyword, contains_escaped_chars)),\n            Err(_) if identifier_name == \"true\" => {\n                TokenKind::BooleanLiteral((true, ContainsEscapeSequence(contains_escaped_chars)))\n            }\n            Err(_) if identifier_name == \"false\" => {\n                TokenKind::BooleanLiteral((false, ContainsEscapeSequence(contains_escaped_chars)))\n            }\n            Err(_) if identifier_name == \"null\" => {\n                TokenKind::NullLiteral(ContainsEscapeSequence(contains_escaped_chars))\n            }\n            Err(_) => TokenKind::IdentifierName((\n                interner.get_or_intern(identifier_name.as_str()),\n                ContainsEscapeSequence(contains_escaped_chars),\n            )),\n        };\n\n        Ok(Token::new_by_position_group(\n            token_kind,\n            start_pos,\n            cursor.pos_group(),\n        ))\n    }\n}\n\nimpl Identifier {\n    pub(super) fn take_identifier_name<R>(\n        cursor: &mut Cursor<R>,\n        start_pos: PositionGroup,\n        init: char,\n    ) -> Result<(String, bool), Error>\n    where\n        R: ReadChar,\n    {\n        let mut contains_escaped_chars = false;\n        let mut identifier_name = if init == '\\\\' && cursor.next_if(0x75 /* u */)? {\n            let ch = StringLiteral::take_unicode_escape_sequence(cursor, start_pos.position())?;\n\n            if Self::is_identifier_start(ch) {\n                contains_escaped_chars = true;\n                String::from(\n                    char::try_from(ch)\n                        .expect(\"all identifier starts must be convertible to strings\"),\n                )\n            } else {\n                return Err(Error::syntax(\"invalid identifier start\", start_pos));\n            }\n        } else {\n            // The caller guarantees that `init` is a valid identifier start\n            String::from(init)\n        };\n\n        loop {\n            let ch = match cursor.peek_char()? {\n                Some(0x005C /* \\ */) if cursor.peek_n(2)?[1] == Some(0x75) /* u */ => {\n                    let pos = cursor.pos();\n                    let _next = cursor.next_char();\n                    let _next = cursor.next_char();\n                    let ch = StringLiteral::take_unicode_escape_sequence(cursor, pos)?;\n\n                    if Self::is_identifier_part(ch) {\n                        contains_escaped_chars = true;\n                        ch\n                    } else {\n                        return Err(Error::Syntax(\"invalid identifier part\".into(), pos));\n                    }\n                }\n                Some(ch) if Self::is_identifier_part(ch) => {\n                    cursor.next_char()?;\n                    ch\n                },\n                _ => break,\n            };\n\n            identifier_name.push(char::try_from(ch).expect(\"checked character value\"));\n        }\n\n        Ok((identifier_name, contains_escaped_chars))\n    }\n}\n"
  },
  {
    "path": "core/parser/src/lexer/mod.rs",
    "content": "//! Boa's lexical analyzer(Lexer) for ECMAScript source code.\n//!\n//! The Lexer splits its input source code into a sequence of input elements called tokens,\n//! represented by the [Token] structure. It also removes\n//! whitespace and comments and attaches them to the next token.\n//!\n//! This is tightly coupled with the parser due to the javascript goal-symbol requirements\n//! as documented by the spec.\n//!\n//! More information:\n//!  - [ECMAScript reference][spec]\n//!\n//! [spec]: https://tc39.es/ecma262/#sec-ecmascript-language-lexical-grammar\n\npub mod error;\npub mod regex;\npub mod token;\n\nmod comment;\nmod cursor;\nmod identifier;\nmod number;\nmod operator;\nmod private_identifier;\nmod spread;\nmod string;\nmod template;\n\n#[cfg(test)]\nmod tests;\n\nuse self::{\n    comment::{HashbangComment, MultiLineComment, SingleLineComment},\n    cursor::Cursor,\n    identifier::Identifier,\n    number::NumberLiteral,\n    operator::Operator,\n    private_identifier::PrivateIdentifier,\n    regex::RegexLiteral,\n    spread::SpreadLiteral,\n    string::StringLiteral,\n    template::TemplateLiteral,\n};\nuse crate::source::{ReadChar, UTF8Input};\nuse boa_ast::{PositionGroup, Punctuator};\nuse boa_interner::Interner;\n\npub use self::{\n    error::Error,\n    token::{Token, TokenKind},\n};\n\ntrait Tokenizer<R> {\n    /// Lexes the next token.\n    fn lex(\n        &mut self,\n        cursor: &mut Cursor<R>,\n        start_pos: PositionGroup,\n        interner: &mut Interner,\n    ) -> Result<Token, Error>\n    where\n        R: ReadChar;\n}\n\n/// Lexer or tokenizer for the Boa JavaScript Engine.\n#[derive(Debug)]\npub struct Lexer<R> {\n    cursor: Cursor<R>,\n    goal_symbol: InputElement,\n}\n\nimpl<R> Lexer<R> {\n    /// Sets the goal symbol for the lexer.\n    pub(crate) fn set_goal(&mut self, elm: InputElement) {\n        self.goal_symbol = elm;\n    }\n\n    /// Gets the goal symbol the lexer is currently using.\n    pub(crate) const fn get_goal(&self) -> InputElement {\n        self.goal_symbol\n    }\n\n    /// Returns if strict mode is currently active.\n    pub(super) const fn strict(&self) -> bool {\n        self.cursor.strict()\n    }\n\n    /// Sets the current strict mode.\n    pub(super) fn set_strict(&mut self, strict: bool) {\n        self.cursor.set_strict(strict);\n    }\n\n    /// Returns if module mode is currently active.\n    pub(super) const fn module(&self) -> bool {\n        self.cursor.module()\n    }\n\n    /// Signals that the goal symbol is a module\n    pub(super) fn set_module(&mut self, module: bool) {\n        self.cursor.set_module(module);\n    }\n\n    /// Creates a new lexer.\n    pub fn new(reader: R) -> Self\n    where\n        R: ReadChar,\n    {\n        Self {\n            cursor: Cursor::new(reader),\n            goal_symbol: InputElement::default(),\n        }\n    }\n\n    /// Handles lexing of a token starting '/' with the '/' already being consumed.\n    /// This could be a divide symbol or the start of a regex.\n    ///\n    /// If `init_with_eq` is `true`, assume that '/=' has already been consumed.\n    ///\n    /// A '/' symbol can always be a comment but if as tested above it is not then\n    /// that means it could be multiple different tokens depending on the input token.\n    ///\n    /// As per <https://tc39.es/ecma262/#sec-ecmascript-language-lexical-grammar>\n    pub(crate) fn lex_slash_token(\n        &mut self,\n        start: PositionGroup,\n        interner: &mut Interner,\n        init_with_eq: bool,\n    ) -> Result<Token, Error>\n    where\n        R: ReadChar,\n    {\n        if let Some(c) = self.cursor.peek_char()? {\n            match (c, init_with_eq) {\n                // /\n                (0x002F, false) => {\n                    self.cursor.next_char()?.expect(\"/ token vanished\"); // Consume the '/'\n                    SingleLineComment.lex(&mut self.cursor, start, interner)\n                }\n                // *\n                (0x002A, false) => {\n                    self.cursor.next_char()?.expect(\"* token vanished\"); // Consume the '*'\n                    MultiLineComment.lex(&mut self.cursor, start, interner)\n                }\n                (ch, init_with_eq) => {\n                    match self.get_goal() {\n                        InputElement::Div | InputElement::TemplateTail => {\n                            // Only div punctuator allowed, regex not.\n\n                            // =\n                            if init_with_eq || ch == 0x003D {\n                                // if `=` is not consumed, consume it\n                                if !init_with_eq {\n                                    // Indicates this is an AssignDiv.\n                                    // Consume the '='\n                                    self.cursor.next_char()?.expect(\"= token vanished\");\n                                }\n                                Ok(Token::new_by_position_group(\n                                    Punctuator::AssignDiv.into(),\n                                    start,\n                                    self.cursor.pos_group(),\n                                ))\n                            } else {\n                                Ok(Token::new_by_position_group(\n                                    Punctuator::Div.into(),\n                                    start,\n                                    self.cursor.pos_group(),\n                                ))\n                            }\n                        }\n                        InputElement::RegExp | InputElement::HashbangOrRegExp => {\n                            // Can be a regular expression.\n                            RegexLiteral::new(init_with_eq).lex(&mut self.cursor, start, interner)\n                        }\n                    }\n                }\n            }\n        } else {\n            Ok(Token::new_by_position_group(\n                Punctuator::Div.into(),\n                start,\n                self.cursor.pos_group(),\n            ))\n        }\n    }\n\n    /// Skips an HTML close comment (`-->`) if the `annex-b` feature is enabled.\n    pub(crate) fn skip_html_close(&mut self, interner: &mut Interner) -> Result<(), Error>\n    where\n        R: ReadChar,\n    {\n        if cfg!(not(feature = \"annex-b\")) || self.module() {\n            return Ok(());\n        }\n\n        while self.cursor.peek_char()?.is_some_and(is_whitespace) {\n            let _next = self.cursor.next_char();\n        }\n\n        // -->\n        if self.cursor.peek_n(3)?[..3] == [Some(0x2D), Some(0x2D), Some(0x3E)] {\n            let _next = self.cursor.next_char();\n            let _next = self.cursor.next_char();\n            let _next = self.cursor.next_char();\n\n            let start = self.cursor.pos_group();\n            SingleLineComment.lex(&mut self.cursor, start, interner)?;\n        }\n\n        Ok(())\n    }\n\n    /// Retrieves the next token from the lexer.\n    ///\n    /// # Errors\n    ///\n    /// Will return `Err` on invalid tokens and invalid reads of the bytes being lexed.\n    // We intentionally don't implement Iterator trait as Result<Option> is cleaner to handle.\n    pub(crate) fn next_no_skip(&mut self, interner: &mut Interner) -> Result<Option<Token>, Error>\n    where\n        R: ReadChar,\n    {\n        let mut start = self.cursor.pos_group();\n        let Some(mut next_ch) = self.cursor.next_char()? else {\n            return Ok(None);\n        };\n\n        // If the goal symbol is HashbangOrRegExp, then we need to check if the next token is a hashbang comment.\n        // Since the goal symbol is only valid for the first token, we need to change it to RegExp after the first token.\n        if self.get_goal() == InputElement::HashbangOrRegExp {\n            self.set_goal(InputElement::RegExp);\n            if next_ch == 0x23 && self.cursor.peek_char()? == Some(0x21) {\n                let _token = HashbangComment.lex(&mut self.cursor, start, interner);\n                return self.next(interner);\n            }\n        }\n\n        // Ignore whitespace\n        if is_whitespace(next_ch) {\n            loop {\n                start = self.cursor.pos_group();\n                let Some(next) = self.cursor.next_char()? else {\n                    return Ok(None);\n                };\n                if !is_whitespace(next) {\n                    next_ch = next;\n                    break;\n                }\n            }\n        }\n\n        if let Ok(c) = char::try_from(next_ch) {\n            let token = match c {\n                '\\r' | '\\n' | '\\u{2028}' | '\\u{2029}' => Ok(Token::new_by_position_group(\n                    TokenKind::LineTerminator,\n                    start,\n                    self.cursor.pos_group(),\n                )),\n                '\"' | '\\'' => StringLiteral::new(c).lex(&mut self.cursor, start, interner),\n                '`' => TemplateLiteral.lex(&mut self.cursor, start, interner),\n                ';' => Ok(Token::new_by_position_group(\n                    Punctuator::Semicolon.into(),\n                    start,\n                    self.cursor.pos_group(),\n                )),\n                ':' => Ok(Token::new_by_position_group(\n                    Punctuator::Colon.into(),\n                    start,\n                    self.cursor.pos_group(),\n                )),\n                '.' => {\n                    if self\n                        .cursor\n                        .peek_char()?\n                        .filter(|c| (0x30..=0x39/* 0..=9 */).contains(c))\n                        .is_some()\n                    {\n                        NumberLiteral::new(b'.').lex(&mut self.cursor, start, interner)\n                    } else {\n                        SpreadLiteral::new().lex(&mut self.cursor, start, interner)\n                    }\n                }\n                '(' => Ok(Token::new_by_position_group(\n                    Punctuator::OpenParen.into(),\n                    start,\n                    self.cursor.pos_group(),\n                )),\n                ')' => Ok(Token::new_by_position_group(\n                    Punctuator::CloseParen.into(),\n                    start,\n                    self.cursor.pos_group(),\n                )),\n                ',' => Ok(Token::new_by_position_group(\n                    Punctuator::Comma.into(),\n                    start,\n                    self.cursor.pos_group(),\n                )),\n                '{' => Ok(Token::new_by_position_group(\n                    Punctuator::OpenBlock.into(),\n                    start,\n                    self.cursor.pos_group(),\n                )),\n                '}' => Ok(Token::new_by_position_group(\n                    Punctuator::CloseBlock.into(),\n                    start,\n                    self.cursor.pos_group(),\n                )),\n                '[' => Ok(Token::new_by_position_group(\n                    Punctuator::OpenBracket.into(),\n                    start,\n                    self.cursor.pos_group(),\n                )),\n                ']' => Ok(Token::new_by_position_group(\n                    Punctuator::CloseBracket.into(),\n                    start,\n                    self.cursor.pos_group(),\n                )),\n                '#' => PrivateIdentifier::new().lex(&mut self.cursor, start, interner),\n                '/' => self.lex_slash_token(start, interner, false),\n                #[cfg(feature = \"annex-b\")]\n                // <!--\n                '<' if !self.module()\n                    && self.cursor.peek_n(3)?[..3] == [Some(0x21), Some(0x2D), Some(0x2D)] =>\n                {\n                    let _next = self.cursor.next_char();\n                    let _next = self.cursor.next_char();\n                    let _next = self.cursor.next_char();\n                    let start = self.cursor.pos_group();\n                    SingleLineComment.lex(&mut self.cursor, start, interner)\n                }\n                #[allow(clippy::cast_possible_truncation)]\n                '=' | '*' | '+' | '-' | '%' | '|' | '&' | '^' | '<' | '>' | '!' | '~' | '?' => {\n                    Operator::new(next_ch as u8).lex(&mut self.cursor, start, interner)\n                }\n                '\\\\' if self.cursor.peek_char()? == Some(0x0075 /* u */) => {\n                    Identifier::new(c).lex(&mut self.cursor, start, interner)\n                }\n                _ if Identifier::is_identifier_start(c as u32) => {\n                    Identifier::new(c).lex(&mut self.cursor, start, interner)\n                }\n                #[allow(clippy::cast_possible_truncation)]\n                _ if c.is_ascii_digit() => {\n                    NumberLiteral::new(next_ch as u8).lex(&mut self.cursor, start, interner)\n                }\n                _ => {\n                    let details = format!(\n                        \"unexpected '{c}' at line {}, column {}\",\n                        start.line_number(),\n                        start.column_number()\n                    );\n                    Err(Error::syntax(details, start.position()))\n                }\n            }?;\n\n            Ok(Some(token))\n        } else {\n            Err(Error::syntax(\n                format!(\n                    \"unexpected utf-8 char '\\\\u{next_ch}' at line {}, column {}\",\n                    start.line_number(),\n                    start.column_number()\n                ),\n                start.position(),\n            ))\n        }\n    }\n\n    /// Retrieves the next token from the lexer, skipping comments.\n    ///\n    /// # Errors\n    ///\n    /// Will return `Err` on invalid tokens and invalid reads of the bytes being lexed.\n    // We intentionally don't implement Iterator trait as Result<Option> is cleaner to handle.\n    #[allow(clippy::should_implement_trait)]\n    pub fn next(&mut self, interner: &mut Interner) -> Result<Option<Token>, Error>\n    where\n        R: ReadChar,\n    {\n        loop {\n            let Some(next) = self.next_no_skip(interner)? else {\n                return Ok(None);\n            };\n\n            if next.kind() != &TokenKind::Comment {\n                return Ok(Some(next));\n            }\n        }\n    }\n\n    /// Performs the lexing of a template literal.\n    pub(crate) fn lex_template(\n        &mut self,\n        start: PositionGroup,\n        interner: &mut Interner,\n    ) -> Result<Token, Error>\n    where\n        R: ReadChar,\n    {\n        TemplateLiteral.lex(&mut self.cursor, start, interner)\n    }\n\n    pub(super) fn take_source(&mut self) -> boa_ast::SourceText {\n        self.cursor.take_source()\n    }\n}\n\nimpl<'a> From<&'a [u8]> for Lexer<UTF8Input<&'a [u8]>> {\n    fn from(input: &'a [u8]) -> Self {\n        Self::new(UTF8Input::new(input))\n    }\n}\n\n/// ECMAScript goal symbols.\n///\n/// <https://tc39.es/ecma262/#sec-ecmascript-language-lexical-grammar>\n#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)]\npub(crate) enum InputElement {\n    Div,\n    #[default]\n    RegExp,\n    TemplateTail,\n    HashbangOrRegExp,\n}\n\n/// Checks if a character is whitespace as per ECMAScript standards.\n///\n/// The Rust `char::is_whitespace` function and the ECMAScript standard use different sets of\n/// characters as whitespaces:\n///  * Rust uses `\\p{White_Space}`,\n///  * ECMAScript standard uses `\\{Space_Separator}` + `\\u{0009}`, `\\u{000B}`, `\\u{000C}`, `\\u{FEFF}`\n///\n/// [More information](https://tc39.es/ecma262/#table-32)\nconst fn is_whitespace(ch: u32) -> bool {\n    matches!(\n        ch,\n        0x0020 | 0x0009 | 0x000B | 0x000C | 0x00A0 | 0xFEFF |\n            // Unicode Space_Separator category (minus \\u{0020} and \\u{00A0} which are already stated above)\n            0x1680 | 0x2000..=0x200A | 0x202F | 0x205F | 0x3000\n    )\n}\n"
  },
  {
    "path": "core/parser/src/lexer/number.rs",
    "content": "//! This module implements lexing for number literals (123, 787) used in ECMAScript.\n\nuse crate::lexer::{Cursor, Error, Token, TokenKind, Tokenizer, token::Numeric};\nuse crate::source::ReadChar;\nuse boa_ast::PositionGroup;\nuse boa_interner::Interner;\nuse num_bigint::BigInt;\nuse num_traits::{ToPrimitive, Zero};\nuse std::str;\n\n/// Number literal lexing.\n///\n/// Assumes the digit is consumed by the cursor (stored in init).\n///\n/// More information:\n///  - [ECMAScript reference][spec]\n///  - [MDN documentation][mdn]\n///\n/// [spec]: https://tc39.es/ecma262/#sec-literals-numeric-literals\n/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Data_structures#Number_type\n#[derive(Debug, Clone, Copy)]\npub(super) struct NumberLiteral {\n    init: u8,\n}\n\nimpl NumberLiteral {\n    /// Creates a new string literal lexer.\n    pub(super) const fn new(init: u8) -> Self {\n        Self { init }\n    }\n}\n\n/// This is a helper structure\n///\n/// This structure helps with identifying what numerical type it is and what base is it.\n#[derive(Debug, Clone, Copy, PartialEq, Eq)]\nenum NumericKind {\n    Rational,\n    Integer(u32),\n    BigInt(u32),\n}\n\nimpl NumericKind {\n    /// Get the base of the number kind.\n    const fn base(self) -> u32 {\n        match self {\n            Self::Rational => 10,\n            Self::Integer(base) | Self::BigInt(base) => base,\n        }\n    }\n\n    /// Converts `self` to `BigInt` kind.\n    fn to_bigint(self) -> Self {\n        match self {\n            Self::Rational => unreachable!(\"can not convert rational number to BigInt\"),\n            Self::Integer(base) | Self::BigInt(base) => Self::BigInt(base),\n        }\n    }\n}\n\nfn take_signed_integer<R>(\n    buf: &mut Vec<u8>,\n    cursor: &mut Cursor<R>,\n    kind: NumericKind,\n) -> Result<(), Error>\nwhere\n    R: ReadChar,\n{\n    // The next part must be SignedInteger.\n    // This is optionally a '+' or '-' followed by 1 or more DecimalDigits.\n    match cursor.next_char()? {\n        Some(0x2B /* + */) => {\n            buf.push(b'+');\n            if !cursor.next_is_ascii_pred(&|ch| ch.is_digit(kind.base()))? {\n                // A digit must follow the + or - symbol.\n                return Err(Error::syntax(\"No digit found after + symbol\", cursor.pos()));\n            }\n        }\n        Some(0x2D /* - */) => {\n            buf.push(b'-');\n            if !cursor.next_is_ascii_pred(&|ch| ch.is_digit(kind.base()))? {\n                // A digit must follow the + or - symbol.\n                return Err(Error::syntax(\"No digit found after - symbol\", cursor.pos()));\n            }\n        }\n        Some(c) => {\n            if let Some(ch) = char::from_u32(c) {\n                if ch.is_ascii() && ch.is_digit(kind.base()) {\n                    #[allow(clippy::cast_possible_truncation)]\n                    buf.push(c as u8);\n                } else {\n                    return Err(Error::syntax(\n                        \"When lexing exponential value found unexpected char\",\n                        cursor.pos(),\n                    ));\n                }\n            } else {\n                return Err(Error::syntax(\n                    \"When lexing exponential value found unexpected char\",\n                    cursor.pos(),\n                ));\n            }\n        }\n        None => {\n            return Err(Error::syntax(\n                \"Abrupt end: No exponential value found\",\n                cursor.pos(),\n            ));\n        }\n    }\n\n    // Consume the decimal digits.\n    take_integer(buf, cursor, kind, true)?;\n\n    Ok(())\n}\n\nfn take_integer<R>(\n    buf: &mut Vec<u8>,\n    cursor: &mut Cursor<R>,\n    kind: NumericKind,\n    separator_allowed: bool,\n) -> Result<(), Error>\nwhere\n    R: ReadChar,\n{\n    let mut prev_is_underscore = false;\n    let mut pos = cursor.pos();\n    while cursor.next_is_ascii_pred(&|c| c.is_digit(kind.base()) || c == '_')? {\n        pos = cursor.pos();\n        match cursor.next_char()? {\n            Some(0x5F /* _ */) if separator_allowed => {\n                if prev_is_underscore {\n                    return Err(Error::syntax(\n                        \"only one underscore is allowed as numeric separator\",\n                        cursor.pos(),\n                    ));\n                }\n                prev_is_underscore = true;\n            }\n            Some(0x5F /* _ */) if !separator_allowed => {\n                return Err(Error::syntax(\"separator is not allowed\", pos));\n            }\n            Some(c) => {\n                if char::from_u32(c).is_some_and(|ch| ch.is_digit(kind.base())) {\n                    prev_is_underscore = false;\n                    #[allow(clippy::cast_possible_truncation)]\n                    buf.push(c as u8);\n                }\n            }\n            _ => (),\n        }\n    }\n    if prev_is_underscore {\n        return Err(Error::syntax(\n            \"underscores are not allowed at the end of numeric literals\",\n            pos,\n        ));\n    }\n    Ok(())\n}\n\n/// Utility function for checking the `NumericLiteral` is not followed by an `IdentifierStart` or `DecimalDigit` character.\n///\n/// More information:\n///  - [ECMAScript Specification][spec]\n///\n/// [spec]: https://tc39.es/ecma262/#sec-literals-numeric-literals\nfn check_after_numeric_literal<R>(cursor: &mut Cursor<R>) -> Result<(), Error>\nwhere\n    R: ReadChar,\n{\n    if cursor.next_is_ascii_pred(&|ch| ch.is_ascii_alphanumeric() || ch == '$' || ch == '_')? {\n        Err(Error::syntax(\n            \"a numeric literal must not be followed by an alphanumeric, $ or _ characters\",\n            cursor.pos(),\n        ))\n    } else {\n        Ok(())\n    }\n}\n\nimpl<R> Tokenizer<R> for NumberLiteral {\n    fn lex(\n        &mut self,\n        cursor: &mut Cursor<R>,\n        start_pos: PositionGroup,\n        _interner: &mut Interner,\n    ) -> Result<Token, Error>\n    where\n        R: ReadChar,\n    {\n        let mut buf = vec![self.init];\n\n        // Default assume the number is a base 10 integer.\n        let mut kind = NumericKind::Integer(10);\n\n        let c = cursor.peek_char();\n        let mut legacy_octal = false;\n\n        if self.init == b'0' {\n            if let Some(ch) = c? {\n                match ch {\n                    // x | X\n                    0x0078 | 0x0058 => {\n                        // Remove the initial '0' from buffer.\n                        cursor.next_char()?.expect(\"x or X character vanished\");\n                        buf.pop();\n\n                        // HexIntegerLiteral\n                        kind = NumericKind::Integer(16);\n\n                        // Checks if the next char after '0x' is a digit of that base. if not return an error.\n                        if !cursor.next_is_ascii_pred(&|ch| ch.is_ascii_hexdigit())? {\n                            return Err(Error::syntax(\n                                \"expected hexadecimal digit after number base prefix\",\n                                cursor.pos(),\n                            ));\n                        }\n                    }\n                    // o | O\n                    0x006F | 0x004F => {\n                        // Remove the initial '0' from buffer.\n                        cursor.next_char()?.expect(\"o or O character vanished\");\n                        buf.pop();\n\n                        // OctalIntegerLiteral\n                        kind = NumericKind::Integer(8);\n\n                        // Checks if the next char after '0o' is a digit of that base. if not return an error.\n                        if !cursor.next_is_ascii_pred(&|ch| ch.is_digit(8))? {\n                            return Err(Error::syntax(\n                                \"expected octal digit after number base prefix\",\n                                cursor.pos(),\n                            ));\n                        }\n                    }\n                    // b | B\n                    0x0062 | 0x0042 => {\n                        // Remove the initial '0' from buffer.\n                        cursor.next_char()?.expect(\"b or B character vanished\");\n                        buf.pop();\n\n                        // BinaryIntegerLiteral\n                        kind = NumericKind::Integer(2);\n\n                        // Checks if the next char after '0b' is a digit of that base. if not return an error.\n                        if !cursor.next_is_ascii_pred(&|ch| ch.is_digit(2))? {\n                            return Err(Error::syntax(\n                                \"expected binary digit after number base prefix\",\n                                cursor.pos(),\n                            ));\n                        }\n                    }\n                    // n\n                    0x006E => {\n                        cursor.next_char()?.expect(\"n character vanished\");\n\n                        // DecimalBigIntegerLiteral '0n'\n                        return Ok(Token::new_by_position_group(\n                            TokenKind::NumericLiteral(Numeric::BigInt(BigInt::zero().into())),\n                            start_pos,\n                            cursor.pos_group(),\n                        ));\n                    }\n                    byte => {\n                        legacy_octal = true;\n                        if let Some(ch) = char::from_u32(byte) {\n                            if ch.is_digit(8) {\n                                // LegacyOctalIntegerLiteral, or a number with leading 0s.\n                                if cursor.strict() {\n                                    // LegacyOctalIntegerLiteral is forbidden with strict mode true.\n                                    return Err(Error::syntax(\n                                        \"implicit octal literals are not allowed in strict mode\",\n                                        start_pos,\n                                    ));\n                                }\n\n                                // Remove the initial '0' from buffer.\n                                buf.pop();\n\n                                #[allow(clippy::cast_possible_truncation)]\n                                buf.push(cursor.next_char()?.expect(\"'0' character vanished\") as u8);\n\n                                take_integer(&mut buf, cursor, NumericKind::Integer(8), false)?;\n\n                                if !cursor\n                                    .next_is_ascii_pred(&|c| c.is_ascii_digit() || c == '_')?\n                                {\n                                    // LegacyOctalIntegerLiteral\n                                    kind = NumericKind::Integer(8);\n                                }\n                            } else if ch.is_ascii_digit() {\n                                // Indicates a numerical digit comes after then 0 but it isn't an octal digit\n                                // so therefore this must be a number with an unneeded leading 0. This is\n                                // forbidden in strict mode.\n                                if cursor.strict() {\n                                    return Err(Error::syntax(\n                                        \"leading 0's are not allowed in strict mode\",\n                                        start_pos,\n                                    ));\n                                }\n                            }\n                        } // Else indicates that the symbol is a non-number.\n                    }\n                }\n            } else {\n                // DecimalLiteral lexing.\n                // Indicates that the number is just a single 0.\n                return Ok(Token::new_by_position_group(\n                    TokenKind::NumericLiteral(Numeric::Integer(0)),\n                    start_pos,\n                    cursor.pos_group(),\n                ));\n            }\n        }\n\n        let next = if self.init == b'.' {\n            Some(0x002E /* . */)\n        } else {\n            // Consume digits and separators until a non-digit non-separator\n            // character is encountered or all the characters are consumed.\n            take_integer(&mut buf, cursor, kind, !legacy_octal)?;\n            cursor.peek_char()?\n        };\n\n        // The non-digit character could be:\n        // 'n' To indicate a BigIntLiteralSuffix.\n        // '.' To indicate a decimal separator.\n        // 'e' | 'E' To indicate an ExponentPart.\n        match next {\n            Some(0x006E /* n */) => {\n                // DecimalBigIntegerLiteral\n                // Lexing finished.\n                // Consume the n\n                if legacy_octal {\n                    return Err(Error::syntax(\n                        \"'n' suffix not allowed in octal representation\",\n                        cursor.pos(),\n                    ));\n                }\n                cursor.next_char()?.expect(\"n character vanished\");\n\n                kind = kind.to_bigint();\n            }\n            Some(0x002E /* . */) => {\n                if kind.base() == 10 {\n                    // Only base 10 numbers can have a decimal separator.\n                    // Number literal lexing finished if a . is found for a number in a different base.\n                    if self.init != b'.' {\n                        cursor.next_char()?.expect(\"'.' token vanished\");\n                        buf.push(b'.'); // Consume the .\n                    }\n                    kind = NumericKind::Rational;\n\n                    if cursor.peek_char()? == Some(0x005F /* _ */) {\n                        return Err(Error::syntax(\n                            \"numeric separator not allowed after '.'\",\n                            cursor.pos(),\n                        ));\n                    }\n\n                    // Consume digits and separators until a non-digit non-separator\n                    // character is encountered or all the characters are consumed.\n                    take_integer(&mut buf, cursor, kind, true)?;\n\n                    // The non-digit character at this point must be an 'e' or 'E' to indicate an Exponent Part.\n                    // Another '.' or 'n' is not allowed.\n                    if let Some(0x0065 /*e */ | 0x0045 /* E */) = cursor.peek_char()? {\n                        // Consume the ExponentIndicator.\n                        cursor.next_char()?.expect(\"e or E token vanished\");\n\n                        buf.push(b'E');\n\n                        take_signed_integer(&mut buf, cursor, kind)?;\n                    } else {\n                        // Finished lexing.\n                    }\n                }\n            }\n            Some(0x0065 /*e */ | 0x0045 /* E */) => {\n                kind = NumericKind::Rational;\n                cursor.next_char()?.expect(\"e or E character vanished\"); // Consume the ExponentIndicator.\n                buf.push(b'E');\n                take_signed_integer(&mut buf, cursor, kind)?;\n            }\n            Some(_) | None => {\n                // Indicates lexing finished.\n            }\n        }\n\n        check_after_numeric_literal(cursor)?;\n\n        let num_str = unsafe { str::from_utf8_unchecked(buf.as_slice()) };\n        let num = match kind {\n            NumericKind::BigInt(base) => {\n                Numeric::BigInt(\n                    BigInt::parse_bytes(num_str.as_bytes(), base).expect(\"Could not convert to BigInt\").into()\n                    )\n            }\n            // casting precisely to check if the float doesn't lose info on truncation\n            #[allow(clippy::cast_possible_truncation)]\n            NumericKind::Rational /* base: 10 */ => {\n                let val: f64 = fast_float2::parse(num_str).expect(\"Failed to parse float after checks\");\n                let int_val = val as i32;\n\n                // The truncated float should be identically to the non-truncated float for the conversion to be loss-less,\n                // any other different and the number must be stored as a rational.\n                #[allow(clippy::float_cmp)]\n                if f64::from(int_val) == val {\n                    // For performance reasons we attempt to store values as integers if possible.\n                    Numeric::Integer(int_val)\n                } else {\n                    Numeric::Rational(val)\n                }\n            },\n            NumericKind::Integer(base) => {\n                i32::from_str_radix(num_str, base).map_or_else(|_| {\n                    let num = BigInt::parse_bytes(num_str.as_bytes(), base).expect(\"Failed to parse integer after checks\");\n                    Numeric::Rational(num.to_f64().unwrap_or(f64::INFINITY))\n                }, Numeric::Integer)\n            }\n        };\n\n        Ok(Token::new_by_position_group(\n            TokenKind::NumericLiteral(num),\n            start_pos,\n            cursor.pos_group(),\n        ))\n    }\n}\n"
  },
  {
    "path": "core/parser/src/lexer/operator.rs",
    "content": "//! Boa's lexing for ECMAScript operators (+, - etc.).\n\nuse crate::lexer::{Cursor, Error, Token, TokenKind, Tokenizer};\nuse crate::source::ReadChar;\nuse boa_ast::{PositionGroup, Punctuator};\nuse boa_interner::Interner;\n\nconst CHAR_ASSIGN: u32 = '=' as u32;\n\n/// `vop` tests the next token to see if we're on an assign operation of just a plain binary operation.\n///\n/// If the next value is not an assignment operation it will pattern match  the provided values and return the corresponding token.\nmacro_rules! vop {\n    ($cursor:ident, $assign_op:expr, $op:expr) => ({\n        match $cursor.peek_char()? {\n            None => $op,\n            Some(CHAR_ASSIGN) => {\n                $cursor.next_char()?.expect(\"= token vanished\");\n                $assign_op\n            }\n            Some(_) => $op,\n        }\n    });\n    ($cursor:ident, $assign_op:expr, $op:expr, {$($case:pat => $block:expr), +}) => ({\n        match $cursor.peek_char()? {\n            None => $op,\n            Some(CHAR_ASSIGN) => {\n                $cursor.next_char()?.expect(\"= token vanished\");\n                $assign_op\n            },\n            $($case => {\n                $cursor.next_char()?.expect(\"Token vanished\");\n                $block\n            })+,\n            Some(_) => $op,\n        }\n    });\n}\n\n/// The `op` macro handles binary operations or assignment operations and converts them into tokens.\nmacro_rules! op {\n    ($cursor:ident, $start_pos:expr, $assign_op:expr, $op:expr) => ({\n        Token::new_by_position_group(\n            vop!($cursor, $assign_op, $op).into(),\n            $start_pos, $cursor.pos_group(),\n        )\n    });\n    ($cursor:ident, $start_pos:expr, $assign_op:expr, $op:expr, {$($case:pat => $block:expr),+}) => ({\n        let punc: Punctuator = vop!($cursor, $assign_op, $op, {$($case => $block),+});\n        Token::new_by_position_group(\n            punc.into(),\n            $start_pos, $cursor.pos_group(),\n        )\n    });\n}\n\n#[derive(Debug, Clone, Copy)]\npub(super) struct Operator {\n    init: u8,\n}\n\n/// Operator lexing.\n///\n/// Assumes that the cursor has already consumed the operator starting symbol (stored in init).\n///\n/// More information:\n///  - [ECMAScript reference][spec]\n///  - [MDN documentation][mdn]\n///\n/// [spec]: https://tc39.es/ecma262/#sec-ecmascript-language-expressions\n/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators\nimpl Operator {\n    /// Creates a new operator lexer.\n    pub(super) const fn new(init: u8) -> Self {\n        Self { init }\n    }\n}\n\nimpl<R> Tokenizer<R> for Operator {\n    fn lex(\n        &mut self,\n        cursor: &mut Cursor<R>,\n        start_pos: PositionGroup,\n        _interner: &mut Interner,\n    ) -> Result<Token, Error>\n    where\n        R: ReadChar,\n    {\n        Ok(match self.init {\n            b'*' => op!(cursor, start_pos, Punctuator::AssignMul, Punctuator::Mul, {\n                Some(0x2A /* * */) => vop!(cursor, Punctuator::AssignPow, Punctuator::Exp)\n            }),\n            b'+' => op!(cursor, start_pos, Punctuator::AssignAdd, Punctuator::Add, {\n                Some(0x2B /* + */) => Punctuator::Inc\n            }),\n            b'-' => op!(cursor, start_pos, Punctuator::AssignSub, Punctuator::Sub, {\n                Some(0x2D /* - */) => Punctuator::Dec\n            }),\n            b'%' => op!(cursor, start_pos, Punctuator::AssignMod, Punctuator::Mod),\n            b'|' => op!(cursor, start_pos, Punctuator::AssignOr, Punctuator::Or, {\n                Some(0x7C /* | */) => vop!(cursor, Punctuator::AssignBoolOr, Punctuator::BoolOr)\n            }),\n            b'&' => op!(cursor, start_pos, Punctuator::AssignAnd, Punctuator::And, {\n                Some(0x26 /* & */) => vop!(cursor, Punctuator::AssignBoolAnd, Punctuator::BoolAnd)\n            }),\n            b'?' => {\n                let (first, second) = (cursor.peek_char()?, cursor.peek_n(2)?[1]);\n                match first {\n                    Some(0x3F /* ? */) => {\n                        cursor.next_char()?.expect(\"? vanished\");\n                        op!(\n                            cursor,\n                            start_pos,\n                            Punctuator::AssignCoalesce,\n                            Punctuator::Coalesce\n                        )\n                    }\n                    Some(0x2E /* . */) if !matches!(second, Some(second) if (0x30..=0x39 /* 0..=9 */).contains(&second)) =>\n                    {\n                        cursor.next_char()?.expect(\". vanished\");\n                        Token::new_by_position_group(\n                            TokenKind::Punctuator(Punctuator::Optional),\n                            start_pos,\n                            cursor.pos_group(),\n                        )\n                    }\n                    _ => Token::new_by_position_group(\n                        TokenKind::Punctuator(Punctuator::Question),\n                        start_pos,\n                        cursor.pos_group(),\n                    ),\n                }\n            }\n            b'^' => op!(cursor, start_pos, Punctuator::AssignXor, Punctuator::Xor),\n            b'=' => op!(cursor, start_pos, if cursor.next_if(0x3D /* = */)? {\n                Punctuator::StrictEq\n            } else {\n                Punctuator::Eq\n            }, Punctuator::Assign, {\n                Some(0x3E /* > */) => {\n                    Punctuator::Arrow\n                }\n            }),\n            b'<' => {\n                op!(cursor, start_pos, Punctuator::LessThanOrEq, Punctuator::LessThan, {\n                    Some(0x3C /* < */) => vop!(cursor, Punctuator::AssignLeftSh, Punctuator::LeftSh)\n                })\n            }\n            b'>' => {\n                op!(cursor, start_pos, Punctuator::GreaterThanOrEq, Punctuator::GreaterThan, {\n                    Some(0x3E /* > */) => vop!(cursor, Punctuator::AssignRightSh, Punctuator::RightSh, {\n                        Some(0x3E /* > */) => vop!(cursor, Punctuator::AssignURightSh, Punctuator::URightSh)\n                    })\n                })\n            }\n            b'!' => op!(\n                cursor,\n                start_pos,\n                vop!(cursor, Punctuator::StrictNotEq, Punctuator::NotEq),\n                Punctuator::Not\n            ),\n            b'~' => {\n                Token::new_by_position_group(Punctuator::Neg.into(), start_pos, cursor.pos_group())\n            }\n            op => {\n                return Err(Error::unexpected(\n                    char::from(op),\n                    \"expected valid operator\",\n                    start_pos.position(),\n                ));\n            }\n        })\n    }\n}\n"
  },
  {
    "path": "core/parser/src/lexer/private_identifier.rs",
    "content": "//! Boa's lexing for ECMAScript private identifiers (#foo, #myvar, etc.).\n\nuse crate::lexer::{Cursor, Error, Token, TokenKind, Tokenizer, identifier::Identifier};\nuse crate::source::ReadChar;\nuse boa_ast::PositionGroup;\nuse boa_interner::Interner;\n\n/// Private Identifier lexing.\n///\n/// More information:\n///  - [ECMAScript reference][spec]\n///\n/// [spec]: https://tc39.es/ecma262/#prod-PrivateIdentifier\n#[derive(Debug, Clone, Copy)]\npub(super) struct PrivateIdentifier;\n\nimpl PrivateIdentifier {\n    /// Creates a new private identifier lexer.\n    pub(super) const fn new() -> Self {\n        Self\n    }\n}\n\nimpl<R> Tokenizer<R> for PrivateIdentifier {\n    fn lex(\n        &mut self,\n        cursor: &mut Cursor<R>,\n        start_pos: PositionGroup,\n        interner: &mut Interner,\n    ) -> Result<Token, Error>\n    where\n        R: ReadChar,\n    {\n        let Some(next_ch) = cursor.next_char()? else {\n            return Err(Error::syntax(\n                \"Abrupt end: Expecting private identifier\",\n                start_pos,\n            ));\n        };\n\n        let Ok(c) = char::try_from(next_ch) else {\n            return Err(Error::syntax(\n                format!(\n                    \"unexpected utf-8 char '\\\\u{next_ch}' at line {}, column {}\",\n                    start_pos.line_number(),\n                    start_pos.column_number()\n                ),\n                start_pos,\n            ));\n        };\n\n        match c {\n            '\\\\' if cursor.peek_char()? == Some(0x0075 /* u */) => {\n                let (name, _) = Identifier::take_identifier_name(cursor, start_pos, c)?;\n                Ok(Token::new_by_position_group(\n                    TokenKind::PrivateIdentifier(interner.get_or_intern(name.as_str())),\n                    start_pos,\n                    cursor.pos_group(),\n                ))\n            }\n            _ if Identifier::is_identifier_start(c as u32) => {\n                let (name, _) = Identifier::take_identifier_name(cursor, start_pos, c)?;\n                Ok(Token::new_by_position_group(\n                    TokenKind::PrivateIdentifier(interner.get_or_intern(name.as_str())),\n                    start_pos,\n                    cursor.pos_group(),\n                ))\n            }\n            _ => Err(Error::syntax(\n                \"Abrupt end: Expecting private identifier\",\n                start_pos,\n            )),\n        }\n    }\n}\n"
  },
  {
    "path": "core/parser/src/lexer/regex.rs",
    "content": "//! Boa's lexing for ECMAScript regex literals.\n\nuse crate::lexer::{Cursor, Error, Token, TokenKind, Tokenizer};\nuse crate::source::ReadChar;\nuse bitflags::bitflags;\nuse boa_ast::PositionGroup;\nuse boa_interner::Interner;\nuse regress::Flags;\nuse std::fmt::{Display, Write};\nuse std::str::{self, FromStr};\n\nconst MAXIMUM_REGEX_FLAGS: usize = 8;\n\n/// Regex literal lexing.\n///\n/// Lexes Division, Assigndiv or Regex literal.\n///\n/// Expects: Initial '/' to already be consumed by cursor.\n///\n/// More information:\n///  - [ECMAScript reference][spec]\n///  - [MDN documentation][mdn]\n///\n/// [spec]: https://tc39.es/ecma262/#sec-literals-regular-expression-literals\n/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_Expressions\n#[derive(Debug, Clone, Copy)]\npub(super) struct RegexLiteral {\n    // If there is more cases than only `/=`\n    // then use `Option<u8>` or (more correct) `Option<enum>`\n    init_with_eq: bool,\n}\n\nimpl RegexLiteral {\n    /// `init_with_eq` is '=' after `/` already consumed?\n    pub(super) fn new(init_with_eq: bool) -> Self {\n        Self { init_with_eq }\n    }\n}\n\nimpl<R> Tokenizer<R> for RegexLiteral {\n    fn lex(\n        &mut self,\n        cursor: &mut Cursor<R>,\n        start_pos: PositionGroup,\n        interner: &mut Interner,\n    ) -> Result<Token, Error>\n    where\n        R: ReadChar,\n    {\n        let mut body = Vec::new();\n        if self.init_with_eq {\n            body.push(u32::from(b'='));\n        }\n\n        let mut is_class_char = false;\n\n        // Lex RegularExpressionBody.\n        loop {\n            match cursor.next_char()? {\n                None => {\n                    // Abrupt end.\n                    return Err(Error::syntax(\n                        \"abrupt end on regular expression\",\n                        cursor.pos(),\n                    ));\n                }\n                Some(b) => {\n                    match b {\n                        // /\n                        0x2F if !is_class_char => break, // RegularExpressionBody finished.\n                        // [\n                        0x5B => {\n                            is_class_char = true;\n                            body.push(b);\n                        }\n                        // ]\n                        0x5D if is_class_char => {\n                            is_class_char = false;\n                            body.push(b);\n                        }\n                        // \\n | \\r | \\u{2028} | \\u{2029}\n                        0xA | 0xD | 0x2028 | 0x2029 => {\n                            // Not allowed in Regex literal.\n                            return Err(Error::syntax(\n                                \"new lines are not allowed in regular expressions\",\n                                cursor.pos(),\n                            ));\n                        }\n                        // \\\n                        0x5C => {\n                            // Escape sequence\n                            body.push(b);\n                            if let Some(sc) = cursor.next_char()? {\n                                match sc {\n                                    // \\n | \\r | \\u{2028} | \\u{2029}\n                                    0xA | 0xD | 0x2028 | 0x2029 => {\n                                        // Not allowed in Regex literal.\n                                        return Err(Error::syntax(\n                                            \"new lines are not allowed in regular expressions\",\n                                            cursor.pos(),\n                                        ));\n                                    }\n                                    b => body.push(b),\n                                }\n                            } else {\n                                // Abrupt end of regex.\n                                return Err(Error::syntax(\n                                    \"abrupt end on regular expression\",\n                                    cursor.pos(),\n                                ));\n                            }\n                        }\n                        _ => body.push(b),\n                    }\n                }\n            }\n        }\n\n        let mut flags: [u32; MAXIMUM_REGEX_FLAGS] = [0; MAXIMUM_REGEX_FLAGS];\n        let n = cursor.take_array_alphabetic(&mut flags)?;\n        if n > MAXIMUM_REGEX_FLAGS {\n            // There can only be a maximum of 8 flags.\n            return Err(Error::syntax(\n                \"Invalid regular expression: too many flags\",\n                start_pos,\n            ));\n        }\n        let flags: RegExpFlags =\n            RegExpFlags::try_from(&flags[..n]).map_err(|e| Error::syntax(e, start_pos))?;\n\n        // We have a vague hint of the size of this vector in the best case scenario.\n        let mut body_utf16 = Vec::with_capacity(body.len());\n\n        // We convert the body to UTF-16 since it may contain code points that are not valid UTF-8.\n        // We already know that the body is valid UTF-16. Casting is fine.\n        #[allow(clippy::cast_possible_truncation)]\n        for cp in &body {\n            let cp = *cp;\n            if cp <= 0xFFFF {\n                body_utf16.push(cp as u16);\n            } else {\n                let cp = cp - 0x1_0000;\n                let high = 0xD800 | ((cp >> 10) as u16);\n                let low = 0xDC00 | ((cp as u16) & 0x3FF);\n                body_utf16.push(high);\n                body_utf16.push(low);\n            }\n        }\n\n        // Only try to parse and validate, do not optimize/compile.\n        drop(\n            regress::backends::try_parse(body.into_iter(), flags.into()).map_err(|error| {\n                Error::syntax(\n                    format!(\"Invalid regular expression literal: {error}\"),\n                    start_pos,\n                )\n            })?,\n        );\n\n        let mut flags_buf = [0u8; MAXIMUM_REGEX_FLAGS];\n        let flags_str = flags.write_to_buf(&mut flags_buf);\n\n        Ok(Token::new_by_position_group(\n            TokenKind::regular_expression_literal(\n                interner.get_or_intern(body_utf16.as_slice()),\n                interner.get_or_intern(flags_str),\n            ),\n            start_pos,\n            cursor.pos_group(),\n        ))\n    }\n}\n\nbitflags! {\n    /// Flags of a regular expression.\n    #[derive(Debug, Default, Copy, Clone)]\n    pub struct RegExpFlags: u8 {\n        /// Whether to test the regular expression against all possible matches in a string,\n        /// or only against the first.\n        const GLOBAL = 0b0000_0001;\n\n        /// Whether to ignore case while attempting a match in a string.\n        const IGNORE_CASE = 0b0000_0010;\n\n        /// Whether or not to search in strings across multiple lines.\n        const MULTILINE = 0b0000_0100;\n\n        /// Whether `.` matches newlines or not.\n        const DOT_ALL = 0b0000_1000;\n\n        /// Whether or not Unicode features are enabled.\n        const UNICODE = 0b0001_0000;\n\n        /// Whether or not the search is sticky.\n        const STICKY = 0b0010_0000;\n\n        /// Whether the regular expression result exposes the start and end indices of\n        /// captured substrings.\n        const HAS_INDICES = 0b0100_0000;\n\n        /// Whether or not UnicodeSets features are enabled.\n        const UNICODE_SETS = 0b1000_0000;\n    }\n}\n\nimpl TryFrom<&[u32]> for RegExpFlags {\n    type Error = String;\n\n    fn try_from(value: &[u32]) -> Result<Self, Self::Error> {\n        let mut flags = Self::default();\n        for c in value {\n            let c = char::from_u32(*c)\n                .ok_or_else(|| format!(\"Invalid regular expression flag: {c}\"))?;\n\n            let new_flag = match c {\n                'g' => Self::GLOBAL,\n                'i' => Self::IGNORE_CASE,\n                'm' => Self::MULTILINE,\n                's' => Self::DOT_ALL,\n                'u' => Self::UNICODE,\n                'y' => Self::STICKY,\n                'd' => Self::HAS_INDICES,\n                'v' => Self::UNICODE_SETS,\n                _ => return Err(format!(\"invalid regular expression flag {c}\")),\n            };\n\n            if flags.contains(new_flag) {\n                return Err(format!(\"repeated regular expression flag {c}\"));\n            }\n            flags.insert(new_flag);\n        }\n\n        if flags.contains(Self::UNICODE) && flags.contains(Self::UNICODE_SETS) {\n            return Err(\"cannot use both 'u' and 'v' flags\".into());\n        }\n\n        Ok(flags)\n    }\n}\n\nimpl FromStr for RegExpFlags {\n    type Err = String;\n\n    fn from_str(s: &str) -> Result<Self, Self::Err> {\n        let mut flags = Self::default();\n        for c in s.bytes() {\n            let new_flag = match c {\n                b'g' => Self::GLOBAL,\n                b'i' => Self::IGNORE_CASE,\n                b'm' => Self::MULTILINE,\n                b's' => Self::DOT_ALL,\n                b'u' => Self::UNICODE,\n                b'y' => Self::STICKY,\n                b'd' => Self::HAS_INDICES,\n                b'v' => Self::UNICODE_SETS,\n                _ => return Err(format!(\"invalid regular expression flag {}\", char::from(c))),\n            };\n\n            if flags.contains(new_flag) {\n                return Err(format!(\n                    \"repeated regular expression flag {}\",\n                    char::from(c)\n                ));\n            }\n            flags.insert(new_flag);\n        }\n\n        if flags.contains(Self::UNICODE) && flags.contains(Self::UNICODE_SETS) {\n            return Err(\"cannot use both 'u' and 'v' flags\".into());\n        }\n\n        Ok(flags)\n    }\n}\n\nimpl RegExpFlags {\n    /// Writes the flags string to a buffer and returns it as `&str`.\n    /// Avoids heap allocation when interning regex flags during lexing.\n    /// The buffer must be at least 8 bytes (maximum number of flags).\n    #[inline]\n    pub fn write_to_buf<'a>(&self, buf: &'a mut [u8; MAXIMUM_REGEX_FLAGS]) -> &'a str {\n        let mut len = 0;\n        if self.contains(Self::HAS_INDICES) {\n            buf[len] = b'd';\n            len += 1;\n        }\n        if self.contains(Self::GLOBAL) {\n            buf[len] = b'g';\n            len += 1;\n        }\n        if self.contains(Self::IGNORE_CASE) {\n            buf[len] = b'i';\n            len += 1;\n        }\n        if self.contains(Self::MULTILINE) {\n            buf[len] = b'm';\n            len += 1;\n        }\n        if self.contains(Self::DOT_ALL) {\n            buf[len] = b's';\n            len += 1;\n        }\n        if self.contains(Self::UNICODE) {\n            buf[len] = b'u';\n            len += 1;\n        }\n        if self.contains(Self::STICKY) {\n            buf[len] = b'y';\n            len += 1;\n        }\n        if self.contains(Self::UNICODE_SETS) {\n            buf[len] = b'v';\n            len += 1;\n        }\n        // SAFETY: We only wrote ASCII bytes (d, g, i, m, s, u, y, v).\n        unsafe { str::from_utf8_unchecked(&buf[..len]) }\n    }\n}\n\nimpl Display for RegExpFlags {\n    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {\n        if self.contains(Self::HAS_INDICES) {\n            f.write_char('d')?;\n        }\n        if self.contains(Self::GLOBAL) {\n            f.write_char('g')?;\n        }\n        if self.contains(Self::IGNORE_CASE) {\n            f.write_char('i')?;\n        }\n        if self.contains(Self::MULTILINE) {\n            f.write_char('m')?;\n        }\n        if self.contains(Self::DOT_ALL) {\n            f.write_char('s')?;\n        }\n        if self.contains(Self::UNICODE) {\n            f.write_char('u')?;\n        }\n        if self.contains(Self::STICKY) {\n            f.write_char('y')?;\n        }\n        if self.contains(Self::UNICODE_SETS) {\n            f.write_char('v')?;\n        }\n        Ok(())\n    }\n}\n\nimpl From<RegExpFlags> for Flags {\n    fn from(value: RegExpFlags) -> Self {\n        Self {\n            icase: value.contains(RegExpFlags::IGNORE_CASE),\n            multiline: value.contains(RegExpFlags::MULTILINE),\n            dot_all: value.contains(RegExpFlags::DOT_ALL),\n            unicode: value.contains(RegExpFlags::UNICODE),\n            unicode_sets: value.contains(RegExpFlags::UNICODE_SETS),\n            ..Self::default()\n        }\n    }\n}\n"
  },
  {
    "path": "core/parser/src/lexer/spread.rs",
    "content": "//! Boa's lexing for ECMAScript spread (...) literals.\n\nuse crate::lexer::{Cursor, Error, Token, Tokenizer};\nuse crate::source::ReadChar;\nuse boa_ast::{PositionGroup, Punctuator};\nuse boa_interner::Interner;\n\n/// Spread literal lexing.\n///\n/// Note: expects for the initializer `'` or `\"` to already be consumed from the cursor.\n///\n/// More information:\n///  - [ECMAScript reference][spec]\n///  - [MDN documentation][mdn]\n///\n/// [spec]: https://tc39.es/ecma262/#prod-SpreadElement\n/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Spread_syntax\n#[derive(Debug, Clone, Copy)]\npub(super) struct SpreadLiteral;\n\nimpl SpreadLiteral {\n    /// Creates a new string literal lexer.\n    pub(super) const fn new() -> Self {\n        Self\n    }\n}\n\nimpl<R> Tokenizer<R> for SpreadLiteral {\n    fn lex(\n        &mut self,\n        cursor: &mut Cursor<R>,\n        start_pos: PositionGroup,\n        _interner: &mut Interner,\n    ) -> Result<Token, Error>\n    where\n        R: ReadChar,\n    {\n        // . or ...\n        if cursor.next_if(0x2E /* . */)? {\n            if cursor.next_if(0x2E /* . */)? {\n                Ok(Token::new_by_position_group(\n                    Punctuator::Spread.into(),\n                    start_pos,\n                    cursor.pos_group(),\n                ))\n            } else {\n                Err(Error::syntax(\n                    \"Expecting Token '.' as part of spread\",\n                    cursor.pos(),\n                ))\n            }\n        } else {\n            Ok(Token::new_by_position_group(\n                Punctuator::Dot.into(),\n                start_pos,\n                cursor.pos_group(),\n            ))\n        }\n    }\n}\n"
  },
  {
    "path": "core/parser/src/lexer/string.rs",
    "content": "//! Boa's lexing for ECMAScript string literals.\n\nuse crate::lexer::{Cursor, Error, Token, TokenKind, Tokenizer, token::EscapeSequence};\nuse crate::source::ReadChar;\nuse boa_ast::{LinearSpan, Position, PositionGroup, Span};\nuse boa_interner::Interner;\nuse std::io::{self, ErrorKind};\n\n/// String literal lexing.\n///\n/// Note: expects for the initializer `'` or `\"` to already be consumed from the cursor.\n///\n/// More information:\n///  - [ECMAScript reference][spec]\n///  - [MDN documentation][mdn]\n///\n/// [spec]: https://tc39.es/ecma262/#sec-literals-string-literals\n/// [mdn]: https://developer.cdn.mozilla.net/en-US/docs/Web/JavaScript/Reference/Global_Objects/String\n#[derive(Debug, Clone, Copy)]\npub(super) struct StringLiteral {\n    terminator: StringTerminator,\n}\n\nimpl StringLiteral {\n    /// Creates a new string literal lexer.\n    pub(super) fn new(init: char) -> Self {\n        let terminator = match init {\n            '\\'' => StringTerminator::SingleQuote,\n            '\"' => StringTerminator::DoubleQuote,\n            _ => unreachable!(),\n        };\n\n        Self { terminator }\n    }\n}\n\n/// Terminator for the string.\n#[derive(Debug, Clone, Copy, PartialEq, Eq)]\npub(crate) enum StringTerminator {\n    SingleQuote,\n    DoubleQuote,\n}\n\n/// Extends a buffer type to store UTF-16 code units and convert to string.\npub(crate) trait UTF16CodeUnitsBuffer {\n    /// Encodes the code point to UTF-16 code units and push to the buffer.\n    fn push_code_point(&mut self, code_point: u32);\n}\n\nimpl UTF16CodeUnitsBuffer for Vec<u16> {\n    fn push_code_point(&mut self, mut code_point: u32) {\n        if let Ok(cp) = code_point.try_into() {\n            self.push(cp);\n            return;\n        }\n        code_point -= 0x10000;\n\n        let cu1 = (code_point / 1024 + 0xD800)\n            .try_into()\n            .expect(\"decoded an u32 into two u16.\");\n        let cu2 = (code_point % 1024 + 0xDC00)\n            .try_into()\n            .expect(\"decoded an u32 into two u16.\");\n        self.push(cu1);\n        self.push(cu2);\n    }\n}\n\nimpl<R> Tokenizer<R> for StringLiteral {\n    fn lex(\n        &mut self,\n        cursor: &mut Cursor<R>,\n        start_pos: PositionGroup,\n        interner: &mut Interner,\n    ) -> Result<Token, Error>\n    where\n        R: ReadChar,\n    {\n        let (lit, span, escape_sequence) = Self::take_string_characters(\n            cursor,\n            start_pos.position(),\n            self.terminator,\n            cursor.strict(),\n        )?;\n\n        Ok(Token::new(\n            TokenKind::string_literal(interner.get_or_intern(&lit[..]), escape_sequence),\n            span,\n            LinearSpan::new(start_pos.linear_position(), cursor.linear_pos()),\n        ))\n    }\n}\n\nimpl StringLiteral {\n    /// Checks if a character is `LineTerminator` as per ECMAScript standards.\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#prod-LineTerminator\n    pub(super) const fn is_line_terminator(ch: u32) -> bool {\n        matches!(\n            ch,\n            0x000A /* <LF> */ | 0x000D /* <CR> */ | 0x2028 /* <LS> */ | 0x2029 /* <PS> */\n        )\n    }\n\n    fn take_string_characters<R>(\n        cursor: &mut Cursor<R>,\n        start_pos: Position,\n        terminator: StringTerminator,\n        strict: bool,\n    ) -> Result<(Vec<u16>, Span, EscapeSequence), Error>\n    where\n        R: ReadChar,\n    {\n        let mut buf = Vec::new();\n        let mut escape_sequence = EscapeSequence::empty();\n\n        loop {\n            let ch_start_pos = cursor.pos();\n            let ch = cursor.next_char()?;\n\n            match ch {\n                Some(0x0027 /* ' */) if terminator == StringTerminator::SingleQuote => break,\n                Some(0x0022 /* \" */) if terminator == StringTerminator::DoubleQuote => break,\n                Some(0x005C /* \\ */) => {\n                    let (escape_value, escape) = Self::take_escape_sequence_or_line_continuation(\n                        cursor,\n                        ch_start_pos,\n                        strict,\n                        false,\n                    )?;\n\n                    escape_sequence |= escape;\n\n                    if let Some(escape_value) = escape_value {\n                        buf.push_code_point(escape_value);\n                    }\n                }\n                Some(0x2028) => buf.push(0x2028 /* <LS> */),\n                Some(0x2029) => buf.push(0x2029 /* <PS> */),\n                Some(ch) if !Self::is_line_terminator(ch) => {\n                    buf.push_code_point(ch);\n                }\n                _ => {\n                    return Err(Error::from(io::Error::new(\n                        ErrorKind::UnexpectedEof,\n                        \"unterminated string literal\",\n                    )));\n                }\n            }\n        }\n\n        Ok((buf, Span::new(start_pos, cursor.pos()), escape_sequence))\n    }\n\n    pub(super) fn take_escape_sequence_or_line_continuation<R>(\n        cursor: &mut Cursor<R>,\n        start_pos: Position,\n        strict: bool,\n        is_template_literal: bool,\n    ) -> Result<(Option<u32>, EscapeSequence), Error>\n    where\n        R: ReadChar,\n    {\n        let escape_ch = cursor.next_char()?.ok_or_else(|| {\n            Error::from(io::Error::new(\n                ErrorKind::UnexpectedEof,\n                \"unterminated escape sequence in literal\",\n            ))\n        })?;\n\n        let escape_value = match escape_ch {\n            0x0062 /* b */ => (Some(0x0008 /* <BS> */), EscapeSequence::OTHER),\n            0x0074 /* t */ => (Some(0x0009 /* <HT> */), EscapeSequence::OTHER),\n            0x006E /* n */ => (Some(0x000A /* <LF> */), EscapeSequence::OTHER),\n            0x0076 /* v */ => (Some(0x000B /* <VT> */), EscapeSequence::OTHER),\n            0x0066 /* f */ => (Some(0x000C /* <FF> */), EscapeSequence::OTHER),\n            0x0072 /* r */ => (Some(0x000D /* <CR> */), EscapeSequence::OTHER),\n            0x0022 /* \" */ => (Some(0x0022 /* \" */), EscapeSequence::OTHER),\n            0x0027 /* ' */ => (Some(0x0027 /* ' */), EscapeSequence::OTHER),\n            0x005C /* \\ */ => (Some(0x005C /* \\ */), EscapeSequence::OTHER),\n            0x0030 /* 0 */ if cursor\n                .peek_char()?\n                .filter(|c| (0x30..=0x39 /* 0..=9 */).contains(c))\n                .is_none() =>\n                (Some(0x0000 /* NULL */), EscapeSequence::OTHER),\n            0x0078 /* x */ => {\n                (Some(Self::take_hex_escape_sequence(cursor, start_pos)?), EscapeSequence::OTHER)\n            }\n            0x0075 /* u */ => {\n                (Some(Self::take_unicode_escape_sequence(cursor, start_pos)?), EscapeSequence::OTHER)\n            }\n            0x0038 /* 8 */ | 0x0039 /* 9 */ => {\n                // Grammar: NonOctalDecimalEscapeSequence\n                if is_template_literal {\n                    return Err(Error::syntax(\n                        \"\\\\8 and \\\\9 are not allowed in template literal\",\n                        start_pos,\n                    ));\n                } else if strict {\n                    return Err(Error::syntax(\n                        \"\\\\8 and \\\\9 are not allowed in strict mode\",\n                        start_pos,\n                    ));\n                }\n                    (Some(escape_ch), EscapeSequence::NON_OCTAL_DECIMAL)\n            }\n            _ if (0x0030..=0x0037 /* '0'..='7' */).contains(&escape_ch) => {\n                if is_template_literal {\n                    return Err(Error::syntax(\n                        \"octal escape sequences are not allowed in template literal\",\n                        start_pos,\n                    ));\n                }\n\n                if strict {\n                    return Err(Error::syntax(\n                        \"octal escape sequences are not allowed in strict mode\",\n                        start_pos,\n                    ));\n                }\n\n                (Some(Self::take_legacy_octal_escape_sequence(\n                    cursor,\n                    escape_ch.try_into().expect(\"an ascii char must not fail to convert\"),\n                )?), EscapeSequence::LEGACY_OCTAL)\n            }\n            _ if Self::is_line_terminator(escape_ch) => {\n                // Grammar: LineContinuation\n                // Grammar: \\ LineTerminatorSequence\n                // LineContinuation is the empty String.\n                (None, EscapeSequence::OTHER)\n            }\n            _ => {\n                (Some(escape_ch), EscapeSequence::OTHER)\n            }\n        };\n\n        Ok(escape_value)\n    }\n\n    pub(super) fn take_unicode_escape_sequence<R>(\n        cursor: &mut Cursor<R>,\n        start_pos: Position,\n    ) -> Result<u32, Error>\n    where\n        R: ReadChar,\n    {\n        // Support \\u{X..X} (Unicode CodePoint)\n        if cursor.next_if(0x7B /* { */)? {\n            // TODO: use bytes for a bit better performance (using stack)\n            let mut code_point_buf = Vec::with_capacity(6);\n            cursor.take_until(0x7D /* } */, &mut code_point_buf)?;\n\n            let mut s = String::with_capacity(code_point_buf.len());\n            for c in code_point_buf {\n                if let Some(c) = char::from_u32(c) {\n                    s.push(c);\n                } else {\n                    return Err(Error::syntax(\n                        \"malformed Unicode character escape sequence\",\n                        start_pos,\n                    ));\n                }\n            }\n\n            let Ok(code_point) = u32::from_str_radix(&s, 16) else {\n                return Err(Error::syntax(\n                    \"malformed Unicode character escape sequence\",\n                    start_pos,\n                ));\n            };\n\n            // UTF16Encoding of a numeric code point value\n            if code_point > 0x10_FFFF {\n                return Err(Error::syntax(\n                    \"Unicode codepoint must not be greater than 0x10FFFF in escape sequence\",\n                    start_pos,\n                ));\n            }\n\n            Ok(code_point)\n        } else {\n            // Grammar: Hex4Digits\n            // Collect each character after \\u e.g \\uD83D will give \"D83D\"\n            let mut buffer = [0u32; 4];\n            buffer[0] = cursor\n                .next_char()?\n                .ok_or_else(|| Error::syntax(\"invalid Unicode escape sequence\", start_pos))?;\n            buffer[1] = cursor\n                .next_char()?\n                .ok_or_else(|| Error::syntax(\"invalid Unicode escape sequence\", start_pos))?;\n            buffer[2] = cursor\n                .next_char()?\n                .ok_or_else(|| Error::syntax(\"invalid Unicode escape sequence\", start_pos))?;\n            buffer[3] = cursor\n                .next_char()?\n                .ok_or_else(|| Error::syntax(\"invalid Unicode escape sequence\", start_pos))?;\n\n            let mut s = String::with_capacity(buffer.len());\n            for c in buffer {\n                if let Some(c) = char::from_u32(c) {\n                    s.push(c);\n                } else {\n                    return Err(Error::syntax(\"invalid Unicode escape sequence\", start_pos));\n                }\n            }\n\n            let Ok(code_point) = u16::from_str_radix(&s, 16) else {\n                return Err(Error::syntax(\"invalid Unicode escape sequence\", start_pos));\n            };\n\n            Ok(u32::from(code_point))\n        }\n    }\n\n    fn take_hex_escape_sequence<R>(\n        cursor: &mut Cursor<R>,\n        start_pos: Position,\n    ) -> Result<u32, Error>\n    where\n        R: ReadChar,\n    {\n        let mut buffer = [0u32; 2];\n        buffer[0] = cursor\n            .next_char()?\n            .ok_or_else(|| Error::syntax(\"invalid Hexadecimal escape sequence\", start_pos))?;\n        buffer[1] = cursor\n            .next_char()?\n            .ok_or_else(|| Error::syntax(\"invalid Hexadecimal escape sequence\", start_pos))?;\n\n        let mut s = String::with_capacity(buffer.len());\n        for c in buffer {\n            if let Some(c) = char::from_u32(c) {\n                s.push(c);\n            } else {\n                return Err(Error::syntax(\n                    \"invalid Hexadecimal escape sequence\",\n                    start_pos,\n                ));\n            }\n        }\n\n        let Ok(code_point) = u16::from_str_radix(&s, 16) else {\n            return Err(Error::syntax(\n                \"invalid Hexadecimal escape sequence\",\n                start_pos,\n            ));\n        };\n\n        Ok(u32::from(code_point))\n    }\n\n    fn take_legacy_octal_escape_sequence<R>(\n        cursor: &mut Cursor<R>,\n        init_byte: u8,\n    ) -> Result<u32, Error>\n    where\n        R: ReadChar,\n    {\n        // Grammar: OctalDigit\n        let mut code_point = u32::from(init_byte - b'0');\n\n        // Grammar: ZeroToThree OctalDigit\n        // Grammar: FourToSeven OctalDigit\n        if let Some(c) = cursor.peek_char()?\n            && (0x30..=0x37/* 0..=7 */).contains(&c)\n        {\n            cursor.next_char()?;\n            code_point = (code_point * 8) + c - 0x30 /* 0 */;\n\n            if (0x30..=0x33/* 0..=3 */).contains(&init_byte) {\n                // Grammar: ZeroToThree OctalDigit OctalDigit\n                if let Some(c) = cursor.peek_char()?\n                    && (0x30..=0x37/* 0..=7 */).contains(&c)\n                {\n                    cursor.next_char()?;\n                    code_point = (code_point * 8) + c - 0x30 /* 0 */;\n                }\n            }\n        }\n\n        Ok(code_point)\n    }\n}\n"
  },
  {
    "path": "core/parser/src/lexer/template.rs",
    "content": "//! Boa's lexing for ECMAScript template literals.\n\nuse crate::{\n    lexer::{Cursor, Error, Token, TokenKind, Tokenizer, string::UTF16CodeUnitsBuffer},\n    source::ReadChar,\n};\nuse boa_ast::PositionGroup;\nuse boa_interner::{Interner, Sym};\nuse std::io::{self, ErrorKind};\n\n#[derive(Debug, Clone, Copy, PartialEq, Eq)]\npub struct TemplateString {\n    /// The raw template string.\n    raw: Sym,\n\n    /// The cooked template string.\n    cooked: Option<Sym>,\n}\n\nimpl TemplateString {\n    /// Creates a new `TemplateString` with the given raw template ans start position.\n    pub fn new(raw: Sym, interner: &mut Interner) -> Self {\n        Self {\n            raw: Self::as_raw(raw, interner),\n            cooked: Self::as_cooked(raw, interner),\n        }\n    }\n\n    /// Returns the raw template string.\n    pub fn raw(self) -> Sym {\n        self.raw\n    }\n\n    /// Returns the cooked template string if it exists.\n    pub fn cooked(self) -> Option<Sym> {\n        self.cooked\n    }\n\n    /// Converts the raw template string into a mutable string slice.\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-static-semantics-templatestrings\n    fn as_raw(raw: Sym, interner: &mut Interner) -> Sym {\n        let string = interner.resolve_expect(raw).utf16();\n        let mut iter = string.iter().peekable();\n        let mut buf: Vec<u16> = Vec::new();\n        loop {\n            match iter.next() {\n                Some(0x5C /* \\ */) => {\n                    buf.push_code_point(0x5C);\n                    match iter.next() {\n                        Some(0x0D /* <CR> */) => {\n                            buf.push_code_point(0x0A);\n                        }\n                        Some(ch) => {\n                            buf.push_code_point(u32::from(*ch));\n                        }\n                        None => break,\n                    }\n                }\n                Some(0x0D /* <CR> */) => {\n                    buf.push_code_point(0x0A);\n                }\n                Some(ch) => {\n                    buf.push_code_point(u32::from(*ch));\n                }\n                None => break,\n            }\n        }\n        interner.get_or_intern(buf.as_slice())\n    }\n\n    /// Creates a new cooked template string. Returns a lexer error if it fails to cook the\n    /// template string.\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-static-semantics-templatestrings\n    fn as_cooked(raw: Sym, interner: &mut Interner) -> Option<Sym> {\n        let string = interner.resolve_expect(raw).utf16();\n        let mut iter = string.iter().peekable();\n        let mut buf: Vec<u16> = Vec::new();\n\n        loop {\n            match iter.next() {\n                Some(0x5C /* \\ */) => {\n                    let escape_value = match iter.next() {\n                        Some(0x62 /* b */) => 0x08 /* <BS> */,\n                        Some(0x74 /* t */) => 0x09 /* <HT> */,\n                        Some(0x6E /* n */) => 0x0A /* <LF> */,\n                        Some(0x76 /* v */) => 0x0B /* <VT> */,\n                        Some(0x66 /* f */) => 0x0C /* <FF> */,\n                        Some(0x72 /* r */) => 0x0D /* <CR> */,\n                        Some(0x22 /* \" */) => 0x22 /* \" */,\n                        Some(0x27 /* ' */) => 0x27 /* ' */,\n                        Some(0x5C /* \\ */) => 0x5C /* \\ */,\n                        Some(0x30 /* 0 */) if iter\n                            .peek()\n                            .filter(|ch| (0x30..=0x39 /* 0..=9 */).contains(**ch))\n                            .is_none() => 0x00 /* NULL */,\n                        // Hex Escape\n                        Some(0x078 /* x */) => {\n                            let mut s = String::with_capacity(2);\n                            s.push(char::from_u32(u32::from(*iter.next()?))?);\n                            s.push(char::from_u32(u32::from(*iter.next()?))?);\n                            u16::from_str_radix(&s, 16).ok()?.into()\n                        }\n                        // Unicode Escape\n                        Some(0x75 /* u */) => {\n                            let next = *iter.next()?;\n                            if next ==  0x7B /* { */ {\n                                let mut buffer = String::with_capacity(6);\n                                loop {\n                                    let next = *iter.next()?;\n                                    if next == 0x7D /* } */ {\n                                        break;\n                                    }\n                                    buffer.push(char::from_u32(u32::from(next))?);\n                                }\n                                let cp = u32::from_str_radix(&buffer, 16).ok()?;\n                                if cp > 0x10_FFFF {\n                                    return None;\n                                }\n                                cp\n                            } else {\n                                let mut s = String::with_capacity(4);\n                                s.push(char::from_u32(u32::from(next))?);\n                                s.push(char::from_u32(u32::from(*iter.next()?))?);\n                                s.push(char::from_u32(u32::from(*iter.next()?))?);\n                                s.push(char::from_u32(u32::from(*iter.next()?))?);\n                                u16::from_str_radix(&s, 16).ok()?.into()\n                            }\n                        }\n                        // NonOctalDecimalEscapeSequence\n                        Some(0x38 /* 8 */ | 0x39 /* 9 */) => {\n                            return None;\n                        }\n                        // LegacyOctalEscapeSequence\n                        Some(ch) if (0x30..=0x37 /* '0'..='7' */).contains(ch) => {\n                            return None;\n                        }\n                        // Line Terminator\n                        Some(0x0A /* <LF> */ | 0x0D /* <CR> */ | 0x2028 /* <LS> */ | 0x2029 /* <PS> */) => {\n                            continue;\n                        }\n                        Some(ch) => {\n                            u32::from(*ch)\n                        }\n                        None => return None,\n                    };\n                    buf.push_code_point(escape_value);\n                }\n                Some(0x0D /* <CR> */) => {\n                    buf.push_code_point(0x0A);\n                }\n                Some(ch) => {\n                    buf.push_code_point(u32::from(*ch));\n                }\n                None => break,\n            }\n        }\n\n        Some(interner.get_or_intern(buf.as_slice()))\n    }\n}\n\n/// Template literal lexing.\n///\n/// Expects: Initial `` ` `` to already be consumed by cursor.\n///\n/// More information:\n///  - [ECMAScript reference][spec]\n///  - [MDN documentation][mdn]\n///\n/// [spec]: https://tc39.es/ecma262/#sec-template-literals\n/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Template_literals\n#[derive(Debug, Clone, Copy)]\npub(super) struct TemplateLiteral;\n\nimpl<R> Tokenizer<R> for TemplateLiteral {\n    fn lex(\n        &mut self,\n        cursor: &mut Cursor<R>,\n        start_pos: PositionGroup,\n        interner: &mut Interner,\n    ) -> Result<Token, Error>\n    where\n        R: ReadChar,\n    {\n        let mut buf = Vec::new();\n        loop {\n            let ch = cursor.next_char()?.ok_or_else(|| {\n                Error::from(io::Error::new(\n                    ErrorKind::UnexpectedEof,\n                    \"unterminated template literal\",\n                ))\n            })?;\n\n            match ch {\n                // `\n                0x0060 => {\n                    let raw_sym = interner.get_or_intern(&buf[..]);\n                    let template_string = TemplateString::new(raw_sym, interner);\n\n                    return Ok(Token::new_by_position_group(\n                        TokenKind::template_no_substitution(template_string),\n                        start_pos,\n                        cursor.pos_group(),\n                    ));\n                }\n                // $\n                0x0024 if cursor.next_if(0x7B /* { */)? => {\n                    let raw_sym = interner.get_or_intern(&buf[..]);\n                    let template_string = TemplateString::new(raw_sym, interner);\n\n                    return Ok(Token::new_by_position_group(\n                        TokenKind::template_middle(template_string),\n                        start_pos,\n                        cursor.pos_group(),\n                    ));\n                }\n                // \\\n                0x005C => {\n                    let escape_ch = cursor.peek_char()?.ok_or_else(|| {\n                        Error::from(io::Error::new(\n                            ErrorKind::UnexpectedEof,\n                            \"unterminated escape sequence in literal\",\n                        ))\n                    })?;\n\n                    buf.push(u16::from(b'\\\\'));\n                    let escape_ch = match escape_ch {\n                        // `\n                        0x0060 => Some(0x0060),\n                        // $\n                        0x0024 => Some(0x0024),\n                        // \\\n                        0x005C => Some(0x005C),\n                        _ => None,\n                    };\n                    if let Some(ch) = escape_ch {\n                        let _ = cursor.next_char()?.expect(\"already checked next character\");\n                        buf.push(ch);\n                    }\n                }\n                ch => {\n                    buf.push_code_point(ch);\n                }\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "core/parser/src/lexer/tests.rs",
    "content": "//! Tests for the lexer.\n\nuse crate::lexer::{\n    Cursor, Error, Interner, Lexer, Punctuator, TokenKind,\n    template::TemplateString,\n    token::{ContainsEscapeSequence, EscapeSequence, Numeric},\n};\nuse crate::source::ReadChar;\nuse boa_ast::{Keyword, Position, Span, Spanned};\nuse boa_interner::Sym;\nuse boa_macros::utf16;\nuse strum::IntoEnumIterator;\n\nfn span(start: (u32, u32), end: (u32, u32)) -> Span {\n    Span::new(Position::new(start.0, start.1), Position::new(end.0, end.1))\n}\n\nfn expect_tokens<R>(lexer: &mut Lexer<R>, expected: &[TokenKind], interner: &mut Interner)\nwhere\n    R: ReadChar,\n{\n    for expect in expected {\n        assert_eq!(&lexer.next(interner).unwrap().unwrap().kind(), &expect);\n    }\n\n    assert!(\n        lexer.next(interner).unwrap().is_none(),\n        \"Unexpected extra token lexed at end of input\"\n    );\n}\n\n#[test]\nfn check_single_line_comment() {\n    let s1 = \"var \\n//This is a comment\\ntrue\";\n    let mut lexer = Lexer::from(s1.as_bytes());\n    let interner = &mut Interner::default();\n\n    let expected = [\n        TokenKind::Keyword((Keyword::Var, false)),\n        TokenKind::LineTerminator,\n        TokenKind::LineTerminator,\n        TokenKind::BooleanLiteral((true, ContainsEscapeSequence(false))),\n    ];\n\n    expect_tokens(&mut lexer, &expected, interner);\n}\n\n#[test]\nfn check_single_line_comment_with_crlf_ending() {\n    let s1 = \"var \\r\\n//This is a comment\\r\\ntrue\";\n    let mut lexer = Lexer::from(s1.as_bytes());\n    let interner = &mut Interner::default();\n\n    let expected = [\n        TokenKind::Keyword((Keyword::Var, false)),\n        TokenKind::LineTerminator,\n        TokenKind::LineTerminator,\n        TokenKind::BooleanLiteral((true, ContainsEscapeSequence(false))),\n    ];\n\n    expect_tokens(&mut lexer, &expected, interner);\n}\n\n#[test]\nfn check_multi_line_comment() {\n    let s = \"var /* await \\n break \\n*/ x\";\n    let mut lexer = Lexer::from(s.as_bytes());\n    let interner = &mut Interner::default();\n\n    let sym = interner.get_or_intern_static(\"x\", utf16!(\"x\"));\n    let expected = [\n        TokenKind::Keyword((Keyword::Var, false)),\n        TokenKind::LineTerminator,\n        TokenKind::identifier(sym),\n    ];\n\n    expect_tokens(&mut lexer, &expected, interner);\n}\n\n#[test]\nfn check_identifier() {\n    let s = \"x x1 _x $x __ $$ Ѐ ЀЀ x\\u{200C}\\u{200D} \\\\u0078 \\\\u0078\\\\u0078 \\\\u{0078}x\\\\u{0078}\";\n    let mut lexer = Lexer::from(s.as_bytes());\n    let interner = &mut Interner::default();\n\n    let expected = [\n        TokenKind::identifier(interner.get_or_intern_static(\"x\", utf16!(\"x\"))),\n        TokenKind::identifier(interner.get_or_intern_static(\"x1\", utf16!(\"x1\"))),\n        TokenKind::identifier(interner.get_or_intern_static(\"_x\", utf16!(\"_x\"))),\n        TokenKind::identifier(interner.get_or_intern_static(\"$x\", utf16!(\"$x\"))),\n        TokenKind::identifier(interner.get_or_intern_static(\"__\", utf16!(\"__\"))),\n        TokenKind::identifier(interner.get_or_intern_static(\"$$\", utf16!(\"$$\"))),\n        TokenKind::identifier(interner.get_or_intern_static(\"Ѐ\", utf16!(\"Ѐ\"))),\n        TokenKind::identifier(interner.get_or_intern_static(\"ЀЀ\", utf16!(\"ЀЀ\"))),\n        TokenKind::identifier(\n            interner.get_or_intern_static(\"x\\u{200C}\\u{200D}\", utf16!(\"x\\u{200C}\\u{200D}\")),\n        ),\n        TokenKind::IdentifierName((\n            interner.get_or_intern_static(\"x\", utf16!(\"x\")),\n            ContainsEscapeSequence(true),\n        )),\n        TokenKind::IdentifierName((\n            interner.get_or_intern_static(\"xx\", utf16!(\"xx\")),\n            ContainsEscapeSequence(true),\n        )),\n        TokenKind::IdentifierName((\n            interner.get_or_intern_static(\"xxx\", utf16!(\"xxx\")),\n            ContainsEscapeSequence(true),\n        )),\n    ];\n\n    expect_tokens(&mut lexer, &expected, interner);\n}\n\n#[test]\nfn check_invalid_identifier_start() {\n    let invalid_identifier_starts = [\"\\u{200C}\", \"\\u{200D}\", \"😀\"];\n\n    for s in &invalid_identifier_starts {\n        let mut lexer = Lexer::from(s.as_bytes());\n        let interner = &mut Interner::default();\n        lexer\n            .next(interner)\n            .expect_err(\"Invalid identifier start not rejected as expected\");\n    }\n}\n\n#[test]\nfn check_invalid_identifier_part() {\n    let invalid_identifier_parts = [\" \", \"\\n\", \".\", \"*\", \"😀\", \"\\u{007F}\"];\n    let interner = &mut Interner::default();\n\n    let sym = interner.get_or_intern_static(\"x\", utf16!(\"x\"));\n    for part in &invalid_identifier_parts {\n        let s = String::from(\"x\") + part;\n        let mut lexer = Lexer::from(s.as_bytes());\n        let interner = &mut Interner::default();\n        assert_eq!(\n            lexer.next(interner).unwrap().unwrap().kind(),\n            &TokenKind::identifier(sym)\n        );\n    }\n}\n\n#[test]\nfn check_string() {\n    let s = \"'aaa' \\\"bbb\\\"\";\n    let mut lexer = Lexer::from(s.as_bytes());\n    let interner = &mut Interner::default();\n\n    let a_sym = interner.get_or_intern_static(\"aaa\", utf16!(\"aaa\"));\n    let b_sym = interner.get_or_intern_static(\"bbb\", utf16!(\"bbb\"));\n    let expected = [\n        TokenKind::string_literal(a_sym, EscapeSequence::empty()),\n        TokenKind::string_literal(b_sym, EscapeSequence::empty()),\n    ];\n\n    expect_tokens(&mut lexer, &expected, interner);\n}\n\n#[test]\nfn check_template_literal_simple() {\n    let s = \"`I'm a template literal`\";\n    let mut lexer = Lexer::from(s.as_bytes());\n    let interner = &mut Interner::default();\n\n    let sym =\n        interner.get_or_intern_static(\"I'm a template literal\", utf16!(\"I'm a template literal\"));\n\n    assert_eq!(\n        lexer.next(interner).unwrap().unwrap().kind(),\n        &TokenKind::template_no_substitution(TemplateString::new(sym, interner))\n    );\n}\n\n#[test]\nfn check_template_literal_unterminated() {\n    let s = \"`I'm a template\";\n    let mut lexer = Lexer::from(s.as_bytes());\n    let interner = &mut Interner::default();\n\n    lexer\n        .next(interner)\n        .expect_err(\"Lexer did not handle unterminated literal with error\");\n}\n\n#[test]\nfn check_punctuators() {\n    // https://tc39.es/ecma262/#sec-punctuators\n    let s = \"{ ( ) [ ] . ... ; , < > <= >= == != === !== \\\n             + - * % -- << >> >>> & | ^ ! ~ && || ? : \\\n             = += -= *= &= **= ++ ** <<= >>= >>>= &= |= ^= => ?? ??= &&= ||= ?.\";\n    let mut lexer = Lexer::from(s.as_bytes());\n    let interner = &mut Interner::default();\n\n    let expected = [\n        TokenKind::Punctuator(Punctuator::OpenBlock),\n        TokenKind::Punctuator(Punctuator::OpenParen),\n        TokenKind::Punctuator(Punctuator::CloseParen),\n        TokenKind::Punctuator(Punctuator::OpenBracket),\n        TokenKind::Punctuator(Punctuator::CloseBracket),\n        TokenKind::Punctuator(Punctuator::Dot),\n        TokenKind::Punctuator(Punctuator::Spread),\n        TokenKind::Punctuator(Punctuator::Semicolon),\n        TokenKind::Punctuator(Punctuator::Comma),\n        TokenKind::Punctuator(Punctuator::LessThan),\n        TokenKind::Punctuator(Punctuator::GreaterThan),\n        TokenKind::Punctuator(Punctuator::LessThanOrEq),\n        TokenKind::Punctuator(Punctuator::GreaterThanOrEq),\n        TokenKind::Punctuator(Punctuator::Eq),\n        TokenKind::Punctuator(Punctuator::NotEq),\n        TokenKind::Punctuator(Punctuator::StrictEq),\n        TokenKind::Punctuator(Punctuator::StrictNotEq),\n        TokenKind::Punctuator(Punctuator::Add),\n        TokenKind::Punctuator(Punctuator::Sub),\n        TokenKind::Punctuator(Punctuator::Mul),\n        TokenKind::Punctuator(Punctuator::Mod),\n        TokenKind::Punctuator(Punctuator::Dec),\n        TokenKind::Punctuator(Punctuator::LeftSh),\n        TokenKind::Punctuator(Punctuator::RightSh),\n        TokenKind::Punctuator(Punctuator::URightSh),\n        TokenKind::Punctuator(Punctuator::And),\n        TokenKind::Punctuator(Punctuator::Or),\n        TokenKind::Punctuator(Punctuator::Xor),\n        TokenKind::Punctuator(Punctuator::Not),\n        TokenKind::Punctuator(Punctuator::Neg),\n        TokenKind::Punctuator(Punctuator::BoolAnd),\n        TokenKind::Punctuator(Punctuator::BoolOr),\n        TokenKind::Punctuator(Punctuator::Question),\n        TokenKind::Punctuator(Punctuator::Colon),\n        TokenKind::Punctuator(Punctuator::Assign),\n        TokenKind::Punctuator(Punctuator::AssignAdd),\n        TokenKind::Punctuator(Punctuator::AssignSub),\n        TokenKind::Punctuator(Punctuator::AssignMul),\n        TokenKind::Punctuator(Punctuator::AssignAnd),\n        TokenKind::Punctuator(Punctuator::AssignPow),\n        TokenKind::Punctuator(Punctuator::Inc),\n        TokenKind::Punctuator(Punctuator::Exp),\n        TokenKind::Punctuator(Punctuator::AssignLeftSh),\n        TokenKind::Punctuator(Punctuator::AssignRightSh),\n        TokenKind::Punctuator(Punctuator::AssignURightSh),\n        TokenKind::Punctuator(Punctuator::AssignAnd),\n        TokenKind::Punctuator(Punctuator::AssignOr),\n        TokenKind::Punctuator(Punctuator::AssignXor),\n        TokenKind::Punctuator(Punctuator::Arrow),\n        TokenKind::Punctuator(Punctuator::Coalesce),\n        TokenKind::Punctuator(Punctuator::AssignCoalesce),\n        TokenKind::Punctuator(Punctuator::AssignBoolAnd),\n        TokenKind::Punctuator(Punctuator::AssignBoolOr),\n        TokenKind::Punctuator(Punctuator::Optional),\n    ];\n\n    expect_tokens(&mut lexer, &expected, interner);\n}\n\n#[test]\nfn check_punctuator_at_the_end() {\n    // Use strum's `EnumIter` to auto-iterate all `Punctuator` variants,\n    // skipping those that the lexer cannot handle at the end of `var a = b <punct>`:\n    //   - `Assign`: already consumed by `=` in `var a = b`\n    //   - `AssignDiv`: `/=` is treated as an unclosed regular expression\n    let last_expected =\n        Punctuator::iter().filter(|p| !matches!(p, Punctuator::Assign | Punctuator::AssignDiv));\n\n    for last in last_expected {\n        let s = format!(\"var a = b {}\", last.as_str());\n        let mut lexer = Lexer::from(s.as_bytes());\n        let interner = &mut Interner::default();\n\n        let expected = [\n            TokenKind::Keyword((Keyword::Var, false)),\n            TokenKind::identifier(interner.get_or_intern_static(\"a\", utf16!(\"a\"))),\n            TokenKind::Punctuator(Punctuator::Assign),\n            TokenKind::identifier(interner.get_or_intern_static(\"b\", utf16!(\"b\"))),\n            TokenKind::Punctuator(last),\n        ];\n\n        expect_tokens(&mut lexer, &expected, interner);\n    }\n}\n\n#[test]\nfn check_keywords() {\n    // https://tc39.es/ecma262/#sec-keywords\n    let s = \"await break case catch class const continue debugger default delete \\\n             do else export extends finally for function if import in instanceof \\\n             new return super switch this throw try typeof var void while with yield\";\n\n    let mut lexer = Lexer::from(s.as_bytes());\n    let interner = &mut Interner::default();\n\n    let expected = [\n        TokenKind::Keyword((Keyword::Await, false)),\n        TokenKind::Keyword((Keyword::Break, false)),\n        TokenKind::Keyword((Keyword::Case, false)),\n        TokenKind::Keyword((Keyword::Catch, false)),\n        TokenKind::Keyword((Keyword::Class, false)),\n        TokenKind::Keyword((Keyword::Const, false)),\n        TokenKind::Keyword((Keyword::Continue, false)),\n        TokenKind::Keyword((Keyword::Debugger, false)),\n        TokenKind::Keyword((Keyword::Default, false)),\n        TokenKind::Keyword((Keyword::Delete, false)),\n        TokenKind::Keyword((Keyword::Do, false)),\n        TokenKind::Keyword((Keyword::Else, false)),\n        TokenKind::Keyword((Keyword::Export, false)),\n        TokenKind::Keyword((Keyword::Extends, false)),\n        TokenKind::Keyword((Keyword::Finally, false)),\n        TokenKind::Keyword((Keyword::For, false)),\n        TokenKind::Keyword((Keyword::Function, false)),\n        TokenKind::Keyword((Keyword::If, false)),\n        TokenKind::Keyword((Keyword::Import, false)),\n        TokenKind::Keyword((Keyword::In, false)),\n        TokenKind::Keyword((Keyword::InstanceOf, false)),\n        TokenKind::Keyword((Keyword::New, false)),\n        TokenKind::Keyword((Keyword::Return, false)),\n        TokenKind::Keyword((Keyword::Super, false)),\n        TokenKind::Keyword((Keyword::Switch, false)),\n        TokenKind::Keyword((Keyword::This, false)),\n        TokenKind::Keyword((Keyword::Throw, false)),\n        TokenKind::Keyword((Keyword::Try, false)),\n        TokenKind::Keyword((Keyword::TypeOf, false)),\n        TokenKind::Keyword((Keyword::Var, false)),\n        TokenKind::Keyword((Keyword::Void, false)),\n        TokenKind::Keyword((Keyword::While, false)),\n        TokenKind::Keyword((Keyword::With, false)),\n        TokenKind::Keyword((Keyword::Yield, false)),\n    ];\n\n    expect_tokens(&mut lexer, &expected, interner);\n}\n\n#[test]\nfn check_variable_definition_tokens() {\n    let s = \"let a = 'hello';\";\n    let mut lexer = Lexer::from(s.as_bytes());\n    let interner = &mut Interner::default();\n\n    let a_sym = interner.get_or_intern_static(\"a\", utf16!(\"a\"));\n    let hello_sym = interner.get_or_intern_static(\"hello\", utf16!(\"hello\"));\n    let expected = [\n        TokenKind::Keyword((Keyword::Let, false)),\n        TokenKind::identifier(a_sym),\n        TokenKind::Punctuator(Punctuator::Assign),\n        TokenKind::string_literal(hello_sym, EscapeSequence::empty()),\n        TokenKind::Punctuator(Punctuator::Semicolon),\n    ];\n\n    expect_tokens(&mut lexer, &expected, interner);\n}\n\n#[test]\nfn check_positions() {\n    let s = r#\"console.log(\"hello world\"); // Test\"#;\n    // --------123456789\n    let mut lexer = Lexer::from(s.as_bytes());\n    let interner = &mut Interner::default();\n\n    // The first column is 1 (not zero indexed)\n    assert_eq!(\n        lexer.next(interner).unwrap().unwrap().span(),\n        span((1, 1), (1, 8))\n    );\n\n    // Dot Token starts on column 8\n    assert_eq!(\n        lexer.next(interner).unwrap().unwrap().span(),\n        span((1, 8), (1, 9))\n    );\n\n    // Log Token starts on column 9\n    assert_eq!(\n        lexer.next(interner).unwrap().unwrap().span(),\n        span((1, 9), (1, 12))\n    );\n\n    // Open parenthesis token starts on column 12\n    assert_eq!(\n        lexer.next(interner).unwrap().unwrap().span(),\n        span((1, 12), (1, 13))\n    );\n\n    // String token starts on column 13\n    assert_eq!(\n        lexer.next(interner).unwrap().unwrap().span(),\n        span((1, 13), (1, 26))\n    );\n\n    // Close parenthesis token starts on column 26.\n    assert_eq!(\n        lexer.next(interner).unwrap().unwrap().span(),\n        span((1, 26), (1, 27))\n    );\n\n    // Semi Colon token starts on column 35\n    assert_eq!(\n        lexer.next(interner).unwrap().unwrap().span(),\n        span((1, 27), (1, 28))\n    );\n}\n\n#[test]\nfn check_positions_codepoint() {\n    let s = r#\"console.log(\"hello world\\u{2764}\"); // Test\"#;\n    // --------123456789\n    let mut lexer = Lexer::from(s.as_bytes());\n    let interner = &mut Interner::default();\n\n    // The first column is 1 (not zero indexed)\n    assert_eq!(\n        lexer.next(interner).unwrap().unwrap().span(),\n        span((1, 1), (1, 8))\n    );\n\n    // Dot Token starts on column 8\n    assert_eq!(\n        lexer.next(interner).unwrap().unwrap().span(),\n        span((1, 8), (1, 9))\n    );\n\n    // Log Token starts on column 9\n    assert_eq!(\n        lexer.next(interner).unwrap().unwrap().span(),\n        span((1, 9), (1, 12))\n    );\n\n    // Open parenthesis token starts on column 12\n    assert_eq!(\n        lexer.next(interner).unwrap().unwrap().span(),\n        span((1, 12), (1, 13))\n    );\n\n    // String token starts on column 13\n    assert_eq!(\n        lexer.next(interner).unwrap().unwrap().span(),\n        span((1, 13), (1, 34))\n    );\n\n    // Close parenthesis token starts on column 34\n    assert_eq!(\n        lexer.next(interner).unwrap().unwrap().span(),\n        span((1, 34), (1, 35))\n    );\n\n    // Semi Colon token starts on column 35\n    assert_eq!(\n        lexer.next(interner).unwrap().unwrap().span(),\n        span((1, 35), (1, 36))\n    );\n}\n\n#[test]\nfn check_line_numbers() {\n    let s = \"x\\ny\\n\";\n\n    let mut lexer = Lexer::from(s.as_bytes());\n    let interner = &mut Interner::default();\n\n    assert_eq!(\n        lexer.next(interner).unwrap().unwrap().span(),\n        span((1, 1), (1, 2))\n    );\n    assert_eq!(\n        lexer.next(interner).unwrap().unwrap().span(),\n        span((1, 2), (2, 1))\n    );\n    assert_eq!(\n        lexer.next(interner).unwrap().unwrap().span(),\n        span((2, 1), (2, 2))\n    );\n    assert_eq!(\n        lexer.next(interner).unwrap().unwrap().span(),\n        span((2, 2), (3, 1))\n    );\n}\n\n// Increment/Decrement\n#[test]\nfn check_decrement_advances_lexer_2_places() {\n    // Here we want an example of decrementing an integer\n    let mut lexer = Lexer::from(&b\"let a = b--;\"[..]);\n    let interner = &mut Interner::default();\n\n    for _ in 0..4 {\n        lexer.next(interner).unwrap();\n    }\n\n    assert_eq!(\n        lexer.next(interner).unwrap().unwrap().kind(),\n        &TokenKind::Punctuator(Punctuator::Dec)\n    );\n    // Decrementing means adding 2 characters '--', the lexer should consume it as a single token\n    // and move the cursor forward by 2, meaning the next token should be a semicolon\n\n    assert_eq!(\n        lexer.next(interner).unwrap().unwrap().kind(),\n        &TokenKind::Punctuator(Punctuator::Semicolon)\n    );\n}\n\n#[test]\nfn single_int() {\n    let mut lexer = Lexer::from(&b\"52\"[..]);\n    let interner = &mut Interner::default();\n\n    let expected = [TokenKind::numeric_literal(52)];\n\n    expect_tokens(&mut lexer, &expected, interner);\n}\n\n#[test]\nfn numbers() {\n    let mut lexer = Lexer::from(\n        \"1 2 0x34 056 7.89 42. 5e3 5e+3 5e-3 0b10 0O123 0999 1.0e1 1.0e-1 1.0E1 1E1 0.0 0.12 -32\"\n            .as_bytes(),\n    );\n    let interner = &mut Interner::default();\n\n    let expected = [\n        TokenKind::numeric_literal(1),\n        TokenKind::numeric_literal(2),\n        TokenKind::numeric_literal(52),\n        TokenKind::numeric_literal(46),\n        TokenKind::numeric_literal(7.89),\n        TokenKind::numeric_literal(42),\n        TokenKind::numeric_literal(5000),\n        TokenKind::numeric_literal(5000),\n        TokenKind::numeric_literal(0.005),\n        TokenKind::numeric_literal(2),\n        TokenKind::numeric_literal(83),\n        TokenKind::numeric_literal(999),\n        TokenKind::numeric_literal(10),\n        TokenKind::numeric_literal(0.1),\n        TokenKind::numeric_literal(10),\n        TokenKind::numeric_literal(10),\n        TokenKind::numeric_literal(0),\n        TokenKind::numeric_literal(0.12),\n        TokenKind::Punctuator(Punctuator::Sub),\n        TokenKind::numeric_literal(32),\n    ];\n\n    expect_tokens(&mut lexer, &expected, interner);\n}\n\n#[test]\nfn numbers_with_separators() {\n    let mut lexer = Lexer::from(\n        \"1_0 2_0 0x3_4 056 7.8_9 4_2. 5_0e2 5_0e+2 5_0e-4 0b1_0 1_0.0_0e2 1.0E-0_1 -3_2\".as_bytes(),\n    );\n    let interner = &mut Interner::default();\n\n    let expected = [\n        TokenKind::numeric_literal(10),\n        TokenKind::numeric_literal(20),\n        TokenKind::numeric_literal(52),\n        TokenKind::numeric_literal(46),\n        TokenKind::numeric_literal(7.89),\n        TokenKind::numeric_literal(42),\n        TokenKind::numeric_literal(5000),\n        TokenKind::numeric_literal(5000),\n        TokenKind::numeric_literal(0.005),\n        TokenKind::numeric_literal(2),\n        TokenKind::numeric_literal(1000),\n        TokenKind::numeric_literal(0.1),\n        TokenKind::Punctuator(Punctuator::Sub),\n        TokenKind::numeric_literal(32),\n    ];\n\n    expect_tokens(&mut lexer, &expected, interner);\n}\n\n#[test]\nfn numbers_with_bad_separators() {\n    let numbers = [\n        \"0b_10\", \"0x_10\", \"10_\", \"1._10\", \"1e+_10\", \"1E_10\", \"10__00\",\n    ];\n\n    for n in &numbers {\n        let mut lexer = Lexer::from(n.as_bytes());\n        let interner = &mut Interner::default();\n        assert!(lexer.next(interner).is_err());\n    }\n}\n\n#[test]\nfn big_exp_numbers() {\n    let mut lexer = Lexer::from(&b\"1.0e25 1.0e36 9.0e50\"[..]);\n    let interner = &mut Interner::default();\n\n    let expected = [\n        TokenKind::numeric_literal(10_000_000_000_000_000_000_000_000.0),\n        TokenKind::numeric_literal(1_000_000_000_000_000_000_000_000_000_000_000_000.0),\n        TokenKind::numeric_literal(\n            900_000_000_000_000_000_000_000_000_000_000_000_000_000_000_000_000.0,\n        ),\n    ];\n\n    expect_tokens(&mut lexer, &expected, interner);\n}\n\n#[test]\nfn big_literal_numbers() {\n    let mut lexer = Lexer::from(&b\"10000000000000000000000000\"[..]);\n    let interner = &mut Interner::default();\n\n    let expected = [TokenKind::numeric_literal(\n        10_000_000_000_000_000_000_000_000.0,\n    )];\n\n    expect_tokens(&mut lexer, &expected, interner);\n}\n\n#[test]\nfn implicit_octal_edge_case() {\n    let mut lexer = Lexer::from(&b\"044.5 094.5\"[..]);\n    let interner = &mut Interner::default();\n\n    let expected = [\n        TokenKind::numeric_literal(36),\n        TokenKind::numeric_literal(0.5),\n        TokenKind::numeric_literal(94.5),\n    ];\n\n    expect_tokens(&mut lexer, &expected, interner);\n}\n\n#[test]\nfn hexadecimal_edge_case() {\n    let mut lexer = Lexer::from(&b\"0xffff.ff 0xffffff\"[..]);\n    let interner = &mut Interner::default();\n\n    let sym = interner.get_or_intern_static(\"ff\", utf16!(\"ff\"));\n    let expected = [\n        TokenKind::numeric_literal(0xffff),\n        TokenKind::Punctuator(Punctuator::Dot),\n        TokenKind::identifier(sym),\n        TokenKind::numeric_literal(0x00ff_ffff),\n    ];\n\n    expect_tokens(&mut lexer, &expected, interner);\n}\n\n#[test]\nfn single_number_without_semicolon() {\n    let mut lexer = Lexer::from(&b\"1\"[..]);\n    let interner = &mut Interner::default();\n\n    let expected = [TokenKind::numeric_literal(Numeric::Integer(1))];\n\n    expect_tokens(&mut lexer, &expected, interner);\n}\n\n#[test]\nfn number_followed_by_dot() {\n    let mut lexer = Lexer::from(&b\"1..\"[..]);\n    let interner = &mut Interner::default();\n\n    let expected = [\n        TokenKind::numeric_literal(1),\n        TokenKind::Punctuator(Punctuator::Dot),\n    ];\n\n    expect_tokens(&mut lexer, &expected, interner);\n}\n\n#[test]\nfn regex_literal() {\n    let mut lexer = Lexer::from(&b\"/(?:)/\"[..]);\n    let interner = &mut Interner::default();\n\n    let expected = [TokenKind::regular_expression_literal(\n        interner.get_or_intern_static(\"(?:)\", utf16!(\"(?:)\")),\n        Sym::EMPTY_STRING,\n    )];\n\n    expect_tokens(&mut lexer, &expected, interner);\n}\n\n#[test]\nfn regex_equals_following_assignment() {\n    let mut lexer = Lexer::from(&b\"const myRegex = /=/;\"[..]);\n    let interner = &mut Interner::default();\n\n    let expected = [\n        TokenKind::Keyword((Keyword::Const, false)),\n        TokenKind::identifier(interner.get_or_intern_static(\"myRegex\", utf16!(\"myRegex\"))),\n        TokenKind::Punctuator(Punctuator::Assign),\n        TokenKind::regular_expression_literal(\n            interner.get_or_intern_static(\"=\", utf16!(\"=\")),\n            Sym::EMPTY_STRING,\n        ),\n        TokenKind::Punctuator(Punctuator::Semicolon),\n    ];\n\n    expect_tokens(&mut lexer, &expected, interner);\n}\n\n#[test]\nfn regex_literal_flags() {\n    let mut lexer = Lexer::from(&br\"/\\/[^\\/]*\\/*/gmi\"[..]);\n    let interner = &mut Interner::default();\n\n    let expected = [TokenKind::regular_expression_literal(\n        interner.get_or_intern_static(\"\\\\/[^\\\\/]*\\\\/*\", utf16!(\"\\\\/[^\\\\/]*\\\\/*\")),\n        interner.get_or_intern_static(\"gim\", utf16!(\"gim\")),\n    )];\n\n    expect_tokens(&mut lexer, &expected, interner);\n}\n\n#[test]\nfn regex_literal_flags_err() {\n    let mut lexer = Lexer::from(&br\"/\\/[^\\/]*\\/*/gmip\"[..]);\n    let interner = &mut Interner::default();\n\n    lexer\n        .next(interner)\n        .expect_err(\"Lexer did not handle regex literal with error\");\n\n    let mut lexer = Lexer::from(&br\"/\\/[^\\/]*\\/*/gmii\"[..]);\n    let interner = &mut Interner::default();\n\n    lexer\n        .next(interner)\n        .expect_err(\"Lexer did not handle regex literal with error\");\n}\n\n#[test]\nfn addition_no_spaces() {\n    let mut lexer = Lexer::from(&b\"1+1\"[..]);\n    let interner = &mut Interner::default();\n\n    let expected = [\n        TokenKind::numeric_literal(1),\n        TokenKind::Punctuator(Punctuator::Add),\n        TokenKind::numeric_literal(1),\n    ];\n\n    expect_tokens(&mut lexer, &expected, interner);\n}\n\n#[test]\nfn addition_no_spaces_left_side() {\n    let mut lexer = Lexer::from(&b\"1+ 1\"[..]);\n    let interner = &mut Interner::default();\n\n    let expected = [\n        TokenKind::numeric_literal(1),\n        TokenKind::Punctuator(Punctuator::Add),\n        TokenKind::numeric_literal(1),\n    ];\n\n    expect_tokens(&mut lexer, &expected, interner);\n}\n\n#[test]\nfn addition_no_spaces_right_side() {\n    let mut lexer = Lexer::from(&b\"1 +1\"[..]);\n    let interner = &mut Interner::default();\n\n    let expected = [\n        TokenKind::numeric_literal(1),\n        TokenKind::Punctuator(Punctuator::Add),\n        TokenKind::numeric_literal(1),\n    ];\n\n    expect_tokens(&mut lexer, &expected, interner);\n}\n\n#[test]\nfn addition_no_spaces_e_number_left_side() {\n    let mut lexer = Lexer::from(&b\"1e2+ 1\"[..]);\n    let interner = &mut Interner::default();\n\n    let expected = [\n        TokenKind::numeric_literal(100),\n        TokenKind::Punctuator(Punctuator::Add),\n        TokenKind::numeric_literal(1),\n    ];\n\n    expect_tokens(&mut lexer, &expected, interner);\n}\n\n#[test]\nfn addition_no_spaces_e_number_right_side() {\n    let mut lexer = Lexer::from(&b\"1 +1e3\"[..]);\n    let interner = &mut Interner::default();\n\n    let expected = [\n        TokenKind::numeric_literal(1),\n        TokenKind::Punctuator(Punctuator::Add),\n        TokenKind::numeric_literal(1000),\n    ];\n\n    expect_tokens(&mut lexer, &expected, interner);\n}\n\n#[test]\nfn addition_no_spaces_e_number() {\n    let mut lexer = Lexer::from(&b\"1e3+1e11\"[..]);\n    let interner = &mut Interner::default();\n\n    let expected = [\n        TokenKind::numeric_literal(1000),\n        TokenKind::Punctuator(Punctuator::Add),\n        TokenKind::numeric_literal(100_000_000_000.0),\n    ];\n\n    expect_tokens(&mut lexer, &expected, interner);\n}\n\n#[test]\nfn take_array_alphabetic_simple() {\n    let mut cur = Cursor::from(&b\"abc0defghijk\"[..]);\n\n    let mut buf: [u32; 8] = [0; 8];\n\n    let n = cur.take_array_alphabetic(&mut buf).unwrap();\n\n    assert_eq!(\n        buf.iter()\n            .filter(|c| **c != 0)\n            .filter_map(|c| char::from_u32(*c))\n            .collect::<Vec<_>>()\n            .as_slice(),\n        &['a', 'b', 'c']\n    );\n    assert_eq!(n, 3);\n}\n\n#[test]\nfn take_array_alphabetic_immediate_stop() {\n    let mut cur = Cursor::from(&b\"0abcdefghijk\"[..]);\n\n    let mut buf: [u32; 8] = [0; 8];\n\n    let n = cur.take_array_alphabetic(&mut buf).unwrap();\n\n    assert!(buf.iter().all(|c| *c == 0));\n    assert_eq!(n, 0);\n}\n\n#[test]\nfn take_array_alphabetic_entire_str() {\n    let mut cur = Cursor::from(&b\"abcdefghijk\"[..]);\n\n    let mut buf: [u32; 16] = [0; 16];\n\n    let n = cur.take_array_alphabetic(&mut buf).unwrap();\n\n    assert_eq!(\n        buf.iter()\n            .filter(|c| **c != 0)\n            .filter_map(|c| char::from_u32(*c))\n            .collect::<Vec<_>>()\n            .as_slice(),\n        &['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k']\n    );\n    assert_eq!(n, 11);\n}\n\n#[test]\nfn take_array_alphabetic_non_stop() {\n    let mut cur = Cursor::from(\"abcde\".as_bytes());\n\n    let mut buf: [u32; 16] = [0; 16];\n    let n = cur.take_array_alphabetic(&mut buf).unwrap();\n\n    assert_eq!(buf.iter().filter(|c| **c != 0).count(), 5);\n    assert_eq!(n, 5);\n}\n\n#[test]\nfn take_array_alphabetic_stop() {\n    let mut cur = Cursor::from(\"abcde\".as_bytes());\n\n    let mut buf: [u32; 4] = [0; 4];\n    let n = cur.take_array_alphabetic(&mut buf).unwrap();\n\n    assert_eq!(buf.iter().filter(|c| **c != 0).count(), 4);\n    assert_eq!(\n        buf.iter()\n            .filter(|c| **c != 0)\n            .filter_map(|c| char::from_u32(*c))\n            .collect::<Vec<_>>()\n            .as_slice(),\n        &['a', 'b', 'c', 'd']\n    );\n    assert_eq!(n, 5);\n}\n\n#[test]\nfn illegal_following_numeric_literal() {\n    // Checks as per https://tc39.es/ecma262/#sec-literals-numeric-literals that a NumericLiteral cannot\n    // be immediately followed by an IdentifierStart or DecimalDigit.\n\n    // Decimal Digit\n    let mut lexer = Lexer::from(&b\"11.6n3\"[..]);\n    let interner = &mut Interner::default();\n\n    let err = lexer\n        .next(interner)\n        .expect_err(\"DecimalDigit following NumericLiteral not rejected as expected\");\n    if let Error::Syntax(_, pos) = err {\n        assert_eq!(pos, Position::new(1, 5));\n    } else {\n        panic!(\"invalid error type\");\n    }\n\n    // Identifier Start\n    let mut lexer = Lexer::from(&b\"17.4$\"[..]);\n    let interner = &mut Interner::default();\n\n    if let Error::Syntax(_, pos) = lexer\n        .next(interner)\n        .expect_err(\"IdentifierStart '$' following NumericLiteral not rejected as expected\")\n    {\n        assert_eq!(pos, Position::new(1, 5));\n    } else {\n        panic!(\"invalid error type\");\n    }\n\n    let mut lexer = Lexer::from(&b\"17.4_\"[..]);\n    let interner = &mut Interner::default();\n\n    if let Error::Syntax(_, pos) = lexer\n        .next(interner)\n        .expect_err(\"IdentifierStart '_' following NumericLiteral not rejected as expected\")\n    {\n        assert_eq!(pos, Position::new(1, 5));\n    } else {\n        panic!(\"invalid error type\");\n    }\n}\n\n#[test]\nfn string_codepoint_with_no_braces() {\n    let mut lexer = Lexer::from(&br#\"\"test\\uD38Dtest\"\"#[..]);\n    let interner = &mut Interner::default();\n\n    assert!(lexer.next(interner).is_ok());\n}\n\n#[test]\n#[ignore = \"Need to assert if this test is valid\"]\nfn illegal_code_point_following_numeric_literal() {\n    // Checks as per https://tc39.es/ecma262/#sec-literals-numeric-literals that a NumericLiteral cannot\n    // be immediately followed by an IdentifierStart where the IdentifierStart\n    let mut lexer = Lexer::from(\"17.4\\u{2764}\".as_bytes());\n    let interner = &mut Interner::default();\n\n    assert!(\n        lexer.next(interner).is_err(),\n        \"{}\",\n        r\"IdentifierStart \\u{2764} following NumericLiteral not rejected as expected\"\n    );\n}\n\n#[test]\nfn string_unicode() {\n    let s = r#\"'中文';\"#;\n\n    let mut lexer = Lexer::from(s.as_bytes());\n    let interner = &mut Interner::default();\n\n    let sym = interner.get_or_intern_static(\"中文\", utf16!(\"中文\"));\n    let expected = [\n        TokenKind::StringLiteral((sym, EscapeSequence::empty())),\n        TokenKind::Punctuator(Punctuator::Semicolon),\n    ];\n\n    expect_tokens(&mut lexer, &expected, interner);\n}\n\n#[test]\nfn string_unicode_escape_with_braces() {\n    let mut lexer = Lexer::from(&br\"'{\\u{20ac}\\u{a0}\\u{a0}}'\"[..]);\n    let interner = &mut Interner::default();\n\n    let sym =\n        interner.get_or_intern_static(\"{\\u{20ac}\\u{a0}\\u{a0}}\", utf16!(\"{\\u{20ac}\\u{a0}\\u{a0}}\"));\n    let expected = [TokenKind::StringLiteral((sym, EscapeSequence::OTHER))];\n\n    expect_tokens(&mut lexer, &expected, interner);\n\n    lexer = Lexer::from(&br\"\\u{{a0}\"[..]);\n\n    if let Error::Syntax(_, pos) = lexer\n        .next(interner)\n        .expect_err(\"Malformed Unicode character sequence expected\")\n    {\n        assert_eq!(pos, Position::new(1, 1));\n    } else {\n        panic!(\"invalid error type\");\n    }\n\n    lexer = Lexer::from(&br\"\\u{{a0}}\"[..]);\n\n    if let Error::Syntax(_, pos) = lexer\n        .next(interner)\n        .expect_err(\"Malformed Unicode character sequence expected\")\n    {\n        assert_eq!(pos, Position::new(1, 1));\n    } else {\n        panic!(\"invalid error type\");\n    }\n}\n\n#[test]\nfn string_unicode_escape_with_braces_2() {\n    let s = r\"'\\u{20ac}\\u{a0}\\u{a0}'\";\n\n    let mut lexer = Lexer::from(s.as_bytes());\n    let interner = &mut Interner::default();\n\n    let sym = interner.get_or_intern_static(\"\\u{20ac}\\u{a0}\\u{a0}\", utf16!(\"\\u{20ac}\\u{a0}\\u{a0}\"));\n    let expected = [TokenKind::StringLiteral((sym, EscapeSequence::OTHER))];\n\n    expect_tokens(&mut lexer, &expected, interner);\n}\n\n#[test]\nfn string_with_single_escape() {\n    let s = r\"'\\Б'\";\n\n    let mut lexer = Lexer::from(s.as_bytes());\n    let interner = &mut Interner::default();\n\n    let sym = interner.get_or_intern_static(\"Б\", utf16!(\"Б\"));\n    let expected = [TokenKind::StringLiteral((sym, EscapeSequence::OTHER))];\n\n    expect_tokens(&mut lexer, &expected, interner);\n}\n\n#[test]\nfn string_legacy_octal_escape() {\n    let test_cases = [\n        (r\"'\\3'\", \"\\u{3}\"),\n        (r\"'\\03'\", \"\\u{3}\"),\n        (r\"'\\003'\", \"\\u{3}\"),\n        (r\"'\\0003'\", \"\\u{0}3\"),\n        (r\"'\\43'\", \"#\"),\n        (r\"'\\043'\", \"#\"),\n        (r\"'\\101'\", \"A\"),\n    ];\n\n    for (s, expected) in &test_cases {\n        let mut lexer = Lexer::from(s.as_bytes());\n        let interner = &mut Interner::default();\n\n        let sym = interner.get_or_intern(expected.encode_utf16().collect::<Vec<_>>().as_slice());\n        let expected_tokens = [TokenKind::StringLiteral((\n            sym,\n            EscapeSequence::LEGACY_OCTAL,\n        ))];\n\n        expect_tokens(&mut lexer, &expected_tokens, interner);\n    }\n\n    for (s, _) in &test_cases {\n        let mut lexer = Lexer::from(s.as_bytes());\n        let interner = &mut Interner::default();\n        lexer.set_strict(true);\n\n        if let Error::Syntax(_, pos) = lexer\n            .next(interner)\n            .expect_err(\"Octal-escape in strict mode not rejected as expected\")\n        {\n            assert_eq!(pos, Position::new(1, 2));\n        } else {\n            panic!(\"invalid error type\");\n        }\n    }\n}\n\n#[test]\nfn string_zero_escape() {\n    let test_cases = [(r\"'\\0'\", \"\\u{0}\"), (r\"'\\0A'\", \"\\u{0}A\")];\n\n    for (s, expected) in &test_cases {\n        let mut lexer = Lexer::from(s.as_bytes());\n        let interner = &mut Interner::default();\n\n        let sym = interner.get_or_intern(expected.encode_utf16().collect::<Vec<_>>().as_slice());\n        let expected_tokens = [TokenKind::StringLiteral((sym, EscapeSequence::OTHER))];\n\n        expect_tokens(&mut lexer, &expected_tokens, interner);\n    }\n}\n\n#[test]\nfn string_non_octal_decimal_escape() {\n    let test_cases = [(r\"'\\8'\", \"8\"), (r\"'\\9'\", \"9\")];\n\n    for (s, expected) in &test_cases {\n        let mut lexer = Lexer::from(s.as_bytes());\n        let interner = &mut Interner::default();\n\n        let sym = interner.get_or_intern(expected.encode_utf16().collect::<Vec<_>>().as_slice());\n        let expected_tokens = [TokenKind::StringLiteral((\n            sym,\n            EscapeSequence::NON_OCTAL_DECIMAL,\n        ))];\n\n        expect_tokens(&mut lexer, &expected_tokens, interner);\n    }\n\n    for (s, _) in &test_cases {\n        let mut lexer = Lexer::from(s.as_bytes());\n        let interner = &mut Interner::default();\n        lexer.set_strict(true);\n\n        if let Error::Syntax(_, pos) = lexer\n            .next(interner)\n            .expect_err(\"Non-octal-decimal-escape in strict mode not rejected as expected\")\n        {\n            assert_eq!(pos, Position::new(1, 2));\n        } else {\n            panic!(\"invalid error type\");\n        }\n    }\n}\n\n#[test]\nfn string_line_continuation() {\n    let s = \"'hello \\\\\\nworld'\";\n\n    let mut lexer = Lexer::from(s.as_bytes());\n    let interner = &mut Interner::default();\n\n    let sym = interner.get_or_intern_static(\"hello world\", utf16!(\"hello world\"));\n    let expected_tokens = [TokenKind::StringLiteral((sym, EscapeSequence::OTHER))];\n\n    expect_tokens(&mut lexer, &expected_tokens, interner);\n}\n\nmod carriage_return {\n    use super::*;\n\n    fn expect_tokens_with_lines(lines: usize, src: &str) {\n        let mut lexer = Lexer::from(src.as_bytes());\n        let interner = &mut Interner::default();\n\n        let mut expected = Vec::with_capacity(lines + 2);\n        expected.push(TokenKind::Punctuator(Punctuator::Sub));\n        for _ in 0..lines {\n            expected.push(TokenKind::LineTerminator);\n        }\n        expected.push(TokenKind::NumericLiteral(Numeric::Integer(3)));\n\n        expect_tokens(&mut lexer, &expected, interner);\n    }\n\n    #[test]\n    fn regular_line() {\n        expect_tokens_with_lines(1, \"-\\n3\");\n        expect_tokens_with_lines(2, \"-\\n\\n3\");\n        expect_tokens_with_lines(3, \"-\\n\\n\\n3\");\n    }\n\n    #[test]\n    fn carriage_return() {\n        expect_tokens_with_lines(1, \"-\\r3\");\n        expect_tokens_with_lines(2, \"-\\r\\r3\");\n        expect_tokens_with_lines(3, \"-\\r\\r\\r3\");\n    }\n\n    #[test]\n    fn windows_line() {\n        expect_tokens_with_lines(1, \"-\\r\\n3\");\n        expect_tokens_with_lines(2, \"-\\r\\n\\r\\n3\");\n        expect_tokens_with_lines(3, \"-\\r\\n\\r\\n\\r\\n3\");\n    }\n\n    #[test]\n    fn mixed_line() {\n        expect_tokens_with_lines(2, \"-\\r\\n\\n3\");\n        expect_tokens_with_lines(2, \"-\\n\\r3\");\n        expect_tokens_with_lines(3, \"-\\r\\n\\n\\r3\");\n    }\n}\n"
  },
  {
    "path": "core/parser/src/lexer/token.rs",
    "content": "//! Boa's implementation of all ECMAScript [Token]s.\n//!\n//! More information:\n//!  - [ECMAScript reference][spec]\n//!\n//! [spec]: https://tc39.es/ecma262/#sec-tokens\n\nuse crate::lexer::template::TemplateString;\nuse bitflags::bitflags;\nuse boa_ast::{Keyword, LinearSpan, PositionGroup, Punctuator, Span, Spanned};\nuse boa_interner::{Interner, Sym};\nuse num_bigint::BigInt;\n\n/// This represents the smallest individual words, phrases, or characters that JavaScript can understand.\n///\n/// More information:\n///  - [ECMAScript reference][spec]\n///\n/// [spec]: https://tc39.es/ecma262/#sec-tokens\n#[derive(Debug, Clone, PartialEq)]\npub struct Token {\n    /// The token kind, which contains the actual data of the token.\n    kind: TokenKind,\n    /// The token position in the original source code.\n    span: Span,\n    /// The token linear position in the original source code.\n    linear_span: LinearSpan,\n}\n\nimpl Token {\n    /// Create a new detailed token from the token data, line number, column number, and linear position\n    #[inline]\n    #[must_use]\n    pub const fn new(kind: TokenKind, span: Span, linear_span: LinearSpan) -> Self {\n        Self {\n            kind,\n            span,\n            linear_span,\n        }\n    }\n\n    /// Create a new detailed token from the token data, line number and column number\n    #[inline]\n    #[must_use]\n    pub fn new_by_position_group(\n        kind: TokenKind,\n        start: PositionGroup,\n        end: PositionGroup,\n    ) -> Self {\n        Self::new(\n            kind,\n            Span::new(start.position(), end.position()),\n            LinearSpan::new(start.linear_position(), end.linear_position()),\n        )\n    }\n\n    /// Gets the kind of the token.\n    #[inline]\n    #[must_use]\n    pub const fn kind(&self) -> &TokenKind {\n        &self.kind\n    }\n\n    /// Gets the starting position group of the token.\n    #[inline]\n    #[must_use]\n    pub const fn start_group(&self) -> PositionGroup {\n        PositionGroup::new(self.span.start(), self.linear_span.start())\n    }\n\n    /// Gets the token span in the original source code.\n    #[inline]\n    #[must_use]\n    pub const fn linear_span(&self) -> LinearSpan {\n        self.linear_span\n    }\n\n    /// Converts the token to a `String`.\n    #[inline]\n    pub(crate) fn to_string(&self, interner: &Interner) -> String {\n        self.kind.to_string(interner)\n    }\n}\n\nimpl Spanned for Token {\n    #[inline]\n    fn span(&self) -> Span {\n        self.span\n    }\n}\n\n/// Represents the type different types of numeric literals.\n#[derive(Clone, PartialEq, Debug)]\npub enum Numeric {\n    /// A floating point number.\n    Rational(f64),\n\n    /// An integer.\n    Integer(i32),\n\n    /// A `BigInt`.\n    BigInt(Box<BigInt>),\n}\n\nimpl From<f64> for Numeric {\n    #[inline]\n    fn from(n: f64) -> Self {\n        Self::Rational(n)\n    }\n}\n\nimpl From<i32> for Numeric {\n    #[inline]\n    fn from(n: i32) -> Self {\n        Self::Integer(n)\n    }\n}\n\nimpl From<BigInt> for Numeric {\n    #[inline]\n    fn from(n: BigInt) -> Self {\n        Self::BigInt(Box::new(n))\n    }\n}\n\n/// Represents the type of Token and the data it has inside.\n#[derive(Clone, PartialEq, Debug)]\npub enum TokenKind {\n    /// A boolean literal, which is either `true` or `false`.\n    BooleanLiteral((bool, ContainsEscapeSequence)),\n\n    /// The end of the file.\n    EOF,\n\n    /// An [**identifier name**][spec].\n    ///\n    /// [spec]: https://tc39.es/ecma262/#prod-IdentifierName\n    IdentifierName((Sym, ContainsEscapeSequence)),\n\n    /// A [**private identifier**][spec].\n    ///\n    /// [spec]: https://tc39.es/ecma262/#prod-PrivateIdentifier\n    PrivateIdentifier(Sym),\n\n    /// A keyword and a flag if the keyword contains unicode escaped chars.\n    ///\n    /// For more information, see [`Keyword`].\n    Keyword((Keyword, bool)),\n\n    /// The [`null` literal][spec].\n    ///\n    /// [spec]: https://tc39.es/ecma262/#prod-NullLiteral\n    NullLiteral(ContainsEscapeSequence),\n\n    /// A numeric literal.\n    NumericLiteral(Numeric),\n\n    /// A piece of punctuation\n    Punctuator(Punctuator),\n\n    /// A [**string literal**][spec].\n    ///\n    /// [spec]: https://tc39.es/ecma262/#prod-StringLiteral\n    StringLiteral((Sym, EscapeSequence)),\n\n    /// A part of a template literal without substitution.\n    TemplateNoSubstitution(TemplateString),\n\n    /// The part of a template literal between substitutions\n    TemplateMiddle(TemplateString),\n\n    /// A regular expression, consisting of body and flags.\n    RegularExpressionLiteral(Sym, Sym),\n\n    /// Indicates a [**line terminator (`\\n`)**][spec].\n    ///\n    /// [spec]: https://tc39.es/ecma262/#prod-LineTerminator\n    LineTerminator,\n\n    /// Indicates a comment, the content isn't stored.\n    Comment,\n}\n\nimpl From<bool> for TokenKind {\n    #[inline]\n    fn from(oth: bool) -> Self {\n        Self::BooleanLiteral((oth, ContainsEscapeSequence(false)))\n    }\n}\n\nimpl From<(Keyword, bool)> for TokenKind {\n    #[inline]\n    fn from(kw: (Keyword, bool)) -> Self {\n        Self::Keyword(kw)\n    }\n}\n\nimpl From<Punctuator> for TokenKind {\n    #[inline]\n    fn from(punc: Punctuator) -> Self {\n        Self::Punctuator(punc)\n    }\n}\n\nimpl From<Numeric> for TokenKind {\n    #[inline]\n    fn from(num: Numeric) -> Self {\n        Self::NumericLiteral(num)\n    }\n}\n\nimpl TokenKind {\n    /// Creates a `BooleanLiteral` token kind.\n    #[inline]\n    #[must_use]\n    pub const fn boolean_literal(lit: bool) -> Self {\n        Self::BooleanLiteral((lit, ContainsEscapeSequence(false)))\n    }\n\n    /// Creates an `EOF` token kind.\n    #[inline]\n    #[must_use]\n    pub const fn eof() -> Self {\n        Self::EOF\n    }\n\n    /// Creates an `Identifier` token type.\n    #[inline]\n    #[must_use]\n    pub const fn identifier(ident: Sym) -> Self {\n        Self::IdentifierName((ident, ContainsEscapeSequence(false)))\n    }\n\n    /// Creates a `NumericLiteral` token kind.\n    #[must_use]\n    pub fn numeric_literal<L>(lit: L) -> Self\n    where\n        L: Into<Numeric>,\n    {\n        Self::NumericLiteral(lit.into())\n    }\n\n    /// Creates a `Punctuator` token type.\n    #[inline]\n    #[must_use]\n    pub const fn punctuator(punc: Punctuator) -> Self {\n        Self::Punctuator(punc)\n    }\n\n    /// Creates a `StringLiteral` token type.\n    #[inline]\n    #[must_use]\n    pub const fn string_literal(lit: Sym, escape_sequence: EscapeSequence) -> Self {\n        Self::StringLiteral((lit, escape_sequence))\n    }\n\n    /// Creates a `TemplateMiddle` token type.\n    #[inline]\n    #[must_use]\n    pub const fn template_middle(template_string: TemplateString) -> Self {\n        Self::TemplateMiddle(template_string)\n    }\n\n    /// Creates a `TemplateNoSubstitution` token type.\n    #[inline]\n    #[must_use]\n    pub const fn template_no_substitution(template_string: TemplateString) -> Self {\n        Self::TemplateNoSubstitution(template_string)\n    }\n\n    /// Creates a `RegularExpressionLiteral` token kind.\n    #[inline]\n    #[must_use]\n    pub const fn regular_expression_literal(body: Sym, flags: Sym) -> Self {\n        Self::RegularExpressionLiteral(body, flags)\n    }\n\n    /// Creates a `LineTerminator` token kind.\n    #[inline]\n    #[must_use]\n    pub const fn line_terminator() -> Self {\n        Self::LineTerminator\n    }\n\n    /// Creates a 'Comment' token kind.\n    #[inline]\n    #[must_use]\n    pub const fn comment() -> Self {\n        Self::Comment\n    }\n\n    /// Implements the `ToString` functionality for the `TokenKind`.\n    #[must_use]\n    pub fn to_string(&self, interner: &Interner) -> String {\n        match *self {\n            Self::BooleanLiteral((val, _)) => val.to_string(),\n            Self::EOF => \"end of file\".to_owned(),\n            Self::IdentifierName((ident, _)) => interner.resolve_expect(ident).to_string(),\n            Self::PrivateIdentifier(ident) => format!(\"#{}\", interner.resolve_expect(ident)),\n            Self::Keyword((word, _)) => word.to_string(),\n            Self::NullLiteral(_) => \"null\".to_owned(),\n            Self::NumericLiteral(Numeric::Rational(num)) => num.to_string(),\n            Self::NumericLiteral(Numeric::Integer(num)) => num.to_string(),\n            Self::NumericLiteral(Numeric::BigInt(ref num)) => format!(\"{num}n\"),\n            Self::Punctuator(punc) => punc.to_string(),\n            Self::StringLiteral((lit, _)) => interner.resolve_expect(lit).to_string(),\n            Self::TemplateNoSubstitution(ts) | Self::TemplateMiddle(ts) => {\n                interner.resolve_expect(ts.raw()).to_string()\n            }\n            Self::RegularExpressionLiteral(body, flags) => {\n                format!(\n                    \"/{}/{}\",\n                    interner.resolve_expect(body),\n                    interner.resolve_expect(flags),\n                )\n            }\n            Self::LineTerminator => \"line terminator\".to_owned(),\n            Self::Comment => \"comment\".to_owned(),\n        }\n    }\n}\n\nbitflags! {\n    /// Indicates the set of escape sequences a string contains.\n    #[derive(Clone, Copy, Debug, PartialEq, Eq)]\n    pub struct EscapeSequence: u8 {\n        /// A legacy escape sequence starting with `0` - `7`.\n        ///\n        /// More information:\n        ///  - [ECMAScript reference][spec]\n        ///\n        /// [spec]: https://tc39.es/ecma262/#prod-LegacyOctalEscapeSequence\n        const LEGACY_OCTAL = 0b0000_0001;\n\n        /// A octal escape sequence starting with `8` - `9`.\n        ///\n        /// More information:\n        ///  - [ECMAScript reference][spec]\n        ///\n        /// [spec]: https://tc39.es/ecma262/#prod-NonOctalDecimalEscapeSequence\n        const NON_OCTAL_DECIMAL = 0b0000_0010;\n\n        /// A generic escape sequence, either single (`\\t`), unicode (`\\u1238`)\n        /// or a line continuation (`\\<LF>`)\n        ///\n        /// More information:\n        /// - [ECMAScript reference][spec]\n        ///\n        /// [spec]: https://tc39.es/ecma262/#prod-LineContinuation\n        const OTHER = 0b0000_0100;\n    }\n\n}\n\n/// Indicates if an identifier contains an escape sequence.\n#[derive(Clone, Copy, Debug, PartialEq, Eq)]\npub struct ContainsEscapeSequence(pub bool);\n"
  },
  {
    "path": "core/parser/src/lib.rs",
    "content": "//! Boa's **`boa_parser`** crate is a parser targeting the latest [ECMAScript language specification][spec].\n//!\n//! # Crate Overview\n//!\n//! This crate contains implementations of a [`Lexer`] and a [`Parser`] for the **ECMAScript**\n//! language. The [lexical grammar][lex] and the [syntactic grammar][grammar] being targeted are\n//! fully defined in the specification. See the links provided for more information.\n//!\n//! [spec]: https://tc39.es/ecma262\n//! [lex]: https://tc39.es/ecma262/#sec-ecmascript-language-lexical-grammar\n//! [grammar]: https://tc39.es/ecma262/#sec-ecmascript-language-expressions\n#![doc = include_str!(\"../ABOUT.md\")]\n#![doc(\n    html_logo_url = \"https://raw.githubusercontent.com/boa-dev/boa/main/assets/logo_black.svg\",\n    html_favicon_url = \"https://raw.githubusercontent.com/boa-dev/boa/main/assets/logo_black.svg\"\n)]\n#![cfg_attr(test, allow(clippy::needless_raw_string_hashes))] // Makes strings a bit more copy-pastable\n#![cfg_attr(not(test), forbid(clippy::unwrap_used))]\n#![allow(\n    clippy::module_name_repetitions,\n    clippy::too_many_lines,\n    clippy::cognitive_complexity,\n    clippy::let_unit_value,\n    clippy::redundant_pub_crate,\n    clippy::struct_field_names\n)]\n\npub mod error;\npub mod lexer;\npub mod parser;\npub mod source;\n\npub use error::Error;\npub use lexer::Lexer;\npub use parser::Parser;\npub use source::Source;\n"
  },
  {
    "path": "core/parser/src/parser/cursor/buffered_lexer/mod.rs",
    "content": "use crate::{\n    Error,\n    lexer::{InputElement, Lexer, Token, TokenKind},\n    parser::ParseResult,\n    source::{ReadChar, UTF8Input},\n};\nuse boa_ast::{LinearPosition, PositionGroup};\nuse boa_interner::Interner;\n\n#[cfg(test)]\nmod tests;\n\n/// The maximum number of tokens which can be peeked ahead.\nconst MAX_PEEK_SKIP: usize = 3;\n\n/// The fixed size of the buffer used for storing values that are peeked ahead.\n///\n/// The size is calculated for a worst case scenario, where we want to peek `MAX_PEEK_SKIP` tokens\n/// skipping line terminators, and the stream ends just after:\n/// ```text\n/// [\\n, B, \\n, C, \\n, D, \\n, E, \\n, F, None]\n///   0  0   1  1   2  2   3  3   4  4  5\n/// ```\nconst PEEK_BUF_SIZE: usize = (MAX_PEEK_SKIP + 1) * 2 + 1;\n\n#[derive(Debug)]\npub(super) struct BufferedLexer<R> {\n    lexer: Lexer<R>,\n    peeked: [Option<Token>; PEEK_BUF_SIZE],\n    read_index: usize,\n    write_index: usize,\n    last_linear_pos: LinearPosition,\n}\n\nimpl<R> From<Lexer<R>> for BufferedLexer<R>\nwhere\n    R: ReadChar,\n{\n    fn from(lexer: Lexer<R>) -> Self {\n        Self {\n            lexer,\n            peeked: [\n                None::<Token>,\n                None::<Token>,\n                None::<Token>,\n                None::<Token>,\n                None::<Token>,\n                None::<Token>,\n                None::<Token>,\n                None::<Token>,\n                None::<Token>,\n            ],\n            read_index: 0,\n            write_index: 0,\n            last_linear_pos: LinearPosition::default(),\n        }\n    }\n}\n\nimpl<R> From<R> for BufferedLexer<R>\nwhere\n    R: ReadChar,\n{\n    fn from(reader: R) -> Self {\n        Lexer::new(reader).into()\n    }\n}\n\nimpl<'a> From<&'a [u8]> for BufferedLexer<UTF8Input<&'a [u8]>> {\n    fn from(reader: &'a [u8]) -> Self {\n        Lexer::from(reader).into()\n    }\n}\n\nimpl<R> BufferedLexer<R>\nwhere\n    R: ReadChar,\n{\n    /// Sets the goal symbol for the lexer.\n    pub(super) fn set_goal(&mut self, elm: InputElement) {\n        self.lexer.set_goal(elm);\n    }\n\n    /// Lexes the next tokens as a regex assuming that the starting '/' has already been consumed.\n    /// If `init_with_eq` is `true`, then assuming that the starting '/=' has already been consumed.\n    pub(super) fn lex_regex(\n        &mut self,\n        start: PositionGroup,\n        interner: &mut Interner,\n        init_with_eq: bool,\n    ) -> ParseResult<Token> {\n        self.set_goal(InputElement::RegExp);\n        self.lexer\n            .lex_slash_token(start, interner, init_with_eq)\n            .map_err(Into::into)\n    }\n\n    /// Lexes the next tokens as template middle or template tail assuming that the starting\n    /// '}' has already been consumed.\n    pub(super) fn lex_template(\n        &mut self,\n        start: PositionGroup,\n        interner: &mut Interner,\n    ) -> ParseResult<Token> {\n        self.lexer\n            .lex_template(start, interner)\n            .map_err(Error::from)\n    }\n\n    pub(super) const fn strict(&self) -> bool {\n        self.lexer.strict()\n    }\n\n    pub(super) fn set_strict(&mut self, strict: bool) {\n        self.lexer.set_strict(strict);\n    }\n\n    pub(super) const fn module(&self) -> bool {\n        self.lexer.module()\n    }\n\n    pub(super) fn set_module(&mut self, module: bool) {\n        self.lexer.set_module(module);\n    }\n\n    /// Fills the peeking buffer with the next token.\n    ///\n    /// It will not fill two line terminators one after the other.\n    fn fill(&mut self, interner: &mut Interner) -> ParseResult<()> {\n        debug_assert!(\n            self.write_index < PEEK_BUF_SIZE,\n            \"write index went out of bounds\"\n        );\n\n        let previous_index = self.write_index.checked_sub(1).unwrap_or(PEEK_BUF_SIZE - 1);\n\n        if let Some(ref token) = self.peeked[previous_index]\n            && token.kind() == &TokenKind::LineTerminator\n        {\n            // We don't want to have multiple contiguous line terminators in the buffer, since\n            // they have no meaning.\n            let next = loop {\n                self.lexer.skip_html_close(interner)?;\n                let next = self.lexer.next_no_skip(interner)?;\n                if let Some(ref token) = next {\n                    match token.kind() {\n                        TokenKind::LineTerminator => { /* skip */ }\n                        TokenKind::Comment => self.lexer.skip_html_close(interner)?,\n                        _ => break next,\n                    }\n                } else {\n                    break None;\n                }\n            };\n\n            self.peeked[self.write_index] = next;\n        } else {\n            self.peeked[self.write_index] = self.lexer.next(interner)?;\n        }\n\n        self.write_index = (self.write_index + 1) % PEEK_BUF_SIZE;\n        debug_assert_ne!(\n            self.read_index, self.write_index,\n            \"we reached the read index with the write index\"\n        );\n        debug_assert!(\n            self.read_index < PEEK_BUF_SIZE,\n            \"read index went out of bounds\"\n        );\n\n        Ok(())\n    }\n\n    /// Moves the cursor to the next token and returns the token.\n    ///\n    /// If `skip_line_terminators` is true then line terminators will be discarded.\n    ///\n    /// This follows iterator semantics in that a `peek(0, false)` followed by a `next(false)` will\n    /// return the same value. Note that because a `peek(n, false)` may return a line terminator a\n    /// subsequent `next(true)` may not return the same value.\n    pub(super) fn next(\n        &mut self,\n        skip_line_terminators: bool,\n        interner: &mut Interner,\n    ) -> ParseResult<Option<Token>> {\n        if self.read_index == self.write_index {\n            self.fill(interner)?;\n        }\n\n        if let Some(ref token) = self.peeked[self.read_index] {\n            if skip_line_terminators && token.kind() == &TokenKind::LineTerminator {\n                // We only store 1 contiguous line terminator, so if the one at `self.read_index`\n                // was a line terminator, we know that the next won't be one.\n                self.read_index = (self.read_index + 1) % PEEK_BUF_SIZE;\n                if self.read_index == self.write_index {\n                    self.fill(interner)?;\n                }\n            }\n            let tok = self.peeked[self.read_index].take();\n            self.read_index = (self.read_index + 1) % PEEK_BUF_SIZE;\n\n            if let Some(tok) = &tok {\n                self.last_linear_pos = tok.linear_span().end();\n            }\n\n            Ok(tok)\n        } else {\n            // We do not update the read index, since we should always return `None` from now on.\n            Ok(None)\n        }\n    }\n\n    /// Peeks the `n`th token after the next token.\n    ///\n    /// **Note:** `n` must be in the range `[0, 3]`.\n    /// i.e. if there are tokens `A`, `B`, `C`, `D`, `E` and `peek(0, false)` returns `A` then:\n    ///  - `peek(1, false) == peek(1, true) == B`.\n    ///  - `peek(2, false)` will return `C`.\n    ///    where `A`, `B`, `C`, `D` and `E` are tokens but not line terminators.\n    ///\n    /// If `skip_line_terminators` is `true` then line terminators will be discarded.\n    /// i.e. If there are tokens `A`, `\\n`, `B` and `peek(0, false)` is `A` then the following\n    /// will hold:\n    ///  - `peek(0, true) == A`\n    ///  - `peek(0, false) == A`\n    ///  - `peek(1, true) == B`\n    ///  - `peek(1, false) == \\n`\n    ///  - `peek(2, true) == None` (End of stream)\n    ///  - `peek(2, false) == B`\n    pub(super) fn peek(\n        &mut self,\n        skip_n: usize,\n        skip_line_terminators: bool,\n        interner: &mut Interner,\n    ) -> ParseResult<Option<&Token>> {\n        assert!(\n            skip_n <= MAX_PEEK_SKIP,\n            \"you cannot skip more than {MAX_PEEK_SKIP} elements\",\n        );\n\n        let mut read_index = self.read_index;\n        let mut count = 0;\n        let res_token = loop {\n            if read_index == self.write_index {\n                self.fill(interner)?;\n            }\n\n            if let Some(ref token) = self.peeked[read_index] {\n                if skip_line_terminators && token.kind() == &TokenKind::LineTerminator {\n                    read_index = (read_index + 1) % PEEK_BUF_SIZE;\n                    // We only store 1 contiguous line terminator, so if the one at `self.read_index`\n                    // was a line terminator, we know that the next won't be one.\n                    if read_index == self.write_index {\n                        self.fill(interner)?;\n                    }\n                }\n                if count == skip_n {\n                    break self.peeked[read_index].as_ref();\n                }\n            } else {\n                break None;\n            }\n            read_index = (read_index + 1) % PEEK_BUF_SIZE;\n            count += 1;\n        };\n\n        Ok(res_token)\n    }\n\n    /// Gets current linear position in the source code.\n    #[inline]\n    pub(super) fn linear_pos(&self) -> LinearPosition {\n        self.last_linear_pos\n    }\n\n    pub(super) fn take_source(&mut self) -> boa_ast::SourceText {\n        self.lexer.take_source()\n    }\n}\n"
  },
  {
    "path": "core/parser/src/parser/cursor/buffered_lexer/tests.rs",
    "content": "use crate::{\n    lexer::{Token, TokenKind},\n    parser::cursor::BufferedLexer,\n};\nuse boa_interner::Interner;\nuse boa_macros::utf16;\n\n#[test]\nfn peek_skip_ascending() {\n    let mut cur = BufferedLexer::from(&b\"a b c d e f g h i\"[..]);\n    let interner = &mut Interner::default();\n\n    assert_eq!(\n        *cur.peek(0, false, interner)\n            .unwrap()\n            .expect(\"Some value expected\")\n            .kind(),\n        TokenKind::identifier(interner.get_or_intern_static(\"a\", utf16!(\"a\")))\n    );\n    assert_eq!(\n        *cur.peek(1, false, interner)\n            .unwrap()\n            .expect(\"Some value expected\")\n            .kind(),\n        TokenKind::identifier(interner.get_or_intern_static(\"b\", utf16!(\"b\")))\n    );\n    assert_eq!(\n        *cur.peek(2, false, interner)\n            .unwrap()\n            .expect(\"Some value expected\")\n            .kind(),\n        TokenKind::identifier(interner.get_or_intern_static(\"c\", utf16!(\"c\")))\n    );\n    assert_eq!(\n        *cur.peek(2, false, interner)\n            .unwrap()\n            .expect(\"Some value expected\")\n            .kind(),\n        TokenKind::identifier(interner.get_or_intern_static(\"c\", utf16!(\"c\")))\n    );\n    assert_eq!(\n        *cur.peek(1, false, interner)\n            .unwrap()\n            .expect(\"Some value expected\")\n            .kind(),\n        TokenKind::identifier(interner.get_or_intern_static(\"b\", utf16!(\"b\")))\n    );\n    assert_eq!(\n        *cur.peek(0, false, interner)\n            .unwrap()\n            .expect(\"Some value expected\")\n            .kind(),\n        TokenKind::identifier(interner.get_or_intern_static(\"a\", utf16!(\"a\")))\n    );\n}\n\n#[test]\nfn peek_skip_next() {\n    let mut cur = BufferedLexer::from(&b\"a b c d e f g h i\"[..]);\n    let interner = &mut Interner::default();\n\n    assert_eq!(\n        *cur.peek(0, false, interner)\n            .unwrap()\n            .expect(\"Some value expected\")\n            .kind(),\n        TokenKind::identifier(interner.get_or_intern_static(\"a\", utf16!(\"a\")))\n    );\n    assert_eq!(\n        *cur.peek(1, false, interner)\n            .unwrap()\n            .expect(\"Some value expected\")\n            .kind(),\n        TokenKind::identifier(interner.get_or_intern_static(\"b\", utf16!(\"b\")))\n    );\n    assert_eq!(\n        *cur.peek(2, false, interner)\n            .unwrap()\n            .expect(\"Some value expected\")\n            .kind(),\n        TokenKind::identifier(interner.get_or_intern_static(\"c\", utf16!(\"c\")))\n    );\n    assert_eq!(\n        *cur.next(false, interner)\n            .unwrap()\n            .expect(\"Some value expected\")\n            .kind(),\n        TokenKind::identifier(interner.get_or_intern_static(\"a\", utf16!(\"a\")))\n    );\n    assert_eq!(\n        *cur.next(false, interner)\n            .unwrap()\n            .expect(\"Some value expected\")\n            .kind(),\n        TokenKind::identifier(interner.get_or_intern_static(\"b\", utf16!(\"b\")))\n    );\n    assert_eq!(\n        *cur.next(false, interner)\n            .unwrap()\n            .expect(\"Some value expected\")\n            .kind(),\n        TokenKind::identifier(interner.get_or_intern_static(\"c\", utf16!(\"c\")))\n    );\n    assert_eq!(\n        *cur.next(false, interner)\n            .unwrap()\n            .expect(\"Some value expected\")\n            .kind(),\n        TokenKind::identifier(interner.get_or_intern_static(\"d\", utf16!(\"d\")))\n    );\n    assert_eq!(\n        *cur.next(false, interner)\n            .unwrap()\n            .expect(\"Some value expected\")\n            .kind(),\n        TokenKind::identifier(interner.get_or_intern_static(\"e\", utf16!(\"e\")))\n    );\n    assert_eq!(\n        *cur.peek(0, false, interner)\n            .unwrap()\n            .expect(\"Some value expected\")\n            .kind(),\n        TokenKind::identifier(interner.get_or_intern_static(\"f\", utf16!(\"f\")))\n    );\n    assert_eq!(\n        *cur.peek(1, false, interner)\n            .unwrap()\n            .expect(\"Some value expected\")\n            .kind(),\n        TokenKind::identifier(interner.get_or_intern_static(\"g\", utf16!(\"g\")))\n    );\n    assert_eq!(\n        *cur.peek(2, false, interner)\n            .unwrap()\n            .expect(\"Some value expected\")\n            .kind(),\n        TokenKind::identifier(interner.get_or_intern_static(\"h\", utf16!(\"h\")))\n    );\n}\n\n#[test]\nfn peek_skip_next_alternating() {\n    let mut cur = BufferedLexer::from(&b\"a b c d e f g h i\"[..]);\n    let interner = &mut Interner::default();\n\n    assert_eq!(\n        *cur.peek(0, false, interner)\n            .unwrap()\n            .expect(\"Some value expected\")\n            .kind(),\n        TokenKind::identifier(interner.get_or_intern_static(\"a\", utf16!(\"a\")))\n    );\n    assert_eq!(\n        *cur.next(false, interner)\n            .unwrap()\n            .expect(\"Some value expected\")\n            .kind(),\n        TokenKind::identifier(interner.get_or_intern_static(\"a\", utf16!(\"a\")))\n    );\n    assert_eq!(\n        *cur.peek(1, false, interner)\n            .unwrap()\n            .expect(\"Some value expected\")\n            .kind(),\n        TokenKind::identifier(interner.get_or_intern_static(\"c\", utf16!(\"c\")))\n    );\n    assert_eq!(\n        *cur.next(false, interner)\n            .unwrap()\n            .expect(\"Some value expected\")\n            .kind(),\n        TokenKind::identifier(interner.get_or_intern_static(\"b\", utf16!(\"b\")))\n    );\n    assert_eq!(\n        *cur.peek(1, false, interner)\n            .unwrap()\n            .expect(\"Some value expected\")\n            .kind(),\n        TokenKind::identifier(interner.get_or_intern_static(\"d\", utf16!(\"d\")))\n    );\n    assert_eq!(\n        *cur.next(false, interner)\n            .unwrap()\n            .expect(\"Some value expected\")\n            .kind(),\n        TokenKind::identifier(interner.get_or_intern_static(\"c\", utf16!(\"c\")))\n    );\n    assert_eq!(\n        *cur.peek(2, false, interner)\n            .unwrap()\n            .expect(\"Some value expected\")\n            .kind(),\n        TokenKind::identifier(interner.get_or_intern_static(\"f\", utf16!(\"f\")))\n    );\n}\n\n#[test]\nfn peek_next_till_end() {\n    let mut cur = BufferedLexer::from(&b\"a b c d e f g h i\"[..]);\n    let interner = &mut Interner::default();\n\n    loop {\n        let peek = cur.peek(0, false, interner).unwrap().cloned();\n        let next = cur.next(false, interner).unwrap();\n\n        assert_eq!(peek, next);\n\n        if peek.is_none() {\n            break;\n        }\n    }\n}\n\n#[test]\nfn peek_skip_next_till_end() {\n    let mut cur = BufferedLexer::from(&b\"a b c d e f g h i\"[..]);\n    let interner = &mut Interner::default();\n\n    let mut peeked: [Option<Token>; super::MAX_PEEK_SKIP + 1] =\n        [None::<Token>, None::<Token>, None::<Token>, None::<Token>];\n\n    loop {\n        for (i, peek) in peeked.iter_mut().enumerate() {\n            *peek = cur.peek(i, false, interner).unwrap().cloned();\n        }\n\n        for peek in &peeked {\n            assert_eq!(&cur.next(false, interner).unwrap(), peek);\n        }\n\n        if peeked[super::MAX_PEEK_SKIP - 1].is_none() {\n            break;\n        }\n    }\n}\n\n#[test]\nfn skip_peeked_terminators() {\n    let mut cur = BufferedLexer::from(&b\"A \\n B\"[..]);\n    let interner = &mut Interner::default();\n\n    assert_eq!(\n        *cur.peek(0, false, interner)\n            .unwrap()\n            .expect(\"Some value expected\")\n            .kind(),\n        TokenKind::identifier(interner.get_or_intern_static(\"A\", utf16!(\"A\")))\n    );\n    assert_eq!(\n        *cur.peek(0, true, interner)\n            .unwrap()\n            .expect(\"Some value expected\")\n            .kind(),\n        TokenKind::identifier(interner.get_or_intern_static(\"A\", utf16!(\"A\")))\n    );\n\n    assert_eq!(\n        *cur.peek(1, false, interner)\n            .unwrap()\n            .expect(\"Some value expected\")\n            .kind(),\n        TokenKind::LineTerminator,\n    );\n    assert_eq!(\n        *cur.peek(1, true, interner)\n            .unwrap()\n            .expect(\"Some value expected\")\n            .kind(),\n        TokenKind::identifier(interner.get_or_intern_static(\"B\", utf16!(\"B\"))) /* This value is after the line terminator */\n    );\n\n    assert_eq!(\n        *cur.peek(2, false, interner)\n            .unwrap()\n            .expect(\"Some value expected\")\n            .kind(),\n        TokenKind::identifier(interner.get_or_intern_static(\"B\", utf16!(\"B\")))\n    );\n    // End of stream\n    assert!(cur.peek(2, true, interner).unwrap().is_none());\n}\n\n#[test]\nfn issue_1768() {\n    let mut cur = BufferedLexer::from(&b\"\\n(\\nx\\n)\\n\"[..]);\n    let interner = &mut Interner::default();\n\n    assert!(cur.peek(3, true, interner).unwrap().is_none());\n}\n"
  },
  {
    "path": "core/parser/src/parser/cursor/mod.rs",
    "content": "//! Cursor implementation for the parser.\nmod buffered_lexer;\n\nuse crate::{\n    Error,\n    lexer::{InputElement, Lexer, Token, TokenKind},\n    parser::{OrAbrupt, ParseResult},\n    source::ReadChar,\n};\nuse boa_ast::{LinearPosition, PositionGroup, Punctuator, Spanned};\nuse boa_interner::Interner;\nuse buffered_lexer::BufferedLexer;\n\n/// The result of a peek for a semicolon.\n#[derive(Debug)]\npub(super) enum SemicolonResult<'s> {\n    Found(Option<&'s Token>),\n    NotFound(&'s Token),\n}\n\n/// Token cursor.\n///\n/// This internal structure gives basic testable operations to the parser.\n#[derive(Debug)]\npub(super) struct Cursor<R> {\n    buffered_lexer: BufferedLexer<R>,\n\n    /// Tracks if the cursor is in a arrow function declaration.\n    arrow: bool,\n\n    /// Indicate if the cursor is used in `JSON.parse`.\n    json_parse: bool,\n\n    /// A unique identifier for each parser instance.\n    /// This is used to generate unique identifiers tagged template literals.\n    identifier: u32,\n\n    /// Tracks the number of tagged templates that are currently being parsed.\n    tagged_templates_count: u32,\n}\n\nimpl<R> Cursor<R>\nwhere\n    R: ReadChar,\n{\n    /// Creates a new cursor with the given reader.\n    pub(super) fn new(reader: R) -> Self {\n        Self {\n            buffered_lexer: Lexer::new(reader).into(),\n            arrow: false,\n            json_parse: false,\n            identifier: 0,\n            tagged_templates_count: 0,\n        }\n    }\n\n    /// Sets the goal symbol of the cursor to `Module`.\n    pub(super) fn set_module(&mut self) {\n        self.buffered_lexer.set_module(true);\n    }\n\n    /// Returns `true` if the cursor is currently parsing a `Module`.\n    pub(super) const fn module(&self) -> bool {\n        self.buffered_lexer.module()\n    }\n\n    pub(super) fn set_goal(&mut self, elm: InputElement) {\n        self.buffered_lexer.set_goal(elm);\n    }\n\n    /// Lexes the next tokens as a regex assuming that the starting '/' has already been consumed.\n    /// If `init_with_eq` is `true`, then assuming that the starting '/=' has already been consumed.\n    pub(super) fn lex_regex(\n        &mut self,\n        start: PositionGroup,\n        interner: &mut Interner,\n        init_with_eq: bool,\n    ) -> ParseResult<Token> {\n        self.buffered_lexer.lex_regex(start, interner, init_with_eq)\n    }\n\n    pub(super) fn lex_template(\n        &mut self,\n        start: PositionGroup,\n        interner: &mut Interner,\n    ) -> ParseResult<Token> {\n        self.buffered_lexer.lex_template(start, interner)\n    }\n\n    /// Advances the cursor and returns the next token.\n    pub(super) fn next(&mut self, interner: &mut Interner) -> ParseResult<Option<Token>> {\n        self.buffered_lexer.next(true, interner)\n    }\n\n    /// Advances the cursor without returning the next token.\n    ///\n    /// # Panics\n    ///\n    /// This function will panic if there is no further token in the cursor.\n    #[track_caller]\n    pub(super) fn advance(&mut self, interner: &mut Interner) {\n        self.next(interner)\n            .expect(\"tried to advance cursor, but the buffer was empty\");\n    }\n\n    /// Peeks a future token, without consuming it or advancing the cursor.\n    /// This peeking **skips** line terminators.\n    ///\n    /// You can skip some tokens with the `skip_n` option.\n    pub(super) fn peek(\n        &mut self,\n        skip_n: usize,\n        interner: &mut Interner,\n    ) -> ParseResult<Option<&Token>> {\n        self.buffered_lexer.peek(skip_n, true, interner)\n    }\n\n    /// Peeks a future token, without consuming it or advancing the cursor.\n    /// This peeking **does not skips** line terminators.\n    ///\n    /// You can skip some tokens with the `skip_n` option.\n    pub(super) fn peek_no_skip_line_term(\n        &mut self,\n        skip_n: usize,\n        interner: &mut Interner,\n    ) -> ParseResult<Option<&Token>> {\n        self.buffered_lexer.peek(skip_n, false, interner)\n    }\n\n    /// Gets the current strict mode for the cursor.\n    pub(super) const fn strict(&self) -> bool {\n        self.buffered_lexer.strict()\n    }\n\n    /// Sets the strict mode to strict or non-strict.\n    pub(super) fn set_strict(&mut self, strict: bool) {\n        self.buffered_lexer.set_strict(strict);\n    }\n\n    /// Returns if the cursor is currently in an arrow function declaration.\n    pub(super) const fn arrow(&self) -> bool {\n        self.arrow\n    }\n\n    /// Set if the cursor is currently in a arrow function declaration.\n    pub(super) fn set_arrow(&mut self, arrow: bool) {\n        self.arrow = arrow;\n    }\n\n    /// Returns if the cursor is currently used in `JSON.parse`.\n    pub(super) const fn json_parse(&self) -> bool {\n        self.json_parse\n    }\n\n    /// Set if the cursor is currently used in `JSON.parse`.\n    pub(super) fn set_json_parse(&mut self, json_parse: bool) {\n        self.json_parse = json_parse;\n    }\n\n    /// Set the identifier of the cursor.\n    #[inline]\n    pub(super) fn set_identifier(&mut self, identifier: u32) {\n        self.identifier = identifier;\n    }\n\n    /// Get the identifier for a tagged template.\n    #[inline]\n    pub(super) fn tagged_template_identifier(&mut self) -> u64 {\n        self.tagged_templates_count += 1;\n\n        let identifier = u64::from(self.identifier);\n        let count = u64::from(self.tagged_templates_count);\n\n        (count << 32) | identifier\n    }\n\n    /// Returns an error if the next token is not of kind `kind`.\n    pub(super) fn expect<K>(\n        &mut self,\n        kind: K,\n        context: &'static str,\n        interner: &mut Interner,\n    ) -> ParseResult<Token>\n    where\n        K: Into<TokenKind>,\n    {\n        let next_token = self.next(interner).or_abrupt()?;\n        let kind = kind.into();\n\n        if next_token.kind() == &kind {\n            Ok(next_token)\n        } else {\n            Err(Error::expected(\n                [kind.to_string(interner)],\n                next_token.to_string(interner),\n                next_token.span(),\n                context,\n            ))\n        }\n    }\n\n    /// It will peek for the next token, to see if it's a semicolon.\n    ///\n    /// It will automatically insert a semicolon if needed, as specified in the [spec][spec].\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-automatic-semicolon-insertion\n    pub(super) fn peek_semicolon(\n        &mut self,\n        interner: &mut Interner,\n    ) -> ParseResult<SemicolonResult<'_>> {\n        self.peek_no_skip_line_term(0, interner)?\n            .map_or(Ok(SemicolonResult::Found(None)), |tk| match tk.kind() {\n                TokenKind::Punctuator(Punctuator::Semicolon | Punctuator::CloseBlock)\n                | TokenKind::LineTerminator => Ok(SemicolonResult::Found(Some(tk))),\n                _ => Ok(SemicolonResult::NotFound(tk)),\n            })\n    }\n\n    /// Consumes the next token if it is a semicolon, or returns a `Error` if it's not.\n    ///\n    /// It will automatically insert a semicolon if needed, as specified in the [spec][spec].\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-automatic-semicolon-insertion\n    pub(super) fn expect_semicolon(\n        &mut self,\n        context: &'static str,\n        interner: &mut Interner,\n    ) -> ParseResult<()> {\n        match self.peek_semicolon(interner)? {\n            SemicolonResult::Found(Some(tk)) => match *tk.kind() {\n                TokenKind::Punctuator(Punctuator::Semicolon) | TokenKind::LineTerminator => {\n                    let _next = self.buffered_lexer.next(false, interner)?;\n                    Ok(())\n                }\n                _ => Ok(()),\n            },\n            SemicolonResult::Found(None) => Ok(()),\n            SemicolonResult::NotFound(tk) => Err(Error::expected(\n                [\";\".to_owned()],\n                tk.to_string(interner),\n                tk.span(),\n                context,\n            )),\n        }\n    }\n\n    /// It will make sure that the peeked token (skipping n tokens) is not a line terminator.\n    ///\n    /// It expects that the token stream does not end here.\n    ///\n    /// This is just syntactic sugar for a `.peek(skip_n)` call followed by a check that the result\n    /// is not a line terminator or `None`.\n    pub(super) fn peek_expect_no_lineterminator(\n        &mut self,\n        skip_n: usize,\n        context: &'static str,\n        interner: &mut Interner,\n    ) -> ParseResult<&Token> {\n        let tok = self.peek_no_skip_line_term(skip_n, interner).or_abrupt()?;\n\n        if tok.kind() == &TokenKind::LineTerminator {\n            Err(Error::unexpected(\n                tok.to_string(interner),\n                tok.span(),\n                context,\n            ))\n        } else {\n            Ok(tok)\n        }\n    }\n\n    /// Check if the peeked token is a line terminator.\n    pub(super) fn peek_is_line_terminator(\n        &mut self,\n        skip_n: usize,\n        interner: &mut Interner,\n    ) -> ParseResult<Option<bool>> {\n        self.peek_no_skip_line_term(skip_n, interner)?\n            .map_or(Ok(None), |t| {\n                Ok(Some(t.kind() == &TokenKind::LineTerminator))\n            })\n    }\n\n    /// Advance the cursor to the next token and retrieve it, only if it's of `kind` type.\n    ///\n    /// When the next token is a `kind` token, get the token, otherwise return `None`.\n    ///\n    /// No next token also returns None.\n    pub(super) fn next_if<K>(\n        &mut self,\n        kind: K,\n        interner: &mut Interner,\n    ) -> ParseResult<Option<Token>>\n    where\n        K: Into<TokenKind>,\n    {\n        if let Some(token) = self.peek(0, interner)?\n            && token.kind() == &kind.into()\n        {\n            self.next(interner)\n        } else {\n            Ok(None)\n        }\n    }\n\n    /// Gets current linear position in the source code.\n    #[inline]\n    pub(super) fn linear_pos(&self) -> LinearPosition {\n        self.buffered_lexer.linear_pos()\n    }\n\n    pub(super) fn take_source(&mut self) -> boa_ast::SourceText {\n        self.buffered_lexer.take_source()\n    }\n}\n"
  },
  {
    "path": "core/parser/src/parser/expression/assignment/arrow_function.rs",
    "content": "//! Arrow function parsing.\n//!\n//! More information:\n//!  - [MDN documentation][mdn]\n//!  - [ECMAScript specification][spec]\n//!\n//! [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/Arrow_functions\n//! [spec]: https://tc39.es/ecma262/#sec-arrow-function-definitions\n\nuse super::AssignmentExpression;\nuse crate::{\n    error::{Error, ErrorContext, ParseResult},\n    lexer::{Error as LexError, TokenKind},\n    parser::{\n        AllowAwait, AllowIn, AllowYield, Cursor, OrAbrupt, TokenParser,\n        expression::BindingIdentifier,\n        function::{FormalParameters, FunctionBody},\n        name_in_lexically_declared_names,\n    },\n    source::ReadChar,\n};\nuse ast::operations::{bound_names, lexically_declared_names};\nuse boa_ast::{\n    self as ast, Expression, Punctuator, Span, Spanned, StatementList,\n    declaration::Variable,\n    function::{FormalParameter, FormalParameterList},\n    operations::{ContainsSymbol, contains},\n    statement::Return,\n};\nuse boa_interner::Interner;\n\n/// Arrow function parsing.\n///\n/// More information:\n///  - [MDN documentation][mdn]\n///  - [ECMAScript specification][spec]\n///\n/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/Arrow_functions\n/// [spec]: https://tc39.es/ecma262/#prod-ArrowFunction\n#[derive(Debug, Clone, Copy)]\npub(in crate::parser) struct ArrowFunction {\n    allow_in: AllowIn,\n    allow_yield: AllowYield,\n    allow_await: AllowAwait,\n}\n\nimpl ArrowFunction {\n    /// Creates a new `ArrowFunction` parser.\n    pub(in crate::parser) fn new<I, Y, A>(allow_in: I, allow_yield: Y, allow_await: A) -> Self\n    where\n        I: Into<AllowIn>,\n        Y: Into<AllowYield>,\n        A: Into<AllowAwait>,\n    {\n        Self {\n            allow_in: allow_in.into(),\n            allow_yield: allow_yield.into(),\n            allow_await: allow_await.into(),\n        }\n    }\n}\n\nimpl<R> TokenParser<R> for ArrowFunction\nwhere\n    R: ReadChar,\n{\n    type Output = ast::function::ArrowFunction;\n\n    fn parse(self, cursor: &mut Cursor<R>, interner: &mut Interner) -> ParseResult<Self::Output> {\n        let next_token = cursor.peek(0, interner).or_abrupt()?;\n        let start_linear_span = next_token.linear_span();\n\n        let (params, params_start_position) =\n            if next_token.kind() == &TokenKind::Punctuator(Punctuator::OpenParen) {\n                // CoverParenthesizedExpressionAndArrowParameterList\n                let params_start_position = cursor\n                    .expect(Punctuator::OpenParen, \"arrow function\", interner)?\n                    .span()\n                    .start();\n\n                let params = FormalParameters::new(self.allow_yield, self.allow_await)\n                    .parse(cursor, interner)?;\n                cursor.expect(Punctuator::CloseParen, \"arrow function\", interner)?;\n                (params, params_start_position)\n            } else {\n                let params_start_position = next_token.span().start();\n                let param = BindingIdentifier::new(self.allow_yield, self.allow_await)\n                    .parse(cursor, interner)\n                    .set_context(\"arrow function\")?;\n                (\n                    FormalParameterList::from(FormalParameter::new(\n                        Variable::from_identifier(param, None),\n                        false,\n                    )),\n                    params_start_position,\n                )\n            };\n\n        // Early Error: ArrowFormalParameters are UniqueFormalParameters.\n        if params.has_duplicates() {\n            return Err(Error::lex(LexError::Syntax(\n                \"Duplicate parameter name not allowed in this context\".into(),\n                params_start_position,\n            )));\n        }\n\n        // Early Error: It is a Syntax Error if ArrowParameters Contains YieldExpression is true.\n        if contains(&params, ContainsSymbol::YieldExpression) {\n            return Err(Error::lex(LexError::Syntax(\n                \"Yield expression not allowed in this context\".into(),\n                params_start_position,\n            )));\n        }\n\n        // Early Error: It is a Syntax Error if ArrowParameters Contains AwaitExpression is true.\n        if contains(&params, ContainsSymbol::AwaitExpression) {\n            return Err(Error::lex(LexError::Syntax(\n                \"Await expression not allowed in this context\".into(),\n                params_start_position,\n            )));\n        }\n\n        cursor.peek_expect_no_lineterminator(0, \"arrow function\", interner)?;\n\n        cursor.expect(\n            TokenKind::Punctuator(Punctuator::Arrow),\n            \"arrow function\",\n            interner,\n        )?;\n        let arrow = cursor.arrow();\n        cursor.set_arrow(true);\n        let body = ConciseBody::new(self.allow_in).parse(cursor, interner)?;\n        cursor.set_arrow(arrow);\n\n        // Early Error: It is a Syntax Error if ConciseBodyContainsUseStrict of ConciseBody is true\n        // and IsSimpleParameterList of ArrowParameters is false.\n        if body.strict() && !params.is_simple() {\n            return Err(Error::lex(LexError::Syntax(\n                \"Illegal 'use strict' directive in function with non-simple parameter list\".into(),\n                params_start_position,\n            )));\n        }\n\n        // It is a Syntax Error if any element of the BoundNames of ArrowParameters\n        // also occurs in the LexicallyDeclaredNames of ConciseBody.\n        // https://tc39.es/ecma262/#sec-arrow-function-definitions-static-semantics-early-errors\n        name_in_lexically_declared_names(\n            &bound_names(&params),\n            &lexically_declared_names(&body),\n            params_start_position,\n            interner,\n        )?;\n\n        let linear_pos_end = body.linear_pos_end();\n        let linear_span = start_linear_span.union(linear_pos_end);\n\n        let body_span_end = body.span().end();\n        Ok(ast::function::ArrowFunction::new(\n            None,\n            params,\n            body,\n            linear_span,\n            Span::new(params_start_position, body_span_end),\n        ))\n    }\n}\n\n/// <https://tc39.es/ecma262/#prod-ConciseBody>\n#[derive(Debug, Clone, Copy)]\npub(in crate::parser) struct ConciseBody {\n    allow_in: AllowIn,\n}\n\nimpl ConciseBody {\n    /// Creates a new `ConciseBody` parser.\n    pub(in crate::parser) fn new<I>(allow_in: I) -> Self\n    where\n        I: Into<AllowIn>,\n    {\n        Self {\n            allow_in: allow_in.into(),\n        }\n    }\n}\n\nimpl<R> TokenParser<R> for ConciseBody\nwhere\n    R: ReadChar,\n{\n    type Output = ast::function::FunctionBody;\n\n    fn parse(self, cursor: &mut Cursor<R>, interner: &mut Interner) -> ParseResult<Self::Output> {\n        let stmts = if let TokenKind::Punctuator(Punctuator::OpenBlock) =\n            cursor.peek(0, interner).or_abrupt()?.kind()\n        {\n            FunctionBody::new(false, false, \"arrow function\").parse(cursor, interner)?\n        } else {\n            let expression = ExpressionBody::new(self.allow_in, false).parse(cursor, interner)?;\n            let span = expression.span();\n            ast::function::FunctionBody::new(\n                StatementList::new(\n                    [ast::Statement::Return(Return::new(expression.into())).into()],\n                    cursor.linear_pos(),\n                    false,\n                ),\n                span,\n            )\n        };\n\n        Ok(stmts)\n    }\n}\n\n/// <https://tc39.es/ecma262/#prod-ExpressionBody>\n#[derive(Debug, Clone, Copy)]\npub(super) struct ExpressionBody {\n    allow_in: AllowIn,\n    allow_await: AllowAwait,\n}\n\nimpl ExpressionBody {\n    /// Creates a new `ExpressionBody` parser.\n    pub(super) fn new<I, A>(allow_in: I, allow_await: A) -> Self\n    where\n        I: Into<AllowIn>,\n        A: Into<AllowAwait>,\n    {\n        Self {\n            allow_in: allow_in.into(),\n            allow_await: allow_await.into(),\n        }\n    }\n}\n\nimpl<R> TokenParser<R> for ExpressionBody\nwhere\n    R: ReadChar,\n{\n    type Output = Expression;\n\n    fn parse(self, cursor: &mut Cursor<R>, interner: &mut Interner) -> ParseResult<Self::Output> {\n        AssignmentExpression::new(self.allow_in, false, self.allow_await).parse(cursor, interner)\n    }\n}\n"
  },
  {
    "path": "core/parser/src/parser/expression/assignment/async_arrow_function.rs",
    "content": "//! Async arrow function parsing.\n//!\n//! More information:\n//!  - [MDN documentation][mdn]\n//!  - [ECMAScript specification][spec]\n//!\n//! [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/Arrow_functions\n//! [spec]: https://tc39.es/ecma262/#sec-async-arrow-function-definitions\n\nuse super::arrow_function::ExpressionBody;\nuse crate::{\n    error::{Error, ErrorContext, ParseResult},\n    lexer::{Error as LexError, TokenKind},\n    parser::{\n        AllowIn, AllowYield, Cursor, OrAbrupt, TokenParser,\n        expression::BindingIdentifier,\n        function::{FormalParameters, FunctionBody},\n        name_in_lexically_declared_names,\n    },\n    source::ReadChar,\n};\nuse ast::{\n    Keyword,\n    operations::{ContainsSymbol, bound_names, contains, lexically_declared_names},\n};\nuse boa_ast::{\n    self as ast, Punctuator, Span, Spanned, StatementList,\n    declaration::Variable,\n    function::{FormalParameter, FormalParameterList},\n    statement::Return,\n};\nuse boa_interner::Interner;\n\n/// Async arrow function parsing.\n///\n/// More information:\n///  - [MDN documentation][mdn]\n///  - [ECMAScript specification][spec]\n///\n/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/Arrow_functions\n/// [spec]: https://tc39.es/ecma262/#prod-AsyncArrowFunction\n#[derive(Debug, Clone, Copy)]\npub(in crate::parser) struct AsyncArrowFunction {\n    allow_in: AllowIn,\n    allow_yield: AllowYield,\n}\n\nimpl AsyncArrowFunction {\n    /// Creates a new `AsyncArrowFunction` parser.\n    pub(in crate::parser) fn new<I, Y>(allow_in: I, allow_yield: Y) -> Self\n    where\n        I: Into<AllowIn>,\n        Y: Into<AllowYield>,\n    {\n        Self {\n            allow_in: allow_in.into(),\n            allow_yield: allow_yield.into(),\n        }\n    }\n}\n\nimpl<R> TokenParser<R> for AsyncArrowFunction\nwhere\n    R: ReadChar,\n{\n    type Output = ast::function::AsyncArrowFunction;\n\n    fn parse(self, cursor: &mut Cursor<R>, interner: &mut Interner) -> ParseResult<Self::Output> {\n        let async_token =\n            cursor.expect((Keyword::Async, false), \"async arrow function\", interner)?;\n        let start_linear_span = async_token.linear_span();\n        let async_token_span = async_token.span();\n        cursor.peek_expect_no_lineterminator(0, \"async arrow function\", interner)?;\n\n        let next_token = cursor.peek(0, interner).or_abrupt()?;\n        let (params, params_start_position) =\n            if next_token.kind() == &TokenKind::Punctuator(Punctuator::OpenParen) {\n                let params_start_position = cursor\n                    .expect(Punctuator::OpenParen, \"async arrow function\", interner)?\n                    .span()\n                    .end();\n\n                let params = FormalParameters::new(false, true).parse(cursor, interner)?;\n                cursor.expect(Punctuator::CloseParen, \"async arrow function\", interner)?;\n                (params, params_start_position)\n            } else {\n                let params_start_position = next_token.span().start();\n                let param = BindingIdentifier::new(self.allow_yield, true)\n                    .parse(cursor, interner)\n                    .set_context(\"async arrow function\")?;\n                (\n                    FormalParameterList::from(FormalParameter::new(\n                        Variable::from_identifier(param, None),\n                        false,\n                    )),\n                    params_start_position,\n                )\n            };\n\n        cursor.peek_expect_no_lineterminator(0, \"async arrow function\", interner)?;\n        cursor.expect(Punctuator::Arrow, \"async arrow function\", interner)?;\n\n        let body = AsyncConciseBody::new(self.allow_in).parse(cursor, interner)?;\n\n        // Early Error: ArrowFormalParameters are UniqueFormalParameters.\n        if params.has_duplicates() {\n            return Err(Error::lex(LexError::Syntax(\n                \"Duplicate parameter name not allowed in this context\".into(),\n                params_start_position,\n            )));\n        }\n\n        // Early Error: It is a Syntax Error if CoverCallExpressionAndAsyncArrowHead Contains YieldExpression is true.\n        if contains(&params, ContainsSymbol::YieldExpression) {\n            return Err(Error::lex(LexError::Syntax(\n                \"Yield expression not allowed in this context\".into(),\n                params_start_position,\n            )));\n        }\n\n        // Early Error: It is a Syntax Error if CoverCallExpressionAndAsyncArrowHead Contains AwaitExpression is true.\n        if contains(&params, ContainsSymbol::AwaitExpression) {\n            return Err(Error::lex(LexError::Syntax(\n                \"Await expression not allowed in this context\".into(),\n                params_start_position,\n            )));\n        }\n\n        // Early Error: It is a Syntax Error if AsyncConciseBodyContainsUseStrict of AsyncConciseBody is true and\n        // IsSimpleParameterList of CoverCallExpressionAndAsyncArrowHead is false.\n        if body.strict() && !params.is_simple() {\n            return Err(Error::lex(LexError::Syntax(\n                \"Illegal 'use strict' directive in function with non-simple parameter list\".into(),\n                params_start_position,\n            )));\n        }\n\n        // Early Error: It is a Syntax Error if any element of the BoundNames of CoverCallExpressionAndAsyncArrowHead\n        // also occurs in the LexicallyDeclaredNames of AsyncConciseBody.\n        name_in_lexically_declared_names(\n            &bound_names(&params),\n            &lexically_declared_names(&body),\n            params_start_position,\n            interner,\n        )?;\n\n        let linear_pos_end = body.linear_pos_end();\n        let linear_span = start_linear_span.union(linear_pos_end);\n\n        let body_span_end = body.span().end();\n        Ok(ast::function::AsyncArrowFunction::new(\n            None,\n            params,\n            body,\n            linear_span,\n            Span::new(async_token_span.start(), body_span_end),\n        ))\n    }\n}\n\n/// <https://tc39.es/ecma262/#prod-AsyncConciseBody>\n#[derive(Debug, Clone, Copy)]\npub(in crate::parser) struct AsyncConciseBody {\n    allow_in: AllowIn,\n}\n\nimpl AsyncConciseBody {\n    /// Creates a new `AsyncConciseBody` parser.\n    pub(in crate::parser) fn new<I>(allow_in: I) -> Self\n    where\n        I: Into<AllowIn>,\n    {\n        Self {\n            allow_in: allow_in.into(),\n        }\n    }\n}\n\nimpl<R> TokenParser<R> for AsyncConciseBody\nwhere\n    R: ReadChar,\n{\n    type Output = ast::function::FunctionBody;\n\n    fn parse(self, cursor: &mut Cursor<R>, interner: &mut Interner) -> ParseResult<Self::Output> {\n        let body = if let TokenKind::Punctuator(Punctuator::OpenBlock) =\n            cursor.peek(0, interner).or_abrupt()?.kind()\n        {\n            FunctionBody::new(false, true, \"async arrow function\").parse(cursor, interner)?\n        } else {\n            let expression = ExpressionBody::new(self.allow_in, true).parse(cursor, interner)?;\n            let span = expression.span();\n            ast::function::FunctionBody::new(\n                StatementList::new(\n                    [ast::Statement::Return(Return::new(expression.into())).into()],\n                    cursor.linear_pos(),\n                    false,\n                ),\n                span,\n            )\n        };\n\n        Ok(body)\n    }\n}\n"
  },
  {
    "path": "core/parser/src/parser/expression/assignment/conditional.rs",
    "content": "//! Conditional operator parsing.\n//!\n//! More information:\n//!  - [MDN documentation][mdn]\n//!  - [ECMAScript specification][spec]\n//!\n//! [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Conditional_Operator\n//! [spec]: https://tc39.es/ecma262/#sec-conditional-operator\n\nuse crate::{\n    lexer::TokenKind,\n    parser::{\n        AllowAwait, AllowIn, AllowYield, Cursor, ParseResult, TokenParser,\n        expression::{\n            AssignmentExpression, FormalParameterListOrExpression, ShortCircuitExpression,\n        },\n    },\n    source::ReadChar,\n};\nuse boa_ast::{Punctuator, expression::operator::Conditional};\nuse boa_interner::Interner;\n\n/// Conditional expression parsing.\n///\n/// More information:\n///  - [MDN documentation][mdn]\n///  - [ECMAScript specification][spec]\n///\n/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Conditional_Operator\n/// [spec]: https://tc39.es/ecma262/#prod-ConditionalExpression\n#[derive(Debug, Clone, Copy)]\npub(in crate::parser::expression) struct ConditionalExpression {\n    allow_in: AllowIn,\n    allow_yield: AllowYield,\n    allow_await: AllowAwait,\n}\n\nimpl ConditionalExpression {\n    /// Creates a new `ConditionalExpression` parser.\n    pub(in crate::parser::expression) fn new<I, Y, A>(\n        allow_in: I,\n        allow_yield: Y,\n        allow_await: A,\n    ) -> Self\n    where\n        I: Into<AllowIn>,\n        Y: Into<AllowYield>,\n        A: Into<AllowAwait>,\n    {\n        Self {\n            allow_in: allow_in.into(),\n            allow_yield: allow_yield.into(),\n            allow_await: allow_await.into(),\n        }\n    }\n}\n\nimpl<R> TokenParser<R> for ConditionalExpression\nwhere\n    R: ReadChar,\n{\n    type Output = FormalParameterListOrExpression;\n\n    fn parse(self, cursor: &mut Cursor<R>, interner: &mut Interner) -> ParseResult<Self::Output> {\n        let lhs = ShortCircuitExpression::new(self.allow_in, self.allow_yield, self.allow_await)\n            .parse(cursor, interner)?;\n\n        if let Some(tok) = cursor.peek(0, interner)?\n            && tok.kind() == &TokenKind::Punctuator(Punctuator::Question)\n        {\n            let lhs = lhs.try_into_expression()?;\n\n            cursor.advance(interner);\n            let then_clause = AssignmentExpression::new(true, self.allow_yield, self.allow_await)\n                .parse(cursor, interner)?;\n            cursor.expect(Punctuator::Colon, \"conditional expression\", interner)?;\n\n            let else_clause =\n                AssignmentExpression::new(self.allow_in, self.allow_yield, self.allow_await)\n                    .parse(cursor, interner)?;\n            return Ok(Conditional::new(lhs, then_clause, else_clause).into());\n        }\n\n        Ok(lhs)\n    }\n}\n"
  },
  {
    "path": "core/parser/src/parser/expression/assignment/exponentiation.rs",
    "content": "//! Exponentiation operator parsing.\n//!\n//! More information:\n//!  - [MDN documentation][mdn]\n//!  - [ECMAScript specification][spec]\n//!\n//! [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Arithmetic_Operators#Exponentiation\n//! [spec]: https://tc39.es/ecma262/#sec-exp-operator\n\nuse crate::{\n    lexer::TokenKind,\n    parser::{\n        AllowAwait, AllowYield, Cursor, OrAbrupt, ParseResult, TokenParser,\n        expression::{\n            FormalParameterListOrExpression, unary::UnaryExpression, update::UpdateExpression,\n        },\n    },\n    source::ReadChar,\n};\nuse boa_ast::{\n    Keyword, Punctuator,\n    expression::operator::{Binary, binary::ArithmeticOp},\n};\nuse boa_interner::Interner;\n\n/// Parses an exponentiation expression.\n///\n/// More information:\n///  - [MDN documentation][mdn]\n///  - [ECMAScript specification][spec]\n///\n/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Arithmetic_Operators#Exponentiation\n/// [spec]: https://tc39.es/ecma262/#prod-ExponentiationExpression\n#[derive(Debug, Clone, Copy)]\npub(in crate::parser::expression) struct ExponentiationExpression {\n    allow_yield: AllowYield,\n    allow_await: AllowAwait,\n}\n\nimpl ExponentiationExpression {\n    /// Creates a new `ExponentiationExpression` parser.\n    pub(in crate::parser::expression) fn new<Y, A>(allow_yield: Y, allow_await: A) -> Self\n    where\n        Y: Into<AllowYield>,\n        A: Into<AllowAwait>,\n    {\n        Self {\n            allow_yield: allow_yield.into(),\n            allow_await: allow_await.into(),\n        }\n    }\n}\n\nimpl<R> TokenParser<R> for ExponentiationExpression\nwhere\n    R: ReadChar,\n{\n    type Output = FormalParameterListOrExpression;\n\n    fn parse(self, cursor: &mut Cursor<R>, interner: &mut Interner) -> ParseResult<Self::Output> {\n        let next = cursor.peek(0, interner).or_abrupt()?;\n        match next.kind() {\n            TokenKind::Keyword((Keyword::Delete | Keyword::Void | Keyword::TypeOf, _))\n            | TokenKind::Punctuator(\n                Punctuator::Add | Punctuator::Sub | Punctuator::Not | Punctuator::Neg,\n            ) => {\n                return UnaryExpression::new(self.allow_yield, self.allow_await)\n                    .parse(cursor, interner);\n            }\n            TokenKind::Keyword((Keyword::Await, _)) if self.allow_await.0 => {\n                return UnaryExpression::new(self.allow_yield, self.allow_await)\n                    .parse(cursor, interner);\n            }\n            _ => {}\n        }\n\n        let lhs =\n            UpdateExpression::new(self.allow_yield, self.allow_await).parse(cursor, interner)?;\n        let FormalParameterListOrExpression::Expression(lhs) = lhs else {\n            return Ok(lhs);\n        };\n\n        if let Some(tok) = cursor.peek(0, interner)?\n            && tok.kind() == &TokenKind::Punctuator(Punctuator::Exp)\n        {\n            cursor.advance(interner);\n            return Ok(Binary::new(\n                ArithmeticOp::Exp.into(),\n                lhs,\n                self.parse(cursor, interner)?.try_into_expression()?,\n            )\n            .into());\n        }\n        Ok(lhs.into())\n    }\n}\n"
  },
  {
    "path": "core/parser/src/parser/expression/assignment/mod.rs",
    "content": "//! Assignment operator parsing.\n//!\n//! More information:\n//!  - [MDN documentation][mdn]\n//!  - [ECMAScript specification][spec]\n//!\n//! [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Assignment_Operators#Assignment\n//! [spec]: https://tc39.es/ecma262/#sec-assignment-operators\n\nmod arrow_function;\nmod async_arrow_function;\nmod conditional;\nmod exponentiation;\nmod r#yield;\n\nuse crate::{\n    Error,\n    lexer::{Error as LexError, InputElement, TokenKind},\n    parser::{\n        AllowAwait, AllowIn, AllowYield, Cursor, OrAbrupt, ParseResult, TokenParser,\n        expression::{\n            FormalParameterListOrExpression,\n            assignment::{\n                arrow_function::{ArrowFunction, ConciseBody},\n                async_arrow_function::AsyncArrowFunction,\n                conditional::ConditionalExpression,\n                r#yield::YieldExpression,\n            },\n        },\n        name_in_lexically_declared_names,\n    },\n    source::ReadChar,\n};\nuse boa_ast::{\n    Expression, Keyword, Punctuator, Span, Spanned,\n    expression::operator::assign::{Assign, AssignOp, AssignTarget},\n    operations::{ContainsSymbol, bound_names, contains, lexically_declared_names},\n};\nuse boa_interner::Interner;\n\npub(super) use exponentiation::ExponentiationExpression;\n\n/// Assignment expression parsing.\n///\n/// This can be one of the following:\n///\n///  - [`ConditionalExpression`](../conditional_operator/struct.ConditionalExpression.html)\n///  - `YieldExpression`\n///  - [`ArrowFunction`](../../function/arrow_function/struct.ArrowFunction.html)\n///  - `AsyncArrowFunction`\n///  - [`LeftHandSideExpression`][lhs] `=` `AssignmentExpression`\n///  - [`LeftHandSideExpression`][lhs] `AssignmentOperator` `AssignmentExpression`\n///\n/// More information:\n///  - [MDN documentation][mdn]\n///  - [ECMAScript specification][spec]\n///\n/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Assignment_Operators#Assignment\n/// [spec]: https://tc39.es/ecma262/#prod-AssignmentExpression\n/// [lhs]: ../lhs_expression/struct.LeftHandSideExpression.html\n#[derive(Debug, Clone, Copy)]\npub(in crate::parser) struct AssignmentExpression {\n    allow_in: AllowIn,\n    allow_yield: AllowYield,\n    allow_await: AllowAwait,\n}\n\nimpl AssignmentExpression {\n    /// Creates a new `AssignmentExpression` parser.\n    pub(in crate::parser) fn new<I, Y, A>(allow_in: I, allow_yield: Y, allow_await: A) -> Self\n    where\n        I: Into<AllowIn>,\n        Y: Into<AllowYield>,\n        A: Into<AllowAwait>,\n    {\n        Self {\n            allow_in: allow_in.into(),\n            allow_yield: allow_yield.into(),\n            allow_await: allow_await.into(),\n        }\n    }\n}\n\nimpl<R> TokenParser<R> for AssignmentExpression\nwhere\n    R: ReadChar,\n{\n    type Output = Expression;\n\n    fn parse(self, cursor: &mut Cursor<R>, interner: &mut Interner) -> ParseResult<Expression> {\n        cursor.set_goal(InputElement::RegExp);\n\n        match cursor.peek(0, interner).or_abrupt()?.kind() {\n            // [+Yield]YieldExpression[?In, ?Await]\n            TokenKind::Keyword((Keyword::Yield, _)) if self.allow_yield.0 => {\n                return YieldExpression::new(self.allow_in, self.allow_await)\n                    .parse(cursor, interner);\n            }\n            // ArrowFunction[?In, ?Yield, ?Await] -> ArrowParameters[?Yield, ?Await] -> BindingIdentifier[?Yield, ?Await]\n            TokenKind::IdentifierName(_)\n            | TokenKind::Keyword((Keyword::Yield | Keyword::Await, _)) => {\n                cursor.set_goal(InputElement::Div);\n\n                // Because we already peeked the identifier token, there may be a line terminator before the identifier token.\n                // In that case we have to skip an additional token on the next peek.\n                let skip_n = if cursor.peek_is_line_terminator(0, interner).or_abrupt()? {\n                    2\n                } else {\n                    1\n                };\n                if let Some(tok) = cursor.peek_no_skip_line_term(skip_n, interner)?\n                    && tok.kind() == &TokenKind::Punctuator(Punctuator::Arrow)\n                {\n                    return ArrowFunction::new(self.allow_in, self.allow_yield, self.allow_await)\n                        .parse(cursor, interner)\n                        .map(Expression::ArrowFunction);\n                }\n            }\n            //  AsyncArrowFunction[?In, ?Yield, ?Await]\n            TokenKind::Keyword((Keyword::Async, false)) => {\n                let skip_n = if cursor.peek_is_line_terminator(0, interner).or_abrupt()? {\n                    2\n                } else {\n                    1\n                };\n\n                let peek_1 = cursor.peek(1, interner).or_abrupt()?.kind().clone();\n                if !cursor\n                    .peek_is_line_terminator(skip_n, interner)\n                    .or_abrupt()?\n                    && (matches!(peek_1, TokenKind::Punctuator(Punctuator::OpenParen))\n                        || (matches!(\n                            peek_1,\n                            TokenKind::IdentifierName(_)\n                                | TokenKind::Keyword((\n                                    Keyword::Yield | Keyword::Await | Keyword::Of,\n                                    _\n                                ))\n                        ) && matches!(\n                            cursor.peek(2, interner).or_abrupt()?.kind(),\n                            TokenKind::Punctuator(Punctuator::Arrow)\n                        )))\n                {\n                    return Ok(AsyncArrowFunction::new(self.allow_in, self.allow_yield)\n                        .parse(cursor, interner)?\n                        .into());\n                }\n            }\n            _ => {}\n        }\n\n        cursor.set_goal(InputElement::Div);\n\n        let peek_token = cursor.peek(0, interner).or_abrupt()?;\n        let position = peek_token.span().start();\n        let start_linear_span = peek_token.linear_span();\n        let lhs = ConditionalExpression::new(self.allow_in, self.allow_yield, self.allow_await)\n            .parse(cursor, interner)?;\n\n        // If the left hand side is a parameter list, we must parse an arrow function.\n        let mut lhs = match lhs {\n            FormalParameterListOrExpression::FormalParameterList {\n                fpl: parameters, ..\n            } => {\n                cursor.peek_expect_no_lineterminator(0, \"arrow function\", interner)?;\n\n                cursor.expect(\n                    TokenKind::Punctuator(Punctuator::Arrow),\n                    \"arrow function\",\n                    interner,\n                )?;\n                let arrow = cursor.arrow();\n                cursor.set_arrow(true);\n                let body = ConciseBody::new(self.allow_in).parse(cursor, interner)?;\n                cursor.set_arrow(arrow);\n\n                // Early Error: ArrowFormalParameters are UniqueFormalParameters.\n                if parameters.has_duplicates() {\n                    return Err(Error::lex(LexError::Syntax(\n                        \"Duplicate parameter name not allowed in this context\".into(),\n                        position,\n                    )));\n                }\n\n                // Early Error: It is a Syntax Error if ArrowParameters Contains YieldExpression is true.\n                if contains(&parameters, ContainsSymbol::YieldExpression) {\n                    return Err(Error::lex(LexError::Syntax(\n                        \"Yield expression not allowed in this context\".into(),\n                        position,\n                    )));\n                }\n\n                // Early Error: It is a Syntax Error if ArrowParameters Contains AwaitExpression is true.\n                if contains(&parameters, ContainsSymbol::AwaitExpression) {\n                    return Err(Error::lex(LexError::Syntax(\n                        \"Await expression not allowed in this context\".into(),\n                        position,\n                    )));\n                }\n\n                // Early Error: It is a Syntax Error if ConciseBodyContainsUseStrict of ConciseBody is true\n                // and IsSimpleParameterList of ArrowParameters is false.\n                if body.strict() && !parameters.is_simple() {\n                    return Err(Error::lex(LexError::Syntax(\n                        \"Illegal 'use strict' directive in function with non-simple parameter list\"\n                            .into(),\n                        position,\n                    )));\n                }\n\n                // It is a Syntax Error if any element of the BoundNames of ArrowParameters\n                // also occurs in the LexicallyDeclaredNames of ConciseBody.\n                // https://tc39.es/ecma262/#sec-arrow-function-definitions-static-semantics-early-errors\n                name_in_lexically_declared_names(\n                    &bound_names(&parameters),\n                    &lexically_declared_names(&body),\n                    position,\n                    interner,\n                )?;\n\n                let linear_pos_end = body.linear_pos_end();\n                let linear_span = start_linear_span.union(linear_pos_end);\n\n                let body_span_end = body.span().end();\n                return Ok(boa_ast::function::ArrowFunction::new(\n                    None,\n                    parameters,\n                    body,\n                    linear_span,\n                    Span::new(position, body_span_end),\n                )\n                .into());\n            }\n            FormalParameterListOrExpression::Expression(expression) => expression,\n        };\n\n        // Review if we are trying to assign to an invalid left hand side expression.\n        if let Some(tok) = cursor.peek(0, interner)?.cloned() {\n            match tok.kind() {\n                TokenKind::Punctuator(Punctuator::Assign) => {\n                    cursor.advance(interner);\n                    cursor.set_goal(InputElement::RegExp);\n\n                    let lhs_name = if let Expression::Identifier(ident) = lhs {\n                        Some(ident)\n                    } else {\n                        None\n                    };\n\n                    if let Some(target) = AssignTarget::from_expression(&lhs, cursor.strict()) {\n                        let mut expr = self.parse(cursor, interner)?;\n                        if let Some(ident) = lhs_name {\n                            expr.set_anonymous_function_definition_name(&ident);\n                        }\n                        lhs = Assign::new(AssignOp::Assign, target, expr).into();\n                    } else {\n                        return Err(Error::lex(LexError::Syntax(\n                            \"Invalid left-hand side in assignment\".into(),\n                            tok.span().start(),\n                        )));\n                    }\n                }\n                TokenKind::Punctuator(p) if p.as_assign_op().is_some() => {\n                    cursor.advance(interner);\n                    if let Some(target) =\n                        AssignTarget::from_expression_simple(&lhs, cursor.strict())\n                    {\n                        let assignop = p.as_assign_op().expect(\"assignop disappeared\");\n\n                        let mut rhs = self.parse(cursor, interner)?;\n                        if (assignop == AssignOp::BoolAnd\n                            || assignop == AssignOp::BoolOr\n                            || assignop == AssignOp::Coalesce)\n                            && let AssignTarget::Identifier(ident) = target\n                        {\n                            rhs.set_anonymous_function_definition_name(&ident);\n                        }\n                        lhs = Assign::new(assignop, target, rhs).into();\n                    } else {\n                        return Err(Error::lex(LexError::Syntax(\n                            \"Invalid left-hand side in assignment\".into(),\n                            tok.span().start(),\n                        )));\n                    }\n                }\n                _ => {}\n            }\n        }\n\n        Ok(lhs)\n    }\n}\n"
  },
  {
    "path": "core/parser/src/parser/expression/assignment/yield.rs",
    "content": "//! `YieldExpression` parsing.\n//!\n//! More information:\n//!  - [MDN documentation][mdn]\n//!  - [ECMAScript specification][spec]\n//!\n//! [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/yield\n//! [spec]: https://tc39.es/ecma262/#prod-YieldExpression\n\nuse super::AssignmentExpression;\nuse crate::{\n    lexer::TokenKind,\n    parser::{AllowAwait, AllowIn, OrAbrupt, ParseResult, TokenParser, cursor::Cursor},\n    source::ReadChar,\n};\nuse boa_ast::{Expression, Keyword, Punctuator, Span, Spanned, expression::Yield};\nuse boa_interner::Interner;\n\n/// `YieldExpression` parsing.\n///\n/// More information:\n///  - [MDN documentation][mdn]\n///  - [ECMAScript specification][spec]\n///\n/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/yield\n/// [spec]: https://tc39.es/ecma262/#prod-YieldExpression\n#[derive(Debug, Clone, Copy)]\npub(in crate::parser) struct YieldExpression {\n    allow_in: AllowIn,\n    allow_await: AllowAwait,\n}\n\nimpl YieldExpression {\n    /// Creates a new `YieldExpression` parser.\n    pub(in crate::parser) fn new<I, A>(allow_in: I, allow_await: A) -> Self\n    where\n        I: Into<AllowIn>,\n        A: Into<AllowAwait>,\n    {\n        Self {\n            allow_in: allow_in.into(),\n            allow_await: allow_await.into(),\n        }\n    }\n}\n\nimpl<R> TokenParser<R> for YieldExpression\nwhere\n    R: ReadChar,\n{\n    type Output = Expression;\n\n    fn parse(self, cursor: &mut Cursor<R>, interner: &mut Interner) -> ParseResult<Self::Output> {\n        let yield_span = cursor\n            .expect(\n                TokenKind::Keyword((Keyword::Yield, false)),\n                \"yield expression\",\n                interner,\n            )?\n            .span();\n\n        if matches!(\n            cursor.peek_is_line_terminator(0, interner)?,\n            Some(true) | None\n        ) {\n            return Ok(Yield::new(None, false, yield_span).into());\n        }\n\n        let token = cursor.peek(0, interner).or_abrupt()?;\n        match token.kind() {\n            TokenKind::Punctuator(Punctuator::Mul) => {\n                cursor.advance(interner);\n                let expr = AssignmentExpression::new(self.allow_in, true, self.allow_await)\n                    .parse(cursor, interner)?;\n                let expr_span_end = expr.span().end();\n\n                Ok(Yield::new(\n                    Some(expr),\n                    true,\n                    Span::new(yield_span.start(), expr_span_end),\n                )\n                .into())\n            }\n            TokenKind::IdentifierName(_)\n            | TokenKind::Punctuator(\n                Punctuator::OpenParen\n                | Punctuator::Add\n                | Punctuator::Sub\n                | Punctuator::Not\n                | Punctuator::Neg\n                | Punctuator::Inc\n                | Punctuator::Dec\n                | Punctuator::OpenBracket\n                | Punctuator::OpenBlock\n                | Punctuator::Div,\n            )\n            | TokenKind::Keyword((\n                Keyword::Yield\n                | Keyword::Await\n                | Keyword::Delete\n                | Keyword::Void\n                | Keyword::TypeOf\n                | Keyword::New\n                | Keyword::This\n                | Keyword::Function\n                | Keyword::Class\n                | Keyword::Async\n                | Keyword::Super\n                | Keyword::Import,\n                _,\n            ))\n            | TokenKind::BooleanLiteral(_)\n            | TokenKind::NullLiteral(_)\n            | TokenKind::StringLiteral(_)\n            | TokenKind::TemplateNoSubstitution(_)\n            | TokenKind::NumericLiteral(_)\n            | TokenKind::RegularExpressionLiteral(_, _)\n            | TokenKind::TemplateMiddle(_) => {\n                let expr = AssignmentExpression::new(self.allow_in, true, self.allow_await)\n                    .parse(cursor, interner)?;\n                let expr_span_end = expr.span().end();\n\n                Ok(Yield::new(\n                    Some(expr),\n                    false,\n                    Span::new(yield_span.start(), expr_span_end),\n                )\n                .into())\n            }\n            _ => Ok(Yield::new(None, false, yield_span).into()),\n        }\n    }\n}\n"
  },
  {
    "path": "core/parser/src/parser/expression/await_expr.rs",
    "content": "//! Await expression parsing.\n//!\n//! More information:\n//!  - [MDN documentation][mdn]\n//!  - [ECMAScript specification][spec]\n//!\n//! [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/await\n//! [spec]: https://tc39.es/ecma262/#prod-AwaitExpression\n\nuse super::unary::UnaryExpression;\nuse crate::{\n    lexer::TokenKind,\n    parser::{AllowYield, Cursor, ParseResult, TokenParser},\n    source::ReadChar,\n};\nuse boa_ast::{Keyword, Span, Spanned, expression::Await};\nuse boa_interner::Interner;\n\n/// Parses an await expression.\n///\n/// More information:\n///  - [MDN documentation][mdn]\n///  - [ECMAScript specification][spec]\n///\n/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/await\n/// [spec]: https://tc39.es/ecma262/#prod-AwaitExpression\n#[derive(Debug, Clone, Copy)]\npub(in crate::parser) struct AwaitExpression {\n    allow_yield: AllowYield,\n}\n\nimpl AwaitExpression {\n    /// Creates a new `AwaitExpression` parser.\n    pub(in crate::parser) fn new<Y>(allow_yield: Y) -> Self\n    where\n        Y: Into<AllowYield>,\n    {\n        Self {\n            allow_yield: allow_yield.into(),\n        }\n    }\n}\n\nimpl<R> TokenParser<R> for AwaitExpression\nwhere\n    R: ReadChar,\n{\n    type Output = Await;\n\n    fn parse(self, cursor: &mut Cursor<R>, interner: &mut Interner) -> ParseResult<Self::Output> {\n        let await_span_start = cursor\n            .expect(\n                TokenKind::Keyword((Keyword::Await, false)),\n                \"Await expression parsing\",\n                interner,\n            )?\n            .span()\n            .start();\n\n        let expr = UnaryExpression::new(self.allow_yield, true)\n            .parse(cursor, interner)?\n            .try_into_expression()?;\n        let expr_span_end = expr.span().end();\n\n        Ok(Await::new(\n            expr.into(),\n            Span::new(await_span_start, expr_span_end),\n        ))\n    }\n}\n"
  },
  {
    "path": "core/parser/src/parser/expression/fpl_or_exp.rs",
    "content": "use crate::{Error, error::ParseResult};\nuse boa_ast::{self as ast, Position, function::FormalParameterList};\n\npub(crate) enum FormalParameterListOrExpression {\n    FormalParameterList {\n        fpl: FormalParameterList,\n        span_start: Position,\n    },\n    Expression(ast::Expression),\n}\n\nimpl FormalParameterListOrExpression {\n    pub(crate) fn try_into_expression(self) -> ParseResult<ast::Expression> {\n        match self {\n            FormalParameterListOrExpression::Expression(expr) => Ok(expr),\n            FormalParameterListOrExpression::FormalParameterList { span_start, .. } => {\n                Err(Error::General {\n                    message: \"invalid arrow-function arguments (parentheses around the arrow-function may help)\".into(),\n                    position: span_start,\n                })\n            }\n        }\n    }\n}\n\nimpl<T> From<T> for FormalParameterListOrExpression\nwhere\n    T: Into<ast::Expression>,\n{\n    fn from(value: T) -> Self {\n        Self::Expression(value.into())\n    }\n}\n"
  },
  {
    "path": "core/parser/src/parser/expression/identifiers.rs",
    "content": "//! Identifiers parsing.\n//!\n//! More information:\n//!  - [ECMAScript specification][spec]\n//!\n//! [spec]: https://tc39.es/ecma262/#sec-identifiers\n\nuse crate::{\n    Error,\n    lexer::TokenKind,\n    parser::{AllowAwait, AllowYield, OrAbrupt, ParseResult, TokenParser, cursor::Cursor},\n    source::ReadChar,\n};\nuse boa_ast::Spanned;\nuse boa_ast::expression::Identifier as AstIdentifier;\nuse boa_interner::{Interner, Sym};\n\n/// Identifier reference parsing.\n///\n/// More information:\n///  - [ECMAScript specification][spec]\n///\n/// [spec]: https://tc39.es/ecma262/#prod-IdentifierReference\n#[derive(Debug, Clone, Copy)]\npub(in crate::parser) struct IdentifierReference {\n    allow_yield: AllowYield,\n    allow_await: AllowAwait,\n}\n\nimpl IdentifierReference {\n    /// Creates a new `IdentifierReference` parser.\n    #[inline]\n    pub(in crate::parser) fn new<Y, A>(allow_yield: Y, allow_await: A) -> Self\n    where\n        Y: Into<AllowYield>,\n        A: Into<AllowAwait>,\n    {\n        Self {\n            allow_yield: allow_yield.into(),\n            allow_await: allow_await.into(),\n        }\n    }\n}\n\nimpl<R> TokenParser<R> for IdentifierReference\nwhere\n    R: ReadChar,\n{\n    type Output = AstIdentifier;\n\n    fn parse(self, cursor: &mut Cursor<R>, interner: &mut Interner) -> ParseResult<Self::Output> {\n        let span = cursor.peek(0, interner).or_abrupt()?.span();\n        let ident = Identifier.parse(cursor, interner)?;\n        match ident.sym() {\n            Sym::YIELD if self.allow_yield.0 => Err(Error::unexpected(\n                \"yield\",\n                span,\n                \"keyword `yield` not allowed in this context\",\n            )),\n            Sym::AWAIT if self.allow_await.0 => Err(Error::unexpected(\n                \"await\",\n                span,\n                \"keyword `await` not allowed in this context\",\n            )),\n            _ => Ok(ident),\n        }\n    }\n}\n\n/// Binding identifier parsing.\n///\n/// More information:\n///  - [ECMAScript specification][spec]\n///\n/// [spec]: https://tc39.es/ecma262/#prod-BindingIdentifier\n#[derive(Debug, Clone, Copy)]\npub(in crate::parser) struct BindingIdentifier {\n    allow_yield: AllowYield,\n    allow_await: AllowAwait,\n}\n\nimpl BindingIdentifier {\n    /// Creates a new `BindingIdentifier` parser.\n    #[inline]\n    pub(in crate::parser) fn new<Y, A>(allow_yield: Y, allow_await: A) -> Self\n    where\n        Y: Into<AllowYield>,\n        A: Into<AllowAwait>,\n    {\n        Self {\n            allow_yield: allow_yield.into(),\n            allow_await: allow_await.into(),\n        }\n    }\n}\n\nimpl<R> TokenParser<R> for BindingIdentifier\nwhere\n    R: ReadChar,\n{\n    type Output = AstIdentifier;\n\n    /// Strict mode parsing as per <https://tc39.es/ecma262/#sec-identifiers-static-semantics-early-errors>.\n    fn parse(self, cursor: &mut Cursor<R>, interner: &mut Interner) -> ParseResult<Self::Output> {\n        let span = cursor.peek(0, interner).or_abrupt()?.span();\n        let ident = Identifier.parse(cursor, interner)?;\n        match ident.sym() {\n            Sym::ARGUMENTS | Sym::EVAL if cursor.strict() => {\n                let name = interner\n                    .resolve_expect(ident.sym())\n                    .utf8()\n                    .expect(\"keyword must be utf-8\");\n                Err(Error::general(\n                    format!(\"binding identifier `{name}` not allowed in strict mode\"),\n                    span.start(),\n                ))\n            }\n            Sym::YIELD if self.allow_yield.0 => Err(Error::general(\n                \"keyword `yield` not allowed in this context\",\n                span.start(),\n            )),\n            Sym::AWAIT if self.allow_await.0 => Err(Error::general(\n                \"keyword `await` not allowed in this context\",\n                span.start(),\n            )),\n            _ => Ok(ident),\n        }\n    }\n}\n\n/// Label identifier parsing.\n///\n/// This seems to be the same as an `IdentifierReference`.\n///\n/// More information:\n///  - [ECMAScript specification][spec]\n///\n/// [spec]: https://tc39.es/ecma262/#prod-LabelIdentifier\npub(in crate::parser) type LabelIdentifier = IdentifierReference;\n\n/// Identifier parsing.\n///\n/// More information:\n///  - [ECMAScript specification][spec]\n///\n/// [spec]: https://tc39.es/ecma262/#prod-Identifier\n#[derive(Debug, Clone, Copy)]\npub(in crate::parser) struct Identifier;\n\nimpl<R> TokenParser<R> for Identifier\nwhere\n    R: ReadChar,\n{\n    type Output = AstIdentifier;\n\n    fn parse(self, cursor: &mut Cursor<R>, interner: &mut Interner) -> ParseResult<Self::Output> {\n        let tok = cursor.next(interner).or_abrupt()?;\n\n        let ident = match tok.kind() {\n            TokenKind::IdentifierName((ident, _)) => *ident,\n            TokenKind::Keyword((kw, _)) => kw.to_sym(),\n            _ => {\n                return Err(Error::expected(\n                    [\"identifier\".to_owned()],\n                    tok.to_string(interner),\n                    tok.span(),\n                    \"identifier parsing\",\n                ));\n            }\n        };\n\n        if cursor.strict() && ident.is_strict_reserved_identifier() {\n            return Err(Error::unexpected(\n                interner\n                    .resolve_expect(ident)\n                    .utf8()\n                    .expect(\"keyword must always be utf-8\"),\n                tok.span(),\n                \"strict reserved word cannot be an identifier\",\n            ));\n        }\n\n        if cursor.module() && ident == Sym::AWAIT {\n            return Err(Error::unexpected(\n                \"await\",\n                tok.span(),\n                \"`await` cannot be used as an identifier in a module\",\n            ));\n        }\n\n        if ident.is_reserved_identifier() {\n            return Err(Error::unexpected(\n                interner\n                    .resolve_expect(ident)\n                    .utf8()\n                    .expect(\"keyword must always be utf-8\"),\n                tok.span(),\n                \"reserved word cannot be an identifier\",\n            ));\n        }\n\n        Ok(AstIdentifier::new(ident, tok.span()))\n    }\n}\n"
  },
  {
    "path": "core/parser/src/parser/expression/left_hand_side/arguments.rs",
    "content": "//! Argument parsing.\n//!\n//! More information:\n//!  - [MDN documentation][mdn]\n//!  - [ECMAScript specification][spec]\n//!\n//! [mdn]: https://developer.mozilla.org/en-US/docs/Glossary/Argument\n//! [spec]: https://tc39.es/ecma262/#prod-Arguments\n\nuse crate::{\n    Error,\n    lexer::{InputElement, TokenKind},\n    parser::{\n        AllowAwait, AllowYield, Cursor, OrAbrupt, ParseResult, TokenParser,\n        expression::AssignmentExpression,\n    },\n    source::ReadChar,\n};\nuse boa_ast::{Expression, Punctuator, Span, Spanned, expression::Spread};\nuse boa_interner::Interner;\n\n/// Parses a list of arguments.\n///\n/// More information:\n///  - [MDN documentation][mdn]\n///  - [ECMAScript specification][spec]\n///\n/// [mdn]: https://developer.mozilla.org/en-US/docs/Glossary/Argument\n/// [spec]: https://tc39.es/ecma262/#prod-Arguments\n#[derive(Debug, Clone, Copy)]\npub(in crate::parser::expression) struct Arguments {\n    allow_yield: AllowYield,\n    allow_await: AllowAwait,\n}\n\nimpl Arguments {\n    /// Creates a new `Arguments` parser.\n    pub(in crate::parser::expression) fn new<Y, A>(allow_yield: Y, allow_await: A) -> Self\n    where\n        Y: Into<AllowYield>,\n        A: Into<AllowAwait>,\n    {\n        Self {\n            allow_yield: allow_yield.into(),\n            allow_await: allow_await.into(),\n        }\n    }\n}\n\nimpl<R> TokenParser<R> for Arguments\nwhere\n    R: ReadChar,\n{\n    type Output = (Box<[Expression]>, Span);\n\n    fn parse(self, cursor: &mut Cursor<R>, interner: &mut Interner) -> ParseResult<Self::Output> {\n        let start = cursor\n            .expect(Punctuator::OpenParen, \"arguments\", interner)?\n            .span()\n            .start();\n\n        let mut args = Vec::new();\n        let end = loop {\n            cursor.set_goal(InputElement::RegExp);\n            let next_token = cursor.peek(0, interner).or_abrupt()?;\n\n            match next_token.kind() {\n                TokenKind::Punctuator(Punctuator::CloseParen) => {\n                    let end = next_token.span().end();\n                    cursor.advance(interner);\n                    break end;\n                }\n                TokenKind::Punctuator(Punctuator::Comma) => {\n                    let next_token = cursor.next(interner)?.expect(\", token vanished\"); // Consume the token.\n\n                    if args.is_empty() {\n                        return Err(Error::expected(\n                            [String::from(\"expression\")],\n                            next_token.to_string(interner),\n                            next_token.span(),\n                            \"call\",\n                        ));\n                    }\n\n                    if let Some(next) = cursor.next_if(Punctuator::CloseParen, interner)? {\n                        break next.span().end();\n                    }\n                }\n                _ => {\n                    if !args.is_empty() {\n                        return Err(Error::expected(\n                            [\",\".to_owned(), \")\".to_owned()],\n                            next_token.to_string(interner),\n                            next_token.span(),\n                            \"argument list\",\n                        ));\n                    }\n                }\n            }\n\n            if let Some(spread_token) = cursor.next_if(Punctuator::Spread, interner)? {\n                let target = AssignmentExpression::new(true, self.allow_yield, self.allow_await)\n                    .parse(cursor, interner)?;\n                let target_span_end = target.span().end();\n\n                args.push(\n                    Spread::new(\n                        target,\n                        Span::new(spread_token.span().start(), target_span_end),\n                    )\n                    .into(),\n                );\n            } else {\n                args.push(\n                    AssignmentExpression::new(true, self.allow_yield, self.allow_await)\n                        .parse(cursor, interner)?,\n                );\n            }\n        };\n        cursor.set_goal(InputElement::Div);\n        Ok((args.into_boxed_slice(), Span::new(start, end)))\n    }\n}\n"
  },
  {
    "path": "core/parser/src/parser/expression/left_hand_side/call.rs",
    "content": "//! Call expression parsing.\n//!\n//! More information:\n//!  - [MDN documentation][mdn]\n//!  - [ECMAScript specification][spec]\n//!\n//! [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Functions\n//! [spec]: https://tc39.es/ecma262/#prod-CallExpression\n\nuse super::arguments::Arguments;\nuse crate::{\n    Error,\n    lexer::TokenKind,\n    parser::{\n        AllowAwait, AllowYield, Cursor, OrAbrupt, ParseResult, TokenParser,\n        expression::{Expression, left_hand_side::template::TaggedTemplateLiteral},\n    },\n    source::ReadChar,\n};\nuse ast::function::PrivateName;\nuse boa_ast::{\n    self as ast, Punctuator, Span, Spanned,\n    expression::{\n        Call, Identifier,\n        access::{PrivatePropertyAccess, SimplePropertyAccess},\n    },\n};\nuse boa_interner::{Interner, Sym};\n\n/// Parses a call expression.\n///\n/// More information:\n///  - [ECMAScript specification][spec]\n///\n/// [spec]: https://tc39.es/ecma262/#prod-CallExpression\n#[derive(Debug)]\npub(super) struct CallExpression {\n    allow_yield: AllowYield,\n    allow_await: AllowAwait,\n    first_member_expr: ast::Expression,\n}\n\nimpl CallExpression {\n    /// Creates a new `CallExpression` parser.\n    pub(super) fn new<Y, A>(\n        allow_yield: Y,\n        allow_await: A,\n        first_member_expr: ast::Expression,\n    ) -> Self\n    where\n        Y: Into<AllowYield>,\n        A: Into<AllowAwait>,\n    {\n        Self {\n            allow_yield: allow_yield.into(),\n            allow_await: allow_await.into(),\n            first_member_expr,\n        }\n    }\n}\n\nimpl<R> TokenParser<R> for CallExpression\nwhere\n    R: ReadChar,\n{\n    type Output = ast::Expression;\n\n    fn parse(self, cursor: &mut Cursor<R>, interner: &mut Interner) -> ParseResult<Self::Output> {\n        let token = cursor.peek(0, interner).or_abrupt()?;\n\n        let lhs = if token.kind() == &TokenKind::Punctuator(Punctuator::OpenParen) {\n            let (args, args_span) =\n                Arguments::new(self.allow_yield, self.allow_await).parse(cursor, interner)?;\n\n            Call::new(self.first_member_expr, args, args_span).into()\n        } else {\n            let next_token = cursor.next(interner)?.expect(\"token vanished\");\n            return Err(Error::expected(\n                [\"(\".to_owned()],\n                next_token.to_string(interner),\n                next_token.span(),\n                \"call expression\",\n            ));\n        };\n\n        CallExpressionTail::new(self.allow_yield, self.allow_await, lhs).parse(cursor, interner)\n    }\n}\n\n/// Parses the tail parts of a call expression (property access, successive call, array access).\n#[derive(Debug)]\npub(super) struct CallExpressionTail {\n    allow_yield: AllowYield,\n    allow_await: AllowAwait,\n    call: ast::Expression,\n}\n\nimpl CallExpressionTail {\n    /// Creates a new `CallExpressionTail` parser.\n    pub(super) fn new<Y, A>(allow_yield: Y, allow_await: A, call: ast::Expression) -> Self\n    where\n        Y: Into<AllowYield>,\n        A: Into<AllowAwait>,\n    {\n        Self {\n            allow_yield: allow_yield.into(),\n            allow_await: allow_await.into(),\n            call,\n        }\n    }\n}\n\nimpl<R> TokenParser<R> for CallExpressionTail\nwhere\n    R: ReadChar,\n{\n    type Output = ast::Expression;\n\n    fn parse(self, cursor: &mut Cursor<R>, interner: &mut Interner) -> ParseResult<Self::Output> {\n        let mut lhs = self.call;\n\n        while let Some(token) = cursor.peek(0, interner)?.cloned() {\n            let lhs_span_start = lhs.span().start();\n            match token.kind() {\n                TokenKind::Punctuator(Punctuator::OpenParen) => {\n                    let (args, args_span) = Arguments::new(self.allow_yield, self.allow_await)\n                        .parse(cursor, interner)?;\n                    lhs = Call::new(lhs, args, args_span).into();\n                }\n                TokenKind::Punctuator(Punctuator::Dot) => {\n                    cursor.advance(interner);\n\n                    let token = cursor.next(interner).or_abrupt()?;\n                    let access = match token.kind() {\n                        TokenKind::IdentifierName((name, _)) => {\n                            SimplePropertyAccess::new(lhs, Identifier::new(*name, token.span()))\n                                .into()\n                        }\n                        TokenKind::Keyword((kw, _)) => SimplePropertyAccess::new(\n                            lhs,\n                            Identifier::new(kw.to_sym(), token.span()),\n                        )\n                        .into(),\n                        TokenKind::BooleanLiteral((true, _)) => {\n                            SimplePropertyAccess::new(lhs, Identifier::new(Sym::TRUE, token.span()))\n                                .into()\n                        }\n                        TokenKind::BooleanLiteral((false, _)) => SimplePropertyAccess::new(\n                            lhs,\n                            Identifier::new(Sym::FALSE, token.span()),\n                        )\n                        .into(),\n                        TokenKind::NullLiteral(_) => {\n                            SimplePropertyAccess::new(lhs, Identifier::new(Sym::NULL, token.span()))\n                                .into()\n                        }\n                        TokenKind::PrivateIdentifier(name) => PrivatePropertyAccess::new(\n                            lhs,\n                            PrivateName::new(*name, token.span()),\n                            Span::new(lhs_span_start, token.span().end()),\n                        )\n                        .into(),\n                        _ => {\n                            return Err(Error::expected(\n                                [\"identifier\".to_owned()],\n                                token.to_string(interner),\n                                token.span(),\n                                \"call expression\",\n                            ));\n                        }\n                    };\n\n                    lhs = ast::Expression::PropertyAccess(access);\n                }\n                TokenKind::Punctuator(Punctuator::OpenBracket) => {\n                    cursor.advance(interner);\n                    let idx = Expression::new(true, self.allow_yield, self.allow_await)\n                        .parse(cursor, interner)?;\n                    cursor.expect(Punctuator::CloseBracket, \"call expression\", interner)?;\n                    lhs =\n                        ast::Expression::PropertyAccess(SimplePropertyAccess::new(lhs, idx).into());\n                }\n                TokenKind::TemplateNoSubstitution { .. } | TokenKind::TemplateMiddle { .. } => {\n                    lhs = TaggedTemplateLiteral::new(\n                        self.allow_yield,\n                        self.allow_await,\n                        token.start_group(),\n                        lhs,\n                    )\n                    .parse(cursor, interner)?\n                    .into();\n                }\n                _ => break,\n            }\n        }\n\n        Ok(lhs)\n    }\n}\n"
  },
  {
    "path": "core/parser/src/parser/expression/left_hand_side/member.rs",
    "content": "//! Member expression parsing.\n//!\n//! More information:\n//!  - [ECMAScript specification][spec]\n//!\n//! [spec]: https://tc39.es/ecma262/#prod-MemberExpression\n\nuse super::arguments::Arguments;\nuse crate::{\n    Error,\n    lexer::{InputElement, TokenKind, token::ContainsEscapeSequence},\n    parser::{\n        AllowAwait, AllowYield, Cursor, OrAbrupt, ParseResult, TokenParser,\n        expression::{\n            Expression, FormalParameterListOrExpression,\n            left_hand_side::template::TaggedTemplateLiteral, primary::PrimaryExpression,\n        },\n    },\n    source::ReadChar,\n};\nuse ast::function::PrivateName;\nuse boa_ast::{\n    self as ast, Keyword, Punctuator, Span, Spanned,\n    expression::{\n        Call, Identifier, ImportMeta, New, NewTarget,\n        access::{PrivatePropertyAccess, SimplePropertyAccess, SuperPropertyAccess},\n    },\n};\nuse boa_interner::{Interner, Sym};\n\n/// Parses a member expression.\n///\n/// More information:\n///  - [ECMAScript specification][spec]\n///\n/// [spec]: https://tc39.es/ecma262/#prod-MemberExpression\n#[derive(Debug, Clone, Copy)]\npub(super) struct MemberExpression {\n    allow_yield: AllowYield,\n    allow_await: AllowAwait,\n}\n\nimpl MemberExpression {\n    /// Creates a new `MemberExpression` parser.\n    pub(super) fn new<Y, A>(allow_yield: Y, allow_await: A) -> Self\n    where\n        Y: Into<AllowYield>,\n        A: Into<AllowAwait>,\n    {\n        Self {\n            allow_yield: allow_yield.into(),\n            allow_await: allow_await.into(),\n        }\n    }\n}\n\nimpl<R> TokenParser<R> for MemberExpression\nwhere\n    R: ReadChar,\n{\n    type Output = FormalParameterListOrExpression;\n\n    fn parse(self, cursor: &mut Cursor<R>, interner: &mut Interner) -> ParseResult<Self::Output> {\n        cursor.set_goal(InputElement::RegExp);\n\n        let token = cursor.peek(0, interner).or_abrupt()?;\n        let position = token.span().start();\n        let lhs: FormalParameterListOrExpression = match token.kind() {\n            TokenKind::Keyword((Keyword::New | Keyword::Super | Keyword::Import, true)) => {\n                return Err(Error::general(\n                    \"keyword must not contain escaped characters\",\n                    token.span().start(),\n                ));\n            }\n            TokenKind::Keyword((Keyword::Import, false)) => {\n                let import_span = token.span();\n                cursor.advance(interner);\n\n                cursor.expect(\n                    TokenKind::Punctuator(Punctuator::Dot),\n                    \"import.meta\",\n                    interner,\n                )?;\n\n                let token = cursor.next(interner).or_abrupt()?;\n\n                match token.kind() {\n                    TokenKind::IdentifierName((Sym::META, ContainsEscapeSequence(ces))) => {\n                        if *ces {\n                            return Err(Error::general(\n                                \"`import.meta` cannot contain escaped characters\",\n                                token.span().start(),\n                            ));\n                        }\n                    }\n                    _ => {\n                        return Err(Error::expected(\n                            [\"property `meta`\".into()],\n                            token.to_string(interner),\n                            token.span(),\n                            \"import.meta\",\n                        ));\n                    }\n                }\n\n                if !cursor.module() {\n                    return Err(Error::general(\n                        \"invalid `import.meta` expression outside a module\",\n                        position,\n                    ));\n                }\n\n                ImportMeta::new(Span::new(import_span.start(), token.span().end())).into()\n            }\n            TokenKind::Keyword((Keyword::New, false)) => {\n                let new_token_span = token.span();\n                cursor.advance(interner);\n\n                if cursor.next_if(Punctuator::Dot, interner)?.is_some() {\n                    let token = cursor.next(interner).or_abrupt()?;\n                    match token.kind() {\n                        TokenKind::IdentifierName((Sym::TARGET, ContainsEscapeSequence(true))) => {\n                            return Err(Error::general(\n                                \"'new.target' must not contain escaped characters\",\n                                token.span().start(),\n                            ));\n                        }\n                        TokenKind::IdentifierName((Sym::TARGET, ContainsEscapeSequence(false))) => {\n                            NewTarget::new(Span::new(new_token_span.start(), token.span().end()))\n                                .into()\n                        }\n                        _ => {\n                            return Err(Error::general(\n                                \"unexpected private identifier\",\n                                token.span().start(),\n                            ));\n                        }\n                    }\n                } else {\n                    let lhs_inner = self.parse(cursor, interner)?.try_into_expression()?;\n                    let (args, args_span) = match cursor.peek(0, interner)? {\n                        Some(next)\n                            if next.kind() == &TokenKind::Punctuator(Punctuator::OpenParen) =>\n                        {\n                            Arguments::new(self.allow_yield, self.allow_await)\n                                .parse(cursor, interner)?\n                        }\n                        _ => (Box::default(), lhs_inner.span()),\n                    };\n                    let call_node = Call::new(\n                        lhs_inner,\n                        args,\n                        Span::new(new_token_span.start(), args_span.end()),\n                    );\n\n                    New::from(call_node).into()\n                }\n            }\n            TokenKind::Keyword((Keyword::Super, _)) => {\n                let super_token_span = token.span();\n                cursor.advance(interner);\n                let token = cursor.next(interner).or_abrupt()?;\n                match token.kind() {\n                    TokenKind::Punctuator(Punctuator::Dot) => {\n                        let token = cursor.next(interner).or_abrupt()?;\n                        let field = match token.kind() {\n                            TokenKind::IdentifierName((name, _)) => SuperPropertyAccess::new(\n                                Identifier::new(*name, token.span()).into(),\n                                Span::new(super_token_span.start(), token.span().end()),\n                            ),\n                            TokenKind::Keyword((kw, _)) => SuperPropertyAccess::new(\n                                Identifier::new(kw.to_sym(), token.span()).into(),\n                                Span::new(super_token_span.start(), token.span().end()),\n                            ),\n                            TokenKind::BooleanLiteral((true, _)) => SuperPropertyAccess::new(\n                                Identifier::new(Sym::TRUE, token.span()).into(),\n                                Span::new(super_token_span.start(), token.span().end()),\n                            ),\n                            TokenKind::BooleanLiteral((false, _)) => SuperPropertyAccess::new(\n                                Identifier::new(Sym::FALSE, token.span()).into(),\n                                Span::new(super_token_span.start(), token.span().end()),\n                            ),\n                            TokenKind::NullLiteral(_) => SuperPropertyAccess::new(\n                                Identifier::new(Sym::NULL, token.span()).into(),\n                                Span::new(super_token_span.start(), token.span().end()),\n                            ),\n                            TokenKind::PrivateIdentifier(_) => {\n                                return Err(Error::general(\n                                    \"unexpected private identifier\",\n                                    token.span().start(),\n                                ));\n                            }\n                            _ => {\n                                return Err(Error::unexpected(\n                                    token.to_string(interner),\n                                    token.span(),\n                                    \"expected super property\",\n                                ));\n                            }\n                        };\n                        ast::Expression::PropertyAccess(field.into()).into()\n                    }\n                    TokenKind::Punctuator(Punctuator::OpenBracket) => {\n                        let expr = Expression::new(true, self.allow_yield, self.allow_await)\n                            .parse(cursor, interner)?;\n                        let token_span = cursor\n                            .expect(Punctuator::CloseBracket, \"super property\", interner)?\n                            .span();\n\n                        ast::Expression::PropertyAccess(\n                            SuperPropertyAccess::new(\n                                expr.into(),\n                                Span::new(super_token_span.start(), token_span.end()),\n                            )\n                            .into(),\n                        )\n                        .into()\n                    }\n                    _ => {\n                        return Err(Error::unexpected(\n                            token.to_string(interner),\n                            token.span(),\n                            \"expected super property\",\n                        ));\n                    }\n                }\n            }\n            _ => PrimaryExpression::new(self.allow_yield, self.allow_await)\n                .parse(cursor, interner)?,\n        };\n\n        let FormalParameterListOrExpression::Expression(mut lhs) = lhs else {\n            return Ok(lhs);\n        };\n\n        cursor.set_goal(InputElement::TemplateTail);\n\n        while let Some(tok) = cursor.peek(0, interner)? {\n            match tok.kind() {\n                TokenKind::Punctuator(Punctuator::Dot) => {\n                    cursor\n                        .next(interner)?\n                        .expect(\"dot punctuator token disappeared\"); // We move the parser forward.\n\n                    let token = cursor.next(interner).or_abrupt()?;\n\n                    let lhs_span = lhs.span();\n                    let access = match token.kind() {\n                        TokenKind::IdentifierName((name, _)) => {\n                            SimplePropertyAccess::new(lhs, Identifier::new(*name, token.span()))\n                                .into()\n                        }\n                        TokenKind::Keyword((kw, _)) => SimplePropertyAccess::new(\n                            lhs,\n                            Identifier::new(kw.to_sym(), token.span()),\n                        )\n                        .into(),\n                        TokenKind::BooleanLiteral((true, _)) => {\n                            SimplePropertyAccess::new(lhs, Identifier::new(Sym::TRUE, token.span()))\n                                .into()\n                        }\n                        TokenKind::BooleanLiteral((false, _)) => SimplePropertyAccess::new(\n                            lhs,\n                            Identifier::new(Sym::FALSE, token.span()),\n                        )\n                        .into(),\n                        TokenKind::NullLiteral(_) => {\n                            SimplePropertyAccess::new(lhs, Identifier::new(Sym::NULL, token.span()))\n                                .into()\n                        }\n                        TokenKind::PrivateIdentifier(name) => PrivatePropertyAccess::new(\n                            lhs,\n                            PrivateName::new(*name, token.span()),\n                            Span::new(lhs_span.start(), token.span().end()),\n                        )\n                        .into(),\n                        _ => {\n                            return Err(Error::expected(\n                                [\"identifier\".to_owned()],\n                                token.to_string(interner),\n                                token.span(),\n                                \"member expression\",\n                            ));\n                        }\n                    };\n\n                    lhs = ast::Expression::PropertyAccess(access);\n                }\n                TokenKind::Punctuator(Punctuator::OpenBracket) => {\n                    cursor\n                        .next(interner)?\n                        .expect(\"open bracket punctuator token disappeared\"); // We move the parser forward.\n                    let idx = Expression::new(true, self.allow_yield, self.allow_await)\n                        .parse(cursor, interner)?;\n                    cursor.expect(Punctuator::CloseBracket, \"member expression\", interner)?;\n                    lhs =\n                        ast::Expression::PropertyAccess(SimplePropertyAccess::new(lhs, idx).into());\n                }\n                TokenKind::TemplateNoSubstitution { .. } | TokenKind::TemplateMiddle { .. } => {\n                    lhs = TaggedTemplateLiteral::new(\n                        self.allow_yield,\n                        self.allow_await,\n                        tok.start_group(),\n                        lhs,\n                    )\n                    .parse(cursor, interner)?\n                    .into();\n                }\n                _ => break,\n            }\n        }\n\n        Ok(lhs.into())\n    }\n}\n"
  },
  {
    "path": "core/parser/src/parser/expression/left_hand_side/mod.rs",
    "content": "//! Left hand side expression parsing.\n//!\n//! More information:\n//!  - [MDN documentation][mdn]\n//!  - [ECMAScript specification][spec]\n//!\n//! [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Expressions_and_Operators#Left-hand-side_expressions\n//! [spec]: https://tc39.es/ecma262/#sec-left-hand-side-expressions\n\n#[cfg(test)]\nmod tests;\n\nmod arguments;\nmod call;\nmod member;\nmod optional;\nmod template;\n\nuse crate::{\n    Error,\n    lexer::{InputElement, TokenKind},\n    parser::{\n        AllowAwait, AllowYield, Cursor, ParseResult, TokenParser,\n        expression::{\n            AssignmentExpression, FormalParameterListOrExpression,\n            left_hand_side::{\n                arguments::Arguments,\n                call::{CallExpression, CallExpressionTail},\n                member::MemberExpression,\n                optional::OptionalExpression,\n            },\n        },\n    },\n    source::ReadChar,\n};\nuse boa_ast::{\n    Keyword, Position, Punctuator, Span, Spanned,\n    expression::{ImportCall, ImportPhase, SuperCall},\n};\nuse boa_interner::{Interner, Sym};\n\n/// Parses a left hand side expression.\n///\n/// More information:\n///  - [MDN documentation][mdn]\n///  - [ECMAScript specification][spec]\n///\n/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Expressions_and_Operators#Left-hand-side_expressions\n/// [spec]: https://tc39.es/ecma262/#prod-LeftHandSideExpression\n#[derive(Debug, Clone, Copy)]\npub(in crate::parser) struct LeftHandSideExpression {\n    allow_yield: AllowYield,\n    allow_await: AllowAwait,\n}\n\nimpl LeftHandSideExpression {\n    /// Creates a new `LeftHandSideExpression` parser.\n    pub(in crate::parser) fn new<Y, A>(allow_yield: Y, allow_await: A) -> Self\n    where\n        Y: Into<AllowYield>,\n        A: Into<AllowAwait>,\n    {\n        Self {\n            allow_yield: allow_yield.into(),\n            allow_await: allow_await.into(),\n        }\n    }\n}\n\nimpl<R> TokenParser<R> for LeftHandSideExpression\nwhere\n    R: ReadChar,\n{\n    type Output = FormalParameterListOrExpression;\n\n    fn parse(self, cursor: &mut Cursor<R>, interner: &mut Interner) -> ParseResult<Self::Output> {\n        /// Checks if we need to parse a keyword call expression `keyword()`.\n        ///\n        /// It first checks if the next token is `keyword`, and if it is, it checks if the second next\n        /// token is the open parenthesis (`(`) punctuator.\n        ///\n        /// This is needed because the `if let` chain is very complex, and putting it inline in the\n        /// initialization of `lhs` would make it very hard to return an expression over all\n        /// possible branches of the `if let`s. Instead, we extract the check into its own function,\n        /// then use it inside the condition of a simple `if ... else` expression.\n        fn is_keyword_call<R: ReadChar>(\n            keyword: Keyword,\n            cursor: &mut Cursor<R>,\n            interner: &mut Interner,\n        ) -> ParseResult<Option<Position>> {\n            if let Some(next) = cursor.peek(0, interner)?\n                && let TokenKind::Keyword((kw, escaped)) = next.kind()\n            {\n                let keyword_token_start = next.span().start();\n                if kw == &keyword {\n                    if *escaped {\n                        return Err(Error::general(\n                            format!(\n                                \"keyword `{}` cannot contain escaped characters\",\n                                kw.as_str().0\n                            ),\n                            keyword_token_start,\n                        ));\n                    }\n                    if let Some(next) = cursor.peek(1, interner)?\n                        && next.kind() == &TokenKind::Punctuator(Punctuator::OpenParen)\n                    {\n                        return Ok(Some(keyword_token_start));\n                    }\n                }\n            }\n            Ok(None)\n        }\n\n        /// Checks if the next tokens form an `import.defer(` or `import.source(` pattern.\n        /// Returns `Some((position, phase))` if matched, `None` otherwise.\n        fn is_import_phase_call<R: ReadChar>(\n            cursor: &mut Cursor<R>,\n            interner: &mut Interner,\n        ) -> ParseResult<Option<(Position, ImportPhase)>> {\n            if let Some(next) = cursor.peek(0, interner)?\n                && let TokenKind::Keyword((Keyword::Import, escaped)) = next.kind()\n            {\n                let keyword_token_start = next.span().start();\n                if *escaped {\n                    return Err(Error::general(\n                        \"keyword `import` cannot contain escaped characters\",\n                        keyword_token_start,\n                    ));\n                }\n                if let Some(dot) = cursor.peek(1, interner)?\n                    && dot.kind() == &TokenKind::Punctuator(Punctuator::Dot)\n                    && let Some(ident_tok) = cursor.peek(2, interner)?\n                    && let TokenKind::IdentifierName((sym, _)) = ident_tok.kind()\n                {\n                    let phase = match *sym {\n                        Sym::DEFER => Some(ImportPhase::Defer),\n                        Sym::SOURCE => Some(ImportPhase::Source),\n                        _ => None,\n                    };\n                    if let Some(phase) = phase\n                        && let Some(paren) = cursor.peek(3, interner)?\n                        && paren.kind() == &TokenKind::Punctuator(Punctuator::OpenParen)\n                    {\n                        return Ok(Some((keyword_token_start, phase)));\n                    }\n                }\n            }\n            Ok(None)\n        }\n\n        cursor.set_goal(InputElement::TemplateTail);\n\n        let mut lhs: FormalParameterListOrExpression =\n            if let Some(start) = is_keyword_call(Keyword::Super, cursor, interner)? {\n                cursor.advance(interner);\n                let (args, args_span) =\n                    Arguments::new(self.allow_yield, self.allow_await).parse(cursor, interner)?;\n                SuperCall::new(args, Span::new(start, args_span.end())).into()\n            } else if let Some(start) = is_keyword_call(Keyword::Import, cursor, interner)? {\n                // Plain `import(...)` call\n                cursor.advance(interner);\n                // `(`\n                cursor.advance(interner);\n\n                let specifier = AssignmentExpression::new(true, self.allow_yield, self.allow_await)\n                    .parse(cursor, interner)?;\n\n                let options =\n                    if cursor\n                        .next_if(TokenKind::Punctuator(Punctuator::Comma), interner)?\n                        .is_some()\n                    {\n                        if cursor.peek(0, interner)?.is_some_and(|t| {\n                            t.kind() == &TokenKind::Punctuator(Punctuator::CloseParen)\n                        }) {\n                            None\n                        } else {\n                            let opts =\n                                AssignmentExpression::new(true, self.allow_yield, self.allow_await)\n                                    .parse(cursor, interner)?;\n                            if cursor.peek(0, interner)?.is_some_and(|t| {\n                                t.kind() == &TokenKind::Punctuator(Punctuator::Comma)\n                            }) {\n                                cursor.advance(interner);\n                            }\n                            Some(opts)\n                        }\n                    } else {\n                        None\n                    };\n\n                let end = cursor\n                    .expect(\n                        TokenKind::Punctuator(Punctuator::CloseParen),\n                        \"import call\",\n                        interner,\n                    )?\n                    .span()\n                    .end();\n\n                CallExpressionTail::new(\n                    self.allow_yield,\n                    self.allow_await,\n                    ImportCall::new(\n                        specifier,\n                        options,\n                        ImportPhase::Evaluation,\n                        Span::new(start, end),\n                    )\n                    .into(),\n                )\n                .parse(cursor, interner)?\n                .into()\n            } else if let Some((start, phase)) = is_import_phase_call(cursor, interner)? {\n                // `import.defer(...)` or `import.source(...)` call\n                // Consume `import`\n                cursor.advance(interner);\n                // Consume `.`\n                cursor.advance(interner);\n                // Consume `defer` or `source`\n                cursor.advance(interner);\n                // Consume `(`\n                cursor.advance(interner);\n\n                let specifier = AssignmentExpression::new(true, self.allow_yield, self.allow_await)\n                    .parse(cursor, interner)?;\n\n                let options =\n                    if cursor\n                        .next_if(TokenKind::Punctuator(Punctuator::Comma), interner)?\n                        .is_some()\n                    {\n                        if cursor.peek(0, interner)?.is_some_and(|t| {\n                            t.kind() == &TokenKind::Punctuator(Punctuator::CloseParen)\n                        }) {\n                            None\n                        } else {\n                            let opts =\n                                AssignmentExpression::new(true, self.allow_yield, self.allow_await)\n                                    .parse(cursor, interner)?;\n                            if cursor.peek(0, interner)?.is_some_and(|t| {\n                                t.kind() == &TokenKind::Punctuator(Punctuator::Comma)\n                            }) {\n                                cursor.advance(interner);\n                            }\n                            Some(opts)\n                        }\n                    } else {\n                        None\n                    };\n\n                let end = cursor\n                    .expect(\n                        TokenKind::Punctuator(Punctuator::CloseParen),\n                        \"import call\",\n                        interner,\n                    )?\n                    .span()\n                    .end();\n\n                CallExpressionTail::new(\n                    self.allow_yield,\n                    self.allow_await,\n                    ImportCall::new(specifier, options, phase, Span::new(start, end)).into(),\n                )\n                .parse(cursor, interner)?\n                .into()\n            } else {\n                let member = MemberExpression::new(self.allow_yield, self.allow_await)\n                    .parse(cursor, interner)?;\n                if let Some(tok) = cursor.peek(0, interner)?\n                    && tok.kind() == &TokenKind::Punctuator(Punctuator::OpenParen)\n                {\n                    CallExpression::new(\n                        self.allow_yield,\n                        self.allow_await,\n                        member.try_into_expression()?,\n                    )\n                    .parse(cursor, interner)?\n                    .into()\n                } else {\n                    member\n                }\n            };\n\n        if let Some(tok) = cursor.peek(0, interner)?\n            && tok.kind() == &TokenKind::Punctuator(Punctuator::Optional)\n        {\n            lhs = OptionalExpression::new(\n                self.allow_yield,\n                self.allow_await,\n                lhs.try_into_expression()?,\n            )\n            .parse(cursor, interner)?\n            .into();\n        }\n\n        Ok(lhs)\n    }\n}\n"
  },
  {
    "path": "core/parser/src/parser/expression/left_hand_side/optional/mod.rs",
    "content": "#[cfg(test)]\nmod tests;\n\nuse crate::{\n    Error,\n    lexer::{Token, TokenKind},\n    parser::{\n        AllowAwait, AllowYield, OrAbrupt, ParseResult, TokenParser, cursor::Cursor,\n        expression::Expression, expression::left_hand_side::arguments::Arguments,\n    },\n    source::ReadChar,\n};\nuse ast::function::PrivateName;\nuse boa_ast::{\n    self as ast, Punctuator, Span, Spanned,\n    expression::{\n        Identifier, Optional, OptionalOperation, OptionalOperationKind, access::PropertyAccessField,\n    },\n};\nuse boa_interner::{Interner, Sym};\n\n/// Parses an optional expression.\n///\n/// More information:\n///  - [MDN documentation][mdn]\n///  - [ECMAScript specification][spec]\n///\n/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Optional_chaining\n/// [spec]: https://tc39.es/ecma262/multipage/ecmascript-language-expressions.html#prod-OptionalExpression\n#[derive(Debug, Clone)]\npub(in crate::parser) struct OptionalExpression {\n    allow_yield: AllowYield,\n    allow_await: AllowAwait,\n    target: ast::Expression,\n}\n\nimpl OptionalExpression {\n    /// Creates a new `OptionalExpression` parser.\n    pub(in crate::parser) fn new<Y, A>(\n        allow_yield: Y,\n        allow_await: A,\n        target: ast::Expression,\n    ) -> Self\n    where\n        Y: Into<AllowYield>,\n        A: Into<AllowAwait>,\n    {\n        Self {\n            allow_yield: allow_yield.into(),\n            allow_await: allow_await.into(),\n            target,\n        }\n    }\n}\n\nimpl<R> TokenParser<R> for OptionalExpression\nwhere\n    R: ReadChar,\n{\n    type Output = Optional;\n\n    fn parse(self, cursor: &mut Cursor<R>, interner: &mut Interner) -> ParseResult<Self::Output> {\n        fn parse_const_access(\n            token: &Token,\n            interner: &Interner,\n        ) -> ParseResult<(OptionalOperationKind, Span)> {\n            let item = match token.kind() {\n                TokenKind::IdentifierName((name, _)) => {\n                    OptionalOperationKind::SimplePropertyAccess {\n                        field: Identifier::new(*name, token.span()).into(),\n                    }\n                }\n                TokenKind::Keyword((kw, _)) => OptionalOperationKind::SimplePropertyAccess {\n                    field: Identifier::new(kw.to_sym(), token.span()).into(),\n                },\n                TokenKind::BooleanLiteral((true, _)) => {\n                    OptionalOperationKind::SimplePropertyAccess {\n                        field: Identifier::new(Sym::TRUE, token.span()).into(),\n                    }\n                }\n                TokenKind::BooleanLiteral((false, _)) => {\n                    OptionalOperationKind::SimplePropertyAccess {\n                        field: Identifier::new(Sym::FALSE, token.span()).into(),\n                    }\n                }\n                TokenKind::NullLiteral(_) => OptionalOperationKind::SimplePropertyAccess {\n                    field: Identifier::new(Sym::NULL, token.span()).into(),\n                },\n                TokenKind::PrivateIdentifier(name) => {\n                    OptionalOperationKind::PrivatePropertyAccess {\n                        field: PrivateName::new(*name, token.span()),\n                    }\n                }\n                _ => {\n                    return Err(Error::expected(\n                        [\"identifier\".to_owned()],\n                        token.to_string(interner),\n                        token.span(),\n                        \"optional chain\",\n                    ));\n                }\n            };\n            Ok((item, token.span()))\n        }\n\n        let mut items = Vec::new();\n\n        while let Some(token) = cursor.peek(0, interner)? {\n            let token_span = token.span();\n            let shorted = match token.kind() {\n                TokenKind::Punctuator(Punctuator::Optional) => {\n                    cursor.advance(interner);\n                    true\n                }\n                TokenKind::Punctuator(Punctuator::OpenParen | Punctuator::OpenBracket) => false,\n                TokenKind::Punctuator(Punctuator::Dot) => {\n                    cursor.advance(interner);\n                    let field = cursor.next(interner).or_abrupt()?;\n\n                    let (item, item_span) = parse_const_access(&field, interner)?;\n                    items.push(OptionalOperation::new(\n                        item,\n                        false,\n                        Span::new(token_span.start(), item_span.end()),\n                    ));\n                    continue;\n                }\n                TokenKind::TemplateMiddle(_) | TokenKind::TemplateNoSubstitution(_) => {\n                    return Err(Error::general(\n                        \"Invalid tagged template on optional chain\",\n                        token.span().start(),\n                    ));\n                }\n                _ => break,\n            };\n\n            let token = cursor.peek(0, interner).or_abrupt()?;\n            let (item, item_span) = match token.kind() {\n                TokenKind::Punctuator(Punctuator::OpenParen) => {\n                    let (args, args_span) = Arguments::new(self.allow_yield, self.allow_await)\n                        .parse(cursor, interner)?;\n                    (OptionalOperationKind::Call { args }, args_span)\n                }\n                TokenKind::Punctuator(Punctuator::OpenBracket) => {\n                    cursor\n                        .next(interner)?\n                        .expect(\"open bracket punctuator token disappeared\"); // We move the parser forward.\n                    let idx = Expression::new(true, self.allow_yield, self.allow_await)\n                        .parse(cursor, interner)?;\n                    let end = cursor\n                        .expect(Punctuator::CloseBracket, \"optional chain\", interner)?\n                        .span()\n                        .end();\n                    (\n                        OptionalOperationKind::SimplePropertyAccess {\n                            field: PropertyAccessField::Expr(Box::new(idx)),\n                        },\n                        Span::new(token_span.start(), end),\n                    )\n                }\n                TokenKind::TemplateMiddle(_) | TokenKind::TemplateNoSubstitution(_) => {\n                    return Err(Error::general(\n                        \"Invalid tagged template on optional chain\",\n                        token_span.start(),\n                    ));\n                }\n                _ => {\n                    let token = cursor.next(interner)?.expect(\"token disappeared\");\n                    let (item, item_span) = parse_const_access(&token, interner)?;\n                    (item, Span::new(token_span.start(), item_span.end()))\n                }\n            };\n\n            items.push(OptionalOperation::new(item, shorted, item_span));\n        }\n\n        let end = items\n            .last()\n            .expect(\"There should be at least one item in the optional AST expression\")\n            .span()\n            .end();\n\n        let target_span_start = self.target.span().start();\n        Ok(Optional::new(\n            self.target,\n            items.into(),\n            Span::new(target_span_start, end),\n        ))\n    }\n}\n"
  },
  {
    "path": "core/parser/src/parser/expression/left_hand_side/optional/tests.rs",
    "content": "use crate::parser::tests::{check_invalid_script, check_script_parser};\n\nuse boa_ast::{\n    Span, Statement,\n    expression::{\n        Identifier, Optional, OptionalOperation, OptionalOperationKind,\n        access::PropertyAccessField, literal::Literal,\n    },\n};\nuse boa_interner::Interner;\nuse boa_macros::utf16;\n\n#[test]\nfn simple() {\n    let interner = &mut Interner::default();\n\n    check_script_parser(\n        r#\"5?.name\"#,\n        vec![\n            Statement::Expression(\n                Optional::new(\n                    Literal::new(5, Span::new((1, 1), (1, 2))).into(),\n                    vec![OptionalOperation::new(\n                        OptionalOperationKind::SimplePropertyAccess {\n                            field: Identifier::new(\n                                interner.get_or_intern_static(\"name\", utf16!(\"name\")),\n                                Span::new((1, 4), (1, 8)),\n                            )\n                            .into(),\n                        },\n                        true,\n                        Span::new((1, 2), (1, 8)),\n                    )]\n                    .into(),\n                    Span::new((1, 1), (1, 8)),\n                )\n                .into(),\n            )\n            .into(),\n        ],\n        interner,\n    );\n}\n\n#[test]\nfn complex_chain() {\n    let interner = &mut Interner::default();\n\n    check_script_parser(\n        r#\"a?.b(true)?.[\"c\"]\"#,\n        vec![\n            Statement::Expression(\n                Optional::new(\n                    Identifier::new(\n                        interner.get_or_intern_static(\"a\", utf16!(\"a\")),\n                        Span::new((1, 1), (1, 2)),\n                    )\n                    .into(),\n                    vec![\n                        OptionalOperation::new(\n                            OptionalOperationKind::SimplePropertyAccess {\n                                field: Identifier::new(\n                                    interner.get_or_intern_static(\"b\", utf16!(\"b\")),\n                                    Span::new((1, 4), (1, 5)),\n                                )\n                                .into(),\n                            },\n                            true,\n                            Span::new((1, 2), (1, 5)),\n                        ),\n                        OptionalOperation::new(\n                            OptionalOperationKind::Call {\n                                args: vec![Literal::new(true, Span::new((1, 6), (1, 10))).into()]\n                                    .into(),\n                            },\n                            false,\n                            Span::new((1, 5), (1, 11)),\n                        ),\n                        OptionalOperation::new(\n                            OptionalOperationKind::SimplePropertyAccess {\n                                field: PropertyAccessField::Expr(Box::new(\n                                    Literal::new(\n                                        interner.get_or_intern_static(\"c\", utf16!(\"c\")),\n                                        Span::new((1, 14), (1, 17)),\n                                    )\n                                    .into(),\n                                )),\n                            },\n                            true,\n                            Span::new((1, 11), (1, 18)),\n                        ),\n                    ]\n                    .into(),\n                    Span::new((1, 1), (1, 18)),\n                )\n                .into(),\n            )\n            .into(),\n        ],\n        interner,\n    );\n}\n\n#[test]\nfn reject_templates() {\n    check_invalid_script(\"console.log?.`Hello`\");\n    check_invalid_script(\"console?.log`Hello`\");\n    check_invalid_script(\n        r#\"\nconst a = console?.log\n`Hello`\"#,\n    );\n}\n\n#[test]\nfn private_identifier_early_error() {\n    check_invalid_script(\"this?.#a\");\n    check_invalid_script(\"this.#a\");\n    check_invalid_script(\"this?.a?.#a\");\n    check_invalid_script(\"this.a.#a\");\n}\n"
  },
  {
    "path": "core/parser/src/parser/expression/left_hand_side/template.rs",
    "content": "use crate::{\n    Error,\n    lexer::TokenKind,\n    parser::{\n        AllowAwait, AllowYield, OrAbrupt, ParseResult, TokenParser, cursor::Cursor,\n        expression::Expression,\n    },\n    source::ReadChar,\n};\nuse boa_ast::{self as ast, PositionGroup, Punctuator, Span, Spanned, expression::TaggedTemplate};\nuse boa_interner::Interner;\n\n/// Parses a tagged template.\n///\n/// More information:\n///  - [ECMAScript specification][spec]\n///\n/// [spec]: https://tc39.es/ecma262/#prod-TemplateLiteral\n#[derive(Debug, Clone)]\npub(super) struct TaggedTemplateLiteral {\n    allow_yield: AllowYield,\n    allow_await: AllowAwait,\n    start: PositionGroup,\n    tag: ast::Expression,\n}\n\nimpl TaggedTemplateLiteral {\n    /// Creates a new `TaggedTemplateLiteral` parser.\n    pub(super) fn new<Y, A>(\n        allow_yield: Y,\n        allow_await: A,\n        start: PositionGroup,\n        tag: ast::Expression,\n    ) -> Self\n    where\n        Y: Into<AllowYield>,\n        A: Into<AllowAwait>,\n    {\n        Self {\n            allow_yield: allow_yield.into(),\n            allow_await: allow_await.into(),\n            start,\n            tag,\n        }\n    }\n}\n\nimpl<R> TokenParser<R> for TaggedTemplateLiteral\nwhere\n    R: ReadChar,\n{\n    type Output = TaggedTemplate;\n\n    fn parse(self, cursor: &mut Cursor<R>, interner: &mut Interner) -> ParseResult<Self::Output> {\n        let mut raws = Vec::new();\n        let mut cookeds = Vec::new();\n        let mut exprs = Vec::new();\n\n        let mut token = cursor.next(interner).or_abrupt()?;\n\n        loop {\n            match token.kind() {\n                TokenKind::TemplateMiddle(template_string) => {\n                    raws.push(template_string.raw());\n                    cookeds.push(template_string.cooked());\n                    exprs.push(\n                        Expression::new(true, self.allow_yield, self.allow_await)\n                            .parse(cursor, interner)?,\n                    );\n                    cursor.expect(\n                        TokenKind::Punctuator(Punctuator::CloseBlock),\n                        \"template literal\",\n                        interner,\n                    )?;\n                }\n                TokenKind::TemplateNoSubstitution(template_string) => {\n                    raws.push(template_string.raw());\n                    cookeds.push(template_string.cooked());\n                    return Ok(TaggedTemplate::new(\n                        self.tag,\n                        raws.into_boxed_slice(),\n                        cookeds.into_boxed_slice(),\n                        exprs.into_boxed_slice(),\n                        cursor.tagged_template_identifier(),\n                        Span::new(self.start.position(), token.span().end()),\n                    ));\n                }\n                _ => {\n                    return Err(Error::general(\n                        \"cannot parse tagged template literal\",\n                        self.start,\n                    ));\n                }\n            }\n            token = cursor.lex_template(self.start, interner)?;\n        }\n    }\n}\n"
  },
  {
    "path": "core/parser/src/parser/expression/left_hand_side/tests.rs",
    "content": "use crate::parser::tests::check_script_parser;\nuse boa_ast::{\n    Expression, Span, Statement,\n    expression::{Call, Identifier, access::SimplePropertyAccess},\n};\nuse boa_interner::Interner;\nuse boa_macros::utf16;\n\nmacro_rules! check_call_property_identifier {\n    ($property:literal) => {{\n        let interner = &mut Interner::default();\n        let input = format!(\"a().{}\", $property);\n        #[allow(clippy::cast_possible_truncation)]\n        let input_end = input.len() as u32 + 1;\n        check_script_parser(\n            input.as_str(),\n            vec![\n                Statement::Expression(Expression::PropertyAccess(\n                    SimplePropertyAccess::new(\n                        Call::new(\n                            Identifier::new(\n                                interner.get_or_intern_static(\"a\", utf16!(\"a\")),\n                                Span::new((1, 1), (1, 2)),\n                            )\n                            .into(),\n                            Box::default(),\n                            Span::new((1, 2), (1, 4)),\n                        )\n                        .into(),\n                        Identifier::new(\n                            interner.get_or_intern_static($property, utf16!($property)),\n                            Span::new((1, 5), (1, input_end)),\n                        ),\n                    )\n                    .into(),\n                ))\n                .into(),\n            ],\n            interner,\n        );\n    }};\n}\n\n#[test]\nfn check_call_properties() {\n    check_call_property_identifier!(\"prop\");\n    check_call_property_identifier!(\"true\");\n    check_call_property_identifier!(\"false\");\n    check_call_property_identifier!(\"null\");\n    check_call_property_identifier!(\"let\");\n}\n\nmacro_rules! check_member_property_identifier {\n    ($property:literal) => {{\n        let interner = &mut Interner::default();\n        let input = format!(\"a.{}\", $property);\n        #[allow(clippy::cast_possible_truncation)]\n        let input_end = input.len() as u32 + 1;\n        check_script_parser(\n            input.as_str(),\n            vec![\n                Statement::Expression(Expression::PropertyAccess(\n                    SimplePropertyAccess::new(\n                        Identifier::new(\n                            interner.get_or_intern_static(\"a\", utf16!(\"a\")),\n                            Span::new((1, 1), (1, 2)),\n                        )\n                        .into(),\n                        Identifier::new(\n                            interner.get_or_intern_static($property, utf16!($property)),\n                            Span::new((1, 3), (1, input_end)),\n                        ),\n                    )\n                    .into(),\n                ))\n                .into(),\n            ],\n            interner,\n        );\n    }};\n}\n\n#[test]\nfn check_member_properties() {\n    check_member_property_identifier!(\"prop\");\n    check_member_property_identifier!(\"true\");\n    check_member_property_identifier!(\"false\");\n    check_member_property_identifier!(\"null\");\n    check_member_property_identifier!(\"let\");\n}\n"
  },
  {
    "path": "core/parser/src/parser/expression/mod.rs",
    "content": "//! Expression parsing.\n//!\n//! More information:\n//!  - [MDN documentation][mdn]\n//!  - [ECMAScript specification][spec]\n//!\n//! [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators\n//! [spec]: https://tc39.es/ecma262/#sec-ecmascript-language-expressions\n\nmod assignment;\nmod fpl_or_exp;\nmod identifiers;\nmod left_hand_side;\nmod primary;\nmod unary;\nmod update;\n\npub(in crate::parser) mod await_expr;\n\n#[cfg(test)]\nmod tests;\n\nuse crate::{\n    Error,\n    lexer::{InputElement, TokenKind},\n    parser::{\n        AllowAwait, AllowIn, AllowYield, Cursor, OrAbrupt, ParseResult, TokenParser,\n        expression::assignment::ExponentiationExpression,\n    },\n    source::ReadChar,\n};\nuse boa_ast::{\n    self as ast, Keyword, Position, Punctuator, Spanned,\n    expression::{\n        Identifier,\n        operator::{\n            Binary, BinaryInPrivate,\n            binary::{BinaryOp, LogicalOp},\n        },\n    },\n    function::PrivateName,\n};\nuse boa_interner::{Interner, Sym};\n\npub(super) use self::{assignment::AssignmentExpression, primary::Initializer};\npub(in crate::parser) use {\n    fpl_or_exp::FormalParameterListOrExpression,\n    identifiers::{BindingIdentifier, LabelIdentifier},\n    left_hand_side::LeftHandSideExpression,\n    primary::object_initializer::{\n        AsyncGeneratorMethod, AsyncMethod, GeneratorMethod, PropertyName,\n    },\n};\n\n/// Generates an expression parser for a number of expressions whose production rules are of the following pattern.\n///\n/// ```text\n/// <TargetExpression>[allowed_identifiers]\n///     => <InnerExpression>[?allowed_identifiers]\n///     => <TargetExpression>[?allowed_identifiers] <op1> <InnerExpression>[?allowed_identifiers]\n///     => <TargetExpression>[?allowed_identifiers] <op2> <InnerExpression>[?allowed_identifiers]\n///     ...\n/// ```\n///\n/// This macro has 2 mandatory identifiers:\n///  - The `$name` identifier is the name of the `TargetExpression` struct that the parser will be implemented for.\n///  - The `$lower` identifier is the name of the `InnerExpression` struct according to the pattern above.\n///\n/// A list of punctuators (operands between the `TargetExpression` and `InnerExpression`) are passed as the third parameter.\n///\n/// The fifth parameter is an `Option<InputElement>` which sets the goal symbol to set before parsing (or None to leave it as is).\nmacro_rules! expression {\n    ($name:ident, $lower:ident, [$( $op:path ),*], [$( $low_param:ident ),*], $goal:expr ) => {\n        impl<R> TokenParser<R> for $name\n        where\n            R: ReadChar\n        {\n            type Output = FormalParameterListOrExpression;\n\n            fn parse(self, cursor: &mut Cursor<R>, interner: &mut Interner)-> ParseResult<Self::Output> {\n\n                if $goal.is_some() {\n                    cursor.set_goal($goal.unwrap());\n                }\n\n                let lhs = $lower::new($( self.$low_param ),*).parse(cursor, interner)?;\n                let FormalParameterListOrExpression::Expression(mut lhs) = lhs else {\n                    return Ok(lhs);\n                };\n\n                while let Some(tok) = cursor.peek(0, interner)? {\n                    match *tok.kind() {\n                        TokenKind::Punctuator(op) if $( op == $op )||* => {\n                            cursor.advance(interner);\n                            lhs = Binary::new(\n                                op.as_binary_op().expect(\"Could not get binary operation.\"),\n                                lhs,\n                                $lower::new($( self.$low_param ),*).parse(cursor, interner)?.try_into_expression()?\n                            ).into();\n                        }\n                        _ => break\n                    }\n                }\n\n                Ok(lhs.into())\n            }\n        }\n    };\n}\n\n/// Expression parsing.\n///\n/// More information:\n///  - [MDN documentation][mdn]\n///  - [ECMAScript specification][spec]\n///\n/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators\n/// [spec]: https://tc39.es/ecma262/#prod-Expression\n#[derive(Debug, Clone, Copy)]\npub(super) struct Expression {\n    allow_in: AllowIn,\n    allow_yield: AllowYield,\n    allow_await: AllowAwait,\n}\n\nimpl Expression {\n    /// Creates a new `Expression` parser.\n    pub(super) fn new<I, Y, A>(allow_in: I, allow_yield: Y, allow_await: A) -> Self\n    where\n        I: Into<AllowIn>,\n        Y: Into<AllowYield>,\n        A: Into<AllowAwait>,\n    {\n        Self {\n            allow_in: allow_in.into(),\n            allow_yield: allow_yield.into(),\n            allow_await: allow_await.into(),\n        }\n    }\n}\n\nimpl<R> TokenParser<R> for Expression\nwhere\n    R: ReadChar,\n{\n    type Output = ast::Expression;\n\n    fn parse(self, cursor: &mut Cursor<R>, interner: &mut Interner) -> ParseResult<Self::Output> {\n        let mut lhs = AssignmentExpression::new(self.allow_in, self.allow_yield, self.allow_await)\n            .parse(cursor, interner)?;\n        while let Some(tok) = cursor.peek(0, interner)? {\n            match *tok.kind() {\n                TokenKind::Punctuator(Punctuator::Comma) => {\n                    if cursor.peek(1, interner).or_abrupt()?.kind()\n                        == &TokenKind::Punctuator(Punctuator::CloseParen)\n                    {\n                        return Ok(lhs);\n                    }\n\n                    if cursor.peek(1, interner).or_abrupt()?.kind()\n                        == &TokenKind::Punctuator(Punctuator::Spread)\n                    {\n                        return Ok(lhs);\n                    }\n\n                    cursor.advance(interner);\n\n                    lhs = Binary::new(\n                        Punctuator::Comma\n                            .as_binary_op()\n                            .expect(\"Could not get binary operation.\"),\n                        lhs,\n                        AssignmentExpression::new(\n                            self.allow_in,\n                            self.allow_yield,\n                            self.allow_await,\n                        )\n                        .parse(cursor, interner)?,\n                    )\n                    .into();\n                }\n                _ => break,\n            }\n        }\n\n        Ok(lhs)\n    }\n}\n\n/// Parses a logical expression expression.\n///\n/// More information:\n///  - [MDN documentation][mdn]\n///  - [ECMAScript specification][spec]\n///\n/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Logical_Operators\n/// [spec]: https://tc39.es/ecma262/#prod-ShortCircuitExpression\n#[derive(Debug, Clone, Copy)]\nstruct ShortCircuitExpression {\n    allow_in: AllowIn,\n    allow_yield: AllowYield,\n    allow_await: AllowAwait,\n    previous: PreviousExpr,\n}\n\n#[derive(Debug, Clone, Copy, PartialEq)]\nenum PreviousExpr {\n    None,\n    Logical,\n    Coalesce,\n}\n\nimpl ShortCircuitExpression {\n    /// Creates a new `ShortCircuitExpression` parser.\n    pub(super) fn new<I, Y, A>(allow_in: I, allow_yield: Y, allow_await: A) -> Self\n    where\n        I: Into<AllowIn>,\n        Y: Into<AllowYield>,\n        A: Into<AllowAwait>,\n    {\n        Self {\n            allow_in: allow_in.into(),\n            allow_yield: allow_yield.into(),\n            allow_await: allow_await.into(),\n            previous: PreviousExpr::None,\n        }\n    }\n\n    fn with_previous<I, Y, A>(\n        allow_in: I,\n        allow_yield: Y,\n        allow_await: A,\n        previous: PreviousExpr,\n    ) -> Self\n    where\n        I: Into<AllowIn>,\n        Y: Into<AllowYield>,\n        A: Into<AllowAwait>,\n    {\n        Self {\n            allow_in: allow_in.into(),\n            allow_yield: allow_yield.into(),\n            allow_await: allow_await.into(),\n            previous,\n        }\n    }\n}\n\nimpl<R> TokenParser<R> for ShortCircuitExpression\nwhere\n    R: ReadChar,\n{\n    type Output = FormalParameterListOrExpression;\n\n    fn parse(self, cursor: &mut Cursor<R>, interner: &mut Interner) -> ParseResult<Self::Output> {\n        let current_node =\n            BitwiseORExpression::new(self.allow_in, self.allow_yield, self.allow_await)\n                .parse(cursor, interner)?;\n        let FormalParameterListOrExpression::Expression(mut current_node) = current_node else {\n            return Ok(current_node);\n        };\n\n        let mut previous = self.previous;\n\n        while let Some(tok) = cursor.peek(0, interner)? {\n            match tok.kind() {\n                TokenKind::Punctuator(Punctuator::BoolAnd) => {\n                    if previous == PreviousExpr::Coalesce {\n                        return Err(Error::expected(\n                            [\"??\".to_owned()],\n                            tok.to_string(interner),\n                            tok.span(),\n                            \"logical expression (cannot use '??' without parentheses within '||' or '&&')\",\n                        ));\n                    }\n                    cursor.advance(interner);\n                    previous = PreviousExpr::Logical;\n                    let rhs =\n                        BitwiseORExpression::new(self.allow_in, self.allow_yield, self.allow_await)\n                            .parse(cursor, interner)?\n                            .try_into_expression()?;\n\n                    current_node =\n                        Binary::new(BinaryOp::Logical(LogicalOp::And), current_node, rhs).into();\n                }\n                TokenKind::Punctuator(Punctuator::BoolOr) => {\n                    if previous == PreviousExpr::Coalesce {\n                        return Err(Error::expected(\n                            [\"??\".to_owned()],\n                            tok.to_string(interner),\n                            tok.span(),\n                            \"logical expression (cannot use '??' without parentheses within '||' or '&&')\",\n                        ));\n                    }\n                    cursor.advance(interner);\n                    previous = PreviousExpr::Logical;\n                    let rhs = Self::with_previous(\n                        self.allow_in,\n                        self.allow_yield,\n                        self.allow_await,\n                        PreviousExpr::Logical,\n                    )\n                    .parse(cursor, interner)?\n                    .try_into_expression()?;\n                    current_node =\n                        Binary::new(BinaryOp::Logical(LogicalOp::Or), current_node, rhs).into();\n                }\n                TokenKind::Punctuator(Punctuator::Coalesce) => {\n                    if previous == PreviousExpr::Logical {\n                        return Err(Error::expected(\n                            [\"&&\".to_owned(), \"||\".to_owned()],\n                            tok.to_string(interner),\n                            tok.span(),\n                            \"cannot use '??' unparenthesized within '||' or '&&'\",\n                        ));\n                    }\n                    cursor.advance(interner);\n                    previous = PreviousExpr::Coalesce;\n                    let rhs =\n                        BitwiseORExpression::new(self.allow_in, self.allow_yield, self.allow_await)\n                            .parse(cursor, interner)?\n                            .try_into_expression()?;\n                    current_node =\n                        Binary::new(BinaryOp::Logical(LogicalOp::Coalesce), current_node, rhs)\n                            .into();\n                }\n                _ => break,\n            }\n        }\n        Ok(current_node.into())\n    }\n}\n\n/// Parses a bitwise `OR` expression.\n///\n/// More information:\n///  - [MDN documentation][mdn]\n///  - [ECMAScript specification][spec]\n///\n/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Bitwise_Operators#Bitwise_OR\n/// [spec]: https://tc39.es/ecma262/#prod-BitwiseORExpression\n#[derive(Debug, Clone, Copy)]\nstruct BitwiseORExpression {\n    allow_in: AllowIn,\n    allow_yield: AllowYield,\n    allow_await: AllowAwait,\n}\n\nimpl BitwiseORExpression {\n    /// Creates a new `BitwiseORExpression` parser.\n    pub(super) fn new<I, Y, A>(allow_in: I, allow_yield: Y, allow_await: A) -> Self\n    where\n        I: Into<AllowIn>,\n        Y: Into<AllowYield>,\n        A: Into<AllowAwait>,\n    {\n        Self {\n            allow_in: allow_in.into(),\n            allow_yield: allow_yield.into(),\n            allow_await: allow_await.into(),\n        }\n    }\n}\n\nexpression!(\n    BitwiseORExpression,\n    BitwiseXORExpression,\n    [Punctuator::Or],\n    [allow_in, allow_yield, allow_await],\n    None::<InputElement>\n);\n\n/// Parses a bitwise `XOR` expression.\n///\n/// More information:\n///  - [MDN documentation][mdn]\n///  - [ECMAScript specification][spec]\n///\n/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Bitwise_Operators#Bitwise_XOR\n/// [spec]: https://tc39.es/ecma262/#prod-BitwiseXORExpression\n#[derive(Debug, Clone, Copy)]\nstruct BitwiseXORExpression {\n    allow_in: AllowIn,\n    allow_yield: AllowYield,\n    allow_await: AllowAwait,\n}\n\nimpl BitwiseXORExpression {\n    /// Creates a new `BitwiseXORExpression` parser.\n    pub(super) fn new<I, Y, A>(allow_in: I, allow_yield: Y, allow_await: A) -> Self\n    where\n        I: Into<AllowIn>,\n        Y: Into<AllowYield>,\n        A: Into<AllowAwait>,\n    {\n        Self {\n            allow_in: allow_in.into(),\n            allow_yield: allow_yield.into(),\n            allow_await: allow_await.into(),\n        }\n    }\n}\n\nexpression!(\n    BitwiseXORExpression,\n    BitwiseANDExpression,\n    [Punctuator::Xor],\n    [allow_in, allow_yield, allow_await],\n    None::<InputElement>\n);\n\n/// Parses a bitwise `AND` expression.\n///\n/// More information:\n///  - [MDN documentation][mdn]\n///  - [ECMAScript specification][spec]\n///\n/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Bitwise_Operators#Bitwise_AND\n/// [spec]: https://tc39.es/ecma262/#prod-BitwiseANDExpression\n#[derive(Debug, Clone, Copy)]\nstruct BitwiseANDExpression {\n    allow_in: AllowIn,\n    allow_yield: AllowYield,\n    allow_await: AllowAwait,\n}\n\nimpl BitwiseANDExpression {\n    /// Creates a new `BitwiseANDExpression` parser.\n    pub(super) fn new<I, Y, A>(allow_in: I, allow_yield: Y, allow_await: A) -> Self\n    where\n        I: Into<AllowIn>,\n        Y: Into<AllowYield>,\n        A: Into<AllowAwait>,\n    {\n        Self {\n            allow_in: allow_in.into(),\n            allow_yield: allow_yield.into(),\n            allow_await: allow_await.into(),\n        }\n    }\n}\n\nexpression!(\n    BitwiseANDExpression,\n    EqualityExpression,\n    [Punctuator::And],\n    [allow_in, allow_yield, allow_await],\n    None::<InputElement>\n);\n\n/// Parses an equality expression.\n///\n/// More information:\n///  - [MDN documentation][mdn]\n///  - [ECMAScript specification][spec]\n///\n/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Comparison_Operators#Equality_operators\n/// [spec]: https://tc39.es/ecma262/#sec-equality-operators\n#[derive(Debug, Clone, Copy)]\nstruct EqualityExpression {\n    allow_in: AllowIn,\n    allow_yield: AllowYield,\n    allow_await: AllowAwait,\n}\n\nimpl EqualityExpression {\n    /// Creates a new `EqualityExpression` parser.\n    pub(super) fn new<I, Y, A>(allow_in: I, allow_yield: Y, allow_await: A) -> Self\n    where\n        I: Into<AllowIn>,\n        Y: Into<AllowYield>,\n        A: Into<AllowAwait>,\n    {\n        Self {\n            allow_in: allow_in.into(),\n            allow_yield: allow_yield.into(),\n            allow_await: allow_await.into(),\n        }\n    }\n}\n\nexpression!(\n    EqualityExpression,\n    RelationalExpression,\n    [\n        Punctuator::Eq,\n        Punctuator::NotEq,\n        Punctuator::StrictEq,\n        Punctuator::StrictNotEq\n    ],\n    [allow_in, allow_yield, allow_await],\n    None::<InputElement>\n);\n\n/// Parses a relational expression.\n///\n/// More information:\n///  - [MDN documentation][mdn]\n///  - [ECMAScript specification][spec]\n///\n/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Comparison_Operators#Relational_operators\n/// [spec]: https://tc39.es/ecma262/#sec-relational-operators\n#[derive(Debug, Clone, Copy)]\nstruct RelationalExpression {\n    allow_in: AllowIn,\n    allow_yield: AllowYield,\n    allow_await: AllowAwait,\n}\n\nimpl RelationalExpression {\n    /// Creates a new `RelationalExpression` parser.\n    pub(super) fn new<I, Y, A>(allow_in: I, allow_yield: Y, allow_await: A) -> Self\n    where\n        I: Into<AllowIn>,\n        Y: Into<AllowYield>,\n        A: Into<AllowAwait>,\n    {\n        Self {\n            allow_in: allow_in.into(),\n            allow_yield: allow_yield.into(),\n            allow_await: allow_await.into(),\n        }\n    }\n}\n\nimpl<R> TokenParser<R> for RelationalExpression\nwhere\n    R: ReadChar,\n{\n    type Output = FormalParameterListOrExpression;\n\n    fn parse(self, cursor: &mut Cursor<R>, interner: &mut Interner) -> ParseResult<Self::Output> {\n        if self.allow_in.0 {\n            let token = cursor.peek(0, interner).or_abrupt()?;\n            if let TokenKind::PrivateIdentifier(identifier) = token.kind() {\n                let identifier = *identifier;\n                let identifier_span = token.span();\n                let token = cursor.peek(1, interner).or_abrupt()?;\n                match token.kind() {\n                    TokenKind::Keyword((Keyword::In, true)) => {\n                        return Err(Error::general(\n                            \"Keyword must not contain escaped characters\",\n                            token.span().start(),\n                        ));\n                    }\n                    TokenKind::Keyword((Keyword::In, false)) => {\n                        cursor.advance(interner);\n                        cursor.advance(interner);\n\n                        let rhs = ShiftExpression::new(self.allow_yield, self.allow_await)\n                            .parse(cursor, interner)?\n                            .try_into_expression()?;\n\n                        return Ok(BinaryInPrivate::new(\n                            PrivateName::new(identifier, identifier_span),\n                            rhs,\n                        )\n                        .into());\n                    }\n                    _ => {}\n                }\n            }\n        }\n\n        let lhs =\n            ShiftExpression::new(self.allow_yield, self.allow_await).parse(cursor, interner)?;\n        let FormalParameterListOrExpression::Expression(mut lhs) = lhs else {\n            return Ok(lhs);\n        };\n\n        while let Some(tok) = cursor.peek(0, interner)? {\n            match *tok.kind() {\n                TokenKind::Punctuator(op)\n                    if op == Punctuator::LessThan\n                        || op == Punctuator::GreaterThan\n                        || op == Punctuator::LessThanOrEq\n                        || op == Punctuator::GreaterThanOrEq =>\n                {\n                    cursor.advance(interner);\n                    lhs = Binary::new(\n                        op.as_binary_op().expect(\"Could not get binary operation.\"),\n                        lhs,\n                        ShiftExpression::new(self.allow_yield, self.allow_await)\n                            .parse(cursor, interner)?\n                            .try_into_expression()?,\n                    )\n                    .into();\n                }\n                TokenKind::Keyword((Keyword::InstanceOf | Keyword::In, true)) => {\n                    return Err(Error::general(\n                        \"Keyword must not contain escaped characters\",\n                        tok.span().start(),\n                    ));\n                }\n                TokenKind::Keyword((op, false))\n                    if op == Keyword::InstanceOf\n                        || (op == Keyword::In && self.allow_in == AllowIn(true)) =>\n                {\n                    cursor.advance(interner);\n                    lhs = Binary::new(\n                        op.as_binary_op().expect(\"Could not get binary operation.\"),\n                        lhs,\n                        ShiftExpression::new(self.allow_yield, self.allow_await)\n                            .parse(cursor, interner)?\n                            .try_into_expression()?,\n                    )\n                    .into();\n                }\n                _ => break,\n            }\n        }\n\n        Ok(lhs.into())\n    }\n}\n\n/// Parses a bitwise shift expression.\n///\n/// More information:\n///  - [MDN documentation][mdn]\n///  - [ECMAScript specification][spec]\n///\n/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Bitwise_Operators#Bitwise_shift_operators\n/// [spec]: https://tc39.es/ecma262/#sec-bitwise-shift-operators\n#[derive(Debug, Clone, Copy)]\nstruct ShiftExpression {\n    allow_yield: AllowYield,\n    allow_await: AllowAwait,\n}\n\nimpl ShiftExpression {\n    /// Creates a new `ShiftExpression` parser.\n    pub(super) fn new<Y, A>(allow_yield: Y, allow_await: A) -> Self\n    where\n        Y: Into<AllowYield>,\n        A: Into<AllowAwait>,\n    {\n        Self {\n            allow_yield: allow_yield.into(),\n            allow_await: allow_await.into(),\n        }\n    }\n}\n\nexpression!(\n    ShiftExpression,\n    AdditiveExpression,\n    [\n        Punctuator::LeftSh,\n        Punctuator::RightSh,\n        Punctuator::URightSh\n    ],\n    [allow_yield, allow_await],\n    None::<InputElement>\n);\n\n/// Parses an additive expression.\n///\n/// This can be either an addition or a subtraction.\n///\n/// More information:\n///  - [MDN documentation][mdn]\n///  - [ECMAScript specification][spec]\n///\n/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Arithmetic_Operators\n/// [spec]: https://tc39.es/ecma262/#sec-additive-operators\n#[derive(Debug, Clone, Copy)]\nstruct AdditiveExpression {\n    allow_yield: AllowYield,\n    allow_await: AllowAwait,\n}\n\nimpl AdditiveExpression {\n    /// Creates a new `AdditiveExpression` parser.\n    pub(super) fn new<Y, A>(allow_yield: Y, allow_await: A) -> Self\n    where\n        Y: Into<AllowYield>,\n        A: Into<AllowAwait>,\n    {\n        Self {\n            allow_yield: allow_yield.into(),\n            allow_await: allow_await.into(),\n        }\n    }\n}\n\nexpression!(\n    AdditiveExpression,\n    MultiplicativeExpression,\n    [Punctuator::Add, Punctuator::Sub],\n    [allow_yield, allow_await],\n    None::<InputElement>\n);\n\n/// Parses a multiplicative expression.\n///\n/// This can be either a multiplication, division or a modulo (remainder) expression.\n///\n/// More information:\n///  - [MDN documentation][mdn]\n///  - [ECMAScript specification][spec]\n///\n/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Arithmetic_Operators#Division\n/// [spec]: https://tc39.es/ecma262/#sec-multiplicative-operators\n#[derive(Debug, Clone, Copy)]\nstruct MultiplicativeExpression {\n    allow_yield: AllowYield,\n    allow_await: AllowAwait,\n}\n\nimpl MultiplicativeExpression {\n    /// Creates a new `MultiplicativeExpression` parser.\n    pub(super) fn new<Y, A>(allow_yield: Y, allow_await: A) -> Self\n    where\n        Y: Into<AllowYield>,\n        A: Into<AllowAwait>,\n    {\n        Self {\n            allow_yield: allow_yield.into(),\n            allow_await: allow_await.into(),\n        }\n    }\n}\n\nexpression!(\n    MultiplicativeExpression,\n    ExponentiationExpression,\n    [Punctuator::Mul, Punctuator::Div, Punctuator::Mod],\n    [allow_yield, allow_await],\n    Some(InputElement::Div)\n);\n\n/// Returns an error if `arguments` or `eval` are used as identifier in strict mode.\nfn check_strict_arguments_or_eval(ident: Identifier, position: Position) -> ParseResult<()> {\n    match ident.sym() {\n        Sym::ARGUMENTS => Err(Error::general(\n            \"unexpected identifier `arguments` in strict mode\",\n            position,\n        )),\n        Sym::EVAL => Err(Error::general(\n            \"unexpected identifier `eval` in strict mode\",\n            position,\n        )),\n        _ => Ok(()),\n    }\n}\n"
  },
  {
    "path": "core/parser/src/parser/expression/primary/array_initializer/mod.rs",
    "content": "//! Array initializer parsing.\n//!\n//! More information:\n//!  - [MDN documentation][mdn]\n//!  - [ECMAScript specification][spec]\n//!\n//! [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array\n//! [spec]: https://tc39.es/ecma262/#sec-array-initializer\n\n#[cfg(test)]\nmod tests;\n\nuse crate::{\n    Error,\n    lexer::{InputElement, TokenKind},\n    parser::{\n        AllowAwait, AllowYield, Cursor, OrAbrupt, ParseResult, TokenParser,\n        expression::AssignmentExpression,\n    },\n    source::ReadChar,\n};\nuse boa_ast::{\n    Punctuator, Span, Spanned,\n    expression::{Spread, literal},\n};\nuse boa_interner::Interner;\n\n/// Parses an array literal.\n///\n/// More information:\n///  - [MDN documentation][mdn]\n///  - [ECMAScript specification][spec]\n///\n/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array\n/// [spec]: https://tc39.es/ecma262/#prod-ArrayLiteral\n#[derive(Debug, Clone, Copy)]\npub(super) struct ArrayLiteral {\n    allow_yield: AllowYield,\n    allow_await: AllowAwait,\n}\n\nimpl ArrayLiteral {\n    /// Creates a new `ArrayLiteral` parser.\n    pub(super) fn new<Y, A>(allow_yield: Y, allow_await: A) -> Self\n    where\n        Y: Into<AllowYield>,\n        A: Into<AllowAwait>,\n    {\n        Self {\n            allow_yield: allow_yield.into(),\n            allow_await: allow_await.into(),\n        }\n    }\n}\n\nimpl<R> TokenParser<R> for ArrayLiteral\nwhere\n    R: ReadChar,\n{\n    type Output = literal::ArrayLiteral;\n\n    fn parse(self, cursor: &mut Cursor<R>, interner: &mut Interner) -> ParseResult<Self::Output> {\n        let open_bracket_token = cursor.expect(\n            TokenKind::Punctuator(Punctuator::OpenBracket),\n            \"array parsing\",\n            interner,\n        )?;\n        cursor.set_goal(InputElement::RegExp);\n\n        let mut elements = Vec::new();\n        let mut has_trailing_comma_spread = false;\n        let mut next_comma = false;\n        let mut last_spread = false;\n\n        let end = loop {\n            let token = cursor.peek(0, interner).or_abrupt()?;\n            match token.kind() {\n                TokenKind::Punctuator(Punctuator::CloseBracket) => {\n                    let end = token.span().end();\n                    cursor.advance(interner);\n                    break end;\n                }\n                TokenKind::Punctuator(Punctuator::Comma) if next_comma => {\n                    cursor.advance(interner);\n\n                    if last_spread {\n                        let token = cursor.peek(0, interner).or_abrupt()?;\n                        if token.kind() == &TokenKind::Punctuator(Punctuator::CloseBracket) {\n                            has_trailing_comma_spread = true;\n                        }\n                    }\n\n                    next_comma = false;\n                }\n                TokenKind::Punctuator(Punctuator::Comma) => {\n                    cursor.advance(interner);\n                    elements.push(None);\n                }\n                TokenKind::Punctuator(Punctuator::Spread) if next_comma => {\n                    return Err(Error::unexpected(\n                        token.to_string(interner),\n                        token.span(),\n                        \"expected comma or end of array\",\n                    ));\n                }\n                TokenKind::Punctuator(Punctuator::Spread) => {\n                    let spread_token_span_start = token.span().start();\n\n                    cursor.advance(interner);\n                    let target =\n                        AssignmentExpression::new(true, self.allow_yield, self.allow_await)\n                            .parse(cursor, interner)?;\n                    let target_span_end = target.span().end();\n\n                    elements.push(Some(\n                        Spread::new(target, Span::new(spread_token_span_start, target_span_end))\n                            .into(),\n                    ));\n                    next_comma = true;\n                    last_spread = true;\n                }\n                _ if next_comma => {\n                    return Err(Error::unexpected(\n                        token.to_string(interner),\n                        token.span(),\n                        \"expected comma or end of array\",\n                    ));\n                }\n                _ => {\n                    let expr = AssignmentExpression::new(true, self.allow_yield, self.allow_await)\n                        .parse(cursor, interner)?;\n                    elements.push(Some(expr));\n                    next_comma = true;\n                    last_spread = false;\n                }\n            }\n        };\n\n        if last_spread && elements.last() == Some(&None) {\n            has_trailing_comma_spread = true;\n        }\n\n        let start = open_bracket_token.span().start();\n        Ok(literal::ArrayLiteral::new(\n            elements,\n            has_trailing_comma_spread,\n            Span::new(start, end),\n        ))\n    }\n}\n"
  },
  {
    "path": "core/parser/src/parser/expression/primary/array_initializer/tests.rs",
    "content": "// ! Tests for array initializer parsing.\n\nuse crate::parser::tests::check_script_parser;\nuse boa_ast::{\n    Span, Statement,\n    expression::literal::{ArrayLiteral, Literal},\n};\nuse boa_interner::{Interner, Sym};\nuse boa_macros::utf16;\n\n/// Checks an empty array.\n#[test]\nfn check_empty() {\n    check_script_parser(\n        \"[]\",\n        vec![\n            Statement::Expression(\n                ArrayLiteral::new(vec![], false, Span::new((1, 1), (1, 3))).into(),\n            )\n            .into(),\n        ],\n        &mut Interner::default(),\n    );\n}\n\n/// Checks an array with empty slot.\n#[test]\nfn check_empty_slot() {\n    check_script_parser(\n        \"[,]\",\n        vec![\n            Statement::Expression(\n                ArrayLiteral::new(vec![None], false, Span::new((1, 1), (1, 4))).into(),\n            )\n            .into(),\n        ],\n        &mut Interner::default(),\n    );\n}\n\n/// Checks a numeric array.\n#[test]\nfn check_numeric_array() {\n    check_script_parser(\n        \"[1, 2, 3]\",\n        vec![\n            Statement::Expression(\n                ArrayLiteral::new(\n                    vec![\n                        Some(Literal::new(1, Span::new((1, 2), (1, 3))).into()),\n                        Some(Literal::new(2, Span::new((1, 5), (1, 6))).into()),\n                        Some(Literal::new(3, Span::new((1, 8), (1, 9))).into()),\n                    ],\n                    false,\n                    Span::new((1, 1), (1, 10)),\n                )\n                .into(),\n            )\n            .into(),\n        ],\n        &mut Interner::default(),\n    );\n}\n\n// Checks a numeric array with trailing comma\n#[test]\nfn check_numeric_array_trailing() {\n    check_script_parser(\n        \"[1, 2, 3,]\",\n        vec![\n            Statement::Expression(\n                ArrayLiteral::new(\n                    vec![\n                        Some(Literal::new(1, Span::new((1, 2), (1, 3))).into()),\n                        Some(Literal::new(2, Span::new((1, 5), (1, 6))).into()),\n                        Some(Literal::new(3, Span::new((1, 8), (1, 9))).into()),\n                    ],\n                    false,\n                    Span::new((1, 1), (1, 11)),\n                )\n                .into(),\n            )\n            .into(),\n        ],\n        &mut Interner::default(),\n    );\n}\n\n/// Checks a numeric array with an elision.\n#[test]\nfn check_numeric_array_elision() {\n    check_script_parser(\n        \"[1, 2, , 3]\",\n        vec![\n            Statement::Expression(\n                ArrayLiteral::new(\n                    vec![\n                        Some(Literal::new(1, Span::new((1, 2), (1, 3))).into()),\n                        Some(Literal::new(2, Span::new((1, 5), (1, 6))).into()),\n                        None,\n                        Some(Literal::new(3, Span::new((1, 10), (1, 11))).into()),\n                    ],\n                    false,\n                    Span::new((1, 1), (1, 12)),\n                )\n                .into(),\n            )\n            .into(),\n        ],\n        &mut Interner::default(),\n    );\n}\n\n/// Checks a numeric array with repeated elisions.\n#[test]\nfn check_numeric_array_repeated_elision() {\n    check_script_parser(\n        \"[1, 2, ,, 3]\",\n        vec![\n            Statement::Expression(\n                ArrayLiteral::new(\n                    vec![\n                        Some(Literal::new(1, Span::new((1, 2), (1, 3))).into()),\n                        Some(Literal::new(2, Span::new((1, 5), (1, 6))).into()),\n                        None,\n                        None,\n                        Some(Literal::new(3, Span::new((1, 11), (1, 12))).into()),\n                    ],\n                    false,\n                    Span::new((1, 1), (1, 13)),\n                )\n                .into(),\n            )\n            .into(),\n        ],\n        &mut Interner::default(),\n    );\n}\n\n/// Checks a combined array.\n#[test]\nfn check_combined() {\n    let interner = &mut Interner::default();\n    check_script_parser(\n        r#\"[1, \"a\", 2]\"#,\n        vec![\n            Statement::Expression(\n                ArrayLiteral::new(\n                    vec![\n                        Some(Literal::new(1, Span::new((1, 2), (1, 3))).into()),\n                        Some(\n                            Literal::new(\n                                interner.get_or_intern_static(\"a\", utf16!(\"a\")),\n                                Span::new((1, 5), (1, 8)),\n                            )\n                            .into(),\n                        ),\n                        Some(Literal::new(2, Span::new((1, 10), (1, 11))).into()),\n                    ],\n                    false,\n                    Span::new((1, 1), (1, 12)),\n                )\n                .into(),\n            )\n            .into(),\n        ],\n        interner,\n    );\n}\n\n/// Checks a combined array with an empty string\n#[test]\nfn check_combined_empty_str() {\n    check_script_parser(\n        r#\"[1, \"\", 2]\"#,\n        vec![\n            Statement::Expression(\n                ArrayLiteral::new(\n                    vec![\n                        Some(Literal::new(1, Span::new((1, 2), (1, 3))).into()),\n                        Some(Literal::new(Sym::EMPTY_STRING, Span::new((1, 5), (1, 7))).into()),\n                        Some(Literal::new(2, Span::new((1, 9), (1, 10))).into()),\n                    ],\n                    false,\n                    Span::new((1, 1), (1, 11)),\n                )\n                .into(),\n            )\n            .into(),\n        ],\n        &mut Interner::default(),\n    );\n}\n\n#[test]\nfn check_elision_start_end() {\n    check_script_parser(\n        \"[, 1 , , ]\",\n        vec![\n            Statement::Expression(\n                ArrayLiteral::new(\n                    vec![\n                        None,\n                        Some(Literal::new(1, Span::new((1, 4), (1, 5))).into()),\n                        None,\n                    ],\n                    false,\n                    Span::new((1, 1), (1, 11)),\n                )\n                .into(),\n            )\n            .into(),\n        ],\n        &mut Interner::default(),\n    );\n}\n"
  },
  {
    "path": "core/parser/src/parser/expression/primary/async_function_expression/mod.rs",
    "content": "#[cfg(test)]\nmod tests;\n\nuse crate::{\n    Error,\n    lexer::{Error as LexError, TokenKind},\n    parser::{\n        Cursor, OrAbrupt, ParseResult, TokenParser,\n        expression::BindingIdentifier,\n        function::{FormalParameters, FunctionBody},\n        name_in_lexically_declared_names,\n    },\n    source::ReadChar,\n};\nuse boa_ast::{\n    Keyword, Punctuator, Span, Spanned,\n    function::AsyncFunctionExpression as AsyncFunctionExpressionNode,\n    operations::{ContainsSymbol, bound_names, contains, lexically_declared_names},\n};\nuse boa_interner::{Interner, Sym};\n\n/// Async Function expression parsing.\n///\n/// More information:\n///  - [MDN documentation][mdn]\n///  - [ECMAScript specification][spec]\n///\n/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/async_function\n/// [spec]: https://tc39.es/ecma262/#prod-AsyncFunctionExpression\n#[derive(Debug, Clone, Copy)]\npub(super) struct AsyncFunctionExpression {}\n\nimpl AsyncFunctionExpression {\n    /// Creates a new `AsyncFunctionExpression` parser.\n    pub(super) fn new() -> Self {\n        Self {}\n    }\n}\n\nimpl<R> TokenParser<R> for AsyncFunctionExpression\nwhere\n    R: ReadChar,\n{\n    type Output = AsyncFunctionExpressionNode;\n\n    fn parse(self, cursor: &mut Cursor<R>, interner: &mut Interner) -> ParseResult<Self::Output> {\n        let token = cursor.expect(\n            (Keyword::Async, false),\n            \"async function expression\",\n            interner,\n        )?;\n        let start_linear_span = token.linear_span();\n        let function_span_start = token.span().start();\n\n        cursor.peek_expect_no_lineterminator(0, \"async function expression\", interner)?;\n        cursor.expect(\n            (Keyword::Function, false),\n            \"async function expression\",\n            interner,\n        )?;\n\n        let token = cursor.peek(0, interner).or_abrupt()?;\n        let (name, name_span) = match token.kind() {\n            TokenKind::IdentifierName(_)\n            | TokenKind::Keyword((\n                Keyword::Yield | Keyword::Await | Keyword::Async | Keyword::Of,\n                _,\n            )) => {\n                let span = token.span();\n                let name = BindingIdentifier::new(false, true).parse(cursor, interner)?;\n\n                (Some(name), span)\n            }\n            _ => (None, token.span()),\n        };\n\n        let params_start_position = cursor\n            .expect(Punctuator::OpenParen, \"async function expression\", interner)?\n            .span()\n            .end();\n\n        let params = FormalParameters::new(false, true).parse(cursor, interner)?;\n\n        cursor.expect(\n            Punctuator::CloseParen,\n            \"async function expression\",\n            interner,\n        )?;\n\n        let body =\n            FunctionBody::new(false, true, \"async function expression\").parse(cursor, interner)?;\n\n        // Early Error: If the source code matching FormalParameters is strict mode code,\n        // the Early Error rules for UniqueFormalParameters : FormalParameters are applied.\n        if (cursor.strict() || body.strict()) && params.has_duplicates() {\n            return Err(Error::lex(LexError::Syntax(\n                \"Duplicate parameter name not allowed in this context\".into(),\n                params_start_position,\n            )));\n        }\n\n        // Early Error: It is a Syntax Error if FunctionBodyContainsUseStrict of AsyncFunctionBody is true\n        // and IsSimpleParameterList of FormalParameters is false.\n        if body.strict() && !params.is_simple() {\n            return Err(Error::lex(LexError::Syntax(\n                \"Illegal 'use strict' directive in function with non-simple parameter list\".into(),\n                params_start_position,\n            )));\n        }\n\n        // Early Error: If BindingIdentifier is present and the source code matching BindingIdentifier is strict mode code,\n        // it is a Syntax Error if the StringValue of BindingIdentifier is \"eval\" or \"arguments\".\n        if let Some(name) = name\n            && (cursor.strict() || body.strict())\n            && [Sym::EVAL, Sym::ARGUMENTS].contains(&name.sym())\n        {\n            return Err(Error::lex(LexError::Syntax(\n                \"unexpected identifier 'eval' or 'arguments' in strict mode\".into(),\n                name_span.start(),\n            )));\n        }\n\n        // Catch early error for BindingIdentifier, because strictness of the functions body is also\n        // relevant for the function parameters.\n        if body.strict() && contains(&params, ContainsSymbol::EvalOrArguments) {\n            return Err(Error::lex(LexError::Syntax(\n                \"unexpected identifier 'eval' or 'arguments' in strict mode\".into(),\n                params_start_position,\n            )));\n        }\n\n        // It is a Syntax Error if any element of the BoundNames of FormalParameters\n        // also occurs in the LexicallyDeclaredNames of FunctionBody.\n        // https://tc39.es/ecma262/#sec-function-definitions-static-semantics-early-errors\n        name_in_lexically_declared_names(\n            &bound_names(&params),\n            &lexically_declared_names(&body),\n            params_start_position,\n            interner,\n        )?;\n\n        let span = start_linear_span.union(body.linear_pos_end());\n\n        let function_span_end = body.span().end();\n        let function = AsyncFunctionExpressionNode::new(\n            name,\n            params,\n            body,\n            span,\n            name.is_some(),\n            Span::new(function_span_start, function_span_end),\n        );\n\n        if contains(&function, ContainsSymbol::Super) {\n            return Err(Error::lex(LexError::Syntax(\n                \"invalid super usage\".into(),\n                params_start_position,\n            )));\n        }\n\n        Ok(function)\n    }\n}\n"
  },
  {
    "path": "core/parser/src/parser/expression/primary/async_function_expression/tests.rs",
    "content": "use crate::parser::tests::check_script_parser;\nuse boa_ast::{\n    Span, Statement, StatementList, StatementListItem,\n    declaration::{Declaration, LexicalDeclaration, Variable},\n    expression::{Identifier, literal::Literal},\n    function::{AsyncFunctionExpression, FormalParameterList, FunctionBody},\n    statement::Return,\n};\nuse boa_interner::Interner;\nuse boa_macros::utf16;\nuse indoc::indoc;\n\nconst PSEUDO_LINEAR_POS: boa_ast::LinearPosition = boa_ast::LinearPosition::new(0);\nconst EMPTY_LINEAR_SPAN: boa_ast::LinearSpan =\n    boa_ast::LinearSpan::new(PSEUDO_LINEAR_POS, PSEUDO_LINEAR_POS);\n\n/// Checks async expression parsing.\n#[test]\nfn check_async_expression() {\n    let interner = &mut Interner::default();\n    let add = interner.get_or_intern_static(\"add\", utf16!(\"add\"));\n    check_script_parser(\n        indoc! {\"\n            const add = async function() {\n                return 1;\n            };\n        \"},\n        vec![\n            Declaration::Lexical(LexicalDeclaration::Const(\n                vec![Variable::from_identifier(\n                    Identifier::new(add, Span::new((1, 7), (1, 10))),\n                    Some(\n                        AsyncFunctionExpression::new(\n                            Some(Identifier::new(add, Span::new((1, 7), (1, 10)))),\n                            FormalParameterList::default(),\n                            FunctionBody::new(\n                                StatementList::new(\n                                    [StatementListItem::Statement(\n                                        Statement::Return(Return::new(Some(\n                                            Literal::new(1, Span::new((2, 12), (2, 13))).into(),\n                                        )))\n                                        .into(),\n                                    )],\n                                    PSEUDO_LINEAR_POS,\n                                    false,\n                                ),\n                                Span::new((1, 30), (3, 2)),\n                            ),\n                            EMPTY_LINEAR_SPAN,\n                            false,\n                            Span::new((1, 13), (3, 2)),\n                        )\n                        .into(),\n                    ),\n                )]\n                .try_into()\n                .unwrap(),\n            ))\n            .into(),\n        ],\n        interner,\n    );\n}\n\n#[test]\nfn check_nested_async_expression() {\n    let interner = &mut Interner::default();\n    let a = interner.get_or_intern_static(\"a\", utf16!(\"a\"));\n    let b = interner.get_or_intern_static(\"b\", utf16!(\"b\"));\n    check_script_parser(\n        indoc! {\"\n            const a = async function() {\n                const b = async function() {\n                    return 1;\n                };\n            };\n        \"},\n        vec![\n            Declaration::Lexical(LexicalDeclaration::Const(\n                vec![Variable::from_identifier(\n                    Identifier::new(a, Span::new((1, 7), (1, 8))),\n                    Some(\n                        AsyncFunctionExpression::new(\n                            Some(Identifier::new(a, Span::new((1, 7), (1, 8)))),\n                            FormalParameterList::default(),\n                            FunctionBody::new(\n                                StatementList::new(\n                                    [Declaration::Lexical(LexicalDeclaration::Const(\n                                        vec![Variable::from_identifier(\n                                            Identifier::new(b, Span::new((2, 11), (2, 12))),\n                                            Some(\n                                                AsyncFunctionExpression::new(\n                                                    Some(Identifier::new(\n                                                        b,\n                                                        Span::new((2, 11), (2, 12)),\n                                                    )),\n                                                    FormalParameterList::default(),\n                                                    FunctionBody::new(\n                                                        StatementList::new(\n                                                            [Statement::Return(Return::new(Some(\n                                                                Literal::new(\n                                                                    1,\n                                                                    Span::new((3, 16), (3, 17)),\n                                                                )\n                                                                .into(),\n                                                            )))\n                                                            .into()],\n                                                            PSEUDO_LINEAR_POS,\n                                                            false,\n                                                        ),\n                                                        Span::new((2, 32), (4, 6)),\n                                                    ),\n                                                    EMPTY_LINEAR_SPAN,\n                                                    false,\n                                                    Span::new((2, 15), (4, 6)),\n                                                )\n                                                .into(),\n                                            ),\n                                        )]\n                                        .try_into()\n                                        .unwrap(),\n                                    ))\n                                    .into()],\n                                    PSEUDO_LINEAR_POS,\n                                    false,\n                                ),\n                                Span::new((1, 28), (5, 2)),\n                            ),\n                            EMPTY_LINEAR_SPAN,\n                            false,\n                            Span::new((1, 11), (5, 2)),\n                        )\n                        .into(),\n                    ),\n                )]\n                .try_into()\n                .unwrap(),\n            ))\n            .into(),\n        ],\n        interner,\n    );\n}\n"
  },
  {
    "path": "core/parser/src/parser/expression/primary/async_generator_expression/mod.rs",
    "content": "//! Async Generator Expression Parser\n//!\n//! Implements `TokenParser` for `AsyncGeneratorExpression` and outputs\n//! an `AsyncGeneratorExpr` ast node\n//!\n//!  More information:\n//!  - [ECMAScript specification][spec]\n//!\n//! [spec]: https://tc39.es/ecma262/#prod-AsyncGeneratorExpression\n#[cfg(test)]\nmod tests;\n\nuse crate::{\n    Error,\n    lexer::{Error as LexError, TokenKind},\n    parser::{\n        Cursor, OrAbrupt, ParseResult, TokenParser,\n        expression::BindingIdentifier,\n        function::{FormalParameters, FunctionBody},\n        name_in_lexically_declared_names,\n    },\n    source::ReadChar,\n};\nuse boa_ast::{\n    Keyword, Punctuator, Span, Spanned,\n    function::AsyncGeneratorExpression as AsyncGeneratorExpressionNode,\n    operations::{ContainsSymbol, bound_names, contains, lexically_declared_names},\n};\nuse boa_interner::{Interner, Sym};\n\n/// Async Generator Expression Parsing\n///\n/// More information:\n///  - [ECMAScript specification][spec]\n///\n/// [spec]: https://tc39.es/ecma262/#prod-AsyncGeneratorExpression\n#[derive(Debug, Clone, Copy)]\npub(super) struct AsyncGeneratorExpression {}\n\nimpl AsyncGeneratorExpression {\n    /// Creates a new `AsyncGeneratorExpression` parser.\n    pub(in crate::parser) fn new() -> Self {\n        Self {}\n    }\n}\n\nimpl<R> TokenParser<R> for AsyncGeneratorExpression\nwhere\n    R: ReadChar,\n{\n    //The below needs to be implemented in ast::node\n    type Output = AsyncGeneratorExpressionNode;\n\n    fn parse(self, cursor: &mut Cursor<R>, interner: &mut Interner) -> ParseResult<Self::Output> {\n        let token = cursor.expect(\n            (Keyword::Async, false),\n            \"async function expression\",\n            interner,\n        )?;\n        let start_linear_span = token.linear_span();\n        let function_span_start = token.span().start();\n\n        cursor.peek_expect_no_lineterminator(0, \"async generator expression\", interner)?;\n        cursor.expect(\n            (Keyword::Function, false),\n            \"async generator expression\",\n            interner,\n        )?;\n        cursor.expect(\n            TokenKind::Punctuator(Punctuator::Mul),\n            \"async generator expression\",\n            interner,\n        )?;\n\n        let token = cursor.peek(0, interner).or_abrupt()?;\n        let (name, name_span) = match token.kind() {\n            TokenKind::IdentifierName(_)\n            | TokenKind::Keyword((\n                Keyword::Yield | Keyword::Await | Keyword::Async | Keyword::Of,\n                _,\n            )) => {\n                let span = token.span();\n                let name = BindingIdentifier::new(true, true).parse(cursor, interner)?;\n\n                (Some(name), span)\n            }\n            _ => (None, token.span()),\n        };\n\n        let params_start_position = cursor\n            .expect(\n                Punctuator::OpenParen,\n                \"async generator expression\",\n                interner,\n            )?\n            .span()\n            .end();\n\n        let params = FormalParameters::new(true, true).parse(cursor, interner)?;\n\n        // It is a Syntax Error if FormalParameters Contains YieldExpression is true.\n        if contains(&params, ContainsSymbol::YieldExpression) {\n            return Err(Error::lex(LexError::Syntax(\n                \"yield expression not allowed in async generator expression parameters\".into(),\n                params_start_position,\n            )));\n        }\n\n        // It is a Syntax Error if FormalParameters Contains AwaitExpression is true.\n        if contains(&params, ContainsSymbol::AwaitExpression) {\n            return Err(Error::lex(LexError::Syntax(\n                \"await expression not allowed in async generator expression parameters\".into(),\n                params_start_position,\n            )));\n        }\n\n        cursor.expect(\n            Punctuator::CloseParen,\n            \"async generator expression\",\n            interner,\n        )?;\n\n        let body =\n            FunctionBody::new(true, true, \"async generator expression\").parse(cursor, interner)?;\n\n        // Early Error: If the source code matching FormalParameters is strict mode code,\n        // the Early Error rules for UniqueFormalParameters : FormalParameters are applied.\n        if (cursor.strict() || body.strict()) && params.has_duplicates() {\n            return Err(Error::lex(LexError::Syntax(\n                \"Duplicate parameter name not allowed in this context\".into(),\n                params_start_position,\n            )));\n        }\n\n        // Early Error: It is a Syntax Error if FunctionBodyContainsUseStrict of GeneratorBody is true\n        // and IsSimpleParameterList of FormalParameters is false.\n        if body.strict() && !params.is_simple() {\n            return Err(Error::lex(LexError::Syntax(\n                \"Illegal 'use strict' directive in function with non-simple parameter list\".into(),\n                params_start_position,\n            )));\n        }\n\n        // Early Error: If BindingIdentifier is present and the source code matching BindingIdentifier is strict mode code,\n        // it is a Syntax Error if the StringValue of BindingIdentifier is \"eval\" or \"arguments\".\n        if let Some(name) = name\n            && (cursor.strict() || body.strict())\n            && [Sym::EVAL, Sym::ARGUMENTS].contains(&name.sym())\n        {\n            return Err(Error::lex(LexError::Syntax(\n                \"unexpected identifier 'eval' or 'arguments' in strict mode\".into(),\n                name_span.start(),\n            )));\n        }\n\n        // Catch early error for BindingIdentifier, because strictness of the functions body is also\n        // relevant for the function parameters.\n        if body.strict() && contains(&params, ContainsSymbol::EvalOrArguments) {\n            return Err(Error::lex(LexError::Syntax(\n                \"unexpected identifier 'eval' or 'arguments' in strict mode\".into(),\n                params_start_position,\n            )));\n        }\n\n        // It is a Syntax Error if any element of the BoundNames of FormalParameters\n        // also occurs in the LexicallyDeclaredNames of FunctionBody.\n        name_in_lexically_declared_names(\n            &bound_names(&params),\n            &lexically_declared_names(&body),\n            params_start_position,\n            interner,\n        )?;\n\n        let span = start_linear_span.union(body.linear_pos_end());\n\n        let function_span_end = body.span().end();\n        let function = AsyncGeneratorExpressionNode::new(\n            name,\n            params,\n            body,\n            span,\n            name.is_some(),\n            Span::new(function_span_start, function_span_end),\n        );\n\n        if contains(&function, ContainsSymbol::Super) {\n            return Err(Error::lex(LexError::Syntax(\n                \"invalid super usage\".into(),\n                params_start_position,\n            )));\n        }\n\n        Ok(function)\n    }\n}\n"
  },
  {
    "path": "core/parser/src/parser/expression/primary/async_generator_expression/tests.rs",
    "content": "use crate::parser::tests::check_script_parser;\nuse boa_ast::{\n    Declaration, Span, Statement, StatementList, StatementListItem,\n    declaration::{LexicalDeclaration, Variable},\n    expression::{Identifier, literal::Literal},\n    function::{AsyncGeneratorExpression, FormalParameterList, FunctionBody},\n    statement::Return,\n};\nuse boa_interner::Interner;\nuse boa_macros::utf16;\nuse indoc::indoc;\n\nconst PSEUDO_LINEAR_POS: boa_ast::LinearPosition = boa_ast::LinearPosition::new(0);\nconst EMPTY_LINEAR_SPAN: boa_ast::LinearSpan =\n    boa_ast::LinearSpan::new(PSEUDO_LINEAR_POS, PSEUDO_LINEAR_POS);\n\n///checks async generator expression parsing\n\n#[test]\nfn check_async_generator_expr() {\n    let interner = &mut Interner::default();\n    let add = interner.get_or_intern_static(\"add\", utf16!(\"add\"));\n    check_script_parser(\n        indoc! {\"\n            const add = async function*(){\n                return 1;\n            };\n        \"},\n        vec![\n            Declaration::Lexical(LexicalDeclaration::Const(\n                vec![Variable::from_identifier(\n                    Identifier::new(add, Span::new((1, 7), (1, 10))),\n                    Some(\n                        AsyncGeneratorExpression::new(\n                            Some(Identifier::new(add, Span::new((1, 7), (1, 10)))),\n                            FormalParameterList::default(),\n                            FunctionBody::new(\n                                StatementList::new(\n                                    [StatementListItem::Statement(\n                                        Statement::Return(Return::new(Some(\n                                            Literal::new(1, Span::new((2, 12), (2, 13))).into(),\n                                        )))\n                                        .into(),\n                                    )],\n                                    PSEUDO_LINEAR_POS,\n                                    false,\n                                ),\n                                Span::new((1, 30), (3, 2)),\n                            ),\n                            EMPTY_LINEAR_SPAN,\n                            false,\n                            Span::new((1, 13), (3, 2)),\n                        )\n                        .into(),\n                    ),\n                )]\n                .try_into()\n                .unwrap(),\n            ))\n            .into(),\n        ],\n        interner,\n    );\n}\n\n#[test]\nfn check_nested_async_generator_expr() {\n    let interner = &mut Interner::default();\n    let a = interner.get_or_intern_static(\"a\", utf16!(\"a\"));\n    let b = interner.get_or_intern_static(\"b\", utf16!(\"b\"));\n    check_script_parser(\n        indoc! {\"\n            const a = async function*() {\n                const b = async function*() {\n                    return 1;\n                };\n            };\n        \"},\n        vec![\n            Declaration::Lexical(LexicalDeclaration::Const(\n                vec![Variable::from_identifier(\n                    Identifier::new(a, Span::new((1, 7), (1, 8))),\n                    Some(\n                        AsyncGeneratorExpression::new(\n                            Some(Identifier::new(a, Span::new((1, 7), (1, 8)))),\n                            FormalParameterList::default(),\n                            FunctionBody::new(\n                                StatementList::new(\n                                    [Declaration::Lexical(LexicalDeclaration::Const(\n                                        vec![Variable::from_identifier(\n                                            Identifier::new(b, Span::new((2, 11), (2, 12))),\n                                            Some(\n                                                AsyncGeneratorExpression::new(\n                                                    Some(Identifier::new(\n                                                        b,\n                                                        Span::new((2, 11), (2, 12)),\n                                                    )),\n                                                    FormalParameterList::default(),\n                                                    FunctionBody::new(\n                                                        StatementList::new(\n                                                            [StatementListItem::Statement(\n                                                                Statement::Return(Return::new(\n                                                                    Some(\n                                                                        Literal::new(\n                                                                            1,\n                                                                            Span::new(\n                                                                                (3, 16),\n                                                                                (3, 17),\n                                                                            ),\n                                                                        )\n                                                                        .into(),\n                                                                    ),\n                                                                ))\n                                                                .into(),\n                                                            )],\n                                                            PSEUDO_LINEAR_POS,\n                                                            false,\n                                                        ),\n                                                        Span::new((2, 33), (4, 6)),\n                                                    ),\n                                                    EMPTY_LINEAR_SPAN,\n                                                    false,\n                                                    Span::new((2, 15), (4, 6)),\n                                                )\n                                                .into(),\n                                            ),\n                                        )]\n                                        .try_into()\n                                        .unwrap(),\n                                    ))\n                                    .into()],\n                                    PSEUDO_LINEAR_POS,\n                                    false,\n                                ),\n                                Span::new((1, 29), (5, 2)),\n                            ),\n                            EMPTY_LINEAR_SPAN,\n                            false,\n                            Span::new((1, 11), (5, 2)),\n                        )\n                        .into(),\n                    ),\n                )]\n                .try_into()\n                .unwrap(),\n            ))\n            .into(),\n        ],\n        interner,\n    );\n}\n"
  },
  {
    "path": "core/parser/src/parser/expression/primary/class_expression/mod.rs",
    "content": "use crate::{\n    lexer::TokenKind,\n    parser::{\n        AllowAwait, AllowYield, Cursor, OrAbrupt, ParseResult, TokenParser,\n        expression::BindingIdentifier, statement::ClassTail,\n    },\n    source::ReadChar,\n};\nuse boa_ast::{Keyword, Span, Spanned, function::ClassExpression as ClassExpressionNode};\nuse boa_interner::Interner;\n\n/// Class expression parsing.\n///\n/// More information:\n///  - [ECMAScript specification][spec]\n///\n/// [spec]: https://tc39.es/ecma262/#prod-ClassExpression\n#[derive(Debug, Clone, Copy)]\npub(super) struct ClassExpression {\n    allow_yield: AllowYield,\n    allow_await: AllowAwait,\n}\n\nimpl ClassExpression {\n    /// Creates a new `ClassExpression` parser.\n    pub(in crate::parser) fn new<Y, A>(allow_yield: Y, allow_await: A) -> Self\n    where\n        Y: Into<AllowYield>,\n        A: Into<AllowAwait>,\n    {\n        Self {\n            allow_yield: allow_yield.into(),\n            allow_await: allow_await.into(),\n        }\n    }\n}\n\nimpl<R> TokenParser<R> for ClassExpression\nwhere\n    R: ReadChar,\n{\n    type Output = ClassExpressionNode;\n\n    fn parse(self, cursor: &mut Cursor<R>, interner: &mut Interner) -> ParseResult<Self::Output> {\n        let class_span_start = cursor\n            .expect(\n                TokenKind::Keyword((Keyword::Class, false)),\n                \"class expression\",\n                interner,\n            )?\n            .span()\n            .start();\n\n        let strict = cursor.strict();\n        cursor.set_strict(true);\n\n        let token = cursor.peek(0, interner).or_abrupt()?;\n        let name = match token.kind() {\n            TokenKind::IdentifierName(_)\n            | TokenKind::Keyword((Keyword::Yield | Keyword::Await, _)) => {\n                BindingIdentifier::new(self.allow_yield, self.allow_await)\n                    .parse(cursor, interner)?\n                    .into()\n            }\n            _ => None,\n        };\n        cursor.set_strict(strict);\n\n        let (super_ref, constructor, elements, end) =\n            ClassTail::new(name, self.allow_yield, self.allow_await).parse(cursor, interner)?;\n\n        Ok(ClassExpressionNode::new(\n            name,\n            super_ref,\n            constructor,\n            elements.into_boxed_slice(),\n            name.is_some(),\n            Span::new(class_span_start, end),\n        ))\n    }\n}\n"
  },
  {
    "path": "core/parser/src/parser/expression/primary/function_expression/mod.rs",
    "content": "//! Function expression parsing.\n//!\n//! More information:\n//!  - [MDN documentation][mdn]\n//!  - [ECMAScript specification][spec]\n//!\n//! [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/function\n//! [spec]: https://tc39.es/ecma262/#prod-FunctionExpression\n\n#[cfg(test)]\nmod tests;\n\nuse crate::{\n    Error,\n    lexer::{Error as LexError, TokenKind},\n    parser::{\n        Cursor, OrAbrupt, ParseResult, TokenParser,\n        expression::BindingIdentifier,\n        function::{FormalParameters, FunctionBody},\n        name_in_lexically_declared_names,\n    },\n    source::ReadChar,\n};\nuse boa_ast::{\n    Keyword, Punctuator, Span, Spanned,\n    function::FunctionExpression as FunctionExpressionNode,\n    operations::{ContainsSymbol, bound_names, contains, lexically_declared_names},\n};\nuse boa_interner::{Interner, Sym};\n\n/// Function expression parsing.\n///\n/// More information:\n///  - [MDN documentation][mdn]\n///  - [ECMAScript specification][spec]\n///\n/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/function\n/// [spec]: https://tc39.es/ecma262/#prod-FunctionExpression\n#[derive(Debug, Clone, Copy)]\npub(super) struct FunctionExpression {}\n\nimpl FunctionExpression {\n    /// Creates a new `FunctionExpression` parser.\n    pub(in crate::parser) fn new() -> Self {\n        Self {}\n    }\n}\n\nimpl<R> TokenParser<R> for FunctionExpression\nwhere\n    R: ReadChar,\n{\n    type Output = FunctionExpressionNode;\n\n    fn parse(self, cursor: &mut Cursor<R>, interner: &mut Interner) -> ParseResult<Self::Output> {\n        let token = cursor.expect((Keyword::Function, false), \"generator expression\", interner)?;\n        let start_linear_span = token.linear_span();\n        let function_token_span_start = token.span().start();\n\n        let token = cursor.peek(0, interner).or_abrupt()?;\n\n        let (name, name_span) = match token.kind() {\n            TokenKind::IdentifierName(_)\n            | TokenKind::Keyword((\n                Keyword::Yield | Keyword::Await | Keyword::Async | Keyword::Of,\n                _,\n            )) => {\n                let span = token.span();\n                let name = BindingIdentifier::new(false, false).parse(cursor, interner)?;\n\n                (Some(name), span)\n            }\n            _ => (None, token.span()),\n        };\n\n        let params_start_position = cursor\n            .expect(Punctuator::OpenParen, \"function expression\", interner)?\n            .span()\n            .end();\n\n        let params = FormalParameters::new(false, false).parse(cursor, interner)?;\n\n        cursor.expect(Punctuator::CloseParen, \"function expression\", interner)?;\n\n        let body =\n            FunctionBody::new(false, false, \"function expression\").parse(cursor, interner)?;\n\n        // Early Error: If the source code matching FormalParameters is strict mode code,\n        // the Early Error rules for UniqueFormalParameters : FormalParameters are applied.\n        if (cursor.strict() || body.strict()) && params.has_duplicates() {\n            return Err(Error::lex(LexError::Syntax(\n                \"Duplicate parameter name not allowed in this context\".into(),\n                params_start_position,\n            )));\n        }\n\n        // Early Error: It is a Syntax Error if FunctionBodyContainsUseStrict of GeneratorBody is true\n        // and IsSimpleParameterList of FormalParameters is false.\n        if body.strict() && !params.is_simple() {\n            return Err(Error::lex(LexError::Syntax(\n                \"Illegal 'use strict' directive in function with non-simple parameter list\".into(),\n                params_start_position,\n            )));\n        }\n\n        // Early Error: If BindingIdentifier is present and the source code matching BindingIdentifier is strict mode code,\n        // it is a Syntax Error if the StringValue of BindingIdentifier is \"eval\" or \"arguments\".\n        if let Some(name) = name\n            && (cursor.strict() || body.strict())\n            && [Sym::EVAL, Sym::ARGUMENTS].contains(&name.sym())\n        {\n            return Err(Error::lex(LexError::Syntax(\n                \"unexpected identifier 'eval' or 'arguments' in strict mode\".into(),\n                name_span.start(),\n            )));\n        }\n\n        // Catch early error for BindingIdentifier, because strictness of the functions body is also\n        // relevant for the function parameters.\n        if body.strict() && contains(&params, ContainsSymbol::EvalOrArguments) {\n            return Err(Error::lex(LexError::Syntax(\n                \"unexpected identifier 'eval' or 'arguments' in strict mode\".into(),\n                params_start_position,\n            )));\n        }\n\n        // It is a Syntax Error if any element of the BoundNames of FormalParameters\n        // also occurs in the LexicallyDeclaredNames of FunctionBody.\n        // https://tc39.es/ecma262/#sec-function-definitions-static-semantics-early-errors\n        name_in_lexically_declared_names(\n            &bound_names(&params),\n            &lexically_declared_names(&body),\n            params_start_position,\n            interner,\n        )?;\n\n        let span = Some(start_linear_span.union(body.linear_pos_end()));\n\n        let body_span_end = body.span().end();\n        let function = FunctionExpressionNode::new(\n            name,\n            params,\n            body,\n            span,\n            name.is_some(),\n            Span::new(function_token_span_start, body_span_end),\n        );\n\n        if contains(&function, ContainsSymbol::Super) {\n            return Err(Error::lex(LexError::Syntax(\n                \"invalid super usage\".into(),\n                params_start_position,\n            )));\n        }\n\n        Ok(function)\n    }\n}\n"
  },
  {
    "path": "core/parser/src/parser/expression/primary/function_expression/tests.rs",
    "content": "use crate::parser::tests::check_script_parser;\nuse boa_ast::{\n    Declaration, Span, Statement, StatementList, StatementListItem,\n    declaration::{LexicalDeclaration, Variable},\n    expression::{Identifier, literal::Literal},\n    function::{FormalParameterList, FunctionBody, FunctionExpression},\n    statement::Return,\n};\nuse boa_interner::Interner;\nuse boa_macros::utf16;\nuse indoc::indoc;\n\nconst PSEUDO_LINEAR_POS: boa_ast::LinearPosition = boa_ast::LinearPosition::new(0);\n\n/// Checks async expression parsing.\n#[test]\nfn check_function_expression() {\n    let interner = &mut Interner::default();\n    let add = interner.get_or_intern_static(\"add\", utf16!(\"add\"));\n    check_script_parser(\n        indoc! {\"\n            const add = function() {\n                return 1;\n            };\n        \"},\n        vec![\n            Declaration::Lexical(LexicalDeclaration::Const(\n                vec![Variable::from_identifier(\n                    Identifier::new(add, Span::new((1, 7), (1, 10))),\n                    Some(\n                        FunctionExpression::new(\n                            Some(Identifier::new(add, Span::new((1, 7), (1, 10)))),\n                            FormalParameterList::default(),\n                            FunctionBody::new(\n                                StatementList::new(\n                                    [StatementListItem::Statement(\n                                        Statement::Return(Return::new(Some(\n                                            Literal::new(1, Span::new((2, 12), (2, 13))).into(),\n                                        )))\n                                        .into(),\n                                    )],\n                                    PSEUDO_LINEAR_POS,\n                                    false,\n                                ),\n                                Span::new((1, 24), (3, 2)),\n                            ),\n                            None,\n                            false,\n                            Span::new((1, 13), (3, 2)),\n                        )\n                        .into(),\n                    ),\n                )]\n                .try_into()\n                .unwrap(),\n            ))\n            .into(),\n        ],\n        interner,\n    );\n}\n\n#[test]\nfn check_nested_function_expression() {\n    let interner = &mut Interner::default();\n    let a = interner.get_or_intern_static(\"a\", utf16!(\"a\"));\n    let b = interner.get_or_intern_static(\"b\", utf16!(\"b\"));\n    check_script_parser(\n        indoc! {\"\n            const a = function() {\n                const b = function() {\n                    return 1;\n                };\n            };\n        \"},\n        vec![\n            Declaration::Lexical(LexicalDeclaration::Const(\n                vec![Variable::from_identifier(\n                    Identifier::new(a, Span::new((1, 7), (1, 8))),\n                    Some(\n                        FunctionExpression::new(\n                            Some(Identifier::new(a, Span::new((1, 7), (1, 8)))),\n                            FormalParameterList::default(),\n                            FunctionBody::new(\n                                StatementList::new(\n                                    [Declaration::Lexical(LexicalDeclaration::Const(\n                                        vec![Variable::from_identifier(\n                                            Identifier::new(b, Span::new((2, 11), (2, 12))),\n                                            Some(\n                                                FunctionExpression::new(\n                                                    Some(Identifier::new(\n                                                        b,\n                                                        Span::new((2, 11), (2, 12)),\n                                                    )),\n                                                    FormalParameterList::default(),\n                                                    FunctionBody::new(\n                                                        StatementList::new(\n                                                            [StatementListItem::Statement(\n                                                                Statement::Return(Return::new(\n                                                                    Some(\n                                                                        Literal::new(\n                                                                            1,\n                                                                            Span::new(\n                                                                                (3, 16),\n                                                                                (3, 17),\n                                                                            ),\n                                                                        )\n                                                                        .into(),\n                                                                    ),\n                                                                ))\n                                                                .into(),\n                                                            )],\n                                                            PSEUDO_LINEAR_POS,\n                                                            false,\n                                                        ),\n                                                        Span::new((2, 26), (4, 6)),\n                                                    ),\n                                                    None,\n                                                    false,\n                                                    Span::new((2, 15), (4, 6)),\n                                                )\n                                                .into(),\n                                            ),\n                                        )]\n                                        .try_into()\n                                        .unwrap(),\n                                    ))\n                                    .into()],\n                                    PSEUDO_LINEAR_POS,\n                                    false,\n                                ),\n                                Span::new((1, 22), (5, 2)),\n                            ),\n                            None,\n                            false,\n                            Span::new((1, 11), (5, 2)),\n                        )\n                        .into(),\n                    ),\n                )]\n                .try_into()\n                .unwrap(),\n            ))\n            .into(),\n        ],\n        interner,\n    );\n}\n\n#[test]\nfn check_function_non_reserved_keyword() {\n    macro_rules! genast {\n        (\n            $keyword:literal,\n            $interner:expr,\n            function: $function_span:expr,\n            name: $name_span:expr,\n            body: $body_span:expr,\n            literal: $literal_span:expr\n        ) => {\n            vec![Declaration::Lexical(LexicalDeclaration::Const(\n                vec![Variable::from_identifier(\n                    Identifier::new($interner.get_or_intern_static(\"add\", utf16!(\"add\")), Span::new((1, 7), (1, 10))),\n                    Some(\n                        FunctionExpression::new(\n                            Some(Identifier::new($interner.get_or_intern_static($keyword, utf16!($keyword)), $name_span)),\n                            FormalParameterList::default(),\n                            FunctionBody::new(\n                                StatementList::new(\n                                    [StatementListItem::Statement(\n                                        Statement::Return(\n                                            Return::new(\n                                                Some(Literal::new(1, $literal_span).into())\n                                            )\n                                        ).into()\n                                    )],\n                                    PSEUDO_LINEAR_POS,\n                                    false,\n                                ),\n                                $body_span,\n                            ),\n                            None,\n                            true,\n                            $function_span,\n                        )\n                        .into(),\n                    ),\n                )]\n                .try_into().unwrap(),\n            ))\n            .into()]\n        };\n    }\n\n    let interner = &mut Interner::default();\n    let ast = genast!(\n        \"as\",\n        interner,\n        function: Span::new((1, 13), (1, 40)),\n        name: Span::new((1, 22), (1, 24)),\n        body: Span::new((1, 27), (1, 40)),\n        literal: Span::new((1, 36), (1, 37))\n    );\n    check_script_parser(\"const add = function as() { return 1; };\", ast, interner);\n\n    let interner = &mut Interner::default();\n    let ast = genast!(\n        \"async\",\n        interner,\n        function: Span::new((1, 13), (1, 43)),\n        name: Span::new((1, 22), (1, 27)),\n        body: Span::new((1, 30), (1, 43)),\n        literal: Span::new((1, 39), (1, 40))\n    );\n    check_script_parser(\"const add = function async() { return 1; };\", ast, interner);\n\n    let interner = &mut Interner::default();\n    let ast = genast!(\n        \"from\",\n        interner,\n        function: Span::new((1, 13), (1, 42)),\n        name: Span::new((1, 22), (1, 26)),\n        body: Span::new((1, 29), (1, 42)),\n        literal: Span::new((1, 38), (1, 39))\n    );\n    check_script_parser(\"const add = function from() { return 1; };\", ast, interner);\n\n    let interner = &mut Interner::default();\n    let ast = genast!(\n        \"get\",\n        interner,\n        function: Span::new((1, 13), (1, 41)),\n        name: Span::new((1, 22), (1, 25)),\n        body: Span::new((1, 28), (1, 41)),\n        literal: Span::new((1, 37), (1, 38))\n    );\n    check_script_parser(\"const add = function get() { return 1; };\", ast, interner);\n\n    let interner = &mut Interner::default();\n    let ast = genast!(\n        \"meta\",\n        interner,\n        function: Span::new((1, 13), (1, 42)),\n        name: Span::new((1, 22), (1, 26)),\n        body: Span::new((1, 29), (1, 42)),\n        literal: Span::new((1, 38), (1, 39))\n    );\n    check_script_parser(\"const add = function meta() { return 1; };\", ast, interner);\n\n    let interner = &mut Interner::default();\n    let ast = genast!(\n        \"of\",\n        interner,\n        function: Span::new((1, 13), (1, 40)),\n        name: Span::new((1, 22), (1, 24)),\n        body: Span::new((1, 27), (1, 40)),\n        literal: Span::new((1, 36), (1, 37))\n    );\n    check_script_parser(\"const add = function of() { return 1; };\", ast, interner);\n\n    let interner = &mut Interner::default();\n    let ast = genast!(\n        \"set\",\n        interner,\n        function: Span::new((1, 13), (1, 41)),\n        name: Span::new((1, 22), (1, 25)),\n        body: Span::new((1, 28), (1, 41)),\n        literal: Span::new((1, 37), (1, 38))\n    );\n    check_script_parser(\"const add = function set() { return 1; };\", ast, interner);\n\n    let interner = &mut Interner::default();\n    let ast = genast!(\n        \"target\",\n        interner,\n        function: Span::new((1, 13), (1, 44)),\n        name: Span::new((1, 22), (1, 28)),\n        body: Span::new((1, 31), (1, 44)),\n        literal: Span::new((1, 40), (1, 41))\n    );\n    check_script_parser(\n        \"const add = function target() { return 1; };\",\n        ast,\n        interner,\n    );\n}\n"
  },
  {
    "path": "core/parser/src/parser/expression/primary/generator_expression/mod.rs",
    "content": "//! Generator expression parsing.\n//!\n//! More information:\n//!  - [MDN documentation][mdn]\n//!  - [ECMAScript specification][spec]\n//!\n//! [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/function*\n//! [spec]: https://tc39.es/ecma262/#prod-GeneratorExpression\n\n#[cfg(test)]\nmod tests;\n\nuse crate::{\n    Error,\n    lexer::{Error as LexError, TokenKind},\n    parser::{\n        Cursor, OrAbrupt, ParseResult, TokenParser,\n        expression::BindingIdentifier,\n        function::{FormalParameters, FunctionBody},\n        name_in_lexically_declared_names,\n    },\n    source::ReadChar,\n};\nuse boa_ast::{\n    Keyword, Punctuator, Span, Spanned,\n    function::GeneratorExpression as GeneratorExpressionNode,\n    operations::{ContainsSymbol, bound_names, contains, lexically_declared_names},\n};\nuse boa_interner::{Interner, Sym};\n\n/// Generator expression parsing.\n///\n/// More information:\n///  - [MDN documentation][mdn]\n///  - [ECMAScript specification][spec]\n///\n/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/function*\n/// [spec]: https://tc39.es/ecma262/#prod-GeneratorExpression\n#[derive(Debug, Clone, Copy)]\npub(super) struct GeneratorExpression {}\n\nimpl GeneratorExpression {\n    /// Creates a new `GeneratorExpression` parser.\n    pub(in crate::parser) fn new() -> Self {\n        Self {}\n    }\n}\n\nimpl<R> TokenParser<R> for GeneratorExpression\nwhere\n    R: ReadChar,\n{\n    type Output = GeneratorExpressionNode;\n\n    fn parse(self, cursor: &mut Cursor<R>, interner: &mut Interner) -> ParseResult<Self::Output> {\n        let token = cursor.expect((Keyword::Function, false), \"generator expression\", interner)?;\n        let start_linear_span = token.linear_span();\n        let function_span_start = token.span().start();\n\n        cursor.expect(\n            TokenKind::Punctuator(Punctuator::Mul),\n            \"generator expression\",\n            interner,\n        )?;\n\n        let token = cursor.peek(0, interner).or_abrupt()?;\n        let (name, name_span) = match token.kind() {\n            TokenKind::IdentifierName(_)\n            | TokenKind::Keyword((\n                Keyword::Yield | Keyword::Await | Keyword::Async | Keyword::Of,\n                _,\n            )) => {\n                let span = token.span();\n                let name = BindingIdentifier::new(true, false).parse(cursor, interner)?;\n\n                (Some(name), span)\n            }\n            _ => (None, token.span()),\n        };\n\n        let params_start_position = cursor\n            .expect(Punctuator::OpenParen, \"generator expression\", interner)?\n            .span()\n            .end();\n\n        let params = FormalParameters::new(true, false).parse(cursor, interner)?;\n\n        cursor.expect(Punctuator::CloseParen, \"generator expression\", interner)?;\n\n        let body =\n            FunctionBody::new(true, false, \"generator expression\").parse(cursor, interner)?;\n\n        // If the source text matched by FormalParameters is strict mode code,\n        // the Early Error rules for UniqueFormalParameters : FormalParameters are applied.\n        // https://tc39.es/ecma262/#sec-generator-function-definitions-static-semantics-early-errors\n        if (cursor.strict() || body.strict()) && params.has_duplicates() {\n            return Err(Error::lex(LexError::Syntax(\n                \"Duplicate parameter name not allowed in this context\".into(),\n                params_start_position,\n            )));\n        }\n\n        // It is a Syntax Error if FunctionBodyContainsUseStrict of GeneratorBody is true\n        // and IsSimpleParameterList of FormalParameters is false.\n        // https://tc39.es/ecma262/#sec-generator-function-definitions-static-semantics-early-errors\n        if body.strict() && !params.is_simple() {\n            return Err(Error::lex(LexError::Syntax(\n                \"Illegal 'use strict' directive in function with non-simple parameter list\".into(),\n                params_start_position,\n            )));\n        }\n\n        // Early Error: If BindingIdentifier is present and the source code matching BindingIdentifier is strict mode code,\n        // it is a Syntax Error if the StringValue of BindingIdentifier is \"eval\" or \"arguments\".\n        if let Some(name) = name\n            && (cursor.strict() || body.strict())\n            && [Sym::EVAL, Sym::ARGUMENTS].contains(&name.sym())\n        {\n            return Err(Error::lex(LexError::Syntax(\n                \"unexpected identifier 'eval' or 'arguments' in strict mode\".into(),\n                name_span.start(),\n            )));\n        }\n\n        // Catch early error for BindingIdentifier, because strictness of the functions body is also\n        // relevant for the function parameters.\n        if body.strict() && contains(&params, ContainsSymbol::EvalOrArguments) {\n            return Err(Error::lex(LexError::Syntax(\n                \"unexpected identifier 'eval' or 'arguments' in strict mode\".into(),\n                params_start_position,\n            )));\n        }\n\n        // It is a Syntax Error if any element of the BoundNames of FormalParameters\n        // also occurs in the LexicallyDeclaredNames of GeneratorBody.\n        // https://tc39.es/ecma262/#sec-generator-function-definitions-static-semantics-early-errors\n        name_in_lexically_declared_names(\n            &bound_names(&params),\n            &lexically_declared_names(&body),\n            params_start_position,\n            interner,\n        )?;\n\n        // It is a Syntax Error if FormalParameters Contains YieldExpression is true.\n        // https://tc39.es/ecma262/#sec-generator-function-definitions-static-semantics-early-errors\n        if contains(&params, ContainsSymbol::YieldExpression) {\n            return Err(Error::lex(LexError::Syntax(\n                \"generator expression cannot contain yield expression in parameters\".into(),\n                params_start_position,\n            )));\n        }\n\n        let span = start_linear_span.union(body.linear_pos_end());\n\n        let function_span_end = body.span().end();\n        let function = GeneratorExpressionNode::new(\n            name,\n            params,\n            body,\n            span,\n            name.is_some(),\n            Span::new(function_span_start, function_span_end),\n        );\n\n        if contains(&function, ContainsSymbol::Super) {\n            return Err(Error::lex(LexError::Syntax(\n                \"invalid super usage\".into(),\n                params_start_position,\n            )));\n        }\n\n        Ok(function)\n    }\n}\n"
  },
  {
    "path": "core/parser/src/parser/expression/primary/generator_expression/tests.rs",
    "content": "use crate::parser::tests::check_script_parser;\nuse boa_ast::{\n    Declaration, Expression, Span, Statement, StatementList, StatementListItem,\n    declaration::{LexicalDeclaration, Variable},\n    expression::{Identifier, Yield, literal::Literal},\n    function::{FormalParameterList, FunctionBody, GeneratorExpression},\n};\nuse boa_interner::Interner;\nuse boa_macros::utf16;\nuse indoc::indoc;\n\nconst PSEUDO_LINEAR_POS: boa_ast::LinearPosition = boa_ast::LinearPosition::new(0);\nconst EMPTY_LINEAR_SPAN: boa_ast::LinearSpan =\n    boa_ast::LinearSpan::new(PSEUDO_LINEAR_POS, PSEUDO_LINEAR_POS);\n\n#[test]\nfn check_generator_function_expression() {\n    let interner = &mut Interner::default();\n    let r#gen = interner.get_or_intern_static(\"gen\", utf16!(\"gen\"));\n    check_script_parser(\n        indoc! {\"\n            const gen = function*() {\n                yield 1;\n            };\n        \"},\n        vec![\n            Declaration::Lexical(LexicalDeclaration::Const(\n                vec![Variable::from_identifier(\n                    Identifier::new(r#gen, Span::new((1, 7), (1, 10))),\n                    Some(\n                        GeneratorExpression::new(\n                            Some(Identifier::new(r#gen, Span::new((1, 7), (1, 10)))),\n                            FormalParameterList::default(),\n                            FunctionBody::new(\n                                StatementList::new(\n                                    [StatementListItem::Statement(\n                                        Statement::Expression(Expression::from(Yield::new(\n                                            Some(\n                                                Literal::new(1, Span::new((2, 11), (2, 12))).into(),\n                                            ),\n                                            false,\n                                            Span::new((2, 5), (2, 12)),\n                                        )))\n                                        .into(),\n                                    )],\n                                    PSEUDO_LINEAR_POS,\n                                    false,\n                                ),\n                                Span::new((1, 25), (3, 2)),\n                            ),\n                            EMPTY_LINEAR_SPAN,\n                            false,\n                            Span::new((1, 13), (3, 2)),\n                        )\n                        .into(),\n                    ),\n                )]\n                .try_into()\n                .unwrap(),\n            ))\n            .into(),\n        ],\n        interner,\n    );\n}\n\n#[test]\nfn check_generator_function_delegate_yield_expression() {\n    let interner = &mut Interner::default();\n    let r#gen = interner.get_or_intern_static(\"gen\", utf16!(\"gen\"));\n    check_script_parser(\n        indoc! {\"\n            const gen = function*() {\n                yield* 1;\n            };\n        \"},\n        vec![\n            Declaration::Lexical(LexicalDeclaration::Const(\n                vec![Variable::from_identifier(\n                    Identifier::new(r#gen, Span::new((1, 7), (1, 10))),\n                    Some(\n                        GeneratorExpression::new(\n                            Some(Identifier::new(r#gen, Span::new((1, 7), (1, 10)))),\n                            FormalParameterList::default(),\n                            FunctionBody::new(\n                                StatementList::new(\n                                    [StatementListItem::Statement(\n                                        Statement::Expression(Expression::from(Yield::new(\n                                            Some(\n                                                Literal::new(1, Span::new((2, 12), (2, 13))).into(),\n                                            ),\n                                            true,\n                                            Span::new((2, 5), (2, 13)),\n                                        )))\n                                        .into(),\n                                    )],\n                                    PSEUDO_LINEAR_POS,\n                                    false,\n                                ),\n                                Span::new((1, 25), (3, 2)),\n                            ),\n                            EMPTY_LINEAR_SPAN,\n                            false,\n                            Span::new((1, 13), (3, 2)),\n                        )\n                        .into(),\n                    ),\n                )]\n                .try_into()\n                .unwrap(),\n            ))\n            .into(),\n        ],\n        interner,\n    );\n}\n"
  },
  {
    "path": "core/parser/src/parser/expression/primary/mod.rs",
    "content": "//! Primary expression parsing.\n//!\n//! More information:\n//!  - [MDN documentation][mdn]\n//!  - [ECMAScript specification][spec]\n//!\n//! [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators#Primary_expressions\n//! [spec]: https://tc39.es/ecma262/#prod-PrimaryExpression\n\n#[cfg(test)]\nmod tests;\n\nmod array_initializer;\nmod async_function_expression;\nmod async_generator_expression;\nmod class_expression;\nmod function_expression;\nmod generator_expression;\nmod template;\n\npub(in crate::parser) mod object_initializer;\n\nuse self::{\n    array_initializer::ArrayLiteral, async_function_expression::AsyncFunctionExpression,\n    async_generator_expression::AsyncGeneratorExpression, class_expression::ClassExpression,\n    function_expression::FunctionExpression, generator_expression::GeneratorExpression,\n    object_initializer::ObjectLiteral,\n};\nuse crate::{\n    Error,\n    lexer::{\n        InputElement, Token, TokenKind,\n        token::{ContainsEscapeSequence, Numeric},\n    },\n    parser::{\n        AllowAwait, AllowYield, Cursor, OrAbrupt, ParseResult, TokenParser,\n        expression::{\n            BindingIdentifier, Expression, FormalParameterListOrExpression,\n            identifiers::IdentifierReference, primary::template::TemplateLiteral,\n        },\n        statement::{ArrayBindingPattern, ObjectBindingPattern},\n    },\n    source::ReadChar,\n};\nuse ast::expression::RegExpLiteral as AstRegExp;\nuse boa_ast::{\n    self as ast, Keyword, Punctuator, Span, Spanned,\n    declaration::Variable,\n    expression::{\n        Identifier, Parenthesized, This,\n        literal::{self, Literal, LiteralKind, TemplateElement},\n        operator::{assign::AssignTarget, binary::BinaryOp},\n    },\n    function::{FormalParameter, FormalParameterList},\n    operations::{ContainsSymbol, contains},\n    pattern::{ArrayPattern, ObjectPattern, Pattern},\n};\nuse boa_interner::{Interner, Sym};\n\npub(in crate::parser) use object_initializer::Initializer;\n\n/// Parses a primary expression.\n///\n/// More information:\n///  - [MDN documentation][mdn]\n///  - [ECMAScript specification][spec]\n///\n/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Expressions_and_Operators#Primary_expressions\n/// [spec]: https://tc39.es/ecma262/#prod-PrimaryExpression\n#[derive(Debug, Clone, Copy)]\npub(super) struct PrimaryExpression {\n    allow_yield: AllowYield,\n    allow_await: AllowAwait,\n}\n\nimpl PrimaryExpression {\n    /// Creates a new `PrimaryExpression` parser.\n    pub(super) fn new<Y, A>(allow_yield: Y, allow_await: A) -> Self\n    where\n        Y: Into<AllowYield>,\n        A: Into<AllowAwait>,\n    {\n        Self {\n            allow_yield: allow_yield.into(),\n            allow_await: allow_await.into(),\n        }\n    }\n}\n\nimpl<R> TokenParser<R> for PrimaryExpression\nwhere\n    R: ReadChar,\n{\n    type Output = FormalParameterListOrExpression;\n\n    fn parse(self, cursor: &mut Cursor<R>, interner: &mut Interner) -> ParseResult<Self::Output> {\n        // TODO: tok currently consumes the token instead of peeking, so the token\n        // isn't passed and consumed by parsers according to spec (EX: GeneratorExpression)\n        let tok = cursor.peek(0, interner).or_abrupt()?;\n        let tok_position = tok.span().start();\n\n        match tok.kind() {\n            TokenKind::Keyword((Keyword::This, true))\n            | TokenKind::BooleanLiteral((_, ContainsEscapeSequence(true)))\n            | TokenKind::NullLiteral(ContainsEscapeSequence(true)) => Err(Error::general(\n                \"Keyword must not contain escaped characters\",\n                tok_position,\n            )),\n            TokenKind::Keyword((Keyword::This, false)) => {\n                let span = tok.span();\n                cursor.advance(interner);\n                Ok(This::new(span).into())\n            }\n            TokenKind::Keyword((Keyword::Function, _)) => {\n                let next_token = cursor.peek(1, interner).or_abrupt()?;\n                if next_token.kind() == &TokenKind::Punctuator(Punctuator::Mul) {\n                    GeneratorExpression::new()\n                        .parse(cursor, interner)\n                        .map(Into::into)\n                } else {\n                    FunctionExpression::new()\n                        .parse(cursor, interner)\n                        .map(Into::into)\n                }\n            }\n            TokenKind::Keyword((Keyword::Class, _)) => {\n                ClassExpression::new(self.allow_yield, self.allow_await)\n                    .parse(cursor, interner)\n                    .map(Into::into)\n            }\n            TokenKind::Keyword((Keyword::Async, contain_escaped_char)) => {\n                let contain_escaped_char = *contain_escaped_char;\n                let skip_n = if cursor.peek_is_line_terminator(0, interner).or_abrupt()? {\n                    2\n                } else {\n                    1\n                };\n                let is_line_terminator = cursor\n                    .peek_is_line_terminator(skip_n, interner)?\n                    .unwrap_or(true);\n\n                match cursor.peek(1, interner)?.map(Token::kind) {\n                    Some(TokenKind::Keyword((Keyword::Function, _)))\n                        if !is_line_terminator && !contain_escaped_char =>\n                    {\n                        match cursor.peek(2, interner)?.map(Token::kind) {\n                            Some(TokenKind::Punctuator(Punctuator::Mul)) => {\n                                AsyncGeneratorExpression::new()\n                                    .parse(cursor, interner)\n                                    .map(Into::into)\n                            }\n                            _ => AsyncFunctionExpression::new()\n                                .parse(cursor, interner)\n                                .map(Into::into),\n                        }\n                    }\n                    _ => IdentifierReference::new(self.allow_yield, self.allow_await)\n                        .parse(cursor, interner)\n                        .map(Into::into),\n                }\n            }\n            TokenKind::Punctuator(Punctuator::OpenParen) => {\n                let expr = CoverParenthesizedExpressionAndArrowParameterList::new(\n                    self.allow_yield,\n                    self.allow_await,\n                )\n                .parse(cursor, interner)?;\n                Ok(expr)\n            }\n            TokenKind::Punctuator(Punctuator::OpenBracket) => {\n                ArrayLiteral::new(self.allow_yield, self.allow_await)\n                    .parse(cursor, interner)\n                    .map(Into::into)\n            }\n            TokenKind::Punctuator(Punctuator::OpenBlock) => {\n                ObjectLiteral::new(self.allow_yield, self.allow_await)\n                    .parse(cursor, interner)\n                    .map(Into::into)\n            }\n            TokenKind::BooleanLiteral((boolean, _)) => {\n                let node = Literal::with_linear_span(*boolean, tok.span(), tok.linear_span());\n                cursor.advance(interner);\n                Ok(node.into())\n            }\n            TokenKind::NullLiteral(_) => {\n                let node =\n                    Literal::with_linear_span(LiteralKind::Null, tok.span(), tok.linear_span());\n                cursor.advance(interner);\n                Ok(node.into())\n            }\n            TokenKind::IdentifierName(_)\n            | TokenKind::Keyword((\n                Keyword::Let | Keyword::Yield | Keyword::Await | Keyword::Of,\n                _,\n            )) => IdentifierReference::new(self.allow_yield, self.allow_await)\n                .parse(cursor, interner)\n                .map(Into::into),\n            TokenKind::StringLiteral((lit, _)) => {\n                let node = Literal::with_linear_span(*lit, tok.span(), tok.linear_span());\n                cursor.advance(interner);\n                Ok(node.into())\n            }\n            TokenKind::TemplateNoSubstitution(template_string) => {\n                let Some(cooked) = template_string.cooked() else {\n                    return Err(Error::general(\n                        \"invalid escape in template literal\",\n                        tok.span().start(),\n                    ));\n                };\n                let temp = literal::TemplateLiteral::new(\n                    Box::new([TemplateElement::String(cooked)]),\n                    tok.span(),\n                );\n                cursor.advance(interner);\n                Ok(temp.into())\n            }\n            TokenKind::NumericLiteral(Numeric::Integer(num)) => {\n                let node = Literal::with_linear_span(*num, tok.span(), tok.linear_span());\n                cursor.advance(interner);\n                Ok(node.into())\n            }\n            TokenKind::NumericLiteral(Numeric::Rational(num)) => {\n                let node = Literal::with_linear_span(*num, tok.span(), tok.linear_span());\n                cursor.advance(interner);\n                Ok(node.into())\n            }\n            TokenKind::NumericLiteral(Numeric::BigInt(num)) => {\n                let node = Literal::with_linear_span(num.clone(), tok.span(), tok.linear_span());\n                cursor.advance(interner);\n                Ok(node.into())\n            }\n            TokenKind::RegularExpressionLiteral(body, flags) => {\n                let node = AstRegExp::new(*body, *flags, tok.span()).into();\n                cursor.advance(interner);\n                Ok(node)\n            }\n            TokenKind::Punctuator(div @ (Punctuator::Div | Punctuator::AssignDiv)) => {\n                let init_with_eq = div == &Punctuator::AssignDiv;\n\n                let start_pos_group = tok.start_group();\n                cursor.advance(interner);\n                let tok = cursor.lex_regex(start_pos_group, interner, init_with_eq)?;\n\n                if let TokenKind::RegularExpressionLiteral(body, flags) = *tok.kind() {\n                    Ok(AstRegExp::new(body, flags, tok.span()).into())\n                } else {\n                    // A regex was expected and nothing else.\n                    Err(Error::unexpected(\n                        tok.to_string(interner),\n                        tok.span(),\n                        \"regular expression literal\",\n                    ))\n                }\n            }\n            TokenKind::TemplateMiddle(template_string) => {\n                let Some(cooked) = template_string.cooked() else {\n                    return Err(Error::general(\n                        \"invalid escape in template literal\",\n                        tok.span().start(),\n                    ));\n                };\n                let parser = TemplateLiteral::new(\n                    self.allow_yield,\n                    self.allow_await,\n                    tok.start_group(),\n                    cooked,\n                );\n                cursor.advance(interner);\n                parser.parse(cursor, interner).map(Into::into)\n            }\n            _ => Err(Error::unexpected(\n                tok.to_string(interner),\n                tok.span(),\n                \"primary expression\",\n            )),\n        }\n    }\n}\n\n/// Parses a `CoverParenthesizedExpressionAndArrowParameterList` expression.\n///\n/// More information:\n///  - [ECMAScript specification][spec]\n///\n/// [spec]: https://tc39.es/ecma262/#prod-CoverParenthesizedExpressionAndArrowParameterList\n#[derive(Debug, Clone, Copy)]\npub(super) struct CoverParenthesizedExpressionAndArrowParameterList {\n    allow_yield: AllowYield,\n    allow_await: AllowAwait,\n}\n\nimpl CoverParenthesizedExpressionAndArrowParameterList {\n    /// Creates a new `CoverParenthesizedExpressionAndArrowParameterList` parser.\n    pub(super) fn new<Y, A>(allow_yield: Y, allow_await: A) -> Self\n    where\n        Y: Into<AllowYield>,\n        A: Into<AllowAwait>,\n    {\n        Self {\n            allow_yield: allow_yield.into(),\n            allow_await: allow_await.into(),\n        }\n    }\n}\n\nimpl<R> TokenParser<R> for CoverParenthesizedExpressionAndArrowParameterList\nwhere\n    R: ReadChar,\n{\n    type Output = FormalParameterListOrExpression;\n\n    fn parse(self, cursor: &mut Cursor<R>, interner: &mut Interner) -> ParseResult<Self::Output> {\n        #[derive(Debug)]\n        enum InnerExpression {\n            Expression(ast::Expression),\n            SpreadObject(ObjectPattern),\n            SpreadArray(ArrayPattern),\n            SpreadBinding(Identifier),\n        }\n        let span_start = cursor\n            .expect(\n                Punctuator::OpenParen,\n                \"parenthesis expression or arrow function\",\n                interner,\n            )?\n            .span();\n\n        cursor.set_goal(InputElement::RegExp);\n\n        let mut expressions = Vec::new();\n        let mut tailing_comma = None;\n\n        let next = cursor.peek(0, interner).or_abrupt()?;\n        let span = match next.kind() {\n            TokenKind::Punctuator(Punctuator::CloseParen) => {\n                let span = next.span();\n                cursor.advance(interner);\n                span\n            }\n            TokenKind::Punctuator(Punctuator::Spread) => {\n                cursor.advance(interner);\n                let next = cursor.peek(0, interner).or_abrupt()?;\n                match next.kind() {\n                    TokenKind::Punctuator(Punctuator::OpenBlock) => {\n                        let bindings =\n                            ObjectBindingPattern::new(self.allow_yield, self.allow_await)\n                                .parse(cursor, interner)?;\n                        expressions.push(InnerExpression::SpreadObject(bindings));\n                    }\n                    TokenKind::Punctuator(Punctuator::OpenBracket) => {\n                        let bindings = ArrayBindingPattern::new(self.allow_yield, self.allow_await)\n                            .parse(cursor, interner)?;\n                        expressions.push(InnerExpression::SpreadArray(bindings));\n                    }\n                    _ => {\n                        let binding = BindingIdentifier::new(self.allow_yield, self.allow_await)\n                            .parse(cursor, interner)?;\n                        expressions.push(InnerExpression::SpreadBinding(binding));\n                    }\n                }\n\n                cursor\n                    .expect(\n                        Punctuator::CloseParen,\n                        \"CoverParenthesizedExpressionAndArrowParameterList\",\n                        interner,\n                    )?\n                    .span()\n            }\n            _ => {\n                let expression = Expression::new(true, self.allow_yield, self.allow_await)\n                    .parse(cursor, interner)?;\n                expressions.push(InnerExpression::Expression(expression));\n\n                let next = cursor.peek(0, interner).or_abrupt()?;\n                match next.kind() {\n                    TokenKind::Punctuator(Punctuator::CloseParen) => {\n                        let span = next.span();\n                        cursor.advance(interner);\n                        span\n                    }\n                    TokenKind::Punctuator(Punctuator::Comma) => {\n                        cursor.advance(interner);\n                        let next = cursor.peek(0, interner).or_abrupt()?;\n                        match next.kind() {\n                            TokenKind::Punctuator(Punctuator::CloseParen) => {\n                                let span = next.span();\n                                tailing_comma = Some(next.span());\n                                cursor.advance(interner);\n                                span\n                            }\n                            TokenKind::Punctuator(Punctuator::Spread) => {\n                                cursor.advance(interner);\n                                let next = cursor.peek(0, interner).or_abrupt()?;\n                                match next.kind() {\n                                    TokenKind::Punctuator(Punctuator::OpenBlock) => {\n                                        let bindings = ObjectBindingPattern::new(\n                                            self.allow_yield,\n                                            self.allow_await,\n                                        )\n                                        .parse(cursor, interner)?;\n                                        expressions.push(InnerExpression::SpreadObject(bindings));\n                                    }\n                                    TokenKind::Punctuator(Punctuator::OpenBracket) => {\n                                        let bindings = ArrayBindingPattern::new(\n                                            self.allow_yield,\n                                            self.allow_await,\n                                        )\n                                        .parse(cursor, interner)?;\n                                        expressions.push(InnerExpression::SpreadArray(bindings));\n                                    }\n                                    _ => {\n                                        let binding = BindingIdentifier::new(\n                                            self.allow_yield,\n                                            self.allow_await,\n                                        )\n                                        .parse(cursor, interner)?;\n                                        expressions.push(InnerExpression::SpreadBinding(binding));\n                                    }\n                                }\n\n                                cursor\n                                    .expect(\n                                        Punctuator::CloseParen,\n                                        \"CoverParenthesizedExpressionAndArrowParameterList\",\n                                        interner,\n                                    )?\n                                    .span()\n                            }\n                            _ => {\n                                return Err(Error::expected(\n                                    vec![\")\".to_owned(), \"...\".to_owned()],\n                                    next.kind().to_string(interner),\n                                    next.span(),\n                                    \"CoverParenthesizedExpressionAndArrowParameterList\",\n                                ));\n                            }\n                        }\n                    }\n                    _ => {\n                        return Err(Error::expected(\n                            vec![\")\".to_owned(), \",\".to_owned()],\n                            next.kind().to_string(interner),\n                            next.span(),\n                            \"CoverParenthesizedExpressionAndArrowParameterList\",\n                        ));\n                    }\n                }\n            }\n        };\n\n        let is_arrow = if cursor.peek(0, interner)?.map(Token::kind)\n            == Some(&TokenKind::Punctuator(Punctuator::Arrow))\n        {\n            !cursor.peek_is_line_terminator(0, interner).or_abrupt()?\n        } else {\n            false\n        };\n\n        // If the next token is not an arrow, we know that we must parse a parenthesized expression.\n        if !is_arrow {\n            if let Some(span) = tailing_comma {\n                return Err(Error::unexpected(\n                    Punctuator::Comma,\n                    span,\n                    \"trailing comma in parenthesized expression\",\n                ));\n            }\n            if expressions.is_empty() {\n                return Err(Error::unexpected(\n                    Punctuator::CloseParen,\n                    span,\n                    \"empty parenthesized expression\",\n                ));\n            }\n            if expressions.len() != 1 {\n                return Err(Error::unexpected(\n                    Punctuator::CloseParen,\n                    span,\n                    \"multiple expressions in parenthesized expression\",\n                ));\n            }\n            if let InnerExpression::Expression(expression) = &expressions[0] {\n                return Ok(ast::Expression::Parenthesized(Parenthesized::new(\n                    expression.clone(),\n                    Span::new(span_start.start(), span.end()),\n                ))\n                .into());\n            }\n            return Err(Error::unexpected(\n                Punctuator::CloseParen,\n                span,\n                \"parenthesized expression with spread expressions\",\n            ));\n        }\n\n        // We know that we must parse an arrow function.\n        // We parse the expressions in to a parameter list.\n\n        let mut parameters = Vec::new();\n\n        for expression in expressions {\n            match expression {\n                InnerExpression::Expression(node) => {\n                    expression_to_formal_parameters(\n                        &node,\n                        &mut parameters,\n                        cursor.strict(),\n                        span_start,\n                    )?;\n                }\n                InnerExpression::SpreadObject(pattern) => {\n                    let declaration = Variable::from_pattern(pattern.into(), None);\n                    let parameter = FormalParameter::new(declaration, true);\n                    parameters.push(parameter);\n                }\n                InnerExpression::SpreadArray(pattern) => {\n                    let declaration = Variable::from_pattern(pattern.into(), None);\n                    let parameter = FormalParameter::new(declaration, true);\n                    parameters.push(parameter);\n                }\n                InnerExpression::SpreadBinding(ident) => {\n                    let declaration = Variable::from_identifier(ident, None);\n                    let parameter = FormalParameter::new(declaration, true);\n                    parameters.push(parameter);\n                }\n            }\n        }\n\n        let parameters = FormalParameterList::from(parameters);\n\n        if let Some(span) = tailing_comma\n            && parameters.has_rest_parameter()\n        {\n            return Err(Error::general(\n                \"rest parameter must be last formal parameter\",\n                span.start(),\n            ));\n        }\n\n        if contains(&parameters, ContainsSymbol::YieldExpression) {\n            return Err(Error::general(\n                \"yield expression is not allowed in formal parameter list of arrow function\",\n                span_start.start(),\n            ));\n        }\n\n        Ok(FormalParameterListOrExpression::FormalParameterList {\n            fpl: parameters,\n            span_start: span.start(),\n        })\n    }\n}\n\n/// Convert an expression to a formal parameter and append it to the given parameter list.\nfn expression_to_formal_parameters(\n    node: &ast::Expression,\n    parameters: &mut Vec<FormalParameter>,\n    strict: bool,\n    span: Span,\n) -> ParseResult<()> {\n    match node {\n        ast::Expression::Identifier(identifier) if strict && *identifier == Sym::EVAL => {\n            return Err(Error::general(\n                \"parameter name 'eval' not allowed in strict mode\",\n                span.start(),\n            ));\n        }\n        ast::Expression::Identifier(identifier) if strict && *identifier == Sym::ARGUMENTS => {\n            return Err(Error::general(\n                \"parameter name 'arguments' not allowed in strict mode\",\n                span.start(),\n            ));\n        }\n        ast::Expression::Identifier(identifier) => {\n            parameters.push(FormalParameter::new(\n                Variable::from_identifier(*identifier, None),\n                false,\n            ));\n        }\n        ast::Expression::Binary(bin_op) if bin_op.op() == BinaryOp::Comma => {\n            expression_to_formal_parameters(bin_op.lhs(), parameters, strict, span)?;\n            expression_to_formal_parameters(bin_op.rhs(), parameters, strict, span)?;\n        }\n        ast::Expression::Assign(assign) => match assign.lhs() {\n            AssignTarget::Identifier(ident) => {\n                parameters.push(FormalParameter::new(\n                    Variable::from_identifier(*ident, Some(assign.rhs().clone())),\n                    false,\n                ));\n            }\n            AssignTarget::Pattern(pattern) => match pattern {\n                Pattern::Object(pattern) => {\n                    parameters.push(FormalParameter::new(\n                        Variable::from_pattern(pattern.clone().into(), Some(assign.rhs().clone())),\n                        false,\n                    ));\n                }\n                Pattern::Array(pattern) => {\n                    parameters.push(FormalParameter::new(\n                        Variable::from_pattern(pattern.clone().into(), Some(assign.rhs().clone())),\n                        false,\n                    ));\n                }\n            },\n            AssignTarget::Access(_) => {\n                return Err(Error::general(\n                    \"invalid initialization expression in formal parameter list\",\n                    span.start(),\n                ));\n            }\n        },\n        ast::Expression::ObjectLiteral(object) => {\n            let pattern = object.to_pattern(strict).ok_or_else(|| {\n                Error::general(\n                    \"invalid object binding pattern in formal parameter list\",\n                    span.start(),\n                )\n            })?;\n\n            parameters.push(FormalParameter::new(\n                Variable::from_pattern(pattern.into(), None),\n                false,\n            ));\n        }\n        ast::Expression::ArrayLiteral(array) => {\n            let pattern = array.to_pattern(strict).ok_or_else(|| {\n                Error::general(\n                    \"invalid array binding pattern in formal parameter list\",\n                    span.start(),\n                )\n            })?;\n\n            parameters.push(FormalParameter::new(\n                Variable::from_pattern(pattern.into(), None),\n                false,\n            ));\n        }\n        _ => {\n            return Err(Error::unexpected(\n                \")\".to_string(),\n                span,\n                \"parenthesized expression with non-binding expression\",\n            ));\n        }\n    }\n    Ok(())\n}\n"
  },
  {
    "path": "core/parser/src/parser/expression/primary/object_initializer/mod.rs",
    "content": "//! Object initializer parsing.\n//!\n//! More information:\n//!  - [MDN documentation][mdn]\n//!  - [ECMAScript specification][spec]\n//!\n//! [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Object_initializer\n//! [spec]: https://tc39.es/ecma262/#sec-object-initializer\n\n#[cfg(test)]\nmod tests;\n\nuse crate::{\n    Error,\n    lexer::{\n        Error as LexError, InputElement, TokenKind,\n        token::{ContainsEscapeSequence, Numeric},\n    },\n    parser::{\n        AllowAwait, AllowIn, AllowYield, Cursor, OrAbrupt, ParseResult, TokenParser,\n        expression::{AssignmentExpression, identifiers::IdentifierReference},\n        function::{FormalParameter, FormalParameters, FunctionBody, UniqueFormalParameters},\n        name_in_lexically_declared_names,\n    },\n    source::ReadChar,\n};\nuse boa_ast::{\n    Expression, Keyword, Punctuator, Span, Spanned,\n    expression::{\n        Identifier,\n        literal::{\n            self, Literal, ObjectMethodDefinition, PropertyDefinition as PropertyDefinitionNode,\n        },\n    },\n    function::{\n        ClassElementName as ClassElementNameNode, FormalParameterList,\n        FunctionBody as FunctionBodyAst, PrivateName,\n    },\n    operations::{\n        ContainsSymbol, bound_names, contains, has_direct_super_new, lexically_declared_names,\n    },\n    property::{MethodDefinitionKind, PropertyName as PropertyNameNode},\n};\nuse boa_interner::{Interner, Sym};\n\n/// Parses an object literal.\n///\n/// More information:\n///  - [MDN documentation][mdn]\n///  - [ECMAScript specification][spec]\n///\n/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Object_initializer\n/// [spec]: https://tc39.es/ecma262/#prod-ObjectLiteral\n#[derive(Debug, Clone, Copy)]\npub(super) struct ObjectLiteral {\n    allow_yield: AllowYield,\n    allow_await: AllowAwait,\n}\n\nimpl ObjectLiteral {\n    /// Creates a new `ObjectLiteral` parser.\n    pub(super) fn new<Y, A>(allow_yield: Y, allow_await: A) -> Self\n    where\n        Y: Into<AllowYield>,\n        A: Into<AllowAwait>,\n    {\n        Self {\n            allow_yield: allow_yield.into(),\n            allow_await: allow_await.into(),\n        }\n    }\n}\n\nimpl<R> TokenParser<R> for ObjectLiteral\nwhere\n    R: ReadChar,\n{\n    type Output = literal::ObjectLiteral;\n\n    fn parse(self, cursor: &mut Cursor<R>, interner: &mut Interner) -> ParseResult<Self::Output> {\n        let open_block_token = cursor.expect(Punctuator::OpenBlock, \"object parsing\", interner)?;\n        cursor.set_goal(InputElement::RegExp);\n\n        let mut elements = Vec::new();\n\n        let mut has_proto = false;\n        let mut duplicate_proto_position = None;\n\n        let end = loop {\n            if let Some(token) = cursor.next_if(Punctuator::CloseBlock, interner)? {\n                break token.span().end();\n            }\n\n            let position = cursor.peek(0, interner).or_abrupt()?.span().start();\n\n            let property = PropertyDefinition::new(self.allow_yield, self.allow_await)\n                .parse(cursor, interner)?;\n\n            if let PropertyDefinitionNode::Property(PropertyNameNode::Literal(ident), _) = property\n                && ident.sym() == Sym::__PROTO__\n            {\n                if has_proto && duplicate_proto_position.is_none() {\n                    duplicate_proto_position = Some(position);\n                } else {\n                    has_proto = true;\n                }\n            }\n\n            elements.push(property);\n\n            if let Some(token) = cursor.next_if(Punctuator::CloseBlock, interner)? {\n                break token.span().end();\n            }\n\n            if cursor.next_if(Punctuator::Comma, interner)?.is_none() {\n                let next_token = cursor.next(interner).or_abrupt()?;\n                return Err(Error::expected(\n                    [\",\".to_owned(), \"}\".to_owned()],\n                    next_token.to_string(interner),\n                    next_token.span(),\n                    \"object literal\",\n                ));\n            }\n        };\n\n        if let Some(position) = duplicate_proto_position\n            && !cursor.json_parse()\n            && cursor\n                .peek(0, interner)?\n                .is_none_or(|token| token.kind() != &TokenKind::Punctuator(Punctuator::Assign))\n        {\n            return Err(Error::general(\n                \"Duplicate __proto__ fields are not allowed in object literals.\",\n                position,\n            ));\n        }\n\n        let start = open_block_token.span().start();\n        Ok(literal::ObjectLiteral::new(elements, Span::new(start, end)))\n    }\n}\n\n/// Parses a property definition.\n///\n/// More information:\n///  - [ECMAScript specification][spec]\n///\n/// [spec]: https://tc39.es/ecma262/#prod-PropertyDefinition\n#[derive(Debug, Clone, Copy)]\npub(in crate::parser) struct PropertyDefinition {\n    allow_yield: AllowYield,\n    allow_await: AllowAwait,\n}\n\nimpl PropertyDefinition {\n    /// Creates a new `PropertyDefinition` parser.\n    pub(in crate::parser) fn new<Y, A>(allow_yield: Y, allow_await: A) -> Self\n    where\n        Y: Into<AllowYield>,\n        A: Into<AllowAwait>,\n    {\n        Self {\n            allow_yield: allow_yield.into(),\n            allow_await: allow_await.into(),\n        }\n    }\n}\n\nimpl<R> TokenParser<R> for PropertyDefinition\nwhere\n    R: ReadChar,\n{\n    type Output = PropertyDefinitionNode;\n\n    fn parse(self, cursor: &mut Cursor<R>, interner: &mut Interner) -> ParseResult<Self::Output> {\n        match cursor.peek(1, interner).or_abrupt()?.kind() {\n            TokenKind::Punctuator(Punctuator::CloseBlock | Punctuator::Comma) => {\n                let ident = IdentifierReference::new(self.allow_yield, self.allow_await)\n                    .parse(cursor, interner)?;\n                return Ok(PropertyDefinitionNode::IdentifierReference(ident));\n            }\n            TokenKind::Punctuator(Punctuator::Assign) => {\n                return CoverInitializedName::new(self.allow_yield, self.allow_await)\n                    .parse(cursor, interner);\n            }\n            _ => {}\n        }\n\n        //  ... AssignmentExpression[+In, ?Yield, ?Await]\n        if cursor.next_if(Punctuator::Spread, interner)?.is_some() {\n            let node = AssignmentExpression::new(true, self.allow_yield, self.allow_await)\n                .parse(cursor, interner)?;\n            return Ok(PropertyDefinitionNode::SpreadObject(node));\n        }\n\n        //Async [AsyncMethod, AsyncGeneratorMethod] object methods\n        let is_keyword = !matches!(\n            cursor.peek(1, interner).or_abrupt()?.kind(),\n            TokenKind::Punctuator(Punctuator::OpenParen | Punctuator::Colon)\n        );\n\n        let token = cursor.peek(0, interner).or_abrupt()?;\n        let start_linear_pos = token.linear_span().start();\n\n        match token.kind() {\n            TokenKind::Keyword((Keyword::Async, true)) if is_keyword => {\n                return Err(Error::general(\n                    \"Keyword must not contain escaped characters\",\n                    token.span().start(),\n                ));\n            }\n            TokenKind::Keyword((Keyword::Async, false)) if is_keyword => {\n                cursor.advance(interner);\n                cursor.peek_expect_no_lineterminator(0, \"Async object methods\", interner)?;\n\n                let token = cursor.peek(0, interner).or_abrupt()?;\n                let position = token.span().start();\n\n                if token.kind() == &TokenKind::Punctuator(Punctuator::Mul) {\n                    let (class_element_name, params, body) =\n                        AsyncGeneratorMethod::new(self.allow_yield, self.allow_await)\n                            .parse(cursor, interner)?;\n\n                    let ClassElementNameNode::PropertyName(property_name) = class_element_name\n                    else {\n                        return Err(Error::general(\n                            \"private identifiers not allowed in object literal\",\n                            position,\n                        ));\n                    };\n\n                    // Early Error: It is a Syntax Error if HasDirectSuper of MethodDefinition is true.\n                    if has_direct_super_new(&params, &body) {\n                        return Err(Error::lex(LexError::Syntax(\n                            \"invalid super call usage\".into(),\n                            position,\n                        )));\n                    }\n\n                    return Ok(PropertyDefinitionNode::MethodDefinition(\n                        ObjectMethodDefinition::new(\n                            property_name,\n                            params,\n                            body,\n                            MethodDefinitionKind::AsyncGenerator,\n                            start_linear_pos,\n                        ),\n                    ));\n                }\n                let (class_element_name, params, body) =\n                    AsyncMethod::new(self.allow_yield, self.allow_await).parse(cursor, interner)?;\n\n                let ClassElementNameNode::PropertyName(property_name) = class_element_name else {\n                    return Err(Error::general(\n                        \"private identifiers not allowed in object literal\",\n                        position,\n                    ));\n                };\n\n                // Early Error: It is a Syntax Error if HasDirectSuper of MethodDefinition is true.\n                if has_direct_super_new(&params, &body) {\n                    return Err(Error::lex(LexError::Syntax(\n                        \"invalid super call usage\".into(),\n                        position,\n                    )));\n                }\n\n                return Ok(PropertyDefinitionNode::MethodDefinition(\n                    ObjectMethodDefinition::new(\n                        property_name,\n                        params,\n                        body,\n                        MethodDefinitionKind::Async,\n                        start_linear_pos,\n                    ),\n                ));\n            }\n            _ => {}\n        }\n\n        let token = cursor.peek(0, interner).or_abrupt()?;\n        let start_linear_pos = token.linear_span().start();\n\n        if token.kind() == &TokenKind::Punctuator(Punctuator::Mul) {\n            let position = cursor.peek(0, interner).or_abrupt()?.span().start();\n            let (class_element_name, params, body) =\n                GeneratorMethod::new(self.allow_yield, self.allow_await).parse(cursor, interner)?;\n\n            let ClassElementNameNode::PropertyName(property_name) = class_element_name else {\n                return Err(Error::general(\n                    \"private identifier not allowed in object literal\",\n                    position,\n                ));\n            };\n\n            // Early Error: It is a Syntax Error if HasDirectSuper of MethodDefinition is true.\n            if has_direct_super_new(&params, &body) {\n                return Err(Error::lex(LexError::Syntax(\n                    \"invalid super call usage\".into(),\n                    position,\n                )));\n            }\n\n            return Ok(PropertyDefinitionNode::MethodDefinition(\n                ObjectMethodDefinition::new(\n                    property_name,\n                    params,\n                    body,\n                    MethodDefinitionKind::Generator,\n                    start_linear_pos,\n                ),\n            ));\n        }\n\n        let set_or_get_escaped_position = match token.kind() {\n            TokenKind::IdentifierName((Sym::GET | Sym::SET, ContainsEscapeSequence(true))) => {\n                Some(token.span().start())\n            }\n            _ => None,\n        };\n\n        let mut property_name =\n            PropertyName::new(self.allow_yield, self.allow_await).parse(cursor, interner)?;\n\n        //  PropertyName[?Yield, ?Await] : AssignmentExpression[+In, ?Yield, ?Await]\n        if cursor.next_if(Punctuator::Colon, interner)?.is_some() {\n            let mut value = AssignmentExpression::new(true, self.allow_yield, self.allow_await)\n                .parse(cursor, interner)?;\n\n            if let Some(name) = property_name.literal()\n                && name != Sym::__PROTO__\n            {\n                value.set_anonymous_function_definition_name(&name);\n            }\n\n            return Ok(PropertyDefinitionNode::Property(property_name, value));\n        }\n\n        let ordinary_method = cursor.peek(0, interner).or_abrupt()?.kind()\n            == &TokenKind::Punctuator(Punctuator::OpenParen);\n\n        match property_name {\n            // MethodDefinition[?Yield, ?Await] -> get ClassElementName[?Yield, ?Await] ( ) { FunctionBody[~Yield, ~Await] }\n            PropertyNameNode::Literal(str) if str == Sym::GET && !ordinary_method => {\n                if let Some(position) = set_or_get_escaped_position {\n                    return Err(Error::general(\n                        \"Keyword must not contain escaped characters\",\n                        position,\n                    ));\n                }\n\n                let position = cursor.peek(0, interner).or_abrupt()?.span().start();\n\n                property_name = PropertyName::new(self.allow_yield, self.allow_await)\n                    .parse(cursor, interner)?;\n\n                cursor.expect(\n                    TokenKind::Punctuator(Punctuator::OpenParen),\n                    \"get method definition\",\n                    interner,\n                )?;\n                cursor.expect(\n                    TokenKind::Punctuator(Punctuator::CloseParen),\n                    \"get method definition\",\n                    interner,\n                )?;\n\n                let body = FunctionBody::new(false, false, \"get method definition\")\n                    .parse(cursor, interner)?;\n\n                // Early Error: It is a Syntax Error if HasDirectSuper of MethodDefinition is true.\n                if has_direct_super_new(&FormalParameterList::default(), &body) {\n                    return Err(Error::lex(LexError::Syntax(\n                        \"invalid super call usage\".into(),\n                        position,\n                    )));\n                }\n\n                Ok(PropertyDefinitionNode::MethodDefinition(\n                    ObjectMethodDefinition::new(\n                        property_name,\n                        FormalParameterList::default(),\n                        body,\n                        MethodDefinitionKind::Get,\n                        start_linear_pos,\n                    ),\n                ))\n            }\n            // MethodDefinition[?Yield, ?Await] -> set ClassElementName[?Yield, ?Await] ( PropertySetParameterList ) { FunctionBody[~Yield, ~Await] }\n            PropertyNameNode::Literal(str) if str == Sym::SET && !ordinary_method => {\n                if let Some(position) = set_or_get_escaped_position {\n                    return Err(Error::general(\n                        \"Keyword must not contain escaped characters\",\n                        position,\n                    ));\n                }\n\n                property_name = PropertyName::new(self.allow_yield, self.allow_await)\n                    .parse(cursor, interner)?;\n\n                let params_start_position = cursor\n                    .expect(\n                        TokenKind::Punctuator(Punctuator::OpenParen),\n                        \"set method definition\",\n                        interner,\n                    )?\n                    .span()\n                    .end();\n                let params: FormalParameterList = FormalParameter::new(false, false)\n                    .parse(cursor, interner)?\n                    .into();\n                cursor.expect(\n                    TokenKind::Punctuator(Punctuator::CloseParen),\n                    \"set method definition\",\n                    interner,\n                )?;\n\n                let body = FunctionBody::new(false, false, \"set method definition\")\n                    .parse(cursor, interner)?;\n\n                // Catch early error for BindingIdentifier.\n                if body.strict() && contains(&params, ContainsSymbol::EvalOrArguments) {\n                    return Err(Error::lex(LexError::Syntax(\n                        \"unexpected identifier 'eval' or 'arguments' in strict mode\".into(),\n                        params_start_position,\n                    )));\n                }\n\n                // It is a Syntax Error if FunctionBodyContainsUseStrict of FunctionBody is true\n                // and IsSimpleParameterList of PropertySetParameterList is false.\n                // https://tc39.es/ecma262/#sec-method-definitions-static-semantics-early-errors\n                if body.strict() && !params.is_simple() {\n                    return Err(Error::lex(LexError::Syntax(\n                        \"Illegal 'use strict' directive in function with non-simple parameter list\"\n                            .into(),\n                        params_start_position,\n                    )));\n                }\n\n                // It is a Syntax Error if any element of the BoundNames of PropertySetParameterList also\n                // occurs in the LexicallyDeclaredNames of FunctionBody.\n                // https://tc39.es/ecma262/#sec-method-definitions-static-semantics-early-errors\n                name_in_lexically_declared_names(\n                    &bound_names(&params),\n                    &lexically_declared_names(&body),\n                    params_start_position,\n                    interner,\n                )?;\n\n                // Early Error: It is a Syntax Error if HasDirectSuper of MethodDefinition is true.\n                if has_direct_super_new(&params, &body) {\n                    return Err(Error::lex(LexError::Syntax(\n                        \"invalid super call usage\".into(),\n                        params_start_position,\n                    )));\n                }\n\n                Ok(PropertyDefinitionNode::MethodDefinition(\n                    ObjectMethodDefinition::new(\n                        property_name,\n                        params,\n                        body,\n                        MethodDefinitionKind::Set,\n                        start_linear_pos,\n                    ),\n                ))\n            }\n            // MethodDefinition[?Yield, ?Await] -> ClassElementName[?Yield, ?Await] ( UniqueFormalParameters[~Yield, ~Await] ) { FunctionBody[~Yield, ~Await] }\n            _ => {\n                let params_start_position = cursor\n                    .expect(\n                        TokenKind::Punctuator(Punctuator::OpenParen),\n                        \"method definition\",\n                        interner,\n                    )?\n                    .span()\n                    .end();\n                let params = FormalParameters::new(false, false).parse(cursor, interner)?;\n                cursor.expect(\n                    TokenKind::Punctuator(Punctuator::CloseParen),\n                    \"method definition\",\n                    interner,\n                )?;\n\n                // Early Error: UniqueFormalParameters : FormalParameters\n                if params.has_duplicates() {\n                    return Err(Error::lex(LexError::Syntax(\n                        \"Duplicate parameter name not allowed in this context\".into(),\n                        params_start_position,\n                    )));\n                }\n\n                let body =\n                    FunctionBody::new(false, false, \"method definition\").parse(cursor, interner)?;\n\n                // Early Error: It is a Syntax Error if FunctionBodyContainsUseStrict of FunctionBody is true\n                // and IsSimpleParameterList of UniqueFormalParameters is false.\n                if body.strict() && !params.is_simple() {\n                    return Err(Error::lex(LexError::Syntax(\n                        \"Illegal 'use strict' directive in function with non-simple parameter list\"\n                            .into(),\n                        params_start_position,\n                    )));\n                }\n\n                // It is a Syntax Error if any element of the BoundNames of FormalParameters also occurs in the\n                // LexicallyDeclaredNames of FunctionBody.\n                name_in_lexically_declared_names(\n                    &bound_names(&params),\n                    &lexically_declared_names(&body),\n                    params_start_position,\n                    interner,\n                )?;\n\n                // Early Error: It is a Syntax Error if HasDirectSuper of MethodDefinition is true.\n                if has_direct_super_new(&params, &body) {\n                    return Err(Error::lex(LexError::Syntax(\n                        \"invalid super call usage\".into(),\n                        params_start_position,\n                    )));\n                }\n\n                Ok(PropertyDefinitionNode::MethodDefinition(\n                    ObjectMethodDefinition::new(\n                        property_name,\n                        params,\n                        body,\n                        MethodDefinitionKind::Ordinary,\n                        start_linear_pos,\n                    ),\n                ))\n            }\n        }\n    }\n}\n\n/// Parses a property name.\n///\n/// More information:\n///  - [ECMAScript specification][spec]\n///\n/// [spec]: https://tc39.es/ecma262/#prod-PropertyName\n#[derive(Debug, Clone)]\npub(in crate::parser) struct PropertyName {\n    allow_yield: AllowYield,\n    allow_await: AllowAwait,\n}\n\nimpl PropertyName {\n    /// Creates a new `PropertyName` parser.\n    pub(in crate::parser) fn new<Y, A>(allow_yield: Y, allow_await: A) -> Self\n    where\n        Y: Into<AllowYield>,\n        A: Into<AllowAwait>,\n    {\n        Self {\n            allow_yield: allow_yield.into(),\n            allow_await: allow_await.into(),\n        }\n    }\n}\n\nimpl<R> TokenParser<R> for PropertyName\nwhere\n    R: ReadChar,\n{\n    type Output = PropertyNameNode;\n\n    fn parse(self, cursor: &mut Cursor<R>, interner: &mut Interner) -> ParseResult<Self::Output> {\n        let token = cursor.peek(0, interner).or_abrupt()?;\n        let name: PropertyNameNode = match token.kind() {\n            TokenKind::Punctuator(Punctuator::OpenBracket) => {\n                cursor.advance(interner);\n                let node = AssignmentExpression::new(true, self.allow_yield, self.allow_await)\n                    .parse(cursor, interner)?;\n                cursor.expect(Punctuator::CloseBracket, \"expected token ']'\", interner)?;\n                return Ok(node.into());\n            }\n            TokenKind::IdentifierName((name, _)) | TokenKind::StringLiteral((name, _)) => {\n                Identifier::new(*name, token.span()).into()\n            }\n            TokenKind::NumericLiteral(num) => match num {\n                Numeric::Rational(num) => {\n                    Expression::Literal(Literal::new(*num, token.span())).into()\n                }\n                Numeric::Integer(num) => {\n                    Expression::Literal(Literal::new(*num, token.span())).into()\n                }\n                Numeric::BigInt(num) => {\n                    Expression::Literal(Literal::new(num.clone(), token.span())).into()\n                }\n            },\n            TokenKind::Keyword((word, _)) => {\n                let (utf8, utf16) = word.as_str();\n                let sym = interner.get_or_intern_static(utf8, utf16);\n                Identifier::new(sym, token.span()).into()\n            }\n            TokenKind::NullLiteral(_) => Identifier::new(Sym::NULL, token.span()).into(),\n            TokenKind::BooleanLiteral((bool, _)) => match bool {\n                true => Identifier::new(Sym::TRUE, token.span()).into(),\n                false => Identifier::new(Sym::FALSE, token.span()).into(),\n            },\n            _ => {\n                return Err(Error::expected(\n                    vec![\"property name\".to_owned()],\n                    token.to_string(interner),\n                    token.span(),\n                    \"property name\",\n                ));\n            }\n        };\n        cursor.advance(interner);\n        Ok(name)\n    }\n}\n\n/// `ClassElementName` can be either a property name or a private identifier.\n///\n/// More information:\n///  - [ECMAScript reference][spec]\n///\n/// [spec]: https://tc39.es/ecma262/#prod-ClassElementName\n#[derive(Debug, Clone)]\npub(in crate::parser) struct ClassElementName {\n    allow_yield: AllowYield,\n    allow_await: AllowAwait,\n}\n\nimpl ClassElementName {\n    /// Creates a new `ClassElementName` parser.\n    pub(in crate::parser) fn new<Y, A>(allow_yield: Y, allow_await: A) -> Self\n    where\n        Y: Into<AllowYield>,\n        A: Into<AllowAwait>,\n    {\n        Self {\n            allow_yield: allow_yield.into(),\n            allow_await: allow_await.into(),\n        }\n    }\n}\n\nimpl<R> TokenParser<R> for ClassElementName\nwhere\n    R: ReadChar,\n{\n    type Output = ClassElementNameNode;\n\n    fn parse(self, cursor: &mut Cursor<R>, interner: &mut Interner) -> ParseResult<Self::Output> {\n        let token = cursor.peek(0, interner).or_abrupt()?;\n        match token.kind() {\n            TokenKind::PrivateIdentifier(ident) => {\n                let ident = *ident;\n                let span = token.span();\n                cursor.advance(interner);\n                Ok(ClassElementNameNode::PrivateName(PrivateName::new(\n                    ident, span,\n                )))\n            }\n            _ => Ok(ClassElementNameNode::PropertyName(\n                PropertyName::new(self.allow_yield, self.allow_await).parse(cursor, interner)?,\n            )),\n        }\n    }\n}\n\n/// Initializer parsing.\n///\n/// More information:\n///  - [ECMAScript specification][spec]\n///\n/// [spec]: https://tc39.es/ecma262/#prod-Initializer\n#[derive(Debug, Clone, Copy)]\npub(in crate::parser) struct Initializer {\n    allow_in: AllowIn,\n    allow_yield: AllowYield,\n    allow_await: AllowAwait,\n}\n\nimpl Initializer {\n    /// Creates a new `Initializer` parser.\n    pub(in crate::parser) fn new<I, Y, A>(allow_in: I, allow_yield: Y, allow_await: A) -> Self\n    where\n        I: Into<AllowIn>,\n        Y: Into<AllowYield>,\n        A: Into<AllowAwait>,\n    {\n        Self {\n            allow_in: allow_in.into(),\n            allow_yield: allow_yield.into(),\n            allow_await: allow_await.into(),\n        }\n    }\n}\n\nimpl<R> TokenParser<R> for Initializer\nwhere\n    R: ReadChar,\n{\n    type Output = Expression;\n\n    fn parse(self, cursor: &mut Cursor<R>, interner: &mut Interner) -> ParseResult<Self::Output> {\n        cursor.expect(Punctuator::Assign, \"initializer\", interner)?;\n        AssignmentExpression::new(self.allow_in, self.allow_yield, self.allow_await)\n            .parse(cursor, interner)\n    }\n}\n\n/// `GeneratorMethod` parsing.\n///\n/// More information:\n///  - [ECMAScript specification][spec]\n///\n/// [spec]: https://tc39.es/ecma262/#prod-GeneratorMethod\n#[derive(Debug, Clone, Copy)]\npub(in crate::parser) struct GeneratorMethod {\n    allow_yield: AllowYield,\n    allow_await: AllowAwait,\n}\n\nimpl GeneratorMethod {\n    /// Creates a new `GeneratorMethod` parser.\n    pub(in crate::parser) fn new<Y, A>(allow_yield: Y, allow_await: A) -> Self\n    where\n        Y: Into<AllowYield>,\n        A: Into<AllowAwait>,\n    {\n        Self {\n            allow_yield: allow_yield.into(),\n            allow_await: allow_await.into(),\n        }\n    }\n}\n\nimpl<R> TokenParser<R> for GeneratorMethod\nwhere\n    R: ReadChar,\n{\n    type Output = (ClassElementNameNode, FormalParameterList, FunctionBodyAst);\n\n    fn parse(self, cursor: &mut Cursor<R>, interner: &mut Interner) -> ParseResult<Self::Output> {\n        cursor.expect(Punctuator::Mul, \"generator method definition\", interner)?;\n\n        let class_element_name =\n            ClassElementName::new(self.allow_yield, self.allow_await).parse(cursor, interner)?;\n\n        let params_start_position = cursor.peek(0, interner).or_abrupt()?.span().start();\n\n        let params = UniqueFormalParameters::new(true, false).parse(cursor, interner)?;\n\n        // It is a Syntax Error if UniqueFormalParameters Contains YieldExpression is true.\n        if contains(&params, ContainsSymbol::YieldExpression) {\n            return Err(Error::lex(LexError::Syntax(\n                \"yield expression not allowed in generator method definition parameters\".into(),\n                params_start_position,\n            )));\n        }\n\n        let body = FunctionBody::new(true, false, \"generator method definition\")\n            .parse(cursor, interner)?;\n\n        let body_start = body.span().start();\n\n        // Early Error: It is a Syntax Error if FunctionBodyContainsUseStrict of FunctionBody is true\n        // and IsSimpleParameterList of UniqueFormalParameters is false.\n        if body.strict() && !params.is_simple() {\n            return Err(Error::lex(LexError::Syntax(\n                \"Illegal 'use strict' directive in function with non-simple parameter list\".into(),\n                body_start,\n            )));\n        }\n\n        // Early Error: It is a Syntax Error if any element of the BoundNames of UniqueFormalParameters also\n        // occurs in the LexicallyDeclaredNames of GeneratorBody.\n        name_in_lexically_declared_names(\n            &bound_names(&params),\n            &lexically_declared_names(&body),\n            params_start_position,\n            interner,\n        )?;\n\n        // Early Error: It is a Syntax Error if HasDirectSuper of AsyncMethod is true.\n        if has_direct_super_new(&params, &body) {\n            return Err(Error::lex(LexError::Syntax(\n                \"invalid super call usage\".into(),\n                body_start,\n            )));\n        }\n\n        Ok((class_element_name, params, body))\n    }\n}\n\n/// `AsyncGeneratorMethod` parsing.\n///\n/// More information:\n///  - [ECMAScript specification][spec]\n///\n/// [spec]: https://tc39.es/ecma262/#prod-AsyncGeneratorMethod\n#[derive(Debug, Clone, Copy)]\npub(in crate::parser) struct AsyncGeneratorMethod {\n    allow_yield: AllowYield,\n    allow_await: AllowAwait,\n}\n\nimpl AsyncGeneratorMethod {\n    /// Creates a new `AsyncGeneratorMethod` parser.\n    pub(in crate::parser) fn new<Y, A>(allow_yield: Y, allow_await: A) -> Self\n    where\n        Y: Into<AllowYield>,\n        A: Into<AllowAwait>,\n    {\n        Self {\n            allow_yield: allow_yield.into(),\n            allow_await: allow_await.into(),\n        }\n    }\n}\n\nimpl<R> TokenParser<R> for AsyncGeneratorMethod\nwhere\n    R: ReadChar,\n{\n    type Output = (ClassElementNameNode, FormalParameterList, FunctionBodyAst);\n\n    fn parse(self, cursor: &mut Cursor<R>, interner: &mut Interner) -> ParseResult<Self::Output> {\n        cursor.expect(\n            Punctuator::Mul,\n            \"async generator method definition\",\n            interner,\n        )?;\n\n        let name =\n            ClassElementName::new(self.allow_yield, self.allow_await).parse(cursor, interner)?;\n\n        let params_start_position = cursor.peek(0, interner).or_abrupt()?.span().start();\n\n        let params = UniqueFormalParameters::new(true, true).parse(cursor, interner)?;\n\n        // Early Error: It is a Syntax Error if UniqueFormalParameters Contains YieldExpression is true.\n        if contains(&params, ContainsSymbol::YieldExpression) {\n            return Err(Error::lex(LexError::Syntax(\n                \"yield expression not allowed in async generator method definition parameters\"\n                    .into(),\n                params_start_position,\n            )));\n        }\n\n        // Early Error: It is a Syntax Error if UniqueFormalParameters Contains AwaitExpression is true.\n        if contains(&params, ContainsSymbol::AwaitExpression) {\n            return Err(Error::lex(LexError::Syntax(\n                \"await expression not allowed in async generator method definition parameters\"\n                    .into(),\n                params_start_position,\n            )));\n        }\n\n        let body = FunctionBody::new(true, true, \"async generator method definition\")\n            .parse(cursor, interner)?;\n\n        let body_start = body.span().start();\n\n        // Early Error: It is a Syntax Error if FunctionBodyContainsUseStrict of FunctionBody is true\n        // and IsSimpleParameterList of UniqueFormalParameters is false.\n        if body.strict() && !params.is_simple() {\n            return Err(Error::lex(LexError::Syntax(\n                \"Illegal 'use strict' directive in function with non-simple parameter list\".into(),\n                body_start,\n            )));\n        }\n\n        // Early Error: It is a Syntax Error if any element of the BoundNames of UniqueFormalParameters\n        // also occurs in the LexicallyDeclaredNames of AsyncGeneratorBody.\n        name_in_lexically_declared_names(\n            &bound_names(&params),\n            &lexically_declared_names(&body),\n            params_start_position,\n            interner,\n        )?;\n\n        // Early Error: It is a Syntax Error if HasDirectSuper of AsyncMethod is true.\n        if has_direct_super_new(&params, &body) {\n            return Err(Error::lex(LexError::Syntax(\n                \"invalid super call usage\".into(),\n                body_start,\n            )));\n        }\n\n        Ok((name, params, body))\n    }\n}\n\n/// `AsyncMethod` parsing.\n///\n/// More information:\n///  - [ECMAScript specification][spec]\n///\n/// [spec]: https://tc39.es/ecma262/#prod-AsyncMethod\n#[derive(Debug, Clone, Copy)]\npub(in crate::parser) struct AsyncMethod {\n    allow_yield: AllowYield,\n    allow_await: AllowAwait,\n}\n\nimpl AsyncMethod {\n    /// Creates a new `AsyncMethod` parser.\n    pub(in crate::parser) fn new<Y, A>(allow_yield: Y, allow_await: A) -> Self\n    where\n        Y: Into<AllowYield>,\n        A: Into<AllowAwait>,\n    {\n        Self {\n            allow_yield: allow_yield.into(),\n            allow_await: allow_await.into(),\n        }\n    }\n}\n\nimpl<R> TokenParser<R> for AsyncMethod\nwhere\n    R: ReadChar,\n{\n    type Output = (ClassElementNameNode, FormalParameterList, FunctionBodyAst);\n\n    fn parse(self, cursor: &mut Cursor<R>, interner: &mut Interner) -> ParseResult<Self::Output> {\n        let class_element_name =\n            ClassElementName::new(self.allow_yield, self.allow_await).parse(cursor, interner)?;\n\n        let params_start_position = cursor.peek(0, interner).or_abrupt()?.span().start();\n\n        let params = UniqueFormalParameters::new(false, true).parse(cursor, interner)?;\n\n        let body =\n            FunctionBody::new(true, true, \"async method definition\").parse(cursor, interner)?;\n\n        let body_start = body.span().start();\n\n        // Early Error: It is a Syntax Error if FunctionBodyContainsUseStrict of AsyncFunctionBody\n        // is true and IsSimpleParameterList of UniqueFormalParameters is false.\n        if body.strict() && !params.is_simple() {\n            return Err(Error::lex(LexError::Syntax(\n                \"Illegal 'use strict' directive in function with non-simple parameter list\".into(),\n                body_start,\n            )));\n        }\n\n        // Early Error: It is a Syntax Error if any element of the BoundNames of UniqueFormalParameters\n        // also occurs in the LexicallyDeclaredNames of AsyncFunctionBody.\n        name_in_lexically_declared_names(\n            &bound_names(&params),\n            &lexically_declared_names(&body),\n            params_start_position,\n            interner,\n        )?;\n\n        // Early Error: It is a Syntax Error if HasDirectSuper of AsyncMethod is true.\n        if has_direct_super_new(&params, &body) {\n            return Err(Error::lex(LexError::Syntax(\n                \"invalid super call usage\".into(),\n                body_start,\n            )));\n        }\n\n        Ok((class_element_name, params, body))\n    }\n}\n\n/// `CoverInitializedName` parsing.\n///\n/// More information:\n///  - [ECMAScript specification][spec]\n///\n/// [spec]: https://tc39.es/ecma262/#prod-CoverInitializedName\n#[derive(Debug, Clone, Copy)]\npub(in crate::parser) struct CoverInitializedName {\n    allow_yield: AllowYield,\n    allow_await: AllowAwait,\n}\n\nimpl CoverInitializedName {\n    /// Creates a new `CoverInitializedName` parser.\n    pub(in crate::parser) fn new<Y, A>(allow_yield: Y, allow_await: A) -> Self\n    where\n        Y: Into<AllowYield>,\n        A: Into<AllowAwait>,\n    {\n        Self {\n            allow_yield: allow_yield.into(),\n            allow_await: allow_await.into(),\n        }\n    }\n}\n\nimpl<R> TokenParser<R> for CoverInitializedName\nwhere\n    R: ReadChar,\n{\n    type Output = PropertyDefinitionNode;\n\n    fn parse(self, cursor: &mut Cursor<R>, interner: &mut Interner) -> ParseResult<Self::Output> {\n        let ident =\n            IdentifierReference::new(self.allow_yield, self.allow_await).parse(cursor, interner)?;\n\n        cursor.expect(Punctuator::Assign, \"CoverInitializedName\", interner)?;\n\n        let expr = AssignmentExpression::new(true, self.allow_yield, self.allow_await)\n            .parse(cursor, interner)?;\n\n        Ok(PropertyDefinitionNode::CoverInitializedName(ident, expr))\n    }\n}\n"
  },
  {
    "path": "core/parser/src/parser/expression/primary/object_initializer/tests.rs",
    "content": "use crate::parser::tests::{check_invalid_script, check_script_parser};\nuse boa_ast::{\n    Declaration, Span, StatementList,\n    declaration::{LexicalDeclaration, Variable},\n    expression::{\n        Identifier,\n        literal::{Literal, ObjectLiteral, ObjectMethodDefinition, PropertyDefinition},\n    },\n    function::{FormalParameter, FormalParameterList, FormalParameterListFlags, FunctionBody},\n    property::MethodDefinitionKind,\n};\nuse boa_interner::{Interner, Sym};\nuse boa_macros::utf16;\nuse indoc::indoc;\n\nconst PSEUDO_LINEAR_POS: boa_ast::LinearPosition = boa_ast::LinearPosition::new(0);\n\n/// Checks object literal parsing.\n#[test]\nfn check_object_literal() {\n    let interner = &mut Interner::default();\n\n    let object_properties = vec![\n        PropertyDefinition::Property(\n            Identifier::new(\n                interner.get_or_intern_static(\"a\", utf16!(\"a\")),\n                Span::new((2, 5), (2, 6)),\n            )\n            .into(),\n            Literal::new(true, Span::new((2, 8), (2, 12))).into(),\n        ),\n        PropertyDefinition::Property(\n            Identifier::new(\n                interner.get_or_intern_static(\"b\", utf16!(\"b\")),\n                Span::new((3, 5), (3, 6)),\n            )\n            .into(),\n            Literal::new(false, Span::new((3, 8), (3, 13))).into(),\n        ),\n    ];\n\n    check_script_parser(\n        indoc! {\"\n            const x = {\n                a: true,\n                b: false,\n            };\n        \"},\n        vec![\n            Declaration::Lexical(LexicalDeclaration::Const(\n                vec![Variable::from_identifier(\n                    Identifier::new(\n                        interner.get_or_intern_static(\"x\", utf16!(\"x\")),\n                        Span::new((1, 7), (1, 8)),\n                    ),\n                    Some(ObjectLiteral::new(object_properties, Span::new((1, 11), (4, 2))).into()),\n                )]\n                .try_into()\n                .unwrap(),\n            ))\n            .into(),\n        ],\n        interner,\n    );\n}\n\n/// Tests short function syntax.\n#[test]\nfn check_object_short_function() {\n    let interner = &mut Interner::default();\n\n    let object_properties = vec![\n        PropertyDefinition::Property(\n            Identifier::new(\n                interner.get_or_intern_static(\"a\", utf16!(\"a\")),\n                Span::new((2, 5), (2, 6)),\n            )\n            .into(),\n            Literal::new(true, Span::new((2, 8), (2, 12))).into(),\n        ),\n        PropertyDefinition::MethodDefinition(ObjectMethodDefinition::new(\n            Identifier::new(\n                interner.get_or_intern_static(\"b\", utf16!(\"b\")),\n                Span::new((3, 5), (3, 6)),\n            )\n            .into(),\n            FormalParameterList::default(),\n            FunctionBody::new(StatementList::default(), Span::new((3, 9), (3, 11))),\n            MethodDefinitionKind::Ordinary,\n            PSEUDO_LINEAR_POS,\n        )),\n    ];\n\n    check_script_parser(\n        indoc! {\"\n            const x = {\n                a: true,\n                b() {},\n            };\n        \"},\n        vec![\n            Declaration::Lexical(LexicalDeclaration::Const(\n                vec![Variable::from_identifier(\n                    Identifier::new(\n                        interner.get_or_intern_static(\"x\", utf16!(\"x\")),\n                        Span::new((1, 7), (1, 8)),\n                    ),\n                    Some(ObjectLiteral::new(object_properties, Span::new((1, 11), (4, 2))).into()),\n                )]\n                .try_into()\n                .unwrap(),\n            ))\n            .into(),\n        ],\n        interner,\n    );\n}\n\n/// Testing short function syntax with arguments.\n#[test]\nfn check_object_short_function_arguments() {\n    let interner = &mut Interner::default();\n\n    let parameters = FormalParameterList::from(FormalParameter::new(\n        Variable::from_identifier(\n            Identifier::new(\n                interner.get_or_intern_static(\"test\", utf16!(\"test\")),\n                Span::new((3, 7), (3, 11)),\n            ),\n            None,\n        ),\n        false,\n    ));\n\n    assert_eq!(parameters.flags(), FormalParameterListFlags::default());\n    assert_eq!(parameters.length(), 1);\n\n    let object_properties = vec![\n        PropertyDefinition::Property(\n            Identifier::new(\n                interner.get_or_intern_static(\"a\", utf16!(\"a\")),\n                Span::new((2, 5), (2, 6)),\n            )\n            .into(),\n            Literal::new(true, Span::new((2, 8), (2, 12))).into(),\n        ),\n        PropertyDefinition::MethodDefinition(ObjectMethodDefinition::new(\n            Identifier::new(\n                interner.get_or_intern_static(\"b\", utf16!(\"b\")),\n                Span::new((3, 5), (3, 6)),\n            )\n            .into(),\n            parameters,\n            FunctionBody::new(StatementList::default(), Span::new((3, 13), (3, 15))),\n            MethodDefinitionKind::Ordinary,\n            PSEUDO_LINEAR_POS,\n        )),\n    ];\n\n    check_script_parser(\n        indoc! {\"\n            const x = {\n                a: true,\n                b(test) {}\n            };\n        \"},\n        vec![\n            Declaration::Lexical(LexicalDeclaration::Const(\n                vec![Variable::from_identifier(\n                    Identifier::new(\n                        interner.get_or_intern_static(\"x\", utf16!(\"x\")),\n                        Span::new((1, 7), (1, 8)),\n                    ),\n                    Some(ObjectLiteral::new(object_properties, Span::new((1, 11), (4, 2))).into()),\n                )]\n                .try_into()\n                .unwrap(),\n            ))\n            .into(),\n        ],\n        interner,\n    );\n}\n\n#[test]\nfn check_object_getter() {\n    let interner = &mut Interner::default();\n\n    let object_properties = vec![\n        PropertyDefinition::Property(\n            Identifier::new(\n                interner.get_or_intern_static(\"a\", utf16!(\"a\")),\n                Span::new((2, 5), (2, 6)),\n            )\n            .into(),\n            Literal::new(true, Span::new((2, 8), (2, 12))).into(),\n        ),\n        PropertyDefinition::MethodDefinition(ObjectMethodDefinition::new(\n            Identifier::new(\n                interner.get_or_intern_static(\"b\", utf16!(\"b\")),\n                Span::new((3, 9), (3, 10)),\n            )\n            .into(),\n            FormalParameterList::default(),\n            FunctionBody::new(StatementList::default(), Span::new((3, 13), (3, 15))),\n            MethodDefinitionKind::Get,\n            PSEUDO_LINEAR_POS,\n        )),\n    ];\n\n    check_script_parser(\n        indoc! {\"\n            const x = {\n                a: true,\n                get b() {}\n            };\n        \"},\n        vec![\n            Declaration::Lexical(LexicalDeclaration::Const(\n                vec![Variable::from_identifier(\n                    Identifier::new(\n                        interner.get_or_intern_static(\"x\", utf16!(\"x\")),\n                        Span::new((1, 7), (1, 8)),\n                    ),\n                    Some(ObjectLiteral::new(object_properties, Span::new((1, 11), (4, 2))).into()),\n                )]\n                .try_into()\n                .unwrap(),\n            ))\n            .into(),\n        ],\n        interner,\n    );\n}\n\n#[test]\nfn check_object_setter() {\n    let interner = &mut Interner::default();\n\n    let params = FormalParameterList::from(FormalParameter::new(\n        Variable::from_identifier(\n            Identifier::new(\n                interner.get_or_intern_static(\"test\", utf16!(\"test\")),\n                Span::new((3, 11), (3, 15)),\n            ),\n            None,\n        ),\n        false,\n    ));\n\n    assert_eq!(params.flags(), FormalParameterListFlags::default());\n    assert_eq!(params.length(), 1);\n\n    let object_properties = vec![\n        PropertyDefinition::Property(\n            Identifier::new(\n                interner.get_or_intern_static(\"a\", utf16!(\"a\")),\n                Span::new((2, 5), (2, 6)),\n            )\n            .into(),\n            Literal::new(true, Span::new((2, 8), (2, 12))).into(),\n        ),\n        PropertyDefinition::MethodDefinition(ObjectMethodDefinition::new(\n            Identifier::new(\n                interner.get_or_intern_static(\"b\", utf16!(\"b\")),\n                Span::new((3, 9), (3, 10)),\n            )\n            .into(),\n            params,\n            FunctionBody::new(StatementList::default(), Span::new((3, 17), (3, 19))),\n            MethodDefinitionKind::Set,\n            PSEUDO_LINEAR_POS,\n        )),\n    ];\n\n    check_script_parser(\n        indoc! {\"\n            const x = {\n                a: true,\n                set b(test) {}\n            };\n        \"},\n        vec![\n            Declaration::Lexical(LexicalDeclaration::Const(\n                vec![Variable::from_identifier(\n                    Identifier::new(\n                        interner.get_or_intern_static(\"x\", utf16!(\"x\")),\n                        Span::new((1, 7), (1, 8)),\n                    ),\n                    Some(ObjectLiteral::new(object_properties, Span::new((1, 11), (4, 2))).into()),\n                )]\n                .try_into()\n                .unwrap(),\n            ))\n            .into(),\n        ],\n        interner,\n    );\n}\n\n#[test]\nfn check_object_short_function_get() {\n    let interner = &mut Interner::default();\n\n    let object_properties = vec![PropertyDefinition::MethodDefinition(\n        ObjectMethodDefinition::new(\n            Identifier::new(Sym::GET, Span::new((2, 5), (2, 8))).into(),\n            FormalParameterList::default(),\n            FunctionBody::new(StatementList::default(), Span::new((2, 11), (2, 13))),\n            MethodDefinitionKind::Ordinary,\n            PSEUDO_LINEAR_POS,\n        ),\n    )];\n\n    check_script_parser(\n        indoc! {\"\n            const x = {\n                get() {}\n            };\n        \"},\n        vec![\n            Declaration::Lexical(LexicalDeclaration::Const(\n                vec![Variable::from_identifier(\n                    Identifier::new(\n                        interner.get_or_intern_static(\"x\", utf16!(\"x\")),\n                        Span::new((1, 7), (1, 8)),\n                    ),\n                    Some(ObjectLiteral::new(object_properties, Span::new((1, 11), (3, 2))).into()),\n                )]\n                .try_into()\n                .unwrap(),\n            ))\n            .into(),\n        ],\n        interner,\n    );\n}\n\n#[test]\nfn check_object_short_function_set() {\n    let interner = &mut Interner::default();\n\n    let object_properties = vec![PropertyDefinition::MethodDefinition(\n        ObjectMethodDefinition::new(\n            Identifier::new(Sym::SET, Span::new((2, 5), (2, 8))).into(),\n            FormalParameterList::default(),\n            FunctionBody::new(StatementList::default(), Span::new((2, 11), (2, 13))),\n            MethodDefinitionKind::Ordinary,\n            PSEUDO_LINEAR_POS,\n        ),\n    )];\n\n    check_script_parser(\n        indoc! {\"\n            const x = {\n                set() {}\n            };\n        \"},\n        vec![\n            Declaration::Lexical(LexicalDeclaration::Const(\n                vec![Variable::from_identifier(\n                    Identifier::new(\n                        interner.get_or_intern_static(\"x\", utf16!(\"x\")),\n                        Span::new((1, 7), (1, 8)),\n                    ),\n                    Some(ObjectLiteral::new(object_properties, Span::new((1, 11), (3, 2))).into()),\n                )]\n                .try_into()\n                .unwrap(),\n            ))\n            .into(),\n        ],\n        interner,\n    );\n}\n\n#[test]\nfn check_object_shorthand_property_names() {\n    let interner = &mut Interner::default();\n\n    let object_properties = vec![PropertyDefinition::IdentifierReference(Identifier::new(\n        interner.get_or_intern_static(\"a\", utf16!(\"a\")),\n        Span::new((2, 13), (2, 14)),\n    ))];\n\n    check_script_parser(\n        indoc! {\"\n            const a = true;\n            const x = { a };\n        \"},\n        vec![\n            Declaration::Lexical(LexicalDeclaration::Const(\n                vec![Variable::from_identifier(\n                    Identifier::new(\n                        interner.get_or_intern_static(\"a\", utf16!(\"a\")),\n                        Span::new((1, 7), (1, 8)),\n                    ),\n                    Some(Literal::new(true, Span::new((1, 11), (1, 15))).into()),\n                )]\n                .try_into()\n                .unwrap(),\n            ))\n            .into(),\n            Declaration::Lexical(LexicalDeclaration::Const(\n                vec![Variable::from_identifier(\n                    Identifier::new(\n                        interner.get_or_intern_static(\"x\", utf16!(\"x\")),\n                        Span::new((2, 7), (2, 8)),\n                    ),\n                    Some(ObjectLiteral::new(object_properties, Span::new((2, 11), (2, 16))).into()),\n                )]\n                .try_into()\n                .unwrap(),\n            ))\n            .into(),\n        ],\n        interner,\n    );\n}\n\n#[test]\nfn check_object_shorthand_multiple_properties() {\n    let interner = &mut Interner::default();\n\n    let object_properties = vec![\n        PropertyDefinition::IdentifierReference(Identifier::new(\n            interner.get_or_intern_static(\"a\", utf16!(\"a\")),\n            Span::new((3, 13), (3, 14)),\n        )),\n        PropertyDefinition::IdentifierReference(Identifier::new(\n            interner.get_or_intern_static(\"b\", utf16!(\"b\")),\n            Span::new((3, 16), (3, 17)),\n        )),\n    ];\n\n    check_script_parser(\n        indoc! {\"\n            const a = true;\n            const b = false;\n            const x = { a, b, };\n        \"},\n        vec![\n            Declaration::Lexical(LexicalDeclaration::Const(\n                vec![Variable::from_identifier(\n                    Identifier::new(\n                        interner.get_or_intern_static(\"a\", utf16!(\"a\")),\n                        Span::new((1, 7), (1, 8)),\n                    ),\n                    Some(Literal::new(true, Span::new((1, 11), (1, 15))).into()),\n                )]\n                .try_into()\n                .unwrap(),\n            ))\n            .into(),\n            Declaration::Lexical(LexicalDeclaration::Const(\n                vec![Variable::from_identifier(\n                    Identifier::new(\n                        interner.get_or_intern_static(\"b\", utf16!(\"b\")),\n                        Span::new((2, 7), (2, 8)),\n                    ),\n                    Some(Literal::new(false, Span::new((2, 11), (2, 16))).into()),\n                )]\n                .try_into()\n                .unwrap(),\n            ))\n            .into(),\n            Declaration::Lexical(LexicalDeclaration::Const(\n                vec![Variable::from_identifier(\n                    Identifier::new(\n                        interner.get_or_intern_static(\"x\", utf16!(\"x\")),\n                        Span::new((3, 7), (3, 8)),\n                    ),\n                    Some(ObjectLiteral::new(object_properties, Span::new((3, 11), (3, 20))).into()),\n                )]\n                .try_into()\n                .unwrap(),\n            ))\n            .into(),\n        ],\n        interner,\n    );\n}\n\n#[test]\nfn check_object_spread() {\n    let interner = &mut Interner::default();\n\n    let object_properties = vec![\n        PropertyDefinition::Property(\n            Identifier::new(\n                interner.get_or_intern_static(\"a\", utf16!(\"a\")),\n                Span::new((1, 13), (1, 14)),\n            )\n            .into(),\n            Literal::new(1, Span::new((1, 16), (1, 17))).into(),\n        ),\n        PropertyDefinition::SpreadObject(\n            Identifier::new(\n                interner.get_or_intern_static(\"b\", utf16!(\"b\")),\n                Span::new((1, 22), (1, 23)),\n            )\n            .into(),\n        ),\n    ];\n\n    check_script_parser(\n        \"const x = { a: 1, ...b };\",\n        vec![\n            Declaration::Lexical(LexicalDeclaration::Const(\n                vec![Variable::from_identifier(\n                    Identifier::new(\n                        interner.get_or_intern_static(\"x\", utf16!(\"x\")),\n                        Span::new((1, 7), (1, 8)),\n                    ),\n                    Some(ObjectLiteral::new(object_properties, Span::new((1, 11), (1, 25))).into()),\n                )]\n                .try_into()\n                .unwrap(),\n            ))\n            .into(),\n        ],\n        interner,\n    );\n}\n\n#[test]\nfn check_async_method() {\n    let interner = &mut Interner::default();\n\n    let object_properties = vec![PropertyDefinition::MethodDefinition(\n        ObjectMethodDefinition::new(\n            Identifier::new(\n                interner.get_or_intern_static(\"dive\", utf16!(\"dive\")),\n                Span::new((2, 11), (2, 15)),\n            )\n            .into(),\n            FormalParameterList::default(),\n            FunctionBody::new(StatementList::default(), Span::new((2, 18), (2, 20))),\n            MethodDefinitionKind::Async,\n            PSEUDO_LINEAR_POS,\n        ),\n    )];\n\n    check_script_parser(\n        indoc! {\"\n            const x = {\n                async dive() {}\n            };\n        \"},\n        vec![\n            Declaration::Lexical(LexicalDeclaration::Const(\n                vec![Variable::from_identifier(\n                    Identifier::new(\n                        interner.get_or_intern_static(\"x\", utf16!(\"x\")),\n                        Span::new((1, 7), (1, 8)),\n                    ),\n                    Some(ObjectLiteral::new(object_properties, Span::new((1, 11), (3, 2))).into()),\n                )]\n                .try_into()\n                .unwrap(),\n            ))\n            .into(),\n        ],\n        interner,\n    );\n}\n\n#[test]\nfn check_async_generator_method() {\n    let interner = &mut Interner::default();\n\n    let object_properties = vec![PropertyDefinition::MethodDefinition(\n        ObjectMethodDefinition::new(\n            Identifier::new(\n                interner.get_or_intern_static(\"vroom\", utf16!(\"vroom\")),\n                Span::new((2, 12), (2, 17)),\n            )\n            .into(),\n            FormalParameterList::default(),\n            FunctionBody::new(StatementList::default(), Span::new((2, 20), (2, 22))),\n            MethodDefinitionKind::AsyncGenerator,\n            PSEUDO_LINEAR_POS,\n        ),\n    )];\n\n    check_script_parser(\n        indoc! {\"\n            const x = {\n                async* vroom() {}\n            };\n        \"},\n        vec![\n            Declaration::Lexical(LexicalDeclaration::Const(\n                vec![Variable::from_identifier(\n                    Identifier::new(\n                        interner.get_or_intern_static(\"x\", utf16!(\"x\")),\n                        Span::new((1, 7), (1, 8)),\n                    ),\n                    Some(ObjectLiteral::new(object_properties, Span::new((1, 11), (3, 2))).into()),\n                )]\n                .try_into()\n                .unwrap(),\n            ))\n            .into(),\n        ],\n        interner,\n    );\n}\n\n#[test]\nfn check_async_method_lineterminator() {\n    check_invalid_script(\n        \"const x = {\n            async\n            dive(){}\n        };\n        \",\n    );\n}\n\n#[test]\nfn check_async_gen_method_lineterminator() {\n    check_invalid_script(\n        \"const x = {\n            async\n            * vroom() {}\n        };\n        \",\n    );\n}\n\n#[test]\nfn check_async_ordinary_method() {\n    let interner = &mut Interner::default();\n\n    let object_properties = vec![PropertyDefinition::MethodDefinition(\n        ObjectMethodDefinition::new(\n            Identifier::new(\n                interner.get_or_intern_static(\"async\", utf16!(\"async\")),\n                Span::new((2, 5), (2, 10)),\n            )\n            .into(),\n            FormalParameterList::default(),\n            FunctionBody::new(StatementList::default(), Span::new((2, 13), (2, 15))),\n            MethodDefinitionKind::Ordinary,\n            PSEUDO_LINEAR_POS,\n        ),\n    )];\n\n    check_script_parser(\n        indoc! {r#\"\n            const x = {\n                async() {}\n            };\n        \"#},\n        vec![\n            Declaration::Lexical(LexicalDeclaration::Const(\n                vec![Variable::from_identifier(\n                    Identifier::new(\n                        interner.get_or_intern_static(\"x\", utf16!(\"x\")),\n                        Span::new((1, 7), (1, 8)),\n                    ),\n                    Some(ObjectLiteral::new(object_properties, Span::new((1, 11), (3, 2))).into()),\n                )]\n                .try_into()\n                .unwrap(),\n            ))\n            .into(),\n        ],\n        interner,\n    );\n}\n\n#[test]\nfn check_async_property() {\n    let interner = &mut Interner::default();\n\n    let object_properties = vec![PropertyDefinition::Property(\n        Identifier::new(\n            interner.get_or_intern_static(\"async\", utf16!(\"async\")),\n            Span::new((2, 5), (2, 10)),\n        )\n        .into(),\n        Literal::new(true, Span::new((2, 12), (2, 16))).into(),\n    )];\n\n    check_script_parser(\n        indoc! {\"\n            const x = {\n                async: true\n            };\n        \"},\n        vec![\n            Declaration::Lexical(LexicalDeclaration::Const(\n                vec![Variable::from_identifier(\n                    Identifier::new(\n                        interner.get_or_intern_static(\"x\", utf16!(\"x\")),\n                        Span::new((1, 7), (1, 8)),\n                    ),\n                    Some(ObjectLiteral::new(object_properties, Span::new((1, 11), (3, 2))).into()),\n                )]\n                .try_into()\n                .unwrap(),\n            ))\n            .into(),\n        ],\n        interner,\n    );\n}\n"
  },
  {
    "path": "core/parser/src/parser/expression/primary/template/mod.rs",
    "content": "//! Template literal parsing.\n//!\n//! More information:\n//!  - [MDN documentation][mdn]\n//!  - [ECMAScript specification][spec]\n//!\n//! [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Template_literals\n//! [spec]: https://tc39.es/ecma262/#sec-template-literals\n\nuse crate::{\n    Error,\n    lexer::TokenKind,\n    parser::{AllowAwait, AllowYield, Cursor, ParseResult, TokenParser, expression::Expression},\n    source::ReadChar,\n};\nuse boa_ast::{\n    PositionGroup, Punctuator, Span, Spanned,\n    expression::literal::{self, TemplateElement},\n};\nuse boa_interner::{Interner, Sym};\n\n/// Parses a template literal.\n///\n/// More information:\n///  - [MDN documentation][mdn]\n///  - [ECMAScript specification][spec]\n///\n/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Template_literals\n/// [spec]: https://tc39.es/ecma262/#prod-TemplateLiteral\n#[derive(Debug, Clone)]\npub(super) struct TemplateLiteral {\n    allow_yield: AllowYield,\n    allow_await: AllowAwait,\n    start: PositionGroup,\n    first: Sym,\n}\n\nimpl TemplateLiteral {\n    /// Creates a new `TemplateLiteral` parser.\n    pub(super) fn new<Y, A>(\n        allow_yield: Y,\n        allow_await: A,\n        start: PositionGroup,\n        first: Sym,\n    ) -> Self\n    where\n        Y: Into<AllowYield>,\n        A: Into<AllowAwait>,\n    {\n        Self {\n            allow_yield: allow_yield.into(),\n            allow_await: allow_await.into(),\n            start,\n            first,\n        }\n    }\n}\n\nimpl<R> TokenParser<R> for TemplateLiteral\nwhere\n    R: ReadChar,\n{\n    type Output = literal::TemplateLiteral;\n\n    fn parse(self, cursor: &mut Cursor<R>, interner: &mut Interner) -> ParseResult<Self::Output> {\n        let mut elements = vec![\n            TemplateElement::String(self.first),\n            TemplateElement::Expr(\n                Expression::new(true, self.allow_yield, self.allow_await)\n                    .parse(cursor, interner)?,\n            ),\n        ];\n        cursor.expect(\n            TokenKind::Punctuator(Punctuator::CloseBlock),\n            \"template literal\",\n            interner,\n        )?;\n\n        loop {\n            let token = cursor.lex_template(self.start, interner)?;\n            match token.kind() {\n                TokenKind::TemplateMiddle(template_string) => {\n                    let Some(cooked) = template_string.cooked() else {\n                        return Err(Error::general(\n                            \"invalid escape in template literal\",\n                            self.start,\n                        ));\n                    };\n                    elements.push(TemplateElement::String(cooked));\n                    elements.push(TemplateElement::Expr(\n                        Expression::new(true, self.allow_yield, self.allow_await)\n                            .parse(cursor, interner)?,\n                    ));\n                    cursor.expect(\n                        TokenKind::Punctuator(Punctuator::CloseBlock),\n                        \"template literal\",\n                        interner,\n                    )?;\n                }\n                TokenKind::TemplateNoSubstitution(template_string) => {\n                    let Some(cooked) = template_string.cooked() else {\n                        return Err(Error::general(\n                            \"invalid escape in template literal\",\n                            self.start,\n                        ));\n                    };\n                    elements.push(TemplateElement::String(cooked));\n                    return Ok(literal::TemplateLiteral::new(\n                        elements.into(),\n                        Span::new(self.start.position(), token.span().end()),\n                    ));\n                }\n                _ => return Err(Error::general(\"cannot parse template literal\", self.start)),\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "core/parser/src/parser/expression/primary/tests.rs",
    "content": "use crate::parser::tests::{check_invalid_script, check_script_parser};\nuse boa_ast::{\n    Expression, Span, Statement,\n    expression::{\n        Identifier, Parenthesized,\n        literal::{ArrayLiteral, Literal},\n        operator::{\n            Assign,\n            assign::{AssignOp, AssignTarget},\n        },\n    },\n    pattern::{ArrayPattern, ArrayPatternElement, ObjectPattern, ObjectPatternElement},\n};\nuse boa_interner::{Interner, Sym};\nuse boa_macros::utf16;\n\n#[test]\nfn check_string() {\n    // Check empty string\n    check_script_parser(\n        \"\\\"\\\"\",\n        vec![\n            Statement::Expression(\n                Literal::new(Sym::EMPTY_STRING, Span::new((1, 1), (1, 3))).into(),\n            )\n            .into(),\n        ],\n        &mut Interner::default(),\n    );\n\n    // Check non-empty string\n    let interner = &mut Interner::default();\n    check_script_parser(\n        \"\\\"hello\\\"\",\n        vec![\n            Statement::Expression(\n                Literal::new(\n                    interner.get_or_intern_static(\"hello\", utf16!(\"hello\")),\n                    Span::new((1, 1), (1, 8)),\n                )\n                .into(),\n            )\n            .into(),\n        ],\n        interner,\n    );\n}\n\n#[test]\nfn check_destructuring_assignment_object_assignment_operator() {\n    let interner = &mut Interner::default();\n    let a = interner.get_or_intern_static(\"a\", utf16!(\"a\"));\n    check_script_parser(\n        \"({ a: a = 0 } = 0);\",\n        vec![\n            Statement::Expression(\n                Parenthesized::new(\n                    Expression::Assign(Assign::new(\n                        AssignOp::Assign,\n                        AssignTarget::Pattern(\n                            ObjectPattern::new(\n                                vec![ObjectPatternElement::SingleName {\n                                    name: Identifier::new(a, Span::new((1, 4), (1, 5))).into(),\n                                    ident: Identifier::new(a, Span::new((1, 7), (1, 8))),\n                                    default_init: Some(\n                                        Literal::new(0, Span::new((1, 11), (1, 12))).into(),\n                                    ),\n                                }]\n                                .into(),\n                                Span::new((1, 2), (1, 14)),\n                            )\n                            .into(),\n                        ),\n                        Literal::new(0, Span::new((1, 17), (1, 18))).into(),\n                    )),\n                    Span::new((1, 1), (1, 19)),\n                )\n                .into(),\n            )\n            .into(),\n        ],\n        interner,\n    );\n}\n\n#[test]\nfn check_destructuring_assignment_object_invalid_assignment_operators() {\n    check_invalid_script(\"({ a: a &&= 0 } = 0);\");\n    check_invalid_script(\"({ a: a ||= 0 } = 0);\");\n    check_invalid_script(\"({ a: a ??= 0 } = 0);\");\n    check_invalid_script(\"({ a: a *= 0 } = 0);\");\n    check_invalid_script(\"({ a: a /= 0 } = 0);\");\n    check_invalid_script(\"({ a: a %= 0 } = 0);\");\n    check_invalid_script(\"({ a: a += 0 } = 0);\");\n    check_invalid_script(\"({ a: a -= 0 } = 0);\");\n    check_invalid_script(\"({ a: a <<= 0 } = 0);\");\n    check_invalid_script(\"({ a: a >>= 0 } = 0);\");\n    check_invalid_script(\"({ a: a >>>= 0 } = 0);\");\n    check_invalid_script(\"({ a: a &= 0 } = 0);\");\n    check_invalid_script(\"({ a: a ^= 0 } = 0);\");\n    check_invalid_script(\"({ a: a |= 0 } = 0);\");\n    check_invalid_script(\"({ a: a **= 0 } = 0);\");\n}\n\n#[test]\nfn check_destructuring_assignment_array_assignment_operator() {\n    let interner = &mut Interner::default();\n    let a = interner.get_or_intern_static(\"a\", utf16!(\"a\"));\n    check_script_parser(\n        \"([ a = 0 ] = []);\",\n        vec![\n            Statement::Expression(\n                Parenthesized::new(\n                    Expression::Assign(Assign::new(\n                        AssignOp::Assign,\n                        AssignTarget::Pattern(\n                            ArrayPattern::new(\n                                vec![ArrayPatternElement::SingleName {\n                                    ident: Identifier::new(a, Span::new((1, 4), (1, 5))),\n                                    default_init: Some(\n                                        Literal::new(0, Span::new((1, 8), (1, 9))).into(),\n                                    ),\n                                }]\n                                .into(),\n                                Span::new((1, 2), (1, 11)),\n                            )\n                            .into(),\n                        ),\n                        ArrayLiteral::new([], false, Span::new((1, 14), (1, 16))).into(),\n                    )),\n                    Span::new((1, 1), (1, 17)),\n                )\n                .into(),\n            )\n            .into(),\n        ],\n        interner,\n    );\n}\n\n#[test]\nfn check_destructuring_assignment_array_invalid_assignment_operators() {\n    check_invalid_script(\"([ a &&= 0 ] = []);\");\n    check_invalid_script(\"([ a ||= 0 ] = []);\");\n    check_invalid_script(\"([ a ??= 0 ] = []);\");\n    check_invalid_script(\"([ a *= 0 ] = []);\");\n    check_invalid_script(\"([ a /= 0 ] = []);\");\n    check_invalid_script(\"([ a %= 0 ] = []);\");\n    check_invalid_script(\"([ a += 0 ] = []);\");\n    check_invalid_script(\"([ a -= 0 ] = []);\");\n    check_invalid_script(\"([ a <<= 0 ] = []);\");\n    check_invalid_script(\"([ a >>= 0 ] = []);\");\n    check_invalid_script(\"([ a >>>= 0 ] = []);\");\n    check_invalid_script(\"([ a &= 0 ] = []);\");\n    check_invalid_script(\"([ a ^= 0 ] = []);\");\n    check_invalid_script(\"([ a |= 0 ] = []);\");\n    check_invalid_script(\"([ a **= 0 ] = []);\");\n}\n"
  },
  {
    "path": "core/parser/src/parser/expression/tests.rs",
    "content": "use crate::parser::tests::{check_invalid_script, check_script_parser};\nuse boa_ast::{\n    Declaration, Expression, LinearPosition, LinearSpan, Span, Statement, StatementList,\n    declaration::{LexicalDeclaration, Variable},\n    expression::{\n        Call, Identifier, Parenthesized, RegExpLiteral,\n        literal::Literal,\n        operator::{\n            Assign, Binary,\n            assign::AssignOp,\n            binary::{ArithmeticOp, BitwiseOp, LogicalOp, RelationalOp},\n        },\n    },\n    function::{AsyncArrowFunction, FormalParameter, FormalParameterList, FunctionBody},\n};\nuse boa_interner::{Interner, Sym};\nuse boa_macros::utf16;\n\n/// Checks numeric operations\n#[test]\nfn check_numeric_operations() {\n    let interner = &mut Interner::default();\n    check_script_parser(\n        \"a + b\",\n        vec![\n            Statement::Expression(Expression::from(Binary::new(\n                ArithmeticOp::Add.into(),\n                Identifier::new(\n                    interner.get_or_intern_static(\"a\", utf16!(\"a\")),\n                    Span::new((1, 1), (1, 2)),\n                )\n                .into(),\n                Identifier::new(\n                    interner.get_or_intern_static(\"b\", utf16!(\"b\")),\n                    Span::new((1, 5), (1, 6)),\n                )\n                .into(),\n            )))\n            .into(),\n        ],\n        interner,\n    );\n\n    let interner = &mut Interner::default();\n    check_script_parser(\n        \"a+1\",\n        vec![\n            Statement::Expression(Expression::from(Binary::new(\n                ArithmeticOp::Add.into(),\n                Identifier::new(\n                    interner.get_or_intern_static(\"a\", utf16!(\"a\")),\n                    Span::new((1, 1), (1, 2)),\n                )\n                .into(),\n                Literal::new(1, Span::new((1, 3), (1, 4))).into(),\n            )))\n            .into(),\n        ],\n        interner,\n    );\n\n    let interner = &mut Interner::default();\n    check_script_parser(\n        \"a - b\",\n        vec![\n            Statement::Expression(Expression::from(Binary::new(\n                ArithmeticOp::Sub.into(),\n                Identifier::new(\n                    interner.get_or_intern_static(\"a\", utf16!(\"a\")),\n                    Span::new((1, 1), (1, 2)),\n                )\n                .into(),\n                Identifier::new(\n                    interner.get_or_intern_static(\"b\", utf16!(\"b\")),\n                    Span::new((1, 5), (1, 6)),\n                )\n                .into(),\n            )))\n            .into(),\n        ],\n        interner,\n    );\n\n    let interner = &mut Interner::default();\n    check_script_parser(\n        \"a-1\",\n        vec![\n            Statement::Expression(Expression::from(Binary::new(\n                ArithmeticOp::Sub.into(),\n                Identifier::new(\n                    interner.get_or_intern_static(\"a\", utf16!(\"a\")),\n                    Span::new((1, 1), (1, 2)),\n                )\n                .into(),\n                Literal::new(1, Span::new((1, 3), (1, 4))).into(),\n            )))\n            .into(),\n        ],\n        interner,\n    );\n\n    let interner = &mut Interner::default();\n    check_script_parser(\n        \"a / b\",\n        vec![\n            Statement::Expression(Expression::from(Binary::new(\n                ArithmeticOp::Div.into(),\n                Identifier::new(\n                    interner.get_or_intern_static(\"a\", utf16!(\"a\")),\n                    Span::new((1, 1), (1, 2)),\n                )\n                .into(),\n                Identifier::new(\n                    interner.get_or_intern_static(\"b\", utf16!(\"b\")),\n                    Span::new((1, 5), (1, 6)),\n                )\n                .into(),\n            )))\n            .into(),\n        ],\n        interner,\n    );\n\n    let interner = &mut Interner::default();\n    check_script_parser(\n        \"a/2\",\n        vec![\n            Statement::Expression(Expression::from(Binary::new(\n                ArithmeticOp::Div.into(),\n                Identifier::new(\n                    interner.get_or_intern_static(\"a\", utf16!(\"a\")),\n                    Span::new((1, 1), (1, 2)),\n                )\n                .into(),\n                Literal::new(2, Span::new((1, 3), (1, 4))).into(),\n            )))\n            .into(),\n        ],\n        interner,\n    );\n\n    let interner = &mut Interner::default();\n    check_script_parser(\n        \"let myRegex = /=/;\",\n        vec![\n            Declaration::Lexical(LexicalDeclaration::Let(\n                vec![Variable::from_identifier(\n                    Identifier::new(\n                        interner.get_or_intern_static(\"myRegex\", utf16!(\"myRegex\")),\n                        Span::new((1, 5), (1, 12)),\n                    ),\n                    Some(\n                        RegExpLiteral::new(\n                            interner.get_or_intern_static(\"=\", utf16!(\"=\")),\n                            Sym::EMPTY_STRING,\n                            Span::new((1, 15), (1, 18)),\n                        )\n                        .into(),\n                    ),\n                )]\n                .try_into()\n                .unwrap(),\n            ))\n            .into(),\n        ],\n        interner,\n    );\n\n    let interner = &mut Interner::default();\n    check_script_parser(\n        \"fn(/=/);\",\n        vec![\n            Statement::Expression(\n                Call::new(\n                    Identifier::new(\n                        interner.get_or_intern_static(\"fn\", utf16!(\"fn\")),\n                        Span::new((1, 1), (1, 3)),\n                    )\n                    .into(),\n                    vec![\n                        RegExpLiteral::new(\n                            interner.get_or_intern_static(\"=\", utf16!(\"=\")),\n                            Sym::EMPTY_STRING,\n                            Span::new((1, 4), (1, 7)),\n                        )\n                        .into(),\n                    ]\n                    .into(),\n                    Span::new((1, 3), (1, 8)),\n                )\n                .into(),\n            )\n            .into(),\n        ],\n        interner,\n    );\n\n    let interner = &mut Interner::default();\n    check_script_parser(\n        \"fn(a / b);\",\n        vec![\n            Statement::Expression(\n                Call::new(\n                    Identifier::new(\n                        interner.get_or_intern_static(\"fn\", utf16!(\"fn\")),\n                        Span::new((1, 1), (1, 3)),\n                    )\n                    .into(),\n                    vec![Expression::from(Binary::new(\n                        ArithmeticOp::Div.into(),\n                        Identifier::new(\n                            interner.get_or_intern_static(\"a\", utf16!(\"a\")),\n                            Span::new((1, 4), (1, 5)),\n                        )\n                        .into(),\n                        Identifier::new(\n                            interner.get_or_intern_static(\"b\", utf16!(\"b\")),\n                            Span::new((1, 8), (1, 9)),\n                        )\n                        .into(),\n                    ))]\n                    .into(),\n                    Span::new((1, 3), (1, 10)),\n                )\n                .into(),\n            )\n            .into(),\n        ],\n        interner,\n    );\n\n    let interner = &mut Interner::default();\n    check_script_parser(\n        \"fn(a) / b;\",\n        vec![\n            Statement::Expression(Expression::from(Binary::new(\n                ArithmeticOp::Div.into(),\n                Call::new(\n                    Identifier::new(\n                        interner.get_or_intern_static(\"fn\", utf16!(\"fn\")),\n                        Span::new((1, 1), (1, 3)),\n                    )\n                    .into(),\n                    vec![\n                        Identifier::new(\n                            interner.get_or_intern_static(\"a\", utf16!(\"a\")),\n                            Span::new((1, 4), (1, 5)),\n                        )\n                        .into(),\n                    ]\n                    .into(),\n                    Span::new((1, 3), (1, 6)),\n                )\n                .into(),\n                Identifier::new(\n                    interner.get_or_intern_static(\"b\", utf16!(\"b\")),\n                    Span::new((1, 9), (1, 10)),\n                )\n                .into(),\n            )))\n            .into(),\n        ],\n        interner,\n    );\n\n    let interner = &mut Interner::default();\n    check_script_parser(\n        \"a * b\",\n        vec![\n            Statement::Expression(Expression::from(Binary::new(\n                ArithmeticOp::Mul.into(),\n                Identifier::new(\n                    interner.get_or_intern_static(\"a\", utf16!(\"a\")),\n                    Span::new((1, 1), (1, 2)),\n                )\n                .into(),\n                Identifier::new(\n                    interner.get_or_intern_static(\"b\", utf16!(\"b\")),\n                    Span::new((1, 5), (1, 6)),\n                )\n                .into(),\n            )))\n            .into(),\n        ],\n        interner,\n    );\n\n    let interner = &mut Interner::default();\n    check_script_parser(\n        \"a*2\",\n        vec![\n            Statement::Expression(Expression::from(Binary::new(\n                ArithmeticOp::Mul.into(),\n                Identifier::new(\n                    interner.get_or_intern_static(\"a\", utf16!(\"a\")),\n                    Span::new((1, 1), (1, 2)),\n                )\n                .into(),\n                Literal::new(2, Span::new((1, 3), (1, 4))).into(),\n            )))\n            .into(),\n        ],\n        interner,\n    );\n\n    let interner = &mut Interner::default();\n    check_script_parser(\n        \"a ** b\",\n        vec![\n            Statement::Expression(Expression::from(Binary::new(\n                ArithmeticOp::Exp.into(),\n                Identifier::new(\n                    interner.get_or_intern_static(\"a\", utf16!(\"a\")),\n                    Span::new((1, 1), (1, 2)),\n                )\n                .into(),\n                Identifier::new(\n                    interner.get_or_intern_static(\"b\", utf16!(\"b\")),\n                    Span::new((1, 6), (1, 7)),\n                )\n                .into(),\n            )))\n            .into(),\n        ],\n        interner,\n    );\n\n    let interner = &mut Interner::default();\n    check_script_parser(\n        \"a**2\",\n        vec![\n            Statement::Expression(Expression::from(Binary::new(\n                ArithmeticOp::Exp.into(),\n                Identifier::new(\n                    interner.get_or_intern_static(\"a\", utf16!(\"a\")),\n                    Span::new((1, 1), (1, 2)),\n                )\n                .into(),\n                Literal::new(2, Span::new((1, 4), (1, 5))).into(),\n            )))\n            .into(),\n        ],\n        interner,\n    );\n\n    let interner = &mut Interner::default();\n    check_script_parser(\n        \"a % b\",\n        vec![\n            Statement::Expression(Expression::from(Binary::new(\n                ArithmeticOp::Mod.into(),\n                Identifier::new(\n                    interner.get_or_intern_static(\"a\", utf16!(\"a\")),\n                    Span::new((1, 1), (1, 2)),\n                )\n                .into(),\n                Identifier::new(\n                    interner.get_or_intern_static(\"b\", utf16!(\"b\")),\n                    Span::new((1, 5), (1, 6)),\n                )\n                .into(),\n            )))\n            .into(),\n        ],\n        interner,\n    );\n\n    let interner = &mut Interner::default();\n    check_script_parser(\n        \"a%2\",\n        vec![\n            Statement::Expression(Expression::from(Binary::new(\n                ArithmeticOp::Mod.into(),\n                Identifier::new(\n                    interner.get_or_intern_static(\"a\", utf16!(\"a\")),\n                    Span::new((1, 1), (1, 2)),\n                )\n                .into(),\n                Literal::new(2, Span::new((1, 3), (1, 4))).into(),\n            )))\n            .into(),\n        ],\n        interner,\n    );\n}\n\n// Checks complex numeric operations.\n#[test]\nfn check_complex_numeric_operations() {\n    let interner = &mut Interner::default();\n    check_script_parser(\n        \"a + d*(b-3)+1\",\n        vec![\n            Statement::Expression(Expression::from(Binary::new(\n                ArithmeticOp::Add.into(),\n                Binary::new(\n                    ArithmeticOp::Add.into(),\n                    Identifier::new(\n                        interner.get_or_intern_static(\"a\", utf16!(\"a\")),\n                        Span::new((1, 1), (1, 2)),\n                    )\n                    .into(),\n                    Binary::new(\n                        ArithmeticOp::Mul.into(),\n                        Identifier::new(\n                            interner.get_or_intern_static(\"d\", utf16!(\"d\")),\n                            Span::new((1, 5), (1, 6)),\n                        )\n                        .into(),\n                        Parenthesized::new(\n                            Binary::new(\n                                ArithmeticOp::Sub.into(),\n                                Identifier::new(\n                                    interner.get_or_intern_static(\"b\", utf16!(\"b\")),\n                                    Span::new((1, 8), (1, 9)),\n                                )\n                                .into(),\n                                Literal::new(3, Span::new((1, 10), (1, 11))).into(),\n                            )\n                            .into(),\n                            Span::new((1, 7), (1, 12)),\n                        )\n                        .into(),\n                    )\n                    .into(),\n                )\n                .into(),\n                Literal::new(1, Span::new((1, 13), (1, 14))).into(),\n            )))\n            .into(),\n        ],\n        interner,\n    );\n}\n\n/// Checks bitwise operations.\n#[test]\nfn check_bitwise_operations() {\n    let interner = &mut Interner::default();\n    check_script_parser(\n        \"a & b\",\n        vec![\n            Statement::Expression(Expression::from(Binary::new(\n                BitwiseOp::And.into(),\n                Identifier::new(\n                    interner.get_or_intern_static(\"a\", utf16!(\"a\")),\n                    Span::new((1, 1), (1, 2)),\n                )\n                .into(),\n                Identifier::new(\n                    interner.get_or_intern_static(\"b\", utf16!(\"b\")),\n                    Span::new((1, 5), (1, 6)),\n                )\n                .into(),\n            )))\n            .into(),\n        ],\n        interner,\n    );\n\n    let interner = &mut Interner::default();\n    check_script_parser(\n        \"a&b\",\n        vec![\n            Statement::Expression(Expression::from(Binary::new(\n                BitwiseOp::And.into(),\n                Identifier::new(\n                    interner.get_or_intern_static(\"a\", utf16!(\"a\")),\n                    Span::new((1, 1), (1, 2)),\n                )\n                .into(),\n                Identifier::new(\n                    interner.get_or_intern_static(\"b\", utf16!(\"b\")),\n                    Span::new((1, 3), (1, 4)),\n                )\n                .into(),\n            )))\n            .into(),\n        ],\n        interner,\n    );\n\n    let interner = &mut Interner::default();\n    check_script_parser(\n        \"a | b\",\n        vec![\n            Statement::Expression(Expression::from(Binary::new(\n                BitwiseOp::Or.into(),\n                Identifier::new(\n                    interner.get_or_intern_static(\"a\", utf16!(\"a\")),\n                    Span::new((1, 1), (1, 2)),\n                )\n                .into(),\n                Identifier::new(\n                    interner.get_or_intern_static(\"b\", utf16!(\"b\")),\n                    Span::new((1, 5), (1, 6)),\n                )\n                .into(),\n            )))\n            .into(),\n        ],\n        interner,\n    );\n\n    let interner = &mut Interner::default();\n    check_script_parser(\n        \"a|b\",\n        vec![\n            Statement::Expression(Expression::from(Binary::new(\n                BitwiseOp::Or.into(),\n                Identifier::new(\n                    interner.get_or_intern_static(\"a\", utf16!(\"a\")),\n                    Span::new((1, 1), (1, 2)),\n                )\n                .into(),\n                Identifier::new(\n                    interner.get_or_intern_static(\"b\", utf16!(\"b\")),\n                    Span::new((1, 3), (1, 4)),\n                )\n                .into(),\n            )))\n            .into(),\n        ],\n        interner,\n    );\n\n    let interner = &mut Interner::default();\n    check_script_parser(\n        \"a ^ b\",\n        vec![\n            Statement::Expression(Expression::from(Binary::new(\n                BitwiseOp::Xor.into(),\n                Identifier::new(\n                    interner.get_or_intern_static(\"a\", utf16!(\"a\")),\n                    Span::new((1, 1), (1, 2)),\n                )\n                .into(),\n                Identifier::new(\n                    interner.get_or_intern_static(\"b\", utf16!(\"b\")),\n                    Span::new((1, 5), (1, 6)),\n                )\n                .into(),\n            )))\n            .into(),\n        ],\n        interner,\n    );\n\n    let interner = &mut Interner::default();\n    check_script_parser(\n        \"a^b\",\n        vec![\n            Statement::Expression(Expression::from(Binary::new(\n                BitwiseOp::Xor.into(),\n                Identifier::new(\n                    interner.get_or_intern_static(\"a\", utf16!(\"a\")),\n                    Span::new((1, 1), (1, 2)),\n                )\n                .into(),\n                Identifier::new(\n                    interner.get_or_intern_static(\"b\", utf16!(\"b\")),\n                    Span::new((1, 3), (1, 4)),\n                )\n                .into(),\n            )))\n            .into(),\n        ],\n        interner,\n    );\n\n    let interner = &mut Interner::default();\n    check_script_parser(\n        \"a << b\",\n        vec![\n            Statement::Expression(Expression::from(Binary::new(\n                BitwiseOp::Shl.into(),\n                Identifier::new(\n                    interner.get_or_intern_static(\"a\", utf16!(\"a\")),\n                    Span::new((1, 1), (1, 2)),\n                )\n                .into(),\n                Identifier::new(\n                    interner.get_or_intern_static(\"b\", utf16!(\"b\")),\n                    Span::new((1, 6), (1, 7)),\n                )\n                .into(),\n            )))\n            .into(),\n        ],\n        interner,\n    );\n\n    let interner = &mut Interner::default();\n    check_script_parser(\n        \"a<<b\",\n        vec![\n            Statement::Expression(Expression::from(Binary::new(\n                BitwiseOp::Shl.into(),\n                Identifier::new(\n                    interner.get_or_intern_static(\"a\", utf16!(\"a\")),\n                    Span::new((1, 1), (1, 2)),\n                )\n                .into(),\n                Identifier::new(\n                    interner.get_or_intern_static(\"b\", utf16!(\"b\")),\n                    Span::new((1, 4), (1, 5)),\n                )\n                .into(),\n            )))\n            .into(),\n        ],\n        interner,\n    );\n\n    let interner = &mut Interner::default();\n    check_script_parser(\n        \"a >> b\",\n        vec![\n            Statement::Expression(Expression::from(Binary::new(\n                BitwiseOp::Shr.into(),\n                Identifier::new(\n                    interner.get_or_intern_static(\"a\", utf16!(\"a\")),\n                    Span::new((1, 1), (1, 2)),\n                )\n                .into(),\n                Identifier::new(\n                    interner.get_or_intern_static(\"b\", utf16!(\"b\")),\n                    Span::new((1, 6), (1, 7)),\n                )\n                .into(),\n            )))\n            .into(),\n        ],\n        interner,\n    );\n\n    let interner = &mut Interner::default();\n    check_script_parser(\n        \"a>>b\",\n        vec![\n            Statement::Expression(Expression::from(Binary::new(\n                BitwiseOp::Shr.into(),\n                Identifier::new(\n                    interner.get_or_intern_static(\"a\", utf16!(\"a\")),\n                    Span::new((1, 1), (1, 2)),\n                )\n                .into(),\n                Identifier::new(\n                    interner.get_or_intern_static(\"b\", utf16!(\"b\")),\n                    Span::new((1, 4), (1, 5)),\n                )\n                .into(),\n            )))\n            .into(),\n        ],\n        interner,\n    );\n}\n\n/// Checks assignment operations.\n#[test]\nfn check_assign_operations() {\n    let interner = &mut Interner::default();\n    check_script_parser(\n        \"a += b\",\n        vec![\n            Statement::Expression(Expression::from(Assign::new(\n                AssignOp::Add,\n                Identifier::new(\n                    interner.get_or_intern_static(\"a\", utf16!(\"a\")),\n                    Span::new((1, 1), (1, 2)),\n                )\n                .into(),\n                Identifier::new(\n                    interner.get_or_intern_static(\"b\", utf16!(\"b\")),\n                    Span::new((1, 6), (1, 7)),\n                )\n                .into(),\n            )))\n            .into(),\n        ],\n        interner,\n    );\n\n    let interner = &mut Interner::default();\n    check_script_parser(\n        \"a -= b\",\n        vec![\n            Statement::Expression(Expression::from(Assign::new(\n                AssignOp::Sub,\n                Identifier::new(\n                    interner.get_or_intern_static(\"a\", utf16!(\"a\")),\n                    Span::new((1, 1), (1, 2)),\n                )\n                .into(),\n                Identifier::new(\n                    interner.get_or_intern_static(\"b\", utf16!(\"b\")),\n                    Span::new((1, 6), (1, 7)),\n                )\n                .into(),\n            )))\n            .into(),\n        ],\n        interner,\n    );\n\n    let interner = &mut Interner::default();\n    check_script_parser(\n        \"a *= b\",\n        vec![\n            Statement::Expression(Expression::from(Assign::new(\n                AssignOp::Mul,\n                Identifier::new(\n                    interner.get_or_intern_static(\"a\", utf16!(\"a\")),\n                    Span::new((1, 1), (1, 2)),\n                )\n                .into(),\n                Identifier::new(\n                    interner.get_or_intern_static(\"b\", utf16!(\"b\")),\n                    Span::new((1, 6), (1, 7)),\n                )\n                .into(),\n            )))\n            .into(),\n        ],\n        interner,\n    );\n\n    let interner = &mut Interner::default();\n    check_script_parser(\n        \"a **= b\",\n        vec![\n            Statement::Expression(Expression::from(Assign::new(\n                AssignOp::Exp,\n                Identifier::new(\n                    interner.get_or_intern_static(\"a\", utf16!(\"a\")),\n                    Span::new((1, 1), (1, 2)),\n                )\n                .into(),\n                Identifier::new(\n                    interner.get_or_intern_static(\"b\", utf16!(\"b\")),\n                    Span::new((1, 7), (1, 8)),\n                )\n                .into(),\n            )))\n            .into(),\n        ],\n        interner,\n    );\n\n    let interner = &mut Interner::default();\n    check_script_parser(\n        \"a /= b\",\n        vec![\n            Statement::Expression(Expression::from(Assign::new(\n                AssignOp::Div,\n                Identifier::new(\n                    interner.get_or_intern_static(\"a\", utf16!(\"a\")),\n                    Span::new((1, 1), (1, 2)),\n                )\n                .into(),\n                Identifier::new(\n                    interner.get_or_intern_static(\"b\", utf16!(\"b\")),\n                    Span::new((1, 6), (1, 7)),\n                )\n                .into(),\n            )))\n            .into(),\n        ],\n        interner,\n    );\n\n    let interner = &mut Interner::default();\n    check_script_parser(\n        \"a %= b\",\n        vec![\n            Statement::Expression(Expression::from(Assign::new(\n                AssignOp::Mod,\n                Identifier::new(\n                    interner.get_or_intern_static(\"a\", utf16!(\"a\")),\n                    Span::new((1, 1), (1, 2)),\n                )\n                .into(),\n                Identifier::new(\n                    interner.get_or_intern_static(\"b\", utf16!(\"b\")),\n                    Span::new((1, 6), (1, 7)),\n                )\n                .into(),\n            )))\n            .into(),\n        ],\n        interner,\n    );\n\n    let interner = &mut Interner::default();\n    check_script_parser(\n        \"a &= b\",\n        vec![\n            Statement::Expression(Expression::from(Assign::new(\n                AssignOp::And,\n                Identifier::new(\n                    interner.get_or_intern_static(\"a\", utf16!(\"a\")),\n                    Span::new((1, 1), (1, 2)),\n                )\n                .into(),\n                Identifier::new(\n                    interner.get_or_intern_static(\"b\", utf16!(\"b\")),\n                    Span::new((1, 6), (1, 7)),\n                )\n                .into(),\n            )))\n            .into(),\n        ],\n        interner,\n    );\n\n    let interner = &mut Interner::default();\n    check_script_parser(\n        \"a |= b\",\n        vec![\n            Statement::Expression(Expression::from(Assign::new(\n                AssignOp::Or,\n                Identifier::new(\n                    interner.get_or_intern_static(\"a\", utf16!(\"a\")),\n                    Span::new((1, 1), (1, 2)),\n                )\n                .into(),\n                Identifier::new(\n                    interner.get_or_intern_static(\"b\", utf16!(\"b\")),\n                    Span::new((1, 6), (1, 7)),\n                )\n                .into(),\n            )))\n            .into(),\n        ],\n        interner,\n    );\n\n    let interner = &mut Interner::default();\n    check_script_parser(\n        \"a ^= b\",\n        vec![\n            Statement::Expression(Expression::from(Assign::new(\n                AssignOp::Xor,\n                Identifier::new(\n                    interner.get_or_intern_static(\"a\", utf16!(\"a\")),\n                    Span::new((1, 1), (1, 2)),\n                )\n                .into(),\n                Identifier::new(\n                    interner.get_or_intern_static(\"b\", utf16!(\"b\")),\n                    Span::new((1, 6), (1, 7)),\n                )\n                .into(),\n            )))\n            .into(),\n        ],\n        interner,\n    );\n\n    let interner = &mut Interner::default();\n    check_script_parser(\n        \"a <<= b\",\n        vec![\n            Statement::Expression(Expression::from(Assign::new(\n                AssignOp::Shl,\n                Identifier::new(\n                    interner.get_or_intern_static(\"a\", utf16!(\"a\")),\n                    Span::new((1, 1), (1, 2)),\n                )\n                .into(),\n                Identifier::new(\n                    interner.get_or_intern_static(\"b\", utf16!(\"b\")),\n                    Span::new((1, 7), (1, 8)),\n                )\n                .into(),\n            )))\n            .into(),\n        ],\n        interner,\n    );\n\n    let interner = &mut Interner::default();\n    check_script_parser(\n        \"a >>= b\",\n        vec![\n            Statement::Expression(Expression::from(Assign::new(\n                AssignOp::Shr,\n                Identifier::new(\n                    interner.get_or_intern_static(\"a\", utf16!(\"a\")),\n                    Span::new((1, 1), (1, 2)),\n                )\n                .into(),\n                Identifier::new(\n                    interner.get_or_intern_static(\"b\", utf16!(\"b\")),\n                    Span::new((1, 7), (1, 8)),\n                )\n                .into(),\n            )))\n            .into(),\n        ],\n        interner,\n    );\n\n    let interner = &mut Interner::default();\n    check_script_parser(\n        \"a >>>= b\",\n        vec![\n            Statement::Expression(Expression::from(Assign::new(\n                AssignOp::Ushr,\n                Identifier::new(\n                    interner.get_or_intern_static(\"a\", utf16!(\"a\")),\n                    Span::new((1, 1), (1, 2)),\n                )\n                .into(),\n                Identifier::new(\n                    interner.get_or_intern_static(\"b\", utf16!(\"b\")),\n                    Span::new((1, 8), (1, 9)),\n                )\n                .into(),\n            )))\n            .into(),\n        ],\n        interner,\n    );\n\n    let interner = &mut Interner::default();\n    check_script_parser(\n        \"a %= 10 / 2\",\n        vec![\n            Statement::Expression(Expression::from(Assign::new(\n                AssignOp::Mod,\n                Identifier::new(\n                    interner.get_or_intern_static(\"a\", utf16!(\"a\")),\n                    Span::new((1, 1), (1, 2)),\n                )\n                .into(),\n                Binary::new(\n                    ArithmeticOp::Div.into(),\n                    Literal::new(10, Span::new((1, 6), (1, 8))).into(),\n                    Literal::new(2, Span::new((1, 11), (1, 12))).into(),\n                )\n                .into(),\n            )))\n            .into(),\n        ],\n        interner,\n    );\n\n    let interner = &mut Interner::default();\n    check_script_parser(\n        \"a ??= b\",\n        vec![\n            Statement::Expression(Expression::from(Assign::new(\n                AssignOp::Coalesce,\n                Identifier::new(\n                    interner.get_or_intern_static(\"a\", utf16!(\"a\")),\n                    Span::new((1, 1), (1, 2)),\n                )\n                .into(),\n                Identifier::new(\n                    interner.get_or_intern_static(\"b\", utf16!(\"b\")),\n                    Span::new((1, 7), (1, 8)),\n                )\n                .into(),\n            )))\n            .into(),\n        ],\n        interner,\n    );\n}\n\n#[test]\nfn check_relational_operations() {\n    let interner = &mut Interner::default();\n    check_script_parser(\n        \"a < b\",\n        vec![\n            Statement::Expression(Expression::from(Binary::new(\n                RelationalOp::LessThan.into(),\n                Identifier::new(\n                    interner.get_or_intern_static(\"a\", utf16!(\"a\")),\n                    Span::new((1, 1), (1, 2)),\n                )\n                .into(),\n                Identifier::new(\n                    interner.get_or_intern_static(\"b\", utf16!(\"b\")),\n                    Span::new((1, 5), (1, 6)),\n                )\n                .into(),\n            )))\n            .into(),\n        ],\n        interner,\n    );\n\n    let interner = &mut Interner::default();\n    check_script_parser(\n        \"a > b\",\n        vec![\n            Statement::Expression(Expression::from(Binary::new(\n                RelationalOp::GreaterThan.into(),\n                Identifier::new(\n                    interner.get_or_intern_static(\"a\", utf16!(\"a\")),\n                    Span::new((1, 1), (1, 2)),\n                )\n                .into(),\n                Identifier::new(\n                    interner.get_or_intern_static(\"b\", utf16!(\"b\")),\n                    Span::new((1, 5), (1, 6)),\n                )\n                .into(),\n            )))\n            .into(),\n        ],\n        interner,\n    );\n\n    let interner = &mut Interner::default();\n    check_script_parser(\n        \"a <= b\",\n        vec![\n            Statement::Expression(Expression::from(Binary::new(\n                RelationalOp::LessThanOrEqual.into(),\n                Identifier::new(\n                    interner.get_or_intern_static(\"a\", utf16!(\"a\")),\n                    Span::new((1, 1), (1, 2)),\n                )\n                .into(),\n                Identifier::new(\n                    interner.get_or_intern_static(\"b\", utf16!(\"b\")),\n                    Span::new((1, 6), (1, 7)),\n                )\n                .into(),\n            )))\n            .into(),\n        ],\n        interner,\n    );\n\n    let interner = &mut Interner::default();\n    check_script_parser(\n        \"a >= b\",\n        vec![\n            Statement::Expression(Expression::from(Binary::new(\n                RelationalOp::GreaterThanOrEqual.into(),\n                Identifier::new(\n                    interner.get_or_intern_static(\"a\", utf16!(\"a\")),\n                    Span::new((1, 1), (1, 2)),\n                )\n                .into(),\n                Identifier::new(\n                    interner.get_or_intern_static(\"b\", utf16!(\"b\")),\n                    Span::new((1, 6), (1, 7)),\n                )\n                .into(),\n            )))\n            .into(),\n        ],\n        interner,\n    );\n\n    let interner = &mut Interner::default();\n    check_script_parser(\n        \"p in o\",\n        vec![\n            Statement::Expression(Expression::from(Binary::new(\n                RelationalOp::In.into(),\n                Identifier::new(\n                    interner.get_or_intern_static(\"p\", utf16!(\"p\")),\n                    Span::new((1, 1), (1, 2)),\n                )\n                .into(),\n                Identifier::new(\n                    interner.get_or_intern_static(\"o\", utf16!(\"o\")),\n                    Span::new((1, 6), (1, 7)),\n                )\n                .into(),\n            )))\n            .into(),\n        ],\n        interner,\n    );\n}\n\n#[test]\nfn check_logical_expressions() {\n    let interner = &mut Interner::default();\n    check_script_parser(\n        \"a && b || c && d || e\",\n        vec![\n            Statement::Expression(Expression::from(Binary::new(\n                LogicalOp::Or.into(),\n                Binary::new(\n                    LogicalOp::And.into(),\n                    Identifier::new(\n                        interner.get_or_intern_static(\"a\", utf16!(\"a\")),\n                        Span::new((1, 1), (1, 2)),\n                    )\n                    .into(),\n                    Identifier::new(\n                        interner.get_or_intern_static(\"b\", utf16!(\"b\")),\n                        Span::new((1, 6), (1, 7)),\n                    )\n                    .into(),\n                )\n                .into(),\n                Binary::new(\n                    LogicalOp::Or.into(),\n                    Binary::new(\n                        LogicalOp::And.into(),\n                        Identifier::new(\n                            interner.get_or_intern_static(\"c\", utf16!(\"c\")),\n                            Span::new((1, 11), (1, 12)),\n                        )\n                        .into(),\n                        Identifier::new(\n                            interner.get_or_intern_static(\"d\", utf16!(\"d\")),\n                            Span::new((1, 16), (1, 17)),\n                        )\n                        .into(),\n                    )\n                    .into(),\n                    Identifier::new(\n                        interner.get_or_intern_static(\"e\", utf16!(\"e\")),\n                        Span::new((1, 21), (1, 22)),\n                    )\n                    .into(),\n                )\n                .into(),\n            )))\n            .into(),\n        ],\n        interner,\n    );\n\n    let interner = &mut Interner::default();\n    check_script_parser(\n        \"a ?? b ?? c\",\n        vec![\n            Statement::Expression(Expression::from(Binary::new(\n                LogicalOp::Coalesce.into(),\n                Binary::new(\n                    LogicalOp::Coalesce.into(),\n                    Identifier::new(\n                        interner.get_or_intern_static(\"a\", utf16!(\"a\")),\n                        Span::new((1, 1), (1, 2)),\n                    )\n                    .into(),\n                    Identifier::new(\n                        interner.get_or_intern_static(\"b\", utf16!(\"b\")),\n                        Span::new((1, 6), (1, 7)),\n                    )\n                    .into(),\n                )\n                .into(),\n                Identifier::new(\n                    interner.get_or_intern_static(\"c\", utf16!(\"c\")),\n                    Span::new((1, 11), (1, 12)),\n                )\n                .into(),\n            )))\n            .into(),\n        ],\n        interner,\n    );\n\n    check_invalid_script(\"a ?? b && c\");\n    check_invalid_script(\"a && b ?? c\");\n    check_invalid_script(\"a ?? b || c\");\n    check_invalid_script(\"a || b ?? c\");\n}\n\n#[test]\nfn parse_async_arrow_function_named_of() {\n    let interner = &mut Interner::default();\n    check_script_parser(\n        \"async of => {}\",\n        vec![\n            Statement::Expression(\n                AsyncArrowFunction::new(\n                    None,\n                    FormalParameterList::from_parameters(vec![FormalParameter::new(\n                        Variable::from_identifier(\n                            Identifier::new(\n                                interner.get_or_intern_static(\"of\", utf16!(\"of\")),\n                                Span::new((1, 7), (1, 9)),\n                            ),\n                            None,\n                        ),\n                        false,\n                    )]),\n                    FunctionBody::new(StatementList::default(), Span::new((1, 13), (1, 15))),\n                    LinearSpan::new(LinearPosition::default(), LinearPosition::default()),\n                    Span::new((1, 1), (1, 15)),\n                )\n                .into(),\n            )\n            .into(),\n        ],\n        interner,\n    );\n}\n\nmacro_rules! check_non_reserved_identifier {\n    ($keyword:literal) => {{\n        let interner = &mut Interner::default();\n        let input = format!(\"({})\", $keyword);\n\n        #[allow(clippy::cast_possible_truncation)]\n        let input_end = input.len() as u32 + 1;\n\n        check_script_parser(\n            input.as_str(),\n            vec![\n                Statement::Expression(\n                    Parenthesized::new(\n                        Identifier::new(\n                            interner.get_or_intern_static($keyword, utf16!($keyword)),\n                            Span::new((1, 2), (1, input_end - 1)),\n                        )\n                        .into(),\n                        Span::new((1, 1), (1, input_end)),\n                    )\n                    .into(),\n                )\n                .into(),\n            ],\n            interner,\n        );\n    }};\n}\n\n#[test]\nfn check_non_reserved_identifiers() {\n    // https://tc39.es/ecma262/#sec-keywords-and-reserved-words\n    // Those that are always allowed as identifiers, but also appear as\n    // keywords within certain syntactic productions, at places where\n    // Identifier is not allowed: as, async, from, get, meta, of, set,\n    // and target.\n\n    check_non_reserved_identifier!(\"as\");\n    check_non_reserved_identifier!(\"async\");\n    check_non_reserved_identifier!(\"from\");\n    check_non_reserved_identifier!(\"get\");\n    check_non_reserved_identifier!(\"meta\");\n    check_non_reserved_identifier!(\"of\");\n    check_non_reserved_identifier!(\"set\");\n    check_non_reserved_identifier!(\"target\");\n}\n"
  },
  {
    "path": "core/parser/src/parser/expression/unary.rs",
    "content": "//! Unary operator parsing.\n//!\n//! More information:\n//!  - [MDN documentation][mdn]\n//!  - [ECMAScript specification][spec]\n//!\n//! [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Expressions_and_Operators#Unary\n//! [spec]: https://tc39.es/ecma262/#sec-unary-operators\n\nuse crate::{\n    Error,\n    lexer::{Error as LexError, TokenKind},\n    parser::{\n        AllowAwait, AllowYield, Cursor, OrAbrupt, ParseResult, TokenParser,\n        expression::{\n            FormalParameterListOrExpression, await_expr::AwaitExpression, update::UpdateExpression,\n        },\n    },\n    source::ReadChar,\n};\nuse boa_ast::{\n    Expression, Keyword, Punctuator, Span, Spanned,\n    expression::{\n        access::PropertyAccess,\n        operator::{Unary, unary::UnaryOp},\n    },\n};\nuse boa_interner::Interner;\n\n/// Parses a unary expression.\n///\n/// More information:\n///  - [MDN documentation][mdn]\n///  - [ECMAScript specification][spec]\n///\n/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Expressions_and_Operators#Unary\n/// [spec]: https://tc39.es/ecma262/#prod-UnaryExpression\n#[derive(Debug, Clone, Copy)]\npub(in crate::parser) struct UnaryExpression {\n    allow_yield: AllowYield,\n    allow_await: AllowAwait,\n}\n\nimpl UnaryExpression {\n    /// Creates a new `UnaryExpression` parser.\n    pub(in crate::parser) fn new<Y, A>(allow_yield: Y, allow_await: A) -> Self\n    where\n        Y: Into<AllowYield>,\n        A: Into<AllowAwait>,\n    {\n        Self {\n            allow_yield: allow_yield.into(),\n            allow_await: allow_await.into(),\n        }\n    }\n}\n\nimpl<R> TokenParser<R> for UnaryExpression\nwhere\n    R: ReadChar,\n{\n    type Output = FormalParameterListOrExpression;\n\n    fn parse(self, cursor: &mut Cursor<R>, interner: &mut Interner) -> ParseResult<Self::Output> {\n        let tok = cursor.peek(0, interner).or_abrupt()?;\n        let token_start = tok.span().start();\n        match tok.kind() {\n            TokenKind::Keyword((Keyword::Delete | Keyword::Void | Keyword::TypeOf, true)) => Err(\n                Error::general(\"Keyword must not contain escaped characters\", token_start),\n            ),\n            TokenKind::Keyword((Keyword::Delete, false)) => {\n                cursor.advance(interner);\n                let position = cursor.peek(0, interner).or_abrupt()?.span().start();\n                let target = self.parse(cursor, interner)?.try_into_expression()?;\n\n                match target.flatten() {\n                    Expression::Identifier(_) if cursor.strict() => {\n                        return Err(Error::lex(LexError::Syntax(\n                            \"cannot delete variables in strict mode\".into(),\n                            token_start,\n                        )));\n                    }\n                    Expression::PropertyAccess(PropertyAccess::Private(_)) => {\n                        return Err(Error::lex(LexError::Syntax(\n                            \"cannot delete private fields\".into(),\n                            position,\n                        )));\n                    }\n                    _ => {}\n                }\n\n                let target_span_end = target.span().end();\n                Ok(Unary::new(\n                    UnaryOp::Delete,\n                    target,\n                    Span::new(token_start, target_span_end),\n                )\n                .into())\n            }\n            TokenKind::Keyword((Keyword::Void, false)) => {\n                cursor.advance(interner);\n                let target = self.parse(cursor, interner)?.try_into_expression()?;\n                let target_span_end = target.span().end();\n                Ok(Unary::new(\n                    UnaryOp::Void,\n                    target,\n                    Span::new(token_start, target_span_end),\n                )\n                .into())\n            }\n            TokenKind::Keyword((Keyword::TypeOf, false)) => {\n                cursor.advance(interner);\n\n                let target = self.parse(cursor, interner)?.try_into_expression()?;\n                let target_span_end = target.span().end();\n                Ok(Unary::new(\n                    UnaryOp::TypeOf,\n                    target,\n                    Span::new(token_start, target_span_end),\n                )\n                .into())\n            }\n            TokenKind::Punctuator(Punctuator::Add) => {\n                cursor.advance(interner);\n\n                let target = self.parse(cursor, interner)?.try_into_expression()?;\n                let target_span_end = target.span().end();\n                Ok(Unary::new(\n                    UnaryOp::Plus,\n                    target,\n                    Span::new(token_start, target_span_end),\n                )\n                .into())\n            }\n            TokenKind::Punctuator(Punctuator::Sub) => {\n                cursor.advance(interner);\n\n                let target = self.parse(cursor, interner)?.try_into_expression()?;\n                let target_span_end = target.span().end();\n                Ok(Unary::new(\n                    UnaryOp::Minus,\n                    target,\n                    Span::new(token_start, target_span_end),\n                )\n                .into())\n            }\n            TokenKind::Punctuator(Punctuator::Neg) => {\n                cursor.advance(interner);\n\n                let target = self.parse(cursor, interner)?.try_into_expression()?;\n                let target_span_end = target.span().end();\n                Ok(Unary::new(\n                    UnaryOp::Tilde,\n                    target,\n                    Span::new(token_start, target_span_end),\n                )\n                .into())\n            }\n            TokenKind::Punctuator(Punctuator::Not) => {\n                cursor.advance(interner);\n\n                let target = self.parse(cursor, interner)?.try_into_expression()?;\n                let target_span_end = target.span().end();\n                Ok(Unary::new(\n                    UnaryOp::Not,\n                    target,\n                    Span::new(token_start, target_span_end),\n                )\n                .into())\n            }\n            TokenKind::Keyword((Keyword::Await, true)) if self.allow_await.0 => {\n                Err(Error::general(\n                    \"Keyword 'await' must not contain escaped characters\",\n                    token_start,\n                ))\n            }\n            TokenKind::Keyword((Keyword::Await, false)) if self.allow_await.0 => {\n                Ok((AwaitExpression::new(self.allow_yield).parse(cursor, interner)?).into())\n            }\n            _ => UpdateExpression::new(self.allow_yield, self.allow_await).parse(cursor, interner),\n        }\n    }\n}\n"
  },
  {
    "path": "core/parser/src/parser/expression/update.rs",
    "content": "//! Update expression parsing.\n//!\n//! More information:\n//!  - [ECMAScript specification][spec]\n//!\n//! [spec]: https://tc39.es/ecma262/#sec-update-expressions\n\nuse crate::{\n    Error,\n    lexer::{Error as LexError, TokenKind},\n    parser::{\n        AllowAwait, AllowYield, Cursor, OrAbrupt, ParseResult, TokenParser,\n        expression::{\n            FormalParameterListOrExpression, check_strict_arguments_or_eval,\n            left_hand_side::LeftHandSideExpression, unary::UnaryExpression,\n        },\n    },\n    source::ReadChar,\n};\nuse boa_ast::{\n    Expression, Position, Punctuator, Span, Spanned,\n    expression::operator::{\n        Update,\n        update::{UpdateOp, UpdateTarget},\n    },\n};\nuse boa_interner::Interner;\n\n/// Parses an update expression.\n///\n/// More information:\n///  - [ECMAScript specification][spec]\n///\n/// [spec]: https://tc39.es/ecma262/#prod-UpdateExpression\n#[derive(Debug, Clone, Copy)]\npub(super) struct UpdateExpression {\n    allow_yield: AllowYield,\n    allow_await: AllowAwait,\n}\n\nimpl UpdateExpression {\n    /// Creates a new `UpdateExpression` parser.\n    pub(super) fn new<Y, A>(allow_yield: Y, allow_await: A) -> Self\n    where\n        Y: Into<AllowYield>,\n        A: Into<AllowAwait>,\n    {\n        Self {\n            allow_yield: allow_yield.into(),\n            allow_await: allow_await.into(),\n        }\n    }\n}\n\n/// Check if the assignment target type is simple and return the target as an `UpdateTarget`.\n///\n/// More information:\n///  - [ECMAScript specification][spec]\n///\n/// [spec]: https://tc39.es/ecma262/#sec-static-semantics-assignmenttargettype\nfn as_simple(\n    expr: &Expression,\n    position: Position,\n    strict: bool,\n) -> ParseResult<Option<UpdateTarget>> {\n    match expr {\n        Expression::Identifier(ident) => {\n            if strict {\n                check_strict_arguments_or_eval(*ident, position)?;\n            }\n            Ok(Some(UpdateTarget::Identifier(*ident)))\n        }\n        Expression::PropertyAccess(access) => {\n            Ok(Some(UpdateTarget::PropertyAccess(access.clone())))\n        }\n        Expression::Parenthesized(p) => as_simple(p.expression(), position, strict),\n        _ => Ok(None),\n    }\n}\n\nimpl<R> TokenParser<R> for UpdateExpression\nwhere\n    R: ReadChar,\n{\n    type Output = FormalParameterListOrExpression;\n\n    fn parse(self, cursor: &mut Cursor<R>, interner: &mut Interner) -> ParseResult<Self::Output> {\n        let tok = cursor.peek(0, interner).or_abrupt()?;\n        let position = tok.span().start();\n        match tok.kind() {\n            TokenKind::Punctuator(Punctuator::Inc) => {\n                cursor\n                    .next(interner)?\n                    .expect(\"Punctuator::Inc token disappeared\");\n\n                let target = UnaryExpression::new(self.allow_yield, self.allow_await)\n                    .parse(cursor, interner)?\n                    .try_into_expression()?;\n                let target_span_end = target.span().end();\n\n                // https://tc39.es/ecma262/#sec-update-expressions-static-semantics-early-errors\n                return (as_simple(&target, position, cursor.strict())?).map_or_else(\n                    || {\n                        Err(Error::lex(LexError::Syntax(\n                            \"Invalid left-hand side in assignment\".into(),\n                            position,\n                        )))\n                    },\n                    |target| {\n                        Ok(Update::new(\n                            UpdateOp::IncrementPre,\n                            target,\n                            Span::new(position, target_span_end),\n                        )\n                        .into())\n                    },\n                );\n            }\n            TokenKind::Punctuator(Punctuator::Dec) => {\n                cursor\n                    .next(interner)?\n                    .expect(\"Punctuator::Dec token disappeared\");\n\n                let target = UnaryExpression::new(self.allow_yield, self.allow_await)\n                    .parse(cursor, interner)?\n                    .try_into_expression()?;\n                let target_span_end = target.span().end();\n\n                // https://tc39.es/ecma262/#sec-update-expressions-static-semantics-early-errors\n                return (as_simple(&target, position, cursor.strict())?).map_or_else(\n                    || {\n                        Err(Error::lex(LexError::Syntax(\n                            \"Invalid left-hand side in assignment\".into(),\n                            position,\n                        )))\n                    },\n                    |target| {\n                        Ok(Update::new(\n                            UpdateOp::DecrementPre,\n                            target,\n                            Span::new(position, target_span_end),\n                        )\n                        .into())\n                    },\n                );\n            }\n            _ => {}\n        }\n\n        let lhs = LeftHandSideExpression::new(self.allow_yield, self.allow_await)\n            .parse(cursor, interner)?;\n        let FormalParameterListOrExpression::Expression(lhs) = lhs else {\n            return Ok(lhs);\n        };\n        let lhs_span_start = lhs.span().start();\n\n        if cursor.peek_is_line_terminator(0, interner)?.unwrap_or(true) {\n            return Ok(lhs.into());\n        }\n\n        if let Some(tok) = cursor.peek(0, interner)? {\n            let token_start = tok.span().start();\n            let token_end = tok.span().end();\n            match tok.kind() {\n                TokenKind::Punctuator(Punctuator::Inc) => {\n                    cursor\n                        .next(interner)?\n                        .expect(\"Punctuator::Inc token disappeared\");\n\n                    // https://tc39.es/ecma262/#sec-update-expressions-static-semantics-early-errors\n                    return (as_simple(&lhs, position, cursor.strict())?).map_or_else(\n                        || {\n                            Err(Error::lex(LexError::Syntax(\n                                \"Invalid left-hand side in assignment\".into(),\n                                token_start,\n                            )))\n                        },\n                        |target| {\n                            Ok(Update::new(\n                                UpdateOp::IncrementPost,\n                                target,\n                                Span::new(lhs_span_start, token_end),\n                            )\n                            .into())\n                        },\n                    );\n                }\n                TokenKind::Punctuator(Punctuator::Dec) => {\n                    cursor\n                        .next(interner)?\n                        .expect(\"Punctuator::Dec token disappeared\");\n\n                    // https://tc39.es/ecma262/#sec-update-expressions-static-semantics-early-errors\n                    return (as_simple(&lhs, position, cursor.strict())?).map_or_else(\n                        || {\n                            Err(Error::lex(LexError::Syntax(\n                                \"Invalid left-hand side in assignment\".into(),\n                                token_start,\n                            )))\n                        },\n                        |target| {\n                            Ok(Update::new(\n                                UpdateOp::DecrementPost,\n                                target,\n                                Span::new(lhs_span_start, token_end),\n                            )\n                            .into())\n                        },\n                    );\n                }\n                _ => {}\n            }\n        }\n\n        Ok(lhs.into())\n    }\n}\n"
  },
  {
    "path": "core/parser/src/parser/function/mod.rs",
    "content": "//! Function definition parsing.\n//!\n//! More information:\n//!  - [MDN documentation][mdn]\n//!  - [ECMAScript specification][spec]\n//!\n//! [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/function\n//! [spec]: https://tc39.es/ecma262/#sec-function-definitions\n\n#[cfg(test)]\nmod tests;\n\nuse crate::{\n    Error,\n    lexer::{Error as LexError, InputElement, TokenKind},\n    parser::{\n        AllowAwait, AllowYield, Cursor, OrAbrupt, ParseResult, TokenParser,\n        expression::{BindingIdentifier, Initializer},\n        statement::{ArrayBindingPattern, ObjectBindingPattern, StatementList},\n    },\n    source::ReadChar,\n};\nuse ast::{\n    Position,\n    operations::{check_labels, contains_invalid_object_literal},\n};\nuse boa_ast::{\n    self as ast, Punctuator, Span, Spanned,\n    declaration::Variable,\n    expression::Identifier,\n    function::{FormalParameterList, FormalParameterListFlags, FunctionBody as AstFunctionBody},\n};\nuse boa_interner::{Interner, Sym};\n\n/// Formal parameters parsing.\n///\n/// More information:\n///  - [MDN documentation][mdn]\n///  - [ECMAScript specification][spec]\n///\n/// [mdn]: https://developer.mozilla.org/en-US/docs/Glossary/Parameter\n/// [spec]: https://tc39.es/ecma262/#prod-FormalParameters\n#[derive(Debug, Clone, Copy)]\npub(in crate::parser) struct FormalParameters {\n    allow_yield: AllowYield,\n    allow_await: AllowAwait,\n}\n\nimpl FormalParameters {\n    /// Creates a new `FormalParameters` parser.\n    pub(in crate::parser) fn new<Y, A>(allow_yield: Y, allow_await: A) -> Self\n    where\n        Y: Into<AllowYield>,\n        A: Into<AllowAwait>,\n    {\n        Self {\n            allow_yield: allow_yield.into(),\n            allow_await: allow_await.into(),\n        }\n    }\n}\n\nimpl<R> TokenParser<R> for FormalParameters\nwhere\n    R: ReadChar,\n{\n    type Output = FormalParameterList;\n\n    fn parse(self, cursor: &mut Cursor<R>, interner: &mut Interner) -> ParseResult<Self::Output> {\n        cursor.set_goal(InputElement::RegExp);\n\n        let Some(start_position) = cursor\n            .peek(0, interner)?\n            .filter(|&tok| tok.kind() != &TokenKind::Punctuator(Punctuator::CloseParen))\n            .map(|tok| tok.span().start())\n        else {\n            return Ok(FormalParameterList::default());\n        };\n\n        let mut params = Vec::new();\n\n        loop {\n            let mut rest_param = false;\n\n            let next_param = match cursor.peek(0, interner)? {\n                Some(tok) if tok.kind() == &TokenKind::Punctuator(Punctuator::Spread) => {\n                    rest_param = true;\n                    FunctionRestParameter::new(self.allow_yield, self.allow_await)\n                        .parse(cursor, interner)?\n                }\n                _ => FormalParameter::new(self.allow_yield, self.allow_await)\n                    .parse(cursor, interner)?,\n            };\n\n            if next_param.is_rest_param() && next_param.init().is_some() {\n                return Err(Error::lex(LexError::Syntax(\n                    \"Rest parameter may not have a default initializer\".into(),\n                    start_position,\n                )));\n            }\n\n            params.push(next_param);\n\n            if cursor\n                .peek(0, interner)?\n                .is_none_or(|tok| tok.kind() == &TokenKind::Punctuator(Punctuator::CloseParen))\n            {\n                break;\n            }\n\n            if rest_param {\n                let next = cursor.next(interner)?.expect(\"peeked token disappeared\");\n                return Err(Error::unexpected(\n                    next.to_string(interner),\n                    next.span(),\n                    \"rest parameter must be the last formal parameter\",\n                ));\n            }\n\n            cursor.expect(Punctuator::Comma, \"parameter list\", interner)?;\n            if cursor\n                .peek(0, interner)?\n                .is_none_or(|tok| tok.kind() == &TokenKind::Punctuator(Punctuator::CloseParen))\n            {\n                break;\n            }\n        }\n\n        let params = FormalParameterList::from_parameters(params);\n\n        // Early Error: It is a Syntax Error if IsSimpleParameterList of FormalParameterList is false\n        // and BoundNames of FormalParameterList contains any duplicate elements.\n        if !params.flags().contains(FormalParameterListFlags::IS_SIMPLE)\n            && params\n                .flags()\n                .contains(FormalParameterListFlags::HAS_DUPLICATES)\n        {\n            return Err(Error::lex(LexError::Syntax(\n                \"Duplicate parameter name not allowed in this context\".into(),\n                start_position,\n            )));\n        }\n        Ok(params)\n    }\n}\n\n/// `UniqueFormalParameters` parsing.\n///\n/// More information:\n///  - [ECMAScript specification][spec]\n///\n/// [spec]: https://tc39.es/ecma262/#prod-UniqueFormalParameters\n#[derive(Debug, Clone, Copy)]\npub(in crate::parser) struct UniqueFormalParameters {\n    allow_yield: AllowYield,\n    allow_await: AllowAwait,\n}\n\nimpl UniqueFormalParameters {\n    /// Creates a new `UniqueFormalParameters` parser.\n    pub(in crate::parser) fn new<Y, A>(allow_yield: Y, allow_await: A) -> Self\n    where\n        Y: Into<AllowYield>,\n        A: Into<AllowAwait>,\n    {\n        Self {\n            allow_yield: allow_yield.into(),\n            allow_await: allow_await.into(),\n        }\n    }\n}\n\nimpl<R> TokenParser<R> for UniqueFormalParameters\nwhere\n    R: ReadChar,\n{\n    type Output = FormalParameterList;\n\n    fn parse(self, cursor: &mut Cursor<R>, interner: &mut Interner) -> ParseResult<Self::Output> {\n        let params_start_position = cursor\n            .expect(\n                TokenKind::Punctuator(Punctuator::OpenParen),\n                \"unique formal parameters\",\n                interner,\n            )?\n            .span()\n            .end();\n        let params =\n            FormalParameters::new(self.allow_yield, self.allow_await).parse(cursor, interner)?;\n        cursor.expect(\n            TokenKind::Punctuator(Punctuator::CloseParen),\n            \"unique formal parameters\",\n            interner,\n        )?;\n\n        // Early Error: UniqueFormalParameters : FormalParameters\n        if params.has_duplicates() {\n            return Err(Error::lex(LexError::Syntax(\n                \"duplicate parameter name not allowed in unique formal parameters\".into(),\n                params_start_position,\n            )));\n        }\n        Ok(params)\n    }\n}\n\n/// Rest parameter parsing.\n///\n/// More information:\n///  - [MDN documentation][mdn]\n///  - [ECMAScript specification][spec]\n///\n/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/rest_parameters\n/// [spec]: https://tc39.es/ecma262/#prod-FunctionRestParameter\ntype FunctionRestParameter = BindingRestElement;\n\n/// Rest parameter parsing.\n///\n/// More information:\n///  - [MDN documentation][mdn]\n///  - [ECMAScript specification][spec]\n///\n/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/rest_parameters\n/// [spec]: https://tc39.es/ecma262/#prod-BindingRestElement\n#[derive(Debug, Clone, Copy)]\nstruct BindingRestElement {\n    allow_yield: AllowYield,\n    allow_await: AllowAwait,\n}\n\nimpl BindingRestElement {\n    /// Creates a new `BindingRestElement` parser.\n    fn new<Y, A>(allow_yield: Y, allow_await: A) -> Self\n    where\n        Y: Into<AllowYield>,\n        A: Into<AllowAwait>,\n    {\n        Self {\n            allow_yield: allow_yield.into(),\n            allow_await: allow_await.into(),\n        }\n    }\n}\n\nimpl<R> TokenParser<R> for BindingRestElement\nwhere\n    R: ReadChar,\n{\n    type Output = ast::function::FormalParameter;\n\n    fn parse(self, cursor: &mut Cursor<R>, interner: &mut Interner) -> ParseResult<Self::Output> {\n        cursor.expect(Punctuator::Spread, \"rest parameter\", interner)?;\n\n        if let Some(t) = cursor.peek(0, interner)? {\n            let declaration = match *t.kind() {\n                TokenKind::Punctuator(Punctuator::OpenBlock) => {\n                    let param = ObjectBindingPattern::new(self.allow_yield, self.allow_await)\n                        .parse(cursor, interner)?;\n\n                    let init = cursor\n                        .peek(0, interner)?\n                        .cloned()\n                        .filter(|t| {\n                            // Check that this is an initializer before attempting parse.\n                            *t.kind() == TokenKind::Punctuator(Punctuator::Assign)\n                        })\n                        .map(|_| {\n                            Initializer::new(true, self.allow_yield, self.allow_await)\n                                .parse(cursor, interner)\n                        })\n                        .transpose()?;\n                    Variable::from_pattern(param.into(), init)\n                }\n\n                TokenKind::Punctuator(Punctuator::OpenBracket) => Variable::from_pattern(\n                    ArrayBindingPattern::new(self.allow_yield, self.allow_await)\n                        .parse(cursor, interner)?\n                        .into(),\n                    None,\n                ),\n\n                _ => {\n                    let params = BindingIdentifier::new(self.allow_yield, self.allow_await)\n                        .parse(cursor, interner)?;\n                    let init = cursor\n                        .peek(0, interner)?\n                        .cloned()\n                        .filter(|t| {\n                            // Check that this is an initializer before attempting parse.\n                            *t.kind() == TokenKind::Punctuator(Punctuator::Assign)\n                        })\n                        .map(|_| {\n                            Initializer::new(true, self.allow_yield, self.allow_await)\n                                .parse(cursor, interner)\n                        })\n                        .transpose()?;\n\n                    Variable::from_identifier(params, init)\n                }\n            };\n            Ok(Self::Output::new(declaration, true))\n        } else {\n            Ok(Self::Output::new(\n                Variable::from_identifier(\n                    Identifier::new(Sym::EMPTY_STRING, Span::new((1234, 1234), (1234, 1234))),\n                    None,\n                ),\n                true,\n            ))\n        }\n    }\n}\n\n/// Formal parameter parsing.\n///\n/// More information:\n///  - [MDN documentation][mdn]\n///  - [ECMAScript specification][spec]\n///\n/// [mdn]: https://developer.mozilla.org/en-US/docs/Glossary/Parameter\n/// [spec]: https://tc39.es/ecma262/#prod-FormalParameter\n#[derive(Debug, Clone, Copy)]\npub(in crate::parser) struct FormalParameter {\n    allow_yield: AllowYield,\n    allow_await: AllowAwait,\n}\n\nimpl FormalParameter {\n    /// Creates a new `FormalParameter` parser.\n    pub(in crate::parser) fn new<Y, A>(allow_yield: Y, allow_await: A) -> Self\n    where\n        Y: Into<AllowYield>,\n        A: Into<AllowAwait>,\n    {\n        Self {\n            allow_yield: allow_yield.into(),\n            allow_await: allow_await.into(),\n        }\n    }\n}\n\nimpl<R> TokenParser<R> for FormalParameter\nwhere\n    R: ReadChar,\n{\n    type Output = ast::function::FormalParameter;\n\n    fn parse(self, cursor: &mut Cursor<R>, interner: &mut Interner) -> ParseResult<Self::Output> {\n        if let Some(t) = cursor.peek(0, interner)? {\n            let declaration = match *t.kind() {\n                TokenKind::Punctuator(Punctuator::OpenBlock) => {\n                    let bindings = ObjectBindingPattern::new(self.allow_yield, self.allow_await)\n                        .parse(cursor, interner)?;\n                    let init = if *cursor.peek(0, interner).or_abrupt()?.kind()\n                        == TokenKind::Punctuator(Punctuator::Assign)\n                    {\n                        Some(\n                            Initializer::new(true, self.allow_yield, self.allow_await)\n                                .parse(cursor, interner)?,\n                        )\n                    } else {\n                        None\n                    };\n\n                    Variable::from_pattern(bindings.into(), init)\n                }\n                TokenKind::Punctuator(Punctuator::OpenBracket) => {\n                    let bindings = ArrayBindingPattern::new(self.allow_yield, self.allow_await)\n                        .parse(cursor, interner)?;\n                    let init = if *cursor.peek(0, interner).or_abrupt()?.kind()\n                        == TokenKind::Punctuator(Punctuator::Assign)\n                    {\n                        Some(\n                            Initializer::new(true, self.allow_yield, self.allow_await)\n                                .parse(cursor, interner)?,\n                        )\n                    } else {\n                        None\n                    };\n\n                    Variable::from_pattern(bindings.into(), init)\n                }\n                _ => {\n                    let ident = BindingIdentifier::new(self.allow_yield, self.allow_await)\n                        .parse(cursor, interner)?;\n                    let init = if cursor\n                        .peek(0, interner)?\n                        .is_some_and(|tok| tok.kind() == &TokenKind::Punctuator(Punctuator::Assign))\n                    {\n                        Some(\n                            Initializer::new(true, self.allow_yield, self.allow_await)\n                                .parse(cursor, interner)?,\n                        )\n                    } else {\n                        None\n                    };\n\n                    Variable::from_identifier(ident, init)\n                }\n            };\n            Ok(Self::Output::new(declaration, false))\n        } else {\n            Ok(Self::Output::new(\n                Variable::from_identifier(\n                    Identifier::new(Sym::EMPTY_STRING, Span::new((1234, 1234), (1234, 1234))),\n                    None,\n                ),\n                false,\n            ))\n        }\n    }\n}\n\n/// A `FunctionBody` is equivalent to a `FunctionStatementList`.\n///\n/// More information:\n///  - [ECMAScript specification][spec]\n///\n/// [spec]: https://tc39.es/ecma262/#prod-FunctionBody\npub(in crate::parser) type FunctionBody = FunctionStatementList;\n\n/// The possible `TokenKind` which indicate the end of a function statement.\npub(in crate::parser) const FUNCTION_BREAK_TOKENS: [TokenKind; 1] =\n    [TokenKind::Punctuator(Punctuator::CloseBlock)];\n\n/// A function statement list\n///\n/// More information:\n///  - [ECMAScript specification][spec]\n///\n/// [spec]: https://tc39.es/ecma262/#prod-FunctionStatementList\n#[derive(Debug, Clone, Copy)]\npub(in crate::parser) struct FunctionStatementList {\n    allow_yield: AllowYield,\n    allow_await: AllowAwait,\n    context: &'static str,\n    parse_full_input: bool,\n}\n\nimpl FunctionStatementList {\n    /// Creates a new `FunctionStatementList` parser.\n    pub(in crate::parser) fn new<Y, A>(\n        allow_yield: Y,\n        allow_await: A,\n        context: &'static str,\n    ) -> Self\n    where\n        Y: Into<AllowYield>,\n        A: Into<AllowAwait>,\n    {\n        Self {\n            allow_yield: allow_yield.into(),\n            allow_await: allow_await.into(),\n            context,\n            parse_full_input: false,\n        }\n    }\n\n    /// Try to consume the whole input, not expecting open/closing parentheses.\n    pub(in crate::parser) fn parse_full_input(&mut self, parse_full_input: bool) {\n        self.parse_full_input = parse_full_input;\n    }\n}\n\nimpl<R> TokenParser<R> for FunctionStatementList\nwhere\n    R: ReadChar,\n{\n    type Output = AstFunctionBody;\n\n    fn parse(self, cursor: &mut Cursor<R>, interner: &mut Interner) -> ParseResult<Self::Output> {\n        let start = if self.parse_full_input {\n            cursor\n                .peek(0, interner)?\n                .map_or_else(|| Position::new(1, 1), |token| token.span().start())\n        } else {\n            cursor\n                .expect(Punctuator::OpenBlock, self.context, interner)?\n                .span()\n                .start()\n        };\n\n        let (body, end) = StatementList::new(\n            self.allow_yield,\n            self.allow_await,\n            true,\n            &FUNCTION_BREAK_TOKENS,\n            true,\n            false,\n        )\n        .parse(cursor, interner)?;\n\n        if let Err(error) = check_labels(&body) {\n            return Err(Error::lex(LexError::Syntax(\n                error.message(interner).into(),\n                Position::new(1, 1),\n            )));\n        }\n\n        if contains_invalid_object_literal(&body) {\n            return Err(Error::lex(LexError::Syntax(\n                \"invalid object literal in function statement list\".into(),\n                Position::new(1, 1),\n            )));\n        }\n\n        let end = if self.parse_full_input {\n            end.unwrap_or(start)\n        } else {\n            cursor\n                .expect(Punctuator::CloseBlock, self.context, interner)?\n                .span()\n                .end()\n        };\n\n        Ok(AstFunctionBody::new(body, Span::new(start, end)))\n    }\n}\n"
  },
  {
    "path": "core/parser/src/parser/function/tests.rs",
    "content": "use crate::parser::tests::{check_invalid_script, check_script_parser};\nuse boa_ast::{\n    Declaration, Span, Statement, StatementList, StatementListItem,\n    declaration::{LexicalDeclaration, Variable},\n    expression::{\n        Identifier,\n        operator::{Binary, binary::ArithmeticOp},\n    },\n    function::{\n        ArrowFunction, FormalParameter, FormalParameterList, FormalParameterListFlags,\n        FunctionBody, FunctionDeclaration,\n    },\n    statement::Return,\n};\nuse boa_interner::Interner;\nuse boa_macros::utf16;\n\nconst PSEUDO_LINEAR_POS: boa_ast::LinearPosition = boa_ast::LinearPosition::new(0);\nconst EMPTY_LINEAR_SPAN: boa_ast::LinearSpan =\n    boa_ast::LinearSpan::new(PSEUDO_LINEAR_POS, PSEUDO_LINEAR_POS);\n\n/// Checks basic function declaration parsing.\n#[test]\nfn check_basic() {\n    let interner = &mut Interner::default();\n    let params = FormalParameterList::from(FormalParameter::new(\n        Variable::from_identifier(\n            Identifier::new(\n                interner.get_or_intern_static(\"a\", utf16!(\"a\")),\n                Span::new((1, 14), (1, 15)),\n            ),\n            None,\n        ),\n        false,\n    ));\n    assert_eq!(params.flags(), FormalParameterListFlags::default());\n    assert_eq!(params.length(), 1);\n\n    check_script_parser(\n        \"function foo(a) { return a; }\",\n        vec![\n            Declaration::FunctionDeclaration(FunctionDeclaration::new(\n                Identifier::new(\n                    interner.get_or_intern_static(\"foo\", utf16!(\"foo\")),\n                    Span::new((1, 10), (1, 13)),\n                ),\n                params,\n                FunctionBody::new(\n                    StatementList::new(\n                        [StatementListItem::Statement(\n                            Statement::Return(Return::new(Some(\n                                Identifier::new(\n                                    interner.get_or_intern_static(\"a\", utf16!(\"a\")),\n                                    Span::new((1, 26), (1, 27)),\n                                )\n                                .into(),\n                            )))\n                            .into(),\n                        )],\n                        PSEUDO_LINEAR_POS,\n                        false,\n                    ),\n                    Span::new((1, 17), (1, 30)),\n                ),\n                EMPTY_LINEAR_SPAN,\n            ))\n            .into(),\n        ],\n        interner,\n    );\n}\n\n/// Checks if duplicate parameter names are allowed with strict mode off.\n#[test]\nfn check_duplicates_strict_off() {\n    let interner = &mut Interner::default();\n    let params = FormalParameterList::from(vec![\n        FormalParameter::new(\n            Variable::from_identifier(\n                Identifier::new(\n                    interner.get_or_intern_static(\"a\", utf16!(\"a\")),\n                    Span::new((1, 14), (1, 15)),\n                ),\n                None,\n            ),\n            false,\n        ),\n        FormalParameter::new(\n            Variable::from_identifier(\n                Identifier::new(\n                    interner.get_or_intern_static(\"a\", utf16!(\"a\")),\n                    Span::new((1, 17), (1, 18)),\n                ),\n                None,\n            ),\n            false,\n        ),\n    ]);\n    assert_eq!(\n        params.flags(),\n        FormalParameterListFlags::default().union(FormalParameterListFlags::HAS_DUPLICATES)\n    );\n    assert_eq!(params.length(), 2);\n    check_script_parser(\n        \"function foo(a, a) { return a; }\",\n        vec![\n            Declaration::FunctionDeclaration(FunctionDeclaration::new(\n                Identifier::new(\n                    interner.get_or_intern_static(\"foo\", utf16!(\"foo\")),\n                    Span::new((1, 10), (1, 13)),\n                ),\n                params,\n                FunctionBody::new(\n                    StatementList::new(\n                        [StatementListItem::Statement(\n                            Statement::Return(Return::new(Some(\n                                Identifier::new(\n                                    interner.get_or_intern_static(\"a\", utf16!(\"a\")),\n                                    Span::new((1, 29), (1, 30)),\n                                )\n                                .into(),\n                            )))\n                            .into(),\n                        )],\n                        PSEUDO_LINEAR_POS,\n                        false,\n                    ),\n                    Span::new((1, 20), (1, 33)),\n                ),\n                EMPTY_LINEAR_SPAN,\n            ))\n            .into(),\n        ],\n        interner,\n    );\n}\n\n/// Checks if duplicate parameter names are an error with strict mode on.\n#[test]\nfn check_duplicates_strict_on() {\n    check_invalid_script(\"'use strict'; function foo(a, a) {}\");\n}\n\n/// Checks basic function declaration parsing with automatic semicolon insertion.\n#[test]\nfn check_basic_semicolon_insertion() {\n    let interner = &mut Interner::default();\n    let params = FormalParameterList::from(FormalParameter::new(\n        Variable::from_identifier(\n            Identifier::new(\n                interner.get_or_intern_static(\"a\", utf16!(\"a\")),\n                Span::new((1, 14), (1, 15)),\n            ),\n            None,\n        ),\n        false,\n    ));\n    assert_eq!(params.flags(), FormalParameterListFlags::default());\n    assert_eq!(params.length(), 1);\n\n    check_script_parser(\n        \"function foo(a) { return a }\",\n        vec![\n            Declaration::FunctionDeclaration(FunctionDeclaration::new(\n                Identifier::new(\n                    interner.get_or_intern_static(\"foo\", utf16!(\"foo\")),\n                    Span::new((1, 10), (1, 13)),\n                ),\n                params,\n                FunctionBody::new(\n                    StatementList::new(\n                        [StatementListItem::Statement(\n                            Statement::Return(Return::new(Some(\n                                Identifier::new(\n                                    interner.get_or_intern_static(\"a\", utf16!(\"a\")),\n                                    Span::new((1, 26), (1, 27)),\n                                )\n                                .into(),\n                            )))\n                            .into(),\n                        )],\n                        PSEUDO_LINEAR_POS,\n                        false,\n                    ),\n                    Span::new((1, 17), (1, 29)),\n                ),\n                EMPTY_LINEAR_SPAN,\n            ))\n            .into(),\n        ],\n        interner,\n    );\n}\n\n/// Checks functions with empty returns.\n#[test]\nfn check_empty_return() {\n    let interner = &mut Interner::default();\n    let params = FormalParameterList::from(FormalParameter::new(\n        Variable::from_identifier(\n            Identifier::new(\n                interner.get_or_intern_static(\"a\", utf16!(\"a\")),\n                Span::new((1, 14), (1, 15)),\n            ),\n            None,\n        ),\n        false,\n    ));\n    assert_eq!(params.flags(), FormalParameterListFlags::default());\n    assert_eq!(params.length(), 1);\n    check_script_parser(\n        \"function foo(a) { return; }\",\n        vec![\n            Declaration::FunctionDeclaration(FunctionDeclaration::new(\n                Identifier::new(\n                    interner.get_or_intern_static(\"foo\", utf16!(\"foo\")),\n                    Span::new((1, 10), (1, 13)),\n                ),\n                params,\n                FunctionBody::new(\n                    StatementList::new(\n                        [StatementListItem::Statement(\n                            Statement::Return(Return::new(None)).into(),\n                        )],\n                        PSEUDO_LINEAR_POS,\n                        false,\n                    ),\n                    Span::new((1, 17), (1, 28)),\n                ),\n                EMPTY_LINEAR_SPAN,\n            ))\n            .into(),\n        ],\n        interner,\n    );\n}\n\n/// Checks functions with empty returns without semicolon\n#[test]\nfn check_empty_return_semicolon_insertion() {\n    let interner = &mut Interner::default();\n    let params = FormalParameterList::from(FormalParameter::new(\n        Variable::from_identifier(\n            Identifier::new(\n                interner.get_or_intern_static(\"a\", utf16!(\"a\")),\n                Span::new((1, 14), (1, 15)),\n            ),\n            None,\n        ),\n        false,\n    ));\n    assert_eq!(params.flags(), FormalParameterListFlags::default());\n    assert_eq!(params.length(), 1);\n    check_script_parser(\n        \"function foo(a) { return }\",\n        vec![\n            Declaration::FunctionDeclaration(FunctionDeclaration::new(\n                Identifier::new(\n                    interner.get_or_intern_static(\"foo\", utf16!(\"foo\")),\n                    Span::new((1, 10), (1, 13)),\n                ),\n                params,\n                FunctionBody::new(\n                    StatementList::new(\n                        [StatementListItem::Statement(\n                            Statement::Return(Return::new(None)).into(),\n                        )],\n                        PSEUDO_LINEAR_POS,\n                        false,\n                    ),\n                    Span::new((1, 17), (1, 27)),\n                ),\n                EMPTY_LINEAR_SPAN,\n            ))\n            .into(),\n        ],\n        interner,\n    );\n}\n\n/// Checks rest operator parsing.\n#[test]\nfn check_rest_operator() {\n    let interner = &mut Interner::default();\n    let params = FormalParameterList::from(vec![\n        FormalParameter::new(\n            Variable::from_identifier(\n                Identifier::new(\n                    interner.get_or_intern_static(\"a\", utf16!(\"a\")),\n                    Span::new((1, 14), (1, 15)),\n                ),\n                None,\n            ),\n            false,\n        ),\n        FormalParameter::new(\n            Variable::from_identifier(\n                Identifier::new(\n                    interner.get_or_intern_static(\"b\", utf16!(\"b\")),\n                    Span::new((1, 20), (1, 21)),\n                ),\n                None,\n            ),\n            true,\n        ),\n    ]);\n    assert_eq!(\n        params.flags(),\n        FormalParameterListFlags::empty().union(FormalParameterListFlags::HAS_REST_PARAMETER)\n    );\n    assert_eq!(params.length(), 1);\n    check_script_parser(\n        \"function foo(a, ...b) {}\",\n        vec![\n            Declaration::FunctionDeclaration(FunctionDeclaration::new(\n                Identifier::new(\n                    interner.get_or_intern_static(\"foo\", utf16!(\"foo\")),\n                    Span::new((1, 10), (1, 13)),\n                ),\n                params,\n                FunctionBody::new(StatementList::default(), Span::new((1, 23), (1, 25))),\n                EMPTY_LINEAR_SPAN,\n            ))\n            .into(),\n        ],\n        interner,\n    );\n}\n\n/// Checks an arrow function with only a rest parameter.\n#[test]\nfn check_arrow_only_rest() {\n    let interner = &mut Interner::default();\n    let params = FormalParameterList::from(FormalParameter::new(\n        Variable::from_identifier(\n            Identifier::new(\n                interner.get_or_intern_static(\"a\", utf16!(\"a\")),\n                Span::new((1, 5), (1, 6)),\n            ),\n            None,\n        ),\n        true,\n    ));\n    assert_eq!(\n        params.flags(),\n        FormalParameterListFlags::empty().union(FormalParameterListFlags::HAS_REST_PARAMETER)\n    );\n    assert_eq!(params.length(), 0);\n    check_script_parser(\n        \"(...a) => {}\",\n        vec![\n            Statement::Expression(\n                ArrowFunction::new(\n                    None,\n                    params,\n                    FunctionBody::new(StatementList::default(), Span::new((1, 11), (1, 13))),\n                    EMPTY_LINEAR_SPAN,\n                    Span::new((1, 1), (1, 13)),\n                )\n                .into(),\n            )\n            .into(),\n        ],\n        interner,\n    );\n}\n\n/// Checks an arrow function with a rest parameter.\n#[test]\nfn check_arrow_rest() {\n    let interner = &mut Interner::default();\n    let params = FormalParameterList::from(vec![\n        FormalParameter::new(\n            Variable::from_identifier(\n                Identifier::new(\n                    interner.get_or_intern_static(\"a\", utf16!(\"a\")),\n                    Span::new((1, 2), (1, 3)),\n                ),\n                None,\n            ),\n            false,\n        ),\n        FormalParameter::new(\n            Variable::from_identifier(\n                Identifier::new(\n                    interner.get_or_intern_static(\"b\", utf16!(\"b\")),\n                    Span::new((1, 5), (1, 6)),\n                ),\n                None,\n            ),\n            false,\n        ),\n        FormalParameter::new(\n            Variable::from_identifier(\n                Identifier::new(\n                    interner.get_or_intern_static(\"c\", utf16!(\"c\")),\n                    Span::new((1, 11), (1, 12)),\n                ),\n                None,\n            ),\n            true,\n        ),\n    ]);\n    assert_eq!(\n        params.flags(),\n        FormalParameterListFlags::empty().union(FormalParameterListFlags::HAS_REST_PARAMETER)\n    );\n    assert_eq!(params.length(), 2);\n    check_script_parser(\n        \"(a, b, ...c) => {}\",\n        vec![\n            Statement::Expression(\n                ArrowFunction::new(\n                    None,\n                    params,\n                    FunctionBody::new(StatementList::default(), Span::new((1, 17), (1, 19))),\n                    EMPTY_LINEAR_SPAN,\n                    Span::new((1, 1), (1, 19)),\n                )\n                .into(),\n            )\n            .into(),\n        ],\n        interner,\n    );\n}\n\n/// Checks an arrow function with expression return.\n#[test]\nfn check_arrow() {\n    let interner = &mut Interner::default();\n    let params = FormalParameterList::from(vec![\n        FormalParameter::new(\n            Variable::from_identifier(\n                Identifier::new(\n                    interner.get_or_intern_static(\"a\", utf16!(\"a\")),\n                    Span::new((1, 2), (1, 3)),\n                ),\n                None,\n            ),\n            false,\n        ),\n        FormalParameter::new(\n            Variable::from_identifier(\n                Identifier::new(\n                    interner.get_or_intern_static(\"b\", utf16!(\"b\")),\n                    Span::new((1, 5), (1, 6)),\n                ),\n                None,\n            ),\n            false,\n        ),\n    ]);\n    assert_eq!(params.flags(), FormalParameterListFlags::default());\n    assert_eq!(params.length(), 2);\n    check_script_parser(\n        \"(a, b) => { return a + b; }\",\n        vec![\n            Statement::Expression(\n                ArrowFunction::new(\n                    None,\n                    params,\n                    FunctionBody::new(\n                        StatementList::new(\n                            [StatementListItem::Statement(\n                                Statement::Return(Return::new(Some(\n                                    Binary::new(\n                                        ArithmeticOp::Add.into(),\n                                        Identifier::new(\n                                            interner.get_or_intern_static(\"a\", utf16!(\"a\")),\n                                            Span::new((1, 20), (1, 21)),\n                                        )\n                                        .into(),\n                                        Identifier::new(\n                                            interner.get_or_intern_static(\"b\", utf16!(\"b\")),\n                                            Span::new((1, 24), (1, 25)),\n                                        )\n                                        .into(),\n                                    )\n                                    .into(),\n                                )))\n                                .into(),\n                            )],\n                            PSEUDO_LINEAR_POS,\n                            false,\n                        ),\n                        Span::new((1, 11), (1, 28)),\n                    ),\n                    EMPTY_LINEAR_SPAN,\n                    Span::new((1, 1), (1, 28)),\n                )\n                .into(),\n            )\n            .into(),\n        ],\n        interner,\n    );\n}\n\n/// Checks an arrow function with expression return and automatic semicolon insertion\n#[test]\nfn check_arrow_semicolon_insertion() {\n    let interner = &mut Interner::default();\n    let params = FormalParameterList::from(vec![\n        FormalParameter::new(\n            Variable::from_identifier(\n                Identifier::new(\n                    interner.get_or_intern_static(\"a\", utf16!(\"a\")),\n                    Span::new((1, 2), (1, 3)),\n                ),\n                None,\n            ),\n            false,\n        ),\n        FormalParameter::new(\n            Variable::from_identifier(\n                Identifier::new(\n                    interner.get_or_intern_static(\"b\", utf16!(\"b\")),\n                    Span::new((1, 5), (1, 6)),\n                ),\n                None,\n            ),\n            false,\n        ),\n    ]);\n    check_script_parser(\n        \"(a, b) => { return a + b }\",\n        vec![\n            Statement::Expression(\n                ArrowFunction::new(\n                    None,\n                    params,\n                    FunctionBody::new(\n                        StatementList::new(\n                            [StatementListItem::Statement(\n                                Statement::Return(Return::new(Some(\n                                    Binary::new(\n                                        ArithmeticOp::Add.into(),\n                                        Identifier::new(\n                                            interner.get_or_intern_static(\"a\", utf16!(\"a\")),\n                                            Span::new((1, 20), (1, 21)),\n                                        )\n                                        .into(),\n                                        Identifier::new(\n                                            interner.get_or_intern_static(\"b\", utf16!(\"b\")),\n                                            Span::new((1, 24), (1, 25)),\n                                        )\n                                        .into(),\n                                    )\n                                    .into(),\n                                )))\n                                .into(),\n                            )],\n                            PSEUDO_LINEAR_POS,\n                            false,\n                        ),\n                        Span::new((1, 11), (1, 27)),\n                    ),\n                    EMPTY_LINEAR_SPAN,\n                    Span::new((1, 1), (1, 27)),\n                )\n                .into(),\n            )\n            .into(),\n        ],\n        interner,\n    );\n}\n\n/// Checks arrow function with empty return\n#[test]\nfn check_arrow_empty_return() {\n    let interner = &mut Interner::default();\n    let params = FormalParameterList::from(vec![\n        FormalParameter::new(\n            Variable::from_identifier(\n                Identifier::new(\n                    interner.get_or_intern_static(\"a\", utf16!(\"a\")),\n                    Span::new((1, 2), (1, 3)),\n                ),\n                None,\n            ),\n            false,\n        ),\n        FormalParameter::new(\n            Variable::from_identifier(\n                Identifier::new(\n                    interner.get_or_intern_static(\"b\", utf16!(\"b\")),\n                    Span::new((1, 5), (1, 6)),\n                ),\n                None,\n            ),\n            false,\n        ),\n    ]);\n    check_script_parser(\n        \"(a, b) => { return; }\",\n        vec![\n            Statement::Expression(\n                ArrowFunction::new(\n                    None,\n                    params,\n                    FunctionBody::new(\n                        StatementList::new(\n                            [StatementListItem::Statement(\n                                Statement::Return(Return::new(None)).into(),\n                            )],\n                            PSEUDO_LINEAR_POS,\n                            false,\n                        ),\n                        Span::new((1, 11), (1, 22)),\n                    ),\n                    EMPTY_LINEAR_SPAN,\n                    Span::new((1, 1), (1, 22)),\n                )\n                .into(),\n            )\n            .into(),\n        ],\n        interner,\n    );\n}\n\n/// Checks an arrow function with empty return, with automatic semicolon insertion.\n#[test]\nfn check_arrow_empty_return_semicolon_insertion() {\n    let interner = &mut Interner::default();\n    let params = FormalParameterList::from(vec![\n        FormalParameter::new(\n            Variable::from_identifier(\n                Identifier::new(\n                    interner.get_or_intern_static(\"a\", utf16!(\"a\")),\n                    Span::new((1, 2), (1, 3)),\n                ),\n                None,\n            ),\n            false,\n        ),\n        FormalParameter::new(\n            Variable::from_identifier(\n                Identifier::new(\n                    interner.get_or_intern_static(\"b\", utf16!(\"b\")),\n                    Span::new((1, 5), (1, 6)),\n                ),\n                None,\n            ),\n            false,\n        ),\n    ]);\n    check_script_parser(\n        \"(a, b) => { return }\",\n        vec![\n            Statement::Expression(\n                ArrowFunction::new(\n                    None,\n                    params,\n                    FunctionBody::new(\n                        StatementList::new(\n                            [StatementListItem::Statement(\n                                Statement::Return(Return::new(None)).into(),\n                            )],\n                            PSEUDO_LINEAR_POS,\n                            false,\n                        ),\n                        Span::new((1, 11), (1, 21)),\n                    ),\n                    EMPTY_LINEAR_SPAN,\n                    Span::new((1, 1), (1, 21)),\n                )\n                .into(),\n            )\n            .into(),\n        ],\n        interner,\n    );\n}\n\n#[test]\nfn check_arrow_assignment() {\n    let interner = &mut Interner::default();\n    let params = FormalParameterList::from(FormalParameter::new(\n        Variable::from_identifier(\n            Identifier::new(\n                interner.get_or_intern_static(\"a\", utf16!(\"a\")),\n                Span::new((1, 12), (1, 13)),\n            ),\n            None,\n        ),\n        false,\n    ));\n    assert_eq!(params.flags(), FormalParameterListFlags::default());\n    assert_eq!(params.length(), 1);\n    check_script_parser(\n        \"let foo = (a) => { return a };\",\n        vec![\n            Declaration::Lexical(LexicalDeclaration::Let(\n                vec![Variable::from_identifier(\n                    Identifier::new(\n                        interner.get_or_intern_static(\"foo\", utf16!(\"foo\")),\n                        Span::new((1, 5), (1, 8)),\n                    ),\n                    Some(\n                        ArrowFunction::new(\n                            Some(Identifier::new(\n                                interner.get_or_intern_static(\"foo\", utf16!(\"foo\")),\n                                Span::new((1, 5), (1, 8)),\n                            )),\n                            params,\n                            FunctionBody::new(\n                                StatementList::new(\n                                    [StatementListItem::Statement(\n                                        Statement::Return(Return::new(Some(\n                                            Identifier::new(\n                                                interner.get_or_intern_static(\"a\", utf16!(\"a\")),\n                                                Span::new((1, 27), (1, 28)),\n                                            )\n                                            .into(),\n                                        )))\n                                        .into(),\n                                    )],\n                                    PSEUDO_LINEAR_POS,\n                                    false,\n                                ),\n                                Span::new((1, 18), (1, 30)),\n                            ),\n                            EMPTY_LINEAR_SPAN,\n                            Span::new((1, 11), (1, 30)),\n                        )\n                        .into(),\n                    ),\n                )]\n                .try_into()\n                .unwrap(),\n            ))\n            .into(),\n        ],\n        interner,\n    );\n}\n\n#[test]\nfn check_arrow_assignment_nobrackets() {\n    let interner = &mut Interner::default();\n    let params = FormalParameterList::from(FormalParameter::new(\n        Variable::from_identifier(\n            Identifier::new(\n                interner.get_or_intern_static(\"a\", utf16!(\"a\")),\n                Span::new((1, 12), (1, 13)),\n            ),\n            None,\n        ),\n        false,\n    ));\n    assert_eq!(params.flags(), FormalParameterListFlags::default());\n    assert_eq!(params.length(), 1);\n    check_script_parser(\n        \"let foo = (a) => a;\",\n        vec![\n            Declaration::Lexical(LexicalDeclaration::Let(\n                vec![Variable::from_identifier(\n                    Identifier::new(\n                        interner.get_or_intern_static(\"foo\", utf16!(\"foo\")),\n                        Span::new((1, 5), (1, 8)),\n                    ),\n                    Some(\n                        ArrowFunction::new(\n                            Some(Identifier::new(\n                                interner.get_or_intern_static(\"foo\", utf16!(\"foo\")),\n                                Span::new((1, 5), (1, 8)),\n                            )),\n                            params,\n                            FunctionBody::new(\n                                StatementList::new(\n                                    [StatementListItem::Statement(\n                                        Statement::Return(Return::new(Some(\n                                            Identifier::new(\n                                                interner.get_or_intern_static(\"a\", utf16!(\"a\")),\n                                                Span::new((1, 18), (1, 19)),\n                                            )\n                                            .into(),\n                                        )))\n                                        .into(),\n                                    )],\n                                    PSEUDO_LINEAR_POS,\n                                    false,\n                                ),\n                                Span::new((1, 18), (1, 19)),\n                            ),\n                            EMPTY_LINEAR_SPAN,\n                            Span::new((1, 11), (1, 19)),\n                        )\n                        .into(),\n                    ),\n                )]\n                .try_into()\n                .unwrap(),\n            ))\n            .into(),\n        ],\n        interner,\n    );\n}\n\n#[test]\nfn check_arrow_assignment_noparenthesis() {\n    let interner = &mut Interner::default();\n    let params = FormalParameterList::from(FormalParameter::new(\n        Variable::from_identifier(\n            Identifier::new(\n                interner.get_or_intern_static(\"a\", utf16!(\"a\")),\n                Span::new((1, 11), (1, 12)),\n            ),\n            None,\n        ),\n        false,\n    ));\n    assert_eq!(params.flags(), FormalParameterListFlags::default());\n    assert_eq!(params.length(), 1);\n    check_script_parser(\n        \"let foo = a => { return a };\",\n        vec![\n            Declaration::Lexical(LexicalDeclaration::Let(\n                vec![Variable::from_identifier(\n                    Identifier::new(\n                        interner.get_or_intern_static(\"foo\", utf16!(\"foo\")),\n                        Span::new((1, 5), (1, 8)),\n                    ),\n                    Some(\n                        ArrowFunction::new(\n                            Some(Identifier::new(\n                                interner.get_or_intern_static(\"foo\", utf16!(\"foo\")),\n                                Span::new((1, 5), (1, 8)),\n                            )),\n                            params,\n                            FunctionBody::new(\n                                StatementList::new(\n                                    [StatementListItem::Statement(\n                                        Statement::Return(Return::new(Some(\n                                            Identifier::new(\n                                                interner.get_or_intern_static(\"a\", utf16!(\"a\")),\n                                                Span::new((1, 25), (1, 26)),\n                                            )\n                                            .into(),\n                                        )))\n                                        .into(),\n                                    )],\n                                    PSEUDO_LINEAR_POS,\n                                    false,\n                                ),\n                                Span::new((1, 16), (1, 28)),\n                            ),\n                            EMPTY_LINEAR_SPAN,\n                            Span::new((1, 11), (1, 28)),\n                        )\n                        .into(),\n                    ),\n                )]\n                .try_into()\n                .unwrap(),\n            ))\n            .into(),\n        ],\n        interner,\n    );\n}\n\n#[test]\nfn check_arrow_assignment_noparenthesis_nobrackets() {\n    let interner = &mut Interner::default();\n    let params = FormalParameterList::from(FormalParameter::new(\n        Variable::from_identifier(\n            Identifier::new(\n                interner.get_or_intern_static(\"a\", utf16!(\"a\")),\n                Span::new((1, 11), (1, 12)),\n            ),\n            None,\n        ),\n        false,\n    ));\n    assert_eq!(params.flags(), FormalParameterListFlags::default());\n    assert_eq!(params.length(), 1);\n    check_script_parser(\n        \"let foo = a => a;\",\n        vec![\n            Declaration::Lexical(LexicalDeclaration::Let(\n                vec![Variable::from_identifier(\n                    Identifier::new(\n                        interner.get_or_intern_static(\"foo\", utf16!(\"foo\")),\n                        Span::new((1, 5), (1, 8)),\n                    ),\n                    Some(\n                        ArrowFunction::new(\n                            Some(Identifier::new(\n                                interner.get_or_intern_static(\"foo\", utf16!(\"foo\")),\n                                Span::new((1, 5), (1, 8)),\n                            )),\n                            params,\n                            FunctionBody::new(\n                                StatementList::new(\n                                    [StatementListItem::Statement(\n                                        Statement::Return(Return::new(Some(\n                                            Identifier::new(\n                                                interner.get_or_intern_static(\"a\", utf16!(\"a\")),\n                                                Span::new((1, 16), (1, 17)),\n                                            )\n                                            .into(),\n                                        )))\n                                        .into(),\n                                    )],\n                                    PSEUDO_LINEAR_POS,\n                                    false,\n                                ),\n                                Span::new((1, 16), (1, 17)),\n                            ),\n                            EMPTY_LINEAR_SPAN,\n                            Span::new((1, 11), (1, 17)),\n                        )\n                        .into(),\n                    ),\n                )]\n                .try_into()\n                .unwrap(),\n            ))\n            .into(),\n        ],\n        interner,\n    );\n}\n\n#[test]\nfn check_arrow_assignment_2arg() {\n    let interner = &mut Interner::default();\n    let params = FormalParameterList::from(vec![\n        FormalParameter::new(\n            Variable::from_identifier(\n                Identifier::new(\n                    interner.get_or_intern_static(\"a\", utf16!(\"a\")),\n                    Span::new((1, 12), (1, 13)),\n                ),\n                None,\n            ),\n            false,\n        ),\n        FormalParameter::new(\n            Variable::from_identifier(\n                Identifier::new(\n                    interner.get_or_intern_static(\"b\", utf16!(\"b\")),\n                    Span::new((1, 15), (1, 16)),\n                ),\n                None,\n            ),\n            false,\n        ),\n    ]);\n    assert_eq!(params.flags(), FormalParameterListFlags::default());\n    assert_eq!(params.length(), 2);\n    check_script_parser(\n        \"let foo = (a, b) => { return a };\",\n        vec![\n            Declaration::Lexical(LexicalDeclaration::Let(\n                vec![Variable::from_identifier(\n                    Identifier::new(\n                        interner.get_or_intern_static(\"foo\", utf16!(\"foo\")),\n                        Span::new((1, 5), (1, 8)),\n                    ),\n                    Some(\n                        ArrowFunction::new(\n                            Some(Identifier::new(\n                                interner.get_or_intern_static(\"foo\", utf16!(\"foo\")),\n                                Span::new((1, 5), (1, 8)),\n                            )),\n                            params,\n                            FunctionBody::new(\n                                StatementList::new(\n                                    [StatementListItem::Statement(\n                                        Statement::Return(Return::new(Some(\n                                            Identifier::new(\n                                                interner.get_or_intern_static(\"a\", utf16!(\"a\")),\n                                                Span::new((1, 30), (1, 31)),\n                                            )\n                                            .into(),\n                                        )))\n                                        .into(),\n                                    )],\n                                    PSEUDO_LINEAR_POS,\n                                    false,\n                                ),\n                                Span::new((1, 21), (1, 33)),\n                            ),\n                            EMPTY_LINEAR_SPAN,\n                            Span::new((1, 11), (1, 33)),\n                        )\n                        .into(),\n                    ),\n                )]\n                .try_into()\n                .unwrap(),\n            ))\n            .into(),\n        ],\n        interner,\n    );\n}\n\n#[test]\nfn check_arrow_assignment_2arg_nobrackets() {\n    let interner = &mut Interner::default();\n    let params = FormalParameterList::from(vec![\n        FormalParameter::new(\n            Variable::from_identifier(\n                Identifier::new(\n                    interner.get_or_intern_static(\"a\", utf16!(\"a\")),\n                    Span::new((1, 12), (1, 13)),\n                ),\n                None,\n            ),\n            false,\n        ),\n        FormalParameter::new(\n            Variable::from_identifier(\n                Identifier::new(\n                    interner.get_or_intern_static(\"b\", utf16!(\"b\")),\n                    Span::new((1, 15), (1, 16)),\n                ),\n                None,\n            ),\n            false,\n        ),\n    ]);\n    assert_eq!(params.flags(), FormalParameterListFlags::default());\n    assert_eq!(params.length(), 2);\n    check_script_parser(\n        \"let foo = (a, b) => a;\",\n        vec![\n            Declaration::Lexical(LexicalDeclaration::Let(\n                vec![Variable::from_identifier(\n                    Identifier::new(\n                        interner.get_or_intern_static(\"foo\", utf16!(\"foo\")),\n                        Span::new((1, 5), (1, 8)),\n                    ),\n                    Some(\n                        ArrowFunction::new(\n                            Some(Identifier::new(\n                                interner.get_or_intern_static(\"foo\", utf16!(\"foo\")),\n                                Span::new((1, 5), (1, 8)),\n                            )),\n                            params,\n                            FunctionBody::new(\n                                StatementList::new(\n                                    [StatementListItem::Statement(\n                                        Statement::Return(Return::new(Some(\n                                            Identifier::new(\n                                                interner.get_or_intern_static(\"a\", utf16!(\"a\")),\n                                                Span::new((1, 21), (1, 22)),\n                                            )\n                                            .into(),\n                                        )))\n                                        .into(),\n                                    )],\n                                    PSEUDO_LINEAR_POS,\n                                    false,\n                                ),\n                                Span::new((1, 21), (1, 22)),\n                            ),\n                            EMPTY_LINEAR_SPAN,\n                            Span::new((1, 11), (1, 22)),\n                        )\n                        .into(),\n                    ),\n                )]\n                .try_into()\n                .unwrap(),\n            ))\n            .into(),\n        ],\n        interner,\n    );\n}\n\n#[test]\nfn check_arrow_assignment_3arg() {\n    let interner = &mut Interner::default();\n    let params = FormalParameterList::from(vec![\n        FormalParameter::new(\n            Variable::from_identifier(\n                Identifier::new(\n                    interner.get_or_intern_static(\"a\", utf16!(\"a\")),\n                    Span::new((1, 12), (1, 13)),\n                ),\n                None,\n            ),\n            false,\n        ),\n        FormalParameter::new(\n            Variable::from_identifier(\n                Identifier::new(\n                    interner.get_or_intern_static(\"b\", utf16!(\"b\")),\n                    Span::new((1, 15), (1, 16)),\n                ),\n                None,\n            ),\n            false,\n        ),\n        FormalParameter::new(\n            Variable::from_identifier(\n                Identifier::new(\n                    interner.get_or_intern_static(\"c\", utf16!(\"c\")),\n                    Span::new((1, 18), (1, 19)),\n                ),\n                None,\n            ),\n            false,\n        ),\n    ]);\n    assert_eq!(params.flags(), FormalParameterListFlags::default());\n    assert_eq!(params.length(), 3);\n    check_script_parser(\n        \"let foo = (a, b, c) => { return a };\",\n        vec![\n            Declaration::Lexical(LexicalDeclaration::Let(\n                vec![Variable::from_identifier(\n                    Identifier::new(\n                        interner.get_or_intern_static(\"foo\", utf16!(\"foo\")),\n                        Span::new((1, 5), (1, 8)),\n                    ),\n                    Some(\n                        ArrowFunction::new(\n                            Some(Identifier::new(\n                                interner.get_or_intern_static(\"foo\", utf16!(\"foo\")),\n                                Span::new((1, 5), (1, 8)),\n                            )),\n                            params,\n                            FunctionBody::new(\n                                StatementList::new(\n                                    [StatementListItem::Statement(\n                                        Statement::Return(Return::new(Some(\n                                            Identifier::new(\n                                                interner.get_or_intern_static(\"a\", utf16!(\"a\")),\n                                                Span::new((1, 33), (1, 34)),\n                                            )\n                                            .into(),\n                                        )))\n                                        .into(),\n                                    )],\n                                    PSEUDO_LINEAR_POS,\n                                    false,\n                                ),\n                                Span::new((1, 24), (1, 36)),\n                            ),\n                            EMPTY_LINEAR_SPAN,\n                            Span::new((1, 11), (1, 36)),\n                        )\n                        .into(),\n                    ),\n                )]\n                .try_into()\n                .unwrap(),\n            ))\n            .into(),\n        ],\n        interner,\n    );\n}\n\n#[test]\nfn check_arrow_assignment_3arg_nobrackets() {\n    let interner = &mut Interner::default();\n    let params = FormalParameterList::from(vec![\n        FormalParameter::new(\n            Variable::from_identifier(\n                Identifier::new(\n                    interner.get_or_intern_static(\"a\", utf16!(\"a\")),\n                    Span::new((1, 12), (1, 13)),\n                ),\n                None,\n            ),\n            false,\n        ),\n        FormalParameter::new(\n            Variable::from_identifier(\n                Identifier::new(\n                    interner.get_or_intern_static(\"b\", utf16!(\"b\")),\n                    Span::new((1, 15), (1, 16)),\n                ),\n                None,\n            ),\n            false,\n        ),\n        FormalParameter::new(\n            Variable::from_identifier(\n                Identifier::new(\n                    interner.get_or_intern_static(\"c\", utf16!(\"c\")),\n                    Span::new((1, 18), (1, 19)),\n                ),\n                None,\n            ),\n            false,\n        ),\n    ]);\n    assert_eq!(params.flags(), FormalParameterListFlags::default());\n    assert_eq!(params.length(), 3);\n    check_script_parser(\n        \"let foo = (a, b, c) => a;\",\n        vec![\n            Declaration::Lexical(LexicalDeclaration::Let(\n                vec![Variable::from_identifier(\n                    Identifier::new(\n                        interner.get_or_intern_static(\"foo\", utf16!(\"foo\")),\n                        Span::new((1, 5), (1, 8)),\n                    ),\n                    Some(\n                        ArrowFunction::new(\n                            Some(Identifier::new(\n                                interner.get_or_intern_static(\"foo\", utf16!(\"foo\")),\n                                Span::new((1, 5), (1, 8)),\n                            )),\n                            params,\n                            FunctionBody::new(\n                                StatementList::new(\n                                    [StatementListItem::Statement(\n                                        Statement::Return(Return::new(Some(\n                                            Identifier::new(\n                                                interner.get_or_intern_static(\"a\", utf16!(\"a\")),\n                                                Span::new((1, 24), (1, 25)),\n                                            )\n                                            .into(),\n                                        )))\n                                        .into(),\n                                    )],\n                                    PSEUDO_LINEAR_POS,\n                                    false,\n                                ),\n                                Span::new((1, 24), (1, 25)),\n                            ),\n                            EMPTY_LINEAR_SPAN,\n                            Span::new((1, 11), (1, 25)),\n                        )\n                        .into(),\n                    ),\n                )]\n                .try_into()\n                .unwrap(),\n            ))\n            .into(),\n        ],\n        interner,\n    );\n}\n"
  },
  {
    "path": "core/parser/src/parser/mod.rs",
    "content": "//! Boa parser implementation.\n\nmod cursor;\nmod expression;\nmod statement;\n\npub(crate) mod function;\n\n#[cfg(test)]\nmod tests;\n\nuse crate::{\n    Error, Source,\n    error::ParseResult,\n    lexer::{Error as LexError, InputElement},\n    parser::{\n        cursor::Cursor,\n        function::{FormalParameters, FunctionStatementList},\n    },\n    source::ReadChar,\n};\nuse boa_ast::{\n    Position, StatementList,\n    function::{FormalParameterList, FunctionBody},\n    operations::{\n        ContainsSymbol, all_private_identifiers_valid, check_labels, contains,\n        contains_invalid_object_literal, lexically_declared_names, var_declared_names,\n    },\n    scope::Scope,\n};\nuse boa_interner::{Interner, Sym};\nuse rustc_hash::FxHashSet;\nuse std::path::Path;\n\nuse self::statement::ModuleItemList;\n\ntype ScriptParseOutput = (boa_ast::Script, boa_ast::SourceText);\ntype ModuleParseOutput = (boa_ast::Module, boa_ast::SourceText);\n\n/// Trait implemented by parsers.\n///\n/// This makes it possible to abstract over the underlying implementation of a parser.\ntrait TokenParser<R>: Sized\nwhere\n    R: ReadChar,\n{\n    /// Output type for the parser.\n    type Output; // = Node; waiting for https://github.com/rust-lang/rust/issues/29661\n\n    /// Parses the token stream using the current parser.\n    ///\n    /// This method needs to be provided by the implementor type.\n    ///\n    /// # Errors\n    ///\n    /// It will fail if the cursor is not placed at the beginning of the expected non-terminal.\n    fn parse(self, cursor: &mut Cursor<R>, interner: &mut Interner) -> ParseResult<Self::Output>;\n}\n\n/// Boolean representing if the parser should allow a `yield` keyword.\n#[derive(Debug, Clone, Copy, PartialEq, Eq)]\nstruct AllowYield(bool);\n\nimpl From<bool> for AllowYield {\n    fn from(allow: bool) -> Self {\n        Self(allow)\n    }\n}\n\n/// Boolean representing if the parser should allow a `await` keyword.\n#[derive(Debug, Clone, Copy, PartialEq, Eq)]\nstruct AllowAwait(bool);\n\nimpl From<bool> for AllowAwait {\n    fn from(allow: bool) -> Self {\n        Self(allow)\n    }\n}\n\n/// Boolean representing if the parser should allow a `in` keyword.\n#[derive(Debug, Clone, Copy, PartialEq, Eq)]\nstruct AllowIn(bool);\n\nimpl From<bool> for AllowIn {\n    fn from(allow: bool) -> Self {\n        Self(allow)\n    }\n}\n\n/// Boolean representing if the parser should allow a `return` keyword.\n#[derive(Debug, Clone, Copy, PartialEq, Eq)]\nstruct AllowReturn(bool);\n\nimpl From<bool> for AllowReturn {\n    fn from(allow: bool) -> Self {\n        Self(allow)\n    }\n}\n\n/// Boolean representing if the parser should allow a `default` keyword.\n#[derive(Debug, Clone, Copy, PartialEq, Eq)]\nstruct AllowDefault(bool);\n\nimpl From<bool> for AllowDefault {\n    fn from(allow: bool) -> Self {\n        Self(allow)\n    }\n}\n\n/// Parser for the ECMAScript language.\n///\n/// This parser implementation tries to be conformant to the most recent\n/// [ECMAScript language specification], and it also implements some legacy features like\n/// [labelled functions][label] or [duplicated block-level function definitions][block].\n///\n/// [spec]: https://tc39.es/ecma262/#sec-ecmascript-language-source-code\n/// [label]: https://tc39.es/ecma262/#sec-labelled-function-declarations\n/// [block]: https://tc39.es/ecma262/#sec-block-duplicates-allowed-static-semantics\n#[derive(Debug)]\npub struct Parser<'a, R> {\n    /// Path to the source being parsed.\n    #[allow(unused)] // Good to have for future improvements.\n    path: Option<&'a Path>,\n    /// Cursor of the parser, pointing to the lexer and used to get tokens for the parser.\n    cursor: Cursor<R>,\n}\n\nimpl<'a, R: ReadChar> Parser<'a, R> {\n    /// Create a new `Parser` with a `Source` as the input to parse.\n    pub fn new(source: Source<'a, R>) -> Self {\n        Self {\n            path: source.path,\n            cursor: Cursor::new(source.reader),\n        }\n    }\n\n    /// Parse the full input as a [ECMAScript Script][spec] into the boa AST representation without source text.\n    /// The resulting `Script` can be compiled into boa bytecode and executed in the boa vm.\n    ///\n    /// # Errors\n    ///\n    /// Will return `Err` on any parsing error, including invalid reads of the bytes being parsed.\n    ///\n    /// [spec]: https://tc39.es/ecma262/#prod-Script\n    pub fn parse_script(\n        &mut self,\n        scope: &Scope,\n        interner: &mut Interner,\n    ) -> ParseResult<boa_ast::Script> {\n        self.parse_script_with_source(scope, interner).map(|x| x.0)\n    }\n\n    /// Parse the full input as a [ECMAScript Script][spec] into the boa AST representation with source text.\n    /// The resulting `Script` can be compiled into boa bytecode and executed in the boa vm.\n    ///\n    /// # Errors\n    ///\n    /// Will return `Err` on any parsing error, including invalid reads of the bytes being parsed.\n    ///\n    /// [spec]: https://tc39.es/ecma262/#prod-Script\n    pub fn parse_script_with_source(\n        &mut self,\n        scope: &Scope,\n        interner: &mut Interner,\n    ) -> ParseResult<ScriptParseOutput> {\n        self.cursor.set_goal(InputElement::HashbangOrRegExp);\n        let (mut ast, source) = ScriptParser::new(false).parse(&mut self.cursor, interner)?;\n        if let Err(reason) = ast.analyze_scope(scope, interner) {\n            return Err(Error::scope_analysis(reason));\n        }\n        Ok((ast, source))\n    }\n\n    /// Parse the full input as an [ECMAScript Module][spec] into the boa AST representation without source text.\n    /// The resulting `ModuleItemList` can be compiled into boa bytecode and executed in the boa vm.\n    ///\n    /// # Errors\n    ///\n    /// Will return `Err` on any parsing error, including invalid reads of the bytes being parsed.\n    ///\n    /// [spec]: https://tc39.es/ecma262/#prod-Module\n    pub fn parse_module(\n        &mut self,\n        scope: &Scope,\n        interner: &mut Interner,\n    ) -> ParseResult<boa_ast::Module>\n    where\n        R: ReadChar,\n    {\n        self.parse_module_with_source(scope, interner).map(|x| x.0)\n    }\n\n    /// Parse the full input as an [ECMAScript Module][spec] into the boa AST representation with source text.\n    /// The resulting `ModuleItemList` can be compiled into boa bytecode and executed in the boa vm.\n    ///\n    /// # Errors\n    ///\n    /// Will return `Err` on any parsing error, including invalid reads of the bytes being parsed.\n    ///\n    /// [spec]: https://tc39.es/ecma262/#prod-Module\n    pub fn parse_module_with_source(\n        &mut self,\n        scope: &Scope,\n        interner: &mut Interner,\n    ) -> ParseResult<ModuleParseOutput>\n    where\n        R: ReadChar,\n    {\n        self.cursor.set_goal(InputElement::HashbangOrRegExp);\n        let (mut module, source) = ModuleParser.parse(&mut self.cursor, interner)?;\n        if let Err(reason) = module.analyze_scope(scope, interner) {\n            return Err(Error::scope_analysis(reason));\n        }\n        Ok((module, source))\n    }\n\n    /// [`19.2.1.1 PerformEval ( x, strictCaller, direct )`][spec]\n    ///\n    /// Parses the source text input of an `eval` call.\n    ///\n    /// # Errors\n    ///\n    /// Will return `Err` on any parsing error, including invalid reads of the bytes being parsed.\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-performeval\n    pub fn parse_eval(\n        &mut self,\n        direct: bool,\n        interner: &mut Interner,\n    ) -> ParseResult<ScriptParseOutput> {\n        self.cursor.set_goal(InputElement::HashbangOrRegExp);\n        ScriptParser::new(direct).parse(&mut self.cursor, interner)\n    }\n\n    /// Parses the full input as an [ECMAScript `FunctionBody`][spec] into the boa AST representation.\n    ///\n    /// # Errors\n    ///\n    /// Will return `Err` on any parsing error, including invalid reads of the bytes being parsed.\n    ///\n    /// [spec]: https://tc39.es/ecma262/#prod-FunctionBody\n    pub fn parse_function_body(\n        &mut self,\n        interner: &mut Interner,\n        allow_yield: bool,\n        allow_await: bool,\n    ) -> ParseResult<FunctionBody> {\n        let mut parser = FunctionStatementList::new(allow_yield, allow_await, \"function body\");\n        parser.parse_full_input(true);\n        parser.parse(&mut self.cursor, interner)\n    }\n\n    /// Parses the full input as an [ECMAScript `FormalParameterList`][spec] into the boa AST representation.\n    ///\n    /// # Errors\n    ///\n    /// Will return `Err` on any parsing error, including invalid reads of the bytes being parsed.\n    ///\n    /// [spec]: https://tc39.es/ecma262/#prod-FormalParameterList\n    pub fn parse_formal_parameters(\n        &mut self,\n        interner: &mut Interner,\n        allow_yield: bool,\n        allow_await: bool,\n    ) -> ParseResult<FormalParameterList> {\n        FormalParameters::new(allow_yield, allow_await).parse(&mut self.cursor, interner)\n    }\n}\n\nimpl<R> Parser<'_, R> {\n    /// Set the parser strict mode to true.\n    pub fn set_strict(&mut self)\n    where\n        R: ReadChar,\n    {\n        self.cursor.set_strict(true);\n    }\n\n    /// Set the parser JSON mode to true.\n    pub fn set_json_parse(&mut self)\n    where\n        R: ReadChar,\n    {\n        self.cursor.set_json_parse(true);\n    }\n\n    /// Set the unique identifier for the parser.\n    pub fn set_identifier(&mut self, identifier: u32)\n    where\n        R: ReadChar,\n    {\n        self.cursor.set_identifier(identifier);\n    }\n}\n\n/// Parses a full script.\n///\n/// More information:\n///  - [ECMAScript specification][spec]\n///\n/// [spec]: https://tc39.es/ecma262/#prod-Script\n#[derive(Debug, Clone, Copy)]\npub struct ScriptParser {\n    direct_eval: bool,\n}\n\nimpl ScriptParser {\n    /// Create a new `Script` parser.\n    #[inline]\n    const fn new(direct_eval: bool) -> Self {\n        Self { direct_eval }\n    }\n}\n\nimpl<R> TokenParser<R> for ScriptParser\nwhere\n    R: ReadChar,\n{\n    type Output = ScriptParseOutput;\n\n    fn parse(self, cursor: &mut Cursor<R>, interner: &mut Interner) -> ParseResult<Self::Output> {\n        let stmts =\n            ScriptBody::new(true, cursor.strict(), self.direct_eval).parse(cursor, interner)?;\n        let script = boa_ast::Script::new(stmts);\n\n        // It is a Syntax Error if the LexicallyDeclaredNames of ScriptBody contains any duplicate entries.\n        let mut lexical_names = FxHashSet::default();\n        for name in lexically_declared_names(&script) {\n            if !lexical_names.insert(name) {\n                return Err(Error::general(\n                    \"lexical name declared multiple times\",\n                    Position::new(1, 1),\n                ));\n            }\n        }\n\n        // It is a Syntax Error if any element of the LexicallyDeclaredNames of ScriptBody also occurs in the VarDeclaredNames of ScriptBody.\n        for name in var_declared_names(&script) {\n            if lexical_names.contains(&name) {\n                return Err(Error::general(\n                    \"lexical name declared multiple times\",\n                    Position::new(1, 1),\n                ));\n            }\n        }\n\n        let source = cursor.take_source();\n        Ok((script, source))\n    }\n}\n\n/// Parses a script body.\n///\n/// More information:\n///  - [ECMAScript specification][spec]\n///\n/// [spec]: https://tc39.es/ecma262/#prod-ScriptBody\n#[derive(Debug, Clone, Copy)]\npub struct ScriptBody {\n    directive_prologues: bool,\n    strict: bool,\n    direct_eval: bool,\n}\n\nimpl ScriptBody {\n    /// Create a new `ScriptBody` parser.\n    #[inline]\n    const fn new(directive_prologues: bool, strict: bool, direct_eval: bool) -> Self {\n        Self {\n            directive_prologues,\n            strict,\n            direct_eval,\n        }\n    }\n}\n\nimpl<R> TokenParser<R> for ScriptBody\nwhere\n    R: ReadChar,\n{\n    type Output = StatementList;\n\n    fn parse(self, cursor: &mut Cursor<R>, interner: &mut Interner) -> ParseResult<Self::Output> {\n        let (body, _end) = statement::StatementList::new(\n            false,\n            false,\n            false,\n            &[],\n            self.directive_prologues,\n            self.strict,\n        )\n        .parse(cursor, interner)?;\n\n        if !self.direct_eval {\n            // It is a Syntax Error if StatementList Contains super unless the source text containing super is eval\n            // code that is being processed by a direct eval.\n            // Additional early error rules for super within direct eval are defined in 19.2.1.1.\n            if contains(&body, ContainsSymbol::Super) {\n                return Err(Error::general(\"invalid super usage\", Position::new(1, 1)));\n            }\n            // It is a Syntax Error if StatementList Contains NewTarget unless the source text containing NewTarget\n            // is eval code that is being processed by a direct eval.\n            // Additional early error rules for NewTarget in direct eval are defined in 19.2.1.1.\n            if contains(&body, ContainsSymbol::NewTarget) {\n                return Err(Error::general(\n                    \"invalid new.target usage\",\n                    Position::new(1, 1),\n                ));\n            }\n\n            // It is a Syntax Error if AllPrivateIdentifiersValid of StatementList with\n            // argument « » is false unless the source text containing ScriptBody is\n            // eval code that is being processed by a direct eval.\n            if !all_private_identifiers_valid(&body, Vec::new()) {\n                return Err(Error::general(\n                    \"invalid private identifier usage\",\n                    Position::new(1, 1),\n                ));\n            }\n        }\n\n        if let Err(error) = check_labels(&body) {\n            return Err(Error::lex(LexError::Syntax(\n                error.message(interner).into(),\n                Position::new(1, 1),\n            )));\n        }\n\n        if contains_invalid_object_literal(&body) {\n            return Err(Error::lex(LexError::Syntax(\n                \"invalid object literal in script statement list\".into(),\n                Position::new(1, 1),\n            )));\n        }\n\n        Ok(body)\n    }\n}\n\n/// Parses a full module.\n///\n/// More information:\n///  - [ECMAScript specification][spec]\n///\n/// [spec]: https://tc39.es/ecma262/#prod-Module\n#[derive(Debug, Clone, Copy)]\nstruct ModuleParser;\n\nimpl<R> TokenParser<R> for ModuleParser\nwhere\n    R: ReadChar,\n{\n    type Output = ModuleParseOutput;\n\n    fn parse(self, cursor: &mut Cursor<R>, interner: &mut Interner) -> ParseResult<Self::Output> {\n        cursor.set_module();\n\n        let module = boa_ast::Module::new(ModuleItemList.parse(cursor, interner)?);\n\n        // It is a Syntax Error if the LexicallyDeclaredNames of ModuleItemList contains any duplicate entries.\n        let mut bindings = FxHashSet::default();\n        for name in lexically_declared_names(&module) {\n            if !bindings.insert(name) {\n                return Err(Error::general(\n                    format!(\n                        \"lexical name `{}` declared multiple times\",\n                        interner.resolve_expect(name)\n                    ),\n                    Position::new(1, 1),\n                ));\n            }\n        }\n\n        // It is a Syntax Error if any element of the LexicallyDeclaredNames of ModuleItemList also occurs in the\n        // VarDeclaredNames of ModuleItemList.\n        for name in var_declared_names(&module) {\n            if !bindings.insert(name) {\n                return Err(Error::general(\n                    format!(\n                        \"lexical name `{}` declared multiple times\",\n                        interner.resolve_expect(name)\n                    ),\n                    Position::new(1, 1),\n                ));\n            }\n        }\n\n        // It is a Syntax Error if the ExportedNames of ModuleItemList contains any duplicate entries.\n        {\n            let mut exported_names = FxHashSet::default();\n            for name in module.items().exported_names() {\n                if !exported_names.insert(name) {\n                    return Err(Error::general(\n                        format!(\n                            \"exported name `{}` declared multiple times\",\n                            interner.resolve_expect(name)\n                        ),\n                        Position::new(1, 1),\n                    ));\n                }\n            }\n        }\n\n        // It is a Syntax Error if any element of the ExportedBindings of ModuleItemList does not also occur in either\n        // the VarDeclaredNames of ModuleItemList, or the LexicallyDeclaredNames of ModuleItemList.\n        for name in module.items().exported_bindings() {\n            if !bindings.contains(&name) {\n                return Err(Error::general(\n                    format!(\n                        \"could not find the exported binding `{}` in the declared names of the module\",\n                        interner.resolve_expect(name)\n                    ),\n                    Position::new(1, 1),\n                ));\n            }\n        }\n\n        // It is a Syntax Error if ModuleItemList Contains super.\n        if contains(&module, ContainsSymbol::Super) {\n            return Err(Error::general(\n                \"module cannot contain `super` on the top-level\",\n                Position::new(1, 1),\n            ));\n        }\n\n        // It is a Syntax Error if ModuleItemList Contains NewTarget.\n        if contains(&module, ContainsSymbol::NewTarget) {\n            return Err(Error::general(\n                \"module cannot contain `new.target` on the top-level\",\n                Position::new(1, 1),\n            ));\n        }\n\n        // It is a Syntax Error if ContainsDuplicateLabels of ModuleItemList with argument « » is true.\n        // It is a Syntax Error if ContainsUndefinedBreakTarget of ModuleItemList with argument « » is true.\n        // It is a Syntax Error if ContainsUndefinedContinueTarget of ModuleItemList with arguments « » and « » is true.\n        check_labels(&module).map_err(|error| {\n            Error::lex(LexError::Syntax(\n                error.message(interner).into(),\n                Position::new(1, 1),\n            ))\n        })?;\n\n        // It is a Syntax Error if AllPrivateIdentifiersValid of ModuleItemList with argument « » is false.\n        if !all_private_identifiers_valid(&module, Vec::new()) {\n            return Err(Error::general(\n                \"invalid private identifier usage\",\n                Position::new(1, 1),\n            ));\n        }\n\n        let source = cursor.take_source();\n        Ok((module, source))\n    }\n}\n\n/// Helper to check if any parameter names are declared in the given list.\nfn name_in_lexically_declared_names(\n    bound_names: &[Sym],\n    lexical_names: &[Sym],\n    position: Position,\n    interner: &Interner,\n) -> ParseResult<()> {\n    for name in bound_names {\n        if lexical_names.contains(name) {\n            return Err(Error::general(\n                format!(\n                    \"formal parameter `{}` declared in lexically declared names\",\n                    interner.resolve_expect(*name)\n                ),\n                position,\n            ));\n        }\n    }\n    Ok(())\n}\n\n/// Trait to reduce boilerplate in the parser.\ntrait OrAbrupt<T> {\n    /// Will convert an `Ok(None)` to an [`Error::AbruptEnd`] or return the inner type if not.\n    fn or_abrupt(self) -> ParseResult<T>;\n}\n\nimpl<T> OrAbrupt<T> for ParseResult<Option<T>> {\n    fn or_abrupt(self) -> ParseResult<T> {\n        self?.ok_or(Error::AbruptEnd)\n    }\n}\n"
  },
  {
    "path": "core/parser/src/parser/statement/block/mod.rs",
    "content": "//! Block statement parsing.\n//!\n//! More information:\n//!  - [MDN documentation][mdn]\n//!  - [ECMAScript specification][spec]\n//!\n//! [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/block\n//! [spec]: https://tc39.es/ecma262/#sec-block\n\n#[cfg(test)]\nmod tests;\n\nuse crate::{\n    Error,\n    lexer::TokenKind,\n    parser::{\n        AllowAwait, AllowReturn, AllowYield, Cursor, OrAbrupt, ParseResult, TokenParser,\n        statement::StatementList,\n    },\n    source::ReadChar,\n};\nuse boa_ast::{\n    Punctuator, Spanned,\n    operations::{lexically_declared_names_legacy, var_declared_names},\n    statement,\n};\nuse boa_interner::Interner;\nuse rustc_hash::FxHashMap;\n\n/// The possible `TokenKind` which indicate the end of a block statement.\nconst BLOCK_BREAK_TOKENS: [TokenKind; 1] = [TokenKind::Punctuator(Punctuator::CloseBlock)];\n\n/// A `BlockStatement` is equivalent to a `Block`.\n///\n/// More information:\n///  - [ECMAScript specification][spec]\n///\n/// [spec]: https://tc39.es/ecma262/#prod-BlockStatement\npub(super) type BlockStatement = Block;\n\n/// Variable declaration list parsing.\n///\n/// More information:\n///  - [MDN documentation][mdn]\n///  - [ECMAScript specification][spec]\n///\n/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/block\n/// [spec]: https://tc39.es/ecma262/#prod-Block\n#[derive(Debug, Clone, Copy)]\npub(super) struct Block {\n    allow_yield: AllowYield,\n    allow_await: AllowAwait,\n    allow_return: AllowReturn,\n}\n\nimpl Block {\n    /// Creates a new `Block` parser.\n    pub(super) fn new<Y, A, R>(allow_yield: Y, allow_await: A, allow_return: R) -> Self\n    where\n        Y: Into<AllowYield>,\n        A: Into<AllowAwait>,\n        R: Into<AllowReturn>,\n    {\n        Self {\n            allow_yield: allow_yield.into(),\n            allow_await: allow_await.into(),\n            allow_return: allow_return.into(),\n        }\n    }\n}\n\nimpl<R> TokenParser<R> for Block\nwhere\n    R: ReadChar,\n{\n    type Output = statement::Block;\n\n    fn parse(self, cursor: &mut Cursor<R>, interner: &mut Interner) -> ParseResult<Self::Output> {\n        cursor.expect(Punctuator::OpenBlock, \"block\", interner)?;\n        if let Some(tk) = cursor.peek(0, interner)?\n            && tk.kind() == &TokenKind::Punctuator(Punctuator::CloseBlock)\n        {\n            cursor.advance(interner);\n            return Ok(statement::Block::from((vec![], cursor.linear_pos())));\n        }\n        let position = cursor.peek(0, interner).or_abrupt()?.span().start();\n        let (statement_list, _end) = StatementList::new(\n            self.allow_yield,\n            self.allow_await,\n            self.allow_return,\n            &BLOCK_BREAK_TOKENS,\n            false,\n            false,\n        )\n        .parse(cursor, interner)\n        .map(|(statement_list, end)| (statement::Block::from(statement_list), end))?;\n        cursor.expect(Punctuator::CloseBlock, \"block\", interner)?;\n\n        // It is a Syntax Error if the LexicallyDeclaredNames of StatementList contains any duplicate\n        // entries, unless the source text matched by this production is not strict mode code and the\n        // duplicate entries are only bound by FunctionDeclarations.\n        let mut lexical_names = FxHashMap::default();\n        for (name, is_fn) in lexically_declared_names_legacy(&statement_list) {\n            if let Some(is_fn_previous) = lexical_names.insert(name, is_fn) {\n                match (cursor.strict(), is_fn, is_fn_previous) {\n                    (false, true, true) => {}\n                    _ => {\n                        return Err(Error::general(\n                            \"lexical name declared multiple times\",\n                            position,\n                        ));\n                    }\n                }\n            }\n        }\n\n        // It is a Syntax Error if any element of the LexicallyDeclaredNames of StatementList also\n        // occurs in the VarDeclaredNames of StatementList.\n        for name in var_declared_names(&statement_list) {\n            if lexical_names.contains_key(&name) {\n                return Err(Error::general(\n                    \"lexical name declared in var names\",\n                    position,\n                ));\n            }\n        }\n\n        Ok(statement_list)\n    }\n}\n"
  },
  {
    "path": "core/parser/src/parser/statement/block/tests.rs",
    "content": "//! Block statement parsing tests.\n\nuse crate::parser::tests::check_script_parser;\nuse boa_ast::{\n    Declaration, Expression, Span, Statement, StatementList, StatementListItem,\n    declaration::{VarDeclaration, Variable},\n    expression::{\n        Call, Identifier,\n        literal::Literal,\n        operator::{\n            Assign, Update,\n            assign::AssignOp,\n            update::{UpdateOp, UpdateTarget},\n        },\n    },\n    function::{FormalParameterList, FunctionBody, FunctionDeclaration},\n    statement::{Block, Return},\n};\nuse boa_interner::Interner;\nuse boa_macros::utf16;\nuse indoc::indoc;\n\nconst PSEUDO_LINEAR_POS: boa_ast::LinearPosition = boa_ast::LinearPosition::new(0);\nconst EMPTY_LINEAR_SPAN: boa_ast::LinearSpan =\n    boa_ast::LinearSpan::new(PSEUDO_LINEAR_POS, PSEUDO_LINEAR_POS);\n\n/// Helper function to check a block.\n#[track_caller]\nfn check_block<B>(js: &str, block: B, interner: &mut Interner)\nwhere\n    B: Into<Box<[StatementListItem]>>,\n{\n    check_script_parser(\n        js,\n        vec![Statement::Block(Block::from((block.into(), PSEUDO_LINEAR_POS))).into()],\n        interner,\n    );\n}\n\n#[test]\nfn empty() {\n    check_block(\"{}\", vec![], &mut Interner::default());\n}\n\n#[test]\nfn non_empty() {\n    let interner = &mut Interner::default();\n    let a = interner.get_or_intern_static(\"a\", utf16!(\"a\"));\n    check_block(\n        indoc! {\"\n            {\n                var a = 10;\n                a++;\n            }\n        \"},\n        vec![\n            Statement::Var(VarDeclaration(\n                vec![Variable::from_identifier(\n                    Identifier::new(a, Span::new((2, 9), (2, 10))),\n                    Some(Literal::new(10, Span::new((2, 13), (2, 15))).into()),\n                )]\n                .try_into()\n                .unwrap(),\n            ))\n            .into(),\n            Statement::Expression(\n                Update::new(\n                    UpdateOp::IncrementPost,\n                    UpdateTarget::Identifier(Identifier::new(a, Span::new((3, 5), (3, 6)))),\n                    Span::new((3, 5), (3, 8)),\n                )\n                .into(),\n            )\n            .into(),\n        ],\n        interner,\n    );\n\n    let interner = &mut Interner::default();\n    let hello = interner.get_or_intern_static(\"hello\", utf16!(\"hello\"));\n    let a = interner.get_or_intern_static(\"a\", utf16!(\"a\"));\n    check_block(\n        indoc! {\"\n            {\n                function hello() {\n                    return 10\n                }\n\n                var a = hello();\n                a++;\n            }\n        \"},\n        vec![\n            Declaration::FunctionDeclaration(FunctionDeclaration::new(\n                Identifier::new(hello, Span::new((2, 14), (2, 19))),\n                FormalParameterList::default(),\n                FunctionBody::new(\n                    StatementList::new(\n                        [StatementListItem::Statement(\n                            Statement::Return(Return::new(Some(\n                                Literal::new(10, Span::new((3, 16), (3, 18))).into(),\n                            )))\n                            .into(),\n                        )],\n                        PSEUDO_LINEAR_POS,\n                        false,\n                    ),\n                    Span::new((2, 22), (4, 6)),\n                ),\n                EMPTY_LINEAR_SPAN,\n            ))\n            .into(),\n            Statement::Var(VarDeclaration(\n                vec![Variable::from_identifier(\n                    Identifier::new(a, Span::new((6, 9), (6, 10))),\n                    Some(\n                        Call::new(\n                            Identifier::new(hello, Span::new((6, 13), (6, 18))).into(),\n                            Box::default(),\n                            Span::new((6, 18), (6, 20)),\n                        )\n                        .into(),\n                    ),\n                )]\n                .try_into()\n                .unwrap(),\n            ))\n            .into(),\n            Statement::Expression(\n                Update::new(\n                    UpdateOp::IncrementPost,\n                    UpdateTarget::Identifier(Identifier::new(a, Span::new((7, 5), (7, 6)))),\n                    Span::new((7, 5), (7, 8)),\n                )\n                .into(),\n            )\n            .into(),\n        ],\n        interner,\n    );\n}\n\n#[test]\nfn hoisting() {\n    let interner = &mut Interner::default();\n    let hello = interner.get_or_intern_static(\"hello\", utf16!(\"hello\"));\n    let a = interner.get_or_intern_static(\"a\", utf16!(\"a\"));\n    check_block(\n        indoc! {\"\n            {\n                var a = hello();\n                a++;\n\n                function hello() { return 10 }\n            }\n        \"},\n        vec![\n            Statement::Var(VarDeclaration(\n                vec![Variable::from_identifier(\n                    Identifier::new(a, Span::new((2, 9), (2, 10))),\n                    Some(\n                        Call::new(\n                            Identifier::new(hello, Span::new((2, 13), (2, 18))).into(),\n                            Box::default(),\n                            Span::new((2, 18), (2, 20)),\n                        )\n                        .into(),\n                    ),\n                )]\n                .try_into()\n                .unwrap(),\n            ))\n            .into(),\n            Statement::Expression(\n                Update::new(\n                    UpdateOp::IncrementPost,\n                    UpdateTarget::Identifier(Identifier::new(a, Span::new((3, 5), (3, 6)))),\n                    Span::new((3, 5), (3, 8)),\n                )\n                .into(),\n            )\n            .into(),\n            Declaration::FunctionDeclaration(FunctionDeclaration::new(\n                Identifier::new(hello, Span::new((5, 14), (5, 19))),\n                FormalParameterList::default(),\n                FunctionBody::new(\n                    StatementList::new(\n                        [StatementListItem::Statement(\n                            Statement::Return(Return::new(Some(\n                                Literal::new(10, Span::new((5, 31), (5, 33))).into(),\n                            )))\n                            .into(),\n                        )],\n                        PSEUDO_LINEAR_POS,\n                        false,\n                    ),\n                    Span::new((5, 22), (5, 35)),\n                ),\n                EMPTY_LINEAR_SPAN,\n            ))\n            .into(),\n        ],\n        interner,\n    );\n\n    let interner = &mut Interner::default();\n    let a = interner.get_or_intern_static(\"a\", utf16!(\"a\"));\n    check_block(\n        indoc! {\"\n            {\n                a = 10;\n                a++;\n\n                var a;\n            }\n        \"},\n        vec![\n            Statement::Expression(Expression::from(Assign::new(\n                AssignOp::Assign,\n                Identifier::new(a, Span::new((2, 5), (2, 6))).into(),\n                Literal::new(10, Span::new((2, 9), (2, 11))).into(),\n            )))\n            .into(),\n            Statement::Expression(\n                Update::new(\n                    UpdateOp::IncrementPost,\n                    UpdateTarget::Identifier(Identifier::new(a, Span::new((3, 5), (3, 6)))),\n                    Span::new((3, 5), (3, 8)),\n                )\n                .into(),\n            )\n            .into(),\n            Statement::Var(VarDeclaration(\n                vec![Variable::from_identifier(\n                    Identifier::new(a, Span::new((5, 9), (5, 10))),\n                    None,\n                )]\n                .try_into()\n                .unwrap(),\n            ))\n            .into(),\n        ],\n        interner,\n    );\n}\n"
  },
  {
    "path": "core/parser/src/parser/statement/break_stm/mod.rs",
    "content": "//! Break expression parsing.\n//!\n//! More information:\n//!  - [MDN documentation][mdn]\n//!  - [ECMAScript specification][spec]\n//!\n//! [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/break\n//! [spec]: https://tc39.es/ecma262/#sec-break-statement\n\n#[cfg(test)]\nmod tests;\n\nuse crate::{\n    lexer::{Token, TokenKind},\n    parser::{\n        AllowAwait, AllowYield, ParseResult, TokenParser,\n        cursor::{Cursor, SemicolonResult},\n        expression::LabelIdentifier,\n    },\n    source::ReadChar,\n};\nuse boa_ast::{Keyword, Punctuator, statement::Break};\nuse boa_interner::Interner;\n\n/// Break statement parsing\n///\n/// More information:\n///  - [MDN documentation][mdn]\n///  - [ECMAScript specification][spec]\n///\n/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/break\n/// [spec]: https://tc39.es/ecma262/#prod-BreakStatement\n#[derive(Debug, Clone, Copy)]\npub(super) struct BreakStatement {\n    allow_yield: AllowYield,\n    allow_await: AllowAwait,\n}\n\nimpl BreakStatement {\n    /// Creates a new `BreakStatement` parser.\n    pub(super) fn new<Y, A>(allow_yield: Y, allow_await: A) -> Self\n    where\n        Y: Into<AllowYield>,\n        A: Into<AllowAwait>,\n    {\n        Self {\n            allow_yield: allow_yield.into(),\n            allow_await: allow_await.into(),\n        }\n    }\n}\n\nimpl<R> TokenParser<R> for BreakStatement\nwhere\n    R: ReadChar,\n{\n    type Output = Break;\n\n    fn parse(self, cursor: &mut Cursor<R>, interner: &mut Interner) -> ParseResult<Self::Output> {\n        cursor.expect((Keyword::Break, false), \"break statement\", interner)?;\n\n        let label = if let SemicolonResult::Found(tok) = cursor.peek_semicolon(interner)? {\n            if tok.map(Token::kind) == Some(&TokenKind::Punctuator(Punctuator::Semicolon)) {\n                cursor.advance(interner);\n            }\n\n            None\n        } else {\n            let label = LabelIdentifier::new(self.allow_yield, self.allow_await)\n                .parse(cursor, interner)?\n                .sym();\n            cursor.expect_semicolon(\"break statement\", interner)?;\n\n            Some(label)\n        };\n\n        Ok(Break::new(label))\n    }\n}\n"
  },
  {
    "path": "core/parser/src/parser/statement/break_stm/tests.rs",
    "content": "use crate::parser::tests::check_script_parser;\nuse boa_ast::{\n    Span, Statement, StatementListItem,\n    expression::literal::Literal,\n    statement::{Block, Break, Labelled, LabelledItem, WhileLoop},\n};\nuse boa_interner::{Interner, Sym};\nuse boa_macros::utf16;\nuse indoc::indoc;\n\nconst PSEUDO_LINEAR_POS: boa_ast::LinearPosition = boa_ast::LinearPosition::new(0);\n\nfn stmt_block_break_only(break_stmt: Break) -> Statement {\n    Block::from((\n        vec![StatementListItem::Statement(\n            Statement::Break(break_stmt).into(),\n        )],\n        PSEUDO_LINEAR_POS,\n    ))\n    .into()\n}\n\n#[test]\nfn inline() {\n    check_script_parser(\n        \"while (true) break;\",\n        vec![\n            Statement::WhileLoop(WhileLoop::new(\n                Literal::new(true, Span::new((1, 8), (1, 12))).into(),\n                Break::new(None).into(),\n            ))\n            .into(),\n        ],\n        &mut Interner::default(),\n    );\n}\n\n#[test]\nfn new_line() {\n    check_script_parser(\n        indoc! {\"\n            while (true)\n                break;\n        \"},\n        vec![\n            Statement::WhileLoop(WhileLoop::new(\n                Literal::new(true, Span::new((1, 8), (1, 12))).into(),\n                Break::new(None).into(),\n            ))\n            .into(),\n        ],\n        &mut Interner::default(),\n    );\n}\n\n#[test]\nfn inline_block_semicolon_insertion() {\n    check_script_parser(\n        \"while (true) {break}\",\n        vec![\n            Statement::WhileLoop(WhileLoop::new(\n                Literal::new(true, Span::new((1, 8), (1, 12))).into(),\n                stmt_block_break_only(Break::new(None)),\n            ))\n            .into(),\n        ],\n        &mut Interner::default(),\n    );\n}\n\n#[test]\nfn new_line_semicolon_insertion() {\n    let interner = &mut Interner::default();\n    check_script_parser(\n        indoc! {\"\n            test: while (true) {\n                break test\n            }\n        \"},\n        vec![\n            Statement::Labelled(Labelled::new(\n                LabelledItem::Statement(Statement::WhileLoop(WhileLoop::new(\n                    Literal::new(true, Span::new((1, 14), (1, 18))).into(),\n                    stmt_block_break_only(Break::new(Some(\n                        interner.get_or_intern_static(\"test\", utf16!(\"test\")),\n                    ))),\n                ))),\n                interner.get_or_intern_static(\"test\", utf16!(\"test\")),\n            ))\n            .into(),\n        ],\n        interner,\n    );\n}\n\n#[test]\nfn inline_block() {\n    check_script_parser(\n        \"while (true) {break;}\",\n        vec![\n            Statement::WhileLoop(WhileLoop::new(\n                Literal::new(true, Span::new((1, 8), (1, 12))).into(),\n                stmt_block_break_only(Break::new(None)),\n            ))\n            .into(),\n        ],\n        &mut Interner::default(),\n    );\n}\n\n#[test]\nfn new_line_block() {\n    let interner = &mut Interner::default();\n    check_script_parser(\n        indoc! {\"\n            test: while (true) {\n                break test;\n            }\n        \"},\n        vec![\n            Statement::Labelled(Labelled::new(\n                LabelledItem::Statement(Statement::WhileLoop(WhileLoop::new(\n                    Literal::new(true, Span::new((1, 14), (1, 18))).into(),\n                    stmt_block_break_only(Break::new(Some(\n                        interner.get_or_intern_static(\"test\", utf16!(\"test\")),\n                    ))),\n                ))),\n                interner.get_or_intern_static(\"test\", utf16!(\"test\")),\n            ))\n            .into(),\n        ],\n        interner,\n    );\n}\n\n#[test]\nfn reserved_label() {\n    let interner = &mut Interner::default();\n    check_script_parser(\n        indoc! {\"\n            await: while (true) {\n                break await;\n            }\n        \"},\n        vec![\n            Statement::Labelled(Labelled::new(\n                LabelledItem::Statement(Statement::WhileLoop(WhileLoop::new(\n                    Literal::new(true, Span::new((1, 15), (1, 19))).into(),\n                    stmt_block_break_only(Break::new(Some(Sym::AWAIT))),\n                ))),\n                Sym::AWAIT,\n            ))\n            .into(),\n        ],\n        interner,\n    );\n\n    let interner = &mut Interner::default();\n    check_script_parser(\n        indoc! {\"\n            yield: while (true) {\n                break yield;\n            }\n        \"},\n        vec![\n            Statement::Labelled(Labelled::new(\n                LabelledItem::Statement(Statement::WhileLoop(WhileLoop::new(\n                    Literal::new(true, Span::new((1, 15), (1, 19))).into(),\n                    stmt_block_break_only(Break::new(Some(Sym::YIELD))),\n                ))),\n                Sym::YIELD,\n            ))\n            .into(),\n        ],\n        interner,\n    );\n}\n\n#[test]\nfn new_line_block_empty() {\n    check_script_parser(\n        indoc! {\"\n            while (true) {\n                break;\n            }\n        \"},\n        vec![\n            Statement::WhileLoop(WhileLoop::new(\n                Literal::new(true, Span::new((1, 8), (1, 12))).into(),\n                stmt_block_break_only(Break::new(None)),\n            ))\n            .into(),\n        ],\n        &mut Interner::default(),\n    );\n}\n\n#[test]\nfn new_line_block_empty_semicolon_insertion() {\n    check_script_parser(\n        indoc! {\"\n            while (true) {\n                break\n            }\n        \"},\n        vec![\n            Statement::WhileLoop(WhileLoop::new(\n                Literal::new(true, Span::new((1, 8), (1, 12))).into(),\n                stmt_block_break_only(Break::new(None)),\n            ))\n            .into(),\n        ],\n        &mut Interner::default(),\n    );\n}\n"
  },
  {
    "path": "core/parser/src/parser/statement/continue_stm/mod.rs",
    "content": "//! Continue expression parsing.\n//!\n//! More information:\n//!  - [MDN documentation][mdn]\n//!  - [ECMAScript specification][spec]\n//!\n//! [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/continue\n//! [spec]: https://tc39.es/ecma262/#sec-continue-statement\n\n#[cfg(test)]\nmod tests;\n\nuse crate::{\n    lexer::TokenKind,\n    parser::{\n        AllowAwait, AllowYield, ParseResult, TokenParser,\n        cursor::{Cursor, SemicolonResult},\n        expression::LabelIdentifier,\n    },\n    source::ReadChar,\n};\nuse boa_ast::{Keyword, Punctuator, statement::Continue};\nuse boa_interner::Interner;\n\n/// For statement parsing\n///\n/// More information:\n///  - [MDN documentation][mdn]\n///  - [ECMAScript specification][spec]\n///\n/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/continue\n/// [spec]: https://tc39.es/ecma262/#prod-ContinueStatement\n#[derive(Debug, Clone, Copy)]\npub(super) struct ContinueStatement {\n    allow_yield: AllowYield,\n    allow_await: AllowAwait,\n}\n\nimpl ContinueStatement {\n    /// Creates a new `ContinueStatement` parser.\n    pub(super) fn new<Y, A>(allow_yield: Y, allow_await: A) -> Self\n    where\n        Y: Into<AllowYield>,\n        A: Into<AllowAwait>,\n    {\n        Self {\n            allow_yield: allow_yield.into(),\n            allow_await: allow_await.into(),\n        }\n    }\n}\n\nimpl<R> TokenParser<R> for ContinueStatement\nwhere\n    R: ReadChar,\n{\n    type Output = Continue;\n\n    fn parse(self, cursor: &mut Cursor<R>, interner: &mut Interner) -> ParseResult<Self::Output> {\n        cursor.expect((Keyword::Continue, false), \"continue statement\", interner)?;\n\n        let label = if let SemicolonResult::Found(tok) = cursor.peek_semicolon(interner)? {\n            if let Some(token) = tok {\n                if token.kind() == &TokenKind::Punctuator(Punctuator::Semicolon) {\n                    cursor.advance(interner);\n                } else if token.kind() == &TokenKind::LineTerminator\n                    && let Some(token) = cursor.peek(0, interner)?\n                    && token.kind() == &TokenKind::Punctuator(Punctuator::Semicolon)\n                {\n                    cursor.advance(interner);\n                }\n            }\n\n            None\n        } else {\n            let label = LabelIdentifier::new(self.allow_yield, self.allow_await)\n                .parse(cursor, interner)?\n                .sym();\n            cursor.expect_semicolon(\"continue statement\", interner)?;\n\n            Some(label)\n        };\n\n        Ok(Continue::new(label))\n    }\n}\n"
  },
  {
    "path": "core/parser/src/parser/statement/continue_stm/tests.rs",
    "content": "use crate::parser::tests::check_script_parser;\nuse boa_ast::{\n    Span, Statement, StatementListItem,\n    expression::literal::Literal,\n    statement::{Block, Continue, Labelled, LabelledItem, WhileLoop},\n};\nuse boa_interner::{Interner, Sym};\nuse boa_macros::utf16;\nuse indoc::indoc;\n\nconst PSEUDO_LINEAR_POS: boa_ast::LinearPosition = boa_ast::LinearPosition::new(0);\n\nfn stmt_block_continue_only(continue_stmt: Continue) -> Statement {\n    Block::from((\n        vec![StatementListItem::Statement(\n            Statement::Continue(continue_stmt).into(),\n        )],\n        PSEUDO_LINEAR_POS,\n    ))\n    .into()\n}\n\n#[test]\nfn inline() {\n    check_script_parser(\n        \"while (true) continue;\",\n        vec![\n            Statement::WhileLoop(WhileLoop::new(\n                Literal::new(true, Span::new((1, 8), (1, 12))).into(),\n                Continue::new(None).into(),\n            ))\n            .into(),\n        ],\n        &mut Interner::default(),\n    );\n}\n\n#[test]\nfn new_line() {\n    check_script_parser(\n        indoc! {\"\n            while (true)\n                continue;\n        \"},\n        vec![\n            Statement::WhileLoop(WhileLoop::new(\n                Literal::new(true, Span::new((1, 8), (1, 12))).into(),\n                Continue::new(None).into(),\n            ))\n            .into(),\n        ],\n        &mut Interner::default(),\n    );\n}\n\n#[test]\nfn inline_block_semicolon_insertion() {\n    check_script_parser(\n        \"while (true) {continue}\",\n        vec![\n            Statement::WhileLoop(WhileLoop::new(\n                Literal::new(true, Span::new((1, 8), (1, 12))).into(),\n                stmt_block_continue_only(Continue::new(None)),\n            ))\n            .into(),\n        ],\n        &mut Interner::default(),\n    );\n}\n\n#[test]\nfn new_line_semicolon_insertion() {\n    let interner = &mut Interner::default();\n    check_script_parser(\n        indoc! {\"\n            test: while (true) {\n                continue test\n            }\n        \"},\n        vec![\n            Statement::Labelled(Labelled::new(\n                LabelledItem::Statement(Statement::WhileLoop(WhileLoop::new(\n                    Literal::new(true, Span::new((1, 14), (1, 18))).into(),\n                    stmt_block_continue_only(Continue::new(Some(\n                        interner.get_or_intern_static(\"test\", utf16!(\"test\")),\n                    ))),\n                ))),\n                interner.get_or_intern_static(\"test\", utf16!(\"test\")),\n            ))\n            .into(),\n        ],\n        interner,\n    );\n}\n\n#[test]\nfn inline_block() {\n    check_script_parser(\n        \"while (true) {continue;}\",\n        vec![\n            Statement::WhileLoop(WhileLoop::new(\n                Literal::new(true, Span::new((1, 8), (1, 12))).into(),\n                stmt_block_continue_only(Continue::new(None)),\n            ))\n            .into(),\n        ],\n        &mut Interner::default(),\n    );\n}\n\n#[test]\nfn new_line_block() {\n    let interner = &mut Interner::default();\n    check_script_parser(\n        indoc! {\"\n            test: while (true) {\n                continue test;\n            }\n        \"},\n        vec![\n            Statement::Labelled(Labelled::new(\n                LabelledItem::Statement(Statement::WhileLoop(WhileLoop::new(\n                    Literal::new(true, Span::new((1, 14), (1, 18))).into(),\n                    stmt_block_continue_only(Continue::new(Some(\n                        interner.get_or_intern_static(\"test\", utf16!(\"test\")),\n                    ))),\n                ))),\n                interner.get_or_intern_static(\"test\", utf16!(\"test\")),\n            ))\n            .into(),\n        ],\n        interner,\n    );\n}\n\n#[test]\nfn reserved_label() {\n    let interner = &mut Interner::default();\n    check_script_parser(\n        indoc! {\"\n            await: while (true) {\n                continue await;\n            }\n        \"},\n        vec![\n            Statement::Labelled(Labelled::new(\n                LabelledItem::Statement(Statement::WhileLoop(WhileLoop::new(\n                    Literal::new(true, Span::new((1, 15), (1, 19))).into(),\n                    stmt_block_continue_only(Continue::new(Some(Sym::AWAIT))),\n                ))),\n                Sym::AWAIT,\n            ))\n            .into(),\n        ],\n        interner,\n    );\n\n    let interner = &mut Interner::default();\n    check_script_parser(\n        indoc! {\"\n            yield: while (true) {\n                continue yield;\n            }\n        \"},\n        vec![\n            Statement::Labelled(Labelled::new(\n                LabelledItem::Statement(Statement::WhileLoop(WhileLoop::new(\n                    Literal::new(true, Span::new((1, 15), (1, 19))).into(),\n                    stmt_block_continue_only(Continue::new(Some(Sym::YIELD))),\n                ))),\n                Sym::YIELD,\n            ))\n            .into(),\n        ],\n        interner,\n    );\n}\n\n#[test]\nfn new_line_block_empty() {\n    check_script_parser(\n        indoc! {\"\n            while (true) {\n                continue;\n            }\n        \"},\n        vec![\n            Statement::WhileLoop(WhileLoop::new(\n                Literal::new(true, Span::new((1, 8), (1, 12))).into(),\n                stmt_block_continue_only(Continue::new(None)),\n            ))\n            .into(),\n        ],\n        &mut Interner::default(),\n    );\n}\n\n#[test]\nfn new_line_block_empty_semicolon_insertion() {\n    check_script_parser(\n        indoc! {\"\n            while (true) {\n                continue\n            }\n        \"},\n        vec![\n            Statement::WhileLoop(WhileLoop::new(\n                Literal::new(true, Span::new((1, 8), (1, 12))).into(),\n                stmt_block_continue_only(Continue::new(None)),\n            ))\n            .into(),\n        ],\n        &mut Interner::default(),\n    );\n}\n"
  },
  {
    "path": "core/parser/src/parser/statement/declaration/export.rs",
    "content": "//! Export declaration parsing\n//!\n//! This parses `export` declarations.\n//!\n//! More information:\n//! - [MDN documentation][mdn]\n//!  - [ECMAScript specification][spec]\n//!\n//! [spec]: https://tc39.es/ecma262/#sec-exports\n//! [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/export\n\nuse crate::{\n    lexer::{TokenKind, token::ContainsEscapeSequence},\n    parser::{\n        Error, OrAbrupt, ParseResult, TokenParser,\n        cursor::Cursor,\n        expression::AssignmentExpression,\n        statement::{declaration::ClassDeclaration, variable::VariableStatement},\n    },\n    source::ReadChar,\n};\nuse boa_ast::{\n    Keyword, Punctuator, Spanned,\n    declaration::{ExportDeclaration as AstExportDeclaration, ReExportKind},\n};\nuse boa_interner::{Interner, Sym};\n\nuse super::{\n    Declaration, FromClause, FunctionDeclaration, WithClause,\n    hoistable::{AsyncFunctionDeclaration, AsyncGeneratorDeclaration, GeneratorDeclaration},\n};\n\n/// Parses an export declaration.\n///\n/// More information:\n///  - [ECMAScript specification][spec]\n///\n/// [spec]: https://tc39.es/ecma262/#prod-ExportDeclaration\n#[derive(Debug, Clone, Copy)]\npub(in crate::parser) struct ExportDeclaration;\n\nimpl<R> TokenParser<R> for ExportDeclaration\nwhere\n    R: ReadChar,\n{\n    type Output = AstExportDeclaration;\n\n    fn parse(self, cursor: &mut Cursor<R>, interner: &mut Interner) -> ParseResult<Self::Output> {\n        cursor.expect((Keyword::Export, false), \"export declaration\", interner)?;\n\n        let tok = cursor.peek(0, interner).or_abrupt()?;\n        let span = tok.span();\n\n        let export_clause: Self::Output = match tok.kind() {\n            TokenKind::Punctuator(Punctuator::Mul) => {\n                cursor.advance(interner);\n\n                let next = cursor.peek(0, interner).or_abrupt()?;\n\n                let (kind, specifier) = match next.kind() {\n                    TokenKind::IdentifierName((Sym::AS, _)) => {\n                        cursor.advance(interner);\n                        let tok = cursor.next(interner).or_abrupt()?;\n\n                        let alias = match tok.kind() {\n                            TokenKind::StringLiteral((export_name, _))\n                            | TokenKind::IdentifierName((export_name, _)) => *export_name,\n                            TokenKind::Keyword((kw, _)) => kw.to_sym(),\n                            _ => {\n                                return Err(Error::expected(\n                                    [\"identifier name\".to_owned(), \"string literal\".to_owned()],\n                                    tok.to_string(interner),\n                                    tok.span(),\n                                    \"export declaration\",\n                                ));\n                            }\n                        };\n\n                        let specifier =\n                            FromClause::new(\"export declaration\").parse(cursor, interner)?;\n\n                        (ReExportKind::Namespaced { name: Some(alias) }, specifier)\n                    }\n                    TokenKind::IdentifierName((Sym::FROM, _)) => {\n                        let specifier =\n                            FromClause::new(\"export declaration\").parse(cursor, interner)?;\n\n                        (ReExportKind::Namespaced { name: None }, specifier)\n                    }\n                    _ => {\n                        return Err(Error::expected(\n                            [\"as\".to_owned(), \"from\".to_owned()],\n                            next.to_string(interner),\n                            next.span(),\n                            \"export declaration\",\n                        ));\n                    }\n                };\n\n                let attributes = WithClause::new(\"export declaration\").parse(cursor, interner)?;\n                cursor.expect_semicolon(\"star re-export\", interner)?;\n\n                AstExportDeclaration::ReExport {\n                    kind,\n                    specifier,\n                    attributes,\n                }\n            }\n            TokenKind::Punctuator(Punctuator::OpenBlock) => {\n                let names = NamedExports.parse(cursor, interner)?;\n\n                let next = cursor.peek(0, interner).or_abrupt()?;\n\n                if matches!(\n                    next.kind(),\n                    TokenKind::IdentifierName((Sym::FROM, ContainsEscapeSequence(false)))\n                ) {\n                    let specifier =\n                        FromClause::new(\"export declaration\").parse(cursor, interner)?;\n                    let attributes =\n                        WithClause::new(\"export declaration\").parse(cursor, interner)?;\n\n                    cursor.expect_semicolon(\"named re-exports\", interner)?;\n\n                    AstExportDeclaration::ReExport {\n                        kind: ReExportKind::Named { names },\n                        specifier,\n                        attributes,\n                    }\n                } else {\n                    cursor.expect_semicolon(\"named exports\", interner)?;\n\n                    for specifier in &*names {\n                        let name = specifier.private_name();\n\n                        if specifier.string_literal() {\n                            let name = interner.resolve_expect(name);\n                            return Err(Error::general(\n                                format!(\n                                    \"local referenced binding `{name}` cannot be a string literal\",\n                                ),\n                                span.start(),\n                            ));\n                        }\n\n                        if name == Sym::AWAIT\n                            || name.is_reserved_identifier()\n                            || name.is_strict_reserved_identifier()\n                        {\n                            let name = interner.resolve_expect(name);\n                            return Err(Error::general(\n                                format!(\n                                    \"local referenced binding `{name}` cannot be a reserved word\",\n                                ),\n                                span.start(),\n                            ));\n                        }\n                    }\n\n                    AstExportDeclaration::List(names)\n                }\n            }\n            TokenKind::Keyword((Keyword::Var, false)) => VariableStatement::new(false, true)\n                .parse(cursor, interner)\n                .map(AstExportDeclaration::VarStatement)?,\n            TokenKind::Keyword((Keyword::Default, false)) => {\n                cursor.advance(interner);\n\n                let tok = cursor.peek(0, interner).or_abrupt()?;\n\n                match tok.kind() {\n                    TokenKind::Keyword((Keyword::Function, false)) => {\n                        let next_token = cursor.peek(1, interner).or_abrupt()?;\n                        if next_token.kind() == &TokenKind::Punctuator(Punctuator::Mul) {\n                            AstExportDeclaration::DefaultGeneratorDeclaration(\n                                GeneratorDeclaration::new(false, true, true)\n                                    .parse(cursor, interner)?,\n                            )\n                        } else {\n                            AstExportDeclaration::DefaultFunctionDeclaration(\n                                FunctionDeclaration::new(false, true, true)\n                                    .parse(cursor, interner)?,\n                            )\n                        }\n                    }\n                    TokenKind::Keyword((Keyword::Async, false)) => {\n                        let next_token = cursor.peek(2, interner).or_abrupt()?;\n                        if next_token.kind() == &TokenKind::Punctuator(Punctuator::Mul) {\n                            AstExportDeclaration::DefaultAsyncGeneratorDeclaration(\n                                AsyncGeneratorDeclaration::new(false, true, true)\n                                    .parse(cursor, interner)?,\n                            )\n                        } else {\n                            AstExportDeclaration::DefaultAsyncFunctionDeclaration(\n                                AsyncFunctionDeclaration::new(false, true, true)\n                                    .parse(cursor, interner)?,\n                            )\n                        }\n                    }\n                    TokenKind::Keyword((Keyword::Class, false)) => {\n                        AstExportDeclaration::DefaultClassDeclaration(\n                            ClassDeclaration::new(false, true, true)\n                                .parse(cursor, interner)?\n                                .into(),\n                        )\n                    }\n                    _ => {\n                        let expr =\n                            AssignmentExpression::new(true, false, true).parse(cursor, interner)?;\n\n                        cursor.expect_semicolon(\"default expression export\", interner)?;\n\n                        AstExportDeclaration::DefaultAssignmentExpression(expr)\n                    }\n                }\n            }\n            _ => AstExportDeclaration::Declaration(\n                Declaration::new(false, true).parse(cursor, interner)?,\n            ),\n        };\n\n        Ok(export_clause)\n    }\n}\n\n/// Parses a named export list.\n///\n/// More information:\n///  - [ECMAScript specification][spec]\n///\n/// [spec]: https://tc39.es/ecma262/#prod-NamedExports\n#[derive(Debug, Clone, Copy)]\nstruct NamedExports;\n\nimpl<R> TokenParser<R> for NamedExports\nwhere\n    R: ReadChar,\n{\n    type Output = Box<[boa_ast::declaration::ExportSpecifier]>;\n\n    fn parse(self, cursor: &mut Cursor<R>, interner: &mut Interner) -> ParseResult<Self::Output> {\n        cursor.expect(Punctuator::OpenBlock, \"export declaration\", interner)?;\n\n        let mut list = Vec::new();\n\n        loop {\n            let tok = cursor.peek(0, interner).or_abrupt()?;\n            match tok.kind() {\n                TokenKind::Punctuator(Punctuator::CloseBlock) => {\n                    cursor.advance(interner);\n                    break;\n                }\n                TokenKind::Punctuator(Punctuator::Comma) => {\n                    if list.is_empty() {\n                        return Err(Error::expected(\n                            [\n                                Punctuator::CloseBlock.to_string(),\n                                \"string literal\".to_owned(),\n                                \"identifier\".to_owned(),\n                            ],\n                            tok.to_string(interner),\n                            tok.span(),\n                            \"export declaration\",\n                        ));\n                    }\n                    cursor.advance(interner);\n                }\n                TokenKind::StringLiteral(_)\n                | TokenKind::IdentifierName(_)\n                | TokenKind::Keyword(_) => {\n                    list.push(ExportSpecifier.parse(cursor, interner)?);\n                }\n                _ => {\n                    return Err(Error::expected(\n                        [\n                            Punctuator::CloseBlock.to_string(),\n                            Punctuator::Comma.to_string(),\n                        ],\n                        tok.to_string(interner),\n                        tok.span(),\n                        \"export declaration\",\n                    ));\n                }\n            }\n        }\n\n        Ok(list.into_boxed_slice())\n    }\n}\n\n/// Parses a module export name.\n///\n/// More information:\n///  - [ECMAScript specification][spec]\n///\n/// [spec]: https://tc39.es/ecma262/#prod-ModuleExportName\n#[derive(Debug, Clone, Copy)]\npub(super) struct ModuleExportName;\n\nimpl<R> TokenParser<R> for ModuleExportName\nwhere\n    R: ReadChar,\n{\n    type Output = (Sym, bool);\n\n    fn parse(self, cursor: &mut Cursor<R>, interner: &mut Interner) -> ParseResult<Self::Output> {\n        let tok = cursor.next(interner).or_abrupt()?;\n\n        match tok.kind() {\n            TokenKind::StringLiteral((ident, _)) => {\n                if interner.resolve_expect(*ident).utf8().is_none() {\n                    return Err(Error::general(\n                        \"import specifiers don't allow unpaired surrogates\",\n                        tok.span().end(),\n                    ));\n                }\n                Ok((*ident, true))\n            }\n            TokenKind::IdentifierName((ident, _)) => Ok((*ident, false)),\n            TokenKind::Keyword((kw, _)) => Ok((kw.to_sym(), false)),\n            TokenKind::BooleanLiteral((b, _)) => {\n                Ok((if *b { Sym::TRUE } else { Sym::FALSE }, false))\n            }\n            TokenKind::NullLiteral(_) => Ok((Sym::NULL, false)),\n            _ => Err(Error::expected(\n                [\"identifier\".to_owned(), \"string literal\".to_owned()],\n                tok.to_string(interner),\n                tok.span(),\n                \"export specifier parsing\",\n            )),\n        }\n    }\n}\n\n/// Parses an export specifier.\n///\n/// More information:\n///  - [ECMAScript specification][spec]\n///\n/// [spec]: https://tc39.es/ecma262/#prod-ExportSpecifier\n#[derive(Debug, Clone, Copy)]\nstruct ExportSpecifier;\n\nimpl<R> TokenParser<R> for ExportSpecifier\nwhere\n    R: ReadChar,\n{\n    type Output = boa_ast::declaration::ExportSpecifier;\n\n    fn parse(self, cursor: &mut Cursor<R>, interner: &mut Interner) -> ParseResult<Self::Output> {\n        let (inner_name, string_literal) = ModuleExportName.parse(cursor, interner)?;\n\n        if cursor\n            .next_if(TokenKind::identifier(Sym::AS), interner)?\n            .is_some()\n        {\n            let (export_name, _) = ModuleExportName.parse(cursor, interner)?;\n            Ok(boa_ast::declaration::ExportSpecifier::new(\n                export_name,\n                inner_name,\n                string_literal,\n            ))\n        } else {\n            Ok(boa_ast::declaration::ExportSpecifier::new(\n                inner_name,\n                inner_name,\n                string_literal,\n            ))\n        }\n    }\n}\n"
  },
  {
    "path": "core/parser/src/parser/statement/declaration/hoistable/async_function_decl/mod.rs",
    "content": "#[cfg(test)]\nmod tests;\n\nuse crate::{\n    parser::{\n        AllowAwait, AllowDefault, AllowYield, Cursor, ParseResult, TokenParser,\n        statement::declaration::hoistable::{CallableDeclaration, parse_callable_declaration},\n    },\n    source::ReadChar,\n};\nuse boa_ast::{Keyword, function::AsyncFunctionDeclaration as AsyncFunctionDeclarationNode};\nuse boa_interner::Interner;\n\n/// Async Function declaration parsing.\n///\n/// More information:\n///  - [MDN documentation][mdn]\n///  - [ECMAScript specification][spec]\n///\n/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/async_function\n/// [spec]: https://tc39.es/ecma262/#prod-AsyncFunctionDeclaration\n#[derive(Debug, Clone, Copy)]\npub(in crate::parser) struct AsyncFunctionDeclaration {\n    allow_yield: AllowYield,\n    allow_await: AllowAwait,\n    is_default: AllowDefault,\n}\n\nimpl AsyncFunctionDeclaration {\n    /// Creates a new `AsyncFunctionDeclaration` parser.\n    pub(in crate::parser) fn new<Y, A, D>(allow_yield: Y, allow_await: A, is_default: D) -> Self\n    where\n        Y: Into<AllowYield>,\n        A: Into<AllowAwait>,\n        D: Into<AllowDefault>,\n    {\n        Self {\n            allow_yield: allow_yield.into(),\n            allow_await: allow_await.into(),\n            is_default: is_default.into(),\n        }\n    }\n}\n\nimpl CallableDeclaration for AsyncFunctionDeclaration {\n    fn error_context(&self) -> &'static str {\n        \"async function declaration\"\n    }\n    fn is_default(&self) -> bool {\n        self.is_default.0\n    }\n    fn name_allow_yield(&self) -> bool {\n        self.allow_yield.0\n    }\n    fn name_allow_await(&self) -> bool {\n        self.allow_await.0\n    }\n    fn parameters_allow_yield(&self) -> bool {\n        false\n    }\n    fn parameters_allow_await(&self) -> bool {\n        true\n    }\n    fn body_allow_yield(&self) -> bool {\n        false\n    }\n    fn body_allow_await(&self) -> bool {\n        true\n    }\n    fn parameters_await_is_early_error(&self) -> bool {\n        true\n    }\n}\n\nimpl<R> TokenParser<R> for AsyncFunctionDeclaration\nwhere\n    R: ReadChar,\n{\n    type Output = AsyncFunctionDeclarationNode;\n\n    fn parse(self, cursor: &mut Cursor<R>, interner: &mut Interner) -> ParseResult<Self::Output> {\n        let async_token = cursor.expect(\n            (Keyword::Async, false),\n            \"async function declaration\",\n            interner,\n        )?;\n        let start_linear_span = async_token.linear_span();\n\n        cursor.peek_expect_no_lineterminator(0, \"async function declaration\", interner)?;\n        cursor.expect(\n            (Keyword::Function, false),\n            \"async function declaration\",\n            interner,\n        )?;\n\n        let result = parse_callable_declaration(&self, cursor, interner)?;\n        let span = start_linear_span.union(result.2.linear_pos_end());\n\n        Ok(AsyncFunctionDeclarationNode::new(\n            result.0, result.1, result.2, span,\n        ))\n    }\n}\n"
  },
  {
    "path": "core/parser/src/parser/statement/declaration/hoistable/async_function_decl/tests.rs",
    "content": "use crate::parser::tests::check_script_parser;\nuse boa_ast::{\n    Declaration, LinearPosition, LinearSpan, Span, StatementList,\n    expression::Identifier,\n    function::{AsyncFunctionDeclaration, FormalParameterList, FunctionBody},\n};\nuse boa_interner::{Interner, Sym};\nuse boa_macros::utf16;\n\nconst EMPTY_LINEAR_SPAN: LinearSpan =\n    LinearSpan::new(LinearPosition::new(0), LinearPosition::new(0));\n\n/// Async function declaration parsing.\n#[test]\nfn async_function_declaration() {\n    let interner = &mut Interner::default();\n    check_script_parser(\n        \"async function hello() {}\",\n        vec![\n            Declaration::AsyncFunctionDeclaration(AsyncFunctionDeclaration::new(\n                Identifier::new(\n                    interner.get_or_intern_static(\"hello\", utf16!(\"hello\")),\n                    Span::new((1, 16), (1, 21)),\n                ),\n                FormalParameterList::default(),\n                FunctionBody::new(StatementList::default(), Span::new((1, 24), (1, 26))),\n                EMPTY_LINEAR_SPAN,\n            ))\n            .into(),\n        ],\n        interner,\n    );\n}\n\n/// Async function declaration parsing with keywords.\n#[test]\nfn async_function_declaration_keywords() {\n    let interner = &mut Interner::default();\n    check_script_parser(\n        \"async function yield() {}\",\n        vec![\n            Declaration::AsyncFunctionDeclaration(AsyncFunctionDeclaration::new(\n                Identifier::new(Sym::YIELD, Span::new((1, 16), (1, 21))),\n                FormalParameterList::default(),\n                FunctionBody::new(StatementList::default(), Span::new((1, 24), (1, 26))),\n                EMPTY_LINEAR_SPAN,\n            ))\n            .into(),\n        ],\n        interner,\n    );\n\n    let interner = &mut Interner::default();\n    check_script_parser(\n        \"async function await() {}\",\n        vec![\n            Declaration::AsyncFunctionDeclaration(AsyncFunctionDeclaration::new(\n                Identifier::new(Sym::AWAIT, Span::new((1, 16), (1, 21))),\n                FormalParameterList::default(),\n                FunctionBody::new(StatementList::default(), Span::new((1, 24), (1, 26))),\n                EMPTY_LINEAR_SPAN,\n            ))\n            .into(),\n        ],\n        interner,\n    );\n}\n"
  },
  {
    "path": "core/parser/src/parser/statement/declaration/hoistable/async_generator_decl/mod.rs",
    "content": "//! Async Generator Declaration parsing\n//!\n//! Implements `TokenParser` for `AsyncGeneratorDeclaration`on and outputs an `AsyncGeneratorDecl`\n//! ast node.\n\n#[cfg(test)]\nmod tests;\n\nuse crate::{\n    parser::{\n        AllowAwait, AllowDefault, AllowYield, Cursor, ParseResult, TokenParser,\n        statement::declaration::hoistable::{CallableDeclaration, parse_callable_declaration},\n    },\n    source::ReadChar,\n};\nuse boa_ast::{\n    Keyword, Punctuator, function::AsyncGeneratorDeclaration as AsyncGeneratorDeclarationNode,\n};\nuse boa_interner::Interner;\n\n/// Async Generator Declaration Parser\n///\n/// More information:\n/// - [ECMAScript specification][spec]\n///\n/// [spec]: https://tc39.es/ecma262/#prod-AsyncGeneratorDeclaration\n#[derive(Debug, Clone, Copy)]\npub(in crate::parser) struct AsyncGeneratorDeclaration {\n    allow_yield: AllowYield,\n    allow_await: AllowAwait,\n    is_default: AllowDefault,\n}\n\nimpl AsyncGeneratorDeclaration {\n    /// Creates a new `AsyncGeneratorDeclaration` parser.\n    pub(in crate::parser) fn new<Y, A, D>(allow_yield: Y, allow_await: A, is_default: D) -> Self\n    where\n        Y: Into<AllowYield>,\n        A: Into<AllowAwait>,\n        D: Into<AllowDefault>,\n    {\n        Self {\n            allow_yield: allow_yield.into(),\n            allow_await: allow_await.into(),\n            is_default: is_default.into(),\n        }\n    }\n}\n\nimpl CallableDeclaration for AsyncGeneratorDeclaration {\n    fn error_context(&self) -> &'static str {\n        \"async generator declaration\"\n    }\n\n    fn is_default(&self) -> bool {\n        self.is_default.0\n    }\n\n    fn name_allow_yield(&self) -> bool {\n        self.allow_yield.0\n    }\n\n    fn name_allow_await(&self) -> bool {\n        self.allow_await.0\n    }\n\n    fn parameters_allow_yield(&self) -> bool {\n        true\n    }\n\n    fn parameters_allow_await(&self) -> bool {\n        true\n    }\n\n    fn body_allow_yield(&self) -> bool {\n        true\n    }\n\n    fn body_allow_await(&self) -> bool {\n        true\n    }\n    fn parameters_await_is_early_error(&self) -> bool {\n        true\n    }\n    fn parameters_yield_is_early_error(&self) -> bool {\n        true\n    }\n}\n\nimpl<R> TokenParser<R> for AsyncGeneratorDeclaration\nwhere\n    R: ReadChar,\n{\n    type Output = AsyncGeneratorDeclarationNode;\n\n    fn parse(self, cursor: &mut Cursor<R>, interner: &mut Interner) -> ParseResult<Self::Output> {\n        let async_token = cursor.expect(\n            (Keyword::Async, false),\n            \"async generator declaration\",\n            interner,\n        )?;\n        let start_linear_span = async_token.linear_span();\n\n        cursor.peek_expect_no_lineterminator(0, \"async generator declaration\", interner)?;\n        cursor.expect(\n            (Keyword::Function, false),\n            \"async generator declaration\",\n            interner,\n        )?;\n        cursor.expect(Punctuator::Mul, \"async generator declaration\", interner)?;\n\n        let result = parse_callable_declaration(&self, cursor, interner)?;\n        let span = start_linear_span.union(result.2.linear_pos_end());\n\n        Ok(AsyncGeneratorDeclarationNode::new(\n            result.0, result.1, result.2, span,\n        ))\n    }\n}\n"
  },
  {
    "path": "core/parser/src/parser/statement/declaration/hoistable/async_generator_decl/tests.rs",
    "content": "use crate::parser::tests::check_script_parser;\nuse boa_ast::{\n    Declaration, LinearPosition, LinearSpan, Span, StatementList,\n    expression::Identifier,\n    function::{AsyncGeneratorDeclaration, FormalParameterList, FunctionBody},\n};\nuse boa_interner::Interner;\nuse boa_macros::utf16;\n\n#[test]\nfn async_generator_function_declaration() {\n    let interner = &mut Interner::default();\n    check_script_parser(\n        \"async function* gen() {}\",\n        vec![\n            Declaration::AsyncGeneratorDeclaration(AsyncGeneratorDeclaration::new(\n                Identifier::new(\n                    interner.get_or_intern_static(\"gen\", utf16!(\"gen\")),\n                    Span::new((1, 17), (1, 20)),\n                ),\n                FormalParameterList::default(),\n                FunctionBody::new(StatementList::default(), Span::new((1, 23), (1, 25))),\n                LinearSpan::new(LinearPosition::default(), LinearPosition::default()),\n            ))\n            .into(),\n        ],\n        interner,\n    );\n}\n"
  },
  {
    "path": "core/parser/src/parser/statement/declaration/hoistable/class_decl/mod.rs",
    "content": "#[cfg(test)]\nmod tests;\n\nuse crate::{\n    Error,\n    lexer::{Error as LexError, TokenKind, token::ContainsEscapeSequence},\n    parser::{\n        AllowAwait, AllowDefault, AllowYield, Cursor, OrAbrupt, ParseResult, TokenParser,\n        expression::{\n            AssignmentExpression, AsyncGeneratorMethod, AsyncMethod, BindingIdentifier,\n            GeneratorMethod, LeftHandSideExpression, PropertyName,\n        },\n        function::{FUNCTION_BREAK_TOKENS, FunctionBody, UniqueFormalParameters},\n        statement::StatementList,\n    },\n    source::ReadChar,\n};\nuse ast::{\n    function::FunctionBody as AstFunctionBody,\n    function::PrivateName,\n    operations::{\n        check_labels, contains_invalid_object_literal, lexically_declared_names, var_declared_names,\n    },\n    property::MethodDefinitionKind,\n};\nuse boa_ast::{\n    self as ast, Expression, Keyword, Position, Punctuator, Span, Spanned,\n    expression::Identifier,\n    function::{\n        self, ClassDeclaration as ClassDeclarationNode, ClassElementName, ClassFieldDefinition,\n        ClassMethodDefinition, FormalParameterList, FunctionExpression, PrivateFieldDefinition,\n        StaticBlockBody,\n    },\n    operations::{ContainsSymbol, contains, contains_arguments},\n};\nuse boa_interner::{Interner, Sym};\nuse boa_macros::utf16;\nuse rustc_hash::{FxHashMap, FxHashSet};\n\n/// Class declaration parsing.\n///\n/// More information:\n///  - [MDN documentation][mdn]\n///  - [ECMAScript specification][spec]\n///\n/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/class\n/// [spec]: https://tc39.es/ecma262/#prod-ClassDeclaration\n#[derive(Debug, Clone, Copy)]\npub(in crate::parser) struct ClassDeclaration {\n    allow_yield: AllowYield,\n    allow_await: AllowAwait,\n    is_default: AllowDefault,\n}\n\nimpl ClassDeclaration {\n    /// Creates a new `ClassDeclaration` parser.\n    pub(in crate::parser) fn new<Y, A, D>(allow_yield: Y, allow_await: A, is_default: D) -> Self\n    where\n        Y: Into<AllowYield>,\n        A: Into<AllowAwait>,\n        D: Into<AllowDefault>,\n    {\n        Self {\n            allow_yield: allow_yield.into(),\n            allow_await: allow_await.into(),\n            is_default: is_default.into(),\n        }\n    }\n}\n\nimpl<R> TokenParser<R> for ClassDeclaration\nwhere\n    R: ReadChar,\n{\n    type Output = ClassDeclarationNode;\n\n    fn parse(self, cursor: &mut Cursor<R>, interner: &mut Interner) -> ParseResult<Self::Output> {\n        let span = cursor\n            .expect((Keyword::Class, false), \"class declaration\", interner)?\n            .span();\n        let strict = cursor.strict();\n        cursor.set_strict(true);\n\n        let token = cursor.peek(0, interner).or_abrupt()?;\n        let name = match token.kind() {\n            TokenKind::IdentifierName(_)\n            | TokenKind::Keyword((Keyword::Yield | Keyword::Await, _)) => {\n                BindingIdentifier::new(self.allow_yield, self.allow_await)\n                    .parse(cursor, interner)?\n            }\n            TokenKind::Keyword((k, _)) if !k.to_sym().is_reserved_identifier() => {\n                BindingIdentifier::new(self.allow_yield, self.allow_await)\n                    .parse(cursor, interner)?\n            }\n            _ if self.is_default.0 => Identifier::new(Sym::DEFAULT, span),\n            _ => {\n                return Err(Error::unexpected(\n                    token.to_string(interner),\n                    token.span(),\n                    \"expected class identifier\",\n                ));\n            }\n        };\n        cursor.set_strict(strict);\n\n        let (super_ref, constructor, elements, _end) =\n            ClassTail::new(name, self.allow_yield, self.allow_await).parse(cursor, interner)?;\n\n        Ok(ClassDeclarationNode::new(\n            name,\n            super_ref,\n            constructor,\n            elements.into_boxed_slice(),\n        ))\n    }\n}\n\n/// Class Tail parsing.\n///\n/// More information:\n///  - [ECMAScript specification][spec]\n///\n/// [spec]: https://tc39.es/ecma262/#prod-ClassTail\n#[derive(Debug, Clone, Copy)]\npub(in crate::parser) struct ClassTail {\n    name: Option<Identifier>,\n    allow_yield: AllowYield,\n    allow_await: AllowAwait,\n}\n\nimpl ClassTail {\n    /// Creates a new `ClassTail` parser.\n    pub(in crate::parser) fn new<N, Y, A>(name: N, allow_yield: Y, allow_await: A) -> Self\n    where\n        N: Into<Option<Identifier>>,\n        Y: Into<AllowYield>,\n        A: Into<AllowAwait>,\n    {\n        Self {\n            name: name.into(),\n            allow_yield: allow_yield.into(),\n            allow_await: allow_await.into(),\n        }\n    }\n}\n\nimpl<R> TokenParser<R> for ClassTail\nwhere\n    R: ReadChar,\n{\n    type Output = (\n        Option<Expression>,\n        Option<FunctionExpression>,\n        Vec<function::ClassElement>,\n        Position,\n    );\n\n    fn parse(self, cursor: &mut Cursor<R>, interner: &mut Interner) -> ParseResult<Self::Output> {\n        let token = cursor.peek(0, interner).or_abrupt()?;\n        let super_ref = match token.kind() {\n            TokenKind::Keyword((Keyword::Extends, true)) => {\n                return Err(Error::general(\n                    \"Keyword must not contain escaped characters\",\n                    token.span().start(),\n                ));\n            }\n            TokenKind::Keyword((Keyword::Extends, false)) => Some(\n                ClassHeritage::new(self.allow_yield, self.allow_await).parse(cursor, interner)?,\n            ),\n            _ => None,\n        };\n\n        cursor.expect(Punctuator::OpenBlock, \"class tail\", interner)?;\n\n        // Temporarily disable strict mode because \"strict\" may be parsed as a keyword.\n        let strict = cursor.strict();\n        cursor.set_strict(false);\n        let token = cursor.peek(0, interner).or_abrupt()?;\n        let token_span_end = token.span().end();\n        let is_close_block = token.kind() == &TokenKind::Punctuator(Punctuator::CloseBlock);\n        cursor.set_strict(strict);\n\n        if is_close_block {\n            cursor.advance(interner);\n            Ok((super_ref, None, Vec::new(), token_span_end))\n        } else {\n            let body_start = cursor.peek(0, interner).or_abrupt()?.span().start();\n            let (constructor, elements) =\n                ClassBody::new(self.name, self.allow_yield, self.allow_await)\n                    .parse(cursor, interner)?;\n            let end = cursor\n                .expect(Punctuator::CloseBlock, \"class tail\", interner)?\n                .span()\n                .start();\n\n            if super_ref.is_none()\n                && let Some(constructor) = &constructor\n                && contains(constructor, ContainsSymbol::SuperCall)\n            {\n                return Err(Error::lex(LexError::Syntax(\n                    \"invalid super usage\".into(),\n                    body_start,\n                )));\n            }\n\n            Ok((super_ref, constructor, elements, end))\n        }\n    }\n}\n\n/// `ClassHeritage` parsing.\n///\n/// More information:\n///  - [ECMAScript specification][spec]\n///\n/// [spec]: https://tc39.es/ecma262/#prod-ClassHeritage\n#[derive(Debug, Clone, Copy)]\npub(in crate::parser) struct ClassHeritage {\n    allow_yield: AllowYield,\n    allow_await: AllowAwait,\n}\n\nimpl ClassHeritage {\n    /// Creates a new `ClassHeritage` parser.\n    pub(in crate::parser) fn new<Y, A>(allow_yield: Y, allow_await: A) -> Self\n    where\n        Y: Into<AllowYield>,\n        A: Into<AllowAwait>,\n    {\n        Self {\n            allow_yield: allow_yield.into(),\n            allow_await: allow_await.into(),\n        }\n    }\n}\n\nimpl<R> TokenParser<R> for ClassHeritage\nwhere\n    R: ReadChar,\n{\n    type Output = Expression;\n\n    fn parse(self, cursor: &mut Cursor<R>, interner: &mut Interner) -> ParseResult<Self::Output> {\n        cursor.expect(\n            TokenKind::Keyword((Keyword::Extends, false)),\n            \"class heritage\",\n            interner,\n        )?;\n\n        let strict = cursor.strict();\n        cursor.set_strict(true);\n        let lhs = LeftHandSideExpression::new(self.allow_yield, self.allow_await)\n            .parse(cursor, interner)?\n            .try_into_expression()?;\n        cursor.set_strict(strict);\n\n        Ok(lhs)\n    }\n}\n\n/// `ClassBody` parsing.\n///\n/// More information:\n///  - [ECMAScript specification][spec]\n///\n/// [spec]: https://tc39.es/ecma262/#prod-ClassBody\n#[derive(Debug, Clone, Copy)]\npub(in crate::parser) struct ClassBody {\n    name: Option<Identifier>,\n    allow_yield: AllowYield,\n    allow_await: AllowAwait,\n}\n\nimpl ClassBody {\n    /// Creates a new `ClassBody` parser.\n    pub(in crate::parser) fn new<N, Y, A>(name: N, allow_yield: Y, allow_await: A) -> Self\n    where\n        N: Into<Option<Identifier>>,\n        Y: Into<AllowYield>,\n        A: Into<AllowAwait>,\n    {\n        Self {\n            name: name.into(),\n            allow_yield: allow_yield.into(),\n            allow_await: allow_await.into(),\n        }\n    }\n}\n\nimpl<R> TokenParser<R> for ClassBody\nwhere\n    R: ReadChar,\n{\n    type Output = (Option<FunctionExpression>, Vec<function::ClassElement>);\n\n    fn parse(self, cursor: &mut Cursor<R>, interner: &mut Interner) -> ParseResult<Self::Output> {\n        let mut constructor = None;\n        let mut elements = Vec::new();\n        let mut private_elements_names = FxHashMap::default();\n\n        // The identifier \"static\" is forbidden in strict mode but used as a keyword in classes.\n        // Because of this, strict mode has to temporarily be disabled while parsing class field names.\n        let strict = cursor.strict();\n        cursor.set_strict(false);\n        loop {\n            let token = cursor.peek(0, interner).or_abrupt()?;\n            let position = token.span().start();\n            let (parsed_constructor, element) = match token.kind() {\n                TokenKind::Punctuator(Punctuator::CloseBlock) => break,\n                _ => ClassElement::new(self.name, self.allow_yield, self.allow_await)\n                    .parse(cursor, interner)?,\n            };\n            if let Some(c) = parsed_constructor {\n                if constructor.is_some() {\n                    return Err(Error::general(\n                        \"a class may only have one constructor\",\n                        position,\n                    ));\n                }\n                constructor = Some(c);\n            }\n            let Some(element) = element else {\n                continue;\n            };\n\n            match &element {\n                function::ClassElement::MethodDefinition(m) => {\n                    // It is a Syntax Error if PropName of MethodDefinition is not \"constructor\" and HasDirectSuper of MethodDefinition is true.\n                    if let ClassElementName::PropertyName(name) = m.name()\n                        && contains(name, ContainsSymbol::SuperCall)\n                    {\n                        return Err(Error::lex(LexError::Syntax(\n                            \"invalid super call usage\".into(),\n                            position,\n                        )));\n                    }\n                    if contains(m.parameters(), ContainsSymbol::SuperCall)\n                        || contains(m.body(), ContainsSymbol::SuperCall)\n                    {\n                        return Err(Error::lex(LexError::Syntax(\n                            \"invalid super call usage\".into(),\n                            position,\n                        )));\n                    }\n\n                    if let ClassElementName::PrivateName(name) = m.name() {\n                        match m.kind() {\n                            MethodDefinitionKind::Get => {\n                                match private_elements_names.get(&name.description()) {\n                                    Some(PrivateElement::StaticSetter) if m.is_static() => {\n                                        private_elements_names.insert(\n                                            name.description(),\n                                            PrivateElement::StaticValue,\n                                        );\n                                    }\n                                    Some(PrivateElement::Setter) if !m.is_static() => {\n                                        private_elements_names\n                                            .insert(name.description(), PrivateElement::Value);\n                                    }\n                                    Some(_) => {\n                                        return Err(Error::general(\n                                            \"private identifier has already been declared\",\n                                            position,\n                                        ));\n                                    }\n                                    None => {\n                                        private_elements_names.insert(\n                                            name.description(),\n                                            if m.is_static() {\n                                                PrivateElement::StaticGetter\n                                            } else {\n                                                PrivateElement::Getter\n                                            },\n                                        );\n                                    }\n                                }\n                            }\n                            MethodDefinitionKind::Set => {\n                                match private_elements_names.get(&name.description()) {\n                                    Some(PrivateElement::StaticGetter) if m.is_static() => {\n                                        private_elements_names.insert(\n                                            name.description(),\n                                            PrivateElement::StaticValue,\n                                        );\n                                    }\n                                    Some(PrivateElement::Getter) if !m.is_static() => {\n                                        private_elements_names\n                                            .insert(name.description(), PrivateElement::Value);\n                                    }\n                                    Some(_) => {\n                                        return Err(Error::general(\n                                            \"private identifier has already been declared\",\n                                            position,\n                                        ));\n                                    }\n                                    None => {\n                                        private_elements_names.insert(\n                                            name.description(),\n                                            if m.is_static() {\n                                                PrivateElement::StaticSetter\n                                            } else {\n                                                PrivateElement::Setter\n                                            },\n                                        );\n                                    }\n                                }\n                            }\n                            _ => {\n                                if private_elements_names\n                                    .insert(\n                                        name.description(),\n                                        if m.is_static() {\n                                            PrivateElement::StaticValue\n                                        } else {\n                                            PrivateElement::Value\n                                        },\n                                    )\n                                    .is_some()\n                                {\n                                    return Err(Error::general(\n                                        \"private identifier has already been declared\",\n                                        position,\n                                    ));\n                                }\n                            }\n                        }\n                    }\n                }\n                function::ClassElement::PrivateFieldDefinition(field) => {\n                    if let Some(node) = field.initializer()\n                        && contains(node, ContainsSymbol::SuperCall)\n                    {\n                        return Err(Error::lex(LexError::Syntax(\n                            \"invalid super usage\".into(),\n                            position,\n                        )));\n                    }\n                    if private_elements_names\n                        .insert(field.name().description(), PrivateElement::Value)\n                        .is_some()\n                    {\n                        return Err(Error::general(\n                            \"private identifier has already been declared\",\n                            position,\n                        ));\n                    }\n                }\n                function::ClassElement::PrivateStaticFieldDefinition(field) => {\n                    if let Some(node) = field.initializer()\n                        && contains(node, ContainsSymbol::SuperCall)\n                    {\n                        return Err(Error::lex(LexError::Syntax(\n                            \"invalid super usage\".into(),\n                            position,\n                        )));\n                    }\n                    if private_elements_names\n                        .insert(field.name().description(), PrivateElement::StaticValue)\n                        .is_some()\n                    {\n                        return Err(Error::general(\n                            \"private identifier has already been declared\",\n                            position,\n                        ));\n                    }\n                }\n                function::ClassElement::FieldDefinition(field)\n                | function::ClassElement::StaticFieldDefinition(field) => {\n                    if let Some(field) = field.initializer()\n                        && contains(field, ContainsSymbol::SuperCall)\n                    {\n                        return Err(Error::lex(LexError::Syntax(\n                            \"invalid super usage\".into(),\n                            position,\n                        )));\n                    }\n                }\n                function::ClassElement::StaticBlock(_) => {}\n            }\n            elements.push(element);\n        }\n\n        cursor.set_strict(strict);\n\n        Ok((constructor, elements))\n    }\n}\n\n/// Representation of private object elements.\n#[derive(Debug, PartialEq)]\npub(crate) enum PrivateElement {\n    Value,\n    Getter,\n    Setter,\n    StaticValue,\n    StaticSetter,\n    StaticGetter,\n}\n\n/// `ClassElement` parsing.\n///\n/// More information:\n///  - [ECMAScript specification][spec]\n///\n/// [spec]: https://tc39.es/ecma262/#prod-ClassElement\n#[derive(Debug, Clone, Copy)]\npub(in crate::parser) struct ClassElement {\n    name: Option<Identifier>,\n    allow_yield: AllowYield,\n    allow_await: AllowAwait,\n}\n\nimpl ClassElement {\n    /// Creates a new `ClassElement` parser.\n    pub(in crate::parser) fn new<N, Y, A>(name: N, allow_yield: Y, allow_await: A) -> Self\n    where\n        N: Into<Option<Identifier>>,\n        Y: Into<AllowYield>,\n        A: Into<AllowAwait>,\n    {\n        Self {\n            name: name.into(),\n            allow_yield: allow_yield.into(),\n            allow_await: allow_await.into(),\n        }\n    }\n}\n\nimpl<R> TokenParser<R> for ClassElement\nwhere\n    R: ReadChar,\n{\n    type Output = (Option<FunctionExpression>, Option<function::ClassElement>);\n\n    fn parse(self, cursor: &mut Cursor<R>, interner: &mut Interner) -> ParseResult<Self::Output> {\n        let token = cursor.peek(0, interner).or_abrupt()?;\n        let r#static = match token.kind() {\n            TokenKind::Punctuator(Punctuator::Semicolon) => {\n                cursor.advance(interner);\n                return Ok((None, None));\n            }\n            TokenKind::IdentifierName((Sym::STATIC, ContainsEscapeSequence(contains_escape))) => {\n                let contains_escape = *contains_escape;\n                let token = cursor.peek(1, interner).or_abrupt()?;\n                match token.kind() {\n                    TokenKind::IdentifierName(_)\n                    | TokenKind::StringLiteral(_)\n                    | TokenKind::NumericLiteral(_)\n                    | TokenKind::Keyword(_)\n                    | TokenKind::NullLiteral(_)\n                    | TokenKind::BooleanLiteral(_)\n                    | TokenKind::PrivateIdentifier(_)\n                    | TokenKind::Punctuator(\n                        Punctuator::OpenBracket | Punctuator::Mul | Punctuator::OpenBlock,\n                    ) => {\n                        if contains_escape {\n                            return Err(Error::general(\n                                \"keyword must not contain escaped characters\",\n                                token.span().start(),\n                            ));\n                        }\n                        // this \"static\" is a keyword.\n                        cursor.advance(interner);\n                        true\n                    }\n                    _ => false,\n                }\n            }\n            _ => false,\n        };\n\n        let is_keyword = !matches!(\n            cursor.peek(1, interner).or_abrupt()?.kind(),\n            TokenKind::Punctuator(\n                Punctuator::Assign\n                    | Punctuator::CloseBlock\n                    | Punctuator::OpenParen\n                    | Punctuator::Semicolon\n            )\n        );\n\n        let token = cursor.peek(0, interner).or_abrupt()?;\n        let start_linear_span = token.linear_span();\n        let start_linear_pos = start_linear_span.start();\n\n        let position = token.span().start();\n        let element = match token.kind() {\n            TokenKind::IdentifierName((Sym::CONSTRUCTOR, _)) if !r#static => {\n                cursor.advance(interner);\n                let strict = cursor.strict();\n                cursor.set_strict(true);\n\n                let parameters =\n                    UniqueFormalParameters::new(false, false).parse(cursor, interner)?;\n                let body =\n                    FunctionBody::new(false, false, \"class constructor\").parse(cursor, interner)?;\n                cursor.set_strict(strict);\n\n                let span = Some(start_linear_span.union(body.linear_pos_end()));\n\n                let function_span_end = body.span().end();\n                return Ok((\n                    Some(FunctionExpression::new(\n                        self.name,\n                        parameters,\n                        body,\n                        span,\n                        false,\n                        Span::new(position, function_span_end),\n                    )),\n                    None,\n                ));\n            }\n            TokenKind::Punctuator(Punctuator::OpenBlock) if r#static => {\n                cursor.advance(interner);\n                let (statement_list, end) = if let Some(token) =\n                    cursor.next_if(TokenKind::Punctuator(Punctuator::CloseBlock), interner)?\n                {\n                    (ast::StatementList::default(), token.span().end())\n                } else {\n                    let strict = cursor.strict();\n                    cursor.set_strict(true);\n                    let position = cursor.peek(0, interner).or_abrupt()?.span().start();\n                    let (statement_list, _end) =\n                        StatementList::new(false, true, false, &FUNCTION_BREAK_TOKENS, false, true)\n                            .parse(cursor, interner)?;\n\n                    let mut lexical_names = FxHashSet::default();\n\n                    // It is a Syntax Error if the LexicallyDeclaredNames of\n                    // ClassStaticBlockStatementList contains any duplicate entries.\n                    for name in &lexically_declared_names(&statement_list) {\n                        if !lexical_names.insert(*name) {\n                            return Err(Error::general(\n                                \"lexical name declared multiple times\",\n                                position,\n                            ));\n                        }\n                    }\n\n                    // It is a Syntax Error if any element of the LexicallyDeclaredNames of\n                    // ClassStaticBlockStatementList also occurs in the VarDeclaredNames of\n                    // ClassStaticBlockStatementList.\n                    for name in var_declared_names(&statement_list) {\n                        if lexical_names.contains(&name) {\n                            return Err(Error::general(\n                                \"lexical name declared in var names\",\n                                position,\n                            ));\n                        }\n                    }\n\n                    // It is a Syntax Error if ContainsDuplicateLabels of\n                    // ClassStaticBlockStatementList with argument « » is true.\n                    // It is a Syntax Error if ContainsUndefinedBreakTarget of\n                    // ClassStaticBlockStatementList with argument « » is true.\n                    // It is a Syntax Error if ContainsUndefinedContinueTarget of\n                    // ClassStaticBlockStatementList with arguments « » and « » is true.\n                    check_labels(&statement_list).map_err(|error| {\n                        Error::lex(LexError::Syntax(error.message(interner).into(), position))\n                    })?;\n\n                    // It is a Syntax Error if ContainsArguments of ClassStaticBlockStatementList is true.\n                    if contains_arguments(&statement_list) {\n                        return Err(Error::general(\n                            \"'arguments' not allowed in class static block\",\n                            position,\n                        ));\n                    }\n\n                    // It is a Syntax Error if ClassStaticBlockStatementList Contains SuperCall is true.\n                    if contains(&statement_list, ContainsSymbol::SuperCall) {\n                        return Err(Error::general(\"invalid super usage\", position));\n                    }\n\n                    // It is a Syntax Error if ClassStaticBlockStatementList Contains await is true.\n                    if contains(&statement_list, ContainsSymbol::AwaitExpression) {\n                        return Err(Error::general(\"invalid await usage\", position));\n                    }\n\n                    if contains_invalid_object_literal(&statement_list) {\n                        return Err(Error::lex(LexError::Syntax(\n                            \"invalid object literal in class static block statement list\".into(),\n                            position,\n                        )));\n                    }\n\n                    let end = cursor\n                        .expect(\n                            TokenKind::Punctuator(Punctuator::CloseBlock),\n                            \"class definition\",\n                            interner,\n                        )?\n                        .span()\n                        .end();\n\n                    cursor.set_strict(strict);\n\n                    (statement_list, end)\n                };\n                function::ClassElement::StaticBlock(StaticBlockBody::new(AstFunctionBody::new(\n                    statement_list,\n                    Span::new(position, end),\n                )))\n            }\n            TokenKind::Punctuator(Punctuator::Mul) => {\n                let token = cursor.peek(1, interner).or_abrupt()?;\n                let name_position = token.span().start();\n                if !r#static && let TokenKind::IdentifierName((Sym::CONSTRUCTOR, _)) = token.kind()\n                {\n                    return Err(Error::general(\n                        \"class constructor may not be a generator method\",\n                        token.span().start(),\n                    ));\n                }\n                let strict = cursor.strict();\n                cursor.set_strict(true);\n                let (class_element_name, params, body) =\n                    GeneratorMethod::new(self.allow_yield, self.allow_await)\n                        .parse(cursor, interner)?;\n                cursor.set_strict(strict);\n\n                let name = match class_element_name {\n                    ClassElementName::PropertyName(name) => {\n                        if r#static && name.literal().map(Identifier::sym) == Some(Sym::PROTOTYPE) {\n                            return Err(Error::general(\n                                \"class may not have static method definitions named 'prototype'\",\n                                name_position,\n                            ));\n                        }\n                        ClassElementName::PropertyName(name)\n                    }\n                    ClassElementName::PrivateName(name) => {\n                        if name.description() == Sym::CONSTRUCTOR {\n                            return Err(Error::general(\n                                \"class constructor may not be a private method\",\n                                name_position,\n                            ));\n                        }\n                        ClassElementName::PrivateName(name)\n                    }\n                };\n                function::ClassElement::MethodDefinition(ClassMethodDefinition::new(\n                    name,\n                    params,\n                    body,\n                    MethodDefinitionKind::Generator,\n                    r#static,\n                    start_linear_pos,\n                ))\n            }\n            TokenKind::Keyword((Keyword::Async, true)) if is_keyword => {\n                return Err(Error::general(\n                    \"Keyword must not contain escaped characters\",\n                    token.span().start(),\n                ));\n            }\n            TokenKind::Keyword((Keyword::Async, false)) if is_keyword => {\n                cursor.advance(interner);\n                cursor.peek_expect_no_lineterminator(0, \"Async object methods\", interner)?;\n                let token = cursor.peek(0, interner).or_abrupt()?;\n                match token.kind() {\n                    TokenKind::Punctuator(Punctuator::Mul) => {\n                        let token = cursor.peek(1, interner).or_abrupt()?;\n                        let name_position = token.span().start();\n                        match token.kind() {\n                            TokenKind::PrivateIdentifier(Sym::CONSTRUCTOR) => {\n                                return Err(Error::general(\n                                    \"class constructor may not be a private method\",\n                                    token.span().start(),\n                                ));\n                            }\n                            TokenKind::IdentifierName((Sym::CONSTRUCTOR, _)) if !r#static => {\n                                return Err(Error::general(\n                                    \"class constructor may not be a generator method\",\n                                    token.span().start(),\n                                ));\n                            }\n                            _ => {}\n                        }\n                        let strict = cursor.strict();\n                        cursor.set_strict(true);\n                        let (class_element_name, params, body) =\n                            AsyncGeneratorMethod::new(self.allow_yield, self.allow_await)\n                                .parse(cursor, interner)?;\n                        cursor.set_strict(strict);\n\n                        let name = match class_element_name {\n                            ClassElementName::PropertyName(name) => {\n                                if r#static\n                                    && name.literal().map(Identifier::sym) == Some(Sym::PROTOTYPE)\n                                {\n                                    return Err(Error::general(\n                                        \"class may not have static method definitions named 'prototype'\",\n                                        name_position,\n                                    ));\n                                }\n                                ClassElementName::PropertyName(name)\n                            }\n                            ClassElementName::PrivateName(name) => {\n                                ClassElementName::PrivateName(name)\n                            }\n                        };\n\n                        function::ClassElement::MethodDefinition(ClassMethodDefinition::new(\n                            name,\n                            params,\n                            body,\n                            MethodDefinitionKind::AsyncGenerator,\n                            r#static,\n                            start_linear_pos,\n                        ))\n                    }\n                    TokenKind::IdentifierName((Sym::CONSTRUCTOR, _)) if !r#static => {\n                        return Err(Error::general(\n                            \"class constructor may not be an async method\",\n                            token.span().start(),\n                        ));\n                    }\n                    _ => {\n                        let name_position = token.span().start();\n                        let strict = cursor.strict();\n                        cursor.set_strict(true);\n                        let (class_element_name, params, body) =\n                            AsyncMethod::new(self.allow_yield, self.allow_await)\n                                .parse(cursor, interner)?;\n                        cursor.set_strict(strict);\n\n                        let name = match class_element_name {\n                            ClassElementName::PropertyName(name) => {\n                                if r#static\n                                    && name.literal().map(Identifier::sym) == Some(Sym::PROTOTYPE)\n                                {\n                                    return Err(Error::general(\n                                        \"class may not have static method definitions named 'prototype'\",\n                                        name_position,\n                                    ));\n                                }\n                                ClassElementName::PropertyName(name)\n                            }\n                            ClassElementName::PrivateName(name) => {\n                                if r#static && name.description() == Sym::CONSTRUCTOR {\n                                    return Err(Error::general(\n                                        \"class constructor may not be a private method\",\n                                        name_position,\n                                    ));\n                                }\n                                ClassElementName::PrivateName(name)\n                            }\n                        };\n                        function::ClassElement::MethodDefinition(ClassMethodDefinition::new(\n                            name,\n                            params,\n                            body,\n                            MethodDefinitionKind::Async,\n                            r#static,\n                            start_linear_pos,\n                        ))\n                    }\n                }\n            }\n            TokenKind::IdentifierName((Sym::GET | Sym::SET, ContainsEscapeSequence(true)))\n                if is_keyword =>\n            {\n                return Err(Error::general(\n                    \"keyword must not contain escaped characters\",\n                    token.span().start(),\n                ));\n            }\n            TokenKind::IdentifierName((Sym::GET, ContainsEscapeSequence(false))) if is_keyword => {\n                cursor.advance(interner);\n                let token = cursor.peek(0, interner).or_abrupt()?;\n                let start = token.span().start();\n                match token.kind() {\n                    TokenKind::PrivateIdentifier(Sym::CONSTRUCTOR) => {\n                        return Err(Error::general(\n                            \"class constructor may not be a private method\",\n                            token.span().start(),\n                        ));\n                    }\n                    TokenKind::PrivateIdentifier(name) => {\n                        let name = *name;\n                        let name_span = token.span();\n                        cursor.advance(interner);\n                        let strict = cursor.strict();\n                        cursor.set_strict(true);\n                        let params =\n                            UniqueFormalParameters::new(false, false).parse(cursor, interner)?;\n                        let body = FunctionBody::new(false, false, \"method definition\")\n                            .parse(cursor, interner)?;\n\n                        // Early Error: It is a Syntax Error if FunctionBodyContainsUseStrict of FunctionBody is true\n                        // and IsSimpleParameterList of UniqueFormalParameters is false.\n                        if body.strict() && !params.is_simple() {\n                            return Err(Error::lex(LexError::Syntax(\n                            \"Illegal 'use strict' directive in function with non-simple parameter list\"\n                                .into(),\n                                start,\n                        )));\n                        }\n                        cursor.set_strict(strict);\n                        function::ClassElement::MethodDefinition(ClassMethodDefinition::new(\n                            ClassElementName::PrivateName(PrivateName::new(name, name_span)),\n                            params,\n                            body,\n                            MethodDefinitionKind::Get,\n                            r#static,\n                            start_linear_pos,\n                        ))\n                    }\n                    TokenKind::IdentifierName((Sym::CONSTRUCTOR, _)) if !r#static => {\n                        return Err(Error::general(\n                            \"class constructor may not be a getter method\",\n                            token.span().start(),\n                        ));\n                    }\n                    TokenKind::IdentifierName(_)\n                    | TokenKind::StringLiteral(_)\n                    | TokenKind::NumericLiteral(_)\n                    | TokenKind::Keyword(_)\n                    | TokenKind::NullLiteral(_)\n                    | TokenKind::BooleanLiteral(_)\n                    | TokenKind::Punctuator(Punctuator::OpenBracket) => {\n                        let name_position = token.span().start();\n                        let name = PropertyName::new(self.allow_yield, self.allow_await)\n                            .parse(cursor, interner)?;\n                        cursor.expect(\n                            TokenKind::Punctuator(Punctuator::OpenParen),\n                            \"class getter\",\n                            interner,\n                        )?;\n                        cursor.expect(\n                            TokenKind::Punctuator(Punctuator::CloseParen),\n                            \"class getter\",\n                            interner,\n                        )?;\n\n                        let strict = cursor.strict();\n                        cursor.set_strict(true);\n                        let body = FunctionBody::new(false, false, \"class getter\")\n                            .parse(cursor, interner)?;\n                        cursor.set_strict(strict);\n\n                        if r#static && name.literal().map(Identifier::sym) == Some(Sym::PROTOTYPE) {\n                            return Err(Error::general(\n                                \"class may not have static method definitions named 'prototype'\",\n                                name_position,\n                            ));\n                        }\n                        function::ClassElement::MethodDefinition(ClassMethodDefinition::new(\n                            ClassElementName::PropertyName(name),\n                            FormalParameterList::default(),\n                            body,\n                            MethodDefinitionKind::Get,\n                            r#static,\n                            start_linear_pos,\n                        ))\n                    }\n                    _ => {\n                        let span = token.span();\n                        cursor.expect_semicolon(\"expected semicolon\", interner)?;\n                        let field =\n                            ClassFieldDefinition::new(Identifier::new(Sym::GET, span).into(), None);\n                        if r#static {\n                            function::ClassElement::StaticFieldDefinition(field)\n                        } else {\n                            function::ClassElement::FieldDefinition(field)\n                        }\n                    }\n                }\n            }\n            TokenKind::IdentifierName((Sym::SET, ContainsEscapeSequence(false))) if is_keyword => {\n                cursor.advance(interner);\n                let token = cursor.peek(0, interner).or_abrupt()?;\n                let start = token.span().start();\n                match token.kind() {\n                    TokenKind::PrivateIdentifier(Sym::CONSTRUCTOR) => {\n                        return Err(Error::general(\n                            \"class constructor may not be a private method\",\n                            token.span().start(),\n                        ));\n                    }\n                    TokenKind::PrivateIdentifier(name) => {\n                        let name = *name;\n                        let name_span = token.span();\n                        cursor.advance(interner);\n                        let strict = cursor.strict();\n                        cursor.set_strict(true);\n                        let params =\n                            UniqueFormalParameters::new(false, false).parse(cursor, interner)?;\n\n                        let body = FunctionBody::new(false, false, \"method definition\")\n                            .parse(cursor, interner)?;\n\n                        // Early Error: It is a Syntax Error if FunctionBodyContainsUseStrict of FunctionBody is true\n                        // and IsSimpleParameterList of UniqueFormalParameters is false.\n                        if body.strict() && !params.is_simple() {\n                            return Err(Error::lex(LexError::Syntax(\n                            \"Illegal 'use strict' directive in function with non-simple parameter list\"\n                                .into(),\n                                start,\n                        )));\n                        }\n                        cursor.set_strict(strict);\n                        function::ClassElement::MethodDefinition(ClassMethodDefinition::new(\n                            ClassElementName::PrivateName(PrivateName::new(name, name_span)),\n                            params,\n                            body,\n                            MethodDefinitionKind::Set,\n                            r#static,\n                            start_linear_pos,\n                        ))\n                    }\n                    TokenKind::IdentifierName((Sym::CONSTRUCTOR, _)) if !r#static => {\n                        return Err(Error::general(\n                            \"class constructor may not be a setter method\",\n                            start,\n                        ));\n                    }\n                    TokenKind::IdentifierName(_)\n                    | TokenKind::StringLiteral(_)\n                    | TokenKind::NumericLiteral(_)\n                    | TokenKind::Keyword(_)\n                    | TokenKind::NullLiteral(_)\n                    | TokenKind::BooleanLiteral(_)\n                    | TokenKind::Punctuator(Punctuator::OpenBracket) => {\n                        let name = PropertyName::new(self.allow_yield, self.allow_await)\n                            .parse(cursor, interner)?;\n                        let strict = cursor.strict();\n                        cursor.set_strict(true);\n                        let params =\n                            UniqueFormalParameters::new(false, false).parse(cursor, interner)?;\n                        let body = FunctionBody::new(false, false, \"method definition\")\n                            .parse(cursor, interner)?;\n\n                        // Early Error: It is a Syntax Error if FunctionBodyContainsUseStrict of FunctionBody is true\n                        // and IsSimpleParameterList of UniqueFormalParameters is false.\n                        if body.strict() && !params.is_simple() {\n                            return Err(Error::lex(LexError::Syntax(\n                            \"Illegal 'use strict' directive in function with non-simple parameter list\"\n                                .into(),\n                                start,\n                        )));\n                        }\n                        cursor.set_strict(strict);\n                        if r#static && name.literal().map(Identifier::sym) == Some(Sym::PROTOTYPE) {\n                            return Err(Error::general(\n                                \"class may not have static method definitions named 'prototype'\",\n                                start,\n                            ));\n                        }\n                        function::ClassElement::MethodDefinition(ClassMethodDefinition::new(\n                            ClassElementName::PropertyName(name),\n                            params,\n                            body,\n                            MethodDefinitionKind::Set,\n                            r#static,\n                            start_linear_pos,\n                        ))\n                    }\n                    _ => {\n                        let span = token.span();\n                        cursor.expect_semicolon(\"expected semicolon\", interner)?;\n                        let field =\n                            ClassFieldDefinition::new(Identifier::new(Sym::SET, span).into(), None);\n                        if r#static {\n                            function::ClassElement::StaticFieldDefinition(field)\n                        } else {\n                            function::ClassElement::FieldDefinition(field)\n                        }\n                    }\n                }\n            }\n            TokenKind::PrivateIdentifier(Sym::CONSTRUCTOR) => {\n                return Err(Error::general(\n                    \"class constructor may not be a private method\",\n                    token.span().start(),\n                ));\n            }\n            TokenKind::PrivateIdentifier(name) => {\n                let name = *name;\n                let name_span = token.span();\n                cursor.advance(interner);\n                let token = cursor.peek(0, interner).or_abrupt()?;\n                let start = token.span().start();\n                match token.kind() {\n                    TokenKind::Punctuator(Punctuator::Assign) => {\n                        cursor.advance(interner);\n                        let strict = cursor.strict();\n                        cursor.set_strict(true);\n                        let mut rhs =\n                            AssignmentExpression::new(true, self.allow_yield, self.allow_await)\n                                .parse(cursor, interner)?;\n                        cursor.expect_semicolon(\"expected semicolon\", interner)?;\n                        cursor.set_strict(strict);\n                        let function_name = interner.get_or_intern(\n                            [utf16!(\"#\"), interner.resolve_expect(name).utf16()]\n                                .concat()\n                                .as_slice(),\n                        );\n                        rhs.set_anonymous_function_definition_name(&Identifier::new(\n                            function_name,\n                            Span::new((1234, 1234), (1234, 1234)),\n                        ));\n                        let field = PrivateFieldDefinition::new(\n                            PrivateName::new(name, name_span),\n                            Some(rhs),\n                        );\n                        if r#static {\n                            function::ClassElement::PrivateStaticFieldDefinition(field)\n                        } else {\n                            function::ClassElement::PrivateFieldDefinition(field)\n                        }\n                    }\n                    TokenKind::Punctuator(Punctuator::OpenParen) => {\n                        let strict = cursor.strict();\n                        cursor.set_strict(true);\n                        let params =\n                            UniqueFormalParameters::new(false, false).parse(cursor, interner)?;\n                        let body = FunctionBody::new(false, false, \"method definition\")\n                            .parse(cursor, interner)?;\n\n                        // Early Error: It is a Syntax Error if FunctionBodyContainsUseStrict of FunctionBody is true\n                        // and IsSimpleParameterList of UniqueFormalParameters is false.\n                        if body.strict() && !params.is_simple() {\n                            return Err(Error::lex(LexError::Syntax(\n                                \"Illegal 'use strict' directive in function with non-simple parameter list\".into(),\n                                start,\n                            )));\n                        }\n                        cursor.set_strict(strict);\n                        function::ClassElement::MethodDefinition(ClassMethodDefinition::new(\n                            ClassElementName::PrivateName(PrivateName::new(name, name_span)),\n                            params,\n                            body,\n                            MethodDefinitionKind::Ordinary,\n                            r#static,\n                            start_linear_pos,\n                        ))\n                    }\n                    _ => {\n                        cursor.expect_semicolon(\"expected semicolon\", interner)?;\n                        let field =\n                            PrivateFieldDefinition::new(PrivateName::new(name, name_span), None);\n                        if r#static {\n                            function::ClassElement::PrivateStaticFieldDefinition(field)\n                        } else {\n                            function::ClassElement::PrivateFieldDefinition(field)\n                        }\n                    }\n                }\n            }\n            TokenKind::IdentifierName(_)\n            | TokenKind::StringLiteral(_)\n            | TokenKind::NumericLiteral(_)\n            | TokenKind::Keyword(_)\n            | TokenKind::NullLiteral(_)\n            | TokenKind::BooleanLiteral(_)\n            | TokenKind::Punctuator(Punctuator::OpenBracket) => {\n                let start = token.span().start();\n                let name = PropertyName::new(self.allow_yield, self.allow_await)\n                    .parse(cursor, interner)?;\n                let token = cursor.peek(0, interner).or_abrupt()?;\n                match token.kind() {\n                    TokenKind::Punctuator(Punctuator::Assign) => {\n                        if let Some(name) = name.literal() {\n                            if r#static {\n                                if [Sym::CONSTRUCTOR, Sym::PROTOTYPE].contains(&name.sym()) {\n                                    return Err(Error::general(\n                                        \"class may not have static field definitions named 'constructor' or 'prototype'\",\n                                        start,\n                                    ));\n                                }\n                            } else if name == Sym::CONSTRUCTOR {\n                                return Err(Error::general(\n                                    \"class may not have field definitions named 'constructor'\",\n                                    start,\n                                ));\n                            }\n                        }\n                        cursor.advance(interner);\n                        let strict = cursor.strict();\n                        cursor.set_strict(true);\n                        let mut rhs =\n                            AssignmentExpression::new(true, self.allow_yield, self.allow_await)\n                                .parse(cursor, interner)?;\n                        cursor.expect_semicolon(\"expected semicolon\", interner)?;\n                        cursor.set_strict(strict);\n                        if let Some(name) = name.literal() {\n                            rhs.set_anonymous_function_definition_name(&name);\n                        }\n                        let field = ClassFieldDefinition::new(name, Some(rhs));\n                        if r#static {\n                            function::ClassElement::StaticFieldDefinition(field)\n                        } else {\n                            function::ClassElement::FieldDefinition(field)\n                        }\n                    }\n                    TokenKind::Punctuator(Punctuator::OpenParen) => {\n                        if r#static && name.literal().map(Identifier::sym) == Some(Sym::PROTOTYPE) {\n                            return Err(Error::general(\n                                \"class may not have static method definitions named 'prototype'\",\n                                start,\n                            ));\n                        }\n                        let strict = cursor.strict();\n                        cursor.set_strict(true);\n                        let params =\n                            UniqueFormalParameters::new(false, false).parse(cursor, interner)?;\n\n                        let body = FunctionBody::new(false, false, \"method definition\")\n                            .parse(cursor, interner)?;\n\n                        // Early Error: It is a Syntax Error if FunctionBodyContainsUseStrict of FunctionBody is true\n                        // and IsSimpleParameterList of UniqueFormalParameters is false.\n                        if body.strict() && !params.is_simple() {\n                            return Err(Error::lex(LexError::Syntax(\n                            \"Illegal 'use strict' directive in function with non-simple parameter list\"\n                                .into(),\n                                start,\n                        )));\n                        }\n                        cursor.set_strict(strict);\n                        function::ClassElement::MethodDefinition(ClassMethodDefinition::new(\n                            ClassElementName::PropertyName(name),\n                            params,\n                            body,\n                            MethodDefinitionKind::Ordinary,\n                            r#static,\n                            start_linear_pos,\n                        ))\n                    }\n                    _ => {\n                        if let Some(name) = name.literal() {\n                            if r#static {\n                                if [Sym::CONSTRUCTOR, Sym::PROTOTYPE].contains(&name.sym()) {\n                                    return Err(Error::general(\n                                        \"class may not have static field definitions named 'constructor' or 'prototype'\",\n                                        start,\n                                    ));\n                                }\n                            } else if name == Sym::CONSTRUCTOR {\n                                return Err(Error::general(\n                                    \"class may not have field definitions named 'constructor'\",\n                                    start,\n                                ));\n                            }\n                        }\n                        cursor.expect_semicolon(\"expected semicolon\", interner)?;\n                        let field = ClassFieldDefinition::new(name, None);\n                        if r#static {\n                            function::ClassElement::StaticFieldDefinition(field)\n                        } else {\n                            function::ClassElement::FieldDefinition(field)\n                        }\n                    }\n                }\n            }\n            _ => {\n                return Err(Error::unexpected(\n                    token.to_string(interner),\n                    token.span(),\n                    \"expected class element (method, field, or static block)\",\n                ));\n            }\n        };\n\n        match &element {\n            // FieldDefinition : ClassElementName Initializer [opt]\n            // It is a Syntax Error if Initializer is present and ContainsArguments of Initializer is true.\n            function::ClassElement::FieldDefinition(field)\n            | function::ClassElement::StaticFieldDefinition(field) => {\n                if let Some(field) = field.initializer()\n                    && contains_arguments(field)\n                {\n                    return Err(Error::general(\n                        \"'arguments' not allowed in class field definition\",\n                        position,\n                    ));\n                }\n            }\n            function::ClassElement::PrivateFieldDefinition(field)\n            | function::ClassElement::PrivateStaticFieldDefinition(field) => {\n                if let Some(node) = field.initializer()\n                    && contains_arguments(node)\n                {\n                    return Err(Error::general(\n                        \"'arguments' not allowed in class field definition\",\n                        position,\n                    ));\n                }\n            }\n\n            _ => {}\n        }\n\n        Ok((None, Some(element)))\n    }\n}\n"
  },
  {
    "path": "core/parser/src/parser/statement/declaration/hoistable/class_decl/tests.rs",
    "content": "use crate::parser::tests::check_script_parser;\nuse boa_ast::{\n    Declaration, Expression, Span, Statement, StatementList, StatementListItem,\n    declaration::{LexicalDeclaration, Variable, VariableList},\n    expression::{\n        Call, Identifier, NewTarget,\n        access::{PropertyAccess, SimplePropertyAccess},\n        literal::Literal,\n    },\n    function::{\n        ClassDeclaration, ClassElement, ClassFieldDefinition, ClassMethodDefinition,\n        FormalParameterList, FunctionBody, FunctionExpression,\n    },\n    property::MethodDefinitionKind,\n};\nuse boa_interner::Interner;\nuse boa_macros::utf16;\nuse indoc::indoc;\n\n#[test]\nfn check_async_ordinary_method() {\n    let interner = &mut Interner::default();\n\n    let elements = vec![ClassElement::MethodDefinition(ClassMethodDefinition::new(\n        boa_ast::function::ClassElementName::PropertyName(\n            Identifier::new(\n                interner.get_or_intern_static(\"async\", utf16!(\"async\")),\n                Span::new((2, 5), (2, 10)),\n            )\n            .into(),\n        ),\n        FormalParameterList::default(),\n        FunctionBody::new(StatementList::default(), Span::new((2, 13), (2, 16))),\n        MethodDefinitionKind::Ordinary,\n        false,\n        boa_ast::LinearPosition::default(),\n    ))];\n\n    check_script_parser(\n        indoc! {r#\"\n            class A {\n                async() { }\n            }\n        \"#},\n        [Declaration::ClassDeclaration(\n            ClassDeclaration::new(\n                Identifier::new(\n                    interner.get_or_intern_static(\"A\", utf16!(\"A\")),\n                    Span::new((1, 7), (1, 8)),\n                ),\n                None,\n                None,\n                elements.into(),\n            )\n            .into(),\n        )\n        .into()],\n        interner,\n    );\n}\n\n#[test]\nfn check_async_field_initialization() {\n    let interner = &mut Interner::default();\n\n    let elements = vec![ClassElement::FieldDefinition(ClassFieldDefinition::new(\n        Identifier::new(\n            interner.get_or_intern_static(\"async\", utf16!(\"async\")),\n            Span::new((2, 5), (2, 10)),\n        )\n        .into(),\n        Some(Literal::new(1, Span::new((3, 7), (3, 8))).into()),\n    ))];\n\n    check_script_parser(\n        indoc! {\"\n            class A {\n                async\n                = 1\n            }\n        \"},\n        [\n            Declaration::ClassDeclaration(Box::new(ClassDeclaration::new(\n                Identifier::new(\n                    interner.get_or_intern_static(\"A\", utf16!(\"A\")),\n                    Span::new((1, 7), (1, 8)),\n                ),\n                None,\n                None,\n                elements.into(),\n            )))\n            .into(),\n        ],\n        interner,\n    );\n}\n\n#[test]\nfn check_async_field() {\n    let interner = &mut Interner::default();\n\n    let elements = vec![ClassElement::FieldDefinition(ClassFieldDefinition::new(\n        Identifier::new(\n            interner.get_or_intern_static(\"async\", utf16!(\"async\")),\n            Span::new((2, 5), (2, 10)),\n        )\n        .into(),\n        None,\n    ))];\n\n    check_script_parser(\n        indoc! {r#\"\n            class A {\n                async\n            }\n        \"#},\n        [\n            Declaration::ClassDeclaration(Box::new(ClassDeclaration::new(\n                Identifier::new(\n                    interner.get_or_intern_static(\"A\", utf16!(\"A\")),\n                    Span::new((1, 7), (1, 8)),\n                ),\n                None,\n                None,\n                elements.into(),\n            )))\n            .into(),\n        ],\n        interner,\n    );\n}\n\n#[test]\nfn check_new_target_with_property_access() {\n    let interner = &mut Interner::default();\n\n    let new_target = Expression::PropertyAccess(\n        SimplePropertyAccess::new(\n            NewTarget::new(Span::new((3, 21), (3, 31))).into(),\n            Identifier::new(\n                interner.get_or_intern_static(\"name\", utf16!(\"name\")),\n                Span::new((3, 32), (3, 36)),\n            ),\n        )\n        .into(),\n    );\n\n    let console = Call::new(\n        PropertyAccess::Simple(SimplePropertyAccess::new(\n            Identifier::new(\n                interner.get_or_intern_static(\"console\", utf16!(\"console\")),\n                Span::new((3, 9), (3, 16)),\n            )\n            .into(),\n            Identifier::new(\n                interner.get_or_intern_static(\"log\", utf16!(\"log\")),\n                Span::new((3, 17), (3, 20)),\n            ),\n        ))\n        .into(),\n        [new_target].into(),\n        Span::new((3, 20), (3, 37)),\n    )\n    .into();\n\n    let constructor = FunctionExpression::new(\n        Some(Identifier::new(\n            interner.get_or_intern_static(\"A\", utf16!(\"A\")),\n            Span::new((1, 7), (1, 8)),\n        )),\n        FormalParameterList::default(),\n        FunctionBody::new(\n            StatementList::new(\n                [Statement::Expression(console).into()],\n                boa_ast::LinearPosition::new(0),\n                false,\n            ),\n            Span::new((2, 19), (4, 6)),\n        ),\n        None,\n        false,\n        Span::new((2, 5), (4, 6)),\n    );\n\n    let class = ClassDeclaration::new(\n        Identifier::new(interner.get(\"A\").unwrap(), Span::new((1, 7), (1, 8))),\n        None,\n        Some(constructor),\n        Box::default(),\n    );\n\n    let instantiation = Expression::New(\n        Call::new(\n            Identifier::new(interner.get(\"A\").unwrap(), Span::new((6, 15), (6, 16))).into(),\n            Box::default(),\n            Span::new((6, 11), (6, 18)),\n        )\n        .into(),\n    );\n\n    let const_decl = LexicalDeclaration::Const(\n        VariableList::new(\n            [Variable::from_identifier(\n                Identifier::new(\n                    interner.get_or_intern_static(\"a\", utf16!(\"a\")),\n                    Span::new((6, 7), (6, 8)),\n                ),\n                Some(instantiation),\n            )]\n            .into(),\n        )\n        .unwrap(),\n    );\n\n    let script = [\n        StatementListItem::Declaration(Declaration::ClassDeclaration(class.into()).into()),\n        StatementListItem::Declaration(Declaration::Lexical(const_decl).into()),\n    ];\n\n    check_script_parser(\n        indoc! {r#\"\n            class A {\n                constructor() {\n                    console.log(new.target.name);\n                }\n            }\n            const a = new A();\n        \"#},\n        script,\n        interner,\n    );\n}\n\n#[test]\nfn check_non_reserved_keyword_as_identifier() {\n    let interner = &mut Interner::default();\n\n    check_script_parser(\n        indoc! {r#\"\n            class of {}\n        \"#},\n        [Declaration::ClassDeclaration(\n            ClassDeclaration::new(\n                Identifier::new(\n                    interner.get_or_intern_static(\"of\", utf16!(\"of\")),\n                    Span::new((1, 7), (1, 9)),\n                ),\n                None,\n                None,\n                vec![].into(),\n            )\n            .into(),\n        )\n        .into()],\n        interner,\n    );\n}\n"
  },
  {
    "path": "core/parser/src/parser/statement/declaration/hoistable/function_decl/mod.rs",
    "content": "#[cfg(test)]\nmod tests;\n\nuse crate::{\n    parser::{\n        AllowAwait, AllowDefault, AllowYield, Cursor, ParseResult, TokenParser,\n        statement::declaration::hoistable::{CallableDeclaration, parse_callable_declaration},\n    },\n    source::ReadChar,\n};\nuse boa_ast::{Keyword, function::FunctionDeclaration as FunctionDeclarationNode};\nuse boa_interner::Interner;\n\n/// Function declaration parsing.\n///\n/// More information:\n///  - [MDN documentation][mdn]\n///  - [ECMAScript specification][spec]\n///\n/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/function\n/// [spec]: https://tc39.es/ecma262/#prod-FunctionDeclaration\n\n#[derive(Debug, Clone, Copy)]\npub(in crate::parser) struct FunctionDeclaration {\n    allow_yield: AllowYield,\n    allow_await: AllowAwait,\n    is_default: AllowDefault,\n}\n\nimpl FunctionDeclaration {\n    /// Creates a new `FunctionDeclaration` parser.\n    pub(in crate::parser) fn new<Y, A, D>(allow_yield: Y, allow_await: A, is_default: D) -> Self\n    where\n        Y: Into<AllowYield>,\n        A: Into<AllowAwait>,\n        D: Into<AllowDefault>,\n    {\n        Self {\n            allow_yield: allow_yield.into(),\n            allow_await: allow_await.into(),\n            is_default: is_default.into(),\n        }\n    }\n}\n\nimpl CallableDeclaration for FunctionDeclaration {\n    fn error_context(&self) -> &'static str {\n        \"function declaration\"\n    }\n    fn is_default(&self) -> bool {\n        self.is_default.0\n    }\n    fn name_allow_yield(&self) -> bool {\n        self.allow_yield.0\n    }\n    fn name_allow_await(&self) -> bool {\n        self.allow_await.0\n    }\n    fn parameters_allow_yield(&self) -> bool {\n        false\n    }\n    fn parameters_allow_await(&self) -> bool {\n        false\n    }\n    fn body_allow_yield(&self) -> bool {\n        false\n    }\n    fn body_allow_await(&self) -> bool {\n        false\n    }\n}\n\nimpl<R> TokenParser<R> for FunctionDeclaration\nwhere\n    R: ReadChar,\n{\n    type Output = FunctionDeclarationNode;\n\n    fn parse(self, cursor: &mut Cursor<R>, interner: &mut Interner) -> ParseResult<Self::Output> {\n        let func_token =\n            cursor.expect((Keyword::Function, false), \"function declaration\", interner)?;\n        let func_token_span = func_token.linear_span();\n\n        let result = parse_callable_declaration(&self, cursor, interner)?;\n        let linear_pos_end = result.2.linear_pos_end();\n        let span = func_token_span.union(linear_pos_end);\n\n        Ok(FunctionDeclarationNode::new(\n            result.0, result.1, result.2, span,\n        ))\n    }\n}\n"
  },
  {
    "path": "core/parser/src/parser/statement/declaration/hoistable/function_decl/tests.rs",
    "content": "use crate::parser::tests::check_script_parser;\nuse boa_ast::{\n    Declaration, Span, StatementList,\n    expression::Identifier,\n    function::{FormalParameterList, FunctionBody, FunctionDeclaration},\n};\nuse boa_interner::Interner;\nuse boa_macros::utf16;\n\nconst PSEUDO_LINEAR_POS: boa_ast::LinearPosition = boa_ast::LinearPosition::new(0);\nconst EMPTY_LINEAR_SPAN: boa_ast::LinearSpan =\n    boa_ast::LinearSpan::new(PSEUDO_LINEAR_POS, PSEUDO_LINEAR_POS);\n\n/// Function declaration parsing.\n#[test]\nfn function_declaration() {\n    let interner = &mut Interner::default();\n    check_script_parser(\n        \"function hello() {}\",\n        vec![\n            Declaration::FunctionDeclaration(FunctionDeclaration::new(\n                Identifier::new(\n                    interner.get_or_intern_static(\"hello\", utf16!(\"hello\")),\n                    Span::new((1, 10), (1, 15)),\n                ),\n                FormalParameterList::default(),\n                FunctionBody::new(StatementList::default(), Span::new((1, 18), (1, 20))),\n                EMPTY_LINEAR_SPAN,\n            ))\n            .into(),\n        ],\n        interner,\n    );\n}\n\n/// Function declaration parsing with keywords.\n#[test]\nfn function_declaration_keywords() {\n    macro_rules! genast {\n        ($keyword:literal, $interner:expr, $name_span:expr, $body_span:expr) => {\n            vec![\n                Declaration::FunctionDeclaration(FunctionDeclaration::new(\n                    Identifier::new(\n                        $interner\n                            .get_or_intern_static($keyword, utf16!($keyword))\n                            .into(),\n                        $name_span,\n                    ),\n                    FormalParameterList::default(),\n                    FunctionBody::new(StatementList::default(), $body_span),\n                    EMPTY_LINEAR_SPAN,\n                ))\n                .into(),\n            ]\n        };\n    }\n\n    let interner = &mut Interner::default();\n    let ast = genast!(\n        \"yield\",\n        interner,\n        Span::new((1, 10), (1, 15)),\n        Span::new((1, 18), (1, 20))\n    );\n    check_script_parser(\"function yield() {}\", ast, interner);\n\n    let interner = &mut Interner::default();\n    let ast = genast!(\n        \"await\",\n        interner,\n        Span::new((1, 10), (1, 15)),\n        Span::new((1, 18), (1, 20))\n    );\n    check_script_parser(\"function await() {}\", ast, interner);\n\n    let interner = &mut Interner::default();\n    let ast = genast!(\n        \"as\",\n        interner,\n        Span::new((1, 10), (1, 12)),\n        Span::new((1, 15), (1, 17))\n    );\n    check_script_parser(\"function as() {}\", ast, interner);\n\n    let interner = &mut Interner::default();\n    let ast = genast!(\n        \"async\",\n        interner,\n        Span::new((1, 10), (1, 15)),\n        Span::new((1, 18), (1, 20))\n    );\n    check_script_parser(\"function async() {}\", ast, interner);\n\n    let interner = &mut Interner::default();\n    let ast = genast!(\n        \"from\",\n        interner,\n        Span::new((1, 10), (1, 14)),\n        Span::new((1, 17), (1, 19))\n    );\n    check_script_parser(\"function from() {}\", ast, interner);\n\n    let interner = &mut Interner::default();\n    let ast = genast!(\n        \"get\",\n        interner,\n        Span::new((1, 10), (1, 13)),\n        Span::new((1, 16), (1, 18))\n    );\n    check_script_parser(\"function get() {}\", ast, interner);\n\n    let interner = &mut Interner::default();\n    let ast = genast!(\n        \"meta\",\n        interner,\n        Span::new((1, 10), (1, 14)),\n        Span::new((1, 17), (1, 19))\n    );\n    check_script_parser(\"function meta() {}\", ast, interner);\n\n    let interner = &mut Interner::default();\n    let ast = genast!(\n        \"of\",\n        interner,\n        Span::new((1, 10), (1, 12)),\n        Span::new((1, 15), (1, 17))\n    );\n    check_script_parser(\"function of() {}\", ast, interner);\n\n    let interner = &mut Interner::default();\n    let ast = genast!(\n        \"set\",\n        interner,\n        Span::new((1, 10), (1, 13)),\n        Span::new((1, 16), (1, 18))\n    );\n    check_script_parser(\"function set() {}\", ast, interner);\n\n    let interner = &mut Interner::default();\n    let ast = genast!(\n        \"target\",\n        interner,\n        Span::new((1, 10), (1, 16)),\n        Span::new((1, 19), (1, 21))\n    );\n    check_script_parser(\"function target() {}\", ast, interner);\n}\n"
  },
  {
    "path": "core/parser/src/parser/statement/declaration/hoistable/generator_decl/mod.rs",
    "content": "#[cfg(test)]\nmod tests;\n\nuse crate::{\n    parser::{\n        AllowAwait, AllowDefault, AllowYield, Cursor, ParseResult, TokenParser,\n        statement::declaration::hoistable::{CallableDeclaration, parse_callable_declaration},\n    },\n    source::ReadChar,\n};\nuse boa_ast::{Keyword, Punctuator, function::GeneratorDeclaration as GeneratorDeclarationNode};\nuse boa_interner::Interner;\n\n/// Generator declaration parsing.\n///\n/// More information:\n///  - [MDN documentation][mdn]\n///  - [ECMAScript specification][spec]\n///\n/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/function*\n/// [spec]: https://tc39.es/ecma262/#prod-GeneratorDeclaration\n#[derive(Debug, Clone, Copy)]\npub(in crate::parser) struct GeneratorDeclaration {\n    allow_yield: AllowYield,\n    allow_await: AllowAwait,\n    is_default: AllowDefault,\n}\n\nimpl GeneratorDeclaration {\n    /// Creates a new `GeneratorDeclaration` parser.\n    pub(in crate::parser) fn new<Y, A, D>(allow_yield: Y, allow_await: A, is_default: D) -> Self\n    where\n        Y: Into<AllowYield>,\n        A: Into<AllowAwait>,\n        D: Into<AllowDefault>,\n    {\n        Self {\n            allow_yield: allow_yield.into(),\n            allow_await: allow_await.into(),\n            is_default: is_default.into(),\n        }\n    }\n}\n\nimpl CallableDeclaration for GeneratorDeclaration {\n    fn error_context(&self) -> &'static str {\n        \"generator declaration\"\n    }\n    fn is_default(&self) -> bool {\n        self.is_default.0\n    }\n    fn name_allow_yield(&self) -> bool {\n        self.allow_yield.0\n    }\n    fn name_allow_await(&self) -> bool {\n        self.allow_await.0\n    }\n    fn parameters_allow_yield(&self) -> bool {\n        true\n    }\n    fn parameters_allow_await(&self) -> bool {\n        false\n    }\n    fn body_allow_yield(&self) -> bool {\n        true\n    }\n    fn body_allow_await(&self) -> bool {\n        false\n    }\n    fn parameters_yield_is_early_error(&self) -> bool {\n        true\n    }\n}\n\nimpl<R> TokenParser<R> for GeneratorDeclaration\nwhere\n    R: ReadChar,\n{\n    type Output = GeneratorDeclarationNode;\n\n    fn parse(self, cursor: &mut Cursor<R>, interner: &mut Interner) -> ParseResult<Self::Output> {\n        let func_token = cursor.expect(\n            (Keyword::Function, false),\n            \"generator declaration\",\n            interner,\n        )?;\n        let start_linear_span = func_token.linear_span();\n\n        cursor.expect(Punctuator::Mul, \"generator declaration\", interner)?;\n\n        let result = parse_callable_declaration(&self, cursor, interner)?;\n        let span = start_linear_span.union(result.2.linear_pos_end());\n\n        Ok(GeneratorDeclarationNode::new(\n            result.0, result.1, result.2, span,\n        ))\n    }\n}\n"
  },
  {
    "path": "core/parser/src/parser/statement/declaration/hoistable/generator_decl/tests.rs",
    "content": "use crate::parser::tests::check_script_parser;\nuse boa_ast::{\n    Declaration, LinearPosition, LinearSpan, Span, StatementList,\n    expression::Identifier,\n    function::{FormalParameterList, FunctionBody, GeneratorDeclaration},\n};\nuse boa_interner::Interner;\nuse boa_macros::utf16;\n\n#[test]\nfn generator_function_declaration() {\n    let interner = &mut Interner::default();\n    check_script_parser(\n        \"function* gen() {}\",\n        vec![\n            Declaration::GeneratorDeclaration(GeneratorDeclaration::new(\n                Identifier::new(\n                    interner.get_or_intern_static(\"gen\", utf16!(\"gen\")),\n                    Span::new((1, 11), (1, 14)),\n                ),\n                FormalParameterList::default(),\n                FunctionBody::new(StatementList::default(), Span::new((1, 17), (1, 19))),\n                LinearSpan::new(LinearPosition::default(), LinearPosition::default()),\n            ))\n            .into(),\n        ],\n        interner,\n    );\n}\n"
  },
  {
    "path": "core/parser/src/parser/statement/declaration/hoistable/mod.rs",
    "content": "//! Hoistable declaration parsing.\n//!\n//! More information:\n//!  - [ECMAScript specification][spec]\n//!\n//! [spec]: https://tc39.es/ecma262/#prod-HoistableDeclaration\n\n#[cfg(test)]\nmod tests;\n\nmod async_function_decl;\nmod async_generator_decl;\nmod function_decl;\nmod generator_decl;\n\npub(crate) mod class_decl;\n\nuse crate::{\n    Error,\n    lexer::TokenKind,\n    parser::{\n        AllowAwait, AllowDefault, AllowYield, Cursor, OrAbrupt, ParseResult, TokenParser,\n        expression::BindingIdentifier,\n        function::{FormalParameters, FunctionBody},\n        name_in_lexically_declared_names,\n        statement::LexError,\n    },\n    source::ReadChar,\n};\nuse boa_ast::{\n    self as ast, Declaration, Keyword, Punctuator, Spanned,\n    expression::Identifier,\n    function::FormalParameterList,\n    operations::{ContainsSymbol, bound_names, contains, lexically_declared_names},\n};\nuse boa_interner::{Interner, Sym};\n\npub(in crate::parser) use self::{\n    async_function_decl::AsyncFunctionDeclaration, async_generator_decl::AsyncGeneratorDeclaration,\n    class_decl::ClassDeclaration, function_decl::FunctionDeclaration,\n    generator_decl::GeneratorDeclaration,\n};\n\n/// Hoistable declaration parsing.\n///\n/// More information:\n///  - [ECMAScript specification][spec]\n///\n/// [spec]: https://tc39.es/ecma262/#prod-FunctionDeclaration\n#[derive(Debug, Clone, Copy)]\npub(in crate::parser) struct HoistableDeclaration {\n    allow_yield: AllowYield,\n    allow_await: AllowAwait,\n    is_default: AllowDefault,\n}\n\nimpl HoistableDeclaration {\n    /// Creates a new `HoistableDeclaration` parser.\n    pub(in crate::parser) fn new<Y, A, D>(allow_yield: Y, allow_await: A, is_default: D) -> Self\n    where\n        Y: Into<AllowYield>,\n        A: Into<AllowAwait>,\n        D: Into<AllowDefault>,\n    {\n        Self {\n            allow_yield: allow_yield.into(),\n            allow_await: allow_await.into(),\n            is_default: is_default.into(),\n        }\n    }\n}\n\nimpl<R> TokenParser<R> for HoistableDeclaration\nwhere\n    R: ReadChar,\n{\n    type Output = Declaration;\n\n    fn parse(self, cursor: &mut Cursor<R>, interner: &mut Interner) -> ParseResult<Self::Output> {\n        let tok = cursor.peek(0, interner).or_abrupt()?;\n\n        match tok.kind() {\n            TokenKind::Keyword((Keyword::Function | Keyword::Async | Keyword::Class, true)) => {\n                Err(Error::general(\n                    \"Keyword must not contain escaped characters\",\n                    tok.span().start(),\n                ))\n            }\n            TokenKind::Keyword((Keyword::Function, false)) => {\n                let next_token = cursor.peek(1, interner).or_abrupt()?;\n                if next_token.kind() == &TokenKind::Punctuator(Punctuator::Mul) {\n                    GeneratorDeclaration::new(self.allow_yield, self.allow_await, self.is_default)\n                        .parse(cursor, interner)\n                        .map(Declaration::from)\n                } else {\n                    FunctionDeclaration::new(self.allow_yield, self.allow_await, self.is_default)\n                        .parse(cursor, interner)\n                        .map(Declaration::from)\n                }\n            }\n            TokenKind::Keyword((Keyword::Async, false)) => {\n                let next_token = cursor.peek(2, interner).or_abrupt()?;\n                if next_token.kind() == &TokenKind::Punctuator(Punctuator::Mul) {\n                    AsyncGeneratorDeclaration::new(\n                        self.allow_yield,\n                        self.allow_await,\n                        self.is_default,\n                    )\n                    .parse(cursor, interner)\n                    .map(Declaration::from)\n                } else {\n                    AsyncFunctionDeclaration::new(self.allow_yield, self.allow_await, false)\n                        .parse(cursor, interner)\n                        .map(Declaration::from)\n                }\n            }\n            TokenKind::Keyword((Keyword::Class, false)) => {\n                ClassDeclaration::new(self.allow_yield, self.allow_await, false)\n                    .parse(cursor, interner)\n                    .map(Declaration::from)\n            }\n            _ => Err(Error::expected(\n                [\n                    Keyword::Function.to_string(),\n                    Keyword::Async.to_string(),\n                    Keyword::Class.to_string(),\n                ],\n                tok.to_string(interner),\n                tok.span(),\n                \"declaration\",\n            )),\n        }\n    }\n}\n\ntrait CallableDeclaration {\n    fn error_context(&self) -> &'static str;\n    fn is_default(&self) -> bool;\n    fn name_allow_yield(&self) -> bool;\n    fn name_allow_await(&self) -> bool;\n    fn parameters_allow_yield(&self) -> bool;\n    fn parameters_allow_await(&self) -> bool;\n    fn body_allow_yield(&self) -> bool;\n    fn body_allow_await(&self) -> bool;\n    fn parameters_yield_is_early_error(&self) -> bool {\n        false\n    }\n    fn parameters_await_is_early_error(&self) -> bool {\n        false\n    }\n}\n\n// This is a helper function to not duplicate code in the individual callable declaration parsers.\nfn parse_callable_declaration<R: ReadChar, C: CallableDeclaration>(\n    c: &C,\n    cursor: &mut Cursor<R>,\n    interner: &mut Interner,\n) -> ParseResult<(Identifier, FormalParameterList, ast::function::FunctionBody)> {\n    let token = cursor.peek(0, interner).or_abrupt()?;\n    let name_span = token.span();\n    let name = match token.kind() {\n        TokenKind::Punctuator(Punctuator::OpenParen) => {\n            if !c.is_default() {\n                return Err(Error::unexpected(\n                    token.to_string(interner),\n                    token.span(),\n                    c.error_context(),\n                ));\n            }\n            Identifier::new(Sym::DEFAULT, name_span)\n        }\n        _ => BindingIdentifier::new(c.name_allow_yield(), c.name_allow_await())\n            .parse(cursor, interner)?,\n    };\n\n    let params_start_position = cursor\n        .expect(Punctuator::OpenParen, c.error_context(), interner)?\n        .span()\n        .end();\n\n    let params = FormalParameters::new(c.parameters_allow_yield(), c.parameters_allow_await())\n        .parse(cursor, interner)?;\n\n    cursor.expect(Punctuator::CloseParen, c.error_context(), interner)?;\n\n    let body = FunctionBody::new(\n        c.body_allow_yield(),\n        c.body_allow_await(),\n        c.error_context(),\n    )\n    .parse(cursor, interner)?;\n\n    // If the source text matched by FormalParameters is strict mode code,\n    // the Early Error rules for UniqueFormalParameters : FormalParameters are applied.\n    if (cursor.strict() || body.strict()) && params.has_duplicates() {\n        return Err(Error::lex(LexError::Syntax(\n            \"Duplicate parameter name not allowed in this context\".into(),\n            params_start_position,\n        )));\n    }\n\n    // It is a Syntax Error if FunctionBodyContainsUseStrict of FunctionBody is true\n    // and IsSimpleParameterList of FormalParameters is false.\n    if body.strict() && !params.is_simple() {\n        return Err(Error::lex(LexError::Syntax(\n            \"Illegal 'use strict' directive in function with non-simple parameter list\".into(),\n            params_start_position,\n        )));\n    }\n\n    // Early Error: If BindingIdentifier is present and the source code matching BindingIdentifier is strict mode code,\n    // it is a Syntax Error if the StringValue of BindingIdentifier is \"eval\" or \"arguments\".\n    if (cursor.strict() || body.strict()) && [Sym::EVAL, Sym::ARGUMENTS].contains(&name.sym()) {\n        return Err(Error::lex(LexError::Syntax(\n            \"unexpected identifier 'eval' or 'arguments' in strict mode\".into(),\n            name_span.start(),\n        )));\n    }\n\n    // Early Error for BindingIdentifier, because the strictness of the functions body is also\n    // relevant for the function parameters.\n    if body.strict() && contains(&params, ContainsSymbol::EvalOrArguments) {\n        return Err(Error::lex(LexError::Syntax(\n            \"unexpected identifier 'eval' or 'arguments' in strict mode\".into(),\n            params_start_position,\n        )));\n    }\n\n    // It is a Syntax Error if any element of the BoundNames of FormalParameters\n    // also occurs in the LexicallyDeclaredNames of FunctionBody.\n    name_in_lexically_declared_names(\n        &bound_names(&params),\n        &lexically_declared_names(&body),\n        params_start_position,\n        interner,\n    )?;\n\n    // It is a Syntax Error if FormalParameters Contains SuperProperty is true.\n    // It is a Syntax Error if FunctionBody Contains SuperProperty is true.\n    // It is a Syntax Error if FormalParameters Contains SuperCall is true.\n    // It is a Syntax Error if FunctionBody Contains SuperCall is true.\n    if contains(&body, ContainsSymbol::Super) || contains(&params, ContainsSymbol::Super) {\n        return Err(Error::lex(LexError::Syntax(\n            \"invalid super usage\".into(),\n            params_start_position,\n        )));\n    }\n\n    if c.parameters_yield_is_early_error() {\n        // It is a Syntax Error if FormalParameters Contains YieldExpression is true.\n        if contains(&params, ContainsSymbol::YieldExpression) {\n            return Err(Error::lex(LexError::Syntax(\n                \"invalid yield usage in generator function parameters\".into(),\n                params_start_position,\n            )));\n        }\n    }\n\n    if c.parameters_await_is_early_error() {\n        // It is a Syntax Error if FormalParameters Contains AwaitExpression is true.\n        if contains(&params, ContainsSymbol::AwaitExpression) {\n            return Err(Error::lex(LexError::Syntax(\n                \"invalid await usage in generator function parameters\".into(),\n                params_start_position,\n            )));\n        }\n    }\n\n    Ok((name, params, body))\n}\n"
  },
  {
    "path": "core/parser/src/parser/statement/declaration/hoistable/tests.rs",
    "content": "\n"
  },
  {
    "path": "core/parser/src/parser/statement/declaration/import.rs",
    "content": "//! Import declaration parsing\n//!\n//! This parses `import` declarations.\n//!\n//! More information:\n//! - [MDN documentation][mdn]\n//!  - [ECMAScript specification][spec]\n//!\n//! [spec]: https://tc39.es/ecma262/#sec-imports\n//! [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/import\n\nuse crate::{\n    lexer::TokenKind,\n    parser::{\n        Error, OrAbrupt, ParseResult, TokenParser,\n        cursor::Cursor,\n        statement::{\n            BindingIdentifier,\n            declaration::{FromClause, WithClause},\n        },\n    },\n    source::ReadChar,\n};\nuse boa_ast::{\n    Keyword, Punctuator, Spanned,\n    declaration::{\n        ImportAttribute, ImportDeclaration as AstImportDeclaration, ImportKind,\n        ImportSpecifier as AstImportSpecifier, ModuleSpecifier,\n    },\n    expression::Identifier,\n};\nuse boa_interner::{Interner, Sym};\n\n/// Parses an import declaration.\n///\n/// More information:\n///  - [ECMAScript specification][spec]\n///\n/// [spec]: https://tc39.es/ecma262/#prod-ImportDeclaration\n#[derive(Debug, Clone, Copy)]\npub(in crate::parser) struct ImportDeclaration;\n\nimpl ImportDeclaration {\n    /// Tests if the next node is an `ImportDeclaration`.\n    pub(in crate::parser) fn test<R: ReadChar>(\n        cursor: &mut Cursor<R>,\n        interner: &mut Interner,\n    ) -> ParseResult<bool> {\n        if let Some(token) = cursor.peek(0, interner)?\n            && let TokenKind::Keyword((Keyword::Import, escaped)) = token.kind()\n        {\n            if *escaped {\n                return Err(Error::general(\n                    \"keyword `import` must not contain escaped characters\",\n                    token.span().start(),\n                ));\n            }\n\n            if let Some(token) = cursor.peek(1, interner)? {\n                match token.kind() {\n                    TokenKind::StringLiteral(_)\n                    | TokenKind::Punctuator(Punctuator::OpenBlock | Punctuator::Mul)\n                    | TokenKind::IdentifierName(_)\n                    | TokenKind::Keyword(_) => return Ok(true),\n                    _ => {}\n                }\n            }\n        }\n\n        Ok(false)\n    }\n}\n\nimpl<R> TokenParser<R> for ImportDeclaration\nwhere\n    R: ReadChar,\n{\n    type Output = AstImportDeclaration;\n\n    fn parse(self, cursor: &mut Cursor<R>, interner: &mut Interner) -> ParseResult<Self::Output> {\n        cursor.expect((Keyword::Import, false), \"import declaration\", interner)?;\n\n        let tok = cursor.peek(0, interner).or_abrupt()?;\n\n        let import_clause = match tok.kind() {\n            TokenKind::StringLiteral((module_identifier, _)) => {\n                let module_identifier = *module_identifier;\n\n                cursor.advance(interner);\n\n                let attributes = WithClause::new(\"import declaration\").parse(cursor, interner)?;\n\n                cursor.expect_semicolon(\"import declaration\", interner)?;\n\n                return Ok(AstImportDeclaration::new(\n                    None,\n                    ImportKind::DefaultOrUnnamed,\n                    ModuleSpecifier::new(module_identifier),\n                    attributes,\n                ));\n            }\n            TokenKind::Punctuator(Punctuator::OpenBlock) => {\n                let list = NamedImports.parse(cursor, interner)?;\n                ImportClause::ImportList(None, list)\n            }\n            TokenKind::Punctuator(Punctuator::Mul) => {\n                let alias = NameSpaceImport.parse(cursor, interner)?;\n                ImportClause::Namespace(None, alias)\n            }\n            TokenKind::IdentifierName(_)\n            | TokenKind::Keyword((Keyword::Await | Keyword::Yield, _)) => {\n                let imported_binding = ImportedBinding.parse(cursor, interner)?;\n\n                let tok = cursor.peek(0, interner).or_abrupt()?;\n\n                match tok.kind() {\n                    TokenKind::Punctuator(Punctuator::Comma) => {\n                        cursor.advance(interner);\n                        let tok = cursor.peek(0, interner).or_abrupt()?;\n\n                        match tok.kind() {\n                            TokenKind::Punctuator(Punctuator::OpenBlock) => {\n                                let list = NamedImports.parse(cursor, interner)?;\n                                ImportClause::ImportList(Some(imported_binding), list)\n                            }\n                            TokenKind::Punctuator(Punctuator::Mul) => {\n                                let alias = NameSpaceImport.parse(cursor, interner)?;\n                                ImportClause::Namespace(Some(imported_binding), alias)\n                            }\n                            _ => {\n                                return Err(Error::expected(\n                                    [\n                                        Punctuator::OpenBlock.to_string(),\n                                        Punctuator::Mul.to_string(),\n                                    ],\n                                    tok.to_string(interner),\n                                    tok.span(),\n                                    \"import declaration\",\n                                ));\n                            }\n                        }\n                    }\n                    _ => ImportClause::ImportList(Some(imported_binding), Box::default()),\n                }\n            }\n            _ => {\n                return Err(Error::expected(\n                    [\n                        Punctuator::OpenBlock.to_string(),\n                        Punctuator::Mul.to_string(),\n                        \"identifier\".to_owned(),\n                        \"string literal\".to_owned(),\n                    ],\n                    tok.to_string(interner),\n                    tok.span(),\n                    \"import declaration\",\n                ));\n            }\n        };\n\n        let module_identifier = FromClause::new(\"import declaration\").parse(cursor, interner)?;\n        let attributes = WithClause::new(\"import declaration\").parse(cursor, interner)?;\n\n        cursor.expect_semicolon(\"import declaration\", interner)?;\n\n        Ok(import_clause.with_specifier_and_attributes(module_identifier, attributes))\n    }\n}\n\n/// Parses an imported binding\n///\n/// More information:\n///  - [ECMAScript specification][spec]\n///\n/// [spec]: https://tc39.es/ecma262/#prod-ImportedBinding\n#[derive(Debug, Clone, Copy)]\nstruct ImportedBinding;\n\nimpl<R> TokenParser<R> for ImportedBinding\nwhere\n    R: ReadChar,\n{\n    type Output = Identifier;\n\n    #[inline]\n    fn parse(self, cursor: &mut Cursor<R>, interner: &mut Interner) -> ParseResult<Self::Output> {\n        BindingIdentifier::new(false, true).parse(cursor, interner)\n    }\n}\n\n/// Parses a named import list.\n///\n/// More information:\n///  - [ECMAScript specification][spec]\n///\n/// [spec]: https://tc39.es/ecma262/#prod-NamedImports\n#[derive(Debug, Clone, Copy)]\nstruct NamedImports;\n\nimpl<R> TokenParser<R> for NamedImports\nwhere\n    R: ReadChar,\n{\n    type Output = Box<[AstImportSpecifier]>;\n\n    fn parse(self, cursor: &mut Cursor<R>, interner: &mut Interner) -> ParseResult<Self::Output> {\n        cursor.expect(Punctuator::OpenBlock, \"import declaration\", interner)?;\n\n        let mut list = Vec::new();\n\n        loop {\n            let tok = cursor.peek(0, interner).or_abrupt()?;\n            match tok.kind() {\n                TokenKind::Punctuator(Punctuator::CloseBlock) => {\n                    cursor.advance(interner);\n                    break;\n                }\n                TokenKind::Punctuator(Punctuator::Comma) => {\n                    if list.is_empty() {\n                        return Err(Error::expected(\n                            [\n                                Punctuator::CloseBlock.to_string(),\n                                \"string literal\".to_owned(),\n                                \"identifier\".to_owned(),\n                            ],\n                            tok.to_string(interner),\n                            tok.span(),\n                            \"import declaration\",\n                        ));\n                    }\n                    cursor.advance(interner);\n                }\n                TokenKind::StringLiteral(_)\n                | TokenKind::IdentifierName(_)\n                | TokenKind::Keyword(_) => {\n                    list.push(ImportSpecifier.parse(cursor, interner)?);\n                }\n                _ => {\n                    return Err(Error::expected(\n                        [\n                            Punctuator::CloseBlock.to_string(),\n                            Punctuator::Comma.to_string(),\n                        ],\n                        tok.to_string(interner),\n                        tok.span(),\n                        \"import declaration\",\n                    ));\n                }\n            }\n        }\n\n        Ok(list.into_boxed_slice())\n    }\n}\n\n/// Parses an import clause.\n///\n/// More information:\n///  - [ECMAScript specification][spec]\n///\n/// [spec]: https://tc39.es/ecma262/#prod-ImportClause\n#[derive(Debug, Clone)]\nenum ImportClause {\n    Namespace(Option<Identifier>, Identifier),\n    ImportList(Option<Identifier>, Box<[AstImportSpecifier]>),\n}\n\nimpl ImportClause {\n    #[inline]\n    fn with_specifier_and_attributes(\n        self,\n        specifier: ModuleSpecifier,\n        attributes: Box<[ImportAttribute]>,\n    ) -> AstImportDeclaration {\n        match self {\n            Self::Namespace(default, binding) => AstImportDeclaration::new(\n                default,\n                ImportKind::Namespaced { binding },\n                specifier,\n                attributes,\n            ),\n            Self::ImportList(default, names) => {\n                if names.is_empty() {\n                    AstImportDeclaration::new(\n                        default,\n                        ImportKind::DefaultOrUnnamed,\n                        specifier,\n                        attributes,\n                    )\n                } else {\n                    AstImportDeclaration::new(\n                        default,\n                        ImportKind::Named { names },\n                        specifier,\n                        attributes,\n                    )\n                }\n            }\n        }\n    }\n}\n\n/// Parses an import specifier.\n///\n/// More information:\n///  - [ECMAScript specification][spec]\n///\n/// [spec]: https://tc39.es/ecma262/#prod-ImportSpecifier\n#[derive(Debug, Clone, Copy)]\nstruct ImportSpecifier;\n\nimpl<R> TokenParser<R> for ImportSpecifier\nwhere\n    R: ReadChar,\n{\n    type Output = AstImportSpecifier;\n\n    fn parse(self, cursor: &mut Cursor<R>, interner: &mut Interner) -> ParseResult<Self::Output> {\n        let tok = cursor.peek(0, interner).or_abrupt()?;\n\n        match tok.kind() {\n            TokenKind::StringLiteral((name, _)) => {\n                let name = *name;\n                if interner.resolve_expect(name).utf8().is_none() {\n                    return Err(Error::general(\n                        \"import specifiers don't allow unpaired surrogates\",\n                        tok.span().end(),\n                    ));\n                }\n\n                cursor.advance(interner);\n\n                cursor.expect(\n                    TokenKind::identifier(Sym::AS),\n                    \"import declaration\",\n                    interner,\n                )?;\n\n                let binding = ImportedBinding.parse(cursor, interner)?;\n\n                Ok(AstImportSpecifier::new(binding, name))\n            }\n            TokenKind::Keyword((kw, _)) => {\n                let export_name = kw.to_sym();\n\n                cursor.advance(interner);\n\n                cursor.expect(\n                    TokenKind::identifier(Sym::AS),\n                    \"import declaration\",\n                    interner,\n                )?;\n\n                let binding = ImportedBinding.parse(cursor, interner)?;\n\n                Ok(AstImportSpecifier::new(binding, export_name))\n            }\n            TokenKind::IdentifierName((name, _)) => {\n                let name = *name;\n\n                if let Some(token) = cursor.peek(1, interner)?\n                    && token.kind() == &TokenKind::identifier(Sym::AS)\n                {\n                    // export name\n                    cursor.advance(interner);\n\n                    // `as`\n                    cursor.advance(interner);\n\n                    let binding = ImportedBinding.parse(cursor, interner)?;\n                    return Ok(AstImportSpecifier::new(binding, name));\n                }\n\n                let name = ImportedBinding.parse(cursor, interner)?;\n\n                Ok(AstImportSpecifier::new(name, name.sym()))\n            }\n            _ => Err(Error::expected(\n                [\"string literal\".to_owned(), \"identifier\".to_owned()],\n                tok.to_string(interner),\n                tok.span(),\n                \"import declaration\",\n            )),\n        }\n    }\n}\n\n/// Parses a namespace import\n///\n/// More information:\n///  - [ECMAScript specification][spec]\n///\n/// [spec]: https://tc39.es/ecma262/#prod-NameSpaceImport\n#[derive(Debug, Clone, Copy)]\nstruct NameSpaceImport;\n\nimpl<R> TokenParser<R> for NameSpaceImport\nwhere\n    R: ReadChar,\n{\n    type Output = Identifier;\n\n    fn parse(self, cursor: &mut Cursor<R>, interner: &mut Interner) -> ParseResult<Self::Output> {\n        cursor.expect(Punctuator::Mul, \"import declaration\", interner)?;\n        cursor.expect(\n            TokenKind::identifier(Sym::AS),\n            \"import declaration\",\n            interner,\n        )?;\n\n        ImportedBinding.parse(cursor, interner)\n    }\n}\n"
  },
  {
    "path": "core/parser/src/parser/statement/declaration/lexical.rs",
    "content": "//! Lexical declaration parsing.\n//!\n//! This parses `let`, `const`, `using`, and `await using` declarations.\n//!\n//! More information:\n//!  - [ECMAScript specification][spec]\n//!\n//! [spec]: https://tc39.es/ecma262/#sec-let-and-const-declarations\n\nuse crate::{\n    Error,\n    lexer::{Error as LexError, Token, TokenKind},\n    parser::{\n        AllowAwait, AllowIn, AllowYield, OrAbrupt, ParseResult, TokenParser,\n        cursor::{Cursor, SemicolonResult},\n        expression::Initializer,\n        statement::{ArrayBindingPattern, BindingIdentifier, ObjectBindingPattern},\n    },\n    source::ReadChar,\n};\nuse ast::operations::bound_names;\nuse boa_ast::{self as ast, Keyword, Punctuator, Spanned, declaration::Variable};\nuse boa_interner::{Interner, Sym};\nuse rustc_hash::FxHashSet;\n\n/// The type of lexical declaration being parsed.\n#[derive(Debug, Clone, Copy, PartialEq, Eq)]\nenum DeclarationType {\n    Let,\n    Const,\n    Using,\n    AwaitUsing,\n}\n\n/// Parses a lexical declaration.\n///\n/// More information:\n///  - [ECMAScript specification][spec]\n///\n/// [spec]: https://tc39.es/ecma262/#prod-LexicalDeclaration\n#[derive(Debug, Clone, Copy)]\npub(in crate::parser) struct LexicalDeclaration {\n    allow_in: AllowIn,\n    allow_yield: AllowYield,\n    allow_await: AllowAwait,\n    loop_init: bool,\n}\n\nimpl LexicalDeclaration {\n    /// Creates a new `LexicalDeclaration` parser.\n    pub(in crate::parser) fn new<I, Y, A>(\n        allow_in: I,\n        allow_yield: Y,\n        allow_await: A,\n        loop_init: bool,\n    ) -> Self\n    where\n        I: Into<AllowIn>,\n        Y: Into<AllowYield>,\n        A: Into<AllowAwait>,\n    {\n        Self {\n            allow_in: allow_in.into(),\n            allow_yield: allow_yield.into(),\n            allow_await: allow_await.into(),\n            loop_init,\n        }\n    }\n}\n\nimpl<R> TokenParser<R> for LexicalDeclaration\nwhere\n    R: ReadChar,\n{\n    type Output = ast::declaration::LexicalDeclaration;\n\n    fn parse(self, cursor: &mut Cursor<R>, interner: &mut Interner) -> ParseResult<Self::Output> {\n        let tok = cursor.next(interner).or_abrupt()?;\n\n        let lexical_declaration = match tok.kind() {\n            TokenKind::Keyword((Keyword::Const | Keyword::Let | Keyword::Using, true)) => {\n                return Err(Error::general(\n                    \"Keyword must not contain escaped characters\",\n                    tok.span().start(),\n                ));\n            }\n            TokenKind::Keyword((Keyword::Const, false)) => BindingList::new(\n                self.allow_in,\n                self.allow_yield,\n                self.allow_await,\n                DeclarationType::Const,\n                self.loop_init,\n            )\n            .parse(cursor, interner)?,\n            TokenKind::Keyword((Keyword::Let, false)) => BindingList::new(\n                self.allow_in,\n                self.allow_yield,\n                self.allow_await,\n                DeclarationType::Let,\n                self.loop_init,\n            )\n            .parse(cursor, interner)?,\n            TokenKind::Keyword((Keyword::Using, false)) => BindingList::new(\n                self.allow_in,\n                self.allow_yield,\n                self.allow_await,\n                DeclarationType::Using,\n                self.loop_init,\n            )\n            .parse(cursor, interner)?,\n            TokenKind::Keyword((Keyword::Await, false)) => {\n                // Per spec: <https://arai-a.github.io/ecma262-compare/snapshot.html?pr=3000#prod-LexicalDeclaration>\n                // `await using` is only valid when [+Await] is true\n                if !self.allow_await.0 {\n                    return Err(Error::general(\n                        \"Unexpected token 'await'\",\n                        tok.span().start(),\n                    ));\n                }\n\n                // Per spec: <https://arai-a.github.io/ecma262-compare/snapshot.html?pr=3000#prod-AwaitUsingDeclarationHead>\n                // There must be [no LineTerminator here] between `await` and `using`\n                let next_tok = cursor.peek_no_skip_line_term(0, interner).or_abrupt()?;\n\n                // Check if next token is a line terminator\n                if next_tok.kind() == &TokenKind::LineTerminator {\n                    return Err(Error::general(\n                        \"Unexpected token 'await'\",\n                        tok.span().start(),\n                    ));\n                }\n\n                // Check if this is `await using`\n                if matches!(next_tok.kind(), TokenKind::Keyword((Keyword::Using, false))) {\n                    cursor.advance(interner); // consume 'using'\n                    BindingList::new(\n                        self.allow_in,\n                        self.allow_yield,\n                        self.allow_await,\n                        DeclarationType::AwaitUsing,\n                        self.loop_init,\n                    )\n                    .parse(cursor, interner)?\n                } else {\n                    return Err(Error::general(\n                        \"Unexpected token 'await'\",\n                        tok.span().start(),\n                    ));\n                }\n            }\n            _ => unreachable!(\"unknown token found: {:?}\", tok),\n        };\n\n        if !self.loop_init {\n            cursor.expect_semicolon(\"lexical declaration\", interner)?;\n        }\n\n        // It is a Syntax Error if the BoundNames of BindingList contains \"let\".\n        // It is a Syntax Error if the BoundNames of BindingList contains any duplicate entries.\n        let bound_names = bound_names(&lexical_declaration);\n        let mut names = FxHashSet::default();\n        for name in bound_names {\n            if name == Sym::LET {\n                return Err(Error::general(\n                    \"'let' is disallowed as a lexically bound name\",\n                    tok.span().start(),\n                ));\n            }\n            if !names.insert(name) {\n                return Err(Error::general(\n                    \"lexical name declared multiple times\",\n                    tok.span().start(),\n                ));\n            }\n        }\n\n        Ok(lexical_declaration)\n    }\n}\n\n/// Check if the given token is valid after the `let` keyword of a lexical declaration.\npub(crate) fn allowed_token_after_let(token: Option<&Token>) -> bool {\n    matches!(\n        token.map(Token::kind),\n        Some(\n            TokenKind::IdentifierName(_)\n                | TokenKind::Keyword((\n                    Keyword::Await | Keyword::Yield | Keyword::Let | Keyword::Async | Keyword::Of,\n                    _\n                ))\n                | TokenKind::Punctuator(Punctuator::OpenBlock | Punctuator::OpenBracket),\n        )\n    )\n}\n\n/// Parses a binding list.\n///\n/// It will return an error if a `const` or `using` declaration is being parsed and there is no\n/// initializer.\n///\n/// More information:\n///  - [ECMAScript specification][spec]\n///\n/// [spec]: https://tc39.es/ecma262/#prod-BindingList\n#[derive(Debug, Clone, Copy)]\nstruct BindingList {\n    allow_in: AllowIn,\n    allow_yield: AllowYield,\n    allow_await: AllowAwait,\n    declaration_type: DeclarationType,\n    loop_init: bool,\n}\n\nimpl BindingList {\n    /// Creates a new `BindingList` parser.\n    fn new<I, Y, A>(\n        allow_in: I,\n        allow_yield: Y,\n        allow_await: A,\n        declaration_type: DeclarationType,\n        loop_init: bool,\n    ) -> Self\n    where\n        I: Into<AllowIn>,\n        Y: Into<AllowYield>,\n        A: Into<AllowAwait>,\n    {\n        Self {\n            allow_in: allow_in.into(),\n            allow_yield: allow_yield.into(),\n            allow_await: allow_await.into(),\n            declaration_type,\n            loop_init,\n        }\n    }\n}\n\nimpl<R> TokenParser<R> for BindingList\nwhere\n    R: ReadChar,\n{\n    type Output = ast::declaration::LexicalDeclaration;\n\n    fn parse(self, cursor: &mut Cursor<R>, interner: &mut Interner) -> ParseResult<Self::Output> {\n        // Create vectors to store the variable declarations\n        let mut decls = Vec::new();\n        let requires_initializer = matches!(\n            self.declaration_type,\n            DeclarationType::Const | DeclarationType::Using | DeclarationType::AwaitUsing\n        );\n\n        loop {\n            let decl = LexicalBinding::new(\n                self.allow_in,\n                self.allow_yield,\n                self.allow_await,\n                self.declaration_type,\n            )\n            .parse(cursor, interner)?;\n\n            if requires_initializer {\n                let init_is_some = decl.init().is_some();\n\n                if init_is_some || self.loop_init {\n                    decls.push(decl);\n                } else {\n                    let next = cursor.next(interner).or_abrupt()?;\n                    let decl_name = match self.declaration_type {\n                        DeclarationType::Const => \"const\",\n                        DeclarationType::Using => \"using\",\n                        DeclarationType::AwaitUsing => \"await using\",\n                        DeclarationType::Let => unreachable!(),\n                    };\n                    return Err(Error::general(\n                        format!(\"Expected initializer for {decl_name} declaration\"),\n                        next.span().start(),\n                    ));\n                }\n            } else {\n                decls.push(decl);\n            }\n\n            match cursor.peek_semicolon(interner)? {\n                SemicolonResult::Found(_) => break,\n                SemicolonResult::NotFound(tk)\n                    if tk.kind() == &TokenKind::Keyword((Keyword::Of, true))\n                        || tk.kind() == &TokenKind::Keyword((Keyword::In, true)) =>\n                {\n                    return Err(Error::general(\n                        \"Keyword must not contain escaped characters\",\n                        tk.span().start(),\n                    ));\n                }\n                SemicolonResult::NotFound(tk)\n                    if tk.kind() == &TokenKind::Keyword((Keyword::Of, false))\n                        || tk.kind() == &TokenKind::Keyword((Keyword::In, false)) =>\n                {\n                    break;\n                }\n                SemicolonResult::NotFound(tk)\n                    if tk.kind() == &TokenKind::Punctuator(Punctuator::Comma) =>\n                {\n                    // We discard the comma\n                    cursor.advance(interner);\n                }\n                SemicolonResult::NotFound(_) if self.loop_init => break,\n                SemicolonResult::NotFound(_) => {\n                    let next = cursor.next(interner).or_abrupt()?;\n                    return Err(Error::expected(\n                        [\";\".to_owned(), \"line terminator\".to_owned()],\n                        next.to_string(interner),\n                        next.span(),\n                        \"lexical declaration binding list\",\n                    ));\n                }\n            }\n        }\n\n        let decls = decls\n            .try_into()\n            .expect(\"`LexicalBinding` must return at least one variable\");\n\n        Ok(match self.declaration_type {\n            DeclarationType::Const => ast::declaration::LexicalDeclaration::Const(decls),\n            DeclarationType::Let => ast::declaration::LexicalDeclaration::Let(decls),\n            DeclarationType::Using => ast::declaration::LexicalDeclaration::Using(decls),\n            DeclarationType::AwaitUsing => ast::declaration::LexicalDeclaration::AwaitUsing(decls),\n        })\n    }\n}\n\n/// Lexical binding parsing.\n///\n/// More information:\n///  - [ECMAScript specification][spec]\n///\n/// [spec]: https://tc39.es/ecma262/#prod-LexicalBinding\nstruct LexicalBinding {\n    allow_in: AllowIn,\n    allow_yield: AllowYield,\n    allow_await: AllowAwait,\n    declaration_type: DeclarationType,\n}\n\nimpl LexicalBinding {\n    /// Creates a new `BindingList` parser.\n    fn new<I, Y, A>(\n        allow_in: I,\n        allow_yield: Y,\n        allow_await: A,\n        declaration_type: DeclarationType,\n    ) -> Self\n    where\n        I: Into<AllowIn>,\n        Y: Into<AllowYield>,\n        A: Into<AllowAwait>,\n    {\n        Self {\n            allow_in: allow_in.into(),\n            allow_yield: allow_yield.into(),\n            allow_await: allow_await.into(),\n            declaration_type,\n        }\n    }\n}\n\nimpl<R> TokenParser<R> for LexicalBinding\nwhere\n    R: ReadChar,\n{\n    type Output = Variable;\n\n    fn parse(self, cursor: &mut Cursor<R>, interner: &mut Interner) -> ParseResult<Self::Output> {\n        let peek_token = cursor.peek(0, interner).or_abrupt()?;\n        let position = peek_token.span().start();\n\n        match peek_token.kind() {\n            TokenKind::Punctuator(Punctuator::OpenBlock) => {\n                // Per spec: https://tc39.es/proposal-explicit-resource-management/\n                // The grammar uses ~Pattern parameter which means destructuring is NOT allowed\n                // for `using` and `await using` declarations\n                if matches!(\n                    self.declaration_type,\n                    DeclarationType::Using | DeclarationType::AwaitUsing\n                ) {\n                    return Err(Error::general(\n                        \"destructuring patterns are not allowed in using declarations\",\n                        position,\n                    ));\n                }\n\n                let bindings = ObjectBindingPattern::new(self.allow_yield, self.allow_await)\n                    .parse(cursor, interner)?;\n\n                let init = if cursor\n                    .peek(0, interner)?\n                    .filter(|t| *t.kind() == TokenKind::Punctuator(Punctuator::Assign))\n                    .is_some()\n                {\n                    Some(\n                        Initializer::new(self.allow_in, self.allow_yield, self.allow_await)\n                            .parse(cursor, interner)?,\n                    )\n                } else {\n                    None\n                };\n\n                let declaration = bindings.into();\n\n                if bound_names(&declaration).contains(&Sym::LET) {\n                    return Err(Error::lex(LexError::Syntax(\n                        \"'let' is disallowed as a lexically bound name\".into(),\n                        position,\n                    )));\n                }\n\n                Ok(Variable::from_pattern(declaration, init))\n            }\n            TokenKind::Punctuator(Punctuator::OpenBracket) => {\n                // Per spec: https://tc39.es/proposal-explicit-resource-management/\n                // The grammar uses ~Pattern parameter which means destructuring is NOT allowed\n                // for `using` and `await using` declarations\n                if matches!(\n                    self.declaration_type,\n                    DeclarationType::Using | DeclarationType::AwaitUsing\n                ) {\n                    return Err(Error::general(\n                        \"destructuring patterns are not allowed in using declarations\",\n                        position,\n                    ));\n                }\n\n                let bindings = ArrayBindingPattern::new(self.allow_yield, self.allow_await)\n                    .parse(cursor, interner)?;\n\n                let init = if cursor\n                    .peek(0, interner)?\n                    .filter(|t| *t.kind() == TokenKind::Punctuator(Punctuator::Assign))\n                    .is_some()\n                {\n                    Some(\n                        Initializer::new(self.allow_in, self.allow_yield, self.allow_await)\n                            .parse(cursor, interner)?,\n                    )\n                } else {\n                    None\n                };\n\n                let declaration = bindings.into();\n\n                if bound_names(&declaration).contains(&Sym::LET) {\n                    return Err(Error::lex(LexError::Syntax(\n                        \"'let' is disallowed as a lexically bound name\".into(),\n                        position,\n                    )));\n                }\n\n                Ok(Variable::from_pattern(declaration, init))\n            }\n            _ => {\n                let ident = BindingIdentifier::new(self.allow_yield, self.allow_await)\n                    .parse(cursor, interner)?;\n\n                if ident == Sym::LET {\n                    return Err(Error::lex(LexError::Syntax(\n                        \"'let' is disallowed as a lexically bound name\".into(),\n                        position,\n                    )));\n                }\n\n                let init = if cursor\n                    .peek(0, interner)?\n                    .filter(|t| *t.kind() == TokenKind::Punctuator(Punctuator::Assign))\n                    .is_some()\n                {\n                    let mut init =\n                        Initializer::new(self.allow_in, self.allow_yield, self.allow_await)\n                            .parse(cursor, interner)?;\n                    init.set_anonymous_function_definition_name(&ident);\n                    Some(init)\n                } else {\n                    None\n                };\n                Ok(Variable::from_identifier(ident, init))\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "core/parser/src/parser/statement/declaration/mod.rs",
    "content": "//! Declaration parsing.\n//!\n//! More information:\n//!  - [MDN documentation][mdn]\n//!  - [ECMAScript specification][spec]\n//!\n//! [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements#Declarations\n//! [spec]: https://tc39.es/ecma262/#sec-declarations-and-the-variable-statement\n\nmod export;\nmod hoistable;\nmod import;\nmod lexical;\n#[cfg(test)]\nmod tests;\n\npub(in crate::parser) use self::{\n    export::ExportDeclaration,\n    hoistable::{\n        ClassDeclaration, FunctionDeclaration, HoistableDeclaration, class_decl::ClassTail,\n    },\n    import::ImportDeclaration,\n    lexical::{LexicalDeclaration, allowed_token_after_let},\n};\nuse crate::{\n    Error,\n    lexer::TokenKind,\n    parser::{AllowAwait, AllowYield, Cursor, OrAbrupt, ParseResult, TokenParser},\n    source::ReadChar,\n};\nuse boa_ast::{self as ast, Keyword, Punctuator, Spanned, declaration::ImportAttribute};\nuse boa_interner::{Interner, Sym};\n\n/// Parses a declaration.\n///\n/// More information:\n///  - [ECMAScript specification][spec]\n///\n/// [spec]: https://tc39.es/ecma262/#prod-Declaration\n#[derive(Debug, Clone, Copy)]\npub(super) struct Declaration {\n    allow_yield: AllowYield,\n    allow_await: AllowAwait,\n}\n\nimpl Declaration {\n    /// Creates a new declaration parser.\n    #[inline]\n    pub(super) fn new<Y, A>(allow_yield: Y, allow_await: A) -> Self\n    where\n        Y: Into<AllowYield>,\n        A: Into<AllowAwait>,\n    {\n        Self {\n            allow_yield: allow_yield.into(),\n            allow_await: allow_await.into(),\n        }\n    }\n}\n\nimpl<R> TokenParser<R> for Declaration\nwhere\n    R: ReadChar,\n{\n    type Output = ast::Declaration;\n\n    fn parse(self, cursor: &mut Cursor<R>, interner: &mut Interner) -> ParseResult<Self::Output> {\n        let tok = cursor.peek(0, interner).or_abrupt()?;\n        let tok_kind = tok.kind().clone();\n        let tok_span = tok.span();\n        let tok_str = tok.to_string(interner);\n\n        match tok_kind {\n            TokenKind::Keyword((Keyword::Function | Keyword::Async | Keyword::Class, _)) => {\n                HoistableDeclaration::new(self.allow_yield, self.allow_await, false)\n                    .parse(cursor, interner)\n            }\n            TokenKind::Keyword((Keyword::Const | Keyword::Let | Keyword::Using, _)) => {\n                LexicalDeclaration::new(true, self.allow_yield, self.allow_await, false)\n                    .parse(cursor, interner)\n                    .map(Into::into)\n            }\n            TokenKind::Keyword((Keyword::Await, false)) => {\n                // Check if this is `await using`\n                // Per spec, there must be [no LineTerminator here] between `await` and `using`\n                if let Some(next_tok) = cursor.peek_no_skip_line_term(1, interner)?\n                    && next_tok.kind() != &TokenKind::LineTerminator\n                    && matches!(next_tok.kind(), TokenKind::Keyword((Keyword::Using, false)))\n                {\n                    return LexicalDeclaration::new(\n                        true,\n                        self.allow_yield,\n                        self.allow_await,\n                        false,\n                    )\n                    .parse(cursor, interner)\n                    .map(Into::into);\n                }\n                Err(Error::expected(\n                    [\n                        Keyword::Function.to_string(),\n                        Keyword::Async.to_string(),\n                        Keyword::Class.to_string(),\n                        Keyword::Const.to_string(),\n                        Keyword::Let.to_string(),\n                        Keyword::Using.to_string(),\n                    ],\n                    tok_str,\n                    tok_span,\n                    \"export declaration\",\n                ))\n            }\n            _ => Err(Error::expected(\n                [\n                    Keyword::Function.to_string(),\n                    Keyword::Async.to_string(),\n                    Keyword::Class.to_string(),\n                    Keyword::Const.to_string(),\n                    Keyword::Let.to_string(),\n                    Keyword::Using.to_string(),\n                ],\n                tok_str,\n                tok_span,\n                \"export declaration\",\n            )),\n        }\n    }\n}\n\n/// Parses a `from` clause.\n///\n/// More information:\n///  - [ECMAScript specification][spec]\n///\n/// [spec]: https://tc39.es/ecma262/#prod-FromClause\n#[derive(Debug, Clone, Copy)]\nstruct FromClause {\n    context: &'static str,\n}\n\nimpl FromClause {\n    /// Creates a new `from` clause parser\n    #[inline]\n    const fn new(context: &'static str) -> Self {\n        Self { context }\n    }\n}\n\nimpl<R> TokenParser<R> for FromClause\nwhere\n    R: ReadChar,\n{\n    type Output = ast::declaration::ModuleSpecifier;\n\n    fn parse(self, cursor: &mut Cursor<R>, interner: &mut Interner) -> ParseResult<Self::Output> {\n        cursor.expect(TokenKind::identifier(Sym::FROM), self.context, interner)?;\n\n        let tok = cursor.next(interner).or_abrupt()?;\n\n        let TokenKind::StringLiteral((from, _)) = tok.kind() else {\n            return Err(Error::expected(\n                [\"string literal\".to_owned()],\n                tok.to_string(interner),\n                tok.span(),\n                self.context,\n            ));\n        };\n\n        Ok((*from).into())\n    }\n}\n\n/// Parses an optional `with` clause for import attributes.\n///\n/// More information:\n///  - [ECMAScript specification][spec]\n///\n/// [spec]: https://tc39.es/ecma262/#sec-imports\n#[derive(Debug, Clone, Copy)]\npub(in crate::parser) struct WithClause {\n    context: &'static str,\n}\n\nimpl WithClause {\n    /// Creates a new `with` clause parser.\n    #[inline]\n    pub(in crate::parser) const fn new(context: &'static str) -> Self {\n        Self { context }\n    }\n}\n\nimpl<R> TokenParser<R> for WithClause\nwhere\n    R: ReadChar,\n{\n    type Output = Box<[ImportAttribute]>;\n\n    fn parse(self, cursor: &mut Cursor<R>, interner: &mut Interner) -> ParseResult<Self::Output> {\n        let Some(tok) = cursor.peek(0, interner)? else {\n            return Ok(Box::default());\n        };\n\n        if !matches!(tok.kind(), TokenKind::Keyword((Keyword::With, _))) {\n            return Ok(Box::default());\n        }\n\n        let Some(next_tok) = cursor.peek(1, interner)? else {\n            return Ok(Box::default());\n        };\n\n        if next_tok.kind() != &TokenKind::Punctuator(Punctuator::OpenBlock) {\n            return Ok(Box::default());\n        }\n\n        cursor.advance(interner);\n\n        cursor.expect(Punctuator::OpenBlock, self.context, interner)?;\n\n        let mut attributes = Vec::new();\n\n        loop {\n            let tok = cursor.peek(0, interner).or_abrupt()?;\n\n            if tok.kind() == &TokenKind::Punctuator(Punctuator::CloseBlock) {\n                break;\n            }\n\n            let key_tok = cursor.next(interner).or_abrupt()?;\n            let key = match key_tok.kind() {\n                TokenKind::IdentifierName((name, _)) | TokenKind::StringLiteral((name, _)) => *name,\n                TokenKind::Keyword((kw, _)) => kw.to_sym(),\n                _ => {\n                    return Err(Error::expected(\n                        [\"identifier\".to_owned(), \"string literal\".to_owned()],\n                        key_tok.to_string(interner),\n                        key_tok.span(),\n                        self.context,\n                    ));\n                }\n            };\n\n            cursor.expect(Punctuator::Colon, self.context, interner)?;\n\n            let value_tok = cursor.next(interner).or_abrupt()?;\n            let TokenKind::StringLiteral((value, _)) = value_tok.kind() else {\n                return Err(Error::expected(\n                    [\"string literal\".to_owned()],\n                    value_tok.to_string(interner),\n                    value_tok.span(),\n                    self.context,\n                ));\n            };\n\n            if attributes\n                .iter()\n                .any(|attr: &ImportAttribute| attr.key() == key)\n            {\n                return Err(Error::general(\n                    \"duplicate attribute key in import attributes\",\n                    key_tok.span().start(),\n                ));\n            }\n\n            attributes.push(ImportAttribute::new(key, *value));\n\n            let tok = cursor.peek(0, interner).or_abrupt()?;\n            if tok.kind() == &TokenKind::Punctuator(Punctuator::Comma) {\n                cursor.advance(interner);\n            } else if tok.kind() != &TokenKind::Punctuator(Punctuator::CloseBlock) {\n                return Err(Error::expected(\n                    [\",\".to_owned(), \"}\".to_owned()],\n                    tok.to_string(interner),\n                    tok.span(),\n                    self.context,\n                ));\n            }\n        }\n\n        cursor.expect(Punctuator::CloseBlock, self.context, interner)?;\n\n        Ok(attributes.into_boxed_slice())\n    }\n}\n"
  },
  {
    "path": "core/parser/src/parser/statement/declaration/tests.rs",
    "content": "use crate::parser::tests::{check_invalid_script, check_module_parser, check_script_parser};\nuse crate::{Parser, Source};\nuse boa_ast::{\n    Declaration, ModuleItem, Span, Statement,\n    declaration::{\n        ExportDeclaration, ExportSpecifier, ImportAttribute, ImportDeclaration, ImportKind,\n        LexicalDeclaration, ModuleSpecifier, ReExportKind, VarDeclaration, Variable,\n    },\n    expression::{\n        Identifier,\n        literal::{Literal, LiteralKind},\n    },\n};\nuse boa_interner::{Interner, Sym};\nuse boa_macros::utf16;\nuse indoc::indoc;\n\n/// Checks `var` declaration parsing.\n#[test]\nfn var_declaration() {\n    let interner = &mut Interner::default();\n    check_script_parser(\n        \"var a = 5;\",\n        vec![\n            Statement::Var(VarDeclaration(\n                vec![Variable::from_identifier(\n                    Identifier::new(\n                        interner.get_or_intern_static(\"a\", utf16!(\"a\")),\n                        Span::new((1, 5), (1, 6)),\n                    ),\n                    Some(Literal::new(5, Span::new((1, 9), (1, 10))).into()),\n                )]\n                .try_into()\n                .unwrap(),\n            ))\n            .into(),\n        ],\n        interner,\n    );\n}\n\n/// Checks `var` declaration parsing with reserved words.\n#[test]\nfn var_declaration_keywords() {\n    let interner = &mut Interner::default();\n    check_script_parser(\n        \"var yield = 5;\",\n        vec![\n            Statement::Var(VarDeclaration(\n                vec![Variable::from_identifier(\n                    Identifier::new(Sym::YIELD, Span::new((1, 5), (1, 10))),\n                    Some(Literal::new(5, Span::new((1, 13), (1, 14))).into()),\n                )]\n                .try_into()\n                .unwrap(),\n            ))\n            .into(),\n        ],\n        interner,\n    );\n\n    let interner = &mut Interner::default();\n    check_script_parser(\n        \"var await = 5;\",\n        vec![\n            Statement::Var(VarDeclaration(\n                vec![Variable::from_identifier(\n                    Identifier::new(Sym::AWAIT, Span::new((1, 5), (1, 10))),\n                    Some(Literal::new(5, Span::new((1, 13), (1, 14))).into()),\n                )]\n                .try_into()\n                .unwrap(),\n            ))\n            .into(),\n        ],\n        interner,\n    );\n}\n\n/// Checks `var` declaration parsing with no spaces.\n#[test]\nfn var_declaration_no_spaces() {\n    let interner = &mut Interner::default();\n    check_script_parser(\n        \"var a=5;\",\n        vec![\n            Statement::Var(VarDeclaration(\n                vec![Variable::from_identifier(\n                    Identifier::new(\n                        interner.get_or_intern_static(\"a\", utf16!(\"a\")),\n                        Span::new((1, 5), (1, 6)),\n                    ),\n                    Some(Literal::new(5, Span::new((1, 7), (1, 8))).into()),\n                )]\n                .try_into()\n                .unwrap(),\n            ))\n            .into(),\n        ],\n        interner,\n    );\n}\n\n/// Checks empty `var` declaration parsing.\n#[test]\nfn empty_var_declaration() {\n    let interner = &mut Interner::default();\n    check_script_parser(\n        \"var a;\",\n        vec![\n            Statement::Var(VarDeclaration(\n                vec![Variable::from_identifier(\n                    Identifier::new(\n                        interner.get_or_intern_static(\"a\", utf16!(\"a\")),\n                        Span::new((1, 5), (1, 6)),\n                    ),\n                    None,\n                )]\n                .try_into()\n                .unwrap(),\n            ))\n            .into(),\n        ],\n        interner,\n    );\n}\n\n/// Checks multiple `var` declarations.\n#[test]\nfn multiple_var_declaration() {\n    let interner = &mut Interner::default();\n    check_script_parser(\n        \"var a = 5, b, c = 6;\",\n        vec![\n            Statement::Var(VarDeclaration(\n                vec![\n                    Variable::from_identifier(\n                        Identifier::new(\n                            interner.get_or_intern_static(\"a\", utf16!(\"a\")),\n                            Span::new((1, 5), (1, 6)),\n                        ),\n                        Some(Literal::new(5, Span::new((1, 9), (1, 10))).into()),\n                    ),\n                    Variable::from_identifier(\n                        Identifier::new(\n                            interner.get_or_intern_static(\"b\", utf16!(\"b\")),\n                            Span::new((1, 12), (1, 13)),\n                        ),\n                        None,\n                    ),\n                    Variable::from_identifier(\n                        Identifier::new(\n                            interner.get_or_intern_static(\"c\", utf16!(\"c\")),\n                            Span::new((1, 15), (1, 16)),\n                        ),\n                        Some(Literal::new(6, Span::new((1, 19), (1, 20))).into()),\n                    ),\n                ]\n                .try_into()\n                .unwrap(),\n            ))\n            .into(),\n        ],\n        interner,\n    );\n}\n\n/// Checks `let` declaration parsing.\n#[test]\nfn let_declaration() {\n    let interner = &mut Interner::default();\n    check_script_parser(\n        \"let a = 5;\",\n        vec![\n            Declaration::Lexical(LexicalDeclaration::Let(\n                vec![Variable::from_identifier(\n                    Identifier::new(\n                        interner.get_or_intern_static(\"a\", utf16!(\"a\")),\n                        Span::new((1, 5), (1, 6)),\n                    ),\n                    Some(Literal::new(5, Span::new((1, 9), (1, 10))).into()),\n                )]\n                .try_into()\n                .unwrap(),\n            ))\n            .into(),\n        ],\n        interner,\n    );\n}\n\n/// Checks `let` declaration parsing with reserved words.\n#[test]\nfn let_declaration_keywords() {\n    let interner = &mut Interner::default();\n    check_script_parser(\n        \"let yield = 5;\",\n        vec![\n            Declaration::Lexical(LexicalDeclaration::Let(\n                vec![Variable::from_identifier(\n                    Identifier::new(Sym::YIELD, Span::new((1, 5), (1, 10))),\n                    Some(Literal::new(5, Span::new((1, 13), (1, 14))).into()),\n                )]\n                .try_into()\n                .unwrap(),\n            ))\n            .into(),\n        ],\n        interner,\n    );\n\n    let interner = &mut Interner::default();\n    check_script_parser(\n        \"let await = 5;\",\n        vec![\n            Declaration::Lexical(LexicalDeclaration::Let(\n                vec![Variable::from_identifier(\n                    Identifier::new(Sym::AWAIT, Span::new((1, 5), (1, 10))),\n                    Some(Literal::new(5, Span::new((1, 13), (1, 14))).into()),\n                )]\n                .try_into()\n                .unwrap(),\n            ))\n            .into(),\n        ],\n        interner,\n    );\n}\n\n/// Checks `let` declaration parsing with no spaces.\n#[test]\nfn let_declaration_no_spaces() {\n    let interner = &mut Interner::default();\n    check_script_parser(\n        \"let a=5;\",\n        vec![\n            Declaration::Lexical(LexicalDeclaration::Let(\n                vec![Variable::from_identifier(\n                    Identifier::new(\n                        interner.get_or_intern_static(\"a\", utf16!(\"a\")),\n                        Span::new((1, 5), (1, 6)),\n                    ),\n                    Some(Literal::new(5, Span::new((1, 7), (1, 8))).into()),\n                )]\n                .try_into()\n                .unwrap(),\n            ))\n            .into(),\n        ],\n        interner,\n    );\n}\n\n/// Checks `let` declaration with a reserved keyword as identifier.\n#[test]\nfn let_declaration_reserved_keyword_identifier() {\n    let interner = &mut Interner::default();\n    check_script_parser(\n        \"let of = 1;\",\n        vec![\n            Declaration::Lexical(LexicalDeclaration::Let(\n                vec![Variable::from_identifier(\n                    Identifier::new(\n                        interner.get_or_intern_static(\"of\", utf16!(\"of\")),\n                        Span::new((1, 5), (1, 7)),\n                    ),\n                    Some(Literal::new(1, Span::new((1, 10), (1, 11))).into()),\n                )]\n                .try_into()\n                .unwrap(),\n            ))\n            .into(),\n        ],\n        interner,\n    );\n}\n\n/// Checks empty `let` declaration parsing.\n#[test]\nfn empty_let_declaration() {\n    let interner = &mut Interner::default();\n    check_script_parser(\n        \"let a;\",\n        vec![\n            Declaration::Lexical(LexicalDeclaration::Let(\n                vec![Variable::from_identifier(\n                    Identifier::new(\n                        interner.get_or_intern_static(\"a\", utf16!(\"a\")),\n                        Span::new((1, 5), (1, 6)),\n                    ),\n                    None,\n                )]\n                .try_into()\n                .unwrap(),\n            ))\n            .into(),\n        ],\n        interner,\n    );\n}\n\n/// Checks multiple `let` declarations.\n#[test]\nfn multiple_let_declaration() {\n    let interner = &mut Interner::default();\n    check_script_parser(\n        \"let a = 5, b, c = 6;\",\n        vec![\n            Declaration::Lexical(LexicalDeclaration::Let(\n                vec![\n                    Variable::from_identifier(\n                        Identifier::new(\n                            interner.get_or_intern_static(\"a\", utf16!(\"a\")),\n                            Span::new((1, 5), (1, 6)),\n                        ),\n                        Some(Literal::new(5, Span::new((1, 9), (1, 10))).into()),\n                    ),\n                    Variable::from_identifier(\n                        Identifier::new(\n                            interner.get_or_intern_static(\"b\", utf16!(\"b\")),\n                            Span::new((1, 12), (1, 13)),\n                        ),\n                        None,\n                    ),\n                    Variable::from_identifier(\n                        Identifier::new(\n                            interner.get_or_intern_static(\"c\", utf16!(\"c\")),\n                            Span::new((1, 15), (1, 16)),\n                        ),\n                        Some(Literal::new(6, Span::new((1, 19), (1, 20))).into()),\n                    ),\n                ]\n                .try_into()\n                .unwrap(),\n            ))\n            .into(),\n        ],\n        interner,\n    );\n}\n\n/// Checks `const` declaration parsing.\n#[test]\nfn const_declaration() {\n    let interner = &mut Interner::default();\n    check_script_parser(\n        \"const a = 5;\",\n        vec![\n            Declaration::Lexical(LexicalDeclaration::Const(\n                vec![Variable::from_identifier(\n                    Identifier::new(\n                        interner.get_or_intern_static(\"a\", utf16!(\"a\")),\n                        Span::new((1, 7), (1, 8)),\n                    ),\n                    Some(Literal::new(5, Span::new((1, 11), (1, 12))).into()),\n                )]\n                .try_into()\n                .unwrap(),\n            ))\n            .into(),\n        ],\n        interner,\n    );\n}\n\n/// Checks `const` declaration parsing with reserved words.\n#[test]\nfn const_declaration_keywords() {\n    let interner = &mut Interner::default();\n    check_script_parser(\n        \"const yield = 5;\",\n        vec![\n            Declaration::Lexical(LexicalDeclaration::Const(\n                vec![Variable::from_identifier(\n                    Identifier::new(Sym::YIELD, Span::new((1, 7), (1, 12))),\n                    Some(Literal::new(5, Span::new((1, 15), (1, 16))).into()),\n                )]\n                .try_into()\n                .unwrap(),\n            ))\n            .into(),\n        ],\n        interner,\n    );\n\n    let interner = &mut Interner::default();\n    check_script_parser(\n        \"const await = 5;\",\n        vec![\n            Declaration::Lexical(LexicalDeclaration::Const(\n                vec![Variable::from_identifier(\n                    Identifier::new(Sym::AWAIT, Span::new((1, 7), (1, 12))),\n                    Some(Literal::new(5, Span::new((1, 15), (1, 16))).into()),\n                )]\n                .try_into()\n                .unwrap(),\n            ))\n            .into(),\n        ],\n        interner,\n    );\n}\n\n/// Checks `const` declaration parsing with no spaces.\n#[test]\nfn const_declaration_no_spaces() {\n    let interner = &mut Interner::default();\n    check_script_parser(\n        \"const a=5;\",\n        vec![\n            Declaration::Lexical(LexicalDeclaration::Const(\n                vec![Variable::from_identifier(\n                    Identifier::new(\n                        interner.get_or_intern_static(\"a\", utf16!(\"a\")),\n                        Span::new((1, 7), (1, 8)),\n                    ),\n                    Some(Literal::new(5, Span::new((1, 9), (1, 10))).into()),\n                )]\n                .try_into()\n                .unwrap(),\n            ))\n            .into(),\n        ],\n        interner,\n    );\n}\n\n/// Checks empty `const` declaration parsing.\n#[test]\nfn empty_const_declaration() {\n    check_invalid_script(\"const a;\");\n}\n\n/// Checks multiple `const` declarations.\n#[test]\nfn multiple_const_declaration() {\n    let interner = &mut Interner::default();\n    check_script_parser(\n        \"const a = 5, c = 6;\",\n        vec![\n            Declaration::Lexical(LexicalDeclaration::Const(\n                vec![\n                    Variable::from_identifier(\n                        Identifier::new(\n                            interner.get_or_intern_static(\"a\", utf16!(\"a\")),\n                            Span::new((1, 7), (1, 8)),\n                        ),\n                        Some(Literal::new(5, Span::new((1, 11), (1, 12))).into()),\n                    ),\n                    Variable::from_identifier(\n                        Identifier::new(\n                            interner.get_or_intern_static(\"c\", utf16!(\"c\")),\n                            Span::new((1, 14), (1, 15)),\n                        ),\n                        Some(Literal::new(6, Span::new((1, 18), (1, 19))).into()),\n                    ),\n                ]\n                .try_into()\n                .unwrap(),\n            ))\n            .into(),\n        ],\n        interner,\n    );\n}\n\n/// Checks `LexicalDeclaration` early errors.\n#[test]\nfn lexical_declaration_early_errors() {\n    check_invalid_script(\"let let = 0\");\n    check_invalid_script(\"let a = 0, a = 0\");\n    check_invalid_script(\"const a = 0, a = 0\");\n    check_invalid_script(\"for (let let = 0; ; ) {}\");\n    check_invalid_script(\"for (let a = 0, a = 0; ; ) {}\");\n    check_invalid_script(\"for (const a = 0, a = 0; ; ) {}\");\n}\n\n/// Checks module exports with reserved keywords\n#[test]\nfn module_export_reserved() {\n    let interner = &mut Interner::default();\n    let val = interner.get_or_intern_static(\"val\", utf16!(\"val\"));\n    check_module_parser(\n        indoc! {\"\n            const val = null;\n            export { val as null, val as true, val as false };\n        \"},\n        vec![\n            ModuleItem::StatementListItem(\n                Declaration::Lexical(LexicalDeclaration::Const(\n                    vec![Variable::from_identifier(\n                        Identifier::new(val, Span::new((1, 7), (1, 10))),\n                        Some(Literal::new(LiteralKind::Null, Span::new((1, 13), (1, 17))).into()),\n                    )]\n                    .try_into()\n                    .unwrap(),\n                ))\n                .into(),\n            ),\n            ModuleItem::ExportDeclaration(\n                ExportDeclaration::List(\n                    vec![\n                        ExportSpecifier::new(Sym::NULL, val, false),\n                        ExportSpecifier::new(Sym::TRUE, val, false),\n                        ExportSpecifier::new(Sym::FALSE, val, false),\n                    ]\n                    .into(),\n                )\n                .into(),\n            ),\n        ],\n        interner,\n    );\n}\n\n/// Checks import declaration with a single attribute.\n#[test]\nfn import_with_single_attribute() {\n    let interner = &mut Interner::default();\n    let json = interner.get_or_intern_static(\"json\", utf16!(\"json\"));\n    let foo_json = interner.get_or_intern_static(\"./foo.json\", utf16!(\"./foo.json\"));\n    let type_sym = interner.get_or_intern_static(\"type\", utf16!(\"type\"));\n\n    check_module_parser(\n        r#\"import json from \"./foo.json\" with { type: \"json\" };\"#,\n        vec![ModuleItem::ImportDeclaration(ImportDeclaration::new(\n            Some(Identifier::new(json, Span::new((1, 8), (1, 12)))),\n            ImportKind::DefaultOrUnnamed,\n            ModuleSpecifier::new(foo_json),\n            vec![ImportAttribute::new(type_sym, json)].into(),\n        ))],\n        interner,\n    );\n}\n\n/// Checks import declaration with multiple attributes.\n#[test]\nfn import_with_multiple_attributes() {\n    let interner = &mut Interner::default();\n    let json = interner.get_or_intern_static(\"json\", utf16!(\"json\"));\n    let foo_json = interner.get_or_intern_static(\"./foo.json\", utf16!(\"./foo.json\"));\n    let type_sym = interner.get_or_intern_static(\"type\", utf16!(\"type\"));\n    let integrity = interner.get_or_intern_static(\"integrity\", utf16!(\"integrity\"));\n    let hash = interner.get_or_intern_static(\"sha384-abc123\", utf16!(\"sha384-abc123\"));\n\n    check_module_parser(\n        r#\"import json from \"./foo.json\" with { type: \"json\", integrity: \"sha384-abc123\" };\"#,\n        vec![ModuleItem::ImportDeclaration(ImportDeclaration::new(\n            Some(Identifier::new(json, Span::new((1, 8), (1, 12)))),\n            ImportKind::DefaultOrUnnamed,\n            ModuleSpecifier::new(foo_json),\n            vec![\n                ImportAttribute::new(type_sym, json),\n                ImportAttribute::new(integrity, hash),\n            ]\n            .into(),\n        ))],\n        interner,\n    );\n}\n\n/// Checks import declaration with trailing comma in attributes.\n#[test]\nfn import_with_trailing_comma_attribute() {\n    let interner = &mut Interner::default();\n    let json = interner.get_or_intern_static(\"json\", utf16!(\"json\"));\n    let foo_json = interner.get_or_intern_static(\"./foo.json\", utf16!(\"./foo.json\"));\n    let type_sym = interner.get_or_intern_static(\"type\", utf16!(\"type\"));\n\n    check_module_parser(\n        r#\"import json from \"./foo.json\" with { type: \"json\", };\"#,\n        vec![ModuleItem::ImportDeclaration(ImportDeclaration::new(\n            Some(Identifier::new(json, Span::new((1, 8), (1, 12)))),\n            ImportKind::DefaultOrUnnamed,\n            ModuleSpecifier::new(foo_json),\n            vec![ImportAttribute::new(type_sym, json)].into(),\n        ))],\n        interner,\n    );\n}\n\n/// Checks re-export with attributes.\n#[test]\nfn reexport_with_attributes() {\n    let interner = &mut Interner::default();\n    let foo_js = interner.get_or_intern_static(\"./foo.js\", utf16!(\"./foo.js\"));\n    let type_sym = interner.get_or_intern_static(\"type\", utf16!(\"type\"));\n    let json = interner.get_or_intern_static(\"json\", utf16!(\"json\"));\n\n    check_module_parser(\n        r#\"export * from \"./foo.js\" with { type: \"json\" };\"#,\n        vec![ModuleItem::ExportDeclaration(Box::new(\n            ExportDeclaration::ReExport {\n                kind: ReExportKind::Namespaced { name: None },\n                specifier: ModuleSpecifier::new(foo_js),\n                attributes: vec![ImportAttribute::new(type_sym, json)].into(),\n            },\n        ))],\n        interner,\n    );\n}\n\n/// Checks import attributes with string literal key.\n#[test]\nfn import_with_string_literal_key() {\n    let interner = &mut Interner::default();\n    let json = interner.get_or_intern_static(\"json\", utf16!(\"json\"));\n    let foo_json = interner.get_or_intern_static(\"./foo.json\", utf16!(\"./foo.json\"));\n    let type_sym = interner.get_or_intern_static(\"type\", utf16!(\"type\"));\n\n    check_module_parser(\n        r#\"import json from \"./foo.json\" with { \"type\": \"json\" };\"#,\n        vec![ModuleItem::ImportDeclaration(ImportDeclaration::new(\n            Some(Identifier::new(json, Span::new((1, 8), (1, 12)))),\n            ImportKind::DefaultOrUnnamed,\n            ModuleSpecifier::new(foo_json),\n            vec![ImportAttribute::new(type_sym, json)].into(),\n        ))],\n        interner,\n    );\n}\n\n/// Checks that duplicate attribute keys are rejected.\n#[test]\nfn import_duplicate_attribute_key() {\n    assert!(\n        Parser::new(Source::from_bytes(\n            r#\"import json from \"./foo.json\" with { type: \"json\", type: \"css\" };\"#\n        ))\n        .parse_module(\n            &boa_ast::scope::Scope::new_global(),\n            &mut Interner::default()\n        )\n        .is_err()\n    );\n}\n\n/// Checks that non-string attribute values are rejected.\n#[test]\nfn import_non_string_attribute_value() {\n    let scope = boa_ast::scope::Scope::new_global();\n\n    assert!(\n        Parser::new(Source::from_bytes(\n            r#\"import json from \"./foo.json\" with { type: json };\"#\n        ))\n        .parse_module(&scope, &mut Interner::default())\n        .is_err()\n    );\n    assert!(\n        Parser::new(Source::from_bytes(\n            r#\"import json from \"./foo.json\" with { type: 123 };\"#\n        ))\n        .parse_module(&scope, &mut Interner::default())\n        .is_err()\n    );\n    assert!(\n        Parser::new(Source::from_bytes(\n            r#\"import json from \"./foo.json\" with { type: true };\"#\n        ))\n        .parse_module(&scope, &mut Interner::default())\n        .is_err()\n    );\n}\n\n/// Checks `using` declaration parsing.\n#[test]\nfn using_declaration() {\n    let interner = &mut Interner::default();\n    check_script_parser(\n        \"using x = resource;\",\n        vec![\n            Declaration::Lexical(LexicalDeclaration::Using(\n                vec![Variable::from_identifier(\n                    Identifier::new(\n                        interner.get_or_intern_static(\"x\", utf16!(\"x\")),\n                        Span::new((1, 7), (1, 8)),\n                    ),\n                    Some(\n                        Identifier::new(\n                            interner.get_or_intern_static(\"resource\", utf16!(\"resource\")),\n                            Span::new((1, 11), (1, 19)),\n                        )\n                        .into(),\n                    ),\n                )]\n                .try_into()\n                .unwrap(),\n            ))\n            .into(),\n        ],\n        interner,\n    );\n}\n\n/// Checks `using` declaration with multiple bindings.\n#[test]\nfn using_declaration_multiple() {\n    let interner = &mut Interner::default();\n    check_script_parser(\n        \"using a = res1, b = res2;\",\n        vec![\n            Declaration::Lexical(LexicalDeclaration::Using(\n                vec![\n                    Variable::from_identifier(\n                        Identifier::new(\n                            interner.get_or_intern_static(\"a\", utf16!(\"a\")),\n                            Span::new((1, 7), (1, 8)),\n                        ),\n                        Some(\n                            Identifier::new(\n                                interner.get_or_intern_static(\"res1\", utf16!(\"res1\")),\n                                Span::new((1, 11), (1, 15)),\n                            )\n                            .into(),\n                        ),\n                    ),\n                    Variable::from_identifier(\n                        Identifier::new(\n                            interner.get_or_intern_static(\"b\", utf16!(\"b\")),\n                            Span::new((1, 17), (1, 18)),\n                        ),\n                        Some(\n                            Identifier::new(\n                                interner.get_or_intern_static(\"res2\", utf16!(\"res2\")),\n                                Span::new((1, 21), (1, 25)),\n                            )\n                            .into(),\n                        ),\n                    ),\n                ]\n                .try_into()\n                .unwrap(),\n            ))\n            .into(),\n        ],\n        interner,\n    );\n}\n\n/// Checks that `using` declaration without initializer fails.\n#[test]\nfn using_declaration_no_init() {\n    check_invalid_script(\"using x;\");\n}\n\n/// Checks `await using` declaration parsing in async function.\n#[test]\nfn await_using_declaration() {\n    let interner = &mut Interner::default();\n    // await using is only valid in async contexts, so we test it inside an async function\n    // We just verify it parses without error\n    let source = Source::from_bytes(\"async function f() { await using x = resource; }\");\n    let mut parser = Parser::new(source);\n    let scope = boa_ast::scope::Scope::new_global();\n    let result = parser.parse_script(&scope, interner);\n    assert!(\n        result.is_ok(),\n        \"Failed to parse await using in async function: {:?}\",\n        result.err()\n    );\n}\n\n/// Checks that `await using` declaration without initializer fails.\n#[test]\nfn await_using_declaration_no_init() {\n    // Test in async function context\n    check_invalid_script(\"async function f() { await using x; }\");\n}\n\n/// Checks that `await using` is only valid in async contexts.\n#[test]\nfn await_using_requires_async_context() {\n    // Should fail in non-async context (top-level script)\n    check_invalid_script(\"await using x = resource;\");\n}\n\n/// Checks that line terminator between `await` and `using` is rejected.\n/// Per spec: <https://arai-a.github.io/ecma262-compare/snapshot.html?pr=3000#prod-AwaitUsingDeclarationHead>\n/// There must be [no `LineTerminator` here] between the keywords.\n#[test]\nfn await_using_no_line_terminator() {\n    // Line terminator between await and using should fail\n    check_invalid_script(\"async function f() { await\\nusing x = resource; }\");\n\n    // Without line terminator should succeed\n    let interner = &mut Interner::default();\n    let source = Source::from_bytes(\"async function f() { await using x = resource; }\");\n    let mut parser = Parser::new(source);\n    let scope = boa_ast::scope::Scope::new_global();\n    let result = parser.parse_script(&scope, interner);\n    assert!(\n        result.is_ok(),\n        \"Failed to parse await using without line terminator: {:?}\",\n        result.err()\n    );\n}\n\n/// Checks that destructuring patterns are rejected for `using` declarations.\n/// Per spec: <https://tc39.es/proposal-explicit-resource-management/>\n/// The grammar uses ~Pattern parameter which means destructuring is NOT allowed.\n#[test]\nfn using_no_destructuring_object() {\n    check_invalid_script(\"using {x, y} = resource;\");\n}\n\n/// Checks that destructuring patterns are rejected for `using` declarations.\n#[test]\nfn using_no_destructuring_array() {\n    check_invalid_script(\"using [a, b] = resource;\");\n}\n\n/// Checks that destructuring patterns are rejected for `await using` declarations.\n#[test]\nfn await_using_no_destructuring_object() {\n    check_invalid_script(\"async function f() { await using {x, y} = resource; }\");\n}\n\n/// Checks that destructuring patterns are rejected for `await using` declarations.\n#[test]\nfn await_using_no_destructuring_array() {\n    check_invalid_script(\"async function f() { await using [a, b] = resource; }\");\n}\n\n/// Checks that `using let` is rejected (let is not allowed as a bound name).\n#[test]\nfn using_let_rejected() {\n    check_invalid_script(\"using let = resource;\");\n}\n\n/// Checks that duplicate names in `using` declarations are rejected.\n#[test]\nfn using_duplicate_names() {\n    check_invalid_script(\"using x = r1, x = r2;\");\n}\n\n/// Checks that `await using` with duplicate names is rejected.\n#[test]\nfn await_using_duplicate_names() {\n    check_invalid_script(\"async function f() { await using x = r1, x = r2; }\");\n}\n\n/// Checks that `using` works with valid identifiers.\n#[test]\nfn using_valid_identifiers() {\n    let interner = &mut Interner::default();\n    check_script_parser(\n        \"using x = resource, y = resource2;\",\n        vec![\n            Declaration::Lexical(LexicalDeclaration::Using(\n                vec![\n                    Variable::from_identifier(\n                        Identifier::new(\n                            interner.get_or_intern_static(\"x\", utf16!(\"x\")),\n                            Span::new((1, 7), (1, 8)),\n                        ),\n                        Some(\n                            Identifier::new(\n                                interner.get_or_intern_static(\"resource\", utf16!(\"resource\")),\n                                Span::new((1, 11), (1, 19)),\n                            )\n                            .into(),\n                        ),\n                    ),\n                    Variable::from_identifier(\n                        Identifier::new(\n                            interner.get_or_intern_static(\"y\", utf16!(\"y\")),\n                            Span::new((1, 21), (1, 22)),\n                        ),\n                        Some(\n                            Identifier::new(\n                                interner.get_or_intern_static(\"resource2\", utf16!(\"resource2\")),\n                                Span::new((1, 25), (1, 34)),\n                            )\n                            .into(),\n                        ),\n                    ),\n                ]\n                .try_into()\n                .unwrap(),\n            ))\n            .into(),\n        ],\n        interner,\n    );\n}\n\n/// Checks that `await using` works with valid identifiers in async context.\n#[test]\nfn await_using_valid_identifiers() {\n    let interner = &mut Interner::default();\n    let source = Source::from_bytes(\"async function f() { await using x = r1, y = r2; }\");\n    let mut parser = Parser::new(source);\n    let scope = boa_ast::scope::Scope::new_global();\n    let result = parser.parse_script(&scope, interner);\n    assert!(\n        result.is_ok(),\n        \"Failed to parse await using with multiple bindings: {:?}\",\n        result.err()\n    );\n}\n"
  },
  {
    "path": "core/parser/src/parser/statement/expression/mod.rs",
    "content": "use crate::{\n    Error,\n    lexer::TokenKind,\n    parser::{\n        AllowAwait, AllowYield, Cursor, OrAbrupt, ParseResult, TokenParser, expression::Expression,\n    },\n    source::ReadChar,\n};\nuse boa_ast::{Keyword, Punctuator, Spanned, Statement};\nuse boa_interner::Interner;\n\n/// Expression statement parsing.\n///\n/// More information:\n///  - [ECMAScript specification][spec]\n///\n/// [spec]: https://tc39.es/ecma262/#prod-ExpressionStatement\n#[derive(Debug, Clone, Copy)]\npub(in crate::parser::statement) struct ExpressionStatement {\n    allow_yield: AllowYield,\n    allow_await: AllowAwait,\n}\n\nimpl ExpressionStatement {\n    /// Creates a new `ExpressionStatement` parser.\n    pub(in crate::parser::statement) fn new<Y, A>(allow_yield: Y, allow_await: A) -> Self\n    where\n        Y: Into<AllowYield>,\n        A: Into<AllowAwait>,\n    {\n        Self {\n            allow_yield: allow_yield.into(),\n            allow_await: allow_await.into(),\n        }\n    }\n}\n\nimpl<R> TokenParser<R> for ExpressionStatement\nwhere\n    R: ReadChar,\n{\n    type Output = Statement;\n\n    fn parse(self, cursor: &mut Cursor<R>, interner: &mut Interner) -> ParseResult<Self::Output> {\n        let next_token = cursor.peek(0, interner).or_abrupt()?;\n        match next_token.kind() {\n            TokenKind::Keyword((Keyword::Function | Keyword::Class, true)) => {\n                return Err(Error::general(\n                    \"Keyword must not contain escaped characters\",\n                    next_token.span().start(),\n                ));\n            }\n            TokenKind::Keyword((Keyword::Function | Keyword::Class, false)) => {\n                return Err(Error::general(\n                    \"expected statement\",\n                    next_token.span().start(),\n                ));\n            }\n            TokenKind::Keyword((Keyword::Async, false)) => {\n                let skip_n = if cursor.peek_is_line_terminator(0, interner).or_abrupt()? {\n                    2\n                } else {\n                    1\n                };\n                let is_line_terminator = cursor\n                    .peek_is_line_terminator(skip_n, interner)?\n                    .unwrap_or(true);\n\n                if !is_line_terminator {\n                    let next_token = cursor.peek(1, interner).or_abrupt()?;\n                    match next_token.kind() {\n                        TokenKind::Keyword((Keyword::Function, true)) => {\n                            return Err(Error::general(\n                                \"Keyword must not contain escaped characters\",\n                                next_token.span().start(),\n                            ));\n                        }\n                        TokenKind::Keyword((Keyword::Function, false)) => {\n                            return Err(Error::general(\n                                \"expected statement\",\n                                next_token.span().start(),\n                            ));\n                        }\n                        _ => {}\n                    }\n                }\n            }\n            TokenKind::Keyword((Keyword::Let, false)) => {\n                let next_token = cursor.peek(1, interner).or_abrupt()?;\n                if next_token.kind() == &TokenKind::Punctuator(Punctuator::OpenBracket) {\n                    return Err(Error::general(\n                        \"expected statement\",\n                        next_token.span().start(),\n                    ));\n                }\n            }\n            _ => {}\n        }\n\n        let expr =\n            Expression::new(true, self.allow_yield, self.allow_await).parse(cursor, interner)?;\n\n        cursor.expect_semicolon(\"expression statement\", interner)?;\n\n        Ok(expr.into())\n    }\n}\n"
  },
  {
    "path": "core/parser/src/parser/statement/if_stm/mod.rs",
    "content": "#[cfg(test)]\nmod tests;\n\nuse crate::{\n    Error,\n    lexer::TokenKind,\n    parser::{\n        AllowAwait, AllowReturn, AllowYield, Cursor, OrAbrupt, ParseResult, TokenParser,\n        expression::Expression,\n        statement::{Statement, declaration::FunctionDeclaration},\n    },\n    source::ReadChar,\n};\nuse boa_ast::{\n    Declaration, Keyword, Punctuator, Spanned, StatementListItem,\n    statement::{Block, If},\n};\nuse boa_interner::Interner;\n\n/// If statement parsing.\n///\n/// An `if` statement will have a condition, a block statement, and an optional `else` statement.\n///\n/// More information:\n///  - [MDN documentation][mdn]\n///  - [ECMAScript specification][spec]\n///\n/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/if...else\n/// [spec]: https://tc39.es/ecma262/#prod-IfStatement\n#[derive(Debug, Clone, Copy)]\npub(super) struct IfStatement {\n    allow_yield: AllowYield,\n    allow_await: AllowAwait,\n    allow_return: AllowReturn,\n}\n\nimpl IfStatement {\n    /// Creates a new `IfStatement` parser.\n    pub(super) fn new<Y, A, R>(allow_yield: Y, allow_await: A, allow_return: R) -> Self\n    where\n        Y: Into<AllowYield>,\n        A: Into<AllowAwait>,\n        R: Into<AllowReturn>,\n    {\n        Self {\n            allow_yield: allow_yield.into(),\n            allow_await: allow_await.into(),\n            allow_return: allow_return.into(),\n        }\n    }\n}\n\nimpl<R> TokenParser<R> for IfStatement\nwhere\n    R: ReadChar,\n{\n    type Output = If;\n\n    fn parse(self, cursor: &mut Cursor<R>, interner: &mut Interner) -> ParseResult<Self::Output> {\n        cursor.expect((Keyword::If, false), \"if statement\", interner)?;\n        cursor.expect(Punctuator::OpenParen, \"if statement\", interner)?;\n\n        let condition =\n            Expression::new(true, self.allow_yield, self.allow_await).parse(cursor, interner)?;\n\n        let position = cursor\n            .expect(Punctuator::CloseParen, \"if statement\", interner)?\n            .span()\n            .end();\n\n        let strict = cursor.strict();\n        let token = cursor.peek(0, interner).or_abrupt()?;\n        let then_node = match token.kind() {\n            TokenKind::Keyword((Keyword::Function, _)) => {\n                // FunctionDeclarations in IfStatement Statement Clauses\n                // https://tc39.es/ecma262/#sec-functiondeclarations-in-ifstatement-statement-clauses\n                if cfg!(not(feature = \"annex-b\")) || strict {\n                    return Err(Error::misplaced_function_declaration(position, strict));\n                }\n                // Source text matched by this production is processed as if each matching\n                // occurrence of FunctionDeclaration[?Yield, ?Await, ~Default] was the sole\n                // StatementListItem of a BlockStatement occupying that position in the source text.\n                Block::from((\n                    vec![StatementListItem::Declaration(\n                        Declaration::FunctionDeclaration(\n                            FunctionDeclaration::new(self.allow_yield, self.allow_await, false)\n                                .parse(cursor, interner)?,\n                        )\n                        .into(),\n                    )],\n                    cursor.linear_pos(),\n                ))\n                .into()\n            }\n            _ => Statement::new(self.allow_yield, self.allow_await, self.allow_return)\n                .parse(cursor, interner)?,\n        };\n\n        // Early Error: It is a Syntax Error if IsLabelledFunction(the first Statement) is true.\n        if then_node.is_labelled_function() {\n            return Err(Error::wrong_labelled_function_declaration(position));\n        }\n\n        let else_stmt = if let Some(token) = cursor.peek(0, interner)? {\n            match token.kind() {\n                TokenKind::Keyword((Keyword::Else, true)) => {\n                    return Err(Error::general(\n                        \"Keyword must not contain escaped characters\",\n                        token.span().start(),\n                    ));\n                }\n                TokenKind::Keyword((Keyword::Else, false)) => {\n                    cursor.advance(interner);\n\n                    let strict = cursor.strict();\n                    let token = cursor.peek(0, interner).or_abrupt()?;\n                    let position = token.span().start();\n                    let stmt = match token.kind() {\n                        TokenKind::Keyword((Keyword::Function, _)) => {\n                            // FunctionDeclarations in IfStatement Statement Clauses\n                            // https://tc39.es/ecma262/#sec-functiondeclarations-in-ifstatement-statement-clauses\n                            if cfg!(not(feature = \"annex-b\")) || strict {\n                                return Err(Error::misplaced_function_declaration(\n                                    position, strict,\n                                ));\n                            }\n\n                            // Source text matched by this production is processed as if each matching\n                            // occurrence of FunctionDeclaration[?Yield, ?Await, ~Default] was the sole\n                            // StatementListItem of a BlockStatement occupying that position in the source text.\n                            Block::from((\n                                vec![StatementListItem::Declaration(\n                                    Declaration::FunctionDeclaration(\n                                        FunctionDeclaration::new(\n                                            self.allow_yield,\n                                            self.allow_await,\n                                            false,\n                                        )\n                                        .parse(cursor, interner)?,\n                                    )\n                                    .into(),\n                                )],\n                                cursor.linear_pos(),\n                            ))\n                            .into()\n                        }\n                        _ => Statement::new(self.allow_yield, self.allow_await, self.allow_return)\n                            .parse(cursor, interner)?,\n                    };\n\n                    // Early Error: It is a Syntax Error if IsLabelledFunction(the second Statement) is true.\n                    if stmt.is_labelled_function() {\n                        return Err(Error::wrong_labelled_function_declaration(position));\n                    }\n\n                    Some(stmt)\n                }\n                _ => None,\n            }\n        } else {\n            None\n        };\n\n        Ok(If::new(condition, then_node, else_stmt))\n    }\n}\n"
  },
  {
    "path": "core/parser/src/parser/statement/if_stm/tests.rs",
    "content": "use crate::parser::tests::check_script_parser;\nuse boa_ast::{\n    Span, Statement,\n    expression::literal::Literal,\n    statement::{Block, If},\n};\nuse boa_interner::Interner;\n\nconst PSEUDO_LINEAR_POS: boa_ast::LinearPosition = boa_ast::LinearPosition::new(0);\n\n#[test]\nfn if_without_else_block() {\n    check_script_parser(\n        \"if (true) {}\",\n        vec![\n            Statement::If(If::new(\n                Literal::new(true, Span::new((1, 5), (1, 9))).into(),\n                Block::from((Vec::new(), PSEUDO_LINEAR_POS)).into(),\n                None,\n            ))\n            .into(),\n        ],\n        &mut Interner::default(),\n    );\n}\n\n#[test]\nfn if_without_else_block_with_trailing_newline() {\n    check_script_parser(\n        \"if (true) {}\\n\",\n        vec![\n            Statement::If(If::new(\n                Literal::new(true, Span::new((1, 5), (1, 9))).into(),\n                Block::from((Vec::new(), PSEUDO_LINEAR_POS)).into(),\n                None,\n            ))\n            .into(),\n        ],\n        &mut Interner::default(),\n    );\n}\n"
  },
  {
    "path": "core/parser/src/parser/statement/iteration/do_while_statement.rs",
    "content": "//! Do-while statement parsing.\n//!\n//! More information:\n//!  - [MDN documentation][mdn]\n//!  - [ECMAScript specification][spec]\n//!\n//! [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/do...while\n//! [spec]: https://tc39.es/ecma262/#sec-do-while-statement\n\nuse crate::{\n    Error,\n    lexer::{Token, TokenKind},\n    parser::{\n        AllowAwait, AllowReturn, AllowYield, Cursor, OrAbrupt, ParseResult, TokenParser,\n        expression::Expression, statement::Statement,\n    },\n    source::ReadChar,\n};\nuse boa_ast::{Keyword, Punctuator, Spanned, statement::DoWhileLoop};\nuse boa_interner::Interner;\n\n/// Do...while statement parsing\n///\n/// More information:\n///  - [MDN documentation][mdn]\n///  - [ECMAScript specification][spec]\n///\n/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/do...while\n/// [spec]: https://tc39.es/ecma262/#sec-do-while-statement\n#[derive(Debug, Clone, Copy)]\npub(in crate::parser::statement) struct DoWhileStatement {\n    allow_yield: AllowYield,\n    allow_await: AllowAwait,\n    allow_return: AllowReturn,\n}\n\nimpl DoWhileStatement {\n    /// Creates a new `DoWhileStatement` parser.\n    pub(in crate::parser::statement) fn new<Y, A, R>(\n        allow_yield: Y,\n        allow_await: A,\n        allow_return: R,\n    ) -> Self\n    where\n        Y: Into<AllowYield>,\n        A: Into<AllowAwait>,\n        R: Into<AllowReturn>,\n    {\n        Self {\n            allow_yield: allow_yield.into(),\n            allow_await: allow_await.into(),\n            allow_return: allow_return.into(),\n        }\n    }\n}\n\nimpl<R> TokenParser<R> for DoWhileStatement\nwhere\n    R: ReadChar,\n{\n    type Output = DoWhileLoop;\n\n    fn parse(self, cursor: &mut Cursor<R>, interner: &mut Interner) -> ParseResult<Self::Output> {\n        cursor.expect((Keyword::Do, false), \"do while statement\", interner)?;\n\n        let position = cursor.peek(0, interner).or_abrupt()?.span().start();\n\n        let body = Statement::new(self.allow_yield, self.allow_await, self.allow_return)\n            .parse(cursor, interner)?;\n\n        // Early Error: It is a Syntax Error if IsLabelledFunction(Statement) is true.\n        if body.is_labelled_function() {\n            return Err(Error::wrong_labelled_function_declaration(position));\n        }\n\n        let next_token = cursor.peek(0, interner).or_abrupt()?;\n        match next_token.kind() {\n            TokenKind::Keyword((Keyword::While, true)) => {\n                return Err(Error::general(\n                    \"Keyword must not contain escaped characters\",\n                    next_token.span().start(),\n                ));\n            }\n            TokenKind::Keyword((Keyword::While, false)) => {}\n            _ => {\n                return Err(Error::expected(\n                    [\"while\".to_owned()],\n                    next_token.to_string(interner),\n                    next_token.span(),\n                    \"do while statement\",\n                ));\n            }\n        }\n\n        cursor.expect((Keyword::While, false), \"do while statement\", interner)?;\n\n        cursor.expect(Punctuator::OpenParen, \"do while statement\", interner)?;\n\n        let cond =\n            Expression::new(true, self.allow_yield, self.allow_await).parse(cursor, interner)?;\n\n        cursor.expect(Punctuator::CloseParen, \"do while statement\", interner)?;\n\n        // Here, we only care to read the next token if it's a semicolon. If it's not, we\n        // automatically \"enter\" or assume a semicolon, since we have just read the `)` token:\n        // https://tc39.es/ecma262/#sec-automatic-semicolon-insertion\n        if cursor.peek(0, interner)?.map(Token::kind)\n            == Some(&TokenKind::Punctuator(Punctuator::Semicolon))\n        {\n            cursor.advance(interner);\n        }\n\n        Ok(DoWhileLoop::new(body, cond))\n    }\n}\n"
  },
  {
    "path": "core/parser/src/parser/statement/iteration/for_statement.rs",
    "content": "//! For statement parsing.\n//!\n//! More information:\n//!  - [MDN documentation][mdn]\n//!  - [ECMAScript specification][spec]\n//!\n//! [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/for\n//! [spec]: https://tc39.es/ecma262/#sec-for-statement\n\nuse crate::{\n    Error,\n    lexer::{Error as LexError, TokenKind},\n    parser::{\n        AllowAwait, AllowReturn, AllowYield, Cursor, OrAbrupt, ParseResult, TokenParser,\n        expression::{AssignmentExpression, Expression},\n        statement::{\n            Statement,\n            declaration::{LexicalDeclaration, allowed_token_after_let},\n            variable::VariableDeclarationList,\n        },\n    },\n    source::ReadChar,\n};\nuse ast::{\n    declaration::Binding,\n    operations::{bound_names, var_declared_names},\n};\nuse boa_ast::{\n    self as ast, Keyword, Position, Punctuator, Spanned,\n    statement::{\n        ForInLoop, ForLoop, ForOfLoop,\n        iteration::{ForLoopInitializer, IterableLoopInitializer},\n    },\n};\nuse boa_interner::{Interner, Sym};\nuse rustc_hash::FxHashSet;\n\n/// For statement parsing\n///\n/// More information:\n///  - [MDN documentation][mdn]\n///  - [ECMAScript specification][spec]\n///\n/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/for\n/// [spec]: https://tc39.es/ecma262/#sec-for-statement\n#[derive(Debug, Clone, Copy)]\npub(in crate::parser::statement) struct ForStatement {\n    allow_yield: AllowYield,\n    allow_await: AllowAwait,\n    allow_return: AllowReturn,\n}\n\nimpl ForStatement {\n    /// Creates a new `ForStatement` parser.\n    pub(in crate::parser::statement) fn new<Y, A, R>(\n        allow_yield: Y,\n        allow_await: A,\n        allow_return: R,\n    ) -> Self\n    where\n        Y: Into<AllowYield>,\n        A: Into<AllowAwait>,\n        R: Into<AllowReturn>,\n    {\n        Self {\n            allow_yield: allow_yield.into(),\n            allow_await: allow_await.into(),\n            allow_return: allow_return.into(),\n        }\n    }\n}\n\nimpl<R> TokenParser<R> for ForStatement\nwhere\n    R: ReadChar,\n{\n    type Output = ast::Statement;\n\n    fn parse(self, cursor: &mut Cursor<R>, interner: &mut Interner) -> ParseResult<Self::Output> {\n        cursor.expect((Keyword::For, false), \"for statement\", interner)?;\n\n        let mut r#await = false;\n\n        let next = cursor.next(interner).or_abrupt()?;\n        let init_position = match next.kind() {\n            TokenKind::Punctuator(Punctuator::OpenParen) => next.span().end(),\n            TokenKind::Keyword((Keyword::Await, _)) if !self.allow_await.0 => {\n                return Err(Error::unexpected(\n                    next.to_string(interner),\n                    next.span(),\n                    \"for await...of is only valid in async functions or async generators\",\n                ));\n            }\n            TokenKind::Keyword((Keyword::Await, _)) => {\n                r#await = true;\n                cursor\n                    .expect(Punctuator::OpenParen, \"for await...of\", interner)?\n                    .span()\n                    .end()\n            }\n            _ => {\n                return Err(Error::unexpected(\n                    next.to_string(interner),\n                    next.span(),\n                    \"for statement\",\n                ));\n            }\n        };\n\n        let mut init_is_async_of = false;\n        let init = match cursor.peek(0, interner).or_abrupt()?.kind().clone() {\n            TokenKind::Keyword((Keyword::Var, _)) => {\n                cursor.advance(interner);\n                Some(\n                    VariableDeclarationList::new(false, self.allow_yield, self.allow_await)\n                        .parse(cursor, interner)?\n                        .into(),\n                )\n            }\n            TokenKind::Keyword((Keyword::Let, false))\n                if allowed_token_after_let(cursor.peek(1, interner)?) =>\n            {\n                Some(\n                    LexicalDeclaration::new(false, self.allow_yield, self.allow_await, true)\n                        .parse(cursor, interner)?\n                        .into(),\n                )\n            }\n            TokenKind::Keyword((Keyword::Const, _)) => Some(\n                LexicalDeclaration::new(false, self.allow_yield, self.allow_await, true)\n                    .parse(cursor, interner)?\n                    .into(),\n            ),\n            TokenKind::Keyword((Keyword::Async, false)) if !r#await => {\n                if matches!(\n                    cursor.peek(1, interner).or_abrupt()?.kind(),\n                    TokenKind::Keyword((Keyword::Of, false))\n                ) {\n                    init_is_async_of = true;\n                }\n\n                Some(\n                    Expression::new(false, self.allow_yield, self.allow_await)\n                        .parse(cursor, interner)?\n                        .into(),\n                )\n            }\n            TokenKind::Punctuator(Punctuator::Semicolon) => None,\n            _ => Some(\n                Expression::new(false, self.allow_yield, self.allow_await)\n                    .parse(cursor, interner)?\n                    .into(),\n            ),\n        };\n\n        let token = cursor.peek(0, interner).or_abrupt()?;\n        let position = token.span().start();\n        let init = match (init, token.kind()) {\n            (Some(_), TokenKind::Keyword((Keyword::In | Keyword::Of, true))) => {\n                return Err(Error::general(\n                    \"Keyword must not contain escaped characters\",\n                    position,\n                ));\n            }\n            (Some(_), TokenKind::Keyword((Keyword::In, false))) if r#await => {\n                return Err(Error::general(\n                    \"`await` can only be used in a `for await .. of` loop\",\n                    position,\n                ));\n            }\n            (Some(init), TokenKind::Keyword((kw @ (Keyword::In | Keyword::Of), false))) => {\n                if kw == &Keyword::Of\n                    && let ForLoopInitializer::Expression(ast::Expression::Identifier(ident)) = init\n                    && ident.sym() == Sym::LET\n                {\n                    return Err(Error::general(\n                        \"'let' cannot be used as the iterable in a for-of loop\",\n                        position,\n                    ));\n                }\n\n                if init_is_async_of\n                    && let ForLoopInitializer::Expression(ast::Expression::Identifier(ident)) = init\n                    && ident.sym() == Sym::ASYNC\n                {\n                    return Err(Error::lex(LexError::Syntax(\n                        \"invalid left-hand side expression 'async' of a for-of loop\".into(),\n                        init_position,\n                    )));\n                }\n\n                let in_loop = kw == &Keyword::In;\n                let init = initializer_to_iterable_loop_initializer(\n                    init,\n                    position,\n                    cursor.strict(),\n                    in_loop,\n                )?;\n\n                cursor.advance(interner);\n                let expr = if in_loop {\n                    Expression::new(true, self.allow_yield, self.allow_await)\n                        .parse(cursor, interner)?\n                } else {\n                    AssignmentExpression::new(true, self.allow_yield, self.allow_await)\n                        .parse(cursor, interner)?\n                };\n\n                cursor.expect(Punctuator::CloseParen, \"for in/of statement\", interner)?;\n\n                let position = cursor.peek(0, interner).or_abrupt()?.span().start();\n\n                let body = Statement::new(self.allow_yield, self.allow_await, self.allow_return)\n                    .parse(cursor, interner)?;\n\n                // Early Error: It is a Syntax Error if IsLabelledFunction(Statement) is true.\n                if body.is_labelled_function() {\n                    return Err(Error::wrong_labelled_function_declaration(position));\n                }\n\n                // Checks are only applicable to lexical bindings.\n                if matches!(\n                    &init,\n                    IterableLoopInitializer::Const(_) | IterableLoopInitializer::Let(_)\n                ) {\n                    // It is a Syntax Error if the BoundNames of ForDeclaration contains \"let\".\n                    // It is a Syntax Error if any element of the BoundNames of ForDeclaration also occurs in the VarDeclaredNames of Statement.\n                    // It is a Syntax Error if the BoundNames of ForDeclaration contains any duplicate entries.\n                    let vars = var_declared_names(&body);\n                    let mut names = FxHashSet::default();\n                    for name in bound_names(&init) {\n                        if name == Sym::LET {\n                            return Err(Error::general(\n                                \"Cannot use 'let' as a lexically bound name\",\n                                position,\n                            ));\n                        }\n                        if vars.contains(&name) {\n                            return Err(Error::general(\n                                \"For loop initializer declared in loop body\",\n                                position,\n                            ));\n                        }\n                        if !names.insert(name) {\n                            return Err(Error::general(\n                                \"For loop initializer cannot contain duplicate identifiers\",\n                                position,\n                            ));\n                        }\n                    }\n                }\n                return Ok(if in_loop {\n                    ForInLoop::new(init, expr, body).into()\n                } else {\n                    ForOfLoop::new(init, expr, body, r#await).into()\n                });\n            }\n            (init, _) => init,\n        };\n\n        if let Some(ForLoopInitializer::Lexical(initializer)) = &init\n            && let ast::declaration::LexicalDeclaration::Const(list) = initializer.declaration()\n        {\n            for decl in list.as_ref() {\n                if decl.init().is_none() {\n                    return Err(Error::general(\n                        \"Expected initializer for const declaration\",\n                        position,\n                    ));\n                }\n            }\n        }\n\n        cursor.expect(Punctuator::Semicolon, \"for statement\", interner)?;\n\n        let cond = if cursor.next_if(Punctuator::Semicolon, interner)?.is_some() {\n            None\n        } else {\n            let step = Expression::new(true, self.allow_yield, self.allow_await)\n                .parse(cursor, interner)?;\n            cursor.expect(Punctuator::Semicolon, \"for statement\", interner)?;\n            Some(step)\n        };\n\n        let step = if cursor.next_if(Punctuator::CloseParen, interner)?.is_some() {\n            None\n        } else {\n            let step = Expression::new(true, self.allow_yield, self.allow_await)\n                .parse(cursor, interner)?;\n            cursor.expect(\n                TokenKind::Punctuator(Punctuator::CloseParen),\n                \"for statement\",\n                interner,\n            )?;\n            Some(step)\n        };\n\n        let position = cursor.peek(0, interner).or_abrupt()?.span().start();\n\n        let body = Statement::new(self.allow_yield, self.allow_await, self.allow_return)\n            .parse(cursor, interner)?;\n\n        // Early Error: It is a Syntax Error if IsLabelledFunction(Statement) is true.\n        if body.is_labelled_function() {\n            return Err(Error::wrong_labelled_function_declaration(position));\n        }\n\n        // Early Error: It is a Syntax Error if any element of the BoundNames of\n        // LexicalDeclaration also occurs in the VarDeclaredNames of Statement.\n        // Note: only applies to lexical bindings.\n        if let Some(ForLoopInitializer::Lexical(initializer)) = &init {\n            let vars = var_declared_names(&body);\n            for name in bound_names(initializer.declaration()) {\n                if vars.contains(&name) {\n                    return Err(Error::general(\n                        \"For loop initializer declared in loop body\",\n                        position,\n                    ));\n                }\n            }\n        }\n\n        Ok(ForLoop::new(init, cond, step, body).into())\n    }\n}\n\nfn initializer_to_iterable_loop_initializer(\n    initializer: ForLoopInitializer,\n    position: Position,\n    strict: bool,\n    in_loop: bool,\n) -> ParseResult<IterableLoopInitializer> {\n    let loop_type = if in_loop { \"for-in\" } else { \"for-of\" };\n    match initializer {\n        ForLoopInitializer::Expression(mut expr) => {\n            while let ast::Expression::Parenthesized(p) = expr {\n                expr = p.expression().clone();\n            }\n            match expr {\n                ast::Expression::Identifier(ident)\n                    if strict && [Sym::EVAL, Sym::ARGUMENTS].contains(&ident.sym()) =>\n                {\n                    Err(Error::lex(LexError::Syntax(\n                        \"cannot use `eval` or `arguments` as iterable loop variable in strict code\"\n                            .into(),\n                        position,\n                    )))\n                }\n                ast::Expression::Identifier(ident) => {\n                    Ok(IterableLoopInitializer::Identifier(ident))\n                }\n                ast::Expression::ArrayLiteral(array) => array\n                    .to_pattern(strict)\n                    .ok_or_else(|| {\n                        Error::general(\n                            \"invalid array destructuring pattern in iterable loop initializer\",\n                            position,\n                        )\n                    })\n                    .map(|arr| IterableLoopInitializer::Pattern(arr.into())),\n                ast::Expression::ObjectLiteral(object) => object\n                    .to_pattern(strict)\n                    .ok_or_else(|| {\n                        Error::general(\n                            \"invalid object destructuring pattern in iterable loop initializer\",\n                            position,\n                        )\n                    })\n                    .map(|obj| IterableLoopInitializer::Pattern(obj.into())),\n                ast::Expression::PropertyAccess(access) => {\n                    Ok(IterableLoopInitializer::Access(access))\n                }\n                _ => Err(Error::lex(LexError::Syntax(\n                    \"invalid variable for iterable loop\".into(),\n                    position,\n                ))),\n            }\n        }\n        ForLoopInitializer::Lexical(initializer) => {\n            match initializer.declaration().variable_list().as_ref() {\n                [decl] => {\n                    if decl.init().is_some() {\n                        return Err(Error::lex(LexError::Syntax(\n                        format!(\"a lexical declaration in the head of a {loop_type} loop can't have an initializer\")\n                            .into(),\n                        position,\n                    )));\n                    }\n                    Ok(match initializer.declaration() {\n                        ast::declaration::LexicalDeclaration::Const(_) => {\n                            IterableLoopInitializer::Const(decl.binding().clone())\n                        }\n                        ast::declaration::LexicalDeclaration::Let(_)\n                        | ast::declaration::LexicalDeclaration::Using(_)\n                        | ast::declaration::LexicalDeclaration::AwaitUsing(_) => {\n                            IterableLoopInitializer::Let(decl.binding().clone())\n                        }\n                    })\n                }\n                _ => Err(Error::lex(LexError::Syntax(\n                    format!(\"only one variable can be declared in the head of a {loop_type} loop\")\n                        .into(),\n                    position,\n                ))),\n            }\n        }\n        ForLoopInitializer::Var(decl) => match decl.0.as_ref() {\n            [declaration] => {\n                // https://tc39.es/ecma262/#sec-initializers-in-forin-statement-heads\n                let is_pattern = matches!(declaration.binding(), Binding::Pattern(_));\n                if declaration.init().is_some()\n                    && (cfg!(not(feature = \"annex-b\")) || strict || !in_loop || is_pattern)\n                {\n                    return Err(Error::lex(LexError::Syntax(\n                        format!(\n                            \"{}a {} declaration in the head of a {loop_type} loop \\\n                            cannot have an initializer\",\n                            if strict { \"in strict mode, \" } else { \"\" },\n                            if is_pattern {\n                                \"binding pattern\"\n                            } else {\n                                \"binding declaration\"\n                            }\n                        )\n                        .into(),\n                        position,\n                    )));\n                }\n                Ok(IterableLoopInitializer::Var(declaration.clone()))\n            }\n            _ => Err(Error::lex(LexError::Syntax(\n                format!(\"only one variable can be declared in the head of a {loop_type} loop\")\n                    .into(),\n                position,\n            ))),\n        },\n    }\n}\n"
  },
  {
    "path": "core/parser/src/parser/statement/iteration/mod.rs",
    "content": "mod do_while_statement;\nmod for_statement;\n#[cfg(test)]\nmod tests;\nmod while_statement;\n\npub(super) use self::{\n    do_while_statement::DoWhileStatement, for_statement::ForStatement,\n    while_statement::WhileStatement,\n};\n"
  },
  {
    "path": "core/parser/src/parser/statement/iteration/tests.rs",
    "content": "use crate::parser::tests::{check_invalid_script, check_script_parser};\nuse boa_ast::{\n    Expression, Span, Statement, StatementListItem,\n    declaration::{VarDeclaration, Variable},\n    expression::{\n        Call, Identifier,\n        access::SimplePropertyAccess,\n        literal::Literal,\n        operator::{\n            Assign, Binary, Update,\n            assign::AssignOp,\n            binary::RelationalOp,\n            update::{UpdateOp, UpdateTarget},\n        },\n    },\n    statement::{Block, Break, DoWhileLoop, WhileLoop},\n};\nuse boa_interner::Interner;\nuse boa_macros::utf16;\nuse indoc::indoc;\n\nconst PSEUDO_LINEAR_POS: boa_ast::LinearPosition = boa_ast::LinearPosition::new(0);\n\n/// Checks do-while statement parsing.\n#[test]\nfn check_do_while() {\n    let interner = &mut Interner::default();\n    check_script_parser(\n        indoc! {\"\n            do {\n                a += 1;\n            } while (true)\n        \"},\n        vec![\n            Statement::DoWhileLoop(DoWhileLoop::new(\n                Statement::Block(\n                    (\n                        vec![StatementListItem::Statement(\n                            Statement::Expression(Expression::from(Assign::new(\n                                AssignOp::Add,\n                                Identifier::new(\n                                    interner.get_or_intern_static(\"a\", utf16!(\"a\")),\n                                    Span::new((2, 5), (2, 6)),\n                                )\n                                .into(),\n                                Literal::new(1, Span::new((2, 10), (2, 11))).into(),\n                            )))\n                            .into(),\n                        )],\n                        PSEUDO_LINEAR_POS,\n                    )\n                        .into(),\n                ),\n                Literal::new(true, Span::new((3, 10), (3, 14))).into(),\n            ))\n            .into(),\n        ],\n        interner,\n    );\n}\n\n// Checks automatic semicolon insertion after do-while.\n#[test]\nfn check_do_while_semicolon_insertion() {\n    let interner = &mut Interner::default();\n    check_script_parser(\n        indoc! {r#\"\n            var i = 0;\n            do {console.log(\"hello\");} while(i++ < 10) console.log(\"end\");\n        \"#},\n        vec![\n            Statement::Var(VarDeclaration(\n                vec![Variable::from_identifier(\n                    Identifier::new(\n                        interner.get_or_intern_static(\"i\", utf16!(\"i\")),\n                        Span::new((1, 5), (1, 6)),\n                    ),\n                    Some(Literal::new(0, Span::new((1, 9), (1, 10))).into()),\n                )]\n                .try_into()\n                .unwrap(),\n            ))\n            .into(),\n            Statement::DoWhileLoop(DoWhileLoop::new(\n                Statement::Block(\n                    (\n                        vec![StatementListItem::Statement(\n                            Statement::Expression(\n                                Call::new(\n                                    Expression::PropertyAccess(\n                                        SimplePropertyAccess::new(\n                                            Identifier::new(\n                                                interner.get_or_intern_static(\n                                                    \"console\",\n                                                    utf16!(\"console\"),\n                                                ),\n                                                Span::new((2, 5), (2, 12)),\n                                            )\n                                            .into(),\n                                            Identifier::new(\n                                                interner.get_or_intern_static(\"log\", utf16!(\"log\")),\n                                                Span::new((2, 13), (2, 16)),\n                                            ),\n                                        )\n                                        .into(),\n                                    ),\n                                    vec![\n                                        Literal::new(\n                                            interner.get_or_intern_static(\"hello\", utf16!(\"hello\")),\n                                            Span::new((2, 17), (2, 24)),\n                                        )\n                                        .into(),\n                                    ]\n                                    .into(),\n                                    Span::new((2, 16), (2, 25)),\n                                )\n                                .into(),\n                            )\n                            .into(),\n                        )],\n                        PSEUDO_LINEAR_POS,\n                    )\n                        .into(),\n                ),\n                Binary::new(\n                    RelationalOp::LessThan.into(),\n                    Update::new(\n                        UpdateOp::IncrementPost,\n                        UpdateTarget::Identifier(Identifier::new(\n                            interner.get_or_intern_static(\"i\", utf16!(\"i\")),\n                            Span::new((2, 34), (2, 35)),\n                        )),\n                        Span::new((2, 34), (2, 37)),\n                    )\n                    .into(),\n                    Literal::new(10, Span::new((2, 40), (2, 42))).into(),\n                )\n                .into(),\n            ))\n            .into(),\n            Statement::Expression(\n                Call::new(\n                    Expression::PropertyAccess(\n                        SimplePropertyAccess::new(\n                            Identifier::new(\n                                interner.get_or_intern_static(\"console\", utf16!(\"console\")),\n                                Span::new((2, 44), (2, 51)),\n                            )\n                            .into(),\n                            Identifier::new(\n                                interner.get_or_intern_static(\"log\", utf16!(\"log\")),\n                                Span::new((2, 52), (2, 55)),\n                            ),\n                        )\n                        .into(),\n                    ),\n                    vec![\n                        Literal::new(\n                            interner.get_or_intern_static(\"end\", utf16!(\"end\")),\n                            Span::new((2, 56), (2, 61)),\n                        )\n                        .into(),\n                    ]\n                    .into(),\n                    Span::new((2, 55), (2, 62)),\n                )\n                .into(),\n            )\n            .into(),\n        ],\n        interner,\n    );\n}\n\n// Checks automatic semicolon insertion after do-while with no space between closing paren\n// and next statement.\n#[test]\nfn check_do_while_semicolon_insertion_no_space() {\n    let interner = &mut Interner::default();\n    check_script_parser(\n        indoc! {r#\"\n            var i = 0;\n            do {console.log(\"hello\");} while(i++ < 10)console.log(\"end\");\n        \"#},\n        vec![\n            Statement::Var(VarDeclaration(\n                vec![Variable::from_identifier(\n                    Identifier::new(\n                        interner.get_or_intern_static(\"i\", utf16!(\"i\")),\n                        Span::new((1, 5), (1, 6)),\n                    ),\n                    Some(Literal::new(0, Span::new((1, 9), (1, 10))).into()),\n                )]\n                .try_into()\n                .unwrap(),\n            ))\n            .into(),\n            Statement::DoWhileLoop(DoWhileLoop::new(\n                Statement::Block(\n                    (\n                        vec![StatementListItem::Statement(\n                            Statement::Expression(\n                                Call::new(\n                                    Expression::PropertyAccess(\n                                        SimplePropertyAccess::new(\n                                            Identifier::new(\n                                                interner.get_or_intern_static(\n                                                    \"console\",\n                                                    utf16!(\"console\"),\n                                                ),\n                                                Span::new((2, 5), (2, 12)),\n                                            )\n                                            .into(),\n                                            Identifier::new(\n                                                interner.get_or_intern_static(\"log\", utf16!(\"log\")),\n                                                Span::new((2, 13), (2, 16)),\n                                            ),\n                                        )\n                                        .into(),\n                                    ),\n                                    vec![\n                                        Literal::new(\n                                            interner.get_or_intern_static(\"hello\", utf16!(\"hello\")),\n                                            Span::new((2, 17), (2, 24)),\n                                        )\n                                        .into(),\n                                    ]\n                                    .into(),\n                                    Span::new((2, 16), (2, 25)),\n                                )\n                                .into(),\n                            )\n                            .into(),\n                        )],\n                        PSEUDO_LINEAR_POS,\n                    )\n                        .into(),\n                ),\n                Binary::new(\n                    RelationalOp::LessThan.into(),\n                    Update::new(\n                        UpdateOp::IncrementPost,\n                        UpdateTarget::Identifier(Identifier::new(\n                            interner.get_or_intern_static(\"i\", utf16!(\"i\")),\n                            Span::new((2, 34), (2, 35)),\n                        )),\n                        Span::new((2, 34), (2, 37)),\n                    )\n                    .into(),\n                    Literal::new(10, Span::new((2, 40), (2, 42))).into(),\n                )\n                .into(),\n            ))\n            .into(),\n            Statement::Expression(\n                Call::new(\n                    Expression::PropertyAccess(\n                        SimplePropertyAccess::new(\n                            Identifier::new(\n                                interner.get_or_intern_static(\"console\", utf16!(\"console\")),\n                                Span::new((2, 43), (2, 50)),\n                            )\n                            .into(),\n                            Identifier::new(\n                                interner.get_or_intern_static(\"log\", utf16!(\"log\")),\n                                Span::new((2, 51), (2, 54)),\n                            ),\n                        )\n                        .into(),\n                    ),\n                    vec![\n                        Literal::new(\n                            interner.get_or_intern_static(\"end\", utf16!(\"end\")),\n                            Span::new((2, 55), (2, 60)),\n                        )\n                        .into(),\n                    ]\n                    .into(),\n                    Span::new((2, 54), (2, 61)),\n                )\n                .into(),\n            )\n            .into(),\n        ],\n        interner,\n    );\n}\n\n/// Checks parsing of a while statement which is separated out with line terminators.\n#[test]\nfn while_spaces() {\n    check_script_parser(\n        indoc! {\"\n\n            while\n\n            (\n\n            true\n\n            )\n\n            break;\n\n        \"},\n        vec![\n            Statement::WhileLoop(WhileLoop::new(\n                Literal::new(true, Span::new((6, 1), (6, 5))).into(),\n                Break::new(None).into(),\n            ))\n            .into(),\n        ],\n        &mut Interner::default(),\n    );\n}\n\n/// Checks parsing of a while statement which is separated out with line terminators.\n#[test]\nfn do_while_spaces() {\n    check_script_parser(\n        indoc! {\"\n\n        do\n\n        {\n\n            break;\n\n        }\n\n        while (true)\n\n        \"},\n        vec![\n            Statement::DoWhileLoop(DoWhileLoop::new(\n                Block::from((\n                    vec![StatementListItem::Statement(\n                        Statement::Break(Break::new(None)).into(),\n                    )],\n                    PSEUDO_LINEAR_POS,\n                ))\n                .into(),\n                Literal::new(true, Span::new((10, 8), (10, 12))).into(),\n            ))\n            .into(),\n        ],\n        &mut Interner::default(),\n    );\n}\n\n/// Checks rejection of const bindings without init in for loops\n#[test]\nfn reject_const_no_init_for_loop() {\n    check_invalid_script(\"for (const h;;);\");\n}\n\n/// Checks rejection of for await .. in loops\n#[test]\nfn reject_for_await_in_loop() {\n    check_invalid_script(\"for await (x in [1,2,3]);\");\n}\n"
  },
  {
    "path": "core/parser/src/parser/statement/iteration/while_statement.rs",
    "content": "use crate::{\n    Error,\n    parser::{\n        AllowAwait, AllowReturn, AllowYield, Cursor, OrAbrupt, ParseResult, TokenParser,\n        expression::Expression, statement::Statement,\n    },\n    source::ReadChar,\n};\nuse boa_ast::{Keyword, Punctuator, Spanned, statement::WhileLoop};\nuse boa_interner::Interner;\n\n/// While statement parsing\n///\n/// More information:\n///  - [MDN documentation][mdn]\n///  - [ECMAScript specification][spec]\n///\n/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/while\n/// [spec]: https://tc39.es/ecma262/#sec-while-statement\n#[derive(Debug, Clone, Copy)]\npub(in crate::parser::statement) struct WhileStatement {\n    allow_yield: AllowYield,\n    allow_await: AllowAwait,\n    allow_return: AllowReturn,\n}\n\nimpl WhileStatement {\n    /// Creates a new `WhileStatement` parser.\n    pub(in crate::parser::statement) fn new<Y, A, R>(\n        allow_yield: Y,\n        allow_await: A,\n        allow_return: R,\n    ) -> Self\n    where\n        Y: Into<AllowYield>,\n        A: Into<AllowAwait>,\n        R: Into<AllowReturn>,\n    {\n        Self {\n            allow_yield: allow_yield.into(),\n            allow_await: allow_await.into(),\n            allow_return: allow_return.into(),\n        }\n    }\n}\n\nimpl<R> TokenParser<R> for WhileStatement\nwhere\n    R: ReadChar,\n{\n    type Output = WhileLoop;\n\n    fn parse(self, cursor: &mut Cursor<R>, interner: &mut Interner) -> ParseResult<Self::Output> {\n        cursor.expect((Keyword::While, false), \"while statement\", interner)?;\n\n        cursor.expect(Punctuator::OpenParen, \"while statement\", interner)?;\n\n        let cond =\n            Expression::new(true, self.allow_yield, self.allow_await).parse(cursor, interner)?;\n\n        cursor.expect(Punctuator::CloseParen, \"while statement\", interner)?;\n\n        let position = cursor.peek(0, interner).or_abrupt()?.span().start();\n\n        let body = Statement::new(self.allow_yield, self.allow_await, self.allow_return)\n            .parse(cursor, interner)?;\n\n        // Early Error: It is a Syntax Error if IsLabelledFunction(the second Statement) is true.\n        if body.is_labelled_function() {\n            return Err(Error::wrong_labelled_function_declaration(position));\n        }\n\n        Ok(WhileLoop::new(cond, body))\n    }\n}\n"
  },
  {
    "path": "core/parser/src/parser/statement/labelled_stm/mod.rs",
    "content": "use crate::{\n    Error,\n    lexer::TokenKind,\n    parser::{\n        AllowYield, OrAbrupt, ParseResult, TokenParser,\n        cursor::Cursor,\n        expression::LabelIdentifier,\n        statement::{AllowAwait, AllowReturn, Statement, declaration::FunctionDeclaration},\n    },\n    source::ReadChar,\n};\nuse boa_ast::{self as ast, Keyword, Punctuator, Spanned};\nuse boa_interner::Interner;\n\n/// Labelled Statement Parsing\n///\n/// More information\n/// - [MDN documentation][mdn]\n/// - [ECMAScript specification][spec]\n///\n/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/label\n/// [spec]: https://tc39.es/ecma262/#sec-labelled-statements\n#[derive(Debug, Clone, Copy)]\npub(super) struct LabelledStatement {\n    allow_yield: AllowYield,\n    allow_await: AllowAwait,\n    allow_return: AllowReturn,\n}\n\nimpl LabelledStatement {\n    pub(super) fn new<Y, A, R>(allow_yield: Y, allow_await: A, allow_return: R) -> Self\n    where\n        Y: Into<AllowYield>,\n        A: Into<AllowAwait>,\n        R: Into<AllowReturn>,\n    {\n        Self {\n            allow_yield: allow_yield.into(),\n            allow_await: allow_await.into(),\n            allow_return: allow_return.into(),\n        }\n    }\n}\n\nimpl<R> TokenParser<R> for LabelledStatement\nwhere\n    R: ReadChar,\n{\n    type Output = ast::statement::Labelled;\n\n    fn parse(self, cursor: &mut Cursor<R>, interner: &mut Interner) -> ParseResult<Self::Output> {\n        let label = LabelIdentifier::new(self.allow_yield, self.allow_await)\n            .parse(cursor, interner)?\n            .sym();\n\n        cursor.expect(Punctuator::Colon, \"Labelled Statement\", interner)?;\n\n        let strict = cursor.strict();\n        let next_token = cursor.peek(0, interner).or_abrupt()?;\n\n        let labelled_item = match next_token.kind() {\n            // Early Error: It is a Syntax Error if any strict mode source code matches this rule.\n            // https://tc39.es/ecma262/#sec-labelled-statements-static-semantics-early-errors\n            // https://tc39.es/ecma262/#sec-labelled-function-declarations\n            TokenKind::Keyword((Keyword::Function, _))\n                if cfg!(not(feature = \"annex-b\")) || strict =>\n            {\n                return Err(Error::misplaced_function_declaration(\n                    next_token.span().start(),\n                    strict,\n                ));\n            }\n            TokenKind::Keyword((Keyword::Function, _)) => {\n                FunctionDeclaration::new(self.allow_yield, self.allow_await, false)\n                    .parse(cursor, interner)?\n                    .into()\n            }\n            _ => Statement::new(self.allow_yield, self.allow_await, self.allow_return)\n                .parse(cursor, interner)?\n                .into(),\n        };\n\n        Ok(ast::statement::Labelled::new(labelled_item, label))\n    }\n}\n"
  },
  {
    "path": "core/parser/src/parser/statement/mod.rs",
    "content": "//! Statement and declaration parsing.\n//!\n//! More information:\n//!  - [MDN documentation][mdn]\n//!  - [ECMAScript specification][spec]\n//!\n//! [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements\n//! [spec]: https://tc39.es/ecma262/#sec-ecmascript-language-statements-and-declarations\n\nmod block;\nmod break_stm;\nmod continue_stm;\nmod declaration;\nmod expression;\nmod if_stm;\nmod iteration;\nmod labelled_stm;\nmod return_stm;\nmod switch;\nmod throw;\nmod try_stm;\nmod variable;\nmod with;\n\nuse self::{\n    block::BlockStatement,\n    break_stm::BreakStatement,\n    continue_stm::ContinueStatement,\n    declaration::{Declaration, ExportDeclaration, ImportDeclaration, allowed_token_after_let},\n    expression::ExpressionStatement,\n    if_stm::IfStatement,\n    iteration::{DoWhileStatement, ForStatement, WhileStatement},\n    labelled_stm::LabelledStatement,\n    return_stm::ReturnStatement,\n    switch::SwitchStatement,\n    throw::ThrowStatement,\n    try_stm::TryStatement,\n    variable::VariableStatement,\n    with::WithStatement,\n};\nuse crate::{\n    Error,\n    lexer::{Error as LexError, InputElement, Token, TokenKind, token::EscapeSequence},\n    parser::{\n        AllowAwait, AllowReturn, AllowYield, Cursor, OrAbrupt, ParseResult, TokenParser,\n        expression::{BindingIdentifier, Initializer, PropertyName},\n    },\n    source::ReadChar,\n};\nuse ast::{\n    Position,\n    operations::{all_private_identifiers_valid, check_labels, contains_invalid_object_literal},\n};\nuse boa_ast::{\n    self as ast, Keyword, Punctuator, Span, Spanned,\n    pattern::{ArrayPattern, ArrayPatternElement, ObjectPattern, ObjectPatternElement},\n};\nuse boa_interner::Interner;\nuse boa_macros::utf16;\n\npub(in crate::parser) use declaration::ClassTail;\n\n/// Statement parsing.\n///\n/// This can be one of the following:\n///\n///  - `BlockStatement`\n///  - `VariableStatement`\n///  - `EmptyStatement`\n///  - `ExpressionStatement`\n///  - `IfStatement`\n///  - `BreakableStatement`\n///  - `ContinueStatement`\n///  - `BreakStatement`\n///  - `ReturnStatement`\n///  - `WithStatement`\n///  - `LabelledStatement`\n///  - `ThrowStatement`\n///  - `SwitchStatement`\n///  - `TryStatement`\n///  - `DebuggerStatement`\n///\n/// More information:\n///  - [MDN documentation][mdn]\n///  - [ECMAScript specification][spec]\n///\n/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements\n/// [spec]: https://tc39.es/ecma262/#prod-Statement\n#[derive(Debug, Clone, Copy)]\npub(super) struct Statement {\n    allow_yield: AllowYield,\n    allow_await: AllowAwait,\n    allow_return: AllowReturn,\n}\n\nimpl Statement {\n    /// Creates a new `Statement` parser.\n    pub(super) fn new<Y, A, R>(allow_yield: Y, allow_await: A, allow_return: R) -> Self\n    where\n        Y: Into<AllowYield>,\n        A: Into<AllowAwait>,\n        R: Into<AllowReturn>,\n    {\n        Self {\n            allow_yield: allow_yield.into(),\n            allow_await: allow_await.into(),\n            allow_return: allow_return.into(),\n        }\n    }\n}\n\nimpl<R> TokenParser<R> for Statement\nwhere\n    R: ReadChar,\n{\n    type Output = ast::Statement;\n\n    fn parse(self, cursor: &mut Cursor<R>, interner: &mut Interner) -> ParseResult<Self::Output> {\n        // TODO: add BreakableStatement and divide Whiles, fors and so on to another place.\n        let tok = cursor.peek(0, interner).or_abrupt()?;\n\n        match tok.kind() {\n            TokenKind::Keyword((Keyword::With, _)) => {\n                WithStatement::new(self.allow_yield, self.allow_await, self.allow_return)\n                    .parse(cursor, interner)\n                    .map(ast::Statement::from)\n            }\n            TokenKind::Keyword((Keyword::If, _)) => {\n                IfStatement::new(self.allow_yield, self.allow_await, self.allow_return)\n                    .parse(cursor, interner)\n                    .map(ast::Statement::from)\n            }\n            TokenKind::Keyword((Keyword::Var, _)) => {\n                VariableStatement::new(self.allow_yield, self.allow_await)\n                    .parse(cursor, interner)\n                    .map(ast::Statement::from)\n            }\n            TokenKind::Keyword((Keyword::While, _)) => {\n                WhileStatement::new(self.allow_yield, self.allow_await, self.allow_return)\n                    .parse(cursor, interner)\n                    .map(ast::Statement::from)\n            }\n            TokenKind::Keyword((Keyword::Do, _)) => {\n                DoWhileStatement::new(self.allow_yield, self.allow_await, self.allow_return)\n                    .parse(cursor, interner)\n                    .map(ast::Statement::from)\n            }\n            TokenKind::Keyword((Keyword::For, _)) => {\n                ForStatement::new(self.allow_yield, self.allow_await, self.allow_return)\n                    .parse(cursor, interner)\n            }\n            TokenKind::Keyword((Keyword::Return, _)) => {\n                if self.allow_return.0 {\n                    ReturnStatement::new(self.allow_yield, self.allow_await)\n                        .parse(cursor, interner)\n                        .map(ast::Statement::from)\n                } else {\n                    Err(Error::unexpected(\n                        tok.to_string(interner),\n                        tok.span(),\n                        \"statement\",\n                    ))\n                }\n            }\n            TokenKind::Keyword((Keyword::Break, _)) => {\n                BreakStatement::new(self.allow_yield, self.allow_await)\n                    .parse(cursor, interner)\n                    .map(ast::Statement::from)\n            }\n            TokenKind::Keyword((Keyword::Continue, _)) => {\n                ContinueStatement::new(self.allow_yield, self.allow_await)\n                    .parse(cursor, interner)\n                    .map(ast::Statement::from)\n            }\n            TokenKind::Keyword((Keyword::Try, _)) => {\n                TryStatement::new(self.allow_yield, self.allow_await, self.allow_return)\n                    .parse(cursor, interner)\n                    .map(ast::Statement::from)\n            }\n            TokenKind::Keyword((Keyword::Throw, _)) => {\n                ThrowStatement::new(self.allow_yield, self.allow_await)\n                    .parse(cursor, interner)\n                    .map(ast::Statement::from)\n            }\n            TokenKind::Keyword((Keyword::Switch, _)) => {\n                SwitchStatement::new(self.allow_yield, self.allow_await, self.allow_return)\n                    .parse(cursor, interner)\n                    .map(ast::Statement::from)\n            }\n            TokenKind::Punctuator(Punctuator::OpenBlock) => {\n                BlockStatement::new(self.allow_yield, self.allow_await, self.allow_return)\n                    .parse(cursor, interner)\n                    .map(ast::Statement::from)\n            }\n            TokenKind::Punctuator(Punctuator::Semicolon) => {\n                // parse the EmptyStatement\n                cursor.advance(interner);\n                Ok(ast::Statement::Empty)\n            }\n            TokenKind::IdentifierName(_)\n            | TokenKind::Keyword((Keyword::Await | Keyword::Yield, _)) => {\n                // Labelled Statement check\n                cursor.set_goal(InputElement::Div);\n                let tok = cursor.peek(1, interner)?;\n\n                if let Some(tok) = tok\n                    && matches!(tok.kind(), TokenKind::Punctuator(Punctuator::Colon))\n                {\n                    return LabelledStatement::new(\n                        self.allow_yield,\n                        self.allow_await,\n                        self.allow_return,\n                    )\n                    .parse(cursor, interner)\n                    .map(ast::Statement::from);\n                }\n\n                ExpressionStatement::new(self.allow_yield, self.allow_await).parse(cursor, interner)\n            }\n            TokenKind::Keyword((Keyword::Debugger, _)) => {\n                cursor.advance(interner);\n                cursor.expect_semicolon(\"debugger statement\", interner)?;\n                Ok(ast::Statement::Debugger)\n            }\n            _ => {\n                ExpressionStatement::new(self.allow_yield, self.allow_await).parse(cursor, interner)\n            }\n        }\n    }\n}\n\n/// Reads a list of statements.\n///\n/// More information:\n///  - [ECMAScript specification][spec]\n///\n/// [spec]: https://tc39.es/ecma262/#prod-StatementList\n#[derive(Debug, Clone, Copy)]\npub(super) struct StatementList {\n    allow_yield: AllowYield,\n    allow_await: AllowAwait,\n    allow_return: AllowReturn,\n    break_nodes: &'static [TokenKind],\n    directive_prologues: bool,\n    strict: bool,\n}\n\nimpl StatementList {\n    /// Creates a new `StatementList` parser.\n    pub(super) fn new<Y, A, R>(\n        allow_yield: Y,\n        allow_await: A,\n        allow_return: R,\n        break_nodes: &'static [TokenKind],\n        directive_prologues: bool,\n        strict: bool,\n    ) -> Self\n    where\n        Y: Into<AllowYield>,\n        A: Into<AllowAwait>,\n        R: Into<AllowReturn>,\n    {\n        Self {\n            allow_yield: allow_yield.into(),\n            allow_await: allow_await.into(),\n            allow_return: allow_return.into(),\n            break_nodes,\n            directive_prologues,\n            strict,\n        }\n    }\n}\n\nimpl<R> TokenParser<R> for StatementList\nwhere\n    R: ReadChar,\n{\n    type Output = (ast::StatementList, Option<Position>);\n\n    /// The function parses a `node::StatementList` using the `StatementList`'s\n    /// `break_nodes` to know when to terminate.\n    ///\n    /// Returns a `ParseError::AbruptEnd` if end of stream is reached before a\n    /// break token.\n    ///\n    /// Returns a `ParseError::unexpected` if an unexpected token is found.\n    ///\n    /// Note that the last token which causes the parse to finish is not\n    /// consumed.\n    fn parse(self, cursor: &mut Cursor<R>, interner: &mut Interner) -> ParseResult<Self::Output> {\n        let mut items = Vec::new();\n\n        let global_strict = cursor.strict();\n        let mut directive_prologues = self.directive_prologues;\n        let mut strict = self.strict;\n        let mut directives_stack = Vec::new();\n        let mut linear_pos_end = cursor.linear_pos();\n        let mut end_position = None;\n\n        loop {\n            let peek_token = cursor.peek(0, interner)?;\n            if let Some(peek_token) = peek_token {\n                linear_pos_end = peek_token.linear_span().end();\n                end_position = Some(peek_token.span().end());\n            }\n\n            match peek_token {\n                Some(token) if self.break_nodes.contains(token.kind()) => break,\n                Some(token) if directive_prologues => {\n                    if let TokenKind::StringLiteral((_, escape)) = token.kind() {\n                        directives_stack.push((token.span().start(), *escape));\n                    }\n                }\n                None => break,\n                _ => {}\n            }\n\n            let item =\n                StatementListItem::new(self.allow_yield, self.allow_await, self.allow_return)\n                    .parse(cursor, interner)?;\n\n            if directive_prologues {\n                if let ast::StatementListItem::Statement(statement) = &item {\n                    if let ast::Statement::Expression(ast::Expression::Literal(lit)) =\n                        statement.as_ref()\n                    {\n                        if let Some(string) = lit.as_string() {\n                            if strict {\n                                // TODO: should store directives in some place\n                            } else if interner.resolve_expect(string).join(\n                                |s| s == \"use strict\",\n                                |g| g == utf16!(\"use strict\"),\n                                true,\n                            ) && directives_stack.last().expect(\"token should exist\").1\n                                == EscapeSequence::empty()\n                            {\n                                cursor.set_strict(true);\n                                strict = true;\n\n                                directives_stack.pop();\n\n                                for (position, escape) in std::mem::take(&mut directives_stack) {\n                                    if escape.contains(EscapeSequence::LEGACY_OCTAL) {\n                                        return Err(Error::general(\n                                            \"legacy octal escape sequences are not allowed in strict mode\",\n                                            position,\n                                        ));\n                                    }\n\n                                    if escape.contains(EscapeSequence::NON_OCTAL_DECIMAL) {\n                                        return Err(Error::general(\n                                            \"decimal escape sequences are not allowed in strict mode\",\n                                            position,\n                                        ));\n                                    }\n                                }\n                            }\n                        } else {\n                            directive_prologues = false;\n                            directives_stack.clear();\n                        }\n                    } else {\n                        directive_prologues = false;\n                        directives_stack.clear();\n                    }\n                } else {\n                    directive_prologues = false;\n                    directives_stack.clear();\n                }\n            }\n\n            items.push(item);\n        }\n\n        cursor.set_strict(global_strict);\n\n        Ok((\n            ast::StatementList::new(items, linear_pos_end, strict),\n            end_position,\n        ))\n    }\n}\n\n/// Statement list item parsing\n///\n/// A statement list item can either be an statement or a declaration.\n///\n/// More information:\n///  - [MDN documentation][mdn]\n///  - [ECMAScript specification][spec]\n///\n/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements\n/// [spec]: https://tc39.es/ecma262/#prod-StatementListItem\n#[derive(Debug, Clone, Copy)]\nstruct StatementListItem {\n    allow_yield: AllowYield,\n    allow_await: AllowAwait,\n    allow_return: AllowReturn,\n}\n\nimpl StatementListItem {\n    /// Creates a new `StatementListItem` parser.\n    fn new<Y, A, R>(allow_yield: Y, allow_await: A, allow_return: R) -> Self\n    where\n        Y: Into<AllowYield>,\n        A: Into<AllowAwait>,\n        R: Into<AllowReturn>,\n    {\n        Self {\n            allow_yield: allow_yield.into(),\n            allow_await: allow_await.into(),\n            allow_return: allow_return.into(),\n        }\n    }\n}\n\nimpl<R> TokenParser<R> for StatementListItem\nwhere\n    R: ReadChar,\n{\n    type Output = ast::StatementListItem;\n\n    fn parse(self, cursor: &mut Cursor<R>, interner: &mut Interner) -> ParseResult<Self::Output> {\n        let tok = cursor.peek(0, interner).or_abrupt()?;\n\n        match tok.kind().clone() {\n            TokenKind::Keyword((\n                Keyword::Function | Keyword::Class | Keyword::Const | Keyword::Using,\n                _,\n            )) => Declaration::new(self.allow_yield, self.allow_await)\n                .parse(cursor, interner)\n                .map(ast::StatementListItem::from),\n            TokenKind::Keyword((Keyword::Let, false))\n                if allowed_token_after_let(cursor.peek(1, interner)?) =>\n            {\n                Declaration::new(self.allow_yield, self.allow_await)\n                    .parse(cursor, interner)\n                    .map(ast::StatementListItem::from)\n            }\n            TokenKind::Keyword((Keyword::Await, false)) => {\n                // Check if this is `await using`\n                // Per spec, there must be [no LineTerminator here] between `await` and `using`\n                if let Some(next_tok) = cursor.peek_no_skip_line_term(1, interner)?\n                    && next_tok.kind() != &TokenKind::LineTerminator\n                    && matches!(next_tok.kind(), TokenKind::Keyword((Keyword::Using, false)))\n                {\n                    return Declaration::new(self.allow_yield, self.allow_await)\n                        .parse(cursor, interner)\n                        .map(ast::StatementListItem::from);\n                }\n                // Otherwise, parse as a statement (await expression)\n                Statement::new(self.allow_yield, self.allow_await, self.allow_return)\n                    .parse(cursor, interner)\n                    .map(ast::StatementListItem::from)\n            }\n            TokenKind::Keyword((Keyword::Async, false)) => {\n                let skip_n = if cursor.peek_is_line_terminator(0, interner).or_abrupt()? {\n                    2\n                } else {\n                    1\n                };\n                let is_line_terminator = cursor\n                    .peek_is_line_terminator(skip_n, interner)?\n                    .unwrap_or(true);\n\n                match cursor.peek(1, interner)?.map(Token::kind) {\n                    Some(TokenKind::Keyword((Keyword::Function, _))) if !is_line_terminator => {\n                        Declaration::new(self.allow_yield, self.allow_await)\n                            .parse(cursor, interner)\n                            .map(ast::StatementListItem::from)\n                    }\n                    _ => Statement::new(self.allow_yield, self.allow_await, self.allow_return)\n                        .parse(cursor, interner)\n                        .map(ast::StatementListItem::from),\n                }\n            }\n            _ => Statement::new(self.allow_yield, self.allow_await, self.allow_return)\n                .parse(cursor, interner)\n                .map(ast::StatementListItem::from),\n        }\n    }\n}\n\n/// `ObjectBindingPattern` pattern parsing.\n///\n/// More information:\n///  - [ECMAScript specification][spec]\n///\n/// [spec]: https://tc39.es/ecma262/#prod-ObjectBindingPattern\n#[derive(Debug, Clone, Copy)]\npub(super) struct ObjectBindingPattern {\n    allow_yield: AllowYield,\n    allow_await: AllowAwait,\n}\n\nimpl ObjectBindingPattern {\n    /// Creates a new `ObjectBindingPattern` parser.\n    pub(super) fn new<Y, A>(allow_yield: Y, allow_await: A) -> Self\n    where\n        Y: Into<AllowYield>,\n        A: Into<AllowAwait>,\n    {\n        Self {\n            allow_yield: allow_yield.into(),\n            allow_await: allow_await.into(),\n        }\n    }\n}\n\nimpl<R> TokenParser<R> for ObjectBindingPattern\nwhere\n    R: ReadChar,\n{\n    type Output = ObjectPattern;\n\n    fn parse(self, cursor: &mut Cursor<R>, interner: &mut Interner) -> ParseResult<Self::Output> {\n        let start = cursor\n            .expect(\n                TokenKind::Punctuator(Punctuator::OpenBlock),\n                \"object binding pattern\",\n                interner,\n            )?\n            .span()\n            .start();\n\n        let mut patterns = Vec::new();\n\n        loop {\n            let next_token_is_colon = *cursor.peek(1, interner).or_abrupt()?.kind()\n                == TokenKind::Punctuator(Punctuator::Colon);\n            let token = cursor.peek(0, interner).or_abrupt()?;\n            match token.kind() {\n                TokenKind::Punctuator(Punctuator::CloseBlock) => {\n                    let end = cursor\n                        .expect(\n                            TokenKind::Punctuator(Punctuator::CloseBlock),\n                            \"object binding pattern\",\n                            interner,\n                        )?\n                        .span()\n                        .end();\n                    return Ok(ObjectPattern::new(\n                        patterns.into_boxed_slice(),\n                        Span::new(start, end),\n                    ));\n                }\n                TokenKind::Punctuator(Punctuator::Spread) => {\n                    cursor.expect(\n                        TokenKind::Punctuator(Punctuator::Spread),\n                        \"object binding pattern\",\n                        interner,\n                    )?;\n                    let ident = BindingIdentifier::new(self.allow_yield, self.allow_await)\n                        .parse(cursor, interner)?;\n                    patterns.push(ObjectPatternElement::RestProperty { ident });\n\n                    let end = cursor\n                        .expect(\n                            TokenKind::Punctuator(Punctuator::CloseBlock),\n                            \"object binding pattern\",\n                            interner,\n                        )?\n                        .span()\n                        .end();\n                    return Ok(ObjectPattern::new(\n                        patterns.into_boxed_slice(),\n                        Span::new(start, end),\n                    ));\n                }\n                _ => {\n                    let is_property_name = match token.kind() {\n                        TokenKind::Punctuator(Punctuator::OpenBracket)\n                        | TokenKind::StringLiteral(_)\n                        | TokenKind::NumericLiteral(_) => true,\n                        TokenKind::IdentifierName(_)\n                        | TokenKind::Keyword(_)\n                        | TokenKind::BooleanLiteral(_)\n                        | TokenKind::NullLiteral(_)\n                            if next_token_is_colon =>\n                        {\n                            true\n                        }\n                        _ => false,\n                    };\n\n                    if is_property_name {\n                        let property_name = PropertyName::new(self.allow_yield, self.allow_await)\n                            .parse(cursor, interner)?;\n                        cursor.expect(\n                            TokenKind::Punctuator(Punctuator::Colon),\n                            \"object binding pattern\",\n                            interner,\n                        )?;\n                        if let Some(peek_token) = cursor.peek(0, interner)? {\n                            match peek_token.kind() {\n                                TokenKind::Punctuator(Punctuator::OpenBlock) => {\n                                    let bindings = Self::new(self.allow_yield, self.allow_await)\n                                        .parse(cursor, interner)?;\n\n                                    if let Some(peek_token) = cursor.peek(0, interner)? {\n                                        match peek_token.kind() {\n                                            TokenKind::Punctuator(Punctuator::Assign) => {\n                                                let init = Initializer::new(\n                                                    true,\n                                                    self.allow_yield,\n                                                    self.allow_await,\n                                                )\n                                                .parse(cursor, interner)?;\n                                                patterns.push(ObjectPatternElement::Pattern {\n                                                    name: property_name,\n                                                    pattern: bindings.into(),\n                                                    default_init: Some(init),\n                                                });\n                                            }\n                                            _ => {\n                                                patterns.push(ObjectPatternElement::Pattern {\n                                                    name: property_name,\n                                                    pattern: bindings.into(),\n                                                    default_init: None,\n                                                });\n                                            }\n                                        }\n                                    }\n                                }\n                                TokenKind::Punctuator(Punctuator::OpenBracket) => {\n                                    let bindings = ArrayBindingPattern::new(\n                                        self.allow_yield,\n                                        self.allow_await,\n                                    )\n                                    .parse(cursor, interner)?;\n\n                                    if let Some(peek_token) = cursor.peek(0, interner)? {\n                                        match peek_token.kind() {\n                                            TokenKind::Punctuator(Punctuator::Assign) => {\n                                                let init = Initializer::new(\n                                                    true,\n                                                    self.allow_yield,\n                                                    self.allow_await,\n                                                )\n                                                .parse(cursor, interner)?;\n                                                patterns.push(ObjectPatternElement::Pattern {\n                                                    name: property_name,\n                                                    pattern: bindings.into(),\n                                                    default_init: Some(init),\n                                                });\n                                            }\n                                            _ => {\n                                                patterns.push(ObjectPatternElement::Pattern {\n                                                    name: property_name,\n                                                    pattern: bindings.into(),\n                                                    default_init: None,\n                                                });\n                                            }\n                                        }\n                                    }\n                                }\n                                _ => {\n                                    // TODO: Currently parses only BindingIdentifier.\n                                    //       Should parse https://tc39.es/ecma262/#prod-PropertyName\n                                    let ident =\n                                        BindingIdentifier::new(self.allow_yield, self.allow_await)\n                                            .parse(cursor, interner)?;\n\n                                    if let Some(peek_token) = cursor.peek(0, interner)? {\n                                        match peek_token.kind() {\n                                            TokenKind::Punctuator(Punctuator::Assign) => {\n                                                let mut init = Initializer::new(\n                                                    true,\n                                                    self.allow_yield,\n                                                    self.allow_await,\n                                                )\n                                                .parse(cursor, interner)?;\n                                                init.set_anonymous_function_definition_name(&ident);\n                                                patterns.push(ObjectPatternElement::SingleName {\n                                                    ident,\n                                                    name: property_name,\n                                                    default_init: Some(init),\n                                                });\n                                            }\n                                            _ => {\n                                                patterns.push(ObjectPatternElement::SingleName {\n                                                    ident,\n                                                    name: property_name,\n                                                    default_init: None,\n                                                });\n                                            }\n                                        }\n                                    }\n                                }\n                            }\n                        }\n                    } else {\n                        let name = BindingIdentifier::new(self.allow_yield, self.allow_await)\n                            .parse(cursor, interner)?;\n                        match cursor.peek(0, interner)?.map(Token::kind) {\n                            Some(TokenKind::Punctuator(Punctuator::Assign)) => {\n                                let mut init =\n                                    Initializer::new(true, self.allow_yield, self.allow_await)\n                                        .parse(cursor, interner)?;\n                                init.set_anonymous_function_definition_name(&name);\n                                patterns.push(ObjectPatternElement::SingleName {\n                                    ident: name,\n                                    name: name.into(),\n                                    default_init: Some(init),\n                                });\n                            }\n                            _ => {\n                                patterns.push(ObjectPatternElement::SingleName {\n                                    ident: name,\n                                    name: name.into(),\n                                    default_init: None,\n                                });\n                            }\n                        }\n                    }\n                }\n            }\n\n            if let Some(peek_token) = cursor.peek(0, interner)?\n                && peek_token.kind() == &TokenKind::Punctuator(Punctuator::Comma)\n            {\n                cursor.expect(\n                    TokenKind::Punctuator(Punctuator::Comma),\n                    \"object binding pattern\",\n                    interner,\n                )?;\n            }\n        }\n    }\n}\n\n/// `ArrayBindingPattern` pattern parsing.\n///\n/// More information:\n///  - [ECMAScript specification][spec]\n///\n/// [spec]: https://tc39.es/ecma262/#prod-ArrayBindingPattern\n#[derive(Debug, Clone, Copy)]\npub(super) struct ArrayBindingPattern {\n    allow_yield: AllowYield,\n    allow_await: AllowAwait,\n}\n\nimpl ArrayBindingPattern {\n    /// Creates a new `ArrayBindingPattern` parser.\n    pub(super) fn new<Y, A>(allow_yield: Y, allow_await: A) -> Self\n    where\n        Y: Into<AllowYield>,\n        A: Into<AllowAwait>,\n    {\n        Self {\n            allow_yield: allow_yield.into(),\n            allow_await: allow_await.into(),\n        }\n    }\n}\n\nimpl<R> TokenParser<R> for ArrayBindingPattern\nwhere\n    R: ReadChar,\n{\n    type Output = ArrayPattern;\n\n    fn parse(self, cursor: &mut Cursor<R>, interner: &mut Interner) -> ParseResult<Self::Output> {\n        let start = cursor\n            .expect(\n                TokenKind::Punctuator(Punctuator::OpenBracket),\n                \"array binding pattern\",\n                interner,\n            )?\n            .span()\n            .start();\n\n        let mut patterns = Vec::new();\n        let mut last_elision_or_first = true;\n\n        loop {\n            match cursor.peek(0, interner).or_abrupt()?.kind() {\n                TokenKind::Punctuator(Punctuator::CloseBracket) => {\n                    let end = cursor\n                        .expect(\n                            TokenKind::Punctuator(Punctuator::CloseBracket),\n                            \"array binding pattern\",\n                            interner,\n                        )?\n                        .span()\n                        .end();\n                    return Ok(ArrayPattern::new(\n                        patterns.into_boxed_slice(),\n                        Span::new(start, end),\n                    ));\n                }\n                TokenKind::Punctuator(Punctuator::Comma) => {\n                    cursor.expect(\n                        TokenKind::Punctuator(Punctuator::Comma),\n                        \"array binding pattern\",\n                        interner,\n                    )?;\n                    if last_elision_or_first {\n                        patterns.push(ArrayPatternElement::Elision);\n                    } else {\n                        last_elision_or_first = true;\n                    }\n                    continue;\n                }\n                TokenKind::Punctuator(Punctuator::Spread) => {\n                    cursor.expect(\n                        TokenKind::Punctuator(Punctuator::Spread),\n                        \"array binding pattern\",\n                        interner,\n                    )?;\n\n                    match cursor.peek(0, interner).or_abrupt()?.kind() {\n                        TokenKind::Punctuator(Punctuator::OpenBlock) => {\n                            let bindings =\n                                ObjectBindingPattern::new(self.allow_yield, self.allow_await)\n                                    .parse(cursor, interner)?;\n                            patterns.push(ArrayPatternElement::PatternRest {\n                                pattern: bindings.into(),\n                            });\n                        }\n                        TokenKind::Punctuator(Punctuator::OpenBracket) => {\n                            let bindings = Self::new(self.allow_yield, self.allow_await)\n                                .parse(cursor, interner)?;\n                            patterns.push(ArrayPatternElement::PatternRest {\n                                pattern: bindings.into(),\n                            });\n                        }\n                        _ => {\n                            let rest_property_name =\n                                BindingIdentifier::new(self.allow_yield, self.allow_await)\n                                    .parse(cursor, interner)?;\n                            patterns.push(ArrayPatternElement::SingleNameRest {\n                                ident: rest_property_name,\n                            });\n                        }\n                    }\n\n                    let end = cursor\n                        .expect(\n                            TokenKind::Punctuator(Punctuator::CloseBracket),\n                            \"array binding pattern\",\n                            interner,\n                        )?\n                        .span()\n                        .end();\n\n                    return Ok(ArrayPattern::new(\n                        patterns.into_boxed_slice(),\n                        Span::new(start, end),\n                    ));\n                }\n                TokenKind::Punctuator(Punctuator::OpenBlock) => {\n                    last_elision_or_first = false;\n\n                    let bindings = ObjectBindingPattern::new(self.allow_yield, self.allow_await)\n                        .parse(cursor, interner)?;\n\n                    match cursor.peek(0, interner).or_abrupt()?.kind() {\n                        TokenKind::Punctuator(Punctuator::Assign) => {\n                            let default_init =\n                                Initializer::new(true, self.allow_yield, self.allow_await)\n                                    .parse(cursor, interner)?;\n                            patterns.push(ArrayPatternElement::Pattern {\n                                pattern: bindings.into(),\n                                default_init: Some(default_init),\n                            });\n                        }\n                        _ => {\n                            patterns.push(ArrayPatternElement::Pattern {\n                                pattern: bindings.into(),\n                                default_init: None,\n                            });\n                        }\n                    }\n                }\n                TokenKind::Punctuator(Punctuator::OpenBracket) => {\n                    last_elision_or_first = false;\n\n                    let bindings =\n                        Self::new(self.allow_yield, self.allow_await).parse(cursor, interner)?;\n\n                    match cursor.peek(0, interner).or_abrupt()?.kind() {\n                        TokenKind::Punctuator(Punctuator::Assign) => {\n                            let default_init =\n                                Initializer::new(true, self.allow_yield, self.allow_await)\n                                    .parse(cursor, interner)?;\n                            patterns.push(ArrayPatternElement::Pattern {\n                                pattern: bindings.into(),\n                                default_init: Some(default_init),\n                            });\n                        }\n                        _ => {\n                            patterns.push(ArrayPatternElement::Pattern {\n                                pattern: bindings.into(),\n                                default_init: None,\n                            });\n                        }\n                    }\n                }\n                _ => {\n                    last_elision_or_first = false;\n\n                    let ident = BindingIdentifier::new(self.allow_yield, self.allow_await)\n                        .parse(cursor, interner)?;\n                    match cursor.peek(0, interner).or_abrupt()?.kind() {\n                        TokenKind::Punctuator(Punctuator::Assign) => {\n                            let mut init =\n                                Initializer::new(true, self.allow_yield, self.allow_await)\n                                    .parse(cursor, interner)?;\n                            init.set_anonymous_function_definition_name(&ident);\n                            patterns.push(ArrayPatternElement::SingleName {\n                                ident,\n                                default_init: Some(init),\n                            });\n                        }\n                        _ => {\n                            patterns.push(ArrayPatternElement::SingleName {\n                                ident,\n                                default_init: None,\n                            });\n                        }\n                    }\n                }\n            }\n\n            if let Some(peek_token) = cursor.peek(0, interner)?\n                && peek_token.kind() == &TokenKind::Punctuator(Punctuator::Comma)\n            {\n                cursor.expect(\n                    TokenKind::Punctuator(Punctuator::Comma),\n                    \"array binding pattern\",\n                    interner,\n                )?;\n                if last_elision_or_first {\n                    patterns.push(ArrayPatternElement::Elision);\n                } else {\n                    last_elision_or_first = true;\n                }\n            }\n        }\n    }\n}\n\n/// Parses a module body\n///\n/// More information:\n///  - [ECMAScript specification][spec]\n///\n/// [spec]: https://tc39.es/ecma262/#prod-ModuleBody\n#[derive(Debug, Clone, Copy)]\npub(super) struct ModuleItemList;\n\nimpl<R> TokenParser<R> for ModuleItemList\nwhere\n    R: ReadChar,\n{\n    type Output = boa_ast::ModuleItemList;\n\n    fn parse(self, cursor: &mut Cursor<R>, interner: &mut Interner) -> ParseResult<Self::Output> {\n        let mut list = Vec::new();\n        while cursor.peek(0, interner)?.is_some() {\n            let item = ModuleItem.parse(cursor, interner)?;\n\n            if let Err(error) = check_labels(&item) {\n                return Err(Error::lex(LexError::Syntax(\n                    error.message(interner).into(),\n                    Position::new(1, 1),\n                )));\n            }\n\n            if contains_invalid_object_literal(&item) {\n                return Err(Error::lex(LexError::Syntax(\n                    \"invalid object literal in module item list\".into(),\n                    Position::new(1, 1),\n                )));\n            }\n\n            list.push(item);\n        }\n\n        let list = list.into();\n\n        // It is a Syntax Error if AllPrivateIdentifiersValid of ModuleItemList with argument « » is false.\n        if !all_private_identifiers_valid(&list, Vec::new()) {\n            return Err(Error::general(\n                \"invalid private identifier usage\",\n                Position::new(1, 1),\n            ));\n        }\n\n        Ok(list)\n    }\n}\n\n/// Parses a module item.\n///\n/// More information:\n///  - [ECMAScript specification][spec]\n///\n/// [spec]: https://tc39.es/ecma262/#prod-ModuleItem\nstruct ModuleItem;\n\nimpl<R> TokenParser<R> for ModuleItem\nwhere\n    R: ReadChar,\n{\n    type Output = boa_ast::ModuleItem;\n\n    fn parse(self, cursor: &mut Cursor<R>, interner: &mut Interner) -> ParseResult<Self::Output> {\n        let tok = cursor.peek(0, interner).or_abrupt()?;\n\n        match tok.kind() {\n            TokenKind::Keyword((Keyword::Export, false)) => ExportDeclaration\n                .parse(cursor, interner)\n                .map(Box::new)\n                .map(Self::Output::ExportDeclaration),\n            TokenKind::Keyword((Keyword::Import, false)) => {\n                if ImportDeclaration::test(cursor, interner)? {\n                    ImportDeclaration\n                        .parse(cursor, interner)\n                        .map(Self::Output::ImportDeclaration)\n                } else {\n                    StatementListItem::new(false, true, false)\n                        .parse(cursor, interner)\n                        .map(Self::Output::StatementListItem)\n                }\n            }\n            _ => StatementListItem::new(false, true, false)\n                .parse(cursor, interner)\n                .map(Self::Output::StatementListItem),\n        }\n    }\n}\n"
  },
  {
    "path": "core/parser/src/parser/statement/return_stm/mod.rs",
    "content": "use crate::{\n    lexer::{Token, TokenKind},\n    parser::{\n        AllowAwait, AllowYield, ParseResult, TokenParser,\n        cursor::{Cursor, SemicolonResult},\n        expression::Expression,\n    },\n    source::ReadChar,\n};\nuse boa_ast::{Keyword, Punctuator, statement::Return};\nuse boa_interner::Interner;\n\n/// Return statement parsing\n///\n/// More information:\n///  - [MDN documentation][mdn]\n///  - [ECMAScript specification][spec]\n///\n/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/return\n/// [spec]: https://tc39.es/ecma262/#prod-ReturnStatement\n#[derive(Debug, Clone, Copy)]\npub(super) struct ReturnStatement {\n    allow_yield: AllowYield,\n    allow_await: AllowAwait,\n}\n\nimpl ReturnStatement {\n    /// Creates a new `ReturnStatement` parser.\n    pub(super) fn new<Y, A>(allow_yield: Y, allow_await: A) -> Self\n    where\n        Y: Into<AllowYield>,\n        A: Into<AllowAwait>,\n    {\n        Self {\n            allow_yield: allow_yield.into(),\n            allow_await: allow_await.into(),\n        }\n    }\n}\n\nimpl<R> TokenParser<R> for ReturnStatement\nwhere\n    R: ReadChar,\n{\n    type Output = Return;\n\n    fn parse(self, cursor: &mut Cursor<R>, interner: &mut Interner) -> ParseResult<Self::Output> {\n        cursor.expect((Keyword::Return, false), \"return statement\", interner)?;\n\n        if let SemicolonResult::Found(tok) = cursor.peek_semicolon(interner)? {\n            if tok.map(Token::kind) == Some(&TokenKind::Punctuator(Punctuator::Semicolon)) {\n                cursor.advance(interner);\n            }\n\n            return Ok(Return::new(None));\n        }\n\n        let expr =\n            Expression::new(true, self.allow_yield, self.allow_await).parse(cursor, interner)?;\n\n        cursor.expect_semicolon(\"return statement\", interner)?;\n\n        Ok(Return::new(Some(expr)))\n    }\n}\n"
  },
  {
    "path": "core/parser/src/parser/statement/switch/mod.rs",
    "content": "#[cfg(test)]\nmod tests;\n\nuse crate::{\n    Error,\n    lexer::TokenKind,\n    parser::{\n        AllowAwait, AllowReturn, AllowYield, Cursor, OrAbrupt, ParseResult, TokenParser,\n        expression::Expression, statement::StatementList,\n    },\n    source::ReadChar,\n};\nuse ast::operations::{lexically_declared_names_legacy, var_declared_names};\nuse boa_ast::{self as ast, Keyword, Punctuator, Spanned, statement, statement::Switch};\nuse boa_interner::Interner;\nuse rustc_hash::FxHashMap;\n\n/// The possible `TokenKind` which indicate the end of a case statement.\nconst CASE_BREAK_TOKENS: [TokenKind; 3] = [\n    TokenKind::Punctuator(Punctuator::CloseBlock),\n    TokenKind::Keyword((Keyword::Case, false)),\n    TokenKind::Keyword((Keyword::Default, false)),\n];\n\n/// Switch statement parsing.\n///\n/// More information:\n///  - [MDN documentation][mdn]\n///  - [ECMAScript specification][spec]\n///\n/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/switch\n/// [spec]: https://tc39.es/ecma262/#prod-SwitchStatement\n#[derive(Debug, Clone, Copy)]\npub(super) struct SwitchStatement {\n    allow_yield: AllowYield,\n    allow_await: AllowAwait,\n    allow_return: AllowReturn,\n}\n\nimpl SwitchStatement {\n    /// Creates a new `SwitchStatement` parser.\n    pub(super) fn new<Y, A, R>(allow_yield: Y, allow_await: A, allow_return: R) -> Self\n    where\n        Y: Into<AllowYield>,\n        A: Into<AllowAwait>,\n        R: Into<AllowReturn>,\n    {\n        Self {\n            allow_yield: allow_yield.into(),\n            allow_await: allow_await.into(),\n            allow_return: allow_return.into(),\n        }\n    }\n}\n\nimpl<R> TokenParser<R> for SwitchStatement\nwhere\n    R: ReadChar,\n{\n    type Output = Switch;\n\n    fn parse(self, cursor: &mut Cursor<R>, interner: &mut Interner) -> ParseResult<Self::Output> {\n        cursor.expect((Keyword::Switch, false), \"switch statement\", interner)?;\n        cursor.expect(Punctuator::OpenParen, \"switch statement\", interner)?;\n\n        let condition =\n            Expression::new(true, self.allow_yield, self.allow_await).parse(cursor, interner)?;\n\n        cursor.expect(Punctuator::CloseParen, \"switch statement\", interner)?;\n\n        let position = cursor.peek(0, interner).or_abrupt()?.span().start();\n\n        let cases = CaseBlock::new(self.allow_yield, self.allow_await, self.allow_return)\n            .parse(cursor, interner)?;\n\n        let switch = Switch::new(condition, cases);\n\n        // It is a Syntax Error if the LexicallyDeclaredNames of CaseBlock contains any duplicate\n        // entries, unless the source text matched by this production is not strict mode code and the\n        // duplicate entries are only bound by FunctionDeclarations.\n        let mut lexical_names = FxHashMap::default();\n        for (name, is_fn) in lexically_declared_names_legacy(&switch) {\n            if let Some(is_fn_previous) = lexical_names.insert(name, is_fn) {\n                match (cursor.strict(), is_fn, is_fn_previous) {\n                    (false, true, true) => {}\n                    _ => {\n                        return Err(Error::general(\n                            \"lexical name declared multiple times\",\n                            position,\n                        ));\n                    }\n                }\n            }\n        }\n\n        // It is a Syntax Error if any element of the LexicallyDeclaredNames of CaseBlock also occurs\n        // in the VarDeclaredNames of CaseBlock.\n        for name in var_declared_names(&switch) {\n            if lexical_names.contains_key(&name) {\n                return Err(Error::general(\n                    \"lexical name declared in var declared names\",\n                    position,\n                ));\n            }\n        }\n\n        Ok(switch)\n    }\n}\n\n/// Switch case block parsing.\n///\n/// More information:\n///  - [ECMAScript specification][spec]\n///\n/// [spec]: https://tc39.es/ecma262/#prod-CaseBlock\n#[derive(Debug, Clone, Copy)]\nstruct CaseBlock {\n    allow_yield: AllowYield,\n    allow_await: AllowAwait,\n    allow_return: AllowReturn,\n}\n\nimpl CaseBlock {\n    /// Creates a new `CaseBlock` parser.\n    fn new<Y, A, R>(allow_yield: Y, allow_await: A, allow_return: R) -> Self\n    where\n        Y: Into<AllowYield>,\n        A: Into<AllowAwait>,\n        R: Into<AllowReturn>,\n    {\n        Self {\n            allow_yield: allow_yield.into(),\n            allow_await: allow_await.into(),\n            allow_return: allow_return.into(),\n        }\n    }\n}\n\nimpl<R> TokenParser<R> for CaseBlock\nwhere\n    R: ReadChar,\n{\n    type Output = Box<[statement::Case]>;\n\n    fn parse(self, cursor: &mut Cursor<R>, interner: &mut Interner) -> ParseResult<Self::Output> {\n        cursor.expect(Punctuator::OpenBlock, \"switch case block\", interner)?;\n\n        let mut cases = Vec::new();\n        let mut has_default_case = false;\n\n        loop {\n            let token = cursor.next(interner).or_abrupt()?;\n            match token.kind() {\n                TokenKind::Keyword((Keyword::Case | Keyword::Default, true)) => {\n                    return Err(Error::general(\n                        \"Keyword must not contain escaped characters\",\n                        token.span().start(),\n                    ));\n                }\n                TokenKind::Keyword((Keyword::Case, false)) => {\n                    // Case statement.\n                    let cond = Expression::new(true, self.allow_yield, self.allow_await)\n                        .parse(cursor, interner)?;\n\n                    cursor.expect(Punctuator::Colon, \"switch case block\", interner)?;\n\n                    let (statement_list, _) = StatementList::new(\n                        self.allow_yield,\n                        self.allow_await,\n                        self.allow_return,\n                        &CASE_BREAK_TOKENS,\n                        false,\n                        false,\n                    )\n                    .parse(cursor, interner)?;\n\n                    cases.push(statement::Case::new(cond, statement_list));\n                }\n                TokenKind::Keyword((Keyword::Default, false)) => {\n                    if has_default_case {\n                        // If default has already been defined then it cannot be defined again and to do so is an error.\n                        return Err(Error::unexpected(\n                            token.to_string(interner),\n                            token.span(),\n                            \"more than one switch default\",\n                        ));\n                    }\n\n                    cursor.expect(Punctuator::Colon, \"switch default block\", interner)?;\n\n                    let (statement_list, _) = StatementList::new(\n                        self.allow_yield,\n                        self.allow_await,\n                        self.allow_return,\n                        &CASE_BREAK_TOKENS,\n                        false,\n                        false,\n                    )\n                    .parse(cursor, interner)?;\n\n                    cases.push(statement::Case::default(statement_list));\n\n                    has_default_case = true;\n                }\n                TokenKind::Punctuator(Punctuator::CloseBlock) => break,\n                _ => {\n                    return Err(Error::expected(\n                        [\"case\".to_owned(), \"default\".to_owned(), \"}\".to_owned()],\n                        token.to_string(interner),\n                        token.span(),\n                        \"switch case block\",\n                    ));\n                }\n            }\n        }\n\n        Ok(cases.into_boxed_slice())\n    }\n}\n"
  },
  {
    "path": "core/parser/src/parser/statement/switch/tests.rs",
    "content": "use crate::parser::tests::{check_invalid_script, check_script_parser};\nuse boa_ast::{\n    Declaration, Expression, Span, Statement,\n    declaration::{LexicalDeclaration, Variable},\n    expression::{Call, Identifier, access::SimplePropertyAccess, literal::Literal},\n    statement::{Break, Case, Switch},\n};\nuse boa_interner::Interner;\nuse boa_macros::utf16;\nuse indoc::indoc;\n\nconst PSEUDO_LINEAR_POS: boa_ast::LinearPosition = boa_ast::LinearPosition::new(0);\n\n/// Checks parsing malformed switch with no closeblock.\n#[test]\nfn check_switch_no_closeblock() {\n    check_invalid_script(\n        r#\"\n        let a = 10;\n        switch (a) {\n            case 10:\n                a = 20;\n                break;\n\n        \"#,\n    );\n}\n\n/// Checks parsing malformed switch in which a case is started but not finished.\n#[test]\nfn check_switch_case_unclosed() {\n    check_invalid_script(\n        r#\"\n        let a = 10;\n        switch (a) {\n            case 10:\n                a = 20;\n\n        \"#,\n    );\n}\n\n/// Checks parsing malformed switch with 2 defaults.\n#[test]\nfn check_switch_two_default() {\n    check_invalid_script(\n        r#\"\n        let a = 10;\n        switch (a) {\n            default:\n                a = 20;\n                break;\n            default:\n                a = 30;\n                break;\n        }\n        \"#,\n    );\n}\n\n/// Checks parsing malformed switch with no expression.\n#[test]\nfn check_switch_no_expr() {\n    check_invalid_script(\n        r#\"\n        let a = 10;\n        switch {\n            default:\n                a = 20;\n                break;\n        }\n        \"#,\n    );\n}\n\n/// Checks parsing malformed switch with an unknown label.\n#[test]\nfn check_switch_unknown_label() {\n    check_invalid_script(\n        r#\"\n        let a = 10;\n        switch (a) {\n            fake:\n                a = 20;\n                break;\n        }\n        \"#,\n    );\n}\n\n/// Checks parsing malformed switch with two defaults that are separated by cases.\n#[test]\nfn check_switch_separated_defaults() {\n    check_invalid_script(\n        r#\"\n        let a = 10;\n        switch (a) {\n            default:\n                a = 20;\n                break;\n            case 10:\n                a = 60;\n                break;\n            default:\n                a = 30;\n                break;\n        }\n        \"#,\n    );\n}\n\n/// Example of JS code <https://jsfiddle.net/zq6jx47h/4/>.\n#[test]\nfn check_separated_switch() {\n    let s = indoc! {r#\"\n        let a = 10;\n\n        switch\n\n        (a)\n\n        {\n\n        case\n\n        5\n\n        :\n\n        console.log(5);\n\n        break;\n\n        case\n\n        10\n\n        :\n\n        console.log(10);\n\n        break;\n\n        default\n\n        :\n\n        console.log(\"Default\")\n\n        }\n    \"#};\n\n    let interner = &mut Interner::default();\n    let log = interner.get_or_intern_static(\"log\", utf16!(\"log\"));\n    let console = interner.get_or_intern_static(\"console\", utf16!(\"console\"));\n    let a = interner.get_or_intern_static(\"a\", utf16!(\"a\"));\n\n    check_script_parser(\n        s,\n        vec![\n            Declaration::Lexical(LexicalDeclaration::Let(\n                vec![Variable::from_identifier(\n                    Identifier::new(a, Span::new((1, 5), (1, 6))),\n                    Some(Literal::new(10, Span::new((1, 9), (1, 11))).into()),\n                )]\n                .try_into()\n                .unwrap(),\n            ))\n            .into(),\n            Statement::Switch(Switch::new(\n                Identifier::new(a, Span::new((5, 2), (5, 3))).into(),\n                vec![\n                    Case::new(\n                        Literal::new(5, Span::new((11, 1), (11, 2))).into(),\n                        (\n                            vec![\n                                Statement::Expression(\n                                    Call::new(\n                                        Expression::PropertyAccess(\n                                            SimplePropertyAccess::new(\n                                                Identifier::new(\n                                                    console,\n                                                    Span::new((15, 1), (15, 8)),\n                                                )\n                                                .into(),\n                                                Identifier::new(log, Span::new((15, 9), (15, 12))),\n                                            )\n                                            .into(),\n                                        ),\n                                        vec![Literal::new(5, Span::new((15, 13), (15, 14))).into()]\n                                            .into(),\n                                        Span::new((15, 12), (15, 15)),\n                                    )\n                                    .into(),\n                                )\n                                .into(),\n                                Statement::Break(Break::new(None)).into(),\n                            ],\n                            PSEUDO_LINEAR_POS,\n                        )\n                            .into(),\n                    ),\n                    Case::new(\n                        Literal::new(10, Span::new((21, 1), (21, 3))).into(),\n                        (\n                            vec![\n                                Statement::Expression(\n                                    Call::new(\n                                        Expression::PropertyAccess(\n                                            SimplePropertyAccess::new(\n                                                Identifier::new(\n                                                    console,\n                                                    Span::new((25, 1), (25, 8)),\n                                                )\n                                                .into(),\n                                                Identifier::new(log, Span::new((25, 9), (25, 12))),\n                                            )\n                                            .into(),\n                                        ),\n                                        vec![\n                                            Literal::new(10, Span::new((25, 13), (25, 15))).into(),\n                                        ]\n                                        .into(),\n                                        Span::new((25, 12), (25, 16)),\n                                    )\n                                    .into(),\n                                )\n                                .into(),\n                                Statement::Break(Break::new(None)).into(),\n                            ],\n                            PSEUDO_LINEAR_POS,\n                        )\n                            .into(),\n                    ),\n                    Case::default(\n                        (\n                            vec![\n                                Statement::Expression(\n                                    Call::new(\n                                        Expression::PropertyAccess(\n                                            SimplePropertyAccess::new(\n                                                Identifier::new(\n                                                    console,\n                                                    Span::new((33, 1), (33, 8)),\n                                                )\n                                                .into(),\n                                                Identifier::new(log, Span::new((33, 9), (33, 12))),\n                                            )\n                                            .into(),\n                                        ),\n                                        vec![\n                                            Literal::new(\n                                                interner.get_or_intern_static(\n                                                    \"Default\",\n                                                    utf16!(\"Default\"),\n                                                ),\n                                                Span::new((33, 13), (33, 22)),\n                                            )\n                                            .into(),\n                                        ]\n                                        .into(),\n                                        Span::new((33, 12), (33, 23)),\n                                    )\n                                    .into(),\n                                )\n                                .into(),\n                            ],\n                            PSEUDO_LINEAR_POS,\n                        )\n                            .into(),\n                    ),\n                ]\n                .into(),\n            ))\n            .into(),\n        ],\n        interner,\n    );\n}\n"
  },
  {
    "path": "core/parser/src/parser/statement/throw/mod.rs",
    "content": "#[cfg(test)]\nmod tests;\n\nuse crate::{\n    parser::{AllowAwait, AllowYield, Cursor, ParseResult, TokenParser, expression::Expression},\n    source::ReadChar,\n};\nuse boa_ast::{Keyword, statement::Throw};\nuse boa_interner::Interner;\n\n/// For statement parsing\n///\n/// More information:\n///  - [MDN documentation][mdn]\n///  - [ECMAScript specification][spec]\n///\n/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/throw\n/// [spec]: https://tc39.es/ecma262/#prod-ThrowStatement\n#[derive(Debug, Clone, Copy)]\npub(super) struct ThrowStatement {\n    allow_yield: AllowYield,\n    allow_await: AllowAwait,\n}\n\nimpl ThrowStatement {\n    /// Creates a new `ThrowStatement` parser.\n    pub(super) fn new<Y, A>(allow_yield: Y, allow_await: A) -> Self\n    where\n        Y: Into<AllowYield>,\n        A: Into<AllowAwait>,\n    {\n        Self {\n            allow_yield: allow_yield.into(),\n            allow_await: allow_await.into(),\n        }\n    }\n}\n\nimpl<R> TokenParser<R> for ThrowStatement\nwhere\n    R: ReadChar,\n{\n    type Output = Throw;\n\n    fn parse(self, cursor: &mut Cursor<R>, interner: &mut Interner) -> ParseResult<Self::Output> {\n        cursor.expect((Keyword::Throw, false), \"throw statement\", interner)?;\n\n        cursor.peek_expect_no_lineterminator(0, \"throw statement\", interner)?;\n\n        let expr =\n            Expression::new(true, self.allow_yield, self.allow_await).parse(cursor, interner)?;\n\n        cursor.expect_semicolon(\"throw statement\", interner)?;\n\n        Ok(Throw::new(expr))\n    }\n}\n"
  },
  {
    "path": "core/parser/src/parser/statement/throw/tests.rs",
    "content": "use crate::parser::tests::{check_invalid_script, check_script_parser};\nuse boa_ast::{Span, Statement, expression::literal::Literal, statement::Throw};\nuse boa_interner::Interner;\nuse boa_macros::utf16;\n\n#[test]\nfn check_throw_parsing() {\n    let interner = &mut Interner::default();\n    check_script_parser(\n        \"throw 'error';\",\n        vec![\n            Statement::Throw(Throw::new(\n                Literal::new(\n                    interner.get_or_intern_static(\"error\", utf16!(\"error\")),\n                    Span::new((1, 7), (1, 14)),\n                )\n                .into(),\n            ))\n            .into(),\n        ],\n        interner,\n    );\n}\n\n#[test]\nfn check_throw_syntax_error() {\n    check_invalid_script(\"throw async () => {} - 1;\");\n}\n"
  },
  {
    "path": "core/parser/src/parser/statement/try_stm/catch.rs",
    "content": "use crate::{\n    Error,\n    lexer::TokenKind,\n    parser::{\n        AllowAwait, AllowReturn, AllowYield, Cursor, OrAbrupt, ParseResult, TokenParser,\n        statement::{ArrayBindingPattern, BindingIdentifier, ObjectBindingPattern, block::Block},\n    },\n    source::ReadChar,\n};\nuse boa_ast::{\n    Keyword, Punctuator, Spanned,\n    declaration::Binding,\n    operations::{bound_names, lexically_declared_names, var_declared_names},\n    statement,\n};\nuse boa_interner::Interner;\nuse rustc_hash::FxHashSet;\n\n/// Catch parsing\n///\n/// More information:\n///  - [MDN documentation][mdn]\n///  - [ECMAScript specification][spec]\n///\n/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/try...catch\n/// [spec]: https://tc39.es/ecma262/#prod-Catch\n#[derive(Debug, Clone, Copy)]\npub(super) struct Catch {\n    allow_yield: AllowYield,\n    allow_await: AllowAwait,\n    allow_return: AllowReturn,\n}\n\nimpl Catch {\n    /// Creates a new `Catch` block parser.\n    pub(super) fn new<Y, A, R>(allow_yield: Y, allow_await: A, allow_return: R) -> Self\n    where\n        Y: Into<AllowYield>,\n        A: Into<AllowAwait>,\n        R: Into<AllowReturn>,\n    {\n        Self {\n            allow_yield: allow_yield.into(),\n            allow_await: allow_await.into(),\n            allow_return: allow_return.into(),\n        }\n    }\n}\n\nimpl<R> TokenParser<R> for Catch\nwhere\n    R: ReadChar,\n{\n    type Output = statement::Catch;\n\n    fn parse(self, cursor: &mut Cursor<R>, interner: &mut Interner) -> ParseResult<Self::Output> {\n        cursor.expect((Keyword::Catch, false), \"try statement\", interner)?;\n        let position = cursor.peek(0, interner).or_abrupt()?.span().start();\n        let catch_param = if cursor.next_if(Punctuator::OpenParen, interner)?.is_some() {\n            let catch_param =\n                CatchParameter::new(self.allow_yield, self.allow_await).parse(cursor, interner)?;\n\n            cursor.expect(Punctuator::CloseParen, \"catch in try statement\", interner)?;\n            Some(catch_param)\n        } else {\n            None\n        };\n\n        // It is a Syntax Error if BoundNames of CatchParameter contains any duplicate elements.\n        // https://tc39.es/ecma262/#sec-try-statement-static-semantics-early-errors\n        let bound_names: Option<FxHashSet<_>> = catch_param\n            .as_ref()\n            .map(|binding| {\n                let mut set = FxHashSet::default();\n                for ident in bound_names(binding) {\n                    if !set.insert(ident) {\n                        return Err(Error::general(\n                            \"duplicate catch parameter identifier\",\n                            position,\n                        ));\n                    }\n                }\n                Ok(set)\n            })\n            .transpose()?;\n\n        let position = cursor.peek(0, interner).or_abrupt()?.span().start();\n        let catch_block = Block::new(self.allow_yield, self.allow_await, self.allow_return)\n            .parse(cursor, interner)?;\n\n        // It is a Syntax Error if any element of the BoundNames of CatchParameter also occurs in the LexicallyDeclaredNames of Block.\n        // It is a Syntax Error if any element of the BoundNames of CatchParameter also occurs in the VarDeclaredNames of Block unless CatchParameter is CatchParameter : BindingIdentifier .\n        // https://tc39.es/ecma262/#sec-try-statement-static-semantics-early-errors\n        // https://tc39.es/ecma262/#sec-variablestatements-in-catch-blocks\n        if let Some(bound_names) = bound_names {\n            for name in lexically_declared_names(&catch_block) {\n                if bound_names.contains(&name) {\n                    return Err(Error::general(\n                        \"catch parameter identifier declared in catch body\",\n                        position,\n                    ));\n                }\n            }\n            if !matches!(&catch_param, Some(Binding::Identifier(_))) {\n                for name in var_declared_names(&catch_block) {\n                    if bound_names.contains(&name) {\n                        return Err(Error::general(\n                            \"catch parameter identifier declared in catch body\",\n                            position,\n                        ));\n                    }\n                }\n            }\n        }\n\n        let catch_node = statement::Catch::new(catch_param, catch_block);\n        Ok(catch_node)\n    }\n}\n\n/// `CatchParameter` parsing\n///\n/// More information:\n///  - [MDN documentation][mdn]\n///  - [ECMAScript specification][spec]\n///\n/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/try...catch\n/// [spec]: https://tc39.es/ecma262/#prod-CatchParameter\n#[derive(Debug, Clone, Copy)]\npub(super) struct CatchParameter {\n    allow_yield: AllowYield,\n    allow_await: AllowAwait,\n}\n\nimpl CatchParameter {\n    /// Creates a new `CatchParameter` parser.\n    pub(super) fn new<Y, A>(allow_yield: Y, allow_await: A) -> Self\n    where\n        Y: Into<AllowYield>,\n        A: Into<AllowAwait>,\n    {\n        Self {\n            allow_yield: allow_yield.into(),\n            allow_await: allow_await.into(),\n        }\n    }\n}\n\nimpl<R> TokenParser<R> for CatchParameter\nwhere\n    R: ReadChar,\n{\n    type Output = Binding;\n\n    fn parse(self, cursor: &mut Cursor<R>, interner: &mut Interner) -> ParseResult<Self::Output> {\n        let token = cursor.peek(0, interner).or_abrupt()?;\n\n        match token.kind() {\n            TokenKind::Punctuator(Punctuator::OpenBlock) => {\n                let pat = ObjectBindingPattern::new(self.allow_yield, self.allow_await)\n                    .parse(cursor, interner)?;\n\n                Ok(Binding::Pattern(pat.into()))\n            }\n            TokenKind::Punctuator(Punctuator::OpenBracket) => {\n                let pat = ArrayBindingPattern::new(self.allow_yield, self.allow_await)\n                    .parse(cursor, interner)?;\n                Ok(Binding::Pattern(pat.into()))\n            }\n            _ => Ok(Binding::Identifier(\n                BindingIdentifier::new(self.allow_yield, self.allow_await)\n                    .parse(cursor, interner)?,\n            )),\n        }\n    }\n}\n"
  },
  {
    "path": "core/parser/src/parser/statement/try_stm/finally.rs",
    "content": "use crate::{\n    parser::{\n        AllowAwait, AllowReturn, AllowYield, Cursor, ParseResult, TokenParser,\n        statement::block::Block,\n    },\n    source::ReadChar,\n};\nuse boa_ast::{Keyword, statement};\nuse boa_interner::Interner;\n\n/// Finally parsing\n///\n/// More information:\n///  - [MDN documentation][mdn]\n///  - [ECMAScript specification][spec]\n///\n/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/try...catch\n/// [spec]: https://tc39.es/ecma262/#prod-Finally\n#[derive(Debug, Clone, Copy)]\npub(super) struct Finally {\n    allow_yield: AllowYield,\n    allow_await: AllowAwait,\n    allow_return: AllowReturn,\n}\n\nimpl Finally {\n    /// Creates a new `Finally` block parser.\n    pub(super) fn new<Y, A, R>(allow_yield: Y, allow_await: A, allow_return: R) -> Self\n    where\n        Y: Into<AllowYield>,\n        A: Into<AllowAwait>,\n        R: Into<AllowReturn>,\n    {\n        Self {\n            allow_yield: allow_yield.into(),\n            allow_await: allow_await.into(),\n            allow_return: allow_return.into(),\n        }\n    }\n}\n\nimpl<R> TokenParser<R> for Finally\nwhere\n    R: ReadChar,\n{\n    type Output = statement::Finally;\n\n    fn parse(self, cursor: &mut Cursor<R>, interner: &mut Interner) -> ParseResult<Self::Output> {\n        cursor.expect((Keyword::Finally, false), \"try statement\", interner)?;\n        Ok(\n            Block::new(self.allow_yield, self.allow_await, self.allow_return)\n                .parse(cursor, interner)?\n                .into(),\n        )\n    }\n}\n"
  },
  {
    "path": "core/parser/src/parser/statement/try_stm/mod.rs",
    "content": "mod catch;\nmod finally;\n\n#[cfg(test)]\nmod tests;\n\nuse self::{catch::Catch, finally::Finally};\nuse super::block::Block;\nuse crate::{\n    Error,\n    lexer::TokenKind,\n    parser::{AllowAwait, AllowReturn, AllowYield, Cursor, OrAbrupt, ParseResult, TokenParser},\n    source::ReadChar,\n};\nuse boa_ast::{\n    Keyword, Spanned,\n    statement::{ErrorHandler, Try},\n};\nuse boa_interner::Interner;\n\n/// Try...catch statement parsing\n///\n/// More information:\n///  - [MDN documentation][mdn]\n///  - [ECMAScript specification][spec]\n///\n/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/try...catch\n/// [spec]: https://tc39.es/ecma262/#sec-try-statement\n#[derive(Debug, Clone, Copy)]\npub(super) struct TryStatement {\n    allow_yield: AllowYield,\n    allow_await: AllowAwait,\n    allow_return: AllowReturn,\n}\n\nimpl TryStatement {\n    /// Creates a new `TryStatement` parser.\n    pub(super) fn new<Y, A, R>(allow_yield: Y, allow_await: A, allow_return: R) -> Self\n    where\n        Y: Into<AllowYield>,\n        A: Into<AllowAwait>,\n        R: Into<AllowReturn>,\n    {\n        Self {\n            allow_yield: allow_yield.into(),\n            allow_await: allow_await.into(),\n            allow_return: allow_return.into(),\n        }\n    }\n}\n\nimpl<R> TokenParser<R> for TryStatement\nwhere\n    R: ReadChar,\n{\n    type Output = Try;\n\n    fn parse(self, cursor: &mut Cursor<R>, interner: &mut Interner) -> ParseResult<Self::Output> {\n        // TRY\n        cursor.expect((Keyword::Try, false), \"try statement\", interner)?;\n\n        let try_clause = Block::new(self.allow_yield, self.allow_await, self.allow_return)\n            .parse(cursor, interner)?;\n\n        let next_token = cursor.peek(0, interner).or_abrupt()?;\n        match next_token.kind() {\n            TokenKind::Keyword((Keyword::Catch | Keyword::Finally, true)) => {\n                return Err(Error::general(\n                    \"Keyword must not contain escaped characters\",\n                    next_token.span().start(),\n                ));\n            }\n            TokenKind::Keyword((Keyword::Catch | Keyword::Finally, false)) => {}\n            _ => {\n                return Err(Error::expected(\n                    [\"catch\".to_owned(), \"finally\".to_owned()],\n                    next_token.to_string(interner),\n                    next_token.span(),\n                    \"try statement\",\n                ));\n            }\n        }\n\n        let catch = if next_token.kind() == &TokenKind::Keyword((Keyword::Catch, false)) {\n            Some(\n                Catch::new(self.allow_yield, self.allow_await, self.allow_return)\n                    .parse(cursor, interner)?,\n            )\n        } else {\n            None\n        };\n\n        let next_token = cursor.peek(0, interner)?;\n        let finally = if let Some(token) = next_token {\n            match token.kind() {\n                TokenKind::Keyword((Keyword::Finally, true)) => {\n                    return Err(Error::general(\n                        \"Keyword must not contain escaped characters\",\n                        token.span().start(),\n                    ));\n                }\n                TokenKind::Keyword((Keyword::Finally, false)) => Some(\n                    Finally::new(self.allow_yield, self.allow_await, self.allow_return)\n                        .parse(cursor, interner)?,\n                ),\n                _ => None,\n            }\n        } else {\n            None\n        };\n\n        let handler = match (catch, finally) {\n            (Some(catch), None) => ErrorHandler::Catch(catch),\n            (None, Some(finally)) => ErrorHandler::Finally(finally),\n            (Some(catch), Some(finally)) => ErrorHandler::Full(catch, finally),\n            (None, None) => unreachable!(),\n        };\n\n        Ok(Try::new(try_clause, handler))\n    }\n}\n"
  },
  {
    "path": "core/parser/src/parser/statement/try_stm/tests.rs",
    "content": "use crate::parser::tests::{check_invalid_script, check_script_parser};\nuse boa_ast::{\n    Span, Statement, StatementListItem,\n    declaration::{VarDeclaration, Variable},\n    expression::{Identifier, literal::Literal},\n    pattern::{ArrayPattern, ArrayPatternElement, ObjectPattern, ObjectPatternElement, Pattern},\n    statement::{Block, Catch, ErrorHandler, Finally, Try},\n};\nuse boa_interner::Interner;\nuse boa_macros::utf16;\n\nconst PSEUDO_LINEAR_POS: boa_ast::LinearPosition = boa_ast::LinearPosition::new(0);\n\n#[test]\nfn check_inline_with_empty_try_catch() {\n    let interner = &mut Interner::default();\n    check_script_parser(\n        \"try { } catch(e) {}\",\n        vec![\n            Statement::Try(Try::new(\n                Block::default(),\n                ErrorHandler::Catch(Catch::new(\n                    Some(\n                        Identifier::new(\n                            interner.get_or_intern_static(\"e\", utf16!(\"e\")),\n                            Span::new((1, 15), (1, 16)),\n                        )\n                        .into(),\n                    ),\n                    Block::default(),\n                )),\n            ))\n            .into(),\n        ],\n        interner,\n    );\n}\n\n#[test]\nfn check_inline_with_var_decl_inside_try() {\n    let interner = &mut Interner::default();\n    check_script_parser(\n        \"try { var x = 1; } catch(e) {}\",\n        vec![\n            Statement::Try(Try::new(\n                (\n                    vec![\n                        Statement::Var(VarDeclaration(\n                            vec![Variable::from_identifier(\n                                Identifier::new(\n                                    interner.get_or_intern_static(\"x\", utf16!(\"x\")),\n                                    Span::new((1, 11), (1, 12)),\n                                ),\n                                Some(Literal::new(1, Span::new((1, 15), (1, 16))).into()),\n                            )]\n                            .try_into()\n                            .unwrap(),\n                        ))\n                        .into(),\n                    ],\n                    PSEUDO_LINEAR_POS,\n                )\n                    .into(),\n                ErrorHandler::Catch(Catch::new(\n                    Some(\n                        Identifier::new(\n                            interner.get_or_intern_static(\"e\", utf16!(\"e\")),\n                            Span::new((1, 26), (1, 27)),\n                        )\n                        .into(),\n                    ),\n                    Block::default(),\n                )),\n            ))\n            .into(),\n        ],\n        interner,\n    );\n}\n\n#[test]\nfn check_inline_with_var_decl_inside_catch() {\n    let interner = &mut Interner::default();\n    check_script_parser(\n        \"try { var x = 1; } catch(e) { var x = 1; }\",\n        vec![\n            Statement::Try(Try::new(\n                (\n                    vec![\n                        Statement::Var(VarDeclaration(\n                            vec![Variable::from_identifier(\n                                Identifier::new(\n                                    interner.get_or_intern_static(\"x\", utf16!(\"x\")),\n                                    Span::new((1, 11), (1, 12)),\n                                ),\n                                Some(Literal::new(1, Span::new((1, 15), (1, 16))).into()),\n                            )]\n                            .try_into()\n                            .unwrap(),\n                        ))\n                        .into(),\n                    ],\n                    PSEUDO_LINEAR_POS,\n                )\n                    .into(),\n                ErrorHandler::Catch(Catch::new(\n                    Some(\n                        Identifier::new(\n                            interner.get_or_intern_static(\"e\", utf16!(\"e\")),\n                            Span::new((1, 26), (1, 27)),\n                        )\n                        .into(),\n                    ),\n                    (\n                        vec![\n                            Statement::Var(VarDeclaration(\n                                vec![Variable::from_identifier(\n                                    Identifier::new(\n                                        interner.get_or_intern_static(\"x\", utf16!(\"x\")),\n                                        Span::new((1, 35), (1, 36)),\n                                    ),\n                                    Some(Literal::new(1, Span::new((1, 39), (1, 40))).into()),\n                                )]\n                                .try_into()\n                                .unwrap(),\n                            ))\n                            .into(),\n                        ],\n                        PSEUDO_LINEAR_POS,\n                    )\n                        .into(),\n                )),\n            ))\n            .into(),\n        ],\n        interner,\n    );\n}\n\n#[test]\nfn check_inline_with_empty_try_catch_finally() {\n    let interner = &mut Interner::default();\n    check_script_parser(\n        \"try {} catch(e) {} finally {}\",\n        vec![\n            Statement::Try(Try::new(\n                Block::default(),\n                ErrorHandler::Full(\n                    Catch::new(\n                        Some(\n                            Identifier::new(\n                                interner.get_or_intern_static(\"e\", utf16!(\"e\")),\n                                Span::new((1, 14), (1, 15)),\n                            )\n                            .into(),\n                        ),\n                        Block::default(),\n                    ),\n                    Finally::from(Block::default()),\n                ),\n            ))\n            .into(),\n        ],\n        interner,\n    );\n}\n\n#[test]\nfn check_inline_with_empty_try_finally() {\n    check_script_parser(\n        \"try {} finally {}\",\n        vec![\n            Statement::Try(Try::new(\n                Block::default(),\n                ErrorHandler::Finally(Finally::from(Block::default())),\n            ))\n            .into(),\n        ],\n        &mut Interner::default(),\n    );\n}\n\n#[test]\nfn check_inline_with_empty_try_var_decl_in_finally() {\n    let interner = &mut Interner::default();\n    check_script_parser(\n        \"try {} finally { var x = 1; }\",\n        vec![\n            Statement::Try(Try::new(\n                Block::default(),\n                ErrorHandler::Finally(Finally::from(Block::from((\n                    vec![StatementListItem::Statement(\n                        Statement::Var(VarDeclaration(\n                            vec![Variable::from_identifier(\n                                Identifier::new(\n                                    interner.get_or_intern_static(\"x\", utf16!(\"x\")),\n                                    Span::new((1, 22), (1, 23)),\n                                ),\n                                Some(Literal::new(1, Span::new((1, 26), (1, 27))).into()),\n                            )]\n                            .try_into()\n                            .unwrap(),\n                        ))\n                        .into(),\n                    )],\n                    PSEUDO_LINEAR_POS,\n                )))),\n            ))\n            .into(),\n        ],\n        interner,\n    );\n}\n\n#[test]\nfn check_inline_empty_try_paramless_catch() {\n    let interner = &mut Interner::default();\n    check_script_parser(\n        \"try {} catch { var x = 1; }\",\n        vec![\n            Statement::Try(Try::new(\n                Block::default(),\n                ErrorHandler::Catch(Catch::new(\n                    None,\n                    (\n                        vec![\n                            Statement::Var(VarDeclaration(\n                                vec![Variable::from_identifier(\n                                    Identifier::new(\n                                        interner.get_or_intern_static(\"x\", utf16!(\"x\")),\n                                        Span::new((1, 20), (1, 21)),\n                                    ),\n                                    Some(Literal::new(1, Span::new((1, 24), (1, 25))).into()),\n                                )]\n                                .try_into()\n                                .unwrap(),\n                            ))\n                            .into(),\n                        ],\n                        PSEUDO_LINEAR_POS,\n                    )\n                        .into(),\n                )),\n            ))\n            .into(),\n        ],\n        interner,\n    );\n}\n\n#[test]\nfn check_inline_with_binding_pattern_object() {\n    let interner = &mut Interner::default();\n    let a = interner.get_or_intern_static(\"a\", utf16!(\"a\"));\n    check_script_parser(\n        \"try {} catch ({ a, b: c }) {}\",\n        vec![\n            Statement::Try(Try::new(\n                Block::default(),\n                ErrorHandler::Catch(Catch::new(\n                    Some(\n                        Pattern::from(ObjectPattern::new(\n                            vec![\n                                ObjectPatternElement::SingleName {\n                                    ident: Identifier::new(a, Span::new((1, 17), (1, 18))),\n                                    name: Identifier::new(a, Span::new((1, 17), (1, 18))).into(),\n                                    default_init: None,\n                                },\n                                ObjectPatternElement::SingleName {\n                                    ident: Identifier::new(\n                                        interner.get_or_intern_static(\"c\", utf16!(\"c\")),\n                                        Span::new((1, 23), (1, 24)),\n                                    ),\n                                    name: Identifier::new(\n                                        interner.get_or_intern_static(\"b\", utf16!(\"b\")),\n                                        Span::new((1, 20), (1, 21)),\n                                    )\n                                    .into(),\n                                    default_init: None,\n                                },\n                            ]\n                            .into(),\n                            Span::new((1, 15), (1, 26)),\n                        ))\n                        .into(),\n                    ),\n                    Block::default(),\n                )),\n            ))\n            .into(),\n        ],\n        interner,\n    );\n}\n\n#[test]\nfn check_inline_with_binding_pattern_array() {\n    let interner = &mut Interner::default();\n    check_script_parser(\n        \"try {} catch ([a, b]) {}\",\n        vec![\n            Statement::Try(Try::new(\n                Block::from((vec![], PSEUDO_LINEAR_POS)),\n                ErrorHandler::Catch(Catch::new(\n                    Some(\n                        Pattern::from(ArrayPattern::new(\n                            vec![\n                                ArrayPatternElement::SingleName {\n                                    ident: Identifier::new(\n                                        interner.get_or_intern_static(\"a\", utf16!(\"a\")),\n                                        Span::new((1, 16), (1, 17)),\n                                    ),\n                                    default_init: None,\n                                },\n                                ArrayPatternElement::SingleName {\n                                    ident: Identifier::new(\n                                        interner.get_or_intern_static(\"b\", utf16!(\"b\")),\n                                        Span::new((1, 19), (1, 20)),\n                                    ),\n                                    default_init: None,\n                                },\n                            ]\n                            .into(),\n                            Span::new((1, 15), (1, 21)),\n                        ))\n                        .into(),\n                    ),\n                    Block::default(),\n                )),\n            ))\n            .into(),\n        ],\n        interner,\n    );\n}\n\n#[test]\nfn check_catch_with_var_redeclaration() {\n    let interner = &mut Interner::default();\n    check_script_parser(\n        \"try {} catch(e) { var e = 'oh' }\",\n        vec![\n            Statement::Try(Try::new(\n                Block::from((vec![], PSEUDO_LINEAR_POS)),\n                ErrorHandler::Catch(Catch::new(\n                    Some(\n                        Identifier::new(\n                            interner.get_or_intern_static(\"e\", utf16!(\"e\")),\n                            Span::new((1, 14), (1, 15)),\n                        )\n                        .into(),\n                    ),\n                    (\n                        vec![\n                            Statement::Var(VarDeclaration(\n                                vec![Variable::from_identifier(\n                                    Identifier::new(\n                                        interner.get_or_intern_static(\"e\", utf16!(\"e\")),\n                                        Span::new((1, 23), (1, 24)),\n                                    ),\n                                    Some(\n                                        Literal::new(\n                                            interner.get_or_intern_static(\"oh\", utf16!(\"oh\")),\n                                            Span::new((1, 27), (1, 31)),\n                                        )\n                                        .into(),\n                                    ),\n                                )]\n                                .try_into()\n                                .unwrap(),\n                            ))\n                            .into(),\n                        ],\n                        PSEUDO_LINEAR_POS,\n                    )\n                        .into(),\n                )),\n            ))\n            .into(),\n        ],\n        interner,\n    );\n}\n\n#[test]\nfn check_inline_invalid_catch() {\n    check_invalid_script(\"try {} catch\");\n}\n\n#[test]\nfn check_inline_invalid_catch_without_closing_paren() {\n    check_invalid_script(\"try {} catch(e {}\");\n}\n\n#[test]\nfn check_inline_invalid_catch_parameter() {\n    check_invalid_script(\"try {} catch(1) {}\");\n}\n\n#[test]\nfn check_invalid_try_no_catch_finally() {\n    check_invalid_script(\"try {} let a = 10;\");\n}\n\n#[test]\nfn check_invalid_catch_with_empty_paren() {\n    check_invalid_script(\"try {} catch() {}\");\n}\n\n#[test]\nfn check_invalid_catch_with_duplicate_params() {\n    check_invalid_script(\"try {} catch({ a, b: a }) {}\");\n}\n\n#[test]\nfn check_invalid_catch_with_lexical_redeclaration() {\n    check_invalid_script(\"try {} catch(e) { let e = 'oh' }\");\n}\n"
  },
  {
    "path": "core/parser/src/parser/statement/variable/mod.rs",
    "content": "//! Variable statement parsing.\n\nuse crate::{\n    lexer::TokenKind,\n    parser::{\n        AllowAwait, AllowIn, AllowYield, OrAbrupt, ParseResult, TokenParser,\n        cursor::Cursor,\n        expression::Initializer,\n        statement::{ArrayBindingPattern, BindingIdentifier, ObjectBindingPattern},\n    },\n    source::ReadChar,\n};\nuse boa_ast::{\n    Keyword, Punctuator,\n    declaration::{VarDeclaration, Variable},\n};\nuse boa_interner::Interner;\nuse std::convert::TryInto;\n\n/// Variable statement parsing.\n///\n/// A variable statement contains the `var` keyword.\n///\n/// More information:\n///  - [MDN documentation][mdn]\n///  - [ECMAScript specification][spec]\n///\n/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/var\n/// [spec]: https://tc39.es/ecma262/#prod-VariableStatement\n#[derive(Debug, Clone, Copy)]\npub(in crate::parser::statement) struct VariableStatement {\n    allow_yield: AllowYield,\n    allow_await: AllowAwait,\n}\n\nimpl VariableStatement {\n    /// Creates a new `VariableStatement` parser.\n    pub(in crate::parser::statement) fn new<Y, A>(allow_yield: Y, allow_await: A) -> Self\n    where\n        Y: Into<AllowYield>,\n        A: Into<AllowAwait>,\n    {\n        Self {\n            allow_yield: allow_yield.into(),\n            allow_await: allow_await.into(),\n        }\n    }\n}\n\nimpl<R> TokenParser<R> for VariableStatement\nwhere\n    R: ReadChar,\n{\n    type Output = VarDeclaration;\n\n    fn parse(self, cursor: &mut Cursor<R>, interner: &mut Interner) -> ParseResult<Self::Output> {\n        cursor.expect((Keyword::Var, false), \"variable statement\", interner)?;\n\n        let decl_list = VariableDeclarationList::new(true, self.allow_yield, self.allow_await)\n            .parse(cursor, interner)?;\n\n        cursor.expect_semicolon(\"variable statement\", interner)?;\n\n        Ok(decl_list)\n    }\n}\n\n/// Variable declaration list parsing.\n///\n/// More information:\n///  - [MDN documentation][mdn]\n///  - [ECMAScript specification][spec]\n///\n/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/var\n/// [spec]: https://tc39.es/ecma262/#prod-VariableDeclarationList\n#[derive(Debug, Clone, Copy)]\npub(in crate::parser::statement) struct VariableDeclarationList {\n    allow_in: AllowIn,\n    allow_yield: AllowYield,\n    allow_await: AllowAwait,\n}\n\nimpl VariableDeclarationList {\n    /// Creates a new `VariableDeclarationList` parser.\n    pub(in crate::parser::statement) fn new<I, Y, A>(\n        allow_in: I,\n        allow_yield: Y,\n        allow_await: A,\n    ) -> Self\n    where\n        I: Into<AllowIn>,\n        Y: Into<AllowYield>,\n        A: Into<AllowAwait>,\n    {\n        Self {\n            allow_in: allow_in.into(),\n            allow_yield: allow_yield.into(),\n            allow_await: allow_await.into(),\n        }\n    }\n}\n\nimpl<R> TokenParser<R> for VariableDeclarationList\nwhere\n    R: ReadChar,\n{\n    type Output = VarDeclaration;\n\n    fn parse(self, cursor: &mut Cursor<R>, interner: &mut Interner) -> ParseResult<Self::Output> {\n        let mut list = Vec::new();\n\n        loop {\n            list.push(\n                VariableDeclaration::new(self.allow_in, self.allow_yield, self.allow_await)\n                    .parse(cursor, interner)?,\n            );\n\n            if cursor.next_if(Punctuator::Comma, interner)?.is_none() {\n                break;\n            }\n        }\n\n        Ok(VarDeclaration(list.try_into().expect(\n            \"`VariableDeclaration` must parse at least one variable\",\n        )))\n    }\n}\n\n/// Reads an individual variable declaration.\n///\n/// More information:\n///  - [ECMAScript specification][spec]\n///\n/// [spec]: https://tc39.es/ecma262/#prod-VariableDeclaration\n#[derive(Debug, Clone, Copy)]\nstruct VariableDeclaration {\n    allow_in: AllowIn,\n    allow_yield: AllowYield,\n    allow_await: AllowAwait,\n}\n\nimpl VariableDeclaration {\n    /// Creates a new `VariableDeclaration` parser.\n    fn new<I, Y, A>(allow_in: I, allow_yield: Y, allow_await: A) -> Self\n    where\n        I: Into<AllowIn>,\n        Y: Into<AllowYield>,\n        A: Into<AllowAwait>,\n    {\n        Self {\n            allow_in: allow_in.into(),\n            allow_yield: allow_yield.into(),\n            allow_await: allow_await.into(),\n        }\n    }\n}\n\nimpl<R> TokenParser<R> for VariableDeclaration\nwhere\n    R: ReadChar,\n{\n    type Output = Variable;\n\n    fn parse(self, cursor: &mut Cursor<R>, interner: &mut Interner) -> ParseResult<Self::Output> {\n        let peek_token = cursor.peek(0, interner).or_abrupt()?;\n\n        match peek_token.kind() {\n            TokenKind::Punctuator(Punctuator::OpenBlock) => {\n                let bindings = ObjectBindingPattern::new(self.allow_yield, self.allow_await)\n                    .parse(cursor, interner)?;\n\n                let init = if cursor\n                    .peek(0, interner)?\n                    .filter(|t| *t.kind() == TokenKind::Punctuator(Punctuator::Assign))\n                    .is_some()\n                {\n                    Some(\n                        Initializer::new(self.allow_in, self.allow_yield, self.allow_await)\n                            .parse(cursor, interner)?,\n                    )\n                } else {\n                    None\n                };\n\n                Ok(Variable::from_pattern(bindings.into(), init))\n            }\n            TokenKind::Punctuator(Punctuator::OpenBracket) => {\n                let bindings = ArrayBindingPattern::new(self.allow_yield, self.allow_await)\n                    .parse(cursor, interner)?;\n\n                let init = if cursor\n                    .peek(0, interner)?\n                    .filter(|t| *t.kind() == TokenKind::Punctuator(Punctuator::Assign))\n                    .is_some()\n                {\n                    Some(\n                        Initializer::new(self.allow_in, self.allow_yield, self.allow_await)\n                            .parse(cursor, interner)?,\n                    )\n                } else {\n                    None\n                };\n\n                Ok(Variable::from_pattern(bindings.into(), init))\n            }\n            _ => {\n                let ident = BindingIdentifier::new(self.allow_yield, self.allow_await)\n                    .parse(cursor, interner)?;\n\n                let init = if cursor\n                    .peek(0, interner)?\n                    .filter(|t| *t.kind() == TokenKind::Punctuator(Punctuator::Assign))\n                    .is_some()\n                {\n                    let mut init =\n                        Initializer::new(self.allow_in, self.allow_yield, self.allow_await)\n                            .parse(cursor, interner)?;\n                    init.set_anonymous_function_definition_name(&ident);\n                    Some(init)\n                } else {\n                    None\n                };\n                Ok(Variable::from_identifier(ident, init))\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "core/parser/src/parser/statement/with/mod.rs",
    "content": "//! With statement parsing.\n\nuse crate::{\n    Error,\n    parser::{\n        AllowAwait, AllowReturn, AllowYield, ParseResult, TokenParser, cursor::Cursor,\n        expression::Expression, statement::Statement,\n    },\n    source::ReadChar,\n};\nuse boa_ast::{Keyword, Punctuator, Spanned, statement::With};\nuse boa_interner::Interner;\n\n/// With statement parsing.\n///\n/// More information:\n///  - [MDN documentation][mdn]\n///  - [ECMAScript specification][spec]\n///\n/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/with\n/// [spec]: https://tc39.es/ecma262/#prod-WithStatement\n#[derive(Debug, Clone, Copy)]\npub(in crate::parser::statement) struct WithStatement {\n    allow_yield: AllowYield,\n    allow_await: AllowAwait,\n    allow_return: AllowReturn,\n}\n\nimpl WithStatement {\n    /// Creates a new `WithStatement` parser.\n    pub(in crate::parser::statement) fn new<Y, A, R>(\n        allow_yield: Y,\n        allow_await: A,\n        allow_return: R,\n    ) -> Self\n    where\n        Y: Into<AllowYield>,\n        A: Into<AllowAwait>,\n        R: Into<AllowReturn>,\n    {\n        Self {\n            allow_yield: allow_yield.into(),\n            allow_await: allow_await.into(),\n            allow_return: allow_return.into(),\n        }\n    }\n}\n\nimpl<R> TokenParser<R> for WithStatement\nwhere\n    R: ReadChar,\n{\n    type Output = With;\n\n    fn parse(self, cursor: &mut Cursor<R>, interner: &mut Interner) -> ParseResult<Self::Output> {\n        let position = cursor\n            .expect((Keyword::With, false), \"with statement\", interner)?\n            .span()\n            .start();\n\n        // It is a Syntax Error if the source text matched by this production is contained in strict mode code.\n        if cursor.strict() {\n            return Err(Error::general(\n                \"with statement not allowed in strict mode\",\n                position,\n            ));\n        }\n\n        cursor.expect(Punctuator::OpenParen, \"with statement\", interner)?;\n        let expression =\n            Expression::new(true, self.allow_yield, self.allow_await).parse(cursor, interner)?;\n        let position = cursor\n            .expect(Punctuator::CloseParen, \"with statement\", interner)?\n            .span()\n            .end();\n        let statement = Statement::new(self.allow_yield, self.allow_await, self.allow_return)\n            .parse(cursor, interner)?;\n\n        // It is a Syntax Error if IsLabelledFunction(Statement) is true.\n        if statement.is_labelled_function() {\n            return Err(Error::wrong_labelled_function_declaration(position));\n        }\n\n        Ok(With::new(expression, statement))\n    }\n}\n"
  },
  {
    "path": "core/parser/src/parser/tests/format/declaration.rs",
    "content": "use crate::parser::tests::format::test_formatting;\n\n#[test]\nfn binding_pattern() {\n    test_formatting(\n        r#\"\n        var { } = {\n            o: \"1\",\n        };\n        var { o_v1 } = {\n            o_v1: \"1\",\n        };\n        var { o_v2 = \"1\" } = {\n            o_v2: \"2\",\n        };\n        var { a : o_v3 = \"1\" } = {\n            a: \"2\",\n        };\n        var { ... o_rest_v1 } = {\n            a: \"2\",\n        };\n        var { o_v4, o_v5, o_v6 = \"1\", a : o_v7 = \"1\", ... o_rest_v2 } = {\n            o_v4: \"1\",\n            o_v5: \"1\",\n        };\n        var [] = [];\n        var [ , ] = [];\n        var [ a_v1 ] = [1, 2, 3];\n        var [ a_v2, a_v3 ] = [1, 2, 3];\n        var [ a_v2, , a_v3 ] = [1, 2, 3];\n        var [ ... a_rest_v1 ] = [1, 2, 3];\n        var [ a_v4, , ... a_rest_v2 ] = [1, 2, 3];\n        var [ { a_v5 } ] = [{\n            a_v5: 1,\n        }, {\n            a_v5: 2,\n        }, {\n            a_v5: 3,\n        }];\n        \"#,\n    );\n}\n"
  },
  {
    "path": "core/parser/src/parser/tests/format/expression.rs",
    "content": "use crate::parser::tests::format::test_formatting;\n\n#[test]\nfn new() {\n    test_formatting(\n        r#\"\n        function MyClass() {}\n        let inst = new MyClass();\n        \"#,\n    );\n}\n\n#[test]\nfn call() {\n    test_formatting(\n        r#\"\n        call_1(1, 2, 3);\n        call_2(\"argument here\");\n        call_3();\n        \"#,\n    );\n}\n\n#[test]\nfn assign() {\n    test_formatting(\n        r#\"\n        let a = 20;\n        a += 10;\n        a -= 10;\n        a *= 10;\n        a **= 10;\n        a /= 10;\n        a %= 10;\n        a &= 10;\n        a |= 10;\n        a ^= 10;\n        a <<= 10;\n        a >>= 10;\n        a >>>= 10;\n        a &&= 10;\n        a ||= 10;\n        a ??= 10;\n        a;\n        \"#,\n    );\n}\n\n#[test]\nfn spread() {\n    test_formatting(\n        r#\"\n        function f(m) {\n            return m;\n        }\n        function g(...args) {\n            return f(...args);\n        }\n        let a = g(\"message\");\n        a;\n        \"#,\n    );\n}\n\n#[test]\nfn r#await() {\n    // TODO: `let a = await fn()` is invalid syntax as of writing. It should be tested here once implemented.\n    test_formatting(\n        r#\"\n            async function f() {\n                await function_call();\n            }\n            \"#,\n    );\n}\n\n#[test]\nfn array() {\n    test_formatting(\n        r#\"\n            let a = [1, 2, 3, \"words\", \"more words\"];\n            let b = [];\n            \"#,\n    );\n}\n\n#[test]\nfn template() {\n    test_formatting(\n        r\"\n        function tag(t, ...args) {\n            let a = [];\n            a = a.concat([t[0], t[1], t[2]]);\n            a = a.concat([t.raw[0], t.raw[1], t.raw[2]]);\n            a = a.concat([args[0], args[1]]);\n            return a;\n        }\n        let a = 10;\n        tag`result: ${a} \\x26 ${a + 10}`;\n        \",\n    );\n}\n\n#[test]\nfn object() {\n    test_formatting(\n        r#\"\n        let other = {\n            c: 10,\n        };\n        let inst = {\n            val: 5,\n            b: \"hello world\",\n            nested: {\n                a: 5,\n                b: 6,\n            },\n            ...other,\n            say_hi: function() {\n                console.log(\"hello!\");\n            },\n            get a() {\n                return this.val + 1;\n            },\n            set a(new_value) {\n                this.val = new_value;\n            },\n            say_hello(msg) {\n                console.log(\"hello \" + msg);\n            },\n        };\n        inst.a = 20;\n        inst.a;\n        inst.say_hello(\"humans\");\n        \"#,\n    );\n}\n\n#[test]\nfn array_literal_empty() {\n    test_formatting(\n        r\"\n        [];\n        \",\n    );\n}\n\n#[test]\nfn array_literal_values() {\n    test_formatting(\n        r#\"\n        [0, 1, \"a\", this, null, undefined, true, false];\n        \"#,\n    );\n}\n\n#[test]\nfn array_literal_elision() {\n    test_formatting(\n        r\"\n        [, , ,];\n        \",\n    );\n}\n\n#[test]\nfn array_literal_elision_values() {\n    test_formatting(\n        r\"\n        [1, 2, , 3, 4];\n        \",\n    );\n}\n\n#[test]\nfn array_literal_elision_start_end() {\n    test_formatting(\n        r\"\n        [, , 1, 2, , 3, 4, ,];\n        \",\n    );\n}\n"
  },
  {
    "path": "core/parser/src/parser/tests/format/function/class.rs",
    "content": "use crate::parser::tests::format::test_formatting;\n\n#[test]\nfn class_declaration_empty() {\n    test_formatting(\n        r#\"\n        class A {}\n        \"#,\n    );\n}\n\n#[test]\nfn class_declaration_empty_extends() {\n    test_formatting(\n        r#\"\n        class A extends Object {}\n        \"#,\n    );\n}\n\n#[test]\nfn class_declaration_constructor() {\n    test_formatting(\n        r#\"\n        class A {\n            constructor(a, b, c) {\n                this.value = a + b + c;\n            }\n        }\n        \"#,\n    );\n}\n\n#[test]\nfn class_declaration_elements() {\n    test_formatting(\n        r#\"\n        class A {\n            a;\n            b = 1;\n            c() {}\n            d(a, b, c) {\n                return a + b + c;\n            }\n            set e(value) {}\n            get e() {}\n            set(a, b) {}\n            get(a, b) {}\n        }\n        \"#,\n    );\n}\n\n#[test]\nfn class_declaration_elements_private() {\n    test_formatting(\n        r#\"\n        class A {\n            #a;\n            #b = 1;\n            #c() {}\n            #d(a, b, c) {\n                return a + b + c;\n            }\n            set #e(value) {}\n            get #e() {}\n        }\n        \"#,\n    );\n}\n\n#[test]\nfn class_declaration_elements_static() {\n    test_formatting(\n        r#\"\n        class A {\n            static a;\n            static b = 1;\n            static c() {}\n            static d(a, b, c) {\n                return a + b + c;\n            }\n            static set e(value) {}\n            static get e() {}\n        }\n        \"#,\n    );\n}\n\n#[test]\nfn class_declaration_elements_private_static() {\n    test_formatting(\n        r#\"\n        class A {\n            static #a;\n            static #b = 1;\n            static #c() {}\n            static #d(a, b, c) {\n                return a + b + c;\n            }\n            static set #e(value) {}\n            static get #e() {}\n        }\n        \"#,\n    );\n}\n\n// https://github.com/boa-dev/boa/issues/4605\n#[test]\nfn class_declaration_boolean_literal_method_names() {\n    test_formatting(\n        r#\"\n        class A {\n            true() {}\n            false() {}\n            null() {}\n            get true() {}\n            set true(value) {}\n            get false() {}\n            set false(value) {}\n            static true() {}\n            static false() {}\n        }\n        \"#,\n    );\n}\n"
  },
  {
    "path": "core/parser/src/parser/tests/format/function/mod.rs",
    "content": "use crate::parser::tests::format::test_formatting;\n\nmod class;\n\n#[test]\nfn function() {\n    test_formatting(\n        r#\"\n        function func(a, b) {\n            console.log(a);\n        }\n        function func_2(a, b) {}\n        pass_func(function(a, b) {\n            console.log(\"in callback\", a);\n        });\n        pass_func(function(a, b) {});\n        \"#,\n    );\n}\n\n#[test]\nfn arrow() {\n    test_formatting(\n        r#\"\n        let arrow_func = (a, b) => {\n            console.log(\"in multi statement arrow\");\n            console.log(b);\n        };\n        let arrow_func_2 = (a, b) => {};\n        \"#,\n    );\n}\n\n#[test]\nfn r#async() {\n    test_formatting(\n        r#\"\n            async function async_func(a, b) {\n                console.log(a);\n            }\n            async function async_func_2(a, b) {}\n            pass_async_func(async function(a, b) {\n                console.log(\"in async callback\", a);\n            });\n            pass_async_func(async function(a, b) {});\n            \"#,\n    );\n}\n"
  },
  {
    "path": "core/parser/src/parser/tests/format/mod.rs",
    "content": "mod declaration;\nmod expression;\nmod function;\nmod statement;\n\n/// This parses the given source code, and then makes sure that\n/// the resulting `StatementList` is formatted in the same manner\n/// as the source code. This is expected to have a preceding\n/// newline.\n///\n/// This is a utility function for tests. It was made in case people\n/// are using different indents in their source files. This fixes\n/// any strings which may have been changed in a different indent\n/// level.\n#[cfg(test)]\nfn test_formatting(source: &'static str) {\n    // Remove preceding newline.\n\n    use crate::{Parser, Source};\n    use boa_ast::scope::Scope;\n    use boa_interner::{Interner, ToInternedString};\n    let source = &source[1..];\n\n    // Find out how much the code is indented\n    let first_line = &source[..source.find('\\n').unwrap()];\n    let trimmed_first_line = first_line.trim();\n    let characters_to_remove = first_line.len() - trimmed_first_line.len();\n\n    let scenario = source\n        .lines()\n        .map(|l| &l[characters_to_remove..]) // Remove preceding whitespace from each line\n        .collect::<Vec<&'static str>>()\n        .join(\"\\n\");\n    let source = Source::from_bytes(source);\n    let interner = &mut Interner::default();\n    let result = Parser::new(source)\n        .parse_script(&Scope::new_global(), interner)\n        .expect(\"parsing failed\")\n        .to_interned_string(interner);\n    if scenario != result {\n        eprint!(\"========= Expected:\\n{scenario}\");\n        eprint!(\"========= Got:\\n{result}\");\n        // Might be helpful to find differing whitespace\n        eprintln!(\"========= Expected: {scenario:?}\");\n        eprintln!(\"========= Got:      {result:?}\");\n        panic!(\"parsing test did not give the correct result (see above)\");\n    }\n}\n"
  },
  {
    "path": "core/parser/src/parser/tests/format/statement.rs",
    "content": "use crate::parser::tests::format::test_formatting;\n\n#[test]\nfn block() {\n    test_formatting(\n        r#\"\n        {\n            let a = function_call();\n            console.log(\"hello\");\n        }\n        another_statement();\n        \"#,\n    );\n    // TODO: Once block labels are implemented, this should be tested:\n    // super::super::test_formatting(\n    //     r#\"\n    //     block_name: {\n    //         let a = function_call();\n    //         console.log(\"hello\");\n    //     }\n    //     another_statement();\n    //     \"#,\n    // );\n}\n\n#[test]\nfn r#if() {\n    test_formatting(\n        r#\"\n        let a = true ? 5 : 6;\n        if (false) {\n            a = 10;\n        } else {\n            a = 20;\n        }\n        \"#,\n    );\n}\n\n#[test]\nfn r#return() {\n    test_formatting(\n        r#\"\n        function say_hello(msg) {\n            if (msg === \"\") {\n                return 0;\n            }\n            console.log(\"hello \" + msg);\n            return;\n        }\n        say_hello(\"\");\n        say_hello(\"world\");\n        \"#,\n    );\n}\n\n#[test]\nfn throw() {\n    test_formatting(\n        r#\"\n        try {\n            throw \"hello\";\n        } catch(e) {\n            console.log(e);\n        }\n        \"#,\n    );\n}\n\n#[test]\nfn r#try() {\n    test_formatting(\n        r#\"\n        try {\n            throw \"hello\";\n        } catch(e) {\n            console.log(e);\n        } finally {\n            console.log(\"things\");\n        }\n        try {\n            throw \"hello\";\n        } catch {\n            console.log(\"something went wrong\");\n        }\n        \"#,\n    );\n}\n\n#[test]\nfn switch() {\n    test_formatting(\n        r#\"\n        let a = 3;\n        let b = \"unknown\";\n        switch (a) {\n            case 0:\n                b = \"Mon\";\n                break;\n            case 1:\n                b = \"Tue\";\n                break;\n            case 2:\n                b = \"Wed\";\n                break;\n            case 3:\n                b = \"Thurs\";\n                break;\n            case 4:\n                b = \"Fri\";\n                break;\n            case 5:\n                b = \"Sat\";\n                break;\n            case 6:\n                b = \"Sun\";\n                break;\n            default:\n                b = \"Unknown\";\n        }\n        b;\n        \"#,\n    );\n}\n\n#[test]\nfn with() {\n    test_formatting(\n        r#\"\n        with (this) {\n            {\n            }\n        }\n        \"#,\n    );\n}\n"
  },
  {
    "path": "core/parser/src/parser/tests/mod.rs",
    "content": "//! Tests for the parser.\n\nmod format;\n\nuse std::convert::TryInto;\n\nuse crate::{Parser, Source};\nuse boa_ast::{\n    Expression, LinearPosition, LinearSpan, Module, ModuleItem, ModuleItemList, Script, Span,\n    Statement, StatementList, StatementListItem,\n    declaration::{Declaration, LexicalDeclaration, VarDeclaration, Variable},\n    expression::{\n        Call, Identifier, New, Parenthesized,\n        access::SimplePropertyAccess,\n        literal::{Literal, ObjectLiteral, PropertyDefinition},\n        operator::{\n            Assign, Binary, Update,\n            assign::AssignOp,\n            binary::{ArithmeticOp, BinaryOp, LogicalOp, RelationalOp},\n            update::{UpdateOp, UpdateTarget},\n        },\n    },\n    function::{\n        ArrowFunction, FormalParameter, FormalParameterList, FormalParameterListFlags,\n        FunctionBody, FunctionDeclaration,\n    },\n    scope::Scope,\n    statement::{Block, If, Return, With},\n};\nuse boa_interner::Interner;\nuse boa_macros::utf16;\nuse indoc::indoc;\n\nconst PSEUDO_LINEAR_POS: LinearPosition = LinearPosition::new(0);\nconst EMPTY_LINEAR_SPAN: LinearSpan = LinearSpan::new(PSEUDO_LINEAR_POS, PSEUDO_LINEAR_POS);\n\n/// Checks that the given JavaScript string gives the expected expression.\n#[track_caller]\npub(super) fn check_script_parser<L>(js: &str, expr: L, interner: &mut Interner)\nwhere\n    L: Into<Box<[StatementListItem]>>,\n{\n    let mut script = Script::new(StatementList::from((expr.into(), PSEUDO_LINEAR_POS)));\n    let scope = Scope::new_global();\n    script\n        .analyze_scope(&scope, interner)\n        .expect(\"failed to analyze script\");\n    assert_eq!(\n        Parser::new(Source::from_bytes(js))\n            .parse_script(&Scope::new_global(), interner)\n            .expect(\"failed to parse\"),\n        script,\n    );\n}\n\n/// Checks that the given JavaScript string gives the expected expression.\n#[track_caller]\npub(super) fn check_module_parser<L>(js: &str, expr: L, interner: &mut Interner)\nwhere\n    L: Into<Box<[ModuleItem]>>,\n{\n    let mut module = Module::new(ModuleItemList::from(expr.into()));\n    let scope = Scope::new_global();\n    module\n        .analyze_scope(&scope, interner)\n        .expect(\"failed to analyze\");\n    assert_eq!(\n        Parser::new(Source::from_bytes(js))\n            .parse_module(&Scope::new_global(), interner)\n            .expect(\"failed to parse\"),\n        module,\n    );\n}\n\n/// Checks that the given javascript string creates a parse error.\n#[track_caller]\npub(super) fn check_invalid_script(js: &str) {\n    assert!(\n        Parser::new(Source::from_bytes(js))\n            .parse_script(&Scope::new_global(), &mut Interner::default())\n            .is_err()\n    );\n}\n\n/// Should be parsed as `new Class().method()` instead of `new (Class().method())`\n#[test]\nfn check_construct_call_precedence() {\n    let interner = &mut Interner::default();\n    check_script_parser(\n        \"new Date().getTime()\",\n        vec![\n            Statement::Expression(\n                Call::new(\n                    Expression::PropertyAccess(\n                        SimplePropertyAccess::new(\n                            New::from(Call::new(\n                                Identifier::new(\n                                    interner.get_or_intern_static(\"Date\", utf16!(\"Date\")),\n                                    Span::new((1, 5), (1, 9)),\n                                )\n                                .into(),\n                                Box::default(),\n                                Span::new((1, 1), (1, 11)),\n                            ))\n                            .into(),\n                            Identifier::new(\n                                interner.get_or_intern_static(\"getTime\", utf16!(\"getTime\")),\n                                Span::new((1, 12), (1, 19)),\n                            ),\n                        )\n                        .into(),\n                    ),\n                    Box::default(),\n                    Span::new((1, 19), (1, 21)),\n                )\n                .into(),\n            )\n            .into(),\n        ],\n        interner,\n    );\n}\n\n#[test]\nfn assign_operator_precedence() {\n    let interner = &mut Interner::default();\n    check_script_parser(\n        \"a = a + 1\",\n        vec![\n            Statement::Expression(Expression::from(Assign::new(\n                AssignOp::Assign,\n                Identifier::new(\n                    interner.get_or_intern_static(\"a\", utf16!(\"a\")),\n                    Span::new((1, 1), (1, 2)),\n                )\n                .into(),\n                Binary::new(\n                    ArithmeticOp::Add.into(),\n                    Identifier::new(\n                        interner.get_or_intern_static(\"a\", utf16!(\"a\")),\n                        Span::new((1, 5), (1, 6)),\n                    )\n                    .into(),\n                    Literal::new(1, Span::new((1, 9), (1, 10))).into(),\n                )\n                .into(),\n            )))\n            .into(),\n        ],\n        interner,\n    );\n}\n\n#[test]\nfn hoisting() {\n    let interner = &mut Interner::default();\n    let hello = interner.get_or_intern_static(\"hello\", utf16!(\"hello\"));\n    let a = interner.get_or_intern_static(\"a\", utf16!(\"a\"));\n    check_script_parser(\n        indoc! {\"\n            var a = hello();\n            a++;\n\n            function hello() { return 10 }\n        \"},\n        vec![\n            Statement::Var(VarDeclaration(\n                vec![Variable::from_identifier(\n                    Identifier::new(a, Span::new((1, 5), (1, 6))),\n                    Some(\n                        Call::new(\n                            Identifier::new(hello, Span::new((1, 9), (1, 14))).into(),\n                            Box::default(),\n                            Span::new((1, 14), (1, 16)),\n                        )\n                        .into(),\n                    ),\n                )]\n                .try_into()\n                .unwrap(),\n            ))\n            .into(),\n            Statement::Expression(\n                Update::new(\n                    UpdateOp::IncrementPost,\n                    UpdateTarget::Identifier(Identifier::new(a, Span::new((2, 1), (2, 2)))),\n                    Span::new((2, 1), (2, 4)),\n                )\n                .into(),\n            )\n            .into(),\n            Declaration::FunctionDeclaration(FunctionDeclaration::new(\n                Identifier::new(hello, Span::new((4, 10), (4, 15))),\n                FormalParameterList::default(),\n                FunctionBody::new(\n                    StatementList::new(\n                        [Statement::Return(Return::new(Some(\n                            Literal::new(10, Span::new((4, 27), (4, 29))).into(),\n                        )))\n                        .into()],\n                        PSEUDO_LINEAR_POS,\n                        false,\n                    ),\n                    Span::new((4, 18), (4, 31)),\n                ),\n                EMPTY_LINEAR_SPAN,\n            ))\n            .into(),\n        ],\n        interner,\n    );\n\n    let interner = &mut Interner::default();\n    let a = interner.get_or_intern_static(\"a\", utf16!(\"a\"));\n    check_script_parser(\n        indoc! {\"\n            a = 10;\n            a++;\n\n            var a;\n        \"},\n        vec![\n            Statement::Expression(Expression::from(Assign::new(\n                AssignOp::Assign,\n                Identifier::new(a, Span::new((1, 1), (1, 2))).into(),\n                Literal::new(10, Span::new((1, 5), (1, 7))).into(),\n            )))\n            .into(),\n            Statement::Expression(\n                Update::new(\n                    UpdateOp::IncrementPost,\n                    UpdateTarget::Identifier(Identifier::new(a, Span::new((2, 1), (2, 2)))),\n                    Span::new((2, 1), (2, 4)),\n                )\n                .into(),\n            )\n            .into(),\n            Statement::Var(VarDeclaration(\n                vec![Variable::from_identifier(\n                    Identifier::new(a, Span::new((4, 5), (4, 6))),\n                    None,\n                )]\n                .try_into()\n                .unwrap(),\n            ))\n            .into(),\n        ],\n        interner,\n    );\n}\n\n#[test]\nfn ambiguous_regex_divide_expression() {\n    let s = \"1 / a === 1 / b\";\n\n    let interner = &mut Interner::default();\n    check_script_parser(\n        s,\n        vec![\n            Statement::Expression(Expression::from(Binary::new(\n                RelationalOp::StrictEqual.into(),\n                Binary::new(\n                    ArithmeticOp::Div.into(),\n                    Literal::new(1, Span::new((1, 1), (1, 2))).into(),\n                    Identifier::new(\n                        interner.get_or_intern_static(\"a\", utf16!(\"a\")),\n                        Span::new((1, 5), (1, 6)),\n                    )\n                    .into(),\n                )\n                .into(),\n                Binary::new(\n                    ArithmeticOp::Div.into(),\n                    Literal::new(1, Span::new((1, 11), (1, 12))).into(),\n                    Identifier::new(\n                        interner.get_or_intern_static(\"b\", utf16!(\"b\")),\n                        Span::new((1, 15), (1, 16)),\n                    )\n                    .into(),\n                )\n                .into(),\n            )))\n            .into(),\n        ],\n        interner,\n    );\n}\n\n#[test]\nfn two_divisions_in_expression() {\n    let s = \"a !== 0 || 1 / a === 1 / b;\";\n\n    let interner = &mut Interner::default();\n    let a = interner.get_or_intern_static(\"a\", utf16!(\"a\"));\n    check_script_parser(\n        s,\n        vec![\n            Statement::Expression(Expression::from(Binary::new(\n                LogicalOp::Or.into(),\n                Binary::new(\n                    RelationalOp::StrictNotEqual.into(),\n                    Identifier::new(a, Span::new((1, 1), (1, 2))).into(),\n                    Literal::new(0, Span::new((1, 7), (1, 8))).into(),\n                )\n                .into(),\n                Binary::new(\n                    RelationalOp::StrictEqual.into(),\n                    Binary::new(\n                        ArithmeticOp::Div.into(),\n                        Literal::new(1, Span::new((1, 12), (1, 13))).into(),\n                        Identifier::new(a, Span::new((1, 16), (1, 17))).into(),\n                    )\n                    .into(),\n                    Binary::new(\n                        ArithmeticOp::Div.into(),\n                        Literal::new(1, Span::new((1, 22), (1, 23))).into(),\n                        Identifier::new(\n                            interner.get_or_intern_static(\"b\", utf16!(\"b\")),\n                            Span::new((1, 26), (1, 27)),\n                        )\n                        .into(),\n                    )\n                    .into(),\n                )\n                .into(),\n            )))\n            .into(),\n        ],\n        interner,\n    );\n}\n\n#[test]\nfn comment_semi_colon_insertion() {\n    let s = indoc! {\"\n        let a = 10 // Comment\n        let b = 20;\n    \"};\n\n    let interner = &mut Interner::default();\n    check_script_parser(\n        s,\n        vec![\n            Declaration::Lexical(LexicalDeclaration::Let(\n                vec![Variable::from_identifier(\n                    Identifier::new(\n                        interner.get_or_intern_static(\"a\", utf16!(\"a\")),\n                        Span::new((1, 5), (1, 6)),\n                    ),\n                    Some(Literal::new(10, Span::new((1, 9), (1, 11))).into()),\n                )]\n                .try_into()\n                .unwrap(),\n            ))\n            .into(),\n            Declaration::Lexical(LexicalDeclaration::Let(\n                vec![Variable::from_identifier(\n                    Identifier::new(\n                        interner.get_or_intern_static(\"b\", utf16!(\"b\")),\n                        Span::new((2, 5), (2, 6)),\n                    ),\n                    Some(Literal::new(20, Span::new((2, 9), (2, 11))).into()),\n                )]\n                .try_into()\n                .unwrap(),\n            ))\n            .into(),\n        ],\n        interner,\n    );\n}\n\n#[test]\nfn multiline_comment_semi_colon_insertion() {\n    let s = indoc! {\"\n        let a = 10 /* Test\n        Multiline\n        Comment\n        */ let b = 20;\n    \"};\n\n    let interner = &mut Interner::default();\n    check_script_parser(\n        s,\n        vec![\n            Declaration::Lexical(LexicalDeclaration::Let(\n                vec![Variable::from_identifier(\n                    Identifier::new(\n                        interner.get_or_intern_static(\"a\", utf16!(\"a\")),\n                        Span::new((1, 5), (1, 6)),\n                    ),\n                    Some(Literal::new(10, Span::new((1, 9), (1, 11))).into()),\n                )]\n                .try_into()\n                .unwrap(),\n            ))\n            .into(),\n            Declaration::Lexical(LexicalDeclaration::Let(\n                vec![Variable::from_identifier(\n                    Identifier::new(\n                        interner.get_or_intern_static(\"b\", utf16!(\"b\")),\n                        Span::new((4, 8), (4, 9)),\n                    ),\n                    Some(Literal::new(20, Span::new((4, 12), (4, 14))).into()),\n                )]\n                .try_into()\n                .unwrap(),\n            ))\n            .into(),\n        ],\n        interner,\n    );\n}\n\n#[test]\nfn multiline_comment_no_lineterminator() {\n    let s = indoc! {\"\n        let a = 10; /* Test comment */ let b = 20;\n    \"};\n\n    let interner = &mut Interner::default();\n    check_script_parser(\n        s,\n        vec![\n            Declaration::Lexical(LexicalDeclaration::Let(\n                vec![Variable::from_identifier(\n                    Identifier::new(\n                        interner.get_or_intern_static(\"a\", utf16!(\"a\")),\n                        Span::new((1, 5), (1, 6)),\n                    ),\n                    Some(Literal::new(10, Span::new((1, 9), (1, 11))).into()),\n                )]\n                .try_into()\n                .unwrap(),\n            ))\n            .into(),\n            Declaration::Lexical(LexicalDeclaration::Let(\n                vec![Variable::from_identifier(\n                    Identifier::new(\n                        interner.get_or_intern_static(\"b\", utf16!(\"b\")),\n                        Span::new((1, 36), (1, 37)),\n                    ),\n                    Some(Literal::new(20, Span::new((1, 40), (1, 42))).into()),\n                )]\n                .try_into()\n                .unwrap(),\n            ))\n            .into(),\n        ],\n        interner,\n    );\n}\n\n#[test]\nfn assignment_line_terminator() {\n    let s = indoc! {\"\n        let a = 3;\n\n        a =\n        5;\n    \"};\n\n    let interner = &mut Interner::default();\n    check_script_parser(\n        s,\n        vec![\n            Declaration::Lexical(LexicalDeclaration::Let(\n                vec![Variable::from_identifier(\n                    Identifier::new(\n                        interner.get_or_intern_static(\"a\", utf16!(\"a\")),\n                        Span::new((1, 5), (1, 6)),\n                    ),\n                    Some(Literal::new(3, Span::new((1, 9), (1, 10))).into()),\n                )]\n                .try_into()\n                .unwrap(),\n            ))\n            .into(),\n            Statement::Expression(Expression::from(Assign::new(\n                AssignOp::Assign,\n                Identifier::new(\n                    interner.get_or_intern_static(\"a\", utf16!(\"a\")),\n                    Span::new((3, 1), (3, 2)),\n                )\n                .into(),\n                Literal::new(5, Span::new((4, 1), (4, 2))).into(),\n            )))\n            .into(),\n        ],\n        interner,\n    );\n}\n\n#[test]\nfn assignment_multiline_terminator() {\n    let s = indoc! {\"\n        let a = 3;\n\n\n        a =\n\n\n        5;\n    \"};\n\n    let interner = &mut Interner::default();\n    let a = interner.get_or_intern_static(\"a\", utf16!(\"a\"));\n    check_script_parser(\n        s,\n        vec![\n            Declaration::Lexical(LexicalDeclaration::Let(\n                vec![Variable::from_identifier(\n                    Identifier::new(a, Span::new((1, 5), (1, 6))),\n                    Some(Literal::new(3, Span::new((1, 9), (1, 10))).into()),\n                )]\n                .try_into()\n                .unwrap(),\n            ))\n            .into(),\n            Statement::Expression(Expression::from(Assign::new(\n                AssignOp::Assign,\n                Identifier::new(a, Span::new((4, 1), (4, 2))).into(),\n                Literal::new(5, Span::new((7, 1), (7, 2))).into(),\n            )))\n            .into(),\n        ],\n        interner,\n    );\n}\n\n#[test]\nfn bracketed_expr() {\n    let s = \"(b)\";\n\n    let interner = &mut Interner::default();\n    check_script_parser(\n        s,\n        vec![\n            Statement::Expression(\n                Parenthesized::new(\n                    Identifier::new(\n                        interner.get_or_intern_static(\"b\", utf16!(\"b\")),\n                        Span::new((1, 2), (1, 3)),\n                    )\n                    .into(),\n                    Span::new((1, 1), (1, 4)),\n                )\n                .into(),\n            )\n            .into(),\n        ],\n        interner,\n    );\n}\n\n#[test]\nfn increment_in_comma_op() {\n    let s = \"(b++, b)\";\n\n    let interner = &mut Interner::default();\n    let b = interner.get_or_intern_static(\"b\", utf16!(\"b\"));\n    check_script_parser(\n        s,\n        vec![\n            Statement::Expression(\n                Parenthesized::new(\n                    Binary::new(\n                        BinaryOp::Comma,\n                        Update::new(\n                            UpdateOp::IncrementPost,\n                            UpdateTarget::Identifier(Identifier::new(b, Span::new((1, 2), (1, 3)))),\n                            Span::new((1, 2), (1, 5)),\n                        )\n                        .into(),\n                        Identifier::new(b, Span::new((1, 7), (1, 8))).into(),\n                    )\n                    .into(),\n                    Span::new((1, 1), (1, 9)),\n                )\n                .into(),\n            )\n            .into(),\n        ],\n        interner,\n    );\n}\n\n#[test]\nfn spread_in_object() {\n    let s = indoc! {\"\n        let x = {\n            a: 1,\n            ...b,\n        }\n    \"};\n\n    let interner = &mut Interner::default();\n\n    let object_properties = vec![\n        PropertyDefinition::Property(\n            Identifier::new(\n                interner.get_or_intern_static(\"a\", utf16!(\"a\")),\n                Span::new((2, 5), (2, 6)),\n            )\n            .into(),\n            Literal::new(1, Span::new((2, 8), (2, 9))).into(),\n        ),\n        PropertyDefinition::SpreadObject(\n            Identifier::new(\n                interner.get_or_intern_static(\"b\", utf16!(\"b\")),\n                Span::new((3, 8), (3, 9)),\n            )\n            .into(),\n        ),\n    ];\n\n    check_script_parser(\n        s,\n        vec![\n            Declaration::Lexical(LexicalDeclaration::Let(\n                vec![Variable::from_identifier(\n                    Identifier::new(\n                        interner.get_or_intern_static(\"x\", utf16!(\"x\")),\n                        Span::new((1, 5), (1, 6)),\n                    ),\n                    Some(ObjectLiteral::new(object_properties, Span::new((1, 9), (4, 2))).into()),\n                )]\n                .try_into()\n                .unwrap(),\n            ))\n            .into(),\n        ],\n        interner,\n    );\n}\n\n#[test]\nfn spread_in_arrow_function() {\n    let s = indoc! {r#\"\n        (...b) => {\n            b\n        }\n    \"#};\n\n    let interner = &mut Interner::default();\n    let b = interner.get_or_intern_static(\"b\", utf16!(\"b\"));\n    let params = FormalParameterList::from(FormalParameter::new(\n        Variable::from_identifier(Identifier::new(b, Span::new((1, 5), (1, 6))), None),\n        true,\n    ));\n    assert_eq!(params.flags(), FormalParameterListFlags::HAS_REST_PARAMETER);\n    assert_eq!(params.length(), 0);\n    check_script_parser(\n        s,\n        vec![\n            Statement::Expression(\n                ArrowFunction::new(\n                    None,\n                    params,\n                    FunctionBody::new(\n                        StatementList::new(\n                            [Statement::Expression(\n                                Identifier::new(b, Span::new((2, 5), (2, 6))).into(),\n                            )\n                            .into()],\n                            PSEUDO_LINEAR_POS,\n                            false,\n                        ),\n                        Span::new((1, 11), (3, 2)),\n                    ),\n                    EMPTY_LINEAR_SPAN,\n                    Span::new((1, 1), (3, 2)),\n                )\n                .into(),\n            )\n            .into(),\n        ],\n        interner,\n    );\n}\n\n#[test]\nfn empty_statement() {\n    let interner = &mut Interner::default();\n    let a = interner.get_or_intern_static(\"a\", utf16!(\"a\"));\n    check_script_parser(\n        indoc! {\"\n            ;;var a = 10;\n            if(a) ;\n        \"},\n        vec![\n            Statement::Empty.into(),\n            Statement::Empty.into(),\n            Statement::Var(VarDeclaration(\n                vec![Variable::from_identifier(\n                    Identifier::new(a, Span::new((1, 7), (1, 8))),\n                    Some(Literal::new(10, Span::new((1, 11), (1, 13))).into()),\n                )]\n                .try_into()\n                .unwrap(),\n            ))\n            .into(),\n            Statement::If(If::new(\n                Identifier::new(a, Span::new((2, 4), (2, 5))).into(),\n                Statement::Empty,\n                None,\n            ))\n            .into(),\n        ],\n        interner,\n    );\n}\n\n#[test]\nfn empty_statement_ends_directive_prologues() {\n    let interner = &mut Interner::default();\n    let a = interner.get_or_intern_static(\"a\", utf16!(\"a\"));\n    let use_strict = interner.get_or_intern_static(\"use strict\", utf16!(\"use strict\"));\n    let public = interner.get_or_intern_static(\"public\", utf16!(\"public\"));\n    check_script_parser(\n        indoc! {r#\"\n            \"a\";\n            ;\n            \"use strict\";\n            let public = 5;\n        \"#},\n        vec![\n            Statement::Expression(Literal::new(a, Span::new((1, 1), (1, 4))).into()).into(),\n            Statement::Empty.into(),\n            Statement::Expression(Literal::new(use_strict, Span::new((3, 1), (3, 13))).into())\n                .into(),\n            Declaration::Lexical(LexicalDeclaration::Let(\n                vec![Variable::from_identifier(\n                    Identifier::new(public, Span::new((4, 5), (4, 11))),\n                    Some(Literal::new(5, Span::new((4, 14), (4, 15))).into()),\n                )]\n                .try_into()\n                .unwrap(),\n            ))\n            .into(),\n        ],\n        interner,\n    );\n}\n\n#[test]\nfn hashbang_use_strict_no_with() {\n    check_script_parser(\n        r#\"#!\\\"use strict\"\n        \"#,\n        vec![],\n        &mut Interner::default(),\n    );\n}\n\n#[test]\nfn hashbang_use_strict_with_with_statement() {\n    check_script_parser(\n        indoc! {r#\"\n            #!\\\"use strict\"\n            with({}) {}\n        \"#},\n        vec![\n            Statement::With(With::new(\n                ObjectLiteral::new([], Span::new((2, 6), (2, 8))).into(),\n                Block::from(StatementList::new([], LinearPosition::new(27), false)).into(),\n            ))\n            .into(),\n        ],\n        &mut Interner::default(),\n    );\n}\n\n#[test]\nfn hashbang_comment() {\n    check_script_parser(r\"#!Comment Here\", vec![], &mut Interner::default());\n}\n\n#[test]\nfn deny_unicode_escape_in_false_expression() {\n    check_invalid_script(r\"let x = f\\u{61}lse;\");\n}\n\n#[test]\nfn deny_unicode_escape_in_true_expression() {\n    check_invalid_script(r\"let x = tru\\u{65};\");\n}\n\n#[test]\nfn deny_unicode_escape_in_null_expression() {\n    check_invalid_script(r\"let x = n\\u{75}ll;\");\n}\n\n#[test]\nfn stress_test_operations() {\n    let src = (\"1 * 2 + /* comment why not */ 3 / 4 % 5 + \".repeat(1_000)\n        + \"1; // end of line\\n\\n\")\n        .repeat(1_000);\n\n    assert!(\n        Parser::new(Source::from_bytes(&src))\n            .parse_script(&Scope::new_global(), &mut Interner::default())\n            .is_ok()\n    );\n}\n\n#[test]\nfn debugger_statement() {\n    check_script_parser(\n        \"debugger;\",\n        vec![Statement::Debugger.into()],\n        &mut Interner::default(),\n    );\n\n    check_script_parser(\n        \"debugger\",\n        vec![Statement::Debugger.into()],\n        &mut Interner::default(),\n    );\n\n    check_invalid_script(\"!debugger\");\n\n    check_invalid_script(\"let x = debugger;\");\n\n    check_invalid_script(\"debugger + debugger\");\n}\n\n#[test]\nfn invalid_arrow_function() {\n    check_invalid_script(r#\"(!()=>\"#);\n    check_invalid_script(r#\"!()=>{}\"#);\n}\n"
  },
  {
    "path": "core/parser/src/parser/tests/test.js",
    "content": "\"Hello\" + \"World\";\n"
  },
  {
    "path": "core/parser/src/source/mod.rs",
    "content": "//! Boa parser input source types.\n\nuse std::{\n    fs::File,\n    io::{self, BufReader, Read},\n    path::Path,\n};\n\npub use utf8::UTF8Input;\npub use utf16::UTF16Input;\n\nmod utf16;\nmod utf8;\n\n/// A source of ECMAScript code.\n///\n/// [`Source`]s can be created from plain [`str`]s, file [`Path`]s or more generally, any [`Read`]\n/// instance.\n#[derive(Debug)]\npub struct Source<'path, R> {\n    pub(crate) reader: R,\n    pub(crate) path: Option<&'path Path>,\n}\n\nimpl<'bytes> Source<'static, UTF8Input<&'bytes [u8]>> {\n    /// Creates a new `Source` from any type equivalent to a slice of bytes e.g. [`&str`][str],\n    /// <code>[Vec]<[u8]></code>, <code>[Box]<[\\[u8\\]][slice]></code> or a plain slice\n    /// <code>[&\\[u8\\]][slice]</code>.\n    ///\n    /// # Examples\n    ///\n    /// ```\n    /// # use boa_parser::Source;\n    /// let code = r#\"var array = [5, 4, 3, 2, 1];\"#;\n    /// let source = Source::from_bytes(code);\n    /// ```\n    ///\n    /// [slice]: std::slice\n    pub fn from_bytes<T: AsRef<[u8]> + ?Sized>(source: &'bytes T) -> Self {\n        Self {\n            reader: UTF8Input::new(source.as_ref()),\n            path: None,\n        }\n    }\n}\n\nimpl<'input> Source<'static, UTF16Input<'input>> {\n    /// Creates a new `Source` from a UTF-16 encoded slice e.g. <code>[&\\[u16\\]][slice]</code>.\n    ///\n    /// # Examples\n    ///\n    /// ```\n    /// # use boa_parser::Source;\n    /// let utf16: Vec<u16> =\n    ///     \"var array = [5, 4, 3, 2, 1];\".encode_utf16().collect();\n    /// let source = Source::from_utf16(&utf16);\n    /// ```\n    ///\n    /// [slice]: std::slice\n    #[must_use]\n    pub fn from_utf16(input: &'input [u16]) -> Self {\n        Self {\n            reader: UTF16Input::new(input),\n            path: None,\n        }\n    }\n}\n\nimpl<'path> Source<'path, UTF8Input<BufReader<File>>> {\n    /// Creates a new `Source` from a `Path` to a file.\n    ///\n    /// # Errors\n    ///\n    /// See [`File::open`].\n    ///\n    /// # Examples\n    ///\n    /// ```no_run\n    /// # use boa_parser::Source;\n    /// # use std::{fs::File, path::Path};\n    /// # fn main() -> std::io::Result<()> {\n    /// let path = Path::new(\"script.js\");\n    /// let source = Source::from_filepath(path)?;\n    /// # Ok(())\n    /// # }\n    /// ```\n    pub fn from_filepath(source: &'path Path) -> io::Result<Self> {\n        let reader = File::open(source)?;\n        Ok(Self {\n            reader: UTF8Input::new(BufReader::new(reader)),\n            path: Some(source),\n        })\n    }\n}\n\nimpl<'path, R: Read> Source<'path, UTF8Input<R>> {\n    /// Creates a new `Source` from a [`Read`] instance and an optional [`Path`].\n    ///\n    /// # Examples\n    ///\n    /// ```no_run\n    /// # use boa_parser::Source;\n    /// # use std::{fs::File, io::Read, path::Path};\n    /// # fn main() -> std::io::Result<()> {\n    /// let strictler = r#\"\"use strict\"\"#;\n    ///\n    /// let path = Path::new(\"no_strict.js\");\n    /// let file = File::open(path)?;\n    /// let strict = strictler.as_bytes().chain(file);\n    ///\n    /// let source = Source::from_reader(strict, Some(path));\n    /// #    Ok(())\n    /// # }\n    /// ```\n    pub fn from_reader(reader: R, path: Option<&'path Path>) -> Self {\n        Self {\n            reader: UTF8Input::new(reader),\n            path,\n        }\n    }\n}\n\nimpl<'path, R> Source<'path, R> {\n    /// Sets the path of this [`Source`].\n    pub fn with_path(self, new_path: &Path) -> Source<'_, R> {\n        Source {\n            reader: self.reader,\n            path: Some(new_path),\n        }\n    }\n\n    /// Returns the path (if any) of this source file.\n    pub fn path(&self) -> Option<&'path Path> {\n        self.path\n    }\n}\n\n/// This trait is used to abstract over the different types of input readers.\npub trait ReadChar {\n    /// Retrieves the next unicode code point. Returns `None` if the end of the input is reached.\n    ///\n    /// # Errors\n    ///\n    /// Returns an error if the next input in the input is not a valid unicode code point.\n    fn next_char(&mut self) -> io::Result<Option<u32>>;\n}\n\n#[cfg(test)]\nmod tests {\n    use std::io::Cursor;\n\n    use super::*;\n\n    #[test]\n    fn from_bytes() {\n        let mut source = Source::from_bytes(\"'Hello' + 'World';\");\n\n        assert!(source.path.is_none());\n\n        let mut content = String::new();\n        while let Some(c) = source.reader.next_char().unwrap() {\n            content.push(char::from_u32(c).unwrap());\n        }\n\n        assert_eq!(content, \"'Hello' + 'World';\");\n    }\n\n    #[test]\n    fn from_filepath() {\n        let manifest_path = Path::new(env!(\"CARGO_MANIFEST_DIR\"));\n        let filepath = manifest_path.join(\"src/parser/tests/test.js\");\n        let mut source = Source::from_filepath(&filepath).unwrap();\n\n        assert_eq!(source.path, Some(&*filepath));\n\n        let mut content = String::new();\n        while let Some(c) = source.reader.next_char().unwrap() {\n            content.push(char::from_u32(c).unwrap());\n        }\n\n        assert_eq!(content, \"\\\"Hello\\\" + \\\"World\\\";\\n\");\n    }\n\n    #[test]\n    fn from_reader() {\n        // Without path\n        let mut source = Source::from_reader(Cursor::new(\"'Hello' + 'World';\"), None);\n\n        assert!(source.path.is_none());\n\n        let mut content = String::new();\n        while let Some(c) = source.reader.next_char().unwrap() {\n            content.push(char::from_u32(c).unwrap());\n        }\n\n        assert_eq!(content, \"'Hello' + 'World';\");\n\n        // With path\n        let mut source =\n            Source::from_reader(Cursor::new(\"'Hello' + 'World';\"), Some(\"test.js\".as_ref()));\n\n        assert_eq!(source.path, Some(\"test.js\".as_ref()));\n\n        let mut content = String::new();\n        while let Some(c) = source.reader.next_char().unwrap() {\n            content.push(char::from_u32(c).unwrap());\n        }\n\n        assert_eq!(content, \"'Hello' + 'World';\");\n    }\n}\n"
  },
  {
    "path": "core/parser/src/source/utf16.rs",
    "content": "use super::ReadChar;\nuse std::io;\n\n/// Input for UTF-16 encoded sources.\n#[derive(Debug)]\npub struct UTF16Input<'a> {\n    input: &'a [u16],\n    index: usize,\n}\n\nimpl<'a> UTF16Input<'a> {\n    /// Creates a new `UTF16Input` from a UTF-16 encoded slice e.g. <code>[&\\[u16\\]][slice]</code>.\n    ///\n    /// [slice]: std::slice\n    #[must_use]\n    pub const fn new(input: &'a [u16]) -> Self {\n        Self { input, index: 0 }\n    }\n\n    // use `#[cold]` to hint to branch predictor that surrogate pairs are rare\n    #[cold]\n    fn handle_surrogate_pair(&mut self, u1: u16) -> u32 {\n        let Some(u2) = self.input.get(self.index).copied() else {\n            return u1.into();\n        };\n\n        // If the code unit is not a low surrogate, it is not a surrogate pair.\n        if !is_low_surrogate(u2) {\n            return u1.into();\n        }\n\n        self.index += 1;\n\n        code_point_from_surrogates(u1, u2)\n    }\n}\n\nimpl ReadChar for UTF16Input<'_> {\n    /// Retrieves the next unchecked char in u32 code point.\n    fn next_char(&mut self) -> io::Result<Option<u32>> {\n        let Some(u1) = self.input.get(self.index).copied() else {\n            return Ok(None);\n        };\n\n        self.index += 1;\n\n        // If the code unit is not a high surrogate, it is not the start of a surrogate pair.\n        if !is_high_surrogate(u1) {\n            return Ok(Some(u1.into()));\n        }\n\n        Ok(Some(self.handle_surrogate_pair(u1)))\n    }\n}\n\nconst SURROGATE_HIGH_START: u16 = 0xD800;\nconst SURROGATE_HIGH_END: u16 = 0xDBFF;\nconst SURROGATE_LOW_START: u16 = 0xDC00;\nconst SURROGATE_LOW_END: u16 = 0xDFFF;\n\nfn is_high_surrogate(b: u16) -> bool {\n    (SURROGATE_HIGH_START..=SURROGATE_HIGH_END).contains(&b)\n}\n\nfn is_low_surrogate(b: u16) -> bool {\n    (SURROGATE_LOW_START..=SURROGATE_LOW_END).contains(&b)\n}\n\nfn code_point_from_surrogates(high: u16, low: u16) -> u32 {\n    (((u32::from(high & 0x3ff)) << 10) | u32::from(low & 0x3ff)) + 0x1_0000\n}\n"
  },
  {
    "path": "core/parser/src/source/utf8.rs",
    "content": "use super::ReadChar;\nuse std::io::{self, Bytes, Read};\n\n/// Input for UTF-8 encoded sources.\n#[derive(Debug)]\npub struct UTF8Input<R> {\n    input: Bytes<R>,\n}\n\nimpl<R: Read> UTF8Input<R> {\n    /// Creates a new `UTF8Input` from a UTF-8 encoded source.\n    pub(crate) fn new(iter: R) -> Self {\n        Self {\n            #[allow(clippy::unbuffered_bytes)]\n            input: iter.bytes(),\n        }\n    }\n}\n\nimpl<R: Read> UTF8Input<R> {\n    /// Retrieves the next byte\n    fn next_byte(&mut self) -> io::Result<Option<u8>> {\n        self.input.next().transpose()\n    }\n}\n\nimpl<R: Read> ReadChar for UTF8Input<R> {\n    /// Retrieves the next unchecked char in u32 code point.\n    fn next_char(&mut self) -> io::Result<Option<u32>> {\n        // Decode UTF-8\n        let x = match self.next_byte()? {\n            Some(b) if b >= 128 => b,         // UTF-8 codepoint\n            b => return Ok(b.map(u32::from)), // ASCII or None\n        };\n\n        // Multibyte case follows\n        // Decode from a byte combination out of: [[[x y] z] w]\n        // NOTE: Performance is sensitive to the exact formulation here\n        let init = utf8_first_byte(x, 2);\n        let y = self.next_byte()?.unwrap_or(0);\n        let mut ch = utf8_acc_cont_byte(init, y);\n        if x >= 0xE0 {\n            // [[x y z] w] case\n            // 5th bit in 0xE0 .. 0xEF is always clear, so `init` is still valid\n            let z = self.next_byte()?.unwrap_or(0);\n            let y_z = utf8_acc_cont_byte(u32::from(y & CONT_MASK), z);\n            ch = (init << 12) | y_z;\n            if x >= 0xF0 {\n                // [x y z w] case\n                // use only the lower 3 bits of `init`\n                let w = self.next_byte()?.unwrap_or(0);\n                ch = ((init & 7) << 18) | utf8_acc_cont_byte(y_z, w);\n            }\n        }\n\n        Ok(Some(ch))\n    }\n}\n\n/// Mask of the value bits of a continuation byte.\nconst CONT_MASK: u8 = 0b0011_1111;\n\n/// Returns the initial codepoint accumulator for the first byte.\n/// The first byte is special, only want bottom 5 bits for width 2, 4 bits\n/// for width 3, and 3 bits for width 4.\nfn utf8_first_byte(byte: u8, width: u32) -> u32 {\n    u32::from(byte & (0x7F >> width))\n}\n\n/// Returns the value of `ch` updated with continuation byte `byte`.\nfn utf8_acc_cont_byte(ch: u32, byte: u8) -> u32 {\n    (ch << 6) | u32::from(byte & CONT_MASK)\n}\n"
  },
  {
    "path": "core/runtime/ABOUT.md",
    "content": "# About Boa\n\nBoa is an open-source, experimental ECMAScript Engine written in Rust for\nlexing, parsing and executing ECMAScript/JavaScript. Currently, Boa supports some\nof the [language][boa-conformance]. More information can be viewed at [Boa's\nwebsite][boa-web].\n\nTry out the most recent release with Boa's live demo\n[playground][boa-playground].\n\n## Boa Crates\n\n- [**`boa_cli`**][cli] - Boa's CLI && REPL implementation\n- [**`boa_ast`**][ast] - Boa's ECMAScript Abstract Syntax Tree.\n- [**`boa_engine`**][engine] - Boa's implementation of ECMAScript builtin objects and execution.\n- [**`boa_gc`**][gc] - Boa's garbage collector.\n- [**`boa_icu_provider`**][icu] - Boa's ICU4X data provider.\n- [**`boa_interner`**][interner] - Boa's string interner.\n- [**`boa_macros`**][macros] - Boa's macros.\n- [**`boa_parser`**][parser] - Boa's lexer and parser.\n- [**`boa_runtime`**][runtime] - Boa's `WebAPI` features.\n- [**`boa_string`**][string] - Boa's ECMAScript string implementation.\n- [**`boa_wintertc`**][wintertc] - Boa's `WinterTC` (TC55) Minimum Common Web API implementation.\n- [**`tag_ptr`**][tag_ptr] - Utility library that enables a pointer to be associated with a tag of type `usize`.\n- [**`small_btree`**][small_btree] - Utility library that adds the `SmallBTreeMap` data structure.\n\n[boa-conformance]: https://boajs.dev/conformance\n[boa-web]: https://boajs.dev/\n[boa-playground]: https://boajs.dev/playground\n[ast]: https://docs.rs/boa_ast/latest/boa_ast/index.html\n[engine]: https://docs.rs/boa_engine/latest/boa_engine/index.html\n[gc]: https://docs.rs/boa_gc/latest/boa_gc/index.html\n[interner]: https://docs.rs/boa_interner/latest/boa_interner/index.html\n[parser]: https://docs.rs/boa_parser/latest/boa_parser/index.html\n[icu]: https://docs.rs/boa_icu_provider/latest/boa_icu_provider/index.html\n[runtime]: https://docs.rs/boa_runtime/latest/boa_runtime/index.html\n[string]: https://docs.rs/boa_string/latest/boa_string/index.html\n[wintertc]: https://docs.rs/boa_wintertc/latest/boa_wintertc/index.html\n[tag_ptr]: https://docs.rs/tag_ptr/latest/tag_ptr/index.html\n[small_btree]: https://docs.rs/small_btree/latest/small_btree/index.html\n[macros]: https://docs.rs/boa_macros/latest/boa_macros/index.html\n[cli]: https://crates.io/crates/boa_cli\n"
  },
  {
    "path": "core/runtime/Cargo.toml",
    "content": "[package]\nname = \"boa_runtime\"\ndescription = \"Example runtime for the Boa JavaScript engine.\"\nkeywords = [\"javascript\", \"js\", \"runtime\"]\ncategories = [\"command-line-utilities\"]\nversion.workspace = true\nedition.workspace = true\nauthors.workspace = true\nlicense.workspace = true\nrepository.workspace = true\nrust-version.workspace = true\n\n[dependencies]\nboa_engine.workspace = true\nbase64.workspace = true\nboa_gc.workspace = true\nbytemuck.workspace = true\neither = { workspace = true, optional = true }\nfutures = \"0.3.32\"\nfutures-lite.workspace = true\nhttp = { workspace = true, optional = true }\nreqwest = { workspace = true, optional = true }\nrustc-hash = { workspace = true, features = [\"std\"] }\nserde_json = { workspace = true, optional = true }\nurl = { workspace = true, optional = true }\n\n[dev-dependencies]\nindoc.workspace = true\nrstest.workspace = true\ntest-case.workspace = true\ntextwrap.workspace = true\ntemp-env.workspace = true\n\n[lints]\nworkspace = true\n\n[package.metadata.docs.rs]\nall-features = true\n\n[features]\n# Default should not add `reqwest` as it is not available on all platforms.\ndefault = [\n    \"boa_engine/float16\",\n    \"boa_engine/temporal\",\n    \"boa_engine/xsum\",\n    \"fetch\",\n    \"url\",\n]\n\nall = [\"default\", \"reqwest-blocking\"]\nurl = [\"dep:url\"]\nfetch = [\n    \"dep:either\",\n    \"dep:http\",\n    \"dep:serde_json\",\n    \"boa_engine/either\",\n]\nreqwest-blocking = [\"dep:reqwest\", \"reqwest/blocking\"]\nprocess = []\n"
  },
  {
    "path": "core/runtime/assets/harness.js",
    "content": "function assert(cond, message) {\n  if (!cond) {\n    throw `AssertionError: ${message ? message + \", \" : \"\"}condition was falsy`;\n  }\n}\n\nfunction assertEq(lhs, rhs, message) {\n  if (lhs !== rhs) {\n    throw `AssertionError: ${\n      message ? message + \", \" : \"\"\n    }expected ${JSON.stringify(rhs)}, actual ${JSON.stringify(lhs)}`;\n  }\n}\n\nfunction assertNEq(lhs, rhs, message) {\n  if (lhs === rhs) {\n    throw `AssertionError: ${\n      message ? message + \", \" : \"\"\n    }expected ${JSON.stringify(rhs)}, actual ${JSON.stringify(lhs)}`;\n  }\n}\n\nfunction assertArrayEqual(lhs, rhs, message) {\n  if (lhs === rhs) {\n    return;\n  }\n\n  // Supports iterables.\n  let l;\n  try {\n    l = [...lhs];\n  } catch (e) {\n    throw `AssertionError: ${\n      message ? message + \", \" : \"\"\n    }expected an iterable, actual isn't.`;\n  }\n  const r = [...rhs];\n\n  if (l.length === r.length) {\n    for (let i = 0; i < l.length; i++) {\n      assertEq(l[i], r[i], message);\n    }\n    return;\n  }\n\n  throw `AssertionError: ${\n    message ? message + \", \" : \"\"\n  }expected ${JSON.stringify(rhs)}, actual ${JSON.stringify(lhs)}`;\n}\n\nfunction assertThrows(fn, message) {\n  try {\n    fn();\n  } catch (e) {\n    return;\n  }\n  throw `AssertionError: ${\n    message ? message + \", \" : \"\"\n  }function did not throw.`;\n}\n"
  },
  {
    "path": "core/runtime/build.rs",
    "content": "#![allow(unused_crate_dependencies, missing_docs)]\n\npub fn main() {\n    // Rerun the tests if the test files change.\n    println!(\"cargo::rerun-if-changed=tests/\");\n}\n"
  },
  {
    "path": "core/runtime/src/abort/mod.rs",
    "content": "//! `AbortController` and `AbortSignal` Web API implementations.\n\nuse boa_engine::class::Class;\nuse boa_engine::job::GenericJob;\nuse boa_engine::object::builtins::JsFunction;\nuse boa_engine::realm::Realm;\nuse boa_engine::{\n    Context, Finalize, JsData, JsError, JsNativeError, JsObject, JsResult, JsValue, Trace,\n    boa_class, boa_module, js_error, js_string,\n};\nuse boa_gc::GcRefCell;\nuse std::cell::Cell;\nuse std::sync::Arc;\nuse std::sync::atomic::{AtomicBool, Ordering};\n\n#[cfg(test)]\nmod tests;\n\n/// Cancellation token for cooperative abort.\n#[derive(Debug, Clone)]\npub struct CancellationToken(Arc<AtomicBool>);\n\nimpl CancellationToken {\n    fn new() -> Self {\n        Self(Arc::new(AtomicBool::new(false)))\n    }\n\n    /// Cancel the token.\n    pub fn cancel(&self) {\n        self.0.store(true, Ordering::Release);\n    }\n\n    /// Returns `true` if cancelled.\n    #[must_use]\n    pub fn is_cancelled(&self) -> bool {\n        self.0.load(Ordering::Acquire)\n    }\n}\n\nfn make_abort_error(context: &mut Context) -> JsValue {\n    let obj = JsNativeError::error()\n        .with_message(\"signal is aborted without reason\")\n        .into_opaque(context);\n    obj.set(js_string!(\"name\"), js_string!(\"AbortError\"), false, context)\n        .ok();\n    obj.into()\n}\n\n/// The JavaScript `AbortSignal` class.\n#[derive(Debug, Clone, JsData, Trace, Finalize)]\npub struct JsAbortSignal {\n    #[unsafe_ignore_trace]\n    aborted: Cell<bool>,\n    reason: GcRefCell<Option<JsValue>>,\n    listeners: GcRefCell<Vec<JsFunction>>,\n    #[unsafe_ignore_trace]\n    cancel_token: CancellationToken,\n}\n\nimpl Default for JsAbortSignal {\n    fn default() -> Self {\n        Self {\n            aborted: Cell::new(false),\n            reason: GcRefCell::default(),\n            listeners: GcRefCell::default(),\n            cancel_token: CancellationToken::new(),\n        }\n    }\n}\n\nimpl JsAbortSignal {\n    /// # Errors\n    ///\n    /// Returns an error if the signal has already been aborted.\n    pub fn signal_abort(&self, reason: JsValue, context: &mut Context) -> JsResult<()> {\n        if self.aborted.get() {\n            return Ok(());\n        }\n        self.aborted.set(true);\n        *self.reason.borrow_mut() = Some(reason);\n\n        let listeners: Vec<JsFunction> = self.listeners.borrow_mut().drain(..).collect();\n\n        let realm = context.realm().clone();\n        for listener in listeners {\n            context.enqueue_job(\n                GenericJob::new(\n                    move |context| {\n                        listener.call(&JsValue::undefined(), &[], context)?;\n                        Ok(JsValue::undefined())\n                    },\n                    realm.clone(),\n                )\n                .into(),\n            );\n        }\n\n        self.cancel_token.cancel();\n\n        Ok(())\n    }\n\n    /// Returns `true` if this signal has been aborted.\n    #[must_use]\n    pub fn is_aborted(&self) -> bool {\n        self.aborted.get()\n    }\n\n    /// Returns the abort reason.\n    pub fn abort_reason(&self, context: &mut Context) -> JsValue {\n        if !self.aborted.get() {\n            return JsValue::undefined();\n        }\n        self.reason\n            .borrow()\n            .clone()\n            .unwrap_or_else(|| make_abort_error(context))\n    }\n\n    /// Returns the cancellation token.\n    #[must_use]\n    pub fn cancellation_token(&self) -> CancellationToken {\n        self.cancel_token.clone()\n    }\n}\n\n#[boa_class(rename = \"AbortSignal\")]\n#[boa(rename_all = \"camelCase\")]\nimpl JsAbortSignal {\n    #[boa(constructor)]\n    fn constructor() -> JsResult<Self> {\n        Err(JsNativeError::typ()\n            .with_message(\"Illegal constructor\")\n            .into())\n    }\n\n    #[boa(getter)]\n    fn aborted(&self) -> bool {\n        self.aborted.get()\n    }\n\n    #[boa(getter)]\n    fn reason(&self, context: &mut Context) -> JsValue {\n        self.abort_reason(context)\n    }\n\n    fn throw_if_aborted(&self, context: &mut Context) -> JsResult<()> {\n        if self.aborted.get() {\n            Err(JsError::from_opaque(self.abort_reason(context)))\n        } else {\n            Ok(())\n        }\n    }\n\n    fn add_event_listener(\n        &self,\n        event_type: boa_engine::JsString,\n        callback: JsFunction,\n        context: &mut Context,\n    ) -> JsResult<()> {\n        if event_type.to_std_string_escaped() != \"abort\" {\n            return Err(js_error!(TypeError: \"AbortSignal only supports the 'abort' event type\"));\n        }\n        if self.aborted.get() {\n            callback.call(&JsValue::undefined(), &[], context)?;\n        } else {\n            self.listeners.borrow_mut().push(callback);\n        }\n        Ok(())\n    }\n\n    fn remove_event_listener(&self, event_type: boa_engine::JsString, callback: JsFunction) {\n        if event_type.to_std_string_escaped() != \"abort\" {\n            return;\n        }\n        self.listeners\n            .borrow_mut()\n            .retain(|f| !JsObject::equals(f, &callback));\n    }\n}\n\n/// The JavaScript `AbortController` class.\n#[derive(Debug, Clone, JsData, Trace, Finalize)]\npub struct JsAbortController {\n    signal: JsObject,\n}\n\n#[boa_class(rename = \"AbortController\")]\n#[boa(rename_all = \"camelCase\")]\nimpl JsAbortController {\n    #[boa(constructor)]\n    fn constructor(context: &mut Context) -> JsResult<Self> {\n        let signal_obj = Class::from_data(JsAbortSignal::default(), context)?;\n        Ok(Self { signal: signal_obj })\n    }\n\n    #[boa(getter)]\n    fn signal(&self) -> JsObject {\n        self.signal.clone()\n    }\n\n    fn abort(&self, reason: Option<JsValue>, context: &mut Context) -> JsResult<()> {\n        let abort_reason = reason.unwrap_or_else(|| make_abort_error(context));\n\n        let Some(signal) = self.signal.downcast_ref::<JsAbortSignal>() else {\n            return Err(js_error!(TypeError: \"AbortController: invalid signal object\"));\n        };\n        signal.signal_abort(abort_reason, context)\n    }\n}\n\n/// `AbortController` and `AbortSignal` module.\n#[boa_module]\npub mod js_module {\n    type JsAbortController = super::JsAbortController;\n    type JsAbortSignal = super::JsAbortSignal;\n}\n\n/// # Errors\n/// Returns an error if registration fails.\npub fn register(realm: Option<Realm>, context: &mut Context) -> JsResult<()> {\n    js_module::boa_register(realm, context)\n}\n"
  },
  {
    "path": "core/runtime/src/abort/tests.rs",
    "content": "use crate::test::{TestAction, run_test_actions};\n\n#[test]\nfn abort_controller_exists() {\n    run_test_actions([\n        TestAction::run(\"let ctrl = new AbortController()\"),\n        TestAction::run(\"let sig = ctrl.signal\"),\n    ]);\n}\n\n#[test]\nfn signal_not_aborted_initially() {\n    run_test_actions([TestAction::run(\n        r\"\n        let ctrl = new AbortController();\n        if (ctrl.signal.aborted !== false) {\n            throw new Error('signal should not be aborted initially');\n        }\n        \",\n    )]);\n}\n\n#[test]\nfn abort_sets_aborted() {\n    run_test_actions([TestAction::run(\n        r\"\n        let ctrl = new AbortController();\n        ctrl.abort();\n        if (ctrl.signal.aborted !== true) {\n            throw new Error('signal should be aborted after abort()');\n        }\n        \",\n    )]);\n}\n\n#[test]\nfn abort_with_custom_reason() {\n    run_test_actions([TestAction::run(\n        r#\"\n        let ctrl = new AbortController();\n        ctrl.abort(\"custom reason\");\n        if (ctrl.signal.reason !== \"custom reason\") {\n            throw new Error('reason should be \"custom reason\", got: ' + ctrl.signal.reason);\n        }\n        \"#,\n    )]);\n}\n\n#[test]\nfn abort_default_reason() {\n    run_test_actions([TestAction::run(\n        r#\"\n        let ctrl = new AbortController();\n        ctrl.abort();\n        if (ctrl.signal.reason.name !== \"AbortError\") {\n            throw new Error('default reason.name should be \"AbortError\", got: ' + ctrl.signal.reason.name);\n        }\n        \"#,\n    )]);\n}\n\n#[test]\nfn throw_if_aborted_does_nothing_when_not_aborted() {\n    run_test_actions([TestAction::run(\n        r\"\n        let ctrl = new AbortController();\n        ctrl.signal.throwIfAborted();\n        \",\n    )]);\n}\n\n#[test]\nfn throw_if_aborted_throws_when_aborted() {\n    run_test_actions([TestAction::run(\n        r#\"\n        let ctrl = new AbortController();\n        ctrl.abort(\"test error\");\n        let threw = false;\n        try {\n            ctrl.signal.throwIfAborted();\n        } catch (e) {\n            threw = true;\n            if (e !== \"test error\") {\n                throw new Error('expected \"test error\", got: ' + e);\n            }\n        }\n        if (!threw) {\n            throw new Error('throwIfAborted should have thrown');\n        }\n        \"#,\n    )]);\n}\n\n#[test]\nfn add_event_listener_fires_on_abort() {\n    run_test_actions([\n        TestAction::run(\n            r\"\n            let ctrl = new AbortController();\n            let called = false;\n            ctrl.signal.addEventListener('abort', function() {\n                called = true;\n            });\n            ctrl.abort();\n            \",\n        ),\n        TestAction::inspect_context(|ctx| {\n            ctx.run_jobs().unwrap();\n        }),\n        TestAction::run(\n            r\"\n            if (!called) {\n                throw new Error('abort listener was not called');\n            }\n            \",\n        ),\n    ]);\n}\n\n#[test]\nfn multiple_listeners() {\n    run_test_actions([\n        TestAction::run(\n            r\"\n            let ctrl = new AbortController();\n            let count = 0;\n            ctrl.signal.addEventListener('abort', function() { count += 1; });\n            ctrl.signal.addEventListener('abort', function() { count += 1; });\n            ctrl.signal.addEventListener('abort', function() { count += 1; });\n            ctrl.abort();\n            \",\n        ),\n        TestAction::inspect_context(|ctx| {\n            ctx.run_jobs().unwrap();\n        }),\n        TestAction::run(\n            r\"\n            if (count !== 3) {\n                throw new Error('expected 3 listeners called, got: ' + count);\n            }\n            \",\n        ),\n    ]);\n}\n\n#[test]\nfn repeated_abort_is_idempotent() {\n    run_test_actions([\n        TestAction::run(\n            r\"\n            let ctrl = new AbortController();\n            let count = 0;\n            ctrl.signal.addEventListener('abort', function() { count += 1; });\n            ctrl.abort();\n            ctrl.abort();\n            ctrl.abort();\n            \",\n        ),\n        TestAction::inspect_context(|ctx| {\n            ctx.run_jobs().unwrap();\n        }),\n        TestAction::run(\n            r\"\n            if (count !== 1) {\n                throw new Error('listeners should fire only once, got: ' + count);\n            }\n            \",\n        ),\n    ]);\n}\n\n#[test]\nfn signal_reuse_across_references() {\n    run_test_actions([TestAction::run(\n        r\"\n        let ctrl = new AbortController();\n        let sig1 = ctrl.signal;\n        let sig2 = ctrl.signal;\n        ctrl.abort();\n        if (sig1.aborted !== true || sig2.aborted !== true) {\n            throw new Error('both signal references should be aborted');\n        }\n        \",\n    )]);\n}\n\n#[test]\nfn reason_undefined_when_not_aborted() {\n    run_test_actions([TestAction::run(\n        r\"\n        let ctrl = new AbortController();\n        if (ctrl.signal.reason !== undefined) {\n            throw new Error('reason should be undefined before abort, got: ' + ctrl.signal.reason);\n        }\n        \",\n    )]);\n}\n\n#[test]\nfn reason_abort_error_when_aborted_without_reason() {\n    run_test_actions([TestAction::run(\n        r#\"\n        let ctrl = new AbortController();\n        ctrl.abort();\n        if (!(ctrl.signal.reason instanceof Error)) {\n            throw new Error('reason should be an instance of Error');\n        }\n        if (ctrl.signal.reason.name !== \"AbortError\") {\n            throw new Error('reason.name should be \"AbortError\", got: ' + ctrl.signal.reason.name);\n        }\n        \"#,\n    )]);\n}\n"
  },
  {
    "path": "core/runtime/src/base64/mod.rs",
    "content": "//! Base64 utility methods (`atob` and `btoa`).\n//!\n//! See <https://html.spec.whatwg.org/multipage/webappapis.html#atob>.\n#![allow(clippy::needless_pass_by_value)]\n\nuse boa_engine::realm::Realm;\nuse boa_engine::{Context, JsResult, boa_module};\n\n#[cfg(test)]\nmod tests;\n\n/// A forgiving Base64 engine that accepts input with or without padding,\n/// matching the [forgiving-base64 decode](https://infra.spec.whatwg.org/#forgiving-base64-decode)\n/// algorithm used by `atob`.\nconst FORGIVING: base64::engine::GeneralPurpose = base64::engine::GeneralPurpose::new(\n    &base64::alphabet::STANDARD,\n    base64::engine::general_purpose::GeneralPurposeConfig::new()\n        .with_decode_padding_mode(base64::engine::DecodePaddingMode::Indifferent),\n);\n\n/// JavaScript module containing the `atob` and `btoa` functions.\n#[boa_module]\npub mod js_module {\n    use super::FORGIVING;\n    use base64::Engine as _;\n    use boa_engine::{JsResult, js_error};\n\n    /// The [`btoa()`][mdn] method creates a Base64-encoded ASCII string from\n    /// a binary string (i.e., a string in which each character is treated as\n    /// a byte of binary data).\n    ///\n    /// # Errors\n    /// Throws a `DOMException` (`InvalidCharacterError`) if the string\n    /// contains any character whose code point is greater than `0xFF`.\n    ///\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/API/Window/btoa\n    #[allow(clippy::cast_possible_truncation)]\n    pub fn btoa(data: String) -> JsResult<String> {\n        let bytes: Vec<u8> = data\n            .chars()\n            .map(|c| {\n                let cp = c as u32;\n                if cp > 0xFF {\n                    Err(js_error!(\"InvalidCharacterError: The string to be encoded contains characters outside of the Latin1 range.\"))\n                } else {\n                    Ok(cp as u8)\n                }\n            })\n            .collect::<JsResult<Vec<u8>>>()?;\n\n        Ok(base64::engine::general_purpose::STANDARD.encode(&bytes))\n    }\n\n    /// The [`atob()`][mdn] method decodes a string of data which has been\n    /// encoded using Base64 encoding.\n    ///\n    /// # Errors\n    /// Throws a `DOMException` (`InvalidCharacterError`) if the input is\n    /// not valid Base64.\n    ///\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/API/Window/atob\n    pub fn atob(data: String) -> JsResult<String> {\n        let cleaned: String = data\n            .chars()\n            .filter(|c| !matches!(c, ' ' | '\\t' | '\\n' | '\\x0C' | '\\r'))\n            .collect();\n\n        let bytes = FORGIVING.decode(cleaned.as_bytes()).map_err(|_| {\n            js_error!(\"InvalidCharacterError: The string to be decoded is not correctly encoded.\")\n        })?;\n\n        Ok(bytes.into_iter().map(char::from).collect())\n    }\n}\n\n/// Register the `atob` and `btoa` functions in the global context.\n///\n/// # Errors\n/// Returns an error if the functions cannot be registered.\npub fn register(realm: Option<Realm>, context: &mut Context) -> JsResult<()> {\n    js_module::boa_register(realm, context)\n}\n"
  },
  {
    "path": "core/runtime/src/base64/tests.rs",
    "content": "use crate::test::{TestAction, run_test_actions_with};\nuse boa_engine::Context;\nuse indoc::indoc;\n\n#[test]\nfn btoa_basic() {\n    let context = &mut Context::default();\n    crate::base64::register(None, context).unwrap();\n\n    run_test_actions_with(\n        [TestAction::run(indoc! {r#\"\n            if (btoa(\"hello\") !== \"aGVsbG8=\") {\n                throw new Error(\"btoa('hello') failed: \" + btoa(\"hello\"));\n            }\n            if (btoa(\"\") !== \"\") {\n                throw new Error(\"btoa('') failed\");\n            }\n            if (btoa(\"a\") !== \"YQ==\") {\n                throw new Error(\"btoa('a') failed: \" + btoa(\"a\"));\n            }\n            if (btoa(\"ab\") !== \"YWI=\") {\n                throw new Error(\"btoa('ab') failed: \" + btoa(\"ab\"));\n            }\n            if (btoa(\"abc\") !== \"YWJj\") {\n                throw new Error(\"btoa('abc') failed: \" + btoa(\"abc\"));\n            }\n        \"#})],\n        context,\n    );\n}\n\n#[test]\nfn atob_basic() {\n    let context = &mut Context::default();\n    crate::base64::register(None, context).unwrap();\n\n    run_test_actions_with(\n        [TestAction::run(indoc! {r#\"\n            if (atob(\"aGVsbG8=\") !== \"hello\") {\n                throw new Error(\"atob('aGVsbG8=') failed: \" + atob(\"aGVsbG8=\"));\n            }\n            if (atob(\"\") !== \"\") {\n                throw new Error(\"atob('') failed\");\n            }\n            if (atob(\"YQ==\") !== \"a\") {\n                throw new Error(\"atob('YQ==') failed\");\n            }\n            if (atob(\"YWI=\") !== \"ab\") {\n                throw new Error(\"atob('YWI=') failed\");\n            }\n            if (atob(\"YWJj\") !== \"abc\") {\n                throw new Error(\"atob('YWJj') failed\");\n            }\n        \"#})],\n        context,\n    );\n}\n\n#[test]\nfn roundtrip() {\n    let context = &mut Context::default();\n    crate::base64::register(None, context).unwrap();\n\n    run_test_actions_with(\n        [TestAction::run(indoc! {r#\"\n            var inputs = [\"\", \"a\", \"ab\", \"abc\", \"hello\", \"Hello, World!\", \"\\x80\\xFF\", \"caf\\xE9\"];\n            for (var i = 0; i < inputs.length; i++) {\n                if (atob(btoa(inputs[i])) !== inputs[i]) {\n                    throw new Error(\"roundtrip failed for input at index \" + i);\n                }\n            }\n        \"#})],\n        context,\n    );\n}\n\n#[test]\nfn btoa_throws_on_non_latin1() {\n    let context = &mut Context::default();\n    crate::base64::register(None, context).unwrap();\n\n    run_test_actions_with(\n        [TestAction::run(indoc! {r#\"\n            var threw = false;\n            try {\n                btoa(\"\\u{2713}\");\n            } catch (e) {\n                threw = true;\n                var msg = String(e);\n                if (msg.indexOf(\"InvalidCharacterError\") === -1) {\n                    throw new Error(\"Expected InvalidCharacterError, got: \" + msg);\n                }\n            }\n            if (!threw) {\n                throw new Error(\"btoa should throw on non-Latin1 characters\");\n            }\n        \"#})],\n        context,\n    );\n}\n\n#[test]\nfn atob_throws_on_invalid_input() {\n    let context = &mut Context::default();\n    crate::base64::register(None, context).unwrap();\n\n    run_test_actions_with(\n        [TestAction::run(indoc! {r#\"\n            function expectThrow(input, label) {\n                var threw = false;\n                try {\n                    atob(input);\n                } catch (e) {\n                    threw = true;\n                }\n                if (!threw) {\n                    throw new Error(\"atob should throw on \" + label + \": \" + input);\n                }\n            }\n\n            expectThrow(\"a\", \"length % 4 == 1\");\n            expectThrow(\"!!!!\", \"invalid characters\");\n            expectThrow(\"YQ=a\", \"misplaced padding\");\n        \"#})],\n        context,\n    );\n}\n\n#[test]\nfn atob_ignores_whitespace() {\n    let context = &mut Context::default();\n    crate::base64::register(None, context).unwrap();\n\n    run_test_actions_with(\n        [TestAction::run(indoc! {r#\"\n            if (atob(\" aGVs bG8= \") !== \"hello\") {\n                throw new Error(\"atob should ignore spaces\");\n            }\n            if (atob(\"\\taGVs\\tbG8=\\t\") !== \"hello\") {\n                throw new Error(\"atob should ignore tabs\");\n            }\n            if (atob(\"\\naGVs\\nbG8=\\n\") !== \"hello\") {\n                throw new Error(\"atob should ignore newlines\");\n            }\n            if (atob(\"\\x0CaGVs\\x0CbG8=\\x0C\") !== \"hello\") {\n                throw new Error(\"atob should ignore form feeds\");\n            }\n            if (atob(\"\\raGVs\\rbG8=\\r\") !== \"hello\") {\n                throw new Error(\"atob should ignore carriage returns\");\n            }\n        \"#})],\n        context,\n    );\n}\n"
  },
  {
    "path": "core/runtime/src/clone/mod.rs",
    "content": "//! Module containing all types and functions to implement `structuredClone`.\n//!\n//! See <https://developer.mozilla.org/en-US/docs/Web/API/Window/structuredClone>.\n#![allow(clippy::needless_pass_by_value)]\n\nuse boa_engine::realm::Realm;\nuse boa_engine::value::TryFromJs;\nuse boa_engine::{Context, JsResult, JsValue, boa_module};\n\n/// Options used by `structuredClone`. This is currently unused.\n#[derive(Debug, Clone, TryFromJs)]\npub struct StructuredCloneOptions {\n    transfer: Option<Vec<JsValue>>,\n}\n\n/// JavaScript module containing the `structuredClone` types and functions.\n#[boa_module]\npub mod js_module {\n    use super::StructuredCloneOptions;\n    use crate::store::JsValueStore;\n    use boa_engine::value::TryIntoJs;\n    use boa_engine::{Context, JsResult, JsValue};\n\n    /// The [`structuredClone()`][mdn] method of the Window interface creates a\n    /// deep clone of a given value using the [structured clone algorithm][sca].\n    ///\n    /// # Errors\n    /// Will return an error if the context cannot create objects or copy bytes, or\n    /// if any unhandled case by the structured clone algorithm.\n    ///\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/API/Window/structuredClone\n    /// [sca]: https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API/Structured_clone_algorithm\n    pub fn structured_clone(\n        value: JsValue,\n        options: Option<StructuredCloneOptions>,\n        context: &mut Context,\n    ) -> JsResult<JsValue> {\n        let v = JsValueStore::try_from_js(\n            &value,\n            context,\n            options.and_then(|o| o.transfer).unwrap_or_default(),\n        )?;\n        v.try_into_js(context)\n    }\n}\n\n/// Register the `structuredClone` function in the global context.\n///\n/// # Errors\n/// Return an error if the function is already registered.\npub fn register(realm: Option<Realm>, context: &mut Context) -> JsResult<()> {\n    js_module::boa_register(realm, context)\n}\n"
  },
  {
    "path": "core/runtime/src/console/mod.rs",
    "content": "//! Boa's implementation of JavaScript's `console` Web API object.\n//!\n//! The `console` object can be accessed from any global object.\n//!\n//! The specifics of how it works varies from browser to browser, but there is a de facto set of features that are typically provided.\n//!\n//! More information:\n//!  - [MDN documentation][mdn]\n//!  - [WHATWG `console` specification][spec]\n//!\n//! [spec]: https://console.spec.whatwg.org/\n//! [mdn]: https://developer.mozilla.org/en-US/docs/Web/API/Console\n\n#[cfg(test)]\npub(crate) mod tests;\n\nuse boa_engine::JsVariant;\nuse boa_engine::property::Attribute;\nuse boa_engine::{\n    Context, JsArgs, JsData, JsError, JsResult, JsString, JsSymbol, js_str, js_string,\n    native_function::NativeFunction,\n    object::{JsObject, ObjectInitializer},\n    value::{JsValue, Numeric},\n};\nuse boa_gc::{Finalize, Trace};\nuse rustc_hash::FxHashMap;\nuse std::{\n    cell::RefCell, collections::hash_map::Entry, fmt::Write as _, io::Write, rc::Rc,\n    time::SystemTime,\n};\n\n/// A trait that can be used to forward console logs to an implementation.\npub trait Logger: Trace {\n    /// Log a trace message (`console.trace`). By default, passes the message and the\n    /// code block names of each stack trace frame to `log`.\n    ///\n    /// # Errors\n    /// Returning an error will throw an exception in JavaScript.\n    fn trace(&self, msg: String, state: &ConsoleState, context: &mut Context) -> JsResult<()> {\n        self.log(msg, state, context)?;\n\n        let stack_trace_dump = context\n            .stack_trace()\n            .map(|frame| frame.code_block().name())\n            .map(JsString::to_std_string_escaped)\n            .collect::<Vec<_>>();\n\n        for frame in stack_trace_dump {\n            self.log(frame, state, context)?;\n        }\n\n        Ok(())\n    }\n\n    /// Log a debug message (`console.debug`). By default, passes the message to `log`.\n    ///\n    /// # Errors\n    /// Returning an error will throw an exception in JavaScript.\n    fn debug(&self, msg: String, state: &ConsoleState, context: &mut Context) -> JsResult<()> {\n        self.log(msg, state, context)\n    }\n\n    /// Log a log message (`console.log`).\n    ///\n    /// # Errors\n    /// Returning an error will throw an exception in JavaScript.\n    fn log(&self, msg: String, state: &ConsoleState, context: &mut Context) -> JsResult<()>;\n\n    /// Log an info message (`console.info`).\n    ///\n    /// # Errors\n    /// Returning an error will throw an exception in JavaScript.\n    fn info(&self, msg: String, state: &ConsoleState, context: &mut Context) -> JsResult<()>;\n\n    /// Log a warning message (`console.warn`).\n    ///\n    /// # Errors\n    /// Returning an error will throw an exception in JavaScript.\n    fn warn(&self, msg: String, state: &ConsoleState, context: &mut Context) -> JsResult<()>;\n\n    /// Log an error message (`console.error`).\n    ///\n    /// # Errors\n    /// Returning an error will throw an exception in JavaScript.\n    fn error(&self, msg: String, state: &ConsoleState, context: &mut Context) -> JsResult<()>;\n}\n\n/// The default implementation for logging from the console.\n///\n/// Implements the [`Logger`] trait and output errors to stderr and all\n/// the others to stdout. Will add indentation based on the number of\n/// groups.\n#[derive(Debug, Trace, Finalize)]\npub struct DefaultLogger;\n\nimpl Logger for DefaultLogger {\n    #[inline]\n    fn log(&self, msg: String, state: &ConsoleState, _context: &mut Context) -> JsResult<()> {\n        let indent = state.indent();\n        writeln!(std::io::stdout(), \"{msg:>indent$}\").map_err(JsError::from_rust)\n    }\n\n    #[inline]\n    fn info(&self, msg: String, state: &ConsoleState, context: &mut Context) -> JsResult<()> {\n        self.log(msg, state, context)\n    }\n\n    #[inline]\n    fn warn(&self, msg: String, state: &ConsoleState, context: &mut Context) -> JsResult<()> {\n        self.log(msg, state, context)\n    }\n\n    #[inline]\n    fn error(&self, msg: String, state: &ConsoleState, _context: &mut Context) -> JsResult<()> {\n        let indent = state.indent();\n        writeln!(std::io::stderr(), \"{msg:>indent$}\").map_err(JsError::from_rust)\n    }\n}\n\n/// A logger that drops all logging. Useful for testing.\n#[derive(Debug, Trace, Finalize)]\npub struct NullLogger;\n\nimpl Logger for NullLogger {\n    #[inline]\n    fn log(&self, _: String, _: &ConsoleState, _: &mut Context) -> JsResult<()> {\n        Ok(())\n    }\n\n    #[inline]\n    fn info(&self, _: String, _: &ConsoleState, _: &mut Context) -> JsResult<()> {\n        Ok(())\n    }\n\n    #[inline]\n    fn warn(&self, _: String, _: &ConsoleState, _: &mut Context) -> JsResult<()> {\n        Ok(())\n    }\n\n    #[inline]\n    fn error(&self, _: String, _: &ConsoleState, _: &mut Context) -> JsResult<()> {\n        Ok(())\n    }\n}\n\n/// This represents the `console` formatter.\nfn formatter(data: &[JsValue], context: &mut Context) -> JsResult<String> {\n    fn to_string(value: &JsValue, _context: &mut Context) -> String {\n        match value.variant() {\n            JsVariant::String(s) => s.to_std_string_escaped(),\n            _ => value.display().to_string(),\n        }\n    }\n\n    match data {\n        [] => Ok(String::new()),\n        [val] => Ok(to_string(val, context)),\n        data => {\n            let mut formatted = String::new();\n            let mut arg_index = 1;\n            let target = data\n                .get_or_undefined(0)\n                .to_string(context)?\n                .to_std_string_escaped();\n            let mut chars = target.chars();\n            while let Some(c) = chars.next() {\n                if c == '%' {\n                    let fmt = chars.next().unwrap_or('%');\n                    match fmt {\n                        /* integer */\n                        'd' | 'i' => {\n                            let arg = match data.get_or_undefined(arg_index).to_numeric(context)? {\n                                Numeric::Number(r) => (r.floor() + 0.0).to_string(),\n                                Numeric::BigInt(int) => int.to_string(),\n                            };\n                            formatted.push_str(&arg);\n                            arg_index += 1;\n                        }\n                        /* float */\n                        'f' => {\n                            let arg = data.get_or_undefined(arg_index).to_number(context)?;\n                            let _ = write!(formatted, \"{arg:.6}\");\n                            arg_index += 1;\n                        }\n                        /* object: use internals mode for richer inspection */\n                        'o' | 'O' => {\n                            let arg = data.get_or_undefined(arg_index);\n                            formatted.push_str(&arg.display().internals(true).to_string());\n                            arg_index += 1;\n                        }\n                        /* string */\n                        's' => {\n                            let arg = data.get_or_undefined(arg_index);\n\n                            // If a JS value implements `toString()`, call it.\n                            let mut written = false;\n                            if let Some(obj) = arg.as_object()\n                                && let Ok(to_string) = obj.get(js_string!(\"toString\"), context)\n                                && let Some(to_string_fn) = to_string.as_function()\n                            {\n                                let arg =\n                                    to_string_fn.call(arg, &[], context)?.to_string(context)?;\n                                formatted.push_str(&arg.to_std_string_escaped());\n                                written = true;\n                            }\n\n                            if !written {\n                                let arg = arg.to_string(context)?.to_std_string_escaped();\n                                formatted.push_str(&arg);\n                            }\n\n                            arg_index += 1;\n                        }\n                        '%' => formatted.push('%'),\n                        c => {\n                            formatted.push('%');\n                            formatted.push(c);\n                        }\n                    }\n                } else {\n                    formatted.push(c);\n                }\n            }\n\n            /* unformatted data */\n            for rest in data.iter().skip(arg_index) {\n                formatted.push(' ');\n                formatted.push_str(&to_string(rest, context));\n            }\n\n            Ok(formatted)\n        }\n    }\n}\n\n/// The current state of the console, passed to the logger backend.\n/// This should not be copied or cloned. References are only valid\n/// for the current logging call.\n#[derive(Debug, Default, Trace, Finalize)]\npub struct ConsoleState {\n    /// The map of console counters, used in `console.count()`.\n    count_map: FxHashMap<JsString, u32>,\n\n    /// The map of console timers, used in `console.time`, `console.timeLog`\n    /// and `console.timeEnd`.\n    timer_map: FxHashMap<JsString, u128>,\n\n    /// The current list of groups. Groups should be indented, but some logging\n    /// libraries may want to use them in a different way.\n    groups: Vec<String>,\n}\n\nimpl ConsoleState {\n    /// Returns the indentation level that should be applied to logging.\n    #[must_use]\n    pub fn indent(&self) -> usize {\n        2 * self.groups.len()\n    }\n\n    /// Returns the current list of groups.\n    #[must_use]\n    pub fn groups(&self) -> &Vec<String> {\n        &self.groups\n    }\n\n    /// Returns the count map.\n    #[must_use]\n    pub fn count_map(&self) -> &FxHashMap<JsString, u32> {\n        &self.count_map\n    }\n\n    /// Returns the timer map.\n    #[must_use]\n    pub fn timer_map(&self) -> &FxHashMap<JsString, u128> {\n        &self.timer_map\n    }\n}\n\n/// This is the internal console object state.\n#[derive(Debug, Default, Trace, Finalize, JsData)]\npub struct Console {\n    state: ConsoleState,\n}\n\nimpl Console {\n    /// Name of the built-in `console` property.\n    pub const NAME: JsString = js_string!(\"console\");\n\n    /// Modify the context to include the `console` object.\n    ///\n    /// # Errors\n    /// This function will return an error if the property cannot be defined on the global object.\n    pub fn register_with_logger<L>(logger: L, context: &mut Context) -> JsResult<()>\n    where\n        L: Logger + 'static,\n    {\n        let console = Self::init_with_logger(logger, context);\n        context.register_global_property(\n            Self::NAME,\n            console,\n            Attribute::WRITABLE | Attribute::CONFIGURABLE,\n        )?;\n\n        Ok(())\n    }\n\n    /// Initializes the `console` with a special logger.\n    #[allow(clippy::too_many_lines)]\n    pub fn init_with_logger<L>(logger: L, context: &mut Context) -> JsObject\n    where\n        L: Logger + 'static,\n    {\n        fn console_method<L: Logger + 'static>(\n            f: fn(&JsValue, &[JsValue], &Console, &L, &mut Context) -> JsResult<JsValue>,\n            state: Rc<RefCell<Console>>,\n            logger: Rc<L>,\n        ) -> NativeFunction {\n            // SAFETY: `Console` doesn't contain types that need tracing.\n            unsafe {\n                NativeFunction::from_closure(move |this, args, context| {\n                    f(this, args, &state.borrow(), &logger, context)\n                })\n            }\n        }\n        fn console_method_mut<L: Logger + 'static>(\n            f: fn(&JsValue, &[JsValue], &mut Console, &L, &mut Context) -> JsResult<JsValue>,\n            state: Rc<RefCell<Console>>,\n            logger: Rc<L>,\n        ) -> NativeFunction {\n            // SAFETY: `Console` doesn't contain types that need tracing.\n            unsafe {\n                NativeFunction::from_closure(move |this, args, context| {\n                    f(this, args, &mut state.borrow_mut(), &logger, context)\n                })\n            }\n        }\n\n        let state = Rc::new(RefCell::new(Self::default()));\n        let logger = Rc::new(logger);\n\n        ObjectInitializer::with_native_data_and_proto(\n            Self::default(),\n            JsObject::with_object_proto(context.realm().intrinsics()),\n            context,\n        )\n        .property(\n            JsSymbol::to_string_tag(),\n            Self::NAME,\n            Attribute::CONFIGURABLE,\n        )\n        .function(\n            console_method(Self::assert, state.clone(), logger.clone()),\n            js_string!(\"assert\"),\n            0,\n        )\n        .function(\n            console_method_mut(Self::clear, state.clone(), logger.clone()),\n            js_string!(\"clear\"),\n            0,\n        )\n        .function(\n            console_method(Self::debug, state.clone(), logger.clone()),\n            js_string!(\"debug\"),\n            0,\n        )\n        .function(\n            console_method(Self::error, state.clone(), logger.clone()),\n            js_string!(\"error\"),\n            0,\n        )\n        .function(\n            console_method(Self::info, state.clone(), logger.clone()),\n            js_string!(\"info\"),\n            0,\n        )\n        .function(\n            console_method(Self::log, state.clone(), logger.clone()),\n            js_string!(\"log\"),\n            0,\n        )\n        .function(\n            console_method(Self::trace, state.clone(), logger.clone()),\n            js_string!(\"trace\"),\n            0,\n        )\n        .function(\n            console_method(Self::warn, state.clone(), logger.clone()),\n            js_string!(\"warn\"),\n            0,\n        )\n        .function(\n            console_method_mut(Self::count, state.clone(), logger.clone()),\n            js_string!(\"count\"),\n            0,\n        )\n        .function(\n            console_method_mut(Self::count_reset, state.clone(), logger.clone()),\n            js_string!(\"countReset\"),\n            0,\n        )\n        .function(\n            console_method_mut(Self::group, state.clone(), logger.clone()),\n            js_string!(\"group\"),\n            0,\n        )\n        .function(\n            console_method_mut(Self::group_collapsed, state.clone(), logger.clone()),\n            js_string!(\"groupCollapsed\"),\n            0,\n        )\n        .function(\n            console_method_mut(Self::group_end, state.clone(), logger.clone()),\n            js_string!(\"groupEnd\"),\n            0,\n        )\n        .function(\n            console_method_mut(Self::time, state.clone(), logger.clone()),\n            js_string!(\"time\"),\n            0,\n        )\n        .function(\n            console_method(Self::time_log, state.clone(), logger.clone()),\n            js_string!(\"timeLog\"),\n            0,\n        )\n        .function(\n            console_method_mut(Self::time_end, state.clone(), logger.clone()),\n            js_string!(\"timeEnd\"),\n            0,\n        )\n        .function(\n            console_method(Self::dir, state.clone(), logger.clone()),\n            js_string!(\"dir\"),\n            0,\n        )\n        .function(\n            console_method(Self::dir, state, logger.clone()),\n            js_string!(\"dirxml\"),\n            0,\n        )\n        .build()\n    }\n\n    /// Initializes the `console` built-in object.\n    pub fn init(context: &mut Context) -> JsObject {\n        Self::init_with_logger(DefaultLogger, context)\n    }\n\n    /// `console.assert(condition, ...data)`\n    ///\n    /// Prints a JavaScript value to the standard error if first argument evaluates to `false` or there\n    /// were no arguments.\n    ///\n    /// More information:\n    ///  - [MDN documentation][mdn]\n    ///  - [WHATWG `console` specification][spec]\n    ///\n    /// [spec]: https://console.spec.whatwg.org/#assert\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/API/console/assert\n    fn assert(\n        _: &JsValue,\n        args: &[JsValue],\n        console: &Self,\n        logger: &impl Logger,\n        context: &mut Context,\n    ) -> JsResult<JsValue> {\n        let assertion = args.first().is_some_and(JsValue::to_boolean);\n\n        if !assertion {\n            let mut args: Vec<JsValue> = args.iter().skip(1).cloned().collect();\n            let message = js_string!(\"Assertion failed\");\n            if args.is_empty() {\n                args.push(JsValue::new(message));\n            } else if !args[0].is_string() {\n                args.insert(0, JsValue::new(message));\n            } else {\n                let value = JsString::from(args[0].display().to_string());\n                let concat = js_string!(message.as_str(), js_str!(\": \"), &value);\n                args[0] = JsValue::new(concat);\n            }\n\n            logger.error(formatter(&args, context)?, &console.state, context)?;\n        }\n\n        Ok(JsValue::undefined())\n    }\n\n    /// `console.clear()`\n    ///\n    /// Removes all groups and clears console if possible.\n    ///\n    /// More information:\n    ///  - [MDN documentation][mdn]\n    ///  - [WHATWG `console` specification][spec]\n    ///\n    /// [spec]: https://console.spec.whatwg.org/#clear\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/API/console/clear\n    #[allow(clippy::unnecessary_wraps)]\n    fn clear(\n        _: &JsValue,\n        _: &[JsValue],\n        console: &mut Self,\n        _: &impl Logger,\n        _: &mut Context,\n    ) -> JsResult<JsValue> {\n        console.state.groups.clear();\n        Ok(JsValue::undefined())\n    }\n\n    /// `console.debug(...data)`\n    ///\n    /// Prints a JavaScript values with \"debug\" logLevel.\n    ///\n    /// More information:\n    ///  - [MDN documentation][mdn]\n    ///  - [WHATWG `console` specification][spec]\n    ///\n    /// [spec]: https://console.spec.whatwg.org/#debug\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/API/console/debug\n    fn debug(\n        _: &JsValue,\n        args: &[JsValue],\n        console: &Self,\n        logger: &impl Logger,\n        context: &mut Context,\n    ) -> JsResult<JsValue> {\n        logger.debug(formatter(args, context)?, &console.state, context)?;\n        Ok(JsValue::undefined())\n    }\n\n    /// `console.error(...data)`\n    ///\n    /// Prints a JavaScript values with \"error\" logLevel.\n    ///\n    /// More information:\n    ///  - [MDN documentation][mdn]\n    ///  - [WHATWG `console` specification][spec]\n    ///\n    /// [spec]: https://console.spec.whatwg.org/#error\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/API/console/error\n    fn error(\n        _: &JsValue,\n        args: &[JsValue],\n        console: &Self,\n        logger: &impl Logger,\n        context: &mut Context,\n    ) -> JsResult<JsValue> {\n        logger.error(formatter(args, context)?, &console.state, context)?;\n        Ok(JsValue::undefined())\n    }\n\n    /// `console.info(...data)`\n    ///\n    /// Prints a JavaScript values with \"info\" logLevel.\n    ///\n    /// More information:\n    ///  - [MDN documentation][mdn]\n    ///  - [WHATWG `console` specification][spec]\n    ///\n    /// [spec]: https://console.spec.whatwg.org/#info\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/API/console/info\n    fn info(\n        _: &JsValue,\n        args: &[JsValue],\n        console: &Self,\n        logger: &impl Logger,\n        context: &mut Context,\n    ) -> JsResult<JsValue> {\n        logger.info(formatter(args, context)?, &console.state, context)?;\n        Ok(JsValue::undefined())\n    }\n\n    /// `console.log(...data)`\n    ///\n    /// Prints a JavaScript values with \"log\" logLevel.\n    ///\n    /// More information:\n    ///  - [MDN documentation][mdn]\n    ///  - [WHATWG `console` specification][spec]\n    ///\n    /// [spec]: https://console.spec.whatwg.org/#log\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/API/console/log\n    fn log(\n        _: &JsValue,\n        args: &[JsValue],\n        console: &Self,\n        logger: &impl Logger,\n        context: &mut Context,\n    ) -> JsResult<JsValue> {\n        logger.log(formatter(args, context)?, &console.state, context)?;\n        Ok(JsValue::undefined())\n    }\n\n    /// `console.trace(...data)`\n    ///\n    /// Prints a stack trace with \"trace\" logLevel, optionally labelled by data.\n    ///\n    /// More information:\n    ///  - [MDN documentation][mdn]\n    ///  - [WHATWG `console` specification][spec]\n    ///\n    /// [spec]: https://console.spec.whatwg.org/#trace\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/API/console/trace\n    fn trace(\n        _: &JsValue,\n        args: &[JsValue],\n        console: &Self,\n        logger: &impl Logger,\n        context: &mut Context,\n    ) -> JsResult<JsValue> {\n        Logger::trace(logger, formatter(args, context)?, &console.state, context)?;\n        Ok(JsValue::undefined())\n    }\n\n    /// `console.warn(...data)`\n    ///\n    /// Prints a JavaScript values with \"warn\" logLevel.\n    ///\n    /// More information:\n    ///  - [MDN documentation][mdn]\n    ///  - [WHATWG `console` specification][spec]\n    ///\n    /// [spec]: https://console.spec.whatwg.org/#warn\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/API/console/warn\n    fn warn(\n        _: &JsValue,\n        args: &[JsValue],\n        console: &Self,\n        logger: &impl Logger,\n        context: &mut Context,\n    ) -> JsResult<JsValue> {\n        logger.warn(formatter(args, context)?, &console.state, context)?;\n        Ok(JsValue::undefined())\n    }\n\n    /// `console.count(label)`\n    ///\n    /// Prints number of times the function was called with that particular label.\n    ///\n    /// More information:\n    ///  - [MDN documentation][mdn]\n    ///  - [WHATWG `console` specification][spec]\n    ///\n    /// [spec]: https://console.spec.whatwg.org/#count\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/API/console/count\n    fn count(\n        _: &JsValue,\n        args: &[JsValue],\n        console: &mut Self,\n        logger: &impl Logger,\n        context: &mut Context,\n    ) -> JsResult<JsValue> {\n        let label = match args.first() {\n            Some(value) => value.to_string(context)?,\n            None => \"default\".into(),\n        };\n\n        let msg = format!(\"count {}:\", label.to_std_string_escaped());\n        let c = console.state.count_map.entry(label).or_insert(0);\n        *c += 1;\n\n        logger.info(format!(\"{msg} {c}\"), &console.state, context)?;\n        Ok(JsValue::undefined())\n    }\n\n    /// `console.countReset(label)`\n    ///\n    /// Resets the counter for label.\n    ///\n    /// More information:\n    ///  - [MDN documentation][mdn]\n    ///  - [WHATWG `console` specification][spec]\n    ///\n    /// [spec]: https://console.spec.whatwg.org/#countreset\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/API/countReset\n    fn count_reset(\n        _: &JsValue,\n        args: &[JsValue],\n        console: &mut Self,\n        logger: &impl Logger,\n        context: &mut Context,\n    ) -> JsResult<JsValue> {\n        let label = match args.first() {\n            Some(value) => value.to_string(context)?,\n            None => \"default\".into(),\n        };\n\n        console.state.count_map.remove(&label);\n\n        logger.warn(\n            format!(\"countReset {}\", label.to_std_string_escaped()),\n            &console.state,\n            context,\n        )?;\n\n        Ok(JsValue::undefined())\n    }\n\n    /// Returns current system time in ms.\n    fn system_time_in_ms() -> u128 {\n        let now = SystemTime::now();\n        now.duration_since(SystemTime::UNIX_EPOCH)\n            .expect(\"negative duration\")\n            .as_millis()\n    }\n\n    /// `console.time(label)`\n    ///\n    /// Starts the timer for given label.\n    ///\n    /// More information:\n    ///  - [MDN documentation][mdn]\n    ///  - [WHATWG `console` specification][spec]\n    ///\n    /// [spec]: https://console.spec.whatwg.org/#time\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/API/console/time\n    fn time(\n        _: &JsValue,\n        args: &[JsValue],\n        console: &mut Self,\n        logger: &impl Logger,\n        context: &mut Context,\n    ) -> JsResult<JsValue> {\n        let label = match args.first() {\n            Some(value) => value.to_string(context)?,\n            None => \"default\".into(),\n        };\n\n        if let Entry::Vacant(e) = console.state.timer_map.entry(label.clone()) {\n            let time = Self::system_time_in_ms();\n            e.insert(time);\n        } else {\n            logger.warn(\n                format!(\"Timer '{}' already exist\", label.to_std_string_escaped()),\n                &console.state,\n                context,\n            )?;\n        }\n\n        Ok(JsValue::undefined())\n    }\n\n    /// `console.timeLog(label, ...data)`\n    ///\n    /// Prints elapsed time for timer with given label.\n    ///\n    /// More information:\n    ///  - [MDN documentation][mdn]\n    ///  - [WHATWG `console` specification][spec]\n    ///\n    /// [spec]: https://console.spec.whatwg.org/#timelog\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/API/console/timeLog\n    fn time_log(\n        _: &JsValue,\n        args: &[JsValue],\n        console: &Self,\n        logger: &impl Logger,\n        context: &mut Context,\n    ) -> JsResult<JsValue> {\n        let label = match args.first() {\n            Some(value) => value.to_string(context)?,\n            None => \"default\".into(),\n        };\n\n        if let Some(t) = console.state.timer_map.get(&label) {\n            let time = Self::system_time_in_ms();\n            let mut concat = format!(\"{}: {} ms\", label.to_std_string_escaped(), time - t);\n            for msg in args.iter().skip(1) {\n                concat = concat + \" \" + &msg.display().to_string();\n            }\n            logger.log(concat, &console.state, context)?;\n        } else {\n            logger.warn(\n                format!(\"Timer '{}' doesn't exist\", label.to_std_string_escaped()),\n                &console.state,\n                context,\n            )?;\n        }\n\n        Ok(JsValue::undefined())\n    }\n\n    /// `console.timeEnd(label)`\n    ///\n    /// Removes the timer with given label.\n    ///\n    /// More information:\n    ///  - [MDN documentation][mdn]\n    ///  - [WHATWG `console` specification][spec]\n    ///\n    /// [spec]: https://console.spec.whatwg.org/#timeend\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/API/console/timeEnd\n    fn time_end(\n        _: &JsValue,\n        args: &[JsValue],\n        console: &mut Self,\n        logger: &impl Logger,\n        context: &mut Context,\n    ) -> JsResult<JsValue> {\n        let label = match args.first() {\n            Some(value) => value.to_string(context)?,\n            None => \"default\".into(),\n        };\n\n        if let Some(t) = console.state.timer_map.remove(&label) {\n            let time = Self::system_time_in_ms();\n            logger.info(\n                format!(\n                    \"{}: {} ms - timer removed\",\n                    label.to_std_string_escaped(),\n                    time - t\n                ),\n                &console.state,\n                context,\n            )?;\n        } else {\n            logger.warn(\n                format!(\"Timer '{}' doesn't exist\", label.to_std_string_escaped()),\n                &console.state,\n                context,\n            )?;\n        }\n\n        Ok(JsValue::undefined())\n    }\n\n    /// `console.group(...data)`\n    ///\n    /// Adds new group with name from formatted data to stack.\n    ///\n    /// More information:\n    ///  - [MDN documentation][mdn]\n    ///  - [WHATWG `console` specification][spec]\n    ///\n    /// [spec]: https://console.spec.whatwg.org/#group\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/API/console/group\n    fn group(\n        _: &JsValue,\n        args: &[JsValue],\n        console: &mut Self,\n        logger: &impl Logger,\n        context: &mut Context,\n    ) -> JsResult<JsValue> {\n        let group_label = formatter(args, context)?;\n\n        logger.info(format!(\"group: {group_label}\"), &console.state, context)?;\n        console.state.groups.push(group_label);\n\n        Ok(JsValue::undefined())\n    }\n\n    /// `console.groupCollapsed(...data)`\n    ///\n    /// Adds new group collapsed with name from formatted data to stack.\n    ///\n    /// More information:\n    ///  - [MDN documentation][mdn]\n    ///  - [WHATWG `console` specification][spec]\n    ///\n    /// [spec]: https://console.spec.whatwg.org/#groupcollapsed\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/API/console/groupcollapsed_static\n    fn group_collapsed(\n        _: &JsValue,\n        args: &[JsValue],\n        console: &mut Self,\n        logger: &impl Logger,\n        context: &mut Context,\n    ) -> JsResult<JsValue> {\n        Console::group(&JsValue::undefined(), args, console, logger, context)\n    }\n\n    /// `console.groupEnd(label)`\n    ///\n    /// Removes the last group from the stack.\n    ///\n    /// More information:\n    ///  - [MDN documentation][mdn]\n    ///  - [WHATWG `console` specification][spec]\n    ///\n    /// [spec]: https://console.spec.whatwg.org/#groupend\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/API/console/groupEnd\n    #[allow(clippy::unnecessary_wraps)]\n    fn group_end(\n        _: &JsValue,\n        _: &[JsValue],\n        console: &mut Self,\n        _: &impl Logger,\n        _: &mut Context,\n    ) -> JsResult<JsValue> {\n        console.state.groups.pop();\n\n        Ok(JsValue::undefined())\n    }\n\n    /// `console.dir(item, options)`\n    ///\n    /// Prints info about item\n    ///\n    /// More information:\n    ///  - [MDN documentation][mdn]\n    ///  - [WHATWG `console` specification][spec]\n    ///\n    /// [spec]: https://console.spec.whatwg.org/#dir\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/API/console/dir\n    #[allow(clippy::unnecessary_wraps)]\n    fn dir(\n        _: &JsValue,\n        args: &[JsValue],\n        console: &Self,\n        logger: &impl Logger,\n        context: &mut Context,\n    ) -> JsResult<JsValue> {\n        logger.info(\n            args.get_or_undefined(0).display_obj(true),\n            &console.state,\n            context,\n        )?;\n        Ok(JsValue::undefined())\n    }\n}\n"
  },
  {
    "path": "core/runtime/src/console/tests.rs",
    "content": "use super::{Console, ConsoleState, formatter};\nuse crate::test::{TestAction, run_test_actions, run_test_actions_with};\nuse crate::{Logger, NullLogger};\nuse boa_engine::{Context, JsError, JsResult, JsValue, js_string, property::Attribute};\nuse boa_gc::{Gc, GcRefCell};\nuse indoc::indoc;\n\n#[test]\nfn formatter_no_args_is_empty_string() {\n    run_test_actions([TestAction::inspect_context(|ctx| {\n        assert_eq!(formatter(&[], ctx).unwrap(), \"\");\n    })]);\n}\n\n#[test]\nfn formatter_empty_format_string_is_empty_string() {\n    run_test_actions([TestAction::inspect_context(|ctx| {\n        assert_eq!(formatter(&[JsValue::new(js_string!())], ctx).unwrap(), \"\");\n    })]);\n}\n\n#[test]\nfn formatter_format_without_args_renders_verbatim() {\n    run_test_actions([TestAction::inspect_context(|ctx| {\n        assert_eq!(\n            formatter(&[JsValue::new(js_string!(\"%d %s %% %f\"))], ctx).unwrap(),\n            \"%d %s %% %f\"\n        );\n    })]);\n}\n\n#[test]\nfn formatter_empty_format_string_concatenates_rest_of_args() {\n    run_test_actions([TestAction::inspect_context(|ctx| {\n        assert_eq!(\n            formatter(\n                &[\n                    JsValue::new(js_string!(\"\")),\n                    JsValue::new(js_string!(\"to powinno zostać\")),\n                    JsValue::new(js_string!(\"połączone\")),\n                ],\n                ctx\n            )\n            .unwrap(),\n            \" to powinno zostać połączone\"\n        );\n    })]);\n}\n\n#[test]\nfn formatter_utf_8_checks() {\n    run_test_actions([TestAction::inspect_context(|ctx| {\n        assert_eq!(\n            formatter(\n                &[\n                    JsValue::new(js_string!(\"Są takie chwile %dą %są tu%sów %привет%ź\")),\n                    JsValue::new(123),\n                    JsValue::new(1.23),\n                    JsValue::new(js_string!(\"ł\")),\n                ],\n                ctx\n            )\n            .unwrap(),\n            \"Są takie chwile 123ą 1.23ą tułów %привет%ź\"\n        );\n    })]);\n}\n\n#[test]\nfn formatter_trailing_format_leader_renders() {\n    run_test_actions([TestAction::inspect_context(|ctx| {\n        assert_eq!(\n            formatter(\n                &[\n                    JsValue::new(js_string!(\"%%%%%\")),\n                    JsValue::new(js_string!(\"|\"))\n                ],\n                ctx\n            )\n            .unwrap(),\n            \"%%% |\"\n        );\n    })]);\n}\n\n#[test]\n#[allow(clippy::approx_constant)]\nfn formatter_float_format_works() {\n    run_test_actions([TestAction::inspect_context(|ctx| {\n        assert_eq!(\n            formatter(&[JsValue::new(js_string!(\"%f\")), JsValue::new(3.1415)], ctx).unwrap(),\n            \"3.141500\"\n        );\n    })]);\n}\n\n#[test]\nfn console_log_cyclic() {\n    let mut context = Context::default();\n    let console = Console::init_with_logger(NullLogger, &mut context);\n    context\n        .register_global_property(Console::NAME, console, Attribute::all())\n        .unwrap();\n\n    run_test_actions_with(\n        [TestAction::run(indoc! {r#\"\n                let a = [1];\n                a[1] = a;\n                console.log(a);\n            \"#})],\n        &mut context,\n    );\n    // Should not stack overflow\n}\n\n/// A logger that records all log messages.\n#[derive(Clone, Debug, Default, boa_engine::Trace, boa_engine::Finalize)]\npub(crate) struct RecordingLogger {\n    pub log: Gc<GcRefCell<String>>,\n}\n\nimpl Logger for RecordingLogger {\n    fn log(&self, msg: String, state: &ConsoleState, _: &mut Context) -> JsResult<()> {\n        use std::fmt::Write;\n        let indent = state.indent();\n        writeln!(self.log.borrow_mut(), \"{msg:>indent$}\").map_err(JsError::from_rust)\n    }\n\n    fn info(&self, msg: String, state: &ConsoleState, context: &mut Context) -> JsResult<()> {\n        self.log(msg, state, context)\n    }\n\n    fn warn(&self, msg: String, state: &ConsoleState, context: &mut Context) -> JsResult<()> {\n        self.log(msg, state, context)\n    }\n\n    fn error(&self, msg: String, state: &ConsoleState, context: &mut Context) -> JsResult<()> {\n        self.log(msg, state, context)\n    }\n}\n\n/// Harness methods to be used in JS tests.\nconst TEST_HARNESS: &str = r#\"\nfunction assert_true(condition, message) {\n    if (!condition) {\n        throw new Error(`Assertion failed: ${message}`);\n    }\n}\nfunction assert_own_property(obj, prop) {\n    assert_true(\n        Object.prototype.hasOwnProperty.call(obj, prop),\n        `Expected ${prop.toString()} to be an own property`,\n    );\n}\nfunction assert_equals(actual, expected, message) {\n    assert_true(\n        actual === expected,\n        `${message} (actual: ${actual.toString()}, expected: ${expected.toString()})`,\n    );\n}\nfunction assert_throws_js(error, func) {\n    try {\n        func();\n    } catch (e) {\n        if (e instanceof error) {\n            return;\n        }\n        throw new Error(`Expected ${error.name} to be thrown, but got ${e.name}`);\n    }\n    throw new Error(`Expected ${error.name} to be thrown, but no exception was thrown`);\n}\n\n// To keep the tests as close to the WPT tests as possible, we define `self` to\n// be `globalThis`.\nconst self = globalThis;\n\"#;\n\n/// The WPT test `console/console-log-symbol.any.js`.\n#[test]\nfn wpt_log_symbol_any() {\n    let mut context = Context::default();\n    let logger = RecordingLogger::default();\n    Console::register_with_logger(logger.clone(), &mut context).unwrap();\n\n    run_test_actions_with(\n        [\n            TestAction::run(TEST_HARNESS),\n            TestAction::run(indoc! {r#\"\n            console.log(Symbol());\n            console.log(Symbol(\"abc\"));\n            console.log(Symbol.for(\"def\"));\n            console.log(Symbol.isConcatSpreadable);\n        \"#}),\n        ],\n        &mut context,\n    );\n\n    let logs = logger.log.borrow().clone();\n    assert_eq!(\n        logs,\n        indoc! { r#\"\n            Symbol()\n            Symbol(abc)\n            Symbol(def)\n            Symbol(Symbol.isConcatSpreadable)\n        \"# }\n    );\n}\n\n/// The WPT test `console/console-is-a-namespace.any.js`.\n#[test]\nfn wpt_console_is_a_namespace() {\n    let mut context = Context::default();\n    let logger = RecordingLogger::default();\n    Console::register_with_logger(logger.clone(), &mut context).unwrap();\n\n    run_test_actions_with(\n        [\n            TestAction::run(TEST_HARNESS),\n            // console exists on the global object\n            TestAction::run(indoc! {r#\"\n                assert_true(globalThis.hasOwnProperty(\"console\"));\n            \"#}),\n            // console has the right property descriptors\n            TestAction::run(indoc! {r#\"\n                const propDesc = Object.getOwnPropertyDescriptor(self, \"console\");\n                assert_equals(propDesc.writable, true, \"must be writable\");\n                assert_equals(propDesc.enumerable, false, \"must not be enumerable\");\n                assert_equals(propDesc.configurable, true, \"must be configurable\");\n                assert_equals(propDesc.value, console, \"must have the right value\");\n            \"#}),\n            // The prototype chain must be correct\n            TestAction::run(indoc! {r#\"\n                const prototype1 = Object.getPrototypeOf(console);\n                const prototype2 = Object.getPrototypeOf(prototype1);\n\n                assert_equals(Object.getOwnPropertyNames(prototype1).length, 0, \"The [[Prototype]] must have no properties\");\n                assert_equals(prototype2, Object.prototype, \"The [[Prototype]]'s [[Prototype]] must be %ObjectPrototype%\");\n            \"#}),\n        ],\n        &mut context,\n    );\n}\n\n/// The WPT test `console/console-label-conversion.any.js`.\n#[test]\nfn wpt_console_label_conversion() {\n    let mut context = Context::default();\n    let logger = RecordingLogger::default();\n    Console::register_with_logger(logger.clone(), &mut context).unwrap();\n\n    run_test_actions_with(\n        [\n            TestAction::run(TEST_HARNESS),\n            TestAction::run(indoc! {r#\"\n                const methods = ['count', 'countReset', 'time', 'timeLog', 'timeEnd'];\n            \"#}),\n            // console.${method}()'s label gets converted to string via label.toString() when label is an object\n            TestAction::run(indoc! {r#\"\n                for (const method of methods) {\n                    let labelToStringCalled = false;\n\n                    console[method]({\n                        toString() {\n                            labelToStringCalled = true;\n                        }\n                    });\n\n                    assert_true(labelToStringCalled, `${method}() must call toString() on label when label is an object`);\n                }\n            \"#}),\n            // ${method} must re-throw any exceptions thrown by label.toString() conversion\n            TestAction::run(indoc! {r#\"\n                for (const method of methods) {\n                    assert_throws_js(Error, () => {\n                        console[method]({\n                            toString() {\n                                throw new Error('conversion error');\n                            }\n                        });\n                    });\n                }\n            \"#}),\n        ],\n        &mut context,\n    );\n}\n\n/// The WPT test `console/console-namespace-object-class-string.any.js`.\n#[test]\nfn console_namespace_object_class_string() {\n    let mut context = Context::default();\n    let logger = RecordingLogger::default();\n    Console::register_with_logger(logger.clone(), &mut context).unwrap();\n\n    run_test_actions_with(\n        [\n            TestAction::run(TEST_HARNESS),\n            // @@toStringTag exists on the namespace object with the appropriate descriptor\n            TestAction::run(indoc! {r#\"\n                assert_own_property(console, Symbol.toStringTag);\n\n                const propDesc = Object.getOwnPropertyDescriptor(console, Symbol.toStringTag);\n                assert_equals(propDesc.value, \"console\", \"value\");\n                assert_equals(propDesc.writable, false, \"writable\");\n                assert_equals(propDesc.enumerable, false, \"enumerable\");\n                assert_equals(propDesc.configurable, true, \"configurable\");\n            \"#}),\n            // Object.prototype.toString applied to the namespace object\n            TestAction::run(indoc! {r#\"\n                assert_equals(console.toString(), \"[object console]\");\n                assert_equals(Object.prototype.toString.call(console), \"[object console]\");\n            \"#}),\n            // Object.prototype.toString applied after modifying the namespace object's @@toStringTag\n            TestAction::run(indoc! {r#\"\n                assert_own_property(console, Symbol.toStringTag, \"Precondition: @@toStringTag on the namespace object\");\n                // t.add_cleanup(() => {\n                //   Object.defineProperty(console, Symbol.toStringTag, { value: \"console\" });\n                // });\n\n                Object.defineProperty(console, Symbol.toStringTag, { value: \"Test\" });\n                assert_equals(console.toString(), \"[object Test]\");\n                assert_equals(Object.prototype.toString.call(console), \"[object Test]\");\n            \"#}),\n            // Object.prototype.toString applied after deleting @@toStringTag\n            TestAction::run(indoc! {r#\"\n                assert_own_property(console, Symbol.toStringTag, \"Precondition: @@toStringTag on the namespace object\");\n                // t.add_cleanup(() => {\n                //   Object.defineProperty(console, Symbol.toStringTag, { value: \"console\" });\n                // });\n\n                assert_true(delete console[Symbol.toStringTag]);\n                assert_equals(console.toString(), \"[object Object]\");\n                assert_equals(Object.prototype.toString.call(console), \"[object Object]\");\n            \"#}),\n        ],\n        &mut context,\n    );\n}\n\n#[test]\nfn console_log_arguments() {\n    let mut context = Context::default();\n    let logger = RecordingLogger::default();\n    Console::register_with_logger(logger.clone(), &mut context).unwrap();\n\n    run_test_actions_with(\n        [TestAction::run(indoc! {r#\"\n            function test() {\n                console.log(arguments);\n            }\n            test(\"a\", 42);\n        \"#})],\n        &mut context,\n    );\n\n    let logs = logger.log.borrow().clone();\n    assert_eq!(\n        logs,\n        indoc! { r#\"\n            [Arguments] {\n              0: \"a\",\n              1: 42\n            }\n        \"# }\n    );\n}\n\n#[test]\nfn console_log_regexp() {\n    let mut context = Context::default();\n    let logger = RecordingLogger::default();\n    Console::register_with_logger(logger.clone(), &mut context).unwrap();\n\n    run_test_actions_with(\n        [TestAction::run(indoc! {r#\"\n            console.log(/foo/gi);\n            console.log(/^hello$/m);\n            console.log(new RegExp(\"a/b\", \"g\"));\n            console.log(new RegExp(\"foo\\nbar\", \"m\"));\n        \"#})],\n        &mut context,\n    );\n\n    let logs = logger.log.borrow().clone();\n    assert_eq!(\n        logs,\n        indoc! { r#\"\n            /foo/gi\n            /^hello$/m\n            /a\\/b/g\n            /foo\\nbar/m\n        \"# }\n    );\n}\n\n#[test]\nfn console_log_date() {\n    let mut context = Context::default();\n    let logger = RecordingLogger::default();\n    Console::register_with_logger(logger.clone(), &mut context).unwrap();\n\n    run_test_actions_with(\n        [TestAction::run(indoc! {r#\"\n            console.log(new Date(\"Invalid\"));\n            console.log(new Date(0));\n        \"#})],\n        &mut context,\n    );\n\n    let logs = logger.log.borrow().clone();\n    assert_eq!(\n        logs,\n        indoc! { r#\"\n            Invalid Date\n            1970-01-01T00:00:00.000Z\n        \"# }\n    );\n}\n\n#[test]\nfn trace_with_stack_trace() {\n    let mut context = Context::default();\n    let logger = RecordingLogger::default();\n    Console::register_with_logger(logger.clone(), &mut context).unwrap();\n\n    run_test_actions_with(\n        [\n            TestAction::run(TEST_HARNESS),\n            TestAction::run(indoc! {r#\"\n            console.trace(\"one\");\n            a();\n\n            function a() {\n                b();\n            }\n            function b() {\n                console.trace(\"two\");\n            }\n        \"#}),\n        ],\n        &mut context,\n    );\n\n    let logs = logger.log.borrow().clone();\n    assert_eq!(\n        logs,\n        indoc! { r#\"\n            one\n            <main>\n            two\n            b\n            a\n            <main>\n        \"# }\n    );\n}\n"
  },
  {
    "path": "core/runtime/src/extensions.rs",
    "content": "//! This module contains all the Runtime extensions that can be registered.\n\nuse crate::{DefaultLogger, Logger};\nuse boa_engine::realm::Realm;\nuse boa_engine::{Context, JsResult};\nuse std::fmt::Debug;\n\n/// Optional registrable extension (with arguments) in the Boa Runtime should\n/// implement this.\npub trait RuntimeExtension: Debug {\n    /// Register this extension in the context using the specified Realm.\n    /// This consumes the extension options.\n    ///\n    /// # Errors\n    /// This should error if the extension was not able to register classes, modules or\n    /// functions in the context.\n    fn register(self, realm: Option<Realm>, context: &mut Context) -> JsResult<()>;\n}\n\n/// Register the Timeout/Interval functions.\n#[derive(Copy, Clone, Debug)]\npub struct TimeoutExtension;\n\nimpl RuntimeExtension for TimeoutExtension {\n    fn register(self, _realm: Option<Realm>, context: &mut Context) -> JsResult<()> {\n        crate::interval::register(context)\n    }\n}\n\n/// Register the `queueMicrotask` function.\n#[derive(Copy, Clone, Debug)]\npub struct MicrotaskExtension;\n\nimpl RuntimeExtension for MicrotaskExtension {\n    fn register(self, realm: Option<Realm>, context: &mut Context) -> JsResult<()> {\n        crate::microtask::register(realm, context)\n    }\n}\n\n/// Register the `TextEncoder` and `TextDecoder` classes.\n#[derive(Copy, Clone, Debug)]\npub struct EncodingExtension;\n\nimpl RuntimeExtension for EncodingExtension {\n    fn register(self, realm: Option<Realm>, context: &mut Context) -> JsResult<()> {\n        crate::text::register(realm, context)?;\n        Ok(())\n    }\n}\n\n/// Register the `structuredClone` function.\n#[derive(Copy, Clone, Debug)]\npub struct StructuredCloneExtension;\n\nimpl RuntimeExtension for StructuredCloneExtension {\n    fn register(self, realm: Option<Realm>, context: &mut Context) -> JsResult<()> {\n        crate::clone::register(realm, context)\n    }\n}\n\n/// Register the `atob` and `btoa` Base64 utility functions.\n#[derive(Copy, Clone, Debug)]\npub struct Base64Extension;\n\nimpl RuntimeExtension for Base64Extension {\n    fn register(self, realm: Option<Realm>, context: &mut Context) -> JsResult<()> {\n        crate::base64::register(realm, context)\n    }\n}\n\n/// Register the URL classes.\n#[cfg(feature = \"url\")]\n#[derive(Copy, Clone, Debug)]\npub struct UrlExtension;\n\n#[cfg(feature = \"url\")]\nimpl RuntimeExtension for UrlExtension {\n    fn register(self, realm: Option<Realm>, context: &mut Context) -> JsResult<()> {\n        crate::url::Url::register(realm, context)\n    }\n}\n\n/// Register the `Console` JavaScript object with the specified logger.\n/// Use [`ConsoleExtension::default()`] to register the console with a default logger.\n#[derive(Debug)]\npub struct ConsoleExtension<L: Logger>(pub L);\n\nimpl Default for ConsoleExtension<DefaultLogger> {\n    fn default() -> Self {\n        ConsoleExtension(DefaultLogger)\n    }\n}\n\nimpl<L: Logger + Debug + 'static> RuntimeExtension for ConsoleExtension<L> {\n    fn register(self, _realm: Option<Realm>, context: &mut Context) -> JsResult<()> {\n        crate::console::Console::register_with_logger(self.0, context)\n    }\n}\n\n/// Register the `Process` Javascript object.\n#[cfg(feature = \"process\")]\n#[derive(Copy, Clone, Debug)]\npub struct ProcessExtension;\n\n#[cfg(feature = \"process\")]\nimpl RuntimeExtension for ProcessExtension {\n    fn register(self, _realm: Option<Realm>, context: &mut Context) -> JsResult<()> {\n        crate::process::Process::register(context)\n    }\n}\n\n/// Register the `fetch` JavaScript API with the specified [`crate::fetch::Fetcher`].\n#[cfg(feature = \"fetch\")]\n#[derive(Debug)]\npub struct FetchExtension<F: crate::fetch::Fetcher>(pub F);\n\n#[cfg(feature = \"fetch\")]\nimpl<F: crate::fetch::Fetcher + Debug + 'static> RuntimeExtension for FetchExtension<F> {\n    fn register(self, realm: Option<Realm>, context: &mut Context) -> JsResult<()> {\n        crate::fetch::register(self.0, realm, context)\n    }\n}\n\n/// `AbortController` and `AbortSignal` extension.\n#[cfg(feature = \"fetch\")]\n#[derive(Copy, Clone, Debug)]\npub struct AbortControllerExtension;\n\n#[cfg(feature = \"fetch\")]\nimpl RuntimeExtension for AbortControllerExtension {\n    fn register(self, realm: Option<Realm>, context: &mut Context) -> JsResult<()> {\n        crate::abort::register(realm, context)\n    }\n}\n\n/// Register the `postMessage` JavaScript API with the specified\n/// [`crate::message::MessageSender`].\n#[derive(Debug)]\npub struct PostMessageExtension<S: crate::message::MessageSender>(pub S);\n\nimpl<S: crate::message::MessageSender + Debug + 'static> RuntimeExtension\n    for PostMessageExtension<S>\n{\n    fn register(self, realm: Option<Realm>, context: &mut Context) -> JsResult<()> {\n        crate::message::register(self.0, realm, context)\n    }\n}\n\nmacro_rules! decl_runtime_ext_tuple {\n    ($first_name: ident : $first_type: ident) => {\n        impl<$first_type: RuntimeExtension> RuntimeExtension for ($first_type,) {\n            fn register(self, realm: Option<Realm>, context: &mut Context) -> JsResult<()> {\n                RuntimeExtension::register(self.0, realm.clone(), context)?;\n                Ok(())\n            }\n        }\n    };\n    ($first_name: ident : $first_type: ident, $($name: ident : $type: ident),*) => {\n        impl<$first_type: RuntimeExtension, $($type: RuntimeExtension),*> RuntimeExtension for ($first_type, $($type),*) {\n            fn register(self, realm: Option<Realm>, context: &mut Context) -> JsResult<()> {\n                let ($first_name, $($name),*) = self;\n                RuntimeExtension::register($first_name, realm.clone(), context)?;\n                $( RuntimeExtension::register($name, realm.clone(), context)?; )*\n                Ok(())\n            }\n        }\n\n        decl_runtime_ext_tuple!($($name: $type),*);\n    };\n}\n\n// Implement RuntimeExtension for all tuples up to 12.\ndecl_runtime_ext_tuple!(a: A, b: B, c: C, d: D, e: E, f: F, g: G, h: H, i: I, j: J, k: K, l: L);\n"
  },
  {
    "path": "core/runtime/src/fetch/fetchers.rs",
    "content": "//! Module containing various implementations of the [`Fetcher`] trait.\n\nuse crate::fetch::Fetcher;\nuse crate::fetch::request::JsRequest;\nuse crate::fetch::response::JsResponse;\nuse boa_engine::{Context, Finalize, JsData, JsObject, JsResult, Trace, js_error};\nuse std::cell::RefCell;\nuse std::rc::Rc;\n\n/// Implementation of `Fetcher` which will always reject any fetch.\n#[derive(Clone, Debug, Trace, Finalize, JsData)]\npub struct ErrorFetcher;\n\nimpl Fetcher for ErrorFetcher {\n    async fn fetch(\n        self: Rc<Self>,\n        _request: JsRequest,\n        _signal: Option<JsObject>,\n        _context: &RefCell<&mut Context>,\n    ) -> JsResult<JsResponse> {\n        Err(js_error!(ReferenceError: \"ErrorFetcher used in fetch API.\"))\n    }\n}\n\n/// Implementation of `Fetcher` that uses the blocking `reqwest` library as the backend.\n#[cfg(feature = \"reqwest-blocking\")]\n#[derive(Default, Debug, Clone, Trace, Finalize, JsData)]\npub struct BlockingReqwestFetcher {\n    #[unsafe_ignore_trace]\n    client: reqwest::blocking::Client,\n}\n\n#[cfg(feature = \"reqwest-blocking\")]\nimpl Fetcher for BlockingReqwestFetcher {\n    async fn fetch(\n        self: Rc<Self>,\n        request: JsRequest,\n        signal: Option<JsObject>,\n        _context: &RefCell<&mut Context>,\n    ) -> JsResult<JsResponse> {\n        use boa_engine::{JsError, JsString};\n\n        if let Some(ref sig) = signal\n            && let Some(sig_ref) = sig.downcast_ref::<crate::abort::JsAbortSignal>()\n            && sig_ref.is_aborted()\n        {\n            return Err(JsError::from_opaque(\n                boa_engine::js_string!(\"AbortError\").into(),\n            ));\n        }\n\n        let request = request.into_inner();\n        let url = request.uri().to_string();\n        let req = self\n            .client\n            .request(request.method().clone(), &url)\n            .headers(request.headers().clone());\n\n        let req = req\n            .body(request.body().clone())\n            .build()\n            .map_err(JsError::from_rust)?;\n\n        let resp = self.client.execute(req).map_err(JsError::from_rust)?;\n\n        if let Some(ref sig) = signal\n            && let Some(sig_ref) = sig.downcast_ref::<crate::abort::JsAbortSignal>()\n            && sig_ref.is_aborted()\n        {\n            return Err(JsError::from_opaque(\n                boa_engine::js_string!(\"AbortError\").into(),\n            ));\n        }\n\n        let status = resp.status();\n        let headers = resp.headers().clone();\n        let bytes = resp.bytes().map_err(JsError::from_rust)?;\n        let mut builder = http::Response::builder().status(status.as_u16());\n\n        for k in headers.keys() {\n            for v in headers.get_all(k) {\n                builder = builder.header(k.as_str(), v);\n            }\n        }\n\n        builder\n            .body(bytes.to_vec())\n            .map_err(JsError::from_rust)\n            .map(|request| JsResponse::basic(JsString::from(url), request))\n    }\n}\n"
  },
  {
    "path": "core/runtime/src/fetch/headers.rs",
    "content": "//! The `Headers` JavaScript class, implemented as [`JsHeaders`].\n//!\n//! See <https://developer.mozilla.org/en-US/docs/Web/API/Headers>.\n#![allow(clippy::needless_pass_by_value)]\n\nuse boa_engine::interop::JsClass;\nuse boa_engine::object::builtins::{JsArray, TypedJsFunction};\nuse boa_engine::value::{Convert, TryFromJs};\nuse boa_engine::{\n    Context, Finalize, JsData, JsObject, JsResult, JsString, JsValue, Trace, boa_class, js_error,\n};\nuse http::header::HeaderMap as HttpHeaderMap;\nuse http::{HeaderName, HeaderValue};\nuse std::cell::RefCell;\nuse std::collections::BTreeMap;\nuse std::rc::Rc;\nuse std::str::FromStr;\n\n/// A callback function for the `forEach` method.\npub type ForEachCallback = TypedJsFunction<(JsString, JsString, JsObject), ()>;\n\n/// Converts a JavaScript string to a valid header name (or error).\n///\n/// # Errors\n/// If the key is not a valid header name, an error is returned.\n#[inline]\nfn to_header_name(key: impl AsRef<str>) -> JsResult<HeaderName> {\n    HeaderName::from_str(key.as_ref()).map_err(|_| js_error!(TypeError: \"Invalid header name.\"))\n}\n\n/// Trims leading and trailing HTTP whitespace from a header value.\n#[inline]\nfn normalize_header_value(value: &str) -> &str {\n    value.trim_matches(['\\t', '\\n', '\\r', ' '])\n}\n\n/// Converts a JavaScript string to a valid header value (or error).\n///\n/// # Errors\n/// If the value is not a valid header value, an error is returned.\n#[inline]\nfn to_header_value(value: impl AsRef<str>) -> JsResult<HeaderValue> {\n    normalize_header_value(value.as_ref())\n        .parse()\n        .map_err(|_| js_error!(TypeError: \"Invalid header value.\"))\n}\n\n/// A JavaScript wrapper for the `Headers` object.\n#[derive(Debug, Default, Clone, JsData, Trace, Finalize)]\npub struct JsHeaders {\n    #[unsafe_ignore_trace]\n    headers: Rc<RefCell<HttpHeaderMap>>,\n}\n\nimpl TryFromJs for JsHeaders {\n    fn try_from_js(value: &JsValue, context: &mut Context) -> JsResult<Self> {\n        JsHeaders::constructor(value.clone(), context)\n    }\n}\n\nimpl JsHeaders {\n    /// Creates a [`JsHeaders`] from an internal [`http::HeaderMap`]. Takes ownership\n    /// of the inner map.\n    #[must_use]\n    pub fn from_http(http: HttpHeaderMap) -> Self {\n        Self {\n            headers: Rc::new(RefCell::new(http)),\n        }\n    }\n\n    /// Returns a shared handle to the inner [`http::HeaderMap`].\n    #[must_use]\n    pub fn as_header_map(&self) -> Rc<RefCell<HttpHeaderMap>> {\n        self.headers.clone()\n    }\n\n    pub(crate) fn deep_clone(&self) -> Self {\n        Self {\n            headers: Rc::new(RefCell::new((*self.headers.borrow()).clone())),\n        }\n    }\n}\n\n#[boa_class(rename = \"Headers\")]\n#[boa(rename_all = \"camelCase\")]\nimpl JsHeaders {\n    #[boa(constructor)]\n    fn constructor(init: JsValue, context: &mut Context) -> JsResult<Self> {\n        let headers = JsHeaders::default();\n        if init.is_undefined() {\n            return Ok(headers);\n        }\n\n        // `init` can be a simple object literal with String values, an array of name-value\n        // pairs, where each pair is a 2-element string array; or an existing Headers object.\n        let mut h = headers.headers.borrow_mut();\n        if let Some(other_header) = init\n            .as_object()\n            .as_ref()\n            .and_then(JsObject::downcast_ref::<JsHeaders>)\n        {\n            for (key, value) in other_header.headers.borrow().iter() {\n                if h.contains_key(key) {\n                    h.append(key, value.clone());\n                } else {\n                    h.insert(key, value.clone());\n                }\n            }\n        } else if let Ok(init) = Vec::<(String, Convert<String>)>::try_from_js(&init, context) {\n            for (k, v) in init {\n                let key = to_header_name(k)?;\n                let value = to_header_value(&v.0)?;\n                if h.contains_key(&key) {\n                    h.append(key, value);\n                } else {\n                    h.insert(key, value);\n                }\n            }\n        } else if let Ok(init) = BTreeMap::<String, Convert<String>>::try_from_js(&init, context) {\n            for (k, v) in init {\n                let key = to_header_name(k)?;\n                let value = to_header_value(&v.0)?;\n                if h.contains_key(&key) {\n                    h.append(key, value);\n                } else {\n                    h.insert(key, value);\n                }\n            }\n        } else {\n            return Err(js_error!(TypeError: \"Cannot convert init to header object.\"));\n        }\n        drop(h);\n\n        Ok(headers)\n    }\n\n    /// Appends a new value onto an existing header inside a Headers object,\n    /// or adds the header if it does not already exist.\n    ///\n    /// # Errors\n    /// If the key or value is not valid ASCII, an error is returned.\n    pub fn append(&mut self, key: Convert<String>, value: Convert<String>) -> JsResult<()> {\n        let key = to_header_name(key.as_ref())?;\n        let value = to_header_value(value.as_ref())?;\n        if !self.headers.borrow_mut().append(&key, value.clone()) {\n            self.headers.borrow_mut().insert(key, value);\n        }\n        Ok(())\n    }\n\n    /// Deletes a header from a Headers object.\n    ///\n    /// # Errors\n    /// If the key is not valid ASCII, an error is returned.\n    pub fn delete(&mut self, key: Convert<String>) -> JsResult<()> {\n        let key = to_header_name(key.as_ref())?;\n        self.headers.borrow_mut().remove(key);\n        Ok(())\n    }\n\n    /// Returns an iterator allowing to go through all key/value pairs contained in this object.\n    // TODO: This should return a JsIterator, but not such thing exists yet.\n    pub fn entries(&self, context: &mut Context) -> JsValue {\n        JsArray::from_iter(\n            self.headers\n                .borrow()\n                .iter()\n                .map(|(k, v)| {\n                    let k: JsValue = JsString::from(k.as_str()).into();\n                    let v: JsValue = JsString::from(v.to_str().unwrap_or_default()).into();\n                    JsArray::from_iter([k, v], context).into()\n                })\n                .collect::<Vec<_>>(),\n            context,\n        )\n        .into()\n    }\n\n    /// Executes a provided function once for each key/value pair in the Headers object.\n    ///\n    /// # Errors\n    /// If the callback function returns an error, it is returned.\n    #[allow(clippy::needless_pass_by_value)]\n    #[boa(method)]\n    pub fn for_each(\n        this: JsClass<Self>,\n        callback: ForEachCallback,\n        this_arg: Option<JsValue>,\n        context: &mut Context,\n    ) -> JsResult<()> {\n        let object = this.inner().upcast();\n        let this_arg = this_arg.unwrap_or_default();\n        for (k, v) in this.clone_inner().headers.borrow().iter() {\n            let k = JsString::from(k.as_str());\n            let v = JsString::from(v.to_str().unwrap_or(\"\"));\n            callback.call_with_this(&this_arg, context, (v, k, object.clone()))?;\n        }\n        Ok(())\n    }\n\n    /// Returns a byte string of all the values in a header within a Headers object\n    /// with a given name. If the requested header doesn't exist in the Headers\n    /// object, it returns null.\n    ///\n    /// # Errors\n    /// If the key is not valid ASCII, an error is returned.\n    pub fn get(&self, key: JsValue, context: &mut Context) -> JsResult<JsValue> {\n        let key: Convert<String> = Convert::try_from_js(&key, context)?;\n        let name = to_header_name(key.as_ref())?;\n        let value = self\n            .headers\n            .borrow()\n            .get_all(name.clone())\n            .into_iter()\n            .map(|v| v.to_str().unwrap_or(\"\"))\n            .fold(None, |mut acc, v| {\n                let str = acc.get_or_insert_with(String::new);\n                if !str.is_empty() {\n                    str.push_str(\", \");\n                }\n                str.push_str(v);\n                acc\n            });\n\n        Ok(value.map_or_else(JsValue::null, |v| JsString::from(v).into()))\n    }\n\n    /// Returns an array containing the values of all Set-Cookie headers associated with a response.\n    fn get_set_cookie(&self) -> Vec<JsString> {\n        self.headers\n            .borrow()\n            .get_all(\"Set-Cookie\")\n            .into_iter()\n            .map(|v| JsString::from(v.to_str().unwrap_or(\"\")))\n            .collect()\n    }\n\n    /// Returns a boolean stating whether a Headers object contains a certain header.\n    ///\n    /// # Errors\n    /// If the key isn't a valid header name, this will error.\n    pub fn has(&self, key: Convert<String>) -> JsResult<bool> {\n        let key = to_header_name(key.as_ref())?;\n        Ok(self.headers.borrow().get(key).is_some())\n    }\n\n    /// Returns an iterator allowing you to go through all keys of the key/value pairs\n    /// contained in this object.\n    #[allow(clippy::unused_self)]\n    fn keys(&self) -> Vec<JsString> {\n        self.headers\n            .borrow()\n            .keys()\n            .map(|k| JsString::from(k.as_str()))\n            .collect()\n    }\n\n    /// Sets a new value for an existing header inside a Headers object, or adds the\n    /// header if it does not already exist.\n    fn set(&mut self, key: Convert<String>, value: Convert<String>) -> JsResult<()> {\n        let key = to_header_name(key.as_ref())?;\n        let value = to_header_value(value.as_ref())?;\n        self.headers.borrow_mut().insert(key, value);\n        Ok(())\n    }\n\n    fn values(&self) -> Vec<JsString> {\n        self.headers\n            .borrow()\n            .values()\n            .map(|v| JsString::from(v.to_str().unwrap_or(\"\")))\n            .collect()\n    }\n}\n"
  },
  {
    "path": "core/runtime/src/fetch/mod.rs",
    "content": "//! Boa's implementation of JavaScript's `fetch` function.\n//!\n//! More information:\n//!  - [MDN documentation][mdn]\n//!  - [WHATWG `fetch` specification][spec]\n//!\n//! [spec]: https://fetch.spec.whatwg.org/\n//! [mdn]: https://developer.mozilla.org/en-US/docs/Web/API/fetch\n\nuse crate::fetch::headers::JsHeaders;\nuse crate::fetch::request::{JsRequest, RequestInit};\nuse crate::fetch::response::JsResponse;\nuse boa_engine::class::Class;\nuse boa_engine::object::FunctionObjectBuilder;\nuse boa_engine::object::builtins::JsArray;\nuse boa_engine::property::PropertyDescriptor;\nuse boa_engine::realm::Realm;\nuse boa_engine::{\n    Context, Finalize, JsData, JsError, JsObject, JsResult, JsString, JsSymbol, JsValue,\n    NativeObject, Trace, boa_module, js_error, js_string, native_function::NativeFunction,\n};\nuse either::Either;\nuse http::{HeaderName, HeaderValue, Request as HttpRequest, Request};\nuse std::cell::RefCell;\nuse std::rc::Rc;\n\npub mod headers;\npub mod request;\npub mod response;\npub mod tests;\n\nmod fetchers;\n\n#[doc(inline)]\npub use fetchers::*;\n\n/// A trait for backend implementation of an HTTP fetcher.\n// TODO: consider implementing an async version of this.\npub trait Fetcher: NativeObject {\n    /// Resolve a string to a URL. This is used when a string (e.g., the first argument to\n    /// `fetch()`) is passed, and we need resolution. Some cases require resolution of\n    /// a relative path, for example (to the \"page\" base URL).\n    /// By default, this will return the `URI` as is.\n    ///\n    /// # Errors\n    /// This function should return an error if the URL cannot be handled by the [`Fetcher`].\n    fn resolve_uri(&self, uri: String, _context: &mut Context) -> JsResult<String> {\n        Ok(uri)\n    }\n\n    /// Perform the Fetch execution, taking a [`request::JsRequest`] and returning a\n    /// [`response::JsResponse`].\n    ///\n    /// # Errors\n    /// Any errors returned by the HTTP implementation must conform to [`JsError`].\n    #[expect(async_fn_in_trait, reason = \"all our APIs are single-threaded\")]\n    async fn fetch(\n        self: Rc<Self>,\n        request: JsRequest,\n        signal: Option<JsObject>,\n        context: &RefCell<&mut Context>,\n    ) -> JsResult<JsResponse>;\n}\n\n/// A reference counted pointer to a `Fetcher` implementation. This is so we can\n/// add this to the context, but we need to be able to keep an `Rc<>` structure\n/// to make API calls.\n#[derive(Debug, Trace, Finalize, JsData)]\nstruct FetcherRc<T: Fetcher>(#[unsafe_ignore_trace] pub Rc<T>);\n\nimpl<T: Fetcher> Clone for FetcherRc<T> {\n    fn clone(&self) -> Self {\n        Self(self.0.clone())\n    }\n}\n\n/// Get a Fetcher instance from the context.\nfn get_fetcher<T: Fetcher>(context: &mut Context) -> JsResult<Rc<T>> {\n    // Try fetching from the context first, then the current realm. Else fail.\n    let Some(fetcher) = context.get_data::<FetcherRc<T>>().cloned().or_else(|| {\n        context\n            .realm()\n            .host_defined()\n            .get::<FetcherRc<T>>()\n            .cloned()\n    }) else {\n        return Err(\n            js_error!(Error: \"Implementation of fetch requires a fetcher registered in the context\"),\n        );\n    };\n\n    Ok(fetcher.0.clone())\n}\n\nfn check_abort(signal: Option<&JsObject>, context: &mut Context) -> JsResult<()> {\n    if let Some(signal_obj) = signal\n        && let Some(signal_ref) = signal_obj.downcast_ref::<crate::abort::JsAbortSignal>()\n        && signal_ref.is_aborted()\n    {\n        return Err(JsError::from_opaque(signal_ref.abort_reason(context)));\n    }\n    Ok(())\n}\n\n/// The `fetch` function internals.\nasync fn fetch_inner<T: Fetcher>(\n    resource: Either<JsString, JsObject>,\n    options: Option<RequestInit>,\n    context: &RefCell<&mut Context>,\n) -> JsResult<JsValue> {\n    let fetcher = get_fetcher::<T>(&mut context.borrow_mut())?;\n\n    let (options, signal) = match options {\n        Some(mut opts) => {\n            let sig = opts.take_signal();\n            (Some(opts), sig)\n        }\n        None => (None, None),\n    };\n\n    check_abort(signal.as_ref(), &mut context.borrow_mut())?;\n\n    // The resource parsing is complicated, so we parse it in Rust here (instead of relying on\n    // `TryFromJs` and friends).\n    let request: Request<Vec<u8>> = match resource {\n        Either::Left(url) => {\n            let url = url.to_std_string().map_err(JsError::from_rust)?;\n            let url = fetcher\n                .resolve_uri(url, &mut context.borrow_mut())\n                .map_err(JsError::from_rust)?;\n\n            let r = HttpRequest::get(url).body(Vec::new());\n            r.map_err(JsError::from_rust)?\n        }\n        Either::Right(request) => {\n            // This can be a [`JsRequest`] object.\n            let Ok(request) = request.downcast::<JsRequest>() else {\n                return Err(js_error!(TypeError: \"Resource must be a URL or Request object\"));\n            };\n            let Ok(request_ref) = request.try_borrow() else {\n                return Err(js_error!(TypeError: \"Request object is already in use\"));\n            };\n\n            request_ref.data().inner().clone()\n        }\n    };\n\n    let mut request = if let Some(options) = options {\n        options.into_request_builder(Some(request))?\n    } else {\n        request\n    };\n\n    // Add the `Accept-Language` which should be automatically included, unless specified.\n    if !request.headers().contains_key(\n        \"accept-language\"\n            .parse::<HeaderName>()\n            .map_err(JsError::from_rust)?,\n    ) {\n        let lang = HeaderValue::from_static(\"en-US\");\n        request.headers_mut().append(\"Accept-Language\", lang);\n    }\n\n    let response = fetcher\n        .fetch(JsRequest::from(request), signal.clone(), context)\n        .await?;\n\n    check_abort(signal.as_ref(), &mut context.borrow_mut())?;\n\n    let result = Class::from_data(response, &mut context.borrow_mut())?;\n    Ok(result.into())\n}\n\n/// JavaScript module containing the fetch types and functions.\n#[boa_module]\npub mod js_module {\n    use crate::fetch::request::RequestInit;\n    use crate::fetch::{Fetcher, fetch_inner};\n    use boa_engine::object::builtins::JsPromise;\n    use boa_engine::{Context, JsObject, JsString};\n    use either::Either;\n\n    type JsHeaders = super::JsHeaders;\n    type JsRequest = super::JsRequest;\n    type JsResponse = super::JsResponse;\n\n    /// The `fetch` function.\n    ///\n    /// # Errors\n    /// If the fetcher is not registered in the context, an error is returned.\n    /// This function will also return any error that the fetcher returns, or\n    /// any conversion to/from JavaScript types.\n    pub fn fetch<T: Fetcher>(\n        resource: Either<JsString, JsObject>,\n        options: Option<RequestInit>,\n        context: &mut Context,\n    ) -> JsPromise {\n        JsPromise::from_async_fn(\n            async move |context| fetch_inner::<T>(resource, options, context).await,\n            context,\n        )\n    }\n}\n\n#[doc(inline)]\npub use js_module::fetch;\n\nfn headers_iterator(this: &JsValue, _: &[JsValue], context: &mut Context) -> JsResult<JsValue> {\n    let this_object = this.as_object();\n    let headers = this_object\n        .as_ref()\n        .and_then(JsObject::downcast_ref::<JsHeaders>)\n        .ok_or_else(|| {\n            js_error!(TypeError: \"`Headers.prototype[Symbol.iterator]` requires a `Headers` object\")\n        })?;\n\n    let entries = headers.entries(context);\n    let entries_array = JsArray::from_object(entries.to_object(context)?)?;\n    entries_array.values(context)\n}\n\n/// Register the `fetch` function in the realm, as well as ALL supporting classes.\n/// Pass `None` as the realm to register globally.\n///\n/// # Errors\n/// If any of the classes fail to register, an error is returned.\npub fn register<F: Fetcher>(\n    fetcher: F,\n    realm: Option<Realm>,\n    context: &mut Context,\n) -> JsResult<()> {\n    if let Some(ref realm) = realm {\n        realm.host_defined_mut().insert(FetcherRc(Rc::new(fetcher)));\n    } else {\n        context.insert_data(FetcherRc(Rc::new(fetcher)));\n    }\n    js_module::boa_register::<F>(realm.clone(), context)?;\n\n    // TODO(#4688): Replace this manual `[Symbol.iterator]` wiring once `#[boa(class)]`\n    // supports symbol-named methods.\n    let headers_proto = match realm {\n        Some(realm) => realm.get_class::<JsHeaders>(),\n        None => context.get_global_class::<JsHeaders>(),\n    }\n    .ok_or_else(|| js_error!(Error: \"Headers class should be registered\"))?\n    .prototype();\n\n    let iterator = FunctionObjectBuilder::new(\n        context.realm(),\n        NativeFunction::from_fn_ptr(headers_iterator),\n    )\n    .name(js_string!(\"[Symbol.iterator]\"))\n    .length(0)\n    .constructor(false)\n    .build();\n\n    headers_proto.define_property_or_throw(\n        JsSymbol::iterator(),\n        PropertyDescriptor::builder()\n            .value(iterator)\n            .writable(true)\n            .enumerable(false)\n            .configurable(true),\n        context,\n    )?;\n\n    Ok(())\n}\n"
  },
  {
    "path": "core/runtime/src/fetch/request.rs",
    "content": "//! The `Request` JavaScript class and adjacent types, implemented as [`JsRequest`].\n//!\n//! See the [Request interface documentation][mdn] for more information.\n//!\n//! [mdn]: https://developer.mozilla.org/en-US/docs/Web/API/Request\nuse super::HttpRequest;\nuse super::headers::JsHeaders;\nuse boa_engine::value::{Convert, TryFromJs};\nuse boa_engine::{\n    Finalize, JsData, JsObject, JsResult, JsString, JsValue, Trace, boa_class, js_error,\n};\nuse either::Either;\nuse std::mem;\n\n/// A [RequestInit][mdn] object. This is a JavaScript object (not a\n/// class) that can be used as options for creating a [`JsRequest`].\n///\n/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/API/RequestInit\n// TODO: This class does not contain all fields that are defined in the spec.\n#[derive(Debug, Clone, TryFromJs, Trace, Finalize)]\npub struct RequestInit {\n    body: Option<JsValue>,\n    headers: Option<JsHeaders>,\n    method: Option<Convert<JsString>>,\n    signal: Option<JsObject>,\n}\n\nimpl RequestInit {\n    /// Takes the abort signal from the options, if present.\n    pub fn take_signal(&mut self) -> Option<JsObject> {\n        self.signal.take()\n    }\n\n    /// Create an [`http::request::Builder`] object and return both the\n    /// body specified by JavaScript and the builder.\n    ///\n    /// # Errors\n    /// If the body is not a valid type, an error is returned.\n    pub fn into_request_builder(\n        mut self,\n        request: Option<HttpRequest<Vec<u8>>>,\n    ) -> JsResult<HttpRequest<Vec<u8>>> {\n        let mut builder = HttpRequest::builder();\n        let mut request_body = Vec::new();\n        if let Some(r) = request {\n            let (parts, body) = r.into_parts();\n            builder = builder\n                .method(parts.method)\n                .uri(parts.uri)\n                .version(parts.version);\n\n            for (key, value) in &parts.headers {\n                builder = builder.header(key, value);\n            }\n            request_body = body;\n        }\n\n        if let Some(headers) = self.headers.take() {\n            for (k, v) in headers.as_header_map().borrow().iter() {\n                builder = builder.header(k, v);\n            }\n        }\n\n        if let Some(Convert(ref method)) = self.method.take() {\n            builder = builder.method(method.to_std_string().map_err(\n                |_| js_error!(TypeError: \"Request constructor: {} is an invalid method\", method.to_std_string_escaped()),\n            )?.as_str());\n        }\n\n        if let Some(body) = &self.body {\n            // TODO: add more support types.\n            if let Some(body) = body.as_string() {\n                let body = body.to_std_string().map_err(\n                    |_| js_error!(TypeError: \"Request constructor: body is not a valid string\"),\n                )?;\n                request_body = body.into_bytes();\n            } else {\n                return Err(\n                    js_error!(TypeError: \"Request constructor: body is not a supported type\"),\n                );\n            }\n        }\n\n        builder\n            .body(request_body)\n            .map_err(|_| js_error!(Error: \"Cannot construct request\"))\n    }\n}\n\n/// The JavaScript `Request` class.\n///\n/// The `Request` interface of the [Fetch API][mdn] represents a resource request.\n///\n/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API\n#[derive(Clone, Debug, JsData, Trace, Finalize)]\npub struct JsRequest {\n    #[unsafe_ignore_trace]\n    inner: HttpRequest<Vec<u8>>,\n}\n\nimpl JsRequest {\n    /// Get the inner `http::Request` object. This drops the body (if any).\n    pub fn into_inner(mut self) -> HttpRequest<Vec<u8>> {\n        mem::replace(&mut self.inner, HttpRequest::new(Vec::new()))\n    }\n\n    /// Get a reference to the inner `http::Request` object.\n    pub fn inner(&self) -> &HttpRequest<Vec<u8>> {\n        &self.inner\n    }\n\n    /// Get the URI of the request.\n    pub fn uri(&self) -> &http::Uri {\n        self.inner.uri()\n    }\n\n    /// Create a [`JsRequest`] instance from JavaScript arguments, similar to\n    /// calling its constructor in JavaScript.\n    ///\n    /// # Errors\n    /// If the URI is invalid, an error is returned.\n    pub fn create_from_js(\n        input: Either<JsString, JsRequest>,\n        options: Option<RequestInit>,\n    ) -> JsResult<Self> {\n        let request = match input {\n            Either::Left(uri) => {\n                let uri = http::Uri::try_from(\n                    uri.to_std_string()\n                        .map_err(|_| js_error!(URIError: \"URI cannot have unpaired surrogates\"))?,\n                )\n                .map_err(|_| js_error!(URIError: \"Invalid URI\"))?;\n                http::request::Request::builder()\n                    .uri(uri)\n                    .body(Vec::<u8>::new())\n                    .map_err(|_| js_error!(Error: \"Cannot construct request\"))?\n            }\n            Either::Right(r) => r.into_inner(),\n        };\n\n        if let Some(options) = options {\n            let inner = options.into_request_builder(Some(request))?;\n            Ok(Self { inner })\n        } else {\n            Ok(Self { inner: request })\n        }\n    }\n}\n\nimpl From<HttpRequest<Vec<u8>>> for JsRequest {\n    fn from(inner: HttpRequest<Vec<u8>>) -> Self {\n        Self { inner }\n    }\n}\n\n#[boa_class(rename = \"Request\")]\n#[boa(rename_all = \"camelCase\")]\nimpl JsRequest {\n    /// # Errors\n    /// Will return an error if the URL or any underlying error occurred in the\n    /// context.\n    #[boa(constructor)]\n    pub fn constructor(\n        input: Either<JsString, JsObject>,\n        options: Option<RequestInit>,\n    ) -> JsResult<Self> {\n        // Need to use a match as `Either::map_right` does not have an equivalent\n        // `Either::map_right_ok`.\n        let input = match input {\n            Either::Right(r) => {\n                if let Ok(request) = r.clone().downcast::<JsRequest>() {\n                    Either::Right(request.borrow().data().clone())\n                } else {\n                    return Err(js_error!(TypeError: \"invalid input argument\"));\n                }\n            }\n            Either::Left(i) => Either::Left(i),\n        };\n        JsRequest::create_from_js(input, options)\n    }\n\n    #[boa(rename = \"clone\")]\n    fn clone_request(&self) -> Self {\n        Self {\n            inner: self.inner.clone(),\n        }\n    }\n}\n"
  },
  {
    "path": "core/runtime/src/fetch/response.rs",
    "content": "//! Module containing the `Response` JavaScript class and its helpers, implemented as\n//! [`JsResponse`].\n//!\n//! See the [Response interface documentation][mdn] for more information.\n//!\n//! [mdn]: https://developer.mozilla.org/en-US/docs/Web/API/Response\n\nuse crate::fetch::headers::JsHeaders;\nuse boa_engine::object::builtins::{JsPromise, JsUint8Array};\nuse boa_engine::value::{Convert, TryFromJs, TryIntoJs};\nuse boa_engine::{\n    Context, JsData, JsNativeError, JsResult, JsString, JsValue, boa_class, js_error, js_str,\n    js_string,\n};\nuse boa_gc::{Finalize, Trace};\nuse http::{HeaderName, HeaderValue, StatusCode};\nuse std::rc::Rc;\n\n/// The type read-only property of the Response interface contains the type of the\n/// response. The type determines whether scripts are able to access the response body\n/// and headers.\n///\n/// See <https://developer.mozilla.org/en-US/docs/Web/API/Response/type>.\n#[derive(Debug, Copy, Clone)]\npub enum ResponseType {\n    /// This applies in any of the following cases:\n    ///\n    /// The request is same-origin.\n    /// The requested URL's scheme is `data:`.\n    /// The request's mode is `navigate` or `websocket`.\n    ///\n    /// With this type, all response headers are exposed except Set-Cookie.\n    Basic,\n\n    /// The request was cross-origin and was successfully processed using CORS. With this\n    /// type, only CORS-safelisted response headers are exposed.\n    Cors,\n\n    /// The default type for responses created via the `Response()` constructor or\n    /// `Response.json()`. Equivalent to `Basic` in terms of header exposure.\n    Default,\n\n    /// A network error occurred. The status property is set to 0, `body` is null, headers\n    /// are empty and immutable.\n    Error,\n\n    /// A response to a cross-origin request whose mode was set to no-cors. The status\n    /// property is set to 0, `body` is null, headers are empty and immutable.\n    Opaque,\n\n    /// A response to a request whose redirect option was set to manual and which was\n    /// redirected by the server. The status property is set to 0, `body` is null, headers\n    /// are empty and immutable.\n    OpaqueRedirect,\n}\n\nimpl ResponseType {\n    /// Return the JavaScript String representing this response type.\n    #[must_use]\n    pub fn to_string(self) -> JsString {\n        match self {\n            ResponseType::Basic => js_string!(\"basic\"),\n            ResponseType::Cors => js_string!(\"cors\"),\n            ResponseType::Default => js_string!(\"default\"),\n            ResponseType::Error => js_string!(\"error\"),\n            ResponseType::Opaque => js_string!(\"opaque\"),\n            ResponseType::OpaqueRedirect => js_string!(\"opaqueredirect\"),\n        }\n    }\n}\n\nimpl TryFromJs for ResponseType {\n    fn try_from_js(value: &JsValue, context: &mut Context) -> JsResult<Self> {\n        let value_str = value.to_string(context)?;\n        if value_str == js_str!(\"basic\") {\n            Ok(ResponseType::Basic)\n        } else if value_str == js_str!(\"cors\") {\n            Ok(ResponseType::Cors)\n        } else if value_str == js_str!(\"default\") {\n            Ok(ResponseType::Default)\n        } else if value_str == js_str!(\"error\") {\n            Ok(ResponseType::Error)\n        } else if value_str == js_str!(\"opaque\") {\n            Ok(ResponseType::Opaque)\n        } else if value_str == js_str!(\"opaqueredirect\") {\n            Ok(ResponseType::OpaqueRedirect)\n        } else {\n            Err(js_error!(TypeError: \"Invalid response type value\"))\n        }\n    }\n}\n\nimpl TryIntoJs for ResponseType {\n    fn try_into_js(&self, _: &mut Context) -> JsResult<JsValue> {\n        Ok(self.to_string().into())\n    }\n}\n\n/// A null body status is a status that is 101, 103, 204, or 304.\n///\n/// See <https://fetch.spec.whatwg.org/#null-body-status>\nfn is_null_body_status(status: u16) -> bool {\n    matches!(status, 101 | 103 | 204 | 304)\n}\n\n/// Validates that a string matches the `reason-phrase` token production.\n///\n/// ```text\n/// reason-phrase = *( HTAB / SP / VCHAR / obs-text )\n/// ```\n///\n/// See <https://httpwg.org/specs/rfc7230.html#rule.reason.phrase>\nfn is_valid_reason_phrase(s: &str) -> bool {\n    s.bytes()\n        .all(|b| matches!(b, 0x09 | 0x20 | 0x21..=0x7E | 0x80..=0xFF))\n}\n\n/// The `Response` interface of the Fetch API represents the response to a request.\n//\n// You can create a new Response object using the `Response` constructor, but you\n// are more likely to encounter a `Response` object being returned as the result of\n// another API operation.\n#[derive(Clone, Debug, Trace, Finalize, JsData)]\npub struct JsResponse {\n    url: JsString,\n\n    #[unsafe_ignore_trace]\n    r#type: ResponseType,\n\n    /// The HTTP status code. 0 is used for error/opaque/opaqueredirect responses.\n    ///\n    /// See <https://fetch.spec.whatwg.org/#concept-response-status>\n    status: u16,\n\n    /// The HTTP status message (reason phrase).\n    ///\n    /// See <https://fetch.spec.whatwg.org/#concept-response-status-message>\n    status_text: JsString,\n\n    headers: JsHeaders,\n\n    #[unsafe_ignore_trace]\n    body: Rc<Vec<u8>>,\n}\n\nimpl JsResponse {\n    /// Create a new instance from the HTTP response and the URL that requested it.\n    #[must_use]\n    pub fn basic(url: JsString, inner: http::Response<Vec<u8>>) -> Self {\n        let (parts, body) = inner.into_parts();\n        let status = parts.status.as_u16();\n        let status_text = JsString::from(parts.status.canonical_reason().unwrap_or(\"\"));\n        let headers = JsHeaders::from_http(parts.headers);\n        let body = Rc::new(body);\n\n        Self {\n            url,\n            r#type: ResponseType::Basic,\n            status,\n            status_text,\n            headers,\n            body,\n        }\n    }\n\n    /// Create a new instance of [`JsResponse`] that is an error.\n    ///\n    /// See <https://fetch.spec.whatwg.org/#dom-response-error>\n    #[must_use]\n    pub fn error() -> Self {\n        Self {\n            url: js_string!(\"\"),\n            r#type: ResponseType::Error,\n            // A network error's status is always 0.\n            // See https://fetch.spec.whatwg.org/#concept-network-error\n            status: 0,\n            status_text: JsString::default(),\n            headers: JsHeaders::default(),\n            body: Rc::new(Vec::new()),\n        }\n    }\n\n    /// Return a copy of the body.\n    #[must_use]\n    pub fn body(&self) -> Rc<Vec<u8>> {\n        self.body.clone()\n    }\n}\n\n/// Options used in the construction of a `Response` object.\n///\n/// See <https://fetch.spec.whatwg.org/#dictdef-responseinit>\n#[derive(Debug, Default, Clone, TryFromJs, TryIntoJs, Trace, Finalize, JsData)]\n#[boa(rename_all = \"camelCase\")]\npub struct JsResponseOptions {\n    status: Option<u16>,\n    status_text: Option<JsString>,\n    headers: Option<JsHeaders>,\n}\n\n/// Initialize a `Response` from a `ResponseInit` dictionary and an optional body.\n///\n/// This is the shared algorithm used by the `Response()` constructor and\n/// `Response.json()`.\n///\n/// See <https://fetch.spec.whatwg.org/#initialize-a-response>\n/// `body_with_type` is a body paired with its MIME type (if any), as produced by\n/// \"extract a body\". `None` means no body was provided.\nfn initialize_response(\n    init: &JsResponseOptions,\n    body_with_type: Option<(Vec<u8>, Option<&str>)>,\n) -> JsResult<JsResponse> {\n    // Step 1: If init[\"status\"] is not in the range 200 to 599, inclusive, throw a RangeError.\n    let status = init.status.unwrap_or(200);\n    if !(200..=599).contains(&status) {\n        return Err(\n            js_error!(RangeError: \"The status provided ({}) is outside the range [200, 599].\", status),\n        );\n    }\n\n    // Step 2: If init[\"statusText\"] does not match the reason-phrase token production,\n    //         throw a TypeError.\n    let status_text = init.status_text.clone().unwrap_or_default();\n    let status_text_str = status_text.to_std_string_escaped();\n    if !is_valid_reason_phrase(&status_text_str) {\n        return Err(\n            js_error!(TypeError: \"statusText contains characters that are not valid in a reason-phrase.\"),\n        );\n    }\n\n    // Steps 3-4: Set response's status and status message.\n    // (Stored directly in the JsResponse fields below.)\n\n    // Step 5: If init[\"headers\"] exists, fill response's headers with init[\"headers\"].\n    let mut headers = init.headers.clone().unwrap_or_default();\n\n    // Step 6: If body is non-null, then:\n    let body = if let Some((body_bytes, body_type)) = body_with_type {\n        // Step 6.1: If response's status is a null body status, throw a TypeError.\n        if is_null_body_status(status) {\n            return Err(\n                js_error!(TypeError: \"Response body is not allowed for null body status codes (101, 103, 204, 304).\"),\n            );\n        }\n\n        // Step 6.2: Set response's body to body's body.\n\n        // Step 6.3: If body's type is non-null and response's header list does not contain\n        //           `Content-Type`, append (`Content-Type`, body's type) to the header list.\n        if let Some(content_type) = body_type\n            && !headers.has(Convert::from(\"content-type\".to_string()))?\n        {\n            headers.append(\n                Convert::from(\"content-type\".to_string()),\n                Convert::from(content_type.to_string()),\n            )?;\n        }\n\n        Rc::new(body_bytes)\n    } else {\n        Rc::new(Vec::new())\n    };\n\n    Ok(JsResponse {\n        url: js_string!(\"\"),\n        r#type: ResponseType::Default,\n        status,\n        status_text,\n        headers,\n        body,\n    })\n}\n\n/// Extract a body and its associated MIME type from a JS value.\n///\n/// This is a simplified implementation of the \"extract a body\" algorithm.\n///\n/// See <https://fetch.spec.whatwg.org/#concept-bodyinit-extract>\nfn extract_body(val: &JsValue, context: &mut Context) -> JsResult<(Vec<u8>, Option<&'static str>)> {\n    // TODO: handle other BodyInit types: Blob, ArrayBuffer, ArrayBufferView,\n    //       FormData, URLSearchParams.\n    // Currently only USVString is supported.\n    //\n    // For a USVString, the type is \"text/plain;charset=UTF-8\".\n    // See https://fetch.spec.whatwg.org/#concept-bodyinit-extract step 6.\n    let bytes = val.to_string(context)?.to_std_string_escaped().into_bytes();\n    Ok((bytes, Some(\"text/plain;charset=UTF-8\")))\n}\n\n#[boa_class(rename = \"Response\")]\n#[boa(rename_all = \"camelCase\")]\nimpl JsResponse {\n    #[boa(static)]\n    #[boa(rename = \"error\")]\n    fn error_() -> Self {\n        Self::error()\n    }\n\n    /// `Response.redirect(url, status)` per Fetch spec §7.4.\n    #[boa(static)]\n    fn redirect(url: JsValue, status: Option<u16>, context: &mut Context) -> JsResult<Self> {\n        let status = status.unwrap_or(302);\n        if !matches!(status, 301 | 302 | 303 | 307 | 308) {\n            return Err(js_error!(RangeError: \"Invalid redirect status: {}\", status));\n        }\n        let url_str = url.to_string(context)?.to_std_string_escaped();\n        http::Uri::try_from(url_str.as_str())\n            .map_err(|_| js_error!(TypeError: \"Invalid URL: {}\", url_str))?;\n\n        let status_code = StatusCode::from_u16(status)\n            .map_err(|_| js_error!(RangeError: \"Invalid status code: {}\", status))?;\n\n        let mut headers = http::header::HeaderMap::new();\n        headers.insert(\n            HeaderName::from_static(\"location\"),\n            HeaderValue::try_from(url_str)\n                .map_err(|_| js_error!(TypeError: \"Invalid URL for header value\"))?,\n        );\n\n        Ok(Self {\n            url: js_string!(\"\"),\n            r#type: ResponseType::Basic,\n            status: status_code.as_u16(),\n            status_text: JsString::from(status_code.canonical_reason().unwrap_or(\"\")),\n            headers: JsHeaders::from_http(headers),\n            body: Rc::new(Vec::new()),\n        })\n    }\n\n    /// Creates a `Response` with a JSON-serialized body and `Content-Type: application/json`.\n    ///\n    /// See <https://fetch.spec.whatwg.org/#dom-response-json>\n    #[boa(static)]\n    #[boa(rename = \"json\")]\n    fn json_static(data: JsValue, init: JsValue, context: &mut Context) -> JsResult<Self> {\n        // Step 1: Let bytes be the result of running serialize a JavaScript value to JSON bytes on data.\n        let json_val = data.to_json(context)?.ok_or_else(|| {\n            JsNativeError::typ().with_message(\"value cannot be serialized to JSON\")\n        })?;\n        let json_bytes = serde_json::to_vec(&json_val)\n            .map_err(|e| JsNativeError::error().with_message(e.to_string()))?;\n\n        // Step 2: Let body be the result of extracting bytes.\n        // The MIME type for JSON is \"application/json\".\n        let body_with_type = (json_bytes, Some(\"application/json\"));\n\n        // Step 3: Let responseObject be the result of creating a Response object.\n        // Step 4: Perform initialize a response given responseObject, init, and (body, \"application/json\").\n        let options = if init.is_null_or_undefined() {\n            JsResponseOptions::default()\n        } else {\n            JsResponseOptions::try_from_js(&init, context)?\n        };\n\n        initialize_response(&options, Some(body_with_type))\n    }\n\n    /// Creates a `Response` using the constructor.\n    ///\n    /// See <https://fetch.spec.whatwg.org/#dom-response> and\n    /// <https://fetch.spec.whatwg.org/#initialize-a-response>\n    #[boa(constructor)]\n    fn constructor(body: Option<JsValue>, init: JsValue, context: &mut Context) -> JsResult<Self> {\n        // Step 1-2: Set up the response and its headers (handled in initialize_response).\n\n        // Step 3: Let bodyWithType be null.\n        // Step 4: If body is non-null, set bodyWithType to the result of extracting body.\n        let body_with_type: Option<(Vec<u8>, Option<&'static str>)> = match body {\n            None => None,\n            Some(ref val) if val.is_null_or_undefined() => None,\n            Some(val) => Some(extract_body(&val, context)?),\n        };\n\n        // Step 5: Perform initialize a response given this, init, and bodyWithType.\n        let options = if init.is_null_or_undefined() {\n            JsResponseOptions::default()\n        } else {\n            JsResponseOptions::try_from_js(&init, context)?\n        };\n\n        initialize_response(&options, body_with_type)\n    }\n\n    /// Returns the HTTP status code of the response.\n    ///\n    /// See <https://fetch.spec.whatwg.org/#dom-response-status>\n    #[boa(getter)]\n    #[must_use]\n    pub fn status(&self) -> u16 {\n        // 0 is used for error/opaque/opaqueredirect responses.\n        // See https://fetch.spec.whatwg.org/#concept-network-error\n        self.status\n    }\n\n    /// Returns the status message corresponding to the status code.\n    ///\n    /// See <https://fetch.spec.whatwg.org/#dom-response-ok>\n    #[boa(getter)]\n    fn ok(&self) -> bool {\n        let status = self.status();\n        (200..=299).contains(&status)\n    }\n\n    #[boa(getter)]\n    fn status_text(&self) -> JsString {\n        self.status_text.clone()\n    }\n\n    /// Returns the headers associated with the response.\n    ///\n    /// See <https://fetch.spec.whatwg.org/#dom-response-headers>\n    #[boa(getter)]\n    #[must_use]\n    pub fn headers(&self) -> JsHeaders {\n        self.headers.clone()\n    }\n\n    /// See <https://fetch.spec.whatwg.org/#dom-response-type>\n    #[boa(getter)]\n    #[boa(rename = \"type\")]\n    fn r#type(&self) -> JsString {\n        self.r#type.to_string()\n    }\n\n    /// Returns the URL of the response.\n    ///\n    /// See <https://fetch.spec.whatwg.org/#dom-response-url>\n    #[boa(getter)]\n    fn url(&self) -> JsString {\n        // The spec says: return the empty string if this's response's URL is null;\n        // otherwise this's response's URL, serialized with exclude fragment set to true.\n        // See https://fetch.spec.whatwg.org/#dom-response-url\n        let s = self.url.to_std_string_escaped();\n        let without_fragment = s.find('#').map_or(s.as_str(), |i| &s[..i]);\n        JsString::from(without_fragment)\n    }\n\n    /// Returns whether the response is the result of a redirect.\n    ///\n    /// See <https://fetch.spec.whatwg.org/#dom-response-redirected>\n    #[boa(getter)]\n    #[allow(clippy::unused_self)]\n    fn redirected(&self) -> bool {\n        // The spec says: return true if this's response's URL list's size is greater than 1.\n        // TODO: track the full URL list to implement this properly.\n        false\n    }\n\n    #[boa(rename = \"clone\")]\n    fn clone_response(&self) -> Self {\n        Self {\n            url: self.url.clone(),\n            r#type: self.r#type,\n            status: self.status,\n            status_text: self.status_text.clone(),\n            headers: self.headers.deep_clone(),\n            body: Rc::new((*self.body).clone()),\n        }\n    }\n\n    fn bytes(&self, context: &mut Context) -> JsPromise {\n        let body = self.body.clone();\n        JsPromise::from_async_fn(\n            async move |context| {\n                JsUint8Array::from_iter(body.iter().copied(), &mut context.borrow_mut())\n                    .map(Into::into)\n            },\n            context,\n        )\n    }\n\n    fn text(&self, context: &mut Context) -> JsPromise {\n        let body = self.body.clone();\n        JsPromise::from_async_fn(\n            async move |_| {\n                let body = String::from_utf8_lossy(body.as_ref());\n                Ok(JsString::from(body).into())\n            },\n            context,\n        )\n    }\n\n    fn json(&self, context: &mut Context) -> JsPromise {\n        let body = self.body.clone();\n        JsPromise::from_async_fn(\n            async move |context| {\n                let json_string = String::from_utf8_lossy(body.as_ref());\n                let json = serde_json::from_str::<serde_json::Value>(&json_string)\n                    .map_err(|e| JsNativeError::syntax().with_message(e.to_string()))?;\n\n                JsValue::from_json(&json, &mut context.borrow_mut())\n            },\n            context,\n        )\n    }\n}\n"
  },
  {
    "path": "core/runtime/src/fetch/tests/e2e.rs",
    "content": "use crate::fetch::request::JsRequest;\nuse crate::fetch::response::JsResponse;\nuse crate::test::{TestAction, run_test_actions};\nuse boa_engine::{Context, Finalize, JsData, JsError, JsResult, JsString, Trace, js_error, js_str};\nuse http::Response;\nuse std::cell::RefCell;\nuse std::collections::BTreeMap;\nuse std::rc::Rc;\nuse url::Url;\n\n/// This is a special end-to-end fetcher that processes requests.\n#[derive(Debug, Clone, Trace, Finalize, JsData)]\nstruct E2eFetcher;\n\nimpl E2eFetcher {\n    fn headers(request: &JsRequest, _context: &mut Context) -> JsResult<JsResponse> {\n        let url = Url::parse(&request.uri().to_string()).map_err(JsError::from_rust)?;\n        let request_query: BTreeMap<String, String> = url\n            .query_pairs()\n            .map(|(k, v)| (k.into_owned(), v.into_owned()))\n            .collect();\n\n        let Some(header) = request_query.get(\"header\") else {\n            return Err(js_error!(\"Invalid query.\"));\n        };\n\n        let mut response = Response::new(b\"\".to_vec());\n        response.headers_mut().append(\n            \"x-headers\",\n            request\n                .inner()\n                .headers()\n                .get(header)\n                .cloned()\n                .unwrap_or(\"--not found--\".parse().unwrap()),\n        );\n\n        Ok(JsResponse::basic(\n            JsString::from(url.to_string().as_str()),\n            response,\n        ))\n    }\n}\n\nimpl crate::fetch::Fetcher for E2eFetcher {\n    async fn fetch(\n        self: Rc<Self>,\n        request: JsRequest,\n        _signal: Option<boa_engine::JsObject>,\n        context: &RefCell<&mut Context>,\n    ) -> JsResult<JsResponse> {\n        match request.uri().path() {\n            \"/headers\" => Self::headers(&request, &mut context.borrow_mut()),\n            _ => Err(js_error!(\"Invalid request.\")),\n        }\n    }\n}\n\nfn register(ctx: &mut Context) {\n    let fetcher = E2eFetcher;\n    crate::fetch::register(fetcher, None, ctx).expect(\"failed to register fetch\");\n}\n\nfn await_response(ctx: &mut Context) {\n    let response = ctx.global_object().get(js_str!(\"response\"), ctx).unwrap();\n    response.as_promise().unwrap().await_blocking(ctx).unwrap();\n}\n\n#[test]\nfn custom_header() {\n    run_test_actions([\n        TestAction::harness(),\n        TestAction::inspect_context(register),\n        TestAction::run(\n            r#\"\n                globalThis.response = (async () => {\n                    const response = await fetch(\"http://unit.test/headers?header=test\", {\n                        headers: {\n                            \"test\": \"123\",\n                        },\n                    });\n                    assertEq(response.headers.get(\"x-headers\"), \"123\");\n\n                    const text = await response.text();\n                    assertEq(text, \"\");\n                })();\n            \"#,\n        ),\n        TestAction::inspect_context(await_response),\n    ]);\n}\n"
  },
  {
    "path": "core/runtime/src/fetch/tests/headers.rs",
    "content": "use super::TestFetcher;\nuse crate::test::{TestAction, run_test_actions};\n\nfn register(ctx: &mut boa_engine::Context) {\n    crate::fetch::register(TestFetcher::default(), None, ctx).expect(\"failed to register fetch\");\n}\n\n#[test]\nfn headers_are_iterable() {\n    run_test_actions([\n        TestAction::harness(),\n        TestAction::inspect_context(register),\n        TestAction::run(\n            r#\"\n                const headers = new Headers([[\"x\", \"y\"]]);\n                const entries = [...headers];\n                assertEq(entries.length, 1);\n                assertEq(entries[0][0], \"x\");\n                assertEq(entries[0][1], \"y\");\n\n                const map = new Map(headers);\n                assertEq(map.get(\"x\"), \"y\");\n            \"#,\n        ),\n    ]);\n}\n\n#[test]\nfn headers_get_combines_duplicate_values_with_comma_space() {\n    run_test_actions([\n        TestAction::harness(),\n        TestAction::inspect_context(register),\n        TestAction::run(\n            r#\"\n                const headers = new Headers([\n                    [\"x-test\", \"1\"],\n                    [\"x-test\", \"2\"],\n                ]);\n\n                assertEq(headers.get(\"x-test\"), \"1, 2\");\n            \"#,\n        ),\n    ]);\n}\n\n#[test]\nfn headers_normalize_values() {\n    run_test_actions([\n        TestAction::harness(),\n        TestAction::inspect_context(register),\n        TestAction::run(\n            r#\"\n                const expectations = {\n                    name1: [\" space \", \"space\"],\n                    name2: [\"\\ttab\\t\", \"tab\"],\n                    name3: [\" spaceAndTab\\t\", \"spaceAndTab\"],\n                    name4: [\"\\r\\n newLine\", \"newLine\"],\n                    name5: [\"newLine\\r\\n \", \"newLine\"],\n                    name6: [\"\\r\\n\\tnewLine\", \"newLine\"],\n                };\n\n                const fromObject = new Headers(\n                    Object.fromEntries(\n                        Object.entries(expectations).map(([name, [value]]) => [name, value]),\n                    ),\n                );\n\n                for (const [name, [, expected]] of Object.entries(expectations)) {\n                    assertEq(fromObject.get(name), expected, `constructor should normalize ${name}`);\n                }\n\n                const appended = new Headers();\n                for (const [name, [value, expected]] of Object.entries(expectations)) {\n                    appended.append(name, value);\n                    assertEq(appended.get(name), expected, `append should normalize ${name}`);\n                }\n\n                const setHeaders = new Headers();\n                for (const [name, [value, expected]] of Object.entries(expectations)) {\n                    setHeaders.set(name, value);\n                    assertEq(setHeaders.get(name), expected, `set should normalize ${name}`);\n                }\n            \"#,\n        ),\n    ]);\n}\n\n#[test]\nfn headers_invalid_inputs_throw_type_error_objects() {\n    run_test_actions([\n        TestAction::harness(),\n        TestAction::inspect_context(register),\n        TestAction::run(\n            r#\"\n                const cases = [\n                    () => new Headers([[\"a\\n\", \"b\"]]),\n                    () => new Headers([[\"x-test\", \"a\\u0000b\"]]),\n                    () => {\n                        const h = new Headers();\n                        h.append(\"a\\n\", \"b\");\n                    },\n                    () => {\n                        const h = new Headers();\n                        h.set(\"x-test\", \"a\\u0000b\");\n                    },\n                ];\n\n                for (const make of cases) {\n                    try {\n                        make();\n                        throw Error(\"expected the call above to throw\");\n                    } catch (e) {\n                        assert(e instanceof TypeError, \"should throw TypeError instance\");\n                        assert(typeof e.message === \"string\" && e.message.length > 0, \"error message should be non-empty\");\n                    }\n\n                }\n            \"#,\n        ),\n    ]);\n}\n"
  },
  {
    "path": "core/runtime/src/fetch/tests/mod.rs",
    "content": "//! Test types and methods to help with testing the Fetch API.\n\nuse crate::fetch::request::JsRequest;\nuse crate::fetch::response::JsResponse;\nuse boa_engine::{Context, Finalize, JsData, JsResult, JsString, Trace, js_error};\nuse http::{Request, Response, Uri};\nuse std::cell::RefCell;\nuse std::collections::HashMap;\nuse std::rc::Rc;\n\n#[cfg(test)]\nmod e2e;\n#[cfg(test)]\nmod headers;\n#[cfg(test)]\nmod request;\n#[cfg(test)]\nmod response;\n\n/// A [`crate::fetch::Fetcher`] implementation for tests. Maps a URL to a response,\n/// and record requests received for later use.\n///\n/// The actual safety of this implementation is not guaranteed, as it\n/// is only intended for testing purposes.\n#[derive(Default, Debug, Trace, Finalize, JsData)]\npub struct TestFetcher {\n    #[unsafe_ignore_trace]\n    requests_received: RefCell<Vec<Request<Vec<u8>>>>,\n    #[unsafe_ignore_trace]\n    request_mapper: HashMap<Uri, Response<Vec<u8>>>,\n}\n\nimpl TestFetcher {\n    /// Add a response mapping for a URL.\n    pub fn add_response(&mut self, url: Uri, response: Response<Vec<u8>>) {\n        self.request_mapper.insert(url, response);\n    }\n}\n\nimpl crate::fetch::Fetcher for TestFetcher {\n    async fn fetch(\n        self: Rc<Self>,\n        request: JsRequest,\n        _signal: Option<boa_engine::JsObject>,\n        _context: &RefCell<&mut Context>,\n    ) -> JsResult<JsResponse> {\n        let request = request.into_inner();\n        self.requests_received.borrow_mut().push(request.clone());\n        let url = request.uri();\n        self.request_mapper\n            .get(url)\n            .cloned()\n            .map(|response| JsResponse::basic(JsString::from(url.to_string()), response))\n            .ok_or_else(|| js_error!(\"No response found for URL\"))\n    }\n}\n"
  },
  {
    "path": "core/runtime/src/fetch/tests/request.rs",
    "content": "use super::TestFetcher;\nuse crate::fetch::request::JsRequest;\nuse crate::fetch::response::JsResponse;\nuse crate::test::{TestAction, run_test_actions};\nuse boa_engine::{js_str, js_string};\nuse either::Either;\nuse http::{Response, Uri};\n\n#[test]\nfn request_constructor() {\n    run_test_actions([\n        TestAction::inspect_context(|ctx| {\n            let mut fetcher = TestFetcher::default();\n            fetcher.add_response(\n                Uri::from_static(\"http://unit.test\"),\n                Response::new(\"Hello World\".as_bytes().to_vec()),\n            );\n            crate::fetch::register(fetcher, None, ctx).expect(\"failed to register fetch\");\n        }),\n        TestAction::run(\n            r#\"\n                const request = new Request(\"http://unit.test\");\n                globalThis.response = fetch(request);\n            \"#,\n        ),\n        TestAction::inspect_context(|ctx| {\n            let response = ctx.global_object().get(js_str!(\"response\"), ctx).unwrap();\n            let response = response.as_promise().unwrap().await_blocking(ctx).unwrap();\n\n            assert_eq!(\n                response\n                    .as_object()\n                    .as_ref()\n                    .and_then(|o| o.downcast_ref::<JsResponse>())\n                    .unwrap()\n                    .body()\n                    .as_ref()\n                    .as_slice(),\n                \"Hello World\".as_bytes()\n            );\n        }),\n        TestAction::inspect_context(|_ctx| {\n            let request =\n                JsRequest::create_from_js(Either::Left(js_string!(\"http://example.com\")), None)\n                    .unwrap();\n            assert_eq!(request.uri().to_string(), \"http://example.com/\");\n        }),\n    ]);\n}\n\n#[test]\nfn request_clone_preserves_body_without_override() {\n    run_test_actions([\n        TestAction::inspect_context(|ctx| {\n            let fetcher = TestFetcher::default();\n            crate::fetch::register(fetcher, None, ctx).expect(\"failed to register fetch\");\n        }),\n        TestAction::run(\n            r#\"\n                const original = new Request(\"http://unit.test\", {\n                    method: \"POST\",\n                    body: \"payload\",\n                });\n                globalThis.cloned = new Request(original, {\n                    headers: { \"x-test\": \"1\" },\n                });\n            \"#,\n        ),\n        TestAction::inspect_context(|ctx| {\n            let request = ctx.global_object().get(js_str!(\"cloned\"), ctx).unwrap();\n            let request_obj = request.as_object().unwrap();\n            let request = request_obj.downcast_ref::<JsRequest>().unwrap();\n            assert_eq!(request.inner().body().as_slice(), b\"payload\");\n        }),\n    ]);\n}\n\n#[test]\nfn request_clone_empty_body_preserved() {\n    run_test_actions([\n        TestAction::inspect_context(|ctx| {\n            let fetcher = TestFetcher::default();\n            crate::fetch::register(fetcher, None, ctx).expect(\"failed to register fetch\");\n        }),\n        TestAction::run(\n            r#\"\n                const original = new Request(\"http://unit.test\", {\n                    method: \"POST\",\n                    body: \"\",\n                });\n                globalThis.cloned = new Request(original, {\n                    headers: { \"x-test\": \"1\" },\n                });\n            \"#,\n        ),\n        TestAction::inspect_context(|ctx| {\n            let request = ctx.global_object().get(js_str!(\"cloned\"), ctx).unwrap();\n            let request_obj = request.as_object().unwrap();\n            let request = request_obj.downcast_ref::<JsRequest>().unwrap();\n            assert_eq!(request.inner().body().as_slice(), b\"\");\n        }),\n    ]);\n}\n\n#[test]\nfn request_clone_body_override() {\n    run_test_actions([\n        TestAction::inspect_context(|ctx| {\n            let fetcher = TestFetcher::default();\n            crate::fetch::register(fetcher, None, ctx).expect(\"failed to register fetch\");\n        }),\n        TestAction::run(\n            r#\"\n                const original = new Request(\"http://unit.test\", {\n                    method: \"POST\",\n                    body: \"payload\",\n                });\n                globalThis.cloned = new Request(original, {\n                    body: \"override\",\n                });\n            \"#,\n        ),\n        TestAction::inspect_context(|ctx| {\n            let request = ctx.global_object().get(js_str!(\"cloned\"), ctx).unwrap();\n            let request_obj = request.as_object().unwrap();\n            let request = request_obj.downcast_ref::<JsRequest>().unwrap();\n            assert_eq!(request.inner().body().as_slice(), b\"override\");\n        }),\n    ]);\n}\n\n#[test]\nfn request_clone_no_body_preserved() {\n    run_test_actions([\n        TestAction::inspect_context(|ctx| {\n            let fetcher = TestFetcher::default();\n            crate::fetch::register(fetcher, None, ctx).expect(\"failed to register fetch\");\n        }),\n        TestAction::run(\n            r#\"\n                const original = new Request(\"http://unit.test\");\n                globalThis.cloned = new Request(original, {\n                    headers: { \"x-test\": \"1\" },\n                });\n            \"#,\n        ),\n        TestAction::inspect_context(|ctx| {\n            let request = ctx.global_object().get(js_str!(\"cloned\"), ctx).unwrap();\n            let request_obj = request.as_object().unwrap();\n            let request = request_obj.downcast_ref::<JsRequest>().unwrap();\n            assert_eq!(request.inner().body().as_slice(), b\"\");\n        }),\n    ]);\n}\n\n#[test]\nfn request_clone_method_preserves_body() {\n    run_test_actions([\n        TestAction::inspect_context(|ctx| {\n            let fetcher = TestFetcher::default();\n            crate::fetch::register(fetcher, None, ctx).expect(\"failed to register fetch\");\n        }),\n        TestAction::run(\n            r#\"\n                const original = new Request(\"http://unit.test\", {\n                    method: \"POST\",\n                    body: \"payload\",\n                });\n                globalThis.cloned = original.clone();\n            \"#,\n        ),\n        TestAction::inspect_context(|ctx| {\n            let cloned = ctx.global_object().get(js_str!(\"cloned\"), ctx).unwrap();\n            let cloned_obj = cloned.as_object().unwrap();\n            let cloned_req = cloned_obj.downcast_ref::<JsRequest>().unwrap();\n            assert_eq!(cloned_req.inner().body().as_slice(), b\"payload\");\n        }),\n    ]);\n}\n\n#[test]\nfn request_clone_method_is_independent() {\n    run_test_actions([\n        TestAction::inspect_context(|ctx| {\n            let fetcher = TestFetcher::default();\n            crate::fetch::register(fetcher, None, ctx).expect(\"failed to register fetch\");\n        }),\n        TestAction::run(\n            r#\"\n                const original = new Request(\"http://unit.test\", {\n                    method: \"POST\",\n                    body: \"original-body\",\n                });\n                globalThis.original = original;\n                globalThis.cloned = original.clone();\n            \"#,\n        ),\n        TestAction::inspect_context(|ctx| {\n            let original = ctx.global_object().get(js_str!(\"original\"), ctx).unwrap();\n            let original_obj = original.as_object().unwrap();\n            let original_req = original_obj.downcast_ref::<JsRequest>().unwrap();\n\n            let cloned = ctx.global_object().get(js_str!(\"cloned\"), ctx).unwrap();\n            let cloned_obj = cloned.as_object().unwrap();\n            let cloned_req = cloned_obj.downcast_ref::<JsRequest>().unwrap();\n\n            assert_eq!(original_req.inner().body().as_slice(), b\"original-body\");\n            assert_eq!(cloned_req.inner().body().as_slice(), b\"original-body\");\n\n            // Verify they are distinct objects (different pointers).\n            assert!(!std::ptr::eq(\n                original_req.inner().body().as_ptr(),\n                cloned_req.inner().body().as_ptr()\n            ));\n        }),\n    ]);\n}\n"
  },
  {
    "path": "core/runtime/src/fetch/tests/response.rs",
    "content": "use super::TestFetcher;\nuse crate::test::{TestAction, run_test_actions};\nuse boa_engine::{Context, js_str};\nuse http::{Response, Uri};\n\nfn register(responses: &[(&'static str, Response<Vec<u8>>)], ctx: &mut Context) {\n    let mut fetcher = TestFetcher::default();\n\n    for (url, resp) in responses {\n        fetcher.add_response(Uri::from_static(url), resp.clone());\n    }\n    crate::fetch::register(fetcher, None, ctx).expect(\"failed to register fetch\");\n}\n\n#[test]\nfn response_error() {\n    run_test_actions([\n        TestAction::harness(),\n        TestAction::inspect_context(|ctx| register(&[], ctx)),\n        TestAction::run(\n            r#\"\n                const response = Response.error();\n\n                assertEq(response.status, 0);\n                assertEq(response.statusText, \"\");\n                assertEq(response.headers.get(\"custom-header\"), null);\n                assertEq(response.type, \"error\");\n                assertEq(response.url, \"\");\n            \"#,\n        ),\n        // Assertions made in JavaScript.\n    ]);\n}\n\n#[test]\nfn response_text() {\n    run_test_actions([\n        TestAction::harness(),\n        TestAction::inspect_context(|ctx| {\n            register(\n                &[(\"http://unit.test\", Response::new(b\"Hello World\".to_vec()))],\n                ctx,\n            );\n        }),\n        TestAction::run(\n            r#\"\n                globalThis.response = (async () => {\n                    const request = new Request(\"http://unit.test\");\n                    const response = await fetch(request);\n                    const text = await response.text();\n                    assertEq(text, \"Hello World\");\n                })();\n            \"#,\n        ),\n        TestAction::inspect_context(|ctx| {\n            let response = ctx.global_object().get(js_str!(\"response\"), ctx).unwrap();\n            response.as_promise().unwrap().await_blocking(ctx).unwrap();\n        }),\n    ]);\n}\n\n#[test]\nfn response_json() {\n    run_test_actions([\n        TestAction::harness(),\n        TestAction::inspect_context(|ctx| {\n            register(\n                &[(\n                    \"http://unit.test\",\n                    Response::new(b\"{ \\\"hello world\\\": 123 }\".to_vec()),\n                )],\n                ctx,\n            );\n        }),\n        TestAction::run(\n            r#\"\n                globalThis.response = (async () => {\n                    const request = new Request(\"http://unit.test\");\n                    const response = await fetch(request);\n                    const json = await response.json();\n                    assertEq(json[\"hello world\"], 123);\n                    return json;\n                })();\n            \"#,\n        ),\n        TestAction::inspect_context(|ctx| {\n            let response = ctx.global_object().get(js_str!(\"response\"), ctx).unwrap();\n            let response = response.as_promise().unwrap().await_blocking(ctx).unwrap();\n            assert_eq!(\n                format!(\"{}\", response.display_obj(false)),\n                \"{\\n    hello world: 123\\n}\"\n            );\n        }),\n    ]);\n}\n\n#[test]\nfn response_bytes() {\n    run_test_actions([\n        TestAction::harness(),\n        TestAction::inspect_context(|ctx| {\n            register(\n                &[(\"http://unit.test\", Response::new(b\"Hello World\".to_vec()))],\n                ctx,\n            );\n        }),\n        TestAction::run(\n            r#\"\n                globalThis.response = (async () => {\n                    const request = new Request(\"http://unit.test\");\n                    const response = await fetch(request);\n                    const bytes = await response.bytes();\n                    const text = new TextDecoder().decode(bytes);\n                    assertEq(text, \"Hello World\");\n                })();\n            \"#,\n        ),\n        TestAction::inspect_context(|ctx| {\n            let response = ctx.global_object().get(js_str!(\"response\"), ctx).unwrap();\n            response.as_promise().unwrap().await_blocking(ctx).unwrap();\n        }),\n    ]);\n}\n\n#[test]\nfn response_getter() {\n    run_test_actions([\n        TestAction::harness(),\n        TestAction::inspect_context(|ctx| {\n            let mut response = Response::new(b\"Hello World\".to_vec());\n            response\n                .headers_mut()\n                .append(\"custom-header\", \"custom-value\".parse().unwrap());\n            register(&[(\"http://unit.test\", response)], ctx);\n        }),\n        TestAction::run(\n            r#\"\n                globalThis.response = (async () => {\n                    const request = new Request(\"http://unit.test\");\n                    const response = await fetch(request);\n\n                    assertEq(response.status, 200);\n                    assertEq(response.statusText, \"OK\");\n                    assertEq(response.headers.get(\"custom-header\"), \"custom-value\");\n                    assertEq(response.type, \"basic\");\n                    assertEq(response.url, \"http://unit.test/\");\n                })();\n            \"#,\n        ),\n        TestAction::inspect_context(|ctx| {\n            let response = ctx.global_object().get(js_str!(\"response\"), ctx).unwrap();\n            response.as_promise().unwrap().await_blocking(ctx).unwrap();\n\n            // Assertions made in JavaScript.\n        }),\n    ]);\n}\n\n#[test]\nfn response_redirect_default_status() {\n    run_test_actions([\n        TestAction::harness(),\n        TestAction::inspect_context(|ctx| register(&[], ctx)),\n        TestAction::run(\n            r#\"\n                const response = Response.redirect(\"http://example.com/\");\n                assertEq(response.status, 302);\n                assertEq(response.headers.get(\"location\"), \"http://example.com/\");\n            \"#,\n        ),\n    ]);\n}\n\n#[test]\nfn response_redirect_custom_status_and_coercion() {\n    run_test_actions([\n        TestAction::harness(),\n        TestAction::inspect_context(|ctx| register(&[], ctx)),\n        TestAction::run(\n            r#\"\n                const response = Response.redirect(\"http://example.com/\", 301);\n                assertEq(response.status, 301);\n\n                // Tests Web IDL coercion of the URL parameter\n                const response2 = Response.redirect(12345);\n                assertEq(response2.headers.get(\"location\"), \"12345\");\n            \"#,\n        ),\n    ]);\n}\n\n#[test]\nfn response_redirect_invalid_status() {\n    run_test_actions([\n        TestAction::harness(),\n        TestAction::inspect_context(|ctx| register(&[], ctx)),\n        TestAction::run(\n            r#\"\n                let threw = false;\n                try {\n                    Response.redirect(\"http://example.com/\", 200);\n                } catch (e) {\n                    threw = true;\n                    if (!(e instanceof RangeError)) {\n                        throw new Error(\"Expected RangeError, got \" + e.name);\n                    }\n                }\n                if (!threw) {\n                    throw new Error(\"Expected RangeError, but no error was thrown\");\n                }\n            \"#,\n        ),\n    ]);\n}\n\n#[test]\nfn response_json_static() {\n    run_test_actions([\n        TestAction::harness(),\n        TestAction::inspect_context(|ctx| register(&[], ctx)),\n        TestAction::run(\n            r#\"\n                globalThis.p = (async () => {\n                    const response = Response.json({ hello: \"world\" });\n                    assertEq(response.status, 200);\n                    assertEq(response.headers.get(\"content-type\"), \"application/json\");\n                    const body = await response.json();\n                    assertEq(body.hello, \"world\");\n                })();\n            \"#,\n        ),\n        TestAction::inspect_context(|ctx| {\n            let p = ctx.global_object().get(js_str!(\"p\"), ctx).unwrap();\n            p.as_promise().unwrap().await_blocking(ctx).unwrap();\n        }),\n    ]);\n}\n\n#[test]\nfn response_headers_get_combines_duplicate_values_with_comma_space() {\n    run_test_actions([\n        TestAction::harness(),\n        TestAction::inspect_context(|ctx| {\n            let mut response = Response::new(b\"Hello World\".to_vec());\n            response\n                .headers_mut()\n                .append(\"x-test\", \"1\".parse().unwrap());\n            response\n                .headers_mut()\n                .append(\"x-test\", \"2\".parse().unwrap());\n            register(&[(\"http://unit.test\", response)], ctx);\n        }),\n        TestAction::run(\n            r#\"\n                globalThis.response = (async () => {\n                    const response = await fetch(\"http://unit.test\");\n                    assertEq(response.headers.get(\"x-test\"), \"1, 2\");\n                })();\n            \"#,\n        ),\n        TestAction::inspect_context(|ctx| {\n            let response = ctx.global_object().get(js_str!(\"response\"), ctx).unwrap();\n            response.as_promise().unwrap().await_blocking(ctx).unwrap();\n        }),\n    ]);\n}\n\n#[test]\nfn response_clone_read_both() {\n    run_test_actions([\n        TestAction::harness(),\n        TestAction::inspect_context(|ctx| {\n            register(\n                &[(\"http://unit.test\", Response::new(b\"Hello World\".to_vec()))],\n                ctx,\n            );\n        }),\n        TestAction::run(\n            r#\"\n                globalThis.response = (async () => {\n                    const response = await fetch(new Request(\"http://unit.test\"));\n                    const cloned = response.clone();\n                    const t1 = await response.text();\n                    const t2 = await cloned.text();\n                    assertEq(t1, \"Hello World\");\n                    assertEq(t2, \"Hello World\");\n                })();\n            \"#,\n        ),\n        TestAction::inspect_context(|ctx| {\n            let response = ctx.global_object().get(js_str!(\"response\"), ctx).unwrap();\n            response.as_promise().unwrap().await_blocking(ctx).unwrap();\n        }),\n    ]);\n}\n\n#[test]\nfn response_clone_header_independence() {\n    run_test_actions([\n        TestAction::harness(),\n        TestAction::inspect_context(|ctx| {\n            let mut resp = Response::new(b\"body\".to_vec());\n            resp.headers_mut()\n                .append(\"x-original\", \"value\".parse().unwrap());\n            register(&[(\"http://unit.test\", resp)], ctx);\n        }),\n        TestAction::run(\n            r#\"\n                globalThis.response = (async () => {\n                    const response = await fetch(new Request(\"http://unit.test\"));\n                    const cloned = response.clone();\n\n                    cloned.headers.set(\"x-original\", \"mutated\");\n                    cloned.headers.set(\"x-new\", \"added\");\n\n                    assertEq(response.headers.get(\"x-original\"), \"value\");\n                    assertEq(response.headers.get(\"x-new\"), null);\n                    assertEq(cloned.headers.get(\"x-original\"), \"mutated\");\n                    assertEq(cloned.headers.get(\"x-new\"), \"added\");\n                })();\n            \"#,\n        ),\n        TestAction::inspect_context(|ctx| {\n            let response = ctx.global_object().get(js_str!(\"response\"), ctx).unwrap();\n            response.as_promise().unwrap().await_blocking(ctx).unwrap();\n        }),\n    ]);\n}\n\n#[test]\nfn response_clone_preserves_status() {\n    run_test_actions([\n        TestAction::harness(),\n        TestAction::inspect_context(|ctx| {\n            register(&[(\"http://unit.test\", Response::new(b\"ok\".to_vec()))], ctx);\n        }),\n        TestAction::run(\n            r#\"\n                globalThis.response = (async () => {\n                    const response = await fetch(new Request(\"http://unit.test\"));\n                    const cloned = response.clone();\n\n                    assertEq(cloned.status, response.status);\n                    assertEq(cloned.statusText, response.statusText);\n                    assertEq(cloned.type, response.type);\n                    assertEq(cloned.url, response.url);\n                    assertEq(cloned.ok, response.ok);\n                })();\n            \"#,\n        ),\n        TestAction::inspect_context(|ctx| {\n            let response = ctx.global_object().get(js_str!(\"response\"), ctx).unwrap();\n            response.as_promise().unwrap().await_blocking(ctx).unwrap();\n        }),\n    ]);\n}\n"
  },
  {
    "path": "core/runtime/src/interval/tests.rs",
    "content": "use crate::interval;\nuse crate::test::{TestAction, run_test_actions_with};\nuse boa_engine::context::time::FixedClock;\nuse boa_engine::context::{Clock, ContextBuilder};\nuse boa_engine::job::{JobExecutor, SimpleJobExecutor};\nuse boa_engine::{Context, js_str};\nuse futures_lite::future::poll_once;\nuse indoc::indoc;\nuse std::cell::RefCell;\nuse std::pin::pin;\nuse std::rc::Rc;\n\nfn create_context(clock: Rc<impl Clock + 'static>) -> Context {\n    let mut context = ContextBuilder::default().clock(clock).build().unwrap();\n    interval::register(&mut context).unwrap();\n    context\n}\n\n#[test]\nfn two_zero_delay_timeouts_both_fire() {\n    let clock = Rc::new(FixedClock::default());\n    let context = &mut create_context(clock.clone());\n\n    run_test_actions_with(\n        [\n            TestAction::run(indoc! {r#\"\n                order = [];\n                setTimeout(() => order.push(1), 0);\n                setTimeout(() => order.push(2), 0);\n            \"#}),\n            TestAction::inspect_context(move |ctx| {\n                clock.forward(1);\n                ctx.run_jobs().unwrap();\n\n                let order = ctx.global_object().get(js_str!(\"order\"), ctx).unwrap();\n                let order = order.as_object().unwrap();\n                assert_eq!(\n                    order.get(js_str!(\"length\"), ctx).unwrap().as_i32(),\n                    Some(2),\n                    \"both callbacks must fire\"\n                );\n                assert_eq!(order.get(0usize, ctx).unwrap().as_i32(), Some(1));\n                assert_eq!(order.get(1usize, ctx).unwrap().as_i32(), Some(2));\n            }),\n        ],\n        context,\n    );\n}\n\n#[test]\nfn set_timeout_basic() {\n    let clock = Rc::new(FixedClock::default());\n    let context = &mut create_context(clock.clone());\n\n    run_test_actions_with(\n        [\n            TestAction::run(indoc! {r#\"\n                called = false;\n                setTimeout(() => { called = true; });\n            \"#}),\n            TestAction::inspect_context(move |ctx| {\n                let called = ctx.global_object().get(js_str!(\"called\"), ctx).unwrap();\n                assert_eq!(called.as_boolean(), Some(false));\n\n                clock.forward(1);\n                ctx.run_jobs().unwrap();\n\n                let called = ctx.global_object().get(js_str!(\"called\"), ctx).unwrap();\n                assert_eq!(called.as_boolean(), Some(true));\n            }),\n        ],\n        context,\n    );\n}\n\n#[test]\nfn set_timeout_cancel() {\n    let clock = Rc::new(FixedClock::default());\n    let context = &mut create_context(clock.clone());\n    let clock1 = clock.clone();\n    let clock2 = clock.clone();\n\n    run_test_actions_with(\n        [\n            TestAction::run(indoc! {r#\"\n                called = false;\n                id = setTimeout(() => { called = true; }, 100);\n            \"#}),\n            TestAction::inspect_context_async(async |ctx| {\n                let job_executor = ctx.downcast_job_executor::<SimpleJobExecutor>().unwrap();\n                let clock = clock1;\n\n                let called = ctx.global_object().get(js_str!(\"called\"), ctx).unwrap();\n                let ctx = &RefCell::new(ctx);\n                let mut event_loop = pin!(poll_once(job_executor.run_jobs_async(ctx)));\n\n                assert_eq!(called.as_boolean(), Some(false));\n\n                assert!(event_loop.as_mut().await.is_none());\n                clock.forward(50);\n                assert!(event_loop.as_mut().await.is_none());\n\n                let global_object = ctx.borrow().global_object();\n                let called = global_object\n                    .get(js_str!(\"called\"), &mut ctx.borrow_mut())\n                    .unwrap();\n                assert_eq!(called.as_boolean(), Some(false));\n                assert!(event_loop.as_mut().await.is_none());\n            }),\n            TestAction::run(\"clearTimeout(id);\"),\n            TestAction::inspect_context(|ctx| {\n                let clock = clock2;\n                clock.forward(100);\n                let called = ctx.global_object().get(js_str!(\"called\"), ctx).unwrap();\n                // Should still be false, as it was cancelled.\n                assert_eq!(called.as_boolean(), Some(false));\n            }),\n        ],\n        context,\n    );\n}\n\n#[test]\nfn set_timeout_delay() {\n    let clock = Rc::new(FixedClock::default());\n    let context = &mut create_context(clock.clone());\n\n    run_test_actions_with(\n        [\n            TestAction::run(\n                r#\"\n            called = false;\n            setTimeout(() => { called = true; }, 100);\n        \"#,\n            ),\n            TestAction::inspect_context_async(async move |ctx| {\n                let job_executor = ctx.downcast_job_executor::<SimpleJobExecutor>().unwrap();\n                let global_object = ctx.global_object();\n                let ctx = &RefCell::new(ctx);\n                let mut event_loop = pin!(poll_once(job_executor.run_jobs_async(ctx)));\n\n                // As long as the clock isn't updated, `called` will always be false.\n                for _ in 0..5 {\n                    let called = global_object\n                        .get(js_str!(\"called\"), &mut ctx.borrow_mut())\n                        .unwrap();\n                    assert_eq!(called.as_boolean(), Some(false));\n                    assert!(event_loop.as_mut().await.is_none());\n                }\n\n                // Move forward 50 milliseconds, `called` should still be false.\n                clock.forward(50);\n                assert!(event_loop.as_mut().await.is_none());\n                let called = global_object\n                    .get(js_str!(\"called\"), &mut ctx.borrow_mut())\n                    .unwrap();\n                assert_eq!(called.as_boolean(), Some(false));\n\n                clock.forward(51);\n                // Event loop should have exited since there are no more\n                // jobs on the queue.\n                assert!(event_loop.as_mut().await.is_some());\n                let called = global_object\n                    .get(js_str!(\"called\"), &mut ctx.borrow_mut())\n                    .unwrap();\n                assert_eq!(called.as_boolean(), Some(true));\n            }),\n        ],\n        context,\n    );\n}\n\n#[test]\nfn set_interval_delay() {\n    let clock = Rc::new(FixedClock::default());\n    let context = &mut create_context(clock.clone());\n    let clock1 = clock.clone(); // For the first test.\n    let clock2 = clock.clone(); // For the first test.\n\n    run_test_actions_with(\n        [\n            TestAction::run(\n                r#\"\n                called = 0;\n                id = setInterval(() => { called++; }, 100);\n            \"#,\n            ),\n            TestAction::inspect_context_async(async |ctx| {\n                let clock = clock1;\n                let global_object = ctx.global_object();\n\n                let job_executor = ctx.downcast_job_executor::<SimpleJobExecutor>().unwrap();\n                let ctx = &RefCell::new(ctx);\n                let mut event_loop = pin!(poll_once(job_executor.run_jobs_async(ctx)));\n\n                // As long as the clock isn't updated, `called` will always be false.\n                for _ in 0..5 {\n                    let called = global_object\n                        .get(js_str!(\"called\"), &mut ctx.borrow_mut())\n                        .unwrap();\n                    assert_eq!(called.as_i32(), Some(0));\n                    assert!(event_loop.as_mut().await.is_none());\n                }\n\n                // Move forward 50 milliseconds.\n                clock.forward(50);\n                assert!(event_loop.as_mut().await.is_none());\n                let called = global_object\n                    .get(js_str!(\"called\"), &mut ctx.borrow_mut())\n                    .unwrap();\n                assert_eq!(called.as_i32(), Some(0));\n\n                // Move forward 51 milliseconds.\n                clock.forward(51);\n                assert!(event_loop.as_mut().await.is_none());\n                let called = global_object\n                    .get(js_str!(\"called\"), &mut ctx.borrow_mut())\n                    .unwrap();\n                assert_eq!(called.as_i32(), Some(1));\n\n                // Move forward 50 milliseconds.\n                clock.forward(50);\n                assert!(event_loop.as_mut().await.is_none());\n                let called = global_object\n                    .get(js_str!(\"called\"), &mut ctx.borrow_mut())\n                    .unwrap();\n                assert_eq!(called.as_i32(), Some(1));\n\n                // Move forward 51 milliseconds.\n                clock.forward(51);\n                assert!(event_loop.as_mut().await.is_none());\n                let called = global_object\n                    .get(js_str!(\"called\"), &mut ctx.borrow_mut())\n                    .unwrap();\n                assert_eq!(called.as_i32(), Some(2));\n\n                // Move forward 500 milliseconds, should only be called once.\n                clock.forward(500);\n                assert!(event_loop.as_mut().await.is_none());\n                let called = global_object\n                    .get(js_str!(\"called\"), &mut ctx.borrow_mut())\n                    .unwrap();\n                assert_eq!(called.as_i32(), Some(3));\n            }),\n            // Cancel\n            TestAction::run(\"clearInterval(id);\"),\n            TestAction::inspect_context(move |ctx| {\n                let clock = clock2;\n                // Doesn't matter how long, this should not be called ever again.\n                clock.forward(500);\n                ctx.run_jobs().unwrap();\n                let called = ctx.global_object().get(js_str!(\"called\"), ctx).unwrap();\n                assert_eq!(called.as_i32(), Some(3));\n\n                clock.forward(500);\n                ctx.run_jobs().unwrap();\n                let called = ctx.global_object().get(js_str!(\"called\"), ctx).unwrap();\n                assert_eq!(called.as_i32(), Some(3));\n            }),\n        ],\n        context,\n    );\n}\n"
  },
  {
    "path": "core/runtime/src/interval.rs",
    "content": "//! A module that declares any functions for dealing with intervals or\n//! timeouts.\n\nuse boa_engine::interop::JsRest;\nuse boa_engine::job::{NativeJob, TimeoutJob};\nuse boa_engine::object::builtins::JsFunction;\nuse boa_engine::value::{IntegerOrInfinity, Nullable};\nuse boa_engine::{\n    Context, Finalize, IntoJsFunctionCopied, JsData, JsResult, JsValue, Trace, js_error, js_string,\n};\nuse boa_gc::{Gc, GcRefCell};\nuse std::collections::HashSet;\n\n#[cfg(test)]\nmod tests;\n\n/// The internal state of the interval module. The value is whether the interval\n/// function is still active.\n#[derive(Default, Trace, Finalize, JsData)]\nstruct IntervalInnerState {\n    active_map: HashSet<u32>,\n    next_id: u32,\n}\n\nimpl IntervalInnerState {\n    /// Get the interval handler map from the context, or add it to the context if not\n    /// present.\n    fn from_context(context: &mut Context) -> Gc<GcRefCell<Self>> {\n        if !context.has_data::<Gc<GcRefCell<IntervalInnerState>>>() {\n            context.insert_data(Gc::new(GcRefCell::new(Self::default())));\n        }\n\n        context\n            .get_data::<Gc<GcRefCell<Self>>>()\n            .expect(\"Should have inserted.\")\n            .clone()\n    }\n\n    /// Get whether an interval is still active.\n    #[inline]\n    fn is_interval_valid(&self, id: u32) -> bool {\n        self.active_map.contains(&id)\n    }\n\n    /// Create an interval ID, insert it in the active map and return it.\n    fn new_interval(&mut self) -> JsResult<u32> {\n        if self.next_id == u32::MAX {\n            return Err(js_error!(Error: \"Interval ID overflow\"));\n        }\n        self.next_id += 1;\n        self.active_map.insert(self.next_id);\n        Ok(self.next_id)\n    }\n\n    /// Delete an interval ID from the active map.\n    fn clear_interval(&mut self, id: u32) {\n        self.active_map.remove(&id);\n    }\n}\n\n/// Inner handler function for handling intervals and timeout.\n#[allow(clippy::too_many_arguments)]\nfn handle(\n    handler_map: Gc<GcRefCell<IntervalInnerState>>,\n    id: u32,\n    function_ref: JsFunction,\n    args: Vec<JsValue>,\n    reschedule: Option<u64>,\n    context: &mut Context,\n) -> JsResult<JsValue> {\n    // Check if it's still valid.\n    if !handler_map.borrow().is_interval_valid(id) {\n        return Ok(JsValue::undefined());\n    }\n\n    // Call the handler function.\n    // The spec says we should still reschedule an interval even if the function\n    // throws an error.\n    let result = function_ref.call(&JsValue::undefined(), &args, context);\n    if let Some(delay) = reschedule {\n        if handler_map.borrow().is_interval_valid(id) {\n            let job = TimeoutJob::recurring(\n                NativeJob::new(move |context| {\n                    handle(handler_map, id, function_ref, args, reschedule, context)\n                }),\n                delay,\n            );\n            context.enqueue_job(job.into());\n        }\n        return result;\n    }\n\n    handler_map.borrow_mut().clear_interval(id);\n    result\n}\n\n/// Set a timeout to call the given function after the given delay.\n/// The `code` version of this function is not supported at the moment.\n///\n/// See [MDN](https://developer.mozilla.org/en-US/docs/Web/API/WindowOrWorkerGlobalScope/setTimeout).\n///\n/// # Errors\n/// Any errors when trying to read the context, converting the arguments or\n/// enqueuing the job.\npub fn set_timeout(\n    function_ref: Option<JsFunction>,\n    delay_in_msec: Option<JsValue>,\n    rest: JsRest<'_>,\n    context: &mut Context,\n) -> JsResult<u32> {\n    let Some(function_ref) = function_ref else {\n        return Ok(0);\n    };\n\n    let handler_map = IntervalInnerState::from_context(context);\n    let id = handler_map.borrow_mut().new_interval()?;\n\n    // Spec says if delay is not a number, it should be equal to 0.\n    let delay = delay_in_msec\n        .unwrap_or_default()\n        .to_integer_or_infinity(context)\n        .unwrap_or(IntegerOrInfinity::Integer(0));\n    // The spec converts the delay to a 32-bit signed integer.\n    let delay = u64::from(delay.clamp_finite(0, u32::MAX));\n\n    // Get ownership of rest arguments.\n    let rest = rest.to_vec();\n\n    let job = TimeoutJob::new(\n        NativeJob::new(move |context| handle(handler_map, id, function_ref, rest, None, context)),\n        delay,\n    );\n    context.enqueue_job(job.into());\n\n    Ok(id)\n}\n\n/// Call a given function on an interval with the given delay.\n/// The `code` version of this function is not supported at the moment.\n///\n/// See [MDN](https://developer.mozilla.org/en-US/docs/Web/API/WindowOrWorkerGlobalScope/setInterval).\n///\n/// # Errors\n/// Any errors when trying to read the context, converting the arguments or\n/// enqueuing the job.\npub fn set_interval(\n    function_ref: Option<JsFunction>,\n    delay_in_msec: Option<JsValue>,\n    rest: JsRest<'_>,\n    context: &mut Context,\n) -> JsResult<u32> {\n    let Some(function_ref) = function_ref else {\n        return Ok(0);\n    };\n\n    let handler_map = IntervalInnerState::from_context(context);\n    let id = handler_map.borrow_mut().new_interval()?;\n\n    // Spec says if delay is not a number, it should be equal to 0.\n    let delay = delay_in_msec\n        .unwrap_or_default()\n        .to_integer_or_infinity(context)\n        .unwrap_or(IntegerOrInfinity::Integer(0));\n    let delay = u64::from(delay.clamp_finite(0, u32::MAX));\n\n    // Get ownership of rest arguments.\n    let rest = rest.to_vec();\n\n    let job = TimeoutJob::new(\n        NativeJob::new(move |context| {\n            handle(handler_map, id, function_ref, rest, Some(delay), context)\n        }),\n        delay,\n    );\n    context.enqueue_job(job.into());\n\n    Ok(id)\n}\n\n/// Clears a timeout or interval currently running.\n///\n/// See [MDN](https://developer.mozilla.org/en-US/docs/Web/API/Window/clearTimeout).\n///\n/// Please note that this is the same exact method as `clearInterval`, as both can be\n/// used interchangeably.\npub fn clear_timeout(id: Nullable<Option<u32>>, context: &mut Context) {\n    let Some(id) = id.flatten() else {\n        return;\n    };\n    let handler_map = IntervalInnerState::from_context(context);\n    handler_map.borrow_mut().clear_interval(id);\n}\n\n/// Register the interval module into the given context.\n///\n/// # Errors\n/// Any error returned by the context when registering the global functions.\npub fn register(context: &mut Context) -> JsResult<()> {\n    register_functions(context)\n}\n\n/// Register the interval module without any clock. This still needs the proper\n/// typing for the clock, even if it is not registered to the context.\n///\n/// # Errors\n/// Any error returned by the context when registering the global functions.\npub fn register_functions(context: &mut Context) -> JsResult<()> {\n    let set_timeout_ = set_timeout.into_js_function_copied(context);\n    context.register_global_callable(js_string!(\"setTimeout\"), 1, set_timeout_)?;\n\n    let set_interval_ = set_interval.into_js_function_copied(context);\n    context.register_global_callable(js_string!(\"setInterval\"), 1, set_interval_)?;\n\n    // These two methods are identical, just under different names in JavaScript.\n    let clear_timeout_ = clear_timeout.into_js_function_copied(context);\n    context.register_global_callable(js_string!(\"clearTimeout\"), 1, clear_timeout_.clone())?;\n    context.register_global_callable(js_string!(\"clearInterval\"), 1, clear_timeout_)?;\n\n    Ok(())\n}\n"
  },
  {
    "path": "core/runtime/src/lib.rs",
    "content": "//! Boa's **boa_runtime** crate contains an example runtime and basic runtime features and\n//! functionality for the `boa_engine` crate for runtime implementors.\n//!\n//! # Example: Adding Web API's Console Object\n//!\n//! 1. Add **boa_runtime** as a dependency to your project along with **boa_engine**.\n//!\n//! ```\n//! use boa_engine::{js_string, property::Attribute, Context, Source};\n//! use boa_runtime::Console;\n//! use boa_runtime::console::DefaultLogger;\n//!\n//! // Create the context.\n//! let mut context = Context::default();\n//!\n//! // Register the Console object to the context. The DefaultLogger simply\n//! // write errors to STDERR and all other logs to STDOUT.\n//! Console::register_with_logger(DefaultLogger, &mut context)\n//!     .expect(\"the console object shouldn't exist yet\");\n//!\n//! // JavaScript source for parsing.\n//! let js_code = \"console.log('Hello World from a JS code string!')\";\n//!\n//! // Parse the source code\n//! match context.eval(Source::from_bytes(js_code)) {\n//!     Ok(res) => {\n//!         println!(\n//!             \"{}\",\n//!             res.to_string(&mut context).unwrap().to_std_string_escaped()\n//!         );\n//!     }\n//!     Err(e) => {\n//!         // Pretty print the error\n//!         eprintln!(\"Uncaught {e}\");\n//!         # panic!(\"An error occurred in boa_runtime's js_code\");\n//!     }\n//! };\n//! ```\n//!\n//! # Example: Add all supported Boa's Runtime Web API to your context\n//!\n//! ```no_run\n//! use boa_engine::{js_string, property::Attribute, Context, Source};\n//!\n//! // Create the context.\n//! let mut context = Context::default();\n//!\n//! // Register all objects in the context. To conditionally register extensions,\n//! // call `register()` directly on the extension.\n//! boa_runtime::register(\n//!     (\n//!         // Register the default logger.\n//!         boa_runtime::extensions::ConsoleExtension::default(),\n//!         // A fetcher can be added if the `fetch` feature flag is enabled.\n//!         // This fetcher uses the Reqwest blocking API to allow fetching using HTTP.\n//! #       #[cfg(feature = \"reqwest-blocking\")]\n//!         boa_runtime::extensions::FetchExtension(\n//!             boa_runtime::fetch::BlockingReqwestFetcher::default()\n//!         ),\n//!     ),\n//!     None,\n//!     &mut context,\n//! );\n//!\n//! // JavaScript source for parsing.\n//! let js_code = r#\"\n//!     fetch(\"https://google.com/\")\n//!         .then(response => response.text())\n//!         .then(html => console.log(html))\n//! \"#;\n//!\n//! // Parse the source code\n//! match context.eval(Source::from_bytes(js_code)) {\n//!     Ok(res) => {\n//!         // The result is a promise, so we need to await it.\n//!         res\n//!             .as_promise()\n//!             .expect(\"Should be a promise\")\n//!             .await_blocking(&mut context)\n//!             .expect(\"Should resolve()\");\n//!         println!(\n//!             \"{}\",\n//!             res.to_string(&mut context).unwrap().to_std_string_escaped()\n//!         );\n//!     }\n//!     Err(e) => {\n//!         // Pretty print the error\n//!         eprintln!(\"Uncaught {e}\");\n//!         # panic!(\"An error occurred in boa_runtime's js_code\");\n//!     }\n//! };\n//! ```\n#![doc = include_str!(\"../ABOUT.md\")]\n#![doc(\n    html_logo_url = \"https://raw.githubusercontent.com/boa-dev/boa/main/assets/logo_black.svg\",\n    html_favicon_url = \"https://raw.githubusercontent.com/boa-dev/boa/main/assets/logo_black.svg\"\n)]\n#![cfg_attr(test, allow(clippy::needless_raw_string_hashes))] // Makes strings a bit more copy-pastable\n#![cfg_attr(not(test), forbid(clippy::unwrap_used))]\n// Currently throws a false positive regarding dependencies that are only used in tests.\n#![allow(unused_crate_dependencies)]\n#![allow(\n    clippy::module_name_repetitions,\n    clippy::redundant_pub_crate,\n    clippy::let_unit_value\n)]\n\npub mod base64;\npub mod console;\n\n#[doc(inline)]\npub use console::{Console, ConsoleState, DefaultLogger, Logger, NullLogger};\n\n#[cfg(feature = \"fetch\")]\npub mod abort;\npub mod clone;\npub mod extensions;\n#[cfg(feature = \"fetch\")]\npub mod fetch;\npub mod interval;\npub mod message;\npub mod microtask;\n#[cfg(feature = \"process\")]\npub mod process;\npub mod store;\npub mod text;\n#[cfg(feature = \"url\")]\npub mod url;\n\n#[cfg(feature = \"process\")]\nuse crate::extensions::ProcessExtension;\nuse crate::extensions::{\n    Base64Extension, EncodingExtension, MicrotaskExtension, StructuredCloneExtension,\n    TimeoutExtension,\n};\npub use extensions::RuntimeExtension;\n\n/// Register all the built-in objects and functions of the `WebAPI` runtime, plus\n/// any extensions defined.\n///\n/// # Errors\n/// This will error if any of the built-in objects or functions cannot be registered.\npub fn register(\n    extensions: impl RuntimeExtension,\n    realm: Option<boa_engine::realm::Realm>,\n    ctx: &mut boa_engine::Context,\n) -> boa_engine::JsResult<()> {\n    (\n        Base64Extension,\n        TimeoutExtension,\n        EncodingExtension,\n        MicrotaskExtension,\n        StructuredCloneExtension,\n        #[cfg(feature = \"url\")]\n        extensions::UrlExtension,\n        #[cfg(feature = \"process\")]\n        ProcessExtension,\n        #[cfg(feature = \"fetch\")]\n        extensions::AbortControllerExtension,\n        extensions,\n    )\n        .register(realm, ctx)?;\n\n    Ok(())\n}\n\n/// Register only the extensions provided. An application can use this to register\n/// extensions that it previously hadn't registered.\n///\n/// # Errors\n/// This will error if any of the built-in objects or functions cannot be registered.\npub fn register_extensions(\n    extensions: impl RuntimeExtension,\n    realm: Option<boa_engine::realm::Realm>,\n    ctx: &mut boa_engine::Context,\n) -> boa_engine::JsResult<()> {\n    extensions.register(realm, ctx)?;\n\n    Ok(())\n}\n\n#[cfg(test)]\npub(crate) mod test {\n    use crate::extensions::ConsoleExtension;\n    use crate::register;\n    use boa_engine::{Context, JsError, JsResult, JsValue, Source, builtins};\n    use std::borrow::Cow;\n    use std::path::{Path, PathBuf};\n    use std::pin::Pin;\n\n    /// A test action executed in a test function.\n    #[allow(missing_debug_implementations)]\n    pub(crate) struct TestAction(Inner);\n\n    #[allow(dead_code)]\n    #[allow(clippy::type_complexity)]\n    enum Inner {\n        RunHarness,\n        Run {\n            source: Cow<'static, str>,\n        },\n        RunFile {\n            path: PathBuf,\n        },\n        RunJobs,\n        InspectContext {\n            op: Box<dyn FnOnce(&mut Context)>,\n        },\n        InspectContextAsync {\n            op: Box<dyn for<'a> FnOnce(&'a mut Context) -> Pin<Box<dyn Future<Output = ()> + 'a>>>,\n        },\n        Assert {\n            source: Cow<'static, str>,\n        },\n        AssertEq {\n            source: Cow<'static, str>,\n            expected: JsValue,\n        },\n        AssertWithOp {\n            source: Cow<'static, str>,\n            op: fn(JsValue, &mut Context) -> bool,\n        },\n        AssertOpaqueError {\n            source: Cow<'static, str>,\n            expected: JsValue,\n        },\n        AssertNativeError {\n            source: Cow<'static, str>,\n            kind: builtins::error::ErrorKind,\n            message: &'static str,\n        },\n        AssertContext {\n            op: fn(&mut Context) -> bool,\n        },\n    }\n\n    impl TestAction {\n        #[allow(unused)]\n        pub(crate) fn harness() -> Self {\n            Self(Inner::RunHarness)\n        }\n\n        /// Runs `source`, panicking if the execution throws.\n        pub(crate) fn run(source: impl Into<Cow<'static, str>>) -> Self {\n            Self(Inner::Run {\n                source: source.into(),\n            })\n        }\n\n        /// Executes `op` with the currently active context.\n        ///\n        /// Useful to make custom assertions that must be done from Rust code.\n        pub(crate) fn inspect_context(op: impl FnOnce(&mut Context) + 'static) -> Self {\n            Self(Inner::InspectContext { op: Box::new(op) })\n        }\n\n        /// Executes `op` with the currently active context in an async environment.\n        pub(crate) fn inspect_context_async(op: impl AsyncFnOnce(&mut Context) + 'static) -> Self {\n            Self(Inner::InspectContextAsync {\n                op: Box::new(move |ctx| Box::pin(op(ctx))),\n            })\n        }\n    }\n\n    /// Executes a list of test actions on a new, default context.\n    #[track_caller]\n    pub(crate) fn run_test_actions(actions: impl IntoIterator<Item = TestAction>) {\n        let context = &mut Context::default();\n        register(ConsoleExtension::default(), None, context)\n            .expect(\"failed to register WebAPI objects\");\n        run_test_actions_with(actions, context);\n    }\n\n    /// Executes a list of test actions on the provided context.\n    #[track_caller]\n    #[allow(clippy::too_many_lines, clippy::missing_panics_doc)]\n    pub(crate) fn run_test_actions_with(\n        actions: impl IntoIterator<Item = TestAction>,\n        context: &mut Context,\n    ) {\n        #[track_caller]\n        fn forward_val(context: &mut Context, source: &str) -> JsResult<JsValue> {\n            context.eval(Source::from_bytes(source))\n        }\n\n        #[track_caller]\n        fn forward_file(context: &mut Context, path: impl AsRef<Path>) -> JsResult<JsValue> {\n            let p = path.as_ref();\n            context.eval(Source::from_filepath(p).map_err(JsError::from_rust)?)\n        }\n\n        #[track_caller]\n        fn fmt_test(source: &str, test: usize) -> String {\n            format!(\n                \"\\n\\nTest case {test}: \\n```\\n{}\\n```\",\n                textwrap::indent(source, \"    \")\n            )\n        }\n\n        // Some unwrapping patterns look weird because they're replaceable\n        // by simpler patterns like `unwrap_or_else` or `unwrap_err\n        let mut i = 1;\n        for action in actions.into_iter().map(|a| a.0) {\n            match action {\n                Inner::RunHarness => {\n                    if let Err(e) = forward_file(context, \"./assets/harness.js\") {\n                        panic!(\"Uncaught {e} in the test harness\");\n                    }\n                }\n                Inner::Run { source } => {\n                    if let Err(e) = forward_val(context, &source) {\n                        panic!(\"{}\\nUncaught {e}\", fmt_test(&source, i));\n                    }\n                }\n                Inner::RunFile { path } => {\n                    if let Err(e) = forward_file(context, &path) {\n                        panic!(\"Uncaught {e} in file {path:?}\");\n                    }\n                }\n                Inner::RunJobs => {\n                    if let Err(e) = context.run_jobs() {\n                        panic!(\"Uncaught {e} in a job\");\n                    }\n                }\n                Inner::InspectContext { op } => {\n                    op(context);\n                }\n                Inner::InspectContextAsync { op } => futures_lite::future::block_on(op(context)),\n                Inner::Assert { source } => {\n                    let val = match forward_val(context, &source) {\n                        Err(e) => panic!(\"{}\\nUncaught {e}\", fmt_test(&source, i)),\n                        Ok(v) => v,\n                    };\n                    let Some(val) = val.as_boolean() else {\n                        panic!(\n                            \"{}\\nTried to assert with the non-boolean value `{}`\",\n                            fmt_test(&source, i),\n                            val.display()\n                        )\n                    };\n                    assert!(val, \"{}\", fmt_test(&source, i));\n                    i += 1;\n                }\n                Inner::AssertEq { source, expected } => {\n                    let val = match forward_val(context, &source) {\n                        Err(e) => panic!(\"{}\\nUncaught {e}\", fmt_test(&source, i)),\n                        Ok(v) => v,\n                    };\n                    assert_eq!(val, expected, \"{}\", fmt_test(&source, i));\n                    i += 1;\n                }\n                Inner::AssertWithOp { source, op } => {\n                    let val = match forward_val(context, &source) {\n                        Err(e) => panic!(\"{}\\nUncaught {e}\", fmt_test(&source, i)),\n                        Ok(v) => v,\n                    };\n                    assert!(op(val, context), \"{}\", fmt_test(&source, i));\n                    i += 1;\n                }\n                Inner::AssertOpaqueError { source, expected } => {\n                    let err = match forward_val(context, &source) {\n                        Ok(v) => panic!(\n                            \"{}\\nExpected error, got value `{}`\",\n                            fmt_test(&source, i),\n                            v.display()\n                        ),\n                        Err(e) => e,\n                    };\n                    let Some(err) = err.as_opaque() else {\n                        panic!(\n                            \"{}\\nExpected opaque error, got native error `{}`\",\n                            fmt_test(&source, i),\n                            err\n                        )\n                    };\n\n                    assert_eq!(err, &expected, \"{}\", fmt_test(&source, i));\n                    i += 1;\n                }\n                Inner::AssertNativeError {\n                    source,\n                    kind,\n                    message,\n                } => {\n                    let err = match forward_val(context, &source) {\n                        Ok(v) => panic!(\n                            \"{}\\nExpected error, got value `{}`\",\n                            fmt_test(&source, i),\n                            v.display()\n                        ),\n                        Err(e) => e,\n                    };\n                    let native = match err.try_native(context) {\n                        Ok(err) => err,\n                        Err(e) => panic!(\n                            \"{}\\nCouldn't obtain a native error: {e}\",\n                            fmt_test(&source, i)\n                        ),\n                    };\n\n                    assert_eq!(native.kind(), &kind, \"{}\", fmt_test(&source, i));\n                    assert_eq!(native.message(), message, \"{}\", fmt_test(&source, i));\n                    i += 1;\n                }\n                Inner::AssertContext { op } => {\n                    assert!(op(context), \"Test case {i}\");\n                    i += 1;\n                }\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "core/runtime/src/message/mod.rs",
    "content": "//! Boa's implementation of the Message API (mainly `postMessage` and\n//! supporting functions).\n//!\n//! More information:\n//! - [MDN documentation][mdn]\n//!\n//! [mdn]: https://developer.mozilla.org/en-US/docs/Web/API/Window/postMessage\n\nuse crate::store::JsValueStore;\nuse boa_engine::realm::Realm;\nuse boa_engine::value::TryFromJs;\nuse boa_engine::{\n    Context, Finalize, JsData, JsResult, JsString, JsValue, NativeObject, Trace, boa_module,\n    js_error,\n};\nuse std::rc::Rc;\n\n#[cfg(test)]\nmod tests;\n\npub mod senders;\n\n/// A sender of a message. When registering the [`postMessage`][post_message]\n/// API, one must also register an implementation of this trait in the context.\n///\n/// This trait receives a [`JsValueStore`] value and ensures it gets\n/// delivered to the correct location, be it a Rust API, or another context\n/// altogether.\npub trait MessageSender: NativeObject {\n    /// Send a message to the necessary context.\n    ///\n    /// The second argument is the `targetOrigin` argument passed to\n    /// `postMessage`. No default value exists, and so it must be\n    /// resolved by the application. It can be used to filter messages.\n    ///\n    /// # Errors\n    /// Any errors in transit should be returned here. An error should be\n    /// returned before the full transfer is done and tell the application\n    /// that it should retry if possible.\n    ///\n    /// If an error happens after the transfer is done but before the\n    /// message is handled, this must be handled by the host application,\n    /// e.g., using a `messageerror` event.\n    fn send(&self, message: JsValueStore, target_origin: Option<JsString>) -> JsResult<()>;\n\n    /// Stop the sender from receiving messages. The default implementation\n    /// does nothing, but a successful implementation should try to stop\n    /// any queued jobs or anything that might block the context from\n    /// terminating.\n    ///\n    /// # Errors\n    /// Any errors in transit should be returned here.\n    fn stop(&self) -> JsResult<()> {\n        Ok(())\n    }\n}\n\n/// A reference counted pointer to a `MessageSender` implementation. This is so we\n/// can add this to the context, but we need to be able to an `Rc<>` structure to\n/// make API calls.\n#[derive(Debug, Trace, Finalize, JsData)]\nstruct MessageSenderRc<T: MessageSender>(#[unsafe_ignore_trace] Rc<T>);\n\nimpl<T: MessageSender> Clone for MessageSenderRc<T> {\n    fn clone(&self) -> Self {\n        Self(self.0.clone())\n    }\n}\n\n/// Options that can be passed as the second argument to `postMessage`.\n#[derive(Debug, Default, TryFromJs, Trace, Finalize)]\npub struct PostMessageOptions {\n    transfer: Option<Vec<JsValue>>,\n    target_origin: Option<JsString>,\n}\n\n/// Get a `MessageSender` instance from the context.\nfn get_sender<T: MessageSender>(context: &mut Context) -> JsResult<Rc<T>> {\n    // Try fetching from the context first, then the current realm. Else fail.\n    if let Some(sender) = context\n        .get_data::<MessageSenderRc<T>>()\n        .cloned()\n        .or_else(|| {\n            context\n                .realm()\n                .host_defined()\n                .get::<MessageSenderRc<T>>()\n                .cloned()\n        })\n    {\n        Ok(sender.0.clone())\n    } else {\n        Err(\n            js_error!(Error: \"Implementation of postMessage requires a sender registered in the context\"),\n        )\n    }\n}\n\n/// JavaScript module containing the `postMessage` function and supporting types.\n#[boa_module]\npub mod js_module {\n    use crate::message::{MessageSender, PostMessageOptions, get_sender};\n    use crate::store::JsValueStore;\n    use boa_engine::value::TryFromJs;\n    use boa_engine::{Context, JsValue};\n    use boa_engine::{JsResult, js_error};\n\n    /// The `postMessage` function. See [the mdn documentation][mdn].\n    ///\n    /// # Errors\n    /// Either an error from serializing the [`JsValue`] into a store, or the\n    /// sender returned an error.\n    ///\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/API/Window/postMessage\n    #[allow(clippy::needless_pass_by_value)]\n    pub fn post_message<T: MessageSender>(\n        message: JsValue,\n        target_origin_or_options: Option<JsValue>,\n        transfer: Option<Vec<JsValue>>,\n        context: &mut Context,\n    ) -> JsResult<()> {\n        // Build the options based on arguments.\n        let (target_origin, transfer) = if let Some(target_origin_or_options) =\n            target_origin_or_options\n        {\n            if let Ok(options) = PostMessageOptions::try_from_js(&target_origin_or_options, context)\n            {\n                (options.target_origin.clone(), options.transfer.clone())\n            } else if let Some(target_origin) = target_origin_or_options.as_string() {\n                (Some(target_origin), transfer)\n            } else {\n                return Err(js_error!(TypeError: \"targetOrigin must be a string or option object\"));\n            }\n        } else {\n            (None, None)\n        };\n\n        let message = JsValueStore::try_from_js(&message, context, transfer.unwrap_or_default())?;\n        let sender = get_sender::<T>(context)?;\n        sender.send(message, target_origin)\n    }\n}\n\n#[doc(inline)]\npub use js_module::post_message;\n\n/// Register the `postMessage` function in the realm or context.\n///\n/// # Errors\n/// If any of the classes fail to register, an error is returned.\npub fn register<S: MessageSender>(\n    sender: S,\n    realm: Option<Realm>,\n    context: &mut Context,\n) -> JsResult<()> {\n    if let Some(ref realm) = realm {\n        realm\n            .host_defined_mut()\n            .insert(MessageSenderRc(Rc::new(sender)));\n    } else {\n        context.insert_data(MessageSenderRc(Rc::new(sender)));\n    }\n    js_module::boa_register::<S>(realm, context)?;\n\n    Ok(())\n}\n"
  },
  {
    "path": "core/runtime/src/message/senders.rs",
    "content": "//! A collection of [`MessageSender`]s basic implementations.\n\nuse crate::message::MessageSender;\nuse crate::store::JsValueStore;\nuse boa_engine::job::NativeAsyncJob;\nuse boa_engine::object::builtins::JsFunction;\nuse boa_engine::value::TryIntoJs;\nuse boa_engine::{\n    Context, Finalize, JsData, JsError, JsResult, JsString, JsValue, Trace, js_string,\n};\nuse futures::StreamExt;\nuse futures::channel::mpsc::UnboundedSender;\n\n/// A [`MessageSender`] that reads the `onMessageQueue` property of the global\n/// object and calls it if it is a function. Note that this does not support\n/// event listeners, only checks the (made-up) `onMessageQueue` property. It\n/// also does not check the `targetOrigin` and accepts all messages.\n#[derive(Debug, Clone, Trace, Finalize, JsData)]\npub struct OnMessageQueueSender {\n    #[unsafe_ignore_trace]\n    sender: UnboundedSender<JsValueStore>,\n}\n\nimpl MessageSender for OnMessageQueueSender {\n    fn send(&self, message: JsValueStore, _target_origin: Option<JsString>) -> JsResult<()> {\n        self.sender\n            .unbounded_send(message)\n            .map_err(JsError::from_rust)?;\n        Ok(())\n    }\n\n    fn stop(&self) -> JsResult<()> {\n        self.sender.close_channel();\n        Ok(())\n    }\n}\n\nimpl OnMessageQueueSender {\n    /// Create a `MessageQueueSender` that sends messages to a message queue\n    /// array in the destination context. This type is send/sync and can be\n    /// registered in any and many separate `Context` for `postMessage`.\n    pub fn create(destination: &mut Context) -> Self {\n        let (sender, mut receiver) = futures::channel::mpsc::unbounded::<JsValueStore>();\n\n        destination.enqueue_job(\n            NativeAsyncJob::new(async move |ctx| {\n                while let Some(store) = receiver.next().await {\n                    let context = &mut ctx.borrow_mut();\n                    let v = store.try_into_js(context)?;\n                    let global = context.global_object();\n                    if let Some(x) = global\n                        .get(js_string!(\"onMessageQueue\"), context)?\n                        .as_callable()\n                        .and_then(JsFunction::from_object)\n                    {\n                        x.call(&JsValue::undefined(), &[v], context)?;\n                    }\n                }\n\n                Ok(JsValue::undefined())\n            })\n            .into(),\n        );\n\n        Self { sender }\n    }\n}\n"
  },
  {
    "path": "core/runtime/src/message/tests.rs",
    "content": "//! Tests for the `postMessage` extension.\n\nuse crate::message;\nuse crate::message::senders::OnMessageQueueSender;\nuse crate::test::{TestAction, run_test_actions_with};\nuse boa_engine::job::{JobExecutor, SimpleJobExecutor};\nuse boa_engine::{Context, js_string};\nuse futures_lite::future;\nuse std::cell::RefCell;\nuse std::thread;\nuse std::time::Duration;\n\n/// Create a basic context and allow postMessage from the same context.\n#[test]\nfn basic() {\n    let context = &mut Context::default();\n\n    let sender = OnMessageQueueSender::create(context);\n    message::register(sender, None, context).unwrap();\n\n    run_test_actions_with(\n        [\n            TestAction::harness(),\n            TestAction::run(\n                r#\"\n                let latestMessage = null;\n                function onMessageQueue(message) {\n                    latestMessage = message;\n                }\n\n                const message = { \"hello\": \"world\" };\n                postMessage(message);\n                assert(latestMessage === null);\n            \"#,\n            ),\n            TestAction::inspect_context(move |context| {\n                drop(future::block_on(future::poll_once(\n                    context\n                        .downcast_job_executor::<SimpleJobExecutor>()\n                        .expect(\"\")\n                        .run_jobs_async(&RefCell::new(context)),\n                )));\n            }),\n            TestAction::run(\n                r#\"\n                assert(latestMessage !== null);\n                assert(latestMessage !== message);\n                assertEq(latestMessage.hello, \"world\");\n            \"#,\n            ),\n        ],\n        context,\n    );\n}\n\n#[test]\nfn shared_multi_thread() {\n    let (sender, receiver) = std::sync::mpsc::channel::<OnMessageQueueSender>();\n\n    let destination_handle = thread::spawn(move || {\n        let context = &mut Context::default();\n\n        // It's important to declare the `onMessageQueue` function before we might\n        // receive any messages, as those will be lost.\n        run_test_actions_with(\n            [\n                TestAction::harness(),\n                TestAction::run(\n                    r#\"\n                    done = false;\n                    function onMessageQueue(message) {\n                        assert(message.hello === \"world\");\n                        done = true;\n                    }\n                \"#,\n                ),\n            ],\n            context,\n        );\n\n        sender.send(OnMessageQueueSender::create(context)).unwrap();\n\n        loop {\n            thread::sleep(Duration::from_millis(10));\n            context.run_jobs().unwrap();\n\n            let global_object = context.global_object();\n            if global_object\n                .get(js_string!(\"done\"), context)\n                .unwrap()\n                .as_boolean()\n                == Some(true)\n            {\n                break;\n            }\n        }\n    });\n\n    let source_handle = thread::spawn(move || {\n        let context = &mut Context::default();\n        let message_sender = receiver.recv().unwrap();\n        message::register(message_sender, None, context).unwrap();\n\n        run_test_actions_with(\n            [TestAction::run(\n                r#\"\n                    const message = { \"hello\": \"world\" };\n                    postMessage(message);\n                \"#,\n            )],\n            context,\n        );\n    });\n\n    source_handle.join().unwrap();\n    destination_handle.join().unwrap();\n}\n\n#[test]\nfn shared_array_buffer() {\n    let (sender, receiver) = std::sync::mpsc::channel::<OnMessageQueueSender>();\n\n    let destination_handle = thread::spawn(move || {\n        let context = &mut Context::default();\n\n        // It's important to declare the `onMessageQueue` function before we might\n        // receive any messages, as those will be lost.\n        run_test_actions_with(\n            [\n                TestAction::harness(),\n                TestAction::run(\n                    r#\"\n                    done = false;\n                    function onMessageQueue(message) {\n                        const shared = message.shared;\n                        const view = new DataView(shared);\n                        while (true) {\n                            if (view.getUint8(0) == 1) {\n                                view.setUint8(0, 2);\n                                break;\n                            }\n                        }\n\n                        // Verify that it also comes back.\n                        while (true) {\n                            if (view.getUint8(0) == 3) {\n                                done = true;\n                                break;\n                            }\n                        }\n                    }\n                \"#,\n                ),\n            ],\n            context,\n        );\n\n        sender.send(OnMessageQueueSender::create(context)).unwrap();\n\n        loop {\n            thread::sleep(Duration::from_millis(10));\n            context.run_jobs().unwrap();\n\n            let global_object = context.global_object();\n            if global_object\n                .get(js_string!(\"done\"), context)\n                .unwrap()\n                .as_boolean()\n                == Some(true)\n            {\n                break;\n            }\n        }\n    });\n\n    let source_handle = thread::spawn(move || {\n        let context = &mut Context::default();\n\n        let message_sender = receiver.recv().unwrap();\n        message::register(message_sender, None, context).unwrap();\n\n        run_test_actions_with(\n            [TestAction::run(\n                r#\"\n                    const shared = new SharedArrayBuffer(1);\n                    const message = { shared };\n                    postMessage(message);\n\n                    // Set shared to 1.\n                    const view = new DataView(shared);\n                    view.setUint8(0, 1);\n\n                    // This would never work if the two contexts are in the same context.\n                    while (true) {\n                        if (view.getUint8(0) == 2) {\n                            view.setUint8(0, 3);\n                            break;\n                        }\n                    }\n                \"#,\n            )],\n            context,\n        );\n    });\n\n    source_handle.join().unwrap();\n    destination_handle.join().unwrap();\n}\n"
  },
  {
    "path": "core/runtime/src/microtask/mod.rs",
    "content": "//! Microtask-related functions and types.\nuse boa_engine::realm::Realm;\nuse boa_engine::{Context, JsResult, boa_module};\n\n#[cfg(test)]\nmod tests;\n\n/// JavaScript module containing the `queueMicrotask` function.\n#[boa_module]\npub mod js_module {\n    use boa_engine::job::{Job, PromiseJob};\n    use boa_engine::object::builtins::JsFunction;\n    use boa_engine::{Context, JsValue};\n\n    /// The [`queueMicrotask()`][mdn] method of the `Window` interface queues a\n    /// microtask to be executed at a safe time prior to control returning to\n    /// the browser's event loop.\n    ///\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/API/Window/queueMicrotask\n    pub fn queue_microtask(callback: JsFunction, context: &mut Context) {\n        context.enqueue_job(Job::from(PromiseJob::new(move |context| {\n            callback.call(&JsValue::undefined(), &[], context)\n        })));\n    }\n}\n\n/// Register the `queueMicrotask` function to the realm or context.\n///\n/// # Errors\n/// Returns an error if the microtask extension cannot be registered.\npub fn register(realm: Option<Realm>, context: &mut Context) -> JsResult<()> {\n    js_module::boa_register(realm, context)\n}\n"
  },
  {
    "path": "core/runtime/src/microtask/tests.rs",
    "content": "use crate::RuntimeExtension;\nuse crate::console::tests::RecordingLogger;\nuse crate::test::{TestAction, run_test_actions_with};\nuse boa_engine::Context;\nuse indoc::indoc;\n\n#[test]\nfn queue_microtask() {\n    let context = &mut Context::default();\n    crate::microtask::register(None, context).unwrap();\n    let logger = RecordingLogger::default();\n    crate::extensions::ConsoleExtension(logger.clone())\n        .register(None, context)\n        .unwrap();\n\n    run_test_actions_with(\n        [\n            TestAction::run(indoc! {r#\"\n                console.log(1);\n                queueMicrotask(() => console.log(2));\n                console.log(3);\n                queueMicrotask(() => {\n                    console.log(4);\n                    queueMicrotask(() => {\n                        console.log(5);\n                        queueMicrotask(() => console.log(6));\n                        console.log(7);\n                    });\n                    console.log(8);\n                });\n                console.log(9);\n            \"#}),\n            TestAction::inspect_context(|context| {\n                context.run_jobs().unwrap();\n            }),\n        ],\n        context,\n    );\n\n    let logs = logger.log.borrow().clone();\n    assert_eq!(\n        logs,\n        indoc! { r#\"\n            1\n            3\n            9\n            2\n            4\n            8\n            5\n            7\n            6\n        \"# }\n    );\n}\n"
  },
  {
    "path": "core/runtime/src/process/mod.rs",
    "content": "//! Boa's implementation of Node.js' `process` object.  \n//!  \n//! The `process` object can be accessed from any global object.  \n//!  \n//! More information:  \n//!  - [Node.js documentation][node]  \n//!  \n//! [node]: https://nodejs.org/api/process.html  \n\n#[cfg(test)]\npub(crate) mod tests;\n\nuse boa_engine::{\n    Context, JsData, JsObject, JsResult, JsString, JsSymbol, JsValue, js_error, js_string,\n    native_function::NativeFunction, object::ObjectInitializer, property::Attribute,\n};\nuse boa_gc::{Finalize, Trace};\nuse std::rc::Rc;\n\n/// A trait that can be used to forward process provider to an implementation.  \npub trait ProcessProvider: Trace {\n    /// Get current working directory (`process.cwd()`)  \n    ///  \n    /// # Errors  \n    /// Returns an error if the current directory cannot be obtained.  \n    fn cwd(&self) -> JsResult<JsString>;\n\n    /// Get environment variables so as to allow env property (`process.env`)\n    fn env(&self) -> impl IntoIterator<Item = (JsString, JsString)>;\n}\n\n/// The default std implementation of the process provider.  \n///  \n/// Implements the [`ProcessProvider`] trait. Outputs the process properties'\n/// values on the basis of std.  \n#[derive(Debug, Trace, Finalize)]\npub struct StdProcessProvider;\n\nimpl ProcessProvider for StdProcessProvider {\n    fn cwd(&self) -> JsResult<JsString> {\n        let path = std::env::current_dir().map_err(\n            |e| js_error!(TypeError: \"failed to get current working directory: {}\", e.to_string()),\n        )?;\n        Ok(js_string!(path.to_string_lossy()))\n    }\n\n    fn env(&self) -> impl IntoIterator<Item = (JsString, JsString)> {\n        std::env::vars().map(|(k, v)| (js_string!(k), js_string!(v)))\n    }\n}\n\n/// Boa's implementation of Node.js' `process` object.  \n#[derive(Debug, Trace, Finalize, JsData)]\npub struct Process;\n\nimpl Process {\n    /// Name of the built-in `process` property.  \n    pub const NAME: JsString = js_string!(\"process\");\n\n    /// Initializes the `process` built-in object with a custom provider.  \n    ///  \n    /// # Errors  \n    ///  \n    /// Returns a `JsError` if:  \n    /// - Custom process provider returns an error  \n    /// - Defining the `cwd` and `env` properties on the `process` object fails  \n    pub fn init_with_provider<P>(context: &mut Context, provider: P) -> JsResult<JsObject>\n    where\n        P: ProcessProvider + 'static,\n    {\n        fn process_method<P: ProcessProvider + 'static>(\n            f: fn(&JsValue, &[JsValue], &P, &mut Context) -> JsResult<JsValue>,\n            provider: Rc<P>,\n        ) -> NativeFunction {\n            // SAFETY: `Process` doesn't contain types that need tracing.\n            unsafe {\n                NativeFunction::from_closure(move |this, args, context| {\n                    f(this, args, &provider, context)\n                })\n            }\n        }\n\n        let provider = Rc::new(provider);\n\n        let env = JsObject::default(context.intrinsics());\n        for (key, value) in provider.env() {\n            env.set(key, JsValue::from(value), false, context)?;\n        }\n\n        Ok(ObjectInitializer::new(context)\n            .property(\n                JsSymbol::to_string_tag(),\n                Self::NAME,\n                Attribute::CONFIGURABLE,\n            )\n            .property(\n                js_string!(\"env\"),\n                env,\n                Attribute::WRITABLE | Attribute::CONFIGURABLE,\n            )\n            .function(\n                process_method(\n                    |_, _, provider, _| provider.cwd().map(JsValue::from),\n                    provider.clone(),\n                ),\n                js_string!(\"cwd\"),\n                0,\n            )\n            .build())\n    }\n\n    /// Register the `process` object globally by a custom provider.  \n    ///  \n    /// # Errors  \n    /// This function will return an error if the property cannot be defined on the global object.  \n    pub fn register_with_provider<P>(context: &mut Context, provider: P) -> JsResult<()>\n    where\n        P: ProcessProvider + 'static,\n    {\n        let process_object = Self::init_with_provider(context, provider)?;\n\n        context.register_global_property(\n            js_string!(Self::NAME),\n            process_object,\n            Attribute::CONFIGURABLE,\n        )?;\n\n        Ok(())\n    }\n\n    /// Initializes the `process` built-in object with the default std provider.  \n    ///  \n    /// # Errors  \n    ///  \n    /// Returns a `JsError` if:  \n    /// - Defining the `cwd` and `env` properties on the `process` object fails  \n    pub fn init(context: &mut Context) -> JsResult<JsObject> {\n        Self::init_with_provider(context, StdProcessProvider)\n    }\n\n    /// Register the `process` object globally by the default std provider.  \n    ///  \n    /// # Errors  \n    /// This function will return an error if the property cannot be defined on the global object.  \n    pub fn register(context: &mut Context) -> JsResult<()> {\n        Self::register_with_provider(context, StdProcessProvider)\n    }\n}\n"
  },
  {
    "path": "core/runtime/src/process/tests.rs",
    "content": "use crate::test::{TestAction, run_test_actions_with};\nuse boa_engine::Context;\nuse indoc::indoc;\n\n/// Test harness functions for process tests.  \nconst TEST_HARNESS: &str = r#\"  \nfunction assert_true(condition, message) {  \n    if (!condition) {  \n        throw new Error(`Assertion failed: ${message}`);  \n    }  \n}  \nfunction assert_own_property(obj, prop) {  \n    assert_true(  \n        Object.prototype.hasOwnProperty.call(obj, prop),  \n        `Expected ${prop.toString()} to be an own property`,  \n    );  \n}  \nfunction assert_equals(actual, expected, message) {  \n    assert_true(  \n        actual === expected,  \n        `${message} (actual: ${actual.toString()}, expected: ${expected.toString()})`,  \n    );  \n}  \nconst self = globalThis;  \n\"#;\n\n#[test]\nfn process_object_registration() {\n    let mut context = Context::default();\n    crate::process::Process::register(&mut context).unwrap();\n\n    run_test_actions_with(\n        [\n            TestAction::run(TEST_HARNESS),\n            TestAction::run(indoc! {r#\"\n                assert_true(globalThis.hasOwnProperty(\"process\"));  \n                assert_equals(typeof process, \"object\");  \n            \"#}),\n        ],\n        &mut context,\n    );\n}\n\n#[test]\nfn process_property_descriptors() {\n    let mut context = Context::default();\n    crate::process::Process::register(&mut context).unwrap();\n\n    run_test_actions_with(\n        [\n            TestAction::run(TEST_HARNESS),\n            TestAction::run(indoc! {r#\"\n                const propDesc = Object.getOwnPropertyDescriptor(self, \"process\");  \n                assert_equals(propDesc.writable, false, \"must not be writable\");  \n                assert_equals(propDesc.enumerable, false, \"must not be enumerable\");  \n                assert_equals(propDesc.configurable, true, \"must be configurable\");  \n                assert_equals(propDesc.value, process, \"must have the right value\");  \n            \"#}),\n        ],\n        &mut context,\n    );\n}\n\n#[test]\nfn process_cwd_method() {\n    let mut context = Context::default();\n    crate::process::Process::register(&mut context).unwrap();\n\n    run_test_actions_with(\n        [\n            TestAction::run(TEST_HARNESS),\n            TestAction::run(indoc! {r#\"\n                assert_own_property(process, \"cwd\");\n                assert_equals(typeof process.cwd, \"function\");\n                \n                const cwd = process.cwd();\n                assert_equals(typeof cwd, \"string\");\n                assert_true(cwd.length > 0, \"cwd should not be empty\");\n                \n                const cwdDesc = Object.getOwnPropertyDescriptor(process, \"cwd\");\n                assert_equals(cwdDesc.writable, true, \"cwd must be writable\");\n                assert_equals(cwdDesc.enumerable, false, \"cwd must not be enumerable\");\n                assert_equals(cwdDesc.configurable, true, \"cwd must be configurable\");\n            \"#}),\n        ],\n        &mut context,\n    );\n}\n\n#[test]\n#[ignore = \"Unsafe under parallel test execution as it tempers with env.\"]\nfn process_env_contains_variables() {\n    temp_env::with_vars(\n        [\n            (\"TEST_VAR\", Some(\"test_value\")),\n            (\"ANOTHER_VAR\", Some(\"another_value\")),\n        ],\n        || {\n            let mut context = Context::default();\n            crate::process::Process::register(&mut context).unwrap();\n\n            run_test_actions_with(\n                [\n                    TestAction::run(TEST_HARNESS),\n                    TestAction::run(indoc! {r#\"\n                    assert_own_property(process, \"env\");  \n                    assert_equals(typeof process.env, \"object\");  \n                    assert_equals(process.env.TEST_VAR, \"test_value\");  \n                    assert_equals(process.env.ANOTHER_VAR, \"another_value\");  \n                \"#}),\n                ],\n                &mut context,\n            );\n        },\n    );\n}\n\n#[test]\n#[ignore = \"Unsafe under parallel test execution as it tempers with env.\"]\nfn process_env_properties_writable() {\n    temp_env::with_var(\"TEST_VAR\", Some(\"original\"), || {\n        let mut context = Context::default();\n        crate::process::Process::register(&mut context).unwrap();\n\n        run_test_actions_with(\n            [\n                TestAction::run(TEST_HARNESS),\n                TestAction::run(indoc! {r#\"\n                    // Test that env properties are writable  \n                    process.env.TEST_VAR = \"modified\";  \n                    assert_equals(process.env.TEST_VAR, \"modified\");  \n                      \n                    // Test adding new properties  \n                    process.env.NEW_VAR = \"new_value\";  \n                    assert_equals(process.env.NEW_VAR, \"new_value\");  \n                \"#}),\n            ],\n            &mut context,\n        );\n    });\n}\n\n#[test]\nfn process_env_object_properties() {\n    let mut context = Context::default();\n    crate::process::Process::register(&mut context).unwrap();\n\n    run_test_actions_with(\n        [\n            TestAction::run(TEST_HARNESS),\n            TestAction::run(indoc! {r#\"\n                const envDesc = Object.getOwnPropertyDescriptor(process, \"env\");  \n                assert_equals(envDesc.writable, true, \"env must be writable\");  \n                assert_equals(envDesc.enumerable, false, \"env must not be enumerable\");  \n                assert_equals(envDesc.configurable, true, \"env must be configurable\");  \n                assert_equals(typeof envDesc.value, \"object\", \"env must be an object\");  \n            \"#}),\n        ],\n        &mut context,\n    );\n}\n\n#[test]\n#[ignore = \"Unsafe under parallel test execution as it tempers with env.\"]\nfn process_env_iteration() {\n    temp_env::with_vars(\n        [\n            (\"ITER_TEST_1\", Some(\"value1\")),\n            (\"ITER_TEST_2\", Some(\"value2\")),\n        ],\n        || {\n            let mut context = Context::default();\n            crate::process::Process::register(&mut context).unwrap();\n\n            run_test_actions_with(\n                [\n                    TestAction::run(TEST_HARNESS),\n                    TestAction::run(indoc! {r#\"\n                    let found1 = false, found2 = false;  \n                    for (let key in process.env) {  \n                        if (key === \"ITER_TEST_1\") {  \n                            assert_equals(process.env[key], \"value1\");  \n                            found1 = true;  \n                        }  \n                        if (key === \"ITER_TEST_2\") {  \n                            assert_equals(process.env[key], \"value2\");  \n                            found2 = true;  \n                        }  \n                    }  \n                    assert_true(found1, \"ITER_TEST_1 should be found\");  \n                    assert_true(found2, \"ITER_TEST_2 should be found\");  \n                \"#}),\n                ],\n                &mut context,\n            );\n        },\n    );\n}\n"
  },
  {
    "path": "core/runtime/src/store/from.rs",
    "content": "//! All methods for serializing a [`JsValue`] into a [`JsValueStore`].\n\nuse crate::store::{JsValueStore, StringStore, ValueStoreInner, unsupported_type};\nuse boa_engine::builtins::array_buffer::{AlignedVec, ArrayBuffer};\nuse boa_engine::builtins::error::Error;\nuse boa_engine::object::builtins::{\n    JsArray, JsArrayBuffer, JsDataView, JsDate, JsMap, JsRegExp, JsSet, JsSharedArrayBuffer,\n    JsTypedArray,\n};\nuse boa_engine::property::PropertyKey;\nuse boa_engine::{Context, JsError, JsObject, JsResult, JsString, JsValue, JsVariant, js_error};\nuse std::collections::{HashMap, HashSet};\n\n/// A Map of seen objects when walking through the value. We use the address\n/// of the inner object as it is unique per JavaScript value.\n#[derive(Default)]\npub(super) struct SeenMap(HashMap<usize, JsValueStore>);\n\nimpl SeenMap {\n    fn get(&self, object: &JsObject) -> Option<JsValueStore> {\n        let addr = std::ptr::from_ref(object.as_ref()).addr();\n        self.0.get(&addr).cloned()\n    }\n\n    fn insert(&mut self, original: &JsObject, object: JsValueStore) {\n        let addr = std::ptr::from_ref(original.as_ref()).addr();\n        self.0.insert(addr, object);\n    }\n}\n\n/// Return true if an object is transferable.\npub(super) fn is_transferable(object: &JsObject) -> bool {\n    // The only transferable object supported for now is ArrayBuffer.\n    object.downcast_mut::<ArrayBuffer>().is_some()\n}\n\n/// The core logic of the [`JsValueStore::try_from_js`] function.\nfn try_from_js_object(\n    value: &JsObject,\n    transfer: &HashSet<JsObject>,\n    seen: &mut SeenMap,\n    context: &mut Context,\n) -> JsResult<JsValueStore> {\n    // Have we seen this object? If so, return its clone.\n    if let Some(o2) = seen.get(value) {\n        return Ok(o2.clone());\n    }\n\n    // Is it a transferable object?\n    let new_value = if transfer.contains(value) {\n        try_from_js_object_transfer(value, context)?\n    } else {\n        try_from_js_object_clone(value, transfer, seen, context)?\n    };\n\n    Ok(new_value)\n}\n\n/// Transfer an object into a store instead of cloning it. See [mdn].\n///\n/// Only [transferable objects][to] can be transferred. Anything else will return an\n/// error. Since any object t\n///\n/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API/Transferable_objects\n/// [to]: https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API/Transferable_objects#supported_objects\nfn try_from_js_object_transfer(\n    object: &JsObject,\n    _context: &mut Context,\n) -> JsResult<JsValueStore> {\n    if let Some(mut buffer) = object.downcast_mut::<ArrayBuffer>() {\n        let data = buffer.detach(&JsValue::undefined())?;\n        let data = data.ok_or_else(unsupported_type)?;\n\n        Ok(JsValueStore::new(ValueStoreInner::ArrayBuffer(data)))\n    } else {\n        Err(unsupported_type())\n    }\n}\n\nfn try_from_array_clone(\n    array: &JsArray,\n    transfer: &HashSet<JsObject>,\n    seen: &mut SeenMap,\n    context: &mut Context,\n) -> JsResult<JsValueStore> {\n    // Create an empty clone, we will replace its inner values after we gather them.\n    // To stop the recursion, we need to add the right value to the seen map prior,\n    // though.\n    let mut dolly = JsValueStore::empty();\n    seen.insert(&JsObject::from(array.clone()), dolly.clone());\n\n    let length = array.length(context)?;\n    let length = usize::try_from(length).map_err(JsError::from_rust)?;\n    let mut inner = Vec::with_capacity(length);\n    for i in 0..length {\n        let v = array\n            .borrow()\n            .properties()\n            .get(&i.into())\n            .and_then(|x| x.value().cloned());\n        if let Some(v) = v {\n            let v = try_from_js_value(&v, transfer, seen, context)?;\n            inner.push(Some(v));\n        } else {\n            inner.push(None);\n        }\n    }\n\n    // SAFETY: This is safe as this function is the sole owner of the store.\n    unsafe {\n        dolly.replace(ValueStoreInner::Array(inner));\n    }\n    Ok(dolly)\n}\n\nfn try_from_array_buffer_clone(\n    original: &JsObject,\n    buffer: &JsArrayBuffer,\n    seen: &mut SeenMap,\n) -> JsResult<JsValueStore> {\n    let data = buffer.data().ok_or_else(unsupported_type)?;\n    let data = AlignedVec::from_slice(0, &data);\n    let new_value = JsValueStore::new(ValueStoreInner::ArrayBuffer(data));\n    seen.insert(original, new_value.clone());\n\n    Ok(new_value)\n}\n\nfn try_from_shared_array_buffer(\n    original: &JsObject,\n    buffer: &JsSharedArrayBuffer,\n    seen: &mut SeenMap,\n) -> JsValueStore {\n    let new_value = JsValueStore::new(ValueStoreInner::SharedArrayBuffer(buffer.inner()));\n    seen.insert(original, new_value.clone());\n    new_value\n}\n\nfn clone_typed_array(\n    original: &JsObject,\n    buffer: &JsTypedArray,\n    transfer: &HashSet<JsObject>,\n    seen: &mut SeenMap,\n    context: &mut Context,\n) -> JsResult<JsValueStore> {\n    let kind = buffer.kind().ok_or_else(unsupported_type)?;\n    let buffer = buffer.buffer(context)?;\n    let buffer = try_from_js_value(&buffer, transfer, seen, context)?;\n    let dolly = JsValueStore::new(ValueStoreInner::TypedArray { kind, buffer });\n    seen.insert(original, dolly.clone());\n    Ok(dolly)\n}\n\nfn clone_date(\n    original: &JsObject,\n    date: &JsDate,\n    seen: &mut SeenMap,\n    context: &mut Context,\n) -> JsResult<JsValueStore> {\n    let ms_since_epoch = date\n        .get_time(context)?\n        .as_number()\n        .ok_or_else(unsupported_type)?;\n\n    let stored = JsValueStore::new(ValueStoreInner::Date(ms_since_epoch));\n    seen.insert(original, stored.clone());\n    Ok(stored)\n}\n\nfn clone_regexp(\n    original: &JsObject,\n    regexp: &JsRegExp,\n    seen: &mut SeenMap,\n    context: &mut Context,\n) -> JsResult<JsValueStore> {\n    let source = regexp.source(context)?;\n    let flags = regexp.flags(context)?;\n\n    let stored = JsValueStore::new(ValueStoreInner::RegExp { source, flags });\n    seen.insert(original, stored.clone());\n    Ok(stored)\n}\n\nfn try_from_map(\n    original: &JsObject,\n    map: &JsMap,\n    transfer: &HashSet<JsObject>,\n    seen: &mut SeenMap,\n    context: &mut Context,\n) -> JsResult<JsValueStore> {\n    let mut new_map = Vec::new();\n    let mut store = JsValueStore::new(ValueStoreInner::Empty);\n    seen.insert(original, store.clone());\n\n    map.for_each_native(|k, v| {\n        let key = try_from_js_value(&k, transfer, seen, context)?;\n        let value = try_from_js_value(&v, transfer, seen, context)?;\n        new_map.push((key, value));\n\n        Ok(())\n    })?;\n\n    // SAFETY: This is safe as this function is the sole owner of the store.\n    unsafe {\n        store.replace(ValueStoreInner::Map(new_map));\n    }\n\n    Ok(store)\n}\n\nfn try_from_set(\n    original: &JsObject,\n    set: &JsSet,\n    transfer: &HashSet<JsObject>,\n    seen: &mut SeenMap,\n    context: &mut Context,\n) -> JsResult<JsValueStore> {\n    let mut new_set = Vec::new();\n    let mut store = JsValueStore::new(ValueStoreInner::Empty);\n    seen.insert(original, store.clone());\n\n    set.for_each_native(|v| {\n        let value = try_from_js_value(&v, transfer, seen, context)?;\n        new_set.push(value);\n\n        Ok(())\n    })?;\n\n    // SAFETY: This is safe as this function is the sole owner of the store.\n    unsafe {\n        store.replace(ValueStoreInner::Set(new_set));\n    }\n\n    Ok(store)\n}\n\nfn try_from_js_object_clone(\n    object: &JsObject,\n    transfer: &HashSet<JsObject>,\n    seen: &mut SeenMap,\n    context: &mut Context,\n) -> JsResult<JsValueStore> {\n    // If this is a special type of object, apply some special rules to it.\n    // Described in\n    // https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API/Structured_clone_algorithm#supported_types\n\n    if let Ok(array) = JsArray::from_object(object.clone()) {\n        return try_from_array_clone(&array, transfer, seen, context);\n    } else if let Ok(map) = JsMap::from_object(object.clone()) {\n        return try_from_map(object, &map, transfer, seen, context);\n    } else if let Ok(set) = JsSet::from_object(object.clone()) {\n        return try_from_set(object, &set, transfer, seen, context);\n    } else if let Ok(ref buffer) = JsArrayBuffer::from_object(object.clone()) {\n        return try_from_array_buffer_clone(object, buffer, seen);\n    } else if let Ok(ref buffer) = JsSharedArrayBuffer::from_object(object.clone()) {\n        return Ok(try_from_shared_array_buffer(object, buffer, seen));\n    } else if let Ok(ref typed_array) = JsTypedArray::from_object(object.clone()) {\n        return clone_typed_array(object, typed_array, transfer, seen, context);\n    } else if let Ok(ref date) = JsDate::from_object(object.clone()) {\n        return clone_date(object, date, seen, context);\n    } else if let Ok(_error) = object.clone().downcast::<Error>() {\n        return Err(js_error!(TypeError: \"Errors are not supported yet.\"));\n    } else if let Ok(ref regexp) = JsRegExp::from_object(object.clone()) {\n        return clone_regexp(object, regexp, seen, context);\n    } else if let Ok(_dataview) = JsDataView::from_object(object.clone()) {\n        return Err(js_error!(TypeError: \"Data views are not supported yet.\"));\n    } else if object.is_callable() {\n        // Functions are invalid.\n        return Err(unsupported_type());\n    }\n\n    // Create a new object and add own properties to it. This does not preserve\n    // the prototype (nor do we want to).\n    let mut dolly = JsValueStore::empty();\n    seen.insert(object, dolly.clone());\n\n    let keys = object.own_property_keys(context)?;\n    let mut fields: Vec<(StringStore, JsValueStore)> = Vec::with_capacity(keys.len());\n    for k in keys {\n        let value = object.get(k.clone(), context)?;\n        let key = match k {\n            PropertyKey::String(s) => s.into(),\n            PropertyKey::Symbol(_) => return Err(unsupported_type()),\n            PropertyKey::Index(i) => JsString::from(format!(\"{}\", i.get())).into(),\n        };\n\n        let v = try_from_js_value(&value, transfer, seen, context)?;\n        fields.push((key, v));\n    }\n\n    // SAFETY: This is safe as this function is the sole owner of the store.\n    unsafe {\n        dolly.replace(ValueStoreInner::Object(fields));\n    }\n    Ok(dolly)\n}\n\npub(super) fn try_from_js_value(\n    value: &JsValue,\n    transfer: &HashSet<JsObject>,\n    seen: &mut SeenMap,\n    context: &mut Context,\n) -> JsResult<JsValueStore> {\n    match value.variant() {\n        JsVariant::Null => Ok(JsValueStore::new(ValueStoreInner::Null)),\n        JsVariant::Undefined => Ok(JsValueStore::new(ValueStoreInner::Undefined)),\n        JsVariant::Boolean(b) => Ok(JsValueStore::new(ValueStoreInner::Boolean(b))),\n        JsVariant::String(s) => Ok(JsValueStore::new(ValueStoreInner::String(s.into()))),\n        JsVariant::Float64(f) => Ok(JsValueStore::new(ValueStoreInner::Float(f))),\n        JsVariant::Integer32(i) => Ok(JsValueStore::new(ValueStoreInner::Float(f64::from(i)))),\n        JsVariant::BigInt(b) => Ok(JsValueStore::new(ValueStoreInner::BigInt(\n            b.as_inner().clone(),\n        ))),\n        JsVariant::Object(ref o) => try_from_js_object(o, transfer, seen, context),\n\n        // Symbols cannot be transferred/cloned.\n        JsVariant::Symbol(_) => Err(unsupported_type()),\n    }\n}\n"
  },
  {
    "path": "core/runtime/src/store/mod.rs",
    "content": "//! Module containing the types related to the [`JsValueStore`].\nuse boa_engine::bigint::RawBigInt;\nuse boa_engine::builtins::array_buffer::{AlignedVec, SharedArrayBuffer};\nuse boa_engine::builtins::error::ErrorKind;\nuse boa_engine::builtins::typed_array::TypedArrayKind;\nuse boa_engine::value::TryIntoJs;\nuse boa_engine::{Context, JsError, JsResult, JsString, JsValue, JsVariant, js_error};\nuse std::collections::HashSet;\nuse std::sync::Arc;\n\nmod from;\nmod to;\n\n/// Convenience method to avoid copy-pasting the same message.\n#[inline]\nfn unsupported_type() -> JsError {\n    js_error!(Error: \"DataCloneError: unsupported type for structured data\")\n}\n\n#[inline]\nfn unsupported_transfer() -> JsError {\n    js_error!(TypeError: \"Found an invalid value in transferList\")\n}\n\n/// A type to help store [`JsString`]. Because [`JsString`] relies on [`std::rc::Rc`],\n/// it cannot be `Send`, which is a necessary contract for the Store. The [`StringStore`]\n/// can be transformed from and into `JsString`, but owns its data. It is _not_ copy-on-\n/// write.\n#[derive(Debug, Eq, PartialEq, Hash)]\nstruct StringStore(Vec<u16>);\n\nimpl StringStore {\n    fn to_js_string(&self) -> JsString {\n        JsString::from(self.0.as_slice())\n    }\n}\n\nimpl From<JsString> for StringStore {\n    fn from(value: JsString) -> Self {\n        Self(value.to_vec())\n    }\n}\n\nimpl From<StringStore> for JsString {\n    fn from(value: StringStore) -> Self {\n        JsString::from(value.0.as_slice())\n    }\n}\n\n/// Inner value for [`JsValueStore`].\n#[derive(Debug)]\nenum ValueStoreInner {\n    /// An Empty value that will be filled later. This is only used during\n    /// construction, and if encountered at other points will result\n    /// in an error.\n    Empty,\n\n    /// Primitive values - `null`.\n    Null,\n\n    /// Primitive values - `undefined`.\n    Undefined,\n\n    /// Primitive values - `Boolean`.\n    Boolean(bool),\n\n    /// Primitive values - `float64`. No need to store integers separately,\n    /// they'll be checked when recreating the `JsValue`.\n    Float(f64),\n\n    /// [`JsString`]s are context-free, but not `Send`. Since we want to be\n    /// `Send`, we'll have to make a copy of the data.\n    String(StringStore),\n\n    /// [`boa_engine::JsBigInt`]s are context-free but not `Send`. The Raw version\n    /// of it is, though.\n    BigInt(RawBigInt),\n\n    /// A dictionary of strings to values which should be reconstructed into\n    /// a `JsObject`. Note: the prototype and constructor are not maintained,\n    /// and during reconstruction the default `Object` prototype will be used.\n    Object(Vec<(StringStore, JsValueStore)>),\n\n    /// A `Map()` object in JavaScript.\n    Map(Vec<(JsValueStore, JsValueStore)>),\n\n    /// A `Set()` object in JavaScript. The elements are already unique at\n    /// construction.\n    Set(Vec<JsValueStore>),\n\n    /// An `Array` object in JavaScript.\n    Array(Vec<Option<JsValueStore>>),\n\n    /// A `Date` object in JavaScript. Although this can be marshaled, it uses\n    /// the system's datetime library to be reconstructed and may diverge.\n    Date(f64),\n\n    /// Allowed error types (see the structured clone algorithm page).\n    #[expect(unused)]\n    Error {\n        kind: ErrorKind,\n        name: StringStore,\n        message: StringStore,\n        stack: StringStore,\n        cause: StringStore,\n    },\n\n    /// Regular expression. We store the expression and its flags. Everything else\n    /// is reset. These are extracted as `String`, so we don't need to use the\n    /// [`StringStore`] type.\n    RegExp { source: String, flags: String },\n\n    /// Array Buffer.\n    ArrayBuffer(AlignedVec<u8>),\n\n    /// Shared Array Buffer.\n    SharedArrayBuffer(SharedArrayBuffer),\n\n    /// Dataview.\n    #[expect(unused)]\n    DataView {\n        buffer: JsValueStore,\n        byte_length: u64,\n        byte_offset: u64,\n    },\n\n    /// Typed Array, including its kind and data.\n    TypedArray {\n        kind: TypedArrayKind,\n        buffer: JsValueStore,\n    },\n}\n\n/// A [`JsValue`]-like structure that can rebuild its value given any [`Context`].\n/// It essentially stores the value itself and its original type. During\n/// reconstruction, the constructors of the new [`Context`] will be used.\n///\n/// This follows the rules of the [structured clone algorithm][sca], but does not\n/// require a [`Context`] to copy/move, and can be [`Send`] between threads.\n///\n/// It is not serializable as it allows recursive values.\n///\n/// To transform a [`JsValue`] into a [`JsValueStore`], the application MUST\n/// pass in the context of the initial value. To transform it back to a\n/// [`JsValue`], the application MUST pass the context that will contain\n/// all prototypes for the new types (e.g. Object).\n///\n/// [sca]: https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API/Structured_clone_algorithm\n#[derive(Debug, Clone)]\npub struct JsValueStore(Arc<ValueStoreInner>);\n\nimpl TryIntoJs for JsValueStore {\n    fn try_into_js(&self, context: &mut Context) -> JsResult<JsValue> {\n        let mut seen = to::ReverseSeenMap::default();\n        to::try_value_into_js(self, &mut seen, context)\n    }\n}\n\nimpl JsValueStore {\n    /// Replace the inner content with a new inner. This is necessary as the inner\n    /// content holder must be allocated before its own inner content is created\n    /// (to allow for recursive data). Therefore, the pattern is to create the\n    /// store with an empty inner, then create the sub-content, and replace the\n    /// empty inner with the new inner.\n    ///\n    /// # SAFETY\n    /// This should only be done if the inner content is [`ValueStoreInner::Empty`],\n    /// and only by the creator of the current [`JsValueStore`]. We enforce the first\n    /// rule at runtime (and will panic), and the second rule by requiring a mutable\n    /// reference. This is still unsafe and relies on unsafe pointer access.\n    unsafe fn replace(&mut self, other: ValueStoreInner) {\n        let ptr = Arc::as_ptr(&self.0).cast_mut();\n\n        assert!(!ptr.is_null());\n        unsafe {\n            assert!(\n                matches!(*ptr, ValueStoreInner::Empty),\n                \"ValueStoreInner must be empty.\"\n            );\n\n            *ptr = other;\n        }\n    }\n\n    /// A still-being-constructed value.\n    fn empty() -> Self {\n        Self(Arc::new(ValueStoreInner::Empty))\n    }\n\n    fn new(inner: ValueStoreInner) -> Self {\n        Self(Arc::new(inner))\n    }\n\n    /// Create a context-free [`JsValue`] equivalent from an existing `JsValue` and the\n    /// [`Context`] that was used to create it. The `transfer` argument allows for\n    /// transferring ownership of the inner data to the context-free value instead of\n    /// cloning it. By default, if a value isn't in the transfer vector, it is cloned.\n    ///\n    /// # Errors\n    /// Any errors related to transferring or cloning a value's inner data.\n    pub fn try_from_js(\n        value: &JsValue,\n        context: &mut Context,\n        transfer: Vec<JsValue>,\n    ) -> JsResult<Self> {\n        let mut seen = from::SeenMap::default();\n        // Verify the validity of the transfer list and make it a set.\n        let transfer = transfer\n            .into_iter()\n            .map(|v| match v.variant() {\n                JsVariant::Object(o) if from::is_transferable(&o) => Ok(o),\n                _ => Err(unsupported_transfer()),\n            })\n            .collect::<Result<HashSet<_>, _>>()?;\n\n        let v = from::try_from_js_value(value, &transfer, &mut seen, context)?;\n        Ok(v)\n    }\n}\n"
  },
  {
    "path": "core/runtime/src/store/to.rs",
    "content": "//! All methods for deserializing a [`JsValueStore`] into a [`JsValue`].\nuse crate::store::{JsValueStore, StringStore, ValueStoreInner, unsupported_type};\nuse boa_engine::builtins::array_buffer::{AlignedVec, SharedArrayBuffer};\nuse boa_engine::builtins::typed_array::TypedArrayKind;\nuse boa_engine::object::builtins::{\n    JsArray, JsArrayBuffer, JsDataView, JsDate, JsMap, JsRegExp, JsSet, JsSharedArrayBuffer,\n    js_typed_array_from_kind,\n};\nuse boa_engine::{Context, JsBigInt, JsObject, JsResult, JsString, JsValue, js_error};\nuse std::collections::HashMap;\n\n#[derive(Default)]\npub(super) struct ReverseSeenMap(HashMap<usize, JsObject>);\n\nimpl ReverseSeenMap {\n    fn get(&self, object: &JsValueStore) -> Option<JsObject> {\n        let addr = std::ptr::from_ref(object.0.as_ref()).addr();\n        self.0.get(&addr).cloned()\n    }\n\n    fn insert(&mut self, original: &JsValueStore, object: JsObject) {\n        let addr = std::ptr::from_ref(original.0.as_ref()).addr();\n        self.0.insert(addr, object);\n    }\n}\n\nfn try_fields_into_js_object(\n    store: &JsValueStore,\n    fields: &Vec<(StringStore, JsValueStore)>,\n    seen: &mut ReverseSeenMap,\n    context: &mut Context,\n) -> JsResult<JsValue> {\n    let dolly = JsObject::with_object_proto(context.intrinsics());\n    seen.insert(store, dolly.clone());\n\n    for (k, v) in fields {\n        let k = k.to_js_string();\n        let value = try_value_into_js(v, seen, context)?;\n        dolly.set(k, value, true, context)?;\n    }\n    Ok(JsValue::from(dolly))\n}\n\nfn try_items_into_js_array(\n    store: &JsValueStore,\n    items: &[Option<JsValueStore>],\n    seen: &mut ReverseSeenMap,\n    context: &mut Context,\n) -> JsResult<JsValue> {\n    let dolly = JsArray::new(context)?;\n    seen.insert(store, dolly.clone().into());\n\n    for (k, v) in items\n        .iter()\n        .enumerate()\n        .filter_map(|(k, v)| v.as_ref().map(|v| (k, v)))\n    {\n        let value = try_value_into_js(v, seen, context)?;\n        dolly.set(k, value, true, context)?;\n    }\n    Ok(JsValue::from(dolly))\n}\n\nfn try_into_js_array_buffer(\n    store: &JsValueStore,\n    data: &[u8],\n    seen: &mut ReverseSeenMap,\n    context: &mut Context,\n) -> JsResult<JsValue> {\n    let buffer = JsArrayBuffer::from_byte_block(AlignedVec::from_slice(0, data), context)?;\n    let obj = JsObject::from(buffer);\n    seen.insert(store, obj.clone());\n    Ok(JsValue::from(obj))\n}\n\nfn try_into_js_shared_array_buffer(\n    store: &JsValueStore,\n    inner: &SharedArrayBuffer,\n    seen: &mut ReverseSeenMap,\n    context: &mut Context,\n) -> JsValue {\n    let buffer = JsSharedArrayBuffer::from_buffer(inner.clone(), context);\n    let obj = JsObject::from(buffer);\n    seen.insert(store, obj.clone());\n    JsValue::from(obj)\n}\n\nfn try_into_js_typed_array(\n    store: &JsValueStore,\n    kind: TypedArrayKind,\n    buffer: &JsValueStore,\n    seen: &mut ReverseSeenMap,\n    context: &mut Context,\n) -> JsResult<JsValue> {\n    let buffer = try_value_into_js(buffer, seen, context)?;\n    let Some(buffer) = buffer.as_object() else {\n        return Err(unsupported_type());\n    };\n    let buffer = JsArrayBuffer::from_object(buffer)?;\n    let array = js_typed_array_from_kind(kind, buffer, context)?;\n    if let Some(o) = array.as_object() {\n        seen.insert(store, o);\n    }\n    Ok(array)\n}\n\nfn try_into_js_map(\n    store: &JsValueStore,\n    key_values: &[(JsValueStore, JsValueStore)],\n    seen: &mut ReverseSeenMap,\n    context: &mut Context,\n) -> JsResult<JsValue> {\n    let map = JsMap::new(context);\n    seen.insert(store, map.clone().into());\n    for (k, v) in key_values {\n        let k = try_value_into_js(k, seen, context)?;\n        let v = try_value_into_js(v, seen, context)?;\n        map.set(k, v, context)?;\n    }\n\n    Ok(JsValue::from(map))\n}\n\nfn try_into_js_set(\n    store: &JsValueStore,\n    values: &[JsValueStore],\n    seen: &mut ReverseSeenMap,\n    context: &mut Context,\n) -> JsResult<JsValue> {\n    let set = JsSet::new(context);\n    seen.insert(store, set.clone().into());\n    for v in values {\n        let v = try_value_into_js(v, seen, context)?;\n        set.add(v, context)?;\n    }\n\n    Ok(JsValue::from(set))\n}\n\nfn try_into_js_date(\n    store: &JsValueStore,\n    ms_since_epoch: f64,\n    seen: &mut ReverseSeenMap,\n    context: &mut Context,\n) -> JsResult<JsValue> {\n    let date = JsDate::new(context);\n    date.set_time(ms_since_epoch, context)?;\n    seen.insert(store, date.clone().into());\n\n    Ok(JsValue::from(date))\n}\n\nfn try_into_regexp(\n    store: &JsValueStore,\n    source: &str,\n    flags: &str,\n    seen: &mut ReverseSeenMap,\n    context: &mut Context,\n) -> JsResult<JsValue> {\n    let re = JsRegExp::new(JsString::from(source), JsString::from(flags), context)?;\n    seen.insert(store, re.clone().into());\n    Ok(JsValue::from(re))\n}\n\nfn try_into_data_view(\n    store: &JsValueStore,\n    buffer: &JsValueStore,\n    byte_length: u64,\n    byte_offset: u64,\n    seen: &mut ReverseSeenMap,\n    context: &mut Context,\n) -> JsResult<JsValue> {\n    let buffer = try_value_into_js(buffer, seen, context)?;\n    let data_view = JsDataView::from_js_array_buffer(\n        JsArrayBuffer::from_object(buffer.as_object().ok_or_else(unsupported_type)?)?,\n        Some(byte_offset),\n        Some(byte_length),\n        context,\n    )?;\n\n    seen.insert(store, data_view.clone().into());\n    Ok(JsValue::from(data_view))\n}\n\npub(super) fn try_value_into_js(\n    store: &JsValueStore,\n    seen: &mut ReverseSeenMap,\n    context: &mut Context,\n) -> JsResult<JsValue> {\n    if let Some(v) = seen.get(store) {\n        return Ok(JsValue::from(v));\n    }\n\n    // Match the value\n    match &*store.0 {\n        ValueStoreInner::Empty => {\n            unreachable!(\"ValueStoreInner::Empty should not exist after storage.\");\n        }\n        ValueStoreInner::Null => Ok(JsValue::null()),\n        ValueStoreInner::Undefined => Ok(JsValue::undefined()),\n        ValueStoreInner::Boolean(b) => Ok(JsValue::from(*b)),\n        ValueStoreInner::Float(f) => Ok(JsValue::from(*f)),\n        ValueStoreInner::String(s) => Ok(JsValue::from(s.to_js_string())),\n        ValueStoreInner::BigInt(b) => Ok(JsValue::from(JsBigInt::new(b.clone()))),\n        ValueStoreInner::Object(fields) => try_fields_into_js_object(store, fields, seen, context),\n        ValueStoreInner::Map(key_values) => try_into_js_map(store, key_values, seen, context),\n        ValueStoreInner::Set(values) => try_into_js_set(store, values, seen, context),\n        ValueStoreInner::Array(items) => try_items_into_js_array(store, items, seen, context),\n        ValueStoreInner::Date(msec) => try_into_js_date(store, *msec, seen, context),\n        ValueStoreInner::Error { .. } => Err(js_error!(\"Not yet implemented.\")),\n        ValueStoreInner::RegExp { source, flags } => {\n            try_into_regexp(store, source, flags, seen, context)\n        }\n        ValueStoreInner::ArrayBuffer(data) => try_into_js_array_buffer(store, data, seen, context),\n        ValueStoreInner::SharedArrayBuffer(inner) => {\n            Ok(try_into_js_shared_array_buffer(store, inner, seen, context))\n        }\n        ValueStoreInner::DataView {\n            buffer,\n            byte_length,\n            byte_offset,\n        } => try_into_data_view(store, buffer, *byte_length, *byte_offset, seen, context),\n        ValueStoreInner::TypedArray { kind, buffer } => {\n            try_into_js_typed_array(store, *kind, buffer, seen, context)\n        }\n    }\n}\n"
  },
  {
    "path": "core/runtime/src/text/encodings.rs",
    "content": "pub(crate) mod utf8 {\n    use boa_engine::JsString;\n    use boa_engine::string::CodePoint;\n\n    pub(crate) fn encode(input: &JsString) -> Vec<u8> {\n        input\n            .code_points()\n            .flat_map(|s| match s {\n                CodePoint::Unicode(c) => c.to_string().as_bytes().to_vec(),\n                CodePoint::UnpairedSurrogate(_) => \"\\u{FFFD}\".as_bytes().to_vec(),\n            })\n            .collect()\n    }\n\n    pub(crate) fn decode(mut input: &[u8], strip_bom: bool) -> JsString {\n        if strip_bom {\n            input = input.strip_prefix(&[0xEF, 0xBB, 0xBF]).unwrap_or(input);\n        }\n        let string = String::from_utf8_lossy(input);\n        JsString::from(string.as_ref())\n    }\n}\n\npub(crate) mod utf16le {\n    use boa_engine::{JsString, js_string};\n\n    pub(crate) fn decode(mut input: &[u8], strip_bom: bool) -> JsString {\n        if strip_bom {\n            input = input.strip_prefix(&[0xFF, 0xFE]).unwrap_or(input);\n        }\n\n        // After this point, input is of even length.\n        let dangling = if input.len().is_multiple_of(2) {\n            false\n        } else {\n            input = &input[0..input.len() - 1];\n            true\n        };\n\n        let input: &[u16] = bytemuck::cast_slice(input);\n\n        if dangling {\n            JsString::from(&[JsString::from(input), js_string!(\"\\u{FFFD}\")])\n        } else {\n            JsString::from(input)\n        }\n    }\n}\n\npub(crate) mod utf16be {\n    use boa_engine::{JsString, js_string};\n\n    pub(crate) fn decode(mut input: Vec<u8>, strip_bom: bool) -> JsString {\n        if strip_bom && input.starts_with(&[0xFE, 0xFF]) {\n            input.drain(..2);\n        }\n\n        let mut input = input.as_mut_slice();\n        // After this point, input is of even length.\n        let dangling = if input.len().is_multiple_of(2) {\n            false\n        } else {\n            let new_len = input.len() - 1;\n            input = &mut input[0..new_len];\n            true\n        };\n\n        let input: &mut [u16] = bytemuck::cast_slice_mut(input);\n\n        // Swap the bytes.\n        for b in &mut *input {\n            *b = b.swap_bytes();\n        }\n\n        if dangling {\n            JsString::from(&[JsString::from(&*input), js_string!(\"\\u{FFFD}\")])\n        } else {\n            JsString::from(&*input)\n        }\n    }\n}\n"
  },
  {
    "path": "core/runtime/src/text/mod.rs",
    "content": "//! Module implementing JavaScript classes to handle text encoding and decoding.\n//!\n//! See <https://developer.mozilla.org/en-US/docs/Web/API/Encoding_API> for more information.\n\nuse boa_engine::object::builtins::{JsArrayBuffer, JsDataView, JsTypedArray, JsUint8Array};\nuse boa_engine::realm::Realm;\nuse boa_engine::value::TryFromJs;\nuse boa_engine::{\n    Context, Finalize, JsData, JsResult, JsString, JsValue, Trace, boa_class, boa_module, js_error,\n    js_string,\n};\n\n#[cfg(test)]\nmod tests;\n\nmod encodings;\n\n/// Options for the [`TextDecoder`] constructor.\n#[derive(Debug, Default, Clone, Copy, TryFromJs)]\npub struct TextDecoderOptions {\n    #[boa(rename = \"ignoreBOM\")]\n    ignore_bom: Option<bool>,\n}\n\n/// The character encoding used by [`TextDecoder`].\n#[derive(Debug, Default, Clone, Copy)]\npub enum Encoding {\n    /// UTF-8 encoding.\n    #[default]\n    Utf8,\n    /// UTF-16 little endian encoding.\n    Utf16Le,\n    /// UTF-16 big endian encoding.\n    Utf16Be,\n}\n\nconst TEXT_DECODER_LABELS: &[(&str, Encoding)] = &[\n    (\"unicode-1-1-utf-8\", Encoding::Utf8),\n    (\"unicode11utf8\", Encoding::Utf8),\n    (\"unicode20utf8\", Encoding::Utf8),\n    (\"utf-8\", Encoding::Utf8),\n    (\"utf8\", Encoding::Utf8),\n    (\"x-unicode20utf8\", Encoding::Utf8),\n    (\"unicodefffe\", Encoding::Utf16Be),\n    (\"utf-16be\", Encoding::Utf16Be),\n    (\"csunicode\", Encoding::Utf16Le),\n    (\"iso-10646-ucs-2\", Encoding::Utf16Le),\n    (\"ucs-2\", Encoding::Utf16Le),\n    (\"unicode\", Encoding::Utf16Le),\n    (\"unicodefeff\", Encoding::Utf16Le),\n    (\"utf-16\", Encoding::Utf16Le),\n    (\"utf-16le\", Encoding::Utf16Le),\n];\n\n#[inline]\nfn resolve_text_decoder_label(label: &str) -> Option<Encoding> {\n    let label = label.trim_matches(['\\u{0009}', '\\u{000A}', '\\u{000C}', '\\u{000D}', '\\u{0020}']);\n\n    TEXT_DECODER_LABELS\n        .iter()\n        .find_map(|(supported, encoding)| {\n            label.eq_ignore_ascii_case(supported).then_some(*encoding)\n        })\n}\n\n/// The [`TextDecoder`][mdn] class represents an encoder for a specific method, that is\n/// a specific character encoding, like `utf-8`.\n///\n/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/API/TextDecoder\n#[derive(Debug, Default, Clone, JsData, Trace, Finalize)]\npub struct TextDecoder {\n    #[unsafe_ignore_trace]\n    encoding: Encoding,\n    #[unsafe_ignore_trace]\n    ignore_bom: bool,\n}\n\n#[boa_class]\nimpl TextDecoder {\n    /// The [`TextDecoder()`][mdn] constructor returns a new `TextDecoder` object.\n    ///\n    /// # Errors\n    /// This will return an error if the encoding or options are invalid or unsupported.\n    ///\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/API/TextDecoder/TextDecoder\n    #[boa(constructor)]\n    pub fn constructor(\n        encoding: Option<JsString>,\n        options: Option<TextDecoderOptions>,\n    ) -> JsResult<Self> {\n        let ignore_bom = options.and_then(|o| o.ignore_bom).unwrap_or(false);\n\n        let encoding = match encoding {\n            Some(enc) => {\n                let label = enc.to_std_string_lossy();\n                resolve_text_decoder_label(&label).ok_or_else(\n                    || js_error!(RangeError: \"The given encoding '{}' is not supported.\", label),\n                )?\n            }\n            None => Encoding::default(),\n        };\n\n        Ok(Self {\n            encoding,\n            ignore_bom,\n        })\n    }\n\n    /// The [`TextDecoder.encoding`][mdn] read-only property returns a string containing\n    /// the name of the character encoding that this decoder will use.\n    ///\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/API/TextDecoder/encoding\n    #[boa(getter)]\n    #[must_use]\n    pub fn encoding(&self) -> JsString {\n        match self.encoding {\n            Encoding::Utf8 => js_string!(\"utf-8\"),\n            Encoding::Utf16Le => js_string!(\"utf-16le\"),\n            Encoding::Utf16Be => js_string!(\"utf-16be\"),\n        }\n    }\n\n    /// The [`TextDecoder.ignoreBOM`][mdn] read-only property returns a `bool` indicating\n    /// whether the BOM (byte order mark) is ignored.\n    ///\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/API/TextDecoder/ignoreBOM\n    #[boa(getter)]\n    #[boa(rename = \"ignoreBOM\")]\n    #[must_use]\n    pub fn ignore_bom(&self) -> bool {\n        self.ignore_bom\n    }\n\n    /// The [`TextDecoder.decode()`][mdn] method returns a string containing text decoded from the\n    /// buffer passed as a parameter.\n    ///\n    /// If `buffer` is omitted or `undefined`, this returns an empty string.\n    ///\n    /// `buffer` can be an `ArrayBuffer`, a `TypedArray` or a `DataView`.\n    ///\n    /// # Errors\n    /// Any error that arises during decoding the specific encoding.\n    ///\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/API/TextDecoder/decode\n    pub fn decode(&self, buffer: JsValue, context: &mut Context) -> JsResult<JsString> {\n        if buffer.is_undefined() {\n            return Ok(js_string!(\"\"));\n        }\n\n        let mut range = None;\n        let array_buffer = if let Ok(array_buffer) = JsArrayBuffer::try_from_js(&buffer, context) {\n            array_buffer\n        } else if let Ok(typed_array) = JsTypedArray::try_from_js(&buffer, context) {\n            let Some(obj) = typed_array.buffer(context)?.as_object() else {\n                return Err(js_error!(TypeError: \"Invalid buffer backing TypedArray.\"));\n            };\n\n            let offset = typed_array.byte_offset(context)?;\n            let length = typed_array.byte_length(context)?;\n\n            range = Some(offset..offset + length);\n\n            JsArrayBuffer::from_object(obj)?\n        } else if let Ok(data_view) = JsDataView::try_from_js(&buffer, context) {\n            let Some(obj) = data_view.buffer(context)?.as_object() else {\n                return Err(js_error!(TypeError: \"Invalid buffer backing DataView.\"));\n            };\n\n            let offset = usize::try_from(data_view.byte_offset(context)?)\n                .map_err(|_| js_error!(RangeError: \"DataView offset exceeds addressable size.\"))?;\n            let length = usize::try_from(data_view.byte_length(context)?)\n                .map_err(|_| js_error!(RangeError: \"DataView length exceeds addressable size.\"))?;\n\n            range = Some(offset..offset + length);\n\n            JsArrayBuffer::from_object(obj)?\n        } else {\n            return Err(js_error!(\n                TypeError: \"Argument 1 must be an ArrayBuffer, TypedArray or DataView.\"\n            ));\n        };\n\n        let strip_bom = !self.ignore_bom;\n\n        let Some(full_data) = array_buffer.data() else {\n            return Err(js_error!(TypeError: \"cannot decode a detached ArrayBuffer\"));\n        };\n\n        let data: &[u8] = if let Some(range) = range {\n            full_data.get(range).ok_or_else(\n                // We do not say invalid range here, as both subarray(10, 5) and subarray(\"a\", \"b\")\n                // are valid JS, it would just an empty array. If this error occurs, it most likely means something else\n                // is wrong\n                || js_error!(RangeError: \"The range for the underlying ArrayBuffer can not be accessed.\"),\n            )?\n        } else {\n            &full_data\n        };\n\n        Ok(match self.encoding {\n            Encoding::Utf8 => encodings::utf8::decode(data, strip_bom),\n            Encoding::Utf16Le => encodings::utf16le::decode(data, strip_bom),\n            Encoding::Utf16Be => {\n                let owned = data.to_vec();\n                encodings::utf16be::decode(owned, strip_bom)\n            }\n        })\n    }\n}\n\n/// The `TextEncoder`[mdn] class represents a UTF-8 encoder.\n///\n/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/API/TextEncoder\n#[derive(Debug, Default, Clone, JsData, Trace, Finalize)]\npub struct TextEncoder;\n\n#[boa_class]\nimpl TextEncoder {\n    /// The [`TextEncoder()`][mdn] constructor returns a newly created `TextEncoder` object.\n    ///\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/API/TextEncoder/TextEncoder\n    #[boa(constructor)]\n    #[must_use]\n    pub fn constructor() -> Self {\n        Self\n    }\n\n    /// The [`TextEncoder.encoding`][mdn] read-only property returns a string containing\n    /// the name of the encoding algorithm used by the specific encoder.\n    ///\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/API/TextEncoder/encoding\n    #[boa(getter)]\n    #[must_use]\n    fn encoding() -> JsString {\n        js_string!(\"utf-8\")\n    }\n\n    /// The [`TextEncoder.encode()`][mdn] method takes a string as input, and returns\n    /// a `Uint8Array` containing the string encoded using UTF-8.\n    ///\n    /// # Errors\n    /// This will error if there is an issue creating the `Uint8Array` or encoding\n    /// the string itself.\n    ///\n    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/API/TextEncoder/encode\n    pub fn encode(&self, text: Option<JsString>, context: &mut Context) -> JsResult<JsUint8Array> {\n        let Some(text) = text else {\n            return JsUint8Array::from_iter([], context);\n        };\n\n        JsUint8Array::from_iter(encodings::utf8::encode(&text), context)\n    }\n}\n\n/// JavaScript module containing the text encoding/decoding classes.\n#[boa_module]\npub mod js_module {\n    type TextDecoder = super::TextDecoder;\n    type TextEncoder = super::TextEncoder;\n}\n\n/// Register both `TextDecoder` and `TextEncoder` classes into the realm/context.\n///\n/// # Errors\n/// This will error if the context or realm cannot register the class.\npub fn register(realm: Option<Realm>, context: &mut Context) -> JsResult<()> {\n    js_module::boa_register(realm, context)\n}\n"
  },
  {
    "path": "core/runtime/src/text/tests.rs",
    "content": "use crate::test::{TestAction, run_test_actions_with};\nuse crate::text;\nuse boa_engine::object::builtins::JsUint8Array;\nuse boa_engine::property::Attribute;\nuse boa_engine::{Context, JsString, js_str, js_string};\nuse indoc::indoc;\nuse test_case::test_case;\n\n#[test]\nfn encoder_js() {\n    let context = &mut Context::default();\n    text::register(None, context).unwrap();\n\n    run_test_actions_with(\n        [\n            TestAction::run(indoc! {r#\"\n                const encoder = new TextEncoder();\n                encoded = encoder.encode(\"Hello, World!\");\n            \"#}),\n            TestAction::inspect_context(|context| {\n                let encoded = context\n                    .global_object()\n                    .get(js_str!(\"encoded\"), context)\n                    .unwrap();\n                let array =\n                    JsUint8Array::from_object(encoded.as_object().unwrap().clone()).unwrap();\n                let buffer = array.iter(context).collect::<Vec<_>>();\n\n                assert_eq!(buffer, b\"Hello, World!\");\n            }),\n        ],\n        context,\n    );\n}\n\n#[test]\nfn encoder_js_unpaired() {\n    use crate::test::{TestAction, run_test_actions_with};\n    use indoc::indoc;\n\n    let context = &mut Context::default();\n    text::register(None, context).unwrap();\n\n    let unpaired_surrogates: [u16; 3] = [0xDC58, 0xD83C, 0x0015];\n    let text = JsString::from(&unpaired_surrogates);\n    context\n        .register_global_property(js_str!(\"text\"), text, Attribute::default())\n        .unwrap();\n\n    run_test_actions_with(\n        [\n            TestAction::run(indoc! {r#\"\n                const encoder = new TextEncoder();\n                encoded = encoder.encode(text);\n            \"#}),\n            TestAction::inspect_context(|context| {\n                let encoded = context\n                    .global_object()\n                    .get(js_str!(\"encoded\"), context)\n                    .unwrap();\n                let array =\n                    JsUint8Array::from_object(encoded.as_object().unwrap().clone()).unwrap();\n                let buffer = array.iter(context).collect::<Vec<_>>();\n\n                assert_eq!(buffer, \"\\u{FFFD}\\u{FFFD}\\u{15}\".as_bytes());\n            }),\n        ],\n        context,\n    );\n}\n\n#[test]\nfn decoder_js() {\n    let context = &mut Context::default();\n    text::register(None, context).unwrap();\n\n    run_test_actions_with(\n        [\n            TestAction::run(indoc! {r#\"\n                const d = new TextDecoder();\n                decoded = d.decode(\n                    Uint8Array.from([ 72, 101, 108, 108, 111, 44, 32, 87, 111, 114, 108, 100, 33 ])\n                );\n            \"#}),\n            TestAction::inspect_context(|context| {\n                let decoded = context\n                    .global_object()\n                    .get(js_str!(\"decoded\"), context)\n                    .unwrap();\n                assert_eq!(decoded.as_string(), Some(js_string!(\"Hello, World!\")));\n            }),\n        ],\n        context,\n    );\n}\n\n#[test]\nfn decoder_js_without_input() {\n    let context = &mut Context::default();\n    text::register(None, context).unwrap();\n\n    run_test_actions_with(\n        [\n            TestAction::run(indoc! {r#\"\n                const d = new TextDecoder();\n                decoded = d.decode();\n                decodedUndefined = d.decode(undefined);\n            \"#}),\n            TestAction::inspect_context(|context| {\n                let decoded = context\n                    .global_object()\n                    .get(js_str!(\"decoded\"), context)\n                    .unwrap();\n                let decoded_undefined = context\n                    .global_object()\n                    .get(js_str!(\"decodedUndefined\"), context)\n                    .unwrap();\n                assert_eq!(decoded.as_string(), Some(js_string!(\"\")));\n                assert_eq!(decoded_undefined.as_string(), Some(js_string!(\"\")));\n            }),\n        ],\n        context,\n    );\n}\n\n#[test]\nfn decoder_js_invalid() {\n    use crate::test::{TestAction, run_test_actions_with};\n    use indoc::indoc;\n\n    let context = &mut Context::default();\n    text::register(None, context).unwrap();\n\n    run_test_actions_with(\n        [\n            TestAction::run(indoc! {r#\"\n                const d = new TextDecoder();\n                decoded = d.decode(\n                    Uint8Array.from([ 72, 101, 108, 108, 111, 160, 87, 111, 114, 108, 100, 161 ])\n                );\n            \"#}),\n            TestAction::inspect_context(|context| {\n                let decoded = context\n                    .global_object()\n                    .get(js_str!(\"decoded\"), context)\n                    .unwrap();\n                assert_eq!(\n                    decoded.as_string(),\n                    Some(js_string!(\"Hello\\u{FFFD}World\\u{FFFD}\"))\n                );\n            }),\n        ],\n        context,\n    );\n}\n\n#[test]\nfn roundtrip_utf8() {\n    let context = &mut Context::default();\n    text::register(None, context).unwrap();\n\n    run_test_actions_with(\n        [\n            TestAction::run(indoc! {r#\"\n                const encoder = new TextEncoder();\n                const decoder = new TextDecoder();\n                const text = \"Hello, World!\";\n                const encoded = encoder.encode(text);\n                decoded = decoder.decode(encoded);\n            \"#}),\n            TestAction::inspect_context(|context| {\n                let decoded = context\n                    .global_object()\n                    .get(js_str!(\"decoded\"), context)\n                    .unwrap();\n                assert_eq!(decoded.as_string(), Some(js_string!(\"Hello, World!\")));\n            }),\n        ],\n        context,\n    );\n}\n\n#[test_case(\"utf-8\")]\n#[test_case(\"utf-16\")]\n#[test_case(\"utf-16le\")]\n#[test_case(\"utf-16be\")]\nfn encoder_ignores_non_utf_encoding_arguments(encoding: &'static str) {\n    let context = &mut Context::default();\n    text::register(None, context).unwrap();\n\n    run_test_actions_with(\n        [\n            TestAction::run(format!(\n                r#\"\n                const encoder = new TextEncoder({encoding:?});\n                actualEncoding = encoder.encoding;\n                encoded = encoder.encode(\"Hi\");\n            \"#\n            )),\n            TestAction::inspect_context(|context| {\n                let actual_encoding = context\n                    .global_object()\n                    .get(js_str!(\"actualEncoding\"), context)\n                    .unwrap();\n                assert_eq!(actual_encoding.as_string(), Some(js_string!(\"utf-8\")));\n\n                let encoded = context\n                    .global_object()\n                    .get(js_str!(\"encoded\"), context)\n                    .unwrap();\n                let array =\n                    JsUint8Array::from_object(encoded.as_object().unwrap().clone()).unwrap();\n                let buffer = array.iter(context).collect::<Vec<_>>();\n                assert_eq!(buffer, b\"Hi\");\n            }),\n        ],\n        context,\n    );\n}\n\n// Default behavior: BOM is stripped\n#[test_case(\"utf-8\", &[0xEF, 0xBB, 0xBF, 72, 105])]\n#[test_case(\"utf-16le\", &[0xFF, 0xFE, 72, 0, 105, 0])]\n#[test_case(\"utf-16be\", &[0xFE, 0xFF, 0, 72, 0, 105])]\nfn decoder_bom_default_stripped(encoding: &'static str, bytes: &'static [u8]) {\n    let context = &mut Context::default();\n    text::register(None, context).unwrap();\n\n    let input = JsUint8Array::from_iter(bytes.iter().copied(), context).unwrap();\n    context\n        .register_global_property(js_str!(\"input\"), input, Attribute::default())\n        .unwrap();\n\n    run_test_actions_with(\n        [\n            TestAction::run(format!(\n                r#\"\n                const d = new TextDecoder({encoding:?});\n                decoded = d.decode(input);\n            \"#\n            )),\n            TestAction::inspect_context(|context| {\n                let decoded = context\n                    .global_object()\n                    .get(js_str!(\"decoded\"), context)\n                    .unwrap();\n                assert_eq!(decoded.as_string(), Some(js_string!(\"Hi\")));\n            }),\n        ],\n        context,\n    );\n}\n\n// ignoreBOM: true — BOM is kept in the output\n#[test_case(\"utf-8\", &[0xEF, 0xBB, 0xBF, 72, 105])]\n#[test_case(\"utf-16le\", &[0xFF, 0xFE, 72, 0, 105, 0])]\n#[test_case(\"utf-16be\", &[0xFE, 0xFF, 0, 72, 0, 105])]\nfn decoder_bom_ignore_bom_true(encoding: &'static str, bytes: &'static [u8]) {\n    let context = &mut Context::default();\n    text::register(None, context).unwrap();\n\n    let input = JsUint8Array::from_iter(bytes.iter().copied(), context).unwrap();\n    context\n        .register_global_property(js_str!(\"input\"), input, Attribute::default())\n        .unwrap();\n\n    run_test_actions_with(\n        [\n            TestAction::run(format!(\n                r#\"\n                const d = new TextDecoder({encoding:?}, {{ ignoreBOM: true }});\n                decoded = d.decode(input);\n            \"#\n            )),\n            TestAction::inspect_context(|context| {\n                let decoded = context\n                    .global_object()\n                    .get(js_str!(\"decoded\"), context)\n                    .unwrap();\n                assert_eq!(decoded.as_string(), Some(js_string!(\"\\u{FEFF}Hi\")));\n            }),\n        ],\n        context,\n    );\n}\n\n// ignoreBOM: false — same as default, BOM is stripped\n#[test_case(\"utf-8\", &[0xEF, 0xBB, 0xBF, 72, 105])]\n#[test_case(\"utf-16le\", &[0xFF, 0xFE, 72, 0, 105, 0])]\n#[test_case(\"utf-16be\", &[0xFE, 0xFF, 0, 72, 0, 105])]\nfn decoder_bom_ignore_bom_false(encoding: &'static str, bytes: &'static [u8]) {\n    let context = &mut Context::default();\n    text::register(None, context).unwrap();\n\n    let input = JsUint8Array::from_iter(bytes.iter().copied(), context).unwrap();\n    context\n        .register_global_property(js_str!(\"input\"), input, Attribute::default())\n        .unwrap();\n\n    run_test_actions_with(\n        [\n            TestAction::run(format!(\n                r#\"\n                const d = new TextDecoder({encoding:?}, {{ ignoreBOM: false }});\n                decoded = d.decode(input);\n            \"#\n            )),\n            TestAction::inspect_context(|context| {\n                let decoded = context\n                    .global_object()\n                    .get(js_str!(\"decoded\"), context)\n                    .unwrap();\n                assert_eq!(decoded.as_string(), Some(js_string!(\"Hi\")));\n            }),\n        ],\n        context,\n    );\n}\n\n#[test_case(\"UTF-8\", \"utf-8\"; \"uppercase utf8\")]\n#[test_case(\" utf-8 \", \"utf-8\"; \"spaced utf8\")]\n#[test_case(\"\\nutf-16\\t\", \"utf-16le\"; \"spaced utf16\")]\n#[test_case(\"UTF-16BE\", \"utf-16be\"; \"uppercase utf16be\")]\n#[test_case(\"utf8\", \"utf-8\"; \"utf8 alias\")]\n#[test_case(\"Unicode-1-1-UTF-8\", \"utf-8\"; \"unicode alias\")]\n#[test_case(\"csUnicode\", \"utf-16le\"; \"csunicode alias\")]\n#[test_case(\" unicodefeff \", \"utf-16le\"; \"unicodefeff alias\")]\n#[test_case(\"UnicodeFFFE\", \"utf-16be\"; \"unicodefffe alias\")]\nfn decoder_normalizes_supported_labels(label: &'static str, expected: &'static str) {\n    let context = &mut Context::default();\n    text::register(None, context).unwrap();\n\n    run_test_actions_with(\n        [\n            TestAction::run(format!(\n                r#\"\n                const d = new TextDecoder({label:?});\n                encoding = d.encoding;\n            \"#\n            )),\n            TestAction::inspect_context(move |context| {\n                let encoding = context\n                    .global_object()\n                    .get(js_str!(\"encoding\"), context)\n                    .unwrap();\n                assert_eq!(encoding.as_string(), Some(JsString::from(expected)));\n            }),\n        ],\n        context,\n    );\n}\n\n#[test]\nfn decoder_rejects_unsupported_label_after_normalization() {\n    let context = &mut Context::default();\n    text::register(None, context).unwrap();\n\n    run_test_actions_with(\n        [TestAction::run(indoc! {r#\"\n                try {\n                    new TextDecoder(\" utf-32 \");\n                    throw new Error(\"expected RangeError\");\n                } catch (e) {\n                    if (!(e instanceof RangeError)) {\n                        throw e;\n                    }\n                }\n            \"#})],\n        context,\n    );\n}\n\n#[test]\nfn decoder_ignore_bom_getter() {\n    let context = &mut Context::default();\n    text::register(None, context).unwrap();\n\n    run_test_actions_with(\n        [\n            TestAction::run(indoc! {r#\"\n                const d1 = new TextDecoder();\n                const d2 = new TextDecoder(\"utf-8\", { ignoreBOM: true });\n                const d3 = new TextDecoder(\"utf-8\", { ignoreBOM: false });\n                ignoreBOM1 = d1.ignoreBOM;\n                ignoreBOM2 = d2.ignoreBOM;\n                ignoreBOM3 = d3.ignoreBOM;\n            \"#}),\n            TestAction::inspect_context(|context| {\n                let v1 = context\n                    .global_object()\n                    .get(js_str!(\"ignoreBOM1\"), context)\n                    .unwrap();\n                let v2 = context\n                    .global_object()\n                    .get(js_str!(\"ignoreBOM2\"), context)\n                    .unwrap();\n                let v3 = context\n                    .global_object()\n                    .get(js_str!(\"ignoreBOM3\"), context)\n                    .unwrap();\n                assert_eq!(v1.as_boolean(), Some(false));\n                assert_eq!(v2.as_boolean(), Some(true));\n                assert_eq!(v3.as_boolean(), Some(false));\n            }),\n        ],\n        context,\n    );\n}\n\n#[test]\nfn decoder_handle_data_view() {\n    let context = &mut Context::default();\n    text::register(None, context).unwrap();\n\n    run_test_actions_with(\n        [\n            TestAction::run(indoc! {r#\"\n                var decoded = new TextDecoder().decode(\n                    new DataView(new TextEncoder().encode(\"hello\").buffer)\n                );\n            \"#}),\n            TestAction::inspect_context(|context| {\n                let decoded = context\n                    .global_object()\n                    .get(js_str!(\"decoded\"), context)\n                    .unwrap();\n                assert_eq!(decoded.as_string(), Some(js_string!(\"hello\")));\n            }),\n        ],\n        context,\n    );\n}\n\n#[test]\nfn decoder_handle_typed_array_offset_and_length() {\n    let context = &mut Context::default();\n    text::register(None, context).unwrap();\n\n    run_test_actions_with(\n        [\n            TestAction::run(indoc! {r#\"\n                var decoded = new TextDecoder().decode(Uint8Array.of(0x41, 0x43, 0x45, 0x47).subarray(1, 3));\n            \"#}),\n            TestAction::inspect_context(|context| {\n                let decoded = context\n                    .global_object()\n                    .get(js_str!(\"decoded\"), context)\n                    .unwrap();\n                assert_eq!(decoded.as_string(), Some(js_string!(\"CE\")));\n            }),\n        ],\n        context,\n    );\n}\n\n#[test]\nfn decoder_handle_data_view_offset_and_length() {\n    let context = &mut Context::default();\n    text::register(None, context).unwrap();\n\n    run_test_actions_with(\n        [\n            TestAction::run(indoc! {r#\"\n                const buffer = Uint8Array.of(0x41, 0x43, 0x45, 0x47).buffer;\n                const view = new DataView(buffer, 1, 2);\n                var decoded = new TextDecoder().decode(view);\n            \"#}),\n            TestAction::inspect_context(|context| {\n                let decoded = context\n                    .global_object()\n                    .get(js_str!(\"decoded\"), context)\n                    .unwrap();\n                assert_eq!(decoded.as_string(), Some(js_string!(\"CE\")));\n            }),\n        ],\n        context,\n    );\n}\n"
  },
  {
    "path": "core/runtime/src/url/tests.rs",
    "content": "use crate::test::{TestAction, run_test_actions};\n\nconst TEST_HARNESS: &str = r#\"\nfunction assert(condition, message) {\n    if (!condition) {\n        if (!message) {\n            message = \"Assertion failed\";\n        }\n        throw new Error(message);\n    }\n}\n\nfunction assert_eq(a, b, message) {\n    if (a !== b) {\n        throw new Error(`${message} (${JSON.stringify(a)} !== ${JSON.stringify(b)})`);\n    }\n}\n\"#;\n\n#[test]\nfn url_basic() {\n    run_test_actions([\n        TestAction::run(TEST_HARNESS),\n        TestAction::run(\n            r##\"\n                url = new URL(\"https://example.com:8080/path/to/resource?query#fragment\");\n                assert(url instanceof URL);\n                assert_eq(url.href, \"https://example.com:8080/path/to/resource?query#fragment\");\n                assert_eq(url.protocol, \"https:\");\n                assert_eq(url.host, \"example.com:8080\");\n                assert_eq(url.hostname, \"example.com\");\n                assert_eq(url.port, \"8080\");\n                assert_eq(url.pathname, \"/path/to/resource\");\n                assert_eq(url.search, \"?query\");\n                assert_eq(url.hash, \"#fragment\");\n            \"##,\n        ),\n    ]);\n}\n\n#[test]\nfn url_base() {\n    run_test_actions([\n        TestAction::run(TEST_HARNESS),\n        TestAction::run(\n            r##\"\n                url = new URL(\"https://example.com:8080/path/to/resource?query#fragment\", \"http://example.org/\");\n                assert_eq(url.href, \"https://example.com:8080/path/to/resource?query#fragment\");\n                assert_eq(url.protocol, \"https:\");\n                assert_eq(url.host, \"example.com:8080\");\n                assert_eq(url.hostname, \"example.com\");\n                assert_eq(url.port, \"8080\");\n                assert_eq(url.pathname, \"/path/to/resource\");\n                assert_eq(url.search, \"?query\");\n                assert_eq(url.hash, \"#fragment\");\n            \"##,\n        ),\n        TestAction::run(\n            r##\"\n                url = new URL(\"/path/to/resource?query#fragment\", \"http://example.org/\");\n                assert_eq(url.href, \"http://example.org/path/to/resource?query#fragment\");\n                assert_eq(url.protocol, \"http:\");\n                assert_eq(url.host, \"example.org\");\n                assert_eq(url.hostname, \"example.org\");\n                assert_eq(url.port, \"\");\n                assert_eq(url.pathname, \"/path/to/resource\");\n                assert_eq(url.search, \"?query\");\n                assert_eq(url.hash, \"#fragment\");\n            \"##,\n        ),\n    ]);\n}\n\n#[test]\nfn url_setters() {\n    // These were double checked against Firefox.\n    run_test_actions([\n        TestAction::run(TEST_HARNESS),\n        TestAction::run(\n            r##\"\n                url = new URL(\"https://example.com:8080/path/to/resource?query#fragment\");\n                url.protocol = \"http:\";\n                url.host = \"example.org:80\"; // Since protocol is http, port is removed.\n                url.pathname = \"/new/path\";\n                url.search = \"?new-query\";\n                url.hash = \"#new-fragment\";\n                assert_eq(url.href, \"http://example.org/new/path?new-query#new-fragment\");\n                assert_eq(url.protocol, \"http:\");\n                assert_eq(url.host, \"example.org\");\n                assert_eq(url.hostname, \"example.org\");\n                assert_eq(url.port, \"\");\n                assert_eq(url.pathname, \"/new/path\");\n                assert_eq(url.search, \"?new-query\");\n                assert_eq(url.hash, \"#new-fragment\");\n            \"##,\n        ),\n    ]);\n}\n\n#[test]\nfn url_static_methods() {\n    run_test_actions([\n        TestAction::run(TEST_HARNESS),\n        TestAction::run(\n            r##\"\n                assert(URL.canParse(\"http://example.org/new/path?new-query#new-fragment\"));\n                assert(!URL.canParse(\"http//:example.org/new/path?new-query#new-fragment\"));\n                assert(!URL.canParse(\"http://example.org/new/path?new-query#new-fragment\", \"http:\"));\n                assert(URL.canParse(\"/new/path?new-query#new-fragment\", \"http://example.org/\"));\n            \"##,\n        ),\n    ]);\n}\n"
  },
  {
    "path": "core/runtime/src/url.rs",
    "content": "//! Boa's implementation of JavaScript's `URL` Web API class.\n//!\n//! The `URL` class can be instantiated from any global object.\n//! This relies on the `url` feature.\n//!\n//! More information:\n//!  - [MDN documentation][mdn]\n//!  - [WHATWG `URL` specification][spec]\n//!\n//! [spec]: https://url.spec.whatwg.org/\n//! [mdn]: https://developer.mozilla.org/en-US/docs/Web/API/URL\n#![allow(clippy::needless_pass_by_value)]\n\n#[cfg(test)]\nmod tests;\n\nuse boa_engine::class::Class;\nuse boa_engine::realm::Realm;\nuse boa_engine::value::Convert;\nuse boa_engine::{\n    Context, Finalize, JsData, JsResult, JsString, JsValue, Trace, boa_class, boa_module, js_error,\n};\nuse std::fmt::Display;\n\n/// The `URL` class represents a (properly parsed) Uniform Resource Locator.\n#[derive(Debug, Clone, JsData, Trace, Finalize)]\n#[boa_gc(unsafe_no_drop)]\npub struct Url(#[unsafe_ignore_trace] url::Url);\n\nimpl Url {\n    /// Register the `URL` class into the realm. Pass `None` for the realm to\n    /// register globally.\n    ///\n    /// # Errors\n    /// This will error if the context or realm cannot register the class.\n    pub fn register(realm: Option<Realm>, context: &mut Context) -> JsResult<()> {\n        js_module::boa_register(realm, context)\n    }\n}\n\nimpl Display for Url {\n    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {\n        write!(f, \"{}\", self.0)\n    }\n}\n\nimpl From<url::Url> for Url {\n    fn from(url: url::Url) -> Self {\n        Self(url)\n    }\n}\n\nimpl From<Url> for url::Url {\n    fn from(url: Url) -> url::Url {\n        url.0\n    }\n}\n\n#[boa_class(rename = \"URL\")]\n#[boa(rename_all = \"camelCase\")]\nimpl Url {\n    /// Create a new `URL` object. Meant to be called from the JavaScript constructor.\n    ///\n    /// # Errors\n    /// Any errors that might occur during URL parsing.\n    #[boa(constructor)]\n    pub fn new(Convert(ref url): Convert<String>, base: Option<Convert<String>>) -> JsResult<Self> {\n        if let Some(Convert(ref base)) = base {\n            let base_url = url::Url::parse(base)\n                .map_err(|e| js_error!(TypeError: \"Failed to parse base URL: {}\", e))?;\n            if base_url.cannot_be_a_base() {\n                return Err(js_error!(TypeError: \"Base URL {} cannot be a base\", base));\n            }\n\n            let url = base_url\n                .join(url)\n                .map_err(|e| js_error!(TypeError: \"Failed to parse URL: {}\", e))?;\n            Ok(Self(url))\n        } else {\n            let url = url::Url::parse(url)\n                .map_err(|e| js_error!(TypeError: \"Failed to parse URL: {}\", e))?;\n            Ok(Self(url))\n        }\n    }\n\n    #[boa(getter)]\n    fn hash(&self) -> JsString {\n        JsString::from(url::quirks::hash(&self.0))\n    }\n\n    #[boa(setter)]\n    #[boa(rename = \"hash\")]\n    fn set_hash(&mut self, value: Convert<String>) {\n        url::quirks::set_hash(&mut self.0, &value.0);\n    }\n\n    #[boa(getter)]\n    fn hostname(&self) -> JsString {\n        JsString::from(url::quirks::hostname(&self.0))\n    }\n\n    #[boa(setter)]\n    #[boa(rename = \"hostname\")]\n    fn set_hostname(&mut self, value: Convert<String>) {\n        let _ = url::quirks::set_hostname(&mut self.0, &value.0);\n    }\n\n    #[boa(getter)]\n    fn host(&self) -> JsString {\n        JsString::from(url::quirks::host(&self.0))\n    }\n\n    #[boa(setter)]\n    #[boa(rename = \"host\")]\n    fn set_host(&mut self, value: Convert<String>) {\n        let _ = url::quirks::set_host(&mut self.0, &value.0);\n    }\n\n    #[boa(getter)]\n    fn href(&self) -> JsString {\n        JsString::from(url::quirks::href(&self.0))\n    }\n\n    #[boa(setter)]\n    #[boa(rename = \"href\")]\n    fn set_href(&mut self, value: Convert<String>) -> JsResult<()> {\n        url::quirks::set_href(&mut self.0, &value.0)\n            .map_err(|e| js_error!(TypeError: \"Failed to set href: {}\", e))\n    }\n\n    #[boa(getter)]\n    fn origin(&self) -> JsString {\n        JsString::from(url::quirks::origin(&self.0))\n    }\n\n    #[boa(getter)]\n    fn password(&self) -> JsString {\n        JsString::from(url::quirks::password(&self.0))\n    }\n\n    #[boa(setter)]\n    #[boa(rename = \"password\")]\n    fn set_password(&mut self, value: Convert<String>) {\n        let _ = url::quirks::set_password(&mut self.0, &value.0);\n    }\n\n    #[boa(getter)]\n    fn pathname(&self) -> JsString {\n        JsString::from(url::quirks::pathname(&self.0))\n    }\n\n    #[boa(setter)]\n    #[boa(rename = \"pathname\")]\n    fn set_pathname(&mut self, value: Convert<String>) {\n        let () = url::quirks::set_pathname(&mut self.0, &value.0);\n    }\n\n    #[boa(getter)]\n    fn port(&self) -> JsString {\n        JsString::from(url::quirks::port(&self.0))\n    }\n\n    #[boa(setter)]\n    #[boa(rename = \"port\")]\n    fn set_port(&mut self, value: Convert<JsString>) {\n        let _ = url::quirks::set_port(&mut self.0, &value.0.to_std_string_lossy());\n    }\n\n    #[boa(getter)]\n    fn protocol(&self) -> JsString {\n        JsString::from(url::quirks::protocol(&self.0))\n    }\n\n    #[boa(setter)]\n    #[boa(rename = \"protocol\")]\n    fn set_protocol(&mut self, value: Convert<String>) {\n        let _ = url::quirks::set_protocol(&mut self.0, &value.0);\n    }\n\n    #[boa(getter)]\n    fn search(&self) -> JsString {\n        JsString::from(url::quirks::search(&self.0))\n    }\n\n    #[boa(setter)]\n    #[boa(rename = \"search\")]\n    fn set_search(&mut self, value: Convert<String>) {\n        url::quirks::set_search(&mut self.0, &value.0);\n    }\n\n    #[boa(getter)]\n    fn search_params() -> JsResult<()> {\n        Err(js_error!(Error: \"URL.searchParams is not implemented\"))\n    }\n\n    #[boa(getter)]\n    fn username(&self) -> JsString {\n        JsString::from(self.0.username())\n    }\n\n    #[boa(setter)]\n    #[boa(rename = \"username\")]\n    fn set_username(&mut self, value: Convert<String>) {\n        let _ = self.0.set_username(&value.0);\n    }\n\n    fn to_string(&self) -> JsString {\n        JsString::from(format!(\"{}\", self.0))\n    }\n\n    #[boa(rename = \"toJSON\")]\n    fn to_json(&self) -> JsString {\n        JsString::from(format!(\"{}\", self.0))\n    }\n\n    #[boa(static)]\n    fn create_object_url() -> JsResult<()> {\n        Err(js_error!(Error: \"URL.createObjectURL is not implemented\"))\n    }\n\n    #[boa(static)]\n    fn can_parse(url: Convert<String>, base: Option<Convert<String>>) -> bool {\n        Url::new(url, base).is_ok()\n    }\n\n    #[boa(static)]\n    fn parse(\n        url: Convert<String>,\n        base: Option<Convert<String>>,\n        context: &mut Context,\n    ) -> JsResult<JsValue> {\n        Url::new(url, base).map_or(Ok(JsValue::null()), |u| {\n            Url::from_data(u, context).map(JsValue::from)\n        })\n    }\n\n    #[boa(static)]\n    fn revoke_object_url() -> JsResult<()> {\n        Err(js_error!(Error: \"URL.revokeObjectURL is not implemented\"))\n    }\n}\n\n/// JavaScript module containing the Url class.\n#[boa_module]\npub mod js_module {\n    type Url = super::Url;\n}\n"
  },
  {
    "path": "core/runtime/tests/clone/complex.js",
    "content": "// Create a new scope to prevent global namespace poisoning.\n{\n  class SomeClass {\n    constructor() {\n      this.a = 42n;\n      this.x = 1;\n      this.repeat = this;\n      this.repeatArray = [this, this, this];\n    }\n  }\n\n  const buffer = new Uint8Array([1, 2, 3, 4]);\n  const buffer2 = new Uint8Array([5, 6, 7, 8]);\n  const buffer3 = new Uint32Array([9, 10, 11, 12]);\n  const buffer4 = new Uint32Array([13, 14, 15, 16]);\n  const arrayBuffer = new ArrayBuffer(16);\n  new DataView(arrayBuffer).setUint32(0, 100);\n  const arrayBuffer2 = new ArrayBuffer(10);\n  new DataView(arrayBuffer2).setUint32(1, 101);\n\n  const original = {\n    some: new SomeClass(),\n    buffer,\n    bufferTArray: [buffer, buffer, buffer, buffer],\n    buffer2,\n    buffer2Array: [buffer2, buffer2, buffer2, buffer2],\n    buffer3,\n    buffer4,\n    arrayBuffer,\n    arrayBuffer2,\n  };\n\n  let dolly = structuredClone(original, {\n    transfer: [buffer.buffer, buffer3.buffer, arrayBuffer2],\n  });\n\n  assertEq(buffer.byteLength, 0);\n  assertEq(buffer2.byteLength, 4);\n  assertEq(buffer3.byteLength, 0);\n  assertThrows(() => {\n    new Uint8Array(buffer);\n  });\n  assertThrows(() => {\n    new Uint32Array(buffer3);\n  });\n\n  assertEq(dolly.buffer.constructor, Uint8Array);\n  assertEq(dolly.buffer.byteLength, 4);\n  assertEq(dolly.buffer2.constructor, Uint8Array);\n  assertEq(dolly.buffer2.byteLength, 4);\n  assertEq(dolly.buffer3.constructor, Uint32Array);\n  assertEq(dolly.buffer3.byteLength, 16);\n  assertEq(dolly.buffer4.constructor, Uint32Array);\n  assertEq(dolly.buffer4.byteLength, 16);\n\n  assertArrayEqual(dolly.buffer, [1, 2, 3, 4]);\n  assertEq(dolly.buffer, dolly.bufferTArray[0]);\n  assertEq(dolly.bufferTArray[0], dolly.bufferTArray[1]);\n  assertEq(dolly.bufferTArray[0], dolly.bufferTArray[2]);\n  assertEq(dolly.bufferTArray[0], dolly.bufferTArray[3]);\n\n  assertArrayEqual(dolly.buffer2, [5, 6, 7, 8]);\n  assertEq(dolly.buffer2Array[0], dolly.buffer2Array[1]);\n  assertEq(dolly.buffer2Array[0], dolly.buffer2Array[2]);\n  assertEq(dolly.buffer2Array[0], dolly.buffer2Array[3]);\n\n  assertArrayEqual(dolly.buffer3, [9, 10, 11, 12]);\n  assertArrayEqual(dolly.buffer4, [13, 14, 15, 16]);\n\n  assertNEq(dolly.some.constructor, SomeClass);\n  assertEq(dolly.some.a, 42n);\n  assertEq(dolly.some.x, 1);\n  assertEq(dolly.some.repeat, dolly.some);\n\n  assertNEq(dolly.arrayBuffer, arrayBuffer);\n  assertEq(dolly.arrayBuffer.byteLength, 16);\n  assertEq(new DataView(dolly.arrayBuffer).getUint32(0), 100);\n  assertNEq(dolly.arrayBuffer2, arrayBuffer2);\n  assertEq(dolly.arrayBuffer2.byteLength, 10);\n  assertEq(new DataView(dolly.arrayBuffer2).getUint32(1), 101);\n}\n"
  },
  {
    "path": "core/runtime/tests/clone/date.js",
    "content": "{\n  const date1 = new Date();\n  const date2 = new Date(date1.getTime());\n\n  // Make sure the world makes sense.\n  assertNEq(date1, date2, \"Two different date objects should not be equal\");\n\n  const x = {\n    first: date1,\n    second: date1,\n    third: date2,\n  };\n\n  const xx = structuredClone(x);\n\n  assertEq(xx.first, xx.second, \"These should be the same object.\");\n  assertNEq(\n    xx.second,\n    xx.third,\n    \"Second and Third should NOT be the same object.\",\n  );\n}\n"
  },
  {
    "path": "core/runtime/tests/clone/errors.js",
    "content": "assertThrows(() => {\n  structuredClone(() => {});\n});\n"
  },
  {
    "path": "core/runtime/tests/clone/map.js",
    "content": "const m1 = new Map();\n\nconst k1 = new Uint8Array([1, 2]);\nconst k2 = \"someKey\";\nconst k3 = 5;\n\nm1.set(k1, \"hello\");\nm1.set(k2, \"world\");\nm1.set(k3, k1);\n\nassert(k1 === m1.get(5));\n\nconst m2 = structuredClone(m1, { transfer: [k1.buffer] });\nconst m2k1 = [...m2.keys()].find((v) => v instanceof Uint8Array);\n\nassert(k1 !== m2k1);\nassertArrayEqual(m2k1, [1, 2]);\nassert(m2k1 === m2.get(5));\n"
  },
  {
    "path": "core/runtime/tests/clone/object.js",
    "content": "// https://developer.mozilla.org/en-US/docs/Web/API/Window/structuredClone#cloning_an_object\n\nconst mushrooms1 = {\n  amanita: [\"muscaria\", \"virosa\"],\n};\n\nconst mushrooms2 = structuredClone(mushrooms1);\n\nassertNEq(mushrooms1, mushrooms2);\nassertArrayEqual(mushrooms1.amanita, mushrooms2.amanita);\n\nmushrooms2.amanita.push(\"pantherina\");\nmushrooms1.amanita.pop();\n\nassertArrayEqual(mushrooms2.amanita, [\"muscaria\", \"virosa\", \"pantherina\"]);\nassertArrayEqual(mushrooms1.amanita, [\"muscaria\"]);\n"
  },
  {
    "path": "core/runtime/tests/clone/regexp.js",
    "content": "{\n  const re1 = new RegExp(\"abc\", \"i\");\n  const re2 = /abc/i;\n\n  // Make sure the world makes sense.\n  assertNEq(re1, re2, \"Two different regexp objects should not be equal\");\n\n  const x = {\n    first: re1,\n    second: re1,\n    third: re2,\n    fourth: re2,\n  };\n\n  const xx = structuredClone(x);\n\n  assertEq(xx.first, xx.second, \"These should be the same object.\");\n  assertNEq(\n    xx.second,\n    xx.third,\n    \"Second and Third should NOT be the same object.\",\n  );\n  assertEq(xx.third, xx.fourth, \"These should be the same object.\");\n\n  assert(xx.first.test(\"def ABC def\"));\n  assertEq(\n    Object.getPrototypeOf(xx.first),\n    Object.getPrototypeOf(xx.second),\n    \"These should be have the same prototype.\",\n  );\n}\n"
  },
  {
    "path": "core/runtime/tests/clone/set.js",
    "content": "const set1 = new Set();\n\nconst v1 = new Uint8Array([1, 2]);\n\nset1.add(v1);\nset1.add(\"someValue\");\nset1.add(new Uint8Array([3, 4]));\nset1.add(set1); // Russell be damned!\n\nconst set2 = structuredClone(set1, { transfer: [v1.buffer] });\nconst [s2v1, v2, v3, v4] = [...set2.values()];\n\nassert(s2v1 !== v1);\nassertEq(v1.buffer.byteLength, 0);\nassertArrayEqual(s2v1, [1, 2]);\nassertEq(v2, \"someValue\");\nassertArrayEqual(v3, [3, 4]);\nassertEq(set2, v4);\n"
  },
  {
    "path": "core/runtime/tests/clone/simple.js",
    "content": "// From https://developer.mozilla.org/en-US/docs/Web/API/Window/structuredClone#description\n\n// Create an object with a value and a circular reference to itself.\nconst original = { name: \"MDN\" };\noriginal.itself = original;\n\n// Clone it\nconst clone = structuredClone(original);\n\nassertNEq(clone, original); // the objects are not the same (not same identity)\nassertEq(clone.name, \"MDN\"); // they do have the same values\nassertEq(clone.itself, clone); // and the circular reference is preserved\n"
  },
  {
    "path": "core/runtime/tests/clone/transfer.js",
    "content": "// https://developer.mozilla.org/en-US/docs/Web/API/Window/structuredClone#transferring_an_object\n\n{\n  // Create an ArrayBuffer with a size in bytes\n  const buffer = new ArrayBuffer(16);\n\n  const object1 = {\n    buffer,\n  };\n\n  // Clone the object containing the buffer, and transfer it\n  const object2 = structuredClone(object1, { transfer: [buffer] });\n\n  // Create an array from the cloned buffer\n  const int32View2 = new Int32Array(object2.buffer);\n  int32View2[0] = 42;\n  assertEq(int32View2[0], 42);\n\n  // Creating an array from the original buffer throws a TypeError\n  assertThrows(() => {\n    const int32View1 = new Int32Array(object1.buffer);\n  });\n}\n\n{\n  // Verify we can transfer the buffer of a typed array.\n  const array = new Uint8Array([1, 2, 3, 4]);\n  const object1 = {\n    array,\n  };\n\n  const object2 = structuredClone(object1, { transfer: [array.buffer] });\n\n  assert(object2.array !== array);\n  assertEq(object1.array.byteLength, 0);\n  assertArrayEqual(object2.array, [1, 2, 3, 4]);\n}\n\nassertThrows(() => structuredClone({}, { transfer: [1] }));\nassertThrows(() => structuredClone({}, { transfer: [\"error\"] }));\nassertThrows(() => structuredClone({}, { transfer: [{}] }));\nassertThrows(() => structuredClone({}, { transfer: [new Uint8Array([1, 2])] }));\n"
  },
  {
    "path": "core/runtime/tests/clone.rs",
    "content": "#![allow(unused_crate_dependencies, missing_docs)]\n\nuse boa_engine::{Context, Source};\nuse boa_runtime::RuntimeExtension;\nuse rstest::rstest;\nuse std::path::PathBuf;\n\n#[rstest]\nfn clone(#[files(\"tests/clone/**/*.js\")] path: PathBuf) {\n    let context = &mut Context::default();\n    boa_runtime::clone::register(None, context).expect(\"Could not register runtime\");\n    boa_runtime::extensions::ConsoleExtension::default()\n        .register(None, context)\n        .expect(\"Could not register console\");\n\n    let harness_path = PathBuf::from(\"./assets/harness.js\");\n    let harness = Source::from_filepath(&harness_path).expect(\"Could not load harness\");\n    context.eval(harness).expect(\"Could not eval source\");\n\n    let source = Source::from_filepath(&path).expect(\"Could not load source\");\n\n    if let Err(e) = context.eval(source) {\n        panic!(\"Evaluation failed: {e}\");\n    }\n\n    if let Err(e) = context.run_jobs() {\n        panic!(\"Execution error: {e}\");\n    }\n}\n"
  },
  {
    "path": "core/string/ABOUT.md",
    "content": "# About Boa\n\nBoa is an open-source, experimental ECMAScript Engine written in Rust for\nlexing, parsing and executing ECMAScript/JavaScript. Currently, Boa supports some\nof the [language][boa-conformance]. More information can be viewed at [Boa's\nwebsite][boa-web].\n\nTry out the most recent release with Boa's live demo\n[playground][boa-playground].\n\n## Boa Crates\n\n- [**`boa_cli`**][cli] - Boa's CLI && REPL implementation\n- [**`boa_ast`**][ast] - Boa's ECMAScript Abstract Syntax Tree.\n- [**`boa_engine`**][engine] - Boa's implementation of ECMAScript builtin objects and execution.\n- [**`boa_gc`**][gc] - Boa's garbage collector.\n- [**`boa_icu_provider`**][icu] - Boa's ICU4X data provider.\n- [**`boa_interner`**][interner] - Boa's string interner.\n- [**`boa_macros`**][macros] - Boa's macros.\n- [**`boa_parser`**][parser] - Boa's lexer and parser.\n- [**`boa_runtime`**][runtime] - Boa's `WebAPI` features.\n- [**`boa_string`**][string] - Boa's ECMAScript string implementation.\n- [**`boa_wintertc`**][wintertc] - Boa's `WinterTC` (TC55) Minimum Common Web API implementation.\n- [**`tag_ptr`**][tag_ptr] - Utility library that enables a pointer to be associated with a tag of type `usize`.\n- [**`small_btree`**][small_btree] - Utility library that adds the `SmallBTreeMap` data structure.\n\n[boa-conformance]: https://boajs.dev/conformance\n[boa-web]: https://boajs.dev/\n[boa-playground]: https://boajs.dev/playground\n[ast]: https://docs.rs/boa_ast/latest/boa_ast/index.html\n[engine]: https://docs.rs/boa_engine/latest/boa_engine/index.html\n[gc]: https://docs.rs/boa_gc/latest/boa_gc/index.html\n[interner]: https://docs.rs/boa_interner/latest/boa_interner/index.html\n[parser]: https://docs.rs/boa_parser/latest/boa_parser/index.html\n[icu]: https://docs.rs/boa_icu_provider/latest/boa_icu_provider/index.html\n[runtime]: https://docs.rs/boa_runtime/latest/boa_runtime/index.html\n[string]: https://docs.rs/boa_string/latest/boa_string/index.html\n[wintertc]: https://docs.rs/boa_wintertc/latest/boa_wintertc/index.html\n[tag_ptr]: https://docs.rs/tag_ptr/latest/tag_ptr/index.html\n[small_btree]: https://docs.rs/small_btree/latest/small_btree/index.html\n[macros]: https://docs.rs/boa_macros/latest/boa_macros/index.html\n[cli]: https://crates.io/crates/boa_cli\n"
  },
  {
    "path": "core/string/Cargo.toml",
    "content": "[package]\nname = \"boa_string\"\nkeywords = [\"javascript\", \"js\", \"string\"]\ncategories = [\"parser-implementations\", \"compilers\"]\nreadme = \"../../README.md\"\ndescription.workspace = true\nversion.workspace = true\nedition.workspace = true\nauthors.workspace = true\nlicense.workspace = true\nrepository.workspace = true\nrust-version.workspace = true\n\n[dependencies]\nitoa.workspace = true\nrustc-hash = { workspace = true, features = [\"std\"] }\nryu-js.workspace = true\n\nstatic_assertions.workspace = true\npaste.workspace = true\nfast-float2.workspace = true\n\n[lints]\nworkspace = true\n\n[package.metadata.docs.rs]\nall-features = true\n"
  },
  {
    "path": "core/string/src/builder.rs",
    "content": "use crate::r#type::{InternalStringType, Latin1, Utf16};\nuse crate::{JsStr, JsStrVariant, JsString, SequenceString, alloc_overflow};\nuse std::{\n    alloc::{Layout, alloc, dealloc, realloc},\n    marker::PhantomData,\n    ops::{Add, AddAssign},\n    ptr::{self, NonNull},\n    str::{self},\n};\n\n/// A mutable builder to create instances of `JsString`.\n#[derive(Debug)]\n#[allow(private_bounds)]\npub struct JsStringBuilder<D: InternalStringType> {\n    cap: usize,\n    len: usize,\n    inner: NonNull<SequenceString<D>>,\n    phantom_data: PhantomData<D>,\n}\n\nimpl<D: InternalStringType> Default for JsStringBuilder<D> {\n    fn default() -> Self {\n        Self::new()\n    }\n}\n\n#[allow(private_bounds)]\nimpl<D: InternalStringType> JsStringBuilder<D> {\n    const DATA_SIZE: usize = size_of::<D::Byte>();\n    const MIN_NON_ZERO_CAP: usize = 8 / Self::DATA_SIZE;\n\n    /// Create a new `JsStringBuilder` with capacity of zero.\n    #[inline]\n    #[must_use]\n    pub const fn new() -> Self {\n        Self {\n            cap: 0,\n            len: 0,\n            inner: NonNull::dangling(),\n            phantom_data: PhantomData,\n        }\n    }\n\n    /// Returns the number of elements that inner `RawJsString` holds.\n    #[inline]\n    #[must_use]\n    pub const fn len(&self) -> usize {\n        self.len\n    }\n\n    /// Forces the length of the [`JsStringBuilder`] to `new_len`.\n    ///\n    /// # Safety\n    ///\n    /// - `new_len` must be less than or equal to `capacity()`.\n    /// - The elements at `old_len..new_len` must be initialized.\n    #[inline]\n    pub const unsafe fn set_len(&mut self, new_len: usize) {\n        debug_assert!(new_len <= self.capacity());\n\n        self.len = new_len;\n    }\n\n    /// Returns the total number of elements can hold without reallocating\n    #[inline]\n    #[must_use]\n    pub const fn capacity(&self) -> usize {\n        self.cap\n    }\n\n    /// Returns the allocated byte of inner `RawJsString`'s data.\n    #[must_use]\n    const fn allocated_data_byte_len(&self) -> usize {\n        self.len() * Self::DATA_SIZE\n    }\n\n    /// Returns the capacity calculated from given layout.\n    #[must_use]\n    const fn capacity_from_layout(layout: Layout) -> usize {\n        (layout.size() - D::DATA_OFFSET) / Self::DATA_SIZE\n    }\n\n    /// Create a new `JsStringBuilder` with specific capacity\n    #[inline]\n    #[must_use]\n    pub fn with_capacity(cap: usize) -> Self {\n        if cap == 0 {\n            return Self::new();\n        }\n        let layout = Self::new_layout(cap);\n        #[allow(clippy::cast_ptr_alignment)]\n        // SAFETY:\n        // The layout size of `RawJsString` is never zero, since it has to store\n        // the length of the string and the reference count.\n        let ptr = unsafe { alloc(layout) };\n\n        let Some(ptr) = NonNull::new(ptr.cast()) else {\n            std::alloc::handle_alloc_error(layout)\n        };\n        Self {\n            cap: Self::capacity_from_layout(layout),\n            len: 0,\n            inner: ptr,\n            phantom_data: PhantomData,\n        }\n    }\n\n    /// Checks if the inner `RawJsString` is allocated.\n    #[must_use]\n    fn is_allocated(&self) -> bool {\n        self.inner != NonNull::dangling()\n    }\n\n    /// Returns the inner sequence string's layout.\n    ///\n    /// # Safety\n    ///\n    /// Caller should ensure that the inner is allocated.\n    #[must_use]\n    unsafe fn current_layout(&self) -> Layout {\n        // SAFETY:\n        // Caller should ensure that the inner is allocated.\n        unsafe {\n            Layout::for_value(self.inner.as_ref())\n                .extend(Layout::array::<D::Byte>(self.capacity()).unwrap_unchecked())\n                .unwrap_unchecked()\n                .0\n                .pad_to_align()\n        }\n    }\n\n    /// Returns the pointer of `data` of inner.\n    ///\n    /// # Safety\n    ///\n    /// Caller should ensure that the inner is allocated.\n    #[must_use]\n    const unsafe fn data(&self) -> *mut D::Byte {\n        let seq_ptr: *mut D::Byte = self.inner.as_ptr().cast();\n        // SAFETY: Caller should ensure that the inner is allocated.\n        unsafe { seq_ptr.byte_add(D::DATA_OFFSET) }\n    }\n\n    /// Allocates when there is not sufficient capacity.\n    #[allow(clippy::inline_always)]\n    #[inline(always)]\n    fn allocate_if_needed(&mut self, required_cap: usize) {\n        if required_cap > self.capacity() {\n            self.allocate(required_cap);\n        }\n    }\n\n    /// Inner logic of `allocate`.\n    ///\n    /// Use `realloc` here because it has a better performance than using combination of `alloc`, `copy` and `dealloc`.\n    #[allow(clippy::cast_ptr_alignment)]\n    fn allocate_inner(&mut self, new_layout: Layout) {\n        let new_ptr = if self.is_allocated() {\n            let old_ptr = self.inner.as_ptr();\n            // SAFETY:\n            // Allocation check has been made above.\n            let old_layout = unsafe { self.current_layout() };\n            // SAFETY:\n            // Valid pointer is required by `realloc` and pointer is checked above to be valid.\n            // The layout size of the sequence string is never zero, since it has to store\n            // the length of the string and the reference count.\n            unsafe { realloc(old_ptr.cast(), old_layout, new_layout.size()) }\n        } else {\n            // SAFETY:\n            // The layout size of the sequence string is never zero, since it has to store\n            // the length of the string and the reference count.\n            unsafe { alloc(new_layout) }\n        };\n\n        let Some(new_ptr) = NonNull::new(new_ptr.cast::<SequenceString<D>>()) else {\n            std::alloc::handle_alloc_error(new_layout)\n        };\n        self.inner = new_ptr;\n        self.cap = Self::capacity_from_layout(new_layout);\n    }\n\n    /// Appends an element to the inner `RawJsString` of `JsStringBuilder`.\n    #[inline]\n    pub fn push(&mut self, v: D::Byte) {\n        let required_cap = self.len() + 1;\n        self.allocate_if_needed(required_cap);\n        // SAFETY:\n        // Capacity has been expanded to be large enough to hold elements.\n        unsafe {\n            self.push_unchecked(v);\n        }\n    }\n\n    /// Pushes elements from slice to `JsStringBuilder` without doing capacity check.\n    ///\n    /// Unlike the standard vector, our held element types are only `u8` and `u16`, which is [`Copy`] derived,\n    ///\n    /// so we only need to copy them instead of cloning.\n    ///\n    /// # Safety\n    ///\n    /// Caller should ensure the capacity is large enough to hold elements.\n    #[inline]\n    pub const unsafe fn extend_from_slice_unchecked(&mut self, v: &[D::Byte]) {\n        // SAFETY: Caller should ensure the capacity is large enough to hold elements.\n        unsafe {\n            ptr::copy_nonoverlapping(v.as_ptr(), self.data().add(self.len()), v.len());\n        }\n        self.len += v.len();\n    }\n\n    /// Pushes elements from slice to `JsStringBuilder`.\n    #[inline]\n    pub fn extend_from_slice(&mut self, v: &[D::Byte]) {\n        let required_cap = self.len() + v.len();\n        self.allocate_if_needed(required_cap);\n        // SAFETY:\n        // Capacity has been expanded to be large enough to hold elements.\n        unsafe {\n            self.extend_from_slice_unchecked(v);\n        }\n    }\n\n    fn new_layout(cap: usize) -> Layout {\n        let new_layout = Layout::array::<D::Byte>(cap)\n            .and_then(|arr| Layout::new::<SequenceString<D>>().extend(arr))\n            .map(|(layout, offset)| (layout.pad_to_align(), offset))\n            .map_err(|_| None);\n        match new_layout {\n            Ok((new_layout, offset)) => {\n                debug_assert_eq!(offset, D::DATA_OFFSET);\n                new_layout\n            }\n            Err(None) => alloc_overflow(),\n            Err(Some(layout)) => std::alloc::handle_alloc_error(layout),\n        }\n    }\n\n    /// Similar to [`Vec::reserve`]\n    ///\n    /// Reserves capacity for at least `additional` more elements to be inserted\n    /// in the given `JsStringBuilder<D>`. The collection may reserve more space to\n    /// speculatively avoid frequent reallocations. After calling `reserve`,\n    /// capacity will be greater than or equal to `self.len() + additional`.\n    /// Does nothing if capacity is already sufficient.\n    #[inline]\n    pub fn reserve(&mut self, additional: usize) {\n        if additional > self.capacity().wrapping_sub(self.len) {\n            let Some(cap) = self.len().checked_add(additional) else {\n                alloc_overflow()\n            };\n            self.allocate(cap);\n        }\n    }\n\n    /// Similar to [`Vec::reserve_exact`]\n    ///\n    /// Reserves the minimum capacity for at least `additional` more elements to\n    /// be inserted in the given `JsStringBuilder<D>`. Unlike [`reserve`], this will not\n    /// deliberately over-allocate to speculatively avoid frequent allocations.\n    /// After calling `reserve_exact`, capacity will be greater than or equal to\n    /// `self.len() + additional`. Does nothing if the capacity is already\n    /// sufficient.\n    ///\n    /// Note that the allocator may give the collection more space than it\n    /// requests. Therefore, capacity can not be relied upon to be precisely\n    /// minimal. Prefer [`reserve`] if future insertions are expected.\n    ///\n    /// [`reserve`]: JsStringBuilder::reserve\n    #[inline]\n    pub fn reserve_exact(&mut self, additional: usize) {\n        if additional > self.capacity().wrapping_sub(self.len) {\n            let Some(cap) = self.len().checked_add(additional) else {\n                alloc_overflow()\n            };\n            self.allocate_inner(Self::new_layout(cap));\n        }\n    }\n\n    /// Allocates memory to the inner `RawJsString` by the given capacity.\n    /// Capacity calculation is from [`Vec::reserve`].\n    fn allocate(&mut self, cap: usize) {\n        let cap = std::cmp::max(self.capacity() * 2, cap);\n        let cap = std::cmp::max(Self::MIN_NON_ZERO_CAP, cap);\n        self.allocate_inner(Self::new_layout(cap));\n    }\n\n    /// Appends an element to the inner `RawJsString` of `JsStringBuilder` without doing bounds check.\n    /// # Safety\n    ///\n    /// Caller should ensure the capacity is large enough to hold elements.\n    #[inline]\n    pub const unsafe fn push_unchecked(&mut self, v: D::Byte) {\n        // SAFETY: Caller should ensure the capacity is large enough to hold elements.\n        unsafe {\n            self.data().add(self.len()).write(v);\n            self.len += 1;\n        }\n    }\n\n    /// Returns true if this `JsStringBuilder` has a length of zero, and false otherwise.\n    #[inline]\n    #[must_use]\n    pub fn is_empty(&self) -> bool {\n        self.len() == 0\n    }\n\n    /// Checks if all bytes in inner `RawJsString`'s data are ascii.\n    #[inline]\n    #[must_use]\n    pub fn is_ascii(&self) -> bool {\n        // SAFETY:\n        // `NonNull` verified for us that the pointer returned by `alloc` is valid,\n        // meaning we can read to its pointed memory.\n        let data = unsafe {\n            std::slice::from_raw_parts(self.data().cast::<u8>(), self.allocated_data_byte_len())\n        };\n        data.is_ascii()\n    }\n\n    /// Extracts a slice containing the elements in the inner `RawJsString`.\n    #[inline]\n    #[must_use]\n    pub fn as_slice(&self) -> &[D::Byte] {\n        if self.is_allocated() {\n            // SAFETY:\n            // The inner `RawJsString` is allocated which means it is not null.\n            unsafe { std::slice::from_raw_parts(self.data(), self.len()) }\n        } else {\n            &[]\n        }\n    }\n\n    /// Extracts a mutable slice containing the elements in the inner `RawJsString`.\n    ///\n    /// # Safety\n    /// The caller must ensure that the content of the slice is valid encoding before the borrow ends.\n    /// Use of a builder whose contents are not valid encoding is undefined behavior.\n    #[inline]\n    #[must_use]\n    pub unsafe fn as_mut_slice(&mut self) -> &mut [D::Byte] {\n        if self.is_allocated() {\n            // SAFETY:\n            // The inner `RawJsString` is allocated which means it is not null.\n            unsafe { std::slice::from_raw_parts_mut(self.data(), self.len()) }\n        } else {\n            &mut []\n        }\n    }\n\n    /// Builds `JsString` from `JsStringBuilder`\n    #[inline]\n    #[must_use]\n    fn build_inner(mut self) -> JsString {\n        if self.is_empty() {\n            return JsString::default();\n        }\n        let len = self.len();\n\n        // Shrink to fit the length.\n        if len != self.capacity() {\n            let layout = Self::new_layout(self.len());\n            self.allocate_inner(layout);\n        }\n\n        let inner = self.inner;\n\n        // SAFETY:\n        // `NonNull` verified for us that the pointer returned by `alloc` is valid,\n        // meaning we can write to its pointed memory.\n        unsafe {\n            inner.as_ptr().write(SequenceString::<D>::new(len));\n        }\n\n        // Tell the compiler not to call the destructor of `JsStringBuilder`,\n        // because we move inner sequence string to `JsString`.\n        std::mem::forget(self);\n\n        JsString { ptr: inner.cast() }\n    }\n}\n\nimpl<D: InternalStringType> Drop for JsStringBuilder<D> {\n    /// Set cold since [`JsStringBuilder`] should be created to build `JsString`\n    #[cold]\n    #[inline]\n    fn drop(&mut self) {\n        if self.is_allocated() {\n            // SAFETY:\n            // Allocation check has been made above.\n            let layout = unsafe { self.current_layout() };\n            // SAFETY:\n            // layout: All the checks for the validity of the layout have already been made on `allocate_inner`.\n            // `NonNull` verified for us that the pointer returned by `alloc` is valid,\n            // meaning we can free its pointed memory.\n            unsafe {\n                dealloc(self.inner.as_ptr().cast(), layout);\n            }\n        }\n    }\n}\n\nimpl<D: InternalStringType> AddAssign<&JsStringBuilder<D>> for JsStringBuilder<D> {\n    #[inline]\n    fn add_assign(&mut self, rhs: &JsStringBuilder<D>) {\n        self.extend_from_slice(rhs.as_slice());\n    }\n}\n\nimpl<D: InternalStringType> AddAssign<&[D::Byte]> for JsStringBuilder<D> {\n    #[inline]\n    fn add_assign(&mut self, rhs: &[D::Byte]) {\n        self.extend_from_slice(rhs);\n    }\n}\n\nimpl<D: InternalStringType> Add<&JsStringBuilder<D>> for JsStringBuilder<D> {\n    type Output = Self;\n\n    #[inline]\n    fn add(mut self, rhs: &JsStringBuilder<D>) -> Self::Output {\n        self.extend_from_slice(rhs.as_slice());\n        self\n    }\n}\n\nimpl<D: InternalStringType> Add<&[D::Byte]> for JsStringBuilder<D> {\n    type Output = Self;\n\n    #[inline]\n    fn add(mut self, rhs: &[D::Byte]) -> Self::Output {\n        self.extend_from_slice(rhs);\n        self\n    }\n}\n\nimpl<D: InternalStringType> Extend<D::Byte> for JsStringBuilder<D> {\n    #[inline]\n    fn extend<I: IntoIterator<Item = D::Byte>>(&mut self, iter: I) {\n        let iterator = iter.into_iter();\n        let (lower_bound, _) = iterator.size_hint();\n        let require_cap = self.len() + lower_bound;\n        self.allocate_if_needed(require_cap);\n        iterator.for_each(|c| self.push(c));\n    }\n}\n\nimpl<D: InternalStringType> FromIterator<D::Byte> for JsStringBuilder<D> {\n    #[inline]\n    fn from_iter<T: IntoIterator<Item = D::Byte>>(iter: T) -> Self {\n        let mut builder = Self::new();\n        builder.extend(iter);\n        builder\n    }\n}\n\nimpl<D: InternalStringType> From<&[D::Byte]> for JsStringBuilder<D> {\n    #[inline]\n    fn from(value: &[D::Byte]) -> Self {\n        let mut builder = Self::with_capacity(value.len());\n        // SAFETY: The capacity is large enough to hold elements.\n        unsafe { builder.extend_from_slice_unchecked(value) };\n        builder\n    }\n}\n\nimpl<D: InternalStringType> PartialEq for JsStringBuilder<D>\nwhere\n    D::Byte: Eq + PartialEq,\n{\n    #[inline]\n    fn eq(&self, other: &Self) -> bool {\n        let slice: &[D::Byte] = self.as_slice();\n        let other_slice: &[D::Byte] = other.as_slice();\n        slice.eq(other_slice)\n    }\n}\n\nimpl<D: InternalStringType> Clone for JsStringBuilder<D> {\n    #[inline]\n    fn clone(&self) -> Self {\n        if self.is_allocated() {\n            let mut builder = Self::with_capacity(self.capacity());\n            // SAFETY: The capacity is large enough to hold elements.\n            unsafe { builder.extend_from_slice_unchecked(self.as_slice()) };\n            builder\n        } else {\n            Self::new()\n        }\n    }\n\n    /// Performs copy-assignment from `source`.\n    ///\n    /// Rewritten to avoid unnecessary allocation.\n    #[inline]\n    fn clone_from(&mut self, source: &Self) {\n        let source_len = source.len();\n\n        if source_len > self.capacity() {\n            self.allocate(source_len);\n        } else {\n            // At this point, inner sequence string of self or source can be not allocated,\n            // returns earlier to avoid copying from/to `null`.\n            if source_len == 0 {\n                // SAFETY: 0 is always less or equal to self's capacity.\n                unsafe { self.set_len(0) };\n                return;\n            }\n        }\n\n        // SAFETY: self should be allocated after allocation.\n        let self_data = unsafe { self.data() };\n\n        // SAFETY: source_len is greater than 0 so source should be allocated.\n        let source_data = unsafe { source.data() };\n\n        // SAFETY: Borrow checker should not allow this to be overlapped and pointers are valid.\n        unsafe { ptr::copy_nonoverlapping(source_data, self_data, source_len) };\n\n        // SAFETY: source_len has checked to be less or equal to self's capacity.\n        unsafe { self.set_len(source_len) };\n    }\n}\n\n/// **`Latin1`** encoded `JsStringBuilder`\n/// # Warning\n/// If you are not sure the characters that will be added and don't want to preprocess them,\n/// use [`CommonJsStringBuilder`] instead.\n/// ## Examples\n///\n/// ```rust\n/// use boa_string::Latin1JsStringBuilder;\n/// let mut s = Latin1JsStringBuilder::new();\n/// s.push(b'x');\n/// s.extend_from_slice(&[b'1', b'2', b'3']);\n/// s.extend([b'1', b'2', b'3']);\n/// let js_string = s.build();\n/// ```\npub type Latin1JsStringBuilder = JsStringBuilder<Latin1>;\n\nimpl Latin1JsStringBuilder {\n    /// Builds a `JsString` if the current instance is strictly `ASCII`.\n    ///\n    /// When the string contains characters outside the `ASCII` range, it cannot be determined\n    /// whether the encoding is `Latin1` or others. Therefore, this method only returns a\n    /// valid `JsString` when the instance is entirely `ASCII`. If any non-`ASCII` characters\n    /// are present, it returns `None` to avoid ambiguity in encoding.\n    ///\n    /// If the caller is certain that the string is encoded in `Latin1`,\n    /// [`build_as_latin1`](Self::build_as_latin1) can be used to avoid the `ASCII` check.\n    #[inline]\n    #[must_use]\n    pub fn build(self) -> Option<JsString> {\n        if self.is_ascii() {\n            Some(self.build_inner())\n        } else {\n            None\n        }\n    }\n\n    /// Builds `JsString` from `Latin1JsStringBuilder`, assume that the inner data is `Latin1` encoded\n    ///\n    /// # Safety\n    /// Caller must ensure that the string is encoded in `Latin1`.\n    ///\n    /// If the string contains characters outside the `Latin1` range, it may lead to encoding errors,\n    /// resulting in an incorrect or malformed `JsString`. This could cause undefined behavior\n    /// when the resulting string is used in further operations or when interfacing with other\n    /// parts of the system that expect valid `Latin1` encoded string.\n    #[inline]\n    #[must_use]\n    pub unsafe fn build_as_latin1(self) -> JsString {\n        self.build_inner()\n    }\n}\n\n/// **`UTF-16`** encoded `JsStringBuilder`\n/// ## Examples\n///\n/// ```rust\n/// use boa_string::Utf16JsStringBuilder;\n/// let mut s = Utf16JsStringBuilder::new();\n/// s.push(b'x' as u16);\n/// s.extend_from_slice(&[b'1', b'2', b'3'].map(u16::from));\n/// s.extend([0xD83C, 0xDFB9, 0xD83C, 0xDFB6, 0xD83C, 0xDFB5]); // 🎹🎶🎵\n/// let js_string = s.build();\n/// ```\npub type Utf16JsStringBuilder = JsStringBuilder<Utf16>;\n\nimpl Utf16JsStringBuilder {\n    /// Builds `JsString` from `Utf16JsStringBuilder`\n    #[inline]\n    #[must_use]\n    pub fn build(self) -> JsString {\n        self.build_inner()\n    }\n}\n\n/// Represents a segment of a string used to construct a [`JsString`].\n#[derive(Clone, Debug)]\npub enum Segment<'a> {\n    /// A string segment represented as a `JsString`.\n    String(JsString),\n\n    /// A string segment represented as a `JsStr`.\n    Str(JsStr<'a>),\n\n    /// A string segment represented as a byte.\n    Latin1(u8),\n\n    /// A Unicode code point segment represented as a character.\n    CodePoint(char),\n}\n\nimpl Segment<'_> {\n    /// Checks if the segment consists solely of `ASCII` characters.\n    #[inline]\n    #[must_use]\n    fn is_ascii(&self) -> bool {\n        match self {\n            Segment::String(s) => s.as_str().is_latin1(),\n            Segment::Str(s) => s.is_latin1(),\n            Segment::Latin1(b) => *b <= 0x7f,\n            Segment::CodePoint(ch) => *ch as u32 <= 0x7F,\n        }\n    }\n}\n\nimpl From<JsString> for Segment<'_> {\n    #[inline]\n    fn from(value: JsString) -> Self {\n        Self::String(value)\n    }\n}\n\nimpl From<String> for Segment<'_> {\n    #[inline]\n    fn from(value: String) -> Self {\n        Self::String(value.into())\n    }\n}\n\nimpl From<&[u16]> for Segment<'_> {\n    #[inline]\n    fn from(value: &[u16]) -> Self {\n        Self::String(value.into())\n    }\n}\n\nimpl From<&str> for Segment<'_> {\n    #[inline]\n    fn from(value: &str) -> Self {\n        Self::String(value.into())\n    }\n}\n\nimpl<'seg, 'ref_str: 'seg> From<JsStr<'ref_str>> for Segment<'seg> {\n    #[inline]\n    fn from(value: JsStr<'ref_str>) -> Self {\n        Self::Str(value)\n    }\n}\n\nimpl From<u8> for Segment<'_> {\n    #[inline]\n    fn from(value: u8) -> Self {\n        Self::Latin1(value)\n    }\n}\n\nimpl From<char> for Segment<'_> {\n    #[inline]\n    fn from(value: char) -> Self {\n        Self::CodePoint(value)\n    }\n}\n\n/// Common `JsString` builder that accepts multiple variant of string or character.\n///\n/// Originally based on [kiesel-js](https://codeberg.org/kiesel-js/kiesel/src/branch/main/src/types/language/String/Builder.zig)\n#[derive(Clone, Debug, Default)]\npub struct CommonJsStringBuilder<'a> {\n    segments: Vec<Segment<'a>>,\n}\n\nimpl<'seg, 'ref_str: 'seg> CommonJsStringBuilder<'seg> {\n    /// Creates a new `CommonJsStringBuilder` with capacity of zero.\n    #[inline]\n    #[must_use]\n    pub const fn new() -> Self {\n        Self {\n            segments: Vec::new(),\n        }\n    }\n\n    /// Similar to `Vec::with_capacity`.\n    ///\n    /// Creates a new `CommonJsStringBuilder` with given capacity.\n    #[inline]\n    #[must_use]\n    pub fn with_capacity(capacity: usize) -> Self {\n        Self {\n            segments: Vec::with_capacity(capacity),\n        }\n    }\n\n    /// Similar to `Vec::reserve`.\n    ///\n    /// Reserves additional capacity for the inner vector.\n    #[inline]\n    pub fn reserve(&mut self, additional: usize) {\n        self.segments.reserve(additional);\n    }\n\n    /// Similar to `Vec::reserve_exact`.\n    ///\n    /// Reserves the minimum capacity for the inner vector.\n    #[inline]\n    pub fn reserve_exact(&mut self, additional: usize) {\n        self.segments.reserve_exact(additional);\n    }\n\n    /// Appends string segments to the back of the inner vector.\n    #[inline]\n    pub fn push<T: Into<Segment<'ref_str>>>(&mut self, seg: T) {\n        self.segments.push(seg.into());\n    }\n\n    /// Checks if all string segments contains only `ASCII` bytes.\n    #[inline]\n    #[must_use]\n    pub fn is_ascii(&self) -> bool {\n        self.segments.iter().all(Segment::is_ascii)\n    }\n\n    /// Returns the number of string segment in inner vector.\n    #[inline]\n    #[must_use]\n    pub fn len(&self) -> usize {\n        self.segments.len()\n    }\n\n    /// Returns true if this `CommonJsStringBuilder` has a length of zero, and false otherwise.\n    #[inline]\n    #[must_use]\n    pub fn is_empty(&self) -> bool {\n        self.len() == 0\n    }\n\n    /// Builds `Latin1` encoded `JsString` from string segments.\n    ///\n    /// This doesn't consume the builder itself because it may fails to build\n    /// and the caller may wants to keep the builder for further operations.\n    ///\n    /// This processes the following types of segments:\n    ///\n    /// - `Segment::String(s)`: Encodes the string if it can be represented in `Latin1`.\n    /// - `Segment::Str(s)`: Encodes the string slice if it can be represented in `Latin1`.\n    /// - `Segment::Latin1(b)`: Encodes the byte if it's within the `ASCII` range.\n    /// - `Segment::CodePoint(ch)`: Encodes the code point by converting it to a byte if it's within the `ASCII` range.\n    ///\n    /// Return `None` if any segment fails to encode.\n    #[inline]\n    #[must_use]\n    #[allow(clippy::cast_lossless)]\n    pub fn build_from_latin1(&self) -> Option<JsString> {\n        let mut builder = Latin1JsStringBuilder::new();\n        for seg in &self.segments {\n            match seg {\n                Segment::String(s) => {\n                    if let Some(data) = s.as_str().as_latin1() {\n                        builder.extend_from_slice(data);\n                    } else {\n                        return None;\n                    }\n                }\n                Segment::Str(s) => {\n                    if let Some(data) = s.as_latin1() {\n                        builder.extend_from_slice(data);\n                    } else {\n                        return None;\n                    }\n                }\n                Segment::Latin1(b) => {\n                    if *b <= 0x7f {\n                        builder.push(*b);\n                    } else {\n                        return None;\n                    }\n                }\n                Segment::CodePoint(ch) => {\n                    if let Ok(b) = u8::try_from(*ch as u32) {\n                        builder.push(b);\n                    } else {\n                        return None;\n                    }\n                }\n            }\n        }\n        builder.build()\n    }\n\n    /// Builds `Utf-16` encoded `JsString` from string segments.\n    #[inline]\n    #[must_use]\n    #[allow(clippy::cast_possible_truncation)]\n    pub fn build_from_utf16(self) -> JsString {\n        let mut builder = Utf16JsStringBuilder::new();\n        for seg in self.segments {\n            match seg {\n                Segment::String(s) => {\n                    let js_str = s.as_str();\n                    match js_str.variant() {\n                        JsStrVariant::Latin1(s) => builder.extend(s.iter().copied().map(u16::from)),\n                        JsStrVariant::Utf16(s) => builder.extend_from_slice(s),\n                    }\n                }\n                Segment::Str(s) => match s.variant() {\n                    JsStrVariant::Latin1(s) => builder.extend(s.iter().copied().map(u16::from)),\n                    JsStrVariant::Utf16(s) => builder.extend_from_slice(s),\n                },\n                Segment::Latin1(latin1) => builder.push(u16::from(latin1)),\n                Segment::CodePoint(code_point) => {\n                    builder.extend_from_slice(code_point.encode_utf16(&mut [0_u16; 2]));\n                }\n            }\n        }\n        builder.build()\n    }\n\n    /// Builds `JsString` from `CommonJsStringBuilder`,\n    ///\n    /// This function first checks if the instance is empty:\n    /// - If it is empty, it returns the default `JsString`.\n    /// - If it contains only ASCII characters, it safely encodes it as `Latin1`.\n    /// - If it contains non-ASCII characters, it falls back to encoding using `UTF-16`.\n    #[inline]\n    #[must_use]\n    pub fn build(self) -> JsString {\n        if self.is_empty() {\n            JsString::default()\n        } else if self.is_ascii() {\n            // SAFETY:\n            // All string segment contains only ascii byte, so this can be encoded as `Latin1`.\n            unsafe { self.build_as_latin1() }\n        } else {\n            self.build_from_utf16()\n        }\n    }\n\n    /// Builds `Latin1` encoded `JsString` from `CommonJsStringBuilder`, return `None` if segments can't be encoded as `Latin1`\n    ///\n    /// # Safety\n    /// Caller must ensure that the string segments can be `Latin1` encoded.\n    ///\n    /// If string segments can't be `Latin1` encoded, it may lead to encoding errors,\n    /// resulting in an incorrect or malformed `JsString`. This could cause undefined behavior\n    /// when the resulting string is used in further operations or when interfacing with other\n    /// parts of the system that expect valid `Latin1` encoded string.\n    #[inline]\n    #[must_use]\n    pub unsafe fn build_as_latin1(self) -> JsString {\n        let mut builder = Latin1JsStringBuilder::new();\n        for seg in self.segments {\n            match seg {\n                Segment::String(s) => {\n                    let js_str = s.as_str();\n                    let Some(s) = js_str.as_latin1() else {\n                        unreachable!(\"string segment should be latin1\")\n                    };\n                    builder.extend_from_slice(s);\n                }\n                Segment::Str(s) => {\n                    let Some(s) = s.as_latin1() else {\n                        unreachable!(\"string segment should be latin1\")\n                    };\n                    builder.extend_from_slice(s);\n                }\n                Segment::Latin1(latin1) => builder.push(latin1),\n                Segment::CodePoint(code_point) => builder.push(code_point as u8),\n            }\n        }\n        // SAFETY: All string segments can be encoded as `Latin1` string.\n        unsafe { builder.build_as_latin1() }\n    }\n}\n\nimpl<'ref_str, T: Into<Segment<'ref_str>>> AddAssign<T> for CommonJsStringBuilder<'ref_str> {\n    #[inline]\n    fn add_assign(&mut self, rhs: T) {\n        self.push(rhs);\n    }\n}\n\nimpl<'ref_str, T: Into<Segment<'ref_str>>> Add<T> for CommonJsStringBuilder<'ref_str> {\n    type Output = Self;\n\n    #[inline]\n    fn add(mut self, rhs: T) -> Self::Output {\n        self.push(rhs);\n        self\n    }\n}\n"
  },
  {
    "path": "core/string/src/code_point.rs",
    "content": "use std::fmt::Write;\n\n/// Represents a Unicode codepoint within a [`crate::JsString`], which could be a valid\n/// '[Unicode scalar value]', or an unpaired surrogate.\n///\n/// [Unicode scalar value]: https://www.unicode.org/glossary/#unicode_scalar_value\n#[derive(Clone, Copy, Debug, Eq, PartialEq)]\npub enum CodePoint {\n    /// A valid Unicode scalar value.\n    Unicode(char),\n\n    /// An unpaired surrogate.\n    UnpairedSurrogate(u16),\n}\n\nimpl CodePoint {\n    /// Get the number of UTF-16 code units needed to encode this code point.\n    #[inline]\n    #[must_use]\n    pub const fn code_unit_count(self) -> usize {\n        match self {\n            Self::Unicode(c) => c.len_utf16(),\n            Self::UnpairedSurrogate(_) => 1,\n        }\n    }\n\n    /// Convert the code point to its [`u32`] representation.\n    #[inline]\n    #[must_use]\n    pub fn as_u32(self) -> u32 {\n        match self {\n            Self::Unicode(c) => u32::from(c),\n            Self::UnpairedSurrogate(surr) => u32::from(surr),\n        }\n    }\n\n    /// If the code point represents a valid 'Unicode scalar value', returns its [`char`]\n    /// representation, otherwise returns [`None`] on unpaired surrogates.\n    #[inline]\n    #[must_use]\n    pub const fn as_char(self) -> Option<char> {\n        match self {\n            Self::Unicode(c) => Some(c),\n            Self::UnpairedSurrogate(_) => None,\n        }\n    }\n\n    /// Encodes this code point as UTF-16 into the provided u16 buffer, and then returns the subslice\n    /// of the buffer that contains the encoded character.\n    ///\n    /// # Panics\n    ///\n    /// Panics if the buffer is not large enough. A buffer of length 2 is large enough to encode any\n    /// code point.\n    #[inline]\n    #[must_use]\n    pub fn encode_utf16(self, dst: &mut [u16]) -> &mut [u16] {\n        match self {\n            Self::Unicode(c) => c.encode_utf16(dst),\n            Self::UnpairedSurrogate(surr) => {\n                dst[0] = surr;\n                &mut dst[0..=0]\n            }\n        }\n    }\n}\n\nimpl std::fmt::Display for CodePoint {\n    #[inline]\n    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {\n        match self {\n            CodePoint::Unicode(c) => f.write_char(*c),\n            CodePoint::UnpairedSurrogate(c) => {\n                write!(f, \"\\\\u{c:04X}\")\n            }\n        }\n    }\n}\n\nimpl From<char> for CodePoint {\n    fn from(value: char) -> Self {\n        Self::Unicode(value)\n    }\n}\n\nimpl From<u16> for CodePoint {\n    fn from(value: u16) -> Self {\n        char::from_u32(u32::from(value))\n            .map_or_else(|| CodePoint::UnpairedSurrogate(value), CodePoint::Unicode)\n    }\n}\n"
  },
  {
    "path": "core/string/src/common.rs",
    "content": "//! List of commonly used strings in Javascript code.\n\nuse super::JsString;\nuse crate::{JsStr, StaticString};\nuse paste::paste;\nuse rustc_hash::FxHashMap;\nuse std::sync::LazyLock;\n\nmacro_rules! well_known_statics {\n    ( $( $(#[$attr:meta])* ($name:ident, $string:literal) ),+$(,)? ) => {\n        $(\n            paste!{\n                #[doc = \"Gets the static `JsString` for `\\\"\" $string \"\\\"`.\"]\n                pub const $name: JsString = const {\n                    JsString::from_static(Self::find_static_js_string($string))\n                };\n            }\n        )+\n    };\n}\n\n/// List of commonly used strings in Javascript code.\n///\n/// Any strings defined here are used as a static [`JsString`] instead of allocating on the heap.\n#[derive(Debug, Clone, Copy)]\npub struct StaticJsStrings;\n\nimpl StaticJsStrings {\n    // useful to search at compile time a certain string in the array\n    const fn find_static_js_string(candidate: &str) -> &'static StaticString {\n        const fn const_eq(lhs: &[u8], rhs: &[u8]) -> bool {\n            if lhs.len() != rhs.len() {\n                return false;\n            }\n\n            let mut i = 0;\n            while i < lhs.len() {\n                if lhs[i] != rhs[i] {\n                    return false;\n                }\n                i += 1;\n            }\n            true\n        }\n\n        let len = RAW_STATICS.len();\n        let mut i = 0;\n        while i < len {\n            let Some(s) = RAW_STATICS[i].str.as_latin1() else {\n                // All static strings are latin1 encoded\n                unreachable!()\n            };\n            if const_eq(s, candidate.as_bytes()) {\n                return &RAW_STATICS[i];\n            }\n            i += 1;\n        }\n\n        panic!(\"couldn't find the required string on the common string array\");\n    }\n\n    /// Gets the `JsString` corresponding to `string`, or `None` if the string\n    /// doesn't exist inside the static array.\n    #[inline]\n    #[must_use]\n    pub fn get_string(string: &JsStr<'_>) -> Option<JsString> {\n        if string.len() > MAX_STATIC_LENGTH {\n            return None;\n        }\n\n        let static_str = *RAW_STATICS_CACHE.get(string)?;\n        Some(JsString::from_static(static_str))\n    }\n\n    /// Gets the `JsStr` corresponding to `string`, or `None` if the string\n    /// doesn't exist inside the static array.\n    #[inline]\n    #[must_use]\n    pub fn get_js_str(string: &JsStr<'_>) -> Option<JsStr<'static>> {\n        if string.len() > MAX_STATIC_LENGTH {\n            return None;\n        }\n\n        Some(RAW_STATICS_CACHE.get(string)?.str)\n    }\n\n    // Some consts are only used on certain features, which triggers the unused lint.\n    well_known_statics! {\n        (EMPTY_STRING, \"\"),\n        (LENGTH, \"length\"),\n        // Symbols\n        (SYMBOL_ASYNC_ITERATOR, \"Symbol.asyncIterator\"),\n        (SYMBOL_HAS_INSTANCE, \"Symbol.hasInstance\"),\n        (SYMBOL_IS_CONCAT_SPREADABLE, \"Symbol.isConcatSpreadable\"),\n        (SYMBOL_ITERATOR, \"Symbol.iterator\"),\n        (SYMBOL_MATCH, \"Symbol.match\"),\n        (SYMBOL_MATCH_ALL, \"Symbol.matchAll\"),\n        (SYMBOL_REPLACE, \"Symbol.replace\"),\n        (SYMBOL_SEARCH, \"Symbol.search\"),\n        (SYMBOL_SPECIES, \"Symbol.species\"),\n        (SYMBOL_SPLIT, \"Symbol.split\"),\n        (SYMBOL_TO_PRIMITIVE, \"Symbol.toPrimitive\"),\n        (SYMBOL_TO_STRING_TAG, \"Symbol.toStringTag\"),\n        (SYMBOL_UNSCOPABLES, \"Symbol.unscopables\"),\n        (SYMBOL_DISPOSE, \"Symbol.dispose\"),\n        (SYMBOL_ASYNC_DISPOSE, \"Symbol.asyncDispose\"),\n        (FN_SYMBOL_ASYNC_ITERATOR, \"[Symbol.asyncIterator]\"),\n        (FN_SYMBOL_HAS_INSTANCE, \"[Symbol.hasInstance]\"),\n        (FN_SYMBOL_IS_CONCAT_SPREADABLE, \"[Symbol.isConcatSpreadable]\"),\n        (FN_SYMBOL_ITERATOR, \"[Symbol.iterator]\"),\n        (FN_SYMBOL_MATCH, \"[Symbol.match]\"),\n        (FN_SYMBOL_MATCH_ALL, \"[Symbol.matchAll]\"),\n        (FN_SYMBOL_REPLACE, \"[Symbol.replace]\"),\n        (FN_SYMBOL_SEARCH, \"[Symbol.search]\"),\n        (FN_SYMBOL_SPECIES, \"[Symbol.species]\"),\n        (FN_SYMBOL_SPLIT, \"[Symbol.split]\"),\n        (FN_SYMBOL_TO_PRIMITIVE, \"[Symbol.toPrimitive]\"),\n        (FN_SYMBOL_TO_STRING_TAG, \"[Symbol.toStringTag]\"),\n        (FN_SYMBOL_UNSCOPABLES, \"[Symbol.unscopables]\"),\n        (FN_SYMBOL_DISPOSE, \"[Symbol.dispose]\"),\n        (FN_SYMBOL_ASYNC_DISPOSE, \"[Symbol.asyncDispose]\"),\n        // Builtins\n        (ARRAY, \"Array\"),\n        (ARRAY_BUFFER, \"ArrayBuffer\"),\n        (ITERATOR, \"Iterator\"),\n        (SHARED_ARRAY_BUFFER, \"SharedArrayBuffer\"),\n        (ASYNC_FUNCTION, \"AsyncFunction\"),\n        (ASYNC_GENERATOR, \"AsyncGenerator\"),\n        (ASYNC_GENERATOR_FUNCTION, \"AsyncGeneratorFunction\"),\n        (ATOMICS, \"Atomics\"),\n        (BIG_INT, \"BigInt\"),\n        (BOOLEAN, \"Boolean\"),\n        (DATA_VIEW, \"DataView\"),\n        (DATE, \"Date\"),\n        (ERROR, \"Error\"),\n        (AGGREGATE_ERROR, \"AggregateError\"),\n        (EVAL_ERROR, \"EvalError\"),\n        (RANGE_ERROR, \"RangeError\"),\n        (REFERENCE_ERROR, \"ReferenceError\"),\n        (SYNTAX_ERROR, \"SyntaxError\"),\n        (TYPE_ERROR, \"TypeError\"),\n        (URI_ERROR, \"URIError\"),\n        (ESCAPE, \"escape\"),\n        (UNESCAPE, \"unescape\"),\n        (EVAL, \"eval\"),\n        (FUNCTION, \"Function\"),\n        (GENERATOR, \"Generator\"),\n        (GENERATOR_FUNCTION, \"GeneratorFunction\"),\n        (INTL, \"Intl\"),\n        (COLLATOR, \"Collator\"),\n        (LIST_FORMAT, \"ListFormat\"),\n        (LOCALE, \"Locale\"),\n        (PLURAL_RULES, \"PluralRules\"),\n        (SEGMENTER, \"Segmenter\"),\n        (DATE_TIME_FORMAT, \"DateTimeFormat\"),\n        (JSON, \"JSON\"),\n        (MAP, \"Map\"),\n        (MATH, \"Math\"),\n        (NUMBER, \"Number\"),\n        (NUMBER_FORMAT, \"NumberFormat\"),\n        (IS_FINITE, \"isFinite\"),\n        (IS_NAN, \"isNaN\"),\n        (PARSE_INT, \"parseInt\"),\n        (PARSE_FLOAT, \"parseFloat\"),\n        (OBJECT, \"Object\"),\n        (PROMISE, \"Promise\"),\n        (PROXY, \"Proxy\"),\n        (REFLECT, \"Reflect\"),\n        (REG_EXP, \"RegExp\"),\n        (SET, \"Set\"),\n        (STRING, \"String\"),\n        (SYMBOL, \"Symbol\"),\n        (FINALIZATION_REGISTRY, \"FinalizationRegistry\"),\n        (TYPED_ARRAY, \"TypedArray\"),\n        (INT8_ARRAY, \"Int8Array\"),\n        (UINT8_ARRAY, \"Uint8Array\"),\n        (UINT8_CLAMPED_ARRAY, \"Uint8ClampedArray\"),\n        (INT16_ARRAY, \"Int16Array\"),\n        (UINT16_ARRAY, \"Uint16Array\"),\n        (INT32_ARRAY, \"Int32Array\"),\n        (UINT32_ARRAY, \"Uint32Array\"),\n        (BIG_INT64_ARRAY, \"BigInt64Array\"),\n        (BIG_UINT64_ARRAY, \"BigUint64Array\"),\n        #[cfg(feature = \"float16\")]\n        (FLOAT16_ARRAY, \"Float16Array\"),\n        (FLOAT32_ARRAY, \"Float32Array\"),\n        (FLOAT64_ARRAY, \"Float64Array\"),\n        (ENCODE_URI, \"encodeURI\"),\n        (ENCODE_URI_COMPONENT, \"encodeURIComponent\"),\n        (DECODE_URI, \"decodeURI\"),\n        (DECODE_URI_COMPONENT, \"decodeURIComponent\"),\n        (WEAK_REF, \"WeakRef\"),\n        (WEAK_MAP, \"WeakMap\"),\n        (WEAK_SET, \"WeakSet\"),\n        (TEMPORAL, \"Temporal\"),\n        (NOW_TAG, \"Temporal.Now\"),\n        (INSTANT_TAG, \"Temporal.Instant\"),\n        (DURATION_TAG, \"Temporal.Duration\"),\n        (PLAIN_DATE_TAG, \"Temporal.PlainDate\"),\n        (PLAIN_DATETIME_TAG, \"Temporal.PlainDateTime\"),\n        (PLAIN_TIME_TAG, \"Temporal.PlainTime\"),\n        (PLAIN_YM_TAG, \"Temporal.PlainYearMonth\"),\n        (PLAIN_MD_TAG, \"Temporal.PlainMonthDay\"),\n        (ZONED_DT_TAG, \"Temporal.ZonedDateTime\"),\n        (NOW_NAME, \"Now\"),\n        (INSTANT_NAME, \"Instant\"),\n        (DURATION_NAME, \"Duration\"),\n        (PLAIN_DATE_NAME, \"PlainDate\"),\n        (PLAIN_DATETIME_NAME, \"PlainDateTime\"),\n        (PLAIN_TIME_NAME, \"PlainTime\"),\n        (PLAIN_YM_NAME, \"PlainYearMonth\"),\n        (PLAIN_MD_NAME, \"PlainMonthDay\"),\n        (ZONED_DT_NAME, \"ZonedDateTime\"),\n    }\n}\n\nconst MAX_STATIC_LENGTH: usize = {\n    let mut max = 0;\n    let mut i = 0;\n    while i < RAW_STATICS.len() {\n        let len = RAW_STATICS[i].str.len();\n        if len > max {\n            max = len;\n        }\n        i += 1;\n    }\n    max\n};\n\n/// Map from a `JsStr` to its corresponding `StaticJsString`.\nstatic RAW_STATICS_CACHE: LazyLock<FxHashMap<JsStr<'static>, &'static StaticString>> =\n    LazyLock::new(|| RAW_STATICS.iter().map(|s| (s.str, s)).collect());\n\n/// Array of raw static strings that aren't reference counted.\nconst RAW_STATICS: &[StaticString] = &[\n    StaticString::new(JsStr::latin1(\"\".as_bytes())),\n    // Well known symbols\n    StaticString::new(JsStr::latin1(\"Symbol.asyncIterator\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"[Symbol.asyncIterator]\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"Symbol.hasInstance\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"[Symbol.hasInstance]\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"Symbol.isConcatSpreadable\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"[Symbol.isConcatSpreadable]\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"Symbol.iterator\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"[Symbol.iterator]\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"Symbol.match\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"[Symbol.match]\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"Symbol.matchAll\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"[Symbol.matchAll]\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"Symbol.replace\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"[Symbol.replace]\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"Symbol.search\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"[Symbol.search]\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"Symbol.species\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"[Symbol.species]\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"Symbol.split\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"[Symbol.split]\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"Symbol.toPrimitive\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"[Symbol.toPrimitive]\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"Symbol.toStringTag\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"[Symbol.toStringTag]\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"Symbol.unscopables\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"[Symbol.unscopables]\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"Symbol.dispose\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"[Symbol.dispose]\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"Symbol.asyncDispose\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"[Symbol.asyncDispose]\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"get [Symbol.species]\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"get [Symbol.toStringTag]\".as_bytes())),\n    // Well known builtins\n    StaticString::new(JsStr::latin1(\"Array\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"ArrayBuffer\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"Iterator\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"SharedArrayBuffer\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"AsyncFunction\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"AsyncGenerator\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"AsyncGeneratorFunction\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"Atomics\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"BigInt\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"Boolean\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"DataView\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"Date\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"Error\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"AggregateError\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"EvalError\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"RangeError\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"ReferenceError\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"SyntaxError\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"TypeError\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"URIError\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"escape\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"unescape\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"eval\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"Function\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"Generator\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"GeneratorFunction\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"Intl\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"Collator\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"ListFormat\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"Locale\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"PluralRules\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"Segmenter\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"DateTimeFormat\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"JSON\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"Map\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"Math\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"Number\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"NumberFormat\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"isFinite\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"isNaN\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"parseInt\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"parseFloat\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"Object\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"Promise\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"Proxy\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"Reflect\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"RegExp\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"Set\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"String\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"Symbol\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"TypedArray\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"Int8Array\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"Uint8Array\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"Uint8ClampedArray\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"Int16Array\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"Uint16Array\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"Int32Array\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"Uint32Array\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"BigInt64Array\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"BigUint64Array\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"Float16Array\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"Float32Array\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"Float64Array\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"encodeURI\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"encodeURIComponent\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"decodeURI\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"decodeURIComponent\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"WeakRef\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"WeakMap\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"WeakSet\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"FinalizationRegistry\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"Temporal\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"Temporal.Now\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"Temporal.Instant\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"Temporal.Duration\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"Temporal.Calendar\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"Temporal.PlainDate\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"Temporal.PlainDateTime\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"Temporal.PlainMonthDay\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"Temporal.PlainYearMonth\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"Temporal.PlainTime\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"Temporal.TimeZone\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"Temporal.ZonedDateTime\".as_bytes())),\n    // Misc\n    StaticString::new(JsStr::latin1(\",\".as_bytes())),\n    StaticString::new(JsStr::latin1(\":\".as_bytes())),\n    // Generic use\n    StaticString::new(JsStr::latin1(\"name\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"length\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"arguments\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"prototype\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"constructor\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"return\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"throw\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"global\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"globalThis\".as_bytes())),\n    // typeof\n    StaticString::new(JsStr::latin1(\"null\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"undefined\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"number\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"string\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"symbol\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"bigint\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"object\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"function\".as_bytes())),\n    // Property descriptor\n    StaticString::new(JsStr::latin1(\"value\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"get\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"set\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"writable\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"enumerable\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"configurable\".as_bytes())),\n    // Object object\n    StaticString::new(JsStr::latin1(\"assign\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"create\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"toString\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"valueOf\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"is\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"seal\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"isSealed\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"freeze\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"isFrozen\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"isExtensible\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"hasOwnProperty\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"isPrototypeOf\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"setPrototypeOf\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"getPrototypeOf\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"defineProperty\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"defineProperties\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"deleteProperty\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"construct\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"hasOwn\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"ownKeys\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"keys\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"values\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"entries\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"fromEntries\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"propertyIsEnumerable\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"preventExtensions\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"getOwnPropertyDescriptor\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"getOwnPropertyDescriptors\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"getOwnPropertyNames\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"getOwnPropertySymbols\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"__defineGetter__\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"__defineSetter__\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"__lookupGetter__\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"__lookupSetter__\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"__proto__\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"get __proto__\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"set __proto__\".as_bytes())),\n    // Function object\n    StaticString::new(JsStr::latin1(\"apply\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"bind\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"call\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"caller\".as_bytes())),\n    // Arguments object\n    StaticString::new(JsStr::latin1(\"callee\".as_bytes())),\n    // Array object\n    StaticString::new(JsStr::latin1(\"at\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"from\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"isArray\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"of\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"copyWithin\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"every\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"fill\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"filter\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"find\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"findIndex\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"findLast\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"findLastIndex\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"flat\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"flatMap\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"forEach\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"includes\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"indexOf\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"join\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"map\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"next\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"reduce\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"reduceRight\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"reverse\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"shift\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"slice\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"splice\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"some\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"sort\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"unshift\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"push\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"pop\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"groupBy\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"toReversed\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"toSorted\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"toSpliced\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"with\".as_bytes())),\n    // String object\n    StaticString::new(JsStr::latin1(\"charAt\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"charCodeAt\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"codePointAt\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"concat\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"endsWith\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"fromCharCode\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"fromCodePoint\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"lastIndexOf\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"match\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"matchAll\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"normalize\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"padEnd\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"padStart\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"raw\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"repeat\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"replace\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"replaceAll\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"search\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"split\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"startsWith\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"substr\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"substring\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"toLocaleString\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"toLowerCase\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"toUpperCase\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"trim\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"trimEnd\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"trimStart\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"isWellFormed\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"localeCompare\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"toWellFormed\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"toLocaleLowerCase\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"toLocaleUpperCase\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"trimLeft\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"trimRight\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"anchor\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"big\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"blink\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"bold\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"fixed\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"fontcolor\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"fontsize\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"italics\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"link\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"small\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"strike\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"sub\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"sup\".as_bytes())),\n    // Number object\n    StaticString::new(JsStr::latin1(\"Infinity\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"NaN\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"EPSILON\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"MAX_SAFE_INTEGER\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"MIN_SAFE_INTEGER\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"MAX_VALUE\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"MIN_VALUE\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"NEGATIVE_INFINITY\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"POSITIVE_INFINITY\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"isSafeInteger\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"isInteger\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"toExponential\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"toFixed\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"toPrecision\".as_bytes())),\n    // BigInt object\n    StaticString::new(JsStr::latin1(\"asIntN\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"asUintN\".as_bytes())),\n    // RegExp object\n    StaticString::new(JsStr::latin1(\"exec\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"test\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"compile\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"flags\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"index\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"lastIndex\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"hasIndices\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"ignoreCase\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"multiline\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"dotAll\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"unicode\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"sticky\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"source\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"get hasIndices\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"get global\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"get ignoreCase\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"get multiline\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"get dotAll\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"get unicode\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"get sticky\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"get flags\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"get source\".as_bytes())),\n    // Symbol object\n    StaticString::new(JsStr::latin1(\"for\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"keyFor\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"description\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"asyncIterator\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"hasInstance\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"species\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"unscopables\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"iterator\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"toStringTag\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"toPrimitive\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"isConcatSpreadable\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"get description\".as_bytes())),\n    // Map object\n    StaticString::new(JsStr::latin1(\"clear\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"delete\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"has\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"size\".as_bytes())),\n    // Set object\n    StaticString::new(JsStr::latin1(\"add\".as_bytes())),\n    // Reflect object\n    // Proxy object\n    StaticString::new(JsStr::latin1(\"revocable\".as_bytes())),\n    // Error objects\n    StaticString::new(JsStr::latin1(\"message\".as_bytes())),\n    // Date object\n    StaticString::new(JsStr::latin1(\"toJSON\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"getDate\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"getDay\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"getFullYear\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"getHours\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"getMilliseconds\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"getMinutes\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"getMonth\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"getSeconds\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"getTime\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"getYear\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"getUTCDate\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"getUTCDay\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"getUTCFullYear\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"getUTCHours\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"getUTCMinutes\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"getUTCMonth\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"getUTCSeconds\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"setDate\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"setFullYear\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"setHours\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"setMilliseconds\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"setMinutes\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"setMonth\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"setSeconds\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"setYear\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"setTime\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"setUTCDate\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"setUTCFullYear\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"setUTCHours\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"setUTCMinutes\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"setUTCMonth\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"setUTCSeconds\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"toDateString\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"toGMTString\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"toISOString\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"toTimeString\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"toUTCString\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"now\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"UTC\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"getTimezoneOffset\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"getUTCMilliseconds\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"setUTCMilliseconds\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"toLocaleDateString\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"toLocaleTimeString\".as_bytes())),\n    // JSON object\n    StaticString::new(JsStr::latin1(\"parse\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"stringify\".as_bytes())),\n    // Promise object\n    StaticString::new(JsStr::latin1(\"promise\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"resolve\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"reject\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"all\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"allSettled\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"any\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"race\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"then\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"catch\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"finally\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"withResolvers\".as_bytes())),\n    // Iterator object\n    StaticString::new(JsStr::latin1(\"Array Iterator\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"Set Iterator\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"String Iterator\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"Map Iterator\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"For In Iterator\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"RegExp String Iterator\".as_bytes())),\n    // Iterator result object\n    StaticString::new(JsStr::latin1(\"done\".as_bytes())),\n    // Math object\n    StaticString::new(JsStr::latin1(\"LN10\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"LN2\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"LOG10E\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"LOG2E\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"PI\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"SQRT1_2\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"SQRT2\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"abs\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"acos\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"acosh\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"asin\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"asinh\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"atan\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"atanh\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"atan2\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"cbrt\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"ceil\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"clz32\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"cos\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"cosh\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"exp\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"expm1\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"floor\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"fround\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"hypot\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"imul\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"log\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"log1p\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"log10\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"log2\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"max\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"min\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"pow\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"random\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"round\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"sign\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"sin\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"sinh\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"sqrt\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"tan\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"tanh\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"trunc\".as_bytes())),\n    // TypedArray object\n    StaticString::new(JsStr::latin1(\"BYTES_PER_ELEMENT\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"buffer\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"byteLength\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"byteOffset\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"isView\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"subarray\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"get byteLength\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"get buffer\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"get byteOffset\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"get size\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"get length\".as_bytes())),\n    // DataView object\n    StaticString::new(JsStr::latin1(\"getBigInt64\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"getBigUint64\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"getFloat16\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"getFloat32\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"getFloat64\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"getInt8\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"getInt16\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"getInt32\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"getUint8\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"getUint16\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"getUint32\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"setBigInt64\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"setBigUint64\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"setFloat16\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"setFloat32\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"setFloat64\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"setInt8\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"setInt16\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"setInt32\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"setUint8\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"setUint16\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"setUint32\".as_bytes())),\n    // WeakRef object\n    StaticString::new(JsStr::latin1(\"deref\".as_bytes())),\n    // Atomic object\n    StaticString::new(JsStr::latin1(\"and\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"compareExchange\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"exchange\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"isLockFree\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"load\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"or\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"store\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"wait\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"notify\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"xor\".as_bytes())),\n    // Intl object\n    StaticString::new(JsStr::latin1(\"getCanonicalLocales\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"get compare\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"supportedLocalesOf\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"Intl.Collator\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"compare\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"resolvedOptions\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"Intl.ListFormat\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"format\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"formatToParts\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"get baseName\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"get calendar\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"get caseFirst\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"get collation\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"get hourCycle\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"get numeric\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"get numberingSystem\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"get language\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"get script\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"get region\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"Intl.Locale\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"maximize\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"minimize\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"baseName\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"calendar\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"caseFirst\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"collation\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"hourCycle\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"numeric\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"numberingSystem\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"language\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"script\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"region\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"Intl.Segmenter\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"segment\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"containing\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"Segmenter String Iterator\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"Intl.PluralRules\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"select\".as_bytes())),\n    // Temporal object\n    StaticString::new(JsStr::latin1(\"get Id\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"getOffsetNanosecondsFor\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"getOffsetStringFor\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"getPlainDateTimeFor\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"getInstantFor\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"getPossibleInstantFor\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"getNextTransition\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"getPreviousTransition\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"id\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"Now\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"Calendar\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"Duration\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"Instant\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"PlainDate\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"PlainDateTime\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"PlainMonthDay\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"PlainTime\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"PlainYearMonth\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"TimeZone\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"ZonedDateTime\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"timeZoneId\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"instant\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"plainDateTime\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"plainDateTimeISO\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"zonedDateTime\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"zonedDateTimeISO\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"plainDate\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"plainDateISO\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"get epochSeconds\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"get epochMilliseconds\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"get epochMicroseconds\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"get epochNanoseconds\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"epochSeconds\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"epochMilliseconds\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"epochMicroseconds\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"epochNanoseconds\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"subtract\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"until\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"since\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"equals\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"toZonedDateTime\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"toZonedDateTimeISO\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"get Years\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"get Months\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"get Weeks\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"get Days\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"get Hours\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"get Minutes\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"get Seconds\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"get Milliseconds\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"get Microseconds\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"get Nanoseconds\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"get Sign\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"get blank\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"years\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"months\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"weeks\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"days\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"hours\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"minutes\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"seconds\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"milliseconds\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"microseconds\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"nanoseconds\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"blank\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"negated\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"total\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"get calendarId\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"get year\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"get month\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"get monthCode\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"get day\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"get dayOfWeek\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"get dayOfYear\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"get weekOfYear\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"get yearOfWeek\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"get daysInWeek\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"get daysInMonth\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"get daysInYear\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"get monthsInYear\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"get inLeapYear\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"calendarId\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"year\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"month\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"monthCode\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"day\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"dayOfWeek\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"dayOfYear\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"weekOfYear\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"yearOfWeek\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"daysInWeek\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"daysInMonth\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"daysInYear\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"monthsInYear\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"inLeapYear\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"toPlainYearMonth\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"toPlainMonthDay\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"getISOFields\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"getCalendar\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"withCalendar\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"dateFromFields\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"yearMonthFromFields\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"monthDayFromFields\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"dateAdd\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"dateUntil\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"era\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"eraYear\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"fields\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"mergeFields\".as_bytes())),\n    // Console object\n    StaticString::new(JsStr::latin1(\"console\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"assert\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"debug\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"error\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"info\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"trace\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"warn\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"exception\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"count\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"countReset\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"group\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"groupCollapsed\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"groupEnd\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"time\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"timeLog\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"timeEnd\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"dir\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"dirxml\".as_bytes())),\n    // Minified name\n    StaticString::new(JsStr::latin1(\"a\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"c\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"d\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"e\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"f\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"g\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"h\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"i\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"j\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"k\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"l\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"m\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"n\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"o\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"p\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"q\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"r\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"s\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"t\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"u\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"v\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"w\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"x\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"y\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"z\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"A\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"C\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"D\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"E\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"F\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"G\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"H\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"I\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"J\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"K\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"L\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"M\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"N\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"O\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"P\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"Q\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"R\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"S\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"T\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"U\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"V\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"W\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"X\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"Y\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"Z\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"_\".as_bytes())),\n    StaticString::new(JsStr::latin1(\"$\".as_bytes())),\n];\n"
  },
  {
    "path": "core/string/src/display.rs",
    "content": "//! Display implementations for [`JsString`].\n\nuse crate::{CodePoint, JsStr, JsStrVariant, JsString, JsStringKind, SliceString};\nuse std::cell::RefCell;\nuse std::fmt;\nuse std::fmt::Write;\n\n/// `Display` implementation for [`JsString`] that escapes unicode characters.\n// This should not implement debug, only be shown as a standard display.\n#[allow(missing_debug_implementations)]\npub struct JsStrDisplayEscaped<'a> {\n    inner: &'a JsString,\n}\n\nimpl fmt::Display for JsStrDisplayEscaped<'_> {\n    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {\n        match self.inner.variant() {\n            // SAFETY: `JsStrVariant::Latin1` does not contain any unpaired surrogates, so need to check.\n            JsStrVariant::Latin1(v) => v\n                .iter()\n                .copied()\n                .map(char::from)\n                .try_for_each(|c| f.write_char(c)),\n            JsStrVariant::Utf16(_) => self.inner.code_points().try_for_each(|r| match r {\n                CodePoint::Unicode(c) => f.write_char(c),\n                CodePoint::UnpairedSurrogate(c) => {\n                    write!(f, \"\\\\u{c:04X}\")\n                }\n            }),\n        }\n    }\n}\n\nimpl<'a> From<&'a JsString> for JsStrDisplayEscaped<'a> {\n    fn from(inner: &'a JsString) -> Self {\n        Self { inner }\n    }\n}\n\n/// `Display` implementation for [`JsString`] that escapes unicode characters.\n// This should not implement debug, only be shown as a standard display.\n#[allow(missing_debug_implementations)]\npub struct JsStrDisplayLossy<'a> {\n    inner: JsStr<'a>,\n}\n\nimpl fmt::Display for JsStrDisplayLossy<'_> {\n    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {\n        // No need to optimize latin1.\n        self.inner\n            .code_points_lossy()\n            .try_for_each(|c| f.write_char(c))\n    }\n}\n\nimpl<'a> From<JsStr<'a>> for JsStrDisplayLossy<'a> {\n    fn from(inner: JsStr<'a>) -> Self {\n        Self { inner }\n    }\n}\n\n/// Debug displayable for [`JsString`] which shows more information than\n/// debug displaying the original string.\npub struct JsStringDebugInfo<'a> {\n    inner: &'a JsString,\n}\n\nimpl fmt::Debug for JsStringDebugInfo<'_> {\n    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {\n        let inner = self.inner;\n\n        // Show a maximum of 30 characters.\n        let s_repr = if inner.len() > 30 {\n            let it = inner\n                .code_points()\n                .map(|c| c.as_char().unwrap_or('\\u{FFFD}'));\n            it.clone()\n                .take(20)\n                .chain(\"/* ... */\".chars())\n                .chain(it.skip(inner.len() - 20))\n                .collect()\n        } else {\n            inner.display_lossy().to_string()\n        };\n\n        let dbg = RefCell::new(f.debug_struct(\"JsString\"));\n\n        dbg.borrow_mut()\n            .field(\"kind\", &inner.kind())\n            .field(\"length\", &inner.len())\n            .field(\"content\", &s_repr);\n\n        // Show kind specific fields from string.\n        match self.inner.kind() {\n            JsStringKind::Latin1Sequence | JsStringKind::Utf16Sequence => {\n                if let Some(rc) = self.inner.refcount() {\n                    dbg.borrow_mut().field(\"refcount\", &rc);\n                }\n            }\n            JsStringKind::Slice => {\n                // SAFETY: Just verified the kind.\n                let slice: &SliceString = unsafe { self.inner.as_inner() };\n                dbg.borrow_mut()\n                    .field(\"original\", &slice.owned().debug_info());\n            }\n            JsStringKind::Static => {}\n        }\n\n        dbg.borrow_mut().finish()\n    }\n}\n\nimpl<'a> From<&'a JsString> for JsStringDebugInfo<'a> {\n    fn from(inner: &'a JsString) -> Self {\n        Self { inner }\n    }\n}\n\n#[test]\nfn latin1() {\n    // 0xE9 is `é` in ISO-8859-1 (see https://www.ascii-code.com/ISO-8859-1).\n    let s = JsString::from(\"Hello \\u{E9} world!\");\n\n    let rust_str = format!(\"{}\", JsStrDisplayEscaped { inner: &s });\n    assert_eq!(rust_str, \"Hello é world!\");\n\n    let rust_str = format!(\"{}\", JsStrDisplayLossy { inner: s.as_str() });\n    assert_eq!(rust_str, \"Hello é world!\");\n}\n\n#[test]\nfn emoji() {\n    // 0x1F600 is `😀` (see https://www.fileformat.info/info/unicode/char/1f600/index.htm).\n    let s = JsString::from(&[0xD83D, 0xDE00]);\n\n    let rust_str = format!(\"{}\", JsStrDisplayEscaped { inner: &s });\n    assert_eq!(rust_str, \"😀\");\n\n    let rust_str = format!(\"{}\", JsStrDisplayLossy { inner: s.as_str() });\n    assert_eq!(rust_str, \"😀\");\n}\n\n#[test]\nfn unpaired_surrogates() {\n    // 0xD800 is an unpaired surrogate (see https://www.fileformat.info/info/unicode/char/d800/index.htm).\n    let s = JsString::from(&[0xD800]);\n\n    let rust_str = format!(\"{}\", JsStrDisplayEscaped { inner: &s });\n    assert_eq!(rust_str, \"\\\\uD800\");\n\n    let rust_str = format!(\"{}\", JsStrDisplayLossy { inner: s.as_str() });\n    assert_eq!(rust_str, \"�\");\n}\n"
  },
  {
    "path": "core/string/src/iter.rs",
    "content": "use std::iter::FusedIterator;\n\nuse crate::{CodePoint, JsStr};\n\nuse super::JsStrVariant;\n\n#[derive(Debug, Clone)]\nenum IterInner<'a> {\n    U8(std::iter::Copied<std::slice::Iter<'a, u8>>),\n    U16(std::iter::Copied<std::slice::Iter<'a, u16>>),\n}\n\n/// Iterator over a [`JsStr`].\n#[derive(Debug, Clone)]\npub struct Iter<'a> {\n    inner: IterInner<'a>,\n}\n\nimpl<'a> Iter<'a> {\n    #[inline]\n    pub(crate) fn new(s: JsStr<'a>) -> Self {\n        let inner = match s.variant() {\n            JsStrVariant::Latin1(s) => IterInner::U8(s.iter().copied()),\n            JsStrVariant::Utf16(s) => IterInner::U16(s.iter().copied()),\n        };\n        Iter { inner }\n    }\n}\n\nimpl Iterator for Iter<'_> {\n    type Item = u16;\n\n    #[inline]\n    fn next(&mut self) -> Option<Self::Item> {\n        match &mut self.inner {\n            IterInner::U8(iter) => iter.map(u16::from).next(),\n            IterInner::U16(iter) => iter.next(),\n        }\n    }\n}\n\nimpl FusedIterator for Iter<'_> {}\n\nimpl ExactSizeIterator for Iter<'_> {\n    #[inline]\n    fn len(&self) -> usize {\n        match &self.inner {\n            IterInner::U8(v) => v.len(),\n            IterInner::U16(v) => v.len(),\n        }\n    }\n}\n\n#[derive(Debug, Clone)]\nenum WindowsInner<'a> {\n    U8(std::slice::Windows<'a, u8>),\n    U16(std::slice::Windows<'a, u16>),\n}\n\n/// An iterator over overlapping subslices of length size.\n///\n/// This struct is created by the `windows` method.\n#[derive(Debug, Clone)]\npub struct Windows<'a> {\n    inner: WindowsInner<'a>,\n}\n\nimpl<'a> Windows<'a> {\n    #[inline]\n    pub(crate) fn new(string: JsStr<'a>, size: usize) -> Self {\n        let inner = match string.variant() {\n            JsStrVariant::Latin1(v) => WindowsInner::U8(v.windows(size)),\n            JsStrVariant::Utf16(v) => WindowsInner::U16(v.windows(size)),\n        };\n        Self { inner }\n    }\n}\n\nimpl<'a> Iterator for Windows<'a> {\n    type Item = JsStr<'a>;\n\n    #[inline]\n    fn next(&mut self) -> Option<Self::Item> {\n        match &mut self.inner {\n            WindowsInner::U8(iter) => iter.next().map(JsStr::latin1),\n            WindowsInner::U16(iter) => iter.next().map(JsStr::utf16),\n        }\n    }\n}\n\nimpl FusedIterator for Windows<'_> {}\n\nimpl ExactSizeIterator for Windows<'_> {\n    #[inline]\n    fn len(&self) -> usize {\n        match &self.inner {\n            WindowsInner::U8(v) => v.len(),\n            WindowsInner::U16(v) => v.len(),\n        }\n    }\n}\n\n#[derive(Debug, Clone)]\nenum CodePointsIterInner<'a> {\n    Latin1(std::iter::Copied<std::slice::Iter<'a, u8>>),\n    Utf16(std::char::DecodeUtf16<std::iter::Copied<std::slice::Iter<'a, u16>>>),\n}\n\n#[derive(Debug, Clone)]\npub struct CodePointsIter<'a> {\n    inner: CodePointsIterInner<'a>,\n}\n\nimpl<'a> CodePointsIter<'a> {\n    #[inline]\n    pub(crate) fn new(s: JsStr<'a>) -> Self {\n        let inner = match s.variant() {\n            JsStrVariant::Latin1(s) => CodePointsIterInner::Latin1(s.iter().copied()),\n            JsStrVariant::Utf16(s) => {\n                CodePointsIterInner::Utf16(char::decode_utf16(s.iter().copied()))\n            }\n        };\n        CodePointsIter { inner }\n    }\n}\n\nimpl Iterator for CodePointsIter<'_> {\n    type Item = CodePoint;\n\n    #[inline]\n    fn next(&mut self) -> Option<Self::Item> {\n        match &mut self.inner {\n            CodePointsIterInner::Latin1(iter) => {\n                iter.next().map(|b| CodePoint::Unicode(char::from(b)))\n            }\n            CodePointsIterInner::Utf16(iter) => iter.next().map(|res| match res {\n                Ok(c) => CodePoint::Unicode(c),\n                Err(e) => CodePoint::UnpairedSurrogate(e.unpaired_surrogate()),\n            }),\n        }\n    }\n}\n\nimpl FusedIterator for CodePointsIter<'_> {}\n"
  },
  {
    "path": "core/string/src/lib.rs",
    "content": "//! A Latin1 or UTF-16 encoded, reference counted, immutable string.\n\n// Required per unsafe code standards to ensure every unsafe usage is properly documented.\n// - `unsafe_op_in_unsafe_fn` will be warn-by-default in edition 2024:\n//   https://github.com/rust-lang/rust/issues/71668#issuecomment-1189396860\n// - `undocumented_unsafe_blocks` and `missing_safety_doc` requires a `Safety:` section in the\n//   comment or doc of the unsafe block or function, respectively.\n#![deny(\n    unsafe_op_in_unsafe_fn,\n    clippy::undocumented_unsafe_blocks,\n    clippy::missing_safety_doc\n)]\n#![allow(clippy::module_name_repetitions)]\n\nmod builder;\nmod code_point;\nmod common;\nmod display;\nmod iter;\nmod str;\nmod r#type;\nmod vtable;\n\n#[cfg(test)]\nmod tests;\n\nuse self::iter::Windows;\nuse crate::display::{JsStrDisplayEscaped, JsStrDisplayLossy, JsStringDebugInfo};\nuse crate::iter::CodePointsIter;\nuse crate::r#type::{Latin1, Utf16};\npub use crate::vtable::StaticString;\nuse crate::vtable::{SequenceString, SliceString};\n#[doc(inline)]\npub use crate::{\n    builder::{CommonJsStringBuilder, Latin1JsStringBuilder, Utf16JsStringBuilder},\n    code_point::CodePoint,\n    common::StaticJsStrings,\n    iter::Iter,\n    str::{JsStr, JsStrVariant},\n};\nuse std::marker::PhantomData;\nuse std::{borrow::Cow, mem::ManuallyDrop};\nuse std::{\n    convert::Infallible,\n    hash::{Hash, Hasher},\n    ptr::{self, NonNull},\n    str::FromStr,\n};\nuse vtable::JsStringVTable;\n\nfn alloc_overflow() -> ! {\n    panic!(\"detected overflow during string allocation\")\n}\n\n/// Helper function to check if a `char` is trimmable.\npub(crate) const fn is_trimmable_whitespace(c: char) -> bool {\n    // The rust implementation of `trim` does not regard the same characters whitespace as\n    // ecma standard does.\n    //\n    // Rust uses \\p{White_Space} by default, which also includes:\n    // `\\u{0085}' (next line)\n    // And does not include:\n    // '\\u{FEFF}' (zero width non-breaking space)\n    // Explicit whitespace: https://tc39.es/ecma262/#sec-white-space\n    matches!(\n        c,\n        '\\u{0009}' | '\\u{000B}' | '\\u{000C}' | '\\u{0020}' | '\\u{00A0}' | '\\u{FEFF}' |\n    // Unicode Space_Separator category\n    '\\u{1680}' | '\\u{2000}'\n            ..='\\u{200A}' | '\\u{202F}' | '\\u{205F}' | '\\u{3000}' |\n    // Line terminators: https://tc39.es/ecma262/#sec-line-terminators\n    '\\u{000A}' | '\\u{000D}' | '\\u{2028}' | '\\u{2029}'\n    )\n}\n\n/// Helper function to check if a `u8` latin1 character is trimmable.\npub(crate) const fn is_trimmable_whitespace_latin1(c: u8) -> bool {\n    // The rust implementation of `trim` does not regard the same characters whitespace as\n    // ecma standard does.\n    //\n    // Rust uses \\p{White_Space} by default, which also includes:\n    // `\\u{0085}' (next line)\n    // And does not include:\n    // '\\u{FEFF}' (zero width non-breaking space)\n    // Explicit whitespace: https://tc39.es/ecma262/#sec-white-space\n    matches!(\n        c,\n        0x09 | 0x0B | 0x0C | 0x20 | 0xA0 |\n        // Line terminators: https://tc39.es/ecma262/#sec-line-terminators\n        0x0A | 0x0D\n    )\n}\n\n/// Opaque type of a raw string pointer.\n#[allow(missing_copy_implementations, missing_debug_implementations)]\npub struct RawJsString {\n    // Make this non-send, non-sync, invariant and unconstructable.\n    phantom_data: PhantomData<*mut ()>,\n}\n\n/// Strings can be represented internally by multiple kinds. This is used to identify\n/// the storage kind of string.\n#[derive(Debug, Clone, Copy, Eq, PartialEq)]\n#[repr(u8)]\npub(crate) enum JsStringKind {\n    /// A sequential memory slice of Latin1 bytes. See [`SequenceString`].\n    Latin1Sequence = 0,\n\n    /// A sequential memory slice of UTF-16 code units. See [`SequenceString`].\n    Utf16Sequence = 1,\n\n    /// A slice of an existing string. See [`SliceString`].\n    Slice = 2,\n\n    /// A static string that is valid for `'static` lifetime.\n    Static = 3,\n}\n\n/// A Latin1 or UTF-16–encoded, reference counted, immutable string.\n///\n/// This is pretty similar to a <code>[Rc][std::rc::Rc]\\<[\\[u16\\]][slice]\\></code>, but without the\n/// length metadata associated with the `Rc` fat pointer. Instead, the length of every string is\n/// stored on the heap, along with its reference counter and its data.\n///\n/// The string can be latin1 (stored as a byte for space efficiency) or U16 encoding.\n///\n/// We define some commonly used string constants in an interner. For these strings, we don't allocate\n/// memory on the heap to reduce the overhead of memory allocation and reference counting.\n///\n/// # Internal representation\n///\n/// The `ptr` field always points to a structure whose first field is a `JsStringVTable`.\n/// This enables uniform vtable dispatch for all string operations without branching.\n///\n/// Because we ensure this invariant at every construction, we can directly point to this\n/// type to allow for better optimization (and simpler code).\n#[allow(clippy::module_name_repetitions)]\npub struct JsString {\n    /// Pointer to the string data. Always points to a struct whose first field is\n    /// `JsStringVTable`.\n    ptr: NonNull<JsStringVTable>,\n}\n\n// `JsString` should always be thin-pointer sized.\nstatic_assertions::assert_eq_size!(JsString, *const ());\n\nimpl<'a> From<&'a JsString> for JsStr<'a> {\n    #[inline]\n    fn from(value: &'a JsString) -> Self {\n        value.as_str()\n    }\n}\n\nimpl<'a> IntoIterator for &'a JsString {\n    type Item = u16;\n    type IntoIter = Iter<'a>;\n\n    #[inline]\n    fn into_iter(self) -> Self::IntoIter {\n        self.iter()\n    }\n}\n\nimpl JsString {\n    /// Create an iterator over the [`JsString`].\n    #[inline]\n    #[must_use]\n    pub fn iter(&self) -> Iter<'_> {\n        self.as_str().iter()\n    }\n\n    /// Create an iterator over overlapping subslices of length size.\n    #[inline]\n    #[must_use]\n    pub fn windows(&self, size: usize) -> Windows<'_> {\n        self.as_str().windows(size)\n    }\n\n    /// Decodes a [`JsString`] into a [`String`], replacing invalid data with its escaped representation\n    /// in 4 digit hexadecimal.\n    #[inline]\n    #[must_use]\n    pub fn to_std_string_escaped(&self) -> String {\n        self.display_escaped().to_string()\n    }\n\n    /// Decodes a [`JsString`] into a [`String`], replacing invalid data with the\n    /// replacement character U+FFFD.\n    #[inline]\n    #[must_use]\n    pub fn to_std_string_lossy(&self) -> String {\n        self.display_lossy().to_string()\n    }\n\n    /// Decodes a [`JsString`] into a [`String`], returning an error if the string contains unpaired\n    /// surrogates.\n    ///\n    /// # Errors\n    ///\n    /// [`FromUtf16Error`][std::string::FromUtf16Error] if it contains any invalid data.\n    #[inline]\n    pub fn to_std_string(&self) -> Result<String, std::string::FromUtf16Error> {\n        self.as_str().to_std_string()\n    }\n\n    /// Decodes a [`JsString`] into an iterator of [`Result<String, u16>`], returning surrogates as\n    /// errors.\n    #[inline]\n    #[allow(clippy::missing_panics_doc)]\n    pub fn to_std_string_with_surrogates(\n        &self,\n    ) -> impl Iterator<Item = Result<String, u16>> + use<'_> {\n        let mut iter = self.code_points().peekable();\n\n        std::iter::from_fn(move || {\n            let cp = iter.next()?;\n            let char = match cp {\n                CodePoint::Unicode(c) => c,\n                CodePoint::UnpairedSurrogate(surr) => return Some(Err(surr)),\n            };\n\n            let mut string = String::from(char);\n\n            loop {\n                let Some(cp) = iter.peek().and_then(|cp| match cp {\n                    CodePoint::Unicode(c) => Some(*c),\n                    CodePoint::UnpairedSurrogate(_) => None,\n                }) else {\n                    break;\n                };\n\n                string.push(cp);\n\n                iter.next().expect(\"should exist by the check above\");\n            }\n\n            Some(Ok(string))\n        })\n    }\n\n    /// Maps the valid segments of an UTF16 string and leaves the unpaired surrogates unchanged.\n    #[inline]\n    #[must_use]\n    pub fn map_valid_segments<F>(&self, mut f: F) -> Self\n    where\n        F: FnMut(String) -> String,\n    {\n        let mut text = Vec::new();\n\n        for part in self.to_std_string_with_surrogates() {\n            match part {\n                Ok(string) => text.extend(f(string).encode_utf16()),\n                Err(surr) => text.push(surr),\n            }\n        }\n\n        Self::from(&text[..])\n    }\n\n    /// Gets an iterator of all the Unicode codepoints of a [`JsString`].\n    #[inline]\n    #[must_use]\n    pub fn code_points(&self) -> CodePointsIter<'_> {\n        (self.vtable().code_points)(self.ptr)\n    }\n\n    /// Get the variant of this string.\n    #[inline]\n    #[must_use]\n    pub fn variant(&self) -> JsStrVariant<'_> {\n        self.as_str().variant()\n    }\n\n    /// Abstract operation `StringIndexOf ( string, searchValue, fromIndex )`\n    ///\n    /// Note: Instead of returning an isize with `-1` as the \"not found\" value, we make use of the\n    /// type system and return <code>[Option]\\<usize\\></code> with [`None`] as the \"not found\" value.\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-stringindexof\n    #[inline]\n    #[must_use]\n    pub fn index_of(&self, search_value: JsStr<'_>, from_index: usize) -> Option<usize> {\n        self.as_str().index_of(search_value, from_index)\n    }\n\n    /// Abstract operation `CodePointAt( string, position )`.\n    ///\n    /// The abstract operation `CodePointAt` takes arguments `string` (a String) and `position` (a\n    /// non-negative integer) and returns a Record with fields `[[CodePoint]]` (a code point),\n    /// `[[CodeUnitCount]]` (a positive integer), and `[[IsUnpairedSurrogate]]` (a Boolean). It\n    /// interprets string as a sequence of UTF-16 encoded code points, as described in 6.1.4, and reads\n    /// from it a single code point starting with the code unit at index `position`.\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-codepointat\n    ///\n    /// # Panics\n    ///\n    /// If `position` is smaller than size of string.\n    #[inline]\n    #[must_use]\n    pub fn code_point_at(&self, position: usize) -> CodePoint {\n        self.as_str().code_point_at(position)\n    }\n\n    /// Abstract operation `StringToNumber ( str )`\n    ///\n    /// More information:\n    /// - [ECMAScript reference][spec]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-stringtonumber\n    #[inline]\n    #[must_use]\n    pub fn to_number(&self) -> f64 {\n        self.as_str().to_number()\n    }\n\n    /// Get the length of the [`JsString`].\n    #[inline]\n    #[must_use]\n    pub fn len(&self) -> usize {\n        self.vtable().len\n    }\n\n    /// Return true if the [`JsString`] is empty.\n    #[inline]\n    #[must_use]\n    pub fn is_empty(&self) -> bool {\n        self.len() == 0\n    }\n\n    /// Convert the [`JsString`] into a [`Vec<U16>`].\n    #[inline]\n    #[must_use]\n    pub fn to_vec(&self) -> Vec<u16> {\n        self.as_str().to_vec()\n    }\n\n    /// Check if the [`JsString`] contains a byte.\n    #[inline]\n    #[must_use]\n    pub fn contains(&self, element: u8) -> bool {\n        self.as_str().contains(element)\n    }\n\n    /// Trim whitespace from the start and end of the [`JsString`].\n    #[inline]\n    #[must_use]\n    pub fn trim(&self) -> JsString {\n        // Calculate both bounds directly to avoid intermediate allocations.\n        let (start, end) = match self.variant() {\n            JsStrVariant::Latin1(v) => {\n                let Some(start) = v.iter().position(|c| !is_trimmable_whitespace_latin1(*c)) else {\n                    return StaticJsStrings::EMPTY_STRING;\n                };\n                let end = v\n                    .iter()\n                    .rposition(|c| !is_trimmable_whitespace_latin1(*c))\n                    .unwrap_or(start);\n                (start, end)\n            }\n            JsStrVariant::Utf16(v) => {\n                let Some(start) = v.iter().copied().position(|r| {\n                    !char::from_u32(u32::from(r)).is_some_and(is_trimmable_whitespace)\n                }) else {\n                    return StaticJsStrings::EMPTY_STRING;\n                };\n                let end = v\n                    .iter()\n                    .copied()\n                    .rposition(|r| {\n                        !char::from_u32(u32::from(r)).is_some_and(is_trimmable_whitespace)\n                    })\n                    .unwrap_or(start);\n                (start, end)\n            }\n        };\n\n        // SAFETY: `position(...)` and `rposition(...)` cannot exceed the length of the string.\n        unsafe { Self::slice_unchecked(self, start, end + 1) }\n    }\n\n    /// Trim whitespace from the start of the [`JsString`].\n    #[inline]\n    #[must_use]\n    pub fn trim_start(&self) -> JsString {\n        let Some(start) = (match self.variant() {\n            JsStrVariant::Latin1(v) => v.iter().position(|c| !is_trimmable_whitespace_latin1(*c)),\n            JsStrVariant::Utf16(v) => v\n                .iter()\n                .copied()\n                .position(|r| !char::from_u32(u32::from(r)).is_some_and(is_trimmable_whitespace)),\n        }) else {\n            return StaticJsStrings::EMPTY_STRING;\n        };\n\n        // SAFETY: `position(...)` cannot exceed the length of the string.\n        unsafe { Self::slice_unchecked(self, start, self.len()) }\n    }\n\n    /// Trim whitespace from the end of the [`JsString`].\n    #[inline]\n    #[must_use]\n    pub fn trim_end(&self) -> JsString {\n        let Some(end) = (match self.variant() {\n            JsStrVariant::Latin1(v) => v.iter().rposition(|c| !is_trimmable_whitespace_latin1(*c)),\n            JsStrVariant::Utf16(v) => v\n                .iter()\n                .copied()\n                .rposition(|r| !char::from_u32(u32::from(r)).is_some_and(is_trimmable_whitespace)),\n        }) else {\n            return StaticJsStrings::EMPTY_STRING;\n        };\n\n        // SAFETY: `rposition(...)` cannot exceed the length of the string. `end` is the first\n        //         character that is not trimmable, therefore we need to add 1 to it.\n        unsafe { Self::slice_unchecked(self, 0, end + 1) }\n    }\n\n    /// Returns true if needle is a prefix of the [`JsStr`].\n    #[inline]\n    #[must_use]\n    // We check the size, so this should never panic.\n    #[allow(clippy::missing_panics_doc)]\n    pub fn starts_with(&self, needle: JsStr<'_>) -> bool {\n        self.as_str().starts_with(needle)\n    }\n\n    /// Returns `true` if `needle` is a suffix of the [`JsStr`].\n    #[inline]\n    #[must_use]\n    // We check the size, so this should never panic.\n    #[allow(clippy::missing_panics_doc)]\n    pub fn ends_with(&self, needle: JsStr<'_>) -> bool {\n        self.as_str().starts_with(needle)\n    }\n\n    /// Get the `u16` code unit at index. This does not parse any characters if there\n    /// are pairs, it is simply the index of the `u16` elements.\n    #[inline]\n    #[must_use]\n    pub fn code_unit_at(&self, index: usize) -> Option<u16> {\n        self.as_str().get(index)\n    }\n\n    /// Get the element at the given index, or [`None`] if the index is out of range.\n    #[inline]\n    #[must_use]\n    pub fn get<I>(&self, index: I) -> Option<JsString>\n    where\n        I: JsStringSliceIndex,\n    {\n        index.get(self)\n    }\n\n    /// Get the element at the given index, or panic.\n    ///\n    /// # Panics\n    /// If the index returns `None`, this will panic.\n    #[inline]\n    #[must_use]\n    pub fn get_expect<I>(&self, index: I) -> JsString\n    where\n        I: JsStringSliceIndex,\n    {\n        index.get(self).expect(\"Unexpected get()\")\n    }\n\n    /// Gets a displayable escaped string. This may be faster and has fewer\n    /// allocations than `format!(\"{}\", str.to_string_escaped())` when\n    /// displaying.\n    #[inline]\n    #[must_use]\n    pub fn display_escaped(&self) -> JsStrDisplayEscaped<'_> {\n        JsStrDisplayEscaped::from(self)\n    }\n\n    /// Gets a displayable lossy string. This may be faster and has fewer\n    /// allocations than `format!(\"{}\", str.to_string_lossy())` when displaying.\n    #[inline]\n    #[must_use]\n    pub fn display_lossy(&self) -> JsStrDisplayLossy<'_> {\n        self.as_str().display_lossy()\n    }\n\n    /// Get a debug displayable info and metadata for this string.\n    #[inline]\n    #[must_use]\n    pub fn debug_info(&self) -> JsStringDebugInfo<'_> {\n        self.into()\n    }\n\n    /// Consumes the [`JsString`], returning the internal pointer.\n    ///\n    /// To avoid a memory leak the pointer must be converted back to a `JsString` using\n    /// [`JsString::from_raw`].\n    #[inline]\n    #[must_use]\n    pub fn into_raw(self) -> NonNull<RawJsString> {\n        ManuallyDrop::new(self).ptr.cast()\n    }\n\n    /// Constructs a `JsString` from the internal pointer.\n    ///\n    /// The raw pointer must have been previously returned by a call to\n    /// [`JsString::into_raw`].\n    ///\n    /// # Safety\n    ///\n    /// This function is unsafe because improper use may lead to memory unsafety,\n    /// even if the returned `JsString` is never accessed.\n    #[inline]\n    #[must_use]\n    pub const unsafe fn from_raw(ptr: NonNull<RawJsString>) -> Self {\n        Self { ptr: ptr.cast() }\n    }\n\n    /// Constructs a `JsString` from a reference to a `VTable`.\n    ///\n    /// # Safety\n    ///\n    /// This function is unsafe because improper use may lead to memory unsafety,\n    /// even if the returned `JsString` is never accessed.\n    #[inline]\n    #[must_use]\n    pub(crate) const unsafe fn from_ptr(ptr: NonNull<JsStringVTable>) -> Self {\n        Self { ptr }\n    }\n}\n\n// `&JsStr<'static>` must always be aligned so it can be tagged.\nstatic_assertions::const_assert!(align_of::<*const JsStr<'static>>() >= 2);\n\n/// Dealing with inner types.\nimpl JsString {\n    /// Check if this is a static string.\n    #[inline]\n    #[must_use]\n    pub fn is_static(&self) -> bool {\n        // Check the vtable kind tag\n        self.vtable().kind == JsStringKind::Static\n    }\n\n    /// Get the vtable for this string.\n    #[inline]\n    #[must_use]\n    const fn vtable(&self) -> &JsStringVTable {\n        // SAFETY: All JsString variants have vtable as the first field (embedded directly).\n        unsafe { self.ptr.as_ref() }\n    }\n\n    /// Create a [`JsString`] from a [`StaticString`] instance. This is assumed that the\n    /// static string referenced is available for the duration of the `JsString` instance\n    /// returned.\n    #[inline]\n    #[must_use]\n    pub const fn from_static(str: &'static StaticString) -> Self {\n        Self {\n            ptr: NonNull::from_ref(str).cast(),\n        }\n    }\n\n    /// Create a [`JsString`] from an existing `JsString` and start, end\n    /// range. `end` is 1 past the last character (or `== data.len()`\n    /// for the last character).\n    ///\n    /// # Safety\n    /// It is the responsibility of the caller to ensure:\n    ///   - `start` <= `end`. If `start` == `end`, the string is empty.\n    ///   - `end` <= `data.len()`.\n    #[inline]\n    #[must_use]\n    pub unsafe fn slice_unchecked(data: &JsString, start: usize, end: usize) -> Self {\n        // Safety: invariant stated by this whole function.\n        let slice = Box::new(unsafe { SliceString::new(data, start, end) });\n\n        Self {\n            ptr: NonNull::from(Box::leak(slice)).cast(),\n        }\n    }\n\n    /// Create a [`JsString`] from an existing `JsString` and start, end\n    /// range. Returns None if the start/end is invalid.\n    #[inline]\n    #[must_use]\n    pub fn slice(&self, p1: usize, mut p2: usize) -> JsString {\n        if p2 > self.len() {\n            p2 = self.len();\n        }\n        if p1 >= p2 {\n            StaticJsStrings::EMPTY_STRING\n        } else {\n            // SAFETY: We just checked the conditions.\n            unsafe { Self::slice_unchecked(self, p1, p2) }\n        }\n    }\n\n    /// Get the kind of this string (for debugging/introspection).\n    #[inline]\n    #[must_use]\n    pub(crate) fn kind(&self) -> JsStringKind {\n        self.vtable().kind\n    }\n\n    /// Get the inner pointer as a reference of type T.\n    ///\n    /// # Safety\n    /// This should only be used when the inner type has been validated via `kind()`.\n    /// Using an unvalidated inner type is undefined behaviour.\n    #[inline]\n    pub(crate) unsafe fn as_inner<T>(&self) -> &T {\n        // SAFETY: Caller must ensure the type matches.\n        unsafe { self.ptr.cast::<T>().as_ref() }\n    }\n}\n\nimpl JsString {\n    /// Obtains the underlying [`&[u16]`][slice] slice of a [`JsString`]\n    #[inline]\n    #[must_use]\n    pub fn as_str(&self) -> JsStr<'_> {\n        (self.vtable().as_str)(self.ptr)\n    }\n\n    /// Creates a new [`JsString`] from the concatenation of `x` and `y`.\n    #[inline]\n    #[must_use]\n    pub fn concat(x: JsStr<'_>, y: JsStr<'_>) -> Self {\n        Self::concat_array(&[x, y])\n    }\n\n    /// Creates a new [`JsString`] from the concatenation of every element of\n    /// `strings`.\n    #[inline]\n    #[must_use]\n    pub fn concat_array(strings: &[JsStr<'_>]) -> Self {\n        let mut latin1_encoding = true;\n        let mut full_count = 0usize;\n        for string in strings {\n            let Some(sum) = full_count.checked_add(string.len()) else {\n                alloc_overflow()\n            };\n            if !string.is_latin1() {\n                latin1_encoding = false;\n            }\n            full_count = sum;\n        }\n\n        let (ptr, data_offset) = if latin1_encoding {\n            let p = SequenceString::<Latin1>::allocate(full_count);\n            (p.cast::<u8>(), size_of::<SequenceString<Latin1>>())\n        } else {\n            let p = SequenceString::<Utf16>::allocate(full_count);\n            (p.cast::<u8>(), size_of::<SequenceString<Utf16>>())\n        };\n\n        let string = {\n            // SAFETY: `allocate_*_seq` guarantees that `ptr` is a valid pointer to a sequence string.\n            let mut data = unsafe {\n                let seq_ptr = ptr.as_ptr();\n                seq_ptr.add(data_offset)\n            };\n            for &string in strings {\n                // SAFETY:\n                // The sum of all `count` for each `string` equals `full_count`, and since we're\n                // iteratively writing each of them to `data`, `copy_non_overlapping` always stays\n                // in-bounds for `count` reads of each string and `full_count` writes to `data`.\n                //\n                // Each `string` must be properly aligned to be a valid slice, and `data` must be\n                // properly aligned by `allocate_seq`.\n                //\n                // `allocate_seq` must return a valid pointer to newly allocated memory, meaning\n                // `ptr` and all `string`s should never overlap.\n                unsafe {\n                    // NOTE: The alignment is checked when we allocate the array.\n                    #[allow(clippy::cast_ptr_alignment)]\n                    match (latin1_encoding, string.variant()) {\n                        (true, JsStrVariant::Latin1(s)) => {\n                            let count = s.len();\n                            ptr::copy_nonoverlapping(s.as_ptr(), data.cast::<u8>(), count);\n                            data = data.cast::<u8>().add(count).cast::<u8>();\n                        }\n                        (false, JsStrVariant::Latin1(s)) => {\n                            let count = s.len();\n                            for (i, byte) in s.iter().enumerate() {\n                                *data.cast::<u16>().add(i) = u16::from(*byte);\n                            }\n                            data = data.cast::<u16>().add(count).cast::<u8>();\n                        }\n                        (false, JsStrVariant::Utf16(s)) => {\n                            let count = s.len();\n                            ptr::copy_nonoverlapping(s.as_ptr(), data.cast::<u16>(), count);\n                            data = data.cast::<u16>().add(count).cast::<u8>();\n                        }\n                        (true, JsStrVariant::Utf16(_)) => {\n                            unreachable!(\"Already checked that it's latin1 encoding\")\n                        }\n                    }\n                }\n            }\n\n            Self { ptr: ptr.cast() }\n        };\n\n        StaticJsStrings::get_string(&string.as_str()).unwrap_or(string)\n    }\n\n    /// Creates a new [`JsString`] from `data`, without checking if the string is in the interner.\n    fn from_slice_skip_interning(string: JsStr<'_>) -> Self {\n        let count = string.len();\n\n        // SAFETY:\n        // - We read `count = data.len()` elements from `data`, which is within the bounds of the slice.\n        // - `allocate_*_seq` must allocate at least `count` elements, which allows us to safely\n        //   write at least `count` elements.\n        // - `allocate_*_seq` should already take care of the alignment of `ptr`, and `data` must be\n        //   aligned to be a valid slice.\n        // - `allocate_*_seq` must return a valid pointer to newly allocated memory, meaning `ptr`\n        //   and `data` should never overlap.\n        unsafe {\n            // NOTE: The alignment is checked when we allocate the array.\n            #[allow(clippy::cast_ptr_alignment)]\n            match string.variant() {\n                JsStrVariant::Latin1(s) => {\n                    let ptr = SequenceString::<Latin1>::allocate(count);\n                    let data = (&raw mut (*ptr.as_ptr()).data)\n                        .cast::<<Latin1 as r#type::StringType>::Byte>();\n                    ptr::copy_nonoverlapping(s.as_ptr(), data, count);\n                    Self { ptr: ptr.cast() }\n                }\n                JsStrVariant::Utf16(s) => {\n                    let ptr = SequenceString::<Utf16>::allocate(count);\n                    let data = (&raw mut (*ptr.as_ptr()).data)\n                        .cast::<<Utf16 as r#type::StringType>::Byte>();\n                    ptr::copy_nonoverlapping(s.as_ptr(), data, count);\n                    Self { ptr: ptr.cast() }\n                }\n            }\n        }\n    }\n\n    /// Creates a new [`JsString`] from `data`.\n    fn from_js_str(string: JsStr<'_>) -> Self {\n        if let Some(s) = StaticJsStrings::get_string(&string) {\n            return s;\n        }\n        Self::from_slice_skip_interning(string)\n    }\n\n    /// Gets the number of `JsString`s which point to this allocation.\n    #[inline]\n    #[must_use]\n    pub fn refcount(&self) -> Option<usize> {\n        (self.vtable().refcount)(self.ptr)\n    }\n}\n\nimpl Clone for JsString {\n    #[inline]\n    fn clone(&self) -> Self {\n        (self.vtable().clone)(self.ptr)\n    }\n}\n\nimpl Default for JsString {\n    #[inline]\n    fn default() -> Self {\n        StaticJsStrings::EMPTY_STRING\n    }\n}\n\nimpl Drop for JsString {\n    #[inline]\n    fn drop(&mut self) {\n        (self.vtable().drop)(self.ptr);\n    }\n}\n\nimpl std::fmt::Debug for JsString {\n    #[inline]\n    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {\n        f.debug_tuple(\"JsString\")\n            .field(&self.display_escaped().to_string())\n            .finish()\n    }\n}\n\nimpl Eq for JsString {}\n\nmacro_rules! impl_from_number_for_js_string {\n    ($($module: ident => $($ty:ty),+)+) => {\n        $(\n            $(\n                impl From<$ty> for JsString {\n                    #[inline]\n                    fn from(value: $ty) -> Self {\n                        JsString::from_slice_skip_interning(JsStr::latin1(\n                            $module::Buffer::new().format(value).as_bytes(),\n                        ))\n                    }\n                }\n            )+\n        )+\n    };\n}\n\nimpl_from_number_for_js_string!(\n    itoa => i8, i16, i32, i64, i128, u8, u16, u32, u64, u128, isize, usize\n    ryu_js => f32, f64\n);\n\nimpl From<&[u16]> for JsString {\n    #[inline]\n    fn from(s: &[u16]) -> Self {\n        JsString::from_js_str(JsStr::utf16(s))\n    }\n}\n\nimpl From<&str> for JsString {\n    #[inline]\n    fn from(s: &str) -> Self {\n        if s.is_ascii() {\n            let js_str = JsStr::latin1(s.as_bytes());\n            return StaticJsStrings::get_string(&js_str)\n                .unwrap_or_else(|| JsString::from_slice_skip_interning(js_str));\n        }\n        // Non-ASCII but still Latin1-encodable (U+0080..=U+00FF): chars map 1-to-1 to u8.\n        if s.chars().all(|c| c as u32 <= 0xFF) {\n            let bytes: Vec<u8> = s.chars().map(|c| c as u8).collect();\n            let js_str = JsStr::latin1(&bytes);\n            return StaticJsStrings::get_string(&js_str)\n                .unwrap_or_else(|| JsString::from_slice_skip_interning(js_str));\n        }\n        let s = s.encode_utf16().collect::<Vec<_>>();\n        JsString::from_slice_skip_interning(JsStr::utf16(&s[..]))\n    }\n}\n\nimpl From<JsStr<'_>> for JsString {\n    #[inline]\n    fn from(value: JsStr<'_>) -> Self {\n        StaticJsStrings::get_string(&value)\n            .unwrap_or_else(|| JsString::from_slice_skip_interning(value))\n    }\n}\n\nimpl From<&[JsString]> for JsString {\n    #[inline]\n    fn from(value: &[JsString]) -> Self {\n        Self::concat_array(&value.iter().map(Self::as_str).collect::<Vec<_>>()[..])\n    }\n}\n\nimpl<const N: usize> From<&[JsString; N]> for JsString {\n    #[inline]\n    fn from(value: &[JsString; N]) -> Self {\n        Self::concat_array(&value.iter().map(Self::as_str).collect::<Vec<_>>()[..])\n    }\n}\n\nimpl From<String> for JsString {\n    #[inline]\n    fn from(s: String) -> Self {\n        Self::from(s.as_str())\n    }\n}\n\nimpl<'a> From<Cow<'a, str>> for JsString {\n    #[inline]\n    fn from(s: Cow<'a, str>) -> Self {\n        match s {\n            Cow::Borrowed(s) => s.into(),\n            Cow::Owned(s) => s.into(),\n        }\n    }\n}\n\nimpl<const N: usize> From<&[u16; N]> for JsString {\n    #[inline]\n    fn from(s: &[u16; N]) -> Self {\n        Self::from(&s[..])\n    }\n}\n\nimpl Hash for JsString {\n    #[inline]\n    fn hash<H: Hasher>(&self, state: &mut H) {\n        self.as_str().hash(state);\n    }\n}\n\nimpl PartialOrd for JsStr<'_> {\n    #[inline]\n    fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {\n        Some(self.cmp(other))\n    }\n}\n\nimpl Ord for JsString {\n    #[inline]\n    fn cmp(&self, other: &Self) -> std::cmp::Ordering {\n        self.as_str().cmp(&other.as_str())\n    }\n}\n\nimpl PartialEq for JsString {\n    #[inline]\n    fn eq(&self, other: &Self) -> bool {\n        self.as_str() == other.as_str()\n    }\n}\n\nimpl PartialEq<JsString> for [u16] {\n    #[inline]\n    fn eq(&self, other: &JsString) -> bool {\n        if self.len() != other.len() {\n            return false;\n        }\n        for (x, y) in self.iter().copied().zip(other.iter()) {\n            if x != y {\n                return false;\n            }\n        }\n        true\n    }\n}\n\nimpl<const N: usize> PartialEq<JsString> for [u16; N] {\n    #[inline]\n    fn eq(&self, other: &JsString) -> bool {\n        self[..] == *other\n    }\n}\n\nimpl PartialEq<[u16]> for JsString {\n    #[inline]\n    fn eq(&self, other: &[u16]) -> bool {\n        other == self\n    }\n}\n\nimpl<const N: usize> PartialEq<[u16; N]> for JsString {\n    #[inline]\n    fn eq(&self, other: &[u16; N]) -> bool {\n        *self == other[..]\n    }\n}\n\nimpl PartialEq<str> for JsString {\n    #[inline]\n    fn eq(&self, other: &str) -> bool {\n        self.as_str() == other\n    }\n}\n\nimpl PartialEq<&str> for JsString {\n    #[inline]\n    fn eq(&self, other: &&str) -> bool {\n        self.as_str() == *other\n    }\n}\n\nimpl PartialEq<JsString> for str {\n    #[inline]\n    fn eq(&self, other: &JsString) -> bool {\n        other == self\n    }\n}\n\nimpl PartialEq<JsStr<'_>> for JsString {\n    #[inline]\n    fn eq(&self, other: &JsStr<'_>) -> bool {\n        self.as_str() == *other\n    }\n}\n\nimpl PartialEq<JsString> for JsStr<'_> {\n    #[inline]\n    fn eq(&self, other: &JsString) -> bool {\n        other == self\n    }\n}\n\nimpl PartialOrd for JsString {\n    #[inline]\n    fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {\n        Some(self.cmp(other))\n    }\n}\n\nimpl FromStr for JsString {\n    type Err = Infallible;\n\n    #[inline]\n    fn from_str(s: &str) -> Result<Self, Self::Err> {\n        Ok(Self::from(s))\n    }\n}\n\n/// Similar to [`std::ops::RangeBounds`] but custom implemented for getting direct indices.\n// TODO: remove [`str::JsSliceIndex`] and rename this when `JsStr` is no more.\npub trait JsStringSliceIndex {\n    /// Get the substring (or `None` if outside the string).\n    fn get(self, str: &JsString) -> Option<JsString>;\n}\n\nmacro_rules! impl_js_string_slice_index {\n    ($($type:ty),+ $(,)?) => {\n        $(\n        impl JsStringSliceIndex for $type {\n            fn get(self, str: &JsString) -> Option<JsString> {\n                let start = match std::ops::RangeBounds::<usize>::start_bound(&self) {\n                    std::ops::Bound::Included(start) => *start,\n                    std::ops::Bound::Excluded(start) => *start + 1,\n                    std::ops::Bound::Unbounded => 0,\n                };\n\n                let end = match std::ops::RangeBounds::<usize>::end_bound(&self) {\n                    std::ops::Bound::Included(end) => *end + 1,\n                    std::ops::Bound::Excluded(end) => *end,\n                    std::ops::Bound::Unbounded => str.len(),\n                };\n\n                if end > str.len() || start > end {\n                    None\n                } else {\n                    // SAFETY: we just checked the indices.\n                    Some(unsafe { JsString::slice_unchecked(str, start, end) })\n                }\n            }\n        }\n        )+\n    };\n}\n\nimpl_js_string_slice_index!(\n    std::ops::Range<usize>,\n    std::ops::RangeInclusive<usize>,\n    std::ops::RangeTo<usize>,\n    std::ops::RangeToInclusive<usize>,\n    std::ops::RangeFrom<usize>,\n    std::ops::RangeFull,\n);\n"
  },
  {
    "path": "core/string/src/str.rs",
    "content": "use super::iter::{CodePointsIter, Windows};\nuse crate::{CodePoint, Iter, display::JsStrDisplayLossy, is_trimmable_whitespace};\nuse std::{\n    hash::{Hash, Hasher},\n    slice::SliceIndex,\n};\n\n/// Inner representation of a [`JsStr`].\n#[derive(Debug, Clone, Copy)]\npub enum JsStrVariant<'a> {\n    /// Latin1 string representation.\n    Latin1(&'a [u8]),\n\n    /// U16 string representation.\n    Utf16(&'a [u16]),\n}\n\nimpl JsStrVariant<'_> {\n    pub(crate) const fn len(&self) -> usize {\n        match self {\n            JsStrVariant::Latin1(data) => data.len(),\n            JsStrVariant::Utf16(data) => data.len(),\n        }\n    }\n}\n\n/// This is equivalent to Rust's `&str`.\n#[derive(Clone, Copy)]\n#[repr(align(8))]\npub struct JsStr<'a> {\n    inner: JsStrVariant<'a>,\n}\n\n// SAFETY: Inner<'_> has only immutable references to Sync types (u8/u16), so this is safe.\nunsafe impl Sync for JsStr<'_> {}\n\n// SAFETY: It's read-only, sending this reference to another thread doesn't\n//         risk data races (there’s no mutation happening), so this is safe.\nunsafe impl Send for JsStr<'_> {}\n\nimpl<'a> JsStr<'a> {\n    /// This represents an empty string.\n    pub const EMPTY: Self = Self::latin1(\"\".as_bytes());\n\n    /// Creates a [`JsStr`] from codepoints that can fit in a `u8`.\n    #[inline]\n    #[must_use]\n    pub const fn latin1(value: &'a [u8]) -> Self {\n        Self {\n            inner: JsStrVariant::Latin1(value),\n        }\n    }\n\n    /// Creates a [`JsStr`] from utf16 encoded string.\n    #[inline]\n    #[must_use]\n    pub const fn utf16(value: &'a [u16]) -> Self {\n        Self {\n            inner: JsStrVariant::Utf16(value),\n        }\n    }\n\n    /// Get the length of the [`JsStr`].\n    #[inline]\n    #[must_use]\n    pub const fn len(&self) -> usize {\n        self.inner.len()\n    }\n\n    /// Return the inner [`JsStrVariant`] variant of the [`JsStr`].\n    #[inline]\n    #[must_use]\n    pub const fn variant(self) -> JsStrVariant<'a> {\n        self.inner\n    }\n\n    /// Check if the [`JsStr`] is latin1 encoded.\n    #[inline]\n    #[must_use]\n    pub const fn is_latin1(&self) -> bool {\n        matches!(self.inner, JsStrVariant::Latin1(_))\n    }\n\n    /// Returns [`u8`] slice if the [`JsStr`] is latin1 encoded, otherwise [`None`].\n    #[inline]\n    #[must_use]\n    pub const fn as_latin1(&self) -> Option<&[u8]> {\n        match &self.inner {\n            JsStrVariant::Latin1(v) => Some(v),\n            JsStrVariant::Utf16(_) => None,\n        }\n    }\n\n    /// Returns the same string slice but with a static reference, removing any\n    /// lifetime limits.\n    ///\n    /// # Safety\n    /// The caller is responsible to ensure the lifetime of this slice.\n    #[inline]\n    #[must_use]\n    pub unsafe fn as_static(self) -> JsStr<'static> {\n        let inner: JsStrVariant<'static> = match self.inner {\n            JsStrVariant::Latin1(v) => {\n                // SAFETY: Caller is responsible for ensuring the lifetime of this slice.\n                let static_v: &'static [u8] =\n                    unsafe { std::slice::from_raw_parts(v.as_ptr(), v.len()) };\n                JsStrVariant::<'static>::Latin1(static_v)\n            }\n            JsStrVariant::Utf16(v) => {\n                // SAFETY: Caller is responsible for ensuring the lifetime of this slice.\n                let static_v: &'static [u16] =\n                    unsafe { std::slice::from_raw_parts(v.as_ptr(), v.len()) };\n                JsStrVariant::<'static>::Utf16(static_v)\n            }\n        };\n        JsStr::<'static> { inner }\n    }\n\n    /// Iterate over the codepoints of the string.\n    #[inline]\n    #[must_use]\n    pub fn iter(self) -> Iter<'a> {\n        Iter::new(self)\n    }\n\n    /// Iterate over the codepoints of the string.\n    #[inline]\n    #[must_use]\n    pub fn windows(self, size: usize) -> Windows<'a> {\n        Windows::new(self, size)\n    }\n\n    /// Check if the [`JsStr`] is empty.\n    #[inline]\n    #[must_use]\n    pub fn is_empty(&self) -> bool {\n        self.len() == 0\n    }\n\n    /// Returns an element or subslice depending on the type of index, otherwise [`None`].\n    #[inline]\n    #[must_use]\n    pub fn get<I>(self, index: I) -> Option<I::Value>\n    where\n        I: JsSliceIndex<'a>,\n    {\n        JsSliceIndex::get(self, index)\n    }\n\n    /// Get the element at the given index.\n    ///\n    /// # Panics\n    ///\n    /// If the index is out of bounds.\n    #[inline]\n    #[must_use]\n    pub fn get_expect<I>(&self, index: I) -> I::Value\n    where\n        I: JsSliceIndex<'a>,\n    {\n        self.get(index).expect(\"Index out of bounds\")\n    }\n\n    /// Returns an element or subslice depending on the type of index, without doing bounds check.\n    ///\n    /// # Safety\n    ///\n    /// Caller must ensure the index is not out of bounds\n    #[inline]\n    #[must_use]\n    pub unsafe fn get_unchecked<I>(self, index: I) -> I::Value\n    where\n        I: JsSliceIndex<'a>,\n    {\n        // Safety: Caller must ensure the index is not out of bounds\n        unsafe { JsSliceIndex::get_unchecked(self, index) }\n    }\n\n    /// Convert the [`JsStr`] into a [`Vec<U16>`].\n    #[inline]\n    #[must_use]\n    pub fn to_vec(&self) -> Vec<u16> {\n        match self.variant() {\n            JsStrVariant::Latin1(v) => v.iter().copied().map(u16::from).collect(),\n            JsStrVariant::Utf16(v) => v.to_vec(),\n        }\n    }\n\n    /// Returns true if needle is a prefix of the [`JsStr`].\n    #[inline]\n    #[must_use]\n    // We check the size, so this should never panic.\n    #[allow(clippy::missing_panics_doc)]\n    pub fn starts_with(&self, needle: JsStr<'_>) -> bool {\n        let n = needle.len();\n        self.len() >= n && needle == self.get(..n).expect(\"already checked size\")\n    }\n    /// Returns `true` if `needle` is a suffix of the [`JsStr`].\n    #[inline]\n    #[must_use]\n    // We check the size, so this should never panic.\n    #[allow(clippy::missing_panics_doc)]\n    pub fn ends_with(&self, needle: JsStr<'_>) -> bool {\n        let (m, n) = (self.len(), needle.len());\n        m >= n && needle == self.get(m - n..).expect(\"already checked size\")\n    }\n\n    /// Abstract operation `StringIndexOf ( string, searchValue, fromIndex )`\n    ///\n    /// Note: Instead of returning an isize with `-1` as the \"not found\" value, we make use of the\n    /// type system and return <code>[Option]\\<usize\\></code> with [`None`] as the \"not found\" value.\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-stringindexof\n    #[inline]\n    #[must_use]\n    pub fn index_of(&self, search_value: JsStr<'_>, from_index: usize) -> Option<usize> {\n        // 1. Assert: Type(string) is String.\n        // 2. Assert: Type(searchValue) is String.\n        // 3. Assert: fromIndex is a non-negative integer.\n\n        // 4. Let len be the length of string.\n        let len = self.len();\n\n        // 5. If searchValue is the empty String and fromIndex ≤ len, return fromIndex.\n        if search_value.is_empty() {\n            return if from_index <= len {\n                Some(from_index)\n            } else {\n                None\n            };\n        }\n\n        // 6. Let searchLen be the length of searchValue.\n        // 7. For each integer i starting with fromIndex such that i ≤ len - searchLen, in ascending order, do\n        // a. Let candidate be the substring of string from i to i + searchLen.\n        // b. If candidate is the same sequence of code units as searchValue, return i.\n        // 8. Return -1.\n        self.windows(search_value.len())\n            .skip(from_index)\n            .position(|s| s == search_value)\n            .map(|i| i + from_index)\n    }\n\n    /// Abstract operation `CodePointAt( string, position )`.\n    ///\n    /// The abstract operation `CodePointAt` takes arguments `string` (a String) and `position` (a\n    /// non-negative integer) and returns a Record with fields `[[CodePoint]]` (a code point),\n    /// `[[CodeUnitCount]]` (a positive integer), and `[[IsUnpairedSurrogate]]` (a Boolean). It\n    /// interprets string as a sequence of UTF-16 encoded code points, as described in 6.1.4, and reads\n    /// from it a single code point starting with the code unit at index `position`.\n    ///\n    /// More information:\n    ///  - [ECMAScript reference][spec]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-codepointat\n    ///\n    /// # Panics\n    ///\n    /// If `position` is smaller than size of string.\n    #[inline]\n    #[must_use]\n    pub fn code_point_at(&self, position: usize) -> CodePoint {\n        // 1. Let size be the length of string.\n        let size = self.len();\n\n        // 2. Assert: position ≥ 0 and position < size.\n        // position >= 0 ensured by position: usize\n        assert!(position < size);\n\n        match self.variant() {\n            JsStrVariant::Latin1(v) => {\n                let code_point = v.get(position).expect(\"Already checked the size\");\n                CodePoint::Unicode(*code_point as char)\n            }\n            // 3. Let first be the code unit at index position within string.\n            // 4. Let cp be the code point whose numeric value is that of first.\n            // 5. If first is not a leading surrogate or trailing surrogate, then\n            // a. Return the Record { [[CodePoint]]: cp, [[CodeUnitCount]]: 1, [[IsUnpairedSurrogate]]: false }.\n            // 6. If first is a trailing surrogate or position + 1 = size, then\n            // a. Return the Record { [[CodePoint]]: cp, [[CodeUnitCount]]: 1, [[IsUnpairedSurrogate]]: true }.\n            // 7. Let second be the code unit at index position + 1 within string.\n            // 8. If second is not a trailing surrogate, then\n            // a. Return the Record { [[CodePoint]]: cp, [[CodeUnitCount]]: 1, [[IsUnpairedSurrogate]]: true }.\n            // 9. Set cp to ! UTF16SurrogatePairToCodePoint(first, second).\n            JsStrVariant::Utf16(v) => {\n                // We can skip the checks and instead use the `char::decode_utf16` function to take care of that for us.\n                let code_point = v\n                    .get(position..=position + 1)\n                    .unwrap_or(&v[position..=position]);\n\n                match char::decode_utf16(code_point.iter().copied())\n                    .next()\n                    .expect(\"code_point always has a value\")\n                {\n                    Ok(c) => CodePoint::Unicode(c),\n                    Err(e) => CodePoint::UnpairedSurrogate(e.unpaired_surrogate()),\n                }\n            }\n        }\n    }\n\n    /// Abstract operation `StringToNumber ( str )`\n    ///\n    /// More information:\n    /// - [ECMAScript reference][spec]\n    ///\n    /// [spec]: https://tc39.es/ecma262/#sec-stringtonumber\n    #[inline]\n    #[must_use]\n    pub fn to_number(&self) -> f64 {\n        // 1. Let text be ! StringToCodePoints(str).\n        // 2. Let literal be ParseText(text, StringNumericLiteral).\n        let Ok(string) = self.to_std_string() else {\n            // 3. If literal is a List of errors, return NaN.\n            return f64::NAN;\n        };\n        // 4. Return StringNumericValue of literal.\n        let string = string.trim_matches(is_trimmable_whitespace);\n        match string {\n            \"\" => return 0.0,\n            \"-Infinity\" => return f64::NEG_INFINITY,\n            \"Infinity\" | \"+Infinity\" => return f64::INFINITY,\n            _ => {}\n        }\n\n        let mut s = string.bytes();\n        let base = match (s.next(), s.next()) {\n            (Some(b'0'), Some(b'b' | b'B')) => Some(2),\n            (Some(b'0'), Some(b'o' | b'O')) => Some(8),\n            (Some(b'0'), Some(b'x' | b'X')) => Some(16),\n            // Make sure that no further variants of \"infinity\" are parsed.\n            (Some(b'i' | b'I'), _) => {\n                return f64::NAN;\n            }\n            _ => None,\n        };\n\n        // Parse numbers that begin with `0b`, `0o` and `0x`.\n        if let Some(base) = base {\n            let string = &string[2..];\n            if string.is_empty() {\n                return f64::NAN;\n            }\n\n            // Fast path\n            if let Ok(value) = u32::from_str_radix(string, base) {\n                return f64::from(value);\n            }\n\n            // Slow path\n            let mut value: f64 = 0.0;\n            for c in s {\n                if let Some(digit) = char::from(c).to_digit(base) {\n                    value = value.mul_add(f64::from(base), f64::from(digit));\n                } else {\n                    return f64::NAN;\n                }\n            }\n            return value;\n        }\n\n        fast_float2::parse(string).unwrap_or(f64::NAN)\n    }\n\n    /// Gets an iterator of all the Unicode codepoints of a [`JsStr`].\n    #[inline]\n    #[must_use]\n    pub fn code_points(&self) -> CodePointsIter<'a> {\n        CodePointsIter::new(*self)\n    }\n\n    /// Checks if the [`JsStr`] contains a byte.\n    #[inline]\n    #[must_use]\n    pub fn contains(&self, element: u8) -> bool {\n        match self.variant() {\n            JsStrVariant::Latin1(v) => v.contains(&element),\n            JsStrVariant::Utf16(v) => v.contains(&u16::from(element)),\n        }\n    }\n\n    /// Gets an iterator of all the Unicode codepoints of a [`JsStr`], replacing\n    /// unpaired surrogates with the replacement character. This is faster than\n    /// using [`Self::code_points`].\n    #[inline]\n    pub fn code_points_lossy(self) -> impl Iterator<Item = char> + 'a {\n        char::decode_utf16(self.iter()).map(|res| res.unwrap_or('\\u{FFFD}'))\n    }\n\n    /// Decodes a [`JsStr`] into a [`String`], returning an error if it contains any invalid data.\n    ///\n    /// # Errors\n    ///\n    /// [`FromUtf16Error`][std::string::FromUtf16Error] if it contains any invalid data.\n    #[inline]\n    pub fn to_std_string(&self) -> Result<String, std::string::FromUtf16Error> {\n        match self.variant() {\n            JsStrVariant::Latin1(v) => Ok(v.iter().copied().map(char::from).collect()),\n            JsStrVariant::Utf16(v) => String::from_utf16(v),\n        }\n    }\n\n    /// Decodes a [`JsStr`] into a [`String`], replacing invalid data with the\n    /// replacement character U+FFFD.\n    #[inline]\n    #[must_use]\n    pub fn to_std_string_lossy(&self) -> String {\n        self.display_lossy().to_string()\n    }\n\n    /// Gets a displayable lossy string.\n    ///\n    /// This may be faster and has fewer\n    /// allocations than `format!(\"{}\", str.to_string_lossy())` when displaying.\n    #[inline]\n    #[must_use]\n    pub fn display_lossy(&self) -> JsStrDisplayLossy<'a> {\n        JsStrDisplayLossy::from(*self)\n    }\n}\n\nimpl Hash for JsStr<'_> {\n    #[inline]\n    fn hash<H: Hasher>(&self, state: &mut H) {\n        // NOTE: The hash function has been inlined to ensure that a hash of latin1 and U16\n        // encoded strings remains the same if they have the same characters\n        match self.variant() {\n            JsStrVariant::Latin1(s) => {\n                state.write_usize(s.len());\n                for elem in s {\n                    state.write_u16(u16::from(*elem));\n                }\n            }\n            JsStrVariant::Utf16(s) => {\n                state.write_usize(s.len());\n                for elem in s {\n                    state.write_u16(*elem);\n                }\n            }\n        }\n    }\n}\n\nimpl Ord for JsStr<'_> {\n    #[inline]\n    fn cmp(&self, other: &Self) -> std::cmp::Ordering {\n        match (self.variant(), other.variant()) {\n            (JsStrVariant::Latin1(x), JsStrVariant::Latin1(y)) => x.cmp(y),\n            (JsStrVariant::Utf16(x), JsStrVariant::Utf16(y)) => x.cmp(y),\n            _ => self.iter().cmp(other.iter()),\n        }\n    }\n}\n\nimpl Eq for JsStr<'_> {}\n\nimpl PartialEq for JsStr<'_> {\n    #[inline]\n    fn eq(&self, other: &Self) -> bool {\n        match (self.variant(), other.variant()) {\n            (JsStrVariant::Latin1(lhs), JsStrVariant::Latin1(rhs)) => return lhs == rhs,\n            (JsStrVariant::Utf16(lhs), JsStrVariant::Utf16(rhs)) => return lhs == rhs,\n            _ => {}\n        }\n        if self.len() != other.len() {\n            return false;\n        }\n        for (x, y) in self.iter().zip(other.iter()) {\n            if x != y {\n                return false;\n            }\n        }\n        true\n    }\n}\n\nimpl PartialEq<str> for JsStr<'_> {\n    #[inline]\n    fn eq(&self, other: &str) -> bool {\n        match self.variant() {\n            JsStrVariant::Latin1(v) => v == other.as_bytes(),\n            JsStrVariant::Utf16(v) => other.encode_utf16().zip(v).all(|(a, b)| a == *b),\n        }\n    }\n}\n\nimpl PartialEq<&str> for JsStr<'_> {\n    #[inline]\n    fn eq(&self, other: &&str) -> bool {\n        self == *other\n    }\n}\n\nimpl<'a> PartialEq<JsStr<'a>> for [u16] {\n    #[inline]\n    fn eq(&self, other: &JsStr<'a>) -> bool {\n        if self.len() != other.len() {\n            return false;\n        }\n        for (x, y) in self.iter().copied().zip(other.iter()) {\n            if x != y {\n                return false;\n            }\n        }\n        true\n    }\n}\n\nimpl std::fmt::Debug for JsStr<'_> {\n    #[inline]\n    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {\n        f.debug_struct(\"JsStr\").field(\"len\", &self.len()).finish()\n    }\n}\n\npub trait JsSliceIndex<'a>: SliceIndex<[u8]> + SliceIndex<[u16]> {\n    type Value;\n\n    fn get(_: JsStr<'a>, index: Self) -> Option<Self::Value>;\n\n    unsafe fn get_unchecked(value: JsStr<'a>, index: Self) -> Self::Value;\n}\n\nimpl<'a> JsSliceIndex<'a> for usize {\n    type Value = u16;\n\n    #[inline]\n    fn get(value: JsStr<'a>, index: Self) -> Option<Self::Value> {\n        match value.variant() {\n            JsStrVariant::Latin1(v) => v.get(index).copied().map(u16::from),\n            JsStrVariant::Utf16(v) => v.get(index).copied(),\n        }\n    }\n\n    /// # Safety\n    ///\n    /// Caller must ensure the index is not out of bounds\n    #[inline]\n    unsafe fn get_unchecked(value: JsStr<'a>, index: Self) -> Self::Value {\n        // Safety: Caller must ensure the index is not out of bounds\n        unsafe {\n            match value.variant() {\n                JsStrVariant::Latin1(v) => u16::from(*v.get_unchecked(index)),\n                JsStrVariant::Utf16(v) => *v.get_unchecked(index),\n            }\n        }\n    }\n}\n\nimpl<'a> JsSliceIndex<'a> for std::ops::Range<usize> {\n    type Value = JsStr<'a>;\n\n    #[inline]\n    fn get(value: JsStr<'a>, index: Self) -> Option<Self::Value> {\n        match value.variant() {\n            JsStrVariant::Latin1(v) => v.get(index).map(JsStr::latin1),\n            JsStrVariant::Utf16(v) => v.get(index).map(JsStr::utf16),\n        }\n    }\n\n    /// # Safety\n    ///\n    /// Caller must ensure the index is not out of bounds\n    #[inline]\n    unsafe fn get_unchecked(value: JsStr<'a>, index: Self) -> Self::Value {\n        // Safety: Caller must ensure the index is not out of bounds\n        unsafe {\n            match value.variant() {\n                JsStrVariant::Latin1(v) => JsStr::latin1(v.get_unchecked(index)),\n                JsStrVariant::Utf16(v) => JsStr::utf16(v.get_unchecked(index)),\n            }\n        }\n    }\n}\n\nimpl<'a> JsSliceIndex<'a> for std::ops::RangeInclusive<usize> {\n    type Value = JsStr<'a>;\n\n    #[inline]\n    fn get(value: JsStr<'a>, index: Self) -> Option<Self::Value> {\n        match value.variant() {\n            JsStrVariant::Latin1(v) => v.get(index).map(JsStr::latin1),\n            JsStrVariant::Utf16(v) => v.get(index).map(JsStr::utf16),\n        }\n    }\n\n    /// # Safety\n    ///\n    /// Caller must ensure the index is not out of bounds\n    #[inline]\n    unsafe fn get_unchecked(value: JsStr<'a>, index: Self) -> Self::Value {\n        // Safety: Caller must ensure the index is not out of bounds\n        unsafe {\n            match value.variant() {\n                JsStrVariant::Latin1(v) => JsStr::latin1(v.get_unchecked(index)),\n                JsStrVariant::Utf16(v) => JsStr::utf16(v.get_unchecked(index)),\n            }\n        }\n    }\n}\n\nimpl<'a> JsSliceIndex<'a> for std::ops::RangeFrom<usize> {\n    type Value = JsStr<'a>;\n\n    #[inline]\n    fn get(value: JsStr<'a>, index: Self) -> Option<Self::Value> {\n        match value.variant() {\n            JsStrVariant::Latin1(v) => v.get(index).map(JsStr::latin1),\n            JsStrVariant::Utf16(v) => v.get(index).map(JsStr::utf16),\n        }\n    }\n\n    /// # Safety\n    ///\n    /// Caller must ensure the index is not out of bounds\n    #[inline]\n    unsafe fn get_unchecked(value: JsStr<'a>, index: Self) -> Self::Value {\n        // Safety: Caller must ensure the index is not out of bounds\n        unsafe {\n            match value.variant() {\n                JsStrVariant::Latin1(v) => JsStr::latin1(v.get_unchecked(index)),\n                JsStrVariant::Utf16(v) => JsStr::utf16(v.get_unchecked(index)),\n            }\n        }\n    }\n}\n\nimpl<'a> JsSliceIndex<'a> for std::ops::RangeTo<usize> {\n    type Value = JsStr<'a>;\n\n    #[inline]\n    fn get(value: JsStr<'a>, index: Self) -> Option<Self::Value> {\n        match value.variant() {\n            JsStrVariant::Latin1(v) => v.get(index).map(JsStr::latin1),\n            JsStrVariant::Utf16(v) => v.get(index).map(JsStr::utf16),\n        }\n    }\n\n    /// # Safety\n    ///\n    /// Caller must ensure the index is not out of bounds\n    #[inline]\n    unsafe fn get_unchecked(value: JsStr<'a>, index: Self) -> Self::Value {\n        // Safety: Caller must ensure the index is not out of bounds\n        unsafe {\n            match value.variant() {\n                JsStrVariant::Latin1(v) => JsStr::latin1(v.get_unchecked(index)),\n                JsStrVariant::Utf16(v) => JsStr::utf16(v.get_unchecked(index)),\n            }\n        }\n    }\n}\n\nimpl<'a> JsSliceIndex<'a> for std::ops::RangeFull {\n    type Value = JsStr<'a>;\n\n    #[inline]\n    fn get(value: JsStr<'a>, _index: Self) -> Option<Self::Value> {\n        Some(value)\n    }\n\n    /// # Safety\n    ///\n    /// Caller must ensure the index is not out of bounds\n    #[inline]\n    unsafe fn get_unchecked(value: JsStr<'a>, _index: Self) -> Self::Value {\n        value\n    }\n}\n"
  },
  {
    "path": "core/string/src/tests.rs",
    "content": "#![allow(clippy::redundant_clone)]\n\nuse std::hash::{BuildHasher, BuildHasherDefault, Hash};\n\nuse crate::{\n    CodePoint, CommonJsStringBuilder, JsStr, JsString, JsStringKind, Latin1JsStringBuilder,\n    StaticJsStrings, StaticString, Utf16JsStringBuilder,\n};\n\nuse rustc_hash::FxHasher;\n\nfn hash_value<T: Hash>(value: &T) -> u64 {\n    BuildHasherDefault::<FxHasher>::default().hash_one(value)\n}\n\nconst fn ascii_to_utf16<const LEN: usize>(ascii: &[u8; LEN]) -> [u16; LEN] {\n    let mut array = [0; LEN];\n    let mut i = 0;\n    while i < LEN {\n        array[i] = ascii[i] as u16;\n        i += 1;\n    }\n    array\n}\n\n#[test]\nfn empty() {\n    let s = StaticJsStrings::EMPTY_STRING;\n    assert_eq!(&s, &[]);\n}\n\n#[test]\nfn refcount() {\n    let x = JsString::from(\"Hello world\");\n    assert_eq!(x.refcount(), Some(1));\n\n    {\n        let y = x.clone();\n        assert_eq!(x.refcount(), Some(2));\n        assert_eq!(y.refcount(), Some(2));\n\n        {\n            let z = y.clone();\n            assert_eq!(x.refcount(), Some(3));\n            assert_eq!(y.refcount(), Some(3));\n            assert_eq!(z.refcount(), Some(3));\n        }\n\n        assert_eq!(x.refcount(), Some(2));\n        assert_eq!(y.refcount(), Some(2));\n    }\n\n    assert_eq!(x.refcount(), Some(1));\n}\n\n#[test]\nfn static_refcount() {\n    let x = StaticJsStrings::EMPTY_STRING;\n    assert_eq!(x.refcount(), None);\n\n    {\n        let y = x.clone();\n        assert_eq!(x.refcount(), None);\n        assert_eq!(y.refcount(), None);\n    };\n\n    assert_eq!(x.refcount(), None);\n}\n\n#[test]\nfn ptr_eq() {\n    let x = JsString::from(\"Hello\");\n    let y = x.clone();\n\n    assert!(!x.is_static());\n\n    assert_eq!(x.ptr.addr(), y.ptr.addr());\n\n    let z = JsString::from(\"Hello\");\n    assert_ne!(x.ptr.addr(), z.ptr.addr());\n    assert_ne!(y.ptr.addr(), z.ptr.addr());\n}\n\n#[test]\nfn static_ptr_eq() {\n    let x = StaticJsStrings::EMPTY_STRING;\n    let y = x.clone();\n\n    assert!(x.is_static());\n\n    assert_eq!(x.ptr.addr(), y.ptr.addr());\n\n    let z = StaticJsStrings::EMPTY_STRING;\n    assert_eq!(x.ptr.addr(), z.ptr.addr());\n    assert_eq!(y.ptr.addr(), z.ptr.addr());\n}\n\n#[test]\nfn as_str() {\n    const HELLO: &[u16] = &ascii_to_utf16(b\"Hello\");\n    let x = JsString::from(HELLO);\n\n    assert_eq!(&x, HELLO);\n}\n\n#[test]\nfn hash() {\n    const HELLOWORLD: JsStr<'_> = JsStr::latin1(\"Hello World!\".as_bytes());\n    let x = JsString::from(HELLOWORLD);\n\n    assert_eq!(x.as_str(), HELLOWORLD);\n\n    assert!(HELLOWORLD.is_latin1());\n    assert!(x.as_str().is_latin1());\n\n    let s_hash = hash_value(&HELLOWORLD);\n    let x_hash = hash_value(&x);\n\n    assert_eq!(s_hash, x_hash);\n}\n\n#[test]\nfn concat() {\n    const Y: &[u16] = &ascii_to_utf16(b\", \");\n    const W: &[u16] = &ascii_to_utf16(b\"!\");\n\n    let x = JsString::from(\"hello\");\n    let z = JsString::from(\"world\");\n\n    let xy = JsString::concat(x.as_str(), JsString::from(Y).as_str());\n    assert_eq!(&xy, &ascii_to_utf16(b\"hello, \"));\n    assert_eq!(xy.refcount(), Some(1));\n\n    let xyz = JsString::concat(xy.as_str(), z.as_str());\n    assert_eq!(&xyz, &ascii_to_utf16(b\"hello, world\"));\n    assert_eq!(xyz.refcount(), Some(1));\n\n    let xyzw = JsString::concat(xyz.as_str(), JsString::from(W).as_str());\n    assert_eq!(&xyzw, &ascii_to_utf16(b\"hello, world!\"));\n    assert_eq!(xyzw.refcount(), Some(1));\n}\n\n#[test]\nfn trim_start_non_ascii_to_ascii() {\n    let s = \"\\u{2029}abc\";\n    let x = JsString::from(s);\n\n    let y = x.trim_start();\n\n    assert_eq!(&y, s.trim_start());\n}\n\n#[test]\nfn conversion_to_known_static_js_string() {\n    const JS_STR_U8: &JsStr<'_> = &JsStr::latin1(\"length\".as_bytes());\n    const JS_STR_U16: &JsStr<'_> = &JsStr::utf16(&ascii_to_utf16(b\"length\"));\n\n    assert!(JS_STR_U8.is_latin1());\n    assert!(!JS_STR_U16.is_latin1());\n\n    assert_eq!(JS_STR_U8, JS_STR_U8);\n    assert_eq!(JS_STR_U16, JS_STR_U16);\n\n    assert_eq!(JS_STR_U8, JS_STR_U16);\n    assert_eq!(JS_STR_U16, JS_STR_U8);\n\n    assert_eq!(hash_value(JS_STR_U8), hash_value(JS_STR_U16));\n\n    let string = StaticJsStrings::get_string(JS_STR_U8);\n\n    assert!(string.is_some());\n    assert!(string.unwrap().as_str().is_latin1());\n\n    let string = StaticJsStrings::get_string(JS_STR_U16);\n\n    assert!(string.is_some());\n    assert!(string.unwrap().as_str().is_latin1());\n}\n\n#[test]\nfn to_std_string_escaped() {\n    assert_eq!(\n        JsString::from(\"Hello, \\u{1D49E} world!\").to_std_string_escaped(),\n        \"Hello, \\u{1D49E} world!\"\n    );\n\n    assert_eq!(\n        JsString::from(\"Hello, world!\").to_std_string_escaped(),\n        \"Hello, world!\"\n    );\n\n    // 15 should not be escaped.\n    let unpaired_surrogates: [u16; 3] = [0xDC58, 0xD83C, 0x0015];\n    assert_eq!(\n        JsString::from(&unpaired_surrogates).to_std_string_escaped(),\n        \"\\\\uDC58\\\\uD83C\\u{15}\"\n    );\n}\n\n#[test]\nfn from_static_js_string() {\n    static STATIC_HELLO_WORLD: StaticString =\n        StaticString::new(JsStr::latin1(\"hello world\".as_bytes()));\n    static STATIC_EMOJIS: StaticString = StaticString::new(JsStr::utf16(&[\n        0xD83C, 0xDFB9, 0xD83C, 0xDFB6, 0xD83C, 0xDFB5,\n    ])); // 🎹🎶🎵\n\n    let latin1 = JsString::from_static(&STATIC_HELLO_WORLD);\n    let utf16 = JsString::from_static(&STATIC_EMOJIS);\n\n    // content compare\n    assert_eq!(latin1, \"hello world\");\n    assert_eq!(utf16, \"🎹🎶🎵\");\n\n    // refcount check\n    let clone = latin1.clone();\n\n    assert_eq!(clone, latin1);\n\n    let clone = utf16.clone();\n\n    assert_eq!(clone, utf16);\n\n    assert!(latin1.refcount().is_none());\n    assert!(utf16.refcount().is_none());\n\n    // `is_latin1` check\n    assert!(latin1.as_str().is_latin1());\n    assert!(!utf16.as_str().is_latin1());\n}\n\n#[test]\nfn compare_static_and_dynamic_js_string() {\n    static STATIC_HELLO_WORLD: StaticString =\n        StaticString::new(JsStr::latin1(\"hello world\".as_bytes()));\n    static STATIC_EMOJIS: StaticString = StaticString::new(JsStr::utf16(&[\n        0xD83C, 0xDFB9, 0xD83C, 0xDFB6, 0xD83C, 0xDFB5,\n    ])); // 🎹🎶🎵\n\n    let static_latin1 = JsString::from_static(&STATIC_HELLO_WORLD);\n    let static_utf16 = JsString::from_static(&STATIC_EMOJIS);\n\n    let dynamic_latin1 = JsString::from(JsStr::latin1(\"hello world\".as_bytes()));\n    let dynamic_utf16 = JsString::from(&[0xD83C, 0xDFB9, 0xD83C, 0xDFB6, 0xD83C, 0xDFB5]);\n\n    // content compare\n    assert_eq!(static_latin1, dynamic_latin1);\n    assert_eq!(static_utf16, dynamic_utf16);\n\n    // length check\n    assert_eq!(static_latin1.len(), dynamic_latin1.len());\n    assert_eq!(static_utf16.len(), dynamic_utf16.len());\n\n    // `is_static` check\n    assert!(static_latin1.is_static());\n    assert!(static_utf16.is_static());\n    assert!(!dynamic_latin1.is_static());\n    assert!(!dynamic_utf16.is_static());\n}\n\n#[test]\n#[allow(clippy::cast_possible_truncation)]\n#[allow(clippy::undocumented_unsafe_blocks)]\nfn js_string_builder() {\n    let s = \"2024年5月21日\";\n    let utf16 = s.encode_utf16().collect::<Vec<_>>();\n    let s_utf16 = utf16.as_slice();\n    let ascii = \"Lorem ipsum dolor sit amet\";\n    let s_ascii = ascii.as_bytes();\n    let latin1_as_utf8_literal = \"Déjà vu\";\n    let s_latin1_literal: &[u8] = &[\n        b'D', 0xE9, /* é */\n        b'j', 0xE0, /* à */\n        b' ', b'v', b'u',\n    ];\n\n    // latin1 builder -- test\n\n    // push ascii\n    let mut builder = Latin1JsStringBuilder::new();\n    for &code in s_ascii {\n        builder.push(code);\n    }\n    let s_builder = builder.build().unwrap_or_default();\n    assert_eq!(s_builder, ascii);\n\n    // push latin1\n    let mut builder = Latin1JsStringBuilder::new();\n    for &code in s_latin1_literal {\n        builder.push(code);\n    }\n    let s_builder = unsafe { builder.build_as_latin1() };\n    assert_eq!(\n        s_builder.to_std_string().unwrap_or_default(),\n        latin1_as_utf8_literal\n    );\n\n    // from_iter ascii\n    let s_builder = s_ascii\n        .iter()\n        .copied()\n        .collect::<Latin1JsStringBuilder>()\n        .build()\n        .unwrap_or_default();\n    assert_eq!(s_builder.to_std_string().unwrap_or_default(), ascii);\n\n    // from_iter latin1\n    let s_builder = unsafe {\n        s_latin1_literal\n            .iter()\n            .copied()\n            .collect::<Latin1JsStringBuilder>()\n            .build_as_latin1()\n    };\n    assert_eq!(\n        s_builder.to_std_string().unwrap_or_default(),\n        latin1_as_utf8_literal\n    );\n\n    // extend_from_slice ascii\n    let mut builder = Latin1JsStringBuilder::new();\n    builder.extend_from_slice(s_ascii);\n    let s_builder = builder.build().unwrap_or_default();\n    assert_eq!(s_builder.to_std_string().unwrap_or_default(), ascii);\n\n    // extend_from_slice latin1\n    let mut builder = Latin1JsStringBuilder::new();\n    builder.extend_from_slice(s_latin1_literal);\n    let s_builder = unsafe { builder.build_as_latin1() };\n    assert_eq!(\n        s_builder.to_std_string().unwrap_or_default(),\n        latin1_as_utf8_literal\n    );\n\n    // build from utf16 encoded string\n    let s_builder = s\n        .as_bytes()\n        .iter()\n        .copied()\n        .collect::<Latin1JsStringBuilder>()\n        .build();\n    assert_eq!(None, s_builder);\n\n    let s_builder = s_utf16\n        .iter()\n        .copied()\n        .map(|v| v as u8)\n        .collect::<Latin1JsStringBuilder>()\n        .build();\n    assert_eq!(None, s_builder);\n\n    // utf16 builder -- test\n\n    // push\n    let mut builder = Utf16JsStringBuilder::new();\n    for &code in s_utf16 {\n        builder.push(code);\n    }\n    let s_builder = builder.build();\n    assert_eq!(s_builder.to_std_string().unwrap_or_default(), s);\n\n    // from_iter\n    let s_builder = s_utf16\n        .iter()\n        .copied()\n        .collect::<Utf16JsStringBuilder>()\n        .build();\n    assert_eq!(s_builder.to_std_string().unwrap_or_default(), s);\n\n    // extend_from_slice\n    let mut builder = Utf16JsStringBuilder::new();\n    builder.extend_from_slice(s_utf16);\n    let s_builder = builder.build();\n    assert_eq!(s_builder.to_std_string().unwrap_or_default(), s);\n}\n\n#[test]\nfn clone_builder() {\n    // latin1 builder -- test\n    let origin = Latin1JsStringBuilder::from(&b\"0123456789\"[..]);\n    let empty_origin = Latin1JsStringBuilder::new();\n\n    // clone == origin\n    let cloned = origin.clone();\n    assert_eq!(origin, cloned);\n\n    // clone_from == origin\n    let mut cloned_from = Latin1JsStringBuilder::new();\n    cloned_from.clone_from(&origin);\n    assert_eq!(origin, cloned_from);\n\n    // clone == origin(empty)\n    let cloned = empty_origin.clone();\n    assert_eq!(empty_origin, cloned);\n\n    // clone_from == origin(empty)\n\n    cloned_from.clone_from(&empty_origin);\n    assert!(cloned_from.capacity() > 0); // Should not be reallocated so the capacity is preserved.\n    assert_eq!(empty_origin, cloned_from);\n\n    // clone_from(empty) == origin(empty)\n    let mut cloned_from = Latin1JsStringBuilder::new();\n    cloned_from.clone_from(&empty_origin);\n    assert!(cloned_from.capacity() == 0);\n    assert_eq!(empty_origin, cloned_from);\n\n    // utf16 builder -- test\n    let s = \"2024年5月21日\";\n\n    let origin = Utf16JsStringBuilder::from(s.encode_utf16().collect::<Vec<_>>().as_slice());\n    let empty_origin = Utf16JsStringBuilder::new();\n    // clone == origin\n    let cloned = origin.clone();\n    assert_eq!(origin, cloned);\n\n    // clone_from == origin(empty)\n    let mut cloned_from = Utf16JsStringBuilder::new();\n    cloned_from.clone_from(&origin);\n\n    assert_eq!(origin, cloned_from);\n    // clone == origin(empty)\n    let cloned = empty_origin.clone();\n    assert_eq!(empty_origin, cloned);\n\n    // clone_from == origin(empty)\n\n    cloned_from.clone_from(&empty_origin);\n    assert!(cloned_from.capacity() > 0); // should not be reallocated so the capacity is preserved.\n    assert_eq!(empty_origin, cloned_from);\n\n    // clone_from(empty) == origin(empty)\n    let mut cloned_from = Utf16JsStringBuilder::new();\n    cloned_from.clone_from(&empty_origin);\n    assert!(cloned_from.capacity() == 0);\n    assert_eq!(empty_origin, cloned_from);\n}\n\n#[test]\nfn common_js_string_builder() {\n    let utf16 = \"2024年5月21日\".encode_utf16().collect::<Vec<_>>();\n    let s_utf16 = utf16.as_slice();\n    let s = \"Lorem ipsum dolor sit amet\";\n    let js_str_utf16 = JsStr::utf16(s_utf16);\n    let js_str_ascii = JsStr::latin1(s.as_bytes());\n    let latin1_bytes = [\n        b'D', 0xE9, /* é */\n        b'j', 0xE0, /* à */\n        b' ', b'v', b'u',\n    ];\n    let ch = '🎹';\n    let mut builder = CommonJsStringBuilder::with_capacity(10);\n    builder += ch;\n    builder += s;\n    builder += js_str_utf16;\n    builder += js_str_ascii;\n    builder += ch;\n    assert_eq!(builder.len(), 5);\n    let js_string = builder.build_from_utf16();\n    assert_eq!(\n        js_string,\n        \"🎹Lorem ipsum dolor sit amet2024年5月21日Lorem ipsum dolor sit amet🎹\"\n    );\n    let mut builder = CommonJsStringBuilder::new();\n    for b in latin1_bytes {\n        builder += b;\n    }\n    builder += s_utf16;\n    builder += ch;\n    let js_string = builder.build();\n    assert_eq!(\n        js_string.to_std_string().unwrap_or_default(),\n        \"Déjà vu2024年5月21日🎹\"\n    );\n}\n\n#[test]\nfn code_points_optimization() {\n    // Test Latin1 optimization with extended Latin1 characters\n    let latin1_str = JsStr::latin1(b\"Caf\\xe9 na\\xefve\"); // \"Café naïve\" in Latin1 encoding\n    let latin1_points: Vec<CodePoint> = latin1_str.code_points().collect();\n    let expected_latin1: Vec<CodePoint> = \"Café naïve\".chars().map(CodePoint::Unicode).collect();\n    assert_eq!(latin1_points, expected_latin1);\n\n    // Test UTF-16 behavior unchanged (including non-ASCII)\n    let utf16_str = JsStr::utf16(&[\n        0x0043, 0x0061, 0x0066, 0x00E9, // \"Café\"\n        0x0020, // space\n        0x006E, 0x0061, 0x00EF, 0x0076, 0x0065, // \"naïve\"\n    ]);\n    let utf16_points: Vec<CodePoint> = utf16_str.code_points().collect();\n    assert_eq!(latin1_points, utf16_points); // Same result for same content\n}\n\n#[test]\nfn slice() {\n    let sliced = {\n        let base_str = JsString::from(\"Hello World\");\n        assert_eq!(base_str.kind(), JsStringKind::Latin1Sequence);\n\n        base_str.slice(1, 5)\n    };\n    assert_eq!(sliced, JsString::from(\"ello\"));\n    assert_eq!(sliced.kind(), JsStringKind::Slice);\n\n    let sliced2 = sliced.slice(1, 3);\n    drop(sliced);\n    assert_eq!(sliced2, JsString::from(\"ll\"));\n    assert_eq!(sliced2.kind(), JsStringKind::Slice);\n\n    let sliced3 = sliced2.slice(0, 2);\n    drop(sliced2);\n    assert_eq!(sliced3, JsString::from(\"ll\"));\n    assert_eq!(sliced3.kind(), JsStringKind::Slice);\n\n    let sliced4 = sliced3.slice(0, 2);\n    drop(sliced3);\n    assert_eq!(sliced4, JsString::from(\"ll\"));\n    assert_eq!(sliced4.kind(), JsStringKind::Slice);\n\n    let sliced4 = sliced4.slice(0, 2);\n    assert_eq!(sliced4, JsString::from(\"ll\"));\n    assert_eq!(sliced4.kind(), JsStringKind::Slice);\n\n    let sliced5 = sliced4.slice(1, 1);\n    assert_eq!(sliced5, JsString::from(\"\"));\n    assert_eq!(sliced5.kind(), JsStringKind::Static);\n\n    assert_eq!(sliced5.slice(4, 4), StaticJsStrings::EMPTY_STRING);\n}\n\n#[test]\nfn split() {\n    let base_str = JsString::from(\"Hello World\");\n    assert_eq!(base_str.kind(), JsStringKind::Latin1Sequence);\n\n    let str1 = base_str.slice(0, 5);\n    let str2 = base_str.slice(6, base_str.len());\n\n    assert_eq!(str1, JsString::from(\"Hello\"));\n    assert_eq!(str2, JsString::from(\"World\"));\n\n    let str3 = str1.clone();\n    drop(str1);\n    assert_eq!(str3, JsString::from(\"Hello\"));\n    drop(base_str);\n    assert_eq!(str3, JsString::from(\"Hello\"));\n}\n\n#[test]\nfn trim() {\n    // Very basic test for trimming. The extensive testing is done by `boa_engine`.\n    let base_str = JsString::from(\" \\u{000B} Hello World \\t \");\n    assert_eq!(base_str.trim(), JsString::from(\"Hello World\"));\n}\n"
  },
  {
    "path": "core/string/src/type.rs",
    "content": "//! Module containing string types public and crate-specific.\nuse crate::vtable::SequenceString;\nuse crate::{JsStr, JsStringKind};\nuse std::alloc::Layout;\n\nmod sealed {\n    /// Seal to prevent others from implementing their own string types.\n    pub trait Sealed {}\n}\n\n/// Internal trait for crate-specific usage. Contains implementation details\n/// that should not leak through the API.\n#[allow(private_interfaces)]\npub(crate) trait InternalStringType: StringType {\n    /// The offset to the data field in the sequence string struct.\n    const DATA_OFFSET: usize;\n\n    /// The kind of string produced by this string type.\n    const KIND: JsStringKind;\n\n    /// Create the base layout for the sequence string header.\n    fn base_layout() -> Layout;\n\n    /// Construct a [`JsStr`] from a slice of characters.\n    fn str_ctor(slice: &[Self::Byte]) -> JsStr<'_>;\n}\n\n/// Trait that maps the data type to the appropriate internal types and constants.\npub trait StringType: sealed::Sealed {\n    /// The unit of a character for this type of string. For example, UTF-16 should\n    /// have a 16-bits size, while ASCII should have 8 bits.\n    type Byte: Copy + Eq + 'static;\n}\n\n// It is good defensive programming to have [`Latin1`] `!Copy`, as it should\n// not be used as a value anyway.\n#[allow(missing_copy_implementations)]\n#[derive(Debug)]\npub enum Latin1 {}\n\nimpl sealed::Sealed for Latin1 {}\nimpl StringType for Latin1 {\n    type Byte = u8;\n}\n\nimpl InternalStringType for Latin1 {\n    const DATA_OFFSET: usize = size_of::<SequenceString<Self>>();\n    const KIND: JsStringKind = JsStringKind::Latin1Sequence;\n\n    fn base_layout() -> Layout {\n        Layout::new::<SequenceString<Self>>()\n    }\n\n    fn str_ctor(slice: &[Self::Byte]) -> JsStr<'_> {\n        JsStr::latin1(slice)\n    }\n}\n\n// It is good defensive programming to have [`Utf16`] `!Copy`, as it should\n// not be used as a value anyway.\n#[allow(missing_copy_implementations)]\n#[derive(Debug)]\npub enum Utf16 {}\n\nimpl sealed::Sealed for Utf16 {}\nimpl StringType for Utf16 {\n    type Byte = u16;\n}\n\nimpl InternalStringType for Utf16 {\n    const DATA_OFFSET: usize = size_of::<SequenceString<Self>>();\n    const KIND: JsStringKind = JsStringKind::Utf16Sequence;\n\n    fn base_layout() -> Layout {\n        Layout::new::<SequenceString<Self>>()\n    }\n\n    fn str_ctor(slice: &[Self::Byte]) -> JsStr<'_> {\n        JsStr::utf16(slice)\n    }\n}\n"
  },
  {
    "path": "core/string/src/vtable/mod.rs",
    "content": "//! Module defining the [`JsString`] `VTable` and kinds of strings.\nuse crate::iter::CodePointsIter;\nuse crate::{JsStr, JsString, JsStringKind};\nuse std::ptr::NonNull;\n\nmod sequence;\npub(crate) use sequence::SequenceString;\n\npub(crate) mod slice;\npub(crate) use slice::SliceString;\n\npub(crate) mod r#static;\npub use r#static::StaticString;\n\n/// Embedded vtable for `JsString` operations. This is stored directly in each string\n/// struct (not as a reference) to eliminate one level of indirection on hot paths.\n#[derive(Debug, Copy, Clone)]\n#[repr(C)]\npub(crate) struct JsStringVTable {\n    /// Clone the string, incrementing the refcount.\n    pub clone: fn(NonNull<JsStringVTable>) -> JsString,\n    /// Drop the string, decrementing the refcount and freeing if needed.\n    pub drop: fn(NonNull<JsStringVTable>),\n    /// Get the string as a `JsStr`. Although this is marked as `'static`, this is really\n    /// of the lifetime of the string itself. This is conveyed by the [`JsString`] API\n    /// itself rather than this vtable.\n    pub as_str: fn(NonNull<JsStringVTable>) -> JsStr<'static>,\n    /// Get an iterator of code points. This is the basic form of character access.\n    /// Although this is marked as `'static`, this is really of the lifetime of the string\n    /// itself. This is conveyed by the [`JsString`] API itself rather than this vtable.\n    pub code_points: fn(NonNull<JsStringVTable>) -> CodePointsIter<'static>,\n    /// Get the refcount, if applicable.\n    pub refcount: fn(NonNull<JsStringVTable>) -> Option<usize>,\n    /// Get the length of the string. Since a string is immutable, this does not need\n    /// to be a call, can be calculated at construction.\n    pub len: usize,\n    /// Kind tag to identify the string type.\n    pub kind: JsStringKind,\n}\n"
  },
  {
    "path": "core/string/src/vtable/sequence.rs",
    "content": "//! `VTable` implementations for [`SequenceString`].\nuse crate::iter::CodePointsIter;\nuse crate::r#type::InternalStringType;\nuse crate::vtable::JsStringVTable;\nuse crate::{JsStr, JsString, alloc_overflow};\nuse std::alloc::{Layout, alloc, dealloc};\nuse std::cell::Cell;\nuse std::marker::PhantomData;\nuse std::process::abort;\nuse std::ptr;\nuse std::ptr::NonNull;\n\n/// A sequential memory array of `T::Char` elements.\n///\n/// # Notes\n/// A [`SequenceString`] is `!Sync` (using [`Cell`]) and invariant over `T` (strings\n/// of various types cannot be used interchangeably). The string, however, could be\n/// `Send`, although within Boa this does not make sense.\n#[repr(C)]\npub(crate) struct SequenceString<T: InternalStringType> {\n    /// Embedded `VTable` - must be the first field for vtable dispatch.\n    vtable: JsStringVTable,\n    refcount: Cell<usize>,\n    // Forces invariant contract.\n    _marker: PhantomData<fn() -> T>,\n    pub(crate) data: [u8; 0],\n}\n\nimpl<T: InternalStringType> SequenceString<T> {\n    /// Creates a [`SequenceString`] without data. This should only be used to write to\n    /// an allocation which contains all the information.\n    #[inline]\n    #[must_use]\n    pub(crate) fn new(len: usize) -> Self {\n        SequenceString {\n            vtable: JsStringVTable {\n                clone: seq_clone::<T>,\n                drop: seq_drop::<T>,\n                as_str: seq_as_str::<T>,\n                code_points: seq_code_points::<T>,\n                refcount: seq_refcount::<T>,\n                len,\n                kind: T::KIND,\n            },\n            refcount: Cell::new(1),\n            _marker: PhantomData,\n            data: [0; 0],\n        }\n    }\n\n    /// Allocates a new [`SequenceString`] with an internal capacity of `len` characters.\n    ///\n    /// # Panics\n    ///\n    /// Panics if `try_allocate_seq` returns `Err`.\n    pub(crate) fn allocate(len: usize) -> NonNull<SequenceString<T>> {\n        match Self::try_allocate(len) {\n            Ok(v) => v,\n            Err(None) => alloc_overflow(),\n            Err(Some(layout)) => std::alloc::handle_alloc_error(layout),\n        }\n    }\n\n    /// Allocates a new [`SequenceString`] with an internal capacity of `len` characters.\n    ///\n    /// # Errors\n    ///\n    /// Returns `Err(None)` on integer overflows `usize::MAX`.\n    /// Returns `Err(Some(Layout))` on allocation error.\n    pub(crate) fn try_allocate(len: usize) -> Result<NonNull<Self>, Option<Layout>> {\n        let (layout, offset) = Layout::array::<T::Byte>(len)\n            .and_then(|arr| T::base_layout().extend(arr))\n            .map(|(layout, offset)| (layout.pad_to_align(), offset))\n            .map_err(|_| None)?;\n\n        debug_assert_eq!(offset, T::DATA_OFFSET);\n        debug_assert_eq!(layout.align(), align_of::<Self>());\n\n        #[allow(clippy::cast_ptr_alignment)]\n        // SAFETY:\n        // The layout size of `SequenceString` is never zero, since it has to store\n        // the length of the string and the reference count.\n        let inner = unsafe { alloc(layout).cast::<Self>() };\n\n        // We need to verify that the pointer returned by `alloc` is not null, otherwise\n        // we should abort, since an allocation error is pretty unrecoverable for us\n        // right now.\n        let inner = NonNull::new(inner).ok_or(Some(layout))?;\n\n        // SAFETY:\n        // `NonNull` verified for us that the pointer returned by `alloc` is valid,\n        // meaning we can write to its pointed memory.\n        unsafe {\n            // Write the first part, the `SequenceString`.\n            inner.as_ptr().write(Self::new(len));\n        }\n\n        debug_assert!({\n            let inner = inner.as_ptr();\n            // SAFETY:\n            // - `inner` must be a valid pointer, since it comes from a `NonNull`,\n            // meaning we can safely dereference it to `SequenceString`.\n            // - `offset` should point us to the beginning of the array,\n            // and since we requested a `SequenceString` layout with a trailing\n            // `[T::Byte; str_len]`, the memory of the array must be in the `usize`\n            // range for the allocation to succeed.\n            unsafe {\n                // This is `<u8>` as the offset is in bytes.\n                ptr::eq(\n                    inner.cast::<u8>().add(offset).cast(),\n                    (*inner).data().cast_mut(),\n                )\n            }\n        });\n\n        Ok(inner)\n    }\n\n    /// Returns the pointer to the data.\n    #[inline]\n    #[must_use]\n    pub(crate) const fn data(&self) -> *const u8 {\n        self.data.as_ptr()\n    }\n}\n\n#[inline]\nfn seq_clone<T: InternalStringType>(vtable: NonNull<JsStringVTable>) -> JsString {\n    // SAFETY: This is part of the correct vtable which is validated on construction.\n    let this: &SequenceString<T> = unsafe { vtable.cast().as_ref() };\n    let Some(strong) = this.refcount.get().checked_add(1) else {\n        abort();\n    };\n    this.refcount.set(strong);\n    // SAFETY: validated the string outside this function.\n    unsafe { JsString::from_ptr(vtable) }\n}\n\n#[inline]\nfn seq_drop<T: InternalStringType>(vtable: NonNull<JsStringVTable>) {\n    // SAFETY: This is part of the correct vtable which is validated on construction.\n    let this: &SequenceString<T> = unsafe { vtable.cast().as_ref() };\n    let Some(new) = this.refcount.get().checked_sub(1) else {\n        abort();\n    };\n    this.refcount.set(new);\n    if new != 0 {\n        return;\n    }\n\n    // SAFETY: All the checks for the validity of the layout have already been made on allocation.\n    let layout = unsafe {\n        Layout::for_value(this)\n            .extend(Layout::array::<T::Byte>(this.vtable.len).unwrap_unchecked())\n            .unwrap_unchecked()\n            .0\n            .pad_to_align()\n    };\n\n    // SAFETY: If refcount is 0, this is the last reference, so deallocating is safe.\n    unsafe {\n        dealloc(vtable.as_ptr().cast(), layout);\n    }\n}\n\n#[inline]\nfn seq_as_str<T: InternalStringType>(vtable: NonNull<JsStringVTable>) -> JsStr<'static> {\n    // SAFETY: This is part of the correct vtable which is validated on construction.\n    let this: &SequenceString<T> = unsafe { vtable.cast().as_ref() };\n    let len = this.vtable.len;\n    let data_ptr = (&raw const this.data).cast::<T::Byte>();\n\n    // SAFETY: SequenceString data is always valid and properly aligned.\n    let slice = unsafe { std::slice::from_raw_parts(data_ptr, len) };\n    T::str_ctor(slice)\n}\n\n#[inline]\nfn seq_code_points<T: InternalStringType>(\n    vtable: NonNull<JsStringVTable>,\n) -> CodePointsIter<'static> {\n    CodePointsIter::new(seq_as_str::<T>(vtable))\n}\n\n/// `VTable` function for refcount, need to return an `Option<usize>`.\n#[inline]\n#[allow(clippy::unnecessary_wraps)]\nfn seq_refcount<T: InternalStringType>(vtable: NonNull<JsStringVTable>) -> Option<usize> {\n    // SAFETY: This is part of the correct vtable which is validated on construction.\n    let this: &SequenceString<T> = unsafe { vtable.cast().as_ref() };\n    Some(this.refcount.get())\n}\n"
  },
  {
    "path": "core/string/src/vtable/slice.rs",
    "content": "use crate::iter::CodePointsIter;\nuse crate::vtable::JsStringVTable;\nuse crate::{JsStr, JsString, JsStringKind};\nuse std::cell::Cell;\nuse std::process::abort;\nuse std::ptr::NonNull;\n\n/// A slice of an existing string.\n#[repr(C)]\npub(crate) struct SliceString {\n    /// Embedded `VTable` - must be the first field for vtable dispatch.\n    vtable: JsStringVTable,\n    // Keep this for refcounting the original string.\n    owned: JsString,\n    // Pointer to the data itself. This is guaranteed to be safe as long as `owned` is\n    // owned.\n    inner: JsStr<'static>,\n    // Refcount for this string as we need to clone/drop it as well.\n    refcount: Cell<usize>,\n}\n\nimpl SliceString {\n    /// Create a new slice string given its members.\n    ///\n    /// # Safety\n    /// The caller is responsible for ensuring start and end are safe (`start` <= `end`,\n    /// `start` >= 0, `end` <= `owned.len()`).\n    #[inline]\n    #[must_use]\n    pub(crate) unsafe fn new(owned: &JsString, start: usize, end: usize) -> Self {\n        // SAFETY: invariant stated for this whole function.\n        let inner = unsafe { owned.as_str().get_unchecked(start..end) };\n        SliceString {\n            vtable: JsStringVTable {\n                clone: slice_clone,\n                drop: slice_drop,\n                as_str: slice_as_str,\n                code_points: slice_code_points,\n                refcount: slice_refcount,\n                len: end - start,\n                kind: JsStringKind::Slice,\n            },\n            owned: owned.clone(),\n            // SAFETY: this inner's lifetime is tied to the owned string above.\n            inner: unsafe { inner.as_static() },\n            refcount: Cell::new(1),\n        }\n    }\n\n    /// Returns the owned string as a const reference.\n    #[inline]\n    #[must_use]\n    pub(crate) fn owned(&self) -> &JsString {\n        &self.owned\n    }\n}\n\n#[inline]\npub(super) fn slice_clone(vtable: NonNull<JsStringVTable>) -> JsString {\n    // SAFETY: This is part of the correct vtable which is validated on construction.\n    let this: &SliceString = unsafe { vtable.cast().as_ref() };\n    let Some(strong) = this.refcount.get().checked_add(1) else {\n        abort();\n    };\n    this.refcount.set(strong);\n    // SAFETY: validated the string outside this function.\n    unsafe { JsString::from_ptr(vtable) }\n}\n\n#[inline]\nfn slice_drop(vtable: NonNull<JsStringVTable>) {\n    // SAFETY: This is part of the correct vtable which is validated on construction.\n    let this: &SliceString = unsafe { vtable.cast().as_ref() };\n    let Some(new) = this.refcount.get().checked_sub(1) else {\n        abort();\n    };\n    this.refcount.set(new);\n    if new != 0 {\n        return;\n    }\n\n    // SAFETY: This is the last reference, so we can deallocate.\n    // The vtable pointer is actually pointing to a SliceString, so cast it correctly.\n    unsafe {\n        drop(Box::from_raw(vtable.cast::<SliceString>().as_ptr()));\n    }\n}\n\n#[inline]\nfn slice_as_str(vtable: NonNull<JsStringVTable>) -> JsStr<'static> {\n    // SAFETY: This is part of the correct vtable which is validated on construction.\n    let this: &SliceString = unsafe { vtable.cast().as_ref() };\n    this.inner\n}\n\n#[inline]\nfn slice_code_points(vtable: NonNull<JsStringVTable>) -> CodePointsIter<'static> {\n    CodePointsIter::new(slice_as_str(vtable))\n}\n\n/// `VTable` function for refcount, need to return an `Option<usize>`.\n#[inline]\n#[allow(clippy::unnecessary_wraps)]\nfn slice_refcount(vtable: NonNull<JsStringVTable>) -> Option<usize> {\n    // SAFETY: This is part of the correct vtable which is validated on construction.\n    let this: &SliceString = unsafe { vtable.cast().as_ref() };\n    Some(this.refcount.get())\n}\n"
  },
  {
    "path": "core/string/src/vtable/static.rs",
    "content": "use crate::iter::CodePointsIter;\nuse crate::vtable::JsStringVTable;\nuse crate::{JsStr, JsString, JsStringKind};\nuse std::hash::{Hash, Hasher};\nuse std::ptr::NonNull;\n\n/// A static string with vtable for uniform dispatch.\n#[derive(Debug, Clone, Copy)]\n#[repr(C)]\npub struct StaticString {\n    /// Embedded `VTable` - must be the first field for vtable dispatch.\n    vtable: JsStringVTable,\n    /// The actual string data.\n    pub(crate) str: JsStr<'static>,\n}\n\nimpl StaticString {\n    /// Create a new static string.\n    #[must_use]\n    pub const fn new(str: JsStr<'static>) -> Self {\n        Self {\n            vtable: JsStringVTable {\n                clone: static_clone,\n                drop: static_drop,\n                as_str: static_as_str,\n                code_points: static_code_points,\n                refcount: static_refcount,\n                len: str.len(),\n                kind: JsStringKind::Static,\n            },\n            str,\n        }\n    }\n}\n\nimpl Hash for StaticString {\n    fn hash<H: Hasher>(&self, state: &mut H) {\n        self.str.hash(state);\n    }\n}\n\nimpl PartialEq for StaticString {\n    fn eq(&self, other: &Self) -> bool {\n        self.str == other.str\n    }\n}\n\nimpl Eq for StaticString {}\n\nimpl std::borrow::Borrow<JsStr<'static>> for &'static StaticString {\n    fn borrow(&self) -> &JsStr<'static> {\n        &self.str\n    }\n}\n\n#[inline]\npub(crate) fn static_clone(this: NonNull<JsStringVTable>) -> JsString {\n    // Static strings don't need ref counting, just copy the pointer.\n    // SAFETY: validated the string outside this function.\n    unsafe { JsString::from_ptr(this) }\n}\n\n#[inline]\nfn static_drop(_ptr: NonNull<JsStringVTable>) {\n    // Static strings don't need cleanup.\n}\n\n#[inline]\nfn static_as_str(this: NonNull<JsStringVTable>) -> JsStr<'static> {\n    // SAFETY: validated the string outside this function.\n    let this: &StaticString = unsafe { this.cast().as_ref() };\n    this.str\n}\n\n#[inline]\nfn static_code_points(this: NonNull<JsStringVTable>) -> CodePointsIter<'static> {\n    CodePointsIter::new(static_as_str(this))\n}\n\n#[inline]\nfn static_refcount(_ptr: NonNull<JsStringVTable>) -> Option<usize> {\n    // Static strings don't have refcount.\n    None\n}\n"
  },
  {
    "path": "core/wintertc/ABOUT.md",
    "content": "# About Boa\n\nBoa is an open-source, experimental ECMAScript Engine written in Rust for\nlexing, parsing and executing ECMAScript/JavaScript. Currently, Boa supports some\nof the [language][boa-conformance]. More information can be viewed at [Boa's\nwebsite][boa-web].\n\nTry out the most recent release with Boa's live demo\n[playground][boa-playground].\n\n## Boa Crates\n\n- [**`boa_cli`**][cli] - Boa's CLI && REPL implementation\n- [**`boa_ast`**][ast] - Boa's ECMAScript Abstract Syntax Tree.\n- [**`boa_engine`**][engine] - Boa's implementation of ECMAScript builtin objects and execution.\n- [**`boa_gc`**][gc] - Boa's garbage collector.\n- [**`boa_icu_provider`**][icu] - Boa's ICU4X data provider.\n- [**`boa_interner`**][interner] - Boa's string interner.\n- [**`boa_macros`**][macros] - Boa's macros.\n- [**`boa_parser`**][parser] - Boa's lexer and parser.\n- [**`boa_runtime`**][runtime] - Boa's `WebAPI` features.\n- [**`boa_string`**][string] - Boa's ECMAScript string implementation.\n- [**`boa_wintertc`**][wintertc] - Boa's `WinterTC` (TC55) Minimum Common Web API implementation.\n- [**`tag_ptr`**][tag_ptr] - Utility library that enables a pointer to be associated with a tag of type `usize`.\n- [**`small_btree`**][small_btree] - Utility library that adds the `SmallBTreeMap` data structure.\n\n[boa-conformance]: https://boajs.dev/conformance\n[boa-web]: https://boajs.dev/\n[boa-playground]: https://boajs.dev/playground\n[ast]: https://docs.rs/boa_ast/latest/boa_ast/index.html\n[engine]: https://docs.rs/boa_engine/latest/boa_engine/index.html\n[gc]: https://docs.rs/boa_gc/latest/boa_gc/index.html\n[interner]: https://docs.rs/boa_interner/latest/boa_interner/index.html\n[parser]: https://docs.rs/boa_parser/latest/boa_parser/index.html\n[icu]: https://docs.rs/boa_icu_provider/latest/boa_icu_provider/index.html\n[runtime]: https://docs.rs/boa_runtime/latest/boa_runtime/index.html\n[string]: https://docs.rs/boa_string/latest/boa_string/index.html\n[wintertc]: https://docs.rs/boa_wintertc/latest/boa_wintertc/index.html\n[tag_ptr]: https://docs.rs/tag_ptr/latest/tag_ptr/index.html\n[small_btree]: https://docs.rs/small_btree/latest/small_btree/index.html\n[macros]: https://docs.rs/boa_macros/latest/boa_macros/index.html\n[cli]: https://crates.io/crates/boa_cli\n"
  },
  {
    "path": "core/wintertc/Cargo.toml",
    "content": "[package]\nname = \"boa_wintertc\"\ndescription = \"WinterTC (TC55) Minimum Common Web API implementation for the Boa JavaScript engine.\"\nkeywords = [\"javascript\", \"js\", \"runtime\", \"tc55\", \"wintertc\"]\ncategories = [\"wasm\", \"web-programming\"]\nversion.workspace = true\nedition.workspace = true\nauthors.workspace = true\nlicense.workspace = true\nrepository.workspace = true\nrust-version.workspace = true\n\n[dependencies]\nboa_engine.workspace = true\n\n[lints]\nworkspace = true\n\n[package.metadata.docs.rs]\nall-features = true\n\n[features]\ndefault = []\nurl = []\nfetch = []\n"
  },
  {
    "path": "core/wintertc/src/abort/mod.rs",
    "content": "//! TC55 `AbortController` and `AbortSignal` implementation.\n//!\n//! Spec: <https://dom.spec.whatwg.org/#interface-abortcontroller>\n//!\n//! # TC55 Status\n//!\n//! `AbortController` and `AbortSignal` are required in the `WinterTC` TC55 Minimum Common Web API.\n//!\n//! # TODO\n//!\n//! - Migrate `AbortController` and `AbortSignal` from `boa_runtime::abort`.\n\n/// Register `AbortController` and `AbortSignal` into the given context.\n///\n/// # Errors\n///\n/// Returns a [`boa_engine::JsError`] if registration fails.\npub fn register(\n    _realm: Option<boa_engine::realm::Realm>,\n    _ctx: &mut boa_engine::Context,\n) -> boa_engine::JsResult<()> {\n    Ok(())\n}\n"
  },
  {
    "path": "core/wintertc/src/base64/mod.rs",
    "content": "//! TC55 `atob` / `btoa` implementation.\n//!\n//! Spec: <https://html.spec.whatwg.org/multipage/webappapis.html#atob>\n//!\n//! # TC55 Status\n//!\n//! `atob` and `btoa` are required in the `WinterTC` TC55 Minimum Common Web API.\n//!\n//! # TODO\n//!\n//! - Migrate `atob` and `btoa` from `boa_runtime::base64`.\n\n/// Register `atob` and `btoa` into the given context.\n///\n/// # Errors\n///\n/// Returns a [`boa_engine::JsError`] if registration fails.\npub fn register(\n    _realm: Option<boa_engine::realm::Realm>,\n    _ctx: &mut boa_engine::Context,\n) -> boa_engine::JsResult<()> {\n    Ok(())\n}\n"
  },
  {
    "path": "core/wintertc/src/clone/mod.rs",
    "content": "//! TC55 `structuredClone` implementation.\n//!\n//! Spec: <https://html.spec.whatwg.org/multipage/structured-data.html#dom-structuredclone>\n//!\n//! # TC55 Status\n//!\n//! `structuredClone` is required in the `WinterTC` TC55 Minimum Common Web API.\n//!\n//! # TODO\n//!\n//! - Migrate `structuredClone` from `boa_runtime::clone`.\n\n/// Register `structuredClone` into the given context.\n///\n/// # Errors\n///\n/// Returns a [`boa_engine::JsError`] if registration fails.\npub fn register(\n    _realm: Option<boa_engine::realm::Realm>,\n    _ctx: &mut boa_engine::Context,\n) -> boa_engine::JsResult<()> {\n    Ok(())\n}\n"
  },
  {
    "path": "core/wintertc/src/console/mod.rs",
    "content": "//! TC55 `console` object implementation.\n//!\n//! Spec: <https://console.spec.whatwg.org/>\n//!\n//! # TC55 Status\n//!\n//! `console` is required in the `WinterTC` TC55 Minimum Common Web API.\n//!\n//! # TODO\n//!\n//! - Migrate `Console` from `boa_runtime::console`.\n\n/// Register the `console` object into the given context.\n///\n/// # Errors\n///\n/// Returns a [`boa_engine::JsError`] if registration fails.\npub fn register(\n    _realm: Option<boa_engine::realm::Realm>,\n    _ctx: &mut boa_engine::Context,\n) -> boa_engine::JsResult<()> {\n    Ok(())\n}\n"
  },
  {
    "path": "core/wintertc/src/encoding/mod.rs",
    "content": "//! TC55 encoding APIs: `TextEncoder`, `TextDecoder`, `TextEncoderStream`, `TextDecoderStream`.\n//!\n//! Spec: <https://encoding.spec.whatwg.org/>\n//!\n//! # TC55 Status\n//!\n//! `TextEncoder`, `TextDecoder`, `TextEncoderStream`, and `TextDecoderStream` are required\n//! in the `WinterTC` TC55 Minimum Common Web API.\n//!\n//! # TODO\n//!\n//! - Migrate `TextEncoder` and `TextDecoder` from `boa_runtime::text`.\n//! - Implement `TextEncoderStream`.\n//! - Implement `TextDecoderStream`.\n\n/// Register encoding globals (`TextEncoder`, `TextDecoder`, `TextEncoderStream`,\n/// `TextDecoderStream`) into the given context.\n///\n/// # Errors\n///\n/// Returns a [`boa_engine::JsError`] if registration fails.\npub fn register(\n    _realm: Option<boa_engine::realm::Realm>,\n    _ctx: &mut boa_engine::Context,\n) -> boa_engine::JsResult<()> {\n    Ok(())\n}\n"
  },
  {
    "path": "core/wintertc/src/events/mod.rs",
    "content": "//! TC55 event APIs: `EventTarget`, `Event`, `CustomEvent`, `ErrorEvent`, `MessageEvent`.\n//!\n//! Spec: <https://dom.spec.whatwg.org/#interface-eventtarget>\n//!\n//! # TC55 Status\n//!\n//! `EventTarget` and associated event interfaces are required in the `WinterTC` TC55\n//! Minimum Common Web API.\n//!\n//! # TODO\n//!\n//! - Implement `EventTarget`.\n//! - Implement `Event`.\n//! - Implement `CustomEvent`.\n//! - Implement `ErrorEvent`.\n//! - Implement `MessageEvent`.\n\n/// Register event globals (`EventTarget`, `Event`, `CustomEvent`, `ErrorEvent`,\n/// `MessageEvent`) into the given context.\n///\n/// # Errors\n///\n/// Returns a [`boa_engine::JsError`] if registration fails.\npub fn register(\n    _realm: Option<boa_engine::realm::Realm>,\n    _ctx: &mut boa_engine::Context,\n) -> boa_engine::JsResult<()> {\n    Ok(())\n}\n"
  },
  {
    "path": "core/wintertc/src/fetch/mod.rs",
    "content": "//! TC55 Fetch API: `fetch`, `Request`, `Response`, `Headers`.\n//!\n//! Spec: <https://fetch.spec.whatwg.org/>\n//!\n//! # TC55 Status\n//!\n//! The Fetch API is required in the `WinterTC` TC55 Minimum Common Web API.\n//!\n//! # TODO\n//!\n//! - Migrate `fetch`, `Request`, `Response`, and `Headers` from `boa_runtime::fetch`.\n\n/// Register `fetch`, `Request`, `Response`, and `Headers` into the given context.\n///\n/// # Errors\n///\n/// Returns a [`boa_engine::JsError`] if registration fails.\n#[cfg(feature = \"fetch\")]\npub fn register(\n    _realm: Option<boa_engine::realm::Realm>,\n    _ctx: &mut boa_engine::Context,\n) -> boa_engine::JsResult<()> {\n    Ok(())\n}\n"
  },
  {
    "path": "core/wintertc/src/lib.rs",
    "content": "//! Boa's **`boa_wintertc`** crate implements the [WinterTC (TC55) Minimum Common Web API](https://min-common-api.proposal.wintertc.org/)\n//! for the `boa_engine` crate.\n//!\n//! `WinterTC` (TC55) is an Ecma International Technical Committee working towards a baseline set\n//! of Web Platform APIs that all server-side JavaScript runtimes (Deno, Bun, Cloudflare Workers,\n//! Node.js, etc.) agree to implement, enabling portable server-side JavaScript.\n//!\n//! # Relationship to `boa_runtime`\n//!\n//! `boa_wintertc` is a standalone crate that depends only on `boa_engine`.\n//! `boa_runtime` depends on `boa_wintertc` and re-exports its APIs, so users of `boa_runtime`\n//! automatically get TC55 compliance without any extra setup.\n//!\n//! If you only want the TC55-mandated APIs and nothing else, depend on `boa_wintertc` directly.\n//!\n//! # Example: Registering all TC55 APIs\n//!\n//! ```no_run\n//! use boa_engine::Context;\n//!\n//! let mut context = Context::default();\n//!\n//! boa_wintertc::register(None, &mut context)\n//!     .expect(\"failed to register TC55 APIs\");\n//! ```\n#![doc = include_str!(\"../ABOUT.md\")]\n#![doc(\n    html_logo_url = \"https://raw.githubusercontent.com/boa-dev/boa/main/assets/logo_black.svg\",\n    html_favicon_url = \"https://raw.githubusercontent.com/boa-dev/boa/main/assets/logo_black.svg\"\n)]\n#![cfg_attr(not(test), forbid(clippy::unwrap_used))]\n#![allow(\n    clippy::module_name_repetitions,\n    clippy::redundant_pub_crate,\n    clippy::let_unit_value\n)]\n\npub mod abort;\npub mod base64;\npub mod clone;\npub mod console;\npub mod encoding;\npub mod events;\n#[cfg(feature = \"fetch\")]\npub mod fetch;\npub mod microtask;\npub mod timers;\n#[cfg(feature = \"url\")]\npub mod url;\n\n/// Register all TC55-mandated Web APIs into the given [`boa_engine::Context`].\n///\n/// This registers the Minimum Common Web API as specified by `WinterTC` (TC55):\n/// <https://min-common-api.proposal.wintertc.org/>\n///\n/// # Errors\n///\n/// Returns a [`boa_engine::JsError`] if any API fails to register (e.g. a global\n/// object already exists with a conflicting name).\n#[allow(clippy::needless_pass_by_value)]\npub fn register(\n    realm: Option<boa_engine::realm::Realm>,\n    ctx: &mut boa_engine::Context,\n) -> boa_engine::JsResult<()> {\n    console::register(realm.clone(), ctx)?;\n    timers::register(realm.clone(), ctx)?;\n    encoding::register(realm.clone(), ctx)?;\n    microtask::register(realm.clone(), ctx)?;\n    clone::register(realm.clone(), ctx)?;\n    base64::register(realm.clone(), ctx)?;\n    abort::register(realm.clone(), ctx)?;\n    #[cfg(feature = \"url\")]\n    url::register(realm.clone(), ctx)?;\n    #[cfg(feature = \"fetch\")]\n    fetch::register(realm, ctx)?;\n\n    Ok(())\n}\n"
  },
  {
    "path": "core/wintertc/src/microtask/mod.rs",
    "content": "//! TC55 `queueMicrotask` implementation.\n//!\n//! Spec: <https://html.spec.whatwg.org/multipage/timers-and-user-prompts.html#microtask-queuing>\n//!\n//! # TC55 Status\n//!\n//! `queueMicrotask` is required in the `WinterTC` TC55 Minimum Common Web API.\n//!\n//! # TODO\n//!\n//! - Migrate `queueMicrotask` from `boa_runtime::microtask`.\n\n/// Register `queueMicrotask` into the given context.\n///\n/// # Errors\n///\n/// Returns a [`boa_engine::JsError`] if registration fails.\npub fn register(\n    _realm: Option<boa_engine::realm::Realm>,\n    _ctx: &mut boa_engine::Context,\n) -> boa_engine::JsResult<()> {\n    Ok(())\n}\n"
  },
  {
    "path": "core/wintertc/src/timers/mod.rs",
    "content": "//! TC55 timer APIs: `setTimeout`, `clearTimeout`, `setInterval`, `clearInterval`.\n//!\n//! Spec: <https://html.spec.whatwg.org/multipage/timers-and-user-prompts.html#timers>\n//!\n//! # TC55 Status\n//!\n//! Timer APIs are required in the `WinterTC` TC55 Minimum Common Web API.\n//!\n//! # TODO\n//!\n//! - Migrate `setTimeout`, `clearTimeout`, `setInterval`, `clearInterval` from `boa_runtime::interval`.\n\n/// Register timer globals (`setTimeout`, `clearTimeout`, `setInterval`, `clearInterval`)\n/// into the given context.\n///\n/// # Errors\n///\n/// Returns a [`boa_engine::JsError`] if registration fails.\npub fn register(\n    _realm: Option<boa_engine::realm::Realm>,\n    _ctx: &mut boa_engine::Context,\n) -> boa_engine::JsResult<()> {\n    Ok(())\n}\n"
  },
  {
    "path": "core/wintertc/src/url/mod.rs",
    "content": "//! TC55 URL APIs: `URL`, `URLSearchParams`.\n//!\n//! Spec: <https://url.spec.whatwg.org/>\n//!\n//! # TC55 Status\n//!\n//! `URL` and `URLSearchParams` are required in the `WinterTC` TC55 Minimum Common Web API.\n//!\n//! # TODO\n//!\n//! - Migrate `URL` from `boa_runtime::url`.\n//! - Implement `URLSearchParams`.\n\n/// Register `URL` and `URLSearchParams` into the given context.\n///\n/// # Errors\n///\n/// Returns a [`boa_engine::JsError`] if registration fails.\n#[cfg(feature = \"url\")]\npub fn register(\n    _realm: Option<boa_engine::realm::Realm>,\n    _ctx: &mut boa_engine::Context,\n) -> boa_engine::JsResult<()> {\n    Ok(())\n}\n"
  },
  {
    "path": "docs/boa_object.md",
    "content": "# Boa Debug Object\n\nThe `$boa` object contains useful utilities that can be used to debug JavaScript in JavaScript.\n\nIt's injected into the context as global variable with the `--debug-object` command-line flag,\nthe object is separated into modules.\n\n## Module `$boa.gc`\n\nThis module contains functions that are related the garbage collector. It currently has the `.collect()` method.\n\n```JavaScript\n$boa.gc.collect()\n```\n\nThis force triggers the GC to scan the heap and collect garbage.\n\n## Module `$boa.function`\n\nIn this module are utility functions related to execution and debugging function.\n\n### Function `$boa.function.bytecode(func)`\n\nThis function returns the compiled bytecode of a function as a string,\n\n```JavaScript\n>> function add(x, y) {\n  return x + y\n}\n>> $boa.function.bytecode(add)\n\"\n------------------------Compiled Output: 'add'------------------------\nLocation  Count    Handler    Opcode                     Operands\n\n000000    0000      none      CreateMappedArgumentsObject\n000001    0001      none      PutLexicalValue                           2: 0\n000004    0002      none      GetArgument                           0\n000006    0003      none      PutLexicalValue                           2: 1\n000009    0004      none      GetArgument                           1\n000011    0005      none      PutLexicalValue                           2: 2\n000014    0006      none      PushDeclarativeEnvironment                           2\n000016    0007      none      GetName                           0000: 'x'\n000018    0008      none      GetName                           0001: 'y'\n000020    0009      none      Add\n000021    0010      none      SetAccumulatorFromStack\n000022    0011      none      CheckReturn\n000023    0012      none      Return\n000024    0013      none      CheckReturn\n000025    0014      none      Return\n\nConstants:\n    0000: [ENVIRONMENT] index: 1, bindings: 1\n    0001: [ENVIRONMENT] index: 2, bindings: 3\n    0002: [ENVIRONMENT] index: 3, bindings: 0\n\nBindings:\n    0000: x\n    0001: y\n\nHandlers: <empty>\n\"\n```\n\n### Function `$boa.function.trace(func, this, ...args)`\n\nIt only traces the specified function. If the specified function calls other functions,\ntheir instructions aren't traced.\n\n```JavaScript\n>> const add = (a, b) => a + b\n>> $boa.function.trace(add, undefined, 1, 2)\n5μs           DefInitArg                 0000: 'a'                  2\n4μs           DefInitArg                 0001: 'b'                  <empty>\n0μs           RestParameterPop                                      <empty>\n3μs           GetName                    0000: 'a'                  1\n1μs           GetName                    0001: 'b'                  2\n2μs           Add                                                   3\n1μs           Return                                                3\n3\n>>\n```\n\nThe `this` value can be changed as well as the arguments that are passed to the function.\n\n### Function `$boa.function.traceable(func, mode)`\n\nMarks a single function as traceable on all future executions of the function. Both useful to mark\nseveral functions as traceable and to trace functions that suspend their execution (async functions,\ngenerators, async generators).\n\n#### Input\n\n```Javascript\nfunction* g() {\n    yield 1;\n    yield 2;\n    yield 3;\n}\n$boa.function.traceable(g, true);\nvar iter = g();\niter.next();\niter.next();\niter.next();\n```\n\n#### Output\n\n```bash\n1μs           RestParameterPop                                      <empty>\n1μs           PushUndefined                                         undefined\n2μs           Yield                                                 undefined\n4μs           GetName                    0000: 'a'                  1\n0μs           Yield                                                 1\n1μs           GeneratorNext                                         undefined\n1μs           Pop                                                   <empty>\n15μs          GetName                    0001: 'b'                  2\n1μs           Yield                                                 2\n1μs           GeneratorNext                                         undefined\n1μs           Pop                                                   <empty>\n4μs           GetName                    0002: 'c'                  3\n1μs           Yield                                                 3\n```\n\n## Function `$boa.function.flowgraph(func, options)`\n\nIt can be used to get the instruction flowgraph, like the command-line flag.\nThis works on the function level, allows getting the flow graph without\nquitting the boa shell and adding the specified flags.\n\nBesides the function it also takes an argument that, can be a string or an object.\nIf it is a string it represents the flowgraph format, otherwire if it's an object:\n\n```JavaScript\n// These are the defaults, if not specified.\n{\n    format: 'mermaid'\n    direction: 'LeftRight' // or 'LR' shorthand.\n}\n```\n\nExample:\n\n```JavaScript\n$boa.function.flowgraph(func, 'graphviz')\n$boa.function.flowgraph(func, { format: 'mermaid', direction: 'TopBottom' })\n```\n\n## Module `$boa.object`\n\nContains utility functions for getting internal information about an object.\n\n## Function `$boa.object.id(object)`\n\nThis function returns memory address of the given object, as a string.\n\nExample:\n\n```JavaScript\nlet o = { x: 10, y: 20 }\n$boa.object.id(o)    // '0x7F5B3251B718'\n\n// Getting the address of the $boa object in memory\n$boa.object.id($boa) // '0x7F5B3251B5D8'\n```\n\n## Function `$boa.object.indexedStorageType(object)`\n\nThis function returns indexed storage type.\n\nExample:\n\n```JavaScript\nlet a = [1, 2];\n\n$boa.object.indexedStorageType(a); // 'DenseI32'\n\na.push(0xdeadbeef);\n$boa.object.indexedStorageType(a); // 'DenseI32'\n\na.push(0.5);\n$boa.object.indexedStorageType(a); // 'DenseF64'\n\na.push(\"Hello\");\n$boa.object.indexedStorageType(a); // 'DenseElement'\n\na[100] = 100; // Make a hole\n$boa.object.indexedStorageType(a); // 'SparseElement'\n\n// Non-simple property descriptor (e.g., non-writable)\nObject.defineProperty(a, 2, { value: 10, writable: false });\n$boa.object.indexedStorageType(a); // 'SparseProperty'\n```\n\n## Module `$boa.optimizer`\n\nThis modules contains getters and setters for enabling and disabling optimizations.\n\n### Getter & Setter `$boa.optimizer.constantFolding`\n\nThis is and accessor property on the module, its getter returns `true` if enabled or `false` otherwise.\nIts setter can be used to enable/disable the constant folding optimization.\n\n```JavaScript\n$boa.optimizer.constantFolding = true\n$boa.optimizer.constantFolding // true\n```\n\n### Getter & Setter `$boa.optimizer.statistics`\n\nThis is an accessor property on the module, its getter returns `true` if enabled or `false` otherwise.\nIts setter can be used to enable/disable optimization statistics, which are printed to `stdout`.\n\n```JavaScript\n>> $boa.optimizer.constantFolding = true\n>> $boa.optimizer.statistics = true\n>> 1 + 1\nOptimizer {\n    constant folding: 1 run(s), 2 pass(es) (1 mutating, 1 checking)\n}\n\n2\n>>\n```\n\n## Module `$boa.realm`\n\nThis module contains realm utilities to test cross-realm behaviour.\n\n### `$boa.realm.create`\n\nCreates a new realm with a new set of builtins and returns its global object.\n\n```javascript\nlet global = $boa.realm.create();\n\nObject != global.Object; // true\n```\n\n## Module `$boa.shape`\n\nThis module contains helpful functions for getting information about a shape of an object.\n\n### Function `$boa.shape.id(object)`\n\nReturns the pointer of the object's shape in memory as a string encoded in hexadecimal format.\n\n```JavaScript\n$boa.shape.id(Number) // '0x7FC35A073868'\n$boa.shape.id({}) // '0x7FC35A046258'\n```\n\n### Function `$boa.shape.type(object)`\n\nReturns the object's shape type.\n\n```JavaScript\n$boa.shape.type({x: 3}) // 'shared'\n$boa.shape.type(Number) // 'unique'\n```\n\n### Function `$boa.shape.same(o1, o2)`\n\nReturns `true` if both objects have the same shape.\n\n```JavaScript\n// The values of the properties are not important!\nlet o1 = { x: 10 }\nlet o2 = {}\n$boa.shape.same(o1, o2) // false\n\no2.x = 20\n$boa.shape.same(o1, o2) // true\n\no2.y = 200\n$boa.shape.same(o1, o2) // false\n```\n\n## Module `$boa.limits`\n\nThis module contains utilities for changing runtime limits.\n\n### Getter & Setter `$boa.limits.loop`\n\nThis is an accessor property on the module, its getter returns the loop iteration limit before an error is thrown.\nIts setter can be used to set the loop iteration limit.\n\n```javascript\n$boa.limits.loop = 10;\n\nwhile (true) {} // RuntimeLimit: Maximum loop iteration limit 10 exceeded\n```\n\n### Getter & Setter `$boa.limits.stack`\n\nThis is an accessor property on the module, its getter returns the value stack limit before an error is thrown.\nIts setter can be used to set the recursion limit.\n\n```javascript\n$boa.limits.stack = 10;\n\nfunction x() {\n  return;\n}\nx(1, 2, 3, 4, 5, 6, 7, 8, 9, 10); // RuntimeLimit: exceeded maximum call stack length\n```\n\n### Getter & Setter `$boa.limits.recursion`\n\nThis is an accessor property on the module, its getter returns the recursion limit before an error is thrown.\nIts setter can be used to set the recursion limit.\n\n```javascript\n$boa.limits.recursion = 100;\n\nfunction x() {\n  return x();\n}\nx(); // RuntimeLimit: Maximum recursion limit 100 exceeded\n```\n\n### Getter & Setter `$boa.limits.backtrace`\n\nThis is an accessor property on the module, its getter returns the backtrace limit for a thrown error.\nIts setter can be used to set the backtrace limit.\n\n```javascript\n$boa.limits.backtrace = 100;\n\nfunction x() {\n  function y() {\n    function z() {\n      throw \"Hello\";\n    }\n    z();\n  }\n  y();\n}\nx();\n\n// Uncaught \"Hello\"\n//     at z (test.js:6:13)\n//     at y (test.js:8:6)\n//     at x (test.js:10:4)\n//     at <main> (test.js:12:2)\n```\n\n## Module `$boa.string`\n\nThis module contains helpful functions for getting information about a strings.\n\n### Function `$boa.string.storage(str)`\n\nReturns the string's inner storage type, if it's a well known string that is stored in the `STATIC_STRINGS` array in boa,\nthen `\"static\"` is returned, `\"heap\"` otherwise.\n\n```JavaScript\n$boa.string.storage(\"push\")             // \"static\"\n$boa.string.storage(\"specialFunction\")  // \"heap\"\n```\n\n### Function `$boa.string.encoding(str)`\n\nReturns the string's inner encoding of the string.\n\n```JavaScript\n$boa.string.encoding(\"Greeting\") // \"latin1\"\n$boa.string.encoding(\"挨拶\")      // \"utf16\"\n```\n\n### Function `$boa.string.summary(str)`\n\nReturns an object with a short summary of the of the given string.\n\n```JavaScript\n$boa.string.summary(\"Greeting\") // { storage: \"heap\", encoding: \"latin1\" }\n```\n"
  },
  {
    "path": "docs/bytecompiler.md",
    "content": "# Byte Compiler\n\n## Architecture\n\nThe bytecompiler is responsible for lowering ECMAScript AST nodes (from `boa_ast`) into executable bytecode for the virtual machine.\n\nIt traverses the parsed AST and emits instructions into a `CodeBlock` using the `ByteCodeEmitter`. The resulting bytecode is later executed by the VM.\n\nDuring compilation, the bytecompiler performs several tasks:\n\n- Instruction emission\n- Register allocation\n- Lexical and variable scope management\n- Binding resolution\n- Control-flow generation (jumps and exception handlers)\n- Collection of constants and literals\n\nThe produced `CodeBlock` contains the instructions, literal tables, bindings, and handler metadata required for execution.\n\nThe flow of the compilation process is as follows:\n\n```bash\nAST → ByteCompiler → CodeBlock → VM\n```\n\n## Compilation Model\n\nThe bytecompiler performs a lowering step from high-level ECMAScript AST nodes into a lower-level bytecode instruction set understood by the VM.\n\nEach major AST category — such as `expression`, `statement`, and `declaration` - has corresponding compilation logic that emits one or more bytecode instructions.\n\nThe compiler operates in a **single pass** over the AST, generating instructions as it traverses nodes.\n\n## Execution Model\n\nBoa uses a register-based bytecode model. Instructions operate on virtual registers instead of stack-based operations.\n\nIf you see in here the struct `ByteCompiler` contains the register allocation field.\n\n```rust\npub(crate) register_allocator: RegisterAllocator,\n```\n\nRegisters are allocated during the compilation process via `RegisterAllocator`.\n\n## Module Organization\n\nThe bytecompiler module is structured according to ECMAScript syntactic categories:\n\n- `expression/` - compilation of expression nodes (e.g., `Binary`, `Unary`)\n- `statement/` - compilation of statements and control flow (e.g., `If`, `For`, `While`)\n- `declaration/` - handling of variable and function declarations (including patterns)\n\nSupporting modules include:\n\n- `register.rs` - register allocation logic\n- `jump_control.rs` - management of jump targets and control flow\n- `function.rs` - function compilation\n- `class.rs` - class compilation\n- `module.rs` - ECMAScript module compilation\n- `env/` - management of environments and scope-related data\n\nThe `mod.rs` file coordinates the overall compilation process.\n\n## Identifier and Symbol Handling\n\nThe compiler relies on a string interner to store and resolve identifiers efficiently:\n\n```rust\npub(crate) interner: &'ctx mut Interner\n```\n\nIdentifiers are interned and referenced by symbols (`Sym`) rather than raw strings. This avoids repeated heap allocations for the same identifier and allows O(1) comparisons by symbol index.\n\n## Scope and Binding Resolution\n\nThe compiler maintains two separate scopes during AST traversal:\n\n```rust\n/// The current variable scope.\npub(crate) variable_scope: Scope,\n\n/// The current lexical scope.\npub(crate) lexical_scope: Scope,\n```\n\n- `variable_scope` tracks `var` declared bindings, which are function-scoped.\n- `lexical_scope` tracks `let` and `const` declared bindings, which are block-scoped.\n\nBindings are resolved using `BindingLocator` structures, and scope information is embedded into the resulting `CodeBlock` for runtime environment resolution.\n\n## Constants and Literal Collections\n\nDuring compilation, the bytecompiler collects values that cannot be embedded directly into instructions - such as strings, numbers, and scope objects - into a `constants` vector in the `CodeBlock`.\n\nInstead of embedding these values inline, the compiler stores them in `constants` and emits the index as the instruction operand. The VM then looks up the value by index at runtime.\n\nFor example, when pushing a new scope, the compiler stores it in `constants` and emits the index:\n\n```rust\nlet index = self.constants.len() as u32;\nself.constants.push(Constant::Scope(scope.clone()));\n```\n\nThis keeps instructions compact and uniform in size, while still allowing the VM to access arbitrarily complex values.\n\n## Jump Patching and Control Flow\n\nWhen compiling control flow constructs like `if`, `while`, or `for`, the compiler needs to emit jump instructions before the jump target is known - because the target code hasn't been compiled yet.\n\nTo handle this, the compiler emits the jump with a placeholder address, then comes back and fills in the real address once the target location is known. This process is called jump patching.\n\nThe `jump_control.rs` module manages jump targets, labels, and the bookkeeping required to patch jumps correctly. This includes:\n\n- Forward jumps (e.g., jumping past an if block)\n- Loop back-edges (e.g., jumping back to the top of a while)\n- Break and continue targets\n\n## Async and Generator Functions\n\nAsync functions and generators are **not** lowered to explicit state machines at the AST level. Instead, the compiler emits handler metadata and suspend/resume points that allow the VM to pause and continue execution across `await` expressions and `yield` statements.\n\nThe presence of an active async handler is tracked via:\n\n```rust\npub(crate) async_handler: Option,\n```\n\nWhen set, this indicates the index of the handler responsible for managing suspension. The VM uses this information at runtime to correctly propagate values into and out of suspended frames.\n\n## Relationship to the Virtual Machine\n\nThe bytecompiler produces `CodeBlock` structures which contain the instructions and metadata required for execution.\n\nThe VM interprets these instructions at runtime. For execution details, see the VM documentation (`vm.md`).\n"
  },
  {
    "path": "docs/debugging.md",
    "content": "# Debugging\n\nThere are multiple ways to debug what Boa is doing. Or maybe you just want to\nknow how it works under the hood. Or even test some JavaScript.\n\nOne way to do so is to create a file in the root of the repository. For example\n`test.js`. Then execute `cargo run -- test.js` to run the file with boa. You can\ncompile a list of JavaScript files by running `cargo run -- file1.js file2.js`\nand so on.\n\nYou can also run boa interactively by simply calling `cargo run` without any\narguments to start a shell to execute JS.\n\nThese are added in order of how the code is read:\n\n## Tokens and AST nodes\n\nThe first thing boa will do is to generate tokens from the source code.\nThese tokens are then parsed into an abstract syntax tree (AST).\nAny syntax errors should be thrown while the AST is generated.\n\nYou can use the `boa_cli` command-line flag `--dump-ast` to print the AST.\nThe flag supports these formats: `Debug`, `Json`, `JsonPretty`. By default\nit is the `Debug` format.\n\nDumping the AST of a file:\n\n```bash\ncargo run -- test.js --dump-ast # AST dump format is Debug by default.\n```\n\nor with interactive mode (REPL):\n\n```bash\ncargo run -- --dump-ast # AST dump format is Debug by default.\n```\n\n## Bytecode generation and Execution\n\nOnce the AST has been generated, boa will compile it into bytecode, which is then executed by the VM.\nYou can print the bytecode and the executed instructions with the command-line flag `--trace`.\n\nFor more detailed information about the VM and the trace output look [here](./vm.md).\n\n## Instruction flowgraph\n\nWe can also get the VM instructions flowgraph, which is a visual representation of the instruction flow.\n\nThe `Start` (in green) and `End` (in red) node in the graph represents the start and end point of execution.\nThey are not instructions, just markers.\n\nThe conditional instructions are diamond shaped, with the `\"YES\"` branch in green and the `\"NO\"` branch in red.\nThe push and pop environment pairs match colors and are connected by a dotted line.\n\nYou can use the `--flowgraph` (or `--flowgraph=mermaid` for [mermaid][mermaid] format) flag which outputs\n[graphviz][graphviz] format by default, and pipe it to `dot` (from the `graphviz` package which is installed\non most linux distros by default) or use an online editor like: <https://dreampuf.github.io/GraphvizOnline> to\nview the graph.\n\n```bash\ncargo run -- test.js --flowgraph | dot -Tpng > test.png\n```\n\nYou can specify the `-Tsvg` to generate a `svg` instead of a `png` file.\n\n![Graphviz flowgraph](./img/graphviz_flowgraph.svg)\n\nMermaid graphs can be displayed on github [natively without third-party programs][gihub-mermaid].\nBy using a `mermaid` block as seen below.\n\n````\n```mermaid\n// graph contents here...\n```\n````\n\nAdditionally you can specify the direction of \"flow\" by using the `--flowgraph-direction` cli option,\nfor example `--flowgraph-direction=left-to-right`, the default is `top-to-bottom`.\n\n[mermaid]: https://mermaid-js.github.io/\n[gihub-mermaid]: https://docs.github.com/en/get-started/writing-on-github/working-with-advanced-formatting/creating-diagrams\n[graphviz]: https://graphviz.org/\n\n## Debugging through the debug object $boa\n\nCertain debugging actions in JavaScript land are difficult to impossible, like triggering a GC collect.\n\nFor such purposes we have the `$boa` object that contains useful utilities that can be used to debug JavaScript in JavaScript.\nThe debug object becomes available with the `--debug-object` cli flag, It injects the `$boa` debug object in the context as global variable,\nthe object is separated into modules `gc`, `function`, `object`, etc.\n\nWe can now do `$boa.gc.collect()`, which force triggers a GC collect.\n\nIf you want to trace only a particular function (without being flooded by the `--trace` flag, that traces everything),\nfor that we have the `$boa.function.trace(func, this, ...args)`.\n\nThe full documentation of the `$boa` object's modules and functionalities can be found [`here`](./boa_object.md).\n\n## Compiler panics\n\nIn the case of a compiler panic, to get a full backtrace you will need to set\nthe environment variable `RUST_BACKTRACE=1`.\n\n## Debugger\n\n### VS Code Debugger\n\nThe quickest way to get debugging is to use the CodeLLDB plugin and add breakpoints. You can get\nmore information [here][blog_debugging].\n\n### LLDB Manual debugging\n\nYou can also use rust-lldb. You should be able to use that environment to run your code.\n\n```\nrust-lldb ./target/debug/boa [arguments]\n```\n\n[remote_containers]: https://marketplace.visualstudio.com/items?itemName=ms-vscode-remote.remote-containers\n[blog_debugging]: https://jason-williams.co.uk/debugging-rust-in-vscode\n"
  },
  {
    "path": "docs/native_object.md",
    "content": "# `NativeObject` Design Document\n\n## Overview\n\nIn the Boa JavaScript engine, a lot of built-in objects (like Maps, Sets, or Date) and user-defined host objects require holding underlying Rust data structures.\n\nThe `NativeObject` trait serves as a bridge between the JavaScript object system and raw Rust types. It allows any arbitrary piece of Rust data to be securely passed around, managed, and garbage-collected as a JavaScript object.\n\n## The `JsData` Trait\n\nFor a Rust type to be treated as a `NativeObject`, it must implement the `JsData` trait, alongside `Any` and `Trace`.\n\nThe `JsData` trait acts as a marker for types that can be stored securely inside a `JsObject`. Furthermore, the `Trace` and `Finalize` traits (from the `boa_gc` crate) allow the Garbage Collector to manage the memory of these Rust objects seamlessly.\n\nMany standard Rust types have blanket implementations of `JsData` provided out of the box (e.g., `String`, `Vec<T>`, numerical types, `HashMap`, etc.), allowing developers to easily wrap basic data easily.\n\n## Memory Management and Alignment (`ObjectData<T>`)\n\nWhen a `JsData` object is wrapped into a JavaScript object, it is stored inside the `ObjectData<T>` struct.\n\n```rust\n#[derive(Debug, Finalize, Trace)]\n#[boa_gc(unsafe_no_drop)]\n#[repr(C, align(8))]\npub(crate) struct ObjectData<T: ?Sized> {\n    data: T,\n}\n```\n\nA crucial detail here is the `#[repr(C, align(8))]` annotation. It forces the compiler to align the wrapped Rust data to exactly 8 bytes. `ObjectData<T>` includes a compile-time static assertion (`static_assertions::const_assert!(align_of::<Box<()>>() <= 8)`) which enforces that any `JsData` type must have an alignment of 8 bytes or less (if it's larger, it's recommended to wrap the data in a `Box<T>`).\n\nThis strict alignment is required to ensure memory safety when casting pointers back and forth inside the garbage-collected environment and ensuring the GC metadata pointers maintain 8-byte alignment requirements on all architectures.\n\n## Integration with `JsObject`\n\nCreating and interacting with native objects involves a few core `JsObject` methods:\n\n### Creating a Native Object\n\nA native object is typically constructed by combining an ECMAScript prototype and the raw Rust data using `JsObject::from_proto_and_data`:\n\n```rust\nlet my_data = CustomStruct { ... };\nlet object = JsObject::from_proto_and_data(Some(prototype), my_data);\n```\n\n### Retrieving and Mutating the Data\n\nOnce wrapped, the underlying Rust data can be accessed safely via downcasting:\n\n1. `object.downcast::<T>()` -> Returns `Result<JsObject<T>, Self>`, converting the object into a typed `JsObject<T>` that can be borrowed later without losing the type information.\n2. `object.downcast_ref::<T>()` -> Returns `Option<&T>` representing an immutable reference to the native object data if the types match.\n3. `object.downcast_mut::<T>()` -> Returns `Option<&mut T>` representing a mutable reference to the native object data.\n4. `object.borrow_mut()` -> Often used when working closely with garbage collected `NativeObject`s that need interior mutability inside contexts.\n\nBy using `NativeObject`, Boa provides an ergonomic, type-safe, and memory-safe interface for extending ECMAScript objects with powerful native Rust functionality.\n"
  },
  {
    "path": "docs/profiling.md",
    "content": "# Profiling\n\n![Example](img/profiler.png)\n\nBoa can be profiled to understand where time is being spent during execution. This is useful for optimizing performance or diagnosing bottlenecks in script execution.\n\nWe recommend using one of the following tools:\n\n- Flamegraph – generates a visual representation of the call stack, showing which functions consume the most CPU time.\n\n- Valgrind (Callgrind) – provides detailed execution profiles, which can be explored with tools like KCachegrind or QCachegrind.\n\nThese tools do not require any special feature flags or instrumentation in the codebase. They work by sampling or tracing the actual execution and provide a consistent view of performance.\n\n## More Info\n\n- https://blog.rust-lang.org/inside-rust/2020/02/25/intro-rustc-self-profile.html\n- https://github.com/brendangregg/Flamegraph\n- https://valgrind.org/docs/manual/index.html\n"
  },
  {
    "path": "docs/shapes.md",
    "content": "# Shapes (Hidden Classes)\n\nThe best way to explain object shapes is through examples. It all begins with the root shape.\n\n```mermaid\nflowchart LR\n    classDef New style id2 fill:#bbf,stroke:#f66,stroke-width:2px,color:#fff,stroke-dasharray: 5 5\n    style Root stroke:#000,stroke-width:5px\n    style PropertyTable fill:#071E22\n\n    Root(<b>Root Shape</b>\\n<b>Prototype:</i> <i>None</i>) -->| Property Count 0 | PropertyTable(PropertyTable\\n)\n```\n\nThe root shape is where the transition chain starts from, it has a pointer to a `PropertyTable`,\nwe will explain what it is and does later on!\n\n**NOTE:** We will annotate the shapes with `S` followed by a number.\n\nIf we have an example of JavaScript code like:\n\n```js\nlet o = {};\n```\n\nThe following chain is created:\n\n```mermaid\nflowchart LR\n    classDef New style id2 fill:#bbf,stroke:#f66,stroke-width:2px,color:#fff,stroke-dasharray: 5 5\n    style Root stroke:#000,stroke-width:5px\n    style PropertyTable fill:#071E22\n\n    Root(<b>S0: Root Shape</b>\\n<b>Prototype:</b> <i>None</i>) -->|Property Count: 0| PropertyTable(PropertyTable\\n)\n\n    ObjectPrototype(<b>S1: Prototype Shape</b>\\n<b>Prototype:</b> Object.prototype) -->|Property Count: 0|PropertyTable\n    ObjectPrototype:::New -->|Previous| Root\n```\n\nWe transition, the object `o` has `S1` shape. The root shape does not have a prototype. So we transition into a shape that has the\n`Object.prototype` as `__proto__`. We can see that the shapes inherited the `PropertyTable` from the `root`.\n\nOk, Let us add a property `x`:\n\n```js\no.x = 100; // The value is not important!\n```\n\nThen this happens:\n\n```mermaid\nflowchart LR\n    classDef New style id2 fill:#bbf,stroke:#f66,stroke-width:2px,color:#fff,stroke-dasharray: 5 5\n    style Root stroke:#000,stroke-width:5px\n    style PropertyTable fill:#071E22\n\n    Root(<b>S0: Root Shape</b>\\nPrototype: <i>None</i>) -->|Property Count: 0| PropertyTable(<b>PropertyTable</b>\\nx: Slot 0, writable, configurable, enumerable)\n\n    ObjectPrototype(<b>S1: Prototype Shape</b>\\n<b>Prototype:</b> <i>Object.prototype</i>) -->|Property Count: 0|PropertyTable\n    ObjectPrototype -->|Previous| Root\n\n    InsertX(<b>S2: Insert Shape</b>\\n<b>Property:</b> '<i>x</i>') --> |Property Count: 1|PropertyTable\n    InsertX:::New -->|Previous| ObjectPrototype\n```\n\nThe object `o` has shape `S2` shape now, we can see that it also inherited the `PropertyTable`, but it's property count is `1` and\nan entry has been added into the `PropertyTable`.\n\nWe can see that the property added is `writable`, `configurable`, and `enumerable`, but we also see `Slot 0`,\nthis is the index into the dense storage in the object itself.\n\nHere is how it would look with the `o` object:\n\n```mermaid\nflowchart LR\n    classDef New style id2 fill:#bbf,stroke:#f66,stroke-width:2px,color:#fff,stroke-dasharray: 5 5\n    style Root stroke:#000,stroke-width:5px\n    style PropertyTable fill:#071E22\n\n    Root(<b>S0: Root Shape</b>\\n<b>Prototype:</b> <i>None</i>) -->| Property Count: 0 | PropertyTable(<b>PropertyTable</b>\\nx: Slot 0, writable, configurable, enumerable)\n\n    ObjectPrototype(<b>S1: Prototype Shape</b>\\n<b>Prototype:</b> <i>Object.prototype</i>) -->| Property Count: 0 |PropertyTable\n    ObjectPrototype -->|Previous| Root\n\n    InsertX(<b>S2: Insert Shape</b>\\n<b>Property:</b> '<i>x</i>') --> | Property Count: 1 | PropertyTable\n    InsertX -->|Previous| ObjectPrototype\n\n    O(<b>Object o</b>\\n<b>Element 0:</b> JsValue: <i>100</i>)\n    O:::New --> InsertX\n```\n\nLet's define a getter and setter `y`\n\n```js\n// What the getter/setter are not important!\nObject.defineProperty(o, \"y\", {\n  enumerable: true,\n  configurable: true,\n  get: function () {\n    return this.x;\n  },\n  set: function (value) {\n    this.x = value;\n  },\n});\n```\n\n```mermaid\nflowchart LR\n    classDef New style id2 fill:#bbf,stroke:#f66,stroke-width:2px,color:#fff,stroke-dasharray: 5 5\n    style Root stroke:#000,stroke-width:5px\n    style PropertyTable fill:#071E22\n\n    Root(<b>S0: Root Shape</b>\\n<b>Prototype:</b> <i>None</i>) -->|Property Count: 0| PropertyTable(<b>PropertyTable</b>\\nx: Slot 0, writable, configurable, enumerable\\ny: Slot 1, has_get, has_set, configurable, enumerable)\n\n    ObjectPrototype(<b>S1: Prototype Shape</b>\\n<b>Prototype:</b> <i>Object.prototype</i>) -->| Property Count: 0 |PropertyTable\n    ObjectPrototype -->|Previous| Root\n\n    InsertX(<b>S2: Insert Shape</b>\\n<b>Property:</b> '<i>x</i>') --> |Property Count: 1| PropertyTable\n    InsertX -->|Previous| ObjectPrototype\n\n    InsertY(<b>S3: Insert Shape</b>\\n<b>Property:</b> '<i>y</i>') --> |Property Count: 2| PropertyTable\n    InsertY:::New -->|Previous| InsertX\n\n    O(<b>Object o\\nElement 0:</b> JsValue: 100\\n<b>Element 1:</b> JsValue: func\\n<b>Element 2:</b> JsValue: func) --> InsertY\n```\n\nWe can see that the property has been added into the property table, it has the `has_get` and `has_set` flags set,\nin the object there are two elements added, the first is the `get` function and the second is the `set` function.\n\nSlots are varying in length, two for accessor properties and one for data properties, the index points to the first\nvalue in the object storage.\n\nWhat would happen if an object had `S2` shape and we tried to access a property `y` how does it know if it\nhas or doesn't have a property named `y`? By the property count on the shape, it has property count `1`,\nall the object in the `PropertyTable` are stored in a map that preserves the order and and can be indexed.\n\nWhen we do a lookup the on property table, if the index of the property is greater than the property count (`1`),\nthan it does not belong to the shape.\n\nNow, Let's create a new object `o2`, with property `x`:\n\n```js\nlet o2 = { x: 200 };\n```\n\nAfter this `o2` would have the `S2` shape.\n\nHow does the shape know that it can reuse `S1` then to go to `S2`? This is not the real structure!\nEvery shape has pointers to forward transitions that happened, these are weak pointers so we don't keep\nalive unused shapes. The pointers have been omitted, so the diagrams are clearer (too many arrows).\n\nOk, now let us define a property `z` instead of `y`:\n\n```js\no2.z = 300;\n```\n\nThe following changes occur to the shape tree:\n\n```mermaid\nflowchart LR\n    classDef New style id2 fill:#bbf,stroke:#f66,stroke-width:2px,color:#fff,stroke-dasharray: 5 5\n    style Root stroke:#000,stroke-width:5px\n    style PropertyTable fill:#071E22\n    style PropertyTableFork fill:#071E22\n\n    Root(<b>S0: Root Shape</b>\\n<b>Prototype:</b> <i>None</i>) -->| Property Count: 0 | PropertyTable(<b>PropertyTable</b>\\nx: Slot 0, writable, configurable, enumerable\\ny: Slot 1, has_get, has_set, configurable, enumerable)\n\n    ObjectPrototype(<b>S1: Prototype Shape\\nPrototype:</b> <i>Object.prototype</i>) -->| Property Count: 0 |PropertyTable\n    ObjectPrototype -->|Previous| Root\n\n    InsertX(<b>S2: Insert Shape\\nProperty:</b> '<i>x</i>') --> | Property Count: 1 | PropertyTable\n    InsertX -->|Previous| ObjectPrototype\n\n    InsertY(<b>S3: Insert Shape\\nProperty:</b> '<i>y</i>') --> | Property Count: 2 | PropertyTable\n    InsertY -->|Previous| InsertX\n\n    PropertyTableFork(<b>PropertyTable</b>\\nx: Slot 0, writable, configurable, enumerable\\nz: Slot 1, writable, configurable, enumerable)\n    InsertZ(<b>S4: Insert Shape\\nProperty:</b> '<i>z</i>') --> | Property Count: 2 | PropertyTableFork:::New\n    InsertZ:::New -->|Previous| InsertX\n\n    O(<b>Object o\\nElement 0:</b> JsValue: 100\\n<b>Element 1:</b> JsValue: func\\n<b>Element 2:</b> JsValue: func) --> InsertY\n    O2(<b>Object o2\\nElement 0:</b> JsValue: 200\\n<b>Element 1:</b> JsValue: 300)\n    O2:::New --> InsertZ\n```\n\nNow `o2` has `S4` shape. We can also see that `PropertyTable` has been forked, because we can no longer add a property at position `1`.\n\nWhat would happen if we wanted to delete a property `x` from object `o`:\n\n```js\ndelete o.x;\n```\n\n```mermaid\nflowchart LR\n    classDef New style id2 fill:#bbf,stroke:#f66,stroke-width:2px,color:#fff,stroke-dasharray: 5 5\n    style Root stroke:#000,stroke-width:5px\n    style PropertyTable fill:#071E22\n    style PropertyTableFork fill:#071E22\n\n    Root(<b>S0: Root Shape</b>\\n<b>Prototype:</b> <i>None</i>) -->| Property Count: 0 | PropertyTable(<b>PropertyTable</b>\\nx: Slot 0, writable, configurable, enumerable\\ny: Slot 1, has_get, has_set, configurable, enumerable)\n\n    ObjectPrototype(<b>S1: Prototype Shape\\nPrototype:</b> <i>Object.prototype</i>) -->| Property Count: 0 |PropertyTable\n    ObjectPrototype -->|Previous| Root\n\n\n    PropertyTableFork(<b>PropertyTable</b>\\ny: Slot 0, has_get, has_set, configurable, enumerable)\n    InsertYNew(<b>S3: Insert Shape\\nProperty:</b> <i>y</i>) --> | Property Count: 1 |PropertyTableFork:::New\n    InsertYNew:::New -->|Previous| ObjectPrototype\n\n    O(<b>Object o</b>\\n<b>Element 0:</b> JsValue: func\\n<b>Element 1:</b> JsValue: func) --> InsertYNew\n    O:::New\n```\n\n**NOTE:**: `o2` and its shape have been omitted from the diagram.\n\nWhen a deletion happens, we find the node in the chain where we added the property, and get it's parent (`base`),\nwe also remember that what transitions happened after the property insertion, then we apply them\none by one until we construct the chain and return the last shape in that chain.\n"
  },
  {
    "path": "docs/string.md",
    "content": "# `JsString` Design Document\n\n## Overview\n\nIn the Boa JavaScript engine, a [`JsString`] is a reference-counted, immutable string that represents strings in ECMAScript. To optimize memory usage, `JsString` can store data in either Latin-1 (1 byte per character) or UTF-16 (2 bytes per character) encodings.\n\n`JsString` is designed to have a small memory footprint, being exactly the size of a single thin pointer (e.g., 8 bytes on 64-bit systems).\n\n## Memory Layout (`vtable`)\n\nA key aspect of `JsString` is its internal representation. The `ptr` field inside `JsString` always points to a heap allocation where the very first field is a `JsStringVTable` struct.\n\n```rust\npub struct JsString {\n    ptr: NonNull<JsStringVTable>,\n}\n\npub(crate) struct JsStringVTable {\n    pub clone: fn(NonNull<JsStringVTable>) -> JsString,\n    pub drop: fn(NonNull<JsStringVTable>),\n    pub as_str: fn(NonNull<JsStringVTable>) -> JsStr<'static>,\n    pub code_points: fn(NonNull<JsStringVTable>) -> CodePointsIter<'static>,\n    pub refcount: fn(NonNull<JsStringVTable>) -> Option<usize>,\n    pub len: usize,\n    pub kind: JsStringKind,\n}\n```\n\nThis inline `vtable` design allows uniform dispatch for all string operations (like cloning, dropping, or converting to a slice) without branching. Because the `vtable` pointer itself is embedded directly at the start of the memory layout of the various `JsString` representations, `JsString` acts as a thin pointer instead of a fat trait object pointer.\n\n## Representations (`JsStringKind`)\n\nInternally, strings can be represented in multiple forms, identified by the `JsStringKind` enum:\n\n1. **`SequenceString<Latin1>` & `SequenceString<Utf16>` (`JsStringKind::Latin1Sequence` / `JsStringKind::Utf16Sequence`)**\n   A sequential memory slice of characters. When allocated, the layout includes the `vtable`, the reference count, a `PhantomData` marker to ensure invariance, and finally, a trailing slice representing the actual string data (`data: [u8; 0]`). `Latin1` uses `u8` characters, while `Utf16` uses `u16` characters. This is the most common heap-allocated representation.\n2. **`SliceString` (`JsStringKind::Slice`)**\n   Created when taking a substring (slice) of an existing `JsString`. Instead of copying the data, a `SliceString` holds a reference to the original (owned) `JsString`, the start/end bounds (`JsStr<'static>`), its own `vtable`, and a reference count. This avoids copying memory for substring operations.\n3. **`StaticString` (`JsStringKind::Static`)**\n   A static string representation that requires no allocation or reference counting. Boa defines a large number of common JavaScript strings (e.g., `\"length\"`, `\"name\"`, `\"prototype\"`, widely used symbols and built-in object names) at compile-time in `core/string/src/common.rs`. When creating a new `JsString`, Boa checks if it exists in the interned static array. If so, it returns a pointer to the `StaticString` instead of allocating heap memory.\n\n## Reference Counting\n\nFor heap-allocated string representations (`SequenceString` and `SliceString`), reference counting is managed internally using a `Cell<usize>`:\n\n```rust\npub(crate) struct SequenceString<T: InternalStringType> {\n    vtable: JsStringVTable,\n    refcount: Cell<usize>,\n    _marker: PhantomData<fn() -> T>,\n    pub(crate) data: [u8; 0],\n}\n```\n\nSince the `JsString` `ptr` points directly at the structure that contains the `vtable`, `refcount`, and string data all in the same allocation, the engine achieves similar functionality to `std::rc::Rc` but completely avoids the length metadata overhead associated with the `Rc` fat pointer structure. The length configuration is cached cleanly on the `vtable` structure.\n"
  },
  {
    "path": "docs/vm.md",
    "content": "# VM\n\n## Architecture\n\n![image](img/boa_architecture.png)\n\nWhen Boa runs JavaScript, the source code goes through a pipeline:\n\n```text\nSource Code → Parser → AST → ByteCompiler → CodeBlock → VM → Result\n```\n\nThe parser produces an AST, the `ByteCompiler` compiles it into bytecode stored in a `CodeBlock`,\nand then the VM executes that bytecode. Let's dig into how each piece works.\n\n## CodeBlock\n\nEvery function (or script/module) the `ByteCompiler` processes gets its own `CodeBlock`. Think\nof it as the compiled form of a function — it has the bytecode, a pool of constants, info about\nbindings, exception handlers, and some metadata.\n\nHere's a simplified view of what's inside:\n\n```rust\nstruct CodeBlock {\n    bytecode: ByteCode,                     // the actual instruction bytes\n    constants: ThinVec<Constant>,           // strings, nested functions, bigints, scopes\n    bindings: Box<[BindingLocator]>,        // variable binding locators\n    handlers: ThinVec<Handler>,             // try/catch/finally handler ranges\n    ic: Box<[InlineCache]>,                 // inline caches for fast property access\n    register_count: u32,                    // how many local registers this function uses\n    length: u32,                            // the .length property of the function\n    parameter_length: u32,                  // number of formal parameters\n    this_mode: ThisMode,                    // Global, Strict, or Lexical\n    flags: Cell<CodeBlockFlags>,            // strict mode, async, generator, etc.\n    source_info: SourceInfo,                // source maps and function name\n}\n```\n\nConstants are things the bytecode references by index:\n\n```rust\nenum Constant {\n    String(JsString),        // property names, string literals\n    Function(Gc<CodeBlock>), // nested function declarations/expressions\n    BigInt(JsBigInt),        // bigint literals\n    Scope(Scope),            // declarative or function scopes\n}\n```\n\nThe `flags` field uses bitflags to track things like whether the function is `strict`, `async`,\na `generator`, a class constructor, a derived constructor, whether it has a prototype property,\nand so on. If you've built with the `trace` feature, there's also a `TRACEABLE` flag per\nfunction.\n\n## Bytecode and Opcodes\n\nInstructions live in a `ByteCode` struct, which is just a `Box<[u8]>`. Each instruction starts\nwith a one-byte opcode, followed by its operands:\n\n```text\n┌────────┬──────────┬──────────┬───┐\n│ opcode │ operand1 │ operand2 │...│\n│ (1 B)  │ (varies) │ (varies) │   │\n└────────┴──────────┴──────────┴───┘\n```\n\nMost operands use `VaryingOperand`, which picks the smallest encoding that fits the value — `u8`\nif it's ≤ 255, `u16` if ≤ 65535, otherwise `u32`. This keeps bytecode compact in the common case.\n\nAll opcodes are defined in a single `generate_opcodes!` macro invocation. The macro generates\nquite a lot from one definition: the `Opcode` enum, the `Instruction` enum (decoded form with\ntyped fields), a dispatch table of handler functions, and emit methods on `ByteCodeEmitter`.\n\nEach opcode's behavior is implemented via the `Operation` trait:\n\n```rust\ntrait Operation {\n    const NAME: &'static str;\n    const INSTRUCTION: &'static str;\n    const COST: u8;  // used for budget-based async execution\n}\n```\n\nThere are over 100 opcodes, grouped roughly into these categories:\n\n- **push/pop** — push constants, pop values (`PushZero`, `PushInt8`, `PushLiteral`, `Pop`, etc.)\n- **binary ops** — arithmetic and comparison (`Add`, `Sub`, `Mul`, `Eq`, `LessThan`, etc.)\n- **unary ops** — `Neg`, `Pos`, `BitNot`, `LogicalNot`, `TypeOf`, `Inc`, `Dec`\n- **control flow** — `Jump`, `JumpIfTrue`, `JumpIfFalse`, `Return`\n- **call/new** — `Call`, `CallEval`, `New`, `SuperCall`\n- **get/set** — `GetName`, `GetPropertyByName`, `SetName`, `SetPropertyByName`\n- **define/delete** — `DefVar`, `DefineOwnPropertyByName`, `DeletePropertyByValue`\n- **environment** — `PushScope`, `PopScope`, `PushObjectEnvironment`\n- **generator/async** — `Generator`, `GeneratorYield`, `Await`\n- **iteration** — `GetIterator`, `IteratorNext`, `IteratorDone`\n- **copy** — `Move`, `SetRegisterFromAccumulator`\n\n## The Stack\n\nThe VM uses a single `Vec<JsValue>` as its value stack, shared across all call frames. Let's\nlook at how it's organized.\n\nWhen a function gets called, its portion of the stack looks like this:\n\n```text\n                     Setup by the caller\n  ┌─────────────────────────────────────────────────────────┐ ┌───── register pointer (rp)\n  ▼                                                         ▼ ▼\n| -(2+N): this | -(1+N): func | -N: arg1 | ... | -1: argN | 0: reg1 | ... | K: regK |\n  ▲                              ▲   ▲                      ▲   ▲                    ▲\n  └──────────────────────────────┘   └──────────────────────┘   └────────────────────┘\n        function prologue                   arguments             Setup by the callee\n  ▲\n  └─ Frame pointer (fp)\n```\n\nThe first two slots are always `this` and the function object — that's the _prologue_. Then come\nthe arguments. After that, the callee allocates `register_count` slots for its local registers.\nThe register pointer (`rp`) sits right at the boundary, so registers are addressed as simple\noffsets from `rp`.\n\nLet's see a concrete example. Given:\n\n```javascript\nfunction x(a) {}\nfunction y(b, c) {\n  return x(b + c);\n}\n\ny(1, 2);\n```\n\nDuring the call to `x`, the stack looks like:\n\n```text\n    caller prologue    caller arguments   callee prologue   callee arguments\n  ┌─────────────────┐   ┌─────────┐   ┌─────────────────┐  ┌──────┐\n  ▼                 ▼   ▼         ▼   │                 ▼  ▼      ▼\n| 0: undefined | 1: y | 2: 1 | 3: 2 | 4: undefined | 5: x | 6:  3 |\n▲                                   ▲                             ▲\n│       caller register pointer ────┤                             │\n│                                   │                 callee register pointer\n│                             callee frame pointer\n│\n└─────  caller frame pointer\n```\n\nThe calling convention works like this:\n\n1. The caller pushes `this` and the function object (prologue), then pushes the arguments.\n2. The caller creates a `CallFrame` and calls `push_frame()`.\n3. `push_frame()` sets `rp` to the current stack top and extends the stack by `register_count`\n   slots, all initialized to `undefined`.\n4. When the function returns, the stack gets truncated back to the caller's frame pointer.\n\n## Call Frames\n\nA `CallFrame` holds all the execution state for a single function invocation:\n\n```rust\nstruct CallFrame {\n    code_block: Gc<CodeBlock>,     // the function's compiled bytecode\n    pc: u32,                       // program counter (offset into bytecode)\n    rp: u32,                       // register pointer (start of registers in the stack)\n    argument_count: u32,           // how many arguments were passed\n    env_fp: u32,                   // environment frame pointer (for cleanup on exception)\n    environments: EnvironmentStack, // lexical environment chain\n    realm: Realm,                  // the realm this function runs in\n    iterators: ThinVec<IteratorRecord>,  // open iterators (need closing on abrupt completion)\n    binding_stack: Vec<BindingLocator>,  // bindings being updated\n    loop_iteration_count: u64,     // tracks loop iterations for runtime limits\n    active_runnable: Option<ActiveRunnable>,  // owning Script or Module\n    flags: CallFrameFlags,         // EXIT_EARLY, CONSTRUCT, etc.\n}\n```\n\nThe `CallFrame` can figure out where everything lives on the stack using just `rp` and\n`argument_count`:\n\n- `frame_pointer() = rp - argument_count - 2` (start of prologue)\n- `this_index() = rp - argument_count - 2`\n- `function_index() = rp - argument_count - 1`\n- `arguments_range() = (rp - argument_count)..rp`\n\nThere are a few flags worth knowing about:\n\n- **`EXIT_EARLY`** — when we return from this frame, stop the VM entirely and return to the\n  Rust caller, instead of continuing with the parent frame.\n- **`CONSTRUCT`** — this frame was created via `[[Construct]]` (the `new` keyword).\n- **`REGISTERS_ALREADY_PUSHED`** — used when resuming a generator. The register area is\n  already populated from the previous execution, so `push_frame()` skips allocating registers.\n- **`THIS_VALUE_CACHED`** — the `this` value has been resolved and cached.\n\n### How frames get pushed and popped\n\nWhen `push_frame()` is called, the current frame gets swapped out and pushed onto a `frames`\nvector. The new frame becomes `vm.frame`. On `pop_frame()`, the reverse happens — the last\nframe on the vector gets swapped back in.\n\nFor async/generator functions, the first few registers are reserved:\n\n- Registers 0, 1, 2: promise capability (promise object, resolve fn, reject fn)\n- Register 3: async generator object (when applicable)\n\n## Execution Loop\n\nThe core loop lives in `Context::run()`. It's a straightforward fetch-decode-execute loop:\n\n```rust\nfn run(&mut self) -> CompletionRecord {\n    while let Some(byte) = bytecode.get(frame.pc) {\n        let opcode = Opcode::decode(*byte);\n\n        match self.execute_one(Self::execute_bytecode_instruction, opcode) {\n            ControlFlow::Continue(()) => {}\n            ControlFlow::Break(value) => return value,\n        }\n    }\n}\n```\n\nDispatch uses a static handler table — `OPCODE_HANDLERS` is an array of 256 function pointers,\none per possible opcode byte. Each handler decodes the operands from the bytecode, advances `pc`\npast them, runs the operation, and returns a `ControlFlow`.\n\nFor async contexts (like module evaluation), there's `run_async_with_budget()`. Each opcode has\na `COST`, and the budget gets decremented on every instruction. When it hits zero, the function\nyields back to the async executor with `yield_now().await`, preventing a long-running script from\nstarving other tasks.\n\n### Tracing\n\nIf you build with the `trace` feature, the VM can print each instruction as it runs. You can\nenable it globally (`vm.trace = true`) or per-function (`code_block.set_traceable(true)`). The\noutput looks like:\n\n```text\nTime          Opcode                     Operands                   Top Of Stack\n6μs           PushOne                    dst:0                      1\n7μs           PutLexicalValue            src:0, binding_index:0     <empty>\n```\n\n## Exception Handling\n\nException handlers are compiled as `Handler` structs in the `CodeBlock`:\n\n```rust\nstruct Handler {\n    start: u32,              // start of the protected range\n    end: u32,                // handler address (where to jump on catch)\n    environment_count: u32,  // environments to preserve when unwinding\n}\n```\n\nSo for a try/catch like:\n\n```javascript\ntry {\n  // bytecode at pc 10..50\n  riskyOperation();\n} catch (e) {\n  // handler at pc 50\n  handleError(e);\n}\n```\n\n...we'd get `Handler { start: 10, end: 50, environment_count: N }`.\n\nWhen an exception is thrown, here's what happens:\n\n1. The VM captures a backtrace from the shadow stack.\n2. It checks if the error is catchable. Non-catchable errors (like exceeding runtime limits) skip\n   all handler logic and immediately unwind everything.\n3. For catchable errors, `find_handler(pc)` searches the handlers in reverse order (innermost\n   first) for one whose `[start, end)` range contains the current `pc`.\n4. If a handler is found in the current frame: set `pc = handler.end`, truncate environments\n   to `env_fp + handler.environment_count`, store the exception in `vm.pending_exception`,\n   and continue. Bytecode can retrieve it later with the `Exception` opcode.\n5. If no handler in the current frame: check if the frame has `EXIT_EARLY` set (if so, return\n   the error to the Rust caller). Otherwise, pop the frame and try the parent frame's handlers.\n   Keep unwinding until we find a handler or run out of frames.\n\n## Generators and Async Functions\n\nGenerator functions use `GeneratorResumeKind` to track how they're being resumed:\n\n```rust\nenum GeneratorResumeKind {\n    Normal = 0,  // .next(value)\n    Throw = 1,   // .throw(error)\n    Return = 2,  // .return(value)\n}\n```\n\nWhen a generator yields (via the `GeneratorYield` opcode), the current frame gets popped and\nits stack portion is saved. When `.next()` is called again, the saved `CallFrame` — with the\n`REGISTERS_ALREADY_PUSHED` flag set — gets pushed back. Since the registers are already there\nfrom the previous run, `push_frame()` skips the allocation step and execution picks up right\nwhere it left off.\n\nAsync functions work similarly but use reserved register slots for their promise machinery\n(registers 0-2 hold the promise, resolve, and reject). The `Await` opcode suspends execution\nlike `Yield`, but resumption is driven by promise settlement instead of an explicit `.next()`\ncall. Async generators combine both: registers 0-2 for the promise, register 3 for the async\ngenerator object.\n\n## Inline Caching\n\nProperty access can be expensive — the VM has to walk the shape chain each time. To speed things\nup, each `GetPropertyByName` and `SetPropertyByName` instruction references an inline cache\nentry (via `ic_index`). The cache stores the property name and the last-seen object shape. If\nthe object's shape matches the cached one, we already know the property's slot index and can\nskip the full lookup. On a miss, we do the lookup and update the cache.\n\n## Runtime Limits\n\nEmbedders can constrain the VM through `RuntimeLimits`. By default we allow 512 levels of\nrecursion, a stack size of 1024, and practically unlimited loop iterations. These get checked\nat call boundaries and inside loops. Exceeding any limit throws a non-catchable\n`RuntimeLimitError` that bypasses all exception handlers.\n\n## Understanding the trace output\n\nOnce set up you can try some simple javascript in your test file. For example:\n\n```js\nlet a = 1;\nlet b = 2;\n```\n\nOutputs:\n\n```text\n----------------------Compiled Output: '<main>'-----------------------\nLocation  Count    Handler    Opcode                     Operands\n\n000000    0000      none      PushOne\n000001    0001      none      PutLexicalValue            0000: 'a'\n000006    0002      none      PushInt8                   2\n000008    0003      none      PutLexicalValue            0001: 'b'\n000013    0004      none      Return\n\nLiterals:\n    <empty>\n\nBindings:\n    0000: a\n    0001: b\n\nFunctions:\n    <empty>\n\nHandlers:\n    <empty>\n\n\n----------------------------------------- Call Frame -----------------------------------------\nTime          Opcode                     Operands                   Top Of Stack\n\n6μs           PushOne                                               1\n7μs           PutLexicalValue            0000: 'a'                  <empty>\n0μs           PushInt8                   2                          2\n1μs           PutLexicalValue            0001: 'b'                  <empty>\n0μs           Return                                                <empty>\n\nStack:\n    <empty>\n\n\nundefined\n```\n\nThe above output contains the following information:\n\n- The bytecode and properties of the function that will be executed\n  - `Compiled Output`: The bytecode.\n    - `Location`: Location of the instruction (instructions are not the same size).\n    - `Count`: Instruction count.\n    - `Handler`: Exception handler, if the instruction throws an exception, which handler is responsible for that instruction and where it would jump. Additionally `>` denotes the beginning of a handler and `<` the end.\n    - `Opcode`: Opcode name.\n    - `Operands`: The operands of the opcode.\n  - `Literals`: The literals used by the bytecode (like strings).\n  - `Bindings`: Binding names used by the bytecode.\n  - `Functions`: Function names use by the bytecode.\n  - `Handlers`: Exception handlers use by the bytecode, it contains how many values should be on the stack and environments (relative to `CallFrame`'s frame pointers).\n- The code being executed (marked by `Vm Start` or `Call Frame`).\n  - `Time`: The amount of time that instruction took to execute.\n  - `Opcode`: Opcode name.\n  - `Operands`: The operands of the opcode.\n  - `Top Of Stack`: The top element of the stack **after** execution of instruction.\n- `Stack`: The trace of the stack after execution ends.\n- The result of the execution (The top element of the stack, if the stack is empty then `undefined` is returned).\n\n### Comparing Bytecode output\n\nIf you wanted another engine's bytecode output for the same JS, SpiderMonkey's bytecode output is the best to use. You can follow the setup [here](https://udn.realityripple.com/docs/Mozilla/Projects/SpiderMonkey/Introduction_to_the_JavaScript_shell). You will need to build from source because the pre-built binaries don't include the debugging utilities which we need.\n\nI named the binary `js_shell` as `js` conflicts with NodeJS. Once up and running you should be able to use `js_shell -f tests/js/test.js`. You will get no output to begin with, this is because you need to run `dis()` or `dis([func])` in the code. Once you've done that you should get some output like so:\n\n```text\nloc     op\n-----   --\n00000:  GlobalOrEvalDeclInstantiation 0 #\nmain:\n00005:  One                             # 1\n00006:  InitGLexical \"a\"                # 1\n00011:  Pop                             #\n00012:  Int8 2                          # 2\n00014:  InitGLexical \"b\"                # 2\n00019:  Pop                             #\n00020:  GetGName \"dis\"                  # dis\n```\n"
  },
  {
    "path": "examples/Cargo.toml",
    "content": "[package]\nname = \"boa_examples\"\ndescription = \"Usage examples of the Boa JavaScript engine.\"\npublish = false\nversion.workspace = true\nedition.workspace = true\nauthors.workspace = true\nlicense.workspace = true\nrepository.workspace = true\nrust-version.workspace = true\n\n[dependencies]\nboa_engine = { workspace = true, features = [\"annex-b\", \"float16\", \"xsum\", \"temporal\"] }\nboa_ast.workspace = true\nboa_interner.workspace = true\nboa_gc.workspace = true\nboa_parser.workspace = true\nboa_runtime.workspace = true\ntime.workspace = true\nsmol.workspace = true\nfutures-concurrency.workspace = true\nfutures-lite.workspace = true\ntokio = { workspace = true, features = [\"rt\", \"rt-multi-thread\", \"time\", \"macros\"] }\nreqwest.workspace = true\nrustls.workspace = true\n\n# use explicit lints for examples, since we don't need to lint for docs\n[lints.rust]\n# rustc lint groups https://doc.rust-lang.org/rustc/lints/groups.html\nwarnings = \"warn\"\nfuture_incompatible = \"warn\"\nlet_underscore = \"warn\"\nnonstandard_style = \"warn\"\nrust_2018_compatibility = \"warn\"\nrust_2018_idioms = \"warn\"\nrust_2021_compatibility = \"warn\"\nunused = \"warn\"\nmacro_use_extern_crate = \"warn\"\nmeta_variable_misuse = \"warn\"\nmissing_abi = \"warn\"\nmissing_copy_implementations = \"warn\"\nmissing_debug_implementations = \"warn\"\nnon_ascii_idents = \"warn\"\nnoop_method_call = \"warn\"\nsingle_use_lifetimes = \"warn\"\ntrivial_casts = \"warn\"\ntrivial_numeric_casts = \"warn\"\nunreachable_pub = \"warn\"\nunsafe_op_in_unsafe_fn = \"warn\"\nunused_import_braces = \"warn\"\nunused_lifetimes = \"warn\"\nunused_qualifications = \"warn\"\nvariant_size_differences = \"warn\"\n\n[lints.clippy]\nall = \"warn\"\ncorrectness = \"warn\"\nsuspicious = \"warn\"\nstyle = \"warn\"\ncomplexity = \"warn\"\nperf = \"warn\"\n\n[package.metadata.docs.rs]\nall-features = true\n"
  },
  {
    "path": "examples/README.md",
    "content": "# Examples of usage for `boa_engine`\n\nIn this crate you can find examples of the usage of the Boa JavaScript engine. The simplest\nexample can be found in the [loadstring.rs](./src/bin/loadstring.rs) file. A similar example\nthat uses JavaScript stored in a file can be found in [loadfile.rs](./src/bin/loadfile.rs).\n\nYou can also find examples of specific Rust APIs for arrays, maps, sets, typed arrays and much\nmore.\n"
  },
  {
    "path": "examples/scripts/calc.js",
    "content": "module.exports = {\n  add: function (a, b) {\n    return a + b;\n  },\n  subtract: function (a, b) {\n    return a - b;\n  },\n  multiply: function (a, b) {\n    return a * b;\n  },\n  divide: function (a, b) {\n    return a / b;\n  },\n};\n"
  },
  {
    "path": "examples/scripts/calctest.js",
    "content": "//load module\nlet calc = require(\"./scripts/calc.js\");\n\nconsole.log(\"Using calc module\");\nconsole.log(\"Add: \" + calc.add(3, 3));\nconsole.log(\"Subtract: \" + calc.subtract(3, 3));\nconsole.log(\"Multiply: \" + calc.multiply(3, 3));\nconsole.log(\"Divide: \" + calc.divide(3, 3));\n"
  },
  {
    "path": "examples/scripts/enhancedglobal.js",
    "content": "//access custom global variable\nconsole.log(\"Custom global: \" + customstring);\n\n//call a custom global function with arguments\nconsole.log(\"Custom function: \" + rusty_hello(\"Boa! Boa!\"));\n\n//access a custom global object and call a member function of that object\nlet a = 5;\nlet b = 5;\nlet result = rusty_obj.add(a, b);\nconsole.log(\"Custom object: Result from rusty_obj.add() : \" + result);\n"
  },
  {
    "path": "examples/scripts/helloworld.js",
    "content": "console.log(\"Hello World from JS file!\");\n"
  },
  {
    "path": "examples/scripts/modules/operations.mjs",
    "content": "function sum(a, b) {\n  return a + b;\n}\n\nfunction sub(a, b) {\n  return a - b;\n}\n\nfunction mult(a, b) {\n  return a * b;\n}\n\nfunction div(a, b) {\n  return a / b;\n}\n\nfunction sqrt(a) {\n  return Math.sqrt(a);\n}\n\nexport { sum, sub, mult, div, sqrt };\n"
  },
  {
    "path": "examples/scripts/modules/trig.mjs",
    "content": "import { sum, mult, sqrt } from \"./operations.mjs\";\n\nfunction pyth(a, b) {\n  let a2 = mult(a, a);\n  let b2 = mult(b, b);\n  let a2b2 = sum(a2, b2);\n\n  return sqrt(a2b2);\n}\n\nexport { pyth };\n"
  },
  {
    "path": "examples/src/bin/classes.rs",
    "content": "// NOTE: this example requires the `console` feature to run correctly.\nuse boa_engine::{\n    Context, JsArgs, JsData, JsResult, JsString, JsValue, Source,\n    class::{Class, ClassBuilder},\n    error::JsNativeError,\n    js_string,\n    native_function::NativeFunction,\n    property::Attribute,\n};\nuse boa_gc::{Finalize, Trace};\nuse boa_runtime::Console;\n\n// We create a new struct that is going to represent a person.\n//\n// We derive `Debug`, `Trace` and `Finalize`, it automatically implements `NativeObject`\n// so we can pass it as an object in Javascript.\n//\n// The fields of the struct are not accessible by Javascript unless we create accessors for them.\n/// Represents a `Person` object.\n#[derive(Debug, Trace, Finalize, JsData)]\nstruct Person {\n    /// The name of the person.\n    name: JsString,\n    /// The age of the person.\n    age: u32,\n}\n\n// Here we implement a static method for Person that matches the `NativeFunction` signature.\n//\n// NOTE: The function does not have to be implemented inside Person, it can be a free function,\n// or any function that matches the required signature.\nimpl Person {\n    /// Says hello if `this` is a `Person`\n    fn say_hello(this: &JsValue, _: &[JsValue], _: &mut Context) -> JsResult<JsValue> {\n        // We check if this is an object.\n        if let Some(object) = this.as_object() {\n            // If it is we downcast the type to type `Person`.\n            if let Some(person) = object.downcast_ref::<Person>() {\n                // and print a message to stdout.\n                println!(\n                    \"Hello my name is {}, I'm {} years old\",\n                    person.name.to_std_string_escaped(),\n                    person.age // Here we can access the native rust fields of the struct.\n                );\n                return Ok(JsValue::undefined());\n            }\n        }\n        // If `this` was not an object or the type of `this` was not a native object `Person`,\n        // we throw a `TypeError`.\n        Err(JsNativeError::typ()\n            .with_message(\"'this' is not a Person object\")\n            .into())\n    }\n}\n\nimpl Class for Person {\n    // We set the binding name of this function to `\"Person\"`.\n    // It does not have to be `\"Person\"`, it can be any string.\n    const NAME: &'static str = \"Person\";\n    // We set the length to `2` since we accept 2 arguments in the constructor.\n    //\n    // This is the same as `Object.length`.\n    // NOTE: The default value of `LENGTH` is `0`.\n    const LENGTH: usize = 2;\n\n    // This is what is internally called when we construct a `Person` with the expression `new Person()`.\n    fn data_constructor(\n        _this: &JsValue,\n        args: &[JsValue],\n        context: &mut Context,\n    ) -> JsResult<Self> {\n        // We get the first argument. If it is unavailable we default to `undefined`,\n        // and then we call `to_string()`.\n        //\n        // This is equivalent to `String(arg)`.\n        let name = args.get_or_undefined(0).to_string(context)?;\n\n        // We get the second argument. If it is unavailable we default to `undefined`,\n        // and then we call `to_u32`.\n        //\n        // This is equivalent to `arg | 0`.\n        let age = args.get_or_undefined(1).to_u32(context)?;\n\n        // We construct a new native struct `Person`\n        let person = Person { name, age };\n\n        Ok(person) // and we return it.\n    }\n\n    /// Here is where the class is initialized.\n    fn init(class: &mut ClassBuilder<'_>) -> JsResult<()> {\n        // We add a inheritable method `sayHello` with `0` arguments of length.\n        //\n        // This function is added to the `Person` prototype.\n        class.method(\n            js_string!(\"sayHello\"),\n            0,\n            NativeFunction::from_fn_ptr(Self::say_hello),\n        );\n        // We add a static method `is` using a closure, but it must be convertible\n        // to a `NativeFunction`. The `NativeFunction` API has more information on which type of\n        // Rust functions can be used to create `NativeFunction`s.\n        //\n        // This function is added to the `Person` class.\n        class.static_method(\n            js_string!(\"is\"),\n            1,\n            NativeFunction::from_fn_ptr(|_this, args, _ctx| {\n                if let Some(arg) = args.first()\n                    && let Some(object) = arg.as_object()\n                {\n                    // We check if the type of `args[0]` is `Person`\n                    if object.is::<Person>() {\n                        return Ok(true.into()); // and return `true` if it is.\n                    }\n                }\n                Ok(false.into()) // Otherwise we return `false`.\n            }),\n        );\n\n        // We add an `\"inheritedProperty\"` property to the prototype of `Person` with\n        // a value of `10` and default attribute flags `READONLY`, `NON_ENUMERABLE` and `PERMANENT`.\n        class.property(js_string!(\"inheritedProperty\"), 10, Attribute::default());\n\n        // Finally, we add a `\"staticProperty\"` property to `Person` with a value\n        // of `\"Im a static property\"` and attribute flags `WRITABLE`, `ENUMERABLE` and `PERMANENT`.\n        class.static_property(\n            js_string!(\"staticProperty\"),\n            js_string!(\"Im a static property\"),\n            Attribute::WRITABLE | Attribute::ENUMERABLE | Attribute::PERMANENT,\n        );\n\n        Ok(())\n    }\n}\n\n/// Adds the custom runtime to the context.\nfn add_runtime(context: &mut Context) {\n    // We first add the `console` object, to be able to call `console.log()`.\n    let console = Console::init(context);\n    context\n        .register_global_property(Console::NAME, console, Attribute::all())\n        .expect(\"the console builtin shouldn't exist\");\n\n    // Then we need to register the global class `Person` inside `context`.\n    context\n        .register_global_class::<Person>()\n        .expect(\"the Person builtin shouldn't exist\");\n}\n\nfn main() {\n    // First we need to create a Javascript context.\n    let mut context = Context::default();\n\n    // Then, we add our custom runtime.\n    add_runtime(&mut context);\n\n    // Having done all of that, we can execute Javascript code with `eval`,\n    // and access the `Person` class defined in Rust!\n    context\n        .eval(Source::from_bytes(\n            r\"\n\t\tlet person = new Person('John', 19);\n\t\tperson.sayHello();\n\n\t\tif (Person.is(person)) {\n\t\t\tconsole.log('person is a Person class instance.');\n\t\t}\n\t\tif (!Person.is('Hello')) {\n\t\t\tconsole.log('\\'Hello\\' string is not a Person class instance.');\n\t\t}\n\n        console.log(Person.staticProperty);\n        console.log(person.inheritedProperty);\n\t    console.log(Person.prototype.inheritedProperty === person.inheritedProperty);\n    \",\n        ))\n        .unwrap();\n}\n"
  },
  {
    "path": "examples/src/bin/closures.rs",
    "content": "// This example goes into the details on how to pass closures as functions inside Rust and call them\n// from Javascript.\n\nuse std::cell::{Cell, RefCell};\n\nuse boa_engine::{\n    Context, JsError, JsNativeError, JsString, JsValue, Source, js_string,\n    native_function::NativeFunction,\n    object::{FunctionObjectBuilder, JsObject, builtins::JsArray},\n    property::{Attribute, PropertyDescriptor},\n};\nuse boa_gc::{Finalize, GcRefCell, Trace};\n\nfn main() -> Result<(), JsError> {\n    // We create a new `Context` to create a new Javascript executor.\n    let mut context = Context::default();\n\n    // We make some operations in Rust that return a `Copy` value that we want to pass to a Javascript\n    // function.\n    let variable = 128 + 64 + 32 + 16 + 8 + 4 + 2 + 1;\n\n    // We register a global closure function that has the name 'closure' with length 0.\n    context\n        .register_global_callable(\n            JsString::from(\"closure\"),\n            0,\n            NativeFunction::from_copy_closure(move |_, _, _| {\n                println!(\"Called `closure`\");\n                // `variable` is captured from the main function.\n                println!(\"variable = {variable}\");\n                println!();\n\n                // We return the moved variable as a `JsValue`.\n                Ok(JsValue::new(variable))\n            }),\n        )\n        .unwrap();\n\n    assert_eq!(context.eval(Source::from_bytes(\"closure()\"))?, 255.into());\n\n    // We have created a closure with moved variables and executed that closure\n    // inside Javascript!\n\n    // This struct is passed to a closure as a capture.\n    #[derive(Debug, Clone, Trace, Finalize)]\n    struct BigStruct {\n        greeting: JsString,\n        object: JsObject,\n    }\n\n    // We create a new `JsObject` with some data\n    let object = JsObject::with_object_proto(context.intrinsics());\n    object.define_property_or_throw(\n        js_string!(\"name\"),\n        PropertyDescriptor::builder()\n            .value(js_string!(\"Boa dev\"))\n            .writable(false)\n            .enumerable(false)\n            .configurable(false),\n        &mut context,\n    )?;\n\n    // Now, we execute some operations that return a `Clone` type\n    let clone_variable = BigStruct {\n        greeting: JsString::from(\"Hello!\"),\n        object,\n    };\n\n    // We can use `FunctionBuilder` to define a closure with additional captures and custom property\n    // attributes.\n    let js_function = FunctionObjectBuilder::new(\n        context.realm(),\n        NativeFunction::from_copy_closure_with_captures(\n            |_, _, captures, context| {\n                let mut captures = captures.borrow_mut();\n                let BigStruct { greeting, object } = &mut *captures;\n                println!(\"Called `createMessage`\");\n                // We obtain the `name` property of `captures.object`\n                let name = object.get(js_string!(\"name\"), context)?;\n\n                // We create a new message from our captured variable.\n                let message = js_string!(\n                    &js_string!(\"message from `\"),\n                    &name.to_string(context)?,\n                    &js_string!(\"`: \"),\n                    &*greeting\n                );\n\n                // We can also mutate the moved data inside the closure.\n                captures.greeting = js_string!(&*greeting, &js_string!(\" Hello!\"));\n\n                println!(\"{}\", message.to_std_string_escaped());\n                println!();\n\n                // We convert `message` into `JsValue` to be able to return it.\n                Ok(message.into())\n            },\n            // Here is where we move `clone_variable` into the closure.\n            GcRefCell::new(clone_variable),\n        ),\n    )\n    // And here we assign `createMessage` to the `name` property of the closure.\n    .name(js_string!(\"createMessage\"))\n    // By default all `FunctionBuilder`s set the `length` property to `0` and\n    // the `constructable` property to `false`.\n    .build();\n\n    // We bind the newly constructed closure as a global property in Javascript.\n    context\n        .register_global_property(\n            // We set the key to access the function the same as its name for\n            // consistency, but it may be different if needed.\n            js_string!(\"createMessage\"),\n            // We pass `js_function` as a property value.\n            js_function,\n            // We assign to the \"createMessage\" property the desired attributes.\n            Attribute::READONLY | Attribute::NON_ENUMERABLE | Attribute::PERMANENT,\n        )\n        .unwrap();\n\n    assert_eq!(\n        context.eval(Source::from_bytes(\"createMessage()\"))?,\n        js_string!(\"message from `Boa dev`: Hello!\").into()\n    );\n\n    // The data mutates between calls\n    assert_eq!(\n        context.eval(Source::from_bytes(\"createMessage(); createMessage();\"))?,\n        js_string!(\"message from `Boa dev`: Hello! Hello! Hello!\").into()\n    );\n\n    // We have moved `Clone` variables into a closure and executed that closure\n    // inside Javascript!\n\n    // ADVANCED\n\n    // If we can ensure the captured variables are not traceable by the garbage collector,\n    // we can pass any static closure easily.\n\n    let index = Cell::new(0i32);\n    let numbers = RefCell::new(Vec::new());\n\n    // We register a global closure that is not `Copy`.\n    context\n        .register_global_callable(\n            js_string!(\"enumerate\"),\n            0,\n            // Note that it is required to use `unsafe` code, since the compiler cannot verify that the\n            // types captured by the closure are not traceable.\n            unsafe {\n                NativeFunction::from_closure(move |_, _, context| {\n                    println!(\"Called `enumerate`\");\n                    // `index` is captured from the main function.\n                    println!(\"index = {}\", index.get());\n                    println!();\n\n                    numbers.borrow_mut().push(index.get());\n                    index.set(index.get() + 1);\n\n                    // We return the moved variable as a `JsValue`.\n                    Ok(JsArray::from_iter(\n                        numbers.borrow().iter().copied().map(JsValue::from),\n                        context,\n                    )\n                    .into())\n                })\n            },\n        )\n        .unwrap();\n\n    // First call should return the array `[0]`.\n    let result = context.eval(Source::from_bytes(\"enumerate()\"))?;\n    let object = result\n        .as_object()\n        .ok_or_else(|| JsNativeError::typ().with_message(\"not an array!\"))?;\n    let array = JsArray::from_object(object)?;\n\n    assert_eq!(array.get(0, &mut context)?, JsValue::from(0i32));\n    assert_eq!(array.get(1, &mut context)?, JsValue::undefined());\n\n    // First call should return the array `[0, 1]`.\n    let result = context.eval(Source::from_bytes(\"enumerate()\"))?;\n    let object = result\n        .as_object()\n        .ok_or_else(|| JsNativeError::typ().with_message(\"not an array!\"))?;\n    let array = JsArray::from_object(object)?;\n\n    assert_eq!(array.get(0, &mut context)?, JsValue::from(0i32));\n    assert_eq!(array.get(1, &mut context)?, JsValue::from(1i32));\n    assert_eq!(array.get(2, &mut context)?, JsValue::undefined());\n\n    // We have moved non-traceable variables into a closure and executed that closure inside Javascript!\n    Ok(())\n}\n"
  },
  {
    "path": "examples/src/bin/commuter_visitor.rs",
    "content": "// This example demonstrates how to use visitors to modify an AST. Namely, the visitors shown here\n// are used to swap the operands of commutable arithmetic operations. For an example which simply\n// inspects the AST without modifying it, see symbol_visitor.rs.\n\nuse boa_ast::{\n    Expression,\n    expression::operator::{\n        Binary,\n        binary::{ArithmeticOp, BinaryOp},\n    },\n    visitor::{VisitWith, VisitorMut},\n};\nuse boa_engine::{Context, Source};\nuse boa_interner::ToInternedString;\nuse boa_parser::Parser;\nuse core::ops::ControlFlow;\nuse std::{convert::Infallible, path::Path};\n\n/// Visitor which, when applied to a binary expression, will swap the operands. Use in other\n/// circumstances is undefined.\n#[derive(Default)]\nstruct OpExchanger<'ast> {\n    lhs: Option<&'ast mut Expression>,\n}\n\nimpl<'ast> VisitorMut<'ast> for OpExchanger<'ast> {\n    type BreakTy = ();\n\n    fn visit_expression_mut(&mut self, node: &'ast mut Expression) -> ControlFlow<Self::BreakTy> {\n        if let Some(lhs) = self.lhs.take() {\n            core::mem::swap(lhs, node);\n            ControlFlow::Break(())\n        } else {\n            self.lhs = Some(node);\n            // we do not traverse into the expression; we are only to be used with a binary op\n            ControlFlow::Continue(())\n        }\n    }\n}\n\n/// Visitor which walks the AST and swaps the operands of commutable arithmetic binary expressions.\n#[derive(Default)]\nstruct CommutorVisitor {}\n\nimpl<'ast> VisitorMut<'ast> for CommutorVisitor {\n    type BreakTy = Infallible;\n\n    fn visit_binary_mut(&mut self, node: &'ast mut Binary) -> ControlFlow<Self::BreakTy> {\n        if let BinaryOp::Arithmetic(ArithmeticOp::Add | ArithmeticOp::Mul) = node.op() {\n            // set up the exchanger and swap lhs and rhs\n            let mut exchanger = OpExchanger::default();\n            assert!(matches!(\n                exchanger.visit_binary_mut(node),\n                ControlFlow::Break(())\n            ));\n        }\n        // traverse further in; there may nested binary operations\n        node.visit_with_mut(self)\n    }\n}\n\nfn main() {\n    let mut parser = Parser::new(Source::from_filepath(Path::new(\"./scripts/calc.js\")).unwrap());\n    let mut ctx = Context::default();\n\n    let scope = ctx.realm().scope().clone();\n    let mut script = parser.parse_script(&scope, ctx.interner_mut()).unwrap();\n\n    let mut visitor = CommutorVisitor::default();\n\n    assert!(matches!(\n        visitor.visit_statement_list_mut(script.statements_mut()),\n        ControlFlow::Continue(())\n    ));\n\n    println!(\"{}\", script.to_interned_string(ctx.interner()));\n}\n"
  },
  {
    "path": "examples/src/bin/derive.rs",
    "content": "use boa_engine::value::JsVariant;\nuse boa_engine::{Context, JsNativeError, JsResult, JsValue, Source, value::TryFromJs};\n\n/// You can easily derive `TryFromJs` for structures with base Rust types.\n///\n/// By default, the conversion will only work if the type is directly representable by the Rust\n/// type.\n#[derive(Debug, TryFromJs)]\n#[allow(dead_code)]\nstruct TestStruct {\n    inner: bool,\n    hello: String,\n    // You can override the conversion of an attribute.\n    #[boa(from_js_with = \"lossy_conversion\")]\n    my_float: i16,\n}\n\nfn main() {\n    let js_str = r#\"\n    let x = {\n        inner: false,\n        hello: \"World\",\n        my_float: 2.9,\n    };\n    x;\n    \"#;\n    let js = Source::from_bytes(js_str);\n\n    let mut context = Context::default();\n    let res = context.eval(js).unwrap();\n\n    let str = TestStruct::try_from_js(&res, &mut context)\n        .map_err(|e| e.to_string())\n        .unwrap();\n\n    println!(\"{str:?}\");\n}\n\n/// Converts the value lossly\nfn lossy_conversion(value: &JsValue, _context: &mut Context) -> JsResult<i16> {\n    match value.variant() {\n        JsVariant::Float64(r) => Ok(r.round() as i16),\n        JsVariant::Integer32(i) => Ok(i as i16),\n        _ => Err(JsNativeError::typ()\n            .with_message(\"cannot convert value to an i16\")\n            .into()),\n    }\n}\n"
  },
  {
    "path": "examples/src/bin/host_defined.rs",
    "content": "// This example goes into the details on how to store user defined structs/state that is shared.\n\nuse boa_engine::{\n    Context, JsArgs, JsData, JsError, JsNativeError, JsString, JsValue, Source,\n    native_function::NativeFunction,\n};\nuse boa_gc::{Finalize, Trace};\n\n/// Custom host-defined struct that has some state, and can be shared between JavaScript and rust.\n#[derive(Default, Trace, Finalize, JsData)]\nstruct CustomHostDefinedStruct {\n    #[unsafe_ignore_trace]\n    counter: usize,\n}\n\n/// Custom host-defined struct that has some state, and can be shared between JavaScript and rust.\n#[derive(Trace, Finalize, JsData)]\nstruct AnotherCustomHostDefinedStruct {\n    #[unsafe_ignore_trace]\n    counter: usize,\n}\n\nimpl AnotherCustomHostDefinedStruct {\n    fn new(value: usize) -> Self {\n        Self { counter: value }\n    }\n}\n\n/// Custom host-defined struct that tracks the number of calls to the `getRealmValue` and `setRealmValue` functions.\n#[derive(Default, Trace, Finalize, JsData)]\nstruct HostDefinedMetrics {\n    #[unsafe_ignore_trace]\n    counter: usize,\n}\n\nfn main() -> Result<(), JsError> {\n    // We create a new `Context` to create a new Javascript executor..\n    let mut context = Context::default();\n\n    // Get the realm from the context.\n    let realm = context.realm().clone();\n\n    // Insert a default CustomHostDefinedStruct.\n    realm\n        .host_defined_mut()\n        .insert_default::<CustomHostDefinedStruct>();\n\n    {\n        assert!(realm.host_defined().has::<CustomHostDefinedStruct>());\n\n        // Get the [[HostDefined]] field from the realm and downcast it to our concrete type.\n        let host_defined = realm.host_defined();\n        let Some(host_defined) = host_defined.get::<CustomHostDefinedStruct>() else {\n            return Err(JsNativeError::typ()\n                .with_message(\"Realm does not have HostDefined field\")\n                .into());\n        };\n\n        // Assert that the [[HostDefined]] field is in it's initial state.\n        assert_eq!(host_defined.counter, 0);\n    }\n\n    // Insert another struct with state into [[HostDefined]] field.\n    realm\n        .host_defined_mut()\n        .insert(AnotherCustomHostDefinedStruct::new(10));\n\n    {\n        assert!(realm.host_defined().has::<AnotherCustomHostDefinedStruct>());\n\n        // Get the [[HostDefined]] field from the realm and downcast it to our concrete type.\n        let host_defined = realm.host_defined();\n        let Some(host_defined) = host_defined.get::<AnotherCustomHostDefinedStruct>() else {\n            return Err(JsNativeError::typ()\n                .with_message(\"Realm does not have HostDefined field\")\n                .into());\n        };\n\n        // Assert that the [[HostDefined]] field is in it's initial state.\n        assert_eq!(host_defined.counter, 10);\n    }\n\n    // Remove a type from the [[HostDefined]] field.\n    assert!(\n        realm\n            .host_defined_mut()\n            .remove::<AnotherCustomHostDefinedStruct>()\n            .is_some()\n    );\n\n    // Create and register function for getting and setting the realm value.\n    //\n    // The function lives in the context's realm and has access to the host-defined field.\n    context.register_global_builtin_callable(\n        JsString::from(\"setRealmValue\"),\n        1,\n        NativeFunction::from_fn_ptr(|_, args, context| {\n            let value: usize = args.get_or_undefined(0).try_js_into(context)?;\n\n            let mut host_defined = context.realm().host_defined_mut();\n            let (Some(host_defined), Some(metrics)) =\n                host_defined.get_many_mut::<(CustomHostDefinedStruct, HostDefinedMetrics), 2>()\n            else {\n                return Err(JsNativeError::typ()\n                    .with_message(\"Realm does not have HostDefined fields\")\n                    .into());\n            };\n\n            host_defined.counter = value;\n            metrics.counter += 1;\n\n            Ok(value.into())\n        }),\n    )?;\n\n    context.register_global_builtin_callable(\n        JsString::from(\"getRealmValue\"),\n        0,\n        NativeFunction::from_fn_ptr(|_, _, context| {\n            let mut host_defined = context.realm().host_defined_mut();\n\n            let value: JsValue = {\n                let Some(host_defined) = host_defined.get::<CustomHostDefinedStruct>() else {\n                    return Err(JsNativeError::typ()\n                        .with_message(\"Realm does not have HostDefined field\")\n                        .into());\n                };\n                host_defined.counter.into()\n            };\n\n            let Some(metrics) = host_defined.get_mut::<HostDefinedMetrics>() else {\n                return Err(JsNativeError::typ()\n                    .with_message(\"Realm does not have HostDefined field\")\n                    .into());\n            };\n\n            metrics.counter += 1;\n\n            Ok(value)\n        }),\n    )?;\n\n    // Insert HostDefinedMetrics into the [[HostDefined]] field.\n    realm\n        .host_defined_mut()\n        .insert_default::<HostDefinedMetrics>();\n\n    // Run code in JavaScript that mutates the host-defined field on the Realm.\n    context.eval(Source::from_bytes(\n        r\"\n        setRealmValue(50);\n        setRealmValue(getRealmValue() * 2);\n    \",\n    ))?;\n\n    let host_defined = realm.host_defined();\n    let Some(host_defined_value) = host_defined.get::<CustomHostDefinedStruct>() else {\n        return Err(JsNativeError::typ()\n            .with_message(\"Realm does not have HostDefined field\")\n            .into());\n    };\n\n    // Assert that the host-defined field changed.\n    assert_eq!(host_defined_value.counter, 100);\n\n    let Some(metrics) = host_defined.get::<HostDefinedMetrics>() else {\n        return Err(JsNativeError::typ()\n            .with_message(\"Realm does not have HostDefined field\")\n            .into());\n    };\n\n    // Assert that we called the getRealmValue and setRealmValue functions (3 times in total)\n    assert_eq!(metrics.counter, 3);\n\n    Ok(())\n}\n"
  },
  {
    "path": "examples/src/bin/jsarray.rs",
    "content": "// This example shows how to manipulate a Javascript array using Rust code.\n\nuse boa_engine::{\n    Context, JsResult, JsValue, js_string,\n    native_function::NativeFunction,\n    object::{FunctionObjectBuilder, builtins::JsArray},\n};\n\nfn main() -> JsResult<()> {\n    // We create a new `Context` to create a new Javascript executor.\n    let context = &mut Context::default();\n\n    // Create an empty array.\n    let array = JsArray::new(context)?;\n\n    assert!(array.is_empty(context)?);\n\n    array.push(js_string!(\"Hello, world\"), context)?; // [ \"Hello, world\" ]\n    array.push(true, context)?; // [ \"Hello, world\", true ]\n\n    assert!(!array.is_empty(context)?);\n\n    assert_eq!(array.pop(context)?, JsValue::new(true)); // [ \"Hello, world\" ]\n    assert_eq!(\n        array.pop(context)?,\n        JsValue::new(js_string!(\"Hello, world\"))\n    ); // [ ]\n    assert_eq!(array.pop(context)?, JsValue::undefined()); // [ ]\n\n    array.push(1, context)?; // [ 1 ]\n\n    assert_eq!(array.pop(context)?, JsValue::new(1)); // [ ]\n    assert_eq!(array.pop(context)?, JsValue::undefined()); // [ ]\n\n    array.push_items(\n        &[\n            JsValue::new(10),\n            JsValue::new(11),\n            JsValue::new(12),\n            JsValue::new(13),\n            JsValue::new(14),\n        ],\n        context,\n    )?; // [ 10, 11, 12, 13, 14 ]\n\n    array.reverse(context)?; // [ 14, 13, 12, 11, 10 ]\n\n    assert_eq!(array.index_of(12, None, context)?, Some(2));\n\n    // We can also use JsObject method `.get()` through the Deref trait.\n    let element = array.get(2, context)?; // array[ 0 ]\n    assert_eq!(element, JsValue::new(12));\n    // Or we can use the `.at(index)` method.\n    assert_eq!(array.at(0, context)?, JsValue::new(14)); // first element\n    assert_eq!(array.at(-1, context)?, JsValue::new(10)); // last element\n\n    // Join the array with an optional separator (default \",\").\n    let joined_array = array.join(None, context)?;\n    assert_eq!(&joined_array, \"14,13,12,11,10\");\n\n    array.fill(false, Some(1), Some(4), context)?;\n\n    let joined_array = array.join(Some(\"::\".into()), context)?;\n    assert_eq!(&joined_array, \"14::false::false::false::10\");\n\n    let filter_callback = FunctionObjectBuilder::new(\n        context.realm(),\n        NativeFunction::from_fn_ptr(|_this, args, _context| {\n            Ok(args.first().cloned().unwrap_or_default().is_number().into())\n        }),\n    )\n    .build();\n\n    let map_callback = FunctionObjectBuilder::new(\n        context.realm(),\n        NativeFunction::from_fn_ptr(|_this, args, context| {\n            args.first()\n                .cloned()\n                .unwrap_or_default()\n                .pow(&JsValue::new(2), context)\n        }),\n    )\n    .build();\n\n    let mut data = Vec::new();\n    for i in 1..=5 {\n        data.push(JsValue::new(i));\n    }\n    let another_array = JsArray::from_iter(data, context); // [ 1, 2, 3, 4, 5]\n\n    let chained_array = array // [ 14, false, false, false, 10 ]\n        .filter(filter_callback, None, context)? // [ 14, 10 ]\n        .map(map_callback, None, context)? // [ 196, 100 ]\n        .sort(None, context)? // [ 100, 196 ]\n        .concat(&[another_array.into()], context)? // [ 100, 196, 1, 2, 3, 4, 5 ]\n        .slice(Some(1), Some(5), context)?; // [ 196, 1, 2, 3 ]\n\n    assert_eq!(&chained_array.join(None, context)?, \"196,1,2,3\");\n\n    let reduce_callback = FunctionObjectBuilder::new(\n        context.realm(),\n        NativeFunction::from_fn_ptr(|_this, args, context| {\n            let accumulator = args.first().cloned().unwrap_or_default();\n            let value = args.get(1).cloned().unwrap_or_default();\n\n            accumulator.add(&value, context)\n        }),\n    )\n    .build();\n\n    assert_eq!(\n        chained_array.reduce(reduce_callback, Some(JsValue::new(0)), context)?,\n        JsValue::new(202)\n    );\n\n    context\n        .global_object()\n        .set(js_string!(\"myArray\"), array, true, context)?;\n\n    Ok(())\n}\n"
  },
  {
    "path": "examples/src/bin/jsarraybuffer.rs",
    "content": "// This example shows how to manipulate a Javascript array using Rust code.\n\nuse boa_engine::{\n    Context, JsResult, JsValue,\n    builtins::array_buffer::AlignedVec,\n    js_string,\n    object::builtins::{JsArrayBuffer, JsDataView, JsUint8Array, JsUint32Array},\n    property::Attribute,\n};\n\nfn main() -> JsResult<()> {\n    // We create a new `Context` to create a new Javascript executor.\n    let context = &mut Context::default();\n\n    // This create an array buffer of byte length 4\n    let array_buffer = JsArrayBuffer::new(4, context)?;\n\n    // We can now create an typed array to access the data.\n    let uint32_typed_array = JsUint32Array::from_array_buffer(array_buffer, context)?;\n\n    let value = 0x1234_5678_u32;\n    uint32_typed_array.set(0_u64, value, true, context)?;\n\n    assert_eq!(uint32_typed_array.get(0_u64, context)?, JsValue::new(value));\n\n    // We can also create array buffers from a user defined block of data.\n    //\n    // NOTE: The block data will not be cloned.\n    let blob_of_data = AlignedVec::from_iter(0, 0..=255);\n    let array_buffer = JsArrayBuffer::from_byte_block(blob_of_data, context)?;\n\n    // This the byte length of the new array buffer will be the length of block of data.\n    let byte_length = array_buffer.byte_length();\n    assert_eq!(byte_length, 256);\n\n    // We can now create an typed array to access the data.\n    let uint8_typed_array = JsUint8Array::from_array_buffer(array_buffer.clone(), context)?;\n\n    for i in 0..byte_length {\n        assert_eq!(uint8_typed_array.get(i, context)?, JsValue::new(i));\n    }\n\n    // We can create a Dataview from a JsArrayBuffer\n    let dataview =\n        JsDataView::from_js_array_buffer(array_buffer.clone(), None, Some(100_u64), context)?;\n\n    let dataview_length = dataview.byte_length(context)?;\n\n    assert_eq!(dataview_length, 100);\n\n    let second_byte = dataview.get_uint8(2, true, context)?;\n\n    assert_eq!(second_byte, 2_u8);\n\n    // We can also register it as a global property\n    context\n        .register_global_property(\n            js_string!(\"myArrayBuffer\"),\n            array_buffer,\n            Attribute::WRITABLE | Attribute::ENUMERABLE | Attribute::CONFIGURABLE,\n        )\n        .unwrap();\n\n    // We can also take the inner data from a JsArrayBuffer\n    let data_block = AlignedVec::from_iter(0, 0..5);\n    let array_buffer = JsArrayBuffer::from_byte_block(data_block, context)?;\n\n    let internal_buffer = array_buffer.detach(&JsValue::undefined())?;\n\n    assert_eq!(\n        internal_buffer.as_slice(),\n        (0..5).collect::<Vec<u8>>().as_slice()\n    );\n    let detached_err = array_buffer.detach(&JsValue::undefined());\n    assert!(detached_err.is_err());\n\n    Ok(())\n}\n"
  },
  {
    "path": "examples/src/bin/jsasyncgenerator.rs",
    "content": "//! Example demonstrating the `JsAsyncGenerator` API wrapper.\nuse boa_engine::{\n    Context, JsString, JsValue, Source, builtins::promise::PromiseState,\n    object::builtins::JsAsyncGenerator,\n};\n\nfn main() {\n    let mut context = Context::default();\n\n    let result = context\n        .eval(Source::from_bytes(\n            \"async function* count() { yield 1; yield 2; yield 3; } count()\",\n        ))\n        .unwrap();\n\n    let obj = result.as_object().unwrap().clone();\n    let async_gen = JsAsyncGenerator::from_object(obj).unwrap();\n\n    // next() returns a Promise; run_jobs() is required to resolve it\n    let promise = async_gen.next(JsValue::undefined(), &mut context).unwrap();\n    drop(context.run_jobs());\n    if let PromiseState::Fulfilled(val) = promise.state() {\n        let result_obj = val.as_object().unwrap();\n        let value = result_obj\n            .get(JsString::from(\"value\"), &mut context)\n            .unwrap();\n        assert_eq!(value, JsValue::from(1));\n    }\n\n    // return() resolves the generator early with the given value\n    let promise = async_gen.r#return(JsValue::from(42), &mut context).unwrap();\n    drop(context.run_jobs());\n    if let PromiseState::Fulfilled(val) = promise.state() {\n        let result_obj = val.as_object().unwrap();\n        let value = result_obj\n            .get(JsString::from(\"value\"), &mut context)\n            .unwrap();\n        assert_eq!(value, JsValue::from(42));\n    }\n}\n"
  },
  {
    "path": "examples/src/bin/jsdate.rs",
    "content": "use boa_engine::{\n    Context, JsResult, JsValue, context::HostHooks, js_string, object::builtins::JsDate,\n};\nuse std::rc::Rc;\n\nstruct CustomTimezone;\n\n// This pins the local timezone to a system-agnostic value; in this case, UTC+3\nimpl HostHooks for CustomTimezone {\n    fn local_timezone_offset_seconds(&self, _: i64) -> i32 {\n        time::UtcOffset::from_hms(3, 0, 0)\n            .expect(\"must be valid offset\")\n            .whole_seconds()\n    }\n}\n\nfn main() -> JsResult<()> {\n    let context = &mut Context::builder()\n        .host_hooks(Rc::new(CustomTimezone))\n        .build()\n        .unwrap();\n\n    let timestamp = JsDate::utc(\n        &[\n            JsValue::new(96),\n            JsValue::new(1),\n            JsValue::new(2),\n            JsValue::new(3),\n            JsValue::new(4),\n            JsValue::new(5),\n        ],\n        context,\n    )?\n    .as_number()\n    .unwrap();\n\n    assert_eq!(timestamp, 823_230_245_000.0);\n\n    // Gets the current time in UTC time.\n    let date = JsDate::new(context);\n\n    // sets day of the month to 24\n    date.set_date(24, context)?;\n\n    // sets date to 1st of January 2000\n    date.set_full_year(&[2000.into(), 0.into(), 1.into()], context)?;\n\n    // sets time to 10H:10M:10S:10mS\n    date.set_hours(&[23.into(), 23.into(), 23.into(), 23.into()], context)?;\n\n    // sets milliseconds to 999\n    date.set_milliseconds(999, context)?;\n\n    // sets time to 12M:12S:12ms\n    date.set_minutes(&[12.into(), 12.into(), 12.into()], context)?;\n\n    // sets month to 9 (the 10th) and day to 9\n    date.set_month(&[9.into(), 9.into()], context)?;\n\n    // set seconds to 59 and ms to 59\n    date.set_seconds(&[59.into(), 59.into()], context)?;\n\n    assert_eq!(\n        date.to_json(context)?,\n        JsValue::from(js_string!(\"2000-10-09T20:12:59.059Z\"))\n    );\n\n    assert_eq!(\n        date.to_date_string(context)?,\n        JsValue::from(js_string!(\"Mon Oct 09 2000\"))\n    );\n\n    assert_eq!(\n        date.to_iso_string(context)?,\n        JsValue::from(js_string!(\"2000-10-09T20:12:59.059Z\"))\n    );\n\n    assert_eq!(\n        date.to_time_string(context)?,\n        JsValue::from(js_string!(\"23:12:59 GMT+0300\"))\n    );\n\n    assert_eq!(\n        date.to_string(context)?,\n        JsValue::from(js_string!(\"Mon Oct 09 2000 23:12:59 GMT+0300\"))\n    );\n\n    Ok(())\n}\n"
  },
  {
    "path": "examples/src/bin/jsgeneratorfunction.rs",
    "content": "//! Example demonstrating the `JsGeneratorFunction` API wrapper.\nuse boa_engine::{Context, JsValue, Source, object::builtins::JsGeneratorFunction};\n\nfn main() {\n    let mut context = Context::default();\n\n    let result = context\n        .eval(Source::from_bytes(\n            \"function* count() { yield 1; yield 2; yield 3; } count\",\n        ))\n        .unwrap();\n\n    let obj = result.as_object().unwrap().clone();\n    let gen_fn = JsGeneratorFunction::from_object(obj).unwrap();\n\n    // Call the generator function to obtain a generator instance\n    let generator = gen_fn\n        .call(&JsValue::undefined(), &[], &mut context)\n        .unwrap();\n\n    // Iterate the generator\n    let result = generator.next(JsValue::undefined(), &mut context).unwrap();\n    println!(\"next: {}\", result.display());\n\n    let result = generator.next(JsValue::undefined(), &mut context).unwrap();\n    println!(\"next: {}\", result.display());\n}\n"
  },
  {
    "path": "examples/src/bin/jsmap.rs",
    "content": "use boa_engine::{\n    Context, JsResult, JsValue, js_string,\n    object::builtins::{JsArray, JsMap},\n};\n\nfn main() -> JsResult<()> {\n    // Create a `Context` for the Javascript executor.\n    let context = &mut Context::default();\n\n    // Create a new empty map.\n    let map = JsMap::new(context);\n\n    // Set a key-value for the map.\n    map.set(js_string!(\"Key-1\"), js_string!(\"Value-1\"), context)?;\n\n    let map_check = map.has(js_string!(\"Key-1\"), context)?;\n    assert_eq!(map_check, true.into()); // true\n\n    // Set a second key-value to the same map.\n    map.set(2, 4, context)?;\n\n    assert_eq!(map.get_size(context)?, 2.into()); //true\n\n    assert_eq!(\n        map.get(js_string!(\"Key-1\"), context)?,\n        js_string!(\"Value-1\").into()\n    );\n    assert_eq!(map.get(2, context)?, 4.into());\n    // Delete an entry with a provided key.\n    map.delete(js_string!(\"Key-1\"), context)?;\n    assert_eq!(map.get_size(context)?, 1.into());\n\n    let deleted_key_one = map.get(js_string!(\"Key-1\"), context)?;\n\n    assert_eq!(deleted_key_one, JsValue::undefined());\n\n    // Retrieve a MapIterator for all entries in the Map.\n    let entries = map.entries(context)?;\n\n    let _first_value = entries.next(context)?;\n\n    // Create a multidimensional array with key value pairs -> [[first-key, first-value], [second-key, second-value]]\n    let js_array = JsArray::new(context)?;\n\n    let vec_one = vec![\n        JsValue::new(js_string!(\"first-key\")),\n        JsValue::new(js_string!(\"first-value\")),\n    ];\n    let vec_two = vec![\n        JsValue::new(js_string!(\"second-key\")),\n        JsValue::new(js_string!(\"second-value\")),\n    ];\n\n    js_array.push(JsArray::from_iter(vec_one, context), context)?;\n    js_array.push(JsArray::from_iter(vec_two, context), context)?;\n\n    // Create a map from the JsArray using it's iterable property.\n    let iter_map = JsMap::from_js_iterable(&js_array.into(), context)?;\n\n    assert_eq!(\n        iter_map.get(js_string!(\"first-key\"), context)?,\n        js_string!(\"first-value\").into()\n    );\n\n    iter_map.set(js_string!(\"third-key\"), js_string!(\"third-value\"), context)?;\n\n    assert_eq!(iter_map.get_size(context)?, JsValue::new(3));\n\n    Ok(())\n}\n"
  },
  {
    "path": "examples/src/bin/jspromise.rs",
    "content": "use boa_engine::{\n    Context, JsArgs, JsError, JsNativeError, JsResult, JsValue, NativeFunction,\n    builtins::promise::PromiseState, js_string, object::builtins::JsPromise,\n};\n\n// Simulate an API call that returns a Promise\nasync fn simulate_api_call(success: bool, delay_ms: u64) -> JsResult<JsValue> {\n    // simulate network delay\n    tokio::time::sleep(tokio::time::Duration::from_millis(delay_ms)).await;\n\n    if success {\n        Ok(js_string!(\"API call successful!\").into())\n    } else {\n        Err(JsError::from_native(\n            JsNativeError::error().with_message(\"API call failed!\"),\n        ))\n    }\n}\n\n#[tokio::main]\nasync fn main() -> Result<(), Box<dyn std::error::Error>> {\n    let context = &mut Context::default();\n\n    println!(\"1. Basic Promise Creation and Handling\");\n    // Create a promise that resolves after a delay\n    let promise = JsPromise::new(\n        |resolvers, context| {\n            let result = js_string!(\"Hello from Promise!\").into();\n            resolvers\n                .resolve\n                .call(&JsValue::undefined(), &[result], context)?;\n            Ok(JsValue::undefined())\n        },\n        context,\n    )?;\n\n    // Add success and error handlers\n    let _promise = promise\n        .then(\n            Some(\n                NativeFunction::from_fn_ptr(|_, args, _context| {\n                    let value = args.get_or_undefined(0);\n                    println!(\"Promise resolved with: {}\", value.display());\n                    Ok(value.clone())\n                })\n                .to_js_function(context.realm()),\n            ),\n            Some(\n                NativeFunction::from_fn_ptr(|_, args, _context| {\n                    let error = args.get_or_undefined(0);\n                    println!(\"Promise rejected with: {}\", error.display());\n                    Err(JsError::from_opaque(error.clone()))\n                })\n                .to_js_function(context.realm()),\n            ),\n            context,\n        )?\n        .finally(\n            NativeFunction::from_fn_ptr(|_, _, _| {\n                println!(\"Promise settled!\");\n                Ok(JsValue::undefined())\n            })\n            .to_js_function(context.realm()),\n            context,\n        )?;\n\n    // Run the event loop to process promises\n    drop(context.run_jobs());\n\n    println!(\"\\n2. Promise.all Example\");\n    // Create multiple promises\n    let promises = vec![\n        JsPromise::resolve(1, context)?,\n        JsPromise::resolve(2, context)?,\n        JsPromise::resolve(3, context)?,\n    ];\n\n    let all_promise = JsPromise::all(promises, context)?;\n    drop(context.run_jobs());\n\n    match all_promise.state() {\n        PromiseState::Fulfilled(value) => {\n            println!(\"All promises fulfilled with: {}\", value.display());\n        }\n        PromiseState::Rejected(error) => {\n            println!(\"One of the promises rejected with: {}\", error.display());\n        }\n        PromiseState::Pending => {\n            println!(\"Promises are still pending\");\n        }\n    }\n\n    println!(\"\\n3. Promise.race Example\");\n    // Create promises that resolve at different times\n    let (fast_promise, fast_resolvers) = JsPromise::new_pending(context);\n    let (slow_promise, slow_resolvers) = JsPromise::new_pending(context);\n\n    let race_promise = JsPromise::race([fast_promise, slow_promise], context)?;\n\n    // Resolve promises in different order\n    slow_resolvers\n        .resolve\n        .call(&JsValue::undefined(), &[js_string!(\"Slow\").into()], context)?;\n    fast_resolvers\n        .resolve\n        .call(&JsValue::undefined(), &[js_string!(\"Fast\").into()], context)?;\n\n    drop(context.run_jobs());\n\n    if let PromiseState::Fulfilled(value) = race_promise.state() {\n        println!(\"Race won by: {}\", value.display());\n    }\n\n    println!(\"\\n4. Converting Rust Future to Promise\");\n    // Create a promise from an async function\n    let future_promise =\n        JsPromise::from_async_fn(async |_| simulate_api_call(true, 100).await, context);\n    drop(context.run_jobs());\n\n    match future_promise.state() {\n        PromiseState::Fulfilled(value) => {\n            println!(\"Future resolved with: {}\", value.display());\n        }\n        PromiseState::Rejected(error) => {\n            println!(\"Future rejected with: {}\", error.display());\n        }\n        PromiseState::Pending => {\n            println!(\"Future is still pending\");\n        }\n    }\n\n    println!(\"\\n5. Promise.any Example\");\n    let promises = vec![\n        JsPromise::reject(JsNativeError::error().with_message(\"Error 1\"), context)?,\n        JsPromise::resolve(js_string!(\"Success!\"), context)?,\n        JsPromise::reject(JsNativeError::error().with_message(\"Error 2\"), context)?,\n    ];\n\n    let any_promise = JsPromise::any(promises, context)?;\n    drop(context.run_jobs());\n\n    match any_promise.state() {\n        PromiseState::Fulfilled(value) => {\n            println!(\"First fulfilled promise value: {}\", value.display());\n        }\n        PromiseState::Rejected(error) => {\n            println!(\"All promises rejected with: {}\", error.display());\n        }\n        PromiseState::Pending => {\n            println!(\"Promises are still pending\");\n        }\n    }\n\n    Ok(())\n}\n"
  },
  {
    "path": "examples/src/bin/jsregexp.rs",
    "content": "use boa_engine::{Context, JsResult, js_string, object::builtins::JsRegExp};\n\nfn main() -> JsResult<()> {\n    let context = &mut Context::default();\n\n    let regexp = JsRegExp::new(js_string!(\"foo\"), js_string!(\"gi\"), context)?;\n\n    let test_result = regexp.test(js_string!(\"football\"), context)?;\n    assert!(test_result);\n\n    let flags = regexp.flags(context)?;\n    assert_eq!(flags, String::from(\"gi\"));\n\n    let src = regexp.source(context)?;\n    assert_eq!(src, String::from(\"foo\"));\n\n    let to_string = regexp.to_string(context)?;\n    assert_eq!(to_string, String::from(\"/foo/gi\"));\n\n    Ok(())\n}\n"
  },
  {
    "path": "examples/src/bin/jsset.rs",
    "content": "// This example shows how to manipulate a Javascript Set using Rust code.\n#![allow(clippy::bool_assert_comparison)]\nuse boa_engine::{Context, JsError, JsValue, js_string, object::builtins::JsSet};\n\nfn main() -> Result<(), JsError> {\n    // New `Context` for a new Javascript executor.\n    let context = &mut Context::default();\n\n    // Create an empty set.\n    let set = JsSet::new(context);\n\n    assert_eq!(set.size(), 0);\n    set.add(5, context)?;\n    assert_eq!(set.size(), 1);\n    set.add(10, context)?;\n    assert_eq!(set.size(), 2);\n    set.clear();\n    assert_eq!(set.size(), 0);\n\n    set.add(js_string!(\"one\"), context)?;\n    set.add(js_string!(\"two\"), context)?;\n    set.add(js_string!(\"three\"), context)?;\n\n    assert!(set.has(js_string!(\"one\")));\n    assert_eq!(set.has(js_string!(\"One\")), false);\n\n    set.delete(js_string!(\"two\"));\n\n    assert_eq!(set.has(js_string!(\"two\"),), false);\n\n    set.clear();\n\n    assert_eq!(set.has(js_string!(\"one\")), false);\n    assert_eq!(set.has(js_string!(\"three\")), false);\n    assert_eq!(set.size(), 0);\n\n    // Add a slice into a set;\n    set.add_items(\n        &[JsValue::new(1), JsValue::new(2), JsValue::new(3)],\n        context,\n    )?;\n    // Will return 1, as one slice was added.\n    assert_eq!(set.size(), 1);\n\n    // Make a new set from a slice\n    let slice_set = JsSet::from_iter([JsValue::new(1), JsValue::new(2), JsValue::new(3)], context);\n    // Will return 3, as each element of slice was added into the set.\n    assert_eq!(slice_set.size(), 3);\n\n    set.clear();\n\n    Ok(())\n}\n"
  },
  {
    "path": "examples/src/bin/jstypedarray.rs",
    "content": "// This example shows how to manipulate a Javascript array using Rust code.\n\nuse boa_engine::{\n    Context, JsNativeError, JsResult, JsValue, js_string,\n    native_function::NativeFunction,\n    object::{\n        FunctionObjectBuilder,\n        builtins::{JsArray, JsArrayBuffer, JsUint8Array},\n    },\n    property::Attribute,\n};\nuse boa_gc::{Gc, GcRefCell};\n\nfn main() -> JsResult<()> {\n    // We create a new `Context` to create a new Javascript executor.\n    let context = &mut Context::default();\n\n    let data: Vec<u8> = (0..=255).collect();\n\n    let array = JsUint8Array::from_iter(data, context)?;\n\n    assert_eq!(array.get(0, context)?, JsValue::new(0));\n\n    let mut sum = 0;\n\n    for i in 0..=255 {\n        assert_eq!(array.at(i, context)?, JsValue::new(i));\n        sum += i;\n    }\n\n    let callback = FunctionObjectBuilder::new(\n        context.realm(),\n        NativeFunction::from_fn_ptr(|_this, args, context| {\n            let accumulator = args.first().cloned().unwrap_or_default();\n            let value = args.get(1).cloned().unwrap_or_default();\n\n            accumulator.add(&value, context)\n        }),\n    )\n    .build();\n\n    assert_eq!(\n        array.reduce(callback, Some(JsValue::new(0)), context)?,\n        JsValue::new(sum)\n    );\n\n    let greater_than_10_predicate = FunctionObjectBuilder::new(\n        context.realm(),\n        NativeFunction::from_fn_ptr(|_this, args, _context| {\n            let element = args\n                .first()\n                .cloned()\n                .unwrap_or_default()\n                .as_number()\n                .expect(\"error at number conversion\");\n            Ok(JsValue::from(element > 10.0))\n        }),\n    )\n    .build();\n\n    assert_eq!(\n        array.find_index(greater_than_10_predicate, None, context),\n        Ok(Some(11))\n    );\n\n    let lower_than_200_predicate = FunctionObjectBuilder::new(\n        context.realm(),\n        NativeFunction::from_fn_ptr(|_this, args, _context| {\n            let element = args\n                .first()\n                .cloned()\n                .unwrap_or_default()\n                .as_number()\n                .expect(\"error at number conversion\");\n            Ok(JsValue::from(element < 200.0))\n        }),\n    )\n    .build();\n\n    assert_eq!(\n        array.find_last(lower_than_200_predicate.clone(), None, context),\n        Ok(JsValue::from(199u8))\n    );\n\n    let data: Vec<u8> = vec![90, 120, 150, 180, 210, 240];\n    let array = JsUint8Array::from_iter(data, context)?;\n\n    assert_eq!(\n        array.find_last_index(lower_than_200_predicate, None, context),\n        Ok(Some(3))\n    );\n\n    // forEach\n    let array = JsUint8Array::from_iter(vec![1, 2, 3, 4, 5], context)?;\n    let num_to_modify = Gc::new(GcRefCell::new(0u8));\n\n    let js_function = FunctionObjectBuilder::new(\n        context.realm(),\n        NativeFunction::from_copy_closure_with_captures(\n            |_, args, captures, inner_context| {\n                let element = args\n                    .first()\n                    .cloned()\n                    .unwrap_or_default()\n                    .to_uint8(inner_context)\n                    .expect(\"error at number conversion\");\n\n                *captures.borrow_mut() += element;\n                Ok(JsValue::undefined())\n            },\n            Gc::clone(&num_to_modify),\n        ),\n    )\n    .build();\n\n    let _unused = array.for_each(js_function, None, context);\n\n    let borrow = *num_to_modify.borrow();\n    assert_eq!(borrow, 15u8);\n\n    // includes\n    assert_eq!(array.includes(JsValue::new(2), None, context), Ok(true));\n    let empty_array = JsUint8Array::from_iter(vec![], context)?;\n    assert_eq!(\n        empty_array.includes(JsValue::new(2), None, context),\n        Ok(false)\n    );\n\n    // set\n    let array_buffer8 = JsArrayBuffer::new(8, context)?;\n    let initialized8_array = JsUint8Array::from_array_buffer(array_buffer8, context)?;\n    initialized8_array.set_values(\n        JsArray::from_iter(vec![JsValue::new(1), JsValue::new(2)], context).into(),\n        Some(3),\n        context,\n    )?;\n    assert_eq!(initialized8_array.get(0, context)?, JsValue::new(0));\n    assert_eq!(initialized8_array.get(1, context)?, JsValue::new(0));\n    assert_eq!(initialized8_array.get(2, context)?, JsValue::new(0));\n    assert_eq!(initialized8_array.get(3, context)?, JsValue::new(1.0));\n    assert_eq!(initialized8_array.get(4, context)?, JsValue::new(2.0));\n    assert_eq!(initialized8_array.get(5, context)?, JsValue::new(0));\n    assert_eq!(initialized8_array.get(6, context)?, JsValue::new(0));\n    assert_eq!(initialized8_array.get(7, context)?, JsValue::new(0));\n    assert_eq!(initialized8_array.get(8, context)?, JsValue::undefined());\n\n    // subarray\n    let array = JsUint8Array::from_iter(vec![1u8, 2u8, 3u8, 4u8, 5u8, 6u8, 7u8, 8u8], context)?;\n    let subarray2_6 = array.subarray(2, 6, context)?;\n    assert_eq!(subarray2_6.length(context)?, 4);\n    assert_eq!(subarray2_6.get(0, context)?, JsValue::new(3.0));\n    assert_eq!(subarray2_6.get(1, context)?, JsValue::new(4.0));\n    assert_eq!(subarray2_6.get(2, context)?, JsValue::new(5.0));\n    assert_eq!(subarray2_6.get(3, context)?, JsValue::new(6.0));\n\n    let subarray4_6 = array.subarray(-4, 6, context)?;\n    assert_eq!(subarray4_6.length(context)?, 2);\n    assert_eq!(subarray4_6.get(0, context)?, JsValue::new(5.0));\n    assert_eq!(subarray4_6.get(1, context)?, JsValue::new(6.0));\n\n    // buffer\n    let array_buffer8 = JsArrayBuffer::new(8, context)?;\n    let array = JsUint8Array::from_array_buffer(array_buffer8, context)?;\n\n    assert_eq!(\n        array\n            .buffer(context)?\n            .as_object()\n            .unwrap()\n            .get(js_string!(\"byteLength\"), context)\n            .unwrap(),\n        JsValue::new(8)\n    );\n\n    // constructor\n    assert_eq!(\n        Err(JsNativeError::typ()\n            .with_message(\"the TypedArray constructor should never be called directly\")\n            .into()),\n        array.constructor(context)\n    );\n\n    // copyWithin\n    let array = JsUint8Array::from_iter(vec![1u8, 2u8, 3u8, 4u8, 5u8, 6u8, 7u8, 8u8], context)?;\n    array.copy_within(3, 1, Some(3), context)?;\n    assert_eq!(array.get(0, context)?, JsValue::new(1.0));\n    assert_eq!(array.get(1, context)?, JsValue::new(2.0));\n    assert_eq!(array.get(2, context)?, JsValue::new(3.0));\n    assert_eq!(array.get(3, context)?, JsValue::new(2.0));\n    assert_eq!(array.get(4, context)?, JsValue::new(3.0));\n    assert_eq!(array.get(5, context)?, JsValue::new(6.0));\n    assert_eq!(array.get(6, context)?, JsValue::new(7.0));\n    assert_eq!(array.get(7, context)?, JsValue::new(8.0));\n\n    // toLocaleString\n    // let array = JsUint32Array::from_iter(vec![500, 8123, 12], context)?;\n    // let locales: Option<JsValue> = Some(js_string!(\"de-DE\").into());\n    // let options = Some(context.eval(Source::from_bytes(\n    //     r##\"let options = { style: \"currency\", currency: \"EUR\" }; options;\"##,\n    // ))?);\n    // assert_eq!(\n    //     array.to_locale_string(locales, options, context)?,\n    //     js_string!(\"500,00 €,8.123,00 €,12,00 €\").into()\n    // );\n\n    // toStringTag\n    let array = JsUint8Array::from_iter(vec![1u8, 2u8, 3u8, 4u8, 5u8, 6u8, 7u8, 8u8], context)?;\n    let tag = array.to_string_tag(context)?.to_string(context)?;\n    assert_eq!(tag, js_string!(\"Uint8Array\"));\n\n    context\n        .register_global_property(\n            js_string!(\"myUint8Array\"),\n            array,\n            Attribute::WRITABLE | Attribute::ENUMERABLE | Attribute::CONFIGURABLE,\n        )\n        .unwrap();\n\n    Ok(())\n}\n"
  },
  {
    "path": "examples/src/bin/jsweakmap.rs",
    "content": "//! Example demonstrating the `JsWeakMap` API wrapper.\nuse boa_engine::{Context, js_string, object::builtins::JsWeakMap};\n\nfn main() {\n    let mut context = Context::default();\n\n    // Create a new WeakMap\n    let weak_map = JsWeakMap::new(&mut context);\n\n    // Create an object to use as a key\n    let key = context\n        .eval(boa_engine::Source::from_bytes(\"({})\"))\n        .unwrap();\n    let key_obj = key.as_object().unwrap().clone();\n\n    // Set a value\n    weak_map\n        .set(&key_obj, js_string!(\"hello\").into(), &mut context)\n        .unwrap();\n\n    // Get the value\n    let val = weak_map.get(&key_obj, &mut context).unwrap();\n    println!(\"get: {}\", val.display());\n\n    // Has the key\n    let has = weak_map.has(&key_obj, &mut context).unwrap();\n    println!(\"has: {has}\");\n\n    // Delete the key\n    let deleted = weak_map.delete(&key_obj, &mut context).unwrap();\n    println!(\"deleted: {deleted}\");\n\n    // Has after delete\n    let has_after = weak_map.has(&key_obj, &mut context).unwrap();\n    println!(\"has after delete: {has_after}\");\n}\n"
  },
  {
    "path": "examples/src/bin/jsweakset.rs",
    "content": "//! Example demonstrating the `JsWeakSet` API wrapper.\nuse boa_engine::{Context, Source, object::builtins::JsWeakSet};\n\nfn main() {\n    let mut context = Context::default();\n\n    // Create a new WeakSet\n    let weak_set = JsWeakSet::new(&mut context);\n\n    // Create an object to use as a value\n    let val = context.eval(Source::from_bytes(\"({})\")).unwrap();\n    let val_obj = val.as_object().unwrap().clone();\n\n    // Add the object\n    weak_set.add(&val_obj, &mut context).unwrap();\n\n    // Has the object\n    let has = weak_set.has(&val_obj, &mut context).unwrap();\n    println!(\"has: {has}\");\n\n    // Delete the object\n    let deleted = weak_set.delete(&val_obj, &mut context).unwrap();\n    println!(\"deleted: {deleted}\");\n\n    // Has after delete\n    let has_after = weak_set.has(&val_obj, &mut context).unwrap();\n    println!(\"has after delete: {has_after}\");\n}\n"
  },
  {
    "path": "examples/src/bin/loadfile.rs",
    "content": "// This example shows how to load, parse and execute JS code from a source file\n// (./scripts/helloworld.js)\n\nuse std::{error::Error, path::Path};\n\nuse boa_engine::{Context, Source, property::Attribute};\nuse boa_runtime::Console;\n\n/// Adds the custom runtime to the context.\nfn add_runtime(context: &mut Context) {\n    // We first add the `console` object, to be able to call `console.log()`.\n    let console = Console::init(context);\n    context\n        .register_global_property(Console::NAME, console, Attribute::all())\n        .expect(\"the console builtin shouldn't exist\");\n}\n\nfn main() -> Result<(), Box<dyn Error>> {\n    let js_file_path = \"./scripts/helloworld.js\";\n\n    let source = Source::from_filepath(Path::new(js_file_path))?;\n\n    // Instantiate the execution context\n    let mut context = Context::default();\n    // Add the runtime intrinsics\n    add_runtime(&mut context);\n    // Parse the source code and print the result\n    println!(\"{}\", context.eval(source)?.display());\n\n    Ok(())\n}\n"
  },
  {
    "path": "examples/src/bin/loadstring.rs",
    "content": "// This example loads, parses and executes a JS code string\n\nuse boa_engine::{Context, Source};\n\nfn main() {\n    let js_code = \"'Hello World ' + 'from a JS code ' + 'string!'\";\n\n    // Instantiate the execution context\n    let mut context = Context::default();\n\n    // Parse the source code\n    match context.eval(Source::from_bytes(js_code)) {\n        Ok(res) => {\n            println!(\n                \"{}\",\n                res.to_string(&mut context).unwrap().to_std_string_escaped()\n            );\n        }\n        Err(e) => {\n            // Pretty print the error\n            eprintln!(\"Uncaught {e}\");\n        }\n    };\n}\n"
  },
  {
    "path": "examples/src/bin/module_fetch_async.rs",
    "content": "use std::{cell::RefCell, collections::VecDeque, rc::Rc};\n\nuse boa_engine::{\n    Context, JsNativeError, JsResult, JsValue, Module,\n    builtins::promise::PromiseState,\n    job::{Job, JobExecutor, NativeAsyncJob, PromiseJob},\n    js_error, js_string,\n    module::{ModuleLoader, ModuleRequest},\n};\nuse boa_parser::Source;\nuse futures_concurrency::future::FutureGroup;\nuse smol::{future, stream::StreamExt};\n\n#[derive(Debug, Default)]\nstruct HttpModuleLoader;\n\nimpl ModuleLoader for HttpModuleLoader {\n    async fn load_imported_module(\n        self: Rc<Self>,\n        _referrer: boa_engine::module::Referrer,\n        request: ModuleRequest,\n        context: &RefCell<&mut Context>,\n    ) -> JsResult<Module> {\n        let url = request.specifier().to_std_string_escaped();\n\n        // Adding some prints to show the non-deterministic nature of the async fetches.\n        // Try to run the example several times to see how sometimes the fetches start in order\n        // but finish in disorder.\n        println!(\"Fetching `{url}`...\");\n\n        // This could also retry fetching in case there's an error while requesting the module.\n        // async, a poor man's `try` block lmao\n        let response = async { reqwest::get(&url).await?.text().await }\n            .await\n            .map_err(|err| JsNativeError::typ().with_message(err.to_string()))?;\n\n        println!(\"Finished fetching `{url}`\");\n\n        // Could also add a path if needed.\n        let source = Source::from_bytes(&response);\n\n        Module::parse(source, None, &mut context.borrow_mut())\n    }\n}\n\nfn main() -> JsResult<()> {\n    // A simple snippet that imports modules from the web instead of the file system.\n    const SRC: &str = r#\"\n        import YAML from 'https://esm.run/yaml@2.3.4';\n        import fromAsync from 'https://esm.run/array-from-async@3.0.0';\n        import { Base64 } from 'https://esm.run/js-base64@3.7.6';\n\n        const data = `\n            object:\n                array: [\"hello\", \"world\"]\n                key: \"value\"\n        `;\n\n        const object = YAML.parse(data).object;\n\n        let result = await fromAsync([\n            Promise.resolve(Base64.encode(object.array[0])),\n            Promise.resolve(Base64.encode(object.array[1])),\n        ]);\n\n        export default result;\n    \"#;\n\n    rustls::crypto::ring::default_provider()\n        .install_default()\n        .map_err(\n            |_| js_error!(TypeError: \"could not install ring as the default crypto provider\"),\n        )?;\n\n    let context = &mut Context::builder()\n        .job_executor(Rc::new(Queue::new()))\n        // NEW: sets the context module loader to our custom loader\n        .module_loader(Rc::new(HttpModuleLoader))\n        .build()?;\n\n    let module = Module::parse(Source::from_bytes(SRC.as_bytes()), None, context)?;\n\n    // Calling `Module::load_link_evaluate` takes care of having to define promise handlers for\n    // `Module::load` and `Module::evaluate`.\n    let promise = module.load_link_evaluate(context);\n\n    // Important to call `Context::run_jobs`, or else all the futures and promises won't be\n    // pushed forward by the job queue.\n    context.run_jobs()?;\n\n    match promise.state() {\n        // Our job queue guarantees that all promises and futures are finished after returning\n        // from `Context::run_jobs`.\n        // Some other job queue designs only execute a \"microtick\" or a single pass through the\n        // pending promises and futures. In that case, you can pass this logic as a promise handler\n        // for `promise` instead.\n        PromiseState::Pending => panic!(\"module didn't execute!\"),\n        // All modules after successfully evaluating return `JsValue::undefined()`.\n        PromiseState::Fulfilled(v) => {\n            assert_eq!(v, JsValue::undefined())\n        }\n        PromiseState::Rejected(err) => {\n            panic!(\"{}\", err.display());\n        }\n    }\n\n    let default = module\n        .namespace(context)\n        .get(js_string!(\"default\"), context)?;\n\n    // `default` should contain the result of our calculations.\n    let default = default\n        .as_object()\n        .ok_or_else(|| JsNativeError::typ().with_message(\"default export was not an object\"))?;\n\n    assert_eq!(\n        default\n            .get(0, context)?\n            .as_string()\n            .ok_or_else(|| JsNativeError::typ().with_message(\"array element was not a string\"))?,\n        js_string!(\"aGVsbG8=\")\n    );\n    assert_eq!(\n        default\n            .get(1, context)?\n            .as_string()\n            .ok_or_else(|| JsNativeError::typ().with_message(\"array element was not a string\"))?,\n        js_string!(\"d29ybGQ=\")\n    );\n\n    Ok(())\n}\n\n// Taken from the `smol_event_loop.rs` example.\n/// An event queue using smol to drive futures to completion.\nstruct Queue {\n    async_jobs: RefCell<VecDeque<NativeAsyncJob>>,\n    promise_jobs: RefCell<VecDeque<PromiseJob>>,\n}\n\nimpl Queue {\n    fn new() -> Self {\n        Self {\n            async_jobs: RefCell::default(),\n            promise_jobs: RefCell::default(),\n        }\n    }\n\n    fn drain_jobs(&self, context: &mut Context) {\n        let jobs = std::mem::take(&mut *self.promise_jobs.borrow_mut());\n        for job in jobs {\n            if let Err(e) = job.call(context) {\n                eprintln!(\"Uncaught {e}\");\n            }\n        }\n    }\n}\n\nimpl JobExecutor for Queue {\n    fn enqueue_job(self: Rc<Self>, job: Job, _context: &mut Context) {\n        match job {\n            Job::PromiseJob(job) => self.promise_jobs.borrow_mut().push_back(job),\n            Job::AsyncJob(job) => self.async_jobs.borrow_mut().push_back(job),\n            _ => panic!(\"unsupported job type\"),\n        }\n    }\n\n    // While the sync flavor of `run_jobs` will block the current thread until all the jobs have finished...\n    fn run_jobs(self: Rc<Self>, context: &mut Context) -> JsResult<()> {\n        let runtime =\n            tokio::runtime::Runtime::new().map_err(|err| js_error!(TypeError: \"{}\", err))?;\n        runtime.block_on(self.run_jobs_async(&RefCell::new(context)))\n    }\n\n    // ...the async flavor won't, which allows concurrent execution with external async tasks.\n    async fn run_jobs_async(self: Rc<Self>, context: &RefCell<&mut Context>) -> JsResult<()> {\n        let mut group = FutureGroup::new();\n        loop {\n            for job in std::mem::take(&mut *self.async_jobs.borrow_mut()) {\n                group.insert(job.call(context));\n            }\n\n            if group.is_empty() && self.promise_jobs.borrow().is_empty() {\n                // Both queues are empty. We can exit.\n                return Ok(());\n            }\n\n            // We have some jobs pending on the microtask queue. Try to poll the pending\n            // tasks once to see if any of them finished, and run the pending microtasks\n            // otherwise.\n            if let Some(Err(err)) = future::poll_once(group.next()).await.flatten() {\n                eprintln!(\"Uncaught {err}\");\n            };\n\n            // Only one macrotask can be executed before the next drain of the microtask queue.\n            self.drain_jobs(&mut context.borrow_mut());\n            future::yield_now().await\n        }\n    }\n}\n"
  },
  {
    "path": "examples/src/bin/modulehandler.rs",
    "content": "// This example implements a custom module handler which mimics\n// the require/module.exports pattern\n\nuse boa_engine::{\n    Context, JsArgs, JsNativeError, JsResult, JsValue, Source, js_string,\n    native_function::NativeFunction, prelude::JsObject, property::Attribute,\n};\nuse boa_runtime::Console;\nuse std::{error::Error, fs::read_to_string};\n\n/// Adds the custom runtime to the context.\nfn add_runtime(context: &mut Context) {\n    // We first add the `console` object, to be able to call `console.log()`.\n    let console = Console::init(context);\n    context\n        .register_global_property(Console::NAME, console, Attribute::all())\n        .expect(\"the console builtin shouldn't exist\");\n}\n\nfn main() -> Result<(), Box<dyn Error>> {\n    let js_file_path = \"./scripts/calctest.js\";\n    let buffer = read_to_string(js_file_path)?;\n\n    // Creating the execution context\n    let mut ctx = Context::default();\n\n    // Adding the runtime intrinsics to the context\n    add_runtime(&mut ctx);\n\n    // Adding custom implementation that mimics 'require'\n    ctx.register_global_callable(\"require\".into(), 0, NativeFunction::from_fn_ptr(require))?;\n\n    // Adding custom object that mimics 'module.exports'\n    let moduleobj = JsObject::default(ctx.intrinsics());\n    moduleobj.set(js_string!(\"exports\"), js_string!(\" \"), false, &mut ctx)?;\n\n    ctx.register_global_property(\n        js_string!(\"module\"),\n        JsValue::from(moduleobj),\n        Attribute::default(),\n    )?;\n\n    // Instantiating the engine with the execution context\n    // Loading, parsing and executing the JS code from the source file\n    ctx.eval(Source::from_bytes(&buffer))?;\n\n    Ok(())\n}\n\n// Custom implementation that mimics the 'require' module loader\nfn require(_: &JsValue, args: &[JsValue], ctx: &mut Context) -> JsResult<JsValue> {\n    let arg = args.get_or_undefined(0);\n\n    // BUG: Dev branch seems to be passing string arguments along with quotes\n    let libfile = arg.to_string(ctx)?.to_std_string_escaped();\n\n    // Read the module source file\n    println!(\"Loading: {libfile}\");\n    let buffer =\n        read_to_string(libfile).map_err(|e| JsNativeError::typ().with_message(e.to_string()))?;\n    // Load and parse the module source\n    ctx.eval(Source::from_bytes(&buffer))?;\n\n    // Access module.exports and return as ResultValue\n    let global_obj = ctx.global_object();\n    let module = global_obj.get(js_string!(\"module\"), ctx)?;\n    module\n        .as_object()\n        .ok_or_else(|| JsNativeError::typ().with_message(\"`exports` property was not an object\"))?\n        .get(js_string!(\"exports\"), ctx)\n}\n"
  },
  {
    "path": "examples/src/bin/modules.rs",
    "content": "use std::{error::Error, path::Path, rc::Rc};\n\nuse boa_engine::{\n    Context, JsError, JsNativeError, JsValue, Module, NativeFunction,\n    builtins::promise::PromiseState, js_string, module::SimpleModuleLoader,\n};\nuse boa_parser::Source;\n\n// This example demonstrates how to use Boa's module API\nfn main() -> Result<(), Box<dyn Error>> {\n    // A simple module that we want to compile from Rust code.\n    const MODULE_SRC: &str = r#\"\n        import { pyth } from \"./trig.mjs\";\n        import * as ops from \"./operations.mjs\";\n\n        export let result = pyth(3, 4);\n        export function mix(a, b) {\n            return ops.sum(ops.mult(a, ops.sub(b, a)), 10);\n        }\n    \"#;\n\n    // This can be overridden with any custom implementation of `ModuleLoader`.\n    let loader = Rc::new(SimpleModuleLoader::new(\"./scripts/modules\")?);\n\n    // Just need to cast to a `ModuleLoader` before passing it to the builder.\n    let context = &mut Context::builder().module_loader(loader.clone()).build()?;\n    let source = Source::from_reader(MODULE_SRC.as_bytes(), Some(Path::new(\"./main.mjs\")));\n\n    // Can also pass a `Some(realm)` if you need to execute the module in another realm.\n    let module = Module::parse(source, None, context)?;\n\n    // Don't forget to insert the parsed module into the loader itself, since the root module\n    // is not automatically inserted by the `ModuleLoader::load_imported_module` impl.\n    //\n    // Simulate as if the \"fake\" module is located in the modules root, just to ensure that\n    // the loader won't double load in case someone tries to import \"./main.mjs\".\n    loader.insert(\n        Path::new(\"./scripts/modules\")\n            .canonicalize()?\n            .join(\"main.mjs\"),\n        module.clone(),\n    );\n\n    // The lifecycle of the module is tracked using promises which can be a bit cumbersome to use.\n    // If you just want to directly execute a module, you can use the `Module::load_link_evaluate`\n    // method to skip all the boilerplate.\n    // This does the full version for demonstration purposes.\n    //\n    // parse -> load -> link -> evaluate\n    let promise_result = module\n        // Initial load that recursively loads the module's dependencies.\n        // This returns a `JsPromise` that will be resolved when loading finishes,\n        // which allows async loads and async fetches.\n        .load(context)\n        .then(\n            Some(\n                NativeFunction::from_copy_closure_with_captures(\n                    |_, _, module, context| {\n                        // After loading, link all modules by resolving the imports\n                        // and exports on the full module graph, initializing module\n                        // environments. This returns a plain `Err` since all modules\n                        // must link at the same time.\n                        module.link(context)?;\n                        Ok(JsValue::undefined())\n                    },\n                    module.clone(),\n                )\n                .to_js_function(context.realm()),\n            ),\n            None,\n            context,\n        )\n        .expect(\"`then` cannot fail for a native `JsPromise`\")\n        .then(\n            Some(\n                NativeFunction::from_copy_closure_with_captures(\n                    // Finally, evaluate the root module.\n                    // This returns a `JsPromise` since a module could have\n                    // top-level await statements, which defers module execution to the\n                    // job queue.\n                    |_, _, module, context| Ok(module.evaluate(context)?.into()),\n                    module.clone(),\n                )\n                .to_js_function(context.realm()),\n            ),\n            None,\n            context,\n        )\n        .expect(\"`then` cannot fail for a native `JsPromise`\");\n\n    // Very important to push forward the job queue after queueing promises.\n    context.run_jobs()?;\n\n    // Checking if the final promise didn't return an error.\n    match promise_result.state() {\n        PromiseState::Pending => return Err(\"module didn't execute!\".into()),\n        PromiseState::Fulfilled(v) => {\n            assert_eq!(v, JsValue::undefined());\n        }\n        PromiseState::Rejected(err) => {\n            return Err(JsError::from_opaque(err).try_native(context)?.into());\n        }\n    }\n\n    // We can access the full namespace of the module with all its exports.\n    let namespace = module.namespace(context);\n    let result = namespace.get(js_string!(\"result\"), context)?;\n\n    println!(\"result = {}\", result.display());\n\n    assert_eq!(\n        namespace.get(js_string!(\"result\"), context)?,\n        JsValue::from(5)\n    );\n\n    let mix = namespace\n        .get(js_string!(\"mix\"), context)?\n        .as_callable()\n        .ok_or_else(|| JsNativeError::typ().with_message(\"mix export wasn't a function!\"))?;\n    let result = mix.call(&JsValue::undefined(), &[5.into(), 10.into()], context)?;\n\n    println!(\"mix(5, 10) = {}\", result.display());\n\n    assert_eq!(result, 35.into());\n\n    Ok(())\n}\n"
  },
  {
    "path": "examples/src/bin/properties.rs",
    "content": "// This example shows how to access the keys and values of a `JsObject`\n\nuse boa_engine::{\n    Context, JsError, JsNativeError, JsValue, Source, js_string, property::PropertyKey,\n};\n\nfn main() -> Result<(), JsError> {\n    // We create a new `Context` to create a new Javascript executor.\n    let mut context = Context::default();\n\n    let value = context.eval(Source::from_bytes(\"({ x: 10, '1': 20 })\"))?;\n    let object = value\n        .as_object()\n        .ok_or_else(|| JsNativeError::typ().with_message(\"Expected object\"))?;\n\n    let keys = object.own_property_keys(&mut context)?;\n\n    assert_eq!(\n        keys,\n        &[PropertyKey::from(1), PropertyKey::from(js_string!(\"x\"))]\n    );\n\n    let mut values = Vec::new();\n    for key in keys {\n        values.push(object.get(key, &mut context)?);\n    }\n\n    assert_eq!(values, &[JsValue::from(20), JsValue::from(10)]);\n\n    Ok(())\n}\n"
  },
  {
    "path": "examples/src/bin/runtime_limits.rs",
    "content": "use boa_engine::{Context, JsValue, Source};\n\nfn main() {\n    // Create the JavaScript context.\n    let mut context = Context::default();\n\n    // -----------------------------------------\n    //  Loop Iteration Limit\n    // -----------------------------------------\n\n    // Set the context's runtime limit on loops to 10 iterations.\n    context.runtime_limits_mut().set_loop_iteration_limit(10);\n\n    // The code below iterates 5 times, so no error is thrown.\n    let result = context.eval(Source::from_bytes(\n        r\"\n            for (let i = 0; i < 5; ++i) { }\n        \",\n    ));\n    assert!(result.is_ok());\n\n    // Here we exceed the limit by 1 iteration and a `RuntimeLimit` error is thrown.\n    //\n    // This error cannot be caught in JavaScript, it can only be caught in Rust code.\n    let result = context.eval(Source::from_bytes(\n        r\"\n            try {\n                for (let i = 0; i < 12; ++i) { }\n            } catch (e) {\n\n            }\n        \",\n    ));\n    assert!(result.is_err());\n\n    // Preventing an infinite loop\n    let result = context.eval(Source::from_bytes(\n        r\"\n            while (true) { }\n        \",\n    ));\n    assert!(result.is_err());\n\n    // The limit applies to all types of loops.\n    let result = context.eval(Source::from_bytes(\n        r\"\n            for (let e of [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]) { }\n        \",\n    ));\n    assert!(result.is_err());\n\n    // -----------------------------------------\n    //  Recursion Limit\n    // -----------------------------------------\n\n    // Create and register `factorial` function.\n    let result = context.eval(Source::from_bytes(\n        r\"\n            function factorial(n) {\n                if (n == 0) {\n                    return 1;\n                }\n\n                return n * factorial(n - 1);\n            }\n        \",\n    ));\n    assert!(result.is_ok());\n\n    // Run function before setting the limit and assert that it works.\n    let result = context.eval(Source::from_bytes(\"factorial(11)\"));\n    assert_eq!(result, Ok(JsValue::new(39_916_800)));\n\n    // Setting runtime limit for recursion to 10.\n    context.runtime_limits_mut().set_recursion_limit(10);\n\n    // Run without exceeding recursion limit and assert that it works.\n    let result = context.eval(Source::from_bytes(\"factorial(8)\"));\n    assert_eq!(result, Ok(JsValue::new(40_320)));\n\n    // Run exceeding limit by 1 and assert that it fails.\n    let result = context.eval(Source::from_bytes(\"factorial(11)\"));\n    assert!(result.is_err());\n}\n"
  },
  {
    "path": "examples/src/bin/smol_event_loop.rs",
    "content": "use boa_engine::context::time::JsInstant;\nuse boa_engine::job::{GenericJob, TimeoutJob};\nuse boa_engine::{\n    Context, JsArgs, JsNativeError, JsResult, JsValue, Script, Source,\n    context::ContextBuilder,\n    job::{Job, JobExecutor, NativeAsyncJob, PromiseJob},\n    js_string,\n    native_function::NativeFunction,\n    property::Attribute,\n};\nuse boa_runtime::Console;\nuse futures_concurrency::future::FutureGroup;\nuse smol::{future, stream::StreamExt};\nuse std::collections::BTreeMap;\nuse std::ops::DerefMut;\nuse std::{\n    cell::RefCell,\n    collections::VecDeque,\n    future::Future,\n    rc::Rc,\n    time::{Duration, Instant},\n};\n\n// This example shows how to create an event loop using the smol runtime.\n// The example contains two \"flavors\" of event loops:\nfn main() -> JsResult<()> {\n    // An internally async event loop. This event loop blocks the execution of the thread\n    // while executing tasks, but internally uses async to run its tasks.\n    internally_async_event_loop()?;\n\n    // An externally async event loop. This event loop can yield to the runtime to concurrently\n    // run tasks with it.\n    externally_async_event_loop()\n}\n\n// Taken from the `smol_event_loop.rs` example.\n/// An event queue using smol to drive futures to completion.\nstruct Queue {\n    async_jobs: RefCell<VecDeque<NativeAsyncJob>>,\n    promise_jobs: RefCell<VecDeque<PromiseJob>>,\n    timeout_jobs: RefCell<BTreeMap<JsInstant, TimeoutJob>>,\n    generic_jobs: RefCell<VecDeque<GenericJob>>,\n}\n\nimpl Queue {\n    fn new() -> Self {\n        Self {\n            async_jobs: RefCell::default(),\n            promise_jobs: RefCell::default(),\n            timeout_jobs: RefCell::default(),\n            generic_jobs: RefCell::default(),\n        }\n    }\n\n    fn drain_timeout_jobs(&self, context: &mut Context) {\n        let now = context.clock().now();\n\n        let mut timeouts_borrow = self.timeout_jobs.borrow_mut();\n        let mut jobs_to_keep = timeouts_borrow.split_off(&now);\n        jobs_to_keep.retain(|_, job| !job.is_cancelled());\n        let jobs_to_run = std::mem::replace(timeouts_borrow.deref_mut(), jobs_to_keep);\n        drop(timeouts_borrow);\n\n        for job in jobs_to_run.into_values() {\n            if let Err(e) = job.call(context) {\n                eprintln!(\"Uncaught {e}\");\n            }\n        }\n    }\n\n    fn drain_jobs(&self, context: &mut Context) {\n        // Run the timeout jobs first.\n        self.drain_timeout_jobs(context);\n\n        let jobs = std::mem::take(&mut *self.promise_jobs.borrow_mut());\n        for job in jobs {\n            if let Err(e) = job.call(context) {\n                eprintln!(\"Uncaught {e}\");\n            }\n        }\n        let job = self.generic_jobs.borrow_mut().pop_front();\n        if let Some(generic) = job\n            && let Err(err) = generic.call(context)\n        {\n            eprintln!(\"Uncaught {err}\");\n        }\n        context.clear_kept_objects();\n    }\n}\n\nimpl JobExecutor for Queue {\n    fn enqueue_job(self: Rc<Self>, job: Job, context: &mut Context) {\n        match job {\n            Job::PromiseJob(job) => self.promise_jobs.borrow_mut().push_back(job),\n            Job::AsyncJob(job) => self.async_jobs.borrow_mut().push_back(job),\n            Job::TimeoutJob(t) => {\n                let now = context.clock().now();\n                self.timeout_jobs.borrow_mut().insert(now + t.timeout(), t);\n            }\n            Job::GenericJob(g) => self.generic_jobs.borrow_mut().push_back(g),\n            _ => panic!(\"unsupported job type\"),\n        }\n    }\n\n    // While the sync flavor of `run_jobs` will block the current thread until all the jobs have finished...\n    fn run_jobs(self: Rc<Self>, context: &mut Context) -> JsResult<()> {\n        smol::block_on(smol::LocalExecutor::new().run(self.run_jobs_async(&RefCell::new(context))))\n    }\n\n    // ...the async flavor won't, which allows concurrent execution with external async tasks.\n    async fn run_jobs_async(self: Rc<Self>, context: &RefCell<&mut Context>) -> JsResult<()> {\n        let mut group = FutureGroup::new();\n        loop {\n            for job in std::mem::take(&mut *self.async_jobs.borrow_mut()) {\n                group.insert(job.call(context));\n            }\n\n            if group.is_empty()\n                && self.promise_jobs.borrow().is_empty()\n                && self.timeout_jobs.borrow().is_empty()\n                && self.generic_jobs.borrow().is_empty()\n            {\n                // All queues are empty. We can exit.\n                return Ok(());\n            }\n\n            // We could have some jobs pending on the microtask queue. Try to poll the pending\n            // tasks once to see if any of them finished.\n            if let Some(Err(err)) = future::poll_once(group.next()).await.flatten() {\n                eprintln!(\"Uncaught {err}\");\n            }\n\n            // Only one macrotask can be executed before the next drain of the microtask queue.\n            self.drain_jobs(&mut context.borrow_mut());\n            future::yield_now().await;\n        }\n    }\n}\n\n// Example async function. Note that the returned future must be 'static.\nfn delay(\n    _this: &JsValue,\n    args: &[JsValue],\n    context: &RefCell<&mut Context>,\n) -> impl Future<Output = JsResult<JsValue>> {\n    let millis = args.get_or_undefined(0).to_u32(&mut context.borrow_mut());\n\n    async move {\n        let millis = millis?;\n        println!(\"Delaying for {millis} milliseconds ...\");\n        let now = Instant::now();\n        smol::Timer::after(Duration::from_millis(u64::from(millis))).await;\n        let elapsed = now.elapsed().as_secs_f64();\n        Ok(elapsed.into())\n    }\n}\n\n// Example interval function, but using a `NativeAsyncJob` instead of an async\n// function to schedule the async job.\nfn interval(this: &JsValue, args: &[JsValue], context: &mut Context) -> JsResult<JsValue> {\n    let Some(function) = args.get_or_undefined(0).as_callable() else {\n        return Err(JsNativeError::typ()\n            .with_message(\"arg must be a callable\")\n            .into());\n    };\n\n    let this = this.clone();\n    let delay = args.get_or_undefined(1).to_u32(context)?;\n    let args = args.get(2..).unwrap_or_default().to_vec();\n\n    context.enqueue_job(\n        NativeAsyncJob::with_realm(\n            async move |context: &RefCell<&mut Context>| {\n                let mut timer = smol::Timer::interval(Duration::from_millis(u64::from(delay)));\n                for _ in 0..10 {\n                    timer.next().await;\n                    if let Err(err) = function.call(&this, &args, &mut context.borrow_mut()) {\n                        eprintln!(\"Uncaught {err}\");\n                    }\n                }\n                Ok(JsValue::undefined())\n            },\n            context.realm().clone(),\n        )\n        .into(),\n    );\n\n    Ok(JsValue::undefined())\n}\n\n/// Adds the custom runtime to the context.\nfn add_runtime(context: &mut Context) {\n    // First add the `console` object, to be able to call `console.log()`.\n    let console = Console::init(context);\n    context\n        .register_global_property(Console::NAME, console, Attribute::all())\n        .expect(\"the console builtin shouldn't exist\");\n\n    // Then, bind the defined async function to the ECMAScript function \"delay\".\n    context\n        .register_global_builtin_callable(\n            js_string!(\"delay\"),\n            1,\n            NativeFunction::from_async_fn(delay),\n        )\n        .expect(\"the delay builtin shouldn't exist\");\n\n    // Finally, bind the defined async job to the ECMAScript function \"interval\".\n    context\n        .register_global_builtin_callable(\n            js_string!(\"interval\"),\n            1,\n            NativeFunction::from_fn_ptr(interval),\n        )\n        .expect(\"the delay builtin shouldn't exist\");\n}\n\n// Script that does multiple calls to multiple async timers.\nconst SCRIPT: &str = r\"\n    function print(elapsed) {\n        console.log(`Finished delay. Elapsed time: ${elapsed * 1000} ms`);\n    }\n\n    delay(1000).then(print);\n    delay(500).then(print);\n    delay(200).then(print);\n    delay(600).then(print);\n    delay(30).then(print);\n\n    let i = 0;\n    function counter() {\n        console.log(`Iteration number ${i} for JS interval`);\n        i += 1;\n    }\n\n    interval(counter, 100);\n\n    for(let i = 0; i <= 100000; i++) {\n        // Emulate a long-running evaluation of a script.\n    }\n\";\n\n// This flavor is most recommended when you have an application that:\n//  - Needs to wait until the engine finishes executing; depends on the execution result to continue.\n//  - Delegates the execution of the application to the engine's event loop.\nfn internally_async_event_loop() -> JsResult<()> {\n    println!(\"====== Internally async event loop. ======\");\n\n    // Initialize the queue and the context\n    let queue = Queue::new();\n    let context = &mut ContextBuilder::new()\n        .job_executor(Rc::new(queue))\n        .build()\n        .unwrap();\n\n    // Then, add the custom runtime.\n    add_runtime(context);\n\n    let now = Instant::now();\n    println!(\"Evaluating script...\");\n    context.eval(Source::from_bytes(SCRIPT)).unwrap();\n\n    // Important to run this after evaluating, since this is what triggers to run the enqueued jobs.\n    println!(\"Running jobs...\");\n    context.run_jobs()?;\n\n    println!(\"Total elapsed time: {:?}\\n\", now.elapsed());\n    Ok(())\n}\n\n// This flavor is most recommended when you have an application that:\n//  - Cannot afford to block until the engine finishes executing.\n//  - Needs to process IO requests between executions that will be consumed by the engine.\nfn externally_async_event_loop() -> JsResult<()> {\n    println!(\"====== Externally async event loop. ======\");\n    let executor = smol::Executor::new();\n\n    smol::block_on(executor.run(async {\n        // Initialize the queue and the context\n        let queue = Rc::new(Queue::new());\n        let context = &mut ContextBuilder::new()\n            .job_executor(queue.clone())\n            .build()\n            .unwrap();\n\n        // Then, add the custom runtime.\n        add_runtime(context);\n\n        let now = Instant::now();\n\n        // Example of an asynchronous workload that must be run alongside the engine.\n        let counter = executor.spawn(async {\n            let mut interval = smol::Timer::interval(Duration::from_millis(100));\n            println!(\"Starting smol interval job...\");\n            for i in 0..10 {\n                interval.next().await;\n                println!(\"Executed interval tick {i}\");\n            }\n            println!(\"Finished smol interval job...\");\n        });\n\n        let engine = async {\n            let script = Script::parse(Source::from_bytes(SCRIPT), None, context).unwrap();\n\n            // `Script::evaluate_async` will yield to the executor from time to time, Unlike `Context::run`\n            // or `Script::evaluate` which block the current thread until the execution finishes.\n            println!(\"Evaluating script...\");\n            script.evaluate_async(context).await.unwrap();\n\n            // Run the jobs asynchronously, which avoids blocking the main thread.\n            println!(\"Running jobs...\");\n            queue.run_jobs_async(&RefCell::new(context)).await\n        };\n\n        future::zip(counter, engine).await.1?;\n\n        println!(\"Total elapsed time: {:?}\\n\", now.elapsed());\n\n        Ok(())\n    }))\n}\n"
  },
  {
    "path": "examples/src/bin/symbol_visitor.rs",
    "content": "// This example demonstrates how to use a visitor to perform simple operations over the Javascript\n// AST, namely: finding all the `Sym`s present in a script. See commuter_visitor.rs for an example\n// which mutates the AST.\n\nuse boa_ast::visitor::Visitor;\nuse boa_engine::{Context, Source};\nuse boa_interner::Sym;\nuse boa_parser::Parser;\nuse core::ops::ControlFlow;\nuse std::{collections::HashSet, convert::Infallible, path::Path};\n\n#[derive(Debug, Clone, Default)]\nstruct SymbolVisitor {\n    observed: HashSet<Sym>,\n}\n\nimpl<'ast> Visitor<'ast> for SymbolVisitor {\n    type BreakTy = Infallible;\n\n    fn visit_sym(&mut self, node: &'ast Sym) -> ControlFlow<Self::BreakTy> {\n        self.observed.insert(*node);\n        ControlFlow::Continue(())\n    }\n}\n\nfn main() {\n    let mut parser = Parser::new(Source::from_filepath(Path::new(\"./scripts/calc.js\")).unwrap());\n    let mut ctx = Context::default();\n\n    let scope = ctx.realm().scope().clone();\n    let script = parser.parse_script(&scope, ctx.interner_mut()).unwrap();\n\n    let mut visitor = SymbolVisitor::default();\n\n    assert!(matches!(\n        visitor.visit_statement_list(script.statements()),\n        ControlFlow::Continue(_)\n    ));\n\n    println!(\n        \"Observed {} unique strings/symbols:\",\n        visitor.observed.len()\n    );\n    for sym in visitor.observed {\n        println!(\"  - {}\", ctx.interner().resolve(sym).unwrap());\n    }\n}\n"
  },
  {
    "path": "examples/src/bin/synthetic.rs",
    "content": "// This example implements a synthetic Rust module that is exposed to JS code.\n// This mirrors the `modules.rs` example but uses synthetic modules instead.\n\nuse std::path::PathBuf;\nuse std::rc::Rc;\nuse std::{error::Error, path::Path};\n\nuse boa_engine::builtins::promise::PromiseState;\nuse boa_engine::module::{SimpleModuleLoader, SyntheticModuleInitializer};\nuse boa_engine::object::FunctionObjectBuilder;\nuse boa_engine::{\n    Context, JsArgs, JsError, JsNativeError, JsValue, Module, NativeFunction, Source, js_string,\n};\n\nfn main() -> Result<(), Box<dyn Error>> {\n    // A simple module that we want to compile from Rust code.\n    const MODULE_SRC: &str = r#\"\n        import { pyth } from \"./trig.mjs\";\n        import * as ops from \"./operations.mjs\";\n\n        export let result = pyth(3, 4);\n        export function mix(a, b) {\n            return ops.sum(ops.mult(a, ops.sub(b, a)), 10);\n        }\n    \"#;\n\n    // This can be overridden with any custom implementation of `ModuleLoader`.\n    let loader = Rc::new(SimpleModuleLoader::new(\"./scripts/modules\")?);\n\n    // Just need to cast to a `ModuleLoader` before passing it to the builder.\n    let context = &mut Context::builder().module_loader(loader.clone()).build()?;\n\n    // Now, create the synthetic module and insert it into the loader.\n    let operations = create_operations_module(context);\n    loader.insert(\n        PathBuf::from(\"./scripts/modules\")\n            .canonicalize()?\n            .join(\"operations.mjs\"),\n        operations,\n    );\n\n    let source = Source::from_reader(MODULE_SRC.as_bytes(), Some(Path::new(\"./main.mjs\")));\n\n    // Can also pass a `Some(realm)` if you need to execute the module in another realm.\n    let module = Module::parse(source, None, context)?;\n\n    // Don't forget to insert the parsed module into the loader itself, since the root module\n    // is not automatically inserted by the `ModuleLoader::load_imported_module` impl.\n    //\n    // Simulate as if the \"fake\" module is located in the modules root, just to ensure that\n    // the loader won't double load in case someone tries to import \"./main.mjs\".\n    loader.insert(\n        Path::new(\"./scripts/modules\")\n            .canonicalize()?\n            .join(\"main.mjs\"),\n        module.clone(),\n    );\n\n    // This uses the utility function to load, link and evaluate a module without having to deal\n    // with callbacks. For an example demonstrating the whole lifecycle of a module, see\n    // `modules.rs`\n    let promise_result = module.load_link_evaluate(context);\n\n    // Very important to push forward the job queue after queueing promises.\n    context.run_jobs()?;\n\n    // Checking if the final promise didn't return an error.\n    match promise_result.state() {\n        PromiseState::Pending => return Err(\"module didn't execute!\".into()),\n        PromiseState::Fulfilled(v) => {\n            assert_eq!(v, JsValue::undefined());\n        }\n        PromiseState::Rejected(err) => {\n            return Err(JsError::from_opaque(err).try_native(context)?.into());\n        }\n    }\n\n    // We can access the full namespace of the module with all its exports.\n    let namespace = module.namespace(context);\n    let result = namespace.get(js_string!(\"result\"), context)?;\n\n    println!(\"result = {}\", result.display());\n\n    assert_eq!(\n        namespace.get(js_string!(\"result\"), context)?,\n        JsValue::from(5)\n    );\n\n    let mix = namespace\n        .get(js_string!(\"mix\"), context)?\n        .as_callable()\n        .ok_or_else(|| JsNativeError::typ().with_message(\"mix export wasn't a function!\"))?;\n    let result = mix.call(&JsValue::undefined(), &[5.into(), 10.into()], context)?;\n\n    println!(\"mix(5, 10) = {}\", result.display());\n\n    assert_eq!(result, 35.into());\n\n    Ok(())\n}\n\n// Creates the synthetic equivalent to the `./modules/operations.mjs` file.\nfn create_operations_module(context: &mut Context) -> Module {\n    // We first create the function objects that will be exported by the module. More\n    // on that below.\n    let sum = FunctionObjectBuilder::new(\n        context.realm(),\n        NativeFunction::from_fn_ptr(|_, args, ctx| {\n            args.get_or_undefined(0).add(args.get_or_undefined(1), ctx)\n        }),\n    )\n    .length(2)\n    .name(\"sum\")\n    .build();\n    let sub = FunctionObjectBuilder::new(\n        context.realm(),\n        NativeFunction::from_fn_ptr(|_, args, ctx| {\n            args.get_or_undefined(0).sub(args.get_or_undefined(1), ctx)\n        }),\n    )\n    .length(2)\n    .name(\"sub\")\n    .build();\n    let mult = FunctionObjectBuilder::new(\n        context.realm(),\n        NativeFunction::from_fn_ptr(|_, args, ctx| {\n            args.get_or_undefined(0).mul(args.get_or_undefined(1), ctx)\n        }),\n    )\n    .length(2)\n    .name(\"mult\")\n    .build();\n    let div = FunctionObjectBuilder::new(\n        context.realm(),\n        NativeFunction::from_fn_ptr(|_, args, ctx| {\n            args.get_or_undefined(0).div(args.get_or_undefined(1), ctx)\n        }),\n    )\n    .length(2)\n    .name(\"div\")\n    .build();\n    let sqrt = FunctionObjectBuilder::new(\n        context.realm(),\n        NativeFunction::from_fn_ptr(|_, args, ctx| {\n            let a = args.get_or_undefined(0).to_number(ctx)?;\n            Ok(JsValue::from(a.sqrt()))\n        }),\n    )\n    .length(1)\n    .name(\"sqrt\")\n    .build();\n\n    Module::synthetic(\n        // Make sure to list all exports beforehand.\n        &[\n            js_string!(\"sum\"),\n            js_string!(\"sub\"),\n            js_string!(\"mult\"),\n            js_string!(\"div\"),\n            js_string!(\"sqrt\"),\n        ],\n        // The initializer is evaluated every time a module imports this synthetic module,\n        // so we avoid creating duplicate objects by capturing and cloning them instead.\n        SyntheticModuleInitializer::from_copy_closure_with_captures(\n            |module, fns, _| {\n                println!(\"Running initializer!\");\n                module.set_export(&js_string!(\"sum\"), fns.0.clone().into())?;\n                module.set_export(&js_string!(\"sub\"), fns.1.clone().into())?;\n                module.set_export(&js_string!(\"mult\"), fns.2.clone().into())?;\n                module.set_export(&js_string!(\"div\"), fns.3.clone().into())?;\n                module.set_export(&js_string!(\"sqrt\"), fns.4.clone().into())?;\n                Ok(())\n            },\n            (sum, sub, mult, div, sqrt),\n        ),\n        None,\n        None,\n        context,\n    )\n}\n"
  },
  {
    "path": "examples/src/bin/tokio_event_loop.rs",
    "content": "use boa_engine::context::time::JsInstant;\nuse boa_engine::job::{GenericJob, TimeoutJob};\nuse boa_engine::{\n    Context, JsArgs, JsNativeError, JsResult, JsValue, Script, Source,\n    context::ContextBuilder,\n    job::{Job, JobExecutor, NativeAsyncJob, PromiseJob},\n    js_string,\n    native_function::NativeFunction,\n    property::Attribute,\n};\nuse boa_runtime::Console;\nuse futures_concurrency::future::FutureGroup;\nuse futures_lite::{StreamExt, future};\nuse std::collections::BTreeMap;\nuse std::ops::DerefMut;\nuse std::{\n    cell::RefCell,\n    collections::VecDeque,\n    future::Future,\n    rc::Rc,\n    time::{Duration, Instant},\n};\nuse tokio::{task, time};\n\n// This example shows how to create an event loop using the tokio runtime.\n// The example contains two \"flavors\" of event loops:\nfn main() -> JsResult<()> {\n    // An internally async event loop. This event loop blocks the execution of the thread\n    // while executing tasks, but internally uses async to run its tasks.\n    internally_async_event_loop()?;\n\n    // An externally async event loop. This event loop can yield to the runtime to concurrently\n    // run tasks with it.\n    externally_async_event_loop()\n}\n\n/// An event queue using tokio to drive futures to completion.\nstruct Queue {\n    async_jobs: RefCell<VecDeque<NativeAsyncJob>>,\n    promise_jobs: RefCell<VecDeque<PromiseJob>>,\n    timeout_jobs: RefCell<BTreeMap<JsInstant, TimeoutJob>>,\n    generic_jobs: RefCell<VecDeque<GenericJob>>,\n}\n\nimpl Queue {\n    fn new() -> Self {\n        Self {\n            async_jobs: RefCell::default(),\n            promise_jobs: RefCell::default(),\n            timeout_jobs: RefCell::default(),\n            generic_jobs: RefCell::default(),\n        }\n    }\n\n    fn drain_timeout_jobs(&self, context: &mut Context) {\n        let now = context.clock().now();\n\n        let mut timeouts_borrow = self.timeout_jobs.borrow_mut();\n        let mut jobs_to_keep = timeouts_borrow.split_off(&now);\n        jobs_to_keep.retain(|_, job| !job.is_cancelled());\n        let jobs_to_run = std::mem::replace(timeouts_borrow.deref_mut(), jobs_to_keep);\n        drop(timeouts_borrow);\n\n        for job in jobs_to_run.into_values() {\n            if let Err(e) = job.call(context) {\n                eprintln!(\"Uncaught {e}\");\n            }\n        }\n    }\n\n    fn drain_jobs(&self, context: &mut Context) {\n        // Run the timeout jobs first.\n        self.drain_timeout_jobs(context);\n\n        let job = self.generic_jobs.borrow_mut().pop_front();\n        if let Some(generic) = job\n            && let Err(err) = generic.call(context)\n        {\n            eprintln!(\"Uncaught {err}\");\n        }\n\n        let jobs = std::mem::take(&mut *self.promise_jobs.borrow_mut());\n        for job in jobs {\n            if let Err(e) = job.call(context) {\n                eprintln!(\"Uncaught {e}\");\n            }\n        }\n        context.clear_kept_objects();\n    }\n}\n\nimpl JobExecutor for Queue {\n    fn enqueue_job(self: Rc<Self>, job: Job, context: &mut Context) {\n        match job {\n            Job::PromiseJob(job) => self.promise_jobs.borrow_mut().push_back(job),\n            Job::AsyncJob(job) => self.async_jobs.borrow_mut().push_back(job),\n            Job::TimeoutJob(t) => {\n                let now = context.clock().now();\n                self.timeout_jobs.borrow_mut().insert(now + t.timeout(), t);\n            }\n            Job::GenericJob(g) => self.generic_jobs.borrow_mut().push_back(g),\n            _ => panic!(\"unsupported job type\"),\n        }\n    }\n\n    // While the sync flavor of `run_jobs` will block the current thread until all the jobs have finished...\n    fn run_jobs(self: Rc<Self>, context: &mut Context) -> JsResult<()> {\n        let runtime = tokio::runtime::Builder::new_current_thread()\n            .enable_time()\n            .build()\n            .unwrap();\n\n        task::LocalSet::default().block_on(&runtime, self.run_jobs_async(&RefCell::new(context)))\n    }\n\n    // ...the async flavor won't, which allows concurrent execution with external async tasks.\n    async fn run_jobs_async(self: Rc<Self>, context: &RefCell<&mut Context>) -> JsResult<()> {\n        let mut group = FutureGroup::new();\n        loop {\n            for job in std::mem::take(&mut *self.async_jobs.borrow_mut()) {\n                group.insert(job.call(context));\n            }\n\n            if group.is_empty()\n                && self.promise_jobs.borrow().is_empty()\n                && self.timeout_jobs.borrow().is_empty()\n                && self.generic_jobs.borrow().is_empty()\n            {\n                // All queues are empty. We can exit.\n                return Ok(());\n            }\n\n            // We have some jobs pending on the microtask queue. Try to poll the pending\n            // tasks once to see if any of them finished, and run the pending microtasks\n            // otherwise.\n            if let Some(Err(err)) = future::poll_once(group.next()).await.flatten() {\n                eprintln!(\"Uncaught {err}\");\n            };\n\n            // Only one macrotask can be executed before the next drain of the microtask queue.\n            self.drain_jobs(&mut context.borrow_mut());\n            task::yield_now().await\n        }\n    }\n}\n\n// Example async function. Note that the returned future must be 'static.\nfn delay(\n    _this: &JsValue,\n    args: &[JsValue],\n    context: &RefCell<&mut Context>,\n) -> impl Future<Output = JsResult<JsValue>> {\n    let millis = args.get_or_undefined(0).to_u32(&mut context.borrow_mut());\n\n    async move {\n        let millis = millis?;\n        println!(\"Delaying for {millis} milliseconds ...\");\n        let now = Instant::now();\n        time::sleep(Duration::from_millis(u64::from(millis))).await;\n        let elapsed = now.elapsed().as_secs_f64();\n        Ok(elapsed.into())\n    }\n}\n\n// Example interval function, but using a `NativeAsyncJob` instead of an async\n// function to schedule the async job.\nfn interval(this: &JsValue, args: &[JsValue], context: &mut Context) -> JsResult<JsValue> {\n    let Some(function) = args.get_or_undefined(0).as_callable() else {\n        return Err(JsNativeError::typ()\n            .with_message(\"arg must be a callable\")\n            .into());\n    };\n\n    let this = this.clone();\n    let delay = args.get_or_undefined(1).to_u32(context)?;\n    let args = args.get(2..).unwrap_or_default().to_vec();\n\n    context.enqueue_job(\n        NativeAsyncJob::with_realm(\n            async move |context: &RefCell<&mut Context>| {\n                let mut timer = time::interval(Duration::from_millis(u64::from(delay)));\n                for _ in 0..10 {\n                    timer.tick().await;\n                    if let Err(err) = function.call(&this, &args, &mut context.borrow_mut()) {\n                        eprintln!(\"Uncaught {err}\");\n                    }\n                }\n                Ok(JsValue::undefined())\n            },\n            context.realm().clone(),\n        )\n        .into(),\n    );\n\n    Ok(JsValue::undefined())\n}\n\n/// Adds the custom runtime to the context.\nfn add_runtime(context: &mut Context) {\n    // First add the `console` object, to be able to call `console.log()`.\n    let console = Console::init(context);\n    context\n        .register_global_property(Console::NAME, console, Attribute::all())\n        .expect(\"the console builtin shouldn't exist\");\n\n    // Then, bind the defined async function to the ECMAScript function \"delay\".\n    context\n        .register_global_builtin_callable(\n            js_string!(\"delay\"),\n            1,\n            NativeFunction::from_async_fn(delay),\n        )\n        .expect(\"the delay builtin shouldn't exist\");\n\n    // Finally, bind the defined async job to the ECMAScript function \"interval\".\n    context\n        .register_global_builtin_callable(\n            js_string!(\"interval\"),\n            1,\n            NativeFunction::from_fn_ptr(interval),\n        )\n        .expect(\"the delay builtin shouldn't exist\");\n}\n\n// Script that does multiple calls to multiple async timers.\nconst SCRIPT: &str = r\"\n    function print(elapsed) {\n        console.log(`Finished delay. Elapsed time: ${elapsed * 1000} ms`);\n    }\n\n    delay(1000).then(print);\n    delay(500).then(print);\n    delay(200).then(print);\n    delay(600).then(print);\n    delay(30).then(print);\n\n    let i = 0;\n    function counter() {\n        console.log(`Iteration number ${i} for JS interval`);\n        i += 1;\n    }\n\n    interval(counter, 100);\n\n    for(let i = 0; i <= 100000; i++) {\n        // Emulate a long-running evaluation of a script.\n    }\n\";\n\n// This flavor is most recommended when you have an application that:\n//  - Needs to wait until the engine finishes executing; depends on the execution result to continue.\n//  - Delegates the execution of the application to the engine's event loop.\nfn internally_async_event_loop() -> JsResult<()> {\n    println!(\"====== Internally async event loop. ======\");\n\n    // Initialize the queue and the context\n    let queue = Queue::new();\n    let context = &mut ContextBuilder::new()\n        .job_executor(Rc::new(queue))\n        .build()\n        .unwrap();\n\n    // Then, add the custom runtime.\n    add_runtime(context);\n\n    let now = Instant::now();\n    println!(\"Evaluating script...\");\n    context.eval(Source::from_bytes(SCRIPT)).unwrap();\n\n    // Important to run this after evaluating, since this is what triggers to run the enqueued jobs.\n    println!(\"Running jobs...\");\n    context.run_jobs()?;\n\n    println!(\"Total elapsed time: {:?}\\n\", now.elapsed());\n\n    Ok(())\n}\n\n// This flavor is most recommended when you have an application that:\n//  - Cannot afford to block until the engine finishes executing.\n//  - Needs to process IO requests between executions that will be consumed by the engine.\n#[tokio::main]\nasync fn externally_async_event_loop() -> JsResult<()> {\n    println!(\"====== Externally async event loop. ======\");\n    // Initialize the queue and the context\n    let queue = Rc::new(Queue::new());\n    let context = &mut ContextBuilder::new()\n        .job_executor(queue.clone())\n        .build()\n        .unwrap();\n\n    // Then, add the custom runtime.\n    add_runtime(context);\n\n    let now = Instant::now();\n\n    // Example of an asynchronous workload that must be run alongside the engine.\n    let counter = async {\n        tokio::spawn(async {\n            let mut interval = time::interval(Duration::from_millis(100));\n            println!(\"Starting tokio interval job...\");\n            for i in 0..10 {\n                interval.tick().await;\n                println!(\"Executed interval tick {i}\");\n            }\n            println!(\"Finished tokio interval job...\")\n        })\n        .await\n        .map_err(|err| JsNativeError::typ().with_message(err.to_string()).into())\n    };\n\n    let local_set = &mut task::LocalSet::default();\n    let engine = local_set.run_until(async {\n        let script = Script::parse(Source::from_bytes(SCRIPT), None, context).unwrap();\n\n        // `Script::evaluate_async` will yield to the executor from time to time, Unlike `Context::run`\n        // or `Script::evaluate` which block the current thread until the execution finishes.\n        println!(\"Evaluating script...\");\n        script.evaluate_async(context).await.unwrap();\n\n        // Run the jobs asynchronously, which avoids blocking the main thread.\n        println!(\"Running jobs...\");\n        queue.run_jobs_async(&RefCell::new(context)).await\n    });\n\n    tokio::try_join!(counter, engine)?;\n\n    println!(\"Total elapsed time: {:?}\\n\", now.elapsed());\n\n    Ok(())\n}\n"
  },
  {
    "path": "examples/src/bin/try_into_js_derive.rs",
    "content": "use boa_engine::{\n    Context, JsResult, JsValue, Source, js_string,\n    value::{TryFromJs, TryIntoJs},\n};\n\n#[derive(TryFromJs, TryIntoJs, Debug, PartialEq, Eq)]\n#[boa(rename_all = \"camelCase\")]\nstruct InnerTest {\n    hello_world_how_are_you: u8,\n}\n\n#[derive(TryIntoJs)]\nstruct Test {\n    x: i32,\n    #[boa(rename = \"y\")]\n    y_point: i32,\n    #[allow(unused)]\n    #[boa(skip)]\n    tuple: (i32, u8, String),\n    #[boa(rename = \"isReadable\")]\n    #[boa(into_js_with = \"readable_into_js\")]\n    is_readable: i8,\n\n    inner: InnerTest,\n}\n\n#[derive(TryFromJs, Debug, PartialEq, Eq)]\nstruct ResultVerifier {\n    x: i32,\n    y: i32,\n    #[boa(rename = \"isReadable\")]\n    is_readable: bool,\n\n    inner: InnerTest,\n}\n\nfn main() -> JsResult<()> {\n    let js_code = r#\"\n    function pointShift(pointA, pointB) {\n        if (pointA.isReadable === true\n            && pointB.isReadable === true\n            && pointA.inner.helloWorldHowAreYou !== undefined)\n        {\n            return {\n                x: pointA.x + pointB.x,\n                y: pointA.y + pointB.y,\n                isReadable: true,\n                inner: {\n                    helloWorldHowAreYou: pointA.inner.helloWorldHowAreYou + pointB.inner.helloWorldHowAreYou\n                }\n            }\n        }\n        return undefined\n    }\n    \"#;\n\n    let mut context = Context::default();\n    let context = &mut context;\n\n    context.eval(Source::from_bytes(js_code))?;\n\n    let point_shift = context\n        .global_object()\n        .get(js_string!(\"pointShift\"), context)?;\n    let point_shift = point_shift.as_callable().unwrap();\n\n    let a = Test {\n        x: 10,\n        y_point: 20,\n        tuple: (30, 40, \"no matter\".into()),\n        is_readable: 1,\n        inner: InnerTest {\n            hello_world_how_are_you: 1,\n        },\n    };\n    let b = Test {\n        x: 2,\n        y_point: 1,\n        tuple: (30, 40, \"no matter\".into()),\n        is_readable: 2,\n        inner: InnerTest {\n            hello_world_how_are_you: 2,\n        },\n    };\n    let c = Test {\n        x: 2,\n        y_point: 1,\n        tuple: (30, 40, \"no matter\".into()),\n        is_readable: 0,\n        inner: InnerTest {\n            hello_world_how_are_you: 3,\n        },\n    };\n\n    let result = point_shift.call(\n        &JsValue::undefined(),\n        &[a.try_into_js(context)?, b.try_into_js(context)?],\n        context,\n    )?;\n    let verifier = ResultVerifier::try_from_js(&result, context)?;\n    let expect = ResultVerifier {\n        x: 10 + 2,\n        y: 20 + 1,\n        is_readable: true,\n        inner: InnerTest {\n            hello_world_how_are_you: 3,\n        },\n    };\n    assert_eq!(verifier, expect);\n\n    let result = point_shift.call(\n        &JsValue::undefined(),\n        &[a.try_into_js(context)?, c.try_into_js(context)?],\n        context,\n    )?;\n    assert!(result.is_undefined());\n\n    Ok(())\n}\n\nfn readable_into_js(value: &i8, _context: &mut Context) -> JsResult<JsValue> {\n    Ok(JsValue::new(*value != 0))\n}\n"
  },
  {
    "path": "ffi/wasm/.gitignore",
    "content": "pkg"
  },
  {
    "path": "ffi/wasm/Cargo.toml",
    "content": "[package]\nname = \"boa_wasm\"\ndescription = \"Wasm compatibility layer for the Boa JavaScript engine.\"\nkeywords = [\"javascript\", \"compiler\", \"lexer\", \"parser\", \"js\"]\ncategories = [\"parser-implementations\", \"wasm\", \"compilers\"]\npublish = false\nversion.workspace = true\nedition.workspace = true\nauthors.workspace = true\nlicense.workspace = true\nrepository.workspace = true\nrust-version.workspace = true\n\n[dependencies]\nboa_engine = { workspace = true, features = [\"js\"] }\nwasm-bindgen = { workspace = true, default-features = false }\nconsole_error_panic_hook.workspace = true\n\n[target.'cfg(all(any(target_arch = \"wasm32\", target_arch = \"wasm64\"), target_os = \"unknown\"))'.dev-dependencies]\nwasm-bindgen-test.workspace = true\n\n[features]\ndefault = [\n    \"boa_engine/annex-b\",\n    \"boa_engine/experimental\",\n    \"boa_engine/float16\",\n    \"boa_engine/intl_bundled\",\n    \"boa_engine/temporal\",\n    \"boa_engine/xsum\",\n]\n\n[lib]\ncrate-type = [\"cdylib\", \"lib\"]\nname = \"boa_wasm\"\nbench = false\n\n[lints]\nworkspace = true\n\n[package.metadata.docs.rs]\nall-features = true\n\n[package.metadata.wasm-pack.profile.release]\nwasm-opt = [\"--enable-bulk-memory\", \"--enable-nontrapping-float-to-int\"]\n"
  },
  {
    "path": "ffi/wasm/LICENSE-MIT",
    "content": "MIT License\n\nCopyright (c) 2019 Jason Williams\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n"
  },
  {
    "path": "ffi/wasm/LICENSE-UNLICENSE",
    "content": "This is free and unencumbered software released into the public domain.\n\nAnyone is free to copy, modify, publish, use, compile, sell, or\ndistribute this software, either in source code form or as a compiled\nbinary, for any purpose, commercial or non-commercial, and by any\nmeans.\n\nIn jurisdictions that recognize copyright laws, the author or authors\nof this software dedicate any and all copyright interest in the\nsoftware to the public domain. We make this dedication for the benefit\nof the public at large and to the detriment of our heirs and\nsuccessors. We intend this dedication to be an overt act of\nrelinquishment in perpetuity of all present and future rights to this\nsoftware under copyright law.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\nEXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\nMERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.\nIN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR\nOTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,\nARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR\nOTHER DEALINGS IN THE SOFTWARE.\n\nFor more information, please refer to <http://unlicense.org>\n"
  },
  {
    "path": "ffi/wasm/README.md",
    "content": "# @boa-dev/boa_wasm\n\nWebAssembly bindings for [Boa](https://github.com/boa-dev/boa), a Javascript engine written in Rust.\n\n[![npm version](https://img.shields.io/npm/v/@boa-dev/boa_wasm)](https://www.npmjs.com/package/@boa-dev/boa_wasm)\n[![license](https://img.shields.io/npm/l/@boa-dev/boa_wasm)](https://github.com/boa-dev/boa)\n\n## Overview\n\nThis package provides WebAssembly bindings to run JavaScript code using the Boa engine directly in the browser or Node.js environments. Boa supports **more than 90%** of the latest ECMAScript specification.\n\n## Installation\n\n```bash\nnpm install @boa-dev/boa_wasm\n```\n\nOr with yarn:\n\n```bash\nyarn add @boa-dev/boa_wasm\n```\n\n## Usage\n\n### Browser\n\n```javascript\nimport init, { evaluate } from \"@boa-dev/boa_wasm\";\n\n// Initialize the Wasm module\nawait init();\n\n// Evaluate JavaScript code\ntry {\n  const result = evaluate(\"1 + 1\");\n  console.log(result); // \"2\"\n} catch (error) {\n  console.error(\"Evaluation error:\", error);\n}\n```\n\n### Node.js\n\n```javascript\nconst { evaluate } = require(\"@boa-dev/boa_wasm\");\n\ntry {\n  const result = evaluate(\"1 + 1\");\n  console.log(result); // \"2\"\n} catch (error) {\n  console.error(\"Evaluation error:\", error);\n}\n```\n\n### Advanced Example\n\n```javascript\nimport init, { evaluate } from \"@boa-dev/boa_wasm\";\n\nawait init();\n\nconst code = `\n  function fibonacci(n) {\n    if (n <= 1) return n;\n    return fibonacci(n - 1) + fibonacci(n - 2);\n  }\n  \n  fibonacci(10)\n`;\n\nconst result = evaluate(code);\nconsole.log(result); // \"55\"\n```\n\n## API\n\n### `evaluate(src: string): string`\n\nEvaluates the given ECMAScript code and returns the result as a string.\n\n**Parameters:**\n\n- `src` - A string containing the JavaScript code to evaluate\n\n**Returns:**\n\n- A string representation of the evaluation result\n\n**Throws:**\n\n- A `JsValue` error if the execution throws an exception\n\n## Features\n\nBoa's Wasm build includes:\n\n- **Annex B**: Legacy ECMAScript features\n- **Internationalization**: Full Intl API support\n- **Experimental features**: Latest ECMAScript proposals\n\n## Live Demo\n\nTry Boa in your browser at our [live playground](https://boajs.dev/playground)!\n\n## Conformance\n\nCheck out Boa's [ECMAScript Test262 conformance results](https://boajs.dev/conformance) to see our progress on implementing the ECMAScript specification.\n\n## Documentation\n\n- [Boa Repository](https://github.com/boa-dev/boa)\n- [Boa Engine API Documentation](https://docs.rs/boa_engine/latest/boa_engine/)\n- [Boa Website](https://boajs.dev/)\n\n## Contributing\n\nContributions are welcome! Please check out the [contributing guide](https://github.com/boa-dev/boa/blob/main/CONTRIBUTING.md) to get started.\n\n## Communication\n\n- [Matrix](https://matrix.to/#/#boa:matrix.org)\n- [Discord](https://discord.gg/tUFFk9Y)\n\n## License\n\nThis project is licensed under the [Unlicense](https://github.com/boa-dev/boa/blob/main/LICENSE-UNLICENSE) or [MIT](https://github.com/boa-dev/boa/blob/main/LICENSE-MIT) licenses, at your option.\n"
  },
  {
    "path": "ffi/wasm/src/lib.rs",
    "content": "//! An ECMAScript Wasm implementation based on `boa_engine`.\n#![cfg_attr(not(test), forbid(clippy::unwrap_used))]\n#![allow(unused_crate_dependencies)]\n\nuse boa_engine::{Context, Source};\nuse wasm_bindgen::prelude::*;\n\n#[wasm_bindgen(start)]\nfn main_js() {\n    console_error_panic_hook::set_once();\n}\n\n/// Evaluate the given ECMAScript code.\n///\n/// # Errors\n///\n/// If the execution of the script throws, returns a `JsValue` with the error string.\n#[wasm_bindgen]\npub fn evaluate(src: &str) -> Result<String, JsValue> {\n    // Setup the executor\n    Context::default()\n        .eval(Source::from_bytes(src))\n        .map_err(|e| JsValue::from(format!(\"Uncaught {e}\")))\n        .map(|v| v.display().to_string())\n}\n"
  },
  {
    "path": "ffi/wasm/tests/web.rs",
    "content": "//! Tests for the wasm module.\n\n#![expect(\n    unused_crate_dependencies,\n    reason = \"https://github.com/rust-lang/rust/issues/95513\"\n)]\n#![cfg(all(\n    any(target_arch = \"wasm32\", target_arch = \"wasm64\"),\n    target_os = \"unknown\"\n))]\n#![allow(missing_docs)]\n\nuse wasm_bindgen_test::*;\n\nwasm_bindgen_test_configure!(run_in_browser);\n\n#[wasm_bindgen_test]\nfn simple() {\n    const CODE: &str = r\"\n    function greet(targetName) {\n      return 'Hello, ' + targetName + '!';\n    }\n\n    greet('World')\n    \";\n\n    let result = boa_wasm::evaluate(CODE).unwrap();\n\n    assert_eq!(result, \"\\\"Hello, World!\\\"\");\n}\n"
  },
  {
    "path": "flake.nix",
    "content": "{\n  description = \"Boa engine dev shell\";\n\n  inputs = {\n    nixpkgs.url = \"github:nixos/nixpkgs/nixos-unstable\";\n    rust-overlay.url = \"github:oxalica/rust-overlay\";\n    flake-utils.url = \"github:numtide/flake-utils\";\n  };\n\n  outputs =\n    {\n      self,\n      nixpkgs,\n      rust-overlay,\n      flake-utils,\n    }:\n    flake-utils.lib.eachDefaultSystem (\n      system:\n      let\n        pkgs = import nixpkgs {\n          inherit system;\n          overlays = [ rust-overlay.overlays.default ];\n        };\n        rust-toolchain = pkgs.rust-bin.stable.latest.default.override {\n          extensions = [\n            \"rust-src\"\n            \"rust-analyzer\"\n          ];\n        };\n      in\n      {\n        devShells.default =\n          with pkgs;\n          mkShell rec {\n            packages = [\n            ];\n            buildInputs = [\n              openssl\n            ];\n            nativeBuildInputs = [\n              rust-toolchain\n              pkg-config\n            ];\n            # Required for jemalloc, see https://github.com/NixOS/nixpkgs/issues/370494 .\n            CFLAGS = \"-DJEMALLOC_STRERROR_R_RETURNS_CHAR_WITH_GNU_SOURCE\";\n            LD_LIBRARY_PATH = \"${lib.makeLibraryPath buildInputs}\";\n\n            shellHook = ''\n              export SHELL=${pkgs.bashInteractive}/bin/bash\n            '';\n          };\n      }\n    );\n}\n"
  },
  {
    "path": "make/ci.toml",
    "content": "[tasks.ci-lint-all-features]\ncommand = \"cargo\"\nargs = [\"clippy\", \"--all-features\", \"--all-targets\"]\n\n[tasks.ci-lint-no-features]\ncommand = \"cargo\"\nargs = [\"clippy\", \"--no-default-features\"]\n\n[tasks.ci-fmt-check]\ncommand = \"cargo\"\nargs = [\"fmt\", \"--\", \"--check\"]\n\n"
  },
  {
    "path": "package.json",
    "content": "{\n  \"private\": true,\n  \"scripts\": {\n    \"prettier\": \"npx prettier -w .\"\n  }\n}\n"
  },
  {
    "path": "test262_config.toml",
    "content": "commit = \"72ec758c9b6351b5baa299e0a162c2b7fe497cc6\"\n\n[ignored]\n# Not implemented yet:\nflags = []\n\nfeatures = [\n    ### Unimplemented features:\n\n    \"symbols-as-weakmap-keys\",\n    \"Intl.DisplayNames\",\n    \"Intl.RelativeTimeFormat\",\n    \"Intl-enumeration\",\n    \"Intl.DurationFormat\",\n    \"regexp-duplicate-named-groups\",\n    \"explicit-resource-management\",\n    \"joint-iteration\",\n\n    ### Pending proposals\n\n    # https://github.com/tc39/proposal-import-bytes\n    \"import-bytes\",\n\n\n    # https://github.com/tc39/proposal-intl-locale-info\n    \"Intl.Locale-info\",\n\n    # https://github.com/tc39/proposal-regexp-legacy-features\n    \"legacy-regexp\",\n\n    # https://github.com/tc39/proposal-defer-import-eval\n    \"import-defer\",\n\n    # https://github.com/tc39/proposal-realms\n    \"ShadowRealm\",\n\n    # https://github.com/tc39/proposal-decorators\n    \"decorators\",\n\n    # Uint8Array Base64\n    # https://github.com/tc39/proposal-arraybuffer-base64\n    \"uint8array-base64\",\n\n    # Source Phase Imports\n    # test262 special specifier\n    \"source-phase-imports-module-source\",\n\n    # Immutable Array Buffer\n    # https://github.com/tc39/proposal-immutable-arraybuffer\n    \"immutable-arraybuffer\",\n\n    ### Non-standard\n    \"caller\",\n]\n\ntests = [\n    # Should throw an OOM error instead of filling the whole RAM.\n    \"test/staging/sm/String/replace-math.js\",\n    # Panics\n    \"test/staging/sm/regress/regress-554955-2.js\",\n    \"test/staging/sm/class/fields-static-class-name-binding-eval.js\",\n    \"test/staging/sm/regress/regress-554955-1.js\",\n    \"test/staging/sm/regress/regress-554955-3.js\",\n    # Extension canonicalization not yet supported by ICU4X.\n    # TODO: Remove when https://github.com/unicode-org/icu4x/issues/3483 is fixed.\n    \"test/intl402/Intl/getCanonicalLocales/non-iana-canon.js\",\n    \"test/intl402/Intl/getCanonicalLocales/unicode-ext-canonicalize-measurement-system.js\",\n    \"test/intl402/Intl/getCanonicalLocales/unicode-ext-canonicalize-col-strength.js\",\n    \"test/intl402/Intl/getCanonicalLocales/unicode-ext-canonicalize-calendar.js\",\n    \"test/intl402/Intl/getCanonicalLocales/unicode-ext-canonicalize-timezone.js\",\n    \"test/intl402/Intl/getCanonicalLocales/transformed-ext-canonical.js\",\n    # h24 hour cycle not supported by ICU4X.\n    # TODO: Remove when https://github.com/unicode-org/icu4x/issues/6597 is fixed.\n    \"test/intl402/Locale/getters.js\",\n    \"test/intl402/Locale/constructor-getter-order.js\",\n    \"test/intl402/Locale/constructor-options-hourcycle-valid.js\",\n    # Extension canonicalization not yet supported by ICU4X.\n    # TODO: Remove when https://github.com/unicode-org/icu4x/issues/3483 is fixed.\n    \"test/intl402/Locale/prototype/calendar/canonicalize.js\",\n    \"test/intl402/Locale/constructor-non-iana-canon.js\",\n    \"test/intl402/Locale/constructor-options-canonicalized.js\",\n    # TODO: Remove this once regress fixes the named groups parsing issue.\n    \"test/built-ins/RegExp/named-groups/non-unicode-property-names-valid.js\",\n    # Source Phase Imports: AbstractModuleSource built-in and static `import source` not yet implemented.\n    \"test/built-ins/AbstractModuleSource/proto.js\",\n    \"test/built-ins/AbstractModuleSource/name.js\",\n    \"test/built-ins/AbstractModuleSource/length.js\",\n    \"test/built-ins/AbstractModuleSource/prototype/proto.js\",\n    \"test/built-ins/AbstractModuleSource/prototype/Symbol.toStringTag.js\",\n    \"test/built-ins/AbstractModuleSource/prototype/constructor.js\",\n    \"test/built-ins/AbstractModuleSource/prototype.js\",\n    \"test/built-ins/AbstractModuleSource/throw-from-constructor.js\",\n    \"test/language/module-code/source-phase-import/import-source.js\",\n]\n"
  },
  {
    "path": "test_wpt_config.toml",
    "content": "rev = \"82a84e1842d583f1f197d64950453222b9f52c67\"\n"
  },
  {
    "path": "tests/Cargo.toml",
    "content": "# This file is a hack to make `cargo-fuzz` recognize the `fuzz` directory as\n# a fuzz target.\n\n[package]\nname = \"tests\"\npublish = false\nversion.workspace = true\nedition.workspace = true\nauthors.workspace = true\nlicense.workspace = true\nrepository.workspace = true\nrust-version.workspace = true\n\n[package.metadata.docs.rs]\nall-features = true\n"
  },
  {
    "path": "tests/fuzz/.gitignore",
    "content": "target\ncorpus\nartifacts\ncoverage\nCargo.lock\n"
  },
  {
    "path": "tests/fuzz/Cargo.toml",
    "content": "[package]\nname = \"boa_fuzz\"\nversion = \"0.0.0\"\npublish = false\nedition = \"2021\"\n\n[package.metadata]\ncargo-fuzz = true\n\n[dependencies]\nlibfuzzer-sys = \"0.4.7\"\n\narbitrary = \"1.3.2\"\nboa_ast = { path = \"../../core/ast\", features = [\"arbitrary\"] }\nboa_engine = { path = \"../../core/engine\", features = [\"fuzz\"] }\nboa_interner = { path = \"../../core/interner\", features = [\"arbitrary\"] }\nboa_parser = { path = \"../../core/parser\" }\n\n# Prevent this from interfering with workspaces\n[workspace]\nmembers = [\".\"]\n\n[profile.release]\ndebug = 1\n\n[[bin]]\nname = \"parser-idempotency\"\npath = \"fuzz_targets/parser-idempotency.rs\"\ntest = false\ndoc = false\n\n[[bin]]\nname = \"vm-implied\"\npath = \"fuzz_targets/vm-implied.rs\"\ntest = false\ndoc = false\n\n[[bin]]\nname = \"bytecompiler-implied\"\npath = \"fuzz_targets/bytecompiler-implied.rs\"\ntest = false\ndoc = false\n[package.metadata.docs.rs]\nall-features = true\n"
  },
  {
    "path": "tests/fuzz/README.md",
    "content": "# boa_engine-fuzz\n\nThis directory contains fuzzers which can be used to automatically identify faults present in Boa. All the fuzzers in\nthis directory are [grammar-aware](https://www.fuzzingbook.org/html/Grammars.html) (based on\n[Arbitrary](https://docs.rs/arbitrary/latest/arbitrary/)) and coverage-guided. See [common.rs](fuzz/fuzz_targets/common.rs)\nfor implementation specifics.\n\nYou can run any fuzzer you wish with the following command (replacing `your-fuzzer` with a fuzzer available in\nfuzz_targets, e.g. `parser-idempotency`):\n\n```bash\ncargo fuzz run -s none your-fuzzer\n```\n\nNote that you may wish to use a different sanitizer option (`-s`) according to what kind of issue you're looking for.\nRefer to the [cargo-fuzz book](https://rust-fuzz.github.io/book/cargo-fuzz.html) for details on how to select a\nsanitizer and other flags.\n\n## Parser Fuzzer\n\nThe parser fuzzer, located in [parser-idempotency.rs](./fuzz_targets/parser-idempotency.rs), identifies\ncorrectness issues in both the parser and the AST-to-source conversion process (e.g., via `to_interned_string`) by\nsearching for inputs which are not idempotent over parsing and conversion back to source. It does this by doing the\nfollowing:\n\n1. Generate an arbitrary AST\n2. Convert that AST to source code with `to_interned_string`; we'll call this the \"original source\"\n3. Parse the original source into an AST; we'll call this the \"first AST\"\n   - Arbitrary ASTs aren't guaranteed to be parseable; to avoid errors caused by this, we discard errors here.\n4. Convert the first AST to source code with `to_interned_string`; we'll call this the \"first source\"\n5. Parse the first source into an AST; we'll call this the \"second AST\"\n   - Since the original source was parseable, the first source must be parseable; emit any errors parsing produces.\n6. Compare the first AST and the second AST. If they are not equal, emit an error.\n   - An error here indicates that either the parser or the AST-to-source conversion lost information or added incorrect\n     information, as the inputs parsed between the two should be the same.\n\nIn this way, this fuzzer can identify correctness issues present in the parser.\n\n## Bytecompiler Fuzzer\n\nThe bytecompiler fuzzer, located in [bytecompiler-implied.rs](./fuzz_targets/bytecompiler-implied.rs), identifies cases\nwhich cause an assertion failure in the bytecompiler. These crashes can cause denial of service issues and may block the\ndiscovery of crash cases in the VM fuzzer.\n\n## VM Fuzzer\n\nThe VM fuzzer, located in [vm-implied.rs](./fuzz_targets/vm-implied.rs), identifies crash cases in the VM. It does so by\ngenerating an arbitrary AST, converting it to source code (to remove invalid inputs), then executing that source code.\nBecause we are not comparing against any invariants other than \"does it crash\", this fuzzer will only discover faults\nwhich cause the VM to terminate unexpectedly, e.g. as a result of a panic. It will not discover logic errors present in\nthe VM.\n\nTo ensure that the VM does not attempt to execute an infinite loop, Boa is restricted to a finite number of instructions\nbefore the VM is terminated. If a program takes more than a second or so to execute, it likely indicates an issue in the\nVM (as we expect the fuzzer to execute only a certain amount of instructions, which should take significantly less\ntime).\n"
  },
  {
    "path": "tests/fuzz/fuzz_targets/bytecompiler-implied.rs",
    "content": "#![no_main]\n\nmod common;\n\nuse crate::common::FuzzSource;\nuse boa_engine::{Context, Script};\nuse boa_parser::Source;\nuse libfuzzer_sys::{fuzz_target, Corpus};\nuse std::io::Cursor;\n\nfn do_fuzz(original: FuzzSource) -> Corpus {\n    let mut ctx = Context::builder()\n        .interner(original.interner)\n        .instructions_remaining(0)\n        .build()\n        .unwrap();\n    if let Ok(parsed) = Script::parse(\n        Source::from_reader(Cursor::new(&original.source), None),\n        None,\n        &mut ctx,\n    ) {\n        let _ = parsed.codeblock(&mut ctx);\n        Corpus::Keep\n    } else {\n        Corpus::Reject\n    }\n}\n\nfuzz_target!(|original: FuzzSource| -> Corpus { do_fuzz(original) });\n"
  },
  {
    "path": "tests/fuzz/fuzz_targets/common.rs",
    "content": "use arbitrary::{Arbitrary, Unstructured};\nuse boa_ast::{\n    visitor::{VisitWith, VisitorMut},\n    Expression, StatementList,\n};\nuse boa_interner::{Interner, Sym, ToInternedString};\nuse std::{\n    fmt::{Debug, Formatter},\n    ops::ControlFlow,\n};\n\n/// Context for performing fuzzing. This structure contains both the generated AST as well as the\n/// context used to resolve the symbols therein.\npub struct FuzzData {\n    pub interner: Interner,\n    pub ast: StatementList,\n}\n\nimpl<'a> Arbitrary<'a> for FuzzData {\n    fn arbitrary(u: &mut Unstructured<'a>) -> arbitrary::Result<Self> {\n        let mut interner = Interner::with_capacity(8);\n        let mut syms_available = Vec::with_capacity(8);\n        for c in 'a'..='h' {\n            syms_available.push(interner.get_or_intern(&*String::from(c)));\n        }\n\n        let mut ast = StatementList::arbitrary(u)?;\n\n        struct FuzzReplacer<'a, 's, 'u> {\n            syms: &'s [Sym],\n            u: &'u mut Unstructured<'a>,\n        }\n        impl<'a, 's, 'u, 'ast> VisitorMut<'ast> for FuzzReplacer<'a, 's, 'u> {\n            type BreakTy = arbitrary::Error;\n\n            // TODO arbitrary strings literals?\n\n            fn visit_expression_mut(\n                &mut self,\n                node: &'ast mut Expression,\n            ) -> ControlFlow<Self::BreakTy> {\n                node.visit_with_mut(self)\n            }\n\n            fn visit_sym_mut(&mut self, node: &'ast mut Sym) -> ControlFlow<Self::BreakTy> {\n                *node = self.syms[node.get() % self.syms.len()];\n                ControlFlow::Continue(())\n            }\n        }\n\n        let mut replacer = FuzzReplacer {\n            syms: &syms_available,\n            u,\n        };\n        if let ControlFlow::Break(e) = replacer.visit_statement_list_mut(&mut ast) {\n            Err(e)\n        } else {\n            Ok(Self { interner, ast })\n        }\n    }\n}\n\nimpl Debug for FuzzData {\n    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {\n        f.debug_struct(\"FuzzData\")\n            .field(\"ast\", &self.ast)\n            .finish_non_exhaustive()\n    }\n}\n\n#[allow(dead_code)]\npub struct FuzzSource {\n    pub interner: Interner,\n    pub source: String,\n}\n\nimpl<'a> Arbitrary<'a> for FuzzSource {\n    fn arbitrary(u: &mut Unstructured<'a>) -> arbitrary::Result<Self> {\n        let data = FuzzData::arbitrary(u)?;\n        let source = data.ast.to_interned_string(&data.interner);\n        Ok(Self {\n            interner: data.interner,\n            source,\n        })\n    }\n}\n\nimpl Debug for FuzzSource {\n    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {\n        f.write_fmt(format_args!(\"Fuzzed source:\\n{}\", self.source))\n    }\n}\n"
  },
  {
    "path": "tests/fuzz/fuzz_targets/parser-idempotency.rs",
    "content": "#![no_main]\n\nmod common;\n\nuse crate::common::FuzzData;\nuse boa_ast::scope::Scope;\nuse boa_interner::ToInternedString;\nuse boa_parser::{Parser, Source};\nuse libfuzzer_sys::{fuzz_target, Corpus};\nuse std::{error::Error, io::Cursor};\n\n/// Fuzzer test harness. This function accepts the arbitrary AST and performs the fuzzing operation.\n///\n/// See [README.md](../README.md) for details on the design of this fuzzer.\nfn do_fuzz(mut data: FuzzData) -> Result<(), Box<dyn Error>> {\n    let original = data.ast.to_interned_string(&data.interner);\n\n    let mut parser = Parser::new(Source::from_reader(Cursor::new(&original), None));\n    let scope = Scope::new_global();\n    let before = data.interner.len();\n    // For a variety of reasons, we may not actually produce valid code here (e.g., nameless function).\n    // Fail fast and only make the next checks if we were valid.\n    if let Ok(first) = parser.parse_script(&scope, &mut data.interner) {\n        let after_first = data.interner.len();\n        let first_interned = first.to_interned_string(&data.interner);\n\n        assert_eq!(\n            before,\n            after_first,\n            \"The number of interned symbols changed; a new string was read.\\nBefore:\\n{}\\nAfter:\\n{}\\nBefore (AST):\\n{:#?}\\nAfter (AST):\\n{:#?}\",\n            original,\n            first_interned,\n            data.ast,\n            first\n        );\n        let mut parser = Parser::new(Source::from_reader(Cursor::new(&first_interned), None));\n        let second_scope = Scope::new_global();\n\n        // Now, we most assuredly should produce valid code. It has already gone through a first pass.\n        let second = parser\n            .parse_script(&second_scope, &mut data.interner)\n            .expect(\"Could not parse the first-pass interned copy.\");\n        let second_interned = second.to_interned_string(&data.interner);\n        let after_second = data.interner.len();\n        assert_eq!(\n            after_first,\n            after_second,\n            \"The number of interned symbols changed; a new string was read.\\nBefore:\\n{}\\nAfter:\\n{}\\nBefore (AST):\\n{:#?}\\nAfter (AST):\\n{:#?}\",\n            first_interned,\n            second_interned,\n            first,\n            second\n        );\n        assert_eq!(\n            first,\n            second,\n            \"Expected the same AST after two intern passes, but found dissimilar.\\nOriginal:\\n{}\\nFirst:\\n{}\\nSecond:\\n{}\",\n            original,\n            first_interned,\n            second_interned,\n        );\n    }\n    Ok(())\n}\n\n// Fuzz harness wrapper to expose it to libfuzzer (and thus cargo-fuzz)\n// See: https://rust-fuzz.github.io/book/cargo-fuzz.html\nfuzz_target!(|data: FuzzData| -> Corpus {\n    if do_fuzz(data).is_ok() {\n        Corpus::Keep\n    } else {\n        Corpus::Reject\n    }\n});\n"
  },
  {
    "path": "tests/fuzz/fuzz_targets/vm-implied.rs",
    "content": "#![no_main]\n\nmod common;\n\nuse crate::common::FuzzSource;\nuse boa_engine::{Context, JsResult, JsValue};\nuse boa_parser::Source;\nuse libfuzzer_sys::fuzz_target;\nuse std::io::Cursor;\n\nfn do_fuzz(original: FuzzSource) -> JsResult<JsValue> {\n    let mut ctx = Context::builder()\n        .interner(original.interner)\n        .instructions_remaining(1 << 16)\n        .build()\n        .unwrap();\n    ctx.eval(Source::from_reader(Cursor::new(&original.source), None))\n}\n\nfuzz_target!(|original: FuzzSource| {\n    let _ = do_fuzz(original);\n});\n"
  },
  {
    "path": "tests/insta-bytecode/Cargo.toml",
    "content": "[package]\nname = \"insta-bytecode\"\npublish = false\nversion.workspace = true\nedition.workspace = true\n\n[dependencies]\nboa_engine = { workspace = true }\n\n[dev-dependencies]\ninsta = { version = \"1.46.3\", features = [\"filters\", \"glob\"] }\n"
  },
  {
    "path": "tests/insta-bytecode/README.md",
    "content": "# Bytecode Snapshot tests\n\nThis crate includes snapshot tests for Boa's bytecode. This gives us a workable diff\nof the bytecode output.\n\nRequired dependency: `cargo-insta`\n\n## Reviewing snapshots\n\nSnapshots can be reviewed with the below command.\n\n```bash\ncargo insta test --review\n```\n\nOR\n\n```bash\ncargo insta review\n```\n"
  },
  {
    "path": "tests/insta-bytecode/scripts/basic-loop.js",
    "content": "for (let i = 0; i < 100; ++i) {}\n"
  },
  {
    "path": "tests/insta-bytecode/scripts/double-loop-function.js",
    "content": "function f(x) {\n  return x * x;\n}\n\nfor (let n = 0; n < 20; n++) {\n  for (let n = 0; n < 50; n++) {\n    f(n);\n  }\n}\n\nundefined;\n"
  },
  {
    "path": "tests/insta-bytecode/scripts/loop-hoisting.js",
    "content": "// Verify this is hoisted outside the loop.\nfor (let i = 0; i < 100; i++) {}\n\nconst n = 100;\nfor (let i = 0; i < n; i++) {}\n\n// This should also be hoisted since it's const.\nconst z = 100;\n\nfunction bar() {\n  for (let i = 0; i < z; i++) {}\n}\n\nbar();\n\n// This should NOT be hoisted since it's a mutable binding.\nlet x = 100;\n\nfunction foo() {\n  for (let i = 0; i < x; i++) {}\n}\n\nfoo();\n"
  },
  {
    "path": "tests/insta-bytecode/scripts/new.js",
    "content": "class SomeClass {}\n\n[...new Array(100_000).map(() => new SomeClass())].length;\n"
  },
  {
    "path": "tests/insta-bytecode/src/lib.rs",
    "content": "#[test]\nfn compile_bytecode() {\n    use boa_engine::{Context, Script, Source};\n    use insta::glob;\n\n    glob!(\"../scripts/\", \"**/*.js\", |path| {\n        let context = &mut Context::default();\n        let source = Source::from_filepath(path).expect(\"Could not load source\");\n        let script = Script::parse(source, None, context).unwrap();\n        let output = script.codeblock(context).unwrap().to_string();\n        insta::assert_snapshot!(output);\n    });\n}\n"
  },
  {
    "path": "tests/insta-bytecode/src/snapshots/insta_bytecode__compile_bytecode@basic-loop.js.snap",
    "content": "---\nsource: tests/insta-bytecode/src/lib.rs\nexpression: output\ninput_file: tests/insta-bytecode/scripts/basic-loop.js\n---\n-------------------------- Compiled Output: '<main>' ---------------------------\nLocation     Handler      Opcode                            Operands\n  000000                    StoreZero                         dst:r02\n  000005                    StoreInt8                         value:100, dst:r03\n  00000b                    Jump                              address:00001a\n  000010                    IncrementLoopIteration            \n  000011                    Inc                               src:r02, dst:r02\n  00001a                    JumpIfNotLessThan                 lhs:r02, rhs:r03, address:00002c\n  000027                    Jump                              address:000010\n  00002c                    CheckReturn                       \n  00002d                    Return                            \n\nRegister Count: 5, Flags: CodeBlockFlags(HAS_PROTOTYPE_PROPERTY)\nConstants: <empty>\nBindings: <empty>\nHandlers: <empty>\nSource Map:\n    0000: 17..26: (1, 26)\n"
  },
  {
    "path": "tests/insta-bytecode/src/snapshots/insta_bytecode__compile_bytecode@double-loop-function.js.snap",
    "content": "---\nsource: tests/insta-bytecode/src/lib.rs\nexpression: output\ninput_file: tests/insta-bytecode/scripts/double-loop-function.js\n---\n-------------------------- Compiled Output: '<main>' ---------------------------\nLocation     Handler      Opcode                            Operands\n  000000                    StoreZero                         dst:r02\n  000005                    StoreInt8                         value:20, dst:r03\n  00000b                    Jump                              address:00001a\n  000010                    IncrementLoopIteration            \n  000011                    Inc                               src:r02, dst:r02\n  00001a                    JumpIfNotLessThan                 lhs:r02, rhs:r03, address:000083\n  000027                    StoreZero                         dst:r05\n  00002c                    StoreInt8                         value:50, dst:r06\n  000032                    Jump                              address:000041\n  000037                    IncrementLoopIteration            \n  000038                    Inc                               src:r05, dst:r05\n  000041                    JumpIfNotLessThan                 lhs:r05, rhs:r06, address:00007e\n  00004e                    PushFromRegister                  src:r00\n  000053                    GetNameGlobal                     dst:r07, binding_index:0, ic_index:0\n  000060                    PushFromRegister                  src:r07\n  000065                    Move                              src:r05, dst:r07\n  00006e                    PushFromRegister                  src:r07\n  000073                    Call                              argument_count:1\n  000078                    Pop                               \n  000079                    Jump                              address:000037\n  00007e                    Jump                              address:000010\n  000083                    GetNameGlobal                     dst:r03, binding_index:1, ic_index:1\n  000090                    SetAccumulator                    src:r03\n  000095                    CheckReturn                       \n  000096                    Return                            \n\nRegister Count: 8, Flags: CodeBlockFlags(HAS_PROTOTYPE_PROPERTY)\nConstants:\n    0000: [STRING] \"f\"\n    0001: [FUNCTION] name: 'f' (length: 1)\nBindings:\n    0000: f, scope: GlobalObject\n    0001: undefined, scope: GlobalObject\nHandlers: <empty>\nSource Map:\n    0000: 17..56: (5, 25)\n    0001: 56..101: (6, 27)\n    0002: 101..121: (7, 6)\n"
  },
  {
    "path": "tests/insta-bytecode/src/snapshots/insta_bytecode__compile_bytecode@loop-hoisting.js.snap",
    "content": "---\nsource: tests/insta-bytecode/src/lib.rs\nexpression: output\ninput_file: tests/insta-bytecode/scripts/loop-hoisting.js\n---\n-------------------------- Compiled Output: '<main>' ---------------------------\nLocation     Handler      Opcode                            Operands\n  000000                    StoreZero                         dst:r02\n  000005                    StoreInt8                         value:100, dst:r03\n  00000b                    Jump                              address:00001a\n  000010                    IncrementLoopIteration            \n  000011                    Inc                               src:r02, dst:r02\n  00001a                    JumpIfNotLessThan                 lhs:r02, rhs:r03, address:00002c\n  000027                    Jump                              address:000010\n  00002c                    StoreInt8                         value:100, dst:r03\n  000032                    PutLexicalValue                   src:r03, binding_index:0\n  00003b                    Move                              src:r03, dst:r04\n  000044                    StoreZero                         dst:r05\n  000049                    Jump                              address:000058\n  00004e                    IncrementLoopIteration            \n  00004f                    Inc                               src:r05, dst:r05\n  000058                    JumpIfNotLessThan                 lhs:r05, rhs:r04, address:00006a\n  000065                    Jump                              address:00004e\n  00006a                    StoreInt8                         value:100, dst:r06\n  000070                    PutLexicalValue                   src:r06, binding_index:1\n  000079                    Move                              src:r06, dst:r07\n  000082                    PushFromRegister                  src:r00\n  000087                    GetNameGlobal                     dst:r06, binding_index:2, ic_index:0\n  000094                    PushFromRegister                  src:r06\n  000099                    Call                              argument_count:0\n  00009e                    Pop                               \n  00009f                    StoreInt8                         value:100, dst:r06\n  0000a5                    PutLexicalValue                   src:r06, binding_index:3\n  0000ae                    PushFromRegister                  src:r00\n  0000b3                    GetNameGlobal                     dst:r08, binding_index:4, ic_index:1\n  0000c0                    PushFromRegister                  src:r08\n  0000c5                    Call                              argument_count:0\n  0000ca                    PopIntoRegister                   dst:r06\n  0000cf                    SetAccumulator                    src:r06\n  0000d4                    CheckReturn                       \n  0000d5                    Return                            \n\nRegister Count: 9, Flags: CodeBlockFlags(HAS_PROTOTYPE_PROPERTY)\nConstants:\n    0000: [STRING] \"n\"\n    0001: [STRING] \"z\"\n    0002: [STRING] \"x\"\n    0003: [STRING] \"bar\"\n    0004: [FUNCTION] name: 'bar' (length: 0)\n    0005: [STRING] \"foo\"\n    0006: [FUNCTION] name: 'foo' (length: 0)\nBindings:\n    0000: n, scope: GlobalDeclarative\n    0001: z, scope: GlobalDeclarative\n    0002: bar, scope: GlobalObject\n    0003: x, scope: GlobalDeclarative\n    0004: foo, scope: GlobalObject\nHandlers: <empty>\nSource Map:\n    0000: 17..79: (2, 26)\n    0001: 79..153: (5, 24)\n    0002: 153..197: (14, 4)\n    0003: 197..207: (23, 4)\n"
  },
  {
    "path": "tests/insta-bytecode/src/snapshots/insta_bytecode__compile_bytecode@new.js.snap",
    "content": "---\nsource: tests/insta-bytecode/src/lib.rs\nexpression: output\ninput_file: tests/insta-bytecode/scripts/new.js\n---\n-------------------------- Compiled Output: '<main>' ---------------------------\nLocation     Handler      Opcode                            Operands\n  000000                    PushScope                         scope_index:1\n  000005                    GetFunction                       index:2, dst:r01\n  00000e                    StoreUndefined                    dst:r02\n  000013                    SetClassPrototype                 dst:r03, prototype:r02, class:r01\n  000020                    PushPrivateEnvironment            class:r01, names:[]\n  000029                    PutLexicalValue                   src:r01, binding_index:0\n  000032                    PopEnvironment                    \n  000033                    PopPrivateEnvironment             \n  000034                    DefInitVar                        src:r01, binding_index:1\n  00003d                    StoreNewArray                     dst:r02\n  000042                    GetNameGlobal                     dst:r06, binding_index:2, ic_index:0\n  00004f                    PushFromRegister                  src:r00\n  000054                    PushFromRegister                  src:r06\n  000059                    StoreInt32                        value:100000, dst:r06\n  000062                    PushFromRegister                  src:r06\n  000067                    New                               argument_count:1\n  00006c                    PopIntoRegister                   dst:r04\n  000071                    GetPropertyByName                 dst:r05, value:r04, ic:(name:map entries:())\n  00007e                    PushFromRegister                  src:r04\n  000083                    PushFromRegister                  src:r05\n  000088                    GetFunction                       index:4, dst:r04\n  000091                    PushFromRegister                  src:r04\n  000096                    Call                              argument_count:1\n  00009b                    PopIntoRegister                   dst:r03\n  0000a0                    GetIterator                       src:r03\n  0000a5                    PushIteratorToArray               array:r02\n  0000aa                    GetLengthProperty                 dst:r01, value:r02, ic:(name:length entries:())\n  0000b7                    SetAccumulator                    src:r01\n  0000bc                    CheckReturn                       \n  0000bd                    Return                            \n\nRegister Count: 7, Flags: CodeBlockFlags(HAS_PROTOTYPE_PROPERTY)\nConstants:\n    0000: [STRING] \"SomeClass\"\n    0001: [SCOPE] index: 1, bindings: 1\n    0002: [FUNCTION] name: 'SomeClass' (length: 0)\n    0003: [STRING] \"map\"\n    0004: [FUNCTION] name: '' (length: 0)\n    0005: [STRING] \"length\"\nBindings:\n    0000: SomeClass, scope: Stack(0)\n    0001: SomeClass, scope: GlobalDeclarative\n    0002: Array, scope: GlobalObject\nHandlers: <empty>\nSource Map:\n    0000: 61..89: (3, 52)\n    0001: 89..113: (3, 5)\n    0002: 113..136: (3, 52)\n    0003: 136..160: (3, 27)\n    0004: 160..183: (3, 52)\n"
  },
  {
    "path": "tests/macros/Cargo.toml",
    "content": "[package]\nname = \"boa_macros_tests\"\ndescription = \"Testing crate for boa_macros\"\nkeywords = [\"javascript\", \"ECMAScript\", \"compiler\", \"tester\"]\npublish = false\nversion.workspace = true\nedition.workspace = true\nauthors.workspace = true\nlicense.workspace = true\nrepository.workspace = true\nrust-version.workspace = true\n\n[dev-dependencies]\ntrybuild.workspace = true\nboa_engine = { workspace = true, features = [\"embedded_lz4\"] }\nboa_gc.workspace = true\n\n[lints]\nworkspace = true\n\n[package.metadata.docs.rs]\nall-features = true\n"
  },
  {
    "path": "tests/macros/tests/assets/fibonacci.js",
    "content": "/**\n * Calculate a fibonacci number by calling callbacks with intermediate results,\n * switching between Rust and JavaScript.\n * @param {number} a The fibonacci number to calculate.\n * @param {function} callback_a A callback method.\n * @param {function} callback_b A callback method.\n * @returns {number} The {a}th fibonacci number.\n */\nexport function fibonacci(a, callback_a, callback_b) {\n  if (a <= 1) {\n    return a;\n  }\n\n  // Switch the callbacks around.\n  return (\n    callback_a(a - 1, callback_b, callback_a) +\n    callback_b(a - 2, callback_b, callback_a)\n  );\n}\n"
  },
  {
    "path": "tests/macros/tests/assets/gcd_callback.js",
    "content": "/**\n * Calculate the greatest common divisor of two numbers.\n * @param {number} a\n * @param {number} b\n * @param {function} callback A callback method to call with the result.\n * @returns {number|*} The greatest common divisor of {a} and {b}.\n * @throws {TypeError} If either {a} or {b} is not finite.\n */\nexport function gcd_callback(a, b, callback) {\n  a = +a;\n  b = +b;\n  if (!Number.isFinite(a) || !Number.isFinite(b)) {\n    throw new TypeError(\"Invalid input\");\n  }\n\n  // Euclidean algorithm\n  function inner_gcd(a, b) {\n    while (b !== 0) {\n      let t = b;\n      b = a % b;\n      a = t;\n    }\n    return a;\n  }\n\n  let result = inner_gcd(a, b);\n  callback(result);\n}\n"
  },
  {
    "path": "tests/macros/tests/class.rs",
    "content": "//! Test for the class proc-macro.\n#![allow(unused_crate_dependencies)]\n\nuse boa_engine::{\n    Context, Finalize, JsData, JsObject, JsString, JsValue, Source, Trace, boa_class, js_string,\n};\n\n#[derive(Clone, Trace, Finalize, JsData)]\nenum AnimalType {\n    Cat,\n    Dog,\n    Other,\n}\n\n#[derive(Clone, Trace, Finalize, JsData)]\nstruct Animal {\n    ty: AnimalType,\n    age: i32,\n}\n\n#[boa_class]\nimpl Animal {\n    #[boa(constructor)]\n    #[allow(clippy::needless_pass_by_value)]\n    fn new(name: String, age: i32) -> Self {\n        let ty = match name.as_str() {\n            \"cat\" => AnimalType::Cat,\n            \"dog\" => AnimalType::Dog,\n            _ => AnimalType::Other,\n        };\n\n        Self { ty, age }\n    }\n\n    #[boa(static)]\n    fn marked_static_method() -> i32 {\n        123\n    }\n\n    fn static_method() -> i32 {\n        42\n    }\n\n    // Force this being a method (instead of a static function) by declaring it\n    // as a method.\n    #[boa(method)]\n    #[boa(length = 11)]\n    fn method(context: &mut Context) -> JsObject {\n        let obj = JsObject::with_null_proto();\n        obj.set(js_string!(\"key\"), 43, false, context).unwrap();\n        obj\n    }\n\n    #[boa(getter)]\n    fn age(&self) -> i32 {\n        self.age\n    }\n\n    #[boa(setter)]\n    #[boa(method)]\n    #[boa(rename = \"age\")]\n    fn set_age(&mut self, age: i32) {\n        self.age = age;\n    }\n\n    fn speak(#[boa(error = \"`this` was not an animal\")] &self) -> JsString {\n        match self.ty {\n            AnimalType::Cat => js_string!(\"meow\"),\n            AnimalType::Dog => js_string!(\"woof\"),\n            AnimalType::Other => js_string!(r\"¯\\_(ツ)_/¯\"),\n        }\n    }\n}\n\n#[derive(Clone, Default, Trace, Finalize, JsData)]\nstruct Pair {\n    value: i32,\n}\n\n#[boa_class]\nimpl Pair {\n    #[boa(constructor)]\n    fn new(value: i32) -> Self {\n        Self { value }\n    }\n\n    #[boa(symbol = \"toPrimitive\")]\n    fn to_primitive(&self) -> i32 {\n        self.value\n    }\n\n    #[boa(symbol = \"iterator\")]\n    fn iterator(&self, context: &mut Context) -> JsValue {\n        use boa_engine::object::builtins::JsArray;\n        let arr = JsArray::from_iter(\n            [JsValue::from(self.value), JsValue::from(self.value * 10)],\n            context,\n        );\n        let iter_fn = arr\n            .get(boa_engine::JsSymbol::iterator(), context)\n            .expect(\"array should have @@iterator\");\n        iter_fn\n            .as_callable()\n            .expect(\"@@iterator should be callable\")\n            .call(&arr.into(), &[], context)\n            .expect(\"@@iterator call should succeed\")\n    }\n}\n\nconst ASSERT_DECL: &str = r\"\n    function assertEq(lhs, rhs, message) {\n      if (lhs !== rhs) {\n        throw `AssertionError: ${message ? message + ',' : ''} expected ${JSON.stringify(rhs)}, actual ${JSON.stringify(lhs)}`;\n      }\n    }\n\";\n\n#[test]\nfn boa_class() {\n    let mut context = Context::default();\n\n    context.register_global_class::<Animal>().unwrap();\n\n    context\n        .eval(Source::from_bytes(ASSERT_DECL))\n        .expect(\"Unreachable.\");\n\n    context\n        .eval(Source::from_bytes(\n            r#\"\n            let pet = new Animal(\"dog\", 3);\n            assertEq(pet.age, 3, \"Age should be the age passed to constructor\");\n\n            assertEq(Animal.staticMethod(), 42, \"Static method\");\n            assertEq(Animal.markedStaticMethod(), 123, \"Marked static method\");\n\n            v = pet.method();\n            assertEq(v.key, 43, \"Method returned\");\n\n            pet.age = 4;\n            assertEq(pet.age, 4, \"Pet setter\");\n\n            pet.setAge(5);\n            assertEq(pet.age, 5, \"Pet.setAge\");\n\n            assertEq(Animal.prototype.method.length, 11, \"Method.length\");\n            assertEq(Animal.prototype.speak.length, 0, \"speak.length\");\n            assertEq(Animal.prototype.setAge.length, 1, \"setAge.length\");\n     \"#,\n        ))\n        .expect(\"Could not evaluate script\");\n}\n\n#[test]\nfn boa_class_symbol_methods() {\n    let mut context = Context::default();\n\n    context.register_global_class::<Pair>().unwrap();\n\n    context\n        .eval(Source::from_bytes(ASSERT_DECL))\n        .expect(\"Unreachable.\");\n\n    context\n        .eval(Source::from_bytes(\n            r#\"\n            let p = new Pair(7);\n\n            // Symbol.toPrimitive should be defined on the prototype.\n            assertEq(typeof p[Symbol.toPrimitive], \"function\", \"@@toPrimitive is a function\");\n            assertEq(+p, 7, \"toPrimitive numeric coercion\");\n            assertEq(p + 3, 10, \"toPrimitive addition\");\n\n            // Symbol.iterator should be defined on the prototype.\n            assertEq(typeof p[Symbol.iterator], \"function\", \"@@iterator is a function\");\n\n            // Spread should work via [Symbol.iterator].\n            let spread = [...p];\n            assertEq(spread.length, 2, \"spread length\");\n            assertEq(spread[0], 7, \"spread[0]\");\n            assertEq(spread[1], 70, \"spread[1]\");\n\n            // for-of should also work.\n            let sum = 0;\n            for (let v of p) { sum += v; }\n            assertEq(sum, 77, \"for-of sum\");\n     \"#,\n        ))\n        .expect(\"Could not evaluate script\");\n}\n"
  },
  {
    "path": "tests/macros/tests/derive/from_js_with.rs",
    "content": "#![allow(unused)]\n\nuse boa_engine::{value::TryFromJs, Context, JsNativeError, JsResult, JsValue, JsVariant};\n\n#[derive(TryFromJs)]\nstruct TestStruct {\n    inner: bool,\n    #[boa(from_js_with = \"lossy_float\")]\n    my_int: i16,\n}\n\nfn main() {}\n\nfn lossy_float(value: &JsValue, _context: &mut Context) -> JsResult<i16> {\n    match value.variant() {\n        JsVariant::Float64(r) => Ok(r.round() as i16),\n        JsVariant::Integer32(i) => Ok(i as i16),\n        _ => Err(JsNativeError::typ()\n            .with_message(\"cannot convert value to an i16\")\n            .into()),\n    }\n}\n"
  },
  {
    "path": "tests/macros/tests/derive/simple_struct.rs",
    "content": "#![allow(unused)]\n\nuse boa_engine::value::TryFromJs;\n\n#[derive(TryFromJs)]\nstruct TestStruct {\n    inner: bool,\n}\n\nfn main() {}\n"
  },
  {
    "path": "tests/macros/tests/derive.rs",
    "content": "//! Tests for the `TryFromJs` derive macro.\n\n#![allow(unused_crate_dependencies)]\n\n#[test]\nfn try_from_js() {\n    let t = trybuild::TestCases::new();\n    t.pass(\"tests/derive/*.rs\");\n}\n"
  },
  {
    "path": "tests/macros/tests/embedded/dir1/file3.js",
    "content": "// Enable this when https://github.com/boa-dev/boa/pull/3781 is fixed and merged.\nexport { foo } from \"./file4.js\";\n"
  },
  {
    "path": "tests/macros/tests/embedded/dir1/file4.js",
    "content": "export function foo() {\n  return 3;\n}\n"
  },
  {
    "path": "tests/macros/tests/embedded/file1.js",
    "content": "import { foo } from \"./file2.js\";\nimport { foo as foo2 } from \"./dir1/file3.js\";\n\nexport function bar() {\n  return foo() + foo2() + 1;\n}\n"
  },
  {
    "path": "tests/macros/tests/embedded/file2.js",
    "content": "export function foo() {\n  return 2;\n}\n"
  },
  {
    "path": "tests/macros/tests/embedded.rs",
    "content": "//! Tests for the embedded module loader.\n\n#![allow(unused_crate_dependencies)]\n\nuse std::rc::Rc;\n\nuse boa_engine::builtins::promise::PromiseState;\nuse boa_engine::embed_module;\nuse boa_engine::module::embedded::EmbeddedModuleLoader;\nuse boa_engine::{Context, JsString, JsValue, Module, Source, js_string};\n\nfn load_module_and_test(module_loader: &Rc<EmbeddedModuleLoader>) {\n    let mut context = Context::builder()\n        .module_loader(module_loader.clone())\n        .build()\n        .unwrap();\n\n    // Resolving modules that exist but haven't been cached yet should return None.\n    assert_eq!(module_loader.get_module(&JsString::from(\"/file1.js\")), None);\n    assert_eq!(\n        module_loader.get_module(&JsString::from(\"/non-existent.js\")),\n        None\n    );\n\n    let module = Module::parse(\n        Source::from_bytes(b\"export { bar } from '/file1.js';\"),\n        None,\n        &mut context,\n    )\n    .expect(\"failed to parse module\");\n    let promise = module.load_link_evaluate(&mut context);\n    context.run_jobs().unwrap();\n\n    match promise.state() {\n        PromiseState::Fulfilled(value) => {\n            assert!(\n                value.is_undefined(),\n                \"Expected undefined, got {}\",\n                value.display()\n            );\n\n            let bar = module\n                .namespace(&mut context)\n                .get(js_string!(\"bar\"), &mut context)\n                .unwrap()\n                .as_callable()\n                .unwrap();\n            let value = bar.call(&JsValue::undefined(), &[], &mut context).unwrap();\n            assert_eq!(\n                value.as_number(),\n                Some(6.),\n                \"Expected 6, got {}\",\n                value.display()\n            );\n        }\n        PromiseState::Rejected(err) => panic!(\n            \"promise was not fulfilled: {:?}\",\n            err.to_string(&mut context)\n        ),\n        PromiseState::Pending => panic!(\"Promise was not settled\"),\n    }\n}\n\n#[test]\nfn simple() {\n    #[cfg(target_family = \"unix\")]\n    let module_loader = Rc::new(embed_module!(\"tests/embedded/\", compress = \"none\"));\n    #[cfg(target_family = \"windows\")]\n    let module_loader = Rc::new(embed_module!(\"tests\\\\embedded\\\\\"));\n\n    load_module_and_test(&module_loader);\n}\n\n#[test]\nfn compressed_lz4() {\n    #[cfg(target_family = \"unix\")]\n    let module_loader = Rc::new(embed_module!(\"tests/embedded/\", compress = \"lz4\"));\n    #[cfg(target_family = \"windows\")]\n    let module_loader = Rc::new(embed_module!(\"tests\\\\embedded\\\\\", compress = \"lz4\"));\n\n    load_module_and_test(&module_loader);\n}\n"
  },
  {
    "path": "tests/macros/tests/fibonacci.rs",
    "content": "#![allow(unused_crate_dependencies)]\n//! A test that goes back and forth between JavaScript and Rust.\n\n// You can execute this example with `cargo run --example gcd`\n\nuse boa_engine::object::builtins::{JsFunction, TypedJsFunction};\nuse boa_engine::{Context, IntoJsFunctionCopied, JsResult, Module, Source, js_error, js_string};\nuse std::path::PathBuf;\n\n#[allow(clippy::needless_pass_by_value)]\nfn fibonacci(\n    a: usize,\n    cb_a: TypedJsFunction<(usize, JsFunction, JsFunction), usize>,\n    cb_b: TypedJsFunction<(usize, JsFunction, JsFunction), usize>,\n    context: &mut Context,\n) -> JsResult<usize> {\n    if a <= 1 {\n        Ok(a)\n    } else {\n        Ok(\n            cb_a.call(context, (a - 1, cb_b.clone().into(), cb_a.clone().into()))?\n                + cb_b.call(context, (a - 2, cb_b.clone().into(), cb_a.clone().into()))?,\n        )\n    }\n}\n\nfn fibonacci_throw(\n    a: usize,\n    cb_a: TypedJsFunction<(usize, JsFunction, JsFunction), usize>,\n    cb_b: TypedJsFunction<(usize, JsFunction, JsFunction), usize>,\n    context: &mut Context,\n) -> JsResult<usize> {\n    if a < 5 {\n        Err(js_error!(\"a is too small\"))\n    } else {\n        fibonacci(a, cb_a, cb_b, context)\n    }\n}\n\n#[test]\nfn fibonacci_test() {\n    let assets_dir =\n        PathBuf::from(std::env::var(\"CARGO_MANIFEST_DIR\").unwrap()).join(\"tests/assets\");\n\n    // Create the engine.\n    let context = &mut Context::default();\n\n    // Load the JavaScript code.\n    let gcd_path = assets_dir.join(\"fibonacci.js\");\n    let source = Source::from_filepath(&gcd_path).unwrap();\n    let module = Module::parse(source, None, context).unwrap();\n    module\n        .load_link_evaluate(context)\n        .await_blocking(context)\n        .unwrap();\n\n    let fibonacci_js = module\n        .get_typed_fn::<(usize, JsFunction, JsFunction), usize>(js_string!(\"fibonacci\"), context)\n        .unwrap();\n\n    let fibonacci_rust = fibonacci\n        .into_js_function_copied(context)\n        .to_js_function(context.realm());\n\n    assert_eq!(\n        fibonacci_js\n            .call(\n                context,\n                (\n                    10,\n                    fibonacci_rust.clone(),\n                    fibonacci_js.as_js_function().clone()\n                )\n            )\n            .unwrap(),\n        55\n    );\n\n    let fibonacci_throw = fibonacci_throw\n        .into_js_function_copied(context)\n        .to_js_function(context.realm());\n    assert!(\n        fibonacci_js\n            .call(\n                context,\n                (\n                    10,\n                    fibonacci_throw.clone(),\n                    fibonacci_js.as_js_function().clone()\n                )\n            )\n            .unwrap_err()\n            .to_string()\n            .contains(\"\\\"a is too small\\\"\"),\n    );\n}\n"
  },
  {
    "path": "tests/macros/tests/gcd_callback.rs",
    "content": "#![allow(unused_crate_dependencies)]\n//! A test that mimics the `boa_engine`'s GCD test with a typed callback.\n\nuse boa_engine::interop::ContextData;\nuse boa_engine::object::builtins::JsFunction;\nuse boa_engine::{Context, IntoJsFunctionCopied, Module, Source, js_string};\nuse boa_gc::Gc;\nuse std::path::PathBuf;\nuse std::sync::atomic::{AtomicUsize, Ordering};\n\nfn callback_from_js(ContextData(r): ContextData<Gc<AtomicUsize>>, result: usize) {\n    r.store(result, Ordering::Relaxed);\n}\n\n#[test]\nfn gcd_callback() {\n    let assets_dir =\n        PathBuf::from(std::env::var(\"CARGO_MANIFEST_DIR\").unwrap()).join(\"tests/assets\");\n\n    // Create the engine.\n    let context = &mut Context::default();\n    let result = Gc::new(AtomicUsize::new(0));\n    context.insert_data(result.clone());\n\n    // Load the JavaScript code.\n    let gcd_path = assets_dir.join(\"gcd_callback.js\");\n    let source = Source::from_filepath(&gcd_path).unwrap();\n    let module = Module::parse(source, None, context).unwrap();\n    module\n        .load_link_evaluate(context)\n        .await_blocking(context)\n        .unwrap();\n\n    let js_gcd = module\n        .get_typed_fn::<(i32, i32, JsFunction), ()>(js_string!(\"gcd_callback\"), context)\n        .unwrap();\n\n    let function = callback_from_js\n        .into_js_function_copied(context)\n        .to_js_function(context.realm());\n\n    result.store(0, Ordering::Relaxed);\n    assert_eq!(js_gcd.call(context, (6, 9, function.clone())), Ok(()));\n    assert_eq!(result.load(Ordering::Relaxed), 3);\n\n    result.store(0, Ordering::Relaxed);\n    assert_eq!(js_gcd.call(context, (9, 6, function)), Ok(()));\n    assert_eq!(result.load(Ordering::Relaxed), 3);\n}\n"
  },
  {
    "path": "tests/macros/tests/module.rs",
    "content": "//! Test for the class proc-macro.\n#![allow(unused_crate_dependencies)]\n\nuse boa_engine::module::MapModuleLoader;\nuse boa_engine::{\n    Context, Finalize, JsData, JsString, Module, Source, Trace, boa_class, boa_module, js_string,\n};\nuse std::rc::Rc;\n\n#[derive(Clone, Trace, Finalize, JsData)]\nenum AnimalType {\n    Cat,\n    Dog,\n    Other,\n}\n\n#[derive(Clone, Trace, Finalize, JsData)]\nstruct Animal {\n    ty: AnimalType,\n    age: i32,\n}\n\n#[boa_class]\nimpl Animal {\n    #[boa(constructor)]\n    #[allow(clippy::needless_pass_by_value)]\n    fn new(name: String, age: i32) -> Self {\n        let ty = match name.as_str() {\n            \"cat\" => AnimalType::Cat,\n            \"dog\" => AnimalType::Dog,\n            _ => AnimalType::Other,\n        };\n\n        Self { ty, age }\n    }\n\n    #[boa(getter)]\n    fn age(&self) -> i32 {\n        self.age\n    }\n\n    fn speak(#[boa(error = \"`this` was not an animal\")] &self) -> JsString {\n        match self.ty {\n            AnimalType::Cat => js_string!(\"meow\"),\n            AnimalType::Dog => js_string!(\"woof\"),\n            AnimalType::Other => js_string!(r\"¯\\_(ツ)_/¯\"),\n        }\n    }\n}\n\n#[boa_module]\nmod hello {\n    use boa_engine::{JsString, js_string};\n\n    fn world() -> JsString {\n        js_string!(\"hello world\")\n    }\n\n    type Animal = super::Animal;\n\n    const SOME_LITERAL_NUMBER: i32 = 1234;\n\n    #[boa(rename = \"this_is_different\")]\n    const SOME_OTHER_LITERAL: i32 = 5678;\n}\n\nconst ASSERT_DECL: &str = r\"\n    function assertEq(lhs, rhs, message) {\n      if (lhs !== rhs) {\n        throw `AssertionError: ${message ? message + ',' : ''} expected ${JSON.stringify(rhs)}, actual ${JSON.stringify(lhs)}`;\n      }\n    }\n\";\n\n#[test]\nfn boa_module() {\n    let module_loader = Rc::new(MapModuleLoader::new());\n    let mut context = Context::builder()\n        .module_loader(module_loader.clone())\n        .build()\n        .expect(\"Could not create context.\");\n\n    module_loader.insert(\"/hello.js\", hello::boa_module(None, &mut context));\n\n    context\n        .eval(Source::from_bytes(ASSERT_DECL))\n        .expect(\"Unreachable.\");\n\n    let module = Module::parse(\n        Source::from_bytes(\n            r#\"\n                import * as m from '/hello.js';\n\n                assertEq(m.someLiteralNumber, 1234, \"Const value\");\n                assertEq(m.this_is_different, 5678, \"Renamed const value\");\n\n                assertEq(m.world(), \"hello world\", \"Method call\");\n\n                let pet = new m.Animal(\"dog\", 8);\n                assertEq(pet.age, 8, \"Property of class\");\n                assertEq(pet.speak(), \"woof\", \"Method class of class\");\n            \"#,\n        ),\n        None,\n        &mut context,\n    )\n    .expect(\"Could not load module\");\n\n    let result = module\n        .load_link_evaluate(&mut context)\n        .await_blocking(&mut context);\n\n    if let Err(e) = result {\n        panic!(\"error: {e:?}\\n{e}\");\n    }\n}\n"
  },
  {
    "path": "tests/macros/tests/optional.rs",
    "content": "//! Tests for optional values in `TryFromJs` derive.\n\n#![allow(unused_crate_dependencies)]\n\nuse boa_engine::Source;\nuse boa_engine::value::TryFromJs;\n\n#[derive(PartialEq, Eq, TryFromJs)]\nstruct Deserialize {\n    required: String,\n    optional: Option<String>,\n}\n\n#[test]\nfn optional_missing_try_from_js() {\n    let mut context = boa_engine::Context::default();\n    let value = context\n        .eval(Source::from_bytes(\n            r#\"\n            let empty = {\n                \"required\":\"foo\",\n            };\n            empty\n        \"#,\n        ))\n        .unwrap();\n\n    let deserialized: Deserialize = Deserialize::try_from_js(&value, &mut context).unwrap();\n    assert_eq!(deserialized.required, \"foo\");\n    assert_eq!(deserialized.optional, None);\n}\n\n#[test]\nfn optional_try_from_js() {\n    let mut context = boa_engine::Context::default();\n    let value = context\n        .eval(Source::from_bytes(\n            r#\"\n            let empty = {\n                \"required\": \"foo\",\n                \"optional\": \"bar\",\n            };\n            empty\n        \"#,\n        ))\n        .unwrap();\n\n    let deserialized: Deserialize = Deserialize::try_from_js(&value, &mut context).unwrap();\n    assert_eq!(deserialized.required, \"foo\");\n    assert_eq!(deserialized.optional, Some(\"bar\".to_string()));\n}\n\n#[test]\nfn required_missing_try_from_js() {\n    let mut context = boa_engine::Context::default();\n    let value = context\n        .eval(Source::from_bytes(\n            r\"\n            let value = {};\n            value\n        \",\n        ))\n        .unwrap();\n\n    assert!(\n        Deserialize::try_from_js(&value, &mut context).is_err(),\n        \"foo\"\n    );\n}\n"
  },
  {
    "path": "tests/src/lib.rs",
    "content": "\n"
  },
  {
    "path": "tests/tester/Cargo.toml",
    "content": "[package]\nname = \"boa_tester\"\ndescription = \"ECMA-262 tests runner for the Boa JavaScript engine.\"\nkeywords = [\"javascript\", \"ECMAScript\", \"compiler\", \"test262\", \"tester\"]\ncategories = [\"command-line-utilites\"]\npublish = false\nversion.workspace = true\nedition.workspace = true\nauthors.workspace = true\nlicense.workspace = true\nrepository.workspace = true\nrust-version.workspace = true\n\n[dependencies]\nboa_engine = { workspace = true, features = [\"float16\"] }\nboa_runtime.workspace = true\nboa_gc.workspace = true\nclap = { workspace = true, features = [\"derive\"] }\nserde = { workspace = true, features = [\"derive\"] }\nserde_yaml = \"0.9.34\" # TODO: Track https://github.com/saphyr-rs/saphyr.\nserde_json.workspace = true\nbitflags.workspace = true\ncolored.workspace = true\nrustc-hash = { workspace = true, features = [\"std\"] }\nrayon.workspace = true\ntoml.workspace = true\ncolor-eyre.workspace = true\nphf = { workspace = true, features = [\"macros\"] }\ncomfy-table.workspace = true\nserde_repr.workspace = true\nbus.workspace = true\ncow-utils.workspace = true\n\n[features]\nannex-b = [\"boa_engine/annex-b\"]\ndefault = [\"boa_engine/intl_bundled\", \"boa_engine/experimental\", \"annex-b\"]\n\n[lints]\nworkspace = true\n\n[package.metadata.docs.rs]\nall-features = true\n"
  },
  {
    "path": "tests/tester/src/edition.rs",
    "content": "//! Edition detection utilities.\n//!\n//! This module contains the [`SpecEdition`] struct, which is used in the tester to\n//! classify all tests per minimum required ECMAScript edition.\n\nuse std::fmt::Display;\n\nuse serde_repr::{Deserialize_repr, Serialize_repr};\n\nuse crate::read::{MetaData, TestFlag};\n\n/// Minimum edition required by a specific feature in the `test262` repository.\nstatic FEATURE_EDITION: phf::Map<&'static str, SpecEdition> = phf::phf_map! {\n    // Proposed language features\n\n    // Import bytes proposal\n    // https://github.com/tc39/proposal-import-bytes\n    \"import-bytes\"  => SpecEdition::ESNext,\n\n    // Await dictionary proposal\n    // https://github.com/tc39/proposal-await-dictionary\n    \"await-dictionary\"  => SpecEdition::ESNext,\n\n    // Intl.Locale Info\n    // https://github.com/tc39/proposal-intl-locale-info\n    \"Intl.Locale-info\"  => SpecEdition::ESNext,\n\n    // FinalizationRegistry#cleanupSome\n    // https://github.com/tc39/proposal-cleanup-some\n    \"FinalizationRegistry.prototype.cleanupSome\" => SpecEdition::ESNext,\n\n    // Legacy RegExp features\n    // https://github.com/tc39/proposal-regexp-legacy-features\n    \"legacy-regexp\"  => SpecEdition::ESNext,\n\n    // Import Defer\n    // https://tc39.es/proposal-defer-import-eval\n    \"import-defer\" => SpecEdition::ESNext,\n\n    // Iterator sequencing\n    // https://github.com/tc39/proposal-iterator-sequencing\n    \"iterator-sequencing\" => SpecEdition::ESNext,\n\n    // Time Zone Canonicalization\n    // https://github.com/tc39/proposal-canonical-tz\n    \"canonical-tz\" => SpecEdition::ESNext,\n\n    // Upsert\n    // https://github.com/tc39/proposal-upsert\n    \"upsert\" => SpecEdition::ESNext,\n\n    // Temporal\n    // https://github.com/tc39/proposal-temporal\n    \"Temporal\" => SpecEdition::ESNext,\n\n    // ShadowRealm, née Callable Boundary Realms\n    // https://github.com/tc39/proposal-realms\n    \"ShadowRealm\" => SpecEdition::ESNext,\n\n    // Decorators\n    // https://github.com/tc39/proposal-decorators\n    \"decorators\" => SpecEdition::ESNext,\n\n    // Array.fromAsync\n    // https://github.com/tc39/proposal-array-from-async\n    \"Array.fromAsync\" => SpecEdition::ESNext,\n\n    // JSON.parse with source\n    // https://github.com/tc39/proposal-json-parse-with-source\n    \"json-parse-with-source\" => SpecEdition::ESNext,\n\n    // Regular expression modifiers\n    // https://github.com/tc39/proposal-regexp-modifiers\n    \"regexp-modifiers\" => SpecEdition::ESNext,\n\n    // Explicit Resource Management\n    // https://github.com/tc39/proposal-explicit-resource-management\n    \"explicit-resource-management\" => SpecEdition::ESNext,\n\n    // Math.sumPrecise\n    // https://github.com/tc39/proposal-math-sum\n    \"Math.sumPrecise\" => SpecEdition::ESNext,\n\n    // Source Phase Imports\n    // https://github.com/tc39/proposal-source-phase-imports\n    \"source-phase-imports\" => SpecEdition::ESNext,\n    // test262 special specifier\n    \"source-phase-imports-module-source\" => SpecEdition::ESNext,\n\n    // Uint8Array Base64\n    // https://github.com/tc39/proposal-arraybuffer-base64\n    \"uint8array-base64\" => SpecEdition::ESNext,\n\n    // Atomics.pause\n    // https://github.com/tc39/proposal-atomics-microwait\n    \"Atomics.pause\" => SpecEdition::ESNext,\n\n    // Immutable Array Buffer\n    // https://github.com/tc39/proposal-immutable-arraybuffer\n    \"immutable-arraybuffer\" => SpecEdition::ESNext,\n\n    // Non-extensible Applies to Private\n    // https://github.com/tc39/proposal-nonextensible-applies-to-private\n    \"nonextensible-applies-to-private\" => SpecEdition::ESNext,\n\n    // ===== Next ES version =====\n\n    // Error.isError\n    // https://github.com/tc39/proposal-is-error\n    \"Error.isError\" => SpecEdition::ESNext,\n\n    // Import Attributes\n    // https://github.com/tc39/proposal-import-attributes/\n    \"import-attributes\" => SpecEdition::ESNext,\n\n    // Joint iteration\n    // https://github.com/tc39/proposal-joint-iteration\n    \"joint-iteration\" => SpecEdition::ESNext,\n\n    // JSON modules\n    // https://github.com/tc39/proposal-json-modules\n    \"json-modules\"  => SpecEdition::ESNext,\n\n    // Intl.DurationFormat\n    // https://github.com/tc39/proposal-intl-duration-format\n    \"Intl.DurationFormat\" => SpecEdition::ESNext,\n\n    // Intl.Era-monthcode\n    // https://github.com/tc39/proposal-intl-era-monthcode\n    \"Intl.Era-monthcode\" => SpecEdition::ESNext,\n\n    // Duplicate named capturing groups\n    // https://github.com/tc39/proposal-duplicate-named-capturing-groups\n    \"regexp-duplicate-named-groups\" => SpecEdition::ESNext,\n\n    // RegExp.escape\n    // https://github.com/tc39/proposal-regex-escaping\n    \"RegExp.escape\" => SpecEdition::ESNext,\n\n    // Iterator Helpers\n    // https://github.com/tc39/proposal-iterator-helpers\n    \"iterator-helpers\" => SpecEdition::ESNext,\n\n    // Promise.try\n    // https://github.com/tc39/proposal-promise-try\n    \"promise-try\" => SpecEdition::ESNext,\n\n    // Float16Array + Math.f16round\n    // https://github.com/tc39/proposal-float16array\n    \"Float16Array\" => SpecEdition::ESNext,\n\n    // Standard language features\n    \"AggregateError\" => SpecEdition::ES12,\n    \"Atomics.waitAsync\"  => SpecEdition::ES15,\n    \"align-detached-buffer-semantics-with-web-reality\" => SpecEdition::ES12,\n    \"arbitrary-module-namespace-names\" => SpecEdition::ES13,\n    // https://github.com/tc39/proposal-arraybuffer-transfer\n    \"arraybuffer-transfer\" => SpecEdition::ES15,\n    \"array-grouping\" => SpecEdition::ES15,\n    \"ArrayBuffer\" => SpecEdition::ES6,\n    \"array-find-from-last\" => SpecEdition::ES14,\n    \"Array.prototype.at\" => SpecEdition::ES13,\n    \"Array.prototype.flat\" => SpecEdition::ES10,\n    \"Array.prototype.flatMap\" => SpecEdition::ES10,\n    \"Array.prototype.includes\" => SpecEdition::ES7,\n    \"Array.prototype.values\" => SpecEdition::ES6,\n    \"arrow-function\" => SpecEdition::ES6,\n    \"async-iteration\" => SpecEdition::ES9,\n    \"async-functions\" => SpecEdition::ES8,\n    \"Atomics\" => SpecEdition::ES8,\n    \"BigInt\" => SpecEdition::ES11,\n    \"caller\" => SpecEdition::ES5,\n    \"change-array-by-copy\" => SpecEdition::ES14,\n    \"class\" => SpecEdition::ES6,\n    \"class-fields-private\" => SpecEdition::ES13,\n    \"class-fields-private-in\" => SpecEdition::ES13,\n    \"class-fields-public\" => SpecEdition::ES13,\n    \"class-methods-private\" => SpecEdition::ES13,\n    \"class-static-block\" => SpecEdition::ES13,\n    \"class-static-fields-private\" => SpecEdition::ES13,\n    \"class-static-fields-public\" => SpecEdition::ES13,\n    \"class-static-methods-private\" => SpecEdition::ES13,\n    \"coalesce-expression\" => SpecEdition::ES11,\n    \"computed-property-names\" => SpecEdition::ES6,\n    \"const\" => SpecEdition::ES6,\n    \"cross-realm\" => SpecEdition::ES6,\n    \"DataView\" => SpecEdition::ES6,\n    \"DataView.prototype.getFloat16\" => SpecEdition::ES6,\n    \"DataView.prototype.getFloat32\" => SpecEdition::ES6,\n    \"DataView.prototype.getFloat64\" => SpecEdition::ES6,\n    \"DataView.prototype.getInt16\" => SpecEdition::ES6,\n    \"DataView.prototype.getInt32\" => SpecEdition::ES6,\n    \"DataView.prototype.getInt8\" => SpecEdition::ES6,\n    \"DataView.prototype.getUint16\" => SpecEdition::ES6,\n    \"DataView.prototype.getUint32\" => SpecEdition::ES6,\n    \"DataView.prototype.setUint8\" => SpecEdition::ES6,\n    \"default-parameters\" => SpecEdition::ES6,\n    \"destructuring-assignment\" => SpecEdition::ES6,\n    \"destructuring-binding\" => SpecEdition::ES6,\n    \"dynamic-import\" => SpecEdition::ES11,\n    \"error-cause\" => SpecEdition::ES13,\n    \"exponentiation\" => SpecEdition::ES7,\n    \"export-star-as-namespace-from-module\" => SpecEdition::ES11,\n    \"FinalizationRegistry\" => SpecEdition::ES12,\n    \"for-in-order\" => SpecEdition::ES11,\n    \"for-of\" => SpecEdition::ES6,\n    \"Float32Array\" => SpecEdition::ES6,\n    \"Float64Array\" => SpecEdition::ES6,\n    \"generators\" => SpecEdition::ES6,\n    \"globalThis\" => SpecEdition::ES11,\n    \"hashbang\" => SpecEdition::ES14,\n    \"import.meta\" => SpecEdition::ES11,\n    \"Int8Array\" => SpecEdition::ES6,\n    \"Int16Array\" => SpecEdition::ES6,\n    \"Int32Array\" => SpecEdition::ES6,\n    \"Intl-enumeration\" => SpecEdition::ES14,\n    \"intl-normative-optional\" => SpecEdition::ES8,\n    \"Intl.DateTimeFormat-datetimestyle\" => SpecEdition::ES12,\n    \"Intl.DateTimeFormat-dayPeriod\" => SpecEdition::ES8,\n    \"Intl.DateTimeFormat-extend-timezonename\" => SpecEdition::ES13,\n    \"Intl.DateTimeFormat-formatRange\" => SpecEdition::ES12,\n    \"Intl.DateTimeFormat-fractionalSecondDigits\" => SpecEdition::ES12,\n    \"Intl.DisplayNames\" => SpecEdition::ES12,\n    \"Intl.DisplayNames-v2\" => SpecEdition::ES13,\n    \"Intl.ListFormat\" => SpecEdition::ES12,\n    \"Intl.Locale\" => SpecEdition::ES12,\n    \"Intl.NumberFormat-unified\" => SpecEdition::ES11,\n    \"Intl.NumberFormat-v3\" => SpecEdition::ES14,\n    \"Intl.RelativeTimeFormat\" => SpecEdition::ES11,\n    \"Intl.Segmenter\" => SpecEdition::ES13,\n    \"json-superset\" => SpecEdition::ES10,\n    \"let\" => SpecEdition::ES6,\n    \"logical-assignment-operators\" => SpecEdition::ES12,\n    \"Map\" => SpecEdition::ES6,\n    \"new.target\" => SpecEdition::ES6,\n    \"numeric-separator-literal\" => SpecEdition::ES12,\n    \"object-rest\" => SpecEdition::ES9,\n    \"object-spread\" => SpecEdition::ES9,\n    \"Object.fromEntries\" => SpecEdition::ES10,\n    \"Object.hasOwn\" => SpecEdition::ES13,\n    \"Object.is\" => SpecEdition::ES6,\n    \"optional-catch-binding\" => SpecEdition::ES10,\n    \"optional-chaining\" => SpecEdition::ES11,\n    \"Promise\" => SpecEdition::ES6,\n    \"Promise.allSettled\" => SpecEdition::ES11,\n    \"Promise.any\" => SpecEdition::ES12,\n    \"Promise.prototype.finally\" => SpecEdition::ES9,\n    \"Proxy\" => SpecEdition::ES6,\n    \"promise-with-resolvers\" => SpecEdition::ES15,\n    \"proxy-missing-checks\" => SpecEdition::ES6,\n    \"Reflect\" => SpecEdition::ES6,\n    \"Reflect.construct\" => SpecEdition::ES6,\n    \"Reflect.set\" => SpecEdition::ES6,\n    \"Reflect.setPrototypeOf\" => SpecEdition::ES6,\n    \"regexp-dotall\" => SpecEdition::ES9,\n    \"regexp-lookbehind\" => SpecEdition::ES9,\n    \"regexp-match-indices\" => SpecEdition::ES13,\n    \"regexp-named-groups\" => SpecEdition::ES9,\n    \"regexp-unicode-property-escapes\" => SpecEdition::ES9,\n    \"regexp-v-flag\" => SpecEdition::ES15,\n    \"resizable-arraybuffer\" => SpecEdition::ES15,\n    \"rest-parameters\" => SpecEdition::ES6,\n    \"Set\" => SpecEdition::ES6,\n    \"SharedArrayBuffer\" => SpecEdition::ES8,\n    \"stable-array-sort\" => SpecEdition::ES10,\n    \"stable-typedarray-sort\" => SpecEdition::ES10,\n    \"string-trimming\" => SpecEdition::ES10,\n    \"String.fromCodePoint\" => SpecEdition::ES6,\n    \"String.prototype.at\" => SpecEdition::ES13,\n    \"String.prototype.endsWith\" => SpecEdition::ES6,\n    \"String.prototype.includes\" => SpecEdition::ES6,\n    \"String.prototype.isWellFormed\" => SpecEdition::ES15,\n    \"String.prototype.matchAll\" => SpecEdition::ES11,\n    \"String.prototype.replaceAll\" => SpecEdition::ES12,\n    \"String.prototype.toWellFormed\" => SpecEdition::ES15,\n    \"String.prototype.trimEnd\" => SpecEdition::ES10,\n    \"String.prototype.trimStart\" => SpecEdition::ES10,\n    // https://github.com/tc39/proposal-set-methods\n    \"set-methods\" => SpecEdition::ES16,\n    \"super\" => SpecEdition::ES6,\n    \"Symbol\" => SpecEdition::ES6,\n    \"symbols-as-weakmap-keys\" => SpecEdition::ES14,\n    \"Symbol.asyncIterator\" => SpecEdition::ES9,\n    \"Symbol.hasInstance\" => SpecEdition::ES6,\n    \"Symbol.isConcatSpreadable\" => SpecEdition::ES6,\n    \"Symbol.iterator\" => SpecEdition::ES6,\n    \"Symbol.match\" => SpecEdition::ES6,\n    \"Symbol.matchAll\" => SpecEdition::ES11,\n    \"Symbol.prototype.description\" => SpecEdition::ES10,\n    \"Symbol.replace\" => SpecEdition::ES6,\n    \"Symbol.search\" => SpecEdition::ES6,\n    \"Symbol.species\" => SpecEdition::ES6,\n    \"Symbol.split\" => SpecEdition::ES6,\n    \"Symbol.toPrimitive\" => SpecEdition::ES6,\n    \"Symbol.toStringTag\" => SpecEdition::ES6,\n    \"Symbol.unscopables\" => SpecEdition::ES6,\n    \"tail-call-optimization\" => SpecEdition::ES6,\n    \"template\" => SpecEdition::ES6,\n    \"top-level-await\" => SpecEdition::ES13,\n    \"TypedArray\" => SpecEdition::ES6,\n    \"TypedArray.prototype.at\" => SpecEdition::ES13,\n    \"u180e\" => SpecEdition::ES7,\n    \"Uint8Array\" => SpecEdition::ES6,\n    \"Uint16Array\" => SpecEdition::ES6,\n    \"Uint32Array\" => SpecEdition::ES6,\n    \"Uint8ClampedArray\" => SpecEdition::ES6,\n    \"WeakMap\" => SpecEdition::ES6,\n    \"WeakRef\" => SpecEdition::ES12,\n    \"WeakSet\" => SpecEdition::ES6,\n    \"well-formed-json-stringify\" => SpecEdition::ES10,\n    \"__proto__\" => SpecEdition::ES6,\n    \"__getter__\" => SpecEdition::ES8,\n    \"__setter__\" => SpecEdition::ES8,\n\n    // Test-Harness Features\n\n    \"IsHTMLDDA\" => SpecEdition::ES9,\n    \"host-gc-required\" => SpecEdition::ES5,\n};\n\n/// List of ECMAScript editions that can be tested in the `test262` repository.\n#[derive(\n    Debug,\n    Clone,\n    Copy,\n    PartialEq,\n    Eq,\n    PartialOrd,\n    Ord,\n    Default,\n    Serialize_repr,\n    Deserialize_repr,\n    clap::ValueEnum,\n)]\n#[repr(u8)]\npub(crate) enum SpecEdition {\n    /// ECMAScript 5.1 Edition\n    ///\n    /// <https://262.ecma-international.org/5.1>\n    ES5 = 5,\n    /// ECMAScript 6th Edition\n    ///\n    /// <https://262.ecma-international.org/6.0>\n    ES6,\n    /// ECMAScript 7th Edition\n    ///\n    /// <https://262.ecma-international.org/7.0>\n    ES7,\n    /// ECMAScript 8th Edition\n    ///\n    /// <https://262.ecma-international.org/8.0>\n    ES8,\n    /// ECMAScript 9th Edition\n    ///\n    /// <https://262.ecma-international.org/9.0>\n    ES9,\n    /// ECMAScript 10th Edition\n    ///\n    /// <https://262.ecma-international.org/10.0>\n    ES10,\n    /// ECMAScript 11th Edition\n    ///\n    /// <https://262.ecma-international.org/11.0>\n    ES11,\n    /// ECMAScript 12th Edition\n    ///\n    /// <https://262.ecma-international.org/12.0>\n    ES12,\n    /// ECMAScript 13th Edition\n    ///\n    /// <https://262.ecma-international.org/13.0>\n    ES13,\n    /// ECMAScript 14th Edition\n    ///\n    /// <https://262.ecma-international.org/14.0>\n    ES14,\n    /// ECMAScript 15th Edition\n    ///\n    /// <https://262.ecma-international.org/15.0>\n    ES15,\n    /// ECMAScript 16th Edition\n    ///\n    /// <https://262.ecma-international.org/16.0>\n    ES16,\n    /// The edition being worked on right now.\n    ///\n    /// A draft is currently available [here](https://tc39.es/ecma262).\n    #[default]\n    ESNext = 255,\n}\n\nimpl Display for SpecEdition {\n    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {\n        match *self {\n            Self::ESNext => write!(f, \"ECMAScript Next\"),\n            Self::ES5 => write!(f, \"ECMAScript 5.1\"),\n            v => write!(f, \"ECMAScript {}\", v as u8),\n        }\n    }\n}\n\nimpl SpecEdition {\n    /// Gets the minimum required ECMAScript edition of a test from its metadata.\n    ///\n    /// If the function finds unknown features in `metadata`, returns an `Err(Vec<&str>)` containing\n    /// the list of unknown features.\n    pub(crate) fn from_test_metadata(metadata: &MetaData) -> Result<Self, Vec<&str>> {\n        let mut min_edition = if metadata.flags.contains(&TestFlag::Async) {\n            Self::ES8\n        } else if metadata.flags.contains(&TestFlag::Module)\n            || metadata.esid.is_some()\n            || metadata.es6id.is_some()\n        {\n            Self::ES6\n        } else {\n            Self::ES5\n        };\n\n        let mut unknowns = Vec::new();\n        for feature in &*metadata.features {\n            let Some(feature_edition) = FEATURE_EDITION.get(feature).copied() else {\n                unknowns.push(&**feature);\n                continue;\n            };\n            min_edition = std::cmp::max(min_edition, feature_edition);\n        }\n\n        if unknowns.is_empty() {\n            Ok(min_edition)\n        } else {\n            Err(unknowns)\n        }\n    }\n\n    /// Gets an iterator of all currently available editions.\n    pub(crate) fn all_editions() -> impl Iterator<Item = Self> {\n        [\n            Self::ES5,\n            Self::ES6,\n            Self::ES7,\n            Self::ES8,\n            Self::ES9,\n            Self::ES10,\n            Self::ES11,\n            Self::ES12,\n            Self::ES13,\n            Self::ES14,\n            Self::ES15,\n            Self::ES16,\n            Self::ESNext,\n        ]\n        .into_iter()\n    }\n}\n"
  },
  {
    "path": "tests/tester/src/exec/js262.rs",
    "content": "use std::{\n    cell::RefCell,\n    rc::Rc,\n    sync::mpsc::{self, Sender},\n    thread::JoinHandle,\n    time::Duration,\n};\n\n#[cfg(feature = \"annex-b\")]\nuse boa_engine::builtins::is_html_dda::IsHTMLDDA;\nuse boa_engine::{\n    Context, JsArgs, JsNativeError, JsResult, JsValue, Source,\n    builtins::array_buffer::{ArrayBuffer, SharedArrayBuffer},\n    js_string,\n    native_function::NativeFunction,\n    object::{JsObject, ObjectInitializer, builtins::JsSharedArrayBuffer},\n    property::Attribute,\n};\nuse bus::BusReader;\n\nuse crate::START;\n\npub(super) enum WorkerResult {\n    Ok,\n    Err(String),\n    Panic(String),\n}\n\npub(super) type WorkerHandle = JoinHandle<Result<(), String>>;\n\n#[derive(Debug, Clone)]\npub(super) struct WorkerHandles(Rc<RefCell<Vec<WorkerHandle>>>);\n\nimpl WorkerHandles {\n    pub(super) fn new() -> Self {\n        Self(Rc::default())\n    }\n\n    pub(super) fn join_all(&mut self) -> Vec<WorkerResult> {\n        let handles = std::mem::take(&mut *self.0.borrow_mut());\n\n        handles\n            .into_iter()\n            .map(|h| {\n                let result = h.join();\n\n                match result {\n                    Ok(Ok(())) => WorkerResult::Ok,\n                    Ok(Err(msg)) => {\n                        eprintln!(\"Detected error on worker thread: {msg}\");\n                        WorkerResult::Err(msg)\n                    }\n                    Err(e) => {\n                        let msg = e\n                            .downcast_ref::<&str>()\n                            .map(|&s| String::from(s))\n                            .unwrap_or_default();\n                        eprintln!(\"Detected panic on worker thread: {msg}\");\n                        WorkerResult::Panic(msg)\n                    }\n                }\n            })\n            .collect()\n    }\n}\n\nimpl Drop for WorkerHandles {\n    fn drop(&mut self) {\n        self.join_all();\n    }\n}\n\n/// Creates the object $262 in the context.\npub(super) fn register_js262(\n    handles: WorkerHandles,\n    console: bool,\n    context: &mut Context,\n) -> JsObject {\n    let global_obj = context.global_object();\n\n    let agent = agent_obj(handles, console, context);\n\n    let js262 = ObjectInitializer::new(context)\n        .function(\n            NativeFunction::from_fn_ptr(create_realm),\n            js_string!(\"createRealm\"),\n            0,\n        )\n        .function(\n            NativeFunction::from_fn_ptr(detach_array_buffer),\n            js_string!(\"detachArrayBuffer\"),\n            2,\n        )\n        .function(\n            NativeFunction::from_fn_ptr(eval_script),\n            js_string!(\"evalScript\"),\n            1,\n        )\n        .function(NativeFunction::from_fn_ptr(gc), js_string!(\"gc\"), 0)\n        .property(\n            js_string!(\"global\"),\n            global_obj,\n            Attribute::WRITABLE | Attribute::CONFIGURABLE,\n        )\n        .property(\n            js_string!(\"agent\"),\n            agent,\n            Attribute::WRITABLE | Attribute::CONFIGURABLE,\n        )\n        .build();\n\n    #[cfg(feature = \"annex-b\")]\n    js262\n        .create_data_property_or_throw(\n            js_string!(\"IsHTMLDDA\"),\n            JsObject::from_proto_and_data(None, IsHTMLDDA),\n            context,\n        )\n        .expect(\"the IsHTMLDDA property must be definable\");\n\n    context\n        .register_global_property(\n            js_string!(\"$262\"),\n            js262.clone(),\n            Attribute::WRITABLE | Attribute::CONFIGURABLE,\n        )\n        .expect(\"shouldn't fail with the default global\");\n\n    js262\n}\n\n/// The `$262.createRealm()` function.\n///\n/// Creates a new ECMAScript Realm, defines this API on the new realm's global object, and\n/// returns the `$262` property of the new realm's global object.\n#[allow(clippy::unnecessary_wraps)]\nfn create_realm(_: &JsValue, _: &[JsValue], context: &mut Context) -> JsResult<JsValue> {\n    let mut realm = context.create_realm()?;\n\n    realm = context.enter_realm(realm);\n    let js262 = register_js262(WorkerHandles::new(), false, context);\n    context.enter_realm(realm);\n\n    Ok(JsValue::new(js262))\n}\n\n/// The `$262.detachArrayBuffer()` function.\n///\n/// Implements the `DetachArrayBuffer` abstract operation.\nfn detach_array_buffer(_: &JsValue, args: &[JsValue], _: &mut Context) -> JsResult<JsValue> {\n    fn type_err() -> JsNativeError {\n        JsNativeError::typ().with_message(\"The provided object was not an ArrayBuffer\")\n    }\n\n    // 1. Assert: IsSharedArrayBuffer(arrayBuffer) is false.\n    let object = args.first().and_then(JsValue::as_object);\n    let mut array_buffer = object\n        .as_ref()\n        .and_then(|o| o.downcast_mut::<ArrayBuffer>())\n        .ok_or_else(type_err)?;\n\n    // 2. If key is not present, set key to undefined.\n    let key = args.get_or_undefined(1);\n\n    // 3. If SameValue(arrayBuffer.[[ArrayBufferDetachKey]], key) is false, throw a TypeError exception.\n    // 4. Set arrayBuffer.[[ArrayBufferData]] to null.\n    // 5. Set arrayBuffer.[[ArrayBufferByteLength]] to 0.\n    array_buffer.detach(key)?;\n\n    // 6. Return NormalCompletion(null).\n    Ok(JsValue::null())\n}\n\n/// The `$262.evalScript()` function.\n///\n/// Accepts a string value as its first argument and executes it as an ECMAScript script.\nfn eval_script(_this: &JsValue, args: &[JsValue], context: &mut Context) -> JsResult<JsValue> {\n    args.first().and_then(JsValue::as_string).map_or_else(\n        || Ok(JsValue::undefined()),\n        |source_text| context.eval(Source::from_bytes(&source_text.to_std_string_escaped())),\n    )\n}\n\n/// The `$262.gc()` function.\n///\n/// Wraps the host's garbage collection invocation mechanism, if such a capability exists.\n/// Must throw an exception if no capability exists. This is necessary for testing the\n/// semantics of any feature that relies on garbage collection, e.g. the `WeakRef` API.\n#[allow(clippy::unnecessary_wraps)]\nfn gc(_this: &JsValue, _: &[JsValue], _context: &mut Context) -> JsResult<JsValue> {\n    boa_gc::force_collect();\n    Ok(JsValue::undefined())\n}\n\n/// The `$262.agent.sleep()` function.\nfn sleep(_: &JsValue, args: &[JsValue], context: &mut Context) -> JsResult<JsValue> {\n    let ms = args.get_or_undefined(0).to_number(context)? / 1000.0;\n    std::thread::sleep(Duration::from_secs_f64(ms));\n    Ok(JsValue::undefined())\n}\n\n/// The `$262.agent.monotonicNow()` function.\n#[allow(clippy::unnecessary_wraps)]\nfn monotonic_now(_: &JsValue, _: &[JsValue], _: &mut Context) -> JsResult<JsValue> {\n    let clock = START\n        .get()\n        .ok_or_else(|| JsNativeError::typ().with_message(\"could not get the monotonic clock\"))?;\n    Ok(JsValue::from(clock.elapsed().as_millis() as f64))\n}\n\n/// Initializes the `$262.agent` object in the main agent.\nfn agent_obj(handles: WorkerHandles, console: bool, context: &mut Context) -> JsObject {\n    // TODO: improve initialization of this by using a `[[HostDefined]]` field on `Context`.\n    let bus = Rc::new(RefCell::new(bus::Bus::new(1)));\n\n    let (reports_tx, reports_rx) = mpsc::channel();\n\n    let start = unsafe {\n        let bus = bus.clone();\n        NativeFunction::from_closure(move |_, args, context| {\n            let script = args\n                .get_or_undefined(0)\n                .to_string(context)?\n                .to_std_string()\n                .map_err(|e| JsNativeError::typ().with_message(e.to_string()))?;\n\n            let rx = bus.borrow_mut().add_rx();\n            let tx = reports_tx.clone();\n\n            handles.0.borrow_mut().push(std::thread::spawn(move || {\n                let context = &mut Context::builder()\n                    .can_block(true)\n                    .build()\n                    .map_err(|e| e.to_string())?;\n                register_js262_worker(rx, tx, context);\n\n                if console {\n                    let console = boa_runtime::Console::init(context);\n\n                    context\n                        .register_global_property(\n                            boa_runtime::Console::NAME,\n                            console,\n                            Attribute::all(),\n                        )\n                        .expect(\"the console builtin shouldn't exist\");\n                }\n\n                let src = Source::from_bytes(&script);\n\n                context.eval(src).map_err(|e| e.to_string())?;\n                context.run_jobs().map_err(|e| e.to_string())?;\n\n                Ok(())\n            }));\n\n            Ok(JsValue::undefined())\n        })\n    };\n\n    let broadcast = unsafe {\n        // should technically also have a second numeric argument, but the test262 never uses it.\n        NativeFunction::from_closure(move |_, args, _| {\n            let buffer = args.get_or_undefined(0).as_object().ok_or_else(|| {\n                JsNativeError::typ().with_message(\"argument was not a shared array\")\n            })?;\n            let buffer = buffer\n                .downcast_ref::<SharedArrayBuffer>()\n                .ok_or_else(|| {\n                    JsNativeError::typ().with_message(\"argument was not a shared array\")\n                })?\n                .clone();\n\n            bus.borrow_mut().broadcast(buffer);\n\n            Ok(JsValue::undefined())\n        })\n    };\n\n    let get_report = unsafe {\n        NativeFunction::from_closure(move |_, _, _| {\n            let Ok(msg) = reports_rx.try_recv() else {\n                return Ok(JsValue::null());\n            };\n\n            Ok(js_string!(&msg[..]).into())\n        })\n    };\n\n    ObjectInitializer::new(context)\n        .function(start, js_string!(\"start\"), 1)\n        .function(broadcast, js_string!(\"broadcast\"), 2)\n        .function(get_report, js_string!(\"getReport\"), 0)\n        .function(NativeFunction::from_fn_ptr(sleep), js_string!(\"sleep\"), 1)\n        .function(\n            NativeFunction::from_fn_ptr(monotonic_now),\n            js_string!(\"monotonicNow\"),\n            0,\n        )\n        .build()\n}\n\n/// Initializes the `$262` object in a worker agent.\nfn register_js262_worker(\n    rx: BusReader<SharedArrayBuffer>,\n    tx: Sender<Vec<u16>>,\n    context: &mut Context,\n) {\n    let rx = RefCell::new(rx);\n    let receive_broadcast = unsafe {\n        // should technically also have a second numeric argument, but the test262 never uses it.\n        NativeFunction::from_closure(move |_, args, context| {\n            let array = rx.borrow_mut().recv().map_err(|err| {\n                JsNativeError::typ().with_message(format!(\"failed to receive buffer: {err}\"))\n            })?;\n\n            let callable = args\n                .get_or_undefined(0)\n                .as_callable()\n                .ok_or_else(|| JsNativeError::typ().with_message(\"argument is not callable\"))?;\n\n            let buffer = JsSharedArrayBuffer::from_buffer(array, context);\n            callable.call(&JsValue::undefined(), &[buffer.into()], context)\n        })\n    };\n\n    let report = unsafe {\n        NativeFunction::from_closure(move |_, args, context| {\n            let string = args.get_or_undefined(0).to_string(context)?.to_vec();\n            tx.send(string)\n                .map_err(|e| JsNativeError::typ().with_message(e.to_string()))?;\n            Ok(JsValue::undefined())\n        })\n    };\n\n    let agent = ObjectInitializer::new(context)\n        .function(receive_broadcast, js_string!(\"receiveBroadcast\"), 1)\n        .function(report, js_string!(\"report\"), 1)\n        .function(NativeFunction::from_fn_ptr(sleep), js_string!(\"sleep\"), 1)\n        // Don't need to signal leaving, the main thread will join with the worker\n        // threads anyways.\n        .function(\n            NativeFunction::from_fn_ptr(|_, _, _| Ok(JsValue::undefined())),\n            js_string!(\"leaving\"),\n            0,\n        )\n        .function(\n            NativeFunction::from_fn_ptr(monotonic_now),\n            js_string!(\"monotonicNow\"),\n            0,\n        )\n        .build();\n\n    let js262 = ObjectInitializer::new(context)\n        .property(\n            js_string!(\"agent\"),\n            agent,\n            Attribute::WRITABLE | Attribute::CONFIGURABLE,\n        )\n        .build();\n\n    context\n        .register_global_property(\n            js_string!(\"$262\"),\n            js262,\n            Attribute::WRITABLE | Attribute::CONFIGURABLE,\n        )\n        .expect(\"shouldn't fail with the default global\");\n}\n"
  },
  {
    "path": "tests/tester/src/exec/mod.rs",
    "content": "//! Execution module for the test runner.\n\nmod js262;\n\nuse crate::{\n    Harness, Outcome, Phase, SpecEdition, Statistics, SuiteResult, Test, TestFlags,\n    TestOutcomeResult, TestResult, TestSuite, VersionedStats, read::ErrorType,\n};\nuse boa_engine::{\n    Context, JsArgs, JsError, JsNativeErrorKind, JsResult, JsValue, Source,\n    builtins::promise::PromiseState,\n    js_str,\n    module::{Module, SimpleModuleLoader},\n    native_function::NativeFunction,\n    object::FunctionObjectBuilder,\n    optimizer::OptimizerOptions,\n    parser::source::ReadChar,\n    property::Attribute,\n    script::Script,\n};\nuse colored::Colorize;\nuse rayon::prelude::*;\nuse rustc_hash::FxHashSet;\nuse std::{cell::RefCell, eprintln, path::Path, rc::Rc};\n\nuse self::js262::WorkerHandles;\n\nimpl TestSuite {\n    /// Runs the test suite.\n    pub(crate) fn run(\n        &self,\n        harness: &Harness,\n        verbose: u8,\n        parallel: bool,\n        max_edition: SpecEdition,\n        optimizer_options: OptimizerOptions,\n        console: bool,\n    ) -> SuiteResult {\n        if verbose != 0 {\n            println!(\"Suite {}:\", self.path.display());\n        }\n\n        let suites: Vec<_> = if parallel {\n            self.suites\n                .par_iter()\n                .map(|suite| {\n                    suite.run(\n                        harness,\n                        verbose,\n                        parallel,\n                        max_edition,\n                        optimizer_options,\n                        console,\n                    )\n                })\n                .collect()\n        } else {\n            self.suites\n                .iter()\n                .map(|suite| {\n                    suite.run(\n                        harness,\n                        verbose,\n                        parallel,\n                        max_edition,\n                        optimizer_options,\n                        console,\n                    )\n                })\n                .collect()\n        };\n\n        let tests: Vec<_> = if parallel {\n            self.tests\n                .par_iter()\n                .filter(|test| test.edition <= max_edition)\n                .map(|test| test.run(harness, verbose, optimizer_options, console))\n                .collect()\n        } else {\n            self.tests\n                .iter()\n                .filter(|test| test.edition <= max_edition)\n                .map(|test| test.run(harness, verbose, optimizer_options, console))\n                .collect()\n        };\n\n        let mut features = FxHashSet::default();\n        for test_iter in &*self.tests {\n            features.extend(test_iter.features.iter().map(ToString::to_string));\n        }\n\n        if verbose != 0 {\n            println!();\n        }\n\n        // Count passed tests and es specs\n        let mut versioned_stats = VersionedStats::default();\n        let mut es_next = Statistics::default();\n\n        for test in &tests {\n            match test.result {\n                TestOutcomeResult::Passed => {\n                    versioned_stats.apply(test.edition, |stats| {\n                        stats.passed += 1;\n                    });\n                    es_next.passed += 1;\n                }\n                TestOutcomeResult::Ignored => {\n                    versioned_stats.apply(test.edition, |stats| {\n                        stats.ignored += 1;\n                    });\n                    es_next.ignored += 1;\n                }\n                TestOutcomeResult::Panic => {\n                    versioned_stats.apply(test.edition, |stats| {\n                        stats.panic += 1;\n                    });\n                    es_next.panic += 1;\n                }\n                TestOutcomeResult::Failed => {}\n            }\n            versioned_stats.apply(test.edition, |stats| {\n                stats.total += 1;\n            });\n            es_next.total += 1;\n        }\n\n        // Count total tests\n        for suite in &suites {\n            versioned_stats += suite.versioned_stats;\n            es_next += suite.stats;\n            features.extend(suite.features.iter().cloned());\n        }\n\n        if verbose != 0 {\n            println!(\n                \"Suite {} results: total: {}, passed: {}, ignored: {}, failed: {} {}, conformance: {:.2}%\",\n                self.path.display(),\n                es_next.total,\n                es_next.passed.to_string().green(),\n                es_next.ignored.to_string().yellow(),\n                (es_next.total - es_next.passed - es_next.ignored)\n                    .to_string()\n                    .red(),\n                if es_next.panic == 0 {\n                    String::new()\n                } else {\n                    format!(\"({})\", format!(\"{} panics\", es_next.panic).red())\n                },\n                (es_next.passed as f64 / es_next.total as f64) * 100.0\n            );\n        }\n        SuiteResult {\n            name: self.name.clone(),\n            stats: es_next,\n            versioned_stats,\n            suites,\n            tests,\n            features,\n        }\n    }\n}\n\nimpl Test {\n    /// Runs the test.\n    pub(crate) fn run(\n        &self,\n        harness: &Harness,\n        verbose: u8,\n        optimizer_options: OptimizerOptions,\n        console: bool,\n    ) -> TestResult {\n        if self.flags.contains(TestFlags::MODULE) || self.flags.contains(TestFlags::RAW) {\n            return self.run_once(harness, false, verbose, optimizer_options, console);\n        }\n\n        if self\n            .flags\n            .contains(TestFlags::STRICT | TestFlags::NO_STRICT)\n        {\n            let r = self.run_once(harness, false, verbose, optimizer_options, console);\n            if r.result != TestOutcomeResult::Passed {\n                return r;\n            }\n            self.run_once(harness, true, verbose, optimizer_options, console)\n        } else {\n            self.run_once(\n                harness,\n                self.flags.contains(TestFlags::STRICT),\n                verbose,\n                optimizer_options,\n                console,\n            )\n        }\n    }\n\n    /// Creates the test result from the outcome and message.\n    fn create_result<S: Into<Box<str>>>(\n        &self,\n        outcome: TestOutcomeResult,\n        text: S,\n        strict: bool,\n        verbosity: u8,\n    ) -> TestResult {\n        let result_text = text.into();\n\n        if verbosity > 1 {\n            println!(\n                \"`{}`{}: {}\",\n                self.path.display(),\n                if strict { \" (strict)\" } else { \"\" },\n                match outcome {\n                    TestOutcomeResult::Passed => \"Passed\".green(),\n                    TestOutcomeResult::Ignored => \"Ignored\".yellow(),\n                    TestOutcomeResult::Failed => \"Failed\".red(),\n                    TestOutcomeResult::Panic => \"⚠ Panic ⚠\".red(),\n                }\n            );\n        } else {\n            let symbol = match outcome {\n                TestOutcomeResult::Passed => \".\".green(),\n                TestOutcomeResult::Ignored => \"-\".yellow(),\n                TestOutcomeResult::Failed | TestOutcomeResult::Panic => \"F\".red(),\n            };\n\n            print!(\"{symbol}\");\n        }\n\n        if verbosity > 2 {\n            println!(\n                \"`{}`{}: result text\\n{result_text}\\n\",\n                self.path.display(),\n                if strict { \" (strict)\" } else { \"\" },\n            );\n        }\n\n        TestResult {\n            name: self.name.clone(),\n            edition: self.edition,\n            result_text,\n            result: outcome,\n        }\n    }\n\n    /// Runs the test once, in strict or non-strict mode\n    fn run_once(\n        &self,\n        harness: &Harness,\n        strict: bool,\n        verbosity: u8,\n        optimizer_options: OptimizerOptions,\n        console: bool,\n    ) -> TestResult {\n        let Ok(source) = Source::from_filepath(&self.path) else {\n            return self.create_result(\n                TestOutcomeResult::Failed,\n                \"Could not read test file\",\n                strict,\n                verbosity,\n            );\n        };\n\n        if self.ignored {\n            return self.create_result(TestOutcomeResult::Ignored, \"\", strict, verbosity);\n        }\n\n        if verbosity > 1 {\n            println!(\n                \"`{}`{}: starting\",\n                self.path.display(),\n                if strict { \" (strict mode)\" } else { \"\" }\n            );\n        }\n\n        let result = std::panic::catch_unwind(|| match self.expected_outcome {\n            Outcome::Positive => {\n                let (ref mut context, async_result, mut handles) =\n                    match self.create_context(harness, optimizer_options, console) {\n                        Ok(r) => r,\n                        Err(e) => return (false, e),\n                    };\n\n                // TODO: timeout\n                let value = if self.is_module() {\n                    let module = match parse_module_and_register(source, &self.path, context) {\n                        Ok(module) => module,\n                        Err(err) => return (false, format!(\"Uncaught {err}\")),\n                    };\n\n                    let promise = module.load_link_evaluate(context);\n\n                    if let Err(err) = context.run_jobs() {\n                        return (false, format!(\"Uncaught {err}\"));\n                    }\n\n                    match promise.state() {\n                        PromiseState::Pending => {\n                            return (false, \"module should have been executed\".to_string());\n                        }\n                        PromiseState::Fulfilled(v) => v,\n                        PromiseState::Rejected(err) => {\n                            let output = JsError::from_opaque(err.clone())\n                                .try_native(context)\n                                .map_or_else(\n                                    |_| format!(\"Uncaught {}\", err.display()),\n                                    |err| {\n                                        format!(\n                                            \"Uncaught {err}{}\",\n                                            err.cause().map_or_else(String::new, |cause| format!(\n                                                \"\\n  caused by {cause}\"\n                                            ))\n                                        )\n                                    },\n                                );\n\n                            return (false, output);\n                        }\n                    }\n                } else {\n                    context.strict(strict);\n                    match context.eval(source) {\n                        Ok(v) => v,\n                        Err(err) => return (false, format!(\"Uncaught {err}\")),\n                    }\n                };\n\n                if let Err(err) = context.run_jobs() {\n                    return (false, format!(\"Uncaught {err}\"));\n                }\n\n                match *async_result.inner.borrow() {\n                    UninitResult::Err(ref e) => return (false, format!(\"Uncaught {e}\")),\n                    UninitResult::Uninit if self.flags.contains(TestFlags::ASYNC) => {\n                        return (\n                            false,\n                            \"async test did not print \\\"Test262:AsyncTestComplete\\\"\".to_string(),\n                        );\n                    }\n                    _ => {}\n                }\n\n                for result in handles.join_all() {\n                    match result {\n                        js262::WorkerResult::Err(msg) => return (false, msg),\n                        js262::WorkerResult::Panic(msg) => panic!(\"Worker thread panicked: {msg}\"),\n                        js262::WorkerResult::Ok => {}\n                    }\n                }\n\n                (true, value.display().to_string())\n            }\n            Outcome::Negative {\n                phase: Phase::Parse,\n                error_type,\n            } => {\n                assert_eq!(\n                    error_type,\n                    ErrorType::SyntaxError,\n                    \"non-SyntaxError parsing/early error found in {}\",\n                    self.path.display()\n                );\n\n                let context = &mut Context::default();\n\n                if self.is_module() {\n                    match Module::parse(source, None, context) {\n                        Ok(_) => (false, \"ModuleItemList parsing should fail\".to_owned()),\n                        Err(e) => (true, format!(\"Uncaught {e}\")),\n                    }\n                } else {\n                    context.strict(strict);\n                    match Script::parse(source, None, context) {\n                        Ok(_) => (false, \"StatementList parsing should fail\".to_owned()),\n                        Err(e) => (true, format!(\"Uncaught {e}\")),\n                    }\n                }\n            }\n            Outcome::Negative {\n                phase: Phase::Resolution,\n                error_type,\n            } => {\n                let context = &mut match self.create_context(harness, optimizer_options, console) {\n                    Ok(r) => r,\n                    Err(e) => return (false, e),\n                }\n                .0;\n\n                let module = match parse_module_and_register(source, &self.path, context) {\n                    Ok(module) => module,\n                    Err(err) => return (false, format!(\"Uncaught {err}\")),\n                };\n\n                let promise = module.load(context);\n\n                if let Err(err) = context.run_jobs() {\n                    return (false, format!(\"Uncaught {err}\"));\n                }\n\n                match promise.state() {\n                    PromiseState::Pending => {\n                        return (false, \"module didn't try to load\".to_string());\n                    }\n                    PromiseState::Fulfilled(_) => {\n                        // Try to link to see if the resolution error shows there.\n                    }\n                    PromiseState::Rejected(err) => {\n                        let err = JsError::from_opaque(err);\n                        return (\n                            is_error_type(&err, error_type, context),\n                            format!(\"Uncaught {err}\"),\n                        );\n                    }\n                }\n\n                if let Err(err) = module.link(context) {\n                    (\n                        is_error_type(&err, error_type, context),\n                        format!(\"Uncaught {err}\"),\n                    )\n                } else {\n                    (false, \"module resolution didn't fail\".to_string())\n                }\n            }\n            Outcome::Negative {\n                phase: Phase::Runtime,\n                error_type,\n            } => {\n                let (ref mut context, _async_result, mut handles) =\n                    match self.create_context(harness, optimizer_options, console) {\n                        Ok(r) => r,\n                        Err(e) => return (false, e),\n                    };\n\n                let error = if self.is_module() {\n                    let module = match parse_module_and_register(source, &self.path, context) {\n                        Ok(module) => module,\n                        Err(err) => return (false, format!(\"Uncaught {err}\")),\n                    };\n\n                    let promise = module.load(context);\n\n                    if let Err(err) = context.run_jobs() {\n                        return (false, format!(\"Uncaught {err}\"));\n                    }\n\n                    match promise.state() {\n                        PromiseState::Pending => {\n                            return (false, \"module didn't try to load\".to_string());\n                        }\n                        PromiseState::Fulfilled(_) => {}\n                        PromiseState::Rejected(err) => {\n                            return (false, format!(\"Uncaught {}\", err.display()));\n                        }\n                    }\n\n                    if let Err(err) = module.link(context) {\n                        return (false, format!(\"Uncaught {err}\"));\n                    }\n\n                    let promise = match module.evaluate(context) {\n                        Ok(p) => p,\n                        Err(err) => return (false, format!(\"Uncaught {err}\")),\n                    };\n\n                    if let Err(err) = context.run_jobs() {\n                        return (false, format!(\"Uncaught {err}\"));\n                    }\n\n                    match promise.state() {\n                        PromiseState::Pending => {\n                            return (false, \"module didn't try to evaluate\".to_string());\n                        }\n                        PromiseState::Fulfilled(val) => return (false, val.display().to_string()),\n                        PromiseState::Rejected(err) => JsError::from_opaque(err),\n                    }\n                } else {\n                    context.strict(strict);\n                    let script = match Script::parse(source, None, context) {\n                        Ok(code) => code,\n                        Err(e) => return (false, format!(\"Uncaught {e}\")),\n                    };\n\n                    match script.evaluate(context) {\n                        Ok(_) => return (false, \"Script execution should fail\".to_owned()),\n                        Err(e) => e,\n                    }\n                };\n\n                for result in handles.join_all() {\n                    match result {\n                        js262::WorkerResult::Err(msg) => return (false, msg),\n                        js262::WorkerResult::Panic(msg) => panic!(\"Worker thread panicked: {msg}\"),\n                        js262::WorkerResult::Ok => {}\n                    }\n                }\n\n                (\n                    is_error_type(&error, error_type, context),\n                    format!(\"Uncaught {error}\"),\n                )\n            }\n        });\n\n        let (result, result_text) = result.map_or_else(\n            |_| {\n                eprintln!(\"last panic was on test \\\"{}\\\"\", self.path.display());\n                (TestOutcomeResult::Panic, String::new())\n            },\n            |(res, text)| {\n                if res {\n                    (TestOutcomeResult::Passed, text)\n                } else {\n                    (TestOutcomeResult::Failed, text)\n                }\n            },\n        );\n\n        self.create_result(result, result_text, strict, verbosity)\n    }\n\n    /// Creates the context to run the test.\n    fn create_context(\n        &self,\n        harness: &Harness,\n        optimizer_options: OptimizerOptions,\n        console: bool,\n    ) -> Result<(Context, AsyncResult, WorkerHandles), String> {\n        let async_result = AsyncResult::default();\n        let handles = WorkerHandles::new();\n        let loader = Rc::new(\n            SimpleModuleLoader::new(self.path.parent().expect(\"test should have a parent dir\"))\n                .expect(\"test path should be canonicalizable\"),\n        );\n        let mut context = Context::builder()\n            .module_loader(loader.clone())\n            .can_block(!self.flags.contains(TestFlags::CAN_BLOCK_IS_FALSE))\n            .build()\n            .expect(\"cannot fail with default global object\");\n\n        context.set_optimizer_options(optimizer_options);\n\n        // Register the print() function.\n        register_print_fn(&mut context, async_result.clone());\n\n        // add the $262 object.\n        let _js262 = js262::register_js262(handles.clone(), console, &mut context);\n\n        if console {\n            let console = boa_runtime::Console::init(&mut context);\n            context\n                .register_global_property(boa_runtime::Console::NAME, console, Attribute::all())\n                .expect(\"the console builtin shouldn't exist\");\n        }\n\n        if self.flags.contains(TestFlags::RAW) {\n            return Ok((context, async_result, handles));\n        }\n\n        let assert = Source::from_reader(\n            harness.assert.content.as_bytes(),\n            Some(&harness.assert.path),\n        );\n        let sta = Source::from_reader(harness.sta.content.as_bytes(), Some(&harness.sta.path));\n\n        context\n            .eval(assert)\n            .map_err(|e| format!(\"could not run assert.js:\\n{e}\"))?;\n        context\n            .eval(sta)\n            .map_err(|e| format!(\"could not run sta.js:\\n{e}\"))?;\n\n        if self.flags.contains(TestFlags::ASYNC) {\n            let dph = Source::from_reader(\n                harness.doneprint_handle.content.as_bytes(),\n                Some(&harness.doneprint_handle.path),\n            );\n            context\n                .eval(dph)\n                .map_err(|e| format!(\"could not run doneprintHandle.js:\\n{e}\"))?;\n        }\n\n        for include_name in &self.includes {\n            let include = harness\n                .includes\n                .get(include_name)\n                .ok_or_else(|| format!(\"could not find the {include_name} include file.\"))?;\n            let source = Source::from_reader(include.content.as_bytes(), Some(&include.path));\n            context.eval(source).map_err(|e| {\n                format!(\"could not run the harness `{include_name}`:\\nUncaught {e}\",)\n            })?;\n        }\n\n        Ok((context, async_result, handles))\n    }\n}\n\n/// Returns `true` if `error` is a `target_type` error.\nfn is_error_type(error: &JsError, target_type: ErrorType, context: &mut Context) -> bool {\n    if let Ok(error) = error.try_native(context) {\n        match error.kind() {\n            JsNativeErrorKind::Syntax if target_type == ErrorType::SyntaxError => {}\n            JsNativeErrorKind::Reference if target_type == ErrorType::ReferenceError => {}\n            JsNativeErrorKind::Range if target_type == ErrorType::RangeError => {}\n            JsNativeErrorKind::Type if target_type == ErrorType::TypeError => {}\n            _ => return false,\n        }\n        true\n    } else {\n        error\n            .as_opaque()\n            .expect(\"try_native cannot fail if e is not opaque\")\n            .as_object()\n            .and_then(|o| o.get(js_str!(\"constructor\"), context).ok())\n            .as_ref()\n            .and_then(JsValue::as_object)\n            .and_then(|o| o.get(js_str!(\"name\"), context).ok())\n            .as_ref()\n            .and_then(JsValue::as_string)\n            .is_some_and(|s| s == target_type.as_str())\n    }\n}\n\n/// Registers the print function in the context.\nfn register_print_fn(context: &mut Context, async_result: AsyncResult) {\n    // We use `FunctionBuilder` to define a closure with additional captures.\n    let js_function = FunctionObjectBuilder::new(\n        context.realm(),\n        // SAFETY: `AsyncResult` has only non-traceable captures, making this safe.\n        unsafe {\n            NativeFunction::from_closure(move |_, args, context| {\n                let message = args\n                    .get_or_undefined(0)\n                    .to_string(context)?\n                    .to_std_string_escaped();\n                let mut result = async_result.inner.borrow_mut();\n\n                match *result {\n                    UninitResult::Uninit | UninitResult::Ok(()) => {\n                        if message == \"Test262:AsyncTestComplete\" {\n                            *result = UninitResult::Ok(());\n                        } else {\n                            *result = UninitResult::Err(message);\n                        }\n                    }\n                    UninitResult::Err(_) => {}\n                }\n\n                Ok(JsValue::undefined())\n            })\n        },\n    )\n    .name(\"print\")\n    .length(1)\n    .build();\n\n    context\n        .register_global_property(\n            js_str!(\"print\"),\n            js_function,\n            Attribute::WRITABLE | Attribute::NON_ENUMERABLE | Attribute::CONFIGURABLE,\n        )\n        .expect(\"shouldn't fail with the default global\");\n}\n\n/// Parses a module and registers it into the `ModuleLoader` of the context.\nfn parse_module_and_register(\n    source: Source<'_, impl ReadChar>,\n    path: &Path,\n    context: &mut Context,\n) -> JsResult<Module> {\n    let module = Module::parse(source, None, context)?;\n\n    let path = path\n        .canonicalize()\n        .expect(\"test path should be canonicalizable\");\n\n    let loader = context\n        .downcast_module_loader::<SimpleModuleLoader>()\n        .expect(\"context must use a SimpleModuleLoader\");\n\n    loader.insert(path, module.clone());\n\n    Ok(module)\n}\n\n/// A `Result` value that is possibly uninitialized.\n///\n/// This is mainly used to check if an async test did call `print` to signal the termination of\n/// a test. Otherwise, all async tests that result in `UninitResult::Uninit` are considered\n/// failures.\n///\n/// The Test262 [interpreting guide][guide] contains more information about how to run async tests.\n///\n/// [guide]: https://github.com/tc39/test262/blob/main/INTERPRETING.md#flags\n#[derive(Debug, Clone, Copy, Default)]\nenum UninitResult<T, E> {\n    #[default]\n    Uninit,\n    Ok(T),\n    Err(E),\n}\n\n/// Object which includes the result of the async operation.\n#[derive(Debug, Clone)]\nstruct AsyncResult {\n    inner: Rc<RefCell<UninitResult<(), String>>>,\n}\n\nimpl Default for AsyncResult {\n    #[inline]\n    fn default() -> Self {\n        Self {\n            inner: Rc::new(RefCell::new(UninitResult::default())),\n        }\n    }\n}\n"
  },
  {
    "path": "tests/tester/src/main.rs",
    "content": "//! Test262 test runner\n//!\n//! This crate will run the full ECMAScript test suite (Test262) and report compliance of the\n//! `boa` engine.\n#![cfg_attr(not(test), deny(clippy::unwrap_used))]\n#![allow(\n    clippy::too_many_lines,\n    clippy::redundant_pub_crate,\n    clippy::cast_precision_loss,\n    clippy::print_stderr,\n    clippy::print_stdout\n)]\n\nuse std::{\n    ops::{Add, AddAssign},\n    path::{Path, PathBuf},\n    process::Command,\n    sync::OnceLock,\n    time::Instant,\n};\n\nuse bitflags::bitflags;\nuse clap::{ArgAction, Parser, ValueHint};\nuse color_eyre::{\n    Result,\n    eyre::{WrapErr, bail, eyre},\n};\nuse colored::Colorize;\nuse rustc_hash::{FxHashMap, FxHashSet};\nuse serde::{\n    Deserialize, Deserializer, Serialize,\n    de::{Unexpected, Visitor},\n};\n\nuse boa_engine::optimizer::OptimizerOptions;\nuse edition::SpecEdition;\nuse read::ErrorType;\n\nuse self::{\n    read::{MetaData, Negative, TestFlag, read_harness, read_suite, read_test},\n    results::{compare_results, write_json},\n};\n\nmod edition;\nmod exec;\nmod read;\nmod results;\n\nstatic START: OnceLock<Instant> = OnceLock::new();\n\n/// Structure that contains the configuration of the tester.\n#[derive(Debug, Deserialize)]\nstruct Config {\n    #[serde(default)]\n    commit: String,\n    #[serde(default)]\n    ignored: Ignored,\n}\n\nimpl Config {\n    /// Get the `Test262` repository commit.\n    pub(crate) fn commit(&self) -> &str {\n        &self.commit\n    }\n\n    /// Get [`Ignored`] `Test262` tests and features.\n    pub(crate) const fn ignored(&self) -> &Ignored {\n        &self.ignored\n    }\n}\n\n/// Structure to allow defining ignored tests, features and files that should\n/// be ignored even when reading.\n#[derive(Default, Debug, Deserialize)]\nstruct Ignored {\n    #[serde(default)]\n    tests: FxHashSet<Box<str>>,\n    #[serde(default)]\n    features: FxHashSet<Box<str>>,\n    #[serde(default = \"TestFlags::empty\")]\n    flags: TestFlags,\n}\n\nimpl Ignored {\n    /// Checks if the ignore list contains the given test in the list of\n    /// tests to ignore.\n    pub(crate) fn contains_test(&self, test_path: &Path) -> bool {\n        let Some(test_path) = test_path.to_str() else {\n            return false;\n        };\n        self.tests\n            .iter()\n            .any(|ignored| test_path.contains(&**ignored))\n    }\n\n    /// Checks if the ignore list contains the given feature name in the list\n    /// of features to ignore.\n    pub(crate) fn contains_feature(&self, feature: &str) -> bool {\n        if self.features.contains(feature) {\n            return true;\n        }\n        // Some features are an accessor instead of a simple feature name e.g. `Intl.DurationFormat`.\n        // This ensures those are also ignored.\n        feature\n            .split('.')\n            .next()\n            .is_some_and(|feat| self.features.contains(feat))\n    }\n\n    pub(crate) const fn contains_any_flag(&self, flags: TestFlags) -> bool {\n        flags.intersects(self.flags)\n    }\n}\n\n/// Boa test262 tester\n#[derive(Debug, Parser)]\n#[command(author, version, about, name = \"Boa test262 tester\")]\nenum Cli {\n    /// Run the test suite.\n    Run {\n        /// Whether to show verbose output.\n        #[arg(short, long, action = ArgAction::Count)]\n        verbose: u8,\n\n        /// Path to the Test262 suite.\n        #[arg(\n            long,\n            value_hint = ValueHint::DirPath,\n            conflicts_with = \"test262_commit\"\n        )]\n        test262_path: Option<PathBuf>,\n\n        /// Override config's Test262 commit. To checkout the latest commit set this to \"latest\".\n        #[arg(long)]\n        test262_commit: Option<String>,\n\n        /// Which specific test or test suite to run. Should be a path relative to the Test262 directory: e.g. \"test/language/types/number\"\n        #[arg(short, long, default_value = \"test\", value_hint = ValueHint::AnyPath)]\n        suite: PathBuf,\n\n        /// Enable optimizations\n        #[arg(long, short = 'O')]\n        optimize: bool,\n\n        /// Optional output folder for the full results information.\n        #[arg(short, long, value_hint = ValueHint::DirPath)]\n        output: Option<PathBuf>,\n\n        /// Execute tests serially\n        #[arg(short, long)]\n        disable_parallelism: bool,\n\n        /// Path to a TOML file containing tester config.\n        #[arg(short, long, default_value = \"test262_config.toml\", value_hint = ValueHint::FilePath)]\n        config: PathBuf,\n\n        /// Maximum ECMAScript edition to test for.\n        #[arg(long)]\n        edition: Option<SpecEdition>,\n\n        /// Displays the conformance results per ECMAScript edition.\n        #[arg(long)]\n        versioned: bool,\n\n        /// Injects the `Console` object into every context created.\n        #[arg(long)]\n        console: bool,\n    },\n    /// Compare two test suite results.\n    Compare {\n        /// Base results of the suite.\n        #[arg(value_hint = ValueHint::FilePath)]\n        base: PathBuf,\n\n        /// New results to compare.\n        #[arg(value_hint = ValueHint::FilePath)]\n        new: PathBuf,\n\n        /// Whether to use markdown output\n        #[arg(short, long)]\n        markdown: bool,\n    },\n}\n\nconst DEFAULT_TEST262_DIRECTORY: &str = \"test262\";\n\n/// Program entry point.\nfn main() -> Result<()> {\n    color_eyre::install()?;\n\n    // initializes the monotonic clock.\n    START\n        .set(Instant::now())\n        .map_err(|_| eyre!(\"could not initialize the monotonic clock\"))?;\n\n    match Cli::parse() {\n        Cli::Run {\n            verbose,\n            test262_path,\n            test262_commit,\n            suite,\n            output,\n            optimize,\n            disable_parallelism,\n            config: config_path,\n            edition,\n            versioned,\n            console,\n        } => {\n            let config: Config = {\n                let input = std::fs::read_to_string(&config_path).wrap_err_with(|| {\n                    eyre!(\"could not read config file `{}`\", config_path.display())\n                })?;\n                toml::from_str(&input)\n                    .wrap_err_with(|| eyre!(\"invalid config file `{}`\", config_path.display()))?\n            };\n\n            let test262_commit = test262_commit\n                .as_deref()\n                .or_else(|| Some(config.commit()))\n                .filter(|s| ![\"\", \"latest\"].contains(s));\n\n            let test262_path = if let Some(path) = test262_path.as_deref() {\n                path\n            } else {\n                clone_test262(test262_commit, verbose)?;\n\n                Path::new(DEFAULT_TEST262_DIRECTORY)\n            }\n            .canonicalize();\n            let test262_path = &test262_path.wrap_err(\"could not get the Test262 path\")?;\n\n            run_test_suite(\n                &config,\n                verbose,\n                !disable_parallelism,\n                test262_path,\n                suite.as_path(),\n                output.as_deref(),\n                edition.unwrap_or_default(),\n                versioned,\n                if optimize {\n                    OptimizerOptions::OPTIMIZE_ALL\n                } else {\n                    OptimizerOptions::empty()\n                },\n                console,\n            )\n        }\n        Cli::Compare {\n            base,\n            new,\n            markdown,\n        } => compare_results(base.as_path(), new.as_path(), markdown),\n    }\n}\n\n/// Returns the commit hash and commit message of the provided branch name.\nfn get_last_branch_commit(branch: &str, verbose: u8) -> Result<(String, String)> {\n    if verbose > 1 {\n        println!(\"Getting last commit on '{branch}' branch\");\n    }\n    let result = Command::new(\"git\")\n        .arg(\"log\")\n        .args([\"-n\", \"1\"])\n        .arg(\"--pretty=format:%H %s\")\n        .arg(branch)\n        .current_dir(DEFAULT_TEST262_DIRECTORY)\n        .output()?;\n\n    if !result.status.success() {\n        bail!(\n            \"test262 getting commit hash and message failed with return code {:?}\",\n            result.status.code()\n        );\n    }\n\n    let output = std::str::from_utf8(&result.stdout)?.trim();\n\n    let (hash, message) = output\n        .split_once(' ')\n        .expect(\"git log output to contain hash and message\");\n\n    Ok((hash.into(), message.into()))\n}\n\nfn reset_test262_commit(commit: &str, verbose: u8) -> Result<()> {\n    if verbose != 0 {\n        println!(\"Reset test262 to commit: {commit}...\");\n    }\n\n    let result = Command::new(\"git\")\n        .arg(\"reset\")\n        .arg(\"--hard\")\n        .arg(commit)\n        .current_dir(DEFAULT_TEST262_DIRECTORY)\n        .status()?;\n\n    if !result.success() {\n        bail!(\n            \"test262 commit {commit} checkout failed with return code: {:?}\",\n            result.code()\n        );\n    }\n\n    Ok(())\n}\n\nfn clone_test262(commit: Option<&str>, verbose: u8) -> Result<()> {\n    const TEST262_REPOSITORY: &str = \"https://github.com/tc39/test262\";\n\n    let update = commit.is_none();\n\n    if Path::new(DEFAULT_TEST262_DIRECTORY).is_dir() {\n        let (current_commit_hash, current_commit_message) =\n            get_last_branch_commit(\"HEAD\", verbose)?;\n\n        if let Some(commit) = commit\n            && current_commit_hash == commit\n        {\n            return Ok(());\n        }\n\n        if verbose != 0 {\n            println!(\"Fetching latest test262 commits...\");\n        }\n        let result = Command::new(\"git\")\n            .arg(\"fetch\")\n            .current_dir(DEFAULT_TEST262_DIRECTORY)\n            .status()?;\n\n        if !result.success() {\n            bail!(\n                \"Test262 fetching latest failed with return code {:?}\",\n                result.code()\n            );\n        }\n\n        if let Some(commit) = commit {\n            println!(\"Test262 switching to commit {commit}...\");\n            reset_test262_commit(commit, verbose)?;\n            return Ok(());\n        }\n\n        if verbose != 0 {\n            println!(\"Checking latest Test262 with current HEAD...\");\n        }\n        let (latest_commit_hash, latest_commit_message) =\n            get_last_branch_commit(\"origin/main\", verbose)?;\n\n        if current_commit_hash != latest_commit_hash {\n            if update {\n                println!(\"Updating Test262 repository:\");\n            } else {\n                println!(\n                    \"Warning Test262 repository is not in sync, use '--test262-commit latest' to automatically update it:\"\n                );\n            }\n\n            println!(\"    Current commit: {current_commit_hash} {current_commit_message}\");\n            println!(\"    Latest commit:  {latest_commit_hash} {latest_commit_message}\");\n\n            if update {\n                reset_test262_commit(&latest_commit_hash, verbose)?;\n            }\n        }\n\n        return Ok(());\n    }\n\n    println!(\"Cloning test262...\");\n    let result = Command::new(\"git\")\n        .arg(\"clone\")\n        .arg(TEST262_REPOSITORY)\n        .arg(DEFAULT_TEST262_DIRECTORY)\n        .status()?;\n\n    if !result.success() {\n        bail!(\n            \"Cloning Test262 repository failed with return code {:?}\",\n            result.code()\n        );\n    }\n\n    if let Some(commit) = commit {\n        if verbose != 0 {\n            println!(\"Reset Test262 to commit: {commit}...\");\n        }\n\n        reset_test262_commit(commit, verbose)?;\n    }\n\n    Ok(())\n}\n\n/// Runs the full test suite.\n#[allow(clippy::too_many_arguments)]\nfn run_test_suite(\n    config: &Config,\n    verbose: u8,\n    parallel: bool,\n    test262_path: &Path,\n    suite: &Path,\n    output: Option<&Path>,\n    edition: SpecEdition,\n    versioned: bool,\n    optimizer_options: OptimizerOptions,\n    console: bool,\n) -> Result<()> {\n    if let Some(path) = output {\n        if path.exists() {\n            if !path.is_dir() {\n                bail!(\"the output path must be a directory.\");\n            }\n        } else {\n            std::fs::create_dir_all(path).wrap_err(\"could not create the output directory\")?;\n        }\n    }\n\n    if verbose != 0 {\n        println!(\"Loading the test suite...\");\n    }\n    let harness = read_harness(test262_path).wrap_err(\"could not read harness\")?;\n\n    if suite.to_string_lossy().ends_with(\".js\") {\n        let test = read_test(&test262_path.join(suite)).wrap_err_with(|| {\n            let suite = suite.display();\n            format!(\"could not read the test {suite}\")\n        })?;\n\n        if test.edition <= edition {\n            if verbose != 0 {\n                println!(\"Test loaded, starting...\");\n            }\n            test.run(&harness, verbose, optimizer_options, console);\n        } else {\n            println!(\n                \"Minimum spec edition of test is bigger than the specified edition. Skipping.\"\n            );\n        }\n\n        println!();\n    } else {\n        let suite =\n            read_suite(&test262_path.join(suite), config.ignored(), false).wrap_err_with(|| {\n                let suite = suite.display();\n                format!(\"could not read the suite {suite}\")\n            })?;\n\n        if verbose != 0 {\n            println!(\"Test suite loaded, starting tests...\");\n        }\n        let results = suite.run(\n            &harness,\n            verbose,\n            parallel,\n            edition,\n            optimizer_options,\n            console,\n        );\n\n        if versioned {\n            let mut table = comfy_table::Table::new();\n            table.load_preset(comfy_table::presets::UTF8_HORIZONTAL_ONLY);\n            table.set_header(vec![\n                \"Edition\", \"Total\", \"Passed\", \"Ignored\", \"Failed\", \"Panics\", \"%\",\n            ]);\n            for column in table.column_iter_mut().skip(1) {\n                column.set_cell_alignment(comfy_table::CellAlignment::Right);\n            }\n            for (v, stats) in SpecEdition::all_editions()\n                .filter(|v| *v <= edition)\n                .map(|v| {\n                    let stats = results.versioned_stats.get(v).unwrap_or(results.stats);\n                    (v, stats)\n                })\n            {\n                let Statistics {\n                    total,\n                    passed,\n                    ignored,\n                    panic,\n                } = stats;\n                let failed = total - passed - ignored;\n                let conformance = (passed as f64 / total as f64) * 100.0;\n                let conformance = format!(\"{conformance:.2}\");\n                table.add_row(vec![\n                    v.to_string(),\n                    total.to_string(),\n                    passed.to_string(),\n                    ignored.to_string(),\n                    failed.to_string(),\n                    panic.to_string(),\n                    conformance,\n                ]);\n            }\n            println!(\"\\n\\nResults\\n\");\n            println!(\"{table}\");\n        } else {\n            let Statistics {\n                total,\n                passed,\n                ignored,\n                panic,\n            } = results.stats;\n            println!(\"\\n\\nResults ({edition}):\");\n            println!(\"Total tests: {total}\");\n            println!(\"Passed tests: {}\", passed.to_string().green());\n            println!(\"Ignored tests: {}\", ignored.to_string().yellow());\n            println!(\n                \"Failed tests: {} ({})\",\n                (total - passed - ignored).to_string().red(),\n                format!(\"{panic} panics\").red()\n            );\n            println!(\n                \"Conformance: {:.2}%\",\n                (passed as f64 / total as f64) * 100.0\n            );\n        }\n\n        if let Some(output) = output {\n            write_json(results, output, verbose, test262_path)\n                .wrap_err(\"could not write the results to the output JSON file\")?;\n        }\n    }\n\n    Ok(())\n}\n\n/// All the harness include files.\n#[derive(Debug, Clone)]\nstruct Harness {\n    assert: HarnessFile,\n    sta: HarnessFile,\n    doneprint_handle: HarnessFile,\n    includes: FxHashMap<Box<str>, HarnessFile>,\n}\n\n#[derive(Debug, Clone)]\nstruct HarnessFile {\n    content: Box<str>,\n    path: Box<Path>,\n}\n\n/// Represents a test suite.\n#[derive(Debug, Clone)]\nstruct TestSuite {\n    name: Box<str>,\n    path: Box<Path>,\n    suites: Box<[TestSuite]>,\n    tests: Box<[Test]>,\n}\n\n/// Represents a tests statistic\n#[derive(Default, Debug, Copy, Clone, Serialize, Deserialize)]\nstruct Statistics {\n    #[serde(rename = \"t\")]\n    total: usize,\n    #[serde(rename = \"o\")]\n    passed: usize,\n    #[serde(rename = \"i\")]\n    ignored: usize,\n    #[serde(rename = \"p\")]\n    panic: usize,\n}\n\nimpl Add for Statistics {\n    type Output = Self;\n\n    fn add(self, rhs: Self) -> Self::Output {\n        Self {\n            total: self.total + rhs.total,\n            passed: self.passed + rhs.passed,\n            ignored: self.ignored + rhs.ignored,\n            panic: self.panic + rhs.panic,\n        }\n    }\n}\n\nimpl AddAssign for Statistics {\n    fn add_assign(&mut self, rhs: Self) {\n        self.total += rhs.total;\n        self.passed += rhs.passed;\n        self.ignored += rhs.ignored;\n        self.panic += rhs.panic;\n    }\n}\n\n/// Represents tests statistics separated by ECMAScript edition\n#[derive(Default, Debug, Copy, Clone, Serialize)]\nstruct VersionedStats {\n    es5: Statistics,\n    es6: Statistics,\n    es7: Statistics,\n    es8: Statistics,\n    es9: Statistics,\n    es10: Statistics,\n    es11: Statistics,\n    es12: Statistics,\n    es13: Statistics,\n    es14: Statistics,\n    es15: Statistics,\n    es16: Statistics,\n}\n\nimpl<'de> Deserialize<'de> for VersionedStats {\n    fn deserialize<D>(deserializer: D) -> std::result::Result<Self, D::Error>\n    where\n        D: Deserializer<'de>,\n    {\n        #[derive(Deserialize)]\n        struct Inner {\n            es5: Statistics,\n            es6: Statistics,\n            es7: Statistics,\n            es8: Statistics,\n            es9: Statistics,\n            es10: Statistics,\n            es11: Statistics,\n            es12: Statistics,\n            es13: Statistics,\n            #[serde(default)]\n            es14: Option<Statistics>,\n            #[serde(default)]\n            es15: Option<Statistics>,\n            #[serde(default)]\n            es16: Option<Statistics>,\n        }\n\n        let inner = Inner::deserialize(deserializer)?;\n\n        let Inner {\n            es5,\n            es6,\n            es7,\n            es8,\n            es9,\n            es10,\n            es11,\n            es12,\n            es13,\n            es14,\n            es15,\n            es16,\n        } = inner;\n        let es14 = es14.unwrap_or(es13);\n        let es15 = es15.unwrap_or(es14);\n        let es16 = es16.unwrap_or(es15);\n\n        Ok(Self {\n            es5,\n            es6,\n            es7,\n            es8,\n            es9,\n            es10,\n            es11,\n            es12,\n            es13,\n            es14,\n            es15,\n            es16,\n        })\n    }\n}\n\nimpl VersionedStats {\n    /// Applies `f` to all the statistics for which its edition is bigger or equal\n    /// than `min_edition`.\n    fn apply(&mut self, min_edition: SpecEdition, f: fn(&mut Statistics)) {\n        for edition in SpecEdition::all_editions().filter(|&edition| min_edition <= edition) {\n            if let Some(stats) = self.get_mut(edition) {\n                f(stats);\n            }\n        }\n    }\n\n    /// Gets the statistics corresponding to `edition`, returning `None` if `edition`\n    /// is `SpecEdition::ESNext`.\n    const fn get(&self, edition: SpecEdition) -> Option<Statistics> {\n        let stats = match edition {\n            SpecEdition::ES5 => self.es5,\n            SpecEdition::ES6 => self.es6,\n            SpecEdition::ES7 => self.es7,\n            SpecEdition::ES8 => self.es8,\n            SpecEdition::ES9 => self.es9,\n            SpecEdition::ES10 => self.es10,\n            SpecEdition::ES11 => self.es11,\n            SpecEdition::ES12 => self.es12,\n            SpecEdition::ES13 => self.es13,\n            SpecEdition::ES14 => self.es14,\n            SpecEdition::ES15 => self.es15,\n            SpecEdition::ES16 => self.es16,\n            SpecEdition::ESNext => return None,\n        };\n        Some(stats)\n    }\n\n    /// Gets a mutable reference to the statistics corresponding to `edition`, returning `None` if\n    /// `edition` is `SpecEdition::ESNext`.\n    fn get_mut(&mut self, edition: SpecEdition) -> Option<&mut Statistics> {\n        let stats = match edition {\n            SpecEdition::ES5 => &mut self.es5,\n            SpecEdition::ES6 => &mut self.es6,\n            SpecEdition::ES7 => &mut self.es7,\n            SpecEdition::ES8 => &mut self.es8,\n            SpecEdition::ES9 => &mut self.es9,\n            SpecEdition::ES10 => &mut self.es10,\n            SpecEdition::ES11 => &mut self.es11,\n            SpecEdition::ES12 => &mut self.es12,\n            SpecEdition::ES13 => &mut self.es13,\n            SpecEdition::ES14 => &mut self.es14,\n            SpecEdition::ES15 => &mut self.es15,\n            SpecEdition::ES16 => &mut self.es16,\n            SpecEdition::ESNext => return None,\n        };\n        Some(stats)\n    }\n}\n\nimpl Add for VersionedStats {\n    type Output = Self;\n\n    fn add(self, rhs: Self) -> Self::Output {\n        Self {\n            es5: self.es5 + rhs.es5,\n            es6: self.es6 + rhs.es6,\n            es7: self.es7 + rhs.es7,\n            es8: self.es8 + rhs.es8,\n            es9: self.es9 + rhs.es9,\n            es10: self.es10 + rhs.es10,\n            es11: self.es11 + rhs.es11,\n            es12: self.es12 + rhs.es12,\n            es13: self.es13 + rhs.es13,\n            es14: self.es14 + rhs.es14,\n            es15: self.es15 + rhs.es15,\n            es16: self.es16 + rhs.es16,\n        }\n    }\n}\n\nimpl AddAssign for VersionedStats {\n    fn add_assign(&mut self, rhs: Self) {\n        self.es5 += rhs.es5;\n        self.es6 += rhs.es6;\n        self.es7 += rhs.es7;\n        self.es8 += rhs.es8;\n        self.es9 += rhs.es9;\n        self.es10 += rhs.es10;\n        self.es11 += rhs.es11;\n        self.es12 += rhs.es12;\n        self.es13 += rhs.es13;\n        self.es14 += rhs.es14;\n        self.es15 += rhs.es15;\n        self.es16 += rhs.es16;\n    }\n}\n\n/// Outcome of a test suite.\n#[derive(Debug, Clone, Serialize, Deserialize)]\nstruct SuiteResult {\n    #[serde(rename = \"n\")]\n    name: Box<str>,\n    #[serde(rename = \"a\")]\n    stats: Statistics,\n    #[serde(rename = \"av\", default)]\n    versioned_stats: VersionedStats,\n    #[serde(skip_serializing_if = \"Vec::is_empty\", default)]\n    #[serde(rename = \"s\")]\n    suites: Vec<SuiteResult>,\n    #[serde(skip_serializing_if = \"Vec::is_empty\", default)]\n    #[serde(rename = \"t\")]\n    tests: Vec<TestResult>,\n    #[serde(skip_serializing_if = \"FxHashSet::is_empty\", default)]\n    #[serde(rename = \"f\")]\n    features: FxHashSet<String>,\n}\n\n/// Outcome of a test.\n#[derive(Debug, Clone, Serialize, Deserialize)]\n#[allow(dead_code)]\nstruct TestResult {\n    #[serde(rename = \"n\")]\n    name: Box<str>,\n    #[serde(rename = \"v\", default)]\n    edition: SpecEdition,\n    #[serde(skip)]\n    result_text: Box<str>,\n    #[serde(rename = \"r\")]\n    result: TestOutcomeResult,\n}\n\n#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]\nenum TestOutcomeResult {\n    #[serde(rename = \"O\")]\n    Passed,\n    #[serde(rename = \"I\")]\n    Ignored,\n    #[serde(rename = \"F\")]\n    Failed,\n    #[serde(rename = \"P\")]\n    Panic,\n}\n\n/// Represents a test.\n#[derive(Debug, Clone)]\n#[allow(dead_code)]\nstruct Test {\n    name: Box<str>,\n    path: Box<Path>,\n    description: Box<str>,\n    esid: Option<Box<str>>,\n    edition: SpecEdition,\n    flags: TestFlags,\n    information: Box<str>,\n    expected_outcome: Outcome,\n    features: FxHashSet<Box<str>>,\n    includes: FxHashSet<Box<str>>,\n    locale: Locale,\n    ignored: bool,\n}\n\nimpl Test {\n    /// Creates a new test.\n    fn new<N, C>(name: N, path: C, metadata: MetaData) -> Result<Self>\n    where\n        N: Into<Box<str>>,\n        C: Into<Box<Path>>,\n    {\n        let edition = SpecEdition::from_test_metadata(&metadata)\n            .map_err(|feats| eyre!(\"test metadata contained unknown features: {feats:?}\"))?;\n\n        Ok(Self {\n            edition,\n            name: name.into(),\n            description: metadata.description,\n            esid: metadata.esid,\n            flags: metadata.flags.into(),\n            information: metadata.info,\n            features: metadata.features.into_vec().into_iter().collect(),\n            expected_outcome: Outcome::from(metadata.negative),\n            includes: metadata.includes.into_vec().into_iter().collect(),\n            locale: metadata.locale,\n            path: path.into(),\n            ignored: false,\n        })\n    }\n\n    /// Sets the test as ignored.\n    #[inline]\n    fn set_ignored(&mut self) {\n        self.ignored = true;\n    }\n\n    /// Checks if this is a module test.\n    #[inline]\n    const fn is_module(&self) -> bool {\n        self.flags.contains(TestFlags::MODULE)\n    }\n}\n\n/// An outcome for a test.\n#[derive(Debug, Default, Clone)]\nenum Outcome {\n    #[default]\n    Positive,\n    Negative {\n        phase: Phase,\n        error_type: ErrorType,\n    },\n}\n\nimpl From<Option<Negative>> for Outcome {\n    fn from(neg: Option<Negative>) -> Self {\n        neg.map(|neg| Self::Negative {\n            phase: neg.phase,\n            error_type: neg.error_type,\n        })\n        .unwrap_or_default()\n    }\n}\n\nbitflags! {\n    #[derive(Debug, Clone, Copy)]\n    struct TestFlags: u16 {\n        const STRICT = 0b0_0000_0001;\n        const NO_STRICT = 0b0_0000_0010;\n        const MODULE = 0b0_0000_0100;\n        const RAW = 0b0_0000_1000;\n        const ASYNC = 0b0_0001_0000;\n        const GENERATED = 0b0_0010_0000;\n        const CAN_BLOCK_IS_FALSE = 0b0_0100_0000;\n        const CAN_BLOCK_IS_TRUE = 0b0_1000_0000;\n        const NON_DETERMINISTIC = 0b1_0000_0000;\n    }\n}\n\nimpl Default for TestFlags {\n    fn default() -> Self {\n        Self::STRICT | Self::NO_STRICT\n    }\n}\n\nimpl From<TestFlag> for TestFlags {\n    fn from(flag: TestFlag) -> Self {\n        match flag {\n            TestFlag::OnlyStrict => Self::STRICT,\n            TestFlag::NoStrict => Self::NO_STRICT,\n            TestFlag::Module => Self::MODULE,\n            TestFlag::Raw => Self::RAW,\n            TestFlag::Async => Self::ASYNC,\n            TestFlag::Generated => Self::GENERATED,\n            TestFlag::CanBlockIsFalse => Self::CAN_BLOCK_IS_FALSE,\n            TestFlag::CanBlockIsTrue => Self::CAN_BLOCK_IS_TRUE,\n            TestFlag::NonDeterministic => Self::NON_DETERMINISTIC,\n        }\n    }\n}\n\nimpl<T> From<T> for TestFlags\nwhere\n    T: AsRef<[TestFlag]>,\n{\n    fn from(flags: T) -> Self {\n        let flags = flags.as_ref();\n        if flags.is_empty() {\n            Self::default()\n        } else {\n            let mut result = Self::empty();\n            for flag in flags {\n                result |= Self::from(*flag);\n            }\n\n            if !result.intersects(Self::default()) {\n                result |= Self::default();\n            }\n\n            result\n        }\n    }\n}\n\nimpl<'de> Deserialize<'de> for TestFlags {\n    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>\n    where\n        D: Deserializer<'de>,\n    {\n        struct FlagsVisitor;\n\n        impl<'de> Visitor<'de> for FlagsVisitor {\n            type Value = TestFlags;\n\n            fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {\n                write!(formatter, \"a sequence of flags\")\n            }\n\n            fn visit_seq<A>(self, mut seq: A) -> Result<Self::Value, A::Error>\n            where\n                A: serde::de::SeqAccess<'de>,\n            {\n                let mut flags = TestFlags::empty();\n                while let Some(elem) = seq.next_element::<TestFlag>()? {\n                    flags |= elem.into();\n                }\n                Ok(flags)\n            }\n        }\n\n        struct RawFlagsVisitor;\n\n        impl Visitor<'_> for RawFlagsVisitor {\n            type Value = TestFlags;\n\n            fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {\n                write!(formatter, \"a flags number\")\n            }\n\n            fn visit_u16<E>(self, v: u16) -> Result<Self::Value, E>\n            where\n                E: serde::de::Error,\n            {\n                TestFlags::from_bits(v).ok_or_else(|| {\n                    E::invalid_value(Unexpected::Unsigned(v.into()), &\"a valid flag number\")\n                })\n            }\n        }\n\n        if deserializer.is_human_readable() {\n            deserializer.deserialize_seq(FlagsVisitor)\n        } else {\n            deserializer.deserialize_u16(RawFlagsVisitor)\n        }\n    }\n}\n\n/// Phase for an error.\n#[derive(Debug, Clone, Copy, Deserialize)]\n#[serde(rename_all = \"lowercase\")]\nenum Phase {\n    Parse,\n    Resolution,\n    Runtime,\n}\n\n/// Locale information structure.\n#[derive(Debug, Default, Clone, Deserialize)]\n#[serde(transparent)]\n#[allow(dead_code)]\nstruct Locale {\n    locale: Box<[Box<str>]>,\n}\n"
  },
  {
    "path": "tests/tester/src/read.rs",
    "content": "//! Module to read the list of test suites from disk.\n\nuse std::{\n    collections::HashMap,\n    ffi::OsStr,\n    fs,\n    path::{Path, PathBuf},\n};\n\nuse color_eyre::{\n    Result,\n    eyre::{OptionExt, WrapErr},\n};\nuse cow_utils::CowUtils;\nuse rustc_hash::{FxBuildHasher, FxHashMap};\nuse serde::Deserialize;\n\nuse crate::{HarnessFile, Ignored};\n\nuse super::{Harness, Locale, Phase, Test, TestSuite};\n\n/// Representation of the YAML metadata in Test262 tests.\n#[derive(Debug, Clone, Deserialize)]\npub(super) struct MetaData {\n    pub(super) description: Box<str>,\n    pub(super) esid: Option<Box<str>>,\n    #[allow(dead_code)]\n    pub(super) es5id: Option<Box<str>>,\n    pub(super) es6id: Option<Box<str>>,\n    #[serde(default)]\n    pub(super) info: Box<str>,\n    #[serde(default)]\n    pub(super) features: Box<[Box<str>]>,\n    #[serde(default)]\n    pub(super) includes: Box<[Box<str>]>,\n    #[serde(default)]\n    pub(super) flags: Box<[TestFlag]>,\n    #[serde(default)]\n    pub(super) negative: Option<Negative>,\n    #[serde(default)]\n    pub(super) locale: Locale,\n}\n\n/// Negative test information structure.\n#[derive(Debug, Clone, Deserialize)]\npub(super) struct Negative {\n    pub(super) phase: Phase,\n    #[serde(rename = \"type\")]\n    pub(super) error_type: ErrorType,\n}\n\n/// All possible error types\n#[derive(Debug, Copy, Clone, Deserialize, PartialEq, Eq)]\n#[allow(clippy::enum_variant_names)] // Better than appending `rename` to all variants\npub(super) enum ErrorType {\n    Test262Error,\n    SyntaxError,\n    ReferenceError,\n    RangeError,\n    TypeError,\n    EvalError,\n}\n\nimpl ErrorType {\n    pub(super) const fn as_str(self) -> &'static str {\n        match self {\n            Self::Test262Error => \"Test262Error\",\n            Self::SyntaxError => \"SyntaxError\",\n            Self::ReferenceError => \"ReferenceError\",\n            Self::RangeError => \"RangeError\",\n            Self::TypeError => \"TypeError\",\n            Self::EvalError => \"EvalError\",\n        }\n    }\n}\n\n/// Individual test flag.\n#[derive(Debug, Clone, Copy, Eq, PartialEq, Deserialize)]\n#[serde(rename_all = \"camelCase\")]\npub(super) enum TestFlag {\n    OnlyStrict,\n    NoStrict,\n    Module,\n    Raw,\n    Async,\n    Generated,\n    #[serde(rename = \"CanBlockIsFalse\")]\n    CanBlockIsFalse,\n    #[serde(rename = \"CanBlockIsTrue\")]\n    CanBlockIsTrue,\n    #[serde(rename = \"non-deterministic\")]\n    NonDeterministic,\n}\n\n/// Reads the Test262 defined bindings.\npub(super) fn read_harness(test262_path: &Path) -> Result<Harness> {\n    let mut includes: HashMap<Box<str>, HarnessFile, FxBuildHasher> = FxHashMap::default();\n\n    let harness_path = &test262_path.join(\"harness\");\n\n    read_harness_dir(harness_path, harness_path, &mut includes)?;\n\n    let assert = includes\n        .remove(\"assert.js\")\n        .ok_or_eyre(\"failed to load harness file `assert.js`\")?;\n    let sta = includes\n        .remove(\"sta.js\")\n        .ok_or_eyre(\"failed to load harness file `sta.js`\")?;\n    let doneprint_handle = includes\n        .remove(\"doneprintHandle.js\")\n        .ok_or_eyre(\"failed to load harness file `donePrintHandle.js`\")?;\n\n    Ok(Harness {\n        assert,\n        sta,\n        doneprint_handle,\n        includes,\n    })\n}\n\nfn read_harness_dir(\n    harness_root: &Path,\n    directory_name: &Path,\n    includes: &mut HashMap<Box<str>, HarnessFile, FxBuildHasher>,\n) -> Result<()> {\n    for entry in fs::read_dir(directory_name).wrap_err(\"error reading the harness directory\")? {\n        let entry = entry?;\n        let entry_path = entry.path();\n\n        if entry.file_type()?.is_dir() {\n            read_harness_dir(harness_root, &entry_path, includes)?;\n            continue;\n        }\n\n        let key = entry_path\n            .strip_prefix(harness_root)\n            .wrap_err(\"invalid harness file path\")?;\n\n        includes.insert(key.to_string_lossy().into(), read_harness_file(entry_path)?);\n    }\n\n    Ok(())\n}\n\nfn read_harness_file(path: PathBuf) -> Result<HarnessFile> {\n    let content = fs::read_to_string(path.as_path())\n        .wrap_err_with(|| format!(\"error reading the harness file `{}`\", path.display()))?;\n\n    Ok(HarnessFile {\n        content: content.into_boxed_str(),\n        path: path.into_boxed_path(),\n    })\n}\n\n/// Reads a test suite in the given path.\npub(super) fn read_suite(\n    path: &Path,\n    ignored: &Ignored,\n    mut ignore_suite: bool,\n) -> Result<TestSuite> {\n    let name = path\n        .file_name()\n        .and_then(OsStr::to_str)\n        .ok_or_eyre(\"invalid path for test suite\")?;\n\n    ignore_suite |= ignored.contains_test(path);\n\n    let mut suites = Vec::new();\n    let mut tests = Vec::new();\n\n    // TODO: iterate in parallel\n    for entry in path.read_dir().wrap_err(\"could not retrieve entry\")? {\n        let entry = entry?;\n        let filetype = entry.file_type().wrap_err(\"could not retrieve file type\")?;\n\n        if filetype.is_dir() {\n            suites.push(\n                read_suite(entry.path().as_path(), ignored, ignore_suite).wrap_err_with(|| {\n                    let path = entry.path();\n                    let suite = path.display();\n                    format!(\"error reading sub-suite {suite}\")\n                })?,\n            );\n            continue;\n        }\n\n        let path = entry.path();\n\n        if path.extension() != Some(OsStr::new(\"js\")) {\n            // Ignore files that aren't executable.\n            continue;\n        }\n\n        if path\n            .file_stem()\n            .is_some_and(|stem| stem.as_encoded_bytes().ends_with(b\"FIXTURE\"))\n        {\n            // Ignore files that are fixtures.\n            continue;\n        }\n\n        let mut test = read_test(&path).wrap_err_with(|| {\n            let path = entry.path();\n            let suite = path.display();\n            format!(\"error reading test {suite}\")\n        })?;\n\n        if ignore_suite\n            || ignored.contains_any_flag(test.flags)\n            || ignored.contains_test(&test.path)\n            || test\n                .features\n                .iter()\n                .any(|feat| ignored.contains_feature(feat))\n        {\n            test.set_ignored();\n        }\n        tests.push(test);\n    }\n\n    Ok(TestSuite {\n        name: name.into(),\n        path: Box::from(path),\n        suites: suites.into_boxed_slice(),\n        tests: tests.into_boxed_slice(),\n    })\n}\n\n/// Reads information about a given test case.\npub(super) fn read_test(path: &Path) -> Result<Test> {\n    let name = path\n        .file_stem()\n        .and_then(OsStr::to_str)\n        .ok_or_eyre(\"invalid path for test\")?;\n\n    let metadata = read_metadata(path)?;\n\n    Test::new(name, path, metadata)\n}\n\n/// Reads the metadata from the input test code.\nfn read_metadata(test: &Path) -> Result<MetaData> {\n    let code = fs::read_to_string(test)?;\n\n    let (_, metadata) = code\n        .split_once(\"/*---\")\n        .ok_or_eyre(\"invalid test metadata\")?;\n    let (metadata, _) = metadata\n        .split_once(\"---*/\")\n        .ok_or_eyre(\"invalid test metadata\")?;\n    let metadata = metadata.cow_replace('\\r', \"\\n\");\n\n    serde_yaml::from_str(&metadata).map_err(Into::into)\n}\n"
  },
  {
    "path": "tests/tester/src/results.rs",
    "content": "use crate::{Statistics, VersionedStats};\n\nuse super::SuiteResult;\nuse color_eyre::{Result, eyre::WrapErr};\nuse rustc_hash::FxHashSet;\nuse serde::{Deserialize, Serialize};\nuse std::{\n    env, fs,\n    io::{BufReader, BufWriter},\n    path::Path,\n};\n\n/// Structure to store full result information.\n#[derive(Debug, Clone, Deserialize, Serialize)]\nstruct ResultInfo {\n    #[serde(rename = \"c\")]\n    commit: Box<str>,\n    #[serde(rename = \"u\")]\n    test262_commit: Box<str>,\n    #[serde(rename = \"r\")]\n    results: SuiteResult,\n}\n\n/// Structure to store full result information.\n#[derive(Debug, Clone, Deserialize, Serialize)]\nstruct ReducedResultInfo {\n    #[serde(rename = \"c\")]\n    commit: Box<str>,\n    #[serde(rename = \"u\")]\n    test262_commit: Box<str>,\n    #[serde(rename = \"a\")]\n    stats: Statistics,\n    #[serde(rename = \"av\", default)]\n    versioned_stats: VersionedStats,\n}\n\nimpl From<ResultInfo> for ReducedResultInfo {\n    /// Creates a new reduced suite result from a full suite result.\n    fn from(info: ResultInfo) -> Self {\n        Self {\n            commit: info.commit,\n            test262_commit: info.test262_commit,\n            stats: info.results.stats,\n            versioned_stats: info.results.versioned_stats,\n        }\n    }\n}\n\n#[derive(Debug, Clone, Deserialize, Serialize)]\nstruct FeaturesInfo {\n    #[serde(rename = \"c\")]\n    commit: Box<str>,\n    #[serde(rename = \"u\")]\n    test262_commit: Box<str>,\n    #[serde(rename = \"n\")]\n    suite_name: Box<str>,\n    #[serde(rename = \"f\")]\n    features: FxHashSet<String>,\n}\n\nimpl From<ResultInfo> for FeaturesInfo {\n    fn from(info: ResultInfo) -> Self {\n        Self {\n            commit: info.commit,\n            test262_commit: info.test262_commit,\n            suite_name: info.results.name,\n            features: info.results.features,\n        }\n    }\n}\n\n/// File name of the \"latest results\" JSON file.\nconst LATEST_FILE_NAME: &str = \"latest.json\";\n\n/// File name of the \"all results\" JSON file.\nconst RESULTS_FILE_NAME: &str = \"results.json\";\n\n/// File name of the \"features\" JSON file.\nconst FEATURES_FILE_NAME: &str = \"features.json\";\n\n/// Writes the results of running the test suite to the given JSON output file.\n///\n/// It will append the results to the ones already present, in an array.\npub(crate) fn write_json(\n    results: SuiteResult,\n    output_dir: &Path,\n    verbose: u8,\n    test262_path: &Path,\n) -> Result<()> {\n    let mut branch = env::var(\"GITHUB_REF\").unwrap_or_default();\n    if branch.starts_with(\"refs/pull\") {\n        \"pull\".clone_into(&mut branch);\n    }\n\n    let output_dir = if branch.is_empty() {\n        output_dir.to_path_buf()\n    } else {\n        let folder = output_dir.join(branch);\n        fs::create_dir_all(&folder)?;\n        folder\n    };\n\n    if verbose != 0 {\n        println!(\"Writing the results to {}...\", output_dir.display());\n    }\n\n    // Write the latest results.\n\n    let latest = output_dir.join(LATEST_FILE_NAME);\n\n    let new_results = ResultInfo {\n        commit: env::var(\"GITHUB_SHA\").unwrap_or_default().into_boxed_str(),\n        test262_commit: get_test262_commit(test262_path)?,\n        results,\n    };\n\n    let latest = BufWriter::new(fs::File::create(latest)?);\n    serde_json::to_writer(latest, &new_results)?;\n\n    // Write the full list of results, retrieving the existing ones first.\n\n    let all_path = output_dir.join(RESULTS_FILE_NAME);\n\n    let mut all_results: Vec<ReducedResultInfo> = if all_path.exists() {\n        serde_json::from_reader(BufReader::new(fs::File::open(&all_path)?))?\n    } else {\n        Vec::new()\n    };\n\n    all_results.push(new_results.clone().into());\n\n    let output = BufWriter::new(fs::File::create(&all_path)?);\n    serde_json::to_writer(output, &all_results)?;\n\n    if verbose != 0 {\n        println!(\"Results written correctly\");\n    }\n\n    // Write the full list of features, existing features go first.\n\n    let features = output_dir.join(FEATURES_FILE_NAME);\n\n    let mut all_features: Vec<FeaturesInfo> = if features.exists() {\n        serde_json::from_reader(BufReader::new(fs::File::open(&features)?))?\n    } else {\n        Vec::new()\n    };\n\n    all_features.push(new_results.into());\n\n    let features = BufWriter::new(fs::File::create(&features)?);\n    serde_json::to_writer(features, &all_features)?;\n\n    if verbose != 0 {\n        println!(\"Features written correctly\");\n    }\n\n    Ok(())\n}\n\n/// Gets the commit OID of the test262 submodule.\nfn get_test262_commit(test262_path: &Path) -> Result<Box<str>> {\n    let main_head_path = test262_path.join(\".git/refs/heads/main\");\n    let mut commit_id = fs::read_to_string(main_head_path)?;\n    // Remove newline.\n    commit_id.pop();\n    Ok(commit_id.into_boxed_str())\n}\n\n/// Compares the results of two test suite runs.\n#[allow(clippy::cast_possible_wrap)]\npub(crate) fn compare_results(base: &Path, new: &Path, markdown: bool) -> Result<()> {\n    // If the path is a directory, use latest.json from that directory\n    let base_path = if base.is_dir() {\n        base.join(LATEST_FILE_NAME)\n    } else {\n        base.to_path_buf()\n    };\n\n    let new_path = if new.is_dir() {\n        new.join(LATEST_FILE_NAME)\n    } else {\n        new.to_path_buf()\n    };\n\n    let base_results: ResultInfo = serde_json::from_reader(BufReader::new(\n        fs::File::open(&base_path).wrap_err(\"could not open the base results file\")?,\n    ))\n    .wrap_err(\"could not read the base results\")?;\n\n    let new_results: ResultInfo = serde_json::from_reader(BufReader::new(\n        fs::File::open(&new_path).wrap_err(\"could not open the new results file\")?,\n    ))\n    .wrap_err(\"could not read the new results\")?;\n\n    let base_total = base_results.results.stats.total as isize;\n    let new_total = new_results.results.stats.total as isize;\n    let total_diff = new_total - base_total;\n\n    let base_passed = base_results.results.stats.passed as isize;\n    let new_passed = new_results.results.stats.passed as isize;\n    let passed_diff = new_passed - base_passed;\n\n    let base_ignored = base_results.results.stats.ignored as isize;\n    let new_ignored = new_results.results.stats.ignored as isize;\n    let ignored_diff = new_ignored - base_ignored;\n\n    let base_failed = base_total - base_passed - base_ignored;\n    let new_failed = new_total - new_passed - new_ignored;\n    let failed_diff = new_failed - base_failed;\n\n    let base_panics = base_results.results.stats.panic as isize;\n    let new_panics = new_results.results.stats.panic as isize;\n    let panic_diff = new_panics - base_panics;\n\n    let base_conformance = (base_passed as f64 / base_total as f64) * 100_f64;\n    let new_conformance = (new_passed as f64 / new_total as f64) * 100_f64;\n    let conformance_diff = new_conformance - base_conformance;\n\n    let test_diff = compute_result_diff(Path::new(\"\"), &base_results.results, &new_results.results);\n\n    if markdown {\n        /// Simple function to add commas as thousands separator for integers.\n        fn pretty_int(i: isize) -> String {\n            let mut res = String::new();\n\n            for (idx, val) in i.abs().to_string().chars().rev().enumerate() {\n                if idx != 0 && idx % 3 == 0 {\n                    res.insert(0, ',');\n                }\n                res.insert(0, val);\n            }\n            res\n        }\n\n        /// Generates a proper diff format, with some bold text if things change.\n        fn diff_format(diff: isize) -> String {\n            format!(\n                \"{}{}{}{}\",\n                if diff == 0 { \"\" } else { \"**\" },\n                if diff.is_positive() {\n                    \"+\"\n                } else if diff.is_negative() {\n                    \"-\"\n                } else {\n                    \"\"\n                },\n                pretty_int(diff),\n                if diff == 0 { \"\" } else { \"**\" }\n            )\n        }\n\n        println!(\"| Test result | main count | PR count | difference |\");\n        println!(\"| :---------: | :----------: | :------: | :--------: |\");\n        println!(\n            \"| Total | {} | {} | {} |\",\n            pretty_int(base_total),\n            pretty_int(new_total),\n            diff_format(total_diff),\n        );\n        println!(\n            \"| Passed | {} | {} | {} |\",\n            pretty_int(base_passed),\n            pretty_int(new_passed),\n            diff_format(passed_diff),\n        );\n        println!(\n            \"| Ignored | {} | {} | {} |\",\n            pretty_int(base_ignored),\n            pretty_int(new_ignored),\n            diff_format(ignored_diff),\n        );\n        println!(\n            \"| Failed | {} | {} | {} |\",\n            pretty_int(base_failed),\n            pretty_int(new_failed),\n            diff_format(failed_diff),\n        );\n        println!(\n            \"| Panics | {} | {} | {} |\",\n            pretty_int(base_panics),\n            pretty_int(new_panics),\n            diff_format(panic_diff),\n        );\n        println!(\n            \"| Conformance | {:.2}% | {:.2}% | {}{}{:.2}%{} |\",\n            base_conformance,\n            new_conformance,\n            if conformance_diff.abs() > f64::EPSILON {\n                \"**\"\n            } else {\n                \"\"\n            },\n            if conformance_diff > 0_f64 { \"+\" } else { \"\" },\n            conformance_diff,\n            if conformance_diff.abs() > f64::EPSILON {\n                \"**\"\n            } else {\n                \"\"\n            },\n        );\n\n        if !test_diff.fixed.is_empty() {\n            println!();\n            println!(\n                \"<details><summary><b>Fixed tests ({}):</b></summary>\",\n                test_diff.fixed.len()\n            );\n            println!(\"\\n```\");\n            for test in test_diff.fixed {\n                println!(\"{test}\");\n            }\n            println!(\"```\");\n            println!(\"</details>\");\n        }\n\n        if !test_diff.broken.is_empty() {\n            println!();\n            println!(\n                \"<details><summary><b>Broken tests ({}):</b></summary>\",\n                test_diff.broken.len()\n            );\n            println!(\"\\n```\");\n            for test in test_diff.broken {\n                println!(\"{test}\");\n            }\n            println!(\"```\");\n            println!(\"</details>\");\n        }\n\n        if !test_diff.new_panics.is_empty() {\n            println!();\n            println!(\n                \"<details><summary><b>New panics ({}):</b></summary>\",\n                test_diff.new_panics.len()\n            );\n            println!(\"\\n```\");\n            for test in test_diff.new_panics {\n                println!(\"{test}\");\n            }\n            println!(\"```\");\n            println!(\"</details>\");\n        }\n\n        if !test_diff.panic_fixes.is_empty() {\n            println!();\n            println!(\n                \"<details><summary><b>Fixed panics ({}):</b></summary>\",\n                test_diff.panic_fixes.len()\n            );\n            println!(\"\\n```\");\n            for test in test_diff.panic_fixes {\n                println!(\"{test}\");\n            }\n            println!(\"```\");\n            println!(\"</details>\");\n        }\n    } else {\n        println!(\"Test262 conformance changes:\");\n        println!(\"| Test result | main |    PR   | difference |\");\n        println!(\n            \"|    Passed   | {base_passed:^6} | {new_passed:^5} | {:^10} |\",\n            base_passed - new_passed\n        );\n        println!(\n            \"|   Ignored   | {base_ignored:^6} | {new_ignored:^5} | {:^10} |\",\n            base_ignored - new_ignored\n        );\n        println!(\n            \"|   Failed    | {base_failed:^6} | {new_failed:^5} | {:^10} |\",\n            base_failed - new_failed,\n        );\n        println!(\n            \"|   Panics    | {base_panics:^6} | {new_panics:^5} | {:^10} |\",\n            base_panics - new_panics\n        );\n\n        if !test_diff.fixed.is_empty() {\n            println!();\n            println!(\"Fixed tests ({}):\", test_diff.fixed.len());\n            for test in test_diff.fixed {\n                println!(\"{test}\");\n            }\n        }\n\n        if !test_diff.broken.is_empty() {\n            println!();\n            println!(\"Broken tests ({}):\", test_diff.broken.len());\n            for test in test_diff.broken {\n                println!(\"{test}\");\n            }\n        }\n\n        if !test_diff.new_panics.is_empty() {\n            println!();\n            println!(\"New panics ({}):\", test_diff.new_panics.len());\n            for test in test_diff.new_panics {\n                println!(\"{test}\");\n            }\n        }\n\n        if !test_diff.panic_fixes.is_empty() {\n            println!();\n            println!(\"Fixed panics ({}):\", test_diff.panic_fixes.len());\n            for test in test_diff.panic_fixes {\n                println!(\"{test}\");\n            }\n        }\n    }\n\n    Ok(())\n}\n\n/// Test differences.\n#[derive(Debug, Clone, Default)]\nstruct ResultDiff {\n    fixed: Vec<Box<str>>,\n    broken: Vec<Box<str>>,\n    new_panics: Vec<Box<str>>,\n    panic_fixes: Vec<Box<str>>,\n}\n\nimpl ResultDiff {\n    /// Extends the diff with new results.\n    fn extend(&mut self, new: Self) {\n        self.fixed.extend(new.fixed);\n        self.broken.extend(new.broken);\n        self.new_panics.extend(new.new_panics);\n        self.panic_fixes.extend(new.panic_fixes);\n    }\n}\n\n/// Compares a base and a new result and returns the list of differences.\nfn compute_result_diff(\n    base: &Path,\n    base_result: &SuiteResult,\n    new_result: &SuiteResult,\n) -> ResultDiff {\n    use super::TestOutcomeResult;\n\n    let mut final_diff = ResultDiff::default();\n\n    for base_test in &base_result.tests {\n        if let Some(new_test) = new_result\n            .tests\n            .iter()\n            .find(|new_test| new_test.name == base_test.name)\n        {\n            let test_name = format!(\n                \"test/{}/{}.js (previously {:?})\",\n                base.display(),\n                new_test.name,\n                base_test.result\n            )\n            .into_boxed_str();\n\n            match (base_test.result, new_test.result) {\n                (a, b) if a == b => {}\n                (TestOutcomeResult::Ignored, TestOutcomeResult::Failed) => {}\n\n                (_, TestOutcomeResult::Passed) => final_diff.fixed.push(test_name),\n                (TestOutcomeResult::Panic, _) => final_diff.panic_fixes.push(test_name),\n                (_, TestOutcomeResult::Failed) => final_diff.broken.push(test_name),\n                (_, TestOutcomeResult::Panic) => final_diff.new_panics.push(test_name),\n\n                _ => {}\n            }\n        }\n    }\n\n    for base_suite in &base_result.suites {\n        if let Some(new_suite) = new_result\n            .suites\n            .iter()\n            .find(|new_suite| new_suite.name == base_suite.name)\n        {\n            let new_base = base.join(new_suite.name.as_ref());\n            let diff = compute_result_diff(new_base.as_path(), base_suite, new_suite);\n\n            final_diff.extend(diff);\n        }\n    }\n\n    final_diff\n}\n"
  },
  {
    "path": "tests/wpt/.gitignore",
    "content": "target\ncorpus\nartifacts\ncoverage\nCargo.lock\n"
  },
  {
    "path": "tests/wpt/Cargo.toml",
    "content": "[package]\nname = \"boa_wpt\"\npublish = false\nversion = \"0.1.0\"\nedition = \"2021\"\n\n[dependencies]\nboa_engine = { path = \"../../core/engine\" }\nboa_gc = { path = \"../../core/gc\" }\nboa_runtime = { path = \"../../core/runtime\", features = [\"all\"] }\nrstest = \"0.25.0\"\nurl = { version = \"2.5.4\", features = [] }\n\n[build-dependencies]\ngit2 = \"0.20.2\"\ntoml = \"0.8.23\"\nserde = { version = \"1.0.219\", features = [\"derive\"] }\n"
  },
  {
    "path": "tests/wpt/build.rs",
    "content": "#![allow(dead_code)]\n\nuse serde::Deserialize;\nuse std::path::Path;\n\n/// Structure that contains the configuration of the tester.\n#[derive(Debug, Deserialize)]\nstruct Config {\n    rev: Option<String>,\n}\n\nfn prep_repository(rev: &str, root: impl AsRef<Path>) {\n    let root = root.as_ref();\n    // See if the repo already exists.\n    let repo = if !std::fs::exists(root).unwrap() {\n        // This repo is quite large, so do a shallow clone then perform the update.\n        let mut options = git2::FetchOptions::new();\n        options.depth(1);\n\n        let mut repo_builder = git2::build::RepoBuilder::new();\n        repo_builder.fetch_options(options);\n\n        repo_builder\n            .clone(\"https://github.com/web-platform-tests/wpt\", root)\n            .expect(\"Could not clone\")\n    } else {\n        git2::Repository::open(root).unwrap()\n    };\n\n    let head_sha = repo\n        .head()\n        .unwrap()\n        .peel_to_commit()\n        .unwrap()\n        .id()\n        .to_string();\n    if head_sha == rev {\n        // There's nothing to do.\n        return;\n    }\n\n    // Fetch the sha instead.\n    let mut options = git2::FetchOptions::new();\n    options.depth(1);\n    repo.find_remote(\"origin\")\n        .expect(\"Could not find remote (origin)\")\n        .fetch(&[rev], Some(&mut options), None)\n        .expect(\"Could not fetch repo\");\n\n    // Then checkout to it and we're done.\n    repo.reset(\n        repo.find_commit(git2::Oid::from_str(rev).unwrap())\n            .unwrap()\n            .as_object(),\n        git2::ResetType::Hard,\n        None,\n    )\n    .expect(\"Could not reset the repository\");\n}\n\nfn main() {\n    const CONFIG_PATH: &str = \"../../test_wpt_config.toml\";\n    println!(\"cargo:rerun-if-changed=../../tests_wpt\");\n    println!(\"cargo:rerun-if-changed={CONFIG_PATH}\");\n\n    let config: Config = {\n        let input = std::fs::read_to_string(CONFIG_PATH).expect(\"Could not read config file\");\n        toml::from_str(&input).expect(\"Config file is invalid TOML\")\n    };\n\n    let root = \"../../tests_wpt\";\n\n    // Clone the WPT repository.\n    if let Some(rev) = config.rev {\n        prep_repository(&rev, root);\n    }\n\n    // If user already declared WPT_ROOT, keep it.\n    if std::env::var(\"WPT_ROOT\").is_err() {\n        println!(\"cargo:rerun-if-changed={root}\");\n        println!(\"cargo:rustc-env=WPT_ROOT={root}\");\n    }\n}\n"
  },
  {
    "path": "tests/wpt/src/fetcher/mod.rs",
    "content": "use boa_engine::{js_error, Context, Finalize, JsData, JsError, JsResult, Trace};\nuse boa_runtime::fetch::request::JsRequest;\nuse boa_runtime::fetch::response::JsResponse;\nuse boa_runtime::fetch::BlockingReqwestFetcher;\nuse boa_runtime::fetch::Fetcher;\nuse std::cell::RefCell;\nuse std::path::PathBuf;\nuse std::rc::Rc;\nuse url::Url;\n\n/// The Fetcher implementation used by the WPT tests suite.\n/// This implementation understands the current running test script and will\n/// resolve URLs relative to it as a base.\n///\n// TODO: Look into what NodeJS or other browserless runtimes do for WPT tests.\n#[derive(Debug, Clone, Trace, Finalize, JsData)]\npub struct WptFetcher {\n    wpt_server: String,\n    wpt_root: PathBuf,\n    #[unsafe_ignore_trace]\n    current_file: Rc<RefCell<Option<PathBuf>>>,\n\n    #[unsafe_ignore_trace]\n    inner: Rc<BlockingReqwestFetcher>,\n}\n\nimpl WptFetcher {\n    pub fn new(wpt_root: impl Into<PathBuf>, wpt_server: String) -> Self {\n        Self {\n            wpt_server,\n            wpt_root: wpt_root.into(),\n            current_file: Default::default(),\n            inner: Rc::new(BlockingReqwestFetcher::default()),\n        }\n    }\n\n    pub fn set_current_file(&mut self, file: impl Into<PathBuf>) {\n        self.current_file.borrow_mut().replace(file.into());\n    }\n}\n\nimpl Fetcher for WptFetcher {\n    fn resolve_uri(&self, uri: String, _context: &mut Context) -> JsResult<String> {\n        // If it's already a valid URL, return it.\n        if let Ok(u) = Url::parse(&uri) {\n            return Ok(u.to_string());\n        }\n\n        // If it's a relative URL, we need to perform some resolution first...\n        let cf = self.current_file.borrow();\n        let Some(current_file) = cf.as_ref() else {\n            return Err(js_error!(\"No current file was set by the test framework.\"));\n        };\n        let wpt_root = &self.wpt_root;\n\n        let base = Url::from_file_path(current_file).expect(\"Invalid wpt root path\");\n        let url = base.join(&uri).expect(\"Invalid URL (join)\");\n\n        let full = PathBuf::from(url.path());\n        let path = full\n            .strip_prefix(wpt_root)\n            .expect(\"File should always be under root.\")\n            .to_str()\n            .unwrap();\n\n        let wpt_server = &self.wpt_server;\n        let query = url.query().map_or(\"\".to_string(), |q| format!(\"?{q}\"));\n        let fragment = url.fragment().map_or(\"\".to_string(), |q| format!(\"#{q}\"));\n\n        Url::parse(&format!(\"http://{wpt_server}/{path}{query}{fragment}\"))\n            .map_err(JsError::from_rust)\n            .map(|url| url.to_string())\n    }\n\n    async fn fetch(\n        self: Rc<Self>,\n        request: JsRequest,\n        signal: Option<boa_engine::JsObject>,\n        context: &RefCell<&mut Context>,\n    ) -> JsResult<JsResponse> {\n        eprintln!(\"request: {request:?}\");\n        let response = self.inner.clone().fetch(request, signal, context).await;\n        eprintln!(\"response: {response:?}\");\n        response\n    }\n}\n"
  },
  {
    "path": "tests/wpt/src/lib.rs",
    "content": "//! Integration tests running the Web Platform Tests (WPT) for the `boa_runtime` crate.\n#![allow(unused_crate_dependencies)]\n\nuse boa_engine::class::Class;\nuse boa_engine::interop::ContextData;\nuse boa_engine::parser::source::UTF16Input;\nuse boa_engine::property::Attribute;\nuse boa_engine::value::{Nullable, TryFromJs};\nuse boa_engine::{\n    js_error, js_str, js_string, Context, Finalize, IntoJsFunctionCopied, JsData, JsResult,\n    JsString, JsValue, Source, Trace,\n};\nuse boa_runtime::url::Url;\nuse boa_runtime::{DefaultLogger, NullLogger};\nuse logger::RecordingLogEvent;\nuse std::cell::{OnceCell, RefCell};\nuse std::collections::BTreeMap;\nuse std::path::{Path, PathBuf};\nuse std::rc::Rc;\n\nmod fetcher;\nmod logger;\n\n/// The test status JavaScript type from WPT. This is defined in the test harness.\n#[derive(Debug, Clone, PartialEq, Eq)]\nenum TestStatus {\n    Pass = 0,\n    Fail = 1,\n    Timeout = 2,\n    NotRun = 3,\n    PreconditionFailed = 4,\n}\n\nimpl std::fmt::Display for TestStatus {\n    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {\n        match self {\n            Self::Pass => write!(f, \"PASS\"),\n            Self::Fail => write!(f, \"FAIL\"),\n            Self::Timeout => write!(f, \"TIMEOUT\"),\n            Self::NotRun => write!(f, \"NOTRUN\"),\n            Self::PreconditionFailed => write!(f, \"PRECONDITION FAILED\"),\n        }\n    }\n}\n\nimpl TryFromJs for TestStatus {\n    fn try_from_js(value: &JsValue, context: &mut Context) -> JsResult<Self> {\n        match value.to_u32(context) {\n            Ok(0) => Ok(Self::Pass),\n            Ok(1) => Ok(Self::Fail),\n            Ok(2) => Ok(Self::Timeout),\n            Ok(3) => Ok(Self::NotRun),\n            Ok(4) => Ok(Self::PreconditionFailed),\n            _ => Err(js_error!(\"Invalid test status\")),\n        }\n    }\n}\n\n/// A single test.\n#[derive(TryFromJs)]\nstruct Test {\n    name: JsString,\n    status: TestStatus,\n    message: Nullable<JsString>,\n    properties: BTreeMap<JsString, JsValue>,\n}\n\n/// A Test suite source code.\nstruct TestSuiteSource {\n    path: PathBuf,\n    bytes: OnceCell<Vec<u16>>,\n}\n\nconst REWRITE_RULES: &[(&str, &str)] = &[(\n    \"/resources/WebIDLParser.js\",\n    \"/resources/webidl2/webidl2.js\",\n)];\n\nimpl TestSuiteSource {\n    /// Create a new test suite source.\n    fn new(source: impl AsRef<Path>) -> Self {\n        Self {\n            path: source.as_ref().to_path_buf(),\n            bytes: OnceCell::new(),\n        }\n    }\n\n    fn read_to_string(&self) -> Result<String, Box<dyn std::error::Error>> {\n        fn read_string(slice: &[u8], size: usize) -> Option<String> {\n            assert!(2 * size <= slice.len());\n            let iter = (0..size).map(|i| u16::from_be_bytes([slice[2 * i], slice[2 * i + 1]]));\n\n            std::char::decode_utf16(iter)\n                .collect::<Result<String, _>>()\n                .ok()\n        }\n        let buffer = std::fs::read(&self.path)?;\n        // Check if the buffer contains UTF8 or UTF16.\n        let maybe_utf8 = String::from_utf8(buffer.clone());\n        if let Ok(utf8) = maybe_utf8 {\n            Ok(utf8)\n        } else if let Some(utf16) = read_string(&buffer, buffer.len() / 2) {\n            Ok(utf16)\n        } else {\n            Err(\"Could not determine encoding\".into())\n        }\n    }\n\n    fn source(&self) -> Source<'_, UTF16Input<'_>> {\n        let b = self.bytes.get_or_init(|| {\n            self.read_to_string()\n                .unwrap()\n                .encode_utf16()\n                .collect::<Vec<u16>>()\n        });\n        Source::from_utf16(b).with_path(&self.path)\n    }\n\n    fn scripts(&self) -> Result<Vec<String>, Box<dyn std::error::Error>> {\n        let mut scripts: Vec<String> = Vec::new();\n        let dir = self\n            .path\n            .parent()\n            .expect(\"Could not get the parent directory\");\n\n        'outer: for script in self.meta()?.get(\"script\").unwrap_or(&Vec::new()) {\n            let script = script\n                .split_once('?')\n                .map_or(script.to_string(), |(s, _)| s.to_string());\n\n            // Resolve the source path relative to the script path, but under the wpt_path.\n            let script_path = Path::new(&script);\n            let path = if script_path.is_relative() {\n                dir.join(script_path)\n            } else {\n                script_path.to_path_buf()\n            };\n\n            for (from, to) in REWRITE_RULES {\n                if path.to_string_lossy().as_ref() == *from {\n                    scripts.push((*to).to_string());\n                    continue 'outer;\n                }\n            }\n            scripts.push(path.to_string_lossy().to_string());\n        }\n        Ok(scripts)\n    }\n\n    fn meta(&self) -> Result<BTreeMap<String, Vec<String>>, Box<dyn std::error::Error>> {\n        let mut meta: BTreeMap<String, Vec<String>> = BTreeMap::new();\n\n        // Read the whole file and extract the metadata.\n        let content = self.read_to_string()?;\n        for line in content.lines() {\n            if let Some(kv) = line.strip_prefix(\"// META:\") {\n                let kv = kv.trim();\n                if let Some((key, value)) = kv.split_once('=') {\n                    meta.entry(key.to_string())\n                        .or_default()\n                        .push(value.to_string());\n                }\n            } else if !line.starts_with(\"//\") && !line.is_empty() {\n                break;\n            }\n        }\n\n        Ok(meta)\n    }\n}\n\n/// Create the BOA context and add the necessary global objects for WPT.\nfn create_context(wpt_path: &Path) -> (Context, logger::RecordingLogger, fetcher::WptFetcher) {\n    let mut context = Context::default();\n    let logger = if std::env::var(\"WPT_CONSOLE\").is_ok() {\n        logger::RecordingLogger::new(DefaultLogger)\n    } else {\n        logger::RecordingLogger::new(NullLogger)\n    };\n\n    let fetcher = fetcher::WptFetcher::new(wpt_path, \"web-platform.test:8000\".to_string());\n    boa_runtime::register(\n        (\n            boa_runtime::extensions::ConsoleExtension(logger.clone()),\n            boa_runtime::extensions::FetchExtension(fetcher.clone()),\n        ),\n        None,\n        &mut context,\n    )\n    .expect(\"Failed to register boa_runtime\");\n\n    // Define self as the globalThis.\n    let global_this = context.global_object();\n    context\n        .register_global_property(js_str!(\"self\"), global_this, Attribute::all())\n        .unwrap();\n\n    // Define location to be an empty URL.\n    let location =\n        Url::new(\"about:blank\".to_string().into(), None).expect(\"Could not parse the location URL\");\n    let location =\n        Url::from_data(location, &mut context).expect(\"Could not create the location URL\");\n    context\n        .register_global_property(js_str!(\"location\"), location, Attribute::all())\n        .unwrap();\n\n    let harness_path = wpt_path.join(\"resources/testharness.js\");\n    let harness = Source::from_filepath(&harness_path).expect(\"Could not create a source.\");\n\n    if let Err(e) = context.eval(harness) {\n        panic!(\"Failed to eval testharness.js: {e}\");\n    }\n\n    (context, logger, fetcher)\n}\n\n/// The result callback for the WPT test.\n#[track_caller]\nfn result_callback__(\n    ContextData(logger): ContextData<logger::RecordingLogger>,\n    test: Test,\n    context: &mut Context,\n) -> JsResult<()> {\n    // Check the logs if the test succeeded.\n    assert_eq!(\n        test.status,\n        TestStatus::Pass,\n        \"Test {:?} failed with message:\\n  {:?}\",\n        test.name.to_std_string_lossy(),\n        test.message.unwrap_or_default()\n    );\n\n    // Check the logs.\n    let logs = logger.all_logs();\n    if let Some(log_regex) = test.properties.get(&js_string!(\"logs\")) {\n        if let Ok(logs_re) = log_regex.try_js_into::<Vec<JsValue>>(context) {\n            for re in logs_re {\n                let passes = if let Some(re) = re.as_regexp() {\n                    logs.iter().any(|log: &RecordingLogEvent| -> bool {\n                        let s = JsString::from(log.msg.clone());\n                        re.test(s, context).unwrap_or(false)\n                    })\n                } else {\n                    let re_str = re.to_string(context)?.to_std_string_escaped();\n                    logs.iter()\n                        .any(|log: &RecordingLogEvent| -> bool { log.msg.contains(&re_str) })\n                };\n                assert!(\n                    passes,\n                    \"Test {:?} failed to find log: {}\",\n                    test.name.to_std_string_lossy(),\n                    re.display()\n                );\n            }\n        }\n    }\n\n    Ok(())\n}\n\n#[track_caller]\nfn complete_callback__(ContextData(test_done): ContextData<TestCompletion>) {\n    test_done.done();\n}\n\n#[derive(Debug, Clone, Trace, Finalize, JsData)]\nstruct TestCompletion(#[unsafe_ignore_trace] Rc<RefCell<bool>>);\n\nimpl TestCompletion {\n    fn new() -> Self {\n        Self(Rc::new(RefCell::new(false)))\n    }\n\n    fn done(&self) {\n        self.0.replace(true);\n    }\n\n    fn is_done(&self) -> bool {\n        *self.0.borrow()\n    }\n}\n\n/// Load and execute the test file.\n// This can be marked as allow unused because it would give false positives\n// in clippy.\n#[allow(unused)]\nfn execute_test_file(path: &Path) {\n    let dir = path.parent().unwrap();\n    let wpt_path = PathBuf::from(\n        std::env::var(\"WPT_ROOT\").expect(\"Could not find the WPT_ROOT environment variable\"),\n    );\n    let wpt_path = if wpt_path.is_absolute() {\n        wpt_path\n    } else {\n        std::env::current_dir()\n            .unwrap()\n            .join(wpt_path)\n            .canonicalize()\n            .unwrap()\n    };\n    let (mut context, logger, mut fetcher) = create_context(&wpt_path);\n    let test_done = TestCompletion::new();\n\n    // Insert the logger to be able to access the logs after the test is done.\n    context.insert_data(logger.clone());\n    context.insert_data(test_done.clone());\n\n    let function = result_callback__\n        .into_js_function_copied(&mut context)\n        .to_js_function(context.realm());\n    context\n        .register_global_property(js_str!(\"result_callback__\"), function, Attribute::all())\n        .expect(\"Could not register result_callback__\");\n    context\n        .eval(Source::from_bytes(\n            b\"add_result_callback(result_callback__);\",\n        ))\n        .expect(\"Could not eval add_result_callback\");\n\n    let function = complete_callback__\n        .into_js_function_copied(&mut context)\n        .to_js_function(context.realm());\n    context\n        .register_global_property(js_str!(\"complete_callback__\"), function, Attribute::all())\n        .expect(\"Could not register complete_callback__\");\n    context\n        .eval(Source::from_bytes(\n            b\"add_completion_callback(complete_callback__);\",\n        ))\n        .expect(\"Could not eval add_completion_callback\");\n\n    // Load the test.\n    let source = TestSuiteSource::new(path);\n    for script in source.scripts().expect(\"Could not get scripts\") {\n        // Resolve the source path relative to the script path, but under the wpt_path.\n        let script_path = Path::new(&script);\n        let path = if script_path.is_relative() {\n            dir.join(script_path)\n        } else if script_path.starts_with(&wpt_path) {\n            script_path.to_path_buf()\n        } else {\n            wpt_path.join(script_path.strip_prefix(\"/\").unwrap())\n        };\n\n        let path = path.canonicalize().expect(\"Could not canonicalize path\");\n\n        if path.exists() {\n            let source = Source::from_filepath(&path).expect(\"Could not parse the source.\");\n            if let Err(err) = context.eval(source) {\n                panic!(\"Could not eval script, path = {path:?}, err = {err:?}\");\n            }\n        } else {\n            panic!(\"Script does not exist, path = {path:?}\");\n        }\n    }\n\n    fetcher.set_current_file(&source.path);\n\n    if let Err(e) = context.eval(source.source()) {\n        panic!(\"Could not run the test source:\\n{e}\")\n    }\n\n    context.run_jobs().expect(\"Could not run jobs\");\n\n    // Done()\n    if let Err(e) = context.eval(Source::from_bytes(b\"done()\")) {\n        panic!(\"`done()` returned an error\\n{e}\");\n    }\n\n    let start = std::time::Instant::now();\n    while !test_done.is_done() {\n        context.run_jobs();\n\n        assert!(\n            start.elapsed().as_secs() < 10,\n            \"Test did not complete in 10 seconds.\"\n        );\n    }\n}\n\n/// Test the console with the WPT test suite.\n#[cfg(not(clippy))]\n#[rstest::rstest]\nfn console(\n    #[base_dir = \"${WPT_ROOT}\"]\n    #[files(\"console/*.any.js\")]\n    // TODO: The console-log-large-array.any.js test is too slow.\n    #[exclude(\"console-log-large-array.any.js\")]\n    #[exclude(\"idlharness\")]\n    path: PathBuf,\n) {\n    execute_test_file(&path);\n}\n\n/// Test the text encoder/decoder with the WPT test suite.\n#[cfg(not(clippy))]\n#[rstest::rstest]\nfn encoding(\n    #[base_dir = \"${WPT_ROOT}\"]\n    #[files(\"encoding/api-*.any.js\")]\n    #[files(\"encoding/textencoder-constructor-non-utf.any.js\")]\n    // TODO: re-enable those when better encoding and options are supported.\n    // #[files(\"encoding/textdecoder-*.any.js\")]\n    // #[files(\"encoding/textencoder-*.any.js\")]\n    #[exclude(\"idlharness\")]\n    path: PathBuf,\n) {\n    execute_test_file(&path);\n}\n\n/// Test the URL class with the WPT test suite.\n// A bunch of these tests are failing due to lack of support in the URL class\n// or missing APIs such as fetch.\n#[cfg(not(clippy))]\n#[rstest::rstest]\nfn url(\n    #[base_dir = \"${WPT_ROOT}\"]\n    #[files(\"url/url-*.any.js\")]\n    #[exclude(\"idlharness\")]\n    // \"Base URL about:blank cannot be a base\"\n    #[exclude(\"url-searchparams.any.js\")]\n    // \"fetch is not defined\"\n    #[exclude(\"url-origin.any.js\")]\n    #[exclude(\"url-setters.any.js\")]\n    #[exclude(\"url-constructor.any.js\")]\n    path: PathBuf,\n) {\n    execute_test_file(&path);\n}\n\n/// Test the `fetch` with the WPT test suite.\n#[cfg(not(clippy))]\n#[ignore] // This is nowhere near ready for production. It also requires a web-server.\n#[rstest::rstest]\nfn fetch(\n    #[base_dir = \"${WPT_ROOT}\"]\n    #[files(\"fetch/api/**/*.any.js\")]\n    #[exclude(\"idlharness\")]\n    path: PathBuf,\n) {\n    execute_test_file(&path);\n}\n"
  },
  {
    "path": "tests/wpt/src/logger/mod.rs",
    "content": "use boa_engine::{Context, Finalize, JsData, JsResult, Trace};\nuse boa_gc::Gc;\nuse boa_runtime::{ConsoleState, Logger};\nuse std::cell::RefCell;\nuse std::fmt::Debug;\nuse std::rc::Rc;\nuse std::sync::atomic::{AtomicUsize, Ordering};\n\n/// A unique index of all logs.\nstatic UNIQUE: AtomicUsize = AtomicUsize::new(0);\n\n#[derive(Clone, Debug, Trace, Finalize, JsData)]\npub(crate) struct RecordingLogEvent {\n    pub index: usize,\n    pub indent: usize,\n    pub msg: String,\n}\n\nimpl RecordingLogEvent {\n    pub(crate) fn new(msg: String, state: &ConsoleState) -> Self {\n        Self {\n            index: UNIQUE.fetch_add(1, Ordering::SeqCst),\n            indent: state.indent(),\n            msg,\n        }\n    }\n}\n\n#[derive(Default, Trace, Finalize, JsData)]\nstruct RecordingLoggerInner {\n    pub log: Vec<RecordingLogEvent>,\n    pub error: Vec<RecordingLogEvent>,\n}\n\n#[derive(Clone, Trace, Finalize, JsData)]\npub(crate) struct RecordingLogger {\n    /// Also send logs to this logger.\n    tee: Gc<Box<dyn Logger>>,\n\n    #[unsafe_ignore_trace]\n    inner: Rc<RefCell<RecordingLoggerInner>>,\n}\n\nimpl Debug for RecordingLogger {\n    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {\n        f.write_str(\"RecordingLogger { ... }\")\n    }\n}\n\nimpl Logger for RecordingLogger {\n    fn log(&self, msg: String, state: &ConsoleState, ctx: &mut Context) -> JsResult<()> {\n        self.inner\n            .borrow_mut()\n            .log\n            .push(RecordingLogEvent::new(msg.clone(), state));\n        self.tee.log(msg, state, ctx)?;\n        Ok(())\n    }\n\n    fn info(&self, msg: String, state: &ConsoleState, ctx: &mut Context) -> JsResult<()> {\n        self.inner\n            .borrow_mut()\n            .log\n            .push(RecordingLogEvent::new(msg.clone(), state));\n        self.tee.info(msg, state, ctx)?;\n        Ok(())\n    }\n\n    fn warn(&self, msg: String, state: &ConsoleState, ctx: &mut Context) -> JsResult<()> {\n        self.inner\n            .borrow_mut()\n            .log\n            .push(RecordingLogEvent::new(msg.clone(), state));\n        self.tee.warn(msg, state, ctx)?;\n        Ok(())\n    }\n\n    fn error(&self, msg: String, state: &ConsoleState, ctx: &mut Context) -> JsResult<()> {\n        self.inner\n            .borrow_mut()\n            .error\n            .push(RecordingLogEvent::new(msg.clone(), state));\n        self.tee.error(msg, state, ctx)?;\n        Ok(())\n    }\n}\n\nimpl RecordingLogger {\n    pub(crate) fn new<L: Logger + 'static>(tee: L) -> Self {\n        Self {\n            tee: Gc::new(Box::new(tee)),\n            inner: Rc::new(RefCell::new(Default::default())),\n        }\n    }\n}\n\nimpl RecordingLogger {\n    pub(crate) fn all_logs(&self) -> Vec<RecordingLogEvent> {\n        let mut all: Vec<RecordingLogEvent> = self.log().into_iter().chain(self.error()).collect();\n        all.sort_by_key(|x| x.index);\n        all\n    }\n\n    pub(crate) fn log(&self) -> Vec<RecordingLogEvent> {\n        self.inner.borrow().log.clone()\n    }\n\n    pub(crate) fn error(&self) -> Vec<RecordingLogEvent> {\n        self.inner.borrow().error.clone()\n    }\n}\n"
  },
  {
    "path": "tools/gen-icu4x-data/Cargo.toml",
    "content": "[package]\nname = \"gen-icu4x-data\"\npublish = false\nedition.workspace = true\nversion.workspace = true\nrust-version.workspace = true\nauthors.workspace = true\nrepository.workspace = true\nlicense.workspace = true\ndescription.workspace = true\n\n[dependencies]\nicu_provider_export = { workspace = true, features = [\n    \"blob_exporter\",\n    \"rayon\",\n] }\nicu_provider_source = { workspace = true, features = [\n    \"networking\",\n    \"use_wasm\",\n    \"experimental\"\n] }\nlog.workspace = true\nsimple_logger.workspace = true\n\n# Components\n\nicu_casemap = { workspace = true, features = [\"datagen\"] }\nicu_collator = { workspace = true, features = [\"datagen\"] }\nicu_datetime = { workspace = true, features = [\"datagen\"] }\nicu_time = { workspace = true, features = [\"datagen\"] }\nicu_decimal = { workspace = true, features = [\"datagen\"] }\nicu_list = { workspace = true, features = [\"datagen\"] }\nicu_locale = { workspace = true, features = [\"datagen\"] }\nicu_normalizer = { workspace = true, features = [\"datagen\"] }\nicu_plurals = { workspace = true, features = [\"datagen\", \"experimental\"] }\nicu_segmenter = { workspace = true, features = [\"datagen\"] }\n\n[lints]\nworkspace = true\n\n[package.metadata.docs.rs]\nall-features = true\n"
  },
  {
    "path": "tools/gen-icu4x-data/README.md",
    "content": "# boa_icu_provider\n\n`gen-icu4x-data` generates the [ICU4X](https://github.com/unicode-org/icu4x) data provider\nfor `boa_icu_provider`.\n\n## Datagen\n\nTo regenerate the data:\n\n```bash\n$ cargo run --release --bin gen-icu4x-data\n```\n"
  },
  {
    "path": "tools/gen-icu4x-data/src/main.rs",
    "content": "#![allow(missing_docs, rustdoc::missing_crate_level_docs)]\n\nuse std::path::Path;\nuse std::{error::Error, fs::File};\n\nuse icu_provider_export::blob_exporter::BlobExporter;\nuse icu_provider_export::prelude::*;\nuse icu_provider_source::{CoverageLevel, SourceDataProvider};\n\n/// Path to the directory where the exported data lives.\nconst EXPORT_PATH: &str = \"core/icu_provider/data\";\n\n/// List of services used by `Intl` components.\n///\n/// This must be kept in sync with the list of implemented services for `Intl`.\nconst SERVICES: &[(&str, &[DataMarkerInfo])] = &[\n    (\"icu_casemap\", icu_casemap::provider::MARKERS),\n    (\"icu_collator\", icu_collator::provider::MARKERS),\n    (\"icu_datetime\", icu_datetime::provider::MARKERS),\n    (\"icu_time\", icu_time::provider::MARKERS),\n    (\"icu_decimal\", icu_decimal::provider::MARKERS),\n    (\"icu_list\", icu_list::provider::MARKERS),\n    (\"icu_locale\", icu_locale::provider::MARKERS),\n    (\"icu_normalizer\", icu_normalizer::provider::MARKERS),\n    (\"icu_plurals\", icu_plurals::provider::MARKERS),\n    (\"icu_segmenter\", icu_segmenter::provider::MARKERS),\n];\n\nfn export_for_service(\n    service: &str,\n    markers: &[DataMarkerInfo],\n    provider: &SourceDataProvider,\n    driver: ExportDriver,\n) -> Result<(), Box<dyn Error>> {\n    log::info!(\"Generating ICU4X data for service `{service}` with markers: {markers:#?}\");\n\n    let export_path = Path::new(EXPORT_PATH);\n    let export_file = export_path.join(format!(\"{service}.postcard\"));\n\n    driver.with_markers(markers.iter().copied()).export(\n        provider,\n        BlobExporter::new_with_sink(Box::new(File::create(export_file)?)),\n    )?;\n\n    Ok(())\n}\n\nfn main() -> Result<(), Box<dyn Error>> {\n    simple_logger::SimpleLogger::new()\n        .env()\n        .with_level(log::LevelFilter::Info)\n        .init()?;\n\n    // Removal will throw an error if the directory doesn't exist, hence\n    // why we can ignore the error.\n    let _unused = std::fs::remove_dir_all(EXPORT_PATH);\n    std::fs::create_dir_all(EXPORT_PATH)?;\n\n    let provider = &SourceDataProvider::new();\n    let locales = provider\n        .locales_for_coverage_levels([CoverageLevel::Modern])?\n        .into_iter()\n        .map(DataLocaleFamily::with_descendants)\n        .chain([\n            // test262 assumes the en-US locale does not fallback.\n            // Required by https://github.com/tc39/test262/blob/a073f479f80b336256b7fc4e04700c827293e2fe/test/intl402/ListFormat/prototype/resolvedOptions/type.js\n            DataLocaleFamily::single(locale!(\"en-US\").into()),\n            // test262 uses the Manx locale.\n            // Required by https://github.com/tc39/test262/blob/a073f479f80b336256b7fc4e04700c827293e2fe/test/intl402/PluralRules/prototype/resolvedOptions/plural-categories-order.js\n            DataLocaleFamily::with_descendants(locale!(\"gv\").into()),\n        ]);\n\n    let driver = ExportDriver::new(\n        locales,\n        DeduplicationStrategy::None.into(),\n        LocaleFallbacker::try_new_unstable(provider)?,\n    )\n    .with_additional_collations([String::from(\"search*\")])\n    .with_recommended_segmenter_models();\n\n    for (service, keys) in SERVICES {\n        export_for_service(service, keys, provider, driver.clone())?;\n    }\n\n    Ok(())\n}\n"
  },
  {
    "path": "tools/scripts/Cargo.toml",
    "content": "[package]\nname = \"scripts\"\npublish = false\nversion = \"0.0.0\"\nedition.workspace = true\nrust-version.workspace = true\n\n[dependencies]\ncargo_metadata.workspace = true\nlog.workspace = true\nsimple_logger.workspace = true\n\n[lints]\nworkspace = true\n\n[package.metadata.docs.rs]\nall-features = true\n"
  },
  {
    "path": "tools/scripts/src/bin/regenerate-about.rs",
    "content": "//! Regenerates the `ABOUT.md` document for all publishable crates.\n\nuse std::{error::Error, path::Path};\n\nuse log::{info, warn};\n\nfn main() -> Result<(), Box<dyn Error>> {\n    simple_logger::SimpleLogger::new().env().init()?;\n\n    let metadata = cargo_metadata::MetadataCommand::new()\n        .manifest_path(\"./Cargo.toml\")\n        .exec()?;\n\n    for member_id in &metadata.workspace_members {\n        let member = &metadata[member_id];\n\n        info!(\"Checking member `{}`\", member.manifest_path);\n\n        if member.publish.as_ref().is_some_and(Vec::is_empty) {\n            warn!(\"Skipping unpublishable member...\");\n            continue;\n        }\n\n        let clone_path = member.manifest_path.with_file_name(\"ABOUT.md\");\n\n        info!(\"Cloning ABOUT.md into path `{clone_path}`\");\n\n        std::fs::copy(Path::new(\"./ABOUT.md\"), clone_path)?;\n    }\n\n    Ok(())\n}\n"
  },
  {
    "path": "typos.toml",
    "content": "[files]\nextend-exclude = [\"CHANGELOG.md\", \"benches/scripts/**/*.js\"]\n\n[default.extend-words]\nba = \"ba\"\nbeng = \"beng\"\nfo = \"fo\"\nforin = \"forin\"\nmis = \"mis\"\npn = \"pn\"\ntru = \"tru\"\nCaf = \"Caf\"\n"
  },
  {
    "path": "utils/small_btree/ABOUT.md",
    "content": "# About Boa\n\nBoa is an open-source, experimental ECMAScript Engine written in Rust for\nlexing, parsing and executing ECMAScript/JavaScript. Currently, Boa supports some\nof the [language][boa-conformance]. More information can be viewed at [Boa's\nwebsite][boa-web].\n\nTry out the most recent release with Boa's live demo\n[playground][boa-playground].\n\n## Boa Crates\n\n- [**`boa_cli`**][cli] - Boa's CLI && REPL implementation\n- [**`boa_ast`**][ast] - Boa's ECMAScript Abstract Syntax Tree.\n- [**`boa_engine`**][engine] - Boa's implementation of ECMAScript builtin objects and execution.\n- [**`boa_gc`**][gc] - Boa's garbage collector.\n- [**`boa_icu_provider`**][icu] - Boa's ICU4X data provider.\n- [**`boa_interner`**][interner] - Boa's string interner.\n- [**`boa_macros`**][macros] - Boa's macros.\n- [**`boa_parser`**][parser] - Boa's lexer and parser.\n- [**`boa_runtime`**][runtime] - Boa's `WebAPI` features.\n- [**`boa_string`**][string] - Boa's ECMAScript string implementation.\n- [**`boa_wintertc`**][wintertc] - Boa's `WinterTC` (TC55) Minimum Common Web API implementation.\n- [**`tag_ptr`**][tag_ptr] - Utility library that enables a pointer to be associated with a tag of type `usize`.\n- [**`small_btree`**][small_btree] - Utility library that adds the `SmallBTreeMap` data structure.\n\n[boa-conformance]: https://boajs.dev/conformance\n[boa-web]: https://boajs.dev/\n[boa-playground]: https://boajs.dev/playground\n[ast]: https://docs.rs/boa_ast/latest/boa_ast/index.html\n[engine]: https://docs.rs/boa_engine/latest/boa_engine/index.html\n[gc]: https://docs.rs/boa_gc/latest/boa_gc/index.html\n[interner]: https://docs.rs/boa_interner/latest/boa_interner/index.html\n[parser]: https://docs.rs/boa_parser/latest/boa_parser/index.html\n[icu]: https://docs.rs/boa_icu_provider/latest/boa_icu_provider/index.html\n[runtime]: https://docs.rs/boa_runtime/latest/boa_runtime/index.html\n[string]: https://docs.rs/boa_string/latest/boa_string/index.html\n[wintertc]: https://docs.rs/boa_wintertc/latest/boa_wintertc/index.html\n[tag_ptr]: https://docs.rs/tag_ptr/latest/tag_ptr/index.html\n[small_btree]: https://docs.rs/small_btree/latest/small_btree/index.html\n[macros]: https://docs.rs/boa_macros/latest/boa_macros/index.html\n[cli]: https://crates.io/crates/boa_cli\n"
  },
  {
    "path": "utils/small_btree/Cargo.toml",
    "content": "[package]\nname = \"small_btree\"\nkeywords = [\"small\", \"map\"]\ncategories = [\"data-structures\"]\nreadme = \"../../README.md\"\ndescription = \"Utility library that add SmallBTreeMap data structure\"\nversion = \"0.1.0\"\nedition.workspace = true\nauthors.workspace = true\nlicense.workspace = true\nrepository.workspace = true\nrust-version.workspace = true\n\n\n[lints]\nworkspace = true\n\n[package.metadata.docs.rs]\nall-features = true\n\n[dependencies]\narrayvec.workspace = true\n"
  },
  {
    "path": "utils/small_btree/src/entry.rs",
    "content": "use std::{\n    collections::{BTreeMap, btree_map},\n    fmt::Debug,\n};\n\nuse arrayvec::ArrayVec;\n\nuse super::SmallBTreeMap;\n\nuse Entry::{Occupied, Vacant};\n\n/// A view into a single entry in a map, which may either be vacant or occupied.\n///\n/// This `enum` is constructed from the [`entry`] method on [`SmallBTreeMap`].\n///\n/// [`entry`]: SmallBTreeMap::entry\npub enum Entry<'a, K, V, const ARRAY_SIZE: usize> {\n    /// A vacant entry.\n    Vacant(VacantEntry<'a, K, V, ARRAY_SIZE>),\n    /// An occupied entry.\n    Occupied(OccupiedEntry<'a, K, V, ARRAY_SIZE>),\n}\n\nimpl<K: Debug + Ord, V: Debug, const ARRAY_SIZE: usize> Debug for Entry<'_, K, V, ARRAY_SIZE> {\n    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {\n        match self {\n            Self::Vacant(v) => f.debug_tuple(\"Entry\").field(v).finish(),\n            Self::Occupied(o) => f.debug_tuple(\"Entry\").field(o).finish(),\n        }\n    }\n}\n\n/// A view into a vacant entry in a `SmallBTreeMap`.\n/// It is part of the [`Entry`] enum.\npub struct VacantEntry<'a, K, V, const ARRAY_SIZE: usize> {\n    pub(super) inner: InnerVacant<'a, K, V, ARRAY_SIZE>,\n}\n\npub(super) enum InnerVacant<'a, K, V, const ARRAY_SIZE: usize> {\n    Inline(InlineVacantEntry<'a, K, V, ARRAY_SIZE>),\n    Heap(btree_map::VacantEntry<'a, K, V>),\n}\n\nimpl<K: Debug + Ord, V, const ARRAY_SIZE: usize> Debug for VacantEntry<'_, K, V, ARRAY_SIZE> {\n    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {\n        f.debug_tuple(\"VacantEntry\").field(self.key()).finish()\n    }\n}\n\n/// A view into an occupied entry in a `SmallBTreeMap`.\n/// It is part of the [`Entry`] enum.\npub struct OccupiedEntry<'a, K, V, const ARRAY_SIZE: usize> {\n    pub(super) inner: InnerOccupied<'a, K, V, ARRAY_SIZE>,\n}\n\npub(super) enum InnerOccupied<'a, K, V, const ARRAY_SIZE: usize> {\n    Inline(InlineOccupiedEntry<'a, K, V, ARRAY_SIZE>),\n    Heap(btree_map::OccupiedEntry<'a, K, V>),\n}\n\nimpl<K: Ord + Debug, V: Debug, const ARRAY_SIZE: usize> Debug\n    for OccupiedEntry<'_, K, V, ARRAY_SIZE>\n{\n    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {\n        f.debug_struct(\"OccupiedEntry\")\n            .field(\"key\", self.key())\n            .field(\"value\", self.get())\n            .finish()\n    }\n}\n\nimpl<'a, K: Ord, V, const ARRAY_SIZE: usize> Entry<'a, K, V, ARRAY_SIZE> {\n    /// Ensures a value is in the entry by inserting the default if empty, and returns\n    /// a mutable reference to the value in the entry.\n    pub fn or_insert(self, default: V) -> &'a mut V {\n        match self {\n            Occupied(entry) => entry.into_mut(),\n            Vacant(entry) => entry.insert(default),\n        }\n    }\n\n    /// Ensures a value is in the entry by inserting the result of the default function if empty,\n    /// and returns a mutable reference to the value in the entry.\n    pub fn or_insert_with<F: FnOnce() -> V>(self, default: F) -> &'a mut V {\n        match self {\n            Occupied(entry) => entry.into_mut(),\n            Vacant(entry) => entry.insert(default()),\n        }\n    }\n\n    /// Ensures a value is in the entry by inserting, if empty, the result of the default function.\n    /// This method allows for generating key-derived values for insertion by providing the default\n    /// function a reference to the key that was moved during the `.entry(key)` method call.\n    ///\n    /// The reference to the moved key is provided so that cloning or copying the key is\n    /// unnecessary, unlike with `.or_insert_with(|| ... )`.\n    pub fn or_insert_with_key<F: FnOnce(&K) -> V>(self, default: F) -> &'a mut V {\n        match self {\n            Occupied(entry) => entry.into_mut(),\n            Vacant(entry) => {\n                let value = default(entry.key());\n                entry.insert(value)\n            }\n        }\n    }\n\n    /// Returns a reference to this entry's key.\n    #[must_use]\n    pub fn key(&self) -> &K {\n        match self {\n            Occupied(entry) => entry.key(),\n            Vacant(entry) => entry.key(),\n        }\n    }\n\n    /// Provides in-place mutable access to an occupied entry before any\n    /// potential inserts into the map.\n    #[allow(clippy::return_self_not_must_use)]\n    pub fn and_modify<F>(self, f: F) -> Self\n    where\n        F: FnOnce(&mut V),\n    {\n        match self {\n            Occupied(mut entry) => {\n                f(entry.get_mut());\n                Occupied(entry)\n            }\n            Vacant(entry) => Vacant(entry),\n        }\n    }\n}\n\nimpl<'a, K: Ord, V: Default, const ARRAY_SIZE: usize> Entry<'a, K, V, ARRAY_SIZE> {\n    /// Ensures a value is in the entry by inserting the default value if empty,\n    /// and returns a mutable reference to the value in the entry.\n    pub fn or_default(self) -> &'a mut V {\n        match self {\n            Occupied(entry) => entry.into_mut(),\n            Vacant(entry) => entry.insert(Default::default()),\n        }\n    }\n}\n\nimpl<'a, K: Ord, V, const ARRAY_SIZE: usize> VacantEntry<'a, K, V, ARRAY_SIZE> {\n    /// Gets a reference to the key that would be used when inserting a value\n    /// through the `VacantEntry`.\n    #[must_use]\n    pub fn key(&self) -> &K {\n        match &self.inner {\n            InnerVacant::Inline(i) => i.key(),\n            InnerVacant::Heap(v) => v.key(),\n        }\n    }\n\n    /// Takes ownership of the key.\n    #[must_use]\n    pub fn into_key(self) -> K {\n        match self.inner {\n            InnerVacant::Inline(i) => i.into_key(),\n            InnerVacant::Heap(v) => v.into_key(),\n        }\n    }\n\n    /// Sets the value of the entry with the `VacantEntry`'s key,\n    /// and returns a mutable reference to it.\n    pub fn insert(self, value: V) -> &'a mut V {\n        match self.inner {\n            InnerVacant::Inline(i) => i.insert(value),\n            InnerVacant::Heap(v) => v.insert(value),\n        }\n    }\n}\n\nimpl<'a, K: Ord, V, const ARRAY_SIZE: usize> OccupiedEntry<'a, K, V, ARRAY_SIZE> {\n    /// Gets a reference to the key in the entry.\n    #[must_use]\n    pub fn key(&self) -> &K {\n        match &self.inner {\n            InnerOccupied::Inline(o) => o.key(),\n            InnerOccupied::Heap(o) => o.key(),\n        }\n    }\n\n    /// Takes ownership of the key and value from the map.\n    #[allow(clippy::must_use_candidate)]\n    pub fn remove_entry(self) -> (K, V) {\n        match self.inner {\n            InnerOccupied::Inline(o) => o.remove_entry(),\n            InnerOccupied::Heap(o) => o.remove_entry(),\n        }\n    }\n\n    /// Gets a reference to the value in the entry.\n    #[must_use]\n    pub fn get(&self) -> &V {\n        match &self.inner {\n            InnerOccupied::Inline(o) => o.get(),\n            InnerOccupied::Heap(o) => o.get(),\n        }\n    }\n\n    /// Gets a mutable reference to the value in the entry.\n    ///\n    /// If you need a reference to the `OccupiedEntry` that may outlive the\n    /// destruction of the `Entry` value, see [`into_mut`].\n    ///\n    /// [`into_mut`]: OccupiedEntry::into_mut\n    #[must_use]\n    pub fn get_mut(&mut self) -> &mut V {\n        match &mut self.inner {\n            InnerOccupied::Inline(o) => o.get_mut(),\n            InnerOccupied::Heap(o) => o.get_mut(),\n        }\n    }\n\n    /// Converts the entry into a mutable reference to its value.\n    ///\n    /// If you need multiple references to the `OccupiedEntry`, see [`get_mut`].\n    ///\n    /// [`get_mut`]: OccupiedEntry::get_mut\n    #[must_use]\n    pub fn into_mut(self) -> &'a mut V {\n        match self.inner {\n            InnerOccupied::Inline(o) => o.into_mut(),\n            InnerOccupied::Heap(o) => o.into_mut(),\n        }\n    }\n\n    /// Sets the value of the entry with the `OccupiedEntry`'s key,\n    /// and returns the entry's old value.\n    #[allow(clippy::must_use_candidate)]\n    pub fn insert(&mut self, value: V) -> V {\n        match &mut self.inner {\n            InnerOccupied::Inline(o) => o.insert(value),\n            InnerOccupied::Heap(o) => o.insert(value),\n        }\n    }\n\n    /// Takes the value of the entry out of the map, and returns it.\n    #[allow(clippy::must_use_candidate)]\n    pub fn remove(self) -> V {\n        match self.inner {\n            InnerOccupied::Inline(o) => o.remove(),\n            InnerOccupied::Heap(o) => o.remove(),\n        }\n    }\n}\n\npub(super) struct InlineVacantEntry<'a, K, V, const ARRAY_SIZE: usize> {\n    pub(super) key: K,\n    pub(super) map: &'a mut SmallBTreeMap<K, V, ARRAY_SIZE>,\n}\n\nimpl<'a, K: Ord + Eq, V, const ARRAY_SIZE: usize> InlineVacantEntry<'a, K, V, ARRAY_SIZE> {\n    pub(super) fn key(&self) -> &K {\n        &self.key\n    }\n\n    pub(super) fn into_key(self) -> K {\n        self.key\n    }\n\n    pub(super) fn insert(self, value: V) -> &'a mut V {\n        let InlineVacantEntry { key, map } = self;\n\n        let vec = match &mut map.inner {\n            super::Inner::Inline(vec) => {\n                if !vec.is_full() {\n                    let len = vec.len();\n                    vec.push((key, value));\n\n                    // Workaround for Problem case 3 of the current borrow checker.\n                    // https://rust-lang.github.io/rfcs/2094-nll.html#problem-case-3-conditional-control-flow-across-functions\n\n                    match &mut map.inner {\n                        super::Inner::Inline(vec) => return &mut vec[len].1,\n                        super::Inner::Heap(_) => unreachable!(),\n                    }\n                }\n\n                std::mem::take(vec)\n            }\n            super::Inner::Heap(_) => unreachable!(),\n        };\n\n        // Need to convert to a heap allocated map.\n\n        let btree = BTreeMap::from_iter(vec);\n\n        *map = SmallBTreeMap {\n            inner: super::Inner::Heap(btree),\n        };\n\n        match &mut map.inner {\n            super::Inner::Inline(_) => unreachable!(),\n            super::Inner::Heap(h) => h.entry(key).or_insert(value),\n        }\n    }\n}\n\npub(super) struct InlineOccupiedEntry<'a, K, V, const ARRAY_SIZE: usize> {\n    pub(super) index: usize,\n    pub(super) array: &'a mut ArrayVec<(K, V), ARRAY_SIZE>,\n}\n\nimpl<'a, K, V, const ARRAY_SIZE: usize> InlineOccupiedEntry<'a, K, V, ARRAY_SIZE> {\n    pub(super) fn key(&self) -> &K {\n        &self.array[self.index].0\n    }\n\n    pub(super) fn remove_entry(self) -> (K, V) {\n        self.array.remove(self.index)\n    }\n\n    pub(super) fn get(&self) -> &V {\n        &self.array[self.index].1\n    }\n\n    pub(super) fn get_mut(&mut self) -> &mut V {\n        &mut self.array[self.index].1\n    }\n\n    pub(super) fn into_mut(self) -> &'a mut V {\n        &mut self.array[self.index].1\n    }\n\n    pub(super) fn insert(&mut self, value: V) -> V {\n        std::mem::replace(&mut self.array[self.index].1, value)\n    }\n\n    pub(super) fn remove(self) -> V {\n        self.remove_entry().1\n    }\n}\n"
  },
  {
    "path": "utils/small_btree/src/lib.rs",
    "content": "//! A crate that provides a `SmallBTreeMap` collection, which is initially backed by an inline vec\n//! but changes its backing to a heap map if its number of elements exceeds `ARRAY_SIZE`.\n//!\n//! This provides performance benefits for maps that are expected to be small most of the time,\n//! by avoiding heap allocations for the common case while still supporting larger collections when needed.\n\n#![doc = include_str!(\"../ABOUT.md\")]\n#![doc(\n    html_logo_url = \"https://raw.githubusercontent.com/boa-dev/boa/main/assets/logo_black.svg\",\n    html_favicon_url = \"https://raw.githubusercontent.com/boa-dev/boa/main/assets/logo_black.svg\"\n)]\nuse std::{\n    borrow::Borrow,\n    collections::{BTreeMap, btree_map},\n    fmt,\n    hash::{Hash, Hasher},\n    iter::FusedIterator,\n    ops::{Index, IndexMut},\n};\n\nuse arrayvec::ArrayVec;\n\nmod entry;\n\npub use entry::{Entry, OccupiedEntry, VacantEntry};\n\nuse Entry::{Occupied, Vacant};\n\n/// A map that is initially backed by an inline vec, but changes its backing to a heap map if its\n/// number of elements exceeds `ARRAY_SIZE`.\n#[derive(Clone)]\npub struct SmallBTreeMap<K, V, const ARRAY_SIZE: usize> {\n    inner: Inner<K, V, ARRAY_SIZE>,\n}\n\n#[derive(Debug, Clone)]\nenum Inner<K, V, const ARRAY_SIZE: usize> {\n    Inline(ArrayVec<(K, V), ARRAY_SIZE>),\n    Heap(BTreeMap<K, V>),\n}\n\n/// An iterator over the entries of a `SmallBTreeMap`.\n///\n/// This `struct` is created by the [`iter`] method on [`SmallBTreeMap`]. See its\n/// documentation for more.\n///\n/// [`iter`]: SmallBTreeMap::iter\n#[derive(Clone)]\npub struct Iter<'a, K, V> {\n    inner: InnerIter<'a, K, V>,\n}\n\n#[derive(Clone)]\nenum InnerIter<'a, K, V> {\n    Inline(std::slice::Iter<'a, (K, V)>),\n    Heap(btree_map::Iter<'a, K, V>),\n}\n\nimpl<K: fmt::Debug, V: fmt::Debug> fmt::Debug for Iter<'_, K, V> {\n    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {\n        match &self.inner {\n            InnerIter::Inline(i) => f.debug_tuple(\"Inline\").field(i).finish(),\n            InnerIter::Heap(h) => f.debug_tuple(\"Heap\").field(h).finish(),\n        }\n    }\n}\n\nimpl<K, V> Default for Iter<'_, K, V> {\n    /// Creates an empty `small_btree::Iter`.\n    fn default() -> Self {\n        Self {\n            inner: InnerIter::Inline(std::slice::Iter::default()),\n        }\n    }\n}\n\n/// A mutable iterator over the entries of a `SmallBTreeMap`.\n///\n/// This `struct` is created by the [`iter_mut`] method on [`SmallBTreeMap`]. See its\n/// documentation for more.\n///\n/// [`iter_mut`]: SmallBTreeMap::iter_mut\npub struct IterMut<'a, K, V> {\n    inner: InnerIterMut<'a, K, V>,\n}\n\nenum InnerIterMut<'a, K, V> {\n    Inline(std::slice::IterMut<'a, (K, V)>),\n    Heap(btree_map::IterMut<'a, K, V>),\n}\n\nimpl<K: fmt::Debug, V: fmt::Debug> fmt::Debug for IterMut<'_, K, V> {\n    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {\n        match &self.inner {\n            InnerIterMut::Inline(i) => f.debug_tuple(\"Inline\").field(i).finish(),\n            InnerIterMut::Heap(h) => f.debug_tuple(\"Heap\").field(h).finish(),\n        }\n    }\n}\n\nimpl<K, V> Default for IterMut<'_, K, V> {\n    /// Creates an empty `small_btree::IterMut`.\n    fn default() -> Self {\n        Self {\n            inner: InnerIterMut::Inline(std::slice::IterMut::default()),\n        }\n    }\n}\n\n/// An owning iterator over the entries of a `SmallBTreeMap`.\n///\n/// This `struct` is created by the [`into_iter`] method on [`SmallBTreeMap`]\n/// (provided by the [`IntoIterator`] trait). See its documentation for more.\n///\n/// [`into_iter`]: IntoIterator::into_iter\npub struct IntoIter<K, V, const ARRAY_SIZE: usize> {\n    inner: InnerIntoIter<K, V, ARRAY_SIZE>,\n}\n\nenum InnerIntoIter<K, V, const ARRAY_SIZE: usize> {\n    Inline(arrayvec::IntoIter<(K, V), ARRAY_SIZE>),\n    Heap(btree_map::IntoIter<K, V>),\n}\n\nimpl<K: fmt::Debug, V: fmt::Debug, const ARRAY_SIZE: usize> fmt::Debug\n    for IntoIter<K, V, ARRAY_SIZE>\n{\n    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {\n        match &self.inner {\n            InnerIntoIter::Inline(i) => f.debug_tuple(\"Inline\").field(i).finish(),\n            InnerIntoIter::Heap(h) => f.debug_tuple(\"Heap\").field(h).finish(),\n        }\n    }\n}\n\nimpl<K, V, const ARRAY_SIZE: usize> Default for IntoIter<K, V, ARRAY_SIZE> {\n    /// Creates an empty `small_btree::IntoIter`.\n    fn default() -> Self {\n        Self {\n            inner: InnerIntoIter::Inline(ArrayVec::new().into_iter()),\n        }\n    }\n}\n\nimpl<K, V, const ARRAY_SIZE: usize> SmallBTreeMap<K, V, ARRAY_SIZE> {\n    /// Makes a new, empty `SmallBTreeMap`.\n    #[must_use]\n    pub const fn new() -> Self {\n        Self {\n            inner: Inner::Inline(ArrayVec::new_const()),\n        }\n    }\n\n    /// Clears the map, removing all elements.\n    ///\n    /// The current implementation will preserve the heap map allocation\n    /// if the map has already transitioned to the fallback heap map.\n    pub fn clear(&mut self) {\n        match &mut self.inner {\n            Inner::Inline(v) => v.clear(),\n            Inner::Heap(h) => h.clear(),\n        }\n    }\n\n    /// Returns a reference to the value corresponding to the key.\n    ///\n    /// The key may be any borrowed form of the map's key type, but the ordering\n    /// on the borrowed form *must* match the ordering on the key type.\n    pub fn get<Q>(&self, key: &Q) -> Option<&V>\n    where\n        K: Borrow<Q> + Ord + Eq,\n        Q: ?Sized + Ord + Eq,\n    {\n        match &self.inner {\n            Inner::Inline(v) => v.iter().find(|(k, _)| k.borrow() == key).map(|(_, v)| v),\n            Inner::Heap(h) => h.get(key),\n        }\n    }\n\n    /// Returns the key-value pair corresponding to the supplied key.\n    ///\n    /// The supplied key may be any borrowed form of the map's key type, but the ordering\n    /// on the borrowed form *must* match the ordering on the key type.\n    #[allow(clippy::map_identity)]\n    pub fn get_key_value<Q>(&self, key: &Q) -> Option<(&K, &V)>\n    where\n        K: Borrow<Q> + Ord + Eq,\n        Q: ?Sized + Ord + Eq,\n    {\n        match &self.inner {\n            Inner::Inline(v) => v\n                .iter()\n                .find(|(k, _)| k.borrow() == key)\n                .map(|(k, v)| (k, v)),\n            Inner::Heap(h) => h.get_key_value(key),\n        }\n    }\n\n    /// Returns `true` if the map contains a value for the specified key.\n    ///\n    /// The key may be any borrowed form of the map's key type, but the ordering\n    /// on the borrowed form *must* match the ordering on the key type.\n    pub fn contains_key<Q>(&self, key: &Q) -> bool\n    where\n        K: Borrow<Q> + Ord + Eq,\n        Q: ?Sized + Ord + Eq,\n    {\n        self.get(key).is_some()\n    }\n\n    /// Returns a mutable reference to the value corresponding to the key.\n    ///\n    /// The key may be any borrowed form of the map's key type, but the ordering\n    /// on the borrowed form *must* match the ordering on the key type.\n    pub fn get_mut<Q>(&mut self, key: &Q) -> Option<&mut V>\n    where\n        K: Borrow<Q> + Ord + Eq,\n        Q: ?Sized + Ord + Eq,\n    {\n        match &mut self.inner {\n            Inner::Inline(v) => v\n                .iter_mut()\n                .find(|(k, _)| k.borrow() == key)\n                .map(|(_, v)| v),\n            Inner::Heap(h) => h.get_mut(key),\n        }\n    }\n\n    /// Inserts a key-value pair into the map.\n    ///\n    /// If the map did not have this key present, `None` is returned.\n    ///\n    /// If the map did have this key present, the value is updated, and the old\n    /// value is returned. The key is not updated, though; this matters for\n    /// types that can be `==` without being identical. See the [**Insert and complex keys**][keys]\n    /// section from the [`std::collections`] module documentation for more information.\n    ///\n    /// [keys]: https://doc.rust-lang.org/std/collections/index.html#insert-and-complex-keys\n    pub fn insert(&mut self, key: K, value: V) -> Option<V>\n    where\n        K: Eq + Ord,\n    {\n        match self.entry(key) {\n            Occupied(mut entry) => Some(entry.insert(value)),\n            Vacant(entry) => {\n                entry.insert(value);\n                None\n            }\n        }\n    }\n\n    /// Removes a key from the map, returning the value at the key if the key\n    /// was previously in the map.\n    ///\n    /// The key may be any borrowed form of the map's key type, but the ordering\n    /// on the borrowed form *must* match the ordering on the key type.\n    pub fn remove<Q>(&mut self, key: &Q) -> Option<V>\n    where\n        K: Borrow<Q> + Ord + Eq,\n        Q: ?Sized + Ord + Eq,\n    {\n        self.remove_entry(key).map(|(_, v)| v)\n    }\n\n    /// Removes a key from the map, returning the stored key and value if the key\n    /// was previously in the map.\n    ///\n    /// The key may be any borrowed form of the map's key type, but the ordering\n    /// on the borrowed form *must* match the ordering on the key type.\n    pub fn remove_entry<Q>(&mut self, key: &Q) -> Option<(K, V)>\n    where\n        K: Borrow<Q> + Ord,\n        Q: ?Sized + Ord,\n    {\n        match &mut self.inner {\n            Inner::Inline(v) => v\n                .iter()\n                .position(|(k, _)| k.borrow() == key)\n                .map(|idx| v.remove(idx)),\n            Inner::Heap(h) => h.remove_entry(key),\n        }\n    }\n\n    /// Retains only the elements specified by the predicate.\n    ///\n    /// In other words, remove all pairs `(k, v)` for which `f(&k, &mut v)` returns `false`.\n    pub fn retain<F>(&mut self, mut f: F)\n    where\n        K: Ord,\n        F: FnMut(&K, &mut V) -> bool,\n    {\n        match &mut self.inner {\n            Inner::Inline(v) => v.retain(|(k, v)| f(k, v)),\n            Inner::Heap(h) => h.retain(f),\n        }\n    }\n\n    /// Moves all elements from `other` into `self`, leaving `other` empty.\n    ///\n    /// If a key from `other` is already present in `self`, the respective\n    /// value from `self` will be overwritten with the respective value from `other`.\n    pub fn append<const OTHER_SIZE: usize>(&mut self, other: &mut SmallBTreeMap<K, V, OTHER_SIZE>)\n    where\n        K: Ord + Eq,\n    {\n        if other.is_empty() {\n            return;\n        }\n\n        let inline = matches!(other.inner, Inner::Inline(_));\n\n        let other = std::mem::replace(\n            other,\n            SmallBTreeMap {\n                inner: if inline {\n                    Inner::Inline(ArrayVec::new())\n                } else {\n                    Inner::Heap(BTreeMap::new())\n                },\n            },\n        );\n\n        self.extend(other);\n    }\n\n    /// Gets the given key's corresponding entry in the map for in-place manipulation.\n    pub fn entry(&mut self, key: K) -> Entry<'_, K, V, ARRAY_SIZE>\n    where\n        K: Eq + Ord,\n    {\n        match &mut self.inner {\n            Inner::Inline(array) => {\n                let Some(index) = array.iter().position(|(k, _)| *k == key) else {\n                    return Vacant(VacantEntry {\n                        inner: entry::InnerVacant::Inline(entry::InlineVacantEntry {\n                            key,\n                            map: self,\n                        }),\n                    });\n                };\n\n                // Workaround for Problem case 3 of the current borrow checker.\n                // https://rust-lang.github.io/rfcs/2094-nll.html#problem-case-3-conditional-control-flow-across-functions\n                // Hopefully we can remove this with some improvements to the borrow checker.\n                match &mut self.inner {\n                    Inner::Inline(array) => Occupied(OccupiedEntry {\n                        inner: entry::InnerOccupied::Inline(entry::InlineOccupiedEntry {\n                            index,\n                            array,\n                        }),\n                    }),\n                    Inner::Heap(_) => unreachable!(),\n                }\n            }\n            // Same workaround as above.\n            Inner::Heap(_) => match &mut self.inner {\n                Inner::Heap(h) => match h.entry(key) {\n                    btree_map::Entry::Vacant(entry) => Vacant(VacantEntry {\n                        inner: entry::InnerVacant::Heap(entry),\n                    }),\n                    btree_map::Entry::Occupied(entry) => Occupied(OccupiedEntry {\n                        inner: entry::InnerOccupied::Heap(entry),\n                    }),\n                },\n                Inner::Inline(_) => unreachable!(),\n            },\n        }\n    }\n}\n\nimpl<'a, K, V, const ARRAY_SIZE: usize> IntoIterator for &'a SmallBTreeMap<K, V, ARRAY_SIZE> {\n    type Item = (&'a K, &'a V);\n    type IntoIter = Iter<'a, K, V>;\n\n    fn into_iter(self) -> Self::IntoIter {\n        self.iter()\n    }\n}\n\nimpl<'a, K, V> Iterator for Iter<'a, K, V> {\n    type Item = (&'a K, &'a V);\n\n    #[allow(clippy::map_identity)]\n    fn next(&mut self) -> Option<Self::Item> {\n        match &mut self.inner {\n            InnerIter::Inline(i) => i.next().map(|(k, v)| (k, v)),\n            InnerIter::Heap(h) => h.next(),\n        }\n    }\n\n    fn size_hint(&self) -> (usize, Option<usize>) {\n        match &self.inner {\n            InnerIter::Inline(i) => i.size_hint(),\n            InnerIter::Heap(h) => h.size_hint(),\n        }\n    }\n\n    #[allow(clippy::map_identity)]\n    fn last(self) -> Option<(&'a K, &'a V)> {\n        match self.inner {\n            InnerIter::Inline(i) => i.last().map(|(k, v)| (k, v)),\n            InnerIter::Heap(h) => h.last(),\n        }\n    }\n}\n\nimpl<K, V> FusedIterator for Iter<'_, K, V> {}\n\nimpl<'a, K: 'a, V: 'a> DoubleEndedIterator for Iter<'a, K, V> {\n    #[allow(clippy::map_identity)]\n    fn next_back(&mut self) -> Option<(&'a K, &'a V)> {\n        match &mut self.inner {\n            InnerIter::Inline(i) => i.next_back().map(|(k, v)| (k, v)),\n            InnerIter::Heap(h) => h.next_back(),\n        }\n    }\n}\n\nimpl<K, V> ExactSizeIterator for Iter<'_, K, V> {\n    fn len(&self) -> usize {\n        match &self.inner {\n            InnerIter::Inline(i) => i.len(),\n            InnerIter::Heap(h) => h.len(),\n        }\n    }\n}\n\nimpl<'a, K, V, const ARRAY_SIZE: usize> IntoIterator for &'a mut SmallBTreeMap<K, V, ARRAY_SIZE> {\n    type Item = (&'a K, &'a mut V);\n    type IntoIter = IterMut<'a, K, V>;\n\n    fn into_iter(self) -> Self::IntoIter {\n        self.iter_mut()\n    }\n}\n\nimpl<'a, K, V> Iterator for IterMut<'a, K, V> {\n    type Item = (&'a K, &'a mut V);\n\n    fn next(&mut self) -> Option<Self::Item> {\n        match &mut self.inner {\n            InnerIterMut::Inline(i) => i.next().map(|(k, v)| (&*k, v)),\n            InnerIterMut::Heap(h) => h.next(),\n        }\n    }\n\n    fn size_hint(&self) -> (usize, Option<usize>) {\n        match &self.inner {\n            InnerIterMut::Inline(i) => i.size_hint(),\n            InnerIterMut::Heap(h) => h.size_hint(),\n        }\n    }\n\n    fn last(self) -> Option<(&'a K, &'a mut V)> {\n        match self.inner {\n            InnerIterMut::Inline(i) => i.last().map(|(k, v)| (&*k, v)),\n            InnerIterMut::Heap(h) => h.last(),\n        }\n    }\n}\n\nimpl<K, V> FusedIterator for IterMut<'_, K, V> {}\n\nimpl<'a, K: 'a, V: 'a> DoubleEndedIterator for IterMut<'a, K, V> {\n    fn next_back(&mut self) -> Option<(&'a K, &'a mut V)> {\n        match &mut self.inner {\n            InnerIterMut::Inline(i) => i.next_back().map(|(k, v)| (&*k, v)),\n            InnerIterMut::Heap(h) => h.next_back(),\n        }\n    }\n}\n\nimpl<K, V> ExactSizeIterator for IterMut<'_, K, V> {\n    fn len(&self) -> usize {\n        match &self.inner {\n            InnerIterMut::Inline(i) => i.len(),\n            InnerIterMut::Heap(h) => h.len(),\n        }\n    }\n}\n\nimpl<K, V, const ARRAY_SIZE: usize> IntoIterator for SmallBTreeMap<K, V, ARRAY_SIZE> {\n    type Item = (K, V);\n    type IntoIter = IntoIter<K, V, ARRAY_SIZE>;\n\n    fn into_iter(self) -> Self::IntoIter {\n        match self.inner {\n            Inner::Inline(i) => IntoIter {\n                inner: InnerIntoIter::Inline(i.into_iter()),\n            },\n            Inner::Heap(h) => IntoIter {\n                inner: InnerIntoIter::Heap(h.into_iter()),\n            },\n        }\n    }\n}\n\nimpl<K, V, const ARRAY_SIZE: usize> Iterator for IntoIter<K, V, ARRAY_SIZE> {\n    type Item = (K, V);\n\n    fn next(&mut self) -> Option<(K, V)> {\n        match &mut self.inner {\n            InnerIntoIter::Inline(i) => i.next(),\n            InnerIntoIter::Heap(h) => h.next(),\n        }\n    }\n\n    fn size_hint(&self) -> (usize, Option<usize>) {\n        match &self.inner {\n            InnerIntoIter::Inline(i) => i.size_hint(),\n            InnerIntoIter::Heap(h) => h.size_hint(),\n        }\n    }\n}\n\nimpl<K, V, const ARRAY_SIZE: usize> DoubleEndedIterator for IntoIter<K, V, ARRAY_SIZE> {\n    fn next_back(&mut self) -> Option<(K, V)> {\n        match &mut self.inner {\n            InnerIntoIter::Inline(i) => i.next_back(),\n            InnerIntoIter::Heap(h) => h.next_back(),\n        }\n    }\n}\n\nimpl<K, V, const ARRAY_SIZE: usize> ExactSizeIterator for IntoIter<K, V, ARRAY_SIZE> {\n    fn len(&self) -> usize {\n        match &self.inner {\n            InnerIntoIter::Inline(i) => i.len(),\n            InnerIntoIter::Heap(h) => h.len(),\n        }\n    }\n}\n\nimpl<K, V, const ARRAY_SIZE: usize> FusedIterator for IntoIter<K, V, ARRAY_SIZE> {}\n\nimpl<K: Eq + Ord, V, const ARRAY_SIZE: usize> Extend<(K, V)> for SmallBTreeMap<K, V, ARRAY_SIZE> {\n    fn extend<T: IntoIterator<Item = (K, V)>>(&mut self, iter: T) {\n        iter.into_iter().for_each(move |(k, v)| {\n            self.insert(k, v);\n        });\n    }\n}\n\nimpl<'a, K: Eq + Ord + Copy, V: Copy, const ARRAY_SIZE: usize> Extend<(&'a K, &'a V)>\n    for SmallBTreeMap<K, V, ARRAY_SIZE>\n{\n    fn extend<I: IntoIterator<Item = (&'a K, &'a V)>>(&mut self, iter: I) {\n        self.extend(iter.into_iter().map(|(&key, &value)| (key, value)));\n    }\n}\n\nimpl<K: Hash, V: Hash, const ARRAY_SIZE: usize> Hash for SmallBTreeMap<K, V, ARRAY_SIZE> {\n    fn hash<H: Hasher>(&self, state: &mut H) {\n        // TODO: track https://github.com/rust-lang/rust/issues/96762\n        // state.write_length_prefix(self.len());\n        state.write_usize(self.len());\n        for elt in self {\n            elt.hash(state);\n        }\n    }\n}\n\nimpl<K, V, const ARRAY_SIZE: usize> Default for SmallBTreeMap<K, V, ARRAY_SIZE> {\n    /// Creates an empty `SmallBTreeMap`.\n    fn default() -> Self {\n        Self::new()\n    }\n}\n\nimpl<K: PartialEq + Ord, V: PartialEq, const LHS_SIZE: usize, const RHS_SIZE: usize>\n    PartialEq<SmallBTreeMap<K, V, RHS_SIZE>> for SmallBTreeMap<K, V, LHS_SIZE>\n{\n    fn eq(&self, other: &SmallBTreeMap<K, V, RHS_SIZE>) -> bool {\n        if let (Inner::Heap(lhs), Inner::Heap(rhs)) = (&self.inner, &other.inner) {\n            return lhs == rhs;\n        }\n\n        if self.len() != other.len() {\n            return false;\n        }\n\n        self.iter()\n            .all(|(key, value)| other.get(key).is_some_and(|v| *value == *v))\n    }\n}\n\nimpl<K: Eq + Ord, V: Eq, const ARRAY_SIZE: usize> Eq for SmallBTreeMap<K, V, ARRAY_SIZE> {}\n\nimpl<K: fmt::Debug, V: fmt::Debug, const ARRAY_SIZE: usize> fmt::Debug\n    for SmallBTreeMap<K, V, ARRAY_SIZE>\n{\n    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {\n        f.debug_map().entries(self.iter()).finish()\n    }\n}\n\nimpl<K, Q: ?Sized, V, const ARRAY_SIZE: usize> Index<&Q> for SmallBTreeMap<K, V, ARRAY_SIZE>\nwhere\n    K: Eq + Ord + Borrow<Q>,\n    Q: Eq + Ord,\n{\n    type Output = V;\n\n    fn index(&self, index: &Q) -> &Self::Output {\n        self.get(index).expect(\"no entry found for key\")\n    }\n}\n\nimpl<K, Q: ?Sized, V, const ARRAY_SIZE: usize> IndexMut<&Q> for SmallBTreeMap<K, V, ARRAY_SIZE>\nwhere\n    K: Eq + Ord + Borrow<Q>,\n    Q: Eq + Ord,\n{\n    fn index_mut(&mut self, index: &Q) -> &mut Self::Output {\n        self.get_mut(index).expect(\"no entry found for key\")\n    }\n}\n\nimpl<K, V, const ARRAY_SIZE: usize> SmallBTreeMap<K, V, ARRAY_SIZE> {\n    /// Gets an iterator over the entries of the map.\n    pub fn iter(&self) -> Iter<'_, K, V> {\n        match &self.inner {\n            Inner::Inline(i) => Iter {\n                inner: InnerIter::Inline(i.iter()),\n            },\n            Inner::Heap(h) => Iter {\n                inner: InnerIter::Heap(h.iter()),\n            },\n        }\n    }\n\n    /// Gets a mutable iterator over the entries of the map.\n    pub fn iter_mut(&mut self) -> IterMut<'_, K, V> {\n        match &mut self.inner {\n            Inner::Inline(i) => IterMut {\n                inner: InnerIterMut::Inline(i.iter_mut()),\n            },\n            Inner::Heap(h) => IterMut {\n                inner: InnerIterMut::Heap(h.iter_mut()),\n            },\n        }\n    }\n\n    /// Returns the number of elements in the map.\n    pub fn len(&self) -> usize {\n        match &self.inner {\n            Inner::Inline(i) => i.len(),\n            Inner::Heap(h) => h.len(),\n        }\n    }\n\n    /// Returns `true` if the map contains no elements.\n    pub fn is_empty(&self) -> bool {\n        self.len() == 0\n    }\n}\n"
  },
  {
    "path": "utils/tag_ptr/ABOUT.md",
    "content": "# About Boa\n\nBoa is an open-source, experimental ECMAScript Engine written in Rust for\nlexing, parsing and executing ECMAScript/JavaScript. Currently, Boa supports some\nof the [language][boa-conformance]. More information can be viewed at [Boa's\nwebsite][boa-web].\n\nTry out the most recent release with Boa's live demo\n[playground][boa-playground].\n\n## Boa Crates\n\n- [**`boa_cli`**][cli] - Boa's CLI && REPL implementation\n- [**`boa_ast`**][ast] - Boa's ECMAScript Abstract Syntax Tree.\n- [**`boa_engine`**][engine] - Boa's implementation of ECMAScript builtin objects and execution.\n- [**`boa_gc`**][gc] - Boa's garbage collector.\n- [**`boa_icu_provider`**][icu] - Boa's ICU4X data provider.\n- [**`boa_interner`**][interner] - Boa's string interner.\n- [**`boa_macros`**][macros] - Boa's macros.\n- [**`boa_parser`**][parser] - Boa's lexer and parser.\n- [**`boa_runtime`**][runtime] - Boa's `WebAPI` features.\n- [**`boa_string`**][string] - Boa's ECMAScript string implementation.\n- [**`boa_wintertc`**][wintertc] - Boa's `WinterTC` (TC55) Minimum Common Web API implementation.\n- [**`tag_ptr`**][tag_ptr] - Utility library that enables a pointer to be associated with a tag of type `usize`.\n- [**`small_btree`**][small_btree] - Utility library that adds the `SmallBTreeMap` data structure.\n\n[boa-conformance]: https://boajs.dev/conformance\n[boa-web]: https://boajs.dev/\n[boa-playground]: https://boajs.dev/playground\n[ast]: https://docs.rs/boa_ast/latest/boa_ast/index.html\n[engine]: https://docs.rs/boa_engine/latest/boa_engine/index.html\n[gc]: https://docs.rs/boa_gc/latest/boa_gc/index.html\n[interner]: https://docs.rs/boa_interner/latest/boa_interner/index.html\n[parser]: https://docs.rs/boa_parser/latest/boa_parser/index.html\n[icu]: https://docs.rs/boa_icu_provider/latest/boa_icu_provider/index.html\n[runtime]: https://docs.rs/boa_runtime/latest/boa_runtime/index.html\n[string]: https://docs.rs/boa_string/latest/boa_string/index.html\n[wintertc]: https://docs.rs/boa_wintertc/latest/boa_wintertc/index.html\n[tag_ptr]: https://docs.rs/tag_ptr/latest/tag_ptr/index.html\n[small_btree]: https://docs.rs/small_btree/latest/small_btree/index.html\n[macros]: https://docs.rs/boa_macros/latest/boa_macros/index.html\n[cli]: https://crates.io/crates/boa_cli\n"
  },
  {
    "path": "utils/tag_ptr/Cargo.toml",
    "content": "[package]\nname = \"tag_ptr\"\nkeywords = [\"tag\", \"pointer\"]\ncategories = [\"data-structures\"]\nreadme = \"../../README.md\"\ndescription = \"Utility library that enables a pointer to be associated with a tag of type `usize`\"\nversion = \"0.1.0\"\nedition.workspace = true\nauthors.workspace = true\nlicense.workspace = true\nrepository.workspace = true\nrust-version.workspace = true\n\n[lints]\nworkspace = true\n\n[package.metadata.docs.rs]\nall-features = true\n"
  },
  {
    "path": "utils/tag_ptr/src/lib.rs",
    "content": "//! Utility library that enables a pointer to be associated with a tag of type `usize`\n\n#![doc = include_str!(\"../ABOUT.md\")]\n#![doc(\n    html_logo_url = \"https://raw.githubusercontent.com/boa-dev/boa/main/assets/logo_black.svg\",\n    html_favicon_url = \"https://raw.githubusercontent.com/boa-dev/boa/main/assets/logo_black.svg\"\n)]\n\nuse std::ptr::{self, NonNull};\n\n/// A pointer that can be tagged with an `usize`.\n///\n/// Only pointers with a minimum alignment of 2-bytes are valid, and the tag must have its most\n/// significant bit (MSB) unset. In other words, the tag must fit inside `usize::BITS - 1` bits.\n/// Using pointers that are not 2-byte aligned won't cause Undefined Behaviour, but it could cause\n/// logical errors where the pointer is interpreted as an `usize` instead.\n///\n/// # Representation\n///\n/// If the least significant bit (LSB) of the internal [`NonNull`] is set (1), then the pointer\n/// address represents a tag where the remaining bits store the tag. Otherwise, the whole pointer\n/// represents the pointer itself.\n///\n/// It uses [`NonNull`], which guarantees that [`Tagged`] can use the \"null pointer optimization\"\n/// to optimize the size of [`Option<Tagged>`].\n///\n/// # Provenance\n///\n/// This struct stores a [`NonNull<T>`] instead of a [`NonZeroUsize`][std::num::NonZeroUsize]\n/// in order to preserve the provenance of our valid heap pointers.\n/// On the other hand, all index values are just casted to invalid pointers, because we don't need to\n/// preserve the provenance of [`usize`] indices.\n///\n/// [tagged_wp]: https://en.wikipedia.org/wiki/Tagged_pointer\n#[derive(Debug)]\npub struct Tagged<T>(NonNull<T>);\n\nimpl<T> Clone for Tagged<T> {\n    fn clone(&self) -> Self {\n        *self\n    }\n}\n\nimpl<T> Copy for Tagged<T> {}\n\nimpl<T> Tagged<T> {\n    /// Creates a new, tagged `Tagged` pointer from an integer.\n    ///\n    /// # Requirements\n    ///\n    /// - `tag` must fit inside `usize::BITS - 1` bits\n    #[inline]\n    #[must_use]\n    pub const fn from_tag(tag: usize) -> Self {\n        let addr = (tag << 1) | 1;\n        // SAFETY: `addr` is never zero, since we always set its LSB to 1\n        unsafe { Self(NonNull::new_unchecked(ptr::without_provenance_mut(addr))) }\n    }\n\n    /// Creates a new `Tagged` pointer from a raw pointer.\n    ///\n    /// # Requirements\n    ///\n    /// - `ptr` must have an alignment of at least 2.\n    ///\n    /// # Safety\n    ///\n    /// - `ptr` must be non null.\n    #[inline]\n    pub const unsafe fn from_ptr(ptr: *mut T) -> Self {\n        // SAFETY: the caller must ensure the invariants hold.\n        unsafe { Self(NonNull::new_unchecked(ptr)) }\n    }\n\n    /// Creates a new `Tagged` pointer from a (possibly tagged) `NonNull` pointer.\n    #[inline]\n    #[must_use]\n    pub const fn from_non_null(ptr: NonNull<T>) -> Self {\n        Self(ptr)\n    }\n\n    /// Unwraps the `Tagged` pointer.\n    #[inline]\n    #[must_use]\n    pub fn unwrap(self) -> UnwrappedTagged<T> {\n        let addr = self.0.as_ptr().addr();\n        if addr & 1 == 0 {\n            UnwrappedTagged::Ptr(self.0)\n        } else {\n            UnwrappedTagged::Tag(addr >> 1)\n        }\n    }\n\n    /// Gets the address of the inner pointer.\n    #[inline]\n    #[must_use]\n    pub fn addr(self) -> usize {\n        self.0.as_ptr().addr()\n    }\n\n    /// Gets the inner pointer.\n    ///\n    /// This may be a pointer or a tag, you can use\n    /// [`Self::is_tagged()`] to check.\n    #[inline]\n    #[must_use]\n    pub const fn as_inner_ptr(&self) -> NonNull<T> {\n        self.0\n    }\n\n    /// Returns `true` if `self ` is a tagged pointer.\n    #[inline]\n    #[must_use]\n    pub fn is_tagged(self) -> bool {\n        self.0.as_ptr().addr() & 1 > 0\n    }\n}\n\n/// The unwrapped value of a [`Tagged`] pointer.\n#[derive(Debug, Clone, Copy)]\npub enum UnwrappedTagged<T> {\n    /// Pointer variant.\n    Ptr(NonNull<T>),\n    /// Tag variant.\n    Tag(usize),\n}\n"
  }
]